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:
authorAntonio Vazquez <blendergit@gmail.com>2022-09-22 12:52:48 +0300
committerAntonio Vazquez <blendergit@gmail.com>2022-09-22 12:54:53 +0300
commit2db84a8ee7cd3873e11eb0c13abc09869caa0042 (patch)
treeea15209cc9742e23f36a7e692eb837723bfa64b3 /source
parenta41ee7ea1ae9bb1bcc537576d9ffdb3bca3201f3 (diff)
Fix all merge conflicts
There was a lot of conflicts and to solve, I refresh manually the source folders.
Diffstat (limited to 'source')
-rw-r--r--source/blender/CMakeLists.txt4
-rw-r--r--source/blender/blendthumb/CMakeLists.txt2
-rw-r--r--source/blender/blendthumb/src/blendthumb_extract.cc8
-rw-r--r--source/blender/blenfont/BLF_api.h8
-rw-r--r--source/blender/blenfont/CMakeLists.txt1
-rw-r--r--source/blender/blenfont/intern/blf.c28
-rw-r--r--source/blender/blenfont/intern/blf_font.c516
-rw-r--r--source/blender/blenfont/intern/blf_font_default.c71
-rw-r--r--source/blender/blenfont/intern/blf_glyph.c222
-rw-r--r--source/blender/blenfont/intern/blf_internal.h29
-rw-r--r--source/blender/blenfont/intern/blf_internal_types.h157
-rw-r--r--source/blender/blenfont/intern/blf_thumbs.c29
-rw-r--r--source/blender/blenkernel/BKE_DerivedMesh.h13
-rw-r--r--source/blender/blenkernel/BKE_appdir.h31
-rw-r--r--source/blender/blenkernel/BKE_attribute.hh169
-rw-r--r--source/blender/blenkernel/BKE_attribute_math.hh225
-rw-r--r--source/blender/blenkernel/BKE_blender_version.h4
-rw-r--r--source/blender/blenkernel/BKE_bpath.h3
-rw-r--r--source/blender/blenkernel/BKE_cachefile.h10
-rw-r--r--source/blender/blenkernel/BKE_cdderivedmesh.h4
-rw-r--r--source/blender/blenkernel/BKE_cloth.h2
-rw-r--r--source/blender/blenkernel/BKE_collection.h13
-rw-r--r--source/blender/blenkernel/BKE_collision.h1
-rw-r--r--source/blender/blenkernel/BKE_compute_contexts.hh46
-rw-r--r--source/blender/blenkernel/BKE_crazyspace.hh53
-rw-r--r--source/blender/blenkernel/BKE_cryptomatte.h2
-rw-r--r--source/blender/blenkernel/BKE_cryptomatte.hh26
-rw-r--r--source/blender/blenkernel/BKE_curves.h2
-rw-r--r--source/blender/blenkernel/BKE_curves.hh131
-rw-r--r--source/blender/blenkernel/BKE_curves_utils.hh355
-rw-r--r--source/blender/blenkernel/BKE_customdata.h60
-rw-r--r--source/blender/blenkernel/BKE_deform.h80
-rw-r--r--source/blender/blenkernel/BKE_displist.h6
-rw-r--r--source/blender/blenkernel/BKE_effect.h1
-rw-r--r--source/blender/blenkernel/BKE_fcurve.h26
-rw-r--r--source/blender/blenkernel/BKE_fcurve_driver.h1
-rw-r--r--source/blender/blenkernel/BKE_geometry_fields.hh176
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.h3
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh102
-rw-r--r--source/blender/blenkernel/BKE_global.h2
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h22
-rw-r--r--source/blender/blenkernel/BKE_icons.h4
-rw-r--r--source/blender/blenkernel/BKE_idprop.h8
-rw-r--r--source/blender/blenkernel/BKE_idprop.hh9
-rw-r--r--source/blender/blenkernel/BKE_idtype.h8
-rw-r--r--source/blender/blenkernel/BKE_image.h38
-rw-r--r--source/blender/blenkernel/BKE_image_format.h2
-rw-r--r--source/blender/blenkernel/BKE_image_save.h4
-rw-r--r--source/blender/blenkernel/BKE_key.h10
-rw-r--r--source/blender/blenkernel/BKE_lattice.h1
-rw-r--r--source/blender/blenkernel/BKE_layer.h151
-rw-r--r--source/blender/blenkernel/BKE_lib_id.h83
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h15
-rw-r--r--source/blender/blenkernel/BKE_lib_principle_properties.h85
-rw-r--r--source/blender/blenkernel/BKE_lib_query.h1
-rw-r--r--source/blender/blenkernel/BKE_lib_remap.h8
-rw-r--r--source/blender/blenkernel/BKE_main.h20
-rw-r--r--source/blender/blenkernel/BKE_main_namemap.h66
-rw-r--r--source/blender/blenkernel/BKE_mball.h24
-rw-r--r--source/blender/blenkernel/BKE_mball_tessellate.h8
-rw-r--r--source/blender/blenkernel/BKE_mesh.h237
-rw-r--r--source/blender/blenkernel/BKE_mesh_fair.h14
-rw-r--r--source/blender/blenkernel/BKE_mesh_legacy_convert.h29
-rw-r--r--source/blender/blenkernel/BKE_mesh_mapping.h64
-rw-r--r--source/blender/blenkernel/BKE_mesh_remap.h16
-rw-r--r--source/blender/blenkernel/BKE_mesh_remesh_voxel.h4
-rw-r--r--source/blender/blenkernel/BKE_mesh_sample.hh46
-rw-r--r--source/blender/blenkernel/BKE_modifier.h23
-rw-r--r--source/blender/blenkernel/BKE_nla.h2
-rw-r--r--source/blender/blenkernel/BKE_node.h77
-rw-r--r--source/blender/blenkernel/BKE_node_runtime.hh486
-rw-r--r--source/blender/blenkernel/BKE_node_tree_update.h1
-rw-r--r--source/blender/blenkernel/BKE_object.h31
-rw-r--r--source/blender/blenkernel/BKE_outliner_treehash.h46
-rw-r--r--source/blender/blenkernel/BKE_outliner_treehash.hh76
-rw-r--r--source/blender/blenkernel/BKE_paint.h259
-rw-r--r--source/blender/blenkernel/BKE_particle.h2
-rw-r--r--source/blender/blenkernel/BKE_pbvh.h183
-rw-r--r--source/blender/blenkernel/BKE_pbvh_pixels.hh10
-rw-r--r--source/blender/blenkernel/BKE_pointcache.h2
-rw-r--r--source/blender/blenkernel/BKE_pointcloud.h8
-rw-r--r--source/blender/blenkernel/BKE_scene.h6
-rw-r--r--source/blender/blenkernel/BKE_screen.h12
-rw-r--r--source/blender/blenkernel/BKE_shrinkwrap.h4
-rw-r--r--source/blender/blenkernel/BKE_sound.h2
-rw-r--r--source/blender/blenkernel/BKE_spline.hh688
-rw-r--r--source/blender/blenkernel/BKE_subdiv_ccg.h1
-rw-r--r--source/blender/blenkernel/BKE_subdiv_eval.h2
-rw-r--r--source/blender/blenkernel/BKE_subdiv_mesh.h8
-rw-r--r--source/blender/blenkernel/BKE_volume.h9
-rw-r--r--source/blender/blenkernel/BKE_writeffmpeg.h2
-rw-r--r--source/blender/blenkernel/CMakeLists.txt44
-rw-r--r--source/blender/blenkernel/intern/DerivedMesh.cc223
-rw-r--r--source/blender/blenkernel/intern/action.c7
-rw-r--r--source/blender/blenkernel/intern/action_mirror.c2
-rw-r--r--source/blender/blenkernel/intern/anim_data.c13
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c11
-rw-r--r--source/blender/blenkernel/intern/appdir.c54
-rw-r--r--source/blender/blenkernel/intern/armature.c12
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c54
-rw-r--r--source/blender/blenkernel/intern/armature_update.c2
-rw-r--r--source/blender/blenkernel/intern/asset_catalog.cc2
-rw-r--r--source/blender/blenkernel/intern/attribute.cc241
-rw-r--r--source/blender/blenkernel/intern/attribute_access.cc451
-rw-r--r--source/blender/blenkernel/intern/attribute_access_intern.hh55
-rw-r--r--source/blender/blenkernel/intern/attribute_math.cc69
-rw-r--r--source/blender/blenkernel/intern/blender_copybuffer.c2
-rw-r--r--source/blender/blenkernel/intern/blendfile.c6
-rw-r--r--source/blender/blenkernel/intern/blendfile_link_append.c22
-rw-r--r--source/blender/blenkernel/intern/boids.c2
-rw-r--r--source/blender/blenkernel/intern/brush.cc24
-rw-r--r--source/blender/blenkernel/intern/bvhutils.cc64
-rw-r--r--source/blender/blenkernel/intern/cachefile.c8
-rw-r--r--source/blender/blenkernel/intern/camera.c95
-rw-r--r--source/blender/blenkernel/intern/cdderivedmesh.c40
-rw-r--r--source/blender/blenkernel/intern/cloth.c23
-rw-r--r--source/blender/blenkernel/intern/collection.c98
-rw-r--r--source/blender/blenkernel/intern/collision.c3
-rw-r--r--source/blender/blenkernel/intern/colorband.c2
-rw-r--r--source/blender/blenkernel/intern/compute_contexts.cc38
-rw-r--r--source/blender/blenkernel/intern/constraint.c118
-rw-r--r--source/blender/blenkernel/intern/context.c4
-rw-r--r--source/blender/blenkernel/intern/crazyspace.cc (renamed from source/blender/blenkernel/intern/crazyspace.c)189
-rw-r--r--source/blender/blenkernel/intern/cryptomatte.cc79
-rw-r--r--source/blender/blenkernel/intern/cryptomatte_test.cc2
-rw-r--r--source/blender/blenkernel/intern/curve.cc8
-rw-r--r--source/blender/blenkernel/intern/curve_catmull_rom.cc26
-rw-r--r--source/blender/blenkernel/intern/curve_eval.cc587
-rw-r--r--source/blender/blenkernel/intern/curve_legacy_convert.cc14
-rw-r--r--source/blender/blenkernel/intern/curve_nurbs.cc40
-rw-r--r--source/blender/blenkernel/intern/curve_poly.cc17
-rw-r--r--source/blender/blenkernel/intern/curve_to_mesh_convert.cc10
-rw-r--r--source/blender/blenkernel/intern/curves.cc111
-rw-r--r--source/blender/blenkernel/intern/curves_geometry.cc340
-rw-r--r--source/blender/blenkernel/intern/curves_utils.cc27
-rw-r--r--source/blender/blenkernel/intern/customdata.cc785
-rw-r--r--source/blender/blenkernel/intern/data_transfer.c143
-rw-r--r--source/blender/blenkernel/intern/deform.c195
-rw-r--r--source/blender/blenkernel/intern/displist.cc164
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c65
-rw-r--r--source/blender/blenkernel/intern/editmesh_tangent.cc (renamed from source/blender/blenkernel/intern/editmesh_tangent.c)301
-rw-r--r--source/blender/blenkernel/intern/effect.c6
-rw-r--r--source/blender/blenkernel/intern/fcurve.c80
-rw-r--r--source/blender/blenkernel/intern/fcurve_driver.c1
-rw-r--r--source/blender/blenkernel/intern/fcurve_test.cc1
-rw-r--r--source/blender/blenkernel/intern/fluid.c192
-rw-r--r--source/blender/blenkernel/intern/fmodifier.c30
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curve.cc1463
-rw-r--r--source/blender/blenkernel/intern/geometry_component_curves.cc100
-rw-r--r--source/blender/blenkernel/intern/geometry_component_edit_data.cc58
-rw-r--r--source/blender/blenkernel/intern/geometry_component_instances.cc3
-rw-r--r--source/blender/blenkernel/intern/geometry_component_mesh.cc291
-rw-r--r--source/blender/blenkernel/intern/geometry_component_pointcloud.cc24
-rw-r--r--source/blender/blenkernel/intern/geometry_fields.cc365
-rw-r--r--source/blender/blenkernel/intern/geometry_set.cc115
-rw-r--r--source/blender/blenkernel/intern/geometry_set_instances.cc4
-rw-r--r--source/blender/blenkernel/intern/gpencil.c6
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c11
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.cc177
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c7
-rw-r--r--source/blender/blenkernel/intern/icons_rasterize.c2
-rw-r--r--source/blender/blenkernel/intern/idprop.c10
-rw-r--r--source/blender/blenkernel/intern/idprop_create.cc12
-rw-r--r--source/blender/blenkernel/intern/idtype.c39
-rw-r--r--source/blender/blenkernel/intern/image.cc378
-rw-r--r--source/blender/blenkernel/intern/image_gpu.cc20
-rw-r--r--source/blender/blenkernel/intern/image_partial_update.cc2
-rw-r--r--source/blender/blenkernel/intern/image_save.cc35
-rw-r--r--source/blender/blenkernel/intern/image_test.cc19
-rw-r--r--source/blender/blenkernel/intern/ipo.c6
-rw-r--r--source/blender/blenkernel/intern/key.c185
-rw-r--r--source/blender/blenkernel/intern/lattice.c17
-rw-r--r--source/blender/blenkernel/intern/lattice_deform.c6
-rw-r--r--source/blender/blenkernel/intern/layer.c211
-rw-r--r--source/blender/blenkernel/intern/layer_utils.c127
-rw-r--r--source/blender/blenkernel/intern/lib_id.c368
-rw-r--r--source/blender/blenkernel/intern/lib_id_delete.c9
-rw-r--r--source/blender/blenkernel/intern/lib_id_remapper.cc1
-rw-r--r--source/blender/blenkernel/intern/lib_id_remapper_test.cc1
-rw-r--r--source/blender/blenkernel/intern/lib_id_test.cc386
-rw-r--r--source/blender/blenkernel/intern/lib_override.cc583
-rw-r--r--source/blender/blenkernel/intern/lib_principle_properties.c106
-rw-r--r--source/blender/blenkernel/intern/lib_query.c14
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c7
-rw-r--r--source/blender/blenkernel/intern/library.c26
-rw-r--r--source/blender/blenkernel/intern/light.c3
-rw-r--r--source/blender/blenkernel/intern/lightprobe.c2
-rw-r--r--source/blender/blenkernel/intern/linestyle.c7
-rw-r--r--source/blender/blenkernel/intern/main.c5
-rw-r--r--source/blender/blenkernel/intern/main_namemap.cc500
-rw-r--r--source/blender/blenkernel/intern/mask.c4
-rw-r--r--source/blender/blenkernel/intern/material.c23
-rw-r--r--source/blender/blenkernel/intern/mball.cc (renamed from source/blender/blenkernel/intern/mball.c)289
-rw-r--r--source/blender/blenkernel/intern/mball_tessellate.c87
-rw-r--r--source/blender/blenkernel/intern/mesh.cc482
-rw-r--r--source/blender/blenkernel/intern/mesh_boolean_convert.cc114
-rw-r--r--source/blender/blenkernel/intern/mesh_calc_edges.cc14
-rw-r--r--source/blender/blenkernel/intern/mesh_convert.cc612
-rw-r--r--source/blender/blenkernel/intern/mesh_debug.cc9
-rw-r--r--source/blender/blenkernel/intern/mesh_evaluate.cc226
-rw-r--r--source/blender/blenkernel/intern/mesh_fair.cc85
-rw-r--r--source/blender/blenkernel/intern/mesh_iterators.c33
-rw-r--r--source/blender/blenkernel/intern/mesh_legacy_convert.cc306
-rw-r--r--source/blender/blenkernel/intern/mesh_mapping.cc (renamed from source/blender/blenkernel/intern/mesh_mapping.c)133
-rw-r--r--source/blender/blenkernel/intern/mesh_merge.c96
-rw-r--r--source/blender/blenkernel/intern/mesh_merge_customdata.cc15
-rw-r--r--source/blender/blenkernel/intern/mesh_mirror.c40
-rw-r--r--source/blender/blenkernel/intern/mesh_normals.cc160
-rw-r--r--source/blender/blenkernel/intern/mesh_remap.c95
-rw-r--r--source/blender/blenkernel/intern/mesh_remesh_voxel.cc104
-rw-r--r--source/blender/blenkernel/intern/mesh_runtime.cc54
-rw-r--r--source/blender/blenkernel/intern/mesh_sample.cc152
-rw-r--r--source/blender/blenkernel/intern/mesh_tangent.cc (renamed from source/blender/blenkernel/intern/mesh_tangent.c)526
-rw-r--r--source/blender/blenkernel/intern/mesh_tessellate.cc (renamed from source/blender/blenkernel/intern/mesh_tessellate.c)53
-rw-r--r--source/blender/blenkernel/intern/mesh_validate.cc188
-rw-r--r--source/blender/blenkernel/intern/mesh_wrapper.cc30
-rw-r--r--source/blender/blenkernel/intern/modifier.c53
-rw-r--r--source/blender/blenkernel/intern/movieclip.c2
-rw-r--r--source/blender/blenkernel/intern/multires.c25
-rw-r--r--source/blender/blenkernel/intern/multires_reshape.c3
-rw-r--r--source/blender/blenkernel/intern/multires_reshape.h8
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_apply_base.c31
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_smooth.c21
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_subdivide.c23
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_util.c23
-rw-r--r--source/blender/blenkernel/intern/multires_reshape_vertcos.c3
-rw-r--r--source/blender/blenkernel/intern/multires_unsubdivide.c51
-rw-r--r--source/blender/blenkernel/intern/nla.c7
-rw-r--r--source/blender/blenkernel/intern/node.cc277
-rw-r--r--source/blender/blenkernel/intern/node_runtime.cc432
-rw-r--r--source/blender/blenkernel/intern/node_tree_update.cc553
-rw-r--r--source/blender/blenkernel/intern/object.cc191
-rw-r--r--source/blender/blenkernel/intern/object_deform.c16
-rw-r--r--source/blender/blenkernel/intern/object_dupli.cc46
-rw-r--r--source/blender/blenkernel/intern/object_update.c59
-rw-r--r--source/blender/blenkernel/intern/ocean.c7
-rw-r--r--source/blender/blenkernel/intern/outliner_treehash.c238
-rw-r--r--source/blender/blenkernel/intern/outliner_treehash.cc209
-rw-r--r--source/blender/blenkernel/intern/packedFile.c36
-rw-r--r--source/blender/blenkernel/intern/paint.cc (renamed from source/blender/blenkernel/intern/paint.c)1385
-rw-r--r--source/blender/blenkernel/intern/particle.c69
-rw-r--r--source/blender/blenkernel/intern/particle_child.c9
-rw-r--r--source/blender/blenkernel/intern/particle_distribute.c40
-rw-r--r--source/blender/blenkernel/intern/particle_system.c12
-rw-r--r--source/blender/blenkernel/intern/pbvh.c286
-rw-r--r--source/blender/blenkernel/intern/pbvh.cc26
-rw-r--r--source/blender/blenkernel/intern/pbvh_bmesh.c80
-rw-r--r--source/blender/blenkernel/intern/pbvh_intern.h16
-rw-r--r--source/blender/blenkernel/intern/pbvh_pixels.cc4
-rw-r--r--source/blender/blenkernel/intern/pointcache.c90
-rw-r--r--source/blender/blenkernel/intern/pointcloud.cc119
-rw-r--r--source/blender/blenkernel/intern/report.c16
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c41
-rw-r--r--source/blender/blenkernel/intern/scene.cc86
-rw-r--r--source/blender/blenkernel/intern/screen.c44
-rw-r--r--source/blender/blenkernel/intern/shader_fx.c3
-rw-r--r--source/blender/blenkernel/intern/shrinkwrap.c27
-rw-r--r--source/blender/blenkernel/intern/simulation.cc6
-rw-r--r--source/blender/blenkernel/intern/softbody.c38
-rw-r--r--source/blender/blenkernel/intern/sound.c6
-rw-r--r--source/blender/blenkernel/intern/speaker.c2
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc526
-rw-r--r--source/blender/blenkernel/intern/spline_bezier.cc646
-rw-r--r--source/blender/blenkernel/intern/spline_nurbs.cc395
-rw-r--r--source/blender/blenkernel/intern/spline_poly.cc97
-rw-r--r--source/blender/blenkernel/intern/studiolight.c22
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg.c5
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg_mask.c7
-rw-r--r--source/blender/blenkernel/intern/subdiv_ccg_material.c14
-rw-r--r--source/blender/blenkernel/intern/subdiv_converter_mesh.c42
-rw-r--r--source/blender/blenkernel/intern/subdiv_displacement_multires.c7
-rw-r--r--source/blender/blenkernel/intern/subdiv_eval.c16
-rw-r--r--source/blender/blenkernel/intern/subdiv_foreach.c85
-rw-r--r--source/blender/blenkernel/intern/subdiv_mesh.cc (renamed from source/blender/blenkernel/intern/subdiv_mesh.c)371
-rw-r--r--source/blender/blenkernel/intern/subdiv_modifier.c5
-rw-r--r--source/blender/blenkernel/intern/subsurf_ccg.c21
-rw-r--r--source/blender/blenkernel/intern/text.c2
-rw-r--r--source/blender/blenkernel/intern/texture.c3
-rw-r--r--source/blender/blenkernel/intern/tracking_detect.c4
-rw-r--r--source/blender/blenkernel/intern/tracking_stabilize.c2
-rw-r--r--source/blender/blenkernel/intern/type_conversions.cc61
-rw-r--r--source/blender/blenkernel/intern/unit.c40
-rw-r--r--source/blender/blenkernel/intern/vfont.c23
-rw-r--r--source/blender/blenkernel/intern/volume.cc52
-rw-r--r--source/blender/blenkernel/intern/volume_to_mesh.cc6
-rw-r--r--source/blender/blenkernel/intern/workspace.cc (renamed from source/blender/blenkernel/intern/workspace.c)150
-rw-r--r--source/blender/blenkernel/intern/world.c3
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c2
-rw-r--r--source/blender/blenkernel/nla_private.h13
-rw-r--r--source/blender/blenlib/BLI_any.hh4
-rw-r--r--source/blender/blenlib/BLI_array_store.h1
-rw-r--r--source/blender/blenlib/BLI_array_utils.hh110
-rw-r--r--source/blender/blenlib/BLI_bit_vector.hh529
-rw-r--r--source/blender/blenlib/BLI_bitmap.h13
-rw-r--r--source/blender/blenlib/BLI_compute_context.hh173
-rw-r--r--source/blender/blenlib/BLI_cpp_type.hh2
-rw-r--r--source/blender/blenlib/BLI_fileops.h1
-rw-r--r--source/blender/blenlib/BLI_float3x3.hh16
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh2
-rw-r--r--source/blender/blenlib/BLI_function_ref.hh1
-rw-r--r--source/blender/blenlib/BLI_generic_span.hh57
-rw-r--r--source/blender/blenlib/BLI_generic_virtual_array.hh1
-rw-r--r--source/blender/blenlib/BLI_index_range.hh46
-rw-r--r--source/blender/blenlib/BLI_kdopbvh.h23
-rw-r--r--source/blender/blenlib/BLI_lazy_threading.hh83
-rw-r--r--source/blender/blenlib/BLI_length_parameterize.hh49
-rw-r--r--source/blender/blenlib/BLI_listbase.h23
-rw-r--r--source/blender/blenlib/BLI_listbase_wrapper.hh4
-rw-r--r--source/blender/blenlib/BLI_map.hh4
-rw-r--r--source/blender/blenlib/BLI_math_color.h4
-rw-r--r--source/blender/blenlib/BLI_math_geom.h4
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h272
-rw-r--r--source/blender/blenlib/BLI_math_rotation.h101
-rw-r--r--source/blender/blenlib/BLI_math_rotation.hh9
-rw-r--r--source/blender/blenlib/BLI_math_vector.h88
-rw-r--r--source/blender/blenlib/BLI_memory_utils.hh43
-rw-r--r--source/blender/blenlib/BLI_multi_value_map.hh13
-rw-r--r--source/blender/blenlib/BLI_path_util.h32
-rw-r--r--source/blender/blenlib/BLI_pool.hh84
-rw-r--r--source/blender/blenlib/BLI_probing_strategies.hh6
-rw-r--r--source/blender/blenlib/BLI_scanfill.h2
-rw-r--r--source/blender/blenlib/BLI_serialize.hh2
-rw-r--r--source/blender/blenlib/BLI_set.hh4
-rw-r--r--source/blender/blenlib/BLI_string_utf8.h16
-rw-r--r--source/blender/blenlib/BLI_task.hh20
-rw-r--r--source/blender/blenlib/BLI_utildefines.h2
-rw-r--r--source/blender/blenlib/BLI_vector_adaptor.hh88
-rw-r--r--source/blender/blenlib/BLI_vector_set.hh2
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh88
-rw-r--r--source/blender/blenlib/CMakeLists.txt26
-rw-r--r--source/blender/blenlib/intern/BLI_index_range.cc30
-rw-r--r--source/blender/blenlib/intern/BLI_kdopbvh.c2
-rw-r--r--source/blender/blenlib/intern/BLI_memarena.c3
-rw-r--r--source/blender/blenlib/intern/BLI_memblock.c1
-rw-r--r--source/blender/blenlib/intern/array_utils.cc31
-rw-r--r--source/blender/blenlib/intern/bitmap.c20
-rw-r--r--source/blender/blenlib/intern/boxpack_2d.c1
-rw-r--r--source/blender/blenlib/intern/compute_context.cc48
-rw-r--r--source/blender/blenlib/intern/cpp_type.cc1
-rw-r--r--source/blender/blenlib/intern/filereader_zstd.c5
-rw-r--r--source/blender/blenlib/intern/generic_virtual_array.cc4
-rw-r--r--source/blender/blenlib/intern/index_mask.cc5
-rw-r--r--source/blender/blenlib/intern/lazy_threading.cc30
-rw-r--r--source/blender/blenlib/intern/math_geom.c6
-rw-r--r--source/blender/blenlib/intern/math_matrix.c174
-rw-r--r--source/blender/blenlib/intern/math_rotation.c150
-rw-r--r--source/blender/blenlib/intern/math_rotation.cc13
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc34
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc2
-rw-r--r--source/blender/blenlib/intern/noise.c8
-rw-r--r--source/blender/blenlib/intern/noise.cc69
-rw-r--r--source/blender/blenlib/intern/path_util.c105
-rw-r--r--source/blender/blenlib/intern/rct.c4
-rw-r--r--source/blender/blenlib/intern/scanfill.c2
-rw-r--r--source/blender/blenlib/intern/smallhash.c3
-rw-r--r--source/blender/blenlib/intern/storage.c2
-rw-r--r--source/blender/blenlib/intern/string_cursor_utf8.c2
-rw-r--r--source/blender/blenlib/intern/string_search.cc4
-rw-r--r--source/blender/blenlib/intern/string_utf8.c56
-rw-r--r--source/blender/blenlib/intern/system.c14
-rw-r--r--source/blender/blenlib/intern/task_pool.cc8
-rw-r--r--source/blender/blenlib/intern/task_range.cc3
-rw-r--r--source/blender/blenlib/intern/winstuff.c33
-rw-r--r--source/blender/blenlib/tests/BLI_array_store_test.cc10
-rw-r--r--source/blender/blenlib/tests/BLI_bit_vector_test.cc186
-rw-r--r--source/blender/blenlib/tests/BLI_bitmap_test.cc46
-rw-r--r--source/blender/blenlib/tests/BLI_float3x3_test.cc16
-rw-r--r--source/blender/blenlib/tests/BLI_index_range_test.cc63
-rw-r--r--source/blender/blenlib/tests/BLI_length_parameterize_test.cc20
-rw-r--r--source/blender/blenlib/tests/BLI_math_rotation_test.cc103
-rw-r--r--source/blender/blenlib/tests/BLI_path_util_test.cc7
-rw-r--r--source/blender/blenlib/tests/BLI_pool_test.cc51
-rw-r--r--source/blender/blenlib/tests/BLI_set_test.cc10
-rw-r--r--source/blender/blenlib/tests/BLI_string_search_test.cc2
-rw-r--r--source/blender/blenlib/tests/BLI_vector_test.cc2
-rw-r--r--source/blender/blenlib/tests/performance/CMakeLists.txt4
-rw-r--r--source/blender/blenloader/BLO_blend_validate.h8
-rw-r--r--source/blender/blenloader/BLO_read_write.h5
-rw-r--r--source/blender/blenloader/BLO_readfile.h8
-rw-r--r--source/blender/blenloader/BLO_undofile.h8
-rw-r--r--source/blender/blenloader/CMakeLists.txt27
-rw-r--r--source/blender/blenloader/intern/blend_validate.cc (renamed from source/blender/blenloader/intern/blend_validate.c)28
-rw-r--r--source/blender/blenloader/intern/readblenentry.cc (renamed from source/blender/blenloader/intern/readblenentry.c)31
-rw-r--r--source/blender/blenloader/intern/readfile.cc (renamed from source/blender/blenloader/intern/readfile.c)875
-rw-r--r--source/blender/blenloader/intern/readfile.h10
-rw-r--r--source/blender/blenloader/intern/readfile_tempload.cc (renamed from source/blender/blenloader/intern/readfile_tempload.c)3
-rw-r--r--source/blender/blenloader/intern/undofile.cc (renamed from source/blender/blenloader/intern/undofile.c)65
-rw-r--r--source/blender/blenloader/intern/versioning_250.c19
-rw-r--r--source/blender/blenloader/intern/versioning_280.c15
-rw-r--r--source/blender/blenloader/intern/versioning_290.c26
-rw-r--r--source/blender/blenloader/intern/versioning_300.cc (renamed from source/blender/blenloader/intern/versioning_300.c)491
-rw-r--r--source/blender/blenloader/intern/versioning_400.cc52
-rw-r--r--source/blender/blenloader/intern/versioning_common.cc2
-rw-r--r--source/blender/blenloader/intern/versioning_cycles.c3
-rw-r--r--source/blender/blenloader/intern/versioning_defaults.c32
-rw-r--r--source/blender/blenloader/intern/versioning_legacy.c8
-rw-r--r--source/blender/blenloader/intern/versioning_userdef.c5
-rw-r--r--source/blender/blenloader/intern/writefile.cc (renamed from source/blender/blenloader/intern/writefile.c)168
-rw-r--r--source/blender/blenloader/tests/blendfile_loading_base_test.cc10
-rw-r--r--source/blender/blentranslation/BLT_translation.h2
-rw-r--r--source/blender/blentranslation/msgfmt/CMakeLists.txt21
-rw-r--r--source/blender/bmesh/CMakeLists.txt4
-rw-r--r--source/blender/bmesh/bmesh.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_construct.c46
-rw-r--r--source/blender/bmesh/intern/bmesh_core.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_delete.c7
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c4
-rw-r--r--source/blender/bmesh/intern/bmesh_log.c32
-rw-r--r--source/blender/bmesh/intern/bmesh_log.h115
-rw-r--r--source/blender/bmesh/intern/bmesh_marking.c4
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.cc (renamed from source/blender/bmesh/intern/bmesh_mesh.c)211
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.cc346
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_convert.h2
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh_tessellate.c2
-rw-r--r--source/blender/bmesh/intern/bmesh_query.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_query_uv.cc (renamed from source/blender/bmesh/intern/bmesh_query_uv.c)44
-rw-r--r--source/blender/bmesh/intern/bmesh_structure.c3
-rw-r--r--source/blender/bmesh/operators/bmo_connect.c2
-rw-r--r--source/blender/bmesh/operators/bmo_connect_nonplanar.c4
-rw-r--r--source/blender/bmesh/operators/bmo_connect_pair.c24
-rw-r--r--source/blender/bmesh/operators/bmo_create.c1
-rw-r--r--source/blender/bmesh/operators/bmo_join_triangles.c4
-rw-r--r--source/blender/bmesh/operators/bmo_poke.c15
-rw-r--r--source/blender/bmesh/operators/bmo_primitive.c32
-rw-r--r--source/blender/bmesh/operators/bmo_removedoubles.c4
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide.c4
-rw-r--r--source/blender/bmesh/operators/bmo_subdivide_edgering.c28
-rw-r--r--source/blender/bmesh/operators/bmo_utils.c4
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c146
-rw-r--r--source/blender/bmesh/tools/bmesh_path_region_uv.c9
-rw-r--r--source/blender/bmesh/tools/bmesh_path_region_uv.h6
-rw-r--r--source/blender/bmesh/tools/bmesh_path_uv.c207
-rw-r--r--source/blender/bmesh/tools/bmesh_path_uv.h10
-rw-r--r--source/blender/compositor/CMakeLists.txt1328
-rw-r--r--source/blender/compositor/COM_compositor.h2
-rw-r--r--source/blender/compositor/intern/COM_Node.h4
-rw-r--r--source/blender/compositor/nodes/COM_AlphaOverNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_AntiAliasingNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_BokehBlurNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_BokehImageNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_BoxMaskNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_BrightnessNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ChannelMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ChromaMatteNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ColorBalanceNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ColorCorrectionNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ColorCurveNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_ColorMatteNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ColorRampNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ColorSpillNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc2
-rw-r--r--source/blender/compositor/nodes/COM_CompositorNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ConvertAlphaNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_CropNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_CryptomatteNode.cc5
-rw-r--r--source/blender/compositor/nodes/COM_DefocusNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DenoiseNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DespeckleNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_DifferenceMatteNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_DilateErodeNode.cc9
-rw-r--r--source/blender/compositor/nodes/COM_DirectionalBlurNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_DistanceMatteNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_FilterNode.cc16
-rw-r--r--source/blender/compositor/nodes/COM_GlareNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_IDMaskNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ImageNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_InpaintNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_InvertNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_KeyingNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_KeyingScreenNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_LensDistortionNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_LuminanceMatteNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_MapUVNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_MapValueNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_MaskNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_MixNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_MovieClipNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_MovieDistortionNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_OutputFileNode.cc10
-rw-r--r--source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ScaleNode.cc15
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc2
-rw-r--r--source/blender/compositor/nodes/COM_SplitViewerNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_Stabilize2dNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_SunBeamsNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_SwitchViewNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_TextureNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_TimeNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_TonemapNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_TrackPositionNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_TranslateNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_VectorBlurNode.cc4
-rw-r--r--source/blender/compositor/nodes/COM_VectorCurveNode.cc2
-rw-r--r--source/blender/compositor/nodes/COM_ViewerNode.cc2
-rw-r--r--source/blender/compositor/operations/COM_BokehImageOperation.h4
-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_CompositorOperation.cc9
-rw-r--r--source/blender/compositor/operations/COM_CurveBaseOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_CurveBaseOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_DenoiseOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_DenoiseOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_DirectionalBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_GlareBaseOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_GlareFogGlowOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_GlareFogGlowOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_GlareGhostOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_GlareGhostOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_GlareSimpleStarOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_GlareStreaksOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_GlareStreaksOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_GlareThresholdOperation.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_MovieClipOperation.cc2
-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.cc2
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.h2
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.cc4
-rw-r--r--source/blender/compositor/operations/COM_SMAAOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_ScaleOperation.cc2
-rw-r--r--source/blender/compositor/operations/COM_TextureOperation.cc5
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.cc4
-rw-r--r--source/blender/compositor/operations/COM_TonemapOperation.h8
-rw-r--r--source/blender/compositor/operations/COM_VectorBlurOperation.cc7
-rw-r--r--source/blender/compositor/operations/COM_VectorBlurOperation.h4
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.cc6
-rw-r--r--source/blender/compositor/realtime_compositor/CMakeLists.txt66
-rw-r--r--source/blender/compositor/realtime_compositor/COM_compile_state.hh170
-rw-r--r--source/blender/compositor/realtime_compositor/COM_context.hh72
-rw-r--r--source/blender/compositor/realtime_compositor/COM_conversion_operation.hh158
-rw-r--r--source/blender/compositor/realtime_compositor/COM_domain.hh166
-rw-r--r--source/blender/compositor/realtime_compositor/COM_evaluator.hh170
-rw-r--r--source/blender/compositor/realtime_compositor/COM_input_descriptor.hh34
-rw-r--r--source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh46
-rw-r--r--source/blender/compositor/realtime_compositor/COM_node_operation.hh56
-rw-r--r--source/blender/compositor/realtime_compositor/COM_operation.hh175
-rw-r--r--source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh49
-rw-r--r--source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh31
-rw-r--r--source/blender/compositor/realtime_compositor/COM_result.hh234
-rw-r--r--source/blender/compositor/realtime_compositor/COM_scheduler.hh21
-rw-r--r--source/blender/compositor/realtime_compositor/COM_shader_node.hh87
-rw-r--r--source/blender/compositor/realtime_compositor/COM_shader_operation.hh242
-rw-r--r--source/blender/compositor/realtime_compositor/COM_simple_operation.hh64
-rw-r--r--source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh33
-rw-r--r--source/blender/compositor/realtime_compositor/COM_texture_pool.hh86
-rw-r--r--source/blender/compositor/realtime_compositor/COM_utilities.hh71
-rw-r--r--source/blender/compositor/realtime_compositor/intern/compile_state.cc168
-rw-r--r--source/blender/compositor/realtime_compositor/intern/context.cc36
-rw-r--r--source/blender/compositor/realtime_compositor/intern/conversion_operation.cc239
-rw-r--r--source/blender/compositor/realtime_compositor/intern/domain.cc38
-rw-r--r--source/blender/compositor/realtime_compositor/intern/evaluator.cc170
-rw-r--r--source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc59
-rw-r--r--source/blender/compositor/realtime_compositor/intern/node_operation.cc67
-rw-r--r--source/blender/compositor/realtime_compositor/intern/operation.cc206
-rw-r--r--source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc130
-rw-r--r--source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc67
-rw-r--r--source/blender/compositor/realtime_compositor/intern/result.cc257
-rw-r--r--source/blender/compositor/realtime_compositor/intern/scheduler.cc314
-rw-r--r--source/blender/compositor/realtime_compositor/intern/shader_node.cc157
-rw-r--r--source/blender/compositor/realtime_compositor/intern/shader_operation.cc526
-rw-r--r--source/blender/compositor/realtime_compositor/intern/simple_operation.cc55
-rw-r--r--source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc24
-rw-r--r--source/blender/compositor/realtime_compositor/intern/texture_pool.cc88
-rw-r--r--source/blender/compositor/realtime_compositor/intern/utilities.cc134
-rwxr-xr-xsource/blender/datatoc/datatoc_icon.py4
-rw-r--r--source/blender/depsgraph/CMakeLists.txt5
-rw-r--r--source/blender/depsgraph/DEG_depsgraph.h6
-rw-r--r--source/blender/depsgraph/DEG_depsgraph_build.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.cc129
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder.h18
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.cc17
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cache.h24
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_cycle.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_key.cc96
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_key.h201
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_map.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc176
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h37
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc25
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc193
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h75
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc179
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc3
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.cc12
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_rna.h6
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_transitive.h6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline.cc2
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline.h6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_all_objects.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_all_objects.h6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_compositor.h6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_from_ids.cc4
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_from_ids.h6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_render.h6
-rw-r--r--source/blender/depsgraph/intern/builder/pipeline_view_layer.h6
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug.h6
-rw-r--r--source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc1
-rw-r--r--source/blender/depsgraph/intern/debug/deg_time_average.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph.cc8
-rw-r--r--source/blender/depsgraph/intern/depsgraph.h18
-rw-r--r--source/blender/depsgraph/intern/depsgraph_build.cc10
-rw-r--r--source/blender/depsgraph/intern/depsgraph_physics.cc2
-rw-r--r--source/blender/depsgraph/intern/depsgraph_physics.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query.cc3
-rw-r--r--source/blender/depsgraph/intern/depsgraph_query_iter.cc15
-rw-r--r--source/blender/depsgraph/intern/depsgraph_registry.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_relation.h8
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc97
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.h10
-rw-r--r--source/blender/depsgraph/intern/depsgraph_type.h6
-rw-r--r--source/blender/depsgraph/intern/depsgraph_update.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc154
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc9
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc16
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_animation.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequence.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_stats.h6
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.cc232
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_visibility.h29
-rw-r--r--source/blender/depsgraph/intern/node/deg_node.cc4
-rw-r--r--source/blender/depsgraph/intern/node/deg_node.h10
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.cc23
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_component.h55
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_factory.h7
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_factory_impl.h15
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.cc9
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_id.h23
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.cc24
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_operation.h25
-rw-r--r--source/blender/depsgraph/intern/node/deg_node_time.h6
-rw-r--r--source/blender/draw/CMakeLists.txt167
-rw-r--r--source/blender/draw/engines/compositor/compositor_engine.cc203
-rw-r--r--source/blender/draw/engines/compositor/compositor_engine.h13
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c15
-rw-r--r--source/blender/draw/engines/eevee/eevee_engine.c4
-rw-r--r--source/blender/draw/engines/eevee/eevee_lightcache.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_private.h4
-rw-r--r--source/blender/draw/engines/eevee/eevee_render.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_sampling.c5
-rw-r--r--source/blender/draw/engines/eevee/eevee_shaders.c14
-rw-r--r--source/blender/draw/engines/eevee/eevee_shadows_cascade.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_volumes.c11
-rw-r--r--source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/cryptomatte_lib.glsl19
-rw-r--r--source/blender/draw/engines/eevee/shaders/cryptomatte_vert.glsl1
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl2
-rw-r--r--source/blender/draw/engines/eevee/shaders/prepass_frag.glsl14
-rw-r--r--source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl3
-rw-r--r--source/blender/draw/engines/eevee/shaders/shadow_vert.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_frag.glsl6
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_lib.glsl8
-rw-r--r--source/blender/draw/engines/eevee/shaders/surface_vert.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl4
-rw-r--r--source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl5
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_camera.cc18
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_camera.hh23
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc132
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh68
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_defines.hh80
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc761
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh200
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_engine.cc22
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_film.cc362
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_film.hh148
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc99
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh88
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.cc190
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_instance.hh27
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_light.cc488
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_light.hh169
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_material.cc93
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_material.hh15
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_motion_blur.cc256
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_motion_blur.hh130
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_pipeline.cc304
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_pipeline.hh84
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc84
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh8
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sampling.cc38
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sampling.hh37
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.cc94
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader.hh35
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_shader_shared.hh425
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sync.cc87
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_sync.hh12
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_velocity.cc124
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_velocity.hh86
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.cc69
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_view.hh11
-rw-r--r--source/blender/draw/engines/eevee_next/eevee_world.cc4
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl42
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl20
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl37
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl70
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl680
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl55
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl32
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl163
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl99
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl70
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl327
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl247
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl178
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl62
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl45
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl46
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl367
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl97
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl78
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl77
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl14
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl480
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl24
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl121
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl54
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl62
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl57
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl188
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl56
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl129
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_light_iter_lib.glsl72
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl209
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_ltc_lib.glsl299
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl115
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl103
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl221
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl48
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl86
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl104
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl27
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl74
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl6
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl10
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl107
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl58
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh247
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh40
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh31
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh76
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh72
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh46
-rw-r--r--source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh43
-rw-r--r--source/blender/draw/engines/external/external_engine.c12
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_draw_data.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.c2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_engine.h2
-rw-r--r--source/blender/draw/engines/gpencil/gpencil_shader_shared.h26
-rw-r--r--source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl2
-rw-r--r--source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh4
-rw-r--r--source/blender/draw/engines/overlay/overlay_antialiasing.cc (renamed from source/blender/draw/engines/overlay/overlay_antialiasing.c)13
-rw-r--r--source/blender/draw/engines/overlay/overlay_armature.cc (renamed from source/blender/draw/engines/overlay/overlay_armature.c)209
-rw-r--r--source/blender/draw/engines/overlay/overlay_background.cc (renamed from source/blender/draw/engines/overlay/overlay_background.c)10
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_curve.cc (renamed from source/blender/draw/engines/overlay/overlay_edit_curve.c)4
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_curves.cc2
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_mesh.cc (renamed from source/blender/draw/engines/overlay/overlay_edit_mesh.c)35
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_text.cc (renamed from source/blender/draw/engines/overlay/overlay_edit_text.c)52
-rw-r--r--source/blender/draw/engines/overlay/overlay_edit_uv.cc (renamed from source/blender/draw/engines/overlay/overlay_edit_uv.c)100
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.cc (renamed from source/blender/draw/engines/overlay/overlay_engine.c)305
-rw-r--r--source/blender/draw/engines/overlay/overlay_engine.h8
-rw-r--r--source/blender/draw/engines/overlay/overlay_extra.cc (renamed from source/blender/draw/engines/overlay/overlay_extra.c)78
-rw-r--r--source/blender/draw/engines/overlay/overlay_facing.cc (renamed from source/blender/draw/engines/overlay/overlay_facing.c)2
-rw-r--r--source/blender/draw/engines/overlay/overlay_fade.cc (renamed from source/blender/draw/engines/overlay/overlay_fade.c)2
-rw-r--r--source/blender/draw/engines/overlay/overlay_gpencil.cc (renamed from source/blender/draw/engines/overlay/overlay_gpencil.c)61
-rw-r--r--source/blender/draw/engines/overlay/overlay_grid.cc (renamed from source/blender/draw/engines/overlay/overlay_grid.c)32
-rw-r--r--source/blender/draw/engines/overlay/overlay_image.cc (renamed from source/blender/draw/engines/overlay/overlay_image.c)66
-rw-r--r--source/blender/draw/engines/overlay/overlay_lattice.cc (renamed from source/blender/draw/engines/overlay/overlay_lattice.c)2
-rw-r--r--source/blender/draw/engines/overlay/overlay_metaball.cc (renamed from source/blender/draw/engines/overlay/overlay_metaball.c)9
-rw-r--r--source/blender/draw/engines/overlay/overlay_mode_transfer.cc (renamed from source/blender/draw/engines/overlay/overlay_mode_transfer.c)2
-rw-r--r--source/blender/draw/engines/overlay/overlay_motion_path.cc (renamed from source/blender/draw/engines/overlay/overlay_motion_path.c)12
-rw-r--r--source/blender/draw/engines/overlay/overlay_outline.cc (renamed from source/blender/draw/engines/overlay/overlay_outline.c)37
-rw-r--r--source/blender/draw/engines/overlay/overlay_paint.cc (renamed from source/blender/draw/engines/overlay/overlay_paint.c)27
-rw-r--r--source/blender/draw/engines/overlay/overlay_particle.cc (renamed from source/blender/draw/engines/overlay/overlay_particle.c)22
-rw-r--r--source/blender/draw/engines/overlay/overlay_private.hh (renamed from source/blender/draw/engines/overlay/overlay_private.h)12
-rw-r--r--source/blender/draw/engines/overlay/overlay_sculpt.cc (renamed from source/blender/draw/engines/overlay/overlay_sculpt.c)4
-rw-r--r--source/blender/draw/engines/overlay/overlay_sculpt_curves.cc2
-rw-r--r--source/blender/draw/engines/overlay/overlay_shader.cc (renamed from source/blender/draw/engines/overlay/overlay_shader.c)10
-rw-r--r--source/blender/draw/engines/overlay/overlay_shader_shared.h3
-rw-r--r--source/blender/draw/engines/overlay/overlay_volume.cc (renamed from source/blender/draw/engines/overlay/overlay_volume.c)8
-rw-r--r--source/blender/draw/engines/overlay/overlay_wireframe.cc (renamed from source/blender/draw/engines/overlay/overlay_wireframe.c)32
-rw-r--r--source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh1
-rw-r--r--source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl2
-rw-r--r--source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl2
-rw-r--r--source/blender/draw/engines/overlay/shaders/overlay_edit_uv_stretching_vert.glsl6
-rw-r--r--source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl99
-rw-r--r--source/blender/draw/engines/select/select_engine.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c2
-rw-r--r--source/blender/draw/intern/DRW_gpu_wrapper.hh294
-rw-r--r--source/blender/draw/intern/DRW_render.h93
-rw-r--r--source/blender/draw/intern/draw_attributes.cc3
-rw-r--r--source/blender/draw/intern/draw_cache.c168
-rw-r--r--source/blender/draw/intern/draw_cache.h9
-rw-r--r--source/blender/draw/intern/draw_cache_extract.hh2
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh.cc2
-rw-r--r--source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc138
-rw-r--r--source/blender/draw/intern/draw_cache_impl.h38
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curves.cc31
-rw-r--r--source/blender/draw/intern/draw_cache_impl_displist.c354
-rw-r--r--source/blender/draw/intern/draw_cache_impl_lattice.c6
-rw-r--r--source/blender/draw/intern/draw_cache_impl_mesh.cc499
-rw-r--r--source/blender/draw/intern/draw_cache_impl_metaball.c294
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c76
-rw-r--r--source/blender/draw/intern/draw_cache_impl_pointcloud.cc (renamed from source/blender/draw/intern/draw_cache_impl_pointcloud.c)171
-rw-r--r--source/blender/draw/intern/draw_cache_impl_subdivision.cc140
-rw-r--r--source/blender/draw/intern/draw_color_management.cc2
-rw-r--r--source/blender/draw/intern/draw_command.cc600
-rw-r--r--source/blender/draw/intern/draw_command.hh534
-rw-r--r--source/blender/draw/intern/draw_command_shared.hh87
-rw-r--r--source/blender/draw/intern/draw_common.c10
-rw-r--r--source/blender/draw/intern/draw_common_shader_shared.h2
-rw-r--r--source/blender/draw/intern/draw_curves.cc236
-rw-r--r--source/blender/draw/intern/draw_debug.c196
-rw-r--r--source/blender/draw/intern/draw_debug.cc739
-rw-r--r--source/blender/draw/intern/draw_debug.h19
-rw-r--r--source/blender/draw/intern/draw_debug.hh198
-rw-r--r--source/blender/draw/intern/draw_defines.h27
-rw-r--r--source/blender/draw/intern/draw_hair.cc243
-rw-r--r--source/blender/draw/intern/draw_hair_private.h6
-rw-r--r--source/blender/draw/intern/draw_handle.hh59
-rw-r--r--source/blender/draw/intern/draw_instance_data.c24
-rw-r--r--source/blender/draw/intern/draw_instance_data.h2
-rw-r--r--source/blender/draw/intern/draw_manager.c101
-rw-r--r--source/blender/draw/intern/draw_manager.cc214
-rw-r--r--source/blender/draw/intern/draw_manager.h51
-rw-r--r--source/blender/draw/intern/draw_manager.hh237
-rw-r--r--source/blender/draw/intern/draw_manager_data.c117
-rw-r--r--source/blender/draw/intern/draw_manager_exec.c31
-rw-r--r--source/blender/draw/intern/draw_pass.hh1005
-rw-r--r--source/blender/draw/intern/draw_resource.cc109
-rw-r--r--source/blender/draw/intern/draw_resource.hh206
-rw-r--r--source/blender/draw/intern/draw_shader.cc54
-rw-r--r--source/blender/draw/intern/draw_shader.h6
-rw-r--r--source/blender/draw/intern/draw_shader_shared.h249
-rw-r--r--source/blender/draw/intern/draw_state.h225
-rw-r--r--source/blender/draw/intern/draw_subdivision.h1
-rw-r--r--source/blender/draw/intern/draw_texture_pool.cc13
-rw-r--r--source/blender/draw/intern/draw_texture_pool.h17
-rw-r--r--source/blender/draw/intern/draw_view.c4
-rw-r--r--source/blender/draw/intern/draw_view.cc334
-rw-r--r--source/blender/draw/intern/draw_view.hh94
-rw-r--r--source/blender/draw/intern/draw_view_data.cc56
-rw-r--r--source/blender/draw/intern/draw_view_data.h1
-rw-r--r--source/blender/draw/intern/draw_volume.cc4
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh.hh84
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc105
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc6
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc61
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc19
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc12
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc10
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc8
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc39
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc6
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc2
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc25
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc12
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc16
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc19
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc17
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc8
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc387
-rw-r--r--source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc8
-rw-r--r--source/blender/draw/intern/shaders/common_aabb_lib.glsl59
-rw-r--r--source/blender/draw/intern/shaders/common_attribute_lib.glsl1
-rw-r--r--source/blender/draw/intern/shaders/common_debug_draw_lib.glsl215
-rw-r--r--source/blender/draw/intern/shaders/common_debug_print_lib.glsl388
-rw-r--r--source/blender/draw/intern/shaders/common_debug_shape_lib.glsl57
-rw-r--r--source/blender/draw/intern/shaders/common_intersect_lib.glsl466
-rw-r--r--source/blender/draw/intern/shaders/common_math_geom_lib.glsl144
-rw-r--r--source/blender/draw/intern/shaders/common_math_lib.glsl14
-rw-r--r--source/blender/draw/intern/shaders/common_shape_lib.glsl202
-rw-r--r--source/blender/draw/intern/shaders/common_view_lib.glsl17
-rw-r--r--source/blender/draw/intern/shaders/draw_command_generate_comp.glsl84
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl9
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl15
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_info.hh56
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl133
-rw-r--r--source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl29
-rw-r--r--source/blender/draw/intern/shaders/draw_object_infos_info.hh20
-rw-r--r--source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl64
-rw-r--r--source/blender/draw/intern/shaders/draw_view_info.hh81
-rw-r--r--source/blender/draw/intern/shaders/draw_visibility_comp.glsl46
-rw-r--r--source/blender/draw/tests/draw_pass_test.cc441
-rw-r--r--source/blender/draw/tests/shaders_test.cc2
-rw-r--r--source/blender/editors/animation/CMakeLists.txt1
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c15
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c26
-rw-r--r--source/blender/editors/animation/anim_deps.c3
-rw-r--r--source/blender/editors/animation/anim_draw.c9
-rw-r--r--source/blender/editors/animation/anim_filter.c54
-rw-r--r--source/blender/editors/animation/anim_ipo_utils.c1
-rw-r--r--source/blender/editors/animation/anim_markers.c135
-rw-r--r--source/blender/editors/animation/drivers.c3
-rw-r--r--source/blender/editors/animation/fmodifier_ui.c2
-rw-r--r--source/blender/editors/animation/keyframes_draw.c2
-rw-r--r--source/blender/editors/animation/keyframes_edit.c13
-rw-r--r--source/blender/editors/animation/keyframes_general.c84
-rw-r--r--source/blender/editors/animation/keyframes_keylist.cc7
-rw-r--r--source/blender/editors/animation/keyframing.c20
-rw-r--r--source/blender/editors/animation/keyingsets.c5
-rw-r--r--source/blender/editors/animation/time_scrub_ui.c6
-rw-r--r--source/blender/editors/armature/CMakeLists.txt1
-rw-r--r--source/blender/editors/armature/armature_add.c11
-rw-r--r--source/blender/editors/armature/armature_edit.c27
-rw-r--r--source/blender/editors/armature/armature_naming.c18
-rw-r--r--source/blender/editors/armature/armature_relations.c5
-rw-r--r--source/blender/editors/armature/armature_select.c55
-rw-r--r--source/blender/editors/armature/armature_skinning.c8
-rw-r--r--source/blender/editors/armature/editarmature_undo.c7
-rw-r--r--source/blender/editors/armature/meshlaplacian.c30
-rw-r--r--source/blender/editors/armature/pose_edit.c16
-rw-r--r--source/blender/editors/armature/pose_lib.c8
-rw-r--r--source/blender/editors/armature/pose_select.c43
-rw-r--r--source/blender/editors/armature/pose_slide.c16
-rw-r--r--source/blender/editors/armature/pose_transform.c9
-rw-r--r--source/blender/editors/armature/pose_utils.c5
-rw-r--r--source/blender/editors/asset/ED_asset_handle.h13
-rw-r--r--source/blender/editors/asset/ED_asset_list.h2
-rw-r--r--source/blender/editors/asset/intern/asset_handle.cc30
-rw-r--r--source/blender/editors/asset/intern/asset_indexer.cc16
-rw-r--r--source/blender/editors/asset/intern/asset_library_reference_enum.cc6
-rw-r--r--source/blender/editors/asset/intern/asset_list.cc7
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc3
-rw-r--r--source/blender/editors/curve/CMakeLists.txt1
-rw-r--r--source/blender/editors/curve/editcurve.c498
-rw-r--r--source/blender/editors/curve/editcurve_add.c6
-rw-r--r--source/blender/editors/curve/editcurve_paint.c2
-rw-r--r--source/blender/editors/curve/editcurve_pen.c4
-rw-r--r--source/blender/editors/curve/editcurve_query.c2
-rw-r--r--source/blender/editors/curve/editcurve_select.c98
-rw-r--r--source/blender/editors/curve/editcurve_undo.c7
-rw-r--r--source/blender/editors/curve/editfont.c83
-rw-r--r--source/blender/editors/curve/editfont_undo.c5
-rw-r--r--source/blender/editors/curves/CMakeLists.txt1
-rw-r--r--source/blender/editors/curves/intern/curves_add.cc15
-rw-r--r--source/blender/editors/curves/intern/curves_ops.cc171
-rw-r--r--source/blender/editors/geometry/CMakeLists.txt1
-rw-r--r--source/blender/editors/geometry/geometry_attributes.cc18
-rw-r--r--source/blender/editors/gizmo_library/CMakeLists.txt1
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c2
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c6
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt2
-rw-r--r--source/blender/editors/gpencil/annotate_paint.c12
-rw-r--r--source/blender/editors/gpencil/gpencil_add_blank.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_add_lineart.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c18
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c18
-rw-r--r--source/blender/editors/gpencil/gpencil_armature.c13
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.cc2
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c7
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c565
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c390
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h2
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c13
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.cc4
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_ops_versioning.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c103
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c14
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_ops.c31
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c19
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c2
-rw-r--r--source/blender/editors/include/ED_anim_api.h21
-rw-r--r--source/blender/editors/include/ED_armature.h9
-rw-r--r--source/blender/editors/include/ED_curves.h6
-rw-r--r--source/blender/editors/include/ED_curves_sculpt.h25
-rw-r--r--source/blender/editors/include/ED_fileselect.h8
-rw-r--r--source/blender/editors/include/ED_gpencil.h8
-rw-r--r--source/blender/editors/include/ED_image.h5
-rw-r--r--source/blender/editors/include/ED_keyframes_edit.h5
-rw-r--r--source/blender/editors/include/ED_mesh.h45
-rw-r--r--source/blender/editors/include/ED_object.h16
-rw-r--r--source/blender/editors/include/ED_paint.h7
-rw-r--r--source/blender/editors/include/ED_screen.h6
-rw-r--r--source/blender/editors/include/ED_screen_types.h2
-rw-r--r--source/blender/editors/include/ED_sculpt.h11
-rw-r--r--source/blender/editors/include/ED_space_api.h2
-rw-r--r--source/blender/editors/include/ED_transform.h3
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h6
-rw-r--r--source/blender/editors/include/ED_types.h27
-rw-r--r--source/blender/editors/include/ED_undo.h8
-rw-r--r--source/blender/editors/include/ED_uvedit.h45
-rw-r--r--source/blender/editors/include/ED_view3d.h36
-rw-r--r--source/blender/editors/include/UI_abstract_view.hh217
-rw-r--r--source/blender/editors/include/UI_grid_view.hh35
-rw-r--r--source/blender/editors/include/UI_interface.h85
-rw-r--r--source/blender/editors/include/UI_interface.hh13
-rw-r--r--source/blender/editors/include/UI_resources.h2
-rw-r--r--source/blender/editors/include/UI_tree_view.hh151
-rw-r--r--source/blender/editors/include/UI_view2d.h14
-rw-r--r--source/blender/editors/interface/CMakeLists.txt42
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_color.c (renamed from source/blender/editors/interface/interface_eyedropper_color.c)4
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_colorband.c (renamed from source/blender/editors/interface/interface_eyedropper_colorband.c)2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_datablock.c (renamed from source/blender/editors/interface/interface_eyedropper_datablock.c)2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_depth.c (renamed from source/blender/editors/interface/interface_eyedropper_depth.c)2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_driver.c (renamed from source/blender/editors/interface/interface_eyedropper_driver.c)7
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c (renamed from source/blender/editors/interface/interface_eyedropper_gpencil_color.c)2
-rw-r--r--source/blender/editors/interface/eyedroppers/eyedropper_intern.h (renamed from source/blender/editors/interface/interface_eyedropper_intern.h)4
-rw-r--r--source/blender/editors/interface/eyedroppers/interface_eyedropper.c (renamed from source/blender/editors/interface/interface_eyedropper.c)4
-rw-r--r--source/blender/editors/interface/interface.cc102
-rw-r--r--source/blender/editors/interface/interface_anim.cc (renamed from source/blender/editors/interface/interface_anim.c)57
-rw-r--r--source/blender/editors/interface/interface_context_menu.c18
-rw-r--r--source/blender/editors/interface/interface_drag.cc10
-rw-r--r--source/blender/editors/interface/interface_draw.c50
-rw-r--r--source/blender/editors/interface/interface_dropboxes.cc32
-rw-r--r--source/blender/editors/interface/interface_handlers.c152
-rw-r--r--source/blender/editors/interface/interface_icons.c10
-rw-r--r--source/blender/editors/interface/interface_icons_event.c100
-rw-r--r--source/blender/editors/interface/interface_intern.h35
-rw-r--r--source/blender/editors/interface/interface_layout.c9
-rw-r--r--source/blender/editors/interface/interface_ops.cc (renamed from source/blender/editors/interface/interface_ops.c)649
-rw-r--r--source/blender/editors/interface/interface_panel.cc (renamed from source/blender/editors/interface/interface_panel.c)363
-rw-r--r--source/blender/editors/interface/interface_query.cc32
-rw-r--r--source/blender/editors/interface/interface_region_color_picker.cc4
-rw-r--r--source/blender/editors/interface/interface_region_menu_pie.cc5
-rw-r--r--source/blender/editors/interface/interface_region_menu_popup.cc4
-rw-r--r--source/blender/editors/interface/interface_region_popover.cc4
-rw-r--r--source/blender/editors/interface/interface_region_popup.cc4
-rw-r--r--source/blender/editors/interface/interface_region_search.cc26
-rw-r--r--source/blender/editors/interface/interface_region_tooltip.cc (renamed from source/blender/editors/interface/interface_region_tooltip.c)694
-rw-r--r--source/blender/editors/interface/interface_regions.cc4
-rw-r--r--source/blender/editors/interface/interface_regions_intern.hh (renamed from source/blender/editors/interface/interface_regions_intern.h)15
-rw-r--r--source/blender/editors/interface/interface_template_asset_view.cc4
-rw-r--r--source/blender/editors/interface/interface_template_attribute_search.cc6
-rw-r--r--source/blender/editors/interface/interface_template_list.cc22
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.cc1
-rw-r--r--source/blender/editors/interface/interface_template_search_operator.cc (renamed from source/blender/editors/interface/interface_template_search_operator.c)26
-rw-r--r--source/blender/editors/interface/interface_templates.c209
-rw-r--r--source/blender/editors/interface/interface_undo.cc (renamed from source/blender/editors/interface/interface_undo.c)33
-rw-r--r--source/blender/editors/interface/interface_utils.cc2
-rw-r--r--source/blender/editors/interface/interface_widgets.c96
-rw-r--r--source/blender/editors/interface/resources.c6
-rw-r--r--source/blender/editors/interface/view2d.cc91
-rw-r--r--source/blender/editors/interface/view2d_draw.cc2
-rw-r--r--source/blender/editors/interface/view2d_ops.cc6
-rw-r--r--source/blender/editors/interface/views/abstract_view.cc (renamed from source/blender/editors/interface/abstract_view.cc)7
-rw-r--r--source/blender/editors/interface/views/abstract_view_item.cc373
-rw-r--r--source/blender/editors/interface/views/grid_view.cc (renamed from source/blender/editors/interface/grid_view.cc)55
-rw-r--r--source/blender/editors/interface/views/interface_view.cc (renamed from source/blender/editors/interface/interface_view.cc)57
-rw-r--r--source/blender/editors/interface/views/tree_view.cc (renamed from source/blender/editors/interface/tree_view.cc)361
-rw-r--r--source/blender/editors/io/CMakeLists.txt6
-rw-r--r--source/blender/editors/io/io_alembic.c40
-rw-r--r--source/blender/editors/io/io_collada.c32
-rw-r--r--source/blender/editors/io/io_gpencil_export.c32
-rw-r--r--source/blender/editors/io/io_gpencil_import.c45
-rw-r--r--source/blender/editors/io/io_obj.c176
-rw-r--r--source/blender/editors/io/io_stl_ops.c4
-rw-r--r--source/blender/editors/io/io_usd.c48
-rw-r--r--source/blender/editors/lattice/editlattice_select.c24
-rw-r--r--source/blender/editors/lattice/editlattice_tools.c6
-rw-r--r--source/blender/editors/lattice/editlattice_undo.c9
-rw-r--r--source/blender/editors/mask/CMakeLists.txt1
-rw-r--r--source/blender/editors/mask/mask_draw.c10
-rw-r--r--source/blender/editors/mask/mask_query.c3
-rw-r--r--source/blender/editors/mask/mask_shapekey.c4
-rw-r--r--source/blender/editors/mesh/CMakeLists.txt1
-rw-r--r--source/blender/editors/mesh/editface.cc287
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c4
-rw-r--r--source/blender/editors/mesh/editmesh_bisect.c5
-rw-r--r--source/blender/editors/mesh/editmesh_extrude.c24
-rw-r--r--source/blender/editors/mesh/editmesh_extrude_screw.c9
-rw-r--r--source/blender/editors/mesh/editmesh_extrude_spin.c3
-rw-r--r--source/blender/editors/mesh/editmesh_inset.c2
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c9
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c23
-rw-r--r--source/blender/editors/mesh/editmesh_knife_project.c2
-rw-r--r--source/blender/editors/mesh/editmesh_loopcut.c4
-rw-r--r--source/blender/editors/mesh/editmesh_mask_extract.c8
-rw-r--r--source/blender/editors/mesh/editmesh_path.c16
-rw-r--r--source/blender/editors/mesh/editmesh_polybuild.c40
-rw-r--r--source/blender/editors/mesh/editmesh_rip.c3
-rw-r--r--source/blender/editors/mesh/editmesh_rip_edge.c3
-rw-r--r--source/blender/editors/mesh/editmesh_select.c80
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c12
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c194
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c16
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c704
-rw-r--r--source/blender/editors/mesh/mesh_data.cc547
-rw-r--r--source/blender/editors/mesh/mesh_intern.h14
-rw-r--r--source/blender/editors/mesh/mesh_mirror.c13
-rw-r--r--source/blender/editors/mesh/mesh_ops.c8
-rw-r--r--source/blender/editors/mesh/meshtools.cc139
-rw-r--r--source/blender/editors/metaball/editmball_undo.c7
-rw-r--r--source/blender/editors/metaball/mball_edit.c27
-rw-r--r--source/blender/editors/object/CMakeLists.txt4
-rw-r--r--source/blender/editors/object/object_add.cc133
-rw-r--r--source/blender/editors/object/object_bake.c5
-rw-r--r--source/blender/editors/object/object_bake_api.c133
-rw-r--r--source/blender/editors/object/object_collection.c4
-rw-r--r--source/blender/editors/object/object_constraint.c17
-rw-r--r--source/blender/editors/object/object_data_transfer.c8
-rw-r--r--source/blender/editors/object/object_data_transform.c2
-rw-r--r--source/blender/editors/object/object_edit.c73
-rw-r--r--source/blender/editors/object/object_facemap_ops.c2
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c3
-rw-r--r--source/blender/editors/object/object_hook.c18
-rw-r--r--source/blender/editors/object/object_intern.h8
-rw-r--r--source/blender/editors/object/object_modes.c10
-rw-r--r--source/blender/editors/object/object_modifier.cc88
-rw-r--r--source/blender/editors/object/object_ops.c5
-rw-r--r--source/blender/editors/object/object_random.c4
-rw-r--r--source/blender/editors/object/object_relations.c200
-rw-r--r--source/blender/editors/object/object_remesh.cc53
-rw-r--r--source/blender/editors/object/object_select.c72
-rw-r--r--source/blender/editors/object/object_shader_fx.c9
-rw-r--r--source/blender/editors/object/object_shapekey.c46
-rw-r--r--source/blender/editors/object/object_transform.cc7
-rw-r--r--source/blender/editors/object/object_utils.c9
-rw-r--r--source/blender/editors/object/object_vgroup.cc (renamed from source/blender/editors/object/object_vgroup.c)600
-rw-r--r--source/blender/editors/physics/CMakeLists.txt1
-rw-r--r--source/blender/editors/physics/dynamicpaint_ops.c3
-rw-r--r--source/blender/editors/physics/particle_edit.c31
-rw-r--r--source/blender/editors/physics/particle_edit_undo.c7
-rw-r--r--source/blender/editors/physics/particle_object.c12
-rw-r--r--source/blender/editors/physics/physics_fluid.c1
-rw-r--r--source/blender/editors/physics/rigidbody_constraint.c9
-rw-r--r--source/blender/editors/render/CMakeLists.txt1
-rw-r--r--source/blender/editors/render/render_internal.cc8
-rw-r--r--source/blender/editors/render/render_opengl.cc58
-rw-r--r--source/blender/editors/render/render_preview.cc78
-rw-r--r--source/blender/editors/render/render_shading.cc26
-rw-r--r--source/blender/editors/render/render_update.cc26
-rw-r--r--source/blender/editors/render/render_view.cc12
-rw-r--r--source/blender/editors/scene/scene_edit.c2
-rw-r--r--source/blender/editors/screen/CMakeLists.txt1
-rw-r--r--source/blender/editors/screen/area.c43
-rw-r--r--source/blender/editors/screen/glutil.c7
-rw-r--r--source/blender/editors/screen/screen_context.c175
-rw-r--r--source/blender/editors/screen/screen_draw.c6
-rw-r--r--source/blender/editors/screen/screen_edit.c17
-rw-r--r--source/blender/editors/screen/screen_geometry.c2
-rw-r--r--source/blender/editors/screen/screen_intern.h2
-rw-r--r--source/blender/editors/screen/screen_ops.c27
-rw-r--r--source/blender/editors/screen/screen_user_menu.c10
-rw-r--r--source/blender/editors/screen/screendump.c3
-rw-r--r--source/blender/editors/screen/workspace_edit.c23
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt5
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_add.cc277
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_brush.cc93
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_comb.cc107
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_delete.cc39
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_density.cc422
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc102
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_intern.hh35
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_ops.cc91
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc46
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_puff.cc54
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_selection.cc54
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc25
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_slide.cc485
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc106
-rw-r--r--source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc48
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c27
-rw-r--r--source/blender/editors/sculpt_paint/paint_hide.c22
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.cc23
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_ops_paint.cc6
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c45
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h9
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c69
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c30
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c54
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c86
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.cc143
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc24
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c69
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c706
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.cc95
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_boundary.c321
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_brush_types.c146
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c73
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_dyntopo.c60
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_expand.c377
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.cc (renamed from source/blender/editors/sculpt_paint/sculpt_face_set.c)575
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c21
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c29
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c42
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_geodesic.c80
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h169
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_expand.c58
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_mask_init.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_ops.c299
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c73
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_image.cc12
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_pose.c108
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_smooth.c87
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_transform.c18
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_undo.c212
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_uv.c438
-rw-r--r--source/blender/editors/sound/sound_ops.c15
-rw-r--r--source/blender/editors/space_action/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_action/action_data.c5
-rw-r--r--source/blender/editors/space_action/action_draw.c4
-rw-r--r--source/blender/editors/space_action/action_edit.c18
-rw-r--r--source/blender/editors/space_action/action_select.c11
-rw-r--r--source/blender/editors/space_action/space_action.c16
-rw-r--r--source/blender/editors/space_api/spacetypes.c1
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c19
-rw-r--r--source/blender/editors/space_buttons/buttons_intern.h2
-rw-r--r--source/blender/editors/space_buttons/buttons_ops.c6
-rw-r--r--source/blender/editors/space_buttons/buttons_texture.c3
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c11
-rw-r--r--source/blender/editors/space_clip/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_clip/clip_buttons.c2
-rw-r--r--source/blender/editors/space_clip/clip_dopesheet_draw.c4
-rw-r--r--source/blender/editors/space_clip/clip_draw.c26
-rw-r--r--source/blender/editors/space_clip/clip_graph_draw.c2
-rw-r--r--source/blender/editors/space_clip/clip_utils.c2
-rw-r--r--source/blender/editors/space_clip/space_clip.c16
-rw-r--r--source/blender/editors/space_clip/tracking_ops.c1
-rw-r--r--source/blender/editors/space_clip/tracking_ops_orient.c7
-rw-r--r--source/blender/editors/space_console/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_console/console_draw.c2
-rw-r--r--source/blender/editors/space_console/console_intern.h2
-rw-r--r--source/blender/editors/space_console/console_ops.c14
-rw-r--r--source/blender/editors/space_console/space_console.c11
-rw-r--r--source/blender/editors/space_file/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_file/asset_catalog_tree_view.cc58
-rw-r--r--source/blender/editors/space_file/file_draw.c14
-rw-r--r--source/blender/editors/space_file/file_intern.h23
-rw-r--r--source/blender/editors/space_file/file_ops.c176
-rw-r--r--source/blender/editors/space_file/filelist.cc (renamed from source/blender/editors/space_file/filelist.c)405
-rw-r--r--source/blender/editors/space_file/filelist.h11
-rw-r--r--source/blender/editors/space_file/filesel.c30
-rw-r--r--source/blender/editors/space_file/folder_history.cc191
-rw-r--r--source/blender/editors/space_file/fsmenu.c21
-rw-r--r--source/blender/editors/space_file/fsmenu.h7
-rw-r--r--source/blender/editors/space_file/space_file.c8
-rw-r--r--source/blender/editors/space_graph/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c11
-rw-r--r--source/blender/editors/space_graph/graph_draw.c16
-rw-r--r--source/blender/editors/space_graph/graph_edit.c20
-rw-r--r--source/blender/editors/space_graph/graph_select.c9
-rw-r--r--source/blender/editors/space_graph/space_graph.c11
-rw-r--r--source/blender/editors/space_image/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_image/image_buttons.c10
-rw-r--r--source/blender/editors/space_image/image_draw.c37
-rw-r--r--source/blender/editors/space_image/image_edit.c14
-rw-r--r--source/blender/editors/space_image/image_ops.c58
-rw-r--r--source/blender/editors/space_image/image_undo.cc (renamed from source/blender/editors/space_image/image_undo.c)350
-rw-r--r--source/blender/editors/space_image/space_image.c24
-rw-r--r--source/blender/editors/space_info/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_info/info_stats.cc83
-rw-r--r--source/blender/editors/space_info/space_info.c6
-rw-r--r--source/blender/editors/space_info/textview.c4
-rw-r--r--source/blender/editors/space_nla/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c11
-rw-r--r--source/blender/editors/space_nla/nla_channels.c6
-rw-r--r--source/blender/editors/space_nla/nla_draw.c42
-rw-r--r--source/blender/editors/space_nla/nla_edit.c66
-rw-r--r--source/blender/editors/space_nla/nla_ops.c24
-rw-r--r--source/blender/editors/space_nla/nla_select.c2
-rw-r--r--source/blender/editors/space_nla/space_nla.c14
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt6
-rw-r--r--source/blender/editors/space_node/add_node_search.cc312
-rw-r--r--source/blender/editors/space_node/drawnode.cc582
-rw-r--r--source/blender/editors/space_node/link_drag_search.cc177
-rw-r--r--source/blender/editors/space_node/node_add.cc348
-rw-r--r--source/blender/editors/space_node/node_context_path.cc31
-rw-r--r--source/blender/editors/space_node/node_draw.cc586
-rw-r--r--source/blender/editors/space_node/node_edit.cc235
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc65
-rw-r--r--source/blender/editors/space_node/node_gizmo.cc45
-rw-r--r--source/blender/editors/space_node/node_group.cc43
-rw-r--r--source/blender/editors/space_node/node_intern.hh53
-rw-r--r--source/blender/editors/space_node/node_ops.cc5
-rw-r--r--source/blender/editors/space_node/node_relationships.cc533
-rw-r--r--source/blender/editors/space_node/node_select.cc248
-rw-r--r--source/blender/editors/space_node/node_templates.cc67
-rw-r--r--source/blender/editors/space_node/node_view.cc22
-rw-r--r--source/blender/editors/space_node/space_node.cc16
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt3
-rw-r--r--source/blender/editors/space_outliner/outliner_collections.cc158
-rw-r--r--source/blender/editors/space_outliner/outliner_context.cc4
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.cc77
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.cc189
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.cc164
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.hh115
-rw-r--r--source/blender/editors/space_outliner/outliner_ops.cc5
-rw-r--r--source/blender/editors/space_outliner/outliner_query.cc4
-rw-r--r--source/blender/editors/space_outliner/outliner_select.cc177
-rw-r--r--source/blender/editors/space_outliner/outliner_sync.cc42
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.cc1403
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.cc152
-rw-r--r--source/blender/editors/space_outliner/outliner_utils.cc39
-rw-r--r--source/blender/editors/space_outliner/space_outliner.cc37
-rw-r--r--source/blender/editors/space_outliner/tree/common.cc6
-rw-r--r--source/blender/editors/space_outliner/tree/common.hh4
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.cc5
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display.hh16
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_data.cc5
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc89
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_view_layer.cc5
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.cc43
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element.hh24
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_anim_data.hh2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_driver.hh2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_label.cc36
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_label.hh36
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.cc393
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_overrides.hh40
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_rna.cc15
-rw-r--r--source/blender/editors/space_outliner/tree/tree_iterator.hh4
-rw-r--r--source/blender/editors/space_script/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_script/script_edit.c2
-rw-r--r--source/blender/editors/space_script/space_script.c2
-rw-r--r--source/blender/editors/space_sequencer/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_drag_drop.c295
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c547
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c28
-rw-r--r--source/blender/editors/space_sequencer/sequencer_scopes.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c12
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c12
-rw-r--r--source/blender/editors/space_spreadsheet/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc8
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc233
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh4
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_draw.cc2
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_layout.cc4
-rw-r--r--source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc9
-rw-r--r--source/blender/editors/space_statusbar/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_statusbar/space_statusbar.c4
-rw-r--r--source/blender/editors/space_text/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_text/space_text.c7
-rw-r--r--source/blender/editors/space_text/text_autocomplete.c2
-rw-r--r--source/blender/editors/space_text/text_draw.c24
-rw-r--r--source/blender/editors/space_text/text_format_py.c2
-rw-r--r--source/blender/editors/space_text/text_ops.c19
-rw-r--r--source/blender/editors/space_topbar/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_topbar/space_topbar.c6
-rw-r--r--source/blender/editors/space_userpref/space_userpref.c2
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt5
-rw-r--r--source/blender/editors/space_view3d/drawobject.c25
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c40
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c32
-rw-r--r--source/blender/editors/space_view3d/view3d_cursor_snap.c151
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c29
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_armature.c14
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_camera.c20
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_empty.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_forcefield.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_light.c24
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c17
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_ruler.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_header.c9
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h10
-rw-r--r--source/blender/editors/space_view3d/view3d_iterators.c27
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate.c112
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate.h24
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_dolly.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_fly.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_move.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_ndof.c7
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_roll.c13
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_rotate.c3
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_smoothview.c278
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_walk.c10
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_zoom.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_navigate_zoom_border.c11
-rw-r--r--source/blender/editors/space_view3d/view3d_select.cc (renamed from source/blender/editors/space_view3d/view3d_select.c)831
-rw-r--r--source/blender/editors/space_view3d/view3d_snap.c14
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c60
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c54
-rw-r--r--source/blender/editors/transform/CMakeLists.txt2
-rw-r--r--source/blender/editors/transform/transform.c26
-rw-r--r--source/blender/editors/transform/transform.h47
-rw-r--r--source/blender/editors/transform/transform_constraints.c44
-rw-r--r--source/blender/editors/transform/transform_constraints.h2
-rw-r--r--source/blender/editors/transform/transform_convert.c750
-rw-r--r--source/blender/editors/transform/transform_convert.h137
-rw-r--r--source/blender/editors/transform/transform_convert_action.c24
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c26
-rw-r--r--source/blender/editors/transform/transform_convert_cursor.c33
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c11
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c15
-rw-r--r--source/blender/editors/transform/transform_convert_graph.c31
-rw-r--r--source/blender/editors/transform/transform_convert_lattice.c11
-rw-r--r--source/blender/editors/transform/transform_convert_mask.c13
-rw-r--r--source/blender/editors/transform/transform_convert_mball.c11
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c42
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_edge.c19
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_skin.c11
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c54
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_vert_cdata.c298
-rw-r--r--source/blender/editors/transform/transform_convert_nla.c13
-rw-r--r--source/blender/editors/transform/transform_convert_node.c49
-rw-r--r--source/blender/editors/transform/transform_convert_object.c70
-rw-r--r--source/blender/editors/transform/transform_convert_object_texspace.c15
-rw-r--r--source/blender/editors/transform/transform_convert_paintcurve.c11
-rw-r--r--source/blender/editors/transform/transform_convert_particle.c20
-rw-r--r--source/blender/editors/transform/transform_convert_sculpt.c25
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer.c17
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer_image.c13
-rw-r--r--source/blender/editors/transform/transform_convert_tracking.c13
-rw-r--r--source/blender/editors/transform/transform_draw_cursors.c2
-rw-r--r--source/blender/editors/transform/transform_generics.c97
-rw-r--r--source/blender/editors/transform/transform_gizmo_2d.c2
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c17
-rw-r--r--source/blender/editors/transform/transform_gizmo_extrude_3d.c4
-rw-r--r--source/blender/editors/transform/transform_input.c69
-rw-r--r--source/blender/editors/transform/transform_mode.c13
-rw-r--r--source/blender/editors/transform/transform_mode.h2
-rw-r--r--source/blender/editors/transform/transform_mode_bend.c2
-rw-r--r--source/blender/editors/transform/transform_mode_curveshrinkfatten.c10
-rw-r--r--source/blender/editors/transform/transform_mode_edge_bevelweight.c11
-rw-r--r--source/blender/editors/transform/transform_mode_edge_crease.c11
-rw-r--r--source/blender/editors/transform/transform_mode_edge_slide.c160
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c2
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c2
-rw-r--r--source/blender/editors/transform/transform_mode_maskshrinkfatten.c2
-rw-r--r--source/blender/editors/transform/transform_mode_resize.c95
-rw-r--r--source/blender/editors/transform/transform_mode_rotate.c83
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c45
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c26
-rw-r--r--source/blender/editors/transform/transform_ops.c62
-rw-r--r--source/blender/editors/transform/transform_orientations.c19
-rw-r--r--source/blender/editors/transform/transform_orientations.h3
-rw-r--r--source/blender/editors/transform/transform_snap.c244
-rw-r--r--source/blender/editors/transform/transform_snap_object.cc43
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c5
-rw-r--r--source/blender/editors/undo/CMakeLists.txt1
-rw-r--r--source/blender/editors/undo/ed_undo.c54
-rw-r--r--source/blender/editors/util/CMakeLists.txt2
-rw-r--r--source/blender/editors/util/ed_draw.c24
-rw-r--r--source/blender/editors/util/ed_util.c21
-rw-r--r--source/blender/editors/util/ed_util_imbuf.c4
-rw-r--r--source/blender/editors/util/numinput.c13
-rw-r--r--source/blender/editors/util/select_utils.c16
-rw-r--r--source/blender/editors/uvedit/CMakeLists.txt3
-rw-r--r--source/blender/editors/uvedit/uvedit_buttons.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h3
-rw-r--r--source/blender/editors/uvedit/uvedit_islands.cc (renamed from source/blender/editors/uvedit/uvedit_islands.c)95
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c67
-rw-r--r--source/blender/editors/uvedit/uvedit_path.c313
-rw-r--r--source/blender/editors/uvedit/uvedit_rip.c6
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c432
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c193
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c146
-rw-r--r--source/blender/freestyle/CMakeLists.txt5
-rw-r--r--source/blender/freestyle/intern/application/Controller.h4
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp46
-rw-r--r--source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp82
-rw-r--r--source/blender/freestyle/intern/geometry/FastGrid.h4
-rw-r--r--source/blender/freestyle/intern/geometry/Grid.h2
-rw-r--r--source/blender/freestyle/intern/geometry/HashGrid.h2
-rw-r--r--source/blender/freestyle/intern/view_map/ViewMap.cpp2
-rw-r--r--source/blender/functions/CMakeLists.txt9
-rw-r--r--source/blender/functions/FN_field.hh22
-rw-r--r--source/blender/functions/FN_field_cpp_type.hh2
-rw-r--r--source/blender/functions/FN_lazy_function.hh442
-rw-r--r--source/blender/functions/FN_lazy_function_execute.hh125
-rw-r--r--source/blender/functions/FN_lazy_function_graph.hh421
-rw-r--r--source/blender/functions/FN_lazy_function_graph_executor.hh98
-rw-r--r--source/blender/functions/FN_multi_function.hh1
-rw-r--r--source/blender/functions/FN_multi_function_builder.hh5
-rw-r--r--source/blender/functions/intern/cpp_types.cc3
-rw-r--r--source/blender/functions/intern/field.cc48
-rw-r--r--source/blender/functions/intern/lazy_function.cc71
-rw-r--r--source/blender/functions/intern/lazy_function_execute.cc70
-rw-r--r--source/blender/functions/intern/lazy_function_graph.cc181
-rw-r--r--source/blender/functions/intern/lazy_function_graph_executor.cc1240
-rw-r--r--source/blender/functions/tests/FN_lazy_function_test.cc115
-rw-r--r--source/blender/geometry/CMakeLists.txt6
-rw-r--r--source/blender/geometry/GEO_add_curves_on_mesh.hh29
-rw-r--r--source/blender/geometry/GEO_fillet_curves.hh23
-rw-r--r--source/blender/geometry/GEO_mesh_to_curve.hh17
-rw-r--r--source/blender/geometry/GEO_point_merge_by_distance.hh2
-rw-r--r--source/blender/geometry/GEO_resample_curves.hh31
-rw-r--r--source/blender/geometry/GEO_set_curve_type.hh18
-rw-r--r--source/blender/geometry/GEO_subdivide_curves.hh10
-rw-r--r--source/blender/geometry/GEO_trim_curves.hh36
-rw-r--r--source/blender/geometry/GEO_uv_parametrizer.h9
-rw-r--r--source/blender/geometry/intern/add_curves_on_mesh.cc216
-rw-r--r--source/blender/geometry/intern/fillet_curves.cc561
-rw-r--r--source/blender/geometry/intern/mesh_merge_by_distance.cc73
-rw-r--r--source/blender/geometry/intern/mesh_primitive_cuboid.cc11
-rw-r--r--source/blender/geometry/intern/mesh_to_curve_convert.cc47
-rw-r--r--source/blender/geometry/intern/mesh_to_volume.cc9
-rw-r--r--source/blender/geometry/intern/point_merge_by_distance.cc22
-rw-r--r--source/blender/geometry/intern/realize_instances.cc240
-rw-r--r--source/blender/geometry/intern/resample_curves.cc274
-rw-r--r--source/blender/geometry/intern/reverse_uv_sampler.cc20
-rw-r--r--source/blender/geometry/intern/set_curve_type.cc200
-rw-r--r--source/blender/geometry/intern/subdivide_curves.cc99
-rw-r--r--source/blender/geometry/intern/trim_curves.cc1299
-rw-r--r--source/blender/geometry/intern/uv_parametrizer.cc (renamed from source/blender/geometry/intern/uv_parametrizer.c)554
-rw-r--r--source/blender/gpencil_modifiers/CMakeLists.txt2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencildash.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillength.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c24
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c14
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c7
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c2
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c4
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h20
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c32
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c174
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h1
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c9
-rw-r--r--source/blender/gpu/CMakeLists.txt200
-rw-r--r--source/blender/gpu/GPU_batch.h31
-rw-r--r--source/blender/gpu/GPU_buffers.h12
-rw-r--r--source/blender/gpu/GPU_capabilities.h6
-rw-r--r--source/blender/gpu/GPU_compute.h2
-rw-r--r--source/blender/gpu/GPU_context.h8
-rw-r--r--source/blender/gpu/GPU_framebuffer.h6
-rw-r--r--source/blender/gpu/GPU_glew.h15
-rw-r--r--source/blender/gpu/GPU_index_buffer.h7
-rw-r--r--source/blender/gpu/GPU_legacy_stubs.h497
-rw-r--r--source/blender/gpu/GPU_material.h60
-rw-r--r--source/blender/gpu/GPU_primitive.h74
-rw-r--r--source/blender/gpu/GPU_shader.h40
-rw-r--r--source/blender/gpu/GPU_shader_shared_utils.h29
-rw-r--r--source/blender/gpu/GPU_storage_buffer.h7
-rw-r--r--source/blender/gpu/GPU_texture.h8
-rw-r--r--source/blender/gpu/GPU_vertex_buffer.h16
-rw-r--r--source/blender/gpu/intern/gpu_backend.hh1
-rw-r--r--source/blender/gpu/intern/gpu_batch.cc50
-rw-r--r--source/blender/gpu/intern/gpu_batch_private.hh5
-rw-r--r--source/blender/gpu/intern/gpu_buffers.c106
-rw-r--r--source/blender/gpu/intern/gpu_capabilities.cc25
-rw-r--r--source/blender/gpu/intern/gpu_capabilities_private.hh1
-rw-r--r--source/blender/gpu/intern/gpu_codegen.cc96
-rw-r--r--source/blender/gpu/intern/gpu_context.cc53
-rw-r--r--source/blender/gpu/intern/gpu_context_private.hh28
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer.cc5
-rw-r--r--source/blender/gpu/intern/gpu_framebuffer_private.hh4
-rw-r--r--source/blender/gpu/intern/gpu_immediate.cc3
-rw-r--r--source/blender/gpu/intern/gpu_immediate_private.hh6
-rw-r--r--source/blender/gpu/intern/gpu_immediate_util.c6
-rw-r--r--source/blender/gpu/intern/gpu_index_buffer.cc105
-rw-r--r--source/blender/gpu/intern/gpu_index_buffer_private.hh21
-rw-r--r--source/blender/gpu/intern/gpu_init_exit.c2
-rw-r--r--source/blender/gpu/intern/gpu_material.c135
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.c165
-rw-r--r--source/blender/gpu/intern/gpu_node_graph.h10
-rw-r--r--source/blender/gpu/intern/gpu_platform.cc14
-rw-r--r--source/blender/gpu/intern/gpu_private.h4
-rw-r--r--source/blender/gpu/intern/gpu_shader.cc48
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder.cc1
-rw-r--r--source/blender/gpu/intern/gpu_shader_builder_stubs.cc80
-rw-r--r--source/blender/gpu/intern/gpu_shader_builtin.c39
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.cc18
-rw-r--r--source/blender/gpu/intern/gpu_shader_create_info.hh128
-rw-r--r--source/blender/gpu/intern/gpu_shader_dependency.cc268
-rw-r--r--source/blender/gpu/intern/gpu_shader_interface.hh47
-rw-r--r--source/blender/gpu/intern/gpu_shader_log.cc1
-rw-r--r--source/blender/gpu/intern/gpu_shader_private.hh2
-rw-r--r--source/blender/gpu/intern/gpu_storage_buffer.cc5
-rw-r--r--source/blender/gpu/intern/gpu_storage_buffer_private.hh3
-rw-r--r--source/blender/gpu/intern/gpu_texture.cc149
-rw-r--r--source/blender/gpu/intern/gpu_texture_private.hh8
-rw-r--r--source/blender/gpu/intern/gpu_uniform_buffer_private.hh2
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer.cc22
-rw-r--r--source/blender/gpu/intern/gpu_vertex_buffer_private.hh12
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format.cc119
-rw-r--r--source/blender/gpu/intern/gpu_vertex_format_private.h1
-rw-r--r--source/blender/gpu/metal/kernels/compute_texture_read.msl2
-rw-r--r--source/blender/gpu/metal/kernels/compute_texture_update.msl16
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl5
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_info.hh35
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl4
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl5
-rw-r--r--source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl6
-rw-r--r--source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_frag.glsl5
-rw-r--r--source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh23
-rw-r--r--source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_vert.glsl8
-rw-r--r--source/blender/gpu/metal/mtl_backend.hh6
-rw-r--r--source/blender/gpu/metal/mtl_backend.mm25
-rw-r--r--source/blender/gpu/metal/mtl_capabilities.hh2
-rw-r--r--source/blender/gpu/metal/mtl_command_buffer.mm16
-rw-r--r--source/blender/gpu/metal/mtl_common.hh2
-rw-r--r--source/blender/gpu/metal/mtl_context.hh53
-rw-r--r--source/blender/gpu/metal/mtl_context.mm209
-rw-r--r--source/blender/gpu/metal/mtl_framebuffer.hh36
-rw-r--r--source/blender/gpu/metal/mtl_framebuffer.mm20
-rw-r--r--source/blender/gpu/metal/mtl_index_buffer.hh80
-rw-r--r--source/blender/gpu/metal/mtl_index_buffer.mm517
-rw-r--r--source/blender/gpu/metal/mtl_memory.hh24
-rw-r--r--source/blender/gpu/metal/mtl_memory.mm61
-rw-r--r--source/blender/gpu/metal/mtl_primitive.hh100
-rw-r--r--source/blender/gpu/metal/mtl_pso_descriptor_state.hh250
-rw-r--r--source/blender/gpu/metal/mtl_query.hh41
-rw-r--r--source/blender/gpu/metal/mtl_query.mm122
-rw-r--r--source/blender/gpu/metal/mtl_shader.hh1165
-rw-r--r--source/blender/gpu/metal/mtl_shader.mm1266
-rw-r--r--source/blender/gpu/metal/mtl_shader_generator.hh727
-rw-r--r--source/blender/gpu/metal/mtl_shader_generator.mm2980
-rw-r--r--source/blender/gpu/metal/mtl_shader_interface.hh267
-rw-r--r--source/blender/gpu/metal/mtl_shader_interface.mm604
-rw-r--r--source/blender/gpu/metal/mtl_shader_interface_type.hh251
-rw-r--r--source/blender/gpu/metal/mtl_shader_shared.h32
-rw-r--r--source/blender/gpu/metal/mtl_state.hh14
-rw-r--r--source/blender/gpu/metal/mtl_state.mm3
-rw-r--r--source/blender/gpu/metal/mtl_texture.hh46
-rw-r--r--source/blender/gpu/metal/mtl_texture.mm22
-rw-r--r--source/blender/gpu/metal/mtl_texture_util.mm116
-rw-r--r--source/blender/gpu/metal/mtl_uniform_buffer.hh50
-rw-r--r--source/blender/gpu/metal/mtl_uniform_buffer.mm162
-rw-r--r--source/blender/gpu/opengl/gl_backend.cc112
-rw-r--r--source/blender/gpu/opengl/gl_backend.hh12
-rw-r--r--source/blender/gpu/opengl/gl_batch.cc52
-rw-r--r--source/blender/gpu/opengl/gl_batch.hh11
-rw-r--r--source/blender/gpu/opengl/gl_context.cc6
-rw-r--r--source/blender/gpu/opengl/gl_context.hh3
-rw-r--r--source/blender/gpu/opengl/gl_debug.cc19
-rw-r--r--source/blender/gpu/opengl/gl_debug.hh2
-rw-r--r--source/blender/gpu/opengl/gl_framebuffer.cc5
-rw-r--r--source/blender/gpu/opengl/gl_framebuffer.hh8
-rw-r--r--source/blender/gpu/opengl/gl_immediate.hh2
-rw-r--r--source/blender/gpu/opengl/gl_index_buffer.hh12
-rw-r--r--source/blender/gpu/opengl/gl_primitive.hh2
-rw-r--r--source/blender/gpu/opengl/gl_query.hh2
-rw-r--r--source/blender/gpu/opengl/gl_shader.cc116
-rw-r--r--source/blender/gpu/opengl/gl_shader.hh4
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.cc68
-rw-r--r--source/blender/gpu/opengl/gl_shader_interface.hh2
-rw-r--r--source/blender/gpu/opengl/gl_state.cc6
-rw-r--r--source/blender/gpu/opengl/gl_state.hh2
-rw-r--r--source/blender/gpu/opengl/gl_storage_buffer.cc19
-rw-r--r--source/blender/gpu/opengl/gl_storage_buffer.hh3
-rw-r--r--source/blender/gpu/opengl/gl_texture.cc9
-rw-r--r--source/blender/gpu/opengl/gl_texture.hh6
-rw-r--r--source/blender/gpu/opengl/gl_uniform_buffer.cc11
-rw-r--r--source/blender/gpu/opengl/gl_uniform_buffer.hh2
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.cc15
-rw-r--r--source/blender/gpu/opengl/gl_vertex_array.hh2
-rw-r--r--source/blender/gpu/opengl/gl_vertex_buffer.hh2
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl119
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl114
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl27
-rw-r--r--source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl46
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl11
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_bilateral_blur.glsl31
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_blur.glsl55
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_bokeh_image.glsl118
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_box_mask.glsl27
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_convert.glsl8
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_despeckle.glsl70
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl21
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_edge_filter.glsl31
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl27
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_filter.glsl20
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_flip.glsl15
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_image_crop.glsl7
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_morphological_distance.glsl24
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_morphological_distance_feather.glsl101
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_morphological_distance_threshold.glsl88
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_morphological_step.glsl19
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl16
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl29
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl151
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl8
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl14
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_symmetric_blur.glsl77
-rw-r--r--source/blender/gpu/shaders/compositor/compositor_symmetric_separable_blur.glsl53
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_bilateral_blur_info.hh13
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh14
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_bokeh_image_info.hh14
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh35
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh69
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_despeckle_info.hh13
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_edge_filter_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh35
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_filter_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh12
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh11
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_feather_info.hh21
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_info.hh22
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh13
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_morphological_step_info.hh22
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh11
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh24
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh20
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh11
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh22
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_symmetric_blur_info.hh13
-rw-r--r--source/blender/gpu/shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh14
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl48
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_blur_common.glsl32
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl38
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl52
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl43
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl34
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl87
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl27
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl13
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl6
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl10
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl26
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl6
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl7
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl39
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl16
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl13
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl14
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl7
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl56
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl9
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl6
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl132
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl9
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl26
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl35
-rw-r--r--source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl29
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_flat_color_vert.glsl6
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_image_vert.glsl1
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_line_dashed_uniform_color_vert.glsl13
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl58
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl7
-rw-r--r--source/blender/gpu/shaders/gpu_shader_2D_smooth_color_vert.glsl6
-rw-r--r--source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl4
-rw-r--r--source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl6
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh21
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh14
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh5
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh1
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh20
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh18
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh13
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh21
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh18
-rw-r--r--source/blender/gpu/shaders/infos/gpu_shader_line_dashed_uniform_color_info.hh15
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl17
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl537
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl1
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl6
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl12
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl68
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl44
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl1
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl5
-rw-r--r--source/blender/gpu/shaders/metal/mtl_shader_common.msl109
-rw-r--r--source/blender/gpu/shaders/metal/mtl_shader_defines.msl1065
-rw-r--r--source/blender/gpu/tests/gpu_shader_builtin_test.cc7
-rw-r--r--source/blender/gpu/tests/gpu_shader_test.cc6
-rw-r--r--source/blender/gpu/tests/gpu_testing.cc1
-rw-r--r--source/blender/imbuf/IMB_colormanagement.h2
-rw-r--r--source/blender/imbuf/IMB_imbuf.h78
-rw-r--r--source/blender/imbuf/IMB_imbuf_types.h10
-rw-r--r--source/blender/imbuf/intern/IMB_filetype.h6
-rw-r--r--source/blender/imbuf/intern/allocimbuf.c37
-rw-r--r--source/blender/imbuf/intern/anim_movie.c11
-rw-r--r--source/blender/imbuf/intern/bmp.c3
-rw-r--r--source/blender/imbuf/intern/cache.c32
-rw-r--r--source/blender/imbuf/intern/cineon/cineon_dpx.c25
-rw-r--r--source/blender/imbuf/intern/cineon/cineonlib.c23
-rw-r--r--source/blender/imbuf/intern/cineon/cineonlib.h30
-rw-r--r--source/blender/imbuf/intern/cineon/dpxlib.c19
-rw-r--r--source/blender/imbuf/intern/cineon/logImageCore.c119
-rw-r--r--source/blender/imbuf/intern/cineon/logmemfile.c30
-rw-r--r--source/blender/imbuf/intern/colormanagement.c128
-rw-r--r--source/blender/imbuf/intern/colormanagement_inline.c7
-rw-r--r--source/blender/imbuf/intern/dds/BlockDXT.cpp4
-rw-r--r--source/blender/imbuf/intern/dds/DirectDrawSurface.cpp8
-rw-r--r--source/blender/imbuf/intern/dds/FlipDXT.cpp44
-rw-r--r--source/blender/imbuf/intern/dds/Stream.cpp16
-rw-r--r--source/blender/imbuf/intern/dds/dds_api.cpp23
-rw-r--r--source/blender/imbuf/intern/divers.c42
-rw-r--r--source/blender/imbuf/intern/filetype.c2
-rw-r--r--source/blender/imbuf/intern/filter.c34
-rw-r--r--source/blender/imbuf/intern/imageprocess.c61
-rw-r--r--source/blender/imbuf/intern/indexer.c6
-rw-r--r--source/blender/imbuf/intern/iris.c2
-rw-r--r--source/blender/imbuf/intern/jp2.c46
-rw-r--r--source/blender/imbuf/intern/jpeg.c21
-rw-r--r--source/blender/imbuf/intern/moviecache.cc2
-rw-r--r--source/blender/imbuf/intern/oiio/openimageio_api.cpp6
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp58
-rw-r--r--source/blender/imbuf/intern/png.c60
-rw-r--r--source/blender/imbuf/intern/radiance_hdr.c53
-rw-r--r--source/blender/imbuf/intern/readimage.c19
-rw-r--r--source/blender/imbuf/intern/rectop.c86
-rw-r--r--source/blender/imbuf/intern/rotate.c4
-rw-r--r--source/blender/imbuf/intern/scaling.c102
-rw-r--r--source/blender/imbuf/intern/stereoimbuf.c36
-rw-r--r--source/blender/imbuf/intern/targa.c57
-rw-r--r--source/blender/imbuf/intern/thumbs.c8
-rw-r--r--source/blender/imbuf/intern/thumbs_font.c6
-rw-r--r--source/blender/imbuf/intern/tiff.c59
-rw-r--r--source/blender/imbuf/intern/transform.cc28
-rw-r--r--source/blender/imbuf/intern/util.c11
-rw-r--r--source/blender/imbuf/intern/util_gpu.c110
-rw-r--r--source/blender/imbuf/intern/webp.c107
-rw-r--r--source/blender/io/alembic/ABC_alembic.h30
-rw-r--r--source/blender/io/alembic/exporter/abc_subdiv_disabler.cc4
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_hair.cc13
-rw-r--r--source/blender/io/alembic/exporter/abc_writer_mesh.cc52
-rw-r--r--source/blender/io/alembic/intern/abc_customdata.cc13
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.cc92
-rw-r--r--source/blender/io/alembic/intern/abc_reader_mesh.h9
-rw-r--r--source/blender/io/alembic/intern/abc_reader_object.cc6
-rw-r--r--source/blender/io/alembic/intern/abc_reader_points.cc2
-rw-r--r--source/blender/io/alembic/intern/alembic_capi.cc26
-rw-r--r--source/blender/io/collada/AnimationImporter.cpp4
-rw-r--r--source/blender/io/collada/ArmatureExporter.cpp2
-rw-r--r--source/blender/io/collada/BCAnimationCurve.cpp4
-rw-r--r--source/blender/io/collada/BlenderContext.cpp28
-rw-r--r--source/blender/io/collada/BlenderContext.h8
-rw-r--r--source/blender/io/collada/ControllerExporter.cpp7
-rw-r--r--source/blender/io/collada/DocumentImporter.cpp11
-rw-r--r--source/blender/io/collada/EffectExporter.cpp6
-rw-r--r--source/blender/io/collada/ExportSettings.h7
-rw-r--r--source/blender/io/collada/GeometryExporter.cpp89
-rw-r--r--source/blender/io/collada/ImportSettings.h1
-rw-r--r--source/blender/io/collada/Materials.cpp2
-rw-r--r--source/blender/io/collada/MeshImporter.cpp135
-rw-r--r--source/blender/io/collada/MeshImporter.h7
-rw-r--r--source/blender/io/collada/SceneExporter.cpp8
-rw-r--r--source/blender/io/collada/collada.cpp4
-rw-r--r--source/blender/io/collada/collada_utils.cpp3
-rw-r--r--source/blender/io/common/CMakeLists.txt4
-rw-r--r--source/blender/io/common/IO_abstract_hierarchy_iterator.h6
-rw-r--r--source/blender/io/gpencil/gpencil_io.h2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.cc10
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_base.hh6
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_export_svg.cc2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_base.cc17
-rw-r--r--source/blender/io/stl/CMakeLists.txt8
-rw-r--r--source/blender/io/stl/importer/stl_import.cc3
-rw-r--r--source/blender/io/stl/importer/stl_import_mesh.cc25
-rw-r--r--source/blender/io/usd/intern/usd_capi_import.cc28
-rw-r--r--source/blender/io/usd/intern/usd_reader_material.cc86
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.cc132
-rw-r--r--source/blender/io/usd/intern/usd_reader_mesh.h11
-rw-r--r--source/blender/io/usd/intern/usd_reader_stage.cc15
-rw-r--r--source/blender/io/usd/intern/usd_reader_stage.h2
-rw-r--r--source/blender/io/usd/intern/usd_writer_mesh.cc64
-rw-r--r--source/blender/io/usd/intern/usd_writer_volume.cc2
-rw-r--r--source/blender/io/usd/usd.h2
-rw-r--r--source/blender/io/wavefront_obj/IO_wavefront_obj.cc17
-rw-r--r--source/blender/io/wavefront_obj/IO_wavefront_obj.h13
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc349
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh34
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_io.hh404
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc116
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh5
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc214
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh99
-rw-r--r--source/blender/io/wavefront_obj/exporter/obj_exporter.cc11
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc222
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mesh.cc145
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mesh.hh3
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mtl.cc335
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_mtl.hh88
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_objects.hh71
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc28
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh14
-rw-r--r--source/blender/io/wavefront_obj/importer/obj_importer.cc16
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc47
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh1
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc23
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_importer_tests.cc450
-rw-r--r--source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc259
-rw-r--r--source/blender/makesdna/DNA_ID.h217
-rw-r--r--source/blender/makesdna/DNA_anim_types.h3
-rw-r--r--source/blender/makesdna/DNA_asset_types.h2
-rw-r--r--source/blender/makesdna/DNA_brush_enums.h10
-rw-r--r--source/blender/makesdna/DNA_brush_types.h13
-rw-r--r--source/blender/makesdna/DNA_camera_types.h1
-rw-r--r--source/blender/makesdna/DNA_collection_types.h4
-rw-r--r--source/blender/makesdna/DNA_color_types.h3
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h2
-rw-r--r--source/blender/makesdna/DNA_curve_types.h4
-rw-r--r--source/blender/makesdna/DNA_curves_types.h18
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h1
-rw-r--r--source/blender/makesdna/DNA_fileglobal_types.h4
-rw-r--r--source/blender/makesdna/DNA_gpencil_modifier_types.h7
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h6
-rw-r--r--source/blender/makesdna/DNA_image_types.h19
-rw-r--r--source/blender/makesdna/DNA_layer_types.h59
-rw-r--r--source/blender/makesdna/DNA_lightprobe_types.h1
-rw-r--r--source/blender/makesdna/DNA_lineart_types.h1
-rw-r--r--source/blender/makesdna/DNA_mesh_types.h136
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h28
-rw-r--r--source/blender/makesdna/DNA_meta_types.h2
-rw-r--r--source/blender/makesdna/DNA_modifier_types.h10
-rw-r--r--source/blender/makesdna/DNA_node_types.h304
-rw-r--r--source/blender/makesdna/DNA_object_types.h9
-rw-r--r--source/blender/makesdna/DNA_outliner_types.h7
-rw-r--r--source/blender/makesdna/DNA_particle_types.h4
-rw-r--r--source/blender/makesdna/DNA_pointcloud_types.h16
-rw-r--r--source/blender/makesdna/DNA_scene_types.h27
-rw-r--r--source/blender/makesdna/DNA_screen_types.h14
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h9
-rw-r--r--source/blender/makesdna/DNA_sound_types.h2
-rw-r--r--source/blender/makesdna/DNA_space_types.h43
-rw-r--r--source/blender/makesdna/DNA_texture_types.h2
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h10
-rw-r--r--source/blender/makesdna/DNA_view2d_types.h18
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h12
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h35
-rw-r--r--source/blender/makesdna/DNA_workspace_types.h2
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt43
-rw-r--r--source/blender/makesdna/intern/dna_genfile.c29
-rw-r--r--source/blender/makesdna/intern/dna_rename_defs.h4
-rw-r--r--source/blender/makesdna/intern/makesdna.c68
-rw-r--r--source/blender/makesrna/RNA_access.h249
-rw-r--r--source/blender/makesrna/RNA_path.h258
-rw-r--r--source/blender/makesrna/RNA_types.h14
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt72
-rw-r--r--source/blender/makesrna/intern/makesrna.c88
-rw-r--r--source/blender/makesrna/intern/rna_ID.c43
-rw-r--r--source/blender/makesrna/intern/rna_access.c1362
-rw-r--r--source/blender/makesrna/intern/rna_access_compare_override.c5
-rw-r--r--source/blender/makesrna/intern/rna_access_internal.h10
-rw-r--r--source/blender/makesrna/intern/rna_action.c7
-rw-r--r--source/blender/makesrna/intern/rna_armature.c4
-rw-r--r--source/blender/makesrna/intern/rna_attribute.c54
-rw-r--r--source/blender/makesrna/intern/rna_brush.c61
-rw-r--r--source/blender/makesrna/intern/rna_camera.c11
-rw-r--r--source/blender/makesrna/intern/rna_collection.c91
-rw-r--r--source/blender/makesrna/intern/rna_color.c4
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c3
-rw-r--r--source/blender/makesrna/intern/rna_curve.c16
-rw-r--r--source/blender/makesrna/intern/rna_curveprofile.c3
-rw-r--r--source/blender/makesrna/intern/rna_curves.c117
-rw-r--r--source/blender/makesrna/intern/rna_depsgraph.c120
-rw-r--r--source/blender/makesrna/intern/rna_fcurve.c21
-rw-r--r--source/blender/makesrna/intern/rna_fluid.c72
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c1
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c35
-rw-r--r--source/blender/makesrna/intern/rna_image.c156
-rw-r--r--source/blender/makesrna/intern/rna_internal.h12
-rw-r--r--source/blender/makesrna/intern/rna_layer.c36
-rw-r--r--source/blender/makesrna/intern/rna_light.c2
-rw-r--r--source/blender/makesrna/intern/rna_main.c7
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c6
-rw-r--r--source/blender/makesrna/intern/rna_material.c4
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c552
-rw-r--r--source/blender/makesrna/intern/rna_mesh_api.c21
-rw-r--r--source/blender/makesrna/intern/rna_mesh_utils.h4
-rw-r--r--source/blender/makesrna/intern/rna_meta_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_modifier.c8
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c146
-rw-r--r--source/blender/makesrna/intern/rna_object.c24
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c99
-rw-r--r--source/blender/makesrna/intern/rna_object_force.c2
-rw-r--r--source/blender/makesrna/intern/rna_particle.c34
-rw-r--r--source/blender/makesrna/intern/rna_path.cc1364
-rw-r--r--source/blender/makesrna/intern/rna_pointcloud.c72
-rw-r--r--source/blender/makesrna/intern/rna_pose.c4
-rw-r--r--source/blender/makesrna/intern/rna_render.c2
-rw-r--r--source/blender/makesrna/intern/rna_rigidbody.c4
-rw-r--r--source/blender/makesrna/intern/rna_rna.c17
-rw-r--r--source/blender/makesrna/intern/rna_scene.c110
-rw-r--r--source/blender/makesrna/intern/rna_sculpt_paint.c31
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c9
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_space.c117
-rw-r--r--source/blender/makesrna/intern/rna_texture.c2
-rw-r--r--source/blender/makesrna/intern/rna_ui.c8
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c3
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c115
-rw-r--r--source/blender/makesrna/intern/rna_wm.c37
-rw-r--r--source/blender/makesrna/intern/rna_wm_api.c8
-rw-r--r--source/blender/makesrna/intern/rna_wm_gizmo.c7
-rw-r--r--source/blender/makesrna/intern/rna_wm_gizmo_api.c40
-rw-r--r--source/blender/makesrna/intern/rna_workspace.c2
-rw-r--r--source/blender/modifiers/CMakeLists.txt3
-rw-r--r--source/blender/modifiers/intern/MOD_armature.c11
-rw-r--r--source/blender/modifiers/intern/MOD_array.c83
-rw-r--r--source/blender/modifiers/intern/MOD_bevel.c12
-rw-r--r--source/blender/modifiers/intern/MOD_boolean.cc26
-rw-r--r--source/blender/modifiers/intern/MOD_build.c37
-rw-r--r--source/blender/modifiers/intern/MOD_cast.c19
-rw-r--r--source/blender/modifiers/intern/MOD_cloth.c14
-rw-r--r--source/blender/modifiers/intern/MOD_collision.c12
-rw-r--r--source/blender/modifiers/intern/MOD_correctivesmooth.c255
-rw-r--r--source/blender/modifiers/intern/MOD_curve.c12
-rw-r--r--source/blender/modifiers/intern/MOD_datatransfer.c18
-rw-r--r--source/blender/modifiers/intern/MOD_decimate.c12
-rw-r--r--source/blender/modifiers/intern/MOD_displace.c26
-rw-r--r--source/blender/modifiers/intern/MOD_dynamicpaint.c8
-rw-r--r--source/blender/modifiers/intern/MOD_edgesplit.c2
-rw-r--r--source/blender/modifiers/intern/MOD_explode.c51
-rw-r--r--source/blender/modifiers/intern/MOD_fluid.c8
-rw-r--r--source/blender/modifiers/intern/MOD_hook.c13
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciandeform.c30
-rw-r--r--source/blender/modifiers/intern/MOD_laplaciansmooth.c24
-rw-r--r--source/blender/modifiers/intern/MOD_lattice.c10
-rw-r--r--source/blender/modifiers/intern/MOD_mask.cc190
-rw-r--r--source/blender/modifiers/intern/MOD_mesh_to_volume.cc17
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache.c17
-rw-r--r--source/blender/modifiers/intern/MOD_meshcache_util.h8
-rw-r--r--source/blender/modifiers/intern/MOD_meshdeform.c20
-rw-r--r--source/blender/modifiers/intern/MOD_meshsequencecache.cc19
-rw-r--r--source/blender/modifiers/intern/MOD_mirror.c4
-rw-r--r--source/blender/modifiers/intern/MOD_multires.c6
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc578
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc1928
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.hh44
-rw-r--r--source/blender/modifiers/intern/MOD_normal_edit.c74
-rw-r--r--source/blender/modifiers/intern/MOD_ocean.c147
-rw-r--r--source/blender/modifiers/intern/MOD_particleinstance.c32
-rw-r--r--source/blender/modifiers/intern/MOD_particlesystem.cc9
-rw-r--r--source/blender/modifiers/intern/MOD_remesh.c27
-rw-r--r--source/blender/modifiers/intern/MOD_screw.c123
-rw-r--r--source/blender/modifiers/intern/MOD_shapekey.c13
-rw-r--r--source/blender/modifiers/intern/MOD_shrinkwrap.c19
-rw-r--r--source/blender/modifiers/intern/MOD_simpledeform.c17
-rw-r--r--source/blender/modifiers/intern/MOD_skin.c37
-rw-r--r--source/blender/modifiers/intern/MOD_smooth.c18
-rw-r--r--source/blender/modifiers/intern/MOD_softbody.c4
-rw-r--r--source/blender/modifiers/intern/MOD_solidify.c6
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_extrude.c223
-rw-r--r--source/blender/modifiers/intern/MOD_solidify_nonmanifold.c218
-rw-r--r--source/blender/modifiers/intern/MOD_subsurf.c6
-rw-r--r--source/blender/modifiers/intern/MOD_surface.c8
-rw-r--r--source/blender/modifiers/intern/MOD_surfacedeform.c42
-rw-r--r--source/blender/modifiers/intern/MOD_triangulate.c4
-rw-r--r--source/blender/modifiers/intern/MOD_util.c21
-rw-r--r--source/blender/modifiers/intern/MOD_util.h3
-rw-r--r--source/blender/modifiers/intern/MOD_uvproject.c31
-rw-r--r--source/blender/modifiers/intern/MOD_uvwarp.c29
-rw-r--r--source/blender/modifiers/intern/MOD_volume_displace.cc4
-rw-r--r--source/blender/modifiers/intern/MOD_volume_to_mesh.cc8
-rw-r--r--source/blender/modifiers/intern/MOD_warp.c16
-rw-r--r--source/blender/modifiers/intern/MOD_wave.c43
-rw-r--r--source/blender/modifiers/intern/MOD_weighted_normal.c96
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgedit.c26
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgmix.c22
-rw-r--r--source/blender/modifiers/intern/MOD_weightvgproximity.c16
-rw-r--r--source/blender/modifiers/intern/MOD_weld.cc6
-rw-r--r--source/blender/modifiers/intern/MOD_wireframe.c6
-rw-r--r--source/blender/nodes/CMakeLists.txt22
-rw-r--r--source/blender/nodes/NOD_composite.h2
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh172
-rw-r--r--source/blender/nodes/NOD_geometry.h17
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh213
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_eval_log.hh406
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_lazy_function.hh181
-rw-r--r--source/blender/nodes/NOD_geometry_nodes_log.hh340
-rw-r--r--source/blender/nodes/NOD_multi_function.hh26
-rw-r--r--source/blender/nodes/NOD_node_declaration.hh55
-rw-r--r--source/blender/nodes/NOD_node_tree_ref.hh760
-rw-r--r--source/blender/nodes/NOD_shader.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h266
-rw-r--r--source/blender/nodes/composite/CMakeLists.txt11
-rw-r--r--source/blender/nodes/composite/node_composite_tree.cc5
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_alpha_over.cc68
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_antialiasing.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc73
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_blur.cc419
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehblur.cc121
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_bokehimage.cc66
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_boxmask.cc99
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_brightness.cc45
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_channel_matte.cc96
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc63
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_matte.cc62
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_color_spill.cc112
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorbalance.cc69
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc81
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_composite.cc129
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cornerpin.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_crop.cc168
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_curves.cc246
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_defocus.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_denoise.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_despeckle.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_diff_matte.cc56
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_dilate.cc486
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_directionalblur.cc140
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_displace.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_distance_matte.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc99
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_exposure.cc31
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_filter.cc130
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_flip.cc64
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_gamma.cc32
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_glare.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc49
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_huecorrect.cc65
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_id_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.cc275
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_inpaint.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_invert.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keying.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_lensdist.cc204
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_levels.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_luma_matte.cc55
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_range.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_uv.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_map_value.cc58
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mask.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_math.cc67
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_mixrgb.cc125
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_movieclip.cc190
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normal.cc42
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_normalize.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_output_file.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_pixelate.cc49
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_posterize.cc35
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_premulkey.cc39
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rgb.cc30
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_rotate.cc54
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scale.cc167
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_scene_time.cc36
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc124
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc63
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc126
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc76
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_setalpha.cc42
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_split_viewer.cc71
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_sunbeams.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switch.cc28
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_texture.cc21
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_tonemap.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_trackpos.cc22
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_transform.cc77
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_translate.cc72
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc150
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_value.cc26
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_vec_blur.cc20
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_viewer.cc129
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_zcombine.cc21
-rw-r--r--source/blender/nodes/function/node_function_util.hh2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc4
-rw-r--r--source/blender/nodes/function/nodes/node_fn_boolean_math.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_combine_color.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_compare.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_float_to_int.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_bool.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_color.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_int.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_string.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_vector.cc2
-rw-r--r--source/blender/nodes/function/nodes/node_fn_rotate_euler.cc2
-rw-r--r--source/blender/nodes/geometry/CMakeLists.txt21
-rw-r--r--source/blender/nodes/geometry/node_geometry_exec.cc1
-rw-r--r--source/blender/nodes/geometry/node_geometry_tree.cc6
-rw-r--r--source/blender/nodes/geometry/node_geometry_util.hh5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc40
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_boolean.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc4
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc84
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc595
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc17
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc12
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc318
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc16
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc391
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc485
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc158
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc445
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc285
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc122
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc200
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc279
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc110
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc134
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_edge_split.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc204
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc47
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc78
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc64
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc32
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc48
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc66
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc53
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc58
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc236
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc25
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc7
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc42
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc)57
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc3
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_material_selection.cc38
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc40
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc89
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc10
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc154
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc19
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc52
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_object_info.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc46
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_raycast.cc8
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc6
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc158
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc37
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc23
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_id.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material.cc13
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc29
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc24
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc28
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc75
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_join.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc22
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc60
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_transform.cc129
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc5
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_triangulate.cc9
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc44
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc49
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc21
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc15
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc139
-rw-r--r--source/blender/nodes/intern/geometry_nodes_eval_log.cc508
-rw-r--r--source/blender/nodes/intern/geometry_nodes_lazy_function.cc1423
-rw-r--r--source/blender/nodes/intern/geometry_nodes_log.cc610
-rw-r--r--source/blender/nodes/intern/node_common.cc28
-rw-r--r--source/blender/nodes/intern/node_geometry_exec.cc88
-rw-r--r--source/blender/nodes/intern/node_multi_function.cc26
-rw-r--r--source/blender/nodes/intern/node_tree_ref.cc678
-rw-r--r--source/blender/nodes/intern/node_util.c2
-rw-r--r--source/blender/nodes/shader/CMakeLists.txt27
-rw-r--r--source/blender/nodes/shader/node_shader_tree.cc31
-rw-r--r--source/blender/nodes/shader/node_shader_util.hh2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_attribute.cc16
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc3
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc21
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_color_ramp.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_curves.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_geometry.cc5
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_hair_info.cc4
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_math.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mix.cc443
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc33
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_rgb.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_brick.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_coord.cc8
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_environment.cc6
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_magic.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_sky.cc52
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_wave.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_value.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_math.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_transform.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vertex_color.cc7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_volume_info.cc2
-rw-r--r--source/blender/nodes/texture/CMakeLists.txt16
-rw-r--r--source/blender/nodes/texture/node_texture_tree.c7
-rw-r--r--source/blender/python/BPY_extern.h2
-rw-r--r--source/blender/python/CMakeLists.txt4
-rw-r--r--source/blender/python/bmesh/bmesh_py_api.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_geometry.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_ops.c2
-rw-r--r--source/blender/python/bmesh/bmesh_py_types.c6
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_meshdata.c4
-rw-r--r--source/blender/python/bmesh/bmesh_py_utils.c2
-rw-r--r--source/blender/python/generic/CMakeLists.txt7
-rw-r--r--source/blender/python/generic/bgl.c70
-rw-r--r--source/blender/python/generic/bl_math_py_api.c2
-rw-r--r--source/blender/python/generic/blf_py_api.c2
-rw-r--r--source/blender/python/generic/idprop_py_api.c4
-rw-r--r--source/blender/python/generic/imbuf_py_api.c4
-rw-r--r--source/blender/python/generic/py_capi_utils.c43
-rw-r--r--source/blender/python/generic/py_capi_utils.h1
-rw-r--r--source/blender/python/gpu/CMakeLists.txt6
-rw-r--r--source/blender/python/gpu/gpu_py_batch.c6
-rw-r--r--source/blender/python/gpu/gpu_py_buffer.c4
-rw-r--r--source/blender/python/gpu/gpu_py_element.c2
-rw-r--r--source/blender/python/gpu/gpu_py_framebuffer.c28
-rw-r--r--source/blender/python/gpu/gpu_py_matrix.c12
-rw-r--r--source/blender/python/gpu/gpu_py_platform.c32
-rw-r--r--source/blender/python/gpu/gpu_py_select.c2
-rw-r--r--source/blender/python/gpu/gpu_py_shader.c231
-rw-r--r--source/blender/python/gpu/gpu_py_shader.h5
-rw-r--r--source/blender/python/gpu/gpu_py_shader_create_info.cc85
-rw-r--r--source/blender/python/gpu/gpu_py_state.c27
-rw-r--r--source/blender/python/gpu/gpu_py_texture.c4
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_buffer.c16
-rw-r--r--source/blender/python/gpu/gpu_py_vertex_format.c8
-rw-r--r--source/blender/python/intern/CMakeLists.txt4
-rw-r--r--source/blender/python/intern/bpy.c171
-rw-r--r--source/blender/python/intern/bpy.h2
-rw-r--r--source/blender/python/intern/bpy_app.c41
-rw-r--r--source/blender/python/intern/bpy_app_build_options.c4
-rw-r--r--source/blender/python/intern/bpy_app_icons.c2
-rw-r--r--source/blender/python/intern/bpy_app_timers.c2
-rw-r--r--source/blender/python/intern/bpy_driver.c98
-rw-r--r--source/blender/python/intern/bpy_driver.h10
-rw-r--r--source/blender/python/intern/bpy_gizmo_wrap.c2
-rw-r--r--source/blender/python/intern/bpy_interface.c59
-rw-r--r--source/blender/python/intern/bpy_interface_atexit.c2
-rw-r--r--source/blender/python/intern/bpy_library_load.c2
-rw-r--r--source/blender/python/intern/bpy_operator.c2
-rw-r--r--source/blender/python/intern/bpy_path.c2
-rw-r--r--source/blender/python/intern/bpy_rna.c121
-rw-r--r--source/blender/python/intern/bpy_rna.h6
-rw-r--r--source/blender/python/intern/bpy_rna_anim.c4
-rw-r--r--source/blender/python/intern/bpy_rna_array.c2
-rw-r--r--source/blender/python/intern/bpy_rna_data.c2
-rw-r--r--source/blender/python/intern/bpy_rna_gizmo.c2
-rw-r--r--source/blender/python/intern/bpy_rna_operator.c2
-rw-r--r--source/blender/python/intern/bpy_rna_types_capi.c12
-rw-r--r--source/blender/python/intern/bpy_traceback.c2
-rw-r--r--source/blender/python/intern/bpy_utils_units.c4
-rw-r--r--source/blender/python/intern/stubs.c2
-rw-r--r--source/blender/python/mathutils/mathutils.c4
-rw-r--r--source/blender/python/mathutils/mathutils.h8
-rw-r--r--source/blender/python/mathutils/mathutils_Color.c15
-rw-r--r--source/blender/python/mathutils/mathutils_Euler.c4
-rw-r--r--source/blender/python/mathutils/mathutils_Matrix.c31
-rw-r--r--source/blender/python/mathutils/mathutils_Quaternion.c5
-rw-r--r--source/blender/python/mathutils/mathutils_Vector.c2
-rw-r--r--source/blender/python/mathutils/mathutils_bvhtree.c10
-rw-r--r--source/blender/python/mathutils/mathutils_geometry.c2
-rw-r--r--source/blender/python/mathutils/mathutils_interpolate.c2
-rw-r--r--source/blender/python/mathutils/mathutils_kdtree.c2
-rw-r--r--source/blender/python/mathutils/mathutils_noise.c2
-rw-r--r--source/blender/render/CMakeLists.txt9
-rw-r--r--source/blender/render/RE_bake.h5
-rw-r--r--source/blender/render/RE_engine.h28
-rw-r--r--source/blender/render/RE_pipeline.h8
-rw-r--r--source/blender/render/intern/bake.c42
-rw-r--r--source/blender/render/intern/engine.cc (renamed from source/blender/render/intern/engine.c)327
-rw-r--r--source/blender/render/intern/initrender.cc (renamed from source/blender/render/intern/initrender.c)11
-rw-r--r--source/blender/render/intern/multires_bake.c22
-rw-r--r--source/blender/render/intern/pipeline.cc (renamed from source/blender/render/intern/pipeline.c)425
-rw-r--r--source/blender/render/intern/render_result.cc (renamed from source/blender/render/intern/render_result.c)305
-rw-r--r--source/blender/render/intern/render_result.h3
-rw-r--r--source/blender/render/intern/render_types.h7
-rw-r--r--source/blender/render/intern/texture_common.h4
-rw-r--r--source/blender/render/intern/texture_image.c10
-rw-r--r--source/blender/render/intern/texture_margin.cc14
-rw-r--r--source/blender/render/intern/texture_pointdensity.c8
-rw-r--r--source/blender/render/intern/texture_procedural.c1
-rw-r--r--source/blender/sequencer/SEQ_edit.h8
-rw-r--r--source/blender/sequencer/SEQ_relations.h2
-rw-r--r--source/blender/sequencer/SEQ_select.h6
-rw-r--r--source/blender/sequencer/SEQ_transform.h2
-rw-r--r--source/blender/sequencer/intern/effects.c23
-rw-r--r--source/blender/sequencer/intern/modifier.c6
-rw-r--r--source/blender/sequencer/intern/proxy.c16
-rw-r--r--source/blender/sequencer/intern/render.c20
-rw-r--r--source/blender/sequencer/intern/sequencer.c14
-rw-r--r--source/blender/sequencer/intern/sound.c3
-rw-r--r--source/blender/sequencer/intern/strip_edit.c31
-rw-r--r--source/blender/sequencer/intern/strip_select.c6
-rw-r--r--source/blender/sequencer/intern/strip_time.c26
-rw-r--r--source/blender/sequencer/intern/strip_time.h1
-rw-r--r--source/blender/sequencer/intern/strip_transform.c14
-rw-r--r--source/blender/sequencer/intern/utils.c7
-rw-r--r--source/blender/shader_fx/intern/FX_shader_blur.c2
-rw-r--r--source/blender/shader_fx/intern/FX_shader_colorize.c2
-rw-r--r--source/blender/shader_fx/intern/FX_shader_flip.c2
-rw-r--r--source/blender/shader_fx/intern/FX_shader_glow.c4
-rw-r--r--source/blender/shader_fx/intern/FX_shader_pixel.c2
-rw-r--r--source/blender/shader_fx/intern/FX_shader_rim.c2
-rw-r--r--source/blender/shader_fx/intern/FX_shader_shadow.c2
-rw-r--r--source/blender/shader_fx/intern/FX_shader_swirl.c4
-rw-r--r--source/blender/shader_fx/intern/FX_shader_wave.c4
-rw-r--r--source/blender/simulation/intern/hair_volume.cpp2
-rw-r--r--source/blender/windowmanager/CMakeLists.txt8
-rw-r--r--source/blender/windowmanager/WM_api.h38
-rw-r--r--source/blender/windowmanager/WM_toolsystem.h8
-rw-r--r--source/blender/windowmanager/WM_types.h14
-rw-r--r--source/blender/windowmanager/gizmo/WM_gizmo_types.h2
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c4
-rw-r--r--source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c10
-rw-r--r--source/blender/windowmanager/intern/wm.c15
-rw-r--r--source/blender/windowmanager/intern/wm_dragdrop.cc (renamed from source/blender/windowmanager/intern/wm_dragdrop.c)151
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c57
-rw-r--r--source/blender/windowmanager/intern/wm_event_query.c21
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.cc283
-rw-r--r--source/blender/windowmanager/intern/wm_files.c78
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c2
-rw-r--r--source/blender/windowmanager/intern/wm_gesture.c16
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c38
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c18
-rw-r--r--source/blender/windowmanager/intern/wm_operator_utils.c3
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c23
-rw-r--r--source/blender/windowmanager/intern/wm_platform_support.c44
-rw-r--r--source/blender/windowmanager/intern/wm_playanim.c20
-rw-r--r--source/blender/windowmanager/intern/wm_splash_screen.c81
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c4
-rw-r--r--source/blender/windowmanager/intern/wm_toolsystem.c64
-rw-r--r--source/blender/windowmanager/intern/wm_window.c357
-rw-r--r--source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c1
-rw-r--r--source/blender/windowmanager/message_bus/wm_message_bus.h2
-rw-r--r--source/blender/windowmanager/wm_event_types.h37
-rw-r--r--source/blender/windowmanager/wm_window.h1
-rw-r--r--source/creator/CMakeLists.txt630
-rw-r--r--source/creator/blender.map173
-rw-r--r--source/creator/creator.c50
-rw-r--r--source/creator/creator_intern.h12
-rw-r--r--source/creator/creator_signals.c7
-rw-r--r--source/creator/symbols_apple.map (renamed from source/creator/osx_locals.map)0
-rw-r--r--source/creator/symbols_unix.map44
2493 files changed, 113916 insertions, 55574 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt
index 8ba6e7318bb..28c15d9224c 100644
--- a/source/blender/CMakeLists.txt
+++ b/source/blender/CMakeLists.txt
@@ -151,14 +151,12 @@ add_subdirectory(io)
add_subdirectory(functions)
add_subdirectory(makesdna)
add_subdirectory(makesrna)
+add_subdirectory(compositor)
if(WITH_BLENDER_THUMBNAILER)
add_subdirectory(blendthumb)
endif()
-if(WITH_COMPOSITOR)
- add_subdirectory(compositor)
-endif()
if(WITH_IMAGE_OPENEXR)
add_subdirectory(imbuf/intern/openexr)
diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt
index 330cefa247a..6160d225d45 100644
--- a/source/blender/blendthumb/CMakeLists.txt
+++ b/source/blender/blendthumb/CMakeLists.txt
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2006 Blender Foundation. All rights reserved.
-#-----------------------------------------------------------------------------
+# -----------------------------------------------------------------------------
# Shared Thumbnail Extraction Logic
include_directories(
diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc
index de1f50dfdce..fff1242f2ce 100644
--- a/source/blender/blendthumb/src/blendthumb_extract.cc
+++ b/source/blender/blendthumb/src/blendthumb_extract.cc
@@ -121,6 +121,9 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file,
while (file_read(file, bhead_data, bhead_size)) {
/* Parse type and size from `BHead`. */
const int32_t block_size = bytes_to_native_i32(&bhead_data[4], endian_switch);
+ if (UNLIKELY(block_size < 0)) {
+ return BT_INVALID_THUMB;
+ }
/* We're looking for the thumbnail, so skip any other block. */
switch (*((int32_t *)bhead_data)) {
@@ -133,8 +136,9 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file,
thumb->height = bytes_to_native_i32(&shape[4], endian_switch);
/* Verify that image dimensions and data size make sense. */
- size_t data_size = block_size - 8;
- const size_t expected_size = thumb->width * thumb->height * 4;
+ size_t data_size = block_size - sizeof(shape);
+ const uint64_t expected_size = static_cast<uint64_t>(thumb->width) *
+ static_cast<uint64_t>(thumb->height) * 4;
if (thumb->width < 0 || thumb->height < 0 || data_size != expected_size) {
return BT_INVALID_THUMB;
}
diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h
index 83ca9158efc..d3226a8f609 100644
--- a/source/blender/blenfont/BLF_api.h
+++ b/source/blender/blenfont/BLF_api.h
@@ -18,10 +18,10 @@ extern "C" {
#define BLF_DATAFILES_FONTS_DIR "fonts"
/* File name of the default variable-width font. */
-#define BLF_DEFAULT_PROPORTIONAL_FONT "droidsans.ttf"
+#define BLF_DEFAULT_PROPORTIONAL_FONT "DejaVuSans.woff2"
/* File name of the default fixed-pitch font. */
-#define BLF_DEFAULT_MONOSPACED_FONT "bmonofont-i18n.ttf"
+#define BLF_DEFAULT_MONOSPACED_FONT "DejaVuSansMono.woff2"
/* enable this only if needed (unused circa 2016) */
#define BLF_BLUR_ENABLE 0
@@ -351,6 +351,10 @@ enum {
BLF_DEFAULT = 1 << 14,
/** Must only be used as last font in the stack. */
BLF_LAST_RESORT = 1 << 15,
+ /** Failure to load this font. Don't try again. */
+ BLF_BAD_FONT = 1 << 16,
+ /** This font is managed by the FreeType cache subsystem. */
+ BLF_CACHED = 1 << 17,
};
#define BLF_DRAW_STR_DUMMY_MAX 1024
diff --git a/source/blender/blenfont/CMakeLists.txt b/source/blender/blenfont/CMakeLists.txt
index a2b84290e67..986a261dc4b 100644
--- a/source/blender/blenfont/CMakeLists.txt
+++ b/source/blender/blenfont/CMakeLists.txt
@@ -10,7 +10,6 @@ set(INC
../imbuf
../makesdna
../makesrna
- ../../../intern/glew-mx
../../../intern/guardedalloc
)
diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c
index a1fcc17ca3f..9d9cc51ebcc 100644
--- a/source/blender/blenfont/intern/blf.c
+++ b/source/blender/blenfont/intern/blf.c
@@ -22,6 +22,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
+#include "BLI_string.h"
#include "BLI_threads.h"
#include "BLF_api.h"
@@ -98,7 +99,7 @@ bool blf_font_id_is_valid(int fontid)
static int blf_search(const char *name)
{
for (int i = 0; i < BLF_MAX_FONT; i++) {
- FontBLF *font = global_font[i];
+ const FontBLF *font = global_font[i];
if (font && (STREQ(font->name, name))) {
return i;
}
@@ -122,7 +123,7 @@ bool BLF_has_glyph(int fontid, unsigned int unicode)
{
FontBLF *font = blf_get(fontid);
if (font) {
- return FT_Get_Char_Index(font->face, unicode) != FT_Err_Ok;
+ return blf_get_char_index(font, unicode) != FT_Err_Ok;
}
return false;
}
@@ -483,7 +484,7 @@ void BLF_batch_draw_end(void)
g_batch.enabled = false;
}
-static void blf_draw_gl__start(FontBLF *font)
+static void blf_draw_gl__start(const FontBLF *font)
{
/*
* The pixmap alignment hack is handle
@@ -511,7 +512,7 @@ static void blf_draw_gl__start(FontBLF *font)
}
}
-static void blf_draw_gl__end(FontBLF *font)
+static void blf_draw_gl__end(const FontBLF *font)
{
if ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) != 0) {
GPU_matrix_pop();
@@ -885,12 +886,21 @@ void BLF_draw_buffer(int fontid, const char *str, const size_t str_len)
char *BLF_display_name_from_file(const char *filepath)
{
- FontBLF *font = blf_font_new("font_name", filepath);
- if (!font) {
- return NULL;
+ /* While listing font directories this function can be called simultaneously from a greater
+ * number of threads than we want the FreeType cache to keep open at a time. Therefore open
+ * with own FT_Library object and use FreeType calls directly to avoid any contention. */
+ char *name = NULL;
+ FT_Library ft_library;
+ if (FT_Init_FreeType(&ft_library) == FT_Err_Ok) {
+ FT_Face face;
+ if (FT_New_Face(ft_library, filepath, 0, &face) == FT_Err_Ok) {
+ if (face->family_name) {
+ name = BLI_sprintfN("%s %s", face->family_name, face->style_name);
+ }
+ FT_Done_Face(face);
+ }
+ FT_Done_FreeType(ft_library);
}
- char *name = blf_display_name(font);
- blf_font_free(font);
return name;
}
diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c
index 038e73cc928..eaea88be9ae 100644
--- a/source/blender/blenfont/intern/blf_font.c
+++ b/source/blender/blenfont/intern/blf_font.c
@@ -17,9 +17,11 @@
#include <ft2build.h>
#include FT_FREETYPE_H
+#include FT_CACHE_H /* FreeType Cache. */
#include FT_GLYPH_H
-#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */
#include FT_MULTIPLE_MASTERS_H /* Variable font support. */
+#include FT_TRUETYPE_IDS_H /* Code-point coverage constants. */
+#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */
#include "MEM_guardedalloc.h"
@@ -28,6 +30,7 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_math_color_blend.h"
+#include "BLI_path_util.h"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
@@ -52,9 +55,12 @@
BatchBLF g_batch;
/* freetype2 handle ONLY for this file! */
-static FT_Library ft_lib;
-static SpinLock ft_lib_mutex;
-static SpinLock blf_glyph_cache_mutex;
+static FT_Library ft_lib = NULL;
+static FTC_Manager ftc_manager = NULL;
+static FTC_CMapCache ftc_charmap_cache = NULL;
+
+/* Lock for FreeType library, used around face creation and deletion. */
+static ThreadMutex ft_lib_mutex;
/* May be set to #UI_widgetbase_draw_cache_flush. */
static void (*blf_draw_cache_flush)(void) = NULL;
@@ -63,19 +69,92 @@ static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font);
static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font);
/* -------------------------------------------------------------------- */
+
+/** \name FreeType Caching
+ * \{ */
+
+/**
+ * Called when a face is removed by the cache. FreeType will call #FT_Done_Face.
+ */
+static void blf_face_finalizer(void *object)
+{
+ FT_Face face = object;
+ FontBLF *font = (FontBLF *)face->generic.data;
+ font->face = NULL;
+}
+
+/**
+ * Called in response to #FTC_Manager_LookupFace. Now add a face to our font.
+ *
+ * \note Unused arguments are kept to match #FTC_Face_Requester function signature.
+ */
+static FT_Error blf_cache_face_requester(FTC_FaceID faceID,
+ FT_Library lib,
+ FT_Pointer UNUSED(reqData),
+ FT_Face *face)
+{
+ FontBLF *font = (FontBLF *)faceID;
+ int err = FT_Err_Cannot_Open_Resource;
+
+ BLI_mutex_lock(&ft_lib_mutex);
+ if (font->filepath) {
+ err = FT_New_Face(lib, font->filepath, 0, face);
+ }
+ else if (font->mem) {
+ err = FT_New_Memory_Face(lib, font->mem, (FT_Long)font->mem_size, 0, face);
+ }
+ BLI_mutex_unlock(&ft_lib_mutex);
+
+ if (err == FT_Err_Ok) {
+ font->face = *face;
+ font->face->generic.data = font;
+ font->face->generic.finalizer = blf_face_finalizer;
+ }
+ else {
+ /* Clear this on error to avoid exception in FTC_Manager_LookupFace. */
+ *face = NULL;
+ }
+
+ return err;
+}
+
+/**
+ * Called when the FreeType cache is removing a font size.
+ */
+static void blf_size_finalizer(void *object)
+{
+ FT_Size size = object;
+ FontBLF *font = (FontBLF *)size->generic.data;
+ font->ft_size = NULL;
+}
+
+/* -------------------------------------------------------------------- */
/** \name FreeType Utilities (Internal)
* \{ */
-/* Convert a FreeType 26.6 value representing an unscaled design size to factional pixels. */
+uint blf_get_char_index(FontBLF *font, uint charcode)
+{
+ if (font->flags & BLF_CACHED) {
+ /* Use char-map cache for much faster lookup. */
+ return FTC_CMapCache_Lookup(ftc_charmap_cache, font, -1, charcode);
+ }
+ /* Fonts that are not cached need to use the regular lookup function. */
+ return blf_ensure_face(font) ? FT_Get_Char_Index(font->face, charcode) : 0;
+}
+
+/* Convert a FreeType 26.6 value representing an unscaled design size to fractional pixels. */
static ft_pix blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value)
{
+ /* Make sure we have a valid font->ft_size. */
+ blf_ensure_size(font);
+
/* Scale value by font size using integer-optimized multiplication. */
- FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale);
+ FT_Long scaled = FT_MulFix(value, font->ft_size->metrics.x_scale);
/* Copied from FreeType's FT_Get_Kerning (with FT_KERNING_DEFAULT), scaling down */
- /* kerning distances at small ppem values so that they don't become too big. */
- if (font->face->size->metrics.x_ppem < 25) {
- scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25);
+ /* kerning distances at small PPEM values so that they don't become too big. */
+ if (font->ft_size->metrics.x_ppem < 25) {
+ scaled = FT_MulDiv(scaled, font->ft_size->metrics.x_ppem, 25);
}
return (ft_pix)scaled;
@@ -144,7 +223,7 @@ void blf_batch_draw_begin(FontBLF *font)
g_batch.ofs[1] = font->pos[1];
}
else {
- /* Offset is baked in modelview mat. */
+ /* Offset is baked in model-view matrix. */
zero_v2_int(g_batch.ofs);
}
@@ -152,16 +231,16 @@ void blf_batch_draw_begin(FontBLF *font)
float gpumat[4][4];
GPU_matrix_model_view_get(gpumat);
- bool mat_changed = (memcmp(gpumat, g_batch.mat, sizeof(g_batch.mat)) != 0);
+ bool mat_changed = equals_m4m4(gpumat, g_batch.mat) == false;
if (mat_changed) {
- /* Modelviewmat is no longer the same.
- * Flush cache but with the previous mat. */
+ /* Model view matrix is no longer the same.
+ * Flush cache but with the previous matrix. */
GPU_matrix_push();
GPU_matrix_set(g_batch.mat);
}
- /* flush cache if config is not the same. */
+ /* Flush cache if configuration is not the same. */
if (mat_changed || font_changed || shader_changed) {
blf_batch_draw();
g_batch.simple_shader = simple_shader;
@@ -174,7 +253,7 @@ void blf_batch_draw_begin(FontBLF *font)
if (mat_changed) {
GPU_matrix_pop();
- /* Save for next memcmp. */
+ /* Save for next `memcmp`. */
memcpy(g_batch.mat, gpumat, sizeof(g_batch.mat));
}
}
@@ -200,7 +279,7 @@ static GPUTexture *blf_batch_cache_texture_load(void)
int offset_x = bitmap_len_landed % tex_width;
int offset_y = bitmap_len_landed / tex_width;
- /* TODO(germano): Update more than one row in a single call. */
+ /* TODO(@germano): Update more than one row in a single call. */
while (remain) {
int remain_row = tex_width - offset_x;
int width = remain > remain_row ? remain_row : remain;
@@ -294,22 +373,22 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph
/* Small adjust if there is hinting. */
adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0);
- if (FT_HAS_KERNING(font->face) && g_prev) {
+ if (FT_HAS_KERNING(font) && g_prev) {
FT_Vector delta = {KERNING_ENTRY_UNSET};
/* Get unscaled kerning value from our cache if ASCII. */
- if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) {
+ if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < KERNING_CACHE_TABLE_SIZE)) {
delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c];
}
/* If not ASCII or not found in cache, ask FreeType for kerning. */
- if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) {
+ if (UNLIKELY(font->face && delta.x == KERNING_ENTRY_UNSET)) {
/* Note that this function sets delta values to zero on any error. */
FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta);
}
/* If ASCII we save this value to our cache for quicker access next time. */
- if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) {
+ if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < KERNING_CACHE_TABLE_SIZE)) {
font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x;
}
@@ -333,7 +412,7 @@ static void blf_font_draw_ex(FontBLF *font,
const char *str,
const size_t str_len,
struct ResultBLF *r_info,
- ft_pix pen_y)
+ const ft_pix pen_y)
{
GlyphBLF *g, *g_prev = NULL;
ft_pix pen_x = 0;
@@ -434,7 +513,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
/* buffer specific vars */
FontBufInfoBLF *buf_info = &font->buf_info;
const float *b_col_float = buf_info->col_float;
- const unsigned char *b_col_char = buf_info->col_char;
+ const uchar *b_col_char = buf_info->col_char;
int chx, chy;
int y, x;
@@ -523,7 +602,7 @@ static void blf_font_draw_buffer_ex(FontBLF *font,
const size_t buf_ofs = (((size_t)(chx + x) +
((size_t)(pen_y_px + y) * (size_t)buf_info->dims[0])) *
(size_t)buf_info->ch);
- unsigned char *cbuf = buf_info->cbuf + buf_ofs;
+ uchar *cbuf = buf_info->cbuf + buf_ofs;
uchar font_pixel[4];
font_pixel[0] = b_col_char[0];
@@ -836,7 +915,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font,
size_t i = 0, i_curr;
rcti gbox_px;
- if (str_len == 0) {
+ if (str_len == 0 || str[0] == 0) {
/* early output. */
return;
}
@@ -923,8 +1002,7 @@ static void blf_font_wrap_apply(FontBLF *font,
int lines = 0;
ft_pix pen_x_next = 0;
- /* Space between lines needs to be aligned to the pixel grid (T97310). */
- ft_pix line_height = FT_PIX_FLOOR(blf_font_height_max_ft_pix(font));
+ ft_pix line_height = blf_font_height_max_ft_pix(font);
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
@@ -1081,14 +1159,14 @@ int blf_font_count_missing_chars(FontBLF *font,
*r_tot_chars = 0;
while (i < str_len) {
- unsigned int c;
+ uint c;
if ((c = str[i]) < GLYPH_ASCII_TABLE_SIZE) {
i++;
}
else {
c = BLI_str_utf8_as_unicode_step(str, str_len, &i);
- if (FT_Get_Char_Index((font)->face, c) == 0) {
+ if (blf_get_char_index(font, c) == 0) {
missing++;
}
}
@@ -1105,18 +1183,9 @@ int blf_font_count_missing_chars(FontBLF *font,
static ft_pix blf_font_height_max_ft_pix(FontBLF *font)
{
- ft_pix height_max;
- FT_Face face = font->face;
- if (FT_IS_SCALABLE(face)) {
- height_max = ft_pix_from_int((int)(face->ascender - face->descender) *
- (int)face->size->metrics.y_ppem) /
- (ft_pix)face->units_per_EM;
- }
- else {
- height_max = (ft_pix)face->size->metrics.height;
- }
- /* can happen with size 1 fonts */
- return MAX2(height_max, ft_pix_from_int(1));
+ blf_ensure_size(font);
+ /* Metrics.height is rounded to pixel. Force minimum of one pixel. */
+ return MAX2((ft_pix)font->ft_size->metrics.height, ft_pix_from_int(1));
}
int blf_font_height_max(FontBLF *font)
@@ -1126,18 +1195,9 @@ int blf_font_height_max(FontBLF *font)
static ft_pix blf_font_width_max_ft_pix(FontBLF *font)
{
- ft_pix width_max;
- const FT_Face face = font->face;
- if (FT_IS_SCALABLE(face)) {
- width_max = ft_pix_from_int((int)(face->bbox.xMax - face->bbox.xMin) *
- (int)face->size->metrics.x_ppem) /
- (ft_pix)face->units_per_EM;
- }
- else {
- width_max = (ft_pix)face->size->metrics.max_advance;
- }
- /* can happen with size 1 fonts */
- return MAX2(width_max, ft_pix_from_int(1));
+ blf_ensure_size(font);
+ /* Metrics.max_advance is rounded to pixel. Force minimum of one pixel. */
+ return MAX2((ft_pix)font->ft_size->metrics.max_advance, ft_pix_from_int(1));
}
int blf_font_width_max(FontBLF *font)
@@ -1147,17 +1207,19 @@ int blf_font_width_max(FontBLF *font)
int blf_font_descender(FontBLF *font)
{
- return ft_pix_to_int((ft_pix)font->face->size->metrics.descender);
+ blf_ensure_size(font);
+ return ft_pix_to_int((ft_pix)font->ft_size->metrics.descender);
}
int blf_font_ascender(FontBLF *font)
{
- return ft_pix_to_int((ft_pix)font->face->size->metrics.ascender);
+ blf_ensure_size(font);
+ return ft_pix_to_int((ft_pix)font->ft_size->metrics.ascender);
}
char *blf_display_name(FontBLF *font)
{
- if (!font->face->family_name) {
+ if (!blf_ensure_face(font) || !font->face->family_name) {
return NULL;
}
return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name);
@@ -1172,16 +1234,34 @@ char *blf_display_name(FontBLF *font)
int blf_font_init(void)
{
memset(&g_batch, 0, sizeof(g_batch));
- BLI_spin_init(&ft_lib_mutex);
- BLI_spin_init(&blf_glyph_cache_mutex);
- return FT_Init_FreeType(&ft_lib);
+ BLI_mutex_init(&ft_lib_mutex);
+ int err = FT_Init_FreeType(&ft_lib);
+ if (err == FT_Err_Ok) {
+ /* Create a FreeType cache manager. */
+ err = FTC_Manager_New(ft_lib,
+ BLF_CACHE_MAX_FACES,
+ BLF_CACHE_MAX_SIZES,
+ BLF_CACHE_BYTES,
+ blf_cache_face_requester,
+ NULL,
+ &ftc_manager);
+ if (err == FT_Err_Ok) {
+ /* Create a charmap cache to speed up glyph index lookups. */
+ err = FTC_CMapCache_New(ftc_manager, &ftc_charmap_cache);
+ }
+ }
+ return err;
}
void blf_font_exit(void)
{
- FT_Done_FreeType(ft_lib);
- BLI_spin_end(&ft_lib_mutex);
- BLI_spin_end(&blf_glyph_cache_mutex);
+ BLI_mutex_end(&ft_lib_mutex);
+ if (ftc_manager) {
+ FTC_Manager_Done(ftc_manager);
+ }
+ if (ft_lib) {
+ FT_Done_FreeType(ft_lib);
+ }
blf_batch_draw_exit();
}
@@ -1238,20 +1318,38 @@ static void blf_font_fill(FontBLF *font)
font->buf_info.col_init[1] = 0;
font->buf_info.col_init[2] = 0;
font->buf_info.col_init[3] = 0;
-
- font->ft_lib = ft_lib;
- font->ft_lib_mutex = &ft_lib_mutex;
- font->glyph_cache_mutex = &blf_glyph_cache_mutex;
}
-FontBLF *blf_font_new(const char *name, const char *filepath)
+/**
+ * Create an FT_Face for this font if not already existing.
+ */
+bool blf_ensure_face(FontBLF *font)
{
- FontBLF *font;
+ if (font->face) {
+ return true;
+ }
+
+ if (font->flags & BLF_BAD_FONT) {
+ return false;
+ }
+
FT_Error err;
- char *mfile;
- font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new");
- err = FT_New_Face(ft_lib, filepath, 0, &font->face);
+ if (font->flags & BLF_CACHED) {
+ err = FTC_Manager_LookupFace(ftc_manager, font, &font->face);
+ }
+ else {
+ BLI_mutex_lock(&ft_lib_mutex);
+ if (font->filepath) {
+ err = FT_New_Face(font->ft_lib, font->filepath, 0, &font->face);
+ }
+ if (font->mem) {
+ err = FT_New_Memory_Face(font->ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face);
+ }
+ font->face->generic.data = font;
+ BLI_mutex_unlock(&ft_lib_mutex);
+ }
+
if (err) {
if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) {
printf("Format of this font file is not supported\n");
@@ -1259,8 +1357,13 @@ FontBLF *blf_font_new(const char *name, const char *filepath)
else {
printf("Error encountered while opening font file\n");
}
- MEM_freeN(font);
- return NULL;
+ font->flags |= BLF_BAD_FONT;
+ return false;
+ }
+
+ if (font->face && !(font->face->face_flags & FT_FACE_FLAG_SCALABLE)) {
+ printf("Font is not scalable\n");
+ return false;
}
err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE);
@@ -1272,48 +1375,49 @@ FontBLF *blf_font_new(const char *name, const char *filepath)
}
if (err) {
printf("Can't set a character map!\n");
- FT_Done_Face(font->face);
- MEM_freeN(font);
- return NULL;
+ font->flags |= BLF_BAD_FONT;
+ return false;
}
- mfile = blf_dir_metrics_search(filepath);
- if (mfile) {
- err = FT_Attach_File(font->face, mfile);
- if (err) {
- fprintf(stderr, "FT_Attach_File failed to load '%s' with error %d\n", filepath, (int)err);
+ if (font->filepath) {
+ char *mfile = blf_dir_metrics_search(font->filepath);
+ if (mfile) {
+ err = FT_Attach_File(font->face, mfile);
+ if (err) {
+ fprintf(stderr,
+ "FT_Attach_File failed to load '%s' with error %d\n",
+ font->filepath,
+ (int)err);
+ }
+ MEM_freeN(mfile);
}
- MEM_freeN(mfile);
}
- if (FT_HAS_MULTIPLE_MASTERS(font->face)) {
- FT_Get_MM_Var(font->face, &(font->variations));
+ if (!(font->flags & BLF_CACHED)) {
+ /* Not cached so point at the face's size for convenience. */
+ font->ft_size = font->face->size;
}
- font->name = BLI_strdup(name);
- font->filepath = BLI_strdup(filepath);
- blf_font_fill(font);
+ font->face_flags = font->face->face_flags;
+
+ if (FT_HAS_MULTIPLE_MASTERS(font)) {
+ FT_Get_MM_Var(font->face, &(font->variations));
+ }
/* Save TrueType table with bits to quickly test most unicode block coverage. */
TT_OS2 *os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(font->face, FT_SFNT_OS2);
if (os2_table) {
- font->UnicodeRanges[0] = (uint)os2_table->ulUnicodeRange1;
- font->UnicodeRanges[1] = (uint)os2_table->ulUnicodeRange2;
- font->UnicodeRanges[2] = (uint)os2_table->ulUnicodeRange3;
- font->UnicodeRanges[3] = (uint)os2_table->ulUnicodeRange4;
- }
-
- /* Detect "Last resort" fonts. They have everything. Usually except last 5 bits. */
- if (font->UnicodeRanges[0] == 0xffffffffU && font->UnicodeRanges[1] == 0xffffffffU &&
- font->UnicodeRanges[2] == 0xffffffffU && font->UnicodeRanges[3] >= 0x7FFFFFFU) {
- font->flags |= BLF_LAST_RESORT;
+ font->unicode_ranges[0] = (uint)os2_table->ulUnicodeRange1;
+ font->unicode_ranges[1] = (uint)os2_table->ulUnicodeRange2;
+ font->unicode_ranges[2] = (uint)os2_table->ulUnicodeRange3;
+ font->unicode_ranges[3] = (uint)os2_table->ulUnicodeRange4;
}
- if (FT_IS_FIXED_WIDTH(font->face)) {
+ if (FT_IS_FIXED_WIDTH(font)) {
font->flags |= BLF_MONOSPACED;
}
- if (FT_HAS_KERNING(font->face)) {
+ if (FT_HAS_KERNING(font) && !font->kerning_cache) {
/* Create kerning cache table and fill with value indicating "unset". */
font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__);
for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) {
@@ -1323,49 +1427,138 @@ FontBLF *blf_font_new(const char *name, const char *filepath)
}
}
- return font;
+ return true;
}
-void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, int mem_size)
+struct FaceDetails {
+ char name[50];
+ uint coverage1;
+ uint coverage2;
+ uint coverage3;
+ uint coverage4;
+};
+
+/* Details about the fallback fonts we ship, so that we can load only when needed. */
+static const struct FaceDetails static_face_details[] = {
+ {"lastresort.woff2", UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX},
+ {"Noto Sans CJK Regular.woff2", 0x30000083L, 0x2BDF3C10L, 0x16L, 0},
+ {"NotoEmoji-VariableFont_wght.woff2", 0x80000003L, 0x241E4ACL, 0x14000000L, 0x4000000L},
+ {"NotoSansArabic-VariableFont_wdth,wght.woff2",
+ TT_UCR_ARABIC,
+ (uint)TT_UCR_ARABIC_PRESENTATION_FORMS_A,
+ TT_UCR_ARABIC_PRESENTATION_FORMS_B,
+ 0},
+ {"NotoSansArmenian-VariableFont_wdth,wght.woff2", TT_UCR_ARMENIAN, 0, 0, 0},
+ {"NotoSansBengali-VariableFont_wdth,wght.woff2", TT_UCR_BENGALI, 0, 0, 0},
+ {"NotoSansDevanagari-Regular.woff2", TT_UCR_DEVANAGARI, 0, 0, 0},
+ {"NotoSansEthiopic-Regular.woff2", 0, 0, TT_UCR_ETHIOPIC, 0},
+ {"NotoSansGeorgian-VariableFont_wdth,wght.woff2", TT_UCR_GEORGIAN, 0, 0, 0},
+ {"NotoSansGujarati-Regular.woff2", TT_UCR_GUJARATI, 0, 0, 0},
+ {"NotoSansGurmukhi-VariableFont_wdth,wght.woff2", TT_UCR_GURMUKHI, 0, 0, 0},
+ {"NotoSansHebrew-Regular.woff2", TT_UCR_HEBREW, 0, 0, 0},
+ {"NotoSansJavanese-Regular.woff2", 0x80000003L, 0x2000L, 0, 0},
+ {"NotoSansKannada-VariableFont_wdth,wght.woff2", TT_UCR_KANNADA, 0, 0, 0},
+ {"NotoSansMalayalam-VariableFont_wdth,wght.woff2", TT_UCR_MALAYALAM, 0, 0, 0},
+ {"NotoSansMath-Regular.woff2", 0, TT_UCR_MATHEMATICAL_OPERATORS, 0, 0},
+ {"NotoSansMyanmar-Regular.woff2", 0, 0, TT_UCR_MYANMAR, 0},
+ {"NotoSansSymbols-VariableFont_wght.woff2", 0x3L, 0x200E4B4L, 0, 0},
+ {"NotoSansSymbols2-Regular.woff2", 0x80000003L, 0x200E3E4L, 0x40020L, 0x580A048L},
+ {"NotoSansTamil-VariableFont_wdth,wght.woff2", TT_UCR_TAMIL, 0, 0, 0},
+ {"NotoSansTelugu-VariableFont_wdth,wght.woff2", TT_UCR_TELUGU, 0, 0, 0},
+ {"NotoSansThai-VariableFont_wdth,wght.woff2", TT_UCR_THAI, 0, 0, 0},
+};
+
+/**
+ * Create a new font from filename OR memory pointer.
+ * For normal operation pass NULL as FT_Library object. Pass a custom FT_Library if you
+ * want to use the font without its lifetime being managed by the FreeType cache subsystem.
+ */
+FontBLF *blf_font_new_ex(const char *name,
+ const char *filepath,
+ const uchar *mem,
+ const size_t mem_size,
+ void *ft_library)
{
- FT_Open_Args open;
+ FontBLF *font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new");
- open.flags = FT_OPEN_MEMORY;
- open.memory_base = (const FT_Byte *)mem;
- open.memory_size = mem_size;
- FT_Attach_Stream(font->face, &open);
-}
+ font->name = BLI_strdup(name);
+ font->filepath = filepath ? BLI_strdup(filepath) : NULL;
+ if (mem) {
+ font->mem = (void *)mem;
+ font->mem_size = mem_size;
+ }
+ blf_font_fill(font);
-FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size)
-{
- FontBLF *font;
- FT_Error err;
+ if (ft_library && ((FT_Library)ft_library != ft_lib)) {
+ font->ft_lib = (FT_Library)ft_library;
+ }
+ else {
+ font->ft_lib = ft_lib;
+ font->flags |= BLF_CACHED;
+ }
- font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new_from_mem");
- err = FT_New_Memory_Face(ft_lib, mem, mem_size, 0, &font->face);
- if (err) {
- MEM_freeN(font);
- return NULL;
+ font->ft_lib = ft_library ? (FT_Library)ft_library : ft_lib;
+
+ BLI_mutex_init(&font->glyph_cache_mutex);
+
+ /* If we have static details about this font file, we don't have to load the Face yet. */
+ bool face_needed = true;
+
+ if (font->filepath) {
+ const struct FaceDetails *static_details = NULL;
+ char filename[256];
+ for (int i = 0; i < (int)ARRAY_SIZE(static_face_details); i++) {
+ BLI_split_file_part(font->filepath, filename, sizeof(filename));
+ if (STREQ(static_face_details[i].name, filename)) {
+ static_details = &static_face_details[i];
+ font->unicode_ranges[0] = static_details->coverage1;
+ font->unicode_ranges[1] = static_details->coverage2;
+ font->unicode_ranges[2] = static_details->coverage3;
+ font->unicode_ranges[3] = static_details->coverage4;
+ face_needed = false;
+ break;
+ }
+ }
}
- err = FT_Select_Charmap(font->face, ft_encoding_unicode);
- if (err) {
- printf("Can't set the unicode character map!\n");
- FT_Done_Face(font->face);
- MEM_freeN(font);
- return NULL;
+ if (face_needed) {
+ if (!blf_ensure_face(font)) {
+ blf_font_free(font);
+ return NULL;
+ }
}
- if (FT_HAS_MULTIPLE_MASTERS(font->face)) {
- FT_Get_MM_Var(font->face, &(font->variations));
+ /* Detect "Last resort" fonts. They have everything. Usually except last 5 bits. */
+ if (font->unicode_ranges[0] == 0xffffffffU && font->unicode_ranges[1] == 0xffffffffU &&
+ font->unicode_ranges[2] == 0xffffffffU && font->unicode_ranges[3] >= 0x7FFFFFFU) {
+ font->flags |= BLF_LAST_RESORT;
}
- font->name = BLI_strdup(name);
- font->filepath = NULL;
- blf_font_fill(font);
return font;
}
+FontBLF *blf_font_new(const char *name, const char *filepath)
+{
+ return blf_font_new_ex(name, filepath, NULL, 0, NULL);
+}
+
+FontBLF *blf_font_new_from_mem(const char *name, const uchar *mem, const size_t mem_size)
+{
+ return blf_font_new_ex(name, NULL, mem, mem_size, NULL);
+}
+
+void blf_font_attach_from_mem(FontBLF *font, const uchar *mem, const size_t mem_size)
+{
+ FT_Open_Args open;
+
+ open.flags = FT_OPEN_MEMORY;
+ open.memory_base = (const FT_Byte *)mem;
+ open.memory_size = (FT_Long)mem_size;
+ if (blf_ensure_face(font)) {
+ FT_Attach_Stream(font->face, &open);
+ }
+}
+
void blf_font_free(FontBLF *font)
{
blf_glyph_cache_clear(font);
@@ -1375,16 +1568,29 @@ void blf_font_free(FontBLF *font)
}
if (font->variations) {
- FT_Done_MM_Var(ft_lib, font->variations);
+ FT_Done_MM_Var(font->ft_lib, font->variations);
}
- FT_Done_Face(font->face);
+ if (font->face) {
+ BLI_mutex_lock(&ft_lib_mutex);
+ if (font->flags & BLF_CACHED) {
+ FTC_Manager_RemoveFaceID(ftc_manager, font);
+ }
+ else {
+ FT_Done_Face(font->face);
+ }
+ BLI_mutex_unlock(&ft_lib_mutex);
+ font->face = NULL;
+ }
if (font->filepath) {
MEM_freeN(font->filepath);
}
if (font->name) {
MEM_freeN(font->name);
}
+
+ BLI_mutex_end(&font->glyph_cache_mutex);
+
MEM_freeN(font);
}
@@ -1394,24 +1600,64 @@ void blf_font_free(FontBLF *font)
/** \name Font Configure
* \{ */
-bool blf_font_size(FontBLF *font, float size, unsigned int dpi)
+void blf_ensure_size(FontBLF *font)
+{
+ if (font->ft_size || !(font->flags & BLF_CACHED)) {
+ return;
+ }
+
+ FTC_ScalerRec scaler = {0};
+ scaler.face_id = font;
+ scaler.width = 0;
+ scaler.height = round_fl_to_uint(font->size * 64.0f);
+ scaler.pixel = 0;
+ scaler.x_res = font->dpi;
+ scaler.y_res = font->dpi;
+ if (FTC_Manager_LookupSize(ftc_manager, &scaler, &font->ft_size) == FT_Err_Ok) {
+ font->ft_size->generic.data = (void *)font;
+ font->ft_size->generic.finalizer = blf_size_finalizer;
+ return;
+ }
+
+ BLI_assert_unreachable();
+}
+
+bool blf_font_size(FontBLF *font, float size, uint dpi)
{
+ if (!blf_ensure_face(font)) {
+ return false;
+ }
+
/* FreeType uses fixed-point integers in 64ths. */
- FT_F26Dot6 ft_size = lroundf(size * 64.0f);
+ FT_UInt ft_size = round_fl_to_uint(size * 64.0f);
/* Adjust our new size to be on even 64ths. */
size = (float)ft_size / 64.0f;
if (font->size != size || font->dpi != dpi) {
- if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) == FT_Err_Ok) {
- font->size = size;
- font->dpi = dpi;
+ if (font->flags & BLF_CACHED) {
+ FTC_ScalerRec scaler = {0};
+ scaler.face_id = font;
+ scaler.width = 0;
+ scaler.height = ft_size;
+ scaler.pixel = 0;
+ scaler.x_res = dpi;
+ scaler.y_res = dpi;
+ if (FTC_Manager_LookupSize(ftc_manager, &scaler, &font->ft_size) != FT_Err_Ok) {
+ return false;
+ }
+ font->ft_size->generic.data = (void *)font;
+ font->ft_size->generic.finalizer = blf_size_finalizer;
}
else {
- printf("The current font does not support the size, %f and DPI, %u\n", size, dpi);
- return false;
+ if (FT_Set_Char_Size(font->face, 0, ft_size, dpi, dpi) != FT_Err_Ok) {
+ return false;
+ }
+ font->ft_size = font->face->size;
}
}
+ font->size = size;
+ font->dpi = dpi;
return true;
}
diff --git a/source/blender/blenfont/intern/blf_font_default.c b/source/blender/blenfont/intern/blf_font_default.c
index 1bde25b5776..d35692f6eae 100644
--- a/source/blender/blenfont/intern/blf_font_default.c
+++ b/source/blender/blenfont/intern/blf_font_default.c
@@ -16,6 +16,10 @@
#include "BKE_appdir.h"
+#ifdef WIN32
+# include "BLI_winstuff.h"
+#endif
+
static int blf_load_font_default(const char *filename, const bool unique)
{
const char *dir = BKE_appdir_folder_id(BLENDER_DATAFILES, BLF_DATAFILES_FONTS_DIR);
@@ -47,34 +51,51 @@ int BLF_load_mono_default(const bool unique)
return font_id;
}
+static void blf_load_datafiles_dir(void)
+{
+ const char *datafiles_fonts_dir = BLF_DATAFILES_FONTS_DIR SEP_STR;
+ const char *path = BKE_appdir_folder_id(BLENDER_DATAFILES, datafiles_fonts_dir);
+ if (UNLIKELY(!path)) {
+ fprintf(stderr, "Font data directory \"%s\" could not be detected!\n", datafiles_fonts_dir);
+ return;
+ }
+ if (UNLIKELY(!BLI_exists(path))) {
+ fprintf(stderr, "Font data directory \"%s\" does not exist!\n", path);
+ return;
+ }
+
+ struct direntry *file_list;
+ uint file_list_num = BLI_filelist_dir_contents(path, &file_list);
+ for (int i = 0; i < file_list_num; i++) {
+ if (S_ISDIR(file_list[i].s.st_mode)) {
+ continue;
+ }
+
+ const char *filepath = file_list[i].path;
+ if (!BLI_path_extension_check_n(
+ filepath, ".ttf", ".ttc", ".otf", ".otc", ".woff", ".woff2", NULL)) {
+ continue;
+ }
+ if (BLF_is_loaded(filepath)) {
+ continue;
+ }
+
+ /* Attempt to load the font. */
+ int font_id = BLF_load(filepath);
+ if (font_id == -1) {
+ fprintf(stderr, "Unable to load font: %s\n", filepath);
+ continue;
+ }
+
+ BLF_enable(font_id, BLF_DEFAULT);
+ }
+ BLI_filelist_free(file_list, file_list_num);
+}
+
void BLF_load_font_stack()
{
/* Load these if not already, might have been replaced by user custom. */
BLF_load_default(false);
BLF_load_mono_default(false);
-
- const char *path = BKE_appdir_folder_id(BLENDER_DATAFILES, BLF_DATAFILES_FONTS_DIR SEP_STR);
- if (path && BLI_exists(path)) {
- struct direntry *dir;
- uint num_files = BLI_filelist_dir_contents(path, &dir);
- for (int f = 0; f < num_files; f++) {
- if (!FILENAME_IS_CURRPAR(dir[f].relname) && !BLI_is_dir(dir[f].path)) {
- if (!BLF_is_loaded(dir[f].path)) {
- int font_id = BLF_load(dir[f].path);
- if (font_id == -1) {
- fprintf(stderr, "Unable to load font: %s\n", dir[f].path);
- }
- else {
- BLF_enable(font_id, BLF_DEFAULT);
- /* TODO: FontBLF will later load FT_Face on demand. When this is in
- * place we can drop this face now since we have all needed data. */
- }
- }
- }
- }
- BLI_filelist_free(dir, num_files);
- }
- else {
- fprintf(stderr, "Fonts not found at %s\n", path);
- }
+ blf_load_datafiles_dir();
}
diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c
index 215f79e6795..c08d52307b7 100644
--- a/source/blender/blenfont/intern/blf_glyph.c
+++ b/source/blender/blenfont/intern/blf_glyph.c
@@ -23,9 +23,6 @@
#include "MEM_guardedalloc.h"
-#include "DNA_userdef_types.h"
-#include "DNA_vec_types.h"
-
#include "BLI_listbase.h"
#include "BLI_rect.h"
#include "BLI_threads.h"
@@ -33,7 +30,6 @@
#include "BLF_api.h"
#include "GPU_capabilities.h"
-#include "GPU_immediate.h"
#include "blf_internal.h"
#include "blf_internal_types.h"
@@ -42,6 +38,13 @@
#include "BLI_strict_flags.h"
#include "BLI_string_utf8.h"
+/**
+ * Convert glyph coverage amounts to lightness values. Uses a LUT that perceptually improves
+ * anti-aliasing and results in text that looks a bit fuller and slightly brighter. This should
+ * be reconsidered in some - or all - cases when we transform the entire UI.
+ */
+#define BLF_GAMMA_CORRECT_GLYPHS
+
/* -------------------------------------------------------------------- */
/** \name Internal Utilities
* \{ */
@@ -60,7 +63,7 @@ static FT_Fixed to_16dot16(double val)
/** \name Glyph Cache
* \{ */
-static GlyphCacheBLF *blf_glyph_cache_find(FontBLF *font, float size, unsigned int dpi)
+static GlyphCacheBLF *blf_glyph_cache_find(const FontBLF *font, const float size, uint dpi)
{
GlyphCacheBLF *gc = (GlyphCacheBLF *)font->cache.first;
while (gc) {
@@ -93,17 +96,19 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table));
memset(gc->bucket, 0, sizeof(gc->bucket));
+ blf_ensure_size(font);
+
/* Determine ideal fixed-width size for monospaced output. */
- FT_UInt gindex = FT_Get_Char_Index(font->face, U'0');
- if (gindex) {
+ FT_UInt gindex = blf_get_char_index(font, U'0');
+ if (gindex && font->face) {
FT_Fixed advance = 0;
FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance);
/* Use CSS 'ch unit' width, advance of zero character. */
gc->fixed_width = (int)(advance >> 16);
}
else {
- /* Font does not contain "0" so use CSS fallback of 1/2 of em. */
- gc->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6);
+ /* Font does not have a face or does not contain "0" so use CSS fallback of 1/2 of em. */
+ gc->fixed_width = (int)((font->ft_size->metrics.height / 2) >> 6);
}
if (gc->fixed_width < 1) {
gc->fixed_width = 1;
@@ -115,7 +120,7 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font)
{
- BLI_spin_lock(font->glyph_cache_mutex);
+ BLI_mutex_lock(&font->glyph_cache_mutex);
GlyphCacheBLF *gc = blf_glyph_cache_find(font, font->size, font->dpi);
@@ -128,7 +133,7 @@ GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font)
void blf_glyph_cache_release(FontBLF *font)
{
- BLI_spin_unlock(font->glyph_cache_mutex);
+ BLI_mutex_unlock(&font->glyph_cache_mutex);
}
static void blf_glyph_cache_free(GlyphCacheBLF *gc)
@@ -152,13 +157,13 @@ void blf_glyph_cache_clear(FontBLF *font)
{
GlyphCacheBLF *gc;
- BLI_spin_lock(font->glyph_cache_mutex);
+ BLI_mutex_lock(&font->glyph_cache_mutex);
while ((gc = BLI_pophead(&font->cache))) {
blf_glyph_cache_free(gc);
}
- BLI_spin_unlock(font->glyph_cache_mutex);
+ BLI_mutex_unlock(&font->glyph_cache_mutex);
}
/**
@@ -166,7 +171,7 @@ void blf_glyph_cache_clear(FontBLF *font)
*
* \return NULL if not found.
*/
-static GlyphBLF *blf_glyph_cache_find_glyph(GlyphCacheBLF *gc, uint charcode)
+static GlyphBLF *blf_glyph_cache_find_glyph(const GlyphCacheBLF *gc, uint charcode)
{
if (charcode < GLYPH_ASCII_TABLE_SIZE) {
return gc->glyph_ascii_table[charcode];
@@ -182,6 +187,43 @@ static GlyphBLF *blf_glyph_cache_find_glyph(GlyphCacheBLF *gc, uint charcode)
return NULL;
}
+#ifdef BLF_GAMMA_CORRECT_GLYPHS
+
+/**
+ * Gamma correction of glyph coverage values with widely-recommended gamma of 1.43.
+ * "The reasons are historical. Because so many programmers have neglected gamma blending for so
+ * long, people who have created fonts have tried to work around the problem of fonts looking too
+ * thin by just making the fonts thicker! Obviously it doesn't help the jaggedness, but it does
+ * make them look the proper weight, as originally intended. The obvious problem with this is
+ * that if we want to gamma blend correctly many older fonts will look wrong. So we compromise,
+ * and use a lower gamma value, so we get a bit better anti-aliasing, but the fonts don't look too
+ * heavy."
+ * https://www.puredevsoftware.com/blog/2019/01/22/sub-pixel-gamma-correct-font-rendering/
+ */
+static uchar blf_glyph_gamma(uchar c)
+{
+ /* The following is `(char)(powf(c / 256.0f, 1.0f / 1.43f) * 256.0f)`. */
+ static const uchar gamma[256] = {
+ 0, 5, 9, 11, 14, 16, 19, 21, 23, 25, 26, 28, 30, 32, 34, 35, 37, 38,
+ 40, 41, 43, 44, 46, 47, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 62, 64,
+ 65, 66, 67, 69, 70, 71, 72, 73, 75, 76, 77, 78, 79, 80, 82, 83, 84, 85,
+ 86, 87, 88, 89, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 133, 134, 135, 136, 137, 138, 139,
+ 140, 141, 142, 143, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155,
+ 156, 157, 157, 158, 159, 160, 161, 162, 163, 163, 164, 165, 166, 167, 168, 168, 169, 170,
+ 171, 172, 173, 173, 174, 175, 176, 177, 178, 178, 179, 180, 181, 182, 182, 183, 184, 185,
+ 186, 186, 187, 188, 189, 190, 190, 191, 192, 193, 194, 194, 195, 196, 197, 198, 198, 199,
+ 200, 201, 201, 202, 203, 204, 205, 205, 206, 207, 208, 208, 209, 210, 211, 211, 212, 213,
+ 214, 214, 215, 216, 217, 217, 218, 219, 220, 220, 221, 222, 223, 223, 224, 225, 226, 226,
+ 227, 228, 229, 229, 230, 231, 231, 232, 233, 234, 234, 235, 236, 237, 237, 238, 239, 239,
+ 240, 241, 242, 242, 243, 244, 244, 245, 246, 247, 247, 248, 249, 249, 250, 251, 251, 252,
+ 253, 254, 254, 255};
+ return gamma[c];
+}
+
+#endif /* BLF_GAMMA_CORRECT_GLYPHS */
+
/**
* Add a rendered glyph to a cache.
*/
@@ -217,11 +259,19 @@ static GlyphBLF *blf_glyph_cache_add_glyph(
glyph->bitmap.buffer[i] = glyph->bitmap.buffer[i] ? 255 : 0;
}
}
+ else {
+#ifdef BLF_GAMMA_CORRECT_GLYPHS
+ /* Convert coverage amounts to perceptually-improved lightness values. */
+ for (int i = 0; i < buffer_size; i++) {
+ glyph->bitmap.buffer[i] = blf_glyph_gamma(glyph->bitmap.buffer[i]);
+ }
+#endif /* BLF_GAMMA_CORRECT_GLYPHS */
+ }
g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap");
memcpy(g->bitmap, glyph->bitmap.buffer, (size_t)buffer_size);
}
- unsigned int key = blf_hash(g->c);
+ const uint key = blf_hash(g->c);
BLI_addhead(&(gc->bucket[key]), g);
if (charcode < GLYPH_ASCII_TABLE_SIZE) {
gc->glyph_ascii_table[charcode] = g;
@@ -230,18 +280,24 @@ static GlyphBLF *blf_glyph_cache_add_glyph(
return g;
}
-/* This table can be used to find a coverage bit based on a charcode. later we can get default
- * language and script from codepoint. */
+/** \} */
-typedef struct eUnicodeBlock {
- unsigned int first;
- unsigned int last;
+/* -------------------------------------------------------------------- */
+/** \name Glyph Unicode Block Lookup
+ *
+ * This table can be used to find a coverage bit based on a charcode.
+ * Later we can get default language and script from `codepoint`.
+ */
+
+struct UnicodeBlock {
+ uint first;
+ uint last;
int coverage_bit; /* 0-122. -1 is N/A. */
/* Later we add primary script and language for Harfbuzz, data from
* https://en.wikipedia.org/wiki/Unicode_block */
-} eUnicodeBlock;
+};
-static eUnicodeBlock unicode_blocks[] = {
+static const struct UnicodeBlock unicode_blocks[] = {
/* Must be in ascending order by start of range. */
{0x0, 0x7F, 0}, /* Basic Latin. */
{0x80, 0xFF, 1}, /* Latin-1 Supplement. */
@@ -500,8 +556,10 @@ static eUnicodeBlock unicode_blocks[] = {
{0xE0100, 0xE01EF, 91}, /* Variation Selectors. */
{0xF0000, 0x10FFFD, 90}}; /* Private Use Supplementary. */
-/* Find a unicode block that a charcode belongs to. */
-static eUnicodeBlock *blf_charcode_to_unicode_block(uint charcode)
+/**
+ * Find a unicode block that a `charcode` belongs to.
+ */
+static const struct UnicodeBlock *blf_charcode_to_unicode_block(const uint charcode)
{
if (charcode < 0x80) {
/* Shortcut to Basic Latin. */
@@ -512,14 +570,13 @@ static eUnicodeBlock *blf_charcode_to_unicode_block(uint charcode)
int min = 0;
int max = ARRAY_SIZE(unicode_blocks) - 1;
- int mid;
if (charcode < unicode_blocks[0].first || charcode > unicode_blocks[max].last) {
return NULL;
}
while (max >= min) {
- mid = (min + max) / 2;
+ const int mid = (min + max) / 2;
if (charcode > unicode_blocks[mid].last) {
min = mid + 1;
}
@@ -537,26 +594,26 @@ static eUnicodeBlock *blf_charcode_to_unicode_block(uint charcode)
static int blf_charcode_to_coverage_bit(uint charcode)
{
int coverage_bit = -1;
- eUnicodeBlock *block = blf_charcode_to_unicode_block(charcode);
+ const struct UnicodeBlock *block = blf_charcode_to_unicode_block(charcode);
if (block) {
coverage_bit = block->coverage_bit;
}
if (coverage_bit < 0 && charcode > 0xFFFF) {
/* No coverage bit, but OpenType specs v.1.3+ says bit 57 implies that there
- * are codepoints supported beyond the BMP, so only check fonts with this set. */
+ * are code-points supported beyond the BMP, so only check fonts with this set. */
coverage_bit = 57;
}
return coverage_bit;
}
-static bool blf_font_has_coverage_bit(FontBLF *font, int coverage_bit)
+static bool blf_font_has_coverage_bit(const FontBLF *font, int coverage_bit)
{
if (coverage_bit < 0) {
return false;
}
- return (font->UnicodeRanges[(uint)coverage_bit >> 5] & (1u << ((uint)coverage_bit % 32)));
+ return (font->unicode_ranges[(uint)coverage_bit >> 5] & (1u << ((uint)coverage_bit % 32)));
}
/**
@@ -565,11 +622,16 @@ static bool blf_font_has_coverage_bit(FontBLF *font, int coverage_bit)
*/
static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode)
{
- FT_UInt glyph_index = FT_Get_Char_Index((*font)->face, charcode);
+ FT_UInt glyph_index = blf_get_char_index(*font, charcode);
if (glyph_index) {
return glyph_index;
}
+ /* Only fonts managed by the cache can fallback. */
+ if (!((*font)->flags & BLF_CACHED)) {
+ return 0;
+ }
+
/* Not found in main font, so look in the others. */
FontBLF *last_resort = NULL;
int coverage_bit = blf_charcode_to_coverage_bit(charcode);
@@ -584,7 +646,7 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode
continue;
}
if (coverage_bit < 0 || blf_font_has_coverage_bit(f, coverage_bit)) {
- glyph_index = FT_Get_Char_Index(f->face, charcode);
+ glyph_index = blf_get_char_index(f, charcode);
if (glyph_index) {
*font = f;
return glyph_index;
@@ -592,9 +654,13 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode
}
}
+#ifdef DEBUG
+ printf("Unicode character U+%04X not found in loaded fonts. \n", charcode);
+#endif
+
/* Not found in the stack, return from Last Resort if there is one. */
if (last_resort) {
- glyph_index = FT_Get_Char_Index(last_resort->face, charcode);
+ glyph_index = blf_get_char_index(last_resort, charcode);
if (glyph_index) {
*font = last_resort;
return glyph_index;
@@ -604,6 +670,12 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode
return 0;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Glyph Load
+ * \{ */
+
/**
* Load a glyph into the glyph slot of a font's face object.
*/
@@ -638,19 +710,19 @@ static FT_GlyphSlot blf_glyph_load(FontBLF *font, FT_UInt glyph_index)
return NULL;
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Glyph Render
+ * \{ */
+
/**
* Convert a glyph from outlines to a bitmap that we can display.
*/
static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph)
{
- int render_mode;
-
- if (font->flags & BLF_MONOCHROME) {
- render_mode = FT_RENDER_MODE_MONO;
- }
- else {
- render_mode = FT_RENDER_MODE_NORMAL;
- }
+ const int render_mode = (font->flags & BLF_MONOCHROME) ? FT_RENDER_MODE_MONO :
+ FT_RENDER_MODE_NORMAL;
/* Render the glyph curves to a bitmap. */
FT_Error err = FT_Render_Glyph(glyph, render_mode);
@@ -689,17 +761,19 @@ static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph)
*
* \param variations: Variation descriptors from `FT_Get_MM_Var`.
* \param tag: Axis tag (4-character string as uint), like 'wght'
- * \param axis_index: returns index of axis in variations array.
+ * \param r_axis_index: returns index of axis in variations array.
*/
-static FT_Var_Axis *blf_var_axis_by_tag(FT_MM_Var *variations, uint tag, int *axis_index)
+static const FT_Var_Axis *blf_var_axis_by_tag(const FT_MM_Var *variations,
+ const uint tag,
+ int *r_axis_index)
{
- *axis_index = -1;
+ *r_axis_index = -1;
if (!variations) {
return NULL;
}
for (int i = 0; i < (int)variations->num_axis; i++) {
if (variations->axis[i].tag == tag) {
- *axis_index = i;
+ *r_axis_index = i;
return &(variations->axis)[i];
break;
}
@@ -713,7 +787,7 @@ static FT_Var_Axis *blf_var_axis_by_tag(FT_MM_Var *variations, uint tag, int *ax
* \param axis: Pointer to a design space axis structure.
* \param factor: -1 to 1 with 0 meaning "default"
*/
-static FT_Fixed blf_factor_to_coordinate(FT_Var_Axis *axis, float factor)
+static FT_Fixed blf_factor_to_coordinate(const FT_Var_Axis *axis, const float factor)
{
FT_Fixed value = axis->def;
if (factor > 0) {
@@ -734,13 +808,13 @@ static FT_Fixed blf_factor_to_coordinate(FT_Var_Axis *axis, float factor)
* \param tag: Axis tag (4-character string as uint), like 'wght'
* \param factor: -1 to 1 with 0 meaning "default"
*/
-static bool blf_glyph_set_variation_normalized(FontBLF *font,
+static bool blf_glyph_set_variation_normalized(const FontBLF *font,
FT_Fixed coords[],
- uint tag,
- float factor)
+ const uint tag,
+ const float factor)
{
int axis_index;
- FT_Var_Axis *axis = blf_var_axis_by_tag(font->variations, tag, &axis_index);
+ const FT_Var_Axis *axis = blf_var_axis_by_tag(font->variations, tag, &axis_index);
if (axis && (axis_index < BLF_VARIATIONS_MAX)) {
coords[axis_index] = blf_factor_to_coordinate(axis, factor);
return true;
@@ -758,7 +832,7 @@ static bool blf_glyph_set_variation_normalized(FontBLF *font,
static bool blf_glyph_set_variation_float(FontBLF *font, FT_Fixed coords[], uint tag, float value)
{
int axis_index;
- FT_Var_Axis *axis = blf_var_axis_by_tag(font->variations, tag, &axis_index);
+ const FT_Var_Axis *axis = blf_var_axis_by_tag(font->variations, tag, &axis_index);
if (axis && (axis_index < BLF_VARIATIONS_MAX)) {
FT_Fixed int_value = to_16dot16(value);
CLAMP(int_value, axis->minimum, axis->maximum);
@@ -783,8 +857,8 @@ static bool blf_glyph_transform_weight(FT_GlyphSlot glyph, float factor, bool mo
{
if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
/* Fake bold if the font does not have this variable axis. */
- const FT_Pos average_width = FT_MulFix(glyph->face->units_per_EM,
- glyph->face->size->metrics.x_scale);
+ const FontBLF *font = (FontBLF *)glyph->face->generic.data;
+ const FT_Pos average_width = font->ft_size->metrics.height;
FT_Pos change = (FT_Pos)((float)average_width * factor * 0.1f);
FT_Outline_EmboldenXY(&glyph->outline, change, change / 2);
if (monospaced) {
@@ -843,7 +917,8 @@ static bool blf_glyph_transform_width(FT_GlyphSlot glyph, float factor)
static bool blf_glyph_transform_spacing(FT_GlyphSlot glyph, float factor)
{
if (glyph->advance.x > 0) {
- const long int size = glyph->face->size->metrics.height;
+ const FontBLF *font = (FontBLF *)glyph->face->generic.data;
+ const long int size = font->ft_size->metrics.height;
glyph->advance.x += (FT_Pos)(factor * (float)size / 6.0f);
return true;
}
@@ -892,15 +967,11 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font,
int fixed_width)
{
if (glyph_font != settings_font) {
- FT_Set_Char_Size(glyph_font->face,
- 0,
- ((FT_F26Dot6)(settings_font->size)) * 64,
- settings_font->dpi,
- settings_font->dpi);
- glyph_font->size = settings_font->size;
- glyph_font->dpi = settings_font->dpi;
+ blf_font_size(glyph_font, settings_font->size, settings_font->dpi);
}
+ blf_ensure_size(glyph_font);
+
/* We need to keep track if changes are still needed. */
bool weight_done = false;
bool slant_done = false;
@@ -929,16 +1000,16 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font,
FT_Get_Var_Design_Coordinates(glyph_font->face, BLF_VARIATIONS_MAX, &coords[0]);
/* Update design coordinates with new values. */
weight_done = blf_glyph_set_variation_normalized(
- glyph_font, coords, blf_variation_axis_weight, weight);
+ glyph_font, coords, BLF_VARIATION_AXIS_WEIGHT, weight);
slant_done = blf_glyph_set_variation_normalized(
- glyph_font, coords, blf_variation_axis_slant, slant);
+ glyph_font, coords, BLF_VARIATION_AXIS_SLANT, slant);
width_done = blf_glyph_set_variation_normalized(
- glyph_font, coords, blf_variation_axis_width, width);
+ glyph_font, coords, BLF_VARIATION_AXIS_WIDTH, width);
spacing_done = blf_glyph_set_variation_normalized(
- glyph_font, coords, blf_variation_axis_spacing, spacing);
+ glyph_font, coords, BLF_VARIATION_AXIS_SPACING, spacing);
/* Optical size, if available, is set to current font size. */
blf_glyph_set_variation_float(
- glyph_font, coords, blf_variation_axis_optsize, settings_font->size);
+ glyph_font, coords, BLF_VARIATION_AXIS_OPTSIZE, settings_font->size);
/* Save updated design coordinates. */
FT_Set_Var_Design_Coordinates(glyph_font->face, BLF_VARIATIONS_MAX, &coords[0]);
}
@@ -955,7 +1026,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font,
/* Fallback glyph transforms, but only if required and not yet done. */
if (weight != 0.0f && !weight_done) {
- blf_glyph_transform_weight(glyph, weight, glyph->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);
+ blf_glyph_transform_weight(glyph, weight, FT_IS_FIXED_WIDTH(glyph_font));
}
if (slant != 0.0f && !slant_done) {
blf_glyph_transform_slant(glyph, slant);
@@ -973,7 +1044,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font,
return NULL;
}
-GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode)
+GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, const uint charcode)
{
GlyphBLF *g = blf_glyph_cache_find_glyph(gc, charcode);
if (g) {
@@ -984,11 +1055,9 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode)
FontBLF *font_with_glyph = font;
FT_UInt glyph_index = blf_glyph_index_from_charcode(&font_with_glyph, charcode);
- /* Glyphs are dynamically created as needed by font rendering. this means that
- * to make font rendering thread safe we have to do locking here. note that this
- * must be a lock for the whole library and not just per font, because the font
- * renderer uses a shared buffer internally. */
- BLI_spin_lock(font_with_glyph->ft_lib_mutex);
+ if (!blf_ensure_face(font_with_glyph)) {
+ return NULL;
+ }
FT_GlyphSlot glyph = blf_glyph_render(
font, font_with_glyph, glyph_index, charcode, gc->fixed_width);
@@ -998,7 +1067,6 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode)
g = blf_glyph_cache_add_glyph(font, gc, glyph, charcode, glyph_index);
}
- BLI_spin_unlock(font_with_glyph->ft_lib_mutex);
return g;
}
@@ -1047,7 +1115,7 @@ static void blf_glyph_calc_rect_shadow(
/** \name Glyph Drawing
* \{ */
-static void blf_texture_draw(const unsigned char color[4],
+static void blf_texture_draw(const uchar color[4],
const int glyph_size[2],
const int offset,
const int x1,
@@ -1073,7 +1141,7 @@ static void blf_texture_draw(const unsigned char color[4],
}
}
-static void blf_texture5_draw(const unsigned char color_in[4],
+static void blf_texture5_draw(const uchar color_in[4],
const int glyph_size[2],
const int offset,
const int x1,
@@ -1089,7 +1157,7 @@ static void blf_texture5_draw(const unsigned char color_in[4],
blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2);
}
-static void blf_texture3_draw(const unsigned char color_in[4],
+static void blf_texture3_draw(const uchar color_in[4],
const int glyph_size[2],
const int offset,
const int x1,
diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h
index 84037ff4bd0..39d3af22562 100644
--- a/source/blender/blenfont/intern/blf_internal.h
+++ b/source/blender/blenfont/intern/blf_internal.h
@@ -14,9 +14,16 @@ struct ResultBLF;
struct rctf;
struct rcti;
-/* Max number of fonts in memory. Take care that every font has a glyph cache per size/dpi,
+/* Max number of FontBLFs in memory. Take care that every font has a glyph cache per size/dpi,
* so we don't need load the same font with different size, just load one and call BLF_size. */
-#define BLF_MAX_FONT 32
+#define BLF_MAX_FONT 64
+
+/* Maximum number of opened FT_Face objects managed by cache. 0 is default of 2. */
+#define BLF_CACHE_MAX_FACES 4
+/* Maximum number of opened FT_Size objects managed by cache. 0 is default of 4 */
+#define BLF_CACHE_MAX_SIZES 8
+/* Maximum number of bytes to use for cached data nodes. 0 is default of 200,000. */
+#define BLF_CACHE_BYTES 400000
extern struct FontBLF *global_font[BLF_MAX_FONT];
@@ -39,12 +46,26 @@ void blf_font_exit(void);
bool blf_font_id_is_valid(int fontid);
+/**
+ * Return glyph id from char-code.
+ */
+uint blf_get_char_index(struct FontBLF *font, uint charcode);
+
+bool blf_ensure_face(struct FontBLF *font);
+void blf_ensure_size(struct FontBLF *font);
+
void blf_draw_buffer__start(struct FontBLF *font);
void blf_draw_buffer__end(void);
+struct FontBLF *blf_font_new_ex(const char *name,
+ const char *filepath,
+ const unsigned char *mem,
+ size_t mem_size,
+ void *ft_library);
+
struct FontBLF *blf_font_new(const char *name, const char *filepath);
-struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size);
-void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size);
+struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, size_t mem_size);
+void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, size_t mem_size);
/**
* Change font's output size. Returns true if successful in changing the size.
diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h
index 5b55f4af0b8..d64bd9c5452 100644
--- a/source/blender/blenfont/intern/blf_internal_types.h
+++ b/source/blender/blenfont/intern/blf_internal_types.h
@@ -12,16 +12,17 @@
#include FT_MULTIPLE_MASTERS_H /* Variable font support. */
-#define BLF_VARIATIONS_MAX 16 /* Maximum variation axes per font. */
+/** Maximum variation axes per font. */
+#define BLF_VARIATIONS_MAX 16
#define MAKE_DVAR_TAG(a, b, c, d) \
(((uint32_t)a << 24u) | ((uint32_t)b << 16u) | ((uint32_t)c << 8u) | ((uint32_t)d))
-#define blf_variation_axis_weight MAKE_DVAR_TAG('w', 'g', 'h', 't') /* 'wght' weight axis. */
-#define blf_variation_axis_slant MAKE_DVAR_TAG('s', 'l', 'n', 't') /* 'slnt' slant axis. */
-#define blf_variation_axis_width MAKE_DVAR_TAG('w', 'd', 't', 'h') /* 'wdth' width axis. */
-#define blf_variation_axis_spacing MAKE_DVAR_TAG('s', 'p', 'a', 'c') /* 'spac' spacing axis. */
-#define blf_variation_axis_optsize MAKE_DVAR_TAG('o', 'p', 's', 'z') /* 'opsz' optical size. */
+#define BLF_VARIATION_AXIS_WEIGHT MAKE_DVAR_TAG('w', 'g', 'h', 't') /* 'wght' weight axis. */
+#define BLF_VARIATION_AXIS_SLANT MAKE_DVAR_TAG('s', 'l', 'n', 't') /* 'slnt' slant axis. */
+#define BLF_VARIATION_AXIS_WIDTH MAKE_DVAR_TAG('w', 'd', 't', 'h') /* 'wdth' width axis. */
+#define BLF_VARIATION_AXIS_SPACING MAKE_DVAR_TAG('s', 'p', 'a', 'c') /* 'spac' spacing axis. */
+#define BLF_VARIATION_AXIS_OPTSIZE MAKE_DVAR_TAG('o', 'p', 's', 'z') /* 'opsz' optical size. */
/* -------------------------------------------------------------------- */
/** \name Sub-Pixel Offset & Utilities
@@ -38,10 +39,12 @@ typedef int32_t ft_pix;
/* Macros copied from `include/freetype/internal/ftobjs.h`. */
-/* FIXME(@campbellbarton): Follow rounding from Blender 3.1x and older.
+/**
+ * FIXME(@campbellbarton): Follow rounding from Blender 3.1x and older.
* This is what users will expect and changing this creates wider spaced text.
* Use this macro to communicate that rounding should be used, using floor is to avoid
- * user visible changes, which can be reviewed and handled separately. */
+ * user visible changes, which can be reviewed and handled separately.
+ */
#define USE_LEGACY_SPACING
#define FT_PIX_FLOOR(x) ((x) & ~63)
@@ -85,7 +88,7 @@ BLI_INLINE ft_pix ft_pix_from_float(float v)
BLI_INLINE ft_pix ft_pix_round_advance(ft_pix v, ft_pix step)
{
- /* See #USE_LEGACY_SPACING, rounding logic could change here. */
+ /** See #USE_LEGACY_SPACING, rounding logic could change here. */
return FT_PIX_DEFAULT_ROUNDING(v) + FT_PIX_DEFAULT_ROUNDING(step);
}
@@ -97,24 +100,27 @@ BLI_INLINE ft_pix ft_pix_round_advance(ft_pix v, ft_pix step)
#define BLF_BATCH_DRAW_LEN_MAX 2048 /* in glyph */
-/* Number of characters in GlyphCacheBLF.glyph_ascii_table. */
+/** Number of characters in #GlyphCacheBLF.glyph_ascii_table. */
#define GLYPH_ASCII_TABLE_SIZE 128
-/* Number of characters in KerningCacheBLF.table. */
+/** Number of characters in #KerningCacheBLF.table. */
#define KERNING_CACHE_TABLE_SIZE 128
-/* A value in the kerning cache that indicates it is not yet set. */
+/** A value in the kerning cache that indicates it is not yet set. */
#define KERNING_ENTRY_UNSET INT_MAX
typedef struct BatchBLF {
- struct FontBLF *font; /* can only batch glyph from the same font */
+ /** Can only batch glyph from the same font. */
+ struct FontBLF *font;
struct GPUBatch *batch;
struct GPUVertBuf *verts;
struct GPUVertBufRaw pos_step, col_step, offset_step, glyph_size_step;
unsigned int pos_loc, col_loc, offset_loc, glyph_size_loc;
unsigned int glyph_len;
- int ofs[2]; /* copy of font->pos */
- float mat[4][4]; /* previous call modelmatrix. */
+ /** Copy of `font->pos`. */
+ int ofs[2];
+ /* Previous call `modelmatrix`. */
+ float mat[4][4];
bool enabled, active, simple_shader;
struct GlyphCacheBLF *glyph_cache;
} BatchBLF;
@@ -133,11 +139,12 @@ typedef struct GlyphCacheBLF {
struct GlyphCacheBLF *next;
struct GlyphCacheBLF *prev;
- /* font size. */
+ /** Font size. */
float size;
- /* and DPI. */
+ /** DPI. */
unsigned int dpi;
+
float char_weight;
float char_slant;
float char_width;
@@ -146,16 +153,16 @@ typedef struct GlyphCacheBLF {
bool bold;
bool italic;
- /* Column width when printing monospaced. */
+ /** Column width when printing monospaced. */
int fixed_width;
- /* and the glyphs. */
+ /** The glyphs. */
ListBase bucket[257];
- /* fast ascii lookup */
+ /** Fast ascii lookup */
struct GlyphBLF *glyph_ascii_table[GLYPH_ASCII_TABLE_SIZE];
- /* texture array, to draw the glyphs. */
+ /** Texture array, to draw the glyphs. */
GPUTexture *texture;
char *bitmap_result;
int bitmap_len;
@@ -168,13 +175,13 @@ typedef struct GlyphBLF {
struct GlyphBLF *next;
struct GlyphBLF *prev;
- /* and the character, as UTF-32 */
+ /** The character, as UTF-32. */
unsigned int c;
- /* freetype2 index, to speed-up the search. */
+ /** Freetype2 index, to speed-up the search. */
FT_UInt idx;
- /* glyph box. */
+ /** Glyph bounding-box. */
ft_pix box_xmin;
ft_pix box_xmax;
ft_pix box_ymin;
@@ -182,19 +189,20 @@ typedef struct GlyphBLF {
ft_pix advance_x;
- /* The difference in bearings when hinting is active, zero otherwise. */
+ /** The difference in bearings when hinting is active, zero otherwise. */
ft_pix lsb_delta;
ft_pix rsb_delta;
- /* position inside the texture where this glyph is store. */
+ /** Position inside the texture where this glyph is store. */
int offset;
- /* Bitmap data, from freetype. Take care that this
+ /**
+ * Bitmap data, from freetype. Take care that this
* can be NULL.
*/
unsigned char *bitmap;
- /* Glyph width and height. */
+ /** Glyph width and height. */
int dims[2];
int pitch;
@@ -209,52 +217,57 @@ typedef struct GlyphBLF {
} GlyphBLF;
typedef struct FontBufInfoBLF {
- /* for draw to buffer, always set this to NULL after finish! */
+ /** For draw to buffer, always set this to NULL after finish! */
float *fbuf;
- /* the same but unsigned char */
+ /** The same but unsigned char. */
unsigned char *cbuf;
/** Buffer size, keep signed so comparisons with negative values work. */
int dims[2];
- /* number of channels. */
+ /** Number of channels. */
int ch;
- /* display device used for color management */
+ /** Display device used for color management. */
struct ColorManagedDisplay *display;
- /* and the color, the alphas is get from the glyph!
- * color is sRGB space */
+ /** The color, the alphas is get from the glyph! (color is sRGB space). */
float col_init[4];
- /* cached conversion from 'col_init' */
+ /** Cached conversion from 'col_init'. */
unsigned char col_char[4];
float col_float[4];
} FontBufInfoBLF;
typedef struct FontBLF {
- /* font name. */
+ /** Font name. */
char *name;
- /* # of times this font was loaded */
- unsigned int reference_count;
-
- /** File-path or NULL. */
+ /** Full path to font file or NULL if from memory. */
char *filepath;
- /* Copied from the SFNT OS/2 table. Bit flags for unicode blocks and ranges
+ /** Pointer to in-memory font, or NULL if from file. */
+ void *mem;
+ size_t mem_size;
+
+ /**
+ * Copied from the SFNT OS/2 table. Bit flags for unicode blocks and ranges
* considered "functional". Cached here because face might not always exist.
- * See: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur */
- uint UnicodeRanges[4];
+ * See: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur
+ */
+ uint unicode_ranges[4];
+
+ /** Number of times this font was loaded. */
+ unsigned int reference_count;
- /* aspect ratio or scale. */
+ /** Aspect ratio or scale. */
float aspect[3];
- /* initial position for draw the text. */
+ /** Initial position for draw the text. */
int pos[3];
- /* angle in radians. */
+ /** Angle in radians. */
float angle;
#if 0 /* BLF_BLUR_ENABLE */
@@ -262,49 +275,50 @@ typedef struct FontBLF {
int blur;
#endif
- /* shadow level. */
+ /** Shadow level. */
int shadow;
- /* and shadow offset. */
+ /** And shadow offset. */
int shadow_x;
int shadow_y;
- /* shadow color. */
+ /** Shadow color. */
unsigned char shadow_color[4];
- /* main text color. */
+ /** Main text color. */
unsigned char color[4];
- /* Multiplied this matrix with the current one before
- * draw the text! see blf_draw__start.
+ /**
+ * Multiplied this matrix with the current one before draw the text!
+ * see #blf_draw_gl__start.
*/
float m[16];
- /* clipping rectangle. */
+ /** Clipping rectangle. */
rcti clip_rec;
- /* the width to wrap the text, see BLF_WORD_WRAP */
+ /** The width to wrap the text, see #BLF_WORD_WRAP. */
int wrap_width;
- /* Font DPI (default 72). */
+ /** Font DPI (default 72). */
unsigned int dpi;
- /* font size. */
+ /** Font size. */
float size;
- /* Axes data for Adobe MM, TrueType GX, or OpenType variation fonts. */
+ /** Axes data for Adobe MM, TrueType GX, or OpenType variation fonts. */
FT_MM_Var *variations;
- /* Character variation; 0=default, -1=min, +1=max. */
+ /** Character variation; 0=default, -1=min, +1=max. */
float char_weight;
float char_slant;
float char_width;
float char_spacing;
- /* max texture size. */
+ /** Max texture size. */
int tex_size_max;
- /* font options. */
+ /** Font options. */
int flags;
/**
@@ -313,29 +327,32 @@ typedef struct FontBLF {
*/
ListBase cache;
- /* Cache of unscaled kerning values. Will be NULL if font does not have kerning. */
+ /** Cache of unscaled kerning values. Will be NULL if font does not have kerning. */
KerningCacheBLF *kerning_cache;
- /* freetype2 lib handle. */
+ /** Freetype2 lib handle. */
FT_Library ft_lib;
- /* Mutex lock for library */
- SpinLock *ft_lib_mutex;
-
- /* freetype2 face. */
+ /** Freetype2 face. */
FT_Face face;
- /* data for buffer usage (drawing into a texture buffer) */
+ /** Point to face->size or to cache's size. */
+ FT_Size ft_size;
+
+ /** Copy of the font->face->face_flags, in case we don't have a face loaded. */
+ FT_Long face_flags;
+
+ /** Data for buffer usage (drawing into a texture buffer) */
FontBufInfoBLF buf_info;
- /* Mutex lock for glyph cache. */
- SpinLock *glyph_cache_mutex;
+ /** Mutex lock for glyph cache. */
+ ThreadMutex glyph_cache_mutex;
} FontBLF;
typedef struct DirBLF {
struct DirBLF *next;
struct DirBLF *prev;
- /* full path where search fonts. */
+ /** Full path where search fonts. */
char *path;
} DirBLF;
diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c
index 9460e9413d1..1670674ebba 100644
--- a/source/blender/blenfont/intern/blf_thumbs.c
+++ b/source/blender/blenfont/intern/blf_thumbs.c
@@ -32,26 +32,34 @@
void BLF_thumb_preview(const char *filepath,
const char **draw_str,
const char **i18n_draw_str,
- const unsigned char draw_str_lines,
+ const uchar draw_str_lines,
const float font_color[4],
const int font_size,
- unsigned char *buf,
- int w,
- int h,
- int channels)
+ uchar *buf,
+ const int w,
+ const int h,
+ const int channels)
{
- const unsigned int dpi = 72;
+ const uint dpi = 72;
const int font_size_min = 6;
int font_size_curr;
/* shrink 1/th each line */
int font_shrink = 4;
- FontBLF *font;
+ /* While viewing thumbnails in font directories this function can be called simultaneously from a
+ * greater number of threads than we want the FreeType cache to keep open at a time. Therefore
+ * pass own FT_Library to font creation so that it is not managed by the FreeType cache system.
+ */
- /* Create a new blender font obj and fill it with default values */
- font = blf_font_new("thumb_font", filepath);
+ FT_Library ft_library = NULL;
+ if (FT_Init_FreeType(&ft_library) != FT_Err_Ok) {
+ return;
+ }
+
+ FontBLF *font = blf_font_new_ex("thumb_font", filepath, NULL, 0, ft_library);
if (!font) {
printf("Info: Can't load font '%s', no preview possible\n", filepath);
+ FT_Done_FreeType(ft_library);
return;
}
@@ -86,7 +94,7 @@ void BLF_thumb_preview(const char *filepath,
font->pos[1] -= (int)((float)blf_font_ascender(font) * 1.1f);
- /* We fallback to default english strings in case not enough chars are available in current
+ /* We fallback to default English strings in case not enough chars are available in current
* font for given translated string (useful in non-Latin i18n context, like Chinese,
* since many fonts will then show nothing but ugly 'missing char' in their preview).
* Does not handle all cases, but much better than nothing.
@@ -102,4 +110,5 @@ void BLF_thumb_preview(const char *filepath,
blf_draw_buffer__end();
blf_font_free(font);
+ FT_Done_FreeType(ft_library);
}
diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h
index 4274ca97fd1..da1e45ababd 100644
--- a/source/blender/blenkernel/BKE_DerivedMesh.h
+++ b/source/blender/blenkernel/BKE_DerivedMesh.h
@@ -141,14 +141,6 @@ struct DerivedMesh {
void (*copyLoopArray)(DerivedMesh *dm, struct MLoop *r_loop);
void (*copyPolyArray)(DerivedMesh *dm, struct MPoly *r_poly);
- /** Return a copy of all verts/edges/faces from the derived mesh
- * it is the caller's responsibility to free the returned pointer
- */
- struct MVert *(*dupVertArray)(DerivedMesh *dm);
- struct MEdge *(*dupEdgeArray)(DerivedMesh *dm);
- struct MLoop *(*dupLoopArray)(DerivedMesh *dm);
- struct MPoly *(*dupPolyArray)(DerivedMesh *dm);
-
/** Return a pointer to the entire array of vert/edge/face custom data
* from the derived mesh (this gives a pointer to the actual data, not
* a copy)
@@ -254,11 +246,6 @@ void DM_copy_vert_data(struct DerivedMesh *source,
int count);
/**
- * Sets up mpolys for a DM based on face iterators in source.
- */
-void DM_DupPolys(DerivedMesh *source, DerivedMesh *target);
-
-/**
* Ensure the array is large enough.
*
* \note This function must always be thread-protected by caller.
diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h
index dcacc2ca7b3..0f00ab9c321 100644
--- a/source/blender/blenkernel/BKE_appdir.h
+++ b/source/blender/blenkernel/BKE_appdir.h
@@ -56,7 +56,7 @@ const char *BKE_appdir_folder_home(void);
*
* \returns True if the path is valid and points to an existing directory.
*/
-bool BKE_appdir_folder_documents(char *dir);
+bool BKE_appdir_folder_documents(char *dir) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Get the user's cache directory, i.e.
* - Linux: `$HOME/.cache/blender/`
@@ -66,7 +66,7 @@ bool BKE_appdir_folder_documents(char *dir);
* \returns True if the path is valid. It doesn't create or checks format
* if the `blender` folder exists. It does check if the parent of the path exists.
*/
-bool BKE_appdir_folder_caches(char *r_path, size_t path_len);
+bool BKE_appdir_folder_caches(char *r_path, size_t path_len) ATTR_NONNULL(1);
/**
* Get a folder out of the \a folder_id presets for paths.
*
@@ -75,15 +75,17 @@ bool BKE_appdir_folder_caches(char *r_path, size_t path_len);
* \return The path if found, NULL string if not.
*/
bool BKE_appdir_folder_id_ex(int folder_id, const char *subfolder, char *path, size_t path_len);
-const char *BKE_appdir_folder_id(int folder_id, const char *subfolder);
+const char *BKE_appdir_folder_id(int folder_id, const char *subfolder) ATTR_WARN_UNUSED_RESULT;
/**
* Returns the path to a folder in the user area, creating it if it doesn't exist.
*/
-const char *BKE_appdir_folder_id_create(int folder_id, const char *subfolder);
+const char *BKE_appdir_folder_id_create(int folder_id,
+ const char *subfolder) ATTR_WARN_UNUSED_RESULT;
/**
* Returns the path to a folder in the user area without checking that it actually exists first.
*/
-const char *BKE_appdir_folder_id_user_notest(int folder_id, const char *subfolder);
+const char *BKE_appdir_folder_id_user_notest(int folder_id,
+ const char *subfolder) ATTR_WARN_UNUSED_RESULT;
/**
* Returns the path of the top-level version-specific local, user or system directory.
* If check_is_dir, then the result will be NULL if the directory doesn't exist.
@@ -99,23 +101,24 @@ bool BKE_appdir_app_is_portable_install(void);
* Return true if templates exist
*/
bool BKE_appdir_app_template_any(void);
-bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len);
-bool BKE_appdir_app_template_has_userpref(const char *app_template);
-void BKE_appdir_app_templates(struct ListBase *templates);
+bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len)
+ ATTR_NONNULL(1);
+bool BKE_appdir_app_template_has_userpref(const char *app_template) ATTR_NONNULL(1);
+void BKE_appdir_app_templates(struct ListBase *templates) ATTR_NONNULL(1);
/**
* Initialize path to program executable.
*/
-void BKE_appdir_program_path_init(const char *argv0);
+void BKE_appdir_program_path_init(const char *argv0) ATTR_NONNULL(1);
/**
* Path to executable
*/
-const char *BKE_appdir_program_path(void);
+const char *BKE_appdir_program_path(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Path to directory of executable
*/
-const char *BKE_appdir_program_dir(void);
+const char *BKE_appdir_program_dir(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Gets a good default directory for fonts.
@@ -128,7 +131,7 @@ bool BKE_appdir_font_folder_default(char *dir);
bool BKE_appdir_program_python_search(char *fullpath,
size_t fullpath_len,
int version_major,
- int version_minor);
+ int version_minor) ATTR_NONNULL(1);
/**
* Initialize path to temporary directory.
@@ -138,11 +141,11 @@ void BKE_tempdir_init(const char *userdir);
/**
* Path to persistent temporary directory (with trailing slash)
*/
-const char *BKE_tempdir_base(void);
+const char *BKE_tempdir_base(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Path to temporary directory (with trailing slash)
*/
-const char *BKE_tempdir_session(void);
+const char *BKE_tempdir_session(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL;
/**
* Delete content of this instance's temp dir.
*/
diff --git a/source/blender/blenkernel/BKE_attribute.hh b/source/blender/blenkernel/BKE_attribute.hh
index b92d0d4326b..7b13b8a2b09 100644
--- a/source/blender/blenkernel/BKE_attribute.hh
+++ b/source/blender/blenkernel/BKE_attribute.hh
@@ -2,6 +2,8 @@
#pragma once
+#include <optional>
+
#include "BLI_color.hh"
#include "BLI_function_ref.hh"
#include "BLI_generic_span.hh"
@@ -14,6 +16,10 @@
struct Mesh;
struct PointCloud;
+namespace blender::fn {
+class MultiFunction;
+class GField;
+} // namespace blender::fn
namespace blender::bke {
@@ -71,8 +77,13 @@ struct AttributeKind {
*/
struct AttributeInit {
enum class Type {
- Default,
+ /** #AttributeInitConstruct. */
+ Construct,
+ /** #AttributeInitDefaultValue. */
+ DefaultValue,
+ /** #AttributeInitVArray. */
VArray,
+ /** #AttributeInitMoveArray. */
MoveArray,
};
Type type;
@@ -82,11 +93,20 @@ struct AttributeInit {
};
/**
- * Create an attribute using the default value for the data type.
- * The default values may depend on the attribute provider implementation.
+ * Default construct new attribute values. Does nothing for trivial types. This should be used
+ * if all attribute element values will be set by the caller after creating the attribute.
+ */
+struct AttributeInitConstruct : public AttributeInit {
+ AttributeInitConstruct() : AttributeInit(Type::Construct)
+ {
+ }
+};
+
+/**
+ * Create an attribute using the default value for the data type (almost always "zero").
*/
-struct AttributeInitDefault : public AttributeInit {
- AttributeInitDefault() : AttributeInit(Type::Default)
+struct AttributeInitDefaultValue : public AttributeInit {
+ AttributeInitDefaultValue() : AttributeInit(Type::DefaultValue)
{
}
};
@@ -94,14 +114,11 @@ struct AttributeInitDefault : public AttributeInit {
/**
* 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 {
- blender::GVArray varray;
+ GVArray varray;
- AttributeInitVArray(blender::GVArray varray)
- : AttributeInit(Type::VArray), varray(std::move(varray))
+ AttributeInitVArray(GVArray varray) : AttributeInit(Type::VArray), varray(std::move(varray))
{
}
};
@@ -117,10 +134,10 @@ struct AttributeInitVArray : public AttributeInit {
* 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 {
+struct AttributeInitMoveArray : public AttributeInit {
void *data = nullptr;
- AttributeInitMove(void *data) : AttributeInit(Type::MoveArray), data(data)
+ AttributeInitMoveArray(void *data) : AttributeInit(Type::MoveArray), data(data)
{
}
};
@@ -150,7 +167,28 @@ template<typename T> struct AttributeReader {
};
/**
- * Result when looking up an attribute from some geometry with read an write access. After writing
+ * A utility to make sure attribute values are valid, for attributes like "material_index" which
+ * can only be positive, or attributes that represent enum options. This is usually only necessary
+ * when writing attributes from an untrusted/arbitrary user input.
+ */
+struct AttributeValidator {
+ /**
+ * Single input, single output function that corrects attribute values if necessary.
+ */
+ const fn::MultiFunction *function;
+
+ operator bool() const
+ {
+ return this->function != nullptr;
+ }
+ /**
+ * Return a field that creates corrected attribute values.
+ */
+ fn::GField validate_field_if_necessary(const fn::GField &field) const;
+};
+
+/**
+ * Result when looking up an attribute from some geometry with read and write access. After writing
* to the attribute, the #finish method has to be called. This may invalidate caches based on this
* attribute.
*/
@@ -226,7 +264,9 @@ template<typename T> struct SpanAttributeWriter {
*/
void finish()
{
- this->span.save();
+ if (this->span.varray()) {
+ this->span.save();
+ }
if (this->tag_modified_fn) {
this->tag_modified_fn();
}
@@ -301,7 +341,9 @@ struct GSpanAttributeWriter {
void finish()
{
- this->span.save();
+ if (this->span.varray()) {
+ this->span.save();
+ }
if (this->tag_modified_fn) {
this->tag_modified_fn();
}
@@ -330,7 +372,7 @@ struct AttributeAccessorFunctions {
eAttrDomain to_domain);
bool (*for_all)(const void *owner,
FunctionRef<bool(const AttributeIDRef &, const AttributeMetaData &)> fn);
-
+ AttributeValidator (*lookup_validator)(const void *owner, const AttributeIDRef &attribute_id);
GAttributeWriter (*lookup_for_write)(void *owner, const AttributeIDRef &attribute_id);
bool (*remove)(void *owner, const AttributeIDRef &attribute_id);
bool (*add)(void *owner,
@@ -351,8 +393,9 @@ class AttributeAccessor {
/**
* The data that actually owns the attributes, for example, a pointer to a #Mesh or #PointCloud
* Most commonly this is a pointer to a #Mesh or #PointCloud.
- * Under some circumstances this can be null. In that case most methods can't be used. Just e.g.
- * the #domain_size method works and returns 0 for every domain.
+ * Under some circumstances this can be null. In that case most methods can't be used. Allowed
+ * methods are #domain_size, #for_all and #is_builtin. We could potentially make these methods
+ * accessible without #AttributeAccessor and then #owner_ could always be non-null.
*
* \note This class cannot modify the owner's attributes, but the pointer is still non-const, so
* this class can be a base class for the mutable version.
@@ -484,6 +527,14 @@ class AttributeAccessor {
}
/**
+ * Same as the generic version above, but should be used when the type is known at compile time.
+ */
+ AttributeValidator lookup_validator(const AttributeIDRef &attribute_id) const
+ {
+ return fn_->lookup_validator(owner_, attribute_id);
+ }
+
+ /**
* Interpolate data from one domain to another.
*/
GVArray adapt_domain(const GVArray &varray,
@@ -509,7 +560,10 @@ class AttributeAccessor {
*/
bool for_all(const AttributeForeachCallback fn) const
{
- return fn_->for_all(owner_, fn);
+ if (owner_ != nullptr) {
+ return fn_->for_all(owner_, fn);
+ }
+ return true;
}
/**
@@ -533,10 +587,12 @@ class MutableAttributeAccessor : public AttributeAccessor {
* Get a writable attribute or none if it does not exist.
* Make sure to call #finish after changes are done.
*/
- GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id)
- {
- return fn_->lookup_for_write(owner_, attribute_id);
- }
+ GAttributeWriter lookup_for_write(const AttributeIDRef &attribute_id);
+
+ /**
+ * Same as above, but returns a type that makes it easier to work with the attribute as a span.
+ */
+ GSpanAttributeWriter lookup_for_write_span(const AttributeIDRef &attribute_id);
/**
* Get a writable attribute or non if it does not exist.
@@ -555,6 +611,19 @@ class MutableAttributeAccessor : public AttributeAccessor {
}
/**
+ * Same as above, but returns a type that makes it easier to work with the attribute as a span.
+ */
+ template<typename T>
+ SpanAttributeWriter<T> lookup_for_write_span(const AttributeIDRef &attribute_id)
+ {
+ AttributeWriter<T> attribute = this->lookup_for_write<T>(attribute_id);
+ if (attribute) {
+ return SpanAttributeWriter<T>{std::move(attribute), true};
+ }
+ return {};
+ }
+
+ /**
* Create a new attribute.
* \return True, when a new attribute has been created. False, when it's not possible to create
* this attribute or there is already an attribute with that id.
@@ -576,7 +645,7 @@ class MutableAttributeAccessor : public AttributeAccessor {
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const eCustomDataType data_type,
- const AttributeInit &initializer = AttributeInitDefault());
+ const AttributeInit &initializer = AttributeInitDefaultValue());
/**
* Same as above, but returns a type that makes it easier to work with the attribute as a span.
@@ -587,7 +656,7 @@ class MutableAttributeAccessor : public AttributeAccessor {
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
const eCustomDataType data_type,
- const AttributeInit &initializer = AttributeInitDefault());
+ const AttributeInit &initializer = AttributeInitDefaultValue());
/**
* Same as above, but should be used when the type is known at compile time.
@@ -596,7 +665,7 @@ class MutableAttributeAccessor : public AttributeAccessor {
AttributeWriter<T> lookup_or_add_for_write(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
- const AttributeInit &initializer = AttributeInitDefault())
+ const AttributeInit &initializer = AttributeInitDefaultValue())
{
const CPPType &cpp_type = CPPType::get<T>();
const eCustomDataType data_type = cpp_type_to_custom_data_type(cpp_type);
@@ -610,7 +679,7 @@ class MutableAttributeAccessor : public AttributeAccessor {
SpanAttributeWriter<T> lookup_or_add_for_write_span(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
- const AttributeInit &initializer = AttributeInitDefault())
+ const AttributeInit &initializer = AttributeInitDefaultValue())
{
AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(
attribute_id, domain, initializer);
@@ -627,6 +696,8 @@ class MutableAttributeAccessor : public AttributeAccessor {
* The "only" in the name indicates that the caller should not read existing values from the
* span. If the attribute is not stored as span internally, the existing values won't be copied
* over to the span.
+ *
+ * For trivial types, the values in a newly created attribute will not be initialized.
*/
GSpanAttributeWriter lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id,
const eAttrDomain domain,
@@ -639,7 +710,9 @@ class MutableAttributeAccessor : public AttributeAccessor {
SpanAttributeWriter<T> lookup_or_add_for_write_only_span(const AttributeIDRef &attribute_id,
const eAttrDomain domain)
{
- AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(attribute_id, domain);
+ AttributeWriter<T> attribute = this->lookup_or_add_for_write<T>(
+ attribute_id, domain, AttributeInitConstruct());
+
if (attribute) {
return SpanAttributeWriter<T>{std::move(attribute), false};
}
@@ -662,6 +735,35 @@ class MutableAttributeAccessor : public AttributeAccessor {
void remove_anonymous();
};
+struct AttributeTransferData {
+ /* Expect that if an attribute exists, it is stored as a contiguous array internally anyway. */
+ GVArraySpan src;
+ AttributeMetaData meta_data;
+ bke::GSpanAttributeWriter dst;
+};
+/**
+ * Retrieve attribute arrays and writers for attributes that should be transferred between
+ * data-blocks of the same type.
+ */
+Vector<AttributeTransferData> retrieve_attributes_for_transfer(
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
+ eAttrDomainMask domain_mask,
+ const Set<std::string> &skip = {});
+
+/**
+ * Copy attributes for the domain based on the elementwise mask.
+ *
+ * \param mask_indices: Indexed elements to copy from the source data-block.
+ * \param domain: Attribute domain to transfer.
+ * \param skip: Named attributes to ignore/skip.
+ */
+void copy_attribute_domain(AttributeAccessor src_attributes,
+ MutableAttributeAccessor dst_attributes,
+ IndexMask selection,
+ eAttrDomain domain,
+ const Set<std::string> &skip = {});
+
bool allow_procedural_attribute_access(StringRef attribute_name);
extern const char *no_procedural_access_message;
@@ -722,20 +824,9 @@ class CustomDataAttributes {
bool create_by_move(const AttributeIDRef &attribute_id, eCustomDataType data_type, void *buffer);
bool remove(const AttributeIDRef &attribute_id);
- /**
- * Change the order of the attributes to match the order of IDs in the argument.
- */
- void reorder(Span<AttributeIDRef> new_order);
-
bool foreach_attribute(const AttributeForeachCallback callback, eAttrDomain domain) const;
};
-AttributeAccessor mesh_attributes(const Mesh &mesh);
-MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh);
-
-AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud);
-MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud);
-
/* -------------------------------------------------------------------- */
/** \name #AttributeIDRef Inline Methods
* \{ */
diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh
index 01c2ef988f2..abd8b33b260 100644
--- a/source/blender/blenkernel/BKE_attribute_math.hh
+++ b/source/blender/blenkernel/BKE_attribute_math.hh
@@ -46,6 +46,58 @@ inline void convert_to_static_type(const eCustomDataType data_type, const Func &
}
/* -------------------------------------------------------------------- */
+/** \name Mix two values of the same type.
+ *
+ * This is just basic linear interpolation.
+ * \{ */
+
+template<typename T> T mix2(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 int8_t mix2(const float factor, const int8_t &a, const int8_t &b)
+{
+ return static_cast<int8_t>(std::round((1.0f - factor) * a + factor * b));
+}
+
+template<> inline int mix2(const float factor, const int &a, const int &b)
+{
+ return static_cast<int>(std::round((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 math::interpolate(a, b, factor);
+}
+
+template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
+{
+ return math::interpolate(a, b, factor);
+}
+
+template<>
+inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
+{
+ return math::interpolate(a, b, factor);
+}
+
+template<>
+inline ColorGeometry4b mix2(const float factor, const ColorGeometry4b &a, const ColorGeometry4b &b)
+{
+ return math::interpolate(a, b, factor);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Mix three values of the same type.
*
* This is typically used to interpolate values within a triangle.
@@ -117,53 +169,85 @@ inline ColorGeometry4b mix3(const float3 &weights,
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Mix two values of the same type.
+/** \name Mix four values of the same type.
*
- * This is just basic linear interpolation.
* \{ */
-template<typename T> T mix2(float factor, const T &a, const T &b);
+template<typename T>
+T mix4(const float4 &weights, const T &v0, const T &v1, const T &v2, const T &v3);
-template<> inline bool mix2(const float factor, const bool &a, const bool &b)
+template<>
+inline int8_t mix4(
+ const float4 &weights, const int8_t &v0, const int8_t &v1, const int8_t &v2, const int8_t &v3)
{
- return ((1.0f - factor) * a + factor * b) >= 0.5f;
+ return static_cast<int8_t>(
+ std::round(weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3));
}
-template<> inline int8_t mix2(const float factor, const int8_t &a, const int8_t &b)
+template<>
+inline bool mix4(
+ const float4 &weights, const bool &v0, const bool &v1, const bool &v2, const bool &v3)
{
- return static_cast<int8_t>(std::round((1.0f - factor) * a + factor * b));
+ return (weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3) >= 0.5f;
}
-template<> inline int mix2(const float factor, const int &a, const int &b)
+template<>
+inline int mix4(const float4 &weights, const int &v0, const int &v1, const int &v2, const int &v3)
{
- return static_cast<int>(std::round((1.0f - factor) * a + factor * b));
+ return static_cast<int>(
+ std::round(weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3));
}
-template<> inline float mix2(const float factor, const float &a, const float &b)
+template<>
+inline float mix4(
+ const float4 &weights, const float &v0, const float &v1, const float &v2, const float &v3)
{
- return (1.0f - factor) * a + factor * b;
+ return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
}
-template<> inline float2 mix2(const float factor, const float2 &a, const float2 &b)
+template<>
+inline float2 mix4(
+ const float4 &weights, const float2 &v0, const float2 &v1, const float2 &v2, const float2 &v3)
{
- return math::interpolate(a, b, factor);
+ return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
}
-template<> inline float3 mix2(const float factor, const float3 &a, const float3 &b)
+template<>
+inline float3 mix4(
+ const float4 &weights, const float3 &v0, const float3 &v1, const float3 &v2, const float3 &v3)
{
- return math::interpolate(a, b, factor);
+ return weights.x * v0 + weights.y * v1 + weights.z * v2 + weights.w * v3;
}
template<>
-inline ColorGeometry4f mix2(const float factor, const ColorGeometry4f &a, const ColorGeometry4f &b)
+inline ColorGeometry4f mix4(const float4 &weights,
+ const ColorGeometry4f &v0,
+ const ColorGeometry4f &v1,
+ const ColorGeometry4f &v2,
+ const ColorGeometry4f &v3)
{
- return math::interpolate(a, b, factor);
+ ColorGeometry4f result;
+ interp_v4_v4v4v4v4(result, v0, v1, v2, v3, weights);
+ return result;
}
template<>
-inline ColorGeometry4b mix2(const float factor, const ColorGeometry4b &a, const ColorGeometry4b &b)
+inline ColorGeometry4b mix4(const float4 &weights,
+ const ColorGeometry4b &v0,
+ const ColorGeometry4b &v1,
+ const ColorGeometry4b &v2,
+ const ColorGeometry4b &v3)
{
- return math::interpolate(a, b, factor);
+ const float4 v0_f{&v0.r};
+ const float4 v1_f{&v1.r};
+ const float4 v2_f{&v2.r};
+ const float4 v3_f{&v3.r};
+ float4 mixed;
+ interp_v4_v4v4v4v4(mixed, v0_f, v1_f, v2_f, v3_f, weights);
+ return ColorGeometry4b{static_cast<uint8_t>(mixed[0]),
+ static_cast<uint8_t>(mixed[1]),
+ static_cast<uint8_t>(mixed[2]),
+ static_cast<uint8_t>(mixed[3])};
}
/** \} */
@@ -188,10 +272,28 @@ template<typename T> class SimpleMixer {
* \param default_value: Output value for an element that has not been affected by a #mix_in.
*/
SimpleMixer(MutableSpan<T> buffer, T default_value = {})
+ : SimpleMixer(buffer, buffer.index_range(), default_value)
+ {
+ }
+
+ /**
+ * \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
+ */
+ SimpleMixer(MutableSpan<T> buffer, const IndexMask mask, T default_value = {})
: buffer_(buffer), default_value_(default_value), total_weights_(buffer.size(), 0.0f)
{
BLI_STATIC_ASSERT(std::is_trivial_v<T>, "");
- memset(buffer_.data(), 0, sizeof(T) * buffer_.size());
+ mask.foreach_index([&](const int64_t i) { buffer_[i] = default_value_; });
+ }
+
+ /**
+ * Set a #value into the element with the given #index.
+ */
+ void set(const int64_t index, const T &value, const float weight = 1.0f)
+ {
+ BLI_assert(weight >= 0.0f);
+ buffer_[index] = value * weight;
+ total_weights_[index] = weight;
}
/**
@@ -209,7 +311,12 @@ template<typename T> class SimpleMixer {
*/
void finalize()
{
- for (const int64_t i : buffer_.index_range()) {
+ this->finalize(IndexMask(buffer_.size()));
+ }
+
+ void finalize(const IndexMask mask)
+ {
+ mask.foreach_index([&](const int64_t i) {
const float weight = total_weights_[i];
if (weight > 0.0f) {
buffer_[i] *= 1.0f / weight;
@@ -217,7 +324,7 @@ template<typename T> class SimpleMixer {
else {
buffer_[i] = default_value_;
}
- }
+ });
}
};
@@ -237,9 +344,25 @@ class BooleanPropagationMixer {
/**
* \param buffer: Span where the interpolated values should be stored.
*/
- BooleanPropagationMixer(MutableSpan<bool> buffer) : buffer_(buffer)
+ BooleanPropagationMixer(MutableSpan<bool> buffer)
+ : BooleanPropagationMixer(buffer, buffer.index_range())
{
- buffer_.fill(false);
+ }
+
+ /**
+ * \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
+ */
+ BooleanPropagationMixer(MutableSpan<bool> buffer, const IndexMask mask) : buffer_(buffer)
+ {
+ mask.foreach_index([&](const int64_t i) { buffer_[i] = false; });
+ }
+
+ /**
+ * Set a #value into the element with the given #index.
+ */
+ void set(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f)
+ {
+ buffer_[index] = value;
}
/**
@@ -256,6 +379,10 @@ class BooleanPropagationMixer {
void finalize()
{
}
+
+ void finalize(const IndexMask /*mask*/)
+ {
+ }
};
/**
@@ -277,8 +404,27 @@ class SimpleMixerWithAccumulationType {
public:
SimpleMixerWithAccumulationType(MutableSpan<T> buffer, T default_value = {})
+ : SimpleMixerWithAccumulationType(buffer, buffer.index_range(), default_value)
+ {
+ }
+
+ /**
+ * \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
+ */
+ SimpleMixerWithAccumulationType(MutableSpan<T> buffer,
+ const IndexMask mask,
+ T default_value = {})
: buffer_(buffer), default_value_(default_value), accumulation_buffer_(buffer.size())
{
+ mask.foreach_index([&](const int64_t index) { buffer_[index] = default_value_; });
+ }
+
+ void set(const int64_t index, const T &value, const float weight = 1.0f)
+ {
+ const AccumulationT converted_value = static_cast<AccumulationT>(value);
+ Item &item = accumulation_buffer_[index];
+ item.value = converted_value * weight;
+ item.weight = weight;
}
void mix_in(const int64_t index, const T &value, const float weight = 1.0f)
@@ -291,7 +437,12 @@ class SimpleMixerWithAccumulationType {
void finalize()
{
- for (const int64_t i : buffer_.index_range()) {
+ this->finalize(buffer_.index_range());
+ }
+
+ void finalize(const IndexMask mask)
+ {
+ mask.foreach_index([&](const int64_t i) {
const Item &item = accumulation_buffer_[i];
if (item.weight > 0.0f) {
const float weight_inv = 1.0f / item.weight;
@@ -301,7 +452,7 @@ class SimpleMixerWithAccumulationType {
else {
buffer_[i] = default_value_;
}
- }
+ });
}
};
@@ -314,8 +465,16 @@ class ColorGeometry4fMixer {
public:
ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ /**
+ * \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
+ */
+ ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
+ IndexMask mask,
+ ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f));
+ void set(int64_t index, const ColorGeometry4f &color, float weight = 1.0f);
void mix_in(int64_t index, const ColorGeometry4f &color, float weight = 1.0f);
void finalize();
+ void finalize(IndexMask mask);
};
class ColorGeometry4bMixer {
@@ -328,8 +487,16 @@ class ColorGeometry4bMixer {
public:
ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer,
ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255));
+ /**
+ * \param mask: Only initialize these indices. Other indices in the buffer will be invalid.
+ */
+ ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer,
+ IndexMask mask,
+ ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255));
+ void set(int64_t index, const ColorGeometry4b &color, float weight = 1.0f);
void mix_in(int64_t index, const ColorGeometry4b &color, float weight = 1.0f);
void finalize();
+ void finalize(IndexMask mask);
};
template<typename T> struct DefaultMixerStruct {
@@ -381,12 +548,12 @@ template<> struct DefaultMixerStruct<int8_t> {
using type = SimpleMixerWithAccumulationType<int8_t, float, float_to_int8_t>;
};
-template<typename T> struct DefaultPropatationMixerStruct {
+template<typename T> struct DefaultPropagationMixerStruct {
/* Use void by default. This can be checked for in `if constexpr` statements. */
using type = typename DefaultMixerStruct<T>::type;
};
-template<> struct DefaultPropatationMixerStruct<bool> {
+template<> struct DefaultPropagationMixerStruct<bool> {
using type = BooleanPropagationMixer;
};
@@ -396,7 +563,7 @@ template<> struct DefaultPropatationMixerStruct<bool> {
* (the default mixing for booleans).
*/
template<typename T>
-using DefaultPropatationMixer = typename DefaultPropatationMixerStruct<T>::type;
+using DefaultPropagationMixer = typename DefaultPropagationMixerStruct<T>::type;
/* Utility to get a good default mixer for a given type. This is `void` when there is no default
* mixer for the given type. */
diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h
index efe44ec657b..ee9c7a964d9 100644
--- a/source/blender/blenkernel/BKE_blender_version.h
+++ b/source/blender/blenkernel/BKE_blender_version.h
@@ -17,7 +17,7 @@ extern "C" {
*/
/* Blender major and minor version. */
-#define BLENDER_VERSION 303
+#define BLENDER_VERSION 304
/* Blender patch version for bugfix releases. */
#define BLENDER_VERSION_PATCH 0
/** Blender release cycle stage: alpha/beta/rc/release. */
@@ -25,7 +25,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
-#define BLENDER_FILE_SUBVERSION 5
+#define BLENDER_FILE_SUBVERSION 0
/* 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_bpath.h b/source/blender/blenkernel/BKE_bpath.h
index ea6049e87da..bc60b6f050e 100644
--- a/source/blender/blenkernel/BKE_bpath.h
+++ b/source/blender/blenkernel/BKE_bpath.h
@@ -12,6 +12,8 @@
#pragma once
+#include "BLI_utildefines.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -57,6 +59,7 @@ typedef enum eBPathForeachFlag {
* \note Only used by Image IDType currently. */
BKE_BPATH_FOREACH_PATH_RELOAD_EDITED = (1 << 9),
} eBPathForeachFlag;
+ENUM_OPERATORS(eBPathForeachFlag, BKE_BPATH_FOREACH_PATH_RELOAD_EDITED)
struct BPathForeachPathData;
diff --git a/source/blender/blenkernel/BKE_cachefile.h b/source/blender/blenkernel/BKE_cachefile.h
index fd559abf7f2..43ad340103a 100644
--- a/source/blender/blenkernel/BKE_cachefile.h
+++ b/source/blender/blenkernel/BKE_cachefile.h
@@ -33,7 +33,7 @@ void BKE_cachefile_eval(struct Main *bmain,
bool BKE_cachefile_filepath_get(const struct Main *bmain,
const struct Depsgraph *depsgrah,
const struct CacheFile *cache_file,
- char r_filename[1024]);
+ char r_filepath[1024]);
double BKE_cachefile_time_offset(const struct CacheFile *cache_file, double time, double fps);
@@ -53,10 +53,12 @@ void BKE_cachefile_reader_free(struct CacheFile *cache_file, struct CacheReader
bool BKE_cache_file_uses_render_procedural(const struct CacheFile *cache_file,
struct Scene *scene);
-/* Add a layer to the cache_file. Return NULL if the filename is already that of an existing layer
- * or if the number of layers exceeds the maximum allowed layer count. */
+/**
+ * Add a layer to the cache_file. Return NULL if the `filepath` is already that of an existing
+ * layer or if the number of layers exceeds the maximum allowed layer count.
+ */
struct CacheFileLayer *BKE_cachefile_add_layer(struct CacheFile *cache_file,
- const char filename[1024]);
+ const char filepath[1024]);
struct CacheFileLayer *BKE_cachefile_get_active_layer(struct CacheFile *cache_file);
diff --git a/source/blender/blenkernel/BKE_cdderivedmesh.h b/source/blender/blenkernel/BKE_cdderivedmesh.h
index 3c929857c14..2d1aca7c3c8 100644
--- a/source/blender/blenkernel/BKE_cdderivedmesh.h
+++ b/source/blender/blenkernel/BKE_cdderivedmesh.h
@@ -25,10 +25,6 @@ struct Mesh;
* data to not overwrite the original. */
struct DerivedMesh *CDDM_from_mesh(struct Mesh *mesh);
-/* Copies the given DerivedMesh with verts, faces & edges stored as
- * custom element data. */
-struct DerivedMesh *CDDM_copy(struct DerivedMesh *source);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h
index 6af38b14ea4..f5ffd1190b1 100644
--- a/source/blender/blenkernel/BKE_cloth.h
+++ b/source/blender/blenkernel/BKE_cloth.h
@@ -79,7 +79,7 @@ typedef struct Cloth {
int last_frame;
float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */
float average_acceleration[3]; /* Moving average of overall acceleration. */
- struct MEdge *edges; /* Used for hair collisions. */
+ const struct MEdge *edges; /* Used for hair collisions. */
struct EdgeSet *sew_edge_graph; /* Sewing edges represented using a GHash */
} Cloth;
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h
index feb3dc7de80..dd7866d83e5 100644
--- a/source/blender/blenkernel/BKE_collection.h
+++ b/source/blender/blenkernel/BKE_collection.h
@@ -26,6 +26,7 @@ struct BlendExpander;
struct BlendLibReader;
struct BlendWriter;
struct Collection;
+struct ID;
struct Library;
struct Main;
struct Object;
@@ -94,7 +95,7 @@ struct Collection *BKE_collection_duplicate(struct Main *bmain,
/* Master Collection for Scene */
#define BKE_SCENE_COLLECTION_NAME "Scene Collection"
-struct Collection *BKE_collection_master_add(void);
+struct Collection *BKE_collection_master_add(struct Scene *scene);
/* Collection Objects */
@@ -215,7 +216,8 @@ struct ListBase BKE_collection_object_cache_get(struct Collection *collection);
ListBase BKE_collection_object_cache_instanced_get(struct Collection *collection);
void BKE_collection_object_cache_free(struct Collection *collection);
-struct Base *BKE_collection_or_layer_objects(const struct ViewLayer *view_layer,
+struct Base *BKE_collection_or_layer_objects(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct Collection *collection);
/* Editing. */
@@ -238,7 +240,8 @@ const char *BKE_collection_ui_name_get(struct Collection *collection);
* Select all the objects in this Collection (and its nested collections) for this ViewLayer.
* Return true if any object was selected.
*/
-bool BKE_collection_objects_select(struct ViewLayer *view_layer,
+bool BKE_collection_objects_select(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct Collection *collection,
bool deselect);
@@ -296,7 +299,9 @@ void BKE_main_collections_parent_relations_rebuild(struct Main *bmain);
/* .blend file I/O */
void BKE_collection_blend_write_nolib(struct BlendWriter *writer, struct Collection *collection);
-void BKE_collection_blend_read_data(struct BlendDataReader *reader, struct Collection *collection);
+void BKE_collection_blend_read_data(struct BlendDataReader *reader,
+ struct Collection *collection,
+ struct ID *owner_id);
void BKE_collection_blend_read_lib(struct BlendLibReader *reader, struct Collection *collection);
void BKE_collection_blend_read_expand(struct BlendExpander *expander,
struct Collection *collection);
diff --git a/source/blender/blenkernel/BKE_collision.h b/source/blender/blenkernel/BKE_collision.h
index b93babaaefa..291d76df4c8 100644
--- a/source/blender/blenkernel/BKE_collision.h
+++ b/source/blender/blenkernel/BKE_collision.h
@@ -17,6 +17,7 @@ struct Depsgraph;
struct MVert;
struct MVertTri;
struct Object;
+struct Scene;
////////////////////////////////////////
// used for collisions in collision.c
diff --git a/source/blender/blenkernel/BKE_compute_contexts.hh b/source/blender/blenkernel/BKE_compute_contexts.hh
new file mode 100644
index 00000000000..a8f0022f49b
--- /dev/null
+++ b/source/blender/blenkernel/BKE_compute_contexts.hh
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/**
+ * This file implements some specific compute contexts for concepts in Blender.
+ */
+
+#include "BLI_compute_context.hh"
+
+namespace blender::bke {
+
+class ModifierComputeContext : public ComputeContext {
+ private:
+ static constexpr const char *s_static_type = "MODIFIER";
+
+ /**
+ * Use modifier name instead of something like `session_uuid` for now because:
+ * - It's more obvious that the name matches between the original and evaluated object.
+ * - We might want that the context hash is consistent between sessions in the future.
+ */
+ std::string modifier_name_;
+
+ public:
+ ModifierComputeContext(const ComputeContext *parent, std::string modifier_name);
+
+ private:
+ void print_current_in_line(std::ostream &stream) const override;
+};
+
+class NodeGroupComputeContext : public ComputeContext {
+ private:
+ static constexpr const char *s_static_type = "NODE_GROUP";
+
+ std::string node_name_;
+
+ public:
+ NodeGroupComputeContext(const ComputeContext *parent, std::string node_name);
+
+ StringRefNull node_name() const;
+
+ private:
+ void print_current_in_line(std::ostream &stream) const override;
+};
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_crazyspace.hh b/source/blender/blenkernel/BKE_crazyspace.hh
new file mode 100644
index 00000000000..adebf0b7884
--- /dev/null
+++ b/source/blender/blenkernel/BKE_crazyspace.hh
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bke
+ */
+
+#pragma once
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_span.hh"
+
+struct Depsgraph;
+struct Object;
+
+namespace blender::bke::crazyspace {
+
+/**
+ * Contains information about how points have been deformed during evaluation.
+ * This allows mapping edits on evaluated data back to original data in some cases.
+ */
+struct GeometryDeformation {
+ /**
+ * Positions of the deformed points. This may also point to the original position if no
+ * deformation data is available.
+ */
+ Span<float3> positions;
+ /**
+ * Matrices that transform point translations on original data into corresponding translations in
+ * evaluated data. This may be empty if not available.
+ */
+ Span<float3x3> deform_mats;
+
+ float3 translation_from_deformed_to_original(const int position_i,
+ const float3 &translation) const
+ {
+ if (this->deform_mats.is_empty()) {
+ return translation;
+ }
+ const float3x3 &deform_mat = this->deform_mats[position_i];
+ return deform_mat.inverted() * translation;
+ }
+};
+
+/**
+ * During evaluation of the object, deformation data may have been generated for this object. This
+ * function either retrieves the deformation data from the evaluated object, or falls back to
+ * returning the original data.
+ */
+GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph,
+ const Object &ob_orig);
+
+} // namespace blender::bke::crazyspace
diff --git a/source/blender/blenkernel/BKE_cryptomatte.h b/source/blender/blenkernel/BKE_cryptomatte.h
index 56049ecf405..b2024f09278 100644
--- a/source/blender/blenkernel/BKE_cryptomatte.h
+++ b/source/blender/blenkernel/BKE_cryptomatte.h
@@ -25,6 +25,8 @@ struct CryptomatteSession *BKE_cryptomatte_init(void);
struct CryptomatteSession *BKE_cryptomatte_init_from_render_result(
const struct RenderResult *render_result);
struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *scene);
+struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer(
+ const struct ViewLayer *view_layer);
void BKE_cryptomatte_free(struct CryptomatteSession *session);
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name);
diff --git a/source/blender/blenkernel/BKE_cryptomatte.hh b/source/blender/blenkernel/BKE_cryptomatte.hh
index cd3f8dc9f58..dd08f7b5c4f 100644
--- a/source/blender/blenkernel/BKE_cryptomatte.hh
+++ b/source/blender/blenkernel/BKE_cryptomatte.hh
@@ -12,6 +12,7 @@
#include "BKE_cryptomatte.h"
+#include "BLI_hash_mm3.h"
#include "BLI_map.hh"
#include "BLI_string_ref.hh"
@@ -54,10 +55,14 @@ struct CryptomatteHash {
uint32_t hash;
CryptomatteHash(uint32_t hash);
- CryptomatteHash(const char *name, int name_len);
- static CryptomatteHash from_hex_encoded(blender::StringRef hex_encoded);
+ CryptomatteHash(const char *name, int name_len)
+ {
+ hash = BLI_hash_mm3((const unsigned char *)name, name_len, 0);
+ }
+ static CryptomatteHash from_hex_encoded(blender::StringRef hex_encoded);
std::string hex_encoded() const;
+
/**
* Convert a cryptomatte hash to a float.
*
@@ -70,7 +75,20 @@ struct CryptomatteHash {
*
* Note that this conversion assumes to be running on a L-endian system.
*/
- float float_encoded() const;
+ float float_encoded() const
+ {
+ uint32_t mantissa = hash & ((1 << 23) - 1);
+ uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
+ exponent = MAX2(exponent, (uint32_t)1);
+ exponent = MIN2(exponent, (uint32_t)254);
+ exponent = exponent << 23;
+ uint32_t sign = (hash >> 31);
+ sign = sign << 31;
+ uint32_t float_bits = sign | exponent | mantissa;
+ float f;
+ memcpy(&f, &float_bits, sizeof(uint32_t));
+ return f;
+ }
};
struct CryptomatteLayer {
@@ -107,6 +125,8 @@ struct CryptomatteStampDataCallbackData {
const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get(
const CryptomatteSession &session);
+CryptomatteLayer *BKE_cryptomatte_layer_get(CryptomatteSession &session,
+ const StringRef layer_name);
struct CryptomatteSessionDeleter {
void operator()(CryptomatteSession *session)
diff --git a/source/blender/blenkernel/BKE_curves.h b/source/blender/blenkernel/BKE_curves.h
index c3302c6d2aa..71a0562e1df 100644
--- a/source/blender/blenkernel/BKE_curves.h
+++ b/source/blender/blenkernel/BKE_curves.h
@@ -25,7 +25,7 @@ void *BKE_curves_add(struct Main *bmain, const char *name);
struct BoundBox *BKE_curves_boundbox_get(struct Object *ob);
-bool BKE_curves_customdata_required(const struct Curves *curves, const char *name);
+bool BKE_curves_attribute_required(const struct Curves *curves, const char *name);
/* Depsgraph */
diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh
index fb97e52f6da..0d67152dec8 100644
--- a/source/blender/blenkernel/BKE_curves.hh
+++ b/source/blender/blenkernel/BKE_curves.hh
@@ -11,6 +11,7 @@
#include <mutex>
+#include "BLI_float3x3.hh"
#include "BLI_float4x4.hh"
#include "BLI_generic_virtual_array.hh"
#include "BLI_index_mask.hh"
@@ -21,6 +22,7 @@
#include "BLI_virtual_array.hh"
#include "BKE_attribute.hh"
+#include "BKE_attribute_math.hh"
namespace blender::bke {
@@ -96,7 +98,7 @@ class CurvesGeometryRuntime {
mutable Span<float3> evaluated_positions_span;
/**
- * Cache of lengths along each evaluated curve for for each evaluated point. If a curve is
+ * Cache of lengths along each evaluated curve for each evaluated point. If a curve is
* cyclic, it needs one more length value to correspond to the last segment, so in order to
* make slicing this array for a curve fast, an extra float is stored for every curve.
*/
@@ -149,12 +151,23 @@ class CurvesGeometry : public ::CurvesGeometry {
* Accessors.
*/
+ /**
+ * The total number of control points in all curves.
+ */
int points_num() const;
+ /**
+ * The number of curves in the data-block.
+ */
int curves_num() const;
IndexRange points_range() const;
IndexRange curves_range() const;
/**
+ * Number of control points in the indexed curve.
+ */
+ int points_num_for_curve(const int index) const;
+
+ /**
* The index of the first point in every curve. The size of this span is one larger than the
* number of curves. Consider using #points_for_curve rather than using the offsets directly.
*/
@@ -205,7 +218,7 @@ class CurvesGeometry : public ::CurvesGeometry {
/**
* How many evaluated points to create for each segment when evaluating Bezier,
- * Catmull Rom, and NURBS curves. On the curve domain.
+ * Catmull Rom, and NURBS curves. On the curve domain. Values must be one or greater.
*/
VArray<int> resolution() const;
/** Mutable access to curve resolution. Call #tag_topology_changed after changes. */
@@ -337,12 +350,14 @@ class CurvesGeometry : public ::CurvesGeometry {
/** Calculates the data described by #evaluated_lengths_for_curve if necessary. */
void ensure_evaluated_lengths() const;
+ void ensure_can_interpolate_to_evaluated() const;
+
/**
* Evaluate a generic data to the standard evaluated points of a specific curve,
* defined by the resolution attribute or other factors, depending on the curve type.
*
* \warning This function expects offsets to the evaluated points for each curve to be
- * calculated. That can be ensured with #ensure_evaluated_offsets.
+ * calculated. That can be ensured with #ensure_can_interpolate_to_evaluated.
*/
void interpolate_to_evaluated(int curve_index, GSpan src, GMutableSpan dst) const;
/**
@@ -385,8 +400,6 @@ class CurvesGeometry : public ::CurvesGeometry {
void calculate_bezier_auto_handles();
- void update_customdata_pointers();
-
void remove_points(IndexMask points_to_delete);
void remove_curves(IndexMask curves_to_delete);
@@ -416,6 +429,38 @@ class CurvesGeometry : public ::CurvesGeometry {
}
};
+/**
+ * Used to propagate deformation data through modifier evaluation so that sculpt tools can work on
+ * evaluated data.
+ */
+class CurvesEditHints {
+ public:
+ /**
+ * Original data that the edit hints below are meant to be used for.
+ */
+ const Curves &curves_id_orig;
+ /**
+ * Evaluated positions for the points in #curves_orig. If this is empty, the positions from the
+ * evaluated #Curves should be used if possible.
+ */
+ std::optional<Array<float3>> positions;
+ /**
+ * Matrices which transform point movement vectors from original data to corresponding movements
+ * of evaluated data.
+ */
+ std::optional<Array<float3x3>> deform_mats;
+
+ CurvesEditHints(const Curves &curves_id_orig) : curves_id_orig(curves_id_orig)
+ {
+ }
+
+ /**
+ * The edit hints have to correspond to the original curves, i.e. the number of deformed points
+ * is the same as the number of original points.
+ */
+ bool is_valid() const;
+};
+
namespace curves {
/* -------------------------------------------------------------------- */
@@ -493,6 +538,16 @@ bool segment_is_vector(Span<int8_t> handle_types_left,
int segment_index);
/**
+ * True if the Bezier curve contains polygonal segments of HandleType::BEZIER_HANDLE_VECTOR.
+ *
+ * \param num_curve_points: Number of points in the curve.
+ * \param evaluated_size: Number of evaluated points in the curve.
+ * \param cyclic: If curve is cyclic.
+ * \param resolution: Curve resolution.
+ */
+bool has_vector_handles(int num_curve_points, int64_t evaluated_size, bool cyclic, int resolution);
+
+/**
* Return true if the curve's last cyclic segment has a vector type.
* This only makes a difference in the shape of cyclic curves.
*/
@@ -520,7 +575,7 @@ void calculate_evaluated_offsets(Span<int8_t> handle_types_left,
int resolution,
MutableSpan<int> evaluated_offsets);
-/** See #insert. */
+/** Knot insertion result, see #insert. */
struct Insertion {
float3 handle_prev;
float3 left_handle;
@@ -530,8 +585,12 @@ struct Insertion {
};
/**
- * Compute the Bezier segment insertion for the given parameter on the segment, returning
- * the position and handles of the new point and the updated existing handle positions.
+ * Compute the insertion of a control point and handles in a Bezier segment without changing its
+ * shape.
+ * \param parameter: Factor in from 0 to 1 defining the insertion point within the segment.
+ * \return Inserted point parameters including position, and both new and updated handles for
+ * neighboring control points.
+ *
* <pre>
* handle_prev handle_next
* x-----------------x
@@ -650,6 +709,29 @@ void interpolate_to_evaluated(const GSpan src,
const Span<int> evaluated_offsets,
GMutableSpan dst);
+void calculate_basis(const float parameter, float4 &r_weights);
+
+/**
+ * Interpolate the control point values for the given parameter on the piecewise segment.
+ * \param a: Value associated with the first control point influencing the segment.
+ * \param d: Value associated with the fourth control point.
+ * \param parameter: Parameter in range [0, 1] to compute the interpolation for.
+ */
+template<typename T>
+T interpolate(const T &a, const T &b, const T &c, const T &d, const float parameter)
+{
+ BLI_assert(0.0f <= parameter && parameter <= 1.0f);
+ float4 n;
+ calculate_basis(parameter, n);
+ if constexpr (is_same_any_v<T, float, float2, float3>) {
+ /* Save multiplications by adjusting weights after mix. */
+ return 0.5f * attribute_math::mix4<T>(n, a, b, c, d);
+ }
+ else {
+ return attribute_math::mix4<T>(n * 0.5f, a, b, c, d);
+ }
+}
+
} // namespace catmull_rom
/** \} */
@@ -735,6 +817,12 @@ Curves *curves_new_nomain(CurvesGeometry curves);
*/
Curves *curves_new_nomain_single(int points_num, CurveType type);
+/**
+ * Copy data from #src to #dst, except the geometry data in #CurvesGeometry. Typically used to
+ * copy high-level parameters when a geometry-altering operation creates a new curves data-block.
+ */
+void curves_copy_parameters(const Curves &src, Curves &dst);
+
std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &types);
/* -------------------------------------------------------------------- */
@@ -758,6 +846,16 @@ inline IndexRange CurvesGeometry::curves_range() const
return IndexRange(this->curves_num());
}
+inline int CurvesGeometry::points_num_for_curve(const int index) const
+{
+ BLI_assert(this->curve_num > 0);
+ BLI_assert(this->curve_num > index);
+ BLI_assert(this->curve_offsets != nullptr);
+ const int offset = this->curve_offsets[index];
+ const int offset_next = this->curve_offsets[index + 1];
+ return offset_next - offset;
+}
+
inline bool CurvesGeometry::is_single_type(const CurveType type) const
{
return this->curve_type_counts()[type] == this->curves_num();
@@ -784,6 +882,7 @@ inline IndexRange CurvesGeometry::points_for_curve(const int index) const
{
/* Offsets are not allocated when there are no curves. */
BLI_assert(this->curve_num > 0);
+ BLI_assert(this->curve_num > index);
BLI_assert(this->curve_offsets != nullptr);
const int offset = this->curve_offsets[index];
const int offset_next = this->curve_offsets[index + 1];
@@ -856,11 +955,13 @@ inline float CurvesGeometry::evaluated_length_total_for_curve(const int curve_in
/** \} */
+namespace curves {
+
/* -------------------------------------------------------------------- */
/** \name Bezier Inline Methods
* \{ */
-namespace curves::bezier {
+namespace bezier {
inline bool point_is_sharp(const Span<int8_t> handle_types_left,
const Span<int8_t> handle_types_right,
@@ -880,14 +981,24 @@ inline bool segment_is_vector(const int8_t left, const int8_t right)
return segment_is_vector(HandleType(left), HandleType(right));
}
+inline bool has_vector_handles(const int num_curve_points,
+ const int64_t evaluated_size,
+ const bool cyclic,
+ const int resolution)
+{
+ return evaluated_size - !cyclic != (int64_t)segments_num(num_curve_points, cyclic) * resolution;
+}
+
inline float3 calculate_vector_handle(const float3 &point, const float3 &next_point)
{
return math::interpolate(point, next_point, 1.0f / 3.0f);
}
+} // namespace bezier
+
/** \} */
-} // namespace curves::bezier
+} // namespace curves
struct CurvesSurfaceTransforms {
float4x4 curves_to_world;
diff --git a/source/blender/blenkernel/BKE_curves_utils.hh b/source/blender/blenkernel/BKE_curves_utils.hh
index f223e173ea9..f9155023db7 100644
--- a/source/blender/blenkernel/BKE_curves_utils.hh
+++ b/source/blender/blenkernel/BKE_curves_utils.hh
@@ -11,9 +11,301 @@
#include "BLI_function_ref.hh"
#include "BLI_generic_pointer.hh"
+#include "BLI_index_range.hh"
namespace blender::bke::curves {
+/* -------------------------------------------------------------------- */
+/** \name Utility Structs
+ * \{ */
+
+/**
+ * Reference to a piecewise segment on a spline curve.
+ */
+struct CurveSegment {
+ /**
+ * Index of the previous control/evaluated point on the curve. First point on the segment.
+ */
+ int index;
+ /**
+ * Index of the next control/evaluated point on the curve. Last point on the curve segment.
+ * Should be 0 for looped segments.
+ */
+ int next_index;
+};
+
+/**
+ * Reference to a point on a piecewise curve (spline).
+ *
+ * Tracks indices of the neighboring control/evaluated point pair associated with the segment
+ * in which the point resides. Referenced point within the segment is defined by a
+ * normalized parameter in the range [0, 1].
+ */
+struct CurvePoint : public CurveSegment {
+ /**
+ * Normalized parameter in the range [0, 1] defining the point on the piecewise segment.
+ * Note that the curve point representation is not unique at segment endpoints.
+ */
+ float parameter;
+
+ /**
+ * True if the parameter is an integer and references a control/evaluated point.
+ */
+ inline bool is_controlpoint() const;
+
+ /*
+ * Compare if the points are equal.
+ */
+ inline bool operator==(const CurvePoint &other) const;
+ inline bool operator!=(const CurvePoint &other) const;
+
+ /**
+ * Compare if 'this' point comes before 'other'. Loop segment for cyclical curves counts
+ * as the first (least) segment.
+ */
+ inline bool operator<(const CurvePoint &other) const;
+};
+
+/**
+ * Cyclical index range. Iterates the interval [start, end).
+ */
+class IndexRangeCyclic {
+ /* Index to the start and end of the iterated range.
+ */
+ int64_t start_ = 0;
+ int64_t end_ = 0;
+ /* Index for the start and end of the entire iterable range which contains the iterated range
+ * (e.g. the point range for an individual spline/curve within the entire Curves point domain).
+ */
+ int64_t range_start_ = 0;
+ int64_t range_end_ = 0;
+ /* Number of times the range end is passed when the range is iterated.
+ */
+ int64_t cycles_ = 0;
+
+ constexpr IndexRangeCyclic(int64_t begin,
+ int64_t end,
+ int64_t iterable_range_start,
+ int64_t iterable_range_end,
+ int64_t cycles)
+ : start_(begin),
+ end_(end),
+ range_start_(iterable_range_start),
+ range_end_(iterable_range_end),
+ cycles_(cycles)
+ {
+ }
+
+ public:
+ constexpr IndexRangeCyclic() = default;
+ ~IndexRangeCyclic() = default;
+
+ constexpr IndexRangeCyclic(int64_t start, int64_t end, IndexRange iterable_range, int64_t cycles)
+ : start_(start),
+ end_(end),
+ range_start_(iterable_range.first()),
+ range_end_(iterable_range.one_after_last()),
+ cycles_(cycles)
+ {
+ }
+
+ /**
+ * Create an iterator over the cyclical interval [start_index, end_index).
+ */
+ constexpr IndexRangeCyclic(int64_t start, int64_t end, IndexRange iterable_range)
+ : start_(start),
+ end_(end == iterable_range.one_after_last() ? iterable_range.first() : end),
+ range_start_(iterable_range.first()),
+ range_end_(iterable_range.one_after_last()),
+ cycles_(end < start)
+ {
+ }
+
+ /**
+ * Increment the range by adding the given number of indices to the beginning of the range.
+ */
+ constexpr IndexRangeCyclic push_forward(int n)
+ {
+ BLI_assert(n >= 0);
+ int64_t nstart = start_ - n;
+ int64_t cycles = cycles_;
+ if (nstart < range_start_) {
+
+ cycles += (int64_t)(n / (range_end_ - range_start_)) + (end_ < nstart) - (end_ < start_);
+ }
+ return {nstart, end_, range_start_, range_end_, cycles};
+ }
+ /**
+ * Increment the range by adding the given number of indices to the end of the range.
+ */
+ constexpr IndexRangeCyclic push_backward(int n)
+ {
+ BLI_assert(n >= 0);
+ int64_t new_end = end_ + n;
+ int64_t cycles = cycles_;
+ if (range_end_ <= new_end) {
+ cycles += (int64_t)(n / (range_end_ - range_start_)) + (new_end < start_) - (end_ < start_);
+ }
+ return {start_, new_end, range_start_, range_end_, cycles};
+ }
+
+ /**
+ * Get the index range for the curve buffer.
+ */
+ constexpr IndexRange curve_range() const
+ {
+ return IndexRange(range_start_, total_size());
+ }
+
+ /**
+ * Range between the first element up to the end of the range.
+ */
+ constexpr IndexRange range_before_loop() const
+ {
+ return IndexRange(start_, size_before_loop());
+ }
+
+ /**
+ * Range between the first element in the iterable range up to the last element in the range.
+ */
+ constexpr IndexRange range_after_loop() const
+ {
+ return IndexRange(range_start_, size_after_loop());
+ }
+
+ /**
+ * Size of the entire iterable range.
+ */
+ constexpr int64_t total_size() const
+ {
+ return range_end_ - range_start_;
+ }
+
+ /**
+ * Number of elements between the first element in the range up to the last element in the curve.
+ */
+ constexpr int64_t size_before_loop() const
+ {
+ return range_end_ - start_;
+ }
+
+ /**
+ * Number of elements between the first element in the iterable range up to the last element in
+ * the range.
+ */
+ constexpr int64_t size_after_loop() const
+ {
+ return end_ - range_start_;
+ }
+
+ /**
+ * Get number of elements iterated by the cyclical index range.
+ */
+ constexpr int64_t size() const
+ {
+ if (cycles_ > 0) {
+ return size_before_loop() + end_ + (cycles_ - 1) * (range_end_ - range_start_);
+ }
+ else {
+ return end_ - start_;
+ }
+ }
+
+ /**
+ * Return the number of times the iterator will cycle before ending.
+ */
+ constexpr int64_t cycles() const
+ {
+ return cycles_;
+ }
+
+ constexpr int64_t first() const
+ {
+ return start_;
+ }
+
+ constexpr int64_t one_after_last() const
+ {
+ return end_;
+ }
+
+ struct CyclicIterator; /* Forward declaration */
+
+ constexpr CyclicIterator begin() const
+ {
+ return CyclicIterator(range_start_, range_end_, start_, 0);
+ }
+
+ constexpr CyclicIterator end() const
+ {
+ return CyclicIterator(range_start_, range_end_, end_, cycles_);
+ }
+
+ struct CyclicIterator {
+ int64_t index_, begin_, end_, cycles_;
+
+ constexpr CyclicIterator(int64_t range_begin, int64_t range_end, int64_t index, int64_t cycles)
+ : index_(index), begin_(range_begin), end_(range_end), cycles_(cycles)
+ {
+ BLI_assert(range_begin <= index && index <= range_end);
+ }
+
+ constexpr CyclicIterator(const CyclicIterator &copy)
+ : index_(copy.index_), begin_(copy.begin_), end_(copy.end_), cycles_(copy.cycles_)
+ {
+ }
+ ~CyclicIterator() = default;
+
+ constexpr CyclicIterator &operator=(const CyclicIterator &copy)
+ {
+ if (this == &copy) {
+ return *this;
+ }
+ index_ = copy.index_;
+ begin_ = copy.begin_;
+ end_ = copy.end_;
+ cycles_ = copy.cycles_;
+ return *this;
+ }
+ constexpr CyclicIterator &operator++()
+ {
+ index_++;
+ if (index_ == end_) {
+ index_ = begin_;
+ cycles_++;
+ }
+ return *this;
+ }
+
+ void increment(int64_t n)
+ {
+ for (int i = 0; i < n; i++) {
+ ++*this;
+ }
+ }
+
+ constexpr const int64_t &operator*() const
+ {
+ return index_;
+ }
+
+ constexpr bool operator==(const CyclicIterator &other) const
+ {
+ return index_ == other.index_ && cycles_ == other.cycles_;
+ }
+ constexpr bool operator!=(const CyclicIterator &other) const
+ {
+ return !this->operator==(other);
+ }
+ };
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utility Functions
+ * \{ */
+
/**
* Copy the provided point attribute values between all curves in the #curve_ranges index
* ranges, assuming that all curves have the same number of control points in #src_curves
@@ -34,8 +326,8 @@ void copy_point_data(const CurvesGeometry &src_curves,
template<typename T>
void copy_point_data(const CurvesGeometry &src_curves,
const CurvesGeometry &dst_curves,
- const IndexMask src_curve_selection,
- const Span<T> src,
+ IndexMask src_curve_selection,
+ Span<T> src,
MutableSpan<T> dst)
{
copy_point_data(src_curves, dst_curves, src_curve_selection, GSpan(src), GMutableSpan(dst));
@@ -48,13 +340,34 @@ void fill_points(const CurvesGeometry &curves,
template<typename T>
void fill_points(const CurvesGeometry &curves,
- const IndexMask curve_selection,
+ IndexMask curve_selection,
const T &value,
MutableSpan<T> dst)
{
fill_points(curves, curve_selection, &value, dst);
}
+void fill_points(const CurvesGeometry &curves,
+ Span<IndexRange> curve_ranges,
+ GPointer value,
+ GMutableSpan dst);
+
+template<typename T>
+void fill_points(const CurvesGeometry &curves,
+ Span<IndexRange> curve_ranges,
+ const T &value,
+ MutableSpan<T> dst)
+{
+ fill_points(curves, curve_ranges, &value, dst);
+}
+
+/**
+ * Copy only the information on the point domain, but not the offsets or any point attributes,
+ * meant for operations that change the number of points but not the number of curves.
+ * \warning The returned curves have invalid offsets!
+ */
+bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves);
+
/**
* Copy the size of every curve in #curve_ranges to the corresponding index in #counts.
*/
@@ -81,4 +394,40 @@ void foreach_curve_by_type(const VArray<int8_t> &types,
FunctionRef<void(IndexMask)> bezier_fn,
FunctionRef<void(IndexMask)> nurbs_fn);
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #CurvePoint Inline Methods
+ * \{ */
+
+inline bool CurvePoint::is_controlpoint() const
+{
+ return parameter == 0.0 || parameter == 1.0;
+}
+
+inline bool CurvePoint::operator==(const CurvePoint &other) const
+{
+ return (parameter == other.parameter && index == other.index) ||
+ (parameter == 1.0 && other.parameter == 0.0 && next_index == other.index) ||
+ (parameter == 0.0 && other.parameter == 1.0 && index == other.next_index);
+}
+inline bool CurvePoint::operator!=(const CurvePoint &other) const
+{
+ return !this->operator==(other);
+}
+
+inline bool CurvePoint::operator<(const CurvePoint &other) const
+{
+ if (index == other.index) {
+ return parameter < other.parameter;
+ }
+ else {
+ /* Use next index for cyclic comparison due to loop segment < first segment. */
+ return next_index < other.next_index &&
+ !(next_index == other.index && parameter == 1.0 && other.parameter == 0.0);
+ }
+}
+
+/** \} */
+
} // namespace blender::bke::curves
diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h
index 010fbb27172..064eb9ca1ed 100644
--- a/source/blender/blenkernel/BKE_customdata.h
+++ b/source/blender/blenkernel/BKE_customdata.h
@@ -11,7 +11,9 @@
#include "BLI_sys_types.h"
#include "BLI_utildefines.h"
#ifdef __cplusplus
+# include "BLI_set.hh"
# include "BLI_span.hh"
+# include "BLI_string_ref.hh"
# include "BLI_vector.hh"
#endif
@@ -53,14 +55,17 @@ extern const CustomData_MeshMasks CD_MASK_EVERYTHING;
typedef enum eCDAllocType {
/** Use the data pointer. */
CD_ASSIGN = 0,
- /** Allocate blank memory. */
- CD_CALLOC = 1,
- /** Allocate and set to default. */
- CD_DEFAULT = 2,
+ /** Allocate and set to default, which is usually just zeroed memory. */
+ CD_SET_DEFAULT = 2,
/** Use data pointers, set layer flag NOFREE. */
CD_REFERENCE = 3,
/** Do a full copy of all layers, only allowed if source has same number of elements. */
CD_DUPLICATE = 4,
+ /**
+ * Default construct new layer values. Does nothing for trivial types. This should be used
+ * if all layer values will be set by the caller after creating the layer.
+ */
+ CD_CONSTRUCT = 5,
} eCDAllocType;
#define CD_TYPE_AS_MASK(_type) (eCustomDataMask)((eCustomDataMask)1 << (eCustomDataMask)(_type))
@@ -132,7 +137,7 @@ void CustomData_data_add(int type, void *data1, const void *data2);
/**
* Initializes a CustomData object with the same layer setup as source.
- * mask is a bitfield where `(mask & (1 << (layer type)))` indicates
+ * mask is a bit-field where `(mask & (1 << (layer type)))` indicates
* if a layer should be copied or not. alloctype must be one of the above.
*/
void CustomData_copy(const struct CustomData *source,
@@ -141,6 +146,15 @@ void CustomData_copy(const struct CustomData *source,
eCDAllocType alloctype,
int totelem);
+/**
+ * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh.
+ */
+void CustomData_copy_mesh_to_bmesh(const struct CustomData *source,
+ struct CustomData *dest,
+ eCustomDataMask mask,
+ eCDAllocType alloctype,
+ int totelem);
+
/* BMESH_TODO, not really a public function but readfile.c needs it */
void CustomData_update_typemap(struct CustomData *data);
@@ -155,13 +169,20 @@ bool CustomData_merge(const struct CustomData *source,
int totelem);
/**
- * Reallocate custom data to a new element count.
- * Only affects on data layers which are owned by the CustomData itself,
- * referenced data is kept unchanged,
- *
- * \note Take care of referenced layers by yourself!
+ * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh.
+ */
+bool CustomData_merge_mesh_to_bmesh(const struct CustomData *source,
+ struct CustomData *dest,
+ eCustomDataMask mask,
+ eCDAllocType alloctype,
+ int totelem);
+
+/**
+ * Reallocate custom data to a new element count. If the new size is larger, the new values use
+ * the #CD_CONSTRUCT behavior, so trivial types must be initialized by the caller. After being
+ * resized, the #CustomData does not contain any referenced layers.
*/
-void CustomData_realloc(struct CustomData *data, int totelem);
+void CustomData_realloc(struct CustomData *data, int old_size, int new_size);
/**
* BMesh version of CustomData_merge; merges the layouts of source and `dest`,
@@ -397,7 +418,7 @@ void *CustomData_bmesh_get_n(const struct CustomData *data, void *block, int typ
*/
void *CustomData_bmesh_get_layer_n(const struct CustomData *data, void *block, int n);
-bool CustomData_set_layer_name(const struct CustomData *data, int type, int n, const char *name);
+bool CustomData_set_layer_name(struct CustomData *data, int type, int n, const char *name);
const char *CustomData_get_layer_name(const struct CustomData *data, int type, int n);
/**
@@ -408,6 +429,7 @@ void *CustomData_get_layer(const struct CustomData *data, int type);
void *CustomData_get_layer_n(const struct CustomData *data, int type, int n);
void *CustomData_get_layer_named(const struct CustomData *data, int type, const char *name);
int CustomData_get_offset(const struct CustomData *data, int type);
+int CustomData_get_offset_named(const CustomData *data, int type, const char *name);
int CustomData_get_n_offset(const struct CustomData *data, int type, int n);
int CustomData_get_layer_index(const struct CustomData *data, int type);
@@ -430,6 +452,12 @@ int CustomData_get_stencil_layer(const struct CustomData *data, int type);
const char *CustomData_get_active_layer_name(const struct CustomData *data, int type);
/**
+ * Returns name of the default layer of the given type or NULL
+ * if no such active layer is defined.
+ */
+const char *CustomData_get_render_layer_name(const struct CustomData *data, int type);
+
+/**
* Copies the data from source to the data element at index in the first layer of type
* no effect if there is no layer of type.
*/
@@ -610,7 +638,6 @@ enum {
CD_FAKE_CREASE = CD_FAKE | CD_CREASE, /* *sigh*. */
/* Multiple types of mesh elements... */
- CD_FAKE_BWEIGHT = CD_FAKE | CD_BWEIGHT, /* *sigh*. */
CD_FAKE_UV = CD_FAKE |
CD_MLOOPUV, /* UV flag, because we handle both loop's UVs and poly's textures. */
@@ -666,7 +693,7 @@ typedef struct CustomDataTransferLayerMap {
size_t data_size;
/** Offset of actual data we transfer (in element contained in data_src/dst). */
size_t data_offset;
- /** For bitflag transfer, flag(s) to affect in transferred data. */
+ /** For bit-flag transfer, flag(s) to affect in transferred data. */
uint64_t data_flag;
/** Opaque pointer, to be used by specific interp callback (e.g. transformspace for normals). */
@@ -696,7 +723,8 @@ void CustomData_data_transfer(const struct MeshPairRemap *me_remap,
* the struct.
*/
void CustomData_blend_write_prepare(CustomData &data,
- blender::Vector<CustomDataLayer, 16> &layers_to_write);
+ blender::Vector<CustomDataLayer, 16> &layers_to_write,
+ const blender::Set<std::string> &skip_names = {});
/**
* \param layers_to_write: Layers created by #CustomData_blend_write_prepare.
@@ -712,6 +740,8 @@ void CustomData_blend_write(BlendWriter *writer,
void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count);
+size_t CustomData_get_elem_size(struct CustomDataLayer *layer);
+
#ifndef NDEBUG
struct DynStr;
/** Use to inspect mesh data when debugging. */
diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h
index a21d9141ac0..4023d6829d4 100644
--- a/source/blender/blenkernel/BKE_deform.h
+++ b/source/blender/blenkernel/BKE_deform.h
@@ -50,16 +50,34 @@ void BKE_defgroup_copy_list(struct ListBase *outbase, const struct ListBase *inb
struct bDeformGroup *BKE_defgroup_duplicate(const struct bDeformGroup *ingroup);
struct bDeformGroup *BKE_object_defgroup_find_name(const struct Object *ob, const char *name);
/**
- * \note caller must free.
+ * Returns flip map for the vertex-groups of `ob`.
+ *
+ * \param use_default: How to handle cases where no symmetrical group is found.
+ * - false: sets these indices to -1, indicating the group should be ignored.
+ * - true: sets the index to its location in the array (making the group point to it's self).
+ * Enable this for symmetrical actions which apply weight operations on symmetrical vertices
+ * where the symmetrical group will be used (if found), otherwise the same group is used.
+ *
+ * \return An index array `r_flip_map_num` length,
+ * (aligned with the list result from `BKE_id_defgroup_list_get(ob)`).
+ * referencing the index of the symmetrical vertex-group of a fall-back value (see `use_default`).
+ * The caller is responsible for freeing the array.
+ */
+int *BKE_object_defgroup_flip_map(const struct Object *ob, bool use_default, int *r_flip_map_num);
+
+/**
+ * A version of #BKE_object_defgroup_flip_map that ignores locked groups.
*/
-int *BKE_object_defgroup_flip_map(const struct Object *ob, int *flip_map_len, bool use_default);
+int *BKE_object_defgroup_flip_map_unlocked(const struct Object *ob,
+ bool use_default,
+ int *r_flip_map_num);
/**
- * \note caller must free.
+ * A version of #BKE_object_defgroup_flip_map that only takes a single group into account.
*/
int *BKE_object_defgroup_flip_map_single(const struct Object *ob,
- int *flip_map_len,
bool use_default,
- int defgroup);
+ int defgroup,
+ int *r_flip_map_num);
int BKE_object_defgroup_flip_index(const struct Object *ob, int index, bool use_default);
int BKE_object_defgroup_name_index(const struct Object *ob, const char *name);
void BKE_object_defgroup_unique_name(struct bDeformGroup *dg, struct Object *ob);
@@ -112,7 +130,7 @@ float BKE_defvert_array_find_weight_safe(const struct MDeformVert *dvert, int in
* \return The total weight in all groups marked in the selection mask.
*/
float BKE_defvert_total_selected_weight(const struct MDeformVert *dv,
- int defbase_tot,
+ int defbase_num,
const bool *defbase_sel);
/**
@@ -124,9 +142,9 @@ float BKE_defvert_total_selected_weight(const struct MDeformVert *dv,
* commutative with the collective weight function.
*/
float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
- int defbase_tot,
+ int defbase_num,
const bool *defbase_sel,
- int defbase_tot_sel,
+ int defbase_sel_num,
bool is_normalized);
/* This much unlocked weight is considered equivalent to none. */
@@ -147,7 +165,7 @@ float BKE_defvert_calc_lock_relative_weight(float weight,
*/
float BKE_defvert_lock_relative_weight(float weight,
const struct MDeformVert *dv,
- int defbase_tot,
+ int defbase_num,
const bool *defbase_locked,
const bool *defbase_unlocked);
@@ -160,7 +178,7 @@ void BKE_defvert_copy(struct MDeformVert *dvert_dst, const struct MDeformVert *d
void BKE_defvert_copy_subset(struct MDeformVert *dvert_dst,
const struct MDeformVert *dvert_src,
const bool *vgroup_subset,
- int vgroup_tot);
+ int vgroup_num);
/**
* Overwrite weights filtered by vgroup_subset and with mirroring specified by the flip map
* - do nothing if neither are set.
@@ -169,9 +187,9 @@ void BKE_defvert_copy_subset(struct MDeformVert *dvert_dst,
void BKE_defvert_mirror_subset(struct MDeformVert *dvert_dst,
const struct MDeformVert *dvert_src,
const bool *vgroup_subset,
- int vgroup_tot,
+ int vgroup_num,
const int *flip_map,
- int flip_map_len);
+ int flip_map_num);
/**
* Copy an index from one #MDeformVert to another.
* - do nothing if neither are set.
@@ -194,43 +212,43 @@ void BKE_defvert_sync(struct MDeformVert *dvert_dst,
void BKE_defvert_sync_mapped(struct MDeformVert *dvert_dst,
const struct MDeformVert *dvert_src,
const int *flip_map,
- int flip_map_len,
+ int flip_map_num,
bool use_ensure);
/**
* be sure all flip_map values are valid
*/
void BKE_defvert_remap(struct MDeformVert *dvert, const int *map, int map_len);
-void BKE_defvert_flip(struct MDeformVert *dvert, const int *flip_map, int flip_map_len);
-void BKE_defvert_flip_merged(struct MDeformVert *dvert, const int *flip_map, int flip_map_len);
+void BKE_defvert_flip(struct MDeformVert *dvert, const int *flip_map, int flip_map_num);
+void BKE_defvert_flip_merged(struct MDeformVert *dvert, const int *flip_map, int flip_map_num);
void BKE_defvert_normalize(struct MDeformVert *dvert);
/**
* Same as #BKE_defvert_normalize but takes a bool array.
*/
void BKE_defvert_normalize_subset(struct MDeformVert *dvert,
const bool *vgroup_subset,
- int vgroup_tot);
+ int vgroup_num);
/**
* Same as BKE_defvert_normalize() if the locked vgroup is not a member of the subset
*/
void BKE_defvert_normalize_lock_single(struct MDeformVert *dvert,
const bool *vgroup_subset,
- int vgroup_tot,
+ int vgroup_num,
uint def_nr_lock);
/**
* Same as BKE_defvert_normalize() if no locked vgroup is a member of the subset
*/
void BKE_defvert_normalize_lock_map(struct MDeformVert *dvert,
const bool *vgroup_subset,
- int vgroup_tot,
+ int vgroup_num,
const bool *lock_flags,
- int defbase_tot);
+ int defbase_num);
/* Utilities to 'extract' a given vgroup into a simple float array,
* for verts, but also edges/polys/loops. */
void BKE_defvert_extract_vgroup_to_vertweights(const struct MDeformVert *dvert,
int defgroup,
- int num_verts,
+ int verts_num,
bool invert_vgroup,
float *r_weights);
/**
@@ -239,25 +257,25 @@ void BKE_defvert_extract_vgroup_to_vertweights(const struct MDeformVert *dvert,
*/
void BKE_defvert_extract_vgroup_to_edgeweights(const struct MDeformVert *dvert,
int defgroup,
- int num_verts,
- struct MEdge *edges,
- int num_edges,
+ int verts_num,
+ const struct MEdge *edges,
+ int edges_num,
bool invert_vgroup,
float *r_weights);
void BKE_defvert_extract_vgroup_to_loopweights(const struct MDeformVert *dvert,
int defgroup,
- int num_verts,
- struct MLoop *loops,
- int num_loops,
+ int verts_num,
+ const struct MLoop *loops,
+ int loops_num,
bool invert_vgroup,
float *r_weights);
void BKE_defvert_extract_vgroup_to_polyweights(const struct MDeformVert *dvert,
int defgroup,
- int num_verts,
- struct MLoop *loops,
- int num_loops,
- struct MPoly *polys,
- int num_polys,
+ int verts_num,
+ const struct MLoop *loops,
+ int loops_num,
+ const struct MPoly *polys,
+ int polys_num,
bool invert_vgroup,
float *r_weights);
diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h
index 7a21e85e310..6551e732300 100644
--- a/source/blender/blenkernel/BKE_displist.h
+++ b/source/blender/blenkernel/BKE_displist.h
@@ -24,8 +24,6 @@ enum {
DL_SURF = 2,
/** Triangles. */
DL_INDEX3 = 4,
- /** Quads, with support for triangles (when values of the 3rd and 4th indices match). */
- DL_INDEX4 = 5,
// DL_VERTCOL = 6, /* UNUSED */
/** Isolated points. */
DL_VERTS = 7,
@@ -61,17 +59,13 @@ typedef struct DispList {
int totindex; /* indexed array drawing surfaces */
} DispList;
-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(const struct ListBase *lb, int *totvert, int *totface, int *tottri);
void BKE_displist_free(struct ListBase *lb);
void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph,
const struct Scene *scene,
struct Object *ob,
bool for_render);
-void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph,
const struct Scene *scene,
diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h
index 113f9ac3b4c..3226455a183 100644
--- a/source/blender/blenkernel/BKE_effect.h
+++ b/source/blender/blenkernel/BKE_effect.h
@@ -103,6 +103,7 @@ void BKE_partdeflect_free(struct PartDeflect *pd);
* lookup of effectors during evaluation.
*/
struct ListBase *BKE_effector_relations_create(struct Depsgraph *depsgraph,
+ const struct Scene *scene,
struct ViewLayer *view_layer,
struct Collection *collection);
void BKE_effector_relations_free(struct ListBase *lb);
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index 10d9ce3364d..c11e6353bc0 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -106,7 +106,7 @@ typedef enum eFMI_Action_Types {
/* Flags for the requirements of a FModifier Type */
typedef enum eFMI_Requirement_Flags {
- /* modifier requires original data-points (kindof beats the purpose of a modifier stack?) */
+ /* modifier requires original data-points (kind of beats the purpose of a modifier stack?) */
FMI_REQUIRES_ORIGINAL_DATA = (1 << 0),
/* modifier doesn't require on any preceding data (i.e. it will generate a curve).
* Use in conjunction with FMI_TYPE_GENRATE_CURVE
@@ -463,23 +463,39 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt,
struct BezTriple *next,
float *r_pdelta);
+/**
+ * Delete a keyframe from an F-curve at a specific index.
+ */
+void BKE_fcurve_delete_key(struct FCurve *fcu, int index);
+
+/**
+ * Delete selected keyframes from an F-curve.
+ */
+bool BKE_fcurve_delete_keys_selected(struct FCurve *fcu);
+
+/**
+ * Delete all keyframes from an F-curve.
+ */
+void BKE_fcurve_delete_keys_all(struct FCurve *fcu);
+
/* -------- Curve Sanity -------- */
/**
* This function recalculates the handles of an F-Curve. Acts based on selection with `SELECT`
- * flag. To use a different flag, use #calchandles_fcurve_ex().
+ * flag. To use a different flag, use #BKE_fcurve_handles_recalc_ex().
*
* If the BezTriples have been rearranged, sort them first before using this.
*/
-void calchandles_fcurve(struct FCurve *fcu);
+void BKE_fcurve_handles_recalc(struct FCurve *fcu);
/**
- * Variant of #calchandles_fcurve() that allows calculating based on a different select flag.
+ * Variant of #BKE_fcurve_handles_recalc() that allows calculating based on a different select
+ * flag.
*
* \param handle_sel_flag: The flag (bezt.f1/2/3) value to use to determine selection.
* Usually `SELECT`, but may want to use a different one at times
* (if caller does not operate on selection).
*/
-void calchandles_fcurve_ex(struct FCurve *fcu, eBezTriple_Flag handle_sel_flag);
+void BKE_fcurve_handles_recalc_ex(struct FCurve *fcu, eBezTriple_Flag handle_sel_flag);
/**
* Update handles, making sure the handle-types are valid (e.g. correctly deduced from an "Auto"
* type), and recalculating their position vectors.
diff --git a/source/blender/blenkernel/BKE_fcurve_driver.h b/source/blender/blenkernel/BKE_fcurve_driver.h
index b6b1bdab109..a1b97222019 100644
--- a/source/blender/blenkernel/BKE_fcurve_driver.h
+++ b/source/blender/blenkernel/BKE_fcurve_driver.h
@@ -85,7 +85,6 @@ void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dva
void driver_change_variable_type(struct DriverVar *dvar, int type);
/**
* Validate driver variable name (after being renamed).
- *
*/
void driver_variable_name_validate(struct DriverVar *dvar);
/**
diff --git a/source/blender/blenkernel/BKE_geometry_fields.hh b/source/blender/blenkernel/BKE_geometry_fields.hh
index 7c504826044..62aac5a4120 100644
--- a/source/blender/blenkernel/BKE_geometry_fields.hh
+++ b/source/blender/blenkernel/BKE_geometry_fields.hh
@@ -8,47 +8,186 @@
* Common field utilities and field definitions for geometry components.
*/
+#include "BKE_attribute.h"
#include "BKE_geometry_set.hh"
#include "FN_field.hh"
+struct Mesh;
+struct PointCloud;
+
namespace blender::bke {
-class GeometryComponentFieldContext : public fn::FieldContext {
+class CurvesGeometry;
+class GeometryFieldInput;
+
+class MeshFieldContext : public fn::FieldContext {
+ private:
+ const Mesh &mesh_;
+ const eAttrDomain domain_;
+
+ public:
+ MeshFieldContext(const Mesh &mesh, const eAttrDomain domain);
+ const Mesh &mesh() const
+ {
+ return mesh_;
+ }
+
+ eAttrDomain domain() const
+ {
+ return domain_;
+ }
+};
+
+class CurvesFieldContext : public fn::FieldContext {
+ private:
+ const CurvesGeometry &curves_;
+ const eAttrDomain domain_;
+
+ public:
+ CurvesFieldContext(const CurvesGeometry &curves, const eAttrDomain domain);
+
+ const CurvesGeometry &curves() const
+ {
+ return curves_;
+ }
+
+ eAttrDomain domain() const
+ {
+ return domain_;
+ }
+};
+
+class PointCloudFieldContext : public fn::FieldContext {
+ private:
+ const PointCloud &pointcloud_;
+
+ public:
+ PointCloudFieldContext(const PointCloud &pointcloud) : pointcloud_(pointcloud)
+ {
+ }
+
+ const PointCloud &pointcloud() const
+ {
+ return pointcloud_;
+ }
+};
+
+class InstancesFieldContext : public fn::FieldContext {
+ private:
+ const InstancesComponent &instances_;
+
+ public:
+ InstancesFieldContext(const InstancesComponent &instances) : instances_(instances)
+ {
+ }
+
+ const InstancesComponent &instances() const
+ {
+ return instances_;
+ }
+};
+
+/**
+ * A field context that can represent meshes, curves, point clouds, or instances,
+ * used for field inputs that can work for multiple geometry types.
+ */
+class GeometryFieldContext : public fn::FieldContext {
private:
- const GeometryComponent &component_;
+ /**
+ * Store the geometry as a void pointer instead of a #GeometryComponent to allow referencing data
+ * that doesn't correspond directly to a geometry component type, in this case #CurvesGeometry
+ * instead of #Curves.
+ */
+ const void *geometry_;
+ const GeometryComponentType type_;
const eAttrDomain domain_;
+ friend GeometryFieldInput;
+
public:
- GeometryComponentFieldContext(const GeometryComponent &component, const eAttrDomain domain)
- : component_(component), domain_(domain)
+ GeometryFieldContext(const GeometryComponent &component, eAttrDomain domain);
+ GeometryFieldContext(const void *geometry, GeometryComponentType type, eAttrDomain domain);
+
+ const void *geometry() const
{
+ return geometry_;
}
- const GeometryComponent &geometry_component() const
+ GeometryComponentType type() const
{
- return component_;
+ return type_;
}
eAttrDomain domain() const
{
return domain_;
}
+
+ std::optional<AttributeAccessor> attributes() const;
+ const Mesh *mesh() const;
+ const CurvesGeometry *curves() const;
+ const PointCloud *pointcloud() const;
+ const InstancesComponent *instances() const;
+
+ private:
+ GeometryFieldContext(const Mesh &mesh, eAttrDomain domain);
+ GeometryFieldContext(const CurvesGeometry &curves, eAttrDomain domain);
+ GeometryFieldContext(const PointCloud &points);
+ GeometryFieldContext(const InstancesComponent &instances);
};
class GeometryFieldInput : public fn::FieldInput {
public:
using fn::FieldInput::FieldInput;
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask mask,
+ ResourceScope &scope) const override;
+ virtual GVArray get_varray_for_context(const GeometryFieldContext &context,
+ IndexMask mask) const = 0;
+};
+class MeshFieldInput : public fn::FieldInput {
+ public:
+ using fn::FieldInput::FieldInput;
GVArray get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
+ virtual GVArray get_varray_for_context(const Mesh &mesh,
+ eAttrDomain domain,
+ IndexMask mask) const = 0;
+};
- virtual GVArray get_varray_for_context(const GeometryComponent &component,
+class CurvesFieldInput : public fn::FieldInput {
+ public:
+ using fn::FieldInput::FieldInput;
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask mask,
+ ResourceScope &scope) const override;
+ virtual GVArray get_varray_for_context(const CurvesGeometry &curves,
eAttrDomain domain,
IndexMask mask) const = 0;
};
+class PointCloudFieldInput : public fn::FieldInput {
+ public:
+ using fn::FieldInput::FieldInput;
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask mask,
+ ResourceScope &scope) const override;
+ virtual GVArray get_varray_for_context(const PointCloud &pointcloud, IndexMask mask) const = 0;
+};
+
+class InstancesFieldInput : public fn::FieldInput {
+ public:
+ using fn::FieldInput::FieldInput;
+ GVArray get_varray_for_context(const fn::FieldContext &context,
+ IndexMask mask,
+ ResourceScope &scope) const override;
+ virtual GVArray get_varray_for_context(const InstancesComponent &instances,
+ IndexMask mask) const = 0;
+};
+
class AttributeFieldInput : public GeometryFieldInput {
private:
std::string name_;
@@ -72,8 +211,7 @@ class AttributeFieldInput : public GeometryFieldInput {
return name_;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- eAttrDomain domain,
+ GVArray get_varray_for_context(const GeometryFieldContext &context,
IndexMask mask) const override;
std::string socket_inspection_name() const override;
@@ -89,8 +227,7 @@ class IDAttributeFieldInput : public GeometryFieldInput {
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- eAttrDomain domain,
+ GVArray get_varray_for_context(const GeometryFieldContext &context,
IndexMask mask) const override;
std::string socket_inspection_name() const override;
@@ -99,12 +236,9 @@ class IDAttributeFieldInput : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override;
};
-VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttrDomain domain);
+VArray<float3> curve_normals_varray(const CurvesGeometry &curves, const eAttrDomain domain);
-VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
- const Mesh &mesh,
- const IndexMask mask,
- eAttrDomain domain);
+VArray<float3> mesh_normals_varray(const Mesh &mesh, const IndexMask mask, eAttrDomain domain);
class NormalFieldInput : public GeometryFieldInput {
public:
@@ -113,8 +247,7 @@ class NormalFieldInput : public GeometryFieldInput {
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
+ GVArray get_varray_for_context(const GeometryFieldContext &context,
IndexMask mask) const override;
std::string socket_inspection_name() const override;
@@ -152,8 +285,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
return fn::Field<T>{field_input};
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- eAttrDomain domain,
+ GVArray get_varray_for_context(const GeometryFieldContext &context,
IndexMask mask) const override;
std::string socket_inspection_name() const override;
@@ -162,10 +294,10 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override;
};
-class CurveLengthFieldInput final : public GeometryFieldInput {
+class CurveLengthFieldInput final : public CurvesFieldInput {
public:
CurveLengthFieldInput();
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const CurvesGeometry &curves,
eAttrDomain domain,
IndexMask mask) const final;
uint64_t hash() const override;
diff --git a/source/blender/blenkernel/BKE_geometry_set.h b/source/blender/blenkernel/BKE_geometry_set.h
index a28e9e6bdf6..97e69f3fe1f 100644
--- a/source/blender/blenkernel/BKE_geometry_set.h
+++ b/source/blender/blenkernel/BKE_geometry_set.h
@@ -23,9 +23,10 @@ typedef enum GeometryComponentType {
GEO_COMPONENT_TYPE_INSTANCES = 2,
GEO_COMPONENT_TYPE_VOLUME = 3,
GEO_COMPONENT_TYPE_CURVE = 4,
+ GEO_COMPONENT_TYPE_EDIT = 5,
} GeometryComponentType;
-#define GEO_COMPONENT_TYPE_ENUM_SIZE 5
+#define GEO_COMPONENT_TYPE_ENUM_SIZE 6
void BKE_geometry_set_free(struct GeometrySet *geometry_set);
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index 4108e2f7e2e..2ef9556afc7 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -26,7 +26,6 @@
struct Curves;
struct Collection;
struct Curve;
-struct CurveEval;
struct Mesh;
struct Object;
struct PointCloud;
@@ -43,7 +42,8 @@ enum class GeometryOwnershipType {
namespace blender::bke {
class ComponentAttributeProviders;
-}
+class CurvesEditHints;
+} // namespace blender::bke
class GeometryComponent;
@@ -168,6 +168,12 @@ struct GeometrySet {
* Remove all geometry components with types that are not in the provided list.
*/
void keep_only(const blender::Span<GeometryComponentType> component_types);
+ /**
+ * Keeps the provided geometry types, but also instances and edit data.
+ * Instances must not be removed while using #modify_geometry_sets.
+ */
+ void keep_only_during_modify(const blender::Span<GeometryComponentType> component_types);
+ void remove_geometry_during_modify();
void add(const GeometryComponent &component);
@@ -287,6 +293,10 @@ struct GeometrySet {
* Returns a read-only curves data-block or null.
*/
const Curves *get_curves_for_read() const;
+ /**
+ * Returns read-only curve edit hints or null.
+ */
+ const blender::bke::CurvesEditHints *get_curve_edit_hints_for_read() const;
/**
* Returns a mutable mesh or null. No ownership is transferred.
@@ -304,6 +314,10 @@ struct GeometrySet {
* Returns a mutable curves data-block or null. No ownership is transferred.
*/
Curves *get_curves_for_write();
+ /**
+ * Returns mutable curve edit hints or null.
+ */
+ blender::bke::CurvesEditHints *get_curve_edit_hints_for_write();
/* Utility methods for replacement. */
/**
@@ -450,47 +464,8 @@ class PointCloudComponent : public GeometryComponent {
};
/**
- * Legacy runtime-only curves type.
- * These curves are stored differently than other geometry components, because the data structure
- * used here does not correspond exactly to the #Curve DNA data structure. A #CurveEval is stored
- * here instead, though the component does give access to a #Curve for interfacing with render
- * engines and other areas of Blender that expect to use a data-block with an #ID.
- */
-class CurveComponentLegacy : public GeometryComponent {
- private:
- CurveEval *curve_ = nullptr;
- GeometryOwnershipType ownership_ = GeometryOwnershipType::Owned;
-
- public:
- CurveComponentLegacy();
- ~CurveComponentLegacy();
- GeometryComponent *copy() const override;
-
- void clear();
- bool has_curve() const;
- /**
- * Clear the component and replace it with the new curve.
- */
- void replace(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned);
- CurveEval *release();
-
- const CurveEval *get_for_read() const;
- CurveEval *get_for_write();
-
- bool is_empty() const final;
-
- bool owns_direct_data() const override;
- void ensure_owns_direct_data() override;
-
- std::optional<blender::bke::AttributeAccessor> attributes() const final;
- std::optional<blender::bke::MutableAttributeAccessor> attributes_for_write() final;
-
- static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_CURVE;
-};
-
-/**
- * A geometry component that stores a group of curves, corresponding the #Curves data-block type
- * and the #CurvesGeometry type. Attributes are are stored on the control point domain and the
+ * A geometry component that stores a group of curves, corresponding the #Curves data-block
+ * and the #CurvesGeometry type. Attributes are stored on the control point domain and the
* curve domain.
*/
class CurveComponent : public GeometryComponent {
@@ -499,10 +474,9 @@ class CurveComponent : public GeometryComponent {
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.
+ * Because rendering #Curves isn't fully working yet, we must provide a #Curve for the render
+ * engine and depsgraph object iterator in some cases. This allows using the old curve rendering
+ * even when the new curve data structure is used.
*/
mutable Curve *curve_for_render_ = nullptr;
mutable std::mutex curve_for_render_mutex_;
@@ -825,3 +799,37 @@ class VolumeComponent : public GeometryComponent {
static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_VOLUME;
};
+
+/**
+ * When the original data is in some edit mode, we want to propagate some additional information
+ * through object evaluation. This information can be used by edit modes to support working on
+ * evaluated data.
+ *
+ * This component is added at the beginning of modifier evaluation.
+ */
+class GeometryComponentEditData final : public GeometryComponent {
+ public:
+ /**
+ * Information about how original curves are manipulated during evaluation. This data is used so
+ * that curve sculpt tools can work on evaluated data. It is not stored in #CurveComponent
+ * because the data remains valid even when there is no actual curves geometry anymore, for
+ * example, when the curves have been converted to a mesh.
+ */
+ std::unique_ptr<blender::bke::CurvesEditHints> curves_edit_hints_;
+
+ GeometryComponentEditData();
+
+ GeometryComponent *copy() const final;
+ bool owns_direct_data() const final;
+ void ensure_owns_direct_data() final;
+
+ /**
+ * The first node that does topology changing operations on curves should store the curve point
+ * positions it retrieved as input. Without this, information about the deformed positions is
+ * lost, which would make curves sculpt mode fall back to using original curve positions instead
+ * of deformed ones.
+ */
+ static void remember_deformed_curve_positions_if_necessary(GeometrySet &geometry);
+
+ static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_EDIT;
+};
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index 96b6f7a53b0..e28c87cd7d6 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -80,7 +80,7 @@ typedef struct Global {
* * -1: Disable faster motion paths computation (since 08/2018).
* * 1 - 30: EEVEE debug/stats values (01/2018).
* * 31: Enable the Select Debug Engine. Only available with #WITH_DRAW_DEBUG (08/2021).
- * * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014).
+ * * 101: Enable UI debug drawing of full-screen area's corner widget (10/2014).
* * 102: Enable extra items in string search UI (05/2022).
* * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019).
* * 777: Enable UI node panel's sockets polling (11/2011).
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index ad3b1971ca9..b9219814c08 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -401,12 +401,19 @@ void BKE_gpencil_stroke_set_random_color(struct bGPDstroke *gps);
/**
* Join two strokes using the shortest distance (reorder stroke if necessary).
+ * \param auto_flip: Flip the stroke if the join between two strokes is not end->start points.
*/
void BKE_gpencil_stroke_join(struct bGPDstroke *gps_a,
struct bGPDstroke *gps_b,
bool leave_gaps,
bool fit_thickness,
- bool smooth);
+ bool smooth,
+ bool auto_flip);
+/**
+ * Set stroke start point in the selected index. Only works for Cyclic strokes.
+ * \param start_idx: Index of the point to be the start point.
+ */
+void BKE_gpencil_stroke_start_set(struct bGPDstroke *gps, int start_idx);
/**
* Copy the stroke of the frame to all frames selected (except current).
*/
@@ -466,8 +473,8 @@ void BKE_gpencil_stroke_uniform_subdivide(struct bGPdata *gpd,
* This allows for manipulations in 2D but also easy conversion back to 3D.
* \note also takes care of parent space transform.
*/
-void BKE_gpencil_stroke_to_view_space(struct RegionView3D *rv3d,
- struct bGPDstroke *gps,
+void BKE_gpencil_stroke_to_view_space(struct bGPDstroke *gps,
+ float viewmat[4][4],
const float diff_mat[4][4]);
/**
* Stroke from view space
@@ -475,20 +482,21 @@ void BKE_gpencil_stroke_to_view_space(struct RegionView3D *rv3d,
* Inverse of #BKE_gpencil_stroke_to_view_space
* \note also takes care of parent space transform.
*/
-void BKE_gpencil_stroke_from_view_space(struct RegionView3D *rv3d,
- struct bGPDstroke *gps,
+void BKE_gpencil_stroke_from_view_space(struct bGPDstroke *gps,
+ float viewinv[4][4],
const float diff_mat[4][4]);
/**
* Calculates the perimeter of a stroke projected from the view and returns it as a new stroke.
* \param subdivisions: Number of subdivisions for the start and end caps.
* \return: bGPDstroke pointer to stroke perimeter.
*/
-struct bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
+struct bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(float viewmat[4][4],
struct bGPdata *gpd,
const struct bGPDlayer *gpl,
struct bGPDstroke *gps,
int subdivisions,
- const float diff_mat[4][4]);
+ const float diff_mat[4][4],
+ const float thickness_chg);
/**
* Get average pressure.
*/
diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h
index 8d9351806c4..10d17c7f4c8 100644
--- a/source/blender/blenkernel/BKE_icons.h
+++ b/source/blender/blenkernel/BKE_icons.h
@@ -20,6 +20,8 @@ extern "C" {
#include "BLI_compiler_attrs.h"
+#include "DNA_ID_enums.h"
+
typedef void (*DrawInfoFreeFP)(void *drawinfo);
enum {
@@ -77,8 +79,6 @@ struct PreviewImage;
struct StudioLight;
struct bGPDlayer;
-enum eIconSizes;
-
void BKE_icons_init(int first_dyn_id);
/**
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h
index 3f7d9498e39..9ac4b4e4619 100644
--- a/source/blender/blenkernel/BKE_idprop.h
+++ b/source/blender/blenkernel/BKE_idprop.h
@@ -7,6 +7,7 @@
*/
#include "BLI_compiler_attrs.h"
+#include "BLI_sys_types.h"
#ifdef __cplusplus
extern "C" {
@@ -19,6 +20,7 @@ struct BlendWriter;
struct ID;
struct IDProperty;
struct IDPropertyUIData;
+struct Library;
typedef union IDPropertyTemplate {
int i;
@@ -96,7 +98,7 @@ void IDP_AssignID(struct IDProperty *prop, struct ID *id, int flag);
* Sync values from one group to another when values name and types match,
* copy the values, else ignore.
*
- * \note Use for syncing proxies.
+ * \note Was used for syncing proxies.
*/
void IDP_SyncGroupValues(struct IDProperty *dest, const struct IDProperty *src) ATTR_NONNULL();
void IDP_SyncGroupTypes(struct IDProperty *dest, const struct IDProperty *src, bool do_arraylen)
@@ -318,7 +320,7 @@ void IDP_BlendReadData_impl(struct BlendDataReader *reader,
struct IDProperty **prop,
const char *caller_func_id);
#define IDP_BlendDataRead(reader, prop) IDP_BlendReadData_impl(reader, prop, __func__)
-void IDP_BlendReadLib(struct BlendLibReader *reader, struct IDProperty *prop);
+void IDP_BlendReadLib(struct BlendLibReader *reader, struct Library *lib, struct IDProperty *prop);
void IDP_BlendReadExpand(struct BlendExpander *expander, struct IDProperty *prop);
typedef enum eIDPropertyUIDataType {
@@ -326,7 +328,7 @@ typedef enum eIDPropertyUIDataType {
IDP_UI_DATA_TYPE_UNSUPPORTED = -1,
/** IDP_INT or IDP_ARRAY with subtype IDP_INT. */
IDP_UI_DATA_TYPE_INT = 0,
- /** IDP_FLOAT and IDP_DOUBLE or IDP_ARRAY properties with a float or double subtypes. */
+ /** IDP_FLOAT and IDP_DOUBLE or IDP_ARRAY properties with a float or double sub-types. */
IDP_UI_DATA_TYPE_FLOAT = 1,
/** IDP_STRING properties. */
IDP_UI_DATA_TYPE_STRING = 2,
diff --git a/source/blender/blenkernel/BKE_idprop.hh b/source/blender/blenkernel/BKE_idprop.hh
index 1e741cc252f..e8ac4119970 100644
--- a/source/blender/blenkernel/BKE_idprop.hh
+++ b/source/blender/blenkernel/BKE_idprop.hh
@@ -45,8 +45,11 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, d
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
const StringRefNull value);
+/** \brief Allocate a new IDProperty of type IDP_ID, set its name and value. */
+std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, ID *id);
+
/**
- * \brief Allocate a new IDProperty of type IDP_ARRAY and subtype IDP_INT.
+ * \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_INT.
*
* \param values: The values will be copied into the IDProperty.
*/
@@ -54,14 +57,14 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name,
Span<int32_t> values);
/**
- * \brief Allocate a new IDProperty of type IDP_ARRAY and subtype IDP_FLOAT.
+ * \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_FLOAT.
*
* \param values: The values will be copied into the IDProperty.
*/
std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, Span<float> values);
/**
- * \brief Allocate a new IDProperty of type IDP_ARRAY and subtype IDP_DOUBLE.
+ * \brief Allocate a new IDProperty of type IDP_ARRAY and sub-type IDP_DOUBLE.
*
* \param values: The values will be copied into the IDProperty.
*/
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index 30f7ed45859..256ddec5505 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -85,7 +85,7 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id,
typedef void (*IDTypeForeachPathFunction)(struct ID *id, struct BPathForeachPathData *bpath_data);
-typedef struct ID *(*IDTypeEmbeddedOwnerGetFunction)(struct Main *bmain, struct ID *id);
+typedef struct ID **(*IDTypeEmbeddedOwnerPointerGetFunction)(struct ID *id);
typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer,
struct ID *id,
@@ -109,7 +109,7 @@ typedef struct IDTypeInfo {
*/
short id_code;
/**
- * Bitflag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's
+ * Bit-flag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's
* FILTER_ID_XX enums.
*/
uint64_t id_filter;
@@ -180,9 +180,9 @@ typedef struct IDTypeInfo {
IDTypeForeachPathFunction foreach_path;
/**
- * For embedded IDs, return their owner ID.
+ * For embedded IDs, return the address of the pointer to their owner ID.
*/
- IDTypeEmbeddedOwnerGetFunction owner_get;
+ IDTypeEmbeddedOwnerPointerGetFunction owner_pointer_get;
/* ********** Callbacks for reading and writing .blend files. ********** */
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 6ec1285af0c..eb43ce823ac 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -6,6 +6,7 @@
* \ingroup bke
*/
+#include "BLI_compiler_attrs.h"
#include "BLI_utildefines.h"
#include "BLI_rect.h"
@@ -97,7 +98,7 @@ int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, const struct ImageForm
*/
int BKE_imbuf_write_as(struct ImBuf *ibuf,
const char *name,
- struct ImageFormatData *imf,
+ const struct ImageFormatData *imf,
bool save_copy);
/**
@@ -236,11 +237,13 @@ void BKE_image_ensure_viewer_views(const struct RenderData *rd,
*/
void BKE_image_user_frame_calc(struct Image *ima, struct ImageUser *iuser, int cfra);
int BKE_image_user_frame_get(const struct ImageUser *iuser, int cfra, bool *r_is_in_range);
-void BKE_image_user_file_path(struct ImageUser *iuser, struct Image *ima, char *path);
-void BKE_image_user_file_path_ex(struct ImageUser *iuser,
- struct Image *ima,
+void BKE_image_user_file_path(const struct ImageUser *iuser, const struct Image *ima, char *path);
+void BKE_image_user_file_path_ex(const struct Main *bmain,
+ const struct ImageUser *iuser,
+ const struct Image *ima,
char *path,
- bool resolve_udim);
+ const bool resolve_udim,
+ const bool resolve_multiview);
void BKE_image_editors_update_frame(const struct Main *bmain, int cfra);
/**
@@ -258,15 +261,15 @@ struct RenderPass *BKE_image_multilayer_index(struct RenderResult *rr, struct Im
/**
* Sets index offset for multi-view files.
*/
-void BKE_image_multiview_index(struct Image *ima, struct ImageUser *iuser);
+void BKE_image_multiview_index(const struct Image *ima, struct ImageUser *iuser);
/**
* For multi-layer images as well as for render-viewer
* and because rendered results use fake layer/passes, don't correct for wrong indices here.
*/
-bool BKE_image_is_multilayer(struct Image *ima);
-bool BKE_image_is_multiview(struct Image *ima);
-bool BKE_image_is_stereo(struct Image *ima);
+bool BKE_image_is_multilayer(const struct Image *ima);
+bool BKE_image_is_multiview(const struct Image *ima);
+bool BKE_image_is_stereo(const struct Image *ima);
struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima);
void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima);
@@ -363,14 +366,7 @@ bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile);
void BKE_image_reassign_tile(struct Image *ima, struct ImageTile *tile, int new_tile_number);
void BKE_image_sort_tiles(struct Image *ima);
-bool BKE_image_fill_tile(struct Image *ima,
- struct ImageTile *tile,
- int width,
- int height,
- const float color[4],
- int gen_type,
- int planes,
- bool is_float);
+bool BKE_image_fill_tile(struct Image *ima, struct ImageTile *tile);
typedef enum {
UDIM_TILE_FORMAT_NONE = 0,
@@ -422,9 +418,13 @@ int BKE_image_get_tile_from_pos(struct Image *ima,
void BKE_image_get_tile_uv(const struct Image *ima, const int tile_number, float r_uv[2]);
/**
- * Return the tile_number for the closest UDIM tile.
+ * Return the tile_number for the closest UDIM tile to `co`.
*/
-int BKE_image_find_nearest_tile(const struct Image *image, const float co[2]);
+int BKE_image_find_nearest_tile_with_offset(const struct Image *image,
+ const float co[2],
+ float r_uv_offset[2]) ATTR_NONNULL(2, 3);
+int BKE_image_find_nearest_tile(const struct Image *image, const float co[2])
+ ATTR_NONNULL(2) ATTR_WARN_UNUSED_RESULT;
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_image_format.h b/source/blender/blenkernel/BKE_image_format.h
index 6a03d1d8df5..8f71e1f4648 100644
--- a/source/blender/blenkernel/BKE_image_format.h
+++ b/source/blender/blenkernel/BKE_image_format.h
@@ -69,7 +69,7 @@ char BKE_imtype_valid_depths(char imtype);
* String is from command line `--render-format` argument,
* keep in sync with `creator_args.c` help info.
*/
-char BKE_imtype_from_arg(const char *arg);
+char BKE_imtype_from_arg(const char *imtype_arg);
/* Conversion between ImBuf settings. */
diff --git a/source/blender/blenkernel/BKE_image_save.h b/source/blender/blenkernel/BKE_image_save.h
index 673a7dffb82..e17136174eb 100644
--- a/source/blender/blenkernel/BKE_image_save.h
+++ b/source/blender/blenkernel/BKE_image_save.h
@@ -48,14 +48,14 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts,
struct ImageUser *iuser,
const bool guess_path,
const bool save_as_render);
-void BKE_image_save_options_update(struct ImageSaveOptions *opts, struct Image *ima);
+void BKE_image_save_options_update(struct ImageSaveOptions *opts, const struct Image *ima);
void BKE_image_save_options_free(struct ImageSaveOptions *opts);
bool BKE_image_save(struct ReportList *reports,
struct Main *bmain,
struct Image *ima,
struct ImageUser *iuser,
- struct ImageSaveOptions *opts);
+ const struct ImageSaveOptions *opts);
/* Render saving. */
diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h
index 676ac6fe37b..0fe351c0aa4 100644
--- a/source/blender/blenkernel/BKE_key.h
+++ b/source/blender/blenkernel/BKE_key.h
@@ -22,7 +22,7 @@ extern "C" {
#endif
/**
- * Free (or release) any data used by this shapekey (does not free the key itself).
+ * Free (or release) any data used by this shape-key (does not free the key itself).
*/
void BKE_key_free_data(struct Key *key);
void BKE_key_free_nolib(struct Key *key);
@@ -46,8 +46,11 @@ void key_curve_normal_weights(float t, float data[4], int type);
/**
* Returns key coordinates (+ tilt) when key applied, NULL otherwise.
+ *
+ * \param obdata: if given, also update that geometry with the result of the shape keys evaluation.
*/
-float *BKE_key_evaluate_object_ex(struct Object *ob, int *r_totelem, float *arr, size_t arr_size);
+float *BKE_key_evaluate_object_ex(
+ struct Object *ob, int *r_totelem, float *arr, size_t arr_size, struct ID *obdata);
float *BKE_key_evaluate_object(struct Object *ob, int *r_totelem);
/**
@@ -92,6 +95,9 @@ struct KeyBlock *BKE_keyblock_from_key(struct Key *key, int index);
* Get the appropriate #KeyBlock given a name to search for.
*/
struct KeyBlock *BKE_keyblock_find_name(struct Key *key, const char name[]);
+
+struct KeyBlock *BKE_keyblock_find_uid(struct Key *key, int uid);
+
/**
* \brief copy shape-key attributes, but not key data or name/UID.
*/
diff --git a/source/blender/blenkernel/BKE_lattice.h b/source/blender/blenkernel/BKE_lattice.h
index 9fa59c9e81b..aa4b1c69d24 100644
--- a/source/blender/blenkernel/BKE_lattice.h
+++ b/source/blender/blenkernel/BKE_lattice.h
@@ -27,7 +27,6 @@ void BKE_lattice_resize(struct Lattice *lt, int u, int v, int w, struct Object *
struct Lattice *BKE_lattice_add(struct Main *bmain, const char *name);
void calc_lat_fudu(int flag, int res, float *r_fu, float *r_du);
-bool object_deform_mball(struct Object *ob, struct ListBase *dispbase);
void outside_lattice(struct Lattice *lt);
float (*BKE_lattice_vert_coords_alloc(const struct Lattice *lt, int *r_vert_len))[3];
diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h
index 3e4f2fe154e..8cfc9ef8be9 100644
--- a/source/blender/blenkernel/BKE_layer.h
+++ b/source/blender/blenkernel/BKE_layer.h
@@ -10,6 +10,7 @@
#include "DNA_layer_types.h"
#include "DNA_listBase.h"
+#include "DNA_object_enums.h"
#ifdef __cplusplus
extern "C" {
@@ -77,7 +78,9 @@ void BKE_view_layer_free_ex(struct ViewLayer *view_layer, bool do_id_user);
/**
* Tag all the selected objects of a render-layer.
*/
-void BKE_view_layer_selected_objects_tag(struct ViewLayer *view_layer, int tag);
+void BKE_view_layer_selected_objects_tag(const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ int tag);
/**
* Fallback for when a Scene has no camera to use.
@@ -86,14 +89,14 @@ void BKE_view_layer_selected_objects_tag(struct ViewLayer *view_layer, int tag);
* If rendering you pass the scene active layer, when viewing in the viewport
* you want to get #ViewLayer from context.
*/
-struct Object *BKE_view_layer_camera_find(struct ViewLayer *view_layer);
+struct Object *BKE_view_layer_camera_find(const struct Scene *scene, struct ViewLayer *view_layer);
/**
* Find the #ViewLayer a #LayerCollection belongs to.
*/
struct ViewLayer *BKE_view_layer_find_from_collection(const struct Scene *scene,
struct LayerCollection *lc);
struct Base *BKE_view_layer_base_find(struct ViewLayer *view_layer, struct Object *ob);
-void BKE_view_layer_base_deselect_all(struct ViewLayer *view_layer);
+void BKE_view_layer_base_deselect_all(const struct Scene *scene, struct ViewLayer *view_layer);
void BKE_view_layer_base_select_and_set_active(struct ViewLayer *view_layer, struct Base *selbase);
@@ -160,7 +163,9 @@ void BKE_scene_collection_sync(const struct Scene *scene);
* and on file loaded in case linked data changed or went missing.
*/
void BKE_layer_collection_sync(const struct Scene *scene, struct ViewLayer *view_layer);
-void BKE_layer_collection_local_sync(struct ViewLayer *view_layer, const struct View3D *v3d);
+void BKE_layer_collection_local_sync(const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d);
/**
* Sync the local collection for all the 3D Viewports.
*/
@@ -191,10 +196,12 @@ bool BKE_scene_has_object(struct Scene *scene, struct Object *ob);
* It also select the objects that are in nested collections.
* \note Recursive.
*/
-bool BKE_layer_collection_objects_select(struct ViewLayer *view_layer,
+bool BKE_layer_collection_objects_select(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct LayerCollection *lc,
bool deselect);
-bool BKE_layer_collection_has_selected_objects(struct ViewLayer *view_layer,
+bool BKE_layer_collection_has_selected_objects(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct LayerCollection *lc);
bool BKE_layer_collection_has_layer_collection(struct LayerCollection *lc_parent,
struct LayerCollection *lc_child);
@@ -225,7 +232,8 @@ void BKE_layer_collection_isolate_global(struct Scene *scene,
*
* Same as #BKE_layer_collection_isolate_local but for a viewport
*/
-void BKE_layer_collection_isolate_local(struct ViewLayer *view_layer,
+void BKE_layer_collection_isolate_local(const struct Scene *scene,
+ struct ViewLayer *view_layer,
const struct View3D *v3d,
struct LayerCollection *lc,
bool extend);
@@ -234,7 +242,8 @@ void BKE_layer_collection_isolate_local(struct ViewLayer *view_layer,
* Don't change the collection children enable/disable state,
* but it may change it for the collection itself.
*/
-void BKE_layer_collection_set_visible(struct ViewLayer *view_layer,
+void BKE_layer_collection_set_visible(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct LayerCollection *lc,
bool visible,
bool hierarchy);
@@ -244,8 +253,8 @@ void BKE_layer_collection_set_flag(struct LayerCollection *lc, int flag, bool va
/**
* Applies object's restrict flags on top of flags coming from the collection
- * and stores those in `base->flag`. #BASE_VISIBLE_DEPSGRAPH ignores viewport flags visibility
- * (i.e., restriction and local collection).
+ * and stores those in `base->flag`. #BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT ignores viewport
+ * flags visibility (i.e., restriction and local collection).
*/
void BKE_base_eval_flags(struct Base *base);
@@ -255,7 +264,9 @@ void BKE_layer_eval_view_layer_indexed(struct Depsgraph *depsgraph,
/* .blend file I/O */
-void BKE_view_layer_blend_write(struct BlendWriter *writer, struct ViewLayer *view_layer);
+void BKE_view_layer_blend_write(struct BlendWriter *writer,
+ const struct Scene *scene,
+ struct ViewLayer *view_layer);
void BKE_view_layer_blend_read_data(struct BlendDataReader *reader, struct ViewLayer *view_layer);
void BKE_view_layer_blend_read_lib(struct BlendLibReader *reader,
struct Library *lib,
@@ -351,15 +362,17 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter);
} \
((void)0)
-#define FOREACH_BASE_IN_MODE_BEGIN(_view_layer, _v3d, _object_type, _object_mode, _instance) \
+#define FOREACH_BASE_IN_MODE_BEGIN( \
+ _scene, _view_layer, _v3d, _object_type, _object_mode, _instance) \
{ \
- struct ObjectsInModeIteratorData data_ = { \
- .object_mode = _object_mode, \
- .object_type = _object_type, \
- .view_layer = _view_layer, \
- .v3d = _v3d, \
- .base_active = _view_layer->basact, \
- }; \
+ struct ObjectsInModeIteratorData data_; \
+ memset(&data_, 0, sizeof(data_)); \
+ data_.object_mode = _object_mode; \
+ data_.object_type = _object_type; \
+ data_.view_layer = _view_layer; \
+ data_.v3d = _v3d; \
+ BKE_view_layer_synced_ensure(_scene, _view_layer); \
+ data_.base_active = BKE_view_layer_active_base_get(_view_layer); \
ITER_BEGIN (BKE_view_layer_bases_in_mode_iterator_begin, \
BKE_view_layer_bases_in_mode_iterator_next, \
BKE_view_layer_bases_in_mode_iterator_end, \
@@ -372,21 +385,22 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter);
} \
((void)0)
-#define FOREACH_BASE_IN_EDIT_MODE_BEGIN(_view_layer, _v3d, _instance) \
- FOREACH_BASE_IN_MODE_BEGIN (_view_layer, _v3d, -1, OB_MODE_EDIT, _instance)
+#define FOREACH_BASE_IN_EDIT_MODE_BEGIN(_scene, _view_layer, _v3d, _instance) \
+ FOREACH_BASE_IN_MODE_BEGIN (_scene, _view_layer, _v3d, -1, OB_MODE_EDIT, _instance)
#define FOREACH_BASE_IN_EDIT_MODE_END FOREACH_BASE_IN_MODE_END
-#define FOREACH_OBJECT_IN_MODE_BEGIN(_view_layer, _v3d, _object_type, _object_mode, _instance) \
- FOREACH_BASE_IN_MODE_BEGIN (_view_layer, _v3d, _object_type, _object_mode, _base) { \
+#define FOREACH_OBJECT_IN_MODE_BEGIN( \
+ _scene, _view_layer, _v3d, _object_type, _object_mode, _instance) \
+ FOREACH_BASE_IN_MODE_BEGIN (_scene, _view_layer, _v3d, _object_type, _object_mode, _base) { \
Object *_instance = _base->object;
#define FOREACH_OBJECT_IN_MODE_END \
} \
FOREACH_BASE_IN_MODE_END
-#define FOREACH_OBJECT_IN_EDIT_MODE_BEGIN(_view_layer, _v3d, _instance) \
- FOREACH_BASE_IN_EDIT_MODE_BEGIN (_view_layer, _v3d, _base) { \
+#define FOREACH_OBJECT_IN_EDIT_MODE_BEGIN(_scene, _view_layer, _v3d, _instance) \
+ FOREACH_BASE_IN_EDIT_MODE_BEGIN (_scene, _view_layer, _v3d, _base) { \
Object *_instance = _base->object;
#define FOREACH_OBJECT_IN_EDIT_MODE_END \
@@ -403,11 +417,12 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter);
#define FOREACH_SELECTED_BASE_END ITER_END
-#define FOREACH_VISIBLE_BASE_BEGIN(_view_layer, _v3d, _instance) \
+#define FOREACH_VISIBLE_BASE_BEGIN(_scene, _view_layer, _v3d, _instance) \
{ \
struct ObjectsVisibleIteratorData data_ = {NULL}; \
data_.view_layer = _view_layer; \
data_.v3d = _v3d; \
+ BKE_view_layer_synced_ensure(_scene, _view_layer); \
ITER_BEGIN (BKE_view_layer_visible_bases_iterator_begin, \
BKE_view_layer_visible_bases_iterator_next, \
BKE_view_layer_visible_bases_iterator_end, \
@@ -420,11 +435,13 @@ void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter);
} \
((void)0)
-#define FOREACH_OBJECT_BEGIN(view_layer, _instance) \
+#define FOREACH_OBJECT_BEGIN(scene, view_layer, _instance) \
{ \
Object *_instance; \
Base *_base; \
- for (_base = (Base *)(view_layer)->object_bases.first; _base; _base = _base->next) { \
+ BKE_view_layer_synced_ensure(scene, view_layer); \
+ for (_base = (Base *)BKE_view_layer_object_bases_get(view_layer)->first; _base; \
+ _base = _base->next) { \
_instance = _base->object;
#define FOREACH_OBJECT_END \
@@ -493,7 +510,8 @@ struct Object **BKE_view_layer_array_selected_objects_params(
* Returns NULL with it finds multiple other selected objects
* as behavior in this case would be random from the user perspective.
*/
-struct Object *BKE_view_layer_non_active_selected_object(struct ViewLayer *view_layer,
+struct Object *BKE_view_layer_non_active_selected_object(const struct Scene *scene,
+ struct ViewLayer *view_layer,
const struct View3D *v3d);
#define BKE_view_layer_array_selected_objects(view_layer, v3d, r_len, ...) \
@@ -509,57 +527,63 @@ struct ObjectsInModeParams {
};
struct Base **BKE_view_layer_array_from_bases_in_mode_params(
+ const struct Scene *scene,
struct ViewLayer *view_layer,
const struct View3D *v3d,
uint *r_len,
const struct ObjectsInModeParams *params);
struct Object **BKE_view_layer_array_from_objects_in_mode_params(
+ const struct Scene *scene,
struct ViewLayer *view_layer,
const struct View3D *v3d,
uint *len,
const struct ObjectsInModeParams *params);
-#define BKE_view_layer_array_from_objects_in_mode(view_layer, v3d, r_len, ...) \
- BKE_view_layer_array_from_objects_in_mode_params( \
- view_layer, v3d, r_len, &(const struct ObjectsInModeParams)__VA_ARGS__)
-
-#define BKE_view_layer_array_from_bases_in_mode(view_layer, v3d, r_len, ...) \
- BKE_view_layer_array_from_bases_in_mode_params( \
- view_layer, v3d, r_len, &(const struct ObjectsInModeParams)__VA_ARGS__)
-
bool BKE_view_layer_filter_edit_mesh_has_uvs(const struct Object *ob, void *user_data);
bool BKE_view_layer_filter_edit_mesh_has_edges(const struct Object *ob, void *user_data);
-/* Utility macros that wrap common args (add more as needed). */
-
-#define BKE_view_layer_array_from_objects_in_edit_mode(view_layer, v3d, r_len) \
- BKE_view_layer_array_from_objects_in_mode(view_layer, v3d, r_len, {.object_mode = OB_MODE_EDIT})
-
-#define BKE_view_layer_array_from_bases_in_edit_mode(view_layer, v3d, r_len) \
- BKE_view_layer_array_from_bases_in_mode(view_layer, v3d, r_len, {.object_mode = OB_MODE_EDIT})
+/* Utility functions that wrap common arguments (add more as needed). */
+
+struct Object **BKE_view_layer_array_from_objects_in_edit_mode(const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len);
+struct Base **BKE_view_layer_array_from_bases_in_edit_mode(const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len);
+struct Object **BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
+ const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len);
-#define BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, v3d, r_len) \
- BKE_view_layer_array_from_objects_in_mode( \
- view_layer, v3d, r_len, {.object_mode = OB_MODE_EDIT, .no_dup_data = true})
+struct Base **BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
+ const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len);
+struct Object **BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len);
+struct Object **BKE_view_layer_array_from_objects_in_mode_unique_data(const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ const struct View3D *v3d,
+ uint *r_len,
+ eObjectMode mode);
+struct Object *BKE_view_layer_active_object_get(const struct ViewLayer *view_layer);
+struct Object *BKE_view_layer_edit_object_get(const struct ViewLayer *view_layer);
-#define BKE_view_layer_array_from_bases_in_edit_mode_unique_data(view_layer, v3d, r_len) \
- BKE_view_layer_array_from_bases_in_mode( \
- view_layer, v3d, r_len, {.object_mode = OB_MODE_EDIT, .no_dup_data = true})
+struct ListBase *BKE_view_layer_object_bases_get(struct ViewLayer *view_layer);
+struct Base *BKE_view_layer_active_base_get(struct ViewLayer *view_layer);
-#define BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( \
- view_layer, v3d, r_len) \
- BKE_view_layer_array_from_objects_in_mode( \
- view_layer, \
- v3d, \
- r_len, \
- {.object_mode = OB_MODE_EDIT, \
- .no_dup_data = true, \
- .filter_fn = BKE_view_layer_filter_edit_mesh_has_uvs})
+struct LayerCollection *BKE_view_layer_active_collection_get(struct ViewLayer *view_layer);
-#define BKE_view_layer_array_from_objects_in_mode_unique_data(view_layer, v3d, r_len, mode) \
- BKE_view_layer_array_from_objects_in_mode( \
- view_layer, v3d, r_len, {.object_mode = mode, .no_dup_data = true})
+void BKE_view_layer_need_resync_tag(struct ViewLayer *view_layer);
+void BKE_view_layer_synced_ensure(const struct Scene *scene, struct ViewLayer *view_layer);
struct ViewLayerAOV *BKE_view_layer_add_aov(struct ViewLayer *view_layer);
void BKE_view_layer_remove_aov(struct ViewLayer *view_layer, struct ViewLayerAOV *aov);
@@ -590,7 +614,8 @@ void BKE_view_layer_set_active_lightgroup(struct ViewLayer *view_layer,
struct ViewLayerLightgroup *lightgroup);
struct ViewLayer *BKE_view_layer_find_with_lightgroup(
struct Scene *scene, struct ViewLayerLightgroup *view_layer_lightgroup);
-void BKE_view_layer_rename_lightgroup(ViewLayer *view_layer,
+void BKE_view_layer_rename_lightgroup(struct Scene *scene,
+ ViewLayer *view_layer,
ViewLayerLightgroup *lightgroup,
const char *name);
diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h
index beac608a138..aa3bdb502f8 100644
--- a/source/blender/blenkernel/BKE_lib_id.h
+++ b/source/blender/blenkernel/BKE_lib_id.h
@@ -77,19 +77,19 @@ void BKE_libblock_runtime_reset_remapping_status(struct ID *id) ATTR_NONNULL(1);
/* *** ID's session_uuid management. *** */
/**
- * When an ID's uuid is of that value, it is unset/invalid (e.g. for runtime IDs, etc.).
+ * When an ID's UUID is of that value, it is unset/invalid (e.g. for runtime IDs, etc.).
*/
#define MAIN_ID_SESSION_UUID_UNSET 0
/**
- * Generate a session-wise uuid for the given \a id.
+ * Generate a session-wise UUID for the given \a id.
*
* \note "session-wise" here means while editing a given .blend file. Once a new .blend file is
- * loaded or created, undo history is cleared/reset, and so is the uuid counter.
+ * loaded or created, undo history is cleared/reset, and so is the UUID counter.
*/
void BKE_lib_libblock_session_uuid_ensure(struct ID *id);
/**
- * Re-generate a new session-wise uuid for the given \a id.
+ * Re-generate a new session-wise UUID for the given \a id.
*
* \warning This has a few very specific use-cases, no other usage is expected currently:
* - To handle UI-related data-blocks that are kept across new file reading, when we do keep
@@ -117,14 +117,14 @@ void *BKE_id_new_nomain(short type, const char *name);
*/
enum {
/* *** Generic options (should be handled by all ID types copying, ID creation, etc.). *** */
- /** Create datablock outside of any main database -
+ /** Create data-block outside of any main database -
* similar to 'localize' functions of materials etc. */
LIB_ID_CREATE_NO_MAIN = 1 << 0,
- /** Do not affect user refcount of datablocks used by new one
- * (which also gets zero usercount then).
+ /** Do not affect user refcount of data-blocks used by new one
+ * (which also gets zero user-count then).
* Implies LIB_ID_CREATE_NO_MAIN. */
LIB_ID_CREATE_NO_USER_REFCOUNT = 1 << 1,
- /** Assume given 'newid' already points to allocated memory for whole datablock
+ /** Assume given 'newid' already points to allocated memory for whole data-block
* (ID + data) - USE WITH CAUTION!
* Implies LIB_ID_CREATE_NO_MAIN. */
LIB_ID_CREATE_NO_ALLOCATE = 1 << 2,
@@ -150,7 +150,7 @@ enum {
LIB_ID_COPY_NO_PREVIEW = 1 << 17,
/** Copy runtime data caches. */
LIB_ID_COPY_CACHES = 1 << 18,
- /** Don't copy id->adt, used by ID data-block localization routines. */
+ /** Don't copy `id->adt`, used by ID data-block localization routines. */
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,
@@ -239,12 +239,12 @@ enum {
/** Do not try to remove freed ID from given Main (passed Main may be NULL). */
LIB_ID_FREE_NO_MAIN = 1 << 0,
/**
- * Do not affect user refcount of datablocks used by freed one.
+ * Do not affect user refcount of data-blocks used by freed one.
* Implies LIB_ID_FREE_NO_MAIN.
*/
LIB_ID_FREE_NO_USER_REFCOUNT = 1 << 1,
/**
- * Assume freed ID datablock memory is managed elsewhere, do not free it
+ * Assume freed ID data-block memory is managed elsewhere, do not free it
* (still calls relevant ID type's freeing function though) - USE WITH CAUTION!
* Implies LIB_ID_FREE_NO_MAIN.
*/
@@ -254,6 +254,8 @@ enum {
LIB_ID_FREE_NO_DEG_TAG = 1 << 8,
/** Do not attempt to remove freed ID from UI data/notifiers/... */
LIB_ID_FREE_NO_UI_USER = 1 << 9,
+ /** Do not remove freed ID's name from a potential runtime name-map. */
+ LIB_ID_FREE_NO_NAMEMAP_REMOVE = 1 << 10,
};
void BKE_libblock_free_datablock(struct ID *id, int flag) ATTR_NONNULL();
@@ -281,7 +283,7 @@ void BKE_libblock_free_data_py(struct ID *id);
* \param idv: Pointer to ID to be freed.
* \param flag: Set of \a LIB_ID_FREE_... flags controlling/overriding usual freeing process,
* 0 to get default safe behavior.
- * \param use_flag_from_idtag: Still use freeing info flags from given #ID datablock,
+ * \param use_flag_from_idtag: Still use freeing info flags from given #ID data-block,
* even if some overriding ones are passed in \a flag parameter.
*/
void BKE_id_free_ex(struct Main *bmain, void *idv, int flag, bool use_flag_from_idtag);
@@ -298,7 +300,7 @@ void BKE_id_free(struct Main *bmain, void *idv);
/**
* Not really a freeing function by itself,
- * it decrements usercount of given id, and only frees it if it reaches 0.
+ * it decrements user-count of given id, and only frees it if it reaches 0.
*/
void BKE_id_free_us(struct Main *bmain, void *idv) ATTR_NONNULL();
@@ -314,12 +316,12 @@ void BKE_id_delete(struct Main *bmain, void *idv) ATTR_NONNULL();
*
* \warning Considered experimental for now, seems to be working OK but this is
* risky code in a complicated area.
- * \return Number of deleted datablocks.
+ * \return Number of deleted data-blocks.
*/
size_t BKE_id_multi_tagged_delete(struct Main *bmain) ATTR_NONNULL();
/**
- * Add a 'NO_MAIN' data-block to given main (also sets usercounts of its IDs if needed).
+ * Add a 'NO_MAIN' data-block to given main (also sets user-counts of its IDs if needed).
*/
void BKE_libblock_management_main_add(struct Main *bmain, void *idv);
/** Remove a data-block from given main (set it to 'NO_MAIN' status). */
@@ -398,13 +400,10 @@ bool id_single_user(struct bContext *C,
struct ID *id,
struct PointerRNA *ptr,
struct PropertyRNA *prop);
+
+/** Test whether given `id` can be copied or not. */
bool BKE_id_copy_is_allowed(const struct ID *id);
/**
- * Invokes the appropriate copy method for the block and returns the result in
- * #ID.newid, unless test. Returns true if the block can be copied.
- */
-struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id);
-/**
* Generic entry point for copying a data-block (new API).
*
* \note Copy is generally only affecting the given data-block
@@ -428,8 +427,26 @@ struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id);
*/
struct ID *BKE_id_copy_ex(struct Main *bmain, const struct ID *id, struct ID **r_newid, int flag);
/**
- * Invokes the appropriate copy method for the block and returns the result in
- * newid, unless test. Returns true if the block can be copied.
+ * Invoke the appropriate copy method for the block and return the new id as result.
+ *
+ * See #BKE_id_copy_ex for details.
+ */
+struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id);
+
+/**
+ * Invoke the appropriate copy method for the block and return the new id as result.
+ *
+ * Unlike #BKE_id_copy, it does set the #ID.newid pointer of the given `id` to the copied one.
+ *
+ * It is designed as a basic common helper for the higher-level 'duplicate' operations (aka 'deep
+ * copy' of data-blocks and some of their dependency ones), see e.g. #BKE_object_duplicate.
+ *
+ * Currently, it only handles the given ID, and their shape keys and actions if any, according to
+ * the given `duplicate_flags`.
+ *
+ * \param duplicate_flags: is of type #eDupli_ID_Flags, see #UserDef.dupflag. Currently only
+ * `USER_DUP_LINKED_ID` and `USER_DUP_ACT` have an effect here.
+ * \param copy_flags: flags passed to #BKE_id_copy_ex.
*/
struct ID *BKE_id_copy_for_duplicate(struct Main *bmain,
struct ID *id,
@@ -437,6 +454,13 @@ struct ID *BKE_id_copy_for_duplicate(struct Main *bmain,
int copy_flags);
/**
+ * Special version of #BKE_id_copy which is safe from using evaluated id as source with a copy
+ * result appearing in the main database.
+ * Takes care of the referenced data-blocks consistency.
+ */
+struct ID *BKE_id_copy_for_use_in_bmain(struct Main *bmain, const struct ID *id);
+
+/**
* Does a mere memory swap over the whole IDs data (including type-specific memory).
* \note Most internal ID data itself is not swapped (only IDProperties are).
*
@@ -478,10 +502,12 @@ void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id, int flags);
*
* \return true if a new name had to be created.
*/
-bool BKE_id_new_name_validate(struct ListBase *lb,
+bool BKE_id_new_name_validate(struct Main *bmain,
+ struct ListBase *lb,
struct ID *id,
const char *name,
- bool do_linked_data) ATTR_NONNULL(1, 2);
+ bool do_linked_data) ATTR_NONNULL(1, 2, 3);
+
/**
* Pull an ID out of a library (make it local). Only call this for IDs that
* don't have other library users.
@@ -526,7 +552,7 @@ void BKE_main_lib_objects_recalc_all(struct Main *bmain);
/**
* Only for repairing files via versioning, avoid for general use.
*/
-void BKE_main_id_repair_duplicate_names_listbase(struct ListBase *lb);
+void BKE_main_id_repair_duplicate_names_listbase(struct Main *bmain, struct ListBase *lb);
#define MAX_ID_FULL_NAME (64 + 64 + 3 + 1) /* 64 is MAX_ID_NAME - 2 */
#define MAX_ID_FULL_NAME_UI (MAX_ID_FULL_NAME + 3) /* Adds 'keycode' two letters at beginning. */
@@ -594,6 +620,13 @@ bool BKE_id_is_in_global_main(struct ID *id);
bool BKE_id_can_be_asset(const struct ID *id);
+/**
+ * Return the owner ID of the given `id`, if any.
+ *
+ * \note This will only return non-NULL for embedded IDs (master collections etc.), and shape-keys.
+ */
+struct ID *BKE_id_owner_get(struct ID *id);
+
/** Check if that ID can be considered as editable from a high-level (editor) perspective.
*
* NOTE: This used to be done with a check on whether ID was linked or not, but now with system
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index b2162e651fd..8542c02fab5 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -60,6 +60,21 @@ void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, bool do_
void BKE_lib_override_library_free(struct IDOverrideLibrary **override, bool do_id_user);
/**
+ * Return the actual #IDOverrideLibrary data 'controlling' the given `id`, and the actual ID owning
+ * it.
+ *
+ * \note This is especially useful when `id` is a non-real override (e.g. embedded ID like a master
+ * collection or root node tree, or a shape key).
+ *
+ * \param owner_id_hint: If not NULL, a potential owner for the given override-embedded `id`.
+ * \param r_owner_id: If given, will be set with the actual ID owning the return liboverride data.
+ */
+IDOverrideLibrary *BKE_lib_override_library_get(struct Main *bmain,
+ struct ID *id,
+ struct ID *owner_id_hint,
+ struct ID **r_owner_id);
+
+/**
* Check if given ID has some override rules that actually indicate the user edited it.
*/
bool BKE_lib_override_library_is_user_edited(const struct ID *id);
diff --git a/source/blender/blenkernel/BKE_lib_principle_properties.h b/source/blender/blenkernel/BKE_lib_principle_properties.h
deleted file mode 100644
index 42177204efb..00000000000
--- a/source/blender/blenkernel/BKE_lib_principle_properties.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2022 Blender Foundation. All rights reserved. */
-
-#pragma once
-
-/** \file
- * \ingroup bke
- *
- * API to manage principle properties in data-blocks.
- *
- * Principle properties are properties that are defined as the ones most user will need to
- * edit when using this data-block.
- *
- * They current main usage in is library overrides.
- *
- * \note `BKE_lib_` files are for operations over data-blocks themselves, although they might
- * alter Main as well (when creating/renaming/deleting an ID e.g.).
- *
- * \section Function Names
- *
- * - `BKE_lib_principleprop_` should be used for function affecting a single ID.
- * - `BKE_lib_principleprop_main_` should be used for function affecting the whole collection
- * of IDs in a given Main data-base.
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct ID;
-struct IDPrincipleProperties;
-struct IDPrincipleProperty;
-struct PointerRNA;
-struct PropertyRNA;
-struct ReportList;
-
-/**
- * Initialize empty list of principle properties for \a id.
- */
-struct IDPrincipleProperties *BKE_lib_principleprop_init(struct ID *id);
-#if 0
-/**
- * Shallow or deep copy of a whole principle properties from \a src_id to \a dst_id.
- */
-void BKE_lib_principleprop_copy(struct ID *dst_id, const struct ID *src_id, bool do_full_copy);
-#endif
-/**
- * Clear any principle properties data from given \a override.
- */
-void BKE_lib_principleprop_clear(struct IDPrincipleProperties *principle_props, bool do_id_user);
-/**
- * Free given \a principle_props.
- */
-void BKE_lib_principleprop_free(struct IDPrincipleProperties **principle_props, bool do_id_user);
-
-/**
- * Find principle property from given RNA path, if it exists.
- */
-struct IDPrincipleProperty *BKE_lib_principleprop_find(
- struct IDPrincipleProperties *principle_props, const char *rna_path);
-/**
- * Find principle property from given RNA path, or create it if it does not exist.
- */
-struct IDPrincipleProperty *BKE_lib_principleprop_get(
- struct IDPrincipleProperties *principle_props, const char *rna_path, bool *r_created);
-/**
- * Remove and free given \a principle_prop from given ID \a principle_props.
- */
-void BKE_lib_principleprop_delete(struct IDPrincipleProperties *principle_props,
- struct IDPrincipleProperty *principle_prop);
-/**
- * Get the RNA-property matching the \a principle_prop principle property. Used for UI to query
- * additional data about the principle property (e.g. UI name).
- *
- * \param idpoin: RNA Pointer of the ID.
- * \param principle_prop: The principle property to find the matching RNA property for.
- */
-bool BKE_lib_principleprop_rna_property_find(struct PointerRNA *idpoin,
- const struct IDPrincipleProperty *principle_prop,
- struct PointerRNA *r_principle_poin,
- struct PropertyRNA **r_principle_prop);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h
index 48cffcf8d2c..a70d128cd95 100644
--- a/source/blender/blenkernel/BKE_lib_query.h
+++ b/source/blender/blenkernel/BKE_lib_query.h
@@ -36,7 +36,6 @@ enum {
/**
* Indicates whether this is direct (i.e. by local data) or indirect (i.e. by linked data) usage.
- * \note Object proxies are half-local, half-linked...
*/
IDWALK_CB_INDIRECT_USAGE = (1 << 2),
diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h
index 37b626fb4da..a17ef8c7c5d 100644
--- a/source/blender/blenkernel/BKE_lib_remap.h
+++ b/source/blender/blenkernel/BKE_lib_remap.h
@@ -180,12 +180,12 @@ typedef enum IDRemapperApplyResult {
typedef enum IDRemapperApplyOptions {
/**
- * Update the user count of the old and new ID datablock.
+ * Update the user count of the old and new ID data-block.
*
* For remapping the old ID users will be decremented and the new ID users will be
* incremented. When un-assigning the old ID users will be decremented.
*
- * NOTE: Currently unused by main remapping code, since usercount is handled by
+ * NOTE: Currently unused by main remapping code, since user-count is handled by
* `foreach_libblock_remap_callback_apply` there, depending on whether the remapped pointer does
* use it or not. Need for rare cases in UI handling though (see e.g. `image_id_remap` in
* `space_image.c`).
@@ -193,14 +193,14 @@ typedef enum IDRemapperApplyOptions {
ID_REMAP_APPLY_UPDATE_REFCOUNT = (1 << 0),
/**
- * Make sure that the new ID datablock will have a 'real' user.
+ * Make sure that the new ID data-block will have a 'real' user.
*
* NOTE: See Note for #ID_REMAP_APPLY_UPDATE_REFCOUNT above.
*/
ID_REMAP_APPLY_ENSURE_REAL = (1 << 1),
/**
- * Unassign in stead of remap when the new ID datablock would become id_self.
+ * Unassign in stead of remap when the new ID data-block would become id_self.
*
* To use this option 'BKE_id_remapper_apply_ex' must be used with a not-null id_self parameter.
*/
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 2c444f42c46..0048ad4dde5 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -36,6 +36,7 @@ struct IDNameLib_Map;
struct ImBuf;
struct Library;
struct MainLock;
+struct UniqueName_Map;
/* Blender thumbnail, as written on file (width, height, and data as char RGBA). */
/* We pack pixel data after that struct. */
@@ -112,28 +113,30 @@ typedef struct Main {
char filepath[1024]; /* 1024 = FILE_MAX */
short versionfile, subversionfile; /* see BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION */
short minversionfile, minsubversionfile;
- uint64_t build_commit_timestamp; /* commit's timestamp from buildinfo */
- char build_hash[16]; /* hash from buildinfo */
+ /** Commit timestamp from `buildinfo`. */
+ uint64_t build_commit_timestamp;
+ /** Commit Hash from `buildinfo`. */
+ char build_hash[16];
/** Indicate the #Main.filepath (file) is the recovered one. */
- char recovered;
+ bool recovered;
/** All current ID's exist in the last memfile undo step. */
- char is_memfile_undo_written;
+ bool is_memfile_undo_written;
/**
* An ID needs its data to be flushed back.
* use "needs_flush_to_id" in edit data to flag data which needs updating.
*/
- char is_memfile_undo_flush_needed;
+ bool is_memfile_undo_flush_needed;
/**
* Indicates that next memfile undo step should not allow reusing old bmain when re-read, but
* instead do a complete full re-read/update from stored memfile.
*/
- char use_memfile_full_barrier;
+ bool use_memfile_full_barrier;
/**
* When linking, disallow creation of new data-blocks.
* Make sure we don't do this by accident, see T76738.
*/
- char is_locked_for_linking;
+ bool is_locked_for_linking;
BlendThumbnail *blen_thumb;
@@ -193,6 +196,9 @@ typedef struct Main {
/* IDMap of IDs. Currently used when reading (expanding) libraries. */
struct IDNameLib_Map *id_map;
+ /* Used for efficient calculations of unique names. */
+ struct UniqueName_Map *name_map;
+
struct MainLock *lock;
} Main;
diff --git a/source/blender/blenkernel/BKE_main_namemap.h b/source/blender/blenkernel/BKE_main_namemap.h
new file mode 100644
index 00000000000..d6f184b4b30
--- /dev/null
+++ b/source/blender/blenkernel/BKE_main_namemap.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#pragma once
+
+/** \file
+ * \ingroup bke
+ *
+ * API to ensure name uniqueness.
+ *
+ * Main database contains the UniqueName_Map which is a cache that tracks names, base
+ * names and their suffixes currently in use. So that whenever a new name has to be
+ * assigned or validated, it can quickly ensure uniqueness and adjust the name in case
+ * of collisions.
+ *
+ * \section Function Names
+ *
+ * - `BKE_main_namemap_` Should be used for functions in this file.
+ */
+
+#include "BLI_compiler_attrs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ID;
+struct Main;
+struct UniqueName_Map;
+
+struct UniqueName_Map *BKE_main_namemap_create(void) ATTR_WARN_UNUSED_RESULT;
+void BKE_main_namemap_destroy(struct UniqueName_Map **r_name_map) ATTR_NONNULL();
+
+/**
+ * Ensures the given name is unique within the given ID type.
+ *
+ * In case of name collisions, the name will be adjusted to be unique.
+ *
+ * \return true if the name had to be adjusted for uniqueness.
+ */
+bool BKE_main_namemap_get_name(struct Main *bmain, struct ID *id, char *name) ATTR_NONNULL();
+
+/**
+ * Remove a given name from usage.
+ *
+ * Call this whenever deleting or renaming an object.
+ */
+void BKE_main_namemap_remove_name(struct Main *bmain, struct ID *id, const char *name)
+ ATTR_NONNULL();
+
+/**
+ * Check that all ID names in given `bmain` are unique (per ID type and library), and that existing
+ * name maps are consistent with existing relevant IDs.
+ *
+ * This is typically called within an assert, or in tests.
+ */
+bool BKE_main_namemap_validate(struct Main *bmain) ATTR_NONNULL();
+
+/** Same as #BKE_main_namemap_validate, but also fixes any issue by re-generating all name maps,
+ * and ensuring again all ID names are unique.
+ *
+ * This is typically only used in `do_versions` code to fix broken files.
+ */
+bool BKE_main_namemap_validate_and_fix(struct Main *bmain) ATTR_NONNULL();
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenkernel/BKE_mball.h b/source/blender/blenkernel/BKE_mball.h
index a23d010b51f..5ffaa13d9d2 100644
--- a/source/blender/blenkernel/BKE_mball.h
+++ b/source/blender/blenkernel/BKE_mball.h
@@ -55,18 +55,9 @@ bool BKE_mball_is_basis(const struct Object *ob);
struct Object *BKE_mball_basis_find(struct Scene *scene, struct Object *ob);
/**
- * Compute bounding box of all meta-elements / meta-ball.
- *
- * Bounding box is computed from polygonized surface. \a ob is
- * basic meta-balls (with name `Meta` for example). All other meta-ball objects
- * (with names `Meta.001`, `Meta.002`, etc) are included in this bounding-box.
- */
-void BKE_mball_texspace_calc(struct Object *ob);
-/**
* Return or compute bounding-box for given meta-ball object.
*/
struct BoundBox *BKE_mball_boundbox_get(struct Object *ob);
-float *BKE_mball_make_orco(struct Object *ob, struct ListBase *dispbase);
/**
* Copy some properties from a meta-ball obdata to all other meta-ball obdata belonging to the same
@@ -97,7 +88,7 @@ void BKE_mball_translate(struct MetaBall *mb, const float offset[3]);
*/
struct MetaElem *BKE_mball_element_add(struct MetaBall *mb, int type);
-/* *** select funcs *** */
+/* *** Select functions *** */
int BKE_mball_select_count(const struct MetaBall *mb);
int BKE_mball_select_count_multi(struct Base **bases, int bases_len);
@@ -110,18 +101,7 @@ bool BKE_mball_select_swap_multi_ex(struct Base **bases, int bases_len);
/* **** Depsgraph evaluation **** */
-struct Depsgraph;
-
-/* Draw Cache */
-
-enum {
- BKE_MBALL_BATCH_DIRTY_ALL = 0,
-};
-void BKE_mball_batch_cache_dirty_tag(struct MetaBall *mb, int mode);
-void BKE_mball_batch_cache_free(struct MetaBall *mb);
-
-extern void (*BKE_mball_batch_cache_dirty_tag_cb)(struct MetaBall *mb, int mode);
-extern void (*BKE_mball_batch_cache_free_cb)(struct MetaBall *mb);
+void BKE_mball_data_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_mball_tessellate.h b/source/blender/blenkernel/BKE_mball_tessellate.h
index 2dc16dc64d6..0840c51bb4d 100644
--- a/source/blender/blenkernel/BKE_mball_tessellate.h
+++ b/source/blender/blenkernel/BKE_mball_tessellate.h
@@ -12,11 +12,11 @@ extern "C" {
struct Depsgraph;
struct Object;
struct Scene;
+struct Mesh;
-void BKE_mball_polygonize(struct Depsgraph *depsgraph,
- struct Scene *scene,
- struct Object *ob,
- struct ListBase *dispbase);
+struct Mesh *BKE_mball_polygonize(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct Object *ob);
void BKE_mball_cubeTable_free(void);
diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h
index 366083fee7f..ef57c9a2e0e 100644
--- a/source/blender/blenkernel/BKE_mesh.h
+++ b/source/blender/blenkernel/BKE_mesh.h
@@ -6,10 +6,16 @@
* \ingroup bke
*/
-#include "BKE_mesh_types.h"
#include "BLI_compiler_attrs.h"
+#include "BLI_compiler_compat.h"
#include "BLI_utildefines.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_customdata.h"
+#include "BKE_mesh_types.h"
+
struct BLI_Stack;
struct BMesh;
struct BMeshCreateParams;
@@ -85,10 +91,18 @@ struct Mesh *BKE_mesh_from_bmesh_for_eval_nomain(struct BMesh *bm,
* Add original index (#CD_ORIGINDEX) layers if they don't already exist. This is meant to be used
* when creating an evaluated mesh from an original edit mode mesh, to allow mapping from the
* evaluated vertices to the originals.
+ *
+ * The mesh is expected to of a `ME_WRAPPER_TYPE_MDATA` wrapper type. This is asserted.
*/
void BKE_mesh_ensure_default_orig_index_customdata(struct Mesh *mesh);
/**
+ * Same as #BKE_mesh_ensure_default_orig_index_customdata but does not perform any checks: they
+ * must be done by the caller.
+ */
+void BKE_mesh_ensure_default_orig_index_customdata_no_check(struct Mesh *mesh);
+
+/**
* Find the index of the loop in 'poly' which references vertex,
* returns -1 if not found
*/
@@ -137,7 +151,6 @@ void BKE_mesh_copy_parameters_for_eval(struct Mesh *me_dst, const struct Mesh *m
* when a new mesh is based on an existing mesh.
*/
void BKE_mesh_copy_parameters(struct Mesh *me_dst, const struct Mesh *me_src);
-void BKE_mesh_update_customdata_pointers(struct Mesh *me, bool do_ensure_tess_cd);
void BKE_mesh_ensure_skin_customdata(struct Mesh *me);
struct Mesh *BKE_mesh_new_nomain(
@@ -185,7 +198,6 @@ void BKE_mesh_orco_ensure(struct Object *ob, struct Mesh *mesh);
struct Mesh *BKE_mesh_from_object(struct Object *ob);
void BKE_mesh_assign_object(struct Main *bmain, struct Object *ob, struct Mesh *me);
-void BKE_mesh_from_metaball(struct ListBase *lb, struct Mesh *me);
void BKE_mesh_to_curve_nurblist(const struct Mesh *me,
struct ListBase *nurblist,
int edge_users_test);
@@ -280,13 +292,10 @@ struct Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
bool build_shapekey_layers);
/**
- * Copies a nomain-Mesh into an existing Mesh.
+ * Move data from a mesh outside of the main data-base into a mesh in the data-base.
+ * Takes ownership of the source mesh.
*/
-void BKE_mesh_nomain_to_mesh(struct Mesh *mesh_src,
- struct Mesh *mesh_dst,
- struct Object *ob,
- const struct CustomData_MeshMasks *mask,
- bool take_ownership);
+void BKE_mesh_nomain_to_mesh(struct Mesh *mesh_src, struct Mesh *mesh_dst, struct Object *ob);
void BKE_mesh_nomain_to_meshkey(struct Mesh *mesh_src, struct Mesh *mesh_dst, struct KeyBlock *kb);
/* vertex level transformations & checks (no derived mesh) */
@@ -322,7 +331,7 @@ void BKE_mesh_vert_coords_apply_with_mat4(struct Mesh *mesh,
const float mat[4][4]);
void BKE_mesh_vert_coords_apply(struct Mesh *mesh, const float (*vert_coords)[3]);
-/* *** mesh_tessellate.c *** */
+/* *** mesh_tessellate.cc *** */
/**
* Calculate tessellation into #MLoopTri which exist only for this purpose.
@@ -507,9 +516,9 @@ void BKE_edges_sharp_from_angle_set(const struct MVert *mverts,
int numVerts,
struct MEdge *medges,
int numEdges,
- struct MLoop *mloops,
+ const struct MLoop *mloops,
int numLoops,
- struct MPoly *mpolys,
+ const struct MPoly *mpolys,
const float (*polynors)[3],
int numPolys,
float split_angle);
@@ -627,12 +636,12 @@ void BKE_lnor_space_custom_normal_to_data(MLoopNorSpace *lnor_space,
void BKE_mesh_normals_loop_split(const struct MVert *mverts,
const float (*vert_normals)[3],
int numVerts,
- struct MEdge *medges,
+ const struct MEdge *medges,
int numEdges,
- struct MLoop *mloops,
+ const struct MLoop *mloops,
float (*r_loopnors)[3],
int numLoops,
- struct MPoly *mpolys,
+ const struct MPoly *mpolys,
const float (*polynors)[3],
int numPolys,
bool use_split_normals,
@@ -646,25 +655,25 @@ void BKE_mesh_normals_loop_custom_set(const struct MVert *mverts,
int numVerts,
struct MEdge *medges,
int numEdges,
- struct MLoop *mloops,
+ const struct MLoop *mloops,
float (*r_custom_loopnors)[3],
int numLoops,
- struct MPoly *mpolys,
+ const struct MPoly *mpolys,
const float (*polynors)[3],
int numPolys,
short (*r_clnors_data)[2]);
-void BKE_mesh_normals_loop_custom_from_vertices_set(const struct MVert *mverts,
- const float (*vert_normals)[3],
- float (*r_custom_vertnors)[3],
- int numVerts,
- struct MEdge *medges,
- int numEdges,
- struct MLoop *mloops,
- int numLoops,
- struct MPoly *mpolys,
- const float (*polynors)[3],
- int numPolys,
- short (*r_clnors_data)[2]);
+void BKE_mesh_normals_loop_custom_from_verts_set(const struct MVert *mverts,
+ const float (*vert_normals)[3],
+ float (*r_custom_vertnors)[3],
+ int numVerts,
+ struct MEdge *medges,
+ int numEdges,
+ const struct MLoop *mloops,
+ int numLoops,
+ const struct MPoly *mpolys,
+ const float (*polynors)[3],
+ int numPolys,
+ short (*r_clnors_data)[2]);
/**
* Computes average per-vertex normals from given custom loop normals.
@@ -705,12 +714,12 @@ void BKE_mesh_calc_normals_split_ex(struct Mesh *mesh,
void BKE_mesh_set_custom_normals(struct Mesh *mesh, float (*r_custom_loopnors)[3]);
/**
* Higher level functions hiding most of the code needed around call to
- * #BKE_mesh_normals_loop_custom_from_vertices_set().
+ * #BKE_mesh_normals_loop_custom_from_verts_set().
*
* \param r_custom_vertnors: is not const, since code will replace zero_v3 normals there
* with automatically computed vectors.
*/
-void BKE_mesh_set_custom_normals_from_vertices(struct Mesh *mesh, float (*r_custom_vertnors)[3]);
+void BKE_mesh_set_custom_normals_from_verts(struct Mesh *mesh, float (*r_custom_vertnors)[3]);
/* *** mesh_evaluate.cc *** */
@@ -786,22 +795,24 @@ void BKE_mesh_mdisp_flip(struct MDisps *md, bool use_loop_mdisp_flip);
* \param mloop: the full loops array.
* \param ldata: the loops custom data.
*/
-void BKE_mesh_polygon_flip_ex(struct MPoly *mpoly,
+void BKE_mesh_polygon_flip_ex(const struct MPoly *mpoly,
struct MLoop *mloop,
struct CustomData *ldata,
float (*lnors)[3],
struct MDisps *mdisp,
bool use_loop_mdisp_flip);
-void BKE_mesh_polygon_flip(struct MPoly *mpoly, struct MLoop *mloop, struct CustomData *ldata);
+void BKE_mesh_polygon_flip(const struct MPoly *mpoly,
+ struct MLoop *mloop,
+ struct CustomData *ldata);
/**
* Flip (invert winding of) all polygons (used to inverse their normals).
*
* \note Invalidates tessellation, caller must handle that.
*/
-void BKE_mesh_polygons_flip(struct MPoly *mpoly,
- struct MLoop *mloop,
- struct CustomData *ldata,
- int totpoly);
+void BKE_mesh_polys_flip(const struct MPoly *mpoly,
+ struct MLoop *mloop,
+ struct CustomData *ldata,
+ int totpoly);
/* Merge verts. */
/* Enum for merge_mode of #BKE_mesh_merge_verts.
@@ -845,7 +856,7 @@ struct Mesh *BKE_mesh_merge_verts(struct Mesh *mesh,
int merge_mode);
/**
- * Account for custom-data such as UV's becoming detached because of of imprecision
+ * Account for custom-data such as UV's becoming detached because of imprecision
* in custom-data interpolation.
* Without running this operation subdivision surface can cause UV's to be disconnected,
* see: T81065.
@@ -857,19 +868,7 @@ void BKE_mesh_merge_customdata_for_apply_modifier(struct Mesh *me);
/**
* Update the hide flag for edges and faces from the corresponding flag in verts.
*/
-void BKE_mesh_flush_hidden_from_verts_ex(const struct MVert *mvert,
- const struct MLoop *mloop,
- struct MEdge *medge,
- int totedge,
- struct MPoly *mpoly,
- int totpoly);
void BKE_mesh_flush_hidden_from_verts(struct Mesh *me);
-void BKE_mesh_flush_hidden_from_polys_ex(struct MVert *mvert,
- const struct MLoop *mloop,
- struct MEdge *medge,
- int totedge,
- const struct MPoly *mpoly,
- int totpoly);
void BKE_mesh_flush_hidden_from_polys(struct Mesh *me);
/**
* simple poly -> vert/edge selection.
@@ -986,7 +985,7 @@ void BKE_mesh_strip_loose_edges(struct Mesh *me);
/**
* If the mesh is from a very old blender version,
- * convert mface->edcode to edge drawflags
+ * convert #MFace.edcode to edge #ME_EDGEDRAW.
*/
void BKE_mesh_calc_edges_legacy(struct Mesh *me, bool use_old);
void BKE_mesh_calc_edges_loose(struct Mesh *mesh);
@@ -1002,8 +1001,8 @@ void BKE_mesh_calc_edges(struct Mesh *mesh, bool keep_existing_edges, bool selec
void BKE_mesh_calc_edges_tessface(struct Mesh *mesh);
/* In DerivedMesh.cc */
-void BKE_mesh_wrapper_deferred_finalize(struct Mesh *me_eval,
- const struct CustomData_MeshMasks *cd_mask_finalize);
+void BKE_mesh_wrapper_deferred_finalize_mdata(struct Mesh *me_eval,
+ const struct CustomData_MeshMasks *cd_mask_finalize);
/* **** Depsgraph evaluation **** */
@@ -1024,6 +1023,138 @@ char *BKE_mesh_debug_info(const struct Mesh *me)
void BKE_mesh_debug_print(const struct Mesh *me) ATTR_NONNULL(1);
#endif
+/* -------------------------------------------------------------------- */
+/** \name Inline Mesh Data Access
+ * \{ */
+
+/**
+ * \return The material index for each polygon. May be null.
+ * \note In C++ code, prefer using the attribute API (#MutableAttributeAccessor)/
+ */
+BLI_INLINE const int *BKE_mesh_material_indices(const Mesh *mesh)
+{
+ return (const int *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_INT32, "material_index");
+}
+
+/**
+ * \return The material index for each polygon. Create the layer if it doesn't exist.
+ * \note In C++ code, prefer using the attribute API (#MutableAttributeAccessor)/
+ */
+BLI_INLINE int *BKE_mesh_material_indices_for_write(Mesh *mesh)
+{
+ int *indices = (int *)CustomData_duplicate_referenced_layer_named(
+ &mesh->pdata, CD_PROP_INT32, "material_index", mesh->totpoly);
+ if (indices) {
+ return indices;
+ }
+ return (int *)CustomData_add_layer_named(
+ &mesh->pdata, CD_PROP_INT32, CD_SET_DEFAULT, NULL, mesh->totpoly, "material_index");
+}
+
+BLI_INLINE const MVert *BKE_mesh_verts(const Mesh *mesh)
+{
+ return (const MVert *)CustomData_get_layer(&mesh->vdata, CD_MVERT);
+}
+BLI_INLINE MVert *BKE_mesh_verts_for_write(Mesh *mesh)
+{
+ return (MVert *)CustomData_duplicate_referenced_layer(&mesh->vdata, CD_MVERT, mesh->totvert);
+}
+
+BLI_INLINE const MEdge *BKE_mesh_edges(const Mesh *mesh)
+{
+ return (const MEdge *)CustomData_get_layer(&mesh->edata, CD_MEDGE);
+}
+BLI_INLINE MEdge *BKE_mesh_edges_for_write(Mesh *mesh)
+{
+ return (MEdge *)CustomData_duplicate_referenced_layer(&mesh->edata, CD_MEDGE, mesh->totedge);
+}
+
+BLI_INLINE const MPoly *BKE_mesh_polys(const Mesh *mesh)
+{
+ return (const MPoly *)CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+}
+BLI_INLINE MPoly *BKE_mesh_polys_for_write(Mesh *mesh)
+{
+ return (MPoly *)CustomData_duplicate_referenced_layer(&mesh->pdata, CD_MPOLY, mesh->totpoly);
+}
+
+BLI_INLINE const MLoop *BKE_mesh_loops(const Mesh *mesh)
+{
+ return (const MLoop *)CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+}
+BLI_INLINE MLoop *BKE_mesh_loops_for_write(Mesh *mesh)
+{
+ return (MLoop *)CustomData_duplicate_referenced_layer(&mesh->ldata, CD_MLOOP, mesh->totloop);
+}
+
+BLI_INLINE const MDeformVert *BKE_mesh_deform_verts(const Mesh *mesh)
+{
+ return (const MDeformVert *)CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT);
+}
+BLI_INLINE MDeformVert *BKE_mesh_deform_verts_for_write(Mesh *mesh)
+{
+ MDeformVert *dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
+ &mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
+ if (dvert) {
+ return dvert;
+ }
+ return (MDeformVert *)CustomData_add_layer(
+ &mesh->vdata, CD_MDEFORMVERT, CD_SET_DEFAULT, NULL, mesh->totvert);
+}
+
#ifdef __cplusplus
}
#endif
+
+#ifdef __cplusplus
+
+# include "BLI_span.hh"
+
+inline blender::Span<MVert> Mesh::verts() const
+{
+ return {BKE_mesh_verts(this), this->totvert};
+}
+inline blender::MutableSpan<MVert> Mesh::verts_for_write()
+{
+ return {BKE_mesh_verts_for_write(this), this->totvert};
+}
+
+inline blender::Span<MEdge> Mesh::edges() const
+{
+ return {BKE_mesh_edges(this), this->totedge};
+}
+inline blender::MutableSpan<MEdge> Mesh::edges_for_write()
+{
+ return {BKE_mesh_edges_for_write(this), this->totedge};
+}
+
+inline blender::Span<MPoly> Mesh::polys() const
+{
+ return {BKE_mesh_polys(this), this->totpoly};
+}
+inline blender::MutableSpan<MPoly> Mesh::polys_for_write()
+{
+ return {BKE_mesh_polys_for_write(this), this->totpoly};
+}
+
+inline blender::Span<MLoop> Mesh::loops() const
+{
+ return {BKE_mesh_loops(this), this->totloop};
+}
+inline blender::MutableSpan<MLoop> Mesh::loops_for_write()
+{
+ return {BKE_mesh_loops_for_write(this), this->totloop};
+}
+
+inline blender::Span<MDeformVert> Mesh::deform_verts() const
+{
+ return {BKE_mesh_deform_verts(this), this->totvert};
+}
+inline blender::MutableSpan<MDeformVert> Mesh::deform_verts_for_write()
+{
+ return {BKE_mesh_deform_verts_for_write(this), this->totvert};
+}
+
+#endif
+
+/** \} */
diff --git a/source/blender/blenkernel/BKE_mesh_fair.h b/source/blender/blenkernel/BKE_mesh_fair.h
index 0dc44ecb247..9d94c692858 100644
--- a/source/blender/blenkernel/BKE_mesh_fair.h
+++ b/source/blender/blenkernel/BKE_mesh_fair.h
@@ -25,16 +25,16 @@ typedef enum eMeshFairingDepth {
/* affect_vertices is used to define the fairing area. Indexed by vertex index, set to true when
* the vertex should be modified by fairing. */
-void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm,
- bool *affect_vertices,
- eMeshFairingDepth depth);
+void BKE_bmesh_prefair_and_fair_verts(struct BMesh *bm,
+ bool *affect_verts,
+ eMeshFairingDepth depth);
/* This function can optionally use the MVert coordinates of deform_mverts to read and write the
* fairing result. When NULL, the function will use mesh->mverts directly. */
-void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh,
- struct MVert *deform_mverts,
- bool *affect_vertices,
- eMeshFairingDepth depth);
+void BKE_mesh_prefair_and_fair_verts(struct Mesh *mesh,
+ struct MVert *deform_mverts,
+ bool *affect_verts,
+ eMeshFairingDepth depth);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.h b/source/blender/blenkernel/BKE_mesh_legacy_convert.h
index 909fd0e0dea..e67aec0b9ce 100644
--- a/source/blender/blenkernel/BKE_mesh_legacy_convert.h
+++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.h
@@ -18,6 +18,35 @@ struct Mesh;
struct MFace;
/**
+ * Copy bevel weights from separate layers into vertices and edges.
+ */
+void BKE_mesh_legacy_bevel_weight_from_layers(struct Mesh *mesh);
+/**
+ * Copy bevel weights from vertices and edges to separate layers.
+ */
+void BKE_mesh_legacy_bevel_weight_to_layers(struct Mesh *mesh);
+
+/**
+ * Convert the hidden element attributes to the old flag format for writing.
+ */
+void BKE_mesh_legacy_convert_hide_layers_to_flags(struct Mesh *mesh);
+/**
+ * Convert the old hide flags (#ME_HIDE) to the hidden element attribute for reading.
+ * Only add the attributes when there are any elements in each domain hidden.
+ */
+void BKE_mesh_legacy_convert_flags_to_hide_layers(struct Mesh *mesh);
+
+/**
+ * Move material indices from a generic attribute to #MPoly.
+ */
+void BKE_mesh_legacy_convert_material_indices_to_mpoly(struct Mesh *mesh);
+/**
+ * Move material indices from the #MPoly struct to a generic attributes.
+ * Only add the attribute when the indices are not all zero.
+ */
+void BKE_mesh_legacy_convert_mpoly_to_material_indices(struct Mesh *mesh);
+
+/**
* Recreate #MFace Tessellation.
*
* \note This doesn't use multi-threading like #BKE_mesh_recalc_looptri since
diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h
index 163acf062e0..cf9763d50a4 100644
--- a/source/blender/blenkernel/BKE_mesh_mapping.h
+++ b/source/blender/blenkernel/BKE_mesh_mapping.h
@@ -17,11 +17,10 @@ struct MLoopUV;
struct MPoly;
struct MVert;
-/* map from uv vertex to face (for select linked, stitch, uv suburf) */
-
/* UvVertMap */
#define STD_UV_CONNECT_LIMIT 0.0001f
+/* Map from uv vertex to face. Used by select linked, uv subdivision-surface and obj exporter. */
typedef struct UvVertMap {
struct UvMapVert **vert;
struct UvMapVert *buf;
@@ -52,24 +51,38 @@ typedef struct UvElement {
unsigned int island;
} UvElement;
-/* UvElementMap is a container for UvElements of a mesh. It stores some UvElements belonging to the
- * same uv island in sequence and the number of uvs per island so it is possible to access all uvs
- * belonging to an island directly by iterating through the buffer.
+/** UvElementMap is a container for UvElements of a BMesh.
+ *
+ * It simplifies access to UV information and ensures the
+ * different UV selection modes are respected.
+ *
+ * If islands are calculated, it also stores UvElements
+ * belonging to the same uv island in sequence and
+ * the number of uvs per island.
*/
typedef struct UvElementMap {
- /* address UvElements by their vertex */
- struct UvElement **vert;
- /* UvElement Store */
- struct UvElement *buf;
- /* Total number of UVs in the layer. Useful to know */
- int totalUVs;
- /* Number of Islands in the mesh */
- int totalIslands;
- /* Stores the starting index in buf where each island begins */
- int *islandIndices;
-} UvElementMap;
+ /** UvElement Storage. */
+ struct UvElement *storage;
+ /** Total number of UVs. */
+ int total_uvs;
+ /** Total number of unique UVs. */
+ int total_unique_uvs;
-#define INVALID_ISLAND ((unsigned int)-1)
+ /** If Non-NULL, address UvElements by `BM_elem_index_get(BMVert*)`. */
+ struct UvElement **vertex;
+
+ /** If Non-NULL, pointer to local head of each unique UV. */
+ struct UvElement **head_table;
+
+ /** Number of islands, or zero if not calculated. */
+ int total_islands;
+ /** Array of starting index in #storage where each island begins. */
+ int *island_indices;
+ /** Array of number of UVs in each island. */
+ int *island_total_uvs;
+ /** Array of number of unique UVs in each island. */
+ int *island_total_unique_uvs;
+} UvElementMap;
/* Connectivity data */
typedef struct MeshElemMap {
@@ -79,6 +92,7 @@ typedef struct MeshElemMap {
/* mapping */
UvVertMap *BKE_mesh_uv_vert_map_create(const struct MPoly *mpoly,
+ const bool *hide_poly,
const struct MLoop *mloop,
const struct MLoopUV *mloopuv,
unsigned int totpoly,
@@ -234,13 +248,13 @@ void BKE_mesh_loop_islands_add(MeshIslandStore *island_store,
int num_innercut_items,
int *innercut_item_indices);
-typedef bool (*MeshRemapIslandsCalc)(struct MVert *verts,
+typedef bool (*MeshRemapIslandsCalc)(const struct MVert *verts,
int totvert,
- struct MEdge *edges,
+ const struct MEdge *edges,
int totedge,
- struct MPoly *polys,
+ const struct MPoly *polys,
int totpoly,
- struct MLoop *loops,
+ const struct MLoop *loops,
int totloop,
struct MeshIslandStore *r_island_store);
@@ -251,13 +265,13 @@ typedef bool (*MeshRemapIslandsCalc)(struct MVert *verts,
* Calculate 'generic' UV islands, i.e. based only on actual geometry data (edge seams),
* not some UV layers coordinates.
*/
-bool BKE_mesh_calc_islands_loop_poly_edgeseam(struct MVert *verts,
+bool BKE_mesh_calc_islands_loop_poly_edgeseam(const struct MVert *verts,
int totvert,
- struct MEdge *edges,
+ const struct MEdge *edges,
int totedge,
- struct MPoly *polys,
+ const struct MPoly *polys,
int totpoly,
- struct MLoop *loops,
+ const struct MLoop *loops,
int totloop,
MeshIslandStore *r_island_store);
diff --git a/source/blender/blenkernel/BKE_mesh_remap.h b/source/blender/blenkernel/BKE_mesh_remap.h
index ee0179c7a77..efbf542c831 100644
--- a/source/blender/blenkernel/BKE_mesh_remap.h
+++ b/source/blender/blenkernel/BKE_mesh_remap.h
@@ -199,13 +199,13 @@ void BKE_mesh_remap_calc_loops_from_mesh(int mode,
float max_dist,
float ray_radius,
struct Mesh *mesh_dst,
- struct MVert *verts_dst,
+ const struct MVert *verts_dst,
int numverts_dst,
- struct MEdge *edges_dst,
+ const struct MEdge *edges_dst,
int numedges_dst,
- struct MLoop *loops_dst,
+ const struct MLoop *loops_dst,
int numloops_dst,
- struct MPoly *polys_dst,
+ const struct MPoly *polys_dst,
int numpolys_dst,
struct CustomData *ldata_dst,
bool use_split_nors_dst,
@@ -220,10 +220,10 @@ void BKE_mesh_remap_calc_polys_from_mesh(int mode,
const struct SpaceTransform *space_transform,
float max_dist,
float ray_radius,
- struct Mesh *mesh_dst,
- struct MVert *verts_dst,
- struct MLoop *loops_dst,
- struct MPoly *polys_dst,
+ const struct Mesh *mesh_dst,
+ const struct MVert *verts_dst,
+ const struct MLoop *loops_dst,
+ const struct MPoly *polys_dst,
int numpolys_dst,
struct Mesh *me_src,
struct MeshPairRemap *r_map);
diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
index dff17bd6575..d9f5a75ca61 100644
--- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
+++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h
@@ -28,9 +28,9 @@ struct Mesh *BKE_mesh_remesh_quadriflow(const struct Mesh *mesh,
void *update_cb_data);
/* Data reprojection functions */
-void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source);
+void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, const struct Mesh *source);
void BKE_remesh_reproject_vertex_paint(struct Mesh *target, const struct Mesh *source);
-void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, struct Mesh *source);
+void BKE_remesh_reproject_sculpt_face_sets(struct Mesh *target, const struct Mesh *source);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh
index 356709d8942..94dc52d5ec9 100644
--- a/source/blender/blenkernel/BKE_mesh_sample.hh
+++ b/source/blender/blenkernel/BKE_mesh_sample.hh
@@ -13,7 +13,6 @@
#include "DNA_meshdata_types.h"
#include "BKE_attribute.h"
-#include "BKE_attribute.hh"
struct Mesh;
struct BVHTreeFromMesh;
@@ -27,22 +26,22 @@ namespace blender::bke::mesh_surface_sample {
void sample_point_attribute(const Mesh &mesh,
Span<int> looptri_indices,
Span<float3> bary_coords,
- const GVArray &data_in,
- const IndexMask mask,
- GMutableSpan data_out);
+ const GVArray &src,
+ IndexMask mask,
+ GMutableSpan dst);
void sample_corner_attribute(const Mesh &mesh,
Span<int> looptri_indices,
Span<float3> bary_coords,
- const GVArray &data_in,
- const IndexMask mask,
- GMutableSpan data_out);
+ const GVArray &src,
+ IndexMask mask,
+ GMutableSpan dst);
void sample_face_attribute(const Mesh &mesh,
Span<int> looptri_indices,
- const GVArray &data_in,
- const IndexMask mask,
- GMutableSpan data_out);
+ const GVArray &src,
+ IndexMask mask,
+ GMutableSpan dst);
enum class eAttributeMapMode {
INTERPOLATED,
@@ -57,7 +56,6 @@ enum class eAttributeMapMode {
* these are computed lazily when needed and re-used.
*/
class MeshAttributeInterpolator {
- private:
const Mesh *mesh_;
const IndexMask mask_;
const Span<float3> positions_;
@@ -68,18 +66,14 @@ class MeshAttributeInterpolator {
public:
MeshAttributeInterpolator(const Mesh *mesh,
- const IndexMask mask,
- const Span<float3> positions,
- const Span<int> looptri_indices);
+ IndexMask mask,
+ Span<float3> positions,
+ Span<int> looptri_indices);
void sample_data(const GVArray &src,
eAttrDomain domain,
eAttributeMapMode mode,
- const GMutableSpan dst);
-
- void sample_attribute(const GAttributeReader &src_attribute,
- GSpanAttributeWriter &dst_attribute,
- eAttributeMapMode mode);
+ GMutableSpan dst);
protected:
Span<float3> ensure_barycentric_coords();
@@ -133,8 +127,20 @@ int sample_surface_points_projected(
Vector<int> &r_looptri_indices,
Vector<float3> &r_positions);
-float3 compute_bary_coord_in_triangle(const Mesh &mesh,
+float3 compute_bary_coord_in_triangle(Span<MVert> verts,
+ Span<MLoop> loops,
const MLoopTri &looptri,
const float3 &position);
+template<typename T>
+inline T sample_corner_attrribute_with_bary_coords(const float3 &bary_weights,
+ const MLoopTri &looptri,
+ const Span<T> corner_attribute)
+{
+ return attribute_math::mix3(bary_weights,
+ corner_attribute[looptri.tri[0]],
+ corner_attribute[looptri.tri[1]],
+ corner_attribute[looptri.tri[2]]);
+}
+
} // namespace blender::bke::mesh_surface_sample
diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h
index 866b0353d07..f46672a5033 100644
--- a/source/blender/blenkernel/BKE_modifier.h
+++ b/source/blender/blenkernel/BKE_modifier.h
@@ -265,9 +265,7 @@ typedef struct ModifierTypeInfo {
*
* This function is optional.
*/
- void (*requiredDataMask)(struct Object *ob,
- struct ModifierData *md,
- struct CustomData_MeshMasks *r_cddata_masks);
+ void (*requiredDataMask)(struct ModifierData *md, struct CustomData_MeshMasks *r_cddata_masks);
/**
* Free internal modifier data variables, this function should
@@ -446,10 +444,22 @@ bool BKE_modifier_is_enabled(const struct Scene *scene,
*/
bool BKE_modifier_is_nonlocal_in_liboverride(const struct Object *ob,
const struct ModifierData *md);
+
+/* Set modifier execution error.
+ * The message will be shown in the interface and will be logged as an error to the console. */
void BKE_modifier_set_error(const struct Object *ob,
struct ModifierData *md,
const char *format,
...) ATTR_PRINTF_FORMAT(3, 4);
+
+/* Set modifier execution warning, which does not prevent the modifier from being applied but which
+ * might need an attention. The message will only be shown in the interface, but will not appear in
+ * the logs. */
+void BKE_modifier_set_warning(const struct Object *ob,
+ struct ModifierData *md,
+ const char *format,
+ ...) ATTR_PRINTF_FORMAT(3, 4);
+
bool BKE_modifier_is_preview(struct ModifierData *md);
void BKE_modifiers_foreach_ID_link(struct Object *ob, IDWalkFunc walk, void *userData);
@@ -509,7 +519,6 @@ typedef struct CDMaskLink {
* final_datamask is required at the end of the stack.
*/
struct CDMaskLink *BKE_modifier_calc_data_masks(const struct Scene *scene,
- struct Object *ob,
struct ModifierData *md,
struct CustomData_MeshMasks *final_datamask,
int required_mode,
@@ -586,12 +595,8 @@ void BKE_modifier_deform_vertsEM(ModifierData *md,
* e.g. second operand for boolean modifier.
* Note that modifiers in stack always get fully evaluated COW ID pointers,
* never original ones. Makes things simpler.
- *
- * \param get_cage_mesh: Return evaluated mesh with only deforming modifiers applied
- * (i.e. mesh topology remains the same as original one, a.k.a. 'cage' mesh).
*/
-struct Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(struct Object *ob_eval,
- bool get_cage_mesh);
+struct Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(struct Object *ob_eval);
void BKE_modifier_check_uuids_unique_and_report(const struct Object *object);
diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h
index 2613f4286f0..9d3000759ab 100644
--- a/source/blender/blenkernel/BKE_nla.h
+++ b/source/blender/blenkernel/BKE_nla.h
@@ -7,7 +7,7 @@
* \ingroup bke
*/
-/* temp constant defined for these funcs only... */
+/** Temp constant defined for these functions only. */
#define NLASTRIP_MIN_LEN_THRESH 0.1f
#ifdef __cplusplus
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index b7962ade312..f504d5d1384 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -101,6 +101,7 @@ typedef struct bNodeSocketTemplate {
namespace blender {
class CPPType;
namespace nodes {
+class DNode;
class NodeMultiFunctionBuilder;
class GeoNodeExecParams;
class NodeDeclarationBuilder;
@@ -109,6 +110,11 @@ class GatherLinkSearchOpParams;
namespace fn {
class MFDataType;
} // namespace fn
+namespace realtime_compositor {
+class Context;
+class NodeOperation;
+class ShaderNode;
+} // namespace realtime_compositor
} // namespace blender
using CPPTypeHandle = blender::CPPType;
@@ -123,7 +129,14 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket
using NodeGatherSocketLinkOperationsFunction =
void (*)(blender::nodes::GatherLinkSearchOpParams &params);
+using NodeGetCompositorOperationFunction = blender::realtime_compositor::NodeOperation
+ *(*)(blender::realtime_compositor::Context &context, blender::nodes::DNode node);
+using NodeGetCompositorShaderNodeFunction =
+ blender::realtime_compositor::ShaderNode *(*)(blender::nodes::DNode node);
+
#else
+typedef void *NodeGetCompositorOperationFunction;
+typedef void *NodeGetCompositorShaderNodeFunction;
typedef void *NodeMultiFunctionBuildFunction;
typedef void *NodeGeometryExecFunction;
typedef void *NodeDeclareFunction;
@@ -309,11 +322,23 @@ typedef struct bNodeType {
/* gpu */
NodeGPUExecFunction gpu_fn;
+ /* Get an instance of this node's compositor operation. Freeing the instance is the
+ * responsibility of the caller. */
+ NodeGetCompositorOperationFunction get_compositor_operation;
+
+ /* Get an instance of this node's compositor shader node. Freeing the instance is the
+ * responsibility of the caller. */
+ NodeGetCompositorShaderNodeFunction get_compositor_shader_node;
+
/* Build a multi-function for this node. */
NodeMultiFunctionBuildFunction build_multi_function;
/* Execute a geometry node. */
NodeGeometryExecFunction geometry_node_execute;
+ /**
+ * If true, the geometry nodes evaluator can call the execute function multiple times to improve
+ * performance by specifying required data in one call and using it for calculations in another.
+ */
bool geometry_node_execute_supports_laziness;
/* Declares which sockets the node has. */
@@ -370,6 +395,9 @@ typedef struct bNodeTreeType {
int type; /* type identifier */
char idname[64]; /* identifier name */
+ /* The ID name of group nodes for this type. */
+ char group_idname[64];
+
char ui_name[64];
char ui_description[256];
int ui_icon;
@@ -444,7 +472,12 @@ void ntreeSetTypes(const struct bContext *C, struct bNodeTree *ntree);
struct bNodeTree *ntreeAddTree(struct Main *bmain, const char *name, const char *idname);
-/* copy/free funcs, need to manage ID users */
+struct bNodeTree *ntreeAddTreeEmbedded(struct Main *bmain,
+ struct ID *owner_id,
+ const char *name,
+ const char *idname);
+
+/* Copy/free functions, need to manage ID users. */
/**
* Free (or release) any data used by this node-tree.
@@ -512,7 +545,9 @@ void ntreeBlendWrite(struct BlendWriter *writer, struct bNodeTree *ntree);
/**
* \note `ntree` itself has been read!
*/
-void ntreeBlendReadData(struct BlendDataReader *reader, struct bNodeTree *ntree);
+void ntreeBlendReadData(struct BlendDataReader *reader,
+ struct ID *owner_id,
+ struct bNodeTree *ntree);
void ntreeBlendReadLib(struct BlendLibReader *reader, struct bNodeTree *ntree);
void ntreeBlendReadExpand(struct BlendExpander *expander, struct bNodeTree *ntree);
@@ -679,7 +714,10 @@ struct bNodeLink *nodeAddLink(struct bNodeTree *ntree,
struct bNodeSocket *tosock);
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link);
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
-void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link);
+/**
+ * Set the mute status of a single link.
+ */
+void nodeLinkSetMute(struct bNodeTree *ntree, struct bNodeLink *link, const bool muted);
bool nodeLinkIsHidden(const struct bNodeLink *link);
bool nodeLinkIsSelected(const struct bNodeLink *link);
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node);
@@ -922,8 +960,8 @@ void nodeLabel(const struct bNodeTree *ntree, const struct bNode *node, char *la
*/
const char *nodeSocketLabel(const struct bNodeSocket *sock);
-bool nodeGroupPoll(struct bNodeTree *nodetree,
- struct bNodeTree *grouptree,
+bool nodeGroupPoll(const struct bNodeTree *nodetree,
+ const struct bNodeTree *grouptree,
const char **r_disabled_hint);
/**
@@ -1074,7 +1112,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
//#define SH_NODE_MATERIAL 100
#define SH_NODE_RGB 101
#define SH_NODE_VALUE 102
-#define SH_NODE_MIX_RGB 103
+#define SH_NODE_MIX_RGB_LEGACY 103
#define SH_NODE_VALTORGB 104
#define SH_NODE_RGBTOBW 105
#define SH_NODE_SHADERTORGB 106
@@ -1177,6 +1215,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define SH_NODE_POINT_INFO 710
#define SH_NODE_COMBINE_COLOR 711
#define SH_NODE_SEPARATE_COLOR 712
+#define SH_NODE_MIX 713
/** \} */
@@ -1298,25 +1337,6 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define CMP_CHAN_RGB 1
#define CMP_CHAN_A 2
-/* filter types */
-#define CMP_FILT_SOFT 0
-#define CMP_FILT_SHARP_BOX 1
-#define CMP_FILT_LAPLACE 2
-#define CMP_FILT_SOBEL 3
-#define CMP_FILT_PREWITT 4
-#define CMP_FILT_KIRSCH 5
-#define CMP_FILT_SHADOW 6
-#define CMP_FILT_SHARP_DIAMOND 7
-
-/* scale node type, in custom1 */
-#define CMP_SCALE_RELATIVE 0
-#define CMP_SCALE_ABSOLUTE 1
-#define CMP_SCALE_SCENEPERCENT 2
-#define CMP_SCALE_RENDERPERCENT 3
-/* custom2 */
-#define CMP_SCALE_RENDERSIZE_FRAME_ASPECT (1 << 0)
-#define CMP_SCALE_RENDERSIZE_FRAME_CROP (1 << 1)
-
/* track position node, in custom1 */
#define CMP_TRACKPOS_ABSOLUTE 0
#define CMP_TRACKPOS_RELATIVE_START 1
@@ -1497,11 +1517,16 @@ struct TexResult;
#define GEO_NODE_INPUT_INSTANCE_SCALE 1160
#define GEO_NODE_VOLUME_CUBE 1161
#define GEO_NODE_POINTS 1162
-#define GEO_NODE_FIELD_ON_DOMAIN 1163
+#define GEO_NODE_INTERPOLATE_DOMAIN 1163
#define GEO_NODE_MESH_TO_VOLUME 1164
#define GEO_NODE_UV_UNWRAP 1165
#define GEO_NODE_UV_PACK_ISLANDS 1166
#define GEO_NODE_DEFORM_CURVES_ON_SURFACE 1167
+#define GEO_NODE_INPUT_SHORTEST_EDGE_PATHS 1168
+#define GEO_NODE_EDGE_PATHS_TO_CURVES 1169
+#define GEO_NODE_EDGE_PATHS_TO_SELECTION 1170
+#define GEO_NODE_MESH_FACE_SET_BOUNDARIES 1171
+#define GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME 1172
/** \} */
diff --git a/source/blender/blenkernel/BKE_node_runtime.hh b/source/blender/blenkernel/BKE_node_runtime.hh
index f5fb53f962b..65c801a087b 100644
--- a/source/blender/blenkernel/BKE_node_runtime.hh
+++ b/source/blender/blenkernel/BKE_node_runtime.hh
@@ -3,13 +3,25 @@
#pragma once
#include <memory>
+#include <mutex>
-#include "BLI_sys_types.h"
+#include "BLI_multi_value_map.hh"
#include "BLI_utility_mixins.hh"
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+
+#include "BKE_node.h"
+
+struct bNode;
+struct bNodeSocket;
+struct bNodeTree;
+struct bNodeType;
namespace blender::nodes {
struct FieldInferencingInterface;
class NodeDeclaration;
+struct GeometryNodesLazyFunctionGraphInfo;
} // namespace blender::nodes
namespace blender::bke {
@@ -36,6 +48,42 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
/** Information about how inputs and outputs of the node group interact with fields. */
std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface;
+
+ /**
+ * For geometry nodes, a lazy function graph with some additional info is cached. This is used to
+ * evaluate the node group. Caching it here allows us to reuse the preprocessed node tree in case
+ * its used multiple times.
+ */
+ std::mutex geometry_nodes_lazy_function_graph_info_mutex;
+ std::unique_ptr<nodes::GeometryNodesLazyFunctionGraphInfo>
+ geometry_nodes_lazy_function_graph_info;
+
+ /**
+ * Protects access to all topology cache variables below. This is necessary so that the cache can
+ * be updated on a const #bNodeTree.
+ */
+ std::mutex topology_cache_mutex;
+ bool topology_cache_is_dirty = true;
+ bool topology_cache_exists = false;
+ /**
+ * Under some circumstances, it can be useful to use the cached data while editing the
+ * #bNodeTree. By default, this is protected against using an assert.
+ */
+ mutable std::atomic<int> allow_use_dirty_topology_cache = 0;
+
+ /** Only valid when #topology_cache_is_dirty is false. */
+ Vector<bNode *> nodes;
+ Vector<bNodeLink *> links;
+ Vector<bNodeSocket *> sockets;
+ Vector<bNodeSocket *> input_sockets;
+ Vector<bNodeSocket *> output_sockets;
+ MultiValueMap<const bNodeType *, bNode *> nodes_by_type;
+ Vector<bNode *> toposort_left_to_right;
+ Vector<bNode *> toposort_right_to_left;
+ Vector<bNode *> group_nodes;
+ bool has_available_link_cycle = false;
+ bool has_undefined_nodes_or_sockets = false;
+ bNode *group_output_node = nullptr;
};
/**
@@ -47,12 +95,24 @@ class bNodeSocketRuntime : NonCopyable, NonMovable {
public:
/**
* References a socket declaration that is owned by `node->declaration`. This is only runtime
- * data. It has to be updated when the node declaration changes.
+ * data. It has to be updated when the node declaration changes. Access can be allowed by using
+ * #AllowUsingOutdatedInfo.
*/
const SocketDeclarationHandle *declaration = nullptr;
/** #eNodeTreeChangedFlag. */
uint32_t changed_flag = 0;
+
+ /** Only valid when #topology_cache_is_dirty is false. */
+ Vector<bNodeLink *> directly_linked_links;
+ Vector<bNodeSocket *> directly_linked_sockets;
+ Vector<bNodeSocket *> logically_linked_sockets;
+ Vector<bNodeSocket *> logically_linked_skipped_sockets;
+ bNode *owner_node = nullptr;
+ bNodeSocket *internal_link_input = nullptr;
+ int index_in_node = -1;
+ int index_in_all_sockets = -1;
+ int index_in_inout_sockets = -1;
};
/**
@@ -84,6 +144,428 @@ class bNodeRuntime : NonCopyable, NonMovable {
/** #eNodeTreeChangedFlag. */
uint32_t changed_flag = 0;
+
+ /** Only valid if #topology_cache_is_dirty is false. */
+ Vector<bNodeSocket *> inputs;
+ Vector<bNodeSocket *> outputs;
+ Vector<bNodeLink *> internal_links;
+ Map<StringRefNull, bNodeSocket *> inputs_by_identifier;
+ Map<StringRefNull, bNodeSocket *> outputs_by_identifier;
+ int index_in_tree = -1;
+ bool has_available_linked_inputs = false;
+ bool has_available_linked_outputs = false;
+ bNodeTree *owner_tree = nullptr;
+};
+
+namespace node_tree_runtime {
+
+/**
+ * Is executed when the node tree changed in the depsgraph.
+ */
+void preprocess_geometry_node_tree_for_evaluation(bNodeTree &tree_cow);
+
+class AllowUsingOutdatedInfo : NonCopyable, NonMovable {
+ private:
+ const bNodeTree &tree_;
+
+ public:
+ AllowUsingOutdatedInfo(const bNodeTree &tree) : tree_(tree)
+ {
+ tree_.runtime->allow_use_dirty_topology_cache.fetch_add(1);
+ }
+
+ ~AllowUsingOutdatedInfo()
+ {
+ tree_.runtime->allow_use_dirty_topology_cache.fetch_sub(1);
+ }
};
+inline bool topology_cache_is_available(const bNodeTree &tree)
+{
+ if (!tree.runtime->topology_cache_exists) {
+ return false;
+ }
+ if (tree.runtime->allow_use_dirty_topology_cache.load() > 0) {
+ return true;
+ }
+ if (tree.runtime->topology_cache_is_dirty) {
+ return false;
+ }
+ return true;
+}
+
+inline bool topology_cache_is_available(const bNode &node)
+{
+ const bNodeTree *ntree = node.runtime->owner_tree;
+ if (ntree == nullptr) {
+ return false;
+ }
+ return topology_cache_is_available(*ntree);
+}
+
+inline bool topology_cache_is_available(const bNodeSocket &socket)
+{
+ const bNode *node = socket.runtime->owner_node;
+ if (node == nullptr) {
+ return false;
+ }
+ return topology_cache_is_available(*node);
+}
+
+} // namespace node_tree_runtime
+
} // namespace blender::bke
+
+/* -------------------------------------------------------------------- */
+/** \name #bNodeTree Inline Methods
+ * \{ */
+
+inline blender::Span<bNode *> bNodeTree::nodes_by_type(const blender::StringRefNull type_idname)
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->nodes_by_type.lookup(nodeTypeFind(type_idname.c_str()));
+}
+
+inline blender::Span<const bNode *> bNodeTree::nodes_by_type(
+ const blender::StringRefNull type_idname) const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->nodes_by_type.lookup(nodeTypeFind(type_idname.c_str()));
+}
+
+inline blender::Span<const bNode *> bNodeTree::toposort_left_to_right() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->toposort_left_to_right;
+}
+
+inline blender::Span<const bNode *> bNodeTree::toposort_right_to_left() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->toposort_right_to_left;
+}
+
+inline blender::Span<const bNode *> bNodeTree::all_nodes() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->nodes;
+}
+
+inline blender::Span<bNode *> bNodeTree::all_nodes()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->nodes;
+}
+
+inline blender::Span<const bNode *> bNodeTree::group_nodes() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->group_nodes;
+}
+
+inline blender::Span<bNode *> bNodeTree::group_nodes()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->group_nodes;
+}
+
+inline bool bNodeTree::has_available_link_cycle() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->has_available_link_cycle;
+}
+
+inline bool bNodeTree::has_undefined_nodes_or_sockets() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->has_undefined_nodes_or_sockets;
+}
+
+inline const bNode *bNodeTree::group_output_node() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->group_output_node;
+}
+
+inline blender::Span<const bNodeSocket *> bNodeTree::all_input_sockets() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->input_sockets;
+}
+
+inline blender::Span<bNodeSocket *> bNodeTree::all_input_sockets()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->input_sockets;
+}
+
+inline blender::Span<const bNodeSocket *> bNodeTree::all_output_sockets() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->output_sockets;
+}
+
+inline blender::Span<bNodeSocket *> bNodeTree::all_output_sockets()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->output_sockets;
+}
+
+inline blender::Span<const bNodeSocket *> bNodeTree::all_sockets() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->sockets;
+}
+
+inline blender::Span<bNodeSocket *> bNodeTree::all_sockets()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->sockets;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #bNode Inline Methods
+ * \{ */
+
+inline blender::Span<bNodeSocket *> bNode::input_sockets()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->inputs;
+}
+
+inline blender::Span<bNodeSocket *> bNode::output_sockets()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->outputs;
+}
+
+inline blender::Span<const bNodeSocket *> bNode::input_sockets() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->inputs;
+}
+
+inline blender::Span<const bNodeSocket *> bNode::output_sockets() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->outputs;
+}
+
+inline bNodeSocket &bNode::input_socket(int index)
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->inputs[index];
+}
+
+inline bNodeSocket &bNode::output_socket(int index)
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->outputs[index];
+}
+
+inline const bNodeSocket &bNode::input_socket(int index) const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->inputs[index];
+}
+
+inline const bNodeSocket &bNode::output_socket(int index) const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->outputs[index];
+}
+
+inline const bNodeSocket &bNode::input_by_identifier(blender::StringRef identifier) const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->inputs_by_identifier.lookup_as(identifier);
+}
+
+inline const bNodeSocket &bNode::output_by_identifier(blender::StringRef identifier) const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->outputs_by_identifier.lookup_as(identifier);
+}
+
+inline const bNodeTree &bNode::owner_tree() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->owner_tree;
+}
+
+inline blender::StringRefNull bNode::label_or_name() const
+{
+ if (this->label[0] == '\0') {
+ return this->name;
+ }
+ return this->label;
+}
+
+inline bool bNode::is_muted() const
+{
+ return this->flag & NODE_MUTED;
+}
+
+inline bool bNode::is_reroute() const
+{
+ return this->type == NODE_REROUTE;
+}
+
+inline bool bNode::is_frame() const
+{
+ return this->type == NODE_FRAME;
+}
+
+inline bool bNode::is_group() const
+{
+ return ELEM(this->type, NODE_GROUP, NODE_CUSTOM_GROUP);
+}
+
+inline bool bNode::is_group_input() const
+{
+ return this->type == NODE_GROUP_INPUT;
+}
+
+inline bool bNode::is_group_output() const
+{
+ return this->type == NODE_GROUP_OUTPUT;
+}
+
+inline blender::Span<const bNodeLink *> bNode::internal_links_span() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->internal_links;
+}
+
+inline const blender::nodes::NodeDeclaration *bNode::declaration() const
+{
+ return this->runtime->declaration;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #bNodeLink Inline Methods
+ * \{ */
+
+inline bool bNodeLink::is_muted() const
+{
+ return this->flag & NODE_LINK_MUTED;
+}
+
+inline bool bNodeLink::is_available() const
+{
+ return this->fromsock->is_available() && this->tosock->is_available();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #bNodeSocket Inline Methods
+ * \{ */
+
+inline int bNodeSocket::index() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->index_in_node;
+}
+
+inline int bNodeSocket::index_in_tree() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->index_in_all_sockets;
+}
+
+inline bool bNodeSocket::is_available() const
+{
+ return (this->flag & SOCK_UNAVAIL) == 0;
+}
+
+inline bNode &bNodeSocket::owner_node()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->owner_node;
+}
+
+inline const bNodeTree &bNodeSocket::owner_tree() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->owner_node->runtime->owner_tree;
+}
+
+inline blender::Span<const bNodeSocket *> bNodeSocket::logically_linked_sockets() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->logically_linked_sockets;
+}
+
+inline blender::Span<const bNodeLink *> bNodeSocket::directly_linked_links() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->directly_linked_links;
+}
+
+inline blender::Span<bNodeLink *> bNodeSocket::directly_linked_links()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->directly_linked_links;
+}
+
+inline blender::Span<const bNodeSocket *> bNodeSocket::directly_linked_sockets() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->directly_linked_sockets;
+}
+
+inline blender::Span<bNodeSocket *> bNodeSocket::directly_linked_sockets()
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return this->runtime->directly_linked_sockets;
+}
+
+inline bool bNodeSocket::is_directly_linked() const
+{
+ return !this->directly_linked_links().is_empty();
+}
+
+inline bool bNodeSocket::is_logically_linked() const
+{
+ return !this->logically_linked_sockets().is_empty();
+}
+
+inline const bNodeSocket *bNodeSocket::internal_link_input() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ BLI_assert(this->in_out == SOCK_OUT);
+ return this->runtime->internal_link_input;
+}
+
+template<typename T> const T *bNodeSocket::default_value_typed() const
+{
+ return static_cast<const T *>(this->default_value);
+}
+
+inline bool bNodeSocket::is_input() const
+{
+ return this->in_out == SOCK_IN;
+}
+
+inline bool bNodeSocket::is_output() const
+{
+ return this->in_out == SOCK_OUT;
+}
+
+inline bool bNodeSocket::is_multi_input() const
+{
+ return this->flag & SOCK_MULTI_INPUT;
+}
+
+inline const bNode &bNodeSocket::owner_node() const
+{
+ BLI_assert(blender::bke::node_tree_runtime::topology_cache_is_available(*this));
+ return *this->runtime->owner_node;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/BKE_node_tree_update.h b/source/blender/blenkernel/BKE_node_tree_update.h
index 5e377728bb7..801ba22b3e9 100644
--- a/source/blender/blenkernel/BKE_node_tree_update.h
+++ b/source/blender/blenkernel/BKE_node_tree_update.h
@@ -33,6 +33,7 @@ void BKE_ntree_update_tag_all(struct bNodeTree *ntree);
void BKE_ntree_update_tag_node_property(struct bNodeTree *ntree, struct bNode *node);
void BKE_ntree_update_tag_node_new(struct bNodeTree *ntree, struct bNode *node);
void BKE_ntree_update_tag_node_removed(struct bNodeTree *ntree);
+void BKE_ntree_update_tag_node_reordered(struct bNodeTree *ntree);
void BKE_ntree_update_tag_node_mute(struct bNodeTree *ntree, struct bNode *node);
void BKE_ntree_update_tag_node_internal_link(struct bNodeTree *ntree, struct bNode *node);
diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h
index f0eb16a819d..60dfc0af25f 100644
--- a/source/blender/blenkernel/BKE_object.h
+++ b/source/blender/blenkernel/BKE_object.h
@@ -175,9 +175,10 @@ struct Object *BKE_object_add_only_object(struct Main *bmain,
* \note Creates minimum required data, but without vertices etc.
*/
struct Object *BKE_object_add(struct Main *bmain,
+ struct Scene *scene,
struct ViewLayer *view_layer,
int type,
- const char *name) ATTR_NONNULL(1, 2) ATTR_RETURNS_NONNULL;
+ const char *name) ATTR_NONNULL(1, 2, 3) ATTR_RETURNS_NONNULL;
/**
* Add a new object, using another one as a reference
*
@@ -200,6 +201,7 @@ struct Object *BKE_object_add_from(struct Main *bmain,
* assigning it to the object.
*/
struct Object *BKE_object_add_for_data(struct Main *bmain,
+ const struct Scene *scene,
struct ViewLayer *view_layer,
int type,
const char *name,
@@ -283,31 +285,38 @@ void BKE_object_matrix_local_get(struct Object *ob, float r_mat[4][4]);
bool BKE_object_pose_context_check(const struct Object *ob);
struct Object *BKE_object_pose_armature_get(struct Object *ob);
struct Object *BKE_object_pose_armature_get_visible(struct Object *ob,
+ const struct Scene *scene,
struct ViewLayer *view_layer,
struct View3D *v3d);
/**
* Access pose array with special check to get pose object when in weight paint mode.
*/
-struct Object **BKE_object_pose_array_get_ex(struct ViewLayer *view_layer,
+struct Object **BKE_object_pose_array_get_ex(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
unsigned int *r_objects_len,
bool unique);
-struct Object **BKE_object_pose_array_get_unique(struct ViewLayer *view_layer,
+struct Object **BKE_object_pose_array_get_unique(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
unsigned int *r_objects_len);
-struct Object **BKE_object_pose_array_get(struct ViewLayer *view_layer,
+struct Object **BKE_object_pose_array_get(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
unsigned int *r_objects_len);
-struct Base **BKE_object_pose_base_array_get_ex(struct ViewLayer *view_layer,
+struct Base **BKE_object_pose_base_array_get_ex(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
unsigned int *r_bases_len,
bool unique);
-struct Base **BKE_object_pose_base_array_get_unique(struct ViewLayer *view_layer,
+struct Base **BKE_object_pose_base_array_get_unique(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
unsigned int *r_bases_len);
-struct Base **BKE_object_pose_base_array_get(struct ViewLayer *view_layer,
+struct Base **BKE_object_pose_base_array_get(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
unsigned int *r_bases_len);
@@ -474,8 +483,8 @@ void BKE_object_handle_data_update(struct Depsgraph *depsgraph,
*/
void BKE_object_handle_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob);
/**
- * The main object update call, for object matrix, constraints, keys and #DispList (modifiers)
- * requires flags to be set!
+ * The main object update call, for object matrix, constraints, keys and modifiers.
+ * Requires flags to be set!
*
* Ideally we shouldn't have to pass the rigid body world,
* but need bigger restructuring to avoid id.
@@ -586,7 +595,6 @@ void BKE_object_runtime_reset_on_copy(struct Object *object, int flag);
void BKE_object_runtime_free_data(struct Object *object);
void BKE_object_batch_cache_dirty_tag(struct Object *ob);
-void BKE_object_data_batch_cache_dirty_tag(struct ID *object_data);
/* this function returns a superset of the scenes selection based on relationships */
@@ -613,7 +621,8 @@ typedef enum eObjectSet {
* If #OB_SET_VISIBLE or#OB_SET_SELECTED are collected,
* then also add related objects according to the given \a includeFilter.
*/
-struct LinkNode *BKE_object_relational_superset(struct ViewLayer *view_layer,
+struct LinkNode *BKE_object_relational_superset(const struct Scene *scene,
+ struct ViewLayer *view_layer,
eObjectSet objectSet,
eObRelationTypes includeFilter);
/**
diff --git a/source/blender/blenkernel/BKE_outliner_treehash.h b/source/blender/blenkernel/BKE_outliner_treehash.h
deleted file mode 100644
index 6f4d126fcbf..00000000000
--- a/source/blender/blenkernel/BKE_outliner_treehash.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-#pragma once
-
-/** \file
- * \ingroup bke
- */
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct BLI_mempool;
-struct ID;
-struct TreeStoreElem;
-
-/* create and fill hashtable with treestore elements */
-void *BKE_outliner_treehash_create_from_treestore(struct BLI_mempool *treestore);
-
-/* full rebuild for already allocated hashtable */
-void *BKE_outliner_treehash_rebuild_from_treestore(void *treehash, struct BLI_mempool *treestore);
-
-/* clear element usage flags */
-void BKE_outliner_treehash_clear_used(void *treehash);
-
-/* Add/remove hashtable elements */
-void BKE_outliner_treehash_add_element(void *treehash, struct TreeStoreElem *elem);
-void BKE_outliner_treehash_remove_element(void *treehash, struct TreeStoreElem *elem);
-
-/* find first unused element with specific type, nr and id */
-struct TreeStoreElem *BKE_outliner_treehash_lookup_unused(void *treehash,
- short type,
- short nr,
- struct ID *id);
-
-/* find user or unused element with specific type, nr and id */
-struct TreeStoreElem *BKE_outliner_treehash_lookup_any(void *treehash,
- short type,
- short nr,
- struct ID *id);
-
-/* free treehash structure */
-void BKE_outliner_treehash_free(void *treehash);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/blenkernel/BKE_outliner_treehash.hh b/source/blender/blenkernel/BKE_outliner_treehash.hh
new file mode 100644
index 00000000000..7f1dad5fd68
--- /dev/null
+++ b/source/blender/blenkernel/BKE_outliner_treehash.hh
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#pragma once
+
+/** \file
+ * \ingroup bke
+ *
+ * Hash table of tree-store elements (#TreeStoreElem) for fast lookups via a (id, type, index)
+ * tuple as key.
+ *
+ * The Outliner may have to perform many lookups for rebuilding complex trees, so this should be
+ * treated as performance sensitive.
+ */
+
+#include <memory>
+
+#include "BLI_map.hh"
+
+struct BLI_mempool;
+struct ID;
+struct TreeStoreElem;
+
+namespace blender::bke::outliner::treehash {
+
+/* -------------------------------------------------------------------- */
+
+class TreeStoreElemKey {
+ public:
+ ID *id = nullptr;
+ short type = 0;
+ short nr = 0;
+
+ explicit TreeStoreElemKey(const TreeStoreElem &elem);
+ TreeStoreElemKey(ID *id, short type, short nr);
+
+ uint64_t hash() const;
+ friend bool operator==(const TreeStoreElemKey &a, const TreeStoreElemKey &b);
+};
+
+/* -------------------------------------------------------------------- */
+
+class TreeHash {
+ Map<TreeStoreElemKey, std::unique_ptr<class TseGroup>> elem_groups_;
+
+ public:
+ ~TreeHash();
+
+ /** Create and fill hash-table with treestore elements */
+ static std::unique_ptr<TreeHash> create_from_treestore(BLI_mempool &treestore);
+
+ /** Full rebuild for already allocated hash-table. */
+ void rebuild_from_treestore(BLI_mempool &treestore);
+
+ /** Clear element usage flags. */
+ void clear_used();
+
+ /** Add hash-table element. */
+ void add_element(TreeStoreElem &elem);
+ /** Remove hash-table element. */
+ void remove_element(TreeStoreElem &elem);
+
+ /** Find first unused element with specific type, nr and id. */
+ TreeStoreElem *lookup_unused(short type, short nr, ID *id) const;
+
+ /** Find user or unused element with specific type, nr and id. */
+ TreeStoreElem *lookup_any(short type, short nr, ID *id) const;
+
+ private:
+ TreeHash() = default;
+
+ TseGroup *lookup_group(const TreeStoreElemKey &key) const;
+ TseGroup *lookup_group(const TreeStoreElem &elem) const;
+ TseGroup *lookup_group(short type, short nr, ID *id) const;
+ void fill_treehash(BLI_mempool &treestore);
+};
+
+} // namespace blender::bke::outliner::treehash
diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index ffe80ff47b6..ed0969a6306 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -8,11 +8,14 @@
*/
#include "BLI_bitmap.h"
+#include "BLI_compiler_compat.h"
#include "BLI_utildefines.h"
+
#include "DNA_brush_enums.h"
#include "DNA_object_enums.h"
#include "BKE_attribute.h"
+#include "BKE_pbvh.h"
#ifdef __cplusplus
extern "C" {
@@ -59,10 +62,10 @@ struct bContext;
struct bToolRef;
struct tPaletteColorHSV;
-extern const char PAINT_CURSOR_SCULPT[3];
-extern const char PAINT_CURSOR_VERTEX_PAINT[3];
-extern const char PAINT_CURSOR_WEIGHT_PAINT[3];
-extern const char PAINT_CURSOR_TEXTURE_PAINT[3];
+extern const uchar PAINT_CURSOR_SCULPT[3];
+extern const uchar PAINT_CURSOR_VERTEX_PAINT[3];
+extern const uchar PAINT_CURSOR_WEIGHT_PAINT[3];
+extern const uchar PAINT_CURSOR_TEXTURE_PAINT[3];
typedef enum ePaintMode {
PAINT_MODE_SCULPT = 0,
@@ -97,6 +100,7 @@ typedef enum ePaintOverlayControlFlags {
PAINT_OVERLAY_OVERRIDE_PRIMARY = (1 << 5),
PAINT_OVERLAY_OVERRIDE_SECONDARY = (1 << 6),
} ePaintOverlayControlFlags;
+ENUM_OPERATORS(ePaintOverlayControlFlags, PAINT_OVERLAY_OVERRIDE_SECONDARY);
#define PAINT_OVERRIDE_MASK \
(PAINT_OVERLAY_OVERRIDE_SECONDARY | PAINT_OVERLAY_OVERRIDE_PRIMARY | \
@@ -156,7 +160,7 @@ struct PaintCurve *BKE_paint_curve_add(struct Main *bmain, const char *name);
* Call when entering each respective paint mode.
*/
bool BKE_paint_ensure(struct ToolSettings *ts, struct Paint **r_paint);
-void BKE_paint_init(struct Main *bmain, struct Scene *sce, ePaintMode mode, const char col[3]);
+void BKE_paint_init(struct Main *bmain, struct Scene *sce, ePaintMode mode, const uchar col[3]);
void BKE_paint_free(struct Paint *p);
/**
* Called when copying scene settings, so even if 'src' and 'tar' are the same still do a
@@ -200,15 +204,18 @@ bool BKE_paint_select_vert_test(struct Object *ob);
* (when we don't care if its face or vert)
*/
bool BKE_paint_select_elem_test(struct Object *ob);
+/**
+ * Checks if face/vertex hiding is always applied in the current mode.
+ * Returns true in vertex/weight paint.
+ */
+bool BKE_paint_always_hide_test(struct Object *ob);
/* Partial visibility. */
/**
* Returns non-zero if any of the face's vertices are hidden, zero otherwise.
*/
-bool paint_is_face_hidden(const struct MLoopTri *lt,
- const struct MVert *mvert,
- const struct MLoop *mloop);
+bool paint_is_face_hidden(const struct MLoopTri *lt, const bool *hide_poly);
/**
* Returns non-zero if any of the corners of the grid
* face whose inner corner is at (x, y) are hidden, zero otherwise.
@@ -392,10 +399,10 @@ typedef struct SculptVertexInfo {
typedef struct SculptBoundaryEditInfo {
/* Vertex index from where the topology propagation reached this vertex. */
- int original_vertex;
+ int original_vertex_i;
/* How many steps were needed to reach this vertex from the boundary. */
- int num_propagation_steps;
+ int propagation_steps_num;
/* Strength that is used to deform this vertex. */
float strength_factor;
@@ -403,15 +410,15 @@ typedef struct SculptBoundaryEditInfo {
/* Edge for drawing the boundary preview in the cursor. */
typedef struct SculptBoundaryPreviewEdge {
- int v1;
- int v2;
+ PBVHVertRef v1;
+ PBVHVertRef v2;
} SculptBoundaryPreviewEdge;
typedef struct SculptBoundary {
/* Vertex indices of the active boundary. */
- int *vertices;
- int vertices_capacity;
- int num_vertices;
+ PBVHVertRef *verts;
+ int verts_capacity;
+ int verts_num;
/* Distance from a vertex in the boundary to initial vertex indexed by vertex index, taking into
* account the length of all edges between them. Any vertex that is not in the boundary will have
@@ -421,18 +428,19 @@ typedef struct SculptBoundary {
/* Data for drawing the preview. */
SculptBoundaryPreviewEdge *edges;
int edges_capacity;
- int num_edges;
+ int edges_num;
/* True if the boundary loops into itself. */
bool forms_loop;
/* Initial vertex in the boundary which is closest to the current sculpt active vertex. */
- int initial_vertex;
+ PBVHVertRef initial_vertex;
+ int initial_vertex_i;
/* Vertex that at max_propagation_steps from the boundary and closest to the original active
* vertex that was used to initialize the boundary. This is used as a reference to check how much
* the deformation will go into the mesh and to calculate the strength of the brushes. */
- int pivot_vertex;
+ PBVHVertRef pivot_vertex;
/* Stores the initial positions of the pivot and boundary initial vertex as they may be deformed
* during the brush action. This allows to use them as a reference positions and vectors for some
@@ -478,6 +486,74 @@ typedef struct SculptFakeNeighbors {
/* Session data (mode-specific) */
+/* Custom Temporary Attributes */
+
+typedef struct SculptAttributeParams {
+ /* Allocate a flat array outside the CustomData system. Cannot be combined with permanent. */
+ int simple_array : 1;
+
+ /* Do not mark CustomData layer as temporary. Cannot be combined with simple_array. Doesn't
+ * work with PBVH_GRIDS.
+ */
+ int permanent : 1; /* Cannot be combined with simple_array. */
+ int stroke_only : 1; /* Release layer at end of struct */
+} SculptAttributeParams;
+
+typedef struct SculptAttribute {
+ /* Domain, data type and name */
+ eAttrDomain domain;
+ eCustomDataType proptype;
+ char name[MAX_CUSTOMDATA_LAYER_NAME];
+
+ /* Source layer on mesh/bmesh, if any. */
+ struct CustomDataLayer *layer;
+
+ /* Data stored as flat array. */
+ void *data;
+ int elem_size, elem_num;
+ bool data_for_bmesh; /* Temporary data store as array outside of bmesh. */
+
+ /* Data stored per BMesh element. */
+ int bmesh_cd_offset;
+
+ /* Sculpt usage */
+ SculptAttributeParams params;
+
+ /* Used to keep track of which preallocated SculptAttribute instances
+ * inside of SculptSession.temp_attribute are used.
+ */
+ bool used;
+} SculptAttribute;
+
+#define SCULPT_MAX_ATTRIBUTES 64
+
+/* Get a standard attribute name. Key must match up with a member
+ * of SculptAttributePointers.
+ */
+
+#define SCULPT_ATTRIBUTE_NAME(key) \
+ (offsetof(SculptAttributePointers, key) >= 0 ? /* Spellcheck name. */ \
+ (".sculpt_" #key) /* Make name. */ \
+ : \
+ "You misspelled the layer name key")
+
+/* Convenience pointers for standard sculpt attributes. */
+
+typedef struct SculptAttributePointers {
+ /* Persistent base. */
+ SculptAttribute *persistent_co;
+ SculptAttribute *persistent_no;
+ SculptAttribute *persistent_disp;
+
+ /* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
+ * initialized in #SCULPT_automasking_cache_init when needed. */
+ SculptAttribute *automasking_factor;
+
+ /* BMesh */
+ SculptAttribute *dyntopo_node_id_vertex;
+ SculptAttribute *dyntopo_node_id_face;
+} SculptAttributePointers;
+
typedef struct SculptSession {
/* Mesh data (not copied) can come either directly from a Mesh, or from a MultiresDM */
struct { /* Special handling for multires meshes */
@@ -491,8 +567,8 @@ typedef struct SculptSession {
/* These are always assigned to base mesh data when using PBVH_FACES and PBVH_GRIDS. */
struct MVert *mvert;
- struct MPoly *mpoly;
- struct MLoop *mloop;
+ const struct MPoly *mpoly;
+ const struct MLoop *mloop;
/* These contain the vertex and poly counts of the final mesh. */
int totvert, totpoly;
@@ -522,18 +598,20 @@ typedef struct SculptSession {
/* Mesh Face Sets */
/* 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.
- * The 0 ID is not used by the tools or the visibility system, it is just used when creating new
+
+ /* 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;
+ /**
+ * A reference to the ".hide_poly" attribute, to store whether (base) polygons are hidden.
+ * May be null.
+ */
+ bool *hide_poly;
/* BMesh for dynamic topology sculpting */
struct BMesh *bm;
- int cd_vert_node_offset;
- int cd_face_node_offset;
bool bm_smooth_shading;
/* Undo/redo log for dynamic topology sculpting */
struct BMLog *bm_log;
@@ -560,12 +638,13 @@ typedef struct SculptSession {
struct ExpandCache *expand_cache;
/* Cursor data and active vertex for tools */
- int active_vertex_index;
+ PBVHVertRef active_vertex;
int active_face_index;
int active_grid_index;
- /* When active, the cursor draws with faded colors, indicating that there is an action enabled.
+ /* When active, the cursor draws with faded colors, indicating that there is an action
+ * enabled.
*/
bool draw_faded_cursor;
float cursor_radius;
@@ -574,8 +653,10 @@ typedef struct SculptSession {
float cursor_sampled_normal[3];
float cursor_view_normal[3];
- /* For Sculpt trimming gesture tools, initial ray-cast data from the position of the mouse when
- * the gesture starts (intersection with the surface and if they ray hit the surface or not). */
+ /* For Sculpt trimming gesture tools, initial ray-cast data from the position of the mouse
+ * when
+ * the gesture starts (intersection with the surface and if they ray hit the surface or not).
+ */
float gesture_initial_location[3];
float gesture_initial_normal[3];
bool gesture_initial_hit;
@@ -586,8 +667,8 @@ typedef struct SculptSession {
struct Scene *scene;
/* Dynamic mesh preview */
- int *preview_vert_index_list;
- int preview_vert_index_count;
+ PBVHVertRef *preview_vert_list;
+ int preview_vert_count;
/* Pose Brush Preview */
float pose_origin[3];
@@ -596,10 +677,6 @@ typedef struct SculptSession {
/* Boundary Brush Preview */
SculptBoundary *boundary_preview;
- /* Mesh State Persistence */
- /* This is freed with the PBVH, so it is always in sync with the mesh. */
- SculptPersistentBase *persistent_base;
-
SculptVertexInfo vertex_info;
SculptFakeNeighbors fake_neighbors;
@@ -645,6 +722,14 @@ typedef struct SculptSession {
*/
char needs_flush_to_id;
+ /* This is a fixed-size array so we can pass pointers to its elements
+ * to client code. This is important to keep bmesh offsets up to date.
+ */
+ struct SculptAttribute temp_attributes[SCULPT_MAX_ATTRIBUTES];
+
+ /* Convenience #SculptAttribute pointers. */
+ SculptAttributePointers attrs;
+
/**
* Some tools follows the shading chosen by the last used tool canvas.
* When not set the viewport shading color would be used.
@@ -665,6 +750,75 @@ void BKE_sculptsession_free_deformMats(struct SculptSession *ss);
void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss);
void BKE_sculptsession_bm_to_me(struct Object *ob, bool reorder);
void BKE_sculptsession_bm_to_me_for_render(struct Object *object);
+int BKE_sculptsession_vertex_count(const SculptSession *ss);
+
+/* Ensure an attribute layer exists. */
+SculptAttribute *BKE_sculpt_attribute_ensure(struct Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name,
+ const SculptAttributeParams *params);
+
+/* Returns nullptr if attribute does not exist. */
+SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name);
+
+bool BKE_sculpt_attribute_exists(struct Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name);
+
+bool BKE_sculpt_attribute_destroy(struct Object *ob, SculptAttribute *attr);
+
+/* Destroy all attributes and pseudo-attributes created by sculpt mode. */
+void BKE_sculpt_attribute_destroy_temporary_all(struct Object *ob);
+
+/* Destroy attributes that were marked as stroke only in SculptAttributeParams. */
+void BKE_sculpt_attributes_destroy_temporary_stroke(struct Object *ob);
+
+BLI_INLINE void *BKE_sculpt_vertex_attr_get(const PBVHVertRef vertex, const SculptAttribute *attr)
+{
+ if (attr->data) {
+ char *p = (char *)attr->data;
+ int idx = (int)vertex.i;
+
+ if (attr->data_for_bmesh) {
+ BMElem *v = (BMElem *)vertex.i;
+ idx = v->head.index;
+ }
+
+ return p + attr->elem_size * (int)idx;
+ }
+ else {
+ BMElem *v = (BMElem *)vertex.i;
+ return BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset);
+ }
+
+ return NULL;
+}
+
+BLI_INLINE void *BKE_sculpt_face_attr_get(const PBVHFaceRef vertex, const SculptAttribute *attr)
+{
+ if (attr->data) {
+ char *p = (char *)attr->data;
+ int idx = (int)vertex.i;
+
+ if (attr->data_for_bmesh) {
+ BMElem *v = (BMElem *)vertex.i;
+ idx = v->head.index;
+ }
+
+ return p + attr->elem_size * (int)idx;
+ }
+ else {
+ BMElem *v = (BMElem *)vertex.i;
+ return BM_ELEM_CD_GET_VOID_P(v, attr->bmesh_cd_offset);
+ }
+
+ return NULL;
+}
/**
* Create new color layer on object if it doesn't have one and if experimental feature set has
@@ -680,7 +834,7 @@ void BKE_sculpt_update_object_for_edit(struct Depsgraph *depsgraph,
bool need_pmap,
bool need_mask,
bool is_paint_tool);
-void BKE_sculpt_update_object_before_eval(const struct Scene *scene, struct Object *ob_eval);
+void BKE_sculpt_update_object_before_eval(struct Object *ob_eval);
void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Object *ob_eval);
/**
@@ -689,6 +843,13 @@ void BKE_sculpt_update_object_after_eval(struct Depsgraph *depsgraph, struct Obj
*/
struct MultiresModifierData *BKE_sculpt_multires_active(const struct Scene *scene,
struct Object *ob);
+int *BKE_sculpt_face_sets_ensure(struct Mesh *mesh);
+/**
+ * Create the attribute used to store face visibility and retrieve its data.
+ * Note that changes to the face visibility have to be propagated to other domains
+ * (see #SCULPT_visibility_sync_all_from_faces).
+ */
+bool *BKE_sculpt_hide_poly_ensure(struct Mesh *mesh);
int BKE_sculpt_mask_layers_ensure(struct Object *ob, struct MultiresModifierData *mmd);
void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene);
@@ -696,32 +857,8 @@ struct PBVH *BKE_sculpt_object_pbvh_ensure(struct Depsgraph *depsgraph, struct O
void BKE_sculpt_bvh_update_from_ccg(struct PBVH *pbvh, struct SubdivCCG *subdiv_ccg);
-/**
- * This ensure that all elements in the mesh (both vertices and grids) have their visibility
- * updated according to the face sets.
- */
-void BKE_sculpt_sync_face_set_visibility(struct Mesh *mesh, struct SubdivCCG *subdiv_ccg);
-
-/**
- * Individual function to sync the Face Set visibility to mesh and grids.
- */
-void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(struct Mesh *mesh);
-void BKE_sculpt_sync_face_sets_visibility_to_grids(struct Mesh *mesh,
- struct SubdivCCG *subdiv_ccg);
-
-/**
- * Ensures that a Face Set data-layers exists. If it does not, it creates one respecting the
- * visibility stored in the vertices of the mesh. If it does, it copies the visibility from the
- * mesh to the Face Sets. */
-void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(struct Mesh *mesh);
-
-/**
- * Ensures we do have expected mesh data in original mesh for the sculpt mode.
- *
- * \note IDs are expected to be original ones here, and calling code should ensure it updates its
- * depsgraph properly after calling this function if it needs up-to-date evaluated data.
- */
void BKE_sculpt_ensure_orig_mesh_data(struct Scene *scene, struct Object *object);
+void BKE_sculpt_sync_face_visibility_to_grids(struct Mesh *mesh, struct SubdivCCG *subdiv_ccg);
/**
* Test if PBVH can be used directly for drawing, which is faster than
diff --git a/source/blender/blenkernel/BKE_particle.h b/source/blender/blenkernel/BKE_particle.h
index 5f8b2fafdd3..a797aef73f6 100644
--- a/source/blender/blenkernel/BKE_particle.h
+++ b/source/blender/blenkernel/BKE_particle.h
@@ -579,7 +579,7 @@ void psys_get_texture(struct ParticleSimulationData *sim,
* Interpolate a location on a face based on face coordinates.
*/
void psys_interpolate_face(struct Mesh *mesh,
- struct MVert *mvert,
+ const struct MVert *mvert,
const float (*vert_normals)[3],
struct MFace *mface,
struct MTFace *tface,
diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h
index f517ff3a949..ff2140732cc 100644
--- a/source/blender/blenkernel/BKE_pbvh.h
+++ b/source/blender/blenkernel/BKE_pbvh.h
@@ -8,8 +8,11 @@
*/
#include "BLI_bitmap.h"
+#include "BLI_compiler_compat.h"
#include "BLI_ghash.h"
+#include "bmesh.h"
+
/* For embedding CCGKey in iterator. */
#include "BKE_attribute.h"
#include "BKE_ccg.h"
@@ -43,6 +46,41 @@ struct MeshElemMap;
typedef struct PBVH PBVH;
typedef struct PBVHNode PBVHNode;
+typedef enum {
+ PBVH_FACES,
+ PBVH_GRIDS,
+ PBVH_BMESH,
+} PBVHType;
+
+/* Public members of PBVH, used for inlined functions. */
+struct PBVHPublic {
+ PBVHType type;
+ BMesh *bm;
+};
+
+/*
+ * These structs represent logical verts/edges/faces.
+ * for PBVH_GRIDS and PBVH_FACES they store integer
+ * offsets, PBVH_BMESH stores pointers.
+ *
+ * The idea is to enforce stronger type checking by encapsulating
+ * intptr_t's in structs.
+ */
+
+typedef struct PBVHVertRef {
+ intptr_t i;
+} PBVHVertRef;
+
+typedef struct PBVHEdgeRef {
+ intptr_t i;
+} PBVHEdgeRef;
+
+typedef struct PBVHFaceRef {
+ intptr_t i;
+} PBVHFaceRef;
+
+#define PBVH_REF_NONE -1LL
+
typedef struct {
float (*co)[3];
} PBVHProxyNode;
@@ -87,9 +125,97 @@ typedef struct PBVHFrustumPlanes {
int num_planes;
} PBVHFrustumPlanes;
+BLI_INLINE PBVHType BKE_pbvh_type(const PBVH *pbvh)
+{
+ return ((const struct PBVHPublic *)pbvh)->type;
+}
+
+BLI_INLINE BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh)
+{
+ return ((struct PBVHPublic *)pbvh)->bm;
+}
+
void BKE_pbvh_set_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes);
void BKE_pbvh_get_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes);
+BLI_INLINE PBVHVertRef BKE_pbvh_make_vref(intptr_t i)
+{
+ PBVHVertRef ret = {i};
+ return ret;
+}
+
+BLI_INLINE PBVHEdgeRef BKE_pbvh_make_eref(intptr_t i)
+{
+ PBVHEdgeRef ret = {i};
+ return ret;
+}
+
+BLI_INLINE PBVHFaceRef BKE_pbvh_make_fref(intptr_t i)
+{
+ PBVHFaceRef ret = {i};
+ return ret;
+}
+
+BLI_INLINE int BKE_pbvh_vertex_to_index(PBVH *pbvh, PBVHVertRef v)
+{
+ return (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != PBVH_REF_NONE ?
+ BM_elem_index_get((BMVert *)(v.i)) :
+ (v.i));
+}
+
+BLI_INLINE PBVHVertRef BKE_pbvh_index_to_vertex(PBVH *pbvh, int index)
+{
+ switch (BKE_pbvh_type(pbvh)) {
+ case PBVH_FACES:
+ case PBVH_GRIDS:
+ return BKE_pbvh_make_vref(index);
+ case PBVH_BMESH:
+ return BKE_pbvh_make_vref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->vtable[index]);
+ }
+
+ return BKE_pbvh_make_vref(PBVH_REF_NONE);
+}
+
+BLI_INLINE int BKE_pbvh_edge_to_index(PBVH *pbvh, PBVHEdgeRef e)
+{
+ return (BKE_pbvh_type(pbvh) == PBVH_BMESH && e.i != PBVH_REF_NONE ?
+ BM_elem_index_get((BMEdge *)(e.i)) :
+ (e.i));
+}
+
+BLI_INLINE PBVHEdgeRef BKE_pbvh_index_to_edge(PBVH *pbvh, int index)
+{
+ switch (BKE_pbvh_type(pbvh)) {
+ case PBVH_FACES:
+ case PBVH_GRIDS:
+ return BKE_pbvh_make_eref(index);
+ case PBVH_BMESH:
+ return BKE_pbvh_make_eref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->etable[index]);
+ }
+
+ return BKE_pbvh_make_eref(PBVH_REF_NONE);
+}
+
+BLI_INLINE int BKE_pbvh_face_to_index(PBVH *pbvh, PBVHFaceRef f)
+{
+ return (BKE_pbvh_type(pbvh) == PBVH_BMESH && f.i != PBVH_REF_NONE ?
+ BM_elem_index_get((BMFace *)(f.i)) :
+ (f.i));
+}
+
+BLI_INLINE PBVHFaceRef BKE_pbvh_index_to_face(PBVH *pbvh, int index)
+{
+ switch (BKE_pbvh_type(pbvh)) {
+ case PBVH_FACES:
+ case PBVH_GRIDS:
+ return BKE_pbvh_make_fref(index);
+ case PBVH_BMESH:
+ return BKE_pbvh_make_fref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->ftable[index]);
+ }
+
+ return BKE_pbvh_make_fref(PBVH_REF_NONE);
+}
+
/* Callbacks */
/**
@@ -104,7 +230,7 @@ typedef void (*BKE_pbvh_SearchNearestCallback)(PBVHNode *node, void *data, float
/* Building */
-PBVH *BKE_pbvh_new(void);
+PBVH *BKE_pbvh_new(PBVHType type);
/**
* Do a full rebuild with on Mesh data structure.
*
@@ -142,6 +268,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
int cd_vert_node_offset,
int cd_face_node_offset);
+void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset);
+
void BKE_pbvh_build_pixels(PBVH *pbvh,
struct Mesh *mesh,
struct Image *image,
@@ -181,7 +309,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
float *depth,
- int *active_vertex_index,
+ PBVHVertRef *active_vertex,
int *active_face_grid_index,
float *face_normal);
@@ -224,19 +352,16 @@ void BKE_pbvh_draw_cb(PBVH *pbvh,
void *user_data,
bool full_render);
-void BKE_pbvh_draw_debug_cb(
- PBVH *pbvh,
- void (*draw_fn)(void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag),
- void *user_data);
+void BKE_pbvh_draw_debug_cb(PBVH *pbvh,
+ void (*draw_fn)(PBVHNode *node,
+ void *user_data,
+ const float bmin[3],
+ const float bmax[3],
+ PBVHNodeFlags flag),
+ void *user_data);
/* PBVH Access */
-typedef enum {
- PBVH_FACES,
- PBVH_GRIDS,
- PBVH_BMESH,
-} PBVHType;
-PBVHType BKE_pbvh_type(const PBVH *pbvh);
bool BKE_pbvh_has_faces(const PBVH *pbvh);
/**
@@ -257,8 +382,6 @@ int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden,
int totgrid,
int gridsize);
-void BKE_pbvh_sync_face_sets_to_grids(PBVH *pbvh);
-
/**
* Multi-res level, only valid for type == #PBVH_GRIDS.
*/
@@ -266,13 +389,12 @@ const struct CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh);
struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh);
BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh);
-int BKE_pbvh_get_grid_num_vertices(const PBVH *pbvh);
+int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh);
int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh);
/**
* Only valid for type == #PBVH_BMESH.
*/
-struct BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh);
void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size);
typedef enum {
@@ -308,7 +430,7 @@ void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked);
bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node);
void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh);
-void BKE_pbvh_vert_mark_update(PBVH *pbvh, int index);
+void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex);
void BKE_pbvh_node_get_grids(PBVH *pbvh,
PBVHNode *node,
@@ -369,6 +491,12 @@ void BKE_pbvh_grids_update(PBVH *pbvh,
void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, struct SubdivCCG *subdiv_ccg);
void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets);
+/**
+ * If an operation causes the hide status stored in the mesh to change, this must be called
+ * to update the references to those attributes, since they are only added when necessary.
+ */
+void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh);
+
void BKE_pbvh_face_sets_color_set(PBVH *pbvh, int seed, int color_default);
void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide);
@@ -399,6 +527,7 @@ typedef struct PBVHVertexIter {
int gy;
int i;
int index;
+ PBVHVertRef vertex;
bool respect_hide;
/* grid */
@@ -413,6 +542,7 @@ typedef struct PBVHVertexIter {
/* mesh */
struct MVert *mverts;
float (*vert_normals)[3];
+ const bool *hide_vert;
int totvert;
const int *vert_indices;
float *vmask;
@@ -443,7 +573,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
if (vi.grids) { \
vi.width = vi.gridsize; \
vi.height = vi.gridsize; \
- vi.index = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \
+ vi.index = vi.vertex.i = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \
vi.grid = vi.grids[vi.grid_indices[vi.g]]; \
if (mode == PBVH_ITER_UNIQUE) { \
vi.gh = vi.grid_hidden[vi.grid_indices[vi.g]]; \
@@ -462,6 +592,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi.mask = vi.key.has_mask ? CCG_elem_mask(&vi.key, vi.grid) : NULL; \
vi.grid = CCG_elem_next(&vi.key, vi.grid); \
vi.index++; \
+ vi.vertex.i++; \
vi.visible = true; \
if (vi.gh) { \
if (BLI_BITMAP_TEST(vi.gh, vi.gy * vi.gridsize + vi.gx)) { \
@@ -472,7 +603,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
else if (vi.mverts) { \
vi.mvert = &vi.mverts[vi.vert_indices[vi.gx]]; \
if (vi.respect_hide) { \
- vi.visible = !(vi.mvert->flag & ME_HIDE); \
+ vi.visible = !(vi.hide_vert && vi.hide_vert[vi.vert_indices[vi.gx]]); \
if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \
continue; \
} \
@@ -482,7 +613,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
} \
vi.co = vi.mvert->co; \
vi.no = vi.vert_normals[vi.vert_indices[vi.gx]]; \
- vi.index = vi.vert_indices[vi.i]; \
+ vi.index = vi.vertex.i = vi.vert_indices[vi.i]; \
if (vi.vmask) { \
vi.mask = &vi.vmask[vi.index]; \
} \
@@ -502,6 +633,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
} \
vi.co = vi.bm_vert->co; \
vi.fno = vi.bm_vert->no; \
+ vi.vertex = BKE_pbvh_make_vref((intptr_t)vi.bm_vert); \
vi.index = BM_elem_index_get(vi.bm_vert); \
vi.mask = (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \
}
@@ -526,7 +658,7 @@ void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node,
* however this is important to avoid having to recalculate bound-box & sync the buffers to the
* GPU (which is far more expensive!) See: T47232.
*/
-bool BKE_pbvh_node_vert_update_check_any(PBVH *pbvh, PBVHNode *node);
+bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node);
// void BKE_pbvh_node_BB_reset(PBVHNode *node);
// void BKE_pbvh_node_BB_expand(PBVHNode *node, float co[3]);
@@ -545,6 +677,10 @@ void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings,
struct MVert *BKE_pbvh_get_verts(const PBVH *pbvh);
const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3];
+const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh);
+bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh);
+
+const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh);
PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node);
void BKE_pbvh_node_color_buffer_free(PBVH *pbvh);
@@ -581,11 +717,12 @@ void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop);
void BKE_pbvh_update_active_vcol(PBVH *pbvh, const struct Mesh *mesh);
void BKE_pbvh_pmap_set(PBVH *pbvh, const struct MeshElemMap *pmap);
-void BKE_pbvh_vertex_color_set(PBVH *pbvh, int vertex, const float color[4]);
-void BKE_pbvh_vertex_color_get(const PBVH *pbvh, int vertex, float r_color[4]);
+void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color[4]);
+void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_color[4]);
void BKE_pbvh_ensure_node_loops(PBVH *pbvh);
bool BKE_pbvh_draw_cache_invalid(const PBVH *pbvh);
+int BKE_pbvh_debug_draw_gen_get(PBVHNode *node);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_pbvh_pixels.hh b/source/blender/blenkernel/BKE_pbvh_pixels.hh
index e73950e6299..ad8eca2b36f 100644
--- a/source/blender/blenkernel/BKE_pbvh_pixels.hh
+++ b/source/blender/blenkernel/BKE_pbvh_pixels.hh
@@ -186,8 +186,14 @@ struct NodeData {
{
UDIMTilePixels *tile = find_tile_data(image_tile);
if (tile && tile->flags.dirty) {
- BKE_image_partial_update_mark_region(
- &image, image_tile.image_tile, &image_buffer, &tile->dirty_region);
+ if (image_buffer.planes == 8) {
+ image_buffer.planes = 32;
+ BKE_image_partial_update_mark_full_update(&image);
+ }
+ else {
+ BKE_image_partial_update_mark_region(
+ &image, image_tile.image_tile, &image_buffer, &tile->dirty_region);
+ }
tile->clear_dirty();
}
}
diff --git a/source/blender/blenkernel/BKE_pointcache.h b/source/blender/blenkernel/BKE_pointcache.h
index 48402123365..efb8c2a9bb1 100644
--- a/source/blender/blenkernel/BKE_pointcache.h
+++ b/source/blender/blenkernel/BKE_pointcache.h
@@ -285,7 +285,7 @@ void BKE_ptcache_ids_from_object(struct ListBase *lb,
struct Scene *scene,
int duplis);
-/****************** Query funcs ****************************/
+/****************** Query functions ****************************/
/**
* Check whether object has a point cache.
diff --git a/source/blender/blenkernel/BKE_pointcloud.h b/source/blender/blenkernel/BKE_pointcloud.h
index 6dbba11a56d..d6367ac5a61 100644
--- a/source/blender/blenkernel/BKE_pointcloud.h
+++ b/source/blender/blenkernel/BKE_pointcloud.h
@@ -25,17 +25,17 @@ extern const char *POINTCLOUD_ATTR_RADIUS;
void *BKE_pointcloud_add(struct Main *bmain, const char *name);
void *BKE_pointcloud_add_default(struct Main *bmain, const char *name);
struct PointCloud *BKE_pointcloud_new_nomain(int totpoint);
+void BKE_pointcloud_nomain_to_pointcloud(struct PointCloud *pointcloud_src,
+ struct PointCloud *pointcloud_dst,
+ bool take_ownership);
struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob);
bool BKE_pointcloud_minmax(const struct PointCloud *pointcloud, float r_min[3], float r_max[3]);
-void BKE_pointcloud_update_customdata_pointers(struct PointCloud *pointcloud);
-bool BKE_pointcloud_customdata_required(const struct PointCloud *pointcloud, const char *name);
+bool BKE_pointcloud_attribute_required(const struct PointCloud *pointcloud, const char *name);
/* Dependency Graph */
-struct PointCloud *BKE_pointcloud_new_for_eval(const struct PointCloud *pointcloud_src,
- int totpoint);
struct PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool reference);
void BKE_pointcloud_data_update(struct Depsgraph *depsgraph,
diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h
index a6402a1e8a1..c6ccd4493fe 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -97,7 +97,7 @@ int BKE_scene_base_iter_next(struct Depsgraph *depsgraph,
struct Base **base,
struct Object **ob);
-void BKE_scene_base_flag_to_objects(struct ViewLayer *view_layer);
+void BKE_scene_base_flag_to_objects(const struct Scene *scene, struct ViewLayer *view_layer);
/**
* Synchronize object base flags
*
@@ -251,6 +251,10 @@ bool BKE_scene_check_rigidbody_active(const struct Scene *scene);
int BKE_scene_num_threads(const struct Scene *scene);
int BKE_render_num_threads(const struct RenderData *r);
+void BKE_render_resolution(const struct RenderData *r,
+ const bool use_crop,
+ int *r_width,
+ int *r_height);
int BKE_render_preview_pixel_size(const struct RenderData *r);
/**********************************/
diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h
index 3922bfb6c0d..62b04785983 100644
--- a/source/blender/blenkernel/BKE_screen.h
+++ b/source/blender/blenkernel/BKE_screen.h
@@ -55,7 +55,7 @@ struct wmWindowManager;
typedef struct wmSpaceTypeListenerParams {
struct wmWindow *window;
struct ScrArea *area;
- struct wmNotifier *notifier;
+ const struct wmNotifier *notifier;
const struct Scene *scene;
} wmSpaceTypeListenerParams;
@@ -113,7 +113,7 @@ typedef struct SpaceType {
/* read and write... */
- /* default keymaps to add */
+ /** Default key-maps to add. */
int keymapflag;
} SpaceType;
@@ -124,7 +124,7 @@ typedef struct wmRegionListenerParams {
struct wmWindow *window;
struct ScrArea *area; /* Can be NULL when the region is not part of an area. */
struct ARegion *region;
- struct wmNotifier *notifier;
+ const struct wmNotifier *notifier;
const struct Scene *scene;
} wmRegionListenerParams;
@@ -291,7 +291,7 @@ enum {
/* Draw an item in the uiList */
typedef void (*uiListDrawItemFunc)(struct uiList *ui_list,
- struct bContext *C,
+ const struct bContext *C,
struct uiLayout *layout,
struct PointerRNA *dataptr,
struct PointerRNA *itemptr,
@@ -303,12 +303,12 @@ typedef void (*uiListDrawItemFunc)(struct uiList *ui_list,
/* Draw the filtering part of an uiList */
typedef void (*uiListDrawFilterFunc)(struct uiList *ui_list,
- struct bContext *C,
+ const struct bContext *C,
struct uiLayout *layout);
/* Filter items of an uiList */
typedef void (*uiListFilterItemsFunc)(struct uiList *ui_list,
- struct bContext *C,
+ const struct bContext *C,
struct PointerRNA *,
const char *propname);
diff --git a/source/blender/blenkernel/BKE_shrinkwrap.h b/source/blender/blenkernel/BKE_shrinkwrap.h
index 3e06bd84805..b4f87e6fc73 100644
--- a/source/blender/blenkernel/BKE_shrinkwrap.h
+++ b/source/blender/blenkernel/BKE_shrinkwrap.h
@@ -30,6 +30,7 @@ struct BVHTree;
struct MDeformVert;
struct Mesh;
struct ModifierEvalContext;
+struct MPoly;
struct Object;
struct ShrinkwrapGpencilModifierData;
struct ShrinkwrapModifierData;
@@ -72,6 +73,7 @@ typedef struct ShrinkwrapTreeData {
BVHTree *bvh;
BVHTreeFromMesh treeData;
+ const struct MPoly *polys;
const float (*pnors)[3];
const float (*clnors)[3];
ShrinkwrapBoundaryData *boundary;
@@ -104,7 +106,7 @@ void shrinkwrapModifier_deform(struct ShrinkwrapModifierData *smd,
struct Scene *scene,
struct Object *ob,
struct Mesh *mesh,
- struct MDeformVert *dvert,
+ const struct MDeformVert *dvert,
int defgrp_index,
float (*vertexCos)[3],
int numVerts);
diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h
index 9965b6f1351..11c37a74a54 100644
--- a/source/blender/blenkernel/BKE_sound.h
+++ b/source/blender/blenkernel/BKE_sound.h
@@ -134,7 +134,7 @@ void *BKE_sound_add_scene_sound_defaults(struct Scene *scene, struct Sequence *s
void BKE_sound_remove_scene_sound(struct Scene *scene, void *handle);
-void BKE_sound_mute_scene_sound(void *handle, char mute);
+void BKE_sound_mute_scene_sound(void *handle, bool mute);
void BKE_sound_move_scene_sound(const struct Scene *scene,
void *handle,
diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh
deleted file mode 100644
index 767018ae0bb..00000000000
--- a/source/blender/blenkernel/BKE_spline.hh
+++ /dev/null
@@ -1,688 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-/** \file
- * \ingroup bke
- */
-
-#include <mutex>
-
-#include "DNA_curves_types.h"
-
-#include "BLI_float4x4.hh"
-#include "BLI_generic_virtual_array.hh"
-#include "BLI_math_vec_types.hh"
-#include "BLI_vector.hh"
-
-#include "BKE_attribute.hh"
-#include "BKE_attribute_math.hh"
-
-struct Curve;
-struct Curves;
-struct ListBase;
-
-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. Conceptually, the derived vs. original data is
- * an essential distinction. Derived data is usually calculated lazily and cached on the spline.
- *
- * 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 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:
- NormalMode normal_mode = NORMAL_MODE_MINIMUM_TWIST;
-
- blender::bke::CustomDataAttributes attributes;
-
- protected:
- CurveType 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 CurveType type) : type_(type)
- {
- }
- Spline(Spline &other) : attributes(other.attributes), type_(other.type_)
- {
- copy_base_settings(other, *this);
- }
-
- /**
- * Return a new spline with the same data, settings, and attributes.
- */
- SplinePtr copy() const;
- /**
- * Return a new spline with the same type and settings like "cyclic", but without any data.
- */
- SplinePtr copy_only_settings() const;
- /**
- * The same as #copy, but skips copying dynamic attributes to the new spline.
- */
- SplinePtr copy_without_attributes() const;
- static void copy_base_settings(const Spline &src, Spline &dst);
-
- CurveType type() const;
-
- /** Return the number of control points. */
- virtual int size() const = 0;
- int segments_num() const;
- bool is_cyclic() const;
- void set_cyclic(bool value);
-
- virtual void resize(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);
-
- /**
- * Change the direction of the spline (switch the start and end) without changing its shape.
- */
- void reverse();
-
- /**
- * 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_num() const = 0;
- int evaluated_edges_num() const;
-
- float length() const;
-
- virtual blender::Span<blender::float3> evaluated_positions() const = 0;
-
- /**
- * 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, and it is necessarily single threaded, so it
- * is cached.
- */
- blender::Span<float> evaluated_lengths() const;
- /**
- * Return non-owning access to the direction of the curve at each evaluated point.
- */
- blender::Span<blender::float3> evaluated_tangents() const;
- /**
- * 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.
- */
- blender::Span<blender::float3> evaluated_normals() const;
-
- void bounds_min_max(blender::float3 &min, blender::float3 &max, 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_num - 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;
- };
- /**
- * Find the position on the evaluated spline at the given portion of the total length.
- * The return value is the indices of the two neighboring points at that location and the
- * factor between them, which can be used to look up any attribute on the evaluated points.
- * \note This does not support extrapolation.
- */
- LookupResult lookup_evaluated_factor(float factor) const;
- /**
- * The same as #lookup_evaluated_factor, but looks up a length directly instead of
- * a portion of the total.
- */
- LookupResult lookup_evaluated_length(float length) const;
-
- /**
- * 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.
- */
- blender::Array<float> sample_uniform_index_factors(int samples_num) const;
- LookupResult lookup_data_from_index_factor(float index_factor) const;
-
- /**
- * 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 sample_with_index_factors(const blender::GVArray &src,
- blender::Span<float> index_factors,
- blender::GMutableSpan dst) const;
- template<typename T>
- void sample_with_index_factors(const blender::VArray<T> &src,
- blender::Span<float> index_factors,
- blender::MutableSpan<T> dst) const
- {
- this->sample_with_index_factors(
- blender::GVArray(src), index_factors, blender::GMutableSpan(dst));
- }
- template<typename T>
- void sample_with_index_factors(blender::Span<T> src,
- blender::Span<float> index_factors,
- blender::MutableSpan<T> dst) const
- {
- this->sample_with_index_factors(blender::VArray<T>::ForSpan(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::GVArray interpolate_to_evaluated(const blender::GVArray &src) const = 0;
- blender::GVArray interpolate_to_evaluated(blender::GSpan data) const;
- template<typename T> blender::VArray<T> interpolate_to_evaluated(blender::Span<T> data) const
- {
- return this->interpolate_to_evaluated(blender::GSpan(data)).typed<T>();
- }
-
- protected:
- virtual void correct_end_tangents() const = 0;
- virtual void copy_settings(Spline &dst) const = 0;
- virtual void copy_data(Spline &dst) const = 0;
- virtual void reverse_impl() = 0;
-};
-
-/**
- * A Bezier 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 {
- blender::Vector<blender::float3> positions_;
- blender::Vector<float> radii_;
- blender::Vector<float> tilts_;
- int resolution_;
-
- blender::Vector<int8_t> handle_types_left_;
- blender::Vector<int8_t> 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:
- BezierSpline() : Spline(CURVE_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(int value);
-
- void resize(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<int8_t> handle_types_left() const;
- blender::MutableSpan<int8_t> handle_types_left();
- blender::Span<blender::float3> handle_positions_left() const;
- /**
- * Get writable access to the handle position.
- *
- * \param write_only: pass true for an uninitialized spline, this prevents accessing
- * uninitialized memory while auto-generating handles.
- */
- blender::MutableSpan<blender::float3> handle_positions_left(bool write_only = false);
- blender::Span<int8_t> handle_types_right() const;
- blender::MutableSpan<int8_t> handle_types_right();
- blender::Span<blender::float3> handle_positions_right() const;
- /**
- * Get writable access to the handle position.
- *
- * \param write_only: pass true for an uninitialized spline, this prevents accessing
- * uninitialized memory while auto-generating handles.
- */
- blender::MutableSpan<blender::float3> handle_positions_right(bool write_only = false);
- /**
- * Recalculate all #Auto and #Vector handles with positions automatically
- * derived from the neighboring control points.
- */
- void ensure_auto_handles() const;
-
- void translate(const blender::float3 &translation) override;
- void transform(const blender::float4x4 &matrix) override;
-
- /**
- * Set positions for the right handle of the control point, ensuring that
- * aligned handles stay aligned. Has no effect for auto and vector type handles.
- */
- void set_handle_position_right(int index, const blender::float3 &value);
- /**
- * Set positions for the left handle of the control point, ensuring that
- * aligned handles stay aligned. Has no effect for auto and vector type handles.
- */
- void set_handle_position_left(int index, const blender::float3 &value);
-
- bool point_is_sharp(int index) const;
-
- void mark_cache_invalid() final;
- int evaluated_points_num() const final;
-
- /**
- * 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.
- */
- blender::Span<int> control_point_offsets() const;
- /**
- * 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.
- */
- 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;
- };
- /**
- * 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.
- */
- InterpolationData interpolation_data_from_index_factor(float index_factor) const;
-
- virtual blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const override;
-
- void evaluate_segment(int index,
- int next_index,
- blender::MutableSpan<blender::float3> positions) const;
- /**
- * \warning This functional assumes that the spline has more than one point.
- */
- bool segment_is_vector(int start_index) const;
-
- /** See comment and diagram for #calculate_segment_insertion. */
- struct InsertResult {
- blender::float3 handle_prev;
- blender::float3 left_handle;
- blender::float3 position;
- blender::float3 right_handle;
- blender::float3 handle_next;
- };
- /**
- * De Casteljau Bezier subdivision.
- * \param index: The index of the segment's start control point.
- * \param next_index: The index of the control point at the end of the segment. Could be 0,
- * if the spline is cyclic.
- * \param parameter: The factor along the segment, between 0 and 1. Note that this is used
- * directly by the calculation, it doesn't correspond to a portion of the evaluated length.
- *
- * <pre>
- * handle_prev handle_next
- * x----------------x
- * / \
- * / x---O---x \
- * / result \
- * / \
- * O O
- * point_prev point_next
- * </pre>
- */
- InsertResult calculate_segment_insertion(int index, int next_index, float parameter);
-
- private:
- /**
- * 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 correct_end_tangents() const final;
- void copy_settings(Spline &dst) const final;
- void copy_data(Spline &dst) const final;
-
- protected:
- void reverse_impl() override;
-};
-
-/**
- * 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:
- /** Method used to recalculate the knots vector when points are added or removed. */
- KnotsMode knots_mode;
-
- struct BasisCache {
- /**
- * For each evaluated point, the weight for all control points that influences it.
- * The vector's size is the evaluated point count multiplied by the spline's order.
- */
- blender::Vector<float> weights;
- /**
- * An offset for the start of #weights: the first control point index with a non-zero weight.
- */
- blender::Vector<int> start_indices;
- };
-
- 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_num(), 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 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:
- NURBSpline() : Spline(CURVE_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(int value);
- uint8_t order() const;
- void set_order(uint8_t value);
-
- bool check_valid_num_and_order() const;
- int knots_num() const;
-
- void resize(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_num() const final;
-
- blender::Span<blender::float3> evaluated_positions() const final;
-
- blender::GVArray interpolate_to_evaluated(const blender::GVArray &src) const final;
-
- protected:
- void correct_end_tangents() const final;
- void copy_settings(Spline &dst) const final;
- void copy_data(Spline &dst) const final;
- void reverse_impl() override;
-
- void calculate_knots() const;
- const BasisCache &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.
- *
- * Poly spline code is very simple, since it doesn't do anything that the base #Spline doesn't
- * handle. Mostly it just worries about storing the data used by the base class.
- */
-class PolySpline final : public Spline {
- blender::Vector<blender::float3> positions_;
- blender::Vector<float> radii_;
- blender::Vector<float> tilts_;
-
- public:
- PolySpline() : Spline(CURVE_TYPE_POLY)
- {
- }
- PolySpline(const PolySpline &other)
- : Spline((Spline &)other),
- positions_(other.positions_),
- radii_(other.radii_),
- tilts_(other.tilts_)
- {
- }
-
- int size() const final;
-
- void resize(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_num() const final;
-
- blender::Span<blender::float3> evaluated_positions() const final;
-
- /**
- * 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::GVArray interpolate_to_evaluated(const blender::GVArray &src) const final;
-
- protected:
- void correct_end_tangents() const final;
- void copy_settings(Spline &dst) const final;
- void copy_data(Spline &dst) const final;
- void reverse_impl() override;
-};
-
-/**
- * A collection of #Spline objects with the same attribute types and names. Most data and
- * functionality is in splines, but this contains some helpers for working with them as a group.
- *
- * \note 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();
- /**
- * \return True if the curve contains a spline with the given type.
- *
- * \note If you are looping over all of the splines in the same scope anyway,
- * it's better to avoid calling this function, in case there are many splines.
- */
- bool has_spline_with_type(const CurveType type) const;
-
- void resize(int size);
- /**
- * \warning Call #reallocate on the spline's attributes after adding all splines.
- */
- void add_spline(SplinePtr spline);
- void add_splines(blender::MutableSpan<SplinePtr> splines);
- void remove_splines(blender::IndexMask mask);
-
- void translate(const blender::float3 &translation);
- void transform(const blender::float4x4 &matrix);
- bool bounds_min_max(blender::float3 &min, blender::float3 &max, bool use_evaluated) const;
-
- blender::bke::MutableAttributeAccessor attributes_for_write();
-
- /**
- * Return the start indices for each of the curve spline's control points, 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 is one longer than the spline count; the last element is the total size.
- */
- blender::Array<int> control_point_offsets() const;
- /**
- * Exactly like #control_point_offsets, but uses the number of evaluated points instead.
- */
- blender::Array<int> evaluated_point_offsets() const;
- /**
- * Return the accumulated length at the start of every spline in the curve.
- * \note The result is one longer than the spline count; the last element is the total length.
- */
- blender::Array<float> accumulated_spline_lengths() const;
-
- float total_length() const;
- int total_control_point_num() const;
-
- void mark_cache_invalid();
-
- /**
- * 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.
- * - Attributes are in the same order on every spline.
- */
- void assert_valid_point_attributes() const;
-};
-
-std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &curve,
- const ListBase &nurbs_list);
-std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve);
-std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves);
-Curves *curve_eval_to_curves(const CurveEval &curve_eval);
diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h
index b30b707759c..ced7ff2aa71 100644
--- a/source/blender/blenkernel/BKE_subdiv_ccg.h
+++ b/source/blender/blenkernel/BKE_subdiv_ccg.h
@@ -325,6 +325,7 @@ const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG *subdiv_ccg);
const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG *subdiv_ccg);
void BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG *subdiv_ccg, int grid_index);
+void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG *subdiv_ccg, int grid_index);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_subdiv_eval.h b/source/blender/blenkernel/BKE_subdiv_eval.h
index cb0f2ac7e32..e4a7f813a8b 100644
--- a/source/blender/blenkernel/BKE_subdiv_eval.h
+++ b/source/blender/blenkernel/BKE_subdiv_eval.h
@@ -20,7 +20,7 @@ struct Subdiv;
typedef enum eSubdivEvaluatorType {
SUBDIV_EVALUATOR_TYPE_CPU,
- SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE,
+ SUBDIV_EVALUATOR_TYPE_GPU,
} eSubdivEvaluatorType;
/* Returns true if evaluator is ready for use. */
diff --git a/source/blender/blenkernel/BKE_subdiv_mesh.h b/source/blender/blenkernel/BKE_subdiv_mesh.h
index b24db517143..49c45efafe0 100644
--- a/source/blender/blenkernel/BKE_subdiv_mesh.h
+++ b/source/blender/blenkernel/BKE_subdiv_mesh.h
@@ -14,7 +14,9 @@ extern "C" {
#endif
struct Mesh;
+struct MeshElemMap;
struct MEdge;
+struct MVert;
struct Subdiv;
typedef struct SubdivToMeshSettings {
@@ -37,8 +39,10 @@ struct Mesh *BKE_subdiv_to_mesh(struct Subdiv *subdiv,
/* Interpolate a position along the `coarse_edge` at the relative `u` coordinate. If `is_simple` is
* false, this will perform a B-Spline interpolation using the edge neighbors, otherwise a linear
* interpolation will be done base on the edge vertices. */
-void BKE_subdiv_mesh_interpolate_position_on_edge(const struct Mesh *coarse_mesh,
- const struct MEdge *coarse_edge,
+void BKE_subdiv_mesh_interpolate_position_on_edge(const struct MVert *coarse_verts,
+ const struct MEdge *coarse_edges,
+ const struct MeshElemMap *vert_to_edge_map,
+ int coarse_edge_index,
bool is_simple,
float u,
float pos_r[3]);
diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h
index bc578ef8b28..8549cd14b3c 100644
--- a/source/blender/blenkernel/BKE_volume.h
+++ b/source/blender/blenkernel/BKE_volume.h
@@ -114,6 +114,7 @@ int BKE_volume_grid_channels(const struct VolumeGrid *grid);
* Transformation from index space to object space.
*/
void BKE_volume_grid_transform_matrix(const struct VolumeGrid *grid, float mat[4][4]);
+void BKE_volume_grid_transform_matrix_set(struct VolumeGrid *volume_grid, const float mat[4][4]);
/* Volume Editing
*
@@ -133,6 +134,11 @@ struct VolumeGrid *BKE_volume_grid_add(struct Volume *volume,
VolumeGridType type);
void BKE_volume_grid_remove(struct Volume *volume, struct VolumeGrid *grid);
+/**
+ * OpenVDB crashes when the determinant of the transform matrix becomes too small.
+ */
+bool BKE_volume_grid_determinant_valid(double determinant);
+
/* Simplify */
int BKE_volume_simplify_level(const struct Depsgraph *depsgraph);
float BKE_volume_simplify_factor(const struct Depsgraph *depsgraph);
@@ -186,6 +192,9 @@ openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const struct Volume *vo
struct VolumeGrid *grid,
bool clear);
+void BKE_volume_grid_clear_tree(Volume &volume, VolumeGrid &volume_grid);
+void BKE_volume_grid_clear_tree(openvdb::GridBase &grid);
+
VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase &grid);
template<typename OpType>
diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h
index 3f92d6fa117..736f7548bb4 100644
--- a/source/blender/blenkernel/BKE_writeffmpeg.h
+++ b/source/blender/blenkernel/BKE_writeffmpeg.h
@@ -68,7 +68,7 @@ void BKE_ffmpeg_filepath_get(char *string,
const char *suffix);
void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset);
-void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf);
+void BKE_ffmpeg_image_type_verify(struct RenderData *rd, const struct ImageFormatData *imf);
bool BKE_ffmpeg_alpha_channel_is_supported(const struct RenderData *rd);
void *BKE_ffmpeg_context_create(void);
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 14e3856f93d..88df849ae61 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -25,7 +25,6 @@ set(INC
../simulation
../../../intern/eigen
../../../intern/ghost
- ../../../intern/glew-mx
../../../intern/guardedalloc
../../../intern/iksolver/extern
../../../intern/atomic
@@ -99,9 +98,10 @@ set(SRC
intern/collision.c
intern/colorband.c
intern/colortools.c
+ intern/compute_contexts.cc
intern/constraint.c
intern/context.c
- intern/crazyspace.c
+ intern/crazyspace.cc
intern/cryptomatte.cc
intern/curve.cc
intern/curve_bevel.c
@@ -110,7 +110,6 @@ set(SRC
intern/curve_convert.c
intern/curve_decimate.c
intern/curve_deform.c
- intern/curve_eval.cc
intern/curve_legacy_convert.cc
intern/curve_nurbs.cc
intern/curve_poly.cc
@@ -129,7 +128,7 @@ set(SRC
intern/editmesh.c
intern/editmesh_bvh.c
intern/editmesh_cache.cc
- intern/editmesh_tangent.c
+ intern/editmesh_tangent.cc
intern/effect.c
intern/fcurve.c
intern/fcurve_cache.c
@@ -137,12 +136,13 @@ set(SRC
intern/fluid.c
intern/fmodifier.c
intern/freestyle.c
- intern/geometry_component_curve.cc
intern/geometry_component_curves.cc
+ intern/geometry_component_edit_data.cc
intern/geometry_component_instances.cc
intern/geometry_component_mesh.cc
intern/geometry_component_pointcloud.cc
intern/geometry_component_volume.cc
+ intern/geometry_fields.cc
intern/geometry_set.cc
intern/geometry_set_instances.cc
intern/gpencil.c
@@ -185,11 +185,12 @@ set(SRC
intern/linestyle.c
intern/main.c
intern/main_idmap.c
+ intern/main_namemap.cc
intern/mask.c
intern/mask_evaluate.c
intern/mask_rasterize.c
intern/material.c
- intern/mball.c
+ intern/mball.cc
intern/mball_tessellate.c
intern/mesh.cc
intern/mesh_boolean_convert.cc
@@ -200,7 +201,7 @@ set(SRC
intern/mesh_fair.cc
intern/mesh_iterators.c
intern/mesh_legacy_convert.cc
- intern/mesh_mapping.c
+ intern/mesh_mapping.cc
intern/mesh_merge.c
intern/mesh_merge_customdata.cc
intern/mesh_mirror.c
@@ -209,8 +210,8 @@ set(SRC
intern/mesh_remesh_voxel.cc
intern/mesh_runtime.cc
intern/mesh_sample.cc
- intern/mesh_tangent.c
- intern/mesh_tessellate.c
+ intern/mesh_tangent.cc
+ intern/mesh_tessellate.cc
intern/mesh_validate.cc
intern/mesh_wrapper.cc
intern/modifier.c
@@ -228,6 +229,7 @@ set(SRC
intern/multires_versioning.c
intern/nla.c
intern/node.cc
+ intern/node_runtime.cc
intern/node_tree_update.cc
intern/object.cc
intern/object_deform.c
@@ -236,9 +238,9 @@ set(SRC
intern/object_update.c
intern/ocean.c
intern/ocean_spectrum.c
- intern/outliner_treehash.c
+ intern/outliner_treehash.cc
intern/packedFile.c
- intern/paint.c
+ intern/paint.cc
intern/paint_canvas.cc
intern/paint_toolslots.c
intern/particle.c
@@ -262,10 +264,6 @@ 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
@@ -278,7 +276,7 @@ set(SRC
intern/subdiv_displacement_multires.c
intern/subdiv_eval.c
intern/subdiv_foreach.c
- intern/subdiv_mesh.c
+ intern/subdiv_mesh.cc
intern/subdiv_modifier.c
intern/subdiv_stats.c
intern/subdiv_topology.c
@@ -302,7 +300,7 @@ set(SRC
intern/volume.cc
intern/volume_render.cc
intern/volume_to_mesh.cc
- intern/workspace.c
+ intern/workspace.cc
intern/world.c
intern/writeavi.c
@@ -349,9 +347,11 @@ set(SRC
BKE_collision.h
BKE_colorband.h
BKE_colortools.h
+ BKE_compute_contexts.hh
BKE_constraint.h
BKE_context.h
BKE_crazyspace.h
+ BKE_crazyspace.hh
BKE_cryptomatte.h
BKE_cryptomatte.hh
BKE_curve.h
@@ -396,6 +396,7 @@ set(SRC
BKE_image_format.h
BKE_image_partial_update.hh
BKE_image_save.h
+ BKE_image_wrappers.hh
BKE_ipo.h
BKE_kelvinlet.h
BKE_key.h
@@ -412,6 +413,7 @@ set(SRC
BKE_linestyle.h
BKE_main.h
BKE_main_idmap.h
+ BKE_main_namemap.h
BKE_mask.h
BKE_material.h
BKE_mball.h
@@ -441,11 +443,12 @@ set(SRC
BKE_object_deform.h
BKE_object_facemap.h
BKE_ocean.h
- BKE_outliner_treehash.h
+ BKE_outliner_treehash.hh
BKE_packedFile.h
BKE_paint.h
BKE_particle.h
BKE_pbvh.h
+ BKE_pbvh_pixels.hh
BKE_pointcache.h
BKE_pointcloud.h
BKE_preferences.h
@@ -460,7 +463,6 @@ set(SRC
BKE_softbody.h
BKE_sound.h
BKE_speaker.h
- BKE_spline.hh
BKE_studiolight.h
BKE_subdiv.h
BKE_subdiv_ccg.h
@@ -652,6 +654,10 @@ if(WITH_PYTHON)
)
add_definitions(-DWITH_PYTHON)
+ if(WITH_PYTHON_MODULE)
+ add_definitions(-DWITH_PYTHON_MODULE)
+ endif()
+
if(WITH_PYTHON_SAFETY)
add_definitions(-DWITH_PYTHON_SAFETY)
endif()
diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc
index c2ea01bcadf..3aeb2a17b3c 100644
--- a/source/blender/blenkernel/intern/DerivedMesh.cc
+++ b/source/blender/blenkernel/intern/DerivedMesh.cc
@@ -68,6 +68,8 @@
using blender::float3;
using blender::IndexRange;
+using blender::Span;
+using blender::VArray;
/* very slow! enable for testing only! */
//#define USE_MODIFIER_VALIDATE
@@ -84,6 +86,8 @@ static ThreadRWMutex loops_cache_lock = PTHREAD_RWLOCK_INITIALIZER;
static void mesh_init_origspace(Mesh *mesh);
static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final,
const CustomData_MeshMasks *final_datamask);
+static void editbmesh_calc_modifier_final_normals_or_defer(
+ Mesh *mesh_final, const CustomData_MeshMasks *final_datamask);
/* -------------------------------------------------------------------- */
@@ -93,7 +97,7 @@ static MVert *dm_getVertArray(DerivedMesh *dm)
if (!mvert) {
mvert = (MVert *)CustomData_add_layer(
- &dm->vertData, CD_MVERT, CD_CALLOC, nullptr, dm->getNumVerts(dm));
+ &dm->vertData, CD_MVERT, CD_SET_DEFAULT, nullptr, dm->getNumVerts(dm));
CustomData_set_layer_flag(&dm->vertData, CD_MVERT, CD_FLAG_TEMPORARY);
dm->copyVertArray(dm, mvert);
}
@@ -107,7 +111,7 @@ static MEdge *dm_getEdgeArray(DerivedMesh *dm)
if (!medge) {
medge = (MEdge *)CustomData_add_layer(
- &dm->edgeData, CD_MEDGE, CD_CALLOC, nullptr, dm->getNumEdges(dm));
+ &dm->edgeData, CD_MEDGE, CD_SET_DEFAULT, nullptr, dm->getNumEdges(dm));
CustomData_set_layer_flag(&dm->edgeData, CD_MEDGE, CD_FLAG_TEMPORARY);
dm->copyEdgeArray(dm, medge);
}
@@ -121,7 +125,7 @@ static MLoop *dm_getLoopArray(DerivedMesh *dm)
if (!mloop) {
mloop = (MLoop *)CustomData_add_layer(
- &dm->loopData, CD_MLOOP, CD_CALLOC, nullptr, dm->getNumLoops(dm));
+ &dm->loopData, CD_MLOOP, CD_SET_DEFAULT, nullptr, dm->getNumLoops(dm));
CustomData_set_layer_flag(&dm->loopData, CD_MLOOP, CD_FLAG_TEMPORARY);
dm->copyLoopArray(dm, mloop);
}
@@ -135,7 +139,7 @@ static MPoly *dm_getPolyArray(DerivedMesh *dm)
if (!mpoly) {
mpoly = (MPoly *)CustomData_add_layer(
- &dm->polyData, CD_MPOLY, CD_CALLOC, nullptr, dm->getNumPolys(dm));
+ &dm->polyData, CD_MPOLY, CD_SET_DEFAULT, nullptr, dm->getNumPolys(dm));
CustomData_set_layer_flag(&dm->polyData, CD_MPOLY, CD_FLAG_TEMPORARY);
dm->copyPolyArray(dm, mpoly);
}
@@ -143,54 +147,6 @@ static MPoly *dm_getPolyArray(DerivedMesh *dm)
return mpoly;
}
-static MVert *dm_dupVertArray(DerivedMesh *dm)
-{
- MVert *tmp = (MVert *)MEM_malloc_arrayN(
- dm->getNumVerts(dm), sizeof(*tmp), "dm_dupVertArray tmp");
-
- if (tmp) {
- dm->copyVertArray(dm, tmp);
- }
-
- return tmp;
-}
-
-static MEdge *dm_dupEdgeArray(DerivedMesh *dm)
-{
- MEdge *tmp = (MEdge *)MEM_malloc_arrayN(
- dm->getNumEdges(dm), sizeof(*tmp), "dm_dupEdgeArray tmp");
-
- if (tmp) {
- dm->copyEdgeArray(dm, tmp);
- }
-
- return tmp;
-}
-
-static MLoop *dm_dupLoopArray(DerivedMesh *dm)
-{
- MLoop *tmp = (MLoop *)MEM_malloc_arrayN(
- dm->getNumLoops(dm), sizeof(*tmp), "dm_dupLoopArray tmp");
-
- if (tmp) {
- dm->copyLoopArray(dm, tmp);
- }
-
- return tmp;
-}
-
-static MPoly *dm_dupPolyArray(DerivedMesh *dm)
-{
- MPoly *tmp = (MPoly *)MEM_malloc_arrayN(
- dm->getNumPolys(dm), sizeof(*tmp), "dm_dupPolyArray tmp");
-
- if (tmp) {
- dm->copyPolyArray(dm, tmp);
- }
-
- return tmp;
-}
-
static int dm_getNumLoopTri(DerivedMesh *dm)
{
const int numlooptris = poly_to_tri_count(dm->getNumPolys(dm), dm->getNumLoops(dm));
@@ -229,14 +185,10 @@ void DM_init_funcs(DerivedMesh *dm)
dm->getEdgeArray = dm_getEdgeArray;
dm->getLoopArray = dm_getLoopArray;
dm->getPolyArray = dm_getPolyArray;
- dm->dupVertArray = dm_dupVertArray;
- dm->dupEdgeArray = dm_dupEdgeArray;
- dm->dupLoopArray = dm_dupLoopArray;
- dm->dupPolyArray = dm_dupPolyArray;
dm->getLoopTriArray = dm_getLoopTriArray;
- /* subtypes handle getting actual data */
+ /* Sub-types handle getting actual data. */
dm->getNumLoopTri = dm_getNumLoopTri;
dm->getVertDataArray = DM_get_vert_data_layer;
@@ -282,11 +234,11 @@ void DM_from_template(DerivedMesh *dm,
int numPolys)
{
const CustomData_MeshMasks *mask = &CD_MASK_DERIVEDMESH;
- CustomData_copy(&source->vertData, &dm->vertData, mask->vmask, CD_CALLOC, numVerts);
- CustomData_copy(&source->edgeData, &dm->edgeData, mask->emask, CD_CALLOC, numEdges);
- CustomData_copy(&source->faceData, &dm->faceData, mask->fmask, CD_CALLOC, numTessFaces);
- CustomData_copy(&source->loopData, &dm->loopData, mask->lmask, CD_CALLOC, numLoops);
- CustomData_copy(&source->polyData, &dm->polyData, mask->pmask, CD_CALLOC, numPolys);
+ CustomData_copy(&source->vertData, &dm->vertData, mask->vmask, CD_SET_DEFAULT, numVerts);
+ CustomData_copy(&source->edgeData, &dm->edgeData, mask->emask, CD_SET_DEFAULT, numEdges);
+ CustomData_copy(&source->faceData, &dm->faceData, mask->fmask, CD_SET_DEFAULT, numTessFaces);
+ CustomData_copy(&source->loopData, &dm->loopData, mask->lmask, CD_SET_DEFAULT, numLoops);
+ CustomData_copy(&source->polyData, &dm->polyData, mask->pmask, CD_SET_DEFAULT, numPolys);
dm->cd_flag = source->cd_flag;
@@ -327,36 +279,6 @@ bool DM_release(DerivedMesh *dm)
return false;
}
-void DM_DupPolys(DerivedMesh *source, DerivedMesh *target)
-{
- CustomData_free(&target->loopData, source->numLoopData);
- CustomData_free(&target->polyData, source->numPolyData);
-
- CustomData_copy(&source->loopData,
- &target->loopData,
- CD_MASK_DERIVEDMESH.lmask,
- CD_DUPLICATE,
- source->numLoopData);
- CustomData_copy(&source->polyData,
- &target->polyData,
- CD_MASK_DERIVEDMESH.pmask,
- CD_DUPLICATE,
- source->numPolyData);
-
- target->numLoopData = source->numLoopData;
- target->numPolyData = source->numPolyData;
-
- if (!CustomData_has_layer(&target->polyData, CD_MPOLY)) {
- MPoly *mpoly;
- MLoop *mloop;
-
- mloop = source->dupLoopArray(source);
- mpoly = source->dupPolyArray(source);
- CustomData_add_layer(&target->loopData, CD_MLOOP, CD_ASSIGN, mloop, source->numLoopData);
- CustomData_add_layer(&target->polyData, CD_MPOLY, CD_ASSIGN, mpoly, source->numPolyData);
- }
-}
-
void DM_ensure_looptri_data(DerivedMesh *dm)
{
const unsigned int totpoly = dm->numPolyData;
@@ -582,8 +504,7 @@ static void add_orco_mesh(Object *ob, BMEditMesh *em, Mesh *mesh, Mesh *mesh_orc
}
if (!(layerorco = (float(*)[3])CustomData_get_layer(&mesh->vdata, layer))) {
- CustomData_add_layer(&mesh->vdata, layer, CD_CALLOC, nullptr, mesh->totvert);
- BKE_mesh_update_customdata_pointers(mesh, false);
+ CustomData_add_layer(&mesh->vdata, layer, CD_SET_DEFAULT, nullptr, mesh->totvert);
layerorco = (float(*)[3])CustomData_get_layer(&mesh->vdata, layer);
}
@@ -663,8 +584,8 @@ static void mesh_calc_finalize(const Mesh *mesh_input, Mesh *mesh_eval)
mesh_eval->edit_mesh = mesh_input->edit_mesh;
}
-void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval,
- const CustomData_MeshMasks *cd_mask_finalize)
+void BKE_mesh_wrapper_deferred_finalize_mdata(Mesh *me_eval,
+ const CustomData_MeshMasks *cd_mask_finalize)
{
if (me_eval->runtime.wrapper_type_finalize & (1 << ME_WRAPPER_TYPE_BMESH)) {
editbmesh_calc_modifier_final_normals(me_eval, cd_mask_finalize);
@@ -739,6 +660,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
Mesh **r_final,
GeometrySet **r_geometry_set)
{
+ using namespace blender::bke;
/* Input and final mesh. Final mesh is only created the moment the first
* constructive modifier is executed, or a deform modifier needs normals
* or certain data layers. */
@@ -809,7 +731,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
* subdividing them is expensive. */
CustomData_MeshMasks final_datamask = *dataMask;
CDMaskLink *datamasks = BKE_modifier_calc_data_masks(
- scene, ob, md, &final_datamask, required_mode, previewmd, &previewmask);
+ scene, md, &final_datamask, required_mode, previewmd, &previewmask);
CDMaskLink *md_datamask = datamasks;
/* XXX Always copying POLYINDEX, else tessellated data are no more valid! */
CustomData_MeshMasks append_mask = CD_MASK_BAREMESH_ORIGINDEX;
@@ -822,18 +744,13 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
mesh_final = BKE_mesh_copy_for_eval(mesh_input, true);
ASSERT_IS_VALID_MESH(mesh_final);
}
- float3 *rest_positions = static_cast<float3 *>(CustomData_add_layer_named(&mesh_final->vdata,
- CD_PROP_FLOAT3,
- CD_DEFAULT,
- nullptr,
- mesh_final->totvert,
- "rest_position"));
- blender::threading::parallel_for(
- IndexRange(mesh_final->totvert), 1024, [&](const IndexRange range) {
- for (const int i : range) {
- rest_positions[i] = mesh_final->mvert[i].co;
- }
- });
+ MutableAttributeAccessor attributes = mesh_final->attributes_for_write();
+ SpanAttributeWriter<float3> rest_positions =
+ attributes.lookup_or_add_for_write_only_span<float3>("rest_position", ATTR_DOMAIN_POINT);
+ if (rest_positions) {
+ attributes.lookup<float3>("position").materialize(rest_positions.span);
+ rest_positions.finish();
+ }
}
/* Apply all leading deform modifiers. */
@@ -935,7 +852,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Add orco mesh as layer if needed by this modifier. */
if (mesh_final && mesh_orco && mti->requiredDataMask) {
CustomData_MeshMasks mask = {0};
- mti->requiredDataMask(ob, md, &mask);
+ mti->requiredDataMask(md, &mask);
if (mask.vmask & CD_MASK_ORCO) {
add_orco_mesh(ob, nullptr, mesh_final, mesh_orco, CD_ORCO);
}
@@ -1005,11 +922,11 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
((nextmask.vmask | nextmask.emask | nextmask.pmask) & CD_MASK_ORIGINDEX)) {
/* calc */
CustomData_add_layer(
- &mesh_final->vdata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totvert);
+ &mesh_final->vdata, CD_ORIGINDEX, CD_CONSTRUCT, nullptr, mesh_final->totvert);
CustomData_add_layer(
- &mesh_final->edata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totedge);
+ &mesh_final->edata, CD_ORIGINDEX, CD_CONSTRUCT, nullptr, mesh_final->totedge);
CustomData_add_layer(
- &mesh_final->pdata, CD_ORIGINDEX, CD_CALLOC, nullptr, mesh_final->totpoly);
+ &mesh_final->pdata, CD_ORIGINDEX, CD_CONSTRUCT, nullptr, mesh_final->totpoly);
/* Not worth parallelizing this,
* gives less than 0.1% overall speedup in best of best cases... */
@@ -1045,8 +962,11 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
/* add an origspace layer if needed */
if ((md_datamask->mask.lmask) & CD_MASK_ORIGSPACE_MLOOP) {
if (!CustomData_has_layer(&mesh_final->ldata, CD_ORIGSPACE_MLOOP)) {
- CustomData_add_layer(
- &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, nullptr, mesh_final->totloop);
+ CustomData_add_layer(&mesh_final->ldata,
+ CD_ORIGSPACE_MLOOP,
+ CD_SET_DEFAULT,
+ nullptr,
+ mesh_final->totloop);
mesh_init_origspace(mesh_final);
}
}
@@ -1083,7 +1003,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
temp_cddata_masks.pmask = CD_MASK_ORIGINDEX;
if (mti->requiredDataMask != nullptr) {
- mti->requiredDataMask(ob, md, &temp_cddata_masks);
+ mti->requiredDataMask(md, &temp_cddata_masks);
}
CustomData_MeshMasks_update(&temp_cddata_masks, &nextmask);
mesh_set_only_copy(mesh_orco, &temp_cddata_masks);
@@ -1286,12 +1206,6 @@ bool editbmesh_modifier_is_enabled(const Scene *scene,
static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final,
const CustomData_MeshMasks *final_datamask)
{
- if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) {
- /* Generated at draw time. */
- mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type);
- return;
- }
-
const bool calc_loop_normals = ((mesh_final->flag & ME_AUTOSMOOTH) != 0 ||
(final_datamask->lmask & CD_MASK_NORMAL) != 0);
@@ -1319,6 +1233,18 @@ static void editbmesh_calc_modifier_final_normals(Mesh *mesh_final,
}
}
+static void editbmesh_calc_modifier_final_normals_or_defer(
+ Mesh *mesh_final, const CustomData_MeshMasks *final_datamask)
+{
+ if (mesh_final->runtime.wrapper_type != ME_WRAPPER_TYPE_MDATA) {
+ /* Generated at draw time. */
+ mesh_final->runtime.wrapper_type_finalize = (1 << mesh_final->runtime.wrapper_type);
+ return;
+ }
+
+ editbmesh_calc_modifier_final_normals(mesh_final, final_datamask);
+}
+
static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
const Scene *scene,
Object *ob,
@@ -1372,7 +1298,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
* subdividing them is expensive. */
CustomData_MeshMasks final_datamask = *dataMask;
CDMaskLink *datamasks = BKE_modifier_calc_data_masks(
- scene, ob, md, &final_datamask, required_mode, nullptr, nullptr);
+ scene, md, &final_datamask, required_mode, nullptr, nullptr);
CDMaskLink *md_datamask = datamasks;
CustomData_MeshMasks append_mask = CD_MASK_BAREMESH;
@@ -1402,7 +1328,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Add an orco mesh as layer if needed by this modifier. */
if (mesh_final && mesh_orco && mti->requiredDataMask) {
CustomData_MeshMasks mask = {0};
- mti->requiredDataMask(ob, md, &mask);
+ mti->requiredDataMask(md, &mask);
if (mask.vmask & CD_MASK_ORCO) {
add_orco_mesh(ob, em_input, mesh_final, mesh_orco, CD_ORCO);
}
@@ -1502,8 +1428,11 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
if (mask.lmask & CD_MASK_ORIGSPACE_MLOOP) {
if (!CustomData_has_layer(&mesh_final->ldata, CD_ORIGSPACE_MLOOP)) {
- CustomData_add_layer(
- &mesh_final->ldata, CD_ORIGSPACE_MLOOP, CD_CALLOC, nullptr, mesh_final->totloop);
+ CustomData_add_layer(&mesh_final->ldata,
+ CD_ORIGSPACE_MLOOP,
+ CD_SET_DEFAULT,
+ nullptr,
+ mesh_final->totloop);
mesh_init_origspace(mesh_final);
}
}
@@ -1562,11 +1491,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
* then we need to build one. */
if (mesh_final) {
if (deformed_verts) {
- Mesh *mesh_tmp = BKE_mesh_copy_for_eval(mesh_final, false);
- if (mesh_final != mesh_cage) {
- BKE_id_free(nullptr, mesh_final);
+ if (mesh_final == mesh_cage) {
+ mesh_final = BKE_mesh_copy_for_eval(mesh_final, false);
}
- mesh_final = mesh_tmp;
BKE_mesh_vert_coords_apply(mesh_final, deformed_verts);
}
}
@@ -1587,7 +1514,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Add orco coordinates to final and deformed mesh if requested. */
if (final_datamask.vmask & CD_MASK_ORCO) {
- /* FIXME(Campbell): avoid the need to convert to mesh data just to add an orco layer. */
+ /* FIXME(@campbellbarton): avoid the need to convert to mesh data just to add an orco layer. */
BKE_mesh_wrapper_ensure_mdata(mesh_final);
add_orco_mesh(ob, em_input, mesh_final, mesh_orco, CD_ORCO);
@@ -1598,9 +1525,9 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
}
/* Compute normals. */
- editbmesh_calc_modifier_final_normals(mesh_final, &final_datamask);
+ editbmesh_calc_modifier_final_normals_or_defer(mesh_final, &final_datamask);
if (mesh_cage && (mesh_cage != mesh_final)) {
- editbmesh_calc_modifier_final_normals(mesh_cage, &final_datamask);
+ editbmesh_calc_modifier_final_normals_or_defer(mesh_cage, &final_datamask);
}
/* Return final mesh. */
@@ -1735,6 +1662,7 @@ static void object_get_datamask(const Depsgraph *depsgraph,
CustomData_MeshMasks *r_mask,
bool *r_need_mapping)
{
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
DEG_get_customdata_mask_for_object(depsgraph, ob, r_mask);
@@ -1749,8 +1677,11 @@ static void object_get_datamask(const Depsgraph *depsgraph,
return;
}
- Object *actob = view_layer->basact ? DEG_get_original_object(view_layer->basact->object) :
- nullptr;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *actob = BKE_view_layer_active_object_get(view_layer);
+ if (actob) {
+ actob = DEG_get_original_object(actob);
+ }
if (DEG_get_original_object(ob) == actob) {
bool editing = BKE_paint_select_face_test(actob);
@@ -1793,7 +1724,7 @@ void makeDerivedMesh(struct Depsgraph *depsgraph,
BKE_object_free_derived_caches(ob);
if (DEG_is_active(depsgraph)) {
- BKE_sculpt_update_object_before_eval(scene, ob);
+ BKE_sculpt_update_object_before_eval(ob);
}
/* NOTE: Access the `edit_mesh` after freeing the derived caches, so that `ob->data` is restored
@@ -1994,9 +1925,9 @@ void mesh_get_mapped_verts_coords(Mesh *me_eval, float (*r_cos)[3], const int to
MEM_freeN(userData.vertex_visit);
}
else {
- MVert *mv = me_eval->mvert;
- for (int i = 0; i < totcos; i++, mv++) {
- copy_v3_v3(r_cos[i], mv->co);
+ const Span<MVert> verts = me_eval->verts();
+ for (int i = 0; i < totcos; i++) {
+ copy_v3_v3(r_cos[i], verts[i].co);
}
}
}
@@ -2009,9 +1940,11 @@ static void mesh_init_origspace(Mesh *mesh)
CD_ORIGSPACE_MLOOP);
const int numpoly = mesh->totpoly;
// const int numloop = mesh->totloop;
- MVert *mv = mesh->mvert;
- MLoop *ml = mesh->mloop;
- MPoly *mp = mesh->mpoly;
+ const Span<MVert> verts = mesh->verts();
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
+
+ const MPoly *mp = polys.data();
int i, j, k;
blender::Vector<blender::float2, 64> vcos_2d;
@@ -2025,19 +1958,19 @@ static void mesh_init_origspace(Mesh *mesh)
}
}
else {
- MLoop *l = &ml[mp->loopstart];
+ const MLoop *l = &loops[mp->loopstart];
float p_nor[3], co[3];
float mat[3][3];
float min[2] = {FLT_MAX, FLT_MAX}, max[2] = {-FLT_MAX, -FLT_MAX};
float translate[2], scale[2];
- BKE_mesh_calc_poly_normal(mp, l, mv, p_nor);
+ BKE_mesh_calc_poly_normal(mp, l, verts.data(), p_nor);
axis_dominant_v3_to_m3(mat, p_nor);
vcos_2d.resize(mp->totloop);
for (j = 0; j < mp->totloop; j++, l++) {
- mul_v3_m3v3(co, mat, mv[l->v].co);
+ mul_v3_m3v3(co, mat, verts[l->v].co);
copy_v2_v2(vcos_2d[j], co);
for (k = 0; k < 2; k++) {
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index fee7582acb3..10aa4ec7906 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -53,6 +53,7 @@
#include "BIK_api.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "BLO_read_write.h"
@@ -112,7 +113,7 @@ static void action_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src,
BLI_addtail(&action_dst->curves, fcurve_dst);
- /* Fix group links (kindof bad list-in-list search, but this is the most reliable way). */
+ /* Fix group links (kind of bad list-in-list search, but this is the most reliable way). */
for (group_dst = action_dst->groups.first, group_src = action_src->groups.first;
group_dst && group_src;
group_dst = group_dst->next, group_src = group_src->next) {
@@ -314,7 +315,7 @@ IDTypeInfo IDType_ID_AC = {
.foreach_id = action_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = action_blend_write,
.blend_read_data = action_blend_read_data,
@@ -1950,7 +1951,7 @@ void BKE_pose_blend_read_lib(BlendLibReader *reader, Object *ob, bPose *pose)
pchan->bone = BKE_armature_find_bone_name(arm, pchan->name);
- IDP_BlendReadLib(reader, pchan->prop);
+ IDP_BlendReadLib(reader, ob->id.lib, pchan->prop);
BLO_read_id_address(reader, ob->id.lib, &pchan->custom);
if (UNLIKELY(pchan->bone == NULL)) {
diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c
index 7ef561c8216..0663538f3bb 100644
--- a/source/blender/blenkernel/intern/action_mirror.c
+++ b/source/blender/blenkernel/intern/action_mirror.c
@@ -363,7 +363,7 @@ static void action_flip_pchan(Object *ob_arm,
/* Recalculate handles. */
for (int i = 0; i < fcurve_array_len; i++) {
- calchandles_fcurve_ex(fcurve_array[i], 0);
+ BKE_fcurve_handles_recalc_ex(fcurve_array[i], 0);
}
MEM_freeN((void *)keyed_frames);
diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c
index 861a89ea9d7..65ce3e3b523 100644
--- a/source/blender/blenkernel/intern/anim_data.c
+++ b/source/blender/blenkernel/intern/anim_data.c
@@ -43,6 +43,7 @@
#include "BLO_read_write.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "CLG_log.h"
@@ -135,7 +136,7 @@ bool BKE_animdata_set_action(ReportList *reports, ID *id, bAction *act)
return false;
}
- /* Reduce usercount for current action. */
+ /* Reduce user-count for current action. */
if (adt->action) {
id_us_min((ID *)adt->action);
}
@@ -286,11 +287,11 @@ AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
/* make a copy of action - at worst, user has to delete copies... */
if (do_action) {
- /* Recursive copy of 'real' IDs is a bit hairy. Even if do not want to deal with usercount
- * when copying ID's data itself, we still need to do so with sub-IDs, since those will not be
- * handled by later 'update usercounts of used IDs' code as used e.g. at end of
- * BKE_id_copy_ex().
- * So in case we do copy the ID and its sub-IDs in bmain, silence the 'no usercount' flag for
+ /* Recursive copy of 'real' IDs is a bit hairy. Even if do not want to deal with user-count
+ * when copying ID's data itself, we still need to do so with sub-IDs, since those will not be
+ * handled by later 'update user-counts of used IDs' code as used e.g. at end of
+ * #BKE_id_copy_ex().
+ * So in case we do copy the ID and its sub-IDs in bmain, silence the 'no user-count' flag for
* the sub-IDs copying.
* NOTE: This is a bit weak, as usually when it comes to recursive ID copy. Should work for
* now, but we may have to revisit this at some point and add a proper extra flag to deal with
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index eb4784bebff..85ce647fcab 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -53,6 +53,7 @@
#include "DEG_depsgraph_query.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "BLO_read_write.h"
@@ -3623,16 +3624,6 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
}
}
-/** Using \a blended_snapshot and \a upper_snapshot, we can solve for the \a r_lower_snapshot.
- *
- * Only channels that exist within \a blended_snapshot are processed.
- * Only blended values within the \a remap_domain are processed.
- *
- * Writes to \a r_upper_snapshot NlaEvalChannelSnapshot->remap_domain to match remapping success.
- *
- * Assumes caller marked upper values that are in the \a blend_domain. This determines whether the
- * blended value came directly from the lower snapshot or a result of blending.
- **/
void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data,
NlaEvalSnapshot *blended_snapshot,
NlaEvalSnapshot *upper_snapshot,
diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c
index 031d3647878..e3c42c8bb78 100644
--- a/source/blender/blenkernel/intern/appdir.c
+++ b/source/blender/blenkernel/intern/appdir.c
@@ -371,14 +371,16 @@ static bool get_path_local_ex(char *targetpath,
relfolder[0] = '\0';
}
- /* Try `{g_app.program_dirname}/2.xx/{folder_name}` the default directory
+ /* Try `{g_app.program_dirname}/3.xx/{folder_name}` the default directory
* for a portable distribution. See `WITH_INSTALL_PORTABLE` build-option. */
const char *path_base = g_app.program_dirname;
-#ifdef __APPLE__
+#if defined(__APPLE__) && !defined(WITH_PYTHON_MODULE)
/* Due new code-sign situation in OSX > 10.9.5
- * we must move the blender_version dir with contents to Resources. */
- char osx_resourses[FILE_MAX];
- BLI_snprintf(osx_resourses, sizeof(osx_resourses), "%s../Resources", g_app.program_dirname);
+ * we must move the blender_version dir with contents to Resources.
+ * Add 4 + 9 for the temporary `/../` path & `Resources`. */
+ char osx_resourses[FILE_MAX + 4 + 9];
+ BLI_path_join(
+ osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources", NULL);
/* Remove the '/../' added above. */
BLI_path_normalize(NULL, osx_resourses);
path_base = osx_resourses;
@@ -734,6 +736,7 @@ const char *BKE_appdir_folder_id_create(const int folder_id, const char *subfold
BLENDER_USER_CONFIG,
BLENDER_USER_SCRIPTS,
BLENDER_USER_AUTOSAVE)) {
+ BLI_assert_unreachable();
return NULL;
}
@@ -782,6 +785,7 @@ const char *BKE_appdir_folder_id_version(const int folder_id,
* Access locations of Blender & Python.
* \{ */
+#ifndef WITH_PYTHON_MODULE
/**
* Checks if name is a fully qualified filename to an executable.
* If not it searches `$PATH` for the file. On Windows it also
@@ -796,7 +800,7 @@ const char *BKE_appdir_folder_id_version(const int folder_id,
*/
static void where_am_i(char *fullname, const size_t maxlen, const char *name)
{
-#ifdef WITH_BINRELOC
+# ifdef WITH_BINRELOC
/* Linux uses `binreloc` since `argv[0]` is not reliable, call `br_init(NULL)` first. */
{
const char *path = NULL;
@@ -807,9 +811,9 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
return;
}
}
-#endif
+# endif
-#ifdef _WIN32
+# ifdef _WIN32
{
wchar_t *fullname_16 = MEM_mallocN(maxlen * sizeof(wchar_t), "ProgramPath");
if (GetModuleFileNameW(0, fullname_16, maxlen)) {
@@ -825,7 +829,7 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
MEM_freeN(fullname_16);
}
-#endif
+# endif
/* Unix and non Linux. */
if (name && name[0]) {
@@ -833,16 +837,16 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
BLI_strncpy(fullname, name, maxlen);
if (name[0] == '.') {
BLI_path_abs_from_cwd(fullname, maxlen);
-#ifdef _WIN32
+# ifdef _WIN32
BLI_path_program_extensions_add_win32(fullname, maxlen);
-#endif
+# endif
}
else if (BLI_path_slash_rfind(name)) {
/* Full path. */
BLI_strncpy(fullname, name, maxlen);
-#ifdef _WIN32
+# ifdef _WIN32
BLI_path_program_extensions_add_win32(fullname, maxlen);
-#endif
+# endif
}
else {
BLI_path_program_search(fullname, maxlen, name);
@@ -850,23 +854,43 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
/* Remove "/./" and "/../" so string comparisons can be used on the path. */
BLI_path_normalize(NULL, fullname);
-#if defined(DEBUG)
+# if defined(DEBUG)
if (!STREQ(name, fullname)) {
CLOG_INFO(&LOG, 2, "guessing '%s' == '%s'", name, fullname);
}
-#endif
+# endif
}
}
+#endif /* WITH_PYTHON_MODULE */
void BKE_appdir_program_path_init(const char *argv0)
{
+#ifdef WITH_PYTHON_MODULE
+ /* NOTE(@campbellbarton): Always use `argv[0]` as is, when building as a Python module.
+ * Otherwise other methods of detecting the binary that override this argument
+ * which must point to the Python module for data-files to be detected. */
+ STRNCPY(g_app.program_filepath, argv0);
+ BLI_path_abs_from_cwd(g_app.program_filepath, sizeof(g_app.program_filepath));
+ BLI_path_normalize(NULL, g_app.program_filepath);
+
+ if (g_app.program_dirname[0] == '\0') {
+ /* First time initializing, the file binary path isn't valid from a Python module.
+ * Calling again must set the `filepath` and leave the directory as-is. */
+ BLI_split_dir_part(
+ g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname));
+ g_app.program_filepath[0] = '\0';
+ }
+#else
where_am_i(g_app.program_filepath, sizeof(g_app.program_filepath), argv0);
BLI_split_dir_part(g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname));
+#endif
}
const char *BKE_appdir_program_path(void)
{
+#ifndef WITH_PYTHON_MODULE /* Default's to empty when building as a Python module. */
BLI_assert(g_app.program_filepath[0]);
+#endif
return g_app.program_filepath;
}
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index f29074c827c..9b00d427320 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -97,7 +97,7 @@ static void armature_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src
Bone *bone_src, *bone_dst;
Bone *bone_dst_act = NULL;
- /* We never handle usercount here for own data. */
+ /* We never handle user-count here for own data. */
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
armature_dst->bonehash = NULL;
@@ -261,12 +261,12 @@ static void armature_blend_read_data(BlendDataReader *reader, ID *id)
BKE_armature_bone_hash_make(arm);
}
-static void lib_link_bones(BlendLibReader *reader, Bone *bone)
+static void lib_link_bones(BlendLibReader *reader, Library *lib, Bone *bone)
{
- IDP_BlendReadLib(reader, bone->prop);
+ IDP_BlendReadLib(reader, lib, bone->prop);
LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) {
- lib_link_bones(reader, curbone);
+ lib_link_bones(reader, lib, curbone);
}
}
@@ -274,7 +274,7 @@ static void armature_blend_read_lib(BlendLibReader *reader, ID *id)
{
bArmature *arm = (bArmature *)id;
LISTBASE_FOREACH (Bone *, curbone, &arm->bonebase) {
- lib_link_bones(reader, curbone);
+ lib_link_bones(reader, id->lib, curbone);
}
}
@@ -313,7 +313,7 @@ IDTypeInfo IDType_ID_AR = {
.foreach_id = armature_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = armature_blend_write,
.blend_read_data = armature_blend_read_data,
diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c
index 0769049e9a9..84bb1af011a 100644
--- a/source/blender/blenkernel/intern/armature_deform.c
+++ b/source/blender/blenkernel/intern/armature_deform.c
@@ -35,6 +35,7 @@
#include "BKE_deform.h"
#include "BKE_editmesh.h"
#include "BKE_lattice.h"
+#include "BKE_mesh.h"
#include "DEG_depsgraph_build.h"
@@ -159,9 +160,9 @@ float distfactor_to_bone(
}
static float dist_bone_deform(
- bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3])
+ const bPoseChannel *pchan, float vec[3], DualQuat *dq, float mat[3][3], const float co[3])
{
- Bone *bone = pchan->bone;
+ const Bone *bone = pchan->bone;
float fac, contrib = 0.0;
if (bone == NULL) {
@@ -188,7 +189,7 @@ static float dist_bone_deform(
return contrib;
}
-static void pchan_bone_deform(bPoseChannel *pchan,
+static void pchan_bone_deform(const bPoseChannel *pchan,
float weight,
float vec[3],
DualQuat *dq,
@@ -196,7 +197,7 @@ static void pchan_bone_deform(bPoseChannel *pchan,
const float co[3],
float *contrib)
{
- Bone *bone = pchan->bone;
+ const Bone *bone = pchan->bone;
if (!weight) {
return;
@@ -223,7 +224,6 @@ static void pchan_bone_deform(bPoseChannel *pchan,
typedef struct ArmatureUserdata {
const Object *ob_arm;
- const Object *ob_target;
const Mesh *me_target;
float (*vert_coords)[3];
float (*vert_deform_mats)[3][3];
@@ -264,7 +264,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
const int armature_def_nr = data->armature_def_nr;
DualQuat sumdq, *dq = NULL;
- bPoseChannel *pchan;
+ const bPoseChannel *pchan;
float *co, dco[3];
float sumvec[3], summat[3][3];
float *vec = NULL, (*smat)[3] = NULL;
@@ -319,7 +319,7 @@ static void armature_vert_task_with_dvert(const ArmatureUserdata *data,
const uint index = dw->def_nr;
if (index < data->defbase_len && (pchan = data->pchan_from_defbase[index])) {
float weight = dw->weight;
- Bone *bone = pchan->bone;
+ const Bone *bone = pchan->bone;
deformed = 1;
@@ -407,8 +407,8 @@ static void armature_vert_task(void *__restrict userdata,
if (data->use_dverts || data->armature_def_nr != -1) {
if (data->me_target) {
BLI_assert(i < data->me_target->totvert);
- if (data->me_target->dvert != NULL) {
- dvert = data->me_target->dvert + i;
+ if (data->dverts != NULL) {
+ dvert = data->dverts + i;
}
else {
dvert = NULL;
@@ -434,7 +434,7 @@ static void armature_vert_task_editmesh(void *__restrict userdata,
{
const ArmatureUserdata *data = userdata;
BMVert *v = (BMVert *)iter;
- MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset);
+ const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(v, data->bmesh.cd_dvert_offset);
armature_vert_task_with_dvert(data, BM_elem_index_get(v), dvert);
}
@@ -459,15 +459,14 @@ static void armature_deform_coords_impl(const Object *ob_arm,
BMEditMesh *em_target,
bGPDstroke *gps_target)
{
- bArmature *arm = ob_arm->data;
+ const bArmature *arm = ob_arm->data;
bPoseChannel **pchan_from_defbase = NULL;
const MDeformVert *dverts = NULL;
- bDeformGroup *dg;
const bool use_envelope = (deformflag & ARM_DEF_ENVELOPE) != 0;
const bool use_quaternion = (deformflag & ARM_DEF_QUATERNION) != 0;
const bool invert_vgroup = (deformflag & ARM_DEF_INVERT_VGROUP) != 0;
- int defbase_len = 0; /* safety for vertexgroup index overflow */
- int i, dverts_len = 0; /* safety for vertexgroup overflow */
+ int defbase_len = 0; /* safety for vertexgroup index overflow */
+ int dverts_len = 0; /* safety for vertexgroup overflow */
bool use_dverts = false;
int armature_def_nr = -1;
int cd_dvert_offset = -1;
@@ -485,34 +484,38 @@ static void armature_deform_coords_impl(const Object *ob_arm,
}
if (BKE_object_supports_vertex_groups(ob_target)) {
- /* get the def_nr for the overall armature vertex group if present */
- armature_def_nr = BKE_object_defgroup_name_index(ob_target, defgrp_name);
-
- defbase_len = BKE_object_defgroup_count(ob_target);
-
+ const ID *target_data_id = NULL;
if (ob_target->type == OB_MESH) {
+ target_data_id = me_target == NULL ? (const ID *)ob_target->data : &me_target->id;
if (em_target == NULL) {
- Mesh *me = ob_target->data;
- dverts = me->dvert;
+ const Mesh *me = (const Mesh *)target_data_id;
+ dverts = BKE_mesh_deform_verts(me);
if (dverts) {
dverts_len = me->totvert;
}
}
}
else if (ob_target->type == OB_LATTICE) {
- Lattice *lt = ob_target->data;
+ const Lattice *lt = ob_target->data;
+ target_data_id = (const ID *)ob_target->data;
dverts = lt->dvert;
if (dverts) {
dverts_len = lt->pntsu * lt->pntsv * lt->pntsw;
}
}
else if (ob_target->type == OB_GPENCIL) {
+ target_data_id = (const ID *)ob_target->data;
dverts = gps_target->dvert;
if (dverts) {
dverts_len = gps_target->totpoints;
}
}
+ /* Collect the vertex group names from the evaluated data. */
+ armature_def_nr = BKE_id_defgroup_name_index(target_data_id, defgrp_name);
+ const ListBase *defbase = BKE_id_defgroup_list_get(target_data_id);
+ defbase_len = BLI_listbase_count(defbase);
+
/* get a vertex-deform-index to posechannel array */
if (deformflag & ARM_DEF_VGROUP) {
/* if we have a Mesh, only use dverts if it has them */
@@ -521,7 +524,7 @@ static void armature_deform_coords_impl(const Object *ob_arm,
use_dverts = (cd_dvert_offset != -1);
}
else if (me_target) {
- use_dverts = (me_target->dvert != NULL);
+ use_dverts = (BKE_mesh_deform_verts(me_target) != NULL);
}
else if (dverts) {
use_dverts = true;
@@ -533,8 +536,8 @@ static void armature_deform_coords_impl(const Object *ob_arm,
*
* - Check whether keeping this consistent across frames gives speedup.
*/
- const ListBase *defbase = BKE_object_defgroup_list(ob_target);
- for (i = 0, dg = defbase->first; dg; i++, dg = dg->next) {
+ int i;
+ LISTBASE_FOREACH_INDEX (bDeformGroup *, dg, defbase, i) {
pchan_from_defbase[i] = BKE_pose_channel_find_name(ob_arm->pose, dg->name);
/* exclude non-deforming bones */
if (pchan_from_defbase[i]) {
@@ -549,7 +552,6 @@ static void armature_deform_coords_impl(const Object *ob_arm,
ArmatureUserdata data = {
.ob_arm = ob_arm,
- .ob_target = ob_target,
.me_target = me_target,
.vert_coords = vert_coords,
.vert_deform_mats = vert_deform_mats,
diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 2db4c086e04..6d7aed239e7 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -288,7 +288,7 @@ static int position_tail_on_spline(bSplineIKConstraint *ik_data,
int max_seg_idx = BKE_anim_path_get_array_size(cache) - 1;
/* Make an initial guess of where our intersection point will be.
- * If the curve was a straight line, then the faction passed in r_new_curve_pos
+ * If the curve was a straight line, then the fraction passed in r_new_curve_pos
* would be the correct location.
* So make it our first initial guess.
*/
diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc
index fccff602d2e..f7b14cc3479 100644
--- a/source/blender/blenkernel/intern/asset_catalog.cc
+++ b/source/blender/blenkernel/intern/asset_catalog.cc
@@ -516,7 +516,7 @@ CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing(
sizeof(asset_lib_cdf_path),
suitable_root_path,
DEFAULT_CATALOG_FILENAME.c_str(),
- NULL);
+ nullptr);
return asset_lib_cdf_path;
}
diff --git a/source/blender/blenkernel/intern/attribute.cc b/source/blender/blenkernel/intern/attribute.cc
index 030e4941874..bd3e452b7f2 100644
--- a/source/blender/blenkernel/intern/attribute.cc
+++ b/source/blender/blenkernel/intern/attribute.cc
@@ -8,6 +8,7 @@
*/
#include <cstring>
+#include <optional>
#include "MEM_guardedalloc.h"
@@ -19,12 +20,15 @@
#include "DNA_pointcloud_types.h"
#include "BLI_index_range.hh"
+#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_string_utils.h"
+#include "BLT_translation.h"
+
#include "BKE_attribute.h"
#include "BKE_attribute.hh"
-#include "BKE_curves.h"
+#include "BKE_curves.hh"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_pointcloud.h"
@@ -89,6 +93,36 @@ static void get_domains(const ID *id, DomainInfo info[ATTR_DOMAIN_NUM])
}
}
+namespace blender::bke {
+
+static std::optional<blender::bke::MutableAttributeAccessor> get_attribute_accessor_for_write(
+ ID &id)
+{
+ switch (GS(id.name)) {
+ case ID_ME: {
+ Mesh &mesh = reinterpret_cast<Mesh &>(id);
+ /* The attribute API isn't implemented for BMesh, so edit mode meshes are not supported. */
+ BLI_assert(mesh.edit_mesh == nullptr);
+ return mesh.attributes_for_write();
+ }
+ case ID_PT: {
+ PointCloud &pointcloud = reinterpret_cast<PointCloud &>(id);
+ return pointcloud.attributes_for_write();
+ }
+ case ID_CV: {
+ Curves &curves_id = reinterpret_cast<Curves &>(id);
+ CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
+ return curves.attributes_for_write();
+ }
+ default: {
+ BLI_assert_unreachable();
+ return {};
+ }
+ }
+}
+
+} // namespace blender::bke
+
bool BKE_id_attributes_supported(const ID *id)
{
DomainInfo info[ATTR_DOMAIN_NUM];
@@ -115,6 +149,13 @@ bool BKE_id_attribute_rename(ID *id,
BLI_assert_msg(0, "Required attribute name is not editable");
return false;
}
+ if (STREQ(new_name, "")) {
+ BKE_report(reports, RPT_ERROR, "Attribute name can not be empty");
+ return false;
+ }
+ if (STREQ(old_name, new_name)) {
+ return false;
+ }
CustomDataLayer *layer = BKE_id_attribute_search(
id, old_name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
@@ -146,9 +187,9 @@ static bool unique_name_cb(void *arg, const char *name)
continue;
}
- CustomData *cdata = info[domain].customdata;
+ const CustomData *cdata = info[domain].customdata;
for (int i = 0; i < cdata->totlayer; i++) {
- CustomDataLayer *layer = cdata->layers + i;
+ const CustomDataLayer *layer = cdata->layers + i;
if (STREQ(layer->name, name)) {
return true;
@@ -163,7 +204,14 @@ bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname)
{
AttrUniqueData data{id};
- BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME);
+ /* Set default name if none specified.
+ * NOTE: We only call IFACE_() if needed to avoid locale lookup overhead. */
+ if (!name || name[0] == '\0') {
+ BLI_strncpy(outname, IFACE_("Attribute"), MAX_CUSTOMDATA_LAYER_NAME);
+ }
+ else {
+ BLI_strncpy_utf8(outname, name, MAX_CUSTOMDATA_LAYER_NAME);
+ }
return BLI_uniquename_cb(
unique_name_cb, &data, nullptr, '.', outname, MAX_CUSTOMDATA_LAYER_NAME);
@@ -172,6 +220,7 @@ bool BKE_id_attribute_calc_unique_name(ID *id, const char *name, char *outname)
CustomDataLayer *BKE_id_attribute_new(
ID *id, const char *name, const int type, const eAttrDomain domain, ReportList *reports)
{
+ using namespace blender::bke;
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
@@ -184,64 +233,65 @@ CustomDataLayer *BKE_id_attribute_new(
char uniquename[MAX_CUSTOMDATA_LAYER_NAME];
BKE_id_attribute_calc_unique_name(id, name, uniquename);
- switch (GS(id->name)) {
- case ID_ME: {
- Mesh *me = (Mesh *)id;
- BMEditMesh *em = me->edit_mesh;
- if (em != nullptr) {
- BM_data_layer_add_named(em->bm, customdata, type, uniquename);
- }
- else {
- CustomData_add_layer_named(
- customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename);
- }
- break;
- }
- default: {
- CustomData_add_layer_named(
- customdata, type, CD_DEFAULT, nullptr, info[domain].length, uniquename);
- break;
+ if (GS(id->name) == ID_ME) {
+ Mesh *mesh = reinterpret_cast<Mesh *>(id);
+ if (BMEditMesh *em = mesh->edit_mesh) {
+ BM_data_layer_add_named(em->bm, customdata, type, uniquename);
+ const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
+ return (index == -1) ? nullptr : &(customdata->layers[index]);
}
}
+ std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id);
+ if (!attributes) {
+ return nullptr;
+ }
+
+ attributes->add(uniquename, domain, eCustomDataType(type), AttributeInitDefaultValue());
+
const int index = CustomData_get_named_layer_index(customdata, type, uniquename);
return (index == -1) ? nullptr : &(customdata->layers[index]);
}
CustomDataLayer *BKE_id_attribute_duplicate(ID *id, const char *name, ReportList *reports)
{
- const CustomDataLayer *src_layer = BKE_id_attribute_search(
- id, name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
- if (src_layer == nullptr) {
- BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
- return nullptr;
- }
-
- const eCustomDataType type = (eCustomDataType)src_layer->type;
- const eAttrDomain domain = BKE_id_attribute_domain(id, src_layer);
+ using namespace blender::bke;
+ char uniquename[MAX_CUSTOMDATA_LAYER_NAME];
+ BKE_id_attribute_calc_unique_name(id, name, uniquename);
- /* Make a copy of name in case CustomData API reallocates the layers. */
- const std::string name_copy = name;
+ if (GS(id->name) == ID_ME) {
+ Mesh *mesh = reinterpret_cast<Mesh *>(id);
+ if (BMEditMesh *em = mesh->edit_mesh) {
+ BLI_assert_unreachable();
+ UNUSED_VARS(em);
+ return nullptr;
+ }
+ }
- DomainInfo info[ATTR_DOMAIN_NUM];
- get_domains(id, info);
- CustomData *customdata = info[domain].customdata;
+ std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id);
+ if (!attributes) {
+ return nullptr;
+ }
- CustomDataLayer *new_layer = BKE_id_attribute_new(id, name_copy.c_str(), type, domain, reports);
- if (new_layer == nullptr) {
+ GAttributeReader src = attributes->lookup(name);
+ if (!src) {
+ BKE_report(reports, RPT_ERROR, "Attribute is not part of this geometry");
return nullptr;
}
- const int from_index = CustomData_get_named_layer_index(customdata, type, name_copy.c_str());
- const int to_index = CustomData_get_named_layer_index(customdata, type, new_layer->name);
- CustomData_copy_data_layer(
- customdata, customdata, from_index, to_index, 0, 0, info[domain].length);
+ const eCustomDataType type = cpp_type_to_custom_data_type(src.varray.type());
+ attributes->add(uniquename, src.domain, type, AttributeInitVArray(src.varray));
- return new_layer;
+ return BKE_id_attribute_search(id, uniquename, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
}
bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports)
{
+ using namespace blender::bke;
+ if (!name || name[0] == '\0') {
+ BKE_report(reports, RPT_ERROR, "The attribute name must not be empty");
+ return false;
+ }
if (BKE_id_attribute_required(id, name)) {
BKE_report(reports, RPT_ERROR, "Attribute is required and can't be removed");
return false;
@@ -250,31 +300,26 @@ bool BKE_id_attribute_remove(ID *id, const char *name, ReportList *reports)
DomainInfo info[ATTR_DOMAIN_NUM];
get_domains(id, info);
- switch (GS(id->name)) {
- case ID_ME: {
- Mesh *mesh = reinterpret_cast<Mesh *>(id);
- if (BMEditMesh *em = mesh->edit_mesh) {
- for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
- if (CustomData *data = info[domain].customdata) {
- if (BM_data_layer_free_named(em->bm, data, name)) {
- return true;
- }
- }
- }
- return false;
- }
- ATTR_FALLTHROUGH;
- }
- default:
+ if (GS(id->name) == ID_ME) {
+ Mesh *mesh = reinterpret_cast<Mesh *>(id);
+ if (BMEditMesh *em = mesh->edit_mesh) {
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
if (CustomData *data = info[domain].customdata) {
- if (CustomData_free_layer_named(data, name, info[domain].length)) {
+ if (BM_data_layer_free_named(em->bm, data, name)) {
return true;
}
}
}
return false;
+ }
+ }
+
+ std::optional<MutableAttributeAccessor> attributes = get_attribute_accessor_for_write(*id);
+ if (!attributes) {
+ return false;
}
+
+ return attributes->remove(name);
}
CustomDataLayer *BKE_id_attribute_find(const ID *id,
@@ -338,9 +383,12 @@ int BKE_id_attributes_length(const ID *id, eAttrDomainMask domain_mask, eCustomD
int length = 0;
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
- CustomData *customdata = info[domain].customdata;
+ const CustomData *customdata = info[domain].customdata;
+ if (customdata == nullptr) {
+ continue;
+ }
- if (customdata && ((1 << (int)domain) & domain_mask)) {
+ if ((1 << (int)domain) & domain_mask) {
length += CustomData_number_of_layers_typemask(customdata, mask);
}
}
@@ -354,9 +402,11 @@ eAttrDomain BKE_id_attribute_domain(const ID *id, const CustomDataLayer *layer)
get_domains(id, info);
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
- CustomData *customdata = info[domain].customdata;
- if (customdata &&
- ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) {
+ const CustomData *customdata = info[domain].customdata;
+ if (customdata == nullptr) {
+ continue;
+ }
+ if (ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) {
return static_cast<eAttrDomain>(domain);
}
}
@@ -376,6 +426,7 @@ int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer)
if (mesh->edit_mesh != nullptr) {
return 0;
}
+ break;
}
default:
break;
@@ -385,9 +436,11 @@ int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer)
get_domains(id, info);
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
- CustomData *customdata = info[domain].customdata;
- if (customdata &&
- ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) {
+ const CustomData *customdata = info[domain].customdata;
+ if (customdata == nullptr) {
+ continue;
+ }
+ if (ARRAY_HAS_ITEM((CustomDataLayer *)layer, customdata->layers, customdata->totlayer)) {
return info[domain].length;
}
}
@@ -399,12 +452,10 @@ int BKE_id_attribute_data_length(ID *id, CustomDataLayer *layer)
bool BKE_id_attribute_required(const ID *id, const char *name)
{
switch (GS(id->name)) {
- case ID_PT: {
- return BKE_pointcloud_customdata_required((const PointCloud *)id, name);
- }
- case ID_CV: {
- return BKE_curves_customdata_required((const Curves *)id, name);
- }
+ case ID_PT:
+ return BKE_pointcloud_attribute_required((const PointCloud *)id, name);
+ case ID_CV:
+ return BKE_curves_attribute_required((const Curves *)id, name);
default:
return false;
}
@@ -424,15 +475,19 @@ CustomDataLayer *BKE_id_attributes_active_get(ID *id)
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
CustomData *customdata = info[domain].customdata;
- if (customdata) {
- for (int i = 0; i < customdata->totlayer; i++) {
- CustomDataLayer *layer = &customdata->layers[i];
- if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) {
- if (index == active_index) {
+ if (customdata == nullptr) {
+ continue;
+ }
+ for (int i = 0; i < customdata->totlayer; i++) {
+ CustomDataLayer *layer = &customdata->layers[i];
+ if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) {
+ if (index == active_index) {
+ if (BKE_attribute_allow_procedural_access(layer->name)) {
return layer;
}
- index++;
+ return nullptr;
}
+ index++;
}
}
}
@@ -448,17 +503,18 @@ void BKE_id_attributes_active_set(ID *id, CustomDataLayer *active_layer)
int index = 0;
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
- CustomData *customdata = info[domain].customdata;
- if (customdata) {
- for (int i = 0; i < customdata->totlayer; i++) {
- CustomDataLayer *layer = &customdata->layers[i];
- if (layer == active_layer) {
- *BKE_id_attributes_active_index_p(id) = index;
- return;
- }
- if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) {
- index++;
- }
+ const CustomData *customdata = info[domain].customdata;
+ if (customdata == nullptr) {
+ continue;
+ }
+ for (int i = 0; i < customdata->totlayer; i++) {
+ const CustomDataLayer *layer = &customdata->layers[i];
+ if (layer == active_layer) {
+ *BKE_id_attributes_active_index_p(id) = index;
+ return;
+ }
+ if (CD_MASK_PROP_ALL & CD_TYPE_AS_MASK(layer->type)) {
+ index++;
}
}
}
@@ -490,7 +546,10 @@ CustomData *BKE_id_attributes_iterator_next_domain(ID *id, CustomDataLayer *laye
for (const int domain : IndexRange(ATTR_DOMAIN_NUM)) {
CustomData *customdata = info[domain].customdata;
- if (customdata && customdata->layers && customdata->totlayer) {
+ if (customdata == nullptr) {
+ continue;
+ }
+ if (customdata->layers && customdata->totlayer) {
if (customdata->layers == layers) {
use_next = true;
}
diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc
index ac1ee19927c..df7787986db 100644
--- a/source/blender/blenkernel/intern/attribute_access.cc
+++ b/source/blender/blenkernel/intern/attribute_access.cc
@@ -5,7 +5,6 @@
#include "BKE_attribute_math.hh"
#include "BKE_customdata.h"
#include "BKE_deform.h"
-#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
#include "BKE_mesh.h"
#include "BKE_pointcloud.h"
@@ -15,10 +14,13 @@
#include "DNA_meshdata_types.h"
#include "DNA_pointcloud_types.h"
+#include "BLI_array_utils.hh"
#include "BLI_color.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_span.hh"
+#include "FN_field.hh"
+
#include "BLT_translation.h"
#include "CLG_log.h"
@@ -56,7 +58,8 @@ const char *no_procedural_access_message =
bool allow_procedural_attribute_access(StringRef attribute_name)
{
- return !attribute_name.startswith(".selection");
+ return !attribute_name.startswith(".sculpt") && !attribute_name.startswith(".selection") &&
+ !attribute_name.startswith(".hide");
}
static int attribute_data_type_complexity(const eCustomDataType data_type)
@@ -161,12 +164,19 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data
const AttributeInit &initializer)
{
switch (initializer.type) {
- case AttributeInit::Type::Default: {
- void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num);
+ case AttributeInit::Type::Construct: {
+ void *data = CustomData_add_layer(
+ &custom_data, data_type, CD_CONSTRUCT, nullptr, domain_num);
+ return data != nullptr;
+ }
+ case AttributeInit::Type::DefaultValue: {
+ void *data = CustomData_add_layer(
+ &custom_data, data_type, CD_SET_DEFAULT, nullptr, domain_num);
return data != nullptr;
}
case AttributeInit::Type::VArray: {
- void *data = CustomData_add_layer(&custom_data, data_type, CD_DEFAULT, nullptr, domain_num);
+ void *data = CustomData_add_layer(
+ &custom_data, data_type, CD_CONSTRUCT, nullptr, domain_num);
if (data == nullptr) {
return false;
}
@@ -175,7 +185,7 @@ static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data
return true;
}
case AttributeInit::Type::MoveArray: {
- void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *source_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
void *data = CustomData_add_layer(
&custom_data, data_type, CD_ASSIGN, source_data, domain_num);
if (data == nullptr) {
@@ -214,36 +224,38 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr
const int domain_num,
const AttributeInit &initializer)
{
+ const int old_layer_num = custom_data.totlayer;
switch (initializer.type) {
- case AttributeInit::Type::Default: {
- void *data = add_generic_custom_data_layer(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id);
- return data != nullptr;
+ case AttributeInit::Type::Construct: {
+ add_generic_custom_data_layer(
+ custom_data, data_type, CD_CONSTRUCT, nullptr, domain_num, attribute_id);
+ break;
+ }
+ case AttributeInit::Type::DefaultValue: {
+ add_generic_custom_data_layer(
+ custom_data, data_type, CD_SET_DEFAULT, nullptr, domain_num, attribute_id);
+ break;
}
case AttributeInit::Type::VArray: {
void *data = add_generic_custom_data_layer(
- custom_data, data_type, CD_DEFAULT, nullptr, domain_num, attribute_id);
- if (data == nullptr) {
- return false;
+ custom_data, data_type, CD_CONSTRUCT, nullptr, domain_num, attribute_id);
+ if (data != nullptr) {
+ const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
+ varray.materialize_to_uninitialized(varray.index_range(), data);
}
- const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
- varray.materialize_to_uninitialized(varray.index_range(), data);
- return true;
+ break;
}
case AttributeInit::Type::MoveArray: {
- void *source_data = static_cast<const AttributeInitMove &>(initializer).data;
+ void *source_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
void *data = add_generic_custom_data_layer(
custom_data, data_type, CD_ASSIGN, source_data, domain_num, attribute_id);
- if (data == nullptr) {
+ if (source_data != nullptr && data == nullptr) {
MEM_freeN(source_data);
- return false;
}
- return true;
+ break;
}
}
-
- BLI_assert_unreachable();
- return false;
+ return old_layer_num < custom_data.totlayer;
}
static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
@@ -258,6 +270,14 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
return layer.name == attribute_id.name();
}
+bool BuiltinCustomDataLayerProvider::layer_exists(const CustomData &custom_data) const
+{
+ if (stored_as_named_attribute_) {
+ return CustomData_get_named_layer_index(&custom_data, stored_type_, name_.c_str()) != -1;
+ }
+ return CustomData_has_layer(&custom_data, stored_type_);
+}
+
GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) const
{
const CustomData *custom_data = custom_data_access_.get_const_custom_data(owner);
@@ -265,7 +285,16 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) cons
return {};
}
- const void *data;
+ /* When the number of elements is zero, layers might have null data but still exist. */
+ const int element_num = custom_data_access_.get_element_num(owner);
+ if (element_num == 0) {
+ if (this->layer_exists(*custom_data)) {
+ return as_read_attribute_(nullptr, 0);
+ }
+ return {};
+ }
+
+ const void *data = nullptr;
if (stored_as_named_attribute_) {
data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str());
}
@@ -275,8 +304,6 @@ GVArray BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) cons
if (data == nullptr) {
return {};
}
-
- const int element_num = custom_data_access_.get_element_num(owner);
return as_read_attribute_(data, element_num);
}
@@ -289,40 +316,32 @@ GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner)
if (custom_data == nullptr) {
return {};
}
- const int element_num = custom_data_access_.get_element_num(owner);
- void *data;
- if (stored_as_named_attribute_) {
- data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str());
- }
- else {
- data = CustomData_get_layer(custom_data, stored_type_);
+ std::function<void()> tag_modified_fn;
+ if (update_on_change_ != nullptr) {
+ tag_modified_fn = [owner, update = update_on_change_]() { update(owner); };
}
- if (data == nullptr) {
+
+ /* When the number of elements is zero, layers might have null data but still exist. */
+ const int element_num = custom_data_access_.get_element_num(owner);
+ if (element_num == 0) {
+ if (this->layer_exists(*custom_data)) {
+ return {as_write_attribute_(nullptr, 0), domain_, std::move(tag_modified_fn)};
+ }
return {};
}
- void *new_data;
+ void *data = nullptr;
if (stored_as_named_attribute_) {
- new_data = CustomData_duplicate_referenced_layer_named(
+ data = CustomData_duplicate_referenced_layer_named(
custom_data, stored_type_, name_.c_str(), element_num);
}
else {
- new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, element_num);
+ data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, element_num);
}
-
- if (data != new_data) {
- if (custom_data_access_.update_custom_data_pointers) {
- custom_data_access_.update_custom_data_pointers(owner);
- }
- data = new_data;
- }
-
- std::function<void()> tag_modified_fn;
- if (update_on_write_ != nullptr) {
- tag_modified_fn = [owner, update = update_on_write_]() { update(owner); };
+ if (data == nullptr) {
+ return {};
}
-
return {as_write_attribute_(data, element_num), domain_, std::move(tag_modified_fn)};
}
@@ -336,12 +355,16 @@ bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const
return {};
}
+ auto update = [&]() {
+ if (update_on_change_ != nullptr) {
+ update_on_change_(owner);
+ }
+ };
+
const int element_num = custom_data_access_.get_element_num(owner);
if (stored_as_named_attribute_) {
if (CustomData_free_layer_named(custom_data, name_.c_str(), element_num)) {
- if (custom_data_access_.update_custom_data_pointers) {
- custom_data_access_.update_custom_data_pointers(owner);
- }
+ update();
return true;
}
return false;
@@ -349,11 +372,10 @@ bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const
const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
if (CustomData_free_layer(custom_data, stored_type_, element_num, layer_index)) {
- if (custom_data_access_.update_custom_data_pointers) {
- custom_data_access_.update_custom_data_pointers(owner);
- }
+ update();
return true;
}
+
return false;
}
@@ -369,29 +391,21 @@ bool BuiltinCustomDataLayerProvider::try_create(void *owner,
}
const int element_num = custom_data_access_.get_element_num(owner);
- bool success;
if (stored_as_named_attribute_) {
if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) {
/* Exists already. */
return false;
}
- success = add_custom_data_layer_from_attribute_init(
+ return add_custom_data_layer_from_attribute_init(
name_, *custom_data, stored_type_, element_num, initializer);
}
- else {
- if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
- /* Exists already. */
- return false;
- }
- success = add_builtin_type_custom_data_layer_from_init(
- *custom_data, stored_type_, element_num, initializer);
- }
- if (success) {
- if (custom_data_access_.update_custom_data_pointers) {
- custom_data_access_.update_custom_data_pointers(owner);
- }
+
+ if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
+ /* Exists already. */
+ return false;
}
- return success;
+ return add_builtin_type_custom_data_layer_from_init(
+ *custom_data, stored_type_, element_num, initializer);
}
bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
@@ -553,15 +567,9 @@ GAttributeWriter NamedLegacyCustomDataProvider::try_get_for_write(
if (layer.type == stored_type_) {
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const int element_num = custom_data_access_.get_element_num(owner);
- void *data_old = layer.data;
- void *data_new = CustomData_duplicate_referenced_layer_named(
+ void *data = CustomData_duplicate_referenced_layer_named(
custom_data, stored_type_, layer.name, element_num);
- if (data_old != data_new) {
- if (custom_data_access_.update_custom_data_pointers) {
- custom_data_access_.update_custom_data_pointers(owner);
- }
- }
- return {as_write_attribute_(layer.data, element_num), domain_};
+ return {as_write_attribute_(data, element_num), domain_};
}
}
}
@@ -581,9 +589,6 @@ bool NamedLegacyCustomDataProvider::try_delete(void *owner,
if (custom_data_layer_matches_attribute_id(layer, attribute_id)) {
const int element_num = custom_data_access_.get_element_num(owner);
CustomData_free_layer(custom_data, stored_type_, element_num, i);
- if (custom_data_access_.update_custom_data_pointers) {
- custom_data_access_.update_custom_data_pointers(owner);
- }
return true;
}
}
@@ -698,7 +703,7 @@ bool CustomDataAttributes::create(const AttributeIDRef &attribute_id,
const eCustomDataType data_type)
{
void *result = add_generic_custom_data_layer(
- data, data_type, CD_DEFAULT, nullptr, size_, attribute_id);
+ data, data_type, CD_SET_DEFAULT, nullptr, size_, attribute_id);
return result != nullptr;
}
@@ -725,8 +730,22 @@ bool CustomDataAttributes::remove(const AttributeIDRef &attribute_id)
void CustomDataAttributes::reallocate(const int size)
{
+ const int old_size = size_;
size_ = size;
- CustomData_realloc(&data, size);
+ CustomData_realloc(&data, old_size, size_);
+ if (size_ > old_size) {
+ /* Fill default new values. */
+ const int new_elements_num = size_ - old_size;
+ this->foreach_attribute(
+ [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
+ GMutableSpan new_data = this->get_for_write(id)->take_back(new_elements_num);
+ const CPPType &type = new_data.type();
+ type.fill_assign_n(type.default_value(), new_data.data(), new_data.size());
+ return true;
+ },
+ /* Dummy. */
+ ATTR_DOMAIN_POINT);
+ }
}
void CustomDataAttributes::clear()
@@ -748,28 +767,8 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call
return true;
}
-void CustomDataAttributes::reorder(Span<AttributeIDRef> new_order)
-{
- BLI_assert(new_order.size() == data.totlayer);
-
- Map<AttributeIDRef, int> old_order;
- old_order.reserve(data.totlayer);
- Array<CustomDataLayer> old_layers(Span(data.layers, data.totlayer));
- for (const int i : old_layers.index_range()) {
- old_order.add_new(attribute_id_from_custom_data_layer(old_layers[i]), i);
- }
-
- MutableSpan layers(data.layers, data.totlayer);
- for (const int i : layers.index_range()) {
- const int old_index = old_order.lookup(new_order[i]);
- layers[i] = old_layers[old_index];
- }
-
- CustomData_update_typemap(&data);
-}
-
/* -------------------------------------------------------------------- */
-/** \name Geometry Component
+/** \name Attribute API
* \{ */
static blender::GVArray try_adapt_data_type(blender::GVArray varray,
@@ -780,123 +779,6 @@ static blender::GVArray try_adapt_data_type(blender::GVArray varray,
return conversions.try_convert(std::move(varray), to_type);
}
-GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &context,
- IndexMask mask,
- ResourceScope &UNUSED(scope)) const
-{
- if (const GeometryComponentFieldContext *geometry_context =
- dynamic_cast<const GeometryComponentFieldContext *>(&context)) {
- const GeometryComponent &component = geometry_context->geometry_component();
- const eAttrDomain domain = geometry_context->domain();
- return this->get_varray_for_context(component, domain, mask);
- }
- return {};
-}
-
-GVArray AttributeFieldInput::get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask UNUSED(mask)) const
-{
- const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_);
- if (auto attributes = component.attributes()) {
- return attributes->lookup(name_, domain, data_type);
- }
- return {};
-}
-
-std::string AttributeFieldInput::socket_inspection_name() const
-{
- std::stringstream ss;
- ss << '"' << name_ << '"' << TIP_(" attribute from geometry");
- return ss.str();
-}
-
-uint64_t AttributeFieldInput::hash() const
-{
- return get_default_hash_2(name_, type_);
-}
-
-bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
-{
- if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) {
- return name_ == other_typed->name_ && type_ == other_typed->type_;
- }
- return false;
-}
-
-static StringRef get_random_id_attribute_name(const eAttrDomain domain)
-{
- switch (domain) {
- case ATTR_DOMAIN_POINT:
- case ATTR_DOMAIN_INSTANCE:
- return "id";
- default:
- return "";
- }
-}
-
-GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask mask) const
-{
-
- const StringRef name = get_random_id_attribute_name(domain);
- if (auto attributes = component.attributes()) {
- if (GVArray attribute = attributes->lookup(name, domain, CD_PROP_INT32)) {
- return attribute;
- }
- }
-
- /* Use the index as the fallback if no random ID attribute exists. */
- return fn::IndexFieldInput::get_index_varray(mask);
-}
-
-std::string IDAttributeFieldInput::socket_inspection_name() const
-{
- return TIP_("ID / Index");
-}
-
-uint64_t IDAttributeFieldInput::hash() const
-{
- /* All random ID attribute inputs are the same within the same evaluation context. */
- return 92386459827;
-}
-
-bool IDAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
-{
- /* All random ID attribute inputs are the same within the same evaluation context. */
- return dynamic_cast<const IDAttributeFieldInput *>(&other) != nullptr;
-}
-
-GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask UNUSED(mask)) const
-{
- const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_);
- return component.attributes()->lookup(anonymous_id_.get(), domain, data_type);
-}
-
-std::string AnonymousAttributeFieldInput::socket_inspection_name() const
-{
- std::stringstream ss;
- ss << '"' << debug_name_ << '"' << TIP_(" from ") << producer_name_;
- return ss.str();
-}
-
-uint64_t AnonymousAttributeFieldInput::hash() const
-{
- return get_default_hash_2(anonymous_id_.get(), type_);
-}
-
-bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
-{
- if (const AnonymousAttributeFieldInput *other_typed =
- dynamic_cast<const AnonymousAttributeFieldInput *>(&other)) {
- return anonymous_id_.get() == other_typed->anonymous_id_.get() && type_ == other_typed->type_;
- }
- return false;
-}
-
GVArray AttributeAccessor::lookup(const AttributeIDRef &attribute_id,
const std::optional<eAttrDomain> domain,
const std::optional<eCustomDataType> data_type) const
@@ -968,6 +850,59 @@ void MutableAttributeAccessor::remove_anonymous()
}
}
+/**
+ * Debug utility that checks whether the #finish function of an #AttributeWriter has been called.
+ */
+#ifdef DEBUG
+struct FinishCallChecker {
+ std::string name;
+ bool finish_called = false;
+ std::function<void()> real_finish_fn;
+
+ ~FinishCallChecker()
+ {
+ if (!this->finish_called) {
+ std::cerr << "Forgot to call `finish()` for '" << this->name << "'.\n";
+ }
+ }
+};
+#endif
+
+GAttributeWriter MutableAttributeAccessor::lookup_for_write(const AttributeIDRef &attribute_id)
+{
+ GAttributeWriter attribute = fn_->lookup_for_write(owner_, attribute_id);
+ /* Check that the #finish method is called in debug builds. */
+#ifdef DEBUG
+ if (attribute) {
+ auto checker = std::make_shared<FinishCallChecker>();
+ if (attribute_id.is_named()) {
+ checker->name = attribute_id.name();
+ }
+ else {
+ checker->name = BKE_anonymous_attribute_id_debug_name(&attribute_id.anonymous_id());
+ }
+ checker->real_finish_fn = attribute.tag_modified_fn;
+ attribute.tag_modified_fn = [checker]() {
+ if (checker->real_finish_fn) {
+ checker->real_finish_fn();
+ }
+ checker->finish_called = true;
+ };
+ }
+#endif
+ return attribute;
+}
+
+GSpanAttributeWriter MutableAttributeAccessor::lookup_for_write_span(
+ const AttributeIDRef &attribute_id)
+{
+ GAttributeWriter attribute = this->lookup_for_write(attribute_id);
+ if (attribute) {
+ return GSpanAttributeWriter{std::move(attribute), true};
+ }
+ return {};
+}
+
GAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write(
const AttributeIDRef &attribute_id,
const eAttrDomain domain,
@@ -1004,13 +939,85 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_span(
GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span(
const AttributeIDRef &attribute_id, const eAttrDomain domain, const eCustomDataType data_type)
{
- GAttributeWriter attribute = this->lookup_or_add_for_write(attribute_id, domain, data_type);
+ GAttributeWriter attribute = this->lookup_or_add_for_write(
+ attribute_id, domain, data_type, AttributeInitConstruct());
if (attribute) {
return GSpanAttributeWriter{std::move(attribute), false};
}
return {};
}
+fn::GField AttributeValidator::validate_field_if_necessary(const fn::GField &field) const
+{
+ if (function) {
+ auto validate_op = fn::FieldOperation::Create(*function, {field});
+ return fn::GField(validate_op);
+ }
+ return field;
+}
+
+Vector<AttributeTransferData> retrieve_attributes_for_transfer(
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
+ const eAttrDomainMask domain_mask,
+ const Set<std::string> &skip)
+{
+ Vector<AttributeTransferData> attributes;
+ src_attributes.for_all(
+ [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
+ if (!(ATTR_DOMAIN_AS_MASK(meta_data.domain) & domain_mask)) {
+ return true;
+ }
+ if (id.is_named() && skip.contains(id.name())) {
+ return true;
+ }
+ if (!id.should_be_kept()) {
+ return true;
+ }
+
+ GVArray src = src_attributes.lookup(id, meta_data.domain);
+ BLI_assert(src);
+ bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
+ id, meta_data.domain, meta_data.data_type);
+ BLI_assert(dst);
+ attributes.append({std::move(src), meta_data, std::move(dst)});
+
+ return true;
+ });
+ return attributes;
+}
+
+void copy_attribute_domain(const AttributeAccessor src_attributes,
+ MutableAttributeAccessor dst_attributes,
+ const IndexMask selection,
+ const eAttrDomain domain,
+ const Set<std::string> &skip)
+{
+ src_attributes.for_all(
+ [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData &meta_data) {
+ if (meta_data.domain != domain) {
+ return true;
+ }
+ if (id.is_named() && skip.contains(id.name())) {
+ return true;
+ }
+ if (!id.should_be_kept()) {
+ return true;
+ }
+
+ const GVArray src = src_attributes.lookup(id, meta_data.domain);
+ BLI_assert(src);
+
+ /* Copy attribute. */
+ GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
+ id, domain, meta_data.data_type);
+ array_utils::copy(src, selection, dst.span);
+ dst.finish();
+
+ return true;
+ });
+}
+
} // namespace blender::bke
/** \} */
diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh
index 17432fa2726..5fbca283399 100644
--- a/source/blender/blenkernel/intern/attribute_access_intern.hh
+++ b/source/blender/blenkernel/intern/attribute_access_intern.hh
@@ -23,7 +23,6 @@ struct CustomDataAccessInfo {
CustomDataGetter get_custom_data;
ConstCustomDataGetter get_const_custom_data;
GetElementNum get_element_num;
- UpdateCustomDataPointers update_custom_data_pointers;
};
/**
@@ -54,6 +53,7 @@ class BuiltinAttributeProvider {
const CreatableEnum createable_;
const WritableEnum writable_;
const DeletableEnum deletable_;
+ const AttributeValidator validator_;
public:
BuiltinAttributeProvider(std::string name,
@@ -61,13 +61,15 @@ class BuiltinAttributeProvider {
const eCustomDataType data_type,
const CreatableEnum createable,
const WritableEnum writable,
- const DeletableEnum deletable)
+ const DeletableEnum deletable,
+ AttributeValidator validator = {})
: name_(std::move(name)),
domain_(domain),
data_type_(data_type),
createable_(createable),
writable_(writable),
- deletable_(deletable)
+ deletable_(deletable),
+ validator_(validator)
{
}
@@ -91,6 +93,11 @@ class BuiltinAttributeProvider {
{
return data_type_;
}
+
+ AttributeValidator validator() const
+ {
+ return validator_;
+ }
};
/**
@@ -125,10 +132,7 @@ class DynamicAttributesProvider {
*/
class CustomDataAttributeProvider final : public DynamicAttributesProvider {
private:
- static constexpr uint64_t supported_types_mask = CD_MASK_PROP_FLOAT | CD_MASK_PROP_FLOAT2 |
- CD_MASK_PROP_FLOAT3 | CD_MASK_PROP_INT32 |
- CD_MASK_PROP_COLOR | CD_MASK_PROP_BOOL |
- CD_MASK_PROP_INT8 | CD_MASK_PROP_BYTE_COLOR;
+ static constexpr uint64_t supported_types_mask = CD_MASK_PROP_ALL;
const eAttrDomain domain_;
const CustomDataAccessInfo custom_data_access_;
@@ -226,12 +230,12 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
using AsReadAttribute = GVArray (*)(const void *data, int element_num);
using AsWriteAttribute = GVMutableArray (*)(void *data, int element_num);
using UpdateOnRead = void (*)(const void *owner);
- using UpdateOnWrite = void (*)(void *owner);
+ using UpdateOnChange = void (*)(void *owner);
const eCustomDataType stored_type_;
const CustomDataAccessInfo custom_data_access_;
const AsReadAttribute as_read_attribute_;
const AsWriteAttribute as_write_attribute_;
- const UpdateOnWrite update_on_write_;
+ const UpdateOnChange update_on_change_;
bool stored_as_named_attribute_;
public:
@@ -245,14 +249,20 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
const CustomDataAccessInfo custom_data_access,
const AsReadAttribute as_read_attribute,
const AsWriteAttribute as_write_attribute,
- const UpdateOnWrite update_on_write)
- : BuiltinAttributeProvider(
- std::move(attribute_name), domain, attribute_type, creatable, writable, deletable),
+ const UpdateOnChange update_on_write,
+ const AttributeValidator validator = {})
+ : BuiltinAttributeProvider(std::move(attribute_name),
+ domain,
+ attribute_type,
+ creatable,
+ writable,
+ deletable,
+ validator),
stored_type_(stored_type),
custom_data_access_(custom_data_access),
as_read_attribute_(as_read_attribute),
as_write_attribute_(as_write_attribute),
- update_on_write_(update_on_write),
+ update_on_change_(update_on_write),
stored_as_named_attribute_(data_type_ == stored_type_)
{
}
@@ -262,6 +272,9 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
bool try_delete(void *owner) const final;
bool try_create(void *owner, const AttributeInit &initializer) const final;
bool exists(const void *owner) const final;
+
+ private:
+ bool layer_exists(const CustomData &custom_data) const;
};
/**
@@ -380,6 +393,21 @@ inline bool for_all(const void *owner,
}
template<const ComponentAttributeProviders &providers>
+inline AttributeValidator lookup_validator(const void * /*owner*/,
+ const blender::bke::AttributeIDRef &attribute_id)
+{
+ if (!attribute_id.is_named()) {
+ return {};
+ }
+ const BuiltinAttributeProvider *provider =
+ providers.builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr);
+ if (!provider) {
+ return {};
+ }
+ return provider->validator();
+}
+
+template<const ComponentAttributeProviders &providers>
inline bool contains(const void *owner, const blender::bke::AttributeIDRef &attribute_id)
{
bool found = false;
@@ -490,6 +518,7 @@ inline AttributeAccessorFunctions accessor_functions_for_providers()
lookup<providers>,
nullptr,
for_all<providers>,
+ lookup_validator<providers>,
lookup_for_write<providers>,
remove<providers>,
add<providers>};
diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc
index c38df2a2969..d8102b4eeb8 100644
--- a/source/blender/blenkernel/intern/attribute_math.cc
+++ b/source/blender/blenkernel/intern/attribute_math.cc
@@ -4,13 +4,31 @@
namespace blender::attribute_math {
-ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> output_buffer,
+ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
ColorGeometry4f default_color)
- : buffer_(output_buffer),
- default_color_(default_color),
- total_weights_(output_buffer.size(), 0.0f)
+ : ColorGeometry4fMixer(buffer, buffer.index_range(), default_color)
+{
+}
+
+ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer,
+ const IndexMask mask,
+ const ColorGeometry4f default_color)
+ : buffer_(buffer), default_color_(default_color), total_weights_(buffer.size(), 0.0f)
{
- buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f));
+ const ColorGeometry4f zero{0.0f, 0.0f, 0.0f, 0.0f};
+ mask.foreach_index([&](const int64_t i) { buffer_[i] = zero; });
+}
+
+void ColorGeometry4fMixer::set(const int64_t index,
+ const ColorGeometry4f &color,
+ const float weight)
+{
+ BLI_assert(weight >= 0.0f);
+ buffer_[index].r = color.r * weight;
+ buffer_[index].g = color.g * weight;
+ buffer_[index].b = color.b * weight;
+ buffer_[index].a = color.a * weight;
+ total_weights_[index] = weight;
}
void ColorGeometry4fMixer::mix_in(const int64_t index,
@@ -28,7 +46,12 @@ void ColorGeometry4fMixer::mix_in(const int64_t index,
void ColorGeometry4fMixer::finalize()
{
- for (const int64_t i : buffer_.index_range()) {
+ this->finalize(buffer_.index_range());
+}
+
+void ColorGeometry4fMixer::finalize(const IndexMask mask)
+{
+ mask.foreach_index([&](const int64_t i) {
const float weight = total_weights_[i];
ColorGeometry4f &output_color = buffer_[i];
if (weight > 0.0f) {
@@ -41,16 +64,37 @@ void ColorGeometry4fMixer::finalize()
else {
output_color = default_color_;
}
- }
+ });
}
ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer,
- ColorGeometry4b default_color)
+ const ColorGeometry4b default_color)
+ : ColorGeometry4bMixer(buffer, buffer.index_range(), default_color)
+{
+}
+
+ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer,
+ const IndexMask mask,
+ const ColorGeometry4b default_color)
: buffer_(buffer),
default_color_(default_color),
total_weights_(buffer.size(), 0.0f),
accumulation_buffer_(buffer.size(), float4(0, 0, 0, 0))
{
+ const ColorGeometry4b zero{0, 0, 0, 0};
+ mask.foreach_index([&](const int64_t i) { buffer_[i] = zero; });
+}
+
+void ColorGeometry4bMixer::ColorGeometry4bMixer::set(int64_t index,
+ const ColorGeometry4b &color,
+ const float weight)
+{
+ BLI_assert(weight >= 0.0f);
+ accumulation_buffer_[index][0] = color.r * weight;
+ accumulation_buffer_[index][1] = color.g * weight;
+ accumulation_buffer_[index][2] = color.b * weight;
+ accumulation_buffer_[index][3] = color.a * weight;
+ total_weights_[index] = weight;
}
void ColorGeometry4bMixer::mix_in(int64_t index, const ColorGeometry4b &color, float weight)
@@ -66,7 +110,12 @@ void ColorGeometry4bMixer::mix_in(int64_t index, const ColorGeometry4b &color, f
void ColorGeometry4bMixer::finalize()
{
- for (const int64_t i : buffer_.index_range()) {
+ this->finalize(buffer_.index_range());
+}
+
+void ColorGeometry4bMixer::finalize(const IndexMask mask)
+{
+ mask.foreach_index([&](const int64_t i) {
const float weight = total_weights_[i];
const float4 &accum_value = accumulation_buffer_[i];
ColorGeometry4b &output_color = buffer_[i];
@@ -80,7 +129,7 @@ void ColorGeometry4bMixer::finalize()
else {
output_color = default_color_;
}
- }
+ });
}
} // namespace blender::attribute_math
diff --git a/source/blender/blenkernel/intern/blender_copybuffer.c b/source/blender/blenkernel/intern/blender_copybuffer.c
index 4b507beb6b3..82df2714a71 100644
--- a/source/blender/blenkernel/intern/blender_copybuffer.c
+++ b/source/blender/blenkernel/intern/blender_copybuffer.c
@@ -150,7 +150,7 @@ int BKE_copybuffer_paste(bContext *C,
return 0;
}
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
copybuffer_append(lapp_context, bmain, reports);
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 70c3dc2de39..bf40d07054d 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -364,7 +364,7 @@ static void setup_app_data(bContext *C,
BKE_lib_override_library_main_hierarchy_root_ensure(bmain);
}
- bmain->recovered = 0;
+ bmain->recovered = false;
/* startup.blend or recovered startup */
if (is_startup) {
@@ -372,7 +372,7 @@ static void setup_app_data(bContext *C,
}
else if (recover) {
/* In case of autosave or quit.blend, use original filepath instead. */
- bmain->recovered = 1;
+ bmain->recovered = true;
STRNCPY(bmain->filepath, bfd->filepath);
}
@@ -453,7 +453,7 @@ static void handle_subversion_warning(Main *main, BlendFileReadReport *reports)
(main->minversionfile == BLENDER_FILE_VERSION &&
main->minsubversionfile > BLENDER_FILE_SUBVERSION)) {
BKE_reportf(reports->reports,
- RPT_ERROR,
+ RPT_WARNING,
"File written by newer Blender binary (%d.%d), expect loss of data!",
main->minversionfile,
main->minsubversionfile);
diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c
index ebf48acde0f..e3b76122ff0 100644
--- a/source/blender/blenkernel/intern/blendfile_link_append.c
+++ b/source/blender/blenkernel/intern/blendfile_link_append.c
@@ -493,6 +493,7 @@ static void loose_data_instantiate_ensure_active_collection(
static void loose_data_instantiate_object_base_instance_init(Main *bmain,
Collection *collection,
Object *ob,
+ const Scene *scene,
ViewLayer *view_layer,
const View3D *v3d,
const int flag,
@@ -506,7 +507,7 @@ static void loose_data_instantiate_object_base_instance_init(Main *bmain,
}
BKE_collection_object_add(bmain, collection, ob);
-
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (v3d != NULL) {
@@ -686,8 +687,14 @@ static void loose_data_instantiate_collection_process(
/* TODO: why is it OK to make this active here but not in other situations?
* See other callers of #object_base_instance_init */
const bool set_active = set_selected;
- loose_data_instantiate_object_base_instance_init(
- bmain, active_collection, ob, view_layer, v3d, lapp_context->params->flag, set_active);
+ loose_data_instantiate_object_base_instance_init(bmain,
+ active_collection,
+ ob,
+ scene,
+ view_layer,
+ v3d,
+ lapp_context->params->flag,
+ set_active);
/* Assign the collection. */
ob->instance_collection = collection;
@@ -698,6 +705,7 @@ static void loose_data_instantiate_collection_process(
else {
/* Add collection as child of active collection. */
BKE_collection_child_add(bmain, active_collection, collection);
+ BKE_view_layer_synced_ensure(scene, view_layer);
if ((lapp_context->params->flag & FILE_AUTOSELECT) != 0) {
LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) {
@@ -717,6 +725,7 @@ static void loose_data_instantiate_object_process(LooseDataInstantiateContext *i
{
BlendfileLinkAppendContext *lapp_context = instantiate_context->lapp_context;
Main *bmain = lapp_context->params->bmain;
+ const Scene *scene = lapp_context->params->context.scene;
ViewLayer *view_layer = lapp_context->params->context.view_layer;
const View3D *v3d = lapp_context->params->context.v3d;
@@ -762,6 +771,7 @@ static void loose_data_instantiate_object_process(LooseDataInstantiateContext *i
loose_data_instantiate_object_base_instance_init(bmain,
active_collection,
ob,
+ scene,
view_layer,
v3d,
lapp_context->params->flag,
@@ -809,6 +819,7 @@ static void loose_data_instantiate_obdata_process(LooseDataInstantiateContext *i
loose_data_instantiate_object_base_instance_init(bmain,
active_collection,
ob,
+ scene,
view_layer,
v3d,
lapp_context->params->flag,
@@ -1232,8 +1243,9 @@ void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *re
mainl = BLO_library_link_begin(&blo_handle, libname, lapp_context->params);
lib = mainl->curlib;
- BLI_assert(lib);
- UNUSED_VARS_NDEBUG(lib);
+ BLI_assert(lib != NULL);
+ /* In case lib was already existing but not found originally, see T99820. */
+ lib->id.tag &= ~LIB_TAG_MISSING;
if (mainl->versionfile < 250) {
BKE_reportf(reports,
diff --git a/source/blender/blenkernel/intern/boids.c b/source/blender/blenkernel/intern/boids.c
index a86d6e25ee9..2e07b52c7bf 100644
--- a/source/blender/blenkernel/intern/boids.c
+++ b/source/blender/blenkernel/intern/boids.c
@@ -1567,7 +1567,7 @@ void boid_body(BoidBrainData *bbd, ParticleData *pa)
cross_v3_v3v3(mat[1], mat[2], mat[0]);
/* apply rotation */
- mat3_to_quat_is_ok(q, mat);
+ mat3_to_quat_legacy(q, mat);
copy_qt_qt(pa->state.rot, q);
}
diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc
index ffa63f9792f..a998fc0a75f 100644
--- a/source/blender/blenkernel/intern/brush.cc
+++ b/source/blender/blenkernel/intern/brush.cc
@@ -186,6 +186,7 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->paint_curve, IDWALK_CB_USER);
if (brush->gpencil_settings) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material, IDWALK_CB_USER);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material_alt, IDWALK_CB_USER);
}
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_texture_mtex_foreach_id(data, &brush->mtex));
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
@@ -346,6 +347,7 @@ static void brush_blend_read_lib(BlendLibReader *reader, ID *id)
else {
brush->gpencil_settings->material = nullptr;
}
+ BLO_read_id_address(reader, brush->id.lib, &brush->gpencil_settings->material_alt);
}
}
@@ -358,6 +360,7 @@ static void brush_blend_read_expand(BlendExpander *expander, ID *id)
BLO_expand(expander, brush->paint_curve);
if (brush->gpencil_settings != nullptr) {
BLO_expand(expander, brush->gpencil_settings->material);
+ BLO_expand(expander, brush->gpencil_settings->material_alt);
}
}
@@ -410,7 +413,7 @@ IDTypeInfo IDType_ID_BR = {
/* foreach_id */ brush_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ brush_foreach_path,
- /* owner_get */ nullptr,
+ /* owner_pointer_get */ nullptr,
/* blend_write */ brush_blend_write,
/* blend_read_data */ brush_blend_read_data,
@@ -444,7 +447,8 @@ static void brush_defaults(Brush *brush)
const Brush *brush_def = DNA_struct_default_get(Brush);
-#define FROM_DEFAULT(member) memcpy(&brush->member, &brush_def->member, sizeof(brush->member))
+#define FROM_DEFAULT(member) \
+ memcpy((void *)&brush->member, (void *)&brush_def->member, sizeof(brush->member))
#define FROM_DEFAULT_PTR(member) memcpy(brush->member, brush_def->member, sizeof(brush->member))
FROM_DEFAULT(blend);
@@ -586,17 +590,17 @@ bool BKE_brush_delete(Main *bmain, Brush *brush)
return true;
}
-/* grease pencil cumapping->preset */
-typedef enum eGPCurveMappingPreset {
+/** Local grease pencil curve mapping preset. */
+using eGPCurveMappingPreset = enum eGPCurveMappingPreset {
GPCURVE_PRESET_PENCIL = 0,
GPCURVE_PRESET_INK = 1,
GPCURVE_PRESET_INKNOISE = 2,
GPCURVE_PRESET_MARKER = 3,
GPCURVE_PRESET_CHISEL_SENSIVITY = 4,
GPCURVE_PRESET_CHISEL_STRENGTH = 5,
-} eGPCurveMappingPreset;
+};
-static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset)
+static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, eGPCurveMappingPreset preset)
{
if (cuma->curve) {
MEM_freeN(cuma->curve);
@@ -703,6 +707,7 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
/* Set vertex mix factor. */
brush->gpencil_settings->vertex_mode = GPPAINT_MODE_BOTH;
brush->gpencil_settings->vertex_factor = 1.0f;
+ brush->gpencil_settings->material_alt = nullptr;
switch (type) {
case GP_BRUSH_PRESET_AIRBRUSH: {
@@ -977,7 +982,6 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
case GP_BRUSH_PRESET_FILL_AREA: {
brush->size = 5.0f;
- brush->gpencil_settings->fill_leak = 3;
brush->gpencil_settings->fill_threshold = 0.1f;
brush->gpencil_settings->fill_simplylvl = 1;
brush->gpencil_settings->fill_factor = 1.0f;
@@ -2511,8 +2515,10 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool
if (display_gradient || have_texture) {
for (int i = 0; i < side; i++) {
for (int j = 0; j < side; j++) {
- float magn = sqrtf(pow2f(i - half) + pow2f(j - half));
- im->rect_float[i * side + j] *= BKE_brush_curve_strength_clamped(br, magn, half);
+ const float magn = sqrtf(pow2f(i - half) + pow2f(j - half));
+ const float strength = BKE_brush_curve_strength_clamped(br, magn, half);
+ im->rect_float[i * side + j] = (have_texture) ? im->rect_float[i * side + j] * strength :
+ strength;
}
}
}
diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc
index 35c2039634a..9bea8a0d6d3 100644
--- a/source/blender/blenkernel/intern/bvhutils.cc
+++ b/source/blender/blenkernel/intern/bvhutils.cc
@@ -15,10 +15,12 @@
#include "BLI_linklist.h"
#include "BLI_math.h"
+#include "BLI_span.hh"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#include "BKE_attribute.hh"
#include "BKE_bvhutils.h"
#include "BKE_editmesh.h"
#include "BKE_mesh.h"
@@ -26,6 +28,9 @@
#include "MEM_guardedalloc.h"
+using blender::Span;
+using blender::VArray;
+
/* -------------------------------------------------------------------- */
/** \name BVHCache
* \{ */
@@ -1180,9 +1185,13 @@ static BLI_bitmap *loose_edges_map_get(const MEdge *medge,
}
static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly,
+ const VArray<bool> &hide_poly,
const int looptri_len,
int *r_looptri_active_len)
{
+ if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
+ return nullptr;
+ }
BLI_bitmap *looptri_mask = BLI_BITMAP_NEW(looptri_len, __func__);
int looptri_no_hidden_len = 0;
@@ -1190,8 +1199,7 @@ static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly,
int i_poly = 0;
while (looptri_iter != looptri_len) {
int mp_totlooptri = mpoly[i_poly].totloop - 2;
- const MPoly &mp = mpoly[i_poly];
- if (mp.flag & ME_HIDE) {
+ if (hide_poly[i_poly]) {
looptri_iter += mp_totlooptri;
}
else {
@@ -1223,14 +1231,17 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
looptri = BKE_mesh_runtime_looptri_ensure(mesh);
looptri_len = BKE_mesh_runtime_looptri_len(mesh);
}
+ const Span<MVert> verts = mesh->verts();
+ const Span<MEdge> edges = mesh->edges();
+ const Span<MLoop> loops = mesh->loops();
/* Setup BVHTreeFromMesh */
bvhtree_from_mesh_setup_data(nullptr,
bvh_cache_type,
- mesh->mvert,
- mesh->medge,
- mesh->mface,
- mesh->mloop,
+ verts.data(),
+ edges.data(),
+ (const MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE),
+ loops.data(),
looptri,
BKE_mesh_vertex_normals_ensure(mesh),
data);
@@ -1254,36 +1265,49 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
switch (bvh_cache_type) {
case BVHTREE_FROM_LOOSEVERTS:
mask = loose_verts_map_get(
- mesh->medge, mesh->totedge, mesh->mvert, mesh->totvert, &mask_bits_act_len);
+ edges.data(), mesh->totedge, verts.data(), mesh->totvert, &mask_bits_act_len);
ATTR_FALLTHROUGH;
case BVHTREE_FROM_VERTS:
data->tree = bvhtree_from_mesh_verts_create_tree(
- 0.0f, tree_type, 6, mesh->mvert, mesh->totvert, mask, mask_bits_act_len);
+ 0.0f, tree_type, 6, verts.data(), mesh->totvert, mask, mask_bits_act_len);
break;
case BVHTREE_FROM_LOOSEEDGES:
- mask = loose_edges_map_get(mesh->medge, mesh->totedge, &mask_bits_act_len);
+ mask = loose_edges_map_get(edges.data(), mesh->totedge, &mask_bits_act_len);
ATTR_FALLTHROUGH;
case BVHTREE_FROM_EDGES:
data->tree = bvhtree_from_mesh_edges_create_tree(
- mesh->mvert, mesh->medge, mesh->totedge, mask, mask_bits_act_len, 0.0f, tree_type, 6);
+ verts.data(), edges.data(), mesh->totedge, mask, mask_bits_act_len, 0.0f, tree_type, 6);
break;
case BVHTREE_FROM_FACES:
BLI_assert(!(mesh->totface == 0 && mesh->totpoly != 0));
data->tree = bvhtree_from_mesh_faces_create_tree(
- 0.0f, tree_type, 6, mesh->mvert, mesh->mface, mesh->totface, nullptr, -1);
+ 0.0f,
+ tree_type,
+ 6,
+ verts.data(),
+ (const MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE),
+ mesh->totface,
+ nullptr,
+ -1);
break;
- case BVHTREE_FROM_LOOPTRI_NO_HIDDEN:
- mask = looptri_no_hidden_map_get(mesh->mpoly, looptri_len, &mask_bits_act_len);
+ case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: {
+ blender::bke::AttributeAccessor attributes = mesh->attributes();
+ mask = looptri_no_hidden_map_get(
+ mesh->polys().data(),
+ attributes.lookup_or_default(".hide_poly", ATTR_DOMAIN_FACE, false),
+ looptri_len,
+ &mask_bits_act_len);
ATTR_FALLTHROUGH;
+ }
case BVHTREE_FROM_LOOPTRI:
data->tree = bvhtree_from_mesh_looptri_create_tree(0.0f,
tree_type,
6,
- mesh->mvert,
- mesh->mloop,
+ verts.data(),
+ loops.data(),
looptri,
looptri_len,
mask,
@@ -1430,13 +1454,17 @@ BVHTree *BKE_bvhtree_from_pointcloud_get(BVHTreeFromPointCloud *data,
return nullptr;
}
- for (int i = 0; i < pointcloud->totpoint; i++) {
- BLI_bvhtree_insert(tree, i, pointcloud->co[i], 1);
+ blender::bke::AttributeAccessor attributes = pointcloud->attributes();
+ blender::VArraySpan<blender::float3> positions = attributes.lookup_or_default<blender::float3>(
+ "position", ATTR_DOMAIN_POINT, blender::float3(0));
+
+ for (const int i : positions.index_range()) {
+ BLI_bvhtree_insert(tree, i, positions[i], 1);
}
BLI_assert(BLI_bvhtree_get_len(tree) == pointcloud->totpoint);
bvhtree_balance(tree, false);
- data->coords = pointcloud->co;
+ data->coords = (const float(*)[3])positions.data();
data->tree = tree;
data->nearest_callback = nullptr;
diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c
index 6b6b7223a0b..5d19db323f8 100644
--- a/source/blender/blenkernel/intern/cachefile.c
+++ b/source/blender/blenkernel/intern/cachefile.c
@@ -146,7 +146,7 @@ IDTypeInfo IDType_ID_CF = {
.foreach_id = NULL,
.foreach_cache = NULL,
.foreach_path = cache_file_foreach_path,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = cache_file_blend_write,
.blend_read_data = cache_file_blend_read_data,
@@ -429,10 +429,10 @@ bool BKE_cache_file_uses_render_procedural(const CacheFile *cache_file, Scene *s
return cache_file->use_render_procedural;
}
-CacheFileLayer *BKE_cachefile_add_layer(CacheFile *cache_file, const char filename[1024])
+CacheFileLayer *BKE_cachefile_add_layer(CacheFile *cache_file, const char filepath[1024])
{
for (CacheFileLayer *layer = cache_file->layers.first; layer; layer = layer->next) {
- if (STREQ(layer->filepath, filename)) {
+ if (STREQ(layer->filepath, filepath)) {
return NULL;
}
}
@@ -440,7 +440,7 @@ CacheFileLayer *BKE_cachefile_add_layer(CacheFile *cache_file, const char filena
const int num_layers = BLI_listbase_count(&cache_file->layers);
CacheFileLayer *layer = MEM_callocN(sizeof(CacheFileLayer), "CacheFileLayer");
- BLI_strncpy(layer->filepath, filename, sizeof(layer->filepath));
+ BLI_strncpy(layer->filepath, filepath, sizeof(layer->filepath));
BLI_addtail(&cache_file->layers, layer);
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index fbe03ac365c..c3384239cb6 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -25,6 +25,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BKE_action.h"
#include "BKE_anim_data.h"
#include "BKE_camera.h"
#include "BKE_idtype.h"
@@ -71,7 +72,7 @@ static void camera_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src,
Camera *cam_dst = (Camera *)id_dst;
const Camera *cam_src = (const Camera *)id_src;
- /* We never handle usercount here for own data. */
+ /* We never handle user-count here for own data. */
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
BLI_listbase_clear(&cam_dst->bg_images);
@@ -185,7 +186,7 @@ IDTypeInfo IDType_ID_CA = {
.foreach_id = camera_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = camera_blend_write,
.blend_read_data = camera_blend_read_data,
@@ -221,7 +222,16 @@ float BKE_camera_object_dof_distance(const Object *ob)
if (cam->dof.focus_object) {
float view_dir[3], dof_dir[3];
normalize_v3_v3(view_dir, ob->obmat[2]);
- sub_v3_v3v3(dof_dir, ob->obmat[3], cam->dof.focus_object->obmat[3]);
+ bPoseChannel *pchan = BKE_pose_channel_find_name(cam->dof.focus_object->pose,
+ cam->dof.focus_subtarget);
+ if (pchan) {
+ float posemat[4][4];
+ mul_m4_m4m4(posemat, cam->dof.focus_object->obmat, pchan->pose_mat);
+ sub_v3_v3v3(dof_dir, ob->obmat[3], posemat[3]);
+ }
+ else {
+ sub_v3_v3v3(dof_dir, ob->obmat[3], cam->dof.focus_object->obmat[3]);
+ }
return fabsf(dot_v3v3(view_dir, dof_dir));
}
return cam->dof.focus_distance;
@@ -340,7 +350,7 @@ void BKE_camera_params_from_view3d(CameraParams *params,
/* orthographic view */
float sensor_size = BKE_camera_sensor_size(
params->sensor_fit, params->sensor_x, params->sensor_y);
- /* Halve, otherwise too extreme low zbuffer quality. */
+ /* Halve, otherwise too extreme low Z-buffer quality. */
params->clip_end *= 0.5f;
params->clip_start = -params->clip_end;
@@ -391,7 +401,7 @@ void BKE_camera_params_compute_viewplane(
pixsize *= params->zoom;
/* compute view plane:
- * fully centered, zbuffer fills in jittered between -.5 and +.5 */
+ * Fully centered, Z-buffer fills in jittered between `-.5` and `+.5`. */
viewplane.xmin = -0.5f * (float)winx;
viewplane.ymin = -0.5f * params->ycor * (float)winy;
viewplane.xmax = 0.5f * (float)winx;
@@ -559,6 +569,11 @@ void BKE_camera_view_frame(const Scene *scene, const Camera *camera, float r_vec
#define CAMERA_VIEWFRAME_NUM_PLANES 4
+#define Y_MIN 0
+#define Y_MAX 1
+#define Z_MIN 2
+#define Z_MAX 3
+
typedef struct CameraViewFrameData {
float plane_tx[CAMERA_VIEWFRAME_NUM_PLANES][4]; /* 4 planes normalized */
float dist_vals[CAMERA_VIEWFRAME_NUM_PLANES]; /* distance (signed) */
@@ -622,15 +637,13 @@ static void camera_frame_fit_data_init(const Scene *scene,
invert_m4(camera_rotmat_transposed_inversed);
/* Extract frustum planes from projection matrix. */
- planes_from_projmat(
- params->winmat,
- /* left right top bottom near far */
- data->plane_tx[2],
- data->plane_tx[0],
- data->plane_tx[3],
- data->plane_tx[1],
- NULL,
- NULL);
+ planes_from_projmat(params->winmat,
+ data->plane_tx[Y_MIN],
+ data->plane_tx[Y_MAX],
+ data->plane_tx[Z_MIN],
+ data->plane_tx[Z_MAX],
+ NULL,
+ NULL);
/* Rotate planes and get normals from them */
for (uint i = 0; i < CAMERA_VIEWFRAME_NUM_PLANES; i++) {
@@ -670,21 +683,21 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params,
const float *cam_axis_y = data->camera_rotmat[1];
const float *cam_axis_z = data->camera_rotmat[2];
const float *dists = data->dist_vals;
- float scale_diff;
+ const float dist_span_y = dists[Y_MIN] + dists[Y_MAX];
+ const float dist_span_z = dists[Z_MIN] + dists[Z_MAX];
+ const float dist_mid_y = (dists[Y_MIN] - dists[Y_MAX]) * 0.5f;
+ const float dist_mid_z = (dists[Z_MIN] - dists[Z_MAX]) * 0.5f;
+ const float scale_diff = (dist_span_z < dist_span_y) ?
+ (dist_span_z * (BLI_rctf_size_x(&params->viewplane) /
+ BLI_rctf_size_y(&params->viewplane))) :
+ (dist_span_y * (BLI_rctf_size_y(&params->viewplane) /
+ BLI_rctf_size_x(&params->viewplane)));
- if ((dists[0] + dists[2]) > (dists[1] + dists[3])) {
- scale_diff = (dists[1] + dists[3]) *
- (BLI_rctf_size_x(&params->viewplane) / BLI_rctf_size_y(&params->viewplane));
- }
- else {
- scale_diff = (dists[0] + dists[2]) *
- (BLI_rctf_size_y(&params->viewplane) / BLI_rctf_size_x(&params->viewplane));
- }
*r_scale = params->ortho_scale - scale_diff;
zero_v3(r_co);
- madd_v3_v3fl(r_co, cam_axis_x, (dists[2] - dists[0]) * 0.5f + params->shiftx * scale_diff);
- madd_v3_v3fl(r_co, cam_axis_y, (dists[1] - dists[3]) * 0.5f + params->shifty * scale_diff);
+ madd_v3_v3fl(r_co, cam_axis_x, dist_mid_y + (params->shiftx * scale_diff));
+ madd_v3_v3fl(r_co, cam_axis_y, dist_mid_z + (params->shifty * scale_diff));
madd_v3_v3fl(r_co, cam_axis_z, -(data->z_range[0] - 1.0f - params->clip_start));
}
else {
@@ -700,36 +713,37 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params,
plane_from_point_normal_v3(plane_tx[i], co, data->plane_tx[i]);
}
- if ((!isect_plane_plane_v3(plane_tx[0], plane_tx[2], plane_isect_1, plane_isect_1_no)) ||
- (!isect_plane_plane_v3(plane_tx[1], plane_tx[3], plane_isect_2, plane_isect_2_no))) {
+ if ((!isect_plane_plane_v3(
+ plane_tx[Y_MIN], plane_tx[Y_MAX], plane_isect_1, plane_isect_1_no)) ||
+ (!isect_plane_plane_v3(
+ plane_tx[Z_MIN], plane_tx[Z_MAX], plane_isect_2, plane_isect_2_no))) {
return false;
}
add_v3_v3v3(plane_isect_1_other, plane_isect_1, plane_isect_1_no);
add_v3_v3v3(plane_isect_2_other, plane_isect_2, plane_isect_2_no);
- if (isect_line_line_v3(plane_isect_1,
- plane_isect_1_other,
- plane_isect_2,
- plane_isect_2_other,
- plane_isect_pt_1,
- plane_isect_pt_2) == 0) {
+ if (!isect_line_line_v3(plane_isect_1,
+ plane_isect_1_other,
+ plane_isect_2,
+ plane_isect_2_other,
+ plane_isect_pt_1,
+ plane_isect_pt_2)) {
return false;
}
float cam_plane_no[3];
float plane_isect_delta[3];
- float plane_isect_delta_len;
- float shift_fac = BKE_camera_sensor_size(
- params->sensor_fit, params->sensor_x, params->sensor_y) /
- params->lens;
+ const float shift_fac = BKE_camera_sensor_size(
+ params->sensor_fit, params->sensor_x, params->sensor_y) /
+ params->lens;
/* we want (0, 0, -1) transformed by camera_rotmat, this is a quicker shortcut. */
negate_v3_v3(cam_plane_no, data->camera_rotmat[2]);
sub_v3_v3v3(plane_isect_delta, plane_isect_pt_2, plane_isect_pt_1);
- plane_isect_delta_len = len_v3(plane_isect_delta);
+ const float plane_isect_delta_len = len_v3(plane_isect_delta);
if (dot_v3v3(plane_isect_delta, cam_plane_no) > 0.0f) {
copy_v3_v3(r_co, plane_isect_pt_1);
@@ -755,6 +769,11 @@ static bool camera_frame_fit_calc_from_data(CameraParams *params,
return true;
}
+#undef Y_MIN
+#undef Y_MAX
+#undef Z_MIN
+#undef Z_MAX
+
bool BKE_camera_view_frame_fit_to_scene(Depsgraph *depsgraph,
const Scene *scene,
Object *camera_ob,
diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c
index 93286751f92..0261b2d7674 100644
--- a/source/blender/blenkernel/intern/cdderivedmesh.c
+++ b/source/blender/blenkernel/intern/cdderivedmesh.c
@@ -244,43 +244,3 @@ DerivedMesh *CDDM_from_mesh(Mesh *mesh)
{
return cdDM_from_mesh_ex(mesh, CD_REFERENCE, &CD_MASK_MESH);
}
-
-DerivedMesh *CDDM_copy(DerivedMesh *source)
-{
- CDDerivedMesh *cddm = cdDM_create("CDDM_copy cddm");
- DerivedMesh *dm = &cddm->dm;
- int numVerts = source->numVertData;
- int numEdges = source->numEdgeData;
- int numTessFaces = 0;
- int numLoops = source->numLoopData;
- int numPolys = source->numPolyData;
-
- /* NOTE: Don't copy tessellation faces if not requested explicitly. */
-
- /* ensure these are created if they are made on demand */
- source->getVertDataArray(source, CD_ORIGINDEX);
- source->getEdgeDataArray(source, CD_ORIGINDEX);
- source->getPolyDataArray(source, CD_ORIGINDEX);
-
- /* this initializes dm, and copies all non mvert/medge/mface layers */
- DM_from_template(dm, source, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys);
- dm->deformedOnly = source->deformedOnly;
- dm->cd_flag = source->cd_flag;
-
- CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts);
- CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges);
-
- /* now add mvert/medge/mface layers */
- cddm->mvert = source->dupVertArray(source);
- cddm->medge = source->dupEdgeArray(source);
-
- CustomData_add_layer(&dm->vertData, CD_MVERT, CD_ASSIGN, cddm->mvert, numVerts);
- CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_ASSIGN, cddm->medge, numEdges);
-
- DM_DupPolys(source, dm);
-
- cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP);
- cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY);
-
- return dm;
-}
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index 8622174231c..f3bda8b1c99 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -97,7 +97,7 @@ static BVHTree *bvhtree_build_from_cloth(ClothModifierData *clmd, float epsilon)
}
}
else {
- MEdge *edges = cloth->edges;
+ const MEdge *edges = cloth->edges;
for (int i = 0; i < cloth->primitive_num; i++) {
float co[2][3];
@@ -177,7 +177,7 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self)
}
else {
if (verts) {
- MEdge *edges = cloth->edges;
+ const MEdge *edges = cloth->edges;
for (i = 0; i < cloth->primitive_num; i++) {
float co[2][3];
@@ -258,7 +258,7 @@ static int do_step_cloth(
cloth = clmd->clothObject;
verts = cloth->verts;
- mvert = result->mvert;
+ mvert = BKE_mesh_verts_for_write(result);
vert_mass_changed = verts->mass != clmd->sim_parms->mass;
/* force any pinned verts to their constrained location. */
@@ -713,7 +713,6 @@ static bool cloth_from_object(
Object *ob, ClothModifierData *clmd, Mesh *mesh, float UNUSED(framenr), int first)
{
int i = 0;
- MVert *mvert = NULL;
ClothVertex *verts = NULL;
const float(*shapekey_rest)[3] = NULL;
const float tnull[3] = {0, 0, 0};
@@ -755,7 +754,7 @@ static bool cloth_from_object(
shapekey_rest = CustomData_get_layer(&mesh->vdata, CD_CLOTH_ORCO);
}
- mvert = mesh->mvert;
+ MVert *mvert = BKE_mesh_verts_for_write(mesh);
verts = clmd->clothObject->verts;
@@ -824,7 +823,7 @@ static bool cloth_from_object(
static void cloth_from_mesh(ClothModifierData *clmd, const Object *ob, Mesh *mesh)
{
- const MLoop *mloop = mesh->mloop;
+ const MLoop *mloop = BKE_mesh_loops(mesh);
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh);
const unsigned int mvert_num = mesh->totvert;
const unsigned int looptri_num = mesh->runtime.looptris.len;
@@ -859,7 +858,7 @@ static void cloth_from_mesh(ClothModifierData *clmd, const Object *ob, Mesh *mes
}
BKE_mesh_runtime_verttri_from_looptri(clmd->clothObject->tri, mloop, looptri, looptri_num);
- clmd->clothObject->edges = mesh->medge;
+ clmd->clothObject->edges = BKE_mesh_edges(mesh);
/* Free the springs since they can't be correct if the vertices
* changed.
@@ -1150,7 +1149,7 @@ static void cloth_update_springs(ClothModifierData *clmd)
static void cloth_update_verts(Object *ob, ClothModifierData *clmd, Mesh *mesh)
{
unsigned int i = 0;
- MVert *mvert = mesh->mvert;
+ const MVert *mvert = BKE_mesh_verts(mesh);
ClothVertex *verts = clmd->clothObject->verts;
/* vertex count is already ensured to match */
@@ -1165,7 +1164,7 @@ static Mesh *cloth_make_rest_mesh(ClothModifierData *clmd, Mesh *mesh)
{
Mesh *new_mesh = BKE_mesh_copy_for_eval(mesh, false);
ClothVertex *verts = clmd->clothObject->verts;
- MVert *mvert = new_mesh->mvert;
+ MVert *mvert = BKE_mesh_verts_for_write(mesh);
/* vertex count is already ensured to match */
for (unsigned i = 0; i < mesh->totvert; i++, verts++) {
@@ -1459,9 +1458,9 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh)
unsigned int numedges = (unsigned int)mesh->totedge;
unsigned int numpolys = (unsigned int)mesh->totpoly;
float shrink_factor;
- const MEdge *medge = mesh->medge;
- const MPoly *mpoly = mesh->mpoly;
- const MLoop *mloop = mesh->mloop;
+ const MEdge *medge = BKE_mesh_edges(mesh);
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
+ const MLoop *mloop = BKE_mesh_loops(mesh);
int index2 = 0; /* our second vertex index */
LinkNodePair *edgelist = NULL;
EdgeSet *edgeset = NULL;
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index b71bcef229a..98b1e3d0039 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -7,6 +7,8 @@
/* Allow using deprecated functionality for .blend file I/O. */
#define DNA_DEPRECATED_ALLOW
+#include "CLG_log.h"
+
#include <string.h>
#include "BLI_blenlib.h"
@@ -46,6 +48,8 @@
#include "BLO_read_write.h"
+static CLG_LogRef LOG = {"bke.collection"};
+
/* -------------------------------------------------------------------- */
/** \name Prototypes
* \{ */
@@ -143,6 +147,9 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
{
Collection *collection = (Collection *)id;
+ BKE_LIB_FOREACHID_PROCESS_ID(
+ data, collection->owner_id, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF);
+
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, cob->ob, IDWALK_CB_USER);
}
@@ -162,24 +169,20 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
-static ID *collection_owner_get(Main *bmain, ID *id)
+static ID **collection_owner_pointer_get(ID *id)
{
if ((id->flag & LIB_EMBEDDED_DATA) == 0) {
- return id;
+ return NULL;
}
BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0);
Collection *master_collection = (Collection *)id;
BLI_assert((master_collection->flag & COLLECTION_IS_MASTER) != 0);
+ BLI_assert(master_collection->owner_id != NULL);
+ BLI_assert(GS(master_collection->owner_id->name) == ID_SCE);
+ BLI_assert(((Scene *)master_collection->owner_id)->master_collection == master_collection);
- LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->master_collection == master_collection) {
- return &scene->id;
- }
- }
-
- BLI_assert_msg(0, "Embedded collection with no owner. Critical Main inconsistency.");
- return NULL;
+ return &master_collection->owner_id;
}
void BKE_collection_blend_write_nolib(BlendWriter *writer, Collection *collection)
@@ -228,8 +231,35 @@ void BKE_collection_compat_blend_read_data(BlendDataReader *reader, SceneCollect
}
#endif
-void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collection)
-{
+void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collection, ID *owner_id)
+{
+ /* Special case for this pointer, do not rely on regular `lib_link` process here. Avoids needs
+ * for do_versioning, and ensures coherence of data in any case.
+ *
+ * NOTE: Old versions are very often 'broken' here, just fix it silently in these cases.
+ */
+ if (BLO_read_fileversion_get(reader) > 300) {
+ BLI_assert((collection->id.flag & LIB_EMBEDDED_DATA) != 0 || owner_id == NULL);
+ }
+ BLI_assert(owner_id == NULL || owner_id->lib == collection->id.lib);
+ if (owner_id != NULL && (collection->id.flag & LIB_EMBEDDED_DATA) == 0) {
+ /* This is unfortunate, but currently a lot of existing files (including startup ones) have
+ * missing `LIB_EMBEDDED_DATA` flag.
+ *
+ * NOTE: Using do_version is not a solution here, since this code will be called before any
+ * do_version takes place. Keeping it here also ensures future (or unknown existing) similar
+ * bugs won't go easily unnoticed. */
+ if (BLO_read_fileversion_get(reader) > 300) {
+ CLOG_WARN(&LOG,
+ "Fixing root node tree '%s' owned by '%s' missing EMBEDDED tag, please consider "
+ "re-saving your (startup) file",
+ collection->id.name,
+ owner_id->name);
+ }
+ collection->id.flag |= LIB_EMBEDDED_DATA;
+ }
+ collection->owner_id = owner_id;
+
BLO_read_list(reader, &collection->gobject);
BLO_read_list(reader, &collection->children);
@@ -260,7 +290,7 @@ void BKE_collection_blend_read_data(BlendDataReader *reader, Collection *collect
static void collection_blend_read_data(BlendDataReader *reader, ID *id)
{
Collection *collection = (Collection *)id;
- BKE_collection_blend_read_data(reader, collection);
+ BKE_collection_blend_read_data(reader, collection, NULL);
}
static void lib_link_collection_data(BlendLibReader *reader, Library *lib, Collection *collection)
@@ -370,7 +400,7 @@ IDTypeInfo IDType_ID_GR = {
.foreach_id = collection_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = collection_owner_get,
+ .owner_pointer_get = collection_owner_pointer_get,
.blend_write = collection_blend_write,
.blend_read_data = collection_blend_read_data,
@@ -461,8 +491,8 @@ void BKE_collection_add_from_collection(Main *bmain,
is_instantiated = true;
}
else if (!is_instantiated && collection_find_child(collection, collection_dst)) {
- /* If given collection_dst is already instantiated in scene, even if its 'model' src one is
- * not, do not add it to master scene collection. */
+ /* If given collection_dst is already instantiated in scene, even if its 'model'
+ * collection_src one is not, do not add it to master scene collection. */
is_instantiated = true;
}
}
@@ -710,10 +740,11 @@ void BKE_collection_new_name_get(Collection *collection_parent, char *rname)
char *name;
if (!collection_parent) {
- name = BLI_strdup("Collection");
+ name = BLI_strdup(DATA_("Collection"));
}
else if (collection_parent->flag & COLLECTION_IS_MASTER) {
- name = BLI_sprintfN("Collection %d", BLI_listbase_count(&collection_parent->children) + 1);
+ name = BLI_sprintfN(DATA_("Collection %d"),
+ BLI_listbase_count(&collection_parent->children) + 1);
}
else {
const int number = BLI_listbase_count(&collection_parent->children) + 1;
@@ -828,13 +859,15 @@ void BKE_collection_object_cache_free(Collection *collection)
collection_object_cache_free(collection);
}
-Base *BKE_collection_or_layer_objects(const ViewLayer *view_layer, Collection *collection)
+Base *BKE_collection_or_layer_objects(const Scene *scene,
+ ViewLayer *view_layer,
+ Collection *collection)
{
if (collection) {
return BKE_collection_object_cache_get(collection).first;
}
-
- return FIRSTBASE(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ return BKE_view_layer_object_bases_get(view_layer)->first;
}
/** \} */
@@ -843,14 +876,18 @@ Base *BKE_collection_or_layer_objects(const ViewLayer *view_layer, Collection *c
/** \name Scene Master Collection
* \{ */
-Collection *BKE_collection_master_add()
+Collection *BKE_collection_master_add(Scene *scene)
{
+ BLI_assert(scene != NULL && scene->master_collection == NULL);
+
/* Not an actual datablock, but owned by scene. */
Collection *master_collection = BKE_libblock_alloc(
NULL, ID_GR, BKE_SCENE_COLLECTION_NAME, LIB_ID_CREATE_NO_MAIN);
master_collection->id.flag |= LIB_EMBEDDED_DATA;
+ master_collection->owner_id = &scene->id;
master_collection->flag |= COLLECTION_IS_MASTER;
master_collection->color_tag = COLLECTION_COLOR_NONE;
+
return master_collection;
}
@@ -1714,7 +1751,10 @@ Collection *BKE_collection_from_index(Scene *scene, const int index)
return collection_from_index_recursive(master_collection, index, &index_current);
}
-static bool collection_objects_select(ViewLayer *view_layer, Collection *collection, bool deselect)
+static bool collection_objects_select(const Scene *scene,
+ ViewLayer *view_layer,
+ Collection *collection,
+ bool deselect)
{
bool changed = false;
@@ -1722,6 +1762,7 @@ static bool collection_objects_select(ViewLayer *view_layer, Collection *collect
return false;
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
@@ -1742,7 +1783,7 @@ static bool collection_objects_select(ViewLayer *view_layer, Collection *collect
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
- if (collection_objects_select(view_layer, collection, deselect)) {
+ if (collection_objects_select(scene, view_layer, collection, deselect)) {
changed = true;
}
}
@@ -1750,16 +1791,19 @@ static bool collection_objects_select(ViewLayer *view_layer, Collection *collect
return changed;
}
-bool BKE_collection_objects_select(ViewLayer *view_layer, Collection *collection, bool deselect)
+bool BKE_collection_objects_select(const Scene *scene,
+ ViewLayer *view_layer,
+ Collection *collection,
+ bool deselect)
{
LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(view_layer,
collection);
if (layer_collection != NULL) {
- return BKE_layer_collection_objects_select(view_layer, layer_collection, deselect);
+ return BKE_layer_collection_objects_select(scene, view_layer, layer_collection, deselect);
}
- return collection_objects_select(view_layer, collection, deselect);
+ return collection_objects_select(scene, view_layer, collection, deselect);
}
/** \} */
diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c
index ae99f0d17df..0bacd657981 100644
--- a/source/blender/blenkernel/intern/collision.c
+++ b/source/blender/blenkernel/intern/collision.c
@@ -1248,8 +1248,9 @@ ListBase *BKE_collision_relations_create(Depsgraph *depsgraph,
Collection *collection,
unsigned int modifier_type)
{
+ const Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
- Base *base = BKE_collection_or_layer_objects(view_layer, collection);
+ Base *base = BKE_collection_or_layer_objects(scene, view_layer, collection);
const bool for_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int base_flag = (for_render) ? BASE_ENABLED_RENDER : BASE_ENABLED_VIEWPORT;
diff --git a/source/blender/blenkernel/intern/colorband.c b/source/blender/blenkernel/intern/colorband.c
index b2f817b7821..5145f1cbbc0 100644
--- a/source/blender/blenkernel/intern/colorband.c
+++ b/source/blender/blenkernel/intern/colorband.c
@@ -144,7 +144,7 @@ static float color_sample_remove_cost(const struct ColorResampleElem *c)
return area;
}
-/* TODO(campbell): create BLI_math_filter? */
+/* TODO(@campbellbarton): create `BLI_math_filter` ? */
static float filter_gauss(float x)
{
const float gaussfac = 1.6f;
diff --git a/source/blender/blenkernel/intern/compute_contexts.cc b/source/blender/blenkernel/intern/compute_contexts.cc
new file mode 100644
index 00000000000..026706d363e
--- /dev/null
+++ b/source/blender/blenkernel/intern/compute_contexts.cc
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_compute_contexts.hh"
+
+namespace blender::bke {
+
+ModifierComputeContext::ModifierComputeContext(const ComputeContext *parent,
+ std::string modifier_name)
+ : ComputeContext(s_static_type, parent), modifier_name_(std::move(modifier_name))
+{
+ hash_.mix_in(s_static_type, strlen(s_static_type));
+ hash_.mix_in(modifier_name_.data(), modifier_name_.size());
+}
+
+void ModifierComputeContext::print_current_in_line(std::ostream &stream) const
+{
+ stream << "Modifier: " << modifier_name_;
+}
+
+NodeGroupComputeContext::NodeGroupComputeContext(const ComputeContext *parent,
+ std::string node_name)
+ : ComputeContext(s_static_type, parent), node_name_(std::move(node_name))
+{
+ hash_.mix_in(s_static_type, strlen(s_static_type));
+ hash_.mix_in(node_name_.data(), node_name_.size());
+}
+
+StringRefNull NodeGroupComputeContext::node_name() const
+{
+ return node_name_;
+}
+
+void NodeGroupComputeContext::print_current_in_line(std::ostream &stream) const
+{
+ stream << "Node: " << node_name_;
+}
+
+} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 8400d610a32..cd381e15635 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -528,9 +528,27 @@ static void contarget_get_mesh_mat(Object *ob, const char *substring, float mat[
float vec[3] = {0.0f, 0.0f, 0.0f};
float normal[3] = {0.0f, 0.0f, 0.0f};
float weightsum = 0.0f;
- if (me_eval) {
+ if (em) {
+ if (CustomData_has_layer(&em->bm->vdata, CD_MDEFORMVERT)) {
+ BMVert *v;
+ BMIter iter;
+
+ BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
+ MDeformVert *dv = CustomData_bmesh_get(&em->bm->vdata, v->head.data, CD_MDEFORMVERT);
+ MDeformWeight *dw = BKE_defvert_find_index(dv, defgroup);
+
+ if (dw && dw->weight > 0.0f) {
+ madd_v3_v3fl(vec, v->co, dw->weight);
+ madd_v3_v3fl(normal, v->no, dw->weight);
+ weightsum += dw->weight;
+ }
+ }
+ }
+ }
+ else if (me_eval) {
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval);
const MDeformVert *dvert = CustomData_get_layer(&me_eval->vdata, CD_MDEFORMVERT);
+ const MVert *verts = BKE_mesh_verts(me_eval);
int numVerts = me_eval->totvert;
/* check that dvert is a valid pointers (just in case) */
@@ -539,7 +557,7 @@ static void contarget_get_mesh_mat(Object *ob, const char *substring, float mat[
/* get the average of all verts with that are in the vertex-group */
for (int i = 0; i < numVerts; i++) {
const MDeformVert *dv = &dvert[i];
- const MVert *mv = &me_eval->mvert[i];
+ const MVert *mv = &verts[i];
const MDeformWeight *dw = BKE_defvert_find_index(dv, defgroup);
if (dw && dw->weight > 0.0f) {
@@ -550,23 +568,6 @@ static void contarget_get_mesh_mat(Object *ob, const char *substring, float mat[
}
}
}
- else if (em) {
- if (CustomData_has_layer(&em->bm->vdata, CD_MDEFORMVERT)) {
- BMVert *v;
- BMIter iter;
-
- BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
- MDeformVert *dv = CustomData_bmesh_get(&em->bm->vdata, v->head.data, CD_MDEFORMVERT);
- MDeformWeight *dw = BKE_defvert_find_index(dv, defgroup);
-
- if (dw && dw->weight > 0.0f) {
- madd_v3_v3fl(vec, v->co, dw->weight);
- madd_v3_v3fl(normal, v->no, dw->weight);
- weightsum += dw->weight;
- }
- }
- }
- }
else {
/* No valid edit or evaluated mesh, just abort. */
return;
@@ -1111,7 +1112,7 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
static bConstraintTypeInfo CTI_CHILDOF = {
CONSTRAINT_TYPE_CHILDOF, /* type */
sizeof(bChildOfConstraint), /* size */
- "Child Of", /* name */
+ N_("Child Of"), /* name */
"bChildOfConstraint", /* struct name */
NULL, /* free data */
childof_id_looper, /* id looper */
@@ -1282,9 +1283,8 @@ static void trackto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
cob->matrix[2][1] = 0;
cob->matrix[2][2] = size[2];
- /* targetmat[2] instead of ownermat[2] is passed to vectomat
- * for backwards compatibility it seems... (Aligorith)
- */
+ /* NOTE(@joshualung): `targetmat[2]` instead of `ownermat[2]` is passed to #vectomat
+ * for backwards compatibility it seems. */
sub_v3_v3v3(vec, cob->matrix[3], ct->matrix[3]);
vectomat(
vec, ct->matrix[2], (short)data->reserved1, (short)data->reserved2, data->flags, totmat);
@@ -1296,7 +1296,7 @@ static void trackto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
static bConstraintTypeInfo CTI_TRACKTO = {
CONSTRAINT_TYPE_TRACKTO, /* type */
sizeof(bTrackToConstraint), /* size */
- "Track To", /* name */
+ N_("Track To"), /* name */
"bTrackToConstraint", /* struct name */
NULL, /* free data */
trackto_id_looper, /* id looper */
@@ -1402,7 +1402,7 @@ static void kinematic_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
static bConstraintTypeInfo CTI_KINEMATIC = {
CONSTRAINT_TYPE_KINEMATIC, /* type */
sizeof(bKinematicConstraint), /* size */
- "IK", /* name */
+ N_("IK"), /* name */
"bKinematicConstraint", /* struct name */
NULL, /* free data */
kinematic_id_looper, /* id looper */
@@ -1559,7 +1559,7 @@ static void followpath_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *
/* un-apply scaling caused by path */
if ((data->followflag & FOLLOWPATH_RADIUS) == 0) {
- /* XXX(campbell): Assume that scale correction means that radius
+ /* XXX(@campbellbarton): Assume that scale correction means that radius
* will have some scale error in it. */
float obsize[3];
@@ -1580,7 +1580,7 @@ static void followpath_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *
static bConstraintTypeInfo CTI_FOLLOWPATH = {
CONSTRAINT_TYPE_FOLLOWPATH, /* type */
sizeof(bFollowPathConstraint), /* size */
- "Follow Path", /* name */
+ N_("Follow Path"), /* name */
"bFollowPathConstraint", /* struct name */
NULL, /* free data */
followpath_id_looper, /* id looper */
@@ -1633,7 +1633,7 @@ static void loclimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN
static bConstraintTypeInfo CTI_LOCLIMIT = {
CONSTRAINT_TYPE_LOCLIMIT, /* type */
sizeof(bLocLimitConstraint), /* size */
- "Limit Location", /* name */
+ N_("Limit Location"), /* name */
"bLocLimitConstraint", /* struct name */
NULL, /* free data */
NULL, /* id looper */
@@ -1714,7 +1714,7 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN
static bConstraintTypeInfo CTI_ROTLIMIT = {
CONSTRAINT_TYPE_ROTLIMIT, /* type */
sizeof(bRotLimitConstraint), /* size */
- "Limit Rotation", /* name */
+ N_("Limit Rotation"), /* name */
"bRotLimitConstraint", /* struct name */
NULL, /* free data */
NULL, /* id looper */
@@ -1781,7 +1781,7 @@ static void sizelimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *U
static bConstraintTypeInfo CTI_SIZELIMIT = {
CONSTRAINT_TYPE_SIZELIMIT, /* type */
sizeof(bSizeLimitConstraint), /* size */
- "Limit Scale", /* name */
+ N_("Limit Scale"), /* name */
"bSizeLimitConstraint", /* struct name */
NULL, /* free data */
NULL, /* id looper */
@@ -1878,7 +1878,7 @@ static void loclike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
static bConstraintTypeInfo CTI_LOCLIKE = {
CONSTRAINT_TYPE_LOCLIKE, /* type */
sizeof(bLocateLikeConstraint), /* size */
- "Copy Location", /* name */
+ N_("Copy Location"), /* name */
"bLocateLikeConstraint", /* struct name */
NULL, /* free data */
loclike_id_looper, /* id looper */
@@ -2055,7 +2055,7 @@ static void rotlike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
static bConstraintTypeInfo CTI_ROTLIKE = {
CONSTRAINT_TYPE_ROTLIKE, /* type */
sizeof(bRotateLikeConstraint), /* size */
- "Copy Rotation", /* name */
+ N_("Copy Rotation"), /* name */
"bRotateLikeConstraint", /* struct name */
NULL, /* free data */
rotlike_id_looper, /* id looper */
@@ -2185,7 +2185,7 @@ static void sizelike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *ta
static bConstraintTypeInfo CTI_SIZELIKE = {
CONSTRAINT_TYPE_SIZELIKE, /* type */
sizeof(bSizeLikeConstraint), /* size */
- "Copy Scale", /* name */
+ N_("Copy Scale"), /* name */
"bSizeLikeConstraint", /* struct name */
NULL, /* free data */
sizelike_id_looper, /* id looper */
@@ -2291,7 +2291,7 @@ static void translike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
static bConstraintTypeInfo CTI_TRANSLIKE = {
CONSTRAINT_TYPE_TRANSLIKE, /* type */
sizeof(bTransLikeConstraint), /* size */
- "Copy Transforms", /* name */
+ N_("Copy Transforms"), /* name */
"bTransLikeConstraint", /* struct name */
NULL, /* free data */
translike_id_looper, /* id looper */
@@ -2360,7 +2360,7 @@ static void samevolume_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *
static bConstraintTypeInfo CTI_SAMEVOL = {
CONSTRAINT_TYPE_SAMEVOL, /* type */
sizeof(bSameVolumeConstraint), /* size */
- "Maintain Volume", /* name */
+ N_("Maintain Volume"), /* name */
"bSameVolumeConstraint", /* struct name */
NULL, /* free data */
NULL, /* id looper */
@@ -2492,7 +2492,7 @@ static void pycon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targe
static bConstraintTypeInfo CTI_PYTHON = {
CONSTRAINT_TYPE_PYTHON, /* type */
sizeof(bPythonConstraint), /* size */
- "Script", /* name */
+ N_("Script"), /* name */
"bPythonConstraint", /* struct name */
pycon_free, /* free data */
pycon_id_looper, /* id looper */
@@ -2739,7 +2739,7 @@ static void armdef_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ
static bConstraintTypeInfo CTI_ARMATURE = {
CONSTRAINT_TYPE_ARMATURE, /* type */
sizeof(bArmatureConstraint), /* size */
- "Armature", /* name */
+ N_("Armature"), /* name */
"bArmatureConstraint", /* struct name */
armdef_free, /* free data */
armdef_id_looper, /* id looper */
@@ -2955,7 +2955,7 @@ static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ
static bConstraintTypeInfo CTI_ACTION = {
CONSTRAINT_TYPE_ACTION, /* type */
sizeof(bActionConstraint), /* size */
- "Action", /* name */
+ N_("Action"), /* name */
"bActionConstraint", /* struct name */
NULL, /* free data */
actcon_id_looper, /* id looper */
@@ -3271,7 +3271,7 @@ static void locktrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
static bConstraintTypeInfo CTI_LOCKTRACK = {
CONSTRAINT_TYPE_LOCKTRACK, /* type */
sizeof(bLockTrackConstraint), /* size */
- "Locked Track", /* name */
+ N_("Locked Track"), /* name */
"bLockTrackConstraint", /* struct name */
NULL, /* free data */
locktrack_id_looper, /* id looper */
@@ -3414,7 +3414,7 @@ static void distlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
static bConstraintTypeInfo CTI_DISTLIMIT = {
CONSTRAINT_TYPE_DISTLIMIT, /* type */
sizeof(bDistLimitConstraint), /* size */
- "Limit Distance", /* name */
+ N_("Limit Distance"), /* name */
"bDistLimitConstraint", /* struct name */
NULL, /* free data */
distlimit_id_looper, /* id looper */
@@ -3622,7 +3622,7 @@ static void stretchto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
static bConstraintTypeInfo CTI_STRETCHTO = {
CONSTRAINT_TYPE_STRETCHTO, /* type */
sizeof(bStretchToConstraint), /* size */
- "Stretch To", /* name */
+ N_("Stretch To"), /* name */
"bStretchToConstraint", /* struct name */
NULL, /* free data */
stretchto_id_looper, /* id looper */
@@ -3753,7 +3753,7 @@ static void minmax_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targ
static bConstraintTypeInfo CTI_MINMAX = {
CONSTRAINT_TYPE_MINMAX, /* type */
sizeof(bMinMaxConstraint), /* size */
- "Floor", /* name */
+ N_("Floor"), /* name */
"bMinMaxConstraint", /* struct name */
NULL, /* free data */
minmax_id_looper, /* id looper */
@@ -3939,7 +3939,7 @@ static void clampto_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
static bConstraintTypeInfo CTI_CLAMPTO = {
CONSTRAINT_TYPE_CLAMPTO, /* type */
sizeof(bClampToConstraint), /* size */
- "Clamp To", /* name */
+ N_("Clamp To"), /* name */
"bClampToConstraint", /* struct name */
NULL, /* free data */
clampto_id_looper, /* id looper */
@@ -4148,7 +4148,7 @@ static void transform_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
static bConstraintTypeInfo CTI_TRANSFORM = {
CONSTRAINT_TYPE_TRANSFORM, /* type */
sizeof(bTransformConstraint), /* size */
- "Transformation", /* name */
+ N_("Transformation"), /* name */
"bTransformConstraint", /* struct name */
NULL, /* free data */
transform_id_looper, /* id looper */
@@ -4379,7 +4379,7 @@ static void shrinkwrap_evaluate(bConstraint *UNUSED(con), bConstraintOb *cob, Li
static bConstraintTypeInfo CTI_SHRINKWRAP = {
CONSTRAINT_TYPE_SHRINKWRAP, /* type */
sizeof(bShrinkwrapConstraint), /* size */
- "Shrinkwrap", /* name */
+ N_("Shrinkwrap"), /* name */
"bShrinkwrapConstraint", /* struct name */
NULL, /* free data */
shrinkwrap_id_looper, /* id looper */
@@ -4544,7 +4544,7 @@ static void damptrack_do_transform(float matrix[4][4], const float tarvec_in[3],
static bConstraintTypeInfo CTI_DAMPTRACK = {
CONSTRAINT_TYPE_DAMPTRACK, /* type */
sizeof(bDampTrackConstraint), /* size */
- "Damped Track", /* name */
+ N_("Damped Track"), /* name */
"bDampTrackConstraint", /* struct name */
NULL, /* free data */
damptrack_id_looper, /* id looper */
@@ -4639,7 +4639,7 @@ static void splineik_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
static bConstraintTypeInfo CTI_SPLINEIK = {
CONSTRAINT_TYPE_SPLINEIK, /* type */
sizeof(bSplineIKConstraint), /* size */
- "Spline IK", /* name */
+ N_("Spline IK"), /* name */
"bSplineIKConstraint", /* struct name */
splineik_free, /* free data */
splineik_id_looper, /* id looper */
@@ -4763,7 +4763,7 @@ static void pivotcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *ta
static bConstraintTypeInfo CTI_PIVOT = {
CONSTRAINT_TYPE_PIVOT, /* type */
sizeof(bPivotConstraint), /* size */
- "Pivot", /* name */
+ N_("Pivot"), /* name */
"bPivotConstraint", /* struct name */
NULL, /* free data */
pivotcon_id_looper, /* id looper */
@@ -5184,7 +5184,7 @@ static void followtrack_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
static bConstraintTypeInfo CTI_FOLLOWTRACK = {
CONSTRAINT_TYPE_FOLLOWTRACK, /* type */
sizeof(bFollowTrackConstraint), /* size */
- "Follow Track", /* name */
+ N_("Follow Track"), /* name */
"bFollowTrackConstraint", /* struct name */
NULL, /* free data */
followtrack_id_looper, /* id looper */
@@ -5242,7 +5242,7 @@ static void camerasolver_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
static bConstraintTypeInfo CTI_CAMERASOLVER = {
CONSTRAINT_TYPE_CAMERASOLVER, /* type */
sizeof(bCameraSolverConstraint), /* size */
- "Camera Solver", /* name */
+ N_("Camera Solver"), /* name */
"bCameraSolverConstraint", /* struct name */
NULL, /* free data */
camerasolver_id_looper, /* id looper */
@@ -5330,7 +5330,7 @@ static void objectsolver_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
static bConstraintTypeInfo CTI_OBJECTSOLVER = {
CONSTRAINT_TYPE_OBJECTSOLVER, /* type */
sizeof(bObjectSolverConstraint), /* size */
- "Object Solver", /* name */
+ N_("Object Solver"), /* name */
"bObjectSolverConstraint", /* struct name */
NULL, /* free data */
objectsolver_id_looper, /* id looper */
@@ -5427,7 +5427,7 @@ static void transformcache_new_data(void *cdata)
static bConstraintTypeInfo CTI_TRANSFORM_CACHE = {
CONSTRAINT_TYPE_TRANSFORM_CACHE, /* type */
sizeof(bTransformCacheConstraint), /* size */
- "Transform Cache", /* name */
+ N_("Transform Cache"), /* name */
"bTransformCacheConstraint", /* struct name */
transformcache_free, /* free data */
transformcache_id_looper, /* id looper */
@@ -5844,9 +5844,12 @@ static bConstraint *add_new_constraint(Object *ob,
return con;
}
-bool BKE_constraint_target_uses_bbone(struct bConstraint *con,
- struct bConstraintTarget *UNUSED(ct))
+bool BKE_constraint_target_uses_bbone(struct bConstraint *con, struct bConstraintTarget *ct)
{
+ if (ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE) {
+ return false;
+ }
+
return (con->flag & CONSTRAINT_BBONE_SHAPE) || (con->type == CONSTRAINT_TYPE_ARMATURE);
}
@@ -5928,12 +5931,12 @@ static void constraint_copy_data_ex(bConstraint *dst,
cti->copy_data(dst, src);
}
- /* Fix usercounts for all referenced data that need it. */
+ /* Fix user-counts for all referenced data that need it. */
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
con_invoke_id_looper(cti, dst, con_fix_copied_refs_cb, NULL);
}
- /* for proxies we don't want to make extern */
+ /* For proxies we don't want to make external. */
if (do_extern) {
/* go over used ID-links for this constraint to ensure that they are valid for proxies */
con_invoke_id_looper(cti, dst, con_extern_cb, NULL);
@@ -6259,6 +6262,9 @@ void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,
}
}
+ /* Initialize the custom space for use in calculating the matrices. */
+ BKE_constraint_custom_object_space_init(cob, con);
+
/* get targets - we only need the first one though (and there should only be one) */
cti->get_constraint_targets(con, &targets);
diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c
index 1f1a49ca030..ceb84d213c3 100644
--- a/source/blender/blenkernel/intern/context.c
+++ b/source/blender/blenkernel/intern/context.c
@@ -440,6 +440,7 @@ static int ctx_data_base_collection_get(const bContext *C, const char *member, L
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
+ BKE_view_layer_synced_ensure(scene, view_layer);
bool ok = false;
@@ -1362,8 +1363,9 @@ struct Base *CTX_data_active_base(const bContext *C)
if (ob == NULL) {
return NULL;
}
-
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
+ BKE_view_layer_synced_ensure(scene, view_layer);
return BKE_view_layer_base_find(view_layer, ob);
}
diff --git a/source/blender/blenkernel/intern/crazyspace.c b/source/blender/blenkernel/intern/crazyspace.cc
index 14e862c2377..190e2d3bb7e 100644
--- a/source/blender/blenkernel/intern/crazyspace.c
+++ b/source/blender/blenkernel/intern/crazyspace.cc
@@ -19,7 +19,10 @@
#include "BKE_DerivedMesh.h"
#include "BKE_crazyspace.h"
+#include "BKE_crazyspace.hh"
+#include "BKE_curves.hh"
#include "BKE_editmesh.h"
+#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
#include "BKE_mesh_wrapper.h"
@@ -70,9 +73,9 @@ static void set_crazy_vertex_quat(float r_quat[4],
static bool modifiers_disable_subsurf_temporary(struct Scene *scene, Object *ob)
{
bool disabled = false;
- int cageIndex = BKE_modifiers_get_cage_index(scene, ob, NULL, 1);
+ int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, true);
- ModifierData *md = ob->modifiers.first;
+ ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first);
for (int i = 0; md && i <= cageIndex; i++, md = md->next) {
if (md->type == eModifierType_Subsurf) {
md->mode ^= eModifierMode_DisableTemporary;
@@ -88,12 +91,12 @@ float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object
Scene *scene = DEG_get_input_scene(depsgraph);
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
- Mesh *mesh_eval = obedit_eval->data;
+ Mesh *mesh_eval = static_cast<Mesh *>(obedit_eval->data);
BMEditMesh *editmesh_eval = mesh_eval->edit_mesh;
/* disable subsurf temporal, get mapped cos, and enable it */
if (modifiers_disable_subsurf_temporary(scene_eval, obedit_eval)) {
- /* need to make new derivemesh */
+ /* Need to make new derived-mesh. */
makeDerivedMesh(depsgraph, scene_eval, obedit_eval, &CD_MASK_BAREMESH);
}
@@ -102,7 +105,8 @@ float (*BKE_crazyspace_get_mapped_editverts(struct Depsgraph *depsgraph, Object
depsgraph, scene, obedit, &CD_MASK_BAREMESH);
const int nverts = editmesh_eval->bm->totvert;
- float(*vertexcos)[3] = MEM_mallocN(sizeof(*vertexcos) * nverts, "vertexcos map");
+ float(*vertexcos)[3] = static_cast<float(*)[3]>(
+ MEM_mallocN(sizeof(*vertexcos) * nverts, "vertexcos map"));
mesh_get_mapped_verts_coords(mesh_eval_cage, vertexcos, nverts);
/* set back the flag, no new cage needs to be built, transform does it */
@@ -178,19 +182,22 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me,
float (*mappedcos)[3],
float (*quats)[4])
{
+ using namespace blender;
+ using namespace blender::bke;
BLI_bitmap *vert_tag = BLI_BITMAP_NEW(me->totvert, __func__);
/* first store two sets of tangent vectors in vertices, we derive it just from the face-edges */
- MVert *mvert = me->mvert;
- MPoly *mp = me->mpoly;
- MLoop *mloop = me->mloop;
+ const Span<MVert> verts = me->verts();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
- for (int i = 0; i < me->totpoly; i++, mp++) {
- MLoop *ml_next = &mloop[mp->loopstart];
- MLoop *ml_curr = &ml_next[mp->totloop - 1];
- MLoop *ml_prev = &ml_next[mp->totloop - 2];
+ for (int i = 0; i < me->totpoly; i++) {
+ const MPoly *poly = &polys[i];
+ const MLoop *ml_next = &loops[poly->loopstart];
+ const MLoop *ml_curr = &ml_next[poly->totloop - 1];
+ const MLoop *ml_prev = &ml_next[poly->totloop - 2];
- for (int j = 0; j < mp->totloop; j++) {
+ for (int j = 0; j < poly->totloop; j++) {
if (!BLI_BITMAP_TEST(vert_tag, ml_curr->v)) {
const float *co_prev, *co_curr, *co_next; /* orig */
const float *vd_prev, *vd_curr, *vd_next; /* deform */
@@ -206,9 +213,9 @@ void BKE_crazyspace_set_quats_mesh(Mesh *me,
co_next = origcos[ml_next->v];
}
else {
- co_prev = mvert[ml_prev->v].co;
- co_curr = mvert[ml_curr->v].co;
- co_next = mvert[ml_next->v].co;
+ co_prev = verts[ml_prev->v].co;
+ co_curr = verts[ml_curr->v].co;
+ co_next = verts[ml_next->v].co;
}
set_crazy_vertex_quat(
@@ -234,13 +241,13 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra
float (**deformcos)[3])
{
ModifierData *md;
- Mesh *me_input = ob->data;
- Mesh *me = NULL;
+ Mesh *me_input = static_cast<Mesh *>(ob->data);
+ Mesh *me = nullptr;
int i, a, modifiers_left_num = 0, verts_num = 0;
- int cageIndex = BKE_modifiers_get_cage_index(scene, ob, NULL, 1);
- float(*defmats)[3][3] = NULL, (*deformedVerts)[3] = NULL;
+ int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, true);
+ float(*defmats)[3][3] = nullptr, (*deformedVerts)[3] = nullptr;
VirtualModifierData virtualModifierData;
- ModifierEvalContext mectx = {depsgraph, ob, 0};
+ ModifierEvalContext mectx = {depsgraph, ob, ModifierApplyFlag(0)};
BKE_modifiers_clear_errors(ob);
@@ -250,9 +257,9 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra
* modifiers with on cage editing that are enabled and support computing
* deform matrices */
for (i = 0; md && i <= cageIndex; i++, md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
- if (!editbmesh_modifier_is_enabled(scene, ob, md, me != NULL)) {
+ if (!editbmesh_modifier_is_enabled(scene, ob, md, me != nullptr)) {
continue;
}
@@ -261,13 +268,14 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra
const int required_mode = eModifierMode_Realtime | eModifierMode_Editmode;
CustomData_MeshMasks cd_mask_extra = CD_MASK_BAREMESH;
CDMaskLink *datamasks = BKE_modifier_calc_data_masks(
- scene, ob, md, &cd_mask_extra, required_mode, NULL, NULL);
+ scene, md, &cd_mask_extra, required_mode, nullptr, nullptr);
cd_mask_extra = datamasks->mask;
- BLI_linklist_free((LinkNode *)datamasks, NULL);
+ BLI_linklist_free((LinkNode *)datamasks, nullptr);
- me = BKE_mesh_wrapper_from_editmesh_with_coords(em, &cd_mask_extra, NULL, me_input);
+ me = BKE_mesh_wrapper_from_editmesh_with_coords(em, &cd_mask_extra, nullptr, me_input);
deformedVerts = editbmesh_vert_coords_alloc(em, &verts_num);
- defmats = MEM_mallocN(sizeof(*defmats) * verts_num, "defmats");
+ defmats = static_cast<float(*)[3][3]>(
+ MEM_mallocN(sizeof(*defmats) * verts_num, "defmats"));
for (a = 0; a < verts_num; a++) {
unit_m3(defmats[a]);
@@ -281,14 +289,14 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra
}
for (; md && i <= cageIndex; md = md->next, i++) {
- if (editbmesh_modifier_is_enabled(scene, ob, md, me != NULL) &&
+ if (editbmesh_modifier_is_enabled(scene, ob, md, me != nullptr) &&
BKE_modifier_is_correctable_deformed(md)) {
modifiers_left_num++;
}
}
if (me) {
- BKE_id_free(NULL, me);
+ BKE_id_free(nullptr, me);
}
*deformmats = defmats;
@@ -309,8 +317,8 @@ static void crazyspace_init_object_for_eval(struct Depsgraph *depsgraph,
Object *object_crazy)
{
Object *object_eval = DEG_get_evaluated_object(depsgraph, object);
- *object_crazy = *object_eval;
- if (object_crazy->runtime.data_orig != NULL) {
+ *object_crazy = blender::dna::shallow_copy(*object_eval);
+ if (object_crazy->runtime.data_orig != nullptr) {
object_crazy->data = object_crazy->runtime.data_orig;
}
}
@@ -321,7 +329,8 @@ static void crazyspace_init_verts_and_matrices(const Mesh *mesh,
{
int verts_num;
*deformcos = BKE_mesh_vert_coords_alloc(mesh, &verts_num);
- *deformmats = MEM_callocN(sizeof(**deformmats) * verts_num, "defmats");
+ *deformmats = static_cast<float(*)[3][3]>(
+ MEM_callocN(sizeof(**deformmats) * verts_num, "defmats"));
for (int a = 0; a < verts_num; a++) {
unit_m3((*deformmats)[a]);
}
@@ -333,13 +342,13 @@ static bool crazyspace_modifier_supports_deform_matrices(ModifierData *md)
if (ELEM(md->type, eModifierType_Subsurf, eModifierType_Multires)) {
return true;
}
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
return (mti->type == eModifierTypeType_OnlyDeform);
}
static bool crazyspace_modifier_supports_deform(ModifierData *md)
{
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
return (mti->type == eModifierTypeType_OnlyDeform);
}
@@ -350,20 +359,20 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph,
float (**deformcos)[3])
{
ModifierData *md;
- Mesh *me_eval = NULL;
- float(*defmats)[3][3] = NULL, (*deformedVerts)[3] = NULL;
+ Mesh *me_eval = nullptr;
+ float(*defmats)[3][3] = nullptr, (*deformedVerts)[3] = nullptr;
int modifiers_left_num = 0;
VirtualModifierData virtualModifierData;
Object object_eval;
crazyspace_init_object_for_eval(depsgraph, object, &object_eval);
- MultiresModifierData *mmd = get_multires_modifier(scene, &object_eval, 0);
+ MultiresModifierData *mmd = get_multires_modifier(scene, &object_eval, false);
const bool is_sculpt_mode = (object->mode & OB_MODE_SCULPT) != 0;
- const bool has_multires = mmd != NULL && mmd->sculptlvl > 0;
- const ModifierEvalContext mectx = {depsgraph, &object_eval, 0};
+ const bool has_multires = mmd != nullptr && mmd->sculptlvl > 0;
+ const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)};
if (is_sculpt_mode && has_multires) {
- *deformmats = NULL;
- *deformcos = NULL;
+ *deformmats = nullptr;
+ *deformcos = nullptr;
return modifiers_left_num;
}
@@ -375,10 +384,10 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph,
}
if (crazyspace_modifier_supports_deform_matrices(md)) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
- if (defmats == NULL) {
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
+ if (defmats == nullptr) {
/* NOTE: Evaluated object is re-set to its original un-deformed state. */
- Mesh *me = object_eval.data;
+ Mesh *me = static_cast<Mesh *>(object_eval.data);
me_eval = BKE_mesh_copy_for_eval(me, true);
crazyspace_init_verts_and_matrices(me_eval, &defmats, &deformedVerts);
}
@@ -405,8 +414,8 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph,
}
}
- if (me_eval != NULL) {
- BKE_id_free(NULL, me_eval);
+ if (me_eval != nullptr) {
+ BKE_id_free(nullptr, me_eval);
}
*deformmats = defmats;
@@ -429,21 +438,21 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph,
* Need additional crazy-space correction. */
Mesh *mesh = (Mesh *)object->data;
- Mesh *mesh_eval = NULL;
+ Mesh *mesh_eval = nullptr;
- if (*deformcos == NULL) {
+ if (*deformcos == nullptr) {
crazyspace_init_verts_and_matrices(mesh, deformmats, deformcos);
}
float(*deformedVerts)[3] = *deformcos;
- float(*origVerts)[3] = MEM_dupallocN(deformedVerts);
+ float(*origVerts)[3] = static_cast<float(*)[3]>(MEM_dupallocN(deformedVerts));
float(*quats)[4];
int i, deformed = 0;
VirtualModifierData virtualModifierData;
Object object_eval;
crazyspace_init_object_for_eval(depsgraph, object, &object_eval);
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(&object_eval, &virtualModifierData);
- const ModifierEvalContext mectx = {depsgraph, &object_eval, 0};
+ const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)};
for (; md; md = md->next) {
if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) {
@@ -451,7 +460,7 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph,
}
if (crazyspace_modifier_supports_deform(md)) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
/* skip leading modifiers which have been already
* handled in sculpt_get_first_deform_matrices */
@@ -459,7 +468,7 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph,
continue;
}
- if (mesh_eval == NULL) {
+ if (mesh_eval == nullptr) {
mesh_eval = BKE_mesh_copy_for_eval(mesh, true);
}
@@ -468,7 +477,7 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph,
}
}
- quats = MEM_mallocN(mesh->totvert * sizeof(*quats), "crazy quats");
+ quats = static_cast<float(*)[4]>(MEM_mallocN(mesh->totvert * sizeof(*quats), "crazy quats"));
BKE_crazyspace_set_quats_mesh(mesh, origVerts, deformedVerts, quats);
@@ -483,17 +492,18 @@ void BKE_crazyspace_build_sculpt(struct Depsgraph *depsgraph,
MEM_freeN(origVerts);
MEM_freeN(quats);
- if (mesh_eval != NULL) {
- BKE_id_free(NULL, mesh_eval);
+ if (mesh_eval != nullptr) {
+ BKE_id_free(nullptr, mesh_eval);
}
}
- if (*deformmats == NULL) {
+ if (*deformmats == nullptr) {
int a, verts_num;
Mesh *mesh = (Mesh *)object->data;
*deformcos = BKE_mesh_vert_coords_alloc(mesh, &verts_num);
- *deformmats = MEM_callocN(sizeof(*(*deformmats)) * verts_num, "defmats");
+ *deformmats = static_cast<float(*)[3][3]>(
+ MEM_callocN(sizeof(*(*deformmats)) * verts_num, "defmats"));
for (a = 0; a < verts_num; a++) {
unit_m3((*deformmats)[a]);
@@ -510,8 +520,8 @@ void BKE_crazyspace_api_eval(Depsgraph *depsgraph,
Object *object,
struct ReportList *reports)
{
- if (object->runtime.crazyspace_deform_imats != NULL ||
- object->runtime.crazyspace_deform_cos != NULL) {
+ if (object->runtime.crazyspace_deform_imats != nullptr ||
+ object->runtime.crazyspace_deform_cos != nullptr) {
return;
}
@@ -582,3 +592,64 @@ void BKE_crazyspace_api_eval_clear(Object *object)
}
/** \} */
+
+namespace blender::bke::crazyspace {
+
+GeometryDeformation get_evaluated_curves_deformation(const Depsgraph &depsgraph,
+ const Object &ob_orig)
+{
+ BLI_assert(ob_orig.type == OB_CURVES);
+ const Curves &curves_id_orig = *static_cast<const Curves *>(ob_orig.data);
+ const CurvesGeometry &curves_orig = CurvesGeometry::wrap(curves_id_orig.geometry);
+ const int points_num = curves_orig.points_num();
+
+ GeometryDeformation deformation;
+ /* Use the undeformed positions by default. */
+ deformation.positions = curves_orig.positions();
+
+ const Object *ob_eval = DEG_get_evaluated_object(&depsgraph, const_cast<Object *>(&ob_orig));
+ if (ob_eval == nullptr) {
+ return deformation;
+ }
+ const GeometrySet *geometry_eval = ob_eval->runtime.geometry_set_eval;
+ if (geometry_eval == nullptr) {
+ return deformation;
+ }
+
+ /* If available, use deformation information generated during evaluation. */
+ const GeometryComponentEditData *edit_component_eval =
+ geometry_eval->get_component_for_read<GeometryComponentEditData>();
+ bool uses_extra_positions = false;
+ if (edit_component_eval != nullptr) {
+ const CurvesEditHints *edit_hints = edit_component_eval->curves_edit_hints_.get();
+ if (edit_hints != nullptr && &edit_hints->curves_id_orig == &curves_id_orig) {
+ if (edit_hints->positions.has_value()) {
+ BLI_assert(edit_hints->positions->size() == points_num);
+ deformation.positions = *edit_hints->positions;
+ uses_extra_positions = true;
+ }
+ if (edit_hints->deform_mats.has_value()) {
+ BLI_assert(edit_hints->deform_mats->size() == points_num);
+ deformation.deform_mats = *edit_hints->deform_mats;
+ }
+ }
+ }
+
+ /* Use the positions of the evaluated curves directly, if the number of points matches. */
+ if (!uses_extra_positions) {
+ const CurveComponent *curves_component_eval =
+ geometry_eval->get_component_for_read<CurveComponent>();
+ if (curves_component_eval != nullptr) {
+ const Curves *curves_id_eval = curves_component_eval->get_for_read();
+ if (curves_id_eval != nullptr) {
+ const CurvesGeometry &curves_eval = CurvesGeometry::wrap(curves_id_eval->geometry);
+ if (curves_eval.points_num() == points_num) {
+ deformation.positions = curves_eval.positions();
+ }
+ }
+ }
+ }
+ return deformation;
+}
+
+} // namespace blender::bke::crazyspace
diff --git a/source/blender/blenkernel/intern/cryptomatte.cc b/source/blender/blenkernel/intern/cryptomatte.cc
index 102bda0f2b6..72204f6624e 100644
--- a/source/blender/blenkernel/intern/cryptomatte.cc
+++ b/source/blender/blenkernel/intern/cryptomatte.cc
@@ -41,7 +41,9 @@ struct CryptomatteSession {
CryptomatteSession() = default;
CryptomatteSession(const Main *bmain);
CryptomatteSession(StampData *stamp_data);
+ CryptomatteSession(const ViewLayer *view_layer);
CryptomatteSession(const Scene *scene);
+ void init(const ViewLayer *view_layer);
blender::bke::cryptomatte::CryptomatteLayer &add_layer(std::string layer_name);
std::optional<std::string> operator[](float encoded_hash) const;
@@ -54,13 +56,15 @@ struct CryptomatteSession {
CryptomatteSession::CryptomatteSession(const Main *bmain)
{
if (!BLI_listbase_is_empty(&bmain->objects)) {
- blender::bke::cryptomatte::CryptomatteLayer &objects = add_layer("CryptoObject");
+ blender::bke::cryptomatte::CryptomatteLayer &objects = add_layer(
+ RE_PASSNAME_CRYPTOMATTE_OBJECT);
LISTBASE_FOREACH (ID *, id, &bmain->objects) {
objects.add_ID(*id);
}
}
if (!BLI_listbase_is_empty(&bmain->materials)) {
- blender::bke::cryptomatte::CryptomatteLayer &materials = add_layer("CryptoMaterial");
+ blender::bke::cryptomatte::CryptomatteLayer &materials = add_layer(
+ RE_PASSNAME_CRYPTOMATTE_MATERIAL);
LISTBASE_FOREACH (ID *, id, &bmain->materials) {
materials.add_ID(*id);
}
@@ -83,24 +87,34 @@ CryptomatteSession::CryptomatteSession(StampData *stamp_data)
false);
}
+CryptomatteSession::CryptomatteSession(const ViewLayer *view_layer)
+{
+ init(view_layer);
+}
+
CryptomatteSession::CryptomatteSession(const Scene *scene)
{
- LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- eViewLayerCryptomatteFlags cryptoflags = static_cast<eViewLayerCryptomatteFlags>(
- view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL);
- if (cryptoflags == 0) {
- cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL);
- }
+ LISTBASE_FOREACH (const ViewLayer *, view_layer, &scene->view_layers) {
+ init(view_layer);
+ }
+}
- if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) {
- add_layer(blender::StringRefNull(view_layer->name) + ".CryptoObject");
- }
- if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) {
- add_layer(blender::StringRefNull(view_layer->name) + ".CryptoAsset");
- }
- if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) {
- add_layer(blender::StringRefNull(view_layer->name) + ".CryptoMaterial");
- }
+void CryptomatteSession::init(const ViewLayer *view_layer)
+{
+ eViewLayerCryptomatteFlags cryptoflags = static_cast<eViewLayerCryptomatteFlags>(
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ALL);
+ if (cryptoflags == 0) {
+ cryptoflags = static_cast<eViewLayerCryptomatteFlags>(VIEW_LAYER_CRYPTOMATTE_ALL);
+ }
+
+ if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_OBJECT) {
+ add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_OBJECT);
+ }
+ if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_ASSET) {
+ add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_ASSET);
+ }
+ if (cryptoflags & VIEW_LAYER_CRYPTOMATTE_MATERIAL) {
+ add_layer(blender::StringRefNull(view_layer->name) + "." + RE_PASSNAME_CRYPTOMATTE_MATERIAL);
}
}
@@ -142,6 +156,12 @@ struct CryptomatteSession *BKE_cryptomatte_init_from_scene(const struct Scene *s
return session;
}
+struct CryptomatteSession *BKE_cryptomatte_init_from_view_layer(const struct ViewLayer *view_layer)
+{
+ CryptomatteSession *session = new CryptomatteSession(view_layer);
+ return session;
+}
+
void BKE_cryptomatte_add_layer(struct CryptomatteSession *session, const char *layer_name)
{
session->add_layer(layer_name);
@@ -485,11 +505,6 @@ CryptomatteHash::CryptomatteHash(uint32_t hash) : hash(hash)
{
}
-CryptomatteHash::CryptomatteHash(const char *name, const int name_len)
-{
- hash = BLI_hash_mm3((const unsigned char *)name, name_len, 0);
-}
-
CryptomatteHash CryptomatteHash::from_hex_encoded(blender::StringRef hex_encoded)
{
CryptomatteHash result(0);
@@ -504,21 +519,6 @@ std::string CryptomatteHash::hex_encoded() const
return encoded.str();
}
-float CryptomatteHash::float_encoded() const
-{
- uint32_t mantissa = hash & ((1 << 23) - 1);
- uint32_t exponent = (hash >> 23) & ((1 << 8) - 1);
- exponent = MAX2(exponent, (uint32_t)1);
- exponent = MIN2(exponent, (uint32_t)254);
- exponent = exponent << 23;
- uint32_t sign = (hash >> 31);
- sign = sign << 31;
- uint32_t float_bits = sign | exponent | mantissa;
- float f;
- memcpy(&f, &float_bits, sizeof(uint32_t));
- return f;
-}
-
std::unique_ptr<CryptomatteLayer> CryptomatteLayer::read_from_manifest(
blender::StringRefNull manifest)
{
@@ -625,4 +625,9 @@ const blender::Vector<std::string> &BKE_cryptomatte_layer_names_get(
return session.layer_names;
}
+CryptomatteLayer *BKE_cryptomatte_layer_get(CryptomatteSession &session, StringRef layer_name)
+{
+ return session.layers.lookup_ptr(layer_name);
+}
+
} // namespace blender::bke::cryptomatte
diff --git a/source/blender/blenkernel/intern/cryptomatte_test.cc b/source/blender/blenkernel/intern/cryptomatte_test.cc
index 2f15242b4a4..bb09b276645 100644
--- a/source/blender/blenkernel/intern/cryptomatte_test.cc
+++ b/source/blender/blenkernel/intern/cryptomatte_test.cc
@@ -163,7 +163,7 @@ TEST(cryptomatte, session_from_stamp_data)
* best as possible. */
TEST(cryptomatte, parsing_malformed_manifests)
{
- /* Manifest from multilayer.exr in the cryptomatte git-repository. */
+ /* Manifest from `multilayer.exr` in the cryptomatte git-repository. */
test_cryptomatte_manifest(
R"({"/obj/instance1:instances:0":"0d54c6cc","/obj/instance1:instances:1":"293d9340","/obj/instance1:instances:110":"ccb9e1f2","/obj/instance1:instances:111":"f8dd3a48","/obj/instance1:instances:112":"a99e07a8","/obj/instance1:instances:113":"e75599a4","/obj/instance1:instances:114":"794200f3","/obj/instance1:instances:115":"2a3a1728","/obj/instance1:instances:116":"478544a1","/obj/instance1:instances:117":"b2bd969a","/obj/instance1:instances:10":"3a0c8681","/obj/instance1:instances:11":"01e5970d","/obj/box:polygons:1":"9d416418","/obj/instance1:instances:100":"2dcd2966","/obj/instance1:instances:101":"9331cd82","/obj/instance1:instances:102":"df50fccb","/obj/instance1:instances:103":"97f8590d","/obj/instance1:instances:104":"bbcd220d","/obj/instance1:instances:105":"4ae06139","/obj/instance1:instances:106":"8873d5ea","/obj/instance1:instances:107":"39d8af8d","/obj/instance1:instances:108":"bb11bd4e","/obj/instance1:instances:109":"a32bba35"})",
R"({"\/obj\/box:polygons:1":"9d416418","\/obj\/instance1:instances:0":"0d54c6cc","\/obj\/instance1:instances:1":"293d9340","\/obj\/instance1:instances:10":"3a0c8681","\/obj\/instance1:instances:100":"2dcd2966","\/obj\/instance1:instances:101":"9331cd82","\/obj\/instance1:instances:102":"df50fccb","\/obj\/instance1:instances:103":"97f8590d","\/obj\/instance1:instances:104":"bbcd220d","\/obj\/instance1:instances:105":"4ae06139","\/obj\/instance1:instances:106":"8873d5ea","\/obj\/instance1:instances:107":"39d8af8d","\/obj\/instance1:instances:108":"bb11bd4e","\/obj\/instance1:instances:109":"a32bba35","\/obj\/instance1:instances:11":"01e5970d","\/obj\/instance1:instances:110":"ccb9e1f2","\/obj\/instance1:instances:111":"f8dd3a48","\/obj\/instance1:instances:112":"a99e07a8","\/obj\/instance1:instances:113":"e75599a4","\/obj\/instance1:instances:114":"794200f3","\/obj\/instance1:instances:115":"2a3a1728","\/obj\/instance1:instances:116":"478544a1","\/obj\/instance1:instances:117":"b2bd969a","\/obj\/instance1:instance)");
diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc
index 5125e010b81..ca390fae424 100644
--- a/source/blender/blenkernel/intern/curve.cc
+++ b/source/blender/blenkernel/intern/curve.cc
@@ -321,7 +321,7 @@ IDTypeInfo IDType_ID_CU_LEGACY = {
/* foreach_id */ curve_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ nullptr,
- /* owner_get */ nullptr,
+ /* owner_pointer_get */ nullptr,
/* blend_write */ curve_blend_write,
/* blend_read_data */ curve_blend_read_data,
@@ -2247,7 +2247,7 @@ static void minimum_twist_between_two_points(BevPoint *current_point, BevPoint *
static void make_bevel_list_3D_minimum_twist(BevList *bl)
{
- BevPoint *bevp2, *bevp1, *bevp0; /* standard for all make_bevel_list_3D_* funcs */
+ BevPoint *bevp2, *bevp1, *bevp0; /* Standard for all make_bevel_list_3D_* functions. */
int nr;
float q[4];
@@ -2358,7 +2358,7 @@ static void make_bevel_list_3D_minimum_twist(BevList *bl)
static void make_bevel_list_3D_tangent(BevList *bl)
{
- BevPoint *bevp2, *bevp1, *bevp0; /* standard for all make_bevel_list_3D_* funcs */
+ BevPoint *bevp2, *bevp1, *bevp0; /* Standard for all make_bevel_list_3D_* functions. */
int nr;
float bevp0_tan[3];
@@ -2470,7 +2470,7 @@ static void make_bevel_list_segment_2D(BevList *bl)
static void make_bevel_list_2D(BevList *bl)
{
- /* NOTE(campbell): `bevp->dir` and `bevp->quat` are not needed for beveling but are
+ /* NOTE(@campbellbarton): `bevp->dir` and `bevp->quat` are not needed for beveling but are
* used when making a path from a 2D curve, therefore they need to be set. */
BevPoint *bevp0, *bevp1, *bevp2;
diff --git a/source/blender/blenkernel/intern/curve_catmull_rom.cc b/source/blender/blenkernel/intern/curve_catmull_rom.cc
index 952d59edcf9..b5f1a7cc450 100644
--- a/source/blender/blenkernel/intern/curve_catmull_rom.cc
+++ b/source/blender/blenkernel/intern/curve_catmull_rom.cc
@@ -17,16 +17,14 @@ int calculate_evaluated_num(const int points_num, const bool cyclic, const int r
}
/* Adapted from Cycles #catmull_rom_basis_eval function. */
-template<typename T>
-static T calculate_basis(const T &a, const T &b, const T &c, const T &d, const float parameter)
+void calculate_basis(const float parameter, float4 &r_weights)
{
const float t = parameter;
const float s = 1.0f - parameter;
- const float n0 = -t * s * s;
- const float n1 = 2.0f + t * t * (3.0f * t - 5.0f);
- const float n2 = 2.0f + s * s * (3.0f * s - 5.0f);
- const float n3 = -s * t * t;
- return 0.5f * (a * n0 + b * n1 + c * n2 + d * n3);
+ r_weights[0] = -t * s * s;
+ r_weights[1] = 2.0f + t * t * (3.0f * t - 5.0f);
+ r_weights[2] = 2.0f + s * s * (3.0f * s - 5.0f);
+ r_weights[3] = -s * t * t;
}
template<typename T>
@@ -35,7 +33,7 @@ static void evaluate_segment(const T &a, const T &b, const T &c, const T &d, Mut
const float step = 1.0f / dst.size();
dst.first() = b;
for (const int i : dst.index_range().drop_front(1)) {
- dst[i] = calculate_basis<T>(a, b, c, d, i * step);
+ dst[i] = interpolate<T>(a, b, c, d, i * step);
}
}
@@ -141,11 +139,7 @@ void interpolate_to_evaluated(const GSpan src,
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
- /* TODO: Use DefaultMixer or other generic mixing in the basis evaluation function to simplify
- * supporting more types. */
- if constexpr (is_same_any_v<T, float, float2, float3, float4, int8_t, int, int64_t>) {
- interpolate_to_evaluated(src.typed<T>(), cyclic, resolution, dst.typed<T>());
- }
+ interpolate_to_evaluated(src.typed<T>(), cyclic, resolution, dst.typed<T>());
});
}
@@ -156,11 +150,7 @@ void interpolate_to_evaluated(const GSpan src,
{
attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
using T = decltype(dummy);
- /* TODO: Use DefaultMixer or other generic mixing in the basis evaluation function to simplify
- * supporting more types. */
- if constexpr (is_same_any_v<T, float, float2, float3, float4, int8_t, int, int64_t>) {
- interpolate_to_evaluated(src.typed<T>(), cyclic, evaluated_offsets, dst.typed<T>());
- }
+ interpolate_to_evaluated(src.typed<T>(), cyclic, evaluated_offsets, dst.typed<T>());
});
}
diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc
deleted file mode 100644
index 424fa311dc7..00000000000
--- a/source/blender/blenkernel/intern/curve_eval.cc
+++ /dev/null
@@ -1,587 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_array.hh"
-#include "BLI_index_range.hh"
-#include "BLI_listbase.h"
-#include "BLI_map.hh"
-#include "BLI_span.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_task.hh"
-#include "BLI_vector.hh"
-
-#include "DNA_curve_types.h"
-
-#include "BKE_anonymous_attribute.hh"
-#include "BKE_curve.h"
-#include "BKE_curves.hh"
-#include "BKE_geometry_set.hh"
-#include "BKE_spline.hh"
-
-using blender::Array;
-using blender::float3;
-using blender::float4x4;
-using blender::GVArray;
-using blender::GVArraySpan;
-using blender::IndexRange;
-using blender::Map;
-using blender::MutableSpan;
-using blender::Span;
-using blender::StringRefNull;
-using blender::VArray;
-using blender::VArraySpan;
-using blender::Vector;
-using blender::bke::AttributeIDRef;
-using blender::bke::AttributeMetaData;
-
-blender::Span<SplinePtr> CurveEval::splines() const
-{
- return splines_;
-}
-
-blender::MutableSpan<SplinePtr> CurveEval::splines()
-{
- return splines_;
-}
-
-bool CurveEval::has_spline_with_type(const CurveType type) const
-{
- for (const SplinePtr &spline : this->splines()) {
- if (spline->type() == type) {
- return true;
- }
- }
- return false;
-}
-
-void CurveEval::resize(const int size)
-{
- splines_.resize(size);
- attributes.reallocate(size);
-}
-
-void CurveEval::add_spline(SplinePtr spline)
-{
- splines_.append(std::move(spline));
-}
-
-void CurveEval::add_splines(MutableSpan<SplinePtr> splines)
-{
- for (SplinePtr &spline : splines) {
- this->add_spline(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);
- }
-}
-
-bool CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
-{
- bool have_minmax = false;
- for (const SplinePtr &spline : this->splines()) {
- if (spline->size()) {
- spline->bounds_min_max(min, max, use_evaluated);
- have_minmax = true;
- }
- }
-
- return have_minmax;
-}
-
-float CurveEval::total_length() const
-{
- float length = 0.0f;
- for (const SplinePtr &spline : this->splines()) {
- length += spline->length();
- }
- return length;
-}
-
-int CurveEval::total_control_point_num() const
-{
- int count = 0;
- for (const SplinePtr &spline : this->splines()) {
- count += spline->size();
- }
- return count;
-}
-
-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;
-}
-
-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_num();
- }
- offsets.last() = offset;
- return offsets;
-}
-
-blender::Array<float> CurveEval::accumulated_spline_lengths() const
-{
- Array<float> spline_lengths(splines_.size() + 1);
- float spline_length = 0.0f;
- for (const int i : splines_.index_range()) {
- spline_lengths[i] = spline_length;
- spline_length += splines_[i]->length();
- }
- spline_lengths.last() = spline_length;
- return spline_lengths;
-}
-
-void CurveEval::mark_cache_invalid()
-{
- for (SplinePtr &spline : splines_) {
- spline->mark_cache_invalid();
- }
-}
-
-static HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type)
-{
- switch (dna_handle_type) {
- case HD_FREE:
- return BEZIER_HANDLE_FREE;
- case HD_AUTO:
- return BEZIER_HANDLE_AUTO;
- case HD_VECT:
- return BEZIER_HANDLE_VECTOR;
- case HD_ALIGN:
- return BEZIER_HANDLE_ALIGN;
- case HD_AUTO_ANIM:
- return BEZIER_HANDLE_AUTO;
- case HD_ALIGN_DOUBLESIDE:
- return BEZIER_HANDLE_ALIGN;
- }
- BLI_assert_unreachable();
- return BEZIER_HANDLE_AUTO;
-}
-
-static NormalMode normal_mode_from_dna_curve(const int twist_mode)
-{
- switch (twist_mode) {
- case CU_TWIST_Z_UP:
- case CU_TWIST_TANGENT:
- return NORMAL_MODE_Z_UP;
- case CU_TWIST_MINIMUM:
- return NORMAL_MODE_MINIMUM_TWIST;
- }
- BLI_assert_unreachable();
- return NORMAL_MODE_MINIMUM_TWIST;
-}
-
-static KnotsMode knots_mode_from_dna_nurb(const short flag)
-{
- switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) {
- case CU_NURB_ENDPOINT:
- return NURBS_KNOT_MODE_ENDPOINT;
- case CU_NURB_BEZIER:
- return NURBS_KNOT_MODE_BEZIER;
- case CU_NURB_ENDPOINT | CU_NURB_BEZIER:
- return NURBS_KNOT_MODE_ENDPOINT_BEZIER;
- default:
- return NURBS_KNOT_MODE_NORMAL;
- }
-
- BLI_assert_unreachable();
- return NURBS_KNOT_MODE_NORMAL;
-}
-
-static SplinePtr spline_from_dna_bezier(const Nurb &nurb)
-{
- std::unique_ptr<BezierSpline> spline = std::make_unique<BezierSpline>();
- spline->set_resolution(nurb.resolu);
- spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC);
-
- Span<const BezTriple> src_points{nurb.bezt, nurb.pntsu};
- spline->resize(src_points.size());
- MutableSpan<float3> positions = spline->positions();
- MutableSpan<float3> handle_positions_left = spline->handle_positions_left(true);
- MutableSpan<float3> handle_positions_right = spline->handle_positions_right(true);
- MutableSpan<int8_t> handle_types_left = spline->handle_types_left();
- MutableSpan<int8_t> handle_types_right = spline->handle_types_right();
- MutableSpan<float> radii = spline->radii();
- MutableSpan<float> tilts = spline->tilts();
-
- blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) {
- for (const int i : range) {
- const BezTriple &bezt = src_points[i];
- positions[i] = bezt.vec[1];
- handle_positions_left[i] = bezt.vec[0];
- handle_types_left[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h1);
- handle_positions_right[i] = bezt.vec[2];
- handle_types_right[i] = handle_type_from_dna_bezt((eBezTriple_Handle)bezt.h2);
- radii[i] = bezt.radius;
- tilts[i] = bezt.tilt;
- }
- });
-
- return spline;
-}
-
-static SplinePtr spline_from_dna_nurbs(const Nurb &nurb)
-{
- 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);
-
- Span<const BPoint> src_points{nurb.bp, nurb.pntsu};
- spline->resize(src_points.size());
- MutableSpan<float3> positions = spline->positions();
- MutableSpan<float> weights = spline->weights();
- MutableSpan<float> radii = spline->radii();
- MutableSpan<float> tilts = spline->tilts();
-
- blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) {
- for (const int i : range) {
- const BPoint &bp = src_points[i];
- positions[i] = bp.vec;
- weights[i] = bp.vec[3];
- radii[i] = bp.radius;
- tilts[i] = bp.tilt;
- }
- });
-
- return spline;
-}
-
-static SplinePtr spline_from_dna_poly(const Nurb &nurb)
-{
- std::unique_ptr<PolySpline> spline = std::make_unique<PolySpline>();
- spline->set_cyclic(nurb.flagu & CU_NURB_CYCLIC);
-
- Span<const BPoint> src_points{nurb.bp, nurb.pntsu};
- spline->resize(src_points.size());
- MutableSpan<float3> positions = spline->positions();
- MutableSpan<float> radii = spline->radii();
- MutableSpan<float> tilts = spline->tilts();
-
- blender::threading::parallel_for(src_points.index_range(), 2048, [&](IndexRange range) {
- for (const int i : range) {
- const BPoint &bp = src_points[i];
- positions[i] = bp.vec;
- radii[i] = bp.radius;
- tilts[i] = bp.tilt;
- }
- });
-
- return spline;
-}
-
-std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve,
- const ListBase &nurbs_list)
-{
- Vector<const Nurb *> nurbs(nurbs_list);
-
- std::unique_ptr<CurveEval> curve = std::make_unique<CurveEval>();
- curve->resize(nurbs.size());
- MutableSpan<SplinePtr> splines = curve->splines();
-
- blender::threading::parallel_for(nurbs.index_range(), 256, [&](IndexRange range) {
- for (const int i : range) {
- switch (nurbs[i]->type) {
- case CU_BEZIER:
- splines[i] = spline_from_dna_bezier(*nurbs[i]);
- break;
- case CU_NURBS:
- splines[i] = spline_from_dna_nurbs(*nurbs[i]);
- break;
- case CU_POLY:
- splines[i] = spline_from_dna_poly(*nurbs[i]);
- break;
- default:
- BLI_assert_unreachable();
- break;
- }
- }
- });
-
- /* Normal mode is stored separately in each spline to facilitate combining
- * splines from multiple curve objects, where the value may be different. */
- const NormalMode normal_mode = normal_mode_from_dna_curve(dna_curve.twist_mode);
- for (SplinePtr &spline : curve->splines()) {
- spline->normal_mode = normal_mode;
- }
-
- return curve;
-}
-
-std::unique_ptr<CurveEval> curve_eval_from_dna_curve(const Curve &dna_curve)
-{
- return curve_eval_from_dna_curve(dna_curve, *BKE_curve_nurbs_get_for_read(&dna_curve));
-}
-
-static void copy_attributes_between_components(
- const blender::bke::AttributeAccessor &src_attributes,
- blender::bke::MutableAttributeAccessor &dst_attributes,
- Span<std::string> skip)
-{
- src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
- if (id.is_named() && skip.contains(id.name())) {
- return true;
- }
-
- GVArray src_attribute = src_attributes.lookup(id, meta_data.domain, meta_data.data_type);
- if (!src_attribute) {
- return true;
- }
- GVArraySpan src_attribute_data{src_attribute};
-
- blender::bke::GAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write(
- id, meta_data.domain, meta_data.data_type);
- if (!dst_attribute) {
- return true;
- }
- dst_attribute.varray.set_all(src_attribute_data.data());
- dst_attribute.finish();
- return true;
- });
-}
-
-std::unique_ptr<CurveEval> curves_to_curve_eval(const Curves &curves_id)
-{
- CurveComponent src_component;
- src_component.replace(&const_cast<Curves &>(curves_id), GeometryOwnershipType::ReadOnly);
- const blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(
- curves_id.geometry);
- const blender::bke::AttributeAccessor src_attributes = curves.attributes();
-
- VArray<int> resolution = curves.resolution();
- VArray<int8_t> normal_mode = curves.normal_mode();
-
- VArraySpan<float> nurbs_weights{
- src_attributes.lookup_or_default<float>("nurbs_weight", ATTR_DOMAIN_POINT, 0.0f)};
- VArraySpan<int8_t> nurbs_orders{
- src_attributes.lookup_or_default<int8_t>("nurbs_order", ATTR_DOMAIN_CURVE, 4)};
- VArraySpan<int8_t> nurbs_knots_modes{
- src_attributes.lookup_or_default<int8_t>("knots_mode", ATTR_DOMAIN_CURVE, 0)};
-
- VArraySpan<int8_t> handle_types_right{
- src_attributes.lookup_or_default<int8_t>("handle_type_right", ATTR_DOMAIN_POINT, 0)};
- VArraySpan<int8_t> handle_types_left{
- src_attributes.lookup_or_default<int8_t>("handle_type_left", ATTR_DOMAIN_POINT, 0)};
-
- /* Create splines with the correct size and type. */
- VArray<int8_t> curve_types = curves.curve_types();
- std::unique_ptr<CurveEval> curve_eval = std::make_unique<CurveEval>();
- for (const int curve_index : curve_types.index_range()) {
- const IndexRange points = curves.points_for_curve(curve_index);
-
- std::unique_ptr<Spline> spline;
- /* #CurveEval does not support catmull rom curves, so convert those to poly splines. */
- switch (std::max<int8_t>(1, curve_types[curve_index])) {
- case CURVE_TYPE_POLY: {
- spline = std::make_unique<PolySpline>();
- spline->resize(points.size());
- break;
- }
- case CURVE_TYPE_BEZIER: {
- std::unique_ptr<BezierSpline> bezier_spline = std::make_unique<BezierSpline>();
- bezier_spline->resize(points.size());
- bezier_spline->set_resolution(resolution[curve_index]);
- bezier_spline->handle_types_left().copy_from(handle_types_left.slice(points));
- bezier_spline->handle_types_right().copy_from(handle_types_right.slice(points));
-
- spline = std::move(bezier_spline);
- break;
- }
- case CURVE_TYPE_NURBS: {
- std::unique_ptr<NURBSpline> nurb_spline = std::make_unique<NURBSpline>();
- nurb_spline->resize(points.size());
- nurb_spline->set_resolution(resolution[curve_index]);
- nurb_spline->weights().copy_from(nurbs_weights.slice(points));
- nurb_spline->set_order(nurbs_orders[curve_index]);
- nurb_spline->knots_mode = static_cast<KnotsMode>(nurbs_knots_modes[curve_index]);
-
- spline = std::move(nurb_spline);
- break;
- }
- case CURVE_TYPE_CATMULL_ROM:
- /* Not supported yet. */
- BLI_assert_unreachable();
- continue;
- }
- spline->positions().fill(float3(0));
- spline->tilts().fill(0.0f);
- spline->radii().fill(1.0f);
- spline->normal_mode = static_cast<NormalMode>(normal_mode[curve_index]);
- curve_eval->add_spline(std::move(spline));
- }
-
- curve_eval->attributes.reallocate(curve_eval->splines().size());
-
- CurveComponentLegacy dst_component;
- dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable);
- blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
-
- copy_attributes_between_components(src_attributes,
- dst_attributes,
- {"curve_type",
- "resolution",
- "normal_mode",
- "nurbs_weight",
- "nurbs_order",
- "knots_mode",
- "handle_type_right",
- "handle_type_left"});
-
- return curve_eval;
-}
-
-Curves *curve_eval_to_curves(const CurveEval &curve_eval)
-{
- Curves *curves_id = blender::bke::curves_new_nomain(curve_eval.total_control_point_num(),
- curve_eval.splines().size());
- CurveComponent dst_component;
- dst_component.replace(curves_id, GeometryOwnershipType::Editable);
- blender::bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
-
- blender::bke::CurvesGeometry &curves = blender::bke::CurvesGeometry::wrap(curves_id->geometry);
- curves.offsets_for_write().copy_from(curve_eval.control_point_offsets());
- MutableSpan<int8_t> curve_types = curves.curve_types_for_write();
-
- blender::bke::SpanAttributeWriter<int8_t> normal_mode =
- dst_attributes.lookup_or_add_for_write_only_span<int8_t>("normal_mode", ATTR_DOMAIN_CURVE);
- blender::bke::SpanAttributeWriter<float> nurbs_weight;
- blender::bke::SpanAttributeWriter<int> nurbs_order;
- blender::bke::SpanAttributeWriter<int8_t> nurbs_knots_mode;
- if (curve_eval.has_spline_with_type(CURVE_TYPE_NURBS)) {
- nurbs_weight = dst_attributes.lookup_or_add_for_write_only_span<float>("nurbs_weight",
- ATTR_DOMAIN_POINT);
- nurbs_order = dst_attributes.lookup_or_add_for_write_only_span<int>("nurbs_order",
- ATTR_DOMAIN_CURVE);
- nurbs_knots_mode = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("knots_mode",
- ATTR_DOMAIN_CURVE);
- }
- blender::bke::SpanAttributeWriter<int8_t> handle_type_right;
- blender::bke::SpanAttributeWriter<int8_t> handle_type_left;
- if (curve_eval.has_spline_with_type(CURVE_TYPE_BEZIER)) {
- handle_type_right = dst_attributes.lookup_or_add_for_write_only_span<int8_t>(
- "handle_type_right", ATTR_DOMAIN_POINT);
- handle_type_left = dst_attributes.lookup_or_add_for_write_only_span<int8_t>("handle_type_left",
- ATTR_DOMAIN_POINT);
- }
-
- for (const int curve_index : curve_eval.splines().index_range()) {
- const Spline &spline = *curve_eval.splines()[curve_index];
- curve_types[curve_index] = curve_eval.splines()[curve_index]->type();
- normal_mode.span[curve_index] = curve_eval.splines()[curve_index]->normal_mode;
- const IndexRange points = curves.points_for_curve(curve_index);
-
- switch (spline.type()) {
- case CURVE_TYPE_POLY:
- break;
- case CURVE_TYPE_BEZIER: {
- const BezierSpline &src = static_cast<const BezierSpline &>(spline);
- handle_type_right.span.slice(points).copy_from(src.handle_types_right());
- handle_type_left.span.slice(points).copy_from(src.handle_types_left());
- break;
- }
- case CURVE_TYPE_NURBS: {
- const NURBSpline &src = static_cast<const NURBSpline &>(spline);
- nurbs_knots_mode.span[curve_index] = static_cast<int8_t>(src.knots_mode);
- nurbs_order.span[curve_index] = src.order();
- nurbs_weight.span.slice(points).copy_from(src.weights());
- break;
- }
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- break;
- }
- }
- }
-
- curves.update_curve_types();
-
- normal_mode.finish();
- nurbs_weight.finish();
- nurbs_order.finish();
- nurbs_knots_mode.finish();
- handle_type_right.finish();
- handle_type_left.finish();
-
- CurveComponentLegacy src_component;
- src_component.replace(&const_cast<CurveEval &>(curve_eval), GeometryOwnershipType::ReadOnly);
- const blender::bke::AttributeAccessor src_attributes = *src_component.attributes();
-
- copy_attributes_between_components(src_attributes, dst_attributes, {});
-
- return curves_id;
-}
-
-void CurveEval::assert_valid_point_attributes() const
-{
-#ifdef DEBUG
- if (splines_.size() == 0) {
- return;
- }
- const int layer_len = splines_.first()->attributes.data.totlayer;
-
- Array<AttributeIDRef> ids_in_order(layer_len);
- Array<AttributeMetaData> meta_data_in_order(layer_len);
-
- {
- int i = 0;
- splines_.first()->attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- ids_in_order[i] = attribute_id;
- meta_data_in_order[i] = meta_data;
- i++;
- return true;
- },
- ATTR_DOMAIN_POINT);
- }
-
- for (const SplinePtr &spline : splines_) {
- /* All splines should have the same number of attributes. */
- BLI_assert(spline->attributes.data.totlayer == layer_len);
-
- int i = 0;
- spline->attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- /* Attribute names and IDs should have the same order and exist on all splines. */
- BLI_assert(attribute_id == ids_in_order[i]);
-
- /* Attributes with the same ID different splines should all have the same type. */
- BLI_assert(meta_data == meta_data_in_order[i]);
-
- i++;
- return true;
- },
- ATTR_DOMAIN_POINT);
- }
-
-#endif
-}
diff --git a/source/blender/blenkernel/intern/curve_legacy_convert.cc b/source/blender/blenkernel/intern/curve_legacy_convert.cc
index ff5bbc32afe..938dcbd6269 100644
--- a/source/blender/blenkernel/intern/curve_legacy_convert.cc
+++ b/source/blender/blenkernel/intern/curve_legacy_convert.cc
@@ -115,7 +115,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
MutableSpan<float> tilts = curves.tilt_for_write();
auto create_poly = [&](IndexMask selection) {
- threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) {
+ threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span<BPoint> src_points(src_curve.bp, src_curve.pntsu);
@@ -142,7 +142,7 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
MutableSpan<int8_t> handle_types_l = curves.handle_types_left_for_write();
MutableSpan<int8_t> handle_types_r = curves.handle_types_right_for_write();
- threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) {
+ threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span<BezTriple> src_points(src_curve.bezt, src_curve.pntsu);
@@ -165,12 +165,12 @@ Curves *curve_legacy_to_curves(const Curve &curve_legacy, const ListBase &nurbs_
};
auto create_nurbs = [&](IndexMask selection) {
- threading::parallel_for(selection.index_range(), 246, [&](IndexRange range) {
- MutableSpan<int> resolutions = curves.resolution_for_write();
- MutableSpan<float> nurbs_weights = curves.nurbs_weights_for_write();
- MutableSpan<int8_t> nurbs_orders = curves.nurbs_orders_for_write();
- MutableSpan<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes_for_write();
+ MutableSpan<int> resolutions = curves.resolution_for_write();
+ MutableSpan<float> nurbs_weights = curves.nurbs_weights_for_write();
+ MutableSpan<int8_t> nurbs_orders = curves.nurbs_orders_for_write();
+ MutableSpan<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes_for_write();
+ threading::parallel_for(selection.index_range(), 256, [&](IndexRange range) {
for (const int curve_i : selection.slice(range)) {
const Nurb &src_curve = *src_curves[curve_i];
const Span src_points(src_curve.bp, src_curve.pntsu);
diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc
index 3ab6fb01ea5..62d5682da0f 100644
--- a/source/blender/blenkernel/intern/curve_nurbs.cc
+++ b/source/blender/blenkernel/intern/curve_nurbs.cc
@@ -4,8 +4,9 @@
* \ingroup bke
*/
-#include "BKE_attribute_math.hh"
+#include "BLI_task.hh"
+#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
namespace blender::bke::curves::nurbs {
@@ -192,16 +193,16 @@ static void interpolate_to_evaluated(const BasisCache &basis_cache,
{
attribute_math::DefaultMixer<T> mixer{dst};
- for (const int i : dst.index_range()) {
- Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order);
-
- for (const int j : point_weights.index_range()) {
- const int point_index = (basis_cache.start_indices[i] + j) % src.size();
- mixer.mix_in(i, src[point_index], point_weights[j]);
+ threading::parallel_for(dst.index_range(), 128, [&](const IndexRange range) {
+ for (const int i : range) {
+ Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order);
+ for (const int j : point_weights.index_range()) {
+ const int point_index = (basis_cache.start_indices[i] + j) % src.size();
+ mixer.mix_in(i, src[point_index], point_weights[j]);
+ }
}
- }
-
- mixer.finalize();
+ mixer.finalize(range);
+ });
}
template<typename T>
@@ -213,17 +214,18 @@ static void interpolate_to_evaluated_rational(const BasisCache &basis_cache,
{
attribute_math::DefaultMixer<T> mixer{dst};
- for (const int i : dst.index_range()) {
- Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order);
+ threading::parallel_for(dst.index_range(), 128, [&](const IndexRange range) {
+ for (const int i : range) {
+ Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order);
- for (const int j : point_weights.index_range()) {
- const int point_index = (basis_cache.start_indices[i] + j) % src.size();
- const float weight = point_weights[j] * control_weights[point_index];
- mixer.mix_in(i, src[point_index], weight);
+ for (const int j : point_weights.index_range()) {
+ const int point_index = (basis_cache.start_indices[i] + j) % src.size();
+ const float weight = point_weights[j] * control_weights[point_index];
+ mixer.mix_in(i, src[point_index], weight);
+ }
}
- }
-
- mixer.finalize();
+ mixer.finalize(range);
+ });
}
void interpolate_to_evaluated(const BasisCache &basis_cache,
diff --git a/source/blender/blenkernel/intern/curve_poly.cc b/source/blender/blenkernel/intern/curve_poly.cc
index 7ab92068d81..2b2cf9adeee 100644
--- a/source/blender/blenkernel/intern/curve_poly.cc
+++ b/source/blender/blenkernel/intern/curve_poly.cc
@@ -65,8 +65,21 @@ void calculate_tangents(const Span<float3> positions,
tangents.last() = direction_bisect(second_to_last, last, first, used_fallback);
}
else {
- tangents.first() = math::normalize(positions[1] - positions.first());
- tangents.last() = math::normalize(positions.last() - positions[positions.size() - 2]);
+ const float epsilon = 1e-6f;
+ if (math::almost_equal_relative(positions[0], positions[1], epsilon)) {
+ tangents.first() = {0.0f, 0.0f, 0.0f};
+ used_fallback = true;
+ }
+ else {
+ tangents.first() = math::normalize(positions[1] - positions[0]);
+ }
+ if (math::almost_equal_relative(positions.last(0), positions.last(1), epsilon)) {
+ tangents.last() = {0.0f, 0.0f, 0.0f};
+ used_fallback = true;
+ }
+ else {
+ tangents.last() = math::normalize(positions.last(0) - positions.last(1));
+ }
}
if (!used_fallback) {
diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
index 7f051b683b4..b9fea2a27b8 100644
--- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
+++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc
@@ -640,10 +640,10 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last());
mesh->flag |= ME_AUTOSMOOTH;
mesh->smoothresh = DEG2RADF(180.0f);
- 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);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
foreach_curve_combination(curves_info, offsets, [&](const CombinationInfo &info) {
fill_mesh_topology(info.vert_range.start(),
@@ -711,7 +711,7 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
Set<AttributeIDRef> main_attributes_set;
- MutableAttributeAccessor mesh_attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor mesh_attributes = mesh->attributes_for_write();
main_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (!should_add_attribute_to_mesh(main_attributes, mesh_attributes, id)) {
diff --git a/source/blender/blenkernel/intern/curves.cc b/source/blender/blenkernel/intern/curves.cc
index 78791b55b4d..d801484de62 100644
--- a/source/blender/blenkernel/intern/curves.cc
+++ b/source/blender/blenkernel/intern/curves.cc
@@ -53,8 +53,6 @@ using blender::Vector;
static const char *ATTR_POSITION = "position";
-static void update_custom_data_pointers(Curves &curves);
-
static void curves_init_data(ID *id)
{
Curves *curves = (Curves *)id;
@@ -97,8 +95,6 @@ static void curves_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src,
dst.runtime->type_counts = src.runtime->type_counts;
- dst.update_customdata_pointers();
-
curves_dst->batch_cache = nullptr;
}
@@ -170,7 +166,6 @@ static void curves_blend_read_data(BlendDataReader *reader, ID *id)
/* Geometry */
CustomData_blend_read(reader, &curves->geometry.point_data, curves->geometry.point_num);
CustomData_blend_read(reader, &curves->geometry.curve_data, curves->geometry.curve_num);
- update_custom_data_pointers(*curves);
BLO_read_int32_array(reader, curves->geometry.curve_num + 1, &curves->geometry.curve_offsets);
@@ -204,40 +199,35 @@ static void curves_blend_read_expand(BlendExpander *expander, ID *id)
}
IDTypeInfo IDType_ID_CV = {
- /*id_code */ ID_CV,
- /*id_filter */ FILTER_ID_CV,
- /*main_listbase_index */ INDEX_ID_CV,
- /*struct_size */ sizeof(Curves),
- /*name */ "Curves",
- /*name_plural */ "curves",
- /*translation_context */ BLT_I18NCONTEXT_ID_CURVES,
- /*flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
- /*asset_type_info */ nullptr,
-
- /*init_data */ curves_init_data,
- /*copy_data */ curves_copy_data,
- /*free_data */ curves_free_data,
- /*make_local */ nullptr,
- /*foreach_id */ curves_foreach_id,
- /*foreach_cache */ nullptr,
- /*foreach_path */ nullptr,
- /*owner_get */ nullptr,
-
- /*blend_write */ curves_blend_write,
- /*blend_read_data */ curves_blend_read_data,
- /*blend_read_lib */ curves_blend_read_lib,
- /*blend_read_expand */ curves_blend_read_expand,
-
- /*blend_read_undo_preserve */ nullptr,
-
- /*lib_override_apply_post */ nullptr,
+ /* id_code */ ID_CV,
+ /* id_filter */ FILTER_ID_CV,
+ /* main_listbase_index */ INDEX_ID_CV,
+ /* struct_size */ sizeof(Curves),
+ /* name*/ "Curves",
+ /* name_plural */ "hair_curves",
+ /* translation_context */ BLT_I18NCONTEXT_ID_CURVES,
+ /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ curves_init_data,
+ /* copy_data */ curves_copy_data,
+ /* free_data */ curves_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ curves_foreach_id,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_pointer_get */ nullptr,
+
+ /* blend_write */ curves_blend_write,
+ /* blend_read_data */ curves_blend_read_data,
+ /* blend_read_lib */ curves_blend_read_lib,
+ /* blend_read_expand */ curves_blend_read_expand,
+
+ /* blend_read_undo_preserve */ nullptr,
+
+ /* lib_override_apply_post */ nullptr,
};
-static void update_custom_data_pointers(Curves &curves)
-{
- blender::bke::CurvesGeometry::wrap(curves.geometry).update_customdata_pointers();
-}
-
void *BKE_curves_add(Main *bmain, const char *name)
{
Curves *curves = static_cast<Curves *>(BKE_id_new(bmain, ID_CV, name));
@@ -273,7 +263,7 @@ BoundBox *BKE_curves_boundbox_get(Object *ob)
return ob->runtime.bb;
}
-bool BKE_curves_customdata_required(const Curves *UNUSED(curves), const char *name)
+bool BKE_curves_attribute_required(const Curves *UNUSED(curves), const char *name)
{
return STREQ(name, ATTR_POSITION);
}
@@ -301,6 +291,8 @@ static void curves_evaluate_modifiers(struct Depsgraph *depsgraph,
ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
+ BKE_modifiers_clear_errors(object);
+
/* Get effective list of modifiers to execute. Some effects like shape keys
* are added as virtual modifiers before the user created modifiers. */
VirtualModifierData virtualModifierData;
@@ -329,6 +321,14 @@ void BKE_curves_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Ob
Curves *curves = static_cast<Curves *>(object->data);
GeometrySet geometry_set = GeometrySet::create_with_curves(curves,
GeometryOwnershipType::ReadOnly);
+ if (object->mode == OB_MODE_SCULPT_CURVES) {
+ /* Try to propagate deformation data through modifier evaluation, so that sculpt mode can work
+ * on evaluated curves. */
+ GeometryComponentEditData &edit_component =
+ geometry_set.get_component_for_write<GeometryComponentEditData>();
+ edit_component.curves_edit_hints_ = std::make_unique<blender::bke::CurvesEditHints>(
+ *static_cast<const Curves *>(DEG_get_original_object(object)->data));
+ }
curves_evaluate_modifiers(depsgraph, scene, object, geometry_set);
/* Assign evaluated object. */
@@ -366,6 +366,8 @@ namespace blender::bke {
Curves *curves_new_nomain(const int points_num, const int curves_num)
{
+ BLI_assert(points_num >= 0);
+ BLI_assert(curves_num >= 0);
Curves *curves_id = static_cast<Curves *>(BKE_id_new_nomain(ID_CV, nullptr));
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
curves.resize(points_num, curves_num);
@@ -388,6 +390,23 @@ Curves *curves_new_nomain(CurvesGeometry curves)
return curves_id;
}
+void curves_copy_parameters(const Curves &src, Curves &dst)
+{
+ dst.flag = src.flag;
+ dst.attributes_active_index = src.attributes_active_index;
+ MEM_SAFE_FREE(dst.mat);
+ dst.mat = static_cast<Material **>(MEM_malloc_arrayN(src.totcol, sizeof(Material *), __func__));
+ dst.totcol = src.totcol;
+ MutableSpan(dst.mat, dst.totcol).copy_from(Span(src.mat, src.totcol));
+ dst.symmetry = src.symmetry;
+ dst.selection_domain = src.selection_domain;
+ dst.surface = src.surface;
+ MEM_SAFE_FREE(dst.surface_uv_map);
+ if (src.surface_uv_map != nullptr) {
+ dst.surface_uv_map = BLI_strdup(src.surface_uv_map);
+ }
+}
+
CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const Object *surface_ob)
{
this->curves_to_world = curves_ob.obmat;
@@ -402,4 +421,20 @@ CurvesSurfaceTransforms::CurvesSurfaceTransforms(const Object &curves_ob, const
}
}
+bool CurvesEditHints::is_valid() const
+{
+ const int point_num = this->curves_id_orig.geometry.point_num;
+ if (this->positions.has_value()) {
+ if (this->positions->size() != point_num) {
+ return false;
+ }
+ }
+ if (this->deform_mats.has_value()) {
+ if (this->deform_mats->size() != point_num) {
+ return false;
+ }
+ }
+ return true;
+}
+
} // namespace blender::bke
diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc
index 8d955a46275..06789e34ad4 100644
--- a/source/blender/blenkernel/intern/curves_geometry.cc
+++ b/source/blender/blenkernel/intern/curves_geometry.cc
@@ -13,6 +13,7 @@
#include "BLI_index_mask_ops.hh"
#include "BLI_length_parameterize.hh"
#include "BLI_math_rotation.hh"
+#include "BLI_task.hh"
#include "DNA_curves_types.h"
@@ -57,7 +58,7 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
CustomData_add_layer_named(&this->point_data,
CD_PROP_FLOAT3,
- CD_DEFAULT,
+ CD_CONSTRUCT,
nullptr,
this->point_num,
ATTR_POSITION.c_str());
@@ -68,8 +69,6 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
#endif
this->offsets_for_write().first() = 0;
- this->update_customdata_pointers();
-
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
/* Fill the type counts with the default so they're in a valid state. */
this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num;
@@ -95,8 +94,6 @@ static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
/* Though type counts are a cache, they must be copied because they are calculated eagerly. */
dst.runtime->type_counts = src.runtime->type_counts;
-
- dst.update_customdata_pointers();
}
CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
@@ -130,9 +127,6 @@ static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src)
MEM_SAFE_FREE(src.curve_offsets);
std::swap(dst.runtime, src.runtime);
-
- src.update_customdata_pointers();
- dst.update_customdata_pointers();
}
CurvesGeometry::CurvesGeometry(CurvesGeometry &&other)
@@ -228,7 +222,7 @@ static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves,
return {data, num};
}
data = (T *)CustomData_add_layer_named(
- &custom_data, type, CD_CALLOC, nullptr, num, name.c_str());
+ &custom_data, type, CD_SET_DEFAULT, nullptr, num, name.c_str());
MutableSpan<T> span = {data, num};
if (num > 0 && span.first() != default_value) {
span.fill(default_value);
@@ -261,6 +255,12 @@ void CurvesGeometry::fill_curve_types(const IndexMask selection, const CurveType
this->fill_curve_types(type);
return;
}
+ if (std::optional<int8_t> single_type = this->curve_types().get_if_single()) {
+ if (single_type == type) {
+ /* No need for an array if the types are already a single with the correct type. */
+ return;
+ }
+ }
/* A potential performance optimization is only counting the changed indices. */
this->curve_types_for_write().fill_indices(selection, type);
this->update_curve_types();
@@ -306,13 +306,11 @@ void CurvesGeometry::update_curve_types()
Span<float3> CurvesGeometry::positions() const
{
- return {(const float3 *)this->position, this->point_num};
+ return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_POSITION);
}
MutableSpan<float3> CurvesGeometry::positions_for_write()
{
- this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named(
- &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num);
- return {(float3 *)this->position, this->point_num};
+ return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_POSITION);
}
Span<int> CurvesGeometry::offsets() const
@@ -724,8 +722,9 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
}
});
- /* Correct the first and last tangents of Bezier curves so that they align with the inner
- * handles. This is a separate loop to avoid the cost when Bezier type curves are not used. */
+ /* Correct the first and last tangents of non-cyclic Bezier curves so that they align with the
+ * inner handles. This is a separate loop to avoid the cost when Bezier type curves are not
+ * used. */
Vector<int64_t> bezier_indices;
const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, bezier_indices);
if (!bezier_mask.is_empty()) {
@@ -735,14 +734,20 @@ Span<float3> CurvesGeometry::evaluated_tangents() const
threading::parallel_for(bezier_mask.index_range(), 1024, [&](IndexRange range) {
for (const int curve_index : bezier_mask.slice(range)) {
+ if (cyclic[curve_index]) {
+ continue;
+ }
const IndexRange points = this->points_for_curve(curve_index);
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
- if (handles_right[points.first()] != positions[points.first()]) {
+ const float epsilon = 1e-6f;
+ if (!math::almost_equal_relative(
+ handles_right[points.first()], positions[points.first()], epsilon)) {
tangents[evaluated_points.first()] = math::normalize(handles_right[points.first()] -
positions[points.first()]);
}
- if (handles_left[points.last()] != positions[points.last()]) {
+ if (!math::almost_equal_relative(
+ handles_left[points.last()], positions[points.last()], epsilon)) {
tangents[evaluated_points.last()] = math::normalize(positions[points.last()] -
handles_left[points.last()]);
}
@@ -943,6 +948,12 @@ void CurvesGeometry::ensure_evaluated_lengths() const
this->runtime->length_cache_dirty = false;
}
+void CurvesGeometry::ensure_can_interpolate_to_evaluated() const
+{
+ this->ensure_evaluated_offsets();
+ this->ensure_nurbs_basis_cache();
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -952,16 +963,15 @@ void CurvesGeometry::ensure_evaluated_lengths() const
void CurvesGeometry::resize(const int points_num, const int curves_num)
{
if (points_num != this->point_num) {
- CustomData_realloc(&this->point_data, points_num);
+ CustomData_realloc(&this->point_data, this->points_num(), points_num);
this->point_num = points_num;
}
if (curves_num != this->curve_num) {
- CustomData_realloc(&this->curve_data, curves_num);
+ CustomData_realloc(&this->curve_data, this->curves_num(), curves_num);
this->curve_num = curves_num;
this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1));
}
this->tag_topology_changed();
- this->update_customdata_pointers();
}
void CurvesGeometry::tag_positions_changed()
@@ -1060,10 +1070,11 @@ void CurvesGeometry::transform(const float4x4 &matrix)
static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves)
{
- Span<float3> positions = curves.positions();
- if (curves.radius) {
- Span<float> radii{curves.radius, curves.points_num()};
- return bounds::min_max_with_radii(positions, radii);
+ const Span<float3> positions = curves.positions();
+ const VArray<float> radii = curves.attributes().lookup_or_default<float>(
+ ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f);
+ if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) {
+ return bounds::min_max_with_radii(positions, radii.get_internal_span());
}
return bounds::min_max(positions);
}
@@ -1079,31 +1090,6 @@ bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
return true;
}
-void CurvesGeometry::update_customdata_pointers()
-{
- this->position = (float(*)[3])CustomData_get_layer_named(
- &this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str());
- this->radius = (float *)CustomData_get_layer_named(
- &this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str());
- this->curve_type = (int8_t *)CustomData_get_layer_named(
- &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str());
-}
-
-static void *ensure_customdata_layer(CustomData &custom_data,
- const StringRefNull name,
- const eCustomDataType data_type,
- const int tot_elements)
-{
- for (const int other_layer_i : IndexRange(custom_data.totlayer)) {
- CustomDataLayer &new_layer = custom_data.layers[other_layer_i];
- if (name == StringRef(new_layer.name)) {
- return new_layer.data;
- }
- }
- return CustomData_add_layer_named(
- &custom_data, data_type, CD_DEFAULT, nullptr, tot_elements, name.c_str());
-}
-
static void copy_between_buffers(const CPPType &type,
const void *src_buffer,
void *dst_buffer,
@@ -1193,61 +1179,50 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves,
}
CurvesGeometry new_curves{new_point_count, new_curve_count};
+ Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer(
+ curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT);
+ Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer(
+ curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE);
threading::parallel_invoke(
+ 256 < new_point_count * new_curve_count,
/* Initialize curve offsets. */
[&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); },
- /* Copy over point attributes. */
[&]() {
- const CustomData &old_point_data = curves.point_data;
- CustomData &new_point_data = new_curves.point_data;
- for (const int layer_i : IndexRange(old_point_data.totlayer)) {
- const CustomDataLayer &old_layer = old_point_data.layers[layer_i];
- const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- void *dst_buffer = ensure_customdata_layer(
- new_point_data, old_layer.name, data_type, new_point_count);
-
- threading::parallel_for(
- copy_point_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
- for (const int range_i : ranges_range) {
- const IndexRange src_range = copy_point_ranges[range_i];
- copy_between_buffers(type,
- old_layer.data,
- dst_buffer,
- src_range,
- {copy_point_range_dst_offsets[range_i], src_range.size()});
- }
- });
+ /* Copy over point attributes. */
+ for (bke::AttributeTransferData &attribute : point_attributes) {
+ threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) {
+ for (const int range_i : range) {
+ const IndexRange src_range = copy_point_ranges[range_i];
+ copy_between_buffers(attribute.src.type(),
+ attribute.src.data(),
+ attribute.dst.span.data(),
+ src_range,
+ {copy_point_range_dst_offsets[range_i], src_range.size()});
+ }
+ });
}
},
- /* Copy over curve attributes.
- * In some cases points are just dissolved, so the the number of
- * curves will be the same. That could be optimized in the future. */
[&]() {
- const CustomData &old_curve_data = curves.curve_data;
- CustomData &new_curve_data = new_curves.curve_data;
- for (const int layer_i : IndexRange(old_curve_data.totlayer)) {
- const CustomDataLayer &old_layer = old_curve_data.layers[layer_i];
- const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- void *dst_buffer = ensure_customdata_layer(
- new_curve_data, old_layer.name, data_type, new_curve_count);
-
+ /* Copy over curve attributes.
+ * In some cases points are just dissolved, so the number of
+ * curves will be the same. That could be optimized in the future. */
+ for (bke::AttributeTransferData &attribute : curve_attributes) {
if (new_curves.curves_num() == curves.curves_num()) {
- type.copy_construct_n(old_layer.data, dst_buffer, new_curves.curves_num());
+ attribute.dst.span.copy_from(attribute.src);
}
else {
- copy_with_map({type, old_layer.data, curves.curves_num()},
- new_curve_orig_indices,
- {type, dst_buffer, new_curves.curves_num()});
+ copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span);
}
}
});
- new_curves.update_curve_types();
+ for (bke::AttributeTransferData &attribute : point_attributes) {
+ attribute.dst.finish();
+ }
+ for (bke::AttributeTransferData &attribute : curve_attributes) {
+ attribute.dst.finish();
+ }
return new_curves;
}
@@ -1285,8 +1260,13 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
}
CurvesGeometry new_curves{new_tot_points, new_tot_curves};
+ Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer(
+ curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT);
+ Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer(
+ curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE);
threading::parallel_invoke(
+ 256 < new_tot_points * new_tot_curves,
/* Initialize curve offsets. */
[&]() {
MutableSpan<int> new_offsets = new_curves.offsets_for_write();
@@ -1313,56 +1293,41 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
}
});
},
- /* Copy over point attributes. */
[&]() {
- const CustomData &old_point_data = curves.point_data;
- CustomData &new_point_data = new_curves.point_data;
- for (const int layer_i : IndexRange(old_point_data.totlayer)) {
- const CustomDataLayer &old_layer = old_point_data.layers[layer_i];
- const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- void *dst_buffer = ensure_customdata_layer(
- new_point_data, old_layer.name, data_type, new_tot_points);
-
- threading::parallel_for(
- old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
- for (const int range_i : ranges_range) {
- copy_between_buffers(type,
- old_layer.data,
- dst_buffer,
- old_point_ranges[range_i],
- new_point_ranges[range_i]);
- }
- });
+ /* Copy over point attributes. */
+ for (bke::AttributeTransferData &attribute : point_attributes) {
+ threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) {
+ for (const int range_i : range) {
+ copy_between_buffers(attribute.src.type(),
+ attribute.src.data(),
+ attribute.dst.span.data(),
+ old_point_ranges[range_i],
+ new_point_ranges[range_i]);
+ }
+ });
}
},
- /* Copy over curve attributes. */
[&]() {
- const CustomData &old_curve_data = curves.curve_data;
- CustomData &new_curve_data = new_curves.curve_data;
- for (const int layer_i : IndexRange(old_curve_data.totlayer)) {
- const CustomDataLayer &old_layer = old_curve_data.layers[layer_i];
- const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
- const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
-
- void *dst_buffer = ensure_customdata_layer(
- new_curve_data, old_layer.name, data_type, new_tot_curves);
-
- threading::parallel_for(
- old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
- for (const int range_i : ranges_range) {
- copy_between_buffers(type,
- old_layer.data,
- dst_buffer,
- old_curve_ranges[range_i],
- new_curve_ranges[range_i]);
- }
- });
+ /* Copy over curve attributes. */
+ for (bke::AttributeTransferData &attribute : curve_attributes) {
+ threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) {
+ for (const int range_i : range) {
+ copy_between_buffers(attribute.src.type(),
+ attribute.src.data(),
+ attribute.dst.span.data(),
+ old_curve_ranges[range_i],
+ new_curve_ranges[range_i]);
+ }
+ });
}
});
- new_curves.update_curve_types();
+ for (bke::AttributeTransferData &attribute : point_attributes) {
+ attribute.dst.finish();
+ }
+ for (bke::AttributeTransferData &attribute : curve_attributes) {
+ attribute.dst.finish();
+ }
return new_curves;
}
@@ -1407,73 +1372,57 @@ static void reverse_swap_curve_point_data(const CurvesGeometry &curves,
std::swap(a[end_index], b[i]);
std::swap(b[end_index], a[i]);
}
+ if (points.size() % 2) {
+ const int64_t middle_index = points.size() / 2;
+ std::swap(a[middle_index], b[middle_index]);
+ }
}
});
}
-static bool layer_matches_name_and_type(const CustomDataLayer &layer,
- const StringRef name,
- const eCustomDataType type)
-{
- if (layer.type != type) {
- return false;
- }
- return layer.name == name;
-}
-
void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
{
- CustomData_duplicate_referenced_layers(&this->point_data, this->points_num());
+ Set<StringRef> bezier_handle_names{{ATTR_HANDLE_POSITION_LEFT,
+ ATTR_HANDLE_POSITION_RIGHT,
+ ATTR_HANDLE_TYPE_LEFT,
+ ATTR_HANDLE_TYPE_RIGHT}};
- /* Collect the Bezier handle attributes while iterating through the point custom data layers;
- * they need special treatment later. */
- MutableSpan<float3> positions_left;
- MutableSpan<float3> positions_right;
- MutableSpan<int8_t> types_left;
- MutableSpan<int8_t> types_right;
+ MutableAttributeAccessor attributes = this->attributes_for_write();
- for (const int layer_i : IndexRange(this->point_data.totlayer)) {
- CustomDataLayer &layer = this->point_data.layers[layer_i];
-
- if (positions_left.is_empty() &&
- layer_matches_name_and_type(layer, ATTR_HANDLE_POSITION_LEFT, CD_PROP_FLOAT3)) {
- positions_left = {static_cast<float3 *>(layer.data), this->points_num()};
- continue;
- }
- if (positions_right.is_empty() &&
- layer_matches_name_and_type(layer, ATTR_HANDLE_POSITION_RIGHT, CD_PROP_FLOAT3)) {
- positions_right = {static_cast<float3 *>(layer.data), this->points_num()};
- continue;
+ attributes.for_all([&](const AttributeIDRef &id, AttributeMetaData meta_data) {
+ if (meta_data.domain != ATTR_DOMAIN_POINT) {
+ return true;
}
- if (types_left.is_empty() &&
- layer_matches_name_and_type(layer, ATTR_HANDLE_TYPE_LEFT, CD_PROP_INT8)) {
- types_left = {static_cast<int8_t *>(layer.data), this->points_num()};
- continue;
- }
- if (types_right.is_empty() &&
- layer_matches_name_and_type(layer, ATTR_HANDLE_TYPE_RIGHT, CD_PROP_INT8)) {
- types_right = {static_cast<int8_t *>(layer.data), this->points_num()};
- continue;
+ if (id.is_named() && bezier_handle_names.contains(id.name())) {
+ return true;
}
- const eCustomDataType data_type = static_cast<eCustomDataType>(layer.type);
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
+ GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
+ attribute_math::convert_to_static_type(attribute.span.type(), [&](auto dummy) {
using T = decltype(dummy);
- reverse_curve_point_data<T>(
- *this, curves_to_reverse, {static_cast<T *>(layer.data), this->points_num()});
+ reverse_curve_point_data<T>(*this, curves_to_reverse, attribute.span.typed<T>());
});
- }
+ attribute.finish();
+ return true;
+ });
/* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the
* values for the left and right must swap. Use a utility to swap and reverse at the same time,
* to avoid loading the attribute twice. Generally we can expect the right layer to exist when
* the left does, but there's no need to count on it, so check for both attributes. */
- if (!positions_left.is_empty() && !positions_right.is_empty()) {
- reverse_swap_curve_point_data(*this, curves_to_reverse, positions_left, positions_right);
+ if (attributes.contains(ATTR_HANDLE_POSITION_LEFT) &&
+ attributes.contains(ATTR_HANDLE_POSITION_RIGHT)) {
+ reverse_swap_curve_point_data(*this,
+ curves_to_reverse,
+ this->handle_positions_left_for_write(),
+ this->handle_positions_right_for_write());
}
- if (!types_left.is_empty() && !types_right.is_empty()) {
- reverse_swap_curve_point_data(*this, curves_to_reverse, types_left, types_right);
+ if (attributes.contains(ATTR_HANDLE_TYPE_LEFT) && attributes.contains(ATTR_HANDLE_TYPE_RIGHT)) {
+ reverse_swap_curve_point_data(*this,
+ curves_to_reverse,
+ this->handle_types_left_for_write(),
+ this->handle_types_right_for_write());
}
this->tag_topology_changed();
@@ -1481,23 +1430,21 @@ void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
void CurvesGeometry::remove_attributes_based_on_types()
{
- const int points_num = this->points_num();
- const int curves_num = this->curves_num();
+ MutableAttributeAccessor attributes = this->attributes_for_write();
if (!this->has_curve_with_type(CURVE_TYPE_BEZIER)) {
- CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_TYPE_LEFT.c_str(), points_num);
- CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_TYPE_RIGHT.c_str(), points_num);
- CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_POSITION_LEFT.c_str(), points_num);
- CustomData_free_layer_named(&this->point_data, ATTR_HANDLE_POSITION_RIGHT.c_str(), points_num);
+ attributes.remove(ATTR_HANDLE_TYPE_LEFT);
+ attributes.remove(ATTR_HANDLE_TYPE_RIGHT);
+ attributes.remove(ATTR_HANDLE_POSITION_LEFT);
+ attributes.remove(ATTR_HANDLE_POSITION_RIGHT);
}
if (!this->has_curve_with_type(CURVE_TYPE_NURBS)) {
- CustomData_free_layer_named(&this->point_data, ATTR_NURBS_WEIGHT.c_str(), points_num);
- CustomData_free_layer_named(&this->curve_data, ATTR_NURBS_ORDER.c_str(), curves_num);
- CustomData_free_layer_named(&this->curve_data, ATTR_NURBS_KNOTS_MODE.c_str(), curves_num);
+ attributes.remove(ATTR_NURBS_WEIGHT);
+ attributes.remove(ATTR_NURBS_ORDER);
+ attributes.remove(ATTR_NURBS_KNOTS_MODE);
}
if (!this->has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_CATMULL_ROM, CURVE_TYPE_NURBS})) {
- CustomData_free_layer_named(&this->curve_data, ATTR_RESOLUTION.c_str(), curves_num);
+ attributes.remove(ATTR_RESOLUTION);
}
- this->update_customdata_pointers();
}
/** \} */
@@ -1519,12 +1466,15 @@ static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves,
MutableSpan<T> r_values)
{
attribute_math::DefaultMixer<T> mixer(r_values);
- for (const int i_curve : IndexRange(curves.curves_num())) {
- for (const int i_point : curves.points_for_curve(i_curve)) {
- mixer.mix_in(i_curve, old_values[i_point]);
+
+ threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) {
+ for (const int i_curve : range) {
+ for (const int i_point : curves.points_for_curve(i_curve)) {
+ mixer.mix_in(i_curve, old_values[i_point]);
+ }
}
- }
- mixer.finalize();
+ mixer.finalize(range);
+ });
}
/**
diff --git a/source/blender/blenkernel/intern/curves_utils.cc b/source/blender/blenkernel/intern/curves_utils.cc
index f7db60ee588..f5a69a995a3 100644
--- a/source/blender/blenkernel/intern/curves_utils.cc
+++ b/source/blender/blenkernel/intern/curves_utils.cc
@@ -84,6 +84,33 @@ void fill_points(const CurvesGeometry &curves,
});
}
+void fill_points(const CurvesGeometry &curves,
+ Span<IndexRange> curve_ranges,
+ GPointer value,
+ GMutableSpan dst)
+{
+ BLI_assert(*value.type() == dst.type());
+ const CPPType &type = dst.type();
+ threading::parallel_for(curve_ranges.index_range(), 512, [&](IndexRange range) {
+ for (const IndexRange range : curve_ranges.slice(range)) {
+ const IndexRange points = curves.points_for_curves(range);
+ type.fill_assign_n(value.get(), dst.slice(points).data(), points.size());
+ }
+ });
+}
+
+bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
+{
+ bke::CurvesGeometry dst_curves(0, src_curves.curves_num());
+ CustomData_copy(&src_curves.curve_data,
+ &dst_curves.curve_data,
+ CD_MASK_ALL,
+ CD_DUPLICATE,
+ src_curves.curves_num());
+ dst_curves.runtime->type_counts = src_curves.runtime->type_counts;
+ return dst_curves;
+}
+
IndexMask indices_for_type(const VArray<int8_t> &types,
const std::array<int, CURVE_TYPES_NUM> &type_counts,
const CurveType type,
diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc
index 277218033e9..0589e1ef8c7 100644
--- a/source/blender/blenkernel/intern/customdata.cc
+++ b/source/blender/blenkernel/intern/customdata.cc
@@ -26,6 +26,7 @@
#include "BLI_math_vector.hh"
#include "BLI_mempool.h"
#include "BLI_path_util.h"
+#include "BLI_set.hh"
#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_string_ref.hh"
@@ -58,6 +59,7 @@
#include "data_transfer_intern.h"
using blender::IndexRange;
+using blender::Set;
using blender::Span;
using blender::StringRef;
using blender::Vector;
@@ -152,9 +154,15 @@ struct LayerTypeInfo {
void (*swap)(void *data, const int *corner_indices);
/**
- * a function to set a layer's data to default values. if null, the
- * default is assumed to be all zeros */
- void (*set_default)(void *data, int count);
+ * Set values to the type's default. If undefined, the default is assumed to be zeroes.
+ * Memory pointed to by #data is expected to be uninitialized.
+ */
+ void (*set_default_value)(void *data, int count);
+ /**
+ * Construct and fill a valid value for the type. Necessary for non-trivial types.
+ * Memory pointed to by #data is expected to be uninitialized.
+ */
+ void (*construct)(void *data, int count);
/** A function used by mesh validating code, must ensures passed item has valid data. */
cd_validate validate;
@@ -165,7 +173,7 @@ struct LayerTypeInfo {
void (*initminmax)(void *min, void *max);
void (*add)(void *data1, const void *data2);
void (*dominmax)(const void *data1, void *min, void *max);
- void (*copyvalue)(const void *source, void *dest, const int mixmode, const float mixfactor);
+ void (*copyvalue)(const void *source, void *dest, int mixmode, const float mixfactor);
/** a function to read data from a cdf file */
bool (*read)(CDataFile *cdf, void *data, int count);
@@ -187,7 +195,7 @@ struct LayerTypeInfo {
/** \name Callbacks for (#MDeformVert, #CD_MDEFORMVERT)
* \{ */
-static void layerCopy_mdeformvert(const void *source, void *dest, int count)
+static void layerCopy_mdeformvert(const void *source, void *dest, const int count)
{
int i, size = sizeof(MDeformVert);
@@ -209,7 +217,7 @@ static void layerCopy_mdeformvert(const void *source, void *dest, int count)
}
}
-static void layerFree_mdeformvert(void *data, int count, int size)
+static void layerFree_mdeformvert(void *data, const int count, const int size)
{
for (int i = 0; i < count; i++) {
MDeformVert *dvert = static_cast<MDeformVert *>(POINTER_OFFSET(data, i * size));
@@ -222,38 +230,10 @@ static void layerFree_mdeformvert(void *data, int count, int size)
}
}
-/* copy just zeros in this case */
-static void layerCopy_bmesh_elem_py_ptr(const void *UNUSED(source), void *dest, int count)
-{
- const int size = sizeof(void *);
-
- for (int i = 0; i < count; i++) {
- void **ptr = (void **)POINTER_OFFSET(dest, i * size);
- *ptr = nullptr;
- }
-}
-
-#ifndef WITH_PYTHON
-void bpy_bm_generic_invalidate(struct BPy_BMGeneric *UNUSED(self))
-{
- /* dummy */
-}
-#endif
-
-static void layerFree_bmesh_elem_py_ptr(void *data, int count, int size)
-{
- for (int i = 0; i < count; i++) {
- void **ptr = (void **)POINTER_OFFSET(data, i * size);
- if (*ptr) {
- bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(*ptr));
- }
- }
-}
-
static void layerInterp_mdeformvert(const void **sources,
const float *weights,
const float *UNUSED(sub_weights),
- int count,
+ const int count,
void *dest)
{
/* a single linked list of MDeformWeight's
@@ -338,6 +318,11 @@ static void layerInterp_mdeformvert(const void **sources,
}
}
+static void layerConstruct_mdeformvert(void *data, const int count)
+{
+ memset(data, 0, sizeof(MDeformVert) * count);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -347,7 +332,7 @@ static void layerInterp_mdeformvert(const void **sources,
static void layerInterp_normal(const void **sources,
const float *weights,
const float *UNUSED(sub_weights),
- int count,
+ const int count,
void *dest)
{
/* NOTE: This is linear interpolation, which is not optimal for vectors.
@@ -355,8 +340,8 @@ static void layerInterp_normal(const void **sources,
* so for now it will do... */
float no[3] = {0.0f};
- while (count--) {
- madd_v3_v3fl(no, (const float *)sources[count], weights[count]);
+ for (const int i : IndexRange(count)) {
+ madd_v3_v3fl(no, (const float *)sources[i], weights[i]);
}
/* Weighted sum of normalized vectors will **not** be normalized, even if weights are. */
@@ -406,7 +391,7 @@ static void layerCopyValue_normal(const void *source,
/** \name Callbacks for (#MTFace, #CD_MTFACE)
* \{ */
-static void layerCopy_tface(const void *source, void *dest, int count)
+static void layerCopy_tface(const void *source, void *dest, const int count)
{
const MTFace *source_tf = (const MTFace *)source;
MTFace *dest_tf = (MTFace *)dest;
@@ -415,8 +400,11 @@ static void layerCopy_tface(const void *source, void *dest, int count)
}
}
-static void layerInterp_tface(
- const void **sources, const float *weights, const float *sub_weights, int count, void *dest)
+static void layerInterp_tface(const void **sources,
+ const float *weights,
+ const float *sub_weights,
+ const int count,
+ void *dest)
{
MTFace *tf = static_cast<MTFace *>(dest);
float uv[4][2] = {{0.0f}};
@@ -456,7 +444,7 @@ static void layerSwap_tface(void *data, const int *corner_indices)
memcpy(tf->uv, uv, sizeof(tf->uv));
}
-static void layerDefault_tface(void *data, int count)
+static void layerDefault_tface(void *data, const int count)
{
static MTFace default_tf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}};
MTFace *tf = (MTFace *)data;
@@ -477,7 +465,7 @@ static int layerMaxNum_tface()
/** \name Callbacks for (#MFloatProperty, #CD_PROP_FLOAT)
* \{ */
-static void layerCopy_propFloat(const void *source, void *dest, int count)
+static void layerCopy_propFloat(const void *source, void *dest, const int count)
{
memcpy(dest, source, sizeof(MFloatProperty) * count);
}
@@ -485,7 +473,7 @@ static void layerCopy_propFloat(const void *source, void *dest, int count)
static void layerInterp_propFloat(const void **sources,
const float *weights,
const float *UNUSED(sub_weights),
- int count,
+ const int count,
void *dest)
{
float result = 0.0f;
@@ -520,15 +508,10 @@ static bool layerValidate_propFloat(void *data, const uint totitems, const bool
/** \name Callbacks for (#MIntProperty, #CD_PROP_INT32)
* \{ */
-static void layerCopy_propInt(const void *source, void *dest, int count)
-{
- memcpy(dest, source, sizeof(MIntProperty) * count);
-}
-
static void layerInterp_propInt(const void **sources,
const float *weights,
const float *UNUSED(sub_weights),
- int count,
+ const int count,
void *dest)
{
float result = 0.0f;
@@ -547,7 +530,7 @@ static void layerInterp_propInt(const void **sources,
/** \name Callbacks for (#MStringProperty, #CD_PROP_STRING)
* \{ */
-static void layerCopy_propString(const void *source, void *dest, int count)
+static void layerCopy_propString(const void *source, void *dest, const int count)
{
memcpy(dest, source, sizeof(MStringProperty) * count);
}
@@ -558,7 +541,7 @@ static void layerCopy_propString(const void *source, void *dest, int count)
/** \name Callbacks for (#OrigSpaceFace, #CD_ORIGSPACE)
* \{ */
-static void layerCopy_origspace_face(const void *source, void *dest, int count)
+static void layerCopy_origspace_face(const void *source, void *dest, const int count)
{
const OrigSpaceFace *source_tf = (const OrigSpaceFace *)source;
OrigSpaceFace *dest_tf = (OrigSpaceFace *)dest;
@@ -568,8 +551,11 @@ static void layerCopy_origspace_face(const void *source, void *dest, int count)
}
}
-static void layerInterp_origspace_face(
- const void **sources, const float *weights, const float *sub_weights, int count, void *dest)
+static void layerInterp_origspace_face(const void **sources,
+ const float *weights,
+ const float *sub_weights,
+ const int count,
+ void *dest)
{
OrigSpaceFace *osf = static_cast<OrigSpaceFace *>(dest);
float uv[4][2] = {{0.0f}};
@@ -606,7 +592,7 @@ static void layerSwap_origspace_face(void *data, const int *corner_indices)
memcpy(osf->uv, uv, sizeof(osf->uv));
}
-static void layerDefault_origspace_face(void *data, int count)
+static void layerDefault_origspace_face(void *data, const int count)
{
static OrigSpaceFace default_osf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}};
OrigSpaceFace *osf = (OrigSpaceFace *)data;
@@ -652,7 +638,7 @@ static void layerSwap_mdisps(void *data, const int *ci)
}
}
-static void layerCopy_mdisps(const void *source, void *dest, int count)
+static void layerCopy_mdisps(const void *source, void *dest, const int count)
{
const MDisps *s = static_cast<const MDisps *>(source);
MDisps *d = static_cast<MDisps *>(dest);
@@ -673,7 +659,7 @@ static void layerCopy_mdisps(const void *source, void *dest, int count)
}
}
-static void layerFree_mdisps(void *data, int count, int UNUSED(size))
+static void layerFree_mdisps(void *data, const int count, const int UNUSED(size))
{
MDisps *d = static_cast<MDisps *>(data);
@@ -691,7 +677,12 @@ static void layerFree_mdisps(void *data, int count, int UNUSED(size))
}
}
-static bool layerRead_mdisps(CDataFile *cdf, void *data, int count)
+static void layerConstruct_mdisps(void *data, const int count)
+{
+ memset(data, 0, sizeof(MDisps) * count);
+}
+
+static bool layerRead_mdisps(CDataFile *cdf, void *data, const int count)
{
MDisps *d = static_cast<MDisps *>(data);
@@ -709,7 +700,7 @@ static bool layerRead_mdisps(CDataFile *cdf, void *data, int count)
return true;
}
-static bool layerWrite_mdisps(CDataFile *cdf, const void *data, int count)
+static bool layerWrite_mdisps(CDataFile *cdf, const void *data, const int count)
{
const MDisps *d = static_cast<const MDisps *>(data);
@@ -723,7 +714,7 @@ static bool layerWrite_mdisps(CDataFile *cdf, const void *data, int count)
return true;
}
-static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, int count)
+static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, const int count)
{
const MDisps *d = static_cast<const MDisps *>(data);
size_t size = 0;
@@ -738,6 +729,40 @@ static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, int
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Callbacks for (#CD_BM_ELEM_PYPTR)
+ * \{ */
+
+/* copy just zeros in this case */
+static void layerCopy_bmesh_elem_py_ptr(const void *UNUSED(source), void *dest, const int count)
+{
+ const int size = sizeof(void *);
+
+ for (int i = 0; i < count; i++) {
+ void **ptr = (void **)POINTER_OFFSET(dest, i * size);
+ *ptr = nullptr;
+ }
+}
+
+#ifndef WITH_PYTHON
+void bpy_bm_generic_invalidate(struct BPy_BMGeneric *UNUSED(self))
+{
+ /* dummy */
+}
+#endif
+
+static void layerFree_bmesh_elem_py_ptr(void *data, const int count, const int size)
+{
+ for (int i = 0; i < count; i++) {
+ void **ptr = (void **)POINTER_OFFSET(data, i * size);
+ if (*ptr) {
+ bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(*ptr));
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Callbacks for (`float`, #CD_PAINT_MASK)
* \{ */
@@ -762,7 +787,7 @@ static void layerInterp_paint_mask(const void **sources,
/** \name Callbacks for (#GridPaintMask, #CD_GRID_PAINT_MASK)
* \{ */
-static void layerCopy_grid_paint_mask(const void *source, void *dest, int count)
+static void layerCopy_grid_paint_mask(const void *source, void *dest, const int count)
{
const GridPaintMask *s = static_cast<const GridPaintMask *>(source);
GridPaintMask *d = static_cast<GridPaintMask *>(dest);
@@ -779,7 +804,7 @@ static void layerCopy_grid_paint_mask(const void *source, void *dest, int count)
}
}
-static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size))
+static void layerFree_grid_paint_mask(void *data, const int count, const int UNUSED(size))
{
GridPaintMask *gpm = static_cast<GridPaintMask *>(data);
@@ -789,6 +814,11 @@ static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size))
}
}
+static void layerConstruct_grid_paint_mask(void *data, const int count)
+{
+ memset(data, 0, sizeof(GridPaintMask) * count);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -867,7 +897,7 @@ static bool layerEqual_mloopcol(const void *data1, const void *data2)
return r * r + g * g + b * b + a * a < 0.001f;
}
-static void layerMultiply_mloopcol(void *data, float fac)
+static void layerMultiply_mloopcol(void *data, const float fac)
{
MLoopCol *m = static_cast<MLoopCol *>(data);
@@ -936,7 +966,7 @@ static void layerInitMinMax_mloopcol(void *vmin, void *vmax)
max->a = 0;
}
-static void layerDefault_mloopcol(void *data, int count)
+static void layerDefault_mloopcol(void *data, const int count)
{
MLoopCol default_mloopcol = {255, 255, 255, 255};
MLoopCol *mlcol = (MLoopCol *)data;
@@ -978,11 +1008,6 @@ static void layerInterp_mloopcol(const void **sources,
mc->a = round_fl_to_uchar_clamp(col.a);
}
-static int layerMaxNum_mloopcol()
-{
- return MAX_MCOL;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1016,7 +1041,7 @@ static bool layerEqual_mloopuv(const void *data1, const void *data2)
return len_squared_v2v2(luv1->uv, luv2->uv) < 0.00001f;
}
-static void layerMultiply_mloopuv(void *data, float fac)
+static void layerMultiply_mloopuv(void *data, const float fac)
{
MLoopUV *luv = static_cast<MLoopUV *>(data);
@@ -1110,7 +1135,7 @@ static bool layerEqual_mloop_origspace(const void *data1, const void *data2)
return len_squared_v2v2(luv1->uv, luv2->uv) < 0.00001f;
}
-static void layerMultiply_mloop_origspace(void *data, float fac)
+static void layerMultiply_mloop_origspace(void *data, const float fac)
{
OrigSpaceLoop *luv = static_cast<OrigSpaceLoop *>(data);
@@ -1162,8 +1187,11 @@ static void layerInterp_mloop_origspace(const void **sources,
}
/* --- end copy */
-static void layerInterp_mcol(
- const void **sources, const float *weights, const float *sub_weights, int count, void *dest)
+static void layerInterp_mcol(const void **sources,
+ const float *weights,
+ const float *sub_weights,
+ const int count,
+ void *dest)
{
MCol *mc = static_cast<MCol *>(dest);
struct {
@@ -1222,7 +1250,7 @@ static void layerSwap_mcol(void *data, const int *corner_indices)
memcpy(mcol, col, sizeof(col));
}
-static void layerDefault_mcol(void *data, int count)
+static void layerDefault_mcol(void *data, const int count)
{
static MCol default_mcol = {255, 255, 255, 255};
MCol *mcol = (MCol *)data;
@@ -1232,7 +1260,7 @@ static void layerDefault_mcol(void *data, int count)
}
}
-static void layerDefault_origindex(void *data, int count)
+static void layerDefault_origindex(void *data, const int count)
{
copy_vn_i((int *)data, count, ORIGINDEX_NONE);
}
@@ -1290,7 +1318,7 @@ static void layerInterp_shapekey(const void **sources,
/** \name Callbacks for (#MVertSkin, #CD_MVERT_SKIN)
* \{ */
-static void layerDefault_mvert_skin(void *data, int count)
+static void layerDefault_mvert_skin(void *data, const int count)
{
MVertSkin *vs = static_cast<MVertSkin *>(data);
@@ -1300,7 +1328,7 @@ static void layerDefault_mvert_skin(void *data, int count)
}
}
-static void layerCopy_mvert_skin(const void *source, void *dest, int count)
+static void layerCopy_mvert_skin(const void *source, void *dest, const int count)
{
memcpy(dest, source, sizeof(MVertSkin) * count);
}
@@ -1352,7 +1380,7 @@ static void layerSwap_flnor(void *data, const int *corner_indices)
/** \name Callbacks for (`int`, #CD_FACEMAP)
* \{ */
-static void layerDefault_fmap(void *data, int count)
+static void layerDefault_fmap(void *data, const int count)
{
int *fmap_num = (int *)data;
for (int i = 0; i < count; i++) {
@@ -1428,7 +1456,7 @@ static bool layerEqual_propcol(const void *data1, const void *data2)
return tot < 0.001f;
}
-static void layerMultiply_propcol(void *data, float fac)
+static void layerMultiply_propcol(void *data, const float fac)
{
MPropCol *m = static_cast<MPropCol *>(data);
mul_v4_fl(m->color, fac);
@@ -1458,7 +1486,7 @@ static void layerInitMinMax_propcol(void *vmin, void *vmax)
copy_v4_fl(max->color, FLT_MIN);
}
-static void layerDefault_propcol(void *data, int count)
+static void layerDefault_propcol(void *data, const int count)
{
/* Default to white, full alpha. */
MPropCol default_propcol = {{1.0f, 1.0f, 1.0f, 1.0f}};
@@ -1484,11 +1512,6 @@ static void layerInterp_propcol(const void **sources,
copy_v4_v4(mc->color, col);
}
-static int layerMaxNum_propcol()
-{
- return MAX_MCOL;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1510,7 +1533,7 @@ static void layerInterp_propfloat3(const void **sources,
copy_v3_v3((float *)dest, &result.x);
}
-static void layerMultiply_propfloat3(void *data, float fac)
+static void layerMultiply_propfloat3(void *data, const float fac)
{
vec3f *vec = static_cast<vec3f *>(data);
vec->x *= fac;
@@ -1563,7 +1586,7 @@ static void layerInterp_propfloat2(const void **sources,
copy_v2_v2((float *)dest, &result.x);
}
-static void layerMultiply_propfloat2(void *data, float fac)
+static void layerMultiply_propfloat2(void *data, const float fac)
{
vec2f *vec = static_cast<vec2f *>(data);
vec->x *= fac;
@@ -1628,30 +1651,23 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerFree_mdeformvert,
layerInterp_mdeformvert,
nullptr,
- nullptr},
+ nullptr,
+ layerConstruct_mdeformvert},
/* 3: CD_MEDGE */
{sizeof(MEdge), "MEdge", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 4: CD_MFACE */
{sizeof(MFace), "MFace", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr},
/* 5: CD_MTFACE */
- {sizeof(MTFace), "MTFace", 1,
- N_("UVMap"), layerCopy_tface, nullptr,
- layerInterp_tface, layerSwap_tface, layerDefault_tface,
- nullptr, nullptr, nullptr,
- nullptr, nullptr, nullptr,
- nullptr, nullptr, nullptr,
- nullptr, layerMaxNum_tface},
- /* 6: CD_MCOL */
- /* 4 MCol structs per face */
- {sizeof(MCol[4]),
- "MCol",
- 4,
- N_("Col"),
+ {sizeof(MTFace),
+ "MTFace",
+ 1,
+ N_("UVMap"),
+ layerCopy_tface,
nullptr,
+ layerInterp_tface,
+ layerSwap_tface,
nullptr,
- layerInterp_mcol,
- layerSwap_mcol,
- layerDefault_mcol,
+ layerDefault_tface,
nullptr,
nullptr,
nullptr,
@@ -1662,7 +1678,16 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr,
nullptr,
- layerMaxNum_mloopcol},
+ layerMaxNum_tface},
+ /* 6: CD_MCOL */
+ /* 4 MCol structs per face */
+ {sizeof(MCol[4]), "MCol", 4,
+ N_("Col"), nullptr, nullptr,
+ layerInterp_mcol, layerSwap_mcol, layerDefault_mcol,
+ nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr,
+ nullptr, nullptr, nullptr},
/* 7: CD_ORIGINDEX */
{sizeof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, layerDefault_origindex},
/* 8: CD_NORMAL */
@@ -1682,6 +1707,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr,
nullptr,
+ nullptr,
layerCopyValue_normal},
/* 9: CD_FACEMAP */
{sizeof(int), "", 0, nullptr, nullptr, nullptr, nullptr, nullptr, layerDefault_fmap, nullptr},
@@ -1695,13 +1721,14 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerInterp_propFloat,
nullptr,
nullptr,
+ nullptr,
layerValidate_propFloat},
/* 11: CD_PROP_INT32 */
{sizeof(MIntProperty),
"MIntProperty",
1,
N_("Int"),
- layerCopy_propInt,
+ nullptr,
nullptr,
layerInterp_propInt,
nullptr},
@@ -1740,6 +1767,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerInterp_mloopuv,
nullptr,
nullptr,
+ nullptr,
layerValidate_mloopuv,
layerEqual_mloopuv,
layerMultiply_mloopuv,
@@ -1762,6 +1790,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
layerDefault_mloopcol,
nullptr,
+ nullptr,
layerEqual_mloopcol,
layerMultiply_mloopcol,
layerInitMinMax_mloopcol,
@@ -1771,7 +1800,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr,
nullptr,
- layerMaxNum_mloopcol},
+ nullptr},
/* 18: CD_TANGENT */
{sizeof(float[4][4]), "", 0, N_("Tangent"), nullptr, nullptr, nullptr, nullptr, nullptr},
/* 19: CD_MDISPS */
@@ -1784,6 +1813,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
layerSwap_mdisps,
nullptr,
+ layerConstruct_mdisps,
nullptr,
nullptr,
nullptr,
@@ -1837,7 +1867,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
/* 28: CD_SHAPEKEY */
{sizeof(float[3]), "", 0, N_("ShapeKey"), nullptr, nullptr, layerInterp_shapekey},
/* 29: CD_BWEIGHT */
- {sizeof(float), "", 0, N_("BevelWeight"), nullptr, nullptr, layerInterp_bweight},
+ {sizeof(MFloatProperty), "MFloatProperty", 1, nullptr, nullptr, nullptr, layerInterp_bweight},
/* 30: CD_CREASE */
/* NOTE: we do not interpolate crease data as it should be either inherited for subdivided
* edges, or for vertex creases, only present on the original vertex. */
@@ -1853,6 +1883,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr,
nullptr,
+ nullptr,
layerEqual_mloop_origspace,
layerMultiply_mloop_origspace,
layerInitMinMax_mloop_origspace,
@@ -1870,6 +1901,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
layerDefault_mloopcol,
nullptr,
+ nullptr,
layerEqual_mloopcol,
layerMultiply_mloopcol,
layerInitMinMax_mloopcol,
@@ -1897,7 +1929,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerFree_grid_paint_mask,
nullptr,
nullptr,
- nullptr},
+ nullptr,
+ layerConstruct_grid_paint_mask},
/* 36: CD_MVERT_SKIN */
{sizeof(MVertSkin),
"MVertSkin",
@@ -1955,6 +1988,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
layerDefault_propcol,
nullptr,
+ nullptr,
layerEqual_propcol,
layerMultiply_propcol,
layerInitMinMax_propcol,
@@ -1964,7 +1998,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
nullptr,
nullptr,
nullptr,
- layerMaxNum_propcol},
+ nullptr},
/* 48: CD_PROP_FLOAT3 */
{sizeof(float[3]),
"vec3f",
@@ -1975,6 +2009,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerInterp_propfloat3,
nullptr,
nullptr,
+ nullptr,
layerValidate_propfloat3,
nullptr,
layerMultiply_propfloat3,
@@ -1990,6 +2025,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
layerInterp_propfloat2,
nullptr,
nullptr,
+ nullptr,
layerValidate_propfloat2,
nullptr,
layerMultiply_propfloat2,
@@ -2072,23 +2108,23 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
- /* vmask */ CD_MASK_MVERT | CD_MASK_BWEIGHT,
- /* emask */ CD_MASK_MEDGE | CD_MASK_BWEIGHT,
+ /* vmask */ CD_MASK_MVERT,
+ /* emask */ CD_MASK_MEDGE,
/* fmask */ 0,
/* pmask */ CD_MASK_MPOLY | CD_MASK_FACEMAP,
/* lmask */ CD_MASK_MLOOP,
};
const CustomData_MeshMasks CD_MASK_BAREMESH_ORIGINDEX = {
- /* vmask */ CD_MASK_MVERT | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX,
- /* emask */ CD_MASK_MEDGE | CD_MASK_BWEIGHT | CD_MASK_ORIGINDEX,
+ /* vmask */ CD_MASK_MVERT | CD_MASK_ORIGINDEX,
+ /* emask */ CD_MASK_MEDGE | CD_MASK_ORIGINDEX,
/* fmask */ 0,
/* pmask */ CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_ORIGINDEX,
/* lmask */ CD_MASK_MLOOP,
};
const CustomData_MeshMasks CD_MASK_MESH = {
/* vmask */ (CD_MASK_MVERT | CD_MASK_MDEFORMVERT | CD_MASK_MVERT_SKIN | CD_MASK_PAINT_MASK |
- CD_MASK_PROP_ALL | CD_MASK_CREASE),
- /* emask */ (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
+ CD_MASK_PROP_ALL | CD_MASK_CREASE | CD_MASK_BWEIGHT),
+ /* emask */ (CD_MASK_MEDGE | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL | CD_MASK_BWEIGHT),
/* fmask */ 0,
/* pmask */
(CD_MASK_MPOLY | CD_MASK_FACEMAP | CD_MASK_FREESTYLE_FACE | CD_MASK_PROP_ALL |
@@ -2100,8 +2136,8 @@ const CustomData_MeshMasks CD_MASK_MESH = {
const CustomData_MeshMasks CD_MASK_DERIVEDMESH = {
/* vmask */ (CD_MASK_ORIGINDEX | CD_MASK_MDEFORMVERT | CD_MASK_SHAPEKEY | CD_MASK_MVERT_SKIN |
CD_MASK_PAINT_MASK | CD_MASK_ORCO | CD_MASK_CLOTH_ORCO | CD_MASK_PROP_ALL |
- CD_MASK_CREASE),
- /* emask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_PROP_ALL),
+ CD_MASK_CREASE | CD_MASK_BWEIGHT),
+ /* emask */ (CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_EDGE | CD_MASK_BWEIGHT | CD_MASK_PROP_ALL),
/* fmask */ (CD_MASK_ORIGINDEX | CD_MASK_ORIGSPACE | CD_MASK_PREVIEW_MCOL | CD_MASK_TANGENT),
/* pmask */
(CD_MASK_ORIGINDEX | CD_MASK_FREESTYLE_FACE | CD_MASK_FACEMAP | CD_MASK_PROP_ALL |
@@ -2317,8 +2353,16 @@ bool CustomData_merge(const CustomData *source,
changed = true;
if (layer->anonymous_id != nullptr) {
- BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id);
newlayer->anonymous_id = layer->anonymous_id;
+ if (alloctype == CD_ASSIGN) {
+ layer->anonymous_id = nullptr;
+ }
+ else {
+ BKE_anonymous_attribute_id_increment_weak(layer->anonymous_id);
+ }
+ }
+ if (alloctype == CD_ASSIGN) {
+ layer->data = nullptr;
}
}
}
@@ -2327,19 +2371,74 @@ bool CustomData_merge(const CustomData *source,
return changed;
}
-void CustomData_realloc(CustomData *data, int totelem)
+static bool attribute_stored_in_bmesh_flag(const StringRef name)
+{
+ return ELEM(name, ".hide_vert", ".hide_edge", ".hide_poly", "material_index");
+}
+
+static CustomData shallow_copy_remove_non_bmesh_attributes(const CustomData &src)
{
- BLI_assert(totelem >= 0);
+ Vector<CustomDataLayer> dst_layers;
+ for (const CustomDataLayer &layer : Span<CustomDataLayer>{src.layers, src.totlayer}) {
+ if (!attribute_stored_in_bmesh_flag(layer.name)) {
+ dst_layers.append(layer);
+ }
+ }
+
+ CustomData dst = src;
+ dst.layers = static_cast<CustomDataLayer *>(
+ MEM_calloc_arrayN(dst_layers.size(), sizeof(CustomDataLayer), __func__));
+ dst.totlayer = dst_layers.size();
+ memcpy(dst.layers, dst_layers.data(), dst_layers.as_span().size_in_bytes());
+
+ CustomData_update_typemap(&dst);
+
+ return dst;
+}
+
+bool CustomData_merge_mesh_to_bmesh(const CustomData *source,
+ CustomData *dest,
+ const eCustomDataMask mask,
+ const eCDAllocType alloctype,
+ const int totelem)
+{
+ CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source);
+ const bool result = CustomData_merge(&source_copy, dest, mask, alloctype, totelem);
+ MEM_SAFE_FREE(source_copy.layers);
+ return result;
+}
+
+void CustomData_realloc(CustomData *data, const int old_size, const int new_size)
+{
+ BLI_assert(new_size >= 0);
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
- const LayerTypeInfo *typeInfo;
+ const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
+
+ const int64_t old_size_in_bytes = int64_t(old_size) * typeInfo->size;
+ const int64_t new_size_in_bytes = int64_t(new_size) * typeInfo->size;
if (layer->flag & CD_FLAG_NOFREE) {
- continue;
+ const void *old_data = layer->data;
+ layer->data = MEM_malloc_arrayN(new_size, typeInfo->size, __func__);
+ if (typeInfo->copy) {
+ typeInfo->copy(old_data, layer->data, std::min(old_size, new_size));
+ }
+ else {
+ std::memcpy(layer->data, old_data, std::min(old_size_in_bytes, new_size_in_bytes));
+ }
+ layer->flag &= ~CD_FLAG_NOFREE;
+ }
+ else {
+ layer->data = MEM_reallocN(layer->data, new_size_in_bytes);
+ }
+
+ if (new_size > old_size) {
+ /* Initialize new values for non-trivial types. */
+ if (typeInfo->construct) {
+ const int new_elements_num = new_size - old_size;
+ typeInfo->construct(POINTER_OFFSET(layer->data, old_size_in_bytes), new_elements_num);
+ }
}
- typeInfo = layerType_getInfo(layer->type);
- /* Use calloc to avoid the need to manually initialize new data in layers.
- * Useful for types like #MDeformVert which contain a pointer. */
- layer->data = MEM_recallocN(layer->data, (size_t)totelem * typeInfo->size);
}
}
@@ -2358,7 +2457,18 @@ void CustomData_copy(const CustomData *source,
CustomData_merge(source, dest, mask, alloctype, totelem);
}
-static void customData_free_layer__internal(CustomDataLayer *layer, int totelem)
+void CustomData_copy_mesh_to_bmesh(const CustomData *source,
+ CustomData *dest,
+ const eCustomDataMask mask,
+ const eCDAllocType alloctype,
+ const int totelem)
+{
+ CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source);
+ CustomData_copy(&source_copy, dest, mask, alloctype, totelem);
+ MEM_SAFE_FREE(source_copy.layers);
+}
+
+static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem)
{
const LayerTypeInfo *typeInfo;
@@ -2393,7 +2503,7 @@ void CustomData_reset(CustomData *data)
copy_vn_i(data->typemap, CD_NUMTYPES, -1);
}
-void CustomData_free(CustomData *data, int totelem)
+void CustomData_free(CustomData *data, const int totelem)
{
for (int i = 0; i < data->totlayer; i++) {
customData_free_layer__internal(&data->layers[i], totelem);
@@ -2407,7 +2517,7 @@ void CustomData_free(CustomData *data, int totelem)
CustomData_reset(data);
}
-void CustomData_free_typemask(CustomData *data, int totelem, eCustomDataMask mask)
+void CustomData_free_typemask(CustomData *data, const int totelem, eCustomDataMask mask)
{
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
@@ -2442,7 +2552,7 @@ static void customData_update_offsets(CustomData *data)
}
/* to use when we're in the middle of modifying layers */
-static int CustomData_get_layer_index__notypemap(const CustomData *data, int type)
+static int CustomData_get_layer_index__notypemap(const CustomData *data, const int type)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
@@ -2456,13 +2566,13 @@ static int CustomData_get_layer_index__notypemap(const CustomData *data, int typ
/* -------------------------------------------------------------------- */
/* index values to access the layers (offset from the layer start) */
-int CustomData_get_layer_index(const CustomData *data, int type)
+int CustomData_get_layer_index(const CustomData *data, const int type)
{
BLI_assert(customdata_typemap_is_valid(data));
return data->typemap[type];
}
-int CustomData_get_layer_index_n(const CustomData *data, int type, int n)
+int CustomData_get_layer_index_n(const CustomData *data, const int type, const int n)
{
BLI_assert(n >= 0);
int i = CustomData_get_layer_index(data, type);
@@ -2475,7 +2585,7 @@ int CustomData_get_layer_index_n(const CustomData *data, int type, int n)
return i;
}
-int CustomData_get_named_layer_index(const CustomData *data, int type, const char *name)
+int CustomData_get_named_layer_index(const CustomData *data, const int type, const char *name)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
@@ -2488,28 +2598,28 @@ int CustomData_get_named_layer_index(const CustomData *data, int type, const cha
return -1;
}
-int CustomData_get_active_layer_index(const CustomData *data, int type)
+int CustomData_get_active_layer_index(const CustomData *data, const int type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? layer_index + data->layers[layer_index].active : -1;
}
-int CustomData_get_render_layer_index(const CustomData *data, int type)
+int CustomData_get_render_layer_index(const CustomData *data, const int type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? layer_index + data->layers[layer_index].active_rnd : -1;
}
-int CustomData_get_clone_layer_index(const CustomData *data, int type)
+int CustomData_get_clone_layer_index(const CustomData *data, const int type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? layer_index + data->layers[layer_index].active_clone : -1;
}
-int CustomData_get_stencil_layer_index(const CustomData *data, int type)
+int CustomData_get_stencil_layer_index(const CustomData *data, const int type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
@@ -2519,7 +2629,7 @@ int CustomData_get_stencil_layer_index(const CustomData *data, int type)
/* -------------------------------------------------------------------- */
/* index values per layer type */
-int CustomData_get_named_layer(const CustomData *data, int type, const char *name)
+int CustomData_get_named_layer(const CustomData *data, const int type, const char *name)
{
const int named_index = CustomData_get_named_layer_index(data, type, name);
const int layer_index = data->typemap[type];
@@ -2527,28 +2637,28 @@ int CustomData_get_named_layer(const CustomData *data, int type, const char *nam
return (named_index != -1) ? named_index - layer_index : -1;
}
-int CustomData_get_active_layer(const CustomData *data, int type)
+int CustomData_get_active_layer(const CustomData *data, const int type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? data->layers[layer_index].active : -1;
}
-int CustomData_get_render_layer(const CustomData *data, int type)
+int CustomData_get_render_layer(const CustomData *data, const int type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? data->layers[layer_index].active_rnd : -1;
}
-int CustomData_get_clone_layer(const CustomData *data, int type)
+int CustomData_get_clone_layer(const CustomData *data, const int type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
return (layer_index != -1) ? data->layers[layer_index].active_clone : -1;
}
-int CustomData_get_stencil_layer(const CustomData *data, int type)
+int CustomData_get_stencil_layer(const CustomData *data, const int type)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
@@ -2562,7 +2672,13 @@ const char *CustomData_get_active_layer_name(const CustomData *data, const int t
return layer_index < 0 ? nullptr : data->layers[layer_index].name;
}
-void CustomData_set_layer_active(CustomData *data, int type, int n)
+const char *CustomData_get_render_layer_name(const CustomData *data, const int type)
+{
+ const int layer_index = CustomData_get_render_layer_index(data, type);
+ return layer_index < 0 ? nullptr : data->layers[layer_index].name;
+}
+
+void CustomData_set_layer_active(CustomData *data, const int type, const int n)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
@@ -2571,7 +2687,7 @@ void CustomData_set_layer_active(CustomData *data, int type, int n)
}
}
-void CustomData_set_layer_render(CustomData *data, int type, int n)
+void CustomData_set_layer_render(CustomData *data, const int type, const int n)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
@@ -2580,7 +2696,7 @@ void CustomData_set_layer_render(CustomData *data, int type, int n)
}
}
-void CustomData_set_layer_clone(CustomData *data, int type, int n)
+void CustomData_set_layer_clone(CustomData *data, const int type, const int n)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
@@ -2589,7 +2705,7 @@ void CustomData_set_layer_clone(CustomData *data, int type, int n)
}
}
-void CustomData_set_layer_stencil(CustomData *data, int type, int n)
+void CustomData_set_layer_stencil(CustomData *data, const int type, const int n)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
@@ -2598,7 +2714,7 @@ void CustomData_set_layer_stencil(CustomData *data, int type, int n)
}
}
-void CustomData_set_layer_active_index(CustomData *data, int type, int n)
+void CustomData_set_layer_active_index(CustomData *data, const int type, const int n)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
@@ -2610,7 +2726,7 @@ void CustomData_set_layer_active_index(CustomData *data, int type, int n)
}
}
-void CustomData_set_layer_render_index(CustomData *data, int type, int n)
+void CustomData_set_layer_render_index(CustomData *data, const int type, const int n)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
@@ -2622,7 +2738,7 @@ void CustomData_set_layer_render_index(CustomData *data, int type, int n)
}
}
-void CustomData_set_layer_clone_index(CustomData *data, int type, int n)
+void CustomData_set_layer_clone_index(CustomData *data, const int type, const int n)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
@@ -2634,7 +2750,7 @@ void CustomData_set_layer_clone_index(CustomData *data, int type, int n)
}
}
-void CustomData_set_layer_stencil_index(CustomData *data, int type, int n)
+void CustomData_set_layer_stencil_index(CustomData *data, const int type, const int n)
{
const int layer_index = data->typemap[type];
BLI_assert(customdata_typemap_is_valid(data));
@@ -2646,7 +2762,7 @@ void CustomData_set_layer_stencil_index(CustomData *data, int type, int n)
}
}
-void CustomData_set_layer_flag(CustomData *data, int type, int flag)
+void CustomData_set_layer_flag(CustomData *data, const int type, const int flag)
{
for (int i = 0; i < data->totlayer; i++) {
if (data->layers[i].type == type) {
@@ -2655,7 +2771,7 @@ void CustomData_set_layer_flag(CustomData *data, int type, int flag)
}
}
-void CustomData_clear_layer_flag(CustomData *data, int type, int flag)
+void CustomData_clear_layer_flag(CustomData *data, const int type, const int flag)
{
const int nflag = ~flag;
@@ -2666,7 +2782,7 @@ void CustomData_clear_layer_flag(CustomData *data, int type, int flag)
}
}
-static bool customData_resize(CustomData *data, int amount)
+static bool customData_resize(CustomData *data, const int amount)
{
CustomDataLayer *tmp = static_cast<CustomDataLayer *>(
MEM_calloc_arrayN((data->maxlayer + amount), sizeof(*tmp), __func__));
@@ -2685,59 +2801,72 @@ static bool customData_resize(CustomData *data, int amount)
}
static CustomDataLayer *customData_add_layer__internal(CustomData *data,
- int type,
- eCDAllocType alloctype,
+ const int type,
+ const eCDAllocType alloctype,
void *layerdata,
- int totelem,
+ const int totelem,
const char *name)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
- int flag = 0, index = data->totlayer;
- void *newlayerdata = nullptr;
-
- /* Passing a layer-data to copy from with an alloctype that won't copy is
- * most likely a bug */
- BLI_assert(!layerdata || ELEM(alloctype, CD_ASSIGN, CD_DUPLICATE, CD_REFERENCE));
+ int flag = 0;
if (!typeInfo->defaultname && CustomData_has_layer(data, type)) {
return &data->layers[CustomData_get_layer_index(data, type)];
}
- if (ELEM(alloctype, CD_ASSIGN, CD_REFERENCE)) {
- newlayerdata = layerdata;
- }
- else if (totelem > 0 && typeInfo->size > 0) {
- if (alloctype == CD_DUPLICATE && layerdata) {
- newlayerdata = MEM_malloc_arrayN((size_t)totelem, typeInfo->size, layerType_getName(type));
- }
- else {
- newlayerdata = MEM_calloc_arrayN((size_t)totelem, typeInfo->size, layerType_getName(type));
- }
-
- if (!newlayerdata) {
- return nullptr;
- }
- }
-
- if (alloctype == CD_DUPLICATE && layerdata) {
- if (totelem > 0) {
- if (typeInfo->copy) {
- typeInfo->copy(layerdata, newlayerdata, totelem);
+ void *newlayerdata = nullptr;
+ switch (alloctype) {
+ case CD_SET_DEFAULT:
+ if (totelem > 0) {
+ if (typeInfo->set_default_value) {
+ newlayerdata = MEM_malloc_arrayN(totelem, typeInfo->size, layerType_getName(type));
+ typeInfo->set_default_value(newlayerdata, totelem);
+ }
+ else {
+ newlayerdata = MEM_calloc_arrayN(totelem, typeInfo->size, layerType_getName(type));
+ }
+ }
+ break;
+ case CD_CONSTRUCT:
+ if (totelem > 0) {
+ newlayerdata = MEM_malloc_arrayN(totelem, typeInfo->size, layerType_getName(type));
+ if (typeInfo->construct) {
+ typeInfo->construct(newlayerdata, totelem);
+ }
+ }
+ break;
+ case CD_ASSIGN:
+ if (totelem > 0) {
+ BLI_assert(layerdata != nullptr);
+ newlayerdata = layerdata;
}
else {
- memcpy(newlayerdata, layerdata, (size_t)totelem * typeInfo->size);
+ MEM_SAFE_FREE(layerdata);
}
- }
- }
- else if (alloctype == CD_DEFAULT) {
- if (typeInfo->set_default) {
- typeInfo->set_default(newlayerdata, totelem);
- }
- }
- else if (alloctype == CD_REFERENCE) {
- flag |= CD_FLAG_NOFREE;
+ break;
+ case CD_REFERENCE:
+ if (totelem > 0) {
+ BLI_assert(layerdata != nullptr);
+ newlayerdata = layerdata;
+ flag |= CD_FLAG_NOFREE;
+ }
+ break;
+ case CD_DUPLICATE:
+ if (totelem > 0) {
+ newlayerdata = MEM_malloc_arrayN(totelem, typeInfo->size, layerType_getName(type));
+ if (typeInfo->copy) {
+ typeInfo->copy(layerdata, newlayerdata, totelem);
+ }
+ else {
+ BLI_assert(layerdata != nullptr);
+ BLI_assert(newlayerdata != nullptr);
+ memcpy(newlayerdata, layerdata, totelem * typeInfo->size);
+ }
+ }
+ break;
}
+ int index = data->totlayer;
if (index >= data->maxlayer) {
if (!customData_resize(data, CUSTOMDATA_GROW)) {
if (newlayerdata != layerdata) {
@@ -2754,14 +2883,16 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data,
data->layers[index] = data->layers[index - 1];
}
+ CustomDataLayer &new_layer = data->layers[index];
+
/* Clear remaining data on the layer. The original data on the layer has been moved to another
* index. Without this, it can happen that information from the previous layer at that index
* leaks into the new layer. */
- memset(data->layers + index, 0, sizeof(CustomDataLayer));
+ memset(&new_layer, 0, sizeof(CustomDataLayer));
- data->layers[index].type = type;
- data->layers[index].flag = flag;
- data->layers[index].data = newlayerdata;
+ new_layer.type = type;
+ new_layer.flag = flag;
+ new_layer.data = newlayerdata;
/* Set default name if none exists. Note we only call DATA_() once
* we know there is a default name, to avoid overhead of locale lookups
@@ -2771,24 +2902,24 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data,
}
if (name) {
- BLI_strncpy(data->layers[index].name, name, sizeof(data->layers[index].name));
+ BLI_strncpy(new_layer.name, name, sizeof(new_layer.name));
CustomData_set_layer_unique_name(data, index);
}
else {
- data->layers[index].name[0] = '\0';
+ new_layer.name[0] = '\0';
}
if (index > 0 && data->layers[index - 1].type == type) {
- data->layers[index].active = data->layers[index - 1].active;
- data->layers[index].active_rnd = data->layers[index - 1].active_rnd;
- data->layers[index].active_clone = data->layers[index - 1].active_clone;
- data->layers[index].active_mask = data->layers[index - 1].active_mask;
+ new_layer.active = data->layers[index - 1].active;
+ new_layer.active_rnd = data->layers[index - 1].active_rnd;
+ new_layer.active_clone = data->layers[index - 1].active_clone;
+ new_layer.active_mask = data->layers[index - 1].active_mask;
}
else {
- data->layers[index].active = 0;
- data->layers[index].active_rnd = 0;
- data->layers[index].active_clone = 0;
- data->layers[index].active_mask = 0;
+ new_layer.active = 0;
+ new_layer.active_rnd = 0;
+ new_layer.active_clone = 0;
+ new_layer.active_mask = 0;
}
customData_update_offsets(data);
@@ -2797,7 +2928,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data,
}
void *CustomData_add_layer(
- CustomData *data, int type, eCDAllocType alloctype, void *layerdata, int totelem)
+ CustomData *data, const int type, eCDAllocType alloctype, void *layerdata, const int totelem)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
@@ -2813,10 +2944,10 @@ void *CustomData_add_layer(
}
void *CustomData_add_layer_named(CustomData *data,
- int type,
- eCDAllocType alloctype,
+ const int type,
+ const eCDAllocType alloctype,
void *layerdata,
- int totelem,
+ const int totelem,
const char *name)
{
CustomDataLayer *layer = customData_add_layer__internal(
@@ -2831,10 +2962,10 @@ void *CustomData_add_layer_named(CustomData *data,
}
void *CustomData_add_layer_anonymous(CustomData *data,
- int type,
- eCDAllocType alloctype,
+ const int type,
+ const eCDAllocType alloctype,
void *layerdata,
- int totelem,
+ const int totelem,
const AnonymousAttributeID *anonymous_id)
{
const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id);
@@ -2851,7 +2982,7 @@ void *CustomData_add_layer_anonymous(CustomData *data,
return layer->data;
}
-bool CustomData_free_layer(CustomData *data, int type, int totelem, int index)
+bool CustomData_free_layer(CustomData *data, const int type, const int totelem, const int index)
{
const int index_first = CustomData_get_layer_index(data, type);
const int n = index - index_first;
@@ -2915,7 +3046,7 @@ bool CustomData_free_layer_named(CustomData *data, const char *name, const int t
return false;
}
-bool CustomData_free_layer_active(CustomData *data, int type, int totelem)
+bool CustomData_free_layer_active(CustomData *data, const int type, const int totelem)
{
const int index = CustomData_get_active_layer_index(data, type);
if (index == -1) {
@@ -2924,7 +3055,7 @@ bool CustomData_free_layer_active(CustomData *data, int type, int totelem)
return CustomData_free_layer(data, type, totelem, index);
}
-void CustomData_free_layers(CustomData *data, int type, int totelem)
+void CustomData_free_layers(CustomData *data, const int type, const int totelem)
{
const int index = CustomData_get_layer_index(data, type);
while (CustomData_free_layer(data, type, totelem, index)) {
@@ -2932,12 +3063,12 @@ void CustomData_free_layers(CustomData *data, int type, int totelem)
}
}
-bool CustomData_has_layer(const CustomData *data, int type)
+bool CustomData_has_layer(const CustomData *data, const int type)
{
return (CustomData_get_layer_index(data, type) != -1);
}
-int CustomData_number_of_layers(const CustomData *data, int type)
+int CustomData_number_of_layers(const CustomData *data, const int type)
{
int number = 0;
@@ -2950,7 +3081,7 @@ int CustomData_number_of_layers(const CustomData *data, int type)
return number;
}
-int CustomData_number_of_layers_typemask(const CustomData *data, eCustomDataMask mask)
+int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDataMask mask)
{
int number = 0;
@@ -3040,7 +3171,7 @@ void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data,
return nullptr;
}
-void CustomData_duplicate_referenced_layers(CustomData *data, int totelem)
+void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem)
{
for (int i = 0; i < data->totlayer; i++) {
CustomDataLayer *layer = &data->layers[i];
@@ -3048,7 +3179,7 @@ void CustomData_duplicate_referenced_layers(CustomData *data, int totelem)
}
}
-bool CustomData_is_referenced_layer(CustomData *data, int type)
+bool CustomData_is_referenced_layer(CustomData *data, const int type)
{
/* get the layer index of the first layer of type */
int layer_index = CustomData_get_active_layer_index(data, type);
@@ -3061,7 +3192,7 @@ bool CustomData_is_referenced_layer(CustomData *data, int type)
return (layer->flag & CD_FLAG_NOFREE) != 0;
}
-void CustomData_free_temporary(CustomData *data, int totelem)
+void CustomData_free_temporary(CustomData *data, const int totelem)
{
int i, j;
bool changed = false;
@@ -3093,7 +3224,7 @@ void CustomData_free_temporary(CustomData *data, int totelem)
}
}
-void CustomData_set_only_copy(const CustomData *data, eCustomDataMask mask)
+void CustomData_set_only_copy(const CustomData *data, const eCustomDataMask mask)
{
for (int i = 0; i < data->totlayer; i++) {
if (!(mask & CD_TYPE_AS_MASK(data->layers[i].type))) {
@@ -3102,7 +3233,10 @@ void CustomData_set_only_copy(const CustomData *data, eCustomDataMask mask)
}
}
-void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs, int count)
+void CustomData_copy_elements(const int type,
+ void *src_data_ofs,
+ void *dst_data_ofs,
+ const int count)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
@@ -3116,11 +3250,11 @@ void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs,
void CustomData_copy_data_layer(const CustomData *source,
CustomData *dest,
- int src_layer_index,
- int dst_layer_index,
- int src_index,
- int dst_index,
- int count)
+ const int src_layer_index,
+ const int dst_layer_index,
+ const int src_index,
+ const int dst_index,
+ const int count)
{
const LayerTypeInfo *typeInfo;
@@ -3154,8 +3288,11 @@ void CustomData_copy_data_layer(const CustomData *source,
}
}
-void CustomData_copy_data_named(
- const CustomData *source, CustomData *dest, int source_index, int dest_index, int count)
+void CustomData_copy_data_named(const CustomData *source,
+ CustomData *dest,
+ const int source_index,
+ const int dest_index,
+ const int count)
{
/* copies a layer at a time */
for (int src_i = 0; src_i < source->totlayer; src_i++) {
@@ -3170,8 +3307,11 @@ void CustomData_copy_data_named(
}
}
-void CustomData_copy_data(
- const CustomData *source, CustomData *dest, int source_index, int dest_index, int count)
+void CustomData_copy_data(const CustomData *source,
+ CustomData *dest,
+ const int source_index,
+ const int dest_index,
+ const int count)
{
/* copies a layer at a time */
int dest_i = 0;
@@ -3226,7 +3366,7 @@ void CustomData_copy_layer_type_data(const CustomData *source,
count);
}
-void CustomData_free_elem(CustomData *data, int index, int count)
+void CustomData_free_elem(CustomData *data, const int index, const int count)
{
for (int i = 0; i < data->totlayer; i++) {
if (!(data->layers[i].flag & CD_FLAG_NOFREE)) {
@@ -3326,7 +3466,7 @@ void CustomData_interp(const CustomData *source,
}
}
-void CustomData_swap_corners(CustomData *data, int index, const int *corner_indices)
+void CustomData_swap_corners(CustomData *data, const int index, const int *corner_indices)
{
for (int i = 0; i < data->totlayer; i++) {
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type);
@@ -3366,7 +3506,7 @@ void CustomData_swap(CustomData *data, const int index_a, const int index_b)
}
}
-void *CustomData_get(const CustomData *data, int index, int type)
+void *CustomData_get(const CustomData *data, const int index, const int type)
{
BLI_assert(index >= 0);
@@ -3382,7 +3522,7 @@ void *CustomData_get(const CustomData *data, int index, int type)
return POINTER_OFFSET(data->layers[layer_index].data, offset);
}
-void *CustomData_get_n(const CustomData *data, int type, int index, int n)
+void *CustomData_get_n(const CustomData *data, const int type, const int index, const int n)
{
BLI_assert(index >= 0 && n >= 0);
@@ -3396,7 +3536,7 @@ void *CustomData_get_n(const CustomData *data, int type, int index, int n)
return POINTER_OFFSET(data->layers[layer_index + n].data, offset);
}
-void *CustomData_get_layer(const CustomData *data, int type)
+void *CustomData_get_layer(const CustomData *data, const int type)
{
/* get the layer index of the active layer of type */
int layer_index = CustomData_get_active_layer_index(data, type);
@@ -3407,7 +3547,7 @@ void *CustomData_get_layer(const CustomData *data, int type)
return data->layers[layer_index].data;
}
-void *CustomData_get_layer_n(const CustomData *data, int type, int n)
+void *CustomData_get_layer_n(const CustomData *data, const int type, const int n)
{
/* get the layer index of the active layer of type */
int layer_index = CustomData_get_layer_index_n(data, type, n);
@@ -3418,7 +3558,7 @@ void *CustomData_get_layer_n(const CustomData *data, int type, int n)
return data->layers[layer_index].data;
}
-void *CustomData_get_layer_named(const CustomData *data, int type, const char *name)
+void *CustomData_get_layer_named(const CustomData *data, const int type, const char *name)
{
int layer_index = CustomData_get_named_layer_index(data, type, name);
if (layer_index == -1) {
@@ -3428,7 +3568,7 @@ void *CustomData_get_layer_named(const CustomData *data, int type, const char *n
return data->layers[layer_index].data;
}
-int CustomData_get_offset(const CustomData *data, int type)
+int CustomData_get_offset(const CustomData *data, const int type)
{
/* get the layer index of the active layer of type */
int layer_index = CustomData_get_active_layer_index(data, type);
@@ -3439,7 +3579,7 @@ int CustomData_get_offset(const CustomData *data, int type)
return data->layers[layer_index].offset;
}
-int CustomData_get_n_offset(const CustomData *data, int type, int n)
+int CustomData_get_n_offset(const CustomData *data, const int type, const int n)
{
/* get the layer index of the active layer of type */
int layer_index = CustomData_get_layer_index_n(data, type, n);
@@ -3450,7 +3590,17 @@ int CustomData_get_n_offset(const CustomData *data, int type, int n)
return data->layers[layer_index].offset;
}
-bool CustomData_set_layer_name(const CustomData *data, int type, int n, const char *name)
+int CustomData_get_offset_named(const CustomData *data, int type, const char *name)
+{
+ int layer_index = CustomData_get_named_layer_index(data, type, name);
+ if (layer_index == -1) {
+ return -1;
+ }
+
+ return data->layers[layer_index].offset;
+}
+
+bool CustomData_set_layer_name(CustomData *data, const int type, const int n, const char *name)
{
/* get the layer index of the first layer of type */
const int layer_index = CustomData_get_layer_index_n(data, type, n);
@@ -3464,14 +3614,14 @@ bool CustomData_set_layer_name(const CustomData *data, int type, int n, const ch
return true;
}
-const char *CustomData_get_layer_name(const CustomData *data, int type, int n)
+const char *CustomData_get_layer_name(const CustomData *data, const int type, const int n)
{
const int layer_index = CustomData_get_layer_index_n(data, type, n);
return (layer_index == -1) ? nullptr : data->layers[layer_index].name;
}
-void *CustomData_set_layer(const CustomData *data, int type, void *ptr)
+void *CustomData_set_layer(const CustomData *data, const int type, void *ptr)
{
/* get the layer index of the first layer of type */
int layer_index = CustomData_get_active_layer_index(data, type);
@@ -3485,7 +3635,7 @@ void *CustomData_set_layer(const CustomData *data, int type, void *ptr)
return ptr;
}
-void *CustomData_set_layer_n(const CustomData *data, int type, int n, void *ptr)
+void *CustomData_set_layer_n(const CustomData *data, const int type, const int n, void *ptr)
{
/* get the layer index of the first layer of type */
int layer_index = CustomData_get_layer_index_n(data, type, n);
@@ -3498,7 +3648,7 @@ void *CustomData_set_layer_n(const CustomData *data, int type, int n, void *ptr)
return ptr;
}
-void CustomData_set(const CustomData *data, int index, int type, const void *source)
+void CustomData_set(const CustomData *data, const int index, const int type, const void *source)
{
void *dest = CustomData_get(data, index, type);
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
@@ -3550,7 +3700,7 @@ void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata)
}
}
-void CustomData_bmesh_init_pool(CustomData *data, int totelem, const char htype)
+void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char htype)
{
int chunksize;
@@ -3601,7 +3751,7 @@ bool CustomData_bmesh_merge(const CustomData *source,
destold.layers = static_cast<CustomDataLayer *>(MEM_dupallocN(destold.layers));
}
- if (CustomData_merge(source, dest, mask, alloctype, 0) == false) {
+ if (CustomData_merge_mesh_to_bmesh(source, dest, mask, alloctype, 0) == false) {
if (destold.layers) {
MEM_freeN(destold.layers);
}
@@ -3752,13 +3902,13 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data,
}
}
-static void CustomData_bmesh_set_default_n(CustomData *data, void **block, int n)
+static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const int n)
{
int offset = data->layers[n].offset;
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type);
- if (typeInfo->set_default) {
- typeInfo->set_default(POINTER_OFFSET(*block, offset), 1);
+ if (typeInfo->set_default_value) {
+ typeInfo->set_default_value(POINTER_OFFSET(*block, offset), 1);
}
else {
memset(POINTER_OFFSET(*block, offset), 0, typeInfo->size);
@@ -3847,7 +3997,7 @@ void CustomData_bmesh_copy_data(const CustomData *source,
CustomData_bmesh_copy_data_exclude_by_type(source, dest, src_block, dest_block, 0);
}
-void *CustomData_bmesh_get(const CustomData *data, void *block, int type)
+void *CustomData_bmesh_get(const CustomData *data, void *block, const int type)
{
/* get the layer index of the first layer of type */
int layer_index = CustomData_get_active_layer_index(data, type);
@@ -3858,7 +4008,7 @@ void *CustomData_bmesh_get(const CustomData *data, void *block, int type)
return POINTER_OFFSET(block, data->layers[layer_index].offset);
}
-void *CustomData_bmesh_get_n(const CustomData *data, void *block, int type, int n)
+void *CustomData_bmesh_get_n(const CustomData *data, void *block, const int type, const int n)
{
/* get the layer index of the first layer of type */
int layer_index = CustomData_get_layer_index(data, type);
@@ -3869,7 +4019,7 @@ void *CustomData_bmesh_get_n(const CustomData *data, void *block, int type, int
return POINTER_OFFSET(block, data->layers[layer_index + n].offset);
}
-void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n)
+void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, const int n)
{
if (n < 0 || n >= data->totlayer) {
return nullptr;
@@ -3878,7 +4028,7 @@ void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n)
return POINTER_OFFSET(block, data->layers[n].offset);
}
-bool CustomData_layer_has_math(const CustomData *data, int layer_n)
+bool CustomData_layer_has_math(const CustomData *data, const int layer_n)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type);
@@ -3890,7 +4040,7 @@ bool CustomData_layer_has_math(const CustomData *data, int layer_n)
return false;
}
-bool CustomData_layer_has_interp(const CustomData *data, int layer_n)
+bool CustomData_layer_has_interp(const CustomData *data, const int layer_n)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type);
@@ -4011,7 +4161,7 @@ void CustomData_data_dominmax(int type, const void *data, void *min, void *max)
}
}
-void CustomData_data_multiply(int type, void *data, float fac)
+void CustomData_data_multiply(int type, void *data, const float fac)
{
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
@@ -4029,7 +4179,7 @@ void CustomData_data_add(int type, void *data1, const void *data2)
}
}
-void CustomData_bmesh_set(const CustomData *data, void *block, int type, const void *source)
+void CustomData_bmesh_set(const CustomData *data, void *block, const int type, const void *source)
{
void *dest = CustomData_bmesh_get(data, block, type);
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
@@ -4046,7 +4196,8 @@ void CustomData_bmesh_set(const CustomData *data, void *block, int type, const v
}
}
-void CustomData_bmesh_set_n(CustomData *data, void *block, int type, int n, const void *source)
+void CustomData_bmesh_set_n(
+ CustomData *data, void *block, const int type, const int n, const void *source)
{
void *dest = CustomData_bmesh_get_n(data, block, type, n);
const LayerTypeInfo *typeInfo = layerType_getInfo(type);
@@ -4063,7 +4214,7 @@ void CustomData_bmesh_set_n(CustomData *data, void *block, int type, int n, cons
}
}
-void CustomData_bmesh_set_layer_n(CustomData *data, void *block, int n, const void *source)
+void CustomData_bmesh_set_layer_n(CustomData *data, void *block, const int n, const void *source)
{
void *dest = CustomData_bmesh_get_layer_n(data, block, n);
const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type);
@@ -4262,7 +4413,9 @@ void CustomData_file_write_info(int type, const char **r_struct_name, int *r_str
*r_struct_num = typeInfo->structnum;
}
-void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16> &layers_to_write)
+void CustomData_blend_write_prepare(CustomData &data,
+ Vector<CustomDataLayer, 16> &layers_to_write,
+ const Set<std::string> &skip_names)
{
for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) {
if (layer.flag & CD_FLAG_NOCOPY) {
@@ -4271,6 +4424,9 @@ void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16
if (layer.anonymous_id != nullptr) {
continue;
}
+ if (skip_names.contains(layer.name)) {
+ continue;
+ }
layers_to_write.append(layer);
}
data.totlayer = layers_to_write.size();
@@ -4317,7 +4473,7 @@ int CustomData_layertype_layers_max(const int type)
return typeInfo->layers_max();
}
-static bool cd_layer_find_dupe(CustomData *data, const char *name, int type, int index)
+static bool cd_layer_find_dupe(CustomData *data, const char *name, const int type, const int index)
{
/* see if there is a duplicate */
for (int i = 0; i < data->totlayer; i++) {
@@ -4352,7 +4508,7 @@ static bool customdata_unique_check(void *arg, const char *name)
return cd_layer_find_dupe(data_arg->data, name, data_arg->type, data_arg->index);
}
-void CustomData_set_layer_unique_name(CustomData *data, int index)
+void CustomData_set_layer_unique_name(CustomData *data, const int index)
{
CustomDataLayer *nlayer = &data->layers[index];
const LayerTypeInfo *typeInfo = layerType_getInfo(nlayer->type);
@@ -4397,7 +4553,7 @@ void CustomData_validate_layer_name(const CustomData *data,
}
}
-bool CustomData_verify_versions(CustomData *data, int index)
+bool CustomData_verify_versions(CustomData *data, const int index)
{
const LayerTypeInfo *typeInfo;
CustomDataLayer *layer = &data->layers[index];
@@ -4443,9 +4599,52 @@ bool CustomData_verify_versions(CustomData *data, int index)
return keeplayer;
}
+static bool CustomData_layer_ensure_data_exists(CustomDataLayer *layer, size_t count)
+{
+ BLI_assert(layer);
+ const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
+ BLI_assert(typeInfo);
+
+ if (layer->data || count == 0) {
+ return false;
+ }
+
+ switch (layer->type) {
+ /* When more instances of corrupt files are found, add them here. */
+ case CD_PROP_BOOL: /* See T84935. */
+ case CD_MLOOPUV: /* See T90620. */
+ layer->data = MEM_calloc_arrayN(count, typeInfo->size, layerType_getName(layer->type));
+ BLI_assert(layer->data);
+ if (typeInfo->set_default_value) {
+ typeInfo->set_default_value(layer->data, count);
+ }
+ return true;
+ break;
+
+ case CD_MTEXPOLY:
+ /* TODO: Investigate multiple test failures on cycles, e.g. cycles_shadow_catcher_cpu. */
+ break;
+
+ default:
+ /* Log an error so we can collect instances of bad files. */
+ CLOG_WARN(&LOG, "CustomDataLayer->data is NULL for type %d.", layer->type);
+ break;
+ }
+ return false;
+}
+
bool CustomData_layer_validate(CustomDataLayer *layer, const uint totitems, const bool do_fixes)
{
+ BLI_assert(layer);
const LayerTypeInfo *typeInfo = layerType_getInfo(layer->type);
+ BLI_assert(typeInfo);
+
+ if (do_fixes) {
+ CustomData_layer_ensure_data_exists(layer, totitems);
+ }
+
+ BLI_assert((totitems == 0) || layer->data);
+ BLI_assert(MEM_allocN_len(layer->data) >= totitems * typeInfo->size);
if (typeInfo->validate != nullptr) {
return typeInfo->validate(layer->data, totitems, do_fixes);
@@ -4513,7 +4712,7 @@ void CustomData_external_reload(CustomData *data,
}
}
-void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, int totelem)
+void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, const int totelem)
{
CustomDataExternal *external = data->external;
CustomDataLayer *layer;
@@ -4587,7 +4786,7 @@ void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, in
}
void CustomData_external_write(
- CustomData *data, ID *id, eCustomDataMask mask, int totelem, int free)
+ CustomData *data, ID *id, eCustomDataMask mask, const int totelem, const int free)
{
CustomDataExternal *external = data->external;
int update = 0;
@@ -4689,8 +4888,11 @@ void CustomData_external_write(
cdf_free(cdf);
}
-void CustomData_external_add(
- CustomData *data, ID *UNUSED(id), int type, int UNUSED(totelem), const char *filepath)
+void CustomData_external_add(CustomData *data,
+ ID *UNUSED(id),
+ const int type,
+ const int UNUSED(totelem),
+ const char *filepath)
{
CustomDataExternal *external = data->external;
@@ -4714,7 +4916,7 @@ void CustomData_external_add(
layer->flag |= CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY;
}
-void CustomData_external_remove(CustomData *data, ID *id, int type, int totelem)
+void CustomData_external_remove(CustomData *data, ID *id, const int type, const int totelem)
{
CustomDataExternal *external = data->external;
@@ -4738,7 +4940,7 @@ void CustomData_external_remove(CustomData *data, ID *id, int type, int totelem)
}
}
-bool CustomData_external_test(CustomData *data, int type)
+bool CustomData_external_test(CustomData *data, const int type)
{
int layer_index = CustomData_get_active_layer_index(data, type);
if (layer_index == -1) {
@@ -4991,7 +5193,7 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
else {
const LayerTypeInfo *type_info = layerType_getInfo(data_type);
- /* NOTE: we can use 'fake' CDLayers, like e.g. for crease, bweight, etc. :/. */
+ /* NOTE: we can use 'fake' CDLayers for crease :/. */
data_size = (size_t)type_info->size;
data_step = laymap->elem_size ? laymap->elem_size : data_size;
data_offset = laymap->data_offset;
@@ -5039,7 +5241,10 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap,
/** \name Custom Data IO
* \{ */
-static void write_mdisps(BlendWriter *writer, int count, const MDisps *mdlist, int external)
+static void write_mdisps(BlendWriter *writer,
+ const int count,
+ const MDisps *mdlist,
+ const int external)
{
if (mdlist) {
BLO_write_struct_array(writer, MDisps, count, mdlist);
@@ -5139,7 +5344,10 @@ void CustomData_blend_write(BlendWriter *writer,
}
}
-static void blend_read_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external)
+static void blend_read_mdisps(BlendDataReader *reader,
+ const int count,
+ MDisps *mdisps,
+ const int external)
{
if (mdisps) {
for (int i = 0; i < count; i++) {
@@ -5181,7 +5389,7 @@ static void blend_read_paint_mask(BlendDataReader *reader,
}
}
-void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
+void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int count)
{
BLO_read_data_address(reader, &data->layers);
@@ -5206,16 +5414,15 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
if (CustomData_verify_versions(data, i)) {
BLO_read_data_address(reader, &layer->data);
- if (layer->data == nullptr && count > 0 && layer->type == CD_PROP_BOOL) {
- /* Usually this should never happen, except when a custom data layer has not been written
- * to a file correctly. */
- CLOG_WARN(&LOG, "Reallocating custom data layer that was not saved correctly.");
- const LayerTypeInfo *info = layerType_getInfo(layer->type);
- layer->data = MEM_calloc_arrayN((size_t)count, info->size, layerType_getName(layer->type));
- if (info->set_default) {
- info->set_default(layer->data, count);
- }
+ if (CustomData_layer_ensure_data_exists(layer, count)) {
+ /* Under normal operations, this shouldn't happen, but...
+ * For a CD_PROP_BOOL example, see T84935.
+ * For a CD_MLOOPUV example, see T90620. */
+ CLOG_WARN(&LOG,
+ "Allocated custom data layer that was not saved correctly for layer->type = %d.",
+ layer->type);
}
+
if (layer->type == CD_MDISPS) {
blend_read_mdisps(
reader, count, static_cast<MDisps *>(layer->data), layer->flag & CD_FLAG_EXTERNAL);
@@ -5223,6 +5430,9 @@ void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count)
else if (layer->type == CD_GRID_PAINT_MASK) {
blend_read_paint_mask(reader, count, static_cast<GridPaintMask *>(layer->data));
}
+ else if (layer->type == CD_MDEFORMVERT) {
+ BKE_defvert_blend_read(reader, count, static_cast<MDeformVert *>(layer->data));
+ }
i++;
}
}
@@ -5336,3 +5546,8 @@ eCustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type)
/** \} */
} // namespace blender::bke
+
+size_t CustomData_get_elem_size(CustomDataLayer *layer)
+{
+ return LAYERTYPEINFO[layer->type].size;
+}
diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c
index 196a6a00ade..6c7715c625e 100644
--- a/source/blender/blenkernel/intern/data_transfer.c
+++ b/source/blender/blenkernel/intern/data_transfer.c
@@ -70,8 +70,6 @@ void BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types,
r_data_masks->lmask |= CD_MASK_MLOOPUV;
}
else if (cddata_type == CD_FAKE_LNOR) {
- r_data_masks->vmask |= CD_MASK_NORMAL;
- r_data_masks->pmask |= CD_MASK_NORMAL;
r_data_masks->lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL;
}
}
@@ -194,7 +192,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type)
case DT_TYPE_SKIN:
return CD_MVERT_SKIN;
case DT_TYPE_BWEIGHT_VERT:
- return CD_FAKE_BWEIGHT;
+ return CD_BWEIGHT;
case DT_TYPE_SHARP_EDGE:
return CD_FAKE_SHARP;
@@ -203,7 +201,7 @@ int BKE_object_data_transfer_dttype_to_cdtype(const int dtdata_type)
case DT_TYPE_CREASE:
return CD_FAKE_CREASE;
case DT_TYPE_BWEIGHT_EDGE:
- return CD_FAKE_BWEIGHT;
+ return CD_BWEIGHT;
case DT_TYPE_FREESTYLE_EDGE:
return CD_FREESTYLE_EDGE;
@@ -258,13 +256,14 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src,
{
if (dtdata_type == DT_TYPE_LNOR) {
/* Compute custom normals into regular loop normals, which will be used for the transfer. */
- MVert *verts_dst = me_dst->mvert;
+
+ const MVert *verts_dst = BKE_mesh_verts(me_dst);
const int num_verts_dst = me_dst->totvert;
- MEdge *edges_dst = me_dst->medge;
+ const MEdge *edges_dst = BKE_mesh_edges(me_dst);
const int num_edges_dst = me_dst->totedge;
- MPoly *polys_dst = me_dst->mpoly;
+ const MPoly *polys_dst = BKE_mesh_polys(me_dst);
const int num_polys_dst = me_dst->totpoly;
- MLoop *loops_dst = me_dst->mloop;
+ const MLoop *loops_dst = BKE_mesh_loops(me_dst);
const int num_loops_dst = me_dst->totloop;
CustomData *ldata_dst = &me_dst->ldata;
@@ -282,7 +281,8 @@ static void data_transfer_dtdata_type_preprocess(Mesh *me_src,
loop_nors_dst = CustomData_get_layer(ldata_dst, CD_NORMAL);
const bool do_loop_nors_dst = (loop_nors_dst == NULL);
if (do_loop_nors_dst) {
- loop_nors_dst = CustomData_add_layer(ldata_dst, CD_NORMAL, CD_CALLOC, NULL, num_loops_dst);
+ loop_nors_dst = CustomData_add_layer(
+ ldata_dst, CD_NORMAL, CD_SET_DEFAULT, NULL, num_loops_dst);
CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY);
}
if (dirty_nors_dst || do_loop_nors_dst) {
@@ -319,13 +319,13 @@ static void data_transfer_dtdata_type_postprocess(Object *UNUSED(ob_src),
}
/* Bake edited destination loop normals into custom normals again. */
- MVert *verts_dst = me_dst->mvert;
+ const MVert *verts_dst = BKE_mesh_verts(me_dst);
const int num_verts_dst = me_dst->totvert;
- MEdge *edges_dst = me_dst->medge;
+ MEdge *edges_dst = BKE_mesh_edges_for_write(me_dst);
const int num_edges_dst = me_dst->totedge;
- MPoly *polys_dst = me_dst->mpoly;
+ MPoly *polys_dst = BKE_mesh_polys_for_write(me_dst);
const int num_polys_dst = me_dst->totpoly;
- MLoop *loops_dst = me_dst->mloop;
+ MLoop *loops_dst = BKE_mesh_loops_for_write(me_dst);
const int num_loops_dst = me_dst->totloop;
CustomData *ldata_dst = &me_dst->ldata;
@@ -335,7 +335,7 @@ static void data_transfer_dtdata_type_postprocess(Object *UNUSED(ob_src),
if (!custom_nors_dst) {
custom_nors_dst = CustomData_add_layer(
- ldata_dst, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, num_loops_dst);
+ ldata_dst, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, NULL, num_loops_dst);
}
/* Note loop_nors_dst contains our custom normals as transferred from source... */
@@ -563,7 +563,7 @@ static bool data_transfer_layersmapping_cdlayers_multisrc_to_dst(ListBase *r_map
if (use_create) {
/* Create as much data layers as necessary! */
for (; idx_dst < idx_src; idx_dst++) {
- CustomData_add_layer(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst);
+ CustomData_add_layer(cd_dst, cddata_type, CD_SET_DEFAULT, NULL, num_elem_dst);
}
}
else {
@@ -624,7 +624,8 @@ static bool data_transfer_layersmapping_cdlayers_multisrc_to_dst(ListBase *r_map
if ((idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name)) == -1) {
if (use_create) {
- CustomData_add_layer_named(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst, name);
+ CustomData_add_layer_named(
+ cd_dst, cddata_type, CD_SET_DEFAULT, NULL, num_elem_dst, name);
idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name);
}
else {
@@ -712,7 +713,7 @@ static bool data_transfer_layersmapping_cdlayers(ListBase *r_map,
if (!use_create) {
return true;
}
- data_dst = CustomData_add_layer(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst);
+ data_dst = CustomData_add_layer(cd_dst, cddata_type, CD_SET_DEFAULT, NULL, num_elem_dst);
}
else if (use_dupref_dst && r_map) {
/* If dest is a evaluated mesh (from modifier),
@@ -765,7 +766,7 @@ static bool data_transfer_layersmapping_cdlayers(ListBase *r_map,
if (!use_create) {
return true;
}
- data_dst = CustomData_add_layer(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst);
+ data_dst = CustomData_add_layer(cd_dst, cddata_type, CD_SET_DEFAULT, NULL, num_elem_dst);
}
else {
/* If dest is a evaluated mesh (from modifier),
@@ -788,7 +789,7 @@ static bool data_transfer_layersmapping_cdlayers(ListBase *r_map,
}
/* Create as much data layers as necessary! */
for (; num <= idx_dst; num++) {
- CustomData_add_layer(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst);
+ CustomData_add_layer(cd_dst, cddata_type, CD_SET_DEFAULT, NULL, num_elem_dst);
}
}
/* If dest is a evaluated mesh (from modifier),
@@ -807,7 +808,7 @@ static bool data_transfer_layersmapping_cdlayers(ListBase *r_map,
if (!use_create) {
return true;
}
- CustomData_add_layer_named(cd_dst, cddata_type, CD_CALLOC, NULL, num_elem_dst, name);
+ CustomData_add_layer_named(cd_dst, cddata_type, CD_SET_DEFAULT, NULL, num_elem_dst, name);
idx_dst = CustomData_get_named_layer(cd_dst, cddata_type, name);
}
/* If dest is a evaluated mesh (from modifier),
@@ -927,38 +928,6 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map,
}
return true;
}
- if (cddata_type == CD_FAKE_BWEIGHT) {
- const size_t elem_size = sizeof(*((MVert *)NULL));
- const size_t data_size = sizeof(((MVert *)NULL)->bweight);
- const size_t data_offset = offsetof(MVert, bweight);
- const uint64_t data_flag = 0;
-
- if (!(me_src->cd_flag & ME_CDFLAG_VERT_BWEIGHT)) {
- if (use_delete) {
- me_dst->cd_flag &= ~ME_CDFLAG_VERT_BWEIGHT;
- }
- return true;
- }
- me_dst->cd_flag |= ME_CDFLAG_VERT_BWEIGHT;
- if (r_map) {
- data_transfer_layersmapping_add_item(r_map,
- cddata_type,
- mix_mode,
- mix_factor,
- mix_weights,
- me_src->mvert,
- me_dst->mvert,
- me_src->totvert,
- me_dst->totvert,
- elem_size,
- data_size,
- data_offset,
- data_flag,
- data_transfer_interp_char,
- interp_data);
- }
- return true;
- }
if (cddata_type == CD_FAKE_MDEFORMVERT) {
bool ret;
@@ -979,9 +948,6 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map,
me_dst != ob_dst->data,
fromlayers,
tolayers);
-
- /* Mesh stores its dvert in a specific pointer too. :( */
- me_dst->dvert = CustomData_get_layer(&me_dst->vdata, CD_MDEFORMVERT);
return ret;
}
if (cddata_type == CD_FAKE_SHAPEKEY) {
@@ -1034,8 +1000,8 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map,
mix_mode,
mix_factor,
mix_weights,
- me_src->medge,
- me_dst->medge,
+ BKE_mesh_edges(me_src),
+ BKE_mesh_edges_for_write(me_dst),
me_src->totedge,
me_dst->totedge,
elem_size,
@@ -1047,38 +1013,7 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map,
}
return true;
}
- if (cddata_type == CD_FAKE_BWEIGHT) {
- const size_t elem_size = sizeof(*((MEdge *)NULL));
- const size_t data_size = sizeof(((MEdge *)NULL)->bweight);
- const size_t data_offset = offsetof(MEdge, bweight);
- const uint64_t data_flag = 0;
- if (!(me_src->cd_flag & ME_CDFLAG_EDGE_BWEIGHT)) {
- if (use_delete) {
- me_dst->cd_flag &= ~ME_CDFLAG_EDGE_BWEIGHT;
- }
- return true;
- }
- me_dst->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
- if (r_map) {
- data_transfer_layersmapping_add_item(r_map,
- cddata_type,
- mix_mode,
- mix_factor,
- mix_weights,
- me_src->medge,
- me_dst->medge,
- me_src->totedge,
- me_dst->totedge,
- elem_size,
- data_size,
- data_offset,
- data_flag,
- data_transfer_interp_char,
- interp_data);
- }
- return true;
- }
if (r_map && ELEM(cddata_type, CD_FAKE_SHARP, CD_FAKE_SEAM)) {
const size_t elem_size = sizeof(*((MEdge *)NULL));
const size_t data_size = sizeof(((MEdge *)NULL)->flag);
@@ -1090,8 +1025,8 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map,
mix_mode,
mix_factor,
mix_weights,
- me_src->medge,
- me_dst->medge,
+ BKE_mesh_edges(me_src),
+ BKE_mesh_edges_for_write(me_dst),
me_src->totedge,
me_dst->totedge,
elem_size,
@@ -1184,8 +1119,8 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map,
mix_mode,
mix_factor,
mix_weights,
- me_src->mpoly,
- me_dst->mpoly,
+ BKE_mesh_polys(me_src),
+ BKE_mesh_polys_for_write(me_dst),
me_src->totpoly,
me_dst->totpoly,
elem_size,
@@ -1410,7 +1345,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
BKE_mesh_remap_calc_source_cddata_masks_from_map_modes(
map_vert_mode, map_edge_mode, map_loop_mode, map_poly_mode, &me_src_mask);
if (is_modifier) {
- me_src = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_src, false);
+ me_src = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_src);
if (me_src == NULL ||
!CustomData_MeshMasks_are_matching(&ob_src->runtime.last_data_mask, &me_src_mask)) {
@@ -1432,7 +1367,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
}
BKE_mesh_remap_find_best_match_from_mesh(
- me_dst->mvert, me_dst->totvert, me_src, space_transform);
+ BKE_mesh_verts(me_dst), me_dst->totvert, me_src, space_transform);
}
/* Check all possible data types.
@@ -1460,7 +1395,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
}
if (DT_DATATYPE_IS_VERT(dtdata_type)) {
- MVert *verts_dst = me_dst->mvert;
+ MVert *verts_dst = BKE_mesh_verts_for_write(me_dst);
const int num_verts_dst = me_dst->totvert;
if (!geom_map_init[VDATA]) {
@@ -1542,9 +1477,9 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
}
}
if (DT_DATATYPE_IS_EDGE(dtdata_type)) {
- MVert *verts_dst = me_dst->mvert;
+ const MVert *verts_dst = BKE_mesh_verts_for_write(me_dst);
const int num_verts_dst = me_dst->totvert;
- MEdge *edges_dst = me_dst->medge;
+ const MEdge *edges_dst = BKE_mesh_edges(me_dst);
const int num_edges_dst = me_dst->totedge;
if (!geom_map_init[EDATA]) {
@@ -1621,13 +1556,13 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
}
}
if (DT_DATATYPE_IS_LOOP(dtdata_type)) {
- MVert *verts_dst = me_dst->mvert;
+ const MVert *verts_dst = BKE_mesh_verts(me_dst);
const int num_verts_dst = me_dst->totvert;
- MEdge *edges_dst = me_dst->medge;
+ const MEdge *edges_dst = BKE_mesh_edges(me_dst);
const int num_edges_dst = me_dst->totedge;
- MPoly *polys_dst = me_dst->mpoly;
+ const MPoly *polys_dst = BKE_mesh_polys(me_dst);
const int num_polys_dst = me_dst->totpoly;
- MLoop *loops_dst = me_dst->mloop;
+ const MLoop *loops_dst = BKE_mesh_loops(me_dst);
const int num_loops_dst = me_dst->totloop;
CustomData *ldata_dst = &me_dst->ldata;
@@ -1716,11 +1651,11 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
}
}
if (DT_DATATYPE_IS_POLY(dtdata_type)) {
- MVert *verts_dst = me_dst->mvert;
+ const MVert *verts_dst = BKE_mesh_verts(me_dst);
const int num_verts_dst = me_dst->totvert;
- MPoly *polys_dst = me_dst->mpoly;
+ const MPoly *polys_dst = BKE_mesh_polys(me_dst);
const int num_polys_dst = me_dst->totpoly;
- MLoop *loops_dst = me_dst->mloop;
+ const MLoop *loops_dst = BKE_mesh_loops(me_dst);
const int num_loops_dst = me_dst->totloop;
if (!geom_map_init[PDATA]) {
diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c
index ebe06fa85eb..7940d65b1bb 100644
--- a/source/blender/blenkernel/intern/deform.c
+++ b/source/blender/blenkernel/intern/deform.c
@@ -94,10 +94,10 @@ bDeformGroup *BKE_defgroup_duplicate(const bDeformGroup *ingroup)
void BKE_defvert_copy_subset(MDeformVert *dvert_dst,
const MDeformVert *dvert_src,
const bool *vgroup_subset,
- const int vgroup_tot)
+ const int vgroup_num)
{
int defgroup;
- for (defgroup = 0; defgroup < vgroup_tot; defgroup++) {
+ for (defgroup = 0; defgroup < vgroup_num; defgroup++) {
if (vgroup_subset[defgroup]) {
BKE_defvert_copy_index(dvert_dst, defgroup, dvert_src, defgroup);
}
@@ -107,12 +107,12 @@ void BKE_defvert_copy_subset(MDeformVert *dvert_dst,
void BKE_defvert_mirror_subset(MDeformVert *dvert_dst,
const MDeformVert *dvert_src,
const bool *vgroup_subset,
- const int vgroup_tot,
+ const int vgroup_num,
const int *flip_map,
- const int flip_map_len)
+ const int flip_map_num)
{
int defgroup;
- for (defgroup = 0; defgroup < vgroup_tot && defgroup < flip_map_len; defgroup++) {
+ for (defgroup = 0; defgroup < vgroup_num && defgroup < flip_map_num; defgroup++) {
if (vgroup_subset[defgroup] && (dvert_dst != dvert_src || flip_map[defgroup] != defgroup)) {
BKE_defvert_copy_index(dvert_dst, flip_map[defgroup], dvert_src, defgroup);
}
@@ -189,13 +189,13 @@ void BKE_defvert_sync(MDeformVert *dvert_dst, const MDeformVert *dvert_src, cons
void BKE_defvert_sync_mapped(MDeformVert *dvert_dst,
const MDeformVert *dvert_src,
const int *flip_map,
- const int flip_map_len,
+ const int flip_map_num,
const bool use_ensure)
{
if (dvert_src->totweight && dvert_dst->totweight) {
MDeformWeight *dw_src = dvert_src->dw;
for (int i = 0; i < dvert_src->totweight; i++, dw_src++) {
- if (dw_src->def_nr < flip_map_len) {
+ if (dw_src->def_nr < flip_map_num) {
MDeformWeight *dw_dst;
if (use_ensure) {
dw_dst = BKE_defvert_ensure_index(dvert_dst, flip_map[dw_src->def_nr]);
@@ -226,14 +226,14 @@ void BKE_defvert_remap(MDeformVert *dvert, const int *map, const int map_len)
void BKE_defvert_normalize_subset(MDeformVert *dvert,
const bool *vgroup_subset,
- const int vgroup_tot)
+ const int vgroup_num)
{
if (dvert->totweight == 0) {
/* nothing */
}
else if (dvert->totweight == 1) {
MDeformWeight *dw = dvert->dw;
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
dw->weight = 1.0f;
}
}
@@ -241,7 +241,7 @@ void BKE_defvert_normalize_subset(MDeformVert *dvert,
MDeformWeight *dw = dvert->dw;
float tot_weight = 0.0f;
for (int i = dvert->totweight; i != 0; i--, dw++) {
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
tot_weight += dw->weight;
}
}
@@ -250,7 +250,7 @@ void BKE_defvert_normalize_subset(MDeformVert *dvert,
float scalar = 1.0f / tot_weight;
dw = dvert->dw;
for (int i = dvert->totweight; i != 0; i--, dw++) {
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
dw->weight *= scalar;
/* in case of division errors with very low weights */
@@ -292,7 +292,7 @@ void BKE_defvert_normalize(MDeformVert *dvert)
void BKE_defvert_normalize_lock_single(MDeformVert *dvert,
const bool *vgroup_subset,
- const int vgroup_tot,
+ const int vgroup_num,
const uint def_nr_lock)
{
if (dvert->totweight == 0) {
@@ -300,7 +300,7 @@ void BKE_defvert_normalize_lock_single(MDeformVert *dvert,
}
else if (dvert->totweight == 1) {
MDeformWeight *dw = dvert->dw;
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
if (def_nr_lock != dw->def_nr) {
dw->weight = 1.0f;
}
@@ -314,7 +314,7 @@ void BKE_defvert_normalize_lock_single(MDeformVert *dvert,
float lock_iweight = 1.0f;
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
if (dw->def_nr != def_nr_lock) {
tot_weight += dw->weight;
}
@@ -331,7 +331,7 @@ void BKE_defvert_normalize_lock_single(MDeformVert *dvert,
float scalar = (1.0f / tot_weight) * lock_iweight;
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
if (dw != dw_lock) {
dw->weight *= scalar;
@@ -346,17 +346,17 @@ void BKE_defvert_normalize_lock_single(MDeformVert *dvert,
void BKE_defvert_normalize_lock_map(MDeformVert *dvert,
const bool *vgroup_subset,
- const int vgroup_tot,
+ const int vgroup_num,
const bool *lock_flags,
- const int defbase_tot)
+ const int defbase_num)
{
if (dvert->totweight == 0) {
/* nothing */
}
else if (dvert->totweight == 1) {
MDeformWeight *dw = dvert->dw;
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
- if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < defbase_num) && (lock_flags[dw->def_nr] == false)) {
dw->weight = 1.0f;
}
}
@@ -368,8 +368,8 @@ void BKE_defvert_normalize_lock_map(MDeformVert *dvert,
float lock_iweight = 0.0f;
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
- if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < defbase_num) && (lock_flags[dw->def_nr] == false)) {
tot_weight += dw->weight;
}
else {
@@ -386,8 +386,8 @@ void BKE_defvert_normalize_lock_map(MDeformVert *dvert,
float scalar = (1.0f / tot_weight) * lock_iweight;
for (i = dvert->totweight, dw = dvert->dw; i != 0; i--, dw++) {
- if ((dw->def_nr < vgroup_tot) && vgroup_subset[dw->def_nr]) {
- if ((dw->def_nr < defbase_tot) && (lock_flags[dw->def_nr] == false)) {
+ if ((dw->def_nr < vgroup_num) && vgroup_subset[dw->def_nr]) {
+ if ((dw->def_nr < defbase_num) && (lock_flags[dw->def_nr] == false)) {
dw->weight *= scalar;
/* in case of division errors with very low weights */
@@ -399,13 +399,13 @@ void BKE_defvert_normalize_lock_map(MDeformVert *dvert,
}
}
-void BKE_defvert_flip(MDeformVert *dvert, const int *flip_map, const int flip_map_len)
+void BKE_defvert_flip(MDeformVert *dvert, const int *flip_map, const int flip_map_num)
{
MDeformWeight *dw;
int i;
for (dw = dvert->dw, i = 0; i < dvert->totweight; dw++, i++) {
- if (dw->def_nr < flip_map_len) {
+ if (dw->def_nr < flip_map_num) {
if (flip_map[dw->def_nr] >= 0) {
dw->def_nr = flip_map[dw->def_nr];
}
@@ -413,7 +413,7 @@ void BKE_defvert_flip(MDeformVert *dvert, const int *flip_map, const int flip_ma
}
}
-void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int flip_map_len)
+void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int flip_map_num)
{
MDeformWeight *dw, *dw_cpy;
float weight;
@@ -421,7 +421,7 @@ void BKE_defvert_flip_merged(MDeformVert *dvert, const int *flip_map, const int
/* copy weights */
for (dw = dvert->dw, i = 0; i < totweight; dw++, i++) {
- if (dw->def_nr < flip_map_len) {
+ if (dw->def_nr < flip_map_num) {
if (flip_map[dw->def_nr] >= 0) {
/* error checkers complain of this but we'll never get NULL return */
dw_cpy = BKE_defvert_ensure_index(dvert, flip_map[dw->def_nr]);
@@ -572,20 +572,25 @@ void BKE_object_defgroup_active_index_set(Object *ob, const int new_index)
*index = new_index;
}
-int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const bool use_default)
+static int *object_defgroup_unlocked_flip_map_ex(const Object *ob,
+ const bool use_default,
+ const bool use_only_unlocked,
+ int *r_flip_map_num)
{
const ListBase *defbase = BKE_object_defgroup_list(ob);
- int defbase_tot = *flip_map_len = BLI_listbase_count(defbase);
+ const int defbase_num = BLI_listbase_count(defbase);
+ *r_flip_map_num = defbase_num;
- if (defbase_tot == 0) {
+ if (defbase_num == 0) {
return NULL;
}
bDeformGroup *dg;
char name_flip[sizeof(dg->name)];
- int i, flip_num, *map = MEM_mallocN(defbase_tot * sizeof(int), __func__);
+ int i, flip_num;
+ int *map = MEM_mallocN(defbase_num * sizeof(int), __func__);
- for (i = 0; i < defbase_tot; i++) {
+ for (i = 0; i < defbase_num; i++) {
map[i] = -1;
}
@@ -597,11 +602,15 @@ int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const boo
map[i] = i;
}
+ if (use_only_unlocked && (dg->flag & DG_LOCK_WEIGHT)) {
+ continue;
+ }
+
BLI_string_flip_side_name(name_flip, dg->name, false, sizeof(name_flip));
if (!STREQ(name_flip, dg->name)) {
flip_num = BKE_object_defgroup_name_index(ob, name_flip);
- if (flip_num >= 0) {
+ if (flip_num != -1) {
map[i] = flip_num;
map[flip_num] = i; /* save an extra lookup */
}
@@ -611,23 +620,36 @@ int *BKE_object_defgroup_flip_map(const Object *ob, int *flip_map_len, const boo
return map;
}
+int *BKE_object_defgroup_flip_map(const Object *ob, const bool use_default, int *r_flip_map_num)
+{
+ return object_defgroup_unlocked_flip_map_ex(ob, use_default, false, r_flip_map_num);
+}
+
+int *BKE_object_defgroup_flip_map_unlocked(const Object *ob,
+ const bool use_default,
+ int *r_flip_map_num)
+{
+ return object_defgroup_unlocked_flip_map_ex(ob, use_default, true, r_flip_map_num);
+}
+
int *BKE_object_defgroup_flip_map_single(const Object *ob,
- int *flip_map_len,
const bool use_default,
- int defgroup)
+ const int defgroup,
+ int *r_flip_map_num)
{
const ListBase *defbase = BKE_object_defgroup_list(ob);
- int defbase_tot = *flip_map_len = BLI_listbase_count(defbase);
+ const int defbase_num = BLI_listbase_count(defbase);
+ *r_flip_map_num = defbase_num;
- if (defbase_tot == 0) {
+ if (defbase_num == 0) {
return NULL;
}
bDeformGroup *dg;
char name_flip[sizeof(dg->name)];
- int i, flip_num, *map = MEM_mallocN(defbase_tot * sizeof(int), __func__);
+ int i, flip_num, *map = MEM_mallocN(defbase_num * sizeof(int), __func__);
- for (i = 0; i < defbase_tot; i++) {
+ for (i = 0; i < defbase_num; i++) {
map[i] = use_default ? i : -1;
}
@@ -776,7 +798,7 @@ MDeformWeight *BKE_defvert_ensure_index(MDeformVert *dvert, const int defgroup)
return dw_new;
}
-void BKE_defvert_add_index_notest(MDeformVert *dvert, int defgroup, const float weight)
+void BKE_defvert_add_index_notest(MDeformVert *dvert, const int defgroup, const float weight)
{
/* TODO: merge with #BKE_defvert_ensure_index! */
@@ -870,7 +892,7 @@ bool BKE_defvert_is_weight_zero(const struct MDeformVert *dvert, const int defgr
}
float BKE_defvert_total_selected_weight(const struct MDeformVert *dv,
- int defbase_tot,
+ int defbase_num,
const bool *defbase_sel)
{
float total = 0.0f;
@@ -881,7 +903,7 @@ float BKE_defvert_total_selected_weight(const struct MDeformVert *dv,
}
for (int i = dv->totweight; i != 0; i--, dw++) {
- if (dw->def_nr < defbase_tot) {
+ if (dw->def_nr < defbase_num) {
if (defbase_sel[dw->def_nr]) {
total += dw->weight;
}
@@ -892,17 +914,17 @@ float BKE_defvert_total_selected_weight(const struct MDeformVert *dv,
}
float BKE_defvert_multipaint_collective_weight(const struct MDeformVert *dv,
- int defbase_tot,
+ const int defbase_num,
const bool *defbase_sel,
- int defbase_tot_sel,
- bool is_normalized)
+ const int defbase_sel_num,
+ const bool is_normalized)
{
- float total = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_sel);
+ float total = BKE_defvert_total_selected_weight(dv, defbase_num, defbase_sel);
/* in multipaint, get the average if auto normalize is inactive
* get the sum if it is active */
if (!is_normalized) {
- total /= defbase_tot_sel;
+ total /= defbase_sel_num;
}
return total;
@@ -936,19 +958,19 @@ float BKE_defvert_calc_lock_relative_weight(float weight,
return weight / (1.0f - locked_weight);
}
-float BKE_defvert_lock_relative_weight(float weight,
+float BKE_defvert_lock_relative_weight(const float weight,
const struct MDeformVert *dv,
- int defbase_tot,
+ const int defbase_num,
const bool *defbase_locked,
const bool *defbase_unlocked)
{
- float unlocked = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_unlocked);
+ float unlocked = BKE_defvert_total_selected_weight(dv, defbase_num, defbase_unlocked);
if (unlocked > 0.0f) {
return weight / unlocked;
}
- float locked = BKE_defvert_total_selected_weight(dv, defbase_tot, defbase_locked);
+ float locked = BKE_defvert_total_selected_weight(dv, defbase_num, defbase_locked);
return BKE_defvert_calc_lock_relative_weight(weight, locked, unlocked);
}
@@ -1010,12 +1032,12 @@ void BKE_defvert_array_free(MDeformVert *dvert, int totvert)
void BKE_defvert_extract_vgroup_to_vertweights(const MDeformVert *dvert,
const int defgroup,
- const int num_verts,
+ const int verts_num,
const bool invert_vgroup,
float *r_weights)
{
if (dvert && defgroup != -1) {
- int i = num_verts;
+ int i = verts_num;
while (i--) {
const float w = BKE_defvert_find_weight(&dvert[i], defgroup);
@@ -1023,27 +1045,27 @@ void BKE_defvert_extract_vgroup_to_vertweights(const MDeformVert *dvert,
}
}
else {
- copy_vn_fl(r_weights, num_verts, invert_vgroup ? 1.0f : 0.0f);
+ copy_vn_fl(r_weights, verts_num, invert_vgroup ? 1.0f : 0.0f);
}
}
void BKE_defvert_extract_vgroup_to_edgeweights(const MDeformVert *dvert,
const int defgroup,
- const int num_verts,
- MEdge *edges,
- const int num_edges,
+ const int verts_num,
+ const MEdge *edges,
+ const int edges_num,
const bool invert_vgroup,
float *r_weights)
{
if (dvert && defgroup != -1) {
- int i = num_edges;
- float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);
+ int i = edges_num;
+ float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)verts_num, __func__);
BKE_defvert_extract_vgroup_to_vertweights(
- dvert, defgroup, num_verts, invert_vgroup, tmp_weights);
+ dvert, defgroup, verts_num, invert_vgroup, tmp_weights);
while (i--) {
- MEdge *me = &edges[i];
+ const MEdge *me = &edges[i];
r_weights[i] = (tmp_weights[me->v1] + tmp_weights[me->v2]) * 0.5f;
}
@@ -1051,27 +1073,27 @@ void BKE_defvert_extract_vgroup_to_edgeweights(const MDeformVert *dvert,
MEM_freeN(tmp_weights);
}
else {
- copy_vn_fl(r_weights, num_edges, 0.0f);
+ copy_vn_fl(r_weights, edges_num, 0.0f);
}
}
void BKE_defvert_extract_vgroup_to_loopweights(const MDeformVert *dvert,
const int defgroup,
- const int num_verts,
- MLoop *loops,
- const int num_loops,
+ const int verts_num,
+ const MLoop *loops,
+ const int loops_num,
const bool invert_vgroup,
float *r_weights)
{
if (dvert && defgroup != -1) {
- int i = num_loops;
- float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);
+ int i = loops_num;
+ float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)verts_num, __func__);
BKE_defvert_extract_vgroup_to_vertweights(
- dvert, defgroup, num_verts, invert_vgroup, tmp_weights);
+ dvert, defgroup, verts_num, invert_vgroup, tmp_weights);
while (i--) {
- MLoop *ml = &loops[i];
+ const MLoop *ml = &loops[i];
r_weights[i] = tmp_weights[ml->v];
}
@@ -1079,30 +1101,30 @@ void BKE_defvert_extract_vgroup_to_loopweights(const MDeformVert *dvert,
MEM_freeN(tmp_weights);
}
else {
- copy_vn_fl(r_weights, num_loops, 0.0f);
+ copy_vn_fl(r_weights, loops_num, 0.0f);
}
}
void BKE_defvert_extract_vgroup_to_polyweights(const MDeformVert *dvert,
const int defgroup,
- const int num_verts,
- MLoop *loops,
- const int UNUSED(num_loops),
- MPoly *polys,
- const int num_polys,
+ const int verts_num,
+ const MLoop *loops,
+ const int UNUSED(loops_num),
+ const MPoly *polys,
+ const int polys_num,
const bool invert_vgroup,
float *r_weights)
{
if (dvert && defgroup != -1) {
- int i = num_polys;
- float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)num_verts, __func__);
+ int i = polys_num;
+ float *tmp_weights = MEM_mallocN(sizeof(*tmp_weights) * (size_t)verts_num, __func__);
BKE_defvert_extract_vgroup_to_vertweights(
- dvert, defgroup, num_verts, invert_vgroup, tmp_weights);
+ dvert, defgroup, verts_num, invert_vgroup, tmp_weights);
while (i--) {
- MPoly *mp = &polys[i];
- MLoop *ml = &loops[mp->loopstart];
+ const MPoly *mp = &polys[i];
+ const MLoop *ml = &loops[mp->loopstart];
int j = mp->totloop;
float w = 0.0f;
@@ -1115,7 +1137,7 @@ void BKE_defvert_extract_vgroup_to_polyweights(const MDeformVert *dvert,
MEM_freeN(tmp_weights);
}
else {
- copy_vn_fl(r_weights, num_polys, 0.0f);
+ copy_vn_fl(r_weights, polys_num, 0.0f);
}
}
@@ -1207,7 +1229,7 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map,
const ListBase *src_list = BKE_object_defgroup_list(ob_src);
ListBase *dst_defbase = BKE_object_defgroup_list_mutable(ob_dst);
- int tot_dst = BLI_listbase_count(dst_defbase);
+ const int tot_dst = BLI_listbase_count(dst_defbase);
const size_t elem_size = sizeof(*((MDeformVert *)NULL));
@@ -1243,7 +1265,8 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map,
/* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest!
* Again, use_create is not relevant in this case */
if (!data_dst) {
- data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst);
+ data_dst = CustomData_add_layer(
+ cd_dst, CD_MDEFORMVERT, CD_SET_DEFAULT, NULL, num_elem_dst);
}
while (idx_src--) {
@@ -1303,7 +1326,8 @@ static bool data_transfer_layersmapping_vgroups_multisrc_to_dst(ListBase *r_map,
/* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest!
* use_create is not relevant in this case */
if (!data_dst) {
- data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst);
+ data_dst = CustomData_add_layer(
+ cd_dst, CD_MDEFORMVERT, CD_SET_DEFAULT, NULL, num_elem_dst);
}
data_transfer_layersmapping_add_item(r_map,
@@ -1442,7 +1466,8 @@ bool data_transfer_layersmapping_vgroups(ListBase *r_map,
/* At this stage, we **need** a valid CD_MDEFORMVERT layer on dest!
* use_create is not relevant in this case */
if (!data_dst) {
- data_dst = CustomData_add_layer(cd_dst, CD_MDEFORMVERT, CD_CALLOC, NULL, num_elem_dst);
+ data_dst = CustomData_add_layer(
+ cd_dst, CD_MDEFORMVERT, CD_SET_DEFAULT, NULL, num_elem_dst);
}
data_transfer_layersmapping_add_item(r_map,
diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc
index 823fce52b50..279166297ec 100644
--- a/source/blender/blenkernel/intern/displist.cc
+++ b/source/blender/blenkernel/intern/displist.cc
@@ -34,10 +34,8 @@
#include "BKE_displist.h"
#include "BKE_geometry_set.hh"
#include "BKE_key.h"
-#include "BKE_lattice.h"
#include "BKE_lib_id.h"
#include "BKE_mball.h"
-#include "BKE_mball_tessellate.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
@@ -86,133 +84,6 @@ DispList *BKE_displist_find(ListBase *lb, int type)
return nullptr;
}
-void BKE_displist_copy(ListBase *lbn, const ListBase *lb)
-{
- BKE_displist_free(lbn);
-
- LISTBASE_FOREACH (const DispList *, dl, lb) {
- DispList *dln = (DispList *)MEM_dupallocN(dl);
- BLI_addtail(lbn, dln);
- dln->verts = (float *)MEM_dupallocN(dl->verts);
- dln->nors = (float *)MEM_dupallocN(dl->nors);
- dln->index = (int *)MEM_dupallocN(dl->index);
- }
-}
-
-void BKE_displist_normals_add(ListBase *lb)
-{
- float *vdata, *ndata, nor[3];
- float *v1, *v2, *v3, *v4;
- float *n1, *n2, *n3, *n4;
- int a, b, p1, p2, p3, p4;
-
- LISTBASE_FOREACH (DispList *, dl, lb) {
- if (dl->type == DL_INDEX3) {
- if (dl->nors == nullptr) {
- dl->nors = (float *)MEM_callocN(sizeof(float[3]), __func__);
-
- if (dl->flag & DL_BACK_CURVE) {
- dl->nors[2] = -1.0f;
- }
- else {
- dl->nors[2] = 1.0f;
- }
- }
- }
- else if (dl->type == DL_SURF) {
- if (dl->nors == nullptr) {
- dl->nors = (float *)MEM_callocN(sizeof(float[3]) * dl->nr * dl->parts, __func__);
-
- vdata = dl->verts;
- ndata = dl->nors;
-
- for (a = 0; a < dl->parts; a++) {
-
- if (BKE_displist_surfindex_get(dl, a, &b, &p1, &p2, &p3, &p4) == 0) {
- break;
- }
-
- v1 = vdata + 3 * p1;
- n1 = ndata + 3 * p1;
- v2 = vdata + 3 * p2;
- n2 = ndata + 3 * p2;
- v3 = vdata + 3 * p3;
- n3 = ndata + 3 * p3;
- v4 = vdata + 3 * p4;
- n4 = ndata + 3 * p4;
-
- for (; b < dl->nr; b++) {
- normal_quad_v3(nor, v1, v3, v4, v2);
-
- add_v3_v3(n1, nor);
- add_v3_v3(n2, nor);
- add_v3_v3(n3, nor);
- add_v3_v3(n4, nor);
-
- v2 = v1;
- v1 += 3;
- v4 = v3;
- v3 += 3;
- n2 = n1;
- n1 += 3;
- n4 = n3;
- n3 += 3;
- }
- }
- a = dl->parts * dl->nr;
- v1 = ndata;
- while (a--) {
- normalize_v3(v1);
- v1 += 3;
- }
- }
- }
- }
-}
-
-void BKE_displist_count(const ListBase *lb, int *totvert, int *totface, int *tottri)
-{
- LISTBASE_FOREACH (const DispList *, dl, lb) {
- int vert_tot = 0;
- int face_tot = 0;
- int tri_tot = 0;
- bool cyclic_u = dl->flag & DL_CYCL_U;
- bool cyclic_v = dl->flag & DL_CYCL_V;
-
- switch (dl->type) {
- case DL_SURF: {
- int segments_u = dl->nr - (cyclic_u == false);
- int segments_v = dl->parts - (cyclic_v == false);
- vert_tot = dl->nr * dl->parts;
- face_tot = segments_u * segments_v;
- tri_tot = face_tot * 2;
- break;
- }
- case DL_INDEX3: {
- vert_tot = dl->nr;
- face_tot = dl->parts;
- tri_tot = face_tot;
- break;
- }
- case DL_INDEX4: {
- vert_tot = dl->nr;
- face_tot = dl->parts;
- tri_tot = face_tot * 2;
- break;
- }
- case DL_POLY:
- case DL_SEGM: {
- vert_tot = dl->nr * dl->parts;
- break;
- }
- }
-
- *totvert += vert_tot;
- *totface += face_tot;
- *tottri += tri_tot;
- }
-}
-
bool BKE_displist_surfindex_get(
const DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4)
{
@@ -243,7 +114,6 @@ bool BKE_displist_surfindex_get(
return true;
}
-/* ****************** Make #DispList ********************* */
#ifdef __INTEL_COMPILER
/* ICC with the optimization -02 causes crashes. */
# pragma intel optimization_level 1
@@ -638,27 +508,6 @@ float BKE_displist_calc_taper(
return displist_calc_taper(depsgraph, scene, taperobj, fac);
}
-void BKE_displist_make_mball(Depsgraph *depsgraph, Scene *scene, Object *ob)
-{
- if (!ob || ob->type != OB_MBALL) {
- return;
- }
-
- if (ob == BKE_mball_basis_find(scene, ob)) {
- if (ob->runtime.curve_cache) {
- BKE_displist_free(&(ob->runtime.curve_cache->disp));
- }
- else {
- ob->runtime.curve_cache = MEM_cnew<CurveCache>(__func__);
- }
-
- BKE_mball_polygonize(depsgraph, scene, ob, &ob->runtime.curve_cache->disp);
- BKE_mball_texspace_calc(ob);
-
- object_deform_mball(ob, &ob->runtime.curve_cache->disp);
- }
-}
-
static ModifierData *curve_get_tessellate_point(const Scene *scene,
const Object *ob,
const bool for_render,
@@ -785,7 +634,7 @@ void BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
/**
* \return True if the deformed curve control point data should be implicitly
- * converted directly to a mesh, or false if it can be left as curve data via #CurveEval.
+ * converted directly to a mesh, or false if it can be left as curve data via the #Curves type.
*/
static bool do_curve_implicit_mesh_conversion(const Curve *curve,
ModifierData *first_modifier,
@@ -1493,7 +1342,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
* - The dependency graph has handling of edit mode pointers (see #update_edit_mode_pointers)
* but it doesn't seem to work in this case.
*
- * Since the the plan is to replace this legacy curve object with the curves data-block
+ * Since the plan is to replace this legacy curve object with the curves data-block
* (see T95355), this somewhat hacky inefficient solution is relatively temporary.
*/
Curve &cow_curve = *reinterpret_cast<Curve *>(
@@ -1514,20 +1363,19 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3])
{
- bool doit = false;
+ bool empty = true;
LISTBASE_FOREACH (const DispList *, dl, dispbase) {
- const int tot = (ELEM(dl->type, DL_INDEX3, DL_INDEX4)) ? dl->nr : dl->nr * dl->parts;
+ 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 * 3]);
}
if (tot != 0) {
- doit = true;
+ empty = false;
}
}
- if (!doit) {
- /* there's no geometry in displist, use zero-sized boundbox */
+ if (empty) {
zero_v3(min);
zero_v3(max);
}
diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c
index 423e76fce8c..9d46c381d7a 100644
--- a/source/blender/blenkernel/intern/dynamicpaint.c
+++ b/source/blender/blenkernel/intern/dynamicpaint.c
@@ -1402,24 +1402,24 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const b
/* For vertex format, count every vertex that is connected by an edge */
int numOfEdges = mesh->totedge;
int numOfPolys = mesh->totpoly;
- struct MEdge *edge = mesh->medge;
- struct MPoly *mpoly = mesh->mpoly;
- struct MLoop *mloop = mesh->mloop;
+ const MEdge *edges = BKE_mesh_edges(mesh);
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
/* count number of edges per vertex */
for (int i = 0; i < numOfEdges; i++) {
- ad->n_num[edge[i].v1]++;
- ad->n_num[edge[i].v2]++;
+ ad->n_num[edges[i].v1]++;
+ ad->n_num[edges[i].v2]++;
- temp_data[edge[i].v1]++;
- temp_data[edge[i].v2]++;
+ temp_data[edges[i].v1]++;
+ temp_data[edges[i].v2]++;
}
/* also add number of vertices to temp_data
* to locate points on "mesh edge" */
for (int i = 0; i < numOfPolys; i++) {
- for (int j = 0; j < mpoly[i].totloop; j++) {
- temp_data[mloop[mpoly[i].loopstart + j].v]++;
+ for (int j = 0; j < polys[i].totloop; j++) {
+ temp_data[loops[polys[i].loopstart + j].v]++;
}
}
@@ -1444,15 +1444,15 @@ static void dynamicPaint_initAdjacencyData(DynamicPaintSurface *surface, const b
/* and now add neighbor data using that info */
for (int i = 0; i < numOfEdges; i++) {
/* first vertex */
- int index = edge[i].v1;
+ int index = edges[i].v1;
n_pos = ad->n_index[index] + temp_data[index];
- ad->n_target[n_pos] = edge[i].v2;
+ ad->n_target[n_pos] = edges[i].v2;
temp_data[index]++;
/* second vertex */
- index = edge[i].v2;
+ index = edges[i].v2;
n_pos = ad->n_index[index] + temp_data[index];
- ad->n_target[n_pos] = edge[i].v1;
+ ad->n_target[n_pos] = edges[i].v1;
temp_data[index]++;
}
}
@@ -1604,7 +1604,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface
else if (surface->init_color_type == MOD_DPAINT_INITIAL_TEXTURE) {
Tex *tex = surface->init_texture;
- const MLoop *mloop = mesh->mloop;
+ const MLoop *mloop = BKE_mesh_loops(mesh);
const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(mesh);
const int tottri = BKE_mesh_runtime_looptri_len(mesh);
@@ -1660,7 +1660,7 @@ static void dynamicPaint_setInitialColor(const Scene *scene, DynamicPaintSurface
/* for vertex surface, just copy colors from mcol */
if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) {
- const MLoop *mloop = mesh->mloop;
+ const MLoop *mloop = BKE_mesh_loops(mesh);
const int totloop = mesh->totloop;
const MLoopCol *col = CustomData_get_layer_named(
&mesh->ldata, CD_PROP_BYTE_COLOR, surface->init_layername);
@@ -1811,7 +1811,7 @@ static void dynamicPaint_applySurfaceDisplace(DynamicPaintSurface *surface, Mesh
/* displace paint */
if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE) {
- MVert *mvert = result->mvert;
+ MVert *mvert = BKE_mesh_verts_for_write(result);
DynamicPaintModifierApplyData data = {
.surface = surface,
@@ -1913,9 +1913,9 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *
/* vertex color paint */
if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) {
- MLoop *mloop = result->mloop;
+ const MLoop *mloop = BKE_mesh_loops(result);
const int totloop = result->totloop;
- MPoly *mpoly = result->mpoly;
+ const MPoly *mpoly = BKE_mesh_polys(result);
const int totpoly = result->totpoly;
/* paint is stored on dry and wet layers, so mix final color first */
@@ -1944,7 +1944,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *
if (!mloopcol && dynamicPaint_outputLayerExists(surface, ob, 0)) {
mloopcol = CustomData_add_layer_named(&result->ldata,
CD_PROP_BYTE_COLOR,
- CD_CALLOC,
+ CD_SET_DEFAULT,
NULL,
totloop,
surface->output_name);
@@ -1957,7 +1957,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *
if (!mloopcol_wet && dynamicPaint_outputLayerExists(surface, ob, 1)) {
mloopcol_wet = CustomData_add_layer_named(&result->ldata,
CD_PROP_BYTE_COLOR,
- CD_CALLOC,
+ CD_SET_DEFAULT,
NULL,
totloop,
surface->output_name2);
@@ -1988,9 +1988,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *
/* apply weights into a vertex group, if doesn't exists add a new layer */
if (defgrp_index != -1 && !dvert && (surface->output_name[0] != '\0')) {
dvert = CustomData_add_layer(
- &result->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, sData->total_points);
- /* Make the dvert layer easily accessible from the mesh data. */
- result->dvert = dvert;
+ &result->vdata, CD_MDEFORMVERT, CD_SET_DEFAULT, NULL, sData->total_points);
}
if (defgrp_index != -1 && dvert) {
for (int i = 0; i < sData->total_points; i++) {
@@ -2012,7 +2010,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *
}
/* wave simulation */
else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) {
- MVert *mvert = result->mvert;
+ MVert *mvert = BKE_mesh_verts_for_write(result);
DynamicPaintModifierApplyData data = {
.surface = surface,
@@ -2823,7 +2821,7 @@ int dynamicPaint_createUVSurface(Scene *scene,
return setError(canvas, N_("Cannot bake non-'image sequence' formats"));
}
- mloop = mesh->mloop;
+ mloop = BKE_mesh_loops(mesh);
mlooptri = BKE_mesh_runtime_looptri_ensure(mesh);
const int tottri = BKE_mesh_runtime_looptri_len(mesh);
@@ -2963,7 +2961,7 @@ int dynamicPaint_createUVSurface(Scene *scene,
BKE_mesh_vert_looptri_map_create(&vert_to_looptri_map,
&vert_to_looptri_map_mem,
- mesh->mvert,
+ BKE_mesh_verts_for_write(mesh),
mesh->totvert,
mlooptri,
tottri,
@@ -3783,7 +3781,8 @@ static void dynamicPaint_brushMeshCalculateVelocity(Depsgraph *depsgraph,
eModifierType_DynamicPaint);
mesh_p = BKE_mesh_copy_for_eval(dynamicPaint_brush_mesh_get(brush), false);
numOfVerts_p = mesh_p->totvert;
- mvert_p = mesh_p->mvert;
+
+ mvert_p = BKE_mesh_verts_for_write(mesh_p);
copy_m4_m4(prev_obmat, ob->obmat);
/* current frame mesh */
@@ -3799,7 +3798,7 @@ static void dynamicPaint_brushMeshCalculateVelocity(Depsgraph *depsgraph,
eModifierType_DynamicPaint);
mesh_c = dynamicPaint_brush_mesh_get(brush);
numOfVerts_c = mesh_c->totvert;
- mvert_c = mesh_c->mvert;
+ mvert_c = BKE_mesh_verts_for_write(mesh_c);
(*brushVel) = (struct Vec3f *)MEM_mallocN(numOfVerts_c * sizeof(Vec3f),
"Dynamic Paint brush velocity");
@@ -4270,10 +4269,10 @@ static bool dynamicPaint_paintMesh(Depsgraph *depsgraph,
VolumeGrid *grid = bData->grid;
mesh = BKE_mesh_copy_for_eval(brush_mesh, false);
- mvert = mesh->mvert;
+ mvert = BKE_mesh_verts_for_write(mesh);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
mlooptri = BKE_mesh_runtime_looptri_ensure(mesh);
- mloop = mesh->mloop;
+ mloop = BKE_mesh_loops(mesh);
numOfVerts = mesh->totvert;
/* Transform collider vertices to global space
@@ -4759,7 +4758,7 @@ static bool dynamicPaint_paintSinglePoint(
}
const Mesh *brush_mesh = dynamicPaint_brush_mesh_get(brush);
- const MVert *mvert = brush_mesh->mvert;
+ const MVert *mvert = BKE_mesh_verts(brush_mesh);
/*
* Loop through every surface point
@@ -5862,7 +5861,7 @@ static bool dynamicPaint_surfaceHasMoved(DynamicPaintSurface *surface, Object *o
PaintSurfaceData *sData = surface->data;
PaintBakeData *bData = sData->bData;
Mesh *mesh = dynamicPaint_canvas_mesh_get(surface->canvas);
- MVert *mvert = mesh->mvert;
+ const MVert *mvert = BKE_mesh_verts(mesh);
int numOfVerts = mesh->totvert;
@@ -6022,7 +6021,7 @@ static bool dynamicPaint_generateBakeData(DynamicPaintSurface *surface,
const bool do_accel_data = (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) != 0;
int canvasNumOfVerts = mesh->totvert;
- MVert *mvert = mesh->mvert;
+ const MVert *mvert = BKE_mesh_verts(mesh);
Vec3f *canvas_verts;
if (bData) {
diff --git a/source/blender/blenkernel/intern/editmesh_tangent.c b/source/blender/blenkernel/intern/editmesh_tangent.cc
index 0a3107eee24..a65532d083d 100644
--- a/source/blender/blenkernel/intern/editmesh_tangent.c
+++ b/source/blender/blenkernel/intern/editmesh_tangent.cc
@@ -20,7 +20,7 @@
#include "MEM_guardedalloc.h"
/* interface */
-#include "mikktspace.h"
+#include "mikktspace.hh"
/* -------------------------------------------------------------------- */
/** \name Tangent Space Calculation
@@ -29,234 +29,130 @@
/* Necessary complexity to handle looptri's as quads for correct tangents */
#define USE_LOOPTRI_DETECT_QUADS
-typedef struct {
- const float (*precomputedFaceNormals)[3];
- const float (*precomputedLoopNormals)[3];
- const BMLoop *(*looptris)[3];
- int cd_loop_uv_offset; /* texture coordinates */
- const float (*orco)[3];
- float (*tangent)[4]; /* destination */
- int numTessFaces;
-
-#ifdef USE_LOOPTRI_DETECT_QUADS
- /* map from 'fake' face index to looptri,
- * quads will point to the first looptri of the quad */
- const int *face_as_quad_map;
- int num_face_as_quad_map;
-#endif
-
-} SGLSLEditMeshToTangent;
-
-#ifdef USE_LOOPTRI_DETECT_QUADS
-/* seems weak but only used on quads */
-static const BMLoop *bm_loop_at_face_index(const BMFace *f, int vert_index)
-{
- const BMLoop *l = BM_FACE_FIRST_LOOP(f);
- while (vert_index--) {
- l = l->next;
- }
- return l;
-}
-#endif
-
-static int emdm_ts_GetNumFaces(const SMikkTSpaceContext *pContext)
-{
- SGLSLEditMeshToTangent *pMesh = pContext->m_pUserData;
-
+struct SGLSLEditMeshToTangent {
+ uint GetNumFaces()
+ {
#ifdef USE_LOOPTRI_DETECT_QUADS
- return pMesh->num_face_as_quad_map;
+ return (uint)num_face_as_quad_map;
#else
- return pMesh->numTessFaces;
+ return (uint)numTessFaces;
#endif
-}
-
-static int emdm_ts_GetNumVertsOfFace(const SMikkTSpaceContext *pContext, const int face_num)
-{
-#ifdef USE_LOOPTRI_DETECT_QUADS
- SGLSLEditMeshToTangent *pMesh = pContext->m_pUserData;
- if (pMesh->face_as_quad_map) {
- const BMLoop **lt = pMesh->looptris[pMesh->face_as_quad_map[face_num]];
- if (lt[0]->f->len == 4) {
- return 4;
- }
}
- return 3;
-#else
- UNUSED_VARS(pContext, face_num);
- return 3;
-#endif
-}
-
-static void emdm_ts_GetPosition(const SMikkTSpaceContext *pContext,
- float r_co[3],
- const int face_num,
- const int vert_index)
-{
- // BLI_assert(vert_index >= 0 && vert_index < 4);
- SGLSLEditMeshToTangent *pMesh = pContext->m_pUserData;
- const BMLoop **lt;
- const BMLoop *l;
+ uint GetNumVerticesOfFace(const uint face_num)
+ {
#ifdef USE_LOOPTRI_DETECT_QUADS
- if (pMesh->face_as_quad_map) {
- lt = pMesh->looptris[pMesh->face_as_quad_map[face_num]];
- if (lt[0]->f->len == 4) {
- l = bm_loop_at_face_index(lt[0]->f, vert_index);
- goto finally;
+ if (face_as_quad_map) {
+ if (looptris[face_as_quad_map[face_num]][0]->f->len == 4) {
+ return 4;
+ }
}
- /* fall through to regular triangle */
- }
- else {
- lt = pMesh->looptris[face_num];
- }
+ return 3;
#else
- lt = pMesh->looptris[face_num];
+ UNUSED_VARS(pContext, face_num);
+ return 3;
#endif
- l = lt[vert_index];
-
- const float *co;
-
-finally:
- co = l->v->co;
- copy_v3_v3(r_co, co);
-}
+ }
-static void emdm_ts_GetTextureCoordinate(const SMikkTSpaceContext *pContext,
- float r_uv[2],
- const int face_num,
- const int vert_index)
-{
- // BLI_assert(vert_index >= 0 && vert_index < 4);
- SGLSLEditMeshToTangent *pMesh = pContext->m_pUserData;
- const BMLoop **lt;
- const BMLoop *l;
+ const BMLoop *GetLoop(const uint face_num, uint vert_index)
+ {
+ // BLI_assert(vert_index >= 0 && vert_index < 4);
+ const BMLoop **lt;
+ const BMLoop *l;
#ifdef USE_LOOPTRI_DETECT_QUADS
- if (pMesh->face_as_quad_map) {
- lt = pMesh->looptris[pMesh->face_as_quad_map[face_num]];
- if (lt[0]->f->len == 4) {
- l = bm_loop_at_face_index(lt[0]->f, vert_index);
- goto finally;
+ if (face_as_quad_map) {
+ lt = looptris[face_as_quad_map[face_num]];
+ if (lt[0]->f->len == 4) {
+ l = BM_FACE_FIRST_LOOP(lt[0]->f);
+ while (vert_index--) {
+ l = l->next;
+ }
+ return l;
+ }
+ /* fall through to regular triangle */
+ }
+ else {
+ lt = looptris[face_num];
}
- /* fall through to regular triangle */
- }
- else {
- lt = pMesh->looptris[face_num];
- }
#else
- lt = pMesh->looptris[face_num];
+ lt = looptris[face_num];
#endif
- l = lt[vert_index];
-
-finally:
- if (pMesh->cd_loop_uv_offset != -1) {
- const float *uv = BM_ELEM_CD_GET_VOID_P(l, pMesh->cd_loop_uv_offset);
- copy_v2_v2(r_uv, uv);
+ return lt[vert_index];
}
- else {
- const float *orco = pMesh->orco[BM_elem_index_get(l->v)];
- map_to_sphere(&r_uv[0], &r_uv[1], orco[0], orco[1], orco[2]);
- }
-}
-static void emdm_ts_GetNormal(const SMikkTSpaceContext *pContext,
- float r_no[3],
- const int face_num,
- const int vert_index)
-{
- // BLI_assert(vert_index >= 0 && vert_index < 4);
- SGLSLEditMeshToTangent *pMesh = pContext->m_pUserData;
- const BMLoop **lt;
- const BMLoop *l;
+ mikk::float3 GetPosition(const uint face_num, const uint vert_index)
+ {
+ const BMLoop *l = GetLoop(face_num, vert_index);
+ return mikk::float3(l->v->co);
+ }
-#ifdef USE_LOOPTRI_DETECT_QUADS
- if (pMesh->face_as_quad_map) {
- lt = pMesh->looptris[pMesh->face_as_quad_map[face_num]];
- if (lt[0]->f->len == 4) {
- l = bm_loop_at_face_index(lt[0]->f, vert_index);
- goto finally;
+ mikk::float3 GetTexCoord(const uint face_num, const uint vert_index)
+ {
+ const BMLoop *l = GetLoop(face_num, vert_index);
+ if (cd_loop_uv_offset != -1) {
+ const float *uv = (const float *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ return mikk::float3(uv[0], uv[1], 1.0f);
+ }
+ else {
+ const float *orco_p = orco[BM_elem_index_get(l->v)];
+ float u, v;
+ map_to_sphere(&u, &v, orco_p[0], orco_p[1], orco_p[2]);
+ return mikk::float3(u, v, 1.0f);
}
- /* fall through to regular triangle */
- }
- else {
- lt = pMesh->looptris[face_num];
}
-#else
- lt = pMesh->looptris[face_num];
-#endif
- l = lt[vert_index];
-finally:
- if (pMesh->precomputedLoopNormals) {
- copy_v3_v3(r_no, pMesh->precomputedLoopNormals[BM_elem_index_get(l)]);
- }
- else if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH) == 0) { /* flat */
- if (pMesh->precomputedFaceNormals) {
- copy_v3_v3(r_no, pMesh->precomputedFaceNormals[BM_elem_index_get(l->f)]);
+ mikk::float3 GetNormal(const uint face_num, const uint vert_index)
+ {
+ const BMLoop *l = GetLoop(face_num, vert_index);
+ if (precomputedLoopNormals) {
+ return mikk::float3(precomputedLoopNormals[BM_elem_index_get(l)]);
+ }
+ else if (BM_elem_flag_test(l->f, BM_ELEM_SMOOTH) == 0) { /* flat */
+ if (precomputedFaceNormals) {
+ return mikk::float3(precomputedFaceNormals[BM_elem_index_get(l->f)]);
+ }
+ else {
+ return mikk::float3(l->f->no);
+ }
}
else {
- copy_v3_v3(r_no, l->f->no);
+ return mikk::float3(l->v->no);
}
}
- else {
- copy_v3_v3(r_no, l->v->no);
+
+ void SetTangentSpace(const uint face_num,
+ const uint vert_index,
+ mikk::float3 T,
+ bool orientation)
+ {
+ const BMLoop *l = GetLoop(face_num, vert_index);
+ float *p_res = tangent[BM_elem_index_get(l)];
+ copy_v4_fl4(p_res, T.x, T.y, T.z, orientation ? 1.0f : -1.0f);
}
-}
-static void emdm_ts_SetTSpace(const SMikkTSpaceContext *pContext,
- const float fvTangent[3],
- const float fSign,
- const int face_num,
- const int vert_index)
-{
- // BLI_assert(vert_index >= 0 && vert_index < 4);
- SGLSLEditMeshToTangent *pMesh = pContext->m_pUserData;
- const BMLoop **lt;
- const BMLoop *l;
+ const float (*precomputedFaceNormals)[3];
+ const float (*precomputedLoopNormals)[3];
+ const BMLoop *(*looptris)[3];
+ int cd_loop_uv_offset; /* texture coordinates */
+ const float (*orco)[3];
+ float (*tangent)[4]; /* destination */
+ int numTessFaces;
#ifdef USE_LOOPTRI_DETECT_QUADS
- if (pMesh->face_as_quad_map) {
- lt = pMesh->looptris[pMesh->face_as_quad_map[face_num]];
- if (lt[0]->f->len == 4) {
- l = bm_loop_at_face_index(lt[0]->f, vert_index);
- goto finally;
- }
- /* fall through to regular triangle */
- }
- else {
- lt = pMesh->looptris[face_num];
- }
-#else
- lt = pMesh->looptris[face_num];
+ /* map from 'fake' face index to looptri,
+ * quads will point to the first looptri of the quad */
+ const int *face_as_quad_map;
+ int num_face_as_quad_map;
#endif
- l = lt[vert_index];
-
- float *pRes;
-
-finally:
- pRes = pMesh->tangent[BM_elem_index_get(l)];
- copy_v3_v3(pRes, fvTangent);
- pRes[3] = fSign;
-}
+};
static void emDM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata)
{
- struct SGLSLEditMeshToTangent *mesh2tangent = taskdata;
- /* new computation method */
- {
- SMikkTSpaceContext sContext = {NULL};
- SMikkTSpaceInterface sInterface = {NULL};
- sContext.m_pUserData = mesh2tangent;
- sContext.m_pInterface = &sInterface;
- sInterface.m_getNumFaces = emdm_ts_GetNumFaces;
- sInterface.m_getNumVerticesOfFace = emdm_ts_GetNumVertsOfFace;
- sInterface.m_getPosition = emdm_ts_GetPosition;
- sInterface.m_getTexCoord = emdm_ts_GetTextureCoordinate;
- sInterface.m_getNormal = emdm_ts_GetNormal;
- sInterface.m_setTSpaceBasic = emdm_ts_SetTSpace;
- /* 0 if failed */
- genTangSpaceDefault(&sContext);
- }
+ SGLSLEditMeshToTangent *mesh_data = static_cast<SGLSLEditMeshToTangent *>(taskdata);
+
+ mikk::Mikktspace<SGLSLEditMeshToTangent> mikk(*mesh_data);
+ mikk.genTangSpace();
}
void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
@@ -304,7 +200,7 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
if ((tangent_mask & DM_TANGENT_MASK_ORCO) &&
CustomData_get_named_layer_index(loopdata_out, CD_TANGENT, "") == -1) {
CustomData_add_layer_named(
- loopdata_out, CD_TANGENT, CD_CALLOC, NULL, (int)loopdata_out_len, "");
+ loopdata_out, CD_TANGENT, CD_SET_DEFAULT, nullptr, (int)loopdata_out_len, "");
}
if (calc_act && act_uv_name[0]) {
BKE_mesh_add_loop_tangent_named_layer_for_uv(
@@ -317,14 +213,14 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
int totface = em->tottri;
#ifdef USE_LOOPTRI_DETECT_QUADS
int num_face_as_quad_map;
- int *face_as_quad_map = NULL;
+ int *face_as_quad_map = nullptr;
/* map faces to quads */
if (em->tottri != bm->totface) {
/* Over allocate, since we don't know how many ngon or quads we have. */
/* map fake face index to looptri */
- face_as_quad_map = MEM_mallocN(sizeof(int) * totface, __func__);
+ face_as_quad_map = static_cast<int *>(MEM_mallocN(sizeof(int) * totface, __func__));
int i, j;
for (i = 0, j = 0; j < totface; i++, j++) {
face_as_quad_map[i] = j;
@@ -342,7 +238,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_HIGH);
+ task_pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH);
tangent_mask_curr = 0;
/* Calculate tangent layers */
@@ -393,9 +289,10 @@ void BKE_editmesh_loop_tangent_calc(BMEditMesh *em,
BM_mesh_elem_index_ensure(bm, htype_index);
mesh2tangent->looptris = (const BMLoop *(*)[3])em->looptris;
- mesh2tangent->tangent = loopdata_out->layers[index].data;
+ mesh2tangent->tangent = static_cast<float(*)[4]>(loopdata_out->layers[index].data);
- BLI_task_pool_push(task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, NULL);
+ BLI_task_pool_push(
+ task_pool, emDM_calc_loop_tangents_thread, mesh2tangent, false, nullptr);
}
BLI_assert(tangent_mask_curr == tangent_mask);
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index 7722c2fa004..6719590e7c0 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -207,10 +207,11 @@ static void add_effector_evaluation(ListBase **effectors,
}
ListBase *BKE_effector_relations_create(Depsgraph *depsgraph,
+ const Scene *scene,
ViewLayer *view_layer,
Collection *collection)
{
- Base *base = BKE_collection_or_layer_objects(view_layer, collection);
+ Base *base = BKE_collection_or_layer_objects(scene, view_layer, collection);
const bool for_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int base_flag = (for_render) ? BASE_ENABLED_RENDER : BASE_ENABLED_VIEWPORT;
@@ -700,9 +701,10 @@ bool get_effector_data(EffectorCache *eff,
else if (eff->pd && eff->pd->shape == PFIELD_SHAPE_POINTS) {
/* TODO: hair and points object support */
const Mesh *me_eval = BKE_object_get_evaluated_mesh(eff->ob);
+ const MVert *verts = BKE_mesh_verts(me_eval);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval);
if (me_eval != NULL) {
- copy_v3_v3(efd->loc, me_eval->mvert[*efd->index].co);
+ copy_v3_v3(efd->loc, verts[*efd->index].co);
copy_v3_v3(efd->nor, vert_normals[*efd->index]);
mul_m4_v3(eff->ob->obmat, efd->loc);
diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c
index 0203620df84..f5876e48241 100644
--- a/source/blender/blenkernel/intern/fcurve.c
+++ b/source/blender/blenkernel/intern/fcurve.c
@@ -37,6 +37,7 @@
#include "BLO_read_write.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "CLG_log.h"
@@ -1146,7 +1147,7 @@ void fcurve_samples_to_keyframes(FCurve *fcu, const int start, const int end)
MEM_SAFE_FREE(fcu->fpt);
/* Not strictly needed since we use linear interpolation, but better be consistent here. */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* ***************************** F-Curve Sanity ********************************* */
@@ -1216,7 +1217,7 @@ static BezTriple *cycle_offset_triple(
return out;
}
-void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag)
+void BKE_fcurve_handles_recalc_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag)
{
BezTriple *bezt, *prev, *next;
int a = fcu->totvert;
@@ -1299,9 +1300,9 @@ void calchandles_fcurve_ex(FCurve *fcu, eBezTriple_Flag handle_sel_flag)
}
}
-void calchandles_fcurve(FCurve *fcu)
+void BKE_fcurve_handles_recalc(FCurve *fcu)
{
- calchandles_fcurve_ex(fcu, SELECT);
+ BKE_fcurve_handles_recalc_ex(fcu, SELECT);
}
void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_handle)
@@ -1320,7 +1321,7 @@ void testhandles_fcurve(FCurve *fcu, eBezTriple_Flag sel_flag, const bool use_ha
}
/* Recalculate handles. */
- calchandles_fcurve_ex(fcu, sel_flag);
+ BKE_fcurve_handles_recalc_ex(fcu, sel_flag);
}
void sort_time_fcurve(FCurve *fcu)
@@ -1590,6 +1591,12 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b)
}
}
+static void fcurve_bezt_free(FCurve *fcu)
+{
+ MEM_SAFE_FREE(fcu->bezt);
+ fcu->totvert = 0;
+}
+
bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt,
struct BezTriple *prev,
struct BezTriple *next,
@@ -1651,6 +1658,69 @@ bool BKE_fcurve_bezt_subdivide_handles(struct BezTriple *bezt,
return true;
}
+void BKE_fcurve_delete_key(FCurve *fcu, int index)
+{
+ /* sanity check */
+ if (fcu == NULL) {
+ return;
+ }
+
+ /* verify the index:
+ * 1) cannot be greater than the number of available keyframes
+ * 2) negative indices are for specifying a value from the end of the array
+ */
+ if (abs(index) >= fcu->totvert) {
+ return;
+ }
+ if (index < 0) {
+ index += fcu->totvert;
+ }
+
+ /* Delete this keyframe */
+ memmove(
+ &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1));
+ fcu->totvert--;
+
+ /* Free the array of BezTriples if there are not keyframes */
+ if (fcu->totvert == 0) {
+ fcurve_bezt_free(fcu);
+ }
+}
+
+bool BKE_fcurve_delete_keys_selected(FCurve *fcu)
+{
+ bool changed = false;
+
+ if (fcu->bezt == NULL) { /* ignore baked curves */
+ return false;
+ }
+
+ /* Delete selected BezTriples */
+ for (int i = 0; i < fcu->totvert; i++) {
+ if (fcu->bezt[i].f2 & SELECT) {
+ if (i == fcu->active_keyframe_index) {
+ BKE_fcurve_active_keyframe_set(fcu, NULL);
+ }
+ memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1));
+ fcu->totvert--;
+ i--;
+ changed = true;
+ }
+ }
+
+ /* Free the array of BezTriples if there are not keyframes */
+ if (fcu->totvert == 0) {
+ fcurve_bezt_free(fcu);
+ }
+
+ return changed;
+}
+
+void BKE_fcurve_delete_keys_all(FCurve *fcu)
+{
+ fcurve_bezt_free(fcu);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c
index 5d54c5c039b..aa33bef998f 100644
--- a/source/blender/blenkernel/intern/fcurve_driver.c
+++ b/source/blender/blenkernel/intern/fcurve_driver.c
@@ -30,6 +30,7 @@
#include "BKE_object.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "atomic_ops.h"
diff --git a/source/blender/blenkernel/intern/fcurve_test.cc b/source/blender/blenkernel/intern/fcurve_test.cc
index 1912e3a9d8d..285c6a0af4d 100644
--- a/source/blender/blenkernel/intern/fcurve_test.cc
+++ b/source/blender/blenkernel/intern/fcurve_test.cc
@@ -7,7 +7,6 @@
#include "BKE_fcurve.h"
#include "ED_keyframing.h"
-#include "ED_types.h" /* For SELECT. */
#include "DNA_anim_types.h"
diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c
index 8e95bf18c6b..349614b93dd 100644
--- a/source/blender/blenkernel/intern/fluid.c
+++ b/source/blender/blenkernel/intern/fluid.c
@@ -27,6 +27,7 @@
#include "BKE_effect.h"
#include "BKE_fluid.h"
#include "BKE_global.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_modifier.h"
#include "BKE_pointcache.h"
@@ -79,7 +80,7 @@
/** Max value for phi initialization */
#define PHI_MAX 9999.0f
-static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock);
+static void fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock);
#ifdef WITH_FLUID
// #define DEBUG_PRINT
@@ -403,7 +404,8 @@ static void manta_set_domain_from_mesh(FluidDomainSettings *fds,
size_t i;
float min[3] = {FLT_MAX, FLT_MAX, FLT_MAX}, max[3] = {-FLT_MAX, -FLT_MAX, -FLT_MAX};
float size[3];
- MVert *verts = me->mvert;
+
+ MVert *verts = BKE_mesh_verts_for_write(me);
float scale = 0.0;
int res;
@@ -480,7 +482,7 @@ static void update_final_gravity(FluidDomainSettings *fds, Scene *scene)
mul_v3_fl(fds->gravity_final, fds->effector_weights->global_gravity);
}
-static bool BKE_fluid_modifier_init(
+static bool fluid_modifier_init(
FluidModifierData *fmd, Depsgraph *depsgraph, Object *ob, Scene *scene, Mesh *me)
{
int scene_framenr = (int)DEG_get_ctime(depsgraph);
@@ -542,7 +544,9 @@ static bool BKE_fluid_modifier_init(
}
/* Forward declarations. */
-static void manta_smoke_calc_transparency(FluidDomainSettings *fds, ViewLayer *view_layer);
+static void manta_smoke_calc_transparency(FluidDomainSettings *fds,
+ Scene *scene,
+ ViewLayer *view_layer);
static float calc_voxel_transp(
float *result, const float *input, int res[3], int *pixel, float *t_ray, float correct);
static void update_distances(int index,
@@ -552,13 +556,13 @@ static void update_distances(int index,
float surface_thickness,
bool use_plane_init);
-static int get_light(ViewLayer *view_layer, float *light)
+static int get_light(Scene *scene, ViewLayer *view_layer, float *light)
{
- Base *base_tmp = NULL;
int found_light = 0;
/* Try to find a lamp, preferably local. */
- for (base_tmp = FIRSTBASE(view_layer); base_tmp; base_tmp = base_tmp->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base_tmp, BKE_view_layer_object_bases_get(view_layer)) {
if (base_tmp->object->type == OB_LAMP) {
Light *la = base_tmp->object->data;
@@ -994,28 +998,19 @@ static void obstacles_from_mesh(Object *coll_ob,
float dt)
{
if (fes->mesh) {
- Mesh *me = NULL;
- MVert *mvert = NULL;
const MLoopTri *looptri;
- const MLoop *mloop;
BVHTreeFromMesh tree_data = {NULL};
int numverts, i;
float *vert_vel = NULL;
bool has_velocity = false;
- me = BKE_mesh_copy_for_eval(fes->mesh, true);
+ Mesh *me = BKE_mesh_copy_for_eval(fes->mesh, false);
+ MVert *verts = BKE_mesh_verts_for_write(me);
int min[3], max[3], res[3];
- /* Duplicate vertices to modify. */
- if (me->mvert) {
- me->mvert = MEM_dupallocN(me->mvert);
- CustomData_set_layer(&me->vdata, CD_MVERT, me->mvert);
- }
-
- mvert = me->mvert;
- mloop = me->mloop;
+ const MLoop *mloop = BKE_mesh_loops(me);
looptri = BKE_mesh_runtime_looptri_ensure(me);
numverts = me->totvert;
@@ -1038,22 +1033,15 @@ static void obstacles_from_mesh(Object *coll_ob,
/* Transform mesh vertices to domain grid space for fast lookups.
* This is valid because the mesh is copied above. */
- BKE_mesh_vertex_normals_ensure(me);
- float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(me);
for (i = 0; i < numverts; i++) {
float co[3];
/* Vertex position. */
- mul_m4_v3(coll_ob->obmat, mvert[i].co);
- manta_pos_to_cell(fds, mvert[i].co);
-
- /* Vertex normal. */
- mul_mat3_m4_v3(coll_ob->obmat, vert_normals[i]);
- mul_mat3_m4_v3(fds->imat, vert_normals[i]);
- normalize_v3(vert_normals[i]);
+ mul_m4_v3(coll_ob->obmat, verts[i].co);
+ manta_pos_to_cell(fds, verts[i].co);
/* Vertex velocity. */
- add_v3fl_v3fl_v3i(co, mvert[i].co, fds->shift);
+ add_v3fl_v3fl_v3i(co, verts[i].co, fds->shift);
if (has_velocity) {
sub_v3_v3v3(&vert_vel[i * 3], co, &fes->verts_old[i * 3]);
mul_v3_fl(&vert_vel[i * 3], 1.0f / dt);
@@ -1061,7 +1049,7 @@ static void obstacles_from_mesh(Object *coll_ob,
copy_v3_v3(&fes->verts_old[i * 3], co);
/* Calculate emission map bounds. */
- bb_boundInsert(bb, mvert[i].co);
+ bb_boundInsert(bb, verts[i].co);
}
/* Set emission map.
@@ -1083,7 +1071,7 @@ static void obstacles_from_mesh(Object *coll_ob,
ObstaclesFromDMData data = {
.fes = fes,
- .mvert = mvert,
+ .mvert = verts,
.mloop = mloop,
.mlooptri = looptri,
.tree = &tree_data,
@@ -1106,9 +1094,6 @@ static void obstacles_from_mesh(Object *coll_ob,
if (vert_vel) {
MEM_freeN(vert_vel);
}
- if (me->mvert) {
- MEM_freeN(me->mvert);
- }
BKE_id_free(NULL, me);
}
}
@@ -2085,19 +2070,13 @@ static void emit_from_mesh(
/* Copy mesh for thread safety as we modify it.
* Main issue is its VertArray being modified, then replaced and freed. */
- Mesh *me = BKE_mesh_copy_for_eval(ffs->mesh, true);
+ Mesh *me = BKE_mesh_copy_for_eval(ffs->mesh, false);
+ MVert *verts = BKE_mesh_verts_for_write(me);
- /* Duplicate vertices to modify. */
- if (me->mvert) {
- me->mvert = MEM_dupallocN(me->mvert);
- CustomData_set_layer(&me->vdata, CD_MVERT, me->mvert);
- }
-
- MVert *mvert = me->mvert;
- const MLoop *mloop = me->mloop;
+ const MLoop *mloop = BKE_mesh_loops(me);
const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me);
const int numverts = me->totvert;
- const MDeformVert *dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT);
+ const MDeformVert *dvert = BKE_mesh_deform_verts(me);
const MLoopUV *mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, ffs->uvlayer_name);
if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
@@ -2121,8 +2100,8 @@ static void emit_from_mesh(
float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(me);
for (i = 0; i < numverts; i++) {
/* Vertex position. */
- mul_m4_v3(flow_ob->obmat, mvert[i].co);
- manta_pos_to_cell(fds, mvert[i].co);
+ mul_m4_v3(flow_ob->obmat, verts[i].co);
+ manta_pos_to_cell(fds, verts[i].co);
/* Vertex normal. */
mul_mat3_m4_v3(flow_ob->obmat, vert_normals[i]);
@@ -2132,7 +2111,7 @@ static void emit_from_mesh(
/* Vertex velocity. */
if (ffs->flags & FLUID_FLOW_INITVELOCITY) {
float co[3];
- add_v3fl_v3fl_v3i(co, mvert[i].co, fds->shift);
+ add_v3fl_v3fl_v3i(co, verts[i].co, fds->shift);
if (has_velocity) {
sub_v3_v3v3(&vert_vel[i * 3], co, &ffs->verts_old[i * 3]);
mul_v3_fl(&vert_vel[i * 3], 1.0 / dt);
@@ -2141,7 +2120,7 @@ static void emit_from_mesh(
}
/* Calculate emission map bounds. */
- bb_boundInsert(bb, mvert[i].co);
+ bb_boundInsert(bb, verts[i].co);
}
mul_m4_v3(flow_ob->obmat, flow_center);
manta_pos_to_cell(fds, flow_center);
@@ -2166,7 +2145,7 @@ static void emit_from_mesh(
EmitFromDMData data = {
.fds = fds,
.ffs = ffs,
- .mvert = mvert,
+ .mvert = verts,
.vert_normals = vert_normals,
.mloop = mloop,
.mlooptri = mlooptri,
@@ -2194,9 +2173,6 @@ static void emit_from_mesh(
if (vert_vel) {
MEM_freeN(vert_vel);
}
- if (me->mvert) {
- MEM_freeN(me->mvert);
- }
BKE_id_free(NULL, me);
}
}
@@ -3249,12 +3225,13 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds,
* If there are no faces in original mesh, keep materials and flags unchanged. */
MPoly *mpoly;
MPoly mp_example = {0};
- mpoly = orgmesh->mpoly;
+ mpoly = BKE_mesh_polys_for_write(orgmesh);
if (mpoly) {
mp_example = *mpoly;
}
- const short mp_mat_nr = mp_example.mat_nr;
+ const int *orig_material_indices = BKE_mesh_material_indices(orgmesh);
+ const short mp_mat_nr = orig_material_indices ? orig_material_indices[0] : 0;
const char mp_flag = mp_example.flag;
int i;
@@ -3280,9 +3257,9 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds,
if (!me) {
return NULL;
}
- mverts = me->mvert;
- mpolys = me->mpoly;
- mloops = me->mloop;
+ mverts = BKE_mesh_verts_for_write(me);
+ mpolys = BKE_mesh_polys_for_write(me);
+ mloops = BKE_mesh_loops_for_write(me);
/* Get size (dimension) but considering scaling. */
copy_v3_v3(cell_size_scaled, fds->cell_size);
@@ -3365,10 +3342,12 @@ static Mesh *create_liquid_geometry(FluidDomainSettings *fds,
}
}
+ int *material_indices = BKE_mesh_material_indices_for_write(me);
+
/* Loop for triangles. */
for (i = 0; i < num_faces; i++, mpolys++, mloops += 3) {
/* Initialize from existing face. */
- mpolys->mat_nr = mp_mat_nr;
+ material_indices[i] = mp_mat_nr;
mpolys->flag = mp_flag;
mpolys->loopstart = i * 3;
@@ -3414,9 +3393,9 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obje
}
result = BKE_mesh_new_nomain(num_verts, 0, 0, num_faces * 4, num_faces);
- mverts = result->mvert;
- mpolys = result->mpoly;
- mloops = result->mloop;
+ mverts = BKE_mesh_verts_for_write(result);
+ mpolys = BKE_mesh_polys_for_write(result);
+ mloops = BKE_mesh_loops_for_write(result);
if (num_verts) {
/* Volume bounds. */
@@ -3614,7 +3593,8 @@ static int manta_step(
/* Compute shadow grid for gas simulations. Make sure to skip if bake job was canceled early. */
if (fds->type == FLUID_DOMAIN_TYPE_GAS && result) {
- manta_smoke_calc_transparency(fds, DEG_get_evaluated_view_layer(depsgraph));
+ manta_smoke_calc_transparency(
+ fds, DEG_get_evaluated_scene(depsgraph), DEG_get_evaluated_view_layer(depsgraph));
}
BLI_mutex_unlock(&object_update_lock);
@@ -3635,15 +3615,15 @@ static void manta_guiding(
BLI_mutex_unlock(&object_update_lock);
}
-static void BKE_fluid_modifier_processFlow(FluidModifierData *fmd,
- Depsgraph *depsgraph,
- Scene *scene,
- Object *ob,
- Mesh *me,
- const int scene_framenr)
+static void fluid_modifier_processFlow(FluidModifierData *fmd,
+ Depsgraph *depsgraph,
+ Scene *scene,
+ Object *ob,
+ Mesh *me,
+ const int scene_framenr)
{
if (scene_framenr >= fmd->time) {
- BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me);
+ fluid_modifier_init(fmd, depsgraph, ob, scene, me);
}
if (fmd->flow) {
@@ -3658,19 +3638,19 @@ static void BKE_fluid_modifier_processFlow(FluidModifierData *fmd,
}
else if (scene_framenr < fmd->time) {
fmd->time = scene_framenr;
- BKE_fluid_modifier_reset_ex(fmd, false);
+ fluid_modifier_reset_ex(fmd, false);
}
}
-static void BKE_fluid_modifier_processEffector(FluidModifierData *fmd,
- Depsgraph *depsgraph,
- Scene *scene,
- Object *ob,
- Mesh *me,
- const int scene_framenr)
+static void fluid_modifier_processEffector(FluidModifierData *fmd,
+ Depsgraph *depsgraph,
+ Scene *scene,
+ Object *ob,
+ Mesh *me,
+ const int scene_framenr)
{
if (scene_framenr >= fmd->time) {
- BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me);
+ fluid_modifier_init(fmd, depsgraph, ob, scene, me);
}
if (fmd->effector) {
@@ -3685,16 +3665,16 @@ static void BKE_fluid_modifier_processEffector(FluidModifierData *fmd,
}
else if (scene_framenr < fmd->time) {
fmd->time = scene_framenr;
- BKE_fluid_modifier_reset_ex(fmd, false);
+ fluid_modifier_reset_ex(fmd, false);
}
}
-static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd,
- Depsgraph *depsgraph,
- Scene *scene,
- Object *ob,
- Mesh *me,
- const int scene_framenr)
+static void fluid_modifier_processDomain(FluidModifierData *fmd,
+ Depsgraph *depsgraph,
+ Scene *scene,
+ Object *ob,
+ Mesh *me,
+ const int scene_framenr)
{
FluidDomainSettings *fds = fmd->domain;
Object *guide_parent = NULL;
@@ -3740,7 +3720,7 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd,
/* Reset fluid if no fluid present. Also resets active fields. */
if (!fds->fluid) {
- BKE_fluid_modifier_reset_ex(fmd, false);
+ fluid_modifier_reset_ex(fmd, false);
}
/* Ensure cache directory is not relative. */
@@ -3768,12 +3748,12 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd,
if (pid.cache->flag & PTCACHE_OUTDATED) {
BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED);
BKE_fluid_cache_free_all(fds, ob);
- BKE_fluid_modifier_reset_ex(fmd, false);
+ fluid_modifier_reset_ex(fmd, false);
}
}
/* Fluid domain init must not fail in order to continue modifier evaluation. */
- if (!fds->fluid && !BKE_fluid_modifier_init(fmd, depsgraph, ob, scene, me)) {
+ if (!fds->fluid && !fluid_modifier_init(fmd, depsgraph, ob, scene, me)) {
CLOG_ERROR(&LOG, "Fluid initialization failed. Should not happen!");
return;
}
@@ -4101,19 +4081,19 @@ static void BKE_fluid_modifier_processDomain(FluidModifierData *fmd,
fmd->time = scene_framenr;
}
-static void BKE_fluid_modifier_process(
+static void fluid_modifier_process(
FluidModifierData *fmd, Depsgraph *depsgraph, Scene *scene, Object *ob, Mesh *me)
{
const int scene_framenr = (int)DEG_get_ctime(depsgraph);
if (fmd->type & MOD_FLUID_TYPE_FLOW) {
- BKE_fluid_modifier_processFlow(fmd, depsgraph, scene, ob, me, scene_framenr);
+ fluid_modifier_processFlow(fmd, depsgraph, scene, ob, me, scene_framenr);
}
else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
- BKE_fluid_modifier_processEffector(fmd, depsgraph, scene, ob, me, scene_framenr);
+ fluid_modifier_processEffector(fmd, depsgraph, scene, ob, me, scene_framenr);
}
else if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
- BKE_fluid_modifier_processDomain(fmd, depsgraph, scene, ob, me, scene_framenr);
+ fluid_modifier_processDomain(fmd, depsgraph, scene, ob, me, scene_framenr);
}
}
@@ -4131,7 +4111,7 @@ struct Mesh *BKE_fluid_modifier_do(
BLI_rw_mutex_lock(fmd->domain->fluid_mutex, THREAD_LOCK_WRITE);
}
- BKE_fluid_modifier_process(fmd, depsgraph, scene, ob, me);
+ fluid_modifier_process(fmd, depsgraph, scene, ob, me);
if ((fmd->type & MOD_FLUID_TYPE_DOMAIN) && fmd->domain) {
BLI_rw_mutex_unlock(fmd->domain->fluid_mutex);
@@ -4313,7 +4293,9 @@ static void bresenham_linie_3D(int x1,
cb(result, input, res, pixel, t_ray, correct);
}
-static void manta_smoke_calc_transparency(FluidDomainSettings *fds, ViewLayer *view_layer)
+static void manta_smoke_calc_transparency(FluidDomainSettings *fds,
+ Scene *scene,
+ ViewLayer *view_layer)
{
float bv[6] = {0};
float light[3];
@@ -4322,7 +4304,7 @@ static void manta_smoke_calc_transparency(FluidDomainSettings *fds, ViewLayer *v
float *shadow = manta_smoke_get_shadow(fds->fluid);
float correct = -7.0f * fds->dx;
- if (!get_light(view_layer, light)) {
+ if (!get_light(scene, view_layer, light)) {
return;
}
@@ -4704,7 +4686,7 @@ void BKE_fluid_fields_sanitize(FluidDomainSettings *settings)
* Use for versioning, even when fluids are disabled.
* \{ */
-static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd)
+static void fluid_modifier_freeDomain(FluidModifierData *fmd)
{
if (fmd->domain) {
if (fmd->domain->fluid) {
@@ -4733,7 +4715,7 @@ static void BKE_fluid_modifier_freeDomain(FluidModifierData *fmd)
}
}
-static void BKE_fluid_modifier_freeFlow(FluidModifierData *fmd)
+static void fluid_modifier_freeFlow(FluidModifierData *fmd)
{
if (fmd->flow) {
if (fmd->flow->mesh) {
@@ -4750,7 +4732,7 @@ static void BKE_fluid_modifier_freeFlow(FluidModifierData *fmd)
}
}
-static void BKE_fluid_modifier_freeEffector(FluidModifierData *fmd)
+static void fluid_modifier_freeEffector(FluidModifierData *fmd)
{
if (fmd->effector) {
if (fmd->effector->mesh) {
@@ -4767,7 +4749,7 @@ static void BKE_fluid_modifier_freeEffector(FluidModifierData *fmd)
}
}
-static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock)
+static void fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need_lock)
{
if (!fmd) {
return;
@@ -4807,7 +4789,7 @@ static void BKE_fluid_modifier_reset_ex(struct FluidModifierData *fmd, bool need
void BKE_fluid_modifier_reset(struct FluidModifierData *fmd)
{
- BKE_fluid_modifier_reset_ex(fmd, true);
+ fluid_modifier_reset_ex(fmd, true);
}
void BKE_fluid_modifier_free(FluidModifierData *fmd)
@@ -4816,9 +4798,9 @@ void BKE_fluid_modifier_free(FluidModifierData *fmd)
return;
}
- BKE_fluid_modifier_freeDomain(fmd);
- BKE_fluid_modifier_freeFlow(fmd);
- BKE_fluid_modifier_freeEffector(fmd);
+ fluid_modifier_freeDomain(fmd);
+ fluid_modifier_freeFlow(fmd);
+ fluid_modifier_freeEffector(fmd);
}
void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd)
@@ -4829,7 +4811,7 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd)
if (fmd->type & MOD_FLUID_TYPE_DOMAIN) {
if (fmd->domain) {
- BKE_fluid_modifier_freeDomain(fmd);
+ fluid_modifier_freeDomain(fmd);
}
fmd->domain = DNA_struct_default_alloc(FluidDomainSettings);
@@ -4861,7 +4843,7 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd)
}
else if (fmd->type & MOD_FLUID_TYPE_FLOW) {
if (fmd->flow) {
- BKE_fluid_modifier_freeFlow(fmd);
+ fluid_modifier_freeFlow(fmd);
}
fmd->flow = DNA_struct_default_alloc(FluidFlowSettings);
@@ -4869,7 +4851,7 @@ void BKE_fluid_modifier_create_type_data(struct FluidModifierData *fmd)
}
else if (fmd->type & MOD_FLUID_TYPE_EFFEC) {
if (fmd->effector) {
- BKE_fluid_modifier_freeEffector(fmd);
+ fluid_modifier_freeEffector(fmd);
}
fmd->effector = DNA_struct_default_alloc(FluidEffectorSettings);
diff --git a/source/blender/blenkernel/intern/fmodifier.c b/source/blender/blenkernel/intern/fmodifier.c
index 1bb7c49d616..11c3dfc18dc 100644
--- a/source/blender/blenkernel/intern/fmodifier.c
+++ b/source/blender/blenkernel/intern/fmodifier.c
@@ -768,19 +768,19 @@ static void fcm_cycles_evaluate(FCurve *UNUSED(fcu),
}
static FModifierTypeInfo FMI_CYCLES = {
- FMODIFIER_TYPE_CYCLES, /* type */
- sizeof(FMod_Cycles), /* size */
- FMI_TYPE_EXTRAPOLATION, /* action type */
- FMI_REQUIRES_ORIGINAL_DATA, /* requirements */
- N_("Cycles"), /* name */
- "FMod_Cycles", /* struct name */
- sizeof(tFCMED_Cycles), /* storage size */
- NULL, /* free data */
- NULL, /* copy data */
- fcm_cycles_new_data, /* new data */
- NULL /*fcm_cycles_verify*/, /* verify */
- fcm_cycles_time, /* evaluate time */
- fcm_cycles_evaluate, /* evaluate */
+ FMODIFIER_TYPE_CYCLES, /* type */
+ sizeof(FMod_Cycles), /* size */
+ FMI_TYPE_EXTRAPOLATION, /* action type */
+ FMI_REQUIRES_ORIGINAL_DATA, /* requirements */
+ CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Cycles"), /* name */
+ "FMod_Cycles", /* struct name */
+ sizeof(tFCMED_Cycles), /* storage size */
+ NULL, /* free data */
+ NULL, /* copy data */
+ fcm_cycles_new_data, /* new data */
+ NULL /*fcm_cycles_verify*/, /* verify */
+ fcm_cycles_time, /* evaluate time */
+ fcm_cycles_evaluate, /* evaluate */
};
/* Noise F-Curve Modifier --------------------------- */
@@ -1127,7 +1127,7 @@ FModifier *add_fmodifier(ListBase *modifiers, int type, FCurve *owner_fcu)
/* update the fcurve if the Cycles modifier is added */
if ((owner_fcu) && (type == FMODIFIER_TYPE_CYCLES)) {
- calchandles_fcurve(owner_fcu);
+ BKE_fcurve_handles_recalc(owner_fcu);
}
/* return modifier for further editing */
@@ -1215,7 +1215,7 @@ bool remove_fmodifier(ListBase *modifiers, FModifier *fcm)
/* update the fcurve if the Cycles modifier is removed */
if (update_fcu) {
- calchandles_fcurve(update_fcu);
+ BKE_fcurve_handles_recalc(update_fcu);
}
return true;
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc
deleted file mode 100644
index 0d899ec7b06..00000000000
--- a/source/blender/blenkernel/intern/geometry_component_curve.cc
+++ /dev/null
@@ -1,1463 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_task.hh"
-
-#include "DNA_ID_enums.h"
-#include "DNA_curve_types.h"
-
-#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::GMutableSpan;
-using blender::GSpan;
-using blender::GVArray;
-using blender::GVArraySpan;
-
-/* -------------------------------------------------------------------- */
-/** \name Geometry Component Implementation
- * \{ */
-
-CurveComponentLegacy::CurveComponentLegacy() : GeometryComponent(GEO_COMPONENT_TYPE_CURVE)
-{
-}
-
-CurveComponentLegacy::~CurveComponentLegacy()
-{
- this->clear();
-}
-
-GeometryComponent *CurveComponentLegacy::copy() const
-{
- CurveComponentLegacy *new_component = new CurveComponentLegacy();
- if (curve_ != nullptr) {
- new_component->curve_ = new CurveEval(*curve_);
- new_component->ownership_ = GeometryOwnershipType::Owned;
- }
- return new_component;
-}
-
-void CurveComponentLegacy::clear()
-{
- BLI_assert(this->is_mutable());
- if (curve_ != nullptr) {
- if (ownership_ == GeometryOwnershipType::Owned) {
- delete curve_;
- }
- curve_ = nullptr;
- }
-}
-
-bool CurveComponentLegacy::has_curve() const
-{
- return curve_ != nullptr;
-}
-
-void CurveComponentLegacy::replace(CurveEval *curve, GeometryOwnershipType ownership)
-{
- BLI_assert(this->is_mutable());
- this->clear();
- curve_ = curve;
- ownership_ = ownership;
-}
-
-CurveEval *CurveComponentLegacy::release()
-{
- BLI_assert(this->is_mutable());
- CurveEval *curve = curve_;
- curve_ = nullptr;
- return curve;
-}
-
-const CurveEval *CurveComponentLegacy::get_for_read() const
-{
- return curve_;
-}
-
-CurveEval *CurveComponentLegacy::get_for_write()
-{
- BLI_assert(this->is_mutable());
- if (ownership_ == GeometryOwnershipType::ReadOnly) {
- curve_ = new CurveEval(*curve_);
- ownership_ = GeometryOwnershipType::Owned;
- }
- return curve_;
-}
-
-bool CurveComponentLegacy::is_empty() const
-{
- return curve_ == nullptr;
-}
-
-bool CurveComponentLegacy::owns_direct_data() const
-{
- return ownership_ == GeometryOwnershipType::Owned;
-}
-
-void CurveComponentLegacy::ensure_owns_direct_data()
-{
- BLI_assert(this->is_mutable());
- if (ownership_ != GeometryOwnershipType::Owned) {
- curve_ = new CurveEval(*curve_);
- ownership_ = GeometryOwnershipType::Owned;
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Attribute Access Helper Functions
- * \{ */
-
-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();
-}
-
-/**
- * A spline is selected if all of its control points were selected.
- *
- * \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<>
-void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
- const VArray<bool> &old_values,
- MutableSpan<bool> r_values)
-{
- const int splines_len = curve.splines().size();
- Array<int> offsets = curve.control_point_offsets();
- BLI_assert(r_values.size() == splines_len);
-
- r_values.fill(true);
-
- 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)) {
- if (!old_values[spline_offset + i_point]) {
- r_values[i_spline] = false;
- break;
- }
- }
- }
-}
-
-static GVArray adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArray varray)
-{
- GVArray 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 = VArray<T>::ForContainer(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 VArrayImpl<T> {
- GVArray original_varray_;
- /* 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. */
- VArraySpan<T> original_data_;
- Array<int> offsets_;
-
- public:
- VArray_For_SplineToPoint(GVArray original_varray, Array<int> offsets)
- : VArrayImpl<T>(offsets.last()),
- original_varray_(std::move(original_varray)),
- original_data_(original_varray_.typed<T>()),
- offsets_(std::move(offsets))
- {
- }
-
- T get(const int64_t index) const final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- return original_data_[indices.spline_index];
- }
-
- void materialize(const IndexMask mask, MutableSpan<T> r_span) const final
- {
- const int total_num = offsets_.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
- 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(const IndexMask mask, MutableSpan<T> r_span) const final
- {
- T *dst = r_span.data();
- const int total_num = offsets_.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
- 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 GVArray adapt_curve_domain_spline_to_point(const CurveEval &curve, GVArray varray)
-{
- GVArray 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 = VArray<T>::template For<VArray_For_SplineToPoint<T>>(std::move(varray),
- std::move(offsets));
- });
- return new_varray;
-}
-
-} // namespace blender::bke
-
-static GVArray adapt_curve_attribute_domain(const CurveEval &curve,
- const GVArray &varray,
- const eAttrDomain from_domain,
- const eAttrDomain to_domain)
-{
- if (!varray) {
- return {};
- }
- if (varray.is_empty()) {
- 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 {};
-}
-
-/** \} */
-
-namespace blender::bke {
-
-/* -------------------------------------------------------------------- */
-/** \name Builtin Spline Attributes
- *
- * Attributes with a value for every spline, stored contiguously or in every spline separately.
- * \{ */
-
-class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider {
- using AsReadAttribute = GVArray (*)(const CurveEval &data);
- using AsWriteAttribute = GVMutableArray (*)(CurveEval &data);
- const AsReadAttribute as_read_attribute_;
- const AsWriteAttribute as_write_attribute_;
-
- public:
- BuiltinSplineAttributeProvider(std::string attribute_name,
- const eCustomDataType 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)
- {
- }
-
- GVArray try_get_for_read(const void *owner) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
- return as_read_attribute_(*curve);
- }
-
- GAttributeWriter try_get_for_write(void *owner) const final
- {
- if (writable_ != Writable) {
- return {};
- }
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
- return {as_write_attribute_(*curve), domain_};
- }
-
- bool try_delete(void *UNUSED(owner)) const final
- {
- return false;
- }
-
- bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final
- {
- return false;
- }
-
- bool exists(const void *owner) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- return !curve->splines().is_empty();
- }
-};
-
-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 GVArray make_resolution_read_attribute(const CurveEval &curve)
-{
- return VArray<int>::ForDerivedSpan<SplinePtr, get_spline_resolution>(curve.splines());
-}
-
-static GVMutableArray make_resolution_write_attribute(CurveEval &curve)
-{
- return VMutableArray<int>::
- ForDerivedSpan<SplinePtr, 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 GVArray make_cyclic_read_attribute(const CurveEval &curve)
-{
- return VArray<bool>::ForDerivedSpan<SplinePtr, get_cyclic_value>(curve.splines());
-}
-
-static GVMutableArray make_cyclic_write_attribute(CurveEval &curve)
-{
- return VMutableArray<bool>::ForDerivedSpan<SplinePtr, 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.
- * \{ */
-
-/**
- * Individual spans in \a data may be empty if that spline contains no data for the attribute.
- */
-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_num = offsets.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
- for (const int spline_index : data.index_range()) {
- const int offset = offsets[spline_index];
- const int next_offset = offsets[spline_index + 1];
-
- Span<T> src = data[spline_index];
- MutableSpan<T> dst = r_span.slice(offset, next_offset - offset);
- if (src.is_empty()) {
- dst.fill(T());
- }
- else {
- dst.copy_from(src);
- }
- }
- }
- else {
- int spline_index = 0;
- for (const int dst_index : mask) {
- /* Skip splines that don't have any control points in the mask. */
- while (dst_index >= offsets[spline_index + 1]) {
- spline_index++;
- }
-
- const int index_in_spline = dst_index - offsets[spline_index];
- Span<T> src = data[spline_index];
- if (src.is_empty()) {
- r_span[dst_index] = T();
- }
- else {
- r_span[dst_index] = src[index_in_spline];
- }
- }
- }
-}
-
-/**
- * Individual spans in \a data may be empty if that spline contains no data for the attribute.
- */
-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_num = offsets.last();
- if (mask.is_range() && mask.as_range() == IndexRange(total_num)) {
- for (const int spline_index : data.index_range()) {
- const int offset = offsets[spline_index];
- const int next_offset = offsets[spline_index + 1];
-
- Span<T> src = data[spline_index];
- if (src.is_empty()) {
- uninitialized_fill_n(dst + offset, next_offset - offset, T());
- }
- else {
- uninitialized_copy_n(src.data(), next_offset - offset, dst + offset);
- }
- }
- }
- else {
- int spline_index = 0;
- for (const int dst_index : mask) {
- /* Skip splines that don't have any control points in the mask. */
- while (dst_index >= offsets[spline_index + 1]) {
- spline_index++;
- }
-
- const int index_in_spline = dst_index - offsets[spline_index];
- Span<T> src = data[spline_index];
- if (src.is_empty()) {
- new (dst + dst_index) T();
- }
- else {
- new (dst + dst_index) T(src[index_in_spline]);
- }
- }
- }
-}
-
-static GVArray varray_from_initializer(const AttributeInit &initializer,
- const eCustomDataType data_type,
- const Span<SplinePtr> splines)
-{
- 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;
- case AttributeInit::Type::MoveArray:
- int total_num = 0;
- for (const SplinePtr &spline : splines) {
- total_num += spline->size();
- }
- return GVArray::ForSpan(GSpan(*bke::custom_data_type_to_cpp_type(data_type),
- static_cast<const AttributeInitMove &>(initializer).data,
- total_num));
- }
- BLI_assert_unreachable();
- return {};
-}
-
-static bool create_point_attribute(CurveEval *curve,
- const AttributeIDRef &attribute_id,
- const AttributeInit &initializer,
- const eCustomDataType data_type)
-{
- 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.first()->attributes.create_by_move(attribute_id, 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_id, 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;
- }
-
- GAttributeWriter write_attribute = curve->attributes_for_write().lookup_for_write(attribute_id);
- /* We just created the attribute, it should exist. */
- BLI_assert(write_attribute);
-
- GVArray source_varray = varray_from_initializer(initializer, data_type, splines);
- /* TODO: When we can call a variant of #set_all with a virtual array argument,
- * this theoretically unnecessary materialize step could be removed. */
- GVArraySpan source_VArraySpan{source_varray};
- write_attribute.varray.set_all(source_VArraySpan.data());
- write_attribute.finish();
-
- if (initializer.type == AttributeInit::Type::MoveArray) {
- MEM_freeN(static_cast<const AttributeInitMove &>(initializer).data);
- }
-
- return true;
-}
-
-static bool remove_point_attribute(CurveEval *curve, const AttributeIDRef &attribute_id)
-{
- if (curve == nullptr) {
- return false;
- }
-
- /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */
- bool layer_freed = false;
- for (SplinePtr &spline : curve->splines()) {
- layer_freed = spline->attributes.remove(attribute_id);
- }
- return layer_freed;
-}
-
-/**
- * Mutable virtual array for any control point data accessed with spans and an offset array.
- */
-template<typename T> class VArrayImpl_For_SplinePoints final : public VMutableArrayImpl<T> {
- private:
- Array<MutableSpan<T>> data_;
- Array<int> offsets_;
-
- public:
- VArrayImpl_For_SplinePoints(Array<MutableSpan<T>> data, Array<int> offsets)
- : VMutableArrayImpl<T>(offsets.last()), data_(std::move(data)), offsets_(std::move(offsets))
- {
- }
-
- T get(const int64_t index) const final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- return data_[indices.spline_index][indices.point_index];
- }
-
- void set(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(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(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(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> VArray<T> point_data_varray(Array<MutableSpan<T>> spans, Array<int> offsets)
-{
- return VArray<T>::template For<VArrayImpl_For_SplinePoints<T>>(std::move(spans),
- std::move(offsets));
-}
-
-template<typename T>
-VMutableArray<T> point_data_varray_mutable(Array<MutableSpan<T>> spans, Array<int> offsets)
-{
- return VMutableArray<T>::template For<VArrayImpl_For_SplinePoints<T>>(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 VArrayImpl_For_SplinePosition final : public VMutableArrayImpl<float3> {
- private:
- MutableSpan<SplinePtr> splines_;
- Array<int> offsets_;
-
- public:
- VArrayImpl_For_SplinePosition(MutableSpan<SplinePtr> splines, Array<int> offsets)
- : VMutableArrayImpl<float3>(offsets.last()), splines_(splines), offsets_(std::move(offsets))
- {
- }
-
- float3 get(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(const int64_t index, float3 value) final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- Spline &spline = *splines_[indices.spline_index];
- spline.positions()[indices.point_index] = value;
- }
-
- void set_all(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];
- 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(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(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);
- }
-};
-
-class VArrayImpl_For_BezierHandles final : public VMutableArrayImpl<float3> {
- private:
- MutableSpan<SplinePtr> splines_;
- Array<int> offsets_;
- bool is_right_;
-
- public:
- VArrayImpl_For_BezierHandles(MutableSpan<SplinePtr> splines,
- Array<int> offsets,
- const bool is_right)
- : VMutableArrayImpl<float3>(offsets.last()),
- splines_(splines),
- offsets_(std::move(offsets)),
- is_right_(is_right)
- {
- }
-
- float3 get(const int64_t index) const final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- const Spline &spline = *splines_[indices.spline_index];
- if (spline.type() == CURVE_TYPE_BEZIER) {
- const BezierSpline &bezier_spline = static_cast<const BezierSpline &>(spline);
- return is_right_ ? bezier_spline.handle_positions_right()[indices.point_index] :
- bezier_spline.handle_positions_left()[indices.point_index];
- }
- return float3(0);
- }
-
- void set(const int64_t index, float3 value) final
- {
- const PointIndices indices = lookup_point_indices(offsets_, index);
- Spline &spline = *splines_[indices.spline_index];
- if (spline.type() == CURVE_TYPE_BEZIER) {
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
- if (is_right_) {
- bezier_spline.handle_positions_right()[indices.point_index] = value;
- }
- else {
- bezier_spline.handle_positions_left()[indices.point_index] = value;
- }
- bezier_spline.mark_cache_invalid();
- }
- }
-
- void set_all(Span<float3> src) final
- {
- for (const int spline_index : splines_.index_range()) {
- Spline &spline = *splines_[spline_index];
- if (spline.type() == CURVE_TYPE_BEZIER) {
- const int offset = offsets_[spline_index];
-
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
- if (is_right_) {
- for (const int i : IndexRange(bezier_spline.size())) {
- bezier_spline.handle_positions_right()[i] = src[offset + i];
- }
- }
- else {
- for (const int i : IndexRange(bezier_spline.size())) {
- bezier_spline.handle_positions_left()[i] = src[offset + i];
- }
- }
- bezier_spline.mark_cache_invalid();
- }
- }
- }
-
- void materialize(const IndexMask mask, MutableSpan<float3> r_span) const final
- {
- Array<Span<float3>> spans = get_handle_spans(splines_, is_right_);
- point_attribute_materialize(spans.as_span(), offsets_, mask, r_span);
- }
-
- void materialize_to_uninitialized(const IndexMask mask, MutableSpan<float3> r_span) const final
- {
- Array<Span<float3>> spans = get_handle_spans(splines_, is_right_);
- point_attribute_materialize_to_uninitialized(spans.as_span(), offsets_, mask, r_span);
- }
-
- /**
- * Utility so we can pass handle positions to the materialize functions above.
- *
- * \note This relies on the ability of the materialize implementations to
- * handle empty spans, since only Bezier splines have handles.
- */
- static Array<Span<float3>> get_handle_spans(Span<SplinePtr> splines, const bool is_right)
- {
- Array<Span<float3>> spans(splines.size());
- for (const int i : spans.index_range()) {
- if (splines[i]->type() == CURVE_TYPE_BEZIER) {
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(*splines[i]);
- spans[i] = is_right ? bezier_spline.handle_positions_right() :
- bezier_spline.handle_positions_left();
- }
- else {
- spans[i] = {};
- }
- }
- return spans;
- }
-};
-
-/**
- * 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_;
- bool stored_in_custom_data_;
-
- public:
- BuiltinPointAttributeProvider(std::string attribute_name,
- const CreatableEnum creatable,
- const DeletableEnum deletable,
- const GetSpan get_span,
- const GetMutableSpan get_mutable_span,
- const UpdateOnWrite update_on_write,
- const bool stored_in_custom_data)
- : BuiltinAttributeProvider(std::move(attribute_name),
- ATTR_DOMAIN_POINT,
- bke::cpp_type_to_custom_data_type(CPPType::get<T>()),
- creatable,
- WritableEnum::Writable,
- deletable),
- get_span_(get_span),
- get_mutable_span_(get_mutable_span),
- update_on_write_(update_on_write),
- stored_in_custom_data_(stored_in_custom_data)
- {
- }
-
- GVArray try_get_for_read(const void *owner) const override
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- if (!this->exists(owner)) {
- return {};
- }
-
- Span<SplinePtr> splines = curve->splines();
- if (splines.size() == 1) {
- return GVArray::ForSpan(get_span_(*splines.first()));
- }
-
- Array<int> offsets = curve->control_point_offsets();
- Array<MutableSpan<T>> spans(splines.size());
- for (const int i : splines.index_range()) {
- Span<T> span = get_span_(*splines[i]);
- /* Use const-cast because the underlying virtual array implementation is shared between const
- * and non const data. */
- spans[i] = MutableSpan<T>(const_cast<T *>(span.data()), span.size());
- }
-
- return point_data_varray(spans, offsets);
- }
-
- GAttributeWriter try_get_for_write(void *owner) const override
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- if (!this->exists(owner)) {
- return {};
- }
-
- std::function<void()> tag_modified_fn;
- if (update_on_write_ != nullptr) {
- tag_modified_fn = [curve, update = update_on_write_]() {
- for (SplinePtr &spline : curve->splines()) {
- update(*spline);
- }
- };
- }
-
- MutableSpan<SplinePtr> splines = curve->splines();
- if (splines.size() == 1) {
- return {GVMutableArray::ForSpan(get_mutable_span_(*splines.first())),
- domain_,
- std::move(tag_modified_fn)};
- }
-
- 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]);
- }
-
- return {point_data_varray_mutable(spans, offsets), domain_, tag_modified_fn};
- }
-
- bool try_delete(void *owner) const final
- {
- if (deletable_ == DeletableEnum::NonDeletable) {
- return false;
- }
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return remove_point_attribute(curve, name_);
- }
-
- bool try_create(void *owner, const AttributeInit &initializer) const final
- {
- if (createable_ == CreatableEnum::NonCreatable) {
- return false;
- }
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return create_point_attribute(curve, name_, initializer, CD_PROP_INT32);
- }
-
- bool exists(const void *owner) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return false;
- }
-
- Span<SplinePtr> splines = curve->splines();
- if (splines.size() == 0) {
- return false;
- }
-
- if (stored_in_custom_data_) {
- if (!curve->splines().first()->attributes.get_for_read(name_)) {
- return false;
- }
- }
-
- bool has_point = false;
- for (const SplinePtr &spline : curve->splines()) {
- if (spline->size() != 0) {
- has_point = true;
- break;
- }
- }
-
- if (!has_point) {
- return false;
- }
-
- return true;
- }
-};
-
-/**
- * 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::NonCreatable,
- BuiltinAttributeProvider::NonDeletable,
- [](const Spline &spline) { return spline.positions(); },
- [](Spline &spline) { return spline.positions(); },
- [](Spline &spline) { spline.mark_cache_invalid(); },
- false)
- {
- }
-
- GAttributeWriter try_get_for_write(void *owner) const final
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- /* 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_spline_with_type(CURVE_TYPE_BEZIER)) {
- return BuiltinPointAttributeProvider<float3>::try_get_for_write(owner);
- }
-
- auto tag_modified_fn = [curve]() {
- /* 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. */
- curve->mark_cache_invalid();
- };
-
- Array<int> offsets = curve->control_point_offsets();
- return {VMutableArray<float3>::For<VArrayImpl_For_SplinePosition>(curve->splines(),
- std::move(offsets)),
- domain_,
- tag_modified_fn};
- }
-};
-
-class BezierHandleAttributeProvider : public BuiltinAttributeProvider {
- private:
- bool is_right_;
-
- public:
- BezierHandleAttributeProvider(const bool is_right)
- : BuiltinAttributeProvider(is_right ? "handle_right" : "handle_left",
- ATTR_DOMAIN_POINT,
- CD_PROP_FLOAT3,
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::Writable,
- BuiltinAttributeProvider::NonDeletable),
- is_right_(is_right)
- {
- }
-
- GVArray try_get_for_read(const void *owner) const override
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
- return {};
- }
-
- Array<int> offsets = curve->control_point_offsets();
- /* Use const-cast because the underlying virtual array implementation is shared between const
- * and non const data. */
- return VArray<float3>::For<VArrayImpl_For_BezierHandles>(
- const_cast<CurveEval *>(curve)->splines(), std::move(offsets), is_right_);
- }
-
- GAttributeWriter try_get_for_write(void *owner) const override
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- if (curve == nullptr) {
- return {};
- }
-
- if (!curve->has_spline_with_type(CURVE_TYPE_BEZIER)) {
- return {};
- }
-
- auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); };
-
- Array<int> offsets = curve->control_point_offsets();
- return {VMutableArray<float3>::For<VArrayImpl_For_BezierHandles>(
- curve->splines(), std::move(offsets), is_right_),
- domain_,
- tag_modified_fn};
- }
-
- bool try_delete(void *UNUSED(owner)) const final
- {
- return false;
- }
-
- bool try_create(void *UNUSED(owner), const AttributeInit &UNUSED(initializer)) const final
- {
- return false;
- }
-
- bool exists(const void *owner) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- if (curve == nullptr) {
- return false;
- }
-
- CurveComponentLegacy component;
- component.replace(const_cast<CurveEval *>(curve), GeometryOwnershipType::ReadOnly);
-
- return curve->has_spline_with_type(CURVE_TYPE_BEZIER) && !curve->splines().is_empty();
- }
-};
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \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 |
- CD_MASK_PROP_INT8;
-
- public:
- GAttributeReader try_get_for_read(const void *owner,
- const AttributeIDRef &attribute_id) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- 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_id);
- 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_id);
- 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 {GVArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT};
- }
-
- GAttributeReader 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()) {
- Span<T> span = spans[i].typed<T>();
- /* Use const-cast because the underlying virtual array implementation is shared between
- * const and non const data. */
- data[i] = MutableSpan<T>(const_cast<T *>(span.data()), span.size());
- BLI_assert(data[i].data() != nullptr);
- }
- attribute = {point_data_varray(data, offsets), ATTR_DOMAIN_POINT};
- });
- return attribute;
- }
-
- /* This function is almost the same as #try_get_for_read, but without const. */
- GAttributeWriter try_get_for_write(void *owner, const AttributeIDRef &attribute_id) const final
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- 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_id);
- 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_id);
- 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 {GVMutableArray::ForSpan(spans.first()), ATTR_DOMAIN_POINT};
- }
-
- GAttributeWriter 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_varray_mutable(data, offsets), ATTR_DOMAIN_POINT};
- });
- return attribute;
- }
-
- bool try_delete(void *owner, const AttributeIDRef &attribute_id) const final
- {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return remove_point_attribute(curve, attribute_id);
- }
-
- bool try_create(void *owner,
- const AttributeIDRef &attribute_id,
- const eAttrDomain domain,
- const eCustomDataType data_type,
- const AttributeInit &initializer) const final
- {
- BLI_assert(this->type_is_supported(data_type));
- if (domain != ATTR_DOMAIN_POINT) {
- return false;
- }
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return create_point_attribute(curve, attribute_id, initializer, data_type);
- }
-
- bool foreach_attribute(const void *owner, const AttributeForeachCallback callback) const final
- {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- 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(eAttrDomain)> callback) const final
- {
- callback(ATTR_DOMAIN_POINT);
- }
-
- bool type_is_supported(eCustomDataType 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 = {
- [](void *owner) -> CustomData * {
- CurveEval *curve = static_cast<CurveEval *>(owner);
- return curve ? &curve->attributes.data : nullptr;
- },
- [](const void *owner) -> const CustomData * {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- return curve ? &curve->attributes.data : nullptr;
- },
- [](const void *owner) -> int {
- const CurveEval *curve = static_cast<const CurveEval *>(owner);
- return curve->splines().size();
- },
- nullptr};
-
- static CustomDataAttributeProvider spline_custom_data(ATTR_DOMAIN_CURVE,
- spline_custom_data_access);
-
- static PositionAttributeProvider position;
- static BezierHandleAttributeProvider handles_start(false);
- static BezierHandleAttributeProvider handles_end(true);
-
- static BuiltinPointAttributeProvider<int> id(
- "id",
- BuiltinAttributeProvider::Creatable,
- BuiltinAttributeProvider::Deletable,
- [](const Spline &spline) {
- std::optional<GSpan> span = spline.attributes.get_for_read("id");
- return span ? span->typed<int>() : Span<int>();
- },
- [](Spline &spline) {
- std::optional<GMutableSpan> span = spline.attributes.get_for_write("id");
- return span ? span->typed<int>() : MutableSpan<int>();
- },
- {},
- true);
-
- static BuiltinPointAttributeProvider<float> radius(
- "radius",
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::NonDeletable,
- [](const Spline &spline) { return spline.radii(); },
- [](Spline &spline) { return spline.radii(); },
- nullptr,
- false);
-
- static BuiltinPointAttributeProvider<float> tilt(
- "tilt",
- BuiltinAttributeProvider::NonCreatable,
- BuiltinAttributeProvider::NonDeletable,
- [](const Spline &spline) { return spline.tilts(); },
- [](Spline &spline) { return spline.tilts(); },
- [](Spline &spline) { spline.mark_cache_invalid(); },
- false);
-
- static DynamicPointAttributeProvider point_custom_data;
-
- return ComponentAttributeProviders(
- {&position, &id, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic},
- {&spline_custom_data, &point_custom_data});
-}
-
-/** \} */
-
-static AttributeAccessorFunctions get_curve_accessor_functions()
-{
- static const ComponentAttributeProviders providers = create_attribute_providers_for_curve();
- AttributeAccessorFunctions fn =
- attribute_accessor_functions::accessor_functions_for_providers<providers>();
- fn.domain_size = [](const void *owner, const eAttrDomain domain) -> int {
- if (owner == nullptr) {
- return 0;
- }
- const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner);
- switch (domain) {
- case ATTR_DOMAIN_POINT:
- return curve_eval.total_control_point_num();
- case ATTR_DOMAIN_CURVE:
- return curve_eval.splines().size();
- default:
- return 0;
- }
- };
- fn.domain_supported = [](const void *UNUSED(owner), const eAttrDomain domain) {
- return ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
- };
- fn.adapt_domain = [](const void *owner,
- const blender::GVArray &varray,
- const eAttrDomain from_domain,
- const eAttrDomain to_domain) -> GVArray {
- if (owner == nullptr) {
- return {};
- }
- const CurveEval &curve_eval = *static_cast<const CurveEval *>(owner);
- return adapt_curve_attribute_domain(curve_eval, varray, from_domain, to_domain);
- };
- return fn;
-}
-
-static const AttributeAccessorFunctions &get_curve_accessor_functions_ref()
-{
- static const AttributeAccessorFunctions fn = get_curve_accessor_functions();
- return fn;
-}
-
-} // namespace blender::bke
-
-std::optional<blender::bke::AttributeAccessor> CurveComponentLegacy::attributes() const
-{
- return blender::bke::AttributeAccessor(curve_, blender::bke::get_curve_accessor_functions_ref());
-}
-
-std::optional<blender::bke::MutableAttributeAccessor> CurveComponentLegacy::attributes_for_write()
-{
- return blender::bke::MutableAttributeAccessor(curve_,
- blender::bke::get_curve_accessor_functions_ref());
-}
-
-blender::bke::MutableAttributeAccessor CurveEval::attributes_for_write()
-{
- return blender::bke::MutableAttributeAccessor(this,
- blender::bke::get_curve_accessor_functions_ref());
-}
diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc
index 34c17bedc2c..4ace68546ac 100644
--- a/source/blender/blenkernel/intern/geometry_component_curves.cc
+++ b/source/blender/blenkernel/intern/geometry_component_curves.cc
@@ -12,6 +12,8 @@
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
+#include "FN_multi_function_builder.hh"
+
#include "attribute_access_intern.hh"
using blender::GVArray;
@@ -206,18 +208,11 @@ static Array<float3> curve_normal_point_domain(const bke::CurvesGeometry &curves
return results;
}
-VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttrDomain domain)
+VArray<float3> curve_normals_varray(const CurvesGeometry &curves, const eAttrDomain domain)
{
- if (!component.has_curves()) {
- return {};
- }
-
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
const VArray<int8_t> types = curves.curve_types();
if (curves.is_single_type(CURVE_TYPE_POLY)) {
- return component.attributes()->adapt_domain<float3>(
+ return curves.adapt_domain<float3>(
VArray<float3>::ForSpan(curves.evaluated_normals()), ATTR_DOMAIN_POINT, domain);
}
@@ -228,7 +223,7 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttr
}
if (domain == ATTR_DOMAIN_CURVE) {
- return component.attributes()->adapt_domain<float3>(
+ return curves.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(normals)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
}
@@ -241,15 +236,9 @@ VArray<float3> curve_normals_varray(const CurveComponent &component, const eAttr
/** \name Curve Length Field Input
* \{ */
-static VArray<float> construct_curve_length_gvarray(const CurveComponent &component,
+static VArray<float> construct_curve_length_gvarray(const CurvesGeometry &curves,
const eAttrDomain domain)
{
- if (!component.has_curves()) {
- return {};
- }
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
curves.ensure_evaluated_lengths();
VArray<bool> cyclic = curves.cyclic();
@@ -263,28 +252,23 @@ static VArray<float> construct_curve_length_gvarray(const CurveComponent &compon
}
if (domain == ATTR_DOMAIN_POINT) {
- return component.attributes()->adapt_domain<float>(
- std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ return curves.adapt_domain<float>(std::move(lengths), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
}
return {};
}
CurveLengthFieldInput::CurveLengthFieldInput()
- : GeometryFieldInput(CPPType::get<float>(), "Spline Length node")
+ : CurvesFieldInput(CPPType::get<float>(), "Spline Length node")
{
category_ = Category::Generated;
}
-GVArray CurveLengthFieldInput::get_varray_for_context(const GeometryComponent &component,
+GVArray CurveLengthFieldInput::get_varray_for_context(const CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_length_gvarray(curve_component, domain);
- }
- return {};
+ return construct_curve_length_gvarray(curves, domain);
}
uint64_t CurveLengthFieldInput::hash() const
@@ -357,10 +341,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
[](const void *owner) -> int {
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
return curves.curves_num();
- },
- [](void *owner) {
- CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
- curves.update_customdata_pointers();
}};
static CustomDataAccessInfo point_access = {
[](void *owner) -> CustomData * {
@@ -374,10 +354,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
[](const void *owner) -> int {
const CurvesGeometry &curves = *static_cast<const CurvesGeometry *>(owner);
return curves.points_num();
- },
- [](void *owner) {
- CurvesGeometry &curves = *static_cast<CurvesGeometry *>(owner);
- curves.update_customdata_pointers();
}};
static BuiltinCustomDataLayerProvider position("position",
@@ -452,6 +428,12 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
make_array_write_attribute<float3>,
tag_component_positions_changed);
+ static const fn::CustomMF_SI_SO<int8_t, int8_t> handle_type_clamp{
+ "Handle Type Validate",
+ [](int8_t value) {
+ return std::clamp<int8_t>(value, BEZIER_HANDLE_FREE, BEZIER_HANDLE_ALIGN);
+ },
+ fn::CustomMF_presets::AllSpanOrSingle()};
static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
ATTR_DOMAIN_POINT,
CD_PROP_INT8,
@@ -462,7 +444,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
point_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
- tag_component_topology_changed);
+ tag_component_topology_changed,
+ AttributeValidator{&handle_type_clamp});
static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left",
ATTR_DOMAIN_POINT,
@@ -474,7 +457,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
point_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
- tag_component_topology_changed);
+ tag_component_topology_changed,
+ AttributeValidator{&handle_type_clamp});
static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight",
ATTR_DOMAIN_POINT,
@@ -488,6 +472,10 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
make_array_write_attribute<float>,
tag_component_positions_changed);
+ static const fn::CustomMF_SI_SO<int8_t, int8_t> nurbs_order_clamp{
+ "NURBS Order Validate",
+ [](int8_t value) { return std::max<int8_t>(value, 0); },
+ fn::CustomMF_presets::AllSpanOrSingle()};
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
@@ -498,8 +486,15 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
curve_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
- tag_component_topology_changed);
+ tag_component_topology_changed,
+ AttributeValidator{&nurbs_order_clamp});
+ static const fn::CustomMF_SI_SO<int8_t, int8_t> normal_mode_clamp{
+ "Normal Mode Validate",
+ [](int8_t value) {
+ return std::clamp<int8_t>(value, NORMAL_MODE_MINIMUM_TWIST, NORMAL_MODE_Z_UP);
+ },
+ fn::CustomMF_presets::AllSpanOrSingle()};
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
@@ -510,8 +505,15 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
curve_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
- tag_component_normals_changed);
+ tag_component_normals_changed,
+ AttributeValidator{&normal_mode_clamp});
+ static const fn::CustomMF_SI_SO<int8_t, int8_t> knots_mode_clamp{
+ "Knots Mode Validate",
+ [](int8_t value) {
+ return std::clamp<int8_t>(value, NURBS_KNOT_MODE_NORMAL, NURBS_KNOT_MODE_ENDPOINT_BEZIER);
+ },
+ fn::CustomMF_presets::AllSpanOrSingle()};
static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
@@ -522,8 +524,15 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
curve_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
- tag_component_topology_changed);
+ tag_component_topology_changed,
+ AttributeValidator{&knots_mode_clamp});
+ static const fn::CustomMF_SI_SO<int8_t, int8_t> curve_type_clamp{
+ "Curve Type Validate",
+ [](int8_t value) {
+ return std::clamp<int8_t>(value, CURVE_TYPE_CATMULL_ROM, CURVE_TYPES_NUM);
+ },
+ fn::CustomMF_presets::AllSpanOrSingle()};
static BuiltinCustomDataLayerProvider curve_type("curve_type",
ATTR_DOMAIN_CURVE,
CD_PROP_INT8,
@@ -534,8 +543,13 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
curve_access,
make_array_read_attribute<int8_t>,
make_array_write_attribute<int8_t>,
- tag_component_curve_types_changed);
+ tag_component_curve_types_changed,
+ AttributeValidator{&curve_type_clamp});
+ static const fn::CustomMF_SI_SO<int, int> resolution_clamp{
+ "Resolution Validate",
+ [](int value) { return std::max<int>(value, 1); },
+ fn::CustomMF_presets::AllSpanOrSingle()};
static BuiltinCustomDataLayerProvider resolution("resolution",
ATTR_DOMAIN_CURVE,
CD_PROP_INT32,
@@ -546,7 +560,8 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
curve_access,
make_array_read_attribute<int>,
make_array_write_attribute<int>,
- tag_component_topology_changed);
+ tag_component_topology_changed,
+ AttributeValidator{&resolution_clamp});
static BuiltinCustomDataLayerProvider cyclic("cyclic",
ATTR_DOMAIN_CURVE,
@@ -644,6 +659,7 @@ std::optional<blender::bke::AttributeAccessor> CurveComponent::attributes() cons
std::optional<blender::bke::MutableAttributeAccessor> CurveComponent::attributes_for_write()
{
- return blender::bke::MutableAttributeAccessor(curves_ ? &curves_->geometry : nullptr,
+ Curves *curves = this->get_for_write();
+ return blender::bke::MutableAttributeAccessor(curves ? &curves->geometry : nullptr,
blender::bke::get_curves_accessor_functions_ref());
}
diff --git a/source/blender/blenkernel/intern/geometry_component_edit_data.cc b/source/blender/blenkernel/intern/geometry_component_edit_data.cc
new file mode 100644
index 00000000000..2c00de3254f
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_component_edit_data.cc
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
+
+using namespace blender;
+using namespace blender::bke;
+
+GeometryComponentEditData::GeometryComponentEditData() : GeometryComponent(GEO_COMPONENT_TYPE_EDIT)
+{
+}
+
+GeometryComponent *GeometryComponentEditData::copy() const
+{
+ GeometryComponentEditData *new_component = new GeometryComponentEditData();
+ if (curves_edit_hints_) {
+ new_component->curves_edit_hints_ = std::make_unique<CurvesEditHints>(*curves_edit_hints_);
+ }
+ return new_component;
+}
+
+bool GeometryComponentEditData::owns_direct_data() const
+{
+ return true;
+}
+
+void GeometryComponentEditData::ensure_owns_direct_data()
+{
+ /* Nothing to do. */
+}
+
+void GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(
+ GeometrySet &geometry)
+{
+ /* This component should be created at the start of object evaluation if it's necessary. */
+ if (!geometry.has<GeometryComponentEditData>()) {
+ return;
+ }
+ GeometryComponentEditData &edit_component =
+ geometry.get_component_for_write<GeometryComponentEditData>();
+ if (!edit_component.curves_edit_hints_) {
+ return;
+ }
+ if (edit_component.curves_edit_hints_->positions.has_value()) {
+ return;
+ }
+ const Curves *curves_id = geometry.get_curves_for_read();
+ if (curves_id == nullptr) {
+ return;
+ }
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ const int points_num = curves.points_num();
+ if (points_num != edit_component.curves_edit_hints_->curves_id_orig.geometry.point_num) {
+ return;
+ }
+ edit_component.curves_edit_hints_->positions.emplace(points_num);
+ edit_component.curves_edit_hints_->positions->as_mutable_span().copy_from(curves.positions());
+}
diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc
index c16311945ba..3a065c3576b 100644
--- a/source/blender/blenkernel/intern/geometry_component_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_component_instances.cc
@@ -444,8 +444,7 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
[](const void *owner) -> int {
const InstancesComponent &inst = *static_cast<const InstancesComponent *>(owner);
return inst.instances_num();
- },
- nullptr};
+ }};
/**
* IDs of the instances. They are used for consistency over multiple frames for things like
diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc
index cb36b9b19f7..255d0e92964 100644
--- a/source/blender/blenkernel/intern/geometry_component_mesh.cc
+++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc
@@ -14,6 +14,8 @@
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
+#include "FN_multi_function_builder.hh"
+
#include "attribute_access_intern.hh"
extern "C" MDeformVert *BKE_object_defgroup_data_create(ID *id);
@@ -115,8 +117,7 @@ void MeshComponent::ensure_owns_direct_data()
namespace blender::bke {
-VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
- const Mesh &mesh,
+VArray<float3> mesh_normals_varray(const Mesh &mesh,
const IndexMask mask,
const eAttrDomain domain)
{
@@ -135,8 +136,8 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
* instead of the GeometryComponent API to avoid calculating unnecessary values and to
* allow normalizing the result more simply. */
Span<float3> vert_normals{(float3 *)BKE_mesh_vertex_normals_ensure(&mesh), mesh.totvert};
+ const Span<MEdge> edges = mesh.edges();
Array<float3> edge_normals(mask.min_array_size());
- Span<MEdge> edges{mesh.medge, mesh.totedge};
for (const int i : mask) {
const MEdge &edge = edges[i];
edge_normals[i] = math::normalize(
@@ -150,7 +151,7 @@ VArray<float3> mesh_normals_varray(const MeshComponent &mesh_component,
* array and copy the face normal for each of its corners. In this case using the mesh
* component's generic domain interpolation is fine, the data will still be normalized,
* since the face normal is just copied to every corner. */
- return mesh_component.attributes()->adapt_domain(
+ return mesh.attributes().adapt_domain(
VArray<float3>::ForSpan({(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly}),
ATTR_DOMAIN_FACE,
ATTR_DOMAIN_CORNER);
@@ -176,11 +177,13 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
+ const Span<MLoop> loops = mesh.loops();
+
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int loop_index : IndexRange(mesh.totloop)) {
const T value = old_values[loop_index];
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
const int point_index = loop.v;
mixer.mix_in(point_index, value);
}
@@ -194,11 +197,13 @@ void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
+ const Span<MLoop> loops = mesh.loops();
+
Array<bool> loose_verts(mesh.totvert, true);
r_values.fill(true);
for (const int loop_index : IndexRange(mesh.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
const int point_index = loop.v;
loose_verts[point_index] = false;
@@ -236,12 +241,14 @@ static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray
*/
static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray &varray)
{
+ const Span<MLoop> loops = mesh.loops();
+
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
new_varray = VArray<T>::ForFunc(mesh.totloop,
- [&mesh, varray = varray.typed<T>()](const int64_t loop_index) {
- const int vertex_index = mesh.mloop[loop_index].v;
+ [loops, varray = varray.typed<T>()](const int64_t loop_index) {
+ const int vertex_index = loops[loop_index].v;
return varray[vertex_index];
});
});
@@ -250,15 +257,17 @@ static GVArray adapt_mesh_domain_point_to_corner(const Mesh &mesh, const GVArray
static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray &varray)
{
+ const Span<MPoly> polys = mesh.polys();
+
GVArray 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>>) {
if constexpr (std::is_same_v<T, bool>) {
new_varray = VArray<T>::ForFunc(
- mesh.totpoly, [&mesh, varray = varray.typed<bool>()](const int face_index) {
+ polys.size(), [polys, varray = varray.typed<bool>()](const int face_index) {
/* A face is selected if all of its corners were selected. */
- const MPoly &poly = mesh.mpoly[face_index];
+ const MPoly &poly = polys[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
if (!varray[loop_index]) {
return false;
@@ -269,10 +278,10 @@ static GVArray adapt_mesh_domain_corner_to_face(const Mesh &mesh, const GVArray
}
else {
new_varray = VArray<T>::ForFunc(
- mesh.totpoly, [&mesh, varray = varray.typed<T>()](const int face_index) {
+ polys.size(), [polys, varray = varray.typed<T>()](const int face_index) {
T return_value;
attribute_math::DefaultMixer<T> mixer({&return_value, 1});
- const MPoly &poly = mesh.mpoly[face_index];
+ const MPoly &poly = polys[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const T value = varray[loop_index];
mixer.mix_in(0, value);
@@ -292,18 +301,23 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
attribute_math::DefaultMixer<T> mixer(r_values);
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
/* For every edge, mix values from the two adjacent corners (the current and next corner). */
- for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const int loop_index_next = (loop_index + 1) % poly.totloop;
- const MLoop &loop = mesh.mloop[loop_index];
+ for (const int i : IndexRange(poly.totloop)) {
+ const int next_i = (i + 1) % poly.totloop;
+ const int loop_i = poly.loopstart + i;
+ const int next_loop_i = poly.loopstart + next_i;
+ const MLoop &loop = loops[loop_i];
const int edge_index = loop.e;
- mixer.mix_in(edge_index, old_values[loop_index]);
- mixer.mix_in(edge_index, old_values[loop_index_next]);
+ mixer.mix_in(edge_index, old_values[loop_i]);
+ mixer.mix_in(edge_index, old_values[next_loop_i]);
}
}
@@ -317,21 +331,26 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
/* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */
Array<bool> loose_edges(mesh.totedge, true);
r_values.fill(true);
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
-
- for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1);
- const MLoop &loop = mesh.mloop[loop_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
+
+ for (const int i : IndexRange(poly.totloop)) {
+ const int next_i = (i + 1) % poly.totloop;
+ const int loop_i = poly.loopstart + i;
+ const int next_loop_i = poly.loopstart + next_i;
+ const MLoop &loop = loops[loop_i];
const int edge_index = loop.e;
+
loose_edges[edge_index] = false;
- if (!old_values[loop_index] || !old_values[loop_index_next]) {
+ if (!old_values[loop_i] || !old_values[next_loop_i]) {
r_values[edge_index] = false;
}
}
@@ -367,13 +386,16 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
attribute_math::DefaultMixer<T> mixer(r_values);
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
const T value = old_values[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
const int point_index = loop.v;
mixer.mix_in(point_index, value);
}
@@ -389,13 +411,15 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
r_values.fill(false);
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
if (old_values[poly_index]) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
const int vert_index = loop.v;
r_values[vert_index] = true;
}
@@ -424,10 +448,11 @@ void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
+ const Span<MPoly> polys = mesh.polys();
- threading::parallel_for(IndexRange(mesh.totpoly), 1024, [&](const IndexRange range) {
+ threading::parallel_for(polys.index_range(), 1024, [&](const IndexRange range) {
for (const int poly_index : range) {
- const MPoly &poly = mesh.mpoly[poly_index];
+ const MPoly &poly = polys[poly_index];
MutableSpan<T> poly_corner_values = r_values.slice(poly.loopstart, poly.totloop);
poly_corner_values.fill(old_values[poly_index]);
}
@@ -454,13 +479,16 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
attribute_math::DefaultMixer<T> mixer(r_values);
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
const T value = old_values[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
mixer.mix_in(loop.e, value);
}
}
@@ -474,13 +502,15 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totedge);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
r_values.fill(false);
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
if (old_values[poly_index]) {
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
const int edge_index = loop.e;
r_values[edge_index] = true;
}
@@ -504,17 +534,20 @@ static GVArray adapt_mesh_domain_face_to_edge(const Mesh &mesh, const GVArray &v
static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &varray)
{
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
GVArray 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>>) {
if constexpr (std::is_same_v<T, bool>) {
new_varray = VArray<T>::ForFunc(
- mesh.totpoly, [&mesh, varray = varray.typed<bool>()](const int face_index) {
+ mesh.totpoly, [loops, polys, varray = varray.typed<bool>()](const int face_index) {
/* A face is selected if all of its vertices were selected. */
- const MPoly &poly = mesh.mpoly[face_index];
+ const MPoly &poly = polys[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
if (!varray[loop.v]) {
return false;
}
@@ -524,12 +557,12 @@ static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &
}
else {
new_varray = VArray<T>::ForFunc(
- mesh.totpoly, [&mesh, varray = varray.typed<T>()](const int face_index) {
+ mesh.totpoly, [loops, polys, varray = varray.typed<T>()](const int face_index) {
T return_value;
attribute_math::DefaultMixer<T> mixer({&return_value, 1});
- const MPoly &poly = mesh.mpoly[face_index];
+ const MPoly &poly = polys[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
const T value = varray[loop.v];
mixer.mix_in(0, value);
}
@@ -544,6 +577,8 @@ static GVArray adapt_mesh_domain_point_to_face(const Mesh &mesh, const GVArray &
static GVArray adapt_mesh_domain_point_to_edge(const Mesh &mesh, const GVArray &varray)
{
+ const Span<MEdge> edges = mesh.edges();
+
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
@@ -551,17 +586,17 @@ static GVArray adapt_mesh_domain_point_to_edge(const Mesh &mesh, const GVArray &
if constexpr (std::is_same_v<T, bool>) {
/* An edge is selected if both of its vertices were selected. */
new_varray = VArray<bool>::ForFunc(
- mesh.totedge, [&mesh, varray = varray.typed<bool>()](const int edge_index) {
- const MEdge &edge = mesh.medge[edge_index];
+ edges.size(), [edges, varray = varray.typed<bool>()](const int edge_index) {
+ const MEdge &edge = edges[edge_index];
return varray[edge.v1] && varray[edge.v2];
});
}
else {
new_varray = VArray<T>::ForFunc(
- mesh.totedge, [&mesh, varray = varray.typed<T>()](const int edge_index) {
+ edges.size(), [edges, varray = varray.typed<T>()](const int edge_index) {
T return_value;
attribute_math::DefaultMixer<T> mixer({&return_value, 1});
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[edge_index];
mixer.mix_in(0, varray[edge.v1]);
mixer.mix_in(0, varray[edge.v2]);
mixer.finalize();
@@ -579,16 +614,19 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
attribute_math::DefaultMixer<T> mixer(r_values);
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
/* 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 + (loop_index == poly.loopstart) * poly.totloop;
- const MLoop &loop = mesh.mloop[loop_index];
- const MLoop &loop_prev = mesh.mloop[loop_index_prev];
+ const MLoop &loop = loops[loop_index];
+ const MLoop &loop_prev = loops[loop_index_prev];
mixer.mix_in(loop_index, old_values[loop.e]);
mixer.mix_in(loop_index, old_values[loop_prev.e]);
}
@@ -604,15 +642,17 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totloop);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
r_values.fill(false);
- for (const int poly_index : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
for (const int loop_index : IndexRange(poly.loopstart, 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];
+ const MLoop &loop = loops[loop_index];
+ const MLoop &loop_prev = loops[loop_index_prev];
if (old_values[loop.e] && old_values[loop_prev.e]) {
r_values[loop_index] = true;
}
@@ -640,10 +680,12 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
MutableSpan<T> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
+ const Span<MEdge> edges = mesh.edges();
+
attribute_math::DefaultMixer<T> mixer(r_values);
for (const int edge_index : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[edge_index];
const T value = old_values[edge_index];
mixer.mix_in(edge.v1, value);
mixer.mix_in(edge.v2, value);
@@ -659,10 +701,11 @@ void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
MutableSpan<bool> r_values)
{
BLI_assert(r_values.size() == mesh.totvert);
+ const Span<MEdge> edges = mesh.edges();
r_values.fill(false);
- for (const int edge_index : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[edge_index];
+ for (const int edge_index : edges.index_range()) {
+ const MEdge &edge = edges[edge_index];
if (old_values[edge_index]) {
r_values[edge.v1] = true;
r_values[edge.v2] = true;
@@ -686,6 +729,9 @@ static GVArray adapt_mesh_domain_edge_to_point(const Mesh &mesh, const GVArray &
static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &varray)
{
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
GVArray new_varray;
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
using T = decltype(dummy);
@@ -693,10 +739,10 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v
if constexpr (std::is_same_v<T, bool>) {
/* A face is selected if all of its edges are selected. */
new_varray = VArray<bool>::ForFunc(
- mesh.totpoly, [&mesh, varray = varray.typed<T>()](const int face_index) {
- const MPoly &poly = mesh.mpoly[face_index];
+ polys.size(), [loops, polys, varray = varray.typed<T>()](const int face_index) {
+ const MPoly &poly = polys[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
if (!varray[loop.e]) {
return false;
}
@@ -706,12 +752,12 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v
}
else {
new_varray = VArray<T>::ForFunc(
- mesh.totpoly, [&mesh, varray = varray.typed<T>()](const int face_index) {
+ polys.size(), [loops, polys, varray = varray.typed<T>()](const int face_index) {
T return_value;
attribute_math::DefaultMixer<T> mixer({&return_value, 1});
- const MPoly &poly = mesh.mpoly[face_index];
+ const MPoly &poly = polys[face_index];
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
+ const MLoop &loop = loops[loop_index];
const T value = varray[loop.e];
mixer.mix_in(0, value);
}
@@ -838,16 +884,6 @@ static void tag_component_positions_changed(void *owner)
}
}
-static int get_material_index(const MPoly &mpoly)
-{
- return static_cast<int>(mpoly.mat_nr);
-}
-
-static void set_material_index(MPoly &mpoly, int index)
-{
- mpoly.mat_nr = static_cast<short>(std::clamp(index, 0, SHRT_MAX));
-}
-
static bool get_shade_smooth(const MPoly &mpoly)
{
return mpoly.flag & ME_SMOOTH;
@@ -884,8 +920,15 @@ class VArrayImpl_For_VertexWeights final : public VMutableArrayImpl<float> {
const int dvert_index_;
public:
- VArrayImpl_For_VertexWeights(MDeformVert *dverts, const int totvert, const int dvert_index)
- : VMutableArrayImpl<float>(totvert), dverts_(dverts), dvert_index_(dvert_index)
+ VArrayImpl_For_VertexWeights(MutableSpan<MDeformVert> dverts, const int dvert_index)
+ : VMutableArrayImpl<float>(dverts.size()), dverts_(dverts.data()), dvert_index_(dvert_index)
+ {
+ }
+
+ VArrayImpl_For_VertexWeights(Span<MDeformVert> dverts, const int dvert_index)
+ : VMutableArrayImpl<float>(dverts.size()),
+ dverts_(const_cast<MDeformVert *>(dverts.data())),
+ dvert_index_(dvert_index)
{
}
@@ -983,12 +1026,12 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
if (vertex_group_index < 0) {
return {};
}
- if (mesh->dvert == nullptr) {
+ const Span<MDeformVert> dverts = mesh->deform_verts();
+ if (dverts.is_empty()) {
static const float default_value = 0.0f;
return {VArray<float>::ForSingle(default_value, mesh->totvert), ATTR_DOMAIN_POINT};
}
- return {VArray<float>::For<VArrayImpl_For_VertexWeights>(
- mesh->dvert, mesh->totvert, vertex_group_index),
+ return {VArray<float>::For<VArrayImpl_For_VertexWeights>(dverts, vertex_group_index),
ATTR_DOMAIN_POINT};
}
@@ -1008,16 +1051,8 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
if (vertex_group_index < 0) {
return {};
}
- if (mesh->dvert == nullptr) {
- BKE_object_defgroup_data_create(&mesh->id);
- }
- else {
- /* Copy the data layer if it is shared with some other mesh. */
- mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
- &mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
- }
- return {VMutableArray<float>::For<VArrayImpl_For_VertexWeights>(
- mesh->dvert, mesh->totvert, vertex_group_index),
+ MutableSpan<MDeformVert> dverts = mesh->deform_verts_for_write();
+ return {VMutableArray<float>::For<VArrayImpl_For_VertexWeights>(dverts, vertex_group_index),
ATTR_DOMAIN_POINT};
}
@@ -1040,15 +1075,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider {
}
BLI_remlink(&mesh->vertex_group_names, group);
MEM_freeN(group);
- if (mesh->dvert == nullptr) {
+ if (mesh->deform_verts().is_empty()) {
return true;
}
- /* Copy the data layer if it is shared with some other mesh. */
- mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer(
- &mesh->vdata, CD_MDEFORMVERT, mesh->totvert);
-
- for (MDeformVert &dvert : MutableSpan(mesh->dvert, mesh->totvert)) {
+ for (MDeformVert &dvert : mesh->deform_verts_for_write()) {
MDeformWeight *weight = BKE_defvert_find_index(&dvert, index);
BKE_defvert_remove_group(&dvert, weight);
for (MDeformWeight &weight : MutableSpan(dvert.dw, dvert.totweight)) {
@@ -1129,11 +1160,6 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider {
*/
static ComponentAttributeProviders create_attribute_providers_for_mesh()
{
- static auto update_custom_data_pointers = [](void *owner) {
- Mesh *mesh = static_cast<Mesh *>(owner);
- BKE_mesh_update_customdata_pointers(mesh, false);
- };
-
#define MAKE_MUTABLE_CUSTOM_DATA_GETTER(NAME) \
[](void *owner) -> CustomData * { \
Mesh *mesh = static_cast<Mesh *>(owner); \
@@ -1152,20 +1178,16 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static CustomDataAccessInfo corner_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(ldata),
MAKE_CONST_CUSTOM_DATA_GETTER(ldata),
- MAKE_GET_ELEMENT_NUM_GETTER(totloop),
- update_custom_data_pointers};
+ MAKE_GET_ELEMENT_NUM_GETTER(totloop)};
static CustomDataAccessInfo point_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(vdata),
MAKE_CONST_CUSTOM_DATA_GETTER(vdata),
- MAKE_GET_ELEMENT_NUM_GETTER(totvert),
- update_custom_data_pointers};
+ MAKE_GET_ELEMENT_NUM_GETTER(totvert)};
static CustomDataAccessInfo edge_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(edata),
MAKE_CONST_CUSTOM_DATA_GETTER(edata),
- MAKE_GET_ELEMENT_NUM_GETTER(totedge),
- update_custom_data_pointers};
+ MAKE_GET_ELEMENT_NUM_GETTER(totedge)};
static CustomDataAccessInfo face_access = {MAKE_MUTABLE_CUSTOM_DATA_GETTER(pdata),
MAKE_CONST_CUSTOM_DATA_GETTER(pdata),
- MAKE_GET_ELEMENT_NUM_GETTER(totpoly),
- update_custom_data_pointers};
+ MAKE_GET_ELEMENT_NUM_GETTER(totpoly)};
#undef MAKE_CONST_CUSTOM_DATA_GETTER
#undef MAKE_MUTABLE_CUSTOM_DATA_GETTER
@@ -1197,18 +1219,25 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
make_array_write_attribute<int>,
nullptr);
- 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 const fn::CustomMF_SI_SO<int, int> material_index_clamp{
+ "Material Index Validate",
+ [](int value) {
+ /* Use #short for the maximum since many areas still use that type for indices. */
+ return std::clamp<int>(value, 0, std::numeric_limits<short>::max());
+ },
+ fn::CustomMF_presets::AllSpanOrSingle()};
+ static BuiltinCustomDataLayerProvider material_index("material_index",
+ ATTR_DOMAIN_FACE,
+ CD_PROP_INT32,
+ CD_PROP_INT32,
+ BuiltinAttributeProvider::Creatable,
+ BuiltinAttributeProvider::Writable,
+ BuiltinAttributeProvider::Deletable,
+ face_access,
+ make_array_read_attribute<int>,
+ make_array_write_attribute<int>,
+ nullptr,
+ AttributeValidator{&material_index_clamp});
static BuiltinCustomDataLayerProvider shade_smooth(
"shade_smooth",
@@ -1305,18 +1334,19 @@ static const AttributeAccessorFunctions &get_mesh_accessor_functions_ref()
return fn;
}
-AttributeAccessor mesh_attributes(const Mesh &mesh)
+} // namespace blender::bke
+
+blender::bke::AttributeAccessor Mesh::attributes() const
{
- return AttributeAccessor(&mesh, get_mesh_accessor_functions_ref());
+ return blender::bke::AttributeAccessor(this, blender::bke::get_mesh_accessor_functions_ref());
}
-MutableAttributeAccessor mesh_attributes_for_write(Mesh &mesh)
+blender::bke::MutableAttributeAccessor Mesh::attributes_for_write()
{
- return MutableAttributeAccessor(&mesh, get_mesh_accessor_functions_ref());
+ return blender::bke::MutableAttributeAccessor(this,
+ blender::bke::get_mesh_accessor_functions_ref());
}
-} // namespace blender::bke
-
std::optional<blender::bke::AttributeAccessor> MeshComponent::attributes() const
{
return blender::bke::AttributeAccessor(mesh_, blender::bke::get_mesh_accessor_functions_ref());
@@ -1324,7 +1354,8 @@ std::optional<blender::bke::AttributeAccessor> MeshComponent::attributes() const
std::optional<blender::bke::MutableAttributeAccessor> MeshComponent::attributes_for_write()
{
- return blender::bke::MutableAttributeAccessor(mesh_,
+ Mesh *mesh = this->get_for_write();
+ return blender::bke::MutableAttributeAccessor(mesh,
blender::bke::get_mesh_accessor_functions_ref());
}
diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
index b439a9ba7f8..6980b561bc3 100644
--- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
+++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc
@@ -111,10 +111,6 @@ namespace blender::bke {
*/
static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
{
- static auto update_custom_data_pointers = [](void *owner) {
- PointCloud *pointcloud = static_cast<PointCloud *>(owner);
- BKE_pointcloud_update_customdata_pointers(pointcloud);
- };
static CustomDataAccessInfo point_access = {
[](void *owner) -> CustomData * {
PointCloud *pointcloud = static_cast<PointCloud *>(owner);
@@ -127,8 +123,7 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
[](const void *owner) -> int {
const PointCloud *pointcloud = static_cast<const PointCloud *>(owner);
return pointcloud->totpoint;
- },
- update_custom_data_pointers};
+ }};
static BuiltinCustomDataLayerProvider position("position",
ATTR_DOMAIN_POINT,
@@ -206,18 +201,20 @@ static const AttributeAccessorFunctions &get_pointcloud_accessor_functions_ref()
return fn;
}
-AttributeAccessor pointcloud_attributes(const PointCloud &pointcloud)
+} // namespace blender::bke
+
+blender::bke::AttributeAccessor PointCloud::attributes() const
{
- return AttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref());
+ return blender::bke::AttributeAccessor(this,
+ blender::bke::get_pointcloud_accessor_functions_ref());
}
-MutableAttributeAccessor pointcloud_attributes_for_write(PointCloud &pointcloud)
+blender::bke::MutableAttributeAccessor PointCloud::attributes_for_write()
{
- return MutableAttributeAccessor(&pointcloud, get_pointcloud_accessor_functions_ref());
+ return blender::bke::MutableAttributeAccessor(
+ this, blender::bke::get_pointcloud_accessor_functions_ref());
}
-} // namespace blender::bke
-
std::optional<blender::bke::AttributeAccessor> PointCloudComponent::attributes() const
{
return blender::bke::AttributeAccessor(pointcloud_,
@@ -226,8 +223,9 @@ std::optional<blender::bke::AttributeAccessor> PointCloudComponent::attributes()
std::optional<blender::bke::MutableAttributeAccessor> PointCloudComponent::attributes_for_write()
{
+ PointCloud *pointcloud = this->get_for_write();
return blender::bke::MutableAttributeAccessor(
- pointcloud_, blender::bke::get_pointcloud_accessor_functions_ref());
+ pointcloud, blender::bke::get_pointcloud_accessor_functions_ref());
}
/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_fields.cc b/source/blender/blenkernel/intern/geometry_fields.cc
new file mode 100644
index 00000000000..56e9e9dcdff
--- /dev/null
+++ b/source/blender/blenkernel/intern/geometry_fields.cc
@@ -0,0 +1,365 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_attribute.hh"
+#include "BKE_curves.hh"
+#include "BKE_geometry_fields.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_mesh.h"
+#include "BKE_pointcloud.h"
+#include "BKE_type_conversions.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BLT_translation.h"
+
+namespace blender::bke {
+
+MeshFieldContext::MeshFieldContext(const Mesh &mesh, const eAttrDomain domain)
+ : mesh_(mesh), domain_(domain)
+{
+ BLI_assert(mesh.attributes().domain_supported(domain_));
+}
+
+CurvesFieldContext::CurvesFieldContext(const CurvesGeometry &curves, const eAttrDomain domain)
+ : curves_(curves), domain_(domain)
+{
+ BLI_assert(curves.attributes().domain_supported(domain));
+}
+
+GeometryFieldContext::GeometryFieldContext(const void *geometry,
+ const GeometryComponentType type,
+ const eAttrDomain domain)
+ : geometry_(geometry), type_(type), domain_(domain)
+{
+ BLI_assert(ELEM(type,
+ GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_INSTANCES));
+}
+
+GeometryFieldContext::GeometryFieldContext(const GeometryComponent &component,
+ const eAttrDomain domain)
+ : type_(component.type()), domain_(domain)
+{
+ switch (component.type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
+ geometry_ = mesh_component.get_for_read();
+ break;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
+ const Curves *curves = curve_component.get_for_read();
+ geometry_ = curves ? &CurvesGeometry::wrap(curves->geometry) : nullptr;
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ const PointCloudComponent &pointcloud_component = static_cast<const PointCloudComponent &>(
+ component);
+ geometry_ = pointcloud_component.get_for_read();
+ break;
+ }
+ case GEO_COMPONENT_TYPE_INSTANCES: {
+ const InstancesComponent &instances_component = static_cast<const InstancesComponent &>(
+ component);
+ geometry_ = &instances_component;
+ break;
+ }
+ case GEO_COMPONENT_TYPE_VOLUME:
+ case GEO_COMPONENT_TYPE_EDIT:
+ BLI_assert_unreachable();
+ break;
+ }
+}
+
+GeometryFieldContext::GeometryFieldContext(const Mesh &mesh, eAttrDomain domain)
+ : geometry_(&mesh), type_(GEO_COMPONENT_TYPE_MESH), domain_(domain)
+{
+}
+GeometryFieldContext::GeometryFieldContext(const CurvesGeometry &curves, eAttrDomain domain)
+ : geometry_(&curves), type_(GEO_COMPONENT_TYPE_CURVE), domain_(domain)
+{
+}
+GeometryFieldContext::GeometryFieldContext(const PointCloud &points)
+ : geometry_(&points), type_(GEO_COMPONENT_TYPE_POINT_CLOUD), domain_(ATTR_DOMAIN_POINT)
+{
+}
+GeometryFieldContext::GeometryFieldContext(const InstancesComponent &instances)
+ : geometry_(&instances), type_(GEO_COMPONENT_TYPE_INSTANCES), domain_(ATTR_DOMAIN_INSTANCE)
+{
+}
+
+std::optional<AttributeAccessor> GeometryFieldContext::attributes() const
+{
+ if (const Mesh *mesh = this->mesh()) {
+ return mesh->attributes();
+ }
+ if (const CurvesGeometry *curves = this->curves()) {
+ return curves->attributes();
+ }
+ if (const PointCloud *pointcloud = this->pointcloud()) {
+ return pointcloud->attributes();
+ }
+ if (const InstancesComponent *instances = this->instances()) {
+ return instances->attributes();
+ }
+ return {};
+}
+
+const Mesh *GeometryFieldContext::mesh() const
+{
+ return this->type() == GEO_COMPONENT_TYPE_MESH ? static_cast<const Mesh *>(geometry_) : nullptr;
+}
+const CurvesGeometry *GeometryFieldContext::curves() const
+{
+ return this->type() == GEO_COMPONENT_TYPE_CURVE ?
+ static_cast<const CurvesGeometry *>(geometry_) :
+ nullptr;
+}
+const PointCloud *GeometryFieldContext::pointcloud() const
+{
+ return this->type() == GEO_COMPONENT_TYPE_POINT_CLOUD ?
+ static_cast<const PointCloud *>(geometry_) :
+ nullptr;
+}
+const InstancesComponent *GeometryFieldContext::instances() const
+{
+ return this->type() == GEO_COMPONENT_TYPE_INSTANCES ?
+ static_cast<const InstancesComponent *>(geometry_) :
+ nullptr;
+}
+
+GVArray GeometryFieldInput::get_varray_for_context(const fn::FieldContext &context,
+ const IndexMask mask,
+ ResourceScope & /*scope*/) const
+{
+ if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
+ &context)) {
+ return this->get_varray_for_context(*geometry_context, mask);
+ }
+ if (const MeshFieldContext *mesh_context = dynamic_cast<const MeshFieldContext *>(&context)) {
+ return this->get_varray_for_context({mesh_context->mesh(), mesh_context->domain()}, mask);
+ }
+ if (const CurvesFieldContext *curve_context = dynamic_cast<const CurvesFieldContext *>(
+ &context)) {
+ return this->get_varray_for_context({curve_context->curves(), curve_context->domain()}, mask);
+ }
+ if (const PointCloudFieldContext *point_context = dynamic_cast<const PointCloudFieldContext *>(
+ &context)) {
+ return this->get_varray_for_context({point_context->pointcloud()}, mask);
+ }
+ if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>(
+ &context)) {
+ return this->get_varray_for_context({instances_context->instances()}, mask);
+ }
+ return {};
+}
+
+GVArray MeshFieldInput::get_varray_for_context(const fn::FieldContext &context,
+ const IndexMask mask,
+ ResourceScope & /*scope*/) const
+{
+ if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
+ &context)) {
+ if (const Mesh *mesh = geometry_context->mesh()) {
+ return this->get_varray_for_context(*mesh, geometry_context->domain(), mask);
+ }
+ }
+ if (const MeshFieldContext *mesh_context = dynamic_cast<const MeshFieldContext *>(&context)) {
+ return this->get_varray_for_context(mesh_context->mesh(), mesh_context->domain(), mask);
+ }
+ return {};
+}
+
+GVArray CurvesFieldInput::get_varray_for_context(const fn::FieldContext &context,
+ IndexMask mask,
+ ResourceScope & /*scope*/) const
+{
+ if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
+ &context)) {
+ if (const CurvesGeometry *curves = geometry_context->curves()) {
+ return this->get_varray_for_context(*curves, geometry_context->domain(), mask);
+ }
+ }
+ if (const CurvesFieldContext *curves_context = dynamic_cast<const CurvesFieldContext *>(
+ &context)) {
+ return this->get_varray_for_context(curves_context->curves(), curves_context->domain(), mask);
+ }
+ return {};
+}
+
+GVArray PointCloudFieldInput::get_varray_for_context(const fn::FieldContext &context,
+ IndexMask mask,
+ ResourceScope & /*scope*/) const
+{
+ if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
+ &context)) {
+ if (const PointCloud *pointcloud = geometry_context->pointcloud()) {
+ return this->get_varray_for_context(*pointcloud, mask);
+ }
+ }
+ if (const PointCloudFieldContext *point_context = dynamic_cast<const PointCloudFieldContext *>(
+ &context)) {
+ return this->get_varray_for_context(point_context->pointcloud(), mask);
+ }
+ return {};
+}
+
+GVArray InstancesFieldInput::get_varray_for_context(const fn::FieldContext &context,
+ IndexMask mask,
+ ResourceScope & /*scope*/) const
+{
+ if (const GeometryFieldContext *geometry_context = dynamic_cast<const GeometryFieldContext *>(
+ &context)) {
+ if (const InstancesComponent *instances = geometry_context->instances()) {
+ return this->get_varray_for_context(*instances, mask);
+ }
+ }
+ if (const InstancesFieldContext *instances_context = dynamic_cast<const InstancesFieldContext *>(
+ &context)) {
+ return this->get_varray_for_context(instances_context->instances(), mask);
+ }
+ return {};
+}
+
+GVArray AttributeFieldInput::get_varray_for_context(const GeometryFieldContext &context,
+ IndexMask UNUSED(mask)) const
+{
+ const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_);
+ if (auto attributes = context.attributes()) {
+ return attributes->lookup(name_, context.domain(), data_type);
+ }
+ return {};
+}
+
+std::string AttributeFieldInput::socket_inspection_name() const
+{
+ std::stringstream ss;
+ ss << '"' << name_ << '"' << TIP_(" attribute from geometry");
+ return ss.str();
+}
+
+uint64_t AttributeFieldInput::hash() const
+{
+ return get_default_hash_2(name_, type_);
+}
+
+bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
+{
+ if (const AttributeFieldInput *other_typed = dynamic_cast<const AttributeFieldInput *>(&other)) {
+ return name_ == other_typed->name_ && type_ == other_typed->type_;
+ }
+ return false;
+}
+
+static StringRef get_random_id_attribute_name(const eAttrDomain domain)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ case ATTR_DOMAIN_INSTANCE:
+ return "id";
+ default:
+ return "";
+ }
+}
+
+GVArray IDAttributeFieldInput::get_varray_for_context(const GeometryFieldContext &context,
+ const IndexMask mask) const
+{
+
+ const StringRef name = get_random_id_attribute_name(context.domain());
+ if (auto attributes = context.attributes()) {
+ if (GVArray attribute = attributes->lookup(name, context.domain(), CD_PROP_INT32)) {
+ return attribute;
+ }
+ }
+
+ /* Use the index as the fallback if no random ID attribute exists. */
+ return fn::IndexFieldInput::get_index_varray(mask);
+}
+
+std::string IDAttributeFieldInput::socket_inspection_name() const
+{
+ return TIP_("ID / Index");
+}
+
+uint64_t IDAttributeFieldInput::hash() const
+{
+ /* All random ID attribute inputs are the same within the same evaluation context. */
+ return 92386459827;
+}
+
+bool IDAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
+{
+ /* All random ID attribute inputs are the same within the same evaluation context. */
+ return dynamic_cast<const IDAttributeFieldInput *>(&other) != nullptr;
+}
+
+GVArray AnonymousAttributeFieldInput::get_varray_for_context(const GeometryFieldContext &context,
+ const IndexMask /*mask*/) const
+{
+ const eCustomDataType data_type = cpp_type_to_custom_data_type(*type_);
+ return context.attributes()->lookup(anonymous_id_.get(), context.domain(), data_type);
+}
+
+std::string AnonymousAttributeFieldInput::socket_inspection_name() const
+{
+ std::stringstream ss;
+ ss << '"' << debug_name_ << '"' << TIP_(" from ") << producer_name_;
+ return ss.str();
+}
+
+uint64_t AnonymousAttributeFieldInput::hash() const
+{
+ return get_default_hash_2(anonymous_id_.get(), type_);
+}
+
+bool AnonymousAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const
+{
+ if (const AnonymousAttributeFieldInput *other_typed =
+ dynamic_cast<const AnonymousAttributeFieldInput *>(&other)) {
+ return anonymous_id_.get() == other_typed->anonymous_id_.get() && type_ == other_typed->type_;
+ }
+ return false;
+}
+
+} // namespace blender::bke
+
+/* -------------------------------------------------------------------- */
+/** \name Mesh and Curve Normals Field Input
+ * \{ */
+
+namespace blender::bke {
+
+GVArray NormalFieldInput::get_varray_for_context(const GeometryFieldContext &context,
+ const IndexMask mask) const
+{
+ if (const Mesh *mesh = context.mesh()) {
+ return mesh_normals_varray(*mesh, mask, context.domain());
+ }
+ if (const CurvesGeometry *curves = context.curves()) {
+ return curve_normals_varray(*curves, context.domain());
+ }
+ return {};
+}
+
+std::string NormalFieldInput::socket_inspection_name() const
+{
+ return TIP_("Normal");
+}
+
+uint64_t NormalFieldInput::hash() const
+{
+ return 213980475983;
+}
+
+bool NormalFieldInput::is_equal_to(const fn::FieldNode &other) const
+{
+ return dynamic_cast<const NormalFieldInput *>(&other) != nullptr;
+}
+
+} // namespace blender::bke
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc
index c6fe8eebc7f..46ff8141504 100644
--- a/source/blender/blenkernel/intern/geometry_set.cc
+++ b/source/blender/blenkernel/intern/geometry_set.cc
@@ -8,7 +8,6 @@
#include "BKE_attribute.h"
#include "BKE_curves.hh"
-#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set.hh"
#include "BKE_lib_id.h"
#include "BKE_mesh.h"
@@ -19,6 +18,7 @@
#include "DNA_collection_types.h"
#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
#include "BLI_rand.hh"
@@ -53,6 +53,8 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new VolumeComponent();
case GEO_COMPONENT_TYPE_CURVE:
return new CurveComponent();
+ case GEO_COMPONENT_TYPE_EDIT:
+ return new GeometryComponentEditData();
}
BLI_assert_unreachable();
return nullptr;
@@ -175,6 +177,20 @@ void GeometrySet::keep_only(const blender::Span<GeometryComponentType> component
}
}
+void GeometrySet::keep_only_during_modify(
+ const blender::Span<GeometryComponentType> component_types)
+{
+ Vector<GeometryComponentType> extended_types = component_types;
+ extended_types.append_non_duplicates(GEO_COMPONENT_TYPE_INSTANCES);
+ extended_types.append_non_duplicates(GEO_COMPONENT_TYPE_EDIT);
+ this->keep_only(extended_types);
+}
+
+void GeometrySet::remove_geometry_during_modify()
+{
+ this->keep_only_during_modify({});
+}
+
void GeometrySet::add(const GeometryComponent &component)
{
BLI_assert(!components_[component.type()]);
@@ -222,8 +238,40 @@ bool GeometrySet::compute_boundbox_without_instances(float3 *r_min, float3 *r_ma
std::ostream &operator<<(std::ostream &stream, const GeometrySet &geometry_set)
{
- stream << "<GeometrySet at " << &geometry_set << ", " << geometry_set.components_.size()
- << " components>";
+ Vector<std::string> parts;
+ if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
+ parts.append(std::to_string(mesh->totvert) + " verts");
+ parts.append(std::to_string(mesh->totedge) + " edges");
+ parts.append(std::to_string(mesh->totpoly) + " polys");
+ parts.append(std::to_string(mesh->totloop) + " corners");
+ }
+ if (const Curves *curves = geometry_set.get_curves_for_read()) {
+ parts.append(std::to_string(curves->geometry.point_num) + " control points");
+ parts.append(std::to_string(curves->geometry.curve_num) + " curves");
+ }
+ if (const PointCloud *point_cloud = geometry_set.get_pointcloud_for_read()) {
+ parts.append(std::to_string(point_cloud->totpoint) + " points");
+ }
+ if (const Volume *volume = geometry_set.get_volume_for_read()) {
+ parts.append(std::to_string(BKE_volume_num_grids(volume)) + " volume grids");
+ }
+ if (geometry_set.has_instances()) {
+ parts.append(std::to_string(
+ geometry_set.get_component_for_read<InstancesComponent>()->instances_num()) +
+ " instances");
+ }
+ if (geometry_set.get_curve_edit_hints_for_read()) {
+ parts.append("curve edit hints");
+ }
+
+ stream << "<GeometrySet: ";
+ for (const int i : parts.index_range()) {
+ stream << parts[i];
+ if (i < parts.size() - 1) {
+ stream << ", ";
+ }
+ }
+ stream << ">";
return stream;
}
@@ -290,6 +338,13 @@ const Curves *GeometrySet::get_curves_for_read() const
return (component == nullptr) ? nullptr : component->get_for_read();
}
+const blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_read() const
+{
+ const GeometryComponentEditData *component =
+ this->get_component_for_read<GeometryComponentEditData>();
+ return (component == nullptr) ? nullptr : component->curves_edit_hints_.get();
+}
+
bool GeometrySet::has_pointcloud() const
{
const PointCloudComponent *component = this->get_component_for_read<PointCloudComponent>();
@@ -453,6 +508,16 @@ Curves *GeometrySet::get_curves_for_write()
return component == nullptr ? nullptr : component->get_for_write();
}
+blender::bke::CurvesEditHints *GeometrySet::get_curve_edit_hints_for_write()
+{
+ if (!this->has<GeometryComponentEditData>()) {
+ return nullptr;
+ }
+ GeometryComponentEditData &component =
+ this->get_component_for_write<GeometryComponentEditData>();
+ return component.curves_edit_hints_.get();
+}
+
void GeometrySet::attribute_foreach(const Span<GeometryComponentType> component_types,
const bool include_instances,
const AttributeForeachCallback callback) const
@@ -601,48 +666,6 @@ void GeometrySet::modify_geometry_sets(ForeachSubGeometryCallback callback)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Mesh and Curve Normals Field Input
- * \{ */
-
-namespace blender::bke {
-
-GVArray NormalFieldInput::get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask mask) const
-{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- if (const Mesh *mesh = mesh_component.get_for_read()) {
- return mesh_normals_varray(mesh_component, *mesh, mask, domain);
- }
- }
- else if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return curve_normals_varray(curve_component, domain);
- }
- return {};
-}
-
-std::string NormalFieldInput::socket_inspection_name() const
-{
- return TIP_("Normal");
-}
-
-uint64_t NormalFieldInput::hash() const
-{
- return 213980475983;
-}
-
-bool NormalFieldInput::is_equal_to(const fn::FieldNode &other) const
-{
- return dynamic_cast<const NormalFieldInput *>(&other) != nullptr;
-}
-
-} // namespace blender::bke
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name C API
* \{ */
@@ -679,6 +702,8 @@ bool BKE_object_has_geometry_set_instances(const Object *ob)
case GEO_COMPONENT_TYPE_CURVE:
is_instance = !ELEM(ob->type, OB_CURVES_LEGACY, OB_FONT);
break;
+ case GEO_COMPONENT_TYPE_EDIT:
+ break;
}
if (is_instance) {
return true;
diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc
index fbd676e4bee..df48a99f706 100644
--- a/source/blender/blenkernel/intern/geometry_set_instances.cc
+++ b/source/blender/blenkernel/intern/geometry_set_instances.cc
@@ -27,8 +27,8 @@ static void geometry_set_collect_recursive_collection(const Collection &collecti
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);
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(
+ &const_cast<Object &>(object));
if (mesh != nullptr) {
BKE_mesh_wrapper_ensure_mdata(mesh);
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 3d546c5c36a..f6082d886d9 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -271,7 +271,7 @@ static void greasepencil_blend_read_lib(BlendLibReader *reader, ID *id)
{
bGPdata *gpd = (bGPdata *)id;
- /* Relink all data-lock linked by GP data-lock */
+ /* Relink all data-block linked by GP data-block. */
/* Layers */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Layer -> Parent References */
@@ -314,7 +314,7 @@ IDTypeInfo IDType_ID_GD = {
.foreach_id = greasepencil_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = greasepencil_blend_write,
.blend_read_data = greasepencil_blend_read_data,
@@ -675,7 +675,7 @@ bGPDlayer *BKE_gpencil_layer_addnew(bGPdata *gpd,
}
/* auto-name */
- BLI_strncpy(gpl->info, name, sizeof(gpl->info));
+ BLI_strncpy(gpl->info, DATA_(name), sizeof(gpl->info));
BLI_uniquename(&gpd->layers,
gpl,
(gpd->flag & GP_DATA_ANNOTATIONS) ? DATA_("Note") : DATA_("GP_Layer"),
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index d68b322e4c5..bf224a9613e 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -337,7 +337,6 @@ static void gpencil_convert_spline(Main *bmain,
/* Add stroke to frame. */
BLI_addtail(&gpf->strokes, gps);
- float *coord_array = NULL;
float init_co[3];
switch (nu->type) {
@@ -376,8 +375,7 @@ static void gpencil_convert_spline(Main *bmain,
BezTriple *bezt = &nu->bezt[inext];
bool last = (bool)(s == segments - 1);
- coord_array = MEM_callocN((size_t)3 * resolu * sizeof(float), __func__);
-
+ float *coord_array = MEM_callocN(sizeof(float[3]) * resolu, __func__);
for (int j = 0; j < 3; j++) {
BKE_curve_forward_diff_bezier(prevbezt->vec[1][j],
prevbezt->vec[2][j],
@@ -397,8 +395,9 @@ static void gpencil_convert_spline(Main *bmain,
gpencil_add_new_points(
gps, coord_array, radius_start, radius_end, init, resolu, init_co, last);
+
/* Free memory. */
- MEM_SAFE_FREE(coord_array);
+ MEM_freeN(coord_array);
/* As the last point of segment is the first point of next segment, back one array
* element to avoid duplicated points on the same location.
@@ -419,7 +418,7 @@ static void gpencil_convert_spline(Main *bmain,
nurb_points = (nu->pntsu - 1) * resolu;
}
/* Get all curve points. */
- coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__);
+ float *coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__);
BKE_nurb_makeCurve(nu, coord_array, NULL, NULL, NULL, resolu, sizeof(float[3]));
/* Allocate memory for storage points. */
@@ -429,7 +428,7 @@ static void gpencil_convert_spline(Main *bmain,
/* Add points. */
gpencil_add_new_points(gps, coord_array, 1.0f, 1.0f, 0, gps->totpoints, init_co, false);
- MEM_SAFE_FREE(coord_array);
+ MEM_freeN(coord_array);
}
break;
}
diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc
index d0075a7d161..4d0db4d5386 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.cc
+++ b/source/blender/blenkernel/intern/gpencil_geom.cc
@@ -35,6 +35,7 @@
#include "BLT_translation.h"
+#include "BKE_attribute.hh"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
@@ -2463,6 +2464,9 @@ static void gpencil_generate_edgeloops(Object *ob,
if (me->totedge == 0) {
return;
}
+ const Span<MVert> verts = me->verts();
+ const Span<MEdge> edges = me->edges();
+ const Span<MDeformVert> dverts = me->deform_verts();
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me);
/* Arrays for all edge vertices (forward and backward) that form a edge loop.
@@ -2475,15 +2479,15 @@ static void gpencil_generate_edgeloops(Object *ob,
GpEdge *gp_edges = (GpEdge *)MEM_callocN(sizeof(GpEdge) * me->totedge, __func__);
GpEdge *gped = nullptr;
for (int i = 0; i < me->totedge; i++) {
- MEdge *ed = &me->medge[i];
+ const MEdge *ed = &edges[i];
gped = &gp_edges[i];
- MVert *mv1 = &me->mvert[ed->v1];
+ const MVert *mv1 = &verts[ed->v1];
copy_v3_v3(gped->n1, vert_normals[ed->v1]);
gped->v1 = ed->v1;
copy_v3_v3(gped->v1_co, mv1->co);
- MVert *mv2 = &me->mvert[ed->v2];
+ const MVert *mv2 = &verts[ed->v2];
copy_v3_v3(gped->n2, vert_normals[ed->v2]);
gped->v2 = ed->v2;
copy_v3_v3(gped->v2_co, mv2->co);
@@ -2539,8 +2543,7 @@ static void gpencil_generate_edgeloops(Object *ob,
gpf_stroke, MAX2(stroke_mat_index, 0), array_len + 1, thickness * thickness, false);
/* Create dvert data. */
- MDeformVert *me_dvert = me->dvert;
- if (use_vgroups && me_dvert) {
+ if (use_vgroups && !dverts.is_empty()) {
gps_stroke->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (array_len + 1),
"gp_stroke_dverts");
}
@@ -2549,7 +2552,7 @@ static void gpencil_generate_edgeloops(Object *ob,
float fpt[3];
for (int i = 0; i < array_len + 1; i++) {
int vertex_index = i == 0 ? gp_edges[stroke[0]].v1 : gp_edges[stroke[i - 1]].v2;
- MVert *mv = &me->mvert[vertex_index];
+ const MVert *mv = &verts[vertex_index];
/* Add segment. */
bGPDspoint *pt = &gps_stroke->points[i];
@@ -2562,9 +2565,9 @@ static void gpencil_generate_edgeloops(Object *ob,
pt->strength = 1.0f;
/* Copy vertex groups from mesh. Assuming they already exist in the same order. */
- if (use_vgroups && me_dvert) {
+ if (use_vgroups && !dverts.is_empty()) {
MDeformVert *dv = &gps_stroke->dvert[i];
- MDeformVert *src_dv = &me_dvert[vertex_index];
+ const MDeformVert *src_dv = &dverts[vertex_index];
dv->totweight = src_dv->totweight;
dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
"gp_stroke_dverts_dw");
@@ -2662,6 +2665,8 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
const bool use_faces,
const bool use_vgroups)
{
+ using namespace blender;
+ using namespace blender::bke;
if (ELEM(nullptr, ob_gp, ob_mesh) || (ob_gp->type != OB_GPENCIL) || (ob_gp->data == nullptr)) {
return false;
}
@@ -2671,8 +2676,9 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
/* Use evaluated data to get mesh with all modifiers on top. */
Object *ob_eval = (Object *)DEG_get_evaluated_object(depsgraph, ob_mesh);
const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
- const MPoly *mpoly = me_eval->mpoly;
- const MLoop *mloop = me_eval->mloop;
+ const Span<MVert> verts = me_eval->verts();
+ const Span<MPoly> polys = me_eval->polys();
+ const Span<MLoop> loops = me_eval->loops();
int mpoly_len = me_eval->totpoly;
char element_name[200];
@@ -2708,12 +2714,15 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
bGPDframe *gpf_fill = BKE_gpencil_layer_frame_get(
gpl_fill, scene->r.cfra + frame_offset, GP_GETFRAME_ADD_NEW);
int i;
+
+ const VArray<int> mesh_material_indices = me_eval->attributes().lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
for (i = 0; i < mpoly_len; i++) {
- const MPoly *mp = &mpoly[i];
+ const MPoly *mp = &polys[i];
/* Find material. */
int mat_idx = 0;
- Material *ma = BKE_object_material_get(ob_mesh, mp->mat_nr + 1);
+ Material *ma = BKE_object_material_get(ob_mesh, mesh_material_indices[i] + 1);
make_element_name(
ob_mesh->id.name + 2, (ma != nullptr) ? ma->id.name + 2 : "Fill", 64, element_name);
mat_idx = BKE_gpencil_material_find_index_by_name_prefix(ob_gp, element_name);
@@ -2733,16 +2742,16 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
gps_fill->flag |= GP_STROKE_CYCLIC;
/* Create dvert data. */
- MDeformVert *me_dvert = me_eval->dvert;
- if (use_vgroups && me_dvert) {
+ const Span<MDeformVert> dverts = me_eval->deform_verts();
+ if (use_vgroups && !dverts.is_empty()) {
gps_fill->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * mp->totloop,
"gp_fill_dverts");
}
/* Add points to strokes. */
for (int j = 0; j < mp->totloop; j++) {
- const MLoop *ml = &mloop[mp->loopstart + j];
- const MVert *mv = &me_eval->mvert[ml->v];
+ const MLoop *ml = &loops[mp->loopstart + j];
+ const MVert *mv = &verts[ml->v];
bGPDspoint *pt = &gps_fill->points[j];
copy_v3_v3(&pt->x, mv->co);
@@ -2751,9 +2760,9 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
pt->strength = 1.0f;
/* Copy vertex groups from mesh. Assuming they already exist in the same order. */
- if (use_vgroups && me_dvert) {
+ if (use_vgroups && !dverts.is_empty()) {
MDeformVert *dv = &gps_fill->dvert[j];
- MDeformVert *src_dv = &me_dvert[ml->v];
+ const MDeformVert *src_dv = &dverts[ml->v];
dv->totweight = src_dv->totweight;
dv->dw = (MDeformWeight *)MEM_callocN(sizeof(MDeformWeight) * dv->totweight,
"gp_fill_dverts_dw");
@@ -3409,7 +3418,8 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
bGPDstroke *gps_b,
const bool leave_gaps,
const bool fit_thickness,
- const bool smooth)
+ const bool smooth,
+ bool auto_flip)
{
bGPDspoint point;
bGPDspoint *pt;
@@ -3426,52 +3436,54 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
return;
}
- /* define start and end points of each stroke */
- float start_a[3], start_b[3], end_a[3], end_b[3];
- pt = &gps_a->points[0];
- copy_v3_v3(start_a, &pt->x);
+ if (auto_flip) {
+ /* define start and end points of each stroke */
+ float start_a[3], start_b[3], end_a[3], end_b[3];
+ pt = &gps_a->points[0];
+ copy_v3_v3(start_a, &pt->x);
- pt = &gps_a->points[gps_a->totpoints - 1];
- copy_v3_v3(end_a, &pt->x);
+ pt = &gps_a->points[gps_a->totpoints - 1];
+ copy_v3_v3(end_a, &pt->x);
- pt = &gps_b->points[0];
- copy_v3_v3(start_b, &pt->x);
+ pt = &gps_b->points[0];
+ copy_v3_v3(start_b, &pt->x);
- pt = &gps_b->points[gps_b->totpoints - 1];
- copy_v3_v3(end_b, &pt->x);
+ pt = &gps_b->points[gps_b->totpoints - 1];
+ copy_v3_v3(end_b, &pt->x);
- /* Check if need flip strokes. */
- float dist = len_squared_v3v3(end_a, start_b);
- bool flip_a = false;
- bool flip_b = false;
- float lowest = dist;
+ /* Check if need flip strokes. */
+ float dist = len_squared_v3v3(end_a, start_b);
+ bool flip_a = false;
+ bool flip_b = false;
+ float lowest = dist;
- dist = len_squared_v3v3(end_a, end_b);
- if (dist < lowest) {
- lowest = dist;
- flip_a = false;
- flip_b = true;
- }
+ dist = len_squared_v3v3(end_a, end_b);
+ if (dist < lowest) {
+ lowest = dist;
+ flip_a = false;
+ flip_b = true;
+ }
- dist = len_squared_v3v3(start_a, start_b);
- if (dist < lowest) {
- lowest = dist;
- flip_a = true;
- flip_b = false;
- }
+ dist = len_squared_v3v3(start_a, start_b);
+ if (dist < lowest) {
+ lowest = dist;
+ flip_a = true;
+ flip_b = false;
+ }
- dist = len_squared_v3v3(start_a, end_b);
- if (dist < lowest) {
- lowest = dist;
- flip_a = true;
- flip_b = true;
- }
+ dist = len_squared_v3v3(start_a, end_b);
+ if (dist < lowest) {
+ lowest = dist;
+ flip_a = true;
+ flip_b = true;
+ }
- if (flip_a) {
- BKE_gpencil_stroke_flip(gps_a);
- }
- if (flip_b) {
- BKE_gpencil_stroke_flip(gps_b);
+ if (flip_a) {
+ BKE_gpencil_stroke_flip(gps_a);
+ }
+ if (flip_b) {
+ BKE_gpencil_stroke_flip(gps_b);
+ }
}
/* don't visibly link the first and last points? */
@@ -3534,6 +3546,27 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
}
}
+void BKE_gpencil_stroke_start_set(bGPDstroke *gps, int start_idx)
+{
+ if ((start_idx < 1) || (start_idx >= gps->totpoints) || (gps->totpoints < 2)) {
+ return;
+ }
+
+ /* Only cyclic strokes. */
+ if ((gps->flag & GP_STROKE_CYCLIC) == 0) {
+ return;
+ }
+
+ bGPDstroke *gps_b = BKE_gpencil_stroke_duplicate(gps, true, false);
+ BKE_gpencil_stroke_trim_points(gps_b, 0, start_idx - 1);
+ BKE_gpencil_stroke_trim_points(gps, start_idx, gps->totpoints - 1);
+
+ /* Join both strokes. */
+ BKE_gpencil_stroke_join(gps, gps_b, false, false, false, false);
+
+ BKE_gpencil_free_stroke(gps_b);
+}
+
void BKE_gpencil_stroke_copy_to_keyframes(
bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const bool tail)
{
@@ -3761,8 +3794,8 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
-void BKE_gpencil_stroke_to_view_space(RegionView3D *rv3d,
- bGPDstroke *gps,
+void BKE_gpencil_stroke_to_view_space(bGPDstroke *gps,
+ float viewmat[4][4],
const float diff_mat[4][4])
{
for (int i = 0; i < gps->totpoints; i++) {
@@ -3770,12 +3803,12 @@ void BKE_gpencil_stroke_to_view_space(RegionView3D *rv3d,
/* Point to parent space. */
mul_v3_m4v3(&pt->x, diff_mat, &pt->x);
/* point to view space */
- mul_m4_v3(rv3d->viewmat, &pt->x);
+ mul_m4_v3(viewmat, &pt->x);
}
}
-void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d,
- bGPDstroke *gps,
+void BKE_gpencil_stroke_from_view_space(bGPDstroke *gps,
+ float viewinv[4][4],
const float diff_mat[4][4])
{
float inverse_diff_mat[4][4];
@@ -3783,7 +3816,7 @@ void BKE_gpencil_stroke_from_view_space(RegionView3D *rv3d,
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
- mul_v3_m4v3(&pt->x, rv3d->viewinv, &pt->x);
+ mul_v3_m4v3(&pt->x, viewinv, &pt->x);
mul_m4_v3(inverse_diff_mat, &pt->x);
}
}
@@ -3960,6 +3993,7 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
const bGPDlayer *gpl,
const bGPDstroke *gps,
int subdivisions,
+ const float thickness_chg,
int *r_num_perimeter_points)
{
/* sanity check */
@@ -3968,7 +4002,9 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
}
float defaultpixsize = 1000.0f / gpd->pixfactor;
+ float ovr_radius = thickness_chg / defaultpixsize / 2.0f;
float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
+ stroke_radius = max_ff(stroke_radius - ovr_radius, 0.0f);
ListBase *perimeter_right_side = MEM_cnew<ListBase>(__func__);
ListBase *perimeter_left_side = MEM_cnew<ListBase>(__func__);
@@ -4197,16 +4233,21 @@ static ListBase *gpencil_stroke_perimeter_ex(const bGPdata *gpd,
return perimeter_list;
}
-bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
+bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(float viewmat[4][4],
bGPdata *gpd,
const bGPDlayer *gpl,
bGPDstroke *gps,
const int subdivisions,
- const float diff_mat[4][4])
+ const float diff_mat[4][4],
+ const float thickness_chg)
{
if (gps->totpoints == 0) {
return nullptr;
}
+
+ float viewinv[4][4];
+ invert_m4_m4(viewinv, viewmat);
+
/* Duplicate only points and fill data. Weight and Curve are not needed. */
bGPDstroke *gps_temp = (bGPDstroke *)MEM_dupallocN(gps);
gps_temp->prev = gps_temp->next = nullptr;
@@ -4231,10 +4272,10 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
pt_dst->uv_rot = 0;
}
- BKE_gpencil_stroke_to_view_space(rv3d, gps_temp, diff_mat);
+ BKE_gpencil_stroke_to_view_space(gps_temp, viewmat, diff_mat);
int num_perimeter_points = 0;
ListBase *perimeter_points = gpencil_stroke_perimeter_ex(
- gpd, gpl, gps_temp, subdivisions, &num_perimeter_points);
+ gpd, gpl, gps_temp, subdivisions, thickness_chg, &num_perimeter_points);
if (num_perimeter_points == 0) {
return nullptr;
@@ -4254,7 +4295,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
pt->flag |= GP_SPOINT_SELECT;
}
- BKE_gpencil_stroke_from_view_space(rv3d, perimeter_stroke, diff_mat);
+ BKE_gpencil_stroke_from_view_space(perimeter_stroke, viewinv, diff_mat);
/* Free temp data. */
BLI_freelistN(perimeter_points);
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index 8739f2f7082..33f84aff545 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -95,7 +95,7 @@ void BKE_gpencil_cache_data_init(Depsgraph *depsgraph, Object *ob)
MEM_SAFE_FREE(mmd->cache_data);
}
Object *ob_target = DEG_get_evaluated_object(depsgraph, ob);
- Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false);
+ Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
mmd->cache_data = MEM_callocN(sizeof(ShrinkwrapTreeData), __func__);
if (BKE_shrinkwrap_init_tree(
mmd->cache_data, target, mmd->shrink_type, mmd->shrink_mode, false)) {
@@ -360,7 +360,8 @@ GpencilModifierData *BKE_gpencil_modifier_new(int type)
md->type = type;
md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render;
md->flag = eGpencilModifierFlag_OverrideLibrary_Local;
- md->ui_expand_flag = 1; /* Only expand the parent panel at first. */
+ /* Only expand the parent panel at first. */
+ md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) {
md->mode |= eGpencilModifierMode_Editmode;
@@ -694,7 +695,7 @@ static void gpencil_copy_visible_frames_to_eval(Depsgraph *depsgraph, Scene *sce
gpl_eval->actframe = BKE_gpencil_layer_frame_get(gpl_eval, remap_cfra, GP_GETFRAME_USE_PREV);
}
/* Always copy active frame to eval, because the modifiers always evaluate the active frame,
- * even if it's not visible (e.g. the layer is hidden).*/
+ * even if it's not visible (e.g. the layer is hidden). */
if (gpl_eval->actframe != NULL) {
copy_frame_to_eval_ex(gpl_eval->actframe->runtime.gpf_orig, gpl_eval->actframe);
}
diff --git a/source/blender/blenkernel/intern/icons_rasterize.c b/source/blender/blenkernel/intern/icons_rasterize.c
index 5603d84022d..00dbdcfa1e5 100644
--- a/source/blender/blenkernel/intern/icons_rasterize.c
+++ b/source/blender/blenkernel/intern/icons_rasterize.c
@@ -76,7 +76,7 @@ ImBuf *BKE_icon_geom_rasterize(const struct Icon_Geom *geom,
const uchar(*pos)[2] = geom->coords;
const uint *col = (void *)geom->colors;
- /* TODO(campbell): Currently rasterizes to fixed size, then scales.
+ /* TODO(@campbellbarton): Currently rasterizes to fixed size, then scales.
* Should rasterize to double size for eg instead. */
const int rect_size[2] = {max_ii(256, (int)size_x * 2), max_ii(256, (int)size_y * 2)};
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 35f02c29a00..98c317c547b 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -784,7 +784,7 @@ IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed)
if (create_if_needed) {
id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty");
id->properties->type = IDP_GROUP;
- /* NOTE(campbell): Don't overwrite the data's name and type
+ /* NOTE(@campbellbarton): Don't overwrite the data's name and type
* some functions might need this if they
* don't have a real ID, should be named elsewhere. */
// strcpy(id->name, "top_level_group");
@@ -1428,7 +1428,7 @@ void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const ch
}
}
-void IDP_BlendReadLib(BlendLibReader *reader, IDProperty *prop)
+void IDP_BlendReadLib(BlendLibReader *reader, Library *lib, IDProperty *prop)
{
if (!prop) {
return;
@@ -1437,7 +1437,7 @@ void IDP_BlendReadLib(BlendLibReader *reader, IDProperty *prop)
switch (prop->type) {
case IDP_ID: /* PointerProperty */
{
- void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop));
+ void *newaddr = BLO_read_get_new_id_address(reader, lib, IDP_Id(prop));
if (IDP_Id(prop) && !newaddr && G.debug) {
printf("Error while loading \"%s\". Data not found in file!\n", prop->name);
}
@@ -1448,14 +1448,14 @@ void IDP_BlendReadLib(BlendLibReader *reader, IDProperty *prop)
{
IDProperty *idp_array = IDP_IDPArray(prop);
for (int i = 0; i < prop->len; i++) {
- IDP_BlendReadLib(reader, &(idp_array[i]));
+ IDP_BlendReadLib(reader, lib, &(idp_array[i]));
}
break;
}
case IDP_GROUP: /* PointerProperty */
{
LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) {
- IDP_BlendReadLib(reader, loop);
+ IDP_BlendReadLib(reader, lib, loop);
}
break;
}
diff --git a/source/blender/blenkernel/intern/idprop_create.cc b/source/blender/blenkernel/intern/idprop_create.cc
index f549393fd12..8a6e5cdcc50 100644
--- a/source/blender/blenkernel/intern/idprop_create.cc
+++ b/source/blender/blenkernel/intern/idprop_create.cc
@@ -44,6 +44,14 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_n
return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
}
+std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name, ID *value)
+{
+ IDPropertyTemplate prop_template{0};
+ prop_template.id = value;
+ IDProperty *property = IDP_New(IDP_ID, &prop_template, prop_name.c_str());
+ return std::unique_ptr<IDProperty, IDPropertyDeleter>(property);
+}
+
static std::unique_ptr<IDProperty, IDPropertyDeleter> array_create(const StringRefNull prop_name,
eIDPropertyType subtype,
size_t array_len)
@@ -71,12 +79,12 @@ static void array_values_set(IDProperty *property,
template<
/** C-Primitive type of the array. Can be int32_t, float, double. */
typename PrimitiveType,
- /** Subtype of the ID_ARRAY. Must match PrimitiveType. */
+ /** Sub-type of the #ID_ARRAY. Must match #PrimitiveType. */
eIDPropertyType id_property_subtype>
std::unique_ptr<IDProperty, IDPropertyDeleter> create_array(StringRefNull prop_name,
Span<PrimitiveType> values)
{
- static_assert(std::is_same_v<PrimitiveType, int32_t> || std::is_same_v<PrimitiveType, float_t> ||
+ static_assert(std::is_same_v<PrimitiveType, int32_t> || std::is_same_v<PrimitiveType, float> ||
std::is_same_v<PrimitiveType, double>,
"Allowed values for PrimitiveType are int32_t, float and double.");
static_assert(!std::is_same_v<PrimitiveType, int32_t> || id_property_subtype == IDP_INT,
diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c
index e55143d6852..923582dff4c 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -209,7 +209,11 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode)
case ID_##_id: \
return FILTER_ID_##_id
- switch (idcode) {
+#define CASE_IDFILTER_NONE(_id) \
+ case ID_##_id: \
+ return 0
+
+ switch ((ID_Type)idcode) {
CASE_IDFILTER(AC);
CASE_IDFILTER(AR);
CASE_IDFILTER(BR);
@@ -220,7 +224,11 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(GR);
CASE_IDFILTER(CV);
CASE_IDFILTER(IM);
+ CASE_IDFILTER_NONE(IP);
+ CASE_IDFILTER(KE);
CASE_IDFILTER(LA);
+ CASE_IDFILTER(LI);
+ CASE_IDFILTER(LP);
CASE_IDFILTER(LS);
CASE_IDFILTER(LT);
CASE_IDFILTER(MA);
@@ -234,22 +242,25 @@ uint64_t BKE_idtype_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(PAL);
CASE_IDFILTER(PC);
CASE_IDFILTER(PT);
- CASE_IDFILTER(LP);
CASE_IDFILTER(SCE);
+ CASE_IDFILTER(SCR);
CASE_IDFILTER(SIM);
- CASE_IDFILTER(SPK);
CASE_IDFILTER(SO);
+ CASE_IDFILTER(SPK);
CASE_IDFILTER(TE);
CASE_IDFILTER(TXT);
CASE_IDFILTER(VF);
CASE_IDFILTER(VO);
+ CASE_IDFILTER(WM);
CASE_IDFILTER(WO);
CASE_IDFILTER(WS);
- default:
- return 0;
}
+ BLI_assert_unreachable();
+ return 0;
+
#undef CASE_IDFILTER
+#undef CASE_IDFILTER_NONE
}
short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter)
@@ -258,6 +269,8 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter)
case FILTER_ID_##_id: \
return ID_##_id
+#define CASE_IDFILTER_NONE(_id) (void)0
+
switch (idfilter) {
CASE_IDFILTER(AC);
CASE_IDFILTER(AR);
@@ -269,7 +282,11 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter)
CASE_IDFILTER(GR);
CASE_IDFILTER(CV);
CASE_IDFILTER(IM);
+ CASE_IDFILTER_NONE(IP);
+ CASE_IDFILTER(KE);
CASE_IDFILTER(LA);
+ CASE_IDFILTER(LI);
+ CASE_IDFILTER(LP);
CASE_IDFILTER(LS);
CASE_IDFILTER(LT);
CASE_IDFILTER(MA);
@@ -283,21 +300,25 @@ short BKE_idtype_idcode_from_idfilter(const uint64_t idfilter)
CASE_IDFILTER(PAL);
CASE_IDFILTER(PC);
CASE_IDFILTER(PT);
- CASE_IDFILTER(LP);
CASE_IDFILTER(SCE);
+ CASE_IDFILTER(SCR);
CASE_IDFILTER(SIM);
- CASE_IDFILTER(SPK);
CASE_IDFILTER(SO);
+ CASE_IDFILTER(SPK);
CASE_IDFILTER(TE);
CASE_IDFILTER(TXT);
CASE_IDFILTER(VF);
CASE_IDFILTER(VO);
+ CASE_IDFILTER(WM);
CASE_IDFILTER(WO);
- default:
- return 0;
+ CASE_IDFILTER(WS);
}
+ BLI_assert_unreachable();
+ return 0;
+
#undef CASE_IDFILTER
+#undef CASE_IDFILTER_NONE
}
int BKE_idtype_idcode_to_index(const short idcode)
diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc
index 92e98935e83..000e51c0150 100644
--- a/source/blender/blenkernel/intern/image.cc
+++ b/source/blender/blenkernel/intern/image.cc
@@ -442,7 +442,7 @@ constexpr IDTypeInfo get_type_info()
info.foreach_id = nullptr;
info.foreach_cache = image_foreach_cache;
info.foreach_path = image_foreach_path;
- info.owner_get = nullptr;
+ info.owner_pointer_get = nullptr;
info.blend_write = image_blend_write;
info.blend_read_data = image_blend_read_data;
@@ -627,6 +627,16 @@ void BKE_image_free_data(Image *ima)
image_free_data(&ima->id);
}
+static ImageTile *imagetile_alloc(int tile_number)
+{
+ ImageTile *tile = MEM_cnew<ImageTile>("Image Tile");
+ tile->tile_number = tile_number;
+ tile->gen_x = 1024;
+ tile->gen_y = 1024;
+ tile->gen_type = IMA_GENTYPE_GRID;
+ return tile;
+}
+
/* only image block itself */
static void image_init(Image *ima, short source, short type)
{
@@ -641,8 +651,7 @@ static void image_init(Image *ima, short source, short type)
ima->flag |= IMA_VIEW_AS_RENDER;
}
- ImageTile *tile = MEM_cnew<ImageTile>("Image Tiles");
- tile->tile_number = 1001;
+ ImageTile *tile = imagetile_alloc(1001);
BLI_addtail(&ima->tiles, tile);
if (type == IMA_TYPE_R_RESULT) {
@@ -785,7 +794,7 @@ bool BKE_image_has_opengl_texture(Image *ima)
return false;
}
-static int image_get_tile_number_from_iuser(Image *ima, const ImageUser *iuser)
+static int image_get_tile_number_from_iuser(const Image *ima, const ImageUser *iuser)
{
BLI_assert(ima != nullptr && ima->tiles.first);
ImageTile *tile = static_cast<ImageTile *>(ima->tiles.first);
@@ -863,33 +872,98 @@ void BKE_image_get_tile_uv(const Image *ima, const int tile_number, float r_uv[2
}
}
-int BKE_image_find_nearest_tile(const Image *image, const float co[2])
+/** Linear distance between #x and the unit interval. */
+static float distance_to_unit_interval(float x)
{
- const float co_floor[2] = {floorf(co[0]), floorf(co[1])};
- /* Distance to the closest UDIM tile. */
- float dist_best_sq = FLT_MAX;
+ /* The unit interval is between 0 and 1.
+ * Within the interval, return 0.
+ * Outside the interval, return the distance to the nearest boundary.
+ * Intuitively, the function looks like:
+ * \ | | /
+ * __\|___|/__
+ * 0 1
+ */
+
+ if (x <= 0.0f) {
+ return -x; /* Distance to left border. */
+ }
+ if (x <= 1.0f) {
+ return 0.0f; /* Inside unit interval. */
+ }
+ return x - 1.0f; /* Distance to right border. */
+}
+
+/** Distance squared between #co and the unit square with lower-left starting at #udim. */
+static float distance_squared_to_udim(const float co[2], const float udim[2])
+{
+ float delta[2];
+ sub_v2_v2v2(delta, co, udim);
+ delta[0] = distance_to_unit_interval(delta[0]);
+ delta[1] = distance_to_unit_interval(delta[1]);
+ return len_squared_v2(delta);
+}
+
+static bool nearest_udim_tile_tie_break(const float best_dist_sq,
+ const float best_uv[2],
+ const float dist_sq,
+ const float uv[2])
+{
+ if (best_dist_sq == dist_sq) { /* Exact same distance? Tie-break. */
+ if (best_uv[0] == uv[0]) { /* Exact same U? Tie-break. */
+ return (uv[1] > best_uv[1]); /* Higher than previous candidate? */
+ }
+ return (uv[0] > best_uv[0]); /* Further right than previous candidate? */
+ }
+ return (dist_sq < best_dist_sq); /* Closer than previous candidate? */
+}
+
+int BKE_image_find_nearest_tile_with_offset(const Image *image,
+ const float co[2],
+ float r_uv_offset[2])
+{
+ /* NOTE: If the co-ordinates are integers, take special care to break ties. */
+
+ zero_v2(r_uv_offset);
int tile_number_best = -1;
+ if (!image || image->source != IMA_SRC_TILED) {
+ return tile_number_best;
+ }
+
+ /* Distance squared to the closest UDIM tile. */
+ float dist_best_sq = FLT_MAX;
+
LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) {
float uv_offset[2];
BKE_image_get_tile_uv(image, tile->tile_number, uv_offset);
- if (equals_v2v2(co_floor, uv_offset)) {
- return tile->tile_number;
- }
+ /* Distance squared between #co and closest point on UDIM tile. */
+ const float dist_sq = distance_squared_to_udim(co, uv_offset);
- /* Distance between co[2] and UDIM tile. */
- const float dist_sq = len_squared_v2v2(uv_offset, co);
+ if (dist_sq == 0) { /* Either inside in the UDIM, or on its boundary. */
+ if (floorf(co[0]) == uv_offset[0] && floorf(co[1]) == uv_offset[1]) {
+ /* Within the half-open interval of the UDIM. */
+ copy_v2_v2(r_uv_offset, uv_offset);
+ return tile_number_best;
+ }
+ }
- if (dist_sq < dist_best_sq) {
+ if (nearest_udim_tile_tie_break(dist_best_sq, r_uv_offset, dist_sq, uv_offset)) {
+ /* Tile is better than previous best, update. */
dist_best_sq = dist_sq;
+ copy_v2_v2(r_uv_offset, uv_offset);
tile_number_best = tile->tile_number;
}
}
-
return tile_number_best;
}
+int BKE_image_find_nearest_tile(const struct Image *image, const float co[2])
+{
+ float uv_offset_dummy[2];
+ return BKE_image_find_nearest_tile_with_offset(image, co, uv_offset_dummy);
+}
+
static void image_init_color_management(Image *ima)
{
ImBuf *ibuf;
@@ -897,7 +971,7 @@ static void image_init_color_management(Image *ima)
BKE_image_user_file_path(nullptr, ima, name);
- /* will set input color space to image format default's */
+ /* Will set input color space to image format default's. */
ibuf = IMB_loadiffname(name, IB_test | IB_alphamode_detect, ima->colorspace_settings.name);
if (ibuf) {
@@ -1035,73 +1109,70 @@ static void image_buf_fill_isolated(void *usersata_v)
}
}
-static ImBuf *add_ibuf_size(unsigned int width,
- unsigned int height,
- const char *name,
- int depth,
- int floatbuf,
- short gen_type,
- const float color[4],
- ColorManagedColorspaceSettings *colorspace_settings)
+static ImBuf *add_ibuf_for_tile(Image *ima, ImageTile *tile)
{
ImBuf *ibuf;
unsigned char *rect = nullptr;
float *rect_float = nullptr;
float fill_color[4];
+ const bool floatbuf = (tile->gen_flag & IMA_GEN_FLOAT) != 0;
if (floatbuf) {
- ibuf = IMB_allocImBuf(width, height, depth, IB_rectfloat);
+ ibuf = IMB_allocImBuf(tile->gen_x, tile->gen_y, tile->gen_depth, IB_rectfloat);
- if (colorspace_settings->name[0] == '\0') {
+ if (ima->colorspace_settings.name[0] == '\0') {
const char *colorspace = IMB_colormanagement_role_colorspace_name_get(
COLOR_ROLE_DEFAULT_FLOAT);
- STRNCPY(colorspace_settings->name, colorspace);
+ STRNCPY(ima->colorspace_settings.name, colorspace);
}
if (ibuf != nullptr) {
rect_float = ibuf->rect_float;
- IMB_colormanagement_check_is_data(ibuf, colorspace_settings->name);
+ IMB_colormanagement_check_is_data(ibuf, ima->colorspace_settings.name);
}
- if (IMB_colormanagement_space_name_is_data(colorspace_settings->name)) {
- copy_v4_v4(fill_color, color);
+ if (IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) {
+ copy_v4_v4(fill_color, tile->gen_color);
}
else {
/* The input color here should ideally be linear already, but for now
* we just convert and postpone breaking the API for later. */
- srgb_to_linearrgb_v4(fill_color, color);
+ srgb_to_linearrgb_v4(fill_color, tile->gen_color);
}
}
else {
- ibuf = IMB_allocImBuf(width, height, depth, IB_rect);
+ ibuf = IMB_allocImBuf(tile->gen_x, tile->gen_y, tile->gen_depth, IB_rect);
- if (colorspace_settings->name[0] == '\0') {
+ if (ima->colorspace_settings.name[0] == '\0') {
const char *colorspace = IMB_colormanagement_role_colorspace_name_get(
COLOR_ROLE_DEFAULT_BYTE);
- STRNCPY(colorspace_settings->name, colorspace);
+ STRNCPY(ima->colorspace_settings.name, colorspace);
}
if (ibuf != nullptr) {
rect = (unsigned char *)ibuf->rect;
- IMB_colormanagement_assign_rect_colorspace(ibuf, colorspace_settings->name);
+ IMB_colormanagement_assign_rect_colorspace(ibuf, ima->colorspace_settings.name);
}
- copy_v4_v4(fill_color, color);
+ copy_v4_v4(fill_color, tile->gen_color);
}
if (!ibuf) {
return nullptr;
}
- STRNCPY(ibuf->name, name);
+ STRNCPY(ibuf->name, ima->filepath);
+
+ /* Mark the tile itself as having been generated. */
+ tile->gen_flag |= IMA_GEN_TILE;
ImageFillData data;
- data.gen_type = gen_type;
- data.width = width;
- data.height = height;
+ data.gen_type = tile->gen_type;
+ data.width = tile->gen_x;
+ data.height = tile->gen_y;
data.rect = rect;
data.rect_float = rect_float;
copy_v4_v4(data.fill_color, fill_color);
@@ -1140,12 +1211,16 @@ Image *BKE_image_add_generated(Main *bmain,
/* NOTE: leave `ima->filepath` unset,
* setting it to a dummy value may write to an invalid file-path. */
- ima->gen_x = width;
- ima->gen_y = height;
- ima->gen_type = gen_type;
- ima->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0);
- ima->gen_depth = depth;
- copy_v4_v4(ima->gen_color, color);
+
+ /* The generation info is always stored in the tiles. The first tile
+ * will be used for non-tiled images. */
+ ImageTile *tile = static_cast<ImageTile *>(ima->tiles.first);
+ tile->gen_x = width;
+ tile->gen_y = height;
+ tile->gen_type = gen_type;
+ tile->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0);
+ tile->gen_depth = depth;
+ copy_v4_v4(tile->gen_color, color);
if (is_data) {
STRNCPY(ima->colorspace_settings.name,
@@ -1154,8 +1229,7 @@ Image *BKE_image_add_generated(Main *bmain,
for (view_id = 0; view_id < 2; view_id++) {
ImBuf *ibuf;
- ibuf = add_ibuf_size(
- width, height, ima->filepath, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
+ ibuf = add_ibuf_for_tile(ima, tile);
int index = tiled ? 0 : IMA_NO_INDEX;
int entry = tiled ? 1001 : 0;
image_assign_ibuf(ima, ibuf, stereo3d ? view_id : index, entry);
@@ -1174,7 +1248,7 @@ Image *BKE_image_add_generated(Main *bmain,
static void image_colorspace_from_imbuf(Image *image, const ImBuf *ibuf)
{
- const char *colorspace_name = NULL;
+ const char *colorspace_name = nullptr;
if (ibuf->rect_float) {
if (ibuf->float_colorspace) {
@@ -2475,7 +2549,10 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, const ImageFormatData *imf)
return ok;
}
-int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, const bool save_copy)
+int BKE_imbuf_write_as(ImBuf *ibuf,
+ const char *name,
+ const ImageFormatData *imf,
+ const bool save_copy)
{
ImBuf ibuf_back = *ibuf;
int ok;
@@ -2585,7 +2662,7 @@ Image *BKE_image_ensure_viewer(Main *bmain, int type, const char *name)
ima = image_alloc(bmain, name, IMA_SRC_VIEWER, type);
}
- /* Happens on reload, imagewindow cannot be image user when hidden. */
+ /* Happens on reload, image-window cannot be image user when hidden. */
if (ima->id.us == 0) {
id_us_ensure_real(&ima->id);
}
@@ -2933,6 +3010,28 @@ static void image_free_tile(Image *ima, ImageTile *tile)
}
}
+static bool image_remove_tile(Image *ima, ImageTile *tile)
+{
+ if (BLI_listbase_is_single(&ima->tiles)) {
+ /* Can't remove the last remaining tile. */
+ return false;
+ }
+
+ image_free_tile(ima, tile);
+ BLI_remlink(&ima->tiles, tile);
+ MEM_freeN(tile);
+
+ return true;
+}
+
+static void image_remove_all_tiles(Image *ima)
+{
+ /* Remove all but the final tile. */
+ while (image_remove_tile(ima, static_cast<ImageTile *>(ima->tiles.last))) {
+ ;
+ }
+}
+
void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
{
if (ima == nullptr) {
@@ -2959,11 +3058,12 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
}
if (ima->source == IMA_SRC_GENERATED) {
- if (ima->gen_x == 0 || ima->gen_y == 0) {
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ if (base_tile->gen_x == 0 || base_tile->gen_y == 0) {
ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr);
if (ibuf) {
- ima->gen_x = ibuf->x;
- ima->gen_y = ibuf->y;
+ base_tile->gen_x = ibuf->x;
+ base_tile->gen_y = ibuf->y;
IMB_freeImBuf(ibuf);
}
}
@@ -2980,16 +3080,40 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
if (ima->source != IMA_SRC_TILED) {
/* Free all but the first tile. */
+ image_remove_all_tiles(ima);
+
+ /* If the remaining tile is generated, we need to again ensure that we
+ * wouldn't continue to use the old filepath.
+ *
+ * Otherwise, if this used to be a UDIM image, get the concrete filepath associated
+ * with the remaining tile and use that as the new filepath. */
ImageTile *base_tile = BKE_image_get_tile(ima, 0);
- BLI_assert(base_tile == ima->tiles.first);
- for (ImageTile *tile = base_tile->next, *tile_next; tile; tile = tile_next) {
- tile_next = tile->next;
- image_free_tile(ima, tile);
- MEM_freeN(tile);
+ if ((base_tile->gen_flag & IMA_GEN_TILE) != 0) {
+ ima->filepath[0] = '\0';
+ }
+ else if (BKE_image_is_filename_tokenized(ima->filepath)) {
+ const bool was_relative = BLI_path_is_rel(ima->filepath);
+
+ eUDIM_TILE_FORMAT tile_format;
+ char *udim_pattern = BKE_image_get_tile_strformat(ima->filepath, &tile_format);
+ BKE_image_set_filepath_from_tile_number(
+ ima->filepath, udim_pattern, tile_format, base_tile->tile_number);
+ MEM_freeN(udim_pattern);
+
+ if (was_relative) {
+ const char *relbase = ID_BLEND_PATH(bmain, &ima->id);
+ BLI_path_rel(ima->filepath, relbase);
+ }
}
- base_tile->next = nullptr;
+
+ /* If the remaining tile was not number 1001, we need to reassign it so that
+ * ibuf lookups from the cache still succeed. */
base_tile->tile_number = 1001;
- ima->tiles.last = base_tile;
+ }
+ else {
+ /* When changing to UDIM, attempt to tokenize the filepath. */
+ char *filename = (char *)BLI_path_basename(ima->filepath);
+ BKE_image_ensure_tile_token(filename);
}
/* image buffers for non-sequence multilayer will share buffers with RenderResult,
@@ -3005,6 +3129,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
image_tag_frame_recalc(ima, nullptr, iuser, ima);
}
BKE_image_walk_all_users(bmain, ima, image_tag_frame_recalc);
+ BKE_image_partial_update_mark_full_update(ima);
break;
@@ -3056,9 +3181,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
* to account for how the two sets might or might not overlap. To be complete, we start
* the refresh process by clearing all existing tiles, stopping when there's only 1 tile
* left. */
- while (BKE_image_remove_tile(ima, static_cast<ImageTile *>(ima->tiles.last))) {
- ;
- }
+ image_remove_all_tiles(ima);
int remaining_tile_number = ((ImageTile *)ima->tiles.first)->tile_number;
bool needs_final_cleanup = true;
@@ -3241,8 +3364,7 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la
}
}
- ImageTile *tile = MEM_cnew<ImageTile>("image new tile");
- tile->tile_number = tile_number;
+ ImageTile *tile = imagetile_alloc(tile_number);
if (next_tile) {
BLI_insertlinkbefore(&ima->tiles, next_tile, tile);
@@ -3277,16 +3399,7 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile)
return false;
}
- if (BLI_listbase_is_single(&ima->tiles)) {
- /* Can't remove the last remaining tile. */
- return false;
- }
-
- image_free_tile(ima, tile);
- BLI_remlink(&ima->tiles, tile);
- MEM_freeN(tile);
-
- return true;
+ return image_remove_tile(ima, tile);
}
void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number)
@@ -3348,14 +3461,7 @@ void BKE_image_sort_tiles(struct Image *ima)
BLI_listbase_sort(&ima->tiles, tile_sort_cb);
}
-bool BKE_image_fill_tile(struct Image *ima,
- ImageTile *tile,
- int width,
- int height,
- const float color[4],
- int gen_type,
- int planes,
- bool is_float)
+bool BKE_image_fill_tile(struct Image *ima, ImageTile *tile)
{
if (ima == nullptr || tile == nullptr || ima->source != IMA_SRC_TILED) {
return false;
@@ -3363,8 +3469,7 @@ bool BKE_image_fill_tile(struct Image *ima,
image_free_tile(ima, tile);
- ImBuf *tile_ibuf = add_ibuf_size(
- width, height, ima->filepath, planes, is_float, gen_type, color, &ima->colorspace_settings);
+ ImBuf *tile_ibuf = add_ibuf_for_tile(ima, tile);
if (tile_ibuf != nullptr) {
image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number);
@@ -3394,9 +3499,8 @@ void BKE_image_ensure_tile_token(char *filename)
/* General 4-digit "udim" pattern. As this format is susceptible to ambiguity
* with other digit sequences, we can leverage the supported range of roughly
- * 1000 through 2000 to provide better detection.
- */
- std::regex pattern(R"((^|.*?\D)([12]\d{3})(\D.*))");
+ * 1000 through 2000 to provide better detection. */
+ std::regex pattern(R"((.*[._-])([12]\d{3})([._-].*))");
if (std::regex_search(path, match, pattern)) {
BLI_strncpy(filename, match.format("$1<UDIM>$3").c_str(), FILE_MAX);
return;
@@ -3547,7 +3651,7 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser)
return rpass;
}
-void BKE_image_multiview_index(Image *ima, ImageUser *iuser)
+void BKE_image_multiview_index(const Image *ima, ImageUser *iuser)
{
if (iuser) {
bool is_stereo = BKE_image_is_stereo(ima) && (iuser->flag & IMA_SHOW_STEREO);
@@ -3568,7 +3672,7 @@ void BKE_image_multiview_index(Image *ima, ImageUser *iuser)
/* if layer or pass changes, we need an index for the imbufs list */
/* note it is called for rendered results, but it doesn't use the index! */
-bool BKE_image_is_multilayer(Image *ima)
+bool BKE_image_is_multilayer(const Image *ima)
{
if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) {
if (ima->type == IMA_TYPE_MULTILAYER) {
@@ -3583,13 +3687,13 @@ bool BKE_image_is_multilayer(Image *ima)
return false;
}
-bool BKE_image_is_multiview(Image *ima)
+bool BKE_image_is_multiview(const Image *ima)
{
ImageView *view = static_cast<ImageView *>(ima->views.first);
return (view && (view->next || view->name[0]));
}
-bool BKE_image_is_stereo(Image *ima)
+bool BKE_image_is_stereo(const Image *ima)
{
return BKE_image_is_multiview(ima) &&
(BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)) &&
@@ -4538,14 +4642,22 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
}
}
else if (ima->source == IMA_SRC_TILED) {
- if (ima->type == IMA_TYPE_IMAGE) {
- /* Regular files, ibufs in flip-book, allows saving */
- ibuf = image_load_image_file(ima, iuser, entry, 0, false);
+ /* Nothing was cached. Check to see if the tile should be generated. */
+ ImageTile *tile = BKE_image_get_tile(ima, entry);
+ if ((tile->gen_flag & IMA_GEN_TILE) != 0) {
+ ibuf = add_ibuf_for_tile(ima, tile);
+ image_assign_ibuf(ima, ibuf, 0, entry);
}
- /* no else; on load the ima type can change */
- if (ima->type == IMA_TYPE_MULTILAYER) {
- /* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */
- ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0);
+ else {
+ if (ima->type == IMA_TYPE_IMAGE) {
+ /* Regular files, ibufs in flip-book, allows saving */
+ ibuf = image_load_image_file(ima, iuser, entry, 0, false);
+ }
+ /* no else; on load the ima type can change */
+ if (ima->type == IMA_TYPE_MULTILAYER) {
+ /* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */
+ ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0);
+ }
}
}
else if (ima->source == IMA_SRC_FILE) {
@@ -4563,23 +4675,17 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock)
else if (ima->source == IMA_SRC_GENERATED) {
/* Generated is: `ibuf` is allocated dynamically. */
/* UV test-grid or black or solid etc. */
- if (ima->gen_x == 0) {
- ima->gen_x = 1024;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ if (base_tile->gen_x == 0) {
+ base_tile->gen_x = 1024;
}
- if (ima->gen_y == 0) {
- ima->gen_y = 1024;
+ if (base_tile->gen_y == 0) {
+ base_tile->gen_y = 1024;
}
- if (ima->gen_depth == 0) {
- ima->gen_depth = 24;
+ if (base_tile->gen_depth == 0) {
+ base_tile->gen_depth = 24;
}
- ibuf = add_ibuf_size(ima->gen_x,
- ima->gen_y,
- ima->filepath,
- ima->gen_depth,
- (ima->gen_flag & IMA_GEN_FLOAT) != 0,
- ima->gen_type,
- ima->gen_color,
- &ima->colorspace_settings);
+ ibuf = add_ibuf_for_tile(ima, base_tile);
image_assign_ibuf(ima, ibuf, index, 0);
}
else if (ima->source == IMA_SRC_VIEWER) {
@@ -4922,10 +5028,12 @@ static void image_editors_update_frame(Image *ima,
ImageUser *iuser,
void *customdata)
{
- int cfra = *(int *)customdata;
+ if (ima && BKE_image_is_animated(ima)) {
+ if ((iuser->flag & IMA_ANIM_ALWAYS) || (iuser->flag & IMA_NEED_FRAME_RECALC)) {
+ int cfra = *(int *)customdata;
- if ((iuser->flag & IMA_ANIM_ALWAYS) || (iuser->flag & IMA_NEED_FRAME_RECALC)) {
- BKE_image_user_frame_calc(ima, iuser, cfra);
+ BKE_image_user_frame_calc(ima, iuser, cfra);
+ }
}
}
@@ -4985,14 +5093,19 @@ void BKE_image_user_id_eval_animation(Depsgraph *depsgraph, ID *id)
image_walk_id_all_users(id, skip_nested_nodes, depsgraph, image_user_id_eval_animation);
}
-void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath)
+void BKE_image_user_file_path(const ImageUser *iuser, const Image *ima, char *filepath)
{
- BKE_image_user_file_path_ex(iuser, ima, filepath, true);
+ BKE_image_user_file_path_ex(G_MAIN, iuser, ima, filepath, true, true);
}
-void BKE_image_user_file_path_ex(ImageUser *iuser, Image *ima, char *filepath, bool resolve_udim)
+void BKE_image_user_file_path_ex(const Main *bmain,
+ const ImageUser *iuser,
+ const Image *ima,
+ char *filepath,
+ const bool resolve_udim,
+ const bool resolve_multiview)
{
- if (BKE_image_is_multiview(ima)) {
+ if (resolve_multiview && BKE_image_is_multiview(ima)) {
ImageView *iv = static_cast<ImageView *>(BLI_findlink(&ima->views, iuser->view));
if (iv->filepath[0]) {
BLI_strncpy(filepath, iv->filepath, FILE_MAX);
@@ -5025,7 +5138,7 @@ void BKE_image_user_file_path_ex(ImageUser *iuser, Image *ima, char *filepath, b
}
}
- BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
+ BLI_path_abs(filepath, ID_BLEND_PATH(bmain, &ima->id));
}
bool BKE_image_has_alpha(Image *image)
@@ -5057,13 +5170,7 @@ void BKE_image_get_size(Image *image, ImageUser *iuser, int *r_width, int *r_hei
}
else if (image != nullptr && image->type == IMA_TYPE_R_RESULT && iuser != nullptr &&
iuser->scene != nullptr) {
- Scene *scene = iuser->scene;
- *r_width = (scene->r.xsch * scene->r.size) / 100;
- *r_height = (scene->r.ysch * scene->r.size) / 100;
- if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) {
- *r_width *= BLI_rctf_size_x(&scene->r.border);
- *r_height *= BLI_rctf_size_y(&scene->r.border);
- }
+ BKE_render_resolution(&iuser->scene->r, true, r_width, r_height);
}
else {
*r_width = IMG_SIZE_FALLBACK;
@@ -5412,7 +5519,7 @@ RenderSlot *BKE_image_add_renderslot(Image *ima, const char *name)
}
else {
int n = BLI_listbase_count(&ima->renderslots) + 1;
- BLI_snprintf(slot->name, sizeof(slot->name), "Slot %d", n);
+ BLI_snprintf(slot->name, sizeof(slot->name), DATA_("Slot %d"), n);
}
BLI_addtail(&ima->renderslots, slot);
return slot;
@@ -5460,15 +5567,14 @@ bool BKE_image_remove_renderslot(Image *ima, ImageUser *iuser, int slot)
next_last_slot = current_slot;
}
- if (!iuser) {
+ if (iuser == nullptr || iuser->scene == nullptr) {
return false;
}
Render *re = RE_GetSceneRender(iuser->scene);
- if (!re) {
- return false;
+ if (re != nullptr) {
+ RE_SwapResult(re, &current_last_slot->render);
+ RE_SwapResult(re, &next_last_slot->render);
}
- RE_SwapResult(re, &current_last_slot->render);
- RE_SwapResult(re, &next_last_slot->render);
current_last_slot = next_last_slot;
}
@@ -5505,7 +5611,7 @@ bool BKE_image_clear_renderslot(Image *ima, ImageUser *iuser, int slot)
}
RenderSlot *render_slot = static_cast<RenderSlot *>(BLI_findlink(&ima->renderslots, slot));
- if (!slot) {
+ if (!render_slot) {
return false;
}
if (render_slot->render) {
diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc
index 6edb9e1b24c..08fdd715512 100644
--- a/source/blender/blenkernel/intern/image_gpu.cc
+++ b/source/blender/blenkernel/intern/image_gpu.cc
@@ -138,6 +138,8 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
int arraywidth = 0, arrayheight = 0;
ListBase boxes = {nullptr};
+ int planes = 0;
+
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
@@ -164,6 +166,7 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
BKE_image_release_ibuf(ima, ibuf, nullptr);
BLI_addtail(&boxes, packtile);
+ planes = max_ii(planes, ibuf->planes);
}
}
@@ -195,9 +198,15 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
}
const bool use_high_bitdepth = (ima->flag & IMA_HIGH_BITDEPTH);
+ const bool use_grayscale = planes <= 8;
/* Create Texture without content. */
- GPUTexture *tex = IMB_touch_gpu_texture(
- ima->id.name + 2, main_ibuf, arraywidth, arrayheight, arraylayers, use_high_bitdepth);
+ GPUTexture *tex = IMB_touch_gpu_texture(ima->id.name + 2,
+ main_ibuf,
+ arraywidth,
+ arrayheight,
+ arraylayers,
+ use_high_bitdepth,
+ use_grayscale);
/* Upload each tile one by one. */
LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
@@ -223,6 +232,7 @@ static GPUTexture *gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf)
tilelayer,
UNPACK2(tilesize),
use_high_bitdepth,
+ use_grayscale,
store_premultiplied);
}
@@ -737,11 +747,11 @@ static void gpu_texture_update_from_ibuf(
}
else {
/* Byte image is in original colorspace from the file, and may need conversion. */
- if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace) ||
- IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) {
+ if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) {
/* Non-color data, just store buffer as is. */
}
- else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) {
+ else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) ||
+ IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) {
/* sRGB or scene linear, store as byte texture that the GPU can decode directly. */
rect = (uchar *)MEM_mallocN(sizeof(uchar[4]) * w * h, __func__);
if (rect == nullptr) {
diff --git a/source/blender/blenkernel/intern/image_partial_update.cc b/source/blender/blenkernel/intern/image_partial_update.cc
index c77ee77a5f2..6ffd323cc1e 100644
--- a/source/blender/blenkernel/intern/image_partial_update.cc
+++ b/source/blender/blenkernel/intern/image_partial_update.cc
@@ -413,7 +413,7 @@ struct PartialUpdateRegisterImpl {
}
/**
- * /brief Check if data is available to construct the update tiles for the given
+ * \brief Check if data is available to construct the update tiles for the given
* changeset_id.
*
* The update tiles can be created when changeset id is between
diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc
index 3f6f81845e2..6f62ee123cb 100644
--- a/source/blender/blenkernel/intern/image_save.cc
+++ b/source/blender/blenkernel/intern/image_save.cc
@@ -13,6 +13,8 @@
#include "BLI_string.h"
#include "BLI_vector.hh"
+#include "BLT_translation.h"
+
#include "DNA_image_types.h"
#include "MEM_guardedalloc.h"
@@ -144,13 +146,9 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts,
opts->im_format.color_management = R_IMF_COLOR_MANAGEMENT_FOLLOW_SCENE;
- if (ibuf->name[0] == '\0' || ima->source == IMA_SRC_TILED) {
- BLI_strncpy(opts->filepath, ima->filepath, sizeof(opts->filepath));
- BLI_path_abs(opts->filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id));
- }
- else {
- BLI_strncpy(opts->filepath, ibuf->name, sizeof(opts->filepath));
- }
+ /* Compute filepath, but don't resolve multiview and UDIM which are handled
+ * by the image saving code itself. */
+ BKE_image_user_file_path_ex(bmain, iuser, ima, opts->filepath, false, false);
/* sanitize all settings */
@@ -177,12 +175,12 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts,
BLI_strncpy(opts->filepath, G.ima, sizeof(opts->filepath));
}
else {
- BLI_strncpy(opts->filepath, "//untitled", sizeof(opts->filepath));
+ BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", DATA_("untitled"), nullptr);
BLI_path_abs(opts->filepath, BKE_main_blendfile_path(bmain));
}
}
else {
- BLI_snprintf(opts->filepath, sizeof(opts->filepath), "//%s", ima->id.name + 2);
+ BLI_path_join(opts->filepath, sizeof(opts->filepath), "//", ima->id.name + 2, nullptr);
BLI_path_make_safe(opts->filepath);
BLI_path_abs(opts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain));
}
@@ -204,7 +202,7 @@ bool BKE_image_save_options_init(ImageSaveOptions *opts,
return (ibuf != nullptr);
}
-void BKE_image_save_options_update(ImageSaveOptions *opts, Image *image)
+void BKE_image_save_options_update(ImageSaveOptions *opts, const Image *image)
{
/* Auto update color space when changing save as render and file type. */
if (opts->save_as_render) {
@@ -272,7 +270,7 @@ static void image_save_post(ReportList *reports,
Image *ima,
ImBuf *ibuf,
int ok,
- ImageSaveOptions *opts,
+ const ImageSaveOptions *opts,
int save_copy,
const char *filepath,
bool *r_colorspace_changed)
@@ -320,6 +318,8 @@ static void image_save_post(ReportList *reports,
if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) {
ima->source = IMA_SRC_FILE;
ima->type = IMA_TYPE_IMAGE;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ base_tile->gen_flag &= ~IMA_GEN_TILE;
}
/* Update image file color space when saving to another color space. */
@@ -359,7 +359,7 @@ static void imbuf_save_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf)
static bool image_save_single(ReportList *reports,
Image *ima,
ImageUser *iuser,
- ImageSaveOptions *opts,
+ const ImageSaveOptions *opts,
bool *r_colorspace_changed)
{
void *lock;
@@ -375,7 +375,7 @@ static bool image_save_single(ReportList *reports,
ImBuf *colormanaged_ibuf = nullptr;
const bool save_copy = opts->save_copy;
const bool save_as_render = opts->save_as_render;
- ImageFormatData *imf = &opts->im_format;
+ const ImageFormatData *imf = &opts->im_format;
if (ima->type == IMA_TYPE_R_RESULT) {
/* enforce user setting for RGB or RGBA, but skip BW */
@@ -620,7 +620,7 @@ static bool image_save_single(ReportList *reports,
}
bool BKE_image_save(
- ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, ImageSaveOptions *opts)
+ ReportList *reports, Main *bmain, Image *ima, ImageUser *iuser, const ImageSaveOptions *opts)
{
/* For saving a tiled image we need an iuser, so use a local one if there isn't already one. */
ImageUser save_iuser;
@@ -666,8 +666,11 @@ bool BKE_image_save(
}
}
- /* Set the image path only if all tiles were ok. */
+ /* Set the image path and clear the per-tile generated flag only if all tiles were ok. */
if (ok) {
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ tile->gen_flag &= ~IMA_GEN_TILE;
+ }
image_save_update_filepath(ima, opts->filepath, opts);
}
MEM_freeN(udim_pattern);
@@ -820,7 +823,7 @@ bool BKE_image_render_write_exr(ReportList *reports,
const bool pass_RGBA = (STR_ELEM(rp->chan_id, "RGB", "RGBA", "R", "G", "B", "A"));
const bool pass_half_float = half_float && pass_RGBA;
- /* Colorspace conversion only happens on RGBA passes. */
+ /* Color-space conversion only happens on RGBA passes. */
float *output_rect =
(save_as_render && pass_RGBA) ?
image_exr_from_scene_linear_to_output(
diff --git a/source/blender/blenkernel/intern/image_test.cc b/source/blender/blenkernel/intern/image_test.cc
index 9c15fc62d21..7004d39c805 100644
--- a/source/blender/blenkernel/intern/image_test.cc
+++ b/source/blender/blenkernel/intern/image_test.cc
@@ -32,8 +32,12 @@ TEST(udim, image_ensure_tile_token)
verify("test_1002_ao.png", "test_<UDIM>_ao.png");
verify("test.1002.ver0023.png", "test.<UDIM>.ver0023.png");
verify("test.ver0023.1002.png", "test.ver0023.<UDIM>.png");
- verify("1002test.png", "<UDIM>test.png");
- verify("test1002.png", "test<UDIM>.png");
+ verify("test.1002.1.png", "test.<UDIM>.1.png");
+ verify("test.1.1002.png", "test.1.<UDIM>.png");
+ verify("test-2022-01-01.1002.png", "test-2022-01-01.<UDIM>.png");
+ verify("1111_11.1002.png", "1111_11.<UDIM>.png");
+ verify("2111_01.1002.png", "2111_01.<UDIM>.png");
+ verify("2022_1002_100200.1002.png", "2022_1002_100200.<UDIM>.png");
/* UVTILE pattern detection. */
verify("uv-test.u2_v10.png", "uv-test.<UVTILE>.png");
@@ -44,8 +48,15 @@ TEST(udim, image_ensure_tile_token)
verify("u2_v10uv-test.png", "<UVTILE>uv-test.png");
verify("u2_v10u_v-test.png", "<UVTILE>u_v-test.png");
- /* Incorrect patterns. */
- for (const char *incorrect : {"test.123.png",
+ /* Patterns which should not be detected as UDIMs. */
+ for (const char *incorrect : {"1002.png",
+ "1002test.png",
+ "test1002.png",
+ "test(1002).png",
+ "(1002)test.png",
+ "test-1080p.png",
+ "test-1920x1080.png",
+ "test.123.png",
"test.12345.png",
"test.uv.png",
"test.u1v.png",
diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c
index abd6505456e..40a5c08a068 100644
--- a/source/blender/blenkernel/intern/ipo.c
+++ b/source/blender/blenkernel/intern/ipo.c
@@ -178,7 +178,7 @@ IDTypeInfo IDType_ID_IP = {
.foreach_id = NULL,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = NULL,
.blend_read_data = ipo_blend_read_data,
@@ -2142,7 +2142,7 @@ void do_versions_ipos_to_animato(Main *bmain)
if (ob->action) {
action_to_animdata(id, ob->action);
- /* only decrease usercount if this Action isn't now being used by AnimData */
+ /* Only decrease user-count if this Action isn't now being used by AnimData. */
if (ob->action != adt->action) {
id_us_min(&ob->action->id);
ob->action = NULL;
@@ -2246,7 +2246,7 @@ void do_versions_ipos_to_animato(Main *bmain)
/* Add AnimData block */
AnimData *adt = BKE_animdata_ensure_id(id);
- /* Convert Shapekey data... */
+ /* Convert Shape-key data... */
ipo_to_animdata(bmain, id, key->ipo, NULL, NULL, NULL);
if (adt->action) {
diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c
index 594cffe6406..2ba81c54872 100644
--- a/source/blender/blenkernel/intern/key.c
+++ b/source/blender/blenkernel/intern/key.c
@@ -46,6 +46,7 @@
#include "BKE_scene.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "BLO_read_write.h"
@@ -90,9 +91,14 @@ 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)
+static ID **shapekey_owner_pointer_get(ID *id)
{
- return ((Key *)id)->from;
+ Key *key = (Key *)id;
+
+ BLI_assert(key->from != NULL);
+ BLI_assert(BKE_key_from_id(key->from) == key);
+
+ return &key->from;
}
static void shapekey_blend_write(BlendWriter *writer, ID *id, const void *id_address)
@@ -191,7 +197,7 @@ static void shapekey_blend_read_expand(BlendExpander *expander, ID *id)
IDTypeInfo IDType_ID_KE = {
.id_code = ID_KE,
- .id_filter = 0,
+ .id_filter = FILTER_ID_KE,
.main_listbase_index = INDEX_ID_KE,
.struct_size = sizeof(Key),
.name = "Key",
@@ -209,7 +215,7 @@ IDTypeInfo IDType_ID_KE = {
.foreach_path = NULL,
/* A bit weird, due to shape-keys 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,
+ .owner_pointer_get = shapekey_owner_pointer_get,
.blend_write = shapekey_blend_write,
.blend_read_data = shapekey_blend_read_data,
@@ -1257,7 +1263,7 @@ static void do_key(const int start,
static float *get_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cache)
{
- MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
BMEditMesh *em = NULL;
BMIter iter;
BMVert *eve;
@@ -1271,7 +1277,7 @@ static float *get_weights_array(Object *ob, char *vgroup, WeightsArrayCache *cac
/* gather dvert and totvert */
if (ob->type == OB_MESH) {
Mesh *me = ob->data;
- dvert = me->dvert;
+ dvert = BKE_mesh_deform_verts(me);
totvert = me->totvert;
if (me->edit_mesh && me->edit_mesh->bm->totvert == totvert) {
@@ -1501,7 +1507,14 @@ static void do_latt_key(Object *ob, Key *key, char *out, const int tot)
}
}
-float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t arr_size)
+static void keyblock_data_convert_to_mesh(const float (*fp)[3], MVert *mvert, const int totvert);
+static void keyblock_data_convert_to_lattice(const float (*fp)[3],
+ BPoint *bpoint,
+ const int totpoint);
+static void keyblock_data_convert_to_curve(const float *fp, ListBase *nurb, const int totpoint);
+
+float *BKE_key_evaluate_object_ex(
+ Object *ob, int *r_totelem, float *arr, size_t arr_size, ID *obdata)
{
Key *key = BKE_key_from_object(ob);
KeyBlock *actkb = BKE_keyblock_from_object(ob);
@@ -1576,7 +1589,6 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t
}
}
else {
-
if (ob->type == OB_MESH) {
do_mesh_key(ob, key, out, tot);
}
@@ -1591,6 +1603,32 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t
}
}
+ if (obdata != NULL) {
+ switch (GS(obdata->name)) {
+ case ID_ME: {
+ Mesh *mesh = (Mesh *)obdata;
+ MVert *verts = BKE_mesh_verts_for_write(mesh);
+ const int totvert = min_ii(tot, mesh->totvert);
+ keyblock_data_convert_to_mesh((const float(*)[3])out, verts, totvert);
+ break;
+ }
+ case ID_LT: {
+ Lattice *lattice = (Lattice *)obdata;
+ const int totpoint = min_ii(tot, lattice->pntsu * lattice->pntsv * lattice->pntsw);
+ keyblock_data_convert_to_lattice((const float(*)[3])out, lattice->def, totpoint);
+ break;
+ }
+ case ID_CU_LEGACY: {
+ Curve *curve = (Curve *)obdata;
+ const int totpoint = min_ii(tot, BKE_keyblock_curve_element_count(&curve->nurb));
+ keyblock_data_convert_to_curve((const float *)out, &curve->nurb, totpoint);
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ }
+ }
+
if (r_totelem) {
*r_totelem = tot;
}
@@ -1599,7 +1637,7 @@ float *BKE_key_evaluate_object_ex(Object *ob, int *r_totelem, float *arr, size_t
float *BKE_key_evaluate_object(Object *ob, int *r_totelem)
{
- return BKE_key_evaluate_object_ex(ob, r_totelem, NULL, 0);
+ return BKE_key_evaluate_object_ex(ob, r_totelem, NULL, 0, NULL);
}
int BKE_keyblock_element_count_from_shape(const Key *key, const int shape_index)
@@ -1900,6 +1938,16 @@ KeyBlock *BKE_keyblock_find_name(Key *key, const char name[])
return BLI_findstring(&key->block, name, offsetof(KeyBlock, name));
}
+KeyBlock *BKE_keyblock_find_uid(Key *key, const int uid)
+{
+ LISTBASE_FOREACH (KeyBlock *, kb, &key->block) {
+ if (kb->uid == uid) {
+ return kb;
+ }
+ }
+ return NULL;
+}
+
void BKE_keyblock_copy_settings(KeyBlock *kb_dst, const KeyBlock *kb_src)
{
kb_dst->pos = kb_src->pos;
@@ -1971,21 +2019,22 @@ void BKE_keyblock_convert_from_lattice(const Lattice *lt, KeyBlock *kb)
BKE_keyblock_update_from_lattice(lt, kb);
}
-void BKE_keyblock_convert_to_lattice(const KeyBlock *kb, Lattice *lt)
+static void keyblock_data_convert_to_lattice(const float (*fp)[3],
+ BPoint *bpoint,
+ const int totpoint)
{
- BPoint *bp;
- const float(*fp)[3];
- int a, tot;
-
- bp = lt->def;
- fp = kb->data;
+ for (int i = 0; i < totpoint; i++, fp++, bpoint++) {
+ copy_v3_v3(bpoint->vec, *fp);
+ }
+}
- tot = lt->pntsu * lt->pntsv * lt->pntsw;
- tot = min_ii(kb->totelem, tot);
+void BKE_keyblock_convert_to_lattice(const KeyBlock *kb, Lattice *lt)
+{
+ BPoint *bp = lt->def;
+ const float(*fp)[3] = kb->data;
+ const int tot = min_ii(kb->totelem, lt->pntsu * lt->pntsv * lt->pntsw);
- for (a = 0; a < tot; a++, fp++, bp++) {
- copy_v3_v3(bp->vec, *fp);
- }
+ keyblock_data_convert_to_lattice(fp, bp, tot);
}
/************************* Curve ************************/
@@ -2097,47 +2146,44 @@ void BKE_keyblock_convert_from_curve(const Curve *cu, KeyBlock *kb, const ListBa
BKE_keyblock_update_from_curve(cu, kb, nurb);
}
-void BKE_keyblock_convert_to_curve(KeyBlock *kb, Curve *UNUSED(cu), ListBase *nurb)
+static void keyblock_data_convert_to_curve(const float *fp, ListBase *nurb, int totpoint)
{
- Nurb *nu;
- BezTriple *bezt;
- BPoint *bp;
- const float *fp;
- int a, tot;
-
- tot = BKE_keyblock_curve_element_count(nurb);
- tot = min_ii(kb->totelem, tot);
-
- fp = kb->data;
- for (nu = nurb->first; nu && tot > 0; nu = nu->next) {
- if (nu->bezt) {
- for (a = nu->pntsu, bezt = nu->bezt; a && (tot -= KEYELEM_ELEM_LEN_BEZTRIPLE) >= 0;
- a--, bezt++) {
- for (int i = 0; i < 3; i++) {
- copy_v3_v3(bezt->vec[i], &fp[i * 3]);
+ for (Nurb *nu = nurb->first; nu && totpoint > 0; nu = nu->next) {
+ if (nu->bezt != NULL) {
+ BezTriple *bezt = nu->bezt;
+ for (int i = nu->pntsu; i && (totpoint -= KEYELEM_ELEM_LEN_BEZTRIPLE) >= 0;
+ i--, bezt++, fp += KEYELEM_FLOAT_LEN_BEZTRIPLE) {
+ for (int j = 0; j < 3; j++) {
+ copy_v3_v3(bezt->vec[j], &fp[j * 3]);
}
bezt->tilt = fp[9];
bezt->radius = fp[10];
- fp += KEYELEM_FLOAT_LEN_BEZTRIPLE;
}
}
else {
- for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a && (tot -= KEYELEM_ELEM_LEN_BPOINT) >= 0;
- a--, bp++) {
+ BPoint *bp = nu->bp;
+ for (int i = nu->pntsu * nu->pntsv; i && (totpoint -= KEYELEM_ELEM_LEN_BPOINT) >= 0;
+ i--, bp++, fp += KEYELEM_FLOAT_LEN_BPOINT) {
copy_v3_v3(bp->vec, fp);
bp->tilt = fp[3];
bp->radius = fp[4];
- fp += KEYELEM_FLOAT_LEN_BPOINT;
}
}
}
}
+void BKE_keyblock_convert_to_curve(KeyBlock *kb, Curve *UNUSED(cu), ListBase *nurb)
+{
+ const float *fp = kb->data;
+ const int tot = min_ii(kb->totelem, BKE_keyblock_curve_element_count(nurb));
+
+ keyblock_data_convert_to_curve(fp, nurb, tot);
+}
+
/************************* Mesh ************************/
void BKE_keyblock_update_from_mesh(const Mesh *me, KeyBlock *kb)
{
- MVert *mvert;
float(*fp)[3];
int a, tot;
@@ -2148,7 +2194,7 @@ void BKE_keyblock_update_from_mesh(const Mesh *me, KeyBlock *kb)
return;
}
- mvert = me->mvert;
+ const MVert *mvert = BKE_mesh_verts(me);
fp = kb->data;
for (a = 0; a < tot; a++, fp++, mvert++) {
copy_v3_v3(*fp, mvert->co);
@@ -2171,20 +2217,21 @@ void BKE_keyblock_convert_from_mesh(const Mesh *me, const Key *key, KeyBlock *kb
BKE_keyblock_update_from_mesh(me, kb);
}
-void BKE_keyblock_convert_to_mesh(const KeyBlock *kb, MVert *mvert, const int totvert)
+static void keyblock_data_convert_to_mesh(const float (*fp)[3], MVert *mvert, const int totvert)
{
- const float(*fp)[3];
- int a, tot;
-
- fp = kb->data;
-
- tot = min_ii(kb->totelem, totvert);
-
- for (a = 0; a < tot; a++, fp++, mvert++) {
+ for (int i = 0; i < totvert; i++, fp++, mvert++) {
copy_v3_v3(mvert->co, *fp);
}
}
+void BKE_keyblock_convert_to_mesh(const KeyBlock *kb, MVert *mvert, const int totvert)
+{
+ const float(*fp)[3] = kb->data;
+ const int tot = min_ii(kb->totelem, totvert);
+
+ keyblock_data_convert_to_mesh(fp, mvert, tot);
+}
+
void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb,
const Mesh *mesh,
float (*r_vertnors)[3],
@@ -2195,8 +2242,11 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb,
return;
}
- MVert *mvert = MEM_dupallocN(mesh->mvert);
- BKE_keyblock_convert_to_mesh(kb, mvert, mesh->totvert);
+ MVert *verts = MEM_dupallocN(BKE_mesh_verts(mesh));
+ BKE_keyblock_convert_to_mesh(kb, verts, mesh->totvert);
+ const MEdge *edges = BKE_mesh_edges(mesh);
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
const bool loop_normals_needed = r_loopnors != NULL;
const bool vert_normals_needed = r_vertnors != NULL || loop_normals_needed;
@@ -2217,35 +2267,30 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb,
}
if (poly_normals_needed) {
- BKE_mesh_calc_normals_poly(mvert,
- mesh->totvert,
- mesh->mloop,
- mesh->totloop,
- mesh->mpoly,
- mesh->totpoly,
- poly_normals);
+ BKE_mesh_calc_normals_poly(
+ verts, mesh->totvert, loops, mesh->totloop, polys, mesh->totpoly, poly_normals);
}
if (vert_normals_needed) {
- BKE_mesh_calc_normals_poly_and_vertex(mvert,
+ BKE_mesh_calc_normals_poly_and_vertex(verts,
mesh->totvert,
- mesh->mloop,
+ loops,
mesh->totloop,
- mesh->mpoly,
+ polys,
mesh->totpoly,
poly_normals,
vert_normals);
}
if (loop_normals_needed) {
short(*clnors)[2] = CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL); /* May be NULL. */
- BKE_mesh_normals_loop_split(mvert,
+ BKE_mesh_normals_loop_split(verts,
vert_normals,
mesh->totvert,
- mesh->medge,
+ edges,
mesh->totedge,
- mesh->mloop,
+ loops,
r_loopnors,
mesh->totloop,
- mesh->mpoly,
+ polys,
poly_normals,
mesh->totpoly,
(mesh->flag & ME_AUTOSMOOTH) != 0,
@@ -2261,7 +2306,7 @@ void BKE_keyblock_mesh_calc_normals(const KeyBlock *kb,
if (free_poly_normals) {
MEM_freeN(poly_normals);
}
- MEM_freeN(mvert);
+ MEM_freeN(verts);
}
/************************* raw coords ************************/
diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c
index b5c025a40b6..e0959182fa4 100644
--- a/source/blender/blenkernel/intern/lattice.c
+++ b/source/blender/blenkernel/intern/lattice.c
@@ -190,7 +190,7 @@ IDTypeInfo IDType_ID_LT = {
.foreach_id = lattice_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = lattice_blend_write,
.blend_read_data = lattice_blend_read_data,
@@ -398,21 +398,6 @@ Lattice *BKE_lattice_add(Main *bmain, const char *name)
return lt;
}
-bool object_deform_mball(Object *ob, ListBase *dispbase)
-{
- if (ob->parent && ob->parent->type == OB_LATTICE && ob->partype == PARSKEL) {
- DispList *dl;
-
- for (dl = dispbase->first; dl; dl = dl->next) {
- BKE_lattice_deform_coords(ob->parent, ob, (float(*)[3])dl->verts, dl->nr, 0, NULL, 1.0f);
- }
-
- return true;
- }
-
- return false;
-}
-
static BPoint *latt_bp(Lattice *lt, int u, int v, int w)
{
return &lt->def[BKE_lattice_index_from_uvw(lt, u, v, w)];
diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c
index 70f8522aab4..3a1c42b9178 100644
--- a/source/blender/blenkernel/intern/lattice_deform.c
+++ b/source/blender/blenkernel/intern/lattice_deform.c
@@ -30,6 +30,7 @@
#include "BKE_editmesh.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
+#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
@@ -348,7 +349,8 @@ static void lattice_deform_coords_impl(const Object *ob_lattice,
* We want either a Mesh/Lattice with no derived data, or derived data with deformverts.
*/
if (defgrp_name && defgrp_name[0] && ob_target && ELEM(ob_target->type, OB_MESH, OB_LATTICE)) {
- defgrp_index = BKE_id_defgroup_name_index((ID *)ob_target->data, defgrp_name);
+ defgrp_index = BKE_id_defgroup_name_index(me_target ? &me_target->id : (ID *)ob_target->data,
+ defgrp_name);
if (defgrp_index != -1) {
/* if there's derived data without deformverts, don't use vgroups */
@@ -362,7 +364,7 @@ static void lattice_deform_coords_impl(const Object *ob_lattice,
dvert = ((Lattice *)ob_target->data)->dvert;
}
else {
- dvert = ((Mesh *)ob_target->data)->dvert;
+ dvert = BKE_mesh_deform_verts((Mesh *)ob_target->data);
}
}
}
diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c
index 534ff7f1fbc..d779fc4f512 100644
--- a/source/blender/blenkernel/intern/layer.c
+++ b/source/blender/blenkernel/intern/layer.c
@@ -39,6 +39,7 @@
#include "DNA_view3d_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
+#include "DNA_world_types.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_debug.h"
@@ -55,7 +56,8 @@
static CLG_LogRef LOG = {"bke.layercollection"};
/* Set of flags which are dependent on a collection settings. */
-static const short g_base_collection_flags = (BASE_VISIBLE_DEPSGRAPH | BASE_VISIBLE_VIEWLAYER |
+static const short g_base_collection_flags = (BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT |
+ BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT |
BASE_SELECTABLE | BASE_ENABLED_VIEWPORT |
BASE_ENABLED_RENDER | BASE_HOLDOUT |
BASE_INDIRECT_ONLY);
@@ -282,9 +284,10 @@ void BKE_view_layer_free_ex(ViewLayer *view_layer, const bool do_id_user)
MEM_freeN(view_layer);
}
-void BKE_view_layer_selected_objects_tag(ViewLayer *view_layer, const int tag)
+void BKE_view_layer_selected_objects_tag(const Scene *scene, ViewLayer *view_layer, const int tag)
{
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if ((base->flag & BASE_SELECTED) != 0) {
base->object->flag |= tag;
}
@@ -307,9 +310,10 @@ static bool find_scene_collection_in_scene_collections(ListBase *lb, const Layer
return false;
}
-Object *BKE_view_layer_camera_find(ViewLayer *view_layer)
+Object *BKE_view_layer_camera_find(const Scene *scene, ViewLayer *view_layer)
{
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->object->type == OB_CAMERA) {
return base->object;
}
@@ -377,6 +381,8 @@ static void view_layer_bases_hash_create(ViewLayer *view_layer, const bool do_ba
Base *BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
{
+ BLI_assert_msg((view_layer->flag & VIEW_LAYER_OUT_OF_SYNC) == 0,
+ "View layer out of sync, invoke BKE_view_layer_synced_ensure.");
if (!view_layer->object_bases_hash) {
view_layer_bases_hash_create(view_layer, false);
}
@@ -384,11 +390,10 @@ Base *BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
return BLI_ghash_lookup(view_layer->object_bases_hash, ob);
}
-void BKE_view_layer_base_deselect_all(ViewLayer *view_layer)
+void BKE_view_layer_base_deselect_all(const Scene *scene, ViewLayer *view_layer)
{
- Base *base;
-
- for (base = view_layer->object_bases.first; base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->flag &= ~BASE_SELECTED;
}
}
@@ -500,7 +505,9 @@ void BKE_view_layer_copy_data(Scene *scene_dst,
/* Copy layer collections and object bases. */
/* Inline 'BLI_duplicatelist' and update the active base. */
BLI_listbase_clear(&view_layer_dst->object_bases);
- LISTBASE_FOREACH (Base *, base_src, &view_layer_src->object_bases) {
+ BLI_assert_msg((view_layer_src->flag & VIEW_LAYER_OUT_OF_SYNC) == 0,
+ "View Layer Object Base out of sync, invoke BKE_view_layer_synced_ensure.");
+ LISTBASE_FOREACH (const Base *, base_src, &view_layer_src->object_bases) {
Base *base_dst = MEM_dupallocN(base_src);
BLI_addtail(&view_layer_dst->object_bases, base_dst);
if (view_layer_src->basact == base_src) {
@@ -571,7 +578,7 @@ void BKE_view_layer_rename(Main *bmain, Scene *scene, ViewLayer *view_layer, con
}
/* Dependency graph uses view layer name based lookups. */
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
}
/* LayerCollection */
@@ -955,6 +962,19 @@ static void layer_collection_resync_unused_layers_free(ViewLayer *view_layer,
}
}
+void BKE_view_layer_need_resync_tag(struct ViewLayer *view_layer)
+{
+ view_layer->flag |= VIEW_LAYER_OUT_OF_SYNC;
+}
+
+void BKE_view_layer_synced_ensure(const Scene *scene, struct ViewLayer *view_layer)
+{
+ if (view_layer->flag & VIEW_LAYER_OUT_OF_SYNC) {
+ BKE_layer_collection_sync(scene, view_layer);
+ view_layer->flag &= ~VIEW_LAYER_OUT_OF_SYNC;
+ }
+}
+
static void layer_collection_objects_sync(ViewLayer *view_layer,
LayerCollection *layer,
ListBase *r_lb_new_object_bases,
@@ -997,9 +1017,10 @@ static void layer_collection_objects_sync(ViewLayer *view_layer,
}
if ((collection_restrict & COLLECTION_HIDE_VIEWPORT) == 0) {
- base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | BASE_VISIBLE_DEPSGRAPH);
+ base->flag_from_collection |= (BASE_ENABLED_VIEWPORT |
+ BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT);
if ((layer_restrict & LAYER_COLLECTION_HIDE) == 0) {
- base->flag_from_collection |= BASE_VISIBLE_VIEWLAYER;
+ base->flag_from_collection |= BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT;
}
if (((collection_restrict & COLLECTION_HIDE_SELECT) == 0)) {
base->flag_from_collection |= BASE_SELECTABLE;
@@ -1047,8 +1068,14 @@ static void layer_collection_sync(ViewLayer *view_layer,
BLI_assert(layer_resync->is_used);
+ uint64_t skipped_children = 0;
LISTBASE_FOREACH (CollectionChild *, child, &layer_resync->collection->children) {
Collection *child_collection = child->collection;
+ /* Collection relations may not have rebuild yet. */
+ if (child_collection == NULL) {
+ skipped_children++;
+ continue;
+ }
LayerCollectionResync *child_layer_resync = layer_collection_resync_find(layer_resync,
child_collection);
@@ -1153,7 +1180,7 @@ static void layer_collection_sync(ViewLayer *view_layer,
/* Replace layer collection list with new one. */
layer_resync->layer->layer_collections = new_lb_layer;
- BLI_assert(BLI_listbase_count(&layer_resync->collection->children) ==
+ BLI_assert(BLI_listbase_count(&layer_resync->collection->children) - skipped_children ==
BLI_listbase_count(&new_lb_layer));
/* Update bases etc. for objects. */
@@ -1339,7 +1366,7 @@ void BKE_scene_collection_sync(const Scene *scene)
}
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
}
}
@@ -1377,12 +1404,12 @@ void BKE_main_collection_sync_remap(const Main *bmain)
if (view_layer->object_bases_hash) {
BLI_ghash_free(view_layer->object_bases_hash, NULL, NULL);
view_layer->object_bases_hash = NULL;
-
- /* Directly re-create the mapping here, so that we can also deal with duplicates in
- * `view_layer->object_bases` list of bases properly. This is the only place where such
- * duplicates should be fixed, and not considered as a critical error. */
- view_layer_bases_hash_create(view_layer, true);
}
+
+ /* Directly re-create the mapping here, so that we can also deal with duplicates in
+ * `view_layer->object_bases` list of bases properly. This is the only place where such
+ * duplicates should be fixed, and not considered as a critical error. */
+ view_layer_bases_hash_create(view_layer, true);
}
BKE_collection_object_cache_free(scene->master_collection);
@@ -1405,7 +1432,10 @@ void BKE_main_collection_sync_remap(const Main *bmain)
/** \name Object Selection
* \{ */
-bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection *lc, bool deselect)
+bool BKE_layer_collection_objects_select(const Scene *scene,
+ ViewLayer *view_layer,
+ LayerCollection *lc,
+ bool deselect)
{
if (lc->collection->flag & COLLECTION_HIDE_SELECT) {
return false;
@@ -1414,6 +1444,7 @@ bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection
bool changed = false;
if (!(lc->flag & LAYER_COLLECTION_EXCLUDE)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) {
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
@@ -1435,30 +1466,34 @@ bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection
}
LISTBASE_FOREACH (LayerCollection *, iter, &lc->layer_collections) {
- changed |= BKE_layer_collection_objects_select(view_layer, iter, deselect);
+ changed |= BKE_layer_collection_objects_select(scene, view_layer, iter, deselect);
}
return changed;
}
-bool BKE_layer_collection_has_selected_objects(ViewLayer *view_layer, LayerCollection *lc)
+bool BKE_layer_collection_has_selected_objects(const Scene *scene,
+ ViewLayer *view_layer,
+ LayerCollection *lc)
{
if (lc->collection->flag & COLLECTION_HIDE_SELECT) {
return false;
}
if (!(lc->flag & LAYER_COLLECTION_EXCLUDE)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) {
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
- if (base && (base->flag & BASE_SELECTED) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) {
+ if (base && (base->flag & BASE_SELECTED) &&
+ (base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT)) {
return true;
}
}
}
LISTBASE_FOREACH (LayerCollection *, iter, &lc->layer_collections) {
- if (BKE_layer_collection_has_selected_objects(view_layer, iter)) {
+ if (BKE_layer_collection_has_selected_objects(scene, view_layer, iter)) {
return true;
}
}
@@ -1491,7 +1526,8 @@ void BKE_base_set_visible(Scene *scene, ViewLayer *view_layer, Base *base, bool
{
if (!extend) {
/* Make only one base visible. */
- LISTBASE_FOREACH (Base *, other, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, other, BKE_view_layer_object_bases_get(view_layer)) {
other->flag |= BASE_HIDDEN;
}
@@ -1502,17 +1538,17 @@ void BKE_base_set_visible(Scene *scene, ViewLayer *view_layer, Base *base, bool
base->flag ^= BASE_HIDDEN;
}
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
}
bool BKE_base_is_visible(const View3D *v3d, const Base *base)
{
- if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) {
+ if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0) {
return false;
}
if (v3d == NULL) {
- return base->flag & BASE_VISIBLE_VIEWLAYER;
+ return base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT;
}
if ((v3d->localvd) && ((v3d->local_view_uuid & base->local_view_bits) == 0)) {
@@ -1527,7 +1563,7 @@ bool BKE_base_is_visible(const View3D *v3d, const Base *base)
return (v3d->local_collections_uuid & base->local_collections_bits) != 0;
}
- return base->flag & BASE_VISIBLE_VIEWLAYER;
+ return base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT;
}
bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *ob)
@@ -1553,7 +1589,7 @@ bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *o
/* If not using local collection the object may still be in a hidden collection. */
if ((v3d->flag & V3D_LOCAL_COLLECTIONS) == 0) {
- return (ob->base_flag & BASE_VISIBLE_VIEWLAYER) != 0;
+ return (ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) != 0;
}
return true;
@@ -1581,7 +1617,7 @@ static void layer_collection_flag_unset_recursive(LayerCollection *lc, const int
}
}
-void BKE_layer_collection_isolate_global(Scene *scene,
+void BKE_layer_collection_isolate_global(Scene *UNUSED(scene),
ViewLayer *view_layer,
LayerCollection *lc,
bool extend)
@@ -1626,7 +1662,7 @@ void BKE_layer_collection_isolate_global(Scene *scene,
BKE_layer_collection_activate(view_layer, lc);
}
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
}
static void layer_collection_local_visibility_set_recursive(LayerCollection *layer_collection,
@@ -1647,7 +1683,8 @@ static void layer_collection_local_visibility_unset_recursive(LayerCollection *l
}
}
-static void layer_collection_local_sync(ViewLayer *view_layer,
+static void layer_collection_local_sync(const Scene *scene,
+ ViewLayer *view_layer,
LayerCollection *layer_collection,
const unsigned short local_collections_uuid,
bool visible)
@@ -1662,6 +1699,7 @@ static void layer_collection_local_sync(ViewLayer *view_layer,
continue;
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
base->local_collections_bits |= local_collections_uuid;
}
@@ -1669,12 +1707,12 @@ static void layer_collection_local_sync(ViewLayer *view_layer,
LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) {
if ((child->flag & LAYER_COLLECTION_EXCLUDE) == 0) {
- layer_collection_local_sync(view_layer, child, local_collections_uuid, visible);
+ layer_collection_local_sync(scene, view_layer, child, local_collections_uuid, visible);
}
}
}
-void BKE_layer_collection_local_sync(ViewLayer *view_layer, const View3D *v3d)
+void BKE_layer_collection_local_sync(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
{
if (no_resync) {
return;
@@ -1683,12 +1721,13 @@ void BKE_layer_collection_local_sync(ViewLayer *view_layer, const View3D *v3d)
const unsigned short local_collections_uuid = v3d->local_collections_uuid;
/* Reset flags and set the bases visible by default. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->local_collections_bits &= ~local_collections_uuid;
}
LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) {
- layer_collection_local_sync(view_layer, layer_collection, local_collections_uuid, true);
+ layer_collection_local_sync(scene, view_layer, layer_collection, local_collections_uuid, true);
}
}
@@ -1707,7 +1746,7 @@ void BKE_layer_collection_local_sync_all(const Main *bmain)
}
View3D *v3d = area->spacedata.first;
if (v3d->flag & V3D_LOCAL_COLLECTIONS) {
- BKE_layer_collection_local_sync(view_layer, v3d);
+ BKE_layer_collection_local_sync(scene, view_layer, v3d);
}
}
}
@@ -1715,10 +1754,8 @@ void BKE_layer_collection_local_sync_all(const Main *bmain)
}
}
-void BKE_layer_collection_isolate_local(ViewLayer *view_layer,
- const View3D *v3d,
- LayerCollection *lc,
- bool extend)
+void BKE_layer_collection_isolate_local(
+ const Scene *scene, ViewLayer *view_layer, const View3D *v3d, LayerCollection *lc, bool extend)
{
LayerCollection *lc_master = view_layer->layer_collections.first;
bool hide_it = extend && ((v3d->local_collections_uuid & lc->local_collections_bits) != 0);
@@ -1758,36 +1795,43 @@ void BKE_layer_collection_isolate_local(ViewLayer *view_layer,
layer_collection_local_visibility_set_recursive(lc, v3d->local_collections_uuid);
}
- BKE_layer_collection_local_sync(view_layer, v3d);
+ BKE_layer_collection_local_sync(scene, view_layer, v3d);
}
-static void layer_collection_bases_show_recursive(ViewLayer *view_layer, LayerCollection *lc)
+static void layer_collection_bases_show_recursive(const Scene *scene,
+ ViewLayer *view_layer,
+ LayerCollection *lc)
{
if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) {
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
base->flag &= ~BASE_HIDDEN;
}
}
LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc->layer_collections) {
- layer_collection_bases_show_recursive(view_layer, lc_iter);
+ layer_collection_bases_show_recursive(scene, view_layer, lc_iter);
}
}
-static void layer_collection_bases_hide_recursive(ViewLayer *view_layer, LayerCollection *lc)
+static void layer_collection_bases_hide_recursive(const Scene *scene,
+ ViewLayer *view_layer,
+ LayerCollection *lc)
{
if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) {
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
base->flag |= BASE_HIDDEN;
}
}
LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc->layer_collections) {
- layer_collection_bases_hide_recursive(view_layer, lc_iter);
+ layer_collection_bases_hide_recursive(scene, view_layer, lc_iter);
}
}
-void BKE_layer_collection_set_visible(ViewLayer *view_layer,
+void BKE_layer_collection_set_visible(const Scene *scene,
+ ViewLayer *view_layer,
LayerCollection *lc,
const bool visible,
const bool hierarchy)
@@ -1795,11 +1839,11 @@ void BKE_layer_collection_set_visible(ViewLayer *view_layer,
if (hierarchy) {
if (visible) {
layer_collection_flag_unset_recursive(lc, LAYER_COLLECTION_HIDE);
- layer_collection_bases_show_recursive(view_layer, lc);
+ layer_collection_bases_show_recursive(scene, view_layer, lc);
}
else {
layer_collection_flag_set_recursive(lc, LAYER_COLLECTION_HIDE);
- layer_collection_bases_hide_recursive(view_layer, lc);
+ layer_collection_bases_hide_recursive(scene, view_layer, lc);
}
}
else {
@@ -1894,6 +1938,7 @@ bool BKE_view_layer_has_collection(const ViewLayer *view_layer, const Collection
bool BKE_scene_has_object(Scene *scene, Object *ob)
{
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base) {
return true;
@@ -1933,7 +1978,7 @@ static void object_bases_iterator_begin(BLI_Iterator *iter, void *data_in_v, con
ObjectsVisibleIteratorData *data_in = data_in_v;
ViewLayer *view_layer = data_in->view_layer;
const View3D *v3d = data_in->v3d;
- Base *base = view_layer->object_bases.first;
+ Base *base = BKE_view_layer_object_bases_get(view_layer)->first;
/* when there are no objects */
if (base == NULL) {
@@ -2010,12 +2055,13 @@ static void objects_iterator_end(BLI_Iterator *iter)
void BKE_view_layer_selected_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
{
- objects_iterator_begin(iter, data_in, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED);
+ objects_iterator_begin(
+ iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED);
}
void BKE_view_layer_selected_objects_iterator_next(BLI_Iterator *iter)
{
- objects_iterator_next(iter, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED);
+ objects_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED);
}
void BKE_view_layer_selected_objects_iterator_end(BLI_Iterator *iter)
@@ -2052,7 +2098,8 @@ void BKE_view_layer_visible_objects_iterator_end(BLI_Iterator *iter)
void BKE_view_layer_selected_editable_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
{
- objects_iterator_begin(iter, data_in, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED);
+ objects_iterator_begin(
+ iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED);
if (iter->valid) {
if (BKE_object_is_libdata((Object *)iter->current) == false) {
/* First object is valid (selectable and not libdata) -> all good. */
@@ -2069,7 +2116,7 @@ void BKE_view_layer_selected_editable_objects_iterator_next(BLI_Iterator *iter)
/* Search while there are objects and the one we have is not editable (editable = not libdata).
*/
do {
- objects_iterator_next(iter, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED);
+ objects_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED);
} while (iter->valid && BKE_object_is_libdata((Object *)iter->current) != false);
}
@@ -2086,12 +2133,13 @@ void BKE_view_layer_selected_editable_objects_iterator_end(BLI_Iterator *iter)
void BKE_view_layer_selected_bases_iterator_begin(BLI_Iterator *iter, void *data_in)
{
- objects_iterator_begin(iter, data_in, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED);
+ objects_iterator_begin(
+ iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED);
}
void BKE_view_layer_selected_bases_iterator_next(BLI_Iterator *iter)
{
- object_bases_iterator_next(iter, BASE_VISIBLE_DEPSGRAPH | BASE_SELECTED);
+ object_bases_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED);
}
void BKE_view_layer_selected_bases_iterator_end(BLI_Iterator *iter)
@@ -2218,7 +2266,8 @@ void BKE_base_eval_flags(Base *base)
* can change these again, but for tools we always want the viewport
* visibility to be in sync regardless if depsgraph was evaluated. */
if (!(base->flag & BASE_ENABLED_VIEWPORT) || (base->flag & BASE_HIDDEN)) {
- base->flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_VISIBLE_VIEWLAYER | BASE_SELECTABLE);
+ base->flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT |
+ BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT | BASE_SELECTABLE);
}
/* Deselect unselectable objects. */
@@ -2228,18 +2277,19 @@ void BKE_base_eval_flags(Base *base)
}
static void layer_eval_view_layer(struct Depsgraph *depsgraph,
- struct Scene *UNUSED(scene),
+ struct Scene *scene,
ViewLayer *view_layer)
{
DEG_debug_print_eval(depsgraph, __func__, view_layer->name, view_layer);
/* Create array of bases, for fast index-based lookup. */
- const int num_object_bases = BLI_listbase_count(&view_layer->object_bases);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const int num_object_bases = BLI_listbase_count(BKE_view_layer_object_bases_get(view_layer));
MEM_SAFE_FREE(view_layer->object_bases_array);
view_layer->object_bases_array = MEM_malloc_arrayN(
num_object_bases, sizeof(Base *), "view_layer->object_bases_array");
int base_index = 0;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
view_layer->object_bases_array[base_index++] = base;
}
}
@@ -2269,10 +2319,11 @@ static void write_layer_collections(BlendWriter *writer, ListBase *lb)
}
}
-void BKE_view_layer_blend_write(BlendWriter *writer, ViewLayer *view_layer)
+void BKE_view_layer_blend_write(BlendWriter *writer, const Scene *scene, ViewLayer *view_layer)
{
+ BKE_view_layer_synced_ensure(scene, view_layer);
BLO_write_struct(writer, ViewLayer, view_layer);
- BLO_write_struct_list(writer, Base, &view_layer->object_bases);
+ BLO_write_struct_list(writer, Base, BKE_view_layer_object_bases_get(view_layer));
if (view_layer->id_properties) {
IDP_BlendWrite(writer, view_layer->id_properties);
@@ -2302,7 +2353,7 @@ static void direct_link_layer_collections(BlendDataReader *reader, ListBase *lb,
BLO_read_data_address(reader, &lc->scene_collection);
#endif
- /* Master collection is not a real data-lock. */
+ /* Master collection is not a real data-block. */
if (master) {
BLO_read_data_address(reader, &lc->collection);
}
@@ -2342,7 +2393,7 @@ static void lib_link_layer_collection(BlendLibReader *reader,
LayerCollection *layer_collection,
bool master)
{
- /* Master collection is not a real data-lock. */
+ /* Master collection is not a real data-block. */
if (!master) {
BLO_read_id_address(reader, lib, &layer_collection->collection);
}
@@ -2383,7 +2434,7 @@ void BKE_view_layer_blend_read_lib(BlendLibReader *reader, Library *lib, ViewLay
BLO_read_id_address(reader, lib, &view_layer->mat_override);
- IDP_BlendReadLib(reader, view_layer->id_properties);
+ IDP_BlendReadLib(reader, lib, view_layer->id_properties);
}
/** \} */
@@ -2588,12 +2639,36 @@ ViewLayer *BKE_view_layer_find_with_lightgroup(struct Scene *scene,
return NULL;
}
-void BKE_view_layer_rename_lightgroup(ViewLayer *view_layer,
+void BKE_view_layer_rename_lightgroup(Scene *scene,
+ ViewLayer *view_layer,
ViewLayerLightgroup *lightgroup,
const char *name)
{
+ char old_name[64];
+ BLI_strncpy_utf8(old_name, lightgroup->name, sizeof(old_name));
BLI_strncpy_utf8(lightgroup->name, name, sizeof(lightgroup->name));
viewlayer_lightgroup_make_name_unique(view_layer, lightgroup);
+
+ if (scene != NULL) {
+ /* Update objects in the scene to refer to the new name instead. */
+ FOREACH_SCENE_OBJECT_BEGIN (scene, ob) {
+ if (!ID_IS_LINKED(ob) && ob->lightgroup != NULL) {
+ LightgroupMembership *lgm = ob->lightgroup;
+ if (STREQ(lgm->name, old_name)) {
+ BLI_strncpy_utf8(lgm->name, lightgroup->name, sizeof(lgm->name));
+ }
+ }
+ }
+ FOREACH_SCENE_OBJECT_END;
+
+ /* Update the scene's world to refer to the new name instead. */
+ if (scene->world != NULL && !ID_IS_LINKED(scene->world) && scene->world->lightgroup != NULL) {
+ LightgroupMembership *lgm = scene->world->lightgroup;
+ if (STREQ(lgm->name, old_name)) {
+ BLI_strncpy_utf8(lgm->name, lightgroup->name, sizeof(lgm->name));
+ }
+ }
+ }
}
void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *name)
diff --git a/source/blender/blenkernel/intern/layer_utils.c b/source/blender/blenkernel/intern/layer_utils.c
index 0903c2a2cac..23067d1a4e3 100644
--- a/source/blender/blenkernel/intern/layer_utils.c
+++ b/source/blender/blenkernel/intern/layer_utils.c
@@ -84,13 +84,14 @@ Object **BKE_view_layer_array_selected_objects_params(
/** \name Objects in Mode Array
* \{ */
-Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer,
+Base **BKE_view_layer_array_from_bases_in_mode_params(const Scene *scene,
+ ViewLayer *view_layer,
const View3D *v3d,
uint *r_len,
const struct ObjectsInModeParams *params)
{
if (params->no_dup_data) {
- FOREACH_BASE_IN_MODE_BEGIN (view_layer, v3d, -1, params->object_mode, base_iter) {
+ FOREACH_BASE_IN_MODE_BEGIN (scene, view_layer, v3d, -1, params->object_mode, base_iter) {
ID *id = base_iter->object->data;
if (id) {
id->tag |= LIB_TAG_DOIT;
@@ -102,7 +103,7 @@ Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer,
Base **base_array = NULL;
BLI_array_declare(base_array);
- FOREACH_BASE_IN_MODE_BEGIN (view_layer, v3d, -1, params->object_mode, base_iter) {
+ FOREACH_BASE_IN_MODE_BEGIN (scene, view_layer, v3d, -1, params->object_mode, base_iter) {
if (params->filter_fn) {
if (!params->filter_fn(base_iter->object, params->filter_userdata)) {
continue;
@@ -134,13 +135,14 @@ Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer,
return base_array;
}
-Object **BKE_view_layer_array_from_objects_in_mode_params(ViewLayer *view_layer,
+Object **BKE_view_layer_array_from_objects_in_mode_params(const Scene *scene,
+ ViewLayer *view_layer,
const View3D *v3d,
uint *r_len,
const struct ObjectsInModeParams *params)
{
Base **base_array = BKE_view_layer_array_from_bases_in_mode_params(
- view_layer, v3d, r_len, params);
+ scene, view_layer, v3d, r_len, params);
if (base_array != NULL) {
for (uint i = 0; i < *r_len; i++) {
((Object **)base_array)[i] = base_array[i]->object;
@@ -149,6 +151,91 @@ Object **BKE_view_layer_array_from_objects_in_mode_params(ViewLayer *view_layer,
return (Object **)base_array;
}
+struct Object **BKE_view_layer_array_from_objects_in_edit_mode(const Scene *scene,
+ ViewLayer *view_layer,
+ const View3D *v3d,
+ uint *r_len)
+{
+ struct ObjectsInModeParams params = {0};
+ params.object_mode = OB_MODE_EDIT;
+ return BKE_view_layer_array_from_objects_in_mode_params(scene, view_layer, v3d, r_len, &params);
+}
+
+struct Base **BKE_view_layer_array_from_bases_in_edit_mode(const Scene *scene,
+ ViewLayer *view_layer,
+ const View3D *v3d,
+ uint *r_len)
+{
+ struct ObjectsInModeParams params = {0};
+ params.object_mode = OB_MODE_EDIT;
+ return BKE_view_layer_array_from_bases_in_mode_params(scene, view_layer, v3d, r_len, &params);
+}
+
+struct Object **BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene,
+ ViewLayer *view_layer,
+ const View3D *v3d,
+ uint *r_len)
+{
+ struct ObjectsInModeParams params = {0};
+ params.object_mode = OB_MODE_EDIT;
+ params.no_dup_data = true;
+ return BKE_view_layer_array_from_objects_in_mode_params(scene, view_layer, v3d, r_len, &params);
+}
+
+struct Base **BKE_view_layer_array_from_bases_in_edit_mode_unique_data(const Scene *scene,
+ ViewLayer *view_layer,
+ const View3D *v3d,
+ uint *r_len)
+{
+ struct ObjectsInModeParams params = {0};
+ params.object_mode = OB_MODE_EDIT;
+ params.no_dup_data = true;
+ return BKE_view_layer_array_from_bases_in_mode_params(scene, view_layer, v3d, r_len, &params);
+}
+
+struct Object **BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ const Scene *scene, ViewLayer *view_layer, const View3D *v3d, uint *r_len)
+{
+ struct ObjectsInModeParams params = {0};
+ params.object_mode = OB_MODE_EDIT;
+ params.no_dup_data = true;
+ params.filter_fn = BKE_view_layer_filter_edit_mesh_has_uvs;
+ return BKE_view_layer_array_from_objects_in_mode_params(scene, view_layer, v3d, r_len, &params);
+}
+
+struct Object **BKE_view_layer_array_from_objects_in_mode_unique_data(const Scene *scene,
+ ViewLayer *view_layer,
+ const View3D *v3d,
+ uint *r_len,
+ const eObjectMode mode)
+{
+ struct ObjectsInModeParams params = {0};
+ params.object_mode = mode;
+ params.no_dup_data = true;
+ return BKE_view_layer_array_from_objects_in_mode_params(scene, view_layer, v3d, r_len, &params);
+}
+
+ListBase *BKE_view_layer_object_bases_get(ViewLayer *view_layer)
+{
+ BLI_assert_msg((view_layer->flag & VIEW_LAYER_OUT_OF_SYNC) == 0,
+ "Object Bases out of sync, invoke BKE_view_layer_synced_ensure.");
+ return &view_layer->object_bases;
+}
+
+Base *BKE_view_layer_active_base_get(ViewLayer *view_layer)
+{
+ BLI_assert_msg((view_layer->flag & VIEW_LAYER_OUT_OF_SYNC) == 0,
+ "Active Base out of sync, invoke BKE_view_layer_synced_ensure.");
+ return view_layer->basact;
+}
+
+LayerCollection *BKE_view_layer_active_collection_get(ViewLayer *view_layer)
+{
+ BLI_assert_msg((view_layer->flag & VIEW_LAYER_OUT_OF_SYNC) == 0,
+ "Active Collection out of sync, invoke BKE_view_layer_synced_ensure.");
+ return view_layer->active_collection;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -183,10 +270,12 @@ bool BKE_view_layer_filter_edit_mesh_has_edges(const Object *ob, void *UNUSED(us
return false;
}
-Object *BKE_view_layer_non_active_selected_object(struct ViewLayer *view_layer,
+Object *BKE_view_layer_non_active_selected_object(const struct Scene *scene,
+ struct ViewLayer *view_layer,
const struct View3D *v3d)
{
- Object *ob_active = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob_active = BKE_view_layer_active_object_get(view_layer);
Object *ob_result = NULL;
FOREACH_SELECTED_OBJECT_BEGIN (view_layer, v3d, ob_iter) {
if (ob_iter == ob_active) {
@@ -206,3 +295,27 @@ Object *BKE_view_layer_non_active_selected_object(struct ViewLayer *view_layer,
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Active object accessors.
+ * \{ */
+
+Object *BKE_view_layer_active_object_get(const ViewLayer *view_layer)
+{
+ Base *base = BKE_view_layer_active_base_get((ViewLayer *)view_layer);
+ return base ? base->object : NULL;
+}
+
+Object *BKE_view_layer_edit_object_get(const ViewLayer *view_layer)
+{
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
+ if (ob == NULL) {
+ return NULL;
+ }
+ if (!(ob->mode & OB_MODE_EDIT)) {
+ return NULL;
+ }
+ return ob;
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 90a4853fd3e..158aaa961ce 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -53,11 +53,13 @@
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_node.h"
#include "BKE_rigidbody.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
+#include "DEG_depsgraph_query.h"
#include "RNA_access.h"
@@ -91,7 +93,7 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = {
.foreach_id = NULL,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = NULL,
.blend_read_data = NULL,
@@ -178,6 +180,10 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id, const int flags)
const bool id_in_mainlist = (id->tag & LIB_TAG_NO_MAIN) == 0 &&
(id->flag & LIB_EMBEDDED_DATA) == 0;
+ if (id_in_mainlist) {
+ BKE_main_namemap_remove_name(bmain, id, id->name + 2);
+ }
+
lib_id_library_local_paths(bmain, id->lib, id);
id_fake_user_clear(id);
@@ -186,7 +192,7 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id, const int flags)
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, false)) {
+ if (BKE_id_new_name_validate(bmain, which_libbase(bmain, GS(id->name)), id, NULL, false)) {
bmain->is_memfile_undo_written = false;
}
}
@@ -410,7 +416,7 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data)
/* Can happen that we get un-linkable ID here, e.g. with shape-key referring to itself
* (through drivers)...
* Just skip it, shape key can only be either indirectly linked, or fully local, period.
- * And let's curse one more time that stupid useless shapekey ID type! */
+ * And let's curse one more time that stupid useless shape-key ID type! */
if (*id_pointer && *id_pointer != id_self &&
BKE_idtype_idcode_is_linkable(GS((*id_pointer)->name))) {
id_lib_extern(*id_pointer);
@@ -560,7 +566,7 @@ struct IDCopyLibManagementData {
int flag;
};
-/* Increases usercount as required, and remap self ID pointers. */
+/** Increases user-count as required, and remap self ID pointers. */
static int id_copy_libmanagement_cb(LibraryIDLinkCallbackData *cb_data)
{
ID **id_pointer = cb_data->id_pointer;
@@ -703,6 +709,58 @@ ID *BKE_id_copy_for_duplicate(Main *bmain,
return id->newid;
}
+static int foreach_assign_id_to_orig_callback(LibraryIDLinkCallbackData *cb_data)
+{
+ ID **id_p = cb_data->id_pointer;
+
+ if (*id_p) {
+ ID *id = *id_p;
+ *id_p = DEG_get_original_id(id);
+
+ /* If the ID changes increase the user count.
+ *
+ * This means that the reference to evaluated ID has been changed with a reference to the
+ * original ID which implies that the user count of the original ID is increased.
+ *
+ * The evaluated IDs do not maintain their user counter, so do not change it to avoid issues
+ * with the user counter going negative. */
+ if (*id_p != id) {
+ if ((cb_data->cb_flag & IDWALK_CB_USER) != 0) {
+ id_us_plus(*id_p);
+ }
+ }
+ }
+
+ return IDWALK_RET_NOP;
+}
+
+ID *BKE_id_copy_for_use_in_bmain(Main *bmain, const ID *id)
+{
+ ID *newid = BKE_id_copy(bmain, id);
+
+ if (newid == NULL) {
+ return newid;
+ }
+
+ /* Assign ID references directly used by the given ID to their original complementary parts.
+ *
+ * For example, when is called on an evaluated object will assign object->data to its original
+ * pointer, the evaluated object->data will be kept unchanged. */
+ BKE_library_foreach_ID_link(NULL, newid, foreach_assign_id_to_orig_callback, NULL, IDWALK_NOP);
+
+ /* Shape keys reference on evaluated ID is preserved to keep driver paths available, but the key
+ * data is likely to be invalid now due to modifiers, so clear the shape key reference avoiding
+ * any possible shape corruption. */
+ if (DEG_is_evaluated_id(id)) {
+ Key **key_p = BKE_key_from_id_p(newid);
+ if (key_p) {
+ *key_p = NULL;
+ }
+ }
+
+ return newid;
+}
+
/**
* Does a mere memory swap over the whole IDs data (including type-specific memory).
* \note Most internal ID data itself is not swapped (only IDProperties are).
@@ -842,7 +900,7 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv)
BLI_addtail(lb, id);
/* 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);
+ BKE_id_new_name_validate(bmain, 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;
@@ -865,6 +923,7 @@ void BKE_libblock_management_main_remove(Main *bmain, void *idv)
ListBase *lb = which_libbase(bmain, GS(id->name));
BKE_main_lock(bmain);
BLI_remlink(lb, id);
+ BKE_main_namemap_remove_name(bmain, id, id->name + 2);
id->tag |= LIB_TAG_NO_MAIN;
bmain->is_memfile_undo_written = false;
BKE_main_unlock(bmain);
@@ -958,7 +1017,7 @@ void BKE_main_id_flag_all(Main *bmain, const int flag, const bool value)
}
}
-void BKE_main_id_repair_duplicate_names_listbase(ListBase *lb)
+void BKE_main_id_repair_duplicate_names_listbase(Main *bmain, ListBase *lb)
{
int lb_len = 0;
LISTBASE_FOREACH (ID *, id, lb) {
@@ -982,7 +1041,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, false);
+ BKE_id_new_name_validate(bmain, lb, id_array[i], NULL, false);
}
}
BLI_gset_free(gset, NULL);
@@ -1073,11 +1132,14 @@ 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, false);
+ BKE_id_new_name_validate(bmain, lb, id, name, false);
bmain->is_memfile_undo_written = false;
/* alphabetic insertion: is in new_id */
BKE_main_unlock(bmain);
+ /* This assert avoids having to keep name_map consistency when changing the library of an ID,
+ * if this check is not true anymore it will have to be done here too. */
+ BLI_assert(bmain->curlib == NULL || bmain->curlib->runtime.name_map == NULL);
/* This is important in 'readfile doversion after liblink' context mainly, but is a good
* consistency change in general: ID created for a Main should get that main's current
* library pointer. */
@@ -1227,7 +1289,7 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
new_id->flag = (new_id->flag & ~copy_idflag_mask) | (id->flag & copy_idflag_mask);
- /* We do not want any handling of usercount in code duplicating the data here, we do that all
+ /* We do not want any handling of user-count in code duplicating the data here, we do that all
* at once in id_copy_libmanagement_cb() at the end. */
const int copy_data_flag = orig_flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
@@ -1415,255 +1477,8 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
#undef ID_SORT_STEP_SIZE
}
-/* NOTE: this code assumes and ensures that the suffix number can never go beyond 1 billion. */
-#define MAX_NUMBER 1000000000
-/* We do not want to get "name.000", so minimal number is 1. */
-#define MIN_NUMBER 1
-/* The maximum value up to which we search for the actual smallest unused number. Beyond that
- * value, we will only use the first biggest unused number, without trying to 'fill the gaps'
- * in-between already used numbers... */
-#define MAX_NUMBERS_IN_USE 1024
-
-/**
- * Helper building final ID name from given base_name and number.
- *
- * If everything goes well and we do generate a valid final ID name in given name, we return
- * true. In case the final name would overflow the allowed ID name length, or given number is
- * bigger than maximum allowed value, we truncate further the base_name (and given name, which is
- * assumed to have the same 'base_name' part), and return false.
- */
-static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number)
-{
- char number_str[11]; /* Dot + nine digits + NULL terminator. */
- size_t number_str_len = BLI_snprintf_rlen(number_str, ARRAY_SIZE(number_str), ".%.3d", number);
-
- /* If the number would lead to an overflow of the maximum ID name length, we need to truncate
- * the base name part and do all the number checks again. */
- if (base_name_len + number_str_len >= MAX_ID_NAME - 2 || number >= MAX_NUMBER) {
- if (base_name_len + number_str_len >= MAX_ID_NAME - 2) {
- base_name_len = MAX_ID_NAME - 2 - number_str_len - 1;
- }
- else {
- base_name_len--;
- }
- base_name[base_name_len] = '\0';
-
- /* Code above may have generated invalid utf-8 string, due to raw truncation.
- * Ensure we get a valid one now. */
- base_name_len -= (size_t)BLI_str_utf8_invalid_strip(base_name, base_name_len);
-
- /* Also truncate orig name, and start the whole check again. */
- name[base_name_len] = '\0';
- return false;
- }
-
- /* We have our final number, we can put it in name and exit the function. */
- BLI_strncpy(name + base_name_len, number_str, number_str_len + 1);
- return true;
-}
-
-/**
- * Check to see if an ID name is already used, and find a new one if so.
- * Return true if a new name was created (returned in name).
- *
- * Normally the ID that's being checked is already in the ListBase, so ID *id points at the new
- * entry. The Python Library module needs to know what the name of a data-block will be before it
- * is appended, in this case ID *id is NULL.
- */
-static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_hint)
-{
- BLI_assert(strlen(name) < MAX_ID_NAME - 2);
-
- *r_id_sorting_hint = NULL;
-
- ID *id_test = lb->first;
- bool is_name_changed = false;
-
- if (id_test == NULL) {
- return is_name_changed;
- }
-
- const short id_type = (short)GS(id_test->name);
-
- /* Static storage of previous handled ID/name info, used to perform a quicker test and optimize
- * creation of huge number of IDs using the same given base name. */
- static char prev_orig_base_name[MAX_ID_NAME - 2] = {0};
- static char prev_final_base_name[MAX_ID_NAME - 2] = {0};
- static short prev_id_type = ID_LINK_PLACEHOLDER; /* Should never exist in actual ID list. */
- static int prev_number = MIN_NUMBER - 1;
-
- /* Initial test to check whether we can 'shortcut' the more complex loop of the main code
- * below. Note that we do not do that for low numbers, as that would prevent using actual
- * smallest available number in some cases, and benefits of this special case handling mostly
- * show up with high numbers anyway. */
- if (id_type == prev_id_type && prev_number >= MAX_NUMBERS_IN_USE &&
- prev_number < MAX_NUMBER - 1 && name[0] == prev_final_base_name[0]) {
-
- /* Get the name and number parts ("name.number"). */
- char base_name[MAX_ID_NAME - 2];
- int number = MIN_NUMBER;
- size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.');
- size_t prev_final_base_name_len = strlen(prev_final_base_name);
- size_t prev_orig_base_name_len = strlen(prev_orig_base_name);
-
- if (base_name_len == prev_orig_base_name_len &&
- STREQLEN(base_name, prev_orig_base_name, prev_orig_base_name_len)) {
- /* Once we have ensured given base_name and original previous one are the same, we can
- * check that previously used number is actually used, and that next one is free. */
- /* Note that from now on, we only used previous final base name, as it might have been
- * truncated from original one due to number suffix length. */
- char final_name[MAX_ID_NAME - 2];
- char prev_final_name[MAX_ID_NAME - 2];
- BLI_strncpy(final_name, prev_final_base_name, prev_final_base_name_len + 1);
- BLI_strncpy(prev_final_name, prev_final_base_name, prev_final_base_name_len + 1);
-
- if (id_name_final_build(final_name, base_name, prev_final_base_name_len, prev_number + 1) &&
- id_name_final_build(prev_final_name, base_name, prev_final_base_name_len, prev_number)) {
- /* We successfully built valid final names of previous and current iterations,
- * now we have to ensure that previous final name is indeed used in current ID list,
- * 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_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;
- break;
- }
- /* Previous final name should only be found once in the list, so if it was found
- * already, no need to do a string comparison again. */
- if (!is_valid && id_test->name[2] == prev_final_name[0] &&
- STREQ(prev_final_name, id_test->name + 2)) {
- is_valid = true;
- *r_id_sorting_hint = id_test;
- }
- }
- }
-
- if (is_valid) {
- /* Only the number changed, prev_orig_base_name, prev_final_base_name and prev_id_type
- * remain the same. */
- prev_number++;
-
- strcpy(name, final_name);
- return true;
- }
- }
- }
- }
-
- /* To speed up finding smallest unused number within [0 .. MAX_NUMBERS_IN_USE - 1].
- * We do not bother beyond that point. */
- ID *ids_in_use[MAX_NUMBERS_IN_USE] = {NULL};
-
- bool is_first_run = true;
- while (true) {
- /* Get the name and number parts ("name.number"). */
- char base_name[MAX_ID_NAME - 2];
- int number = MIN_NUMBER;
- size_t base_name_len = BLI_split_name_num(base_name, &number, name, '.');
-
- /* Store previous original given base name now, as we might alter it later in code below. */
- if (is_first_run) {
- strcpy(prev_orig_base_name, base_name);
- is_first_run = false;
- }
-
- /* In case we get an insane initial number suffix in given name. */
- /* NOTE: BLI_split_name_num() cannot return negative numbers, so we do not have to check for
- * that here. */
- if (number >= MAX_NUMBER || number < MIN_NUMBER) {
- number = MIN_NUMBER;
- }
-
- bool is_orig_name_used = false;
- 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_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, '.') ==
- base_name_len)) {
- /* If we did not yet encounter exact same name as the given one, check the remaining
- * parts of the strings. */
- if (!is_orig_name_used) {
- is_orig_name_used = STREQ(name + base_name_len, id_test->name + 2 + base_name_len);
- }
- /* Mark number of current id_test name as used, if possible. */
- if (number_test < MAX_NUMBERS_IN_USE) {
- ids_in_use[number_test] = id_test;
- }
- /* Keep track of first largest unused number. */
- if (number <= number_test) {
- *r_id_sorting_hint = id_test;
- number = number_test + 1;
- }
- }
- }
-
- /* If there is no double, we are done.
- * Note however that name might have been changed (truncated) in a previous iteration
- * already.
- */
- if (!is_orig_name_used) {
- /* Don't bother updating `prev_*` static variables here, this case is not supposed to happen
- * that often, and is not straight-forward here, so just ignore and reset them to default. */
- prev_id_type = ID_LINK_PLACEHOLDER;
- prev_final_base_name[0] = '\0';
- prev_number = MIN_NUMBER - 1;
-
- /* Value set previously is meaningless in that case. */
- *r_id_sorting_hint = NULL;
-
- return is_name_changed;
- }
-
- /* Decide which value of number to use, either the smallest unused one if possible, or
- * default to the first largest unused one we got from previous loop. */
- for (int i = MIN_NUMBER; i < MAX_NUMBERS_IN_USE; i++) {
- if (ids_in_use[i] == NULL) {
- number = i;
- if (i > 0) {
- *r_id_sorting_hint = ids_in_use[i - 1];
- }
- break;
- }
- }
- /* At this point, number is either the lowest unused number within
- * [MIN_NUMBER .. MAX_NUMBERS_IN_USE - 1], or 1 greater than the largest used number if all
- * those low ones are taken.
- * We can't be bothered to look for the lowest unused number beyond
- * (MAX_NUMBERS_IN_USE - 1).
- */
- /* We know for wure that name will be changed. */
- is_name_changed = true;
-
- /* If id_name_final_build helper returns false, it had to truncate further given name, hence
- * we have to go over the whole check again. */
- if (!id_name_final_build(name, base_name, base_name_len, number)) {
- /* We have to clear our list of small used numbers before we do the whole check again. */
- memset(ids_in_use, 0, sizeof(ids_in_use));
-
- continue;
- }
-
- /* Update `prev_*` static variables, in case next call is for the same type of IDs and with the
- * same initial base name, we can skip a lot of above process. */
- prev_id_type = id_type;
- strcpy(prev_final_base_name, base_name);
- prev_number = number;
-
- return is_name_changed;
- }
-
-#undef MAX_NUMBERS_IN_USE
-}
-
-#undef MIN_NUMBER
-#undef MAX_NUMBER
-
-bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const bool do_linked_data)
+bool BKE_id_new_name_validate(
+ struct Main *bmain, ListBase *lb, ID *id, const char *tname, const bool do_linked_data)
{
bool result = false;
char name[MAX_ID_NAME - 2];
@@ -1693,22 +1508,10 @@ bool BKE_id_new_name_validate(ListBase *lb, ID *id, const char *tname, const boo
BLI_str_utf8_invalid_strip(name, strlen(name));
}
- ID *id_sorting_hint = NULL;
- result = check_for_dupid(lb, id, name, &id_sorting_hint);
- strcpy(id->name + 2, name);
-
- /* This was in 2.43 and previous releases
- * however all data in blender should be sorted, not just duplicate names
- * sorting should not hurt, but noting just in case it alters the way other
- * functions work, so sort every time. */
-#if 0
- if (result) {
- id_sort_by_name(lb, id, id_sorting_hint);
- }
-#endif
-
- id_sort_by_name(lb, id, id_sorting_hint);
+ result = BKE_main_namemap_get_name(bmain, id, name);
+ strcpy(id->name + 2, name);
+ id_sort_by_name(lb, id, NULL);
return result;
}
@@ -1763,7 +1566,7 @@ void BKE_main_id_refcount_recompute(struct Main *bmain, const bool do_linked_onl
}
FOREACH_MAIN_ID_END;
- /* Go over whole Main database to re-generate proper usercounts... */
+ /* Go over whole Main database to re-generate proper user-counts. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
BKE_library_foreach_ID_link(bmain,
id,
@@ -2074,7 +1877,7 @@ void BLI_libblock_ensure_unique_name(Main *bmain, const char *name)
idtest = BLI_findstring(lb, name + 2, offsetof(ID, name) + 2);
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, false);
+ BKE_id_new_name_validate(bmain, lb, idtest, NULL, false);
bmain->is_memfile_undo_written = false;
}
}
@@ -2082,8 +1885,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));
+ BKE_main_namemap_remove_name(bmain, id, id->name + 2);
ListBase *lb = which_libbase(bmain, GS(id->name));
- if (BKE_id_new_name_validate(lb, id, name, false)) {
+ if (BKE_id_new_name_validate(bmain, lb, id, name, false)) {
bmain->is_memfile_undo_written = false;
}
}
@@ -2161,6 +1965,18 @@ bool BKE_id_can_be_asset(const ID *id)
BKE_idtype_idcode_is_linkable(GS(id->name));
}
+ID *BKE_id_owner_get(ID *id)
+{
+ const IDTypeInfo *idtype = BKE_idtype_get_info_from_id(id);
+ if (idtype->owner_pointer_get != NULL) {
+ ID **owner_id_pointer = idtype->owner_pointer_get(id);
+ if (owner_id_pointer != NULL) {
+ return *owner_id_pointer;
+ }
+ }
+ return NULL;
+}
+
bool BKE_id_is_editable(const Main *bmain, const ID *id)
{
return !(ID_IS_LINKED(id) || BKE_lib_override_library_is_system_defined(bmain, id));
diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c
index f14c11a949e..f4f5ca7a1d7 100644
--- a/source/blender/blenkernel/intern/lib_id_delete.c
+++ b/source/blender/blenkernel/intern/lib_id_delete.c
@@ -28,6 +28,7 @@
#include "BKE_lib_remap.h"
#include "BKE_library.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "lib_intern.h"
@@ -151,6 +152,9 @@ void BKE_id_free_ex(Main *bmain, void *idv, int flag, const bool use_flag_from_i
if ((flag & LIB_ID_FREE_NO_MAIN) == 0) {
ListBase *lb = which_libbase(bmain, type);
BLI_remlink(lb, id);
+ if ((flag & LIB_ID_FREE_NO_NAMEMAP_REMOVE) == 0) {
+ BKE_main_namemap_remove_name(bmain, id, id->name + 2);
+ }
}
BKE_libblock_free_data(id, (flag & LIB_ID_FREE_NO_USER_REFCOUNT) == 0);
@@ -237,6 +241,7 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
/* NOTE: in case we delete a library, we also delete all its datablocks! */
if ((id->tag & tag) || (ID_IS_LINKED(id) && (id->lib->id.tag & tag))) {
BLI_remlink(lb, id);
+ BKE_main_namemap_remove_name(bmain, id, id->name + 2);
BLI_addtail(&tagged_deleted_ids, id);
/* Do not tag as no_main now, we want to unlink it first (lower-level ID management
* code has some specific handling of 'no main' IDs that would be a problem in that
@@ -275,8 +280,8 @@ static size_t id_delete(Main *bmain, const bool do_tagged_deletion)
}
/* Now we can safely mark that ID as not being in Main database anymore. */
- /* NOTE: This needs to be done in a separate loop than above, otherwise some usercounts of
- * deleted IDs may not be properly decreased by the remappings (since `NO_MAIN` ID usercounts
+ /* NOTE: This needs to be done in a separate loop than above, otherwise some user-counts of
+ * deleted IDs may not be properly decreased by the remappings (since `NO_MAIN` ID user-counts
* is never affected). */
for (ID *id = tagged_deleted_ids.first; id; id = id->next) {
id->tag |= LIB_TAG_NO_MAIN;
diff --git a/source/blender/blenkernel/intern/lib_id_remapper.cc b/source/blender/blenkernel/intern/lib_id_remapper.cc
index 7e75e0f5d93..98ac9110a48 100644
--- a/source/blender/blenkernel/intern/lib_id_remapper.cc
+++ b/source/blender/blenkernel/intern/lib_id_remapper.cc
@@ -36,6 +36,7 @@ struct IDRemapper {
BLI_assert(old_id != nullptr);
BLI_assert(new_id == nullptr || (GS(old_id->name) == GS(new_id->name)));
mappings.add(old_id, new_id);
+ BLI_assert(BKE_idtype_idcode_to_idfilter(GS(old_id->name)) != 0);
source_types |= BKE_idtype_idcode_to_idfilter(GS(old_id->name));
}
diff --git a/source/blender/blenkernel/intern/lib_id_remapper_test.cc b/source/blender/blenkernel/intern/lib_id_remapper_test.cc
index 73edc30d077..03f456d2d1e 100644
--- a/source/blender/blenkernel/intern/lib_id_remapper_test.cc
+++ b/source/blender/blenkernel/intern/lib_id_remapper_test.cc
@@ -55,6 +55,7 @@ TEST(lib_id_remapper, unassigned)
{
ID id1;
ID *idp = &id1;
+ BLI_strncpy(id1.name, "OB2", sizeof(id1.name));
IDRemapper *remapper = BKE_id_remapper_create();
BKE_id_remapper_add(remapper, &id1, nullptr);
diff --git a/source/blender/blenkernel/intern/lib_id_test.cc b/source/blender/blenkernel/intern/lib_id_test.cc
index d6101d71be5..d12e0c52870 100644
--- a/source/blender/blenkernel/intern/lib_id_test.cc
+++ b/source/blender/blenkernel/intern/lib_id_test.cc
@@ -10,6 +10,7 @@
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "DNA_ID.h"
#include "DNA_mesh_types.h"
@@ -18,19 +19,18 @@
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();
-}
+ Main *bmain = nullptr;
-static void test_lib_id_main_sort_free(LibIDMainSortTestContext *ctx)
-{
- BKE_main_free(ctx->bmain);
-}
+ LibIDMainSortTestContext()
+ {
+ BKE_idtype_init();
+ bmain = BKE_main_new();
+ }
+ ~LibIDMainSortTestContext()
+ {
+ BKE_main_free(bmain);
+ }
+};
static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list)
{
@@ -47,8 +47,7 @@ static void test_lib_id_main_sort_check_order(std::initializer_list<ID *> list)
TEST(lib_id_main_sort, local_ids_1)
{
- LibIDMainSortTestContext ctx = {nullptr};
- test_lib_id_main_sort_init(&ctx);
+ LibIDMainSortTestContext 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"));
@@ -57,14 +56,28 @@ TEST(lib_id_main_sort, local_ids_1)
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});
+}
+
+static void change_lib(Main *bmain, ID *id, Library *lib)
+{
+ if (id->lib == lib) {
+ return;
+ }
+ BKE_main_namemap_remove_name(bmain, id, id->name + 2);
+ id->lib = lib;
+ BKE_main_namemap_get_name(bmain, id, id->name + 2);
+}
- test_lib_id_main_sort_free(&ctx);
+static void change_name(Main *bmain, ID *id, const char *name)
+{
+ BKE_main_namemap_remove_name(bmain, id, id->name + 2);
+ BLI_strncpy(id->name + 2, name, MAX_NAME);
+ BKE_id_new_name_validate(bmain, &bmain->objects, id, nullptr, true);
}
TEST(lib_id_main_sort, linked_ids_1)
{
- LibIDMainSortTestContext ctx = {nullptr};
- test_lib_id_main_sort_init(&ctx);
+ LibIDMainSortTestContext 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"));
@@ -73,33 +86,32 @@ TEST(lib_id_main_sort, linked_ids_1)
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;
+ change_lib(ctx.bmain, id_a, lib_a);
id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
- id_b->lib = lib_a;
+ change_lib(ctx.bmain, id_b, 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;
+ change_lib(ctx.bmain, id_a, 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;
+ change_lib(ctx.bmain, id_b, 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);
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
}
TEST(lib_id_main_unique_name, local_ids_1)
{
- LibIDMainSortTestContext ctx = {nullptr};
- test_lib_id_main_sort_init(&ctx);
+ LibIDMainSortTestContext 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"));
@@ -107,21 +119,22 @@ TEST(lib_id_main_unique_name, local_ids_1)
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, nullptr, false);
- EXPECT_TRUE(strcmp(id_c->name + 2, "OB_A.001") == 0);
- EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ change_name(ctx.bmain, id_c, "OB_A");
+
+ EXPECT_STREQ(id_c->name + 2, "OB_A.001");
+ EXPECT_STREQ(id_a->name + 2, "OB_A");
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);
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
}
TEST(lib_id_main_unique_name, linked_ids_1)
{
- LibIDMainSortTestContext ctx = {nullptr};
- test_lib_id_main_sort_init(&ctx);
+ LibIDMainSortTestContext 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"));
@@ -130,29 +143,316 @@ TEST(lib_id_main_unique_name, linked_ids_1)
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;
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ change_lib(ctx.bmain, id_a, lib_a);
id_sort_by_name(&ctx.bmain->objects, id_a, nullptr);
- id_b->lib = lib_a;
+ change_lib(ctx.bmain, id_b, 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, nullptr, true);
- EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A.001") == 0);
- EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+
+ change_name(ctx.bmain, id_b, "OB_A");
+ EXPECT_STREQ(id_b->name + 2, "OB_A.001");
+ EXPECT_STREQ(id_a->name + 2, "OB_A");
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;
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ change_lib(ctx.bmain, id_b, 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, nullptr, true);
- EXPECT_TRUE(strcmp(id_b->name + 2, "OB_A") == 0);
- EXPECT_TRUE(strcmp(id_a->name + 2, "OB_A") == 0);
+ change_name(ctx.bmain, id_b, "OB_A");
+ EXPECT_STREQ(id_b->name + 2, "OB_A");
+ EXPECT_STREQ(id_a->name + 2, "OB_A");
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);
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, ids_sorted_by_default)
+{
+ LibIDMainSortTestContext ctx;
+
+ ID *id_foo = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ ID *id_bar = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar"));
+ ID *id_baz = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Baz"));
+ ID *id_yes = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Yes"));
+ test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes});
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+static ID *add_id_in_library(Main *bmain, const char *name, Library *lib)
+{
+ ID *id = static_cast<ID *>(BKE_id_new(bmain, ID_OB, name));
+ change_lib(bmain, id, lib);
+ id_sort_by_name(&bmain->objects, id, nullptr);
+ return id;
+}
+
+TEST(lib_id_main_unique_name, ids_sorted_by_default_with_libraries)
+{
+ LibIDMainSortTestContext ctx;
+
+ Library *lib_one = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LibOne"));
+ Library *lib_two = static_cast<Library *>(BKE_id_new(ctx.bmain, ID_LI, "LibTwo"));
+
+ ID *id_foo = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ ID *id_bar = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar"));
+
+ ID *id_l1c = add_id_in_library(ctx.bmain, "C", lib_one);
+ ID *id_l2b = add_id_in_library(ctx.bmain, "B", lib_two);
+ ID *id_l1a = add_id_in_library(ctx.bmain, "A", lib_one);
+
+ ID *id_baz = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Baz"));
+ ID *id_yes = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Yes"));
+
+ test_lib_id_main_sort_check_order({id_bar, id_baz, id_foo, id_yes, id_l1a, id_l1c, id_l2b});
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, name_too_long_handling)
+{
+ LibIDMainSortTestContext ctx;
+ const char *name_a = "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_Truncated";
+ const char *name_b = "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123456";
+ const char *name_c = "Name_That_Has_Too_Long_Number_Suffix.1234567890";
+
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_a));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_b));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, name_c));
+
+ EXPECT_STREQ(id_a->name + 2, "Long_Name_That_Does_Not_Fit_Into_Max_Name_Limit_And_Should_Get_");
+ EXPECT_STREQ(id_b->name + 2, "Another_Long_Name_That_Does_Not_Fit_And_Has_A_Number_Suffix.123");
+ EXPECT_STREQ(id_c->name + 2, "Name_That_Has_Too_Long_Number_Suffix.1234567890"); /* Unchanged */
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, create_equivalent_numeric_suffixes)
+{
+ LibIDMainSortTestContext ctx;
+
+ /* Create names where many of their numeric suffixes are
+ * the same number, yet the names are different and thus
+ * should be allowed as-is. */
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.000"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.003"));
+ ID *id_d = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.3"));
+ ID *id_e = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0"));
+ ID *id_f = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo."));
+ ID *id_g = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123"));
+ ID *id_h = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ ID *id_i = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.."));
+ ID *id_j = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..001"));
+ ID *id_k = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..000"));
+
+ EXPECT_STREQ(id_a->name + 2, "Foo.123");
+ EXPECT_STREQ(id_b->name + 2, "Foo.000");
+ EXPECT_STREQ(id_c->name + 2, "Foo.003");
+ EXPECT_STREQ(id_d->name + 2, "Foo.3");
+ EXPECT_STREQ(id_e->name + 2, "Foo.0");
+ EXPECT_STREQ(id_f->name + 2, "Foo.");
+ EXPECT_STREQ(id_g->name + 2, "Foo.0123");
+ EXPECT_STREQ(id_h->name + 2, "Foo");
+ EXPECT_STREQ(id_i->name + 2, "Foo..");
+ EXPECT_STREQ(id_j->name + 2, "Foo..001");
+ EXPECT_STREQ(id_k->name + 2, "Foo..000");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ /* Now create their exact duplicates again, and check what happens. */
+ id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123"));
+ id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.000"));
+ id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.003"));
+ id_d = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.3"));
+ id_e = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0"));
+ id_f = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo."));
+ id_g = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.0123"));
+ id_h = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ id_i = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.."));
+ id_j = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..001"));
+ id_k = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo..000"));
+
+ EXPECT_STREQ(id_a->name + 2, "Foo.001");
+ EXPECT_STREQ(id_b->name + 2, "Foo.002");
+ EXPECT_STREQ(id_c->name + 2, "Foo.004");
+ EXPECT_STREQ(id_d->name + 2, "Foo.005");
+ EXPECT_STREQ(id_e->name + 2, "Foo.006");
+ EXPECT_STREQ(id_f->name + 2, "Foo..002");
+ EXPECT_STREQ(id_g->name + 2, "Foo.007");
+ EXPECT_STREQ(id_h->name + 2, "Foo.008");
+ EXPECT_STREQ(id_i->name + 2, "Foo...001");
+ EXPECT_STREQ(id_j->name + 2, "Foo..003");
+ EXPECT_STREQ(id_k->name + 2, "Foo..004");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, zero_suffix_is_never_assigned)
+{
+ LibIDMainSortTestContext ctx;
+
+ /* Creating these should assign 002 to the first one, but the next
+ * ones should start numbers starting from 1: 001 and 003. */
+ ID *id_002 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002"));
+ ID *id_001 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002"));
+ ID *id_003 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.002"));
+
+ EXPECT_STREQ(id_002->name + 2, "Foo.002");
+ EXPECT_STREQ(id_001->name + 2, "Foo.001");
+ EXPECT_STREQ(id_003->name + 2, "Foo.003");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, remove_after_dup_get_original_name)
+{
+ LibIDMainSortTestContext ctx;
+
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+
+ EXPECT_STREQ(id_a->name + 2, "Foo");
+ EXPECT_STREQ(id_b->name + 2, "Foo.001");
+ BKE_id_free(ctx.bmain, id_a);
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ EXPECT_STREQ(id_a->name + 2, "Foo");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, name_number_suffix_assignment)
+{
+ LibIDMainSortTestContext ctx;
+
+ /* Create <1k objects first. */
+ const int total_object_count = 1200;
+ ID *ids[total_object_count] = {};
+ for (int i = 0; i < total_object_count / 2; ++i) {
+ ids[i] = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ }
+
+ /* They should get assigned sequential numeric suffixes. */
+ EXPECT_STREQ(ids[0]->name + 2, "Foo");
+ EXPECT_STREQ(ids[1]->name + 2, "Foo.001");
+ EXPECT_STREQ(ids[total_object_count / 2 - 1]->name + 2, "Foo.599");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ /* Free some of the objects. */
+ BKE_id_free(ctx.bmain, ids[10]);
+ BKE_id_free(ctx.bmain, ids[20]);
+ BKE_id_free(ctx.bmain, ids[30]);
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ /* Create objects again; they should get suffixes that were just free'd up. */
+ ID *id_010 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ EXPECT_STREQ(id_010->name + 2, "Foo.010");
+ ID *id_020 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.123"));
+ EXPECT_STREQ(id_020->name + 2, "Foo.020");
+ /* Suffixes >1k do not get the "use the most proper free one" treatment. */
+ ID *id_2000 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000"));
+ EXPECT_STREQ(id_2000->name + 2, "Foo.2000");
+ /* But smaller than 1k suffixes do get proper empty spots. */
+ ID *id_030 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ EXPECT_STREQ(id_030->name + 2, "Foo.030");
+ ID *id_600 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ EXPECT_STREQ(id_600->name + 2, "Foo.600");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ /* Max possible numeric suffix. */
+ ID *id_max = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999"));
+ EXPECT_STREQ(id_max->name + 2, "Foo.999999999");
+ /* Try with max. possible suffix again: will assign free suffix under 1k. */
+ ID *id_max1 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999"));
+ EXPECT_STREQ(id_max1->name + 2, "Foo.601");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ /* Now create the rest of objects, to use all the suffixes up to 1k.
+ * Once all the ones up to 1k are used, the logic will fall back to
+ * "use largest number seen + 1", but the largest one is already the max
+ * possible. So it will shorten the name part and restart the counter,
+ * i.e. "Fo.001". */
+ for (int i = total_object_count / 2; i < total_object_count; ++i) {
+ ids[i] = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ }
+ /* At this point creating "Foo" based objects will fall always
+ * result in shortened name to "Fo". */
+ ID *id_fo178 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ EXPECT_STREQ(id_fo178->name + 2, "Fo.178");
+ ID *id_fo179 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.2000"));
+ EXPECT_STREQ(id_fo179->name + 2, "Fo.179");
+ ID *id_fo180 = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo.999999999"));
+ EXPECT_STREQ(id_fo180->name + 2, "Fo.180");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, renames_with_duplicates)
+{
+ LibIDMainSortTestContext ctx;
+
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Bar"));
+
+ EXPECT_STREQ(id_a->name + 2, "Foo");
+ EXPECT_STREQ(id_b->name + 2, "Foo.001");
+ EXPECT_STREQ(id_c->name + 2, "Bar");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+
+ BKE_libblock_rename(ctx.bmain, id_a, "Foo.002");
+ EXPECT_STREQ(id_a->name + 2, "Foo.002");
+ BKE_libblock_rename(ctx.bmain, id_b, "Bar");
+ EXPECT_STREQ(id_b->name + 2, "Bar.001");
+ BKE_libblock_rename(ctx.bmain, id_c, "Foo");
+ EXPECT_STREQ(id_c->name + 2, "Foo");
+ BKE_libblock_rename(ctx.bmain, id_b, "Bar");
+ EXPECT_STREQ(id_b->name + 2, "Bar");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, names_are_unique_per_id_type)
+{
+ LibIDMainSortTestContext ctx;
+
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_CA, "Foo"));
+ ID *id_c = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "Foo"));
+
+ EXPECT_STREQ(id_a->name + 2, "Foo");
+ EXPECT_STREQ(id_b->name + 2, "Foo"); /* Different types (OB & CA) can have the same name. */
+ EXPECT_STREQ(id_c->name + 2, "Foo.001");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
+}
+
+TEST(lib_id_main_unique_name, name_huge_number_suffix)
+{
+ LibIDMainSortTestContext ctx;
+
+ /* Use numeric suffix that is really large: should come through
+ * fine, since no duplicates with other names. */
+ ID *id_a = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "SuperLong.1234567890"));
+ EXPECT_STREQ(id_a->name + 2, "SuperLong.1234567890");
+ /* Now create with the same name again: should get 001 suffix. */
+ ID *id_b = static_cast<ID *>(BKE_id_new(ctx.bmain, ID_OB, "SuperLong.1234567890"));
+ EXPECT_STREQ(id_b->name + 2, "SuperLong.001");
+
+ EXPECT_TRUE(BKE_main_namemap_validate(ctx.bmain));
}
} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/lib_override.cc b/source/blender/blenkernel/intern/lib_override.cc
index d816b5ede5f..3f1b80014ac 100644
--- a/source/blender/blenkernel/intern/lib_override.cc
+++ b/source/blender/blenkernel/intern/lib_override.cc
@@ -34,6 +34,7 @@
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_node.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -51,6 +52,7 @@
#include "PIL_time.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "RNA_types.h"
@@ -80,8 +82,8 @@ static void lib_override_library_property_operation_clear(
BLI_INLINE void lib_override_object_posemode_transfer(ID *id_dst, ID *id_src)
{
if (GS(id_src->name) == ID_OB && GS(id_dst->name) == ID_OB) {
- Object *ob_src = (Object *)id_src;
- Object *ob_dst = (Object *)id_dst;
+ Object *ob_src = reinterpret_cast<Object *>(id_src);
+ Object *ob_dst = reinterpret_cast<Object *>(id_dst);
if (ob_src->type == OB_ARMATURE && (ob_src->mode & OB_MODE_POSE) != 0) {
ob_dst->restore_mode = ob_dst->mode;
ob_dst->mode |= OB_MODE_POSE;
@@ -90,36 +92,38 @@ BLI_INLINE void lib_override_object_posemode_transfer(ID *id_dst, ID *id_src)
}
/** Get override data for a given ID. Needed because of our beloved shape keys snowflake. */
-BLI_INLINE const IDOverrideLibrary *lib_override_get(const Main *bmain,
- const ID *id,
- const ID **r_owner_id)
+BLI_INLINE const IDOverrideLibrary *BKE_lib_override_library_get(const Main * /*bmain*/,
+ const ID *id,
+ const ID * /*owner_id_hint*/,
+ const ID **r_owner_id)
{
- if (r_owner_id != nullptr) {
- *r_owner_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 != nullptr) {
- /* The #IDTypeInfo::owner_get callback should not modify the arguments, so casting away const
- * is okay. */
- const ID *owner_id = id_type->owner_get(const_cast<Main *>(bmain), const_cast<ID *>(id));
- if (r_owner_id != nullptr) {
- *r_owner_id = owner_id;
- }
- return owner_id->override_library;
+ const ID *owner_id = BKE_id_owner_get(const_cast<ID *>(id));
+ BLI_assert_msg(owner_id != nullptr, "Liboverride-embedded ID with no owner");
+ if (r_owner_id != nullptr) {
+ *r_owner_id = owner_id;
}
- BLI_assert_msg(0, "IDTypeInfo of liboverride-embedded ID with no owner getter");
+ return owner_id->override_library;
+ }
+
+ if (r_owner_id != nullptr) {
+ *r_owner_id = id;
}
return id->override_library;
}
-BLI_INLINE IDOverrideLibrary *lib_override_get(Main *bmain, ID *id, ID **r_owner_id)
+IDOverrideLibrary *BKE_lib_override_library_get(Main *bmain,
+ ID *id,
+ ID *owner_id_hint,
+ ID **r_owner_id)
{
/* Reuse the implementation of the const access function, which does not change the arguments.
* Add const explicitly to make it clear to the compiler to avoid just calling this function. */
- return const_cast<IDOverrideLibrary *>(lib_override_get(const_cast<const Main *>(bmain),
- const_cast<const ID *>(id),
- const_cast<const ID **>(r_owner_id)));
+ return const_cast<IDOverrideLibrary *>(
+ BKE_lib_override_library_get(const_cast<const Main *>(bmain),
+ const_cast<const ID *>(id),
+ const_cast<const ID *>(owner_id_hint),
+ const_cast<const ID **>(r_owner_id)));
}
IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
@@ -184,7 +188,7 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_f
* Otherwise, source is only an override template, it then becomes reference of dest ID. */
dst_id->override_library->reference = src_id->override_library->reference ?
src_id->override_library->reference :
- (ID *)src_id;
+ const_cast<ID *>(src_id);
id_us_plus(dst_id->override_library->reference);
dst_id->override_library->hierarchy_root = src_id->override_library->hierarchy_root;
@@ -316,7 +320,7 @@ bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id)
{
if (ID_IS_OVERRIDE_LIBRARY(id)) {
const ID *override_owner_id;
- lib_override_get(bmain, id, &override_owner_id);
+ BKE_lib_override_library_get(bmain, id, nullptr, &override_owner_id);
return (override_owner_id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) !=
0;
}
@@ -357,6 +361,10 @@ static int foreachid_is_hierarchy_leaf_fn(LibraryIDLinkCallbackData *cb_data)
ID *id = *cb_data->id_pointer;
bool *is_leaf = static_cast<bool *>(cb_data->user_data);
+ if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) {
+ return IDWALK_RET_NOP;
+ }
+
if (id != nullptr && ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
id->override_library->hierarchy_root == id_owner->override_library->hierarchy_root) {
*is_leaf = false;
@@ -428,14 +436,42 @@ static void lib_override_prefill_newid_from_existing_overrides(Main *bmain, ID *
{
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) &&
- id_iter->override_library->hierarchy_root == id_hierarchy_root) {
- id_iter->override_library->reference->newid = id_iter;
+ ID *id = id_iter;
+ if (GS(id_iter->name) == ID_KE) {
+ id = reinterpret_cast<Key *>(id_iter)->from;
+ BLI_assert(id != nullptr);
+ }
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ id->override_library->hierarchy_root == id_hierarchy_root) {
+ id->override_library->reference->newid = id;
+ if (GS(id_iter->name) == ID_KE) {
+ Key *reference_key = BKE_key_from_id(id->override_library->reference);
+ if (reference_key != nullptr) {
+ reference_key->id.newid = id_iter;
+ }
+ }
}
}
FOREACH_MAIN_ID_END;
}
+static void lib_override_remapper_overrides_add(IDRemapper *id_remapper,
+ ID *reference_id,
+ ID *local_id)
+{
+ BKE_id_remapper_add(id_remapper, reference_id, local_id);
+
+ Key *reference_key, *local_key = nullptr;
+ if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) {
+ if (reference_id->newid != nullptr) {
+ local_key = BKE_key_from_id(reference_id->newid);
+ BLI_assert(local_key != nullptr);
+ }
+
+ BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id);
+ }
+}
+
/* TODO: Make this static local function instead? API is becoming complex, and it's not used
* outside of this file anyway. */
bool BKE_lib_override_library_create_from_tag(Main *bmain,
@@ -544,6 +580,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
BLI_assert(id_hierarchy_root != nullptr);
LinkNode *relinked_ids = nullptr;
+ IDRemapper *id_remapper = BKE_id_remapper_create();
/* 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. */
ID *id;
@@ -568,6 +605,14 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
* consider we should also relink it, as part of recursive resync. */
if ((other_id->tag & LIB_TAG_DOIT) != 0 && other_id->lib != id_root_reference->lib) {
BLI_linklist_prepend(&relinked_ids, other_id);
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(other_id) &&
+ other_id->override_library->hierarchy_root == id_hierarchy_root) {
+ reference_id = other_id->override_library->reference;
+ ID *local_id = reference_id->newid;
+ if (other_id == local_id) {
+ lib_override_remapper_overrides_add(id_remapper, reference_id, local_id);
+ }
+ }
}
if (other_id != id) {
other_id->lib = id_root_reference->lib;
@@ -575,7 +620,6 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
}
FOREACH_MAIN_ID_END;
- IDRemapper *id_remapper = BKE_id_remapper_create();
for (todo_id_iter = static_cast<LinkData *>(todo_ids.first); todo_id_iter != nullptr;
todo_id_iter = todo_id_iter->next) {
reference_id = static_cast<ID *>(todo_id_iter->data);
@@ -587,15 +631,7 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain,
local_id->override_library->hierarchy_root = id_hierarchy_root;
- BKE_id_remapper_add(id_remapper, reference_id, local_id);
-
- Key *reference_key, *local_key = nullptr;
- if ((reference_key = BKE_key_from_id(reference_id)) != nullptr) {
- local_key = BKE_key_from_id(reference_id->newid);
- BLI_assert(local_key != nullptr);
-
- BKE_id_remapper_add(id_remapper, &reference_key->id, &local_key->id);
- }
+ lib_override_remapper_overrides_add(id_remapper, reference_id, local_id);
}
BKE_libblock_relink_multiple(bmain,
@@ -652,7 +688,7 @@ static void lib_override_group_tag_data_object_to_collection_init_collection_pro
LinkNodePair **collections_linkedlist_p;
if (!BLI_ghash_ensure_p(data->linked_object_to_instantiating_collections,
ob,
- (void ***)&collections_linkedlist_p)) {
+ reinterpret_cast<void ***>(&collections_linkedlist_p))) {
*collections_linkedlist_p = static_cast<LinkNodePair *>(
BLI_memarena_calloc(data->mem_arena, sizeof(**collections_linkedlist_p)));
}
@@ -693,7 +729,7 @@ static void lib_override_hierarchy_dependencies_recursive_tag_from(LibOverrideGr
ID *id = data->id_root;
const bool is_override = data->is_override;
- if ((*(uint *)&id->tag & data->tag) == 0) {
+ if ((*reinterpret_cast<uint *>(&id->tag) & data->tag) == 0) {
/* This ID is not tagged, no reason to proceed further to its parents. */
return;
}
@@ -751,7 +787,7 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_PROCESSED_TO) {
/* This ID has already been processed. */
- return (*(uint *)&id->tag & data->tag) != 0;
+ return (*reinterpret_cast<uint *>(&id->tag) & data->tag) != 0;
}
/* This way we won't process again that ID, should we encounter it again through another
* relationship hierarchy. */
@@ -784,11 +820,11 @@ static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTa
*
* This will cover e.g. the case where user override an armature, and would expect the mesh
* object deformed by that armature to also be overridden. */
- if ((*(uint *)&id->tag & data->tag) != 0 && !is_resync) {
+ if ((*reinterpret_cast<uint *>(&id->tag) & data->tag) != 0 && !is_resync) {
lib_override_hierarchy_dependencies_recursive_tag_from(data);
}
- return (*(uint *)&id->tag & data->tag) != 0;
+ return (*reinterpret_cast<uint *>(&id->tag) & data->tag) != 0;
}
static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *data)
@@ -1055,8 +1091,10 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *
continue;
}
- const Library *reference_lib = lib_override_get(bmain, id_owner, nullptr)->reference->lib;
- const ID *to_id_reference = lib_override_get(bmain, to_id, nullptr)->reference;
+ const Library *reference_lib =
+ BKE_lib_override_library_get(bmain, id_owner, nullptr, nullptr)->reference->lib;
+ const ID *to_id_reference =
+ BKE_lib_override_library_get(bmain, to_id, nullptr, nullptr)->reference;
if (to_id_reference->lib != reference_lib) {
/* We do not override data-blocks from other libraries, nor do we process them. */
continue;
@@ -1121,14 +1159,26 @@ static bool lib_override_library_create_do(Main *bmain,
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
lib_override_hierarchy_dependencies_recursive_tag(&data);
+ /* In case the operation is on an already partially overridden hierarchy, all existing overrides
+ * in that hierarchy need to be tagged for remapping from linked reference ID usages to newly
+ * created overrides ones. */
+ if (id_hierarchy_root_reference->lib != id_root_reference->lib) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
+ BLI_assert(id_hierarchy_root_reference->override_library->reference->lib ==
+ id_root_reference->lib);
+
+ BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
+ data.hierarchy_root_id = id_hierarchy_root_reference;
+ data.id_root = id_hierarchy_root_reference;
+ data.is_override = true;
+ lib_override_overrides_group_tag(&data);
+ }
+
BKE_main_relations_free(bmain);
lib_override_group_tag_data_clear(&data);
bool success = false;
if (id_hierarchy_root_reference->lib != id_root_reference->lib) {
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
- BLI_assert(id_hierarchy_root_reference->override_library->reference->lib ==
- id_root_reference->lib);
success = BKE_lib_override_library_create_from_tag(bmain,
owner_library,
id_root_reference,
@@ -1157,6 +1207,7 @@ static void lib_override_library_create_post_process(Main *bmain,
ID *id_root,
ID *id_instance_hint,
Collection *residual_storage,
+ const Object *old_active_object,
const bool is_resync)
{
/* NOTE: We only care about local IDs here, if a linked object is not instantiated in any way we
@@ -1180,9 +1231,9 @@ static void lib_override_library_create_post_process(Main *bmain,
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_instance_hint != nullptr && GS(id_instance_hint->name) == ID_OB ?
- (Object *)id_instance_hint :
+ reinterpret_cast<Object *>(id_instance_hint) :
nullptr;
- Collection *collection_new = ((Collection *)id_root->newid);
+ Collection *collection_new = (reinterpret_cast<Collection *>(id_root->newid));
if (is_resync && BKE_collection_is_in_scene(collection_new)) {
break;
}
@@ -1192,11 +1243,11 @@ static void lib_override_library_create_post_process(Main *bmain,
else if (id_instance_hint != nullptr) {
BLI_assert(GS(id_instance_hint->name) == ID_GR);
BKE_collection_add_from_collection(
- bmain, scene, ((Collection *)id_instance_hint), collection_new);
+ bmain, scene, (reinterpret_cast<Collection *>(id_instance_hint)), collection_new);
}
else {
BKE_collection_add_from_collection(
- bmain, scene, ((Collection *)id_root), collection_new);
+ bmain, scene, (reinterpret_cast<Collection *>(id_root)), collection_new);
}
BLI_assert(BKE_collection_is_in_scene(collection_new));
@@ -1205,9 +1256,10 @@ static void lib_override_library_create_post_process(Main *bmain,
break;
}
case ID_OB: {
- Object *ob_new = (Object *)id_root->newid;
+ Object *ob_new = reinterpret_cast<Object *>(id_root->newid);
if (BLI_gset_lookup(all_objects_in_scene, ob_new) == nullptr) {
- BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
+ BKE_collection_object_add_from(
+ bmain, scene, reinterpret_cast<Object *>(id_root), ob_new);
all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
}
break;
@@ -1217,10 +1269,12 @@ static void lib_override_library_create_post_process(Main *bmain,
}
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
+
/* 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;
+ Object *ob_new = reinterpret_cast<Object *>(ob->id.newid);
if (ob_new == nullptr || (ID_IS_LINKED(ob_new) && ob_new->id.lib != owner_library)) {
continue;
}
@@ -1228,6 +1282,14 @@ static void lib_override_library_create_post_process(Main *bmain,
BLI_assert(ob_new->id.override_library != nullptr &&
ob_new->id.override_library->reference == &ob->id);
+ if (old_active_object == ob) {
+ Base *basact = BKE_view_layer_base_find(view_layer, ob_new);
+ if (basact != nullptr) {
+ view_layer->basact = basact;
+ }
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ }
+
if (BLI_gset_lookup(all_objects_in_scene, ob_new) == nullptr) {
if (id_root != nullptr && default_instantiating_collection == nullptr) {
ID *id_ref = id_root->newid != nullptr ? id_root->newid : id_root;
@@ -1254,7 +1316,7 @@ static void lib_override_library_create_post_process(Main *bmain,
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;
+ Object *ob_ref = reinterpret_cast<Object *>(id_ref);
LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
if (BKE_collection_has_object(collection, ob_ref) &&
(view_layer != nullptr ?
@@ -1284,8 +1346,10 @@ static void lib_override_library_create_post_process(Main *bmain,
ID *id_ref = id_root->newid != nullptr ? id_root->newid : id_root;
switch (GS(id_ref->name)) {
case ID_GR:
- BKE_collection_add_from_collection(
- bmain, scene, (Collection *)id_ref, default_instantiating_collection);
+ BKE_collection_add_from_collection(bmain,
+ scene,
+ reinterpret_cast<Collection *>(id_ref),
+ default_instantiating_collection);
break;
default:
/* Add to master collection. */
@@ -1315,6 +1379,8 @@ bool BKE_lib_override_library_create(Main *bmain,
if (id_hierarchy_root_reference == nullptr) {
id_hierarchy_root_reference = id_root_reference;
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Object *old_active_object = BKE_view_layer_active_object_get(view_layer);
const bool success = lib_override_library_create_do(bmain,
scene,
@@ -1338,6 +1404,7 @@ bool BKE_lib_override_library_create(Main *bmain,
id_root_reference,
id_instance_hint,
nullptr,
+ old_active_object,
false);
/* Cleanup. */
@@ -1392,7 +1459,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int
BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
ID *id_owner;
int best_level_placeholder = 0;
- lib_override_get(bmain, id, &id_owner);
+ BKE_lib_override_library_get(bmain, id, nullptr, &id_owner);
return lib_override_root_find(bmain, id_owner, curr_level + 1, &best_level_placeholder);
}
/* This way we won't process again that ID, should we encounter it again through another
@@ -1431,7 +1498,7 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int
BLI_assert(id->flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
ID *id_owner;
int best_level_placeholder = 0;
- lib_override_get(bmain, best_root_id_candidate, &id_owner);
+ BKE_lib_override_library_get(bmain, best_root_id_candidate, nullptr, &id_owner);
best_root_id_candidate = lib_override_root_find(
bmain, id_owner, curr_level + 1, &best_level_placeholder);
}
@@ -1651,6 +1718,8 @@ static bool lib_override_library_resync(Main *bmain,
ID *id_root_reference = id_root->override_library->reference;
ID *id;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Object *old_active_object = BKE_view_layer_active_object_get(view_layer);
if (id_root_reference->tag & LIB_TAG_MISSING) {
BKE_reportf(reports != nullptr ? reports->reports : nullptr,
@@ -1731,6 +1800,10 @@ static bool lib_override_library_resync(Main *bmain,
lib_override_hierarchy_dependencies_recursive_tag(&data);
FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if ((id->lib != id_root->lib) || !ID_IS_OVERRIDE_LIBRARY(id)) {
+ continue;
+ }
+
/* 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
@@ -1744,62 +1817,67 @@ static bool lib_override_library_resync(Main *bmain,
id->tag |= LIB_TAG_MISSING;
}
- if ((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. */
- IDOverrideLibrary *id_override_library = lib_override_get(bmain, id, nullptr);
+ /* 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. */
+ IDOverrideLibrary *id_override_library = BKE_lib_override_library_get(
+ bmain, id, nullptr, nullptr);
- if (id_override_library->hierarchy_root != id_root->override_library->hierarchy_root) {
- continue;
+ if (id_override_library->hierarchy_root != id_root->override_library->hierarchy_root) {
+ continue;
+ }
+
+ ID *reference_id = id_override_library->reference;
+ if (GS(reference_id->name) != GS(id->name)) {
+ switch (GS(id->name)) {
+ case ID_KE:
+ reference_id = reinterpret_cast<ID *>(BKE_key_from_id(reference_id));
+ break;
+ case ID_GR:
+ BLI_assert(GS(reference_id->name) == ID_SCE);
+ reference_id = reinterpret_cast<ID *>(
+ reinterpret_cast<Scene *>(reference_id)->master_collection);
+ break;
+ case ID_NT:
+ reference_id = reinterpret_cast<ID *>(ntreeFromID(id));
+ break;
+ default:
+ break;
}
+ }
+ if (reference_id == nullptr) {
+ /* Can happen e.g. when there is a local override of a shape-key, but the matching linked
+ * obdata (mesh etc.) does not have any shape-key anymore. */
+ continue;
+ }
+ BLI_assert(GS(reference_id->name) == GS(id->name));
- 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;
- }
+ 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) || (id->tag & LIB_TAG_DOIT) == 0) {
+ continue;
}
- BLI_assert(GS(reference_id->name) == GS(id->name));
+ 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
+ * that we can get rid of this local override. */
+ LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
+ if (!ELEM(op->rna_prop_type, PROP_POINTER, PROP_COLLECTION)) {
+ id->override_library->reference->tag |= LIB_TAG_DOIT;
+ break;
+ }
- 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) || (id->tag & LIB_TAG_DOIT) == 0) {
- 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
- * that we can get rid of this local override. */
- LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
- if (!ELEM(op->rna_prop_type, PROP_POINTER, PROP_COLLECTION)) {
+ bool do_break = false;
+ LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
id->override_library->reference->tag |= LIB_TAG_DOIT;
- break;
- }
-
- bool do_break = false;
- LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
- if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
- id->override_library->reference->tag |= LIB_TAG_DOIT;
- do_break = true;
- break;
- }
- }
- if (do_break) {
+ do_break = true;
break;
}
}
+ if (do_break) {
+ break;
+ }
}
}
}
@@ -1841,60 +1919,62 @@ static bool 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 != nullptr && id->lib == id_root_reference->lib) {
- ID *id_override_new = id->newid;
- ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id));
+ if ((id->tag & LIB_TAG_DOIT) == 0 || id->newid == nullptr ||
+ id->lib != id_root_reference->lib) {
+ continue;
+ }
+ ID *id_override_new = id->newid;
+ ID *id_override_old = static_cast<ID *>(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_IS_LINKED(id_override_new) || */ id_override_new->lib == id->lib);
- BLI_assert(id_override_old == nullptr || 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;
- }
+ 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_IS_LINKED(id_override_new) || */ id_override_new->lib == id->lib);
+ BLI_assert(id_override_old == nullptr || 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 != nullptr) {
- /* 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));
-
- 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;
-
- lib_override_object_posemode_transfer(id_override_new, id_override_old);
-
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old));
-
- id_override_new->override_library->flag = id_override_old->override_library->flag;
-
- /* 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 = static_cast<IDOverrideLibraryProperty *>(
- id_override_new->override_library->properties.first);
- IDOverrideLibraryProperty *op_old = static_cast<IDOverrideLibraryProperty *>(
- 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);
- }
+ if (id_override_old != nullptr) {
+ /* 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));
+
+ 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;
+
+ lib_override_object_posemode_transfer(id_override_new, id_override_old);
+
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_override_old));
+
+ id_override_new->override_library->flag = id_override_old->override_library->flag;
+
+ /* 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 = static_cast<IDOverrideLibraryProperty *>(
+ id_override_new->override_library->properties.first);
+ IDOverrideLibraryProperty *op_old = static_cast<IDOverrideLibraryProperty *>(
+ 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);
}
-
- BLI_addtail(no_main_ids_list, id_override_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);
}
+
+ BLI_addtail(no_main_ids_list, id_override_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;
@@ -1913,53 +1993,56 @@ static bool lib_override_library_resync(Main *bmain,
* 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 != nullptr && 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 = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id));
+ if ((id->tag & LIB_TAG_DOIT) == 0 || id->newid == nullptr ||
+ id->lib != id_root_reference->lib) {
+ continue;
+ }
- if (id_override_old == nullptr) {
- 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. */
- PointerRNA rnaptr_src, rnaptr_dst;
- RNA_id_pointer_create(id_override_old, &rnaptr_src);
- RNA_id_pointer_create(id_override_new, &rnaptr_dst);
-
- /* We remove any operation tagged with `IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE`,
- * that way the potentially new pointer will be properly kept, when old one is still valid
- * too (typical case: assigning new ID to some usage, while old one remains used elsewhere
- * in the override hierarchy). */
- LISTBASE_FOREACH_MUTABLE (
- IDOverrideLibraryProperty *, op, &id_override_new->override_library->properties) {
- LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
- if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) {
- lib_override_library_property_operation_clear(opop);
- BLI_freelinkN(&op->operations, opop);
- }
- }
- if (BLI_listbase_is_empty(&op->operations)) {
- BKE_lib_override_library_property_delete(id_override_new->override_library, op);
+ ID *id_override_new = id->newid;
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_override_new)) {
+ continue;
+ }
+
+ ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id));
+ if (id_override_old == nullptr) {
+ 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. */
+ PointerRNA rnaptr_src, rnaptr_dst;
+ RNA_id_pointer_create(id_override_old, &rnaptr_src);
+ RNA_id_pointer_create(id_override_new, &rnaptr_dst);
+
+ /* We remove any operation tagged with `IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE`,
+ * that way the potentially new pointer will be properly kept, when old one is still valid
+ * too (typical case: assigning new ID to some usage, while old one remains used elsewhere
+ * in the override hierarchy). */
+ LISTBASE_FOREACH_MUTABLE (
+ IDOverrideLibraryProperty *, op, &id_override_new->override_library->properties) {
+ LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
+ if (opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) {
+ lib_override_library_property_operation_clear(opop);
+ BLI_freelinkN(&op->operations, opop);
}
}
-
- RNA_struct_override_apply(bmain,
- &rnaptr_dst,
- &rnaptr_src,
- nullptr,
- id_override_new->override_library,
- do_hierarchy_enforce ?
- RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS :
- RNA_OVERRIDE_APPLY_FLAG_NOP);
+ if (BLI_listbase_is_empty(&op->operations)) {
+ BKE_lib_override_library_property_delete(id_override_new->override_library, op);
+ }
}
- BLI_linklist_prepend(&id_override_old_list, id_override_old);
+ RNA_struct_override_apply(bmain,
+ &rnaptr_dst,
+ &rnaptr_src,
+ nullptr,
+ id_override_new->override_library,
+ do_hierarchy_enforce ? RNA_OVERRIDE_APPLY_FLAG_IGNORE_ID_POINTERS :
+ RNA_OVERRIDE_APPLY_FLAG_NOP);
}
+
+ BLI_linklist_prepend(&id_override_old_list, id_override_old);
}
FOREACH_MAIN_ID_END;
@@ -2003,9 +2086,13 @@ static bool lib_override_library_resync(Main *bmain,
}
/* 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)) {
+ else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY(id)) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
+ id->override_library->reference->lib->id.tag & LIB_TAG_MISSING) {
+ /* Do not delete overrides which reference is missing because the library itself is missing
+ * (ref. T100586). */
+ }
+ else if (!BKE_lib_override_library_is_user_edited(id)) {
/* If user never edited them, we can delete them. */
id->tag |= LIB_TAG_DOIT;
id->tag &= ~LIB_TAG_MISSING;
@@ -2068,6 +2155,7 @@ static bool lib_override_library_resync(Main *bmain,
id_root_reference,
id_root,
override_resync_residual_storage,
+ old_active_object,
true);
}
@@ -2119,12 +2207,12 @@ static bool lib_override_resync_id_lib_level_is_valid(ID *id,
}
/* Find the root of the override hierarchy the given `id` belongs to. */
-static ID *lib_override_library_main_resync_root_get(Main *bmain, ID *id)
+static ID *lib_override_library_main_resync_root_get(Main * /*bmain*/, ID *id)
{
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
- if (id_type->owner_get != nullptr) {
- id = id_type->owner_get(bmain, id);
+ ID *id_owner = BKE_id_owner_get(id);
+ if (id_owner != nullptr) {
+ id = id_owner;
}
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id));
}
@@ -2261,7 +2349,7 @@ static bool lib_override_resync_tagging_finalize_recurse(
BLI_assert(id_root->override_library != nullptr);
LinkNodePair **id_resync_roots_p;
- if (!BLI_ghash_ensure_p(id_roots, id_root, (void ***)&id_resync_roots_p)) {
+ if (!BLI_ghash_ensure_p(id_roots, id_root, reinterpret_cast<void ***>(&id_resync_roots_p))) {
*id_resync_roots_p = MEM_cnew<LinkNodePair>(__func__);
}
@@ -2311,11 +2399,21 @@ static void lib_override_library_main_resync_on_library_indirect_level(
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
+
+ if (!lib_override_resync_id_lib_level_is_valid(id, library_indirect_level, true)) {
+ continue;
+ }
+
if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
/* We already processed that ID as part of another ID's hierarchy. */
continue;
}
+ /* Do not attempt to resync from missing data. */
+ if (((id->tag | id->override_library->reference->tag) & LIB_TAG_MISSING) != 0) {
+ continue;
+ }
+
if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) {
/* This ID is not part of an override hierarchy. */
continue;
@@ -2344,6 +2442,11 @@ static void lib_override_library_main_resync_on_library_indirect_level(
continue;
}
+ /* Do not attempt to resync from missing data. */
+ if (((id->tag | id->override_library->reference->tag) & LIB_TAG_MISSING) != 0) {
+ continue;
+ }
+
if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) {
/* This ID is not part of an override hierarchy. */
BLI_assert((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
@@ -2436,8 +2539,8 @@ static void lib_override_library_main_resync_on_library_indirect_level(
2,
"Resyncing all dependencies under root %s (%p), first one being '%s'...",
id_root->name,
- library,
- ((ID *)id_resync_roots->list->link)->name);
+ reinterpret_cast<void *>(library),
+ reinterpret_cast<ID *>(id_resync_roots->list->link)->name);
const bool success = lib_override_library_resync(bmain,
scene,
view_layer,
@@ -2471,6 +2574,15 @@ static void lib_override_library_main_resync_on_library_indirect_level(
/* Check there are no left-over IDs needing resync from the current (or higher) level of indirect
* library level. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (!ID_IS_OVERRIDE_LIBRARY(id)) {
+ continue;
+ }
+ /* Do not attempt to resync to/from missing data. */
+ if (((id->tag | (ID_IS_OVERRIDE_LIBRARY_REAL(id) ? id->override_library->reference->tag : 0)) &
+ LIB_TAG_MISSING) != 0) {
+ continue;
+ }
+
const bool is_valid_tagged_need_resync = ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 ||
lib_override_resync_id_lib_level_is_valid(
id, library_indirect_level - 1, false));
@@ -2531,7 +2643,7 @@ static int lib_override_sort_libraries_func(LibraryIDLinkCallbackData *cb_data)
if (owner_library_indirect_level >= id->lib->temp_index) {
id->lib->temp_index = owner_library_indirect_level + 1;
- *(bool *)cb_data->user_data = true;
+ *reinterpret_cast<bool *>(cb_data->user_data) = true;
}
}
return IDWALK_RET_NOP;
@@ -2587,6 +2699,9 @@ void BKE_lib_override_library_main_resync(Main *bmain,
/* Hide the collection from viewport and render. */
override_resync_residual_storage->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER;
}
+ /* BKE_collection_add above could have tagged the view_layer out of sync. */
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Object *old_active_object = BKE_view_layer_active_object_get(view_layer);
/* Necessary to improve performances, and prevent layers matching override sub-collections to be
* lost when re-syncing the parent override collection.
@@ -2608,8 +2723,15 @@ void BKE_lib_override_library_main_resync(Main *bmain,
BKE_layer_collection_resync_allow();
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
- lib_override_library_create_post_process(
- bmain, scene, view_layer, nullptr, nullptr, nullptr, override_resync_residual_storage, true);
+ lib_override_library_create_post_process(bmain,
+ scene,
+ view_layer,
+ nullptr,
+ nullptr,
+ nullptr,
+ override_resync_residual_storage,
+ old_active_object,
+ true);
if (BKE_collection_is_empty(override_resync_residual_storage)) {
BKE_collection_delete(bmain, override_resync_residual_storage, true);
@@ -2624,6 +2746,8 @@ void BKE_lib_override_library_main_resync(Main *bmain,
library->filepath);
}
}
+
+ BLI_assert(BKE_main_namemap_validate(bmain));
}
void BKE_lib_override_library_delete(Main *bmain, ID *id_root)
@@ -2687,7 +2811,7 @@ void BKE_lib_override_library_make_local(ID *id)
}
if (GS(id->name) == ID_SCE) {
- Collection *master_collection = ((Scene *)id)->master_collection;
+ Collection *master_collection = reinterpret_cast<Scene *>(id)->master_collection;
if (master_collection != nullptr) {
master_collection->id.flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE;
}
@@ -3069,9 +3193,9 @@ bool BKE_lib_override_library_status_check_local(Main *bmain, ID *local)
/* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
* ensure this is valid, but in some situations (like hidden collections etc.) this won't
* be the case, so we need to take care of this ourselves. */
- Object *ob_local = (Object *)local;
+ Object *ob_local = reinterpret_cast<Object *>(local);
if (ob_local->type == OB_ARMATURE) {
- Object *ob_reference = (Object *)local->override_library->reference;
+ Object *ob_reference = reinterpret_cast<Object *>(local->override_library->reference);
BLI_assert(ob_local->data != nullptr);
BLI_assert(ob_reference->data != nullptr);
BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true);
@@ -3129,9 +3253,9 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
/* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
* ensure this is valid, but in some situations (like hidden collections etc.) this won't
* be the case, so we need to take care of this ourselves. */
- Object *ob_local = (Object *)local;
+ Object *ob_local = reinterpret_cast<Object *>(local);
if (ob_local->type == OB_ARMATURE) {
- Object *ob_reference = (Object *)local->override_library->reference;
+ Object *ob_reference = reinterpret_cast<Object *>(local->override_library->reference);
BLI_assert(ob_local->data != nullptr);
BLI_assert(ob_reference->data != nullptr);
BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true);
@@ -3177,9 +3301,9 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local)
/* Our beloved pose's bone cross-data pointers. Usually, depsgraph evaluation would
* ensure this is valid, but in some situations (like hidden collections etc.) this won't
* be the case, so we need to take care of this ourselves. */
- Object *ob_local = (Object *)local;
+ Object *ob_local = reinterpret_cast<Object *>(local);
if (ob_local->type == OB_ARMATURE) {
- Object *ob_reference = (Object *)local->override_library->reference;
+ Object *ob_reference = reinterpret_cast<Object *>(local->override_library->reference);
BLI_assert(ob_local->data != nullptr);
BLI_assert(ob_reference->data != nullptr);
BKE_pose_ensure(bmain, ob_local, static_cast<bArmature *>(ob_local->data), true);
@@ -3233,7 +3357,7 @@ static void lib_override_library_operations_create_cb(TaskPool *__restrict pool,
if (BKE_lib_override_library_operations_create(create_data->bmain, id)) {
/* Technically no need for atomic, all jobs write the same value and we only care if one did
* it. But play safe and avoid implicit assumptions. */
- atomic_fetch_and_or_uint8((uint8_t *)&create_data->changed, true);
+ atomic_fetch_and_or_uint8(reinterpret_cast<uint8_t *>(&create_data->changed), true);
}
}
@@ -3273,7 +3397,7 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
/* Usual issue with pose, it's quiet rare but sometimes they may not be up to date when this
* function is called. */
if (GS(id->name) == ID_OB) {
- Object *ob = (Object *)id;
+ Object *ob = reinterpret_cast<Object *>(id);
if (ob->type == OB_ARMATURE) {
BLI_assert(ob->data != nullptr);
BKE_pose_ensure(bmain, ob, static_cast<bArmature *>(ob->data), true);
@@ -3592,6 +3716,9 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
return;
}
+ /* Remove the pair (idname, lib) of this temp id from the name map. */
+ BKE_main_namemap_remove_name(bmain, tmp_id, tmp_id->name + 2);
+
tmp_id->lib = local->lib;
/* This ID name is problematic, since it is an 'rna name property' it should not be editable or
@@ -3606,7 +3733,9 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
Key *tmp_key = BKE_key_from_id(tmp_id);
if (local_key != nullptr && tmp_key != nullptr) {
tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
+ BKE_main_namemap_remove_name(bmain, &tmp_key->id, tmp_key->id.name + 2);
tmp_key->id.lib = local_key->id.lib;
+ BLI_strncpy(tmp_key->id.name, local_key->id.name, sizeof(tmp_key->id.name));
}
PointerRNA rnaptr_src, rnaptr_dst, rnaptr_storage_stack, *rnaptr_storage = nullptr;
@@ -3643,8 +3772,10 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
}
/* Again, horribly inefficient in our case, we need something off-Main
- * (aka more generic nolib copy/free stuff)! */
- BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER, true);
+ * (aka more generic nolib copy/free stuff).
+ * NOTE: Do not remove this tmp_id's name from the namemap here, since this name actually still
+ * exists in `bmain`. */
+ BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER | LIB_ID_FREE_NO_NAMEMAP_REMOVE, true);
if (GS(local->name) == ID_AR) {
/* Fun times again, thanks to bone pointers in pose data of objects. We keep same ID addresses,
@@ -3688,6 +3819,8 @@ void BKE_lib_override_library_main_update(Main *bmain)
Main *orig_gmain = G_MAIN;
G_MAIN = bmain;
+ BLI_assert(BKE_main_namemap_validate(bmain));
+
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->override_library != nullptr) {
BKE_lib_override_library_update(bmain, id);
@@ -3695,6 +3828,8 @@ void BKE_lib_override_library_main_update(Main *bmain)
}
FOREACH_MAIN_ID_END;
+ BLI_assert(BKE_main_namemap_validate(bmain));
+
G_MAIN = orig_gmain;
}
@@ -3706,7 +3841,7 @@ bool BKE_lib_override_library_id_is_user_deletable(Main *bmain, ID *id)
if (GS(id->name) != ID_OB) {
return true;
}
- Object *ob = (Object *)id;
+ Object *ob = reinterpret_cast<Object *>(id);
LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
if (!ID_IS_OVERRIDE_LIBRARY(collection)) {
continue;
@@ -3777,7 +3912,7 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain,
* (and possibly all over Blender code).
* Not impossible to do, but would rather see first is extra useless usual user handling is
* actually a (performances) issue here, before doing it. */
- storage_id = BKE_id_copy((Main *)override_storage, local);
+ storage_id = BKE_id_copy(reinterpret_cast<Main *>(override_storage), local);
if (storage_id != nullptr) {
PointerRNA rnaptr_reference, rnaptr_final, rnaptr_storage;
@@ -3816,7 +3951,7 @@ void BKE_lib_override_library_operations_store_end(
void BKE_lib_override_library_operations_store_finalize(OverrideLibraryStorage *override_storage)
{
/* We cannot just call BKE_main_free(override_storage), not until we have option to make
- * 'ghost' copies of IDs without increasing usercount of used data-blocks. */
+ * 'ghost' copies of IDs without increasing user-count of used data-blocks. */
ID *id;
FOREACH_MAIN_ID_BEGIN (override_storage, id) {
diff --git a/source/blender/blenkernel/intern/lib_principle_properties.c b/source/blender/blenkernel/intern/lib_principle_properties.c
deleted file mode 100644
index 204ca9ff9d6..00000000000
--- a/source/blender/blenkernel/intern/lib_principle_properties.c
+++ /dev/null
@@ -1,106 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2022 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup bke
- */
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "CLG_log.h"
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_ID.h"
-
-#include "BKE_lib_id.h"
-#include "BKE_lib_principle_properties.h"
-#include "BKE_report.h"
-
-#include "BLO_readfile.h"
-
-#include "BLI_listbase.h"
-#include "BLI_string.h"
-
-#include "RNA_access.h"
-#include "RNA_prototypes.h"
-#include "RNA_types.h"
-
-static CLG_LogRef LOG = {"bke.idprincipleprops"};
-
-IDPrincipleProperties *BKE_lib_principleprop_init(ID *id)
-{
- BLI_assert(id->principle_properties == NULL);
-
- /* Else, generate new empty override. */
- id->principle_properties = MEM_callocN(sizeof(*id->principle_properties), __func__);
-
- return id->principle_properties;
-}
-
-void BKE_lib_principleprop_clear(IDPrincipleProperties *principle_props, bool UNUSED(do_id_user))
-{
- LISTBASE_FOREACH_MUTABLE (IDPrincipleProperty *, pprop, &principle_props->properties) {
- BLI_assert(pprop->rna_path != NULL);
- MEM_freeN(pprop->rna_path);
- MEM_freeN(pprop);
- }
- BLI_listbase_clear(&principle_props->properties);
- principle_props->flag = 0;
-}
-
-void BKE_lib_principleprop_free(IDPrincipleProperties **principle_props, bool do_id_user)
-{
- BLI_assert(*principle_props != NULL);
-
- BKE_lib_principleprop_clear(*principle_props, do_id_user);
- MEM_freeN(*principle_props);
- *principle_props = NULL;
-}
-
-IDPrincipleProperty *BKE_lib_principleprop_find(IDPrincipleProperties *principle_props,
- const char *rna_path)
-{
- return BLI_findstring_ptr(
- &principle_props->properties, rna_path, offsetof(IDPrincipleProperty, rna_path));
-}
-
-IDPrincipleProperty *BKE_lib_principleprop_get(IDPrincipleProperties *principle_props,
- const char *rna_path,
- bool *r_created)
-{
- IDPrincipleProperty *pprop = BKE_lib_principleprop_find(principle_props, rna_path);
-
- if (pprop == NULL) {
- pprop = MEM_callocN(sizeof(*pprop), __func__);
- pprop->rna_path = BLI_strdup(rna_path);
- BLI_addtail(&principle_props->properties, pprop);
-
- if (r_created) {
- *r_created = true;
- }
- }
- else if (r_created) {
- *r_created = false;
- }
-
- return pprop;
-}
-
-void BKE_lib_principleprop_delete(IDPrincipleProperties *principle_props,
- IDPrincipleProperty *principle_prop)
-{
- BLI_remlink(&principle_props->properties, principle_prop);
-}
-
-bool BKE_lib_principleprop_rna_property_find(struct PointerRNA *idpoin,
- const struct IDPrincipleProperty *principle_prop,
- struct PointerRNA *r_principle_poin,
- struct PropertyRNA **r_principle_prop)
-{
- BLI_assert(RNA_struct_is_ID(idpoin->type));
- return RNA_path_resolve_property(
- idpoin, principle_prop->rna_path, r_principle_poin, r_principle_prop);
-}
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index 38252a46b93..50843b18d18 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -391,8 +391,7 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner)
switch ((ID_Type)id_type_owner) {
case ID_LI:
- /* ID_LI doesn't exist as filter_id. */
- return 0;
+ return FILTER_ID_LI;
case ID_SCE:
return FILTER_ID_OB | FILTER_ID_WO | FILTER_ID_SCE | FILTER_ID_MC | FILTER_ID_MA |
FILTER_ID_GR | FILTER_ID_TXT | FILTER_ID_LS | FILTER_ID_MSK | FILTER_ID_SO |
@@ -472,6 +471,8 @@ uint64_t BKE_library_id_can_use_filter_id(const ID *id_owner)
/* Deprecated... */
return 0;
}
+
+ BLI_assert_unreachable();
return 0;
}
@@ -695,8 +696,8 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
bool has_valid_from_users = false;
/* Preemptively consider this ID as unused. That way if there is a loop of dependency leading
* back to it, it won't create a fake 'valid user' detection.
- * NOTE: This can only only be done for a subset of IDs, some types are never 'indirectly
- * unused', same for IDs with a fake user. */
+ * NOTE: This can only be done for a subset of IDs, some types are never 'indirectly unused',
+ * same for IDs with a fake user. */
if ((id->flag & LIB_FAKEUSER) == 0 && !ELEM(GS(id->name), ID_SCE, ID_WM, ID_SCR, ID_WS, ID_LI)) {
id->tag |= tag;
}
@@ -710,9 +711,8 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
ID *id_from = id_from_item->id_pointer.from;
if ((id_from->flag & LIB_EMBEDDED_DATA) != 0) {
/* Directly 'by-pass' to actual real ID owner. */
- const IDTypeInfo *type_info_from = BKE_idtype_get_info_from_id(id_from);
- BLI_assert(type_info_from->owner_get != NULL);
- id_from = type_info_from->owner_get(bmain, id_from);
+ id_from = BKE_id_owner_get(id_from);
+ BLI_assert(id_from != NULL);
}
lib_query_unused_ids_tag_recurse(
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 246999a1179..2ebdc6788d9 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -428,10 +428,13 @@ static void libblock_remap_data_update_tags(ID *old_id, ID *new_id, void *user_d
}
static void libblock_remap_reset_remapping_status_callback(ID *old_id,
- ID *UNUSED(new_id),
+ ID *new_id,
void *UNUSED(user_data))
{
BKE_libblock_runtime_reset_remapping_status(old_id);
+ if (new_id != NULL) {
+ BKE_libblock_runtime_reset_remapping_status(new_id);
+ }
}
/**
@@ -814,7 +817,7 @@ void BKE_libblock_relink_ex(
Main *bmain, void *idv, void *old_idv, void *new_idv, const short remap_flags)
{
- /* Should be able to replace all _relink() funcs (constraints, rigidbody, etc.) ? */
+ /* Should be able to replace all _relink() functions (constraints, rigidbody, etc.) ? */
ID *id = idv;
ID *old_id = old_idv;
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 03a17b2ecc5..516fb9b75b6 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -26,14 +26,26 @@
#include "BKE_lib_query.h"
#include "BKE_library.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_packedFile.h"
/* Unused currently. */
// static CLG_LogRef LOG = {.identifier = "bke.library"};
+struct BlendWriter;
+struct BlendDataReader;
+
+static void library_runtime_reset(Library *lib)
+{
+ if (lib->runtime.name_map) {
+ BKE_main_namemap_destroy(&lib->runtime.name_map);
+ }
+}
+
static void library_free_data(ID *id)
{
Library *library = (Library *)id;
+ library_runtime_reset(library);
if (library->packedfile) {
BKE_packedfile_free(library->packedfile);
}
@@ -61,9 +73,15 @@ static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data)
}
}
+static void library_blend_read_data(struct BlendDataReader *UNUSED(reader), ID *id)
+{
+ Library *lib = (Library *)id;
+ lib->runtime.name_map = NULL;
+}
+
IDTypeInfo IDType_ID_LI = {
.id_code = ID_LI,
- .id_filter = 0,
+ .id_filter = FILTER_ID_LI,
.main_listbase_index = INDEX_ID_LI,
.struct_size = sizeof(Library),
.name = "Library",
@@ -79,10 +97,10 @@ IDTypeInfo IDType_ID_LI = {
.foreach_id = library_foreach_id,
.foreach_cache = NULL,
.foreach_path = library_foreach_path,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = NULL,
- .blend_read_data = NULL,
+ .blend_read_data = library_blend_read_data,
.blend_read_lib = NULL,
.blend_read_expand = NULL,
@@ -104,7 +122,7 @@ void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
/* Not essential but set `filepath_abs` is an absolute copy of value which
* is more useful if its kept in sync. */
if (BLI_path_is_rel(lib->filepath_abs)) {
- /* NOTE(campbell): the file may be unsaved, in this case, setting the
+ /* NOTE(@campbellbarton): the file may be unsaved, in this case, setting the
* `filepath_abs` on an indirectly linked path is not allowed from the
* outliner, and its not really supported but allow from here for now
* since making local could cause this to be directly linked.
diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c
index 879e4e24928..42af11294c9 100644
--- a/source/blender/blenkernel/intern/light.c
+++ b/source/blender/blenkernel/intern/light.c
@@ -80,6 +80,7 @@ static void light_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
BKE_id_copy_ex(
bmain, (ID *)la_src->nodetree, (ID **)&la_dst->nodetree, flag_private_id_data);
}
+ la_dst->nodetree->owner_id = &la_dst->id;
}
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
@@ -188,7 +189,7 @@ IDTypeInfo IDType_ID_LA = {
.foreach_id = light_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = light_blend_write,
.blend_read_data = light_blend_read_data,
diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c
index 9e731b1f878..a2151d6c6f1 100644
--- a/source/blender/blenkernel/intern/lightprobe.c
+++ b/source/blender/blenkernel/intern/lightprobe.c
@@ -85,7 +85,7 @@ IDTypeInfo IDType_ID_LP = {
.foreach_id = lightprobe_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = lightprobe_blend_write,
.blend_read_data = lightprobe_blend_read_data,
diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c
index 12a661d139b..cba1bc414c1 100644
--- a/source/blender/blenkernel/intern/linestyle.c
+++ b/source/blender/blenkernel/intern/linestyle.c
@@ -72,6 +72,7 @@ static void linestyle_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const
(ID *)linestyle_src->nodetree,
(ID **)&linestyle_dst->nodetree,
flag_private_id_data);
+ linestyle_dst->nodetree->owner_id = &linestyle_dst->id;
}
LineStyleModifier *linestyle_modifier;
@@ -748,7 +749,7 @@ IDTypeInfo IDType_ID_LS = {
.foreach_id = linestyle_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = linestyle_blend_write,
.blend_read_data = linestyle_blend_read_data,
@@ -2041,9 +2042,7 @@ void BKE_linestyle_default_shader(const bContext *C, FreestyleLineStyle *linesty
BLI_assert(linestyle->nodetree == NULL);
- ntree = ntreeAddTree(NULL, "stroke_shader", "ShaderNodeTree");
-
- linestyle->nodetree = ntree;
+ ntree = ntreeAddTreeEmbedded(NULL, &linestyle->id, "stroke_shader", "ShaderNodeTree");
uv_along_stroke = nodeAddStaticNode(C, ntree, SH_NODE_UVALONGSTROKE);
uv_along_stroke->locx = 0.0f;
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index b9ed783fa8c..239aacf28d6 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -24,6 +24,7 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_main_idmap.h"
+#include "BKE_main_namemap.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -184,6 +185,10 @@ void BKE_main_free(Main *mainvar)
BKE_main_idmap_destroy(mainvar->id_map);
}
+ if (mainvar->name_map) {
+ BKE_main_namemap_destroy(&mainvar->name_map);
+ }
+
BLI_spin_end((SpinLock *)mainvar->lock);
MEM_freeN(mainvar->lock);
MEM_freeN(mainvar);
diff --git a/source/blender/blenkernel/intern/main_namemap.cc b/source/blender/blenkernel/intern/main_namemap.cc
new file mode 100644
index 00000000000..a600afb4ed1
--- /dev/null
+++ b/source/blender/blenkernel/intern/main_namemap.cc
@@ -0,0 +1,500 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_main_namemap.h"
+
+#include "BLI_assert.h"
+#include "BLI_bitmap.h"
+#include "BLI_ghash.h"
+#include "BLI_listbase.h"
+#include "BLI_map.hh"
+#include "BLI_math_base.hh"
+#include "BLI_set.hh"
+#include "BLI_string_utf8.h"
+#include "BLI_string_utils.h"
+
+#include "DNA_ID.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "CLG_log.h"
+
+static CLG_LogRef LOG = {"bke.main_namemap"};
+
+//#define DEBUG_PRINT_MEMORY_USAGE
+
+using namespace blender;
+
+/* Assumes and ensure that the suffix number can never go beyond 1 billion. */
+#define MAX_NUMBER 1000000000
+/* We do not want to get "name.000", so minimal number is 1. */
+#define MIN_NUMBER 1
+
+/**
+ * Helper building final ID name from given base_name and number.
+ *
+ * If everything goes well and we do generate a valid final ID name in given name, we return
+ * true. In case the final name would overflow the allowed ID name length, or given number is
+ * bigger than maximum allowed value, we truncate further the base_name (and given name, which is
+ * assumed to have the same 'base_name' part), and return false.
+ */
+static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number)
+{
+ char number_str[11]; /* Dot + nine digits + NULL terminator. */
+ size_t number_str_len = BLI_snprintf_rlen(number_str, ARRAY_SIZE(number_str), ".%.3d", number);
+
+ /* If the number would lead to an overflow of the maximum ID name length, we need to truncate
+ * the base name part and do all the number checks again. */
+ if (base_name_len + number_str_len >= MAX_NAME || number >= MAX_NUMBER) {
+ if (base_name_len + number_str_len >= MAX_NAME) {
+ base_name_len = MAX_NAME - number_str_len - 1;
+ }
+ else {
+ base_name_len--;
+ }
+ base_name[base_name_len] = '\0';
+
+ /* Code above may have generated invalid utf-8 string, due to raw truncation.
+ * Ensure we get a valid one now. */
+ base_name_len -= (size_t)BLI_str_utf8_invalid_strip(base_name, base_name_len);
+
+ /* Also truncate orig name, and start the whole check again. */
+ name[base_name_len] = '\0';
+ return false;
+ }
+
+ /* We have our final number, we can put it in name and exit the function. */
+ BLI_strncpy(name + base_name_len, number_str, number_str_len + 1);
+ return true;
+}
+
+/* Key used in set/map lookups: just a string name. */
+struct UniqueName_Key {
+ char name[MAX_NAME];
+ uint64_t hash() const
+ {
+ return BLI_ghashutil_strhash_n(name, MAX_NAME);
+ }
+ bool operator==(const UniqueName_Key &o) const
+ {
+ return !BLI_ghashutil_strcmp(name, o.name);
+ }
+};
+
+/* Tracking of used numeric suffixes. For each base name:
+ *
+ * - Exactly track which of the lowest 1024 suffixes are in use,
+ * whenever there is a name collision we pick the lowest "unused"
+ * one. This is done with a bit map.
+ * - Above 1024, do not track them exactly, just track the maximum
+ * suffix value seen so far. Upon collision, assign number that is
+ * one larger.
+ */
+struct UniqueName_Value {
+ static constexpr unsigned max_exact_tracking = 1024;
+ BLI_BITMAP_DECLARE(mask, max_exact_tracking);
+ int max_value = 0;
+
+ void mark_used(int number)
+ {
+ if (number >= 0 && number < max_exact_tracking) {
+ BLI_BITMAP_ENABLE(mask, number);
+ }
+ if (number < MAX_NUMBER) {
+ math::max_inplace(max_value, number);
+ }
+ }
+
+ void mark_unused(int number)
+ {
+ if (number >= 0 && number < max_exact_tracking) {
+ BLI_BITMAP_DISABLE(mask, number);
+ }
+ if (number > 0 && number == max_value) {
+ --max_value;
+ }
+ }
+
+ bool use_if_unused(int number)
+ {
+ if (number >= 0 && number < max_exact_tracking) {
+ if (!BLI_BITMAP_TEST_BOOL(mask, number)) {
+ BLI_BITMAP_ENABLE(mask, number);
+ math::max_inplace(max_value, number);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int use_smallest_unused()
+ {
+ /* Find the smallest available one <1k.
+ * However we never want to pick zero ("none") suffix, even if it is
+ * available, e.g. if Foo.001 was used and we want to create another
+ * Foo.001, we should return Foo.002 and not Foo.
+ * So while searching, mark #0 as "used" to make sure we don't find it,
+ * and restore the value afterwards. */
+
+ BLI_bitmap prev_first = mask[0];
+ mask[0] |= 1;
+ int result = BLI_bitmap_find_first_unset(mask, max_exact_tracking);
+ if (result >= 0) {
+ BLI_BITMAP_ENABLE(mask, result);
+ math::max_inplace(max_value, result);
+ }
+ mask[0] |= prev_first & 1; /* Restore previous value of #0 bit. */
+ return result;
+ }
+};
+
+/* Tracking of names for a single ID type. */
+struct UniqueName_TypeMap {
+ /* Set of full names that are in use. */
+ Set<UniqueName_Key> full_names;
+ /* For each base name (i.e. without numeric suffix), track the
+ * numeric suffixes that are in use. */
+ Map<UniqueName_Key, UniqueName_Value> base_name_to_num_suffix;
+};
+
+struct UniqueName_Map {
+ UniqueName_TypeMap type_maps[INDEX_ID_MAX];
+
+ UniqueName_TypeMap *find_by_type(short id_type)
+ {
+ int index = BKE_idtype_idcode_to_index(id_type);
+ return index >= 0 ? &type_maps[index] : nullptr;
+ }
+};
+
+struct UniqueName_Map *BKE_main_namemap_create()
+{
+ struct UniqueName_Map *map = MEM_new<UniqueName_Map>(__func__);
+ return map;
+}
+
+void BKE_main_namemap_destroy(struct UniqueName_Map **r_name_map)
+{
+#ifdef DEBUG_PRINT_MEMORY_USAGE
+ int64_t size_sets = 0;
+ int64_t size_maps = 0;
+ for (const UniqueName_TypeMap &type_map : (*r_name_map)->type_maps) {
+ size_sets += type_map.full_names.size_in_bytes();
+ size_maps += type_map.base_name_to_num_suffix.size_in_bytes();
+ }
+ printf(
+ "NameMap memory usage: sets %.1fKB, maps %.1fKB\n", size_sets / 1024.0, size_maps / 1024.0);
+#endif
+ MEM_delete<UniqueName_Map>(*r_name_map);
+ *r_name_map = nullptr;
+}
+
+static void main_namemap_populate(UniqueName_Map *name_map, struct Main *bmain, ID *ignore_id)
+{
+ BLI_assert_msg(name_map != nullptr, "name_map should not be null");
+ for (UniqueName_TypeMap &type_map : name_map->type_maps) {
+ type_map.base_name_to_num_suffix.clear();
+ }
+ Library *library = ignore_id->lib;
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if ((id == ignore_id) || (id->lib != library)) {
+ continue;
+ }
+ UniqueName_TypeMap *type_map = name_map->find_by_type(GS(id->name));
+ BLI_assert(type_map != nullptr);
+
+ /* Insert the full name into the set. */
+ UniqueName_Key key;
+ BLI_strncpy(key.name, id->name + 2, MAX_NAME);
+ type_map->full_names.add(key);
+
+ /* Get the name and number parts ("name.number"). */
+ int number = MIN_NUMBER;
+ BLI_split_name_num(key.name, &number, id->name + 2, '.');
+
+ /* Get and update the entry for this base name. */
+ UniqueName_Value &val = type_map->base_name_to_num_suffix.lookup_or_add_default(key);
+ val.mark_used(number);
+ }
+ FOREACH_MAIN_ID_END;
+}
+
+/* Get the name map object used for the given Main/ID.
+ * Lazily creates and populates the contents of the name map, if ensure_created is true.
+ * NOTE: if the contents are populated, the name of the given ID itself is not added. */
+static UniqueName_Map *get_namemap_for(Main *bmain, ID *id, bool ensure_created)
+{
+ if (id->lib != nullptr) {
+ if (ensure_created && id->lib->runtime.name_map == nullptr) {
+ id->lib->runtime.name_map = BKE_main_namemap_create();
+ main_namemap_populate(id->lib->runtime.name_map, bmain, id);
+ }
+ return id->lib->runtime.name_map;
+ }
+ if (ensure_created && bmain->name_map == nullptr) {
+ bmain->name_map = BKE_main_namemap_create();
+ main_namemap_populate(bmain->name_map, bmain, id);
+ }
+ return bmain->name_map;
+}
+
+bool BKE_main_namemap_get_name(struct Main *bmain, struct ID *id, char *name)
+{
+#ifndef __GNUC__ /* GCC warns with `nonull-compare`. */
+ BLI_assert(bmain != nullptr);
+ BLI_assert(id != nullptr);
+#endif
+ UniqueName_Map *name_map = get_namemap_for(bmain, id, true);
+ BLI_assert(name_map != nullptr);
+ BLI_assert(strlen(name) < MAX_NAME);
+ UniqueName_TypeMap *type_map = name_map->find_by_type(GS(id->name));
+ BLI_assert(type_map != nullptr);
+
+ bool is_name_changed = false;
+
+ UniqueName_Key key;
+ while (true) {
+ /* Check if the full original name has a duplicate. */
+ BLI_strncpy(key.name, name, MAX_NAME);
+ const bool has_dup = type_map->full_names.contains(key);
+
+ /* Get the name and number parts ("name.number"). */
+ int number = MIN_NUMBER;
+ size_t base_name_len = BLI_split_name_num(key.name, &number, name, '.');
+
+ bool added_new = false;
+ UniqueName_Value &val = type_map->base_name_to_num_suffix.lookup_or_add_cb(key, [&]() {
+ added_new = true;
+ return UniqueName_Value();
+ });
+ if (added_new || !has_dup) {
+ /* This base name is not used at all yet, or the full original
+ * name has no duplicates. The latter could happen if splitting
+ * by number would produce the same values, for different name
+ * strings (e.g. Foo.001 and Foo.1). */
+ val.mark_used(number);
+
+ if (!has_dup) {
+ BLI_strncpy(key.name, name, MAX_NAME);
+ type_map->full_names.add(key);
+ }
+ return is_name_changed;
+ }
+
+ /* The base name is already used. But our number suffix might not be used yet. */
+ int number_to_use = -1;
+ if (val.use_if_unused(number)) {
+ /* Our particular number suffix is not used yet: use it. */
+ number_to_use = number;
+ }
+ else {
+ /* Find lowest free under 1k and use it. */
+ number_to_use = val.use_smallest_unused();
+
+ /* Did not find one under 1k. */
+ if (number_to_use == -1) {
+ if (number >= MIN_NUMBER && number > val.max_value) {
+ val.max_value = number;
+ number_to_use = number;
+ }
+ else {
+ val.max_value++;
+ number_to_use = val.max_value;
+ }
+ }
+ }
+
+ /* Try to build final name from the current base name and the number.
+ * Note that this can fail due to too long base name, or a too large number,
+ * in which case it will shorten the base name, and we'll start again. */
+ BLI_assert(number_to_use >= MIN_NUMBER);
+ if (id_name_final_build(name, key.name, base_name_len, number_to_use)) {
+ /* All good, add final name to the set. */
+ BLI_strncpy(key.name, name, MAX_NAME);
+ type_map->full_names.add(key);
+ break;
+ }
+
+ /* Name had to be truncated, or number too large: mark
+ * the output name as definitely changed, and proceed with the
+ * truncated name again. */
+ is_name_changed = true;
+ }
+ return is_name_changed;
+}
+
+void BKE_main_namemap_remove_name(struct Main *bmain, struct ID *id, const char *name)
+{
+#ifndef __GNUC__ /* GCC warns with `nonull-compare`. */
+ BLI_assert(bmain != nullptr);
+ BLI_assert(id != nullptr);
+ BLI_assert(name != nullptr);
+#endif
+ /* Name is empty or not initialized yet, nothing to remove. */
+ if (name[0] == '\0') {
+ return;
+ }
+
+ struct UniqueName_Map *name_map = get_namemap_for(bmain, id, false);
+ if (name_map == nullptr) {
+ return;
+ }
+ BLI_assert(strlen(name) < MAX_NAME);
+ UniqueName_TypeMap *type_map = name_map->find_by_type(GS(id->name));
+ BLI_assert(type_map != nullptr);
+
+ UniqueName_Key key;
+ /* Remove full name from the set. */
+ BLI_strncpy(key.name, name, MAX_NAME);
+ type_map->full_names.remove(key);
+
+ int number = MIN_NUMBER;
+ BLI_split_name_num(key.name, &number, name, '.');
+ UniqueName_Value *val = type_map->base_name_to_num_suffix.lookup_ptr(key);
+ if (val == nullptr) {
+ return;
+ }
+ if (number == 0 && val->max_value == 0) {
+ /* This was the only base name usage, remove whole key. */
+ type_map->base_name_to_num_suffix.remove(key);
+ return;
+ }
+ val->mark_unused(number);
+}
+
+struct Uniqueness_Key {
+ char name[MAX_ID_NAME];
+ Library *lib;
+ uint64_t hash() const
+ {
+ return BLI_ghashutil_combine_hash(BLI_ghashutil_strhash_n(name, MAX_ID_NAME),
+ BLI_ghashutil_ptrhash(lib));
+ }
+ bool operator==(const Uniqueness_Key &o) const
+ {
+ return lib == o.lib && !BLI_ghashutil_strcmp(name, o.name);
+ }
+};
+
+static bool main_namemap_validate_and_fix(Main *bmain, const bool do_fix)
+{
+ Set<Uniqueness_Key> id_names_libs;
+ bool is_valid = true;
+ ListBase *lb_iter;
+ FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb_iter) {
+ LISTBASE_FOREACH_MUTABLE (ID *, id_iter, lb_iter) {
+ Uniqueness_Key key;
+ BLI_strncpy(key.name, id_iter->name, MAX_ID_NAME);
+ key.lib = id_iter->lib;
+ if (!id_names_libs.add(key)) {
+ is_valid = false;
+ CLOG_ERROR(&LOG,
+ "ID name '%s' (from library '%s') is found more than once",
+ id_iter->name,
+ id_iter->lib != nullptr ? id_iter->lib->filepath : "<None>");
+ if (do_fix) {
+ /* NOTE: this may imply moving this ID in its listbase, however re-checking it later is
+ * not really an issue. */
+ BKE_id_new_name_validate(
+ bmain, which_libbase(bmain, GS(id_iter->name)), id_iter, nullptr, true);
+ BLI_strncpy(key.name, id_iter->name, MAX_ID_NAME);
+ if (!id_names_libs.add(key)) {
+ CLOG_ERROR(&LOG,
+ "\tID has been renamed to '%s', but it still seems to be already in use",
+ id_iter->name);
+ }
+ else {
+ CLOG_WARN(&LOG, "\tID has been renamed to '%s'", id_iter->name);
+ }
+ }
+ }
+
+ UniqueName_Map *name_map = get_namemap_for(bmain, id_iter, false);
+ if (name_map == nullptr) {
+ continue;
+ }
+ UniqueName_TypeMap *type_map = name_map->find_by_type(GS(id_iter->name));
+ BLI_assert(type_map != nullptr);
+
+ UniqueName_Key key_namemap;
+ /* Remove full name from the set. */
+ BLI_strncpy(key_namemap.name, id_iter->name + 2, MAX_NAME);
+ if (!type_map->full_names.contains(key_namemap)) {
+ is_valid = false;
+ CLOG_ERROR(&LOG,
+ "ID name '%s' (from library '%s') exists in current Main, but is not listed in "
+ "the namemap",
+ id_iter->name,
+ id_iter->lib != nullptr ? id_iter->lib->filepath : "<None>");
+ }
+ }
+ }
+ FOREACH_MAIN_LISTBASE_END;
+
+ Library *lib = nullptr;
+ UniqueName_Map *name_map = bmain->name_map;
+ do {
+ if (name_map != nullptr) {
+ int i = 0;
+ for (short idcode = BKE_idtype_idcode_iter_step(&i); idcode != 0;
+ idcode = BKE_idtype_idcode_iter_step(&i)) {
+ UniqueName_TypeMap *type_map = name_map->find_by_type(idcode);
+ if (type_map != nullptr) {
+ for (const UniqueName_Key &id_name : type_map->full_names) {
+ Uniqueness_Key key;
+ *(reinterpret_cast<short *>(key.name)) = idcode;
+ BLI_strncpy(key.name + 2, id_name.name, MAX_NAME);
+ key.lib = lib;
+ if (!id_names_libs.contains(key)) {
+ is_valid = false;
+ CLOG_ERROR(&LOG,
+ "ID name '%s' (from library '%s') is listed in the namemap, but does not "
+ "exists in current Main",
+ key.name,
+ lib != nullptr ? lib->filepath : "<None>");
+ }
+ }
+ }
+ }
+ }
+ lib = static_cast<Library *>((lib == nullptr) ? bmain->libraries.first : lib->id.next);
+ name_map = (lib != nullptr) ? lib->runtime.name_map : nullptr;
+ } while (lib != nullptr);
+
+ if (is_valid || !do_fix) {
+ return is_valid;
+ }
+
+ /* Clear all existing namemaps. */
+ lib = nullptr;
+ UniqueName_Map **name_map_p = &bmain->name_map;
+ do {
+ BLI_assert(name_map_p != nullptr);
+ if (*name_map_p != nullptr) {
+ BKE_main_namemap_destroy(name_map_p);
+ }
+ lib = static_cast<Library *>((lib == nullptr) ? bmain->libraries.first : lib->id.next);
+ name_map_p = (lib != nullptr) ? &lib->runtime.name_map : nullptr;
+ } while (lib != nullptr);
+
+ return is_valid;
+}
+
+bool BKE_main_namemap_validate_and_fix(Main *bmain)
+{
+ const bool is_valid = main_namemap_validate_and_fix(bmain, true);
+ BLI_assert(main_namemap_validate_and_fix(bmain, false));
+ return is_valid;
+}
+
+bool BKE_main_namemap_validate(Main *bmain)
+{
+ return main_namemap_validate_and_fix(bmain, false);
+}
diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c
index 42e65a95404..0b8351efdf4 100644
--- a/source/blender/blenkernel/intern/mask.c
+++ b/source/blender/blenkernel/intern/mask.c
@@ -249,7 +249,7 @@ IDTypeInfo IDType_ID_MSK = {
.foreach_id = mask_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = mask_blend_write,
.blend_read_data = mask_blend_read_data,
@@ -1579,7 +1579,7 @@ void BKE_mask_parent_init(MaskParent *parent)
parent->id_type = ID_MC;
}
-/* *** own animation/shapekey implementation ***
+/* *** own animation/shape-key implementation ***
* BKE_mask_layer_shape_XXX */
int BKE_mask_layer_shape_totvert(MaskLayer *masklay)
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index f899901b54e..3ea6dd3d735 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -102,6 +102,7 @@ static void material_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const
(ID **)&material_dst->nodetree,
flag_private_id_data);
}
+ material_dst->nodetree->owner_id = &material_dst->id;
}
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
@@ -255,7 +256,7 @@ IDTypeInfo IDType_ID_MA = {
.foreach_id = material_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = material_blend_write,
.blend_read_data = material_blend_read_data,
@@ -852,7 +853,7 @@ void BKE_object_material_resize(Main *bmain, Object *ob, const short totcol, boo
ob->mat = newmatar;
ob->matbits = newmatbits;
}
- /* XXX(campbell): why not realloc on shrink? */
+ /* XXX(@campbellbarton): why not realloc on shrink? */
ob->totcol = totcol;
if (ob->totcol && ob->actcol == 0) {
@@ -899,9 +900,15 @@ void BKE_objects_materials_test_all(Main *bmain, ID *id)
}
BKE_main_lock(bmain);
+ int processed_objects = 0;
for (ob = bmain->objects.first; ob; ob = ob->id.next) {
if (ob->data == id) {
BKE_object_material_resize(bmain, ob, *totcol, false);
+ processed_objects++;
+ BLI_assert(processed_objects <= id->us && processed_objects > 0);
+ if (processed_objects == id->us) {
+ break;
+ }
}
}
BKE_main_unlock(bmain);
@@ -1962,8 +1969,8 @@ static void material_default_surface_init(Material *ma)
{
strcpy(ma->id.name, "MADefault Surface");
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ NULL, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
ma->use_nodes = true;
bNode *principled = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_PRINCIPLED);
@@ -1990,8 +1997,8 @@ static void material_default_volume_init(Material *ma)
{
strcpy(ma->id.name, "MADefault Volume");
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ NULL, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
ma->use_nodes = true;
bNode *principled = nodeAddStaticNode(NULL, ntree, SH_NODE_VOLUME_PRINCIPLED);
@@ -2015,8 +2022,8 @@ static void material_default_holdout_init(Material *ma)
{
strcpy(ma->id.name, "MADefault Holdout");
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ NULL, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
ma->use_nodes = true;
bNode *holdout = nodeAddStaticNode(NULL, ntree, SH_NODE_HOLDOUT);
diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.cc
index 2a1c940493c..91797f8ed2f 100644
--- a/source/blender/blenkernel/intern/mball.c
+++ b/source/blender/blenkernel/intern/mball.cc
@@ -4,19 +4,16 @@
/** \file
* \ingroup bke
*
- * MetaBalls are created from a single Object (with a name without number in it),
- * here the DispList and BoundBox also is located.
+ * MetaBalls are created from a single Object (with a name without number in it).
* All objects with the same name (but with a number in it) are added to this.
- *
- * texture coordinates are patched within the displist
*/
-#include <ctype.h>
-#include <float.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cctype>
+#include <cfloat>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -25,6 +22,7 @@
#include "DNA_defaults.h"
#include "DNA_material_types.h"
+#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -41,11 +39,16 @@
#include "BKE_anim_data.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
+#include "BKE_geometry_set.hh"
#include "BKE_idtype.h"
+#include "BKE_lattice.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_material.h"
#include "BKE_mball.h"
+#include "BKE_mball_tessellate.h"
+#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_scene.h"
@@ -72,19 +75,16 @@ static void metaball_copy_data(Main *UNUSED(bmain),
BLI_duplicatelist(&metaball_dst->elems, &metaball_src->elems);
- metaball_dst->mat = MEM_dupallocN(metaball_src->mat);
+ metaball_dst->mat = static_cast<Material **>(MEM_dupallocN(metaball_src->mat));
- metaball_dst->editelems = NULL;
- metaball_dst->lastelem = NULL;
- metaball_dst->batch_cache = NULL;
+ metaball_dst->editelems = nullptr;
+ metaball_dst->lastelem = nullptr;
}
static void metaball_free_data(ID *id)
{
MetaBall *metaball = (MetaBall *)id;
- BKE_mball_batch_cache_free(metaball);
-
MEM_SAFE_FREE(metaball->mat);
BLI_freelistN(&metaball->elems);
@@ -107,11 +107,10 @@ static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_add
/* Clean up, important in undo case to reduce false detection of changed datablocks. */
BLI_listbase_clear(&mb->disp);
- mb->editelems = NULL;
+ mb->editelems = nullptr;
/* Must always be cleared (meta's don't have their own edit-data). */
mb->needs_flush_to_id = 0;
- mb->lastelem = NULL;
- mb->batch_cache = NULL;
+ mb->lastelem = nullptr;
/* write LibData */
BLO_write_id_struct(writer, MetaBall, id_address, &mb->id);
@@ -139,12 +138,11 @@ static void metaball_blend_read_data(BlendDataReader *reader, ID *id)
BLO_read_list(reader, &(mb->elems));
BLI_listbase_clear(&mb->disp);
- mb->editelems = NULL;
+ mb->editelems = nullptr;
/* Must always be cleared (meta's don't have their own edit-data). */
mb->needs_flush_to_id = 0;
- // mb->edit_elems.first = mb->edit_elems.last = NULL;
- mb->lastelem = NULL;
- mb->batch_cache = NULL;
+ // mb->edit_elems.first = mb->edit_elems.last = nullptr;
+ mb->lastelem = nullptr;
}
static void metaball_blend_read_lib(BlendLibReader *reader, ID *id)
@@ -166,49 +164,46 @@ static void metaball_blend_read_expand(BlendExpander *expander, ID *id)
}
IDTypeInfo IDType_ID_MB = {
- .id_code = ID_MB,
- .id_filter = FILTER_ID_MB,
- .main_listbase_index = INDEX_ID_MB,
- .struct_size = sizeof(MetaBall),
- .name = "Metaball",
- .name_plural = "metaballs",
- .translation_context = BLT_I18NCONTEXT_ID_METABALL,
- .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE,
- .asset_type_info = NULL,
-
- .init_data = metaball_init_data,
- .copy_data = metaball_copy_data,
- .free_data = metaball_free_data,
- .make_local = NULL,
- .foreach_id = metaball_foreach_id,
- .foreach_cache = NULL,
- .foreach_path = NULL,
- .owner_get = NULL,
-
- .blend_write = metaball_blend_write,
- .blend_read_data = metaball_blend_read_data,
- .blend_read_lib = metaball_blend_read_lib,
- .blend_read_expand = metaball_blend_read_expand,
-
- .blend_read_undo_preserve = NULL,
-
- .lib_override_apply_post = NULL,
+ /* id_code */ ID_MB,
+ /* id_filter */ FILTER_ID_MB,
+ /* main_listbase_index */ INDEX_ID_MB,
+ /* struct_size */ sizeof(MetaBall),
+ /* name */ "Metaball",
+ /* name_plural */ "metaballs",
+ /* translation_context */ BLT_I18NCONTEXT_ID_METABALL,
+ /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ metaball_init_data,
+ /* copy_data */ metaball_copy_data,
+ /* free_data */ metaball_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ metaball_foreach_id,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_pointer_get */ nullptr,
+
+ /* blend_write */ metaball_blend_write,
+ /* blend_read_data */ metaball_blend_read_data,
+ /* blend_read_lib */ metaball_blend_read_lib,
+ /* blend_read_expand */ metaball_blend_read_expand,
+
+ /* blend_read_undo_preserve */ nullptr,
+
+ /* lib_override_apply_post */ nullptr,
};
/* Functions */
MetaBall *BKE_mball_add(Main *bmain, const char *name)
{
- MetaBall *mb;
-
- mb = BKE_id_new(bmain, ID_MB, name);
-
+ MetaBall *mb = static_cast<MetaBall *>(BKE_id_new(bmain, ID_MB, name));
return mb;
}
MetaElem *BKE_mball_element_add(MetaBall *mb, const int type)
{
- MetaElem *ml = MEM_callocN(sizeof(MetaElem), "metaelem");
+ MetaElem *ml = MEM_cnew<MetaElem>(__func__);
unit_qt(ml->quat);
@@ -252,99 +247,37 @@ MetaElem *BKE_mball_element_add(MetaBall *mb, const int type)
return ml;
}
-void BKE_mball_texspace_calc(Object *ob)
-{
- DispList *dl;
- BoundBox *bb;
- float *data, min[3], max[3] /*, loc[3], size[3] */;
- int tot;
- bool do_it = false;
-
- if (ob->runtime.bb == NULL) {
- ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "mb boundbox");
- }
- bb = ob->runtime.bb;
-
- /* Weird one, this. */
- // INIT_MINMAX(min, max);
- (min)[0] = (min)[1] = (min)[2] = 1.0e30f;
- (max)[0] = (max)[1] = (max)[2] = -1.0e30f;
-
- dl = ob->runtime.curve_cache->disp.first;
- while (dl) {
- tot = dl->nr;
- if (tot) {
- do_it = true;
- }
- data = dl->verts;
- while (tot--) {
- /* Also weird... but longer. From utildefines. */
- minmax_v3v3_v3(min, max, data);
- data += 3;
- }
- dl = dl->next;
- }
-
- if (!do_it) {
- min[0] = min[1] = min[2] = -1.0f;
- max[0] = max[1] = max[2] = 1.0f;
- }
-
- BKE_boundbox_init_from_minmax(bb, min, max);
-
- bb->flag &= ~BOUNDBOX_DIRTY;
-}
BoundBox *BKE_mball_boundbox_get(Object *ob)
{
BLI_assert(ob->type == OB_MBALL);
-
- if (ob->runtime.bb != NULL && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
+ if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
return ob->runtime.bb;
}
-
- /* This should always only be called with evaluated objects,
- * but currently RNA is a problem here... */
- if (ob->runtime.curve_cache != NULL) {
- BKE_mball_texspace_calc(ob);
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
}
- return ob->runtime.bb;
-}
-
-float *BKE_mball_make_orco(Object *ob, ListBase *dispbase)
-{
- BoundBox *bb;
- DispList *dl;
- float *data, *orco, *orcodata;
- float loc[3], size[3];
- int a;
-
- /* restore size and loc */
- bb = ob->runtime.bb;
- loc[0] = (bb->vec[0][0] + bb->vec[4][0]) / 2.0f;
- size[0] = bb->vec[4][0] - loc[0];
- loc[1] = (bb->vec[0][1] + bb->vec[2][1]) / 2.0f;
- size[1] = bb->vec[2][1] - loc[1];
- loc[2] = (bb->vec[0][2] + bb->vec[1][2]) / 2.0f;
- size[2] = bb->vec[1][2] - loc[2];
-
- dl = dispbase->first;
- orcodata = MEM_mallocN(sizeof(float[3]) * dl->nr, "MballOrco");
-
- data = dl->verts;
- orco = orcodata;
- a = dl->nr;
- while (a--) {
- orco[0] = (data[0] - loc[0]) / size[0];
- orco[1] = (data[1] - loc[1]) / size[1];
- orco[2] = (data[2] - loc[2]) / size[2];
-
- data += 3;
- orco += 3;
+ /* Expect that this function is only called for evaluated objects. */
+ const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
+ float min[3];
+ float max[3];
+ if (mesh_eval) {
+ INIT_MINMAX(min, max);
+ if (!BKE_mesh_minmax(mesh_eval, min, max)) {
+ copy_v3_fl(min, -1.0f);
+ copy_v3_fl(max, 1.0f);
+ }
+ }
+ else {
+ copy_v3_fl(min, 0.0f);
+ copy_v3_fl(max, 0.0f);
}
- return orcodata;
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ ob->runtime.bb->flag &= ~BOUNDBOX_DIRTY;
+
+ return ob->runtime.bb;
}
bool BKE_mball_is_basis(const Object *ob)
@@ -393,7 +326,7 @@ bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2)
bool BKE_mball_is_any_selected(const MetaBall *mb)
{
- for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) {
+ LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) {
if (ml->flag & SELECT) {
return true;
}
@@ -415,7 +348,7 @@ bool BKE_mball_is_any_selected_multi(Base **bases, int bases_len)
bool BKE_mball_is_any_unselected(const MetaBall *mb)
{
- for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) {
+ LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) {
if ((ml->flag & SELECT) == 0) {
return true;
}
@@ -451,9 +384,10 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
* Solving this case would drastically increase the complexity of this code though, so don't
* think it would be worth it.
*/
- for (Object *ob_src = bmain->objects.first; ob_src != NULL && !ID_IS_LINKED(ob_src);) {
+ for (Object *ob_src = static_cast<Object *>(bmain->objects.first);
+ ob_src != nullptr && !ID_IS_LINKED(ob_src);) {
if (ob_src->data != metaball_src) {
- ob_src = ob_src->id.next;
+ ob_src = static_cast<Object *>(ob_src->id.next);
continue;
}
@@ -466,12 +400,13 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
* Using this, it is possible to process the whole set of meta-balls with a single loop on the
* whole list of Objects, though additionally going backward on part of the list in some cases.
*/
- Object *ob_iter = NULL;
+ Object *ob_iter = nullptr;
int obactive_nr, ob_nr;
char obactive_name[MAX_ID_NAME], ob_name[MAX_ID_NAME];
BLI_split_name_num(obactive_name, &obactive_nr, ob_src->id.name + 2, '.');
- for (ob_iter = ob_src->id.prev; ob_iter != NULL; ob_iter = ob_iter->id.prev) {
+ for (ob_iter = static_cast<Object *>(ob_src->id.prev); ob_iter != nullptr;
+ ob_iter = static_cast<Object *>(ob_iter->id.prev)) {
if (ob_iter->id.name[2] != obactive_name[0]) {
break;
}
@@ -483,10 +418,11 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
break;
}
- mball_data_properties_copy(ob_iter->data, metaball_src);
+ mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src);
}
- for (ob_iter = ob_src->id.next; ob_iter != NULL; ob_iter = ob_iter->id.next) {
+ for (ob_iter = static_cast<Object *>(ob_src->id.next); ob_iter != nullptr;
+ ob_iter = static_cast<Object *>(ob_iter->id.next)) {
if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) {
break;
}
@@ -498,7 +434,7 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src)
break;
}
- mball_data_properties_copy(ob_iter->data, metaball_src);
+ mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src);
}
ob_src = ob_iter;
@@ -514,7 +450,8 @@ Object *BKE_mball_basis_find(Scene *scene, Object *object)
BLI_split_name_num(basisname, &basisnr, object->id.name + 2, '.');
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
if ((ob->type == OB_MBALL) && !(base->flag & BASE_FROM_DUPLI)) {
if (ob != bob) {
@@ -556,7 +493,7 @@ bool BKE_mball_minmax_ex(
copy_v3_v3(centroid, &ml->x);
}
- /* TODO(campbell): non circle shapes cubes etc, probably nobody notices. */
+ /* TODO(@campbellbarton): non circle shapes cubes etc, probably nobody notices. */
for (int i = -1; i != 3; i += 2) {
copy_v3_v3(vec, centroid);
add_v3_fl(vec, scale_mb * i);
@@ -682,7 +619,7 @@ bool BKE_mball_select_all_multi_ex(Base **bases, int bases_len)
bool changed_multi = false;
for (uint ob_index = 0; ob_index < bases_len; ob_index++) {
Object *obedit = bases[ob_index]->object;
- MetaBall *mb = obedit->data;
+ MetaBall *mb = static_cast<MetaBall *>(obedit->data);
changed_multi |= BKE_mball_select_all(mb);
}
return changed_multi;
@@ -705,7 +642,7 @@ bool BKE_mball_deselect_all_multi_ex(Base **bases, int bases_len)
bool changed_multi = false;
for (uint ob_index = 0; ob_index < bases_len; ob_index++) {
Object *obedit = bases[ob_index]->object;
- MetaBall *mb = obedit->data;
+ MetaBall *mb = static_cast<MetaBall *>(obedit->data);
changed_multi |= BKE_mball_deselect_all(mb);
DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
}
@@ -735,20 +672,44 @@ bool BKE_mball_select_swap_multi_ex(Base **bases, int bases_len)
/* **** Depsgraph evaluation **** */
-/* Draw Engine */
+void BKE_mball_data_update(Depsgraph *depsgraph, Scene *scene, Object *ob)
+{
+ BLI_assert(ob->type == OB_MBALL);
-void (*BKE_mball_batch_cache_dirty_tag_cb)(MetaBall *mb, int mode) = NULL;
-void (*BKE_mball_batch_cache_free_cb)(MetaBall *mb) = NULL;
+ BKE_object_free_derived_caches(ob);
-void BKE_mball_batch_cache_dirty_tag(MetaBall *mb, int mode)
-{
- if (mb->batch_cache) {
- BKE_mball_batch_cache_dirty_tag_cb(mb, mode);
+ const Object *basis_object = BKE_mball_basis_find(scene, ob);
+ if (ob != basis_object) {
+ return;
}
-}
-void BKE_mball_batch_cache_free(MetaBall *mb)
-{
- if (mb->batch_cache) {
- BKE_mball_batch_cache_free_cb(mb);
+
+ Mesh *mesh = BKE_mball_polygonize(depsgraph, scene, ob);
+ if (mesh == nullptr) {
+ return;
}
-}
+
+ const MetaBall *mball = static_cast<MetaBall *>(ob->data);
+ mesh->mat = static_cast<Material **>(MEM_dupallocN(mball->mat));
+ mesh->totcol = mball->totcol;
+
+ if (ob->parent && ob->parent->type == OB_LATTICE && ob->partype == PARSKEL) {
+ int verts_num;
+ float(*positions)[3] = BKE_mesh_vert_coords_alloc(mesh, &verts_num);
+ BKE_lattice_deform_coords(ob->parent, ob, positions, verts_num, 0, nullptr, 1.0f);
+ BKE_mesh_vert_coords_apply(mesh, positions);
+ MEM_freeN(positions);
+ }
+
+ ob->runtime.geometry_set_eval = new GeometrySet(GeometrySet::create_with_mesh(mesh));
+
+ if (ob->runtime.bb == nullptr) {
+ ob->runtime.bb = MEM_cnew<BoundBox>(__func__);
+ }
+ blender::float3 min(std::numeric_limits<float>::max());
+ blender::float3 max(-std::numeric_limits<float>::max());
+ if (!ob->runtime.geometry_set_eval->compute_boundbox_without_instances(&min, &max)) {
+ min = blender::float3(0);
+ max = blender::float3(0);
+ }
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+};
diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c
index 54def0189b1..49963c333ec 100644
--- a/source/blender/blenkernel/intern/mball_tessellate.c
+++ b/source/blender/blenkernel/intern/mball_tessellate.c
@@ -14,6 +14,8 @@
#include "MEM_guardedalloc.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
@@ -24,10 +26,11 @@
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
-#include "BKE_global.h"
-
#include "BKE_displist.h"
+#include "BKE_global.h"
+#include "BKE_lib_id.h"
#include "BKE_mball_tessellate.h" /* own include */
+#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_scene.h"
@@ -427,8 +430,6 @@ static float metaball(PROCESS *process, float x, float y, float z)
*/
static void make_face(PROCESS *process, int i1, int i2, int i3, int i4)
{
- int *cur;
-
#ifdef USE_ACCUM_NORMAL
float n[3];
#endif
@@ -438,10 +439,9 @@ static void make_face(PROCESS *process, int i1, int i2, int i3, int i4)
process->indices = MEM_reallocN(process->indices, sizeof(int[4]) * process->totindex);
}
- cur = process->indices[process->curindex++];
-
- /* #DispList supports array drawing, treat tri's as fake quad. */
+ int *cur = process->indices[process->curindex++];
+ /* Treat triangles as fake quads. */
cur[0] = i1;
cur[1] = i2;
cur[2] = i3;
@@ -1371,7 +1371,7 @@ static void init_meta(Depsgraph *depsgraph, PROCESS *process, Scene *scene, Obje
}
}
-void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *dispbase)
+Mesh *BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
PROCESS process = {0};
const bool is_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER;
@@ -1394,10 +1394,10 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa
}
if (!is_render && (mb->flag == MB_UPDATE_NEVER)) {
- return;
+ return NULL;
}
if ((G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) && mb->flag == MB_UPDATE_FAST) {
- return;
+ return NULL;
}
if (is_render) {
@@ -1418,7 +1418,7 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa
init_meta(depsgraph, &process, scene, ob);
if (process.totelem == 0) {
freepolygonize(&process);
- return;
+ return NULL;
}
build_bvh_spatial(&process, &process.metaball_bvh, 0, process.totelem, &process.allbb);
@@ -1430,40 +1430,61 @@ void BKE_mball_polygonize(Depsgraph *depsgraph, Scene *scene, Object *ob, ListBa
ob->scale[1] < 0.00001f * (process.allbb.max[1] - process.allbb.min[1]) ||
ob->scale[2] < 0.00001f * (process.allbb.max[2] - process.allbb.min[2])) {
freepolygonize(&process);
- return;
+ return NULL;
}
polygonize(&process);
if (process.curindex == 0) {
freepolygonize(&process);
- return;
+ return NULL;
}
- /* add resulting surface to displist */
+ freepolygonize(&process);
- /* Avoid over-allocation since this is stored in the displist. */
- if (process.curindex != process.totindex) {
- process.indices = MEM_reallocN(process.indices, sizeof(int[4]) * process.curindex);
- }
- if (process.curvertex != process.totvertex) {
- process.co = MEM_reallocN(process.co, process.curvertex * sizeof(float[3]));
- process.no = MEM_reallocN(process.no, process.curvertex * sizeof(float[3]));
- }
+ Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)ob->data)->name + 2);
- DispList *dl = MEM_callocN(sizeof(DispList), "mballdisp");
- BLI_addtail(dispbase, dl);
- dl->type = DL_INDEX4;
- dl->nr = (int)process.curvertex;
- dl->parts = (int)process.curindex;
+ mesh->totvert = (int)process.curvertex;
+ MVert *mvert = CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CONSTRUCT, NULL, mesh->totvert);
+ for (int i = 0; i < mesh->totvert; i++) {
+ copy_v3_v3(mvert[i].co, process.co[i]);
+ mvert->flag = 0;
+ }
+ MEM_freeN(process.co);
+
+ mesh->totpoly = (int)process.curindex;
+ MPoly *mpoly = CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CONSTRUCT, NULL, mesh->totpoly);
+ MLoop *mloop = CustomData_add_layer(
+ &mesh->ldata, CD_MLOOP, CD_CONSTRUCT, NULL, mesh->totpoly * 4);
+
+ int loop_offset = 0;
+ for (int i = 0; i < mesh->totpoly; i++) {
+ const int *indices = process.indices[i];
+
+ const int count = indices[2] != indices[3] ? 4 : 3;
+ mpoly[i].loopstart = loop_offset;
+ mpoly[i].totloop = count;
+ mpoly[i].flag = ME_SMOOTH;
+
+ mloop[loop_offset].v = (uint32_t)indices[0];
+ mloop[loop_offset + 1].v = (uint32_t)indices[1];
+ mloop[loop_offset + 2].v = (uint32_t)indices[2];
+ if (count == 4) {
+ mloop[loop_offset + 3].v = (uint32_t)indices[3];
+ }
- dl->index = (int *)process.indices;
+ loop_offset += count;
+ }
+ MEM_freeN(process.indices);
- for (uint a = 0; a < process.curvertex; a++) {
- normalize_v3(process.no[a]);
+ for (int i = 0; i < mesh->totvert; i++) {
+ normalize_v3(process.no[i]);
}
+ mesh->runtime.vert_normals = process.no;
+ BKE_mesh_vertex_normals_clear_dirty(mesh);
- dl->verts = (float *)process.co;
- dl->nors = (float *)process.no;
+ mesh->totloop = loop_offset;
- freepolygonize(&process);
+ BKE_mesh_calc_edges(mesh, false, false);
+
+ return mesh;
}
diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc
index 2a14370bf93..7da9acc3cf6 100644
--- a/source/blender/blenkernel/intern/mesh.cc
+++ b/source/blender/blenkernel/intern/mesh.cc
@@ -17,7 +17,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
-#include "BLI_bitmap.h"
+#include "BLI_bit_vector.hh"
#include "BLI_edgehash.h"
#include "BLI_endian_switch.h"
#include "BLI_ghash.h"
@@ -28,14 +28,17 @@
#include "BLI_math.h"
#include "BLI_math_vector.hh"
#include "BLI_memarena.h"
+#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_task.hh"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
+#include "BLI_virtual_array.hh"
#include "BLT_translation.h"
#include "BKE_anim_data.h"
+#include "BKE_attribute.hh"
#include "BKE_bpath.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
@@ -61,7 +64,11 @@
#include "BLO_read_write.h"
+using blender::BitVector;
using blender::float3;
+using blender::MutableSpan;
+using blender::Span;
+using blender::VArray;
using blender::Vector;
static void mesh_clear_geometry(Mesh *mesh);
@@ -96,6 +103,10 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
const Mesh *mesh_src = (const Mesh *)id_src;
BKE_mesh_runtime_reset_on_copy(mesh_dst, flag);
+ /* Copy face dot tags, since meshes may be duplicated after a subsurf modifier
+ * or node, but we still need to be able to draw face center vertices. */
+ mesh_dst->runtime.subsurf_face_dot_tags = static_cast<uint32_t *>(
+ MEM_dupallocN(mesh_src->runtime.subsurf_face_dot_tags));
if ((mesh_src->id.tag & LIB_TAG_NO_MAIN) == 0) {
/* This is a direct copy of a main mesh, so for now it has the same topology. */
mesh_dst->runtime.deformed_only = true;
@@ -109,7 +120,7 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
*
* While this could be the callers responsibility, keep here since it's
* highly unlikely we want to create a duplicate and not use it for drawing. */
- mesh_dst->runtime.is_original = false;
+ mesh_dst->runtime.is_original_bmesh = false;
/* Only do tessface if we have no polys. */
const bool do_tessface = ((mesh_src->totface != 0) && (mesh_src->totpoly == 0));
@@ -137,8 +148,6 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
mesh_tessface_clear_intern(mesh_dst, false);
}
- BKE_mesh_update_customdata_pointers(mesh_dst, do_tessface);
-
mesh_dst->cd_flag = mesh_src->cd_flag;
mesh_dst->edit_mesh = nullptr;
@@ -208,6 +217,7 @@ static void mesh_foreach_path(ID *id, BPathForeachPathData *bpath_data)
static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
+ using namespace blender;
Mesh *mesh = (Mesh *)id;
const bool is_undo = BLO_write_is_undo(writer);
@@ -224,27 +234,39 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address
/* Do not store actual geometry data in case this is a library override ID. */
if (ID_IS_OVERRIDE_LIBRARY(mesh) && !is_undo) {
- mesh->mvert = nullptr;
mesh->totvert = 0;
memset(&mesh->vdata, 0, sizeof(mesh->vdata));
- mesh->medge = nullptr;
mesh->totedge = 0;
memset(&mesh->edata, 0, sizeof(mesh->edata));
- mesh->mloop = nullptr;
mesh->totloop = 0;
memset(&mesh->ldata, 0, sizeof(mesh->ldata));
- mesh->mpoly = nullptr;
mesh->totpoly = 0;
memset(&mesh->pdata, 0, sizeof(mesh->pdata));
}
else {
- CustomData_blend_write_prepare(mesh->vdata, vert_layers);
- CustomData_blend_write_prepare(mesh->edata, edge_layers);
- CustomData_blend_write_prepare(mesh->ldata, loop_layers);
- CustomData_blend_write_prepare(mesh->pdata, poly_layers);
+ Set<std::string> names_to_skip;
+ if (!BLO_write_is_undo(writer)) {
+ BKE_mesh_legacy_convert_hide_layers_to_flags(mesh);
+ BKE_mesh_legacy_convert_material_indices_to_mpoly(mesh);
+ BKE_mesh_legacy_bevel_weight_from_layers(mesh);
+ /* When converting to the old mesh format, don't save redundant attributes. */
+ names_to_skip.add_multiple_new({".hide_vert", ".hide_edge", ".hide_poly", "material_index"});
+
+ /* Set deprecated mesh data pointers for forward compatibility. */
+ mesh->mvert = const_cast<MVert *>(mesh->verts().data());
+ mesh->medge = const_cast<MEdge *>(mesh->edges().data());
+ mesh->mpoly = const_cast<MPoly *>(mesh->polys().data());
+ mesh->mloop = const_cast<MLoop *>(mesh->loops().data());
+ mesh->dvert = const_cast<MDeformVert *>(mesh->deform_verts().data());
+ }
+
+ CustomData_blend_write_prepare(mesh->vdata, vert_layers, names_to_skip);
+ CustomData_blend_write_prepare(mesh->edata, edge_layers, names_to_skip);
+ CustomData_blend_write_prepare(mesh->ldata, loop_layers, names_to_skip);
+ CustomData_blend_write_prepare(mesh->pdata, poly_layers, names_to_skip);
}
BLO_write_id_struct(writer, Mesh, id_address, &mesh->id);
@@ -277,26 +299,22 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
Mesh *mesh = (Mesh *)id;
BLO_read_pointer_array(reader, (void **)&mesh->mat);
+ /* Deprecated pointers to custom data layers are read here for backward compatibility
+ * with files where these were owning pointers rather than a view into custom data. */
BLO_read_data_address(reader, &mesh->mvert);
BLO_read_data_address(reader, &mesh->medge);
BLO_read_data_address(reader, &mesh->mface);
- BLO_read_data_address(reader, &mesh->mloop);
- BLO_read_data_address(reader, &mesh->mpoly);
- BLO_read_data_address(reader, &mesh->tface);
BLO_read_data_address(reader, &mesh->mtface);
- BLO_read_data_address(reader, &mesh->mcol);
BLO_read_data_address(reader, &mesh->dvert);
- BLO_read_data_address(reader, &mesh->mloopcol);
- BLO_read_data_address(reader, &mesh->mloopuv);
+ BLO_read_data_address(reader, &mesh->tface);
+ BLO_read_data_address(reader, &mesh->mcol);
+
BLO_read_data_address(reader, &mesh->mselect);
/* animdata */
BLO_read_data_address(reader, &mesh->adt);
BKE_animdata_blend_read_data(reader, mesh->adt);
- /* Normally BKE_defvert_blend_read should be called in CustomData_blend_read,
- * but for backwards compatibility in do_versions to work we do it here. */
- BKE_defvert_blend_read(reader, mesh->totvert, mesh->dvert);
BLO_read_list(reader, &mesh->vertex_group_names);
CustomData_blend_read(reader, &mesh->vdata, mesh->totvert);
@@ -304,6 +322,11 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id)
CustomData_blend_read(reader, &mesh->fdata, mesh->totface);
CustomData_blend_read(reader, &mesh->ldata, mesh->totloop);
CustomData_blend_read(reader, &mesh->pdata, mesh->totpoly);
+ if (mesh->deform_verts().is_empty()) {
+ /* Vertex group data was also an owning pointer in old Blender versions.
+ * Don't read them again if they were read as part of #CustomData. */
+ BKE_defvert_blend_read(reader, mesh->totvert, mesh->dvert);
+ }
mesh->texflag &= ~ME_AUTOSPACE_EVALUATED;
mesh->edit_mesh = nullptr;
@@ -375,7 +398,7 @@ IDTypeInfo IDType_ID_ME = {
/* foreach_id */ mesh_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ mesh_foreach_path,
- /* owner_get */ nullptr,
+ /* owner_pointer_get */ nullptr,
/* blend_write */ mesh_blend_write,
/* blend_read_data */ mesh_blend_read_data,
@@ -445,6 +468,8 @@ static int customdata_compare(
CD_MASK_MLOOPUV | CD_MASK_PROP_BYTE_COLOR |
CD_MASK_MDEFORMVERT;
const uint64_t cd_mask_all_attr = CD_MASK_PROP_ALL | cd_mask_non_generic;
+ const Span<MLoop> loops_1 = m1->loops();
+ const Span<MLoop> loops_2 = m2->loops();
for (int i = 0; i < c1->totlayer; i++) {
l1 = &c1->layers[i];
@@ -461,7 +486,8 @@ static int customdata_compare(
}
if (layer_count1 != layer_count2) {
- return MESHCMP_CDLAYERS_MISMATCH;
+ /* TODO(@HooglyBoogly): Reenable after tests are updated for material index refactor. */
+ // return MESHCMP_CDLAYERS_MISMATCH;
}
l1 = c1->layers;
@@ -519,15 +545,14 @@ static int customdata_compare(
int ptot = m1->totpoly;
for (j = 0; j < ptot; j++, p1++, p2++) {
- MLoop *lp1, *lp2;
int k;
if (p1->totloop != p2->totloop) {
return MESHCMP_POLYMISMATCH;
}
- lp1 = m1->mloop + p1->loopstart;
- lp2 = m2->mloop + p2->loopstart;
+ const MLoop *lp1 = &loops_1[p1->loopstart];
+ const MLoop *lp2 = &loops_2[p2->loopstart];
for (k = 0; k < p1->totloop; k++, lp1++, lp2++) {
if (lp1->v != lp2->v) {
@@ -738,47 +763,6 @@ const char *BKE_mesh_cmp(Mesh *me1, Mesh *me2, float thresh)
return nullptr;
}
-static void mesh_ensure_tessellation_customdata(Mesh *me)
-{
- if (UNLIKELY((me->totface != 0) && (me->totpoly == 0))) {
- /* Pass, otherwise this function clears 'mface' before
- * versioning 'mface -> mpoly' code kicks in T30583.
- *
- * Callers could also check but safer to do here - campbell */
- }
- else {
- const int tottex_original = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV);
- const int totcol_original = CustomData_number_of_layers(&me->ldata, CD_PROP_BYTE_COLOR);
-
- const int tottex_tessface = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
- const int totcol_tessface = CustomData_number_of_layers(&me->fdata, CD_MCOL);
-
- if (tottex_tessface != tottex_original || totcol_tessface != totcol_original) {
- BKE_mesh_tessface_clear(me);
-
- BKE_mesh_add_mface_layers(&me->fdata, &me->ldata, me->totface);
-
- /* TODO: add some `--debug-mesh` option. */
- if (G.debug & G_DEBUG) {
- /* NOTE(campbell): this warning may be un-called for if we are initializing the mesh for
- * the first time from #BMesh, rather than giving a warning about this we could be smarter
- * and check if there was any data to begin with, for now just print the warning with
- * some info to help troubleshoot what's going on. */
- printf(
- "%s: warning! Tessellation uvs or vcol data got out of sync, "
- "had to reset!\n CD_MTFACE: %d != CD_MLOOPUV: %d || CD_MCOL: %d != "
- "CD_PROP_BYTE_COLOR: "
- "%d\n",
- __func__,
- tottex_tessface,
- tottex_original,
- totcol_tessface,
- totcol_original);
- }
- }
- }
-}
-
void BKE_mesh_ensure_skin_customdata(Mesh *me)
{
BMesh *bm = me->edit_mesh ? me->edit_mesh->bm : nullptr;
@@ -802,7 +786,7 @@ void BKE_mesh_ensure_skin_customdata(Mesh *me)
else {
if (!CustomData_has_layer(&me->vdata, CD_MVERT_SKIN)) {
vs = (MVertSkin *)CustomData_add_layer(
- &me->vdata, CD_MVERT_SKIN, CD_DEFAULT, nullptr, me->totvert);
+ &me->vdata, CD_MVERT_SKIN, CD_SET_DEFAULT, nullptr, me->totvert);
/* Mark an arbitrary vertex as root */
if (vs) {
@@ -824,7 +808,7 @@ bool BKE_mesh_ensure_facemap_customdata(struct Mesh *me)
}
else {
if (!CustomData_has_layer(&me->pdata, CD_FACEMAP)) {
- CustomData_add_layer(&me->pdata, CD_FACEMAP, CD_DEFAULT, nullptr, me->totpoly);
+ CustomData_add_layer(&me->pdata, CD_FACEMAP, CD_SET_DEFAULT, nullptr, me->totpoly);
changed = true;
}
}
@@ -850,43 +834,6 @@ bool BKE_mesh_clear_facemap_customdata(struct Mesh *me)
return changed;
}
-/**
- * This ensures grouped custom-data (e.g. #CD_MLOOPUV and #CD_MTFACE, or
- * #CD_PROP_BYTE_COLOR and #CD_MCOL) have the same relative active/render/clone/mask indices.
- *
- * NOTE(@campbellbarton): that for undo mesh data we want to skip 'ensure_tess_cd' call since
- * we don't want to store memory for #MFace data when its only used for older
- * versions of the mesh.
- */
-static void mesh_update_linked_customdata(Mesh *me, const bool do_ensure_tess_cd)
-{
- if (do_ensure_tess_cd) {
- mesh_ensure_tessellation_customdata(me);
- }
-
- CustomData_bmesh_update_active_layers(&me->fdata, &me->ldata);
-}
-
-void BKE_mesh_update_customdata_pointers(Mesh *me, const bool do_ensure_tess_cd)
-{
- mesh_update_linked_customdata(me, do_ensure_tess_cd);
-
- me->mvert = (MVert *)CustomData_get_layer(&me->vdata, CD_MVERT);
- me->dvert = (MDeformVert *)CustomData_get_layer(&me->vdata, CD_MDEFORMVERT);
-
- me->medge = (MEdge *)CustomData_get_layer(&me->edata, CD_MEDGE);
-
- me->mface = (MFace *)CustomData_get_layer(&me->fdata, CD_MFACE);
- me->mcol = (MCol *)CustomData_get_layer(&me->fdata, CD_MCOL);
- me->mtface = (MTFace *)CustomData_get_layer(&me->fdata, CD_MTFACE);
-
- me->mpoly = (MPoly *)CustomData_get_layer(&me->pdata, CD_MPOLY);
- me->mloop = (MLoop *)CustomData_get_layer(&me->ldata, CD_MLOOP);
-
- me->mloopcol = (MLoopCol *)CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR);
- me->mloopuv = (MLoopUV *)CustomData_get_layer(&me->ldata, CD_MLOOPUV);
-}
-
bool BKE_mesh_has_custom_loop_normals(Mesh *me)
{
if (me->edit_mesh) {
@@ -931,12 +878,11 @@ static void mesh_clear_geometry(Mesh *mesh)
mesh->act_face = -1;
mesh->totselect = 0;
- BKE_mesh_update_customdata_pointers(mesh, false);
+ BLI_freelistN(&mesh->vertex_group_names);
}
void BKE_mesh_clear_geometry(Mesh *mesh)
{
- BKE_animdata_free(&mesh->id, false);
BKE_mesh_runtime_clear_cache(mesh);
mesh_clear_geometry(mesh);
}
@@ -950,9 +896,6 @@ static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata)
CustomData_reset(&mesh->fdata);
}
- mesh->mface = nullptr;
- mesh->mtface = nullptr;
- mesh->mcol = nullptr;
mesh->totface = 0;
}
@@ -967,20 +910,20 @@ Mesh *BKE_mesh_add(Main *bmain, const char *name)
static void mesh_ensure_cdlayers_primary(Mesh *mesh, bool do_tessface)
{
if (!CustomData_get_layer(&mesh->vdata, CD_MVERT)) {
- CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, nullptr, mesh->totvert);
+ CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, mesh->totvert);
}
if (!CustomData_get_layer(&mesh->edata, CD_MEDGE)) {
- CustomData_add_layer(&mesh->edata, CD_MEDGE, CD_CALLOC, nullptr, mesh->totedge);
+ CustomData_add_layer(&mesh->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, mesh->totedge);
}
if (!CustomData_get_layer(&mesh->ldata, CD_MLOOP)) {
- CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, nullptr, mesh->totloop);
+ CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, mesh->totloop);
}
if (!CustomData_get_layer(&mesh->pdata, CD_MPOLY)) {
- CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, nullptr, mesh->totpoly);
+ CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, mesh->totpoly);
}
if (do_tessface && !CustomData_get_layer(&mesh->fdata, CD_MFACE)) {
- CustomData_add_layer(&mesh->fdata, CD_MFACE, CD_CALLOC, nullptr, mesh->totface);
+ CustomData_add_layer(&mesh->fdata, CD_MFACE, CD_SET_DEFAULT, nullptr, mesh->totface);
}
}
@@ -1005,7 +948,6 @@ Mesh *BKE_mesh_new_nomain(
mesh->totpoly = polys_len;
mesh_ensure_cdlayers_primary(mesh, true);
- BKE_mesh_update_customdata_pointers(mesh, false);
return mesh;
}
@@ -1030,6 +972,7 @@ void BKE_mesh_copy_parameters(Mesh *me_dst, const Mesh *me_src)
copy_v3_v3(me_dst->size, me_src->size);
me_dst->vertex_group_active_index = me_src->vertex_group_active_index;
+ me_dst->attributes_active_index = me_src->attributes_active_index;
}
void BKE_mesh_copy_parameters_for_eval(Mesh *me_dst, const Mesh *me_src)
@@ -1077,12 +1020,12 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src,
me_dst->cd_flag = me_src->cd_flag;
BKE_mesh_copy_parameters_for_eval(me_dst, me_src);
- CustomData_copy(&me_src->vdata, &me_dst->vdata, mask.vmask, CD_CALLOC, verts_len);
- CustomData_copy(&me_src->edata, &me_dst->edata, mask.emask, CD_CALLOC, edges_len);
- CustomData_copy(&me_src->ldata, &me_dst->ldata, mask.lmask, CD_CALLOC, loops_len);
- CustomData_copy(&me_src->pdata, &me_dst->pdata, mask.pmask, CD_CALLOC, polys_len);
+ CustomData_copy(&me_src->vdata, &me_dst->vdata, mask.vmask, CD_SET_DEFAULT, verts_len);
+ CustomData_copy(&me_src->edata, &me_dst->edata, mask.emask, CD_SET_DEFAULT, edges_len);
+ CustomData_copy(&me_src->ldata, &me_dst->ldata, mask.lmask, CD_SET_DEFAULT, loops_len);
+ CustomData_copy(&me_src->pdata, &me_dst->pdata, mask.pmask, CD_SET_DEFAULT, polys_len);
if (do_tessface) {
- CustomData_copy(&me_src->fdata, &me_dst->fdata, mask.fmask, CD_CALLOC, tessface_len);
+ CustomData_copy(&me_src->fdata, &me_dst->fdata, mask.fmask, CD_SET_DEFAULT, tessface_len);
}
else {
mesh_tessface_clear_intern(me_dst, false);
@@ -1091,7 +1034,6 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src,
/* The destination mesh should at least have valid primary CD layers,
* even in cases where the source mesh does not. */
mesh_ensure_cdlayers_primary(me_dst, do_tessface);
- BKE_mesh_update_customdata_pointers(me_dst, false);
/* Expect that normals aren't copied at all, since the destination mesh is new. */
BLI_assert(BKE_mesh_vertex_normals_are_dirty(me_dst));
@@ -1183,13 +1125,18 @@ static void ensure_orig_index_layer(CustomData &data, const int size)
if (CustomData_has_layer(&data, CD_ORIGINDEX)) {
return;
}
- int *indices = (int *)CustomData_add_layer(&data, CD_ORIGINDEX, CD_DEFAULT, nullptr, size);
+ int *indices = (int *)CustomData_add_layer(&data, CD_ORIGINDEX, CD_SET_DEFAULT, nullptr, size);
range_vn_i(indices, size, 0);
}
void BKE_mesh_ensure_default_orig_index_customdata(Mesh *mesh)
{
BLI_assert(mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA);
+ BKE_mesh_ensure_default_orig_index_customdata_no_check(mesh);
+}
+
+void BKE_mesh_ensure_default_orig_index_customdata_no_check(Mesh *mesh)
+{
ensure_orig_index_layer(mesh->vdata, mesh->totvert);
ensure_orig_index_layer(mesh->edata, mesh->totedge);
ensure_orig_index_layer(mesh->pdata, mesh->totpoly);
@@ -1309,11 +1256,12 @@ float (*BKE_mesh_orco_verts_get(Object *ob))[3]
/* Get appropriate vertex coordinates */
float(*vcos)[3] = (float(*)[3])MEM_calloc_arrayN(me->totvert, sizeof(*vcos), "orco mesh");
- MVert *mvert = tme->mvert;
+ const Span<MVert> verts = tme->verts();
+
int totvert = min_ii(tme->totvert, me->totvert);
- for (int a = 0; a < totvert; a++, mvert++) {
- copy_v3_v3(vcos[a], mvert->co);
+ for (int a = 0; a < totvert; a++) {
+ copy_v3_v3(vcos[a], verts[a].co);
}
return vcos;
@@ -1391,61 +1339,57 @@ void BKE_mesh_assign_object(Main *bmain, Object *ob, Mesh *me)
void BKE_mesh_material_index_remove(Mesh *me, short index)
{
- MPoly *mp;
- MFace *mf;
- int i;
-
- for (mp = me->mpoly, i = 0; i < me->totpoly; i++, mp++) {
- if (mp->mat_nr && mp->mat_nr >= index) {
- mp->mat_nr--;
- }
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = me->attributes_for_write();
+ AttributeWriter<int> material_indices = attributes.lookup_for_write<int>("material_index");
+ if (!material_indices) {
+ return;
}
-
- for (mf = me->mface, i = 0; i < me->totface; i++, mf++) {
- if (mf->mat_nr && mf->mat_nr >= index) {
- mf->mat_nr--;
+ if (material_indices.domain != ATTR_DOMAIN_FACE) {
+ BLI_assert_unreachable();
+ return;
+ }
+ MutableVArraySpan<int> indices_span(material_indices.varray);
+ for (const int i : indices_span.index_range()) {
+ if (indices_span[i] > 0 && indices_span[i] > index) {
+ indices_span[i]--;
}
}
+ indices_span.save();
+ material_indices.finish();
+
+ BKE_mesh_tessface_clear(me);
}
bool BKE_mesh_material_index_used(Mesh *me, short index)
{
- MPoly *mp;
- MFace *mf;
- int i;
-
- for (mp = me->mpoly, i = 0; i < me->totpoly; i++, mp++) {
- if (mp->mat_nr == index) {
- return true;
- }
- }
-
- for (mf = me->mface, i = 0; i < me->totface; i++, mf++) {
- if (mf->mat_nr == index) {
- return true;
- }
+ using namespace blender;
+ using namespace blender::bke;
+ const AttributeAccessor attributes = me->attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ if (material_indices.is_single()) {
+ return material_indices.get_internal_single() == index;
}
-
- return false;
+ const VArraySpan<int> indices_span(material_indices);
+ return indices_span.contains(index);
}
void BKE_mesh_material_index_clear(Mesh *me)
{
- MPoly *mp;
- MFace *mf;
- int i;
-
- for (mp = me->mpoly, i = 0; i < me->totpoly; i++, mp++) {
- mp->mat_nr = 0;
- }
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = me->attributes_for_write();
+ attributes.remove("material_index");
- for (mf = me->mface, i = 0; i < me->totface; i++, mf++) {
- mf->mat_nr = 0;
- }
+ BKE_mesh_tessface_clear(me);
}
void BKE_mesh_material_remap(Mesh *me, const uint *remap, uint remap_len)
{
+ using namespace blender;
+ using namespace blender::bke;
const short remap_len_short = (short)remap_len;
#define MAT_NR_REMAP(n) \
@@ -1465,10 +1409,17 @@ void BKE_mesh_material_remap(Mesh *me, const uint *remap, uint remap_len)
}
}
else {
- int i;
- for (i = 0; i < me->totpoly; i++) {
- MAT_NR_REMAP(me->mpoly[i].mat_nr);
+ MutableAttributeAccessor attributes = me->attributes_for_write();
+ SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
+ "material_index", ATTR_DOMAIN_FACE);
+ if (!material_indices) {
+ return;
}
+ for (const int i : material_indices.span.index_range()) {
+ MAT_NR_REMAP(material_indices.span[i]);
+ }
+ material_indices.span.save();
+ material_indices.finish();
}
#undef MAT_NR_REMAP
@@ -1476,14 +1427,15 @@ void BKE_mesh_material_remap(Mesh *me, const uint *remap, uint remap_len)
void BKE_mesh_smooth_flag_set(Mesh *me, const bool use_smooth)
{
+ MutableSpan<MPoly> polys = me->polys_for_write();
if (use_smooth) {
- for (int i = 0; i < me->totpoly; i++) {
- me->mpoly[i].flag |= ME_SMOOTH;
+ for (MPoly &poly : polys) {
+ poly.flag |= ME_SMOOTH;
}
}
else {
- for (int i = 0; i < me->totpoly; i++) {
- me->mpoly[i].flag &= ~ME_SMOOTH;
+ for (MPoly &poly : polys) {
+ poly.flag &= ~ME_SMOOTH;
}
}
}
@@ -1539,9 +1491,12 @@ int BKE_mesh_edge_other_vert(const MEdge *e, int v)
void BKE_mesh_looptri_get_real_edges(const Mesh *mesh, const MLoopTri *looptri, int r_edges[3])
{
+ const Span<MEdge> edges = mesh->edges();
+ const Span<MLoop> loops = mesh->loops();
+
for (int i = 2, i_next = 0; i_next < 3; i = i_next++) {
- const MLoop *l1 = &mesh->mloop[looptri->tri[i]], *l2 = &mesh->mloop[looptri->tri[i_next]];
- const MEdge *e = &mesh->medge[l1->e];
+ const MLoop *l1 = &loops[looptri->tri[i]], *l2 = &loops[looptri->tri[i_next]];
+ const MEdge *e = &edges[l1->e];
bool is_real = (l1->v == e->v1 && l2->v == e->v2) || (l1->v == e->v2 && l2->v == e->v1);
@@ -1560,15 +1515,16 @@ bool BKE_mesh_minmax(const Mesh *me, float r_min[3], float r_max[3])
float3 min;
float3 max;
};
+ const Span<MVert> verts = me->verts();
const Result minmax = threading::parallel_reduce(
- IndexRange(me->totvert),
+ verts.index_range(),
1024,
Result{float3(FLT_MAX), float3(-FLT_MAX)},
- [&](IndexRange range, const Result &init) {
+ [verts](IndexRange range, const Result &init) {
Result result = init;
for (const int i : range) {
- math::min_max(float3(me->mvert[i].co), result.min, result.max);
+ math::min_max(float3(verts[i].co), result.min, result.max);
}
return result;
},
@@ -1584,22 +1540,16 @@ bool BKE_mesh_minmax(const Mesh *me, float r_min[3], float r_max[3])
void BKE_mesh_transform(Mesh *me, const float mat[4][4], bool do_keys)
{
- int i;
- MVert *mvert = (MVert *)CustomData_duplicate_referenced_layer(&me->vdata, CD_MVERT, me->totvert);
- float(*lnors)[3] = (float(*)[3])CustomData_duplicate_referenced_layer(
- &me->ldata, CD_NORMAL, me->totloop);
+ MutableSpan<MVert> verts = me->verts_for_write();
- /* If the referenced layer has been re-allocated need to update pointers stored in the mesh. */
- BKE_mesh_update_customdata_pointers(me, false);
-
- for (i = 0; i < me->totvert; i++, mvert++) {
- mul_m4_v3(mat, mvert->co);
+ for (MVert &vert : verts) {
+ mul_m4_v3(mat, vert.co);
}
if (do_keys && me->key) {
LISTBASE_FOREACH (KeyBlock *, kb, &me->key->block) {
float *fp = (float *)kb->data;
- for (i = kb->totelem; i--; fp += 3) {
+ for (int i = kb->totelem; i--; fp += 3) {
mul_m4_v3(mat, fp);
}
}
@@ -1608,12 +1558,14 @@ void BKE_mesh_transform(Mesh *me, const float mat[4][4], bool do_keys)
/* don't update normals, caller can do this explicitly.
* We do update loop normals though, those may not be auto-generated
* (see e.g. STL import script)! */
+ float(*lnors)[3] = (float(*)[3])CustomData_duplicate_referenced_layer(
+ &me->ldata, CD_NORMAL, me->totloop);
if (lnors) {
float m3[3][3];
copy_m3_m4(m3, mat);
normalize_m3(m3);
- for (i = 0; i < me->totloop; i++, lnors++) {
+ for (int i = 0; i < me->totloop; i++, lnors++) {
mul_m3_v3(m3, *lnors);
}
}
@@ -1622,15 +1574,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)
{
- 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 *mvert = me->mvert; i--; mvert++) {
- add_v3_v3(mvert->co, offset);
+ MutableSpan<MVert> verts = me->verts_for_write();
+ for (MVert &vert : verts) {
+ add_v3_v3(vert.co, offset);
}
+ int i;
if (do_keys && me->key) {
LISTBASE_FOREACH (KeyBlock *, kb, &me->key->block) {
float *fp = (float *)kb->data;
@@ -1653,25 +1602,24 @@ void BKE_mesh_do_versions_cd_flag_init(Mesh *mesh)
return;
}
- MVert *mv;
- MEdge *med;
- int i;
+ const Span<MVert> verts = mesh->verts();
+ const Span<MEdge> edges = mesh->edges();
- for (mv = mesh->mvert, i = 0; i < mesh->totvert; mv++, i++) {
- if (mv->bweight != 0) {
+ for (const MVert &vert : verts) {
+ if (vert.bweight_legacy != 0) {
mesh->cd_flag |= ME_CDFLAG_VERT_BWEIGHT;
break;
}
}
- for (med = mesh->medge, i = 0; i < mesh->totedge; med++, i++) {
- if (med->bweight != 0) {
+ for (const MEdge &edge : edges) {
+ if (edge.bweight_legacy != 0) {
mesh->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
if (mesh->cd_flag & ME_CDFLAG_EDGE_CREASE) {
break;
}
}
- if (med->crease != 0) {
+ if (edge.crease != 0) {
mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) {
break;
@@ -1697,6 +1645,9 @@ void BKE_mesh_mselect_validate(Mesh *me)
if (me->totselect == 0) {
return;
}
+ const Span<MVert> verts = me->verts();
+ const Span<MEdge> edges = me->edges();
+ const Span<MPoly> polys = me->polys();
mselect_src = me->mselect;
mselect_dst = (MSelect *)MEM_malloc_arrayN(
@@ -1706,21 +1657,21 @@ void BKE_mesh_mselect_validate(Mesh *me)
int index = mselect_src[i_src].index;
switch (mselect_src[i_src].type) {
case ME_VSEL: {
- if (me->mvert[index].flag & SELECT) {
+ if (verts[index].flag & SELECT) {
mselect_dst[i_dst] = mselect_src[i_src];
i_dst++;
}
break;
}
case ME_ESEL: {
- if (me->medge[index].flag & SELECT) {
+ if (edges[index].flag & SELECT) {
mselect_dst[i_dst] = mselect_src[i_src];
i_dst++;
}
break;
}
case ME_FSEL: {
- if (me->mpoly[index].flag & SELECT) {
+ if (polys[index].flag & SELECT) {
mselect_dst[i_dst] = mselect_src[i_src];
i_dst++;
}
@@ -1806,10 +1757,10 @@ void BKE_mesh_count_selected_items(const Mesh *mesh, int r_count[3])
void BKE_mesh_vert_coords_get(const Mesh *mesh, float (*vert_coords)[3])
{
- const MVert *mv = mesh->mvert;
- for (int i = 0; i < mesh->totvert; i++, mv++) {
- copy_v3_v3(vert_coords[i], mv->co);
- }
+ blender::bke::AttributeAccessor attributes = mesh->attributes();
+ VArray<float3> positions = attributes.lookup_or_default(
+ "position", ATTR_DOMAIN_POINT, float3(0));
+ positions.materialize({(float3 *)vert_coords, mesh->totvert});
}
float (*BKE_mesh_vert_coords_alloc(const Mesh *mesh, int *r_vert_len))[3]
@@ -1824,12 +1775,9 @@ float (*BKE_mesh_vert_coords_alloc(const Mesh *mesh, int *r_vert_len))[3]
void BKE_mesh_vert_coords_apply(Mesh *mesh, const float (*vert_coords)[3])
{
- /* This will just return the pointer if it wasn't a referenced layer. */
- MVert *mv = (MVert *)CustomData_duplicate_referenced_layer(
- &mesh->vdata, CD_MVERT, mesh->totvert);
- mesh->mvert = mv;
- for (int i = 0; i < mesh->totvert; i++, mv++) {
- copy_v3_v3(mv->co, vert_coords[i]);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ for (const int i : verts.index_range()) {
+ copy_v3_v3(verts[i].co, vert_coords[i]);
}
BKE_mesh_tag_coords_changed(mesh);
}
@@ -1838,12 +1786,9 @@ void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh,
const float (*vert_coords)[3],
const float mat[4][4])
{
- /* This will just return the pointer if it wasn't a referenced layer. */
- MVert *mv = (MVert *)CustomData_duplicate_referenced_layer(
- &mesh->vdata, CD_MVERT, mesh->totvert);
- mesh->mvert = mv;
- for (int i = 0; i < mesh->totvert; i++, mv++) {
- mul_v3_m4v3(mv->co, mat, vert_coords[i]);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ for (const int i : verts.index_range()) {
+ mul_v3_m4v3(verts[i].co, mat, vert_coords[i]);
}
BKE_mesh_tag_coords_changed(mesh);
}
@@ -1857,7 +1802,7 @@ static float (*ensure_corner_normal_layer(Mesh &mesh))[3]
}
else {
r_loopnors = (float(*)[3])CustomData_add_layer(
- &mesh.ldata, CD_NORMAL, CD_CALLOC, nullptr, mesh.totloop);
+ &mesh.ldata, CD_NORMAL, CD_SET_DEFAULT, nullptr, mesh.totloop);
CustomData_set_layer_flag(&mesh.ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
}
return r_loopnors;
@@ -1879,17 +1824,22 @@ void BKE_mesh_calc_normals_split_ex(Mesh *mesh,
/* may be nullptr */
clnors = (short(*)[2])CustomData_get_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL);
- BKE_mesh_normals_loop_split(mesh->mvert,
+ const Span<MVert> verts = mesh->verts();
+ const Span<MEdge> edges = mesh->edges();
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
+
+ BKE_mesh_normals_loop_split(verts.data(),
BKE_mesh_vertex_normals_ensure(mesh),
- mesh->totvert,
- mesh->medge,
- mesh->totedge,
- mesh->mloop,
+ verts.size(),
+ edges.data(),
+ edges.size(),
+ loops.data(),
r_corner_normals,
- mesh->totloop,
- mesh->mpoly,
+ loops.size(),
+ polys.data(),
BKE_mesh_poly_normals_ensure(mesh),
- mesh->totpoly,
+ polys.size(),
use_split_normals,
split_angle,
r_lnors_spacearr,
@@ -1935,22 +1885,22 @@ static int split_faces_prepare_new_verts(Mesh *mesh,
const int loops_len = mesh->totloop;
int verts_len = mesh->totvert;
- MLoop *mloop = mesh->mloop;
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
BKE_mesh_vertex_normals_ensure(mesh);
float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(mesh);
- BLI_bitmap *verts_used = BLI_BITMAP_NEW(verts_len, __func__);
- BLI_bitmap *done_loops = BLI_BITMAP_NEW(loops_len, __func__);
+ BitVector<> verts_used(verts_len, false);
+ BitVector<> done_loops(loops_len, false);
- MLoop *ml = mloop;
+ MLoop *ml = loops.data();
MLoopNorSpace **lnor_space = lnors_spacearr->lspacearr;
BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_LOOP_INDEX);
for (int loop_idx = 0; loop_idx < loops_len; loop_idx++, ml++, lnor_space++) {
- if (!BLI_BITMAP_TEST(done_loops, loop_idx)) {
+ if (!done_loops[loop_idx]) {
const int vert_idx = ml->v;
- const bool vert_used = BLI_BITMAP_TEST_BOOL(verts_used, vert_idx);
+ const bool vert_used = verts_used[vert_idx];
/* If vert is already used by another smooth fan, we need a new vert for this one. */
const int new_vert_idx = vert_used ? verts_len++ : vert_idx;
@@ -1959,7 +1909,7 @@ static int split_faces_prepare_new_verts(Mesh *mesh,
if ((*lnor_space)->flags & MLNOR_SPACE_IS_SINGLE) {
/* Single loop in this fan... */
BLI_assert(POINTER_AS_INT((*lnor_space)->loops) == loop_idx);
- BLI_BITMAP_ENABLE(done_loops, loop_idx);
+ done_loops[loop_idx].set();
if (vert_used) {
ml->v = new_vert_idx;
}
@@ -1967,15 +1917,15 @@ static int split_faces_prepare_new_verts(Mesh *mesh,
else {
for (LinkNode *lnode = (*lnor_space)->loops; lnode; lnode = lnode->next) {
const int ml_fan_idx = POINTER_AS_INT(lnode->link);
- BLI_BITMAP_ENABLE(done_loops, ml_fan_idx);
+ done_loops[ml_fan_idx].set();
if (vert_used) {
- mloop[ml_fan_idx].v = new_vert_idx;
+ loops[ml_fan_idx].v = new_vert_idx;
}
}
}
if (!vert_used) {
- BLI_BITMAP_ENABLE(verts_used, vert_idx);
+ verts_used[vert_idx].set();
/* We need to update that vertex's normal here, we won't go over it again. */
/* This is important! *DO NOT* set vnor to final computed lnor,
* vnor should always be defined to 'automatic normal' value computed from its polys,
@@ -1996,38 +1946,35 @@ static int split_faces_prepare_new_verts(Mesh *mesh,
}
}
- MEM_freeN(done_loops);
- MEM_freeN(verts_used);
-
return verts_len - mesh->totvert;
}
/* Detect needed new edges, and update accordingly loops' edge indices.
* WARNING! Leaves mesh in invalid state. */
-static int split_faces_prepare_new_edges(const Mesh *mesh,
+static int split_faces_prepare_new_edges(Mesh *mesh,
SplitFaceNewEdge **new_edges,
MemArena *memarena)
{
const int num_polys = mesh->totpoly;
int num_edges = mesh->totedge;
- MEdge *medge = mesh->medge;
- MLoop *mloop = mesh->mloop;
- const MPoly *mpoly = mesh->mpoly;
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
+ const Span<MPoly> polys = mesh->polys();
- BLI_bitmap *edges_used = BLI_BITMAP_NEW(num_edges, __func__);
+ BitVector<> edges_used(num_edges, false);
EdgeHash *edges_hash = BLI_edgehash_new_ex(__func__, num_edges);
- const MPoly *mp = mpoly;
+ const MPoly *mp = polys.data();
for (int poly_idx = 0; poly_idx < num_polys; poly_idx++, mp++) {
- MLoop *ml_prev = &mloop[mp->loopstart + mp->totloop - 1];
- MLoop *ml = &mloop[mp->loopstart];
+ MLoop *ml_prev = &loops[mp->loopstart + mp->totloop - 1];
+ MLoop *ml = &loops[mp->loopstart];
for (int loop_idx = 0; loop_idx < mp->totloop; loop_idx++, ml++) {
void **eval;
if (!BLI_edgehash_ensure_p(edges_hash, ml_prev->v, ml->v, &eval)) {
const int edge_idx = ml_prev->e;
/* That edge has not been encountered yet, define it. */
- if (BLI_BITMAP_TEST(edges_used, edge_idx)) {
+ if (edges_used[edge_idx]) {
/* Original edge has already been used, we need to define a new one. */
const int new_edge_idx = num_edges++;
*eval = POINTER_FROM_INT(new_edge_idx);
@@ -2044,10 +1991,10 @@ static int split_faces_prepare_new_edges(const Mesh *mesh,
}
else {
/* We can re-use original edge. */
- medge[edge_idx].v1 = ml_prev->v;
- medge[edge_idx].v2 = ml->v;
+ edges[edge_idx].v1 = ml_prev->v;
+ edges[edge_idx].v2 = ml->v;
*eval = POINTER_FROM_INT(edge_idx);
- BLI_BITMAP_ENABLE(edges_used, edge_idx);
+ edges_used[edge_idx].set();
}
}
else {
@@ -2059,7 +2006,6 @@ static int split_faces_prepare_new_edges(const Mesh *mesh,
}
}
- MEM_freeN(edges_used);
BLI_edgehash_free(edges_hash, nullptr);
return num_edges - mesh->totedge;
@@ -2071,7 +2017,6 @@ static void split_faces_split_new_verts(Mesh *mesh,
const int num_new_verts)
{
const int verts_len = mesh->totvert - num_new_verts;
- MVert *mvert = mesh->mvert;
float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(mesh);
/* Normals were already calculated at the beginning of this operation, we rely on that to update
@@ -2079,8 +2024,7 @@ static void split_faces_split_new_verts(Mesh *mesh,
BLI_assert(!BKE_mesh_vertex_normals_are_dirty(mesh));
/* Remember new_verts is a single linklist, so its items are in reversed order... */
- MVert *new_mv = &mvert[mesh->totvert - 1];
- for (int i = mesh->totvert - 1; i >= verts_len; i--, new_mv--, new_verts = new_verts->next) {
+ for (int i = mesh->totvert - 1; i >= verts_len; i--, new_verts = new_verts->next) {
BLI_assert(new_verts->new_index == i);
BLI_assert(new_verts->new_index != new_verts->orig_index);
CustomData_copy_data(&mesh->vdata, &mesh->vdata, new_verts->orig_index, i, 1);
@@ -2096,10 +2040,10 @@ static void split_faces_split_new_edges(Mesh *mesh,
const int num_new_edges)
{
const int num_edges = mesh->totedge - num_new_edges;
- MEdge *medge = mesh->medge;
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
/* Remember new_edges is a single linklist, so its items are in reversed order... */
- MEdge *new_med = &medge[mesh->totedge - 1];
+ MEdge *new_med = &edges[mesh->totedge - 1];
for (int i = mesh->totedge - 1; i >= num_edges; i--, new_med--, new_edges = new_edges->next) {
BLI_assert(new_edges->new_index == i);
BLI_assert(new_edges->new_index != new_edges->orig_index);
@@ -2127,14 +2071,6 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
SplitFaceNewVert *new_verts = nullptr;
SplitFaceNewEdge *new_edges = nullptr;
- /* Ensure we own the layers, we need to do this before split_faces_prepare_new_verts as it will
- * directly assign new indices to existing edges and loops. */
- CustomData_duplicate_referenced_layers(&mesh->vdata, mesh->totvert);
- CustomData_duplicate_referenced_layers(&mesh->edata, mesh->totedge);
- CustomData_duplicate_referenced_layers(&mesh->ldata, mesh->totloop);
- /* Update pointers in case we duplicated referenced layers. */
- BKE_mesh_update_customdata_pointers(mesh, false);
-
/* Detect loop normal spaces (a.k.a. smooth fans) that will need a new vert. */
const int num_new_verts = split_faces_prepare_new_verts(
mesh, &lnors_spacearr, &new_verts, memarena);
@@ -2149,14 +2085,12 @@ void BKE_mesh_split_faces(Mesh *mesh, bool free_loop_normals)
const bool do_edges = (num_new_edges > 0);
/* Reallocate all vert and edge related data. */
+ CustomData_realloc(&mesh->vdata, mesh->totvert, mesh->totvert + num_new_verts);
mesh->totvert += num_new_verts;
- CustomData_realloc(&mesh->vdata, mesh->totvert);
if (do_edges) {
+ CustomData_realloc(&mesh->edata, mesh->totedge, mesh->totedge + num_new_edges);
mesh->totedge += num_new_edges;
- CustomData_realloc(&mesh->edata, mesh->totedge);
}
- /* Update pointers to a newly allocated memory. */
- BKE_mesh_update_customdata_pointers(mesh, false);
/* Update normals manually to avoid recalculation after this operation. */
mesh->runtime.vert_normals = (float(*)[3])MEM_reallocN(mesh->runtime.vert_normals,
diff --git a/source/blender/blenkernel/intern/mesh_boolean_convert.cc b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
index a1ef2d2e6b5..7a04e45fe00 100644
--- a/source/blender/blenkernel/intern/mesh_boolean_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_boolean_convert.cc
@@ -9,6 +9,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -23,6 +24,7 @@
#include "BLI_mesh_intersect.hh"
#include "BLI_span.hh"
#include "BLI_task.hh"
+#include "BLI_virtual_array.hh"
namespace blender::meshintersect {
@@ -160,9 +162,10 @@ const MPoly *MeshesToIMeshInfo::input_mpoly_for_orig_index(int orig_index,
int orig_mesh_index = input_mesh_for_imesh_face(orig_index);
BLI_assert(0 <= orig_mesh_index && orig_mesh_index < meshes.size());
const Mesh *me = meshes[orig_mesh_index];
+ const Span<MPoly> polys = me->polys();
int index_in_mesh = orig_index - mesh_poly_offset[orig_mesh_index];
BLI_assert(0 <= index_in_mesh && index_in_mesh < me->totpoly);
- const MPoly *mp = &me->mpoly[index_in_mesh];
+ const MPoly *mp = &polys[index_in_mesh];
if (r_orig_mesh) {
*r_orig_mesh = me;
}
@@ -186,9 +189,10 @@ const MVert *MeshesToIMeshInfo::input_mvert_for_orig_index(int orig_index,
int orig_mesh_index = input_mesh_for_imesh_vert(orig_index);
BLI_assert(0 <= orig_mesh_index && orig_mesh_index < meshes.size());
const Mesh *me = meshes[orig_mesh_index];
+ const Span<MVert> verts = me->verts();
int index_in_mesh = orig_index - mesh_vert_offset[orig_mesh_index];
BLI_assert(0 <= index_in_mesh && index_in_mesh < me->totvert);
- const MVert *mv = &me->mvert[index_in_mesh];
+ const MVert *mv = &verts[index_in_mesh];
if (r_orig_mesh) {
*r_orig_mesh = me;
}
@@ -206,9 +210,10 @@ const MEdge *MeshesToIMeshInfo::input_medge_for_orig_index(int orig_index,
int orig_mesh_index = input_mesh_for_imesh_edge(orig_index);
BLI_assert(0 <= orig_mesh_index && orig_mesh_index < meshes.size());
const Mesh *me = meshes[orig_mesh_index];
+ const Span<MEdge> edges = me->edges();
int index_in_mesh = orig_index - mesh_edge_offset[orig_mesh_index];
BLI_assert(0 <= index_in_mesh && index_in_mesh < me->totedge);
- const MEdge *medge = &me->medge[index_in_mesh];
+ const MEdge *medge = &edges[index_in_mesh];
if (r_orig_mesh) {
*r_orig_mesh = me;
}
@@ -304,17 +309,19 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
bool need_face_flip = r_info->has_negative_transform[mi] != r_info->has_negative_transform[0];
Vector<Vert *> verts(me->totvert);
- Span<MVert> mverts = Span(me->mvert, me->totvert);
+ const Span<MVert> mesh_verts = me->verts();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
/* Allocate verts
* 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) {
- threading::parallel_for(mverts.index_range(), 2048, [&](IndexRange range) {
+ threading::parallel_for(mesh_verts.index_range(), 2048, [&](IndexRange range) {
float3 co;
for (int i : range) {
- co = float3(mverts[i].co);
+ co = float3(mesh_verts[i].co);
mpq3 mco = mpq3(co.x, co.y, co.z);
double3 dco(mco[0].get_d(), mco[1].get_d(), mco[2].get_d());
verts[i] = new Vert(mco, dco, NO_INDEX, i);
@@ -322,26 +329,26 @@ static IMesh meshes_to_imesh(Span<const Mesh *> meshes,
});
}
else {
- threading::parallel_for(mverts.index_range(), 2048, [&](IndexRange range) {
+ threading::parallel_for(mesh_verts.index_range(), 2048, [&](IndexRange range) {
float3 co;
for (int i : range) {
- co = r_info->to_target_transform[mi] * float3(mverts[i].co);
+ co = r_info->to_target_transform[mi] * float3(mesh_verts[i].co);
mpq3 mco = mpq3(co.x, co.y, co.z);
double3 dco(mco[0].get_d(), mco[1].get_d(), mco[2].get_d());
verts[i] = new Vert(mco, dco, NO_INDEX, i);
}
});
}
- for (int i : mverts.index_range()) {
+ for (int i : mesh_verts.index_range()) {
r_info->mesh_to_imesh_vert[v] = arena.add_or_find_vert(verts[i]);
++v;
}
- for (const MPoly &poly : Span(me->mpoly, me->totpoly)) {
+ for (const MPoly &poly : polys) {
int flen = poly.totloop;
face_vert.resize(flen);
face_edge_orig.resize(flen);
- const MLoop *l = &me->mloop[poly.loopstart];
+ const MLoop *l = &loops[poly.loopstart];
for (int i = 0; i < flen; ++i) {
int mverti = r_info->mesh_vert_offset[mi] + l->v;
const Vert *fv = r_info->mesh_to_imesh_vert[mverti];
@@ -374,7 +381,6 @@ static void copy_vert_attributes(Mesh *dest_mesh,
int mv_index,
int index_in_orig_me)
{
- mv->bweight = orig_mv->bweight;
mv->flag = orig_mv->flag;
/* For all layers in the orig mesh, copy the layer information. */
@@ -405,13 +411,17 @@ static void copy_poly_attributes(Mesh *dest_mesh,
const Mesh *orig_me,
int mp_index,
int index_in_orig_me,
- Span<short> material_remap)
+ Span<short> material_remap,
+ MutableSpan<int> dst_material_indices)
{
- if (material_remap.size() > 0 && material_remap.index_range().contains(orig_mp->mat_nr)) {
- mp->mat_nr = material_remap[orig_mp->mat_nr];
+ const VArray<int> src_material_indices = orig_me->attributes().lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ const int src_index = src_material_indices[index_in_orig_me];
+ if (material_remap.size() > 0 && material_remap.index_range().contains(src_index)) {
+ dst_material_indices[mp_index] = material_remap[src_index];
}
else {
- mp->mat_nr = orig_mp->mat_nr;
+ dst_material_indices[mp_index] = src_index;
}
mp->flag = orig_mp->flag;
@@ -439,7 +449,6 @@ static void copy_edge_attributes(Mesh *dest_mesh,
int medge_index,
int index_in_orig_me)
{
- medge->bweight = orig_medge->bweight;
medge->crease = orig_medge->crease;
medge->flag = orig_medge->flag;
CustomData *target_cd = &dest_mesh->edata;
@@ -473,14 +482,16 @@ static int fill_orig_loops(const Face *f,
const Mesh *orig_me,
int orig_me_index,
MeshesToIMeshInfo &mim,
- Array<int> &orig_loops)
+ MutableSpan<int> r_orig_loops)
{
- orig_loops.fill(-1);
+ r_orig_loops.fill(-1);
+ const Span<MLoop> orig_loops = orig_me->loops();
+
int orig_mplen = orig_mp->totloop;
if (f->size() != orig_mplen) {
return 0;
}
- BLI_assert(orig_loops.size() == orig_mplen);
+ BLI_assert(r_orig_loops.size() == orig_mplen);
/* We'll look for the case where the first vertex in f has an original vertex
* that is the same as one in orig_me (after correcting for offset in mim meshes).
* Then see that loop and any subsequent ones have the same start and end vertex.
@@ -502,7 +513,7 @@ static int fill_orig_loops(const Face *f,
int offset = -1;
for (int i = 0; i < orig_mplen; ++i) {
int loop_i = i + orig_mp->loopstart;
- if (orig_me->mloop[loop_i].v == first_orig_v_in_orig_me) {
+ if (orig_loops[loop_i].v == first_orig_v_in_orig_me) {
offset = i;
break;
}
@@ -513,7 +524,7 @@ static int fill_orig_loops(const Face *f,
int num_orig_loops_found = 0;
for (int mp_loop_index = 0; mp_loop_index < orig_mplen; ++mp_loop_index) {
int orig_mp_loop_index = (mp_loop_index + offset) % orig_mplen;
- MLoop *l = &orig_me->mloop[orig_mp->loopstart + orig_mp_loop_index];
+ const MLoop *l = &orig_loops[orig_mp->loopstart + orig_mp_loop_index];
int fv_orig = f->vert[mp_loop_index]->orig;
if (fv_orig != NO_INDEX) {
fv_orig -= orig_me_vert_offset;
@@ -522,7 +533,8 @@ static int fill_orig_loops(const Face *f,
}
}
if (l->v == fv_orig) {
- MLoop *lnext = &orig_me->mloop[orig_mp->loopstart + ((orig_mp_loop_index + 1) % orig_mplen)];
+ const MLoop *lnext =
+ &orig_loops[orig_mp->loopstart + ((orig_mp_loop_index + 1) % orig_mplen)];
int fvnext_orig = f->vert[(mp_loop_index + 1) % orig_mplen]->orig;
if (fvnext_orig != NO_INDEX) {
fvnext_orig -= orig_me_vert_offset;
@@ -531,7 +543,7 @@ static int fill_orig_loops(const Face *f,
}
}
if (lnext->v == fvnext_orig) {
- orig_loops[mp_loop_index] = orig_mp->loopstart + orig_mp_loop_index;
+ r_orig_loops[mp_loop_index] = orig_mp->loopstart + orig_mp_loop_index;
++num_orig_loops_found;
}
}
@@ -549,19 +561,18 @@ static void get_poly2d_cos(const Mesh *me,
const float4x4 &trans_mat,
float r_axis_mat[3][3])
{
- int n = mp->totloop;
+ const Span<MVert> verts = me->verts();
+ const Span<MLoop> loops = me->loops();
+ const Span<MLoop> poly_loops = loops.slice(mp->loopstart, mp->totloop);
/* Project coordinates to 2d in cos_2d, using normal as projection axis. */
float axis_dominant[3];
- BKE_mesh_calc_poly_normal(mp, &me->mloop[mp->loopstart], me->mvert, axis_dominant);
+ BKE_mesh_calc_poly_normal(mp, &loops[mp->loopstart], verts.data(), axis_dominant);
axis_dominant_v3_to_m3(r_axis_mat, axis_dominant);
- MLoop *ml = &me->mloop[mp->loopstart];
- const MVert *mverts = me->mvert;
- for (int i = 0; i < n; ++i) {
- float3 co = mverts[ml->v].co;
+ for (const int i : poly_loops.index_range()) {
+ float3 co = verts[poly_loops[i].v].co;
co = trans_mat * co;
mul_v2_m3v3(cos_2d[i], r_axis_mat, co);
- ++ml;
}
}
@@ -596,6 +607,8 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
get_poly2d_cos(orig_me, orig_mp, cos_2d, mim.to_target_transform[orig_me_index], axis_mat);
}
CustomData *target_cd = &dest_mesh->ldata;
+ const Span<MVert> dst_verts = dest_mesh->verts();
+ const Span<MLoop> dst_loops = dest_mesh->loops();
for (int i = 0; i < mp->totloop; ++i) {
int loop_index = mp->loopstart + i;
int orig_loop_index = norig > 0 ? orig_loops[i] : -1;
@@ -605,7 +618,7 @@ static void copy_or_interp_loop_attributes(Mesh *dest_mesh,
* The coordinate needs to be projected into 2d, just like the interpolating polygon's
* coordinates were. The `dest_mesh` coordinates are already in object 0 local space. */
float co[2];
- mul_v2_m3v3(co, axis_mat, dest_mesh->mvert[dest_mesh->mloop[loop_index].v].co);
+ mul_v2_m3v3(co, axis_mat, dst_verts[dst_loops[loop_index].v].co);
interp_weights_poly_v2(weights.data(), cos_2d, orig_mp->totloop, co);
}
for (int source_layer_i = 0; source_layer_i < source_cd->totlayer; ++source_layer_i) {
@@ -663,15 +676,15 @@ static void merge_vertex_loop_poly_customdata_layers(Mesh *target, MeshesToIMesh
const Mesh *me = mim.meshes[mesh_index];
if (me->totvert) {
CustomData_merge(
- &me->vdata, &target->vdata, CD_MASK_MESH.vmask, CD_DEFAULT, target->totvert);
+ &me->vdata, &target->vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, target->totvert);
}
if (me->totloop) {
CustomData_merge(
- &me->ldata, &target->ldata, CD_MASK_MESH.lmask, CD_DEFAULT, target->totloop);
+ &me->ldata, &target->ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, target->totloop);
}
if (me->totpoly) {
CustomData_merge(
- &me->pdata, &target->pdata, CD_MASK_MESH.pmask, CD_DEFAULT, target->totpoly);
+ &me->pdata, &target->pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, target->totpoly);
}
}
}
@@ -682,7 +695,7 @@ static void merge_edge_customdata_layers(Mesh *target, MeshesToIMeshInfo &mim)
const Mesh *me = mim.meshes[mesh_index];
if (me->totedge) {
CustomData_merge(
- &me->edata, &target->edata, CD_MASK_MESH.emask, CD_DEFAULT, target->totedge);
+ &me->edata, &target->edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, target->totedge);
}
}
}
@@ -708,9 +721,10 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
merge_vertex_loop_poly_customdata_layers(result, mim);
/* Set the vertex coordinate values and other data. */
+ MutableSpan<MVert> verts = result->verts_for_write();
for (int vi : im->vert_index_range()) {
const Vert *v = im->vert(vi);
- MVert *mv = &result->mvert[vi];
+ MVert *mv = &verts[vi];
copy_v3fl_v3db(mv->co, v->co);
if (v->orig != NO_INDEX) {
const Mesh *orig_me;
@@ -722,8 +736,13 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
/* Set the loopstart and totloop for each output poly,
* and set the vertices in the appropriate loops. */
+ bke::SpanAttributeWriter<int> dst_material_indices =
+ result->attributes_for_write().lookup_or_add_for_write_only_span<int>("material_index",
+ ATTR_DOMAIN_FACE);
int cur_loop_index = 0;
- MLoop *l = result->mloop;
+ MutableSpan<MLoop> dst_loops = result->loops_for_write();
+ MutableSpan<MPoly> dst_polys = result->polys_for_write();
+ MLoop *l = dst_loops.data();
for (int fi : im->face_index_range()) {
const Face *f = im->face(fi);
const Mesh *orig_me;
@@ -731,7 +750,7 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
int orig_me_index;
const MPoly *orig_mp = mim.input_mpoly_for_orig_index(
f->orig, &orig_me, &orig_me_index, &index_in_orig_me);
- MPoly *mp = &result->mpoly[fi];
+ MPoly *mp = &dst_polys[fi];
mp->totloop = f->size();
mp->loopstart = cur_loop_index;
for (int j : f->index_range()) {
@@ -750,9 +769,11 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
index_in_orig_me,
(mim.material_remaps.size() > 0) ?
mim.material_remaps[orig_me_index].as_span() :
- Span<short>());
+ Span<short>(),
+ dst_material_indices.span);
copy_or_interp_loop_attributes(result, f, mp, orig_mp, orig_me, orig_me_index, mim);
}
+ dst_material_indices.finish();
/* BKE_mesh_calc_edges will calculate and populate all the
* MEdges from the MPolys. */
@@ -761,17 +782,18 @@ static Mesh *imesh_to_mesh(IMesh *im, MeshesToIMeshInfo &mim)
/* Now that the MEdges are populated, we can copy over the required attributes and custom layers.
*/
+ MutableSpan<MEdge> edges = result->edges_for_write();
for (int fi : im->face_index_range()) {
const Face *f = im->face(fi);
- MPoly *mp = &result->mpoly[fi];
+ const MPoly *mp = &dst_polys[fi];
for (int j : f->index_range()) {
if (f->edge_orig[j] != NO_INDEX) {
const Mesh *orig_me;
int index_in_orig_me;
const MEdge *orig_medge = mim.input_medge_for_orig_index(
f->edge_orig[j], &orig_me, &index_in_orig_me);
- int e_index = result->mloop[mp->loopstart + j].e;
- MEdge *medge = &result->medge[e_index];
+ int e_index = dst_loops[mp->loopstart + j].e;
+ MEdge *medge = &edges[e_index];
copy_edge_attributes(result, medge, orig_medge, orig_me, e_index, index_in_orig_me);
}
}
@@ -833,12 +855,14 @@ Mesh *direct_mesh_boolean(Span<const Mesh *> meshes,
/* Store intersecting edge indices. */
if (r_intersecting_edges != nullptr) {
+ const Span<MPoly> polys = result->polys();
+ const Span<MLoop> loops = result->loops();
for (int fi : m_out.face_index_range()) {
const Face &face = *m_out.face(fi);
- const MPoly &poly = result->mpoly[fi];
+ const MPoly &poly = polys[fi];
for (int corner_i : face.index_range()) {
if (face.is_intersect[corner_i]) {
- int e_index = result->mloop[poly.loopstart + corner_i].e;
+ int e_index = loops[poly.loopstart + corner_i].e;
r_intersecting_edges->append(e_index);
}
}
diff --git a/source/blender/blenkernel/intern/mesh_calc_edges.cc b/source/blender/blenkernel/intern/mesh_calc_edges.cc
index 31e20750cf2..cc315130ad1 100644
--- a/source/blender/blenkernel/intern/mesh_calc_edges.cc
+++ b/source/blender/blenkernel/intern/mesh_calc_edges.cc
@@ -80,9 +80,10 @@ static void add_existing_edges_to_hash_maps(Mesh *mesh,
uint32_t parallel_mask)
{
/* Assume existing edges are valid. */
+ const Span<MEdge> edges = mesh->edges();
threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
const int task_index = &edge_map - edge_maps.data();
- for (const MEdge &edge : Span(mesh->medge, mesh->totedge)) {
+ for (const MEdge &edge : edges) {
OrderedEdge ordered_edge{edge.v1, edge.v2};
/* Only add the edge when it belongs into this map. */
if (task_index == (parallel_mask & ordered_edge.hash2())) {
@@ -96,10 +97,11 @@ static void add_polygon_edges_to_hash_maps(Mesh *mesh,
MutableSpan<EdgeMap> edge_maps,
uint32_t parallel_mask)
{
- const Span<MLoop> loops{mesh->mloop, mesh->totloop};
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
threading::parallel_for_each(edge_maps, [&](EdgeMap &edge_map) {
const int task_index = &edge_map - edge_maps.data();
- for (const MPoly &poly : Span(mesh->mpoly, mesh->totpoly)) {
+ for (const MPoly &poly : polys) {
Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
const MLoop *prev_loop = &poly_loops.last();
for (const MLoop &next_loop : poly_loops) {
@@ -157,10 +159,11 @@ static void update_edge_indices_in_poly_loops(Mesh *mesh,
Span<EdgeMap> edge_maps,
uint32_t parallel_mask)
{
- const MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ const Span<MPoly> polys = mesh->polys();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
threading::parallel_for(IndexRange(mesh->totpoly), 100, [&](IndexRange range) {
for (const int poly_index : range) {
- MPoly &poly = mesh->mpoly[poly_index];
+ const MPoly &poly = polys[poly_index];
MutableSpan<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
MLoop *prev_loop = &poly_loops.last();
@@ -242,7 +245,6 @@ void BKE_mesh_calc_edges(Mesh *mesh, bool keep_existing_edges, const bool select
CustomData_reset(&mesh->edata);
CustomData_add_layer(&mesh->edata, CD_MEDGE, CD_ASSIGN, new_edges.data(), new_totedge);
mesh->totedge = new_totedge;
- mesh->medge = new_edges.data();
/* Explicitly clear edge maps, because that way it can be parallelized. */
clear_hash_tables(edge_maps);
diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc
index 7ebb3e25fd4..e56df0e3fe3 100644
--- a/source/blender/blenkernel/intern/mesh_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_convert.cc
@@ -22,6 +22,7 @@
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLI_span.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -53,7 +54,11 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+using blender::float3;
using blender::IndexRange;
+using blender::MutableSpan;
+using blender::Span;
+using blender::StringRefNull;
/* Define for cases when you want extra validation of mesh
* after certain modifications.
@@ -69,121 +74,58 @@ using blender::IndexRange;
static CLG_LogRef LOG = {"bke.mesh_convert"};
-void BKE_mesh_from_metaball(ListBase *lb, Mesh *me)
-{
- DispList *dl;
- MVert *mvert;
- MLoop *mloop, *allloop;
- MPoly *mpoly;
- int a, *index;
-
- dl = (DispList *)lb->first;
- if (dl == nullptr) {
- return;
- }
-
- if (dl->type == DL_INDEX4) {
- mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, dl->nr);
- allloop = mloop = (MLoop *)CustomData_add_layer(
- &me->ldata, CD_MLOOP, CD_CALLOC, nullptr, dl->parts * 4);
- mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, nullptr, dl->parts);
- me->mvert = mvert;
- me->mloop = mloop;
- me->mpoly = mpoly;
- me->totvert = dl->nr;
- me->totpoly = dl->parts;
-
- for (const int i : IndexRange(dl->nr)) {
- copy_v3_v3(me->mvert[i].co, &dl->verts[3 * i]);
- }
-
- a = dl->parts;
- index = dl->index;
- while (a--) {
- int count = index[2] != index[3] ? 4 : 3;
-
- mloop[0].v = index[0];
- mloop[1].v = index[1];
- mloop[2].v = index[2];
- if (count == 4) {
- mloop[3].v = index[3];
- }
-
- mpoly->totloop = count;
- mpoly->loopstart = (int)(mloop - allloop);
- mpoly->flag = ME_SMOOTH;
-
- mpoly++;
- mloop += count;
- me->totloop += count;
- index += 4;
- }
-
- BKE_mesh_update_customdata_pointers(me, true);
- BKE_mesh_calc_edges(me, true, false);
- }
-}
-
/**
* Specialized function to use when we _know_ existing edges don't overlap with poly edges.
*/
-static void make_edges_mdata_extend(
- MEdge **r_alledge, int *r_totedge, const MPoly *mpoly, MLoop *mloop, const int totpoly)
+static void make_edges_mdata_extend(Mesh &mesh)
{
- int totedge = *r_totedge;
- int totedge_new;
- EdgeHash *eh;
- uint eh_reserve;
+ int totedge = mesh.totedge;
const MPoly *mp;
int i;
- eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(totpoly));
- eh = BLI_edgehash_new_ex(__func__, eh_reserve);
+ const Span<MPoly> polys = mesh.polys();
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
- for (i = 0, mp = mpoly; i < totpoly; i++, mp++) {
- BKE_mesh_poly_edgehash_insert(eh, mp, mloop + mp->loopstart);
+ const int eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(mesh.totpoly));
+ EdgeHash *eh = BLI_edgehash_new_ex(__func__, eh_reserve);
+
+ for (const MPoly &poly : polys) {
+ BKE_mesh_poly_edgehash_insert(eh, &poly, &loops[poly.loopstart]);
}
- totedge_new = BLI_edgehash_len(eh);
+ const int totedge_new = BLI_edgehash_len(eh);
#ifdef DEBUG
/* ensure that there's no overlap! */
if (totedge_new) {
- MEdge *medge = *r_alledge;
- for (i = 0; i < totedge; i++, medge++) {
- BLI_assert(BLI_edgehash_haskey(eh, medge->v1, medge->v2) == false);
+ for (const MEdge &edge : mesh.edges()) {
+ BLI_assert(BLI_edgehash_haskey(eh, edge.v1, edge.v2) == false);
}
}
#endif
if (totedge_new) {
+ /* The only layer should be edges, so no other layers need to be initialized. */
+ BLI_assert(mesh.edata.totlayer == 1);
+ CustomData_realloc(&mesh.edata, totedge, totedge + totedge_new);
+ mesh.totedge += totedge_new;
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
+ MEdge *medge = &edges[totedge];
+
EdgeHashIterator *ehi;
- MEdge *medge;
uint e_index = totedge;
-
- *r_alledge = medge = (MEdge *)(*r_alledge ?
- MEM_reallocN(*r_alledge,
- sizeof(MEdge) * (totedge + totedge_new)) :
- MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__));
- medge += totedge;
-
- totedge += totedge_new;
-
- /* --- */
for (ehi = BLI_edgehashIterator_new(eh); BLI_edgehashIterator_isDone(ehi) == false;
BLI_edgehashIterator_step(ehi), ++medge, e_index++) {
BLI_edgehashIterator_getKey(ehi, &medge->v1, &medge->v2);
BLI_edgehashIterator_setValue(ehi, POINTER_FROM_UINT(e_index));
- medge->crease = medge->bweight = 0;
+ medge->crease = 0;
medge->flag = ME_EDGEDRAW | ME_EDGERENDER;
}
BLI_edgehashIterator_free(ehi);
- *r_totedge = totedge;
-
- for (i = 0, mp = mpoly; i < totpoly; i++, mp++) {
- MLoop *l = &mloop[mp->loopstart];
+ for (i = 0, mp = polys.data(); i < mesh.totpoly; i++, mp++) {
+ MLoop *l = &loops[mp->loopstart];
MLoop *l_prev = (l + (mp->totloop - 1));
int j;
for (j = 0; j < mp->totloop; j++, l++) {
@@ -197,25 +139,9 @@ static void make_edges_mdata_extend(
BLI_edgehash_free(eh, nullptr);
}
-/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */
-/* use specified dispbase */
-static int mesh_nurbs_displist_to_mdata(const Curve *cu,
- const ListBase *dispbase,
- MVert **r_allvert,
- int *r_totvert,
- MEdge **r_alledge,
- int *r_totedge,
- MLoop **r_allloop,
- MPoly **r_allpoly,
- MLoopUV **r_alluv,
- int *r_totloop,
- int *r_totpoly)
+static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispbase)
{
- MVert *mvert;
- MPoly *mpoly;
- MLoop *mloop;
- MLoopUV *mloopuv = nullptr;
- MEdge *medge;
+ using namespace blender::bke;
const float *data;
int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0;
int p1, p2, p3, p4, *index;
@@ -257,21 +183,24 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
}
if (totvert == 0) {
- /* Make Sure you check ob->data is a curve. */
- // error("can't convert");
- return -1;
+ return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
}
- *r_allvert = mvert = (MVert *)MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert");
- *r_alledge = medge = (MEdge *)MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge");
- *r_allloop = mloop = (MLoop *)MEM_calloc_arrayN(
- totpoly, sizeof(MLoop[4]), "nurbs_init mloop"); /* totloop */
- *r_allpoly = mpoly = (MPoly *)MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop");
+ Mesh *mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
- if (r_alluv) {
- *r_alluv = mloopuv = (MLoopUV *)MEM_calloc_arrayN(
- totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv");
- }
+ MVert *mvert = verts.data();
+ MEdge *medge = edges.data();
+ MPoly *mpoly = polys.data();
+ MLoop *mloop = loops.data();
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
+ SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>(
+ "material_index", ATTR_DOMAIN_FACE);
+ MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_add_layer_named(
+ &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh->totloop, "UVMap"));
/* verts and faces */
vertcount = 0;
@@ -346,9 +275,9 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
mloop[0].v = startvert + index[0];
mloop[1].v = startvert + index[2];
mloop[2].v = startvert + index[1];
- mpoly->loopstart = (int)(mloop - (*r_allloop));
+ mpoly->loopstart = (int)(mloop - loops.data());
mpoly->totloop = 3;
- mpoly->mat_nr = dl->col;
+ material_indices.span[mpoly - polys.data()] = dl->col;
if (mloopuv) {
for (int i = 0; i < 3; i++, mloopuv++) {
@@ -406,9 +335,9 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
mloop[1].v = p3;
mloop[2].v = p4;
mloop[3].v = p2;
- mpoly->loopstart = (int)(mloop - (*r_allloop));
+ mpoly->loopstart = (int)(mloop - loops.data());
mpoly->totloop = 4;
- mpoly->mat_nr = dl->col;
+ material_indices.span[mpoly - polys.data()] = dl->col;
if (mloopuv) {
int orco_sizeu = dl->nr - 1;
@@ -458,15 +387,12 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
}
if (totpoly) {
- make_edges_mdata_extend(r_alledge, &totedge, *r_allpoly, *r_allloop, totpoly);
+ make_edges_mdata_extend(*mesh);
}
- *r_totpoly = totpoly;
- *r_totloop = totloop;
- *r_totedge = totedge;
- *r_totvert = totvert;
+ material_indices.finish();
- return 0;
+ return mesh;
}
/**
@@ -487,60 +413,12 @@ static void mesh_copy_texture_space_from_curve_type(const Curve *cu, Mesh *me)
Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *dispbase)
{
const Curve *cu = (const Curve *)ob->data;
- Mesh *mesh;
- MVert *allvert;
- MEdge *alledge;
- MLoop *allloop;
- MPoly *allpoly;
- MLoopUV *alluv = nullptr;
- int totvert, totedge, totloop, totpoly;
-
- if (mesh_nurbs_displist_to_mdata(cu,
- dispbase,
- &allvert,
- &totvert,
- &alledge,
- &totedge,
- &allloop,
- &allpoly,
- &alluv,
- &totloop,
- &totpoly) != 0) {
- /* Error initializing mdata. This often happens when curve is empty */
- return BKE_mesh_new_nomain(0, 0, 0, 0, 0);
- }
-
- mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly);
-
- if (totvert != 0) {
- memcpy(mesh->mvert, allvert, totvert * sizeof(MVert));
- }
- if (totedge != 0) {
- memcpy(mesh->medge, alledge, totedge * sizeof(MEdge));
- }
- if (totloop != 0) {
- memcpy(mesh->mloop, allloop, totloop * sizeof(MLoop));
- }
- if (totpoly != 0) {
- memcpy(mesh->mpoly, allpoly, totpoly * sizeof(MPoly));
- }
-
- if (alluv) {
- const char *uvname = "UVMap";
- CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, totloop, uvname);
- }
+ Mesh *mesh = mesh_nurbs_displist_to_mesh(cu, dispbase);
mesh_copy_texture_space_from_curve_type(cu, mesh);
-
- /* Copy curve materials. */
mesh->mat = (Material **)MEM_dupallocN(cu->mat);
mesh->totcol = cu->totcol;
- MEM_freeN(allvert);
- MEM_freeN(alledge);
- MEM_freeN(allloop);
- MEM_freeN(allpoly);
-
return mesh;
}
@@ -557,7 +435,7 @@ Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob)
struct EdgeLink {
struct EdgeLink *next, *prev;
- void *edge;
+ const void *edge;
};
struct VertLink {
@@ -581,10 +459,13 @@ static void appendPolyLineVert(ListBase *lb, uint index)
void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int edge_users_test)
{
- MVert *mvert = me->mvert;
- MEdge *med, *medge = me->medge;
- MPoly *mp, *mpoly = me->mpoly;
- MLoop *mloop = me->mloop;
+ const Span<MVert> verts = me->verts();
+ const Span<MEdge> mesh_edges = me->edges();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
+
+ const MEdge *med;
+ const MPoly *mp;
int medge_len = me->totedge;
int mpoly_len = me->totpoly;
@@ -598,8 +479,8 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
/* get boundary edges */
edge_users = (int *)MEM_calloc_arrayN(medge_len, sizeof(int), __func__);
- for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) {
- MLoop *ml = &mloop[mp->loopstart];
+ for (i = 0, mp = polys.data(); i < mpoly_len; i++, mp++) {
+ const MLoop *ml = &loops[mp->loopstart];
int j;
for (j = 0; j < mp->totloop; j++, ml++) {
edge_users[ml->e]++;
@@ -607,7 +488,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
}
/* create edges from all faces (so as to find edges not in any faces) */
- med = medge;
+ med = mesh_edges.data();
for (i = 0; i < medge_len; i++, med++) {
if (edge_users[i] == edge_users_test) {
EdgeLink *edl = MEM_cnew<EdgeLink>("EdgeLink");
@@ -710,7 +591,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
/* add points */
vl = (VertLink *)polyline.first;
for (i = 0, bp = nu->bp; i < totpoly; i++, bp++, vl = (VertLink *)vl->next) {
- copy_v3_v3(bp->vec, mvert[vl->index].co);
+ copy_v3_v3(bp->vec, verts[vl->index].co);
bp->f1 = SELECT;
bp->radius = bp->weight = 1.0;
}
@@ -751,21 +632,27 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene),
void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud)
{
- BLI_assert(me != nullptr);
+ using namespace blender;
+ BLI_assert(me != nullptr);
+ /* The pointcloud should only contain the position attribute, otherwise more attributes would
+ * need to be initialized below. */
+ BLI_assert(pointcloud->attributes().all_ids().size() == 1);
+ CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint, me->totvert);
pointcloud->totpoint = me->totvert;
- CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
/* Copy over all attributes. */
CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert);
- BKE_pointcloud_update_customdata_pointers(pointcloud);
- CustomData_update_typemap(&pointcloud->pdata);
- MVert *mvert;
- mvert = me->mvert;
- for (int i = 0; i < me->totvert; i++, mvert++) {
- copy_v3_v3(pointcloud->co[i], mvert->co);
- }
+ bke::AttributeAccessor mesh_attributes = me->attributes();
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
+
+ const VArray<float3> mesh_positions = mesh_attributes.lookup_or_default<float3>(
+ "position", ATTR_DOMAIN_POINT, float3(0));
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ mesh_positions.materialize(point_positions.span);
+ point_positions.finish();
}
void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene), Object *ob)
@@ -800,18 +687,16 @@ void BKE_mesh_from_pointcloud(const PointCloud *pointcloud, Mesh *me)
&pointcloud->pdata, &me->vdata, CD_MASK_PROP_ALL, CD_DUPLICATE, pointcloud->totpoint);
/* Convert the Position attribute to a mesh vertex. */
- me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, me->totvert);
- CustomData_update_typemap(&me->vdata);
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, me->totvert);
const int layer_idx = CustomData_get_named_layer_index(
&me->vdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION);
CustomDataLayer *pos_layer = &me->vdata.layers[layer_idx];
float(*positions)[3] = (float(*)[3])pos_layer->data;
- MVert *mvert;
- mvert = me->mvert;
- for (int i = 0; i < me->totvert; i++, mvert++) {
- copy_v3_v3(mvert->co, positions[i]);
+ MutableSpan<MVert> verts = me->verts_for_write();
+ for (int i = 0; i < me->totvert; i++) {
+ copy_v3_v3(verts[i].co, positions[i]);
}
/* Delete Position attribute since it is now in vertex coordinates. */
@@ -820,9 +705,9 @@ void BKE_mesh_from_pointcloud(const PointCloud *pointcloud, Mesh *me)
void BKE_mesh_edges_set_draw_render(Mesh *mesh)
{
- MEdge *med = mesh->medge;
- for (int i = 0; i < mesh->totedge; i++, med++) {
- med->flag |= ME_EDGEDRAW | ME_EDGERENDER;
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ for (int i = 0; i < mesh->totedge; i++) {
+ edges[i].flag |= ME_EDGEDRAW | ME_EDGERENDER;
}
}
@@ -980,6 +865,12 @@ static Mesh *mesh_new_from_curve_type_object(const Object *object)
/* If evaluating the curve replaced object data with different data, free the original data. */
if (temp_data != temp_object->data) {
+ if (GS(temp_data->name) == ID_CU_LEGACY) {
+ /* Clear edit mode pointers that were explicitly copied to the temporary curve. */
+ Curve *curve = reinterpret_cast<Curve *>(temp_data);
+ curve->editfont = nullptr;
+ curve->editnurb = nullptr;
+ }
BKE_id_free(nullptr, temp_data);
}
@@ -992,32 +883,21 @@ static Mesh *mesh_new_from_curve_type_object(const Object *object)
static Mesh *mesh_new_from_mball_object(Object *object)
{
- MetaBall *mball = (MetaBall *)object->data;
-
/* NOTE: We can only create mesh for a polygonized meta ball. This figures out all original meta
* balls and all evaluated child meta balls (since polygonization is only stored in the mother
* ball).
*
* Create empty mesh so script-authors don't run into None objects. */
- if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == nullptr ||
- BLI_listbase_is_empty(&object->runtime.curve_cache->disp)) {
+ if (!DEG_is_evaluated_object(object)) {
return (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
}
- Mesh *mesh_result = (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
- BKE_mesh_from_metaball(&object->runtime.curve_cache->disp, mesh_result);
- BKE_mesh_texspace_copy_from_object(mesh_result, object);
-
- /* Copy materials. */
- mesh_result->totcol = mball->totcol;
- mesh_result->mat = (Material **)MEM_dupallocN(mball->mat);
- if (mball->mat != nullptr) {
- for (int i = mball->totcol; i-- > 0;) {
- mesh_result->mat[i] = BKE_object_material_get(object, i + 1);
- }
+ const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object);
+ if (mesh_eval == nullptr) {
+ return (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
}
- return mesh_result;
+ return BKE_mesh_copy_for_eval(mesh_eval, false);
}
static Mesh *mesh_new_from_mesh(Object *object, Mesh *mesh)
@@ -1206,10 +1086,10 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
mesh_in_bmain->smoothresh = mesh->smoothresh;
mesh->mat = nullptr;
- BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr);
/* Anonymous attributes shouldn't exist on original data. */
- blender::bke::mesh_attributes_for_write(*mesh_in_bmain).remove_anonymous();
+ mesh_in_bmain->attributes_for_write().remove_anonymous();
/* User-count is required because so far mesh was in a limbo, where library management does
* not perform any user management (i.e. copy of a mesh will not increase users of materials). */
@@ -1293,7 +1173,8 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
if (build_shapekey_layers && me->key &&
(kb = (KeyBlock *)BLI_findlink(&me->key->block, ob_eval->shapenr - 1))) {
- BKE_keyblock_convert_to_mesh(kb, me->mvert, me->totvert);
+ MutableSpan<MVert> verts = me->verts_for_write();
+ BKE_keyblock_convert_to_mesh(kb, verts.data(), me->totvert);
}
Mesh *mesh_temp = (Mesh *)BKE_id_copy_ex(nullptr, &me->id, nullptr, LIB_ID_COPY_LOCALIZE);
@@ -1359,242 +1240,113 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
return result;
}
-/* This is a Mesh-based copy of the same function in DerivedMesh.cc */
-static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int actshape_uid)
+static KeyBlock *keyblock_ensure_from_uid(Key &key, const int uid, const StringRefNull name)
{
- KeyBlock *kb;
- int i, j, tot;
-
- if (!mesh_dst->key) {
- return;
+ if (KeyBlock *kb = BKE_keyblock_find_uid(&key, uid)) {
+ return kb;
}
+ KeyBlock *kb = BKE_keyblock_add(&key, name.c_str());
+ kb->uid = uid;
+ return kb;
+}
- tot = CustomData_number_of_layers(&mesh_src->vdata, CD_SHAPEKEY);
- for (i = 0; i < tot; i++) {
- CustomDataLayer *layer =
- &mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)];
- float(*kbcos)[3];
-
- for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) {
- if (kb->uid == layer->uid) {
- break;
- }
- }
+static int find_object_active_key_uid(const Key &key, const Object &object)
+{
+ const int active_kb_index = object.shapenr - 1;
+ const KeyBlock *kb = (const KeyBlock *)BLI_findlink(&key.block, active_kb_index);
+ if (!kb) {
+ CLOG_ERROR(&LOG, "Could not find object's active shapekey %d", active_kb_index);
+ return -1;
+ }
+ return kb->uid;
+}
- if (!kb) {
- kb = BKE_keyblock_add(mesh_dst->key, layer->name);
- kb->uid = layer->uid;
- }
+static void move_shapekey_layers_to_keyblocks(Mesh &mesh, Key &key_dst, const int actshape_uid)
+{
+ using namespace blender::bke;
+ for (const int i : IndexRange(CustomData_number_of_layers(&mesh.vdata, CD_SHAPEKEY))) {
+ const int layer_index = CustomData_get_layer_index_n(&mesh.vdata, CD_SHAPEKEY, i);
+ CustomDataLayer &layer = mesh.vdata.layers[layer_index];
- if (kb->data) {
- MEM_freeN(kb->data);
- }
+ KeyBlock *kb = keyblock_ensure_from_uid(key_dst, layer.uid, layer.name);
+ MEM_SAFE_FREE(kb->data);
- const float(*cos)[3] = (const float(*)[3])CustomData_get_layer_n(
- &mesh_src->vdata, CD_SHAPEKEY, i);
- kb->totelem = mesh_src->totvert;
+ kb->totelem = mesh.totvert;
- kb->data = kbcos = (float(*)[3])MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__);
if (kb->uid == actshape_uid) {
- MVert *mvert = mesh_src->mvert;
-
- for (j = 0; j < mesh_src->totvert; j++, kbcos++, mvert++) {
- copy_v3_v3(*kbcos, mvert->co);
- }
+ kb->data = MEM_malloc_arrayN(kb->totelem, sizeof(float3), __func__);
+ MutableSpan<float3> kb_coords(static_cast<float3 *>(kb->data), kb->totelem);
+ mesh.attributes().lookup<float3>("position").materialize(kb_coords);
}
else {
- for (j = 0; j < kb->totelem; j++, cos++, kbcos++) {
- copy_v3_v3(*kbcos, *cos);
- }
+ kb->data = layer.data;
+ layer.data = nullptr;
}
}
- for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) {
- if (kb->totelem != mesh_src->totvert) {
- if (kb->data) {
- MEM_freeN(kb->data);
- }
-
- kb->totelem = mesh_src->totvert;
- kb->data = MEM_calloc_arrayN(kb->totelem, sizeof(float[3]), __func__);
- CLOG_ERROR(&LOG, "lost a shapekey layer: '%s'! (bmesh internal error)", kb->name);
+ LISTBASE_FOREACH (KeyBlock *, kb, &key_dst.block) {
+ if (kb->totelem != mesh.totvert) {
+ MEM_SAFE_FREE(kb->data);
}
+ kb->totelem = mesh.totvert;
+ kb->data = MEM_cnew_array<float3>(kb->totelem, __func__);
+ CLOG_ERROR(&LOG, "Data for shape key '%s' on mesh missing from evaluated mesh ", kb->name);
}
}
-void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
- Mesh *mesh_dst,
- Object *ob,
- const CustomData_MeshMasks *mask,
- bool take_ownership)
+void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob)
{
+ using namespace blender::bke;
BLI_assert(mesh_src->id.tag & LIB_TAG_NO_MAIN);
-
- /* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */
- /* TODO(Sybren): the above claim came from 2.7x derived-mesh code (DM_to_mesh);
- * check whether it is still true with Mesh */
- Mesh tmp = blender::dna::shallow_copy(*mesh_dst);
- int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly;
- bool did_shapekeys = false;
- eCDAllocType alloctype = CD_DUPLICATE;
-
- if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) {
- bool has_any_referenced_layers = CustomData_has_referenced(&mesh_src->vdata) ||
- CustomData_has_referenced(&mesh_src->edata) ||
- CustomData_has_referenced(&mesh_src->ldata) ||
- CustomData_has_referenced(&mesh_src->fdata) ||
- CustomData_has_referenced(&mesh_src->pdata);
- if (!has_any_referenced_layers) {
- alloctype = CD_ASSIGN;
- }
- }
- CustomData_reset(&tmp.vdata);
- CustomData_reset(&tmp.edata);
- CustomData_reset(&tmp.fdata);
- CustomData_reset(&tmp.ldata);
- CustomData_reset(&tmp.pdata);
-
- totvert = tmp.totvert = mesh_src->totvert;
- totedge = tmp.totedge = mesh_src->totedge;
- totloop = tmp.totloop = mesh_src->totloop;
- totpoly = tmp.totpoly = mesh_src->totpoly;
- tmp.totface = 0;
-
- CustomData_copy(&mesh_src->vdata, &tmp.vdata, mask->vmask, alloctype, totvert);
- CustomData_copy(&mesh_src->edata, &tmp.edata, mask->emask, alloctype, totedge);
- CustomData_copy(&mesh_src->ldata, &tmp.ldata, mask->lmask, alloctype, totloop);
- CustomData_copy(&mesh_src->pdata, &tmp.pdata, mask->pmask, alloctype, totpoly);
- tmp.cd_flag = mesh_src->cd_flag;
- tmp.runtime.deformed_only = mesh_src->runtime.deformed_only;
-
- /* Clear the normals completely, since the new vertex / polygon count might be different. */
- BKE_mesh_clear_derived_normals(&tmp);
-
- if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) {
- KeyBlock *kb;
- int uid;
-
- if (ob) {
- kb = (KeyBlock *)BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1);
- if (kb) {
- uid = kb->uid;
- }
- else {
- CLOG_ERROR(&LOG, "could not find active shapekey %d!", ob->shapenr - 1);
-
- uid = INT_MAX;
- }
- }
- else {
- /* if no object, set to INT_MAX so we don't mess up any shapekey layers */
- uid = INT_MAX;
- }
-
- shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid);
- did_shapekeys = true;
- }
-
- /* copy texture space */
if (ob) {
- BKE_mesh_texspace_copy_from_object(&tmp, ob);
+ BLI_assert(mesh_dst == ob->data);
}
- /* not all DerivedMeshes store their verts/edges/faces in CustomData, so
- * we set them here in case they are missing */
- /* TODO(Sybren): we could probably replace CD_ASSIGN with alloctype and
- * always directly pass mesh_src->mxxx, instead of using a ternary operator. */
- if (!CustomData_has_layer(&tmp.vdata, CD_MVERT)) {
- CustomData_add_layer(&tmp.vdata,
- CD_MVERT,
- CD_ASSIGN,
- (alloctype == CD_ASSIGN) ? mesh_src->mvert :
- MEM_dupallocN(mesh_src->mvert),
- totvert);
- }
- if (!CustomData_has_layer(&tmp.edata, CD_MEDGE)) {
- CustomData_add_layer(&tmp.edata,
- CD_MEDGE,
- CD_ASSIGN,
- (alloctype == CD_ASSIGN) ? mesh_src->medge :
- MEM_dupallocN(mesh_src->medge),
- totedge);
- }
- if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) {
- 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);
- }
+ BKE_mesh_clear_geometry(mesh_dst);
- /* object had got displacement layer, should copy this layer to save sculpted data */
- /* NOTE(nazgul): maybe some other layers should be copied? */
- if (CustomData_has_layer(&mesh_dst->ldata, CD_MDISPS)) {
- if (totloop == mesh_dst->totloop) {
- MDisps *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 nullptr to prevent double-free. */
- CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, nullptr);
- }
- }
- }
+ /* Make sure referenced layers have a single user so assigning them to the mesh in main doesn't
+ * share them. "Referenced" layers are not expected to be shared between original meshes. */
+ CustomData_duplicate_referenced_layers(&mesh_src->vdata, mesh_src->totvert);
+ CustomData_duplicate_referenced_layers(&mesh_src->edata, mesh_src->totedge);
+ CustomData_duplicate_referenced_layers(&mesh_src->pdata, mesh_src->totpoly);
+ CustomData_duplicate_referenced_layers(&mesh_src->ldata, mesh_src->totloop);
- /* yes, must be before _and_ after tessellate */
- BKE_mesh_update_customdata_pointers(&tmp, false);
-
- CustomData_free(&mesh_dst->vdata, mesh_dst->totvert);
- CustomData_free(&mesh_dst->edata, mesh_dst->totedge);
- CustomData_free(&mesh_dst->fdata, mesh_dst->totface);
- CustomData_free(&mesh_dst->ldata, mesh_dst->totloop);
- CustomData_free(&mesh_dst->pdata, mesh_dst->totpoly);
-
- /* ok, this should now use new CD shapekey data,
- * which should be fed through the modifier
- * stack */
- if (tmp.totvert != mesh_dst->totvert && !did_shapekeys && mesh_dst->key) {
- CLOG_ERROR(&LOG, "YEEK! this should be recoded! Shape key loss!: ID '%s'", tmp.id.name);
- if (tmp.key && !(tmp.id.tag & LIB_TAG_NO_MAIN)) {
- id_us_min(&tmp.key->id);
- }
- tmp.key = nullptr;
- }
+ mesh_dst->totvert = mesh_src->totvert;
+ mesh_dst->totedge = mesh_src->totedge;
+ mesh_dst->totpoly = mesh_src->totpoly;
+ mesh_dst->totloop = mesh_src->totloop;
- /* Clear selection history */
- MEM_SAFE_FREE(tmp.mselect);
- tmp.totselect = 0;
- tmp.texflag &= ~ME_AUTOSPACE_EVALUATED;
-
- /* Clear any run-time data.
- * Even though this mesh won't typically have run-time data, the Python API can for e.g.
- * create loop-triangle cache here, which is confusing when left in the mesh, see: T81136. */
- BKE_mesh_runtime_clear_geometry(&tmp);
-
- /* skip the listbase */
- MEMCPY_STRUCT_AFTER(mesh_dst, &tmp, id.prev);
+ /* Using #CD_MASK_MESH ensures that only data that should exist in Main meshes is moved. */
+ const CustomData_MeshMasks mask = CD_MASK_MESH;
+ CustomData_copy(&mesh_src->vdata, &mesh_dst->vdata, mask.vmask, CD_ASSIGN, mesh_src->totvert);
+ CustomData_copy(&mesh_src->edata, &mesh_dst->edata, mask.emask, CD_ASSIGN, mesh_src->totedge);
+ CustomData_copy(&mesh_src->pdata, &mesh_dst->pdata, mask.pmask, CD_ASSIGN, mesh_src->totpoly);
+ CustomData_copy(&mesh_src->ldata, &mesh_dst->ldata, mask.lmask, CD_ASSIGN, mesh_src->totloop);
BLI_freelistN(&mesh_dst->vertex_group_names);
- BKE_defgroup_copy_list(&mesh_dst->vertex_group_names, &mesh_src->vertex_group_names);
- mesh_dst->vertex_group_active_index = mesh_src->vertex_group_active_index;
-
- if (take_ownership) {
- if (alloctype == CD_ASSIGN) {
- CustomData_free_typemask(&mesh_src->vdata, mesh_src->totvert, ~mask->vmask);
- CustomData_free_typemask(&mesh_src->edata, mesh_src->totedge, ~mask->emask);
- CustomData_free_typemask(&mesh_src->ldata, mesh_src->totloop, ~mask->lmask);
- CustomData_free_typemask(&mesh_src->pdata, mesh_src->totpoly, ~mask->pmask);
+ mesh_dst->vertex_group_names = mesh_src->vertex_group_names;
+ BLI_listbase_clear(&mesh_src->vertex_group_names);
+
+ BKE_mesh_copy_parameters(mesh_dst, mesh_src);
+ mesh_dst->cd_flag = mesh_src->cd_flag;
+
+ /* For original meshes, shape key data is stored in the #Key data-block, so it
+ * must be moved from the storage in #CustomData layers used for evaluation. */
+ if (Key *key_dst = mesh_dst->key) {
+ if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) {
+ /* If no object, set to -1 so we don't mess up any shapekey layers. */
+ const int uid_active = ob ? find_object_active_key_uid(*key_dst, *ob) : -1;
+ move_shapekey_layers_to_keyblocks(*mesh_src, *key_dst, uid_active);
+ }
+ else if (mesh_src->totvert != mesh_dst->totvert) {
+ CLOG_ERROR(&LOG, "Mesh in Main '%s' lost shape keys", mesh_src->id.name);
+ if (mesh_src->key) {
+ id_us_min(&mesh_src->key->id);
+ }
}
- BKE_id_free(nullptr, mesh_src);
}
- BKE_mesh_assert_normals_dirty_or_calculated(mesh_dst);
+ BKE_id_free(nullptr, mesh_src);
}
void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb)
@@ -1603,7 +1355,6 @@ void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb)
int a, totvert = mesh_src->totvert;
float *fp;
- MVert *mvert;
if (totvert == 0 || mesh_dst->totvert == 0 || mesh_dst->totvert != totvert) {
return;
@@ -1616,9 +1367,8 @@ void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb)
kb->totelem = totvert;
fp = (float *)kb->data;
- mvert = mesh_src->mvert;
-
- for (a = 0; a < kb->totelem; a++, fp += 3, mvert++) {
- copy_v3_v3(fp, mvert->co);
+ const Span<MVert> verts = mesh_src->verts();
+ for (a = 0; a < kb->totelem; a++, fp += 3) {
+ copy_v3_v3(fp, verts[a].co);
}
}
diff --git a/source/blender/blenkernel/intern/mesh_debug.cc b/source/blender/blenkernel/intern/mesh_debug.cc
index 6f12726d364..8a9ce901923 100644
--- a/source/blender/blenkernel/intern/mesh_debug.cc
+++ b/source/blender/blenkernel/intern/mesh_debug.cc
@@ -30,12 +30,6 @@
static void mesh_debug_info_from_cd_flag(const Mesh *me, DynStr *dynstr)
{
BLI_dynstr_append(dynstr, "'cd_flag': {");
- if (me->cd_flag & ME_CDFLAG_VERT_BWEIGHT) {
- BLI_dynstr_append(dynstr, "'VERT_BWEIGHT', ");
- }
- if (me->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) {
- BLI_dynstr_append(dynstr, "'EDGE_BWEIGHT', ");
- }
if (me->cd_flag & ME_CDFLAG_EDGE_CREASE) {
BLI_dynstr_append(dynstr, "'EDGE_CREASE', ");
}
@@ -58,7 +52,8 @@ char *BKE_mesh_debug_info(const Mesh *me)
BLI_dynstr_appendf(dynstr, " 'totpoly': %d,\n", me->totpoly);
BLI_dynstr_appendf(dynstr, " 'runtime.deformed_only': %d,\n", me->runtime.deformed_only);
- BLI_dynstr_appendf(dynstr, " 'runtime.is_original': %d,\n", me->runtime.is_original);
+ BLI_dynstr_appendf(
+ dynstr, " 'runtime.is_original_bmesh': %d,\n", me->runtime.is_original_bmesh);
BLI_dynstr_append(dynstr, " 'vert_layers': (\n");
CustomData_debug_info_from_layers(&me->vdata, indent8, dynstr);
diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc
index 7d26262a504..938d7e42aa3 100644
--- a/source/blender/blenkernel/intern/mesh_evaluate.cc
+++ b/source/blender/blenkernel/intern/mesh_evaluate.cc
@@ -22,15 +22,17 @@
#include "BLI_math.h"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
+#include "BLI_virtual_array.hh"
#include "BKE_customdata.h"
+#include "BKE_attribute.hh"
#include "BKE_mesh.h"
#include "BKE_multires.h"
-using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
+using blender::VArray;
/* -------------------------------------------------------------------- */
/** \name Polygon Calculations
@@ -203,18 +205,13 @@ float BKE_mesh_calc_poly_area(const MPoly *mpoly, const MLoop *loopstart, const
float BKE_mesh_calc_area(const Mesh *me)
{
- MVert *mvert = me->mvert;
- MLoop *mloop = me->mloop;
- MPoly *mpoly = me->mpoly;
+ const Span<MVert> verts = me->verts();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
- MPoly *mp;
- int i = me->totpoly;
- float total_area = 0;
-
- for (mp = mpoly; i--; mp++) {
- MLoop *ml_start = &mloop[mp->loopstart];
-
- total_area += BKE_mesh_calc_poly_area(mp, ml_start, mvert);
+ float total_area = 0.0f;
+ for (const MPoly &poly : polys) {
+ total_area += BKE_mesh_calc_poly_area(&poly, &loops[poly.loopstart], verts.data());
}
return total_area;
}
@@ -403,11 +400,10 @@ void BKE_mesh_poly_edgebitmap_insert(uint *edge_bitmap, const MPoly *mp, const M
bool BKE_mesh_center_median(const Mesh *me, float r_cent[3])
{
- int i = me->totvert;
- const MVert *mvert;
+ const Span<MVert> verts = me->verts();
zero_v3(r_cent);
- for (mvert = me->mvert; i--; mvert++) {
- add_v3_v3(r_cent, mvert->co);
+ for (const MVert &vert : verts) {
+ add_v3_v3(r_cent, vert.co);
}
/* otherwise we get NAN for 0 verts */
if (me->totvert) {
@@ -418,18 +414,17 @@ bool BKE_mesh_center_median(const Mesh *me, float r_cent[3])
bool BKE_mesh_center_median_from_polys(const Mesh *me, float r_cent[3])
{
- int i = me->totpoly;
int tot = 0;
- const MPoly *mpoly = me->mpoly;
- const MLoop *mloop = me->mloop;
- const MVert *mvert = me->mvert;
+ const Span<MVert> verts = me->verts();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
zero_v3(r_cent);
- 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);
+ for (const MPoly &poly : polys) {
+ int loopend = poly.loopstart + poly.totloop;
+ for (int j = poly.loopstart; j < loopend; j++) {
+ add_v3_v3(r_cent, verts[loops[j].v].co);
}
- tot += mpoly->totloop;
+ tot += poly.totloop;
}
/* otherwise we get NAN for 0 verts */
if (me->totpoly) {
@@ -453,17 +448,19 @@ bool BKE_mesh_center_bounds(const Mesh *me, float r_cent[3])
bool BKE_mesh_center_of_surface(const Mesh *me, float r_cent[3])
{
int i = me->totpoly;
- MPoly *mpoly;
+ const MPoly *mpoly;
float poly_area;
float total_area = 0.0f;
float poly_cent[3];
+ const MVert *verts = BKE_mesh_verts(me);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
zero_v3(r_cent);
/* calculate a weighted average of polygon centroids */
- for (mpoly = me->mpoly; i--; mpoly++) {
- poly_area = mesh_calc_poly_area_centroid(
- mpoly, me->mloop + mpoly->loopstart, me->mvert, poly_cent);
+ for (mpoly = polys; i--; mpoly++) {
+ poly_area = mesh_calc_poly_area_centroid(mpoly, loops + mpoly->loopstart, verts, poly_cent);
madd_v3_v3fl(r_cent, poly_cent, poly_area);
total_area += poly_area;
@@ -484,10 +481,13 @@ bool BKE_mesh_center_of_surface(const Mesh *me, float r_cent[3])
bool BKE_mesh_center_of_volume(const Mesh *me, float r_cent[3])
{
int i = me->totpoly;
- MPoly *mpoly;
+ const MPoly *mpoly;
float poly_volume;
float total_volume = 0.0f;
float poly_cent[3];
+ const MVert *verts = BKE_mesh_verts(me);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
/* Use an initial center to avoid numeric instability of geometry far away from the center. */
float init_cent[3];
@@ -496,9 +496,9 @@ bool BKE_mesh_center_of_volume(const Mesh *me, float r_cent[3])
zero_v3(r_cent);
/* calculate a weighted average of polyhedron centroids */
- for (mpoly = me->mpoly; i--; mpoly++) {
+ for (mpoly = polys; i--; mpoly++) {
poly_volume = mesh_calc_poly_volume_centroid_with_reference_center(
- mpoly, me->mloop + mpoly->loopstart, me->mvert, init_cent, poly_cent);
+ mpoly, loops + mpoly->loopstart, verts, init_cent, poly_cent);
/* poly_cent is already volume-weighted, so no need to multiply by the volume */
add_v3_v3(r_cent, poly_cent);
@@ -668,7 +668,7 @@ void BKE_mesh_mdisp_flip(MDisps *md, const bool use_loop_mdisp_flip)
}
}
-void BKE_mesh_polygon_flip_ex(MPoly *mpoly,
+void BKE_mesh_polygon_flip_ex(const MPoly *mpoly,
MLoop *mloop,
CustomData *ldata,
float (*lnors)[3],
@@ -711,16 +711,16 @@ void BKE_mesh_polygon_flip_ex(MPoly *mpoly,
}
}
-void BKE_mesh_polygon_flip(MPoly *mpoly, MLoop *mloop, CustomData *ldata)
+void BKE_mesh_polygon_flip(const MPoly *mpoly, MLoop *mloop, CustomData *ldata)
{
MDisps *mdisp = (MDisps *)CustomData_get_layer(ldata, CD_MDISPS);
BKE_mesh_polygon_flip_ex(mpoly, mloop, ldata, nullptr, mdisp, true);
}
-void BKE_mesh_polygons_flip(MPoly *mpoly, MLoop *mloop, CustomData *ldata, int totpoly)
+void BKE_mesh_polys_flip(const MPoly *mpoly, MLoop *mloop, CustomData *ldata, int totpoly)
{
MDisps *mdisp = (MDisps *)CustomData_get_layer(ldata, CD_MDISPS);
- MPoly *mp;
+ const MPoly *mp;
int i;
for (mp = mpoly, i = 0; i < totpoly; mp++, i++) {
@@ -732,75 +732,90 @@ void BKE_mesh_polygons_flip(MPoly *mpoly, MLoop *mloop, CustomData *ldata, int t
/** \name Mesh Flag Flushing
* \{ */
-void BKE_mesh_flush_hidden_from_verts_ex(const MVert *mvert,
- const MLoop *mloop,
- MEdge *medge,
- const int totedge,
- MPoly *mpoly,
- const int totpoly)
+void BKE_mesh_flush_hidden_from_verts(Mesh *me)
{
- int i, j;
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = me->attributes_for_write();
- for (i = 0; i < totedge; i++) {
- MEdge *e = &medge[i];
- if (mvert[e->v1].flag & ME_HIDE || mvert[e->v2].flag & ME_HIDE) {
- e->flag |= ME_HIDE;
- }
- else {
- e->flag &= ~ME_HIDE;
- }
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+ if (hide_vert.is_single() && !hide_vert.get_internal_single()) {
+ attributes.remove(".hide_edge");
+ attributes.remove(".hide_poly");
+ return;
}
- for (i = 0; i < totpoly; i++) {
- MPoly *p = &mpoly[i];
- p->flag &= (char)~ME_HIDE;
- for (j = 0; j < p->totloop; j++) {
- if (mvert[mloop[p->loopstart + j].v].flag & ME_HIDE) {
- p->flag |= ME_HIDE;
- }
- }
+ const VArraySpan<bool> hide_vert_span{hide_vert};
+ const Span<MEdge> edges = me->edges();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
+
+ /* Hide edges when either of their vertices are hidden. */
+ SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_edge", ATTR_DOMAIN_EDGE);
+ for (const int i : edges.index_range()) {
+ const MEdge &edge = edges[i];
+ hide_edge.span[i] = hide_vert_span[edge.v1] || hide_vert_span[edge.v2];
}
-}
-void BKE_mesh_flush_hidden_from_verts(Mesh *me)
-{
- BKE_mesh_flush_hidden_from_verts_ex(
- me->mvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly);
+ hide_edge.finish();
+
+ /* Hide polygons when any of their vertices are hidden. */
+ SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE);
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ hide_poly.span[i] = std::any_of(poly_loops.begin(), poly_loops.end(), [&](const MLoop &loop) {
+ return hide_vert_span[loop.v];
+ });
+ }
+ hide_poly.finish();
}
-void BKE_mesh_flush_hidden_from_polys_ex(MVert *mvert,
- const MLoop *mloop,
- MEdge *medge,
- const int UNUSED(totedge),
- const MPoly *mpoly,
- const int totpoly)
+void BKE_mesh_flush_hidden_from_polys(Mesh *me)
{
- int i = totpoly;
- for (const MPoly *mp = mpoly; i--; mp++) {
- if (mp->flag & ME_HIDE) {
- const MLoop *ml;
- int j = mp->totloop;
- for (ml = &mloop[mp->loopstart]; j--; ml++) {
- mvert[ml->v].flag |= ME_HIDE;
- medge[ml->e].flag |= ME_HIDE;
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = me->attributes_for_write();
+
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
+ attributes.remove(".hide_vert");
+ attributes.remove(".hide_edge");
+ return;
+ }
+ const VArraySpan<bool> hide_poly_span{hide_poly};
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
+ SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT);
+ SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_edge", ATTR_DOMAIN_EDGE);
+
+ /* Hide all edges or vertices connected to hidden polygons. */
+ for (const int i : polys.index_range()) {
+ if (hide_poly_span[i]) {
+ const MPoly &poly = polys[i];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ hide_vert.span[loop.v] = true;
+ hide_edge.span[loop.e] = true;
}
}
}
-
- i = totpoly;
- for (const MPoly *mp = mpoly; i--; mp++) {
- if ((mp->flag & ME_HIDE) == 0) {
- const MLoop *ml;
- int j = mp->totloop;
- for (ml = &mloop[mp->loopstart]; j--; ml++) {
- mvert[ml->v].flag &= (char)~ME_HIDE;
- medge[ml->e].flag &= (short)~ME_HIDE;
+ /* Unhide vertices and edges connected to visible polygons. */
+ for (const int i : polys.index_range()) {
+ if (!hide_poly_span[i]) {
+ const MPoly &poly = polys[i];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ hide_vert.span[loop.v] = false;
+ hide_edge.span[loop.e] = false;
}
}
}
-}
-void BKE_mesh_flush_hidden_from_polys(Mesh *me)
-{
- BKE_mesh_flush_hidden_from_polys_ex(
- me->mvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly);
+
+ hide_vert.finish();
+ hide_edge.finish();
}
void BKE_mesh_flush_select_from_polys_ex(MVert *mvert,
@@ -842,17 +857,24 @@ void BKE_mesh_flush_select_from_polys_ex(MVert *mvert,
}
void BKE_mesh_flush_select_from_polys(Mesh *me)
{
- BKE_mesh_flush_select_from_polys_ex(
- me->mvert, me->totvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly);
+ BKE_mesh_flush_select_from_polys_ex(me->verts_for_write().data(),
+ me->totvert,
+ me->loops().data(),
+ me->edges_for_write().data(),
+ me->totedge,
+ me->polys().data(),
+ me->totpoly);
}
static void mesh_flush_select_from_verts(const Span<MVert> verts,
const Span<MLoop> loops,
+ const VArray<bool> &hide_edge,
+ const VArray<bool> &hide_poly,
MutableSpan<MEdge> edges,
MutableSpan<MPoly> polys)
{
for (const int i : edges.index_range()) {
- if ((edges[i].flag & ME_HIDE) == 0) {
+ if (!hide_edge[i]) {
MEdge &edge = edges[i];
if ((verts[edge.v1].flag & SELECT) && (verts[edge.v2].flag & SELECT)) {
edge.flag |= SELECT;
@@ -864,7 +886,7 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts,
}
for (const int i : polys.index_range()) {
- if (polys[i].flag & ME_HIDE) {
+ if (hide_poly[i]) {
continue;
}
MPoly &poly = polys[i];
@@ -885,10 +907,14 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts,
void BKE_mesh_flush_select_from_verts(Mesh *me)
{
- mesh_flush_select_from_verts({me->mvert, me->totvert},
- {me->mloop, me->totloop},
- {me->medge, me->totedge},
- {me->mpoly, me->totpoly});
+ const blender::bke::AttributeAccessor attributes = me->attributes();
+ mesh_flush_select_from_verts(
+ me->verts(),
+ me->loops(),
+ attributes.lookup_or_default<bool>(".hide_edge", ATTR_DOMAIN_EDGE, false),
+ attributes.lookup_or_default<bool>(".hide_poly", ATTR_DOMAIN_FACE, false),
+ me->edges_for_write(),
+ me->polys_for_write());
}
/** \} */
diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc
index 8936d7b0ba6..41dcb3501cc 100644
--- a/source/blender/blenkernel/intern/mesh_fair.cc
+++ b/source/blender/blenkernel/intern/mesh_fair.cc
@@ -27,6 +27,8 @@
#include "eigen_capi.h"
using blender::Map;
+using blender::MutableSpan;
+using blender::Span;
using blender::Vector;
using std::array;
@@ -74,13 +76,13 @@ class FairingContext {
virtual ~FairingContext() = default;
- void fair_vertices(bool *affected,
- const eMeshFairingDepth depth,
- VertexWeight *vertex_weight,
- LoopWeight *loop_weight)
+ void fair_verts(bool *affected,
+ const eMeshFairingDepth depth,
+ VertexWeight *vertex_weight,
+ LoopWeight *loop_weight)
{
- fair_vertices_ex(affected, (int)depth, vertex_weight, loop_weight);
+ fair_verts_ex(affected, (int)depth, vertex_weight, loop_weight);
}
protected:
@@ -141,28 +143,28 @@ class FairingContext {
loop_weight);
}
- void fair_vertices_ex(const bool *affected,
- const int order,
- VertexWeight *vertex_weight,
- LoopWeight *loop_weight)
+ void fair_verts_ex(const bool *affected,
+ const int order,
+ VertexWeight *vertex_weight,
+ LoopWeight *loop_weight)
{
Map<int, int> vert_col_map;
- int num_affected_vertices = 0;
+ int affected_verts_num = 0;
for (int i = 0; i < totvert_; i++) {
if (!affected[i]) {
continue;
}
- vert_col_map.add(i, num_affected_vertices);
- num_affected_vertices++;
+ vert_col_map.add(i, affected_verts_num);
+ affected_verts_num++;
}
/* Early return, nothing to do. */
- if (ELEM(num_affected_vertices, 0, totvert_)) {
+ if (ELEM(affected_verts_num, 0, totvert_)) {
return;
}
/* Setup fairing matrices */
- LinearSolver *solver = EIG_linear_solver_new(num_affected_vertices, num_affected_vertices, 3);
+ LinearSolver *solver = EIG_linear_solver_new(affected_verts_num, affected_verts_num, 3);
for (auto item : vert_col_map.items()) {
const int v = item.key;
const int col = item.value;
@@ -193,13 +195,14 @@ class MeshFairingContext : public FairingContext {
totvert_ = mesh->totvert;
totloop_ = mesh->totloop;
- medge_ = mesh->medge;
- mpoly_ = mesh->mpoly;
- mloop_ = mesh->mloop;
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ medge_ = mesh->edges();
+ mpoly_ = mesh->polys();
+ mloop_ = mesh->loops();
BKE_mesh_vert_loop_map_create(&vlmap_,
&vlmap_mem_,
- mesh->mpoly,
- mesh->mloop,
+ mpoly_.data(),
+ mloop_.data(),
mesh->totvert,
mesh->totpoly,
mesh->totloop);
@@ -213,14 +216,14 @@ class MeshFairingContext : public FairingContext {
}
else {
for (int i = 0; i < mesh->totvert; i++) {
- co_[i] = mesh->mvert[i].co;
+ co_[i] = verts[i].co;
}
}
loop_to_poly_map_.reserve(mesh->totloop);
for (int i = 0; i < mesh->totpoly; i++) {
- for (int l = 0; l < mesh->mpoly[i].totloop; l++) {
- loop_to_poly_map_[l + mesh->mpoly[i].loopstart] = i;
+ for (int l = 0; l < mpoly_[i].totloop; l++) {
+ loop_to_poly_map_[l + mpoly_[i].loopstart] = i;
}
}
}
@@ -244,7 +247,7 @@ class MeshFairingContext : public FairingContext {
int other_vertex_index_from_loop(const int loop, const uint v) override
{
- MEdge *e = &medge_[mloop_[loop].e];
+ const MEdge *e = &medge_[mloop_[loop].e];
if (e->v1 == v) {
return e->v2;
}
@@ -253,9 +256,9 @@ class MeshFairingContext : public FairingContext {
protected:
Mesh *mesh_;
- MLoop *mloop_;
- MPoly *mpoly_;
- MEdge *medge_;
+ Span<MLoop> mloop_;
+ Span<MPoly> mpoly_;
+ Span<MEdge> medge_;
Vector<int> loop_to_poly_map_;
};
@@ -447,42 +450,40 @@ class UniformLoopWeight : public LoopWeight {
}
};
-static void prefair_and_fair_vertices(FairingContext *fairing_context,
- bool *affected_vertices,
- const eMeshFairingDepth depth)
+static void prefair_and_fair_verts(FairingContext *fairing_context,
+ bool *affected_verts,
+ const eMeshFairingDepth depth)
{
/* Prefair. */
UniformVertexWeight *uniform_vertex_weights = new UniformVertexWeight(fairing_context);
UniformLoopWeight *uniform_loop_weights = new UniformLoopWeight();
- fairing_context->fair_vertices(
- affected_vertices, depth, uniform_vertex_weights, uniform_loop_weights);
+ fairing_context->fair_verts(affected_verts, depth, uniform_vertex_weights, uniform_loop_weights);
delete uniform_vertex_weights;
/* Fair. */
VoronoiVertexWeight *voronoi_vertex_weights = new VoronoiVertexWeight(fairing_context);
/* TODO: Implement cotangent loop weights. */
- fairing_context->fair_vertices(
- affected_vertices, depth, voronoi_vertex_weights, uniform_loop_weights);
+ fairing_context->fair_verts(affected_verts, depth, voronoi_vertex_weights, uniform_loop_weights);
delete uniform_loop_weights;
delete voronoi_vertex_weights;
}
-void BKE_mesh_prefair_and_fair_vertices(struct Mesh *mesh,
- struct MVert *deform_mverts,
- bool *affect_vertices,
- const eMeshFairingDepth depth)
+void BKE_mesh_prefair_and_fair_verts(struct Mesh *mesh,
+ struct MVert *deform_mverts,
+ bool *affect_verts,
+ const eMeshFairingDepth depth)
{
MeshFairingContext *fairing_context = new MeshFairingContext(mesh, deform_mverts);
- prefair_and_fair_vertices(fairing_context, affect_vertices, depth);
+ prefair_and_fair_verts(fairing_context, affect_verts, depth);
delete fairing_context;
}
-void BKE_bmesh_prefair_and_fair_vertices(struct BMesh *bm,
- bool *affect_vertices,
- const eMeshFairingDepth depth)
+void BKE_bmesh_prefair_and_fair_verts(struct BMesh *bm,
+ bool *affect_verts,
+ const eMeshFairingDepth depth)
{
BMeshFairingContext *fairing_context = new BMeshFairingContext(bm);
- prefair_and_fair_vertices(fairing_context, affect_vertices, depth);
+ prefair_and_fair_verts(fairing_context, affect_verts, depth);
delete fairing_context;
}
diff --git a/source/blender/blenkernel/intern/mesh_iterators.c b/source/blender/blenkernel/intern/mesh_iterators.c
index 77e62918441..d3a7f6cc72f 100644
--- a/source/blender/blenkernel/intern/mesh_iterators.c
+++ b/source/blender/blenkernel/intern/mesh_iterators.c
@@ -65,7 +65,7 @@ void BKE_mesh_foreach_mapped_vert(
}
}
else {
- const MVert *mv = mesh->mvert;
+ const MVert *mv = BKE_mesh_verts(mesh);
const int *index = CustomData_get_layer(&mesh->vdata, CD_ORIGINDEX);
const float(*vert_normals)[3] = (flag & MESH_FOREACH_USE_NORMAL) ?
BKE_mesh_vertex_normals_ensure(mesh) :
@@ -120,8 +120,8 @@ void BKE_mesh_foreach_mapped_edge(
}
}
else {
- const MVert *mv = mesh->mvert;
- const MEdge *med = mesh->medge;
+ const MVert *mv = BKE_mesh_verts(mesh);
+ const MEdge *med = BKE_mesh_edges(mesh);
const int *index = CustomData_get_layer(&mesh->edata, CD_ORIGINDEX);
if (index) {
@@ -188,9 +188,9 @@ void BKE_mesh_foreach_mapped_loop(Mesh *mesh,
CustomData_get_layer(&mesh->ldata, CD_NORMAL) :
NULL;
- const MVert *mv = mesh->mvert;
- const MLoop *ml = mesh->mloop;
- const MPoly *mp = mesh->mpoly;
+ const MVert *mv = BKE_mesh_verts(mesh);
+ const MLoop *ml = BKE_mesh_loops(mesh);
+ const MPoly *mp = BKE_mesh_polys(mesh);
const int *v_index = CustomData_get_layer(&mesh->vdata, CD_ORIGINDEX);
const int *f_index = CustomData_get_layer(&mesh->pdata, CD_ORIGINDEX);
int p_idx, i;
@@ -261,8 +261,9 @@ void BKE_mesh_foreach_mapped_face_center(
}
}
else {
- const MVert *mvert = mesh->mvert;
- const MPoly *mp = mesh->mpoly;
+ const MVert *mvert = BKE_mesh_verts(mesh);
+ const MPoly *mp = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
const MLoop *ml;
float _no_buf[3];
float *no = (flag & MESH_FOREACH_USE_NORMAL) ? _no_buf : NULL;
@@ -275,7 +276,7 @@ void BKE_mesh_foreach_mapped_face_center(
continue;
}
float cent[3];
- ml = &mesh->mloop[mp->loopstart];
+ ml = &loops[mp->loopstart];
BKE_mesh_calc_poly_center(mp, ml, mvert, cent);
if (flag & MESH_FOREACH_USE_NORMAL) {
BKE_mesh_calc_poly_normal(mp, ml, mvert, no);
@@ -286,7 +287,7 @@ void BKE_mesh_foreach_mapped_face_center(
else {
for (int i = 0; i < mesh->totpoly; i++, mp++) {
float cent[3];
- ml = &mesh->mloop[mp->loopstart];
+ ml = &loops[mp->loopstart];
BKE_mesh_calc_poly_center(mp, ml, mvert, cent);
if (flag & MESH_FOREACH_USE_NORMAL) {
BKE_mesh_calc_poly_normal(mp, ml, mvert, no);
@@ -303,7 +304,9 @@ void BKE_mesh_foreach_mapped_subdiv_face_center(
void *userData,
MeshForeachFlag flag)
{
- const MPoly *mp = mesh->mpoly;
+ const MVert *verts = BKE_mesh_verts(mesh);
+ const MPoly *mp = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
const MLoop *ml;
const MVert *mv;
const float(*vert_normals)[3] = (flag & MESH_FOREACH_USE_NORMAL) ?
@@ -319,9 +322,9 @@ void BKE_mesh_foreach_mapped_subdiv_face_center(
if (orig == ORIGINDEX_NONE) {
continue;
}
- ml = &mesh->mloop[mp->loopstart];
+ ml = &loops[mp->loopstart];
for (int j = 0; j < mp->totloop; j++, ml++) {
- mv = &mesh->mvert[ml->v];
+ mv = &verts[ml->v];
if (BLI_BITMAP_TEST(facedot_tags, ml->v)) {
func(userData,
orig,
@@ -333,9 +336,9 @@ void BKE_mesh_foreach_mapped_subdiv_face_center(
}
else {
for (int i = 0; i < mesh->totpoly; i++, mp++) {
- ml = &mesh->mloop[mp->loopstart];
+ ml = &loops[mp->loopstart];
for (int j = 0; j < mp->totloop; j++, ml++) {
- mv = &mesh->mvert[ml->v];
+ mv = &verts[ml->v];
if (BLI_BITMAP_TEST(facedot_tags, ml->v)) {
func(userData, i, mv->co, (flag & MESH_FOREACH_USE_NORMAL) ? vert_normals[ml->v] : NULL);
}
diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc
index 479dd6a012a..627c0057a28 100644
--- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc
+++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc
@@ -7,7 +7,7 @@
* Functions to convert mesh data to and from legacy formats like #MFace.
*/
-// #include <climits>
+#define DNA_DEPRECATED_ALLOW
#include "MEM_guardedalloc.h"
@@ -18,9 +18,12 @@
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_polyfill_2d.h"
+#include "BLI_task.hh"
#include "BLI_utildefines.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
+#include "BKE_global.h"
#include "BKE_mesh.h"
#include "BKE_mesh_legacy_convert.h"
#include "BKE_multires.h"
@@ -135,19 +138,19 @@ static void CustomData_to_bmeshpoly(CustomData *fdata, CustomData *ldata, int to
for (int i = 0; i < fdata->totlayer; i++) {
if (fdata->layers[i].type == CD_MTFACE) {
CustomData_add_layer_named(
- ldata, CD_MLOOPUV, CD_CALLOC, nullptr, totloop, fdata->layers[i].name);
+ ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, totloop, fdata->layers[i].name);
}
else if (fdata->layers[i].type == CD_MCOL) {
CustomData_add_layer_named(
- ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, nullptr, totloop, fdata->layers[i].name);
+ ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, totloop, fdata->layers[i].name);
}
else if (fdata->layers[i].type == CD_MDISPS) {
CustomData_add_layer_named(
- ldata, CD_MDISPS, CD_CALLOC, nullptr, totloop, fdata->layers[i].name);
+ ldata, CD_MDISPS, CD_SET_DEFAULT, nullptr, totloop, fdata->layers[i].name);
}
else if (fdata->layers[i].type == CD_TESSLOOPNORMAL) {
CustomData_add_layer_named(
- ldata, CD_NORMAL, CD_CALLOC, nullptr, totloop, fdata->layers[i].name);
+ ldata, CD_NORMAL, CD_SET_DEFAULT, nullptr, totloop, fdata->layers[i].name);
}
}
}
@@ -163,9 +166,7 @@ static void convert_mfaces_to_mpolys(ID *id,
MEdge *medge,
MFace *mface,
int *r_totloop,
- int *r_totpoly,
- MLoop **r_mloop,
- MPoly **r_mpoly)
+ int *r_totpoly)
{
MFace *mf;
MLoop *ml, *mloop;
@@ -183,8 +184,13 @@ static void convert_mfaces_to_mpolys(ID *id,
CustomData_free(pdata, totpoly_i);
totpoly = totface_i;
- mpoly = (MPoly *)MEM_calloc_arrayN((size_t)totpoly, sizeof(MPoly), "mpoly converted");
- CustomData_add_layer(pdata, CD_MPOLY, CD_ASSIGN, mpoly, totpoly);
+ mpoly = (MPoly *)CustomData_add_layer(pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, totpoly);
+ int *material_indices = static_cast<int *>(
+ CustomData_get_layer_named(pdata, CD_PROP_INT32, "material_index"));
+ if (material_indices == nullptr) {
+ material_indices = static_cast<int *>(CustomData_add_layer_named(
+ pdata, CD_PROP_INT32, CD_SET_DEFAULT, nullptr, totpoly, "material_index"));
+ }
numTex = CustomData_number_of_layers(fdata, CD_MTFACE);
numCol = CustomData_number_of_layers(fdata, CD_MCOL);
@@ -195,9 +201,7 @@ static void convert_mfaces_to_mpolys(ID *id,
totloop += mf->v4 ? 4 : 3;
}
- mloop = (MLoop *)MEM_calloc_arrayN((size_t)totloop, sizeof(MLoop), "mloop converted");
-
- CustomData_add_layer(ldata, CD_MLOOP, CD_ASSIGN, mloop, totloop);
+ mloop = (MLoop *)CustomData_add_layer(ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, totloop);
CustomData_to_bmeshpoly(fdata, ldata, totloop);
@@ -230,7 +234,7 @@ static void convert_mfaces_to_mpolys(ID *id,
mp->totloop = mf->v4 ? 4 : 3;
- mp->mat_nr = mf->mat_nr;
+ material_indices[i] = mf->mat_nr;
mp->flag = mf->flag;
#define ML(v1, v2) \
@@ -269,12 +273,51 @@ static void convert_mfaces_to_mpolys(ID *id,
*r_totpoly = totpoly;
*r_totloop = totloop;
- *r_mpoly = mpoly;
- *r_mloop = mloop;
#undef ME_FGON
}
+static void mesh_ensure_tessellation_customdata(Mesh *me)
+{
+ if (UNLIKELY((me->totface != 0) && (me->totpoly == 0))) {
+ /* Pass, otherwise this function clears 'mface' before
+ * versioning 'mface -> mpoly' code kicks in T30583.
+ *
+ * Callers could also check but safer to do here - campbell */
+ }
+ else {
+ const int tottex_original = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV);
+ const int totcol_original = CustomData_number_of_layers(&me->ldata, CD_PROP_BYTE_COLOR);
+
+ const int tottex_tessface = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+ const int totcol_tessface = CustomData_number_of_layers(&me->fdata, CD_MCOL);
+
+ if (tottex_tessface != tottex_original || totcol_tessface != totcol_original) {
+ BKE_mesh_tessface_clear(me);
+
+ BKE_mesh_add_mface_layers(&me->fdata, &me->ldata, me->totface);
+
+ /* TODO: add some `--debug-mesh` option. */
+ if (G.debug & G_DEBUG) {
+ /* NOTE(campbell): this warning may be un-called for if we are initializing the mesh for
+ * the first time from #BMesh, rather than giving a warning about this we could be smarter
+ * and check if there was any data to begin with, for now just print the warning with
+ * some info to help troubleshoot what's going on. */
+ printf(
+ "%s: warning! Tessellation uvs or vcol data got out of sync, "
+ "had to reset!\n CD_MTFACE: %d != CD_MLOOPUV: %d || CD_MCOL: %d != "
+ "CD_PROP_BYTE_COLOR: "
+ "%d\n",
+ __func__,
+ tottex_tessface,
+ tottex_original,
+ totcol_tessface,
+ totcol_original);
+ }
+ }
+ }
+}
+
void BKE_mesh_convert_mfaces_to_mpolys(Mesh *mesh)
{
convert_mfaces_to_mpolys(&mesh->id,
@@ -285,14 +328,12 @@ void BKE_mesh_convert_mfaces_to_mpolys(Mesh *mesh)
mesh->totface,
mesh->totloop,
mesh->totpoly,
- mesh->medge,
- mesh->mface,
+ mesh->edges_for_write().data(),
+ (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE),
&mesh->totloop,
- &mesh->totpoly,
- &mesh->mloop,
- &mesh->mpoly);
+ &mesh->totpoly);
- BKE_mesh_update_customdata_pointers(mesh, true);
+ mesh_ensure_tessellation_customdata(mesh);
}
/**
@@ -344,16 +385,14 @@ void BKE_mesh_do_versions_convert_mfaces_to_mpolys(Mesh *mesh)
mesh->totface,
mesh->totloop,
mesh->totpoly,
- mesh->medge,
- mesh->mface,
+ mesh->edges_for_write().data(),
+ (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE),
&mesh->totloop,
- &mesh->totpoly,
- &mesh->mloop,
- &mesh->mpoly);
+ &mesh->totpoly);
CustomData_bmesh_do_versions_update_active_layers(&mesh->fdata, &mesh->ldata);
- BKE_mesh_update_customdata_pointers(mesh, true);
+ mesh_ensure_tessellation_customdata(mesh);
}
/** \} */
@@ -562,6 +601,8 @@ static int mesh_tessface_calc(CustomData *fdata,
mpoly = (const MPoly *)CustomData_get_layer(pdata, CD_MPOLY);
mloop = (const MLoop *)CustomData_get_layer(ldata, CD_MLOOP);
+ const int *material_indices = static_cast<const int *>(
+ CustomData_get_layer_named(pdata, CD_PROP_INT32, "material_index"));
/* Allocate the length of `totfaces`, avoid many small reallocation's,
* if all faces are triangles it will be correct, `quads == 2x` allocations. */
@@ -600,7 +641,7 @@ static int mesh_tessface_calc(CustomData *fdata,
lidx[1] = l2; \
lidx[2] = l3; \
lidx[3] = 0; \
- mf->mat_nr = mp->mat_nr; \
+ mf->mat_nr = material_indices ? material_indices[poly_index] : 0; \
mf->flag = mp->flag; \
mf->edcode = 0; \
(void)0
@@ -623,7 +664,7 @@ static int mesh_tessface_calc(CustomData *fdata,
lidx[1] = l2; \
lidx[2] = l3; \
lidx[3] = l4; \
- mf->mat_nr = mp->mat_nr; \
+ mf->mat_nr = material_indices ? material_indices[poly_index] : 0; \
mf->flag = mp->flag; \
mf->edcode = TESSFACE_IS_QUAD; \
(void)0
@@ -709,7 +750,7 @@ static int mesh_tessface_calc(CustomData *fdata,
lidx[2] = l3;
lidx[3] = 0;
- mf->mat_nr = mp->mat_nr;
+ mf->mat_nr = material_indices ? material_indices[poly_index] : 0;
mf->flag = mp->flag;
mf->edcode = 0;
@@ -783,12 +824,12 @@ void BKE_mesh_tessface_calc(Mesh *mesh)
mesh->totface = mesh_tessface_calc(&mesh->fdata,
&mesh->ldata,
&mesh->pdata,
- mesh->mvert,
+ BKE_mesh_verts_for_write(mesh),
mesh->totface,
mesh->totloop,
mesh->totpoly);
- BKE_mesh_update_customdata_pointers(mesh, true);
+ mesh_ensure_tessellation_customdata(mesh);
}
void BKE_mesh_tessface_ensure(struct Mesh *mesh)
@@ -847,26 +888,27 @@ void BKE_mesh_add_mface_layers(CustomData *fdata, CustomData *ldata, int total)
for (int i = 0; i < ldata->totlayer; i++) {
if (ldata->layers[i].type == CD_MLOOPUV) {
CustomData_add_layer_named(
- fdata, CD_MTFACE, CD_CALLOC, nullptr, total, ldata->layers[i].name);
+ fdata, CD_MTFACE, CD_SET_DEFAULT, nullptr, total, ldata->layers[i].name);
}
if (ldata->layers[i].type == CD_PROP_BYTE_COLOR) {
- CustomData_add_layer_named(fdata, CD_MCOL, CD_CALLOC, nullptr, total, ldata->layers[i].name);
+ CustomData_add_layer_named(
+ fdata, CD_MCOL, CD_SET_DEFAULT, nullptr, total, ldata->layers[i].name);
}
else if (ldata->layers[i].type == CD_PREVIEW_MLOOPCOL) {
CustomData_add_layer_named(
- fdata, CD_PREVIEW_MCOL, CD_CALLOC, nullptr, total, ldata->layers[i].name);
+ fdata, CD_PREVIEW_MCOL, CD_SET_DEFAULT, nullptr, total, ldata->layers[i].name);
}
else if (ldata->layers[i].type == CD_ORIGSPACE_MLOOP) {
CustomData_add_layer_named(
- fdata, CD_ORIGSPACE, CD_CALLOC, nullptr, total, ldata->layers[i].name);
+ fdata, CD_ORIGSPACE, CD_SET_DEFAULT, nullptr, total, ldata->layers[i].name);
}
else if (ldata->layers[i].type == CD_NORMAL) {
CustomData_add_layer_named(
- fdata, CD_TESSLOOPNORMAL, CD_CALLOC, nullptr, total, ldata->layers[i].name);
+ fdata, CD_TESSLOOPNORMAL, CD_SET_DEFAULT, nullptr, total, ldata->layers[i].name);
}
else if (ldata->layers[i].type == CD_TANGENT) {
CustomData_add_layer_named(
- fdata, CD_TANGENT, CD_CALLOC, nullptr, total, ldata->layers[i].name);
+ fdata, CD_TANGENT, CD_SET_DEFAULT, nullptr, total, ldata->layers[i].name);
}
}
@@ -874,3 +916,189 @@ void BKE_mesh_add_mface_layers(CustomData *fdata, CustomData *ldata, int total)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Bevel Weight Conversion
+ * \{ */
+
+void BKE_mesh_legacy_bevel_weight_from_layers(Mesh *mesh)
+{
+ using namespace blender;
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ if (const float *weights = static_cast<const float *>(
+ CustomData_get_layer(&mesh->vdata, CD_BWEIGHT))) {
+ mesh->cd_flag |= ME_CDFLAG_VERT_BWEIGHT;
+ for (const int i : verts.index_range()) {
+ verts[i].bweight_legacy = std::clamp(weights[i], 0.0f, 1.0f) * 255.0f;
+ }
+ }
+ else {
+ mesh->cd_flag &= ~ME_CDFLAG_VERT_BWEIGHT;
+ for (const int i : verts.index_range()) {
+ verts[i].bweight_legacy = 0;
+ }
+ }
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ if (const float *weights = static_cast<const float *>(
+ CustomData_get_layer(&mesh->edata, CD_BWEIGHT))) {
+ mesh->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
+ for (const int i : edges.index_range()) {
+ edges[i].bweight_legacy = std::clamp(weights[i], 0.0f, 1.0f) * 255.0f;
+ }
+ }
+ else {
+ mesh->cd_flag &= ~ME_CDFLAG_EDGE_BWEIGHT;
+ for (const int i : edges.index_range()) {
+ edges[i].bweight_legacy = 0;
+ }
+ }
+}
+
+void BKE_mesh_legacy_bevel_weight_to_layers(Mesh *mesh)
+{
+ using namespace blender;
+ const Span<MVert> verts = mesh->verts();
+ if (mesh->cd_flag & ME_CDFLAG_VERT_BWEIGHT) {
+ float *weights = static_cast<float *>(
+ CustomData_add_layer(&mesh->vdata, CD_BWEIGHT, CD_CONSTRUCT, nullptr, verts.size()));
+ for (const int i : verts.index_range()) {
+ weights[i] = verts[i].bweight_legacy / 255.0f;
+ }
+ }
+
+ const Span<MEdge> edges = mesh->edges();
+ if (mesh->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) {
+ float *weights = static_cast<float *>(
+ CustomData_add_layer(&mesh->edata, CD_BWEIGHT, CD_CONSTRUCT, nullptr, edges.size()));
+ for (const int i : edges.index_range()) {
+ weights[i] = edges[i].bweight_legacy / 255.0f;
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Hide Attribute and Legacy Flag Conversion
+ * \{ */
+
+void BKE_mesh_legacy_convert_hide_layers_to_flags(Mesh *mesh)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ const AttributeAccessor attributes = mesh->attributes();
+
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+ threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ SET_FLAG_FROM_TEST(verts[i].flag, hide_vert[i], ME_HIDE);
+ }
+ });
+
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ const VArray<bool> hide_edge = attributes.lookup_or_default<bool>(
+ ".hide_edge", ATTR_DOMAIN_EDGE, false);
+ threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ SET_FLAG_FROM_TEST(edges[i].flag, hide_edge[i], ME_HIDE);
+ }
+ });
+
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ SET_FLAG_FROM_TEST(polys[i].flag, hide_poly[i], ME_HIDE);
+ }
+ });
+}
+
+void BKE_mesh_legacy_convert_flags_to_hide_layers(Mesh *mesh)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
+
+ const Span<MVert> verts = mesh->verts();
+ if (std::any_of(
+ verts.begin(), verts.end(), [](const MVert &vert) { return vert.flag & ME_HIDE; })) {
+ SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT);
+ threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ hide_vert.span[i] = verts[i].flag & ME_HIDE;
+ }
+ });
+ hide_vert.finish();
+ }
+
+ const Span<MEdge> edges = mesh->edges();
+ if (std::any_of(
+ edges.begin(), edges.end(), [](const MEdge &edge) { return edge.flag & ME_HIDE; })) {
+ SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_edge", ATTR_DOMAIN_EDGE);
+ threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ hide_edge.span[i] = edges[i].flag & ME_HIDE;
+ }
+ });
+ hide_edge.finish();
+ }
+
+ const Span<MPoly> polys = mesh->polys();
+ if (std::any_of(
+ polys.begin(), polys.end(), [](const MPoly &poly) { return poly.flag & ME_HIDE; })) {
+ SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE);
+ threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ hide_poly.span[i] = polys[i].flag & ME_HIDE;
+ }
+ });
+ hide_poly.finish();
+ }
+}
+
+/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Material Index Conversion
+ * \{ */
+
+void BKE_mesh_legacy_convert_material_indices_to_mpoly(Mesh *mesh)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ const AttributeAccessor attributes = mesh->attributes();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ polys[i].mat_nr_legacy = material_indices[i];
+ }
+ });
+}
+
+void BKE_mesh_legacy_convert_mpoly_to_material_indices(Mesh *mesh)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
+ const Span<MPoly> polys = mesh->polys();
+ if (std::any_of(
+ polys.begin(), polys.end(), [](const MPoly &poly) { return poly.mat_nr_legacy != 0; })) {
+ SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_only_span<int>(
+ "material_index", ATTR_DOMAIN_FACE);
+ threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ material_indices.span[i] = polys[i].mat_nr_legacy;
+ }
+ });
+ material_indices.finish();
+ }
+}
+
+/** \} */
diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.cc
index 9c4098e2db6..db091361223 100644
--- a/source/blender/blenkernel/intern/mesh_mapping.c
+++ b/source/blender/blenkernel/intern/mesh_mapping.cc
@@ -12,6 +12,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_vec_types.h"
+#include "BLI_array.hh"
#include "BLI_bitmap.h"
#include "BLI_buffer.h"
#include "BLI_math.h"
@@ -29,6 +30,7 @@
/* ngon version wip, based on BM_uv_vert_map_create */
UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly,
+ const bool *hide_poly,
const MLoop *mloop,
const MLoopUV *mloopuv,
uint totpoly,
@@ -51,7 +53,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly,
/* generate UvMapVert array */
mp = mpoly;
for (a = 0; a < totpoly; a++, mp++) {
- if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) {
+ if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) {
totuv += mp->totloop;
}
}
@@ -64,7 +66,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly,
buf = vmap->buf = (UvMapVert *)MEM_callocN(sizeof(*vmap->buf) * (size_t)totuv, "UvMapVert");
vmap->vert = (UvMapVert **)MEM_callocN(sizeof(*vmap->vert) * totvert, "UvMapVert*");
if (use_winding) {
- winding = MEM_callocN(sizeof(*winding) * totpoly, "winding");
+ winding = static_cast<bool *>(MEM_callocN(sizeof(*winding) * totpoly, "winding"));
}
if (!vmap->vert || !vmap->buf) {
@@ -74,7 +76,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly,
mp = mpoly;
for (a = 0; a < totpoly; a++, mp++) {
- if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) {
+ if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) {
float(*tf_uv)[2] = NULL;
if (use_winding) {
@@ -193,11 +195,12 @@ static void mesh_vert_poly_or_loop_map_create(MeshElemMap **r_map,
int totloop,
const bool do_loops)
{
- MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, __func__);
+ MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totvert, __func__);
int *indices, *index_iter;
int i, j;
- indices = index_iter = MEM_mallocN(sizeof(int) * (size_t)totloop, __func__);
+ indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totloop, __func__));
+ index_iter = indices;
/* Count number of polys for each vertex */
for (i = 0; i < totpoly; i++) {
@@ -264,8 +267,8 @@ void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map,
const MLoop *mloop,
const int UNUSED(totloop))
{
- MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, __func__);
- int *indices = MEM_mallocN(sizeof(int) * (size_t)totlooptri * 3, __func__);
+ MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totvert, __func__);
+ int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totlooptri * 3, __func__));
int *index_step;
const MLoopTri *mlt;
int i;
@@ -302,8 +305,8 @@ void BKE_mesh_vert_looptri_map_create(MeshElemMap **r_map,
void BKE_mesh_vert_edge_map_create(
MeshElemMap **r_map, int **r_mem, const MEdge *medge, int totvert, int totedge)
{
- MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, "vert-edge map");
- int *indices = MEM_mallocN(sizeof(int[2]) * (size_t)totedge, "vert-edge map mem");
+ MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totvert, __func__);
+ int *indices = static_cast<int *>(MEM_mallocN(sizeof(int[2]) * (size_t)totedge, __func__));
int *i_pt = indices;
int i;
@@ -341,8 +344,8 @@ void BKE_mesh_vert_edge_map_create(
void BKE_mesh_vert_edge_vert_map_create(
MeshElemMap **r_map, int **r_mem, const MEdge *medge, int totvert, int totedge)
{
- MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totvert, "vert-edge map");
- int *indices = MEM_mallocN(sizeof(int[2]) * (size_t)totedge, "vert-edge map mem");
+ MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totvert, __func__);
+ int *indices = static_cast<int *>(MEM_mallocN(sizeof(int[2]) * (size_t)totedge, __func__));
int *i_pt = indices;
int i;
@@ -386,8 +389,8 @@ void BKE_mesh_edge_loop_map_create(MeshElemMap **r_map,
const MLoop *mloop,
const int totloop)
{
- MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totedge, "edge-poly map");
- int *indices = MEM_mallocN(sizeof(int) * (size_t)totloop * 2, "edge-poly map mem");
+ MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totedge, __func__);
+ int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totloop * 2, __func__));
int *index_step;
const MPoly *mp;
int i;
@@ -439,8 +442,8 @@ void BKE_mesh_edge_poly_map_create(MeshElemMap **r_map,
const MLoop *mloop,
const int totloop)
{
- MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totedge, "edge-poly map");
- int *indices = MEM_mallocN(sizeof(int) * (size_t)totloop, "edge-poly map mem");
+ MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totedge, __func__);
+ int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totloop, __func__));
int *index_step;
const MPoly *mp;
int i;
@@ -484,8 +487,8 @@ void BKE_mesh_origindex_map_create(MeshElemMap **r_map,
const int *final_origindex,
const int totfinal)
{
- MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)totsource, "poly-tessface map");
- int *indices = MEM_mallocN(sizeof(int) * (size_t)totfinal, "poly-tessface map mem");
+ MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)totsource, __func__);
+ int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totfinal, __func__));
int *index_step;
int i;
@@ -526,8 +529,8 @@ void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map,
const MLoopTri *looptri,
const int looptri_num)
{
- MeshElemMap *map = MEM_callocN(sizeof(MeshElemMap) * (size_t)mpoly_num, "poly-tessface map");
- int *indices = MEM_mallocN(sizeof(int) * (size_t)looptri_num, "poly-tessface map mem");
+ MeshElemMap *map = MEM_cnew_array<MeshElemMap>((size_t)mpoly_num, __func__);
+ int *indices = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)looptri_num, __func__));
int *index_step;
int i;
@@ -558,13 +561,13 @@ void BKE_mesh_origindex_map_create_looptri(MeshElemMap **r_map,
/**
* Callback deciding whether the given poly/loop/edge define an island boundary or not.
*/
-typedef bool (*MeshRemap_CheckIslandBoundary)(const struct MPoly *mpoly,
- const struct MLoop *mloop,
- const struct MEdge *medge,
- const int edge_user_count,
- const struct MPoly *mpoly_array,
- const struct MeshElemMap *edge_poly_map,
- void *user_data);
+using MeshRemap_CheckIslandBoundary = bool (*)(const MPoly *mpoly,
+ const MLoop *mloop,
+ const MEdge *medge,
+ const int edge_user_count,
+ const MPoly *mpoly_array,
+ const MeshElemMap *edge_poly_map,
+ void *user_data);
static void poly_edge_loop_islands_calc(const MEdge *medge,
const int totedge,
@@ -619,8 +622,8 @@ static void poly_edge_loop_islands_calc(const MEdge *medge,
&edge_poly_map, &edge_poly_mem, medge, totedge, mpoly, totpoly, mloop, totloop);
}
- poly_groups = MEM_callocN(sizeof(int) * (size_t)totpoly, __func__);
- poly_stack = MEM_mallocN(sizeof(int) * (size_t)totpoly, __func__);
+ poly_groups = static_cast<int *>(MEM_callocN(sizeof(int) * (size_t)totpoly, __func__));
+ poly_stack = static_cast<int *>(MEM_mallocN(sizeof(int) * (size_t)totpoly, __func__));
while (true) {
int poly;
@@ -833,17 +836,17 @@ void BKE_mesh_loop_islands_init(MeshIslandStore *island_store,
island_store->item_type = item_type;
island_store->items_to_islands_num = items_num;
- island_store->items_to_islands = BLI_memarena_alloc(
- mem, sizeof(*island_store->items_to_islands) * (size_t)items_num);
+ island_store->items_to_islands = static_cast<int *>(
+ BLI_memarena_alloc(mem, sizeof(*island_store->items_to_islands) * (size_t)items_num));
island_store->island_type = island_type;
island_store->islands_num_alloc = MISLAND_DEFAULT_BUFSIZE;
- island_store->islands = BLI_memarena_alloc(
- mem, sizeof(*island_store->islands) * island_store->islands_num_alloc);
+ island_store->islands = static_cast<MeshElemMap **>(
+ BLI_memarena_alloc(mem, sizeof(*island_store->islands) * island_store->islands_num_alloc));
island_store->innercut_type = innercut_type;
- island_store->innercuts = BLI_memarena_alloc(
- mem, sizeof(*island_store->innercuts) * island_store->islands_num_alloc);
+ island_store->innercuts = static_cast<MeshElemMap **>(
+ BLI_memarena_alloc(mem, sizeof(*island_store->innercuts) * island_store->islands_num_alloc));
}
void BKE_mesh_loop_islands_clear(MeshIslandStore *island_store)
@@ -897,24 +900,29 @@ void BKE_mesh_loop_islands_add(MeshIslandStore *island_store,
MeshElemMap **islds, **innrcuts;
island_store->islands_num_alloc *= 2;
- islds = BLI_memarena_alloc(mem, sizeof(*islds) * island_store->islands_num_alloc);
+ islds = static_cast<MeshElemMap **>(
+ BLI_memarena_alloc(mem, sizeof(*islds) * island_store->islands_num_alloc));
memcpy(islds, island_store->islands, sizeof(*islds) * (curr_num_islands - 1));
island_store->islands = islds;
- innrcuts = BLI_memarena_alloc(mem, sizeof(*innrcuts) * island_store->islands_num_alloc);
+ innrcuts = static_cast<MeshElemMap **>(
+ BLI_memarena_alloc(mem, sizeof(*innrcuts) * island_store->islands_num_alloc));
memcpy(innrcuts, island_store->innercuts, sizeof(*innrcuts) * (curr_num_islands - 1));
island_store->innercuts = innrcuts;
}
- island_store->islands[curr_island_idx] = isld = BLI_memarena_alloc(mem, sizeof(*isld));
+ island_store->islands[curr_island_idx] = isld = static_cast<MeshElemMap *>(
+ BLI_memarena_alloc(mem, sizeof(*isld)));
isld->count = num_island_items;
- isld->indices = BLI_memarena_alloc(mem, sizeof(*isld->indices) * (size_t)num_island_items);
+ isld->indices = static_cast<int *>(
+ BLI_memarena_alloc(mem, sizeof(*isld->indices) * (size_t)num_island_items));
memcpy(isld->indices, island_item_indices, sizeof(*isld->indices) * (size_t)num_island_items);
- island_store->innercuts[curr_island_idx] = innrcut = BLI_memarena_alloc(mem, sizeof(*innrcut));
+ island_store->innercuts[curr_island_idx] = innrcut = static_cast<MeshElemMap *>(
+ BLI_memarena_alloc(mem, sizeof(*innrcut)));
innrcut->count = num_innercut_items;
- innrcut->indices = BLI_memarena_alloc(mem,
- sizeof(*innrcut->indices) * (size_t)num_innercut_items);
+ innrcut->indices = static_cast<int *>(
+ BLI_memarena_alloc(mem, sizeof(*innrcut->indices) * (size_t)num_innercut_items));
memcpy(innrcut->indices,
innercut_item_indices,
sizeof(*innrcut->indices) * (size_t)num_innercut_items);
@@ -926,11 +934,11 @@ void BKE_mesh_loop_islands_add(MeshIslandStore *island_store,
* Would make things much more complex though,
* and each UVMap would then need its own mesh mapping, not sure we want that at all!
*/
-typedef struct MeshCheckIslandBoundaryUv {
+struct MeshCheckIslandBoundaryUv {
const MLoop *loops;
const MLoopUV *luvs;
const MeshElemMap *edge_loop_map;
-} MeshCheckIslandBoundaryUv;
+};
static bool mesh_check_island_boundary_uv(const MPoly *UNUSED(mp),
const MLoop *ml,
@@ -941,7 +949,8 @@ static bool mesh_check_island_boundary_uv(const MPoly *UNUSED(mp),
void *user_data)
{
if (user_data) {
- const MeshCheckIslandBoundaryUv *data = user_data;
+ const MeshCheckIslandBoundaryUv *data = static_cast<const MeshCheckIslandBoundaryUv *>(
+ user_data);
const MLoop *loops = data->loops;
const MLoopUV *luvs = data->luvs;
const MeshElemMap *edge_to_loops = &data->edge_loop_map[ml->e];
@@ -975,13 +984,13 @@ static bool mesh_check_island_boundary_uv(const MPoly *UNUSED(mp),
return (me->flag & ME_SEAM) != 0;
}
-static bool mesh_calc_islands_loop_poly_uv(MVert *UNUSED(verts),
+static bool mesh_calc_islands_loop_poly_uv(const MVert *UNUSED(verts),
const int UNUSED(totvert),
- MEdge *edges,
+ const MEdge *edges,
const int totedge,
- MPoly *polys,
+ const MPoly *polys,
const int totpoly,
- MLoop *loops,
+ const MLoop *loops,
const int totloop,
const MLoopUV *luvs,
MeshIslandStore *r_island_store)
@@ -1055,13 +1064,16 @@ static bool mesh_calc_islands_loop_poly_uv(MVert *UNUSED(verts),
}
if (num_edge_borders) {
- edge_border_count = MEM_mallocN(sizeof(*edge_border_count) * (size_t)totedge, __func__);
- edge_innercut_indices = MEM_mallocN(sizeof(*edge_innercut_indices) * (size_t)num_edge_borders,
- __func__);
+ edge_border_count = static_cast<char *>(
+ MEM_mallocN(sizeof(*edge_border_count) * (size_t)totedge, __func__));
+ edge_innercut_indices = static_cast<int *>(
+ MEM_mallocN(sizeof(*edge_innercut_indices) * (size_t)num_edge_borders, __func__));
}
- poly_indices = MEM_mallocN(sizeof(*poly_indices) * (size_t)totpoly, __func__);
- loop_indices = MEM_mallocN(sizeof(*loop_indices) * (size_t)totloop, __func__);
+ poly_indices = static_cast<int *>(
+ MEM_mallocN(sizeof(*poly_indices) * (size_t)totpoly, __func__));
+ loop_indices = static_cast<int *>(
+ MEM_mallocN(sizeof(*loop_indices) * (size_t)totloop, __func__));
/* NOTE: here we ignore '0' invalid group - this should *never* happen in this case anyway? */
for (grp_idx = 1; grp_idx <= num_poly_groups; grp_idx++) {
@@ -1072,16 +1084,13 @@ static bool mesh_calc_islands_loop_poly_uv(MVert *UNUSED(verts),
}
for (p_idx = 0; p_idx < totpoly; p_idx++) {
- MPoly *mp;
-
if (poly_groups[p_idx] != grp_idx) {
continue;
}
-
- mp = &polys[p_idx];
+ const MPoly *mp = &polys[p_idx];
poly_indices[num_pidx++] = p_idx;
for (l_idx = mp->loopstart, pl_idx = 0; pl_idx < mp->totloop; l_idx++, pl_idx++) {
- MLoop *ml = &loops[l_idx];
+ const MLoop *ml = &loops[l_idx];
loop_indices[num_lidx++] = l_idx;
if (num_edge_borders && BLI_BITMAP_TEST(edge_borders, ml->e) &&
(edge_border_count[ml->e] < 2)) {
@@ -1125,13 +1134,13 @@ static bool mesh_calc_islands_loop_poly_uv(MVert *UNUSED(verts),
return true;
}
-bool BKE_mesh_calc_islands_loop_poly_edgeseam(MVert *verts,
+bool BKE_mesh_calc_islands_loop_poly_edgeseam(const MVert *verts,
const int totvert,
- MEdge *edges,
+ const MEdge *edges,
const int totedge,
- MPoly *polys,
+ const MPoly *polys,
const int totpoly,
- MLoop *loops,
+ const MLoop *loops,
const int totloop,
MeshIslandStore *r_island_store)
{
diff --git a/source/blender/blenkernel/intern/mesh_merge.c b/source/blender/blenkernel/intern/mesh_merge.c
index 4459e2514bc..9c0e3c1bf59 100644
--- a/source/blender/blenkernel/intern/mesh_merge.c
+++ b/source/blender/blenkernel/intern/mesh_merge.c
@@ -32,9 +32,9 @@
* and may be called again with direct_reverse=-1 for reverse order.
* \return 1 if polys are identical, 0 if polys are different.
*/
-static int cddm_poly_compare(MLoop *mloop_array,
- MPoly *mpoly_source,
- MPoly *mpoly_target,
+static int cddm_poly_compare(const MLoop *mloop_array,
+ const MPoly *mpoly_source,
+ const MPoly *mpoly_target,
const int *vtargetmap,
const int direct_reverse)
{
@@ -44,7 +44,7 @@ static int cddm_poly_compare(MLoop *mloop_array,
bool compare_completed = false;
bool same_loops = false;
- MLoop *mloop_source, *mloop_target;
+ const MLoop *mloop_source, *mloop_target;
BLI_assert(ELEM(direct_reverse, 1, -1));
@@ -203,10 +203,15 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
const int totedge = mesh->totedge;
const int totloop = mesh->totloop;
const int totpoly = mesh->totpoly;
+ const MVert *src_verts = BKE_mesh_verts(mesh);
+ const MEdge *src_edges = BKE_mesh_edges(mesh);
+ const MPoly *src_polys = BKE_mesh_polys(mesh);
+ const MLoop *src_loops = BKE_mesh_loops(mesh);
const int totvert_final = totvert - tot_vtargetmap;
- MVert *mv, *mvert = MEM_malloc_arrayN(totvert_final, sizeof(*mvert), __func__);
+ const MVert *mv;
+ MVert *mvert = MEM_malloc_arrayN(totvert_final, sizeof(*mvert), __func__);
int *oldv = MEM_malloc_arrayN(totvert_final, sizeof(*oldv), __func__);
int *newv = MEM_malloc_arrayN(totvert, sizeof(*newv), __func__);
STACK_DECLARE(mvert);
@@ -215,13 +220,15 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
/* NOTE: create (totedge + totloop) elements because partially invalid polys due to merge may
* require generating new edges, and while in 99% cases we'll still end with less final edges
* than totedge, cases can be forged that would end requiring more. */
- MEdge *med, *medge = MEM_malloc_arrayN((totedge + totloop), sizeof(*medge), __func__);
+ const MEdge *med;
+ MEdge *medge = MEM_malloc_arrayN((totedge + totloop), sizeof(*medge), __func__);
int *olde = MEM_malloc_arrayN((totedge + totloop), sizeof(*olde), __func__);
int *newe = MEM_malloc_arrayN((totedge + totloop), sizeof(*newe), __func__);
STACK_DECLARE(medge);
STACK_DECLARE(olde);
- MLoop *ml, *mloop = MEM_malloc_arrayN(totloop, sizeof(*mloop), __func__);
+ const MLoop *ml;
+ MLoop *mloop = MEM_malloc_arrayN(totloop, sizeof(*mloop), __func__);
int *oldl = MEM_malloc_arrayN(totloop, sizeof(*oldl), __func__);
#ifdef USE_LOOPS
int *newl = MEM_malloc_arrayN(totloop, sizeof(*newl), __func__);
@@ -229,7 +236,8 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
STACK_DECLARE(mloop);
STACK_DECLARE(oldl);
- MPoly *mp, *mpoly = MEM_malloc_arrayN(totpoly, sizeof(*medge), __func__);
+ const MPoly *mp;
+ MPoly *mpoly = MEM_malloc_arrayN(totpoly, sizeof(*medge), __func__);
int *oldp = MEM_malloc_arrayN(totpoly, sizeof(*oldp), __func__);
STACK_DECLARE(mpoly);
STACK_DECLARE(oldp);
@@ -254,7 +262,7 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
STACK_INIT(mpoly, totpoly);
/* fill newv with destination vertex indices */
- mv = mesh->mvert;
+ mv = src_verts;
c = 0;
for (i = 0; i < totvert; i++, mv++) {
if (vtargetmap[i] == -1) {
@@ -281,7 +289,7 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
*/
/* now go through and fix edges and faces */
- med = mesh->medge;
+ med = src_edges;
c = 0;
for (i = 0; i < totedge; i++, med++) {
const uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1;
@@ -316,12 +324,12 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
/* Duplicates allowed because our compare function is not pure equality */
BLI_gset_flag_set(poly_gset, GHASH_FLAG_ALLOW_DUPES);
- mp = mesh->mpoly;
+ mp = src_polys;
mpgh = poly_keys;
for (i = 0; i < totpoly; i++, mp++, mpgh++) {
mpgh->poly_index = i;
mpgh->totloops = mp->totloop;
- ml = mesh->mloop + mp->loopstart;
+ ml = src_loops + mp->loopstart;
mpgh->hash_sum = mpgh->hash_xor = 0;
for (j = 0; j < mp->totloop; j++, ml++) {
mpgh->hash_sum += ml->v;
@@ -333,24 +341,24 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
/* Can we optimize by reusing an old `pmap`? How do we know an old `pmap` is stale? */
/* When called by `MOD_array.c` the `cddm` has just been created, so it has no valid `pmap`. */
BKE_mesh_vert_poly_map_create(
- &poly_map, &poly_map_mem, mesh->mpoly, mesh->mloop, totvert, totpoly, totloop);
+ &poly_map, &poly_map_mem, src_polys, src_loops, totvert, totpoly, totloop);
} /* done preparing for fast poly compare */
BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__);
- mp = mesh->mpoly;
- mv = mesh->mvert;
+ mp = src_polys;
+ mv = src_verts;
for (i = 0; i < totpoly; i++, mp++) {
MPoly *mp_new;
- ml = mesh->mloop + mp->loopstart;
+ ml = src_loops + mp->loopstart;
/* check faces with all vertices merged */
- bool all_vertices_merged = true;
+ bool all_verts_merged = true;
for (j = 0; j < mp->totloop; j++, ml++) {
if (vtargetmap[ml->v] == -1) {
- all_vertices_merged = false;
+ all_verts_merged = false;
/* This will be used to check for poly using several time the same vert. */
BLI_BITMAP_DISABLE(vert_tag, ml->v);
}
@@ -360,7 +368,7 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
}
}
- if (UNLIKELY(all_vertices_merged)) {
+ if (UNLIKELY(all_verts_merged)) {
if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_MAPPED) {
/* In this mode, all vertices merged is enough to dump face */
continue;
@@ -376,7 +384,7 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
/* Use poly_gset for fast (although not 100% certain) identification of same poly */
/* First, make up a poly_summary structure */
- ml = mesh->mloop + mp->loopstart;
+ ml = src_loops + mp->loopstart;
pkey.hash_sum = pkey.hash_xor = 0;
pkey.totloops = 0;
for (j = 0; j < mp->totloop; j++, ml++) {
@@ -394,17 +402,17 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
*/
/* Consider current loop again */
- ml = mesh->mloop + mp->loopstart;
+ ml = src_loops + mp->loopstart;
/* Consider the target of the loop's first vert */
v_target = vtargetmap[ml->v];
/* Now see if v_target belongs to a poly that shares all vertices with source poly,
* in same order, or reverse order */
for (i_poly = 0; i_poly < poly_map[v_target].count; i_poly++) {
- MPoly *target_poly = mesh->mpoly + *(poly_map[v_target].indices + i_poly);
+ const MPoly *target_poly = src_polys + *(poly_map[v_target].indices + i_poly);
- if (cddm_poly_compare(mesh->mloop, mp, target_poly, vtargetmap, +1) ||
- cddm_poly_compare(mesh->mloop, mp, target_poly, vtargetmap, -1)) {
+ if (cddm_poly_compare(src_loops, mp, target_poly, vtargetmap, +1) ||
+ cddm_poly_compare(src_loops, mp, target_poly, vtargetmap, -1)) {
found = true;
break;
}
@@ -422,7 +430,7 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
* or they were all merged, but targets do not make up an identical poly,
* the poly is retained.
*/
- ml = mesh->mloop + mp->loopstart;
+ ml = src_loops + mp->loopstart;
c = 0;
MLoop *last_valid_ml = NULL;
@@ -434,9 +442,9 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
const uint mlv = (vtargetmap[ml->v] != -1) ? vtargetmap[ml->v] : ml->v;
#ifndef NDEBUG
{
- MLoop *next_ml = mesh->mloop + mp->loopstart + ((j + 1) % mp->totloop);
+ const MLoop *next_ml = src_loops + mp->loopstart + ((j + 1) % mp->totloop);
uint next_mlv = (vtargetmap[next_ml->v] != -1) ? vtargetmap[next_ml->v] : next_ml->v;
- med = mesh->medge + ml->e;
+ med = src_edges + ml->e;
uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1;
uint v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2;
BLI_assert((mlv == v1 && next_mlv == v2) || (mlv == v2 && next_mlv == v1));
@@ -461,7 +469,7 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
else {
const int new_eidx = STACK_SIZE(medge);
STACK_PUSH(olde, olde[last_valid_ml->e]);
- STACK_PUSH(medge, mesh->medge[last_valid_ml->e]);
+ STACK_PUSH(medge, src_edges[last_valid_ml->e]);
medge[new_eidx].v1 = last_valid_ml->v;
medge[new_eidx].v2 = ml->v;
/* DO NOT change newe mapping,
@@ -515,7 +523,7 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
else {
const int new_eidx = STACK_SIZE(medge);
STACK_PUSH(olde, olde[last_valid_ml->e]);
- STACK_PUSH(medge, mesh->medge[last_valid_ml->e]);
+ STACK_PUSH(medge, src_edges[last_valid_ml->e]);
medge[new_eidx].v1 = last_valid_ml->v;
medge[new_eidx].v2 = first_valid_ml->v;
/* DO NOT change newe mapping,
@@ -566,25 +574,25 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
mesh, STACK_SIZE(mvert), STACK_SIZE(medge), 0, STACK_SIZE(mloop), STACK_SIZE(mpoly));
/* Update edge indices and copy customdata. */
- med = medge;
- for (i = 0; i < result->totedge; i++, med++) {
- BLI_assert(newv[med->v1] != -1);
- med->v1 = newv[med->v1];
- BLI_assert(newv[med->v2] != -1);
- med->v2 = newv[med->v2];
+ MEdge *new_med = medge;
+ for (i = 0; i < result->totedge; i++, new_med++) {
+ BLI_assert(newv[new_med->v1] != -1);
+ new_med->v1 = newv[new_med->v1];
+ BLI_assert(newv[new_med->v2] != -1);
+ new_med->v2 = newv[new_med->v2];
/* Can happen in case vtargetmap contains some double chains, we do not support that. */
- BLI_assert(med->v1 != med->v2);
+ BLI_assert(new_med->v1 != new_med->v2);
CustomData_copy_data(&mesh->edata, &result->edata, olde[i], i, 1);
}
/* Update loop indices and copy customdata. */
- ml = mloop;
- for (i = 0; i < result->totloop; i++, ml++) {
+ MLoop *new_ml = mloop;
+ for (i = 0; i < result->totloop; i++, new_ml++) {
/* Edge remapping has already be done in main loop handling part above. */
- BLI_assert(newv[ml->v] != -1);
- ml->v = newv[ml->v];
+ BLI_assert(newv[new_ml->v] != -1);
+ new_ml->v = newv[new_ml->v];
CustomData_copy_data(&mesh->ldata, &result->ldata, oldl[i], i, 1);
}
@@ -603,16 +611,16 @@ Mesh *BKE_mesh_merge_verts(Mesh *mesh,
/* Copy over data. #CustomData_add_layer can do this, need to look it up. */
if (STACK_SIZE(mvert)) {
- memcpy(result->mvert, mvert, sizeof(MVert) * STACK_SIZE(mvert));
+ memcpy(BKE_mesh_verts_for_write(result), mvert, sizeof(MVert) * STACK_SIZE(mvert));
}
if (STACK_SIZE(medge)) {
- memcpy(result->medge, medge, sizeof(MEdge) * STACK_SIZE(medge));
+ memcpy(BKE_mesh_edges_for_write(result), medge, sizeof(MEdge) * STACK_SIZE(medge));
}
if (STACK_SIZE(mloop)) {
- memcpy(result->mloop, mloop, sizeof(MLoop) * STACK_SIZE(mloop));
+ memcpy(BKE_mesh_loops_for_write(result), mloop, sizeof(MLoop) * STACK_SIZE(mloop));
}
if (STACK_SIZE(mpoly)) {
- memcpy(result->mpoly, mpoly, sizeof(MPoly) * STACK_SIZE(mpoly));
+ memcpy(BKE_mesh_polys_for_write(result), mpoly, sizeof(MPoly) * STACK_SIZE(mpoly));
}
MEM_freeN(mvert);
diff --git a/source/blender/blenkernel/intern/mesh_merge_customdata.cc b/source/blender/blenkernel/intern/mesh_merge_customdata.cc
index adaf378ed27..f7936d8a4da 100644
--- a/source/blender/blenkernel/intern/mesh_merge_customdata.cc
+++ b/source/blender/blenkernel/intern/mesh_merge_customdata.cc
@@ -33,11 +33,11 @@ static int compare_v2_classify(const float uv_a[2], const float uv_b[2])
if (uv_a[0] == uv_b[0] && uv_a[1] == uv_b[1]) {
return CMP_EQUAL;
}
- /* Note that the ULP value is the primary value used to compare relative values
- * as the absolute value doesn't account for float precision at difference scales.
+ /* NOTE(@campbellbarton): that the ULP value is the primary value used to compare relative
+ * values as the absolute value doesn't account for float precision at difference scales.
* - For subdivision-surface ULP of 3 is sufficient,
* although this value is extremely small.
- * - For bevel the URL of 12 is sufficient to merge UV's that appear to be connected
+ * - For bevel the ULP of 12 is sufficient to merge UV's that appear to be connected
* with bevel on Suzanne beveled 15% with 6 segments.
*
* These values could be tweaked but should be kept on the small side to prevent
@@ -113,8 +113,13 @@ void BKE_mesh_merge_customdata_for_apply_modifier(Mesh *me)
int *vert_map_mem;
struct MeshElemMap *vert_to_loop;
- BKE_mesh_vert_loop_map_create(
- &vert_to_loop, &vert_map_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
+ BKE_mesh_vert_loop_map_create(&vert_to_loop,
+ &vert_map_mem,
+ BKE_mesh_polys(me),
+ BKE_mesh_loops(me),
+ me->totvert,
+ me->totpoly,
+ me->totloop);
Vector<MLoopUV *> mloopuv_layers;
mloopuv_layers.reserve(mloopuv_layers_num);
diff --git a/source/blender/blenkernel/intern/mesh_mirror.c b/source/blender/blenkernel/intern/mesh_mirror.c
index 715a1c9daf9..261bc3d150b 100644
--- a/source/blender/blenkernel/intern/mesh_mirror.c
+++ b/source/blender/blenkernel/intern/mesh_mirror.c
@@ -208,17 +208,17 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, maxLoops);
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, maxPolys);
- /* Subsurf for eg won't have mesh data in the custom-data arrays.
- * now add mvert/medge/mpoly layers. */
+ /* Subdivision-surface for eg won't have mesh data in the custom-data arrays.
+ * Now add #MVert/#MEdge/#MPoly layers. */
if (!CustomData_has_layer(&mesh->vdata, CD_MVERT)) {
- memcpy(result->mvert, mesh->mvert, sizeof(*result->mvert) * mesh->totvert);
+ memcpy(BKE_mesh_verts_for_write(result), BKE_mesh_verts(mesh), sizeof(MVert) * mesh->totvert);
}
if (!CustomData_has_layer(&mesh->edata, CD_MEDGE)) {
- memcpy(result->medge, mesh->medge, sizeof(*result->medge) * mesh->totedge);
+ memcpy(BKE_mesh_edges_for_write(result), BKE_mesh_edges(mesh), sizeof(MEdge) * mesh->totedge);
}
if (!CustomData_has_layer(&mesh->pdata, CD_MPOLY)) {
- memcpy(result->mloop, mesh->mloop, sizeof(*result->mloop) * mesh->totloop);
- memcpy(result->mpoly, mesh->mpoly, sizeof(*result->mpoly) * mesh->totpoly);
+ memcpy(BKE_mesh_loops_for_write(result), BKE_mesh_loops(mesh), sizeof(MLoop) * mesh->totloop);
+ memcpy(BKE_mesh_polys_for_write(result), BKE_mesh_polys(mesh), sizeof(MPoly) * mesh->totpoly);
}
/* Copy custom-data to new geometry,
@@ -237,7 +237,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
}
/* mirror vertex coordinates */
- mv_prev = result->mvert;
+ mv_prev = BKE_mesh_verts_for_write(result);
mv = mv_prev + maxVerts;
for (i = 0; i < maxVerts; i++, mv++, mv_prev++) {
mul_m4_v3(mtx, mv->co);
@@ -304,15 +304,15 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
}
/* adjust mirrored edge vertex indices */
- me = result->medge + maxEdges;
+ me = BKE_mesh_edges_for_write(result) + maxEdges;
for (i = 0; i < maxEdges; i++, me++) {
me->v1 += maxVerts;
me->v2 += maxVerts;
}
/* adjust mirrored poly loopstart indices, and reverse loop order (normals) */
- mp = result->mpoly + maxPolys;
- ml = result->mloop;
+ mp = BKE_mesh_polys_for_write(result) + maxPolys;
+ ml = BKE_mesh_loops_for_write(result);
for (i = 0; i < maxPolys; i++, mp++) {
MLoop *ml2;
int j, e;
@@ -341,7 +341,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
}
/* adjust mirrored loop vertex and edge indices */
- ml = result->mloop + maxLoops;
+ ml = BKE_mesh_loops_for_write(result) + maxLoops;
for (i = 0; i < maxLoops; i++, ml++) {
ml->v += maxVerts;
ml->e += maxEdges;
@@ -405,15 +405,15 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
/* calculate custom normals into loop_normals, then mirror first half into second half */
- BKE_mesh_normals_loop_split(result->mvert,
+ BKE_mesh_normals_loop_split(BKE_mesh_verts(result),
BKE_mesh_vertex_normals_ensure(result),
result->totvert,
- result->medge,
+ BKE_mesh_edges(result),
result->totedge,
- result->mloop,
+ BKE_mesh_loops(result),
loop_normals,
totloop,
- result->mpoly,
+ BKE_mesh_polys(result),
BKE_mesh_poly_normals_ensure(result),
totpoly,
true,
@@ -423,9 +423,10 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
NULL);
/* mirroring has to account for loops being reversed in polys in second half */
- mp = result->mpoly;
+ MPoly *result_polys = BKE_mesh_polys_for_write(result);
+ mp = result_polys;
for (i = 0; i < maxPolys; i++, mp++) {
- MPoly *mpmirror = result->mpoly + maxPolys + i;
+ MPoly *mpmirror = result_polys + maxPolys + i;
int j;
for (j = mp->loopstart; j < mp->loopstart + mp->totloop; j++) {
@@ -446,11 +447,10 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
/* handle vgroup stuff */
if ((mmd->flag & MOD_MIR_VGROUP) && CustomData_has_layer(&result->vdata, CD_MDEFORMVERT)) {
- MDeformVert *dvert = (MDeformVert *)CustomData_get_layer(&result->vdata, CD_MDEFORMVERT) +
- maxVerts;
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(result) + maxVerts;
int *flip_map = NULL, flip_map_len = 0;
- flip_map = BKE_object_defgroup_flip_map(ob, &flip_map_len, false);
+ flip_map = BKE_object_defgroup_flip_map(ob, false, &flip_map_len);
if (flip_map) {
for (i = 0; i < maxVerts; dvert++, i++) {
diff --git a/source/blender/blenkernel/intern/mesh_normals.cc b/source/blender/blenkernel/intern/mesh_normals.cc
index 2366b7526a1..21dd39586ec 100644
--- a/source/blender/blenkernel/intern/mesh_normals.cc
+++ b/source/blender/blenkernel/intern/mesh_normals.cc
@@ -17,8 +17,7 @@
#include "DNA_meshdata_types.h"
#include "BLI_alloca.h"
-#include "BLI_bitmap.h"
-
+#include "BLI_bit_vector.hh"
#include "BLI_linklist.h"
#include "BLI_linklist_stack.h"
#include "BLI_math.h"
@@ -37,6 +36,8 @@
#include "atomic_ops.h"
+using blender::BitVector;
+using blender::MutableSpan;
using blender::Span;
// #define DEBUG_TIME
@@ -368,16 +369,19 @@ const float (*BKE_mesh_vertex_normals_ensure(const Mesh *mesh))[3]
/* Isolate task because a mutex is locked and computing normals is multi-threaded. */
blender::threading::isolate_task([&]() {
Mesh &mesh_mutable = *const_cast<Mesh *>(mesh);
+ const Span<MVert> verts = mesh_mutable.verts();
+ const Span<MPoly> polys = mesh_mutable.polys();
+ const Span<MLoop> loops = mesh_mutable.loops();
vert_normals = BKE_mesh_vertex_normals_for_write(&mesh_mutable);
poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable);
- BKE_mesh_calc_normals_poly_and_vertex(mesh_mutable.mvert,
- mesh_mutable.totvert,
- mesh_mutable.mloop,
- mesh_mutable.totloop,
- mesh_mutable.mpoly,
- mesh_mutable.totpoly,
+ BKE_mesh_calc_normals_poly_and_vertex(verts.data(),
+ verts.size(),
+ loops.data(),
+ loops.size(),
+ polys.data(),
+ polys.size(),
poly_normals,
vert_normals);
@@ -413,15 +417,18 @@ const float (*BKE_mesh_poly_normals_ensure(const Mesh *mesh))[3]
/* Isolate task because a mutex is locked and computing normals is multi-threaded. */
blender::threading::isolate_task([&]() {
Mesh &mesh_mutable = *const_cast<Mesh *>(mesh);
+ const Span<MVert> verts = mesh_mutable.verts();
+ const Span<MPoly> polys = mesh_mutable.polys();
+ const Span<MLoop> loops = mesh_mutable.loops();
poly_normals = BKE_mesh_poly_normals_for_write(&mesh_mutable);
- BKE_mesh_calc_normals_poly(mesh_mutable.mvert,
- mesh_mutable.totvert,
- mesh_mutable.mloop,
- mesh_mutable.totloop,
- mesh_mutable.mpoly,
- mesh_mutable.totpoly,
+ BKE_mesh_calc_normals_poly(verts.data(),
+ verts.size(),
+ loops.data(),
+ loops.size(),
+ polys.data(),
+ polys.size(),
poly_normals);
BKE_mesh_poly_normals_clear_dirty(&mesh_mutable);
@@ -849,7 +856,10 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data,
int(*edge_to_loops)[2] = data->edge_to_loops;
int *loop_to_poly = data->loop_to_poly;
- BLI_bitmap *sharp_edges = do_sharp_edges_tag ? BLI_BITMAP_NEW(numEdges, __func__) : nullptr;
+ BitVector sharp_edges;
+ if (do_sharp_edges_tag) {
+ sharp_edges.resize(numEdges, false);
+ }
const MPoly *mp;
int mp_index;
@@ -901,7 +911,7 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data,
/* We want to avoid tagging edges as sharp when it is already defined as such by
* other causes than angle threshold. */
if (do_sharp_edges_tag && is_angle_sharp) {
- BLI_BITMAP_SET(sharp_edges, ml_curr->e, true);
+ sharp_edges[ml_curr->e].set();
}
}
else {
@@ -915,7 +925,7 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data,
/* We want to avoid tagging edges as sharp when it is already defined as such by
* other causes than angle threshold. */
if (do_sharp_edges_tag) {
- BLI_BITMAP_SET(sharp_edges, ml_curr->e, false);
+ sharp_edges[ml_curr->e].reset();
}
}
/* Else, edge is already 'disqualified' (i.e. sharp)! */
@@ -927,12 +937,10 @@ static void mesh_edges_sharp_tag(LoopSplitTaskDataCommon *data,
MEdge *me;
int me_index;
for (me = (MEdge *)medges, me_index = 0; me_index < numEdges; me++, me_index++) {
- if (BLI_BITMAP_TEST(sharp_edges, me_index)) {
+ if (sharp_edges[me_index]) {
me->flag |= ME_SHARP;
}
}
-
- MEM_freeN(sharp_edges);
}
}
@@ -940,9 +948,9 @@ void BKE_edges_sharp_from_angle_set(const struct MVert *mverts,
const int UNUSED(numVerts),
struct MEdge *medges,
const int numEdges,
- struct MLoop *mloops,
+ const struct MLoop *mloops,
const int numLoops,
- struct MPoly *mpolys,
+ const struct MPoly *mpolys,
const float (*polynors)[3],
const int numPolys,
const float split_angle)
@@ -1354,7 +1362,7 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops,
const int (*edge_to_loops)[2],
const int *loop_to_poly,
const int *e2l_prev,
- BLI_bitmap *skip_loops,
+ BitVector<> &skip_loops,
const MLoop *ml_curr,
const MLoop *ml_prev,
const int ml_curr_index,
@@ -1383,8 +1391,8 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops,
BLI_assert(mlfan_vert_index >= 0);
BLI_assert(mpfan_curr_index >= 0);
- BLI_assert(!BLI_BITMAP_TEST(skip_loops, mlfan_vert_index));
- BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index);
+ BLI_assert(!skip_loops[mlfan_vert_index]);
+ skip_loops[mlfan_vert_index].set();
while (true) {
/* Find next loop of the smooth fan. */
@@ -1405,7 +1413,7 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops,
return false;
}
/* Smooth loop/edge. */
- if (BLI_BITMAP_TEST(skip_loops, mlfan_vert_index)) {
+ if (skip_loops[mlfan_vert_index]) {
if (mlfan_vert_index == ml_curr_index) {
/* We walked around a whole cyclic smooth fan without finding any already-processed loop,
* means we can use initial `ml_curr` / `ml_prev` edge as start for this smooth fan. */
@@ -1416,7 +1424,7 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const MLoop *mloops,
}
/* We can skip it in future, and keep checking the smooth fan. */
- BLI_BITMAP_ENABLE(skip_loops, mlfan_vert_index);
+ skip_loops[mlfan_vert_index].set();
}
}
@@ -1440,7 +1448,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
int ml_curr_index;
int ml_prev_index;
- BLI_bitmap *skip_loops = BLI_BITMAP_NEW(numLoops, __func__);
+ BitVector<> skip_loops(numLoops, false);
LoopSplitTaskData *data_buff = nullptr;
int data_idx = 0;
@@ -1482,7 +1490,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
ml_curr->e,
ml_curr->v,
IS_EDGE_SHARP(e2l_curr),
- BLI_BITMAP_TEST_BOOL(skip_loops, ml_curr_index));
+ skip_loops[ml_curr_index]);
#endif
/* A smooth edge, we have to check for cyclic smooth fan case.
@@ -1495,7 +1503,7 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
* However, this would complicate the code, add more memory usage, and despite its logical
* complexity, #loop_manifold_fan_around_vert_next() is quite cheap in term of CPU cycles,
* so really think it's not worth it. */
- if (!IS_EDGE_SHARP(e2l_curr) && (BLI_BITMAP_TEST(skip_loops, ml_curr_index) ||
+ if (!IS_EDGE_SHARP(e2l_curr) && (skip_loops[ml_curr_index] ||
!loop_split_generator_check_cyclic_smooth_fan(mloops,
mpolys,
edge_to_loops,
@@ -1590,7 +1598,6 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
if (edge_vectors) {
BLI_stack_free(edge_vectors);
}
- MEM_freeN(skip_loops);
#ifdef DEBUG_TIME
TIMEIT_END_AVERAGED(loop_split_generator);
@@ -1600,12 +1607,12 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
void BKE_mesh_normals_loop_split(const MVert *mverts,
const float (*vert_normals)[3],
const int UNUSED(numVerts),
- MEdge *medges,
+ const MEdge *medges,
const int numEdges,
- MLoop *mloops,
+ const MLoop *mloops,
float (*r_loopnors)[3],
const int numLoops,
- MPoly *mpolys,
+ const MPoly *mpolys,
const float (*polynors)[3],
const int numPolys,
const bool use_split_normals,
@@ -1628,7 +1635,7 @@ void BKE_mesh_normals_loop_split(const MVert *mverts,
int mp_index;
for (mp_index = 0; mp_index < numPolys; mp_index++) {
- MPoly *mp = &mpolys[mp_index];
+ const MPoly *mp = &mpolys[mp_index];
int ml_index = mp->loopstart;
const int ml_index_end = ml_index + mp->totloop;
const bool is_poly_flat = ((mp->flag & ME_SMOOTH) == 0);
@@ -1755,10 +1762,10 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
const int numVerts,
MEdge *medges,
const int numEdges,
- MLoop *mloops,
+ const MLoop *mloops,
float (*r_custom_loopnors)[3],
const int numLoops,
- MPoly *mpolys,
+ const MPoly *mpolys,
const float (*polynors)[3],
const int numPolys,
short (*r_clnors_data)[2],
@@ -1771,7 +1778,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
* (and perhaps from some editing tools later?).
* So better to keep some simplicity here, and just call #BKE_mesh_normals_loop_split() twice! */
MLoopNorSpaceArray lnors_spacearr = {nullptr};
- BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)numLoops, __func__);
+ BitVector<> done_loops(numLoops, false);
float(*lnors)[3] = (float(*)[3])MEM_calloc_arrayN((size_t)numLoops, sizeof(*lnors), __func__);
int *loop_to_poly = (int *)MEM_malloc_arrayN((size_t)numLoops, sizeof(int), __func__);
/* In this case we always consider split nors as ON,
@@ -1829,14 +1836,14 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
/* This should not happen in theory, but in some rare case (probably ugly geometry)
* we can get some nullptr loopspacearr at this point. :/
* Maybe we should set those loops' edges as sharp? */
- BLI_BITMAP_ENABLE(done_loops, i);
+ done_loops[i].set();
if (G.debug & G_DEBUG) {
printf("WARNING! Getting invalid nullptr loop space for loop %d!\n", i);
}
continue;
}
- if (!BLI_BITMAP_TEST(done_loops, i)) {
+ if (!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
@@ -1847,17 +1854,17 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
* 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);
+ done_loops[i].set();
continue;
}
LinkNode *loops = lnors_spacearr.lspacearr[i]->loops;
- MLoop *prev_ml = nullptr;
+ const MLoop *prev_ml = nullptr;
const float *org_nor = nullptr;
while (loops) {
const int lidx = POINTER_AS_INT(loops->link);
- MLoop *ml = &mloops[lidx];
+ const MLoop *ml = &mloops[lidx];
const int nidx = lidx;
float *nor = r_custom_loopnors[nidx];
@@ -1879,7 +1886,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
prev_ml = ml;
loops = loops->next;
- BLI_BITMAP_ENABLE(done_loops, lidx);
+ done_loops[lidx].set();
}
/* We also have to check between last and first loops,
@@ -1889,7 +1896,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
loops = lnors_spacearr.lspacearr[i]->loops;
if (loops && org_nor) {
const int lidx = POINTER_AS_INT(loops->link);
- MLoop *ml = &mloops[lidx];
+ const MLoop *ml = &mloops[lidx];
const int nidx = lidx;
float *nor = r_custom_loopnors[nidx];
@@ -1923,14 +1930,14 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
loop_to_poly);
}
else {
- BLI_bitmap_set_all(done_loops, true, (size_t)numLoops);
+ done_loops.fill(true);
}
/* And we just have to convert plain object-space custom normals to our
* lnor space-encoded ones. */
for (int i = 0; i < numLoops; i++) {
if (!lnors_spacearr.lspacearr[i]) {
- BLI_BITMAP_DISABLE(done_loops, i);
+ done_loops[i].reset();
if (G.debug & G_DEBUG) {
printf("WARNING! Still getting invalid nullptr loop space in second loop for loop %d!\n",
i);
@@ -1938,7 +1945,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
continue;
}
- if (BLI_BITMAP_TEST_BOOL(done_loops, i)) {
+ if (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). */
@@ -1949,7 +1956,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
float *nor = r_custom_loopnors[nidx];
BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], nor, r_clnors_data[i]);
- BLI_BITMAP_DISABLE(done_loops, i);
+ done_loops[i].reset();
}
else {
int avg_nor_count = 0;
@@ -1967,7 +1974,7 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
BLI_SMALLSTACK_PUSH(clnors_data, (short *)r_clnors_data[lidx]);
loops = loops->next;
- BLI_BITMAP_DISABLE(done_loops, lidx);
+ done_loops[lidx].reset();
}
mul_v3_fl(avg_nor, 1.0f / (float)avg_nor_count);
@@ -1983,7 +1990,6 @@ static void mesh_normals_loop_custom_set(const MVert *mverts,
MEM_freeN(lnors);
MEM_freeN(loop_to_poly);
- MEM_freeN(done_loops);
BKE_lnor_spacearr_free(&lnors_spacearr);
}
@@ -1992,10 +1998,10 @@ void BKE_mesh_normals_loop_custom_set(const MVert *mverts,
const int numVerts,
MEdge *medges,
const int numEdges,
- MLoop *mloops,
+ const MLoop *mloops,
float (*r_custom_loopnors)[3],
const int numLoops,
- MPoly *mpolys,
+ const MPoly *mpolys,
const float (*polynors)[3],
const int numPolys,
short (*r_clnors_data)[2])
@@ -2015,18 +2021,18 @@ void BKE_mesh_normals_loop_custom_set(const MVert *mverts,
false);
}
-void BKE_mesh_normals_loop_custom_from_vertices_set(const MVert *mverts,
- const float (*vert_normals)[3],
- float (*r_custom_vertnors)[3],
- const int numVerts,
- MEdge *medges,
- const int numEdges,
- MLoop *mloops,
- const int numLoops,
- MPoly *mpolys,
- const float (*polynors)[3],
- const int numPolys,
- short (*r_clnors_data)[2])
+void BKE_mesh_normals_loop_custom_from_verts_set(const MVert *mverts,
+ const float (*vert_normals)[3],
+ float (*r_custom_vertnors)[3],
+ const int numVerts,
+ MEdge *medges,
+ const int numEdges,
+ const MLoop *mloops,
+ const int numLoops,
+ const MPoly *mpolys,
+ const float (*polynors)[3],
+ const int numPolys,
+ short (*r_clnors_data)[2])
{
mesh_normals_loop_custom_set(mverts,
vert_normals,
@@ -2054,20 +2060,24 @@ static void mesh_set_custom_normals(Mesh *mesh, float (*r_custom_nors)[3], const
}
else {
clnors = (short(*)[2])CustomData_add_layer(
- &mesh->ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, nullptr, numloops);
+ &mesh->ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, nullptr, numloops);
}
+ const Span<MVert> verts = mesh->verts();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
- mesh_normals_loop_custom_set(mesh->mvert,
+ mesh_normals_loop_custom_set(verts.data(),
BKE_mesh_vertex_normals_ensure(mesh),
- mesh->totvert,
- mesh->medge,
- mesh->totedge,
- mesh->mloop,
+ verts.size(),
+ edges.data(),
+ edges.size(),
+ loops.data(),
r_custom_nors,
- mesh->totloop,
- mesh->mpoly,
+ loops.size(),
+ polys.data(),
BKE_mesh_poly_normals_ensure(mesh),
- mesh->totpoly,
+ polys.size(),
clnors,
use_vertices);
}
@@ -2077,7 +2087,7 @@ void BKE_mesh_set_custom_normals(Mesh *mesh, float (*r_custom_loopnors)[3])
mesh_set_custom_normals(mesh, r_custom_loopnors, false);
}
-void BKE_mesh_set_custom_normals_from_vertices(Mesh *mesh, float (*r_custom_vertnors)[3])
+void BKE_mesh_set_custom_normals_from_verts(Mesh *mesh, float (*r_custom_vertnors)[3])
{
mesh_set_custom_normals(mesh, r_custom_vertnors, true);
}
diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c
index 3a37c29c1d0..d63d064eb3c 100644
--- a/source/blender/blenkernel/intern/mesh_remap.c
+++ b/source/blender/blenkernel/intern/mesh_remap.c
@@ -312,15 +312,10 @@ void BKE_mesh_remap_calc_source_cddata_masks_from_map_modes(const int UNUSED(ver
{
/* vert, edge and poly mapping modes never need extra cddata from source object. */
const bool need_lnors_src = (loop_mode & MREMAP_USE_LOOP) && (loop_mode & MREMAP_USE_NORMAL);
- const bool need_pnors_src = need_lnors_src ||
- ((loop_mode & MREMAP_USE_POLY) && (loop_mode & MREMAP_USE_NORMAL));
if (need_lnors_src) {
r_cddata_mask->lmask |= CD_MASK_NORMAL;
}
- if (need_pnors_src) {
- r_cddata_mask->pmask |= CD_MASK_NORMAL;
- }
}
void BKE_mesh_remap_init(MeshPairRemap *map, const int items_num)
@@ -382,7 +377,7 @@ void BKE_mesh_remap_item_define_invalid(MeshPairRemap *map, const int index)
}
static int mesh_remap_interp_poly_data_get(const MPoly *mp,
- MLoop *mloops,
+ const MLoop *mloops,
const float (*vcos_src)[3],
const float point[3],
size_t *buff_size,
@@ -393,7 +388,7 @@ static int mesh_remap_interp_poly_data_get(const MPoly *mp,
const bool do_weights,
int *r_closest_index)
{
- MLoop *ml;
+ const MLoop *ml;
float(*vco)[3];
float ref_dist_sq = FLT_MAX;
int *index;
@@ -525,7 +520,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
}
}
else if (ELEM(mode, MREMAP_MODE_VERT_EDGE_NEAREST, MREMAP_MODE_VERT_EDGEINTERP_NEAREST)) {
- MEdge *edges_src = me_src->medge;
+ const MEdge *edges_src = BKE_mesh_edges(me_src);
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, NULL);
BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_EDGES, 2);
@@ -541,7 +536,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
if (mesh_remap_bvhtree_query_nearest(
&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist)) {
- MEdge *me = &edges_src[nearest.index];
+ const MEdge *me = &edges_src[nearest.index];
const float *v1cos = vcos_src[me->v1];
const float *v2cos = vcos_src[me->v2];
@@ -578,8 +573,8 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
MREMAP_MODE_VERT_POLY_NEAREST,
MREMAP_MODE_VERT_POLYINTERP_NEAREST,
MREMAP_MODE_VERT_POLYINTERP_VNORPROJ)) {
- MPoly *polys_src = me_src->mpoly;
- MLoop *loops_src = me_src->mloop;
+ const MPoly *polys_src = BKE_mesh_polys(me_src);
+ const MLoop *loops_src = BKE_mesh_loops(me_src);
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, NULL);
const float(*vert_normals_dst)[3] = BKE_mesh_vertex_normals_ensure(me_dst);
@@ -604,7 +599,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
if (mesh_remap_bvhtree_query_raycast(
&treedata, &rayhit, tmp_co, tmp_no, ray_radius, max_dist, &hit_dist)) {
const MLoopTri *lt = &treedata.looptri[rayhit.index];
- MPoly *mp_src = &polys_src[lt->poly];
+ const MPoly *mp_src = &polys_src[lt->poly];
const int sources_num = mesh_remap_interp_poly_data_get(mp_src,
loops_src,
(const float(*)[3])vcos_src,
@@ -639,7 +634,7 @@ void BKE_mesh_remap_calc_verts_from_mesh(const int mode,
if (mesh_remap_bvhtree_query_nearest(
&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist)) {
const MLoopTri *lt = &treedata.looptri[nearest.index];
- MPoly *mp = &polys_src[lt->poly];
+ const MPoly *mp = &polys_src[lt->poly];
if (mode == MREMAP_MODE_VERT_POLY_NEAREST) {
int index;
@@ -731,7 +726,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
if (mode == MREMAP_MODE_EDGE_VERT_NEAREST) {
const int num_verts_src = me_src->totvert;
const int num_edges_src = me_src->totedge;
- MEdge *edges_src = me_src->medge;
+ const MEdge *edges_src = BKE_mesh_edges(me_src);
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, NULL);
MeshElemMap *vert_to_edge_src_map;
@@ -802,7 +797,7 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
k = vert_to_edge_src_map[vidx_src].count;
for (; k--; eidx_src++) {
- MEdge *e_src = &edges_src[*eidx_src];
+ const MEdge *e_src = &edges_src[*eidx_src];
const float *other_co_src = vcos_src[BKE_mesh_edge_other_vert(e_src, vidx_src)];
const float *other_co_dst =
verts_dst[BKE_mesh_edge_other_vert(e_dst, (int)vidx_dst)].co;
@@ -878,9 +873,9 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
}
}
else if (mode == MREMAP_MODE_EDGE_POLY_NEAREST) {
- MEdge *edges_src = me_src->medge;
- MPoly *polys_src = me_src->mpoly;
- MLoop *loops_src = me_src->mloop;
+ const MEdge *edges_src = BKE_mesh_edges(me_src);
+ const MPoly *polys_src = BKE_mesh_polys(me_src);
+ const MLoop *loops_src = BKE_mesh_loops(me_src);
float(*vcos_src)[3] = BKE_mesh_vert_coords_alloc(me_src, NULL);
BKE_bvhtree_from_mesh_get(&treedata, me_src, BVHTREE_FROM_LOOPTRI, 2);
@@ -896,14 +891,14 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
if (mesh_remap_bvhtree_query_nearest(
&treedata, &nearest, tmp_co, max_dist_sq, &hit_dist)) {
const MLoopTri *lt = &treedata.looptri[nearest.index];
- MPoly *mp_src = &polys_src[lt->poly];
- MLoop *ml_src = &loops_src[mp_src->loopstart];
+ const MPoly *mp_src = &polys_src[lt->poly];
+ const MLoop *ml_src = &loops_src[mp_src->loopstart];
int nloops = mp_src->totloop;
float best_dist_sq = FLT_MAX;
int best_eidx_src = -1;
for (; nloops--; ml_src++) {
- MEdge *med_src = &edges_src[ml_src->e];
+ const MEdge *med_src = &edges_src[ml_src->e];
float *co1_src = vcos_src[med_src->v1];
float *co2_src = vcos_src[med_src->v2];
float co_src[3];
@@ -1046,9 +1041,9 @@ void BKE_mesh_remap_calc_edges_from_mesh(const int mode,
static void mesh_island_to_astar_graph_edge_process(MeshIslandStore *islands,
const int island_index,
BLI_AStarGraph *as_graph,
- MVert *verts,
- MPoly *polys,
- MLoop *loops,
+ const MVert *verts,
+ const MPoly *polys,
+ const MLoop *loops,
const int edge_idx,
BLI_bitmap *done_edges,
MeshElemMap *edge_to_poly_map,
@@ -1063,7 +1058,7 @@ static void mesh_island_to_astar_graph_edge_process(MeshIslandStore *islands,
for (i = 0; i < edge_to_poly_map[edge_idx].count; i++) {
const int pidx = edge_to_poly_map[edge_idx].indices[i];
- MPoly *mp = &polys[pidx];
+ const MPoly *mp = &polys[pidx];
const int pidx_isld = islands ? poly_island_index_map[pidx] : pidx;
void *custom_data = is_edge_innercut ? POINTER_FROM_INT(edge_idx) : POINTER_FROM_INT(-1);
@@ -1104,11 +1099,11 @@ static void mesh_island_to_astar_graph_edge_process(MeshIslandStore *islands,
static void mesh_island_to_astar_graph(MeshIslandStore *islands,
const int island_index,
- MVert *verts,
+ const MVert *verts,
MeshElemMap *edge_to_poly_map,
const int numedges,
- MLoop *loops,
- MPoly *polys,
+ const MLoop *loops,
+ const MPoly *polys,
const int numpolys,
BLI_AStarGraph *r_as_graph)
{
@@ -1158,7 +1153,7 @@ static void mesh_island_to_astar_graph(MeshIslandStore *islands,
for (pidx_isld = node_num; pidx_isld--;) {
const int pidx = islands ? island_poly_map->indices[pidx_isld] : pidx_isld;
- MPoly *mp = &polys[pidx];
+ const MPoly *mp = &polys[pidx];
int pl_idx, l_idx;
if (poly_status[pidx_isld] == POLY_COMPLETE) {
@@ -1166,7 +1161,7 @@ static void mesh_island_to_astar_graph(MeshIslandStore *islands,
}
for (pl_idx = 0, l_idx = mp->loopstart; pl_idx < mp->totloop; pl_idx++, l_idx++) {
- MLoop *ml = &loops[l_idx];
+ const MLoop *ml = &loops[l_idx];
if (BLI_BITMAP_TEST(done_edges, ml->e)) {
continue;
@@ -1234,13 +1229,13 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
const float max_dist,
const float ray_radius,
Mesh *mesh_dst,
- MVert *verts_dst,
+ const MVert *verts_dst,
const int numverts_dst,
- MEdge *edges_dst,
+ const MEdge *edges_dst,
const int numedges_dst,
- MLoop *loops_dst,
+ const MLoop *loops_dst,
const int numloops_dst,
- MPoly *polys_dst,
+ const MPoly *polys_dst,
const int numpolys_dst,
CustomData *ldata_dst,
const bool use_split_nors_dst,
@@ -1307,14 +1302,14 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
/* Unlike above, those are one-to-one mappings, simpler! */
int *loop_to_poly_map_src = NULL;
- MVert *verts_src = me_src->mvert;
+ const MVert *verts_src = BKE_mesh_verts(me_src);
const int num_verts_src = me_src->totvert;
float(*vcos_src)[3] = NULL;
- MEdge *edges_src = me_src->medge;
+ const MEdge *edges_src = BKE_mesh_edges(me_src);
const int num_edges_src = me_src->totedge;
- MLoop *loops_src = me_src->mloop;
+ const MLoop *loops_src = BKE_mesh_loops(me_src);
const int num_loops_src = me_src->totloop;
- MPoly *polys_src = me_src->mpoly;
+ const MPoly *polys_src = BKE_mesh_polys(me_src);
const int num_polys_src = me_src->totpoly;
const MLoopTri *looptri_src = NULL;
int num_looptri_src = 0;
@@ -1324,8 +1319,10 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
int *indices_interp = NULL;
float *weights_interp = NULL;
- MLoop *ml_src, *ml_dst;
- MPoly *mp_src, *mp_dst;
+ const MLoop *ml_src;
+ const MLoop *ml_dst;
+ const MPoly *mp_src;
+ const MPoly *mp_dst;
int tindex, pidx_dst, lidx_dst, plidx_dst, pidx_src, lidx_src, plidx_src;
IslandResult **islands_res;
@@ -1357,7 +1354,7 @@ void BKE_mesh_remap_calc_loops_from_mesh(const int mode,
const bool do_loop_nors_dst = (loop_nors_dst == NULL);
if (!loop_nors_dst) {
loop_nors_dst = CustomData_add_layer(
- ldata_dst, CD_NORMAL, CD_CALLOC, NULL, numloops_dst);
+ ldata_dst, CD_NORMAL, CD_SET_DEFAULT, NULL, numloops_dst);
CustomData_set_layer_flag(ldata_dst, CD_NORMAL, CD_FLAG_TEMPORARY);
}
if (dirty_nors_dst || do_loop_nors_dst) {
@@ -2153,10 +2150,10 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
const SpaceTransform *space_transform,
const float max_dist,
const float ray_radius,
- Mesh *mesh_dst,
- MVert *verts_dst,
- MLoop *loops_dst,
- MPoly *polys_dst,
+ const Mesh *mesh_dst,
+ const MVert *verts_dst,
+ const MLoop *loops_dst,
+ const MPoly *polys_dst,
const int numpolys_dst,
Mesh *me_src,
MeshPairRemap *r_map)
@@ -2193,7 +2190,7 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
nearest.index = -1;
for (i = 0; i < numpolys_dst; i++) {
- MPoly *mp = &polys_dst[i];
+ const MPoly *mp = &polys_dst[i];
BKE_mesh_calc_poly_center(mp, &loops_dst[mp->loopstart], verts_dst, tmp_co);
@@ -2218,7 +2215,7 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
BLI_assert(poly_nors_dst);
for (i = 0; i < numpolys_dst; i++) {
- MPoly *mp = &polys_dst[i];
+ const MPoly *mp = &polys_dst[i];
BKE_mesh_calc_poly_center(mp, &loops_dst[mp->loopstart], verts_dst, tmp_co);
copy_v3_v3(tmp_no, poly_nors_dst[i]);
@@ -2264,7 +2261,7 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
/* For each dst poly, we sample some rays from it (2D grid in pnor space)
* and use their hits to interpolate from source polys. */
/* NOTE: dst poly is early-converted into src space! */
- MPoly *mp = &polys_dst[i];
+ const MPoly *mp = &polys_dst[i];
int tot_rays, done_rays = 0;
float poly_area_2d_inv, done_area = 0.0f;
@@ -2307,7 +2304,7 @@ void BKE_mesh_remap_calc_polys_from_mesh(const int mode,
INIT_MINMAX2(poly_dst_2d_min, poly_dst_2d_max);
for (j = 0; j < mp->totloop; j++) {
- MLoop *ml = &loops_dst[j + mp->loopstart];
+ const MLoop *ml = &loops_dst[j + mp->loopstart];
copy_v3_v3(tmp_co, verts_dst[ml->v].co);
if (space_transform) {
BLI_space_transform_apply(space_transform, tmp_co);
diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
index 85aed01ce52..a77879fb573 100644
--- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
+++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc
@@ -61,14 +61,15 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh,
void (*update_cb)(void *, float progress, int *cancel),
void *update_cb_data)
{
- /* Ensure that the triangulated mesh data is up to data */
+ const Span<MVert> input_verts = input_mesh->verts();
+ const Span<MLoop> input_loops = input_mesh->loops();
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh);
/* Gather the required data for export to the internal quadriflow mesh format. */
MVertTri *verttri = (MVertTri *)MEM_callocN(
sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh), "remesh_looptri");
BKE_mesh_runtime_verttri_from_looptri(
- verttri, input_mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(input_mesh));
+ verttri, input_loops.data(), looptri, BKE_mesh_runtime_looptri_len(input_mesh));
const int totfaces = BKE_mesh_runtime_looptri_len(input_mesh);
const int totverts = input_mesh->totvert;
@@ -76,7 +77,7 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh,
Array<int> faces(totfaces * 3);
for (const int i : IndexRange(totverts)) {
- verts[i] = input_mesh->mvert[i].co;
+ verts[i] = input_verts[i].co;
}
for (const int i : IndexRange(totfaces)) {
@@ -123,20 +124,24 @@ static Mesh *remesh_quadriflow(const Mesh *input_mesh,
/* Construct the new output mesh */
Mesh *mesh = BKE_mesh_new_nomain(qrd.out_totverts, 0, 0, qrd.out_totfaces * 4, qrd.out_totfaces);
+ BKE_mesh_copy_parameters(mesh, input_mesh);
+ MutableSpan<MVert> mesh_verts = mesh->verts_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
for (const int i : IndexRange(qrd.out_totverts)) {
- copy_v3_v3(mesh->mvert[i].co, &qrd.out_verts[i * 3]);
+ copy_v3_v3(mesh_verts[i].co, &qrd.out_verts[i * 3]);
}
for (const int i : IndexRange(qrd.out_totfaces)) {
- MPoly &poly = mesh->mpoly[i];
+ MPoly &poly = polys[i];
const int loopstart = i * 4;
poly.loopstart = loopstart;
poly.totloop = 4;
- mesh->mloop[loopstart].v = qrd.out_faces[loopstart];
- mesh->mloop[loopstart + 1].v = qrd.out_faces[loopstart + 1];
- mesh->mloop[loopstart + 2].v = qrd.out_faces[loopstart + 2];
- mesh->mloop[loopstart + 3].v = qrd.out_faces[loopstart + 3];
+ loops[loopstart].v = qrd.out_faces[loopstart];
+ loops[loopstart + 1].v = qrd.out_faces[loopstart + 1];
+ loops[loopstart + 2].v = qrd.out_faces[loopstart + 2];
+ loops[loopstart + 3].v = qrd.out_faces[loopstart + 3];
}
BKE_mesh_calc_edges(mesh, false, false);
@@ -186,7 +191,8 @@ Mesh *BKE_mesh_remesh_quadriflow(const Mesh *mesh,
static openvdb::FloatGrid::Ptr remesh_voxel_level_set_create(const Mesh *mesh,
const float voxel_size)
{
- Span<MLoop> mloop{mesh->mloop, mesh->totloop};
+ const Span<MVert> verts = mesh->verts();
+ const Span<MLoop> loops = mesh->loops();
Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh),
BKE_mesh_runtime_looptri_len(mesh)};
@@ -194,14 +200,14 @@ static openvdb::FloatGrid::Ptr remesh_voxel_level_set_create(const Mesh *mesh,
std::vector<openvdb::Vec3I> triangles(looptris.size());
for (const int i : IndexRange(mesh->totvert)) {
- const float3 co = mesh->mvert[i].co;
+ const float3 co = verts[i].co;
points[i] = openvdb::Vec3s(co.x, co.y, co.z);
}
for (const int i : IndexRange(looptris.size())) {
const MLoopTri &loop_tri = looptris[i];
triangles[i] = openvdb::Vec3I(
- mloop[loop_tri.tri[0]].v, mloop[loop_tri.tri[1]].v, mloop[loop_tri.tri[2]].v);
+ loops[loop_tri.tri[0]].v, loops[loop_tri.tri[1]].v, loops[loop_tri.tri[2]].v);
}
openvdb::math::Transform::Ptr transform = openvdb::math::Transform::createLinearTransform(
@@ -225,34 +231,34 @@ static Mesh *remesh_voxel_volume_to_mesh(const openvdb::FloatGrid::Ptr level_set
Mesh *mesh = BKE_mesh_new_nomain(
vertices.size(), 0, 0, quads.size() * 4 + tris.size() * 3, quads.size() + tris.size());
- MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> mloops{mesh->mloop, mesh->totloop};
- MutableSpan<MPoly> mpolys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> mesh_verts = mesh->verts_for_write();
+ MutableSpan<MPoly> mesh_polys = mesh->polys_for_write();
+ MutableSpan<MLoop> mesh_loops = mesh->loops_for_write();
- for (const int i : mverts.index_range()) {
- copy_v3_v3(mverts[i].co, float3(vertices[i].x(), vertices[i].y(), vertices[i].z()));
+ for (const int i : mesh_verts.index_range()) {
+ copy_v3_v3(mesh_verts[i].co, float3(vertices[i].x(), vertices[i].y(), vertices[i].z()));
}
for (const int i : IndexRange(quads.size())) {
- MPoly &poly = mpolys[i];
+ MPoly &poly = mesh_polys[i];
const int loopstart = i * 4;
poly.loopstart = loopstart;
poly.totloop = 4;
- mloops[loopstart].v = quads[i][0];
- mloops[loopstart + 1].v = quads[i][3];
- mloops[loopstart + 2].v = quads[i][2];
- mloops[loopstart + 3].v = quads[i][1];
+ mesh_loops[loopstart].v = quads[i][0];
+ mesh_loops[loopstart + 1].v = quads[i][3];
+ mesh_loops[loopstart + 2].v = quads[i][2];
+ mesh_loops[loopstart + 3].v = quads[i][1];
}
const int triangle_loop_start = quads.size() * 4;
for (const int i : IndexRange(tris.size())) {
- MPoly &poly = mpolys[quads.size() + i];
+ MPoly &poly = mesh_polys[quads.size() + i];
const int loopstart = triangle_loop_start + i * 3;
poly.loopstart = loopstart;
poly.totloop = 3;
- mloops[loopstart].v = tris[i][2];
- mloops[loopstart + 1].v = tris[i][1];
- mloops[loopstart + 2].v = tris[i][0];
+ mesh_loops[loopstart].v = tris[i][2];
+ mesh_loops[loopstart + 1].v = tris[i][1];
+ mesh_loops[loopstart + 2].v = tris[i][0];
}
BKE_mesh_calc_edges(mesh, false, false);
@@ -268,18 +274,24 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh,
{
#ifdef WITH_OPENVDB
openvdb::FloatGrid::Ptr level_set = remesh_voxel_level_set_create(mesh, voxel_size);
- return remesh_voxel_volume_to_mesh(level_set, isovalue, adaptivity, false);
+ Mesh *result = remesh_voxel_volume_to_mesh(level_set, isovalue, adaptivity, false);
+ BKE_mesh_copy_parameters(result, mesh);
+ return result;
#else
UNUSED_VARS(mesh, voxel_size, adaptivity, isovalue);
return nullptr;
#endif
}
-void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source)
+void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, const Mesh *source)
{
BVHTreeFromMesh bvhtree = {nullptr};
BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2);
- MVert *target_verts = (MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
+ const MVert *target_verts = (const MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
+ const float *source_mask = (const float *)CustomData_get_layer(&source->vdata, CD_PAINT_MASK);
+ if (source_mask == nullptr) {
+ return;
+ }
float *target_mask;
if (CustomData_has_layer(&target->vdata, CD_PAINT_MASK)) {
@@ -287,16 +299,7 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source)
}
else {
target_mask = (float *)CustomData_add_layer(
- &target->vdata, CD_PAINT_MASK, CD_CALLOC, nullptr, target->totvert);
- }
-
- const float *source_mask;
- if (CustomData_has_layer(&source->vdata, CD_PAINT_MASK)) {
- source_mask = (float *)CustomData_get_layer(&source->vdata, CD_PAINT_MASK);
- }
- else {
- source_mask = (float *)CustomData_add_layer(
- &source->vdata, CD_PAINT_MASK, CD_CALLOC, nullptr, source->totvert);
+ &target->vdata, CD_PAINT_MASK, CD_CONSTRUCT, nullptr, target->totvert);
}
for (int i = 0; i < target->totvert; i++) {
@@ -313,13 +316,16 @@ void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, Mesh *source)
free_bvhtree_from_mesh(&bvhtree);
}
-void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
+void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, const Mesh *source)
{
- BVHTreeFromMesh bvhtree = {nullptr};
-
const MPoly *target_polys = (const MPoly *)CustomData_get_layer(&target->pdata, CD_MPOLY);
const MVert *target_verts = (const MVert *)CustomData_get_layer(&target->vdata, CD_MVERT);
const MLoop *target_loops = (const MLoop *)CustomData_get_layer(&target->ldata, CD_MLOOP);
+ const int *source_face_sets = (const int *)CustomData_get_layer(&source->pdata,
+ CD_SCULPT_FACE_SETS);
+ if (source_face_sets == nullptr) {
+ return;
+ }
int *target_face_sets;
if (CustomData_has_layer(&target->pdata, CD_SCULPT_FACE_SETS)) {
@@ -327,19 +333,11 @@ void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, Mesh *source)
}
else {
target_face_sets = (int *)CustomData_add_layer(
- &target->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, nullptr, target->totpoly);
- }
-
- const int *source_face_sets;
- if (CustomData_has_layer(&source->pdata, CD_SCULPT_FACE_SETS)) {
- source_face_sets = (const int *)CustomData_get_layer(&source->pdata, CD_SCULPT_FACE_SETS);
- }
- else {
- source_face_sets = (const int *)CustomData_add_layer(
- &source->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, nullptr, source->totpoly);
+ &target->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, target->totpoly);
}
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(source);
+ BVHTreeFromMesh bvhtree = {nullptr};
BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_LOOPTRI, 2);
for (int i = 0; i < target->totpoly; i++) {
@@ -386,7 +384,7 @@ void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source)
int elem_num = domain == ATTR_DOMAIN_POINT ? target->totvert : target->totloop;
CustomData_add_layer_named(
- target_cdata, layer->type, CD_CALLOC, nullptr, elem_num, layer->name);
+ target_cdata, layer->type, CD_SET_DEFAULT, nullptr, elem_num, layer->name);
layer_i = CustomData_get_named_layer_index(target_cdata, layer->type, layer->name);
}
diff --git a/source/blender/blenkernel/intern/mesh_runtime.cc b/source/blender/blenkernel/intern/mesh_runtime.cc
index 4521c519f45..4b6433edd5a 100644
--- a/source/blender/blenkernel/intern/mesh_runtime.cc
+++ b/source/blender/blenkernel/intern/mesh_runtime.cc
@@ -23,6 +23,9 @@
#include "BKE_shrinkwrap.h"
#include "BKE_subdiv_ccg.h"
+using blender::MutableSpan;
+using blender::Span;
+
/* -------------------------------------------------------------------- */
/** \name Mesh Runtime Struct Utils
* \{ */
@@ -147,10 +150,13 @@ void BKE_mesh_runtime_looptri_recalc(Mesh *mesh)
{
mesh_ensure_looptri_data(mesh);
BLI_assert(mesh->totpoly == 0 || mesh->runtime.looptris.array_wip != nullptr);
+ const Span<MVert> verts = mesh->verts();
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
- BKE_mesh_recalc_looptri(mesh->mloop,
- mesh->mpoly,
- mesh->mvert,
+ BKE_mesh_recalc_looptri(loops.data(),
+ polys.data(),
+ verts.data(),
mesh->totloop,
mesh->totpoly,
mesh->runtime.looptris.array_wip);
@@ -272,12 +278,12 @@ void BKE_mesh_tag_coords_changed_uniformly(Mesh *mesh)
const bool poly_normals_were_dirty = BKE_mesh_poly_normals_are_dirty(mesh);
BKE_mesh_tag_coords_changed(mesh);
- /* The normals didn't change, since all vertices moved by the same amount. */
+ /* The normals didn't change, since all verts moved by the same amount. */
if (!vert_normals_were_dirty) {
- BKE_mesh_poly_normals_clear_dirty(mesh);
+ BKE_mesh_vertex_normals_clear_dirty(mesh);
}
if (!poly_normals_were_dirty) {
- BKE_mesh_vertex_normals_clear_dirty(mesh);
+ BKE_mesh_poly_normals_clear_dirty(mesh);
}
}
@@ -324,6 +330,11 @@ bool BKE_mesh_runtime_is_valid(Mesh *me_eval)
printf("MESH: %s\n", me_eval->id.name + 2);
}
+ MutableSpan<MVert> verts = me_eval->verts_for_write();
+ MutableSpan<MEdge> edges = me_eval->edges_for_write();
+ MutableSpan<MPoly> polys = me_eval->polys_for_write();
+ MutableSpan<MLoop> loops = me_eval->loops_for_write();
+
is_valid &= BKE_mesh_validate_all_customdata(
&me_eval->vdata,
me_eval->totvert,
@@ -338,21 +349,22 @@ bool BKE_mesh_runtime_is_valid(Mesh *me_eval)
do_fixes,
&changed);
- is_valid &= BKE_mesh_validate_arrays(me_eval,
- me_eval->mvert,
- me_eval->totvert,
- me_eval->medge,
- me_eval->totedge,
- me_eval->mface,
- me_eval->totface,
- me_eval->mloop,
- me_eval->totloop,
- me_eval->mpoly,
- me_eval->totpoly,
- me_eval->dvert,
- do_verbose,
- do_fixes,
- &changed);
+ is_valid &= BKE_mesh_validate_arrays(
+ me_eval,
+ verts.data(),
+ verts.size(),
+ edges.data(),
+ edges.size(),
+ static_cast<MFace *>(CustomData_get_layer(&me_eval->fdata, CD_MFACE)),
+ me_eval->totface,
+ loops.data(),
+ loops.size(),
+ polys.data(),
+ polys.size(),
+ me_eval->deform_verts_for_write().data(),
+ do_verbose,
+ do_fixes,
+ &changed);
BLI_assert(changed == false);
diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc
index dd09a3d6917..1ddac19304d 100644
--- a/source/blender/blenkernel/intern/mesh_sample.cc
+++ b/source/blender/blenkernel/intern/mesh_sample.cc
@@ -2,6 +2,7 @@
#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
@@ -16,10 +17,11 @@ 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 VArray<T> &src,
const IndexMask mask,
- const MutableSpan<T> data_out)
+ const MutableSpan<T> dst)
{
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -28,34 +30,34 @@ BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh,
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 int v0_index = loops[looptri.tri[0]].v;
+ const int v1_index = loops[looptri.tri[1]].v;
+ const int v2_index = loops[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 v0 = src[v0_index];
+ const T v1 = src[v1_index];
+ const T v2 = src[v2_index];
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
+ dst[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 GVArray &src,
const IndexMask mask,
- const GMutableSpan data_out)
+ const GMutableSpan dst)
{
- BLI_assert(data_in.size() == mesh.totvert);
- BLI_assert(data_in.type() == data_out.type());
+ BLI_assert(src.size() == mesh.totvert);
+ BLI_assert(src.type() == dst.type());
- const CPPType &type = data_in.type();
+ const CPPType &type = src.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>(), mask, data_out.typed<T>());
+ mesh, looptri_indices, bary_coords, src.typed<T>(), mask, dst.typed<T>());
});
}
@@ -63,9 +65,9 @@ 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 VArray<T> &src,
const IndexMask mask,
- const MutableSpan<T> data_out)
+ const MutableSpan<T> dst)
{
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -79,39 +81,39 @@ BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh,
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 v0 = src[loop_index_0];
+ const T v1 = src[loop_index_1];
+ const T v2 = src[loop_index_2];
const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2);
- data_out[i] = interpolated_value;
+ dst[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 GVArray &src,
const IndexMask mask,
- const GMutableSpan data_out)
+ const GMutableSpan dst)
{
- BLI_assert(data_in.size() == mesh.totloop);
- BLI_assert(data_in.type() == data_out.type());
+ BLI_assert(src.size() == mesh.totloop);
+ BLI_assert(src.type() == dst.type());
- const CPPType &type = data_in.type();
+ const CPPType &type = src.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>(), mask, data_out.typed<T>());
+ mesh, looptri_indices, bary_coords, src.typed<T>(), mask, dst.typed<T>());
});
}
template<typename T>
void sample_face_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
- const VArray<T> &data_in,
+ const VArray<T> &src,
const IndexMask mask,
- const MutableSpan<T> data_out)
+ const MutableSpan<T> dst)
{
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -120,23 +122,23 @@ void sample_face_attribute(const Mesh &mesh,
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];
+ dst[i] = src[poly_index];
}
}
void sample_face_attribute(const Mesh &mesh,
const Span<int> looptri_indices,
- const GVArray &data_in,
+ const GVArray &src,
const IndexMask mask,
- const GMutableSpan data_out)
+ const GMutableSpan dst)
{
- BLI_assert(data_in.size() == mesh.totpoly);
- BLI_assert(data_in.type() == data_out.type());
+ BLI_assert(src.size() == mesh.totpoly);
+ BLI_assert(src.type() == dst.type());
- const CPPType &type = data_in.type();
+ const CPPType &type = src.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>(), mask, data_out.typed<T>());
+ sample_face_attribute<T>(mesh, looptri_indices, src.typed<T>(), mask, dst.typed<T>());
});
}
@@ -157,6 +159,8 @@ Span<float3> MeshAttributeInterpolator::ensure_barycentric_coords()
}
bary_coords_.reinitialize(mask_.min_array_size());
+ const Span<MVert> verts = mesh_->verts();
+ const Span<MLoop> loops = mesh_->loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_),
BKE_mesh_runtime_looptri_len(mesh_)};
@@ -164,14 +168,14 @@ Span<float3> MeshAttributeInterpolator::ensure_barycentric_coords()
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;
+ const int v0_index = loops[looptri.tri[0]].v;
+ const int v1_index = loops[looptri.tri[1]].v;
+ const int v2_index = loops[looptri.tri[2]].v;
interp_weights_tri_v3(bary_coords_[i],
- mesh_->mvert[v0_index].co,
- mesh_->mvert[v1_index].co,
- mesh_->mvert[v2_index].co,
+ verts[v0_index].co,
+ verts[v1_index].co,
+ verts[v2_index].co,
positions_[i]);
}
return bary_coords_;
@@ -185,6 +189,8 @@ Span<float3> MeshAttributeInterpolator::ensure_nearest_weights()
}
nearest_weights_.reinitialize(mask_.min_array_size());
+ const Span<MVert> verts = mesh_->verts();
+ const Span<MLoop> loops = mesh_->loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(mesh_),
BKE_mesh_runtime_looptri_len(mesh_)};
@@ -192,13 +198,13 @@ Span<float3> MeshAttributeInterpolator::ensure_nearest_weights()
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;
+ const int v0_index = loops[looptri.tri[0]].v;
+ const int v1_index = loops[looptri.tri[1]].v;
+ const int v2_index = loops[looptri.tri[2]].v;
- const float d0 = len_squared_v3v3(positions_[i], mesh_->mvert[v0_index].co);
- const float d1 = len_squared_v3v3(positions_[i], mesh_->mvert[v1_index].co);
- const float d2 = len_squared_v3v3(positions_[i], mesh_->mvert[v2_index].co);
+ const float d0 = len_squared_v3v3(positions_[i], verts[v0_index].co);
+ const float d1 = len_squared_v3v3(positions_[i], verts[v1_index].co);
+ const float d2 = len_squared_v3v3(positions_[i], verts[v2_index].co);
nearest_weights_[i] = MIN3_PAIR(d0, d1, d2, float3(1, 0, 0), float3(0, 1, 0), float3(0, 0, 1));
}
@@ -219,45 +225,31 @@ void MeshAttributeInterpolator::sample_data(const GVArray &src,
if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) {
switch (mode) {
case eAttributeMapMode::INTERPOLATED:
- weights = ensure_barycentric_coords();
+ weights = this->ensure_barycentric_coords();
break;
case eAttributeMapMode::NEAREST:
- weights = ensure_nearest_weights();
+ weights = this->ensure_nearest_weights();
break;
}
}
/* Interpolate the source attributes on the surface. */
switch (domain) {
- case ATTR_DOMAIN_POINT: {
+ case ATTR_DOMAIN_POINT:
sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst);
break;
- }
- case ATTR_DOMAIN_FACE: {
+ case ATTR_DOMAIN_FACE:
sample_face_attribute(*mesh_, looptri_indices_, src, mask_, dst);
break;
- }
- case ATTR_DOMAIN_CORNER: {
+ case ATTR_DOMAIN_CORNER:
sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst);
break;
- }
- case ATTR_DOMAIN_EDGE: {
+ case ATTR_DOMAIN_EDGE:
/* Not yet supported. */
break;
- }
- default: {
+ default:
BLI_assert_unreachable();
break;
- }
- }
-}
-
-void MeshAttributeInterpolator::sample_attribute(const GAttributeReader &src_attribute,
- GSpanAttributeWriter &dst_attribute,
- eAttributeMapMode mode)
-{
- if (src_attribute && dst_attribute) {
- this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.span);
}
}
@@ -271,6 +263,8 @@ int sample_surface_points_spherical(RandomNumberGenerator &rng,
Vector<int> &r_looptri_indices,
Vector<float3> &r_positions)
{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -284,9 +278,9 @@ int sample_surface_points_spherical(RandomNumberGenerator &rng,
for (const int looptri_index : looptri_indices_to_sample) {
const MLoopTri &looptri = looptris[looptri_index];
- const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co;
- const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].v].co;
- const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co;
+ const float3 &v0 = verts[loops[looptri.tri[0]].v].co;
+ const float3 &v1 = verts[loops[looptri.tri[1]].v].co;
+ const float3 &v2 = verts[loops[looptri.tri[2]].v].co;
const float looptri_area = area_tri_v3(v0, v1, v2);
@@ -367,6 +361,8 @@ int sample_surface_points_projected(
Vector<int> &r_looptri_indices,
Vector<float3> &r_positions)
{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -408,7 +404,8 @@ int sample_surface_points_projected(
const int looptri_index = ray_hit.index;
const float3 pos = ray_hit.co;
- const float3 bary_coords = compute_bary_coord_in_triangle(mesh, looptris[looptri_index], pos);
+ const float3 bary_coords = compute_bary_coord_in_triangle(
+ verts, loops, looptris[looptri_index], pos);
r_positions.append(pos);
r_bary_coords.append(bary_coords);
@@ -418,13 +415,14 @@ int sample_surface_points_projected(
return point_count;
}
-float3 compute_bary_coord_in_triangle(const Mesh &mesh,
+float3 compute_bary_coord_in_triangle(const Span<MVert> verts,
+ const Span<MLoop> loops,
const MLoopTri &looptri,
const float3 &position)
{
- const float3 &v0 = mesh.mvert[mesh.mloop[looptri.tri[0]].v].co;
- const float3 &v1 = mesh.mvert[mesh.mloop[looptri.tri[1]].v].co;
- const float3 &v2 = mesh.mvert[mesh.mloop[looptri.tri[2]].v].co;
+ const float3 &v0 = verts[loops[looptri.tri[0]].v].co;
+ const float3 &v1 = verts[loops[looptri.tri[1]].v].co;
+ const float3 &v2 = verts[loops[looptri.tri[2]].v].co;
float3 bary_coords;
interp_weights_tri_v3(bary_coords, v0, v1, v2, position);
return bary_coords;
diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.cc
index a677a0d6ebb..8f9af5e9258 100644
--- a/source/blender/blenkernel/intern/mesh_tangent.c
+++ b/source/blender/blenkernel/intern/mesh_tangent.cc
@@ -27,77 +27,54 @@
#include "BLI_strict_flags.h"
#include "atomic_ops.h"
-#include "mikktspace.h"
+#include "mikktspace.hh"
/* -------------------------------------------------------------------- */
/** \name Mesh Tangent Calculations (Single Layer)
* \{ */
-/* Tangent space utils. */
+struct BKEMeshToTangent {
+ uint GetNumFaces()
+ {
+ return (uint)num_polys;
+ }
+
+ uint GetNumVerticesOfFace(const uint face_num)
+ {
+ return (uint)mpolys[face_num].totloop;
+ }
+
+ mikk::float3 GetPosition(const uint face_num, const uint vert_num)
+ {
+ const uint loop_idx = (uint)mpolys[face_num].loopstart + vert_num;
+ return mikk::float3(mverts[mloops[loop_idx].v].co);
+ }
+
+ mikk::float3 GetTexCoord(const uint face_num, const uint vert_num)
+ {
+ const float *uv = luvs[(uint)mpolys[face_num].loopstart + vert_num].uv;
+ return mikk::float3(uv[0], uv[1], 1.0f);
+ }
+
+ mikk::float3 GetNormal(const uint face_num, const uint vert_num)
+ {
+ return mikk::float3(lnors[(uint)mpolys[face_num].loopstart + vert_num]);
+ }
+
+ void SetTangentSpace(const uint face_num, const uint vert_num, mikk::float3 T, bool orientation)
+ {
+ float *p_res = tangents[(uint)mpolys[face_num].loopstart + vert_num];
+ copy_v4_fl4(p_res, T.x, T.y, T.z, orientation ? 1.0f : -1.0f);
+ }
-/* User data. */
-typedef struct {
const MPoly *mpolys; /* faces */
- const MLoop *mloops; /* faces's vertices */
+ const MLoop *mloops; /* faces vertices */
const MVert *mverts; /* vertices */
const MLoopUV *luvs; /* texture coordinates */
const float (*lnors)[3]; /* loops' normals */
float (*tangents)[4]; /* output tangents */
int num_polys; /* number of polygons */
-} BKEMeshToTangent;
-
-/* Mikktspace's API */
-static int get_num_faces(const SMikkTSpaceContext *pContext)
-{
- BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData;
- return p_mesh->num_polys;
-}
-
-static int get_num_verts_of_face(const SMikkTSpaceContext *pContext, const int face_idx)
-{
- BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData;
- return p_mesh->mpolys[face_idx].totloop;
-}
-
-static void get_position(const SMikkTSpaceContext *pContext,
- float r_co[3],
- const int face_idx,
- const int vert_idx)
-{
- BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData;
- const int loop_idx = p_mesh->mpolys[face_idx].loopstart + vert_idx;
- copy_v3_v3(r_co, p_mesh->mverts[p_mesh->mloops[loop_idx].v].co);
-}
-
-static void get_texture_coordinate(const SMikkTSpaceContext *pContext,
- float r_uv[2],
- const int face_idx,
- const int vert_idx)
-{
- BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData;
- copy_v2_v2(r_uv, p_mesh->luvs[p_mesh->mpolys[face_idx].loopstart + vert_idx].uv);
-}
-
-static void get_normal(const SMikkTSpaceContext *pContext,
- float r_no[3],
- const int face_idx,
- const int vert_idx)
-{
- BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData;
- copy_v3_v3(r_no, p_mesh->lnors[p_mesh->mpolys[face_idx].loopstart + vert_idx]);
-}
-
-static void set_tspace(const SMikkTSpaceContext *pContext,
- const float fv_tangent[3],
- const float face_sign,
- const int face_idx,
- const int vert_idx)
-{
- BKEMeshToTangent *p_mesh = (BKEMeshToTangent *)pContext->m_pUserData;
- float *p_res = p_mesh->tangents[p_mesh->mpolys[face_idx].loopstart + vert_idx];
- copy_v3_v3(p_res, fv_tangent);
- p_res[3] = face_sign;
-}
+};
void BKE_mesh_calc_loop_tangent_single_ex(const MVert *mverts,
const int UNUSED(numVerts),
@@ -110,23 +87,8 @@ void BKE_mesh_calc_loop_tangent_single_ex(const MVert *mverts,
const int numPolys,
ReportList *reports)
{
- BKEMeshToTangent mesh_to_tangent = {NULL};
- SMikkTSpaceContext s_context = {NULL};
- SMikkTSpaceInterface s_interface = {NULL};
-
- const MPoly *mp;
- int mp_index;
-
- /* First check we do have a tris/quads only mesh. */
- for (mp = mpolys, mp_index = 0; mp_index < numPolys; mp++, mp_index++) {
- if (mp->totloop > 4) {
- BKE_report(
- reports, RPT_ERROR, "Tangent space can only be computed for tris/quads, aborting");
- return;
- }
- }
-
/* Compute Mikktspace's tangent normals. */
+ BKEMeshToTangent mesh_to_tangent;
mesh_to_tangent.mpolys = mpolys;
mesh_to_tangent.mloops = mloops;
mesh_to_tangent.mverts = mverts;
@@ -135,19 +97,18 @@ void BKE_mesh_calc_loop_tangent_single_ex(const MVert *mverts,
mesh_to_tangent.tangents = r_looptangent;
mesh_to_tangent.num_polys = numPolys;
- s_context.m_pUserData = &mesh_to_tangent;
- s_context.m_pInterface = &s_interface;
- s_interface.m_getNumFaces = get_num_faces;
- s_interface.m_getNumVerticesOfFace = get_num_verts_of_face;
- s_interface.m_getPosition = get_position;
- s_interface.m_getTexCoord = get_texture_coordinate;
- s_interface.m_getNormal = get_normal;
- s_interface.m_setTSpaceBasic = set_tspace;
-
- /* 0 if failed */
- if (genTangSpaceDefault(&s_context) == false) {
- BKE_report(reports, RPT_ERROR, "Mikktspace failed to generate tangents for this mesh!");
+ mikk::Mikktspace<BKEMeshToTangent> mikk(mesh_to_tangent);
+
+ /* First check we do have a tris/quads only mesh. */
+ for (int i = 0; i < numPolys; i++) {
+ if (mpolys[i].totloop > 4) {
+ BKE_report(
+ reports, RPT_ERROR, "Tangent space can only be computed for tris/quads, aborting");
+ return;
+ }
}
+
+ mikk.genTangSpace();
}
void BKE_mesh_calc_loop_tangent_single(Mesh *mesh,
@@ -159,10 +120,10 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh,
/* Check we have valid texture coordinates first! */
if (uvmap) {
- loopuvs = CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvmap);
+ loopuvs = static_cast<MLoopUV *>(CustomData_get_layer_named(&mesh->ldata, CD_MLOOPUV, uvmap));
}
else {
- loopuvs = CustomData_get_layer(&mesh->ldata, CD_MLOOPUV);
+ loopuvs = static_cast<MLoopUV *>(CustomData_get_layer(&mesh->ldata, CD_MLOOPUV));
}
if (!loopuvs) {
BKE_reportf(reports,
@@ -172,21 +133,22 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh,
return;
}
- const float(*loopnors)[3] = CustomData_get_layer(&mesh->ldata, CD_NORMAL);
+ const float(*loopnors)[3] = static_cast<const float(*)[3]>(
+ CustomData_get_layer(&mesh->ldata, CD_NORMAL));
if (!loopnors) {
BKE_report(
reports, RPT_ERROR, "Tangent space computation needs loop normals, none found, aborting");
return;
}
- BKE_mesh_calc_loop_tangent_single_ex(mesh->mvert,
+ BKE_mesh_calc_loop_tangent_single_ex(BKE_mesh_verts(mesh),
mesh->totvert,
- mesh->mloop,
+ BKE_mesh_loops(mesh),
r_looptangents,
loopnors,
loopuvs,
mesh->totloop,
- mesh->mpoly,
+ BKE_mesh_polys(mesh),
mesh->totpoly,
reports);
}
@@ -200,249 +162,148 @@ void BKE_mesh_calc_loop_tangent_single(Mesh *mesh,
/* Necessary complexity to handle looptri's as quads for correct tangents */
#define USE_LOOPTRI_DETECT_QUADS
-typedef struct {
- const float (*precomputedFaceNormals)[3];
- const float (*precomputedLoopNormals)[3];
- const MLoopTri *looptri;
- const MLoopUV *mloopuv; /* texture coordinates */
- const MPoly *mpoly; /* indices */
- const MLoop *mloop; /* indices */
- const MVert *mvert; /* vertex coordinates */
- const float (*vert_normals)[3];
- const float (*orco)[3];
- float (*tangent)[4]; /* destination */
- int numTessFaces;
-
-#ifdef USE_LOOPTRI_DETECT_QUADS
- /* map from 'fake' face index to looptri,
- * quads will point to the first looptri of the quad */
- const int *face_as_quad_map;
- int num_face_as_quad_map;
-#endif
-
-} SGLSLMeshToTangent;
-
-/* interface */
-static int dm_ts_GetNumFaces(const SMikkTSpaceContext *pContext)
-{
- SGLSLMeshToTangent *pMesh = pContext->m_pUserData;
-
+struct SGLSLMeshToTangent {
+ uint GetNumFaces()
+ {
#ifdef USE_LOOPTRI_DETECT_QUADS
- return pMesh->num_face_as_quad_map;
+ return (uint)num_face_as_quad_map;
#else
- return pMesh->numTessFaces;
+ return (uint)numTessFaces;
#endif
-}
-
-static int dm_ts_GetNumVertsOfFace(const SMikkTSpaceContext *pContext, const int face_num)
-{
-#ifdef USE_LOOPTRI_DETECT_QUADS
- SGLSLMeshToTangent *pMesh = pContext->m_pUserData;
- if (pMesh->face_as_quad_map) {
- const MLoopTri *lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]];
- const MPoly *mp = &pMesh->mpoly[lt->poly];
- if (mp->totloop == 4) {
- return 4;
- }
}
- return 3;
-#else
- UNUSED_VARS(pContext, face_num);
- return 3;
-#endif
-}
-
-static void dm_ts_GetPosition(const SMikkTSpaceContext *pContext,
- float r_co[3],
- const int face_num,
- const int vert_index)
-{
- // assert(vert_index >= 0 && vert_index < 4);
- SGLSLMeshToTangent *pMesh = pContext->m_pUserData;
- const MLoopTri *lt;
- uint loop_index;
- const float *co;
+ uint GetNumVerticesOfFace(const uint face_num)
+ {
#ifdef USE_LOOPTRI_DETECT_QUADS
- if (pMesh->face_as_quad_map) {
- lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]];
- const MPoly *mp = &pMesh->mpoly[lt->poly];
- if (mp->totloop == 4) {
- loop_index = (uint)(mp->loopstart + vert_index);
- goto finally;
+ if (face_as_quad_map) {
+ const MLoopTri *lt = &looptri[face_as_quad_map[face_num]];
+ const MPoly *mp = &mpoly[lt->poly];
+ if (mp->totloop == 4) {
+ return 4;
+ }
}
- /* fall through to regular triangle */
- }
- else {
- lt = &pMesh->looptri[face_num];
- }
+ return 3;
#else
- lt = &pMesh->looptri[face_num];
+ UNUSED_VARS(pContext, face_num);
+ return 3;
#endif
- loop_index = lt->tri[vert_index];
-
-finally:
- co = pMesh->mvert[pMesh->mloop[loop_index].v].co;
- copy_v3_v3(r_co, co);
-}
-
-static void dm_ts_GetTextureCoordinate(const SMikkTSpaceContext *pContext,
- float r_uv[2],
- const int face_num,
- const int vert_index)
-{
- // assert(vert_index >= 0 && vert_index < 4);
- SGLSLMeshToTangent *pMesh = pContext->m_pUserData;
- const MLoopTri *lt;
- uint loop_index;
+ }
+ uint GetLoop(const uint face_num, const uint vert_num, const MLoopTri *&lt)
+ {
#ifdef USE_LOOPTRI_DETECT_QUADS
- if (pMesh->face_as_quad_map) {
- lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]];
- const MPoly *mp = &pMesh->mpoly[lt->poly];
- if (mp->totloop == 4) {
- loop_index = (uint)(mp->loopstart + vert_index);
- goto finally;
+ if (face_as_quad_map) {
+ lt = &looptri[face_as_quad_map[face_num]];
+ const MPoly *mp = &mpoly[lt->poly];
+ if (mp->totloop == 4) {
+ return ((uint)mp->loopstart + vert_num);
+ }
+ /* fall through to regular triangle */
+ }
+ else {
+ lt = &looptri[face_num];
}
- /* fall through to regular triangle */
- }
- else {
- lt = &pMesh->looptri[face_num];
- }
#else
- lt = &pMesh->looptri[face_num];
+ lt = &looptri[face_num];
#endif
- loop_index = lt->tri[vert_index];
-
-finally:
- if (pMesh->mloopuv != NULL) {
- const float *uv = pMesh->mloopuv[loop_index].uv;
- copy_v2_v2(r_uv, uv);
- }
- else {
- const float *orco = pMesh->orco[pMesh->mloop[loop_index].v];
- map_to_sphere(&r_uv[0], &r_uv[1], orco[0], orco[1], orco[2]);
+ return lt->tri[vert_num];
}
-}
-static void dm_ts_GetNormal(const SMikkTSpaceContext *pContext,
- float r_no[3],
- const int face_num,
- const int vert_index)
-{
- // assert(vert_index >= 0 && vert_index < 4);
- SGLSLMeshToTangent *pMesh = (SGLSLMeshToTangent *)pContext->m_pUserData;
- const MLoopTri *lt;
- uint loop_index;
+ mikk::float3 GetPosition(const uint face_num, const uint vert_num)
+ {
+ const MLoopTri *lt;
+ uint loop_index = GetLoop(face_num, vert_num, lt);
+ return mikk::float3(mvert[mloop[loop_index].v].co);
+ }
-#ifdef USE_LOOPTRI_DETECT_QUADS
- if (pMesh->face_as_quad_map) {
- lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]];
- const MPoly *mp = &pMesh->mpoly[lt->poly];
- if (mp->totloop == 4) {
- loop_index = (uint)(mp->loopstart + vert_index);
- goto finally;
+ mikk::float3 GetTexCoord(const uint face_num, const uint vert_num)
+ {
+ const MLoopTri *lt;
+ uint loop_index = GetLoop(face_num, vert_num, lt);
+ if (mloopuv != nullptr) {
+ const float *uv = mloopuv[loop_index].uv;
+ return mikk::float3(uv[0], uv[1], 1.0f);
+ }
+ else {
+ const float *l_orco = orco[mloop[loop_index].v];
+ float u, v;
+ map_to_sphere(&u, &v, l_orco[0], l_orco[1], l_orco[2]);
+ return mikk::float3(u, v, 1.0f);
}
- /* fall through to regular triangle */
- }
- else {
- lt = &pMesh->looptri[face_num];
}
-#else
- lt = &pMesh->looptri[face_num];
-#endif
- loop_index = lt->tri[vert_index];
-finally:
- if (pMesh->precomputedLoopNormals) {
- copy_v3_v3(r_no, pMesh->precomputedLoopNormals[loop_index]);
- }
- else if ((pMesh->mpoly[lt->poly].flag & ME_SMOOTH) == 0) { /* flat */
- if (pMesh->precomputedFaceNormals) {
- copy_v3_v3(r_no, pMesh->precomputedFaceNormals[lt->poly]);
+ mikk::float3 GetNormal(const uint face_num, const uint vert_num)
+ {
+ const MLoopTri *lt;
+ uint loop_index = GetLoop(face_num, vert_num, lt);
+ if (precomputedLoopNormals) {
+ return mikk::float3(precomputedLoopNormals[loop_index]);
}
- else {
-#ifdef USE_LOOPTRI_DETECT_QUADS
- const MPoly *mp = &pMesh->mpoly[lt->poly];
- if (mp->totloop == 4) {
- normal_quad_v3(r_no,
- pMesh->mvert[pMesh->mloop[mp->loopstart + 0].v].co,
- pMesh->mvert[pMesh->mloop[mp->loopstart + 1].v].co,
- pMesh->mvert[pMesh->mloop[mp->loopstart + 2].v].co,
- pMesh->mvert[pMesh->mloop[mp->loopstart + 3].v].co);
+ else if ((mpoly[lt->poly].flag & ME_SMOOTH) == 0) { /* flat */
+ if (precomputedFaceNormals) {
+ return mikk::float3(precomputedFaceNormals[lt->poly]);
}
- else
+ else {
+#ifdef USE_LOOPTRI_DETECT_QUADS
+ const MPoly *mp = &mpoly[lt->poly];
+ float normal[3];
+ if (mp->totloop == 4) {
+ normal_quad_v3(normal,
+ mvert[mloop[mp->loopstart + 0].v].co,
+ mvert[mloop[mp->loopstart + 1].v].co,
+ mvert[mloop[mp->loopstart + 2].v].co,
+ mvert[mloop[mp->loopstart + 3].v].co);
+ }
+ else
#endif
- {
- normal_tri_v3(r_no,
- pMesh->mvert[pMesh->mloop[lt->tri[0]].v].co,
- pMesh->mvert[pMesh->mloop[lt->tri[1]].v].co,
- pMesh->mvert[pMesh->mloop[lt->tri[2]].v].co);
+ {
+ normal_tri_v3(normal,
+ mvert[mloop[lt->tri[0]].v].co,
+ mvert[mloop[lt->tri[1]].v].co,
+ mvert[mloop[lt->tri[2]].v].co);
+ }
+ return mikk::float3(normal);
}
}
+ else {
+ return mikk::float3(vert_normals[mloop[loop_index].v]);
+ }
}
- else {
- copy_v3_v3(r_no, pMesh->vert_normals[pMesh->mloop[loop_index].v]);
- }
-}
-static void dm_ts_SetTSpace(const SMikkTSpaceContext *pContext,
- const float fvTangent[3],
- const float fSign,
- const int face_num,
- const int vert_index)
-{
- // assert(vert_index >= 0 && vert_index < 4);
- SGLSLMeshToTangent *pMesh = (SGLSLMeshToTangent *)pContext->m_pUserData;
- const MLoopTri *lt;
- uint loop_index;
+ void SetTangentSpace(const uint face_num, const uint vert_num, mikk::float3 T, bool orientation)
+ {
+ const MLoopTri *lt;
+ uint loop_index = GetLoop(face_num, vert_num, lt);
-#ifdef USE_LOOPTRI_DETECT_QUADS
- if (pMesh->face_as_quad_map) {
- lt = &pMesh->looptri[pMesh->face_as_quad_map[face_num]];
- const MPoly *mp = &pMesh->mpoly[lt->poly];
- if (mp->totloop == 4) {
- loop_index = (uint)(mp->loopstart + vert_index);
- goto finally;
- }
- /* fall through to regular triangle */
+ copy_v4_fl4(tangent[loop_index], T.x, T.y, T.z, orientation ? 1.0f : -1.0f);
}
- else {
- lt = &pMesh->looptri[face_num];
- }
-#else
- lt = &pMesh->looptri[face_num];
-#endif
- loop_index = lt->tri[vert_index];
- float *pRes;
+ const float (*precomputedFaceNormals)[3];
+ const float (*precomputedLoopNormals)[3];
+ const MLoopTri *looptri;
+ const MLoopUV *mloopuv; /* texture coordinates */
+ const MPoly *mpoly; /* indices */
+ const MLoop *mloop; /* indices */
+ const MVert *mvert; /* vertex coordinates */
+ const float (*vert_normals)[3];
+ const float (*orco)[3];
+ float (*tangent)[4]; /* destination */
+ int numTessFaces;
-finally:
- pRes = pMesh->tangent[loop_index];
- copy_v3_v3(pRes, fvTangent);
- pRes[3] = fSign;
-}
+#ifdef USE_LOOPTRI_DETECT_QUADS
+ /* map from 'fake' face index to looptri,
+ * quads will point to the first looptri of the quad */
+ const int *face_as_quad_map;
+ int num_face_as_quad_map;
+#endif
+};
static void DM_calc_loop_tangents_thread(TaskPool *__restrict UNUSED(pool), void *taskdata)
{
- struct SGLSLMeshToTangent *mesh2tangent = taskdata;
- /* new computation method */
- {
- SMikkTSpaceContext sContext = {NULL};
- SMikkTSpaceInterface sInterface = {NULL};
-
- sContext.m_pUserData = mesh2tangent;
- sContext.m_pInterface = &sInterface;
- sInterface.m_getNumFaces = dm_ts_GetNumFaces;
- sInterface.m_getNumVerticesOfFace = dm_ts_GetNumVertsOfFace;
- sInterface.m_getPosition = dm_ts_GetPosition;
- sInterface.m_getTexCoord = dm_ts_GetTextureCoordinate;
- sInterface.m_getNormal = dm_ts_GetNormal;
- sInterface.m_setTSpaceBasic = dm_ts_SetTSpace;
-
- /* 0 if failed */
- genTangSpaceDefault(&sContext);
- }
+ SGLSLMeshToTangent *mesh_data = static_cast<SGLSLMeshToTangent *>(taskdata);
+
+ mikk::Mikktspace<SGLSLMeshToTangent> mikk(*mesh_data);
+ mikk.genTangSpace();
}
void BKE_mesh_add_loop_tangent_named_layer_for_uv(CustomData *uv_data,
@@ -452,7 +313,8 @@ void BKE_mesh_add_loop_tangent_named_layer_for_uv(CustomData *uv_data,
{
if (CustomData_get_named_layer_index(tan_data, CD_TANGENT, layer_name) == -1 &&
CustomData_get_named_layer_index(uv_data, CD_MLOOPUV, layer_name) != -1) {
- CustomData_add_layer_named(tan_data, CD_TANGENT, CD_CALLOC, NULL, numLoopData, layer_name);
+ CustomData_add_layer_named(
+ tan_data, CD_TANGENT, CD_SET_DEFAULT, nullptr, numLoopData, layer_name);
}
}
@@ -581,7 +443,7 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
if ((tangent_mask & DM_TANGENT_MASK_ORCO) &&
CustomData_get_named_layer_index(loopdata, CD_TANGENT, "") == -1) {
CustomData_add_layer_named(
- loopdata_out, CD_TANGENT, CD_CALLOC, NULL, (int)loopdata_out_len, "");
+ loopdata_out, CD_TANGENT, CD_SET_DEFAULT, nullptr, (int)loopdata_out_len, "");
}
if (calc_act && act_uv_name[0]) {
BKE_mesh_add_loop_tangent_named_layer_for_uv(
@@ -594,14 +456,14 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
#ifdef USE_LOOPTRI_DETECT_QUADS
int num_face_as_quad_map;
- int *face_as_quad_map = NULL;
+ int *face_as_quad_map = nullptr;
/* map faces to quads */
if (looptri_len != mpoly_len) {
/* Over allocate, since we don't know how many ngon or quads we have. */
/* map fake face index to looptri */
- face_as_quad_map = MEM_mallocN(sizeof(int) * looptri_len, __func__);
+ face_as_quad_map = static_cast<int *>(MEM_mallocN(sizeof(int) * looptri_len, __func__));
int k, j;
for (k = 0, j = 0; j < (int)looptri_len; k++, j++) {
face_as_quad_map[k] = j;
@@ -619,7 +481,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_HIGH);
+ TaskPool *task_pool = BLI_task_pool_create(nullptr, TASK_PRIORITY_HIGH);
tangent_mask_curr = 0;
/* Calculate tangent layers */
@@ -644,9 +506,9 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
mesh2tangent->precomputedLoopNormals = loop_normals;
mesh2tangent->precomputedFaceNormals = poly_normals;
- mesh2tangent->orco = NULL;
- mesh2tangent->mloopuv = CustomData_get_layer_named(
- loopdata, CD_MLOOPUV, loopdata_out->layers[index].name);
+ mesh2tangent->orco = nullptr;
+ mesh2tangent->mloopuv = static_cast<const MLoopUV *>(
+ CustomData_get_layer_named(loopdata, CD_MLOOPUV, loopdata_out->layers[index].name));
/* Fill the resulting tangent_mask */
if (!mesh2tangent->mloopuv) {
@@ -666,8 +528,8 @@ void BKE_mesh_calc_loop_tangent_ex(const MVert *mvert,
tangent_mask_curr |= (short)(1 << (uv_ind - uv_start));
}
- mesh2tangent->tangent = loopdata_out->layers[index].data;
- BLI_task_pool_push(task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, NULL);
+ mesh2tangent->tangent = static_cast<float(*)[4]>(loopdata_out->layers[index].data);
+ BLI_task_pool_push(task_pool, DM_calc_loop_tangents_thread, mesh2tangent, false, nullptr);
}
BLI_assert(tangent_mask_curr == tangent_mask);
@@ -716,26 +578,28 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval,
{
BKE_mesh_runtime_looptri_ensure(me_eval);
- /* TODO(campbell): store in Mesh.runtime to avoid recalculation. */
+ /* TODO(@campbellbarton): store in Mesh.runtime to avoid recalculation. */
short tangent_mask = 0;
- BKE_mesh_calc_loop_tangent_ex(me_eval->mvert,
- me_eval->mpoly,
- (uint)me_eval->totpoly,
- me_eval->mloop,
- me_eval->runtime.looptris.array,
- (uint)me_eval->runtime.looptris.len,
- &me_eval->ldata,
- calc_active_tangent,
- tangent_names,
- tangent_names_len,
- BKE_mesh_vertex_normals_ensure(me_eval),
- BKE_mesh_poly_normals_ensure(me_eval),
- CustomData_get_layer(&me_eval->ldata, CD_NORMAL),
- CustomData_get_layer(&me_eval->vdata, CD_ORCO), /* may be NULL */
- /* result */
- &me_eval->ldata,
- (uint)me_eval->totloop,
- &tangent_mask);
+ BKE_mesh_calc_loop_tangent_ex(
+ BKE_mesh_verts(me_eval),
+ BKE_mesh_polys(me_eval),
+ (uint)me_eval->totpoly,
+ BKE_mesh_loops(me_eval),
+ me_eval->runtime.looptris.array,
+ (uint)me_eval->runtime.looptris.len,
+ &me_eval->ldata,
+ calc_active_tangent,
+ tangent_names,
+ tangent_names_len,
+ BKE_mesh_vertex_normals_ensure(me_eval),
+ BKE_mesh_poly_normals_ensure(me_eval),
+ static_cast<const float(*)[3]>(CustomData_get_layer(&me_eval->ldata, CD_NORMAL)),
+ /* may be nullptr */
+ static_cast<const float(*)[3]>(CustomData_get_layer(&me_eval->vdata, CD_ORCO)),
+ /* result */
+ &me_eval->ldata,
+ (uint)me_eval->totloop,
+ &tangent_mask);
}
/** \} */
diff --git a/source/blender/blenkernel/intern/mesh_tessellate.c b/source/blender/blenkernel/intern/mesh_tessellate.cc
index 173fb98912b..de4c60b28db 100644
--- a/source/blender/blenkernel/intern/mesh_tessellate.c
+++ b/source/blender/blenkernel/intern/mesh_tessellate.cc
@@ -10,7 +10,7 @@
* \see bmesh_mesh_tessellate.c for the #BMesh equivalent of this file.
*/
-#include <limits.h>
+#include <climits>
#include "MEM_guardedalloc.h"
@@ -120,13 +120,14 @@ BLI_INLINE void mesh_calc_tessellation_for_face_impl(const MLoop *mloop,
const uint totfilltri = mp_totloop - 2;
MemArena *pf_arena = *pf_arena_p;
- if (UNLIKELY(pf_arena == NULL)) {
+ if (UNLIKELY(pf_arena == nullptr)) {
pf_arena = *pf_arena_p = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
}
- uint(*tris)[3] = tris = BLI_memarena_alloc(pf_arena, sizeof(*tris) * (size_t)totfilltri);
- float(*projverts)[2] = projverts = BLI_memarena_alloc(
- pf_arena, sizeof(*projverts) * (size_t)mp_totloop);
+ uint(*tris)[3] = static_cast<uint(*)[3]>(
+ BLI_memarena_alloc(pf_arena, sizeof(*tris) * (size_t)totfilltri));
+ float(*projverts)[2] = static_cast<float(*)[2]>(
+ BLI_memarena_alloc(pf_arena, sizeof(*projverts) * (size_t)mp_totloop));
ml = mloop + mp_loopstart;
for (uint j = 0; j < mp_totloop; j++, ml++) {
@@ -157,7 +158,7 @@ static void mesh_calc_tessellation_for_face(const MLoop *mloop,
MemArena **pf_arena_p)
{
mesh_calc_tessellation_for_face_impl(
- mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, false, NULL);
+ mloop, mpoly, mvert, poly_index, mlt, pf_arena_p, false, nullptr);
}
static void mesh_calc_tessellation_for_face_with_normal(const MLoop *mloop,
@@ -180,11 +181,11 @@ static void mesh_recalc_looptri__single_threaded(const MLoop *mloop,
MLoopTri *mlooptri,
const float (*poly_normals)[3])
{
- MemArena *pf_arena = NULL;
+ MemArena *pf_arena = nullptr;
const MPoly *mp = mpoly;
uint tri_index = 0;
- if (poly_normals != NULL) {
+ if (poly_normals != nullptr) {
for (uint poly_index = 0; poly_index < (uint)totpoly; poly_index++, mp++) {
mesh_calc_tessellation_for_face_with_normal(mloop,
mpoly,
@@ -206,7 +207,7 @@ static void mesh_recalc_looptri__single_threaded(const MLoop *mloop,
if (pf_arena) {
BLI_memarena_free(pf_arena);
- pf_arena = NULL;
+ pf_arena = nullptr;
}
BLI_assert(tri_index == (uint)poly_to_tri_count(totpoly, totloop));
UNUSED_VARS_NDEBUG(totloop);
@@ -232,8 +233,8 @@ static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata,
const int index,
const TaskParallelTLS *__restrict tls)
{
- const struct TessellationUserData *data = userdata;
- struct TessellationUserTLS *tls_data = tls->userdata_chunk;
+ const TessellationUserData *data = static_cast<const TessellationUserData *>(userdata);
+ TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls->userdata_chunk);
const int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart);
mesh_calc_tessellation_for_face_impl(data->mloop,
data->mpoly,
@@ -242,15 +243,15 @@ static void mesh_calc_tessellation_for_face_fn(void *__restrict userdata,
&data->mlooptri[tri_index],
&tls_data->pf_arena,
false,
- NULL);
+ nullptr);
}
static void mesh_calc_tessellation_for_face_with_normal_fn(void *__restrict userdata,
const int index,
const TaskParallelTLS *__restrict tls)
{
- const struct TessellationUserData *data = userdata;
- struct TessellationUserTLS *tls_data = tls->userdata_chunk;
+ const TessellationUserData *data = static_cast<const TessellationUserData *>(userdata);
+ TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls->userdata_chunk);
const int tri_index = poly_to_tri_count(index, data->mpoly[index].loopstart);
mesh_calc_tessellation_for_face_impl(data->mloop,
data->mpoly,
@@ -265,7 +266,7 @@ static void mesh_calc_tessellation_for_face_with_normal_fn(void *__restrict user
static void mesh_calc_tessellation_for_face_free_fn(const void *__restrict UNUSED(userdata),
void *__restrict tls_v)
{
- struct TessellationUserTLS *tls_data = tls_v;
+ TessellationUserTLS *tls_data = static_cast<TessellationUserTLS *>(tls_v);
if (tls_data->pf_arena) {
BLI_memarena_free(tls_data->pf_arena);
}
@@ -279,15 +280,15 @@ static void mesh_recalc_looptri__multi_threaded(const MLoop *mloop,
MLoopTri *mlooptri,
const float (*poly_normals)[3])
{
- struct TessellationUserTLS tls_data_dummy = {NULL};
-
- struct TessellationUserData data = {
- .mloop = mloop,
- .mpoly = mpoly,
- .mvert = mvert,
- .mlooptri = mlooptri,
- .poly_normals = poly_normals,
+ struct TessellationUserTLS tls_data_dummy = {nullptr};
+
+ struct TessellationUserData data {
};
+ data.mloop = mloop;
+ data.mpoly = mpoly;
+ data.mvert = mvert;
+ data.mlooptri = mlooptri;
+ data.poly_normals = poly_normals;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
@@ -313,10 +314,10 @@ void BKE_mesh_recalc_looptri(const MLoop *mloop,
MLoopTri *mlooptri)
{
if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) {
- mesh_recalc_looptri__single_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL);
+ mesh_recalc_looptri__single_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, nullptr);
}
else {
- mesh_recalc_looptri__multi_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, NULL);
+ mesh_recalc_looptri__multi_threaded(mloop, mpoly, mvert, totloop, totpoly, mlooptri, nullptr);
}
}
@@ -328,7 +329,7 @@ void BKE_mesh_recalc_looptri_with_normals(const MLoop *mloop,
MLoopTri *mlooptri,
const float (*poly_normals)[3])
{
- BLI_assert(poly_normals != NULL);
+ BLI_assert(poly_normals != nullptr);
if (totloop < MESH_FACE_TESSELLATE_THREADED_LIMIT) {
mesh_recalc_looptri__single_threaded(
mloop, mpoly, mvert, totloop, totpoly, mlooptri, poly_normals);
diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc
index 9b2697ecc84..47de7245ccc 100644
--- a/source/blender/blenkernel/intern/mesh_validate.cc
+++ b/source/blender/blenkernel/intern/mesh_validate.cc
@@ -24,6 +24,7 @@
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_mesh.h"
@@ -32,6 +33,9 @@
#include "MEM_guardedalloc.h"
+using blender::MutableSpan;
+using blender::Span;
+
/* loop v/e are unsigned, so using max uint_32 value as invalid marker... */
#define INVALID_LOOP_EDGE_MARKER 4294967295u
@@ -238,6 +242,10 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
} \
(void)0
+ blender::bke::AttributeWriter<int> material_indices =
+ mesh->attributes_for_write().lookup_for_write<int>("material_index");
+ blender::MutableVArraySpan<int> material_indices_span(material_indices.varray);
+
MVert *mv = mverts;
MEdge *me;
MLoop *ml;
@@ -559,10 +567,10 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
/* Material index, isolated from other tests here. While large indices are clamped,
* negative indices aren't supported by drawing, exporters etc.
* To check the indices are in range, use #BKE_mesh_validate_material_indices */
- if (mp->mat_nr < 0) {
- PRINT_ERR("\tPoly %u has invalid material (%d)", sp->index, mp->mat_nr);
+ if (material_indices && material_indices_span[i] < 0) {
+ PRINT_ERR("\tPoly %u has invalid material (%d)", sp->index, material_indices_span[i]);
if (do_fixes) {
- mp->mat_nr = 0;
+ material_indices_span[i] = 0;
}
}
@@ -916,6 +924,9 @@ bool BKE_mesh_validate_arrays(Mesh *mesh,
}
}
+ material_indices_span.save();
+ material_indices.finish();
+
PRINT_MSG("%s: finished\n\n", __func__);
*r_changed = (fix_flag.as_flag || free_flag.as_flag || recalc_flag.as_flag);
@@ -1001,10 +1012,6 @@ bool BKE_mesh_validate_all_customdata(CustomData *vdata,
CustomData_MeshMasks mask = {0};
if (check_meshmask) {
mask = CD_MASK_MESH;
- /* Normal data isn't in the mask since it is derived data,
- * but it is valid and should not be removed. */
- mask.vmask |= CD_MASK_NORMAL;
- mask.pmask |= CD_MASK_NORMAL;
}
is_valid &= mesh_validate_customdata(
@@ -1017,7 +1024,6 @@ bool BKE_mesh_validate_all_customdata(CustomData *vdata,
pdata, mask.pmask, totpoly, do_verbose, do_fixes, &is_change_p);
const int tot_uvloop = CustomData_number_of_layers(ldata, CD_MLOOPUV);
- const int tot_vcolloop = CustomData_number_of_layers(ldata, CD_PROP_BYTE_COLOR);
if (tot_uvloop > MAX_MTFACE) {
PRINT_ERR(
"\tMore UV layers than %d allowed, %d last ones won't be available for render, shaders, "
@@ -1025,13 +1031,6 @@ bool BKE_mesh_validate_all_customdata(CustomData *vdata,
MAX_MTFACE,
tot_uvloop - MAX_MTFACE);
}
- if (tot_vcolloop > MAX_MCOL) {
- PRINT_ERR(
- "\tMore VCol layers than %d allowed, %d last ones won't be available for render, shaders, "
- "etc.\n",
- MAX_MCOL,
- tot_vcolloop - MAX_MCOL);
- }
/* check indices of clone/stencil */
if (do_fixes && CustomData_get_clone_layer(ldata, CD_MLOOPUV) >= tot_uvloop) {
@@ -1068,19 +1067,23 @@ bool BKE_mesh_validate(Mesh *me, const bool do_verbose, const bool cddata_check_
do_verbose,
true,
&changed);
+ MutableSpan<MVert> verts = me->verts_for_write();
+ MutableSpan<MEdge> edges = me->edges_for_write();
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ MutableSpan<MLoop> loops = me->loops_for_write();
BKE_mesh_validate_arrays(me,
- me->mvert,
- me->totvert,
- me->medge,
- me->totedge,
- me->mface,
+ verts.data(),
+ verts.size(),
+ edges.data(),
+ edges.size(),
+ (MFace *)CustomData_get_layer(&me->fdata, CD_MFACE),
me->totface,
- me->mloop,
- me->totloop,
- me->mpoly,
- me->totpoly,
- me->dvert,
+ loops.data(),
+ loops.size(),
+ polys.data(),
+ polys.size(),
+ me->deform_verts_for_write().data(),
do_verbose,
true,
&changed);
@@ -1117,18 +1120,23 @@ bool BKE_mesh_is_valid(Mesh *me)
do_fixes,
&changed);
+ MutableSpan<MVert> verts = me->verts_for_write();
+ MutableSpan<MEdge> edges = me->edges_for_write();
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ MutableSpan<MLoop> loops = me->loops_for_write();
+
is_valid &= BKE_mesh_validate_arrays(me,
- me->mvert,
- me->totvert,
- me->medge,
- me->totedge,
- me->mface,
+ verts.data(),
+ verts.size(),
+ edges.data(),
+ edges.size(),
+ (MFace *)CustomData_get_layer(&me->fdata, CD_MFACE),
me->totface,
- me->mloop,
- me->totloop,
- me->mpoly,
- me->totpoly,
- me->dvert,
+ loops.data(),
+ loops.size(),
+ polys.data(),
+ polys.size(),
+ me->deform_verts_for_write().data(),
do_verbose,
do_fixes,
&changed);
@@ -1140,19 +1148,20 @@ bool BKE_mesh_is_valid(Mesh *me)
bool BKE_mesh_validate_material_indices(Mesh *me)
{
- /* Cast to unsigned to catch negative indices too. */
- const uint16_t mat_nr_max = max_ii(0, me->totcol - 1);
- MPoly *mp;
- const int totpoly = me->totpoly;
- int i;
+ const int mat_nr_max = max_ii(0, me->totcol - 1);
bool is_valid = true;
- for (mp = me->mpoly, i = 0; i < totpoly; i++, mp++) {
- if ((uint16_t)mp->mat_nr > mat_nr_max) {
- mp->mat_nr = 0;
+ blender::bke::AttributeWriter<int> material_indices =
+ me->attributes_for_write().lookup_for_write<int>("material_index");
+ blender::MutableVArraySpan<int> material_indices_span(material_indices.varray);
+ for (const int i : material_indices_span.index_range()) {
+ if (material_indices_span[i] < 0 || material_indices_span[i] > mat_nr_max) {
+ material_indices_span[i] = 0;
is_valid = false;
}
}
+ material_indices_span.save();
+ material_indices.finish();
if (!is_valid) {
DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY_ALL_MODES);
@@ -1173,11 +1182,12 @@ void BKE_mesh_strip_loose_faces(Mesh *me)
/* NOTE: We need to keep this for edge creation (for now?), and some old `readfile.c` code. */
MFace *f;
int a, b;
+ MFace *mfaces = (MFace *)CustomData_get_layer(&me->fdata, CD_MFACE);
- for (a = b = 0, f = me->mface; a < me->totface; a++, f++) {
+ for (a = b = 0, f = mfaces; a < me->totface; a++, f++) {
if (f->v3) {
if (a != b) {
- memcpy(&me->mface[b], f, sizeof(me->mface[b]));
+ memcpy(&mfaces[b], f, sizeof(mfaces[b]));
CustomData_copy_data(&me->fdata, &me->fdata, a, b, 1);
}
b++;
@@ -1191,13 +1201,16 @@ void BKE_mesh_strip_loose_faces(Mesh *me)
void BKE_mesh_strip_loose_polysloops(Mesh *me)
{
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ MutableSpan<MLoop> loops = me->loops_for_write();
+
MPoly *p;
MLoop *l;
int a, b;
/* New loops idx! */
int *new_idx = (int *)MEM_mallocN(sizeof(int) * me->totloop, __func__);
- for (a = b = 0, p = me->mpoly; a < me->totpoly; a++, p++) {
+ for (a = b = 0, p = polys.data(); a < me->totpoly; a++, p++) {
bool invalid = false;
int i = p->loopstart;
int stop = i + p->totloop;
@@ -1206,7 +1219,7 @@ void BKE_mesh_strip_loose_polysloops(Mesh *me)
invalid = true;
}
else {
- l = &me->mloop[i];
+ l = &loops[i];
i = stop - i;
/* If one of the poly's loops is invalid, the whole poly is invalid! */
for (; i--; l++) {
@@ -1219,7 +1232,7 @@ void BKE_mesh_strip_loose_polysloops(Mesh *me)
if (p->totloop >= 3 && !invalid) {
if (a != b) {
- memcpy(&me->mpoly[b], p, sizeof(me->mpoly[b]));
+ memcpy(&polys[b], p, sizeof(polys[b]));
CustomData_copy_data(&me->pdata, &me->pdata, a, b, 1);
}
b++;
@@ -1231,10 +1244,10 @@ void BKE_mesh_strip_loose_polysloops(Mesh *me)
}
/* And now, get rid of invalid loops. */
- for (a = b = 0, l = me->mloop; a < me->totloop; a++, l++) {
+ for (a = b = 0, l = loops.data(); a < me->totloop; a++, l++) {
if (l->e != INVALID_LOOP_EDGE_MARKER) {
if (a != b) {
- memcpy(&me->mloop[b], l, sizeof(me->mloop[b]));
+ memcpy(&loops[b], l, sizeof(loops[b]));
CustomData_copy_data(&me->ldata, &me->ldata, a, b, 1);
}
new_idx[a] = b;
@@ -1253,8 +1266,8 @@ void BKE_mesh_strip_loose_polysloops(Mesh *me)
/* And now, update polys' start loop index. */
/* NOTE: At this point, there should never be any poly using a striped loop! */
- for (a = 0, p = me->mpoly; a < me->totpoly; a++, p++) {
- p->loopstart = new_idx[p->loopstart];
+ for (const int i : polys.index_range()) {
+ polys[i].loopstart = new_idx[polys[i].loopstart];
}
MEM_freeN(new_idx);
@@ -1263,14 +1276,14 @@ void BKE_mesh_strip_loose_polysloops(Mesh *me)
void BKE_mesh_strip_loose_edges(Mesh *me)
{
MEdge *e;
- MLoop *l;
int a, b;
uint *new_idx = (uint *)MEM_mallocN(sizeof(int) * me->totedge, __func__);
+ MutableSpan<MEdge> edges = me->edges_for_write();
- for (a = b = 0, e = me->medge; a < me->totedge; a++, e++) {
+ for (a = b = 0, e = edges.data(); a < me->totedge; a++, e++) {
if (e->v1 != e->v2) {
if (a != b) {
- memcpy(&me->medge[b], e, sizeof(me->medge[b]));
+ memcpy(&edges[b], e, sizeof(edges[b]));
CustomData_copy_data(&me->edata, &me->edata, a, b, 1);
}
new_idx[a] = b;
@@ -1288,8 +1301,9 @@ void BKE_mesh_strip_loose_edges(Mesh *me)
/* And now, update loops' edge indices. */
/* XXX We hope no loop was pointing to a striped edge!
* Else, its e will be set to INVALID_LOOP_EDGE_MARKER :/ */
- for (a = 0, l = me->mloop; a < me->totloop; a++, l++) {
- l->e = new_idx[l->e];
+ MutableSpan<MLoop> loops = me->loops_for_write();
+ for (MLoop &loop : loops) {
+ loop.e = new_idx[loop.e];
}
MEM_freeN(new_idx);
@@ -1347,10 +1361,10 @@ static int vergedgesort(const void *v1, const void *v2)
/* Create edges based on known verts and faces,
* this function is only used when loading very old blend files */
-static void mesh_calc_edges_mdata(MVert *UNUSED(allvert),
- MFace *allface,
+static void mesh_calc_edges_mdata(const MVert *UNUSED(allvert),
+ const MFace *allface,
MLoop *allloop,
- MPoly *allpoly,
+ const MPoly *allpoly,
int UNUSED(totvert),
int totface,
int UNUSED(totloop),
@@ -1359,8 +1373,8 @@ static void mesh_calc_edges_mdata(MVert *UNUSED(allvert),
MEdge **r_medge,
int *r_totedge)
{
- MPoly *mpoly;
- MFace *mface;
+ const MPoly *mpoly;
+ const MFace *mface;
MEdge *medge, *med;
EdgeHash *hash;
struct EdgeSort *edsort, *ed;
@@ -1483,28 +1497,29 @@ void BKE_mesh_calc_edges_legacy(Mesh *me, const bool use_old)
{
MEdge *medge;
int totedge = 0;
-
- mesh_calc_edges_mdata(me->mvert,
- me->mface,
- me->mloop,
- me->mpoly,
- me->totvert,
+ const Span<MVert> verts = me->verts();
+ const Span<MPoly> polys = me->polys();
+ MutableSpan<MLoop> loops = me->loops_for_write();
+
+ mesh_calc_edges_mdata(verts.data(),
+ (MFace *)CustomData_get_layer(&me->fdata, CD_MFACE),
+ loops.data(),
+ polys.data(),
+ verts.size(),
me->totface,
- me->totloop,
- me->totpoly,
+ loops.size(),
+ polys.size(),
use_old,
&medge,
&totedge);
if (totedge == 0) {
/* flag that mesh has edges */
- me->medge = medge;
me->totedge = 0;
return;
}
medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge);
- me->medge = medge;
me->totedge = totedge;
BKE_mesh_strip_loose_faces(me);
@@ -1512,18 +1527,18 @@ void BKE_mesh_calc_edges_legacy(Mesh *me, const bool use_old)
void BKE_mesh_calc_edges_loose(Mesh *mesh)
{
- MEdge *med = mesh->medge;
- for (int i = 0; i < mesh->totedge; i++, med++) {
- med->flag |= ME_LOOSEEDGE;
+ const Span<MLoop> loops = mesh->loops();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+
+ for (const int i : edges.index_range()) {
+ edges[i].flag |= ME_LOOSEEDGE;
}
- MLoop *ml = mesh->mloop;
- for (int i = 0; i < mesh->totloop; i++, ml++) {
- mesh->medge[ml->e].flag &= ~ME_LOOSEEDGE;
+ for (const int i : loops.index_range()) {
+ edges[loops[i].e].flag &= ~ME_LOOSEEDGE;
}
- med = mesh->medge;
- for (int i = 0; i < mesh->totedge; i++, med++) {
- if (med->flag & ME_LOOSEEDGE) {
- med->flag |= ME_EDGEDRAW;
+ for (const int i : edges.index_range()) {
+ if (edges[i].flag & ME_LOOSEEDGE) {
+ edges[i].flag |= ME_EDGEDRAW;
}
}
}
@@ -1532,8 +1547,9 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh)
{
const int numFaces = mesh->totface;
EdgeSet *eh = BLI_edgeset_new_ex(__func__, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(numFaces));
+ MFace *mfaces = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
- MFace *mf = mesh->mface;
+ MFace *mf = mfaces;
for (int i = 0; i < numFaces; i++, mf++) {
BLI_edgeset_add(eh, mf->v1, mf->v2);
BLI_edgeset_add(eh, mf->v2, mf->v3);
@@ -1552,8 +1568,8 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh)
/* write new edges into a temporary CustomData */
CustomData edgeData;
CustomData_reset(&edgeData);
- CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, nullptr, numEdges);
- CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, nullptr, numEdges);
+ CustomData_add_layer(&edgeData, CD_MEDGE, CD_SET_DEFAULT, nullptr, numEdges);
+ CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_SET_DEFAULT, nullptr, numEdges);
MEdge *med = (MEdge *)CustomData_get_layer(&edgeData, CD_MEDGE);
int *index = (int *)CustomData_get_layer(&edgeData, CD_ORIGINDEX);
@@ -1573,8 +1589,6 @@ void BKE_mesh_calc_edges_tessface(Mesh *mesh)
mesh->edata = edgeData;
mesh->totedge = numEdges;
- mesh->medge = (MEdge *)CustomData_get_layer(&mesh->edata, CD_MEDGE);
-
BLI_edgeset_free(eh);
}
diff --git a/source/blender/blenkernel/intern/mesh_wrapper.cc b/source/blender/blenkernel/intern/mesh_wrapper.cc
index 0362e4866e3..101fad2fce8 100644
--- a/source/blender/blenkernel/intern/mesh_wrapper.cc
+++ b/source/blender/blenkernel/intern/mesh_wrapper.cc
@@ -46,6 +46,8 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
+using blender::Span;
+
Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
const CustomData_MeshMasks *cd_mask_extra,
const float (*vert_coords)[3],
@@ -61,7 +63,7 @@ Mesh *BKE_mesh_wrapper_from_editmesh_with_coords(BMEditMesh *em,
}
/* Use edit-mesh directly where possible. */
- me->runtime.is_original = true;
+ me->runtime.is_original_bmesh = true;
me->edit_mesh = static_cast<BMEditMesh *>(MEM_dupallocN(em));
me->edit_mesh->is_shallow_copy = true;
@@ -103,11 +105,7 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
/* Must isolate multithreaded tasks while holding a mutex lock. */
blender::threading::isolate_task([&]() {
- const eMeshWrapperType geom_type_orig = static_cast<eMeshWrapperType>(
- me->runtime.wrapper_type);
- me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA;
-
- switch (geom_type_orig) {
+ switch (static_cast<eMeshWrapperType>(me->runtime.wrapper_type)) {
case ME_WRAPPER_TYPE_MDATA:
case ME_WRAPPER_TYPE_SUBD: {
break; /* Quiet warning. */
@@ -132,20 +130,24 @@ void BKE_mesh_wrapper_ensure_mdata(Mesh *me)
* There is also a performance aspect, where this also assumes that original indices are
* always needed when converting an edit mesh to a mesh. That might be wrong, but it's not
* harmful. */
- BKE_mesh_ensure_default_orig_index_customdata(me);
+ BKE_mesh_ensure_default_orig_index_customdata_no_check(me);
EditMeshData *edit_data = me->runtime.edit_data;
if (edit_data->vertexCos) {
BKE_mesh_vert_coords_apply(me, edit_data->vertexCos);
- me->runtime.is_original = false;
+ me->runtime.is_original_bmesh = false;
}
break;
}
}
if (me->runtime.wrapper_type_finalize) {
- BKE_mesh_wrapper_deferred_finalize(me, &me->runtime.cd_mask_extra);
+ BKE_mesh_wrapper_deferred_finalize_mdata(me, &me->runtime.cd_mask_extra);
}
+
+ /* Keep type assignment last, so that read-only access only uses the mdata code paths after all
+ * the underlying data has been initialized. */
+ me->runtime.wrapper_type = ME_WRAPPER_TYPE_MDATA;
});
BLI_mutex_unlock(mesh_eval_mutex);
@@ -195,9 +197,9 @@ void BKE_mesh_wrapper_vert_coords_copy(const Mesh *me,
case ME_WRAPPER_TYPE_MDATA:
case ME_WRAPPER_TYPE_SUBD: {
BLI_assert(vert_coords_len <= me->totvert);
- const MVert *mvert = me->mvert;
+ const Span<MVert> verts = me->verts();
for (int i = 0; i < vert_coords_len; i++) {
- copy_v3_v3(vert_coords[i], mvert[i].co);
+ copy_v3_v3(vert_coords[i], verts[i].co);
}
return;
}
@@ -233,9 +235,9 @@ void BKE_mesh_wrapper_vert_coords_copy_with_mat4(const Mesh *me,
case ME_WRAPPER_TYPE_MDATA:
case ME_WRAPPER_TYPE_SUBD: {
BLI_assert(vert_coords_len == me->totvert);
- const MVert *mvert = me->mvert;
+ const Span<MVert> verts = me->verts();
for (int i = 0; i < vert_coords_len; i++) {
- mul_v3_m4v3(vert_coords[i], mat, mvert[i].co);
+ mul_v3_m4v3(vert_coords[i], mat, verts[i].co);
}
return;
}
@@ -343,7 +345,7 @@ static Mesh *mesh_wrapper_ensure_subdivision(Mesh *me)
if (use_clnors) {
float(*lnors)[3] = static_cast<float(*)[3]>(
CustomData_get_layer(&subdiv_mesh->ldata, CD_NORMAL));
- BLI_assert(lnors != NULL);
+ BLI_assert(lnors != nullptr);
BKE_mesh_set_custom_normals(subdiv_mesh, lnors);
CustomData_set_layer_flag(&me->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
CustomData_set_layer_flag(&subdiv_mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c
index 6348d83362e..ba84b27bf31 100644
--- a/source/blender/blenkernel/intern/modifier.c
+++ b/source/blender/blenkernel/intern/modifier.c
@@ -142,7 +142,8 @@ static ModifierData *modifier_allocate_and_init(int type)
md->type = type;
md->mode = eModifierMode_Realtime | eModifierMode_Render;
md->flag = eModifierFlag_OverrideLibrary_Local;
- md->ui_expand_flag = 1; /* Only open the main panel at the beginning, not the sub-panels. */
+ /* Only open the main panel at the beginning, not the sub-panels. */
+ md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
if (mti->flags & eModifierTypeFlag_EnableInEditmode) {
md->mode |= eModifierMode_Editmode;
@@ -456,6 +457,40 @@ void BKE_modifier_set_error(const Object *ob, ModifierData *md, const char *_for
CLOG_ERROR(&LOG, "Object: \"%s\", Modifier: \"%s\", %s", ob->id.name + 2, md->name, md->error);
}
+void BKE_modifier_set_warning(const struct Object *ob,
+ struct ModifierData *md,
+ const char *_format,
+ ...)
+{
+ char buffer[512];
+ va_list ap;
+ const char *format = TIP_(_format);
+
+ va_start(ap, _format);
+ vsnprintf(buffer, sizeof(buffer), format, ap);
+ va_end(ap);
+ buffer[sizeof(buffer) - 1] = '\0';
+
+ /* Store the warning in the same field as the error.
+ * It is not expected to have both error and warning and having a single place to store the
+ * message simplifies interface code. */
+
+ if (md->error) {
+ MEM_freeN(md->error);
+ }
+
+ md->error = BLI_strdup(buffer);
+
+#ifndef NDEBUG
+ if ((md->mode & eModifierMode_Virtual) == 0) {
+ /* Ensure correct object is passed in. */
+ BLI_assert(BKE_modifier_get_original(ob, md) != NULL);
+ }
+#endif
+
+ UNUSED_VARS_NDEBUG(ob);
+}
+
int BKE_modifiers_get_cage_index(const Scene *scene,
Object *ob,
int *r_lastPossibleCageIndex,
@@ -568,7 +603,6 @@ bool BKE_modifier_is_nonlocal_in_liboverride(const Object *ob, const ModifierDat
}
CDMaskLink *BKE_modifier_calc_data_masks(const struct Scene *scene,
- Object *ob,
ModifierData *md,
CustomData_MeshMasks *final_datamask,
int required_mode,
@@ -591,7 +625,7 @@ CDMaskLink *BKE_modifier_calc_data_masks(const struct Scene *scene,
}
if (mti->requiredDataMask) {
- mti->requiredDataMask(ob, md, &curr->mask);
+ mti->requiredDataMask(md, &curr->mask);
}
if (previewmd == md && previewmask != NULL) {
@@ -1001,8 +1035,7 @@ void BKE_modifier_deform_vertsEM(ModifierData *md,
/* end modifier callback wrappers */
-Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval,
- const bool get_cage_mesh)
+Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval)
{
Mesh *me = NULL;
@@ -1011,17 +1044,11 @@ Mesh *BKE_modifier_get_evaluated_mesh_from_evaluated_object(Object *ob_eval,
BMEditMesh *em = BKE_editmesh_from_object(ob_eval);
/* 'em' might not exist yet in some cases, just after loading a .blend file, see T57878. */
if (em != NULL) {
- Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob_eval);
- Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob_eval);
-
- me = (get_cage_mesh && editmesh_eval_cage != NULL) ? editmesh_eval_cage :
- editmesh_eval_final;
+ me = BKE_object_get_editmesh_eval_final(ob_eval);
}
}
if (me == NULL) {
- me = (get_cage_mesh && ob_eval->runtime.mesh_deform_eval != NULL) ?
- ob_eval->runtime.mesh_deform_eval :
- BKE_object_get_evaluated_mesh(ob_eval);
+ me = BKE_object_get_evaluated_mesh(ob_eval);
}
return me;
diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c
index 19ee2ba6605..46e05b39076 100644
--- a/source/blender/blenkernel/intern/movieclip.c
+++ b/source/blender/blenkernel/intern/movieclip.c
@@ -343,7 +343,7 @@ IDTypeInfo IDType_ID_MC = {
.foreach_id = movie_clip_foreach_id,
.foreach_cache = movie_clip_foreach_cache,
.foreach_path = movie_clip_foreach_path,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = movieclip_blend_write,
.blend_read_data = movieclip_blend_read_data,
diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c
index 63945f9ed42..ce61ca483e9 100644
--- a/source/blender/blenkernel/intern/multires.c
+++ b/source/blender/blenkernel/intern/multires.c
@@ -62,7 +62,7 @@ typedef enum {
static void multiresModifier_disp_run(
DerivedMesh *dm, Mesh *me, DerivedMesh *dm2, DispOp op, CCGElem **oldGridData, int totlvl);
-/** Customdata */
+/** Custom-data. */
void multires_customdata_delete(Mesh *me)
{
@@ -183,6 +183,7 @@ static BLI_bitmap *multires_mdisps_downsample_hidden(const BLI_bitmap *old_hidde
static void multires_output_hidden_to_ccgdm(CCGDerivedMesh *ccgdm, Mesh *me, int level)
{
+ const MPoly *polys = BKE_mesh_polys(me);
const MDisps *mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS);
BLI_bitmap **grid_hidden = ccgdm->gridHidden;
int *gridOffset;
@@ -191,7 +192,7 @@ static void multires_output_hidden_to_ccgdm(CCGDerivedMesh *ccgdm, Mesh *me, int
gridOffset = ccgdm->dm.getGridOffset(&ccgdm->dm);
for (i = 0; i < me->totpoly; i++) {
- for (j = 0; j < me->mpoly[i].totloop; j++) {
+ for (j = 0; j < polys[i].totloop; j++) {
int g = gridOffset[i] + j;
const MDisps *md = &mdisps[g];
BLI_bitmap *gh = md->hidden;
@@ -466,15 +467,16 @@ void multires_force_external_reload(Object *object)
static int get_levels_from_disps(Object *ob)
{
Mesh *me = ob->data;
+ const MPoly *polys = BKE_mesh_polys(me);
MDisps *mdisp, *md;
int i, j, totlvl = 0;
mdisp = CustomData_get_layer(&me->ldata, CD_MDISPS);
for (i = 0; i < me->totpoly; i++) {
- md = mdisp + me->mpoly[i].loopstart;
+ md = mdisp + polys[i].loopstart;
- for (j = 0; j < me->mpoly[i].totloop; j++, md++) {
+ for (j = 0; j < polys[i].totloop; j++, md++) {
if (md->totdisp == 0) {
continue;
}
@@ -633,6 +635,7 @@ static void multires_grid_paint_mask_downsample(GridPaintMask *gpm, int level)
static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl)
{
Mesh *me = (Mesh *)ob->data;
+ const MPoly *polys = BKE_mesh_polys(me);
int levels = mmd->totlvl - lvl;
MDisps *mdisps;
GridPaintMask *gpm;
@@ -652,8 +655,8 @@ static void multires_del_higher(MultiresModifierData *mmd, Object *ob, int lvl)
int i, j;
for (i = 0; i < me->totpoly; i++) {
- for (j = 0; j < me->mpoly[i].totloop; j++) {
- int g = me->mpoly[i].loopstart + j;
+ for (j = 0; j < polys[i].totloop; j++) {
+ int g = polys[i].loopstart + j;
MDisps *mdisp = &mdisps[g];
float(*disps)[3], (*ndisps)[3], (*hdisps)[3];
int totdisp = multires_grid_tot[lvl];
@@ -828,7 +831,7 @@ typedef struct MultiresThreadedData {
CCGElem **gridData, **subGridData;
CCGKey *key;
CCGKey *sub_key;
- MPoly *mpoly;
+ const MPoly *mpoly;
MDisps *mdisps;
GridPaintMask *grid_paint_mask;
int *gridOffset;
@@ -846,7 +849,7 @@ static void multires_disp_run_cb(void *__restrict userdata,
CCGElem **gridData = tdata->gridData;
CCGElem **subGridData = tdata->subGridData;
CCGKey *key = tdata->key;
- MPoly *mpoly = tdata->mpoly;
+ const MPoly *mpoly = tdata->mpoly;
MDisps *mdisps = tdata->mdisps;
GridPaintMask *grid_paint_mask = tdata->grid_paint_mask;
int *gridOffset = tdata->gridOffset;
@@ -939,7 +942,7 @@ static void multiresModifier_disp_run(
CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm;
CCGElem **gridData, **subGridData;
CCGKey key;
- MPoly *mpoly = me->mpoly;
+ const MPoly *mpoly = BKE_mesh_polys(me);
MDisps *mdisps = CustomData_get_layer(&me->ldata, CD_MDISPS);
GridPaintMask *grid_paint_mask = NULL;
int *gridOffset;
@@ -960,7 +963,7 @@ static void multiresModifier_disp_run(
if (!mdisps) {
if (op == CALC_DISPLACEMENTS) {
- mdisps = CustomData_add_layer(&me->ldata, CD_MDISPS, CD_DEFAULT, NULL, me->totloop);
+ mdisps = CustomData_add_layer(&me->ldata, CD_MDISPS, CD_SET_DEFAULT, NULL, me->totloop);
}
else {
return;
@@ -1487,7 +1490,7 @@ void multires_ensure_external_read(struct Mesh *mesh, int top_level)
MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS);
if (mdisps == NULL) {
- mdisps = CustomData_add_layer(&mesh->ldata, CD_MDISPS, CD_DEFAULT, NULL, mesh->totloop);
+ mdisps = CustomData_add_layer(&mesh->ldata, CD_MDISPS, CD_SET_DEFAULT, NULL, mesh->totloop);
}
const int totloop = mesh->totloop;
diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c
index b50a0787fe3..17e4860ab1b 100644
--- a/source/blender/blenkernel/intern/multires_reshape.c
+++ b/source/blender/blenkernel/intern/multires_reshape.c
@@ -181,7 +181,8 @@ void multiresModifier_subdivide_to_level(struct Object *object,
* are allocated at a proper level and return. */
const bool has_mdisps = CustomData_has_layer(&coarse_mesh->ldata, CD_MDISPS);
if (!has_mdisps) {
- CustomData_add_layer(&coarse_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, coarse_mesh->totloop);
+ CustomData_add_layer(
+ &coarse_mesh->ldata, CD_MDISPS, CD_SET_DEFAULT, NULL, coarse_mesh->totloop);
}
/* NOTE: Subdivision happens from the top level of the existing multires modifier. If it is set
diff --git a/source/blender/blenkernel/intern/multires_reshape.h b/source/blender/blenkernel/intern/multires_reshape.h
index c4f320b86d8..5e2822ad5df 100644
--- a/source/blender/blenkernel/intern/multires_reshape.h
+++ b/source/blender/blenkernel/intern/multires_reshape.h
@@ -14,8 +14,12 @@
struct Depsgraph;
struct GridPaintMask;
struct MDisps;
+struct MEdge;
struct Mesh;
+struct MLoop;
+struct MPoly;
struct MultiresModifierData;
+struct MVert;
struct Object;
struct Subdiv;
struct SubdivCCG;
@@ -30,6 +34,10 @@ typedef struct MultiresReshapeContext {
/* Base mesh from original object.
* NOTE: Does NOT include any leading modifiers in it. */
struct Mesh *base_mesh;
+ const struct MVert *base_verts;
+ const struct MEdge *base_edges;
+ const struct MPoly *base_polys;
+ const struct MLoop *base_loops;
/* Subdivision surface created for multires modifier.
*
diff --git a/source/blender/blenkernel/intern/multires_reshape_apply_base.c b/source/blender/blenkernel/intern/multires_reshape_apply_base.c
index f7d29806353..81b0abbdcf5 100644
--- a/source/blender/blenkernel/intern/multires_reshape_apply_base.c
+++ b/source/blender/blenkernel/intern/multires_reshape_apply_base.c
@@ -30,11 +30,14 @@
void multires_reshape_apply_base_update_mesh_coords(MultiresReshapeContext *reshape_context)
{
Mesh *base_mesh = reshape_context->base_mesh;
- const MLoop *mloop = base_mesh->mloop;
- MVert *mvert = base_mesh->mvert;
+ MVert *base_verts = BKE_mesh_verts_for_write(base_mesh);
+ /* Update the context in case the vertices were duplicated. */
+ reshape_context->base_verts = base_verts;
+
+ const MLoop *mloop = reshape_context->base_loops;
for (int loop_index = 0; loop_index < base_mesh->totloop; ++loop_index) {
const MLoop *loop = &mloop[loop_index];
- MVert *vert = &mvert[loop->v];
+ MVert *vert = &base_verts[loop->v];
GridCoord grid_coord;
grid_coord.grid_index = loop_index;
@@ -66,13 +69,15 @@ static float v3_dist_from_plane(const float v[3], const float center[3], const f
void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape_context)
{
Mesh *base_mesh = reshape_context->base_mesh;
-
+ MVert *base_verts = BKE_mesh_verts_for_write(base_mesh);
+ /* Update the context in case the vertices were duplicated. */
+ reshape_context->base_verts = base_verts;
MeshElemMap *pmap;
int *pmap_mem;
BKE_mesh_vert_poly_map_create(&pmap,
&pmap_mem,
- base_mesh->mpoly,
- base_mesh->mloop,
+ reshape_context->base_polys,
+ reshape_context->base_loops,
base_mesh->totvert,
base_mesh->totpoly,
base_mesh->totloop);
@@ -80,7 +85,7 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape
float(*origco)[3] = MEM_calloc_arrayN(
base_mesh->totvert, sizeof(float[3]), "multires apply base origco");
for (int i = 0; i < base_mesh->totvert; i++) {
- copy_v3_v3(origco[i], base_mesh->mvert[i].co);
+ copy_v3_v3(origco[i], base_verts[i].co);
}
for (int i = 0; i < base_mesh->totvert; i++) {
@@ -94,11 +99,11 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape
/* Find center. */
int tot = 0;
for (int j = 0; j < pmap[i].count; j++) {
- const MPoly *p = &base_mesh->mpoly[pmap[i].indices[j]];
+ const MPoly *p = &reshape_context->base_polys[pmap[i].indices[j]];
/* This double counts, not sure if that's bad or good. */
for (int k = 0; k < p->totloop; k++) {
- const int vndx = base_mesh->mloop[p->loopstart + k].v;
+ const int vndx = reshape_context->base_loops[p->loopstart + k].v;
if (vndx != i) {
add_v3_v3(center, origco[vndx]);
tot++;
@@ -109,7 +114,7 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape
/* Find normal. */
for (int j = 0; j < pmap[i].count; j++) {
- const MPoly *p = &base_mesh->mpoly[pmap[i].indices[j]];
+ const MPoly *p = &reshape_context->base_polys[pmap[i].indices[j]];
MPoly fake_poly;
MLoop *fake_loops;
float(*fake_co)[3];
@@ -122,7 +127,7 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape
fake_co = MEM_malloc_arrayN(p->totloop, sizeof(float[3]), "fake_co");
for (int k = 0; k < p->totloop; k++) {
- const int vndx = base_mesh->mloop[p->loopstart + k].v;
+ const int vndx = reshape_context->base_loops[p->loopstart + k].v;
fake_loops[k].v = k;
@@ -143,10 +148,10 @@ void multires_reshape_apply_base_refit_base_mesh(MultiresReshapeContext *reshape
normalize_v3(avg_no);
/* Push vertex away from the plane. */
- const float dist = v3_dist_from_plane(base_mesh->mvert[i].co, center, avg_no);
+ const float dist = v3_dist_from_plane(base_verts[i].co, center, avg_no);
copy_v3_v3(push, avg_no);
mul_v3_fl(push, dist);
- add_v3_v3(base_mesh->mvert[i].co, push);
+ add_v3_v3(base_verts[i].co, push);
}
MEM_freeN(origco);
diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c
index 8246de12ebf..e887f543dc5 100644
--- a/source/blender/blenkernel/intern/multires_reshape_smooth.c
+++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c
@@ -354,10 +354,9 @@ static GridCoord *vertex_grid_coord_with_grid_index(const Vertex *vertex, const
/* Get grid coordinates which correspond to corners of the given face.
* All the grid coordinates will be from the same grid index. */
-static void grid_coords_from_face_vertices(
- const MultiresReshapeSmoothContext *reshape_smooth_context,
- const Face *face,
- const GridCoord *grid_coords[])
+static void grid_coords_from_face_verts(const MultiresReshapeSmoothContext *reshape_smooth_context,
+ const Face *face,
+ const GridCoord *grid_coords[])
{
BLI_assert(face->num_corners == 4);
@@ -417,7 +416,7 @@ static void foreach_toplevel_grid_coord_task(void *__restrict userdata_v,
const Face *face = &reshape_smooth_context->geometry.faces[face_index];
const GridCoord *face_grid_coords[4];
- grid_coords_from_face_vertices(reshape_smooth_context, face, face_grid_coords);
+ grid_coords_from_face_verts(reshape_smooth_context, face, face_grid_coords);
for (int y = 0; y < inner_grid_size; ++y) {
const float ptex_v = (float)y * inner_grid_size_1_inv;
@@ -643,8 +642,7 @@ static void foreach_vertex(const SubdivForeachContext *foreach_context,
const int face_index = multires_reshape_grid_to_face_index(reshape_context,
grid_coord.grid_index);
- const Mesh *base_mesh = reshape_context->base_mesh;
- const MPoly *base_poly = &base_mesh->mpoly[face_index];
+ const MPoly *base_poly = &reshape_context->base_polys[face_index];
const int num_corners = base_poly->totloop;
const int start_grid_index = reshape_context->face_start_grid_index[face_index];
const int corner = grid_coord.grid_index - start_grid_index;
@@ -834,8 +832,7 @@ static void foreach_edge(const struct SubdivForeachContext *foreach_context,
return;
}
/* Edges without crease are to be ignored as well. */
- const Mesh *base_mesh = reshape_context->base_mesh;
- const MEdge *base_edge = &base_mesh->medge[coarse_edge_index];
+ const MEdge *base_edge = &reshape_context->base_edges[coarse_edge_index];
const char crease = get_effective_crease_char(reshape_smooth_context, base_edge);
if (crease == 0) {
return;
@@ -847,9 +844,9 @@ static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshap
{
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
const Mesh *base_mesh = reshape_context->base_mesh;
- const MPoly *base_mpoly = base_mesh->mpoly;
- const MLoop *base_mloop = base_mesh->mloop;
- const MEdge *base_edge = base_mesh->medge;
+ const MPoly *base_mpoly = reshape_context->base_polys;
+ const MLoop *base_mloop = reshape_context->base_loops;
+ const MEdge *base_edge = reshape_context->base_edges;
reshape_smooth_context->non_loose_base_edge_map = BLI_BITMAP_NEW(base_mesh->totedge,
"non_loose_base_edge_map");
diff --git a/source/blender/blenkernel/intern/multires_reshape_subdivide.c b/source/blender/blenkernel/intern/multires_reshape_subdivide.c
index 9fa3e93a1e6..effea2467bc 100644
--- a/source/blender/blenkernel/intern/multires_reshape_subdivide.c
+++ b/source/blender/blenkernel/intern/multires_reshape_subdivide.c
@@ -28,12 +28,16 @@
static void multires_subdivide_create_object_space_linear_grids(Mesh *mesh)
{
+ const MVert *verts = BKE_mesh_verts(mesh);
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
+
MDisps *mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS);
const int totpoly = mesh->totpoly;
for (int p = 0; p < totpoly; p++) {
- MPoly *poly = &mesh->mpoly[p];
+ const MPoly *poly = &polys[p];
float poly_center[3];
- BKE_mesh_calc_poly_center(poly, &mesh->mloop[poly->loopstart], mesh->mvert, poly_center);
+ BKE_mesh_calc_poly_center(poly, &loops[poly->loopstart], verts, poly_center);
for (int l = 0; l < poly->totloop; l++) {
const int loop_index = poly->loopstart + l;
@@ -44,14 +48,14 @@ static void multires_subdivide_create_object_space_linear_grids(Mesh *mesh)
int prev_loop_index = l - 1 >= 0 ? loop_index - 1 : loop_index + poly->totloop - 1;
int next_loop_index = l + 1 < poly->totloop ? loop_index + 1 : poly->loopstart;
- MLoop *loop = &mesh->mloop[loop_index];
- MLoop *loop_next = &mesh->mloop[next_loop_index];
- MLoop *loop_prev = &mesh->mloop[prev_loop_index];
+ const MLoop *loop = &loops[loop_index];
+ const MLoop *loop_next = &loops[next_loop_index];
+ const MLoop *loop_prev = &loops[prev_loop_index];
copy_v3_v3(disps[0], poly_center);
- mid_v3_v3v3(disps[1], mesh->mvert[loop->v].co, mesh->mvert[loop_next->v].co);
- mid_v3_v3v3(disps[2], mesh->mvert[loop->v].co, mesh->mvert[loop_prev->v].co);
- copy_v3_v3(disps[3], mesh->mvert[loop->v].co);
+ mid_v3_v3v3(disps[1], verts[loop->v].co, verts[loop_next->v].co);
+ mid_v3_v3v3(disps[2], verts[loop->v].co, verts[loop_prev->v].co);
+ copy_v3_v3(disps[3], verts[loop->v].co);
}
}
}
@@ -68,7 +72,8 @@ void multires_subdivide_create_tangent_displacement_linear_grids(Object *object,
const bool has_mdisps = CustomData_has_layer(&coarse_mesh->ldata, CD_MDISPS);
if (!has_mdisps) {
- CustomData_add_layer(&coarse_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, coarse_mesh->totloop);
+ CustomData_add_layer(
+ &coarse_mesh->ldata, CD_MDISPS, CD_SET_DEFAULT, NULL, coarse_mesh->totloop);
}
if (new_top_level == 1) {
diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c
index 34aa90aa554..5b60394feda 100644
--- a/source/blender/blenkernel/intern/multires_reshape_util.c
+++ b/source/blender/blenkernel/intern/multires_reshape_util.c
@@ -66,7 +66,7 @@ static void context_zero(MultiresReshapeContext *reshape_context)
static void context_init_lookup(MultiresReshapeContext *reshape_context)
{
const Mesh *base_mesh = reshape_context->base_mesh;
- const MPoly *mpoly = base_mesh->mpoly;
+ const MPoly *mpoly = reshape_context->base_polys;
const int num_faces = base_mesh->totpoly;
reshape_context->face_start_grid_index = MEM_malloc_arrayN(
@@ -152,6 +152,10 @@ bool multires_reshape_context_create_from_base_mesh(MultiresReshapeContext *resh
reshape_context->mmd = mmd;
reshape_context->base_mesh = base_mesh;
+ reshape_context->base_verts = BKE_mesh_verts(base_mesh);
+ reshape_context->base_edges = BKE_mesh_edges(base_mesh);
+ reshape_context->base_polys = BKE_mesh_polys(base_mesh);
+ reshape_context->base_loops = BKE_mesh_loops(base_mesh);
reshape_context->subdiv = multires_reshape_create_subdiv(NULL, object, mmd);
reshape_context->need_free_subdiv = true;
@@ -185,6 +189,10 @@ bool multires_reshape_context_create_from_object(MultiresReshapeContext *reshape
reshape_context->mmd = mmd;
reshape_context->base_mesh = base_mesh;
+ reshape_context->base_verts = BKE_mesh_verts(base_mesh);
+ reshape_context->base_edges = BKE_mesh_edges(base_mesh);
+ reshape_context->base_polys = BKE_mesh_polys(base_mesh);
+ reshape_context->base_loops = BKE_mesh_loops(base_mesh);
reshape_context->subdiv = multires_reshape_create_subdiv(depsgraph, object, mmd);
reshape_context->need_free_subdiv = true;
@@ -212,6 +220,10 @@ bool multires_reshape_context_create_from_ccg(MultiresReshapeContext *reshape_co
context_zero(reshape_context);
reshape_context->base_mesh = base_mesh;
+ reshape_context->base_verts = BKE_mesh_verts(base_mesh);
+ reshape_context->base_edges = BKE_mesh_edges(base_mesh);
+ reshape_context->base_polys = BKE_mesh_polys(base_mesh);
+ reshape_context->base_loops = BKE_mesh_loops(base_mesh);
reshape_context->subdiv = subdiv_ccg->subdiv;
reshape_context->need_free_subdiv = false;
@@ -255,6 +267,10 @@ bool multires_reshape_context_create_from_subdiv(MultiresReshapeContext *reshape
reshape_context->mmd = mmd;
reshape_context->base_mesh = base_mesh;
+ reshape_context->base_verts = BKE_mesh_verts(base_mesh);
+ reshape_context->base_edges = BKE_mesh_edges(base_mesh);
+ reshape_context->base_polys = BKE_mesh_polys(base_mesh);
+ reshape_context->base_loops = BKE_mesh_loops(base_mesh);
reshape_context->subdiv = subdiv;
reshape_context->need_free_subdiv = false;
@@ -344,7 +360,7 @@ int multires_reshape_grid_to_corner(const MultiresReshapeContext *reshape_contex
bool multires_reshape_is_quad_face(const MultiresReshapeContext *reshape_context, int face_index)
{
- const MPoly *base_poly = &reshape_context->base_mesh->mpoly[face_index];
+ const MPoly *base_poly = &reshape_context->base_polys[face_index];
return (base_poly->totloop == 4);
}
@@ -636,8 +652,7 @@ static void foreach_grid_face_coordinate_task(void *__restrict userdata_v,
const MultiresReshapeContext *reshape_context = data->reshape_context;
- const Mesh *base_mesh = data->reshape_context->base_mesh;
- const MPoly *mpoly = base_mesh->mpoly;
+ const MPoly *mpoly = reshape_context->base_polys;
const int grid_size = data->grid_size;
const float grid_size_1_inv = 1.0f / (((float)grid_size) - 1.0f);
diff --git a/source/blender/blenkernel/intern/multires_reshape_vertcos.c b/source/blender/blenkernel/intern/multires_reshape_vertcos.c
index e50f5e71bf7..7cf853e0374 100644
--- a/source/blender/blenkernel/intern/multires_reshape_vertcos.c
+++ b/source/blender/blenkernel/intern/multires_reshape_vertcos.c
@@ -52,8 +52,7 @@ static void multires_reshape_vertcos_foreach_vertex(const SubdivForeachContext *
const int face_index = multires_reshape_grid_to_face_index(reshape_context,
grid_coord.grid_index);
- const Mesh *base_mesh = reshape_context->base_mesh;
- const MPoly *base_poly = &base_mesh->mpoly[face_index];
+ const MPoly *base_poly = &reshape_context->base_polys[face_index];
const int num_corners = base_poly->totloop;
const int start_grid_index = reshape_context->face_start_grid_index[face_index];
const int corner = grid_coord.grid_index - start_grid_index;
diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c
index cad680ecedd..353fbec6933 100644
--- a/source/blender/blenkernel/intern/multires_unsubdivide.c
+++ b/source/blender/blenkernel/intern/multires_unsubdivide.c
@@ -161,7 +161,7 @@ static bool is_vertex_diagonal(BMVert *from_v, BMVert *to_v)
*/
static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex)
{
- bool *visited_vertices = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices");
+ bool *visited_verts = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices");
GSQueue *queue;
queue = BLI_gsqueue_new(sizeof(BMVert *));
@@ -177,7 +177,7 @@ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex
int neighbor_vertex_index = BM_elem_index_get(neighbor_v);
if (neighbor_v != initial_vertex && is_vertex_diagonal(neighbor_v, initial_vertex)) {
BLI_gsqueue_push(queue, &neighbor_v);
- visited_vertices[neighbor_vertex_index] = true;
+ visited_verts[neighbor_vertex_index] = true;
BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true);
}
}
@@ -211,10 +211,10 @@ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex
BM_ITER_ELEM (f, &iter, diagonal_v, BM_FACES_OF_VERT) {
BM_ITER_ELEM (neighbor_v, &iter_a, f, BM_VERTS_OF_FACE) {
int neighbor_vertex_index = BM_elem_index_get(neighbor_v);
- if (!visited_vertices[neighbor_vertex_index] && neighbor_v != diagonal_v &&
+ if (!visited_verts[neighbor_vertex_index] && neighbor_v != diagonal_v &&
is_vertex_diagonal(neighbor_v, diagonal_v)) {
BLI_gsqueue_push(queue, &neighbor_v);
- visited_vertices[neighbor_vertex_index] = true;
+ visited_verts[neighbor_vertex_index] = true;
BM_elem_flag_set(neighbor_v, BM_ELEM_TAG, true);
}
}
@@ -224,7 +224,7 @@ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex
}
BLI_gsqueue_free(queue);
- MEM_freeN(visited_vertices);
+ MEM_freeN(visited_verts);
}
/**
@@ -352,14 +352,14 @@ static bool unsubdivide_tag_disconnected_mesh_element(BMesh *bm, int *elem_id, i
*/
static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id)
{
- bool *visited_vertices = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices");
+ bool *visited_verts = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices");
int current_id = 0;
for (int i = 0; i < bm->totvert; i++) {
- if (!visited_vertices[i]) {
+ if (!visited_verts[i]) {
GSQueue *queue;
queue = BLI_gsqueue_new(sizeof(BMVert *));
- visited_vertices[i] = true;
+ visited_verts[i] = true;
elem_id[i] = current_id;
BMVert *iv = BM_vert_at_index(bm, i);
BLI_gsqueue_push(queue, &iv);
@@ -372,8 +372,8 @@ static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id)
BM_ITER_ELEM (ed, &iter, current_v, BM_EDGES_OF_VERT) {
neighbor_v = BM_edge_other_vert(ed, current_v);
const int neighbor_index = BM_elem_index_get(neighbor_v);
- if (!visited_vertices[neighbor_index]) {
- visited_vertices[neighbor_index] = true;
+ if (!visited_verts[neighbor_index]) {
+ visited_verts[neighbor_index] = true;
elem_id[neighbor_index] = current_id;
BLI_gsqueue_push(queue, &neighbor_v);
}
@@ -383,7 +383,7 @@ static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id)
BLI_gsqueue_free(queue);
}
}
- MEM_freeN(visited_vertices);
+ MEM_freeN(visited_verts);
return current_id;
}
@@ -639,9 +639,10 @@ static void store_grid_data(MultiresUnsubdivideContext *context,
int grid_x,
int grid_y)
{
-
Mesh *original_mesh = context->original_mesh;
- MPoly *poly = &original_mesh->mpoly[BM_elem_index_get(f)];
+ const MPoly *polys = BKE_mesh_polys(original_mesh);
+ const MLoop *loops = BKE_mesh_loops(original_mesh);
+ const MPoly *poly = &polys[BM_elem_index_get(f)];
const int corner_vertex_index = BM_elem_index_get(v);
@@ -650,7 +651,7 @@ static void store_grid_data(MultiresUnsubdivideContext *context,
int loop_offset = 0;
for (int i = 0; i < poly->totloop; i++) {
const int loop_index = poly->loopstart + i;
- MLoop *l = &original_mesh->mloop[loop_index];
+ const MLoop *l = &loops[loop_index];
if (l->v == corner_vertex_index) {
loop_offset = i;
break;
@@ -901,10 +902,10 @@ static void multires_unsubdivide_add_original_index_datalayers(Mesh *mesh)
multires_unsubdivide_free_original_datalayers(mesh);
int *l_index = CustomData_add_layer_named(
- &mesh->ldata, CD_PROP_INT32, CD_CALLOC, NULL, mesh->totloop, lname);
+ &mesh->ldata, CD_PROP_INT32, CD_SET_DEFAULT, NULL, mesh->totloop, lname);
int *v_index = CustomData_add_layer_named(
- &mesh->vdata, CD_PROP_INT32, CD_CALLOC, NULL, mesh->totvert, vname);
+ &mesh->vdata, CD_PROP_INT32, CD_SET_DEFAULT, NULL, mesh->totvert, vname);
/* Initialize these data-layer with the indices in the current mesh. */
for (int i = 0; i < mesh->totloop; i++) {
@@ -918,8 +919,9 @@ static void multires_unsubdivide_add_original_index_datalayers(Mesh *mesh)
static void multires_unsubdivide_prepare_original_bmesh_for_extract(
MultiresUnsubdivideContext *context)
{
-
Mesh *original_mesh = context->original_mesh;
+ const MPoly *original_polys = BKE_mesh_polys(original_mesh);
+
Mesh *base_mesh = context->base_mesh;
BMesh *bm_original_mesh = context->bm_original_mesh = get_bmesh_from_mesh(original_mesh);
@@ -949,7 +951,7 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract(
context->loop_to_face_map = MEM_calloc_arrayN(original_mesh->totloop, sizeof(int), "loop map");
for (int i = 0; i < original_mesh->totpoly; i++) {
- MPoly *poly = &original_mesh->mpoly[i];
+ const MPoly *poly = &original_polys[i];
for (int l = 0; l < poly->totloop; l++) {
int original_loop_index = l + poly->loopstart;
context->loop_to_face_map[original_loop_index] = i;
@@ -963,16 +965,19 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract(
*/
static bool multires_unsubdivide_flip_grid_x_axis(Mesh *mesh, int poly, int loop, int v_x)
{
- MPoly *p = &mesh->mpoly[poly];
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
+
+ const MPoly *p = &polys[poly];
- MLoop *l_first = &mesh->mloop[p->loopstart];
+ const MLoop *l_first = &loops[p->loopstart];
if ((loop == (p->loopstart + (p->totloop - 1))) && l_first->v == v_x) {
return true;
}
int next_l_index = loop + 1;
if (next_l_index < p->loopstart + p->totloop) {
- MLoop *l_next = &mesh->mloop[next_l_index];
+ const MLoop *l_next = &loops[next_l_index];
if (l_next->v == v_x) {
return true;
}
@@ -1174,7 +1179,7 @@ static void multires_create_grids_in_unsubdivided_base_mesh(MultiresUnsubdivideC
CustomData_free_layers(&base_mesh->ldata, CD_MDISPS, base_mesh->totloop);
}
MDisps *mdisps = CustomData_add_layer(
- &base_mesh->ldata, CD_MDISPS, CD_CALLOC, NULL, base_mesh->totloop);
+ &base_mesh->ldata, CD_MDISPS, CD_SET_DEFAULT, NULL, base_mesh->totloop);
const int totdisp = pow_i(BKE_ccg_gridsize(context->num_total_levels), 2);
const int totloop = base_mesh->totloop;
@@ -1247,7 +1252,7 @@ int multiresModifier_rebuild_subdiv(struct Depsgraph *depsgraph,
}
/* Copy the new base mesh to the original mesh. */
- BKE_mesh_nomain_to_mesh(unsubdiv_context.base_mesh, object->data, object, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(unsubdiv_context.base_mesh, object->data, object);
Mesh *base_mesh = object->data;
multires_create_grids_in_unsubdivided_base_mesh(&unsubdiv_context, base_mesh);
diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c
index 9457c20eb7d..c0315dcc848 100644
--- a/source/blender/blenkernel/intern/nla.c
+++ b/source/blender/blenkernel/intern/nla.c
@@ -1241,7 +1241,7 @@ static NlaStrip *nlastrip_find_active(ListBase /* NlaStrip */ *strips)
float BKE_nlastrip_compute_frame_from_previous_strip(NlaStrip *strip)
{
- float limit_prev = MINFRAMEF;
+ float limit_prev = MINAFRAMEF;
/* Find the previous end frame, with a special case if the previous strip was a transition : */
if (strip->prev) {
@@ -2072,7 +2072,7 @@ bool BKE_nla_tweakmode_enter(AnimData *adt)
/* handle AnimData level changes:
* - 'real' active action to temp storage (no need to change user-counts).
- * - Action of active strip set to be the 'active action', and have its usercount incremented.
+ * - Action of active strip set to be the 'active action', and have its user-count incremented.
* - Editing-flag for this AnimData block should also get turned on
* (for more efficient restoring).
* - Take note of the active strip for mapping-correction of keyframes
@@ -2136,7 +2136,8 @@ void BKE_nla_tweakmode_exit(AnimData *adt)
}
/* handle AnimData level changes:
- * - 'temporary' active action needs its usercount decreased, since we're removing this reference
+ * - 'temporary' active action needs its user-count decreased,
+ * since we're removing this reference
* - 'real' active action is restored from storage
* - storage pointer gets cleared (to avoid having bad notes hanging around)
* - editing-flag for this AnimData block should also get turned off
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index c6f140b9260..3a241efdac2 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -47,6 +47,7 @@
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
+#include "BKE_asset.h"
#include "BKE_bpath.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
@@ -54,6 +55,7 @@
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_idprop.h"
+#include "BKE_idprop.hh"
#include "BKE_idtype.h"
#include "BKE_image_format.h"
#include "BKE_lib_id.h"
@@ -71,8 +73,8 @@
#include "NOD_composite.h"
#include "NOD_function.h"
#include "NOD_geometry.h"
+#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_node_declaration.hh"
-#include "NOD_node_tree_ref.hh"
#include "NOD_shader.h"
#include "NOD_socket.h"
#include "NOD_texture.h"
@@ -104,7 +106,6 @@ using blender::nodes::NodeDeclaration;
using blender::nodes::OutputFieldDependency;
using blender::nodes::OutputSocketFieldType;
using blender::nodes::SocketDeclaration;
-using namespace blender::nodes::node_tree_ref_types;
/* Fallback types for undefined tree, nodes, sockets */
static bNodeTreeType NodeTreeTypeUndefined;
@@ -120,9 +121,6 @@ static void node_free_node(bNodeTree *ntree, bNode *node);
static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
bNodeSocket *sock,
const bool do_id_user);
-static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree,
- struct bNode *node,
- const bool mute);
static void ntree_init_data(ID *id)
{
@@ -136,7 +134,7 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
bNodeTree *ntree_dst = (bNodeTree *)id_dst;
const bNodeTree *ntree_src = (const bNodeTree *)id_src;
- /* We never handle usercount here for own data. */
+ /* We never handle user-count here for own data. */
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
ntree_dst->runtime = MEM_new<bNodeTreeRuntime>(__func__);
@@ -330,6 +328,8 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data)
{
bNodeTree *ntree = (bNodeTree *)id;
+ BKE_LIB_FOREACHID_PROCESS_ID(data, ntree->owner_id, IDWALK_CB_LOOPBACK);
+
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ntree->gpd, IDWALK_CB_USER);
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -404,34 +404,19 @@ static void node_foreach_path(ID *id, BPathForeachPathData *bpath_data)
}
}
-static ID *node_owner_get(Main *bmain, ID *id)
+static ID **node_owner_pointer_get(ID *id)
{
if ((id->flag & LIB_EMBEDDED_DATA) == 0) {
- return id;
+ return NULL;
}
/* TODO: Sort this NO_MAIN or not for embedded node trees. See T86119. */
// BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0);
- ListBase *lists[] = {&bmain->materials,
- &bmain->lights,
- &bmain->worlds,
- &bmain->textures,
- &bmain->scenes,
- &bmain->linestyles,
- &bmain->simulations,
- nullptr};
-
- bNodeTree *ntree = (bNodeTree *)id;
- for (int i = 0; lists[i] != nullptr; i++) {
- LISTBASE_FOREACH (ID *, id_iter, lists[i]) {
- if (ntreeFromID(id_iter) == ntree) {
- return id_iter;
- }
- }
- }
+ bNodeTree *ntree = reinterpret_cast<bNodeTree *>(id);
+ BLI_assert(ntree->owner_id != NULL);
+ BLI_assert(ntreeFromID(ntree->owner_id) == ntree);
- BLI_assert_msg(0, "Embedded node tree with no owner. Critical Main inconsistency.");
- return nullptr;
+ return &ntree->owner_id;
}
static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock)
@@ -667,8 +652,35 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
sock->runtime = MEM_new<bNodeSocketRuntime>(__func__);
}
-void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
+void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
{
+ /* Special case for this pointer, do not rely on regular `lib_link` process here. Avoids needs
+ * for do_versioning, and ensures coherence of data in any case.
+ *
+ * NOTE: Old versions are very often 'broken' here, just fix it silently in these cases.
+ */
+ if (BLO_read_fileversion_get(reader) > 300) {
+ BLI_assert((ntree->id.flag & LIB_EMBEDDED_DATA) != 0 || owner_id == nullptr);
+ }
+ BLI_assert(owner_id == NULL || owner_id->lib == ntree->id.lib);
+ if (owner_id != nullptr && (ntree->id.flag & LIB_EMBEDDED_DATA) == 0) {
+ /* This is unfortunate, but currently a lot of existing files (including startup ones) have
+ * missing `LIB_EMBEDDED_DATA` flag.
+ *
+ * NOTE: Using do_version is not a solution here, since this code will be called before any
+ * do_version takes place. Keeping it here also ensures future (or unknown existing) similar
+ * bugs won't go easily unnoticed. */
+ if (BLO_read_fileversion_get(reader) > 300) {
+ CLOG_WARN(&LOG,
+ "Fixing root node tree '%s' owned by '%s' missing EMBEDDED tag, please consider "
+ "re-saving your (startup) file",
+ ntree->id.name,
+ owner_id->name);
+ }
+ ntree->id.flag |= LIB_EMBEDDED_DATA;
+ }
+ ntree->owner_id = owner_id;
+
/* NOTE: writing and reading goes in sync, for speed. */
ntree->is_updating = false;
ntree->typeinfo = nullptr;
@@ -830,12 +842,12 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
static void ntree_blend_read_data(BlendDataReader *reader, ID *id)
{
bNodeTree *ntree = (bNodeTree *)id;
- ntreeBlendReadData(reader, ntree);
+ ntreeBlendReadData(reader, nullptr, ntree);
}
static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSocket *sock)
{
- IDP_BlendReadLib(reader, sock->prop);
+ IDP_BlendReadLib(reader, lib, 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
@@ -901,7 +913,7 @@ void ntreeBlendReadLib(struct BlendLibReader *reader, struct bNodeTree *ntree)
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
/* Link ID Properties -- and copy this comment EXACTLY for easy finding
* of library blocks that implement this. */
- IDP_BlendReadLib(reader, node->prop);
+ IDP_BlendReadLib(reader, lib, node->prop);
BLO_read_id_address(reader, lib, &node->id);
@@ -912,9 +924,9 @@ void ntreeBlendReadLib(struct BlendLibReader *reader, struct bNodeTree *ntree)
lib_link_node_sockets(reader, lib, &ntree->inputs);
lib_link_node_sockets(reader, lib, &ntree->outputs);
- /* Set node->typeinfo pointers. This is done in lib linking, after the
+ /* Set `node->typeinfo` pointers. This is done in lib linking, after the
* first versioning that can change types still without functions that
- * update the typeinfo pointers. Versioning after lib linking needs
+ * update the `typeinfo` pointers. Versioning after lib linking needs
* these top be valid. */
ntreeSetTypes(nullptr, ntree);
@@ -1016,6 +1028,33 @@ static void ntree_blend_read_expand(BlendExpander *expander, ID *id)
ntreeBlendReadExpand(expander, ntree);
}
+namespace blender::bke {
+
+static void node_tree_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data)
+{
+ bNodeTree &node_tree = *static_cast<bNodeTree *>(asset_ptr);
+
+ BKE_asset_metadata_idprop_ensure(asset_data, idprop::create("type", node_tree.type).release());
+ auto inputs = idprop::create_group("inputs");
+ auto outputs = idprop::create_group("outputs");
+ LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.inputs) {
+ auto property = idprop::create(socket->name, socket->typeinfo->idname);
+ IDP_AddToGroup(inputs.get(), property.release());
+ }
+ LISTBASE_FOREACH (const bNodeSocket *, socket, &node_tree.outputs) {
+ auto property = idprop::create(socket->name, socket->typeinfo->idname);
+ IDP_AddToGroup(outputs.get(), property.release());
+ }
+ BKE_asset_metadata_idprop_ensure(asset_data, inputs.release());
+ BKE_asset_metadata_idprop_ensure(asset_data, outputs.release());
+}
+
+} // namespace blender::bke
+
+static AssetTypeInfo AssetType_NT = {
+ /* pre_save_fn */ blender::bke::node_tree_asset_pre_save,
+};
+
IDTypeInfo IDType_ID_NT = {
/* id_code */ ID_NT,
/* id_filter */ FILTER_ID_NT,
@@ -1025,7 +1064,7 @@ IDTypeInfo IDType_ID_NT = {
/* name_plural */ "node_groups",
/* translation_context */ BLT_I18NCONTEXT_ID_NODETREE,
/* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE,
- /* asset_type_info */ nullptr,
+ /* asset_type_info */ &AssetType_NT,
/* init_data */ ntree_init_data,
/* copy_data */ ntree_copy_data,
@@ -1034,7 +1073,7 @@ IDTypeInfo IDType_ID_NT = {
/* foreach_id */ node_foreach_id,
/* foreach_cache */ node_foreach_cache,
/* foreach_path */ node_foreach_path,
- /* owner_get */ node_owner_get,
+ /* owner_pointer_get */ node_owner_pointer_get,
/* blend_write */ ntree_blend_write,
/* blend_read_data */ ntree_blend_read_data,
@@ -1071,7 +1110,7 @@ static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType
}
/* NOTE: This function is called to initialize node data based on the type.
- * The bNodeType may not be registered at creation time of the node,
+ * The #bNodeType may not be registered at creation time of the node,
* so this can be delayed until the node type gets registered.
*/
static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node)
@@ -1922,6 +1961,9 @@ static void node_socket_free(bNodeSocket *sock, const bool do_id_user)
}
MEM_freeN(sock->default_value);
}
+ if (sock->default_attribute_name) {
+ MEM_freeN(sock->default_attribute_name);
+ }
MEM_delete(sock->runtime);
}
@@ -2347,102 +2389,11 @@ void nodeRemLink(bNodeTree *ntree, bNodeLink *link)
}
}
-/* Check if all output links are muted or not. */
-static bool nodeMuteFromSocketLinks(const bNodeTree *ntree, const bNodeSocket *sock)
-{
- int tot = 0;
- int muted = 0;
- LISTBASE_FOREACH (const bNodeLink *, link, &ntree->links) {
- if (link->fromsock == sock) {
- tot++;
- if (link->flag & NODE_LINK_MUTED) {
- muted++;
- }
- }
- }
- return tot == muted;
-}
-
-static void nodeMuteLink(bNodeLink *link)
-{
- link->flag |= NODE_LINK_MUTED;
- link->flag |= NODE_LINK_TEST;
- if (!(link->tosock->flag & SOCK_MULTI_INPUT)) {
- link->tosock->flag &= ~SOCK_IN_USE;
- }
-}
-
-static void nodeUnMuteLink(bNodeLink *link)
-{
- link->flag &= ~NODE_LINK_MUTED;
- link->flag |= NODE_LINK_TEST;
- link->tosock->flag |= SOCK_IN_USE;
-}
-
-/* Upstream muting. Always happens when unmuting but checks when muting. O(n^2) algorithm. */
-static void nodeMuteRerouteInputLinks(bNodeTree *ntree, bNode *node, const bool mute)
+void nodeLinkSetMute(bNodeTree *ntree, bNodeLink *link, const bool muted)
{
- if (node->type != NODE_REROUTE) {
- return;
- }
- if (!mute || nodeMuteFromSocketLinks(ntree, (bNodeSocket *)node->outputs.first)) {
- bNodeSocket *sock = (bNodeSocket *)node->inputs.first;
- LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
- if (!(link->flag & NODE_LINK_VALID) || (link->tosock != sock)) {
- continue;
- }
- if (mute) {
- nodeMuteLink(link);
- }
- else {
- nodeUnMuteLink(link);
- }
- nodeMuteRerouteInputLinks(ntree, link->fromnode, mute);
- }
- }
-}
-
-/* Downstream muting propagates when reaching reroute nodes. O(n^2) algorithm. */
-static void nodeMuteRerouteOutputLinks(bNodeTree *ntree, bNode *node, const bool mute)
-{
- if (node->type != NODE_REROUTE) {
- return;
- }
- bNodeSocket *sock;
- sock = (bNodeSocket *)node->outputs.first;
- LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
- if (!(link->flag & NODE_LINK_VALID) || (link->fromsock != sock)) {
- continue;
- }
- if (mute) {
- nodeMuteLink(link);
- }
- else {
- nodeUnMuteLink(link);
- }
- nodeMuteRerouteOutputLinks(ntree, link->tonode, mute);
- }
-}
-
-void nodeMuteLinkToggle(bNodeTree *ntree, bNodeLink *link)
-{
- if (link->tosock) {
- bool mute = !(link->flag & NODE_LINK_MUTED);
- if (mute) {
- nodeMuteLink(link);
- }
- else {
- nodeUnMuteLink(link);
- }
- if (link->tonode->type == NODE_REROUTE) {
- nodeMuteRerouteOutputLinks(ntree, link->tonode, mute);
- }
- if (link->fromnode->type == NODE_REROUTE) {
- nodeMuteRerouteInputLinks(ntree, link->fromnode, mute);
- }
- }
-
- if (ntree) {
+ const bool was_muted = link->flag & NODE_LINK_MUTED;
+ SET_FLAG_FROM_TEST(link->flag, muted, NODE_LINK_MUTED);
+ if (muted != was_muted) {
BKE_ntree_update_tag_link_mute(ntree, link);
}
}
@@ -2657,29 +2608,37 @@ void nodePositionRelative(bNode *from_node,
void nodePositionPropagate(bNode *node)
{
- LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) {
- if (nsock->link != nullptr) {
- bNodeLink *link = nsock->link;
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->link != nullptr) {
+ bNodeLink *link = socket->link;
nodePositionRelative(link->fromnode, link->tonode, link->fromsock, link->tosock);
nodePositionPropagate(link->fromnode);
}
}
}
-bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname)
+static bNodeTree *ntreeAddTree_do(
+ Main *bmain, ID *owner_id, const bool is_embedded, const char *name, const char *idname)
{
/* trees are created as local trees for compositor, material or texture nodes,
* node groups and other tree types are created as library data.
*/
- const bool is_embedded = (bmain == nullptr);
int flag = 0;
- if (is_embedded) {
+ if (is_embedded || bmain == nullptr) {
flag |= LIB_ID_CREATE_NO_MAIN;
}
bNodeTree *ntree = (bNodeTree *)BKE_libblock_alloc(bmain, ID_NT, name, flag);
BKE_libblock_init_empty(&ntree->id);
if (is_embedded) {
+ BLI_assert(owner_id != NULL);
ntree->id.flag |= LIB_EMBEDDED_DATA;
+ ntree->owner_id = owner_id;
+ bNodeTree **ntree_owner_ptr = BKE_ntree_ptr_from_id(owner_id);
+ BLI_assert(ntree_owner_ptr != NULL);
+ *ntree_owner_ptr = ntree;
+ }
+ else {
+ BLI_assert(owner_id == NULL);
}
BLI_strncpy(ntree->idname, idname, sizeof(ntree->idname));
@@ -2688,6 +2647,19 @@ bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname)
return ntree;
}
+bNodeTree *ntreeAddTree(Main *bmain, const char *name, const char *idname)
+{
+ return ntreeAddTree_do(bmain, nullptr, false, name, idname);
+}
+
+bNodeTree *ntreeAddTreeEmbedded(Main *UNUSED(bmain),
+ ID *owner_id,
+ const char *name,
+ const char *idname)
+{
+ return ntreeAddTree_do(nullptr, owner_id, true, name, idname);
+}
+
bNodeTree *ntreeCopyTree_ex(const bNodeTree *ntree, Main *bmain, const bool do_id_user)
{
const int flag = do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_MAIN;
@@ -3047,7 +3019,9 @@ void nodeRemoveNode(Main *bmain, bNodeTree *ntree, bNode *node, bool do_id_user)
}
}
- if (node_has_id) {
+ /* Also update relations for the scene time node, which causes a dependency
+ * on time that users expect to be removed when the node is removed. */
+ if (node_has_id || node->type == GEO_NODE_INPUT_SCENE_TIME) {
if (bmain != nullptr) {
DEG_relations_tag_update(bmain);
}
@@ -3074,6 +3048,9 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
}
MEM_freeN(sock->default_value);
}
+ if (sock->default_attribute_name) {
+ MEM_freeN(sock->default_attribute_name);
+ }
MEM_delete(sock->runtime);
}
@@ -3376,8 +3353,10 @@ struct bNodeSocket *ntreeAddSocketInterfaceFromSocket(bNodeTree *ntree,
bNode *from_node,
bNodeSocket *from_sock)
{
- bNodeSocket *iosock = ntreeAddSocketInterface(
- ntree, static_cast<eNodeSocketInOut>(from_sock->in_out), from_sock->idname, from_sock->name);
+ bNodeSocket *iosock = ntreeAddSocketInterface(ntree,
+ static_cast<eNodeSocketInOut>(from_sock->in_out),
+ from_sock->idname,
+ DATA_(from_sock->name));
if (iosock) {
if (iosock->typeinfo->interface_from_socket) {
iosock->typeinfo->interface_from_socket(ntree, iosock, from_node, from_sock);
@@ -4574,6 +4553,7 @@ static void registerShaderNodes()
register_node_type_sh_wavelength();
register_node_type_sh_blackbody();
register_node_type_sh_mix_rgb();
+ register_node_type_sh_mix();
register_node_type_sh_valtorgb();
register_node_type_sh_rgbtobw();
register_node_type_sh_shadertorgb();
@@ -4751,17 +4731,18 @@ static void registerGeometryNodes()
register_node_type_geo_curve_trim();
register_node_type_geo_deform_curves_on_surface();
register_node_type_geo_delete_geometry();
- register_node_type_geo_duplicate_elements();
+ register_node_type_geo_distribute_points_in_volume();
register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_dual_mesh();
+ register_node_type_geo_duplicate_elements();
+ register_node_type_geo_edge_paths_to_curves();
+ register_node_type_geo_edge_paths_to_selection();
register_node_type_geo_edge_split();
register_node_type_geo_extrude_mesh();
register_node_type_geo_field_at_index();
- register_node_type_geo_field_on_domain();
register_node_type_geo_flip_faces();
register_node_type_geo_geometry_to_instance();
register_node_type_geo_image_texture();
- register_node_type_geo_input_named_attribute();
register_node_type_geo_input_curve_handles();
register_node_type_geo_input_curve_tilt();
register_node_type_geo_input_id();
@@ -4778,22 +4759,26 @@ static void registerGeometryNodes()
register_node_type_geo_input_mesh_face_neighbors();
register_node_type_geo_input_mesh_island();
register_node_type_geo_input_mesh_vertex_neighbors();
+ register_node_type_geo_input_named_attribute();
register_node_type_geo_input_normal();
register_node_type_geo_input_position();
register_node_type_geo_input_radius();
register_node_type_geo_input_scene_time();
register_node_type_geo_input_shade_smooth();
+ register_node_type_geo_input_shortest_edge_paths();
register_node_type_geo_input_spline_cyclic();
register_node_type_geo_input_spline_length();
register_node_type_geo_input_spline_resolution();
register_node_type_geo_input_tangent();
register_node_type_geo_instance_on_points();
register_node_type_geo_instances_to_points();
+ register_node_type_geo_interpolate_domain();
register_node_type_geo_is_viewport();
register_node_type_geo_join_geometry();
register_node_type_geo_material_replace();
register_node_type_geo_material_selection();
register_node_type_geo_merge_by_distance();
+ register_node_type_geo_mesh_face_set_boundaries();
register_node_type_geo_mesh_primitive_circle();
register_node_type_geo_mesh_primitive_cone();
register_node_type_geo_mesh_primitive_cube();
@@ -4807,9 +4792,9 @@ static void registerGeometryNodes()
register_node_type_geo_mesh_to_points();
register_node_type_geo_mesh_to_volume();
register_node_type_geo_object_info();
- register_node_type_geo_points();
register_node_type_geo_points_to_vertices();
register_node_type_geo_points_to_volume();
+ register_node_type_geo_points();
register_node_type_geo_proximity();
register_node_type_geo_raycast();
register_node_type_geo_realize_instances();
@@ -4839,11 +4824,11 @@ static void registerGeometryNodes()
register_node_type_geo_transform();
register_node_type_geo_translate_instances();
register_node_type_geo_triangulate();
+ register_node_type_geo_uv_pack_islands();
+ register_node_type_geo_uv_unwrap();
register_node_type_geo_viewer();
register_node_type_geo_volume_cube();
register_node_type_geo_volume_to_mesh();
- register_node_type_geo_uv_pack_islands();
- register_node_type_geo_uv_unwrap();
}
static void registerFunctionNodes()
diff --git a/source/blender/blenkernel/intern/node_runtime.cc b/source/blender/blenkernel/intern/node_runtime.cc
new file mode 100644
index 00000000000..4fb0e423a33
--- /dev/null
+++ b/source/blender/blenkernel/intern/node_runtime.cc
@@ -0,0 +1,432 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
+
+#include "DNA_node_types.h"
+
+#include "BLI_function_ref.hh"
+#include "BLI_stack.hh"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "NOD_geometry_nodes_lazy_function.hh"
+
+namespace blender::bke::node_tree_runtime {
+
+void preprocess_geometry_node_tree_for_evaluation(bNodeTree &tree_cow)
+{
+ BLI_assert(tree_cow.type == NTREE_GEOMETRY);
+ /* Rebuild geometry nodes lazy function graph. */
+ tree_cow.runtime->geometry_nodes_lazy_function_graph_info.reset();
+ blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree_cow);
+}
+
+static void double_checked_lock(std::mutex &mutex, bool &data_is_dirty, FunctionRef<void()> fn)
+{
+ if (!data_is_dirty) {
+ return;
+ }
+ std::lock_guard lock{mutex};
+ if (!data_is_dirty) {
+ return;
+ }
+ fn();
+ data_is_dirty = false;
+}
+
+static void double_checked_lock_with_task_isolation(std::mutex &mutex,
+ bool &data_is_dirty,
+ FunctionRef<void()> fn)
+{
+ double_checked_lock(mutex, data_is_dirty, [&]() { threading::isolate_task(fn); });
+}
+
+static void update_node_vector(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ tree_runtime.nodes.clear();
+ tree_runtime.group_nodes.clear();
+ tree_runtime.has_undefined_nodes_or_sockets = false;
+ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
+ node->runtime->index_in_tree = tree_runtime.nodes.append_and_get_index(node);
+ node->runtime->owner_tree = const_cast<bNodeTree *>(&ntree);
+ tree_runtime.has_undefined_nodes_or_sockets |= node->typeinfo == &NodeTypeUndefined;
+ if (node->is_group()) {
+ tree_runtime.group_nodes.append(node);
+ }
+ }
+}
+
+static void update_link_vector(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ tree_runtime.links.clear();
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
+ tree_runtime.links.append(link);
+ }
+}
+
+static void update_internal_links(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ for (bNode *node : tree_runtime.nodes) {
+ node->runtime->internal_links.clear();
+ for (bNodeSocket *socket : node->runtime->outputs) {
+ socket->runtime->internal_link_input = nullptr;
+ }
+ LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) {
+ node->runtime->internal_links.append(link);
+ link->tosock->runtime->internal_link_input = link->fromsock;
+ }
+ }
+}
+
+static void update_socket_vectors_and_owner_node(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ tree_runtime.sockets.clear();
+ tree_runtime.input_sockets.clear();
+ tree_runtime.output_sockets.clear();
+ for (bNode *node : tree_runtime.nodes) {
+ bNodeRuntime &node_runtime = *node->runtime;
+ node_runtime.inputs.clear();
+ node_runtime.outputs.clear();
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ socket->runtime->index_in_node = node_runtime.inputs.append_and_get_index(socket);
+ socket->runtime->index_in_all_sockets = tree_runtime.sockets.append_and_get_index(socket);
+ socket->runtime->index_in_inout_sockets = tree_runtime.input_sockets.append_and_get_index(
+ socket);
+ socket->runtime->owner_node = node;
+ tree_runtime.has_undefined_nodes_or_sockets |= socket->typeinfo == &NodeSocketTypeUndefined;
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ socket->runtime->index_in_node = node_runtime.outputs.append_and_get_index(socket);
+ socket->runtime->index_in_all_sockets = tree_runtime.sockets.append_and_get_index(socket);
+ socket->runtime->index_in_inout_sockets = tree_runtime.output_sockets.append_and_get_index(
+ socket);
+ socket->runtime->owner_node = node;
+ tree_runtime.has_undefined_nodes_or_sockets |= socket->typeinfo == &NodeSocketTypeUndefined;
+ }
+ }
+}
+
+static void update_directly_linked_links_and_sockets(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ for (bNode *node : tree_runtime.nodes) {
+ for (bNodeSocket *socket : node->runtime->inputs) {
+ socket->runtime->directly_linked_links.clear();
+ socket->runtime->directly_linked_sockets.clear();
+ }
+ for (bNodeSocket *socket : node->runtime->outputs) {
+ socket->runtime->directly_linked_links.clear();
+ socket->runtime->directly_linked_sockets.clear();
+ }
+ node->runtime->has_available_linked_inputs = false;
+ node->runtime->has_available_linked_outputs = false;
+ }
+ for (bNodeLink *link : tree_runtime.links) {
+ link->fromsock->runtime->directly_linked_links.append(link);
+ link->fromsock->runtime->directly_linked_sockets.append(link->tosock);
+ link->tosock->runtime->directly_linked_links.append(link);
+ if (link->is_available()) {
+ link->fromnode->runtime->has_available_linked_outputs = true;
+ link->tonode->runtime->has_available_linked_inputs = true;
+ }
+ }
+ for (bNodeSocket *socket : tree_runtime.input_sockets) {
+ if (socket->flag & SOCK_MULTI_INPUT) {
+ std::sort(socket->runtime->directly_linked_links.begin(),
+ socket->runtime->directly_linked_links.end(),
+ [&](const bNodeLink *a, const bNodeLink *b) {
+ return a->multi_input_socket_index > b->multi_input_socket_index;
+ });
+ }
+ }
+ for (bNodeSocket *socket : tree_runtime.input_sockets) {
+ for (bNodeLink *link : socket->runtime->directly_linked_links) {
+ /* Do this after sorting the input links. */
+ socket->runtime->directly_linked_sockets.append(link->fromsock);
+ }
+ }
+}
+
+static void find_logical_origins_for_socket_recursive(
+ bNodeSocket &input_socket,
+ bool only_follow_first_input_link,
+ Vector<bNodeSocket *, 16> &sockets_in_current_chain,
+ Vector<bNodeSocket *> &r_logical_origins,
+ Vector<bNodeSocket *> &r_skipped_origins)
+{
+ if (sockets_in_current_chain.contains(&input_socket)) {
+ /* Protect against reroute recursions. */
+ return;
+ }
+ sockets_in_current_chain.append(&input_socket);
+
+ Span<bNodeLink *> links_to_check = input_socket.runtime->directly_linked_links;
+ if (only_follow_first_input_link) {
+ links_to_check = links_to_check.take_front(1);
+ }
+ for (bNodeLink *link : links_to_check) {
+ if (link->is_muted()) {
+ continue;
+ }
+ if (!link->is_available()) {
+ continue;
+ }
+ bNodeSocket &origin_socket = *link->fromsock;
+ bNode &origin_node = *link->fromnode;
+ if (!origin_socket.is_available()) {
+ /* Non available sockets are ignored. */
+ continue;
+ }
+ if (origin_node.type == NODE_REROUTE) {
+ bNodeSocket &reroute_input = *origin_node.runtime->inputs[0];
+ bNodeSocket &reroute_output = *origin_node.runtime->outputs[0];
+ r_skipped_origins.append(&reroute_input);
+ r_skipped_origins.append(&reroute_output);
+ find_logical_origins_for_socket_recursive(
+ reroute_input, false, sockets_in_current_chain, r_logical_origins, r_skipped_origins);
+ continue;
+ }
+ if (origin_node.is_muted()) {
+ if (bNodeSocket *mute_input = origin_socket.runtime->internal_link_input) {
+ r_skipped_origins.append(&origin_socket);
+ r_skipped_origins.append(mute_input);
+ find_logical_origins_for_socket_recursive(
+ *mute_input, true, sockets_in_current_chain, r_logical_origins, r_skipped_origins);
+ }
+ continue;
+ }
+ r_logical_origins.append(&origin_socket);
+ }
+
+ sockets_in_current_chain.pop_last();
+}
+
+static void update_logical_origins(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ threading::parallel_for(tree_runtime.nodes.index_range(), 128, [&](const IndexRange range) {
+ for (const int i : range) {
+ bNode &node = *tree_runtime.nodes[i];
+ for (bNodeSocket *socket : node.runtime->inputs) {
+ Vector<bNodeSocket *, 16> sockets_in_current_chain;
+ socket->runtime->logically_linked_sockets.clear();
+ socket->runtime->logically_linked_skipped_sockets.clear();
+ find_logical_origins_for_socket_recursive(
+ *socket,
+ false,
+ sockets_in_current_chain,
+ socket->runtime->logically_linked_sockets,
+ socket->runtime->logically_linked_skipped_sockets);
+ }
+ }
+ });
+}
+
+static void update_nodes_by_type(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ tree_runtime.nodes_by_type.clear();
+ for (bNode *node : tree_runtime.nodes) {
+ tree_runtime.nodes_by_type.add(node->typeinfo, node);
+ }
+}
+
+static void update_sockets_by_identifier(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ threading::parallel_for(tree_runtime.nodes.index_range(), 128, [&](const IndexRange range) {
+ for (bNode *node : tree_runtime.nodes.as_span().slice(range)) {
+ node->runtime->inputs_by_identifier.clear();
+ node->runtime->outputs_by_identifier.clear();
+ for (bNodeSocket *socket : node->runtime->inputs) {
+ node->runtime->inputs_by_identifier.add_new(socket->identifier, socket);
+ }
+ for (bNodeSocket *socket : node->runtime->outputs) {
+ node->runtime->outputs_by_identifier.add_new(socket->identifier, socket);
+ }
+ }
+ });
+}
+
+enum class ToposortDirection {
+ LeftToRight,
+ RightToLeft,
+};
+
+struct ToposortNodeState {
+ bool is_done = false;
+ bool is_in_stack = false;
+};
+
+static void toposort_from_start_node(const ToposortDirection direction,
+ bNode &start_node,
+ MutableSpan<ToposortNodeState> node_states,
+ Vector<bNode *> &r_sorted_nodes,
+ bool &r_cycle_detected)
+{
+ struct Item {
+ bNode *node;
+ int socket_index = 0;
+ int link_index = 0;
+ };
+
+ Stack<Item, 64> nodes_to_check;
+ nodes_to_check.push({&start_node});
+ node_states[start_node.runtime->index_in_tree].is_in_stack = true;
+ while (!nodes_to_check.is_empty()) {
+ Item &item = nodes_to_check.peek();
+ bNode &node = *item.node;
+ const Span<bNodeSocket *> sockets = (direction == ToposortDirection::LeftToRight) ?
+ node.runtime->inputs :
+ node.runtime->outputs;
+ while (true) {
+ if (item.socket_index == sockets.size()) {
+ /* All sockets have already been visited. */
+ break;
+ }
+ bNodeSocket &socket = *sockets[item.socket_index];
+ const Span<bNodeLink *> linked_links = socket.runtime->directly_linked_links;
+ if (item.link_index == linked_links.size()) {
+ /* All links connected to this socket have already been visited. */
+ item.socket_index++;
+ item.link_index = 0;
+ continue;
+ }
+ bNodeLink &link = *linked_links[item.link_index];
+ if (!link.is_available()) {
+ /* Ignore unavailable links. */
+ item.link_index++;
+ continue;
+ }
+ bNodeSocket &linked_socket = *socket.runtime->directly_linked_sockets[item.link_index];
+ bNode &linked_node = *linked_socket.runtime->owner_node;
+ ToposortNodeState &linked_node_state = node_states[linked_node.runtime->index_in_tree];
+ if (linked_node_state.is_done) {
+ /* The linked node has already been visited. */
+ item.link_index++;
+ continue;
+ }
+ if (linked_node_state.is_in_stack) {
+ r_cycle_detected = true;
+ }
+ else {
+ nodes_to_check.push({&linked_node});
+ linked_node_state.is_in_stack = true;
+ }
+ break;
+ }
+
+ /* If no other element has been pushed, the current node can be pushed to the sorted list. */
+ if (&item == &nodes_to_check.peek()) {
+ ToposortNodeState &node_state = node_states[node.runtime->index_in_tree];
+ node_state.is_done = true;
+ node_state.is_in_stack = false;
+ r_sorted_nodes.append(&node);
+ nodes_to_check.pop();
+ }
+ }
+}
+
+static void update_toposort(const bNodeTree &ntree,
+ const ToposortDirection direction,
+ Vector<bNode *> &r_sorted_nodes,
+ bool &r_cycle_detected)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ r_sorted_nodes.clear();
+ r_sorted_nodes.reserve(tree_runtime.nodes.size());
+ r_cycle_detected = false;
+
+ Array<ToposortNodeState> node_states(tree_runtime.nodes.size());
+ for (bNode *node : tree_runtime.nodes) {
+ if (node_states[node->runtime->index_in_tree].is_done) {
+ /* Ignore nodes that are done already. */
+ continue;
+ }
+ if ((direction == ToposortDirection::LeftToRight) ?
+ node->runtime->has_available_linked_outputs :
+ node->runtime->has_available_linked_inputs) {
+ /* Ignore non-start nodes. */
+ continue;
+ }
+ toposort_from_start_node(direction, *node, node_states, r_sorted_nodes, r_cycle_detected);
+ }
+
+ if (r_sorted_nodes.size() < tree_runtime.nodes.size()) {
+ r_cycle_detected = true;
+ for (bNode *node : tree_runtime.nodes) {
+ if (node_states[node->runtime->index_in_tree].is_done) {
+ /* Ignore nodes that are done already. */
+ continue;
+ }
+ /* Start toposort at this node which is somewhere in the middle of a loop. */
+ toposort_from_start_node(direction, *node, node_states, r_sorted_nodes, r_cycle_detected);
+ }
+ }
+
+ BLI_assert(tree_runtime.nodes.size() == r_sorted_nodes.size());
+}
+
+static void update_group_output_node(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ const bNodeType *node_type = nodeTypeFind("NodeGroupOutput");
+ const Span<bNode *> group_output_nodes = tree_runtime.nodes_by_type.lookup(node_type);
+ if (group_output_nodes.is_empty()) {
+ tree_runtime.group_output_node = nullptr;
+ }
+ else if (group_output_nodes.size() == 1) {
+ tree_runtime.group_output_node = group_output_nodes[0];
+ }
+ else {
+ for (bNode *group_output : group_output_nodes) {
+ if (group_output->flag & NODE_DO_OUTPUT) {
+ tree_runtime.group_output_node = group_output;
+ break;
+ }
+ }
+ }
+}
+
+static void ensure_topology_cache(const bNodeTree &ntree)
+{
+ bNodeTreeRuntime &tree_runtime = *ntree.runtime;
+ double_checked_lock_with_task_isolation(
+ tree_runtime.topology_cache_mutex, tree_runtime.topology_cache_is_dirty, [&]() {
+ update_node_vector(ntree);
+ update_link_vector(ntree);
+ update_socket_vectors_and_owner_node(ntree);
+ update_internal_links(ntree);
+ update_directly_linked_links_and_sockets(ntree);
+ threading::parallel_invoke([&]() { update_logical_origins(ntree); },
+ [&]() { update_nodes_by_type(ntree); },
+ [&]() { update_sockets_by_identifier(ntree); },
+ [&]() {
+ update_toposort(ntree,
+ ToposortDirection::LeftToRight,
+ tree_runtime.toposort_left_to_right,
+ tree_runtime.has_available_link_cycle);
+ },
+ [&]() {
+ bool dummy;
+ update_toposort(ntree,
+ ToposortDirection::RightToLeft,
+ tree_runtime.toposort_right_to_left,
+ dummy);
+ });
+ update_group_output_node(ntree);
+ tree_runtime.topology_cache_exists = true;
+ });
+}
+
+} // namespace blender::bke::node_tree_runtime
+
+void bNodeTree::ensure_topology_cache() const
+{
+ blender::bke::node_tree_runtime::ensure_topology_cache(*this);
+}
diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc
index 019ab114b83..f9bab0959c9 100644
--- a/source/blender/blenkernel/intern/node_tree_update.cc
+++ b/source/blender/blenkernel/intern/node_tree_update.cc
@@ -5,6 +5,7 @@
#include "BLI_noise.hh"
#include "BLI_set.hh"
#include "BLI_stack.hh"
+#include "BLI_timeit.hh"
#include "BLI_vector_set.hh"
#include "DNA_anim_types.h"
@@ -21,7 +22,6 @@
#include "MOD_nodes.h"
#include "NOD_node_declaration.hh"
-#include "NOD_node_tree_ref.hh"
#include "NOD_texture.h"
#include "DEG_depsgraph_query.h"
@@ -50,6 +50,7 @@ enum eNodeTreeChangedFlag {
static void add_tree_tag(bNodeTree *ntree, const eNodeTreeChangedFlag flag)
{
ntree->runtime->changed_flag |= flag;
+ ntree->runtime->topology_cache_is_dirty = true;
}
static void add_node_tag(bNodeTree *ntree, bNode *node, const eNodeTreeChangedFlag flag)
@@ -73,28 +74,32 @@ static bool is_field_socket_type(eNodeSocketDatatype type)
return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
}
-static bool is_field_socket_type(const SocketRef &socket)
+static bool is_field_socket_type(const bNodeSocket &socket)
{
- return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type);
+ return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo->type);
}
-static InputSocketFieldType get_interface_input_field_type(const NodeRef &node,
- const InputSocketRef &socket)
+static InputSocketFieldType get_interface_input_field_type(const bNode &node,
+ const bNodeSocket &socket)
{
if (!is_field_socket_type(socket)) {
return InputSocketFieldType::None;
}
- if (node.is_reroute_node()) {
+ if (node.type == NODE_REROUTE) {
return InputSocketFieldType::IsSupported;
}
- if (node.is_group_output_node()) {
+ if (node.type == NODE_GROUP_OUTPUT) {
/* Outputs always support fields when the data type is correct. */
return InputSocketFieldType::IsSupported;
}
- if (node.is_undefined()) {
+ if (node.typeinfo == &NodeTypeUndefined) {
+ return InputSocketFieldType::None;
+ }
+ if (node.type == NODE_CUSTOM) {
return InputSocketFieldType::None;
}
+ /* TODO: Ensure declaration exists. */
const NodeDeclaration *node_decl = node.declaration();
/* Node declarations should be implemented for nodes involved here. */
@@ -113,22 +118,25 @@ static InputSocketFieldType get_interface_input_field_type(const NodeRef &node,
return field_type;
}
-static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node,
- const OutputSocketRef &socket)
+static OutputFieldDependency get_interface_output_field_dependency(const bNode &node,
+ const bNodeSocket &socket)
{
if (!is_field_socket_type(socket)) {
/* Non-field sockets always output data. */
return OutputFieldDependency::ForDataSource();
}
- if (node.is_reroute_node()) {
+ if (node.type == NODE_REROUTE) {
/* The reroute just forwards what is passed in. */
return OutputFieldDependency::ForDependentField();
}
- if (node.is_group_input_node()) {
+ if (node.type == NODE_GROUP_INPUT) {
/* Input nodes get special treatment in #determine_group_input_states. */
return OutputFieldDependency::ForDependentField();
}
- if (node.is_undefined()) {
+ if (node.typeinfo == &NodeTypeUndefined) {
+ return OutputFieldDependency::ForDataSource();
+ }
+ if (node.type == NODE_CUSTOM) {
return OutputFieldDependency::ForDataSource();
}
@@ -147,12 +155,13 @@ static OutputFieldDependency get_interface_output_field_dependency(const NodeRef
return socket_decl.output_field_dependency();
}
-static FieldInferencingInterface get_dummy_field_inferencing_interface(const NodeRef &node)
+static FieldInferencingInterface get_dummy_field_inferencing_interface(const bNode &node)
{
FieldInferencingInterface inferencing_interface;
- inferencing_interface.inputs.append_n_times(InputSocketFieldType::None, node.inputs().size());
+ inferencing_interface.inputs.append_n_times(InputSocketFieldType::None,
+ node.input_sockets().size());
inferencing_interface.outputs.append_n_times(OutputFieldDependency::ForDataSource(),
- node.outputs().size());
+ node.output_sockets().size());
return inferencing_interface;
}
@@ -161,11 +170,11 @@ static FieldInferencingInterface get_dummy_field_inferencing_interface(const Nod
* In the future, this information can be stored in the node declaration. This would allow this
* function to return a reference, making it more efficient.
*/
-static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node)
+static FieldInferencingInterface get_node_field_inferencing_interface(const bNode &node)
{
/* Node groups already reference all required information, so just return that. */
- if (node.is_group_node()) {
- bNodeTree *group = (bNodeTree *)node.bnode()->id;
+ if (node.is_group()) {
+ bNodeTree *group = (bNodeTree *)node.id;
if (group == nullptr) {
return FieldInferencingInterface();
}
@@ -181,11 +190,11 @@ static FieldInferencingInterface get_node_field_inferencing_interface(const Node
}
FieldInferencingInterface inferencing_interface;
- for (const InputSocketRef *input_socket : node.inputs()) {
+ for (const bNodeSocket *input_socket : node.input_sockets()) {
inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket));
}
- for (const OutputSocketRef *output_socket : node.outputs()) {
+ for (const bNodeSocket *output_socket : node.output_sockets()) {
inferencing_interface.outputs.append(
get_interface_output_field_dependency(node, *output_socket));
}
@@ -209,11 +218,11 @@ struct SocketFieldState {
bool requires_single = false;
};
-static Vector<const InputSocketRef *> gather_input_socket_dependencies(
- const OutputFieldDependency &field_dependency, const NodeRef &node)
+static Vector<const bNodeSocket *> gather_input_socket_dependencies(
+ const OutputFieldDependency &field_dependency, const bNode &node)
{
const OutputSocketFieldType type = field_dependency.field_type();
- Vector<const InputSocketRef *> input_sockets;
+ Vector<const bNodeSocket *> input_sockets;
switch (type) {
case OutputSocketFieldType::FieldSource:
case OutputSocketFieldType::None: {
@@ -221,13 +230,13 @@ static Vector<const InputSocketRef *> gather_input_socket_dependencies(
}
case OutputSocketFieldType::DependentField: {
/* This output depends on all inputs. */
- input_sockets.extend(node.inputs());
+ input_sockets.extend(node.input_sockets());
break;
}
case OutputSocketFieldType::PartiallyDependent: {
/* This output depends only on a few inputs. */
for (const int i : field_dependency.linked_input_indices()) {
- input_sockets.append(&node.input(i));
+ input_sockets.append(&node.input_socket(i));
}
break;
}
@@ -240,8 +249,7 @@ static Vector<const InputSocketRef *> gather_input_socket_dependencies(
* to figure out if it is always a field or if it depends on any group inputs.
*/
static OutputFieldDependency find_group_output_dependencies(
- const InputSocketRef &group_output_socket,
- const Span<SocketFieldState> field_state_by_socket_id)
+ const bNodeSocket &group_output_socket, const Span<SocketFieldState> field_state_by_socket_id)
{
if (!is_field_socket_type(group_output_socket)) {
return OutputFieldDependency::ForDataSource();
@@ -249,8 +257,8 @@ static OutputFieldDependency find_group_output_dependencies(
/* Use a Set here instead of an array indexed by socket id, because we my only need to look at
* very few sockets. */
- Set<const InputSocketRef *> handled_sockets;
- Stack<const InputSocketRef *> sockets_to_check;
+ Set<const bNodeSocket *> handled_sockets;
+ Stack<const bNodeSocket *> sockets_to_check;
handled_sockets.add(&group_output_socket);
sockets_to_check.push(&group_output_socket);
@@ -259,20 +267,21 @@ static OutputFieldDependency find_group_output_dependencies(
Vector<int> linked_input_indices;
while (!sockets_to_check.is_empty()) {
- const InputSocketRef *input_socket = sockets_to_check.pop();
+ const bNodeSocket *input_socket = sockets_to_check.pop();
if (!input_socket->is_directly_linked() &&
- !field_state_by_socket_id[input_socket->id()].is_single) {
+ !field_state_by_socket_id[input_socket->index_in_tree()].is_single) {
/* This socket uses a field as input by default. */
return OutputFieldDependency::ForFieldSource();
}
- for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) {
- const NodeRef &origin_node = origin_socket->node();
- const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()];
+ for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
+ const bNode &origin_node = origin_socket->owner_node();
+ const SocketFieldState &origin_state =
+ field_state_by_socket_id[origin_socket->index_in_tree()];
if (origin_state.is_field_source) {
- if (origin_node.is_group_input_node()) {
+ if (origin_node.type == NODE_GROUP_INPUT) {
/* Found a group input that the group output depends on. */
linked_input_indices.append_non_duplicates(origin_socket->index());
}
@@ -288,12 +297,12 @@ static OutputFieldDependency find_group_output_dependencies(
inferencing_interface.outputs[origin_socket->index()];
/* Propagate search further to the left. */
- for (const InputSocketRef *origin_input_socket :
+ for (const bNodeSocket *origin_input_socket :
gather_input_socket_dependencies(field_dependency, origin_node)) {
if (!origin_input_socket->is_available()) {
continue;
}
- if (!field_state_by_socket_id[origin_input_socket->id()].is_single) {
+ if (!field_state_by_socket_id[origin_input_socket->index_in_tree()].is_single) {
if (handled_sockets.add(origin_input_socket)) {
sockets_to_check.push(origin_input_socket);
}
@@ -306,17 +315,16 @@ static OutputFieldDependency find_group_output_dependencies(
}
static void propagate_data_requirements_from_right_to_left(
- const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id)
+ const bNodeTree &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id)
{
- const NodeTreeRef::ToposortResult toposort_result = tree.toposort(
- NodeTreeRef::ToposortDirection::RightToLeft);
+ const Span<const bNode *> toposort_result = tree.toposort_right_to_left();
- for (const NodeRef *node : toposort_result.sorted_nodes) {
+ for (const bNode *node : toposort_result) {
const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface(
*node);
- for (const OutputSocketRef *output_socket : node->outputs()) {
- SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
+ for (const bNodeSocket *output_socket : node->output_sockets()) {
+ SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
const OutputFieldDependency &field_dependency =
inferencing_interface.outputs[output_socket->index()];
@@ -332,17 +340,18 @@ static void propagate_data_requirements_from_right_to_left(
/* The output is required to be a single value when it is connected to any input that does
* not support fields. */
- for (const InputSocketRef *target_socket : output_socket->directly_linked_sockets()) {
+ for (const bNodeSocket *target_socket : output_socket->directly_linked_sockets()) {
if (target_socket->is_available()) {
- state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single;
+ state.requires_single |=
+ field_state_by_socket_id[target_socket->index_in_tree()].requires_single;
}
}
if (state.requires_single) {
bool any_input_is_field_implicitly = false;
- const Vector<const InputSocketRef *> connected_inputs = gather_input_socket_dependencies(
+ const Vector<const bNodeSocket *> connected_inputs = gather_input_socket_dependencies(
field_dependency, *node);
- for (const InputSocketRef *input_socket : connected_inputs) {
+ for (const bNodeSocket *input_socket : connected_inputs) {
if (!input_socket->is_available()) {
continue;
}
@@ -361,16 +370,16 @@ static void propagate_data_requirements_from_right_to_left(
else {
/* If the output is required to be a single value, the connected inputs in the same node
* must not be fields as well. */
- for (const InputSocketRef *input_socket : connected_inputs) {
- field_state_by_socket_id[input_socket->id()].requires_single = true;
+ for (const bNodeSocket *input_socket : connected_inputs) {
+ field_state_by_socket_id[input_socket->index_in_tree()].requires_single = true;
}
}
}
}
/* Some inputs do not require fields independent of what the outputs are connected to. */
- for (const InputSocketRef *input_socket : node->inputs()) {
- SocketFieldState &state = field_state_by_socket_id[input_socket->id()];
+ for (const bNodeSocket *input_socket : node->input_sockets()) {
+ SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) {
state.requires_single = true;
state.is_always_single = true;
@@ -380,14 +389,14 @@ static void propagate_data_requirements_from_right_to_left(
}
static void determine_group_input_states(
- const NodeTreeRef &tree,
+ const bNodeTree &tree,
FieldInferencingInterface &new_inferencing_interface,
const MutableSpan<SocketFieldState> field_state_by_socket_id)
{
{
/* Non-field inputs never support fields. */
int index;
- LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) {
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.inputs, index) {
if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) {
new_inferencing_interface.inputs[index] = InputSocketFieldType::None;
}
@@ -395,18 +404,18 @@ static void determine_group_input_states(
}
/* Check if group inputs are required to be single values, because they are (indirectly)
* connected to some socket that does not support fields. */
- for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) {
- for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) {
- SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
+ for (const bNode *node : tree.nodes_by_type("NodeGroupInput")) {
+ for (const bNodeSocket *output_socket : node->output_sockets().drop_back(1)) {
+ SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
if (state.requires_single) {
new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None;
}
}
}
/* If an input does not support fields, this should be reflected in all Group Input nodes. */
- for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) {
- for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) {
- SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
+ for (const bNode *node : tree.nodes_by_type("NodeGroupInput")) {
+ for (const bNodeSocket *output_socket : node->output_sockets().drop_back(1)) {
+ SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] !=
InputSocketFieldType::None;
if (supports_field) {
@@ -417,19 +426,19 @@ static void determine_group_input_states(
state.requires_single = true;
}
}
- SocketFieldState &dummy_socket_state = field_state_by_socket_id[node->outputs().last()->id()];
+ SocketFieldState &dummy_socket_state =
+ field_state_by_socket_id[node->output_sockets().last()->index_in_tree()];
dummy_socket_state.requires_single = true;
}
}
static void propagate_field_status_from_left_to_right(
- const NodeTreeRef &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id)
+ const bNodeTree &tree, const MutableSpan<SocketFieldState> field_state_by_socket_id)
{
- const NodeTreeRef::ToposortResult toposort_result = tree.toposort(
- NodeTreeRef::ToposortDirection::LeftToRight);
+ const Span<const bNode *> toposort_result = tree.toposort_left_to_right();
- for (const NodeRef *node : toposort_result.sorted_nodes) {
- if (node->is_group_input_node()) {
+ for (const bNode *node : toposort_result) {
+ if (node->type == NODE_GROUP_INPUT) {
continue;
}
@@ -437,22 +446,22 @@ static void propagate_field_status_from_left_to_right(
*node);
/* Update field state of input sockets, also taking into account linked origin sockets. */
- for (const InputSocketRef *input_socket : node->inputs()) {
- SocketFieldState &state = field_state_by_socket_id[input_socket->id()];
+ for (const bNodeSocket *input_socket : node->input_sockets()) {
+ SocketFieldState &state = field_state_by_socket_id[input_socket->index_in_tree()];
if (state.is_always_single) {
state.is_single = true;
continue;
}
state.is_single = true;
- if (input_socket->directly_linked_sockets().is_empty()) {
+ if (!input_socket->is_directly_linked()) {
if (inferencing_interface.inputs[input_socket->index()] ==
InputSocketFieldType::Implicit) {
state.is_single = false;
}
}
else {
- for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) {
- if (!field_state_by_socket_id[origin_socket->id()].is_single) {
+ for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
+ if (!field_state_by_socket_id[origin_socket->index_in_tree()].is_single) {
state.is_single = false;
break;
}
@@ -461,8 +470,8 @@ static void propagate_field_status_from_left_to_right(
}
/* Update field state of output sockets, also taking into account input sockets. */
- for (const OutputSocketRef *output_socket : node->outputs()) {
- SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
+ for (const bNodeSocket *output_socket : node->output_sockets()) {
+ SocketFieldState &state = field_state_by_socket_id[output_socket->index_in_tree()];
const OutputFieldDependency &field_dependency =
inferencing_interface.outputs[output_socket->index()];
@@ -478,12 +487,12 @@ static void propagate_field_status_from_left_to_right(
}
case OutputSocketFieldType::PartiallyDependent:
case OutputSocketFieldType::DependentField: {
- for (const InputSocketRef *input_socket :
+ for (const bNodeSocket *input_socket :
gather_input_socket_dependencies(field_dependency, *node)) {
if (!input_socket->is_available()) {
continue;
}
- if (!field_state_by_socket_id[input_socket->id()].is_single) {
+ if (!field_state_by_socket_id[input_socket->index_in_tree()].is_single) {
state.is_single = false;
break;
}
@@ -495,17 +504,18 @@ static void propagate_field_status_from_left_to_right(
}
}
-static void determine_group_output_states(const NodeTreeRef &tree,
+static void determine_group_output_states(const bNodeTree &tree,
FieldInferencingInterface &new_inferencing_interface,
const Span<SocketFieldState> field_state_by_socket_id)
{
- for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) {
+ for (const bNode *group_output_node : tree.nodes_by_type("NodeGroupOutput")) {
/* Ignore inactive group output nodes. */
- if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) {
+ if (!(group_output_node->flag & NODE_DO_OUTPUT)) {
continue;
}
/* Determine dependencies of all group outputs. */
- for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) {
+ for (const bNodeSocket *group_output_socket :
+ group_output_node->input_sockets().drop_back(1)) {
OutputFieldDependency field_dependency = find_group_output_dependencies(
*group_output_socket, field_state_by_socket_id);
new_inferencing_interface.outputs[group_output_socket->index()] = std::move(
@@ -515,7 +525,7 @@ static void determine_group_output_states(const NodeTreeRef &tree,
}
}
-static void update_socket_shapes(const NodeTreeRef &tree,
+static void update_socket_shapes(const bNodeTree &tree,
const Span<SocketFieldState> field_state_by_socket_id)
{
const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
@@ -535,32 +545,30 @@ static void update_socket_shapes(const NodeTreeRef &tree,
return data_but_can_be_field_shape;
};
- for (const InputSocketRef *socket : tree.input_sockets()) {
- bNodeSocket *bsocket = socket->bsocket();
- const SocketFieldState &state = field_state_by_socket_id[socket->id()];
- bsocket->display_shape = get_shape_for_state(state);
+ for (const bNodeSocket *socket : tree.all_input_sockets()) {
+ const SocketFieldState &state = field_state_by_socket_id[socket->index_in_tree()];
+ const_cast<bNodeSocket *>(socket)->display_shape = get_shape_for_state(state);
}
- for (const OutputSocketRef *socket : tree.output_sockets()) {
- bNodeSocket *bsocket = socket->bsocket();
- const SocketFieldState &state = field_state_by_socket_id[socket->id()];
- bsocket->display_shape = get_shape_for_state(state);
+ for (const bNodeSocket *socket : tree.all_sockets()) {
+ const SocketFieldState &state = field_state_by_socket_id[socket->index_in_tree()];
+ const_cast<bNodeSocket *>(socket)->display_shape = get_shape_for_state(state);
}
}
-static bool update_field_inferencing(const NodeTreeRef &tree)
+static bool update_field_inferencing(const bNodeTree &tree)
{
- bNodeTree &btree = *tree.btree();
+ tree.ensure_topology_cache();
/* Create new inferencing interface for this node group. */
std::unique_ptr<FieldInferencingInterface> new_inferencing_interface =
std::make_unique<FieldInferencingInterface>();
- new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs),
+ new_inferencing_interface->inputs.resize(BLI_listbase_count(&tree.inputs),
InputSocketFieldType::IsSupported);
- new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs),
+ new_inferencing_interface->outputs.resize(BLI_listbase_count(&tree.outputs),
OutputFieldDependency::ForDataSource());
/* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */
- Array<SocketFieldState> field_state_by_socket_id(tree.sockets().size());
+ Array<SocketFieldState> field_state_by_socket_id(tree.all_sockets().size());
propagate_data_requirements_from_right_to_left(tree, field_state_by_socket_id);
determine_group_input_states(tree, *new_inferencing_interface, field_state_by_socket_id);
@@ -569,10 +577,10 @@ static bool update_field_inferencing(const NodeTreeRef &tree)
update_socket_shapes(tree, field_state_by_socket_id);
/* Update the previous group interface. */
- const bool group_interface_changed = !btree.runtime->field_inferencing_interface ||
- *btree.runtime->field_inferencing_interface !=
+ const bool group_interface_changed = !tree.runtime->field_inferencing_interface ||
+ *tree.runtime->field_inferencing_interface !=
*new_inferencing_interface;
- btree.runtime->field_inferencing_interface = std::move(new_inferencing_interface);
+ tree.runtime->field_inferencing_interface = std::move(new_inferencing_interface);
return group_interface_changed;
}
@@ -973,29 +981,22 @@ class NodeTreeMainUpdater {
{
TreeUpdateResult result;
- /* Use a #NodeTreeRef to speedup certain queries. It is rebuilt whenever the node tree topology
- * changes, which typically happens zero or one times during the entire update of the node
- * tree. */
- std::unique_ptr<NodeTreeRef> tree_ref;
- this->ensure_tree_ref(ntree, tree_ref);
-
- this->update_socket_link_and_use(*tree_ref);
- this->update_individual_nodes(ntree, tree_ref);
- this->update_internal_links(ntree, tree_ref);
- this->update_generic_callback(ntree, tree_ref);
+ this->update_socket_link_and_use(ntree);
+ this->update_individual_nodes(ntree);
+ this->update_internal_links(ntree);
+ this->update_generic_callback(ntree);
this->remove_unused_previews_when_necessary(ntree);
- this->ensure_tree_ref(ntree, tree_ref);
- this->propagate_runtime_flags(*tree_ref);
+ this->propagate_runtime_flags(ntree);
if (ntree.type == NTREE_GEOMETRY) {
- if (node_field_inferencing::update_field_inferencing(*tree_ref)) {
+ if (node_field_inferencing::update_field_inferencing(ntree)) {
result.interface_changed = true;
}
}
- result.output_changed = this->check_if_output_changed(*tree_ref);
+ result.output_changed = this->check_if_output_changed(ntree);
- this->update_socket_link_and_use(*tree_ref);
+ this->update_socket_link_and_use(ntree);
this->update_node_levels(ntree);
this->update_link_validation(ntree);
@@ -1015,86 +1016,83 @@ class NodeTreeMainUpdater {
return result;
}
- void ensure_tree_ref(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref)
- {
- if (!tree_ref) {
- tree_ref = std::make_unique<NodeTreeRef>(&ntree);
- }
- }
-
- void update_socket_link_and_use(const NodeTreeRef &tree)
+ void update_socket_link_and_use(bNodeTree &tree)
{
- for (const InputSocketRef *socket : tree.input_sockets()) {
- bNodeSocket *bsocket = socket->bsocket();
+ tree.ensure_topology_cache();
+ for (bNodeSocket *socket : tree.all_input_sockets()) {
if (socket->directly_linked_links().is_empty()) {
- bsocket->link = nullptr;
+ socket->link = nullptr;
}
else {
- bsocket->link = socket->directly_linked_links()[0]->blink();
+ socket->link = socket->directly_linked_links()[0];
}
}
this->update_socket_used_tags(tree);
}
- void update_socket_used_tags(const NodeTreeRef &tree)
+ void update_socket_used_tags(bNodeTree &tree)
{
- for (const SocketRef *socket : tree.sockets()) {
- bNodeSocket *bsocket = socket->bsocket();
- bsocket->flag &= ~SOCK_IN_USE;
- for (const LinkRef *link : socket->directly_linked_links()) {
+ tree.ensure_topology_cache();
+ for (bNodeSocket *socket : tree.all_sockets()) {
+ socket->flag &= ~SOCK_IN_USE;
+ for (const bNodeLink *link : socket->directly_linked_links()) {
if (!link->is_muted()) {
- bsocket->flag |= SOCK_IN_USE;
+ socket->flag |= SOCK_IN_USE;
break;
}
}
}
}
- void update_individual_nodes(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref)
+ void update_individual_nodes(bNodeTree &ntree)
{
- /* Iterate over nodes instead of #NodeTreeRef, because the #tree_ref might be outdated after
- * some update functions. */
- LISTBASE_FOREACH (bNode *, bnode, &ntree.nodes) {
- this->ensure_tree_ref(ntree, tree_ref);
- const NodeRef &node = *tree_ref->find_node(*bnode);
- if (this->should_update_individual_node(node)) {
- const uint32_t old_changed_flag = ntree.runtime->changed_flag;
- ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING;
-
- /* This may set #ntree.runtime->changed_flag which is detected below. */
- this->update_individual_node(node);
-
- if (ntree.runtime->changed_flag != NTREE_CHANGED_NOTHING) {
- /* The tree ref is outdated and needs to be rebuilt. Generally, only very few update
- * functions change the node. Typically zero or one nodes change after an update. */
- tree_ref.reset();
+ Vector<bNode *> group_inout_nodes;
+ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
+ nodeDeclarationEnsure(&ntree, node);
+ if (this->should_update_individual_node(ntree, *node)) {
+ bNodeType &ntype = *node->typeinfo;
+ if (ntype.group_update_func) {
+ ntype.group_update_func(&ntree, node);
}
- ntree.runtime->changed_flag |= old_changed_flag;
+ if (ntype.updatefunc) {
+ ntype.updatefunc(&ntree, node);
+ }
+ }
+ if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
+ group_inout_nodes.append(node);
+ }
+ }
+ /* The update function of group input/output nodes may add new interface sockets. When that
+ * happens, all the input/output nodes have to be updated again. In the future it would be
+ * better to move this functionality out of the node update function into the operator that's
+ * supposed to create the new interface socket. */
+ if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE) {
+ for (bNode *node : group_inout_nodes) {
+ node->typeinfo->updatefunc(&ntree, node);
}
}
}
- bool should_update_individual_node(const NodeRef &node)
+ bool should_update_individual_node(const bNodeTree &ntree, const bNode &node)
{
- bNodeTree &ntree = *node.btree();
- bNode &bnode = *node.bnode();
if (ntree.runtime->changed_flag & NTREE_CHANGED_ANY) {
return true;
}
- if (bnode.runtime->changed_flag & NTREE_CHANGED_NODE_PROPERTY) {
+ if (node.runtime->changed_flag & NTREE_CHANGED_NODE_PROPERTY) {
return true;
}
if (ntree.runtime->changed_flag & NTREE_CHANGED_LINK) {
+ ntree.ensure_topology_cache();
/* Node groups currently always rebuilt their sockets when they are updated.
* So avoid calling the update method when no new link was added to it. */
- if (node.is_group_input_node()) {
- if (node.outputs().last()->is_directly_linked()) {
+ if (node.type == NODE_GROUP_INPUT) {
+ if (node.output_sockets().last()->is_directly_linked()) {
return true;
}
}
- else if (node.is_group_output_node()) {
- if (node.inputs().last()->is_directly_linked()) {
+ else if (node.type == NODE_GROUP_OUTPUT) {
+ if (node.input_sockets().last()->is_directly_linked()) {
return true;
}
}
@@ -1104,95 +1102,76 @@ class NodeTreeMainUpdater {
}
}
if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE) {
- if (node.is_group_input_node() || node.is_group_output_node()) {
+ if (ELEM(node.type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) {
return true;
}
}
return false;
}
- void update_individual_node(const NodeRef &node)
- {
- bNodeTree &ntree = *node.btree();
- bNode &bnode = *node.bnode();
- bNodeType &ntype = *bnode.typeinfo;
- if (ntype.group_update_func) {
- ntype.group_update_func(&ntree, &bnode);
- }
- if (ntype.updatefunc) {
- ntype.updatefunc(&ntree, &bnode);
- }
- }
-
- void update_internal_links(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref)
+ void update_internal_links(bNodeTree &ntree)
{
- bool any_internal_links_updated = false;
- this->ensure_tree_ref(ntree, tree_ref);
- for (const NodeRef *node : tree_ref->nodes()) {
- if (!this->should_update_individual_node(*node)) {
+ bke::node_tree_runtime::AllowUsingOutdatedInfo allow_outdated_info{ntree};
+ ntree.ensure_topology_cache();
+ for (bNode *node : ntree.all_nodes()) {
+ if (!this->should_update_individual_node(ntree, *node)) {
continue;
}
/* Find all expected internal links. */
Vector<std::pair<bNodeSocket *, bNodeSocket *>> expected_internal_links;
- for (const OutputSocketRef *output_socket : node->outputs()) {
+ for (const bNodeSocket *output_socket : node->output_sockets()) {
if (!output_socket->is_available()) {
continue;
}
if (!output_socket->is_directly_linked()) {
continue;
}
- if (output_socket->bsocket()->flag & SOCK_NO_INTERNAL_LINK) {
+ if (output_socket->flag & SOCK_NO_INTERNAL_LINK) {
continue;
}
- const InputSocketRef *input_socket = this->find_internally_linked_input(output_socket);
+ const bNodeSocket *input_socket = this->find_internally_linked_input(output_socket);
if (input_socket != nullptr) {
- expected_internal_links.append({input_socket->bsocket(), output_socket->bsocket()});
+ expected_internal_links.append(
+ {const_cast<bNodeSocket *>(input_socket), const_cast<bNodeSocket *>(output_socket)});
}
}
- /* rebuilt internal links if they have changed. */
- if (node->internal_links().size() != expected_internal_links.size()) {
- this->update_internal_links_in_node(ntree, *node->bnode(), expected_internal_links);
- any_internal_links_updated = true;
+ /* Rebuilt internal links if they have changed. */
+ if (node->internal_links_span().size() != expected_internal_links.size()) {
+ this->update_internal_links_in_node(ntree, *node, expected_internal_links);
}
else {
for (auto &item : expected_internal_links) {
const bNodeSocket *from_socket = item.first;
const bNodeSocket *to_socket = item.second;
bool found = false;
- for (const InternalLinkRef *internal_link : node->internal_links()) {
- if (from_socket == internal_link->from().bsocket() &&
- to_socket == internal_link->to().bsocket()) {
+ for (const bNodeLink *internal_link : node->internal_links_span()) {
+ if (from_socket == internal_link->fromsock && to_socket == internal_link->tosock) {
found = true;
}
}
if (!found) {
- this->update_internal_links_in_node(ntree, *node->bnode(), expected_internal_links);
- any_internal_links_updated = true;
+ this->update_internal_links_in_node(ntree, *node, expected_internal_links);
break;
}
}
}
}
-
- if (any_internal_links_updated) {
- tree_ref.reset();
- }
}
- const InputSocketRef *find_internally_linked_input(const OutputSocketRef *output_socket)
+ const bNodeSocket *find_internally_linked_input(const bNodeSocket *output_socket)
{
- const InputSocketRef *selected_socket = nullptr;
+ const bNodeSocket *selected_socket = nullptr;
int selected_priority = -1;
bool selected_is_linked = false;
- for (const InputSocketRef *input_socket : output_socket->node().inputs()) {
+ for (const bNodeSocket *input_socket : output_socket->owner_node().input_sockets()) {
if (!input_socket->is_available()) {
continue;
}
- if (input_socket->bsocket()->flag & SOCK_NO_INTERNAL_LINK) {
+ if (input_socket->flag & SOCK_NO_INTERNAL_LINK) {
continue;
}
- const int priority = get_internal_link_type_priority(input_socket->bsocket()->typeinfo,
- output_socket->bsocket()->typeinfo);
+ const int priority = get_internal_link_type_priority(input_socket->typeinfo,
+ output_socket->typeinfo);
if (priority < 0) {
continue;
}
@@ -1227,23 +1206,12 @@ class NodeTreeMainUpdater {
BKE_ntree_update_tag_node_internal_link(&ntree, &node);
}
- void update_generic_callback(bNodeTree &ntree, std::unique_ptr<NodeTreeRef> &tree_ref)
+ void update_generic_callback(bNodeTree &ntree)
{
if (ntree.typeinfo->update == nullptr) {
return;
}
-
- /* Reset the changed_flag to allow detecting when the update callback changed the node tree. */
- const uint32_t old_changed_flag = ntree.runtime->changed_flag;
- ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING;
-
ntree.typeinfo->update(&ntree);
-
- if (ntree.runtime->changed_flag != NTREE_CHANGED_NOTHING) {
- /* The tree ref is outdated and needs to be rebuilt. */
- tree_ref.reset();
- }
- ntree.runtime->changed_flag |= old_changed_flag;
}
void remove_unused_previews_when_necessary(bNodeTree &ntree)
@@ -1258,25 +1226,26 @@ class NodeTreeMainUpdater {
BKE_node_preview_remove_unused(&ntree);
}
- void propagate_runtime_flags(const NodeTreeRef &tree_ref)
+ void propagate_runtime_flags(const bNodeTree &ntree)
{
- bNodeTree &ntree = *tree_ref.btree();
+ ntree.ensure_topology_cache();
+
ntree.runtime->runtime_flag = 0;
if (ntree.type != NTREE_SHADER) {
return;
}
/* Check if a used node group has an animated image. */
- for (const NodeRef *group_node : tree_ref.nodes_by_type("NodeGroup")) {
- const bNodeTree *group = reinterpret_cast<bNodeTree *>(group_node->bnode()->id);
+ for (const bNode *group_node : ntree.nodes_by_type("ShaderNodeGroup")) {
+ const bNodeTree *group = reinterpret_cast<bNodeTree *>(group_node->id);
if (group != nullptr) {
ntree.runtime->runtime_flag |= group->runtime->runtime_flag;
}
}
/* Check if the tree itself has an animated image. */
for (const StringRefNull idname : {"ShaderNodeTexImage", "ShaderNodeTexEnvironment"}) {
- for (const NodeRef *node : tree_ref.nodes_by_type(idname)) {
- Image *image = reinterpret_cast<Image *>(node->bnode()->id);
+ for (const bNode *node : ntree.nodes_by_type(idname)) {
+ Image *image = reinterpret_cast<Image *>(node->id);
if (image != nullptr && BKE_image_is_animated(image)) {
ntree.runtime->runtime_flag |= NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION;
break;
@@ -1288,7 +1257,7 @@ class NodeTreeMainUpdater {
"ShaderNodeOutputLight",
"ShaderNodeOutputWorld",
"ShaderNodeOutputAOV"}) {
- const Span<const NodeRef *> nodes = tree_ref.nodes_by_type(idname);
+ const Span<const bNode *> nodes = ntree.nodes_by_type(idname);
if (!nodes.is_empty()) {
ntree.runtime->runtime_flag |= NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT;
break;
@@ -1319,20 +1288,20 @@ class NodeTreeMainUpdater {
}
}
- bool check_if_output_changed(const NodeTreeRef &tree)
+ bool check_if_output_changed(const bNodeTree &tree)
{
- bNodeTree &btree = *tree.btree();
+ tree.ensure_topology_cache();
/* Compute a hash that represents the node topology connected to the output. This always has to
* be updated even if it is not used to detect changes right now. Otherwise
* #btree.runtime.output_topology_hash will go out of date. */
- const Vector<const SocketRef *> tree_output_sockets = this->find_output_sockets(tree);
- const uint32_t old_topology_hash = btree.runtime->output_topology_hash;
+ const Vector<const bNodeSocket *> tree_output_sockets = this->find_output_sockets(tree);
+ const uint32_t old_topology_hash = tree.runtime->output_topology_hash;
const uint32_t new_topology_hash = this->get_combined_socket_topology_hash(
tree, tree_output_sockets);
- btree.runtime->output_topology_hash = new_topology_hash;
+ tree.runtime->output_topology_hash = new_topology_hash;
- if (const AnimData *adt = BKE_animdata_from_id(&btree.id)) {
+ if (const AnimData *adt = BKE_animdata_from_id(&tree.id)) {
/* Drivers may copy values in the node tree around arbitrarily and may cause the output to
* change even if it wouldn't without drivers. Only some special drivers like `frame/5` can
* be used without causing updates all the time currently. In the future we could try to
@@ -1354,7 +1323,7 @@ class NodeTreeMainUpdater {
}
}
- if (btree.runtime->changed_flag & NTREE_CHANGED_ANY) {
+ if (tree.runtime->changed_flag & NTREE_CHANGED_ANY) {
return true;
}
@@ -1363,8 +1332,8 @@ class NodeTreeMainUpdater {
}
/* The topology hash can only be used when only topology-changing operations have been done. */
- if (btree.runtime->changed_flag ==
- (btree.runtime->changed_flag & (NTREE_CHANGED_LINK | NTREE_CHANGED_REMOVED_NODE))) {
+ if (tree.runtime->changed_flag ==
+ (tree.runtime->changed_flag & (NTREE_CHANGED_LINK | NTREE_CHANGED_REMOVED_NODE))) {
if (old_topology_hash == new_topology_hash) {
return false;
}
@@ -1377,15 +1346,15 @@ class NodeTreeMainUpdater {
return true;
}
- Vector<const SocketRef *> find_output_sockets(const NodeTreeRef &tree)
+ Vector<const bNodeSocket *> find_output_sockets(const bNodeTree &tree)
{
- Vector<const SocketRef *> sockets;
- for (const NodeRef *node : tree.nodes()) {
+ Vector<const bNodeSocket *> sockets;
+ for (const bNode *node : tree.all_nodes()) {
if (!this->is_output_node(*node)) {
continue;
}
- for (const InputSocketRef *socket : node->inputs()) {
- if (socket->idname() != "NodeSocketVirtual") {
+ for (const bNodeSocket *socket : node->input_sockets()) {
+ if (!STREQ(socket->idname, "NodeSocketVirtual")) {
sockets.append(socket);
}
}
@@ -1393,18 +1362,17 @@ class NodeTreeMainUpdater {
return sockets;
}
- bool is_output_node(const NodeRef &node) const
+ bool is_output_node(const bNode &node) const
{
- const bNode &bnode = *node.bnode();
- if (bnode.typeinfo->nclass == NODE_CLASS_OUTPUT) {
+ if (node.typeinfo->nclass == NODE_CLASS_OUTPUT) {
return true;
}
- if (bnode.type == NODE_GROUP_OUTPUT) {
+ if (node.type == NODE_GROUP_OUTPUT) {
return true;
}
/* Assume node groups without output sockets are outputs. */
- if (bnode.type == NODE_GROUP) {
- const bNodeTree *node_group = reinterpret_cast<const bNodeTree *>(bnode.id);
+ if (node.type == NODE_GROUP) {
+ const bNodeTree *node_group = reinterpret_cast<const bNodeTree *>(node.id);
if (node_group != nullptr &&
node_group->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT) {
return true;
@@ -1417,10 +1385,10 @@ class NodeTreeMainUpdater {
* Computes a hash that changes when the node tree topology connected to an output node changes.
* Adding reroutes does not have an effect on the hash.
*/
- uint32_t get_combined_socket_topology_hash(const NodeTreeRef &tree,
- Span<const SocketRef *> sockets)
+ uint32_t get_combined_socket_topology_hash(const bNodeTree &tree,
+ Span<const bNodeSocket *> sockets)
{
- if (tree.has_link_cycles()) {
+ if (tree.has_available_link_cycle()) {
/* Return dummy value when the link has any cycles. The algorithm below could be improved to
* handle cycles more gracefully. */
return 0;
@@ -1433,29 +1401,28 @@ class NodeTreeMainUpdater {
return combined_hash;
}
- Array<uint32_t> get_socket_topology_hashes(const NodeTreeRef &tree,
- Span<const SocketRef *> sockets)
+ Array<uint32_t> get_socket_topology_hashes(const bNodeTree &tree,
+ Span<const bNodeSocket *> sockets)
{
- BLI_assert(!tree.has_link_cycles());
- Array<std::optional<uint32_t>> hash_by_socket_id(tree.sockets().size());
- Stack<const SocketRef *> sockets_to_check = sockets;
+ BLI_assert(!tree.has_available_link_cycle());
+ Array<std::optional<uint32_t>> hash_by_socket_id(tree.all_sockets().size());
+ Stack<const bNodeSocket *> sockets_to_check = sockets;
while (!sockets_to_check.is_empty()) {
- const SocketRef &in_out_socket = *sockets_to_check.peek();
- const NodeRef &node = in_out_socket.node();
+ const bNodeSocket &socket = *sockets_to_check.peek();
+ const bNode &node = socket.owner_node();
- if (hash_by_socket_id[in_out_socket.id()].has_value()) {
+ if (hash_by_socket_id[socket.index_in_tree()].has_value()) {
sockets_to_check.pop();
/* Socket is handled already. */
continue;
}
- if (in_out_socket.is_input()) {
+ if (socket.is_input()) {
/* For input sockets, first compute the hashes of all linked sockets. */
- const InputSocketRef &socket = in_out_socket.as_input();
bool all_origins_computed = true;
- for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) {
- if (!hash_by_socket_id[origin_socket->id()].has_value()) {
+ for (const bNodeSocket *origin_socket : socket.logically_linked_sockets()) {
+ if (!hash_by_socket_id[origin_socket->index_in_tree()].has_value()) {
sockets_to_check.push(origin_socket);
all_origins_computed = false;
}
@@ -1465,22 +1432,21 @@ class NodeTreeMainUpdater {
}
/* When the hashes for the linked sockets are ready, combine them into a hash for the input
* socket. */
- const uint64_t socket_ptr = (uintptr_t)socket.bsocket();
+ const uint64_t socket_ptr = (uintptr_t)&socket;
uint32_t socket_hash = noise::hash(socket_ptr, socket_ptr >> 32);
- for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) {
- const uint32_t origin_socket_hash = *hash_by_socket_id[origin_socket->id()];
+ for (const bNodeSocket *origin_socket : socket.logically_linked_sockets()) {
+ const uint32_t origin_socket_hash = *hash_by_socket_id[origin_socket->index_in_tree()];
socket_hash = noise::hash(socket_hash, origin_socket_hash);
}
- hash_by_socket_id[socket.id()] = socket_hash;
+ hash_by_socket_id[socket.index_in_tree()] = socket_hash;
sockets_to_check.pop();
}
else {
/* For output sockets, first compute the hashes of all available input sockets. */
- const OutputSocketRef &socket = in_out_socket.as_output();
bool all_available_inputs_computed = true;
- for (const InputSocketRef *input_socket : node.inputs()) {
+ for (const bNodeSocket *input_socket : node.input_sockets()) {
if (input_socket->is_available()) {
- if (!hash_by_socket_id[input_socket->id()].has_value()) {
+ if (!hash_by_socket_id[input_socket->index_in_tree()].has_value()) {
sockets_to_check.push(input_socket);
all_available_inputs_computed = false;
}
@@ -1491,25 +1457,25 @@ class NodeTreeMainUpdater {
}
/* When all input socket hashes have been computed, combine them into a hash for the output
* socket. */
- const uint64_t socket_ptr = (uintptr_t)socket.bsocket();
+ const uint64_t socket_ptr = (uintptr_t)&socket;
uint32_t socket_hash = noise::hash(socket_ptr, socket_ptr >> 32);
- for (const InputSocketRef *input_socket : node.inputs()) {
+ for (const bNodeSocket *input_socket : node.input_sockets()) {
if (input_socket->is_available()) {
- const uint32_t input_socket_hash = *hash_by_socket_id[input_socket->id()];
+ const uint32_t input_socket_hash = *hash_by_socket_id[input_socket->index_in_tree()];
socket_hash = noise::hash(socket_hash, input_socket_hash);
}
}
/* The Image Texture node has a special case. The behavior of the color output changes
* depending on whether the Alpha output is linked. */
- if (node.bnode()->type == SH_NODE_TEX_IMAGE && socket.index() == 0) {
- BLI_assert(socket.name() == "Color");
- const OutputSocketRef &alpha_socket = node.output(1);
- BLI_assert(alpha_socket.name() == "Alpha");
+ if (node.type == SH_NODE_TEX_IMAGE && socket.index() == 0) {
+ BLI_assert(STREQ(socket.name, "Color"));
+ const bNodeSocket &alpha_socket = node.output_socket(1);
+ BLI_assert(STREQ(alpha_socket.name, "Alpha"));
if (alpha_socket.is_directly_linked()) {
socket_hash = noise::hash(socket_hash);
}
}
- hash_by_socket_id[socket.id()] = socket_hash;
+ hash_by_socket_id[socket.index_in_tree()] = socket_hash;
sockets_to_check.pop();
}
}
@@ -1517,7 +1483,7 @@ class NodeTreeMainUpdater {
/* Create output array. */
Array<uint32_t> hashes(sockets.size());
for (const int i : sockets.index_range()) {
- hashes[i] = *hash_by_socket_id[sockets[i]->id()];
+ hashes[i] = *hash_by_socket_id[sockets[i]->index_in_tree()];
}
return hashes;
}
@@ -1526,37 +1492,34 @@ class NodeTreeMainUpdater {
* Returns true when any of the provided sockets changed its values. A change is detected by
* checking the #changed_flag on connected sockets and nodes.
*/
- bool check_if_socket_outputs_changed_based_on_flags(const NodeTreeRef &tree,
- Span<const SocketRef *> sockets)
+ bool check_if_socket_outputs_changed_based_on_flags(const bNodeTree &tree,
+ Span<const bNodeSocket *> sockets)
{
/* Avoid visiting the same socket twice when multiple links point to the same socket. */
- Array<bool> pushed_by_socket_id(tree.sockets().size(), false);
- Stack<const SocketRef *> sockets_to_check = sockets;
+ Array<bool> pushed_by_socket_id(tree.all_sockets().size(), false);
+ Stack<const bNodeSocket *> sockets_to_check = sockets;
- for (const SocketRef *socket : sockets) {
- pushed_by_socket_id[socket->id()] = true;
+ for (const bNodeSocket *socket : sockets) {
+ pushed_by_socket_id[socket->index_in_tree()] = true;
}
while (!sockets_to_check.is_empty()) {
- const SocketRef &in_out_socket = *sockets_to_check.pop();
- const NodeRef &node = in_out_socket.node();
- const bNode &bnode = *node.bnode();
- const bNodeSocket &bsocket = *in_out_socket.bsocket();
- if (bsocket.runtime->changed_flag != NTREE_CHANGED_NOTHING) {
+ const bNodeSocket &socket = *sockets_to_check.pop();
+ const bNode &node = socket.owner_node();
+ if (socket.runtime->changed_flag != NTREE_CHANGED_NOTHING) {
return true;
}
- if (bnode.runtime->changed_flag != NTREE_CHANGED_NOTHING) {
- const bool only_unused_internal_link_changed = (bnode.flag & NODE_MUTED) == 0 &&
- bnode.runtime->changed_flag ==
+ if (node.runtime->changed_flag != NTREE_CHANGED_NOTHING) {
+ const bool only_unused_internal_link_changed = !node.is_muted() &&
+ node.runtime->changed_flag ==
NTREE_CHANGED_INTERNAL_LINK;
if (!only_unused_internal_link_changed) {
return true;
}
}
- if (in_out_socket.is_input()) {
- const InputSocketRef &socket = in_out_socket.as_input();
- for (const OutputSocketRef *origin_socket : socket.logically_linked_sockets()) {
- bool &pushed = pushed_by_socket_id[origin_socket->id()];
+ if (socket.is_input()) {
+ for (const bNodeSocket *origin_socket : socket.logically_linked_sockets()) {
+ bool &pushed = pushed_by_socket_id[origin_socket->index_in_tree()];
if (!pushed) {
sockets_to_check.push(origin_socket);
pushed = true;
@@ -1564,10 +1527,9 @@ class NodeTreeMainUpdater {
}
}
else {
- const OutputSocketRef &socket = in_out_socket.as_output();
- for (const InputSocketRef *input_socket : node.inputs()) {
+ for (const bNodeSocket *input_socket : node.input_sockets()) {
if (input_socket->is_available()) {
- bool &pushed = pushed_by_socket_id[input_socket->id()];
+ bool &pushed = pushed_by_socket_id[input_socket->index_in_tree()];
if (!pushed) {
sockets_to_check.push(input_socket);
pushed = true;
@@ -1576,11 +1538,11 @@ class NodeTreeMainUpdater {
}
/* The Normal node has a special case, because the value stored in the first output socket
* is used as input in the node. */
- if (bnode.type == SH_NODE_NORMAL && socket.index() == 1) {
- BLI_assert(socket.name() == "Dot");
- const OutputSocketRef &normal_output = node.output(0);
- BLI_assert(normal_output.name() == "Normal");
- bool &pushed = pushed_by_socket_id[normal_output.id()];
+ if (node.type == SH_NODE_NORMAL && socket.index() == 1) {
+ BLI_assert(STREQ(socket.name, "Dot"));
+ const bNodeSocket &normal_output = node.output_socket(0);
+ BLI_assert(STREQ(normal_output.name, "Normal"));
+ bool &pushed = pushed_by_socket_id[normal_output.index_in_tree()];
if (!pushed) {
sockets_to_check.push(&normal_output);
pushed = true;
@@ -1654,6 +1616,11 @@ void BKE_ntree_update_tag_node_removed(bNodeTree *ntree)
add_tree_tag(ntree, NTREE_CHANGED_REMOVED_NODE);
}
+void BKE_ntree_update_tag_node_reordered(bNodeTree *ntree)
+{
+ add_tree_tag(ntree, NTREE_CHANGED_ANY);
+}
+
void BKE_ntree_update_tag_node_mute(bNodeTree *ntree, bNode *node)
{
add_node_tag(ntree, node, NTREE_CHANGED_NODE_PROPERTY);
diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc
index f7436b6112c..167c6db05e2 100644
--- a/source/blender/blenkernel/intern/object.cc
+++ b/source/blender/blenkernel/intern/object.cc
@@ -145,6 +145,8 @@
#include "atomic_ops.h"
using blender::float3;
+using blender::MutableSpan;
+using blender::Span;
static CLG_LogRef LOG = {"bke.object"};
@@ -186,7 +188,7 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in
/* Do not copy runtime data. */
BKE_object_runtime_reset_on_copy(ob_dst, flag);
- /* We never handle usercount here for own data. */
+ /* We never handle user-count here for own data. */
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
if (ob_src->totcol) {
@@ -243,7 +245,7 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in
BLI_listbase_clear(&ob_dst->modifiers);
BLI_listbase_clear(&ob_dst->greasepencil_modifiers);
- /* NOTE: Also takes care of softbody and particle systems copying. */
+ /* NOTE: Also takes care of soft-body and particle systems copying. */
BKE_object_modifier_stack_copy(ob_dst, ob_src, true, flag_subdata);
BLI_listbase_clear((ListBase *)&ob_dst->drawdata);
@@ -394,7 +396,7 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], IDWALK_CB_USER);
}
- /* Note that ob->gpd is deprecated, so no need to handle it here. */
+ /* Note that `ob->gpd` is deprecated, so no need to handle it here. */
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->instance_collection, IDWALK_CB_USER);
if (object->pd) {
@@ -449,7 +451,7 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
if (object->soft->effector_weights) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
- data, object->soft->effector_weights->group, IDWALK_CB_NOP);
+ data, object->soft->effector_weights->group, IDWALK_CB_USER);
}
}
}
@@ -734,9 +736,9 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id)
BLO_read_data_address(reader, &sb->shared);
if (sb->shared == nullptr) {
/* Link deprecated caches if they exist, so we can use them for versioning.
- * We should only do this when sb->shared == nullptr, because those pointers
+ * We should only do this when `sb->shared == nullptr`, because those pointers
* are always set (for compatibility with older Blenders). We mustn't link
- * the same pointcache twice. */
+ * the same point-cache twice. */
BKE_ptcache_blend_read_data(reader, &sb->ptcaches, &sb->pointcache, false);
}
else {
@@ -1139,7 +1141,7 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src)
* This code is a workaround this to check all point-caches from both source and destination
* objects in parallel, and transfer those flags when it makes sense.
*
- * This allows to keep baked caches across liboverrides applies.
+ * This allows to keep baked caches across lib-overrides applies.
*
* NOTE: This is fairly hackish and weak, but so is the point-cache system as its whole. A more
* robust solution would be e.g. to have a specific RNA entry point to deal with such cases
@@ -1168,7 +1170,7 @@ static void object_lib_override_apply_post(ID *id_dst, ID *id_src)
point_cache_dst != nullptr;
point_cache_dst = point_cache_dst->next,
point_cache_src = (point_cache_src != nullptr) ? point_cache_src->next : nullptr) {
- /* Always force updating info about caches of applied liboverrides. */
+ /* Always force updating info about caches of applied lib-overrides. */
point_cache_dst->flag |= PTCACHE_FLAG_INFO_DIRTY;
if (point_cache_src == nullptr || !STREQ(point_cache_dst->name, point_cache_src->name)) {
continue;
@@ -1237,7 +1239,7 @@ IDTypeInfo IDType_ID_OB = {
/* foreach_id */ object_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ object_foreach_path,
- /* owner_get */ nullptr,
+ /* owner_pointer_get */ nullptr,
/* blend_write */ object_blend_write,
/* blend_read_data */ object_blend_read_data,
@@ -1298,10 +1300,10 @@ void BKE_object_free_modifiers(Object *ob, const int flag)
while ((gp_md = (GpencilModifierData *)BLI_pophead(&ob->greasepencil_modifiers))) {
BKE_gpencil_modifier_free_ex(gp_md, flag);
}
- /* particle modifiers were freed, so free the particlesystems as well */
+ /* Particle modifiers were freed, so free the particle-systems as well. */
BKE_object_free_particlesystems(ob);
- /* same for softbody */
+ /* Same for soft-body */
BKE_object_free_softbody(ob);
/* modifiers may have stored data in the DM cache */
@@ -1402,6 +1404,7 @@ bool BKE_object_supports_modifiers(const Object *ob)
{
return (ELEM(ob->type,
OB_MESH,
+ OB_CURVES,
OB_CURVES_LEGACY,
OB_SURF,
OB_FONT,
@@ -1445,11 +1448,11 @@ static bool object_modifier_type_copy_check(ModifierType md_type)
}
/**
- * 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
+ * modifier using the `psys` is actually copied, and that this copied modifier will be added at the
* end of the stack. That way we can be sure that the particle modifier will be before the one
* using its particle system in the stack.
*/
@@ -1664,7 +1667,7 @@ static void copy_ccg_data(Mesh *mesh_destination, Mesh *mesh_source, int layer_t
const int layer_index = CustomData_get_layer_index(data_destination, layer_type);
CustomData_free_layer(data_destination, layer_type, num_elements, layer_index);
BLI_assert(!CustomData_has_layer(data_destination, layer_type));
- CustomData_add_layer(data_destination, layer_type, CD_CALLOC, nullptr, num_elements);
+ CustomData_add_layer(data_destination, layer_type, CD_SET_DEFAULT, nullptr, num_elements);
BLI_assert(CustomData_has_layer(data_destination, layer_type));
CustomData_copy_layer_type_data(data_source, data_destination, layer_type, 0, 0, num_elements);
}
@@ -2037,7 +2040,7 @@ bool BKE_object_is_mode_compat(const struct Object *ob, eObjectMode object_mode)
int BKE_object_visibility(const Object *ob, const int dag_eval_mode)
{
- if ((ob->base_flag & BASE_VISIBLE_DEPSGRAPH) == 0) {
+ if ((ob->base_flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0) {
return 0;
}
@@ -2255,26 +2258,29 @@ Object *BKE_object_add_only_object(Main *bmain, int type, const char *name)
return ob;
}
-static Object *object_add_common(Main *bmain, ViewLayer *view_layer, int type, const char *name)
+static Object *object_add_common(
+ Main *bmain, const Scene *scene, ViewLayer *view_layer, int type, const char *name)
{
Object *ob = BKE_object_add_only_object(bmain, type, name);
ob->data = BKE_object_obdata_add_from_type(bmain, type, name);
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
DEG_id_tag_update_ex(
bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
return ob;
}
-Object *BKE_object_add(Main *bmain, ViewLayer *view_layer, int type, const char *name)
+Object *BKE_object_add(
+ Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name)
{
- Object *ob = object_add_common(bmain, view_layer, type, name);
+ Object *ob = object_add_common(bmain, scene, view_layer, type, name);
LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer);
BKE_collection_viewlayer_object_add(bmain, view_layer, layer_collection->collection, ob);
/* NOTE: There is no way to be sure that #BKE_collection_viewlayer_object_add will actually
* manage to find a valid collection in given `view_layer` to add the new object to. */
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base != nullptr) {
BKE_view_layer_base_select_and_set_active(view_layer, base);
@@ -2286,17 +2292,23 @@ Object *BKE_object_add(Main *bmain, ViewLayer *view_layer, int type, const char
Object *BKE_object_add_from(
Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name, Object *ob_src)
{
- Object *ob = object_add_common(bmain, view_layer, type, name);
+ Object *ob = object_add_common(bmain, scene, view_layer, type, name);
BKE_collection_object_add_from(bmain, scene, ob_src, ob);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
BKE_view_layer_base_select_and_set_active(view_layer, base);
return ob;
}
-Object *BKE_object_add_for_data(
- Main *bmain, ViewLayer *view_layer, int type, const char *name, ID *data, bool do_id_user)
+Object *BKE_object_add_for_data(Main *bmain,
+ const Scene *scene,
+ ViewLayer *view_layer,
+ int type,
+ const char *name,
+ ID *data,
+ bool do_id_user)
{
/* same as object_add_common, except we don't create new ob->data */
Object *ob = BKE_object_add_only_object(bmain, type, name);
@@ -2305,13 +2317,14 @@ Object *BKE_object_add_for_data(
id_us_plus(data);
}
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
DEG_id_tag_update_ex(
bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer);
BKE_collection_object_add(bmain, layer_collection->collection, ob);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
BKE_view_layer_base_select_and_set_active(view_layer, base);
@@ -2413,7 +2426,7 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f
}
/* XXX(@campbellbarton): from reading existing code this seems correct but intended usage of
- * point-cache should /w cloth should be added in 'ParticleSystem'. */
+ * point-cache should with cloth should be added in 'ParticleSystem'. */
if (psysn->clmd) {
psysn->clmd->point_cache = psysn->pointcache;
}
@@ -2471,8 +2484,8 @@ void BKE_object_copy_particlesystems(Object *ob_dst, const Object *ob_src, const
static void copy_object_pose(Object *obn, const Object *ob, const int flag)
{
- /* NOTE: need to clear obn->pose pointer first,
- * so that BKE_pose_copy_data works (otherwise there's a crash) */
+ /* NOTE: need to clear `obn->pose` pointer first,
+ * so that #BKE_pose_copy_data works (otherwise there's a crash) */
obn->pose = nullptr;
BKE_pose_copy_data_ex(&obn->pose, ob->pose, flag, true); /* true = copy constraints */
@@ -2527,10 +2540,14 @@ Object *BKE_object_pose_armature_get(Object *ob)
return nullptr;
}
-Object *BKE_object_pose_armature_get_visible(Object *ob, ViewLayer *view_layer, View3D *v3d)
+Object *BKE_object_pose_armature_get_visible(Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d)
{
Object *ob_armature = BKE_object_pose_armature_get(ob);
if (ob_armature) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob_armature);
if (base) {
if (BASE_VISIBLE(v3d, base)) {
@@ -2541,12 +2558,11 @@ Object *BKE_object_pose_armature_get_visible(Object *ob, ViewLayer *view_layer,
return nullptr;
}
-Object **BKE_object_pose_array_get_ex(ViewLayer *view_layer,
- View3D *v3d,
- uint *r_objects_len,
- bool unique)
+Object **BKE_object_pose_array_get_ex(
+ const Scene *scene, ViewLayer *view_layer, View3D *v3d, uint *r_objects_len, bool unique)
{
- Object *ob_active = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob_active = BKE_view_layer_active_object_get(view_layer);
Object *ob_pose = BKE_object_pose_armature_get(ob_active);
Object **objects = nullptr;
if (ob_pose == ob_active) {
@@ -2555,7 +2571,7 @@ Object **BKE_object_pose_array_get_ex(ViewLayer *view_layer,
ob_params.no_dup_data = unique;
objects = BKE_view_layer_array_from_objects_in_mode_params(
- view_layer, v3d, r_objects_len, &ob_params);
+ scene, view_layer, v3d, r_objects_len, &ob_params);
}
else if (ob_pose != nullptr) {
*r_objects_len = 1;
@@ -2568,21 +2584,26 @@ Object **BKE_object_pose_array_get_ex(ViewLayer *view_layer,
}
return objects;
}
-Object **BKE_object_pose_array_get_unique(ViewLayer *view_layer, View3D *v3d, uint *r_objects_len)
+Object **BKE_object_pose_array_get_unique(const Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ uint *r_objects_len)
{
- return BKE_object_pose_array_get_ex(view_layer, v3d, r_objects_len, true);
+ return BKE_object_pose_array_get_ex(scene, view_layer, v3d, r_objects_len, true);
}
-Object **BKE_object_pose_array_get(ViewLayer *view_layer, View3D *v3d, uint *r_objects_len)
+Object **BKE_object_pose_array_get(const Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ uint *r_objects_len)
{
- return BKE_object_pose_array_get_ex(view_layer, v3d, r_objects_len, false);
+ return BKE_object_pose_array_get_ex(scene, view_layer, v3d, r_objects_len, false);
}
-Base **BKE_object_pose_base_array_get_ex(ViewLayer *view_layer,
- View3D *v3d,
- uint *r_bases_len,
- bool unique)
+Base **BKE_object_pose_base_array_get_ex(
+ const Scene *scene, ViewLayer *view_layer, View3D *v3d, uint *r_bases_len, bool unique)
{
- Base *base_active = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base_active = BKE_view_layer_active_base_get(view_layer);
Object *ob_pose = base_active ? BKE_object_pose_armature_get(base_active->object) : nullptr;
Base *base_pose = nullptr;
Base **bases = nullptr;
@@ -2602,7 +2623,7 @@ Base **BKE_object_pose_base_array_get_ex(ViewLayer *view_layer,
ob_params.no_dup_data = unique;
bases = BKE_view_layer_array_from_bases_in_mode_params(
- view_layer, v3d, r_bases_len, &ob_params);
+ scene, view_layer, v3d, r_bases_len, &ob_params);
}
else if (base_pose != nullptr) {
*r_bases_len = 1;
@@ -2615,13 +2636,19 @@ Base **BKE_object_pose_base_array_get_ex(ViewLayer *view_layer,
}
return bases;
}
-Base **BKE_object_pose_base_array_get_unique(ViewLayer *view_layer, View3D *v3d, uint *r_bases_len)
+Base **BKE_object_pose_base_array_get_unique(const Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ uint *r_bases_len)
{
- return BKE_object_pose_base_array_get_ex(view_layer, v3d, r_bases_len, true);
+ return BKE_object_pose_base_array_get_ex(scene, view_layer, v3d, r_bases_len, true);
}
-Base **BKE_object_pose_base_array_get(ViewLayer *view_layer, View3D *v3d, uint *r_bases_len)
+Base **BKE_object_pose_base_array_get(const Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ uint *r_bases_len)
{
- return BKE_object_pose_base_array_get_ex(view_layer, v3d, r_bases_len, false);
+ return BKE_object_pose_base_array_get_ex(scene, view_layer, v3d, r_bases_len, false);
}
void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src)
@@ -2889,7 +2916,7 @@ void BKE_object_rot_to_mat3(const Object *ob, float mat[3][3], bool use_drot)
axis_angle_to_mat3(dmat, ob->drotAxis, ob->drotAngle);
}
else {
- /* quats are normalized before use to eliminate scaling issues */
+ /* Quaternions are normalized before use to eliminate scaling issues. */
float tquat[4];
normalize_qt_qt(tquat, ob->quat);
@@ -2925,7 +2952,7 @@ void BKE_object_mat3_to_rot(Object *ob, float mat[3][3], bool use_compat)
float quat[4];
float dquat[4];
- /* without drot we could apply 'mat' directly */
+ /* Without `drot` we could apply 'mat' directly. */
mat3_normalized_to_quat(quat, mat);
axis_angle_to_quat(dquat, ob->drotAxis, ob->drotAngle);
invert_qt_normalized(dquat);
@@ -2938,12 +2965,12 @@ void BKE_object_mat3_to_rot(Object *ob, float mat[3][3], bool use_compat)
float quat[4];
float dquat[4];
- /* without drot we could apply 'mat' directly */
+ /* Without `drot` we could apply 'mat' directly. */
mat3_normalized_to_quat(quat, mat);
eulO_to_quat(dquat, ob->drot, ob->rotmode);
invert_qt_normalized(dquat);
mul_qt_qtqt(quat, dquat, quat);
- /* end drot correction */
+ /* End `drot` correction. */
if (use_compat) {
quat_to_compatible_eulO(ob->rot, ob->rot, ob->rotmode, quat);
@@ -3104,12 +3131,11 @@ static bool ob_parcurve(Object *ob, Object *par, float r_mat[4][4])
return false;
}
- /* ctime is now a proper var setting of Curve which gets set by Animato like any other var
+ /* `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 curve-time 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, which then gets clamped to lie within 0.0 - 1.0 range. */
if (cu->pathlen) {
ctime = cu->ctime / cu->pathlen;
}
@@ -3188,6 +3214,7 @@ static void give_parvert(Object *par, int nr, float vec[3])
BKE_object_get_evaluated_mesh(par);
if (me_eval) {
+ const MVert *verts = BKE_mesh_verts(me_eval);
int count = 0;
int numVerts = me_eval->totvert;
@@ -3221,14 +3248,14 @@ static void give_parvert(Object *par, int nr, float vec[3])
/* Get the average of all verts with (original index == nr). */
for (int i = 0; i < numVerts; i++) {
if (index[i] == nr) {
- add_v3_v3(vec, me_eval->mvert[i].co);
+ add_v3_v3(vec, verts[i].co);
count++;
}
}
}
else {
if (nr < numVerts) {
- add_v3_v3(vec, me_eval->mvert[nr].co);
+ add_v3_v3(vec, verts[nr].co);
count++;
}
}
@@ -3242,7 +3269,7 @@ static void give_parvert(Object *par, int nr, float vec[3])
else {
/* use first index if its out of range */
if (me_eval->totvert) {
- copy_v3_v3(vec, me_eval->mvert[0].co);
+ copy_v3_v3(vec, verts[0].co);
}
}
}
@@ -3387,7 +3414,7 @@ static void solve_parenting(
mul_m4_m4m4(r_obmat, tmat, locmat);
if (r_originmat) {
- /* usable originmat */
+ /* Usable `r_originmat`. */
copy_m3_m4(r_originmat, tmat);
}
@@ -4068,7 +4095,7 @@ bool BKE_object_minmax_dupli(Depsgraph *depsgraph,
}
else {
Object temp_ob = blender::dna::shallow_copy(*dob->ob);
- /* Do not modify the original boundbox. */
+ /* Do not modify the original bounding-box. */
temp_ob.runtime.bb = nullptr;
BKE_object_replace_data_on_shallow_copy(&temp_ob, dob->ob_data);
const BoundBox *bb = BKE_object_boundbox_get(&temp_ob);
@@ -4121,15 +4148,15 @@ void BKE_object_foreach_display_point(Object *ob,
void (*func_cb)(const float[3], void *),
void *user_data)
{
- /* TODO: pointcloud and curves object support */
+ /* TODO: point-cloud and curves object support. */
const Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
float3 co;
if (mesh_eval != nullptr) {
- const MVert *mv = mesh_eval->mvert;
+ const MVert *verts = BKE_mesh_verts(mesh_eval);
const int totvert = mesh_eval->totvert;
- for (int i = 0; i < totvert; i++, mv++) {
- mul_v3_m4v3(co, obmat, mv->co);
+ for (int i = 0; i < totvert; i++) {
+ mul_v3_m4v3(co, obmat, verts[i].co);
func_cb(co, user_data);
}
}
@@ -4270,7 +4297,7 @@ void BKE_object_handle_update_ex(Depsgraph *depsgraph,
* is evaluated on the rebuilt pose, otherwise we get incorrect poses
* on file load */
if (ob->pose == nullptr || (ob->pose->flag & POSE_RECALC)) {
- /* No need to pass bmain here, we assume we do not need to rebuild DEG from here... */
+ /* No need to pass `bmain` here, we assume we do not need to rebuild DEG from here. */
BKE_pose_rebuild(nullptr, ob, (bArmature *)ob->data, true);
}
}
@@ -4753,7 +4780,8 @@ bool BKE_object_shapekey_remove(Main *bmain, Object *ob, KeyBlock *kb)
switch (ob->type) {
case OB_MESH: {
Mesh *mesh = (Mesh *)ob->data;
- BKE_keyblock_convert_to_mesh(key->refkey, mesh->mvert, mesh->totvert);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ BKE_keyblock_convert_to_mesh(key->refkey, verts.data(), mesh->totvert);
break;
}
case OB_CURVES_LEGACY:
@@ -5131,19 +5159,21 @@ static void obrel_list_add(LinkNode **links, Object *ob)
ob->id.tag |= LIB_TAG_DOIT;
}
-LinkNode *BKE_object_relational_superset(struct ViewLayer *view_layer,
+LinkNode *BKE_object_relational_superset(const Scene *scene,
+ struct ViewLayer *view_layer,
eObjectSet objectSet,
eObRelationTypes includeFilter)
{
LinkNode *links = nullptr;
/* Remove markers from all objects */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->object->id.tag &= ~LIB_TAG_DOIT;
}
/* iterate over all selected and visible objects */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (objectSet == OB_SET_ALL) {
/* as we get all anyways just add it */
Object *ob = base->object;
@@ -5179,7 +5209,7 @@ LinkNode *BKE_object_relational_superset(struct ViewLayer *view_layer,
/* child relationship */
if (includeFilter & (OB_REL_CHILDREN | OB_REL_CHILDREN_RECURSIVE)) {
- LISTBASE_FOREACH (Base *, local_base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, local_base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_EDITABLE(((View3D *)nullptr), local_base)) {
Object *child = local_base->object;
@@ -5249,32 +5279,31 @@ KDTree_3d *BKE_object_as_kdtree(Object *ob, int *r_tot)
const int *index;
if (me_eval && (index = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX))) {
- MVert *mvert = me_eval->mvert;
- uint totvert = me_eval->totvert;
+ const Span<MVert> verts = me->verts();
/* Tree over-allocates in case where some verts have #ORIGINDEX_NONE. */
tot = 0;
- tree = BLI_kdtree_3d_new(totvert);
+ tree = BLI_kdtree_3d_new(verts.size());
/* We don't how many verts from the DM we can use. */
- for (i = 0; i < totvert; i++) {
+ for (i = 0; i < verts.size(); i++) {
if (index[i] != ORIGINDEX_NONE) {
float co[3];
- mul_v3_m4v3(co, ob->obmat, mvert[i].co);
+ mul_v3_m4v3(co, ob->obmat, verts[i].co);
BLI_kdtree_3d_insert(tree, index[i], co);
tot++;
}
}
}
else {
- MVert *mvert = me->mvert;
+ const Span<MVert> verts = me->verts();
- tot = me->totvert;
+ tot = verts.size();
tree = BLI_kdtree_3d_new(tot);
for (i = 0; i < tot; i++) {
float co[3];
- mul_v3_m4v3(co, ob->obmat, mvert[i].co);
+ mul_v3_m4v3(co, ob->obmat, verts[i].co);
BLI_kdtree_3d_insert(tree, i, co);
}
}
@@ -5419,8 +5448,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
depsgraph, scene, ob->track, false, recursion, frame, type);
}
- /* skip subframe if object is parented
- * to vertex of a dynamic paint canvas */
+ /* Skip sub-frame if object is parented to vertex of a dynamic paint canvas. */
if (no_update && (ELEM(ob->partype, PARVERT1, PARVERT3))) {
return false;
}
@@ -5451,8 +5479,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
if (update_mesh) {
BKE_animsys_evaluate_animdata(
&ob->id, ob->adt, &anim_eval_context, ADT_RECALC_ANIM, flush_to_original);
- /* ignore cache clear during subframe updates
- * to not mess up cache validity */
+ /* Ignore cache clear during sub-frame updates to not mess up cache validity. */
object_cacheIgnoreClear(ob, 1);
BKE_object_handle_update(depsgraph, scene, ob);
object_cacheIgnoreClear(ob, 0);
diff --git a/source/blender/blenkernel/intern/object_deform.c b/source/blender/blenkernel/intern/object_deform.c
index 310ec7678bd..1fe5d7aa0e7 100644
--- a/source/blender/blenkernel/intern/object_deform.c
+++ b/source/blender/blenkernel/intern/object_deform.c
@@ -114,9 +114,7 @@ bDeformGroup *BKE_object_defgroup_add(Object *ob)
MDeformVert *BKE_object_defgroup_data_create(ID *id)
{
if (GS(id->name) == ID_ME) {
- Mesh *me = (Mesh *)id;
- me->dvert = CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, me->totvert);
- return me->dvert;
+ return BKE_mesh_deform_verts_for_write((Mesh *)id);
}
if (GS(id->name) == ID_LT) {
Lattice *lt = (Lattice *)id;
@@ -164,12 +162,12 @@ bool BKE_object_defgroup_clear(Object *ob, bDeformGroup *dg, const bool use_sele
}
}
else {
- if (me->dvert) {
- MVert *mv;
+ if (BKE_mesh_deform_verts(me)) {
+ const MVert *mv;
int i;
- mv = me->mvert;
- dv = me->dvert;
+ mv = BKE_mesh_verts(me);
+ dv = BKE_mesh_deform_verts_for_write(me);
for (i = 0; i < me->totvert; i++, mv++, dv++) {
if (dv->dw && (!use_selection || (mv->flag & SELECT))) {
@@ -264,7 +262,6 @@ static void object_defgroup_remove_common(Object *ob, bDeformGroup *dg, const in
if (ob->type == OB_MESH) {
Mesh *me = ob->data;
CustomData_free_layer_active(&me->vdata, CD_MDEFORMVERT, me->totvert);
- me->dvert = NULL;
}
else if (ob->type == OB_LATTICE) {
Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data));
@@ -412,7 +409,6 @@ void BKE_object_defgroup_remove_all_ex(struct Object *ob, bool only_unlocked)
if (ob->type == OB_MESH) {
Mesh *me = ob->data;
CustomData_free_layer_active(&me->vdata, CD_MDEFORMVERT, me->totvert);
- me->dvert = NULL;
}
else if (ob->type == OB_LATTICE) {
Lattice *lt = object_defgroup_lattice_get((ID *)(ob->data));
@@ -501,7 +497,7 @@ bool BKE_object_defgroup_array_get(ID *id, MDeformVert **dvert_arr, int *dvert_t
switch (GS(id->name)) {
case ID_ME: {
Mesh *me = (Mesh *)id;
- *dvert_arr = me->dvert;
+ *dvert_arr = BKE_mesh_deform_verts_for_write(me);
*dvert_tot = me->totvert;
return true;
}
diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc
index 407a2c8955c..6db1c864918 100644
--- a/source/blender/blenkernel/intern/object_dupli.cc
+++ b/source/blender/blenkernel/intern/object_dupli.cc
@@ -29,6 +29,7 @@
#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_vfont_types.h"
+#include "DNA_volume_types.h"
#include "BKE_collection.h"
#include "BKE_duplilist.h"
@@ -164,10 +165,8 @@ static bool copy_dupli_context(
*
* \param mat: is transform of the object relative to current context (including #Object.obmat).
*/
-static DupliObject *make_dupli(const DupliContext *ctx,
- Object *ob,
- const float mat[4][4],
- int index)
+static DupliObject *make_dupli(
+ const DupliContext *ctx, Object *ob, const ID *object_data, const float mat[4][4], int index)
{
DupliObject *dob;
int i;
@@ -182,7 +181,7 @@ static DupliObject *make_dupli(const DupliContext *ctx,
}
dob->ob = ob;
- dob->ob_data = (ID *)ob->data;
+ dob->ob_data = const_cast<ID *>(object_data);
mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat);
dob->type = ctx->gen->type;
@@ -202,7 +201,7 @@ static DupliObject *make_dupli(const DupliContext *ctx,
/* Meta-balls never draw in duplis, they are instead merged into one by the basis
* meta-ball outside of the group. this does mean that if that meta-ball is not in the
* scene, they will not show up at all, limitation that should be solved once. */
- if (ob->type == OB_MBALL) {
+ if (object_data && GS(object_data->name) == ID_MB) {
dob->no_draw = true;
}
@@ -226,6 +225,14 @@ static DupliObject *make_dupli(const DupliContext *ctx,
return dob;
}
+static DupliObject *make_dupli(const DupliContext *ctx,
+ Object *ob,
+ const float mat[4][4],
+ int index)
+{
+ return make_dupli(ctx, ob, static_cast<ID *>(ob->data), mat, index);
+}
+
/**
* Recursive dupli-objects.
*
@@ -621,7 +628,7 @@ static void make_duplis_verts(const DupliContext *ctx)
VertexDupliData_Mesh vdd{};
vdd.params = vdd_params;
vdd.totvert = me_eval->totvert;
- vdd.mvert = me_eval->mvert;
+ vdd.mvert = me_eval->verts().data();
vdd.vert_normals = BKE_mesh_vertex_normals_ensure(me_eval);
vdd.orco = (const float(*)[3])CustomData_get_layer(&me_eval->vdata, CD_ORCO);
@@ -777,28 +784,24 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx,
int component_index = 0;
if (ctx->object->type != OB_MESH || geometry_set_is_instance) {
if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
- DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
- dupli->ob_data = (ID *)mesh;
+ make_dupli(ctx, ctx->object, &mesh->id, parent_transform, component_index++);
}
}
if (ctx->object->type != OB_VOLUME || geometry_set_is_instance) {
if (const Volume *volume = geometry_set.get_volume_for_read()) {
- DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
- dupli->ob_data = (ID *)volume;
+ make_dupli(ctx, ctx->object, &volume->id, parent_transform, component_index++);
}
}
if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT, OB_CURVES) || geometry_set_is_instance) {
if (const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>()) {
if (const Curve *curve = component->get_curve_for_render()) {
- DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
- dupli->ob_data = (ID *)curve;
+ make_dupli(ctx, ctx->object, &curve->id, parent_transform, component_index++);
}
}
}
if (ctx->object->type != OB_POINTCLOUD || geometry_set_is_instance) {
if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) {
- DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++);
- dupli->ob_data = (ID *)pointcloud;
+ make_dupli(ctx, ctx->object, &pointcloud->id, parent_transform, component_index++);
}
}
const bool creates_duplis_for_components = component_index >= 1;
@@ -1175,9 +1178,9 @@ static void make_duplis_faces(const DupliContext *ctx)
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.mpoly = me_eval->polys().data();
+ fdd.mloop = me_eval->loops().data();
+ fdd.mvert = me_eval->verts().data();
fdd.mloopuv = (uv_idx != -1) ? (const MLoopUV *)CustomData_get_layer_n(
&me_eval->ldata, CD_MLOOPUV, uv_idx) :
nullptr;
@@ -1560,6 +1563,13 @@ static const DupliGenerator *get_dupli_generator(const DupliContext *ctx)
return nullptr;
}
+ /* Metaball objects can't create instances, but the dupli system is used to "instance" their
+ * evaluated mesh to render engines. We need to exit early to avoid recursively instancing the
+ * evaluated metaball mesh on metaball instances that already contribute to the basis. */
+ if (ctx->object->type == OB_MBALL && ctx->level > 0) {
+ return nullptr;
+ }
+
/* Should the dupli's be generated for this object? - Respect restrict flags. */
if (DEG_get_mode(ctx->depsgraph) == DAG_EVAL_RENDER ? (visibility_flag & OB_HIDE_RENDER) :
(visibility_flag & OB_HIDE_VIEWPORT)) {
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 8ff02c7e698..91170060fee 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -110,7 +110,6 @@ void BKE_object_eval_constraints(Depsgraph *depsgraph, Scene *scene, Object *ob)
* - post (i.e. BKE_constraints_clear_evalob)
*
* Not sure why, this is from Joshua - sergey
- *
*/
cob = BKE_constraints_make_evalob(depsgraph, scene, ob, NULL, CONSTRAINT_OBTYPE_OBJECT);
BKE_constraints_solve(depsgraph, &ob->constraints, cob, ctime);
@@ -149,10 +148,6 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
cddata_masks.pmask |= CD_MASK_PROP_ALL;
cddata_masks.lmask |= CD_MASK_PROP_ALL;
- /* Also copy over normal layers to avoid recomputation. */
- cddata_masks.pmask |= CD_MASK_NORMAL;
- cddata_masks.vmask |= CD_MASK_NORMAL;
-
/* 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
@@ -173,7 +168,7 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
break;
case OB_MBALL:
- BKE_displist_make_mball(depsgraph, scene, ob);
+ BKE_mball_data_update(depsgraph, scene, ob);
break;
case OB_CURVES_LEGACY:
@@ -285,45 +280,45 @@ void BKE_object_eval_uber_transform(Depsgraph *UNUSED(depsgraph), Object *UNUSED
{
}
-void BKE_object_data_batch_cache_dirty_tag(ID *object_data)
+void BKE_object_batch_cache_dirty_tag(Object *ob)
{
- switch (GS(object_data->name)) {
- case ID_ME:
- BKE_mesh_batch_cache_dirty_tag((struct Mesh *)object_data, BKE_MESH_BATCH_DIRTY_ALL);
+ switch (ob->type) {
+ case OB_MESH:
+ BKE_mesh_batch_cache_dirty_tag((struct Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
break;
- case ID_LT:
- BKE_lattice_batch_cache_dirty_tag((struct Lattice *)object_data,
- BKE_LATTICE_BATCH_DIRTY_ALL);
+ case OB_LATTICE:
+ BKE_lattice_batch_cache_dirty_tag((struct Lattice *)ob->data, BKE_LATTICE_BATCH_DIRTY_ALL);
break;
- case ID_CU_LEGACY:
- BKE_curve_batch_cache_dirty_tag((struct Curve *)object_data, BKE_CURVE_BATCH_DIRTY_ALL);
+ case OB_CURVES_LEGACY:
+ BKE_curve_batch_cache_dirty_tag((struct Curve *)ob->data, BKE_CURVE_BATCH_DIRTY_ALL);
break;
- case ID_MB:
- BKE_mball_batch_cache_dirty_tag((struct MetaBall *)object_data, BKE_MBALL_BATCH_DIRTY_ALL);
+ case OB_MBALL: {
+ /* This function is currently called on original objects, so to properly
+ * clear the actual displayed geometry, we have to tag the evaluated mesh. */
+ Mesh *mesh = BKE_object_get_evaluated_mesh_no_subsurf(ob);
+ if (mesh) {
+ BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL);
+ }
break;
- case ID_GD:
- BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)object_data);
+ }
+ case OB_GPENCIL:
+ BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)ob->data);
break;
- case ID_CV:
- BKE_curves_batch_cache_dirty_tag((struct Curves *)object_data, BKE_CURVES_BATCH_DIRTY_ALL);
+ case OB_CURVES:
+ BKE_curves_batch_cache_dirty_tag((struct Curves *)ob->data, BKE_CURVES_BATCH_DIRTY_ALL);
break;
- case ID_PT:
- BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)object_data,
+ case OB_POINTCLOUD:
+ BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)ob->data,
BKE_POINTCLOUD_BATCH_DIRTY_ALL);
break;
- case ID_VO:
- BKE_volume_batch_cache_dirty_tag((struct Volume *)object_data, BKE_VOLUME_BATCH_DIRTY_ALL);
+ case OB_VOLUME:
+ BKE_volume_batch_cache_dirty_tag((struct Volume *)ob->data, BKE_VOLUME_BATCH_DIRTY_ALL);
break;
default:
break;
}
}
-void BKE_object_batch_cache_dirty_tag(Object *ob)
-{
- BKE_object_data_batch_cache_dirty_tag(ob->data);
-}
-
void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob);
@@ -412,10 +407,10 @@ void BKE_object_eval_eval_base_flags(Depsgraph *depsgraph,
* assumed viewport visibility. Select-ability does not matter here. */
if (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER) {
if (base->flag & BASE_ENABLED_RENDER) {
- base->flag |= BASE_VISIBLE_DEPSGRAPH;
+ base->flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT;
}
else {
- base->flag &= ~BASE_VISIBLE_DEPSGRAPH;
+ base->flag &= ~BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT;
}
}
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index dec9a594938..cd1f24fee37 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -1385,9 +1385,8 @@ void BKE_ocean_bake(struct Ocean *o,
void (*update_cb)(void *, float progress, int *cancel),
void *update_cb_data)
{
- /* NOTE(campbell): some of these values remain uninitialized unless certain options
- * are enabled, take care that BKE_ocean_eval_ij() initializes a member
- * before use. */
+ /* NOTE(@campbellbarton): some of these values remain uninitialized unless certain options
+ * are enabled, take care that #BKE_ocean_eval_ij() initializes a member before use. */
OceanResult ocr;
ImageFormatData imf = {0};
@@ -1441,7 +1440,7 @@ void BKE_ocean_bake(struct Ocean *o,
rgb_to_rgba_unit_alpha(&ibuf_disp->rect_float[4 * (res_x * y + x)], ocr.disp);
if (o->_do_jacobian) {
- /* TODO(campbell): cleanup unused code. */
+ /* TODO(@campbellbarton): cleanup unused code. */
float /* r, */ /* UNUSED */ pr = 0.0f, foam_result;
float neg_disp, neg_eplus;
diff --git a/source/blender/blenkernel/intern/outliner_treehash.c b/source/blender/blenkernel/intern/outliner_treehash.c
deleted file mode 100644
index 03c327bec2f..00000000000
--- a/source/blender/blenkernel/intern/outliner_treehash.c
+++ /dev/null
@@ -1,238 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-/** \file
- * \ingroup bke
- *
- * Tree hash for the outliner space.
- */
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "BLI_ghash.h"
-#include "BLI_mempool.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_outliner_types.h"
-
-#include "BKE_outliner_treehash.h"
-
-#include "MEM_guardedalloc.h"
-
-typedef struct TseGroup {
- TreeStoreElem **elems;
- int lastused;
- int size;
- int allocated;
-} TseGroup;
-
-/* Allocate structure for TreeStoreElements;
- * Most of elements in treestore have no duplicates,
- * so there is no need to preallocate memory for more than one pointer */
-static TseGroup *tse_group_create(void)
-{
- TseGroup *tse_group = MEM_mallocN(sizeof(TseGroup), "TseGroup");
- tse_group->elems = MEM_mallocN(sizeof(TreeStoreElem *), "TseGroupElems");
- tse_group->size = 0;
- tse_group->allocated = 1;
- tse_group->lastused = 0;
- return tse_group;
-}
-
-static void tse_group_add_element(TseGroup *tse_group, TreeStoreElem *elem)
-{
- if (UNLIKELY(tse_group->size == tse_group->allocated)) {
- tse_group->allocated *= 2;
- tse_group->elems = MEM_reallocN(tse_group->elems,
- sizeof(TreeStoreElem *) * tse_group->allocated);
- }
- tse_group->elems[tse_group->size] = elem;
- tse_group->size++;
-}
-
-static void tse_group_remove_element(TseGroup *tse_group, TreeStoreElem *elem)
-{
- int min_allocated = MAX2(1, tse_group->allocated / 2);
- BLI_assert(tse_group->allocated == 1 || (tse_group->allocated % 2) == 0);
-
- tse_group->size--;
- BLI_assert(tse_group->size >= 0);
- for (int i = 0; i < tse_group->size; i++) {
- if (tse_group->elems[i] == elem) {
- memcpy(tse_group->elems[i],
- tse_group->elems[i + 1],
- (tse_group->size - (i + 1)) * sizeof(TreeStoreElem *));
- break;
- }
- }
-
- if (UNLIKELY(tse_group->size > 0 && tse_group->size <= min_allocated)) {
- tse_group->allocated = min_allocated;
- tse_group->elems = MEM_reallocN(tse_group->elems,
- sizeof(TreeStoreElem *) * tse_group->allocated);
- }
-}
-
-static void tse_group_free(TseGroup *tse_group)
-{
- MEM_freeN(tse_group->elems);
- MEM_freeN(tse_group);
-}
-
-static unsigned int tse_hash(const void *ptr)
-{
- const TreeStoreElem *tse = ptr;
- union {
- short h_pair[2];
- unsigned int u_int;
- } hash;
-
- BLI_assert((tse->type != TSE_SOME_ID) || !tse->nr);
-
- hash.h_pair[0] = tse->type;
- hash.h_pair[1] = tse->nr;
-
- hash.u_int ^= BLI_ghashutil_ptrhash(tse->id);
-
- return hash.u_int;
-}
-
-static bool tse_cmp(const void *a, const void *b)
-{
- const TreeStoreElem *tse_a = a;
- const TreeStoreElem *tse_b = b;
- return tse_a->type != tse_b->type || tse_a->nr != tse_b->nr || tse_a->id != tse_b->id;
-}
-
-static void fill_treehash(void *treehash, BLI_mempool *treestore)
-{
- TreeStoreElem *tselem;
- BLI_mempool_iter iter;
- BLI_mempool_iternew(treestore, &iter);
-
- BLI_assert(treehash);
-
- while ((tselem = BLI_mempool_iterstep(&iter))) {
- BKE_outliner_treehash_add_element(treehash, tselem);
- }
-}
-
-void *BKE_outliner_treehash_create_from_treestore(BLI_mempool *treestore)
-{
- GHash *treehash = BLI_ghash_new_ex(tse_hash, tse_cmp, "treehash", BLI_mempool_len(treestore));
- fill_treehash(treehash, treestore);
- return treehash;
-}
-
-static void free_treehash_group(void *key)
-{
- tse_group_free(key);
-}
-
-void BKE_outliner_treehash_clear_used(void *treehash)
-{
- GHashIterator gh_iter;
-
- GHASH_ITER (gh_iter, treehash) {
- TseGroup *group = BLI_ghashIterator_getValue(&gh_iter);
- group->lastused = 0;
- }
-}
-
-void *BKE_outliner_treehash_rebuild_from_treestore(void *treehash, BLI_mempool *treestore)
-{
- BLI_assert(treehash);
-
- BLI_ghash_clear_ex(treehash, NULL, free_treehash_group, BLI_mempool_len(treestore));
- fill_treehash(treehash, treestore);
- return treehash;
-}
-
-void BKE_outliner_treehash_add_element(void *treehash, TreeStoreElem *elem)
-{
- TseGroup *group;
- void **val_p;
-
- if (!BLI_ghash_ensure_p(treehash, elem, &val_p)) {
- *val_p = tse_group_create();
- }
- group = *val_p;
- group->lastused = 0;
- tse_group_add_element(group, elem);
-}
-
-void BKE_outliner_treehash_remove_element(void *treehash, TreeStoreElem *elem)
-{
- TseGroup *group = BLI_ghash_lookup(treehash, elem);
-
- BLI_assert(group != NULL);
- if (group->size <= 1) {
- /* one element -> remove group completely */
- BLI_ghash_remove(treehash, elem, NULL, free_treehash_group);
- }
- else {
- tse_group_remove_element(group, elem);
- }
-}
-
-static TseGroup *BKE_outliner_treehash_lookup_group(GHash *th, short type, short nr, struct ID *id)
-{
- TreeStoreElem tse_template;
- tse_template.type = type;
- tse_template.nr = (type == TSE_SOME_ID) ? 0 : nr; /* we're picky! :) */
- tse_template.id = id;
-
- BLI_assert(th);
-
- return BLI_ghash_lookup(th, &tse_template);
-}
-
-TreeStoreElem *BKE_outliner_treehash_lookup_unused(void *treehash,
- short type,
- short nr,
- struct ID *id)
-{
- TseGroup *group;
-
- BLI_assert(treehash);
-
- group = BKE_outliner_treehash_lookup_group(treehash, type, nr, id);
- if (group) {
- /* Find unused element, with optimization to start from previously
- * found element assuming we do repeated lookups. */
- int size = group->size;
- int offset = group->lastused;
-
- for (int i = 0; i < size; i++, offset++) {
- if (offset >= size) {
- offset = 0;
- }
-
- if (!group->elems[offset]->used) {
- group->lastused = offset;
- return group->elems[offset];
- }
- }
- }
- return NULL;
-}
-
-TreeStoreElem *BKE_outliner_treehash_lookup_any(void *treehash,
- short type,
- short nr,
- struct ID *id)
-{
- TseGroup *group;
-
- BLI_assert(treehash);
-
- group = BKE_outliner_treehash_lookup_group(treehash, type, nr, id);
- return group ? group->elems[0] : NULL;
-}
-
-void BKE_outliner_treehash_free(void *treehash)
-{
- BLI_assert(treehash);
-
- BLI_ghash_free(treehash, NULL, free_treehash_group);
-}
diff --git a/source/blender/blenkernel/intern/outliner_treehash.cc b/source/blender/blenkernel/intern/outliner_treehash.cc
new file mode 100644
index 00000000000..3f66f6bb745
--- /dev/null
+++ b/source/blender/blenkernel/intern/outliner_treehash.cc
@@ -0,0 +1,209 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bke
+ *
+ * Tree hash for the outliner space.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "BLI_mempool.h"
+#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
+
+#include "DNA_outliner_types.h"
+
+#include "BKE_outliner_treehash.hh"
+
+#include "MEM_guardedalloc.h"
+
+namespace blender::bke::outliner::treehash {
+
+/* -------------------------------------------------------------------- */
+/** \name #TseGroup
+ * \{ */
+
+class TseGroup {
+ public:
+ blender::Vector<TreeStoreElem *> elems;
+ /* Index of last used #TreeStoreElem item, to speed up search for another one. */
+ int lastused = 0;
+ /* Counter used to reduce the amount of 'rests' of `lastused` index, otherwise search for unused
+ * item is exponential and becomes critically slow when there are a lot of items in the group. */
+ int lastused_reset_count = -1;
+
+ public:
+ void add_element(TreeStoreElem &elem);
+ void remove_element(TreeStoreElem &elem);
+};
+
+/* Only allow reset of #TseGroup.lastused counter to 0 once every 1k search. */
+#define TSEGROUP_LASTUSED_RESET_VALUE 10000
+
+void TseGroup::add_element(TreeStoreElem &elem)
+{
+ const int64_t idx = elems.append_and_get_index(&elem);
+ lastused = idx;
+}
+
+void TseGroup::remove_element(TreeStoreElem &elem)
+{
+ const int64_t idx = elems.first_index_of(&elem);
+ elems.remove(idx);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #TreeStoreElemKey
+ * \{ */
+
+TreeStoreElemKey::TreeStoreElemKey(const TreeStoreElem &elem)
+ : id(elem.id), type(elem.type), nr(elem.nr)
+{
+}
+
+TreeStoreElemKey::TreeStoreElemKey(ID *id, short type, short nr) : id(id), type(type), nr(nr)
+{
+}
+
+uint64_t TreeStoreElemKey::hash() const
+{
+ return get_default_hash_3(id, type, nr);
+}
+
+bool operator==(const TreeStoreElemKey &a, const TreeStoreElemKey &b)
+{
+ return (a.id == b.id) && (a.type == b.type) && (a.nr == b.nr);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #TreeHash
+ * \{ */
+
+TreeHash::~TreeHash() = default;
+
+std::unique_ptr<TreeHash> TreeHash::create_from_treestore(BLI_mempool &treestore)
+{
+ /* Can't use `make_unique()` here because of private constructor. */
+ std::unique_ptr<TreeHash> tree_hash{new TreeHash()};
+ tree_hash->fill_treehash(treestore);
+
+ return tree_hash;
+}
+
+void TreeHash::fill_treehash(BLI_mempool &treestore)
+{
+ TreeStoreElem *tselem;
+ BLI_mempool_iter iter;
+ BLI_mempool_iternew(&treestore, &iter);
+
+ while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
+ add_element(*tselem);
+ }
+}
+
+void TreeHash::clear_used()
+{
+ for (auto &group : elem_groups_.values()) {
+ group->lastused = 0;
+ group->lastused_reset_count = 0;
+ }
+}
+
+void TreeHash::rebuild_from_treestore(BLI_mempool &treestore)
+{
+ elem_groups_.clear();
+ fill_treehash(treestore);
+}
+
+void TreeHash::add_element(TreeStoreElem &elem)
+{
+ std::unique_ptr<TseGroup> &group = elem_groups_.lookup_or_add_cb(
+ TreeStoreElemKey(elem), []() { return std::make_unique<TseGroup>(); });
+ group->add_element(elem);
+}
+
+void TreeHash::remove_element(TreeStoreElem &elem)
+{
+ TseGroup *group = lookup_group(elem);
+ BLI_assert(group != nullptr);
+
+ if (group->elems.size() <= 1) {
+ /* One element -> remove group completely. */
+ elem_groups_.remove(TreeStoreElemKey(elem));
+ }
+ else {
+ group->remove_element(elem);
+ }
+}
+
+TseGroup *TreeHash::lookup_group(const TreeStoreElemKey &key) const
+{
+ auto *group = elem_groups_.lookup_ptr(key);
+ if (group) {
+ return group->get();
+ }
+ return nullptr;
+}
+
+TseGroup *TreeHash::lookup_group(const TreeStoreElem &key_elem) const
+{
+ return lookup_group(TreeStoreElemKey(key_elem));
+}
+
+TseGroup *TreeHash::lookup_group(const short type, const short nr, ID *id) const
+{
+ TreeStoreElemKey key(id, type, nr);
+ if (type == TSE_SOME_ID) {
+ key.nr = 0; /* we're picky! :) */
+ }
+ return lookup_group(key);
+}
+
+TreeStoreElem *TreeHash::lookup_unused(const short type, const short nr, ID *id) const
+{
+ TseGroup *group = lookup_group(type, nr, id);
+ if (!group) {
+ return nullptr;
+ }
+
+ /* Find unused element, with optimization to start from previously
+ * found element assuming we do repeated lookups. */
+ const int size = group->elems.size();
+ int offset = group->lastused;
+
+ for (int i = 0; i < size; i++, offset++) {
+ /* Once at the end of the array of items, in most cases it just means that all items are
+ * used, so only check the whole array once every TSEGROUP_LASTUSED_RESET_VALUE times. */
+ if (offset >= size) {
+ if (LIKELY(group->lastused_reset_count <= TSEGROUP_LASTUSED_RESET_VALUE)) {
+ group->lastused_reset_count++;
+ group->lastused = group->elems.size() - 1;
+ break;
+ }
+ group->lastused_reset_count = 0;
+ offset = 0;
+ }
+
+ if (!group->elems[offset]->used) {
+ group->lastused = offset;
+ return group->elems[offset];
+ }
+ }
+ return nullptr;
+}
+
+TreeStoreElem *TreeHash::lookup_any(const short type, const short nr, ID *id) const
+{
+ const TseGroup *group = lookup_group(type, nr, id);
+ return group ? group->elems[0] : nullptr;
+}
+
+/** \} */
+
+} // namespace blender::bke::outliner::treehash
diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c
index 7c96c463339..901b42ac0b2 100644
--- a/source/blender/blenkernel/intern/packedFile.c
+++ b/source/blender/blenkernel/intern/packedFile.c
@@ -526,21 +526,27 @@ static void unpack_generate_paths(const char *name,
BLI_strncpy(tempdir, "//", sizeof(tempdir));
}
- switch (id_type) {
- case ID_VF:
- BLI_snprintf(r_relpath, relpathlen, "//fonts/%s", tempname);
- break;
- case ID_SO:
- BLI_snprintf(r_relpath, relpathlen, "//sounds/%s", tempname);
- break;
- case ID_IM:
- BLI_snprintf(r_relpath, relpathlen, "//textures/%s", tempname);
- break;
- case ID_VO:
- BLI_snprintf(r_relpath, relpathlen, "//volumes/%s", tempname);
- break;
- default:
- break;
+ {
+ const char *dir_name = NULL;
+ switch (id_type) {
+ case ID_VF:
+ dir_name = "fonts";
+ break;
+ case ID_SO:
+ dir_name = "sounds";
+ break;
+ case ID_IM:
+ dir_name = "textures";
+ break;
+ case ID_VO:
+ dir_name = "volumes";
+ break;
+ default:
+ break;
+ }
+ if (dir_name) {
+ BLI_path_join(r_relpath, relpathlen, "//", dir_name, tempname, NULL);
+ }
}
{
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.cc
index 89cc25b31e6..0181c6e7eac 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.cc
@@ -5,8 +5,8 @@
* \ingroup bke
*/
-#include <stdlib.h>
-#include <string.h>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -25,11 +25,13 @@
#include "BLI_hash.h"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
+#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
#include "BKE_brush.h"
#include "BKE_ccg.h"
#include "BKE_colortools.h"
@@ -40,6 +42,7 @@
#include "BKE_idtype.h"
#include "BKE_image.h"
#include "BKE_key.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -63,6 +66,19 @@
#include "bmesh.h"
+static void sculpt_attribute_update_refs(Object *ob);
+static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name,
+ const SculptAttributeParams *params,
+ PBVHType pbvhtype,
+ bool flat_array_for_bmesh);
+void sculptsession_bmesh_add_layers(Object *ob);
+
+using blender::MutableSpan;
+using blender::Span;
+
static void palette_init_data(ID *id)
{
Palette *palette = (Palette *)id;
@@ -95,13 +111,10 @@ static void palette_blend_write(BlendWriter *writer, ID *id, const void *id_addr
{
Palette *palette = (Palette *)id;
- PaletteColor *color;
BLO_write_id_struct(writer, Palette, id_address, &palette->id);
BKE_id_blend_write(writer, &palette->id);
- for (color = palette->colors.first; color; color = color->next) {
- BLO_write_struct(writer, PaletteColor, color);
- }
+ BLO_write_struct_list(writer, PaletteColor, &palette->colors);
}
static void palette_blend_read_data(BlendDataReader *reader, ID *id)
@@ -116,38 +129,38 @@ static void palette_undo_preserve(BlendLibReader *UNUSED(reader), ID *id_new, ID
/* NOTE: We do not care about potential internal references to self here, Palette has none. */
/* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be
* fairly delicate. */
- BKE_lib_id_swap(NULL, id_new, id_old);
+ BKE_lib_id_swap(nullptr, id_new, id_old);
SWAP(IDProperty *, id_new->properties, id_old->properties);
}
IDTypeInfo IDType_ID_PAL = {
- .id_code = ID_PAL,
- .id_filter = FILTER_ID_PAL,
- .main_listbase_index = INDEX_ID_PAL,
- .struct_size = sizeof(Palette),
- .name = "Palette",
- .name_plural = "palettes",
- .translation_context = BLT_I18NCONTEXT_ID_PALETTE,
- .flags = IDTYPE_FLAGS_NO_ANIMDATA,
- .asset_type_info = NULL,
-
- .init_data = palette_init_data,
- .copy_data = palette_copy_data,
- .free_data = palette_free_data,
- .make_local = NULL,
- .foreach_id = NULL,
- .foreach_cache = NULL,
- .foreach_path = NULL,
- .owner_get = NULL,
-
- .blend_write = palette_blend_write,
- .blend_read_data = palette_blend_read_data,
- .blend_read_lib = NULL,
- .blend_read_expand = NULL,
-
- .blend_read_undo_preserve = palette_undo_preserve,
-
- .lib_override_apply_post = NULL,
+ /* id_code */ ID_PAL,
+ /* id_filter */ FILTER_ID_PAL,
+ /* main_listbase_index */ INDEX_ID_PAL,
+ /* struct_size */ sizeof(Palette),
+ /* name */ "Palette",
+ /* name_plural */ "palettes",
+ /* translation_context */ BLT_I18NCONTEXT_ID_PALETTE,
+ /* flags */ IDTYPE_FLAGS_NO_ANIMDATA,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ palette_init_data,
+ /* copy_data */ palette_copy_data,
+ /* free_data */ palette_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ nullptr,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_pointer_get */ nullptr,
+
+ /* blend_write */ palette_blend_write,
+ /* blend_read_data */ palette_blend_read_data,
+ /* blend_read_lib */ nullptr,
+ /* blend_read_expand */ nullptr,
+
+ /* blend_read_undo_preserve */ palette_undo_preserve,
+
+ /* lib_override_apply_post */ nullptr,
};
static void paint_curve_copy_data(Main *UNUSED(bmain),
@@ -159,7 +172,8 @@ static void paint_curve_copy_data(Main *UNUSED(bmain),
const PaintCurve *paint_curve_src = (const PaintCurve *)id_src;
if (paint_curve_src->tot_points != 0) {
- paint_curve_dst->points = MEM_dupallocN(paint_curve_src->points);
+ paint_curve_dst->points = static_cast<PaintCurvePoint *>(
+ MEM_dupallocN(paint_curve_src->points));
}
}
@@ -188,41 +202,41 @@ static void paint_curve_blend_read_data(BlendDataReader *reader, ID *id)
}
IDTypeInfo IDType_ID_PC = {
- .id_code = ID_PC,
- .id_filter = FILTER_ID_PC,
- .main_listbase_index = INDEX_ID_PC,
- .struct_size = sizeof(PaintCurve),
- .name = "PaintCurve",
- .name_plural = "paint_curves",
- .translation_context = BLT_I18NCONTEXT_ID_PAINTCURVE,
- .flags = IDTYPE_FLAGS_NO_ANIMDATA,
- .asset_type_info = NULL,
-
- .init_data = NULL,
- .copy_data = paint_curve_copy_data,
- .free_data = paint_curve_free_data,
- .make_local = NULL,
- .foreach_id = NULL,
- .foreach_cache = NULL,
- .foreach_path = NULL,
- .owner_get = NULL,
-
- .blend_write = paint_curve_blend_write,
- .blend_read_data = paint_curve_blend_read_data,
- .blend_read_lib = NULL,
- .blend_read_expand = NULL,
-
- .blend_read_undo_preserve = NULL,
-
- .lib_override_apply_post = NULL,
+ /* id_code */ ID_PC,
+ /* id_filter */ FILTER_ID_PC,
+ /* main_listbase_index */ INDEX_ID_PC,
+ /* struct_size */ sizeof(PaintCurve),
+ /* name */ "PaintCurve",
+ /* name_plural */ "paint_curves",
+ /* translation_context */ BLT_I18NCONTEXT_ID_PAINTCURVE,
+ /* flags */ IDTYPE_FLAGS_NO_ANIMDATA,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ nullptr,
+ /* copy_data */ paint_curve_copy_data,
+ /* free_data */ paint_curve_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ nullptr,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_pointer_get */ nullptr,
+
+ /* blend_write */ paint_curve_blend_write,
+ /* blend_read_data */ paint_curve_blend_read_data,
+ /* blend_read_lib */ nullptr,
+ /* blend_read_expand */ nullptr,
+
+ /* blend_read_undo_preserve */ nullptr,
+
+ /* lib_override_apply_post */ nullptr,
};
-const char PAINT_CURSOR_SCULPT[3] = {255, 100, 100};
-const char PAINT_CURSOR_VERTEX_PAINT[3] = {255, 255, 255};
-const char PAINT_CURSOR_WEIGHT_PAINT[3] = {200, 200, 255};
-const char PAINT_CURSOR_TEXTURE_PAINT[3] = {255, 255, 255};
+const uchar PAINT_CURSOR_SCULPT[3] = {255, 100, 100};
+const uchar PAINT_CURSOR_VERTEX_PAINT[3] = {255, 255, 255};
+const uchar PAINT_CURSOR_WEIGHT_PAINT[3] = {200, 200, 255};
+const uchar PAINT_CURSOR_TEXTURE_PAINT[3] = {255, 255, 255};
-static ePaintOverlayControlFlags overlay_flags = 0;
+static ePaintOverlayControlFlags overlay_flags = (ePaintOverlayControlFlags)0;
void BKE_paint_invalidate_overlay_tex(Scene *scene, ViewLayer *view_layer, const Tex *tex)
{
@@ -247,7 +261,7 @@ void BKE_paint_invalidate_overlay_tex(Scene *scene, ViewLayer *view_layer, const
void BKE_paint_invalidate_cursor_overlay(Scene *scene, ViewLayer *view_layer, CurveMapping *curve)
{
Paint *p = BKE_paint_get_active(scene, view_layer);
- if (p == NULL) {
+ if (p == nullptr) {
return;
}
@@ -294,10 +308,10 @@ void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag)
bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode)
{
ToolSettings *ts = sce->toolsettings;
- Paint **paint_ptr = NULL;
+ Paint **paint_ptr = nullptr;
/* Some paint modes don't store paint settings as pointer, for these this can be set and
* referenced by paint_ptr. */
- Paint *paint_tmp = NULL;
+ Paint *paint_tmp = nullptr;
switch (mode) {
case PAINT_MODE_SCULPT:
@@ -370,13 +384,13 @@ Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode)
case PAINT_MODE_SCULPT_CURVES:
return &ts->curves_sculpt->paint;
case PAINT_MODE_INVALID:
- return NULL;
+ return nullptr;
default:
return &ts->imapaint.paint;
}
}
- return NULL;
+ return nullptr;
}
const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode)
@@ -406,7 +420,7 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode)
case PAINT_MODE_INVALID:
break;
}
- return NULL;
+ return nullptr;
}
const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode)
@@ -438,16 +452,18 @@ const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode)
}
/* Invalid paint mode. */
- return NULL;
+ return nullptr;
}
Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
{
if (sce && view_layer) {
ToolSettings *ts = sce->toolsettings;
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ Object *actob = BKE_view_layer_active_object_get(view_layer);
- if (view_layer->basact && view_layer->basact->object) {
- switch (view_layer->basact->object->mode) {
+ if (actob) {
+ switch (actob->mode) {
case OB_MODE_SCULPT:
return &ts->sculpt->paint;
case OB_MODE_VERTEX_PAINT:
@@ -467,7 +483,7 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
case OB_MODE_SCULPT_CURVES:
return &ts->curves_sculpt->paint;
case OB_MODE_EDIT:
- return ts->uvsculpt ? &ts->uvsculpt->paint : NULL;
+ return ts->uvsculpt ? &ts->uvsculpt->paint : nullptr;
default:
break;
}
@@ -477,7 +493,7 @@ Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
return &ts->imapaint.paint;
}
- return NULL;
+ return nullptr;
}
Paint *BKE_paint_get_active_from_context(const bContext *C)
@@ -488,13 +504,10 @@ Paint *BKE_paint_get_active_from_context(const bContext *C)
if (sce && view_layer) {
ToolSettings *ts = sce->toolsettings;
- Object *obact = NULL;
-
- if (view_layer->basact && view_layer->basact->object) {
- obact = view_layer->basact->object;
- }
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
- if ((sima = CTX_wm_space_image(C)) != NULL) {
+ if ((sima = CTX_wm_space_image(C)) != nullptr) {
if (obact && obact->mode == OB_MODE_EDIT) {
if (sima->mode == SI_MODE_PAINT) {
return &ts->imapaint.paint;
@@ -512,7 +525,7 @@ Paint *BKE_paint_get_active_from_context(const bContext *C)
}
}
- return NULL;
+ return nullptr;
}
ePaintMode BKE_paintmode_get_active_from_context(const bContext *C)
@@ -522,13 +535,10 @@ ePaintMode BKE_paintmode_get_active_from_context(const bContext *C)
SpaceImage *sima;
if (sce && view_layer) {
- Object *obact = NULL;
-
- if (view_layer->basact && view_layer->basact->object) {
- obact = view_layer->basact->object;
- }
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
- if ((sima = CTX_wm_space_image(C)) != NULL) {
+ if ((sima = CTX_wm_space_image(C)) != nullptr) {
if (obact && obact->mode == OB_MODE_EDIT) {
if (sima->mode == SI_MODE_PAINT) {
return PAINT_MODE_TEXTURE_2D;
@@ -568,7 +578,7 @@ ePaintMode BKE_paintmode_get_active_from_context(const bContext *C)
return PAINT_MODE_INVALID;
}
-ePaintMode BKE_paintmode_get_from_tool(const struct bToolRef *tref)
+ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
{
if (tref->space_type == SPACE_VIEW3D) {
switch (tref->mode) {
@@ -611,7 +621,7 @@ Brush *BKE_paint_brush(Paint *p)
const Brush *BKE_paint_brush_for_read(const Paint *p)
{
- return p ? p->brush : NULL;
+ return p ? p->brush : nullptr;
}
void BKE_paint_brush_set(Paint *p, Brush *br)
@@ -704,16 +714,13 @@ uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode)
PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name)
{
- PaintCurve *pc;
-
- pc = BKE_id_new(bmain, ID_PC, name);
-
+ PaintCurve *pc = static_cast<PaintCurve *>(BKE_id_new(bmain, ID_PC, name));
return pc;
}
Palette *BKE_paint_palette(Paint *p)
{
- return p ? p->palette : NULL;
+ return p ? p->palette : nullptr;
}
void BKE_paint_palette_set(Paint *p, Palette *palette)
@@ -763,18 +770,18 @@ void BKE_palette_clear(Palette *palette)
Palette *BKE_palette_add(Main *bmain, const char *name)
{
- Palette *palette = BKE_id_new(bmain, ID_PAL, name);
+ Palette *palette = static_cast<Palette *>(BKE_id_new(bmain, ID_PAL, name));
return palette;
}
PaletteColor *BKE_palette_color_add(Palette *palette)
{
- PaletteColor *color = MEM_callocN(sizeof(*color), "Palette Color");
+ PaletteColor *color = MEM_cnew<PaletteColor>(__func__);
BLI_addtail(&palette->colors, color);
return color;
}
-bool BKE_palette_is_empty(const struct Palette *palette)
+bool BKE_palette_is_empty(const Palette *palette)
{
return BLI_listbase_is_empty(&palette->colors);
}
@@ -782,7 +789,8 @@ bool BKE_palette_is_empty(const struct Palette *palette)
/* helper function to sort using qsort */
static int palettecolor_compare_hsv(const void *a1, const void *a2)
{
- const tPaletteColorHSV *ps1 = a1, *ps2 = a2;
+ const tPaletteColorHSV *ps1 = static_cast<const tPaletteColorHSV *>(a1);
+ const tPaletteColorHSV *ps2 = static_cast<const tPaletteColorHSV *>(a2);
/* Hue */
if (ps1->h > ps2->h) {
@@ -814,7 +822,8 @@ static int palettecolor_compare_hsv(const void *a1, const void *a2)
/* helper function to sort using qsort */
static int palettecolor_compare_svh(const void *a1, const void *a2)
{
- const tPaletteColorHSV *ps1 = a1, *ps2 = a2;
+ const tPaletteColorHSV *ps1 = static_cast<const tPaletteColorHSV *>(a1);
+ const tPaletteColorHSV *ps2 = static_cast<const tPaletteColorHSV *>(a2);
/* Saturation. */
if (ps1->s > ps2->s) {
@@ -845,7 +854,8 @@ static int palettecolor_compare_svh(const void *a1, const void *a2)
static int palettecolor_compare_vhs(const void *a1, const void *a2)
{
- const tPaletteColorHSV *ps1 = a1, *ps2 = a2;
+ const tPaletteColorHSV *ps1 = static_cast<const tPaletteColorHSV *>(a1);
+ const tPaletteColorHSV *ps2 = static_cast<const tPaletteColorHSV *>(a2);
/* Value. */
if (1.0f - ps1->v > 1.0f - ps2->v) {
@@ -876,7 +886,8 @@ static int palettecolor_compare_vhs(const void *a1, const void *a2)
static int palettecolor_compare_luminance(const void *a1, const void *a2)
{
- const tPaletteColorHSV *ps1 = a1, *ps2 = a2;
+ const tPaletteColorHSV *ps1 = static_cast<const tPaletteColorHSV *>(a1);
+ const tPaletteColorHSV *ps2 = static_cast<const tPaletteColorHSV *>(a2);
float lumi1 = (ps1->rgb[0] + ps1->rgb[1] + ps1->rgb[2]) / 3.0f;
float lumi2 = (ps2->rgb[0] + ps2->rgb[1] + ps2->rgb[2]) / 3.0f;
@@ -917,14 +928,15 @@ void BKE_palette_sort_luminance(tPaletteColorHSV *color_array, const int totcol)
bool BKE_palette_from_hash(Main *bmain, GHash *color_table, const char *name, const bool linear)
{
- tPaletteColorHSV *color_array = NULL;
- tPaletteColorHSV *col_elm = NULL;
+ tPaletteColorHSV *color_array = nullptr;
+ tPaletteColorHSV *col_elm = nullptr;
bool done = false;
const int totpal = BLI_ghash_len(color_table);
if (totpal > 0) {
- color_array = MEM_calloc_arrayN(totpal, sizeof(tPaletteColorHSV), __func__);
+ color_array = static_cast<tPaletteColorHSV *>(
+ MEM_calloc_arrayN(totpal, sizeof(tPaletteColorHSV), __func__));
/* Put all colors in an array. */
GHashIterator gh_iter;
int t = 0;
@@ -979,14 +991,14 @@ bool BKE_palette_from_hash(Main *bmain, GHash *color_table, const char *name, co
bool BKE_paint_select_face_test(Object *ob)
{
- return ((ob != NULL) && (ob->type == OB_MESH) && (ob->data != NULL) &&
+ return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) &&
(((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) &&
(ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)));
}
bool BKE_paint_select_vert_test(Object *ob)
{
- return ((ob != NULL) && (ob->type == OB_MESH) && (ob->data != NULL) &&
+ return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) &&
(((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_VERT_SEL) &&
(ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT));
}
@@ -996,10 +1008,16 @@ bool BKE_paint_select_elem_test(Object *ob)
return (BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob));
}
+bool BKE_paint_always_hide_test(Object *ob)
+{
+ return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) &&
+ (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT));
+}
+
void BKE_paint_cavity_curve_preset(Paint *p, int preset)
{
- CurveMapping *cumap = NULL;
- CurveMap *cuma = NULL;
+ CurveMapping *cumap = nullptr;
+ CurveMap *cuma = nullptr;
if (!p->cavity_curve) {
p->cavity_curve = BKE_curvemapping_add(1, 0, 0, 1, 1);
@@ -1029,13 +1047,13 @@ eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode)
return OB_MODE_EDIT;
case PAINT_MODE_INVALID:
default:
- return 0;
+ return OB_MODE_OBJECT;
}
}
-bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint)
+bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
{
- Paint *paint = NULL;
+ Paint *paint = nullptr;
if (*r_paint) {
/* Tool offset should never be 0 for initialized paint settings, so it's a reliable way to
* check if already initialized. */
@@ -1047,7 +1065,7 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint)
}
else {
BLI_assert(ELEM(*r_paint,
- /* Cast is annoying, but prevent NULL-pointer access. */
+ /* Cast is annoying, but prevent nullptr-pointer access. */
(Paint *)ts->gp_paint,
(Paint *)ts->gp_vertexpaint,
(Paint *)ts->gp_sculptpaint,
@@ -1059,7 +1077,7 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint)
(Paint *)ts->curves_sculpt,
(Paint *)&ts->imapaint));
#ifdef DEBUG
- struct Paint paint_test = **r_paint;
+ Paint paint_test = **r_paint;
BKE_paint_runtime_init(ts, *r_paint);
/* Swap so debug doesn't hide errors when release fails. */
SWAP(Paint, **r_paint, paint_test);
@@ -1071,41 +1089,41 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint)
}
if (((VPaint **)r_paint == &ts->vpaint) || ((VPaint **)r_paint == &ts->wpaint)) {
- VPaint *data = MEM_callocN(sizeof(*data), __func__);
+ VPaint *data = MEM_cnew<VPaint>(__func__);
paint = &data->paint;
}
else if ((Sculpt **)r_paint == &ts->sculpt) {
- Sculpt *data = MEM_callocN(sizeof(*data), __func__);
+ Sculpt *data = MEM_cnew<Sculpt>(__func__);
paint = &data->paint;
- /* Turn on X plane mirror symmetry by default */
+ /* Turn on X plane mirror symmetry by default. */
paint->symmetry_flags |= PAINT_SYMM_X;
- /* Make sure at least dyntopo subdivision is enabled */
+ /* Make sure at least dyntopo subdivision is enabled. */
data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE;
}
else if ((GpPaint **)r_paint == &ts->gp_paint) {
- GpPaint *data = MEM_callocN(sizeof(*data), __func__);
+ GpPaint *data = MEM_cnew<GpPaint>(__func__);
paint = &data->paint;
}
else if ((GpVertexPaint **)r_paint == &ts->gp_vertexpaint) {
- GpVertexPaint *data = MEM_callocN(sizeof(*data), __func__);
+ GpVertexPaint *data = MEM_cnew<GpVertexPaint>(__func__);
paint = &data->paint;
}
else if ((GpSculptPaint **)r_paint == &ts->gp_sculptpaint) {
- GpSculptPaint *data = MEM_callocN(sizeof(*data), __func__);
+ GpSculptPaint *data = MEM_cnew<GpSculptPaint>(__func__);
paint = &data->paint;
}
else if ((GpWeightPaint **)r_paint == &ts->gp_weightpaint) {
- GpWeightPaint *data = MEM_callocN(sizeof(*data), __func__);
+ GpWeightPaint *data = MEM_cnew<GpWeightPaint>(__func__);
paint = &data->paint;
}
else if ((UvSculpt **)r_paint == &ts->uvsculpt) {
- UvSculpt *data = MEM_callocN(sizeof(*data), __func__);
+ UvSculpt *data = MEM_cnew<UvSculpt>(__func__);
paint = &data->paint;
}
else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) {
- CurvesSculpt *data = MEM_callocN(sizeof(*data), __func__);
+ CurvesSculpt *data = MEM_cnew<CurvesSculpt>(__func__);
paint = &data->paint;
}
else if (*r_paint == &ts->imapaint.paint) {
@@ -1121,7 +1139,7 @@ bool BKE_paint_ensure(ToolSettings *ts, struct Paint **r_paint)
return false;
}
-void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const char col[3])
+void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3])
{
UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings;
Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode);
@@ -1131,18 +1149,18 @@ void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const char col[3])
/* If there's no brush, create one */
if (PAINT_MODE_HAS_BRUSH(mode)) {
Brush *brush = BKE_paint_brush(paint);
- if (brush == NULL) {
+ if (brush == nullptr) {
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
brush = BKE_brush_first_search(bmain, ob_mode);
if (!brush) {
brush = BKE_brush_add(bmain, "Brush", ob_mode);
- id_us_min(&brush->id); /* fake user only */
+ id_us_min(&brush->id); /* Fake user only. */
}
BKE_paint_brush_set(paint, brush);
}
}
- memcpy(paint->paint_cursor_col, col, 3);
+ copy_v3_v3_uchar(paint->paint_cursor_col, col);
paint->paint_cursor_col[3] = 128;
ups->last_stroke_valid = false;
zero_v3(ups->average_stroke_accum);
@@ -1162,12 +1180,12 @@ void BKE_paint_copy(Paint *src, Paint *tar, const int flag)
{
tar->brush = src->brush;
tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
- tar->tool_slots = MEM_dupallocN(src->tool_slots);
+ tar->tool_slots = static_cast<PaintToolSlot *>(MEM_dupallocN(src->tool_slots));
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
id_us_plus((ID *)tar->brush);
id_us_plus((ID *)tar->palette);
- if (src->tool_slots != NULL) {
+ if (src->tool_slots != nullptr) {
for (int i = 0; i < tar->tool_slots_len; i++) {
id_us_plus((ID *)tar->tool_slots[i].brush);
}
@@ -1215,7 +1233,7 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain
const size_t expected_size = sizeof(PaintToolSlot) * p->tool_slots_len;
if (p->tool_slots && MEM_allocN_len(p->tool_slots) < expected_size) {
MEM_freeN(p->tool_slots);
- p->tool_slots = MEM_callocN(expected_size, "PaintToolSlot");
+ p->tool_slots = static_cast<PaintToolSlot *>(MEM_callocN(expected_size, "PaintToolSlot"));
}
BKE_paint_runtime_init(scene->toolsettings, p);
@@ -1226,27 +1244,28 @@ void BKE_paint_blend_read_lib(BlendLibReader *reader, Scene *sce, Paint *p)
if (p) {
BLO_read_id_address(reader, sce->id.lib, &p->brush);
for (int i = 0; i < p->tool_slots_len; i++) {
- if (p->tool_slots[i].brush != NULL) {
+ if (p->tool_slots[i].brush != nullptr) {
BLO_read_id_address(reader, sce->id.lib, &p->tool_slots[i].brush);
}
}
BLO_read_id_address(reader, sce->id.lib, &p->palette);
- p->paint_cursor = NULL;
+ p->paint_cursor = nullptr;
BKE_paint_runtime_init(sce->toolsettings, p);
}
}
-bool paint_is_face_hidden(const MLoopTri *lt, const MVert *mvert, const MLoop *mloop)
+bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_poly)
{
- return ((mvert[mloop[lt->tri[0]].v].flag & ME_HIDE) ||
- (mvert[mloop[lt->tri[1]].v].flag & ME_HIDE) ||
- (mvert[mloop[lt->tri[2]].v].flag & ME_HIDE));
+ if (!hide_poly) {
+ return false;
+ }
+ return hide_poly[lt->poly];
}
bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y)
{
- /* skip face if any of its corners are hidden */
+ /* Skip face if any of its corners are hidden. */
return (BLI_BITMAP_TEST(grid_hidden, y * gridsize + x) ||
BLI_BITMAP_TEST(grid_hidden, y * gridsize + x + 1) ||
BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x + 1) ||
@@ -1276,7 +1295,7 @@ float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y
return gpm->data[(y * factor) * gridsize + (x * factor)];
}
-/* threshold to move before updating the brush rotation */
+/* Threshold to move before updating the brush rotation. */
#define RAKE_THRESHHOLD 20
void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, float rotation)
@@ -1296,13 +1315,22 @@ void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, f
}
}
+static bool paint_rake_rotation_active(const MTex &mtex)
+{
+ return mtex.tex && mtex.brush_angle_mode & MTEX_ANGLE_RAKE;
+}
+
+static bool paint_rake_rotation_active(const Brush &brush)
+{
+ return paint_rake_rotation_active(brush.mtex) || paint_rake_rotation_active(brush.mask_mtex);
+}
+
bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups,
Brush *brush,
const float mouse_pos[2])
{
bool ok = false;
- if ((brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) ||
- (brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
+ if (paint_rake_rotation_active(*brush)) {
const float r = RAKE_THRESHHOLD;
float rotation;
@@ -1319,8 +1347,8 @@ bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups,
paint_update_brush_rake_rotation(ups, brush, rotation);
ok = true;
}
- /* make sure we reset here to the last rotation to avoid accumulating
- * values in case a random rotation is also added */
+ /* Make sure we reset here to the last rotation to avoid accumulating
+ * values in case a random rotation is also added. */
else {
paint_update_brush_rake_rotation(ups, brush, ups->last_rake_angle);
ok = false;
@@ -1340,9 +1368,9 @@ void BKE_sculptsession_free_deformMats(SculptSession *ss)
MEM_SAFE_FREE(ss->deform_imats);
}
-void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss)
+void BKE_sculptsession_free_vwpaint_data(SculptSession *ss)
{
- struct SculptVertexPaintGeomMap *gmap = NULL;
+ SculptVertexPaintGeomMap *gmap = nullptr;
if (ss->mode_type == OB_MODE_VERTEX_PAINT) {
gmap = &ss->mode.vpaint.gmap;
}
@@ -1353,7 +1381,7 @@ void BKE_sculptsession_free_vwpaint_data(struct SculptSession *ss)
if (ss->mode.wpaint.dvert_prev) {
BKE_defvert_array_free_elems(ss->mode.wpaint.dvert_prev, ss->totvert);
MEM_freeN(ss->mode.wpaint.dvert_prev);
- ss->mode.wpaint.dvert_prev = NULL;
+ ss->mode.wpaint.dvert_prev = nullptr;
}
}
else {
@@ -1382,12 +1410,9 @@ static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder)
if (reorder) {
BM_log_mesh_elems_reorder(ss->bm, ss->bm_log);
}
- BM_mesh_bm_to_me(NULL,
- ss->bm,
- ob->data,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }));
+ BMeshToMeshParams params{};
+ params.calc_object_remap = false;
+ BM_mesh_bm_to_me(nullptr, ss->bm, static_cast<Mesh *>(ob->data), &params);
}
}
}
@@ -1413,7 +1438,7 @@ static void sculptsession_free_pbvh(Object *object)
if (ss->pbvh) {
BKE_pbvh_free(ss->pbvh);
- ss->pbvh = NULL;
+ ss->pbvh = nullptr;
}
MEM_SAFE_FREE(ss->pmap);
@@ -1425,12 +1450,8 @@ static void sculptsession_free_pbvh(Object *object)
MEM_SAFE_FREE(ss->vemap);
MEM_SAFE_FREE(ss->vemap_mem);
- MEM_SAFE_FREE(ss->persistent_base);
-
- MEM_SAFE_FREE(ss->preview_vert_index_list);
- ss->preview_vert_index_count = 0;
-
- MEM_SAFE_FREE(ss->preview_vert_index_list);
+ MEM_SAFE_FREE(ss->preview_vert_list);
+ ss->preview_vert_count = 0;
MEM_SAFE_FREE(ss->vertex_info.connected_component);
MEM_SAFE_FREE(ss->vertex_info.boundary);
@@ -1465,6 +1486,8 @@ void BKE_sculptsession_free(Object *ob)
if (ob && ob->sculpt) {
SculptSession *ss = ob->sculpt;
+ BKE_sculpt_attribute_destroy_temporary_all(ob);
+
if (ss->bm) {
BKE_sculptsession_bm_to_me(ob, true);
BM_mesh_free(ss->bm);
@@ -1502,7 +1525,7 @@ void BKE_sculptsession_free(Object *ob)
}
if (ss->boundary_preview) {
- MEM_SAFE_FREE(ss->boundary_preview->vertices);
+ MEM_SAFE_FREE(ss->boundary_preview->verts);
MEM_SAFE_FREE(ss->boundary_preview->edges);
MEM_SAFE_FREE(ss->boundary_preview->distance);
MEM_SAFE_FREE(ss->boundary_preview->edit_info);
@@ -1515,30 +1538,39 @@ void BKE_sculptsession_free(Object *ob)
MEM_freeN(ss);
- ob->sculpt = NULL;
+ ob->sculpt = nullptr;
}
}
-MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob)
+static MultiresModifierData *sculpt_multires_modifier_get(const Scene *scene,
+ Object *ob,
+ const bool auto_create_mdisps)
{
Mesh *me = (Mesh *)ob->data;
ModifierData *md;
VirtualModifierData virtualModifierData;
if (ob->sculpt && ob->sculpt->bm) {
- /* can't combine multires and dynamic topology */
- return NULL;
+ /* Can't combine multires and dynamic topology. */
+ return nullptr;
}
+ bool need_mdisps = false;
+
if (!CustomData_get_layer(&me->ldata, CD_MDISPS)) {
- /* multires can't work without displacement layer */
- return NULL;
+ if (!auto_create_mdisps) {
+ /* Multires can't work without displacement layer. */
+ return nullptr;
+ }
+ else {
+ need_mdisps = true;
+ }
}
/* Weight paint operates on original vertices, and needs to treat multires as regular modifier
* to make it so that PBVH vertices are at the multires surface. */
if ((ob->mode & OB_MODE_SCULPT) == 0) {
- return NULL;
+ return nullptr;
}
for (md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); md; md = md->next) {
@@ -1550,14 +1582,23 @@ MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob)
}
if (mmd->sculptlvl > 0 && !(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) {
+ if (need_mdisps) {
+ CustomData_add_layer(&me->ldata, CD_MDISPS, CD_SET_DEFAULT, nullptr, me->totloop);
+ }
+
return mmd;
}
- return NULL;
+ return nullptr;
}
}
- return NULL;
+ return nullptr;
+}
+
+MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob)
+{
+ return sculpt_multires_modifier_get(scene, ob, false);
}
/* Checks if there are any supported deformation modifiers active */
@@ -1571,16 +1612,16 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
return false;
}
- /* non-locked shape keys could be handled in the same way as deformed mesh */
+ /* Non-locked shape keys could be handled in the same way as deformed mesh. */
if ((ob->shapeflag & OB_SHAPE_LOCK) == 0 && me->key && ob->shapenr) {
return true;
}
md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
- /* exception for shape keys because we can edit those */
+ /* Exception for shape keys because we can edit those. */
for (; md; md = md->next) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type);
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) {
continue;
}
@@ -1605,23 +1646,34 @@ static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
return false;
}
-/**
- * \param need_mask: So that the evaluated mesh that is returned has mask data.
+/* Helper function to keep persistent base attribute references up to
+ * date. This is a bit more tricky since they persist across strokes.
*/
-static void sculpt_update_object(Depsgraph *depsgraph,
- Object *ob,
- Mesh *me_eval,
- bool need_pmap,
- bool need_mask,
- bool is_paint_tool)
+static void sculpt_update_persistent_base(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+
+ ss->attrs.persistent_co = BKE_sculpt_attribute_get(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co));
+ ss->attrs.persistent_no = BKE_sculpt_attribute_get(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no));
+ ss->attrs.persistent_disp = BKE_sculpt_attribute_get(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp));
+}
+
+static void sculpt_update_object(
+ Depsgraph *depsgraph, Object *ob, Object *ob_eval, bool need_pmap, bool is_paint_tool)
{
Scene *scene = DEG_get_input_scene(depsgraph);
Sculpt *sd = scene->toolsettings->sculpt;
SculptSession *ss = ob->sculpt;
- const Mesh *me = BKE_object_get_original_mesh(ob);
- MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
+ Mesh *me = BKE_object_get_original_mesh(ob);
+ Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
+ MultiresModifierData *mmd = sculpt_multires_modifier_get(scene, ob, true);
const bool use_face_sets = (ob->mode & OB_MODE_SCULPT) != 0;
+ BLI_assert(me_eval != nullptr);
+
ss->depsgraph = depsgraph;
ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob);
@@ -1632,16 +1684,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
ss->scene = scene;
- if (need_mask) {
- if (mmd == NULL) {
- BLI_assert(CustomData_has_layer(&me->vdata, CD_PAINT_MASK));
- }
- else {
- BLI_assert(CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK));
- }
- }
-
- ss->shapekey_active = (mmd == NULL) ? BKE_keyblock_from_object(ob) : NULL;
+ ss->shapekey_active = (mmd == nullptr) ? BKE_keyblock_from_object(ob) : nullptr;
/* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path,
* so no extra checks is needed here. */
@@ -1655,54 +1698,55 @@ static void sculpt_update_object(Depsgraph *depsgraph,
/* These are assigned to the base mesh in Multires. This is needed because Face Sets operators
* and tools use the Face Sets data from the base mesh when Multires is active. */
- ss->mvert = me->mvert;
- ss->mpoly = me->mpoly;
- ss->mloop = me->mloop;
+ ss->mvert = BKE_mesh_verts_for_write(me);
+ ss->mpoly = BKE_mesh_polys(me);
+ ss->mloop = BKE_mesh_loops(me);
}
else {
ss->totvert = me->totvert;
ss->totpoly = me->totpoly;
ss->totfaces = me->totpoly;
- ss->mvert = me->mvert;
- ss->mpoly = me->mpoly;
- ss->mloop = me->mloop;
+ ss->mvert = BKE_mesh_verts_for_write(me);
+ ss->mpoly = BKE_mesh_polys(me);
+ ss->mloop = BKE_mesh_loops(me);
ss->multires.active = false;
- ss->multires.modifier = NULL;
+ ss->multires.modifier = nullptr;
ss->multires.level = 0;
- ss->vmask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
+ ss->vmask = static_cast<float *>(CustomData_get_layer(&me->vdata, CD_PAINT_MASK));
CustomDataLayer *layer;
eAttrDomain domain;
if (BKE_pbvh_get_color_layer(me, &layer, &domain)) {
if (layer->type == CD_PROP_COLOR) {
- ss->vcol = layer->data;
+ ss->vcol = static_cast<MPropCol *>(layer->data);
}
else {
- ss->mcol = layer->data;
+ ss->mcol = static_cast<MLoopCol *>(layer->data);
}
ss->vcol_domain = domain;
- ss->vcol_type = layer->type;
+ ss->vcol_type = static_cast<eCustomDataType>(layer->type);
}
else {
- ss->vcol = NULL;
- ss->mcol = NULL;
+ ss->vcol = nullptr;
+ ss->mcol = nullptr;
- ss->vcol_type = -1;
- ss->vcol_domain = ATTR_DOMAIN_NUM;
+ ss->vcol_type = (eCustomDataType)-1;
+ ss->vcol_domain = ATTR_DOMAIN_POINT;
}
}
/* Sculpt Face Sets. */
if (use_face_sets) {
- BLI_assert(CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS));
- ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
+ ss->face_sets = static_cast<int *>(CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS));
}
else {
- ss->face_sets = NULL;
+ ss->face_sets = nullptr;
}
+ ss->hide_poly = (bool *)CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly");
+
ss->subdiv_ccg = me_eval->runtime.subdiv_ccg;
PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob);
@@ -1711,12 +1755,21 @@ static void sculpt_update_object(Depsgraph *depsgraph,
BKE_pbvh_subdiv_cgg_set(ss->pbvh, ss->subdiv_ccg);
BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets);
+ BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh);
BKE_pbvh_face_sets_color_set(ss->pbvh, me->face_sets_color_seed, me->face_sets_color_default);
+ sculpt_attribute_update_refs(ob);
+ sculpt_update_persistent_base(ob);
+
if (need_pmap && ob->type == OB_MESH && !ss->pmap) {
- BKE_mesh_vert_poly_map_create(
- &ss->pmap, &ss->pmap_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
+ BKE_mesh_vert_poly_map_create(&ss->pmap,
+ &ss->pmap_mem,
+ BKE_mesh_polys(me),
+ BKE_mesh_loops(me),
+ me->totvert,
+ me->totpoly,
+ me->totloop);
if (ss->pbvh) {
BKE_pbvh_pmap_set(ss->pbvh, ss->pmap);
@@ -1727,14 +1780,37 @@ static void sculpt_update_object(Depsgraph *depsgraph,
pbvh_show_face_sets_set(ss->pbvh, ss->show_face_sets);
if (ss->deform_modifiers_active) {
- if (!ss->orig_cos) {
+ /* Painting doesn't need crazyspace, use already evaluated mesh coordinates if possible. */
+ bool used_me_eval = false;
+
+ if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
+ Mesh *me_eval_deform = ob_eval->runtime.mesh_deform_eval;
+
+ /* If the fully evaluated mesh has the same topology as the deform-only version, use it.
+ * This matters because crazyspace evaluation is very restrictive and excludes even modifiers
+ * that simply recompute vertex weights (which can even include Geometry Nodes). */
+ if (me_eval_deform->polys().data() == me_eval->polys().data() &&
+ me_eval_deform->loops().data() == me_eval->loops().data() &&
+ me_eval_deform->totvert == me_eval->totvert) {
+ BKE_sculptsession_free_deformMats(ss);
+
+ BLI_assert(me_eval_deform->totvert == me->totvert);
+
+ ss->deform_cos = BKE_mesh_vert_coords_alloc(me_eval, NULL);
+ BKE_pbvh_vert_coords_apply(ss->pbvh, ss->deform_cos, me->totvert);
+
+ used_me_eval = true;
+ }
+ }
+
+ if (!ss->orig_cos && !used_me_eval) {
int a;
BKE_sculptsession_free_deformMats(ss);
ss->orig_cos = (ss->shapekey_active) ?
BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active) :
- BKE_mesh_vert_coords_alloc(me, NULL);
+ BKE_mesh_vert_coords_alloc(me, nullptr);
BKE_crazyspace_build_sculpt(depsgraph, scene, ob, &ss->deform_imats, &ss->deform_cos);
BKE_pbvh_vert_coords_apply(ss->pbvh, ss->deform_cos, me->totvert);
@@ -1748,14 +1824,14 @@ static void sculpt_update_object(Depsgraph *depsgraph,
BKE_sculptsession_free_deformMats(ss);
}
- if (ss->shapekey_active != NULL && ss->deform_cos == NULL) {
+ if (ss->shapekey_active != nullptr && ss->deform_cos == nullptr) {
ss->deform_cos = BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active);
}
/* if pbvh is deformed, key block is already applied to it */
if (ss->shapekey_active) {
bool pbvh_deformed = BKE_pbvh_is_deformed(ss->pbvh);
- if (!pbvh_deformed || ss->deform_cos == NULL) {
+ if (!pbvh_deformed || ss->deform_cos == nullptr) {
float(*vertCos)[3] = BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active);
if (vertCos) {
@@ -1763,7 +1839,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
/* apply shape keys coordinates to PBVH */
BKE_pbvh_vert_coords_apply(ss->pbvh, vertCos, me->totvert);
}
- if (ss->deform_cos == NULL) {
+ if (ss->deform_cos == nullptr) {
ss->deform_cos = vertCos;
}
if (vertCos != ss->deform_cos) {
@@ -1782,7 +1858,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
*/
if (U.experimental.use_sculpt_texture_paint && ss->pbvh) {
char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob);
- if (ss->last_paint_canvas_key == NULL ||
+ if (ss->last_paint_canvas_key == nullptr ||
!STREQ(paint_canvas_key, ss->last_paint_canvas_key)) {
MEM_SAFE_FREE(ss->last_paint_canvas_key);
ss->last_paint_canvas_key = paint_canvas_key;
@@ -1801,24 +1877,7 @@ static void sculpt_update_object(Depsgraph *depsgraph,
}
}
-static void sculpt_face_sets_ensure(Mesh *mesh)
-{
- if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) {
- return;
- }
-
- int *new_face_sets = CustomData_add_layer(
- &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, mesh->totpoly);
-
- /* Initialize the new Face Set data-layer with a default valid visible ID and set the default
- * color to render it white. */
- for (int i = 0; i < mesh->totpoly; i++) {
- new_face_sets[i] = 1;
- }
- mesh->face_sets_color_default = 1;
-}
-
-void BKE_sculpt_update_object_before_eval(const Scene *scene, Object *ob_eval)
+void BKE_sculpt_update_object_before_eval(Object *ob_eval)
{
/* Update before mesh evaluation in the dependency graph. */
SculptSession *ss = ob_eval->sculpt;
@@ -1839,7 +1898,7 @@ void BKE_sculpt_update_object_before_eval(const Scene *scene, Object *ob_eval)
PBVHNode **nodes;
int n, totnode;
- BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+ BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode);
for (n = 0; n < totnode; n++) {
BKE_pbvh_node_mark_update(nodes[n]);
@@ -1848,16 +1907,6 @@ void BKE_sculpt_update_object_before_eval(const Scene *scene, Object *ob_eval)
MEM_freeN(nodes);
}
}
-
- if (ss) {
- Object *ob_orig = DEG_get_original_object(ob_eval);
- Mesh *mesh = BKE_object_get_original_mesh(ob_orig);
- MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob_orig);
-
- /* Ensure attribute layout is still correct. */
- sculpt_face_sets_ensure(mesh);
- BKE_sculpt_mask_layers_ensure(ob_orig, mmd);
- }
}
void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
@@ -1865,13 +1914,11 @@ void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
/* Update after mesh evaluation in the dependency graph, to rebuild PBVH or
* other data when modifiers change the mesh. */
Object *ob_orig = DEG_get_original_object(ob_eval);
- Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
- BLI_assert(me_eval != NULL);
- sculpt_update_object(depsgraph, ob_orig, me_eval, false, false, false);
+ sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false);
}
-void BKE_sculpt_color_layer_create_if_needed(struct Object *object)
+void BKE_sculpt_color_layer_create_if_needed(Object *object)
{
Mesh *orig_me = BKE_object_get_original_mesh(object);
@@ -1891,11 +1938,11 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object)
return;
}
- CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_DEFAULT, NULL, orig_me->totvert);
+ CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_SET_DEFAULT, nullptr, orig_me->totvert);
CustomDataLayer *layer = orig_me->vdata.layers +
CustomData_get_layer_index(&orig_me->vdata, CD_PROP_COLOR);
- BKE_mesh_update_customdata_pointers(orig_me, true);
+ BKE_mesh_tessface_clear(orig_me);
BKE_id_attributes_active_color_set(&orig_me->id, layer);
DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES);
@@ -1906,24 +1953,56 @@ void BKE_sculpt_color_layer_create_if_needed(struct Object *object)
}
void BKE_sculpt_update_object_for_edit(
- Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool need_mask, bool is_paint_tool)
+ Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool /*need_mask*/, bool is_paint_tool)
{
BLI_assert(ob_orig == DEG_get_original_object(ob_orig));
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_orig);
- Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
- BLI_assert(me_eval != NULL);
- sculpt_update_object(depsgraph, ob_orig, me_eval, need_pmap, need_mask, is_paint_tool);
+ sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, is_paint_tool);
+}
+
+int *BKE_sculpt_face_sets_ensure(Mesh *mesh)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) {
+ return static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
+ }
+
+ const AttributeAccessor attributes = mesh->attributes_for_write();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
+ MutableSpan<int> face_sets = {
+ static_cast<int *>(CustomData_add_layer(
+ &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly)),
+ mesh->totpoly};
+
+ face_sets.fill(1);
+ mesh->face_sets_color_default = 1;
+ return face_sets.data();
+}
+
+bool *BKE_sculpt_hide_poly_ensure(Mesh *mesh)
+{
+ if (bool *hide_poly = static_cast<bool *>(
+ CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"))) {
+ return hide_poly;
+ }
+ return static_cast<bool *>(CustomData_add_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, CD_SET_DEFAULT, nullptr, mesh->totpoly, ".hide_poly"));
}
int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
{
- const float *paint_mask;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
int ret = 0;
- paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
+ const float *paint_mask = static_cast<const float *>(
+ CustomData_get_layer(&me->vdata, CD_PAINT_MASK));
/* if multires is active, create a grid paint mask layer if there
* isn't one already */
@@ -1934,24 +2013,26 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
int gridarea = gridsize * gridsize;
int i, j;
- gmask = CustomData_add_layer(&me->ldata, CD_GRID_PAINT_MASK, CD_CALLOC, NULL, me->totloop);
+ gmask = static_cast<GridPaintMask *>(CustomData_add_layer(
+ &me->ldata, CD_GRID_PAINT_MASK, CD_SET_DEFAULT, nullptr, me->totloop));
for (i = 0; i < me->totloop; i++) {
GridPaintMask *gpm = &gmask[i];
gpm->level = level;
- gpm->data = MEM_callocN(sizeof(float) * gridarea, "GridPaintMask.data");
+ gpm->data = static_cast<float *>(
+ MEM_callocN(sizeof(float) * gridarea, "GridPaintMask.data"));
}
/* if vertices already have mask, copy into multires data */
if (paint_mask) {
for (i = 0; i < me->totpoly; i++) {
- const MPoly *p = &me->mpoly[i];
+ const MPoly *p = &polys[i];
float avg = 0;
/* mask center */
for (j = 0; j < p->totloop; j++) {
- const MLoop *l = &me->mloop[p->loopstart + j];
+ const MLoop *l = &loops[p->loopstart + j];
avg += paint_mask[l->v];
}
avg /= (float)p->totloop;
@@ -1959,9 +2040,9 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
/* fill in multires mask corner */
for (j = 0; j < p->totloop; j++) {
GridPaintMask *gpm = &gmask[p->loopstart + j];
- const MLoop *l = &me->mloop[p->loopstart + j];
- const MLoop *prev = ME_POLY_LOOP_PREV(me->mloop, p, j);
- const MLoop *next = ME_POLY_LOOP_NEXT(me->mloop, p, j);
+ const MLoop *l = &loops[p->loopstart + j];
+ const MLoop *prev = ME_POLY_LOOP_PREV(loops, p, j);
+ const MLoop *next = ME_POLY_LOOP_NEXT(loops, p, j);
gpm->data[0] = avg;
gpm->data[1] = (paint_mask[l->v] + paint_mask[next->v]) * 0.5f;
@@ -1970,20 +2051,24 @@ int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
}
}
}
+ /* The evaluated multires CCG must be updated to contain the new data. */
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
ret |= SCULPT_MASK_LAYER_CALC_LOOP;
}
- /* create vertex paint mask layer if there isn't one already */
+ /* Create vertex paint mask layer if there isn't one already. */
if (!paint_mask) {
- CustomData_add_layer(&me->vdata, CD_PAINT_MASK, CD_CALLOC, NULL, me->totvert);
+ CustomData_add_layer(&me->vdata, CD_PAINT_MASK, CD_SET_DEFAULT, nullptr, me->totvert);
+ /* The evaluated mesh must be updated to contain the new data. */
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
ret |= SCULPT_MASK_LAYER_CALC_VERT;
}
return ret;
}
-void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene)
+void BKE_sculpt_toolsettings_data_ensure(Scene *scene)
{
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt);
@@ -1998,7 +2083,7 @@ void BKE_sculpt_toolsettings_data_ensure(struct Scene *scene)
sd->constant_detail = 3.0f;
}
- /* Set sane default tiling offsets */
+ /* Set sane default tiling offsets. */
if (!sd->paint.tile_offset[0]) {
sd->paint.tile_offset[0] = 1.0f;
}
@@ -2016,103 +2101,47 @@ static bool check_sculpt_object_deformed(Object *object, const bool for_construc
/* Active modifiers means extra deformation, which can't be handled correct
* on birth of PBVH and sculpt "layer" levels, so use PBVH only for internal brush
- * stuff and show final evaluated mesh so user would see actual object shape.
- */
+ * stuff and show final evaluated mesh so user would see actual object shape. */
deformed |= object->sculpt->deform_modifiers_active;
if (for_construction) {
- deformed |= object->sculpt->shapekey_active != NULL;
+ deformed |= object->sculpt->shapekey_active != nullptr;
}
else {
/* As in case with modifiers, we can't synchronize deformation made against
* PBVH and non-locked keyblock, so also use PBVH only for brushes and
- * final DM to give final result to user.
- */
+ * final DM to give final result to user. */
deformed |= object->sculpt->shapekey_active && (object->shapeflag & OB_SHAPE_LOCK) == 0;
}
return deformed;
}
-void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh)
+void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg)
{
- const int face_sets_default_visible_id = 1;
- const int face_sets_default_hidden_id = -(face_sets_default_visible_id + 1);
-
- bool initialize_new_face_sets = false;
-
- if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) {
- /* Make everything visible. */
- int *current_face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
- for (int i = 0; i < mesh->totpoly; i++) {
- current_face_sets[i] = abs(current_face_sets[i]);
- }
- }
- else {
- initialize_new_face_sets = true;
- int *new_face_sets = CustomData_add_layer(
- &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, mesh->totpoly);
-
- /* Initialize the new Face Set data-layer with a default valid visible ID and set the default
- * color to render it white. */
- for (int i = 0; i < mesh->totpoly; i++) {
- new_face_sets[i] = face_sets_default_visible_id;
- }
- mesh->face_sets_color_default = face_sets_default_visible_id;
- }
-
- int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
-
- for (int i = 0; i < mesh->totpoly; i++) {
- if (!(mesh->mpoly[i].flag & ME_HIDE)) {
- continue;
- }
-
- if (initialize_new_face_sets) {
- /* When initializing a new Face Set data-layer, assign a new hidden Face Set ID to hidden
- * vertices. This way, we get at initial split in two Face Sets between hidden and
- * visible vertices based on the previous mesh visibly from other mode that can be
- * useful in some cases. */
- face_sets[i] = face_sets_default_hidden_id;
- }
- else {
- /* Otherwise, set the already existing Face Set ID to hidden. */
- face_sets[i] = -abs(face_sets[i]);
- }
- }
-}
-
-void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh)
-{
- const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
- if (!face_sets) {
- return;
- }
-
- for (int i = 0; i < mesh->totpoly; i++) {
- const bool is_face_set_visible = face_sets[i] >= 0;
- SET_FLAG_FROM_TEST(mesh->mpoly[i].flag, !is_face_set_visible, ME_HIDE);
- }
-
- BKE_mesh_flush_hidden_from_polys(mesh);
-}
-
-void BKE_sculpt_sync_face_sets_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg)
-{
- const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
- if (!face_sets) {
+ using namespace blender;
+ using namespace blender::bke;
+ if (!subdiv_ccg) {
return;
}
- if (!subdiv_ccg) {
+ const AttributeAccessor attributes = mesh->attributes();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
+ /* Nothing is hidden, so we can just remove all visibility bitmaps. */
+ for (const int i : hide_poly.index_range()) {
+ BKE_subdiv_ccg_grid_hidden_free(subdiv_ccg, i);
+ }
return;
}
+ const VArraySpan<bool> hide_poly_span(hide_poly);
CCGKey key;
BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
for (int i = 0; i < mesh->totloop; i++) {
const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, i);
- const bool is_hidden = (face_sets[face_index] < 0);
+ const bool is_hidden = hide_poly_span[face_index];
/* Avoid creating and modifying the grid_hidden bitmap if the base mesh face is visible and
* there is not bitmap for the grid. This is because missing grid_hidden implies grid is fully
@@ -2128,61 +2157,18 @@ void BKE_sculpt_sync_face_sets_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv
}
}
-void BKE_sculpt_sync_face_set_visibility(struct Mesh *mesh, struct SubdivCCG *subdiv_ccg)
-{
- BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(mesh);
- BKE_sculpt_sync_face_sets_visibility_to_base_mesh(mesh);
- BKE_sculpt_sync_face_sets_visibility_to_grids(mesh, subdiv_ccg);
-}
-
-void BKE_sculpt_ensure_orig_mesh_data(Scene *scene, Object *object)
+static PBVH *build_pbvh_for_dynamic_topology(Object *ob)
{
- Mesh *mesh = BKE_mesh_from_object(object);
- MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, object);
-
- BLI_assert(object->mode == OB_MODE_SCULPT);
+ PBVH *pbvh = ob->sculpt->pbvh = BKE_pbvh_new(PBVH_BMESH);
- /* Copy the current mesh visibility to the Face Sets. */
- BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(mesh);
- if (object->sculpt != NULL) {
- /* If a sculpt session is active, ensure we have its face-set data properly up-to-date. */
- object->sculpt->face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ sculptsession_bmesh_add_layers(ob);
- /* NOTE: In theory we could add that on the fly when required by sculpt code.
- * But this then requires proper update of depsgraph etc. For now we play safe, optimization is
- * always possible later if it's worth it. */
- BKE_sculpt_mask_layers_ensure(object, mmd);
- }
-
- /* Tessfaces aren't used and will become invalid. */
- BKE_mesh_tessface_clear(mesh);
-
- /* We always need to flush updates from depsgraph here, since at the very least
- * `BKE_sculpt_face_sets_ensure_from_base_mesh_visibility()` will have updated some data layer of
- * the mesh.
- *
- * All known potential sources of updates:
- * - Addition of, or changes to, the `CD_SCULPT_FACE_SETS` data layer
- * (`BKE_sculpt_face_sets_ensure_from_base_mesh_visibility`).
- * - Addition of a `CD_PAINT_MASK` data layer (`BKE_sculpt_mask_layers_ensure`).
- * - Object has any active modifier (modifier stack can be different in Sculpt mode).
- * - Multires:
- * + Differences of subdiv levels between sculpt and object modes
- * (`mmd->sculptlvl != mmd->lvl`).
- * + Addition of a `CD_GRID_PAINT_MASK` data layer (`BKE_sculpt_mask_layers_ensure`).
- */
- DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
-}
-
-static PBVH *build_pbvh_for_dynamic_topology(Object *ob)
-{
- PBVH *pbvh = BKE_pbvh_new();
BKE_pbvh_build_bmesh(pbvh,
ob->sculpt->bm,
ob->sculpt->bm_smooth_shading,
ob->sculpt->bm_log,
- ob->sculpt->cd_vert_node_offset,
- ob->sculpt->cd_face_node_offset);
+ ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset,
+ ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset);
pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
pbvh_show_face_sets_set(pbvh, false);
return pbvh;
@@ -2192,20 +2178,24 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool
{
Mesh *me = BKE_object_get_original_mesh(ob);
const int looptris_num = poly_to_tri_count(me->totpoly, me->totloop);
- PBVH *pbvh = BKE_pbvh_new();
+ PBVH *pbvh = BKE_pbvh_new(PBVH_FACES);
BKE_pbvh_respect_hide_set(pbvh, respect_hide);
- MLoopTri *looptri = MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__);
+ MutableSpan<MVert> verts = me->verts_for_write();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
- BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
+ MLoopTri *looptri = static_cast<MLoopTri *>(
+ MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__));
- BKE_sculpt_sync_face_set_visibility(me, NULL);
+ BKE_mesh_recalc_looptri(
+ loops.data(), polys.data(), verts.data(), me->totloop, me->totpoly, looptri);
BKE_pbvh_build_mesh(pbvh,
me,
- me->mpoly,
- me->mloop,
- me->mvert,
+ polys.data(),
+ loops.data(),
+ verts.data(),
me->totvert,
&me->vdata,
&me->ldata,
@@ -2217,7 +2207,7 @@ static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool
pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets);
const bool is_deformed = check_sculpt_object_deformed(ob, true);
- if (is_deformed && me_eval_deform != NULL) {
+ if (is_deformed && me_eval_deform != nullptr) {
int totvert;
float(*v_cos)[3] = BKE_mesh_vert_coords_alloc(me_eval_deform, &totvert);
BKE_pbvh_vert_coords_apply(pbvh, v_cos, totvert);
@@ -2231,11 +2221,11 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect
{
CCGKey key;
BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
- PBVH *pbvh = BKE_pbvh_new();
+ PBVH *pbvh = BKE_pbvh_new(PBVH_GRIDS);
BKE_pbvh_respect_hide_set(pbvh, respect_hide);
Mesh *base_mesh = BKE_mesh_from_object(ob);
- BKE_sculpt_sync_face_set_visibility(base_mesh, subdiv_ccg);
+ BKE_sculpt_sync_face_visibility_to_grids(base_mesh, subdiv_ccg);
BKE_pbvh_build_grids(pbvh,
subdiv_ccg->grids,
@@ -2251,26 +2241,21 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect
PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
{
- if (ob == NULL || ob->sculpt == NULL) {
- return NULL;
+ if (ob == nullptr || ob->sculpt == nullptr) {
+ return nullptr;
}
- bool respect_hide = true;
- if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
- if (!(BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob))) {
- respect_hide = false;
- }
- }
+ const bool respect_hide = true;
PBVH *pbvh = ob->sculpt->pbvh;
- if (pbvh != NULL) {
+ if (pbvh != nullptr) {
/* NOTE: It is possible that grids were re-allocated due to modifier
* stack. Need to update those pointers. */
if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) {
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
- Mesh *mesh_eval = object_eval->data;
+ Mesh *mesh_eval = static_cast<Mesh *>(object_eval->data);
SubdivCCG *subdiv_ccg = mesh_eval->runtime.subdiv_ccg;
- if (subdiv_ccg != NULL) {
+ if (subdiv_ccg != nullptr) {
BKE_sculpt_bvh_update_from_ccg(pbvh, subdiv_ccg);
}
}
@@ -2281,14 +2266,14 @@ PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
return pbvh;
}
- if (ob->sculpt->bm != NULL) {
+ if (ob->sculpt->bm != nullptr) {
/* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */
pbvh = build_pbvh_for_dynamic_topology(ob);
}
else {
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
- Mesh *mesh_eval = object_eval->data;
- if (mesh_eval->runtime.subdiv_ccg != NULL) {
+ Mesh *mesh_eval = static_cast<Mesh *>(object_eval->data);
+ if (mesh_eval->runtime.subdiv_ccg != nullptr) {
pbvh = build_pbvh_from_ccg(ob, mesh_eval->runtime.subdiv_ccg, respect_hide);
}
else if (ob->type == OB_MESH) {
@@ -2315,7 +2300,7 @@ void BKE_sculpt_bvh_update_from_ccg(PBVH *pbvh, SubdivCCG *subdiv_ccg)
bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const View3D *UNUSED(v3d))
{
SculptSession *ss = ob->sculpt;
- if (ss == NULL || ss->pbvh == NULL || ss->mode_type != OB_MODE_SCULPT) {
+ if (ss == nullptr || ss->pbvh == nullptr || ss->mode_type != OB_MODE_SCULPT) {
return false;
}
@@ -2334,10 +2319,10 @@ bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const View3D *UNUSED(v3d)
void BKE_paint_face_set_overlay_color_get(const int face_set, const int seed, uchar r_color[4])
{
float rgba[4];
- float random_mod_hue = GOLDEN_RATIO_CONJUGATE * (abs(face_set) + (seed % 10));
+ float random_mod_hue = GOLDEN_RATIO_CONJUGATE * (face_set + (seed % 10));
random_mod_hue = random_mod_hue - floorf(random_mod_hue);
- const float random_mod_sat = BLI_hash_int_01(abs(face_set) + seed + 1);
- const float random_mod_val = BLI_hash_int_01(abs(face_set) + seed + 2);
+ const float random_mod_sat = BLI_hash_int_01(face_set + seed + 1);
+ const float random_mod_val = BLI_hash_int_01(face_set + seed + 2);
hsv_to_rgb(random_mod_hue,
0.6f + (random_mod_sat * 0.25f),
1.0f - (random_mod_val * 0.35f),
@@ -2346,3 +2331,557 @@ void BKE_paint_face_set_overlay_color_get(const int face_set, const int seed, uc
&rgba[2]);
rgba_float_to_uchar(r_color, rgba);
}
+
+int BKE_sculptsession_vertex_count(const SculptSession *ss)
+{
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_FACES:
+ return ss->totvert;
+ case PBVH_BMESH:
+ return BM_mesh_elem_count(ss->bm, BM_VERT);
+ case PBVH_GRIDS:
+ return BKE_pbvh_get_grid_num_verts(ss->pbvh);
+ }
+
+ return 0;
+}
+
+/** Returns pointer to a CustomData associated with a given domain, if
+ * one exists. If not nullptr is returned (this may happen with e.g.
+ * multires and ATTR_DOMAIN_POINT).
+ */
+static CustomData *sculpt_get_cdata(Object *ob, eAttrDomain domain)
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (ss->bm) {
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ return &ss->bm->vdata;
+ case ATTR_DOMAIN_FACE:
+ return &ss->bm->pdata;
+ default:
+ BLI_assert_unreachable();
+ return NULL;
+ }
+ }
+ else {
+ Mesh *me = BKE_object_get_original_mesh(ob);
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ /* Cannot get vertex domain for multires grids. */
+ if (ss->pbvh && BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ return nullptr;
+ }
+
+ return &me->vdata;
+ case ATTR_DOMAIN_FACE:
+ return &me->pdata;
+ default:
+ BLI_assert_unreachable();
+ return NULL;
+ }
+ }
+}
+
+static int sculpt_attr_elem_count_get(Object *ob, eAttrDomain domain)
+{
+ SculptSession *ss = ob->sculpt;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ return BKE_sculptsession_vertex_count(ss);
+ break;
+ case ATTR_DOMAIN_FACE:
+ return ss->totfaces;
+ break;
+ default:
+ BLI_assert_unreachable();
+ return 0;
+ }
+}
+
+static bool sculpt_attribute_create(SculptSession *ss,
+ Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name,
+ SculptAttribute *out,
+ const SculptAttributeParams *params,
+ PBVHType pbvhtype,
+ bool flat_array_for_bmesh)
+{
+ Mesh *me = BKE_object_get_original_mesh(ob);
+
+ bool simple_array = params->simple_array;
+ bool permanent = params->permanent;
+
+ out->params = *params;
+ out->proptype = proptype;
+ out->domain = domain;
+ BLI_strncpy_utf8(out->name, name, sizeof(out->name));
+
+ /* Force non-CustomData simple_array mode if not PBVH_FACES. */
+ if (pbvhtype == PBVH_GRIDS || (pbvhtype == PBVH_BMESH && flat_array_for_bmesh)) {
+ if (permanent) {
+ printf(
+ "%s: error: tried to make permanent customdata in multires or bmesh mode; will make "
+ "local "
+ "array "
+ "instead.\n",
+ __func__);
+ permanent = (out->params.permanent = false);
+ }
+
+ simple_array = (out->params.simple_array = true);
+ }
+
+ BLI_assert(!(simple_array && permanent));
+
+ int totelem = sculpt_attr_elem_count_get(ob, domain);
+
+ if (simple_array) {
+ int elemsize = CustomData_sizeof(proptype);
+
+ out->data = MEM_calloc_arrayN(totelem, elemsize, __func__);
+
+ out->data_for_bmesh = ss->bm != NULL;
+ out->bmesh_cd_offset = -1;
+ out->layer = NULL;
+ out->elem_size = elemsize;
+ out->used = true;
+ out->elem_num = totelem;
+
+ return true;
+ }
+
+ switch (BKE_pbvh_type(ss->pbvh)) {
+ case PBVH_BMESH: {
+ CustomData *cdata = NULL;
+ out->data_for_bmesh = true;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ cdata = &ss->bm->vdata;
+ break;
+ case ATTR_DOMAIN_FACE:
+ cdata = &ss->bm->pdata;
+ break;
+ default:
+ out->used = false;
+ return false;
+ }
+
+ BLI_assert(CustomData_get_named_layer_index(cdata, proptype, name) == -1);
+
+ BM_data_layer_add_named(ss->bm, cdata, proptype, name);
+ int index = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (!permanent) {
+ cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY;
+ }
+
+ out->data = NULL;
+ out->layer = cdata->layers + index;
+ out->bmesh_cd_offset = out->layer->offset;
+ out->elem_size = CustomData_sizeof(proptype);
+ break;
+ }
+ case PBVH_FACES: {
+ CustomData *cdata = NULL;
+
+ out->data_for_bmesh = false;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ cdata = &me->vdata;
+ break;
+ case ATTR_DOMAIN_FACE:
+ cdata = &me->pdata;
+ break;
+ default:
+ out->used = false;
+ return false;
+ }
+
+ BLI_assert(CustomData_get_named_layer_index(cdata, proptype, name) == -1);
+
+ CustomData_add_layer_named(cdata, proptype, CD_SET_DEFAULT, NULL, totelem, name);
+ int index = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (!permanent) {
+ cdata->layers[index].flag |= CD_FLAG_TEMPORARY | CD_FLAG_NOCOPY;
+ }
+
+ out->data = NULL;
+ out->layer = cdata->layers + index;
+ out->bmesh_cd_offset = -1;
+ out->data = out->layer->data;
+ out->elem_size = CustomData_get_elem_size(out->layer);
+
+ break;
+ }
+ case PBVH_GRIDS: {
+ /* GRIDS should have been handled as simple arrays. */
+ BLI_assert_unreachable();
+ break;
+ }
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ out->used = true;
+ out->elem_num = totelem;
+
+ return true;
+}
+
+static bool sculpt_attr_update(Object *ob, SculptAttribute *attr)
+{
+ SculptSession *ss = ob->sculpt;
+ int elem_num = sculpt_attr_elem_count_get(ob, attr->domain);
+
+ bool bad = false;
+
+ if (attr->params.simple_array) {
+ bad = attr->elem_num != elem_num;
+
+ if (bad) {
+ MEM_SAFE_FREE(attr->data);
+ }
+ }
+ else {
+ CustomData *cdata = sculpt_get_cdata(ob, attr->domain);
+
+ if (cdata) {
+ int layer_index = CustomData_get_named_layer_index(cdata, attr->proptype, attr->name);
+
+ if (layer_index != -1 && attr->data_for_bmesh) {
+ attr->bmesh_cd_offset = cdata->layers[layer_index].offset;
+ }
+ else {
+ bad = true;
+ }
+ }
+ }
+
+ if (bad) {
+ sculpt_attribute_create(ss,
+ ob,
+ attr->domain,
+ attr->proptype,
+ attr->name,
+ attr,
+ &attr->params,
+ BKE_pbvh_type(ss->pbvh),
+ true);
+ }
+
+ return bad;
+}
+
+static SculptAttribute *sculpt_get_cached_layer(SculptSession *ss,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name)
+{
+ for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
+ SculptAttribute *attr = ss->temp_attributes + i;
+
+ if (attr->used && STREQ(attr->name, name) && attr->proptype == proptype &&
+ attr->domain == domain) {
+
+ return attr;
+ }
+ }
+
+ return NULL;
+}
+
+bool BKE_sculpt_attribute_exists(Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name)
+{
+ SculptSession *ss = ob->sculpt;
+ SculptAttribute *attr = sculpt_get_cached_layer(ss, domain, proptype, name);
+
+ if (attr) {
+ return true;
+ }
+
+ CustomData *cdata = sculpt_get_cdata(ob, domain);
+ return CustomData_get_named_layer_index(cdata, proptype, name) != -1;
+
+ return false;
+}
+
+static SculptAttribute *sculpt_alloc_attr(SculptSession *ss)
+{
+ for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
+ if (!ss->temp_attributes[i].used) {
+ memset((void *)(ss->temp_attributes + i), 0, sizeof(SculptAttribute));
+ ss->temp_attributes[i].used = true;
+
+ return ss->temp_attributes + i;
+ }
+ }
+
+ BLI_assert_unreachable();
+ return NULL;
+}
+
+SculptAttribute *BKE_sculpt_attribute_get(struct Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name)
+{
+ SculptSession *ss = ob->sculpt;
+
+ /* See if attribute is cached in ss->temp_attributes. */
+ SculptAttribute *attr = sculpt_get_cached_layer(ss, domain, proptype, name);
+
+ if (attr) {
+ sculpt_attr_update(ob, attr);
+ return attr;
+ }
+
+ /* Does attribute exist in CustomData layout? */
+ CustomData *cdata = sculpt_get_cdata(ob, domain);
+ if (cdata) {
+ int index = CustomData_get_named_layer_index(cdata, proptype, name);
+
+ if (index != -1) {
+ int totelem = 0;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ totelem = BKE_sculptsession_vertex_count(ss);
+ break;
+ case ATTR_DOMAIN_FACE:
+ totelem = ss->totfaces;
+ break;
+ default:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ attr = sculpt_alloc_attr(ss);
+
+ attr->used = true;
+ attr->proptype = proptype;
+ attr->data = cdata->layers[index].data;
+ attr->bmesh_cd_offset = cdata->layers[index].offset;
+ attr->elem_num = totelem;
+ attr->layer = cdata->layers + index;
+ attr->elem_size = CustomData_get_elem_size(attr->layer);
+
+ BLI_strncpy_utf8(attr->name, name, sizeof(attr->name));
+ return attr;
+ }
+ }
+
+ return NULL;
+}
+
+static SculptAttribute *sculpt_attribute_ensure_ex(Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name,
+ const SculptAttributeParams *params,
+ PBVHType pbvhtype,
+ bool flat_array_for_bmesh)
+{
+ SculptSession *ss = ob->sculpt;
+ SculptAttribute *attr = BKE_sculpt_attribute_get(ob, domain, proptype, name);
+
+ if (attr) {
+ return attr;
+ }
+
+ attr = sculpt_alloc_attr(ss);
+
+ /* Create attribute. */
+ sculpt_attribute_create(
+ ss, ob, domain, proptype, name, attr, params, pbvhtype, flat_array_for_bmesh);
+ sculpt_attribute_update_refs(ob);
+
+ return attr;
+}
+
+SculptAttribute *BKE_sculpt_attribute_ensure(Object *ob,
+ eAttrDomain domain,
+ eCustomDataType proptype,
+ const char *name,
+ const SculptAttributeParams *params)
+{
+ SculptAttributeParams temp_params = *params;
+
+ return sculpt_attribute_ensure_ex(
+ ob, domain, proptype, name, &temp_params, BKE_pbvh_type(ob->sculpt->pbvh), true);
+}
+
+static void sculptsession_bmesh_attr_update_internal(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+
+ sculptsession_bmesh_add_layers(ob);
+
+ if (ss->pbvh) {
+ BKE_pbvh_update_bmesh_offsets(ss->pbvh,
+ ob->sculpt->attrs.dyntopo_node_id_vertex->bmesh_cd_offset,
+ ob->sculpt->attrs.dyntopo_node_id_face->bmesh_cd_offset);
+ }
+}
+
+void sculptsession_bmesh_add_layers(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+ SculptAttributeParams params = {0};
+
+ ss->attrs.dyntopo_node_id_vertex = sculpt_attribute_ensure_ex(
+ ob,
+ ATTR_DOMAIN_POINT,
+ CD_PROP_INT32,
+ SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_vertex),
+ &params,
+ PBVH_BMESH,
+ false);
+
+ ss->attrs.dyntopo_node_id_face = sculpt_attribute_ensure_ex(
+ ob,
+ ATTR_DOMAIN_FACE,
+ CD_PROP_INT32,
+ SCULPT_ATTRIBUTE_NAME(dyntopo_node_id_face),
+ &params,
+ PBVH_BMESH,
+ false);
+}
+
+void BKE_sculpt_attributes_destroy_temporary_stroke(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+
+ for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
+ SculptAttribute *attr = ss->temp_attributes + i;
+
+ if (attr->params.stroke_only) {
+ BKE_sculpt_attribute_destroy(ob, attr);
+ }
+ }
+}
+
+static void sculpt_attribute_update_refs(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+
+ /* run twice, in case sculpt_attr_update had to recreate a layer and
+ messed up the bmesh offsets. */
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < SCULPT_MAX_ATTRIBUTES; j++) {
+ SculptAttribute *attr = ss->temp_attributes + j;
+
+ if (attr->used) {
+ sculpt_attr_update(ob, attr);
+ }
+ }
+
+ if (ss->bm) {
+ sculptsession_bmesh_attr_update_internal(ob);
+ }
+ }
+
+ Mesh *me = BKE_object_get_original_mesh(ob);
+
+ if (ss->pbvh) {
+ BKE_pbvh_update_active_vcol(ss->pbvh, me);
+ }
+}
+
+void BKE_sculpt_attribute_destroy_temporary_all(Object *ob)
+{
+ SculptSession *ss = ob->sculpt;
+
+ for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
+ SculptAttribute *attr = ss->temp_attributes + i;
+
+ if (attr->used && !attr->params.permanent) {
+ BKE_sculpt_attribute_destroy(ob, attr);
+ }
+ }
+}
+
+bool BKE_sculpt_attribute_destroy(Object *ob, SculptAttribute *attr)
+{
+ SculptSession *ss = ob->sculpt;
+ eAttrDomain domain = attr->domain;
+
+ BLI_assert(attr->used);
+
+ /* Remove from convenience pointer struct. */
+ SculptAttribute **ptrs = (SculptAttribute **)&ss->attrs;
+ int ptrs_num = sizeof(ss->attrs) / sizeof(void *);
+
+ for (int i = 0; i < ptrs_num; i++) {
+ if (ptrs[i] == attr) {
+ ptrs[i] = NULL;
+ }
+ }
+
+ /* Remove from internal temp_attributes array. */
+ for (int i = 0; i < SCULPT_MAX_ATTRIBUTES; i++) {
+ SculptAttribute *attr2 = ss->temp_attributes + i;
+
+ if (STREQ(attr2->name, attr->name) && attr2->domain == attr->domain &&
+ attr2->proptype == attr->proptype) {
+
+ attr2->used = false;
+ }
+ }
+
+ Mesh *me = BKE_object_get_original_mesh(ob);
+
+ if (attr->params.simple_array) {
+ MEM_SAFE_FREE(attr->data);
+ }
+ else if (ss->bm) {
+ CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &ss->bm->vdata : &ss->bm->pdata;
+
+ BM_data_layer_free_named(ss->bm, cdata, attr->name);
+ }
+ else {
+ CustomData *cdata = NULL;
+ int totelem = 0;
+
+ switch (domain) {
+ case ATTR_DOMAIN_POINT:
+ cdata = ss->bm ? &ss->bm->vdata : &me->vdata;
+ totelem = ss->totvert;
+ break;
+ case ATTR_DOMAIN_FACE:
+ cdata = ss->bm ? &ss->bm->pdata : &me->pdata;
+ totelem = ss->totfaces;
+ break;
+ default:
+ BLI_assert_unreachable();
+ return false;
+ }
+
+ /* We may have been called after destroying ss->bm in which case attr->layer
+ * might be invalid.
+ */
+ int layer_i = CustomData_get_named_layer_index(cdata, attr->proptype, attr->name);
+ if (layer_i != 0) {
+ CustomData_free_layer(cdata, attr->proptype, totelem, layer_i);
+ }
+
+ sculpt_attribute_update_refs(ob);
+ }
+
+ attr->data = NULL;
+ attr->used = false;
+
+ return true;
+}
diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c
index 2471d3baa59..bbd462d5ae1 100644
--- a/source/blender/blenkernel/intern/particle.c
+++ b/source/blender/blenkernel/intern/particle.c
@@ -53,6 +53,7 @@
#include "BKE_idtype.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
@@ -171,7 +172,7 @@ static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data)
}
if (psett->effector_weights) {
- BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->effector_weights->group, IDWALK_CB_NOP);
+ BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->effector_weights->group, IDWALK_CB_USER);
}
if (psett->pd) {
@@ -494,7 +495,7 @@ IDTypeInfo IDType_ID_PA = {
.foreach_id = particle_settings_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = particle_settings_blend_write,
.blend_read_data = particle_settings_blend_read_data,
@@ -761,13 +762,15 @@ static PTCacheEdit *psys_orig_edit_get(ParticleSystem *psys)
bool psys_in_edit_mode(Depsgraph *depsgraph, const ParticleSystem *psys)
{
- const ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
- if (view_layer->basact == NULL) {
+ const Scene *scene = DEG_get_input_scene(depsgraph);
+ ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Object *object = BKE_view_layer_active_object_get(view_layer);
+ if (object == NULL) {
/* TODO(sergey): Needs double-check with multi-object edit. */
return false;
}
const bool use_render_params = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
- const Object *object = view_layer->basact->object;
if (object->mode != OB_MODE_PARTICLE_EDIT) {
return false;
}
@@ -1399,7 +1402,8 @@ static void init_particle_interpolation(Object *ob,
pind->dietime = (key + pa->totkey - 1)->time;
if (pind->mesh) {
- pind->mvert[0] = &pind->mesh->mvert[pa->hair_index];
+ MVert *verts = BKE_mesh_verts_for_write(pind->mesh);
+ pind->mvert[0] = &verts[pa->hair_index];
pind->mvert[1] = pind->mvert[0] + 1;
}
}
@@ -1664,7 +1668,7 @@ static void interpolate_pathcache(ParticleCacheKey *first, float t, ParticleCach
/************************************************/
void psys_interpolate_face(Mesh *mesh,
- MVert *mvert,
+ const MVert *mvert,
const float (*vert_normals)[3],
MFace *mface,
MTFace *tface,
@@ -1676,7 +1680,7 @@ void psys_interpolate_face(Mesh *mesh,
float vtan[3],
float orco[3])
{
- float *v1 = 0, *v2 = 0, *v3 = 0, *v4 = 0;
+ const float *v1 = 0, *v2 = 0, *v3 = 0, *v4 = 0;
float e1[3], e2[3], s1, s2, t1, t2;
float *uv1, *uv2, *uv3, *uv4;
float n1[3], n2[3], n3[3], n4[3];
@@ -1852,7 +1856,8 @@ static float psys_interpolate_value_from_verts(
return values[index];
case PART_FROM_FACE:
case PART_FROM_VOLUME: {
- MFace *mf = &mesh->mface[index];
+ MFace *mfaces = CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ MFace *mf = &mfaces[index];
return interpolate_particle_value(
values[mf->v1], values[mf->v2], values[mf->v3], values[mf->v4], fw, mf->v4);
}
@@ -1941,7 +1946,7 @@ int psys_particle_dm_face_lookup(Mesh *mesh_final,
index_mf_to_mpoly_deformed = NULL;
- mtessface_final = mesh_final->mface;
+ mtessface_final = CustomData_get_layer(&mesh_final->fdata, CD_MFACE);
osface_final = CustomData_get_layer(&mesh_final->fdata, CD_ORIGSPACE);
if (osface_final == NULL) {
@@ -2061,7 +2066,8 @@ static int psys_map_index_on_dm(Mesh *mesh,
/* modify the original weights to become
* weights for the derived mesh face */
OrigSpaceFace *osface = CustomData_get_layer(&mesh->fdata, CD_ORIGSPACE);
- const MFace *mface = &mesh->mface[i];
+ const MFace *mfaces = CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ const MFace *mface = &mfaces[i];
if (osface == NULL) {
mapfw[0] = mapfw[1] = mapfw[2] = mapfw[3] = 0.0f;
@@ -2117,7 +2123,8 @@ void psys_particle_on_dm(Mesh *mesh_final,
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh_final);
if (from == PART_FROM_VERT) {
- copy_v3_v3(vec, mesh_final->mvert[mapindex].co);
+ const MVert *verts = BKE_mesh_verts(mesh_final);
+ copy_v3_v3(vec, verts[mapindex].co);
if (nor) {
copy_v3_v3(nor, vert_normals[mapindex]);
@@ -2143,9 +2150,10 @@ void psys_particle_on_dm(Mesh *mesh_final,
MTFace *mtface;
MVert *mvert;
- mface = &mesh_final->mface[mapindex];
- mvert = mesh_final->mvert;
- mtface = mesh_final->mtface;
+ MFace *mfaces = CustomData_get_layer(&mesh_final->fdata, CD_MFACE);
+ mface = &mfaces[mapindex];
+ mvert = BKE_mesh_verts_for_write(mesh_final);
+ mtface = CustomData_get_layer(&mesh_final->fdata, CD_MTFACE);
if (mtface) {
mtface += mapindex;
@@ -2634,7 +2642,7 @@ float *psys_cache_vgroup(Mesh *mesh, ParticleSystem *psys, int vgroup)
/* hair dynamics pinning vgroup */
}
else if (psys->vgroup[vgroup]) {
- MDeformVert *dvert = mesh->dvert;
+ const MDeformVert *dvert = BKE_mesh_deform_verts(mesh);
if (dvert) {
int totvert = mesh->totvert, i;
vg = MEM_callocN(sizeof(float) * totvert, "vg_cache");
@@ -3494,7 +3502,7 @@ void psys_cache_paths(ParticleSimulationData *sim, float cfra, const bool use_re
* initial tangent, but taking that in to account will allow
* the possibility of flipping again. -jahka
*/
- mat3_to_quat_is_ok(cache[p]->rot, rotmat);
+ mat3_to_quat_legacy(cache[p]->rot, rotmat);
}
psys->totcached = totpart;
@@ -3684,7 +3692,7 @@ static void psys_cache_edit_paths_iter(void *__restrict iter_data_v,
* initial tangent, but taking that in to account will allow
* the possibility of flipping again. -jahka
*/
- mat3_to_quat_is_ok(cache[iter]->rot, rotmat);
+ mat3_to_quat_legacy(cache[iter]->rot, rotmat);
}
}
@@ -3848,7 +3856,8 @@ static void psys_face_mat(Object *ob, Mesh *mesh, ParticleData *pa, float mat[4]
return;
}
- mface = &mesh->mface[i];
+ MFace *mfaces = CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ mface = &mfaces[i];
const OrigSpaceFace *osface = CustomData_get(&mesh->fdata, i, CD_ORIGSPACE);
if (orco && (orcodata = CustomData_get_layer(&mesh->vdata, CD_ORCO))) {
@@ -3863,9 +3872,10 @@ static void psys_face_mat(Object *ob, Mesh *mesh, ParticleData *pa, float mat[4]
}
}
else {
- copy_v3_v3(v[0], mesh->mvert[mface->v1].co);
- copy_v3_v3(v[1], mesh->mvert[mface->v2].co);
- copy_v3_v3(v[2], mesh->mvert[mface->v3].co);
+ const MVert *verts = BKE_mesh_verts(mesh);
+ copy_v3_v3(v[0], verts[mface->v1].co);
+ copy_v3_v3(v[1], verts[mface->v2].co);
+ copy_v3_v3(v[2], verts[mface->v3].co);
}
triatomat(v[0], v[1], v[2], (osface) ? osface->uv : NULL, mat);
@@ -4155,6 +4165,7 @@ static int get_particle_uv(Mesh *mesh,
float *texco,
bool from_vert)
{
+ MFace *mfaces = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
MFace *mf;
const MTFace *tf;
int i;
@@ -4162,10 +4173,6 @@ static int get_particle_uv(Mesh *mesh,
tf = CustomData_get_layer_named(&mesh->fdata, CD_MTFACE, name);
if (tf == NULL) {
- tf = mesh->mtface;
- }
-
- if (tf == NULL) {
return 0;
}
@@ -4186,7 +4193,7 @@ static int get_particle_uv(Mesh *mesh,
}
else {
if (from_vert) {
- mf = mesh->mface;
+ mf = mfaces;
/* This finds the first face to contain the emitting vertex,
* this is not ideal, but is mostly fine as UV seams generally
@@ -4199,7 +4206,7 @@ static int get_particle_uv(Mesh *mesh,
}
}
else {
- mf = &mesh->mface[i];
+ mf = &mfaces[i];
}
psys_interpolate_uvs(&tf[i], mf->v4, fuv, texco);
@@ -5413,8 +5420,8 @@ void BKE_particle_system_blend_read_lib(BlendLibReader *reader,
BLO_read_id_address(reader, id->lib, &psys->target_ob);
if (psys->clmd) {
- /* XXX(campbell): from reading existing code this seems correct but intended usage of
- * pointcache /w cloth should be added in 'ParticleSystem'. */
+ /* XXX(@campbellbarton): from reading existing code this seems correct but intended usage
+ * of point-cache with cloth should be added in #ParticleSystem. */
psys->clmd->point_cache = psys->pointcache;
psys->clmd->ptcaches.first = psys->clmd->ptcaches.last = NULL;
BLO_read_id_address(reader, id->lib, &psys->clmd->coll_parms->group);
@@ -5422,7 +5429,7 @@ void BKE_particle_system_blend_read_lib(BlendLibReader *reader,
}
}
else {
- /* particle modifier must be removed before particle system */
+ /* Particle modifier must be removed before particle system. */
ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
BKE_modifier_remove_from_list(ob, (ModifierData *)psmd);
BKE_modifier_free((ModifierData *)psmd);
diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c
index 524ee31229b..a890812cfc4 100644
--- a/source/blender/blenkernel/intern/particle_child.c
+++ b/source/blender/blenkernel/intern/particle_child.c
@@ -158,10 +158,8 @@ static void do_kink_spiral(ParticleThreadContext *ctx,
int start_index = 0, end_index = 0;
float kink_base[3];
- if (ptex) {
- kink_amp *= ptex->kink_amp;
- kink_freq *= ptex->kink_freq;
- }
+ kink_amp *= ptex->kink_amp;
+ kink_freq *= ptex->kink_freq;
cut_time = (totkeys - 1) * ptex->length;
zero_v3(spiral_start);
@@ -405,7 +403,7 @@ void do_kink(ParticleKey *state,
float obmat[4][4],
int smooth_start)
{
- float kink[3] = {1.0f, 0.0f, 0.0f}, par_vec[3], q1[4] = {1.0f, 0.0f, 0.0f, 0.0f};
+ float kink[3] = {1.0f, 0.0f, 0.0f}, par_vec[3];
float t, dt = 1.0f, result[3];
if (ELEM(type, PART_KINK_NO, PART_KINK_SPIRAL)) {
@@ -455,6 +453,7 @@ void do_kink(ParticleKey *state,
switch (type) {
case PART_KINK_CURL: {
float curl_offset[3];
+ float q1[4] = {1.0f, 0.0f, 0.0f, 0.0f};
/* rotate kink vector around strand tangent */
mul_v3_v3fl(curl_offset, kink, amplitude);
diff --git a/source/blender/blenkernel/intern/particle_distribute.c b/source/blender/blenkernel/intern/particle_distribute.c
index da769515f08..561043b553e 100644
--- a/source/blender/blenkernel/intern/particle_distribute.c
+++ b/source/blender/blenkernel/intern/particle_distribute.c
@@ -97,7 +97,7 @@ static void distribute_grid(Mesh *mesh, ParticleSystem *psys)
{
ParticleData *pa = NULL;
float min[3], max[3], delta[3], d;
- MVert *mv, *mvert = mesh->mvert;
+ MVert *mv, *mvert = BKE_mesh_verts_for_write(mesh);
int totvert = mesh->totvert, from = psys->part->from;
int i, j, k, p, res = psys->part->grid_res, size[3], axis;
@@ -181,7 +181,7 @@ static void distribute_grid(Mesh *mesh, ParticleSystem *psys)
int amax = from == PART_FROM_FACE ? 3 : 1;
totface = mesh->totface;
- mface = mface_array = mesh->mface;
+ mface = mface_array = CustomData_get_layer(&mesh->fdata, CD_MFACE);
for (a = 0; a < amax; a++) {
if (a == 0) {
@@ -453,7 +453,7 @@ static int distribute_binary_search(const float *sum, int n, float value)
return low;
}
-/* the max number if calls to rng_* funcs within psys_thread_distribute_particle
+/* the max number if calls to rng_* functions within psys_thread_distribute_particle
* be sure to keep up to date if this changes */
#define PSYS_RND_DIST_SKIP 3
@@ -464,7 +464,7 @@ static void distribute_from_verts_exec(ParticleTask *thread, ParticleData *pa, i
ParticleThreadContext *ctx = thread->ctx;
MFace *mface;
- mface = ctx->mesh->mface;
+ mface = CustomData_get_layer(&ctx->mesh->fdata, CD_MFACE);
int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls won't need skipping */
@@ -525,10 +525,11 @@ static void distribute_from_faces_exec(ParticleTask *thread, ParticleData *pa, i
int i;
int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls won't need skipping */
+ MFace *mfaces = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
MFace *mface;
pa->num = i = ctx->index[p];
- mface = &mesh->mface[i];
+ mface = &mfaces[i];
switch (distr) {
case PART_DISTR_JIT:
@@ -575,10 +576,11 @@ static void distribute_from_volume_exec(ParticleTask *thread, ParticleData *pa,
int rng_skip_tot = PSYS_RND_DIST_SKIP; /* count how many rng_* calls won't need skipping */
MFace *mface;
- MVert *mvert = mesh->mvert;
+ MVert *mvert = BKE_mesh_verts_for_write(mesh);
pa->num = i = ctx->index[p];
- mface = &mesh->mface[i];
+ MFace *mfaces = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ mface = &mfaces[i];
switch (distr) {
case PART_DISTR_JIT:
@@ -619,8 +621,8 @@ static void distribute_from_volume_exec(ParticleTask *thread, ParticleData *pa,
min_d = FLT_MAX;
intersect = 0;
-
- for (i = 0, mface = mesh->mface; i < tot; i++, mface++) {
+ mface = CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ for (i = 0; i < tot; i++, mface++) {
if (i == pa->num) {
continue;
}
@@ -689,7 +691,8 @@ static void distribute_children_exec(ParticleTask *thread, ChildParticle *cpa, i
return;
}
- mf = &mesh->mface[ctx->index[p]];
+ MFace *mfaces = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ mf = &mfaces[ctx->index[p]];
randu = BLI_rng_get_float(thread->rng);
randv = BLI_rng_get_float(thread->rng);
@@ -989,7 +992,7 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx,
BKE_mesh_orco_ensure(ob, mesh);
if (from == PART_FROM_VERT) {
- MVert *mv = mesh->mvert;
+ MVert *mv = BKE_mesh_verts_for_write(mesh);
const float(*orcodata)[3] = CustomData_get_layer(&mesh->vdata, CD_ORCO);
int totvert = mesh->totvert;
@@ -1042,8 +1045,9 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx,
orcodata = CustomData_get_layer(&mesh->vdata, CD_ORCO);
+ MFace *mfaces = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
for (i = 0; i < totelem; i++) {
- MFace *mf = &mesh->mface[i];
+ MFace *mf = &mfaces[i];
if (orcodata) {
/* Transform orcos from normalized 0..1 to object space. */
@@ -1059,14 +1063,15 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx,
}
}
else {
- v1 = &mesh->mvert[mf->v1];
- v2 = &mesh->mvert[mf->v2];
- v3 = &mesh->mvert[mf->v3];
+ MVert *verts = BKE_mesh_verts_for_write(mesh);
+ v1 = &verts[mf->v1];
+ v2 = &verts[mf->v2];
+ v3 = &verts[mf->v3];
copy_v3_v3(co1, v1->co);
copy_v3_v3(co2, v2->co);
copy_v3_v3(co3, v3->co);
if (mf->v4) {
- v4 = &mesh->mvert[mf->v4];
+ v4 = &verts[mf->v4];
copy_v3_v3(co4, v4->co);
}
}
@@ -1105,8 +1110,9 @@ static int psys_thread_context_init_distribute(ParticleThreadContext *ctx,
}
}
else { /* PART_FROM_FACE / PART_FROM_VOLUME */
+ MFace *mfaces = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
for (i = 0; i < totelem; i++) {
- MFace *mf = &mesh->mface[i];
+ MFace *mf = &mfaces[i];
tweight = vweight[mf->v1] + vweight[mf->v2] + vweight[mf->v3];
if (mf->v4) {
diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c
index 4a8f029beee..9608676a153 100644
--- a/source/blender/blenkernel/intern/particle_system.c
+++ b/source/blender/blenkernel/intern/particle_system.c
@@ -837,7 +837,7 @@ void psys_get_birth_coords(
cross_v3_v3v3(mat[1], mat[2], mat[0]);
/* apply rotation */
- mat3_to_quat_is_ok(q, mat);
+ mat3_to_quat_legacy(q, mat);
copy_qt_qt(state->rot, q);
}
else {
@@ -969,7 +969,7 @@ void psys_get_birth_coords(
float tmat[3][3];
/* NOTE: utan_local is not taken from 'utan', we calculate from rot_vec/vtan. */
- /* NOTE(campbell): it looks like rotation phase may be applied twice
+ /* NOTE(@campbellbarton): it looks like rotation phase may be applied twice
* (once with vtan, again below) however this isn't the case. */
float *rot_vec_local = tmat[0];
float *vtan_local = tmat[1];
@@ -3322,12 +3322,10 @@ static void hair_create_input_mesh(ParticleSimulationData *sim,
mesh = *r_mesh;
if (!mesh) {
*r_mesh = mesh = BKE_mesh_new_nomain(totpoint, totedge, 0, 0, 0);
- CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, mesh->totvert);
- BKE_mesh_update_customdata_pointers(mesh, false);
}
- mvert = mesh->mvert;
- medge = mesh->medge;
- dvert = mesh->dvert;
+ mvert = BKE_mesh_verts_for_write(mesh);
+ medge = BKE_mesh_edges_for_write(mesh);
+ dvert = BKE_mesh_deform_verts_for_write(mesh);
if (psys->clmd->hairdata == NULL) {
psys->clmd->hairdata = MEM_mallocN(sizeof(ClothHairData) * totpoint, "hair data");
diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c
index 00a4eee47e3..2bed18de6cf 100644
--- a/source/blender/blenkernel/intern/pbvh.c
+++ b/source/blender/blenkernel/intern/pbvh.c
@@ -145,9 +145,14 @@ static void update_node_vb(PBVH *pbvh, PBVHNode *node)
// BB_expand(&node->vb, co);
//}
-static bool face_materials_match(const MPoly *f1, const MPoly *f2)
+static bool face_materials_match(const PBVH *pbvh, const int a, const int b)
{
- return ((f1->flag & ME_SMOOTH) == (f2->flag & ME_SMOOTH) && (f1->mat_nr == f2->mat_nr));
+ if (pbvh->material_indices) {
+ if (pbvh->material_indices[a] != pbvh->material_indices[b]) {
+ return false;
+ }
+ }
+ return (pbvh->mpoly[a].flag & ME_SMOOTH) == (pbvh->mpoly[b].flag & ME_SMOOTH);
}
static bool grid_materials_match(const DMFlagMat *f1, const DMFlagMat *f2)
@@ -180,30 +185,23 @@ static int partition_indices(int *prim_indices, int lo, int hi, int axis, float
/* Returns the index of the first element on the right of the partition */
static int partition_indices_material(PBVH *pbvh, int lo, int hi)
{
- const MPoly *mpoly = pbvh->mpoly;
const MLoopTri *looptri = pbvh->looptri;
const DMFlagMat *flagmats = pbvh->grid_flag_mats;
const int *indices = pbvh->prim_indices;
- const void *first;
int i = lo, j = hi;
- if (pbvh->looptri) {
- first = &mpoly[looptri[pbvh->prim_indices[lo]].poly];
- }
- else {
- first = &flagmats[pbvh->prim_indices[lo]];
- }
-
for (;;) {
if (pbvh->looptri) {
- for (; face_materials_match(first, &mpoly[looptri[indices[i]].poly]); i++) {
+ const int first = looptri[pbvh->prim_indices[lo]].poly;
+ for (; face_materials_match(pbvh, first, looptri[indices[i]].poly); i++) {
/* pass */
}
- for (; !face_materials_match(first, &mpoly[looptri[indices[j]].poly]); j--) {
+ for (; !face_materials_match(pbvh, first, looptri[indices[j]].poly); j--) {
/* pass */
}
}
else {
+ const DMFlagMat *first = &flagmats[pbvh->prim_indices[lo]];
for (; grid_materials_match(first, &flagmats[indices[i]]); i++) {
/* pass */
}
@@ -287,7 +285,7 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node)
}
if (has_visible == false) {
- if (!paint_is_face_hidden(lt, pbvh->verts, pbvh->mloop)) {
+ if (!paint_is_face_hidden(lt, pbvh->hide_poly)) {
has_visible = true;
}
}
@@ -368,26 +366,6 @@ int BKE_pbvh_count_grid_quads(BLI_bitmap **grid_hidden,
return totquad;
}
-void BKE_pbvh_sync_face_sets_to_grids(PBVH *pbvh)
-{
- const int gridsize = pbvh->gridkey.grid_size;
- for (int i = 0; i < pbvh->totgrid; i++) {
- BLI_bitmap *gh = pbvh->grid_hidden[i];
- const int face_index = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, i);
- if (!gh && pbvh->face_sets[face_index] < 0) {
- gh = pbvh->grid_hidden[i] = BLI_BITMAP_NEW(pbvh->gridkey.grid_area,
- "partialvis_update_grids");
- }
- if (gh) {
- for (int y = 0; y < gridsize; y++) {
- for (int x = 0; x < gridsize; x++) {
- BLI_BITMAP_SET(gh, y * gridsize + x, pbvh->face_sets[face_index] < 0);
- }
- }
- }
- }
-}
-
static void build_grid_leaf_node(PBVH *pbvh, PBVHNode *node)
{
int totquads = BKE_pbvh_count_grid_quads(
@@ -424,12 +402,9 @@ static bool leaf_needs_material_split(PBVH *pbvh, int offset, int count)
if (pbvh->looptri) {
const MLoopTri *first = &pbvh->looptri[pbvh->prim_indices[offset]];
- const MPoly *mp = &pbvh->mpoly[first->poly];
-
for (int i = offset + count - 1; i > offset; i--) {
int prim = pbvh->prim_indices[i];
- const MPoly *mp_other = &pbvh->mpoly[pbvh->looptri[prim].poly];
- if (!face_materials_match(mp, mp_other)) {
+ if (!face_materials_match(pbvh, first->poly, pbvh->looptri[prim].poly)) {
return true;
}
}
@@ -555,13 +530,17 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
BB cb;
pbvh->mesh = mesh;
- pbvh->type = PBVH_FACES;
+ pbvh->header.type = PBVH_FACES;
pbvh->mpoly = mpoly;
+ pbvh->hide_poly = (bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly");
+ pbvh->material_indices = (const int *)CustomData_get_layer_named(
+ &mesh->pdata, CD_PROP_INT32, "material_index");
pbvh->mloop = mloop;
pbvh->looptri = looptri;
pbvh->verts = verts;
BKE_mesh_vertex_normals_ensure(mesh);
pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(mesh);
+ pbvh->hide_vert = (bool *)CustomData_get_layer_named(&mesh->vdata, CD_PROP_BOOL, ".hide_vert");
pbvh->vert_bitmap = MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap");
pbvh->totvert = totvert;
pbvh->leaf_limit = LEAF_LIMIT;
@@ -615,7 +594,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
{
const int gridsize = key->grid_size;
- pbvh->type = PBVH_GRIDS;
+ pbvh->header.type = PBVH_GRIDS;
pbvh->grids = grids;
pbvh->gridfaces = gridfaces;
pbvh->grid_flag_mats = flagmats;
@@ -652,11 +631,12 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
MEM_freeN(prim_bbc);
}
-PBVH *BKE_pbvh_new(void)
+PBVH *BKE_pbvh_new(PBVHType type)
{
PBVH *pbvh = MEM_callocN(sizeof(PBVH), "pbvh");
pbvh->respect_hide = true;
pbvh->draw_cache_invalid = true;
+ pbvh->header.type = type;
return pbvh;
}
@@ -1274,7 +1254,7 @@ bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, eAttrDo
if (!layer || !ELEM(layer->type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR)) {
*r_layer = NULL;
- *r_attr = ATTR_DOMAIN_NUM;
+ *r_attr = ATTR_DOMAIN_POINT;
return false;
}
@@ -1282,7 +1262,7 @@ bool BKE_pbvh_get_color_layer(const Mesh *me, CustomDataLayer **r_layer, eAttrDo
if (!ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) {
*r_layer = NULL;
- *r_attr = ATTR_DOMAIN_NUM;
+ *r_attr = ATTR_DOMAIN_POINT;
return false;
}
@@ -1303,19 +1283,8 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
PBVH *pbvh = data->pbvh;
PBVHNode *node = data->nodes[n];
- CustomData *vdata, *ldata;
-
- if (!pbvh->bm) {
- vdata = pbvh->vdata;
- ldata = pbvh->ldata;
- }
- else {
- vdata = &pbvh->bm->vdata;
- ldata = &pbvh->bm->ldata;
- }
-
if (node->flag & PBVH_RebuildDrawBuffers) {
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_GRIDS: {
bool smooth = node->totprim > 0 ?
pbvh->grid_flag_mats[node->prim_indices[0]].flag & ME_SMOOTH :
@@ -1326,14 +1295,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
}
case PBVH_FACES:
node->draw_buffers = GPU_pbvh_mesh_buffers_build(
- pbvh->mpoly,
- pbvh->mloop,
- pbvh->looptri,
- pbvh->verts,
- node->prim_indices,
- CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS),
- node->totprim,
- pbvh->mesh);
+ pbvh->mesh, pbvh->looptri, node->prim_indices, node->totprim);
break;
case PBVH_BMESH:
node->draw_buffers = GPU_pbvh_bmesh_buffers_build(pbvh->flags &
@@ -1343,8 +1305,10 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
}
if (node->flag & PBVH_UpdateDrawBuffers) {
+ node->debug_draw_gen++;
+
const int update_flags = pbvh_get_buffers_update_flags(pbvh);
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_GRIDS:
GPU_pbvh_grid_buffers_update(pbvh->vbo_id,
node->draw_buffers,
@@ -1360,11 +1324,12 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
update_flags);
break;
case PBVH_FACES: {
+ /* Pass vertices separately because they may be not be the same as the mesh's vertices,
+ * and pass normals separately because they are managed by the PBVH. */
GPU_pbvh_mesh_buffers_update(pbvh->vbo_id,
node->draw_buffers,
+ pbvh->mesh,
pbvh->verts,
- vdata,
- ldata,
CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK),
CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS),
pbvh->face_sets_color_seed,
@@ -1376,7 +1341,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata,
case PBVH_BMESH:
GPU_pbvh_bmesh_buffers_update(pbvh->vbo_id,
node->draw_buffers,
- pbvh->bm,
+ pbvh->header.bm,
node->bm_faces,
node->bm_unique_verts,
node->bm_other_verts,
@@ -1405,15 +1370,15 @@ static void pbvh_check_draw_layout(PBVH *pbvh)
pbvh->vbo_id = GPU_pbvh_make_format();
}
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_BMESH:
- if (!pbvh->bm) {
+ if (!pbvh->header.bm) {
/* BMesh hasn't been created yet */
return;
}
- vdata = &pbvh->bm->vdata;
- ldata = &pbvh->bm->ldata;
+ vdata = &pbvh->header.bm->vdata;
+ ldata = &pbvh->header.bm->ldata;
break;
case PBVH_FACES:
vdata = pbvh->vdata;
@@ -1431,7 +1396,7 @@ static void pbvh_check_draw_layout(PBVH *pbvh)
* (there's no guarantee there isn't another EEVEE viewport which would
* free the draw buffers and corrupt the draw cache).
*/
- if (GPU_pbvh_attribute_names_update(pbvh->type, pbvh->vbo_id, vdata, ldata, false)) {
+ if (GPU_pbvh_attribute_names_update(pbvh->header.type, pbvh->vbo_id, vdata, ldata, false)) {
/* attribute layout changed; force rebuild */
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
@@ -1451,14 +1416,14 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode,
pbvh->vbo_id = GPU_pbvh_make_format();
}
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_BMESH:
- if (!pbvh->bm) {
+ if (!pbvh->header.bm) {
/* BMesh hasn't been created yet */
return;
}
- vdata = &pbvh->bm->vdata;
+ vdata = &pbvh->header.bm->vdata;
break;
case PBVH_FACES:
vdata = pbvh->vdata;
@@ -1469,7 +1434,7 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode,
}
UNUSED_VARS(vdata);
- if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->type, PBVH_GRIDS, PBVH_BMESH)) {
+ if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->header.type, PBVH_GRIDS, PBVH_BMESH)) {
/* Free buffers uses OpenGL, so not in parallel. */
for (int n = 0; n < totnode; n++) {
PBVHNode *node = nodes[n];
@@ -1477,11 +1442,11 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode,
pbvh_free_draw_buffers(pbvh, node);
}
else if ((node->flag & PBVH_UpdateDrawBuffers) && node->draw_buffers) {
- if (pbvh->type == PBVH_GRIDS) {
+ if (pbvh->header.type == PBVH_GRIDS) {
GPU_pbvh_grid_buffers_update_free(
node->draw_buffers, pbvh->grid_flag_mats, node->prim_indices);
}
- else if (pbvh->type == PBVH_BMESH) {
+ else if (pbvh->header.type == PBVH_BMESH) {
GPU_pbvh_bmesh_buffers_update_free(node->draw_buffers);
}
}
@@ -1602,9 +1567,12 @@ static void pbvh_faces_node_visibility_update(PBVH *pbvh, PBVHNode *node)
BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert);
BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
+ if (pbvh->hide_vert == NULL) {
+ BKE_pbvh_node_fully_hidden_set(node, false);
+ return;
+ }
for (i = 0; i < totvert; i++) {
- MVert *v = &mvert[vert_indices[i]];
- if (!(v->flag & ME_HIDE)) {
+ if (!(pbvh->hide_vert[vert_indices[i]])) {
BKE_pbvh_node_fully_hidden_set(node, false);
return;
}
@@ -1795,15 +1763,10 @@ void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int
/***************************** PBVH Access ***********************************/
-PBVHType BKE_pbvh_type(const PBVH *pbvh)
-{
- return pbvh->type;
-}
-
bool BKE_pbvh_has_faces(const PBVH *pbvh)
{
- if (pbvh->type == PBVH_BMESH) {
- return (pbvh->bm->totface != 0);
+ if (pbvh->header.type == PBVH_BMESH) {
+ return (pbvh->header.bm->totface != 0);
}
return (pbvh->totprim != 0);
@@ -1824,46 +1787,40 @@ void BKE_pbvh_bounding_box(const PBVH *pbvh, float min[3], float max[3])
BLI_bitmap **BKE_pbvh_grid_hidden(const PBVH *pbvh)
{
- BLI_assert(pbvh->type == PBVH_GRIDS);
+ BLI_assert(pbvh->header.type == PBVH_GRIDS);
return pbvh->grid_hidden;
}
const CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh)
{
- BLI_assert(pbvh->type == PBVH_GRIDS);
+ BLI_assert(pbvh->header.type == PBVH_GRIDS);
return &pbvh->gridkey;
}
struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh)
{
- BLI_assert(pbvh->type == PBVH_GRIDS);
+ BLI_assert(pbvh->header.type == PBVH_GRIDS);
return pbvh->grids;
}
BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh)
{
- BLI_assert(pbvh->type == PBVH_GRIDS);
+ BLI_assert(pbvh->header.type == PBVH_GRIDS);
return pbvh->grid_hidden;
}
-int BKE_pbvh_get_grid_num_vertices(const PBVH *pbvh)
+int BKE_pbvh_get_grid_num_verts(const PBVH *pbvh)
{
- BLI_assert(pbvh->type == PBVH_GRIDS);
+ BLI_assert(pbvh->header.type == PBVH_GRIDS);
return pbvh->totgrid * pbvh->gridkey.grid_area;
}
int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh)
{
- BLI_assert(pbvh->type == PBVH_GRIDS);
+ BLI_assert(pbvh->header.type == PBVH_GRIDS);
return pbvh->totgrid * (pbvh->gridkey.grid_size - 1) * (pbvh->gridkey.grid_size - 1);
}
-BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh)
-{
- BLI_assert(pbvh->type == PBVH_BMESH);
- return pbvh->bm;
-}
-
/***************************** Node Access ***********************************/
void BKE_pbvh_node_mark_update(PBVHNode *node)
@@ -1964,10 +1921,10 @@ bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node)
return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyUnmasked);
}
-void BKE_pbvh_vert_mark_update(PBVH *pbvh, int index)
+void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex)
{
- BLI_assert(pbvh->type == PBVH_FACES);
- pbvh->vert_bitmap[index] = true;
+ BLI_assert(pbvh->header.type == PBVH_FACES);
+ pbvh->vert_bitmap[vertex.i] = true;
}
void BKE_pbvh_node_get_loops(PBVH *pbvh,
@@ -2004,7 +1961,7 @@ void BKE_pbvh_node_num_verts(PBVH *pbvh, PBVHNode *node, int *r_uniquevert, int
{
int tot;
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_GRIDS:
tot = node->totprim * pbvh->gridkey.grid_area;
if (r_totvert) {
@@ -2042,7 +1999,7 @@ void BKE_pbvh_node_get_grids(PBVH *pbvh,
int *r_gridsize,
CCGElem ***r_griddata)
{
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_GRIDS:
if (r_grid_indices) {
*r_grid_indices = node->prim_indices;
@@ -2123,9 +2080,9 @@ void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node,
*r_orco_coords = node->bm_orco;
}
-bool BKE_pbvh_node_vert_update_check_any(PBVH *pbvh, PBVHNode *node)
+bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node)
{
- BLI_assert(pbvh->type == PBVH_FACES);
+ BLI_assert(pbvh->header.type == PBVH_FACES);
const int *verts = node->vert_indices;
const int totvert = node->uniq_verts + node->face_verts;
@@ -2299,7 +2256,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh,
const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
float *depth,
- int *r_active_vertex_index,
+ PBVHVertRef *r_active_vertex,
int *r_active_face_index,
float *r_face_normal)
{
@@ -2314,7 +2271,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh,
const MLoopTri *lt = &pbvh->looptri[faces[i]];
const int *face_verts = node->face_vert_indices[i];
- if (pbvh->respect_hide && paint_is_face_hidden(lt, vert, mloop)) {
+ if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) {
continue;
}
@@ -2339,7 +2296,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh,
normal_tri_v3(r_face_normal, co[0], co[1], co[2]);
}
- if (r_active_vertex_index) {
+ if (r_active_vertex) {
float location[3] = {0.0f};
madd_v3_v3v3fl(location, ray_start, ray_normal, *depth);
for (int j = 0; j < 3; j++) {
@@ -2349,7 +2306,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh,
if (j == 0 ||
len_squared_v3v3(location, co[j]) < len_squared_v3v3(location, nearest_vertex_co)) {
copy_v3_v3(nearest_vertex_co, co[j]);
- *r_active_vertex_index = mloop[lt->tri[j]].v;
+ r_active_vertex->i = mloop[lt->tri[j]].v;
*r_active_face_index = lt->poly;
}
}
@@ -2367,7 +2324,7 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh,
const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
float *depth,
- int *r_active_vertex_index,
+ PBVHVertRef *r_active_vertex,
int *r_active_grid_index,
float *r_face_normal)
{
@@ -2419,12 +2376,12 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh,
normal_quad_v3(r_face_normal, co[0], co[1], co[2], co[3]);
}
- if (r_active_vertex_index) {
+ if (r_active_vertex) {
float location[3] = {0.0};
madd_v3_v3v3fl(location, ray_start, ray_normal, *depth);
const int x_it[4] = {0, 1, 1, 0};
- const int y_it[4] = {0, 0, 1, 1};
+ const int y_it[4] = {1, 1, 0, 0};
for (int j = 0; j < 4; j++) {
/* Always assign nearest_vertex_co in the first iteration to avoid comparison against
@@ -2434,8 +2391,8 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh,
len_squared_v3v3(location, nearest_vertex_co)) {
copy_v3_v3(nearest_vertex_co, co[j]);
- *r_active_vertex_index = gridkey->grid_area * grid_index +
- (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]);
+ r_active_vertex->i = gridkey->grid_area * grid_index +
+ (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]);
}
}
}
@@ -2462,7 +2419,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
const float ray_normal[3],
struct IsectRayPrecalc *isect_precalc,
float *depth,
- int *active_vertex_index,
+ PBVHVertRef *active_vertex,
int *active_face_grid_index,
float *face_normal)
{
@@ -2472,7 +2429,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
return false;
}
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_FACES:
hit |= pbvh_faces_node_raycast(pbvh,
node,
@@ -2481,7 +2438,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
ray_normal,
isect_precalc,
depth,
- active_vertex_index,
+ active_vertex,
active_face_grid_index,
face_normal);
break;
@@ -2493,19 +2450,19 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh,
ray_normal,
isect_precalc,
depth,
- active_vertex_index,
+ active_vertex,
active_face_grid_index,
face_normal);
break;
case PBVH_BMESH:
- BM_mesh_elem_index_ensure(pbvh->bm, BM_VERT);
+ BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT);
hit = pbvh_bmesh_node_raycast(node,
ray_start,
ray_normal,
isect_precalc,
depth,
use_origco,
- active_vertex_index,
+ active_vertex,
face_normal);
break;
}
@@ -2623,7 +2580,7 @@ static bool pbvh_faces_node_nearest_to_ray(PBVH *pbvh,
const MLoopTri *lt = &pbvh->looptri[faces[i]];
const int *face_verts = node->face_vert_indices[i];
- if (pbvh->respect_hide && paint_is_face_hidden(lt, vert, mloop)) {
+ if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_poly)) {
continue;
}
@@ -2729,7 +2686,7 @@ bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh,
return false;
}
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_FACES:
hit |= pbvh_faces_node_nearest_to_ray(
pbvh, node, origco, ray_start, ray_normal, depth, dist_sq);
@@ -2820,13 +2777,13 @@ void BKE_pbvh_update_normals(PBVH *pbvh, struct SubdivCCG *subdiv_ccg)
pbvh, update_search_cb, POINTER_FROM_INT(PBVH_UpdateNormals), &nodes, &totnode);
if (totnode > 0) {
- if (pbvh->type == PBVH_BMESH) {
+ if (pbvh->header.type == PBVH_BMESH) {
pbvh_bmesh_normals_update(nodes, totnode);
}
- else if (pbvh->type == PBVH_FACES) {
+ else if (pbvh->header.type == PBVH_FACES) {
pbvh_faces_update_normals(pbvh, nodes, totnode);
}
- else if (pbvh->type == PBVH_GRIDS) {
+ else if (pbvh->header.type == PBVH_GRIDS) {
struct CCGFace **faces;
int num_faces;
BKE_pbvh_get_grid_updates(pbvh, true, (void ***)&faces, &num_faces);
@@ -2917,15 +2874,18 @@ void BKE_pbvh_draw_cb(PBVH *pbvh,
MEM_SAFE_FREE(nodes);
}
-void BKE_pbvh_draw_debug_cb(
- PBVH *pbvh,
- void (*draw_fn)(void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag),
- void *user_data)
+void BKE_pbvh_draw_debug_cb(PBVH *pbvh,
+ void (*draw_fn)(PBVHNode *node,
+ void *user_data,
+ const float bmin[3],
+ const float bmax[3],
+ PBVHNodeFlags flag),
+ void *user_data)
{
for (int a = 0; a < pbvh->totnode; a++) {
PBVHNode *node = &pbvh->nodes[a];
- draw_fn(user_data, node->vb.bmin, node->vb.bmax, node->flag);
+ draw_fn(node, user_data, node->vb.bmin, node->vb.bmax, node->flag);
}
}
@@ -2991,7 +2951,7 @@ void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], const int
/* no need for float comparison here (memory is exactly equal or not) */
if (memcmp(mvert->co, vertCos[a], sizeof(float[3])) != 0) {
copy_v3_v3(mvert->co, vertCos[a]);
- BKE_pbvh_vert_mark_update(pbvh, a);
+ BKE_pbvh_vert_tag_update_normal(pbvh, BKE_pbvh_make_vref(a));
}
}
@@ -3108,6 +3068,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->no = NULL;
vi->fno = NULL;
vi->mvert = NULL;
+ vi->vertex.i = 0LL;
vi->respect_hide = pbvh->respect_hide;
if (pbvh->respect_hide == false) {
@@ -3134,10 +3095,10 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
vi->vert_indices = vert_indices;
vi->mverts = verts;
- if (pbvh->type == PBVH_BMESH) {
+ if (pbvh->header.type == PBVH_BMESH) {
BLI_gsetIterator_init(&vi->bm_unique_verts, node->bm_unique_verts);
BLI_gsetIterator_init(&vi->bm_other_verts, node->bm_other_verts);
- vi->bm_vdata = &pbvh->bm->vdata;
+ vi->bm_vdata = &pbvh->header.bm->vdata;
vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK);
}
@@ -3147,8 +3108,9 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
}
vi->mask = NULL;
- if (pbvh->type == PBVH_FACES) {
+ if (pbvh->header.type == PBVH_FACES) {
vi->vert_normals = pbvh->vert_normals;
+ vi->hide_vert = pbvh->hide_vert;
vi->vmask = CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK);
}
@@ -3156,13 +3118,14 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m
bool pbvh_has_mask(const PBVH *pbvh)
{
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_GRIDS:
return (pbvh->gridkey.has_mask != 0);
case PBVH_FACES:
return (pbvh->vdata && CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK));
case PBVH_BMESH:
- return (pbvh->bm && (CustomData_get_offset(&pbvh->bm->vdata, CD_PAINT_MASK) != -1));
+ return (pbvh->header.bm &&
+ (CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK) != -1));
}
return false;
@@ -3170,7 +3133,7 @@ bool pbvh_has_mask(const PBVH *pbvh)
bool pbvh_has_face_sets(PBVH *pbvh)
{
- switch (pbvh->type) {
+ switch (pbvh->header.type) {
case PBVH_GRIDS:
return (pbvh->pdata && CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS));
case PBVH_FACES:
@@ -3218,16 +3181,43 @@ void BKE_pbvh_parallel_range_settings(TaskParallelSettings *settings,
MVert *BKE_pbvh_get_verts(const PBVH *pbvh)
{
- BLI_assert(pbvh->type == PBVH_FACES);
+ BLI_assert(pbvh->header.type == PBVH_FACES);
return pbvh->verts;
}
const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3]
{
- BLI_assert(pbvh->type == PBVH_FACES);
+ BLI_assert(pbvh->header.type == PBVH_FACES);
return pbvh->vert_normals;
}
+const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh)
+{
+ BLI_assert(pbvh->header.type == PBVH_FACES);
+ return pbvh->hide_vert;
+}
+
+const bool *BKE_pbvh_get_poly_hide(const PBVH *pbvh)
+{
+ BLI_assert(ELEM(pbvh->header.type, PBVH_FACES, PBVH_GRIDS));
+ return pbvh->hide_poly;
+}
+
+bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh)
+{
+ BLI_assert(pbvh->header.type == PBVH_FACES);
+ if (pbvh->hide_vert) {
+ return pbvh->hide_vert;
+ }
+ pbvh->hide_vert = CustomData_get_layer_named(&pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert");
+ if (pbvh->hide_vert) {
+ return pbvh->hide_vert;
+ }
+ pbvh->hide_vert = (bool *)CustomData_add_layer_named(
+ &pbvh->mesh->vdata, CD_PROP_BOOL, CD_SET_DEFAULT, NULL, pbvh->mesh->totvert, ".hide_vert");
+ return pbvh->hide_vert;
+}
+
void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg)
{
pbvh->subdiv_ccg = subdiv_ccg;
@@ -3238,10 +3228,19 @@ void BKE_pbvh_face_sets_set(PBVH *pbvh, int *face_sets)
pbvh->face_sets = face_sets;
}
+void BKE_pbvh_update_hide_attributes_from_mesh(PBVH *pbvh)
+{
+ if (pbvh->header.type == PBVH_FACES) {
+ pbvh->hide_vert = CustomData_get_layer_named(&pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert");
+ pbvh->hide_poly = CustomData_get_layer_named(&pbvh->mesh->pdata, CD_PROP_BOOL, ".hide_poly");
+ }
+}
+
void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide)
{
pbvh->respect_hide = respect_hide;
}
+
bool BKE_pbvh_is_drawing(const PBVH *pbvh)
{
return pbvh->is_drawing;
@@ -3325,3 +3324,8 @@ void BKE_pbvh_ensure_node_loops(PBVH *pbvh)
MEM_SAFE_FREE(visit);
}
+
+int BKE_pbvh_debug_draw_gen_get(PBVHNode *node)
+{
+ return node->debug_draw_gen;
+}
diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc
index dec93826b9b..70aeb10f087 100644
--- a/source/blender/blenkernel/intern/pbvh.cc
+++ b/source/blender/blenkernel/intern/pbvh.cc
@@ -86,10 +86,12 @@ template<> void from_float(const float src[4], MPropCol &dst)
}
template<typename T>
-static void pbvh_vertex_color_get(const PBVH &pbvh, int vertex, float r_color[4])
+static void pbvh_vertex_color_get(const PBVH &pbvh, PBVHVertRef vertex, float r_color[4])
{
+ int index = vertex.i;
+
if (pbvh.color_domain == ATTR_DOMAIN_CORNER) {
- const MeshElemMap &melem = pbvh.pmap[vertex];
+ const MeshElemMap &melem = pbvh.pmap[index];
int count = 0;
zero_v4(r_color);
@@ -99,7 +101,7 @@ static void pbvh_vertex_color_get(const PBVH &pbvh, int vertex, float r_color[4]
Span<MLoop> loops{pbvh.mloop + mp.loopstart, mp.totloop};
for (const int i_loop : IndexRange(mp.totloop)) {
- if (loops[i_loop].v == vertex) {
+ if (loops[i_loop].v == index) {
float temp[4];
to_float(colors[i_loop], temp);
@@ -114,15 +116,17 @@ static void pbvh_vertex_color_get(const PBVH &pbvh, int vertex, float r_color[4]
}
}
else {
- to_float(static_cast<T *>(pbvh.color_layer->data)[vertex], r_color);
+ to_float(static_cast<T *>(pbvh.color_layer->data)[index], r_color);
}
}
template<typename T>
-static void pbvh_vertex_color_set(PBVH &pbvh, int vertex, const float color[4])
+static void pbvh_vertex_color_set(PBVH &pbvh, PBVHVertRef vertex, const float color[4])
{
+ int index = vertex.i;
+
if (pbvh.color_domain == ATTR_DOMAIN_CORNER) {
- const MeshElemMap &melem = pbvh.pmap[vertex];
+ const MeshElemMap &melem = pbvh.pmap[index];
for (const int i_poly : Span(melem.indices, melem.count)) {
const MPoly &mp = pbvh.mpoly[i_poly];
@@ -130,21 +134,21 @@ static void pbvh_vertex_color_set(PBVH &pbvh, int vertex, const float color[4])
Span<MLoop> loops{pbvh.mloop + mp.loopstart, mp.totloop};
for (const int i_loop : IndexRange(mp.totloop)) {
- if (loops[i_loop].v == vertex) {
+ if (loops[i_loop].v == index) {
from_float(color, colors[i_loop]);
}
}
}
}
else {
- from_float(color, static_cast<T *>(pbvh.color_layer->data)[vertex]);
+ from_float(color, static_cast<T *>(pbvh.color_layer->data)[index]);
}
}
} // namespace blender::bke
extern "C" {
-void BKE_pbvh_vertex_color_get(const PBVH *pbvh, int vertex, float r_color[4])
+void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_color[4])
{
blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) {
using T = decltype(dummy);
@@ -152,7 +156,7 @@ void BKE_pbvh_vertex_color_get(const PBVH *pbvh, int vertex, float r_color[4])
});
}
-void BKE_pbvh_vertex_color_set(PBVH *pbvh, int vertex, const float color[4])
+void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color[4])
{
blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) {
using T = decltype(dummy);
@@ -202,7 +206,7 @@ void BKE_pbvh_store_colors_vertex(PBVH *pbvh,
blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) {
using T = decltype(dummy);
for (const int i : IndexRange(indices_num)) {
- blender::bke::pbvh_vertex_color_get<T>(*pbvh, indices[i], r_colors[i]);
+ blender::bke::pbvh_vertex_color_get<T>(*pbvh, BKE_pbvh_make_vref(indices[i]), r_colors[i]);
}
});
}
diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c
index 112fd01c699..70d442021fe 100644
--- a/source/blender/blenkernel/intern/pbvh_bmesh.c
+++ b/source/blender/blenkernel/intern/pbvh_bmesh.c
@@ -400,7 +400,7 @@ static bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index)
BM_elem_index_set(f, i); /* set_dirty! */
}
/* Likely this is already dirty. */
- pbvh->bm->elem_index_dirty |= BM_FACE;
+ pbvh->header.bm->elem_index_dirty |= BM_FACE;
pbvh_bmesh_node_split(pbvh, bbc_array, node_index);
@@ -484,8 +484,8 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh,
BLI_assert((pbvh->totnode == 1 || node_index) && node_index <= pbvh->totnode);
/* avoid initializing customdata because its quite involved */
- BMVert *v = BM_vert_create(pbvh->bm, co, NULL, BM_CREATE_SKIP_CD);
- CustomData_bmesh_set_default(&pbvh->bm->vdata, &v->head.data);
+ BMVert *v = BM_vert_create(pbvh->header.bm, co, NULL, BM_CREATE_SKIP_CD);
+ CustomData_bmesh_set_default(&pbvh->header.bm->vdata, &v->head.data);
/* This value is logged below */
copy_v3_v3(v->no, no);
@@ -512,7 +512,7 @@ static BMFace *pbvh_bmesh_face_create(
/* ensure we never add existing face */
BLI_assert(!BM_face_exists(v_tri, 3));
- BMFace *f = BM_face_create(pbvh->bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP);
+ BMFace *f = BM_face_create(pbvh->header.bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP);
f->head.hflag = f_example->head.hflag;
BLI_gset_insert(node->bm_faces, f);
@@ -1185,22 +1185,22 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
v_tri[0] = v1;
v_tri[1] = v_new;
v_tri[2] = v_opp;
- bm_edges_from_tri(pbvh->bm, v_tri, e_tri);
+ bm_edges_from_tri(pbvh->header.bm, v_tri, e_tri);
f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj);
long_edge_queue_face_add(eq_ctx, f_new);
v_tri[0] = v_new;
v_tri[1] = v2;
/* v_tri[2] = v_opp; */ /* unchanged */
- e_tri[0] = BM_edge_create(pbvh->bm, v_tri[0], v_tri[1], NULL, BM_CREATE_NO_DOUBLE);
+ e_tri[0] = BM_edge_create(pbvh->header.bm, v_tri[0], v_tri[1], NULL, BM_CREATE_NO_DOUBLE);
e_tri[2] = e_tri[1]; /* switched */
- e_tri[1] = BM_edge_create(pbvh->bm, v_tri[1], v_tri[2], NULL, BM_CREATE_NO_DOUBLE);
+ e_tri[1] = BM_edge_create(pbvh->header.bm, v_tri[1], v_tri[2], NULL, BM_CREATE_NO_DOUBLE);
f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj);
long_edge_queue_face_add(eq_ctx, f_new);
/* Delete original */
pbvh_bmesh_face_remove(pbvh, f_adj);
- BM_face_kill(pbvh->bm, f_adj);
+ BM_face_kill(pbvh->header.bm, f_adj);
/* Ensure new vertex is in the node */
if (!BLI_gset_haskey(pbvh->nodes[ni].bm_unique_verts, v_new)) {
@@ -1217,7 +1217,7 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx,
}
}
- BM_edge_kill(pbvh->bm, e);
+ BM_edge_kill(pbvh->header.bm, e);
}
static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx,
@@ -1303,12 +1303,12 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
BMFace *f_adj = l_adj->f;
pbvh_bmesh_face_remove(pbvh, f_adj);
- BM_face_kill(pbvh->bm, f_adj);
+ BM_face_kill(pbvh->header.bm, f_adj);
}
/* Kill the edge */
BLI_assert(BM_edge_is_wire(e));
- BM_edge_kill(pbvh->bm, e);
+ BM_edge_kill(pbvh->header.bm, e);
/* For all remaining faces of v_del, create a new face that is the
* same except it uses v_conn instead of v_del */
@@ -1355,7 +1355,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
BMEdge *e_tri[3];
PBVHNode *n = pbvh_bmesh_node_from_face(pbvh, f);
int ni = n - pbvh->nodes;
- bm_edges_from_tri(pbvh->bm, v_tri, e_tri);
+ bm_edges_from_tri(pbvh->header.bm, v_tri, e_tri);
pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f);
/* Ensure that v_conn is in the new face's node */
@@ -1388,13 +1388,13 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
/* Remove the face */
pbvh_bmesh_face_remove(pbvh, f_del);
- BM_face_kill(pbvh->bm, f_del);
+ BM_face_kill(pbvh->header.bm, f_del);
/* Check if any of the face's edges are now unused by any
* face, if so delete them */
for (int j = 0; j < 3; j++) {
if (BM_edge_is_wire(e_tri[j])) {
- BM_edge_kill(pbvh->bm, e_tri[j]);
+ BM_edge_kill(pbvh->header.bm, e_tri[j]);
}
}
@@ -1410,7 +1410,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
v_conn = NULL;
}
BLI_ghash_insert(deleted_verts, v_tri[j], NULL);
- BM_vert_kill(pbvh->bm, v_tri[j]);
+ BM_vert_kill(pbvh->header.bm, v_tri[j]);
}
}
}
@@ -1437,7 +1437,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh,
BM_log_vert_removed(pbvh->bm_log, v_del, eq_ctx->cd_vert_mask_offset);
/* v_conn == NULL is OK */
BLI_ghash_insert(deleted_verts, v_del, v_conn);
- BM_vert_kill(pbvh->bm, v_del);
+ BM_vert_kill(pbvh->header.bm, v_del);
}
static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx,
@@ -1501,7 +1501,7 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node,
struct IsectRayPrecalc *isect_precalc,
float *depth,
bool use_original,
- int *r_active_vertex_index,
+ PBVHVertRef *r_active_vertex,
float *r_face_normal)
{
bool hit = false;
@@ -1538,14 +1538,14 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node,
normal_tri_v3(r_face_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co);
}
- if (r_active_vertex_index) {
+ if (r_active_vertex) {
float location[3] = {0.0f};
madd_v3_v3v3fl(location, ray_start, ray_normal, *depth);
for (int j = 0; j < 3; j++) {
if (len_squared_v3v3(location, v_tri[j]->co) <
len_squared_v3v3(location, nearest_vertex_co)) {
copy_v3_v3(nearest_vertex_co, v_tri[j]->co);
- *r_active_vertex_index = BM_elem_index_get(v_tri[j]);
+ r_active_vertex->i = (intptr_t)v_tri[j];
}
}
}
@@ -1863,6 +1863,12 @@ static void pbvh_bmesh_create_nodes_fast_recursive(
/***************************** Public API *****************************/
+void BKE_pbvh_update_bmesh_offsets(PBVH *pbvh, int cd_vert_node_offset, int cd_face_node_offset)
+{
+ pbvh->cd_vert_node_offset = cd_vert_node_offset;
+ pbvh->cd_face_node_offset = cd_face_node_offset;
+}
+
void BKE_pbvh_build_bmesh(PBVH *pbvh,
BMesh *bm,
bool smooth_shading,
@@ -1870,18 +1876,18 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh,
const int cd_vert_node_offset,
const int cd_face_node_offset)
{
- pbvh->cd_vert_node_offset = cd_vert_node_offset;
- pbvh->cd_face_node_offset = cd_face_node_offset;
- pbvh->bm = bm;
+ pbvh->header.bm = bm;
BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75);
- pbvh->type = PBVH_BMESH;
+ pbvh->header.type = PBVH_BMESH;
pbvh->bm_log = log;
/* TODO: choose leaf limit better */
pbvh->leaf_limit = 100;
+ BKE_pbvh_update_bmesh_offsets(pbvh, cd_vert_node_offset, cd_face_node_offset);
+
if (smooth_shading) {
pbvh->flags |= PBVH_DYNTOPO_SMOOTH_SHADING;
}
@@ -1951,7 +1957,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
/* 2 is enough for edge faces - manifold edge */
BLI_buffer_declare_static(BMLoop *, edge_loops, BLI_BUFFER_NOP, 2);
BLI_buffer_declare_static(BMFace *, deleted_faces, BLI_BUFFER_NOP, 32);
- const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->bm->vdata, CD_PAINT_MASK);
+ const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK);
const int cd_vert_node_offset = pbvh->cd_vert_node_offset;
const int cd_face_node_offset = pbvh->cd_face_node_offset;
@@ -1967,7 +1973,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
EdgeQueueContext eq_ctx = {
&q,
queue_pool,
- pbvh->bm,
+ pbvh->header.bm,
cd_vert_mask_offset,
cd_vert_node_offset,
cd_face_node_offset,
@@ -1986,7 +1992,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
EdgeQueueContext eq_ctx = {
&q,
queue_pool,
- pbvh->bm,
+ pbvh->header.bm,
cd_vert_mask_offset,
cd_vert_node_offset,
cd_face_node_offset,
@@ -1999,7 +2005,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh,
BLI_mempool_destroy(queue_pool);
}
- /* Unmark nodes */
+ /* Unmark nodes. */
for (int n = 0; n < pbvh->totnode; n++) {
PBVHNode *node = &pbvh->nodes[n];
@@ -2126,13 +2132,13 @@ static void pbvh_bmesh_print(PBVH *pbvh)
BMIter iter;
BMFace *f;
- BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) {
+ BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) {
fprintf(stderr, " %d -> %d\n", BM_elem_index_get(f), pbvh_bmesh_node_index_from_face(pbvh, f));
}
fprintf(stderr, "bm_vert_to_node:\n");
BMVert *v;
- BM_ITER_MESH (v, &iter, pbvh->bm, BM_FACES_OF_MESH) {
+ BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_FACES_OF_MESH) {
fprintf(stderr, " %d -> %d\n", BM_elem_index_get(v), pbvh_bmesh_node_index_from_vert(pbvh, v));
}
@@ -2171,21 +2177,21 @@ static void print_flag_factors(int flag)
static void pbvh_bmesh_verify(PBVH *pbvh)
{
/* build list of faces & verts to lookup */
- GSet *faces_all = BLI_gset_ptr_new_ex(__func__, pbvh->bm->totface);
+ GSet *faces_all = BLI_gset_ptr_new_ex(__func__, pbvh->header.bm->totface);
BMIter iter;
{
BMFace *f;
- BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) {
+ BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) {
BLI_assert(BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) != DYNTOPO_NODE_NONE);
BLI_gset_insert(faces_all, f);
}
}
- GSet *verts_all = BLI_gset_ptr_new_ex(__func__, pbvh->bm->totvert);
+ GSet *verts_all = BLI_gset_ptr_new_ex(__func__, pbvh->header.bm->totvert);
{
BMVert *v;
- BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
+ BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) {
if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) {
BLI_gset_insert(verts_all, v);
}
@@ -2207,7 +2213,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh)
{
BMFace *f;
- BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) {
+ BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) {
BMIter bm_iter;
BMVert *v;
PBVHNode *n = pbvh_bmesh_node_lookup(pbvh, f);
@@ -2240,7 +2246,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh)
/* Check verts */
{
BMVert *v;
- BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
+ BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) {
/* vertex isn't tracked */
if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) {
continue;
@@ -2286,7 +2292,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh)
# if 0
/* check that every vert belongs somewhere */
/* Slow */
- BM_ITER_MESH (vi, &iter, pbvh->bm, BM_VERTS_OF_MESH) {
+ BM_ITER_MESH (vi, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) {
bool has_unique = false;
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *n = &pbvh->nodes[i];
@@ -2299,7 +2305,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh)
}
/* If totvert differs from number of verts inside the hash. hash-totvert is checked above. */
- BLI_assert(vert_count == pbvh->bm->totvert);
+ BLI_assert(vert_count == pbvh->header.bm->totvert);
# endif
/* Check that node elements are recorded in the top level */
diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h
index a4ac2744a73..8ab56839f9c 100644
--- a/source/blender/blenkernel/intern/pbvh_intern.h
+++ b/source/blender/blenkernel/intern/pbvh_intern.h
@@ -123,6 +123,11 @@ struct PBVHNode {
/* Used to store the brush color during a stroke and composite it over the original color */
PBVHColorBufferNode color_buffer;
PBVHPixelsNode pixels;
+
+ /* Used to flash colors of updated node bounding boxes in
+ * debug draw mode (when G.debug_value / bpy.app.debug_value is 889).
+ */
+ int debug_draw_gen;
};
typedef enum { PBVH_DYNTOPO_SMOOTH_SHADING = 1 } PBVHFlags;
@@ -130,7 +135,7 @@ typedef enum { PBVH_DYNTOPO_SMOOTH_SHADING = 1 } PBVHFlags;
typedef struct PBVHBMeshLog PBVHBMeshLog;
struct PBVH {
- PBVHType type;
+ struct PBVHPublic header;
PBVHFlags flags;
PBVHNode *nodes;
@@ -144,12 +149,16 @@ struct PBVH {
int leaf_limit;
/* Mesh data */
- const struct Mesh *mesh;
+ struct Mesh *mesh;
/* NOTE: Normals are not `const` because they can be updated for drawing by sculpt code. */
float (*vert_normals)[3];
+ bool *hide_vert;
struct MVert *verts;
const struct MPoly *mpoly;
+ bool *hide_poly;
+ /** Material indices. Only valid for polygon meshes. */
+ const int *material_indices;
const struct MLoop *mloop;
const struct MLoopTri *looptri;
CustomData *vdata;
@@ -183,7 +192,6 @@ struct PBVH {
bool respect_hide;
/* Dynamic topology */
- BMesh *bm;
float bm_max_edge_len;
float bm_min_edge_len;
int cd_vert_node_offset;
@@ -265,7 +273,7 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node,
struct IsectRayPrecalc *isect_precalc,
float *dist,
bool use_original,
- int *r_active_vertex_index,
+ PBVHVertRef *r_active_vertex,
float *r_face_normal);
bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node,
const float ray_start[3],
diff --git a/source/blender/blenkernel/intern/pbvh_pixels.cc b/source/blender/blenkernel/intern/pbvh_pixels.cc
index 49397797c0d..f733f3145ec 100644
--- a/source/blender/blenkernel/intern/pbvh_pixels.cc
+++ b/source/blender/blenkernel/intern/pbvh_pixels.cc
@@ -2,6 +2,7 @@
* Copyright 2022 Blender Foundation. All rights reserved. */
#include "BKE_customdata.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_pbvh.h"
#include "BKE_pbvh_pixels.hh"
@@ -291,7 +292,8 @@ static void update_pixels(PBVH *pbvh, Mesh *mesh, Image *image, ImageUser *image
for (PBVHNode *node : nodes_to_update) {
NodeData *node_data = static_cast<NodeData *>(node->pixels.node_data);
- init_triangles(pbvh, node, node_data, mesh->mloop);
+ const Span<MLoop> loops = mesh->loops();
+ init_triangles(pbvh, node, node_data, loops.data());
}
EncodePixelsUserData user_data;
diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c
index 5d8025dce8a..ce04d781980 100644
--- a/source/blender/blenkernel/intern/pointcache.c
+++ b/source/blender/blenkernel/intern/pointcache.c
@@ -1304,7 +1304,7 @@ static int ptcache_frame_from_filename(const char *filename, const char *ext)
#define MAX_PTCACHE_PATH FILE_MAX
#define MAX_PTCACHE_FILE (FILE_MAX * 2)
-static int ptcache_path(PTCacheID *pid, char *filename)
+static int ptcache_path(PTCacheID *pid, char *dirname)
{
const char *blendfile_path = BKE_main_blendfile_path_from_global();
Library *lib = (pid->owner_id) ? pid->owner_id->lib : NULL;
@@ -1314,13 +1314,13 @@ static int ptcache_path(PTCacheID *pid, char *filename)
size_t i;
if (pid->cache->flag & PTCACHE_EXTERNAL) {
- strcpy(filename, pid->cache->path);
+ strcpy(dirname, pid->cache->path);
- if (BLI_path_is_rel(filename)) {
- BLI_path_abs(filename, blendfilename);
+ if (BLI_path_is_rel(dirname)) {
+ BLI_path_abs(dirname, blendfilename);
}
- return BLI_path_slash_ensure(filename); /* new strlen() */
+ return BLI_path_slash_ensure(dirname); /* new strlen() */
}
if ((blendfile_path[0] != '\0') || lib) {
char file[MAX_PTCACHE_PATH]; /* we don't want the dir, only the file */
@@ -1334,28 +1334,28 @@ static int ptcache_path(PTCacheID *pid, char *filename)
}
/* Add blend file name to pointcache dir. */
- BLI_snprintf(filename, MAX_PTCACHE_PATH, "//" PTCACHE_PATH "%s", file);
+ BLI_snprintf(dirname, MAX_PTCACHE_PATH, "//" PTCACHE_PATH "%s", file);
- BLI_path_abs(filename, blendfilename);
- return BLI_path_slash_ensure(filename); /* new strlen() */
+ BLI_path_abs(dirname, blendfilename);
+ return BLI_path_slash_ensure(dirname); /* new strlen() */
}
/* use the temp path. this is weak but better than not using point cache at all */
/* temporary directory is assumed to exist and ALWAYS has a trailing slash */
- BLI_snprintf(filename, MAX_PTCACHE_PATH, "%s" PTCACHE_PATH, BKE_tempdir_session());
+ BLI_snprintf(dirname, MAX_PTCACHE_PATH, "%s" PTCACHE_PATH, BKE_tempdir_session());
- return BLI_path_slash_ensure(filename); /* new strlen() */
+ return BLI_path_slash_ensure(dirname); /* new strlen() */
}
-static size_t ptcache_filename_ext_append(PTCacheID *pid,
- char *filename,
- const size_t filename_len,
+static size_t ptcache_filepath_ext_append(PTCacheID *pid,
+ char *filepath,
+ const size_t filepath_len,
const bool use_frame_number,
const int cfra)
{
- size_t len = filename_len;
+ size_t len = filepath_len;
char *filename_ext;
- filename_ext = filename + filename_len;
+ filename_ext = filepath + filepath_len;
*filename_ext = '\0';
/* PointCaches are inserted in object's list on demand, we need a valid index now. */
@@ -1399,14 +1399,14 @@ static size_t ptcache_filename_ext_append(PTCacheID *pid,
return len;
}
-static int ptcache_filename(
- PTCacheID *pid, char *filename, int cfra, const bool do_path, const bool do_ext)
+static int ptcache_filepath(
+ PTCacheID *pid, char *filepath, int cfra, const bool do_path, const bool do_ext)
{
int len = 0;
char *idname;
char *newname;
- filename[0] = '\0';
- newname = filename;
+ filepath[0] = '\0';
+ newname = filepath;
if ((pid->cache->flag & PTCACHE_EXTERNAL) == 0) {
const char *blendfile_path = BKE_main_blendfile_path_from_global();
@@ -1417,7 +1417,7 @@ static int ptcache_filename(
/* start with temp dir */
if (do_path) {
- len = ptcache_path(pid, filename);
+ len = ptcache_path(pid, filepath);
newname += len;
}
if (pid->cache->name[0] == '\0' && (pid->cache->flag & PTCACHE_EXTERNAL) == 0) {
@@ -1437,7 +1437,7 @@ static int ptcache_filename(
}
if (do_ext) {
- len += ptcache_filename_ext_append(pid, filename, (size_t)len, true, cfra);
+ len += ptcache_filepath_ext_append(pid, filepath, (size_t)len, true, cfra);
}
return len; /* make sure the above string is always 16 chars */
@@ -1465,7 +1465,7 @@ static PTCacheFile *ptcache_file_open(PTCacheID *pid, int mode, int cfra)
}
}
- ptcache_filename(pid, filepath, cfra, true, true);
+ ptcache_filepath(pid, filepath, cfra, true, true);
if (mode == PTCACHE_FILE_READ) {
fp = BLI_fopen(filepath, "rb");
@@ -2596,7 +2596,7 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
DIR *dir;
struct dirent *de;
char path[MAX_PTCACHE_PATH];
- char filename[MAX_PTCACHE_FILE];
+ char filepath[MAX_PTCACHE_FILE];
char path_full[MAX_PTCACHE_FILE];
char ext[MAX_PTCACHE_PATH];
@@ -2631,20 +2631,20 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
return;
}
- len = ptcache_filename(pid, filename, cfra, false, false); /* no path */
+ len = ptcache_filepath(pid, filepath, cfra, false, false); /* no path */
/* append underscore terminator to ensure we don't match similar names
* from objects whose names start with the same prefix
*/
- if (len < sizeof(filename) - 2) {
- BLI_strncpy(filename + len, "_", sizeof(filename) - 2 - len);
+ if (len < sizeof(filepath) - 2) {
+ BLI_strncpy(filepath + len, "_", sizeof(filepath) - 2 - len);
len += 1;
}
- ptcache_filename_ext_append(pid, ext, 0, false, 0);
+ ptcache_filepath_ext_append(pid, ext, 0, false, 0);
while ((de = readdir(dir)) != NULL) {
if (strstr(de->d_name, ext)) { /* Do we have the right extension? */
- if (STREQLEN(filename, de->d_name, len)) { /* Do we have the right prefix. */
+ if (STREQLEN(filepath, de->d_name, len)) { /* Do we have the right prefix. */
if (mode == PTCACHE_CLEAR_ALL) {
pid->cache->last_exact = MIN2(pid->cache->startframe, 0);
BLI_join_dirfile(path_full, sizeof(path_full), path, de->d_name);
@@ -2713,8 +2713,8 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, unsigned int cfra)
case PTCACHE_CLEAR_FRAME:
if (pid->cache->flag & PTCACHE_DISK_CACHE) {
if (BKE_ptcache_id_exist(pid, cfra)) {
- ptcache_filename(pid, filename, cfra, true, true); /* no path */
- BLI_delete(filename, false, false);
+ ptcache_filepath(pid, filepath, cfra, true, true); /* no path */
+ BLI_delete(filepath, false, false);
}
}
else {
@@ -2752,11 +2752,11 @@ bool BKE_ptcache_id_exist(PTCacheID *pid, int cfra)
}
if (pid->cache->flag & PTCACHE_DISK_CACHE) {
- char filename[MAX_PTCACHE_FILE];
+ char filepath[MAX_PTCACHE_FILE];
- ptcache_filename(pid, filename, cfra, true, true);
+ ptcache_filepath(pid, filepath, cfra, true, true);
- return BLI_exists(filename);
+ return BLI_exists(filepath);
}
PTCacheMem *pm = pid->cache->mem_cache.first;
@@ -2824,24 +2824,24 @@ void BKE_ptcache_id_time(
DIR *dir;
struct dirent *de;
char path[MAX_PTCACHE_PATH];
- char filename[MAX_PTCACHE_FILE];
+ char filepath[MAX_PTCACHE_FILE];
char ext[MAX_PTCACHE_PATH];
unsigned int len; /* store the length of the string */
ptcache_path(pid, path);
- len = ptcache_filename(pid, filename, (int)cfra, 0, 0); /* no path */
+ len = ptcache_filepath(pid, filepath, (int)cfra, 0, 0); /* no path */
dir = opendir(path);
if (dir == NULL) {
return;
}
- ptcache_filename_ext_append(pid, ext, 0, false, 0);
+ ptcache_filepath_ext_append(pid, ext, 0, false, 0);
while ((de = readdir(dir)) != NULL) {
if (strstr(de->d_name, ext)) { /* Do we have the right extension? */
- if (STREQLEN(filename, de->d_name, len)) { /* Do we have the right prefix. */
+ if (STREQLEN(filepath, de->d_name, len)) { /* Do we have the right prefix. */
/* read the number of the file */
const int frame = ptcache_frame_from_filename(de->d_name, ext);
@@ -3494,7 +3494,7 @@ void BKE_ptcache_disk_cache_rename(PTCacheID *pid, const char *name_src, const c
DIR *dir;
struct dirent *de;
char path[MAX_PTCACHE_PATH];
- char old_filename[MAX_PTCACHE_FILE];
+ char old_filepath[MAX_PTCACHE_FILE];
char new_path_full[MAX_PTCACHE_FILE];
char old_path_full[MAX_PTCACHE_FILE];
char ext[MAX_PTCACHE_PATH];
@@ -3510,7 +3510,7 @@ void BKE_ptcache_disk_cache_rename(PTCacheID *pid, const char *name_src, const c
/* get "from" filename */
BLI_strncpy(pid->cache->name, name_src, sizeof(pid->cache->name));
- len = ptcache_filename(pid, old_filename, 0, false, false); /* no path */
+ len = ptcache_filepath(pid, old_filepath, 0, false, false); /* no path */
ptcache_path(pid, path);
dir = opendir(path);
@@ -3519,20 +3519,20 @@ void BKE_ptcache_disk_cache_rename(PTCacheID *pid, const char *name_src, const c
return;
}
- ptcache_filename_ext_append(pid, ext, 0, false, 0);
+ ptcache_filepath_ext_append(pid, ext, 0, false, 0);
/* put new name into cache */
BLI_strncpy(pid->cache->name, name_dst, sizeof(pid->cache->name));
while ((de = readdir(dir)) != NULL) {
if (strstr(de->d_name, ext)) { /* Do we have the right extension? */
- if (STREQLEN(old_filename, de->d_name, len)) { /* Do we have the right prefix. */
+ if (STREQLEN(old_filepath, de->d_name, len)) { /* Do we have the right prefix. */
/* read the number of the file */
const int frame = ptcache_frame_from_filename(de->d_name, ext);
if (frame != -1) {
BLI_join_dirfile(old_path_full, sizeof(old_path_full), path, de->d_name);
- ptcache_filename(pid, new_path_full, frame, true, true);
+ ptcache_filepath(pid, new_path_full, frame, true, true);
BLI_rename(old_path_full, new_path_full);
}
}
@@ -3556,7 +3556,7 @@ void BKE_ptcache_load_external(PTCacheID *pid)
DIR *dir;
struct dirent *de;
char path[MAX_PTCACHE_PATH];
- char filename[MAX_PTCACHE_FILE];
+ char filepath[MAX_PTCACHE_FILE];
char ext[MAX_PTCACHE_PATH];
if (!cache) {
@@ -3565,7 +3565,7 @@ void BKE_ptcache_load_external(PTCacheID *pid)
ptcache_path(pid, path);
- len = ptcache_filename(pid, filename, 1, false, false); /* no path */
+ len = ptcache_filepath(pid, filepath, 1, false, false); /* no path */
dir = opendir(path);
if (dir == NULL) {
@@ -3583,7 +3583,7 @@ void BKE_ptcache_load_external(PTCacheID *pid)
while ((de = readdir(dir)) != NULL) {
if (strstr(de->d_name, ext)) { /* Do we have the right extension? */
- if (STREQLEN(filename, de->d_name, len)) { /* Do we have the right prefix. */
+ if (STREQLEN(filepath, de->d_name, len)) { /* Do we have the right prefix. */
/* read the number of the file */
const int frame = ptcache_frame_from_filename(de->d_name, ext);
diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc
index e38c20d8eb7..5c935bf6daf 100644
--- a/source/blender/blenkernel/intern/pointcloud.cc
+++ b/source/blender/blenkernel/intern/pointcloud.cc
@@ -64,11 +64,10 @@ static void pointcloud_init_data(ID *id)
CustomData_reset(&pointcloud->pdata);
CustomData_add_layer_named(&pointcloud->pdata,
CD_PROP_FLOAT3,
- CD_CALLOC,
+ CD_SET_DEFAULT,
nullptr,
pointcloud->totpoint,
POINTCLOUD_ATTR_POSITION);
- BKE_pointcloud_update_customdata_pointers(pointcloud);
}
static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag)
@@ -83,7 +82,6 @@ static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_s
CD_MASK_ALL,
alloc_type,
pointcloud_dst->totpoint);
- BKE_pointcloud_update_customdata_pointers(pointcloud_dst);
pointcloud_dst->batch_cache = nullptr;
}
@@ -138,7 +136,6 @@ static void pointcloud_blend_read_data(BlendDataReader *reader, ID *id)
/* Geometry */
CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint);
- BKE_pointcloud_update_customdata_pointers(pointcloud);
/* Materials */
BLO_read_pointer_array(reader, (void **)&pointcloud->mat);
@@ -178,7 +175,7 @@ IDTypeInfo IDType_ID_PT = {
/* foreach_id */ pointcloud_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ nullptr,
- /* owner_get */ nullptr,
+ /* owner_pointer_get */ nullptr,
/* blend_write */ pointcloud_blend_write,
/* blend_read_data */ pointcloud_blend_read_data,
@@ -192,19 +189,30 @@ IDTypeInfo IDType_ID_PT = {
static void pointcloud_random(PointCloud *pointcloud)
{
+ BLI_assert(pointcloud->totpoint == 0);
pointcloud->totpoint = 400;
- CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
- BKE_pointcloud_update_customdata_pointers(pointcloud);
+ CustomData_realloc(&pointcloud->pdata, 0, pointcloud->totpoint);
RNG *rng = BLI_rng_new(0);
- for (int i = 0; i < pointcloud->totpoint; i++) {
- pointcloud->co[i][0] = 2.0f * BLI_rng_get_float(rng) - 1.0f;
- pointcloud->co[i][1] = 2.0f * BLI_rng_get_float(rng) - 1.0f;
- pointcloud->co[i][2] = 2.0f * BLI_rng_get_float(rng) - 1.0f;
- pointcloud->radius[i] = 0.05f * BLI_rng_get_float(rng);
+ blender::bke::MutableAttributeAccessor attributes = pointcloud->attributes_for_write();
+ blender::bke::SpanAttributeWriter positions =
+ attributes.lookup_or_add_for_write_only_span<float3>(POINTCLOUD_ATTR_POSITION,
+ ATTR_DOMAIN_POINT);
+ blender::bke::SpanAttributeWriter<float> radii =
+ attributes.lookup_or_add_for_write_only_span<float>(POINTCLOUD_ATTR_RADIUS,
+ ATTR_DOMAIN_POINT);
+
+ for (const int i : positions.span.index_range()) {
+ positions.span[i] =
+ float3(BLI_rng_get_float(rng), BLI_rng_get_float(rng), BLI_rng_get_float(rng)) * 2.0f -
+ 1.0f;
+ radii.span[i] = 0.05f * BLI_rng_get_float(rng);
}
+ positions.finish();
+ radii.finish();
+
BLI_rng_free(rng);
}
@@ -220,13 +228,6 @@ void *BKE_pointcloud_add_default(Main *bmain, const char *name)
PointCloud *pointcloud = static_cast<PointCloud *>(BKE_libblock_alloc(bmain, ID_PT, name, 0));
pointcloud_init_data(&pointcloud->id);
-
- CustomData_add_layer_named(&pointcloud->pdata,
- CD_PROP_FLOAT,
- CD_CALLOC,
- nullptr,
- pointcloud->totpoint,
- POINTCLOUD_ATTR_RADIUS);
pointcloud_random(pointcloud);
return pointcloud;
@@ -238,30 +239,61 @@ PointCloud *BKE_pointcloud_new_nomain(const int totpoint)
nullptr, ID_PT, BKE_idtype_idcode_to_name(ID_PT), LIB_ID_CREATE_LOCALIZE));
pointcloud_init_data(&pointcloud->id);
-
- pointcloud->totpoint = totpoint;
-
CustomData_add_layer_named(&pointcloud->pdata,
CD_PROP_FLOAT,
- CD_CALLOC,
+ CD_SET_DEFAULT,
nullptr,
pointcloud->totpoint,
POINTCLOUD_ATTR_RADIUS);
+ CustomData_realloc(&pointcloud->pdata, 0, totpoint);
pointcloud->totpoint = totpoint;
- CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
- BKE_pointcloud_update_customdata_pointers(pointcloud);
return pointcloud;
}
+void BKE_pointcloud_nomain_to_pointcloud(PointCloud *pointcloud_src,
+ PointCloud *pointcloud_dst,
+ bool take_ownership)
+{
+ BLI_assert(pointcloud_src->id.tag & LIB_TAG_NO_MAIN);
+
+ eCDAllocType alloctype = CD_DUPLICATE;
+
+ if (take_ownership) {
+ bool has_any_referenced_layers = CustomData_has_referenced(&pointcloud_src->pdata);
+
+ if (!has_any_referenced_layers) {
+ alloctype = CD_ASSIGN;
+ }
+ }
+
+ CustomData_free(&pointcloud_dst->pdata, pointcloud_dst->totpoint);
+
+ const int totpoint = pointcloud_dst->totpoint = pointcloud_src->totpoint;
+ CustomData_copy(
+ &pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, alloctype, totpoint);
+
+ if (take_ownership) {
+ if (alloctype == CD_ASSIGN) {
+ /* Free the CustomData but keep the layers. */
+ CustomData_free_typemask(&pointcloud_src->pdata, pointcloud_src->totpoint, 0);
+ }
+ BKE_id_free(nullptr, pointcloud_src);
+ }
+}
+
static std::optional<blender::bounds::MinMaxResult<float3>> point_cloud_bounds(
const PointCloud &pointcloud)
{
- Span<float3> positions{reinterpret_cast<float3 *>(pointcloud.co), pointcloud.totpoint};
- if (pointcloud.radius) {
- Span<float> radii{pointcloud.radius, pointcloud.totpoint};
- return blender::bounds::min_max_with_radii(positions, radii);
+ blender::bke::AttributeAccessor attributes = pointcloud.attributes();
+ blender::VArraySpan<float3> positions = attributes.lookup_or_default<float3>(
+ POINTCLOUD_ATTR_POSITION, ATTR_DOMAIN_POINT, float3(0));
+ blender::VArray<float> radii = attributes.lookup_or_default<float>(
+ POINTCLOUD_ATTR_RADIUS, ATTR_DOMAIN_POINT, 0.0f);
+
+ if (!(radii.is_single() && radii.get_internal_single() == 0.0f)) {
+ return blender::bounds::min_max_with_radii(positions, radii.get_internal_span());
}
return blender::bounds::min_max(positions);
}
@@ -307,38 +339,13 @@ BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
return ob->runtime.bb;
}
-void BKE_pointcloud_update_customdata_pointers(PointCloud *pointcloud)
-{
- pointcloud->co = static_cast<float(*)[3]>(
- CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION));
- pointcloud->radius = static_cast<float *>(
- CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT, POINTCLOUD_ATTR_RADIUS));
-}
-
-bool BKE_pointcloud_customdata_required(const PointCloud *UNUSED(pointcloud), const char *name)
+bool BKE_pointcloud_attribute_required(const PointCloud *UNUSED(pointcloud), const char *name)
{
return STREQ(name, POINTCLOUD_ATTR_POSITION);
}
/* Dependency Graph */
-PointCloud *BKE_pointcloud_new_for_eval(const PointCloud *pointcloud_src, int totpoint)
-{
- PointCloud *pointcloud_dst = static_cast<PointCloud *>(BKE_id_new_nomain(ID_PT, nullptr));
- CustomData_free(&pointcloud_dst->pdata, pointcloud_dst->totpoint);
-
- STRNCPY(pointcloud_dst->id.name, pointcloud_src->id.name);
- pointcloud_dst->mat = static_cast<Material **>(MEM_dupallocN(pointcloud_src->mat));
- pointcloud_dst->totcol = pointcloud_src->totcol;
-
- pointcloud_dst->totpoint = totpoint;
- CustomData_copy(
- &pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint);
- BKE_pointcloud_update_customdata_pointers(pointcloud_dst);
-
- return pointcloud_dst;
-}
-
PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool reference)
{
int flags = LIB_ID_COPY_LOCALIZE;
@@ -362,6 +369,8 @@ static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph,
ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
+ BKE_modifiers_clear_errors(object);
+
/* Get effective list of modifiers to execute. Some effects like shape keys
* are added as virtual modifiers before the user created modifiers. */
VirtualModifierData virtualModifierData;
diff --git a/source/blender/blenkernel/intern/report.c b/source/blender/blenkernel/intern/report.c
index 6d654730bca..ccec9c346a8 100644
--- a/source/blender/blenkernel/intern/report.c
+++ b/source/blender/blenkernel/intern/report.c
@@ -258,10 +258,20 @@ char *BKE_reports_string(ReportList *reports, eReportType level)
bool BKE_reports_print_test(const ReportList *reports, eReportType type)
{
+ if (reports == NULL) {
+ return true;
+ }
+ if (reports->flag & RPT_PRINT_HANDLED_BY_OWNER) {
+ return false;
+ }
/* In background mode always print otherwise there are cases the errors won't be displayed,
- * but still add to the report list since this is used for python exception handling. */
- return (G.background || (reports == NULL) ||
- ((reports->flag & RPT_PRINT) && (type >= reports->printlevel)));
+ * but still add to the report list since this is used for Python exception handling. */
+ if (G.background) {
+ return true;
+ }
+
+ /* Common case. */
+ return (reports->flag & RPT_PRINT) && (type >= reports->printlevel);
}
void BKE_reports_print(ReportList *reports, eReportType level)
diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c
index 821976f8e0e..2705241425b 100644
--- a/source/blender/blenkernel/intern/rigidbody.c
+++ b/source/blender/blenkernel/intern/rigidbody.c
@@ -364,7 +364,7 @@ static rbCollisionShape *rigidbody_get_shape_convexhull_from_mesh(Object *ob,
if (ob->type == OB_MESH && ob->data) {
mesh = rigidbody_get_mesh(ob);
- mvert = (mesh) ? mesh->mvert : NULL;
+ mvert = (mesh) ? BKE_mesh_verts_for_write(mesh) : NULL;
totvert = (mesh) ? mesh->totvert : 0;
}
else {
@@ -390,11 +390,9 @@ static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob)
if (ob->type == OB_MESH) {
Mesh *mesh = NULL;
- MVert *mvert;
const MLoopTri *looptri;
int totvert;
int tottri;
- const MLoop *mloop;
mesh = rigidbody_get_mesh(ob);
@@ -403,11 +401,11 @@ static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob)
return NULL;
}
- mvert = mesh->mvert;
+ const MVert *mvert = BKE_mesh_verts(mesh);
totvert = mesh->totvert;
looptri = BKE_mesh_runtime_looptri_ensure(mesh);
tottri = mesh->runtime.looptris.len;
- mloop = mesh->mloop;
+ const MLoop *mloop = BKE_mesh_loops(mesh);
/* sanity checking - potential case when no data will be present */
if ((totvert == 0) || (tottri == 0)) {
@@ -670,21 +668,19 @@ void BKE_rigidbody_calc_volume(Object *ob, float *r_vol)
case RB_SHAPE_TRIMESH: {
if (ob->type == OB_MESH) {
Mesh *mesh = rigidbody_get_mesh(ob);
- MVert *mvert;
const MLoopTri *lt = NULL;
int totvert, tottri = 0;
- const MLoop *mloop = NULL;
/* ensure mesh validity, then grab data */
if (mesh == NULL) {
return;
}
- mvert = mesh->mvert;
+ const MVert *mvert = BKE_mesh_verts(mesh);
totvert = mesh->totvert;
lt = BKE_mesh_runtime_looptri_ensure(mesh);
tottri = mesh->runtime.looptris.len;
- mloop = mesh->mloop;
+ const MLoop *mloop = BKE_mesh_loops(mesh);
if (totvert > 0 && tottri > 0) {
BKE_mesh_calc_volume(mvert, totvert, lt, tottri, mloop, &volume, NULL);
@@ -746,21 +742,19 @@ void BKE_rigidbody_calc_center_of_mass(Object *ob, float r_center[3])
case RB_SHAPE_TRIMESH: {
if (ob->type == OB_MESH) {
Mesh *mesh = rigidbody_get_mesh(ob);
- MVert *mvert;
const MLoopTri *looptri;
int totvert, tottri;
- const MLoop *mloop;
/* ensure mesh validity, then grab data */
if (mesh == NULL) {
return;
}
- mvert = mesh->mvert;
+ const MVert *mvert = BKE_mesh_verts(mesh);
totvert = mesh->totvert;
looptri = BKE_mesh_runtime_looptri_ensure(mesh);
tottri = mesh->runtime.looptris.len;
- mloop = mesh->mloop;
+ const MLoop *mloop = BKE_mesh_loops(mesh);
if (totvert > 0 && tottri > 0) {
BKE_mesh_calc_volume(mvert, totvert, looptri, tottri, mloop, NULL, r_center);
@@ -1176,6 +1170,9 @@ RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw, const int flag)
if (rbw->effector_weights) {
rbw_copy->effector_weights = MEM_dupallocN(rbw->effector_weights);
+ if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
+ id_us_plus((ID *)rbw->effector_weights->group);
+ }
}
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
id_us_plus((ID *)rbw_copy->group);
@@ -1205,9 +1202,9 @@ void BKE_rigidbody_world_groups_relink(RigidBodyWorld *rbw)
void BKE_rigidbody_world_id_loop(RigidBodyWorld *rbw, RigidbodyWorldIDFunc func, void *userdata)
{
- func(rbw, (ID **)&rbw->group, userdata, IDWALK_CB_NOP);
- func(rbw, (ID **)&rbw->constraints, userdata, IDWALK_CB_NOP);
- func(rbw, (ID **)&rbw->effector_weights->group, userdata, IDWALK_CB_NOP);
+ func(rbw, (ID **)&rbw->group, userdata, IDWALK_CB_USER);
+ func(rbw, (ID **)&rbw->constraints, userdata, IDWALK_CB_USER);
+ func(rbw, (ID **)&rbw->effector_weights->group, userdata, IDWALK_CB_USER);
if (rbw->objects) {
int i;
@@ -1424,7 +1421,7 @@ static bool rigidbody_add_object_to_scene(Main *bmain, Scene *scene, Object *ob)
if (rbw->group == NULL) {
rbw->group = BKE_collection_add(bmain, NULL, "RigidBodyWorld");
- id_fake_user_set(&rbw->group->id);
+ id_us_plus(&rbw->group->id);
}
/* Add object to rigid body group. */
@@ -1453,7 +1450,7 @@ static bool rigidbody_add_constraint_to_scene(Main *bmain, Scene *scene, Object
if (rbw->constraints == NULL) {
rbw->constraints = BKE_collection_add(bmain, NULL, "RigidBodyConstraints");
- id_fake_user_set(&rbw->constraints->id);
+ id_us_plus(&rbw->constraints->id);
}
/* Add object to rigid body group. */
@@ -1548,7 +1545,7 @@ void BKE_rigidbody_remove_object(Main *bmain, Scene *scene, Object *ob, const bo
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
- /* Relying on usercount of the object should be OK, and it is much cheaper than looping in all
+ /* Relying on user-count of the object should be OK, and it is much cheaper than looping in all
* collections to check whether the object is already in another one... */
if (ID_REAL_USERS(&ob->id) == 1) {
/* Some users seems to find it funny to use a view-layer instancing collection
@@ -1667,14 +1664,16 @@ static void rigidbody_update_sim_ob(Depsgraph *depsgraph, Object *ob, RigidBodyO
return;
}
+ const Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
const bool is_selected = base ? (base->flag & BASE_SELECTED) != 0 : false;
if (rbo->shape == RB_SHAPE_TRIMESH && rbo->flag & RBO_FLAG_USE_DEFORM) {
Mesh *mesh = ob->runtime.mesh_deform_eval;
if (mesh) {
- MVert *mvert = mesh->mvert;
+ MVert *mvert = BKE_mesh_verts_for_write(mesh);
int totvert = mesh->totvert;
const BoundBox *bb = BKE_object_boundbox_get(ob);
@@ -2011,7 +2010,9 @@ static void rigidbody_free_substep_data(ListBase *substep_targets)
}
static void rigidbody_update_simulation_post_step(Depsgraph *depsgraph, RigidBodyWorld *rbw)
{
+ const Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
+ BKE_view_layer_synced_ensure(scene, view_layer);
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, ob) {
Base *base = BKE_view_layer_base_find(view_layer, ob);
diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc
index 39157bc9890..9c0e5a5534e 100644
--- a/source/blender/blenkernel/intern/scene.cc
+++ b/source/blender/blenkernel/intern/scene.cc
@@ -216,7 +216,7 @@ static void scene_init_data(ID *id)
}
/* Master Collection */
- scene->master_collection = BKE_collection_master_add();
+ scene->master_collection = BKE_collection_master_add(scene);
BKE_view_layer_add(scene, "ViewLayer", nullptr, VIEWLAYER_ADD_NEW);
}
@@ -235,7 +235,7 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
{
Scene *scene_dst = (Scene *)id_dst;
const Scene *scene_src = (const Scene *)id_src;
- /* We never handle usercount here for own data. */
+ /* We never handle user-count here for own data. */
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
/* We always need allocation of our private ID data. */
const int flag_private_id_data = flag & ~LIB_ID_CREATE_NO_ALLOCATE;
@@ -250,6 +250,7 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
(ID *)scene_src->master_collection,
(ID **)&scene_dst->master_collection,
flag_private_id_data);
+ scene_dst->master_collection->owner_id = &scene_dst->id;
}
/* View Layers */
@@ -275,6 +276,7 @@ static void scene_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
(void *)(&scene_src->id),
&scene_dst->id,
ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_SKIP_USER_CLEAR);
+ scene_dst->nodetree->owner_id = &scene_dst->id;
}
if (scene_src->rigidbody_world) {
@@ -801,8 +803,8 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data)
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, view_layer->mat_override, IDWALK_CB_USER);
-
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE);
}
@@ -1054,7 +1056,7 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
BKE_curvemapping_curves_blend_write(writer, &sce->r.mblur_shutter_curve);
LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) {
- BKE_view_layer_blend_write(writer, view_layer);
+ BKE_view_layer_blend_write(writer, sce, view_layer);
}
if (sce->master_collection) {
@@ -1481,7 +1483,7 @@ static void scene_blend_read_lib(BlendLibReader *reader, ID *id)
}
LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) {
- IDP_BlendReadLib(reader, marker->prop);
+ IDP_BlendReadLib(reader, sce->id.lib, marker->prop);
if (marker->camera) {
BLO_read_id_address(reader, sce->id.lib, &marker->camera);
@@ -1673,7 +1675,7 @@ constexpr IDTypeInfo get_type_info()
info.foreach_id = scene_foreach_id;
info.foreach_cache = scene_foreach_cache;
info.foreach_path = scene_foreach_path;
- info.owner_get = nullptr;
+ info.owner_pointer_get = nullptr;
info.blend_write = scene_blend_write;
info.blend_read_data = scene_blend_read_data;
@@ -2048,7 +2050,8 @@ Scene *BKE_scene_add(Main *bmain, const char *name)
bool BKE_scene_object_find(Scene *scene, Object *ob)
{
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- if (BLI_findptr(&view_layer->object_bases, ob, offsetof(Base, object))) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (BLI_findptr(BKE_view_layer_object_bases_get(view_layer), ob, offsetof(Base, object))) {
return true;
}
}
@@ -2058,7 +2061,8 @@ bool BKE_scene_object_find(Scene *scene, Object *ob)
Object *BKE_scene_object_find_by_name(const Scene *scene, const char *name)
{
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (STREQ(base->object->id.name + 2, name)) {
return base->object;
}
@@ -2079,7 +2083,8 @@ void BKE_scene_set_background(Main *bmain, Scene *scene)
/* copy layers and flags from bases to objects */
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
/* collection patch... */
BKE_scene_object_base_flag_sync_from_base(base);
}
@@ -2122,7 +2127,8 @@ int BKE_scene_base_iter_next(
if (iter->phase == F_START) {
ViewLayer *view_layer = (depsgraph) ? DEG_get_evaluated_view_layer(depsgraph) :
BKE_view_layer_context_active_PLACEHOLDER(*scene);
- *base = static_cast<Base *>(view_layer->object_bases.first);
+ BKE_view_layer_synced_ensure(*scene, view_layer);
+ *base = static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
if (*base) {
*ob = (*base)->object;
iter->phase = F_SCENE;
@@ -2132,8 +2138,10 @@ int BKE_scene_base_iter_next(
while ((*scene)->set) {
(*scene) = (*scene)->set;
ViewLayer *view_layer_set = BKE_view_layer_default_render(*scene);
- if (view_layer_set->object_bases.first) {
- *base = static_cast<Base *>(view_layer_set->object_bases.first);
+ BKE_view_layer_synced_ensure(*scene, view_layer_set);
+ ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer_set);
+ if (object_bases->first) {
+ *base = static_cast<Base *>(object_bases->first);
*ob = (*base)->object;
iter->phase = F_SCENE;
break;
@@ -2153,8 +2161,10 @@ int BKE_scene_base_iter_next(
while ((*scene)->set) {
(*scene) = (*scene)->set;
ViewLayer *view_layer_set = BKE_view_layer_default_render(*scene);
- if (view_layer_set->object_bases.first) {
- *base = static_cast<Base *>(view_layer_set->object_bases.first);
+ BKE_view_layer_synced_ensure(*scene, view_layer_set);
+ ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer_set);
+ if (object_bases->first) {
+ *base = static_cast<Base *>(object_bases->first);
*ob = (*base)->object;
break;
}
@@ -2501,9 +2511,11 @@ static bool check_rendered_viewport_visible(Main *bmain)
return false;
}
-/* TODO(campbell): shouldn't we be able to use 'DEG_get_view_layer' here?
+/* TODO(@campbellbarton): shouldn't we be able to use 'DEG_get_view_layer' here?
* Currently this is nullptr on load, so don't. */
-static void prepare_mesh_for_viewport_render(Main *bmain, const ViewLayer *view_layer)
+static void prepare_mesh_for_viewport_render(Main *bmain,
+ const Scene *scene,
+ ViewLayer *view_layer)
{
/* This is needed to prepare mesh to be used by the render
* engine from the viewport rendering. We do loading here
@@ -2513,8 +2525,8 @@ static void prepare_mesh_for_viewport_render(Main *bmain, const ViewLayer *view_
* This makes it so viewport render engine doesn't need to
* call loading of the edit data for the mesh objects.
*/
-
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit) {
Mesh *mesh = static_cast<Mesh *>(obedit->data);
if ((obedit->type == OB_MESH) &&
@@ -2590,8 +2602,8 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on
/* Uncomment this to check if graph was properly tagged for update. */
// DEG_debug_graph_relations_validate(depsgraph, bmain, scene);
/* Flush editing data if needed. */
- prepare_mesh_for_viewport_render(bmain, view_layer);
- /* Update all objects: drivers, matrices, #DispList, etc. flags set
+ prepare_mesh_for_viewport_render(bmain, scene, view_layer);
+ /* Update all objects: drivers, matrices, etc. flags set
* by depsgraph or manual, no layer check here, gets correct flushed. */
DEG_evaluate_on_refresh(depsgraph);
/* Update sound system. */
@@ -2666,7 +2678,7 @@ void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool cle
BKE_image_editors_update_frame(bmain, scene->r.cfra);
BKE_sound_set_cfra(scene->r.cfra);
DEG_graph_relations_update(depsgraph);
- /* Update all objects: drivers, matrices, #DispList, etc. flags set
+ /* Update all objects: drivers, matrices, etc. flags set
* by depsgraph or manual, no layer check here, gets correct flushed.
*
* NOTE: Only update for new frame on first iteration. Second iteration is for ensuring user
@@ -2808,8 +2820,10 @@ Base *_setlooper_base_step(Scene **sce_iter, ViewLayer *view_layer, Base *base)
if ((base == nullptr) && (view_layer != nullptr)) {
/* First time looping, return the scenes first base. */
/* For the first loop we should get the layer from workspace when available. */
- if (view_layer->object_bases.first) {
- return (Base *)view_layer->object_bases.first;
+ BKE_view_layer_synced_ensure(*sce_iter, view_layer);
+ ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
+ if (object_bases->first) {
+ return static_cast<Base *>(object_bases->first);
}
/* No base on this scene layer. */
goto next_set;
@@ -2819,7 +2833,7 @@ Base *_setlooper_base_step(Scene **sce_iter, ViewLayer *view_layer, Base *base)
/* Reached the end, get the next base in the set. */
while ((*sce_iter = (*sce_iter)->set)) {
ViewLayer *view_layer_set = BKE_view_layer_default_render(*sce_iter);
- base = (Base *)view_layer_set->object_bases.first;
+ base = (Base *)BKE_view_layer_object_bases_get(view_layer_set)->first;
if (base) {
return base;
@@ -2878,13 +2892,11 @@ bool BKE_scene_uses_cycles_experimental_features(Scene *scene)
return RNA_enum_get(&cycles_ptr, "feature_set") == CYCLES_FEATURES_EXPERIMENTAL;
}
-void BKE_scene_base_flag_to_objects(ViewLayer *view_layer)
+void BKE_scene_base_flag_to_objects(const Scene *scene, ViewLayer *view_layer)
{
- Base *base = static_cast<Base *>(view_layer->object_bases.first);
-
- while (base) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
BKE_scene_object_base_flag_sync_from_base(base);
- base = base->next;
}
}
@@ -2952,6 +2964,20 @@ int BKE_scene_num_threads(const Scene *scene)
return BKE_render_num_threads(&scene->r);
}
+void BKE_render_resolution(const struct RenderData *r,
+ const bool use_crop,
+ int *r_width,
+ int *r_height)
+{
+ *r_width = (r->xsch * r->size) / 100;
+ *r_height = (r->ysch * r->size) / 100;
+
+ if (use_crop && (r->mode & R_BORDER) && (r->mode & R_CROP)) {
+ *r_width *= BLI_rctf_size_x(&r->border);
+ *r_height *= BLI_rctf_size_y(&r->border);
+ }
+}
+
int BKE_render_preview_pixel_size(const RenderData *r)
{
if (r->preview_pixel_size == 0) {
diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c
index 12dc1b6d1fa..03f0c3ff1e9 100644
--- a/source/blender/blenkernel/intern/screen.c
+++ b/source/blender/blenkernel/intern/screen.c
@@ -148,7 +148,6 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
}
case SPACE_OUTLINER: {
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
- BKE_LIB_FOREACHID_PROCESS_ID(data, space_outliner->search_tse.id, IDWALK_CB_NOP);
if (space_outliner->treestore != NULL) {
TreeStoreElem *tselem;
BLI_mempool_iter iter;
@@ -277,7 +276,7 @@ static void screen_blend_read_lib(BlendLibReader *reader, ID *id)
IDTypeInfo IDType_ID_SCR = {
.id_code = ID_SCR,
- .id_filter = 0,
+ .id_filter = FILTER_ID_SCR,
.main_listbase_index = INDEX_ID_SCR,
.struct_size = sizeof(bScreen),
.name = "Screen",
@@ -293,7 +292,7 @@ IDTypeInfo IDType_ID_SCR = {
.foreach_id = screen_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = screen_blend_write,
/* Cannot be used yet, because #direct_link_screen has a return value. */
@@ -306,7 +305,7 @@ IDTypeInfo IDType_ID_SCR = {
.lib_override_apply_post = NULL,
};
-/* ************ Spacetype/regiontype handling ************** */
+/* ************ Space-type/region-type handling ************** */
/* keep global; this has to be accessible outside of windowmanager */
static ListBase spacetypes = {NULL, NULL};
@@ -1124,40 +1123,50 @@ static void write_uilist(BlendWriter *writer, uiList *ui_list)
}
}
-static void write_space_outliner(BlendWriter *writer, SpaceOutliner *space_outliner)
+static void write_space_outliner(BlendWriter *writer, const SpaceOutliner *space_outliner)
{
BLI_mempool *ts = space_outliner->treestore;
if (ts) {
- SpaceOutliner space_outliner_flat = *space_outliner;
-
- int elems = BLI_mempool_len(ts);
+ const int elems = BLI_mempool_len(ts);
/* linearize mempool to array */
TreeStoreElem *data = elems ? BLI_mempool_as_arrayN(ts, "TreeStoreElem") : NULL;
if (data) {
- /* In this block we use the memory location of the treestore
- * but _not_ its data, the addresses in this case are UUID's,
- * since we can't rely on malloc giving us different values each time.
+ BLO_write_struct(writer, SpaceOutliner, space_outliner);
+
+ /* To store #TreeStore (instead of the mempool), two unique memory addresses are needed,
+ * which can be used to identify the data on read:
+ * 1) One for the #TreeStore data itself.
+ * 2) One for the array of #TreeStoreElem's inside #TreeStore (#TreeStore.data).
+ *
+ * For 1) we just use the mempool's address (#SpaceOutliner::treestore).
+ * For 2) we don't have such a direct choice. We can't just use the array's address from
+ * above, since that may not be unique over all Outliners. So instead use an address relative
+ * to 1).
*/
- TreeStore ts_flat = {0};
+ /* TODO the mempool could be moved to #SpaceOutliner_Runtime so that #SpaceOutliner could
+ * hold the #TreeStore directly. */
- /* we know the treestore is at least as big as a pointer,
- * so offsetting works to give us a UUID. */
+ /* Address relative to the tree-store, as noted above. */
void *data_addr = (void *)POINTER_OFFSET(ts, sizeof(void *));
+ /* There should be plenty of memory addresses within the mempool data that we can point into,
+ * just double-check we don't potentially end up with a memory address that another DNA
+ * struct might use. Assumes BLI_mempool uses the guarded allocator. */
+ BLI_assert(MEM_allocN_len(ts) >= sizeof(void *) * 2);
+ TreeStore ts_flat = {0};
ts_flat.usedelem = elems;
ts_flat.totelem = elems;
ts_flat.data = data_addr;
- BLO_write_struct(writer, SpaceOutliner, space_outliner);
-
BLO_write_struct_at_address(writer, TreeStore, ts, &ts_flat);
BLO_write_struct_array_at_address(writer, TreeStoreElem, elems, data_addr, data);
MEM_freeN(data);
}
else {
+ SpaceOutliner space_outliner_flat = *space_outliner;
space_outliner_flat.treestore = NULL;
BLO_write_struct_at_address(writer, SpaceOutliner, space_outliner, &space_outliner_flat);
}
@@ -1653,7 +1662,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
LISTBASE_FOREACH_MUTABLE (ConsoleLine *, cl, &sconsole->history) {
BLO_read_data_address(reader, &cl->line);
if (cl->line) {
- /* the allocted length is not written, so reset here */
+ /* The allocated length is not written, so reset here. */
cl->len_alloc = cl->len + 1;
}
else {
@@ -1873,7 +1882,6 @@ void BKE_screen_area_blend_read_lib(BlendLibReader *reader, ID *parent_id, ScrAr
}
case SPACE_OUTLINER: {
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
- BLO_read_id_address(reader, NULL, &space_outliner->search_tse.id);
if (space_outliner->treestore) {
TreeStoreElem *tselem;
diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c
index 51ebd232978..745bd2a97e6 100644
--- a/source/blender/blenkernel/intern/shader_fx.c
+++ b/source/blender/blenkernel/intern/shader_fx.c
@@ -70,7 +70,8 @@ ShaderFxData *BKE_shaderfx_new(int type)
fx->type = type;
fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render;
fx->flag = eShaderFxFlag_OverrideLibrary_Local;
- fx->ui_expand_flag = 1; /* Expand only the parent panel by default. */
+ /* Expand only the parent panel by default. */
+ fx->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
if (fxi->flags & eShaderFxTypeFlag_EnableInEditmode) {
fx->mode |= eShaderFxMode_Editmode;
diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c
index 7c7aa80402d..4b4e3bdcfa6 100644
--- a/source/blender/blenkernel/intern/shrinkwrap.c
+++ b/source/blender/blenkernel/intern/shrinkwrap.c
@@ -64,9 +64,9 @@ typedef struct ShrinkwrapCalcData {
float (*vertexCos)[3]; /* vertexs being shrinkwraped */
int numVerts;
- struct MDeformVert *dvert; /* Pointer to mdeform array */
- int vgroup; /* Vertex group num */
- bool invert_vgroup; /* invert vertex group influence */
+ const struct MDeformVert *dvert; /* Pointer to mdeform array */
+ int vgroup; /* Vertex group num */
+ bool invert_vgroup; /* invert vertex group influence */
struct Mesh *target; /* mesh we are shrinking to */
struct SpaceTransform local2target; /* transform to move between local and target space */
@@ -113,6 +113,7 @@ bool BKE_shrinkwrap_init_tree(
}
data->mesh = mesh;
+ data->polys = BKE_mesh_polys(mesh);
if (shrinkType == MOD_SHRINKWRAP_NEAREST_VERTEX) {
data->bvh = BKE_bvhtree_from_mesh_get(&data->treeData, mesh, BVHTREE_FROM_VERTS, 2);
@@ -191,9 +192,9 @@ static void merge_vert_dir(ShrinkwrapBoundaryVertData *vdata,
static ShrinkwrapBoundaryData *shrinkwrap_build_boundary_data(struct Mesh *mesh)
{
- const MLoop *mloop = mesh->mloop;
- const MEdge *medge = mesh->medge;
- const MVert *mvert = mesh->mvert;
+ const MVert *mvert = BKE_mesh_verts(mesh);
+ const MEdge *medge = BKE_mesh_edges(mesh);
+ const MLoop *mloop = BKE_mesh_loops(mesh);
/* Count faces per edge (up to 2). */
char *edge_mode = MEM_calloc_arrayN((size_t)mesh->totedge, sizeof(char), __func__);
@@ -666,7 +667,7 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc)
}
if (calc->aux_target) {
- auxMesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(calc->aux_target, false);
+ auxMesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(calc->aux_target);
if (!auxMesh) {
return;
}
@@ -937,7 +938,7 @@ static void target_project_edge(const ShrinkwrapTreeData *tree,
int eidx)
{
const BVHTreeFromMesh *data = &tree->treeData;
- const MEdge *edge = &tree->mesh->medge[eidx];
+ const MEdge *edge = &data->edge[eidx];
const float *vedge_co[2] = {data->vert[edge->v1].co, data->vert[edge->v2].co};
#ifdef TRACE_TARGET_PROJECT
@@ -1179,7 +1180,7 @@ void BKE_shrinkwrap_compute_smooth_normal(const struct ShrinkwrapTreeData *tree,
const float(*vert_normals)[3] = tree->treeData.vert_normals;
/* Interpolate smooth normals if enabled. */
- if ((tree->mesh->mpoly[tri->poly].flag & ME_SMOOTH) != 0) {
+ if ((tree->polys[tri->poly].flag & ME_SMOOTH) != 0) {
const uint32_t vert_indices[3] = {treeData->loop[tri->tri[0]].v,
treeData->loop[tri->tri[1]].v,
treeData->loop[tri->tri[2]].v};
@@ -1369,7 +1370,7 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd,
struct Scene *scene,
Object *ob,
Mesh *mesh,
- MDeformVert *dvert,
+ const MDeformVert *dvert,
const int defgrp_index,
float (*vertexCos)[3],
int numVerts)
@@ -1397,7 +1398,7 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd,
if (smd->target != NULL) {
Object *ob_target = DEG_get_evaluated_object(ctx->depsgraph, smd->target);
- calc.target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false);
+ calc.target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
/* TODO: there might be several "bugs" with non-uniform scales matrices
* because it will no longer be nearest surface, not sphere projection
@@ -1411,7 +1412,7 @@ void shrinkwrapModifier_deform(ShrinkwrapModifierData *smd,
if (mesh != NULL && smd->shrinkType == MOD_SHRINKWRAP_PROJECT) {
/* Setup arrays to get vertexs positions, normals and deform weights */
- calc.vert = mesh->mvert;
+ calc.vert = BKE_mesh_verts_for_write(mesh);
calc.vert_normals = BKE_mesh_vertex_normals_ensure(mesh);
/* Using vertexs positions/normals as if a subsurface was applied */
@@ -1574,7 +1575,7 @@ void BKE_shrinkwrap_remesh_target_project(Mesh *src_me, Mesh *target_me, Object
calc.vgroup = -1;
calc.target = target_me;
calc.keepDist = ssmd.keepDist;
- calc.vert = src_me->mvert;
+ calc.vert = BKE_mesh_verts_for_write(src_me);
BLI_SPACE_TRANSFORM_SETUP(&calc.local2target, ob_target, ob_target);
ShrinkwrapTreeData tree;
diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc
index 260d67de4d8..9d4d6a4e350 100644
--- a/source/blender/blenkernel/intern/simulation.cc
+++ b/source/blender/blenkernel/intern/simulation.cc
@@ -51,8 +51,7 @@ static void simulation_init_data(ID *id)
MEMCPY_STRUCT_AFTER(simulation, DNA_struct_default_get(Simulation), id);
- bNodeTree *ntree = ntreeAddTree(nullptr, "Geometry Nodetree", ntreeType_Geometry->idname);
- simulation->nodetree = ntree;
+ ntreeAddTreeEmbedded(nullptr, id, "Geometry Nodetree", ntreeType_Geometry->idname);
}
static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int flag)
@@ -68,6 +67,7 @@ static void simulation_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons
(ID *)simulation_src->nodetree,
(ID **)&simulation_dst->nodetree,
flag_private_id_data);
+ simulation_dst->nodetree->owner_id = &simulation_dst->id;
}
}
@@ -149,7 +149,7 @@ IDTypeInfo IDType_ID_SIM = {
/* foreach_id */ simulation_foreach_id,
/* foreach_cache */ nullptr,
/* foreach_path */ nullptr,
- /* owner_get */ nullptr,
+ /* owner_pointer_get */ nullptr,
/* blend_write */ simulation_blend_write,
/* blend_read_data */ simulation_blend_read_data,
diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c
index afb8d5cb9f8..d1451353feb 100644
--- a/source/blender/blenkernel/intern/softbody.c
+++ b/source/blender/blenkernel/intern/softbody.c
@@ -567,7 +567,7 @@ static void ccd_update_deflector_hash(Depsgraph *depsgraph,
static int count_mesh_quads(Mesh *me)
{
int a, result = 0;
- const MPoly *mp = me->mpoly;
+ const MPoly *mp = BKE_mesh_polys(me);
if (mp) {
for (a = me->totpoly; a > 0; a--, mp++) {
@@ -591,8 +591,8 @@ static void add_mesh_quad_diag_springs(Object *ob)
nofquads = count_mesh_quads(me);
if (nofquads) {
- const MLoop *mloop = me->mloop;
- const MPoly *mp = me->mpoly;
+ const MLoop *mloop = BKE_mesh_loops(me);
+ const MPoly *mp = BKE_mesh_polys(me);
BodySpring *bs;
/* resize spring-array to hold additional quad springs */
@@ -2632,6 +2632,7 @@ static void springs_from_mesh(Object *ob)
BodyPoint *bp;
int a;
float scale = 1.0f;
+ const MVert *verts = BKE_mesh_verts(me);
sb = ob->soft;
if (me && sb) {
@@ -2642,7 +2643,7 @@ static void springs_from_mesh(Object *ob)
if (me->totvert) {
bp = ob->soft->bpoint;
for (a = 0; a < me->totvert; a++, bp++) {
- copy_v3_v3(bp->origS, me->mvert[a].co);
+ copy_v3_v3(bp->origS, verts[a].co);
mul_m4_v3(ob->obmat, bp->origS);
}
}
@@ -2663,7 +2664,7 @@ static void mesh_to_softbody(Object *ob)
{
SoftBody *sb;
Mesh *me = ob->data;
- MEdge *medge = me->medge;
+ const MEdge *medge = BKE_mesh_edges(me);
BodyPoint *bp;
BodySpring *bs;
int a, totedge;
@@ -2683,10 +2684,11 @@ static void mesh_to_softbody(Object *ob)
sb = ob->soft;
bp = sb->bpoint;
- defgroup_index = me->dvert ? (sb->vertgroup - 1) : -1;
- defgroup_index_mass = me->dvert ? BKE_id_defgroup_name_index(&me->id, sb->namedVG_Mass) : -1;
- defgroup_index_spring = me->dvert ? BKE_id_defgroup_name_index(&me->id, sb->namedVG_Spring_K) :
- -1;
+ const MDeformVert *dvert = BKE_mesh_deform_verts(me);
+
+ defgroup_index = dvert ? (sb->vertgroup - 1) : -1;
+ defgroup_index_mass = dvert ? BKE_id_defgroup_name_index(&me->id, sb->namedVG_Mass) : -1;
+ defgroup_index_spring = dvert ? BKE_id_defgroup_name_index(&me->id, sb->namedVG_Spring_K) : -1;
for (a = 0; a < me->totvert; a++, bp++) {
/* get scalar values needed *per vertex* from vertex group functions,
@@ -2699,7 +2701,7 @@ static void mesh_to_softbody(Object *ob)
BLI_assert(bp->goal == sb->defgoal);
}
if ((ob->softflag & OB_SB_GOAL) && (defgroup_index != -1)) {
- bp->goal *= BKE_defvert_find_weight(&me->dvert[a], defgroup_index);
+ bp->goal *= BKE_defvert_find_weight(&dvert[a], defgroup_index);
}
/* to proof the concept
@@ -2707,11 +2709,11 @@ static void mesh_to_softbody(Object *ob)
*/
if (defgroup_index_mass != -1) {
- bp->mass *= BKE_defvert_find_weight(&me->dvert[a], defgroup_index_mass);
+ bp->mass *= BKE_defvert_find_weight(&dvert[a], defgroup_index_mass);
}
if (defgroup_index_spring != -1) {
- bp->springweight *= BKE_defvert_find_weight(&me->dvert[a], defgroup_index_spring);
+ bp->springweight *= BKE_defvert_find_weight(&dvert[a], defgroup_index_spring);
}
}
@@ -2753,19 +2755,23 @@ static void mesh_faces_to_scratch(Object *ob)
MLoopTri *looptri, *lt;
BodyFace *bodyface;
int a;
+ const MVert *verts = BKE_mesh_verts(me);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+
/* Allocate and copy faces. */
sb->scratch->totface = poly_to_tri_count(me->totpoly, me->totloop);
looptri = lt = MEM_mallocN(sizeof(*looptri) * sb->scratch->totface, __func__);
- BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
+ BKE_mesh_recalc_looptri(loops, polys, verts, me->totloop, me->totpoly, looptri);
bodyface = sb->scratch->bodyface = MEM_mallocN(sizeof(BodyFace) * sb->scratch->totface,
"SB_body_Faces");
for (a = 0; a < sb->scratch->totface; a++, lt++, bodyface++) {
- bodyface->v1 = me->mloop[lt->tri[0]].v;
- bodyface->v2 = me->mloop[lt->tri[1]].v;
- bodyface->v3 = me->mloop[lt->tri[2]].v;
+ bodyface->v1 = loops[lt->tri[0]].v;
+ bodyface->v2 = loops[lt->tri[1]].v;
+ bodyface->v3 = loops[lt->tri[2]].v;
zero_v3(bodyface->ext_force);
bodyface->ext_force[0] = bodyface->ext_force[1] = bodyface->ext_force[2] = 0.0f;
bodyface->flag = 0;
diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c
index f459b5a82ac..de1d0d3c30e 100644
--- a/source/blender/blenkernel/intern/sound.c
+++ b/source/blender/blenkernel/intern/sound.c
@@ -211,7 +211,7 @@ IDTypeInfo IDType_ID_SO = {
.foreach_id = NULL,
.foreach_cache = sound_foreach_cache,
.foreach_path = sound_foreach_path,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = sound_blend_write,
.blend_read_data = sound_blend_read_data,
@@ -756,7 +756,7 @@ void BKE_sound_remove_scene_sound(Scene *scene, void *handle)
AUD_Sequence_remove(scene->sound_scene, handle);
}
-void BKE_sound_mute_scene_sound(void *handle, char mute)
+void BKE_sound_mute_scene_sound(void *handle, bool mute)
{
AUD_SequenceEntry_setMuted(handle, mute);
}
@@ -1346,7 +1346,7 @@ void *BKE_sound_add_scene_sound_defaults(Scene *UNUSED(scene), Sequence *UNUSED(
void BKE_sound_remove_scene_sound(Scene *UNUSED(scene), void *UNUSED(handle))
{
}
-void BKE_sound_mute_scene_sound(void *UNUSED(handle), char UNUSED(mute))
+void BKE_sound_mute_scene_sound(void *UNUSED(handle), bool UNUSED(mute))
{
}
void BKE_sound_move_scene_sound(const Scene *UNUSED(scene),
diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c
index a8b76954a6f..3d49176d00a 100644
--- a/source/blender/blenkernel/intern/speaker.c
+++ b/source/blender/blenkernel/intern/speaker.c
@@ -94,7 +94,7 @@ IDTypeInfo IDType_ID_SPK = {
.foreach_id = speaker_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = speaker_blend_write,
.blend_read_data = speaker_blend_read_data,
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
deleted file mode 100644
index a674bf7800a..00000000000
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ /dev/null
@@ -1,526 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_array.hh"
-#include "BLI_generic_virtual_array.hh"
-#include "BLI_span.hh"
-#include "BLI_task.hh"
-#include "BLI_timeit.hh"
-
-#include "BKE_attribute_math.hh"
-#include "BKE_spline.hh"
-
-using blender::Array;
-using blender::float3;
-using blender::GMutableSpan;
-using blender::GSpan;
-using blender::GVArray;
-using blender::IndexRange;
-using blender::MutableSpan;
-using blender::Span;
-using blender::VArray;
-using blender::attribute_math::convert_to_static_type;
-using blender::bke::AttributeIDRef;
-using blender::bke::AttributeMetaData;
-
-CurveType Spline::type() const
-{
- return type_;
-}
-
-void Spline::copy_base_settings(const Spline &src, Spline &dst)
-{
- dst.normal_mode = src.normal_mode;
- dst.is_cyclic_ = src.is_cyclic_;
-}
-
-static SplinePtr create_spline(const CurveType type)
-{
- switch (type) {
- case CURVE_TYPE_POLY:
- return std::make_unique<PolySpline>();
- case CURVE_TYPE_BEZIER:
- return std::make_unique<BezierSpline>();
- case CURVE_TYPE_NURBS:
- return std::make_unique<NURBSpline>();
- case CURVE_TYPE_CATMULL_ROM:
- BLI_assert_unreachable();
- return {};
- }
- BLI_assert_unreachable();
- return {};
-}
-
-SplinePtr Spline::copy() const
-{
- SplinePtr dst = this->copy_without_attributes();
- dst->attributes = this->attributes;
- return dst;
-}
-
-SplinePtr Spline::copy_only_settings() const
-{
- SplinePtr dst = create_spline(type_);
- this->copy_base_settings(*this, *dst);
- this->copy_settings(*dst);
- return dst;
-}
-
-SplinePtr Spline::copy_without_attributes() const
-{
- SplinePtr dst = this->copy_only_settings();
- this->copy_data(*dst);
-
- /* Though the attributes storage is empty, it still needs to know the correct size. */
- dst->attributes.reallocate(dst->size());
- return dst;
-}
-
-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();
-}
-
-void Spline::reverse()
-{
- this->positions().reverse();
- this->radii().reverse();
- this->tilts().reverse();
-
- this->attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
- std::optional<blender::GMutableSpan> attribute = this->attributes.get_for_write(id);
- if (!attribute) {
- BLI_assert_unreachable();
- return false;
- }
- convert_to_static_type(meta_data.data_type, [&](auto dummy) {
- using T = decltype(dummy);
- attribute->typed<T>().reverse();
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- this->reverse_impl();
- this->mark_cache_invalid();
-}
-
-int Spline::evaluated_edges_num() const
-{
- const int eval_num = this->evaluated_points_num();
- if (eval_num < 2) {
- /* Two points are required for an edge. */
- return 0;
- }
-
- return this->is_cyclic_ ? eval_num : eval_num - 1;
-}
-
-float Spline::length() const
-{
- Span<float> lengths = this->evaluated_lengths();
- return lengths.is_empty() ? 0.0f : this->evaluated_lengths().last();
-}
-
-int Spline::segments_num() const
-{
- const int num = this->size();
-
- return is_cyclic_ ? num : num - 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)
-{
- using namespace blender::math;
-
- float length = 0.0f;
- for (const int i : IndexRange(positions.size() - 1)) {
- length += distance(positions[i], positions[i + 1]);
- lengths[i] = length;
- }
- if (is_cyclic) {
- lengths.last() = length + distance(positions.last(), positions.first());
- }
-}
-
-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_num();
- evaluated_lengths_cache_.resize(total);
- if (total != 0) {
- 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)
-{
- using namespace blender::math;
-
- const float3 dir_prev = normalize(middle - prev);
- const float3 dir_next = normalize(next - middle);
-
- const float3 result = normalize(dir_prev + dir_next);
- if (UNLIKELY(is_zero(result))) {
- return float3(0.0f, 0.0f, 1.0f);
- }
- return result;
-}
-
-static void calculate_tangents(Span<float3> positions,
- const bool is_cyclic,
- MutableSpan<float3> tangents)
-{
- using namespace blender::math;
-
- if (positions.size() == 1) {
- tangents.first() = float3(0.0f, 0.0f, 1.0f);
- 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() = normalize(positions[1] - positions[0]);
- tangents.last() = normalize(positions.last() - positions[positions.size() - 2]);
- }
-}
-
-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_num = this->evaluated_points_num();
- evaluated_tangents_cache_.resize(eval_num);
-
- Span<float3> positions = this->evaluated_positions();
-
- 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)
-{
- using namespace blender::math;
-
- BLI_ASSERT_UNIT_V3(direction);
- BLI_ASSERT_UNIT_V3(axis);
-
- const float3 axis_scaled = axis * dot(direction, axis);
- const float3 diff = direction - axis_scaled;
- const float3 cross = blender::math::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> r_normals)
-{
- using namespace blender::math;
-
- BLI_assert(r_normals.size() == tangents.size());
-
- /* Same as in `vec_to_quat`. */
- const float epsilon = 1e-4f;
- for (const int i : r_normals.index_range()) {
- const float3 &tangent = tangents[i];
- if (fabsf(tangent.x) + fabsf(tangent.y) < epsilon) {
- r_normals[i] = {1.0f, 0.0f, 0.0f};
- }
- else {
- r_normals[i] = normalize(float3(tangent.y, -tangent.x, 0.0f));
- }
- }
-}
-
-/**
- * Rotate the last normal in the same way the tangent has been rotated.
- */
-static float3 calculate_next_normal(const float3 &last_normal,
- const float3 &last_tangent,
- const float3 &current_tangent)
-{
- using namespace blender::math;
-
- if (is_zero(last_tangent) || is_zero(current_tangent)) {
- return last_normal;
- }
- const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
- if (angle != 0.0) {
- const float3 axis = normalize(cross(last_tangent, current_tangent));
- return rotate_direction_around_axis(last_normal, axis, angle);
- }
- return last_normal;
-}
-
-static void calculate_normals_minimum(Span<float3> tangents,
- const bool cyclic,
- MutableSpan<float3> r_normals)
-{
- using namespace blender::math;
- BLI_assert(r_normals.size() == tangents.size());
-
- if (r_normals.is_empty()) {
- return;
- }
-
- const float epsilon = 1e-4f;
-
- /* Set initial normal. */
- const float3 &first_tangent = tangents[0];
- if (fabs(first_tangent.x) + fabs(first_tangent.y) < epsilon) {
- r_normals[0] = {1.0f, 0.0f, 0.0f};
- }
- else {
- r_normals[0] = normalize(float3(first_tangent.y, -first_tangent.x, 0.0f));
- }
-
- /* Forward normal with minimum twist along the entire spline. */
- for (const int i : IndexRange(1, r_normals.size() - 1)) {
- r_normals[i] = calculate_next_normal(r_normals[i - 1], tangents[i - 1], tangents[i]);
- }
-
- if (!cyclic) {
- return;
- }
-
- /* Compute how much the first normal deviates from the normal that has been forwarded along the
- * entire cyclic spline. */
- const float3 uncorrected_last_normal = calculate_next_normal(
- r_normals.last(), tangents.last(), tangents[0]);
- float correction_angle = angle_signed_on_axis_v3v3_v3(
- r_normals[0], uncorrected_last_normal, tangents[0]);
- if (correction_angle > M_PI) {
- correction_angle = correction_angle - 2 * M_PI;
- }
-
- /* Gradually apply correction by rotating all normals slightly. */
- const float angle_step = correction_angle / r_normals.size();
- for (const int i : r_normals.index_range()) {
- const float angle = angle_step * i;
- r_normals[i] = rotate_direction_around_axis(r_normals[i], tangents[i], angle);
- }
-}
-
-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_num = this->evaluated_points_num();
- evaluated_normals_cache_.resize(eval_num);
-
- Span<float3> tangents = this->evaluated_tangents();
- MutableSpan<float3> normals = evaluated_normals_cache_;
-
- /* Only Z up normals are supported at the moment. */
- switch (this->normal_mode) {
- case NORMAL_MODE_Z_UP: {
- calculate_normals_z_up(tangents, normals);
- break;
- }
- case NORMAL_MODE_MINIMUM_TWIST: {
- calculate_normals_minimum(tangents, is_cyclic_, normals);
- break;
- }
- }
-
- /* Rotate the generated normals with the interpolated tilt data. */
- VArray<float> tilts = this->interpolate_to_evaluated(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);
-}
-
-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->evaluated_points_num() - 1) ? 0 : index + 1;
-
- const float previous_length = (index == 0) ? 0.0f : lengths[index - 1];
- const float length_in_segment = length - previous_length;
- const float segment_length = lengths[index] - previous_length;
- const float factor = segment_length == 0.0f ? 0.0f : length_in_segment / segment_length;
-
- return LookupResult{index, next_index, factor};
-}
-
-Array<float> Spline::sample_uniform_index_factors(const int samples_num) const
-{
- const Span<float> lengths = this->evaluated_lengths();
-
- BLI_assert(samples_num > 0);
- Array<float> samples(samples_num);
-
- samples[0] = 0.0f;
- if (samples_num == 1) {
- return samples;
- }
-
- const float total_length = this->length();
- const float sample_length = total_length / (samples_num - (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_num())) {
- const float length = lengths[i_evaluated];
-
- /* Add every sample that fits in this evaluated edge. */
- while ((sample_length * i_sample) < length && i_sample < samples_num) {
- const float factor = (sample_length * i_sample - prev_length) / (length - prev_length);
- samples[i_sample] = i_evaluated + factor;
- i_sample++;
- }
-
- prev_length = length;
- }
-
- /* Zero lengths or float inaccuracies can cause invalid values, or simply
- * skip some, so set the values that weren't completed in the main loop. */
- for (const int i : IndexRange(i_sample, samples_num - i_sample)) {
- samples[i] = float(samples_num);
- }
-
- 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 eval_num = this->evaluated_points_num();
-
- if (is_cyclic_) {
- if (index_factor < eval_num) {
- const int index = std::floor(index_factor);
- const int next_index = (index < eval_num - 1) ? index + 1 : 0;
- return LookupResult{index, next_index, index_factor - index};
- }
- return LookupResult{eval_num - 1, 0, 1.0f};
- }
-
- if (index_factor < eval_num - 1) {
- const int index = std::floor(index_factor);
- const int next_index = index + 1;
- return LookupResult{index, next_index, index_factor - index};
- }
- return LookupResult{eval_num - 2, eval_num - 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);
- }
-}
-
-GVArray Spline::interpolate_to_evaluated(GSpan data) const
-{
- return this->interpolate_to_evaluated(GVArray::ForSpan(data));
-}
-
-void Spline::sample_with_index_factors(const GVArray &src,
- Span<float> index_factors,
- GMutableSpan dst) const
-{
- BLI_assert(src.size() == this->evaluated_points_num());
-
- blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- const VArray<T> src_typed = src.typed<T>();
- MutableSpan<T> dst_typed = dst.typed<T>();
- if (src.size() == 1) {
- dst_typed.fill(src_typed[0]);
- return;
- }
- blender::threading::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
deleted file mode 100644
index 80515d0ef37..00000000000
--- a/source/blender/blenkernel/intern/spline_bezier.cc
+++ /dev/null
@@ -1,646 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_array.hh"
-#include "BLI_span.hh"
-#include "BLI_task.hh"
-
-#include "BKE_spline.hh"
-
-using blender::Array;
-using blender::float3;
-using blender::GVArray;
-using blender::IndexRange;
-using blender::MutableSpan;
-using blender::Span;
-using blender::VArray;
-
-void BezierSpline::copy_settings(Spline &dst) const
-{
- BezierSpline &bezier = static_cast<BezierSpline &>(dst);
- bezier.resolution_ = resolution_;
-}
-
-void BezierSpline::copy_data(Spline &dst) const
-{
- BezierSpline &bezier = static_cast<BezierSpline &>(dst);
- bezier.positions_ = positions_;
- bezier.handle_types_left_ = handle_types_left_;
- bezier.handle_positions_left_ = handle_positions_left_;
- bezier.handle_types_right_ = handle_types_right_;
- bezier.handle_positions_right_ = handle_positions_right_;
- bezier.radii_ = radii_;
- bezier.tilts_ = tilts_;
-}
-
-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();
-}
-
-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<int8_t> BezierSpline::handle_types_left() const
-{
- return handle_types_left_;
-}
-MutableSpan<int8_t> 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(const bool write_only)
-{
- if (!write_only) {
- this->ensure_auto_handles();
- }
- return handle_positions_left_;
-}
-
-Span<int8_t> BezierSpline::handle_types_right() const
-{
- return handle_types_right_;
-}
-MutableSpan<int8_t> 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(const bool write_only)
-{
- if (!write_only) {
- this->ensure_auto_handles();
- }
- return handle_positions_right_;
-}
-
-void BezierSpline::reverse_impl()
-{
- this->handle_positions_left().reverse();
- this->handle_positions_right().reverse();
- std::swap(this->handle_positions_left_, this->handle_positions_right_);
-
- this->handle_types_left().reverse();
- this->handle_types_right().reverse();
- std::swap(this->handle_types_left_, this->handle_types_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];
-}
-
-void BezierSpline::ensure_auto_handles() const
-{
- if (!auto_handles_dirty_) {
- return;
- }
-
- std::lock_guard lock{auto_handle_mutex_};
- if (!auto_handles_dirty_) {
- return;
- }
-
- if (this->size() == 1) {
- auto_handles_dirty_ = false;
- return;
- }
-
- for (const int i : IndexRange(this->size())) {
- using namespace blender;
-
- if (ELEM(BEZIER_HANDLE_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 = math::length(prev_diff);
- float next_len = math::length(next_diff);
- 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 = math::length(dir) * 2.5614f;
- if (len != 0.0f) {
- if (handle_types_left_[i] == BEZIER_HANDLE_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] == BEZIER_HANDLE_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] == BEZIER_HANDLE_VECTOR) {
- const float3 prev = previous_position(positions_, is_cyclic_, i);
- handle_positions_left_[i] = math::interpolate(positions_[i], prev, 1.0f / 3.0f);
- }
-
- if (handle_types_right_[i] == BEZIER_HANDLE_VECTOR) {
- const float3 next = next_position(positions_, is_cyclic_, i);
- handle_positions_right_[i] = math::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();
-}
-
-static void set_handle_position(const float3 &position,
- const HandleType type,
- const HandleType type_other,
- const float3 &new_value,
- float3 &handle,
- float3 &handle_other)
-{
- using namespace blender::math;
-
- /* Don't bother when the handle positions are calculated automatically anyway. */
- if (ELEM(type, BEZIER_HANDLE_AUTO, BEZIER_HANDLE_VECTOR)) {
- return;
- }
-
- handle = new_value;
- if (type_other == BEZIER_HANDLE_ALIGN) {
- /* Keep track of the old length of the opposite handle. */
- const float length = distance(handle_other, position);
- /* Set the other handle to directly opposite from the current handle. */
- const float3 dir = normalize(handle - position);
- handle_other = position - dir * length;
- }
-}
-
-void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value)
-{
- set_handle_position(positions_[index],
- static_cast<HandleType>(handle_types_right_[index]),
- static_cast<HandleType>(handle_types_left_[index]),
- value,
- handle_positions_right_[index],
- handle_positions_left_[index]);
-}
-
-void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value)
-{
- set_handle_position(positions_[index],
- static_cast<HandleType>(handle_types_right_[index]),
- static_cast<HandleType>(handle_types_left_[index]),
- value,
- handle_positions_left_[index],
- handle_positions_right_[index]);
-}
-
-bool BezierSpline::point_is_sharp(const int index) const
-{
- return ELEM(handle_types_left_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE) ||
- ELEM(handle_types_right_[index], BEZIER_HANDLE_VECTOR, BEZIER_HANDLE_FREE);
-}
-
-bool BezierSpline::segment_is_vector(const int index) const
-{
- /* Two control points are necessary to form a segment, that should be checked by the caller. */
- BLI_assert(this->size() > 1);
-
- if (index == this->size() - 1) {
- if (is_cyclic_) {
- return handle_types_right_.last() == BEZIER_HANDLE_VECTOR &&
- handle_types_left_.first() == BEZIER_HANDLE_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] == BEZIER_HANDLE_VECTOR &&
- handle_types_left_[index + 1] == BEZIER_HANDLE_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_num() const
-{
- BLI_assert(this->size() > 0);
- return this->control_point_offsets().last();
-}
-
-void BezierSpline::correct_end_tangents() const
-{
- using namespace blender::math;
- if (is_cyclic_) {
- return;
- }
-
- MutableSpan<float3> tangents(evaluated_tangents_cache_);
-
- if (handle_positions_right_.first() != positions_.first()) {
- tangents.first() = normalize(handle_positions_right_.first() - positions_.first());
- }
- if (handle_positions_left_.last() != positions_.last()) {
- tangents.last() = normalize(positions_.last() - handle_positions_left_.last());
- }
-}
-
-BezierSpline::InsertResult BezierSpline::calculate_segment_insertion(const int index,
- const int next_index,
- const float parameter)
-{
- using namespace blender::math;
-
- BLI_assert(parameter <= 1.0f && parameter >= 0.0f);
- BLI_assert(ELEM(next_index, 0, index + 1));
- const float3 &point_prev = positions_[index];
- const float3 &handle_prev = handle_positions_right_[index];
- const float3 &handle_next = handle_positions_left_[next_index];
- const float3 &point_next = positions_[next_index];
- const float3 center_point = interpolate(handle_prev, handle_next, parameter);
-
- BezierSpline::InsertResult result;
- result.handle_prev = interpolate(point_prev, handle_prev, parameter);
- result.handle_next = interpolate(handle_next, point_next, parameter);
- result.left_handle = interpolate(result.handle_prev, center_point, parameter);
- result.right_handle = interpolate(center_point, result.handle_next, parameter);
- result.position = interpolate(result.left_handle, result.right_handle, parameter);
- return result;
-}
-
-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_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);
- }
-}
-
-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 size = this->size();
- offset_cache_.resize(size + 1);
-
- MutableSpan<int> offsets = offset_cache_;
- if (size == 1) {
- offsets.first() = 0;
- offsets.last() = 1;
- }
- else {
- int offset = 0;
- for (const int i : IndexRange(size)) {
- 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);
- blender::threading::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;
- }
-}
-
-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 num = this->size();
- const int eval_num = this->evaluated_points_num();
- evaluated_mapping_cache_.resize(eval_num);
- MutableSpan<float> mappings = evaluated_mapping_cache_;
-
- if (eval_num == 1) {
- mappings.first() = 0.0f;
- mapping_cache_dirty_ = false;
- return mappings;
- }
-
- Span<int> offsets = this->control_point_offsets();
-
- blender::threading::isolate_task([&]() {
- /* Isolate the task, since this is function is multi-threaded and holds a lock. */
- calculate_mappings_linear_resolution(offsets, num, 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_;
- }
-
- const int num = this->size();
- const int eval_num = this->evaluated_points_num();
- evaluated_position_cache_.resize(eval_num);
-
- MutableSpan<float3> positions = evaluated_position_cache_;
-
- if (num == 1) {
- /* Use a special case for single point splines to avoid checking in #evaluate_segment. */
- BLI_assert(eval_num == 1);
- positions.first() = positions_.first();
- position_cache_dirty_ = false;
- return positions;
- }
-
- this->ensure_auto_handles();
-
- Span<int> offsets = this->control_point_offsets();
-
- const int grain_size = std::max(512 / resolution_, 1);
- blender::threading::isolate_task([&]() {
- /* Isolate the task, since this is function is multi-threaded and holds a lock. */
- blender::threading::parallel_for(IndexRange(num - 1), grain_size, [&](IndexRange range) {
- for (const int i : range) {
- this->evaluate_segment(i, i + 1, positions.slice(offsets[i], offsets[i + 1] - offsets[i]));
- }
- });
- });
- if (is_cyclic_) {
- this->evaluate_segment(
- num - 1, 0, positions.slice(offsets[num - 1], offsets[num] - offsets[num - 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;
-}
-
-BezierSpline::InterpolationData BezierSpline::interpolation_data_from_index_factor(
- const float index_factor) const
-{
- const int num = this->size();
-
- if (is_cyclic_) {
- if (index_factor < num) {
- const int index = std::floor(index_factor);
- const int next_index = (index < num - 1) ? index + 1 : 0;
- return InterpolationData{index, next_index, index_factor - index};
- }
- return InterpolationData{num - 1, 0, 1.0f};
- }
-
- if (index_factor < num - 1) {
- const int index = std::floor(index_factor);
- const int next_index = index + 1;
- return InterpolationData{index, next_index, index_factor - index};
- }
- return InterpolationData{num - 2, num - 1, 1.0f};
-}
-
-/* Use a spline argument to avoid adding this to the header. */
-template<typename T>
-static void interpolate_to_evaluated_impl(const BezierSpline &spline,
- const blender::VArray<T> &src,
- MutableSpan<T> dst)
-{
- BLI_assert(src.size() == spline.size());
- BLI_assert(dst.size() == spline.evaluated_points_num());
- Span<float> mappings = spline.evaluated_mappings();
-
- for (const int i : dst.index_range()) {
- BezierSpline::InterpolationData interp = spline.interpolation_data_from_index_factor(
- mappings[i]);
-
- const T &value = src[interp.control_point_index];
- const T &next_value = src[interp.next_control_point_index];
-
- dst[i] = blender::attribute_math::mix2(interp.factor, value, next_value);
- }
-}
-
-GVArray BezierSpline::interpolate_to_evaluated(const GVArray &src) const
-{
- BLI_assert(src.size() == this->size());
-
- if (src.is_single()) {
- return src;
- }
-
- const int eval_num = this->evaluated_points_num();
- if (eval_num == 1) {
- return src;
- }
-
- GVArray new_varray;
- blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
- Array<T> values(eval_num);
- interpolate_to_evaluated_impl<T>(*this, src.typed<T>(), values);
- new_varray = VArray<T>::ForContainer(std::move(values));
- }
- });
-
- return new_varray;
-}
diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc
deleted file mode 100644
index a7eeb82d854..00000000000
--- a/source/blender/blenkernel/intern/spline_nurbs.cc
+++ /dev/null
@@ -1,395 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#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::GVArray;
-using blender::IndexRange;
-using blender::MutableSpan;
-using blender::Span;
-using blender::VArray;
-
-void NURBSpline::copy_settings(Spline &dst) const
-{
- NURBSpline &nurbs = static_cast<NURBSpline &>(dst);
- nurbs.knots_mode = knots_mode;
- nurbs.resolution_ = resolution_;
- nurbs.order_ = order_;
-}
-
-void NURBSpline::copy_data(Spline &dst) const
-{
- NURBSpline &nurbs = static_cast<NURBSpline &>(dst);
- nurbs.positions_ = positions_;
- nurbs.weights_ = weights_;
- nurbs.knots_ = knots_;
- nurbs.knots_dirty_ = knots_dirty_;
- nurbs.radii_ = radii_;
- nurbs.tilts_ = tilts_;
-}
-
-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();
-}
-
-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::reverse_impl()
-{
- this->weights().reverse();
-}
-
-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_num() const
-{
- if (!this->check_valid_num_and_order()) {
- return 0;
- }
- return resolution_ * this->segments_num();
-}
-
-void NURBSpline::correct_end_tangents() const
-{
-}
-
-bool NURBSpline::check_valid_num_and_order() const
-{
- if (this->size() < order_) {
- return false;
- }
-
- if (ELEM(this->knots_mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER)) {
- if (this->knots_mode == NURBS_KNOT_MODE_BEZIER && this->size() <= order_) {
- return false;
- }
- return (!is_cyclic_ || this->size() % (order_ - 1) == 0);
- }
-
- return true;
-}
-
-int NURBSpline::knots_num() const
-{
- const int num = this->size() + order_;
- return is_cyclic_ ? num + order_ - 1 : num;
-}
-
-void NURBSpline::calculate_knots() const
-{
- const KnotsMode mode = this->knots_mode;
- const int order = order_;
- const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER);
- const bool is_end_point = ELEM(mode, NURBS_KNOT_MODE_ENDPOINT, NURBS_KNOT_MODE_ENDPOINT_BEZIER);
- /* Inner knots are always repeated once except on Bezier case. */
- const int repeat_inner = is_bezier ? order - 1 : 1;
- /* How many times to repeat 0.0 at the beginning of knot. */
- const int head = is_end_point ? (order - (is_cyclic_ ? 1 : 0)) :
- (is_bezier ? min_ii(2, repeat_inner) : 1);
- /* Number of knots replicating widths of the starting knots.
- * Covers both Cyclic and EndPoint cases. */
- const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0);
-
- knots_.resize(this->knots_num());
- MutableSpan<float> knots = knots_;
-
- int r = head;
- float current = 0.0f;
-
- const int offset = is_end_point && is_cyclic_ ? 1 : 0;
- if (offset) {
- knots[0] = current;
- current += 1.0f;
- }
-
- for (const int i : IndexRange(offset, knots.size() - offset - tail)) {
- knots[i] = current;
- r--;
- if (r == 0) {
- current += 1.0;
- r = repeat_inner;
- }
- }
-
- const int tail_index = knots.size() - tail;
- for (const int i : IndexRange(tail)) {
- knots[tail_index + i] = current + (knots[i] - knots[0]);
- }
-}
-
-Span<float> NURBSpline::knots() const
-{
- if (!knots_dirty_) {
- BLI_assert(knots_.size() == this->knots_num());
- return knots_;
- }
-
- std::lock_guard lock{knots_mutex_};
- if (!knots_dirty_) {
- BLI_assert(knots_.size() == this->knots_num());
- return knots_;
- }
-
- this->calculate_knots();
-
- knots_dirty_ = false;
-
- return knots_;
-}
-
-static void calculate_basis_for_point(const float parameter,
- const int num,
- const int degree,
- const Span<float> knots,
- MutableSpan<float> r_weights,
- int &r_start_index)
-{
- const int order = degree + 1;
-
- int start = 0;
- int end = 0;
- for (const int i : IndexRange(num + degree)) {
- const bool knots_equal = knots[i] == knots[i + 1];
- if (knots_equal || parameter < knots[i] || parameter > knots[i + 1]) {
- continue;
- }
-
- start = std::max(i - degree, 0);
- end = i;
- break;
- }
-
- Array<float, 12> buffer(order * 2, 0.0f);
-
- buffer[end - start] = 1.0f;
-
- for (const int i_order : IndexRange(2, degree)) {
- if (end + i_order >= knots.size()) {
- end = num + degree - i_order;
- }
- for (const int i : IndexRange(end - start + 1)) {
- const int knot_index = start + i;
-
- float new_basis = 0.0f;
- if (buffer[i] != 0.0f) {
- new_basis += ((parameter - knots[knot_index]) * buffer[i]) /
- (knots[knot_index + i_order - 1] - knots[knot_index]);
- }
-
- if (buffer[i + 1] != 0.0f) {
- new_basis += ((knots[knot_index + i_order] - parameter) * buffer[i + 1]) /
- (knots[knot_index + i_order] - knots[knot_index + 1]);
- }
-
- buffer[i] = new_basis;
- }
- }
-
- buffer.as_mutable_span().drop_front(end - start + 1).fill(0.0f);
- r_weights.copy_from(buffer.as_span().take_front(order));
- r_start_index = start;
-}
-
-const NURBSpline::BasisCache &NURBSpline::calculate_basis_cache() const
-{
- if (!basis_cache_dirty_) {
- return basis_cache_;
- }
-
- std::lock_guard lock{basis_cache_mutex_};
- if (!basis_cache_dirty_) {
- return basis_cache_;
- }
-
- const int num = this->size();
- const int eval_num = this->evaluated_points_num();
-
- const int order = this->order();
- const int degree = order - 1;
-
- basis_cache_.weights.resize(eval_num * order);
- basis_cache_.start_indices.resize(eval_num);
-
- if (eval_num == 0) {
- return basis_cache_;
- }
-
- MutableSpan<float> basis_weights(basis_cache_.weights);
- MutableSpan<int> basis_start_indices(basis_cache_.start_indices);
-
- const Span<float> control_weights = this->weights();
- const Span<float> knots = this->knots();
-
- const int last_control_point_index = is_cyclic_ ? num + degree : num;
-
- const float start = knots[degree];
- const float end = knots[last_control_point_index];
- const float step = (end - start) / this->evaluated_edges_num();
- for (const int i : IndexRange(eval_num)) {
- /* Clamp parameter due to floating point inaccuracy. */
- const float parameter = std::clamp(start + step * i, knots[0], knots[num + degree]);
-
- MutableSpan<float> point_weights = basis_weights.slice(i * order, order);
-
- calculate_basis_for_point(
- parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]);
-
- for (const int j : point_weights.index_range()) {
- const int point_index = (basis_start_indices[i] + j) % num;
- point_weights[j] *= control_weights[point_index];
- }
- }
-
- basis_cache_dirty_ = false;
- return basis_cache_;
-}
-
-template<typename T>
-void interpolate_to_evaluated_impl(const NURBSpline::BasisCache &basis_cache,
- const int order,
- const blender::VArray<T> &src,
- MutableSpan<T> dst)
-{
- const int num = src.size();
- blender::attribute_math::DefaultMixer<T> mixer(dst);
-
- for (const int i : dst.index_range()) {
- Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order);
- const int start_index = basis_cache.start_indices[i];
-
- for (const int j : point_weights.index_range()) {
- const int point_index = (start_index + j) % num;
- mixer.mix_in(i, src[point_index], point_weights[j]);
- }
- }
-
- mixer.finalize();
-}
-
-GVArray NURBSpline::interpolate_to_evaluated(const GVArray &src) const
-{
- BLI_assert(src.size() == this->size());
-
- if (src.is_single()) {
- return src;
- }
-
- const BasisCache &basis_cache = this->calculate_basis_cache();
-
- GVArray new_varray;
- blender::attribute_math::convert_to_static_type(src.type(), [&](auto dummy) {
- using T = decltype(dummy);
- if constexpr (!std::is_void_v<blender::attribute_math::DefaultMixer<T>>) {
- Array<T> values(this->evaluated_points_num());
- interpolate_to_evaluated_impl<T>(basis_cache, this->order(), src.typed<T>(), values);
- new_varray = VArray<T>::ForContainer(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_num = this->evaluated_points_num();
- evaluated_position_cache_.resize(eval_num);
-
- /* TODO: Avoid copying the evaluated data from the temporary array. */
- VArray<float3> evaluated = Spline::interpolate_to_evaluated(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
deleted file mode 100644
index c3cc268c81c..00000000000
--- a/source/blender/blenkernel/intern/spline_poly.cc
+++ /dev/null
@@ -1,97 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "BLI_span.hh"
-#include "BLI_virtual_array.hh"
-
-#include "BKE_spline.hh"
-
-using blender::float3;
-using blender::GVArray;
-using blender::MutableSpan;
-using blender::Span;
-
-void PolySpline::copy_settings(Spline &UNUSED(dst)) const
-{
- /* Poly splines have no settings not covered by the base class. */
-}
-
-void PolySpline::copy_data(Spline &dst) const
-{
- PolySpline &poly = static_cast<PolySpline &>(dst);
- poly.positions_ = positions_;
- poly.radii_ = radii_;
- poly.tilts_ = tilts_;
-}
-
-int PolySpline::size() const
-{
- const int size = positions_.size();
- BLI_assert(size == radii_.size());
- BLI_assert(size == tilts_.size());
- return size;
-}
-
-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::reverse_impl()
-{
-}
-
-void PolySpline::mark_cache_invalid()
-{
- tangent_cache_dirty_ = true;
- normal_cache_dirty_ = true;
- length_cache_dirty_ = true;
-}
-
-int PolySpline::evaluated_points_num() const
-{
- return this->size();
-}
-
-void PolySpline::correct_end_tangents() const
-{
-}
-
-Span<float3> PolySpline::evaluated_positions() const
-{
- return this->positions();
-}
-
-GVArray PolySpline::interpolate_to_evaluated(const GVArray &src) const
-{
- BLI_assert(src.size() == this->size());
- return src;
-}
diff --git a/source/blender/blenkernel/intern/studiolight.c b/source/blender/blenkernel/intern/studiolight.c
index f17450ac3f4..64f998ea67f 100644
--- a/source/blender/blenkernel/intern/studiolight.c
+++ b/source/blender/blenkernel/intern/studiolight.c
@@ -1166,19 +1166,21 @@ static void studiolight_add_files_from_datafolder(const int folder_id,
const char *subfolder,
int flag)
{
- struct direntry *dirs;
const char *folder = BKE_appdir_folder_id(folder_id, subfolder);
- if (folder) {
- const uint dirs_num = BLI_filelist_dir_contents(folder, &dirs);
- int i;
- for (i = 0; i < dirs_num; i++) {
- if (dirs[i].type & S_IFREG) {
- studiolight_add_file(dirs[i].path, flag);
- }
+ if (!folder) {
+ return;
+ }
+
+ struct direntry *dirs;
+ const uint dirs_num = BLI_filelist_dir_contents(folder, &dirs);
+ int i;
+ for (i = 0; i < dirs_num; i++) {
+ if (dirs[i].type & S_IFREG) {
+ studiolight_add_file(dirs[i].path, flag);
}
- BLI_filelist_free(dirs, dirs_num);
- dirs = NULL;
}
+ BLI_filelist_free(dirs, dirs_num);
+ dirs = NULL;
}
static int studiolight_flag_cmp_order(const StudioLight *sl)
diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c
index c956ef09af3..03937b270bc 100644
--- a/source/blender/blenkernel/intern/subdiv_ccg.c
+++ b/source/blender/blenkernel/intern/subdiv_ccg.c
@@ -2043,6 +2043,11 @@ void BKE_subdiv_ccg_grid_hidden_ensure(SubdivCCG *subdiv_ccg, int grid_index)
subdiv_ccg->grid_hidden[grid_index] = BLI_BITMAP_NEW(key.grid_area, __func__);
}
+void BKE_subdiv_ccg_grid_hidden_free(SubdivCCG *subdiv_ccg, int grid_index)
+{
+ MEM_SAFE_FREE(subdiv_ccg->grid_hidden[grid_index]);
+}
+
static void subdiv_ccg_coord_to_ptex_coord(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
int *r_ptex_face_index,
diff --git a/source/blender/blenkernel/intern/subdiv_ccg_mask.c b/source/blender/blenkernel/intern/subdiv_ccg_mask.c
index 1290f1e0834..86891f0fa6e 100644
--- a/source/blender/blenkernel/intern/subdiv_ccg_mask.c
+++ b/source/blender/blenkernel/intern/subdiv_ccg_mask.c
@@ -17,6 +17,7 @@
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
+#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "MEM_guardedalloc.h"
@@ -102,7 +103,7 @@ static void free_mask_data(SubdivCCGMaskEvaluator *mask_evaluator)
static int count_num_ptex_faces(const Mesh *mesh)
{
int num_ptex_faces = 0;
- const MPoly *mpoly = mesh->mpoly;
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) {
const MPoly *poly = &mpoly[poly_index];
num_ptex_faces += (poly->totloop == 4) ? 1 : poly->totloop;
@@ -113,7 +114,7 @@ static int count_num_ptex_faces(const Mesh *mesh)
static void mask_data_init_mapping(SubdivCCGMaskEvaluator *mask_evaluator, const Mesh *mesh)
{
GridPaintMaskData *data = mask_evaluator->user_data;
- const MPoly *mpoly = mesh->mpoly;
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
const int num_ptex_faces = count_num_ptex_faces(mesh);
/* Allocate memory. */
data->ptex_poly_corner = MEM_malloc_arrayN(
@@ -141,7 +142,7 @@ static void mask_data_init_mapping(SubdivCCGMaskEvaluator *mask_evaluator, const
static void mask_init_data(SubdivCCGMaskEvaluator *mask_evaluator, const Mesh *mesh)
{
GridPaintMaskData *data = mask_evaluator->user_data;
- data->mpoly = mesh->mpoly;
+ data->mpoly = BKE_mesh_polys(mesh);
data->grid_paint_mask = CustomData_get_layer(&mesh->ldata, CD_GRID_PAINT_MASK);
mask_data_init_mapping(mask_evaluator, mesh);
}
diff --git a/source/blender/blenkernel/intern/subdiv_ccg_material.c b/source/blender/blenkernel/intern/subdiv_ccg_material.c
index cf49db15b7b..891e1d1b630 100644
--- a/source/blender/blenkernel/intern/subdiv_ccg_material.c
+++ b/source/blender/blenkernel/intern/subdiv_ccg_material.c
@@ -5,6 +5,7 @@
* \ingroup bke
*/
+#include "BKE_mesh.h"
#include "BKE_subdiv_ccg.h"
#include "MEM_guardedalloc.h"
@@ -14,19 +15,19 @@
typedef struct CCGMaterialFromMeshData {
const Mesh *mesh;
+ const MPoly *polys;
+ const int *material_indices;
} CCGMaterialFromMeshData;
static DMFlagMat subdiv_ccg_material_flags_eval(
SubdivCCGMaterialFlagsEvaluator *material_flags_evaluator, const int coarse_face_index)
{
CCGMaterialFromMeshData *data = (CCGMaterialFromMeshData *)material_flags_evaluator->user_data;
- const Mesh *mesh = data->mesh;
- BLI_assert(coarse_face_index < mesh->totpoly);
- const MPoly *mpoly = mesh->mpoly;
- const MPoly *poly = &mpoly[coarse_face_index];
+ BLI_assert(coarse_face_index < data->mesh->totpoly);
+ const MPoly *poly = &data->polys[coarse_face_index];
DMFlagMat material_flags;
material_flags.flag = poly->flag;
- material_flags.mat_nr = poly->mat_nr;
+ material_flags.mat_nr = data->material_indices ? data->material_indices[coarse_face_index] : 0;
return material_flags;
}
@@ -42,6 +43,9 @@ void BKE_subdiv_ccg_material_flags_init_from_mesh(
CCGMaterialFromMeshData *data = MEM_mallocN(sizeof(CCGMaterialFromMeshData),
"ccg material eval");
data->mesh = mesh;
+ data->material_indices = (const int *)CustomData_get_layer_named(
+ &mesh->pdata, CD_PROP_INT32, "material_index");
+ data->polys = BKE_mesh_polys(mesh);
material_flags_evaluator->eval_material_flags = subdiv_ccg_material_flags_eval;
material_flags_evaluator->free = subdiv_ccg_material_flags_free;
material_flags_evaluator->user_data = data;
diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c
index 12a5f00a68b..b13aec37c78 100644
--- a/source/blender/blenkernel/intern/subdiv_converter_mesh.c
+++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c
@@ -16,6 +16,7 @@
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_subdiv.h"
@@ -33,6 +34,11 @@
typedef struct ConverterStorage {
SubdivSettings settings;
const Mesh *mesh;
+ const MVert *verts;
+ const MEdge *edges;
+ const MPoly *polys;
+ const MLoop *loops;
+
/* CustomData layer for vertex sharpnesses. */
const float *cd_vertex_crease;
/* Indexed by loop index, value denotes index of face-varying vertex
@@ -116,7 +122,7 @@ static int get_num_vertices(const OpenSubdiv_Converter *converter)
static int get_num_face_vertices(const OpenSubdiv_Converter *converter, int manifold_face_index)
{
ConverterStorage *storage = converter->user_data;
- return storage->mesh->mpoly[manifold_face_index].totloop;
+ return storage->polys[manifold_face_index].totloop;
}
static void get_face_vertices(const OpenSubdiv_Converter *converter,
@@ -124,8 +130,8 @@ static void get_face_vertices(const OpenSubdiv_Converter *converter,
int *manifold_face_vertices)
{
ConverterStorage *storage = converter->user_data;
- const MPoly *poly = &storage->mesh->mpoly[manifold_face_index];
- const MLoop *mloop = storage->mesh->mloop;
+ const MPoly *poly = &storage->polys[manifold_face_index];
+ const MLoop *mloop = storage->loops;
for (int corner = 0; corner < poly->totloop; corner++) {
manifold_face_vertices[corner] =
storage->manifold_vertex_index[mloop[poly->loopstart + corner].v];
@@ -138,7 +144,7 @@ static void get_edge_vertices(const OpenSubdiv_Converter *converter,
{
ConverterStorage *storage = converter->user_data;
const int edge_index = storage->manifold_edge_index_reverse[manifold_edge_index];
- const MEdge *edge = &storage->mesh->medge[edge_index];
+ const MEdge *edge = &storage->edges[edge_index];
manifold_edge_vertices[0] = storage->manifold_vertex_index[edge->v1];
manifold_edge_vertices[1] = storage->manifold_vertex_index[edge->v2];
}
@@ -155,7 +161,7 @@ static float get_edge_sharpness(const OpenSubdiv_Converter *converter, int manif
return 0.0f;
}
const int edge_index = storage->manifold_edge_index_reverse[manifold_edge_index];
- const MEdge *medge = storage->mesh->medge;
+ const MEdge *medge = storage->edges;
return BKE_subdiv_crease_to_sharpness_char(medge[edge_index].crease);
}
@@ -193,8 +199,6 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la
{
ConverterStorage *storage = converter->user_data;
const Mesh *mesh = storage->mesh;
- const MPoly *mpoly = mesh->mpoly;
- const MLoop *mloop = mesh->mloop;
const MLoopUV *mloopuv = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPUV, layer_index);
const int num_poly = mesh->totpoly;
const int num_vert = mesh->totvert;
@@ -205,7 +209,15 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la
mesh->totloop, sizeof(int), "loop uv vertex index");
}
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
- mpoly, mloop, mloopuv, num_poly, num_vert, limit, false, true);
+ storage->polys,
+ (const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"),
+ storage->loops,
+ mloopuv,
+ num_poly,
+ num_vert,
+ limit,
+ false,
+ true);
/* NOTE: First UV vertex is supposed to be always marked as separate. */
storage->num_uv_coordinates = -1;
for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) {
@@ -214,7 +226,7 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la
if (uv_vert->separate) {
storage->num_uv_coordinates++;
}
- const MPoly *mp = &mpoly[uv_vert->poly_index];
+ const MPoly *mp = &storage->polys[uv_vert->poly_index];
const int global_loop_index = mp->loopstart + uv_vert->loop_of_poly_index;
storage->loop_uv_indices[global_loop_index] = storage->num_uv_coordinates;
uv_vert = uv_vert->next;
@@ -242,7 +254,7 @@ static int get_face_corner_uv_index(const OpenSubdiv_Converter *converter,
const int corner)
{
ConverterStorage *storage = converter->user_data;
- const MPoly *mp = &storage->mesh->mpoly[face_index];
+ const MPoly *mp = &storage->polys[face_index];
return storage->loop_uv_indices[mp->loopstart + corner];
}
@@ -336,9 +348,9 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map,
static void initialize_manifold_indices(ConverterStorage *storage)
{
const Mesh *mesh = storage->mesh;
- const MEdge *medge = mesh->medge;
- const MLoop *mloop = mesh->mloop;
- const MPoly *mpoly = mesh->mpoly;
+ const MEdge *medge = storage->edges;
+ const MLoop *mloop = storage->loops;
+ const MPoly *mpoly = storage->polys;
/* Set bits of elements which are not loose. */
BLI_bitmap *vert_used_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
BLI_bitmap *edge_used_map = BLI_BITMAP_NEW(mesh->totedge, "edge used map");
@@ -381,6 +393,10 @@ static void init_user_data(OpenSubdiv_Converter *converter,
ConverterStorage *user_data = MEM_mallocN(sizeof(ConverterStorage), __func__);
user_data->settings = *settings;
user_data->mesh = mesh;
+ user_data->verts = BKE_mesh_verts(mesh);
+ user_data->edges = BKE_mesh_edges(mesh);
+ user_data->polys = BKE_mesh_polys(mesh);
+ user_data->loops = BKE_mesh_loops(mesh);
user_data->cd_vertex_crease = CustomData_get_layer(&mesh->vdata, CD_CREASE);
user_data->loop_uv_indices = NULL;
initialize_manifold_indices(user_data);
diff --git a/source/blender/blenkernel/intern/subdiv_displacement_multires.c b/source/blender/blenkernel/intern/subdiv_displacement_multires.c
index 0decb57bb38..398a4083ee2 100644
--- a/source/blender/blenkernel/intern/subdiv_displacement_multires.c
+++ b/source/blender/blenkernel/intern/subdiv_displacement_multires.c
@@ -18,6 +18,7 @@
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
+#include "BKE_mesh.h"
#include "BKE_multires.h"
#include "BKE_subdiv_eval.h"
@@ -360,7 +361,7 @@ static void free_displacement(SubdivDisplacement *displacement)
static int count_num_ptex_faces(const Mesh *mesh)
{
int num_ptex_faces = 0;
- const MPoly *mpoly = mesh->mpoly;
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) {
const MPoly *poly = &mpoly[poly_index];
num_ptex_faces += (poly->totloop == 4) ? 1 : poly->totloop;
@@ -371,7 +372,7 @@ static int count_num_ptex_faces(const Mesh *mesh)
static void displacement_data_init_mapping(SubdivDisplacement *displacement, const Mesh *mesh)
{
MultiresDisplacementData *data = displacement->user_data;
- const MPoly *mpoly = mesh->mpoly;
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
const int num_ptex_faces = count_num_ptex_faces(mesh);
/* Allocate memory. */
data->ptex_poly_corner = MEM_malloc_arrayN(
@@ -406,7 +407,7 @@ static void displacement_init_data(SubdivDisplacement *displacement,
data->grid_size = BKE_subdiv_grid_size_from_level(mmd->totlvl);
data->mesh = mesh;
data->mmd = mmd;
- data->mpoly = mesh->mpoly;
+ data->mpoly = BKE_mesh_polys(mesh);
data->mdisps = CustomData_get_layer(&mesh->ldata, CD_MDISPS);
data->face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv);
data->is_initialized = false;
diff --git a/source/blender/blenkernel/intern/subdiv_eval.c b/source/blender/blenkernel/intern/subdiv_eval.c
index fda833ffd27..e6f24aa6ff8 100644
--- a/source/blender/blenkernel/intern/subdiv_eval.c
+++ b/source/blender/blenkernel/intern/subdiv_eval.c
@@ -16,6 +16,7 @@
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
+#include "BKE_mesh.h"
#include "BKE_subdiv.h"
#include "MEM_guardedalloc.h"
@@ -34,8 +35,8 @@ static eOpenSubdivEvaluator opensubdiv_evalutor_from_subdiv_evaluator_type(
case SUBDIV_EVALUATOR_TYPE_CPU: {
return OPENSUBDIV_EVALUATOR_CPU;
}
- case SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE: {
- return OPENSUBDIV_EVALUATOR_GLSL_COMPUTE;
+ case SUBDIV_EVALUATOR_TYPE_GPU: {
+ return OPENSUBDIV_EVALUATOR_GPU;
}
}
BLI_assert_msg(0, "Unknown evaluator type");
@@ -80,9 +81,9 @@ static void set_coarse_positions(Subdiv *subdiv,
const Mesh *mesh,
const float (*coarse_vertex_cos)[3])
{
- const MVert *mvert = mesh->mvert;
- const MLoop *mloop = mesh->mloop;
- const MPoly *mpoly = mesh->mpoly;
+ const MVert *mvert = BKE_mesh_verts(mesh);
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
+ const MLoop *mloop = BKE_mesh_loops(mesh);
/* Mark vertices which needs new coordinates. */
/* TODO(sergey): This is annoying to calculate this on every update,
* maybe it's better to cache this mapping. Or make it possible to have
@@ -125,6 +126,7 @@ static void set_coarse_positions(Subdiv *subdiv,
typedef struct FaceVaryingDataFromUVContext {
OpenSubdiv_TopologyRefiner *topology_refiner;
const Mesh *mesh;
+ const MPoly *polys;
const MLoopUV *mloopuv;
float (*buffer)[2];
int layer_index;
@@ -137,8 +139,7 @@ static void set_face_varying_data_from_uv_task(void *__restrict userdata,
FaceVaryingDataFromUVContext *ctx = userdata;
OpenSubdiv_TopologyRefiner *topology_refiner = ctx->topology_refiner;
const int layer_index = ctx->layer_index;
- const Mesh *mesh = ctx->mesh;
- const MPoly *mpoly = &mesh->mpoly[face_index];
+ const MPoly *mpoly = &ctx->polys[face_index];
const MLoopUV *mluv = &ctx->mloopuv[mpoly->loopstart];
/* TODO(sergey): OpenSubdiv's C-API converter can change winding of
@@ -171,6 +172,7 @@ static void set_face_varying_data_from_uv(Subdiv *subdiv,
ctx.layer_index = layer_index;
ctx.mloopuv = mluv;
ctx.mesh = mesh;
+ ctx.polys = BKE_mesh_polys(mesh);
ctx.buffer = buffer;
TaskParallelSettings parallel_range_settings;
diff --git a/source/blender/blenkernel/intern/subdiv_foreach.c b/source/blender/blenkernel/intern/subdiv_foreach.c
index 80364561d01..faf531b0f5e 100644
--- a/source/blender/blenkernel/intern/subdiv_foreach.c
+++ b/source/blender/blenkernel/intern/subdiv_foreach.c
@@ -158,8 +158,8 @@ static void subdiv_foreach_ctx_count(SubdivForeachTaskContext *ctx)
const int num_inner_vertices_per_noquad_patch = (no_quad_patch_resolution - 2) *
(no_quad_patch_resolution - 2);
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
ctx->num_subdiv_vertices = coarse_mesh->totvert;
ctx->num_subdiv_edges = coarse_mesh->totedge * (num_subdiv_vertices_per_coarse_edge + 1);
/* Calculate extra vertices and edges created by non-loose geometry. */
@@ -225,7 +225,7 @@ static void subdiv_foreach_ctx_init_offsets(SubdivForeachTaskContext *ctx)
ctx->edge_inner_offset = ctx->edge_boundary_offset +
coarse_mesh->totedge * num_subdiv_edges_per_coarse_edge;
/* "Indexed" offsets. */
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
int vertex_offset = 0;
int edge_offset = 0;
int polygon_offset = 0;
@@ -301,8 +301,9 @@ static void subdiv_foreach_corner_vertices_regular_do(
{
const float weights[4][2] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}};
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const int coarse_poly_index = coarse_poly - coarse_mesh->mpoly;
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const int coarse_poly_index = coarse_poly - coarse_mpoly;
const int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index];
for (int corner = 0; corner < coarse_poly->totloop; corner++) {
const MLoop *coarse_loop = &coarse_mloop[coarse_poly->loopstart + corner];
@@ -342,8 +343,9 @@ static void subdiv_foreach_corner_vertices_special_do(
bool check_usage)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const int coarse_poly_index = coarse_poly - coarse_mesh->mpoly;
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const int coarse_poly_index = coarse_poly - coarse_mpoly;
int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index];
for (int corner = 0; corner < coarse_poly->totloop; corner++, ptex_face_index++) {
const MLoop *coarse_loop = &coarse_mloop[coarse_poly->loopstart + corner];
@@ -407,7 +409,7 @@ static void subdiv_foreach_every_corner_vertices(SubdivForeachTaskContext *ctx,
return;
}
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) {
const MPoly *coarse_poly = &coarse_mpoly[poly_index];
if (coarse_poly->totloop == 4) {
@@ -432,11 +434,11 @@ static void subdiv_foreach_edge_vertices_regular_do(SubdivForeachTaskContext *ct
const float inv_resolution_1 = 1.0f / (float)resolution_1;
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_medge = coarse_mesh->medge;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MEdge *coarse_medge = BKE_mesh_edges(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
const int coarse_poly_index = coarse_poly - coarse_mpoly;
- const int poly_index = coarse_poly - coarse_mesh->mpoly;
+ const int poly_index = coarse_poly - coarse_mpoly;
const int ptex_face_index = ctx->face_ptex_offset[poly_index];
for (int corner = 0; corner < coarse_poly->totloop; corner++) {
const MLoop *coarse_loop = &coarse_mloop[coarse_poly->loopstart + corner];
@@ -499,11 +501,11 @@ static void subdiv_foreach_edge_vertices_special_do(SubdivForeachTaskContext *ct
const int num_vertices_per_ptex_edge = ((resolution >> 1) + 1);
const float inv_ptex_resolution_1 = 1.0f / (float)(num_vertices_per_ptex_edge - 1);
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_medge = coarse_mesh->medge;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MEdge *coarse_medge = BKE_mesh_edges(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
const int coarse_poly_index = coarse_poly - coarse_mpoly;
- const int poly_index = coarse_poly - coarse_mesh->mpoly;
+ const int poly_index = coarse_poly - coarse_mpoly;
const int ptex_face_start_index = ctx->face_ptex_offset[poly_index];
int ptex_face_index = ptex_face_start_index;
for (int corner = 0; corner < coarse_poly->totloop; corner++, ptex_face_index++) {
@@ -595,7 +597,7 @@ static void subdiv_foreach_every_edge_vertices(SubdivForeachTaskContext *ctx, vo
return;
}
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) {
const MPoly *coarse_poly = &coarse_mpoly[poly_index];
if (coarse_poly->totloop == 4) {
@@ -616,7 +618,8 @@ static void subdiv_foreach_inner_vertices_regular(SubdivForeachTaskContext *ctx,
const int resolution = ctx->settings->resolution;
const float inv_resolution_1 = 1.0f / (float)(resolution - 1);
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const int coarse_poly_index = coarse_poly - coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const int coarse_poly_index = coarse_poly - coarse_mpoly;
const int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index];
const int start_vertex_index = ctx->subdiv_vertex_offset[coarse_poly_index];
int subdiv_vertex_index = ctx->vertices_inner_offset + start_vertex_index;
@@ -644,7 +647,8 @@ static void subdiv_foreach_inner_vertices_special(SubdivForeachTaskContext *ctx,
const int ptex_face_resolution = ptex_face_resolution_get(coarse_poly, resolution);
const float inv_ptex_face_resolution_1 = 1.0f / (float)(ptex_face_resolution - 1);
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const int coarse_poly_index = coarse_poly - coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const int coarse_poly_index = coarse_poly - coarse_mpoly;
int ptex_face_index = ctx->face_ptex_offset[coarse_poly_index];
const int start_vertex_index = ctx->subdiv_vertex_offset[coarse_poly_index];
int subdiv_vertex_index = ctx->vertices_inner_offset + start_vertex_index;
@@ -691,7 +695,7 @@ static void subdiv_foreach_inner_vertices(SubdivForeachTaskContext *ctx,
static void subdiv_foreach_vertices(SubdivForeachTaskContext *ctx, void *tls, const int poly_index)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
const MPoly *coarse_poly = &coarse_mpoly[poly_index];
if (ctx->foreach_context->vertex_inner != NULL) {
subdiv_foreach_inner_vertices(ctx, tls, coarse_poly);
@@ -775,9 +779,9 @@ static void subdiv_foreach_edges_all_patches_regular(SubdivForeachTaskContext *c
const MPoly *coarse_poly)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_medge = coarse_mesh->medge;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MEdge *coarse_medge = BKE_mesh_edges(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
const int poly_index = coarse_poly - coarse_mpoly;
const int resolution = ctx->settings->resolution;
const int start_vertex_index = ctx->vertices_inner_offset +
@@ -856,9 +860,9 @@ static void subdiv_foreach_edges_all_patches_special(SubdivForeachTaskContext *c
const MPoly *coarse_poly)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_medge = coarse_mesh->medge;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MEdge *coarse_medge = BKE_mesh_edges(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
const int poly_index = coarse_poly - coarse_mpoly;
const int resolution = ctx->settings->resolution;
const int ptex_face_resolution = ptex_face_resolution_get(coarse_poly, resolution);
@@ -984,7 +988,7 @@ static void subdiv_foreach_edges_all_patches(SubdivForeachTaskContext *ctx,
static void subdiv_foreach_edges(SubdivForeachTaskContext *ctx, void *tls, int poly_index)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
const MPoly *coarse_poly = &coarse_mpoly[poly_index];
subdiv_foreach_edges_all_patches(ctx, tls, coarse_poly);
}
@@ -994,7 +998,7 @@ static void subdiv_foreach_boundary_edges(SubdivForeachTaskContext *ctx,
int coarse_edge_index)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_medge = coarse_mesh->medge;
+ const MEdge *coarse_medge = BKE_mesh_edges(coarse_mesh);
const MEdge *coarse_edge = &coarse_medge[coarse_edge_index];
const int resolution = ctx->settings->resolution;
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
@@ -1125,9 +1129,9 @@ static void subdiv_foreach_loops_regular(SubdivForeachTaskContext *ctx,
const int resolution = ctx->settings->resolution;
/* Base/coarse mesh information. */
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_medge = coarse_mesh->medge;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MEdge *coarse_medge = BKE_mesh_edges(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
const int coarse_poly_index = coarse_poly - coarse_mpoly;
const int ptex_resolution = ptex_face_resolution_get(coarse_poly, resolution);
const int ptex_inner_resolution = ptex_resolution - 2;
@@ -1319,9 +1323,9 @@ static void subdiv_foreach_loops_special(SubdivForeachTaskContext *ctx,
const int resolution = ctx->settings->resolution;
/* Base/coarse mesh information. */
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_medge = coarse_mesh->medge;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MEdge *coarse_medge = BKE_mesh_edges(coarse_mesh);
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
const int coarse_poly_index = coarse_poly - coarse_mpoly;
const int ptex_face_resolution = ptex_face_resolution_get(coarse_poly, resolution);
const int ptex_face_inner_resolution = ptex_face_resolution - 2;
@@ -1654,7 +1658,7 @@ static void subdiv_foreach_loops_special(SubdivForeachTaskContext *ctx,
static void subdiv_foreach_loops(SubdivForeachTaskContext *ctx, void *tls, int poly_index)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
const MPoly *coarse_poly = &coarse_mpoly[poly_index];
if (coarse_poly->totloop == 4) {
subdiv_foreach_loops_regular(ctx, tls, coarse_poly);
@@ -1676,7 +1680,7 @@ static void subdiv_foreach_polys(SubdivForeachTaskContext *ctx, void *tls, int p
const int start_poly_index = ctx->subdiv_polygon_offset[poly_index];
/* Base/coarse mesh information. */
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
const MPoly *coarse_poly = &coarse_mpoly[poly_index];
const int num_ptex_faces_per_poly = num_ptex_faces_per_poly_get(coarse_poly);
const int ptex_resolution = ptex_face_resolution_get(coarse_poly, resolution);
@@ -1731,7 +1735,8 @@ static void subdiv_foreach_vertices_of_loose_edges_task(void *__restrict userdat
const float inv_resolution_1 = 1.0f / (float)resolution_1;
const int num_subdiv_vertices_per_coarse_edge = resolution - 2;
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_edge = &coarse_mesh->medge[coarse_edge_index];
+ const MEdge *coarse_edges = BKE_mesh_edges(coarse_mesh);
+ const MEdge *coarse_edge = &coarse_edges[coarse_edge_index];
/* Subdivision vertices which corresponds to edge's v1 and v2. */
const int subdiv_v1_index = ctx->vertices_corner_offset + coarse_edge->v1;
const int subdiv_v2_index = ctx->vertices_corner_offset + coarse_edge->v2;
@@ -1768,7 +1773,7 @@ static void subdiv_foreach_single_geometry_vertices(SubdivForeachTaskContext *ct
return;
}
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) {
const MPoly *coarse_poly = &coarse_mpoly[poly_index];
subdiv_foreach_corner_vertices(ctx, tls, coarse_poly);
@@ -1779,8 +1784,8 @@ static void subdiv_foreach_single_geometry_vertices(SubdivForeachTaskContext *ct
static void subdiv_foreach_mark_non_loose_geometry(SubdivForeachTaskContext *ctx)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
+ const MPoly *coarse_mpoly = BKE_mesh_polys(coarse_mesh);
+ const MLoop *coarse_mloop = BKE_mesh_loops(coarse_mesh);
for (int poly_index = 0; poly_index < coarse_mesh->totpoly; poly_index++) {
const MPoly *coarse_poly = &coarse_mpoly[poly_index];
for (int corner = 0; corner < coarse_poly->totloop; corner++) {
diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.cc
index 433bad34479..44bdd6e6d06 100644
--- a/source/blender/blenkernel/intern/subdiv_mesh.c
+++ b/source/blender/blenkernel/intern/subdiv_mesh.cc
@@ -5,19 +5,22 @@
* \ingroup bke
*/
+#include <mutex>
+
#include "atomic_ops.h"
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "BLI_alloca.h"
+#include "BLI_array.hh"
#include "BLI_bitmap.h"
#include "BLI_math_vector.h"
#include "BKE_customdata.h"
#include "BKE_key.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_eval.h"
#include "BKE_subdiv_foreach.h"
@@ -25,15 +28,27 @@
#include "MEM_guardedalloc.h"
+using blender::Span;
+
/* -------------------------------------------------------------------- */
/** \name Subdivision Context
* \{ */
-typedef struct SubdivMeshContext {
+struct SubdivMeshContext {
const SubdivToMeshSettings *settings;
const Mesh *coarse_mesh;
+ const MVert *coarse_verts;
+ const MEdge *coarse_edges;
+ const MPoly *coarse_polys;
+ const MLoop *coarse_loops;
+
Subdiv *subdiv;
Mesh *subdiv_mesh;
+ MVert *subdiv_verts;
+ MEdge *subdiv_edges;
+ MPoly *subdiv_polys;
+ MLoop *subdiv_loops;
+
/* Cached custom data arrays for faster access. */
int *vert_origindex;
int *edge_origindex;
@@ -48,31 +63,45 @@ typedef struct SubdivMeshContext {
/* Per-subdivided vertex counter of averaged values. */
int *accumulated_counters;
bool have_displacement;
-} SubdivMeshContext;
+
+ /* Lazily initialize a map from vertices to connected edges. */
+ std::mutex vert_to_edge_map_mutex;
+ int *vert_to_edge_buffer;
+ MeshElemMap *vert_to_edge_map;
+};
static void subdiv_mesh_ctx_cache_uv_layers(SubdivMeshContext *ctx)
{
Mesh *subdiv_mesh = ctx->subdiv_mesh;
ctx->num_uv_layers = CustomData_number_of_layers(&subdiv_mesh->ldata, CD_MLOOPUV);
for (int layer_index = 0; layer_index < ctx->num_uv_layers; layer_index++) {
- ctx->uv_layers[layer_index] = CustomData_get_layer_n(
- &subdiv_mesh->ldata, CD_MLOOPUV, layer_index);
+ ctx->uv_layers[layer_index] = static_cast<MLoopUV *>(
+ CustomData_get_layer_n(&subdiv_mesh->ldata, CD_MLOOPUV, layer_index));
}
}
static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx)
{
Mesh *subdiv_mesh = ctx->subdiv_mesh;
+ ctx->subdiv_verts = BKE_mesh_verts_for_write(subdiv_mesh);
+ ctx->subdiv_edges = BKE_mesh_edges_for_write(subdiv_mesh);
+ ctx->subdiv_polys = BKE_mesh_polys_for_write(subdiv_mesh);
+ ctx->subdiv_loops = BKE_mesh_loops_for_write(subdiv_mesh);
/* Pointers to original indices layers. */
- ctx->vert_origindex = CustomData_get_layer(&subdiv_mesh->vdata, CD_ORIGINDEX);
- ctx->edge_origindex = CustomData_get_layer(&subdiv_mesh->edata, CD_ORIGINDEX);
- ctx->loop_origindex = CustomData_get_layer(&subdiv_mesh->ldata, CD_ORIGINDEX);
- ctx->poly_origindex = CustomData_get_layer(&subdiv_mesh->pdata, CD_ORIGINDEX);
+ ctx->vert_origindex = static_cast<int *>(
+ CustomData_get_layer(&subdiv_mesh->vdata, CD_ORIGINDEX));
+ ctx->edge_origindex = static_cast<int *>(
+ CustomData_get_layer(&subdiv_mesh->edata, CD_ORIGINDEX));
+ ctx->loop_origindex = static_cast<int *>(
+ CustomData_get_layer(&subdiv_mesh->ldata, CD_ORIGINDEX));
+ ctx->poly_origindex = static_cast<int *>(
+ CustomData_get_layer(&subdiv_mesh->pdata, CD_ORIGINDEX));
/* UV layers interpolation. */
subdiv_mesh_ctx_cache_uv_layers(ctx);
/* Orco interpolation. */
- ctx->orco = CustomData_get_layer(&subdiv_mesh->vdata, CD_ORCO);
- ctx->cloth_orco = CustomData_get_layer(&subdiv_mesh->vdata, CD_CLOTH_ORCO);
+ ctx->orco = static_cast<float(*)[3]>(CustomData_get_layer(&subdiv_mesh->vdata, CD_ORCO));
+ ctx->cloth_orco = static_cast<float(*)[3]>(
+ CustomData_get_layer(&subdiv_mesh->vdata, CD_CLOTH_ORCO));
}
static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vertices)
@@ -80,13 +109,15 @@ static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vert
if (!ctx->have_displacement) {
return;
}
- ctx->accumulated_counters = MEM_calloc_arrayN(
- num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters");
+ ctx->accumulated_counters = static_cast<int *>(
+ MEM_calloc_arrayN(num_vertices, sizeof(*ctx->accumulated_counters), __func__));
}
static void subdiv_mesh_context_free(SubdivMeshContext *ctx)
{
MEM_SAFE_FREE(ctx->accumulated_counters);
+ MEM_SAFE_FREE(ctx->vert_to_edge_buffer);
+ MEM_SAFE_FREE(ctx->vert_to_edge_map);
}
/** \} */
@@ -95,7 +126,7 @@ static void subdiv_mesh_context_free(SubdivMeshContext *ctx)
/** \name Loop custom data copy helpers
* \{ */
-typedef struct LoopsOfPtex {
+struct LoopsOfPtex {
/* First loop of the ptex, starts at ptex (0, 0) and goes in u direction. */
const MLoop *first_loop;
/* Last loop of the ptex, starts at ptex (0, 0) and goes in v direction. */
@@ -103,14 +134,14 @@ typedef struct LoopsOfPtex {
/* For quad coarse faces only. */
const MLoop *second_loop;
const MLoop *third_loop;
-} LoopsOfPtex;
+};
static void loops_of_ptex_get(const SubdivMeshContext *ctx,
LoopsOfPtex *loops_of_ptex,
const MPoly *coarse_poly,
const int ptex_of_poly_index)
{
- const MLoop *coarse_mloop = ctx->coarse_mesh->mloop;
+ const MLoop *coarse_mloop = ctx->coarse_loops;
const int first_ptex_loop_index = coarse_poly->loopstart + ptex_of_poly_index;
/* Loop which look in the (opposite) V direction of the current
* ptex face.
@@ -126,8 +157,8 @@ static void loops_of_ptex_get(const SubdivMeshContext *ctx,
loops_of_ptex->third_loop = loops_of_ptex->first_loop + 2;
}
else {
- loops_of_ptex->second_loop = NULL;
- loops_of_ptex->third_loop = NULL;
+ loops_of_ptex->second_loop = nullptr;
+ loops_of_ptex->third_loop = nullptr;
}
}
@@ -140,7 +171,7 @@ static void loops_of_ptex_get(const SubdivMeshContext *ctx,
/* TODO(sergey): Somehow de-duplicate with loops storage, without too much
* exception cases all over the code. */
-typedef struct VerticesForInterpolation {
+struct VerticesForInterpolation {
/* This field points to a vertex data which is to be used for interpolation.
* The idea is to avoid unnecessary allocations for regular faces, where
* we can simply use corner vertices. */
@@ -159,14 +190,14 @@ typedef struct VerticesForInterpolation {
/* Indices within vertex_data to interpolate for. The indices are aligned
* with uv coordinates in a similar way as indices in loop_data_storage. */
int vertex_indices[4];
-} VerticesForInterpolation;
+};
static void vertex_interpolation_init(const SubdivMeshContext *ctx,
VerticesForInterpolation *vertex_interpolation,
const MPoly *coarse_poly)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
+ const MLoop *coarse_mloop = ctx->coarse_loops;
if (coarse_poly->totloop == 4) {
vertex_interpolation->vertex_data = &coarse_mesh->vdata;
vertex_interpolation->vertex_indices[0] = coarse_mloop[coarse_poly->loopstart + 0].v;
@@ -181,7 +212,7 @@ static void vertex_interpolation_init(const SubdivMeshContext *ctx,
CustomData_copy(&ctx->coarse_mesh->vdata,
&vertex_interpolation->vertex_data_storage,
CD_MASK_EVERYTHING.vmask,
- CD_CALLOC,
+ CD_SET_DEFAULT,
4);
/* Initialize indices. */
vertex_interpolation->vertex_indices[0] = 0;
@@ -192,17 +223,17 @@ static void vertex_interpolation_init(const SubdivMeshContext *ctx,
/* Interpolate center of poly right away, it stays unchanged for all
* ptex faces. */
const float weight = 1.0f / (float)coarse_poly->totloop;
- float *weights = BLI_array_alloca(weights, coarse_poly->totloop);
- int *indices = BLI_array_alloca(indices, coarse_poly->totloop);
+ blender::Array<float, 32> weights(coarse_poly->totloop);
+ blender::Array<int, 32> indices(coarse_poly->totloop);
for (int i = 0; i < coarse_poly->totloop; i++) {
weights[i] = weight;
indices[i] = coarse_mloop[coarse_poly->loopstart + i].v;
}
CustomData_interp(&coarse_mesh->vdata,
&vertex_interpolation->vertex_data_storage,
- indices,
- weights,
- NULL,
+ indices.data(),
+ weights.data(),
+ nullptr,
coarse_poly->totloop,
2);
}
@@ -218,8 +249,7 @@ static void vertex_interpolation_from_corner(const SubdivMeshContext *ctx,
}
else {
const CustomData *vertex_data = &ctx->coarse_mesh->vdata;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
+ const MLoop *coarse_mloop = ctx->coarse_loops;
LoopsOfPtex loops_of_ptex;
loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, corner);
/* Ptex face corner corresponds to a poly loop with same index. */
@@ -237,26 +267,27 @@ static void vertex_interpolation_from_corner(const SubdivMeshContext *ctx,
const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop;
const int last_loop_index = loops_of_ptex.last_loop - coarse_mloop;
const int first_indices[2] = {
- coarse_mloop[first_loop_index].v,
- coarse_mloop[coarse_poly->loopstart +
- (first_loop_index - coarse_poly->loopstart + 1) % coarse_poly->totloop]
- .v};
+ static_cast<int>(coarse_mloop[first_loop_index].v),
+ static_cast<int>(
+ coarse_mloop[coarse_poly->loopstart +
+ (first_loop_index - coarse_poly->loopstart + 1) % coarse_poly->totloop]
+ .v)};
const int last_indices[2] = {
- coarse_mloop[first_loop_index].v,
- coarse_mloop[last_loop_index].v,
+ static_cast<int>(coarse_mloop[first_loop_index].v),
+ static_cast<int>(coarse_mloop[last_loop_index].v),
};
CustomData_interp(vertex_data,
&vertex_interpolation->vertex_data_storage,
first_indices,
weights,
- NULL,
+ nullptr,
2,
1);
CustomData_interp(vertex_data,
&vertex_interpolation->vertex_data_storage,
last_indices,
weights,
- NULL,
+ nullptr,
2,
3);
}
@@ -275,7 +306,7 @@ static void vertex_interpolation_end(VerticesForInterpolation *vertex_interpolat
/** \name Loop custom data interpolation helpers
* \{ */
-typedef struct LoopsForInterpolation {
+struct LoopsForInterpolation {
/* This field points to a loop data which is to be used for interpolation.
* The idea is to avoid unnecessary allocations for regular faces, where
* we can simply interpolate corner vertices. */
@@ -291,10 +322,10 @@ typedef struct LoopsForInterpolation {
* Is allocated for non-regular faces (triangles and n-gons). */
CustomData loop_data_storage;
bool loop_data_storage_allocated;
- /* Infices within loop_data to interpolate for. The indices are aligned with
+ /* Indices within loop_data to interpolate for. The indices are aligned with
* uv coordinates in a similar way as indices in loop_data_storage. */
int loop_indices[4];
-} LoopsForInterpolation;
+};
static void loop_interpolation_init(const SubdivMeshContext *ctx,
LoopsForInterpolation *loop_interpolation,
@@ -315,7 +346,7 @@ static void loop_interpolation_init(const SubdivMeshContext *ctx,
CustomData_copy(&ctx->coarse_mesh->ldata,
&loop_interpolation->loop_data_storage,
CD_MASK_EVERYTHING.lmask,
- CD_CALLOC,
+ CD_SET_DEFAULT,
4);
/* Initialize indices. */
loop_interpolation->loop_indices[0] = 0;
@@ -326,17 +357,17 @@ static void loop_interpolation_init(const SubdivMeshContext *ctx,
/* Interpolate center of poly right away, it stays unchanged for all
* ptex faces. */
const float weight = 1.0f / (float)coarse_poly->totloop;
- float *weights = BLI_array_alloca(weights, coarse_poly->totloop);
- int *indices = BLI_array_alloca(indices, coarse_poly->totloop);
+ blender::Array<float, 32> weights(coarse_poly->totloop);
+ blender::Array<int, 32> indices(coarse_poly->totloop);
for (int i = 0; i < coarse_poly->totloop; i++) {
weights[i] = weight;
indices[i] = coarse_poly->loopstart + i;
}
CustomData_interp(&coarse_mesh->ldata,
&loop_interpolation->loop_data_storage,
- indices,
- weights,
- NULL,
+ indices.data(),
+ weights.data(),
+ nullptr,
coarse_poly->totloop,
2);
}
@@ -352,8 +383,7 @@ static void loop_interpolation_from_corner(const SubdivMeshContext *ctx,
}
else {
const CustomData *loop_data = &ctx->coarse_mesh->ldata;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MLoop *coarse_mloop = coarse_mesh->mloop;
+ const MLoop *coarse_mloop = ctx->coarse_loops;
LoopsOfPtex loops_of_ptex;
loops_of_ptex_get(ctx, &loops_of_ptex, coarse_poly, corner);
/* Ptex face corner corresponds to a poly loop with same index. */
@@ -372,13 +402,13 @@ static void loop_interpolation_from_corner(const SubdivMeshContext *ctx,
(first_loop_index - base_loop_index + 1) % coarse_poly->totloop;
const int first_indices[2] = {first_loop_index, second_loop_index};
const int last_indices[2] = {
- loops_of_ptex.last_loop - coarse_mloop,
- loops_of_ptex.first_loop - coarse_mloop,
+ static_cast<int>(loops_of_ptex.last_loop - coarse_mloop),
+ static_cast<int>(loops_of_ptex.first_loop - coarse_mloop),
};
CustomData_interp(
- loop_data, &loop_interpolation->loop_data_storage, first_indices, weights, NULL, 2, 1);
+ loop_data, &loop_interpolation->loop_data_storage, first_indices, weights, nullptr, 2, 1);
CustomData_interp(
- loop_data, &loop_interpolation->loop_data_storage, last_indices, weights, NULL, 2, 3);
+ loop_data, &loop_interpolation->loop_data_storage, last_indices, weights, nullptr, 2, 3);
}
}
@@ -395,7 +425,7 @@ static void loop_interpolation_end(LoopsForInterpolation *loop_interpolation)
/** \name TLS
* \{ */
-typedef struct SubdivMeshTLS {
+struct SubdivMeshTLS {
bool vertex_interpolation_initialized;
VerticesForInterpolation vertex_interpolation;
const MPoly *vertex_interpolation_coarse_poly;
@@ -405,11 +435,11 @@ typedef struct SubdivMeshTLS {
LoopsForInterpolation loop_interpolation;
const MPoly *loop_interpolation_coarse_poly;
int loop_interpolation_coarse_corner;
-} SubdivMeshTLS;
+};
static void subdiv_mesh_tls_free(void *tls_v)
{
- SubdivMeshTLS *tls = tls_v;
+ SubdivMeshTLS *tls = static_cast<SubdivMeshTLS *>(tls_v);
if (tls->vertex_interpolation_initialized) {
vertex_interpolation_end(&tls->vertex_interpolation);
}
@@ -460,7 +490,7 @@ static void subdiv_accumulate_vertex_displacement(SubdivMeshContext *ctx,
{
/* Accumulate displacement. */
Subdiv *subdiv = ctx->subdiv;
- const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert;
+ const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_verts;
float dummy_P[3], dPdu[3], dPdv[3], D[3];
BKE_subdiv_eval_limit_point_and_derivatives(subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
@@ -492,7 +522,7 @@ static bool subdiv_mesh_topology_info(const SubdivForeachContext *foreach_contex
CustomData_MeshMasks mask = CD_MASK_EVERYTHING;
mask.lmask &= ~CD_MASK_MULTIRES_GRIDS;
- SubdivMeshContext *subdiv_context = foreach_context->user_data;
+ SubdivMeshContext *subdiv_context = static_cast<SubdivMeshContext *>(foreach_context->user_data);
subdiv_context->subdiv_mesh = BKE_mesh_new_nomain_from_template_ex(
subdiv_context->coarse_mesh, num_vertices, num_edges, 0, num_loops, num_polygons, mask);
subdiv_mesh_ctx_cache_custom_data_layers(subdiv_context);
@@ -514,9 +544,8 @@ static void subdiv_vertex_data_copy(const SubdivMeshContext *ctx,
MVert *subdiv_vertex)
{
const Mesh *coarse_mesh = ctx->coarse_mesh;
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- const int coarse_vertex_index = coarse_vertex - coarse_mesh->mvert;
- const int subdiv_vertex_index = subdiv_vertex - subdiv_mesh->mvert;
+ const int coarse_vertex_index = coarse_vertex - ctx->coarse_verts;
+ const int subdiv_vertex_index = subdiv_vertex - ctx->subdiv_verts;
CustomData_copy_data(
&coarse_mesh->vdata, &ctx->subdiv_mesh->vdata, coarse_vertex_index, subdiv_vertex_index, 1);
}
@@ -527,16 +556,16 @@ static void subdiv_vertex_data_interpolate(const SubdivMeshContext *ctx,
const float u,
const float v)
{
- const int subdiv_vertex_index = subdiv_vertex - ctx->subdiv_mesh->mvert;
+ const int subdiv_vertex_index = subdiv_vertex - ctx->subdiv_verts;
const float weights[4] = {(1.0f - u) * (1.0f - v), u * (1.0f - v), u * v, (1.0f - u) * v};
CustomData_interp(vertex_interpolation->vertex_data,
&ctx->subdiv_mesh->vdata,
vertex_interpolation->vertex_indices,
weights,
- NULL,
+ nullptr,
4,
subdiv_vertex_index);
- if (ctx->vert_origindex != NULL) {
+ if (ctx->vert_origindex != nullptr) {
ctx->vert_origindex[subdiv_vertex_index] = ORIGINDEX_NONE;
}
}
@@ -548,7 +577,7 @@ static void evaluate_vertex_and_apply_displacement_copy(const SubdivMeshContext
const MVert *coarse_vert,
MVert *subdiv_vert)
{
- const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert;
+ const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_verts;
/* Displacement is accumulated in subdiv vertex position.
* Needs to be backed up before copying data from original vertex. */
float D[3] = {0.0f, 0.0f, 0.0f};
@@ -564,7 +593,7 @@ static void evaluate_vertex_and_apply_displacement_copy(const SubdivMeshContext
add_v3_v3(subdiv_vert->co, D);
/* Evaluate undeformed texture coordinate. */
subdiv_vertex_orco_evaluate(ctx, ptex_face_index, u, v, subdiv_vertex_index);
- /* Remove facedot flag. This can happen if there is more than one subsurf modifier. */
+ /* Remove face-dot flag. This can happen if there is more than one subsurf modifier. */
BLI_BITMAP_DISABLE(ctx->subdiv_mesh->runtime.subsurf_face_dot_tags, subdiv_vertex_index);
}
@@ -576,7 +605,7 @@ static void evaluate_vertex_and_apply_displacement_interpolate(
VerticesForInterpolation *vertex_interpolation,
MVert *subdiv_vert)
{
- const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert;
+ const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_verts;
/* Displacement is accumulated in subdiv vertex position.
* Needs to be backed up before copying data from original vertex. */
float D[3] = {0.0f, 0.0f, 0.0f};
@@ -602,10 +631,8 @@ static void subdiv_mesh_vertex_displacement_every_corner_or_edge(
const float v,
const int subdiv_vertex_index)
{
- SubdivMeshContext *ctx = foreach_context->user_data;
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MVert *subdiv_mvert = subdiv_mesh->mvert;
- MVert *subdiv_vert = &subdiv_mvert[subdiv_vertex_index];
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
+ MVert *subdiv_vert = &ctx->subdiv_verts[subdiv_vertex_index];
subdiv_accumulate_vertex_displacement(ctx, ptex_face_index, u, v, subdiv_vert);
}
@@ -649,13 +676,9 @@ static void subdiv_mesh_vertex_corner(const SubdivForeachContext *foreach_contex
const int subdiv_vertex_index)
{
BLI_assert(coarse_vertex_index != ORIGINDEX_NONE);
- SubdivMeshContext *ctx = foreach_context->user_data;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MVert *coarse_mvert = coarse_mesh->mvert;
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MVert *subdiv_mvert = subdiv_mesh->mvert;
- const MVert *coarse_vert = &coarse_mvert[coarse_vertex_index];
- MVert *subdiv_vert = &subdiv_mvert[subdiv_vertex_index];
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
+ const MVert *coarse_vert = &ctx->coarse_verts[coarse_vertex_index];
+ MVert *subdiv_vert = &ctx->subdiv_verts[subdiv_vertex_index];
evaluate_vertex_and_apply_displacement_copy(
ctx, ptex_face_index, u, v, coarse_vert, subdiv_vert);
}
@@ -698,14 +721,10 @@ static void subdiv_mesh_vertex_edge(const SubdivForeachContext *foreach_context,
const int coarse_corner,
const int subdiv_vertex_index)
{
- SubdivMeshContext *ctx = foreach_context->user_data;
- SubdivMeshTLS *tls = tls_v;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
- const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MVert *subdiv_mvert = subdiv_mesh->mvert;
- MVert *subdiv_vert = &subdiv_mvert[subdiv_vertex_index];
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
+ SubdivMeshTLS *tls = static_cast<SubdivMeshTLS *>(tls_v);
+ const MPoly *coarse_poly = &ctx->coarse_polys[coarse_poly_index];
+ MVert *subdiv_vert = &ctx->subdiv_verts[subdiv_vertex_index];
subdiv_mesh_ensure_vertex_interpolation(ctx, tls, coarse_poly, coarse_corner);
evaluate_vertex_and_apply_displacement_interpolate(
ctx, ptex_face_index, u, v, &tls->vertex_interpolation, subdiv_vert);
@@ -746,15 +765,12 @@ static void subdiv_mesh_vertex_inner(const SubdivForeachContext *foreach_context
const int coarse_corner,
const int subdiv_vertex_index)
{
- SubdivMeshContext *ctx = foreach_context->user_data;
- SubdivMeshTLS *tls = tls_v;
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
+ SubdivMeshTLS *tls = static_cast<SubdivMeshTLS *>(tls_v);
Subdiv *subdiv = ctx->subdiv;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
- const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
+ const MPoly *coarse_poly = &ctx->coarse_polys[coarse_poly_index];
Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MVert *subdiv_mvert = subdiv_mesh->mvert;
- MVert *subdiv_vert = &subdiv_mvert[subdiv_vertex_index];
+ MVert *subdiv_vert = &ctx->subdiv_verts[subdiv_vertex_index];
subdiv_mesh_ensure_vertex_interpolation(ctx, tls, coarse_poly, coarse_corner);
subdiv_vertex_data_interpolate(ctx, subdiv_vert, &tls->vertex_interpolation, u, v);
BKE_subdiv_eval_final_point(subdiv, ptex_face_index, u, v, subdiv_vert->co);
@@ -772,20 +788,19 @@ static void subdiv_copy_edge_data(SubdivMeshContext *ctx,
MEdge *subdiv_edge,
const MEdge *coarse_edge)
{
- const int subdiv_edge_index = subdiv_edge - ctx->subdiv_mesh->medge;
- if (coarse_edge == NULL) {
+ const int subdiv_edge_index = subdiv_edge - ctx->subdiv_edges;
+ if (coarse_edge == nullptr) {
subdiv_edge->crease = 0;
- subdiv_edge->bweight = 0;
subdiv_edge->flag = 0;
if (!ctx->settings->use_optimal_display) {
subdiv_edge->flag |= ME_EDGERENDER;
}
- if (ctx->edge_origindex != NULL) {
+ if (ctx->edge_origindex != nullptr) {
ctx->edge_origindex[subdiv_edge_index] = ORIGINDEX_NONE;
}
return;
}
- const int coarse_edge_index = coarse_edge - ctx->coarse_mesh->medge;
+ const int coarse_edge_index = coarse_edge - ctx->coarse_edges;
CustomData_copy_data(
&ctx->coarse_mesh->edata, &ctx->subdiv_mesh->edata, coarse_edge_index, subdiv_edge_index, 1);
subdiv_edge->flag |= ME_EDGERENDER;
@@ -799,14 +814,12 @@ static void subdiv_mesh_edge(const SubdivForeachContext *foreach_context,
const int subdiv_v1,
const int subdiv_v2)
{
- SubdivMeshContext *ctx = foreach_context->user_data;
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MEdge *subdiv_medge = subdiv_mesh->medge;
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
+ MEdge *subdiv_medge = ctx->subdiv_edges;
MEdge *subdiv_edge = &subdiv_medge[subdiv_edge_index];
- const MEdge *coarse_edge = NULL;
+ const MEdge *coarse_edge = nullptr;
if (coarse_edge_index != ORIGINDEX_NONE) {
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_medge = coarse_mesh->medge;
+ const MEdge *coarse_medge = ctx->coarse_edges;
coarse_edge = &coarse_medge[coarse_edge_index];
}
subdiv_copy_edge_data(ctx, subdiv_edge, coarse_edge);
@@ -826,13 +839,13 @@ static void subdiv_interpolate_loop_data(const SubdivMeshContext *ctx,
const float u,
const float v)
{
- const int subdiv_loop_index = subdiv_loop - ctx->subdiv_mesh->mloop;
+ const int subdiv_loop_index = subdiv_loop - ctx->subdiv_loops;
const float weights[4] = {(1.0f - u) * (1.0f - v), u * (1.0f - v), u * v, (1.0f - u) * v};
CustomData_interp(loop_interpolation->loop_data,
&ctx->subdiv_mesh->ldata,
loop_interpolation->loop_indices,
weights,
- NULL,
+ nullptr,
4,
subdiv_loop_index);
/* TODO(sergey): Set ORIGINDEX. */
@@ -848,7 +861,7 @@ static void subdiv_eval_uv_layer(SubdivMeshContext *ctx,
return;
}
Subdiv *subdiv = ctx->subdiv;
- const int mloop_index = subdiv_loop - ctx->subdiv_mesh->mloop;
+ const int mloop_index = subdiv_loop - ctx->subdiv_loops;
for (int layer_index = 0; layer_index < ctx->num_uv_layers; layer_index++) {
MLoopUV *subdiv_loopuv = &ctx->uv_layers[layer_index][mloop_index];
BKE_subdiv_eval_face_varying(subdiv, layer_index, ptex_face_index, u, v, subdiv_loopuv->uv);
@@ -895,14 +908,11 @@ static void subdiv_mesh_loop(const SubdivForeachContext *foreach_context,
const int subdiv_vertex_index,
const int subdiv_edge_index)
{
- SubdivMeshContext *ctx = foreach_context->user_data;
- SubdivMeshTLS *tls = tls_v;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
+ SubdivMeshTLS *tls = static_cast<SubdivMeshTLS *>(tls_v);
+ const MPoly *coarse_mpoly = ctx->coarse_polys;
const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MLoop *subdiv_mloop = subdiv_mesh->mloop;
- MLoop *subdiv_loop = &subdiv_mloop[subdiv_loop_index];
+ MLoop *subdiv_loop = &ctx->subdiv_loops[subdiv_loop_index];
subdiv_mesh_ensure_loop_interpolation(ctx, tls, coarse_poly, coarse_corner);
subdiv_interpolate_loop_data(ctx, subdiv_loop, &tls->loop_interpolation, u, v);
subdiv_eval_uv_layer(ctx, subdiv_loop, ptex_face_index, u, v);
@@ -920,8 +930,8 @@ static void subdiv_copy_poly_data(const SubdivMeshContext *ctx,
MPoly *subdiv_poly,
const MPoly *coarse_poly)
{
- const int coarse_poly_index = coarse_poly - ctx->coarse_mesh->mpoly;
- const int subdiv_poly_index = subdiv_poly - ctx->subdiv_mesh->mpoly;
+ const int coarse_poly_index = coarse_poly - ctx->coarse_polys;
+ const int subdiv_poly_index = subdiv_poly - ctx->subdiv_polys;
CustomData_copy_data(
&ctx->coarse_mesh->pdata, &ctx->subdiv_mesh->pdata, coarse_poly_index, subdiv_poly_index, 1);
}
@@ -934,13 +944,9 @@ static void subdiv_mesh_poly(const SubdivForeachContext *foreach_context,
const int num_loops)
{
BLI_assert(coarse_poly_index != ORIGINDEX_NONE);
- SubdivMeshContext *ctx = foreach_context->user_data;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MPoly *coarse_mpoly = coarse_mesh->mpoly;
- const MPoly *coarse_poly = &coarse_mpoly[coarse_poly_index];
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MPoly *subdiv_mpoly = subdiv_mesh->mpoly;
- MPoly *subdiv_poly = &subdiv_mpoly[subdiv_poly_index];
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
+ const MPoly *coarse_poly = &ctx->coarse_polys[coarse_poly_index];
+ MPoly *subdiv_poly = &ctx->subdiv_polys[subdiv_poly_index];
subdiv_copy_poly_data(ctx, subdiv_poly, coarse_poly);
subdiv_poly->loopstart = start_loop_index;
subdiv_poly->totloop = num_loops;
@@ -957,38 +963,39 @@ static void subdiv_mesh_vertex_loose(const SubdivForeachContext *foreach_context
const int coarse_vertex_index,
const int subdiv_vertex_index)
{
- SubdivMeshContext *ctx = foreach_context->user_data;
- const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MVert *coarse_mvert = coarse_mesh->mvert;
- const MVert *coarse_vertex = &coarse_mvert[coarse_vertex_index];
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MVert *subdiv_mvert = subdiv_mesh->mvert;
- MVert *subdiv_vertex = &subdiv_mvert[subdiv_vertex_index];
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
+ const MVert *coarse_vertex = &ctx->coarse_verts[coarse_vertex_index];
+ MVert *subdiv_vertex = &ctx->subdiv_verts[subdiv_vertex_index];
subdiv_vertex_data_copy(ctx, coarse_vertex, subdiv_vertex);
}
/* Get neighbor edges of the given one.
* - neighbors[0] is an edge adjacent to edge->v1.
* - neighbors[1] is an edge adjacent to edge->v2. */
-static void find_edge_neighbors(const Mesh *coarse_mesh,
- const MEdge *edge,
+static void find_edge_neighbors(const MEdge *coarse_edges,
+ const MeshElemMap *vert_to_edge_map,
+ const int edge_index,
const MEdge *neighbors[2])
{
- const MEdge *coarse_medge = coarse_mesh->medge;
- neighbors[0] = NULL;
- neighbors[1] = NULL;
+ const MEdge *edge = &coarse_edges[edge_index];
+ neighbors[0] = nullptr;
+ neighbors[1] = nullptr;
int neighbor_counters[2] = {0, 0};
- for (int edge_index = 0; edge_index < coarse_mesh->totedge; edge_index++) {
- const MEdge *current_edge = &coarse_medge[edge_index];
- if (current_edge == edge) {
+ for (const int i : Span(vert_to_edge_map[edge->v1].indices, vert_to_edge_map[edge->v1].count)) {
+ if (i == edge_index) {
continue;
}
- if (ELEM(edge->v1, current_edge->v1, current_edge->v2)) {
- neighbors[0] = current_edge;
+ if (ELEM(edge->v1, coarse_edges[i].v1, coarse_edges[i].v2)) {
+ neighbors[0] = &coarse_edges[i];
++neighbor_counters[0];
}
- if (ELEM(edge->v2, current_edge->v1, current_edge->v2)) {
- neighbors[1] = current_edge;
+ }
+ for (const int i : Span(vert_to_edge_map[edge->v2].indices, vert_to_edge_map[edge->v2].count)) {
+ if (i == edge_index) {
+ continue;
+ }
+ if (ELEM(edge->v2, coarse_edges[i].v1, coarse_edges[i].v2)) {
+ neighbors[1] = &coarse_edges[i];
++neighbor_counters[1];
}
}
@@ -996,24 +1003,23 @@ static void find_edge_neighbors(const Mesh *coarse_mesh,
* sharp. This is also how topology factory treats vertices of a surface
* which are adjacent to a loose edge. */
if (neighbor_counters[0] > 1) {
- neighbors[0] = NULL;
+ neighbors[0] = nullptr;
}
if (neighbor_counters[1] > 1) {
- neighbors[1] = NULL;
+ neighbors[1] = nullptr;
}
}
-static void points_for_loose_edges_interpolation_get(const Mesh *coarse_mesh,
+static void points_for_loose_edges_interpolation_get(const MVert *coarse_mvert,
const MEdge *coarse_edge,
const MEdge *neighbors[2],
float points_r[4][3])
{
- const MVert *coarse_mvert = coarse_mesh->mvert;
/* Middle points corresponds to the edge. */
copy_v3_v3(points_r[1], coarse_mvert[coarse_edge->v1].co);
copy_v3_v3(points_r[2], coarse_mvert[coarse_edge->v2].co);
/* Start point, duplicate from edge start if no neighbor. */
- if (neighbors[0] != NULL) {
+ if (neighbors[0] != nullptr) {
if (neighbors[0]->v1 == coarse_edge->v1) {
copy_v3_v3(points_r[0], coarse_mvert[neighbors[0]->v2].co);
}
@@ -1026,7 +1032,7 @@ static void points_for_loose_edges_interpolation_get(const Mesh *coarse_mesh,
add_v3_v3(points_r[0], points_r[1]);
}
/* End point, duplicate from edge end if no neighbor. */
- if (neighbors[1] != NULL) {
+ if (neighbors[1] != nullptr) {
if (neighbors[1]->v1 == coarse_edge->v2) {
copy_v3_v3(points_r[3], coarse_mvert[neighbors[1]->v2].co);
}
@@ -1040,24 +1046,26 @@ static void points_for_loose_edges_interpolation_get(const Mesh *coarse_mesh,
}
}
-void BKE_subdiv_mesh_interpolate_position_on_edge(const Mesh *coarse_mesh,
- const MEdge *coarse_edge,
+void BKE_subdiv_mesh_interpolate_position_on_edge(const MVert *coarse_verts,
+ const MEdge *coarse_edges,
+ const MeshElemMap *vert_to_edge_map,
+ const int coarse_edge_index,
const bool is_simple,
const float u,
float pos_r[3])
{
+ const MEdge *coarse_edge = &coarse_edges[coarse_edge_index];
if (is_simple) {
- const MVert *coarse_mvert = coarse_mesh->mvert;
- const MVert *vert_1 = &coarse_mvert[coarse_edge->v1];
- const MVert *vert_2 = &coarse_mvert[coarse_edge->v2];
+ const MVert *vert_1 = &coarse_verts[coarse_edge->v1];
+ const MVert *vert_2 = &coarse_verts[coarse_edge->v2];
interp_v3_v3v3(pos_r, vert_1->co, vert_2->co, u);
}
else {
/* Find neighbors of the coarse edge. */
const MEdge *neighbors[2];
- find_edge_neighbors(coarse_mesh, coarse_edge, neighbors);
+ find_edge_neighbors(coarse_edges, vert_to_edge_map, coarse_edge_index, neighbors);
float points[4][3];
- points_for_loose_edges_interpolation_get(coarse_mesh, coarse_edge, neighbors, points);
+ points_for_loose_edges_interpolation_get(coarse_verts, coarse_edge, neighbors, points);
float weights[4];
key_curve_position_weights(u, weights, KEY_BSPLINE);
interp_v3_v3v3v3v3(pos_r, points[0], points[1], points[2], points[3], weights);
@@ -1075,45 +1083,60 @@ static void subdiv_mesh_vertex_of_loose_edge_interpolate(SubdivMeshContext *ctx,
BLI_assert(u > 0.0f);
BLI_assert(u < 1.0f);
const float interpolation_weights[2] = {1.0f - u, u};
- const int coarse_vertex_indices[2] = {coarse_edge->v1, coarse_edge->v2};
+ const int coarse_vertex_indices[2] = {static_cast<int>(coarse_edge->v1),
+ static_cast<int>(coarse_edge->v2)};
CustomData_interp(&coarse_mesh->vdata,
&subdiv_mesh->vdata,
coarse_vertex_indices,
interpolation_weights,
- NULL,
+ nullptr,
2,
subdiv_vertex_index);
- if (ctx->vert_origindex != NULL) {
+ if (ctx->vert_origindex != nullptr) {
ctx->vert_origindex[subdiv_vertex_index] = ORIGINDEX_NONE;
}
}
-static void subdiv_mesh_vertex_of_loose_edge(const struct SubdivForeachContext *foreach_context,
+static void subdiv_mesh_vertex_of_loose_edge(const SubdivForeachContext *foreach_context,
void *UNUSED(tls),
const int coarse_edge_index,
const float u,
const int subdiv_vertex_index)
{
- SubdivMeshContext *ctx = foreach_context->user_data;
+ SubdivMeshContext *ctx = static_cast<SubdivMeshContext *>(foreach_context->user_data);
const Mesh *coarse_mesh = ctx->coarse_mesh;
- const MEdge *coarse_edge = &coarse_mesh->medge[coarse_edge_index];
- Mesh *subdiv_mesh = ctx->subdiv_mesh;
- MVert *subdiv_mvert = subdiv_mesh->mvert;
+ const MEdge *coarse_edge = &ctx->coarse_edges[coarse_edge_index];
const bool is_simple = ctx->subdiv->settings.is_simple;
+
+ /* Lazily initialize a vertex to edge map to avoid quadratic runtime when subdividing loose
+ * edges. Do this here to avoid the cost in common cases when there are no loose edges at all. */
+ if (ctx->vert_to_edge_map == NULL) {
+ std::lock_guard lock{ctx->vert_to_edge_map_mutex};
+ if (ctx->vert_to_edge_map == NULL) {
+ BKE_mesh_vert_edge_map_create(&ctx->vert_to_edge_map,
+ &ctx->vert_to_edge_buffer,
+ ctx->coarse_edges,
+ coarse_mesh->totvert,
+ ctx->coarse_mesh->totedge);
+ }
+ }
+
/* Interpolate custom data when not an end point.
* This data has already been copied from the original vertex by #subdiv_mesh_vertex_loose. */
if (!ELEM(u, 0.0, 1.0)) {
subdiv_mesh_vertex_of_loose_edge_interpolate(ctx, coarse_edge, u, subdiv_vertex_index);
}
/* Interpolate coordinate. */
- MVert *subdiv_vertex = &subdiv_mvert[subdiv_vertex_index];
- BKE_subdiv_mesh_interpolate_position_on_edge(
- coarse_mesh, coarse_edge, is_simple, u, subdiv_vertex->co);
+ MVert *subdiv_vertex = &ctx->subdiv_verts[subdiv_vertex_index];
+ BKE_subdiv_mesh_interpolate_position_on_edge(ctx->coarse_verts,
+ ctx->coarse_edges,
+ ctx->vert_to_edge_map,
+ coarse_edge_index,
+ is_simple,
+ u,
+ subdiv_vertex->co);
/* Reset flags and such. */
subdiv_vertex->flag = 0;
- /* TODO(sergey): This matches old behavior, but we can as well interpolate
- * it. Maybe even using vertex varying attributes. */
- subdiv_vertex->bweight = 0.0f;
}
/** \} */
@@ -1158,7 +1181,7 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
/* Make sure evaluator is up to date with possible new topology, and that
* it is refined for the new positions of coarse vertices. */
if (!BKE_subdiv_eval_begin_from_mesh(
- subdiv, coarse_mesh, NULL, SUBDIV_EVALUATOR_TYPE_CPU, NULL)) {
+ subdiv, coarse_mesh, nullptr, SUBDIV_EVALUATOR_TYPE_CPU, nullptr)) {
/* This could happen in two situations:
* - OpenSubdiv is disabled.
* - Something totally bad happened, and OpenSubdiv rejected our
@@ -1166,15 +1189,21 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
* In either way, we can't safely continue. */
if (coarse_mesh->totpoly) {
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
- return NULL;
+ return nullptr;
}
}
/* Initialize subdivision mesh creation context. */
SubdivMeshContext subdiv_context = {0};
subdiv_context.settings = settings;
+
subdiv_context.coarse_mesh = coarse_mesh;
+ subdiv_context.coarse_verts = BKE_mesh_verts(coarse_mesh);
+ subdiv_context.coarse_edges = BKE_mesh_edges(coarse_mesh);
+ subdiv_context.coarse_polys = BKE_mesh_polys(coarse_mesh);
+ subdiv_context.coarse_loops = BKE_mesh_loops(coarse_mesh);
+
subdiv_context.subdiv = subdiv;
- subdiv_context.have_displacement = (subdiv->displacement_evaluator != NULL);
+ subdiv_context.have_displacement = (subdiv->displacement_evaluator != nullptr);
/* Multi-threaded traversal/evaluation. */
BKE_subdiv_stats_begin(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
SubdivForeachContext foreach_context;
diff --git a/source/blender/blenkernel/intern/subdiv_modifier.c b/source/blender/blenkernel/intern/subdiv_modifier.c
index 57af0192d59..2271fd90bda 100644
--- a/source/blender/blenkernel/intern/subdiv_modifier.c
+++ b/source/blender/blenkernel/intern/subdiv_modifier.c
@@ -98,11 +98,6 @@ static bool is_subdivision_evaluation_possible_on_gpu(void)
return false;
}
- const int available_evaluators = openSubdiv_getAvailableEvaluators();
- if ((available_evaluators & OPENSUBDIV_EVALUATOR_GLSL_COMPUTE) == 0) {
- return false;
- }
-
return true;
}
diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c
index efabb4f039a..0e5f9f30243 100644
--- a/source/blender/blenkernel/intern/subsurf_ccg.c
+++ b/source/blender/blenkernel/intern/subsurf_ccg.c
@@ -284,7 +284,8 @@ static int ss_sync_from_uv(CCGSubSurf *ss,
* UV map in really simple cases with mirror + subsurf, see second part of T44530.
* Also, initially intention is to treat merged vertices from mirror modifier as seams.
* This fixes a very old regression (2.49 was correct here) */
- vmap = BKE_mesh_uv_vert_map_create(mpoly, mloop, mloopuv, totface, totvert, limit, false, true);
+ vmap = BKE_mesh_uv_vert_map_create(
+ mpoly, NULL, mloop, mloopuv, totface, totvert, limit, false, true);
if (!vmap) {
return 0;
}
@@ -878,7 +879,7 @@ static void ccgDM_getFinalVertNo(DerivedMesh *dm, int vertNum, float r_no[3])
BLI_INLINE void ccgDM_to_MVert(MVert *mv, const CCGKey *key, CCGElem *elem)
{
copy_v3_v3(mv->co, CCG_elem_co(key, elem));
- mv->flag = mv->bweight = 0;
+ mv->flag = 0;
}
static void ccgDM_copyFinalVertArray(DerivedMesh *dm, MVert *mvert)
@@ -948,7 +949,7 @@ BLI_INLINE void ccgDM_to_MEdge(MEdge *med, const int v1, const int v2, const sho
{
med->v1 = v1;
med->v2 = v2;
- med->crease = med->bweight = 0;
+ med->crease = 0;
med->flag = flag;
}
@@ -1133,14 +1134,12 @@ static void ccgDM_copyFinalPolyArray(DerivedMesh *dm, MPoly *mpoly)
CCGFace *f = ccgdm->faceMap[index].face;
int x, y, S, numVerts = ccgSubSurf_getFaceNumVerts(f);
int flag = (faceFlags) ? faceFlags[index].flag : ME_SMOOTH;
- int mat_nr = (faceFlags) ? faceFlags[index].mat_nr : 0;
for (S = 0; S < numVerts; S++) {
for (y = 0; y < gridSize - 1; y++) {
for (x = 0; x < gridSize - 1; x++) {
MPoly *mp = &mpoly[i];
- mp->mat_nr = mat_nr;
mp->flag = flag;
mp->loopstart = k;
mp->totloop = 4;
@@ -1247,7 +1246,7 @@ static void *ccgDM_get_vert_data_layer(DerivedMesh *dm, int type)
BLI_rw_mutex_lock(&ccgdm->origindex_cache_rwlock, THREAD_LOCK_WRITE);
origindex = CustomData_add_layer(
- &dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numVertData);
+ &dm->vertData, CD_ORIGINDEX, CD_SET_DEFAULT, NULL, dm->numVertData);
totorig = ccgSubSurf_getNumVerts(ss);
totnone = dm->numVertData - totorig;
@@ -1286,7 +1285,7 @@ static void *ccgDM_get_edge_data_layer(DerivedMesh *dm, int type)
}
origindex = CustomData_add_layer(
- &dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numEdgeData);
+ &dm->edgeData, CD_ORIGINDEX, CD_SET_DEFAULT, NULL, dm->numEdgeData);
totedge = ccgSubSurf_getNumEdges(ss);
totorig = totedge * (edgeSize - 1);
@@ -1329,7 +1328,7 @@ static void *ccgDM_get_poly_data_layer(DerivedMesh *dm, int type)
}
origindex = CustomData_add_layer(
- &dm->polyData, CD_ORIGINDEX, CD_CALLOC, NULL, dm->numPolyData);
+ &dm->polyData, CD_ORIGINDEX, CD_SET_DEFAULT, NULL, dm->numPolyData);
totface = ccgSubSurf_getNumFaces(ss);
@@ -1599,13 +1598,15 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm,
gridSize = ccgSubSurf_getGridSize(ss);
gridFaces = gridSize - 1;
gridCuts = gridSize - 2;
- /*gridInternalVerts = gridSideVerts * gridSideVerts; - as yet, unused */
+ // gridInternalVerts = gridSideVerts * gridSideVerts; /* As yet, unused. */
gridSideEdges = gridSize - 1;
gridInternalEdges = (gridSideEdges - 1) * gridSideEdges * 2;
medge = dm->getEdgeArray(dm);
const MPoly *mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY);
+ const int *material_indices = CustomData_get_layer_named(
+ &dm->polyData, CD_MPOLY, "material_index");
const int *base_polyOrigIndex = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX);
int *vertOrigIndex = DM_get_vert_data_layer(&ccgdm->dm, CD_ORIGINDEX);
@@ -1634,7 +1635,7 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm,
ccgdm->faceMap[index].startFace = faceNum;
faceFlags->flag = mpoly ? mpoly[origIndex].flag : 0;
- faceFlags->mat_nr = mpoly ? mpoly[origIndex].mat_nr : 0;
+ faceFlags->mat_nr = material_indices ? material_indices[origIndex] : 0;
faceFlags++;
/* set the face base vert */
diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c
index 9acf387b930..1444ba1e1c4 100644
--- a/source/blender/blenkernel/intern/text.c
+++ b/source/blender/blenkernel/intern/text.c
@@ -245,7 +245,7 @@ IDTypeInfo IDType_ID_TXT = {
.foreach_id = NULL,
.foreach_cache = NULL,
.foreach_path = text_foreach_path,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = text_blend_write,
.blend_read_data = text_blend_read_data,
diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c
index d9e5887a9a8..5cdaa12f514 100644
--- a/source/blender/blenkernel/intern/texture.c
+++ b/source/blender/blenkernel/intern/texture.c
@@ -96,6 +96,7 @@ static void texture_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const i
BKE_id_copy_ex(
bmain, (ID *)texture_src->nodetree, (ID **)&texture_dst->nodetree, flag_private_id_data);
}
+ texture_dst->nodetree->owner_id = &texture_dst->id;
}
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
@@ -206,7 +207,7 @@ IDTypeInfo IDType_ID_TE = {
.foreach_id = texture_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = texture_blend_write,
.blend_read_data = texture_blend_read_data,
diff --git a/source/blender/blenkernel/intern/tracking_detect.c b/source/blender/blenkernel/intern/tracking_detect.c
index 51ffce4a3e3..540f880905d 100644
--- a/source/blender/blenkernel/intern/tracking_detect.c
+++ b/source/blender/blenkernel/intern/tracking_detect.c
@@ -81,7 +81,6 @@ static void detect_retrieve_libmv_features(MovieTracking *tracking,
a = libmv_countFeatures(features);
while (a--) {
- MovieTrackingTrack *track;
double x, y, size, score;
bool ok = true;
float xu, yu;
@@ -99,7 +98,8 @@ static void detect_retrieve_libmv_features(MovieTracking *tracking,
}
if (ok) {
- track = BKE_tracking_track_add(tracking, tracksbase, xu, yu, framenr, width, height);
+ MovieTrackingTrack *track = BKE_tracking_track_add(
+ tracking, tracksbase, xu, yu, framenr, width, height);
track->flag |= SELECT;
track->pat_flag |= SELECT;
track->search_flag |= SELECT;
diff --git a/source/blender/blenkernel/intern/tracking_stabilize.c b/source/blender/blenkernel/intern/tracking_stabilize.c
index e2e0b4227e3..b03d226964c 100644
--- a/source/blender/blenkernel/intern/tracking_stabilize.c
+++ b/source/blender/blenkernel/intern/tracking_stabilize.c
@@ -1342,7 +1342,7 @@ ImBuf *BKE_tracking_stabilize_frame(
return ibuf;
}
- /* Allocate frame for stabilization result, copy alpha mode and colorspace. */
+ /* Allocate frame for stabilization result, copy alpha mode and color-space. */
ibuf_flags = 0;
if (ibuf->rect) {
ibuf_flags |= IB_rect;
diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc
index 0b5d6ad7b10..a01f5d19088 100644
--- a/source/blender/blenkernel/intern/type_conversions.cc
+++ b/source/blender/blenkernel/intern/type_conversions.cc
@@ -367,20 +367,38 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type,
functions->convert_single_to_uninitialized(from_value, to_value);
}
+static void call_convert_to_uninitialized_fn(const GVArray &from,
+ const fn::MultiFunction &fn,
+ const IndexMask mask,
+ GMutableSpan to)
+{
+ fn::MFParamsBuilder params{fn, from.size()};
+ params.add_readonly_single_input(from);
+ params.add_uninitialized_single_output(to);
+ fn::MFContextBuilder context;
+ fn.call_auto(mask, params, context);
+}
+
+static void call_convert_to_uninitialized_fn(const GVArray &from,
+ const fn::MultiFunction &fn,
+ GMutableSpan to)
+{
+ call_convert_to_uninitialized_fn(from, fn, IndexMask(from.size()), to);
+}
+
void DataTypeConversions::convert_to_initialized_n(GSpan from_span, GMutableSpan to_span) const
{
const CPPType &from_type = from_span.type();
const CPPType &to_type = to_span.type();
+
BLI_assert(from_span.size() == to_span.size());
BLI_assert(this->is_convertible(from_type, to_type));
+
const fn::MultiFunction *fn = this->get_conversion_multi_function(
MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type));
- fn::MFParamsBuilder params{*fn, from_span.size()};
- params.add_readonly_single_input(from_span);
+
to_type.destruct_n(to_span.data(), to_span.size());
- params.add_uninitialized_single_output(to_span);
- fn::MFContextBuilder context;
- fn->call_auto(IndexRange(from_span.size()), params, context);
+ call_convert_to_uninitialized_fn(GVArray::ForSpan(from_span), *fn, to_span);
}
class GVArray_For_ConvertedGVArray : public GVArrayImpl {
@@ -414,6 +432,20 @@ class GVArray_For_ConvertedGVArray : public GVArrayImpl {
old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value);
from_type_.destruct(buffer);
}
+
+ void materialize(const IndexMask mask, void *dst) const override
+ {
+ type_->destruct_n(dst, mask.min_array_size());
+ this->materialize_to_uninitialized(mask, dst);
+ }
+
+ void materialize_to_uninitialized(const IndexMask mask, void *dst) const override
+ {
+ call_convert_to_uninitialized_fn(varray_,
+ *old_to_new_conversions_.multi_function,
+ mask,
+ {this->type(), dst, mask.min_array_size()});
+ }
};
class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArrayImpl {
@@ -458,6 +490,20 @@ class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArrayImpl {
new_to_old_conversions_.convert_single_to_uninitialized(value, buffer);
varray_.set_by_relocate(index, buffer);
}
+
+ void materialize(const IndexMask mask, void *dst) const override
+ {
+ type_->destruct_n(dst, mask.min_array_size());
+ this->materialize_to_uninitialized(mask, dst);
+ }
+
+ void materialize_to_uninitialized(const IndexMask mask, void *dst) const override
+ {
+ call_convert_to_uninitialized_fn(varray_,
+ *old_to_new_conversions_.multi_function,
+ mask,
+ {this->type(), dst, mask.min_array_size()});
+ }
};
GVArray DataTypeConversions::try_convert(GVArray varray, const CPPType &to_type) const
@@ -495,9 +541,8 @@ fn::GField DataTypeConversions::try_convert(fn::GField field, const CPPType &to_
if (!this->is_convertible(from_type, to_type)) {
return {};
}
- const fn::MultiFunction &fn =
- *bke::get_implicit_type_conversions().get_conversion_multi_function(
- fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type));
+ const fn::MultiFunction &fn = *this->get_conversion_multi_function(
+ fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type));
return {std::make_shared<fn::FieldOperation>(fn, Vector<fn::GField>{std::move(field)})};
}
diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c
index b31632f0234..f7ea4c81fbf 100644
--- a/source/blender/blenkernel/intern/unit.c
+++ b/source/blender/blenkernel/intern/unit.c
@@ -845,8 +845,8 @@ static bool unit_distribute_negatives(char *str, const int len_max)
bool changed = false;
char *remaining_str = str;
- int remaining_str_len = len_max;
while ((remaining_str = find_next_negative(str, remaining_str)) != NULL) {
+ int remaining_str_len;
/* Exit early in the unlikely situation that we've run out of length to add the parentheses. */
remaining_str_len = len_max - (int)(remaining_str - str);
if (remaining_str_len <= 2) {
@@ -1025,6 +1025,16 @@ static bool unit_find(const char *str, const bUnitDef *unit)
return false;
}
+static const bUnitDef *unit_find_in_collection(const bUnitCollection *usys, const char *str)
+{
+ for (const bUnitDef *unit = usys->units; unit->name; unit++) {
+ if (unit_find(str, unit)) {
+ return unit;
+ }
+ }
+ return NULL;
+}
+
/**
* Try to find a default unit from current or previous string.
* This allows us to handle cases like 2 + 2mm, people would expect to get 4mm, not 2.002m!
@@ -1035,25 +1045,15 @@ static const bUnitDef *unit_detect_from_str(const bUnitCollection *usys,
const char *str,
const char *str_prev)
{
- const bUnitDef *unit = NULL;
-
/* See which units the new value has. */
- for (unit = usys->units; unit->name; unit++) {
- if (unit_find(str, unit)) {
- break;
- }
- }
+ const bUnitDef *unit = unit_find_in_collection(usys, str);
/* Else, try to infer the default unit from the previous string. */
- if (str_prev && (unit == NULL || unit->name == NULL)) {
+ if (str_prev && (unit == NULL)) {
/* See which units the original value had. */
- for (unit = usys->units; unit->name; unit++) {
- if (unit_find(str_prev, unit)) {
- break;
- }
- }
+ unit = unit_find_in_collection(usys, str_prev);
}
/* Else, fall back to default unit. */
- if (unit == NULL || unit->name == NULL) {
+ if (unit == NULL) {
unit = unit_default(usys);
}
@@ -1067,11 +1067,8 @@ bool BKE_unit_string_contains_unit(const char *str, int type)
if (!is_valid_unit_collection(usys)) {
continue;
}
-
- for (int i = 0; i < usys->length; i++) {
- if (unit_find(str, usys->units + i)) {
- return true;
- }
+ if (unit_find_in_collection(usys, str)) {
+ return true;
}
}
return false;
@@ -1155,13 +1152,12 @@ bool BKE_unit_replace_string(
*/
{
char *str_found = str;
- const char *ch = str;
while ((str_found = strchr(str_found, SEP_CHR))) {
bool op_found = false;
/* Any operators after this? */
- for (ch = str_found + 1; *ch != '\0'; ch++) {
+ for (const char *ch = str_found + 1; *ch != '\0'; ch++) {
if (ELEM(*ch, ' ', '\t')) {
continue;
}
diff --git a/source/blender/blenkernel/intern/vfont.c b/source/blender/blenkernel/intern/vfont.c
index 9a6f861eae8..ddc758c6887 100644
--- a/source/blender/blenkernel/intern/vfont.c
+++ b/source/blender/blenkernel/intern/vfont.c
@@ -81,7 +81,7 @@ static void vfont_copy_data(Main *UNUSED(bmain),
{
VFont *vfont_dst = (VFont *)id_dst;
- /* We never handle usercount here for own data. */
+ /* We never handle user-count here for own data. */
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
/* Just to be sure, should not have any value actually after reading time. */
@@ -171,7 +171,7 @@ IDTypeInfo IDType_ID_VF = {
.foreach_id = NULL,
.foreach_cache = NULL,
.foreach_path = vfont_foreach_path,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = vfont_blend_write,
.blend_read_data = vfont_blend_read_data,
@@ -1422,7 +1422,8 @@ static bool vfont_to_curve(Object *ob,
for (i = 0; i <= selend; i++, ct++) {
if (i >= selstart) {
selboxes[i - selstart].x = ct->xof * font_size;
- selboxes[i - selstart].y = ct->yof * font_size;
+ selboxes[i - selstart].y = (ct->yof - 0.25f) * font_size;
+ selboxes[i - selstart].h = font_size;
}
}
}
@@ -1481,17 +1482,17 @@ static bool vfont_to_curve(Object *ob,
f = ef->textcurs[0];
- f[0] = font_size * (-0.1f * co + ct->xof);
- f[1] = font_size * (0.1f * si + ct->yof);
+ f[0] = font_size * (-0.02f * co + ct->xof);
+ f[1] = font_size * (0.1f * si - (0.25f * co) + ct->yof);
- f[2] = font_size * (0.1f * co + ct->xof);
- f[3] = font_size * (-0.1f * si + ct->yof);
+ f[2] = font_size * (0.02f * co + ct->xof);
+ f[3] = font_size * (-0.1f * si - (0.25f * co) + ct->yof);
- f[4] = font_size * (0.1f * co + 0.8f * si + ct->xof);
- f[5] = font_size * (-0.1f * si + 0.8f * co + ct->yof);
+ f[4] = font_size * (0.02f * co + 0.8f * si + ct->xof);
+ f[5] = font_size * (-0.1f * si + 0.75f * co + ct->yof);
- f[6] = font_size * (-0.1f * co + 0.8f * si + ct->xof);
- f[7] = font_size * (0.1f * si + 0.8f * co + ct->yof);
+ f[6] = font_size * (-0.02f * co + 0.8f * si + ct->xof);
+ f[7] = font_size * (0.1f * si + 0.75f * co + ct->yof);
}
if (mode == FO_SELCHANGE) {
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
index 82405830437..502d3ac6d40 100644
--- a/source/blender/blenkernel/intern/volume.cc
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -661,7 +661,7 @@ IDTypeInfo IDType_ID_VO = {
/* foreach_id */ volume_foreach_id,
/* foreach_cache */ volume_foreach_cache,
/* foreach_path */ volume_foreach_path,
- /* owner_get */ nullptr,
+ /* owner_pointer_get */ nullptr,
/* blend_write */ volume_blend_write,
/* blend_read_data */ volume_blend_read_data,
@@ -1089,6 +1089,8 @@ static void volume_evaluate_modifiers(struct Depsgraph *depsgraph,
ModifierApplyFlag apply_flag = use_render ? MOD_APPLY_RENDER : MOD_APPLY_USECACHE;
const ModifierEvalContext mectx = {depsgraph, object, apply_flag};
+ BKE_modifiers_clear_errors(object);
+
/* Get effective list of modifiers to execute. Some effects like shape keys
* are added as virtual modifiers before the user created modifiers. */
VirtualModifierData virtualModifierData;
@@ -1359,6 +1361,26 @@ const char *BKE_volume_grid_name(const VolumeGrid *volume_grid)
}
#ifdef WITH_OPENVDB
+struct ClearGridOp {
+ openvdb::GridBase &grid;
+
+ template<typename Grid> void operator()()
+ {
+ static_cast<Grid &>(grid).clear();
+ }
+};
+void BKE_volume_grid_clear_tree(openvdb::GridBase &grid)
+{
+ const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid);
+ ClearGridOp op{grid};
+ BKE_volume_grid_type_operation(grid_type, op);
+}
+void BKE_volume_grid_clear_tree(Volume &volume, VolumeGrid &volume_grid)
+{
+ openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, &volume_grid, false);
+ BKE_volume_grid_clear_tree(*grid);
+}
+
VolumeGridType BKE_volume_grid_type_openvdb(const openvdb::GridBase &grid)
{
if (grid.isType<openvdb::FloatGrid>()) {
@@ -1449,6 +1471,23 @@ void BKE_volume_grid_transform_matrix(const VolumeGrid *volume_grid, float mat[4
#endif
}
+void BKE_volume_grid_transform_matrix_set(struct VolumeGrid *volume_grid, const float mat[4][4])
+{
+#ifdef WITH_OPENVDB
+ openvdb::math::Mat4f mat_openvdb;
+ for (int col = 0; col < 4; col++) {
+ for (int row = 0; row < 4; row++) {
+ mat_openvdb(col, row) = mat[col][row];
+ }
+ }
+ openvdb::GridBase::Ptr grid = volume_grid->grid();
+ grid->setTransform(std::make_shared<openvdb::math::Transform>(
+ std::make_shared<openvdb::math::AffineMap>(mat_openvdb)));
+#else
+ UNUSED_VARS(volume_grid, mat);
+#endif
+}
+
/* Grid Tree and Voxels */
/* Volume Editing */
@@ -1545,6 +1584,17 @@ void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid)
#endif
}
+bool BKE_volume_grid_determinant_valid(const double determinant)
+{
+#ifdef WITH_OPENVDB
+ /* Limit taken from openvdb/math/Maps.h. */
+ return std::abs(determinant) >= 3.0 * openvdb::math::Tolerance<double>::value();
+#else
+ UNUSED_VARS(determinant);
+ return true;
+#endif
+}
+
int BKE_volume_simplify_level(const Depsgraph *depsgraph)
{
if (DEG_get_mode(depsgraph) != DAG_EVAL_RENDER) {
diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc
index ef75d3d2482..f3bb8726b4f 100644
--- a/source/blender/blenkernel/intern/volume_to_mesh.cc
+++ b/source/blender/blenkernel/intern/volume_to_mesh.cc
@@ -178,9 +178,9 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid,
0,
0,
0,
- {mesh->mvert, mesh->totvert},
- {mesh->mpoly, mesh->totpoly},
- {mesh->mloop, mesh->totloop});
+ mesh->verts_for_write(),
+ mesh->polys_for_write(),
+ mesh->loops_for_write());
BKE_mesh_calc_edges(mesh, false, false);
diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.cc
index 0265dd037b1..8008dacc853 100644
--- a/source/blender/blenkernel/intern/workspace.c
+++ b/source/blender/blenkernel/intern/workspace.cc
@@ -57,7 +57,7 @@ static void workspace_free_data(ID *id)
BLI_freelistN(&workspace->layouts);
while (!BLI_listbase_is_empty(&workspace->tools)) {
- BKE_workspace_tool_remove(workspace, workspace->tools.first);
+ BKE_workspace_tool_remove(workspace, static_cast<bToolRef *>(workspace->tools.first));
}
MEM_SAFE_FREE(workspace->status_text);
@@ -107,12 +107,12 @@ static void workspace_blend_read_data(BlendDataReader *reader, ID *id)
}
LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
- tref->runtime = NULL;
+ tref->runtime = nullptr;
BLO_read_data_address(reader, &tref->properties);
IDP_BlendDataRead(reader, &tref->properties);
}
- workspace->status_text = NULL;
+ workspace->status_text = nullptr;
id_us_ensure_real(&workspace->id);
}
@@ -125,15 +125,15 @@ static void workspace_blend_read_lib(BlendLibReader *reader, ID *id)
/* Do not keep the scene reference when appending a workspace. Setting a scene for a workspace is
* a convenience feature, but the workspace should never truly depend on scene data. */
if (ID_IS_LINKED(id)) {
- workspace->pin_scene = NULL;
+ workspace->pin_scene = nullptr;
}
else {
- BLO_read_id_address(reader, NULL, &workspace->pin_scene);
+ BLO_read_id_address(reader, nullptr, &workspace->pin_scene);
}
/* Restore proper 'parent' pointers to relevant data, and clean up unused/invalid entries. */
LISTBASE_FOREACH_MUTABLE (WorkSpaceDataRelation *, relation, &workspace->hook_layout_relations) {
- relation->parent = NULL;
+ relation->parent = nullptr;
LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) {
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
if (win->winid == relation->parentid) {
@@ -141,7 +141,7 @@ static void workspace_blend_read_lib(BlendLibReader *reader, ID *id)
}
}
}
- if (relation->parent == NULL) {
+ if (relation->parent == nullptr) {
BLI_freelinkN(&workspace->hook_layout_relations, relation);
}
}
@@ -176,33 +176,33 @@ static void workspace_blend_read_expand(BlendExpander *expander, ID *id)
}
IDTypeInfo IDType_ID_WS = {
- .id_code = ID_WS,
- .id_filter = FILTER_ID_WS,
- .main_listbase_index = INDEX_ID_WS,
- .struct_size = sizeof(WorkSpace),
- .name = "WorkSpace",
- .name_plural = "workspaces",
- .translation_context = BLT_I18NCONTEXT_ID_WORKSPACE,
- .flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA,
- .asset_type_info = NULL,
-
- .init_data = workspace_init_data,
- .copy_data = NULL,
- .free_data = workspace_free_data,
- .make_local = NULL,
- .foreach_id = workspace_foreach_id,
- .foreach_cache = NULL,
- .foreach_path = NULL,
- .owner_get = NULL,
-
- .blend_write = workspace_blend_write,
- .blend_read_data = workspace_blend_read_data,
- .blend_read_lib = workspace_blend_read_lib,
- .blend_read_expand = workspace_blend_read_expand,
-
- .blend_read_undo_preserve = NULL,
-
- .lib_override_apply_post = NULL,
+ /* id_code */ ID_WS,
+ /* id_filter */ FILTER_ID_WS,
+ /* main_listbase_index */ INDEX_ID_WS,
+ /* struct_size */ sizeof(WorkSpace),
+ /* name */ "WorkSpace",
+ /* name_plural */ "workspaces",
+ /* translation_context */ BLT_I18NCONTEXT_ID_WORKSPACE,
+ /* flags */ IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ workspace_init_data,
+ /* copy_data */ nullptr,
+ /* free_data */ workspace_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ workspace_foreach_id,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_pointer_get */ nullptr,
+
+ /* blend_write */ workspace_blend_write,
+ /* blend_read_data */ workspace_blend_read_data,
+ /* blend_read_lib */ workspace_blend_read_lib,
+ /* blend_read_expand */ workspace_blend_read_expand,
+
+ /* blend_read_undo_preserve */ nullptr,
+
+ /* lib_override_apply_post */ nullptr,
};
/* -------------------------------------------------------------------- */
@@ -230,7 +230,8 @@ static void workspace_layout_name_set(WorkSpace *workspace,
static WorkSpaceLayout *workspace_layout_find_exec(const WorkSpace *workspace,
const bScreen *screen)
{
- return BLI_findptr(&workspace->layouts, screen, offsetof(WorkSpaceLayout, screen));
+ return static_cast<WorkSpaceLayout *>(
+ BLI_findptr(&workspace->layouts, screen, offsetof(WorkSpaceLayout, screen)));
}
static void workspace_relation_add(ListBase *relation_list,
@@ -238,7 +239,7 @@ static void workspace_relation_add(ListBase *relation_list,
const int parentid,
void *data)
{
- WorkSpaceDataRelation *relation = MEM_callocN(sizeof(*relation), __func__);
+ WorkSpaceDataRelation *relation = MEM_cnew<WorkSpaceDataRelation>(__func__);
relation->parent = parent;
relation->parentid = parentid;
relation->value = data;
@@ -256,9 +257,9 @@ static void workspace_relation_ensure_updated(ListBase *relation_list,
const int parentid,
void *data)
{
- WorkSpaceDataRelation *relation = BLI_listbase_bytes_find(
- relation_list, &parentid, sizeof(parentid), offsetof(WorkSpaceDataRelation, parentid));
- if (relation != NULL) {
+ WorkSpaceDataRelation *relation = static_cast<WorkSpaceDataRelation *>(BLI_listbase_bytes_find(
+ relation_list, &parentid, sizeof(parentid), offsetof(WorkSpaceDataRelation, parentid)));
+ if (relation != nullptr) {
relation->parent = parent;
relation->value = data;
/* reinsert at the head of the list, so that more commonly used relations are found faster. */
@@ -274,13 +275,13 @@ static void workspace_relation_ensure_updated(ListBase *relation_list,
static void *workspace_relation_get_data_matching_parent(const ListBase *relation_list,
const void *parent)
{
- WorkSpaceDataRelation *relation = BLI_findptr(
- relation_list, parent, offsetof(WorkSpaceDataRelation, parent));
- if (relation != NULL) {
+ WorkSpaceDataRelation *relation = static_cast<WorkSpaceDataRelation *>(
+ BLI_findptr(relation_list, parent, offsetof(WorkSpaceDataRelation, parent)));
+ if (relation != nullptr) {
return relation->value;
}
- return NULL;
+ return nullptr;
}
/**
@@ -296,7 +297,8 @@ static bool UNUSED_FUNCTION(workspaces_is_screen_used)
#endif
(const Main *bmain, bScreen *screen)
{
- for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
+ for (WorkSpace *workspace = static_cast<WorkSpace *>(bmain->workspaces.first); workspace;
+ workspace = static_cast<WorkSpace *>(workspace->id.next)) {
if (workspace_layout_find_exec(workspace, screen)) {
return true;
}
@@ -313,14 +315,16 @@ static bool UNUSED_FUNCTION(workspaces_is_screen_used)
WorkSpace *BKE_workspace_add(Main *bmain, const char *name)
{
- WorkSpace *new_workspace = BKE_id_new(bmain, ID_WS, name);
+ WorkSpace *new_workspace = static_cast<WorkSpace *>(BKE_id_new(bmain, ID_WS, name));
id_us_ensure_real(&new_workspace->id);
return new_workspace;
}
void BKE_workspace_remove(Main *bmain, WorkSpace *workspace)
{
- for (WorkSpaceLayout *layout = workspace->layouts.first, *layout_next; layout;
+ for (WorkSpaceLayout *layout = static_cast<WorkSpaceLayout *>(workspace->layouts.first),
+ *layout_next;
+ layout;
layout = layout_next) {
layout_next = layout->next;
BKE_workspace_layout_remove(bmain, workspace, layout);
@@ -330,11 +334,13 @@ void BKE_workspace_remove(Main *bmain, WorkSpace *workspace)
WorkSpaceInstanceHook *BKE_workspace_instance_hook_create(const Main *bmain, const int winid)
{
- WorkSpaceInstanceHook *hook = MEM_callocN(sizeof(WorkSpaceInstanceHook), __func__);
+ WorkSpaceInstanceHook *hook = MEM_cnew<WorkSpaceInstanceHook>(__func__);
/* set an active screen-layout for each possible window/workspace combination */
- for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
- BKE_workspace_active_layout_set(hook, winid, workspace, workspace->layouts.first);
+ for (WorkSpace *workspace = static_cast<WorkSpace *>(bmain->workspaces.first); workspace;
+ workspace = static_cast<WorkSpace *>(workspace->id.next)) {
+ BKE_workspace_active_layout_set(
+ hook, winid, workspace, static_cast<WorkSpaceLayout *>(workspace->layouts.first));
}
return hook;
@@ -347,8 +353,11 @@ void BKE_workspace_instance_hook_free(const Main *bmain, WorkSpaceInstanceHook *
BLI_assert(!BLI_listbase_is_empty(&bmain->workspaces) || G.background);
/* Free relations for this hook */
- for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
- for (WorkSpaceDataRelation *relation = workspace->hook_layout_relations.first, *relation_next;
+ for (WorkSpace *workspace = static_cast<WorkSpace *>(bmain->workspaces.first); workspace;
+ workspace = static_cast<WorkSpace *>(workspace->id.next)) {
+ for (WorkSpaceDataRelation *relation = static_cast<WorkSpaceDataRelation *>(
+ workspace->hook_layout_relations.first),
+ *relation_next;
relation;
relation = relation_next) {
relation_next = relation->next;
@@ -366,7 +375,7 @@ WorkSpaceLayout *BKE_workspace_layout_add(Main *bmain,
bScreen *screen,
const char *name)
{
- WorkSpaceLayout *layout = MEM_callocN(sizeof(*layout), __func__);
+ WorkSpaceLayout *layout = MEM_cnew<WorkSpaceLayout>(__func__);
BLI_assert(!workspaces_is_screen_used(bmain, screen));
#ifndef DEBUG
@@ -393,7 +402,10 @@ void BKE_workspace_layout_remove(Main *bmain, WorkSpace *workspace, WorkSpaceLay
void BKE_workspace_relations_free(ListBase *relation_list)
{
- for (WorkSpaceDataRelation *relation = relation_list->first, *relation_next; relation;
+ for (WorkSpaceDataRelation *
+ relation = static_cast<WorkSpaceDataRelation *>(relation_list->first),
+ *relation_next;
+ relation;
relation = relation_next) {
relation_next = relation->next;
workspace_relation_remove(relation_list, relation);
@@ -420,7 +432,7 @@ WorkSpaceLayout *BKE_workspace_layout_find(const WorkSpace *workspace, const bSc
workspace->id.name + 2,
screen->id.name + 2);
- return NULL;
+ return nullptr;
}
WorkSpaceLayout *BKE_workspace_layout_find_global(const Main *bmain,
@@ -430,10 +442,11 @@ WorkSpaceLayout *BKE_workspace_layout_find_global(const Main *bmain,
WorkSpaceLayout *layout;
if (r_workspace) {
- *r_workspace = NULL;
+ *r_workspace = nullptr;
}
- for (WorkSpace *workspace = bmain->workspaces.first; workspace; workspace = workspace->id.next) {
+ for (WorkSpace *workspace = static_cast<WorkSpace *>(bmain->workspaces.first); workspace;
+ workspace = static_cast<WorkSpace *>(workspace->id.next)) {
if ((layout = workspace_layout_find_exec(workspace, screen))) {
if (r_workspace) {
*r_workspace = workspace;
@@ -443,7 +456,7 @@ WorkSpaceLayout *BKE_workspace_layout_find_global(const Main *bmain,
}
}
- return NULL;
+ return nullptr;
}
WorkSpaceLayout *BKE_workspace_layout_iter_circular(const WorkSpace *workspace,
@@ -456,23 +469,23 @@ WorkSpaceLayout *BKE_workspace_layout_iter_circular(const WorkSpace *workspace,
WorkSpaceLayout *iter_layout;
if (iter_backward) {
- LISTBASE_CIRCULAR_BACKWARD_BEGIN (&workspace->layouts, iter_layout, start) {
+ LISTBASE_CIRCULAR_BACKWARD_BEGIN (WorkSpaceLayout *, &workspace->layouts, iter_layout, start) {
if (!callback(iter_layout, arg)) {
return iter_layout;
}
}
- LISTBASE_CIRCULAR_BACKWARD_END(&workspace->layouts, iter_layout, start);
+ LISTBASE_CIRCULAR_BACKWARD_END(WorkSpaceLayout *, &workspace->layouts, iter_layout, start);
}
else {
- LISTBASE_CIRCULAR_FORWARD_BEGIN (&workspace->layouts, iter_layout, start) {
+ LISTBASE_CIRCULAR_FORWARD_BEGIN (WorkSpaceLayout *, &workspace->layouts, iter_layout, start) {
if (!callback(iter_layout, arg)) {
return iter_layout;
}
}
- LISTBASE_CIRCULAR_FORWARD_END(&workspace->layouts, iter_layout, start);
+ LISTBASE_CIRCULAR_FORWARD_END(WorkSpaceLayout *, &workspace->layouts, iter_layout, start);
}
- return NULL;
+ return nullptr;
}
void BKE_workspace_tool_remove(struct WorkSpace *workspace, struct bToolRef *tref)
@@ -494,13 +507,13 @@ bool BKE_workspace_owner_id_check(const WorkSpace *workspace, const char *owner_
}
/* We could use hash lookup, for now this list is highly likely under < ~16 items. */
- return BLI_findstring(&workspace->owner_ids, owner_id, offsetof(wmOwnerID, name)) != NULL;
+ return BLI_findstring(&workspace->owner_ids, owner_id, offsetof(wmOwnerID, name)) != nullptr;
}
void BKE_workspace_id_tag_all_visible(Main *bmain, int tag)
{
BKE_main_id_tag_listbase(&bmain->workspaces, tag, false);
- wmWindowManager *wm = bmain->wm.first;
+ wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
WorkSpace *workspace = BKE_workspace_active_get(win->workspace_hook);
workspace->id.tag |= tag;
@@ -523,14 +536,14 @@ void BKE_workspace_active_set(WorkSpaceInstanceHook *hook, WorkSpace *workspace)
* that optimization is possible and needed.
* This code can be called from places where we might have this equality, but still want to
* ensure/update the active layout below.
- * Known case where this is buggy and will crash later due to NULL active layout: reading
+ * Known case where this is buggy and will crash later due to nullptr active layout: reading
* a blend file, when the new read workspace ID happens to have the exact same memory address
* as when it was saved in the blend file (extremely unlikely, but possible). */
hook->active = workspace;
if (workspace) {
- WorkSpaceLayout *layout = workspace_relation_get_data_matching_parent(
- &workspace->hook_layout_relations, hook);
+ WorkSpaceLayout *layout = static_cast<WorkSpaceLayout *>(
+ workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook));
if (layout) {
hook->act_layout = layout;
}
@@ -551,7 +564,8 @@ WorkSpaceLayout *BKE_workspace_active_layout_for_workspace_get(const WorkSpaceIn
}
/* Inactive workspace */
- return workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook);
+ return static_cast<WorkSpaceLayout *>(
+ workspace_relation_get_data_matching_parent(&workspace->hook_layout_relations, hook));
}
void BKE_workspace_active_layout_set(WorkSpaceInstanceHook *hook,
diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c
index cc3ee06f539..c6c50ee068c 100644
--- a/source/blender/blenkernel/intern/world.c
+++ b/source/blender/blenkernel/intern/world.c
@@ -98,6 +98,7 @@ static void world_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
BKE_id_copy_ex(
bmain, (ID *)wrld_src->nodetree, (ID **)&wrld_dst->nodetree, flag_private_id_data);
}
+ wrld_dst->nodetree->owner_id = &wrld_dst->id;
}
BLI_listbase_clear(&wrld_dst->gpumaterial);
@@ -197,7 +198,7 @@ IDTypeInfo IDType_ID_WO = {
.foreach_id = world_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = world_blend_write,
.blend_read_data = world_blend_read_data,
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 5e11cd0703a..883591e3e87 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -1485,7 +1485,7 @@ void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
}
}
-void BKE_ffmpeg_image_type_verify(RenderData *rd, ImageFormatData *imf)
+void BKE_ffmpeg_image_type_verify(RenderData *rd, const ImageFormatData *imf)
{
int audio = 0;
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index 41d1eef733c..96aecadd3f5 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -128,7 +128,7 @@ typedef struct NlaEvalData {
int num_channels;
NlaEvalSnapshot base_snapshot;
- /* Evaluation result shapshot. */
+ /* Evaluation result snapshot. */
NlaEvalSnapshot eval_snapshot;
} NlaEvalData;
@@ -241,6 +241,17 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data,
float upper_influence,
NlaEvalSnapshot *r_upper_snapshot);
+/**
+ * Using \a blended_snapshot and \a upper_snapshot, we can solve for the \a r_lower_snapshot.
+ *
+ * Only channels that exist within \a blended_snapshot are processed.
+ * Only blended values within the \a remap_domain are processed.
+ *
+ * Writes to \a r_upper_snapshot `NlaEvalChannelSnapshot->remap_domain` to match remapping success.
+ *
+ * Assumes caller marked upper values that are in the \a blend_domain. This determines whether the
+ * blended value came directly from the lower snapshot or a result of blending.
+ */
void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data,
NlaEvalSnapshot *blended_snapshot,
NlaEvalSnapshot *upper_snapshot,
diff --git a/source/blender/blenlib/BLI_any.hh b/source/blender/blenlib/BLI_any.hh
index a20239f214f..f9b53436763 100644
--- a/source/blender/blenlib/BLI_any.hh
+++ b/source/blender/blenlib/BLI_any.hh
@@ -39,7 +39,7 @@ template<typename ExtraInfo> struct AnyTypeInfo {
* Used when #T is stored directly in the inline buffer of the #Any.
*/
template<typename ExtraInfo, typename T>
-static constexpr AnyTypeInfo<ExtraInfo> info_for_inline = {
+inline constexpr AnyTypeInfo<ExtraInfo> info_for_inline = {
is_trivially_copy_constructible_extended_v<T> ?
nullptr :
+[](void *dst, const void *src) { new (dst) T(*(const T *)src); },
@@ -57,7 +57,7 @@ static constexpr AnyTypeInfo<ExtraInfo> info_for_inline = {
*/
template<typename T> using Ptr = std::unique_ptr<T>;
template<typename ExtraInfo, typename T>
-static constexpr AnyTypeInfo<ExtraInfo> info_for_unique_ptr = {
+inline constexpr AnyTypeInfo<ExtraInfo> info_for_unique_ptr = {
[](void *dst, const void *src) { new (dst) Ptr<T>(new T(**(const Ptr<T> *)src)); },
[](void *dst, void *src) { new (dst) Ptr<T>(new T(std::move(**(Ptr<T> *)src))); },
[](void *src) { std::destroy_at((Ptr<T> *)src); },
diff --git a/source/blender/blenlib/BLI_array_store.h b/source/blender/blenlib/BLI_array_store.h
index 8a91825da6f..c04c392627d 100644
--- a/source/blender/blenlib/BLI_array_store.h
+++ b/source/blender/blenlib/BLI_array_store.h
@@ -57,7 +57,6 @@ size_t BLI_array_store_calc_size_expanded_get(const BArrayStore *bs);
size_t BLI_array_store_calc_size_compacted_get(const BArrayStore *bs);
/**
- *
* \param data: Data used to create
* \param state_reference: The state to use as a reference when adding the new state,
* typically this is the previous state,
diff --git a/source/blender/blenlib/BLI_array_utils.hh b/source/blender/blenlib/BLI_array_utils.hh
new file mode 100644
index 00000000000..95b3bde10f4
--- /dev/null
+++ b/source/blender/blenlib/BLI_array_utils.hh
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_generic_span.hh"
+#include "BLI_generic_virtual_array.hh"
+#include "BLI_index_mask.hh"
+#include "BLI_task.hh"
+#include "BLI_virtual_array.hh"
+
+namespace blender::array_utils {
+
+/**
+ * Fill the destination span by copying masked values from the `src` array. Threaded based on
+ * grain-size.
+ */
+void copy(const GVArray &src, IndexMask selection, GMutableSpan dst, int64_t grain_size = 4096);
+
+/**
+ * Fill the destination span by copying values from the `src` array. Threaded based on
+ * grain-size.
+ */
+template<typename T>
+inline void copy(const Span<T> src,
+ const IndexMask selection,
+ MutableSpan<T> dst,
+ const int64_t grain_size = 4096)
+{
+ BLI_assert(src.size() == dst.size());
+ threading::parallel_for(selection.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int64_t index : selection.slice(range)) {
+ dst[index] = src[index];
+ }
+ });
+}
+
+/**
+ * Fill the destination span by gathering indexed values from the `src` array.
+ */
+void gather(const GVArray &src, IndexMask indices, GMutableSpan dst, int64_t grain_size = 4096);
+
+/**
+ * Fill the destination span by gathering indexed values from the `src` array.
+ */
+template<typename T>
+inline void gather(const VArray<T> &src,
+ const IndexMask indices,
+ MutableSpan<T> dst,
+ const int64_t grain_size = 4096)
+{
+ BLI_assert(indices.size() == dst.size());
+ threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
+ src.materialize_compressed_to_uninitialized(indices.slice(range), dst.slice(range).data());
+ });
+}
+
+/**
+ * Fill the destination span by gathering indexed values from the `src` array.
+ */
+template<typename T, typename IndexT>
+inline void gather(const Span<T> src,
+ const IndexMask indices,
+ MutableSpan<T> dst,
+ const int64_t grain_size = 4096)
+{
+ BLI_assert(indices.size() == dst.size());
+ threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int64_t i : range) {
+ dst[i] = src[indices[i]];
+ }
+ });
+}
+
+/**
+ * Fill the destination span by gathering indexed values from the `src` array.
+ */
+template<typename T, typename IndexT>
+inline void gather(const Span<T> src,
+ const Span<IndexT> indices,
+ MutableSpan<T> dst,
+ const int64_t grain_size = 4096)
+{
+ BLI_assert(indices.size() == dst.size());
+ threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int64_t i : range) {
+ dst[i] = src[indices[i]];
+ }
+ });
+}
+
+/**
+ * Fill the destination span by gathering indexed values from the `src` array.
+ */
+template<typename T, typename IndexT>
+inline void gather(const VArray<T> &src,
+ const Span<IndexT> indices,
+ MutableSpan<T> dst,
+ const int64_t grain_size = 4096)
+{
+ BLI_assert(indices.size() == dst.size());
+ devirtualize_varray(src, [&](const auto &src) {
+ threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
+ for (const int64_t i : range) {
+ dst[i] = src[indices[i]];
+ }
+ });
+ });
+}
+
+} // namespace blender::array_utils
diff --git a/source/blender/blenlib/BLI_bit_vector.hh b/source/blender/blenlib/BLI_bit_vector.hh
new file mode 100644
index 00000000000..2cec190f84a
--- /dev/null
+++ b/source/blender/blenlib/BLI_bit_vector.hh
@@ -0,0 +1,529 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * A `blender::BitVector` is a dynamically growing contiguous arrays of bits. Its main purpose is
+ * to provide a compact way to map indices to bools. It requires 8 times less memory compared to a
+ * `blender::Vector<bool>`.
+ *
+ * Advantages of using a bit- instead of byte-vector are:
+ * - Uses less memory.
+ * - Allows checking the state of many elements at the same time (8 times more bits than bytes fit
+ * into a CPU register). This can improve performance.
+ *
+ * The compact nature of storing bools in individual bits has some downsides that have to be kept
+ * in mind:
+ * - Writing to separate bits in the same byte is not thread-safe. Therefore, an existing vector of
+ * bool can't easily be replaced with a bit vector, if it is written to from multiple threads.
+ * Read-only access from multiple threads is fine though.
+ * - Writing individual elements is more expensive when the array is in cache already. That is
+ * because changing a bit is always a read-modify-write operation on the byte the bit resides in.
+ * - Reading individual elements is more expensive when the array is in cache already. That is
+ * because additional bit-wise operations have to be applied after the corresponding byte is
+ * read.
+ *
+ * Comparison to `std::vector<bool>`:
+ * - `blender::BitVector` has an interface that is more optimized for dealing with bits.
+ * - `blender::BitVector` has an inline buffer that is used to avoid allocations when the vector is
+ * small.
+ *
+ * Comparison to `BLI_bitmap`:
+ * - `blender::BitVector` offers a more C++ friendly interface.
+ * - `BLI_bitmap` should only be used in C code that can not use `blender::BitVector`.
+ */
+
+#include <cstring>
+
+#include "BLI_allocator.hh"
+#include "BLI_index_range.hh"
+#include "BLI_memory_utils.hh"
+#include "BLI_span.hh"
+
+namespace blender {
+
+/**
+ * This is a read-only pointer to a specific bit. The value of the bit can be retrieved, but not
+ * changed.
+ */
+class BitRef {
+ private:
+ /** Points to the exact byte that the bit is in. */
+ const uint8_t *byte_ptr_;
+ /** All zeros except for a single one at the bit that is referenced. */
+ uint8_t mask_;
+
+ friend class MutableBitRef;
+
+ public:
+ BitRef() = default;
+
+ /**
+ * Reference a specific bit in a byte array. Note that #byte_ptr does *not* have to point to the
+ * exact byte the bit is in.
+ */
+ BitRef(const uint8_t *byte_ptr, const int64_t bit_index)
+ {
+ byte_ptr_ = byte_ptr + (bit_index >> 3);
+ mask_ = 1 << (bit_index & 7);
+ }
+
+ /**
+ * Return true when the bit is currently 1 and false otherwise.
+ */
+ bool test() const
+ {
+ const uint8_t byte = *byte_ptr_;
+ const uint8_t masked_byte = byte & mask_;
+ return masked_byte != 0;
+ }
+
+ operator bool() const
+ {
+ return this->test();
+ }
+};
+
+/**
+ * Similar to #BitRef, but also allows changing the referenced bit.
+ */
+class MutableBitRef {
+ private:
+ /** Points to the exact byte that the bit is in. */
+ uint8_t *byte_ptr_;
+ /** All zeros except for a single one at the bit that is referenced. */
+ uint8_t mask_;
+
+ public:
+ MutableBitRef() = default;
+
+ /**
+ * Reference a specific bit in a byte array. Note that #byte_ptr does *not* have to point to the
+ * exact byte the bit is in.
+ */
+ MutableBitRef(uint8_t *byte_ptr, const int64_t bit_index)
+ {
+ byte_ptr_ = byte_ptr + (bit_index >> 3);
+ mask_ = 1 << static_cast<uint8_t>(bit_index & 7);
+ }
+
+ /**
+ * Support implicitly casting to a read-only #BitRef.
+ */
+ operator BitRef() const
+ {
+ BitRef bit_ref;
+ bit_ref.byte_ptr_ = byte_ptr_;
+ bit_ref.mask_ = mask_;
+ return bit_ref;
+ }
+
+ /**
+ * Return true when the bit is currently 1 and false otherwise.
+ */
+ bool test() const
+ {
+ const uint8_t byte = *byte_ptr_;
+ const uint8_t masked_byte = byte & mask_;
+ return masked_byte != 0;
+ }
+
+ operator bool() const
+ {
+ return this->test();
+ }
+
+ /**
+ * Change the bit to a 1.
+ */
+ void set()
+ {
+ *byte_ptr_ |= mask_;
+ }
+
+ /**
+ * Change the bit to a 0.
+ */
+ void reset()
+ {
+ *byte_ptr_ &= ~mask_;
+ }
+
+ /**
+ * Change the bit to a 1 if #value is true and 0 otherwise.
+ */
+ void set(const bool value)
+ {
+ if (value) {
+ this->set();
+ }
+ else {
+ this->reset();
+ }
+ }
+};
+
+template<
+ /**
+ * Number of bits that can be stored in the vector without doing an allocation.
+ */
+ int64_t InlineBufferCapacity = 32,
+ /**
+ * The allocator used by this vector. Should rarely be changed, except when you don't want that
+ * MEM_* is used internally.
+ */
+ typename Allocator = GuardedAllocator>
+class BitVector {
+ private:
+ static constexpr int64_t required_bytes_for_bits(const int64_t number_of_bits)
+ {
+ return (number_of_bits + BitsPerByte - 1) / BitsPerByte;
+ }
+
+ static constexpr int64_t BitsPerByte = 8;
+ static constexpr int64_t BytesInInlineBuffer = required_bytes_for_bits(InlineBufferCapacity);
+ static constexpr int64_t BitsInInlineBuffer = BytesInInlineBuffer * BitsPerByte;
+ static constexpr int64_t AllocationAlignment = 8;
+
+ /**
+ * Points to the first byte used by the vector. It might point to the memory in the inline
+ * buffer.
+ */
+ uint8_t *data_;
+
+ /** Current size of the vector in bits. */
+ int64_t size_in_bits_;
+
+ /** Number of bits that fit into the vector until a reallocation has to occur. */
+ int64_t capacity_in_bits_;
+
+ /** Used for allocations when the inline buffer is too small. */
+ Allocator allocator_;
+
+ /** Contains the bits as long as the vector is small enough. */
+ TypedBuffer<uint8_t, BytesInInlineBuffer> inline_buffer_;
+
+ public:
+ BitVector(Allocator allocator = {}) noexcept : allocator_(allocator)
+ {
+ data_ = inline_buffer_;
+ size_in_bits_ = 0;
+ capacity_in_bits_ = BitsInInlineBuffer;
+ uninitialized_fill_n(data_, BytesInInlineBuffer, static_cast<uint8_t>(0));
+ }
+
+ BitVector(NoExceptConstructor, Allocator allocator = {}) noexcept : BitVector(allocator)
+ {
+ }
+
+ BitVector(const BitVector &other) : BitVector(NoExceptConstructor(), other.allocator_)
+ {
+ const int64_t bytes_to_copy = other.used_bytes_amount();
+ if (other.size_in_bits_ <= BitsInInlineBuffer) {
+ /* The data is copied into the owned inline buffer. */
+ data_ = inline_buffer_;
+ capacity_in_bits_ = BitsInInlineBuffer;
+ }
+ else {
+ /* Allocate a new byte array because the inline buffer is too small. */
+ data_ = static_cast<uint8_t *>(
+ allocator_.allocate(bytes_to_copy, AllocationAlignment, __func__));
+ capacity_in_bits_ = bytes_to_copy * BitsPerByte;
+ }
+ size_in_bits_ = other.size_in_bits_;
+ uninitialized_copy_n(other.data_, bytes_to_copy, data_);
+ }
+
+ BitVector(BitVector &&other) noexcept : BitVector(NoExceptConstructor(), other.allocator_)
+ {
+ if (other.is_inline()) {
+ /* Copy the data into the inline buffer. */
+ const int64_t bytes_to_copy = other.used_bytes_amount();
+ data_ = inline_buffer_;
+ uninitialized_copy_n(other.data_, bytes_to_copy, data_);
+ }
+ else {
+ /* Steal the pointer. */
+ data_ = other.data_;
+ }
+ size_in_bits_ = other.size_in_bits_;
+ capacity_in_bits_ = other.capacity_in_bits_;
+
+ /* Clear the other vector because it has been moved from. */
+ other.data_ = other.inline_buffer_;
+ other.size_in_bits_ = 0;
+ other.capacity_in_bits_ = BitsInInlineBuffer;
+ }
+
+ /**
+ * Create a new vector with the given size and fill it with #value.
+ */
+ BitVector(const int64_t size_in_bits, const bool value = false, Allocator allocator = {})
+ : BitVector(NoExceptConstructor(), allocator)
+ {
+ this->resize(size_in_bits, value);
+ }
+
+ /**
+ * Create a bit vector based on an array of bools. Each byte of the input array maps to one bit.
+ */
+ explicit BitVector(const Span<bool> values, Allocator allocator = {})
+ : BitVector(NoExceptConstructor(), allocator)
+ {
+ this->resize(values.size());
+ for (const int64_t i : this->index_range()) {
+ (*this)[i].set(values[i]);
+ }
+ }
+
+ ~BitVector()
+ {
+ if (!this->is_inline()) {
+ allocator_.deallocate(data_);
+ }
+ }
+
+ BitVector &operator=(const BitVector &other)
+ {
+ return copy_assign_container(*this, other);
+ }
+
+ BitVector &operator=(BitVector &&other)
+ {
+ return move_assign_container(*this, std::move(other));
+ }
+
+ /**
+ * Number of bits in the bit vector.
+ */
+ int64_t size() const
+ {
+ return size_in_bits_;
+ }
+
+ /**
+ * Get a read-only reference to a specific bit.
+ */
+ BitRef operator[](const int64_t index) const
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_in_bits_);
+ return {data_, index};
+ }
+
+ /**
+ * Get a mutable reference to a specific bit.
+ */
+ MutableBitRef operator[](const int64_t index)
+ {
+ BLI_assert(index >= 0);
+ BLI_assert(index < size_in_bits_);
+ return {data_, index};
+ }
+
+ IndexRange index_range() const
+ {
+ return {0, size_in_bits_};
+ }
+
+ /**
+ * Add a new bit to the end of the vector.
+ */
+ void append(const bool value)
+ {
+ this->ensure_space_for_one();
+ MutableBitRef bit{data_, size_in_bits_};
+ bit.set(value);
+ size_in_bits_++;
+ }
+
+ class Iterator {
+ private:
+ const BitVector *vector_;
+ int64_t index_;
+
+ public:
+ Iterator(const BitVector &vector, const int64_t index) : vector_(&vector), index_(index)
+ {
+ }
+
+ Iterator &operator++()
+ {
+ index_++;
+ return *this;
+ }
+
+ friend bool operator!=(const Iterator &a, const Iterator &b)
+ {
+ BLI_assert(a.vector_ == b.vector_);
+ return a.index_ != b.index_;
+ }
+
+ BitRef operator*() const
+ {
+ return (*vector_)[index_];
+ }
+ };
+
+ class MutableIterator {
+ private:
+ BitVector *vector_;
+ int64_t index_;
+
+ public:
+ MutableIterator(BitVector &vector, const int64_t index) : vector_(&vector), index_(index)
+ {
+ }
+
+ MutableIterator &operator++()
+ {
+ index_++;
+ return *this;
+ }
+
+ friend bool operator!=(const MutableIterator &a, const MutableIterator &b)
+ {
+ BLI_assert(a.vector_ == b.vector_);
+ return a.index_ != b.index_;
+ }
+
+ MutableBitRef operator*() const
+ {
+ return (*vector_)[index_];
+ }
+ };
+
+ Iterator begin() const
+ {
+ return {*this, 0};
+ }
+
+ Iterator end() const
+ {
+ return {*this, size_in_bits_};
+ }
+
+ MutableIterator begin()
+ {
+ return {*this, 0};
+ }
+
+ MutableIterator end()
+ {
+ return {*this, size_in_bits_};
+ }
+
+ /**
+ * Change the size of the vector. If the new vector is larger than the old one, the new elements
+ * are filled with #value.
+ */
+ void resize(const int64_t new_size_in_bits, const bool value = false)
+ {
+ BLI_assert(new_size_in_bits >= 0);
+ const int64_t old_size_in_bits = size_in_bits_;
+ if (new_size_in_bits > old_size_in_bits) {
+ this->reserve(new_size_in_bits);
+ }
+ size_in_bits_ = new_size_in_bits;
+ if (old_size_in_bits < new_size_in_bits) {
+ this->fill_range(IndexRange(old_size_in_bits, new_size_in_bits - old_size_in_bits), value);
+ }
+ }
+
+ /**
+ * Set #value for every element in #range.
+ */
+ void fill_range(const IndexRange range, const bool value)
+ {
+ const AlignedIndexRanges aligned_ranges = split_index_range_by_alignment(range, BitsPerByte);
+
+ /* Fill first few bits. */
+ for (const int64_t i : aligned_ranges.prefix) {
+ (*this)[i].set(value);
+ }
+
+ /* Fill entire bytes at once. */
+ const int64_t start_fill_byte_index = aligned_ranges.aligned.start() / BitsPerByte;
+ const int64_t bytes_to_fill = aligned_ranges.aligned.size() / BitsPerByte;
+ const uint8_t fill_value = value ? (uint8_t)0xff : (uint8_t)0x00;
+ initialized_fill_n(data_ + start_fill_byte_index, bytes_to_fill, fill_value);
+
+ /* Fill bits in the end that don't cover a full byte. */
+ for (const int64_t i : aligned_ranges.suffix) {
+ (*this)[i].set(value);
+ }
+ }
+
+ /**
+ * Set #value on every element.
+ */
+ void fill(const bool value)
+ {
+ this->fill_range(IndexRange(0, size_in_bits_), value);
+ }
+
+ /**
+ * Make sure that the capacity of the vector is large enough to hold the given amount of bits.
+ * The actual size is not changed.
+ */
+ void reserve(const int new_capacity_in_bits)
+ {
+ this->realloc_to_at_least(new_capacity_in_bits);
+ }
+
+ private:
+ void ensure_space_for_one()
+ {
+ if (UNLIKELY(size_in_bits_ >= capacity_in_bits_)) {
+ this->realloc_to_at_least(size_in_bits_ + 1);
+ }
+ }
+
+ BLI_NOINLINE void realloc_to_at_least(const int64_t min_capacity_in_bits,
+ const uint8_t initial_value_for_new_bytes = 0x00)
+ {
+ if (capacity_in_bits_ >= min_capacity_in_bits) {
+ return;
+ }
+
+ const int64_t min_capacity_in_bytes = this->required_bytes_for_bits(min_capacity_in_bits);
+
+ /* At least double the size of the previous allocation. */
+ const int64_t min_new_capacity_in_bytes = capacity_in_bits_ * 2;
+
+ const int64_t new_capacity_in_bytes = std::max(min_capacity_in_bytes,
+ min_new_capacity_in_bytes);
+ const int64_t bytes_to_copy = this->used_bytes_amount();
+
+ uint8_t *new_data = static_cast<uint8_t *>(
+ allocator_.allocate(new_capacity_in_bytes, AllocationAlignment, __func__));
+ uninitialized_copy_n(data_, bytes_to_copy, new_data);
+ /* Always initialize new capacity even if it isn't used yet. That's necessary to avoid warnings
+ * caused by using uninitialized memory. This happens when e.g. setting a clearing a bit in an
+ * uninitialized byte. */
+ uninitialized_fill_n(new_data + bytes_to_copy,
+ new_capacity_in_bytes - bytes_to_copy,
+ (uint8_t)initial_value_for_new_bytes);
+
+ if (!this->is_inline()) {
+ allocator_.deallocate(data_);
+ }
+
+ data_ = new_data;
+ capacity_in_bits_ = new_capacity_in_bytes * BitsPerByte;
+ }
+
+ bool is_inline() const
+ {
+ return data_ == inline_buffer_;
+ }
+
+ int64_t used_bytes_amount() const
+ {
+ return this->required_bytes_for_bits(size_in_bits_);
+ }
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_bitmap.h b/source/blender/blenlib/BLI_bitmap.h
index 19d8525311c..973cc5c3d1e 100644
--- a/source/blender/blenlib/BLI_bitmap.h
+++ b/source/blender/blenlib/BLI_bitmap.h
@@ -27,7 +27,7 @@ typedef unsigned int BLI_bitmap;
/**
* Number of blocks needed to hold '_num' bits.
*/
-#define _BITMAP_NUM_BLOCKS(_num) (((_num) >> _BITMAP_POWER) + 1)
+#define _BITMAP_NUM_BLOCKS(_num) (((_num) + _BITMAP_MASK) >> _BITMAP_POWER)
/**
* Size (in bytes) used to hold '_num' bits.
@@ -54,6 +54,11 @@ typedef unsigned int BLI_bitmap;
((BLI_bitmap *)BLI_memarena_calloc(_mem, BLI_BITMAP_SIZE(_num))))
/**
+ * Declares a bitmap as a variable.
+ */
+#define BLI_BITMAP_DECLARE(_name, _num) BLI_bitmap _name[_BITMAP_NUM_BLOCKS(_num)] = {}
+
+/**
* Get the value of a single bit at '_index'.
*/
#define BLI_BITMAP_TEST(_bitmap, _index) \
@@ -137,6 +142,12 @@ void BLI_bitmap_and_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits);
*/
void BLI_bitmap_or_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits);
+/**
+ * Find index of the lowest unset bit.
+ * Returns -1 if all the bits are set.
+ */
+int BLI_bitmap_find_first_unset(const BLI_bitmap *bitmap, size_t bits);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenlib/BLI_compute_context.hh b/source/blender/blenlib/BLI_compute_context.hh
new file mode 100644
index 00000000000..e3e5b6f9e85
--- /dev/null
+++ b/source/blender/blenlib/BLI_compute_context.hh
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * When logging computed values, we generally want to know where the value was computed. For
+ * example, geometry nodes logs socket values so that they can be displayed in the ui. For that we
+ * can combine the logged value with a `ComputeContext`, which identifies the place where the value
+ * was computed.
+ *
+ * This is not a trivial problem because e.g. just storing a pointer to the socket a value
+ * belongs to is not enough. That's because the same socket may correspond to many different values
+ * when the socket is used in a node group that is used multiple times. In this case, not only does
+ * the socket have to be stored but also the entire nested node group path that led to the
+ * evaluation of the socket.
+ *
+ * Storing the entire "context path" for every logged value is not feasible, because that path can
+ * become quite long. So that would need much more memory, more compute overhead and makes it
+ * complicated to compare if two contexts are the same. If the identifier for a compute context
+ * would have a variable size, it would also be much harder to create a map from context to values.
+ *
+ * The solution implemented below uses the following key ideas:
+ * - Every compute context can be hashed to a unique fixed size value (`ComputeContextHash`). While
+ * technically there could be hash collisions, the hashing algorithm has to be chosen to make
+ * that practically impossible. This way an entire context path, possibly consisting of many
+ * nested contexts, is represented by a single value that can be stored easily.
+ * - A nested compute context is build as singly linked list, where every compute context has a
+ * pointer to the parent compute context. Note that a link in the other direction is not possible
+ * because the same parent compute context may be used by many different children which possibly
+ * run on different threads.
+ */
+
+#include "BLI_array.hh"
+#include "BLI_linear_allocator.hh"
+#include "BLI_stack.hh"
+#include "BLI_string_ref.hh"
+
+namespace blender {
+
+/**
+ * A hash that uniquely identifies a specific (non-fixed-size) compute context. The hash has to
+ * have enough bits to make collisions practically impossible.
+ */
+struct ComputeContextHash {
+ static constexpr int64_t HashSizeInBytes = 16;
+ uint64_t v1 = 0;
+ uint64_t v2 = 0;
+
+ uint64_t hash() const
+ {
+ return v1;
+ }
+
+ friend bool operator==(const ComputeContextHash &a, const ComputeContextHash &b)
+ {
+ return a.v1 == b.v1 && a.v2 == b.v2;
+ }
+
+ void mix_in(const void *data, int64_t len);
+
+ friend std::ostream &operator<<(std::ostream &stream, const ComputeContextHash &hash);
+};
+
+static_assert(sizeof(ComputeContextHash) == ComputeContextHash::HashSizeInBytes);
+
+/**
+ * Identifies the context in which a computation happens. This context can be used to identify
+ * values logged during the computation. For more details, see the comment at the top of the file.
+ *
+ * This class should be subclassed to implement specific contexts.
+ */
+class ComputeContext {
+ private:
+ /**
+ * Only used for debugging currently.
+ */
+ const char *static_type_;
+ /**
+ * Pointer to the context that this context is child of. That allows nesting compute contexts.
+ */
+ const ComputeContext *parent_ = nullptr;
+
+ protected:
+ /**
+ * The hash that uniquely identifies this context. It's a combined hash of this context as well
+ * as all the parent contexts.
+ */
+ ComputeContextHash hash_;
+
+ public:
+ ComputeContext(const char *static_type, const ComputeContext *parent)
+ : static_type_(static_type), parent_(parent)
+ {
+ if (parent != nullptr) {
+ hash_ = parent_->hash_;
+ }
+ }
+ virtual ~ComputeContext() = default;
+
+ const ComputeContextHash &hash() const
+ {
+ return hash_;
+ }
+
+ const char *static_type() const
+ {
+ return static_type_;
+ }
+
+ const ComputeContext *parent() const
+ {
+ return parent_;
+ }
+
+ /**
+ * Print the entire nested context stack.
+ */
+ void print_stack(std::ostream &stream, StringRef name) const;
+
+ /**
+ * Print information about this specific context. This has to be implemented by each subclass.
+ */
+ virtual void print_current_in_line(std::ostream &stream) const = 0;
+
+ friend std::ostream &operator<<(std::ostream &stream, const ComputeContext &compute_context);
+};
+
+/**
+ * Utility class to build a context stack in one place. This is typically used to get the hash that
+ * corresponds to a specific nested compute context, in order to look up corresponding logged
+ * values.
+ */
+class ComputeContextBuilder {
+ private:
+ LinearAllocator<> allocator_;
+ Stack<destruct_ptr<ComputeContext>> contexts_;
+
+ public:
+ bool is_empty() const
+ {
+ return contexts_.is_empty();
+ }
+
+ const ComputeContext *current() const
+ {
+ if (contexts_.is_empty()) {
+ return nullptr;
+ }
+ return contexts_.peek().get();
+ }
+
+ const ComputeContextHash hash() const
+ {
+ BLI_assert(!contexts_.is_empty());
+ return this->current()->hash();
+ }
+
+ template<typename T, typename... Args> void push(Args &&...args)
+ {
+ const ComputeContext *current = this->current();
+ destruct_ptr<T> context = allocator_.construct<T>(current, std::forward<Args>(args)...);
+ contexts_.push(std::move(context));
+ }
+
+ void pop()
+ {
+ contexts_.pop();
+ }
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_cpp_type.hh b/source/blender/blenlib/BLI_cpp_type.hh
index cc48b456da7..8cf5ead1c7b 100644
--- a/source/blender/blenlib/BLI_cpp_type.hh
+++ b/source/blender/blenlib/BLI_cpp_type.hh
@@ -20,7 +20,7 @@
* cost of longer compile time, a larger binary and the complexity that comes from using
* templates).
* - If the code is not performance sensitive, it usually makes sense to use #CPPType instead.
- * - Sometimes a combination can make sense. Optimized code can be be generated at compile-time for
+ * - Sometimes a combination can make sense. Optimized code can be generated at compile-time for
* some types, while there is a fallback code path using #CPPType for all other types.
* #CPPType::to_static_type allows dispatching between both versions based on the type.
*
diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h
index 063e60ecf03..0ff75ca16e5 100644
--- a/source/blender/blenlib/BLI_fileops.h
+++ b/source/blender/blenlib/BLI_fileops.h
@@ -101,6 +101,7 @@ typedef enum eFileAttributes {
FILE_ATTR_MOUNT_POINT = 1 << 14, /* Volume mounted as a folder. */
FILE_ATTR_HARDLINK = 1 << 15, /* Duplicated directory entry. */
} eFileAttributes;
+ENUM_OPERATORS(eFileAttributes, FILE_ATTR_HARDLINK);
#define FILE_ATTR_ANY_LINK \
(FILE_ATTR_ALIAS | FILE_ATTR_REPARSE_POINT | FILE_ATTR_SYMLINK | FILE_ATTR_JUNCTION_POINT | \
diff --git a/source/blender/blenlib/BLI_float3x3.hh b/source/blender/blenlib/BLI_float3x3.hh
index 6a9e7dd04f0..178973c155d 100644
--- a/source/blender/blenlib/BLI_float3x3.hh
+++ b/source/blender/blenlib/BLI_float3x3.hh
@@ -63,6 +63,15 @@ struct float3x3 {
return result;
}
+ static float3x3 from_scale(const float2 scale)
+ {
+ float3x3 result = zero();
+ result.values[0][0] = scale.x;
+ result.values[1][1] = scale.y;
+ result.values[2][2] = 1.0f;
+ return result;
+ }
+
static float3x3 from_translation_rotation_scale(const float2 translation,
float rotation,
const float2 scale)
@@ -190,6 +199,13 @@ struct float3x3 {
return result;
}
+ float2 scale_2d() const
+ {
+ float2 scale;
+ mat3_to_size_2d(scale, values);
+ return scale;
+ }
+
friend bool operator==(const float3x3 &a, const float3x3 &b)
{
return equals_m3m3(a.values, b.values);
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index 64e6e68432f..ca0d9ea0028 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -266,7 +266,7 @@ struct float4x4 {
for (int j = 0; j < 4; j++) {
snprintf(fchar, sizeof(fchar), "%11.6f", mat[j][i]);
stream << fchar;
- if (i != 3) {
+ if (j != 3) {
stream << ", ";
}
}
diff --git a/source/blender/blenlib/BLI_function_ref.hh b/source/blender/blenlib/BLI_function_ref.hh
index 5f18e994991..9a38176c988 100644
--- a/source/blender/blenlib/BLI_function_ref.hh
+++ b/source/blender/blenlib/BLI_function_ref.hh
@@ -63,7 +63,6 @@
*
* void some_function(FunctionRef<int()> f);
* some_function([]() { return 0; });
- *
*/
#include "BLI_memory_utils.hh"
diff --git a/source/blender/blenlib/BLI_generic_span.hh b/source/blender/blenlib/BLI_generic_span.hh
index 143ab235d2e..e7a08988c46 100644
--- a/source/blender/blenlib/BLI_generic_span.hh
+++ b/source/blender/blenlib/BLI_generic_span.hh
@@ -100,6 +100,34 @@ class GSpan {
{
return this->slice(range.start(), range.size());
}
+
+ GSpan drop_front(const int64_t n) const
+ {
+ BLI_assert(n >= 0);
+ const int64_t new_size = std::max<int64_t>(0, size_ - n);
+ return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * n), new_size);
+ }
+
+ GSpan drop_back(const int64_t n) const
+ {
+ BLI_assert(n >= 0);
+ const int64_t new_size = std::max<int64_t>(0, size_ - n);
+ return GSpan(*type_, data_, new_size);
+ }
+
+ GSpan take_front(const int64_t n) const
+ {
+ BLI_assert(n >= 0);
+ const int64_t new_size = std::min<int64_t>(size_, n);
+ return GSpan(*type_, data_, new_size);
+ }
+
+ GSpan take_back(const int64_t n) const
+ {
+ BLI_assert(n >= 0);
+ const int64_t new_size = std::min<int64_t>(size_, n);
+ return GSpan(*type_, POINTER_OFFSET(data_, type_->size() * (size_ - new_size)), new_size);
+ }
};
/**
@@ -199,6 +227,35 @@ class GMutableSpan {
return this->slice(range.start(), range.size());
}
+ GMutableSpan drop_front(const int64_t n) const
+ {
+ BLI_assert(n >= 0);
+ const int64_t new_size = std::max<int64_t>(0, size_ - n);
+ return GMutableSpan(*type_, POINTER_OFFSET(data_, type_->size() * n), new_size);
+ }
+
+ GMutableSpan drop_back(const int64_t n) const
+ {
+ BLI_assert(n >= 0);
+ const int64_t new_size = std::max<int64_t>(0, size_ - n);
+ return GMutableSpan(*type_, data_, new_size);
+ }
+
+ GMutableSpan take_front(const int64_t n) const
+ {
+ BLI_assert(n >= 0);
+ const int64_t new_size = std::min<int64_t>(size_, n);
+ return GMutableSpan(*type_, data_, new_size);
+ }
+
+ GMutableSpan take_back(const int64_t n) const
+ {
+ BLI_assert(n >= 0);
+ const int64_t new_size = std::min<int64_t>(size_, n);
+ return GMutableSpan(
+ *type_, POINTER_OFFSET(data_, type_->size() * (size_ - new_size)), new_size);
+ }
+
/**
* Copy all values from another span into this span. This invokes undefined behavior when the
* destination contains uninitialized data and T is not trivially copy constructible.
diff --git a/source/blender/blenlib/BLI_generic_virtual_array.hh b/source/blender/blenlib/BLI_generic_virtual_array.hh
index 43ca16a894f..21549896f45 100644
--- a/source/blender/blenlib/BLI_generic_virtual_array.hh
+++ b/source/blender/blenlib/BLI_generic_virtual_array.hh
@@ -860,6 +860,7 @@ template<typename T> inline GVArray::GVArray(const VArray<T> &varray)
* #this is destructed. */
if (info.type == CommonVArrayInfo::Type::Span && !info.may_have_ownership) {
*this = GVArray::ForSpan(GSpan(CPPType::get<T>(), info.data, varray.size()));
+ return;
}
if (varray.try_assign_GVArray(*this)) {
return;
diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh
index bd0a7e5bb7a..c259282ca64 100644
--- a/source/blender/blenlib/BLI_index_range.hh
+++ b/source/blender/blenlib/BLI_index_range.hh
@@ -90,10 +90,10 @@ class IndexRange {
return *this;
}
- constexpr Iterator operator++(int) const
+ constexpr Iterator operator++(int)
{
Iterator copied_iterator = *this;
- ++copied_iterator;
+ ++(*this);
return copied_iterator;
}
@@ -140,6 +140,10 @@ class IndexRange {
{
return (a.size_ == b.size_) && (a.start_ == b.start_ || a.size_ == 0);
}
+ constexpr friend bool operator!=(IndexRange a, IndexRange b)
+ {
+ return !(a == b);
+ }
/**
* Get the amount of numbers in the range.
@@ -198,6 +202,16 @@ class IndexRange {
}
/**
+ * Get the element one before the beginning. The returned value is undefined when the range is
+ * empty, and the range must start after zero already.
+ */
+ constexpr int64_t one_before_start() const
+ {
+ BLI_assert(start_ > 0);
+ return start_ - 1;
+ }
+
+ /**
* Get the element one after the end. The returned value is undefined when the range is empty.
*/
constexpr int64_t one_after_last() const
@@ -238,6 +252,19 @@ class IndexRange {
}
/**
+ * Returns a new IndexRange that contains the intersection of the current one with the given
+ * range. Returns empty range if there are no overlapping indices. The returned range is always
+ * a valid slice of this range.
+ */
+ constexpr IndexRange intersect(IndexRange other) const
+ {
+ const int64_t old_end = start_ + size_;
+ const int64_t new_start = std::min(old_end, std::max(start_, other.start_));
+ const int64_t new_end = std::max(new_start, std::min(old_end, other.start_ + other.size_));
+ return IndexRange(new_start, new_end - new_start);
+ }
+
+ /**
* Returns a new IndexRange with n elements removed from the beginning of the range.
* This invokes undefined behavior when n is negative.
*/
@@ -308,4 +335,19 @@ class IndexRange {
Span<int64_t> as_span_internal() const;
};
+struct AlignedIndexRanges {
+ IndexRange prefix;
+ IndexRange aligned;
+ IndexRange suffix;
+};
+
+/**
+ * Split a range into three parts so that the boundaries of the middle part are aligned to some
+ * power of two.
+ *
+ * This can be used when an algorithm can be optimized on aligned indices/memory. The algorithm
+ * then needs a slow path for the beginning and end, and a fast path for the aligned elements.
+ */
+AlignedIndexRanges split_index_range_by_alignment(const IndexRange range, const int64_t alignment);
+
} // namespace blender
diff --git a/source/blender/blenlib/BLI_kdopbvh.h b/source/blender/blenlib/BLI_kdopbvh.h
index 8642d728909..17759b6a8ac 100644
--- a/source/blender/blenlib/BLI_kdopbvh.h
+++ b/source/blender/blenlib/BLI_kdopbvh.h
@@ -332,6 +332,29 @@ extern const float bvhtree_kdop_axes[13][3];
namespace blender {
+using BVHTree_RayCastCallback_CPP =
+ FunctionRef<void(int index, const BVHTreeRay &ray, BVHTreeRayHit &hit)>;
+
+inline void BLI_bvhtree_ray_cast_all_cpp(BVHTree &tree,
+ const float3 co,
+ const float3 dir,
+ float radius,
+ float hit_dist,
+ BVHTree_RayCastCallback_CPP fn)
+{
+ BLI_bvhtree_ray_cast_all(
+ &tree,
+ co,
+ dir,
+ radius,
+ hit_dist,
+ [](void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) {
+ BVHTree_RayCastCallback_CPP fn = *static_cast<BVHTree_RayCastCallback_CPP *>(userdata);
+ fn(index, *ray, *hit);
+ },
+ &fn);
+}
+
using BVHTree_RangeQuery_CPP = FunctionRef<void(int index, const float3 &co, float dist_sq)>;
inline void BLI_bvhtree_range_query_cpp(BVHTree &tree,
diff --git a/source/blender/blenlib/BLI_lazy_threading.hh b/source/blender/blenlib/BLI_lazy_threading.hh
new file mode 100644
index 00000000000..b5a15919c89
--- /dev/null
+++ b/source/blender/blenlib/BLI_lazy_threading.hh
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup bli
+ *
+ * The goal of "lazy threading" is to avoid using threads unless one can reasonably assume that it
+ * is worth distributing work over multiple threads. Using threads can lead to worse overall
+ * performance by introducing inter-thread communication overhead. Keeping all work on a single
+ * thread reduces this overhead to zero and also makes better use of the CPU cache.
+ *
+ * Functions like #parallel_for also solve this to some degree by using a "grain size". When the
+ * number of individual tasks is too small, no multi-threading is used. This works very well when
+ * there are many homogeneous tasks that can be expected to take approximately the same time.
+ *
+ * The situation becomes more difficult when:
+ * - The individual tasks are not homogeneous, i.e. they take different amounts of time to compute.
+ * - It is practically impossible to guess how long each task will take in advance.
+ *
+ * Given those constraints, a single grain size cannot be determined. One could just schedule all
+ * tasks individually but that would create a lot of overhead when the tasks happen to be very
+ * small. While TBB will keep all tasks on a single thread if the other threads are busy, if they
+ * are idle they will start stealing the work even if that's not beneficial for overall
+ * performance.
+ *
+ * This file provides a simple API that allows a task scheduler to properly handle tasks whose size
+ * is not known in advance. The key idea is this:
+ *
+ * > By default, all work stays on a single thread. If an individual task notices that it is about
+ * > start a computation that will take a while, it notifies the task scheduler further up on the
+ * > stack. The scheduler then allows other threads to take over other tasks that were originally
+ * > meant for the current thread.
+ *
+ * This way, when all tasks are small, no threading overhead has to be paid for. Whenever there is
+ * a task that keeps the current thread busy for a while, the other tasks are moved to a separate
+ * thread so that they can be executed without waiting for the long computation to finish.
+ *
+ * Consequently, the earlier a task knows during it execution that it will take a while, the
+ * better. That's because if it is blocking anyway, it's more efficient to move the other tasks to
+ * another thread earlier.
+ *
+ * To make this work, three things have to be solved:
+ * 1. The task scheduler has to be able to start single-threaded and become multi-threaded after
+ * tasks have started executing. This has to be solved in the specific task scheduler.
+ * 2. There has to be a way for the currently running task to tell the task scheduler that it is
+ * about to perform a computation that will take a while and that it would be reasonable to move
+ * other tasks to other threads. This part is implemented in the API provided by this file.
+ * 3. Individual tasks have to decide when a computation is long enough to justify talking to the
+ * scheduler. This is always based on heuristics that have to be fine tuned over time. One could
+ * assume that this means adding new work-size checks to many parts in Blender, but that's
+ * actually not necessary, because these checks exist already in the form of grain sizes passed
+ * to e.g. #parallel_for. The assumption here is that when the task thinks the current work load
+ * is big enough to justify using threads, it's also big enough to justify using another thread
+ * for waiting tasks on the current thread.
+ */
+
+#include "BLI_function_ref.hh"
+
+namespace blender::lazy_threading {
+
+/**
+ * Tell task schedulers on the current thread that it is about to start a long computation
+ * and that other waiting tasks should better be moved to another thread if possible.
+ */
+void send_hint();
+
+/**
+ * Used by the task scheduler to receive hints from current tasks that they will take a while.
+ * This should only be allocated on the stack.
+ */
+class HintReceiver {
+ public:
+ /**
+ * The passed in function is called when a task signals that it will take a while.
+ * \note The function has to stay alive after the call to the constructor. So one must not pass a
+ * lambda directly into this constructor but store it in a separate variable on the stack first.
+ */
+ HintReceiver(FunctionRef<void()> fn);
+ ~HintReceiver();
+};
+
+} // namespace blender::lazy_threading
diff --git a/source/blender/blenlib/BLI_length_parameterize.hh b/source/blender/blenlib/BLI_length_parameterize.hh
index c44bb94f65d..d81bcbe1e7a 100644
--- a/source/blender/blenlib/BLI_length_parameterize.hh
+++ b/source/blender/blenlib/BLI_length_parameterize.hh
@@ -6,10 +6,10 @@
* \ingroup bli
*/
+#include "BLI_index_mask.hh"
#include "BLI_math_base.hh"
#include "BLI_math_color.hh"
#include "BLI_math_vector.hh"
-#include "BLI_vector.hh"
namespace blender::length_parameterize {
@@ -42,26 +42,38 @@ void accumulate_lengths(const Span<T> values, const bool cyclic, MutableSpan<flo
}
template<typename T>
-inline void linear_interpolation(const Span<T> src,
- const Span<int> indices,
- const Span<float> factors,
- MutableSpan<T> dst)
+inline void interpolate_to_masked(const Span<T> src,
+ const Span<int> indices,
+ const Span<float> factors,
+ const IndexMask dst_mask,
+ MutableSpan<T> dst)
{
BLI_assert(indices.size() == factors.size());
- BLI_assert(indices.size() == dst.size());
+ BLI_assert(indices.size() == dst_mask.size());
const int last_src_index = src.size() - 1;
- for (const int i : dst.index_range()) {
- const int prev_index = indices[i];
- const float factor = factors[i];
- const bool is_cyclic_case = prev_index == last_src_index;
- if (is_cyclic_case) {
- dst[i] = math::interpolate(src.last(), src.first(), factor);
+ dst_mask.to_best_mask_type([&](auto dst_mask) {
+ for (const int i : IndexRange(dst_mask.size())) {
+ const int prev_index = indices[i];
+ const float factor = factors[i];
+ const bool is_cyclic_case = prev_index == last_src_index;
+ if (is_cyclic_case) {
+ dst[dst_mask[i]] = math::interpolate(src.last(), src.first(), factor);
+ }
+ else {
+ dst[dst_mask[i]] = math::interpolate(src[prev_index], src[prev_index + 1], factor);
+ }
}
- else {
- dst[i] = math::interpolate(src[prev_index], src[prev_index + 1], factor);
- }
- }
+ });
+}
+
+template<typename T>
+inline void interpolate(const Span<T> src,
+ const Span<int> indices,
+ const Span<float> factors,
+ MutableSpan<T> dst)
+{
+ interpolate_to_masked(src, indices, factors, dst.index_range(), dst);
}
/**
@@ -75,6 +87,7 @@ struct SampleSegmentHint {
/**
* \param accumulated_segment_lengths: Lengths of individual segments added up.
+ * Each value describes the total length at the end of the segment following a point.
* \param sample_length: The position to sample at.
* \param r_segment_index: Returns the index of the segment that #sample_length is in.
* \param r_factor: Returns the position within the segment.
@@ -82,7 +95,7 @@ struct SampleSegmentHint {
* \note #sample_length must not be outside of any segment.
*/
inline void sample_at_length(const Span<float> accumulated_segment_lengths,
- float sample_length,
+ const float sample_length,
int &r_segment_index,
float &r_factor,
SampleSegmentHint *hint = nullptr)
@@ -117,7 +130,7 @@ inline void sample_at_length(const Span<float> accumulated_segment_lengths,
const float segment_start = prev_point_index == 0 ? 0.0f : lengths[prev_point_index - 1];
const float segment_end = lengths[prev_point_index];
const float segment_length = segment_end - segment_start;
- const float segment_length_inv = safe_divide(1.0f, segment_length);
+ const float segment_length_inv = math::safe_divide(1.0f, segment_length);
const float length_in_segment = sample_length - segment_start;
const float factor = length_in_segment * segment_length_inv;
diff --git a/source/blender/blenlib/BLI_listbase.h b/source/blender/blenlib/BLI_listbase.h
index 237fcdae8b9..6a41fce27b3 100644
--- a/source/blender/blenlib/BLI_listbase.h
+++ b/source/blender/blenlib/BLI_listbase.h
@@ -301,32 +301,33 @@ struct LinkData *BLI_genericNodeN(void *data);
*
* \code{.c}
*
- * LISTBASE_CIRCULAR_FORWARD_BEGIN(listbase, item, item_init)
+ * LISTBASE_CIRCULAR_FORWARD_BEGIN(type, listbase, item, item_init)
* {
* ...operate on marker...
* }
- * LISTBASE_CIRCULAR_FORWARD_END (listbase, item, item_init);
+ * LISTBASE_CIRCULAR_FORWARD_END (type, listbase, item, item_init);
*
* \endcode
*/
-#define LISTBASE_CIRCULAR_FORWARD_BEGIN(lb, lb_iter, lb_init) \
- if ((lb)->first && (lb_init || (lb_init = (lb)->first))) { \
- lb_iter = lb_init; \
+#define LISTBASE_CIRCULAR_FORWARD_BEGIN(type, lb, lb_iter, lb_init) \
+ if ((lb)->first && (lb_init || (lb_init = (type)(lb)->first))) { \
+ lb_iter = (type)(lb_init); \
do {
-#define LISTBASE_CIRCULAR_FORWARD_END(lb, lb_iter, lb_init) \
+#define LISTBASE_CIRCULAR_FORWARD_END(type, lb, lb_iter, lb_init) \
} \
- while ((lb_iter = (lb_iter)->next ? (lb_iter)->next : (lb)->first), (lb_iter != lb_init)) \
+ while ((lb_iter = (lb_iter)->next ? (type)(lb_iter)->next : (type)(lb)->first), \
+ (lb_iter != lb_init)) \
; \
} \
((void)0)
-#define LISTBASE_CIRCULAR_BACKWARD_BEGIN(lb, lb_iter, lb_init) \
- if ((lb)->last && (lb_init || (lb_init = (lb)->last))) { \
+#define LISTBASE_CIRCULAR_BACKWARD_BEGIN(type, lb, lb_iter, lb_init) \
+ if ((lb)->last && (lb_init || (lb_init = (type)(lb)->last))) { \
lb_iter = lb_init; \
do {
-#define LISTBASE_CIRCULAR_BACKWARD_END(lb, lb_iter, lb_init) \
+#define LISTBASE_CIRCULAR_BACKWARD_END(type, lb, lb_iter, lb_init) \
} \
- while ((lb_iter = (lb_iter)->prev ? (lb_iter)->prev : (lb)->last), (lb_iter != lb_init)) \
+ while ((lb_iter = (lb_iter)->prev ? (lb_iter)->prev : (type)(lb)->last), (lb_iter != lb_init)) \
; \
} \
((void)0)
diff --git a/source/blender/blenlib/BLI_listbase_wrapper.hh b/source/blender/blenlib/BLI_listbase_wrapper.hh
index 25e029a5616..a32243bc411 100644
--- a/source/blender/blenlib/BLI_listbase_wrapper.hh
+++ b/source/blender/blenlib/BLI_listbase_wrapper.hh
@@ -42,7 +42,7 @@ template<typename T> class ListBaseWrapper {
Iterator &operator++()
{
- /* Some types store next/prev using `void *`, so cast is necessary. */
+ /* Some types store `next/prev` using `void *`, so cast is necessary. */
current_ = static_cast<T *>(current_->next);
return *this;
}
@@ -50,7 +50,7 @@ template<typename T> class ListBaseWrapper {
Iterator operator++(int)
{
Iterator iterator = *this;
- ++*this;
+ ++(*this);
return iterator;
}
diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh
index 55233676ed8..95d1e344894 100644
--- a/source/blender/blenlib/BLI_map.hh
+++ b/source/blender/blenlib/BLI_map.hh
@@ -669,10 +669,10 @@ class Map {
return *this;
}
- BaseIterator operator++(int) const
+ BaseIterator operator++(int)
{
BaseIterator copied_iterator = *this;
- ++copied_iterator;
+ ++(*this);
return copied_iterator;
}
diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h
index 6386a7f76f8..3aa2e35476d 100644
--- a/source/blender/blenlib/BLI_math_color.h
+++ b/source/blender/blenlib/BLI_math_color.h
@@ -164,7 +164,9 @@ void rgba_float_to_uchar(unsigned char r_col[4], const float col_f[4]);
MINLINE float rgb_to_grayscale(const float rgb[3]);
MINLINE unsigned char rgb_to_grayscale_byte(const unsigned char rgb[3]);
-MINLINE int compare_rgb_uchar(const unsigned char a[3], const unsigned char b[3], int limit);
+MINLINE int compare_rgb_uchar(const unsigned char col_a[3],
+ const unsigned char col_b[3],
+ int limit);
/**
* Return triangle noise in [-0.5..1.5] range.
diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h
index 93b413ab755..d056c42e019 100644
--- a/source/blender/blenlib/BLI_math_geom.h
+++ b/source/blender/blenlib/BLI_math_geom.h
@@ -1270,8 +1270,8 @@ MINLINE void mul_sh_fl(float r[9], float f);
MINLINE void add_sh_shsh(float r[9], const float a[9], const float b[9]);
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(const float r[9], const float v[3]);
+MINLINE float eval_shv3(float sh[9], const float v[3]);
+MINLINE float diffuse_shv3(const float sh[9], const float v[3]);
MINLINE void vec_fac_to_sh(float r[9], const float v[3], float f);
MINLINE void madd_sh_shfl(float r[9], const float sh[9], float f);
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 2cd2a299d53..19943614881 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -98,110 +98,110 @@ void mul_m4_m4_post(float R[4][4], const float B[4][4]);
/* Implement #mul_m3_series macro. */
-void _va_mul_m3_series_3(float R[3][3], const float M1[3][3], const float M2[3][3]) ATTR_NONNULL();
-void _va_mul_m3_series_4(float R[3][3],
- const float M1[3][3],
- const float M2[3][3],
- const float M3[3][3]) ATTR_NONNULL();
-void _va_mul_m3_series_5(float R[3][3],
- const float M1[3][3],
- const float M2[3][3],
- const float M3[3][3],
- const float M4[3][3]) ATTR_NONNULL();
-void _va_mul_m3_series_6(float R[3][3],
- const float M1[3][3],
- const float M2[3][3],
- const float M3[3][3],
- const float M4[3][3],
- const float M5[3][3]) ATTR_NONNULL();
-void _va_mul_m3_series_7(float R[3][3],
- const float M1[3][3],
- const float M2[3][3],
- const float M3[3][3],
- const float M4[3][3],
- const float M5[3][3],
- const float M6[3][3]) ATTR_NONNULL();
-void _va_mul_m3_series_8(float R[3][3],
- const float M1[3][3],
- const float M2[3][3],
- const float M3[3][3],
- const float M4[3][3],
- const float M5[3][3],
- const float M6[3][3],
- const float M7[3][3]) ATTR_NONNULL();
-void _va_mul_m3_series_9(float R[3][3],
- const float M1[3][3],
- const float M2[3][3],
- const float M3[3][3],
- const float M4[3][3],
- const float M5[3][3],
- const float M6[3][3],
- const float M7[3][3],
- const float M8[3][3]) ATTR_NONNULL();
+void _va_mul_m3_series_3(float r[3][3], const float m1[3][3], const float m2[3][3]) ATTR_NONNULL();
+void _va_mul_m3_series_4(float r[3][3],
+ const float m1[3][3],
+ const float m2[3][3],
+ const float m3[3][3]) ATTR_NONNULL();
+void _va_mul_m3_series_5(float r[3][3],
+ const float m1[3][3],
+ const float m2[3][3],
+ const float m3[3][3],
+ const float m4[3][3]) ATTR_NONNULL();
+void _va_mul_m3_series_6(float r[3][3],
+ const float m1[3][3],
+ const float m2[3][3],
+ const float m3[3][3],
+ const float m4[3][3],
+ const float m5[3][3]) ATTR_NONNULL();
+void _va_mul_m3_series_7(float r[3][3],
+ const float m1[3][3],
+ const float m2[3][3],
+ const float m3[3][3],
+ const float m4[3][3],
+ const float m5[3][3],
+ const float m6[3][3]) ATTR_NONNULL();
+void _va_mul_m3_series_8(float r[3][3],
+ const float m1[3][3],
+ const float m2[3][3],
+ const float m3[3][3],
+ const float m4[3][3],
+ const float m5[3][3],
+ const float m6[3][3],
+ const float m7[3][3]) ATTR_NONNULL();
+void _va_mul_m3_series_9(float r[3][3],
+ const float m1[3][3],
+ const float m2[3][3],
+ const float m3[3][3],
+ const float m4[3][3],
+ const float m5[3][3],
+ const float m6[3][3],
+ const float m7[3][3],
+ const float m8[3][3]) ATTR_NONNULL();
/* Implement #mul_m4_series macro. */
-void _va_mul_m4_series_3(float R[4][4], const float M1[4][4], const float M2[4][4]) ATTR_NONNULL();
-void _va_mul_m4_series_4(float R[4][4],
- const float M1[4][4],
- const float M2[4][4],
- const float M3[4][4]) ATTR_NONNULL();
-void _va_mul_m4_series_5(float R[4][4],
- const float M1[4][4],
- const float M2[4][4],
- const float M3[4][4],
- const float M4[4][4]) ATTR_NONNULL();
-void _va_mul_m4_series_6(float R[4][4],
- const float M1[4][4],
- const float M2[4][4],
- const float M3[4][4],
- const float M4[4][4],
- const float M5[4][4]) ATTR_NONNULL();
-void _va_mul_m4_series_7(float R[4][4],
- const float M1[4][4],
- const float M2[4][4],
- const float M3[4][4],
- const float M4[4][4],
- const float M5[4][4],
- const float M6[4][4]) ATTR_NONNULL();
-void _va_mul_m4_series_8(float R[4][4],
- const float M1[4][4],
- const float M2[4][4],
- const float M3[4][4],
- const float M4[4][4],
- const float M5[4][4],
- const float M6[4][4],
- const float M7[4][4]) ATTR_NONNULL();
-void _va_mul_m4_series_9(float R[4][4],
- const float M1[4][4],
- const float M2[4][4],
- const float M3[4][4],
- const float M4[4][4],
- const float M5[4][4],
- const float M6[4][4],
- const float M7[4][4],
- const float M8[4][4]) ATTR_NONNULL();
+void _va_mul_m4_series_3(float r[4][4], const float m1[4][4], const float m2[4][4]) ATTR_NONNULL();
+void _va_mul_m4_series_4(float r[4][4],
+ const float m1[4][4],
+ const float m2[4][4],
+ const float m3[4][4]) ATTR_NONNULL();
+void _va_mul_m4_series_5(float r[4][4],
+ const float m1[4][4],
+ const float m2[4][4],
+ const float m3[4][4],
+ const float m4[4][4]) ATTR_NONNULL();
+void _va_mul_m4_series_6(float r[4][4],
+ const float m1[4][4],
+ const float m2[4][4],
+ const float m3[4][4],
+ const float m4[4][4],
+ const float m5[4][4]) ATTR_NONNULL();
+void _va_mul_m4_series_7(float r[4][4],
+ const float m1[4][4],
+ const float m2[4][4],
+ const float m3[4][4],
+ const float m4[4][4],
+ const float m5[4][4],
+ const float m6[4][4]) ATTR_NONNULL();
+void _va_mul_m4_series_8(float r[4][4],
+ const float m1[4][4],
+ const float m2[4][4],
+ const float m3[4][4],
+ const float m4[4][4],
+ const float m5[4][4],
+ const float m6[4][4],
+ const float m7[4][4]) ATTR_NONNULL();
+void _va_mul_m4_series_9(float r[4][4],
+ const float m1[4][4],
+ const float m2[4][4],
+ const float m3[4][4],
+ const float m4[4][4],
+ const float m5[4][4],
+ const float m6[4][4],
+ const float m7[4][4],
+ const float m8[4][4]) ATTR_NONNULL();
#define mul_m3_series(...) VA_NARGS_CALL_OVERLOAD(_va_mul_m3_series_, __VA_ARGS__)
#define mul_m4_series(...) VA_NARGS_CALL_OVERLOAD(_va_mul_m4_series_, __VA_ARGS__)
void mul_m4_v3(const float M[4][4], float r[3]);
-void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3]);
+void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3]);
void mul_v3_m4v3_db(double r[3], const double mat[4][4], const double vec[3]);
void mul_v4_m4v3_db(double r[4], const double mat[4][4], const double vec[3]);
-void mul_v2_m4v3(float r[2], const float M[4][4], const float v[3]);
-void mul_v2_m2v2(float r[2], const float M[2][2], const float v[2]);
-void mul_m2_v2(const float M[2][2], float v[2]);
+void mul_v2_m4v3(float r[2], const float mat[4][4], const float vec[3]);
+void mul_v2_m2v2(float r[2], const float mat[2][2], const float vec[2]);
+void mul_m2_v2(const float mat[2][2], float vec[2]);
/** Same as #mul_m4_v3() but doesn't apply translation component. */
-void mul_mat3_m4_v3(const float M[4][4], float r[3]);
-void mul_v3_mat3_m4v3(float r[3], const float M[4][4], const float v[3]);
-void mul_v3_mat3_m4v3_db(double r[3], const double M[4][4], const double v[3]);
-void mul_m4_v4(const float M[4][4], float r[4]);
-void mul_v4_m4v4(float r[4], const float M[4][4], const float v[4]);
+void mul_mat3_m4_v3(const float mat[4][4], float r[3]);
+void mul_v3_mat3_m4v3(float r[3], const float mat[4][4], const float vec[3]);
+void mul_v3_mat3_m4v3_db(double r[3], const double mat[4][4], const double vec[3]);
+void mul_m4_v4(const float mat[4][4], float r[4]);
+void mul_v4_m4v4(float r[4], const float mat[4][4], const float v[4]);
void mul_v4_m4v3(float r[4], const float M[4][4], const float v[3]); /* v has implicit w = 1.0f */
-void mul_project_m4_v3(const float M[4][4], float vec[3]);
+void mul_project_m4_v3(const float mat[4][4], float vec[3]);
void mul_v3_project_m4_v3(float r[3], const float mat[4][4], const float vec[3]);
-void mul_v2_project_m4_v3(float r[2], const float M[4][4], const float vec[3]);
+void mul_v2_project_m4_v3(float r[2], const float mat[4][4], const float vec[3]);
void mul_m3_v2(const float m[3][3], float r[2]);
void mul_v2_m3v2(float r[2], const float m[3][3], const float v[2]);
@@ -234,13 +234,14 @@ void negate_m3(float R[3][3]);
void negate_mat3_m4(float R[4][4]);
void negate_m4(float R[4][4]);
-bool invert_m3_ex(float m[3][3], float epsilon);
-bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], float epsilon);
+bool invert_m3_ex(float mat[3][3], float epsilon);
+bool invert_m3_m3_ex(float inverse[3][3], const float mat[3][3], float epsilon);
-bool invert_m3(float R[3][3]);
-bool invert_m3_m3(float R[3][3], const float A[3][3]);
-bool invert_m4(float R[4][4]);
-bool invert_m4_m4(float R[4][4], const float A[4][4]);
+bool invert_m3(float mat[3][3]);
+bool invert_m2_m2(float inverse[2][2], const float mat[2][2]);
+bool invert_m3_m3(float inverse[3][3], const float mat[3][3]);
+bool invert_m4(float mat[4][4]);
+bool invert_m4_m4(float inverse[4][4], const float mat[4][4]);
/**
* Computes the inverse of mat and puts it in inverse.
* Uses Gaussian Elimination with partial (maximal column) pivoting.
@@ -251,12 +252,12 @@ bool invert_m4_m4(float R[4][4], const float A[4][4]);
* for non-invertible scale matrices, finding a partial solution can
* be useful to have a valid local transform center, see T57767.
*/
-bool invert_m4_m4_fallback(float R[4][4], const float A[4][4]);
+bool invert_m4_m4_fallback(float inverse[4][4], const float mat[4][4]);
/* Double arithmetic (mixed float/double). */
-void mul_m4_v4d(const float M[4][4], double r[4]);
-void mul_v4d_m4v4d(double r[4], const float M[4][4], const double v[4]);
+void mul_m4_v4d(const float mat[4][4], double r[4]);
+void mul_v4d_m4v4d(double r[4], const float mat[4][4], const double v[4]);
/* Double matrix functions (no mixing types). */
@@ -290,8 +291,8 @@ void normalize_m3_m3_ex(float R[3][3], const float M[3][3], float r_scale[3]) AT
void normalize_m3_m3(float R[3][3], const float M[3][3]) ATTR_NONNULL();
void normalize_m4_ex(float R[4][4], float r_scale[3]) ATTR_NONNULL();
void normalize_m4(float R[4][4]) ATTR_NONNULL();
-void normalize_m4_m4_ex(float R[4][4], const float M[4][4], float r_scale[3]) ATTR_NONNULL();
-void normalize_m4_m4(float R[4][4], const float M[4][4]) ATTR_NONNULL();
+void normalize_m4_m4_ex(float rmat[4][4], const float mat[4][4], float r_scale[3]) ATTR_NONNULL();
+void normalize_m4_m4(float rmat[4][4], const float mat[4][4]) ATTR_NONNULL();
/**
* Make an orthonormal matrix around the selected axis of the given matrix.
@@ -325,15 +326,15 @@ 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], float unit_length);
-bool orthogonalize_m4_zero_axes(float R[4][4], float unit_length);
+bool orthogonalize_m3_zero_axes(float m[3][3], float unit_length);
+bool orthogonalize_m4_zero_axes(float m[4][4], 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]);
-bool is_orthonormal_m4(const float mat[4][4]);
+bool is_orthogonal_m3(const float m[3][3]);
+bool is_orthogonal_m4(const float m[4][4]);
+bool is_orthonormal_m3(const float m[3][3]);
+bool is_orthonormal_m4(const float m[4][4]);
-bool is_uniform_scaled_m3(const float mat[3][3]);
+bool is_uniform_scaled_m3(const float m[3][3]);
bool is_uniform_scaled_m4(const float m[4][4]);
/* NOTE: 'adjoint' here means the adjugate (adjunct, "classical adjoint") matrix!
@@ -361,22 +362,22 @@ float determinant_m4(const float m[4][4]);
* From this decomposition it is trivial to compute the (pseudo-inverse)
* of `A` as `Ainv = V.Winv.transpose(U)`.
*/
-void svd_m4(float U[4][4], float s[4], float V[4][4], float A[4][4]);
-void pseudoinverse_m4_m4(float Ainv[4][4], const float A[4][4], float epsilon);
-void pseudoinverse_m3_m3(float Ainv[3][3], const float A[3][3], float epsilon);
+void svd_m4(float U[4][4], float s[4], float V[4][4], float A_[4][4]);
+void pseudoinverse_m4_m4(float inverse[4][4], const float mat[4][4], float epsilon);
+void pseudoinverse_m3_m3(float inverse[3][3], const float mat[3][3], float epsilon);
bool has_zero_axis_m4(const float matrix[4][4]);
-void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4]);
+void invert_m4_m4_safe(float inverse[4][4], const float mat[4][4]);
-void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3]);
+void invert_m3_m3_safe_ortho(float inverse[3][3], const float mat[3][3]);
/**
* A safe version of invert that uses valid axes, calculating the zero'd axis
* based on the non-zero ones.
*
* This works well for transformation matrices, when a single axis is zeroed.
*/
-void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]);
+void invert_m4_m4_safe_ortho(float inverse[4][4], const float mat[4][4]);
/** \} */
@@ -393,22 +394,24 @@ void scale_m4_v2(float R[4][4], const float scale[2]);
* For an orthogonal matrix, it is the product of all three scale values.
* Returns a negative value if the transform is flipped by negative scale.
*/
-float mat3_to_volume_scale(const float M[3][3]);
-float mat4_to_volume_scale(const float M[4][4]);
+float mat3_to_volume_scale(const float mat[3][3]);
+float mat4_to_volume_scale(const float mat[4][4]);
/**
* This gets the average scale of a matrix, only use when your scaling
* data that has no idea of scale axis, examples are bone-envelope-radius
* and curve radius.
*/
-float mat3_to_scale(const float M[3][3]);
-float mat4_to_scale(const float M[4][4]);
+float mat3_to_scale(const float mat[3][3]);
+float mat4_to_scale(const float mat[4][4]);
/** Return 2D scale (in XY plane) of given mat4. */
-float mat4_to_xy_scale(const float M[4][4]);
+float mat4_to_xy_scale(const float mat[4][4]);
void size_to_mat3(float R[3][3], const float size[3]);
void size_to_mat4(float R[4][4], const float size[3]);
+/** Return 2D size assuming the given matrix is a 2D affine matrix. */
+void mat3_to_size_2d(float size[2], const float M[3][3]);
void mat3_to_size(float size[3], const float M[3][3]);
void mat4_to_size(float size[3], const float M[4][4]);
@@ -432,7 +435,7 @@ float mat4_to_size_max_axis(const float M[4][4]);
*/
void mat4_to_size_fix_shear(float size[3], const float M[4][4]);
-void translate_m4(float mat[4][4], float tx, float ty, float tz);
+void translate_m4(float mat[4][4], float Tx, float Ty, float Tz);
/**
* Rotate a matrix in-place.
*
@@ -453,8 +456,20 @@ void rescale_m4(float mat[4][4], const float scale[3]);
*/
void transform_pivot_set_m4(float mat[4][4], const float pivot[3]);
+/**
+ * \param rot: A 3x3 rotation matrix, normalized never negative.
+ */
void mat4_to_rot(float rot[3][3], const float wmat[4][4]);
+
+/**
+ * \param rot: A 3x3 rotation matrix, normalized never negative.
+ * \param size: The scale, negative if `mat3` is negative.
+ */
void mat3_to_rot_size(float rot[3][3], float size[3], const float mat3[3][3]);
+/**
+ * \param rot: A 3x3 rotation matrix, normalized never negative.
+ * \param size: The scale, negative if `mat3` is negative.
+ */
void mat4_to_loc_rot_size(float loc[3], float rot[3][3], float size[3], const float wmat[4][4]);
void mat4_to_loc_quat(float loc[3], float quat[4], const float wmat[4][4]);
void mat4_decompose(float loc[3], float quat[4], float size[3], const float wmat[4][4]);
@@ -527,7 +542,18 @@ void interp_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3], flo
*/
void interp_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4], float t);
+/**
+ * Return true when the matrices determinant is less than zero.
+ *
+ * \note This is often used to check if a matrix flips content in 3D space,
+ * where transforming geometry (for example) would flip the direction of polygon normals
+ * from pointing outside a closed volume, to pointing inside (or the reverse).
+ *
+ * When the matrix is constructed from location, rotation & scale
+ * as matrix will be negative when it has an odd number of negative scales.
+ */
bool is_negative_m3(const float mat[3][3]);
+/** A version of #is_negative_m3 that takes a 4x4 matrix. */
bool is_negative_m4(const float mat[4][4]);
bool is_zero_m3(const float mat[3][3]);
@@ -604,8 +630,8 @@ void BLI_space_transform_invert_normal(const struct SpaceTransform *data, float
/** \name Other
* \{ */
-void print_m3(const char *str, const float M[3][3]);
-void print_m4(const char *str, const float M[4][4]);
+void print_m3(const char *str, const float m[3][3]);
+void print_m4(const char *str, const float m[4][4]);
#define print_m3_id(M) print_m3(STRINGIFY(M), M)
#define print_m4_id(M) print_m4(STRINGIFY(M), M)
diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h
index 192ad482a69..7fb7085360b 100644
--- a/source/blender/blenlib/BLI_math_rotation.h
+++ b/source/blender/blenlib/BLI_math_rotation.h
@@ -71,7 +71,7 @@ void mul_qt_fl(float q[4], float f);
/**
* Raise a unit quaternion to the specified power.
*/
-void pow_qt_fl_normalized(float q[4], float f);
+void pow_qt_fl_normalized(float q[4], float fac);
void sub_qt_qtqt(float q[4], const float a[4], const float b[4]);
@@ -109,8 +109,8 @@ void add_qt_qtqt(float q[4], const float a[4], const float b[4], float t);
/* Conversion. */
-void quat_to_mat3(float mat[3][3], const float q[4]);
-void quat_to_mat4(float mat[4][4], const float q[4]);
+void quat_to_mat3(float m[3][3], const float q[4]);
+void quat_to_mat4(float m[4][4], const float q[4]);
/**
* Apply the rotation of \a a to \a q keeping the values compatible with \a old.
@@ -118,6 +118,11 @@ void quat_to_mat4(float mat[4][4], const float q[4]);
*/
void quat_to_compatible_quat(float q[4], const float a[4], const float old[4]);
+/**
+ * A version of #mat3_normalized_to_quat that skips error checking.
+ */
+void mat3_normalized_to_quat_fast(float q[4], const float mat[3][3]);
+
void mat3_normalized_to_quat(float q[4], const float mat[3][3]);
void mat4_normalized_to_quat(float q[4], const float mat[4][4]);
void mat3_to_quat(float q[4], const float mat[3][3]);
@@ -157,7 +162,10 @@ void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q
* \param r_twist: if not NULL, receives the twist quaternion.
* \returns twist angle.
*/
-float quat_split_swing_and_twist(const float q[4], int axis, float r_swing[4], float r_twist[4]);
+float quat_split_swing_and_twist(const float q_in[4],
+ int axis,
+ float r_swing[4],
+ float r_twist[4]);
float angle_normalized_qt(const float q[4]);
float angle_normalized_qtqt(const float q1[4], const float q2[4]);
@@ -170,12 +178,32 @@ float angle_signed_qt(const float q[4]);
float angle_signed_qtqt(const float q1[4], const float q2[4]);
/**
- * TODO: don't what this is, but it's not the same as #mat3_to_quat.
+ * Legacy matrix to quaternion conversion, keep to prevent changes to existing
+ * boids & particle-system behavior. Use #mat3_to_quat for new code.
*/
-void mat3_to_quat_is_ok(float q[4], const float mat[3][3]);
+void mat3_to_quat_legacy(float q[4], const float wmat[3][3]);
/* Other. */
+/**
+ * Utility that performs `sinf` & `cosf` intended for plotting a 2D circle,
+ * where the values of the coordinates with are exactly symmetrical although this
+ * favors even numbers as odd numbers can only be symmetrical on a single axis.
+ *
+ * Besides adjustments to precision, this function is the equivalent of:
+ * \code {.c}
+ * float phi = (2 * M_PI) * (float)i / (float)denominator;
+ * *r_sin = sinf(phi);
+ * *r_cos = cosf(phi);
+ * \endcode
+ *
+ * \param numerator: An integer factor in [0..denominator] (inclusive).
+ * \param denominator: The fraction denominator (typically the number of segments of the circle).
+ * \param r_sin: The resulting sine.
+ * \param r_cos: The resulting cosine.
+ */
+void sin_cos_from_fraction(int numerator, int denominator, float *r_sin, float *r_cos);
+
void print_qt(const char *str, const float q[4]);
#define print_qt_id(q) print_qt(STRINGIFY(q), q)
@@ -216,16 +244,16 @@ void axis_angle_to_mat4(float R[4][4], const float axis[3], float angle);
/**
* 3x3 matrix to axis angle.
*/
-void mat3_normalized_to_axis_angle(float axis[3], float *angle, const float M[3][3]);
+void mat3_normalized_to_axis_angle(float axis[3], float *angle, const float mat[3][3]);
/**
* 4x4 matrix to axis angle.
*/
-void mat4_normalized_to_axis_angle(float axis[3], float *angle, const float M[4][4]);
-void mat3_to_axis_angle(float axis[3], float *angle, const float M[3][3]);
+void mat4_normalized_to_axis_angle(float axis[3], float *angle, const float mat[4][4]);
+void mat3_to_axis_angle(float axis[3], float *angle, const float mat[3][3]);
/**
* 4x4 matrix to axis angle.
*/
-void mat4_to_axis_angle(float axis[3], float *angle, const float M[4][4]);
+void mat4_to_axis_angle(float axis[3], float *angle, const float mat[4][4]);
/**
* Quaternions to Axis Angle.
*/
@@ -264,19 +292,19 @@ void eul_to_mat3(float mat[3][3], const float eul[3]);
void eul_to_mat4(float mat[4][4], const float eul[3]);
void mat3_normalized_to_eul(float eul[3], const float mat[3][3]);
-void mat4_normalized_to_eul(float eul[3], const float mat[4][4]);
+void mat4_normalized_to_eul(float eul[3], const float m[4][4]);
void mat3_to_eul(float eul[3], const float mat[3][3]);
void mat4_to_eul(float eul[3], const float mat[4][4]);
void quat_to_eul(float eul[3], const float quat[4]);
-void mat3_normalized_to_compatible_eul(float eul[3], const float old[3], float mat[3][3]);
-void mat3_to_compatible_eul(float eul[3], const float old[3], float mat[3][3]);
+void mat3_normalized_to_compatible_eul(float eul[3], const float oldrot[3], float mat[3][3]);
+void mat3_to_compatible_eul(float eul[3], const float oldrot[3], float mat[3][3]);
void quat_to_compatible_eul(float eul[3], const float oldrot[3], const float quat[4]);
-void rotate_eul(float eul[3], char axis, float angle);
+void rotate_eul(float beul[3], char axis, float angle);
/* Order independent. */
-void compatible_eul(float eul[3], const float old[3]);
+void compatible_eul(float eul[3], const float oldrot[3]);
void add_eul_euleul(float r_eul[3], float a[3], float b[3], short order);
void sub_eul_euleul(float r_eul[3], float a[3], float b[3], short order);
@@ -304,15 +332,15 @@ typedef enum eEulerRotationOrders {
/**
* Construct quaternion from Euler angles (in radians).
*/
-void eulO_to_quat(float quat[4], const float eul[3], short order);
+void eulO_to_quat(float q[4], const float e[3], short order);
/**
* Construct 3x3 matrix from Euler angles (in radians).
*/
-void eulO_to_mat3(float mat[3][3], const float eul[3], short order);
+void eulO_to_mat3(float M[3][3], const float e[3], short order);
/**
* Construct 4x4 matrix from Euler angles (in radians).
*/
-void eulO_to_mat4(float mat[4][4], const float eul[3], short order);
+void eulO_to_mat4(float mat[4][4], const float e[3], short order);
/**
* Euler Rotation to Axis Angle.
*/
@@ -325,17 +353,17 @@ void eulO_to_gimbal_axis(float gmat[3][3], const float eul[3], short order);
/**
* Convert 3x3 matrix to Euler angles (in radians).
*/
-void mat3_normalized_to_eulO(float eul[3], short order, const float mat[3][3]);
+void mat3_normalized_to_eulO(float eul[3], short order, const float m[3][3]);
/**
* Convert 4x4 matrix to Euler angles (in radians).
*/
-void mat4_normalized_to_eulO(float eul[3], short order, const float mat[4][4]);
-void mat3_to_eulO(float eul[3], short order, const float mat[3][3]);
-void mat4_to_eulO(float eul[3], short order, const float mat[4][4]);
+void mat4_normalized_to_eulO(float eul[3], short order, const float m[4][4]);
+void mat3_to_eulO(float eul[3], short order, const float m[3][3]);
+void mat4_to_eulO(float eul[3], short order, const float m[4][4]);
/**
* Convert quaternion to Euler angles (in radians).
*/
-void quat_to_eulO(float eul[3], short order, const float quat[4]);
+void quat_to_eulO(float e[3], short order, const float q[4]);
/**
* Axis Angle to Euler Rotation.
*/
@@ -344,18 +372,27 @@ void axis_angle_to_eulO(float eul[3], short order, const float axis[3], float an
/* Uses 2 methods to retrieve eulers, and picks the closest. */
void mat3_normalized_to_compatible_eulO(float eul[3],
- const float old[3],
+ const float oldrot[3],
short order,
const float mat[3][3]);
void mat4_normalized_to_compatible_eulO(float eul[3],
- const float old[3],
+ const float oldrot[3],
short order,
const float mat[4][4]);
-void mat3_to_compatible_eulO(float eul[3], const float old[3], short order, const float mat[3][3]);
-void mat4_to_compatible_eulO(float eul[3], const float old[3], short order, const float mat[4][4]);
-void quat_to_compatible_eulO(float eul[3], const float old[3], short order, const float quat[4]);
-
-void rotate_eulO(float eul[3], short order, char axis, float angle);
+void mat3_to_compatible_eulO(float eul[3],
+ const float oldrot[3],
+ short order,
+ const float mat[3][3]);
+void mat4_to_compatible_eulO(float eul[3],
+ const float oldrot[3],
+ short order,
+ const float mat[4][4]);
+void quat_to_compatible_eulO(float eul[3],
+ const float oldrot[3],
+ short order,
+ const float quat[4]);
+
+void rotate_eulO(float beul[3], short order, char axis, float angle);
/** \} */
@@ -364,7 +401,7 @@ void rotate_eulO(float eul[3], short order, char axis, float angle);
* \{ */
void copy_dq_dq(DualQuat *r, const DualQuat *dq);
-void normalize_dq(DualQuat *dq, float totw);
+void normalize_dq(DualQuat *dq, float totweight);
void add_weighted_dq_dq(DualQuat *dq_sum, const DualQuat *dq, float weight);
void mul_v3m3_dq(float r[3], float R[3][3], DualQuat *dq);
@@ -381,7 +418,7 @@ void vec_apply_track(float vec[3], short axis);
* Lens/angle conversion (radians).
*/
float focallength_to_fov(float focal_length, float sensor);
-float fov_to_focallength(float fov, float sensor);
+float fov_to_focallength(float hfov, float sensor);
float angle_wrap_rad(float angle);
float angle_wrap_deg(float angle);
diff --git a/source/blender/blenlib/BLI_math_rotation.hh b/source/blender/blenlib/BLI_math_rotation.hh
index e8b746b34df..50a062162ad 100644
--- a/source/blender/blenlib/BLI_math_rotation.hh
+++ b/source/blender/blenlib/BLI_math_rotation.hh
@@ -15,4 +15,13 @@ namespace blender::math {
*/
float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis, float angle);
+/**
+ * Rotate any arbitrary \a vector around the \a center position, with a unit-length \a axis
+ * and the specified \a angle.
+ */
+float3 rotate_around_axis(const float3 &vector,
+ const float3 &center,
+ const float3 &axis,
+ float angle);
+
} // namespace blender::math
diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h
index 0b178064a4c..17fe25ec67b 100644
--- a/source/blender/blenlib/BLI_math_vector.h
+++ b/source/blender/blenlib/BLI_math_vector.h
@@ -155,7 +155,7 @@ MINLINE void mul_v3_v3db_db(double r[3], const double a[3], double f);
MINLINE void mul_v2_v2(float r[2], const float a[2]);
MINLINE void mul_v2_v2v2(float r[2], const float a[2], const float b[2]);
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_v3_v3v3(float r[3], const float v1[3], const float v2[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[4], const float a[4], float f);
@@ -271,10 +271,10 @@ MINLINE float len_squared_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_manhattan_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE int len_manhattan_v2_int(const int v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_manhattan_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT;
-MINLINE float len_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE double len_v2_db(const double v[2]) ATTR_WARN_UNUSED_RESULT;
-MINLINE float len_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT;
-MINLINE double len_v2v2_db(const double a[2], const double b[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE double len_v2v2_db(const double v1[2], const double v2[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE float len_v2v2_int(const int v1[2], const int v2[2]);
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE double len_squared_v2v2_db(const double a[2], const double b[2]) ATTR_WARN_UNUSED_RESULT;
@@ -288,22 +288,22 @@ MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESU
MINLINE double len_v3_db(const double a[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE double len_squared_v3_db(const double v[3]) ATTR_WARN_UNUSED_RESULT;
-MINLINE float normalize_v2_length(float r[2], float unit_scale);
+MINLINE float normalize_v2_length(float n[2], float unit_length);
/**
* \note any vectors containing `nan` will be zeroed out.
*/
-MINLINE float normalize_v2_v2_length(float r[2], const float a[2], float unit_scale);
-MINLINE float normalize_v3_length(float r[3], float unit_scale);
+MINLINE float normalize_v2_v2_length(float r[2], const float a[2], float unit_length);
+MINLINE float normalize_v3_length(float n[3], float unit_length);
/**
* \note any vectors containing `nan` will be zeroed out.
*/
-MINLINE float normalize_v3_v3_length(float r[3], const float a[3], float unit_scale);
-MINLINE double normalize_v3_length_db(double n[3], double unit_scale);
-MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], double unit_scale);
+MINLINE float normalize_v3_v3_length(float r[3], const float a[3], float unit_length);
+MINLINE double normalize_v3_length_db(double n[3], double unit_length);
+MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], double unit_length);
-MINLINE float normalize_v2(float r[2]);
+MINLINE float normalize_v2(float n[2]);
MINLINE float normalize_v2_v2(float r[2], const float a[2]);
-MINLINE float normalize_v3(float r[3]);
+MINLINE float normalize_v3(float n[3]);
MINLINE float normalize_v3_v3(float r[3], const float a[3]);
MINLINE double normalize_v3_v3_db(double r[3], const double a[3]);
MINLINE double normalize_v3_db(double n[3]);
@@ -424,45 +424,51 @@ void flip_v2_v2v2(float v[2], const float v1[2], const float v2[2]);
/** \name Comparison
* \{ */
-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;
+MINLINE bool is_zero_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_zero_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_zero_v4(const float v[4]) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool is_zero_v2_db(const double a[2]) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool is_zero_v3_db(const double a[3]) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool is_zero_v4_db(const double a[4]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_zero_v2_db(const double v[2]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_zero_v3_db(const double v[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_zero_v4_db(const double v[4]) 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;
+bool is_finite_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT;
+bool is_finite_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT;
+bool is_finite_v4(const float v[4]) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool is_one_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool is_one_v3(const float v[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool equals_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool equals_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool equals_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool equals_v4v4(const float v1[4], const float v2[4]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool equals_v3v3_int(const int v1[3], const int v2[3]) ATTR_WARN_UNUSED_RESULT;
MINLINE bool equals_v4v4_int(const int v1[4], const int v2[4]) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool compare_v2v2(const float a[2], const float b[2], float limit) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool compare_v3v3(const float a[3], const float b[3], float limit) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool compare_v4v4(const float a[4], const float b[4], float limit) ATTR_WARN_UNUSED_RESULT;
-
-MINLINE bool compare_v2v2_relative(const float a[2], const float b[2], float limit, int max_ulps)
+MINLINE bool compare_v2v2(const float v1[2],
+ const float v2[2],
+ float limit) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool compare_v3v3(const float v1[3],
+ const float v2[3],
+ float limit) ATTR_WARN_UNUSED_RESULT;
+MINLINE bool compare_v4v4(const float v1[4],
+ const float v2[4],
+ float limit) ATTR_WARN_UNUSED_RESULT;
+
+MINLINE bool compare_v2v2_relative(const float v1[2], const float v2[2], float limit, int max_ulps)
ATTR_WARN_UNUSED_RESULT;
-MINLINE bool compare_v3v3_relative(const float a[3], const float b[3], float limit, int max_ulps)
+MINLINE bool compare_v3v3_relative(const float v1[3], const float v2[3], float limit, int max_ulps)
ATTR_WARN_UNUSED_RESULT;
-MINLINE bool compare_v4v4_relative(const float a[4], const float b[4], float limit, int max_ulps)
+MINLINE bool compare_v4v4_relative(const float v1[4], const float v2[4], float limit, int max_ulps)
ATTR_WARN_UNUSED_RESULT;
-MINLINE bool compare_len_v3v3(const float a[3],
- const float b[3],
+MINLINE bool compare_len_v3v3(const float v1[3],
+ const float v2[3],
float limit) ATTR_WARN_UNUSED_RESULT;
-MINLINE bool compare_size_v3v3(const float a[3],
- const float b[3],
+MINLINE bool compare_size_v3v3(const float v1[3],
+ const float v2[3],
float limit) ATTR_WARN_UNUSED_RESULT;
/**
@@ -606,8 +612,8 @@ void project_v3_plane(float out[3], const float plane_no[3], const float plane_c
* out: result (negate for a 'bounce').
* </pre>
*/
-void reflect_v3_v3v3(float out[3], const float vec[3], const float normal[3]);
-void reflect_v3_v3v3_db(double out[3], const double vec[3], const double normal[3]);
+void reflect_v3_v3v3(float out[3], const float v[3], const float normal[3]);
+void reflect_v3_v3v3_db(double out[3], const double v[3], const double normal[3]);
/**
* Takes a vector and computes 2 orthogonal directions.
*
@@ -655,10 +661,10 @@ void print_vn(const char *str, const float v[], int n);
#define print_v4_id(v) print_v4(STRINGIFY(v), v)
#define print_vn_id(v, n) print_vn(STRINGIFY(v), v, n)
-MINLINE void normal_float_to_short_v2(short r[2], const float n[2]);
-MINLINE void normal_short_to_float_v3(float r[3], const short n[3]);
-MINLINE void normal_float_to_short_v3(short r[3], const float n[3]);
-MINLINE void normal_float_to_short_v4(short r[4], const float n[4]);
+MINLINE void normal_float_to_short_v2(short out[2], const float in[2]);
+MINLINE void normal_short_to_float_v3(float out[3], const short in[3]);
+MINLINE void normal_float_to_short_v3(short out[3], const float in[3]);
+MINLINE void normal_float_to_short_v4(short out[4], const float in[4]);
void minmax_v4v4_v4(float min[4], float max[4], const float vec[4]);
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3]);
diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh
index 39b73deb276..7d504c18b4b 100644
--- a/source/blender/blenlib/BLI_memory_utils.hh
+++ b/source/blender/blenlib/BLI_memory_utils.hh
@@ -195,51 +195,51 @@ template<typename T> void initialized_move_n(T *src, int64_t n, T *dst)
}
/**
- * Move n values from src to dst starting with the last value.
+ * Move n values from src to dst.
*
* Exception Safety: Basic.
*
* Before:
* src: initialized
- * dst: initialized
+ * dst: uninitialized
* After:
* src: initialized, moved-from
* dst: initialized
*/
-template<typename T> void initialized_reversed_move_n(T *src, int64_t n, T *dst)
+template<typename T> void uninitialized_move_n(T *src, int64_t n, T *dst)
{
BLI_assert(n >= 0);
- for (int64_t i = n - 1; i >= 0; i--) {
- dst[i] = std::move(src[i]);
+ int64_t current = 0;
+ try {
+ for (; current < n; current++) {
+ new (static_cast<void *>(dst + current)) T(std::move(src[current]));
+ }
+ }
+ catch (...) {
+ destruct_n(dst, current);
+ throw;
}
}
/**
- * Move n values from src to dst.
+ * Move n values from src to dst starting with the last value.
*
* Exception Safety: Basic.
*
* Before:
* src: initialized
- * dst: uninitialized
+ * dst: initialized
* After:
* src: initialized, moved-from
* dst: initialized
*/
-template<typename T> void uninitialized_move_n(T *src, int64_t n, T *dst)
+template<typename T> void initialized_reversed_move_n(T *src, int64_t n, T *dst)
{
BLI_assert(n >= 0);
- int64_t current = 0;
- try {
- for (; current < n; current++) {
- new (static_cast<void *>(dst + current)) T(std::move(src[current]));
- }
- }
- catch (...) {
- destruct_n(dst, current);
- throw;
+ for (int64_t i = n - 1; i >= 0; i--) {
+ dst[i] = std::move(src[i]);
}
}
@@ -354,8 +354,7 @@ template<typename T> using destruct_ptr = std::unique_ptr<T, DestructValueAtAddr
* not be initialized by the default constructor.
*/
template<size_t Size, size_t Alignment> class AlignedBuffer {
- struct Empty {
- };
+ struct Empty {};
struct alignas(Alignment) Sized {
/* Don't create an empty array. This causes problems with some compilers. */
std::byte buffer_[Size > 0 ? Size : 1];
@@ -485,8 +484,7 @@ class alignas(ReservedAlignment) DynamicStackBuffer {
* This can be used by container constructors. A parameter of this type should be used to indicate
* that the constructor does not construct the elements.
*/
-class NoInitialization {
-};
+class NoInitialization {};
/**
* This can be used to mark a constructor of an object that does not throw exceptions. Other
@@ -494,8 +492,7 @@ class NoInitialization {
* With this, the destructor of the object will be called, even when the remaining constructor
* throws.
*/
-class NoExceptConstructor {
-};
+class NoExceptConstructor {};
/**
* Helper variable that checks if a pointer type can be converted into another pointer type without
diff --git a/source/blender/blenlib/BLI_multi_value_map.hh b/source/blender/blenlib/BLI_multi_value_map.hh
index 4b6300c09aa..81b536e7d3c 100644
--- a/source/blender/blenlib/BLI_multi_value_map.hh
+++ b/source/blender/blenlib/BLI_multi_value_map.hh
@@ -115,6 +115,14 @@ template<typename Key, typename Value> class MultiValueMap {
}
/**
+ * Get the number of keys.
+ */
+ int64_t size() const
+ {
+ return map_.size();
+ }
+
+ /**
* NOTE: This signature will change when the implementation changes.
*/
typename MapType::ItemIterator items() const
@@ -137,6 +145,11 @@ template<typename Key, typename Value> class MultiValueMap {
{
return map_.values();
}
+
+ void clear()
+ {
+ map_.clear();
+ }
};
} // namespace blender
diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h
index 06dd9ab0db9..1e45e76afe1 100644
--- a/source/blender/blenlib/BLI_path_util.h
+++ b/source/blender/blenlib/BLI_path_util.h
@@ -36,16 +36,6 @@ void BLI_setenv_if_new(const char *env, const char *val) ATTR_NONNULL(1);
const char *BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
- * Returns in `string` the concatenation of `dir` and `file` (also with `relabase` on the
- * front if specified and `dir` begins with "//"). Normalizes all occurrences of path
- * separators, including ensuring there is exactly one between the copies of `dir` and `file`,
- * and between the copies of `relabase` and `dir`.
- *
- * \param relabase: Optional prefix to substitute for "//" on front of `dir`.
- * \param string: Area to return result.
- */
-void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file);
-/**
* Ensures that the parent directory of `name` exists.
*
* \return true on success (i.e. given path now exists on file-system), false otherwise.
@@ -94,10 +84,18 @@ void BLI_join_dirfile(char *__restrict dst,
* Join multiple strings into a path, ensuring only a single path separator between each,
* and trailing slash is kept.
*
+ * \param path: The first patch which has special treatment,
+ * allowing `//` prefix which is kept intact unlike double-slashes which are stripped
+ * from the bounds of all other paths passed in.
+ * Passing in the following paths all result in the same output (`//a/b/c`):
+ * - `"//", "a", "b", "c"`.
+ * - `"//", "/a/", "/b/", "/c"`.
+ * - `"//a", "b/c"`.
+ *
* \note If you want a trailing slash, add `SEP_STR` as the last path argument,
* duplicate slashes will be cleaned up.
*/
-size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path_first, ...)
+size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path, ...)
ATTR_NONNULL(1, 3) ATTR_SENTINEL(0);
/**
* Like Python's `os.path.basename()`
@@ -108,10 +106,10 @@ size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path_firs
const char *BLI_path_basename(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT;
/**
* Get an element of the path at an index, eg:
- * "/some/path/file.txt" where an index of:
- * - 0 or -3: "some"
- * - 1 or -2: "path"
- * - 2 or -1: "file.txt"
+ * `/some/path/file.txt` where an index of:
+ * - 0 or -3: `some`
+ * - 1 or -2: `path`
+ * - 2 or -1: `file.txt`
*
* Ignores multiple slashes at any point in the path (including start/end).
*/
@@ -367,8 +365,8 @@ void BLI_path_normalize_unc(char *path_16, int maxlen);
/**
* Appends a suffix to the string, fitting it before the extension
*
- * string = Foo.png, suffix = 123, separator = _
- * Foo.png -> Foo_123.png
+ * string = `Foo.png`, suffix = `123`, separator = `_`.
+ * `Foo.png` -> `Foo_123.png`.
*
* \param string: original (and final) string
* \param maxlen: Maximum length of string
diff --git a/source/blender/blenlib/BLI_pool.hh b/source/blender/blenlib/BLI_pool.hh
new file mode 100644
index 00000000000..8745c019db5
--- /dev/null
+++ b/source/blender/blenlib/BLI_pool.hh
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bli
+ *
+ * A `blender::Pool` allows fast allocation and deallocation of many elements of the same type.
+ *
+ * It is compatible with types that are not movable.
+ *
+ * Freed elements memory will be reused by next allocations.
+ * Elements are allocated in chunks to reduce memory fragmentation and avoid reallocation.
+ */
+
+#pragma once
+
+#include "BLI_stack.hh"
+#include "BLI_utility_mixins.hh"
+#include "BLI_vector.hh"
+
+namespace blender {
+
+template<typename T, int64_t ChunkLen = 64> class Pool : NonCopyable {
+ private:
+ using Chunk = TypedBuffer<T, ChunkLen>;
+
+ /** Allocated item buffer. */
+ Vector<std::unique_ptr<Chunk>> values_;
+ /** List of freed elements to be use for the next allocations. A Stack is best here to avoid
+ * overhead when growing the free list. It also offers better cache performance than a queue
+ * since last added entries will be reused first. */
+ Stack<T *, 0> free_list_;
+
+ public:
+ ~Pool()
+ {
+ /* All elements need to be freed before freeing the pool. */
+ BLI_assert(this->size() == 0);
+ }
+
+ /**
+ * Construct an object inside this pool's memory.
+ */
+ template<typename... ForwardT> T &construct(ForwardT &&...value)
+ {
+ if (free_list_.is_empty()) {
+ values_.append(std::make_unique<Chunk>());
+ T *chunk_start = values_.last()->ptr();
+ for (auto i : IndexRange(ChunkLen)) {
+ free_list_.push(chunk_start + i);
+ }
+ }
+ T *ptr = free_list_.pop();
+ new (ptr) T(std::forward<ForwardT>(value)...);
+ return *ptr;
+ }
+
+ /**
+ * Destroy the given element inside this memory pool. Memory will be reused by next element
+ * construction. This invokes undefined behavior if the item is not from this pool.
+ */
+ void destruct(T &value)
+ {
+ value.~T();
+ free_list_.push(&value);
+ }
+
+ /**
+ * Return the number of constructed elements in this pool.
+ */
+ int64_t size() const
+ {
+ return values_.size() * ChunkLen - free_list_.size();
+ }
+
+ /**
+ * Returns true when the pool contains no elements, otherwise false.
+ */
+ bool is_empty() const
+ {
+ return this->size() == 0;
+ }
+};
+
+} // namespace blender
diff --git a/source/blender/blenlib/BLI_probing_strategies.hh b/source/blender/blenlib/BLI_probing_strategies.hh
index c6152e4d03d..2c001270495 100644
--- a/source/blender/blenlib/BLI_probing_strategies.hh
+++ b/source/blender/blenlib/BLI_probing_strategies.hh
@@ -2,6 +2,8 @@
#pragma once
+#include <numeric>
+
/** \file
* \ingroup bli
*
@@ -20,7 +22,7 @@
* clustering issues. However, more linear steps can also make things slower when the initial hash
* produces many collisions.
*
- * Every probing strategy has to guarantee, that every possible uint64_t is returned eventually.
+ * Every probing strategy has to guarantee that every possible uint64_t is returned eventually.
* This is necessary for correctness. If this is not the case, empty slots might not be found.
*
* The SLOT_PROBING_BEGIN and SLOT_PROBING_END macros can be used to implement a loop that iterates
@@ -69,7 +71,7 @@ class LinearProbingStrategy {
int64_t linear_steps() const
{
- return UINT32_MAX;
+ return std::numeric_limits<int64_t>::max();
}
};
diff --git a/source/blender/blenlib/BLI_scanfill.h b/source/blender/blenlib/BLI_scanfill.h
index 04ac7cb05e4..b5b100ac27d 100644
--- a/source/blender/blenlib/BLI_scanfill.h
+++ b/source/blender/blenlib/BLI_scanfill.h
@@ -82,7 +82,7 @@ struct ScanFillEdge *BLI_scanfill_edge_add(ScanFillContext *sf_ctx,
struct ScanFillVert *v2);
enum {
- /* NOTE(campbell): using BLI_SCANFILL_CALC_REMOVE_DOUBLES
+ /* NOTE(@campbellbarton): using #BLI_SCANFILL_CALC_REMOVE_DOUBLES
* Assumes ordered edges, otherwise we risk an eternal loop
* removing double verts. */
BLI_SCANFILL_CALC_REMOVE_DOUBLES = (1 << 1),
diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh
index bd91c522d06..e23d7d20d0b 100644
--- a/source/blender/blenlib/BLI_serialize.hh
+++ b/source/blender/blenlib/BLI_serialize.hh
@@ -55,7 +55,6 @@
*
* To add a new formatter a new sub-class of `Formatter` must be created and the
* `serialize`/`deserialize` methods should be implemented.
- *
*/
#include <ostream>
@@ -110,7 +109,6 @@ using ArrayValue = ContainerValue<Vector<std::shared_ptr<Value>>, eValueType::Ar
* - `DoubleValue`: contains a double precision floating point number.
* - `DictionaryValue`: represents an object (key value pairs where keys are strings and values can
* be of different types.
- *
*/
class Value {
private:
diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh
index 62de4b79e41..a1b6ad9754e 100644
--- a/source/blender/blenlib/BLI_set.hh
+++ b/source/blender/blenlib/BLI_set.hh
@@ -427,10 +427,10 @@ class Set {
return *this;
}
- Iterator operator++(int) const
+ Iterator operator++(int)
{
Iterator copied_iterator = *this;
- ++copied_iterator;
+ ++(*this);
return copied_iterator;
}
diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h
index 4c5cc3fd9c5..61a21fd8bbf 100644
--- a/source/blender/blenlib/BLI_string_utf8.h
+++ b/source/blender/blenlib/BLI_string_utf8.h
@@ -157,8 +157,8 @@ size_t BLI_strnlen_utf8(const char *strc, size_t maxlen) ATTR_NONNULL(1) ATTR_WA
size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst,
const wchar_t *__restrict src,
size_t maxncpy) ATTR_NONNULL(1, 2);
-size_t BLI_strncpy_wchar_from_utf8(wchar_t *__restrict dst,
- const char *__restrict src,
+size_t BLI_strncpy_wchar_from_utf8(wchar_t *__restrict dst_w,
+ const char *__restrict src_c,
size_t maxncpy) ATTR_NONNULL(1, 2);
/**
@@ -174,17 +174,17 @@ int BLI_str_utf8_char_width_safe(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NON
size_t BLI_str_partition_utf8(const char *str,
const unsigned int delim[],
- const char **sep,
- const char **suf) ATTR_NONNULL(1, 2, 3, 4);
+ const char **r_sep,
+ const char **r_suf) ATTR_NONNULL(1, 2, 3, 4);
size_t BLI_str_rpartition_utf8(const char *str,
const unsigned int delim[],
- const char **sep,
- const char **suf) ATTR_NONNULL(1, 2, 3, 4);
+ const char **r_sep,
+ const char **r_suf) ATTR_NONNULL(1, 2, 3, 4);
size_t BLI_str_partition_ex_utf8(const char *str,
const char *end,
const unsigned int delim[],
- const char **sep,
- const char **suf,
+ const char **r_sep,
+ const char **r_suf,
bool from_right) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 3, 4, 5);
int BLI_str_utf8_offset_to_index(const char *str, int offset) ATTR_WARN_UNUSED_RESULT
diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh
index 904dea66f7a..9f9a57be634 100644
--- a/source/blender/blenlib/BLI_task.hh
+++ b/source/blender/blenlib/BLI_task.hh
@@ -31,6 +31,7 @@
#endif
#include "BLI_index_range.hh"
+#include "BLI_lazy_threading.hh"
#include "BLI_utildefines.h"
namespace blender::threading {
@@ -56,6 +57,7 @@ void parallel_for(IndexRange range, int64_t grain_size, const Function &function
#ifdef WITH_TBB
/* Invoking tbb for small workloads has a large overhead. */
if (range.size() >= grain_size) {
+ lazy_threading::send_hint();
tbb::parallel_for(
tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size),
[&](const tbb::blocked_range<int64_t> &subrange) {
@@ -78,6 +80,7 @@ Value parallel_reduce(IndexRange range,
{
#ifdef WITH_TBB
if (range.size() >= grain_size) {
+ lazy_threading::send_hint();
return tbb::parallel_reduce(
tbb::blocked_range<int64_t>(range.first(), range.one_after_last(), grain_size),
identity,
@@ -105,6 +108,23 @@ template<typename... Functions> void parallel_invoke(Functions &&...functions)
#endif
}
+/**
+ * Same #parallel_invoke, but allows disabling threading dynamically. This is useful because when
+ * the individual functions do very little work, there is a lot of overhead from starting parallel
+ * tasks.
+ */
+template<typename... Functions>
+void parallel_invoke(const bool use_threading, Functions &&...functions)
+{
+ if (use_threading) {
+ lazy_threading::send_hint();
+ parallel_invoke(std::forward<Functions>(functions)...);
+ }
+ else {
+ (functions(), ...);
+ }
+}
+
/** See #BLI_task_isolate for a description of what isolating a task means. */
template<typename Function> void isolate_task(const Function &function)
{
diff --git a/source/blender/blenlib/BLI_utildefines.h b/source/blender/blenlib/BLI_utildefines.h
index 7f9470a9111..9f68795a4a2 100644
--- a/source/blender/blenlib/BLI_utildefines.h
+++ b/source/blender/blenlib/BLI_utildefines.h
@@ -589,7 +589,7 @@ extern "C" {
/** Performs `offsetof(typeof(data), member) + sizeof((data)->member)` for non-gcc compilers. */
#define OFFSETOF_STRUCT_AFTER(_struct, _member) \
- ((((const char *)&((_struct)->_member)) - ((const char *)(_struct))) + \
+ ((size_t)(((const char *)&((_struct)->_member)) - ((const char *)(_struct))) + \
sizeof((_struct)->_member))
/**
diff --git a/source/blender/blenlib/BLI_vector_adaptor.hh b/source/blender/blenlib/BLI_vector_adaptor.hh
deleted file mode 100644
index 82d731aacd8..00000000000
--- a/source/blender/blenlib/BLI_vector_adaptor.hh
+++ /dev/null
@@ -1,88 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-/** \file
- * \ingroup bli
- *
- * A `blender::VectorAdaptor` is a container with a fixed maximum size and does not own the
- * underlying memory. When an adaptor is constructed, you have to provide it with an uninitialized
- * array that will be filled when elements are added to the vector. The vector adaptor is not able
- * to grow. Therefore, it is undefined behavior to add more elements than fit into the provided
- * buffer.
- */
-
-#include "BLI_span.hh"
-
-namespace blender {
-
-template<typename T> class VectorAdaptor {
- private:
- T *begin_;
- T *end_;
- T *capacity_end_;
-
- public:
- VectorAdaptor() : begin_(nullptr), end_(nullptr), capacity_end_(nullptr)
- {
- }
-
- VectorAdaptor(T *data, int64_t capacity, int64_t size = 0)
- : begin_(data), end_(data + size), capacity_end_(data + capacity)
- {
- }
-
- VectorAdaptor(MutableSpan<T> span) : VectorAdaptor(span.data(), span.size(), 0)
- {
- }
-
- void append(const T &value)
- {
- BLI_assert(end_ < capacity_end_);
- new (end_) T(value);
- end_++;
- }
-
- void append(T &&value)
- {
- BLI_assert(end_ < capacity_end_);
- new (end_) T(std::move(value));
- end_++;
- }
-
- void append_n_times(const T &value, int64_t n)
- {
- BLI_assert(end_ + n <= capacity_end_);
- uninitialized_fill_n(end_, n, value);
- end_ += n;
- }
-
- void extend(Span<T> values)
- {
- BLI_assert(end_ + values.size() <= capacity_end_);
- uninitialized_copy_n(values.data(), values.size(), end_);
- end_ += values.size();
- }
-
- int64_t capacity() const
- {
- return capacity_end_ - begin_;
- }
-
- int64_t size() const
- {
- return end_ - begin_;
- }
-
- bool is_empty() const
- {
- return begin_ == end_;
- }
-
- bool is_full() const
- {
- return end_ == capacity_end_;
- }
-};
-
-} // namespace blender
diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh
index b0a3696f245..1a42e776d3d 100644
--- a/source/blender/blenlib/BLI_vector_set.hh
+++ b/source/blender/blenlib/BLI_vector_set.hh
@@ -358,7 +358,7 @@ class VectorSet {
}
/**
- * Return the location of the key in the vector. It is assumed, that the key is in the vector
+ * Return the location of the key in the vector. It is assumed that the key is in the vector
* set. If this is not necessarily the case, use `index_of_try`.
*/
int64_t index_of(const Key &key) const
diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh
index 438fcc4b8f7..19ee2334bd9 100644
--- a/source/blender/blenlib/BLI_virtual_array.hh
+++ b/source/blender/blenlib/BLI_virtual_array.hh
@@ -23,6 +23,8 @@
* see of the increased compile time and binary size is worth it.
*/
+#include <optional>
+
#include "BLI_any.hh"
#include "BLI_array.hh"
#include "BLI_index_mask.hh"
@@ -106,25 +108,7 @@ template<typename T> class VArrayImpl {
*/
virtual void materialize(IndexMask mask, MutableSpan<T> r_span) const
{
- T *dst = r_span.data();
- /* Optimize for a few different common cases. */
- const CommonVArrayInfo info = this->common_info();
- switch (info.type) {
- case CommonVArrayInfo::Type::Any: {
- mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); });
- break;
- }
- case CommonVArrayInfo::Type::Span: {
- const T *src = static_cast<const T *>(info.data);
- mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; });
- break;
- }
- case CommonVArrayInfo::Type::Single: {
- const T single = *static_cast<const T *>(info.data);
- mask.foreach_index([&](const int64_t i) { dst[i] = single; });
- break;
- }
- }
+ mask.foreach_index([&](const int64_t i) { r_span[i] = this->get(i); });
}
/**
@@ -133,24 +117,7 @@ template<typename T> class VArrayImpl {
virtual void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const
{
T *dst = r_span.data();
- /* Optimize for a few different common cases. */
- const CommonVArrayInfo info = this->common_info();
- switch (info.type) {
- case CommonVArrayInfo::Type::Any: {
- mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); });
- break;
- }
- case CommonVArrayInfo::Type::Span: {
- const T *src = static_cast<const T *>(info.data);
- mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); });
- break;
- }
- case CommonVArrayInfo::Type::Single: {
- const T single = *static_cast<const T *>(info.data);
- mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); });
- break;
- }
- }
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); });
}
/**
@@ -286,8 +253,20 @@ template<typename T> class VArrayImpl_For_Span : public VMutableArrayImpl<T> {
return data_ == static_cast<const T *>(other_info.data);
}
+ void materialize(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ mask.foreach_index([&](const int64_t i) { r_span[i] = data_[i]; });
+ }
+
+ void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ T *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(data_[i]); });
+ }
+
void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override
{
+ BLI_assert(mask.size() == r_span.size());
mask.to_best_mask_type([&](auto best_mask) {
for (const int64_t i : IndexRange(best_mask.size())) {
r_span[i] = data_[best_mask[i]];
@@ -298,6 +277,7 @@ template<typename T> class VArrayImpl_For_Span : public VMutableArrayImpl<T> {
void materialize_compressed_to_uninitialized(IndexMask mask,
MutableSpan<T> r_span) const override
{
+ BLI_assert(mask.size() == r_span.size());
T *dst = r_span.data();
mask.to_best_mask_type([&](auto best_mask) {
for (const int64_t i : IndexRange(best_mask.size())) {
@@ -315,6 +295,12 @@ template<typename T> class VArrayImpl_For_Span_final final : public VArrayImpl_F
public:
using VArrayImpl_For_Span<T>::VArrayImpl_For_Span;
+ VArrayImpl_For_Span_final(const Span<T> data)
+ /* Cast const away, because the implementation for const and non const spans is shared. */
+ : VArrayImpl_For_Span<T>({const_cast<T *>(data.data()), data.size()})
+ {
+ }
+
private:
CommonVArrayInfo common_info() const final
{
@@ -370,6 +356,17 @@ template<typename T> class VArrayImpl_For_Single final : public VArrayImpl<T> {
return CommonVArrayInfo(CommonVArrayInfo::Type::Single, true, &value_);
}
+ void materialize(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ r_span.fill_indices(mask, value_);
+ }
+
+ void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const override
+ {
+ T *dst = r_span.data();
+ mask.foreach_index([&](const int64_t i) { new (dst + i) T(value_); });
+ }
+
void materialize_compressed(IndexMask mask, MutableSpan<T> r_span) const override
{
BLI_assert(mask.size() == r_span.size());
@@ -797,6 +794,18 @@ template<typename T> class VArrayCommon {
}
/**
+ * Return the value that is returned for every index, if the array is stored as a single value.
+ */
+ std::optional<T> get_if_single() const
+ {
+ const CommonVArrayInfo info = impl_->common_info();
+ if (info.type != CommonVArrayInfo::Type::Single) {
+ return std::nullopt;
+ }
+ return *static_cast<const T *>(info.data);
+ }
+
+ /**
* Return true when the other virtual references the same underlying memory.
*/
bool is_same(const VArrayCommon<T> &other) const
@@ -898,10 +907,7 @@ template<typename T> class VArray : public VArrayCommon<T> {
VArray(varray_tag::span /* tag */, Span<T> span)
{
- /* Cast const away, because the virtual array implementation for const and non const spans is
- * shared. */
- MutableSpan<T> mutable_span{const_cast<T *>(span.data()), span.size()};
- this->template emplace<VArrayImpl_For_Span_final<T>>(mutable_span);
+ this->template emplace<VArrayImpl_For_Span_final<T>>(span);
}
VArray(varray_tag::single /* tag */, T value, const int64_t size)
diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt
index 95b4987596e..e0d05f8c36a 100644
--- a/source/blender/blenlib/CMakeLists.txt
+++ b/source/blender/blenlib/CMakeLists.txt
@@ -1,8 +1,13 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2006 Blender Foundation. All rights reserved.
+if(HAVE_EXECINFO_H)
+ add_definitions(-DHAVE_EXECINFO_H)
+endif()
+
set(INC
.
+ ..
# ../blenkernel # don't add this back!
../makesdna
../../../intern/atomic
@@ -19,7 +24,6 @@ set(INC_SYS
)
set(SRC
- intern/BLI_args.c
intern/BLI_array.c
intern/BLI_assert.c
intern/BLI_color.cc
@@ -44,11 +48,13 @@ set(SRC
intern/array_store.c
intern/array_store_utils.c
intern/array_utils.c
+ intern/array_utils.cc
intern/astar.c
intern/bitmap.c
intern/bitmap_draw_2d.c
intern/boxpack_2d.c
intern/buffer.c
+ intern/compute_context.cc
intern/convexhull_2d.c
intern/cpp_type.cc
intern/delaunay_2d.cc
@@ -79,6 +85,7 @@ set(SRC
intern/kdtree_3d.c
intern/kdtree_4d.c
intern/lasso_2d.c
+ intern/lazy_threading.cc
intern/length_parameterize.cc
intern/listbase.c
intern/math_base.c
@@ -155,17 +162,18 @@ set(SRC
BLI_alloca.h
BLI_allocator.hh
BLI_any.hh
- BLI_args.h
BLI_array.h
BLI_array.hh
BLI_array_store.h
BLI_array_store_utils.h
BLI_array_utils.h
+ BLI_array_utils.hh
BLI_asan.h
BLI_assert.h
BLI_astar.h
BLI_bitmap.h
BLI_bitmap_draw_2d.h
+ BLI_bit_vector.hh
BLI_blenlib.h
BLI_bounds.hh
BLI_boxpack_2d.h
@@ -175,6 +183,7 @@ set(SRC
BLI_compiler_attrs.h
BLI_compiler_compat.h
BLI_compiler_typecheck.h
+ BLI_compute_context.hh
BLI_console.h
BLI_convexhull_2d.h
BLI_cpp_type.hh
@@ -279,6 +288,7 @@ set(SRC
BLI_path_util.h
BLI_polyfill_2d.h
BLI_polyfill_2d_beautify.h
+ BLI_pool.hh
BLI_probing_strategies.hh
BLI_quadric.h
BLI_rand.h
@@ -323,7 +333,6 @@ set(SRC
BLI_uuid.h
BLI_uvproject.h
BLI_vector.hh
- BLI_vector_adaptor.hh
BLI_vector_set.hh
BLI_vector_set_slots.hh
BLI_virtual_array.hh
@@ -347,6 +356,14 @@ set(LIB
${ZSTD_LIBRARIES}
)
+if(NOT WITH_PYTHON_MODULE)
+ list(APPEND SRC
+ intern/BLI_args.c
+
+ BLI_args.h
+ )
+endif()
+
if(WITH_MEM_VALGRIND)
add_definitions(-DWITH_MEM_VALGRIND)
endif()
@@ -424,6 +441,8 @@ if(WITH_GTESTS)
tests/BLI_array_store_test.cc
tests/BLI_array_test.cc
tests/BLI_array_utils_test.cc
+ tests/BLI_bit_vector_test.cc
+ tests/BLI_bitmap_test.cc
tests/BLI_bounds_test.cc
tests/BLI_color_test.cc
tests/BLI_cpp_type_test.cc
@@ -469,6 +488,7 @@ if(WITH_GTESTS)
tests/BLI_multi_value_map_test.cc
tests/BLI_path_util_test.cc
tests/BLI_polyfill_2d_test.cc
+ tests/BLI_pool_test.cc
tests/BLI_ressource_strings.h
tests/BLI_serialize_test.cc
tests/BLI_session_uuid_test.cc
diff --git a/source/blender/blenlib/intern/BLI_index_range.cc b/source/blender/blenlib/intern/BLI_index_range.cc
index 398228ab461..624dcc39fc5 100644
--- a/source/blender/blenlib/intern/BLI_index_range.cc
+++ b/source/blender/blenlib/intern/BLI_index_range.cc
@@ -44,4 +44,34 @@ Span<int64_t> IndexRange::as_span_internal() const
return Span<int64_t>(s_current_array + start_, size_);
}
+AlignedIndexRanges split_index_range_by_alignment(const IndexRange range, const int64_t alignment)
+{
+ BLI_assert(is_power_of_2_i(alignment));
+ const int64_t mask = alignment - 1;
+
+ AlignedIndexRanges aligned_ranges;
+
+ const int64_t start_chunk = range.start() & ~mask;
+ const int64_t end_chunk = range.one_after_last() & ~mask;
+ if (start_chunk == end_chunk) {
+ aligned_ranges.prefix = range;
+ }
+ else {
+ int64_t prefix_size = 0;
+ int64_t suffix_size = 0;
+ if (range.start() != start_chunk) {
+ prefix_size = alignment - (range.start() & mask);
+ }
+ if (range.one_after_last() != end_chunk) {
+ suffix_size = range.one_after_last() - end_chunk;
+ }
+ aligned_ranges.prefix = IndexRange(range.start(), prefix_size);
+ aligned_ranges.suffix = IndexRange(end_chunk, suffix_size);
+ aligned_ranges.aligned = IndexRange(aligned_ranges.prefix.one_after_last(),
+ range.size() - prefix_size - suffix_size);
+ }
+
+ return aligned_ranges;
+}
+
} // namespace blender
diff --git a/source/blender/blenlib/intern/BLI_kdopbvh.c b/source/blender/blenlib/intern/BLI_kdopbvh.c
index 62bf17bd415..a43b725b6e3 100644
--- a/source/blender/blenlib/intern/BLI_kdopbvh.c
+++ b/source/blender/blenlib/intern/BLI_kdopbvh.c
@@ -1385,7 +1385,7 @@ BVHTreeOverlap *BLI_bvhtree_overlap(
static bool tree_intersect_plane_test(const float *bv, const float plane[4])
{
- /* TODO(germano): Support other KDOP geometries. */
+ /* TODO(@germano): Support other KDOP geometries. */
const float bb_min[3] = {bv[0], bv[2], bv[4]};
const float bb_max[3] = {bv[1], bv[3], bv[5]};
float bb_near[3], bb_far[3];
diff --git a/source/blender/blenlib/intern/BLI_memarena.c b/source/blender/blenlib/intern/BLI_memarena.c
index 3b73a81012d..71a5dd0e044 100644
--- a/source/blender/blenlib/intern/BLI_memarena.c
+++ b/source/blender/blenlib/intern/BLI_memarena.c
@@ -53,7 +53,7 @@ static void memarena_buf_free_all(struct MemBuf *mb)
while (mb != NULL) {
struct MemBuf *mb_next = mb->next;
- /* Unpoison memory because MEM_freeN might overwrite it. */
+ /* Unpoison memory because #MEM_freeN might overwrite it. */
BLI_asan_unpoison(mb, (uint)MEM_allocN_len(mb));
MEM_freeN(mb);
@@ -158,6 +158,7 @@ void *BLI_memarena_calloc(MemArena *ma, size_t size)
BLI_assert(ma->use_calloc == false);
ptr = BLI_memarena_alloc(ma, size);
+ BLI_assert(ptr != NULL);
memset(ptr, 0, size);
return ptr;
diff --git a/source/blender/blenlib/intern/BLI_memblock.c b/source/blender/blenlib/intern/BLI_memblock.c
index f780d520301..b03efd2b8a2 100644
--- a/source/blender/blenlib/intern/BLI_memblock.c
+++ b/source/blender/blenlib/intern/BLI_memblock.c
@@ -5,7 +5,6 @@
* \ingroup bli
*
* Dead simple, fast memory allocator for allocating many elements of the same size.
- *
*/
#include <stdlib.h>
diff --git a/source/blender/blenlib/intern/array_utils.cc b/source/blender/blenlib/intern/array_utils.cc
new file mode 100644
index 00000000000..a837d6aceec
--- /dev/null
+++ b/source/blender/blenlib/intern/array_utils.cc
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_array_utils.hh"
+
+namespace blender::array_utils {
+
+void copy(const GVArray &src,
+ const IndexMask selection,
+ GMutableSpan dst,
+ const int64_t grain_size)
+{
+ BLI_assert(src.type() == dst.type());
+ BLI_assert(src.size() == dst.size());
+ threading::parallel_for(selection.index_range(), grain_size, [&](const IndexRange range) {
+ src.materialize_to_uninitialized(selection.slice(range), dst.data());
+ });
+}
+
+void gather(const GVArray &src,
+ const IndexMask indices,
+ GMutableSpan dst,
+ const int64_t grain_size)
+{
+ BLI_assert(src.type() == dst.type());
+ BLI_assert(indices.size() == dst.size());
+ threading::parallel_for(indices.index_range(), grain_size, [&](const IndexRange range) {
+ src.materialize_compressed_to_uninitialized(indices.slice(range), dst.slice(range).data());
+ });
+}
+
+} // namespace blender::array_utils
diff --git a/source/blender/blenlib/intern/bitmap.c b/source/blender/blenlib/intern/bitmap.c
index 7fcbc31c066..2cc2fbc3e2f 100644
--- a/source/blender/blenlib/intern/bitmap.c
+++ b/source/blender/blenlib/intern/bitmap.c
@@ -11,6 +11,7 @@
#include <string.h>
#include "BLI_bitmap.h"
+#include "BLI_math_bits.h"
#include "BLI_utildefines.h"
void BLI_bitmap_set_all(BLI_bitmap *bitmap, bool set, size_t bits)
@@ -46,3 +47,22 @@ void BLI_bitmap_or_all(BLI_bitmap *dst, const BLI_bitmap *src, size_t bits)
dst[i] |= src[i];
}
}
+
+int BLI_bitmap_find_first_unset(const BLI_bitmap *bitmap, const size_t bits)
+{
+ const size_t blocks_num = _BITMAP_NUM_BLOCKS(bits);
+ int result = -1;
+ /* Skip over completely set blocks. */
+ int index = 0;
+ while (index < blocks_num && bitmap[index] == ~0u) {
+ index++;
+ }
+ if (index < blocks_num) {
+ /* Found a partially used block: find the lowest unused bit. */
+ const uint m = ~bitmap[index];
+ BLI_assert(m != 0);
+ const uint bit_index = bitscan_forward_uint(m);
+ result = bit_index + (index << _BITMAP_POWER);
+ }
+ return result;
+}
diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c
index 78f5088e8b1..d55a4a8c9ff 100644
--- a/source/blender/blenlib/intern/boxpack_2d.c
+++ b/source/blender/blenlib/intern/boxpack_2d.c
@@ -712,7 +712,6 @@ void BLI_box_pack_2d_fixedarea(ListBase *boxes, int width, int height, ListBase
* # Box * Small # # Box * #
* # * # # * #
* ################### ###################
- *
*/
int area_hsplit_large = space->w * (space->h - box->h);
int area_vsplit_large = (space->w - box->w) * space->h;
diff --git a/source/blender/blenlib/intern/compute_context.cc b/source/blender/blenlib/intern/compute_context.cc
new file mode 100644
index 00000000000..50a4a90a4a9
--- /dev/null
+++ b/source/blender/blenlib/intern/compute_context.cc
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_compute_context.hh"
+#include "BLI_hash_md5.h"
+
+namespace blender {
+
+void ComputeContextHash::mix_in(const void *data, int64_t len)
+{
+ DynamicStackBuffer<> buffer_owner(HashSizeInBytes + len, 8);
+ char *buffer = static_cast<char *>(buffer_owner.buffer());
+ memcpy(buffer, this, HashSizeInBytes);
+ memcpy(buffer + HashSizeInBytes, data, len);
+
+ BLI_hash_md5_buffer(buffer, HashSizeInBytes + len, this);
+}
+
+std::ostream &operator<<(std::ostream &stream, const ComputeContextHash &hash)
+{
+ std::stringstream ss;
+ ss << "0x" << std::hex << hash.v1 << hash.v2;
+ stream << ss.str();
+ return stream;
+}
+
+void ComputeContext::print_stack(std::ostream &stream, StringRef name) const
+{
+ Stack<const ComputeContext *> stack;
+ for (const ComputeContext *current = this; current; current = current->parent_) {
+ stack.push(current);
+ }
+ stream << "Context Stack: " << name << "\n";
+ while (!stack.is_empty()) {
+ const ComputeContext *current = stack.pop();
+ stream << "-> ";
+ current->print_current_in_line(stream);
+ const ComputeContextHash &current_hash = current->hash_;
+ stream << " \t(hash: " << current_hash << ")\n";
+ }
+}
+
+std::ostream &operator<<(std::ostream &stream, const ComputeContext &compute_context)
+{
+ compute_context.print_stack(stream, "");
+ return stream;
+}
+
+} // namespace blender
diff --git a/source/blender/blenlib/intern/cpp_type.cc b/source/blender/blenlib/intern/cpp_type.cc
index d6a087cf175..38de32d3ec8 100644
--- a/source/blender/blenlib/intern/cpp_type.cc
+++ b/source/blender/blenlib/intern/cpp_type.cc
@@ -26,3 +26,4 @@ BLI_CPP_TYPE_MAKE(ColorGeometry4f, blender::ColorGeometry4f, CPPTypeFlags::Basic
BLI_CPP_TYPE_MAKE(ColorGeometry4b, blender::ColorGeometry4b, CPPTypeFlags::BasicType)
BLI_CPP_TYPE_MAKE(string, std::string, CPPTypeFlags::BasicType)
+BLI_CPP_TYPE_MAKE(StringVector, blender::Vector<std::string>, CPPTypeFlags::None)
diff --git a/source/blender/blenlib/intern/filereader_zstd.c b/source/blender/blenlib/intern/filereader_zstd.c
index 5f114f24fb0..aeb000e9754 100644
--- a/source/blender/blenlib/intern/filereader_zstd.c
+++ b/source/blender/blenlib/intern/filereader_zstd.c
@@ -281,7 +281,10 @@ static void zstd_close(FileReader *reader)
if (zstd->reader.seek) {
MEM_freeN(zstd->seek.uncompressed_ofs);
MEM_freeN(zstd->seek.compressed_ofs);
- MEM_freeN(zstd->seek.cached_content);
+ /* When an error has occurred this may be NULL, see: T99744. */
+ if (zstd->seek.cached_content) {
+ MEM_freeN(zstd->seek.cached_content);
+ }
}
else {
MEM_freeN((void *)zstd->in_buf.src);
diff --git a/source/blender/blenlib/intern/generic_virtual_array.cc b/source/blender/blenlib/intern/generic_virtual_array.cc
index 28bc66e8524..f66b1e14fc6 100644
--- a/source/blender/blenlib/intern/generic_virtual_array.cc
+++ b/source/blender/blenlib/intern/generic_virtual_array.cc
@@ -323,6 +323,7 @@ GVArraySpan::GVArraySpan(GVArraySpan &&other)
else {
data_ = owned_data_;
}
+ other.owned_data_ = nullptr;
other.data_ = nullptr;
other.size_ = 0;
}
@@ -393,6 +394,7 @@ GMutableVArraySpan::GMutableVArraySpan(GMutableVArraySpan &&other)
else {
data_ = owned_data_;
}
+ other.owned_data_ = nullptr;
other.data_ = nullptr;
other.size_ = 0;
}
@@ -402,7 +404,7 @@ GMutableVArraySpan::~GMutableVArraySpan()
if (varray_) {
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";
+ std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n";
}
}
}
diff --git a/source/blender/blenlib/intern/index_mask.cc b/source/blender/blenlib/intern/index_mask.cc
index f3590e4a41c..e9af183d60d 100644
--- a/source/blender/blenlib/intern/index_mask.cc
+++ b/source/blender/blenlib/intern/index_mask.cc
@@ -142,6 +142,7 @@ IndexMask find_indices_based_on_predicate__merge(
int64_t result_mask_size = 0;
for (Vector<Vector<int64_t>> &local_sub_masks : sub_masks) {
for (Vector<int64_t> &sub_mask : local_sub_masks) {
+ BLI_assert(!sub_mask.is_empty());
all_vectors.append(&sub_mask);
result_mask_size += sub_mask.size();
}
@@ -232,7 +233,9 @@ IndexMask find_indices_from_virtual_array(const IndexMask indices_to_check,
}
}
});
- sub_masks.local().append(std::move(masked_indices));
+ if (!masked_indices.is_empty()) {
+ sub_masks.local().append(std::move(masked_indices));
+ }
});
return detail::find_indices_based_on_predicate__merge(indices_to_check, sub_masks, r_indices);
diff --git a/source/blender/blenlib/intern/lazy_threading.cc b/source/blender/blenlib/intern/lazy_threading.cc
new file mode 100644
index 00000000000..803fd81a96d
--- /dev/null
+++ b/source/blender/blenlib/intern/lazy_threading.cc
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_lazy_threading.hh"
+#include "BLI_vector.hh"
+
+namespace blender::lazy_threading {
+
+/**
+ * This is a #RawVector so that it can be destructed after Blender checks for memory leaks.
+ */
+thread_local RawVector<FunctionRef<void()>, 0> hint_receivers;
+
+void send_hint()
+{
+ for (const FunctionRef<void()> &fn : hint_receivers) {
+ fn();
+ }
+}
+
+HintReceiver::HintReceiver(const FunctionRef<void()> fn)
+{
+ hint_receivers.append(fn);
+}
+
+HintReceiver::~HintReceiver()
+{
+ hint_receivers.pop_last();
+}
+
+} // namespace blender::lazy_threading
diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c
index e7ccdeab80a..773aac95193 100644
--- a/source/blender/blenlib/intern/math_geom.c
+++ b/source/blender/blenlib/intern/math_geom.c
@@ -1667,8 +1667,8 @@ bool isect_ray_tri_v3(const float ray_origin[3],
float *r_lambda,
float r_uv[2])
{
- /* NOTE(campbell): these values were 0.000001 in 2.4x but for projection snapping on
- * a human head (1BU == 1m), subsurf level 2, this gave many errors. */
+ /* NOTE(@campbellbarton): these values were 0.000001 in 2.4x but for projection snapping on
+ * a human head `(1BU == 1m)`, subdivision-surface level 2, this gave many errors. */
const float epsilon = 0.00000001f;
float p[3], s[3], e1[3], e2[3], q[3];
float a, f, u, v;
@@ -3773,7 +3773,7 @@ void barycentric_weights_v2_quad(const float v1[2],
const float co[2],
float w[4])
{
- /* NOTE(campbell): fabsf() here is not needed for convex quads
+ /* NOTE(@campbellbarton): fabsf() here is not needed for convex quads
* (and not used in #interp_weights_poly_v2).
* But in the case of concave/bow-tie quads for the mask rasterizer it
* gives unreliable results without adding `absf()`. If this becomes an issue for more general
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index ce9abc36cad..221ae84e74d 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -113,7 +113,6 @@ void copy_m4_m3(float m1[4][4], const float m2[3][3]) /* no clear */
m1[2][1] = m2[2][1];
m1[2][2] = m2[2][2];
- /* Reevan's Bugfix */
m1[0][3] = 0.0f;
m1[1][3] = 0.0f;
m1[2][3] = 0.0f;
@@ -787,14 +786,14 @@ void mul_m2_v2(const float mat[2][2], float vec[2])
mul_v2_m2v2(vec, mat, vec);
}
-void mul_mat3_m4_v3(const float M[4][4], float r[3])
+void mul_mat3_m4_v3(const float mat[4][4], float r[3])
{
const float x = r[0];
const float y = r[1];
- r[0] = x * M[0][0] + y * M[1][0] + M[2][0] * r[2];
- r[1] = x * M[0][1] + y * M[1][1] + M[2][1] * r[2];
- r[2] = x * M[0][2] + y * M[1][2] + M[2][2] * r[2];
+ r[0] = x * mat[0][0] + y * mat[1][0] + mat[2][0] * r[2];
+ r[1] = x * mat[0][1] + y * mat[1][1] + mat[2][1] * r[2];
+ r[2] = x * mat[0][2] + y * mat[1][2] + mat[2][2] * r[2];
}
void mul_v3_mat3_m4v3(float r[3], const float mat[4][4], const float vec[3])
@@ -1116,16 +1115,32 @@ double determinant_m3_array_db(const double m[3][3])
m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1]));
}
-bool invert_m3_ex(float m[3][3], const float epsilon)
+bool invert_m2_m2(float inverse[2][2], const float mat[2][2])
{
- float tmp[3][3];
- const bool success = invert_m3_m3_ex(tmp, m, epsilon);
+ adjoint_m2_m2(inverse, mat);
+ float det = determinant_m2(mat[0][0], mat[1][0], mat[0][1], mat[1][1]);
- copy_m3_m3(m, tmp);
+ bool success = (det != 0.0f);
+ if (success) {
+ inverse[0][0] /= det;
+ inverse[1][0] /= det;
+ inverse[0][1] /= det;
+ inverse[1][1] /= det;
+ }
+
+ return success;
+}
+
+bool invert_m3_ex(float mat[3][3], const float epsilon)
+{
+ float mat_tmp[3][3];
+ const bool success = invert_m3_m3_ex(mat_tmp, mat, epsilon);
+
+ copy_m3_m3(mat, mat_tmp);
return success;
}
-bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], const float epsilon)
+bool invert_m3_m3_ex(float inverse[3][3], const float mat[3][3], const float epsilon)
{
float det;
int a, b;
@@ -1134,10 +1149,10 @@ bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], const float epsilon)
BLI_assert(epsilon >= 0.0f);
/* calc adjoint */
- adjoint_m3_m3(m1, m2);
+ adjoint_m3_m3(inverse, mat);
/* then determinant old matrix! */
- det = determinant_m3_array(m2);
+ det = determinant_m3_array(mat);
success = (fabsf(det) > epsilon);
@@ -1145,33 +1160,33 @@ bool invert_m3_m3_ex(float m1[3][3], const float m2[3][3], const float epsilon)
det = 1.0f / det;
for (a = 0; a < 3; a++) {
for (b = 0; b < 3; b++) {
- m1[a][b] *= det;
+ inverse[a][b] *= det;
}
}
}
return success;
}
-bool invert_m3(float m[3][3])
+bool invert_m3(float mat[3][3])
{
- float tmp[3][3];
- const bool success = invert_m3_m3(tmp, m);
+ float mat_tmp[3][3];
+ const bool success = invert_m3_m3(mat_tmp, mat);
- copy_m3_m3(m, tmp);
+ copy_m3_m3(mat, mat_tmp);
return success;
}
-bool invert_m3_m3(float m1[3][3], const float m2[3][3])
+bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
{
float det;
int a, b;
bool success;
/* calc adjoint */
- adjoint_m3_m3(m1, m2);
+ adjoint_m3_m3(inverse, mat);
/* then determinant old matrix! */
- det = determinant_m3_array(m2);
+ det = determinant_m3_array(mat);
success = (det != 0.0f);
@@ -1179,7 +1194,7 @@ bool invert_m3_m3(float m1[3][3], const float m2[3][3])
det = 1.0f / det;
for (a = 0; a < 3; a++) {
for (b = 0; b < 3; b++) {
- m1[a][b] *= det;
+ inverse[a][b] *= det;
}
}
}
@@ -1187,12 +1202,12 @@ bool invert_m3_m3(float m1[3][3], const float m2[3][3])
return success;
}
-bool invert_m4(float m[4][4])
+bool invert_m4(float mat[4][4])
{
- float tmp[4][4];
- const bool success = invert_m4_m4(tmp, m);
+ float mat_tmp[4][4];
+ const bool success = invert_m4_m4(mat_tmp, mat);
- copy_m4_m4(m, tmp);
+ copy_m4_m4(mat, mat_tmp);
return success;
}
@@ -2112,6 +2127,12 @@ void size_to_mat4(float R[4][4], const float size[3])
R[3][3] = 1.0f;
}
+void mat3_to_size_2d(float size[2], const float M[3][3])
+{
+ size[0] = len_v2(M[0]);
+ size[1] = len_v2(M[1]);
+}
+
void mat3_to_size(float size[3], const float M[3][3])
{
size[0] = len_v3(M[0]);
@@ -2175,11 +2196,11 @@ float mat4_to_scale(const float mat[4][4])
return len_v3(unit_vec);
}
-float mat4_to_xy_scale(const float M[4][4])
+float mat4_to_xy_scale(const float mat[4][4])
{
/* unit length vector in xy plane */
float unit_vec[3] = {(float)M_SQRT1_2, (float)M_SQRT1_2, 0.0f};
- mul_mat3_m4_v3(M, unit_vec);
+ mul_mat3_m4_v3(mat, unit_vec);
return len_v3(unit_vec);
}
@@ -2224,12 +2245,6 @@ void mat4_to_loc_quat(float loc[3], float quat[4], const float wmat[4][4])
copy_m3_m4(mat3, wmat);
normalize_m3_m3(mat3_n, mat3);
- /* So scale doesn't interfere with rotation T24291. */
- /* FIXME: this is a workaround for negative matrix not working for rotation conversion. */
- if (is_negative_m3(mat3)) {
- negate_m3(mat3_n);
- }
-
mat3_normalized_to_quat(quat, mat3_n);
copy_v3_v3(loc, wmat[3]);
}
@@ -2238,7 +2253,7 @@ void mat4_decompose(float loc[3], float quat[4], float size[3], const float wmat
{
float rot[3][3];
mat4_to_loc_rot_size(loc, rot, size, wmat);
- mat3_normalized_to_quat(quat, rot);
+ mat3_normalized_to_quat_fast(quat, rot);
}
/**
@@ -2377,8 +2392,8 @@ void blend_m3_m3m3(float out[3][3],
mat3_to_rot_size(drot, dscale, dst);
mat3_to_rot_size(srot, sscale, src);
- mat3_normalized_to_quat(dquat, drot);
- mat3_normalized_to_quat(squat, srot);
+ mat3_normalized_to_quat_fast(dquat, drot);
+ mat3_normalized_to_quat_fast(squat, srot);
/* do blending */
interp_qt_qtqt(fquat, dquat, squat, srcweight);
@@ -2403,8 +2418,8 @@ void blend_m4_m4m4(float out[4][4],
mat4_to_loc_rot_size(dloc, drot, dscale, dst);
mat4_to_loc_rot_size(sloc, srot, sscale, src);
- mat3_normalized_to_quat(dquat, drot);
- mat3_normalized_to_quat(squat, srot);
+ mat3_normalized_to_quat_fast(dquat, drot);
+ mat3_normalized_to_quat_fast(squat, srot);
/* do blending */
interp_v3_v3v3(floc, dloc, sloc, srcweight);
@@ -2440,11 +2455,11 @@ void interp_m3_m3m3(float R[3][3], const float A[3][3], const float B[3][3], con
* Note that a flip of two axes is just a rotation of 180 degrees around the third axis, and
* three flipped axes are just an 180 degree rotation + a single axis flip. It is thus sufficient
* to solve this problem for single axis flips. */
- if (determinant_m3_array(U_A) < 0) {
+ if (is_negative_m3(U_A)) {
mul_m3_fl(U_A, -1.0f);
mul_m3_fl(P_A, -1.0f);
}
- if (determinant_m3_array(U_B) < 0) {
+ if (is_negative_m3(U_B)) {
mul_m3_fl(U_B, -1.0f);
mul_m3_fl(P_B, -1.0f);
}
@@ -2485,16 +2500,14 @@ void interp_m4_m4m4(float R[4][4], const float A[4][4], const float B[4][4], con
bool is_negative_m3(const float mat[3][3])
{
- float vec[3];
- cross_v3_v3v3(vec, mat[0], mat[1]);
- return (dot_v3v3(vec, mat[2]) < 0.0f);
+ return determinant_m3_array(mat) < 0.0f;
}
bool is_negative_m4(const float mat[4][4])
{
- float vec[3];
- cross_v3_v3v3(vec, mat[0], mat[1]);
- return (dot_v3v3(vec, mat[2]) < 0.0f);
+ /* Don't use #determinant_m4 as only the 3x3 components are needed
+ * when the matrix is used as a transformation to represent location/scale/rotation. */
+ return determinant_m4_mat3_array(mat) < 0.0f;
}
bool is_zero_m3(const float mat[3][3])
@@ -2552,11 +2565,8 @@ void loc_eul_size_to_mat4(float R[4][4],
R[3][2] = loc[2];
}
-void loc_eulO_size_to_mat4(float R[4][4],
- const float loc[3],
- const float eul[3],
- const float size[3],
- const short rotOrder)
+void loc_eulO_size_to_mat4(
+ float R[4][4], const float loc[3], const float eul[3], const float size[3], const short order)
{
float rmat[3][3], smat[3][3], tmat[3][3];
@@ -2564,7 +2574,7 @@ void loc_eulO_size_to_mat4(float R[4][4],
unit_m4(R);
/* Make rotation + scaling part. */
- eulO_to_mat3(rmat, eul, rotOrder);
+ eulO_to_mat3(rmat, eul, order);
size_to_mat3(smat, size);
mul_m3_m3m3(tmat, rmat, smat);
@@ -3066,14 +3076,14 @@ void svd_m4(float U[4][4], float s[4], float V[4][4], float A_[4][4])
}
}
-void pseudoinverse_m4_m4(float Ainv[4][4], const float A_[4][4], float epsilon)
+void pseudoinverse_m4_m4(float inverse[4][4], const float mat[4][4], float epsilon)
{
/* compute Moore-Penrose pseudo inverse of matrix, singular values
* below epsilon are ignored for stability (truncated SVD) */
float A[4][4], V[4][4], W[4], Wm[4][4], U[4][4];
int i;
- transpose_m4_m4(A, A_);
+ transpose_m4_m4(A, mat);
svd_m4(V, W, U, A);
transpose_m4(U);
transpose_m4(V);
@@ -3085,18 +3095,18 @@ void pseudoinverse_m4_m4(float Ainv[4][4], const float A_[4][4], float epsilon)
transpose_m4(V);
- mul_m4_series(Ainv, U, Wm, V);
+ mul_m4_series(inverse, U, Wm, V);
}
-void pseudoinverse_m3_m3(float Ainv[3][3], const float A[3][3], float epsilon)
+void pseudoinverse_m3_m3(float inverse[3][3], const float mat[3][3], float epsilon)
{
/* try regular inverse when possible, otherwise fall back to slow svd */
- if (!invert_m3_m3(Ainv, A)) {
- float tmp[4][4], tmpinv[4][4];
+ if (!invert_m3_m3(inverse, mat)) {
+ float mat_tmp[4][4], tmpinv[4][4];
- copy_m4_m3(tmp, A);
- pseudoinverse_m4_m4(tmpinv, tmp, epsilon);
- copy_m3_m4(Ainv, tmpinv);
+ copy_m4_m3(mat_tmp, mat);
+ pseudoinverse_m4_m4(tmpinv, mat_tmp, epsilon);
+ copy_m3_m4(inverse, tmpinv);
}
}
@@ -3106,22 +3116,22 @@ bool has_zero_axis_m4(const float matrix[4][4])
len_squared_v3(matrix[2]) < FLT_EPSILON;
}
-void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
+void invert_m4_m4_safe(float inverse[4][4], const float mat[4][4])
{
- if (!invert_m4_m4(Ainv, A)) {
- float Atemp[4][4];
+ if (!invert_m4_m4(inverse, mat)) {
+ float mat_tmp[4][4];
- copy_m4_m4(Atemp, A);
+ copy_m4_m4(mat_tmp, mat);
/* Matrix is degenerate (e.g. 0 scale on some axis), ideally we should
* never be in this situation, but try to invert it anyway with tweak.
*/
- Atemp[0][0] += 1e-8f;
- Atemp[1][1] += 1e-8f;
- Atemp[2][2] += 1e-8f;
+ mat_tmp[0][0] += 1e-8f;
+ mat_tmp[1][1] += 1e-8f;
+ mat_tmp[2][2] += 1e-8f;
- if (!invert_m4_m4(Ainv, Atemp)) {
- unit_m4(Ainv);
+ if (!invert_m4_m4(inverse, mat_tmp)) {
+ unit_m4(inverse);
}
}
}
@@ -3141,24 +3151,24 @@ void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
* where we want to specify the length of the degenerate axes.
* \{ */
-void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4])
+void invert_m4_m4_safe_ortho(float inverse[4][4], const float mat[4][4])
{
- if (UNLIKELY(!invert_m4_m4(Ainv, A))) {
- float Atemp[4][4];
- copy_m4_m4(Atemp, A);
- if (UNLIKELY(!(orthogonalize_m4_zero_axes(Atemp, 1.0f) && invert_m4_m4(Ainv, Atemp)))) {
- unit_m4(Ainv);
+ if (UNLIKELY(!invert_m4_m4(inverse, mat))) {
+ float mat_tmp[4][4];
+ copy_m4_m4(mat_tmp, mat);
+ if (UNLIKELY(!(orthogonalize_m4_zero_axes(mat_tmp, 1.0f) && invert_m4_m4(inverse, mat_tmp)))) {
+ unit_m4(inverse);
}
}
}
-void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3])
+void invert_m3_m3_safe_ortho(float inverse[3][3], const float mat[3][3])
{
- if (UNLIKELY(!invert_m3_m3(Ainv, A))) {
- float Atemp[3][3];
- copy_m3_m3(Atemp, A);
- if (UNLIKELY(!(orthogonalize_m3_zero_axes(Atemp, 1.0f) && invert_m3_m3(Ainv, Atemp)))) {
- unit_m3(Ainv);
+ if (UNLIKELY(!invert_m3_m3(inverse, mat))) {
+ float mat_tmp[3][3];
+ copy_m3_m3(mat_tmp, mat);
+ if (UNLIKELY(!(orthogonalize_m3_zero_axes(mat_tmp, 1.0f) && invert_m3_m3(inverse, mat_tmp)))) {
+ unit_m3(inverse);
}
}
}
diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c
index 92223bdf1d5..ae068e3fb19 100644
--- a/source/blender/blenlib/intern/math_rotation.c
+++ b/source/blender/blenlib/intern/math_rotation.c
@@ -176,7 +176,7 @@ void quat_to_compatible_quat(float q[4], const float a[4], const float old[4])
}
}
-/* skip error check, currently only needed by mat3_to_quat_is_ok */
+/* Skip error check, currently only needed by #mat3_to_quat_legacy. */
static void quat_to_mat3_no_error(float m[3][3], const float q[4])
{
double q0, q1, q2, q3, qda, qdb, qdc, qaa, qab, qac, qbb, qbc, qcc;
@@ -269,9 +269,11 @@ void quat_to_mat4(float m[4][4], const float q[4])
m[3][3] = 1.0f;
}
-void mat3_normalized_to_quat(float q[4], const float mat[3][3])
+void mat3_normalized_to_quat_fast(float q[4], const float mat[3][3])
{
BLI_ASSERT_UNIT_M3(mat);
+ /* Caller must ensure matrices aren't negative for valid results, see: T24291, T94231. */
+ BLI_assert(!is_negative_m3(mat));
/* Check the trace of the matrix - bad precision if close to -1. */
const float trace = mat[0][0] + mat[1][1] + mat[2][2];
@@ -332,34 +334,54 @@ void mat3_normalized_to_quat(float q[4], const float mat[3][3])
normalize_qt(q);
}
-void mat3_to_quat(float q[4], const float m[3][3])
-{
- float unit_mat[3][3];
- /* work on a copy */
- /* this is needed AND a 'normalize_qt' in the end */
- normalize_m3_m3(unit_mat, m);
- mat3_normalized_to_quat(q, unit_mat);
+static void mat3_normalized_to_quat_with_checks(float q[4], float mat[3][3])
+{
+ const float det = determinant_m3_array(mat);
+ if (UNLIKELY(!isfinite(det))) {
+ unit_m3(mat);
+ }
+ else if (UNLIKELY(det < 0.0f)) {
+ negate_m3(mat);
+ }
+ mat3_normalized_to_quat_fast(q, mat);
}
-void mat4_normalized_to_quat(float q[4], const float m[4][4])
+void mat3_normalized_to_quat(float q[4], const float mat[3][3])
{
- float mat3[3][3];
+ float unit_mat_abs[3][3];
+ copy_m3_m3(unit_mat_abs, mat);
+ mat3_normalized_to_quat_with_checks(q, unit_mat_abs);
+}
- copy_m3_m4(mat3, m);
- mat3_normalized_to_quat(q, mat3);
+void mat3_to_quat(float q[4], const float mat[3][3])
+{
+ float unit_mat_abs[3][3];
+ normalize_m3_m3(unit_mat_abs, mat);
+ mat3_normalized_to_quat_with_checks(q, unit_mat_abs);
}
-void mat4_to_quat(float q[4], const float m[4][4])
+void mat4_normalized_to_quat(float q[4], const float mat[4][4])
{
- float mat3[3][3];
+ float unit_mat_abs[3][3];
+ copy_m3_m4(unit_mat_abs, mat);
+ mat3_normalized_to_quat_with_checks(q, unit_mat_abs);
+}
- copy_m3_m4(mat3, m);
- mat3_to_quat(q, mat3);
+void mat4_to_quat(float q[4], const float mat[4][4])
+{
+ float unit_mat_abs[3][3];
+ copy_m3_m4(unit_mat_abs, mat);
+ normalize_m3(unit_mat_abs);
+ mat3_normalized_to_quat_with_checks(q, unit_mat_abs);
}
-void mat3_to_quat_is_ok(float q[4], const float wmat[3][3])
+void mat3_to_quat_legacy(float q[4], const float wmat[3][3])
{
+ /* Legacy version of #mat3_to_quat which has slightly different behavior.
+ * Keep for particle-system & boids since replacing this will make subtle changes
+ * that impact hair in existing files. See: D15772. */
+
float mat[3][3], matr[3][3], matn[3][3], q1[4], q2[4], angle, si, co, nor[3];
/* work on a copy */
@@ -498,7 +520,10 @@ void rotation_between_quats_to_quat(float q[4], const float q1[4], const float q
mul_qt_qtqt(q, tquat, q2);
}
-float quat_split_swing_and_twist(const float q_in[4], int axis, float r_swing[4], float r_twist[4])
+float quat_split_swing_and_twist(const float q_in[4],
+ const int axis,
+ float r_swing[4],
+ float r_twist[4])
{
BLI_assert(axis >= 0 && axis <= 2);
@@ -915,6 +940,65 @@ float tri_to_quat(float q[4], const float a[3], const float b[3], const float c[
return len;
}
+void sin_cos_from_fraction(int numerator, int denominator, float *r_sin, float *r_cos)
+{
+ /* By default, creating a circle from an integer: calling #sinf & #cosf on the fraction doesn't
+ * create symmetrical values (because floats can't represent Pi exactly).
+ * Resolve this when the rotation is calculated from a fraction by mapping the `numerator`
+ * to lower values so X/Y values for points around a circle are exactly symmetrical, see T87779.
+ *
+ * Multiply both the `numerator` and `denominator` by eight to ensure we can divide the circle
+ * into 8 octants. For each octant, we then use symmetry and negation to bring the `numerator`
+ * closer to the origin where precision is highest.
+ *
+ * Cases 2, 4, 5 and 7, use the trigonometric identity sin(-x) == -sin(x).
+ * Cases 1, 2, 5 and 6, swap the pointers `r_sin` and `r_cos`.
+ */
+ BLI_assert(0 <= numerator);
+ BLI_assert(numerator <= denominator);
+ BLI_assert(denominator > 0);
+
+ numerator *= 8; /* Multiply numerator the same as denominator. */
+ const int octant = numerator / denominator; /* Determine the octant. */
+ denominator *= 8; /* Ensure denominator is a multiple of eight. */
+ float cos_sign = 1.0f; /* Either 1.0f or -1.0f. */
+
+ switch (octant) {
+ case 0:
+ /* Primary octant, nothing to do. */
+ break;
+ case 1:
+ case 2:
+ numerator = (denominator / 4) - numerator;
+ SWAP(float *, r_sin, r_cos);
+ break;
+ case 3:
+ case 4:
+ numerator = (denominator / 2) - numerator;
+ cos_sign = -1.0f;
+ break;
+ case 5:
+ case 6:
+ numerator = numerator - (denominator * 3 / 4);
+ SWAP(float *, r_sin, r_cos);
+ cos_sign = -1.0f;
+ break;
+ case 7:
+ numerator = numerator - denominator;
+ break;
+ default:
+ BLI_assert_unreachable();
+ }
+
+ BLI_assert(-denominator / 4 <= numerator); /* Numerator may be negative. */
+ BLI_assert(numerator <= denominator / 4);
+ BLI_assert(cos_sign == -1.0f || cos_sign == 1.0f);
+
+ const float angle = (float)(2.0 * M_PI) * ((float)numerator / (float)denominator);
+ *r_sin = sinf(angle);
+ *r_cos = cosf(angle) * cos_sign;
+}
+
void print_qt(const char *str, const float q[4])
{
printf("%s: %.3f %.3f %.3f %.3f\n", str, q[0], q[1], q[2], q[3]);
@@ -1322,10 +1406,10 @@ void mat4_normalized_to_eul(float eul[3], const float m[4][4])
copy_m3_m4(mat3, m);
mat3_normalized_to_eul(eul, mat3);
}
-void mat4_to_eul(float eul[3], const float m[4][4])
+void mat4_to_eul(float eul[3], const float mat[4][4])
{
float mat3[3][3];
- copy_m3_m4(mat3, m);
+ copy_m3_m4(mat3, mat);
mat3_to_eul(eul, mat3);
}
@@ -1360,7 +1444,7 @@ void eul_to_quat(float quat[4], const float eul[3])
quat[3] = cj * cs - sj * sc;
}
-void rotate_eul(float beul[3], const char axis, const float ang)
+void rotate_eul(float beul[3], const char axis, const float angle)
{
float eul[3], mat1[3][3], mat2[3][3], totmat[3][3];
@@ -1368,13 +1452,13 @@ void rotate_eul(float beul[3], const char axis, const float ang)
eul[0] = eul[1] = eul[2] = 0.0f;
if (axis == 'X') {
- eul[0] = ang;
+ eul[0] = angle;
}
else if (axis == 'Y') {
- eul[1] = ang;
+ eul[1] = angle;
}
else {
- eul[2] = ang;
+ eul[2] = angle;
}
eul_to_mat3(mat1, eul);
@@ -1730,23 +1814,23 @@ void mat3_to_compatible_eulO(float eul[3],
void mat4_normalized_to_compatible_eulO(float eul[3],
const float oldrot[3],
const short order,
- const float m[4][4])
+ const float mat[4][4])
{
float mat3[3][3];
/* for now, we'll just do this the slow way (i.e. copying matrices) */
- copy_m3_m4(mat3, m);
+ copy_m3_m4(mat3, mat);
mat3_normalized_to_compatible_eulO(eul, oldrot, order, mat3);
}
void mat4_to_compatible_eulO(float eul[3],
const float oldrot[3],
const short order,
- const float m[4][4])
+ const float mat[4][4])
{
float mat3[3][3];
/* for now, we'll just do this the slow way (i.e. copying matrices) */
- copy_m3_m4(mat3, m);
+ copy_m3_m4(mat3, mat);
normalize_m3(mat3);
mat3_normalized_to_compatible_eulO(eul, oldrot, order, mat3);
}
@@ -1765,7 +1849,7 @@ void quat_to_compatible_eulO(float eul[3],
/* rotate the given euler by the given angle on the specified axis */
/* NOTE: is this safe to do with different axis orders? */
-void rotate_eulO(float beul[3], const short order, char axis, float ang)
+void rotate_eulO(float beul[3], const short order, const char axis, const float angle)
{
float eul[3], mat1[3][3], mat2[3][3], totmat[3][3];
@@ -1774,13 +1858,13 @@ void rotate_eulO(float beul[3], const short order, char axis, float ang)
zero_v3(eul);
if (axis == 'X') {
- eul[0] = ang;
+ eul[0] = angle;
}
else if (axis == 'Y') {
- eul[1] = ang;
+ eul[1] = angle;
}
else {
- eul[2] = ang;
+ eul[2] = angle;
}
eulO_to_mat3(mat1, eul, order);
diff --git a/source/blender/blenlib/intern/math_rotation.cc b/source/blender/blenlib/intern/math_rotation.cc
index 74300d55954..091e8af85d9 100644
--- a/source/blender/blenlib/intern/math_rotation.cc
+++ b/source/blender/blenlib/intern/math_rotation.cc
@@ -23,4 +23,17 @@ float3 rotate_direction_around_axis(const float3 &direction, const float3 &axis,
return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
}
+float3 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_normalized_to_mat3(mat, axis, angle);
+ mul_m3_v3(mat, result);
+ return result + center;
+}
+
} // namespace blender::math
diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc
index 700c126ca4c..0d8ad1da582 100644
--- a/source/blender/blenlib/intern/mesh_boolean.cc
+++ b/source/blender/blenlib/intern/mesh_boolean.cc
@@ -1675,7 +1675,7 @@ static Edge find_good_sorting_edge(const Vert *testp,
* The algorithm is similar to the one for find_ambient_cell, except that
* instead of an arbitrary point known to be outside the whole mesh, we
* have a particular point (v) and we just want to determine the patches
- * that that point is between in sorting-around-an-edge order.
+ * that point is between in sorting-around-an-edge order.
*/
static int find_containing_cell(const Vert *v,
int t,
@@ -2966,6 +2966,11 @@ static std::ostream &operator<<(std::ostream &os, const FaceMergeState &fms)
* \a tris all have the same original face.
* Find the 2d edge/triangle topology for these triangles, but only the ones facing in the
* norm direction, and whether each edge is dissolvable or not.
+ * If we did the initial triangulation properly, and any Delaunay triangulations of intersections
+ * properly, then each triangle edge should have at most one neighbor.
+ * However, there can be anomalies. For example, if an input face is self-intersecting, we fall
+ * back on the floating point poly-fill triangulation, which, after which all bets are off.
+ * Hence, try to be tolerant of such unexpected topology.
*/
static void init_face_merge_state(FaceMergeState *fms,
const Vector<int> &tris,
@@ -3053,16 +3058,35 @@ static void init_face_merge_state(FaceMergeState *fms,
std::cout << "me.v1 == mf.vert[i] so set edge[" << me_index << "].left_face = " << f
<< "\n";
}
- BLI_assert(me.left_face == -1);
- fms->edge[me_index].left_face = f;
+ if (me.left_face != -1) {
+ /* Unexpected in the normal case: this means more than one triangle shares this
+ * edge in the same orientation. But be tolerant of this case. By making this
+ * edge not dissolvable, we'll avoid future problems due to this non-manifold topology.
+ */
+ if (dbg_level > 1) {
+ std::cout << "me.left_face was already occupied, so triangulation wasn't good\n";
+ }
+ me.dissolvable = false;
+ }
+ else {
+ fms->edge[me_index].left_face = f;
+ }
}
else {
if (dbg_level > 1) {
std::cout << "me.v1 != mf.vert[i] so set edge[" << me_index << "].right_face = " << f
<< "\n";
}
- BLI_assert(me.right_face == -1);
- fms->edge[me_index].right_face = f;
+ if (me.right_face != -1) {
+ /* Unexpected, analogous to the me.left_face != -1 case above. */
+ if (dbg_level > 1) {
+ std::cout << "me.right_face was already occupied, so triangulation wasn't good\n";
+ }
+ me.dissolvable = false;
+ }
+ else {
+ fms->edge[me_index].right_face = f;
+ }
}
fms->face[f].edge.append(me_index);
}
diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc
index d5585f953ec..e8aa359fbe4 100644
--- a/source/blender/blenlib/intern/mesh_intersect.cc
+++ b/source/blender/blenlib/intern/mesh_intersect.cc
@@ -710,7 +710,7 @@ bool IMesh::erase_face_positions(int f_index, Span<bool> face_pos_erase, IMeshAr
* mark with null pointer and caller should call remove_null_faces().
* the loop is done.
*/
- this->face_[f_index] = NULL;
+ this->face_[f_index] = nullptr;
return true;
}
Array<const Vert *> new_vert(new_len);
diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c
index c39a2b5a27e..3ec7c3f9804 100644
--- a/source/blender/blenlib/intern/noise.c
+++ b/source/blender/blenlib/intern/noise.c
@@ -1125,7 +1125,7 @@ float BLI_noise_cell(float x, float y, float z)
return (2.0f * BLI_cellNoiseU(x, y, z) - 1.0f);
}
-void BLI_noise_cell_v3(float x, float y, float z, float ca[3])
+void BLI_noise_cell_v3(float x, float y, float z, float r_ca[3])
{
/* avoid precision issues on unit coordinates */
x = (x + 0.000001f) * 1.00001f;
@@ -1136,9 +1136,9 @@ void BLI_noise_cell_v3(float x, float y, float z, float ca[3])
int yi = (int)(floor(y));
int zi = (int)(floor(z));
const float *p = HASHPNT(xi, yi, zi);
- ca[0] = p[0];
- ca[1] = p[1];
- ca[2] = p[2];
+ r_ca[0] = p[0];
+ r_ca[1] = p[1];
+ r_ca[2] = p[2];
}
/** \} */
diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc
index a514c9e5183..8a073239b31 100644
--- a/source/blender/blenlib/intern/noise.cc
+++ b/source/blender/blenlib/intern/noise.cc
@@ -263,7 +263,6 @@ BLI_INLINE float mix(float v0, float v1, float x)
* + + |
* @ + + + + @ @------> x
* v0 v1
- *
*/
BLI_INLINE float mix(float v0, float v1, float v2, float v3, float x, float y)
{
@@ -809,15 +808,14 @@ float musgrave_hybrid_multi_fractal(const float co,
{
float p = co;
const float pwHL = std::pow(lacunarity, -H);
- float pwr = pwHL;
- float value = perlin_signed(p) + offset;
- float weight = gain * value;
- p *= lacunarity;
+ float pwr = 1.0f;
+ float value = 0.0f;
+ float weight = 1.0f;
const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f);
- for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) {
+ for (int i = 0; (weight > 0.001f) && (i < (int)octaves); i++) {
if (weight > 1.0f) {
weight = 1.0f;
}
@@ -830,8 +828,12 @@ float musgrave_hybrid_multi_fractal(const float co,
}
const float rmd = octaves - floorf(octaves);
- if (rmd != 0.0f) {
- value += rmd * ((perlin_signed(p) + offset) * pwr);
+ if ((rmd != 0.0f) && (weight > 0.001f)) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+ float signal = (perlin_signed(p) + offset) * pwr;
+ value += rmd * weight * signal;
}
return value;
@@ -961,15 +963,14 @@ float musgrave_hybrid_multi_fractal(const float2 co,
{
float2 p = co;
const float pwHL = std::pow(lacunarity, -H);
- float pwr = pwHL;
- float value = perlin_signed(p) + offset;
- float weight = gain * value;
- p *= lacunarity;
+ float pwr = 1.0f;
+ float value = 0.0f;
+ float weight = 1.0f;
const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f);
- for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) {
+ for (int i = 0; (weight > 0.001f) && (i < (int)octaves); i++) {
if (weight > 1.0f) {
weight = 1.0f;
}
@@ -982,8 +983,12 @@ float musgrave_hybrid_multi_fractal(const float2 co,
}
const float rmd = octaves - floorf(octaves);
- if (rmd != 0.0f) {
- value += rmd * ((perlin_signed(p) + offset) * pwr);
+ if ((rmd != 0.0f) && (weight > 0.001f)) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+ float signal = (perlin_signed(p) + offset) * pwr;
+ value += rmd * weight * signal;
}
return value;
@@ -1115,15 +1120,14 @@ float musgrave_hybrid_multi_fractal(const float3 co,
{
float3 p = co;
const float pwHL = std::pow(lacunarity, -H);
- float pwr = pwHL;
- float value = perlin_signed(p) + offset;
- float weight = gain * value;
- p *= lacunarity;
+ float pwr = 1.0f;
+ float value = 0.0f;
+ float weight = 1.0f;
const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f);
- for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) {
+ for (int i = 0; (weight > 0.001f) && (i < (int)octaves); i++) {
if (weight > 1.0f) {
weight = 1.0f;
}
@@ -1136,8 +1140,12 @@ float musgrave_hybrid_multi_fractal(const float3 co,
}
const float rmd = octaves - floorf(octaves);
- if (rmd != 0.0f) {
- value += rmd * ((perlin_signed(p) + offset) * pwr);
+ if ((rmd != 0.0f) && (weight > 0.001f)) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+ float signal = (perlin_signed(p) + offset) * pwr;
+ value += rmd * weight * signal;
}
return value;
@@ -1269,15 +1277,14 @@ float musgrave_hybrid_multi_fractal(const float4 co,
{
float4 p = co;
const float pwHL = std::pow(lacunarity, -H);
- float pwr = pwHL;
- float value = perlin_signed(p) + offset;
- float weight = gain * value;
- p *= lacunarity;
+ float pwr = 1.0f;
+ float value = 0.0f;
+ float weight = 1.0f;
const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f);
- for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) {
+ for (int i = 0; (weight > 0.001f) && (i < (int)octaves); i++) {
if (weight > 1.0f) {
weight = 1.0f;
}
@@ -1290,8 +1297,12 @@ float musgrave_hybrid_multi_fractal(const float4 co,
}
const float rmd = octaves - floorf(octaves);
- if (rmd != 0.0f) {
- value += rmd * ((perlin_signed(p) + offset) * pwr);
+ if ((rmd != 0.0f) && (weight > 0.001f)) {
+ if (weight > 1.0f) {
+ weight = 1.0f;
+ }
+ float signal = (perlin_signed(p) + offset) * pwr;
+ value += rmd * weight * signal;
}
return value;
diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c
index 5a96221c8d1..9b91636b055 100644
--- a/source/blender/blenlib/intern/path_util.c
+++ b/source/blender/blenlib/intern/path_util.c
@@ -466,8 +466,8 @@ void BLI_path_rel(char *file, const char *relfile)
#ifdef WIN32
if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
char *ptemp;
- /* fix missing volume name in relative base,
- * can happen with old recent-files.txt files */
+ /* Fix missing volume name in relative base,
+ * can happen with old `recent-files.txt` files. */
BLI_windows_get_default_root_dir(temp);
ptemp = &temp[2];
if (!ELEM(relfile[0], '\\', '/')) {
@@ -1105,29 +1105,29 @@ bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *na
path = BLI_getenv("PATH");
if (path) {
- char filename[FILE_MAX];
+ char filepath_test[FILE_MAX];
const char *temp;
do {
temp = strchr(path, separator);
if (temp) {
- memcpy(filename, path, temp - path);
- filename[temp - path] = 0;
+ memcpy(filepath_test, path, temp - path);
+ filepath_test[temp - path] = 0;
path = temp + 1;
}
else {
- BLI_strncpy(filename, path, sizeof(filename));
+ BLI_strncpy(filepath_test, path, sizeof(filepath_test));
}
- BLI_path_append(filename, maxlen, name);
+ BLI_path_append(filepath_test, maxlen, name);
if (
#ifdef _WIN32
- BLI_path_program_extensions_add_win32(filename, maxlen)
+ BLI_path_program_extensions_add_win32(filepath_test, maxlen)
#else
- BLI_exists(filename)
+ BLI_exists(filepath_test)
#endif
) {
- BLI_strncpy(fullname, filename, maxlen);
+ BLI_strncpy(fullname, filepath_test, maxlen);
retval = true;
break;
}
@@ -1204,87 +1204,6 @@ bool BLI_make_existing_file(const char *name)
return BLI_dir_create_recursive(di);
}
-void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
-{
- int sl;
-
- if (string) {
- /* ensure this is always set even if dir/file are NULL */
- string[0] = '\0';
-
- if (ELEM(NULL, dir, file)) {
- return; /* We don't want any NULLs */
- }
- }
- else {
- return; /* string is NULL, probably shouldn't happen but return anyway */
- }
-
- /* Resolve relative references */
- if (relabase && dir[0] == '/' && dir[1] == '/') {
- char *lslash;
-
- /* Get the file name, chop everything past the last slash (ie. the filename) */
- strcpy(string, relabase);
-
- lslash = (char *)BLI_path_slash_rfind(string);
- if (lslash) {
- *(lslash + 1) = 0;
- }
-
- dir += 2; /* Skip over the relative reference */
- }
-#ifdef WIN32
- else {
- if (BLI_strnlen(dir, 3) >= 2 && dir[1] == ':') {
- BLI_strncpy(string, dir, 3);
- dir += 2;
- }
- else if (BLI_strnlen(dir, 3) >= 2 && BLI_path_is_unc(dir)) {
- string[0] = 0;
- }
- else { /* no drive specified */
- /* first option: get the drive from the relabase if it has one */
- if (relabase && BLI_strnlen(relabase, 3) >= 2 && relabase[1] == ':') {
- BLI_strncpy(string, relabase, 3);
- string[2] = '\\';
- string[3] = '\0';
- }
- else { /* we're out of luck here, guessing the first valid drive, usually c:\ */
- BLI_windows_get_default_root_dir(string);
- }
-
- /* ignore leading slashes */
- while (ELEM(*dir, '/', '\\')) {
- dir++;
- }
- }
- }
-#endif
-
- strcat(string, dir);
-
- /* Make sure string ends in one (and only one) slash */
- /* first trim all slashes from the end of the string */
- sl = strlen(string);
- while ((sl > 0) && ELEM(string[sl - 1], '/', '\\')) {
- string[sl - 1] = '\0';
- sl--;
- }
- /* since we've now removed all slashes, put back one slash at the end. */
- strcat(string, "/");
-
- while (ELEM(*file, '/', '\\')) {
- /* Trim slashes from the front of file */
- file++;
- }
-
- strcat(string, file);
-
- /* Push all slashes to the system preferred direction */
- BLI_path_slash_native(string);
-}
-
static bool path_extension_check_ex(const char *str,
const size_t str_len,
const char *ext,
@@ -1586,8 +1505,8 @@ size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *pat
return ofs;
}
- /* remove trailing slashes, unless there are _only_ trailing slashes
- * (allow "//" as the first argument). */
+ /* Remove trailing slashes, unless there are *only* trailing slashes
+ * (allow `//` or `//some_path` as the first argument). */
bool has_trailing_slash = false;
if (ofs != 0) {
size_t len = ofs;
diff --git a/source/blender/blenlib/intern/rct.c b/source/blender/blenlib/intern/rct.c
index 0bb606c288e..7248db5b718 100644
--- a/source/blender/blenlib/intern/rct.c
+++ b/source/blender/blenlib/intern/rct.c
@@ -265,7 +265,7 @@ bool BLI_rcti_isect_segment(const rcti *rect, const int s1[2], const int s2[2])
/* diagonal: [/] */
tvec1[0] = rect->xmin;
tvec1[1] = rect->ymin;
- tvec2[0] = rect->xmin;
+ tvec2[0] = rect->xmax;
tvec2[1] = rect->ymax;
if (isect_segments_i(s1, s2, tvec1, tvec2)) {
return true;
@@ -311,7 +311,7 @@ bool BLI_rctf_isect_segment(const rctf *rect, const float s1[2], const float s2[
/* diagonal: [/] */
tvec1[0] = rect->xmin;
tvec1[1] = rect->ymin;
- tvec2[0] = rect->xmin;
+ tvec2[0] = rect->xmax;
tvec2[1] = rect->ymax;
if (isect_segments_fl(s1, s2, tvec1, tvec2)) {
return true;
diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c
index 92fd7f5937b..33d387a5447 100644
--- a/source/blender/blenlib/intern/scanfill.c
+++ b/source/blender/blenlib/intern/scanfill.c
@@ -44,7 +44,7 @@ typedef struct ScanFillVertLink {
ScanFillEdge *edge_first, *edge_last;
} ScanFillVertLink;
-/* local funcs */
+/* Local functions. */
#define SF_EPSILON 0.00003f
#define SF_EPSILON_SQ (SF_EPSILON * SF_EPSILON)
diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c
index 2d76f662611..8263f8ff34e 100644
--- a/source/blender/blenlib/intern/smallhash.c
+++ b/source/blender/blenlib/intern/smallhash.c
@@ -329,8 +329,7 @@ void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr
/** \name Debugging & Introspection
* \{ */
-/* NOTE(campbell): this was called _print_smhash in knifetool.c
- * it may not be intended for general use. */
+/* NOTE(@campbellbarton): useful for debugging but may not be intended for general use. */
#if 0
void BLI_smallhash_print(SmallHash *sh)
{
diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c
index 4fa5ca0c088..c04fc41ab4d 100644
--- a/source/blender/blenlib/intern/storage.c
+++ b/source/blender/blenlib/intern/storage.c
@@ -259,7 +259,7 @@ eFileAttributes BLI_file_attributes(const char *path)
#ifndef __APPLE__
bool BLI_file_alias_target(const char *filepath,
/* This parameter can only be `const` on Linux since
- * redirections are not supported there.
+ * redirection is not supported there.
* NOLINTNEXTLINE: readability-non-const-parameter. */
char r_targetpath[/*FILE_MAXDIR*/])
{
diff --git a/source/blender/blenlib/intern/string_cursor_utf8.c b/source/blender/blenlib/intern/string_cursor_utf8.c
index 7a23b4bb4ad..6b7151be969 100644
--- a/source/blender/blenlib/intern/string_cursor_utf8.c
+++ b/source/blender/blenlib/intern/string_cursor_utf8.c
@@ -206,7 +206,7 @@ void BLI_str_cursor_step_utf8(const char *str,
* less complex since it doesn't need to do multi-byte stepping.
*/
-/* helper funcs so we can match BLI_str_cursor_step_utf8 */
+/* Helper functions so we can match #BLI_str_cursor_step_utf8. */
static bool cursor_step_next_utf32(const char32_t *UNUSED(str), size_t maxlen, int *pos)
{
if ((*pos) >= (int)maxlen) {
diff --git a/source/blender/blenlib/intern/string_search.cc b/source/blender/blenlib/intern/string_search.cc
index 14d85b99739..31ea24eb494 100644
--- a/source/blender/blenlib/intern/string_search.cc
+++ b/source/blender/blenlib/intern/string_search.cc
@@ -11,8 +11,8 @@
#include "BLI_timeit.hh"
/* Right arrow, keep in sync with #UI_MENU_ARROW_SEP in `UI_interface.h`. */
-#define UI_MENU_ARROW_SEP "\xe2\x96\xb6"
-#define UI_MENU_ARROW_SEP_UNICODE 0x25b6
+#define UI_MENU_ARROW_SEP "\xe2\x96\xb8"
+#define UI_MENU_ARROW_SEP_UNICODE 0x25b8
namespace blender::string_search {
diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c
index 0cbf62cce03..17fb451e422 100644
--- a/source/blender/blenlib/intern/string_utf8.c
+++ b/source/blender/blenlib/intern/string_utf8.c
@@ -403,7 +403,7 @@ int BLI_str_utf8_char_width_safe(const char *p)
/* copied from glib's gutf8.c, added 'Err' arg */
-/* NOTE(campbell): glib uses uint for unicode, best we do the same,
+/* NOTE(@campbellbarton): glib uses uint for unicode, best we do the same,
* though we don't typedef it. */
#define UTF8_COMPUTE(Char, Mask, Len, Err) \
@@ -692,25 +692,25 @@ const char *BLI_str_find_next_char_utf8(const char *p, const char *str_end)
size_t BLI_str_partition_utf8(const char *str,
const uint delim[],
- const char **sep,
- const char **suf)
+ const char **r_sep,
+ const char **r_suf)
{
- return BLI_str_partition_ex_utf8(str, NULL, delim, sep, suf, false);
+ return BLI_str_partition_ex_utf8(str, NULL, delim, r_sep, r_suf, false);
}
size_t BLI_str_rpartition_utf8(const char *str,
const uint delim[],
- const char **sep,
- const char **suf)
+ const char **r_sep,
+ const char **r_suf)
{
- return BLI_str_partition_ex_utf8(str, NULL, delim, sep, suf, true);
+ return BLI_str_partition_ex_utf8(str, NULL, delim, r_sep, r_suf, true);
}
size_t BLI_str_partition_ex_utf8(const char *str,
const char *end,
const uint delim[],
- const char **sep,
- const char **suf,
+ const char **r_sep,
+ const char **r_suf,
const bool from_right)
{
const size_t str_len = end ? (size_t)(end - str) : strlen(str);
@@ -721,36 +721,32 @@ size_t BLI_str_partition_ex_utf8(const char *str,
/* Note that here, we assume end points to a valid utf8 char! */
BLI_assert((end >= str) && (BLI_str_utf8_as_unicode(end) != BLI_UTF8_ERR));
- *suf = (char *)(str + str_len);
-
- size_t index;
- for (*sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(end, str) : str), index = 0;
- from_right ? (*sep > str) : ((*sep < end) && (**sep != '\0'));
- *sep = (char *)(from_right ? (str != *sep ? BLI_str_find_prev_char_utf8(*sep, str) : NULL) :
- str + index)) {
+ char *suf = (char *)(str + str_len);
+ size_t index = 0;
+ for (char *sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(end, str) : str);
+ from_right ? (sep > str) : ((sep < end) && (*sep != '\0'));
+ sep = (char *)(from_right ? (str != sep ? BLI_str_find_prev_char_utf8(sep, str) : NULL) :
+ str + index)) {
size_t index_ofs = 0;
- const uint c = BLI_str_utf8_as_unicode_step_or_error(*sep, (size_t)(end - *sep), &index_ofs);
- index += index_ofs;
-
- if (c == BLI_UTF8_ERR) {
- *suf = *sep = NULL;
+ const uint c = BLI_str_utf8_as_unicode_step_or_error(sep, (size_t)(end - sep), &index_ofs);
+ if (UNLIKELY(c == BLI_UTF8_ERR)) {
break;
}
+ index += index_ofs;
for (const uint *d = delim; *d != '\0'; d++) {
if (*d == c) {
- /* *suf is already correct in case from_right is true. */
- if (!from_right) {
- *suf = (char *)(str + index);
- }
- return (size_t)(*sep - str);
+ /* `suf` is already correct in case from_right is true. */
+ *r_sep = sep;
+ *r_suf = from_right ? suf : (char *)(str + index);
+ return (size_t)(sep - str);
}
}
- *suf = *sep; /* Useful in 'from_right' case! */
+ suf = sep; /* Useful in 'from_right' case! */
}
- *suf = *sep = NULL;
+ *r_suf = *r_sep = NULL;
return str_len;
}
@@ -790,9 +786,9 @@ int BLI_str_utf8_offset_to_column(const char *str, int offset)
int BLI_str_utf8_offset_from_column(const char *str, int column)
{
- int offset = 0, pos = 0, col;
+ int offset = 0, pos = 0;
while (*(str + offset) && pos < column) {
- col = BLI_str_utf8_char_width_safe(str + offset);
+ const int col = BLI_str_utf8_char_width_safe(str + offset);
if (pos + col > column) {
break;
}
diff --git a/source/blender/blenlib/intern/system.c b/source/blender/blenlib/intern/system.c
index 35e26e0cb33..f7249e491d7 100644
--- a/source/blender/blenlib/intern/system.c
+++ b/source/blender/blenlib/intern/system.c
@@ -21,7 +21,9 @@
# include "BLI_winstuff.h"
#else
-# include <execinfo.h>
+# if defined(HAVE_EXECINFO_H)
+# include <execinfo.h>
+# endif
# include <unistd.h>
#endif
@@ -61,9 +63,9 @@ int BLI_cpu_support_sse2(void)
#if !defined(_MSC_VER)
void BLI_system_backtrace(FILE *fp)
{
- /* ------------- */
- /* Linux / Apple */
-# if defined(__linux__) || defined(__APPLE__)
+ /* ----------------------- */
+ /* If system as execinfo.h */
+# if defined(HAVE_EXECINFO_H)
# define SIZE 100
void *buffer[SIZE];
@@ -152,12 +154,12 @@ void BLI_hostname_get(char *buffer, size_t bufsize)
if (gethostname(buffer, bufsize - 1) < 0) {
BLI_strncpy(buffer, "-unknown-", bufsize);
}
- /* When gethostname() truncates, it doesn't guarantee the trailing \0. */
+ /* When `gethostname()` truncates, it doesn't guarantee the trailing `\0`. */
buffer[bufsize - 1] = '\0';
#else
DWORD bufsize_inout = bufsize;
if (!GetComputerName(buffer, &bufsize_inout)) {
- strncpy(buffer, "-unknown-", bufsize);
+ BLI_strncpy(buffer, "-unknown-", bufsize);
}
#endif
}
diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc
index a29dbe95ba9..c335d04413c 100644
--- a/source/blender/blenlib/intern/task_pool.cc
+++ b/source/blender/blenlib/intern/task_pool.cc
@@ -84,11 +84,11 @@ class Task {
free_taskdata(other.free_taskdata),
freedata(other.freedata)
{
- ((Task &)other).pool = NULL;
- ((Task &)other).run = NULL;
- ((Task &)other).taskdata = NULL;
+ ((Task &)other).pool = nullptr;
+ ((Task &)other).run = nullptr;
+ ((Task &)other).taskdata = nullptr;
((Task &)other).free_taskdata = false;
- ((Task &)other).freedata = NULL;
+ ((Task &)other).freedata = nullptr;
}
#else
Task(const Task &other) = delete;
diff --git a/source/blender/blenlib/intern/task_range.cc b/source/blender/blenlib/intern/task_range.cc
index 7e405529f03..181b760bea1 100644
--- a/source/blender/blenlib/intern/task_range.cc
+++ b/source/blender/blenlib/intern/task_range.cc
@@ -12,6 +12,7 @@
#include "DNA_listBase.h"
+#include "BLI_lazy_threading.hh"
#include "BLI_task.h"
#include "BLI_threads.h"
@@ -104,6 +105,8 @@ void BLI_task_parallel_range(const int start,
const size_t grainsize = MAX2(settings->min_iter_per_thread, 1);
const tbb::blocked_range<int> range(start, stop, grainsize);
+ blender::lazy_threading::send_hint();
+
if (settings->func_reduce) {
parallel_reduce(range, task);
if (settings->userdata_chunk) {
diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c
index e90a0ee02db..7e2c5e8f1dd 100644
--- a/source/blender/blenlib/intern/winstuff.c
+++ b/source/blender/blenlib/intern/winstuff.c
@@ -63,23 +63,17 @@ bool BLI_windows_register_blend_extension(const bool background)
char buffer[256];
char BlPath[MAX_PATH];
- char InstallDir[FILE_MAXDIR];
- char SysDir[FILE_MAXDIR];
- const char *ThumbHandlerDLL;
- char RegCmd[MAX_PATH * 2];
char MBox[256];
- char *blender_app;
-# ifndef _WIN64
- BOOL IsWOW64;
-# endif
printf("Registering file extension...");
GetModuleFileName(0, BlPath, MAX_PATH);
/* Replace the actual app name with the wrapper. */
- blender_app = strstr(BlPath, "blender.exe");
- if (blender_app != NULL) {
- strcpy(blender_app, "blender-launcher.exe");
+ {
+ char *blender_app = strstr(BlPath, "blender.exe");
+ if (blender_app != NULL) {
+ strcpy(blender_app, "blender-launcher.exe");
+ }
}
/* root is HKLM by default */
@@ -157,12 +151,17 @@ bool BLI_windows_register_blend_extension(const bool background)
}
# ifdef WITH_BLENDER_THUMBNAILER
- BLI_windows_get_executable_dir(InstallDir);
- GetSystemDirectory(SysDir, FILE_MAXDIR);
- ThumbHandlerDLL = "BlendThumb.dll";
- snprintf(
- RegCmd, MAX_PATH * 2, "%s\\regsvr32 /s \"%s\\%s\"", SysDir, InstallDir, ThumbHandlerDLL);
- system(RegCmd);
+ {
+ char RegCmd[MAX_PATH * 2];
+ char InstallDir[FILE_MAXDIR];
+ char SysDir[FILE_MAXDIR];
+ BLI_windows_get_executable_dir(InstallDir);
+ GetSystemDirectory(SysDir, FILE_MAXDIR);
+ const char *ThumbHandlerDLL = "BlendThumb.dll";
+ snprintf(
+ RegCmd, MAX_PATH * 2, "%s\\regsvr32 /s \"%s\\%s\"", SysDir, InstallDir, ThumbHandlerDLL);
+ system(RegCmd);
+ }
# endif
RegCloseKey(root);
diff --git a/source/blender/blenlib/tests/BLI_array_store_test.cc b/source/blender/blenlib/tests/BLI_array_store_test.cc
index 20e2a4d88f8..5d05e3be1f3 100644
--- a/source/blender/blenlib/tests/BLI_array_store_test.cc
+++ b/source/blender/blenlib/tests/BLI_array_store_test.cc
@@ -181,7 +181,7 @@ static void testbuffer_list_state_from_data__stride_expand(ListBase *lb,
#define TESTBUFFER_STRINGS_CREATE(lb, ...) \
{ \
BLI_listbase_clear(lb); \
- const char *data_array[] = {__VA_ARGS__ NULL}; \
+ const char *data_array[] = {__VA_ARGS__ nullptr}; \
testbuffer_list_state_from_string_array((lb), data_array); \
} \
((void)0)
@@ -795,10 +795,10 @@ TEST(array_store, TestChunk_Rand31_Stride11_Chunk21)
/* Test From Files (disabled, keep for local tests.) */
-void *file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
+static void *file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
{
FILE *fp = fopen(filepath, "rb");
- void *mem = NULL;
+ void *mem = nullptr;
if (fp) {
long int filelen_read;
@@ -810,14 +810,14 @@ void *file_read_binary_as_mem(const char *filepath, size_t pad_bytes, size_t *r_
fseek(fp, 0L, SEEK_SET);
mem = MEM_mallocN(filelen + pad_bytes, __func__);
- if (mem == NULL) {
+ if (mem == nullptr) {
goto finally;
}
filelen_read = fread(mem, 1, filelen, fp);
if ((filelen_read != filelen) || ferror(fp)) {
MEM_freeN(mem);
- mem = NULL;
+ mem = nullptr;
goto finally;
}
diff --git a/source/blender/blenlib/tests/BLI_bit_vector_test.cc b/source/blender/blenlib/tests/BLI_bit_vector_test.cc
new file mode 100644
index 00000000000..210f2be012d
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_bit_vector_test.cc
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "BLI_bit_vector.hh"
+#include "BLI_exception_safety_test_utils.hh"
+#include "BLI_strict_flags.h"
+
+#include "testing/testing.h"
+
+namespace blender::tests {
+
+TEST(bit_vector, DefaultConstructor)
+{
+ BitVector vec;
+ EXPECT_EQ(vec.size(), 0);
+}
+
+TEST(bit_vector, CopyConstructorInline)
+{
+ BitVector<> vec({false, false, true, true, false});
+ BitVector<> vec2 = vec;
+
+ EXPECT_EQ(vec.size(), 5);
+ EXPECT_EQ(vec2.size(), 5);
+
+ vec2[1].set();
+ EXPECT_FALSE(vec[1]);
+
+ EXPECT_FALSE(vec2[0]);
+ EXPECT_TRUE(vec2[1]);
+ EXPECT_TRUE(vec2[2]);
+ EXPECT_TRUE(vec2[3]);
+ EXPECT_FALSE(vec2[4]);
+}
+
+TEST(bit_vector, CopyConstructorLarge)
+{
+ BitVector<> vec(500, false);
+ vec[1].set();
+
+ BitVector<> vec2 = vec;
+
+ EXPECT_EQ(vec.size(), 500);
+ EXPECT_EQ(vec2.size(), 500);
+
+ vec2[2].set();
+ EXPECT_FALSE(vec[2]);
+
+ EXPECT_FALSE(vec2[0]);
+ EXPECT_TRUE(vec2[1]);
+ EXPECT_TRUE(vec2[2]);
+}
+
+TEST(bit_vector, MoveConstructorInline)
+{
+ BitVector<> vec({false, false, true, true, false});
+ BitVector<> vec2 = std::move(vec);
+
+ EXPECT_EQ(vec.size(), 0);
+ EXPECT_EQ(vec2.size(), 5);
+
+ EXPECT_FALSE(vec2[0]);
+ EXPECT_FALSE(vec2[1]);
+ EXPECT_TRUE(vec2[2]);
+ EXPECT_TRUE(vec2[3]);
+ EXPECT_FALSE(vec2[4]);
+}
+
+TEST(bit_vector, MoveConstructorLarge)
+{
+ BitVector<> vec(500, false);
+ vec[3].set();
+
+ BitVector<> vec2 = std::move(vec);
+
+ EXPECT_EQ(vec.size(), 0);
+ EXPECT_EQ(vec2.size(), 500);
+
+ EXPECT_FALSE(vec2[0]);
+ EXPECT_FALSE(vec2[1]);
+ EXPECT_FALSE(vec2[2]);
+ EXPECT_TRUE(vec2[3]);
+ EXPECT_FALSE(vec2[4]);
+}
+
+TEST(bit_vector, SizeConstructor)
+{
+ {
+ BitVector<> vec(0);
+ EXPECT_EQ(vec.size(), 0);
+ }
+ {
+ BitVector<> vec(5);
+ EXPECT_EQ(vec.size(), 5);
+ for (BitRef bit : vec) {
+ EXPECT_FALSE(bit);
+ }
+ }
+ {
+ BitVector<> vec(123);
+ EXPECT_EQ(vec.size(), 123);
+ for (BitRef bit : vec) {
+ EXPECT_FALSE(bit);
+ }
+ }
+}
+
+TEST(bit_vector, SizeFillConstructor)
+{
+ {
+ BitVector<> vec(5, false);
+ for (const int64_t i : IndexRange(5)) {
+ EXPECT_FALSE(vec[i]);
+ }
+ }
+ {
+ BitVector<> vec(123, true);
+ for (const int64_t i : IndexRange(123)) {
+ EXPECT_TRUE(vec[i]);
+ }
+ }
+}
+
+TEST(bit_vector, IndexAccess)
+{
+ BitVector<> vec(100, false);
+ vec[55].set();
+ EXPECT_FALSE(vec[50]);
+ EXPECT_FALSE(vec[51]);
+ EXPECT_FALSE(vec[52]);
+ EXPECT_FALSE(vec[53]);
+ EXPECT_FALSE(vec[54]);
+ EXPECT_TRUE(vec[55]);
+ EXPECT_FALSE(vec[56]);
+ EXPECT_FALSE(vec[57]);
+ EXPECT_FALSE(vec[58]);
+}
+
+TEST(bit_vector, Iterator)
+{
+ BitVector<> vec(100, false);
+ {
+ int64_t index = 0;
+ for (MutableBitRef bit : vec) {
+ bit.set(ELEM(index, 0, 4, 7, 10, 11));
+ index++;
+ }
+ }
+ {
+ int64_t index = 0;
+ for (BitRef bit : const_cast<const BitVector<> &>(vec)) {
+ EXPECT_EQ(bit, ELEM(index, 0, 4, 7, 10, 11));
+ index++;
+ }
+ }
+}
+
+TEST(bit_vector, Append)
+{
+ BitVector<> vec;
+ vec.append(false);
+ vec.append(true);
+ vec.append(true);
+ vec.append(false);
+
+ EXPECT_EQ(vec.size(), 4);
+ EXPECT_FALSE(vec[0]);
+ EXPECT_TRUE(vec[1]);
+ EXPECT_TRUE(vec[2]);
+ EXPECT_FALSE(vec[3]);
+}
+
+TEST(bit_vector, AppendMany)
+{
+ BitVector<> vec;
+ for (const int64_t i : IndexRange(1000)) {
+ vec.append(i % 2);
+ }
+ EXPECT_FALSE(vec[0]);
+ EXPECT_TRUE(vec[1]);
+ EXPECT_FALSE(vec[2]);
+ EXPECT_TRUE(vec[3]);
+ EXPECT_FALSE(vec[4]);
+ EXPECT_TRUE(vec[5]);
+}
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_bitmap_test.cc b/source/blender/blenlib/tests/BLI_bitmap_test.cc
new file mode 100644
index 00000000000..fb9e03e3136
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_bitmap_test.cc
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "BLI_bitmap.h"
+#include "testing/testing.h"
+
+namespace blender::tests {
+
+TEST(bitmap, empty_is_all_unset)
+{
+ BLI_BITMAP_DECLARE(bitmap, 10);
+ for (int i = 0; i < 10; ++i) {
+ EXPECT_FALSE(BLI_BITMAP_TEST_BOOL(bitmap, i));
+ }
+}
+
+TEST(bitmap, find_first_unset_empty)
+{
+ BLI_BITMAP_DECLARE(bitmap, 10);
+ EXPECT_EQ(0, BLI_bitmap_find_first_unset(bitmap, 10));
+}
+
+TEST(bitmap, find_first_unset_full)
+{
+ BLI_BITMAP_DECLARE(bitmap, 10);
+ BLI_bitmap_flip_all(bitmap, 10);
+ EXPECT_EQ(-1, BLI_bitmap_find_first_unset(bitmap, 10));
+}
+
+TEST(bitmap, find_first_unset_middle)
+{
+ BLI_BITMAP_DECLARE(bitmap, 100);
+ BLI_bitmap_flip_all(bitmap, 100);
+ /* Turn some bits off */
+ BLI_BITMAP_DISABLE(bitmap, 53);
+ BLI_BITMAP_DISABLE(bitmap, 81);
+ BLI_BITMAP_DISABLE(bitmap, 85);
+ BLI_BITMAP_DISABLE(bitmap, 86);
+
+ /* Find lowest unset bit, and set it. */
+ EXPECT_EQ(53, BLI_bitmap_find_first_unset(bitmap, 100));
+ BLI_BITMAP_ENABLE(bitmap, 53);
+ /* Now should find the next lowest bit. */
+ EXPECT_EQ(81, BLI_bitmap_find_first_unset(bitmap, 100));
+}
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_float3x3_test.cc b/source/blender/blenlib/tests/BLI_float3x3_test.cc
index d22993ee69e..cd823b6e368 100644
--- a/source/blender/blenlib/tests/BLI_float3x3_test.cc
+++ b/source/blender/blenlib/tests/BLI_float3x3_test.cc
@@ -34,6 +34,15 @@ TEST(float3x3, Rotation)
EXPECT_FLOAT_EQ(result[1], 1.0f);
}
+TEST(float3x3, Scale)
+{
+ float2 point(1.0f, 2.0f);
+ float3x3 transformation = float3x3::from_scale(float2(2.0f, 3.0f));
+ float2 result = transformation * point;
+ EXPECT_FLOAT_EQ(result[0], 2.0f);
+ EXPECT_FLOAT_EQ(result[1], 6.0f);
+}
+
TEST(float3x3, TranslationRotationScale)
{
float2 point(1.0f, 2.0f);
@@ -116,4 +125,11 @@ TEST(float3x3, Origin)
EXPECT_FLOAT_EQ(result[1], 3.0f);
}
+TEST(float3x3, GetScale2D)
+{
+ float2 scale(2.0f, 3.0f);
+ float3x3 transformation = float3x3::from_scale(scale);
+ EXPECT_EQ(scale, transformation.scale_2d());
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_index_range_test.cc b/source/blender/blenlib/tests/BLI_index_range_test.cc
index 10f6784cd44..8ec7ad85d9c 100644
--- a/source/blender/blenlib/tests/BLI_index_range_test.cc
+++ b/source/blender/blenlib/tests/BLI_index_range_test.cc
@@ -105,6 +105,12 @@ TEST(index_range, OneAfterEnd)
EXPECT_EQ(range.one_after_last(), 8);
}
+TEST(index_range, OneBeforeStart)
+{
+ IndexRange range = IndexRange(5, 3);
+ EXPECT_EQ(range.one_before_start(), 4);
+}
+
TEST(index_range, Start)
{
IndexRange range = IndexRange(6, 2);
@@ -120,6 +126,17 @@ TEST(index_range, Slice)
EXPECT_EQ(slice.last(), 12);
}
+TEST(index_range, Intersect)
+{
+ IndexRange range = IndexRange(5, 15);
+ EXPECT_EQ(range.intersect(IndexRange(2, 2)), IndexRange(5, 0));
+ EXPECT_EQ(range.intersect(IndexRange(4, 2)), IndexRange(5, 1));
+ EXPECT_EQ(range.intersect(IndexRange(3, 20)), IndexRange(5, 15));
+ EXPECT_EQ(range.intersect(IndexRange(5, 15)), IndexRange(5, 15));
+ EXPECT_EQ(range.intersect(IndexRange(15, 10)), IndexRange(15, 5));
+ EXPECT_EQ(range.intersect(IndexRange(22, 2)), IndexRange(20, 0));
+}
+
TEST(index_range, SliceRange)
{
IndexRange range = IndexRange(5, 15);
@@ -229,4 +246,50 @@ TEST(index_range, GenericAlgorithms)
EXPECT_EQ(std::count_if(range.begin(), range.end(), [](int v) { return v < 7; }), 3);
}
+TEST(index_range, SplitByAlignment)
+{
+ {
+ AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(0, 0), 16);
+ EXPECT_EQ(ranges.prefix, IndexRange());
+ EXPECT_EQ(ranges.aligned, IndexRange());
+ EXPECT_EQ(ranges.suffix, IndexRange());
+ }
+ {
+ AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(0, 24), 8);
+ EXPECT_EQ(ranges.prefix, IndexRange());
+ EXPECT_EQ(ranges.aligned, IndexRange(0, 24));
+ EXPECT_EQ(ranges.suffix, IndexRange());
+ }
+ {
+ AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(1, 2), 4);
+ EXPECT_EQ(ranges.prefix, IndexRange(1, 2));
+ EXPECT_EQ(ranges.aligned, IndexRange());
+ EXPECT_EQ(ranges.suffix, IndexRange());
+ }
+ {
+ AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(3, 50), 8);
+ EXPECT_EQ(ranges.prefix, IndexRange(3, 5));
+ EXPECT_EQ(ranges.aligned, IndexRange(8, 40));
+ EXPECT_EQ(ranges.suffix, IndexRange(48, 5));
+ }
+ {
+ AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(3, 50), 1);
+ EXPECT_EQ(ranges.prefix, IndexRange());
+ EXPECT_EQ(ranges.aligned, IndexRange(3, 50));
+ EXPECT_EQ(ranges.suffix, IndexRange());
+ }
+ {
+ AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(64, 16), 16);
+ EXPECT_EQ(ranges.prefix, IndexRange());
+ EXPECT_EQ(ranges.aligned, IndexRange(64, 16));
+ EXPECT_EQ(ranges.suffix, IndexRange());
+ }
+ {
+ AlignedIndexRanges ranges = split_index_range_by_alignment(IndexRange(3, 5), 8);
+ EXPECT_EQ(ranges.prefix, IndexRange(3, 5));
+ EXPECT_EQ(ranges.aligned, IndexRange());
+ EXPECT_EQ(ranges.suffix, IndexRange());
+ }
+}
+
} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_length_parameterize_test.cc b/source/blender/blenlib/tests/BLI_length_parameterize_test.cc
index 11f4997f563..3b41a7aed0c 100644
--- a/source/blender/blenlib/tests/BLI_length_parameterize_test.cc
+++ b/source/blender/blenlib/tests/BLI_length_parameterize_test.cc
@@ -32,7 +32,7 @@ TEST(length_parameterize, FloatSimple)
Array<float> factors(4);
sample_uniform(lengths, true, indices, factors);
Array<float> results(4);
- linear_interpolation<float>(values, indices, factors, results);
+ interpolate<float>(values, indices, factors, results);
Array<float> expected({
0.0f,
1.33333f,
@@ -54,7 +54,7 @@ TEST(length_parameterize, Float)
Array<float> factors(20);
sample_uniform(lengths, true, indices, factors);
Array<float> results(20);
- linear_interpolation<float>(values, indices, factors, results);
+ interpolate<float>(values, indices, factors, results);
Array<float> expected({
1.0f, 1.47368f, 1.94737f, 2.42105f, 2.89474f, 3.36842f, 3.84211f,
4.31579f, 4.78947f, 5.26316f, 5.73684f, 6.21053f, 6.68421f, 7.1579f,
@@ -75,7 +75,7 @@ TEST(length_parameterize, Float2)
Array<float> factors(12);
sample_uniform(lengths, true, indices, factors);
Array<float2> results(12);
- linear_interpolation<float2>(values, indices, factors, results);
+ interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.0f, 0.0f},
{0.272727f, 0.0f},
@@ -105,7 +105,7 @@ TEST(length_parameterize, Float2Cyclic)
Array<float> factors(12);
sample_uniform(lengths, false, indices, factors);
Array<float2> results(12);
- linear_interpolation<float2>(values, indices, factors, results);
+ interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.0f, 0.0f},
{0.333333f, 0.0f},
@@ -135,7 +135,7 @@ TEST(length_parameterize, LineMany)
Array<float> factors(5007);
sample_uniform(lengths, true, indices, factors);
Array<float> results(5007);
- linear_interpolation<float>(values, indices, factors, results);
+ interpolate<float>(values, indices, factors, results);
Array<float> expected({
1.9962f, 1.9964f, 1.9966f, 1.9968f, 1.997f, 1.9972f, 1.9974f, 1.9976f, 1.9978f, 1.998f,
1.9982f, 1.9984f, 1.9986f, 1.9988f, 1.999f, 1.9992f, 1.9994f, 1.9996f, 1.9998f, 2.0f,
@@ -154,7 +154,7 @@ TEST(length_parameterize, CyclicMany)
Array<float> factors(5007);
sample_uniform(lengths, false, indices, factors);
Array<float2> results(5007);
- linear_interpolation<float2>(values, indices, factors, results);
+ interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0, 0.0159776}, {0, 0.0151787}, {0, 0.0143797}, {0, 0.013581}, {0, 0.0127821},
{0, 0.0119832}, {0, 0.0111842}, {0, 0.0103855}, {0, 0.00958657}, {0, 0.00878763},
@@ -178,7 +178,7 @@ TEST(length_parameterize, InterpolateColor)
Array<float> factors(10);
sample_uniform(lengths, false, indices, factors);
Array<ColorGeometry4f> results(10);
- linear_interpolation<ColorGeometry4f>(colors, indices, factors, results);
+ interpolate<ColorGeometry4f>(colors, indices, factors, results);
Array<ColorGeometry4f> expected({
{0, 0, 0, 1},
{0.4, 0, 0, 1},
@@ -209,8 +209,7 @@ TEST(length_parameterize, ArbitraryFloatSimple)
Array<float> factors(4);
sample_at_lengths(lengths, sample_lengths, indices, factors);
Array<float> results(4);
- linear_interpolation<float>(values, indices, factors, results);
- results.as_span().print_as_lines("results");
+ interpolate<float>(values, indices, factors, results);
Array<float> expected({
0.5f,
1.5f,
@@ -233,8 +232,7 @@ TEST(length_parameterize, ArbitraryFloat2)
Array<float> factors(12);
sample_at_lengths(lengths, sample_lengths, indices, factors);
Array<float2> results(12);
- linear_interpolation<float2>(values, indices, factors, results);
- results.as_span().print_as_lines("results");
+ interpolate<float2>(values, indices, factors, results);
Array<float2> expected({
{0.5f, 0.0f},
{1.0f, 0.5f},
diff --git a/source/blender/blenlib/tests/BLI_math_rotation_test.cc b/source/blender/blenlib/tests/BLI_math_rotation_test.cc
index a283118bea2..460cfd2d36c 100644
--- a/source/blender/blenlib/tests/BLI_math_rotation_test.cc
+++ b/source/blender/blenlib/tests/BLI_math_rotation_test.cc
@@ -7,6 +7,8 @@
#include "BLI_math_rotation.hh"
#include "BLI_math_vector.hh"
+#include "BLI_vector.hh"
+
#include <cmath>
/* Test that quaternion converts to itself via matrix. */
@@ -150,6 +152,107 @@ TEST(math_rotation, quat_split_swing_and_twist_negative)
EXPECT_V4_NEAR(twist, expected_twist, FLT_EPSILON);
}
+/* -------------------------------------------------------------------- */
+/** \name Test `sin_cos_from_fraction` Accuracy & Exact Symmetry
+ * \{ */
+
+static void test_sin_cos_from_fraction_accuracy(const int range, const float expected_eps)
+{
+ for (int i = 0; i < range; i++) {
+ float sin_cos_fl[2];
+ sin_cos_from_fraction(i, range, &sin_cos_fl[0], &sin_cos_fl[1]);
+ const float phi = (float)(2.0 * M_PI) * ((float)i / (float)range);
+ const float sin_cos_test_fl[2] = {sinf(phi), cosf(phi)};
+ EXPECT_V2_NEAR(sin_cos_fl, sin_cos_test_fl, expected_eps);
+ }
+}
+
+/** Ensure the result of #sin_cos_from_fraction match #sinf & #cosf. */
+TEST(math_rotation, sin_cos_from_fraction_accuracy)
+{
+ for (int range = 1; range <= 64; range++) {
+ test_sin_cos_from_fraction_accuracy(range, 1e-6f);
+ }
+}
+
+/** Ensure values are exactly symmetrical where possible. */
+static void test_sin_cos_from_fraction_symmetry(const int range)
+{
+ /* The expected number of unique numbers depends on the range being a multiple of 4/2/1. */
+ const enum {
+ MULTIPLE_OF_1 = 1,
+ MULTIPLE_OF_2 = 2,
+ MULTIPLE_OF_4 = 3,
+ } multiple_of = (range & 1) ? MULTIPLE_OF_1 : ((range & 3) ? MULTIPLE_OF_2 : MULTIPLE_OF_4);
+
+ blender::Vector<blender::float2> coords;
+ coords.reserve(range);
+ for (int i = 0; i < range; i++) {
+ float sin_cos_fl[2];
+ sin_cos_from_fraction(i, range, &sin_cos_fl[0], &sin_cos_fl[1]);
+ switch (multiple_of) {
+ case MULTIPLE_OF_1: {
+ sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
+ break;
+ }
+ case MULTIPLE_OF_2: {
+ sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
+ sin_cos_fl[1] = fabsf(sin_cos_fl[1]);
+ break;
+ }
+ case MULTIPLE_OF_4: {
+ sin_cos_fl[0] = fabsf(sin_cos_fl[0]);
+ sin_cos_fl[1] = fabsf(sin_cos_fl[1]);
+ if (sin_cos_fl[0] > sin_cos_fl[1]) {
+ SWAP(float, sin_cos_fl[0], sin_cos_fl[1]);
+ }
+ break;
+ }
+ }
+ coords.append_unchecked(sin_cos_fl);
+ }
+ /* Sort, then count unique items. */
+ std::sort(coords.begin(), coords.end(), [](const blender::float2 &a, const blender::float2 &b) {
+ float delta = b[0] - a[0];
+ if (delta == 0.0f) {
+ delta = b[1] - a[1];
+ }
+ return delta > 0.0f;
+ });
+ int unique_coords_count = 1;
+ if (range > 1) {
+ int i_prev = 0;
+ for (int i = 1; i < range; i_prev = i++) {
+ if (coords[i_prev] != coords[i]) {
+ unique_coords_count += 1;
+ }
+ }
+ }
+ switch (multiple_of) {
+ case MULTIPLE_OF_1: {
+ EXPECT_EQ(unique_coords_count, (range / 2) + 1);
+ break;
+ }
+ case MULTIPLE_OF_2: {
+ EXPECT_EQ(unique_coords_count, (range / 4) + 1);
+ break;
+ }
+ case MULTIPLE_OF_4: {
+ EXPECT_EQ(unique_coords_count, (range / 8) + 1);
+ break;
+ }
+ }
+}
+
+TEST(math_rotation, sin_cos_from_fraction_symmetry)
+{
+ for (int range = 1; range <= 64; range++) {
+ test_sin_cos_from_fraction_symmetry(range);
+ }
+}
+
+/** \} */
+
namespace blender::math::tests {
TEST(math_rotation, RotateDirectionAroundAxis)
diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc
index 4f6f4a5c413..54afc3d975d 100644
--- a/source/blender/blenlib/tests/BLI_path_util_test.cc
+++ b/source/blender/blenlib/tests/BLI_path_util_test.cc
@@ -298,6 +298,13 @@ TEST(path_util, JoinComplex)
JOIN("1/2/3/", 100, "1", "////////", "", "2", "3\\");
}
+TEST(path_util, JoinRelativePrefix)
+{
+ JOIN("//a/b/c", 100, "//a", "b", "c");
+ JOIN("//a/b/c", 100, "//", "//a//", "//b//", "//c");
+ JOIN("//a/b/c", 100, "//", "//", "a", "//", "b", "//", "c");
+}
+
#undef JOIN
/* BLI_path_frame */
diff --git a/source/blender/blenlib/tests/BLI_pool_test.cc b/source/blender/blenlib/tests/BLI_pool_test.cc
new file mode 100644
index 00000000000..31cb255d997
--- /dev/null
+++ b/source/blender/blenlib/tests/BLI_pool_test.cc
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "BLI_exception_safety_test_utils.hh"
+#include "BLI_pool.hh"
+#include "BLI_strict_flags.h"
+#include "testing/testing.h"
+
+namespace blender::tests {
+
+TEST(pool, DefaultConstructor)
+{
+ Pool<int> pool;
+ EXPECT_EQ(pool.size(), 0);
+}
+
+TEST(pool, Allocation)
+{
+ Vector<int *> ptrs;
+ Pool<int> pool;
+ for (int i = 0; i < 100; i++) {
+ ptrs.append(&pool.construct(i));
+ }
+ EXPECT_EQ(pool.size(), 100);
+
+ for (int *ptr : ptrs) {
+ pool.destruct(*ptr);
+ }
+ EXPECT_EQ(pool.size(), 0);
+}
+
+TEST(pool, Reuse)
+{
+ Vector<int *> ptrs;
+ Pool<int> pool;
+ for (int i = 0; i < 32; i++) {
+ ptrs.append(&pool.construct(i));
+ }
+
+ int *freed_ptr = ptrs[6];
+ pool.destruct(*freed_ptr);
+
+ ptrs[6] = &pool.construct(0);
+
+ EXPECT_EQ(ptrs[6], freed_ptr);
+
+ for (int *ptr : ptrs) {
+ pool.destruct(*ptr);
+ }
+}
+
+} // namespace blender::tests
diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc
index 5a97b2c7999..9dfa48b5822 100644
--- a/source/blender/blenlib/tests/BLI_set_test.cc
+++ b/source/blender/blenlib/tests/BLI_set_test.cc
@@ -532,8 +532,14 @@ TEST(set, ForwardIterator)
Set<int>::iterator iter1 = set.begin();
int value1 = *iter1;
Set<int>::iterator iter2 = iter1++;
- EXPECT_EQ(*iter1, value1);
- EXPECT_EQ(*iter2, *(++iter1));
+ EXPECT_EQ(*iter2, value1);
+ EXPECT_EQ(*(++iter2), *iter1);
+ /* Interesting find: On GCC & MSVC this will succeed, as the 2nd argument is evaluated before the
+ * 1st. On Apple Clang it's the other way around, and the test fails. */
+ // EXPECT_EQ(*iter1, *(++iter1));
+ Set<int>::iterator iter3 = ++iter1;
+ /* Check that #iter1 itself changed. */
+ EXPECT_EQ(*iter3, *iter1);
}
TEST(set, GenericAlgorithms)
diff --git a/source/blender/blenlib/tests/BLI_string_search_test.cc b/source/blender/blenlib/tests/BLI_string_search_test.cc
index aa6adae8d76..ab1d073ed33 100644
--- a/source/blender/blenlib/tests/BLI_string_search_test.cc
+++ b/source/blender/blenlib/tests/BLI_string_search_test.cc
@@ -9,7 +9,7 @@
namespace blender::string_search::tests {
/* Right arrow, keep in sync with #UI_MENU_ARROW_SEP in `UI_interface.h`. */
-#define UI_MENU_ARROW_SEP "\xe2\x96\xb6"
+#define UI_MENU_ARROW_SEP "\xe2\x96\xb8"
TEST(string_search, damerau_levenshtein_distance)
{
diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc
index 3a12fe14de3..bbc4cecee4a 100644
--- a/source/blender/blenlib/tests/BLI_vector_test.cc
+++ b/source/blender/blenlib/tests/BLI_vector_test.cc
@@ -416,7 +416,7 @@ TEST(vector, Remove)
vec.remove(1);
EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span<int>({2}).begin()));
vec.remove(0);
- EXPECT_TRUE(std::equal(vec.begin(), vec.end(), Span<int>({}).begin()));
+ EXPECT_EQ(vec.begin(), vec.end());
}
TEST(vector, ExtendSmallVector)
diff --git a/source/blender/blenlib/tests/performance/CMakeLists.txt b/source/blender/blenlib/tests/performance/CMakeLists.txt
index c4f03255a11..2e4fcd4516c 100644
--- a/source/blender/blenlib/tests/performance/CMakeLists.txt
+++ b/source/blender/blenlib/tests/performance/CMakeLists.txt
@@ -4,6 +4,10 @@
set(INC
.
..
+ ../..
+ ../../../makesdna
+ ../../../../../intern/guardedalloc
+ ../../../../../intern/atomic
)
include_directories(${INC})
diff --git a/source/blender/blenloader/BLO_blend_validate.h b/source/blender/blenloader/BLO_blend_validate.h
index 0ce40987adc..4594f730758 100644
--- a/source/blender/blenloader/BLO_blend_validate.h
+++ b/source/blender/blenloader/BLO_blend_validate.h
@@ -12,6 +12,10 @@
struct Main;
struct ReportList;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/**
* Check (but do *not* fix) that all linked data-blocks are still valid
* (i.e. pointing to the right library).
@@ -21,3 +25,7 @@ bool BLO_main_validate_libraries(struct Main *bmain, struct ReportList *reports)
* * Check (and fix if needed) that shape key's 'from' pointer is valid.
*/
bool BLO_main_validate_shapekeys(struct Main *bmain, struct ReportList *reports);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h
index 536c3989aff..6f39670a226 100644
--- a/source/blender/blenloader/BLO_read_write.h
+++ b/source/blender/blenloader/BLO_read_write.h
@@ -219,8 +219,8 @@ void *BLO_read_get_new_packed_address(BlendDataReader *reader, const void *old_a
typedef void (*BlendReadListFn)(BlendDataReader *reader, void *data);
/**
- * Updates all ->prev and ->next pointers of the list elements.
- * Updates the list->first and list->last pointers.
+ * Updates all `->prev` and `->next` pointers of the list elements.
+ * Updates the `list->first` and `list->last` pointers.
* When not NULL, calls the callback on every element.
*/
void BLO_read_list_cb(BlendDataReader *reader, struct ListBase *list, BlendReadListFn callback);
@@ -237,6 +237,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p);
/* Misc. */
+int BLO_read_fileversion_get(BlendDataReader *reader);
bool BLO_read_requires_endian_switch(BlendDataReader *reader);
bool BLO_read_data_is_undo(BlendDataReader *reader);
void BLO_read_data_globmap_add(BlendDataReader *reader, void *oldaddr, void *newaddr);
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index 043f9ffd723..93040fa01ee 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -229,10 +229,10 @@ struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
* \return A BLI_linklist of `BLODataBlockInfo *`.
* The links and #BLODataBlockInfo.asset_data should be freed with MEM_freeN.
*/
-struct LinkNode * /*BLODataBlockInfo */ BLO_blendhandle_get_datablock_info(BlendHandle *bh,
- int ofblocktype,
- bool use_assets_only,
- int *r_tot_info_items);
+struct LinkNode * /*BLODataBlockInfo*/ BLO_blendhandle_get_datablock_info(BlendHandle *bh,
+ int ofblocktype,
+ bool use_assets_only,
+ int *r_tot_info_items);
/**
* Gets the previews of all the data-blocks in a file of a certain type
* (e.g. all the scene previews in a file).
diff --git a/source/blender/blenloader/BLO_undofile.h b/source/blender/blenloader/BLO_undofile.h
index 0584f95d85f..0dee167db53 100644
--- a/source/blender/blenloader/BLO_undofile.h
+++ b/source/blender/blenloader/BLO_undofile.h
@@ -61,6 +61,10 @@ typedef struct {
bool memchunk_identical;
} UndoReader;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* Actually only used `writefile.c`. */
void BLO_memfile_write_init(MemFileWriteData *mem_data,
@@ -101,3 +105,7 @@ extern struct Main *BLO_memfile_main_get(struct MemFile *memfile,
extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filepath);
FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt
index 5ca026ae9a3..f0209d1337c 100644
--- a/source/blender/blenloader/CMakeLists.txt
+++ b/source/blender/blenloader/CMakeLists.txt
@@ -19,6 +19,7 @@ set(INC
../windowmanager
../../../intern/clog
../../../intern/guardedalloc
+ ../bmesh
# for writefile.c: dna_type_offsets.h
${CMAKE_BINARY_DIR}/source/blender/makesdna/intern
@@ -32,24 +33,25 @@ set(INC_SYS
set(SRC
${CMAKE_SOURCE_DIR}/release/datafiles/userdef/userdef_default_theme.c
- intern/blend_validate.c
- intern/readblenentry.c
- intern/readfile.c
- intern/readfile_tempload.c
- intern/undofile.c
+ intern/blend_validate.cc
+ intern/readblenentry.cc
+ intern/readfile.cc
+ intern/readfile_tempload.cc
+ intern/undofile.cc
intern/versioning_250.c
intern/versioning_260.c
intern/versioning_270.c
intern/versioning_280.c
intern/versioning_290.c
- intern/versioning_300.c
+ intern/versioning_300.cc
+ intern/versioning_400.cc
intern/versioning_common.cc
intern/versioning_cycles.c
intern/versioning_defaults.c
intern/versioning_dna.c
intern/versioning_legacy.c
intern/versioning_userdef.c
- intern/writefile.c
+ intern/writefile.cc
BLO_blend_defs.h
BLO_blend_validate.h
@@ -81,6 +83,17 @@ if(WITH_ALEMBIC)
add_definitions(-DWITH_ALEMBIC)
endif()
+if(WITH_TBB)
+ list(APPEND INC_SYS
+ ${TBB_INCLUDE_DIRS}
+ )
+ add_definitions(-DWITH_TBB)
+endif()
+
+if(WIN32)
+ add_definitions(-DNOMINMAX)
+endif()
+
blender_add_lib(bf_blenloader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# needed so writefile.c can use dna_type_offsets.h
diff --git a/source/blender/blenloader/intern/blend_validate.c b/source/blender/blenloader/intern/blend_validate.cc
index e1527201e22..96314ab4324 100644
--- a/source/blender/blenloader/intern/blend_validate.c
+++ b/source/blender/blenloader/intern/blend_validate.cc
@@ -45,7 +45,8 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
ListBase *lbarray[INDEX_ID_MAX];
int i = set_listbasepointers(bmain, lbarray);
while (i--) {
- for (ID *id = lbarray[i]->first; id != NULL; id = id->next) {
+ for (ID *id = static_cast<ID *>(lbarray[i]->first); id != nullptr;
+ id = static_cast<ID *>(id->next)) {
if (ID_IS_LINKED(id)) {
is_valid = false;
BKE_reportf(reports,
@@ -57,18 +58,19 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
}
}
- for (Main *curmain = bmain->next; curmain != NULL; curmain = curmain->next) {
+ for (Main *curmain = bmain->next; curmain != nullptr; curmain = curmain->next) {
Library *curlib = curmain->curlib;
- if (curlib == NULL) {
- BKE_report(reports, RPT_ERROR, "Library database with NULL library data-block!");
+ if (curlib == nullptr) {
+ BKE_report(reports, RPT_ERROR, "Library database with nullptr library data-block!");
continue;
}
BKE_library_filepath_set(bmain, curlib, curlib->filepath);
- BlendFileReadReport bf_reports = {.reports = reports};
+ BlendFileReadReport bf_reports{};
+ bf_reports.reports = reports;
BlendHandle *bh = BLO_blendhandle_from_file(curlib->filepath_abs, &bf_reports);
- if (bh == NULL) {
+ if (bh == nullptr) {
BKE_reportf(reports,
RPT_ERROR,
"Library ID %s not found at expected path %s!",
@@ -79,8 +81,8 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
i = set_listbasepointers(curmain, lbarray);
while (i--) {
- ID *id = lbarray[i]->first;
- if (id == NULL) {
+ ID *id = static_cast<ID *>(lbarray[i]->first);
+ if (id == nullptr) {
continue;
}
@@ -96,12 +98,12 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
int totnames = 0;
LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), false, &totnames);
- for (; id != NULL; id = id->next) {
+ for (; id != nullptr; id = static_cast<ID *>(id->next)) {
if (!ID_IS_LINKED(id)) {
is_valid = false;
BKE_reportf(reports,
RPT_ERROR,
- "ID %s has NULL lib pointer while being in library %s!",
+ "ID %s has nullptr lib pointer while being in library %s!",
id->name,
curlib->filepath);
continue;
@@ -120,7 +122,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports)
}
}
- if (name == NULL) {
+ if (name == nullptr) {
is_valid = false;
BKE_reportf(reports,
RPT_ERROR,
@@ -163,7 +165,7 @@ bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports)
if (!ID_IS_LINKED(id)) {
/* We assume lib data is valid... */
Key *shapekey = BKE_key_from_id(id);
- if (shapekey != NULL && shapekey->from != id) {
+ if (shapekey != nullptr && shapekey->from != id) {
is_valid = false;
BKE_reportf(reports,
RPT_ERROR,
@@ -184,7 +186,7 @@ bool BLO_main_validate_shapekeys(Main *bmain, ReportList *reports)
/* NOTE: #BKE_id_delete also locks `bmain`, so we need to do this loop outside of the lock here.
*/
LISTBASE_FOREACH_MUTABLE (Key *, shapekey, &bmain->shapekeys) {
- if (shapekey->from != NULL) {
+ if (shapekey->from != nullptr) {
continue;
}
diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.cc
index 1bfa3b0e2bb..e3d6864b962 100644
--- a/source/blender/blenloader/intern/readblenentry.c
+++ b/source/blender/blenloader/intern/readblenentry.cc
@@ -70,7 +70,7 @@ void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp)
FileData *fd = (FileData *)bh;
BHead *bhead;
- fprintf(fp, "[\n");
+ fprintf(static_cast<FILE *>(fp), "[\n");
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == ENDB) {
break;
@@ -90,14 +90,14 @@ void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp)
buf[2] = buf[2] ? buf[2] : ' ';
buf[3] = buf[3] ? buf[3] : ' ';
- fprintf(fp,
+ fprintf(static_cast<FILE *>(fp),
"['%.4s', '%s', %d, %ld ],\n",
buf,
name,
bhead->nr,
(long int)(bhead->len + sizeof(BHead)));
}
- fprintf(fp, "]\n");
+ fprintf(static_cast<FILE *>(fp), "]\n");
}
LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
@@ -152,7 +152,8 @@ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
if (skip_datablock) {
continue;
}
- struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__);
+ struct BLODataBlockInfo *info = static_cast<BLODataBlockInfo *>(
+ MEM_mallocN(sizeof(*info), __func__));
/* Lastly, read asset data from the following blocks. */
if (asset_meta_data) {
@@ -199,7 +200,8 @@ static BHead *blo_blendhandle_read_preview_rects(FileData *fd,
bhead = blo_bhead_next(fd, bhead);
BLI_assert((preview_from_file->w[preview_index] * preview_from_file->h[preview_index] *
sizeof(uint)) == bhead->len);
- result->rect[preview_index] = BLO_library_read_struct(fd, bhead, "PreviewImage Icon Rect");
+ result->rect[preview_index] = static_cast<uint *>(
+ BLO_library_read_struct(fd, bhead, "PreviewImage Icon Rect"));
}
else {
/* This should not be needed, but can happen in 'broken' .blend files,
@@ -227,13 +229,14 @@ PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh,
for (BHead *bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == DATA) {
if (looking && bhead->SDNAnr == sdna_preview_image) {
- PreviewImage *preview_from_file = BLO_library_read_struct(fd, bhead, "PreviewImage");
+ PreviewImage *preview_from_file = static_cast<PreviewImage *>(
+ BLO_library_read_struct(fd, bhead, "PreviewImage"));
if (preview_from_file == NULL) {
break;
}
- PreviewImage *result = MEM_dupallocN(preview_from_file);
+ PreviewImage *result = static_cast<PreviewImage *>(MEM_dupallocN(preview_from_file));
bhead = blo_blendhandle_read_preview_rects(fd, bhead, result, preview_from_file);
MEM_freeN(preview_from_file);
return result;
@@ -279,7 +282,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *r_
case ID_SCE: /* fall through */
case ID_AC: /* fall through */
case ID_NT: /* fall through */
- new_prv = MEM_callocN(sizeof(PreviewImage), "newpreview");
+ new_prv = static_cast<PreviewImage *>(MEM_callocN(sizeof(PreviewImage), "newpreview"));
BLI_linklist_prepend(&previews, new_prv);
tot++;
looking = 1;
@@ -291,7 +294,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *r_
else if (bhead->code == DATA) {
if (looking) {
if (bhead->SDNAnr == DNA_struct_find_nr(fd->filesdna, "PreviewImage")) {
- prv = BLO_library_read_struct(fd, bhead, "PreviewImage");
+ prv = static_cast<PreviewImage *>(BLO_library_read_struct(fd, bhead, "PreviewImage"));
if (prv) {
memcpy(new_prv, prv, sizeof(PreviewImage));
@@ -375,7 +378,8 @@ BlendFileData *BLO_read_from_memory(const void *mem,
{
BlendFileData *bfd = NULL;
FileData *fd;
- BlendFileReadReport bf_reports = {.reports = reports};
+ BlendFileReadReport bf_reports{};
+ bf_reports.reports = reports;
fd = blo_filedata_from_memory(mem, memsize, &bf_reports);
if (fd) {
@@ -396,11 +400,12 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain,
BlendFileData *bfd = NULL;
FileData *fd;
ListBase old_mainlist;
- BlendFileReadReport bf_reports = {.reports = reports};
+ BlendFileReadReport bf_reports{};
+ bf_reports.reports = reports;
fd = blo_filedata_from_memfile(memfile, params, &bf_reports);
if (fd) {
- fd->skip_flags = params->skip_flags;
+ fd->skip_flags = eBLOReadSkip(params->skip_flags);
BLI_strncpy(fd->relabase, filepath, sizeof(fd->relabase));
/* separate libraries from old main */
@@ -411,7 +416,7 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain,
if ((params->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0) {
/* Build idmap of old main (we only care about local data here, so we can do that after
* split_main() call. */
- blo_make_old_idmap_from_main(fd, old_mainlist.first);
+ blo_make_old_idmap_from_main(fd, static_cast<Main *>(old_mainlist.first));
}
/* removed packed data from this trick - it's internal data that needs saves */
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.cc
index f0d390677bb..71708c09c01 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.cc
@@ -73,6 +73,7 @@
#include "BKE_main.h" /* for Main */
#include "BKE_main_idmap.h"
#include "BKE_material.h"
+#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_node.h" /* for tree type defines */
#include "BKE_object.h"
@@ -299,7 +300,7 @@ static OldNew *oldnewmap_lookup_entry(const OldNewMap *onm, const void *addr)
}
}
else {
- return NULL;
+ return nullptr;
}
}
}
@@ -312,8 +313,9 @@ static void oldnewmap_clear_map(OldNewMap *onm)
static void oldnewmap_increase_size(OldNewMap *onm)
{
onm->capacity_exp++;
- onm->entries = MEM_reallocN(onm->entries, sizeof(*onm->entries) * ENTRIES_CAPACITY(onm));
- onm->map = MEM_reallocN(onm->map, sizeof(*onm->map) * MAP_CAPACITY(onm));
+ onm->entries = static_cast<OldNew *>(
+ MEM_reallocN(onm->entries, sizeof(*onm->entries) * ENTRIES_CAPACITY(onm)));
+ onm->map = static_cast<int32_t *>(MEM_reallocN(onm->map, sizeof(*onm->map) * MAP_CAPACITY(onm)));
oldnewmap_clear_map(onm);
for (int i = 0; i < onm->nentries; i++) {
oldnewmap_insert_index_in_map(onm, onm->entries[i].oldp, i);
@@ -327,15 +329,16 @@ static void oldnewmap_init_data(OldNewMap *onm, const int capacity_exp)
memset(onm, 0x0, sizeof(*onm));
onm->capacity_exp = capacity_exp;
- onm->entries = MEM_malloc_arrayN(
- ENTRIES_CAPACITY(onm), sizeof(*onm->entries), "OldNewMap.entries");
- onm->map = MEM_malloc_arrayN(MAP_CAPACITY(onm), sizeof(*onm->map), "OldNewMap.map");
+ onm->entries = static_cast<OldNew *>(
+ MEM_malloc_arrayN(ENTRIES_CAPACITY(onm), sizeof(*onm->entries), "OldNewMap.entries"));
+ onm->map = static_cast<int32_t *>(
+ MEM_malloc_arrayN(MAP_CAPACITY(onm), sizeof(*onm->map), "OldNewMap.map"));
oldnewmap_clear_map(onm);
}
static OldNewMap *oldnewmap_new(void)
{
- OldNewMap *onm = MEM_mallocN(sizeof(*onm), "OldNewMap");
+ OldNewMap *onm = static_cast<OldNewMap *>(MEM_mallocN(sizeof(*onm), "OldNewMap"));
oldnewmap_init_data(onm, DEFAULT_SIZE_EXP);
@@ -344,7 +347,7 @@ static OldNewMap *oldnewmap_new(void)
static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr)
{
- if (oldaddr == NULL || newaddr == NULL) {
+ if (oldaddr == nullptr || newaddr == nullptr) {
return;
}
@@ -359,6 +362,11 @@ static void oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr,
oldnewmap_insert_or_replace(onm, entry);
}
+static void oldnewmap_lib_insert(FileData *fd, const void *oldaddr, ID *newaddr, int nr)
+{
+ oldnewmap_insert(fd->libmap, oldaddr, newaddr, nr);
+}
+
void blo_do_versions_oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void *newaddr, int nr)
{
oldnewmap_insert(onm, oldaddr, newaddr, nr);
@@ -367,8 +375,8 @@ void blo_do_versions_oldnewmap_insert(OldNewMap *onm, const void *oldaddr, void
static void *oldnewmap_lookup_and_inc(OldNewMap *onm, const void *addr, bool increase_users)
{
OldNew *entry = oldnewmap_lookup_entry(onm, addr);
- if (entry == NULL) {
- return NULL;
+ if (entry == nullptr) {
+ return nullptr;
}
if (increase_users) {
entry->nr++;
@@ -379,18 +387,18 @@ static void *oldnewmap_lookup_and_inc(OldNewMap *onm, const void *addr, bool inc
/* for libdata, OldNew.nr has ID code, no increment */
static void *oldnewmap_liblookup(OldNewMap *onm, const void *addr, const void *lib)
{
- if (addr == NULL) {
- return NULL;
+ if (addr == nullptr) {
+ return nullptr;
}
- ID *id = oldnewmap_lookup_and_inc(onm, addr, false);
- if (id == NULL) {
- return NULL;
+ ID *id = static_cast<ID *>(oldnewmap_lookup_and_inc(onm, addr, false));
+ if (id == nullptr) {
+ return nullptr;
}
if (!lib || id->lib) {
return id;
}
- return NULL;
+ return nullptr;
}
static void oldnewmap_clear(OldNewMap *onm)
@@ -400,7 +408,7 @@ static void oldnewmap_clear(OldNewMap *onm)
OldNew *entry = &onm->entries[i];
if (entry->nr == 0) {
MEM_freeN(entry->newp);
- entry->newp = NULL;
+ entry->newp = nullptr;
}
}
@@ -446,12 +454,12 @@ void blo_join_main(ListBase *mainlist)
{
Main *tojoin, *mainl;
- mainl = mainlist->first;
+ mainl = static_cast<Main *>(mainlist->first);
- if (mainl->id_map != NULL) {
+ if (mainl->id_map != nullptr) {
/* Cannot keep this since we add some IDs from joined mains. */
BKE_main_idmap_destroy(mainl->id_map);
- mainl->id_map = NULL;
+ mainl->id_map = nullptr;
}
while ((tojoin = mainl->next)) {
@@ -463,8 +471,8 @@ void blo_join_main(ListBase *mainlist)
static void split_libdata(ListBase *lb_src, Main **lib_main_array, const uint lib_main_array_len)
{
- for (ID *id = lb_src->first, *idnext; id; id = idnext) {
- idnext = id->next;
+ for (ID *id = static_cast<ID *>(lb_src->first), *idnext; id; id = idnext) {
+ idnext = static_cast<ID *>(id->next);
if (id->lib) {
if (((uint)id->lib->temp_index < lib_main_array_len) &&
@@ -485,24 +493,26 @@ static void split_libdata(ListBase *lb_src, Main **lib_main_array, const uint li
void blo_split_main(ListBase *mainlist, Main *main)
{
mainlist->first = mainlist->last = main;
- main->next = NULL;
+ main->next = nullptr;
if (BLI_listbase_is_empty(&main->libraries)) {
return;
}
- if (main->id_map != NULL) {
+ if (main->id_map != nullptr) {
/* Cannot keep this since we remove some IDs from given main. */
BKE_main_idmap_destroy(main->id_map);
- main->id_map = NULL;
+ main->id_map = nullptr;
}
/* (Library.temp_index -> Main), lookup table */
const uint lib_main_array_len = BLI_listbase_count(&main->libraries);
- Main **lib_main_array = MEM_malloc_arrayN(lib_main_array_len, sizeof(*lib_main_array), __func__);
+ Main **lib_main_array = static_cast<Main **>(
+ MEM_malloc_arrayN(lib_main_array_len, sizeof(*lib_main_array), __func__));
int i = 0;
- for (Library *lib = main->libraries.first; lib; lib = lib->id.next, i++) {
+ for (Library *lib = static_cast<Library *>(main->libraries.first); lib;
+ lib = static_cast<Library *>(lib->id.next), i++) {
Main *libmain = BKE_main_new();
libmain->curlib = lib;
libmain->versionfile = lib->versionfile;
@@ -515,9 +525,9 @@ void blo_split_main(ListBase *mainlist, Main *main)
ListBase *lbarray[INDEX_ID_MAX];
i = set_listbasepointers(main, lbarray);
while (i--) {
- ID *id = lbarray[i]->first;
- if (id == NULL || GS(id->name) == ID_LI) {
- /* No ID_LI data-lock should ever be linked anyway, but just in case, better be explicit. */
+ ID *id = static_cast<ID *>(lbarray[i]->first);
+ if (id == nullptr || GS(id->name) == ID_LI) {
+ /* No ID_LI data-block should ever be linked anyway, but just in case, better be explicit. */
continue;
}
split_libdata(lbarray[i], lib_main_array, lib_main_array_len);
@@ -532,7 +542,7 @@ static void read_file_version(FileData *fd, Main *main)
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == GLOB) {
- FileGlobal *fg = read_struct(fd, bhead, "Global");
+ FileGlobal *fg = static_cast<FileGlobal *>(read_struct(fd, bhead, "Global"));
if (fg) {
main->subversionfile = fg->subversion;
main->minversionfile = fg->minversion;
@@ -590,7 +600,7 @@ static void read_file_bhead_idname_map_create(FileData *fd)
}
}
- BLI_assert(fd->bhead_idname_hash == NULL);
+ BLI_assert(fd->bhead_idname_hash == nullptr);
fd->bhead_idname_hash = BLI_ghash_str_new_ex(__func__, reserve);
@@ -623,7 +633,7 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
// printf("blo_find_main: original in %s\n", filepath);
// printf("blo_find_main: converted to %s\n", name1);
- for (m = mainlist->first; m; m = m->next) {
+ for (m = static_cast<Main *>(mainlist->first); m; m = m->next) {
const char *libname = (m->curlib) ? m->curlib->filepath_abs : m->filepath;
if (BLI_path_cmp(name1, libname) == 0) {
@@ -639,7 +649,8 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
/* Add library data-block itself to 'main' Main, since libraries are **never** linked data.
* Fixes bug where you could end with all ID_LI data-blocks having the same name... */
- lib = BKE_libblock_alloc(mainlist->first, ID_LI, BLI_path_basename(filepath), 0);
+ lib = static_cast<Library *>(BKE_libblock_alloc(
+ static_cast<Main *>(mainlist->first), ID_LI, BLI_path_basename(filepath), 0));
/* Important, consistency with main ID reading code from read_libblock(). */
lib->id.us = ID_FAKE_USERS(lib);
@@ -750,7 +761,7 @@ static void bh8_from_bh4(BHead *bhead, BHead4 *bhead4)
static BHeadN *get_bhead(FileData *fd)
{
- BHeadN *new_bhead = NULL;
+ BHeadN *new_bhead = nullptr;
ssize_t readsize;
if (fd) {
@@ -827,11 +838,11 @@ static BHeadN *get_bhead(FileData *fd)
/* pass */
}
#ifdef USE_BHEAD_READ_ON_DEMAND
- else if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&bhead)) {
+ else if (fd->file->seek != nullptr && BHEAD_USE_READ_ON_DEMAND(&bhead)) {
/* Delay reading bhead content. */
- new_bhead = MEM_mallocN(sizeof(BHeadN), "new_bhead");
+ new_bhead = static_cast<BHeadN *>(MEM_mallocN(sizeof(BHeadN), "new_bhead"));
if (new_bhead) {
- new_bhead->next = new_bhead->prev = NULL;
+ new_bhead->next = new_bhead->prev = nullptr;
new_bhead->file_offset = fd->file->offset;
new_bhead->has_data = false;
new_bhead->is_memchunk_identical = false;
@@ -840,7 +851,7 @@ static BHeadN *get_bhead(FileData *fd)
if (seek_new == -1) {
fd->is_eof = true;
MEM_freeN(new_bhead);
- new_bhead = NULL;
+ new_bhead = nullptr;
}
BLI_assert(fd->file->offset == seek_new);
}
@@ -850,9 +861,10 @@ static BHeadN *get_bhead(FileData *fd)
}
#endif
else {
- new_bhead = MEM_mallocN(sizeof(BHeadN) + (size_t)bhead.len, "new_bhead");
+ new_bhead = static_cast<BHeadN *>(
+ MEM_mallocN(sizeof(BHeadN) + (size_t)bhead.len, "new_bhead"));
if (new_bhead) {
- new_bhead->next = new_bhead->prev = NULL;
+ new_bhead->next = new_bhead->prev = nullptr;
#ifdef USE_BHEAD_READ_ON_DEMAND
new_bhead->file_offset = 0; /* don't seek. */
new_bhead->has_data = true;
@@ -865,7 +877,7 @@ static BHeadN *get_bhead(FileData *fd)
if (readsize != bhead.len) {
fd->is_eof = true;
MEM_freeN(new_bhead);
- new_bhead = NULL;
+ new_bhead = nullptr;
}
if (fd->flags & FD_FLAGS_IS_MEMFILE) {
@@ -892,13 +904,13 @@ static BHeadN *get_bhead(FileData *fd)
BHead *blo_bhead_first(FileData *fd)
{
BHeadN *new_bhead;
- BHead *bhead = NULL;
+ BHead *bhead = nullptr;
/* Rewind the file
* Read in a new block if necessary
*/
- new_bhead = fd->bhead_list.first;
- if (new_bhead == NULL) {
+ new_bhead = static_cast<BHeadN *>(fd->bhead_list.first);
+ if (new_bhead == nullptr) {
new_bhead = get_bhead(fd);
}
@@ -914,13 +926,13 @@ BHead *blo_bhead_prev(FileData *UNUSED(fd), BHead *thisblock)
BHeadN *bheadn = BHEADN_FROM_BHEAD(thisblock);
BHeadN *prev = bheadn->prev;
- return (prev) ? &prev->bhead : NULL;
+ return (prev) ? &prev->bhead : nullptr;
}
BHead *blo_bhead_next(FileData *fd, BHead *thisblock)
{
- BHeadN *new_bhead = NULL;
- BHead *bhead = NULL;
+ BHeadN *new_bhead = nullptr;
+ BHead *bhead = nullptr;
if (thisblock) {
/* bhead is actually a sub part of BHeadN
@@ -929,7 +941,7 @@ BHead *blo_bhead_next(FileData *fd, BHead *thisblock)
/* get the next BHeadN. If it doesn't exist we read in the next one */
new_bhead = new_bhead->next;
- if (new_bhead == NULL) {
+ if (new_bhead == nullptr) {
new_bhead = get_bhead(fd);
}
}
@@ -970,14 +982,15 @@ static bool blo_bhead_read_data(FileData *fd, BHead *thisblock, void *buf)
static BHead *blo_bhead_read_full(FileData *fd, BHead *thisblock)
{
BHeadN *new_bhead = BHEADN_FROM_BHEAD(thisblock);
- BHeadN *new_bhead_data = MEM_mallocN(sizeof(BHeadN) + new_bhead->bhead.len, "new_bhead");
+ BHeadN *new_bhead_data = static_cast<BHeadN *>(
+ MEM_mallocN(sizeof(BHeadN) + new_bhead->bhead.len, "new_bhead"));
new_bhead_data->bhead = new_bhead->bhead;
new_bhead_data->file_offset = new_bhead->file_offset;
new_bhead_data->has_data = true;
new_bhead_data->is_memchunk_identical = false;
if (!blo_bhead_read_data(fd, thisblock, new_bhead_data + 1)) {
MEM_freeN(new_bhead_data);
- return NULL;
+ return nullptr;
}
return &new_bhead_data->bhead;
}
@@ -993,7 +1006,7 @@ AssetMetaData *blo_bhead_id_asset_data_address(const FileData *fd, const BHead *
BLI_assert(blo_bhead_is_id_valid_type(bhead));
return (fd->id_asset_data_offset >= 0) ?
*(AssetMetaData **)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_asset_data_offset) :
- NULL;
+ nullptr;
}
static void decode_blender_header(FileData *fd)
@@ -1053,7 +1066,7 @@ static bool read_file_dna(FileData *fd, const char **r_error_message)
}
/* We can't use read_global because this needs 'DNA1' to be decoded,
* however the first 4 chars are _always_ the subversion. */
- FileGlobal *fg = (void *)&bhead[1];
+ FileGlobal *fg = reinterpret_cast<FileGlobal *>(&bhead[1]);
BLI_STATIC_ASSERT(offsetof(FileGlobal, subvstr) == 0, "Must be first: subvstr")
char num[5];
memcpy(num, fg->subvstr, 4);
@@ -1093,7 +1106,7 @@ static bool read_file_dna(FileData *fd, const char **r_error_message)
static int *read_file_thumbnail(FileData *fd)
{
BHead *bhead;
- int *blend_thumb = NULL;
+ int *blend_thumb = nullptr;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
if (bhead->code == TEST) {
@@ -1138,9 +1151,9 @@ static int *read_file_thumbnail(FileData *fd)
static FileData *filedata_new(BlendFileReadReport *reports)
{
- BLI_assert(reports != NULL);
+ BLI_assert(reports != nullptr);
- FileData *fd = MEM_callocN(sizeof(FileData), "FileData");
+ FileData *fd = static_cast<FileData *>(MEM_callocN(sizeof(FileData), "FileData"));
fd->memsdna = DNA_sdna_current_get();
@@ -1158,19 +1171,19 @@ static FileData *blo_decode_and_check(FileData *fd, ReportList *reports)
decode_blender_header(fd);
if (fd->flags & FD_FLAGS_FILE_OK) {
- const char *error_message = NULL;
+ const char *error_message = nullptr;
if (read_file_dna(fd, &error_message) == false) {
BKE_reportf(
reports, RPT_ERROR, "Failed to read blend file '%s': %s", fd->relabase, error_message);
blo_filedata_free(fd);
- fd = NULL;
+ fd = nullptr;
}
}
else {
BKE_reportf(
reports, RPT_ERROR, "Failed to read blend file '%s', not a blend file", fd->relabase);
blo_filedata_free(fd);
- fd = NULL;
+ fd = nullptr;
}
return fd;
@@ -1182,11 +1195,11 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath,
{
char header[7];
FileReader *rawfile = BLI_filereader_new_file(filedes);
- FileReader *file = NULL;
+ FileReader *file = nullptr;
errno = 0;
/* If opening the file failed or we can't read the header, give up. */
- if (rawfile == NULL || rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
+ if (rawfile == nullptr || rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) {
BKE_reportf(reports->reports,
RPT_WARNING,
"Unable to read '%s': %s",
@@ -1198,7 +1211,7 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath,
else {
close(filedes);
}
- return NULL;
+ return nullptr;
}
/* Rewind the file after reading the header. */
@@ -1208,32 +1221,32 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath,
if (memcmp(header, "BLENDER", sizeof(header)) == 0) {
/* Try opening the file with memory-mapped IO. */
file = BLI_filereader_new_mmap(filedes);
- if (file == NULL) {
+ if (file == nullptr) {
/* mmap failed, so just keep using rawfile. */
file = rawfile;
- rawfile = NULL;
+ rawfile = nullptr;
}
}
else if (BLI_file_magic_is_gzip(header)) {
file = BLI_filereader_new_gzip(rawfile);
- if (file != NULL) {
- rawfile = NULL; /* The `Gzip` #FileReader takes ownership of `rawfile`. */
+ if (file != nullptr) {
+ rawfile = nullptr; /* The `Gzip` #FileReader takes ownership of `rawfile`. */
}
}
else if (BLI_file_magic_is_zstd(header)) {
file = BLI_filereader_new_zstd(rawfile);
- if (file != NULL) {
- rawfile = NULL; /* The `Zstd` #FileReader takes ownership of `rawfile`. */
+ if (file != nullptr) {
+ rawfile = nullptr; /* The `Zstd` #FileReader takes ownership of `rawfile`. */
}
}
/* Clean up `rawfile` if it wasn't taken over. */
- if (rawfile != NULL) {
+ if (rawfile != nullptr) {
rawfile->close(rawfile);
}
- if (file == NULL) {
+ if (file == nullptr) {
BKE_reportf(reports->reports, RPT_WARNING, "Unrecognized file format '%s'", filepath);
- return NULL;
+ return nullptr;
}
FileData *fd = filedata_new(reports);
@@ -1252,7 +1265,7 @@ static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileRead
"Unable to open '%s': %s",
filepath,
errno ? strerror(errno) : TIP_("unknown error reading file"));
- return NULL;
+ return nullptr;
}
return blo_filedata_from_file_descriptor(filepath, reports, file);
}
@@ -1260,13 +1273,13 @@ static FileData *blo_filedata_from_file_open(const char *filepath, BlendFileRead
FileData *blo_filedata_from_file(const char *filepath, BlendFileReadReport *reports)
{
FileData *fd = blo_filedata_from_file_open(filepath, reports);
- if (fd != NULL) {
+ if (fd != nullptr) {
/* needed for library_append and read_libraries */
BLI_strncpy(fd->relabase, filepath, sizeof(fd->relabase));
return blo_decode_and_check(fd, reports->reports);
}
- return NULL;
+ return nullptr;
}
/**
@@ -1275,15 +1288,16 @@ FileData *blo_filedata_from_file(const char *filepath, BlendFileReadReport *repo
*/
static FileData *blo_filedata_from_file_minimal(const char *filepath)
{
- FileData *fd = blo_filedata_from_file_open(filepath, &(BlendFileReadReport){.reports = NULL});
- if (fd != NULL) {
+ BlendFileReadReport read_report{};
+ FileData *fd = blo_filedata_from_file_open(filepath, &read_report);
+ if (fd != nullptr) {
decode_blender_header(fd);
if (fd->flags & FD_FLAGS_FILE_OK) {
return fd;
}
blo_filedata_free(fd);
}
- return NULL;
+ return nullptr;
}
FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadReport *reports)
@@ -1291,23 +1305,23 @@ FileData *blo_filedata_from_memory(const void *mem, int memsize, BlendFileReadRe
if (!mem || memsize < SIZEOFBLENDERHEADER) {
BKE_report(
reports->reports, RPT_WARNING, (mem) ? TIP_("Unable to read") : TIP_("Unable to open"));
- return NULL;
+ return nullptr;
}
FileReader *mem_file = BLI_filereader_new_memory(mem, memsize);
FileReader *file = mem_file;
- if (BLI_file_magic_is_gzip(mem)) {
+ if (BLI_file_magic_is_gzip(static_cast<const char *>(mem))) {
file = BLI_filereader_new_gzip(mem_file);
}
- else if (BLI_file_magic_is_zstd(mem)) {
+ else if (BLI_file_magic_is_zstd(static_cast<const char *>(mem))) {
file = BLI_filereader_new_zstd(mem_file);
}
- if (file == NULL) {
+ if (file == nullptr) {
/* Compression initialization failed. */
mem_file->close(mem_file);
- return NULL;
+ return nullptr;
}
FileData *fd = filedata_new(reports);
@@ -1322,7 +1336,7 @@ FileData *blo_filedata_from_memfile(MemFile *memfile,
{
if (!memfile) {
BKE_report(reports->reports, RPT_WARNING, "Unable to open blend <memory>");
- return NULL;
+ return nullptr;
}
FileData *fd = filedata_new(reports);
@@ -1343,7 +1357,7 @@ void blo_filedata_free(FileData *fd)
#else
/* Sanity check we're not keeping memory we don't need. */
LISTBASE_FOREACH_MUTABLE (BHeadN *, new_bhead, &fd->bhead_list) {
- if (fd->file->seek != NULL && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) {
+ if (fd->file->seek != nullptr && BHEAD_USE_READ_ON_DEMAND(&new_bhead->bhead)) {
BLI_assert(new_bhead->has_data == 0);
}
MEM_freeN(new_bhead);
@@ -1373,7 +1387,7 @@ void blo_filedata_free(FileData *fd)
if (fd->libmap && !(fd->flags & FD_FLAGS_NOT_MY_LIBMAP)) {
oldnewmap_free(fd->libmap);
}
- if (fd->old_idmap != NULL) {
+ if (fd->old_idmap != nullptr) {
BKE_main_idmap_destroy(fd->old_idmap);
}
blo_cache_storage_end(fd);
@@ -1383,7 +1397,7 @@ void blo_filedata_free(FileData *fd)
#ifdef USE_GHASH_BHEAD
if (fd->bhead_idname_hash) {
- BLI_ghash_free(fd->bhead_idname_hash, NULL, NULL);
+ BLI_ghash_free(fd->bhead_idname_hash, nullptr, nullptr);
}
#endif
@@ -1399,7 +1413,7 @@ void blo_filedata_free(FileData *fd)
bool BLO_has_bfile_extension(const char *str)
{
- const char *ext_test[4] = {".blend", ".ble", ".blend.gz", NULL};
+ const char *ext_test[4] = {".blend", ".ble", ".blend.gz", nullptr};
return BLI_path_extension_check_array(str, ext_test);
}
@@ -1408,14 +1422,14 @@ bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, cha
/* We might get some data names with slashes,
* so we have to go up in path until we find blend file itself,
* then we know next path item is group, and everything else is data name. */
- char *slash = NULL, *prev_slash = NULL, c = '\0';
+ char *slash = nullptr, *prev_slash = nullptr, c = '\0';
r_dir[0] = '\0';
if (r_group) {
- *r_group = NULL;
+ *r_group = nullptr;
}
if (r_name) {
- *r_name = NULL;
+ *r_name = nullptr;
}
/* if path leads to an existing directory, we can be sure we're not (in) a library */
@@ -1466,18 +1480,18 @@ bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, cha
BlendThumbnail *BLO_thumbnail_from_file(const char *filepath)
{
FileData *fd;
- BlendThumbnail *data = NULL;
+ BlendThumbnail *data = nullptr;
int *fd_data;
fd = blo_filedata_from_file_minimal(filepath);
- fd_data = fd ? read_file_thumbnail(fd) : NULL;
+ fd_data = fd ? read_file_thumbnail(fd) : nullptr;
if (fd_data) {
const int width = fd_data[0];
const int height = fd_data[1];
if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) {
const size_t data_size = BLEN_THUMB_MEMSIZE(width, height);
- data = MEM_mallocN(data_size, __func__);
+ data = static_cast<BlendThumbnail *>(MEM_mallocN(data_size, __func__));
if (data) {
BLI_assert((data_size - sizeof(*data)) ==
(BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*fd_data) * 2)));
@@ -1538,15 +1552,17 @@ void *blo_do_versions_newlibadr(FileData *fd, const void *lib, const void *adr)
}
/* increases user number */
-static void change_link_placeholder_to_real_ID_pointer_fd(FileData *fd, const void *old, void *new)
+static void change_link_placeholder_to_real_ID_pointer_fd(FileData *fd,
+ const void *old,
+ void *newp)
{
for (int i = 0; i < fd->libmap->nentries; i++) {
OldNew *entry = &fd->libmap->entries[i];
if (old == entry->newp && entry->nr == ID_LINK_PLACEHOLDER) {
- entry->newp = new;
- if (new) {
- entry->nr = GS(((ID *)new)->name);
+ entry->newp = newp;
+ if (newp) {
+ entry->nr = GS(((ID *)newp)->name);
}
}
}
@@ -1555,7 +1571,7 @@ static void change_link_placeholder_to_real_ID_pointer_fd(FileData *fd, const vo
static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist,
FileData *basefd,
void *old,
- void *new)
+ void *newp)
{
LISTBASE_FOREACH (Main *, mainptr, mainlist) {
FileData *fd;
@@ -1568,7 +1584,7 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist,
}
if (fd) {
- change_link_placeholder_to_real_ID_pointer_fd(fd, old, new);
+ change_link_placeholder_to_real_ID_pointer_fd(fd, old, newp);
}
}
}
@@ -1630,32 +1646,32 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain)
/* used entries were restored, so we put them to zero */
for (int i = 0; i < fd->packedmap->nentries; i++, entry++) {
if (entry->nr > 0) {
- entry->newp = NULL;
+ entry->newp = nullptr;
}
}
LISTBASE_FOREACH (Image *, ima, &oldmain->images) {
- ima->packedfile = newpackedadr(fd, ima->packedfile);
+ ima->packedfile = static_cast<PackedFile *>(newpackedadr(fd, ima->packedfile));
LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) {
- imapf->packedfile = newpackedadr(fd, imapf->packedfile);
+ imapf->packedfile = static_cast<PackedFile *>(newpackedadr(fd, imapf->packedfile));
}
}
LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) {
- vfont->packedfile = newpackedadr(fd, vfont->packedfile);
+ vfont->packedfile = static_cast<PackedFile *>(newpackedadr(fd, vfont->packedfile));
}
LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) {
- sound->packedfile = newpackedadr(fd, sound->packedfile);
+ sound->packedfile = static_cast<PackedFile *>(newpackedadr(fd, sound->packedfile));
}
LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) {
- lib->packedfile = newpackedadr(fd, lib->packedfile);
+ lib->packedfile = static_cast<PackedFile *>(newpackedadr(fd, lib->packedfile));
}
LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) {
- volume->packedfile = newpackedadr(fd, volume->packedfile);
+ volume->packedfile = static_cast<PackedFile *>(newpackedadr(fd, volume->packedfile));
}
}
@@ -1667,7 +1683,7 @@ void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd)
int i = set_listbasepointers(ptr, lbarray);
while (i--) {
LISTBASE_FOREACH (ID *, id, lbarray[i]) {
- oldnewmap_insert(fd->libmap, id, id, GS(id->name));
+ oldnewmap_lib_insert(fd, id, id, GS(id->name));
}
}
}
@@ -1677,10 +1693,10 @@ void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd)
void blo_make_old_idmap_from_main(FileData *fd, Main *bmain)
{
- if (fd->old_idmap != NULL) {
+ if (fd->old_idmap != nullptr) {
BKE_main_idmap_destroy(fd->old_idmap);
}
- fd->old_idmap = BKE_main_idmap_create(bmain, false, NULL, MAIN_IDMAP_TYPE_UUID);
+ fd->old_idmap = BKE_main_idmap_create(bmain, false, nullptr, MAIN_IDMAP_TYPE_UUID);
}
typedef struct BLOCacheStorage {
@@ -1700,13 +1716,14 @@ static void blo_cache_storage_entry_register(
BLI_assert(key->id_session_uuid == id->session_uuid);
UNUSED_VARS_NDEBUG(id);
- BLOCacheStorage *cache_storage = cache_storage_v;
+ BLOCacheStorage *cache_storage = static_cast<BLOCacheStorage *>(cache_storage_v);
BLI_assert(!BLI_ghash_haskey(cache_storage->cache_map, key));
- IDCacheKey *storage_key = BLI_memarena_alloc(cache_storage->memarena, sizeof(*storage_key));
+ IDCacheKey *storage_key = static_cast<IDCacheKey *>(
+ BLI_memarena_alloc(cache_storage->memarena, sizeof(*storage_key)));
*storage_key = *key;
- BLOCacheStorageValue *storage_value = BLI_memarena_alloc(cache_storage->memarena,
- sizeof(*storage_value));
+ BLOCacheStorageValue *storage_value = static_cast<BLOCacheStorageValue *>(
+ BLI_memarena_alloc(cache_storage->memarena, sizeof(*storage_value)));
storage_value->cache_v = *cache_p;
storage_value->new_usage_count = 0;
BLI_ghash_insert(cache_storage->cache_map, storage_key, storage_value);
@@ -1716,21 +1733,22 @@ static void blo_cache_storage_entry_register(
static void blo_cache_storage_entry_restore_in_new(
ID *UNUSED(id), const IDCacheKey *key, void **cache_p, uint flags, void *cache_storage_v)
{
- BLOCacheStorage *cache_storage = cache_storage_v;
+ BLOCacheStorage *cache_storage = static_cast<BLOCacheStorage *>(cache_storage_v);
- if (cache_storage == NULL) {
+ if (cache_storage == nullptr) {
/* In non-undo case, only clear the pointer if it is a purely runtime one.
* If it may be stored in a persistent way in the .blend file, direct_link code is responsible
* to properly deal with it. */
if ((flags & IDTYPE_CACHE_CB_FLAGS_PERSISTENT) == 0) {
- *cache_p = NULL;
+ *cache_p = nullptr;
}
return;
}
- BLOCacheStorageValue *storage_value = BLI_ghash_lookup(cache_storage->cache_map, key);
- if (storage_value == NULL) {
- *cache_p = NULL;
+ BLOCacheStorageValue *storage_value = static_cast<BLOCacheStorageValue *>(
+ BLI_ghash_lookup(cache_storage->cache_map, key));
+ if (storage_value == nullptr) {
+ *cache_p = nullptr;
return;
}
storage_value->new_usage_count++;
@@ -1744,17 +1762,18 @@ static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id),
uint UNUSED(flags),
void *cache_storage_v)
{
- BLOCacheStorage *cache_storage = cache_storage_v;
+ BLOCacheStorage *cache_storage = static_cast<BLOCacheStorage *>(cache_storage_v);
- BLOCacheStorageValue *storage_value = BLI_ghash_lookup(cache_storage->cache_map, key);
- if (storage_value == NULL) {
- *cache_p = NULL;
+ BLOCacheStorageValue *storage_value = static_cast<BLOCacheStorageValue *>(
+ BLI_ghash_lookup(cache_storage->cache_map, key));
+ if (storage_value == nullptr) {
+ *cache_p = nullptr;
return;
}
/* If that cache has been restored into some new ID, we want to remove it from old one, otherwise
* keep it there so that it gets properly freed together with its ID. */
if (storage_value->new_usage_count != 0) {
- *cache_p = NULL;
+ *cache_p = nullptr;
}
else {
BLI_assert(*cache_p == storage_value->cache_v);
@@ -1764,21 +1783,22 @@ static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id),
void blo_cache_storage_init(FileData *fd, Main *bmain)
{
if (fd->flags & FD_FLAGS_IS_MEMFILE) {
- BLI_assert(fd->cache_storage == NULL);
- fd->cache_storage = MEM_mallocN(sizeof(*fd->cache_storage), __func__);
+ BLI_assert(fd->cache_storage == nullptr);
+ fd->cache_storage = static_cast<BLOCacheStorage *>(
+ MEM_mallocN(sizeof(*fd->cache_storage), __func__));
fd->cache_storage->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
fd->cache_storage->cache_map = BLI_ghash_new(
BKE_idtype_cache_key_hash, BKE_idtype_cache_key_cmp, __func__);
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
- ID *id = lb->first;
- if (id == NULL) {
+ ID *id = static_cast<ID *>(lb->first);
+ if (id == nullptr) {
continue;
}
const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id);
- if (type_info->foreach_cache == NULL) {
+ if (type_info->foreach_cache == nullptr) {
continue;
}
@@ -1793,22 +1813,22 @@ void blo_cache_storage_init(FileData *fd, Main *bmain)
FOREACH_MAIN_LISTBASE_END;
}
else {
- fd->cache_storage = NULL;
+ fd->cache_storage = nullptr;
}
}
void blo_cache_storage_old_bmain_clear(FileData *fd, Main *bmain_old)
{
- if (fd->cache_storage != NULL) {
+ if (fd->cache_storage != nullptr) {
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain_old, lb) {
- ID *id = lb->first;
- if (id == NULL) {
+ ID *id = static_cast<ID *>(lb->first);
+ if (id == nullptr) {
continue;
}
const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(id);
- if (type_info->foreach_cache == NULL) {
+ if (type_info->foreach_cache == nullptr) {
continue;
}
@@ -1826,11 +1846,11 @@ void blo_cache_storage_old_bmain_clear(FileData *fd, Main *bmain_old)
void blo_cache_storage_end(FileData *fd)
{
- if (fd->cache_storage != NULL) {
- BLI_ghash_free(fd->cache_storage->cache_map, NULL, NULL);
+ if (fd->cache_storage != nullptr) {
+ BLI_ghash_free(fd->cache_storage->cache_map, nullptr, nullptr);
BLI_memarena_free(fd->cache_storage->memarena);
MEM_freeN(fd->cache_storage);
- fd->cache_storage = NULL;
+ fd->cache_storage = nullptr;
}
}
@@ -1858,7 +1878,7 @@ static void switch_endian_structs(const struct SDNA *filesdna, BHead *bhead)
static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
{
- void *temp = NULL;
+ void *temp = nullptr;
if (bh->len) {
#ifdef USE_BHEAD_READ_ON_DEMAND
@@ -1870,9 +1890,9 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
#ifdef USE_BHEAD_READ_ON_DEMAND
if (BHEADN_FROM_BHEAD(bh)->has_data == false) {
bh = blo_bhead_read_full(fd, bh);
- if (UNLIKELY(bh == NULL)) {
+ if (UNLIKELY(bh == nullptr)) {
fd->flags &= ~FD_FLAGS_FILE_OK;
- return NULL;
+ return nullptr;
}
}
#endif
@@ -1884,9 +1904,9 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
#ifdef USE_BHEAD_READ_ON_DEMAND
if (BHEADN_FROM_BHEAD(bh)->has_data == false) {
bh = blo_bhead_read_full(fd, bh);
- if (UNLIKELY(bh == NULL)) {
+ if (UNLIKELY(bh == nullptr)) {
fd->flags &= ~FD_FLAGS_FILE_OK;
- return NULL;
+ return nullptr;
}
}
#endif
@@ -1905,7 +1925,7 @@ static void *read_struct(FileData *fd, BHead *bh, const char *blockname)
if (UNLIKELY(!blo_bhead_read_data(fd, bh, temp))) {
fd->flags &= ~FD_FLAGS_FILE_OK;
MEM_freeN(temp);
- temp = NULL;
+ temp = nullptr;
}
}
#else
@@ -1930,7 +1950,7 @@ static const void *peek_struct_undo(FileData *fd, BHead *bhead)
{
BLI_assert(fd->flags & FD_FLAGS_IS_MEMFILE);
UNUSED_VARS_NDEBUG(fd);
- return (bhead->len) ? (const void *)(bhead + 1) : NULL;
+ return (bhead->len) ? (const void *)(bhead + 1) : nullptr;
}
static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */
@@ -1947,14 +1967,14 @@ static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */
}
lb->first = poin;
- ln = lb->first;
- prev = NULL;
+ ln = static_cast<Link *>(lb->first);
+ prev = nullptr;
while (ln) {
poin = newdataadr(fd, ln->next);
if (ln->next) {
oldnewmap_insert(fd->globmap, ln->next, poin, 0);
}
- ln->next = poin;
+ ln->next = static_cast<Link *>(poin);
ln->prev = prev;
prev = ln;
ln = ln->next;
@@ -1975,14 +1995,14 @@ static void lib_link_id_embedded_id(BlendLibReader *reader, ID *id)
/* Handle 'private IDs'. */
bNodeTree *nodetree = ntreeFromID(id);
- if (nodetree != NULL) {
+ if (nodetree != nullptr) {
lib_link_id(reader, &nodetree->id);
ntreeBlendReadLib(reader, nodetree);
}
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
- if (scene->master_collection != NULL) {
+ if (scene->master_collection != nullptr) {
lib_link_id(reader, &scene->master_collection->id);
BKE_collection_blend_read_lib(reader, scene->master_collection);
}
@@ -1991,12 +2011,12 @@ static void lib_link_id_embedded_id(BlendLibReader *reader, ID *id)
static void lib_link_id(BlendLibReader *reader, ID *id)
{
- /* NOTE: WM IDProperties are never written to file, hence they should always be NULL here. */
- BLI_assert((GS(id->name) != ID_WM) || id->properties == NULL);
- IDP_BlendReadLib(reader, id->properties);
+ /* NOTE: WM IDProperties are never written to file, hence they should always be nullptr here. */
+ BLI_assert((GS(id->name) != ID_WM) || id->properties == nullptr);
+ IDP_BlendReadLib(reader, id->lib, id->properties);
AnimData *adt = BKE_animdata_from_id(id);
- if (adt != NULL) {
+ if (adt != nullptr) {
BKE_animdata_blend_read_lib(reader, id, adt);
}
@@ -2011,7 +2031,8 @@ static void lib_link_id(BlendLibReader *reader, ID *id)
static void direct_link_id_override_property_operation_cb(BlendDataReader *reader, void *data)
{
- IDOverrideLibraryPropertyOperation *opop = data;
+ IDOverrideLibraryPropertyOperation *opop = static_cast<IDOverrideLibraryPropertyOperation *>(
+ data);
BLO_read_data_address(reader, &opop->subitem_reference_name);
BLO_read_data_address(reader, &opop->subitem_local_name);
@@ -2021,7 +2042,7 @@ static void direct_link_id_override_property_operation_cb(BlendDataReader *reade
static void direct_link_id_override_property_cb(BlendDataReader *reader, void *data)
{
- IDOverrideLibraryProperty *op = data;
+ IDOverrideLibraryProperty *op = static_cast<IDOverrideLibraryProperty *>(data);
BLO_read_data_address(reader, &op->rna_path);
@@ -2040,26 +2061,27 @@ static void direct_link_id_embedded_id(BlendDataReader *reader,
{
/* Handle 'private IDs'. */
bNodeTree **nodetree = BKE_ntree_ptr_from_id(id);
- if (nodetree != NULL && *nodetree != NULL) {
+ if (nodetree != nullptr && *nodetree != nullptr) {
BLO_read_data_address(reader, nodetree);
direct_link_id_common(reader,
current_library,
(ID *)*nodetree,
- id_old != NULL ? (ID *)ntreeFromID(id_old) : NULL,
+ id_old != nullptr ? (ID *)ntreeFromID(id_old) : nullptr,
0);
- ntreeBlendReadData(reader, *nodetree);
+ ntreeBlendReadData(reader, id, *nodetree);
}
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
- if (scene->master_collection != NULL) {
+ if (scene->master_collection != nullptr) {
BLO_read_data_address(reader, &scene->master_collection);
direct_link_id_common(reader,
current_library,
&scene->master_collection->id,
- id_old != NULL ? &((Scene *)id_old)->master_collection->id : NULL,
+ id_old != nullptr ? &((Scene *)id_old)->master_collection->id :
+ nullptr,
0);
- BKE_collection_blend_read_data(reader, scene->master_collection);
+ BKE_collection_blend_read_data(reader, scene->master_collection, &scene->id);
}
}
}
@@ -2085,7 +2107,7 @@ static int direct_link_id_restore_recalc(const FileData *fd,
* flush back changes to the original datablock. */
int recalc = id_target->recalc;
- if (id_current == NULL) {
+ if (id_current == nullptr) {
/* ID does not currently exist in the database, so also will not exist in
* the dependency graphs. That means it will be newly created and as a
* result also fully re-evaluated regardless of the recalc flag set here. */
@@ -2137,15 +2159,15 @@ static void direct_link_id_common(
id->lib = current_library;
id->us = ID_FAKE_USERS(id);
id->icon_id = 0;
- id->newid = NULL; /* Needed because .blend may have been saved with crap value here... */
- id->orig_id = NULL;
- id->py_instance = NULL;
+ id->newid = nullptr; /* Needed because .blend may have been saved with crap value here... */
+ id->orig_id = nullptr;
+ id->py_instance = nullptr;
/* Initialize with provided tag. */
id->tag = tag;
if (ID_IS_LINKED(id)) {
- id->library_weak_reference = NULL;
+ id->library_weak_reference = nullptr;
}
else {
BLO_read_data_address(reader, &id->library_weak_reference);
@@ -2195,10 +2217,10 @@ static void direct_link_id_common(
if (id->override_library) {
BLO_read_data_address(reader, &id->override_library);
/* Work around file corruption on writing, see T86853. */
- if (id->override_library != NULL) {
+ if (id->override_library != nullptr) {
BLO_read_list_cb(
reader, &id->override_library->properties, direct_link_id_override_property_cb);
- id->override_library->runtime = NULL;
+ id->override_library->runtime = nullptr;
}
}
@@ -2246,7 +2268,7 @@ static bool scene_validate_setscene__liblink(Scene *sce, const int totscene)
Scene *sce_iter;
int a;
- if (sce->set == NULL) {
+ if (sce->set == nullptr) {
return true;
}
@@ -2264,7 +2286,7 @@ static bool scene_validate_setscene__liblink(Scene *sce, const int totscene)
}
if (a > totscene) {
- sce->set = NULL;
+ sce->set = nullptr;
return false;
}
}
@@ -2338,7 +2360,7 @@ static void *restore_pointer_by_name_main(Main *mainp, ID *id, ePointerUserMode
return idn;
}
}
- return NULL;
+ return nullptr;
}
#endif
@@ -2363,7 +2385,7 @@ static void *restore_pointer_by_name(struct IDNameLib_Map *id_map, ID *id, ePoin
}
return idn;
}
- return NULL;
+ return nullptr;
#else
Main *mainp = BKE_main_idmap_main_get(id_map);
return restore_pointer_by_name_main(mainp, id, user);
@@ -2374,13 +2396,13 @@ static void lib_link_seq_clipboard_pt_restore(ID *id, struct IDNameLib_Map *id_m
{
if (id) {
/* clipboard must ensure this */
- BLI_assert(id->newid != NULL);
- id->newid = restore_pointer_by_name(id_map, id->newid, USER_REAL);
+ BLI_assert(id->newid != nullptr);
+ id->newid = static_cast<ID *>(restore_pointer_by_name(id_map, id->newid, USER_REAL));
}
}
static bool lib_link_seq_clipboard_cb(Sequence *seq, void *arg_pt)
{
- struct IDNameLib_Map *id_map = arg_pt;
+ struct IDNameLib_Map *id_map = static_cast<IDNameLib_Map *>(arg_pt);
lib_link_seq_clipboard_pt_restore((ID *)seq->scene, id_map);
lib_link_seq_clipboard_pt_restore((ID *)seq->scene_camera, id_map);
@@ -2400,7 +2422,7 @@ static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data)
{
const int cb_flag = cb_data->cb_flag;
ID **id_pointer = cb_data->id_pointer;
- if (cb_flag & IDWALK_CB_EMBEDDED || *id_pointer == NULL) {
+ if (cb_flag & IDWALK_CB_EMBEDDED || *id_pointer == nullptr) {
return IDWALK_RET_NOP;
}
@@ -2417,13 +2439,13 @@ static int lib_link_main_data_restore_cb(LibraryIDLinkCallbackData *cb_data)
}
}
- struct IDNameLib_Map *id_map = cb_data->user_data;
+ struct IDNameLib_Map *id_map = static_cast<IDNameLib_Map *>(cb_data->user_data);
- /* NOTE: Handling of usercount here is really bad, defining its own system...
+ /* NOTE: Handling of user-count here is really bad, defining its own system...
* Will have to be refactored at some point, but that is not top priority task for now.
* And all user-counts are properly recomputed at the end of the undo management code anyway. */
- *id_pointer = restore_pointer_by_name(
- id_map, *id_pointer, (cb_flag & IDWALK_CB_USER_ONE) ? USER_REAL : USER_IGNORE);
+ *id_pointer = static_cast<ID *>(restore_pointer_by_name(
+ id_map, *id_pointer, (cb_flag & IDWALK_CB_USER_ONE) ? USER_REAL : USER_IGNORE));
return IDWALK_RET_NOP;
}
@@ -2439,8 +2461,8 @@ static void lib_link_main_data_restore(struct IDNameLib_Map *id_map, Main *newma
static void lib_link_wm_xr_data_restore(struct IDNameLib_Map *id_map, wmXrData *xr_data)
{
- xr_data->session_settings.base_pose_object = restore_pointer_by_name(
- id_map, (ID *)xr_data->session_settings.base_pose_object, USER_REAL);
+ xr_data->session_settings.base_pose_object = static_cast<Object *>(restore_pointer_by_name(
+ id_map, (ID *)xr_data->session_settings.base_pose_object, USER_REAL));
}
static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, ViewLayer *view_layer)
@@ -2452,25 +2474,26 @@ static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, View
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
- if (v3d->camera == NULL || v3d->scenelock) {
+ if (v3d->camera == nullptr || v3d->scenelock) {
v3d->camera = scene->camera;
}
if (v3d->localvd) {
- Base *base = NULL;
+ Base *base = nullptr;
v3d->localvd->camera = scene->camera;
/* Local-view can become invalid during undo/redo steps,
* so we exit it when no could be found. */
- for (base = view_layer->object_bases.first; base; base = base->next) {
+ for (base = static_cast<Base *>(view_layer->object_bases.first); base;
+ base = base->next) {
if (base->local_view_bits & v3d->local_view_uuid) {
break;
}
}
- if (base == NULL) {
+ if (base == nullptr) {
MEM_freeN(v3d->localvd);
- v3d->localvd = NULL;
+ v3d->localvd = nullptr;
v3d->local_view_uuid = 0;
/* Region-base storage is different depending if the space is active. */
@@ -2478,10 +2501,10 @@ static void lib_link_window_scene_data_restore(wmWindow *win, Scene *scene, View
&sl->regionbase;
LISTBASE_FOREACH (ARegion *, region, regionbase) {
if (region->regiontype == RGN_TYPE_WINDOW) {
- RegionView3D *rv3d = region->regiondata;
+ RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
if (rv3d->localvd) {
MEM_freeN(rv3d->localvd);
- rv3d->localvd = NULL;
+ rv3d->localvd = nullptr;
}
}
}
@@ -2505,19 +2528,22 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
- v3d->camera = restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL);
- v3d->ob_center = restore_pointer_by_name(id_map, (ID *)v3d->ob_center, USER_REAL);
+ v3d->camera = static_cast<Object *>(
+ restore_pointer_by_name(id_map, (ID *)v3d->camera, USER_REAL));
+ v3d->ob_center = static_cast<Object *>(
+ restore_pointer_by_name(id_map, (ID *)v3d->ob_center, USER_REAL));
}
else if (sl->spacetype == SPACE_GRAPH) {
SpaceGraph *sipo = (SpaceGraph *)sl;
bDopeSheet *ads = sipo->ads;
if (ads) {
- ads->source = restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL);
+ ads->source = static_cast<ID *>(
+ restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL));
if (ads->filter_grp) {
- ads->filter_grp = restore_pointer_by_name(
- id_map, (ID *)ads->filter_grp, USER_IGNORE);
+ ads->filter_grp = static_cast<Collection *>(
+ restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE));
}
}
@@ -2528,8 +2554,9 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
}
else if (sl->spacetype == SPACE_PROPERTIES) {
SpaceProperties *sbuts = (SpaceProperties *)sl;
- sbuts->pinid = restore_pointer_by_name(id_map, sbuts->pinid, USER_IGNORE);
- if (sbuts->pinid == NULL) {
+ sbuts->pinid = static_cast<ID *>(
+ restore_pointer_by_name(id_map, sbuts->pinid, USER_IGNORE));
+ if (sbuts->pinid == nullptr) {
sbuts->flag &= ~SB_PIN_CONTEXT;
}
@@ -2539,19 +2566,20 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
}
else if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
- sfile->op = NULL;
+ sfile->op = nullptr;
sfile->tags = FILE_TAG_REBUILD_MAIN_FILES;
}
else if (sl->spacetype == SPACE_ACTION) {
SpaceAction *saction = (SpaceAction *)sl;
- saction->action = restore_pointer_by_name(id_map, (ID *)saction->action, USER_REAL);
- saction->ads.source = restore_pointer_by_name(
- id_map, (ID *)saction->ads.source, USER_REAL);
+ saction->action = static_cast<bAction *>(
+ restore_pointer_by_name(id_map, (ID *)saction->action, USER_REAL));
+ saction->ads.source = static_cast<ID *>(
+ restore_pointer_by_name(id_map, (ID *)saction->ads.source, USER_REAL));
if (saction->ads.filter_grp) {
- saction->ads.filter_grp = restore_pointer_by_name(
- id_map, (ID *)saction->ads.filter_grp, USER_IGNORE);
+ saction->ads.filter_grp = static_cast<Collection *>(
+ restore_pointer_by_name(id_map, (ID *)saction->ads.filter_grp, USER_IGNORE));
}
/* force recalc of list of channels, potentially updating the active action
@@ -2562,11 +2590,12 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
else if (sl->spacetype == SPACE_IMAGE) {
SpaceImage *sima = (SpaceImage *)sl;
- sima->image = restore_pointer_by_name(id_map, (ID *)sima->image, USER_REAL);
+ sima->image = static_cast<Image *>(
+ restore_pointer_by_name(id_map, (ID *)sima->image, USER_REAL));
/* this will be freed, not worth attempting to find same scene,
* since it gets initialized later */
- sima->iuser.scene = NULL;
+ sima->iuser.scene = nullptr;
#if 0
/* Those are allocated and freed by space code, no need to handle them here. */
@@ -2580,9 +2609,10 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so assume that here we're doing for undo only...
*/
- sima->gpd = restore_pointer_by_name(id_map, (ID *)sima->gpd, USER_REAL);
- sima->mask_info.mask = restore_pointer_by_name(
- id_map, (ID *)sima->mask_info.mask, USER_REAL);
+ sima->gpd = static_cast<bGPdata *>(
+ restore_pointer_by_name(id_map, (ID *)sima->gpd, USER_REAL));
+ sima->mask_info.mask = static_cast<Mask *>(
+ restore_pointer_by_name(id_map, (ID *)sima->mask_info.mask, USER_REAL));
}
else if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
@@ -2590,35 +2620,39 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
/* NOTE: pre-2.5, this was local data not lib data, but now we need this as lib data
* so assume that here we're doing for undo only...
*/
- sseq->gpd = restore_pointer_by_name(id_map, (ID *)sseq->gpd, USER_REAL);
+ sseq->gpd = static_cast<bGPdata *>(
+ restore_pointer_by_name(id_map, (ID *)sseq->gpd, USER_REAL));
}
else if (sl->spacetype == SPACE_NLA) {
SpaceNla *snla = (SpaceNla *)sl;
bDopeSheet *ads = snla->ads;
if (ads) {
- ads->source = restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL);
+ ads->source = static_cast<ID *>(
+ restore_pointer_by_name(id_map, (ID *)ads->source, USER_REAL));
if (ads->filter_grp) {
- ads->filter_grp = restore_pointer_by_name(
- id_map, (ID *)ads->filter_grp, USER_IGNORE);
+ ads->filter_grp = static_cast<Collection *>(
+ restore_pointer_by_name(id_map, (ID *)ads->filter_grp, USER_IGNORE));
}
}
}
else if (sl->spacetype == SPACE_TEXT) {
SpaceText *st = (SpaceText *)sl;
- st->text = restore_pointer_by_name(id_map, (ID *)st->text, USER_IGNORE);
- if (st->text == NULL) {
- st->text = newmain->texts.first;
+ st->text = static_cast<Text *>(
+ restore_pointer_by_name(id_map, (ID *)st->text, USER_IGNORE));
+ if (st->text == nullptr) {
+ st->text = static_cast<Text *>(newmain->texts.first);
}
}
else if (sl->spacetype == SPACE_SCRIPT) {
SpaceScript *scpt = (SpaceScript *)sl;
- scpt->script = restore_pointer_by_name(id_map, (ID *)scpt->script, USER_REAL);
+ scpt->script = static_cast<Script *>(
+ restore_pointer_by_name(id_map, (ID *)scpt->script, USER_REAL));
- // screen->script = NULL; /* 2.45 set to null, better re-run the script. */
+ // screen->script = nullptr; /* 2.45 set to null, better re-run the script. */
if (scpt->script) {
SCRIPT_SET_NULL(scpt->script);
}
@@ -2626,22 +2660,20 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
else if (sl->spacetype == SPACE_OUTLINER) {
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
- space_outliner->search_tse.id = restore_pointer_by_name(
- id_map, space_outliner->search_tse.id, USER_IGNORE);
-
if (space_outliner->treestore) {
TreeStoreElem *tselem;
BLI_mempool_iter iter;
BLI_mempool_iternew(space_outliner->treestore, &iter);
- while ((tselem = BLI_mempool_iterstep(&iter))) {
+ while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
/* Do not try to restore pointers to drivers/sequence/etc.,
* can crash in undo case! */
if (TSE_IS_REAL_ID(tselem)) {
- tselem->id = restore_pointer_by_name(id_map, tselem->id, USER_IGNORE);
+ tselem->id = static_cast<ID *>(
+ restore_pointer_by_name(id_map, tselem->id, USER_IGNORE));
}
else {
- tselem->id = NULL;
+ tselem->id = nullptr;
}
}
/* rebuild hash table, because it depends on ids too */
@@ -2654,21 +2686,24 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
bNodeTree *ntree;
/* node tree can be stored locally in id too, link this first */
- snode->id = restore_pointer_by_name(id_map, snode->id, USER_REAL);
- snode->from = restore_pointer_by_name(id_map, snode->from, USER_IGNORE);
+ snode->id = static_cast<ID *>(restore_pointer_by_name(id_map, snode->id, USER_REAL));
+ snode->from = static_cast<ID *>(
+ restore_pointer_by_name(id_map, snode->from, USER_IGNORE));
- ntree = snode->id ? ntreeFromID(snode->id) : NULL;
- snode->nodetree = ntree ?
- ntree :
- restore_pointer_by_name(id_map, (ID *)snode->nodetree, USER_REAL);
+ ntree = snode->id ? ntreeFromID(snode->id) : nullptr;
+ snode->nodetree = ntree ? ntree :
+ static_cast<bNodeTree *>(restore_pointer_by_name(
+ id_map, (ID *)snode->nodetree, USER_REAL));
- for (path = snode->treepath.first; path; path = path->next) {
+ for (path = static_cast<bNodeTreePath *>(snode->treepath.first); path;
+ path = path->next) {
if (path == snode->treepath.first) {
/* first nodetree in path is same as snode->nodetree */
path->nodetree = snode->nodetree;
}
else {
- path->nodetree = restore_pointer_by_name(id_map, (ID *)path->nodetree, USER_REAL);
+ path->nodetree = static_cast<bNodeTree *>(
+ restore_pointer_by_name(id_map, (ID *)path->nodetree, USER_REAL));
}
if (!path->nodetree) {
@@ -2687,19 +2722,20 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
/* edittree is just the last in the path,
* set this directly since the path may have been shortened above */
if (snode->treepath.last) {
- path = snode->treepath.last;
+ path = static_cast<bNodeTreePath *>(snode->treepath.last);
snode->edittree = path->nodetree;
}
else {
- snode->edittree = NULL;
+ snode->edittree = nullptr;
}
}
else if (sl->spacetype == SPACE_CLIP) {
SpaceClip *sclip = (SpaceClip *)sl;
- sclip->clip = restore_pointer_by_name(id_map, (ID *)sclip->clip, USER_REAL);
- sclip->mask_info.mask = restore_pointer_by_name(
- id_map, (ID *)sclip->mask_info.mask, USER_REAL);
+ sclip->clip = static_cast<MovieClip *>(
+ restore_pointer_by_name(id_map, (ID *)sclip->clip, USER_REAL));
+ sclip->mask_info.mask = static_cast<Mask *>(
+ restore_pointer_by_name(id_map, (ID *)sclip->mask_info.mask, USER_REAL));
sclip->scopes.ok = 0;
}
@@ -2709,8 +2745,8 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
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);
+ object_context->object = static_cast<Object *>(
+ restore_pointer_by_name(id_map, (ID *)object_context->object, USER_IGNORE));
}
}
}
@@ -2732,8 +2768,8 @@ void blo_lib_link_restore(Main *oldmain,
LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) {
lib_link_workspace_layout_restore(id_map, newmain, layout);
}
- workspace->pin_scene = restore_pointer_by_name(
- id_map, (ID *)workspace->pin_scene, USER_IGNORE);
+ workspace->pin_scene = static_cast<Scene *>(
+ restore_pointer_by_name(id_map, (ID *)workspace->pin_scene, USER_IGNORE));
}
LISTBASE_FOREACH (wmWindow *, win, &curwm->windows) {
@@ -2741,14 +2777,16 @@ void blo_lib_link_restore(Main *oldmain,
ID *workspace_id = (ID *)workspace;
Scene *oldscene = win->scene;
- workspace = restore_pointer_by_name(id_map, workspace_id, USER_REAL);
+ workspace = static_cast<WorkSpace *>(restore_pointer_by_name(id_map, workspace_id, USER_REAL));
BKE_workspace_active_set(win->workspace_hook, workspace);
- win->scene = restore_pointer_by_name(id_map, (ID *)win->scene, USER_REAL);
- if (win->scene == NULL) {
+ win->scene = static_cast<Scene *>(
+ restore_pointer_by_name(id_map, (ID *)win->scene, USER_REAL));
+ if (win->scene == nullptr) {
win->scene = curscene;
}
- win->unpinned_scene = restore_pointer_by_name(id_map, (ID *)win->unpinned_scene, USER_IGNORE);
- if (BKE_view_layer_find(win->scene, win->view_layer_name) == NULL) {
+ win->unpinned_scene = static_cast<Scene *>(
+ restore_pointer_by_name(id_map, (ID *)win->unpinned_scene, USER_IGNORE));
+ if (BKE_view_layer_find(win->scene, win->view_layer_name) == nullptr) {
STRNCPY(win->view_layer_name, cur_view_layer->name);
}
BKE_workspace_active_set(win->workspace_hook, workspace);
@@ -2763,7 +2801,7 @@ void blo_lib_link_restore(Main *oldmain,
* potential local view, and needs window's scene pointer to be final... */
lib_link_window_scene_data_restore(win, win->scene, cur_view_layer);
- BLI_assert(win->screen == NULL);
+ BLI_assert(win->screen == nullptr);
}
lib_link_wm_xr_data_restore(id_map, &curwm->xr);
@@ -2793,7 +2831,7 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
Main *newmain;
/* check if the library was already read */
- for (newmain = fd->mainlist->first; newmain; newmain = newmain->next) {
+ for (newmain = static_cast<Main *>(fd->mainlist->first); newmain; newmain = newmain->next) {
if (newmain->curlib) {
if (BLI_path_cmp(newmain->curlib->filepath_abs, lib->filepath_abs) == 0) {
BLO_reportf_wrap(fd->reports,
@@ -2837,7 +2875,7 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
BLI_addtail(fd->mainlist, newmain);
newmain->curlib = lib;
- lib->parent = NULL;
+ lib->parent = nullptr;
id_us_ensure_real(&lib->id);
}
@@ -2851,7 +2889,7 @@ static void lib_link_library(BlendLibReader *UNUSED(reader), Library *UNUSED(lib
static void fix_relpaths_library(const char *basepath, Main *main)
{
/* #BLO_read_from_memory uses a blank file-path. */
- if (basepath == NULL || basepath[0] == '\0') {
+ if (basepath == nullptr || basepath[0] == '\0') {
LISTBASE_FOREACH (Library *, lib, &main->libraries) {
/* when loading a linked lib into a file which has not been saved,
* there is nothing we can be relative to, so instead we need to make
@@ -2885,7 +2923,7 @@ static void fix_relpaths_library(const char *basepath, Main *main)
static ID *create_placeholder(Main *mainvar, const short idcode, const char *idname, const int tag)
{
ListBase *lb = which_libbase(mainvar, idcode);
- ID *ph_id = BKE_libblock_alloc_notest(idcode);
+ ID *ph_id = static_cast<ID *>(BKE_libblock_alloc_notest(idcode));
*((short *)ph_id->name) = idcode;
BLI_strncpy(ph_id->name + 2, idname, sizeof(ph_id->name) - 2);
@@ -2896,9 +2934,9 @@ static ID *create_placeholder(Main *mainvar, const short idcode, const char *idn
ph_id->icon_id = 0;
BLI_addtail(lb, ph_id);
- id_sort_by_name(lb, ph_id, NULL);
+ id_sort_by_name(lb, ph_id, nullptr);
- if (mainvar->id_map != NULL) {
+ if (mainvar->id_map != nullptr) {
BKE_main_idmap_insert_id(mainvar->id_map, ph_id);
}
@@ -2914,8 +2952,8 @@ static void placeholders_ensure_valid(Main *bmain)
/* Placeholder ObData IDs won't have any material, we have to update their objects for that,
* otherwise the inconsistency between both will lead to crashes (especially in Eevee?). */
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
- ID *obdata = ob->data;
- if (obdata != NULL && obdata->tag & LIB_TAG_MISSING) {
+ ID *obdata = static_cast<ID *>(ob->data);
+ if (obdata != nullptr && obdata->tag & LIB_TAG_MISSING) {
BKE_object_materials_test(bmain, ob, obdata);
}
}
@@ -3022,7 +3060,7 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *
}
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
- if (id_type->blend_read_data != NULL) {
+ if (id_type->blend_read_data != nullptr) {
id_type->blend_read_data(&reader, id);
}
@@ -3043,7 +3081,7 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID *
}
/* try to restore (when undoing) or clear ID's cache pointers. */
- if (id_type->foreach_cache != NULL) {
+ if (id_type->foreach_cache != nullptr) {
BKE_idtype_id_foreach_cache(
id, blo_cache_storage_entry_restore_in_new, reader.fd->cache_storage);
}
@@ -3063,9 +3101,13 @@ static BHead *read_data_into_datamap(FileData *fd, BHead *bhead, const char *all
* With the code below we get the struct-name to help tracking down the leak.
* This is kept disabled as the #malloc for the text always leaks memory. */
#if 0
- {
- const short *sp = fd->filesdna->structs[bhead->SDNAnr];
- allocname = fd->filesdna->types[sp[0]];
+ if (bhead->SDNAnr == 0) {
+ /* The data type here is unclear because #writedata sets SDNAnr to 0. */
+ allocname = "likely raw data";
+ }
+ else {
+ SDNA_Struct *sp = fd->filesdna->structs[bhead->SDNAnr];
+ allocname = fd->filesdna->types[sp->type];
size_t allocname_size = strlen(allocname) + 1;
char *allocname_buf = malloc(allocname_size);
memcpy(allocname_buf, allocname, allocname_size);
@@ -3119,15 +3161,15 @@ static bool read_libblock_undo_restore_library(FileData *fd, Main *main, const I
* otherwise we have to do a full read of that bhead... */
CLOG_INFO(&LOG_UNDO, 2, "UNDO: restore library %s", id->name);
- Main *libmain = fd->old_mainlist->first;
+ Main *libmain = static_cast<Main *>(fd->old_mainlist->first);
/* Skip oldmain itself... */
for (libmain = libmain->next; libmain; libmain = libmain->next) {
if (libmain->curlib && STREQ(id->name, libmain->curlib->id.name)) {
- Main *oldmain = fd->old_mainlist->first;
+ Main *oldmain = static_cast<Main *>(fd->old_mainlist->first);
CLOG_INFO(&LOG_UNDO,
2,
" compare with %s -> match",
- libmain->curlib ? libmain->curlib->id.name : "<NULL>");
+ libmain->curlib ? libmain->curlib->id.name : "<nullptr>");
/* In case of a library, we need to re-add its main to fd->mainlist,
* because if we have later a missing ID_LINK_PLACEHOLDER,
* we need to get the correct lib it is linked to!
@@ -3142,7 +3184,7 @@ static bool read_libblock_undo_restore_library(FileData *fd, Main *main, const I
CLOG_INFO(&LOG_UNDO,
2,
" compare with %s -> NO match",
- libmain->curlib ? libmain->curlib->id.name : "<NULL>");
+ libmain->curlib ? libmain->curlib->id.name : "<nullptr>");
}
return false;
@@ -3154,16 +3196,16 @@ static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID
CLOG_INFO(&LOG_UNDO, 2, "UNDO: restore linked datablock %s", id->name);
ID *id_old = BKE_libblock_find_name(main, GS(id->name), id->name + 2);
- if (id_old != NULL) {
+ if (id_old != nullptr) {
CLOG_INFO(&LOG_UNDO,
2,
" from %s (%s): found",
- main->curlib ? main->curlib->id.name : "<NULL>",
- main->curlib ? main->curlib->filepath : "<NULL>");
+ main->curlib ? main->curlib->id.name : "<nullptr>",
+ main->curlib ? main->curlib->filepath : "<nullptr>");
/* Even though we found our linked ID, there is no guarantee its address
* is still the same. */
if (id_old != bhead->old) {
- oldnewmap_insert(fd->libmap, bhead->old, id_old, GS(id_old->name));
+ oldnewmap_lib_insert(fd, bhead->old, id_old, GS(id_old->name));
}
/* No need to do anything else for ID_LINK_PLACEHOLDER, it's assumed
@@ -3174,8 +3216,8 @@ static bool read_libblock_undo_restore_linked(FileData *fd, Main *main, const ID
CLOG_INFO(&LOG_UNDO,
2,
" from %s (%s): NOT found",
- main->curlib ? main->curlib->id.name : "<NULL>",
- main->curlib ? main->curlib->filepath : "<NULL>");
+ main->curlib ? main->curlib->id.name : "<nullptr>",
+ main->curlib ? main->curlib->filepath : "<nullptr>");
return false;
}
@@ -3184,7 +3226,7 @@ static void read_libblock_undo_restore_identical(
FileData *fd, Main *main, const ID *UNUSED(id), ID *id_old, const int tag)
{
BLI_assert((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0);
- BLI_assert(id_old != NULL);
+ BLI_assert(id_old != nullptr);
/* Some tags need to be preserved here. */
id_old->tag = tag | (id_old->tag & LIB_TAG_EXTRAUSER);
@@ -3192,11 +3234,11 @@ static void read_libblock_undo_restore_identical(
id_old->us = ID_FAKE_USERS(id_old);
/* Do not reset id->icon_id here, memory allocated for it remains valid. */
/* Needed because .blend may have been saved with crap value here... */
- id_old->newid = NULL;
- id_old->orig_id = NULL;
+ id_old->newid = nullptr;
+ id_old->orig_id = nullptr;
const short idcode = GS(id_old->name);
- Main *old_bmain = fd->old_mainlist->first;
+ Main *old_bmain = static_cast<Main *>(fd->old_mainlist->first);
ListBase *old_lb = which_libbase(old_bmain, idcode);
ListBase *new_lb = which_libbase(main, idcode);
BLI_remlink(old_lb, id_old);
@@ -3223,11 +3265,11 @@ static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main,
* helps reducing further detected changes by the depsgraph (since unchanged IDs remain fully
* unchanged, even if they are using/pointing to a changed one). */
BLI_assert((fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0);
- BLI_assert(id_old != NULL);
+ BLI_assert(id_old != nullptr);
const short idcode = GS(id->name);
- Main *old_bmain = fd->old_mainlist->first;
+ Main *old_bmain = static_cast<Main *>(fd->old_mainlist->first);
ListBase *old_lb = which_libbase(old_bmain, idcode);
ListBase *new_lb = which_libbase(main, idcode);
BLI_remlink(old_lb, id_old);
@@ -3235,8 +3277,8 @@ static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main,
/* We do not need any remapping from this call here, since no ID pointer is valid in the data
* currently (they are all pointing to old addresses, and need to go through `lib_link`
- * process). So we can pass NULL for the Main pointer parameter. */
- BKE_lib_id_swap_full(NULL, id, id_old);
+ * process). So we can pass nullptr for the Main pointer parameter. */
+ BKE_lib_id_swap_full(nullptr, id, id_old);
/* Special temporary usage of this pointer, necessary for the `undo_preserve` call after
* lib-linking to restore some data that should never be affected by undo, e.g. the 3D cursor of
@@ -3251,7 +3293,7 @@ static bool read_libblock_undo_restore(
FileData *fd, Main *main, BHead *bhead, const int tag, ID **r_id_old)
{
/* Get pointer to memory of new ID that we will be reading. */
- const ID *id = peek_struct_undo(fd, bhead);
+ const ID *id = static_cast<const ID *>(peek_struct_undo(fd, bhead));
const short idcode = GS(id->name);
if (bhead->code == ID_LI) {
@@ -3274,19 +3316,19 @@ static bool read_libblock_undo_restore(
}
/* Restore local datablocks. */
- ID *id_old = NULL;
+ ID *id_old = nullptr;
const bool do_partial_undo = (fd->skip_flags & BLO_READ_SKIP_UNDO_OLD_MAIN) == 0;
if (do_partial_undo && (bhead->code != ID_LINK_PLACEHOLDER)) {
/* This code should only ever be reached for local data-blocks. */
- BLI_assert(main->curlib == NULL);
+ BLI_assert(main->curlib == nullptr);
/* Find the 'current' existing ID we want to reuse instead of the one we
* would read from the undo memfile. */
- BLI_assert(fd->old_idmap != NULL);
+ BLI_assert(fd->old_idmap != nullptr);
id_old = BKE_main_idmap_lookup_uuid(fd->old_idmap, id->session_uuid);
}
- if (id_old != NULL && read_libblock_is_identical(fd, bhead)) {
+ if (id_old != nullptr && read_libblock_is_identical(fd, bhead)) {
/* Local datablock was unchanged, restore from the old main. */
CLOG_INFO(&LOG_UNDO,
2,
@@ -3305,12 +3347,12 @@ static bool read_libblock_undo_restore(
/* Insert into library map for lookup by newly read datablocks (with pointer value bhead->old).
* Note that existing datablocks in memory (which pointer value would be id_old) are not
* remapped anymore, so no need to store this info here. */
- oldnewmap_insert(fd->libmap, bhead->old, id_old, bhead->code);
+ oldnewmap_lib_insert(fd, bhead->old, id_old, bhead->code);
*r_id_old = id_old;
return true;
}
- if (id_old != NULL) {
+ if (id_old != nullptr) {
/* Local datablock was changed. Restore at the address of the old datablock. */
CLOG_INFO(&LOG_UNDO,
2,
@@ -3344,13 +3386,13 @@ static BHead *read_libblock(FileData *fd,
/* First attempt to restore existing datablocks for undo.
* When datablocks are changed but still exist, we restore them at the old
* address and inherit recalc flags for the dependency graph. */
- ID *id_old = NULL;
+ ID *id_old = nullptr;
if (fd->flags & FD_FLAGS_IS_MEMFILE) {
if (read_libblock_undo_restore(fd, main, bhead, tag, &id_old)) {
if (r_id) {
*r_id = id_old;
}
- if (main->id_map != NULL) {
+ if (main->id_map != nullptr) {
BKE_main_idmap_insert_id(main->id_map, id_old);
}
@@ -3359,10 +3401,10 @@ static BHead *read_libblock(FileData *fd,
}
/* Read libblock struct. */
- ID *id = read_struct(fd, bhead, "lib block");
- if (id == NULL) {
+ ID *id = static_cast<ID *>(read_struct(fd, bhead, "lib block"));
+ if (id == nullptr) {
if (r_id) {
- *r_id = NULL;
+ *r_id = nullptr;
}
return blo_bhead_next(fd, bhead);
}
@@ -3370,12 +3412,12 @@ static BHead *read_libblock(FileData *fd,
/* Determine ID type and add to main database list. */
const short idcode = GS(id->name);
ListBase *lb = which_libbase(main, idcode);
- if (lb == NULL) {
+ if (lb == nullptr) {
/* Unknown ID type. */
CLOG_WARN(&LOG, "Unknown id code '%c%c'", (idcode & 0xff), (idcode >> 8));
MEM_freeN(id);
if (r_id) {
- *r_id = NULL;
+ *r_id = nullptr;
}
return blo_bhead_next(fd, bhead);
}
@@ -3388,7 +3430,7 @@ static BHead *read_libblock(FileData *fd,
* Note that existing datablocks in memory (which pointer value would be id_old) are not remapped
* remapped anymore, so no need to store this info here. */
ID *id_target = id_old ? id_old : id;
- oldnewmap_insert(fd->libmap, bhead->old, id_target, bhead->code);
+ oldnewmap_lib_insert(fd, bhead->old, id_target, bhead->code);
if (r_id) {
*r_id = id_target;
@@ -3413,7 +3455,7 @@ static BHead *read_libblock(FileData *fd,
direct_link_id(fd, main, id_tag, id, id_old);
- if (main->id_map != NULL) {
+ if (main->id_map != nullptr) {
BKE_main_idmap_insert_id(main->id_map, id);
}
@@ -3433,19 +3475,19 @@ static BHead *read_libblock(FileData *fd,
* been added to the fd->libmap mapping, which in theory could lead to nice crashes...
* This should be properly solved at some point. */
BKE_id_free(main, id);
- if (r_id != NULL) {
- *r_id = NULL;
+ if (r_id != nullptr) {
+ *r_id = nullptr;
}
}
else if (id_old) {
/* For undo, store contents read into id at id_old. */
read_libblock_undo_restore_at_old_address(fd, main, id, id_old);
- if (main->id_map != NULL) {
+ if (main->id_map != nullptr) {
BKE_main_idmap_insert_id(main->id_map, id_old);
}
}
- else if (main->id_map != NULL) {
+ else if (main->id_map != nullptr) {
BKE_main_idmap_insert_id(main->id_map, id);
}
@@ -3483,7 +3525,7 @@ BHead *blo_read_asset_data_block(FileData *fd, BHead *bhead, AssetMetaData **r_a
/* also version info is written here */
static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead)
{
- FileGlobal *fg = read_struct(fd, bhead, "Global");
+ FileGlobal *fg = static_cast<FileGlobal *>(read_struct(fd, bhead, "Global"));
/* copy to bfd handle */
bfd->main->subversionfile = fg->subversion;
@@ -3530,11 +3572,12 @@ static BHead *read_global(BlendFileData *bfd, FileData *fd, BHead *bhead)
/* NOTE: this has to be kept for reading older files... */
static void link_global(FileData *fd, BlendFileData *bfd)
{
- bfd->cur_view_layer = blo_read_get_new_globaldata_address(fd, bfd->cur_view_layer);
- bfd->curscreen = newlibadr(fd, NULL, bfd->curscreen);
- bfd->curscene = newlibadr(fd, NULL, bfd->curscene);
+ bfd->cur_view_layer = static_cast<ViewLayer *>(
+ blo_read_get_new_globaldata_address(fd, bfd->cur_view_layer));
+ bfd->curscreen = static_cast<bScreen *>(newlibadr(fd, nullptr, bfd->curscreen));
+ bfd->curscene = static_cast<Scene *>(newlibadr(fd, nullptr, bfd->curscene));
/* this happens in files older than 2.35 */
- if (bfd->curscene == NULL) {
+ if (bfd->curscene == nullptr) {
if (bfd->curscreen) {
bfd->curscene = bfd->curscreen->scene;
}
@@ -3551,7 +3594,7 @@ static void do_versions_userdef(FileData *UNUSED(fd), BlendFileData *bfd)
{
UserDef *user = bfd->user;
- if (user == NULL) {
+ if (user == nullptr) {
return;
}
@@ -3568,7 +3611,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
if (G.debug & G_DEBUG) {
char build_commit_datetime[32];
time_t temp_time = main->build_commit_timestamp;
- struct tm *tm = (temp_time) ? gmtime(&temp_time) : NULL;
+ struct tm *tm = (temp_time) ? gmtime(&temp_time) : nullptr;
if (LIKELY(tm)) {
strftime(build_commit_datetime, sizeof(build_commit_datetime), "%Y-%m-%d %H:%M", tm);
}
@@ -3593,6 +3636,7 @@ static void do_versions(FileData *fd, Library *lib, Main *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_400(fd, lib, main);
blo_do_versions_cycles(fd, lib, main);
/* WATCH IT!!!: pointers from libdata have not been converted yet here! */
@@ -3663,7 +3707,7 @@ static void lib_link_all(FileData *fd, Main *bmain)
lib_link_id(&reader, id);
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
- if (id_type->blend_read_lib != NULL) {
+ if (id_type->blend_read_lib != nullptr) {
id_type->blend_read_lib(&reader, id);
}
@@ -3675,7 +3719,7 @@ static void lib_link_all(FileData *fd, Main *bmain)
/* Some data that should be persistent, like the 3DCursor or the tool settings, are
* stored in IDs affected by undo, like Scene. So this requires some specific handling. */
- if (id_type->blend_read_undo_preserve != NULL && id->orig_id != NULL) {
+ if (id_type->blend_read_undo_preserve != nullptr && id->orig_id != nullptr) {
id_type->blend_read_undo_preserve(&reader, id, id->orig_id);
}
}
@@ -3683,7 +3727,7 @@ static void lib_link_all(FileData *fd, Main *bmain)
/* Cleanup `ID.orig_id`, this is now reserved for depsgraph/COW usage only. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
- id->orig_id = NULL;
+ id->orig_id = nullptr;
}
FOREACH_MAIN_ID_END;
@@ -3705,14 +3749,14 @@ static void lib_link_all(FileData *fd, Main *bmain)
static void after_liblink_merged_bmain_process(Main *bmain)
{
/* We only expect a merged Main here, not a split one. */
- BLI_assert((bmain->prev == NULL) && (bmain->next == NULL));
+ BLI_assert((bmain->prev == nullptr) && (bmain->next == nullptr));
/* Check for possible cycles in scenes' 'set' background property. */
lib_link_scenes_check_set(bmain);
/* We could integrate that to mesh/curve/lattice lib_link, but this is really cheap process,
* so simpler to just use it directly in this single call. */
- BLO_main_validate_shapekeys(bmain, NULL);
+ BLO_main_validate_shapekeys(bmain, nullptr);
/* We have to rebuild that runtime information *after* all data-blocks have been properly linked.
*/
@@ -3729,14 +3773,14 @@ static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi)
{
BLO_read_data_address(reader, &kmi->properties);
IDP_BlendDataRead(reader, &kmi->properties);
- kmi->ptr = NULL;
+ kmi->ptr = nullptr;
kmi->flag &= ~KMI_UPDATE;
}
static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
{
UserDef *user;
- bfd->user = user = read_struct(fd, bhead, "user def");
+ bfd->user = user = static_cast<UserDef *>(read_struct(fd, bhead, "user def"));
/* User struct has separate do-version handling */
user->versionfile = bfd->main->versionfile;
@@ -3757,8 +3801,8 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
BLO_read_list(reader, &user->asset_libraries);
LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) {
- keymap->modal_items = NULL;
- keymap->poll = NULL;
+ keymap->modal_items = nullptr;
+ keymap->poll = nullptr;
keymap->flag &= ~KEYMAP_UPDATE;
BLO_read_list(reader, &keymap->diff_items);
@@ -3803,7 +3847,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
}
/* XXX */
- user->uifonts.first = user->uifonts.last = NULL;
+ user->uifonts.first = user->uifonts.last = nullptr;
BLO_read_list(reader, &user->uistyles);
@@ -3830,13 +3874,13 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
{
BHead *bhead = blo_bhead_first(fd);
BlendFileData *bfd;
- ListBase mainlist = {NULL, NULL};
+ ListBase mainlist = {nullptr, nullptr};
if (fd->flags & FD_FLAGS_IS_MEMFILE) {
CLOG_INFO(&LOG_UNDO, 2, "UNDO: read step");
}
- bfd = MEM_callocN(sizeof(BlendFileData), "blendfiledata");
+ bfd = static_cast<BlendFileData *>(MEM_callocN(sizeof(BlendFileData), "blendfiledata"));
bfd->main = BKE_main_new();
bfd->main->versionfile = fd->fileversion;
@@ -3860,7 +3904,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
const int height = data[1];
if (BLEN_THUMB_MEMSIZE_IS_VALID(width, height)) {
const size_t data_size = BLEN_THUMB_MEMSIZE(width, height);
- bfd->main->blen_thumb = MEM_mallocN(data_size, __func__);
+ bfd->main->blen_thumb = static_cast<BlendThumbnail *>(MEM_mallocN(data_size, __func__));
BLI_assert((data_size - sizeof(*bfd->main->blen_thumb)) ==
(BLEN_THUMB_MEMSIZE_FILE(width, height) - (sizeof(*data) * 2)));
@@ -3891,7 +3935,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
}
break;
case ENDB:
- bhead = NULL;
+ bhead = nullptr;
break;
case ID_LINK_PLACEHOLDER:
@@ -3903,8 +3947,8 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
* The library is the most recently loaded ID_LI block, according
* to the file format definition. So we can use the entry at the
* end of mainlist, added in direct_link_library. */
- Main *libmain = mainlist.last;
- bhead = read_libblock(fd, libmain, bhead, 0, true, NULL);
+ Main *libmain = static_cast<Main *>(mainlist.last);
+ bhead = read_libblock(fd, libmain, bhead, 0, true, nullptr);
}
break;
/* in 2.50+ files, the file identifier for screens is patched, forward compatibility */
@@ -3917,7 +3961,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
bhead = blo_bhead_next(fd, bhead);
}
else {
- bhead = read_libblock(fd, bfd->main, bhead, LIB_TAG_LOCAL, false, NULL);
+ bhead = read_libblock(fd, bfd->main, bhead, LIB_TAG_LOCAL, false, nullptr);
}
}
}
@@ -3925,7 +3969,7 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
/* do before read_libraries, but skip undo case */
if ((fd->flags & FD_FLAGS_IS_MEMFILE) == 0) {
if ((fd->skip_flags & BLO_READ_SKIP_DATA) == 0) {
- do_versions(fd, NULL, bfd->main);
+ do_versions(fd, nullptr, bfd->main);
}
if ((fd->skip_flags & BLO_READ_SKIP_USERDEF) == 0) {
@@ -3999,9 +4043,9 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
link_global(fd, bfd); /* as last */
}
- fd->mainlist = NULL; /* Safety, this is local variable, shall not be used afterward. */
+ fd->mainlist = nullptr; /* Safety, this is local variable, shall not be used afterward. */
- BLI_assert(bfd->main->id_map == NULL);
+ BLI_assert(bfd->main->id_map == nullptr);
return bfd;
}
@@ -4021,7 +4065,8 @@ struct BHeadSort {
static int verg_bheadsort(const void *v1, const void *v2)
{
- const struct BHeadSort *x1 = v1, *x2 = v2;
+ const struct BHeadSort *x1 = static_cast<const BHeadSort *>(v1),
+ *x2 = static_cast<const BHeadSort *>(v2);
if (x1->old > x2->old) {
return 1;
@@ -4047,7 +4092,8 @@ static void sort_bhead_old_map(FileData *fd)
return;
}
- bhs = fd->bheadmap = MEM_malloc_arrayN(tot, sizeof(struct BHeadSort), "BHeadSort");
+ bhs = fd->bheadmap = static_cast<BHeadSort *>(
+ MEM_malloc_arrayN(tot, sizeof(struct BHeadSort), "BHeadSort"));
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead), bhs++) {
bhs->bhead = bhead;
@@ -4061,7 +4107,7 @@ static BHead *find_previous_lib(FileData *fd, BHead *bhead)
{
/* Skip library data-blocks in undo, see comment in read_libblock. */
if (fd->flags & FD_FLAGS_IS_MEMFILE) {
- return NULL;
+ return nullptr;
}
for (; bhead; bhead = blo_bhead_prev(fd, bhead)) {
@@ -4081,15 +4127,16 @@ static BHead *find_bhead(FileData *fd, void *old)
struct BHeadSort *bhs, bhs_s;
if (!old) {
- return NULL;
+ return nullptr;
}
- if (fd->bheadmap == NULL) {
+ if (fd->bheadmap == nullptr) {
sort_bhead_old_map(fd);
}
bhs_s.old = old;
- bhs = bsearch(&bhs_s, fd->bheadmap, fd->tot_bheadmap, sizeof(struct BHeadSort), verg_bheadsort);
+ bhs = static_cast<BHeadSort *>(
+ bsearch(&bhs_s, fd->bheadmap, fd->tot_bheadmap, sizeof(struct BHeadSort), verg_bheadsort));
if (bhs) {
return bhs->bhead;
@@ -4103,7 +4150,7 @@ static BHead *find_bhead(FileData *fd, void *old)
}
#endif
- return NULL;
+ return nullptr;
}
static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const char *name)
@@ -4115,7 +4162,7 @@ static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const
*((short *)idname_full) = idcode;
BLI_strncpy(idname_full + 2, name, sizeof(idname_full) - 2);
- return BLI_ghash_lookup(fd->bhead_idname_hash, idname_full);
+ return static_cast<BHead *>(BLI_ghash_lookup(fd->bhead_idname_hash, idname_full));
#else
BHead *bhead;
@@ -4132,14 +4179,14 @@ static BHead *find_bhead_from_code_name(FileData *fd, const short idcode, const
}
}
- return NULL;
+ return nullptr;
#endif
}
static BHead *find_bhead_from_idname(FileData *fd, const char *idname)
{
#ifdef USE_GHASH_BHEAD
- return BLI_ghash_lookup(fd->bhead_idname_hash, idname);
+ return static_cast<BHead *>(BLI_ghash_lookup(fd->bhead_idname_hash, idname));
#else
return find_bhead_from_code_name(fd, GS(idname), idname + 2);
#endif
@@ -4147,8 +4194,8 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname)
static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead)
{
- if (mainvar->id_map == NULL) {
- mainvar->id_map = BKE_main_idmap_create(mainvar, false, NULL, MAIN_IDMAP_TYPE_NAME);
+ if (mainvar->id_map == nullptr) {
+ mainvar->id_map = BKE_main_idmap_create(mainvar, false, nullptr, MAIN_IDMAP_TYPE_NAME);
}
BLI_assert(BKE_main_idmap_main_get(mainvar->id_map) == mainvar);
@@ -4167,24 +4214,24 @@ static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead)
static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
{
- FileData *fd = fdhandle;
+ FileData *fd = static_cast<FileData *>(fdhandle);
BHead *bhead = find_bhead(fd, old);
- if (bhead == NULL) {
+ if (bhead == nullptr) {
return;
}
if (bhead->code == ID_LINK_PLACEHOLDER) {
- /* Placeholder link to data-lock in another library. */
+ /* Placeholder link to data-block in another library. */
BHead *bheadlib = find_previous_lib(fd, bhead);
- if (bheadlib == NULL) {
+ if (bheadlib == nullptr) {
return;
}
- Library *lib = read_struct(fd, bheadlib, "Library");
+ Library *lib = static_cast<Library *>(read_struct(fd, bheadlib, "Library"));
Main *libmain = blo_find_main(fd, lib->filepath, fd->relabase);
- if (libmain->curlib == NULL) {
+ if (libmain->curlib == nullptr) {
const char *idname = blo_bhead_id_name(fd, bhead);
BLO_reportf_wrap(fd->reports,
@@ -4197,11 +4244,12 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
ID *id = is_yet_read(fd, libmain, bhead);
- if (id == NULL) {
+ if (id == nullptr) {
/* 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, &id);
- id_sort_by_name(which_libbase(libmain, GS(id->name)), id, id->prev);
+ BLI_assert(id != nullptr);
+ id_sort_by_name(which_libbase(libmain, GS(id->name)), id, static_cast<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);
@@ -4227,9 +4275,9 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
* (B) forest.blend: contains Forest collection linking in Tree from tree.blend.
* (C) shot.blend: links in both Tree from tree.blend and Forest from forest.blend.
*/
- oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code);
+ oldnewmap_lib_insert(fd, bhead->old, id, bhead->code);
- /* If "id" is a real data-lock and not a placeholder, we need to
+ /* If "id" is a real data-block and not a placeholder, we need to
* update fd->libmap to replace ID_LINK_PLACEHOLDER with the real
* ID_* code.
*
@@ -4256,14 +4304,15 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
}
ID *id = is_yet_read(fd, mainvar, bhead);
- if (id == NULL) {
+ if (id == nullptr) {
read_libblock(fd,
mainvar,
bhead,
fd->id_tag_extra | LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT,
false,
&id);
- id_sort_by_name(which_libbase(mainvar, GS(id->name)), id, id->prev);
+ BLI_assert(id != nullptr);
+ id_sort_by_name(which_libbase(mainvar, GS(id->name)), id, static_cast<ID *>(id->prev));
}
else {
/* Convert any previously read weak link to regular link
@@ -4275,7 +4324,7 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
/* this is actually only needed on UI call? when ID was already read before,
* and another append happens which invokes same ID...
* in that case the lookup table needs this entry */
- oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code);
+ oldnewmap_lib_insert(fd, bhead->old, id, bhead->code);
/* commented because this can print way too much */
// if (G.debug & G_DEBUG) printf("expand: already read %s\n", id->name);
}
@@ -4290,14 +4339,14 @@ static void expand_id_embedded_id(BlendExpander *expander, ID *id)
{
/* Handle 'private IDs'. */
bNodeTree *nodetree = ntreeFromID(id);
- if (nodetree != NULL) {
+ if (nodetree != nullptr) {
expand_id(expander, &nodetree->id);
ntreeBlendReadExpand(expander, nodetree);
}
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
- if (scene->master_collection != NULL) {
+ if (scene->master_collection != nullptr) {
expand_id(expander, &scene->master_collection->id);
BKE_collection_blend_read_expand(expander, scene->master_collection);
}
@@ -4314,7 +4363,7 @@ static void expand_id(BlendExpander *expander, ID *id)
}
AnimData *adt = BKE_animdata_from_id(id);
- if (adt != NULL) {
+ if (adt != nullptr) {
BKE_animdata_blend_read_expand(expander, adt);
}
@@ -4329,7 +4378,7 @@ void BLO_main_expander(BLOExpandDoitCallback expand_doit_func)
void BLO_expand_main(void *fdhandle, Main *mainvar)
{
ListBase *lbarray[INDEX_ID_MAX];
- FileData *fd = fdhandle;
+ FileData *fd = static_cast<FileData *>(fdhandle);
ID *id;
int a;
bool do_it = true;
@@ -4341,20 +4390,20 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
a = set_listbasepointers(mainvar, lbarray);
while (a--) {
- id = lbarray[a]->first;
+ id = static_cast<ID *>(lbarray[a]->first);
while (id) {
if (id->tag & LIB_TAG_NEED_EXPAND) {
expand_id(&expander, id);
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
- if (id_type->blend_read_expand != NULL) {
+ if (id_type->blend_read_expand != nullptr) {
id_type->blend_read_expand(&expander, id);
}
do_it = true;
id->tag &= ~LIB_TAG_NEED_EXPAND;
}
- id = id->next;
+ id = static_cast<ID *>(id->next);
}
}
}
@@ -4381,7 +4430,7 @@ static ID *link_named_part(
if (bhead) {
id = is_yet_read(fd, mainl, bhead);
- if (id == NULL) {
+ if (id == nullptr) {
/* not read yet */
const int tag = ((force_indirect ? LIB_TAG_INDIRECT : LIB_TAG_EXTERN) | fd->id_tag_extra);
read_libblock(fd, mainl, bhead, tag | LIB_TAG_NEED_EXPAND, false, &id);
@@ -4389,13 +4438,13 @@ static ID *link_named_part(
if (id) {
/* sort by name in list */
ListBase *lb = which_libbase(mainl, idcode);
- id_sort_by_name(lb, id, NULL);
+ id_sort_by_name(lb, id, nullptr);
}
}
else {
/* already linked */
CLOG_WARN(&LOG, "Append: ID '%s' is already linked", id->name);
- oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code);
+ oldnewmap_lib_insert(fd, bhead->old, id, bhead->code);
if (!force_indirect && (id->tag & LIB_TAG_INDIRECT)) {
id->tag &= ~LIB_TAG_INDIRECT;
id->flag &= ~LIB_INDIRECT_WEAK_LINK;
@@ -4409,11 +4458,11 @@ static ID *link_named_part(
mainl, idcode, name, force_indirect ? LIB_TAG_INDIRECT : LIB_TAG_EXTERN);
}
else {
- id = NULL;
+ id = nullptr;
}
- /* if we found the id but the id is NULL, this is really bad */
- BLI_assert(!((bhead != NULL) && (id == NULL)));
+ /* if we found the id but the id is nullptr, this is really bad */
+ BLI_assert(!((bhead != nullptr) && (id == nullptr)));
return id;
}
@@ -4444,7 +4493,7 @@ static Main *library_link_begin(Main *mainvar,
(*fd)->id_tag_extra = id_tag_extra;
- (*fd)->mainlist = MEM_callocN(sizeof(ListBase), "FileData.mainlist");
+ (*fd)->mainlist = static_cast<ListBase *>(MEM_callocN(sizeof(ListBase), "FileData.mainlist"));
/* make mains */
blo_split_main((*fd)->mainlist, mainvar);
@@ -4483,7 +4532,7 @@ void BLO_library_link_params_init_with_context(struct LibraryLink_Params *params
const struct View3D *v3d)
{
BLO_library_link_params_init(params, bmain, flag, id_tag_extra);
- if (scene != NULL) {
+ if (scene != nullptr) {
params->context.scene = scene;
params->context.view_layer = view_layer;
params->context.v3d = v3d;
@@ -4527,8 +4576,8 @@ static void library_link_end(Main *mainl, FileData **fd, const int flag)
Main *mainvar;
Library *curlib;
- if (mainl->id_map == NULL) {
- mainl->id_map = BKE_main_idmap_create(mainl, false, NULL, MAIN_IDMAP_TYPE_NAME);
+ if (mainl->id_map == nullptr) {
+ mainl->id_map = BKE_main_idmap_create(mainl, false, nullptr, MAIN_IDMAP_TYPE_NAME);
}
/* expander now is callback function */
@@ -4552,8 +4601,8 @@ static void library_link_end(Main *mainl, FileData **fd, const int flag)
}
blo_join_main((*fd)->mainlist);
- mainvar = (*fd)->mainlist->first;
- mainl = NULL; /* blo_join_main free's mainl, can't use anymore */
+ mainvar = static_cast<Main *>((*fd)->mainlist->first);
+ mainl = nullptr; /* blo_join_main free's mainl, can't use anymore */
lib_link_all(*fd, mainvar);
after_liblink_merged_bmain_process(mainvar);
@@ -4581,7 +4630,7 @@ static void library_link_end(Main *mainl, FileData **fd, const int flag)
}
blo_join_main((*fd)->mainlist);
- mainvar = (*fd)->mainlist->first;
+ mainvar = static_cast<Main *>((*fd)->mainlist->first);
MEM_freeN((*fd)->mainlist);
/* This does not take into account old, deprecated data, so we also have to do it after
@@ -4610,7 +4659,7 @@ static void library_link_end(Main *mainl, FileData **fd, const int flag)
/* patch to prevent switch_endian happens twice */
if ((*fd)->flags & FD_FLAGS_SWITCH_ENDIAN) {
blo_filedata_free(*fd);
- *fd = NULL;
+ *fd = nullptr;
}
}
@@ -4651,7 +4700,7 @@ static int has_linked_ids_to_read(Main *mainvar)
static void read_library_linked_id(
FileData *basefd, FileData *fd, Main *mainvar, ID *id, ID **r_id)
{
- BHead *bhead = NULL;
+ BHead *bhead = nullptr;
const bool is_valid = BKE_idtype_idcode_is_linkable(GS(id->name)) ||
((id->tag & LIB_TAG_EXTERN) == 0);
@@ -4690,7 +4739,8 @@ static void read_library_linked_id(
/* Generate a placeholder for this ID (simplified version of read_libblock actually...). */
if (r_id) {
- *r_id = is_valid ? create_placeholder(mainvar, GS(id->name), id->name + 2, id->tag) : NULL;
+ *r_id = is_valid ? create_placeholder(mainvar, GS(id->name), id->name + 2, id->tag) :
+ nullptr;
}
}
}
@@ -4706,14 +4756,14 @@ static void read_library_linked_ids(FileData *basefd,
int a = set_listbasepointers(mainvar, lbarray);
while (a--) {
- ID *id = lbarray[a]->first;
- ListBase pending_free_ids = {NULL};
+ ID *id = static_cast<ID *>(lbarray[a]->first);
+ ListBase pending_free_ids = {nullptr};
while (id) {
- ID *id_next = id->next;
+ ID *id_next = static_cast<ID *>(id->next);
if ((id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) && !(id->flag & LIB_INDIRECT_WEAK_LINK)) {
BLI_remlink(lbarray[a], id);
- if (mainvar->id_map != NULL) {
+ if (mainvar->id_map != nullptr) {
BKE_main_idmap_remove_id(mainvar->id_map, id);
}
@@ -4721,14 +4771,14 @@ static void read_library_linked_ids(FileData *basefd,
* you have more than one linked ID of the same data-block from same
* library. This is absolutely horrible, hence we use a ghash to ensure
* we go back to a single linked data when loading the file. */
- ID **realid = NULL;
+ ID **realid = nullptr;
if (!BLI_ghash_ensure_p(loaded_ids, id->name, (void ***)&realid)) {
read_library_linked_id(basefd, fd, mainvar, id, realid);
}
- /* `realid` shall never be NULL - unless some source file/lib is broken
- * (known case: some directly linked shapekey from a missing lib...). */
- // BLI_assert(*realid != NULL);
+ /* `realid` shall never be nullptr - unless some source file/lib is broken
+ * (known case: some directly linked shape-key from a missing lib...). */
+ // BLI_assert(*realid != nullptr);
/* Now that we have a real ID, replace all pointers to placeholders in
* fd->libmap with pointers to the real data-blocks. We do this for all
@@ -4743,28 +4793,28 @@ static void read_library_linked_ids(FileData *basefd,
}
/* Clear GHash and free link placeholder IDs of the current type. */
- BLI_ghash_clear(loaded_ids, NULL, NULL);
+ BLI_ghash_clear(loaded_ids, nullptr, nullptr);
BLI_freelistN(&pending_free_ids);
}
- BLI_ghash_free(loaded_ids, NULL, NULL);
+ BLI_ghash_free(loaded_ids, nullptr, nullptr);
}
static void read_library_clear_weak_links(FileData *basefd, ListBase *mainlist, Main *mainvar)
{
/* Any remaining weak links at this point have been lost, silently drop
- * those by setting them to NULL pointers. */
+ * those by setting them to nullptr pointers. */
ListBase *lbarray[INDEX_ID_MAX];
int a = set_listbasepointers(mainvar, lbarray);
while (a--) {
- ID *id = lbarray[a]->first;
+ ID *id = static_cast<ID *>(lbarray[a]->first);
while (id) {
- ID *id_next = id->next;
+ ID *id_next = static_cast<ID *>(id->next);
if ((id->tag & LIB_TAG_ID_LINK_PLACEHOLDER) && (id->flag & LIB_INDIRECT_WEAK_LINK)) {
CLOG_INFO(&LOG, 3, "Dropping weak link to '%s'", id->name);
- change_link_placeholder_to_real_ID_pointer(mainlist, basefd, id, NULL);
+ change_link_placeholder_to_real_ID_pointer(mainlist, basefd, id, nullptr);
BLI_freelinkN(lbarray[a], id);
}
id = id_next;
@@ -4779,7 +4829,7 @@ static FileData *read_library_file_data(FileData *basefd,
{
FileData *fd = mainptr->curlib->filedata;
- if (fd != NULL) {
+ if (fd != nullptr) {
/* File already open. */
return fd;
}
@@ -4834,14 +4884,14 @@ static FileData *read_library_file_data(FileData *basefd,
#endif
}
else {
- mainptr->curlib->filedata = NULL;
+ mainptr->curlib->filedata = nullptr;
mainptr->curlib->id.tag |= LIB_TAG_MISSING;
/* Set lib version to current main one... Makes assert later happy. */
mainptr->versionfile = mainptr->curlib->versionfile = mainl->versionfile;
mainptr->subversionfile = mainptr->curlib->subversionfile = mainl->subversionfile;
}
- if (fd == NULL) {
+ if (fd == nullptr) {
BLO_reportf_wrap(
basefd->reports, RPT_INFO, TIP_("Cannot find lib '%s'"), mainptr->curlib->filepath_abs);
basefd->reports->count.missing_libraries++;
@@ -4852,7 +4902,7 @@ static FileData *read_library_file_data(FileData *basefd,
static void read_libraries(FileData *basefd, ListBase *mainlist)
{
- Main *mainl = mainlist->first;
+ Main *mainl = static_cast<Main *>(mainlist->first);
bool do_it = true;
/* Expander is now callback function. */
@@ -4885,28 +4935,32 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
if (fd) {
do_it = true;
- if (mainptr->id_map == NULL) {
- mainptr->id_map = BKE_main_idmap_create(mainptr, false, NULL, MAIN_IDMAP_TYPE_NAME);
+ if (mainptr->id_map == nullptr) {
+ mainptr->id_map = BKE_main_idmap_create(mainptr, false, nullptr, MAIN_IDMAP_TYPE_NAME);
}
}
- /* Read linked data-locks for each link placeholder, and replace
- * the placeholder with the real data-lock. */
+ /* Read linked data-blocks for each link placeholder, and replace
+ * the placeholder with the real data-block. */
read_library_linked_ids(basefd, fd, mainlist, mainptr);
- /* Test if linked data-locks need to read further linked data-locks
+ /* Test if linked data-blocks need to read further linked data-blocks
* and create link placeholders for them. */
BLO_expand_main(fd, mainptr);
}
}
}
- Main *main_newid = BKE_main_new();
for (Main *mainptr = mainl->next; mainptr; mainptr = mainptr->next) {
- /* Drop weak links for which no data-block was found. */
+ /* Drop weak links for which no data-block was found.
+ * Since this can remap pointers in `libmap` of all libraries, it needs to be performed in its
+ * own loop, before any call to `lib_link_all` (and the freeing of the libraries' filedata). */
read_library_clear_weak_links(basefd, mainlist, mainptr);
+ }
- /* Do versioning for newly added linked data-locks. If no data-locks
+ Main *main_newid = BKE_main_new();
+ for (Main *mainptr = mainl->next; mainptr; mainptr = mainptr->next) {
+ /* Do versioning for newly added linked data-blocks. If no data-blocks
* were read from a library versionfile will still be zero and we can
* skip it. */
if (mainptr->versionfile) {
@@ -4919,7 +4973,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
do_versions(mainptr->curlib->filedata, mainptr->curlib, main_newid);
}
else {
- do_versions(basefd, NULL, main_newid);
+ do_versions(basefd, nullptr, main_newid);
}
add_main_to_main(mainptr, main_newid);
@@ -4939,7 +4993,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
if (mainptr->curlib->filedata) {
blo_filedata_free(mainptr->curlib->filedata);
}
- mainptr->curlib->filedata = NULL;
+ mainptr->curlib->filedata = nullptr;
}
BKE_main_free(main_newid);
}
@@ -4961,7 +5015,12 @@ void *BLO_read_get_new_packed_address(BlendDataReader *reader, const void *old_a
ID *BLO_read_get_new_id_address(BlendLibReader *reader, Library *lib, ID *id)
{
- return newlibadr(reader->fd, lib, id);
+ return static_cast<ID *>(newlibadr(reader->fd, lib, id));
+}
+
+int BLO_read_fileversion_get(BlendDataReader *reader)
+{
+ return reader->fd->fileversion;
}
bool BLO_read_requires_endian_switch(BlendDataReader *reader)
@@ -4976,14 +5035,14 @@ void BLO_read_list_cb(BlendDataReader *reader, ListBase *list, BlendReadListFn c
}
BLO_read_data_address(reader, &list->first);
- if (callback != NULL) {
+ if (callback != nullptr) {
callback(reader, list->first);
}
- Link *ln = list->first;
- Link *prev = NULL;
+ Link *ln = static_cast<Link *>(list->first);
+ Link *prev = nullptr;
while (ln) {
BLO_read_data_address(reader, &ln->next);
- if (ln->next != NULL && callback != NULL) {
+ if (ln->next != nullptr && callback != nullptr) {
callback(reader, ln->next);
}
ln->prev = prev;
@@ -4995,7 +5054,7 @@ void BLO_read_list_cb(BlendDataReader *reader, ListBase *list, BlendReadListFn c
void BLO_read_list(BlendDataReader *reader, struct ListBase *list)
{
- BLO_read_list_cb(reader, list, NULL);
+ BLO_read_list_cb(reader, list, nullptr);
}
void BLO_read_int32_array(BlendDataReader *reader, int array_size, int32_t **ptr_p)
@@ -5071,8 +5130,8 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p)
FileData *fd = reader->fd;
void *orig_array = newdataadr(fd, *ptr_p);
- if (orig_array == NULL) {
- *ptr_p = NULL;
+ if (orig_array == nullptr) {
+ *ptr_p = nullptr;
return;
}
@@ -5082,7 +5141,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p)
/* Over-allocation is fine, but might be better to pass the length as parameter. */
int array_size = MEM_allocN_len(orig_array) / file_pointer_size;
- void *final_array = NULL;
+ void *final_array = nullptr;
if (file_pointer_size == current_pointer_size) {
/* No pointer conversion necessary. */
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index 4522adb2aef..2171d513d69 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -17,6 +17,10 @@
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h" /* for eReportType */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct BLI_mmap_file;
struct BLOCacheStorage;
struct IDNameLib_Map;
@@ -38,6 +42,7 @@ enum eFileDataFlag {
/* XXX Unused in practice (checked once but never set). */
FD_FLAGS_NOT_MY_LIBMAP = 1 << 5,
};
+ENUM_OPERATORS(eFileDataFlag, FD_FLAGS_NOT_MY_LIBMAP)
/* Disallow since it's 32bit on ms-windows. */
#ifdef __GNUC__
@@ -207,6 +212,7 @@ void blo_do_versions_270(struct FileData *fd, struct Library *lib, struct Main *
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_400(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);
@@ -224,3 +230,7 @@ void do_versions_after_linking_cycles(struct Main *bmain);
* but better use that nasty hack in do_version than readfile itself.
*/
void *blo_read_get_new_globaldata_address(struct FileData *fd, const void *adr);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/blenloader/intern/readfile_tempload.c b/source/blender/blenloader/intern/readfile_tempload.cc
index d642dbb5012..933077924f5 100644
--- a/source/blender/blenloader/intern/readfile_tempload.c
+++ b/source/blender/blenloader/intern/readfile_tempload.cc
@@ -20,7 +20,8 @@ TempLibraryContext *BLO_library_temp_load_id(struct Main *real_main,
const char *idname,
struct ReportList *reports)
{
- TempLibraryContext *temp_lib_ctx = MEM_callocN(sizeof(*temp_lib_ctx), __func__);
+ TempLibraryContext *temp_lib_ctx = static_cast<TempLibraryContext *>(
+ MEM_callocN(sizeof(*temp_lib_ctx), __func__));
temp_lib_ctx->bmain_base = BKE_main_new();
temp_lib_ctx->bf_reports.reports = reports;
diff --git a/source/blender/blenloader/intern/undofile.c b/source/blender/blenloader/intern/undofile.cc
index b5c2a73c268..58c1020f4cb 100644
--- a/source/blender/blenloader/intern/undofile.c
+++ b/source/blender/blenloader/intern/undofile.cc
@@ -42,7 +42,7 @@ void BLO_memfile_free(MemFile *memfile)
{
MemFileChunk *chunk;
- while ((chunk = BLI_pophead(&memfile->chunks))) {
+ while ((chunk = static_cast<MemFileChunk *>(BLI_pophead(&memfile->chunks)))) {
if (chunk->is_identical == false) {
MEM_freeN((void *)chunk->buf);
}
@@ -59,7 +59,8 @@ void BLO_memfile_merge(MemFile *first, MemFile *second)
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
/* First, detect all memchunks in second memfile that are not owned by it. */
- for (MemFileChunk *sc = second->chunks.first; sc != NULL; sc = sc->next) {
+ for (MemFileChunk *sc = static_cast<MemFileChunk *>(second->chunks.first); sc != nullptr;
+ sc = static_cast<MemFileChunk *>(sc->next)) {
if (sc->is_identical) {
BLI_ghash_insert(buffer_to_second_memchunk, (void *)sc->buf, sc);
}
@@ -67,10 +68,12 @@ void BLO_memfile_merge(MemFile *first, MemFile *second)
/* Now, check all chunks from first memfile (the one we are removing), and if a memchunk owned by
* it is also used by the second memfile, transfer the ownership. */
- for (MemFileChunk *fc = first->chunks.first; fc != NULL; fc = fc->next) {
+ for (MemFileChunk *fc = static_cast<MemFileChunk *>(first->chunks.first); fc != nullptr;
+ fc = static_cast<MemFileChunk *>(fc->next)) {
if (!fc->is_identical) {
- MemFileChunk *sc = BLI_ghash_lookup(buffer_to_second_memchunk, fc->buf);
- if (sc != NULL) {
+ MemFileChunk *sc = static_cast<MemFileChunk *>(
+ BLI_ghash_lookup(buffer_to_second_memchunk, fc->buf));
+ if (sc != nullptr) {
BLI_assert(sc->is_identical);
sc->is_identical = false;
fc->is_identical = true;
@@ -81,7 +84,7 @@ void BLO_memfile_merge(MemFile *first, MemFile *second)
}
}
- BLI_ghash_free(buffer_to_second_memchunk, NULL, NULL);
+ BLI_ghash_free(buffer_to_second_memchunk, nullptr, nullptr);
BLO_memfile_free(first);
}
@@ -99,14 +102,16 @@ void BLO_memfile_write_init(MemFileWriteData *mem_data,
{
mem_data->written_memfile = written_memfile;
mem_data->reference_memfile = reference_memfile;
- mem_data->reference_current_chunk = reference_memfile ? reference_memfile->chunks.first : NULL;
+ mem_data->reference_current_chunk = reference_memfile ? static_cast<MemFileChunk *>(
+ reference_memfile->chunks.first) :
+ nullptr;
/* If we have a reference memfile, we generate a mapping between the session_uuid's of the
* IDs stored in that previous undo step, and its first matching memchunk. This will allow
* us to easily find the existing undo memory storage of IDs even when some re-ordering in
* current Main data-base broke the order matching with the memchunks from previous step.
*/
- if (reference_memfile != NULL) {
+ if (reference_memfile != nullptr) {
mem_data->id_session_uuid_mapping = BLI_ghash_new(
BLI_ghashutil_inthash_p_simple, BLI_ghashutil_intcmp, __func__);
uint current_session_uuid = MAIN_ID_SESSION_UUID_UNSET;
@@ -129,8 +134,8 @@ void BLO_memfile_write_init(MemFileWriteData *mem_data,
void BLO_memfile_write_finalize(MemFileWriteData *mem_data)
{
- if (mem_data->id_session_uuid_mapping != NULL) {
- BLI_ghash_free(mem_data->id_session_uuid_mapping, NULL, NULL);
+ if (mem_data->id_session_uuid_mapping != nullptr) {
+ BLI_ghash_free(mem_data->id_session_uuid_mapping, nullptr, nullptr);
}
}
@@ -139,9 +144,10 @@ void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t s
MemFile *memfile = mem_data->written_memfile;
MemFileChunk **compchunk_step = &mem_data->reference_current_chunk;
- MemFileChunk *curchunk = MEM_mallocN(sizeof(MemFileChunk), "MemFileChunk");
+ MemFileChunk *curchunk = static_cast<MemFileChunk *>(
+ MEM_mallocN(sizeof(MemFileChunk), "MemFileChunk"));
curchunk->size = size;
- curchunk->buf = NULL;
+ curchunk->buf = nullptr;
curchunk->is_identical = false;
/* This is unsafe in the sense that an app handler or other code that does not
* perform an undo push may make changes after the last undo push that
@@ -151,7 +157,7 @@ void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t s
BLI_addtail(&memfile->chunks, curchunk);
/* we compare compchunk with buf */
- if (*compchunk_step != NULL) {
+ if (*compchunk_step != nullptr) {
MemFileChunk *compchunk = *compchunk_step;
if (compchunk->size == curchunk->size) {
if (memcmp(compchunk->buf, buf, size) == 0) {
@@ -160,12 +166,12 @@ void BLO_memfile_chunk_add(MemFileWriteData *mem_data, const char *buf, size_t s
compchunk->is_identical_future = true;
}
}
- *compchunk_step = compchunk->next;
+ *compchunk_step = static_cast<MemFileChunk *>(compchunk->next);
}
/* not equal... */
- if (curchunk->buf == NULL) {
- char *buf_new = MEM_mallocN(size, "Chunk buffer");
+ if (curchunk->buf == nullptr) {
+ char *buf_new = static_cast<char *>(MEM_mallocN(size, "Chunk buffer"));
memcpy(buf_new, buf, size);
curchunk->buf = buf_new;
memfile->size += size;
@@ -176,12 +182,10 @@ struct Main *BLO_memfile_main_get(struct MemFile *memfile,
struct Main *bmain,
struct Scene **r_scene)
{
- struct Main *bmain_undo = NULL;
- BlendFileData *bfd = BLO_read_from_memfile(bmain,
- BKE_main_blendfile_path(bmain),
- memfile,
- &(const struct BlendFileReadParams){0},
- NULL);
+ struct Main *bmain_undo = nullptr;
+ BlendFileReadParams read_params{};
+ BlendFileData *bfd = BLO_read_from_memfile(
+ bmain, BKE_main_blendfile_path(bmain), memfile, &read_params, nullptr);
if (bfd) {
bmain_undo = bfd->main;
@@ -226,7 +230,8 @@ bool BLO_memfile_write_file(struct MemFile *memfile, const char *filepath)
return false;
}
- for (chunk = memfile->chunks.first; chunk; chunk = chunk->next) {
+ for (chunk = static_cast<MemFileChunk *>(memfile->chunks.first); chunk;
+ chunk = static_cast<MemFileChunk *>(chunk->next)) {
#ifdef _WIN32
if ((size_t)write(file, chunk->buf, (uint)chunk->size) != chunk->size)
#else
@@ -255,7 +260,7 @@ static ssize_t undo_read(FileReader *reader, void *buffer, size_t size)
static size_t seek = SIZE_MAX; /* The current position. */
static size_t offset = 0; /* Size of previous chunks. */
- static MemFileChunk *chunk = NULL;
+ static MemFileChunk *chunk = nullptr;
size_t chunkoffset, readsize, totread;
undo->memchunk_identical = true;
@@ -265,7 +270,7 @@ static ssize_t undo_read(FileReader *reader, void *buffer, size_t size)
}
if (seek != (size_t)undo->reader.offset) {
- chunk = undo->memfile->chunks.first;
+ chunk = static_cast<MemFileChunk *>(undo->memfile->chunks.first);
seek = 0;
while (chunk) {
@@ -273,7 +278,7 @@ static ssize_t undo_read(FileReader *reader, void *buffer, size_t size)
break;
}
seek += chunk->size;
- chunk = chunk->next;
+ chunk = static_cast<MemFileChunk *>(chunk->next);
}
offset = seek;
seek = (size_t)undo->reader.offset;
@@ -286,11 +291,11 @@ static ssize_t undo_read(FileReader *reader, void *buffer, size_t size)
/* First check if it's on the end if current chunk. */
if (seek - offset == chunk->size) {
offset += chunk->size;
- chunk = chunk->next;
+ chunk = static_cast<MemFileChunk *>(chunk->next);
}
/* Debug, should never happen. */
- if (chunk == NULL) {
+ if (chunk == nullptr) {
printf("illegal read, chunk zero\n");
return 0;
}
@@ -331,13 +336,13 @@ static void undo_close(FileReader *reader)
FileReader *BLO_memfile_new_filereader(MemFile *memfile, int undo_direction)
{
- UndoReader *undo = MEM_callocN(sizeof(UndoReader), __func__);
+ UndoReader *undo = static_cast<UndoReader *>(MEM_callocN(sizeof(UndoReader), __func__));
undo->memfile = memfile;
undo->undo_direction = undo_direction;
undo->reader.read = undo_read;
- undo->reader.seek = NULL;
+ undo->reader.seek = nullptr;
undo->reader.close = undo_close;
return (FileReader *)undo;
diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c
index 67b6ad25d8b..9e5ef41892a 100644
--- a/source/blender/blenloader/intern/versioning_250.c
+++ b/source/blender/blenloader/intern/versioning_250.c
@@ -54,6 +54,7 @@
#include "BKE_global.h" /* for G */
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_multires.h"
#include "BKE_node_tree_update.h"
@@ -425,14 +426,14 @@ static void do_versions_windowmanager_2_50(bScreen *screen)
}
}
-static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name)
+static void versions_gpencil_add_main(Main *bmain, ListBase *lb, ID *id, const char *name)
{
BLI_addtail(lb, id);
id->us = 1;
id->flag = LIB_FAKEUSER;
*((short *)id->name) = ID_GD;
- BKE_id_new_name_validate(lb, id, name, false);
+ BKE_id_new_name_validate(bmain, lb, id, name, false);
/* alphabetic insertion: is in BKE_id_new_name_validate */
if ((id->tag & LIB_TAG_TEMP_MAIN) == 0) {
@@ -455,21 +456,21 @@ static void do_versions_gpencil_2_50(Main *main, bScreen *screen)
if (sl->spacetype == SPACE_VIEW3D) {
View3D *v3d = (View3D *)sl;
if (v3d->gpd) {
- versions_gpencil_add_main(&main->gpencils, (ID *)v3d->gpd, "GPencil View3D");
+ versions_gpencil_add_main(main, &main->gpencils, (ID *)v3d->gpd, "GPencil View3D");
v3d->gpd = NULL;
}
}
else if (sl->spacetype == SPACE_NODE) {
SpaceNode *snode = (SpaceNode *)sl;
if (snode->gpd) {
- versions_gpencil_add_main(&main->gpencils, (ID *)snode->gpd, "GPencil Node");
+ versions_gpencil_add_main(main, &main->gpencils, (ID *)snode->gpd, "GPencil Node");
snode->gpd = NULL;
}
}
else if (sl->spacetype == SPACE_SEQ) {
SpaceSeq *sseq = (SpaceSeq *)sl;
if (sseq->gpd) {
- versions_gpencil_add_main(&main->gpencils, (ID *)sseq->gpd, "GPencil Node");
+ versions_gpencil_add_main(main, &main->gpencils, (ID *)sseq->gpd, "GPencil Node");
sseq->gpd = NULL;
}
}
@@ -477,7 +478,7 @@ static void do_versions_gpencil_2_50(Main *main, bScreen *screen)
SpaceImage *sima = (SpaceImage *)sl;
#if 0 /* see comment on r28002 */
if (sima->gpd) {
- versions_gpencil_add_main(&main->gpencil, (ID *)sima->gpd, "GPencil Image");
+ versions_gpencil_add_main(main, &main->gpencil, (ID *)sima->gpd, "GPencil Image");
sima->gpd = NULL;
}
#else
@@ -989,15 +990,15 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain)
int a, tot;
/* shape keys are no longer applied to the mesh itself, but rather
- * to the evaluated #Mesh / #DispList, so here we ensure that the basis
+ * to the evaluated #Mesh, so here we ensure that the basis
* shape key is always set in the mesh coordinates. */
for (me = bmain->meshes.first; me; me = me->id.next) {
if ((key = blo_do_versions_newlibadr(fd, lib, me->key)) && key->refkey) {
data = key->refkey->data;
tot = MIN2(me->totvert, key->refkey->totelem);
-
+ MVert *verts = BKE_mesh_verts_for_write(me);
for (a = 0; a < tot; a++, data += 3) {
- copy_v3_v3(me->mvert[a].co, data);
+ copy_v3_v3(verts[a].co, data);
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 82e24801b66..5cbfcb66bf9 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -367,7 +367,7 @@ static void do_version_scene_collection_to_collection(Main *bmain, Scene *scene)
BLI_listbase_clear(&scene->view_layers);
if (!scene->master_collection) {
- scene->master_collection = BKE_collection_master_add();
+ scene->master_collection = BKE_collection_master_add(scene);
}
/* Convert scene collections. */
@@ -411,7 +411,7 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene)
/* Since we don't have access to FileData we check the (always valid) first
* render layer instead. */
if (!scene->master_collection) {
- scene->master_collection = BKE_collection_master_add();
+ scene->master_collection = BKE_collection_master_add(scene);
}
if (scene->view_layers.first) {
@@ -512,12 +512,13 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene)
}
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
/* for convenience set the same active object in all the layers */
if (scene->basact) {
view_layer->basact = BKE_view_layer_base_find(view_layer, scene->basact->object);
}
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if ((base->flag & BASE_SELECTABLE) && (base->object->flag & SELECT)) {
base->flag |= BASE_SELECTED;
}
@@ -537,13 +538,14 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene)
view_layer->flag &= ~VIEW_LAYER_RENDER;
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
/* convert active base */
if (scene->basact) {
view_layer->basact = BKE_view_layer_base_find(view_layer, scene->basact->object);
}
/* convert selected bases */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if ((base->flag & BASE_SELECTABLE) && (base->object->flag & SELECT)) {
base->flag |= BASE_SELECTED;
}
@@ -1794,10 +1796,9 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (DNA_struct_find(fd->filesdna, "MTexPoly")) {
for (Mesh *me = bmain->meshes.first; me; me = me->id.next) {
/* If we have UV's, so this file will have MTexPoly layers too! */
- if (me->mloopuv != NULL) {
+ if (CustomData_has_layer(&me->ldata, CD_MLOOPUV)) {
CustomData_update_typemap(&me->pdata);
CustomData_free_layers(&me->pdata, CD_MTEXPOLY, me->totpoly);
- BKE_mesh_update_customdata_pointers(me, false);
}
}
}
@@ -3549,7 +3550,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 280, 43)) {
ListBase *lb = which_libbase(bmain, ID_BR);
- BKE_main_id_repair_duplicate_names_listbase(lb);
+ BKE_main_id_repair_duplicate_names_listbase(bmain, lb);
}
if (!MAIN_VERSION_ATLEAST(bmain, 280, 44)) {
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index de652b40590..b285e1829b7 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -819,21 +819,21 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (MAIN_VERSION_ATLEAST(bmain, 290, 2) && MAIN_VERSION_OLDER(bmain, 291, 1)) {
/* In this range, the extrude manifold could generate meshes with degenerated face. */
LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) {
- for (MPoly *mp = me->mpoly, *mp_end = mp + me->totpoly; mp < mp_end; mp++) {
+ for (const MPoly *mp = BKE_mesh_polys(me), *mp_end = mp + me->totpoly; mp < mp_end; mp++) {
if (mp->totloop == 2) {
bool changed;
BKE_mesh_validate_arrays(me,
- me->mvert,
+ BKE_mesh_verts_for_write(me),
me->totvert,
- me->medge,
+ BKE_mesh_edges_for_write(me),
me->totedge,
- me->mface,
+ (MFace *)CustomData_get_layer(&me->fdata, CD_MFACE),
me->totface,
- me->mloop,
+ BKE_mesh_loops_for_write(me),
me->totloop,
- me->mpoly,
+ BKE_mesh_polys_for_write(me),
me->totpoly,
- me->dvert,
+ BKE_mesh_deform_verts_for_write(me),
false,
true,
&changed);
@@ -849,7 +849,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
short id_codes[] = {ID_BR, ID_PAL};
for (int i = 0; i < ARRAY_SIZE(id_codes); i++) {
ListBase *lb = which_libbase(bmain, id_codes[i]);
- BKE_main_id_repair_duplicate_names_listbase(lb);
+ BKE_main_id_repair_duplicate_names_listbase(bmain, lb);
}
}
@@ -926,7 +926,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
if (md->mode & eModifierMode_Expanded_DEPRECATED) {
- md->ui_expand_flag = 1;
+ md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
}
else {
md->ui_expand_flag = 0;
@@ -954,7 +954,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (bConstraint *, con, &object->constraints) {
if (con->flag & CONSTRAINT_EXPAND_DEPRECATED) {
- con->ui_expand_flag = 1;
+ con->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
}
else {
con->ui_expand_flag = 0;
@@ -968,7 +968,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (GpencilModifierData *, md, &object->greasepencil_modifiers) {
if (md->mode & eGpencilModifierMode_Expanded_DEPRECATED) {
- md->ui_expand_flag = 1;
+ md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
}
else {
md->ui_expand_flag = 0;
@@ -982,7 +982,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) {
LISTBASE_FOREACH (ShaderFxData *, fx, &object->shader_fx) {
if (fx->mode & eShaderFxMode_Expanded_DEPRECATED) {
- fx->ui_expand_flag = 1;
+ fx->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT;
}
else {
fx->ui_expand_flag = 0;
@@ -1697,7 +1697,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /* Add subpanels for FModifiers, which requires a field to store expansion. */
+ /* Add sub-panels for FModifiers, which requires a field to store expansion. */
if (!DNA_struct_elem_find(fd->filesdna, "FModifier", "short", "ui_expand_flag")) {
LISTBASE_FOREACH (bAction *, act, &bmain->actions) {
LISTBASE_FOREACH (FCurve *, fcu, &act->curves) {
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.cc
index 14204479849..8f1f2fa2c17 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.cc
@@ -6,7 +6,7 @@
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
-#include <string.h>
+#include <cstring>
#include "CLG_log.h"
@@ -57,6 +57,7 @@
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_screen.h"
@@ -85,39 +86,40 @@ static IDProperty *idproperty_find_ui_container(IDProperty *idprop_group)
return prop;
}
}
- return NULL;
+ return nullptr;
}
static void version_idproperty_move_data_int(IDPropertyUIDataInt *ui_data,
const IDProperty *prop_ui_data)
{
IDProperty *min = IDP_GetPropertyFromGroup(prop_ui_data, "min");
- if (min != NULL) {
+ if (min != nullptr) {
ui_data->min = ui_data->soft_min = IDP_coerce_to_int_or_zero(min);
}
IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max");
- if (max != NULL) {
+ if (max != nullptr) {
ui_data->max = ui_data->soft_max = IDP_coerce_to_int_or_zero(max);
}
IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min");
- if (soft_min != NULL) {
+ if (soft_min != nullptr) {
ui_data->soft_min = IDP_coerce_to_int_or_zero(soft_min);
ui_data->soft_min = MIN2(ui_data->soft_min, ui_data->min);
}
IDProperty *soft_max = IDP_GetPropertyFromGroup(prop_ui_data, "soft_max");
- if (soft_max != NULL) {
+ if (soft_max != nullptr) {
ui_data->soft_max = IDP_coerce_to_int_or_zero(soft_max);
ui_data->soft_max = MAX2(ui_data->soft_max, ui_data->max);
}
IDProperty *step = IDP_GetPropertyFromGroup(prop_ui_data, "step");
- if (step != NULL) {
+ if (step != nullptr) {
ui_data->step = IDP_coerce_to_int_or_zero(soft_max);
}
IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
- if (default_value != NULL) {
+ if (default_value != nullptr) {
if (default_value->type == IDP_ARRAY) {
if (default_value->subtype == IDP_INT) {
- ui_data->default_array = MEM_malloc_arrayN(default_value->len, sizeof(int), __func__);
+ ui_data->default_array = static_cast<int *>(
+ MEM_malloc_arrayN(default_value->len, sizeof(int), __func__));
memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(int) * default_value->len);
ui_data->default_array_len = default_value->len;
}
@@ -132,45 +134,47 @@ static void version_idproperty_move_data_float(IDPropertyUIDataFloat *ui_data,
const IDProperty *prop_ui_data)
{
IDProperty *min = IDP_GetPropertyFromGroup(prop_ui_data, "min");
- if (min != NULL) {
+ if (min != nullptr) {
ui_data->min = ui_data->soft_min = IDP_coerce_to_double_or_zero(min);
}
IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max");
- if (max != NULL) {
+ if (max != nullptr) {
ui_data->max = ui_data->soft_max = IDP_coerce_to_double_or_zero(max);
}
IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min");
- if (soft_min != NULL) {
+ if (soft_min != nullptr) {
ui_data->soft_min = IDP_coerce_to_double_or_zero(soft_min);
ui_data->soft_min = MAX2(ui_data->soft_min, ui_data->min);
}
IDProperty *soft_max = IDP_GetPropertyFromGroup(prop_ui_data, "soft_max");
- if (soft_max != NULL) {
+ if (soft_max != nullptr) {
ui_data->soft_max = IDP_coerce_to_double_or_zero(soft_max);
ui_data->soft_max = MIN2(ui_data->soft_max, ui_data->max);
}
IDProperty *step = IDP_GetPropertyFromGroup(prop_ui_data, "step");
- if (step != NULL) {
+ if (step != nullptr) {
ui_data->step = IDP_coerce_to_float_or_zero(step);
}
IDProperty *precision = IDP_GetPropertyFromGroup(prop_ui_data, "precision");
- if (precision != NULL) {
+ if (precision != nullptr) {
ui_data->precision = IDP_coerce_to_int_or_zero(precision);
}
IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
- if (default_value != NULL) {
+ if (default_value != nullptr) {
if (default_value->type == IDP_ARRAY) {
const int array_len = default_value->len;
ui_data->default_array_len = array_len;
if (default_value->subtype == IDP_FLOAT) {
- ui_data->default_array = MEM_malloc_arrayN(array_len, sizeof(double), __func__);
- const float *old_default_array = IDP_Array(default_value);
+ ui_data->default_array = static_cast<double *>(
+ MEM_malloc_arrayN(array_len, sizeof(double), __func__));
+ const float *old_default_array = static_cast<const float *>(IDP_Array(default_value));
for (int i = 0; i < ui_data->default_array_len; i++) {
ui_data->default_array[i] = (double)old_default_array[i];
}
}
else if (default_value->subtype == IDP_DOUBLE) {
- ui_data->default_array = MEM_malloc_arrayN(array_len, sizeof(double), __func__);
+ ui_data->default_array = static_cast<double *>(
+ MEM_malloc_arrayN(array_len, sizeof(double), __func__));
memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(double) * array_len);
}
}
@@ -184,25 +188,26 @@ static void version_idproperty_move_data_string(IDPropertyUIDataString *ui_data,
const IDProperty *prop_ui_data)
{
IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
- if (default_value != NULL && default_value->type == IDP_STRING) {
+ if (default_value != nullptr && default_value->type == IDP_STRING) {
ui_data->default_value = BLI_strdup(IDP_String(default_value));
}
}
static void version_idproperty_ui_data(IDProperty *idprop_group)
{
- if (idprop_group == NULL) { /* NULL check here to reduce verbosity of calls to this function. */
+ if (idprop_group ==
+ nullptr) { /* nullptr check here to reduce verbosity of calls to this function. */
return;
}
IDProperty *ui_container = idproperty_find_ui_container(idprop_group);
- if (ui_container == NULL) {
+ if (ui_container == nullptr) {
return;
}
LISTBASE_FOREACH (IDProperty *, prop, &idprop_group->data.group) {
IDProperty *prop_ui_data = IDP_GetPropertyFromGroup(ui_container, prop->name);
- if (prop_ui_data == NULL) {
+ if (prop_ui_data == nullptr) {
continue;
}
@@ -213,7 +218,7 @@ static void version_idproperty_ui_data(IDProperty *idprop_group)
IDPropertyUIData *ui_data = IDP_ui_data_ensure(prop);
IDProperty *subtype = IDP_GetPropertyFromGroup(prop_ui_data, "subtype");
- if (subtype != NULL && subtype->type == IDP_STRING) {
+ if (subtype != nullptr && subtype->type == IDP_STRING) {
const char *subtype_string = IDP_String(subtype);
int result = PROP_NONE;
RNA_enum_value_from_id(rna_enum_property_subtype_items, subtype_string, &result);
@@ -221,7 +226,7 @@ static void version_idproperty_ui_data(IDProperty *idprop_group)
}
IDProperty *description = IDP_GetPropertyFromGroup(prop_ui_data, "description");
- if (description != NULL && description->type == IDP_STRING) {
+ if (description != nullptr && description->type == IDP_STRING) {
ui_data->description = BLI_strdup(IDP_String(description));
}
@@ -317,7 +322,7 @@ static void do_versions_idproperty_ui_data(Main *bmain)
}
/* Object post bones. */
- if (ob->type == OB_ARMATURE && ob->pose != NULL) {
+ if (ob->type == OB_ARMATURE && ob->pose != nullptr) {
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
version_idproperty_ui_data(pchan->prop);
}
@@ -326,7 +331,7 @@ static void do_versions_idproperty_ui_data(Main *bmain)
/* Sequences. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->ed != NULL) {
+ if (scene->ed != nullptr) {
do_versions_idproperty_seq_recursive(&scene->ed->seqbase);
}
}
@@ -342,7 +347,7 @@ static void sort_linked_ids(Main *bmain)
if (ID_IS_LINKED(id)) {
BLI_remlink(lb, id);
BLI_addtail(&temp_list, id);
- id_sort_by_name(&temp_list, id, NULL);
+ id_sort_by_name(&temp_list, id, nullptr);
}
}
BLI_movelisttolist(lb, &temp_list);
@@ -355,9 +360,9 @@ static void assert_sorted_ids(Main *bmain)
#ifndef NDEBUG
ListBase *lb;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
- ID *id_prev = NULL;
+ ID *id_prev = nullptr;
LISTBASE_FOREACH (ID *, id, lb) {
- if (id_prev == NULL) {
+ if (id_prev == nullptr) {
continue;
}
BLI_assert(id_prev->lib != id->lib || BLI_strcasecmp(id_prev->name, id->name) < 0);
@@ -398,7 +403,7 @@ static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const Lis
LISTBASE_FOREACH (Sequence *, seq, seqbase) {
if (seq->type == SEQ_TYPE_SPEED) {
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
- const char *substr = NULL;
+ const char *substr = nullptr;
float globalSpeed = v->globalSpeed;
if (seq->flag & SEQ_USE_EFFECT_DEFAULT_FADE) {
if (globalSpeed == 1.0f) {
@@ -432,7 +437,8 @@ static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const Lis
v->flags &= ~(SEQ_SPEED_INTEGRATE | SEQ_SPEED_COMPRESS_IPO_Y);
if (substr || globalSpeed != 1.0f) {
- FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
+ FCurve *fcu = id_data_find_fcurve(
+ &scene->id, seq, &RNA_Sequence, "speed_factor", 0, nullptr);
if (fcu) {
if (globalSpeed != 1.0f) {
for (int i = 0; i < fcu->totvert; i++) {
@@ -488,7 +494,7 @@ static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket)
return link;
}
}
- return NULL;
+ return nullptr;
}
static void add_realize_instances_before_socket(bNodeTree *ntree,
@@ -497,7 +503,7 @@ static void add_realize_instances_before_socket(bNodeTree *ntree,
{
BLI_assert(geometry_socket->type == SOCK_GEOMETRY);
bNodeLink *link = find_connected_link(ntree, geometry_socket);
- if (link == NULL) {
+ if (link == nullptr) {
return;
}
@@ -506,13 +512,17 @@ static void add_realize_instances_before_socket(bNodeTree *ntree,
return;
}
- bNode *realize_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_REALIZE_INSTANCES);
+ bNode *realize_node = nodeAddStaticNode(nullptr, ntree, GEO_NODE_REALIZE_INSTANCES);
realize_node->parent = node->parent;
realize_node->locx = node->locx - 100;
realize_node->locy = node->locy;
- nodeAddLink(ntree, link->fromnode, link->fromsock, realize_node, realize_node->inputs.first);
+ nodeAddLink(ntree,
+ link->fromnode,
+ link->fromsock,
+ realize_node,
+ static_cast<bNodeSocket *>(realize_node->inputs.first));
link->fromnode = realize_node;
- link->fromsock = realize_node->outputs.first;
+ link->fromsock = static_cast<bNodeSocket *>(realize_node->outputs.first);
}
/**
@@ -536,7 +546,7 @@ static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree)
GEO_NODE_REPLACE_MATERIAL,
GEO_NODE_SUBDIVIDE_MESH,
GEO_NODE_TRIANGULATE)) {
- bNodeSocket *geometry_socket = node->inputs.first;
+ bNodeSocket *geometry_socket = static_cast<bNodeSocket *>(node->inputs.first);
add_realize_instances_before_socket(ntree, node, geometry_socket);
}
/* Also realize instances for the profile input of the curve to mesh node. */
@@ -559,33 +569,65 @@ static bNodeTree *add_realize_node_tree(Main *bmain)
ntreeAddSocketInterface(node_tree, SOCK_IN, "NodeSocketGeometry", "Geometry");
ntreeAddSocketInterface(node_tree, SOCK_OUT, "NodeSocketGeometry", "Geometry");
- bNode *group_input = nodeAddStaticNode(NULL, node_tree, NODE_GROUP_INPUT);
+ bNode *group_input = nodeAddStaticNode(nullptr, node_tree, NODE_GROUP_INPUT);
group_input->locx = -400.0f;
- bNode *group_output = nodeAddStaticNode(NULL, node_tree, NODE_GROUP_OUTPUT);
+ bNode *group_output = nodeAddStaticNode(nullptr, node_tree, NODE_GROUP_OUTPUT);
group_output->locx = 500.0f;
group_output->flag |= NODE_DO_OUTPUT;
- bNode *join = nodeAddStaticNode(NULL, node_tree, GEO_NODE_JOIN_GEOMETRY);
+ bNode *join = nodeAddStaticNode(nullptr, node_tree, GEO_NODE_JOIN_GEOMETRY);
join->locx = group_output->locx - 175.0f;
join->locy = group_output->locy;
- bNode *conv = nodeAddStaticNode(NULL, node_tree, GEO_NODE_POINTS_TO_VERTICES);
+ bNode *conv = nodeAddStaticNode(nullptr, node_tree, GEO_NODE_POINTS_TO_VERTICES);
conv->locx = join->locx - 175.0f;
conv->locy = join->locy - 70.0;
- bNode *separate = nodeAddStaticNode(NULL, node_tree, GEO_NODE_SEPARATE_COMPONENTS);
+ bNode *separate = nodeAddStaticNode(nullptr, node_tree, GEO_NODE_SEPARATE_COMPONENTS);
separate->locx = join->locx - 350.0f;
separate->locy = join->locy + 50.0f;
- bNode *realize = nodeAddStaticNode(NULL, node_tree, GEO_NODE_REALIZE_INSTANCES);
+ bNode *realize = nodeAddStaticNode(nullptr, node_tree, GEO_NODE_REALIZE_INSTANCES);
realize->locx = separate->locx - 200.0f;
realize->locy = join->locy;
- nodeAddLink(node_tree, group_input, group_input->outputs.first, realize, realize->inputs.first);
- nodeAddLink(node_tree, realize, realize->outputs.first, separate, separate->inputs.first);
- nodeAddLink(node_tree, conv, conv->outputs.first, join, join->inputs.first);
- nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 3), join, join->inputs.first);
- nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 1), conv, conv->inputs.first);
- nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 2), join, join->inputs.first);
- nodeAddLink(node_tree, separate, separate->outputs.first, join, join->inputs.first);
- nodeAddLink(node_tree, join, join->outputs.first, group_output, group_output->inputs.first);
+ nodeAddLink(node_tree,
+ group_input,
+ static_cast<bNodeSocket *>(group_input->outputs.first),
+ realize,
+ static_cast<bNodeSocket *>(realize->inputs.first));
+ nodeAddLink(node_tree,
+ realize,
+ static_cast<bNodeSocket *>(realize->outputs.first),
+ separate,
+ static_cast<bNodeSocket *>(separate->inputs.first));
+ nodeAddLink(node_tree,
+ conv,
+ static_cast<bNodeSocket *>(conv->outputs.first),
+ join,
+ static_cast<bNodeSocket *>(join->inputs.first));
+ nodeAddLink(node_tree,
+ separate,
+ static_cast<bNodeSocket *>(BLI_findlink(&separate->outputs, 3)),
+ join,
+ static_cast<bNodeSocket *>(join->inputs.first));
+ nodeAddLink(node_tree,
+ separate,
+ static_cast<bNodeSocket *>(BLI_findlink(&separate->outputs, 1)),
+ conv,
+ static_cast<bNodeSocket *>(conv->inputs.first));
+ nodeAddLink(node_tree,
+ separate,
+ static_cast<bNodeSocket *>(BLI_findlink(&separate->outputs, 2)),
+ join,
+ static_cast<bNodeSocket *>(join->inputs.first));
+ nodeAddLink(node_tree,
+ separate,
+ static_cast<bNodeSocket *>(separate->outputs.first),
+ join,
+ static_cast<bNodeSocket *>(join->inputs.first));
+ nodeAddLink(node_tree,
+ join,
+ static_cast<bNodeSocket *>(join->outputs.first),
+ group_output,
+ static_cast<bNodeSocket *>(group_output->inputs.first));
LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) {
nodeSetSelected(node, false);
@@ -601,7 +643,7 @@ static void seq_speed_factor_fix_rna_path(Sequence *seq, ListBase *fcurves)
BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc));
char *path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].pitch", name_esc);
FCurve *fcu = BKE_fcurve_find(fcurves, path, 0);
- if (fcu != NULL) {
+ if (fcu != nullptr) {
MEM_freeN(fcu->rna_path);
fcu->rna_path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].speed_factor", name_esc);
}
@@ -610,7 +652,7 @@ static void seq_speed_factor_fix_rna_path(Sequence *seq, ListBase *fcurves)
static bool seq_speed_factor_set(Sequence *seq, void *user_data)
{
- const Scene *scene = user_data;
+ const Scene *scene = static_cast<const Scene *>(user_data);
if (seq->type == SEQ_TYPE_SOUND_RAM) {
/* Move `pitch` animation to `speed_factor` */
if (scene->adt && scene->adt->action) {
@@ -654,7 +696,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
if (!MAIN_VERSION_ATLEAST(bmain, 300, 13)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->ed != NULL) {
+ if (scene->ed != nullptr) {
do_versions_sequencer_speed_effect_recursive(scene, &scene->ed->seqbase);
}
}
@@ -668,24 +710,24 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *tool_settings = scene->toolsettings;
ImagePaintSettings *imapaint = &tool_settings->imapaint;
- if (imapaint->canvas != NULL &&
+ if (imapaint->canvas != nullptr &&
ELEM(imapaint->canvas->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
- imapaint->canvas = NULL;
+ imapaint->canvas = nullptr;
}
- if (imapaint->stencil != NULL &&
+ if (imapaint->stencil != nullptr &&
ELEM(imapaint->stencil->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
- imapaint->stencil = NULL;
+ imapaint->stencil = nullptr;
}
- if (imapaint->clone != NULL &&
+ if (imapaint->clone != nullptr &&
ELEM(imapaint->clone->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
- imapaint->clone = NULL;
+ imapaint->clone = nullptr;
}
}
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
- if (brush->clone.image != NULL &&
+ if (brush->clone.image != nullptr &&
ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
- brush->clone.image = NULL;
+ brush->clone.image = nullptr;
}
}
}
@@ -744,20 +786,20 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
if (!MAIN_VERSION_ATLEAST(bmain, 300, 35)) {
/* Add a new modifier to realize instances from previous modifiers.
* Previously that was done automatically by geometry nodes. */
- bNodeTree *realize_instances_node_tree = NULL;
+ bNodeTree *realize_instances_node_tree = nullptr;
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
LISTBASE_FOREACH_MUTABLE (ModifierData *, md, &ob->modifiers) {
if (md->type != eModifierType_Nodes) {
continue;
}
- if (md->next == NULL) {
+ if (md->next == nullptr) {
break;
}
if (md->next->type == eModifierType_Nodes) {
continue;
}
NodesModifierData *nmd = (NodesModifierData *)md;
- if (nmd->node_group == NULL) {
+ if (nmd->node_group == nullptr) {
continue;
}
@@ -765,7 +807,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
STRNCPY(new_nmd->modifier.name, "Realize Instances 2.93 Legacy");
BKE_modifier_unique_name(&ob->modifiers, &new_nmd->modifier);
BLI_insertlinkafter(&ob->modifiers, md, new_nmd);
- if (realize_instances_node_tree == NULL) {
+ if (realize_instances_node_tree == nullptr) {
realize_instances_node_tree = add_realize_node_tree(bmain);
}
new_nmd->node_group = realize_instances_node_tree;
@@ -778,7 +820,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_BOUNDING_BOX) {
- bNodeSocket *geometry_socket = node->inputs.first;
+ bNodeSocket *geometry_socket = static_cast<bNodeSocket *>(node->inputs.first);
add_realize_instances_before_socket(ntree, node, geometry_socket);
}
}
@@ -791,7 +833,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
AnimData *adt = BKE_animdata_from_id(id);
- if (adt == NULL) {
+ if (adt == nullptr) {
continue;
}
LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) {
@@ -824,7 +866,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
if (!MAIN_VERSION_ATLEAST(bmain, 302, 14)) {
/* Sequencer channels region. */
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype != SPACE_SEQ) {
@@ -842,7 +884,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
ARegion *timeline_region = BKE_region_find_in_listbase_by_type(regionbase,
RGN_TYPE_WINDOW);
- if (timeline_region == NULL) {
+ if (timeline_region == nullptr) {
continue;
}
@@ -856,13 +898,41 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
if (!MAIN_VERSION_ATLEAST(bmain, 303, 5)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Editing *ed = SEQ_editing_get(scene);
- if (ed == NULL) {
+ if (ed == nullptr) {
continue;
}
SEQ_for_each_callback(&ed->seqbase, seq_speed_factor_set, scene);
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 303, 6)) {
+ /* In the Dope Sheet, for every mode other than Timeline, open the Properties panel. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype != SPACE_ACTION) {
+ continue;
+ }
+
+ /* Skip the timeline, it shouldn't get its Properties panel opened. */
+ SpaceAction *saction = (SpaceAction *)sl;
+ if (saction->mode == SACTCONT_TIMELINE) {
+ continue;
+ }
+
+ const bool is_first_space = sl == area->spacedata.first;
+ ListBase *regionbase = is_first_space ? &area->regionbase : &sl->regionbase;
+ ARegion *region = BKE_region_find_in_listbase_by_type(regionbase, RGN_TYPE_UI);
+ if (region == nullptr) {
+ continue;
+ }
+
+ region->flag &= ~RGN_FLAG_HIDDEN;
+ }
+ }
+ }
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -908,7 +978,7 @@ static bool replace_bbone_len_scale_rnapath(char **p_old_path, int *p_index)
{
char *old_path = *p_old_path;
- if (old_path == NULL) {
+ if (old_path == nullptr) {
return false;
}
@@ -948,7 +1018,7 @@ static void do_version_bbone_len_scale_fcurve_fix(FCurve *fcu)
if (fcu->driver) {
LISTBASE_FOREACH (DriverVar *, dvar, &fcu->driver->variables) {
DRIVER_TARGETS_LOOPER_BEGIN (dvar) {
- replace_bbone_len_scale_rnapath(&dtar->rna_path, NULL);
+ replace_bbone_len_scale_rnapath(&dtar->rna_path, nullptr);
}
DRIVER_TARGETS_LOOPER_END;
}
@@ -987,7 +1057,7 @@ static void do_version_constraints_spline_ik_joint_bindings(ListBase *lb)
LISTBASE_FOREACH (bConstraint *, con, lb) {
if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
bSplineIKConstraint *data = (bSplineIKConstraint *)con->data;
- if (data->points == NULL) {
+ if (data->points == nullptr) {
data->numpoints = 0;
}
}
@@ -1011,7 +1081,7 @@ static bNodeSocket *do_version_replace_float_size_with_vector(bNodeTree *ntree,
static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data))
{
StripTransform *transform = seq->strip->transform;
- if (seq->strip->transform != NULL) {
+ if (seq->strip->transform != nullptr) {
transform->origin[0] = transform->origin[1] = 0.5f;
}
return true;
@@ -1020,7 +1090,7 @@ static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data))
static bool seq_transform_filter_set(Sequence *seq, void *UNUSED(user_data))
{
StripTransform *transform = seq->strip->transform;
- if (seq->strip->transform != NULL) {
+ if (seq->strip->transform != nullptr) {
transform->filter = SEQ_TRANSFORM_FILTER_BILINEAR;
}
return true;
@@ -1050,7 +1120,7 @@ static void do_version_subsurface_methods(bNode *node)
static void version_geometry_nodes_add_attribute_input_settings(NodesModifierData *nmd)
{
- if (nmd->settings.properties == NULL) {
+ if (nmd->settings.properties == nullptr) {
return;
}
/* Before versioning the properties, make sure it hasn't been done already. */
@@ -1210,7 +1280,7 @@ static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree)
/* The offset socket didn't exist in the file yet. */
return;
}
- bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3);
+ bNodeSocket *old_offset_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 3));
if (old_offset_socket->type == SOCK_VECTOR) {
/* Versioning happened already. */
return;
@@ -1228,7 +1298,8 @@ static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree)
if (!STREQ(link->tosock->identifier, "Position")) {
continue;
}
- bNodeSocket *old_offset_socket = BLI_findlink(&link->tonode->inputs, 3);
+ bNodeSocket *old_offset_socket = static_cast<bNodeSocket *>(
+ BLI_findlink(&link->tonode->inputs, 3));
/* This assumes that the offset is not linked to something else. That seems to be a reasonable
* assumption, because the node is probably only ever used in one or the other mode. */
const bool offset_enabled =
@@ -1244,7 +1315,7 @@ static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree)
if (node->type != GEO_NODE_SET_POSITION) {
continue;
}
- bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3);
+ bNodeSocket *old_offset_socket = static_cast<bNodeSocket *>(BLI_findlink(&node->inputs, 3));
nodeRemoveSocket(ntree, node, old_offset_socket);
}
}
@@ -1296,14 +1367,16 @@ static void version_liboverride_rnacollections_insertion_object_constraints(
if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) {
continue;
}
- bConstraint *constraint_anchor = BLI_listbase_string_or_index_find(constraints,
- opop->subitem_local_name,
- offsetof(bConstraint, name),
- opop->subitem_local_index);
- bConstraint *constraint_src = constraint_anchor != NULL ? constraint_anchor->next :
- constraints->first;
+ bConstraint *constraint_anchor = static_cast<bConstraint *>(
+ BLI_listbase_string_or_index_find(constraints,
+ opop->subitem_local_name,
+ offsetof(bConstraint, name),
+ opop->subitem_local_index));
+ bConstraint *constraint_src = constraint_anchor != nullptr ?
+ constraint_anchor->next :
+ static_cast<bConstraint *>(constraints->first);
- if (constraint_src == NULL) {
+ if (constraint_src == nullptr) {
/* Invalid case, just remove that override property operation. */
CLOG_ERROR(&LOG, "Could not find source constraint in stored override data");
BKE_lib_override_library_property_operation_delete(op, opop);
@@ -1323,18 +1396,21 @@ static void version_liboverride_rnacollections_insertion_object(Object *object)
IDOverrideLibraryProperty *op;
op = BKE_lib_override_library_property_find(liboverride, "modifiers");
- if (op != NULL) {
+ if (op != nullptr) {
LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) {
continue;
}
- ModifierData *mod_anchor = BLI_listbase_string_or_index_find(&object->modifiers,
- opop->subitem_local_name,
- offsetof(ModifierData, name),
- opop->subitem_local_index);
- ModifierData *mod_src = mod_anchor != NULL ? mod_anchor->next : object->modifiers.first;
+ ModifierData *mod_anchor = static_cast<ModifierData *>(
+ BLI_listbase_string_or_index_find(&object->modifiers,
+ opop->subitem_local_name,
+ offsetof(ModifierData, name),
+ opop->subitem_local_index));
+ ModifierData *mod_src = mod_anchor != nullptr ?
+ mod_anchor->next :
+ static_cast<ModifierData *>(object->modifiers.first);
- if (mod_src == NULL) {
+ if (mod_src == nullptr) {
/* Invalid case, just remove that override property operation. */
CLOG_ERROR(&LOG, "Could not find source modifier in stored override data");
BKE_lib_override_library_property_operation_delete(op, opop);
@@ -1349,21 +1425,22 @@ static void version_liboverride_rnacollections_insertion_object(Object *object)
}
op = BKE_lib_override_library_property_find(liboverride, "grease_pencil_modifiers");
- if (op != NULL) {
+ if (op != nullptr) {
LISTBASE_FOREACH_MUTABLE (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) {
continue;
}
- GpencilModifierData *gp_mod_anchor = BLI_listbase_string_or_index_find(
- &object->greasepencil_modifiers,
- opop->subitem_local_name,
- offsetof(GpencilModifierData, name),
- opop->subitem_local_index);
- GpencilModifierData *gp_mod_src = gp_mod_anchor != NULL ?
+ GpencilModifierData *gp_mod_anchor = static_cast<GpencilModifierData *>(
+ BLI_listbase_string_or_index_find(&object->greasepencil_modifiers,
+ opop->subitem_local_name,
+ offsetof(GpencilModifierData, name),
+ opop->subitem_local_index));
+ GpencilModifierData *gp_mod_src = gp_mod_anchor != nullptr ?
gp_mod_anchor->next :
- object->greasepencil_modifiers.first;
+ static_cast<GpencilModifierData *>(
+ object->greasepencil_modifiers.first);
- if (gp_mod_src == NULL) {
+ if (gp_mod_src == nullptr) {
/* Invalid case, just remove that override property operation. */
CLOG_ERROR(&LOG, "Could not find source GP modifier in stored override data");
BKE_lib_override_library_property_operation_delete(op, opop);
@@ -1378,18 +1455,18 @@ static void version_liboverride_rnacollections_insertion_object(Object *object)
}
op = BKE_lib_override_library_property_find(liboverride, "constraints");
- if (op != NULL) {
+ if (op != nullptr) {
version_liboverride_rnacollections_insertion_object_constraints(&object->constraints, op);
}
- if (object->pose != NULL) {
+ if (object->pose != nullptr) {
LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) {
char rna_path[26 + (sizeof(pchan->name) * 2) + 1];
char name_esc[sizeof(pchan->name) * 2];
BLI_str_escape(name_esc, pchan->name, sizeof(name_esc));
SNPRINTF(rna_path, "pose.bones[\"%s\"].constraints", name_esc);
op = BKE_lib_override_library_property_find(liboverride, rna_path);
- if (op != NULL) {
+ if (op != nullptr) {
version_liboverride_rnacollections_insertion_object_constraints(&pchan->constraints, op);
}
}
@@ -1399,7 +1476,7 @@ static void version_liboverride_rnacollections_insertion_object(Object *object)
static void version_liboverride_rnacollections_insertion_animdata(ID *id)
{
AnimData *anim_data = BKE_animdata_from_id(id);
- if (anim_data == NULL) {
+ if (anim_data == nullptr) {
return;
}
@@ -1407,7 +1484,7 @@ static void version_liboverride_rnacollections_insertion_animdata(ID *id)
IDOverrideLibraryProperty *op;
op = BKE_lib_override_library_property_find(liboverride, "animation_data.nla_tracks");
- if (op != NULL) {
+ if (op != nullptr) {
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if (opop->operation != IDOVERRIDE_LIBRARY_OP_INSERT_AFTER) {
continue;
@@ -1417,7 +1494,7 @@ static void version_liboverride_rnacollections_insertion_animdata(ID *id)
*
* This makes things simple here. */
opop->subitem_reference_name = opop->subitem_local_name;
- opop->subitem_local_name = NULL;
+ opop->subitem_local_name = nullptr;
opop->subitem_reference_index = opop->subitem_local_index;
opop->subitem_local_index++;
}
@@ -1668,6 +1745,27 @@ static void versioning_replace_legacy_combined_and_separate_color_nodes(bNodeTre
}
}
+static void versioning_replace_legacy_mix_rgb_node(bNodeTree *ntree)
+{
+ version_node_input_socket_name(ntree, SH_NODE_MIX_RGB_LEGACY, "Fac", "Factor_Float");
+ version_node_input_socket_name(ntree, SH_NODE_MIX_RGB_LEGACY, "Color1", "A_Color");
+ version_node_input_socket_name(ntree, SH_NODE_MIX_RGB_LEGACY, "Color2", "B_Color");
+ version_node_output_socket_name(ntree, SH_NODE_MIX_RGB_LEGACY, "Color", "Result_Color");
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type == SH_NODE_MIX_RGB_LEGACY) {
+ strcpy(node->idname, "ShaderNodeMix");
+ node->type = SH_NODE_MIX;
+ NodeShaderMix *data = (NodeShaderMix *)MEM_callocN(sizeof(NodeShaderMix), __func__);
+ data->blend_type = node->custom1;
+ data->clamp_result = node->custom2;
+ data->clamp_factor = 1;
+ data->data_type = SOCK_RGBA;
+ data->factor_mode = NODE_MIX_MODE_UNIFORM;
+ node->storage = data;
+ }
+ }
+}
+
static void version_fix_image_format_copy(Main *bmain, ImageFormatData *format)
{
/* Fix bug where curves in image format were not properly copied to file output
@@ -1685,9 +1783,9 @@ static void version_fix_image_format_copy(Main *bmain, ImageFormatData *format)
}
/* Remove any invalid curves with missing data. */
- if (format->view_settings.curve_mapping->cm[0].curve == NULL) {
+ if (format->view_settings.curve_mapping->cm[0].curve == nullptr) {
BKE_curvemapping_free(format->view_settings.curve_mapping);
- format->view_settings.curve_mapping = NULL;
+ format->view_settings.curve_mapping = nullptr;
format->view_settings.flag &= ~COLORMANAGE_VIEW_USE_CURVES;
}
}
@@ -1728,7 +1826,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "float", "custom_scale_xyz[3]")) {
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
- if (ob->pose == NULL) {
+ if (ob->pose == nullptr) {
continue;
}
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
@@ -1748,7 +1846,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
&sl->regionbase;
ARegion *new_sidebar = do_versions_add_region_if_not_found(
regionbase, RGN_TYPE_UI, "sidebar for spreadsheet", RGN_TYPE_FOOTER);
- if (new_sidebar != NULL) {
+ if (new_sidebar != nullptr) {
new_sidebar->alignment = RGN_ALIGN_RIGHT;
new_sidebar->flag |= RGN_FLAG_HIDDEN;
}
@@ -1814,7 +1912,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- BKE_animdata_main_cb(bmain, do_version_bbone_len_scale_animdata_cb, NULL);
+ BKE_animdata_main_cb(bmain, do_version_bbone_len_scale_animdata_cb, nullptr);
}
}
@@ -1891,7 +1989,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 300, 8)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->master_collection != NULL) {
+ if (scene->master_collection != nullptr) {
BLI_strncpy(scene->master_collection->id.name + 2,
BKE_SCENE_COLLECTION_NAME,
sizeof(scene->master_collection->id.name) - 2);
@@ -2017,7 +2115,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Font names were copied directly into ID names, see: T90417. */
if (!MAIN_VERSION_ATLEAST(bmain, 300, 16)) {
ListBase *lb = which_libbase(bmain, ID_VF);
- BKE_main_id_repair_duplicate_names_listbase(lb);
+ BKE_main_id_repair_duplicate_names_listbase(bmain, lb);
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 17)) {
@@ -2088,7 +2186,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 300, 19)) {
/* Disable Fade Inactive Overlay by default as it is redundant after introducing flash on
* mode transfer. */
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
@@ -2159,7 +2257,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 23)) {
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_FILE) {
@@ -2203,8 +2301,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene);
sequencer_tool_settings->pivot_point = V3D_AROUND_CENTER_MEDIAN;
- if (scene->ed != NULL) {
- SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_origin_set, NULL);
+ if (scene->ed != nullptr) {
+ SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_origin_set, nullptr);
}
}
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
@@ -2303,11 +2401,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
break;
}
- case SPACE_IMAGE: {
- SpaceImage *sima = (SpaceImage *)sl;
- sima->custom_grid_subdiv = 10;
- break;
- }
}
}
}
@@ -2316,12 +2409,12 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 300, 31)) {
/* Swap header with the tool header so the regular header is always on the edge. */
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase :
&sl->regionbase;
- ARegion *region_tool = NULL, *region_head = NULL;
+ ARegion *region_tool = nullptr, *region_head = nullptr;
int region_tool_index = -1, region_head_index = -1, i;
LISTBASE_FOREACH_INDEX (ARegion *, region, regionbase, i) {
if (region->regiontype == RGN_TYPE_TOOL_HEADER) {
@@ -2342,8 +2435,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Set strip color tags to SEQUENCE_COLOR_NONE. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->ed != NULL) {
- SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_tags, NULL);
+ if (scene->ed != nullptr) {
+ SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_tags, nullptr);
}
}
@@ -2361,14 +2454,15 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Set defaults for new color balance modifier parameters. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->ed != NULL) {
- SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_balance_sop, NULL);
+ if (scene->ed != nullptr) {
+ SEQ_for_each_callback(
+ &scene->ed->seqbase, do_versions_sequencer_color_balance_sop, nullptr);
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 33)) {
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
switch (sl->spacetype) {
@@ -2478,7 +2572,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_VIEWER) {
- if (node->storage == NULL) {
+ if (node->storage == nullptr) {
NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(
sizeof(NodeGeometryViewer), __func__);
data->data_type = CD_PROP_FLOAT;
@@ -2575,7 +2669,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
* It was possible to save .blend file with incorrect state of meta strip
* range. The root cause is expected to be fixed, but need to ensure files
* with invalid meta strip range are corrected. */
- if (ed != NULL) {
+ if (ed != nullptr) {
SEQ_for_each_callback(&ed->seqbase, version_fix_seq_meta_range, scene);
}
}
@@ -2613,7 +2707,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Convert float compare into a more general compare node. */
if (node->type == FN_NODE_COMPARE) {
- if (node->storage == NULL) {
+ if (node->storage == nullptr) {
NodeFunctionCompare *data = (NodeFunctionCompare *)MEM_callocN(
sizeof(NodeFunctionCompare), __func__);
data->data_type = SOCK_FLOAT;
@@ -2657,8 +2751,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == SH_NODE_MAP_RANGE) {
- if (node->storage == NULL) {
- NodeMapRange *data = MEM_callocN(sizeof(NodeMapRange), __func__);
+ if (node->storage == nullptr) {
+ NodeMapRange *data = MEM_cnew<NodeMapRange>(__func__);
data->clamp = node->custom1;
data->data_type = CD_PROP_FLOAT;
data->interpolation_type = node->custom2;
@@ -2688,7 +2782,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Initialize the bone wireframe opacity setting. */
if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "bone_wire_alpha")) {
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_VIEW3D) {
@@ -2731,8 +2825,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 302, 2)) {
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- if (scene->ed != NULL) {
- SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_filter_set, NULL);
+ if (scene->ed != nullptr) {
+ SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_filter_set, nullptr);
}
}
}
@@ -2824,7 +2918,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) {
for (int step = 0; step < 2; step++) {
- CustomDataLayer *actlayer = NULL;
+ CustomDataLayer *actlayer = nullptr;
int vact1, vact2;
@@ -2883,14 +2977,14 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (brush->ob_mode != OB_MODE_SCULPT_CURVES) {
continue;
}
- if (brush->curves_sculpt_settings != NULL) {
+ if (brush->curves_sculpt_settings != nullptr) {
continue;
}
- brush->curves_sculpt_settings = MEM_callocN(sizeof(BrushCurvesSculptSettings), __func__);
+ brush->curves_sculpt_settings = MEM_cnew<BrushCurvesSculptSettings>(__func__);
brush->curves_sculpt_settings->add_amount = 1;
}
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_OUTLINER) {
@@ -2904,7 +2998,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 302, 9)) {
/* Sequencer channels region. */
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype != SPACE_SEQ) {
@@ -2929,7 +3023,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
ARegion *timeline_region = BKE_region_find_in_listbase_by_type(regionbase,
RGN_TYPE_WINDOW);
- if (timeline_region != NULL) {
+ if (timeline_region != nullptr) {
timeline_region->v2d.flag |= V2D_VIEWSYNC_AREA_VERTICAL;
}
}
@@ -2939,11 +3033,11 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Initialize channels. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Editing *ed = SEQ_editing_get(scene);
- if (ed == NULL) {
+ if (ed == nullptr) {
continue;
}
SEQ_channels_ensure(&ed->channels);
- SEQ_for_each_callback(&scene->ed->seqbase, seq_meta_channels_ensure, NULL);
+ SEQ_for_each_callback(&scene->ed->seqbase, seq_meta_channels_ensure, nullptr);
ed->displayed_channels = &ed->channels;
@@ -2958,7 +3052,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
if (!MAIN_VERSION_ATLEAST(bmain, 302, 10)) {
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype != SPACE_FILE) {
@@ -2989,7 +3083,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Rebuild active/render color attribute references. */
LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) {
for (int step = 0; step < 2; step++) {
- CustomDataLayer *actlayer = NULL;
+ CustomDataLayer *actlayer = nullptr;
int vact1, vact2;
@@ -3058,9 +3152,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (ntree->type == NTREE_GEOMETRY) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_MERGE_BY_DISTANCE) {
- if (node->storage == NULL) {
- NodeGeometryMergeByDistance *data = MEM_callocN(sizeof(NodeGeometryMergeByDistance),
- __func__);
+ if (node->storage == nullptr) {
+ NodeGeometryMergeByDistance *data = MEM_cnew<NodeGeometryMergeByDistance>(__func__);
data->mode = GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL;
node->storage = data;
}
@@ -3093,7 +3186,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings;
- if (settings == NULL) {
+ if (settings == nullptr) {
continue;
}
if (settings->curve_length == 0.0f) {
@@ -3102,6 +3195,13 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
+ if (!MAIN_VERSION_ATLEAST(bmain, 302, 14)) {
+ /* Compensate for previously wrong squared distance. */
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ scene->r.bake.max_ray_distance = sasqrt(scene->r.bake.max_ray_distance);
+ }
+ }
+
if (!MAIN_VERSION_ATLEAST(bmain, 303, 1)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
versioning_replace_legacy_combined_and_separate_color_nodes(ntree);
@@ -3120,7 +3220,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* UDIM Packing. */
if (!DNA_struct_elem_find(fd->filesdna, "ImagePackedFile", "int", "tile_number")) {
- for (Image *ima = bmain->images.first; ima; ima = ima->id.next) {
+ LISTBASE_FOREACH (Image *, ima, &bmain->images) {
int view;
LISTBASE_FOREACH_INDEX (ImagePackedFile *, imapf, &ima->packedfiles, view) {
imapf->view = view;
@@ -3132,8 +3232,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
/* Merge still offsets into start/end offsets. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Editing *ed = SEQ_editing_get(scene);
- if (ed != NULL) {
- SEQ_for_each_callback(&ed->seqbase, version_merge_still_offsets, NULL);
+ if (ed != nullptr) {
+ SEQ_for_each_callback(&ed->seqbase, version_merge_still_offsets, nullptr);
}
}
@@ -3239,7 +3339,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
if (!MAIN_VERSION_ATLEAST(bmain, 303, 5)) {
/* Fix for T98925 - remove channels region, that was initialized in incorrect editor types. */
- for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (ELEM(sl->spacetype, SPACE_ACTION, SPACE_CLIP, SPACE_GRAPH, SPACE_NLA, SPACE_SEQ)) {
@@ -3257,6 +3357,27 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ if (!MAIN_VERSION_ATLEAST(bmain, 303, 6)) {
+ /* Initialize brush curves sculpt settings. */
+ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
+ if (brush->ob_mode != OB_MODE_SCULPT_CURVES) {
+ continue;
+ }
+ brush->curves_sculpt_settings->density_add_attempts = 100;
+ }
+
+ /* Disable 'show_bounds' option of curve objects. Option was set as there was no object mode
+ * outline implementation. See T95933. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ if (ob->type == OB_CURVES) {
+ ob->dtx &= ~OB_DRAWBOUNDOX;
+ }
+ }
+
+ BKE_main_namemap_validate_and_fix(bmain);
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -3269,19 +3390,51 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
{
/* Keep this block, even when empty. */
- /* Initialize brush curves sculpt settings. */
- LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
- if (brush->ob_mode != OB_MODE_SCULPT_CURVES) {
- continue;
+ /* Image generation information transferred to tiles. */
+ if (!DNA_struct_elem_find(fd->filesdna, "ImageTile", "int", "gen_x")) {
+ LISTBASE_FOREACH (Image *, ima, &bmain->images) {
+ LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) {
+ tile->gen_x = ima->gen_x;
+ tile->gen_y = ima->gen_y;
+ tile->gen_type = ima->gen_type;
+ tile->gen_flag = ima->gen_flag;
+ tile->gen_depth = ima->gen_depth;
+ copy_v4_v4(tile->gen_color, ima->gen_color);
+ }
}
- brush->curves_sculpt_settings->density_add_attempts = 100;
}
- /* Disable 'show_bounds' option of curve objects. Option was set as there was no object mode
- * outline implementation. See T95933. */
- LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
- if (ob->type == OB_CURVES) {
- ob->dtx &= ~OB_DRAWBOUNDOX;
+ /* Convert mix rgb node to new mix node and add storage. */
+ {
+ FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
+ versioning_replace_legacy_mix_rgb_node(ntree);
+ }
+ FOREACH_NODETREE_END;
+ }
+
+ /* Face sets no longer store whether the corresponding face is hidden. */
+ LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
+ int *face_sets = (int *)CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ if (face_sets) {
+ for (int i = 0; i < mesh->totpoly; i++) {
+ face_sets[i] = abs(face_sets[i]);
+ }
+ }
+ }
+
+ /* Custom grids in UV Editor have separate X and Y divisions. */
+ LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ switch (sl->spacetype) {
+ case SPACE_IMAGE: {
+ SpaceImage *sima = (SpaceImage *)sl;
+ sima->custom_grid_subdiv[0] = 10;
+ sima->custom_grid_subdiv[1] = 10;
+ break;
+ }
+ }
+ }
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_400.cc b/source/blender/blenloader/intern/versioning_400.cc
new file mode 100644
index 00000000000..a5e1791d24c
--- /dev/null
+++ b/source/blender/blenloader/intern/versioning_400.cc
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup blenloader
+ */
+
+#define DNA_DEPRECATED_ALLOW
+
+#include "CLG_log.h"
+
+#include "BKE_main.h"
+#include "BKE_mesh_legacy_convert.h"
+
+#include "BLO_readfile.h"
+
+#include "readfile.h"
+
+#include "versioning_common.h"
+
+// static CLG_LogRef LOG = {"blo.readfile.doversion"};
+
+static void version_mesh_legacy_to_struct_of_array_format(Mesh &mesh)
+{
+ BKE_mesh_legacy_convert_flags_to_hide_layers(&mesh);
+ BKE_mesh_legacy_convert_mpoly_to_material_indices(&mesh);
+ BKE_mesh_legacy_bevel_weight_to_layers(&mesh);
+}
+
+void blo_do_versions_400(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
+{
+ // if (!MAIN_VERSION_ATLEAST(bmain, 400, 0)) {
+ /* This is done here because we will continue to write with the old format until 4.0, so we need
+ * to convert even "current" files. Keep the check commented out for now so the versioning isn't
+ * turned off right after the 4.0 bump. */
+ LISTBASE_FOREACH (Mesh *, mesh, &bmain->meshes) {
+ version_mesh_legacy_to_struct_of_array_format(*mesh);
+ }
+ // }
+
+ /**
+ * Versioning code until next subversion bump goes here.
+ *
+ * \note Be sure to check when bumping the version:
+ * - "versioning_userdef.c", #blo_do_versions_userdef
+ * - "versioning_userdef.c", #do_versions_theme
+ *
+ * \note Keep this message at the bottom of the function.
+ */
+ {
+ /* Keep this block, even when empty. */
+ }
+}
diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc
index d2a55f6f37e..823385727e1 100644
--- a/source/blender/blenloader/intern/versioning_common.cc
+++ b/source/blender/blenloader/intern/versioning_common.cc
@@ -18,6 +18,7 @@
#include "BKE_animsys.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_node.h"
#include "MEM_guardedalloc.h"
@@ -66,6 +67,7 @@ ID *do_versions_rename_id(Main *bmain,
}
}
if (id != nullptr) {
+ BKE_main_namemap_remove_name(bmain, id, id->name + 2);
BLI_strncpy(id->name + 2, name_dst, sizeof(id->name) - 2);
/* We know it's unique, this just sorts. */
BLI_libblock_ensure_unique_name(bmain, id->name);
diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c
index e1ceed82ba7..51063f47ef9 100644
--- a/source/blender/blenloader/intern/versioning_cycles.c
+++ b/source/blender/blenloader/intern/versioning_cycles.c
@@ -689,7 +689,6 @@ static void update_vector_math_node_normalize_operator(bNodeTree *ntree)
* a value of -1 just to be identified later in the versioning code:
*
* Average Operator : 2 -> -1
- *
*/
static void update_vector_math_node_operators_enum_mapping(bNodeTree *ntree)
{
@@ -867,7 +866,6 @@ static void update_mapping_node_fcurve_rna_path_callback(ID *UNUSED(id),
* and check if they control a property of the node, if they do, we update
* the path to be that of the corresponding socket in the node or the added
* minimum/maximum node.
- *
*/
static void update_mapping_node_inputs_and_properties(bNodeTree *ntree)
{
@@ -1057,7 +1055,6 @@ static void update_voronoi_node_fac_output(bNodeTree *ntree)
* the inputs of the subtract node.
* 6. The output of the subtract node is connected to the
* appropriate sockets.
- *
*/
static void update_voronoi_node_crackle(bNodeTree *ntree)
{
diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c
index 6ce53e4a648..06903865381 100644
--- a/source/blender/blenloader/intern/versioning_defaults.c
+++ b/source/blender/blenloader/intern/versioning_defaults.c
@@ -45,6 +45,7 @@
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_node.h"
@@ -55,6 +56,8 @@
#include "BLO_readfile.h"
+#include "BLT_translation.h"
+
#include "versioning_common.h"
/* Make preferences read-only, use versioning_userdef.c. */
@@ -63,8 +66,9 @@
static bool blo_is_builtin_template(const char *app_template)
{
/* For all builtin templates shipped with Blender. */
- return (!app_template ||
- STR_ELEM(app_template, "2D_Animation", "Sculpting", "VFX", "Video_Editing"));
+ return (
+ !app_template ||
+ STR_ELEM(app_template, N_("2D_Animation"), N_("Sculpting"), N_("VFX"), N_("Video_Editing")));
}
static void blo_update_defaults_screen(bScreen *screen,
@@ -134,6 +138,14 @@ static void blo_update_defaults_screen(bScreen *screen,
}
}
}
+ else {
+ /* Open properties panel by default. */
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ if (region->regiontype == RGN_TYPE_UI) {
+ region->flag &= ~RGN_FLAG_HIDDEN;
+ }
+ }
+ }
}
else if (area->spacetype == SPACE_GRAPH) {
SpaceGraph *sipo = area->spacedata.first;
@@ -333,7 +345,8 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene)
/* Correct default startup UV's. */
Mesh *me = BLI_findstring(&bmain->meshes, "Cube", offsetof(ID, name) + 2);
- if (me && (me->totloop == 24) && (me->mloopuv != NULL)) {
+ if (me && (me->totloop == 24) && CustomData_has_layer(&me->ldata, CD_MLOOPUV)) {
+ MLoopUV *mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
const float uv_values[24][2] = {
{0.625, 0.50}, {0.875, 0.50}, {0.875, 0.75}, {0.625, 0.75}, {0.375, 0.75}, {0.625, 0.75},
{0.625, 1.00}, {0.375, 1.00}, {0.375, 0.00}, {0.625, 0.00}, {0.625, 0.25}, {0.375, 0.25},
@@ -341,7 +354,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene)
{0.625, 0.75}, {0.375, 0.75}, {0.375, 0.25}, {0.625, 0.25}, {0.625, 0.50}, {0.375, 0.50},
};
for (int i = 0; i < ARRAY_SIZE(uv_values); i++) {
- copy_v2_v2(me->mloopuv[i].uv, uv_values[i]);
+ copy_v2_v2(mloopuv[i].uv, uv_values[i]);
}
}
@@ -479,8 +492,11 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
/* Default only has one window. */
if (layout->screen) {
bScreen *screen = layout->screen;
- BLI_strncpy(screen->id.name + 2, workspace->id.name + 2, sizeof(screen->id.name) - 2);
- BLI_libblock_ensure_unique_name(bmain, screen->id.name);
+ if (!STREQ(screen->id.name + 2, workspace->id.name + 2)) {
+ BKE_main_namemap_remove_name(bmain, &screen->id, screen->id.name + 2);
+ BLI_strncpy(screen->id.name + 2, workspace->id.name + 2, sizeof(screen->id.name) - 2);
+ BLI_libblock_ensure_unique_name(bmain, screen->id.name);
+ }
}
/* For some reason we have unused screens, needed until re-saving.
@@ -566,14 +582,14 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
/* Materials */
for (Material *ma = bmain->materials.first; ma; ma = ma->id.next) {
/* Update default material to be a bit more rough. */
- ma->roughness = 0.4f;
+ ma->roughness = 0.5f;
if (ma->nodetree) {
LISTBASE_FOREACH (bNode *, node, &ma->nodetree->nodes) {
if (node->type == SH_NODE_BSDF_PRINCIPLED) {
bNodeSocket *roughness_socket = nodeFindSocket(node, SOCK_IN, "Roughness");
bNodeSocketValueFloat *roughness_data = roughness_socket->default_value;
- roughness_data->value = 0.4f;
+ roughness_data->value = 0.5f;
node->custom2 = SHD_SUBSURFACE_RANDOM_WALK;
BKE_ntree_update_tag_node_property(ma->nodetree, node);
}
diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c
index a3f17878f68..94438acf4fa 100644
--- a/source/blender/blenloader/intern/versioning_legacy.c
+++ b/source/blender/blenloader/intern/versioning_legacy.c
@@ -291,8 +291,8 @@ static void customdata_version_242(Mesh *me)
MEM_freeN(me->mcol);
}
- me->mcol = CustomData_add_layer(&me->fdata, CD_MCOL, CD_CALLOC, NULL, me->totface);
- me->mtface = CustomData_add_layer(&me->fdata, CD_MTFACE, CD_CALLOC, NULL, me->totface);
+ me->mcol = CustomData_add_layer(&me->fdata, CD_MCOL, CD_SET_DEFAULT, NULL, me->totface);
+ me->mtface = CustomData_add_layer(&me->fdata, CD_MTFACE, CD_SET_DEFAULT, NULL, me->totface);
mtf = me->mtface;
mcol = me->mcol;
@@ -342,8 +342,6 @@ static void customdata_version_242(Mesh *me)
mcoln++;
}
}
-
- BKE_mesh_update_customdata_pointers(me, true);
}
/* Only copy render texface layer from active. */
@@ -1468,7 +1466,7 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain)
for (me = bmain->meshes.first; me; me = me->id.next) {
if (!me->medge) {
- BKE_mesh_calc_edges_legacy(me, true); /* true = use mface->edcode */
+ BKE_mesh_calc_edges_legacy(me, true); /* true = use #MFace.edcode. */
}
else {
BKE_mesh_strip_loose_faces(me);
diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c
index 4f62be26196..f8fb3b47570 100644
--- a/source/blender/blenloader/intern/versioning_userdef.c
+++ b/source/blender/blenloader/intern/versioning_userdef.c
@@ -83,6 +83,10 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
FROM_DEFAULT_V4_UCHAR(space_sequencer.list_text_hi);
}
+ if (!USER_VERSION_ATLEAST(303, 6)) {
+ btheme->tui.wcol_view_item = U_theme_default.tui.wcol_view_item;
+ }
+
/**
* Versioning code until next subversion bump goes here.
*
@@ -94,7 +98,6 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme)
*/
{
/* Keep this block, even when empty. */
- btheme->tui.wcol_view_item = U_theme_default.tui.wcol_view_item;
}
#undef FROM_DEFAULT_V4_UCHAR
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.cc
index 68171f26a66..6b7c4489eee 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.cc
@@ -6,7 +6,6 @@
*/
/**
- *
* FILE FORMAT
* ===========
*
@@ -208,17 +207,17 @@ static size_t ww_write_none(WriteWrap *ww, const char *buf, size_t buf_len)
/* zstd */
-typedef struct {
- struct ZstdWriteBlockTask *next, *prev;
+struct ZstdWriteBlockTask {
+ ZstdWriteBlockTask *next, *prev;
void *data;
size_t size;
int frame_number;
WriteWrap *ww;
-} ZstdWriteBlockTask;
+};
static void *zstd_write_task(void *userdata)
{
- ZstdWriteBlockTask *task = userdata;
+ ZstdWriteBlockTask *task = static_cast<ZstdWriteBlockTask *>(userdata);
WriteWrap *ww = task->ww;
size_t out_buf_len = ZSTD_compressBound(task->size);
@@ -238,8 +237,9 @@ static void *zstd_write_task(void *userdata)
ww->zstd.write_error = true;
}
else {
- if (ww_write_none(ww, out_buf, out_size) == out_size) {
- ZstdFrame *frameinfo = MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo");
+ if (ww_write_none(ww, static_cast<const char *>(out_buf), out_size) == out_size) {
+ ZstdFrame *frameinfo = static_cast<ZstdFrame *>(
+ MEM_mallocN(sizeof(ZstdFrame), "zstd frameinfo"));
frameinfo->uncompressed_size = task->size;
frameinfo->compressed_size = out_size;
BLI_addtail(&ww->zstd.frames, frameinfo);
@@ -255,7 +255,7 @@ static void *zstd_write_task(void *userdata)
BLI_condition_notify_all(&ww->zstd.condition);
MEM_freeN(out_buf);
- return NULL;
+ return nullptr;
}
static bool ww_open_zstd(WriteWrap *ww, const char *filepath)
@@ -334,7 +334,8 @@ static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len)
return 0;
}
- ZstdWriteBlockTask *task = MEM_mallocN(sizeof(ZstdWriteBlockTask), __func__);
+ ZstdWriteBlockTask *task = static_cast<ZstdWriteBlockTask *>(
+ MEM_mallocN(sizeof(ZstdWriteBlockTask), __func__));
task->data = MEM_mallocN(buf_len, __func__);
memcpy(task->data, buf, buf_len);
task->size = buf_len;
@@ -348,7 +349,7 @@ static size_t ww_write_zstd(WriteWrap *ww, const char *buf, size_t buf_len)
* Otherwise, we wait for the earliest thread to finish.
* We look up the earliest thread while holding the mutex, but release it
* before joining the thread to prevent a deadlock. */
- ZstdWriteBlockTask *first_task = ww->zstd.tasks.first;
+ ZstdWriteBlockTask *first_task = static_cast<ZstdWriteBlockTask *>(ww->zstd.tasks.first);
BLI_mutex_unlock(&ww->zstd.mutex);
if (!BLI_available_threads(&ww->zstd.threadpool)) {
BLI_threadpool_remove(&ww->zstd.threadpool, first_task);
@@ -425,7 +426,7 @@ typedef struct {
/**
* Wrap writing, so we can use zstd or
* other compression types later, see: G_FILE_COMPRESS
- * Will be NULL for UNDO.
+ * Will be nullptr for UNDO.
*/
WriteWrap *ww;
} WriteData;
@@ -436,14 +437,14 @@ typedef struct BlendWriter {
static WriteData *writedata_new(WriteWrap *ww)
{
- WriteData *wd = MEM_callocN(sizeof(*wd), "writedata");
+ WriteData *wd = static_cast<WriteData *>(MEM_callocN(sizeof(*wd), "writedata"));
wd->sdna = DNA_sdna_current_get();
wd->ww = ww;
- if ((ww == NULL) || (ww->use_buf)) {
- if (ww == NULL) {
+ if ((ww == nullptr) || (ww->use_buf)) {
+ if (ww == nullptr) {
wd->buffer.max_size = MEM_BUFFER_SIZE;
wd->buffer.chunk_size = MEM_CHUNK_SIZE;
}
@@ -451,7 +452,7 @@ static WriteData *writedata_new(WriteWrap *ww)
wd->buffer.max_size = ZSTD_BUFFER_SIZE;
wd->buffer.chunk_size = ZSTD_CHUNK_SIZE;
}
- wd->buffer.buf = MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf");
+ wd->buffer.buf = static_cast<uchar *>(MEM_mallocN(wd->buffer.max_size, "wd->buffer.buf"));
}
return wd;
@@ -459,7 +460,7 @@ static WriteData *writedata_new(WriteWrap *ww)
static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
{
- if ((wd == NULL) || wd->error || (mem == NULL) || memlen < 1) {
+ if ((wd == nullptr) || wd->error || (mem == nullptr) || memlen < 1) {
return;
}
@@ -474,10 +475,10 @@ static void writedata_do_write(WriteData *wd, const void *mem, size_t memlen)
/* memory based save */
if (wd->use_memfile) {
- BLO_memfile_chunk_add(&wd->mem, mem, memlen);
+ BLO_memfile_chunk_add(&wd->mem, static_cast<const char *>(mem), memlen);
}
else {
- if (wd->ww->write(wd->ww, mem, memlen) != memlen) {
+ if (wd->ww->write(wd->ww, static_cast<const char *>(mem), memlen) != memlen) {
wd->error = true;
}
}
@@ -520,7 +521,7 @@ static void mywrite(WriteData *wd, const void *adr, size_t len)
return;
}
- if (UNLIKELY(adr == NULL)) {
+ if (UNLIKELY(adr == nullptr)) {
BLI_assert(0);
return;
}
@@ -529,7 +530,7 @@ static void mywrite(WriteData *wd, const void *adr, size_t len)
wd->write_len += len;
#endif
- if (wd->buffer.buf == NULL) {
+ if (wd->buffer.buf == nullptr) {
writedata_do_write(wd, adr, len);
}
else {
@@ -566,15 +567,15 @@ static void mywrite(WriteData *wd, const void *adr, size_t len)
/**
* BeGiN initializer for mywrite
* \param ww: File write wrapper.
- * \param compare: Previous memory file (can be NULL).
- * \param current: The current memory file (can be NULL).
+ * \param compare: Previous memory file (can be nullptr).
+ * \param current: The current memory file (can be nullptr).
* \warning Talks to other functions with global parameters
*/
static WriteData *mywrite_begin(WriteWrap *ww, MemFile *compare, MemFile *current)
{
WriteData *wd = writedata_new(ww);
- if (current != NULL) {
+ if (current != nullptr) {
BLO_memfile_write_init(&wd->mem, current, compare);
wd->use_memfile = true;
}
@@ -618,15 +619,17 @@ static void mywrite_id_begin(WriteData *wd, ID *id)
/* If current next memchunk does not match the ID we are about to write, or is not the _first_
* one for said ID, try to find the correct memchunk in the mapping using ID's session_uuid. */
MemFileChunk *curr_memchunk = wd->mem.reference_current_chunk;
- MemFileChunk *prev_memchunk = curr_memchunk != NULL ? curr_memchunk->prev : NULL;
- if (wd->mem.id_session_uuid_mapping != NULL &&
- (curr_memchunk == NULL || curr_memchunk->id_session_uuid != id->session_uuid ||
- (prev_memchunk != NULL &&
+ MemFileChunk *prev_memchunk = curr_memchunk != nullptr ?
+ static_cast<MemFileChunk *>(curr_memchunk->prev) :
+ nullptr;
+ if (wd->mem.id_session_uuid_mapping != nullptr &&
+ (curr_memchunk == nullptr || curr_memchunk->id_session_uuid != id->session_uuid ||
+ (prev_memchunk != nullptr &&
(prev_memchunk->id_session_uuid == curr_memchunk->id_session_uuid)))) {
void *ref = BLI_ghash_lookup(wd->mem.id_session_uuid_mapping,
POINTER_FROM_UINT(id->session_uuid));
- if (ref != NULL) {
- wd->mem.reference_current_chunk = ref;
+ if (ref != nullptr) {
+ wd->mem.reference_current_chunk = static_cast<MemFileChunk *>(ref);
}
/* Else, no existing memchunk found, i.e. this is supposed to be a new ID. */
}
@@ -663,7 +666,7 @@ static void writestruct_at_address_nr(
BLI_assert(struct_nr > 0 && struct_nr < SDNA_TYPE_MAX);
- if (adr == NULL || data == NULL || nr == 0) {
+ if (adr == nullptr || data == nullptr || nr == 0) {
return;
}
@@ -696,7 +699,7 @@ static void writedata(WriteData *wd, int filecode, size_t len, const void *adr)
{
BHead bh;
- if (adr == NULL || len == 0) {
+ if (adr == nullptr || len == 0) {
return;
}
@@ -722,7 +725,7 @@ static void writedata(WriteData *wd, int filecode, size_t len, const void *adr)
/* use this to force writing of lists in same order as reading (using link_list) */
static void writelist_nr(WriteData *wd, int filecode, const int struct_nr, const ListBase *lb)
{
- const Link *link = lb->first;
+ const Link *link = static_cast<Link *>(lb->first);
while (link) {
writestruct_nr(wd, filecode, struct_nr, 1, link);
@@ -778,35 +781,35 @@ static void current_screen_compat(Main *mainvar,
ViewLayer **r_view_layer)
{
wmWindowManager *wm;
- wmWindow *window = NULL;
+ wmWindow *window = nullptr;
/* find a global current screen in the first open window, to have
* a reasonable default for reading in older versions */
- wm = mainvar->wm.first;
+ wm = static_cast<wmWindowManager *>(mainvar->wm.first);
if (wm) {
if (use_active_win) {
/* write the active window into the file, needed for multi-window undo T43424 */
- for (window = wm->windows.first; window; window = window->next) {
+ for (window = static_cast<wmWindow *>(wm->windows.first); window; window = window->next) {
if (window->active) {
break;
}
}
/* fallback */
- if (window == NULL) {
- window = wm->windows.first;
+ if (window == nullptr) {
+ window = static_cast<wmWindow *>(wm->windows.first);
}
}
else {
- window = wm->windows.first;
+ window = static_cast<wmWindow *>(wm->windows.first);
}
}
- *r_screen = (window) ? BKE_workspace_active_screen_get(window->workspace_hook) : NULL;
- *r_scene = (window) ? window->scene : NULL;
+ *r_screen = (window) ? BKE_workspace_active_screen_get(window->workspace_hook) : nullptr;
+ *r_scene = (window) ? window->scene : nullptr;
*r_view_layer = (window && *r_scene) ? BKE_view_layer_find(*r_scene, window->view_layer_name) :
- NULL;
+ nullptr;
}
typedef struct RenderInfo {
@@ -824,7 +827,7 @@ typedef struct RenderInfo {
static void write_renderinfo(WriteData *wd, Main *mainvar)
{
bScreen *curscreen;
- Scene *curscene = NULL;
+ Scene *curscene = nullptr;
ViewLayer *view_layer;
/* XXX in future, handle multiple windows with multiple screens? */
@@ -952,7 +955,7 @@ static void write_libraries(WriteData *wd, Main *main)
else {
found_one = false;
while (!found_one && tot--) {
- for (id = lbarray[tot]->first; id; id = id->next) {
+ for (id = static_cast<ID *>(lbarray[tot]->first); id; id = static_cast<ID *>(id->next)) {
if (id->us > 0 &&
((id->tag & LIB_TAG_EXTERN) ||
((id->tag & LIB_TAG_INDIRECT) && (id->flag & LIB_INDIRECT_WEAK_LINK)))) {
@@ -970,10 +973,15 @@ static void write_libraries(WriteData *wd, Main *main)
if (found_one) {
/* Not overridable. */
+ void *runtime_name_data = main->curlib->runtime.name_map;
+ main->curlib->runtime.name_map = nullptr;
+
BlendWriter writer = {wd};
writestruct(wd, ID_LI, Library, 1, main->curlib);
BKE_id_blend_write(&writer, &main->curlib->id);
+ main->curlib->runtime.name_map = static_cast<UniqueName_Map *>(runtime_name_data);
+
if (main->curlib->packedfile) {
BKE_packedfile_blend_write(&writer, main->curlib->packedfile);
if (wd->use_memfile == false) {
@@ -983,7 +991,7 @@ static void write_libraries(WriteData *wd, Main *main)
/* Write link placeholders for all direct linked IDs. */
while (a--) {
- for (id = lbarray[a]->first; id; id = id->next) {
+ for (id = static_cast<ID *>(lbarray[a]->first); id; id = static_cast<ID *>(id->next)) {
if (id->us > 0 &&
((id->tag & LIB_TAG_EXTERN) ||
((id->tag & LIB_TAG_INDIRECT) && (id->flag & LIB_INDIRECT_WEAK_LINK)))) {
@@ -1004,6 +1012,11 @@ static void write_libraries(WriteData *wd, Main *main)
mywrite_flush(wd);
}
+#ifdef WITH_BUILDINFO
+extern "C" unsigned long build_commit_timestamp;
+extern "C" char build_hash[];
+#endif
+
/* context is usually defined by WM, two cases where no WM is available:
* - for forward compatibility, curscreen has to be saved
* - for undofile, curscene needs to be saved */
@@ -1020,7 +1033,7 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar)
memset(fg._pad, 0, sizeof(fg._pad));
memset(fg.filepath, 0, sizeof(fg.filepath));
memset(fg.build_hash, 0, sizeof(fg.build_hash));
- fg._pad1 = NULL;
+ fg._pad1 = nullptr;
current_screen_compat(mainvar, is_undo, &screen, &scene, &view_layer);
@@ -1044,13 +1057,9 @@ static void write_global(WriteData *wd, int fileflags, Main *mainvar)
fg.minversion = BLENDER_FILE_MIN_VERSION;
fg.minsubversion = BLENDER_FILE_MIN_SUBVERSION;
#ifdef WITH_BUILDINFO
- {
- extern unsigned long build_commit_timestamp;
- extern char build_hash[];
- /* TODO(sergey): Add branch name to file as well? */
- fg.build_commit_timestamp = build_commit_timestamp;
- BLI_strncpy(fg.build_hash, build_hash, sizeof(fg.build_hash));
- }
+ /* TODO(sergey): Add branch name to file as well? */
+ fg.build_commit_timestamp = build_commit_timestamp;
+ BLI_strncpy(fg.build_hash, build_hash, sizeof(fg.build_hash));
#else
fg.build_commit_timestamp = 0;
BLI_strncpy(fg.build_hash, "unknown", sizeof(fg.build_hash));
@@ -1112,7 +1121,7 @@ static bool write_file_handle(Main *mainvar,
mywrite_flush(wd);
OverrideLibraryStorage *override_storage = wd->use_memfile ?
- NULL :
+ nullptr :
BKE_lib_override_library_operations_store_init();
#define ID_BUFFER_STATIC_SIZE 8192
@@ -1124,9 +1133,9 @@ static bool write_file_handle(Main *mainvar,
ListBase *lbarray[INDEX_ID_MAX];
int a = set_listbasepointers(bmain, lbarray);
while (a--) {
- ID *id = lbarray[a]->first;
+ ID *id = static_cast<ID *>(lbarray[a]->first);
- if (id == NULL || GS(id->name) == ID_LI) {
+ if (id == nullptr || GS(id->name) == ID_LI) {
continue; /* Libraries are handled separately below. */
}
@@ -1144,7 +1153,7 @@ static bool write_file_handle(Main *mainvar,
id_buffer = MEM_mallocN(idtype_struct_size, __func__);
}
- for (; id; id = id->next) {
+ for (; id; id = static_cast<ID *>(id->next)) {
/* We should never attempt to write non-regular IDs
* (i.e. all kind of temp/runtime ones). */
BLI_assert(
@@ -1152,13 +1161,13 @@ static bool write_file_handle(Main *mainvar,
/* We only write unused IDs in undo case.
* NOTE: All Scenes, WindowManagers and WorkSpaces should always be written to disk, so
- * their usercount should never be NULL currently. */
+ * their user-count should never be nullptr currently. */
if (id->us == 0 && !wd->use_memfile) {
BLI_assert(!ELEM(GS(id->name), ID_SCE, ID_WM, ID_WS));
continue;
}
- const bool do_override = !ELEM(override_storage, NULL, bmain) &&
+ const bool do_override = !ELEM(override_storage, nullptr, bmain) &&
ID_IS_OVERRIDE_LIBRARY_REAL(id);
if (do_override) {
@@ -1167,19 +1176,19 @@ static bool write_file_handle(Main *mainvar,
if (wd->use_memfile) {
/* Record the changes that happened up to this undo push in
- * recalc_up_to_undo_push, and clear recalc_after_undo_push again
+ * recalc_up_to_undo_push, and clear `recalc_after_undo_push` again
* to start accumulating for the next undo push. */
id->recalc_up_to_undo_push = id->recalc_after_undo_push;
id->recalc_after_undo_push = 0;
bNodeTree *nodetree = ntreeFromID(id);
- if (nodetree != NULL) {
+ if (nodetree != nullptr) {
nodetree->id.recalc_up_to_undo_push = nodetree->id.recalc_after_undo_push;
nodetree->id.recalc_after_undo_push = 0;
}
if (GS(id->name) == ID_SCE) {
Scene *scene = (Scene *)id;
- if (scene->master_collection != NULL) {
+ if (scene->master_collection != nullptr) {
scene->master_collection->id.recalc_up_to_undo_push =
scene->master_collection->id.recalc_after_undo_push;
scene->master_collection->id.recalc_after_undo_push = 0;
@@ -1198,18 +1207,18 @@ static bool write_file_handle(Main *mainvar,
/* Those listbase data change every time we add/remove an ID, and also often when
* renaming one (due to re-sorting). This avoids generating a lot of false 'is changed'
* detections between undo steps. */
- ((ID *)id_buffer)->prev = NULL;
- ((ID *)id_buffer)->next = NULL;
+ ((ID *)id_buffer)->prev = nullptr;
+ ((ID *)id_buffer)->next = nullptr;
/* Those runtime pointers should never be set during writing stage, but just in case clear
* them too. */
- ((ID *)id_buffer)->orig_id = NULL;
- ((ID *)id_buffer)->newid = NULL;
+ ((ID *)id_buffer)->orig_id = nullptr;
+ ((ID *)id_buffer)->newid = nullptr;
/* Even though in theory we could be able to preserve this python instance across undo even
* when we need to re-read the ID into its original address, this is currently cleared in
* #direct_link_id_common in `readfile.c` anyway, */
- ((ID *)id_buffer)->py_instance = NULL;
+ ((ID *)id_buffer)->py_instance = nullptr;
- if (id_type->blend_write != NULL) {
+ if (id_type->blend_write != nullptr) {
id_type->blend_write(&writer, (ID *)id_buffer, id);
}
@@ -1230,7 +1239,7 @@ static bool write_file_handle(Main *mainvar,
if (override_storage) {
BKE_lib_override_library_operations_store_finalize(override_storage);
- override_storage = NULL;
+ override_storage = nullptr;
}
/* Special handling, operating over split Mains... */
@@ -1327,11 +1336,11 @@ bool BLO_write_file(Main *mainvar,
const bool relbase_valid = (mainvar->filepath[0] != '\0');
/* path backup/restore */
- void *path_list_backup = NULL;
+ void *path_list_backup = nullptr;
const eBPathForeachFlag path_list_flag = (BKE_BPATH_FOREACH_PATH_SKIP_LINKED |
BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE);
- if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
+ if (G.debug & G_DEBUG_IO && mainvar->lock != nullptr) {
BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *BEFORE* save to disk");
BLO_main_validate_libraries(mainvar, reports);
BLO_main_validate_shapekeys(mainvar, reports);
@@ -1371,13 +1380,13 @@ bool BLO_write_file(Main *mainvar,
/* Normalize the paths in case there is some subtle difference (so they can be compared). */
if (relbase_valid) {
BLI_split_dir_part(mainvar->filepath, dir_src, sizeof(dir_src));
- BLI_path_normalize(NULL, dir_src);
+ BLI_path_normalize(nullptr, dir_src);
}
else {
dir_src[0] = '\0';
}
BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
- BLI_path_normalize(NULL, dir_dst);
+ BLI_path_normalize(nullptr, dir_dst);
/* Only for relative, not relative-all, as this means making existing paths relative. */
if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {
@@ -1411,16 +1420,16 @@ bool BLO_write_file(Main *mainvar,
case BLO_WRITE_PATH_REMAP_RELATIVE:
/* Saved, make relative paths relative to new location (if possible). */
BLI_assert(relbase_valid);
- BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, NULL);
+ BKE_bpath_relative_rebase(mainvar, dir_src, dir_dst, nullptr);
break;
case BLO_WRITE_PATH_REMAP_RELATIVE_ALL:
/* Make all relative (when requested or unsaved). */
- BKE_bpath_relative_convert(mainvar, dir_dst, NULL);
+ BKE_bpath_relative_convert(mainvar, dir_dst, nullptr);
break;
case BLO_WRITE_PATH_REMAP_ABSOLUTE:
/* Make all absolute (when requested or unsaved). */
BLI_assert(relbase_valid);
- BKE_bpath_absolute_convert(mainvar, dir_src, NULL);
+ BKE_bpath_absolute_convert(mainvar, dir_src, nullptr);
break;
case BLO_WRITE_PATH_REMAP_NONE:
BLI_assert_unreachable(); /* Unreachable. */
@@ -1432,7 +1441,8 @@ bool BLO_write_file(Main *mainvar,
}
/* actual file writing */
- const bool err = write_file_handle(mainvar, &ww, NULL, NULL, write_flags, use_userdef, thumb);
+ const bool err = write_file_handle(
+ mainvar, &ww, nullptr, nullptr, write_flags, use_userdef, thumb);
ww.close(&ww);
@@ -1463,7 +1473,7 @@ bool BLO_write_file(Main *mainvar,
return false;
}
- if (G.debug & G_DEBUG_IO && mainvar->lock != NULL) {
+ if (G.debug & G_DEBUG_IO && mainvar->lock != nullptr) {
BKE_report(reports, RPT_INFO, "Checking sanity of current .blend file *AFTER* save to disk");
BLO_main_validate_libraries(mainvar, reports);
}
@@ -1476,7 +1486,7 @@ bool BLO_write_file_mem(Main *mainvar, MemFile *compare, MemFile *current, int w
bool use_userdef = false;
const bool err = write_file_handle(
- mainvar, NULL, compare, current, write_flags, use_userdef, NULL);
+ mainvar, nullptr, compare, current, write_flags, use_userdef, nullptr);
return (err == 0);
}
@@ -1595,7 +1605,7 @@ void BLO_write_float3_array(BlendWriter *writer, uint num, const float *data_ptr
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
{
- if (data_ptr != NULL) {
+ if (data_ptr != nullptr) {
BLO_write_raw(writer, strlen(data_ptr) + 1, data_ptr);
}
}
diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
index af570aa9d4b..615e30a728e 100644
--- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc
+++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc
@@ -11,6 +11,7 @@
#include "BKE_global.h"
#include "BKE_idtype.h"
#include "BKE_image.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_mball_tessellate.h"
#include "BKE_modifier.h"
@@ -116,7 +117,7 @@ bool BlendfileLoadingBaseTest::blendfile_load(const char *filepath)
}
char abspath[FILENAME_MAX];
- BLI_path_join(abspath, sizeof(abspath), test_assets_dir.c_str(), filepath, NULL);
+ BLI_path_join(abspath, sizeof(abspath), test_assets_dir.c_str(), filepath, nullptr);
BlendFileReadReport bf_reports = {nullptr};
bfile = BLO_read_from_file(abspath, BLO_READ_SKIP_NONE, &bf_reports);
@@ -125,6 +126,13 @@ bool BlendfileLoadingBaseTest::blendfile_load(const char *filepath)
<< test_assets_dir << "'";
return false;
}
+
+ /* Make sure that all view_layers in the file are synced. Depsgraph can make a copy of the whole
+ * scene, which will fail when one view layer isn't synced. */
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &bfile->curscene->view_layers) {
+ BKE_view_layer_synced_ensure(bfile->curscene, view_layer);
+ }
+
return true;
}
diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h
index 129eba3de2f..a4554c4e0bd 100644
--- a/source/blender/blentranslation/BLT_translation.h
+++ b/source/blender/blentranslation/BLT_translation.h
@@ -74,7 +74,7 @@ const char *BLT_translate_do_new_dataname(const char *msgctxt, const char *msgid
#define BLT_I18NCONTEXT_UI_EVENTS "UI_Events_KeyMaps"
/* Mark the msgid applies to several elements
- * (needed in some cases, as english adjectives have no plural mark :( ). */
+ * (needed in some cases, as English adjectives have no plural mark :( ). */
#define BLT_I18NCONTEXT_PLURAL "Plural"
/* ID-types contexts. */
diff --git a/source/blender/blentranslation/msgfmt/CMakeLists.txt b/source/blender/blentranslation/msgfmt/CMakeLists.txt
index 7b95bf210d7..dce3f0014f0 100644
--- a/source/blender/blentranslation/msgfmt/CMakeLists.txt
+++ b/source/blender/blentranslation/msgfmt/CMakeLists.txt
@@ -4,7 +4,7 @@
# -----------------------------------------------------------------------------
# Build msgfmt executable
-blender_include_dirs(
+set(INC
../../../../intern/guardedalloc
../../blenlib
)
@@ -13,21 +13,20 @@ set(SRC
msgfmt.c
)
+set(LIB
+ bf_blenlib
+ bf_intern_guardedalloc
+ ${ZLIB_LIBRARIES}
+ ${PLATFORM_LINKLIBS})
+
add_cc_flags_custom_test(msgfmt)
if(WIN32)
string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " /nodefaultlib:MSVCRT.lib")
+ list(APPEND LIB bf_intern_utfconv)
endif()
add_executable(msgfmt ${SRC})
setup_platform_linker_flags(msgfmt)
-
-target_link_libraries(msgfmt bf_blenlib)
-target_link_libraries(msgfmt bf_intern_guardedalloc)
-
-if(WIN32)
- target_link_libraries(msgfmt bf_intern_utfconv)
-endif()
-
-target_link_libraries(msgfmt ${ZLIB_LIBRARIES})
-target_link_libraries(msgfmt ${PLATFORM_LINKLIBS})
+blender_target_include_dirs(msgfmt ${INC})
+target_link_libraries(msgfmt ${LIB})
diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt
index 31492cd5c13..0efa5f73ae4 100644
--- a/source/blender/bmesh/CMakeLists.txt
+++ b/source/blender/bmesh/CMakeLists.txt
@@ -80,7 +80,7 @@ set(SRC
intern/bmesh_log.h
intern/bmesh_marking.c
intern/bmesh_marking.h
- intern/bmesh_mesh.c
+ intern/bmesh_mesh.cc
intern/bmesh_mesh.h
intern/bmesh_mesh_convert.cc
intern/bmesh_mesh_convert.h
@@ -111,7 +111,7 @@ set(SRC
intern/bmesh_query.c
intern/bmesh_query.h
intern/bmesh_query_inline.h
- intern/bmesh_query_uv.c
+ intern/bmesh_query_uv.cc
intern/bmesh_query_uv.h
intern/bmesh_structure.c
intern/bmesh_structure.h
diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h
index 2833105e5a4..3956db0288a 100644
--- a/source/blender/bmesh/bmesh.h
+++ b/source/blender/bmesh/bmesh.h
@@ -148,7 +148,7 @@
*
* These conventions should be used throughout the bmesh module.
*
- * - `bmesh_kernel_*()` - Low level API, for primitive functions that others are built ontop of.
+ * - `bmesh_kernel_*()` - Low level API, for primitive functions that others are built on top of.
* - `bmesh_***()` - Low level API function.
* - `bm_***()` - 'static' functions, not a part of the API at all,
* but use prefix since they operate on BMesh data.
diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c
index a7637d2712c..757d006b04d 100644
--- a/source/blender/bmesh/intern/bmesh_construct.c
+++ b/source/blender/bmesh/intern/bmesh_construct.c
@@ -512,16 +512,24 @@ void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst,
for (int i = 0; i < me_src_array_len; i++) {
const Mesh *me_src = me_src_array[i];
if (i == 0) {
- CustomData_copy(&me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0);
- CustomData_copy(&me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0);
- CustomData_copy(&me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0);
- CustomData_copy(&me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(
+ &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(
+ &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(
+ &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(
+ &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
}
else {
- CustomData_merge(&me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0);
- CustomData_merge(&me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0);
- CustomData_merge(&me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0);
- CustomData_merge(&me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0);
+ CustomData_merge_mesh_to_bmesh(
+ &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
+ CustomData_merge_mesh_to_bmesh(
+ &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
+ CustomData_merge_mesh_to_bmesh(
+ &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
+ CustomData_merge_mesh_to_bmesh(
+ &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
}
cd_flag |= me_src->cd_flag;
@@ -550,10 +558,10 @@ void BM_mesh_copy_init_customdata(BMesh *bm_dst, BMesh *bm_src, const BMAllocTem
allocsize = &bm_mesh_allocsize_default;
}
- CustomData_copy(&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0);
- CustomData_copy(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0);
- CustomData_copy(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0);
- CustomData_copy(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0);
+ CustomData_copy(&bm_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_SET_DEFAULT, 0);
+ CustomData_copy(&bm_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_SET_DEFAULT, 0);
+ CustomData_copy(&bm_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_SET_DEFAULT, 0);
+ CustomData_copy(&bm_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_SET_DEFAULT, 0);
CustomData_bmesh_init_pool(&bm_dst->vdata, allocsize->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm_dst->edata, allocsize->totedge, BM_EDGE);
@@ -588,7 +596,7 @@ void BM_mesh_copy_init_customdata_all_layers(BMesh *bm_dst,
for (int l = 0; l < src->totlayer; l++) {
CustomData_add_layer_named(
- dst, src->layers[l].type, CD_CALLOC, NULL, 0, src->layers[l].name);
+ dst, src->layers[l].type, CD_SET_DEFAULT, NULL, 0, src->layers[l].name);
}
CustomData_bmesh_init_pool(dst, size, htypes[i]);
}
@@ -714,26 +722,25 @@ BMesh *BM_mesh_copy(BMesh *bm_old)
char BM_vert_flag_from_mflag(const char mflag)
{
- return (((mflag & SELECT) ? BM_ELEM_SELECT : 0) | ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0));
+ return ((mflag & SELECT) ? BM_ELEM_SELECT : 0);
}
char BM_edge_flag_from_mflag(const short mflag)
{
return (((mflag & SELECT) ? BM_ELEM_SELECT : 0) | ((mflag & ME_SEAM) ? BM_ELEM_SEAM : 0) |
((mflag & ME_EDGEDRAW) ? BM_ELEM_DRAW : 0) |
- ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0) | /* invert */
- ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0));
+ ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0));
}
char BM_face_flag_from_mflag(const char mflag)
{
return (((mflag & ME_FACE_SEL) ? BM_ELEM_SELECT : 0) |
- ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0) | ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0));
+ ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0));
}
char BM_vert_flag_to_mflag(BMVert *v)
{
const char hflag = v->head.hflag;
- return (((hflag & BM_ELEM_SELECT) ? SELECT : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0));
+ return (((hflag & BM_ELEM_SELECT) ? SELECT : 0));
}
short BM_edge_flag_to_mflag(BMEdge *e)
@@ -743,7 +750,6 @@ short BM_edge_flag_to_mflag(BMEdge *e)
return (((hflag & BM_ELEM_SELECT) ? SELECT : 0) | ((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) |
((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) |
((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) |
- ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) |
(BM_edge_is_wire(e) ? ME_LOOSEEDGE : 0) | /* not typical */
ME_EDGERENDER);
}
@@ -752,5 +758,5 @@ char BM_face_flag_to_mflag(BMFace *f)
const char hflag = f->head.hflag;
return (((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) |
- ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0));
+ ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0));
}
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c
index 4d84d558cd7..5fbbd087d7e 100644
--- a/source/blender/bmesh/intern/bmesh_core.c
+++ b/source/blender/bmesh/intern/bmesh_core.c
@@ -95,7 +95,7 @@ BMVert *BM_vert_create(BMesh *bm,
/* handles 'v->no' too */
BM_elem_attrs_copy(bm, bm, v_example, v);
- /* exception: don't copy the original shapekey index */
+ /* Exception: don't copy the original shape-key index. */
keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX);
if (keyi) {
*keyi = ORIGINDEX_NONE;
diff --git a/source/blender/bmesh/intern/bmesh_delete.c b/source/blender/bmesh/intern/bmesh_delete.c
index e2436e53099..8ec7dc410d0 100644
--- a/source/blender/bmesh/intern/bmesh_delete.c
+++ b/source/blender/bmesh/intern/bmesh_delete.c
@@ -86,7 +86,6 @@ void BMO_mesh_delete_oflag_tagged(BMesh *bm, const short oflag, const char htype
void BMO_mesh_delete_oflag_context(BMesh *bm, const short oflag, const int type)
{
BMEdge *e;
- BMFace *f;
BMIter eiter;
BMIter fiter;
@@ -128,6 +127,7 @@ void BMO_mesh_delete_oflag_context(BMesh *bm, const short oflag, const int type)
case DEL_FACES:
case DEL_FACES_KEEP_BOUNDARY: {
/* go through and mark all edges and all verts of all faces for delete */
+ BMFace *f;
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
if (BMO_face_flag_test(bm, f, oflag)) {
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
@@ -257,8 +257,6 @@ void BM_mesh_delete_hflag_tagged(BMesh *bm, const char hflag, const char htype)
void BM_mesh_delete_hflag_context(BMesh *bm, const char hflag, const int type)
{
- BMEdge *e;
- BMFace *f;
BMIter eiter;
BMIter fiter;
@@ -271,6 +269,7 @@ void BM_mesh_delete_hflag_context(BMesh *bm, const char hflag, const int type)
}
case DEL_EDGES: {
/* flush down to vert */
+ BMEdge *e;
BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
if (BM_elem_flag_test(e, hflag)) {
BM_elem_flag_enable(e->v1, hflag);
@@ -299,6 +298,8 @@ void BM_mesh_delete_hflag_context(BMesh *bm, const char hflag, const int type)
}
case DEL_FACES: {
/* go through and mark all edges and all verts of all faces for delete */
+ BMFace *f;
+ BMEdge *e;
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
if (BM_elem_flag_test(f, hflag)) {
BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c
index 0c3db31dd1f..b7028dee5e1 100644
--- a/source/blender/bmesh/intern/bmesh_interp.c
+++ b/source/blender/bmesh/intern/bmesh_interp.c
@@ -846,7 +846,7 @@ void BM_data_layer_add(BMesh *bm, CustomData *data, int type)
/* the pool is now owned by olddata and must not be shared */
data->pool = NULL;
- CustomData_add_layer(data, type, CD_DEFAULT, NULL, 0);
+ CustomData_add_layer(data, type, CD_SET_DEFAULT, NULL, 0);
update_data_blocks(bm, &olddata, data);
if (olddata.layers) {
@@ -864,7 +864,7 @@ void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const char *
/* the pool is now owned by olddata and must not be shared */
data->pool = NULL;
- CustomData_add_layer_named(data, type, CD_DEFAULT, NULL, 0, name);
+ CustomData_add_layer_named(data, type, CD_SET_DEFAULT, NULL, 0, name);
update_data_blocks(bm, &olddata, data);
if (olddata.layers) {
diff --git a/source/blender/bmesh/intern/bmesh_log.c b/source/blender/bmesh/intern/bmesh_log.c
index a81ae934629..64e6c63e9f0 100644
--- a/source/blender/bmesh/intern/bmesh_log.c
+++ b/source/blender/bmesh/intern/bmesh_log.c
@@ -34,42 +34,41 @@
struct BMLogEntry {
struct BMLogEntry *next, *prev;
- /* The following GHashes map from an element ID to one of the log
- * types above */
+ /* The following #GHash members map from an element ID to one of the log types above. */
- /* Elements that were in the previous entry, but have been
- * deleted */
+ /** Elements that were in the previous entry, but have been deleted. */
GHash *deleted_verts;
GHash *deleted_faces;
- /* Elements that were not in the previous entry, but are in the
- * result of this entry */
+ /** Elements that were not in the previous entry, but are in the result of this entry. */
GHash *added_verts;
GHash *added_faces;
- /* Vertices whose coordinates, mask value, or hflag have changed */
+ /** Vertices whose coordinates, mask value, or hflag have changed. */
GHash *modified_verts;
GHash *modified_faces;
BLI_mempool *pool_verts;
BLI_mempool *pool_faces;
- /* This is only needed for dropping BMLogEntries while still in
+ /**
+ * This is only needed for dropping BMLogEntries while still in
* dynamic-topology mode, as that should release vert/face IDs
- * back to the BMLog but no BMLog pointer is available at that
- * time.
+ * back to the BMLog but no BMLog pointer is available at that time.
*
* This field is not guaranteed to be valid, any use of it should
- * check for NULL. */
+ * check for NULL.
+ */
BMLog *log;
};
struct BMLog {
- /* Tree of free IDs */
+ /** Tree of free IDs */
struct RangeTreeUInt *unused_ids;
- /* Mapping from unique IDs to vertices and faces
+ /**
+ * Mapping from unique IDs to vertices and faces
*
- * Each vertex and face in the log gets a unique uinteger
+ * Each vertex and face in the log gets a unique `uint`
* assigned. That ID is taken from the set managed by the
* unused_ids range tree.
*
@@ -79,10 +78,11 @@ struct BMLog {
GHash *id_to_elem;
GHash *elem_to_id;
- /* All BMLogEntrys, ordered from earliest to most recent */
+ /** All #BMLogEntrys, ordered from earliest to most recent. */
ListBase entries;
- /* The current log entry from entries list
+ /**
+ * The current log entry from entries list
*
* If null, then the original mesh from before any of the log
* entries is current (i.e. there is nothing left to undo.)
diff --git a/source/blender/bmesh/intern/bmesh_log.h b/source/blender/bmesh/intern/bmesh_log.h
index 189aa97509f..75ff54c7d46 100644
--- a/source/blender/bmesh/intern/bmesh_log.h
+++ b/source/blender/bmesh/intern/bmesh_log.h
@@ -14,14 +14,15 @@ struct RangeTreeUInt;
typedef struct BMLog BMLog;
typedef struct BMLogEntry BMLogEntry;
-/* Allocate and initialize a new BMLog */
-/* Allocate, initialize, and assign a new BMLog */
+/**
+ * Allocate, initialize, and assign a new BMLog.
+ */
BMLog *BM_log_create(BMesh *bm);
-/* Allocate and initialize a new BMLog using existing BMLogEntries */
-/* Allocate and initialize a new BMLog using existing BMLogEntries
+/**
+ * Allocate and initialize a new #BMLog using existing #BMLogEntries
*
- * The 'entry' should be the last entry in the BMLog. Its prev pointer
+ * The 'entry' should be the last entry in the #BMLog. Its `prev` pointer
* will be followed back to find the first entry.
*
* The unused IDs field of the log will be initialized by taking all
@@ -29,20 +30,21 @@ BMLog *BM_log_create(BMesh *bm);
*/
BMLog *BM_log_from_existing_entries_create(BMesh *bm, BMLogEntry *entry);
-/* Free all the data in a BMLog including the log itself */
-/* Free all the data in a BMLog including the log itself */
+/**
+ * Free all the data in a BMLog including the log itself.
+ */
void BM_log_free(BMLog *log);
-/* Get the number of log entries */
-/* Get the number of log entries */
+/**
+ * Get the number of log entries.
+ */
int BM_log_length(const BMLog *log);
-/* Apply a consistent ordering to BMesh vertices and faces */
-/* Apply a consistent ordering to BMesh vertices */
+/** Apply a consistent ordering to BMesh vertices and faces. */
void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log);
-/* Start a new log entry and update the log entry list */
-/* Start a new log entry and update the log entry list
+/**
+ * Start a new log entry and update the log entry list.
*
* If the log entry list is empty, or if the current log entry is the
* last entry, the new entry is simply appended to the end.
@@ -54,35 +56,36 @@ void BM_log_mesh_elems_reorder(BMesh *bm, BMLog *log);
*/
BMLogEntry *BM_log_entry_add(BMLog *log);
-/* Mark all used ids as unused for this node */
+/** Mark all used ids as unused for this node */
void BM_log_cleanup_entry(BMLogEntry *entry);
-/* Remove an entry from the log */
-/* Remove an entry from the log
+/**
+ * Remove an entry from the log.
*
* Uses entry->log as the log. If the log is NULL, the entry will be
- * free'd but not removed from any list, nor shall its IDs be
- * released.
+ * free'd but not removed from any list, nor shall its IDs be released.
*
* This operation is only valid on the first and last entries in the
* log. Deleting from the middle will assert.
*/
void BM_log_entry_drop(BMLogEntry *entry);
-/* Undo one BMLogEntry */
-/* Undo one BMLogEntry
+/**
+ * Undo one #BMLogEntry.
*
- * Has no effect if there's nothing left to undo */
+ * Has no effect if there's nothing left to undo.
+ */
void BM_log_undo(BMesh *bm, BMLog *log);
-/* Redo one BMLogEntry */
-/* Redo one BMLogEntry
+/**
+ * Redo one #BMLogEntry.
*
- * Has no effect if there's nothing left to redo */
+ * Has no effect if there's nothing left to redo.
+ */
void BM_log_redo(BMesh *bm, BMLog *log);
-/* Log a vertex before it is modified */
-/* Log a vertex before it is modified
+/**
+ * Log a vertex before it is modified.
*
* Before modifying vertex coordinates, masks, or hflags, call this
* function to log its current values. This is better than logging
@@ -107,8 +110,8 @@ void BM_log_redo(BMesh *bm, BMLog *log);
*/
void BM_log_vert_before_modified(BMLog *log, struct BMVert *v, int cd_vert_mask_offset);
-/* Log a new vertex as added to the BMesh */
-/* Log a new vertex as added to the BMesh
+/**
+ * Log a new vertex as added to the #BMesh.
*
* The new vertex gets a unique ID assigned. It is then added to a map
* of added vertices, with the key being its ID and the value
@@ -116,16 +119,16 @@ void BM_log_vert_before_modified(BMLog *log, struct BMVert *v, int cd_vert_mask_
*/
void BM_log_vert_added(BMLog *log, struct BMVert *v, int cd_vert_mask_offset);
-/* Log a face before it is modified */
-/* Log a face before it is modified
+/**
+ * Log a face before it is modified.
*
* This is intended to handle only header flags and we always
- * assume face has been added before
+ * assume face has been added before.
*/
void BM_log_face_modified(BMLog *log, struct BMFace *f);
-/* Log a new face as added to the BMesh */
-/* Log a new face as added to the BMesh
+/**
+ * Log a new face as added to the #BMesh.
*
* The new face gets a unique ID assigned. It is then added to a map
* of added faces, with the key being its ID and the value containing
@@ -133,8 +136,8 @@ void BM_log_face_modified(BMLog *log, struct BMFace *f);
*/
void BM_log_face_added(BMLog *log, struct BMFace *f);
-/* Log a vertex as removed from the BMesh */
-/* Log a vertex as removed from the BMesh
+/**
+ * Log a vertex as removed from the #BMesh.
*
* A couple things can happen here:
*
@@ -142,7 +145,7 @@ void BM_log_face_added(BMLog *log, struct BMFace *f);
* deleted and forgotten about entirely. Its unique ID is returned to
* the unused pool.
*
- * If the vertex was already part of the BMesh before the current log
+ * If the vertex was already part of the #BMesh before the current log
* entry, it is added to a map of deleted vertices, with the key being
* its ID and the value containing everything needed to reconstruct
* that vertex.
@@ -152,8 +155,8 @@ void BM_log_face_added(BMLog *log, struct BMFace *f);
*/
void BM_log_vert_removed(BMLog *log, struct BMVert *v, int cd_vert_mask_offset);
-/* Log a face as removed from the BMesh */
-/* Log a face as removed from the BMesh
+/**
+ * Log a face as removed from the #BMesh.
*
* A couple things can happen here:
*
@@ -161,43 +164,45 @@ void BM_log_vert_removed(BMLog *log, struct BMVert *v, int cd_vert_mask_offset);
* deleted and forgotten about entirely. Its unique ID is returned to
* the unused pool.
*
- * If the face was already part of the BMesh before the current log
+ * If the face was already part of the #BMesh before the current log
* entry, it is added to a map of deleted faces, with the key being
* its ID and the value containing everything needed to reconstruct
* that face.
*/
void BM_log_face_removed(BMLog *log, struct BMFace *f);
-/* Log all vertices/faces in the BMesh as added */
-/* Log all vertices/faces in the BMesh as added */
+/**
+ * Log all vertices/faces in the #BMesh as added.
+ */
void BM_log_all_added(BMesh *bm, BMLog *log);
-/* Log all vertices/faces in the BMesh as removed */
-/* Log all vertices/faces in the BMesh as removed */
+/** Log all vertices/faces in the #BMesh as removed. */
void BM_log_before_all_removed(BMesh *bm, BMLog *log);
-/* Get the logged coordinates of a vertex */
-/* Get the logged coordinates of a vertex
+/**
+ * Get the logged coordinates of a vertex.
*
- * Does not modify the log or the vertex */
+ * Does not modify the log or the vertex.
+ */
const float *BM_log_original_vert_co(BMLog *log, BMVert *v);
-/* Get the logged normal of a vertex
+/**
+ * Get the logged normal of a vertex
*
- * Does not modify the log or the vertex */
+ * Does not modify the log or the vertex.
+ */
const float *BM_log_original_vert_no(BMLog *log, BMVert *v);
-/* Get the logged mask of a vertex */
-/* Get the logged mask of a vertex
+/** Get the logged mask of a vertex
*
- * Does not modify the log or the vertex */
+ * Does not modify the log or the vertex.
+ */
float BM_log_original_mask(BMLog *log, BMVert *v);
-/* Get the logged data of a vertex (avoid multiple lookups) */
+/** Get the logged data of a vertex (avoid multiple lookups). */
void BM_log_original_vert_data(BMLog *log, BMVert *v, const float **r_co, const float **r_no);
-/* For internal use only (unit testing) */
-/* For internal use only (unit testing) */
+/** For internal use only (unit testing). */
BMLogEntry *BM_log_current_entry(BMLog *log);
-/* For internal use only (unit testing) */
+/** For internal use only (unit testing) */
struct RangeTreeUInt *BM_log_unused_ids(BMLog *log);
diff --git a/source/blender/bmesh/intern/bmesh_marking.c b/source/blender/bmesh/intern/bmesh_marking.c
index f348cd58085..a1c2815ab2f 100644
--- a/source/blender/bmesh/intern/bmesh_marking.c
+++ b/source/blender/bmesh/intern/bmesh_marking.c
@@ -1020,7 +1020,7 @@ static BMEditSelection *bm_select_history_create(BMHeader *ele)
return ese;
}
-/* --- macro wrapped funcs --- */
+/* --- Macro wrapped functions. --- */
bool _bm_select_history_check(BMesh *bm, const BMHeader *ele)
{
@@ -1075,7 +1075,7 @@ void _bm_select_history_store_after(BMesh *bm, BMEditSelection *ese_ref, BMHeade
BM_select_history_store_after_notest(bm, ese_ref, (BMElem *)ele);
}
}
-/* --- end macro wrapped funcs --- */
+/* --- End macro wrapped functions --- */
void BM_select_history_clear(BMesh *bm)
{
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.cc
index 7dba854b9ef..4f42ce4a470 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.cc
@@ -88,19 +88,19 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm)
BMVert_OFlag *v_olfag;
BLI_mempool *toolflagpool = bm->vtoolflagpool;
BM_ITER_MESH (v_olfag, &iter, bm, BM_VERTS_OF_MESH) {
- v_olfag->oflags = BLI_mempool_calloc(toolflagpool);
+ v_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool));
}
BMEdge_OFlag *e_olfag;
toolflagpool = bm->etoolflagpool;
BM_ITER_MESH (e_olfag, &iter, bm, BM_EDGES_OF_MESH) {
- e_olfag->oflags = BLI_mempool_calloc(toolflagpool);
+ e_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool));
}
BMFace_OFlag *f_olfag;
toolflagpool = bm->ftoolflagpool;
BM_ITER_MESH (f_olfag, &iter, bm, BM_FACES_OF_MESH) {
- f_olfag->oflags = BLI_mempool_calloc(toolflagpool);
+ f_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool));
}
bm->totflags = 1;
@@ -110,22 +110,22 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm)
{
if (bm->vtoolflagpool) {
BLI_mempool_destroy(bm->vtoolflagpool);
- bm->vtoolflagpool = NULL;
+ bm->vtoolflagpool = nullptr;
}
if (bm->etoolflagpool) {
BLI_mempool_destroy(bm->etoolflagpool);
- bm->etoolflagpool = NULL;
+ bm->etoolflagpool = nullptr;
}
if (bm->ftoolflagpool) {
BLI_mempool_destroy(bm->ftoolflagpool);
- bm->ftoolflagpool = NULL;
+ bm->ftoolflagpool = nullptr;
}
}
BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreateParams *params)
{
/* allocate the structure */
- BMesh *bm = MEM_callocN(sizeof(BMesh), __func__);
+ BMesh *bm = static_cast<BMesh *>(MEM_callocN(sizeof(BMesh), __func__));
/* allocate the memory pools for the mesh elements */
bm_mempool_init(bm, allocsize, params->use_toolflags);
@@ -262,8 +262,8 @@ void BM_mesh_free(BMesh *bm)
if (bm->py_handle) {
/* keep this out of 'BM_mesh_data_free' because we want python
* to be able to clear the mesh and maintain access. */
- bpy_bm_generic_invalidate(bm->py_handle);
- bm->py_handle = NULL;
+ bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(bm->py_handle));
+ bm->py_handle = nullptr;
}
MEM_freeN(bm);
@@ -336,7 +336,7 @@ void BM_mesh_elem_index_ensure_ex(BMesh *bm, const char htype, int elem_offset[4
BM_ELEM_INDEX_VALIDATE(bm, "Should Never Fail!", __func__);
#endif
- if (elem_offset == NULL) {
+ if (elem_offset == nullptr) {
/* Simple case. */
const char htype_needed = bm->elem_index_dirty & htype;
if (htype_needed == 0) {
@@ -445,7 +445,7 @@ finally:
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
{
- BM_mesh_elem_index_ensure_ex(bm, htype, NULL);
+ BM_mesh_elem_index_ensure_ex(bm, htype, nullptr);
}
void BM_mesh_elem_index_validate(
@@ -459,7 +459,7 @@ void BM_mesh_elem_index_validate(
BMIter iter;
BMElem *ele;
int i;
- bool is_any_error = 0;
+ bool is_any_error = false;
for (i = 0; i < 3; i++) {
const bool is_dirty = (flag_types[i] & bm->elem_index_dirty) != 0;
@@ -581,9 +581,11 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
if (bm->vtable) {
MEM_freeN(bm->vtable);
}
- bm->vtable = MEM_mallocN(sizeof(void **) * bm->totvert, "bm->vtable");
+ bm->vtable = static_cast<BMVert **>(
+ MEM_mallocN(sizeof(void **) * bm->totvert, "bm->vtable"));
bm->vtable_tot = bm->totvert;
}
+ BM_iter_as_array(bm, BM_VERTS_OF_MESH, nullptr, (void **)bm->vtable, bm->totvert);
}
if (htype_needed & BM_EDGE) {
if (bm->etable && bm->totedge <= bm->etable_tot && bm->totedge * 2 >= bm->etable_tot) {
@@ -593,9 +595,11 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
if (bm->etable) {
MEM_freeN(bm->etable);
}
- bm->etable = MEM_mallocN(sizeof(void **) * bm->totedge, "bm->etable");
+ bm->etable = static_cast<BMEdge **>(
+ MEM_mallocN(sizeof(void **) * bm->totedge, "bm->etable"));
bm->etable_tot = bm->totedge;
}
+ BM_iter_as_array(bm, BM_EDGES_OF_MESH, nullptr, (void **)bm->etable, bm->totedge);
}
if (htype_needed & BM_FACE) {
if (bm->ftable && bm->totface <= bm->ftable_tot && bm->totface * 2 >= bm->ftable_tot) {
@@ -605,21 +609,11 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
if (bm->ftable) {
MEM_freeN(bm->ftable);
}
- bm->ftable = MEM_mallocN(sizeof(void **) * bm->totface, "bm->ftable");
+ bm->ftable = static_cast<BMFace **>(
+ MEM_mallocN(sizeof(void **) * bm->totface, "bm->ftable"));
bm->ftable_tot = bm->totface;
}
- }
-
- if (htype_needed & BM_VERT) {
- BM_iter_as_array(bm, BM_VERTS_OF_MESH, NULL, (void **)bm->vtable, bm->totvert);
- }
-
- if (htype_needed & BM_EDGE) {
- BM_iter_as_array(bm, BM_EDGES_OF_MESH, NULL, (void **)bm->etable, bm->totedge);
- }
-
- if (htype_needed & BM_FACE) {
- BM_iter_as_array(bm, BM_FACES_OF_MESH, NULL, (void **)bm->ftable, bm->totface);
+ BM_iter_as_array(bm, BM_FACES_OF_MESH, nullptr, (void **)bm->ftable, bm->totface);
}
finally:
@@ -656,17 +650,17 @@ void BM_mesh_elem_table_free(BMesh *bm, const char htype)
BMVert *BM_vert_at_index_find(BMesh *bm, const int index)
{
- return BLI_mempool_findelem(bm->vpool, index);
+ return static_cast<BMVert *>(BLI_mempool_findelem(bm->vpool, index));
}
BMEdge *BM_edge_at_index_find(BMesh *bm, const int index)
{
- return BLI_mempool_findelem(bm->epool, index);
+ return static_cast<BMEdge *>(BLI_mempool_findelem(bm->epool, index));
}
BMFace *BM_face_at_index_find(BMesh *bm, const int index)
{
- return BLI_mempool_findelem(bm->fpool, index);
+ return static_cast<BMFace *>(BLI_mempool_findelem(bm->fpool, index));
}
BMLoop *BM_loop_at_index_find(BMesh *bm, const int index)
@@ -687,13 +681,13 @@ BMLoop *BM_loop_at_index_find(BMesh *bm, const int index)
}
i -= f->len;
}
- return NULL;
+ return nullptr;
}
BMVert *BM_vert_at_index_find_or_table(BMesh *bm, const int index)
{
if ((bm->elem_table_dirty & BM_VERT) == 0) {
- return (index < bm->totvert) ? bm->vtable[index] : NULL;
+ return (index < bm->totvert) ? bm->vtable[index] : nullptr;
}
return BM_vert_at_index_find(bm, index);
}
@@ -701,7 +695,7 @@ BMVert *BM_vert_at_index_find_or_table(BMesh *bm, const int index)
BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, const int index)
{
if ((bm->elem_table_dirty & BM_EDGE) == 0) {
- return (index < bm->totedge) ? bm->etable[index] : NULL;
+ return (index < bm->totedge) ? bm->etable[index] : nullptr;
}
return BM_edge_at_index_find(bm, index);
}
@@ -709,7 +703,7 @@ BMEdge *BM_edge_at_index_find_or_table(BMesh *bm, const int index)
BMFace *BM_face_at_index_find_or_table(BMesh *bm, const int index)
{
if ((bm->elem_table_dirty & BM_FACE) == 0) {
- return (index < bm->totface) ? bm->ftable[index] : NULL;
+ return (index < bm->totface) ? bm->ftable[index] : nullptr;
}
return BM_face_at_index_find(bm, index);
}
@@ -735,7 +729,7 @@ int BM_mesh_elem_count(BMesh *bm, const char htype)
void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const uint *face_idx)
{
/* Mapping old to new pointers. */
- GHash *vptr_map = NULL, *eptr_map = NULL, *fptr_map = NULL;
+ GHash *vptr_map = nullptr, *eptr_map = nullptr, *fptr_map = nullptr;
BMIter iter, iterl;
BMVert *ve;
BMEdge *ed;
@@ -763,14 +757,17 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
/* Make a copy of all vertices. */
verts_pool = bm->vtable;
- verts_copy = MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy");
- void **pyptrs = (cd_vert_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totvert, __func__) : NULL;
+ verts_copy = static_cast<BMVert *>(
+ MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy"));
+ void **pyptrs = (cd_vert_pyptr != -1) ?
+ static_cast<void **>(MEM_mallocN(sizeof(void *) * totvert, __func__)) :
+ nullptr;
for (i = totvert, ve = verts_copy + totvert - 1, vep = verts_pool + totvert - 1; i--;
ve--, vep--) {
*ve = **vep;
// printf("*vep: %p, verts_pool[%d]: %p\n", *vep, i, verts_pool[i]);
if (cd_vert_pyptr != -1) {
- void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr);
+ void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr));
pyptrs[i] = *pyptr;
}
}
@@ -788,7 +785,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
#endif
BLI_ghash_insert(vptr_map, *vep, new_vep);
if (cd_vert_pyptr != -1) {
- void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr);
+ void **pyptr = static_cast<void **>(
+ BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr));
*pyptr = pyptrs[*new_idx];
}
}
@@ -815,13 +813,16 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
/* Make a copy of all vertices. */
edges_pool = bm->etable;
- edges_copy = MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy");
- void **pyptrs = (cd_edge_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totedge, __func__) : NULL;
+ edges_copy = static_cast<BMEdge *>(
+ MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy"));
+ void **pyptrs = (cd_edge_pyptr != -1) ?
+ static_cast<void **>(MEM_mallocN(sizeof(void *) * totedge, __func__)) :
+ nullptr;
for (i = totedge, ed = edges_copy + totedge - 1, edp = edges_pool + totedge - 1; i--;
ed--, edp--) {
*ed = **edp;
if (cd_edge_pyptr != -1) {
- void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr);
+ void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr));
pyptrs[i] = *pyptr;
}
}
@@ -839,7 +840,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
"mapping edge from %d to %d (%p/%p to %p)\n", i, *new_idx, *edp, edges_pool[i], new_edp);
#endif
if (cd_edge_pyptr != -1) {
- void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr);
+ void **pyptr = static_cast<void **>(
+ BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr));
*pyptr = pyptrs[*new_idx];
}
}
@@ -866,13 +868,16 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
/* Make a copy of all vertices. */
faces_pool = bm->ftable;
- faces_copy = MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy");
- void **pyptrs = (cd_poly_pyptr != -1) ? MEM_mallocN(sizeof(void *) * totface, __func__) : NULL;
+ faces_copy = static_cast<BMFace *>(
+ MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy"));
+ void **pyptrs = (cd_poly_pyptr != -1) ?
+ static_cast<void **>(MEM_mallocN(sizeof(void *) * totface, __func__)) :
+ nullptr;
for (i = totface, fa = faces_copy + totface - 1, fap = faces_pool + totface - 1; i--;
fa--, fap--) {
*fa = **fap;
if (cd_poly_pyptr != -1) {
- void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr);
+ void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr));
pyptrs[i] = *pyptr;
}
}
@@ -886,7 +891,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
*new_fap = *fa;
BLI_ghash_insert(fptr_map, *fap, new_fap);
if (cd_poly_pyptr != -1) {
- void **pyptr = BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr);
+ void **pyptr = static_cast<void **>(
+ BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr));
*pyptr = pyptrs[*new_idx];
}
}
@@ -906,7 +912,7 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
BM_ITER_MESH (ve, &iter, bm, BM_VERTS_OF_MESH) {
// printf("Vert e: %p -> %p\n", ve->e, BLI_ghash_lookup(eptr_map, ve->e));
if (ve->e) {
- ve->e = BLI_ghash_lookup(eptr_map, ve->e);
+ ve->e = static_cast<BMEdge *>(BLI_ghash_lookup(eptr_map, ve->e));
BLI_assert(ve->e);
}
}
@@ -922,8 +928,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
printf("Edge v1: %p -> %p\n", ed->v1, BLI_ghash_lookup(vptr_map, ed->v1));
printf("Edge v2: %p -> %p\n", ed->v2, BLI_ghash_lookup(vptr_map, ed->v2));
#endif
- ed->v1 = BLI_ghash_lookup(vptr_map, ed->v1);
- ed->v2 = BLI_ghash_lookup(vptr_map, ed->v2);
+ ed->v1 = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, ed->v1));
+ ed->v2 = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, ed->v2));
BLI_assert(ed->v1);
BLI_assert(ed->v2);
}
@@ -942,10 +948,14 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
ed->v2_disk_link.next,
BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next));
#endif
- ed->v1_disk_link.prev = BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev);
- ed->v1_disk_link.next = BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next);
- ed->v2_disk_link.prev = BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev);
- ed->v2_disk_link.next = BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next);
+ ed->v1_disk_link.prev = static_cast<BMEdge *>(
+ BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev));
+ ed->v1_disk_link.next = static_cast<BMEdge *>(
+ BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next));
+ ed->v2_disk_link.prev = static_cast<BMEdge *>(
+ BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev));
+ ed->v2_disk_link.next = static_cast<BMEdge *>(
+ BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next));
BLI_assert(ed->v1_disk_link.prev);
BLI_assert(ed->v1_disk_link.next);
BLI_assert(ed->v2_disk_link.prev);
@@ -959,17 +969,17 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
BM_ITER_ELEM (lo, &iterl, fa, BM_LOOPS_OF_FACE) {
if (vptr_map) {
// printf("Loop v: %p -> %p\n", lo->v, BLI_ghash_lookup(vptr_map, lo->v));
- lo->v = BLI_ghash_lookup(vptr_map, lo->v);
+ lo->v = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, lo->v));
BLI_assert(lo->v);
}
if (eptr_map) {
// printf("Loop e: %p -> %p\n", lo->e, BLI_ghash_lookup(eptr_map, lo->e));
- lo->e = BLI_ghash_lookup(eptr_map, lo->e);
+ lo->e = static_cast<BMEdge *>(BLI_ghash_lookup(eptr_map, lo->e));
BLI_assert(lo->e);
}
if (fptr_map) {
// printf("Loop f: %p -> %p\n", lo->f, BLI_ghash_lookup(fptr_map, lo->f));
- lo->f = BLI_ghash_lookup(fptr_map, lo->f);
+ lo->f = static_cast<BMFace *>(BLI_ghash_lookup(fptr_map, lo->f));
BLI_assert(lo->f);
}
}
@@ -978,23 +988,23 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
/* Selection history */
{
BMEditSelection *ese;
- for (ese = bm->selected.first; ese; ese = ese->next) {
+ for (ese = static_cast<BMEditSelection *>(bm->selected.first); ese; ese = ese->next) {
switch (ese->htype) {
case BM_VERT:
if (vptr_map) {
- ese->ele = BLI_ghash_lookup(vptr_map, ese->ele);
+ ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(vptr_map, ese->ele));
BLI_assert(ese->ele);
}
break;
case BM_EDGE:
if (eptr_map) {
- ese->ele = BLI_ghash_lookup(eptr_map, ese->ele);
+ ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(eptr_map, ese->ele));
BLI_assert(ese->ele);
}
break;
case BM_FACE:
if (fptr_map) {
- ese->ele = BLI_ghash_lookup(fptr_map, ese->ele);
+ ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(fptr_map, ese->ele));
BLI_assert(ese->ele);
}
break;
@@ -1004,19 +1014,19 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const
if (fptr_map) {
if (bm->act_face) {
- bm->act_face = BLI_ghash_lookup(fptr_map, bm->act_face);
+ bm->act_face = static_cast<BMFace *>(BLI_ghash_lookup(fptr_map, bm->act_face));
BLI_assert(bm->act_face);
}
}
if (vptr_map) {
- BLI_ghash_free(vptr_map, NULL, NULL);
+ BLI_ghash_free(vptr_map, nullptr, nullptr);
}
if (eptr_map) {
- BLI_ghash_free(eptr_map, NULL, NULL);
+ BLI_ghash_free(eptr_map, nullptr, nullptr);
}
if (fptr_map) {
- BLI_ghash_free(fptr_map, NULL, NULL);
+ BLI_ghash_free(fptr_map, nullptr, nullptr);
}
}
@@ -1030,14 +1040,18 @@ void BM_mesh_rebuild(BMesh *bm,
const char remap = (vpool_dst ? BM_VERT : 0) | (epool_dst ? BM_EDGE : 0) |
(lpool_dst ? BM_LOOP : 0) | (fpool_dst ? BM_FACE : 0);
- BMVert **vtable_dst = (remap & BM_VERT) ? MEM_mallocN(bm->totvert * sizeof(BMVert *), __func__) :
- NULL;
- BMEdge **etable_dst = (remap & BM_EDGE) ? MEM_mallocN(bm->totedge * sizeof(BMEdge *), __func__) :
- NULL;
- BMLoop **ltable_dst = (remap & BM_LOOP) ? MEM_mallocN(bm->totloop * sizeof(BMLoop *), __func__) :
- NULL;
- BMFace **ftable_dst = (remap & BM_FACE) ? MEM_mallocN(bm->totface * sizeof(BMFace *), __func__) :
- NULL;
+ BMVert **vtable_dst = (remap & BM_VERT) ? static_cast<BMVert **>(MEM_mallocN(
+ sizeof(BMVert *) * bm->totvert, __func__)) :
+ nullptr;
+ BMEdge **etable_dst = (remap & BM_EDGE) ? static_cast<BMEdge **>(MEM_mallocN(
+ sizeof(BMEdge *) * bm->totedge, __func__)) :
+ nullptr;
+ BMLoop **ltable_dst = (remap & BM_LOOP) ? static_cast<BMLoop **>(MEM_mallocN(
+ sizeof(BMLoop *) * bm->totloop, __func__)) :
+ nullptr;
+ BMFace **ftable_dst = (remap & BM_FACE) ? static_cast<BMFace **>(MEM_mallocN(
+ sizeof(BMFace *) * bm->totface, __func__)) :
+ nullptr;
const bool use_toolflags = params->use_toolflags;
@@ -1046,12 +1060,13 @@ void BM_mesh_rebuild(BMesh *bm,
int index;
BMVert *v_src;
BM_ITER_MESH_INDEX (v_src, &iter, bm, BM_VERTS_OF_MESH, index) {
- BMVert *v_dst = BLI_mempool_alloc(vpool_dst);
+ BMVert *v_dst = static_cast<BMVert *>(BLI_mempool_alloc(vpool_dst));
memcpy(v_dst, v_src, sizeof(BMVert));
if (use_toolflags) {
((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ?
- BLI_mempool_calloc(bm->vtoolflagpool) :
- NULL;
+ static_cast<BMFlagLayer *>(
+ BLI_mempool_calloc(bm->vtoolflagpool)) :
+ nullptr;
}
vtable_dst[index] = v_dst;
@@ -1064,12 +1079,13 @@ void BM_mesh_rebuild(BMesh *bm,
int index;
BMEdge *e_src;
BM_ITER_MESH_INDEX (e_src, &iter, bm, BM_EDGES_OF_MESH, index) {
- BMEdge *e_dst = BLI_mempool_alloc(epool_dst);
+ BMEdge *e_dst = static_cast<BMEdge *>(BLI_mempool_alloc(epool_dst));
memcpy(e_dst, e_src, sizeof(BMEdge));
if (use_toolflags) {
((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ?
- BLI_mempool_calloc(bm->etoolflagpool) :
- NULL;
+ static_cast<BMFlagLayer *>(
+ BLI_mempool_calloc(bm->etoolflagpool)) :
+ nullptr;
}
etable_dst[index] = e_dst;
@@ -1084,12 +1100,13 @@ void BM_mesh_rebuild(BMesh *bm,
BM_ITER_MESH_INDEX (f_src, &iter, bm, BM_FACES_OF_MESH, index) {
if (remap & BM_FACE) {
- BMFace *f_dst = BLI_mempool_alloc(fpool_dst);
+ BMFace *f_dst = static_cast<BMFace *>(BLI_mempool_alloc(fpool_dst));
memcpy(f_dst, f_src, sizeof(BMFace));
if (use_toolflags) {
((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ?
- BLI_mempool_calloc(bm->ftoolflagpool) :
- NULL;
+ static_cast<BMFlagLayer *>(
+ BLI_mempool_calloc(bm->ftoolflagpool)) :
+ nullptr;
}
ftable_dst[index] = f_dst;
@@ -1101,7 +1118,7 @@ void BM_mesh_rebuild(BMesh *bm,
BMLoop *l_iter_src, *l_first_src;
l_iter_src = l_first_src = BM_FACE_FIRST_LOOP((BMFace *)f_src);
do {
- BMLoop *l_dst = BLI_mempool_alloc(lpool_dst);
+ BMLoop *l_dst = static_cast<BMLoop *>(BLI_mempool_alloc(lpool_dst));
memcpy(l_dst, l_iter_src, sizeof(BMLoop));
ltable_dst[index_loop] = l_dst;
BM_elem_index_set(l_iter_src, index_loop++); /* set_ok */
@@ -1279,30 +1296,25 @@ void BM_mesh_toolflags_set(BMesh *bm, bool use_toolflags)
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_BM(bm);
- BLI_mempool *vpool_dst = NULL;
- BLI_mempool *epool_dst = NULL;
- BLI_mempool *fpool_dst = NULL;
+ BLI_mempool *vpool_dst = nullptr;
+ BLI_mempool *epool_dst = nullptr;
+ BLI_mempool *fpool_dst = nullptr;
- bm_mempool_init_ex(&allocsize, use_toolflags, &vpool_dst, &epool_dst, NULL, &fpool_dst);
+ bm_mempool_init_ex(&allocsize, use_toolflags, &vpool_dst, &epool_dst, nullptr, &fpool_dst);
if (use_toolflags == false) {
BLI_mempool_destroy(bm->vtoolflagpool);
BLI_mempool_destroy(bm->etoolflagpool);
BLI_mempool_destroy(bm->ftoolflagpool);
- bm->vtoolflagpool = NULL;
- bm->etoolflagpool = NULL;
- bm->ftoolflagpool = NULL;
+ bm->vtoolflagpool = nullptr;
+ bm->etoolflagpool = nullptr;
+ bm->ftoolflagpool = nullptr;
}
+ struct BMeshCreateParams params = {};
+ params.use_toolflags = use_toolflags;
- BM_mesh_rebuild(bm,
- &((struct BMeshCreateParams){
- .use_toolflags = use_toolflags,
- }),
- vpool_dst,
- epool_dst,
- NULL,
- fpool_dst);
+ BM_mesh_rebuild(bm, &params, vpool_dst, epool_dst, nullptr, fpool_dst);
bm->use_toolflags = use_toolflags;
}
@@ -1323,7 +1335,8 @@ void BM_mesh_vert_coords_get(BMesh *bm, float (*vert_coords)[3])
float (*BM_mesh_vert_coords_alloc(BMesh *bm, int *r_vert_len))[3]
{
- float(*vert_coords)[3] = MEM_mallocN(bm->totvert * sizeof(*vert_coords), __func__);
+ float(*vert_coords)[3] = static_cast<float(*)[3]>(
+ MEM_mallocN(bm->totvert * sizeof(*vert_coords), __func__));
BM_mesh_vert_coords_get(bm, vert_coords);
*r_vert_len = bm->totvert;
return vert_coords;
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index a5994b52bc2..d766a26cf6e 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -17,7 +17,7 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
struct BMeshCreateParams {
- bool use_toolflags : true;
+ bool use_toolflags : 1;
};
/**
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc
index 884f6b5388a..0bbf425971f 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc
@@ -83,7 +83,10 @@
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_task.hh"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -103,7 +106,9 @@ static CLG_LogRef LOG = {"bmesh.mesh.convert"};
using blender::Array;
using blender::IndexRange;
+using blender::MutableSpan;
using blender::Span;
+using blender::StringRef;
void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag)
{
@@ -121,17 +126,6 @@ void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag)
BLI_assert(bm->edata.totlayer == 0 || bm->edata.pool != nullptr);
BLI_assert(bm->pdata.totlayer == 0 || bm->pdata.pool != nullptr);
- if (cd_flag & ME_CDFLAG_VERT_BWEIGHT) {
- if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) {
- BM_data_layer_add(bm, &bm->vdata, CD_BWEIGHT);
- }
- }
- else {
- if (CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) {
- BM_data_layer_free(bm, &bm->vdata, CD_BWEIGHT);
- }
- }
-
if (cd_flag & ME_CDFLAG_VERT_CREASE) {
if (!CustomData_has_layer(&bm->vdata, CD_CREASE)) {
BM_data_layer_add(bm, &bm->vdata, CD_CREASE);
@@ -143,17 +137,6 @@ void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag)
}
}
- if (cd_flag & ME_CDFLAG_EDGE_BWEIGHT) {
- if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) {
- BM_data_layer_add(bm, &bm->edata, CD_BWEIGHT);
- }
- }
- else {
- if (CustomData_has_layer(&bm->edata, CD_BWEIGHT)) {
- BM_data_layer_free(bm, &bm->edata, CD_BWEIGHT);
- }
- }
-
if (cd_flag & ME_CDFLAG_EDGE_CREASE) {
if (!CustomData_has_layer(&bm->edata, CD_CREASE)) {
BM_data_layer_add(bm, &bm->edata, CD_CREASE);
@@ -169,15 +152,9 @@ void BM_mesh_cd_flag_apply(BMesh *bm, const char cd_flag)
char BM_mesh_cd_flag_from_bmesh(BMesh *bm)
{
char cd_flag = 0;
- if (CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) {
- cd_flag |= ME_CDFLAG_VERT_BWEIGHT;
- }
if (CustomData_has_layer(&bm->vdata, CD_CREASE)) {
cd_flag |= ME_CDFLAG_VERT_CREASE;
}
- if (CustomData_has_layer(&bm->edata, CD_BWEIGHT)) {
- cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
- }
if (CustomData_has_layer(&bm->edata, CD_CREASE)) {
cd_flag |= ME_CDFLAG_EDGE_CREASE;
}
@@ -212,10 +189,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
if (!me || !me->totvert) {
if (me && is_new) { /* No verts? still copy custom-data layout. */
- CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0);
- CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0);
- CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0);
- CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_CONSTRUCT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_CONSTRUCT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_CONSTRUCT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_CONSTRUCT, 0);
CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT);
CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE);
@@ -231,16 +208,16 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
}
if (is_new) {
- CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, 0);
- CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_CALLOC, 0);
- CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_CALLOC, 0);
- CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_CALLOC, 0);
+ CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_SET_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, 0);
+ CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, 0);
}
else {
- CustomData_bmesh_merge(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, bm, BM_VERT);
- CustomData_bmesh_merge(&me->edata, &bm->edata, mask.emask, CD_CALLOC, bm, BM_EDGE);
- CustomData_bmesh_merge(&me->ldata, &bm->ldata, mask.lmask, CD_CALLOC, bm, BM_LOOP);
- CustomData_bmesh_merge(&me->pdata, &bm->pdata, mask.pmask, CD_CALLOC, bm, BM_FACE);
+ CustomData_bmesh_merge(&me->vdata, &bm->vdata, mask.vmask, CD_SET_DEFAULT, bm, BM_VERT);
+ CustomData_bmesh_merge(&me->edata, &bm->edata, mask.emask, CD_SET_DEFAULT, bm, BM_EDGE);
+ CustomData_bmesh_merge(&me->ldata, &bm->ldata, mask.lmask, CD_SET_DEFAULT, bm, BM_LOOP);
+ CustomData_bmesh_merge(&me->pdata, &bm->pdata, mask.pmask, CD_SET_DEFAULT, bm, BM_FACE);
}
/* -------------------------------------------------------------------- */
@@ -337,12 +314,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Only copy these values over if the source mesh is flagged to be using them.
* Even if `bm` has these layers, they may have been added from another mesh, when `!is_new`. */
- const int cd_vert_bweight_offset = (me->cd_flag & ME_CDFLAG_VERT_BWEIGHT) ?
- CustomData_get_offset(&bm->vdata, CD_BWEIGHT) :
- -1;
- const int cd_edge_bweight_offset = (me->cd_flag & ME_CDFLAG_EDGE_BWEIGHT) ?
- CustomData_get_offset(&bm->edata, CD_BWEIGHT) :
- -1;
const int cd_edge_crease_offset = (me->cd_flag & ME_CDFLAG_EDGE_CREASE) ?
CustomData_get_offset(&bm->edata, CD_CREASE) :
-1;
@@ -352,7 +323,16 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) :
-1;
- Span<MVert> mvert{me->mvert, me->totvert};
+ const bool *hide_vert = (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert");
+ const bool *hide_edge = (const bool *)CustomData_get_layer_named(
+ &me->edata, CD_PROP_BOOL, ".hide_edge");
+ const bool *hide_poly = (const bool *)CustomData_get_layer_named(
+ &me->pdata, CD_PROP_BOOL, ".hide_poly");
+ const int *material_indices = (const int *)CustomData_get_layer_named(
+ &me->pdata, CD_PROP_INT32, "material_index");
+
+ Span<MVert> mvert = me->verts();
Array<BMVert *> vtable(me->totvert);
for (const int i : mvert.index_range()) {
BMVert *v = vtable[i] = BM_vert_create(
@@ -361,6 +341,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Transfer flag. */
v->head.hflag = BM_vert_flag_from_mflag(mvert[i].flag & ~SELECT);
+ if (hide_vert && hide_vert[i]) {
+ BM_elem_flag_enable(v, BM_ELEM_HIDDEN);
+ }
/* This is necessary for selection counts to work properly. */
if (mvert[i].flag & SELECT) {
@@ -374,10 +357,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Copy Custom Data */
CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true);
- if (cd_vert_bweight_offset != -1) {
- BM_ELEM_CD_SET_FLOAT(v, cd_vert_bweight_offset, (float)mvert[i].bweight / 255.0f);
- }
-
/* Set shape key original index. */
if (cd_shape_keyindex_offset != -1) {
BM_ELEM_CD_SET_INT(v, cd_shape_keyindex_offset, i);
@@ -395,7 +374,7 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
bm->elem_index_dirty &= ~BM_VERT; /* Added in order, clear dirty flag. */
}
- Span<MEdge> medge{me->medge, me->totedge};
+ const Span<MEdge> medge = me->edges();
Array<BMEdge *> etable(me->totedge);
for (const int i : medge.index_range()) {
BMEdge *e = etable[i] = BM_edge_create(
@@ -404,6 +383,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Transfer flags. */
e->head.hflag = BM_edge_flag_from_mflag(medge[i].flag & ~SELECT);
+ if (hide_edge && hide_edge[i]) {
+ BM_elem_flag_enable(e, BM_ELEM_HIDDEN);
+ }
/* This is necessary for selection counts to work properly. */
if (medge[i].flag & SELECT) {
@@ -413,9 +395,6 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Copy Custom Data */
CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data, true);
- if (cd_edge_bweight_offset != -1) {
- BM_ELEM_CD_SET_FLOAT(e, cd_edge_bweight_offset, (float)medge[i].bweight / 255.0f);
- }
if (cd_edge_crease_offset != -1) {
BM_ELEM_CD_SET_FLOAT(e, cd_edge_crease_offset, (float)medge[i].crease / 255.0f);
}
@@ -424,8 +403,8 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
bm->elem_index_dirty &= ~BM_EDGE; /* Added in order, clear dirty flag. */
}
- Span<MPoly> mpoly{me->mpoly, me->totpoly};
- Span<MLoop> mloop{me->mloop, me->totloop};
+ const Span<MPoly> mpoly = me->polys();
+ const Span<MLoop> mloop = me->loops();
/* Only needed for selection. */
@@ -457,13 +436,16 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar
/* Transfer flag. */
f->head.hflag = BM_face_flag_from_mflag(mpoly[i].flag & ~ME_FACE_SEL);
+ if (hide_poly && hide_poly[i]) {
+ BM_elem_flag_enable(f, BM_ELEM_HIDDEN);
+ }
/* This is necessary for selection counts to work properly. */
if (mpoly[i].flag & ME_FACE_SEL) {
BM_face_select_set(bm, f, true);
}
- f->mat_nr = mpoly[i].mat_nr;
+ f->mat_nr = material_indices == nullptr ? 0 : material_indices[i];
if (i == me->act_face) {
bm->act_face = f;
}
@@ -682,7 +664,7 @@ static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
*/
static void bm_to_mesh_shape(BMesh *bm,
Key *key,
- MVert *mvert,
+ MutableSpan<MVert> mvert,
const bool active_shapekey_to_mvert)
{
KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&key->block, bm->shapenr - 1));
@@ -902,17 +884,79 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
}
}
+template<typename T, typename GetFn>
+static void write_fn_to_attribute(blender::bke::MutableAttributeAccessor attributes,
+ const StringRef attribute_name,
+ const eAttrDomain domain,
+ const bool do_write,
+ const GetFn &get_fn)
+{
+ using namespace blender;
+ if (do_write) {
+ bke::SpanAttributeWriter<T> attribute = attributes.lookup_or_add_for_write_only_span<T>(
+ attribute_name, domain);
+ threading::parallel_for(attribute.span.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ attribute.span[i] = get_fn(i);
+ }
+ });
+ attribute.finish();
+ }
+ else {
+ /* To avoid overhead, remove the hide attribute if possible. */
+ attributes.remove(attribute_name);
+ }
+}
+
+static void assert_bmesh_has_no_mesh_only_attributes(const BMesh &bm)
+{
+ (void)bm; /* Unused in the release builds. */
+
+ /* The "hide" attributes are stored as flags on #BMesh. */
+ BLI_assert(CustomData_get_layer_named(&bm.vdata, CD_PROP_BOOL, ".hide_vert") == nullptr);
+ BLI_assert(CustomData_get_layer_named(&bm.edata, CD_PROP_BOOL, ".hide_edge") == nullptr);
+ BLI_assert(CustomData_get_layer_named(&bm.pdata, CD_PROP_BOOL, ".hide_poly") == nullptr);
+}
+
+static void convert_bmesh_hide_flags_to_mesh_attributes(BMesh &bm,
+ const bool need_hide_vert,
+ const bool need_hide_edge,
+ const bool need_hide_poly,
+ Mesh &mesh)
+{
+ using namespace blender;
+ /* The "hide" attributes are stored as flags on #BMesh. */
+ assert_bmesh_has_no_mesh_only_attributes(bm);
+
+ if (!(need_hide_vert || need_hide_edge || need_hide_poly)) {
+ return;
+ }
+
+ bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ BM_mesh_elem_table_ensure(&bm, BM_VERT | BM_EDGE | BM_FACE);
+
+ write_fn_to_attribute<bool>(
+ attributes, ".hide_vert", ATTR_DOMAIN_POINT, need_hide_vert, [&](const int i) {
+ return BM_elem_flag_test(BM_vert_at_index(&bm, i), BM_ELEM_HIDDEN);
+ });
+ write_fn_to_attribute<bool>(
+ attributes, ".hide_edge", ATTR_DOMAIN_EDGE, need_hide_edge, [&](const int i) {
+ return BM_elem_flag_test(BM_edge_at_index(&bm, i), BM_ELEM_HIDDEN);
+ });
+ write_fn_to_attribute<bool>(
+ attributes, ".hide_poly", ATTR_DOMAIN_FACE, need_hide_poly, [&](const int i) {
+ return BM_elem_flag_test(BM_face_at_index(&bm, i), BM_ELEM_HIDDEN);
+ });
+}
+
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params)
{
- MEdge *med;
BMVert *v, *eve;
BMEdge *e;
BMFace *f;
BMIter iter;
int i, j;
- const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
- const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
@@ -938,25 +982,25 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
{
CustomData_MeshMasks mask = CD_MASK_MESH;
CustomData_MeshMasks_update(&mask, &params->cd_mask_extra);
- CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert);
- CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge);
- CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop);
- CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly);
+ CustomData_copy_mesh_to_bmesh(&bm->vdata, &me->vdata, mask.vmask, CD_SET_DEFAULT, me->totvert);
+ CustomData_copy_mesh_to_bmesh(&bm->edata, &me->edata, mask.emask, CD_SET_DEFAULT, me->totedge);
+ CustomData_copy_mesh_to_bmesh(&bm->ldata, &me->ldata, mask.lmask, CD_SET_DEFAULT, me->totloop);
+ CustomData_copy_mesh_to_bmesh(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly);
}
- MVert *mvert = bm->totvert ? (MVert *)MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") :
- nullptr;
- MEdge *medge = bm->totedge ? (MEdge *)MEM_callocN(sizeof(MEdge) * bm->totedge, "bm_to_me.edge") :
- nullptr;
- MLoop *mloop = bm->totloop ? (MLoop *)MEM_callocN(sizeof(MLoop) * bm->totloop, "bm_to_me.loop") :
- nullptr;
- MPoly *mpoly = bm->totface ? (MPoly *)MEM_callocN(sizeof(MPoly) * bm->totface, "bm_to_me.poly") :
- nullptr;
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, me->totvert);
+ CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, me->totedge);
+ CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, me->totloop);
+ CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, me->totpoly);
+ MutableSpan<MVert> mvert = me->verts_for_write();
+ MutableSpan<MEdge> medge = me->edges_for_write();
+ MutableSpan<MPoly> mpoly = me->polys_for_write();
+ MutableSpan<MLoop> mloop = me->loops_for_write();
- CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert);
- CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge);
- CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop);
- CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly);
+ bool need_hide_vert = false;
+ bool need_hide_edge = false;
+ bool need_hide_poly = false;
+ bool need_material_index = false;
/* Clear normals on the mesh completely, since the original vertex and polygon count might be
* different than the BMesh's. */
@@ -964,55 +1008,48 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm);
- /* This is called again, 'dotess' arg is used there. */
- BKE_mesh_update_customdata_pointers(me, false);
-
i = 0;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
- copy_v3_v3(mvert->co, v->co);
+ copy_v3_v3(mvert[i].co, v->co);
- mvert->flag = BM_vert_flag_to_mflag(v);
+ mvert[i].flag = BM_vert_flag_to_mflag(v);
+ if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) {
+ need_hide_vert = true;
+ }
BM_elem_index_set(v, i); /* set_inline */
/* Copy over custom-data. */
CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i);
- if (cd_vert_bweight_offset != -1) {
- mvert->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(v, cd_vert_bweight_offset);
- }
-
i++;
- mvert++;
BM_CHECK_ELEMENT(v);
}
bm->elem_index_dirty &= ~BM_VERT;
- med = medge;
i = 0;
BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
- med->v1 = BM_elem_index_get(e->v1);
- med->v2 = BM_elem_index_get(e->v2);
+ medge[i].v1 = BM_elem_index_get(e->v1);
+ medge[i].v2 = BM_elem_index_get(e->v2);
- med->flag = BM_edge_flag_to_mflag(e);
+ medge[i].flag = BM_edge_flag_to_mflag(e);
+ if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) {
+ need_hide_edge = true;
+ }
BM_elem_index_set(e, i); /* set_inline */
/* Copy over custom-data. */
CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i);
- bmesh_quick_edgedraw_flag(med, e);
+ bmesh_quick_edgedraw_flag(&medge[i], e);
if (cd_edge_crease_offset != -1) {
- med->crease = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_crease_offset);
- }
- if (cd_edge_bweight_offset != -1) {
- med->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_bweight_offset);
+ medge[i].crease = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(e, cd_edge_crease_offset);
}
i++;
- med++;
BM_CHECK_ELEMENT(e);
}
bm->elem_index_dirty &= ~BM_EDGE;
@@ -1021,21 +1058,25 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
j = 0;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BMLoop *l_iter, *l_first;
- mpoly->loopstart = j;
- mpoly->totloop = f->len;
- mpoly->mat_nr = f->mat_nr;
- mpoly->flag = BM_face_flag_to_mflag(f);
+ mpoly[i].loopstart = j;
+ mpoly[i].totloop = f->len;
+ if (f->mat_nr != 0) {
+ need_material_index = true;
+ }
+ mpoly[i].flag = BM_face_flag_to_mflag(f);
+ if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) {
+ need_hide_poly = true;
+ }
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
- mloop->e = BM_elem_index_get(l_iter->e);
- mloop->v = BM_elem_index_get(l_iter->v);
+ mloop[j].e = BM_elem_index_get(l_iter->e);
+ mloop[j].v = BM_elem_index_get(l_iter->v);
/* Copy over custom-data. */
CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l_iter->head.data, j);
j++;
- mloop++;
BM_CHECK_ELEMENT(l_iter);
BM_CHECK_ELEMENT(l_iter->e);
BM_CHECK_ELEMENT(l_iter->v);
@@ -1049,10 +1090,17 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i);
i++;
- mpoly++;
BM_CHECK_ELEMENT(f);
}
+ if (need_material_index) {
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+ write_fn_to_attribute<int>(
+ me->attributes_for_write(), "material_index", ATTR_DOMAIN_FACE, true, [&](const int i) {
+ return static_cast<int>(BM_face_at_index(bm, i)->mat_nr);
+ });
+ }
+
/* Patch hook indices and vertex parents. */
if (params->calc_object_remap && (ototvert > 0)) {
BLI_assert(bmain != nullptr);
@@ -1117,7 +1165,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
}
- BKE_mesh_update_customdata_pointers(me, false);
+ convert_bmesh_hide_flags_to_mesh_attributes(
+ *bm, need_hide_vert, need_hide_edge, need_hide_poly, *me);
{
me->totselect = BLI_listbase_count(&(bm->selected));
@@ -1144,7 +1193,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
}
if (me->key) {
- bm_to_mesh_shape(bm, me->key, me->mvert, params->active_shapekey_to_mvert);
+ bm_to_mesh_shape(bm, me->key, mvert, params->active_shapekey_to_mvert);
}
/* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */
@@ -1165,8 +1214,12 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
BKE_mesh_runtime_clear_geometry(me);
}
+/* NOTE: The function is called from multiple threads with the same input BMesh and different
+ * mesh objects. */
void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *cd_mask_extra)
{
+ using namespace blender;
+
/* Must be an empty mesh. */
BLI_assert(me->totvert == 0);
BLI_assert(cd_mask_extra == nullptr || (cd_mask_extra->vmask & CD_MASK_SHAPEKEY) == 0);
@@ -1177,10 +1230,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
me->totloop = bm->totloop;
me->totpoly = bm->totface;
- CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, bm->totvert);
- CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, bm->totedge);
- CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, nullptr, bm->totloop);
- CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, nullptr, bm->totface);
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, bm->totvert);
+ CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, bm->totedge);
+ CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, bm->totloop);
+ CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, bm->totface);
/* Don't process shape-keys, we only feed them through the modifier stack as needed,
* e.g. for applying modifiers or the like. */
@@ -1189,25 +1242,22 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
CustomData_MeshMasks_update(&mask, cd_mask_extra);
}
mask.vmask &= ~CD_MASK_SHAPEKEY;
- CustomData_merge(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert);
- CustomData_merge(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge);
- CustomData_merge(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop);
- CustomData_merge(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly);
-
- BKE_mesh_update_customdata_pointers(me, false);
+ CustomData_merge(&bm->vdata, &me->vdata, mask.vmask, CD_SET_DEFAULT, me->totvert);
+ CustomData_merge(&bm->edata, &me->edata, mask.emask, CD_SET_DEFAULT, me->totedge);
+ CustomData_merge(&bm->ldata, &me->ldata, mask.lmask, CD_SET_DEFAULT, me->totloop);
+ CustomData_merge(&bm->pdata, &me->pdata, mask.pmask, CD_SET_DEFAULT, me->totpoly);
BMIter iter;
BMVert *eve;
BMEdge *eed;
BMFace *efa;
- MVert *mvert = me->mvert;
- MEdge *medge = me->medge;
- MLoop *mloop = me->mloop;
- MPoly *mpoly = me->mpoly;
+ MutableSpan<MVert> mvert = me->verts_for_write();
+ MutableSpan<MEdge> medge = me->edges_for_write();
+ MutableSpan<MPoly> mpoly = me->polys_for_write();
+ MutableSpan<MLoop> loops = me->loops_for_write();
+ MLoop *mloop = loops.data();
unsigned int i, j;
- const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
- const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
/* Clear normals on the mesh completely, since the original vertex and polygon count might be
@@ -1216,6 +1266,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
me->runtime.deformed_only = true;
+ bke::MutableAttributeAccessor mesh_attributes = me->attributes_for_write();
+
+ bke::SpanAttributeWriter<bool> hide_vert_attribute;
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
MVert *mv = &mvert[i];
@@ -1224,15 +1277,19 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
BM_elem_index_set(eve, i); /* set_inline */
mv->flag = BM_vert_flag_to_mflag(eve);
-
- if (cd_vert_bweight_offset != -1) {
- mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset);
+ if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ if (!hide_vert_attribute) {
+ hide_vert_attribute = mesh_attributes.lookup_or_add_for_write_span<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT);
+ }
+ hide_vert_attribute.span[i] = true;
}
CustomData_from_bmesh_block(&bm->vdata, &me->vdata, eve->head.data, i);
}
bm->elem_index_dirty &= ~BM_VERT;
+ bke::SpanAttributeWriter<bool> hide_edge_attribute;
BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) {
MEdge *med = &medge[i];
@@ -1242,6 +1299,13 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
med->v2 = BM_elem_index_get(eed->v2);
med->flag = BM_edge_flag_to_mflag(eed);
+ if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
+ if (!hide_edge_attribute) {
+ hide_edge_attribute = mesh_attributes.lookup_or_add_for_write_span<bool>(".hide_edge",
+ ATTR_DOMAIN_EDGE);
+ }
+ hide_edge_attribute.span[i] = true;
+ }
/* Handle this differently to editmode switching,
* only enable draw for single user edges rather than calculating angle. */
@@ -1254,15 +1318,14 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
if (cd_edge_crease_offset != -1) {
med->crease = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eed, cd_edge_crease_offset);
}
- if (cd_edge_bweight_offset != -1) {
- med->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eed, cd_edge_bweight_offset);
- }
CustomData_from_bmesh_block(&bm->edata, &me->edata, eed->head.data, i);
}
bm->elem_index_dirty &= ~BM_EDGE;
j = 0;
+ bke::SpanAttributeWriter<int> material_index_attribute;
+ bke::SpanAttributeWriter<bool> hide_poly_attribute;
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
BMLoop *l_iter;
BMLoop *l_first;
@@ -1272,8 +1335,22 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
mp->totloop = efa->len;
mp->flag = BM_face_flag_to_mflag(efa);
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ if (!hide_poly_attribute) {
+ hide_poly_attribute = mesh_attributes.lookup_or_add_for_write_span<bool>(".hide_poly",
+ ATTR_DOMAIN_FACE);
+ }
+ hide_poly_attribute.span[i] = true;
+ }
+
mp->loopstart = j;
- mp->mat_nr = efa->mat_nr;
+ if (efa->mat_nr != 0) {
+ if (!material_index_attribute) {
+ material_index_attribute = mesh_attributes.lookup_or_add_for_write_span<int>(
+ "material_index", ATTR_DOMAIN_FACE);
+ }
+ material_index_attribute.span[i] = efa->mat_nr;
+ }
l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
do {
@@ -1291,5 +1368,12 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks *
}
bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+ assert_bmesh_has_no_mesh_only_attributes(*bm);
+
+ material_index_attribute.finish();
+ hide_vert_attribute.finish();
+ hide_edge_attribute.finish();
+ hide_poly_attribute.finish();
+
me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm);
}
diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h
index e2871dc04d3..a04136afc1d 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_convert.h
+++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h
@@ -61,8 +61,8 @@ struct BMeshToMeshParams {
bool active_shapekey_to_mvert;
struct CustomData_MeshMasks cd_mask_extra;
};
+
/**
- *
* \param bmain: May be NULL in case \a calc_object_remap parameter option is not set.
*/
void BM_mesh_bm_to_me(struct Main *bmain,
diff --git a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
index 8d401ae1e5c..69510263ec9 100644
--- a/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
+++ b/source/blender/bmesh/intern/bmesh_mesh_tessellate.c
@@ -6,7 +6,7 @@
* This file contains code for polygon tessellation
* (creating triangles from polygons).
*
- * \see mesh_tessellate.c for the #Mesh equivalent of this file.
+ * \see mesh_tessellate.cc for the #Mesh equivalent of this file.
*/
#include "DNA_meshdata_types.h"
diff --git a/source/blender/bmesh/intern/bmesh_query.h b/source/blender/bmesh/intern/bmesh_query.h
index 85eadd3076a..9d690395d72 100644
--- a/source/blender/bmesh/intern/bmesh_query.h
+++ b/source/blender/bmesh/intern/bmesh_query.h
@@ -138,7 +138,6 @@ BMLoop *BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v) ATTR_WARN_
* +----------+ <-- This loop defines the face and vertex..
* l
* </pre>
- *
*/
BMLoop *BM_loop_other_vert_loop_by_edge(BMLoop *l, BMEdge *e) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL();
diff --git a/source/blender/bmesh/intern/bmesh_query_uv.c b/source/blender/bmesh/intern/bmesh_query_uv.cc
index 1225543cd06..5a725407c6b 100644
--- a/source/blender/bmesh/intern/bmesh_query_uv.c
+++ b/source/blender/bmesh/intern/bmesh_query_uv.cc
@@ -6,9 +6,10 @@
#include "MEM_guardedalloc.h"
-#include "BLI_alloca.h"
+#include "BLI_array.hh"
#include "BLI_linklist.h"
#include "BLI_math.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_utildefines_stack.h"
#include "BKE_customdata.h"
@@ -80,7 +81,7 @@ void BM_face_uv_calc_center_median(const BMFace *f, const int cd_loop_uv_offset,
zero_v2(r_cent);
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ const MLoopUV *luv = (const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
add_v2_v2(r_cent, luv->uv);
} while ((l_iter = l_iter->next) != l_first);
@@ -89,16 +90,16 @@ void BM_face_uv_calc_center_median(const BMFace *f, const int cd_loop_uv_offset,
float BM_face_uv_calc_cross(const BMFace *f, const int cd_loop_uv_offset)
{
- float(*uvs)[2] = BLI_array_alloca(uvs, f->len);
+ blender::Array<blender::float2, BM_DEFAULT_NGON_STACK_SIZE> uvs(f->len);
const BMLoop *l_iter;
const BMLoop *l_first;
int i = 0;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
- copy_v2_v2(uvs[i++], luv->uv);
+ const MLoopUV *luv = (const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ uvs[i++] = luv->uv;
} while ((l_iter = l_iter->next) != l_first);
- return cross_poly_v2(uvs, f->len);
+ return cross_poly_v2(reinterpret_cast<const float(*)[2]>(uvs.data()), f->len);
}
void BM_face_uv_minmax(const BMFace *f, float min[2], float max[2], const int cd_loop_uv_offset)
@@ -107,7 +108,7 @@ void BM_face_uv_minmax(const BMFace *f, float min[2], float max[2], const int cd
const BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ const MLoopUV *luv = (const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
minmax_v2v2_v2(min, max, luv->uv);
} while ((l_iter = l_iter->next) != l_first);
}
@@ -118,7 +119,7 @@ void BM_face_uv_transform(BMFace *f, const float matrix[2][2], const int cd_loop
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ MLoopUV *luv = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
mul_m2_v2(matrix, luv->uv);
} while ((l_iter = l_iter->next) != l_first);
}
@@ -126,12 +127,12 @@ void BM_face_uv_transform(BMFace *f, const float matrix[2][2], const int cd_loop
bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
{
BLI_assert(l_a->e == l_b->e);
- MLoopUV *luv_a_curr = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
- MLoopUV *luv_a_next = BM_ELEM_CD_GET_VOID_P(l_a->next, cd_loop_uv_offset);
- MLoopUV *luv_b_curr = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset);
- MLoopUV *luv_b_next = BM_ELEM_CD_GET_VOID_P(l_b->next, cd_loop_uv_offset);
+ MLoopUV *luv_a_curr = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
+ MLoopUV *luv_a_next = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_a->next, cd_loop_uv_offset);
+ MLoopUV *luv_b_curr = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset);
+ MLoopUV *luv_b_next = (MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_b->next, cd_loop_uv_offset);
if (l_a->v != l_b->v) {
- SWAP(MLoopUV *, luv_b_curr, luv_b_next);
+ std::swap(luv_b_curr, luv_b_next);
}
return (equals_v2v2(luv_a_curr->uv, luv_b_curr->uv) &&
equals_v2v2(luv_a_next->uv, luv_b_next->uv));
@@ -140,8 +141,8 @@ bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_
bool BM_loop_uv_share_vert_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
{
BLI_assert(l_a->v == l_b->v);
- const MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
- const MLoopUV *luv_b = BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset);
+ const MLoopUV *luv_a = (const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
+ const MLoopUV *luv_b = (const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_b, cd_loop_uv_offset);
if (!equals_v2v2(luv_a->uv, luv_b->uv)) {
return false;
}
@@ -160,8 +161,10 @@ bool BM_edge_uv_share_vert_check(BMEdge *e, BMLoop *l_a, BMLoop *l_b, const int
const BMLoop *l_other_b = BM_loop_other_vert_loop_by_edge(l_b, e);
{
- const MLoopUV *luv_other_a = BM_ELEM_CD_GET_VOID_P(l_other_a, cd_loop_uv_offset);
- const MLoopUV *luv_other_b = BM_ELEM_CD_GET_VOID_P(l_other_b, cd_loop_uv_offset);
+ const MLoopUV *luv_other_a = (const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_other_a,
+ cd_loop_uv_offset);
+ const MLoopUV *luv_other_b = (const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_other_b,
+ cd_loop_uv_offset);
if (!equals_v2v2(luv_other_a->uv, luv_other_b->uv)) {
return false;
}
@@ -172,7 +175,7 @@ bool BM_edge_uv_share_vert_check(BMEdge *e, BMLoop *l_a, BMLoop *l_b, const int
bool BM_face_uv_point_inside_test(const BMFace *f, const float co[2], const int cd_loop_uv_offset)
{
- float(*projverts)[2] = BLI_array_alloca(projverts, f->len);
+ blender::Array<blender::float2, BM_DEFAULT_NGON_STACK_SIZE> projverts(f->len);
BMLoop *l_iter;
int i;
@@ -180,8 +183,9 @@ bool BM_face_uv_point_inside_test(const BMFace *f, const float co[2], const int
BLI_assert(BM_face_is_normal_valid(f));
for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l_iter = l_iter->next) {
- copy_v2_v2(projverts[i], BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset));
+ projverts[i] = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset))->uv;
}
- return isect_point_poly_v2(co, projverts, f->len, false);
+ return isect_point_poly_v2(
+ co, reinterpret_cast<const float(*)[2]>(projverts.data()), f->len, false);
}
diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c
index 6baaeb43f1a..7d340f02f2e 100644
--- a/source/blender/bmesh/intern/bmesh_structure.c
+++ b/source/blender/bmesh/intern/bmesh_structure.c
@@ -182,9 +182,8 @@ void bmesh_disk_edge_remove(BMEdge *e, BMVert *v)
BMEdge *bmesh_disk_edge_exists(const BMVert *v1, const BMVert *v2)
{
- BMEdge *e_iter, *e_first;
-
if (v1->e) {
+ BMEdge *e_iter, *e_first;
e_first = e_iter = v1->e;
do {
diff --git a/source/blender/bmesh/operators/bmo_connect.c b/source/blender/bmesh/operators/bmo_connect.c
index 42de425103e..59a12db9241 100644
--- a/source/blender/bmesh/operators/bmo_connect.c
+++ b/source/blender/bmesh/operators/bmo_connect.c
@@ -18,7 +18,7 @@
#define VERT_INPUT 1
#define EDGE_OUT 1
-/* Edge spans 2 VERT_INPUT's, its a nop,
+/* Edge spans 2 VERT_INPUT's, its a NOP,
* but include in "edges.out" */
#define EDGE_OUT_ADJ 2
diff --git a/source/blender/bmesh/operators/bmo_connect_nonplanar.c b/source/blender/bmesh/operators/bmo_connect_nonplanar.c
index ac88ffb9065..8112844fc8a 100644
--- a/source/blender/bmesh/operators/bmo_connect_nonplanar.c
+++ b/source/blender/bmesh/operators/bmo_connect_nonplanar.c
@@ -24,7 +24,7 @@
static float bm_face_subset_calc_planar(BMLoop *l_first, BMLoop *l_last, const float no[3])
{
float axis_mat[3][3];
- float z_prev, z_curr;
+ float z_prev;
float delta_z = 0.0f;
/* Newell's Method */
@@ -35,7 +35,7 @@ static float bm_face_subset_calc_planar(BMLoop *l_first, BMLoop *l_last, const f
z_prev = dot_m3_v3_row_z(axis_mat, l_last->v->co);
do {
- z_curr = dot_m3_v3_row_z(axis_mat, l_iter->v->co);
+ const float z_curr = dot_m3_v3_row_z(axis_mat, l_iter->v->co);
delta_z += fabsf(z_curr - z_prev);
z_prev = z_curr;
} while ((l_iter = l_iter->next) != l_term);
diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c
index e91dab3dd6f..26f1a9e626e 100644
--- a/source/blender/bmesh/operators/bmo_connect_pair.c
+++ b/source/blender/bmesh/operators/bmo_connect_pair.c
@@ -83,7 +83,7 @@ typedef struct PathContext {
/* only to access BMO flags */
BMesh *bm_bmoflag;
- BMVert *v_a, *v_b;
+ BMVert *v_pair[2];
BLI_mempool *link_pool;
} PathContext;
@@ -593,17 +593,17 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op)
}
pc.bm_bmoflag = bm;
- pc.v_a = ((BMVert **)op_verts_slot->data.p)[0];
- pc.v_b = ((BMVert **)op_verts_slot->data.p)[1];
+ pc.v_pair[0] = ((BMVert **)op_verts_slot->data.p)[0];
+ pc.v_pair[1] = ((BMVert **)op_verts_slot->data.p)[1];
/* fail! */
- if (!(pc.v_a && pc.v_b)) {
+ if (!(pc.v_pair[0] && pc.v_pair[1])) {
return;
}
#ifdef DEBUG_PRINT
- printf("%s: v_a: %d\n", __func__, BM_elem_index_get(pc.v_a));
- printf("%s: v_b: %d\n", __func__, BM_elem_index_get(pc.v_b));
+ printf("%s: v_pair[0]: %d\n", __func__, BM_elem_index_get(pc.v_pair[0]));
+ printf("%s: v_pair[1]: %d\n", __func__, BM_elem_index_get(pc.v_pair[1]));
#endif
/* tag so we won't touch ever (typically hidden faces) */
@@ -618,15 +618,15 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op)
/* calculate matrix */
{
- bm_vert_pair_to_matrix(&pc.v_a, pc.matrix);
- pc.axis_sep = dot_m3_v3_row_x(pc.matrix, pc.v_a->co);
+ bm_vert_pair_to_matrix(pc.v_pair, pc.matrix);
+ pc.axis_sep = dot_m3_v3_row_x(pc.matrix, pc.v_pair[0]->co);
}
/* add first vertex */
{
PathLinkState *state;
state = MEM_callocN(sizeof(*state), __func__);
- state_link_add(&pc, state, (BMElem *)pc.v_a, NULL);
+ state_link_add(&pc, state, (BMElem *)pc.v_pair[0], NULL);
BLI_heapsimple_insert(pc.states, state->dist, state);
}
@@ -642,7 +642,7 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op)
/* either we insert this into 'pc.states' or its freed */
bool continue_search;
- if (state->link_last->ele == (BMElem *)pc.v_b) {
+ if (state->link_last->ele == (BMElem *)pc.v_pair[1]) {
/* pass, wait until all are found */
#ifdef DEBUG_PRINT
printf("%s: state %p loop found %.4f\n", __func__, state, state->dist);
@@ -698,8 +698,8 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op)
} while ((link = link->next));
}
- BMO_vert_flag_enable(bm, pc.v_a, VERT_OUT);
- BMO_vert_flag_enable(bm, pc.v_b, VERT_OUT);
+ BMO_vert_flag_enable(bm, pc.v_pair[0], VERT_OUT);
+ BMO_vert_flag_enable(bm, pc.v_pair[1], VERT_OUT);
BLI_mempool_destroy(pc.link_pool);
diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c
index a809fe6ee3d..9dba48a8961 100644
--- a/source/blender/bmesh/operators/bmo_create.c
+++ b/source/blender/bmesh/operators/bmo_create.c
@@ -77,7 +77,6 @@ void bmo_contextual_create_exec(BMesh *bm, BMOperator *op)
* | .
* | .
* +........+ <-- starts out free standing.
- *
*/
/* Here we check for consistency and create 2 edges */
diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c
index 1339efb3057..ab74aa2e1bb 100644
--- a/source/blender/bmesh/operators/bmo_join_triangles.c
+++ b/source/blender/bmesh/operators/bmo_join_triangles.c
@@ -22,7 +22,9 @@
#include "intern/bmesh_operators_private.h" /* own include */
-/* assumes edges are validated before reaching this poin */
+/**
+ * \note Assumes edges are validated before reaching this point.
+ */
static float quad_calc_error(const float v1[3],
const float v2[3],
const float v3[3],
diff --git a/source/blender/bmesh/operators/bmo_poke.c b/source/blender/bmesh/operators/bmo_poke.c
index 7ff3b9b072c..6622dbf7575 100644
--- a/source/blender/bmesh/operators/bmo_poke.c
+++ b/source/blender/bmesh/operators/bmo_poke.c
@@ -52,8 +52,7 @@ void bmo_poke_exec(BMesh *bm, BMOperator *op)
}
BMO_ITER (f, &oiter, op->slots_in, "faces", BM_FACE) {
- BMFace *f_new;
- float f_center[3], f_center_mean[3];
+ float f_center[3];
BMVert *v_center = NULL;
BMLoop *l_iter, *l_first;
/* only interpolate the central loop from the face once,
@@ -69,15 +68,6 @@ void bmo_poke_exec(BMesh *bm, BMOperator *op)
v_center = BM_vert_create(bm, f_center, NULL, BM_CREATE_NOP);
BMO_vert_flag_enable(bm, v_center, ELE_NEW);
- if (cd_loop_mdisp_offset != -1) {
- if (center_mode == BMOP_POKE_MEDIAN) {
- copy_v3_v3(f_center_mean, f_center);
- }
- else {
- BM_face_calc_center_median(f, f_center_mean);
- }
- }
-
/* handled by BM_loop_interp_from_face */
// BM_vert_interp_from_face(bm, v_center, f);
@@ -92,8 +82,7 @@ void bmo_poke_exec(BMesh *bm, BMOperator *op)
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
BMLoop *l_new;
-
- f_new = BM_face_create_quad_tri(
+ BMFace *f_new = BM_face_create_quad_tri(
bm, l_iter->v, l_iter->next->v, v_center, NULL, f, BM_CREATE_NOP);
l_new = BM_FACE_FIRST_LOOP(f_new);
diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c
index 432c7590f3c..c60ffbedb94 100644
--- a/source/blender/bmesh/operators/bmo_primitive.c
+++ b/source/blender/bmesh/operators/bmo_primitive.c
@@ -855,12 +855,12 @@ void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
/* one segment first */
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);
+ float sin_phi, cos_phi;
+ sin_cos_from_fraction(a, 2 * tot, &sin_phi, &cos_phi);
- vec[0] = 0.0;
- vec[1] = rad * sinf(phi);
- vec[2] = rad * cosf(phi);
+ vec[0] = 0.0f;
+ vec[1] = rad * sin_phi;
+ vec[2] = rad * cos_phi;
eve = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP);
BMO_vert_flag_enable(bm, eve, VERT_MARK);
@@ -1262,11 +1262,9 @@ void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
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);
+ sin_cos_from_fraction(a, segs, &vec[0], &vec[1]);
+ vec[0] *= -radius;
+ vec[1] *= radius;
vec[2] = 0.0f;
mul_m4_v3(mat, vec);
v1 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP);
@@ -1393,16 +1391,18 @@ void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
BMFace **side_faces = MEM_mallocN(sizeof(*side_faces) * side_faces_len, __func__);
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] = rad1 * sinf(phi);
- vec[1] = rad1 * cosf(phi);
+ /* Calculate with higher precision, see: T87779. */
+ float sin_phi, cos_phi;
+ sin_cos_from_fraction(i, segs, &sin_phi, &cos_phi);
+
+ vec[0] = rad1 * sin_phi;
+ vec[1] = rad1 * cos_phi;
vec[2] = -depth_half;
mul_m4_v3(mat, vec);
v1 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP);
- vec[0] = rad2 * sinf(phi);
- vec[1] = rad2 * cosf(phi);
+ vec[0] = rad2 * sin_phi;
+ vec[1] = rad2 * cos_phi;
vec[2] = depth_half;
mul_m4_v3(mat, vec);
v2 = BM_vert_create(bm, vec, NULL, BM_CREATE_NOP);
diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c
index e3275555b86..99ca49f3d4f 100644
--- a/source/blender/bmesh/operators/bmo_removedoubles.c
+++ b/source/blender/bmesh/operators/bmo_removedoubles.c
@@ -657,10 +657,10 @@ static void bmesh_find_doubles_common(BMesh *bm,
for (int i = 0; i < verts_len; i++) {
BMVert *v_check = verts[i];
if (duplicates[i] == -1) {
- /* nop (others can use as target) */
+ /* NOP (others can use as target). */
}
else if (duplicates[i] == i) {
- /* keep (others can use as target) */
+ /* Keep (others can use as target). */
}
else {
BMVert *v_other = verts[duplicates[i]];
diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c
index d6c64809e13..6b2df40549d 100644
--- a/source/blender/bmesh/operators/bmo_subdivide.c
+++ b/source/blender/bmesh/operators/bmo_subdivide.c
@@ -86,7 +86,7 @@ typedef struct SubDPattern {
* split the edge only?
*/
-/* flags for all elements share a common bitfield space */
+/** Flags for all elements share a common bit-field space. */
#define SUBD_SPLIT 1
#define EDGE_PERCENT 2
@@ -966,7 +966,7 @@ void bmo_subdivide_edges_exec(BMesh *bm, BMOperator *op)
patterns[5] = NULL;
}
- /* add a temporary shapekey layer to store displacements on current geometry */
+ /* Add a temporary shape-key layer to store displacements on current geometry. */
BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY);
bmo_subd_init_shape_info(bm, &params);
diff --git a/source/blender/bmesh/operators/bmo_subdivide_edgering.c b/source/blender/bmesh/operators/bmo_subdivide_edgering.c
index 3d2f5947539..6f41da7bb43 100644
--- a/source/blender/bmesh/operators/bmo_subdivide_edgering.c
+++ b/source/blender/bmesh/operators/bmo_subdivide_edgering.c
@@ -42,7 +42,8 @@
#define FACE_IN_STACK (1 << 2)
/* -------------------------------------------------------------------- */
-/* Specialized Utility Funcs */
+/** \name Specialized Utility Functions
+ * \{ */
#ifndef NDEBUG
static uint bm_verts_tag_count(BMesh *bm)
@@ -183,9 +184,13 @@ finally:
return has_overlap;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Edge Loop Pairs */
-/* key (ordered loop pointers) */
+/** \name Edge Loop Pairs
+ *
+ * key (ordered loop pointers).
+ * \{ */
static GSet *bm_edgering_pair_calc(BMesh *bm, ListBase *eloops_rim)
{
/**
@@ -258,8 +263,11 @@ static GSet *bm_edgering_pair_calc(BMesh *bm, ListBase *eloops_rim)
return eloop_pair_gs;
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Subdivide an edge 'n' times and return an open edgeloop */
+/** \name Subdivide an edge 'n' times and return an open edgeloop
+ * \{ */
static void bm_edge_subdiv_as_loop(
BMesh *bm, ListBase *eloops, BMEdge *e, BMVert *v_a, const int cuts)
@@ -290,8 +298,11 @@ static void bm_edge_subdiv_as_loop(
BLI_addtail(eloops, eloop);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* LoopPair Cache (struct and util funcs) */
+/** \name Loop Pair Cache (struct and utilities functions)
+ * \{ */
/**
* Use for finding spline handle direction from surrounding faces.
@@ -510,8 +521,11 @@ static void bm_edgering_pair_store_free(LoopPairStore *lpair, const int interp_m
MEM_freeN(lpair);
}
+/** \} */
+
/* -------------------------------------------------------------------- */
-/* Interpolation Function */
+/** \name Interpolation Function
+ * \{ */
static void bm_edgering_pair_interpolate(BMesh *bm,
LoopPairStore *lpair,
@@ -1216,3 +1230,5 @@ cleanup:
BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, FACE_OUT);
}
}
+
+/** \} */
diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c
index 309ef2cf21e..d88f3112a71 100644
--- a/source/blender/bmesh/operators/bmo_utils.c
+++ b/source/blender/bmesh/operators/bmo_utils.c
@@ -470,7 +470,7 @@ void bmo_rotate_uvs_exec(BMesh *bm, BMOperator *op)
BMLoop *lf; /* current face loops */
MLoopUV *f_luv; /* first face loop uv */
float p_uv[2]; /* previous uvs */
- float t_uv[2]; /* tmp uvs */
+ float t_uv[2]; /* temp uvs */
int n = 0;
BM_ITER_ELEM (lf, &l_iter, fs, BM_LOOPS_OF_FACE) {
@@ -603,7 +603,7 @@ void bmo_rotate_colors_exec(BMesh *bm, BMOperator *op)
const size_t size = cd_loop_color_type == CD_PROP_COLOR ? sizeof(MPropCol) : sizeof(MLoopCol);
void *p_col; /* previous color */
- void *t_col = alloca(size); /* tmp color */
+ void *t_col = alloca(size); /* Temp color. */
BMO_ITER (fs, &fs_iter, op->slots_in, "faces", BM_FACE) {
if (use_ccw == false) { /* same loops direction */
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index fa852cdd6da..eaf697e78f7 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -437,16 +437,6 @@ static bool nearly_parallel_normalized(const float d1[3], const float d2[3])
return compare_ff(fabsf(direction_dot), 1.0f, BEVEL_EPSILON_ANG_DOT);
}
-/**
- * calculate the determinant of a matrix formed by three vectors
- * \return dot(a, cross(b, c)) = determinant(a, b, c)
- */
-static float determinant_v3v3v3(const float a[3], const float b[3], const float c[3])
-{
- return a[0] * b[1] * c[2] + a[1] * b[2] * c[0] + a[2] * b[0] * c[1] - a[0] * b[2] * c[1] -
- a[1] * b[0] * c[2] - a[2] * b[1] * c[0];
-}
-
/* Make a new BoundVert of the given kind, inserting it at the end of the circular linked
* list with entry point bv->boundstart, and return it. */
static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float co[3])
@@ -4134,114 +4124,44 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
VMesh *vm_out = new_adj_vmesh(bp->mem_arena, n_boundary, ns_out, vm_in->boundstart);
/* First we adjust the boundary vertices of the input mesh, storing in output mesh. */
- BoundVert *bndv = vm_in->boundstart;
for (int i = 0; i < n_boundary; i++) {
- float co1[3], co2[3], acc[3];
- EdgeHalf *e = bndv->elast;
- /* Generate tangents. This is hacked together and would ideally be done elsewhere and then only
- * used here. */
- float tangent[3], tangent2[3], normal[3];
- bool convex = true;
- bool orthogonal = false;
- float stretch = 0.0f;
- if (e) {
- /* Projection direction is direction of the edge. */
- sub_v3_v3v3(tangent, e->e->v1->co, e->e->v2->co);
- if (e->is_rev) {
- negate_v3(tangent);
- }
- normalize_v3(tangent);
- if (bndv->is_arc_start || bndv->is_patch_start) {
- BMFace *face = e->fnext;
- if (face) {
- copy_v3_v3(normal, face->no);
- }
- else {
- zero_v3(normal);
- }
- madd_v3_v3v3fl(co2, bndv->profile.middle, normal, 0.1f);
- }
- if (bndv->is_arc_start || bp->affect_type == BEVEL_AFFECT_VERTICES) {
- EdgeHalf *e1 = bndv->next->elast;
- BLI_assert(e1);
- sub_v3_v3v3(tangent2, e1->e->v1->co, e1->e->v2->co);
- if (e1->is_rev) {
- negate_v3(tangent2);
- }
- normalize_v3(tangent2);
-
- convex = determinant_v3v3v3(tangent2, tangent, normal) < 0;
-
- add_v3_v3(tangent2, tangent);
- normalize_v3(tangent2);
- copy_v3_v3(tangent, tangent2);
- }
- /* Calculate a factor which determines how much the interpolated mesh is
- * going to be stretched out into the direction of the tangent.
- * It is currently using the difference along the tangent of the
- * central point on the profile and the current center vertex position. */
- get_profile_point(bp, &bndv->profile, ns_in2, ns_in, co);
- stretch = dot_v3v3(tangent, mesh_vert(vm_in, i, ns_in2, ns_in2)->co) - dot_v3v3(tangent, co);
- stretch = fabsf(stretch);
- /* Scale the tangent by stretch. The divide by ns_in2 comes from the Levin Paper. */
- mul_v3_fl(tangent, stretch / ns_in2);
- orthogonal = bndv->is_patch_start;
- }
- else if (bndv->prev->is_patch_start) {
- /* If this is the second edge of a patch and therefore #e is NULL,
- * then e->fprev has to be used/not NULL. */
- BLI_assert(bndv->prev->elast);
- BMFace *face = bndv->prev->elast->fnext;
- if (face) {
- copy_v3_v3(normal, face->no);
- }
- else {
- zero_v3(normal);
- }
- orthogonal = true;
- }
- else {
- /** Should only come here from make_cube_corner_adj_vmesh. */
- sub_v3_v3v3(co1, mesh_vert(vm_in, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 1)->co);
- sub_v3_v3v3(co2, mesh_vert(vm_in, i, 0, 1)->co, mesh_vert(vm_in, i, 0, 2)->co);
- cross_v3_v3v3(tangent, co1, co2);
- /** The following constant is chosen to best match the old results. */
- normalize_v3_length(tangent, 1.5f / ns_out);
- }
- /** Copy corner vertex. */
copy_v3_v3(mesh_vert(vm_out, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 0)->co);
- /** Copy the rest of the boundary vertices. */
for (int k = 1; k < ns_in; k++) {
copy_v3_v3(co, mesh_vert(vm_in, i, 0, k)->co);
- copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co);
- copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co);
-
- add_v3_v3v3(acc, co1, co2);
- if (bndv->is_arc_start) {
- sub_v3_v3(co1, co);
- sub_v3_v3(co2, co);
- normalize_v3(co1);
- normalize_v3(co2);
- add_v3_v3v3(tangent, co1, co2);
- /* This is an empirical formula to make the result look good. */
- normalize_v3(tangent);
- float dot = convex ? fminf(0, dot_v3v3(tangent2, tangent)) : 1.0f;
- mul_v3_fl(tangent, stretch / ns_in * dot);
- }
- else if (orthogonal) {
- sub_v3_v3(co1, co);
- cross_v3_v3v3(tangent, normal, co1);
- /* This is an empirical formula to make the result look good. */
- normalize_v3_length(tangent, -bp->offset * 0.7071f / ns_in);
- }
- mul_v3_fl(co, 2.0f);
- madd_v3_v3fl(co, acc, -0.25f);
- madd_v3_v3fl(co, mesh_vert(vm_in, i, 1, k)->co, -0.5f);
- add_v3_v3(co, tangent);
+ /* Smooth boundary rule. Custom profiles shouldn't be smoothed. */
+ if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
+ float co1[3], co2[3], acc[3];
+ copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co);
+ copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co);
+
+ add_v3_v3v3(acc, co1, co2);
+ madd_v3_v3fl(acc, co, -2.0f);
+ madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ }
copy_v3_v3(mesh_vert_canon(vm_out, i, 0, 2 * k)->co, co);
}
+ }
+ /* Now adjust odd boundary vertices in output mesh, based on even ones. */
+ BoundVert *bndv = vm_out->boundstart;
+ for (int i = 0; i < n_boundary; i++) {
+ for (int k = 1; k < ns_out; k += 2) {
+ get_profile_point(bp, &bndv->profile, k, ns_out, co);
+
+ /* Smooth if using a non-custom profile. */
+ if (bp->profile_type != BEVEL_PROFILE_CUSTOM) {
+ float co1[3], co2[3], acc[3];
+ copy_v3_v3(co1, mesh_vert_canon(vm_out, i, 0, k - 1)->co);
+ copy_v3_v3(co2, mesh_vert_canon(vm_out, i, 0, k + 1)->co);
+
+ add_v3_v3v3(acc, co1, co2);
+ madd_v3_v3fl(acc, co, -2.0f);
+ madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ }
+
+ copy_v3_v3(mesh_vert_canon(vm_out, i, 0, k)->co, co);
+ }
bndv = bndv->next;
}
vmesh_copy_equiv_verts(vm_out);
@@ -4249,7 +4169,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
/* Copy adjusted verts back into vm_in. */
for (int i = 0; i < n_boundary; i++) {
for (int k = 0; k < ns_in; k++) {
- copy_v3_v3(mesh_vert_canon(vm_in, i, 0, k)->co, mesh_vert_canon(vm_out, i, 0, 2 * k)->co);
+ copy_v3_v3(mesh_vert(vm_in, i, 0, k)->co, mesh_vert(vm_out, i, 0, 2 * k)->co);
}
}
@@ -4334,7 +4254,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
vmesh_copy_equiv_verts(vm_out);
/* The center vertex is special. */
- gamma = sabin_gamma(n_boundary) * 0.5f;
+ gamma = sabin_gamma(n_boundary);
beta = -gamma;
/* Accumulate edge verts in co1, face verts in co2. */
float co1[3], co2[3];
@@ -7163,7 +7083,7 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
}
}
- /* Fix UVs along end edge joints. A nop unless other side built already. */
+ /* Fix UVs along end edge joints. A NOP unless other side built already. */
/* TODO: If some seam, may want to do selective merge. */
if (!bv1->any_seam && bv1->vmesh->mesh_kind == M_NONE) {
bev_merge_end_uvs(bm, bv1, e1);
diff --git a/source/blender/bmesh/tools/bmesh_path_region_uv.c b/source/blender/bmesh/tools/bmesh_path_region_uv.c
index 5c70f7fa5ec..56090ed9916 100644
--- a/source/blender/bmesh/tools/bmesh_path_region_uv.c
+++ b/source/blender/bmesh/tools/bmesh_path_region_uv.c
@@ -109,9 +109,10 @@ static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int
static LinkNode *mesh_calc_path_region_elem(BMesh *bm,
BMElem *ele_src,
BMElem *ele_dst,
- const uint cd_loop_uv_offset,
+ const int cd_loop_uv_offset,
const char path_htype)
{
+ BLI_assert(cd_loop_uv_offset >= 0);
int ele_loops_len[2];
BMLoop **ele_loops[2];
@@ -397,7 +398,7 @@ static LinkNode *mesh_calc_path_region_elem(BMesh *bm,
LinkNode *BM_mesh_calc_path_uv_region_vert(BMesh *bm,
BMElem *ele_src,
BMElem *ele_dst,
- const uint cd_loop_uv_offset,
+ const int cd_loop_uv_offset,
bool (*filter_fn)(BMLoop *, void *user_data),
void *user_data)
{
@@ -426,7 +427,7 @@ LinkNode *BM_mesh_calc_path_uv_region_vert(BMesh *bm,
LinkNode *BM_mesh_calc_path_uv_region_edge(BMesh *bm,
BMElem *ele_src,
BMElem *ele_dst,
- const uint cd_loop_uv_offset,
+ const int cd_loop_uv_offset,
bool (*filter_fn)(BMLoop *, void *user_data),
void *user_data)
{
@@ -455,7 +456,7 @@ LinkNode *BM_mesh_calc_path_uv_region_edge(BMesh *bm,
LinkNode *BM_mesh_calc_path_uv_region_face(BMesh *bm,
BMElem *ele_src,
BMElem *ele_dst,
- const uint cd_loop_uv_offset,
+ const int cd_loop_uv_offset,
bool (*filter_fn)(BMFace *, void *user_data),
void *user_data)
{
diff --git a/source/blender/bmesh/tools/bmesh_path_region_uv.h b/source/blender/bmesh/tools/bmesh_path_region_uv.h
index fa1b2bfcf9b..f399395e051 100644
--- a/source/blender/bmesh/tools/bmesh_path_region_uv.h
+++ b/source/blender/bmesh/tools/bmesh_path_region_uv.h
@@ -9,7 +9,7 @@
struct LinkNode *BM_mesh_calc_path_uv_region_vert(BMesh *bm,
BMElem *ele_src,
BMElem *ele_dst,
- uint cd_loop_uv_offset,
+ int cd_loop_uv_offset,
bool (*filter_fn)(BMLoop *, void *user_data),
void *user_data) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL(1, 2, 3);
@@ -17,7 +17,7 @@ struct LinkNode *BM_mesh_calc_path_uv_region_vert(BMesh *bm,
struct LinkNode *BM_mesh_calc_path_uv_region_edge(BMesh *bm,
BMElem *ele_src,
BMElem *ele_dst,
- uint cd_loop_uv_offset,
+ int cd_loop_uv_offset,
bool (*filter_fn)(BMLoop *, void *user_data),
void *user_data) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL(1, 2, 3);
@@ -25,7 +25,7 @@ struct LinkNode *BM_mesh_calc_path_uv_region_edge(BMesh *bm,
struct LinkNode *BM_mesh_calc_path_uv_region_face(BMesh *bm,
BMElem *ele_src,
BMElem *ele_dst,
- uint cd_loop_uv_offset,
+ int cd_loop_uv_offset,
bool (*filter_fn)(BMFace *, void *user_data),
void *user_data) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL(1, 2, 3);
diff --git a/source/blender/bmesh/tools/bmesh_path_uv.c b/source/blender/bmesh/tools/bmesh_path_uv.c
index 76697f51ac7..6531677fce6 100644
--- a/source/blender/bmesh/tools/bmesh_path_uv.c
+++ b/source/blender/bmesh/tools/bmesh_path_uv.c
@@ -47,9 +47,7 @@ static float step_cost_3_v2_ex(
return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v2v2(d1, d2)))));
}
-static float UNUSED_FUNCTION(step_cost_3_v2)(const float v1[2],
- const float v2[2],
- const float v3[2])
+static float step_cost_3_v2(const float v1[2], const float v2[2], const float v3[2])
{
return step_cost_3_v2_ex(v1, v2, v3, false, false);
}
@@ -60,14 +58,14 @@ static float UNUSED_FUNCTION(step_cost_3_v2)(const float v1[2],
/** \name BM_mesh_calc_path_uv_vert
* \{ */
-static void looptag_add_adjacent_uv(HeapSimple *heap,
+static void verttag_add_adjacent_uv(HeapSimple *heap,
BMLoop *l_a,
BMLoop **loops_prev,
float *cost,
const struct BMCalcPathUVParams *params)
{
BLI_assert(params->aspect_y != 0.0f);
- const uint cd_loop_uv_offset = params->cd_loop_uv_offset;
+ const int cd_loop_uv_offset = params->cd_loop_uv_offset;
const int l_a_index = BM_elem_index_get(l_a);
const MLoopUV *luv_a = BM_ELEM_CD_GET_VOID_P(l_a, cd_loop_uv_offset);
const float uv_a[2] = {luv_a->uv[0], luv_a->uv[1] / params->aspect_y};
@@ -162,7 +160,7 @@ struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm,
if (!BM_elem_flag_test(l, BM_ELEM_TAG)) {
/* Adjacent loops are tagged while stepping to avoid 2x loops. */
BM_elem_flag_enable(l, BM_ELEM_TAG);
- looptag_add_adjacent_uv(heap, l, loops_prev, cost, params);
+ verttag_add_adjacent_uv(heap, l, loops_prev, cost, params);
}
}
@@ -185,8 +183,199 @@ struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm,
/** \name BM_mesh_calc_path_uv_edge
* \{ */
-/* TODO(@sidd017): Setting this as todo, since we now support proper UV edge selection (D12028).
- * Till then, continue using vertex path to fake shortest path calculation for edges. */
+static float edgetag_cut_cost_vert_uv(
+ BMLoop *l_e_a, BMLoop *l_e_b, BMLoop *l_v, const float aspect_y, const int cd_loop_uv_offset)
+{
+ BMLoop *l_v1 = (l_v->v == l_e_a->v) ? l_e_a->next : l_e_a;
+ BMLoop *l_v2 = (l_v->v == l_e_b->v) ? l_e_b->next : l_e_b;
+
+ MLoopUV *luv_v1 = BM_ELEM_CD_GET_VOID_P(l_v1, cd_loop_uv_offset);
+ MLoopUV *luv_v2 = BM_ELEM_CD_GET_VOID_P(l_v2, cd_loop_uv_offset);
+ MLoopUV *luv_v = BM_ELEM_CD_GET_VOID_P(l_v, cd_loop_uv_offset);
+
+ float uv_v1[2] = {luv_v1->uv[0], luv_v1->uv[1] / aspect_y};
+ float uv_v2[2] = {luv_v2->uv[0], luv_v2->uv[1] / aspect_y};
+ float uv_v[2] = {luv_v->uv[0], luv_v->uv[1] / aspect_y};
+
+ return step_cost_3_v2(uv_v1, uv_v, uv_v2);
+}
+
+static float edgetag_cut_cost_face_uv(
+ BMLoop *l_e_a, BMLoop *l_e_b, BMFace *f, const float aspect_v2[2], const int cd_loop_uv_offset)
+{
+ float l_e_a_cent[2], l_e_b_cent[2], f_cent[2];
+ MLoopUV *luv_e_a = BM_ELEM_CD_GET_VOID_P(l_e_a, cd_loop_uv_offset);
+ MLoopUV *luv_e_b = BM_ELEM_CD_GET_VOID_P(l_e_b, cd_loop_uv_offset);
+
+ mid_v2_v2v2(l_e_a_cent, luv_e_a->uv, luv_e_a->uv);
+ mid_v2_v2v2(l_e_b_cent, luv_e_b->uv, luv_e_b->uv);
+
+ mul_v2_v2(l_e_a_cent, aspect_v2);
+ mul_v2_v2(l_e_b_cent, aspect_v2);
+
+ BM_face_uv_calc_center_median_weighted(f, aspect_v2, cd_loop_uv_offset, f_cent);
+
+ return step_cost_3_v2(l_e_a_cent, l_e_b_cent, f_cent);
+}
+
+static void edgetag_add_adjacent_uv(HeapSimple *heap,
+ BMLoop *l_a,
+ BMLoop **loops_prev,
+ float *cost,
+ const struct BMCalcPathUVParams *params)
+{
+ BLI_assert(params->aspect_y != 0.0f);
+ const int cd_loop_uv_offset = params->cd_loop_uv_offset;
+ BMLoop *l_a_verts[2] = {l_a, l_a->next};
+ const int l_a_index = BM_elem_index_get(l_a);
+
+ if (params->use_step_face == false) {
+ for (int i = 0; i < ARRAY_SIZE(l_a_verts); i++) {
+
+ /* Skip current UV vert if it is part of the previous UV edge in the path. */
+ if (loops_prev[l_a_index]) {
+ BMLoop *l_prev = loops_prev[l_a_index];
+ if (l_a_verts[i]->v != l_prev->v) {
+ l_prev = (l_a_verts[i]->v == l_prev->next->v) ? l_prev->next : NULL;
+ }
+ if (l_prev && BM_loop_uv_share_vert_check(l_a_verts[i], l_prev, cd_loop_uv_offset)) {
+ continue;
+ }
+ }
+
+ BMEdge *e_b;
+ BMIter eiter;
+ BM_ITER_ELEM (e_b, &eiter, l_a_verts[i]->v, BM_EDGES_OF_VERT) {
+ BMLoop *l_first, *l_b;
+ l_first = l_b = e_b->l;
+ do {
+ if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) {
+ BMLoop *l_b_vert = (l_a_verts[i]->v == l_b->v) ? l_b : l_b->next;
+ if (BM_loop_uv_share_vert_check(l_a_verts[i], l_b_vert, cd_loop_uv_offset)) {
+ /* We know 'l_b' is not visited, check it out! */
+ const int l_b_index = BM_elem_index_get(l_b);
+ const float cost_cut = params->use_topology_distance ?
+ 1.0f :
+ edgetag_cut_cost_vert_uv(l_a,
+ l_b,
+ l_a_verts[i],
+ params->aspect_y,
+ cd_loop_uv_offset);
+ const float cost_new = cost[l_a_index] + cost_cut;
+
+ if (cost[l_b_index] > cost_new) {
+ cost[l_b_index] = cost_new;
+ loops_prev[l_b_index] = l_a;
+ BLI_heapsimple_insert(heap, cost_new, l_b);
+ }
+ }
+ }
+ } while ((l_b = l_b->radial_next) != l_first);
+ }
+ }
+ }
+ else {
+ const float aspect_v2[2] = {1.0f, 1.0f / params->aspect_y};
+ BMLoop *l_first, *l_iter;
+ l_iter = l_first = l_a;
+ do {
+ /* Ensures connected UVs and that they lie on the same island. */
+ if (!BM_loop_uv_share_edge_check(l_a, l_iter, cd_loop_uv_offset)) {
+ continue;
+ }
+
+ BMLoop *l_cycle_iter, *l_cycle_end;
+ l_cycle_iter = l_iter->next;
+ l_cycle_end = l_iter;
+ do {
+ BMLoop *l_b = l_cycle_iter;
+ if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) {
+ /* We know 'l_b' is not visited, check it out! */
+ const int l_b_index = BM_elem_index_get(l_b);
+ const float cost_cut = params->use_topology_distance ?
+ 1.0f :
+ edgetag_cut_cost_face_uv(l_a,
+ l_b,
+ l_iter->f,
+ aspect_v2,
+ params->cd_loop_uv_offset);
+ const float cost_new = cost[l_a_index] + cost_cut;
+
+ if (cost[l_b_index] > cost_new) {
+ cost[l_b_index] = cost_new;
+ loops_prev[l_b_index] = l_a;
+ BLI_heapsimple_insert(heap, cost_new, l_b);
+ }
+ }
+ } while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_end);
+ } while ((l_iter = l_iter->radial_next) != l_first);
+ }
+}
+
+struct LinkNode *BM_mesh_calc_path_uv_edge(BMesh *bm,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMLoop *, void *),
+ void *user_data)
+{
+ LinkNode *path = NULL;
+
+ BMFace *f;
+ BMIter iter;
+ HeapSimple *heap;
+ float *cost;
+ BMLoop **loops_prev;
+ int i = 0, totloop;
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
+ BMLoop *l_iter = l_first;
+ do {
+ BM_elem_flag_set(l_iter, BM_ELEM_TAG, !filter_fn(l_iter, user_data));
+ BM_elem_index_set(l_iter, i);
+ i += 1;
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~BM_LOOP;
+
+ totloop = bm->totloop;
+ loops_prev = MEM_callocN(sizeof(*loops_prev) * totloop, __func__);
+ cost = MEM_mallocN(sizeof(*cost) * totloop, __func__);
+
+ copy_vn_fl(cost, totloop, COST_INIT_MAX);
+
+ /* Regular dijkstra shortest path, but over UV loops/edges instead of vertices. */
+ heap = BLI_heapsimple_new();
+ BLI_heapsimple_insert(heap, 0.0f, l_src);
+ cost[BM_elem_index_get(l_src)] = 0.0f;
+
+ BMLoop *l = NULL;
+ while (!BLI_heapsimple_is_empty(heap)) {
+ l = BLI_heapsimple_pop_min(heap);
+
+ if ((l->e == l_dst->e) && (BM_loop_uv_share_edge_check(l, l_dst, params->cd_loop_uv_offset))) {
+ break;
+ }
+
+ if (!BM_elem_flag_test(l, BM_ELEM_TAG)) {
+ BM_elem_flag_enable(l, BM_ELEM_TAG);
+ edgetag_add_adjacent_uv(heap, l, loops_prev, cost, params);
+ }
+ }
+
+ if ((l->e == l_dst->e) && (BM_loop_uv_share_edge_check(l, l_dst, params->cd_loop_uv_offset))) {
+ do {
+ BLI_linklist_prepend(&path, l);
+ } while ((l = loops_prev[BM_elem_index_get(l)]));
+ }
+
+ MEM_freeN(loops_prev);
+ MEM_freeN(cost);
+ BLI_heapsimple_free(heap, NULL);
+
+ return path;
+}
/** \} */
@@ -273,7 +462,7 @@ static void facetag_add_adjacent_uv(HeapSimple *heap,
const float aspect_v2[2],
const struct BMCalcPathUVParams *params)
{
- const uint cd_loop_uv_offset = params->cd_loop_uv_offset;
+ const int cd_loop_uv_offset = params->cd_loop_uv_offset;
const int f_a_index = BM_elem_index_get(f_a);
/* Loop over faces of face, but do so by first looping over loops. */
diff --git a/source/blender/bmesh/tools/bmesh_path_uv.h b/source/blender/bmesh/tools/bmesh_path_uv.h
index af7341e2219..ebfedba70bb 100644
--- a/source/blender/bmesh/tools/bmesh_path_uv.h
+++ b/source/blender/bmesh/tools/bmesh_path_uv.h
@@ -9,7 +9,7 @@
struct BMCalcPathUVParams {
uint use_topology_distance : 1;
uint use_step_face : 1;
- uint cd_loop_uv_offset;
+ int cd_loop_uv_offset;
float aspect_y;
};
@@ -21,6 +21,14 @@ struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm,
void *user_data) ATTR_WARN_UNUSED_RESULT
ATTR_NONNULL(1, 2, 3, 5);
+struct LinkNode *BM_mesh_calc_path_uv_edge(BMesh *bm,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const struct BMCalcPathUVParams *params,
+ bool (*filter_fn)(BMLoop *, void *),
+ void *user_data) ATTR_WARN_UNUSED_RESULT
+ ATTR_NONNULL(1, 2, 3, 5);
+
struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm,
BMFace *f_src,
BMFace *f_dst,
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 55e349423bb..f49a9694ab3 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -1,676 +1,682 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2011 Blender Foundation. All rights reserved.
-set(INC
- .
- intern
- nodes
- operations
- ../blenkernel
- ../blenlib
- ../blentranslation
- ../depsgraph
- ../imbuf
- ../makesdna
- ../makesrna
- ../nodes
- ../windowmanager
- ../nodes/composite
- ../nodes/intern
- ../render
- ../render/intern
- ../../../extern/clew/include
- ../../../intern/atomic
- ../../../intern/clog
- ../../../intern/guardedalloc
-
- # dna_type_offsets.h
- ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern
- # RNA_prototypes.h
- ${CMAKE_BINARY_DIR}/source/blender/makesrna
-)
-
-set(INC_SYS
-
-)
-
-set(SRC
- COM_compositor.h
- COM_defines.h
-
- intern/COM_BufferArea.h
- intern/COM_BufferOperation.cc
- intern/COM_BufferOperation.h
- intern/COM_BufferRange.h
- intern/COM_BuffersIterator.h
- intern/COM_CPUDevice.cc
- intern/COM_CPUDevice.h
- intern/COM_ChunkOrder.cc
- intern/COM_ChunkOrder.h
- intern/COM_ChunkOrderHotspot.cc
- intern/COM_ChunkOrderHotspot.h
- intern/COM_CompositorContext.cc
- intern/COM_CompositorContext.h
- intern/COM_ConstantFolder.cc
- intern/COM_ConstantFolder.h
- intern/COM_Converter.cc
- intern/COM_Converter.h
- intern/COM_Debug.cc
- intern/COM_Debug.h
- intern/COM_Device.cc
- intern/COM_Device.h
- intern/COM_Enums.cc
- intern/COM_Enums.h
- 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_MultiThreadedRowOperation.cc
- intern/COM_MultiThreadedRowOperation.h
- intern/COM_Node.cc
- intern/COM_Node.h
- intern/COM_NodeConverter.cc
- intern/COM_NodeConverter.h
- intern/COM_NodeGraph.cc
- intern/COM_NodeGraph.h
- intern/COM_NodeOperation.cc
- intern/COM_NodeOperation.h
- intern/COM_NodeOperationBuilder.cc
- 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_TiledExecutionModel.cc
- intern/COM_TiledExecutionModel.h
- intern/COM_WorkPackage.cc
- intern/COM_WorkPackage.h
- intern/COM_WorkScheduler.cc
- intern/COM_WorkScheduler.h
- intern/COM_compositor.cc
-
- operations/COM_QualityStepHelper.cc
- operations/COM_QualityStepHelper.h
-
- # Internal nodes
- nodes/COM_SocketProxyNode.cc
- nodes/COM_SocketProxyNode.h
-
- # input nodes
- nodes/COM_BokehImageNode.cc
- nodes/COM_BokehImageNode.h
- nodes/COM_ColorNode.cc
- nodes/COM_ColorNode.h
- nodes/COM_ImageNode.cc
- nodes/COM_ImageNode.h
- nodes/COM_MaskNode.cc
- nodes/COM_MaskNode.h
- nodes/COM_MovieClipNode.cc
- nodes/COM_MovieClipNode.h
- nodes/COM_OutputFileNode.cc
- nodes/COM_OutputFileNode.h
- nodes/COM_RenderLayersNode.cc
- nodes/COM_RenderLayersNode.h
- nodes/COM_SceneTimeNode.cc
- nodes/COM_SceneTimeNode.h
- nodes/COM_SwitchNode.cc
- nodes/COM_SwitchNode.h
- nodes/COM_SwitchViewNode.cc
- nodes/COM_SwitchViewNode.h
- nodes/COM_TextureNode.cc
- nodes/COM_TextureNode.h
- nodes/COM_TimeNode.cc
- nodes/COM_TimeNode.h
- nodes/COM_ValueNode.cc
- nodes/COM_ValueNode.h
-
- # output nodes
- nodes/COM_CompositorNode.cc
- nodes/COM_CompositorNode.h
- nodes/COM_SplitViewerNode.cc
- nodes/COM_SplitViewerNode.h
- nodes/COM_ViewLevelsNode.cc
- nodes/COM_ViewLevelsNode.h
- nodes/COM_ViewerNode.cc
- nodes/COM_ViewerNode.h
- operations/COM_CalculateMeanOperation.cc
- operations/COM_CalculateMeanOperation.h
- operations/COM_CalculateStandardDeviationOperation.cc
- operations/COM_CalculateStandardDeviationOperation.h
-
- # distort nodes
- nodes/COM_FlipNode.cc
- nodes/COM_FlipNode.h
- nodes/COM_RotateNode.cc
- nodes/COM_RotateNode.h
- nodes/COM_ScaleNode.cc
- nodes/COM_ScaleNode.h
- nodes/COM_TranslateNode.cc
- nodes/COM_TranslateNode.h
-
- nodes/COM_DisplaceNode.cc
- nodes/COM_DisplaceNode.h
- nodes/COM_MapUVNode.cc
- nodes/COM_MapUVNode.h
-
- nodes/COM_ChannelMatteNode.cc
- nodes/COM_ChannelMatteNode.h
- nodes/COM_ChromaMatteNode.cc
- nodes/COM_ChromaMatteNode.h
- nodes/COM_ColorMatteNode.cc
- nodes/COM_ColorMatteNode.h
- nodes/COM_DifferenceMatteNode.cc
- nodes/COM_DifferenceMatteNode.h
- nodes/COM_DistanceMatteNode.cc
- nodes/COM_DistanceMatteNode.h
- nodes/COM_LensDistortionNode.cc
- nodes/COM_LensDistortionNode.h
- nodes/COM_LuminanceMatteNode.cc
- nodes/COM_LuminanceMatteNode.h
-
- nodes/COM_GlareNode.cc
- nodes/COM_GlareNode.h
-
- nodes/COM_SunBeamsNode.cc
- nodes/COM_SunBeamsNode.h
- operations/COM_SunBeamsOperation.cc
- operations/COM_SunBeamsOperation.h
-
- nodes/COM_CryptomatteNode.cc
- nodes/COM_CryptomatteNode.h
- operations/COM_CryptomatteOperation.cc
- operations/COM_CryptomatteOperation.h
-
- nodes/COM_CornerPinNode.cc
- nodes/COM_CornerPinNode.h
- nodes/COM_PlaneTrackDeformNode.cc
- nodes/COM_PlaneTrackDeformNode.h
-
- nodes/COM_CropNode.cc
- nodes/COM_CropNode.h
- operations/COM_CropOperation.cc
- operations/COM_CropOperation.h
-
- nodes/COM_DefocusNode.cc
- nodes/COM_DefocusNode.h
- nodes/COM_MovieDistortionNode.cc
- nodes/COM_MovieDistortionNode.h
- nodes/COM_Stabilize2dNode.cc
- nodes/COM_Stabilize2dNode.h
- nodes/COM_TransformNode.cc
- nodes/COM_TransformNode.h
-
- # color nodes
- nodes/COM_AlphaOverNode.cc
- nodes/COM_AlphaOverNode.h
- nodes/COM_BrightnessNode.cc
- nodes/COM_BrightnessNode.h
- nodes/COM_ColorBalanceNode.cc
- nodes/COM_ColorBalanceNode.h
- nodes/COM_ColorCorrectionNode.cc
- nodes/COM_ColorCorrectionNode.h
- nodes/COM_ColorCurveNode.cc
- nodes/COM_ColorCurveNode.h
- nodes/COM_ColorExposureNode.cc
- nodes/COM_ColorExposureNode.h
- nodes/COM_ColorRampNode.cc
- nodes/COM_ColorRampNode.h
- nodes/COM_ColorToBWNode.cc
- nodes/COM_ColorToBWNode.h
- nodes/COM_ConvertAlphaNode.cc
- nodes/COM_ConvertAlphaNode.h
- nodes/COM_ConvertColorSpaceNode.cc
- nodes/COM_ConvertColorSpaceNode.h
- nodes/COM_GammaNode.cc
- nodes/COM_GammaNode.h
- nodes/COM_HueSaturationValueCorrectNode.cc
- nodes/COM_HueSaturationValueCorrectNode.h
- nodes/COM_HueSaturationValueNode.cc
- nodes/COM_HueSaturationValueNode.h
- nodes/COM_InvertNode.cc
- nodes/COM_InvertNode.h
- nodes/COM_MixNode.cc
- nodes/COM_MixNode.h
- nodes/COM_SetAlphaNode.cc
- nodes/COM_SetAlphaNode.h
- nodes/COM_TonemapNode.cc
- nodes/COM_TonemapNode.h
- nodes/COM_VectorCurveNode.cc
- nodes/COM_VectorCurveNode.h
- nodes/COM_ZCombineNode.cc
- nodes/COM_ZCombineNode.h
- operations/COM_TonemapOperation.cc
- operations/COM_TonemapOperation.h
-
- # converter nodes
- nodes/COM_CombineColorNode.cc
- nodes/COM_CombineColorNode.h
- nodes/COM_CombineColorNodeLegacy.cc
- nodes/COM_CombineColorNodeLegacy.h
- nodes/COM_CombineXYZNode.cc
- nodes/COM_CombineXYZNode.h
- nodes/COM_IDMaskNode.cc
- nodes/COM_IDMaskNode.h
- nodes/COM_SeparateColorNode.cc
- nodes/COM_SeparateColorNode.h
- nodes/COM_SeparateColorNodeLegacy.cc
- nodes/COM_SeparateColorNodeLegacy.h
- nodes/COM_SeparateXYZNode.cc
- nodes/COM_SeparateXYZNode.h
-
- nodes/COM_MapRangeNode.cc
- nodes/COM_MapRangeNode.h
- nodes/COM_MapValueNode.cc
- nodes/COM_MapValueNode.h
- nodes/COM_MathNode.cc
- nodes/COM_MathNode.h
- nodes/COM_NormalNode.cc
- nodes/COM_NormalNode.h
- nodes/COM_NormalizeNode.cc
- nodes/COM_NormalizeNode.h
-
- operations/COM_NormalizeOperation.cc
- operations/COM_NormalizeOperation.h
-
- nodes/COM_PixelateNode.cc
- nodes/COM_PixelateNode.h
- operations/COM_PixelateOperation.cc
- operations/COM_PixelateOperation.h
-
- # Filter nodes
- nodes/COM_BilateralBlurNode.cc
- nodes/COM_BilateralBlurNode.h
- operations/COM_BilateralBlurOperation.cc
- operations/COM_BilateralBlurOperation.h
- nodes/COM_VectorBlurNode.cc
- 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
- nodes/COM_BokehBlurNode.h
- nodes/COM_DenoiseNode.cc
- nodes/COM_DenoiseNode.h
- nodes/COM_DespeckleNode.cc
- nodes/COM_DespeckleNode.h
- nodes/COM_DilateErodeNode.cc
- nodes/COM_DilateErodeNode.h
- nodes/COM_DirectionalBlurNode.cc
- nodes/COM_DirectionalBlurNode.h
- nodes/COM_FilterNode.cc
- nodes/COM_FilterNode.h
- nodes/COM_InpaintNode.cc
- nodes/COM_InpaintNode.h
- nodes/COM_PosterizeNode.cc
- nodes/COM_PosterizeNode.h
-
- operations/COM_BlurBaseOperation.cc
- operations/COM_BlurBaseOperation.h
- operations/COM_BokehBlurOperation.cc
- operations/COM_BokehBlurOperation.h
- operations/COM_DirectionalBlurOperation.cc
- operations/COM_DirectionalBlurOperation.h
- operations/COM_FastGaussianBlurOperation.cc
- operations/COM_FastGaussianBlurOperation.h
- operations/COM_GammaCorrectOperation.cc
- operations/COM_GammaCorrectOperation.h
- operations/COM_GaussianAlphaBlurBaseOperation.cc
- operations/COM_GaussianAlphaBlurBaseOperation.h
- operations/COM_GaussianAlphaXBlurOperation.cc
- operations/COM_GaussianAlphaXBlurOperation.h
- operations/COM_GaussianAlphaYBlurOperation.cc
- operations/COM_GaussianAlphaYBlurOperation.h
- operations/COM_GaussianBlurBaseOperation.cc
- operations/COM_GaussianBlurBaseOperation.h
- operations/COM_GaussianBokehBlurOperation.cc
- operations/COM_GaussianBokehBlurOperation.h
- operations/COM_GaussianXBlurOperation.cc
- operations/COM_GaussianXBlurOperation.h
- operations/COM_GaussianYBlurOperation.cc
- operations/COM_GaussianYBlurOperation.h
- operations/COM_MovieClipAttributeOperation.cc
- operations/COM_MovieClipAttributeOperation.h
- operations/COM_MovieDistortionOperation.cc
- operations/COM_MovieDistortionOperation.h
- operations/COM_PosterizeOperation.cc
- operations/COM_PosterizeOperation.h
- operations/COM_SMAAOperation.cc
- operations/COM_SMAAOperation.h
- operations/COM_VariableSizeBokehBlurOperation.cc
- operations/COM_VariableSizeBokehBlurOperation.h
-
- # Matte nodes
- nodes/COM_BoxMaskNode.cc
- nodes/COM_BoxMaskNode.h
- nodes/COM_ColorSpillNode.cc
- nodes/COM_ColorSpillNode.h
- nodes/COM_DoubleEdgeMaskNode.cc
- nodes/COM_DoubleEdgeMaskNode.h
- nodes/COM_EllipseMaskNode.cc
- nodes/COM_EllipseMaskNode.h
-
- operations/COM_DoubleEdgeMaskOperation.cc
- operations/COM_DoubleEdgeMaskOperation.h
-
-
- nodes/COM_KeyingScreenNode.cc
- nodes/COM_KeyingScreenNode.h
- operations/COM_KeyingScreenOperation.cc
- operations/COM_KeyingScreenOperation.h
-
- nodes/COM_TrackPositionNode.cc
- nodes/COM_TrackPositionNode.h
- operations/COM_TrackPositionOperation.cc
- operations/COM_TrackPositionOperation.h
-
- nodes/COM_KeyingNode.cc
- nodes/COM_KeyingNode.h
- operations/COM_KeyingBlurOperation.cc
- operations/COM_KeyingBlurOperation.h
- operations/COM_KeyingClipOperation.cc
- operations/COM_KeyingClipOperation.h
- operations/COM_KeyingDespillOperation.cc
- operations/COM_KeyingDespillOperation.h
- operations/COM_KeyingOperation.cc
- operations/COM_KeyingOperation.h
-
- operations/COM_ColorSpillOperation.cc
- operations/COM_ColorSpillOperation.h
- operations/COM_RenderLayersProg.cc
- operations/COM_RenderLayersProg.h
-
- operations/COM_BokehImageOperation.cc
- operations/COM_BokehImageOperation.h
- operations/COM_ImageOperation.cc
- operations/COM_ImageOperation.h
- operations/COM_MultilayerImageOperation.cc
- operations/COM_MultilayerImageOperation.h
- operations/COM_TextureOperation.cc
- operations/COM_TextureOperation.h
-
-
- operations/COM_SocketProxyOperation.cc
- operations/COM_SocketProxyOperation.h
-
- operations/COM_CompositorOperation.cc
- operations/COM_CompositorOperation.h
- operations/COM_ConvertDepthToRadiusOperation.cc
- operations/COM_ConvertDepthToRadiusOperation.h
- operations/COM_OutputFileMultiViewOperation.cc
- operations/COM_OutputFileMultiViewOperation.h
- operations/COM_OutputFileOperation.cc
- operations/COM_OutputFileOperation.h
- operations/COM_PreviewOperation.cc
- operations/COM_PreviewOperation.h
- operations/COM_SplitOperation.cc
- operations/COM_SplitOperation.h
- operations/COM_ViewerOperation.cc
- operations/COM_ViewerOperation.h
- operations/COM_ZCombineOperation.cc
- operations/COM_ZCombineOperation.h
-
- operations/COM_ChangeHSVOperation.cc
- operations/COM_ChangeHSVOperation.h
- operations/COM_ChannelMatteOperation.cc
- operations/COM_ChannelMatteOperation.h
- operations/COM_ChromaMatteOperation.cc
- operations/COM_ChromaMatteOperation.h
- operations/COM_ColorCurveOperation.cc
- operations/COM_ColorCurveOperation.h
- operations/COM_ColorExposureOperation.cc
- operations/COM_ColorExposureOperation.h
- operations/COM_ColorMatteOperation.cc
- operations/COM_ColorMatteOperation.h
- operations/COM_ColorRampOperation.cc
- operations/COM_ColorRampOperation.h
- operations/COM_CurveBaseOperation.cc
- operations/COM_CurveBaseOperation.h
- operations/COM_DifferenceMatteOperation.cc
- operations/COM_DifferenceMatteOperation.h
- operations/COM_DistanceRGBMatteOperation.cc
- operations/COM_DistanceRGBMatteOperation.h
- operations/COM_DistanceYCCMatteOperation.cc
- operations/COM_DistanceYCCMatteOperation.h
- operations/COM_HueSaturationValueCorrectOperation.cc
- operations/COM_HueSaturationValueCorrectOperation.h
- operations/COM_LuminanceMatteOperation.cc
- operations/COM_LuminanceMatteOperation.h
- operations/COM_VectorCurveOperation.cc
- operations/COM_VectorCurveOperation.h
-
- operations/COM_BrightnessOperation.cc
- operations/COM_BrightnessOperation.h
- operations/COM_ColorCorrectionOperation.cc
- operations/COM_ColorCorrectionOperation.h
- operations/COM_ConstantOperation.cc
- operations/COM_ConstantOperation.h
- operations/COM_GammaOperation.cc
- operations/COM_GammaOperation.h
- operations/COM_MixOperation.cc
- operations/COM_MixOperation.h
- operations/COM_ReadBufferOperation.cc
- operations/COM_ReadBufferOperation.h
- operations/COM_SetColorOperation.cc
- operations/COM_SetColorOperation.h
- operations/COM_SetValueOperation.cc
- operations/COM_SetValueOperation.h
- operations/COM_SetVectorOperation.cc
- operations/COM_SetVectorOperation.h
- operations/COM_WriteBufferOperation.cc
- operations/COM_WriteBufferOperation.h
-
- operations/COM_MathBaseOperation.cc
- operations/COM_MathBaseOperation.h
-
- operations/COM_AlphaOverKeyOperation.cc
- operations/COM_AlphaOverKeyOperation.h
- operations/COM_AlphaOverMixedOperation.cc
- operations/COM_AlphaOverMixedOperation.h
- operations/COM_AlphaOverPremultiplyOperation.cc
- operations/COM_AlphaOverPremultiplyOperation.h
-
- operations/COM_ColorBalanceASCCDLOperation.cc
- operations/COM_ColorBalanceASCCDLOperation.h
- operations/COM_ColorBalanceLGGOperation.cc
- operations/COM_ColorBalanceLGGOperation.h
- operations/COM_InvertOperation.cc
- operations/COM_InvertOperation.h
- operations/COM_MapRangeOperation.cc
- operations/COM_MapRangeOperation.h
- operations/COM_MapValueOperation.cc
- operations/COM_MapValueOperation.h
- operations/COM_SetAlphaMultiplyOperation.cc
- operations/COM_SetAlphaMultiplyOperation.h
- operations/COM_SetAlphaReplaceOperation.cc
- operations/COM_SetAlphaReplaceOperation.h
-
- # Distort operation
- operations/COM_DisplaceOperation.cc
- operations/COM_DisplaceOperation.h
- operations/COM_DisplaceSimpleOperation.cc
- operations/COM_DisplaceSimpleOperation.h
- operations/COM_FlipOperation.cc
- operations/COM_FlipOperation.h
- operations/COM_MapUVOperation.cc
- operations/COM_MapUVOperation.h
- operations/COM_PlaneCornerPinOperation.cc
- operations/COM_PlaneCornerPinOperation.h
- operations/COM_PlaneDistortCommonOperation.cc
- operations/COM_PlaneDistortCommonOperation.h
- operations/COM_PlaneTrackOperation.cc
- operations/COM_PlaneTrackOperation.h
- operations/COM_ProjectorLensDistortionOperation.cc
- operations/COM_ProjectorLensDistortionOperation.h
- operations/COM_RotateOperation.cc
- operations/COM_RotateOperation.h
- operations/COM_ScaleOperation.cc
- operations/COM_ScaleOperation.h
- operations/COM_ScreenLensDistortionOperation.cc
- operations/COM_ScreenLensDistortionOperation.h
- operations/COM_TransformOperation.cc
- operations/COM_TransformOperation.h
- operations/COM_TranslateOperation.cc
- operations/COM_TranslateOperation.h
- operations/COM_WrapOperation.cc
- operations/COM_WrapOperation.h
-
- # Filter operations
- operations/COM_ConvolutionEdgeFilterOperation.cc
- operations/COM_ConvolutionEdgeFilterOperation.h
- operations/COM_ConvolutionFilterOperation.cc
- operations/COM_ConvolutionFilterOperation.h
- operations/COM_DenoiseOperation.cc
- operations/COM_DenoiseOperation.h
- operations/COM_DespeckleOperation.cc
- operations/COM_DespeckleOperation.h
- operations/COM_DilateErodeOperation.cc
- operations/COM_DilateErodeOperation.h
- operations/COM_GlareBaseOperation.cc
- operations/COM_GlareBaseOperation.h
- operations/COM_GlareFogGlowOperation.cc
- operations/COM_GlareFogGlowOperation.h
- operations/COM_GlareGhostOperation.cc
- operations/COM_GlareGhostOperation.h
- operations/COM_GlareSimpleStarOperation.cc
- operations/COM_GlareSimpleStarOperation.h
- operations/COM_GlareStreaksOperation.cc
- operations/COM_GlareStreaksOperation.h
- operations/COM_GlareThresholdOperation.cc
- operations/COM_GlareThresholdOperation.h
- operations/COM_InpaintOperation.cc
- operations/COM_InpaintOperation.h
- operations/COM_SetSamplerOperation.cc
- operations/COM_SetSamplerOperation.h
-
-
- # Convert operations
- operations/COM_ConvertOperation.cc
- operations/COM_ConvertOperation.h
- operations/COM_IDMaskOperation.cc
- operations/COM_IDMaskOperation.h
-
- operations/COM_ConvertColorSpaceOperation.cc
- operations/COM_ConvertColorSpaceOperation.h
- operations/COM_DotproductOperation.cc
- operations/COM_DotproductOperation.h
-
- # Matte operation
- operations/COM_BoxMaskOperation.cc
- operations/COM_BoxMaskOperation.h
- operations/COM_EllipseMaskOperation.cc
- operations/COM_EllipseMaskOperation.h
-
- operations/COM_ConvertColorProfileOperation.cc
- operations/COM_ConvertColorProfileOperation.h
- operations/COM_MovieClipOperation.cc
- operations/COM_MovieClipOperation.h
-
- operations/COM_AntiAliasOperation.cc
- operations/COM_AntiAliasOperation.h
-
- operations/COM_MaskOperation.cc
- operations/COM_MaskOperation.h
-)
-
-set(LIB
- bf_blenkernel
- bf_blenlib
- extern_clew
-)
-
-list(APPEND INC
- ${CMAKE_CURRENT_BINARY_DIR}/operations
-)
-
-data_to_c(
- ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl
- ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h
- SRC
-)
-
-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_OPENIMAGEDENOISE)
- add_definitions(-DWITH_OPENIMAGEDENOISE)
- add_definitions(-DOIDN_STATIC_LIB)
- list(APPEND INC_SYS
- ${OPENIMAGEDENOISE_INCLUDE_DIRS}
- ${TBB_INCLUDE_DIRS}
+add_subdirectory(realtime_compositor)
+
+if(WITH_COMPOSITOR_CPU)
+ set(INC
+ .
+ intern
+ nodes
+ operations
+ ../blenkernel
+ ../blenlib
+ ../blentranslation
+ ../depsgraph
+ ../imbuf
+ ../makesdna
+ ../makesrna
+ ../nodes
+ ../windowmanager
+ ../nodes/composite
+ ../nodes/intern
+ ../render
+ ../render/intern
+ ../../../extern/clew/include
+ ../../../intern/atomic
+ ../../../intern/clog
+ ../../../intern/guardedalloc
+
+ # dna_type_offsets.h
+ ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern
+ # RNA_prototypes.h
+ ${CMAKE_BINARY_DIR}/source/blender/makesrna
)
- list(APPEND LIB
- ${OPENIMAGEDENOISE_LIBRARIES}
- ${TBB_LIBRARIES}
+
+ set(INC_SYS
+
)
-endif()
-blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+ set(SRC
+ COM_compositor.h
+ COM_defines.h
+
+ intern/COM_BufferArea.h
+ intern/COM_BufferOperation.cc
+ intern/COM_BufferOperation.h
+ intern/COM_BufferRange.h
+ intern/COM_BuffersIterator.h
+ intern/COM_CPUDevice.cc
+ intern/COM_CPUDevice.h
+ intern/COM_ChunkOrder.cc
+ intern/COM_ChunkOrder.h
+ intern/COM_ChunkOrderHotspot.cc
+ intern/COM_ChunkOrderHotspot.h
+ intern/COM_CompositorContext.cc
+ intern/COM_CompositorContext.h
+ intern/COM_ConstantFolder.cc
+ intern/COM_ConstantFolder.h
+ intern/COM_Converter.cc
+ intern/COM_Converter.h
+ intern/COM_Debug.cc
+ intern/COM_Debug.h
+ intern/COM_Device.cc
+ intern/COM_Device.h
+ intern/COM_Enums.cc
+ intern/COM_Enums.h
+ 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_MultiThreadedRowOperation.cc
+ intern/COM_MultiThreadedRowOperation.h
+ intern/COM_Node.cc
+ intern/COM_Node.h
+ intern/COM_NodeConverter.cc
+ intern/COM_NodeConverter.h
+ intern/COM_NodeGraph.cc
+ intern/COM_NodeGraph.h
+ intern/COM_NodeOperation.cc
+ intern/COM_NodeOperation.h
+ intern/COM_NodeOperationBuilder.cc
+ 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_TiledExecutionModel.cc
+ intern/COM_TiledExecutionModel.h
+ intern/COM_WorkPackage.cc
+ intern/COM_WorkPackage.h
+ intern/COM_WorkScheduler.cc
+ intern/COM_WorkScheduler.h
+ intern/COM_compositor.cc
+
+ operations/COM_QualityStepHelper.cc
+ operations/COM_QualityStepHelper.h
+
+ # Internal nodes
+ nodes/COM_SocketProxyNode.cc
+ nodes/COM_SocketProxyNode.h
+
+ # input nodes
+ nodes/COM_BokehImageNode.cc
+ nodes/COM_BokehImageNode.h
+ nodes/COM_ColorNode.cc
+ nodes/COM_ColorNode.h
+ nodes/COM_ImageNode.cc
+ nodes/COM_ImageNode.h
+ nodes/COM_MaskNode.cc
+ nodes/COM_MaskNode.h
+ nodes/COM_MovieClipNode.cc
+ nodes/COM_MovieClipNode.h
+ nodes/COM_OutputFileNode.cc
+ nodes/COM_OutputFileNode.h
+ nodes/COM_RenderLayersNode.cc
+ nodes/COM_RenderLayersNode.h
+ nodes/COM_SceneTimeNode.cc
+ nodes/COM_SceneTimeNode.h
+ nodes/COM_SwitchNode.cc
+ nodes/COM_SwitchNode.h
+ nodes/COM_SwitchViewNode.cc
+ nodes/COM_SwitchViewNode.h
+ nodes/COM_TextureNode.cc
+ nodes/COM_TextureNode.h
+ nodes/COM_TimeNode.cc
+ nodes/COM_TimeNode.h
+ nodes/COM_ValueNode.cc
+ nodes/COM_ValueNode.h
+
+ # output nodes
+ nodes/COM_CompositorNode.cc
+ nodes/COM_CompositorNode.h
+ nodes/COM_SplitViewerNode.cc
+ nodes/COM_SplitViewerNode.h
+ nodes/COM_ViewLevelsNode.cc
+ nodes/COM_ViewLevelsNode.h
+ nodes/COM_ViewerNode.cc
+ nodes/COM_ViewerNode.h
+ operations/COM_CalculateMeanOperation.cc
+ operations/COM_CalculateMeanOperation.h
+ operations/COM_CalculateStandardDeviationOperation.cc
+ operations/COM_CalculateStandardDeviationOperation.h
+
+ # distort nodes
+ nodes/COM_FlipNode.cc
+ nodes/COM_FlipNode.h
+ nodes/COM_RotateNode.cc
+ nodes/COM_RotateNode.h
+ nodes/COM_ScaleNode.cc
+ nodes/COM_ScaleNode.h
+ nodes/COM_TranslateNode.cc
+ nodes/COM_TranslateNode.h
+
+ nodes/COM_DisplaceNode.cc
+ nodes/COM_DisplaceNode.h
+ nodes/COM_MapUVNode.cc
+ nodes/COM_MapUVNode.h
+
+ nodes/COM_ChannelMatteNode.cc
+ nodes/COM_ChannelMatteNode.h
+ nodes/COM_ChromaMatteNode.cc
+ nodes/COM_ChromaMatteNode.h
+ nodes/COM_ColorMatteNode.cc
+ nodes/COM_ColorMatteNode.h
+ nodes/COM_DifferenceMatteNode.cc
+ nodes/COM_DifferenceMatteNode.h
+ nodes/COM_DistanceMatteNode.cc
+ nodes/COM_DistanceMatteNode.h
+ nodes/COM_LensDistortionNode.cc
+ nodes/COM_LensDistortionNode.h
+ nodes/COM_LuminanceMatteNode.cc
+ nodes/COM_LuminanceMatteNode.h
+
+ nodes/COM_GlareNode.cc
+ nodes/COM_GlareNode.h
+
+ nodes/COM_SunBeamsNode.cc
+ nodes/COM_SunBeamsNode.h
+ operations/COM_SunBeamsOperation.cc
+ operations/COM_SunBeamsOperation.h
+
+ nodes/COM_CryptomatteNode.cc
+ nodes/COM_CryptomatteNode.h
+ operations/COM_CryptomatteOperation.cc
+ operations/COM_CryptomatteOperation.h
+
+ nodes/COM_CornerPinNode.cc
+ nodes/COM_CornerPinNode.h
+ nodes/COM_PlaneTrackDeformNode.cc
+ nodes/COM_PlaneTrackDeformNode.h
+
+ nodes/COM_CropNode.cc
+ nodes/COM_CropNode.h
+ operations/COM_CropOperation.cc
+ operations/COM_CropOperation.h
+
+ nodes/COM_DefocusNode.cc
+ nodes/COM_DefocusNode.h
+ nodes/COM_MovieDistortionNode.cc
+ nodes/COM_MovieDistortionNode.h
+ nodes/COM_Stabilize2dNode.cc
+ nodes/COM_Stabilize2dNode.h
+ nodes/COM_TransformNode.cc
+ nodes/COM_TransformNode.h
+
+ # color nodes
+ nodes/COM_AlphaOverNode.cc
+ nodes/COM_AlphaOverNode.h
+ nodes/COM_BrightnessNode.cc
+ nodes/COM_BrightnessNode.h
+ nodes/COM_ColorBalanceNode.cc
+ nodes/COM_ColorBalanceNode.h
+ nodes/COM_ColorCorrectionNode.cc
+ nodes/COM_ColorCorrectionNode.h
+ nodes/COM_ColorCurveNode.cc
+ nodes/COM_ColorCurveNode.h
+ nodes/COM_ColorExposureNode.cc
+ nodes/COM_ColorExposureNode.h
+ nodes/COM_ColorRampNode.cc
+ nodes/COM_ColorRampNode.h
+ nodes/COM_ColorToBWNode.cc
+ nodes/COM_ColorToBWNode.h
+ nodes/COM_ConvertAlphaNode.cc
+ nodes/COM_ConvertAlphaNode.h
+ nodes/COM_ConvertColorSpaceNode.cc
+ nodes/COM_ConvertColorSpaceNode.h
+ nodes/COM_GammaNode.cc
+ nodes/COM_GammaNode.h
+ nodes/COM_HueSaturationValueCorrectNode.cc
+ nodes/COM_HueSaturationValueCorrectNode.h
+ nodes/COM_HueSaturationValueNode.cc
+ nodes/COM_HueSaturationValueNode.h
+ nodes/COM_InvertNode.cc
+ nodes/COM_InvertNode.h
+ nodes/COM_MixNode.cc
+ nodes/COM_MixNode.h
+ nodes/COM_SetAlphaNode.cc
+ nodes/COM_SetAlphaNode.h
+ nodes/COM_TonemapNode.cc
+ nodes/COM_TonemapNode.h
+ nodes/COM_VectorCurveNode.cc
+ nodes/COM_VectorCurveNode.h
+ nodes/COM_ZCombineNode.cc
+ nodes/COM_ZCombineNode.h
+ operations/COM_TonemapOperation.cc
+ operations/COM_TonemapOperation.h
+
+ # converter nodes
+ nodes/COM_CombineColorNode.cc
+ nodes/COM_CombineColorNode.h
+ nodes/COM_CombineColorNodeLegacy.cc
+ nodes/COM_CombineColorNodeLegacy.h
+ nodes/COM_CombineXYZNode.cc
+ nodes/COM_CombineXYZNode.h
+ nodes/COM_IDMaskNode.cc
+ nodes/COM_IDMaskNode.h
+ nodes/COM_SeparateColorNode.cc
+ nodes/COM_SeparateColorNode.h
+ nodes/COM_SeparateColorNodeLegacy.cc
+ nodes/COM_SeparateColorNodeLegacy.h
+ nodes/COM_SeparateXYZNode.cc
+ nodes/COM_SeparateXYZNode.h
+
+ nodes/COM_MapRangeNode.cc
+ nodes/COM_MapRangeNode.h
+ nodes/COM_MapValueNode.cc
+ nodes/COM_MapValueNode.h
+ nodes/COM_MathNode.cc
+ nodes/COM_MathNode.h
+ nodes/COM_NormalNode.cc
+ nodes/COM_NormalNode.h
+ nodes/COM_NormalizeNode.cc
+ nodes/COM_NormalizeNode.h
+
+ operations/COM_NormalizeOperation.cc
+ operations/COM_NormalizeOperation.h
+
+ nodes/COM_PixelateNode.cc
+ nodes/COM_PixelateNode.h
+ operations/COM_PixelateOperation.cc
+ operations/COM_PixelateOperation.h
+
+ # Filter nodes
+ nodes/COM_BilateralBlurNode.cc
+ nodes/COM_BilateralBlurNode.h
+ operations/COM_BilateralBlurOperation.cc
+ operations/COM_BilateralBlurOperation.h
+ nodes/COM_VectorBlurNode.cc
+ 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
+ nodes/COM_BokehBlurNode.h
+ nodes/COM_DenoiseNode.cc
+ nodes/COM_DenoiseNode.h
+ nodes/COM_DespeckleNode.cc
+ nodes/COM_DespeckleNode.h
+ nodes/COM_DilateErodeNode.cc
+ nodes/COM_DilateErodeNode.h
+ nodes/COM_DirectionalBlurNode.cc
+ nodes/COM_DirectionalBlurNode.h
+ nodes/COM_FilterNode.cc
+ nodes/COM_FilterNode.h
+ nodes/COM_InpaintNode.cc
+ nodes/COM_InpaintNode.h
+ nodes/COM_PosterizeNode.cc
+ nodes/COM_PosterizeNode.h
+
+ operations/COM_BlurBaseOperation.cc
+ operations/COM_BlurBaseOperation.h
+ operations/COM_BokehBlurOperation.cc
+ operations/COM_BokehBlurOperation.h
+ operations/COM_DirectionalBlurOperation.cc
+ operations/COM_DirectionalBlurOperation.h
+ operations/COM_FastGaussianBlurOperation.cc
+ operations/COM_FastGaussianBlurOperation.h
+ operations/COM_GammaCorrectOperation.cc
+ operations/COM_GammaCorrectOperation.h
+ operations/COM_GaussianAlphaBlurBaseOperation.cc
+ operations/COM_GaussianAlphaBlurBaseOperation.h
+ operations/COM_GaussianAlphaXBlurOperation.cc
+ operations/COM_GaussianAlphaXBlurOperation.h
+ operations/COM_GaussianAlphaYBlurOperation.cc
+ operations/COM_GaussianAlphaYBlurOperation.h
+ operations/COM_GaussianBlurBaseOperation.cc
+ operations/COM_GaussianBlurBaseOperation.h
+ operations/COM_GaussianBokehBlurOperation.cc
+ operations/COM_GaussianBokehBlurOperation.h
+ operations/COM_GaussianXBlurOperation.cc
+ operations/COM_GaussianXBlurOperation.h
+ operations/COM_GaussianYBlurOperation.cc
+ operations/COM_GaussianYBlurOperation.h
+ operations/COM_MovieClipAttributeOperation.cc
+ operations/COM_MovieClipAttributeOperation.h
+ operations/COM_MovieDistortionOperation.cc
+ operations/COM_MovieDistortionOperation.h
+ operations/COM_PosterizeOperation.cc
+ operations/COM_PosterizeOperation.h
+ operations/COM_SMAAOperation.cc
+ operations/COM_SMAAOperation.h
+ operations/COM_VariableSizeBokehBlurOperation.cc
+ operations/COM_VariableSizeBokehBlurOperation.h
+
+ # Matte nodes
+ nodes/COM_BoxMaskNode.cc
+ nodes/COM_BoxMaskNode.h
+ nodes/COM_ColorSpillNode.cc
+ nodes/COM_ColorSpillNode.h
+ nodes/COM_DoubleEdgeMaskNode.cc
+ nodes/COM_DoubleEdgeMaskNode.h
+ nodes/COM_EllipseMaskNode.cc
+ nodes/COM_EllipseMaskNode.h
+
+ operations/COM_DoubleEdgeMaskOperation.cc
+ operations/COM_DoubleEdgeMaskOperation.h
+
+
+ nodes/COM_KeyingScreenNode.cc
+ nodes/COM_KeyingScreenNode.h
+ operations/COM_KeyingScreenOperation.cc
+ operations/COM_KeyingScreenOperation.h
+
+ nodes/COM_TrackPositionNode.cc
+ nodes/COM_TrackPositionNode.h
+ operations/COM_TrackPositionOperation.cc
+ operations/COM_TrackPositionOperation.h
+
+ nodes/COM_KeyingNode.cc
+ nodes/COM_KeyingNode.h
+ operations/COM_KeyingBlurOperation.cc
+ operations/COM_KeyingBlurOperation.h
+ operations/COM_KeyingClipOperation.cc
+ operations/COM_KeyingClipOperation.h
+ operations/COM_KeyingDespillOperation.cc
+ operations/COM_KeyingDespillOperation.h
+ operations/COM_KeyingOperation.cc
+ operations/COM_KeyingOperation.h
+
+ operations/COM_ColorSpillOperation.cc
+ operations/COM_ColorSpillOperation.h
+ operations/COM_RenderLayersProg.cc
+ operations/COM_RenderLayersProg.h
+
+ operations/COM_BokehImageOperation.cc
+ operations/COM_BokehImageOperation.h
+ operations/COM_ImageOperation.cc
+ operations/COM_ImageOperation.h
+ operations/COM_MultilayerImageOperation.cc
+ operations/COM_MultilayerImageOperation.h
+ operations/COM_TextureOperation.cc
+ operations/COM_TextureOperation.h
+
+
+ operations/COM_SocketProxyOperation.cc
+ operations/COM_SocketProxyOperation.h
+
+ operations/COM_CompositorOperation.cc
+ operations/COM_CompositorOperation.h
+ operations/COM_ConvertDepthToRadiusOperation.cc
+ operations/COM_ConvertDepthToRadiusOperation.h
+ operations/COM_OutputFileMultiViewOperation.cc
+ operations/COM_OutputFileMultiViewOperation.h
+ operations/COM_OutputFileOperation.cc
+ operations/COM_OutputFileOperation.h
+ operations/COM_PreviewOperation.cc
+ operations/COM_PreviewOperation.h
+ operations/COM_SplitOperation.cc
+ operations/COM_SplitOperation.h
+ operations/COM_ViewerOperation.cc
+ operations/COM_ViewerOperation.h
+ operations/COM_ZCombineOperation.cc
+ operations/COM_ZCombineOperation.h
+
+ operations/COM_ChangeHSVOperation.cc
+ operations/COM_ChangeHSVOperation.h
+ operations/COM_ChannelMatteOperation.cc
+ operations/COM_ChannelMatteOperation.h
+ operations/COM_ChromaMatteOperation.cc
+ operations/COM_ChromaMatteOperation.h
+ operations/COM_ColorCurveOperation.cc
+ operations/COM_ColorCurveOperation.h
+ operations/COM_ColorExposureOperation.cc
+ operations/COM_ColorExposureOperation.h
+ operations/COM_ColorMatteOperation.cc
+ operations/COM_ColorMatteOperation.h
+ operations/COM_ColorRampOperation.cc
+ operations/COM_ColorRampOperation.h
+ operations/COM_CurveBaseOperation.cc
+ operations/COM_CurveBaseOperation.h
+ operations/COM_DifferenceMatteOperation.cc
+ operations/COM_DifferenceMatteOperation.h
+ operations/COM_DistanceRGBMatteOperation.cc
+ operations/COM_DistanceRGBMatteOperation.h
+ operations/COM_DistanceYCCMatteOperation.cc
+ operations/COM_DistanceYCCMatteOperation.h
+ operations/COM_HueSaturationValueCorrectOperation.cc
+ operations/COM_HueSaturationValueCorrectOperation.h
+ operations/COM_LuminanceMatteOperation.cc
+ operations/COM_LuminanceMatteOperation.h
+ operations/COM_VectorCurveOperation.cc
+ operations/COM_VectorCurveOperation.h
+
+ operations/COM_BrightnessOperation.cc
+ operations/COM_BrightnessOperation.h
+ operations/COM_ColorCorrectionOperation.cc
+ operations/COM_ColorCorrectionOperation.h
+ operations/COM_ConstantOperation.cc
+ operations/COM_ConstantOperation.h
+ operations/COM_GammaOperation.cc
+ operations/COM_GammaOperation.h
+ operations/COM_MixOperation.cc
+ operations/COM_MixOperation.h
+ operations/COM_ReadBufferOperation.cc
+ operations/COM_ReadBufferOperation.h
+ operations/COM_SetColorOperation.cc
+ operations/COM_SetColorOperation.h
+ operations/COM_SetValueOperation.cc
+ operations/COM_SetValueOperation.h
+ operations/COM_SetVectorOperation.cc
+ operations/COM_SetVectorOperation.h
+ operations/COM_WriteBufferOperation.cc
+ operations/COM_WriteBufferOperation.h
+
+ operations/COM_MathBaseOperation.cc
+ operations/COM_MathBaseOperation.h
+
+ operations/COM_AlphaOverKeyOperation.cc
+ operations/COM_AlphaOverKeyOperation.h
+ operations/COM_AlphaOverMixedOperation.cc
+ operations/COM_AlphaOverMixedOperation.h
+ operations/COM_AlphaOverPremultiplyOperation.cc
+ operations/COM_AlphaOverPremultiplyOperation.h
+
+ operations/COM_ColorBalanceASCCDLOperation.cc
+ operations/COM_ColorBalanceASCCDLOperation.h
+ operations/COM_ColorBalanceLGGOperation.cc
+ operations/COM_ColorBalanceLGGOperation.h
+ operations/COM_InvertOperation.cc
+ operations/COM_InvertOperation.h
+ operations/COM_MapRangeOperation.cc
+ operations/COM_MapRangeOperation.h
+ operations/COM_MapValueOperation.cc
+ operations/COM_MapValueOperation.h
+ operations/COM_SetAlphaMultiplyOperation.cc
+ operations/COM_SetAlphaMultiplyOperation.h
+ operations/COM_SetAlphaReplaceOperation.cc
+ operations/COM_SetAlphaReplaceOperation.h
+
+ # Distort operation
+ operations/COM_DisplaceOperation.cc
+ operations/COM_DisplaceOperation.h
+ operations/COM_DisplaceSimpleOperation.cc
+ operations/COM_DisplaceSimpleOperation.h
+ operations/COM_FlipOperation.cc
+ operations/COM_FlipOperation.h
+ operations/COM_MapUVOperation.cc
+ operations/COM_MapUVOperation.h
+ operations/COM_PlaneCornerPinOperation.cc
+ operations/COM_PlaneCornerPinOperation.h
+ operations/COM_PlaneDistortCommonOperation.cc
+ operations/COM_PlaneDistortCommonOperation.h
+ operations/COM_PlaneTrackOperation.cc
+ operations/COM_PlaneTrackOperation.h
+ operations/COM_ProjectorLensDistortionOperation.cc
+ operations/COM_ProjectorLensDistortionOperation.h
+ operations/COM_RotateOperation.cc
+ operations/COM_RotateOperation.h
+ operations/COM_ScaleOperation.cc
+ operations/COM_ScaleOperation.h
+ operations/COM_ScreenLensDistortionOperation.cc
+ operations/COM_ScreenLensDistortionOperation.h
+ operations/COM_TransformOperation.cc
+ operations/COM_TransformOperation.h
+ operations/COM_TranslateOperation.cc
+ operations/COM_TranslateOperation.h
+ operations/COM_WrapOperation.cc
+ operations/COM_WrapOperation.h
+
+ # Filter operations
+ operations/COM_ConvolutionEdgeFilterOperation.cc
+ operations/COM_ConvolutionEdgeFilterOperation.h
+ operations/COM_ConvolutionFilterOperation.cc
+ operations/COM_ConvolutionFilterOperation.h
+ operations/COM_DenoiseOperation.cc
+ operations/COM_DenoiseOperation.h
+ operations/COM_DespeckleOperation.cc
+ operations/COM_DespeckleOperation.h
+ operations/COM_DilateErodeOperation.cc
+ operations/COM_DilateErodeOperation.h
+ operations/COM_GlareBaseOperation.cc
+ operations/COM_GlareBaseOperation.h
+ operations/COM_GlareFogGlowOperation.cc
+ operations/COM_GlareFogGlowOperation.h
+ operations/COM_GlareGhostOperation.cc
+ operations/COM_GlareGhostOperation.h
+ operations/COM_GlareSimpleStarOperation.cc
+ operations/COM_GlareSimpleStarOperation.h
+ operations/COM_GlareStreaksOperation.cc
+ operations/COM_GlareStreaksOperation.h
+ operations/COM_GlareThresholdOperation.cc
+ operations/COM_GlareThresholdOperation.h
+ operations/COM_InpaintOperation.cc
+ operations/COM_InpaintOperation.h
+ operations/COM_SetSamplerOperation.cc
+ operations/COM_SetSamplerOperation.h
+
+
+ # Convert operations
+ operations/COM_ConvertOperation.cc
+ operations/COM_ConvertOperation.h
+ operations/COM_IDMaskOperation.cc
+ operations/COM_IDMaskOperation.h
+
+ operations/COM_ConvertColorSpaceOperation.cc
+ operations/COM_ConvertColorSpaceOperation.h
+ operations/COM_DotproductOperation.cc
+ operations/COM_DotproductOperation.h
+
+ # Matte operation
+ operations/COM_BoxMaskOperation.cc
+ operations/COM_BoxMaskOperation.h
+ operations/COM_EllipseMaskOperation.cc
+ operations/COM_EllipseMaskOperation.h
+
+ operations/COM_ConvertColorProfileOperation.cc
+ operations/COM_ConvertColorProfileOperation.h
+ operations/COM_MovieClipOperation.cc
+ operations/COM_MovieClipOperation.h
+
+ operations/COM_AntiAliasOperation.cc
+ operations/COM_AntiAliasOperation.h
+
+ operations/COM_MaskOperation.cc
+ operations/COM_MaskOperation.h
+ )
-if(WITH_UNITY_BUILD)
- set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON)
- set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10)
-endif()
+ set(LIB
+ bf_blenkernel
+ bf_blenlib
+ extern_clew
+ )
-if(COMMAND target_precompile_headers)
- target_precompile_headers(bf_compositor PRIVATE COM_precomp.h)
-endif()
+ list(APPEND INC
+ ${CMAKE_CURRENT_BINARY_DIR}/operations
+ )
-if(CXX_WARN_NO_SUGGEST_OVERRIDE)
- target_compile_options(bf_compositor PRIVATE "-Wsuggest-override")
-endif()
+ data_to_c(
+ ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl
+ ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h
+ SRC
+ )
-add_dependencies(bf_compositor smaa_areatex_header)
+ add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS)
-if(WITH_GTESTS)
- set(TEST_SRC
- tests/COM_BufferArea_test.cc
- tests/COM_BufferRange_test.cc
- tests/COM_BuffersIterator_test.cc
- tests/COM_NodeOperation_test.cc
+ 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
)
- set(TEST_INC
+ add_custom_target(smaa_areatex_header
+ SOURCES ${GENSRC}
)
- set(TEST_LIB
- bf_compositor
+ list(APPEND SRC
+ ${GENSRC}
)
- include(GTestTesting)
- blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
-endif()
+ unset(GENSRC)
+ unset(GENSRC_DIR)
+
+ if(WITH_OPENIMAGEDENOISE)
+ add_definitions(-DWITH_OPENIMAGEDENOISE)
+ add_definitions(-DOIDN_STATIC_LIB)
+ list(APPEND INC_SYS
+ ${OPENIMAGEDENOISE_INCLUDE_DIRS}
+ ${TBB_INCLUDE_DIRS}
+ )
+ list(APPEND LIB
+ ${OPENIMAGEDENOISE_LIBRARIES}
+ ${TBB_LIBRARIES}
+ )
+ endif()
+
+ blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
+
+ if(WITH_UNITY_BUILD)
+ set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON)
+ set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10)
+ endif()
+
+ if(COMMAND target_precompile_headers)
+ target_precompile_headers(bf_compositor PRIVATE COM_precomp.h)
+ endif()
+
+ if(CXX_WARN_NO_SUGGEST_OVERRIDE)
+ target_compile_options(bf_compositor PRIVATE "-Wsuggest-override")
+ endif()
+
+ add_dependencies(bf_compositor smaa_areatex_header)
+
+ if(WITH_GTESTS)
+ set(TEST_SRC
+ tests/COM_BufferArea_test.cc
+ tests/COM_BufferRange_test.cc
+ tests/COM_BuffersIterator_test.cc
+ tests/COM_NodeOperation_test.cc
+ )
+ set(TEST_INC
+ )
+ set(TEST_LIB
+ bf_compositor
+ )
+ include(GTestTesting)
+ blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
+ endif()
+
+ # Needed so we can use dna_type_offsets.h for defaults initialization.
+ add_dependencies(bf_compositor bf_dna)
+ # RNA_prototypes.h
+ add_dependencies(bf_compositor bf_rna)
-# Needed so we can use dna_type_offsets.h for defaults initialization.
-add_dependencies(bf_compositor bf_dna)
-# RNA_prototypes.h
-add_dependencies(bf_compositor bf_rna)
+# End WITH_COMPOSITOR_CPU.
+endif()
diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h
index 0fdd7647f8d..fd53460f854 100644
--- a/source/blender/compositor/COM_compositor.h
+++ b/source/blender/compositor/COM_compositor.h
@@ -12,8 +12,8 @@ extern "C" {
/* Keep ascii art. */
/* clang-format off */
+
/**
- *
* \defgroup Model The data model of the compositor
* \ingroup compositor
* \defgroup Memory The memory management stuff
diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h
index ae910c28342..364f9439366 100644
--- a/source/blender/compositor/intern/COM_Node.h
+++ b/source/blender/compositor/intern/COM_Node.h
@@ -31,7 +31,7 @@ class Node {
/**
* \brief stores the reference to the SDNA bNode struct
*/
- bNode *editor_node_;
+ const bNode *editor_node_;
/**
* \brief Is this node part of the active group
@@ -61,7 +61,7 @@ class Node {
/**
* \brief get the reference to the SDNA bNode struct
*/
- bNode *get_bnode() const
+ const bNode *get_bnode() const
{
return editor_node_;
}
diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc
index 5b0f7a1d0e6..8f9f6f07395 100644
--- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc
+++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc
@@ -14,10 +14,10 @@ void AlphaOverNode::convert_to_operations(NodeConverter &converter,
{
NodeInput *color1Socket = this->get_input_socket(1);
NodeInput *color2Socket = this->get_input_socket(2);
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
MixBaseOperation *convert_prog;
- NodeTwoFloats *ntf = (NodeTwoFloats *)editor_node->storage;
+ const NodeTwoFloats *ntf = (const NodeTwoFloats *)editor_node->storage;
if (ntf->x != 0.0f) {
AlphaOverMixedOperation *mix_operation = new AlphaOverMixedOperation();
mix_operation->setX(ntf->x);
diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
index e201720c53b..eb71b70d0bb 100644
--- a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
+++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc
@@ -9,8 +9,8 @@ namespace blender::compositor {
void AntiAliasingNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *node = this->get_bnode();
- NodeAntiAliasingData *data = (NodeAntiAliasingData *)node->storage;
+ const bNode *node = this->get_bnode();
+ const NodeAntiAliasingData *data = (const NodeAntiAliasingData *)node->storage;
/* Edge Detection (First Pass) */
SMAAEdgeDetectionOperation *operation1 = nullptr;
diff --git a/source/blender/compositor/nodes/COM_BlurNode.cc b/source/blender/compositor/nodes/COM_BlurNode.cc
index a8148d7abd7..9377cfa783c 100644
--- a/source/blender/compositor/nodes/COM_BlurNode.cc
+++ b/source/blender/compositor/nodes/COM_BlurNode.cc
@@ -22,8 +22,8 @@ BlurNode::BlurNode(bNode *editor_node) : Node(editor_node)
void BlurNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
- NodeBlurData *data = (NodeBlurData *)editor_node->storage;
+ const bNode *editor_node = this->get_bnode();
+ const NodeBlurData *data = (const NodeBlurData *)editor_node->storage;
NodeInput *input_size_socket = this->get_input_socket(1);
bool connected_size_socket = input_size_socket->is_linked();
diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.cc b/source/blender/compositor/nodes/COM_BokehBlurNode.cc
index 31258ddb56a..ebdc82b0d19 100644
--- a/source/blender/compositor/nodes/COM_BokehBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_BokehBlurNode.cc
@@ -15,7 +15,7 @@ BokehBlurNode::BokehBlurNode(bNode *editor_node) : Node(editor_node)
void BokehBlurNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *b_node = this->get_bnode();
+ const bNode *b_node = this->get_bnode();
NodeInput *input_size_socket = this->get_input_socket(2);
diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.cc b/source/blender/compositor/nodes/COM_BokehImageNode.cc
index 6bc56aa5184..f25ac47fef9 100644
--- a/source/blender/compositor/nodes/COM_BokehImageNode.cc
+++ b/source/blender/compositor/nodes/COM_BokehImageNode.cc
@@ -15,7 +15,7 @@ void BokehImageNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
BokehImageOperation *operation = new BokehImageOperation();
- operation->set_data((NodeBokehImage *)this->get_bnode()->storage);
+ operation->set_data((const NodeBokehImage *)this->get_bnode()->storage);
converter.add_operation(operation);
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));
diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
index 41717804dba..c1a1b72d063 100644
--- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc
@@ -22,7 +22,7 @@ void BoxMaskNode::convert_to_operations(NodeConverter &converter,
BoxMaskOperation *operation;
operation = new BoxMaskOperation();
- operation->set_data((NodeBoxMask *)this->get_bnode()->storage);
+ operation->set_data((const NodeBoxMask *)this->get_bnode()->storage);
operation->set_mask_type(this->get_bnode()->custom1);
converter.add_operation(operation);
diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.cc b/source/blender/compositor/nodes/COM_BrightnessNode.cc
index 88efd541fe9..a7877ca9378 100644
--- a/source/blender/compositor/nodes/COM_BrightnessNode.cc
+++ b/source/blender/compositor/nodes/COM_BrightnessNode.cc
@@ -14,7 +14,7 @@ BrightnessNode::BrightnessNode(bNode *editor_node) : Node(editor_node)
void BrightnessNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *bnode = this->get_bnode();
+ const bNode *bnode = this->get_bnode();
BrightnessOperation *operation = new BrightnessOperation();
operation->set_use_premultiply((bnode->custom1 & 1) != 0);
converter.add_operation(operation);
diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
index 81fc638970f..6a0a28cd171 100644
--- a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc
@@ -16,14 +16,14 @@ ChannelMatteNode::ChannelMatteNode(bNode *editor_node) : Node(editor_node)
void ChannelMatteNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *node = this->get_bnode();
+ const bNode *node = this->get_bnode();
NodeInput *input_socket_image = this->get_input_socket(0);
NodeOutput *output_socket_image = this->get_output_socket(0);
NodeOutput *output_socket_matte = this->get_output_socket(1);
NodeOperation *convert = nullptr, *inv_convert = nullptr;
- /* colorspace */
+ /* color-space */
switch (node->custom1) {
case CMP_NODE_CHANNEL_MATTE_CS_RGB:
break;
diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
index cbc5a06ce11..35bf6210da5 100644
--- a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc
@@ -16,7 +16,7 @@ ChromaMatteNode::ChromaMatteNode(bNode *editor_node) : Node(editor_node)
void ChromaMatteNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editorsnode = get_bnode();
+ const bNode *editorsnode = get_bnode();
NodeInput *input_socket_image = this->get_input_socket(0);
NodeInput *input_socket_key = this->get_input_socket(1);
diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
index cb009ecc634..f3f5aa4e49b 100644
--- a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc
@@ -15,7 +15,7 @@ ColorBalanceNode::ColorBalanceNode(bNode *editor_node) : Node(editor_node)
void ColorBalanceNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *node = this->get_bnode();
+ const bNode *node = this->get_bnode();
NodeColorBalance *n = (NodeColorBalance *)node->storage;
NodeInput *input_socket = this->get_input_socket(0);
diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
index 2497f1d57bc..3e021296fee 100644
--- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc
@@ -14,7 +14,7 @@ ColorCorrectionNode::ColorCorrectionNode(bNode *editor_node) : Node(editor_node)
void ColorCorrectionNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editor_node = get_bnode();
+ const bNode *editor_node = get_bnode();
ColorCorrectionOperation *operation = new ColorCorrectionOperation();
operation->set_data((NodeColorCorrection *)editor_node->storage);
diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.cc b/source/blender/compositor/nodes/COM_ColorCurveNode.cc
index ba81d375846..5275bfcab84 100644
--- a/source/blender/compositor/nodes/COM_ColorCurveNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorCurveNode.cc
@@ -16,7 +16,7 @@ void ColorCurveNode::convert_to_operations(NodeConverter &converter,
{
if (this->get_input_socket(2)->is_linked() || this->get_input_socket(3)->is_linked()) {
ColorCurveOperation *operation = new ColorCurveOperation();
- operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage);
+ operation->set_curve_mapping((const CurveMapping *)this->get_bnode()->storage);
converter.add_operation(operation);
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
@@ -33,7 +33,7 @@ void ColorCurveNode::convert_to_operations(NodeConverter &converter,
operation->set_black_level(col);
this->get_input_socket(3)->get_editor_value_color(col);
operation->set_white_level(col);
- operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage);
+ operation->set_curve_mapping((const CurveMapping *)this->get_bnode()->storage);
converter.add_operation(operation);
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.cc b/source/blender/compositor/nodes/COM_ColorMatteNode.cc
index f8fccd53a91..163ca4c7573 100644
--- a/source/blender/compositor/nodes/COM_ColorMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorMatteNode.cc
@@ -16,7 +16,7 @@ ColorMatteNode::ColorMatteNode(bNode *editor_node) : Node(editor_node)
void ColorMatteNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editorsnode = get_bnode();
+ const bNode *editorsnode = get_bnode();
NodeInput *input_socket_image = this->get_input_socket(0);
NodeInput *input_socket_key = this->get_input_socket(1);
diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.cc b/source/blender/compositor/nodes/COM_ColorRampNode.cc
index fd91678800a..c2569a27fe6 100644
--- a/source/blender/compositor/nodes/COM_ColorRampNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorRampNode.cc
@@ -18,7 +18,7 @@ void ColorRampNode::convert_to_operations(NodeConverter &converter,
NodeInput *input_socket = this->get_input_socket(0);
NodeOutput *output_socket = this->get_output_socket(0);
NodeOutput *output_socket_alpha = this->get_output_socket(1);
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
ColorRampOperation *operation = new ColorRampOperation();
operation->set_color_band((ColorBand *)editor_node->storage);
diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.cc b/source/blender/compositor/nodes/COM_ColorSpillNode.cc
index 118879ebdcc..448c3db6c46 100644
--- a/source/blender/compositor/nodes/COM_ColorSpillNode.cc
+++ b/source/blender/compositor/nodes/COM_ColorSpillNode.cc
@@ -14,7 +14,7 @@ ColorSpillNode::ColorSpillNode(bNode *editor_node) : Node(editor_node)
void ColorSpillNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editorsnode = get_bnode();
+ const bNode *editorsnode = get_bnode();
NodeInput *input_socket_image = this->get_input_socket(0);
NodeInput *input_socket_fac = this->get_input_socket(1);
diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc
index ca2c59478fd..36ff24cb9c3 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNode.cc
+++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc
@@ -40,7 +40,7 @@ void CombineColorNode::convert_to_operations(NodeConverter &converter,
converter.map_input_socket(input_bsocket, operation->get_input_socket(2));
converter.map_input_socket(input_asocket, operation->get_input_socket(3));
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage;
NodeOperation *color_conv = nullptr;
diff --git a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc
index d5ba379bfc2..8ab84715179 100644
--- a/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc
+++ b/source/blender/compositor/nodes/COM_CombineColorNodeLegacy.cc
@@ -65,7 +65,7 @@ NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*
NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const
{
ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation();
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
operation->set_mode(editor_node->custom1);
return operation;
}
diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cc b/source/blender/compositor/nodes/COM_CompositorNode.cc
index d9009e313dd..444dcbd5c6c 100644
--- a/source/blender/compositor/nodes/COM_CompositorNode.cc
+++ b/source/blender/compositor/nodes/COM_CompositorNode.cc
@@ -14,7 +14,7 @@ CompositorNode::CompositorNode(bNode *editor_node) : Node(editor_node)
void CompositorNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
bool is_active = (editor_node->flag & NODE_DO_OUTPUT_RECALC) || context.is_rendering();
bool ignore_alpha = (editor_node->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0;
diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
index 690be1c4551..98fae0a47bf 100644
--- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
+++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc
@@ -10,7 +10,7 @@ void ConvertAlphaNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
NodeOperation *operation = nullptr;
- bNode *node = this->get_bnode();
+ const bNode *node = this->get_bnode();
/* value hardcoded in rna_nodetree.c */
if (node->custom1 == 1) {
diff --git a/source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc b/source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc
index 219b3acbd04..7d557de66e4 100644
--- a/source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc
+++ b/source/blender/compositor/nodes/COM_ConvertColorSpaceNode.cc
@@ -27,7 +27,7 @@ ConvertColorSpaceNode::ConvertColorSpaceNode(bNode *editorNode) : Node(editorNod
void ConvertColorSpaceNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &UNUSED(context)) const
{
- bNode *b_node = get_bnode();
+ const bNode *b_node = get_bnode();
NodeInput *inputSocketImage = this->get_input_socket(0);
NodeOutput *outputSocketImage = this->get_output_socket(0);
@@ -50,7 +50,7 @@ void ConvertColorSpaceNode::convert_to_operations(NodeConverter &converter,
bool ConvertColorSpaceNode::performs_conversion(NodeConvertColorSpace &settings) const
{
- bNode *b_node = get_bnode();
+ const bNode *b_node = get_bnode();
if (IMB_colormanagement_space_name_is_data(settings.from_color_space)) {
CLOG_INFO(&LOG,
diff --git a/source/blender/compositor/nodes/COM_CropNode.cc b/source/blender/compositor/nodes/COM_CropNode.cc
index fd07b028a01..849fb80a8a8 100644
--- a/source/blender/compositor/nodes/COM_CropNode.cc
+++ b/source/blender/compositor/nodes/COM_CropNode.cc
@@ -14,7 +14,7 @@ CropNode::CropNode(bNode *editor_node) : Node(editor_node)
void CropNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *node = get_bnode();
+ const bNode *node = get_bnode();
NodeTwoXYs *crop_settings = (NodeTwoXYs *)node->storage;
bool relative = (bool)node->custom2;
bool crop_image = (bool)node->custom1;
diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
index b1cae5bfc24..42d699af01b 100644
--- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc
+++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc
@@ -23,8 +23,9 @@ void CryptomatteBaseNode::convert_to_operations(NodeConverter &converter,
{
NodeOutput *output_image_socket = this->get_output_socket(0);
- bNode *node = this->get_bnode();
- NodeCryptomatte *cryptomatte_settings = static_cast<NodeCryptomatte *>(node->storage);
+ const bNode *node = this->get_bnode();
+ const NodeCryptomatte *cryptomatte_settings = static_cast<const NodeCryptomatte *>(
+ node->storage);
CryptomatteOperation *cryptomatte_operation = create_cryptomatte_operation(
converter, context, *node, cryptomatte_settings);
diff --git a/source/blender/compositor/nodes/COM_DefocusNode.cc b/source/blender/compositor/nodes/COM_DefocusNode.cc
index 26bb79a5846..40ccf0fad15 100644
--- a/source/blender/compositor/nodes/COM_DefocusNode.cc
+++ b/source/blender/compositor/nodes/COM_DefocusNode.cc
@@ -19,8 +19,8 @@ DefocusNode::DefocusNode(bNode *editor_node) : Node(editor_node)
void DefocusNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *node = this->get_bnode();
- NodeDefocus *data = (NodeDefocus *)node->storage;
+ const bNode *node = this->get_bnode();
+ const NodeDefocus *data = (const NodeDefocus *)node->storage;
Scene *scene = node->id ? (Scene *)node->id : context.get_scene();
Object *camob = scene ? scene->camera : nullptr;
diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cc b/source/blender/compositor/nodes/COM_DenoiseNode.cc
index 301cb123359..81f45da475d 100644
--- a/source/blender/compositor/nodes/COM_DenoiseNode.cc
+++ b/source/blender/compositor/nodes/COM_DenoiseNode.cc
@@ -19,8 +19,8 @@ void DenoiseNode::convert_to_operations(NodeConverter &converter,
return;
}
- bNode *node = this->get_bnode();
- NodeDenoise *denoise = (NodeDenoise *)node->storage;
+ const bNode *node = this->get_bnode();
+ const NodeDenoise *denoise = (const NodeDenoise *)node->storage;
DenoiseOperation *operation = new DenoiseOperation();
converter.add_operation(operation);
diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.cc b/source/blender/compositor/nodes/COM_DespeckleNode.cc
index cbdff384eda..c2601435f34 100644
--- a/source/blender/compositor/nodes/COM_DespeckleNode.cc
+++ b/source/blender/compositor/nodes/COM_DespeckleNode.cc
@@ -14,7 +14,7 @@ DespeckleNode::DespeckleNode(bNode *editor_node) : Node(editor_node)
void DespeckleNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
NodeInput *input_socket = this->get_input_socket(0);
NodeInput *input_image_socket = this->get_input_socket(1);
NodeOutput *output_socket = this->get_output_socket(0);
diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
index 7c5d1f69fb1..7ec90cb33d8 100644
--- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc
@@ -19,7 +19,7 @@ void DifferenceMatteNode::convert_to_operations(NodeConverter &converter,
NodeInput *input_socket2 = this->get_input_socket(1);
NodeOutput *output_socket_image = this->get_output_socket(0);
NodeOutput *output_socket_matte = this->get_output_socket(1);
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
DifferenceMatteOperation *operation_set = new DifferenceMatteOperation();
operation_set->set_settings((NodeChroma *)editor_node->storage);
diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cc b/source/blender/compositor/nodes/COM_DilateErodeNode.cc
index ee3ad8f4639..1b8c9d3b0a0 100644
--- a/source/blender/compositor/nodes/COM_DilateErodeNode.cc
+++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cc
@@ -27,9 +27,8 @@ DilateErodeNode::DilateErodeNode(bNode *editor_node) : Node(editor_node)
void DilateErodeNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
-
- bNode *editor_node = this->get_bnode();
- if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE_THRESH) {
+ const bNode *editor_node = this->get_bnode();
+ if (editor_node->custom1 == CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD) {
DilateErodeThresholdOperation *operation = new DilateErodeThresholdOperation();
operation->set_distance(editor_node->custom2);
operation->set_inset(editor_node->custom3);
@@ -48,7 +47,7 @@ void DilateErodeNode::convert_to_operations(NodeConverter &converter,
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));
}
}
- else if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE) {
+ else if (editor_node->custom1 == CMP_NODE_DILATE_ERODE_DISTANCE) {
if (editor_node->custom2 > 0) {
DilateDistanceOperation *operation = new DilateDistanceOperation();
operation->set_distance(editor_node->custom2);
@@ -66,7 +65,7 @@ void DilateErodeNode::convert_to_operations(NodeConverter &converter,
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));
}
}
- else if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE_FEATHER) {
+ else if (editor_node->custom1 == CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER) {
/* this uses a modified gaussian blur function otherwise its far too slow */
eCompositorQuality quality = context.get_quality();
diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
index b9477cc6783..52df671674b 100644
--- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc
@@ -14,7 +14,7 @@ DirectionalBlurNode::DirectionalBlurNode(bNode *editor_node) : Node(editor_node)
void DirectionalBlurNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- NodeDBlurData *data = (NodeDBlurData *)this->get_bnode()->storage;
+ const NodeDBlurData *data = (const NodeDBlurData *)this->get_bnode()->storage;
DirectionalBlurOperation *operation = new DirectionalBlurOperation();
operation->set_quality(context.get_quality());
operation->set_data(data);
diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
index ea0327d3d46..249fc48ab67 100644
--- a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc
@@ -16,8 +16,8 @@ DistanceMatteNode::DistanceMatteNode(bNode *editor_node) : Node(editor_node)
void DistanceMatteNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editorsnode = get_bnode();
- NodeChroma *storage = (NodeChroma *)editorsnode->storage;
+ const bNode *editorsnode = this->get_bnode();
+ const NodeChroma *storage = (const NodeChroma *)editorsnode->storage;
NodeInput *input_socket_image = this->get_input_socket(0);
NodeInput *input_socket_key = this->get_input_socket(1);
diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
index eb4510a104c..7bd14d7a8d4 100644
--- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc
@@ -15,7 +15,7 @@ void DoubleEdgeMaskNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
DoubleEdgeMaskOperation *operation;
- bNode *bnode = this->get_bnode();
+ const bNode *bnode = this->get_bnode();
operation = new DoubleEdgeMaskOperation();
operation->set_adjecent_only(bnode->custom1);
diff --git a/source/blender/compositor/nodes/COM_FilterNode.cc b/source/blender/compositor/nodes/COM_FilterNode.cc
index f2efa8caefd..dce08b4cf2c 100644
--- a/source/blender/compositor/nodes/COM_FilterNode.cc
+++ b/source/blender/compositor/nodes/COM_FilterNode.cc
@@ -21,7 +21,7 @@ void FilterNode::convert_to_operations(NodeConverter &converter,
ConvolutionFilterOperation *operation = nullptr;
switch (this->get_bnode()->custom1) {
- case CMP_FILT_SOFT:
+ case CMP_NODE_FILTER_SOFT:
operation = new ConvolutionFilterOperation();
operation->set3x3Filter(1 / 16.0f,
2 / 16.0f,
@@ -33,11 +33,11 @@ void FilterNode::convert_to_operations(NodeConverter &converter,
2 / 16.0f,
1 / 16.0f);
break;
- case CMP_FILT_SHARP_BOX:
+ case CMP_NODE_FILTER_SHARP_BOX:
operation = new ConvolutionFilterOperation();
operation->set3x3Filter(-1, -1, -1, -1, 9, -1, -1, -1, -1);
break;
- case CMP_FILT_LAPLACE:
+ case CMP_NODE_FILTER_LAPLACE:
operation = new ConvolutionEdgeFilterOperation();
operation->set3x3Filter(-1 / 8.0f,
-1 / 8.0f,
@@ -49,23 +49,23 @@ void FilterNode::convert_to_operations(NodeConverter &converter,
-1 / 8.0f,
-1 / 8.0f);
break;
- case CMP_FILT_SOBEL:
+ case CMP_NODE_FILTER_SOBEL:
operation = new ConvolutionEdgeFilterOperation();
operation->set3x3Filter(1, 2, 1, 0, 0, 0, -1, -2, -1);
break;
- case CMP_FILT_PREWITT:
+ case CMP_NODE_FILTER_PREWITT:
operation = new ConvolutionEdgeFilterOperation();
operation->set3x3Filter(1, 1, 1, 0, 0, 0, -1, -1, -1);
break;
- case CMP_FILT_KIRSCH:
+ case CMP_NODE_FILTER_KIRSCH:
operation = new ConvolutionEdgeFilterOperation();
operation->set3x3Filter(5, 5, 5, -3, -3, -3, -2, -2, -2);
break;
- case CMP_FILT_SHADOW:
+ case CMP_NODE_FILTER_SHADOW:
operation = new ConvolutionFilterOperation();
operation->set3x3Filter(1, 2, 1, 0, 1, 0, -1, -2, -1);
break;
- case CMP_FILT_SHARP_DIAMOND:
+ case CMP_NODE_FILTER_SHARP_DIAMOND:
operation = new ConvolutionFilterOperation();
operation->set3x3Filter(0, -1, 0, -1, 5, -1, 0, -1, 0);
break;
diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc
index 0d7be486b9b..d80e6f9543f 100644
--- a/source/blender/compositor/nodes/COM_GlareNode.cc
+++ b/source/blender/compositor/nodes/COM_GlareNode.cc
@@ -20,8 +20,8 @@ GlareNode::GlareNode(bNode *editor_node) : Node(editor_node)
void GlareNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *node = this->get_bnode();
- NodeGlare *glare = (NodeGlare *)node->storage;
+ const bNode *node = this->get_bnode();
+ const NodeGlare *glare = (const NodeGlare *)node->storage;
GlareBaseOperation *glareoperation = nullptr;
switch (glare->type) {
diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
index 8ae415b9c28..163cddb6f46 100644
--- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
+++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc
@@ -21,7 +21,7 @@ void HueSaturationValueCorrectNode::convert_to_operations(
NodeInput *value_socket = this->get_input_socket(0);
NodeInput *color_socket = this->get_input_socket(1);
NodeOutput *output_socket = this->get_output_socket(0);
- bNode *editorsnode = get_bnode();
+ const bNode *editorsnode = get_bnode();
CurveMapping *storage = (CurveMapping *)editorsnode->storage;
ConvertRGBToHSVOperation *rgbToHSV = new ConvertRGBToHSVOperation();
diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc
index f9c284b916c..a1e08bee0cb 100644
--- a/source/blender/compositor/nodes/COM_IDMaskNode.cc
+++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc
@@ -14,7 +14,7 @@ IDMaskNode::IDMaskNode(bNode *editor_node) : Node(editor_node)
void IDMaskNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *bnode = this->get_bnode();
+ const bNode *bnode = this->get_bnode();
IDMaskOperation *operation;
operation = new IDMaskOperation();
diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc
index 35031888d5c..a7cc6bf39df 100644
--- a/source/blender/compositor/nodes/COM_ImageNode.cc
+++ b/source/blender/compositor/nodes/COM_ImageNode.cc
@@ -55,7 +55,7 @@ void ImageNode::convert_to_operations(NodeConverter &converter,
{
/** Image output */
NodeOutput *output_image = this->get_output_socket(0);
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
Image *image = (Image *)editor_node->id;
ImageUser *imageuser = (ImageUser *)editor_node->storage;
int framenumber = context.get_framenumber();
diff --git a/source/blender/compositor/nodes/COM_InpaintNode.cc b/source/blender/compositor/nodes/COM_InpaintNode.cc
index 1b899163e4d..dd80380387f 100644
--- a/source/blender/compositor/nodes/COM_InpaintNode.cc
+++ b/source/blender/compositor/nodes/COM_InpaintNode.cc
@@ -15,7 +15,7 @@ void InpaintNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
/* if (editor_node->custom1 == CMP_NODE_INPAINT_SIMPLE) { */
if (true) {
diff --git a/source/blender/compositor/nodes/COM_InvertNode.cc b/source/blender/compositor/nodes/COM_InvertNode.cc
index 002136e5c2a..ce5c4f48f9e 100644
--- a/source/blender/compositor/nodes/COM_InvertNode.cc
+++ b/source/blender/compositor/nodes/COM_InvertNode.cc
@@ -16,7 +16,7 @@ void InvertNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
InvertOperation *operation = new InvertOperation();
- bNode *node = this->get_bnode();
+ const bNode *node = this->get_bnode();
operation->set_color(node->custom1 & CMP_CHAN_RGB);
operation->set_alpha(node->custom1 & CMP_CHAN_A);
converter.add_operation(operation);
diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cc b/source/blender/compositor/nodes/COM_KeyingNode.cc
index 10e6b5597f2..a9138f026f7 100644
--- a/source/blender/compositor/nodes/COM_KeyingNode.cc
+++ b/source/blender/compositor/nodes/COM_KeyingNode.cc
@@ -205,8 +205,8 @@ NodeOperationOutput *KeyingNode::setup_clip(NodeConverter &converter,
void KeyingNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
- NodeKeyingData *keying_data = (NodeKeyingData *)editor_node->storage;
+ const bNode *editor_node = this->get_bnode();
+ const NodeKeyingData *keying_data = (const NodeKeyingData *)editor_node->storage;
NodeInput *input_image = this->get_input_socket(0);
NodeInput *input_screen = this->get_input_socket(1);
diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
index 1e159e11966..7470d49bc1b 100644
--- a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
+++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc
@@ -14,7 +14,7 @@ KeyingScreenNode::KeyingScreenNode(bNode *editor_node) : Node(editor_node)
void KeyingScreenNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
MovieClip *clip = (MovieClip *)editor_node->id;
NodeKeyingScreenData *keyingscreen_data = (NodeKeyingScreenData *)editor_node->storage;
diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.cc b/source/blender/compositor/nodes/COM_LensDistortionNode.cc
index 3fe1125382e..5ed97c614ed 100644
--- a/source/blender/compositor/nodes/COM_LensDistortionNode.cc
+++ b/source/blender/compositor/nodes/COM_LensDistortionNode.cc
@@ -15,7 +15,7 @@ LensDistortionNode::LensDistortionNode(bNode *editor_node) : Node(editor_node)
void LensDistortionNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
NodeLensDist *data = (NodeLensDist *)editor_node->storage;
if (data->proj) {
ProjectorLensDistortionOperation *operation = new ProjectorLensDistortionOperation();
diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
index 00ae679581c..06e8eb58c4d 100644
--- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
+++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc
@@ -15,7 +15,7 @@ LuminanceMatteNode::LuminanceMatteNode(bNode *editor_node) : Node(editor_node)
void LuminanceMatteNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *editorsnode = get_bnode();
+ const bNode *editorsnode = get_bnode();
NodeInput *input_socket = this->get_input_socket(0);
NodeOutput *output_socket_image = this->get_output_socket(0);
NodeOutput *output_socket_matte = this->get_output_socket(1);
diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc
index 54a24a94ad1..ed9bff657f3 100644
--- a/source/blender/compositor/nodes/COM_MapUVNode.cc
+++ b/source/blender/compositor/nodes/COM_MapUVNode.cc
@@ -14,7 +14,7 @@ MapUVNode::MapUVNode(bNode *editor_node) : Node(editor_node)
void MapUVNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- bNode *node = this->get_bnode();
+ const bNode *node = this->get_bnode();
MapUVOperation *operation = new MapUVOperation();
operation->set_alpha((float)node->custom1);
diff --git a/source/blender/compositor/nodes/COM_MapValueNode.cc b/source/blender/compositor/nodes/COM_MapValueNode.cc
index be7289e61b1..8b3c3cc3ec6 100644
--- a/source/blender/compositor/nodes/COM_MapValueNode.cc
+++ b/source/blender/compositor/nodes/COM_MapValueNode.cc
@@ -15,7 +15,7 @@ MapValueNode::MapValueNode(bNode *editor_node) : Node(editor_node)
void MapValueNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- TexMapping *storage = (TexMapping *)this->get_bnode()->storage;
+ const TexMapping *storage = (const TexMapping *)this->get_bnode()->storage;
NodeInput *color_socket = this->get_input_socket(0);
NodeOutput *value_socket = this->get_output_socket(0);
diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc
index 01f98416a9e..f92e307328d 100644
--- a/source/blender/compositor/nodes/COM_MaskNode.cc
+++ b/source/blender/compositor/nodes/COM_MaskNode.cc
@@ -19,8 +19,8 @@ void MaskNode::convert_to_operations(NodeConverter &converter,
NodeOutput *output_mask = this->get_output_socket(0);
- bNode *editor_node = this->get_bnode();
- NodeMask *data = (NodeMask *)editor_node->storage;
+ const bNode *editor_node = this->get_bnode();
+ const NodeMask *data = (const NodeMask *)editor_node->storage;
Mask *mask = (Mask *)editor_node->id;
/* Always connect the output image. */
diff --git a/source/blender/compositor/nodes/COM_MixNode.cc b/source/blender/compositor/nodes/COM_MixNode.cc
index a44dfc6caa6..e9179d7063c 100644
--- a/source/blender/compositor/nodes/COM_MixNode.cc
+++ b/source/blender/compositor/nodes/COM_MixNode.cc
@@ -21,7 +21,7 @@ void MixNode::convert_to_operations(NodeConverter &converter,
NodeInput *color1Socket = this->get_input_socket(1);
NodeInput *color2Socket = this->get_input_socket(2);
NodeOutput *output_socket = this->get_output_socket(0);
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
bool use_alpha_premultiply = (this->get_bnode()->custom2 & 1) != 0;
bool use_clamp = (this->get_bnode()->custom2 & 2) != 0;
diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc
index d3f522fd8cd..e870cd7e0da 100644
--- a/source/blender/compositor/nodes/COM_MovieClipNode.cc
+++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc
@@ -29,7 +29,7 @@ void MovieClipNode::convert_to_operations(NodeConverter &converter,
NodeOutput *scale_movie_clip = this->get_output_socket(4);
NodeOutput *angle_movie_clip = this->get_output_socket(5);
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
MovieClip *movie_clip = (MovieClip *)editor_node->id;
MovieClipUser *movie_clip_user = (MovieClipUser *)editor_node->storage;
bool cache_frame = !context.is_rendering();
diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
index 41ecfc24a8a..7c6e19c03dd 100644
--- a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
+++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc
@@ -15,7 +15,7 @@ MovieDistortionNode::MovieDistortionNode(bNode *editor_node) : Node(editor_node)
void MovieDistortionNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *bnode = this->get_bnode();
+ const bNode *bnode = this->get_bnode();
MovieClip *clip = (MovieClip *)bnode->id;
NodeInput *input_socket = this->get_input_socket(0);
diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc
index f69511d88e6..c83bcf42efd 100644
--- a/source/blender/compositor/nodes/COM_OutputFileNode.cc
+++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc
@@ -37,6 +37,10 @@ void OutputFileNode::map_input_sockets(NodeConverter &converter,
void OutputFileNode::add_preview_to_first_linked_input(NodeConverter &converter) const
{
+ if (get_input_sockets().is_empty()) {
+ return;
+ }
+
NodeInput *first_socket = this->get_input_socket(0);
if (first_socket->is_linked()) {
converter.add_node_input_preview(first_socket);
@@ -46,7 +50,7 @@ void OutputFileNode::add_preview_to_first_linked_input(NodeConverter &converter)
void OutputFileNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- NodeImageMultiFile *storage = (NodeImageMultiFile *)this->get_bnode()->storage;
+ const NodeImageMultiFile *storage = (const NodeImageMultiFile *)this->get_bnode()->storage;
const bool is_multiview = (context.get_render_data()->scemode & R_MULTIVIEW) != 0;
add_preview_to_first_linked_input(converter);
@@ -95,8 +99,8 @@ void OutputFileNode::convert_to_operations(NodeConverter &converter,
if (input->is_linked()) {
NodeImageMultiFileSocket *sockdata =
(NodeImageMultiFileSocket *)input->get_bnode_socket()->storage;
- ImageFormatData *format = (sockdata->use_node_format ? &storage->format :
- &sockdata->format);
+ const ImageFormatData *format = (sockdata->use_node_format ? &storage->format :
+ &sockdata->format);
char path[FILE_MAX];
/* combine file path for the input */
diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
index a2037812677..308fd81a12d 100644
--- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
+++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc
@@ -15,7 +15,7 @@ PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editor_node) : Node(editor_nod
void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
MovieClip *clip = (MovieClip *)editor_node->id;
NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)editor_node->storage;
diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc
index 6eea61b1568..1d613a030d7 100644
--- a/source/blender/compositor/nodes/COM_ScaleNode.cc
+++ b/source/blender/compositor/nodes/COM_ScaleNode.cc
@@ -17,7 +17,7 @@ ScaleNode::ScaleNode(bNode *editor_node) : Node(editor_node)
void ScaleNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *bnode = this->get_bnode();
+ const bNode *bnode = this->get_bnode();
NodeInput *input_socket = this->get_input_socket(0);
NodeInput *input_xsocket = this->get_input_socket(1);
@@ -25,7 +25,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter,
NodeOutput *output_socket = this->get_output_socket(0);
switch (bnode->custom1) {
- case CMP_SCALE_RELATIVE: {
+ case CMP_NODE_SCALE_RELATIVE: {
ScaleRelativeOperation *operation = new ScaleRelativeOperation();
converter.add_operation(operation);
@@ -39,7 +39,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter,
break;
}
- case CMP_SCALE_SCENEPERCENT: {
+ case CMP_NODE_SCALE_RENDER_PERCENT: {
SetValueOperation *scale_factor_operation = new SetValueOperation();
scale_factor_operation->set_value(context.get_render_percentage_as_factor());
converter.add_operation(scale_factor_operation);
@@ -59,13 +59,14 @@ void ScaleNode::convert_to_operations(NodeConverter &converter,
break;
}
- case CMP_SCALE_RENDERPERCENT: {
+ case CMP_NODE_SCALE_RENDER_SIZE: {
const RenderData *rd = context.get_render_data();
const float render_size_factor = context.get_render_percentage_as_factor();
ScaleFixedSizeOperation *operation = new ScaleFixedSizeOperation();
/* framing options */
- operation->set_is_aspect((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_ASPECT) != 0);
- operation->set_is_crop((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_CROP) != 0);
+ operation->set_is_aspect(
+ ELEM(bnode->custom2, CMP_NODE_SCALE_RENDER_SIZE_FIT, CMP_NODE_SCALE_RENDER_SIZE_CROP));
+ operation->set_is_crop(bnode->custom2 == CMP_NODE_SCALE_RENDER_SIZE_CROP);
operation->set_offset(bnode->custom3, bnode->custom4);
operation->set_new_width(rd->xsch * render_size_factor);
operation->set_new_height(rd->ysch * render_size_factor);
@@ -79,7 +80,7 @@ void ScaleNode::convert_to_operations(NodeConverter &converter,
break;
}
- case CMP_SCALE_ABSOLUTE: {
+ case CMP_NODE_SCALE_ABSOLUTE: {
/* TODO: what is the use of this one.... perhaps some issues when the ui was updated... */
ScaleAbsoluteOperation *operation = new ScaleAbsoluteOperation();
converter.add_operation(operation);
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc
index a956c02ed42..28ebbb35e9a 100644
--- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc
+++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc
@@ -20,8 +20,8 @@ void SeparateColorNode::convert_to_operations(NodeConverter &converter,
NodeOutput *output_bsocket = this->get_output_socket(2);
NodeOutput *output_asocket = this->get_output_socket(3);
- bNode *editor_node = this->get_bnode();
- NodeCMPCombSepColor *storage = (NodeCMPCombSepColor *)editor_node->storage;
+ const bNode *editor_node = this->get_bnode();
+ const NodeCMPCombSepColor *storage = (const NodeCMPCombSepColor *)editor_node->storage;
NodeOperation *color_conv = nullptr;
switch (storage->mode) {
diff --git a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc
index c3728bc152f..11d8ca31d35 100644
--- a/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc
+++ b/source/blender/compositor/nodes/COM_SeparateColorNodeLegacy.cc
@@ -97,7 +97,7 @@ NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /
NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const
{
ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation();
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
operation->set_mode(editor_node->custom1);
return operation;
}
diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cc b/source/blender/compositor/nodes/COM_SplitViewerNode.cc
index d02bc6e773d..0f21bd6e50d 100644
--- a/source/blender/compositor/nodes/COM_SplitViewerNode.cc
+++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cc
@@ -16,7 +16,7 @@ SplitViewerNode::SplitViewerNode(bNode *editor_node) : Node(editor_node)
void SplitViewerNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
bool do_output = (editor_node->flag & NODE_DO_OUTPUT_RECALC || context.is_rendering()) &&
(editor_node->flag & NODE_DO_OUTPUT);
diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
index bcb1cf2accc..360e9e0bce3 100644
--- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
+++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc
@@ -18,7 +18,7 @@ Stabilize2dNode::Stabilize2dNode(bNode *editor_node) : Node(editor_node)
void Stabilize2dNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
NodeInput *image_input = this->get_input_socket(0);
MovieClip *clip = (MovieClip *)editor_node->id;
bool invert = (editor_node->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0;
diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.cc b/source/blender/compositor/nodes/COM_SunBeamsNode.cc
index 197098f2ff4..c33d9d0faf5 100644
--- a/source/blender/compositor/nodes/COM_SunBeamsNode.cc
+++ b/source/blender/compositor/nodes/COM_SunBeamsNode.cc
@@ -16,7 +16,7 @@ void SunBeamsNode::convert_to_operations(NodeConverter &converter,
{
NodeInput *input_socket = this->get_input_socket(0);
NodeOutput *output_socket = this->get_output_socket(0);
- NodeSunBeams *data = (NodeSunBeams *)get_bnode()->storage;
+ const NodeSunBeams *data = (const NodeSunBeams *)get_bnode()->storage;
SunBeamsOperation *operation = new SunBeamsOperation();
operation->set_data(*data);
diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cc b/source/blender/compositor/nodes/COM_SwitchViewNode.cc
index ed9e311f1b4..9507b35b4ca 100644
--- a/source/blender/compositor/nodes/COM_SwitchViewNode.cc
+++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cc
@@ -15,7 +15,7 @@ void SwitchViewNode::convert_to_operations(NodeConverter &converter,
{
NodeOperationOutput *result;
const char *view_name = context.get_view_name();
- bNode *bnode = this->get_bnode();
+ const bNode *bnode = this->get_bnode();
/* get the internal index of the socket with a matching name */
int nr = BLI_findstringindex(&bnode->inputs, view_name, offsetof(bNodeSocket, name));
diff --git a/source/blender/compositor/nodes/COM_TextureNode.cc b/source/blender/compositor/nodes/COM_TextureNode.cc
index be5f7b90e11..44d4a41fcec 100644
--- a/source/blender/compositor/nodes/COM_TextureNode.cc
+++ b/source/blender/compositor/nodes/COM_TextureNode.cc
@@ -14,7 +14,7 @@ TextureNode::TextureNode(bNode *editor_node) : Node(editor_node)
void TextureNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
Tex *texture = (Tex *)editor_node->id;
TextureOperation *operation = new TextureOperation();
bool scene_color_manage = !STREQ(context.get_scene()->display_settings.display_device, "None");
diff --git a/source/blender/compositor/nodes/COM_TimeNode.cc b/source/blender/compositor/nodes/COM_TimeNode.cc
index b3dbd74b795..4f4f6f7bf8a 100644
--- a/source/blender/compositor/nodes/COM_TimeNode.cc
+++ b/source/blender/compositor/nodes/COM_TimeNode.cc
@@ -18,7 +18,7 @@ void TimeNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
SetValueOperation *operation = new SetValueOperation();
- bNode *node = this->get_bnode();
+ const bNode *node = this->get_bnode();
/* stack order output: fac */
float fac = 0.0f;
diff --git a/source/blender/compositor/nodes/COM_TonemapNode.cc b/source/blender/compositor/nodes/COM_TonemapNode.cc
index d20b9698163..f08798db19c 100644
--- a/source/blender/compositor/nodes/COM_TonemapNode.cc
+++ b/source/blender/compositor/nodes/COM_TonemapNode.cc
@@ -14,7 +14,7 @@ TonemapNode::TonemapNode(bNode *editor_node) : Node(editor_node)
void TonemapNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
- NodeTonemap *data = (NodeTonemap *)this->get_bnode()->storage;
+ const NodeTonemap *data = (const NodeTonemap *)this->get_bnode()->storage;
TonemapOperation *operation = data->type == 1 ? new PhotoreceptorTonemapOperation() :
new TonemapOperation();
diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.cc b/source/blender/compositor/nodes/COM_TrackPositionNode.cc
index 1143ce81c80..da12f72b451 100644
--- a/source/blender/compositor/nodes/COM_TrackPositionNode.cc
+++ b/source/blender/compositor/nodes/COM_TrackPositionNode.cc
@@ -40,7 +40,7 @@ static TrackPositionOperation *create_motion_operation(NodeConverter &converter,
void TrackPositionNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
MovieClip *clip = (MovieClip *)editor_node->id;
NodeTrackPosData *trackpos_data = (NodeTrackPosData *)editor_node->storage;
diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc
index 50c5b4c0d2c..8c53d773ad2 100644
--- a/source/blender/compositor/nodes/COM_TranslateNode.cc
+++ b/source/blender/compositor/nodes/COM_TranslateNode.cc
@@ -17,8 +17,8 @@ TranslateNode::TranslateNode(bNode *editor_node) : Node(editor_node)
void TranslateNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *bnode = this->get_bnode();
- NodeTranslateData *data = (NodeTranslateData *)bnode->storage;
+ const bNode *bnode = this->get_bnode();
+ const NodeTranslateData *data = (const NodeTranslateData *)bnode->storage;
NodeInput *input_socket = this->get_input_socket(0);
NodeInput *input_xsocket = this->get_input_socket(1);
diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.cc b/source/blender/compositor/nodes/COM_VectorBlurNode.cc
index f8a2a90ca81..90b8d5a2012 100644
--- a/source/blender/compositor/nodes/COM_VectorBlurNode.cc
+++ b/source/blender/compositor/nodes/COM_VectorBlurNode.cc
@@ -14,8 +14,8 @@ VectorBlurNode::VectorBlurNode(bNode *editor_node) : Node(editor_node)
void VectorBlurNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *node = this->get_bnode();
- NodeBlurData *vector_blur_settings = (NodeBlurData *)node->storage;
+ const bNode *node = this->get_bnode();
+ const NodeBlurData *vector_blur_settings = (const NodeBlurData *)node->storage;
VectorBlurOperation *operation = new VectorBlurOperation();
operation->set_vector_blur_settings(vector_blur_settings);
diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.cc b/source/blender/compositor/nodes/COM_VectorCurveNode.cc
index c0871ab21e1..d42d2831bc9 100644
--- a/source/blender/compositor/nodes/COM_VectorCurveNode.cc
+++ b/source/blender/compositor/nodes/COM_VectorCurveNode.cc
@@ -15,7 +15,7 @@ void VectorCurveNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
VectorCurveOperation *operation = new VectorCurveOperation();
- operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage);
+ operation->set_curve_mapping((const CurveMapping *)this->get_bnode()->storage);
converter.add_operation(operation);
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc
index ebef331c62f..47056bcd8c4 100644
--- a/source/blender/compositor/nodes/COM_ViewerNode.cc
+++ b/source/blender/compositor/nodes/COM_ViewerNode.cc
@@ -15,7 +15,7 @@ ViewerNode::ViewerNode(bNode *editor_node) : Node(editor_node)
void ViewerNode::convert_to_operations(NodeConverter &converter,
const CompositorContext &context) const
{
- bNode *editor_node = this->get_bnode();
+ const bNode *editor_node = this->get_bnode();
bool do_output = (editor_node->flag & NODE_DO_OUTPUT_RECALC || context.is_rendering()) &&
(editor_node->flag & NODE_DO_OUTPUT);
bool ignore_alpha = (editor_node->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0;
diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h
index 751f1ad8d8e..28506ba36b5 100644
--- a/source/blender/compositor/operations/COM_BokehImageOperation.h
+++ b/source/blender/compositor/operations/COM_BokehImageOperation.h
@@ -39,7 +39,7 @@ class BokehImageOperation : public MultiThreadedOperation {
/**
* \brief Settings of the bokeh image
*/
- NodeBokehImage *data_;
+ const NodeBokehImage *data_;
/**
* \brief precalculate center of the image
@@ -119,7 +119,7 @@ class BokehImageOperation : public MultiThreadedOperation {
* \brief set the node data
* \param data:
*/
- void set_data(NodeBokehImage *data)
+ void set_data(const NodeBokehImage *data)
{
data_ = data;
}
diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h
index 9f18d110f3c..7e976cab6b6 100644
--- a/source/blender/compositor/operations/COM_BoxMaskOperation.h
+++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h
@@ -22,7 +22,7 @@ class BoxMaskOperation : public MultiThreadedOperation {
float aspect_ratio_;
int mask_type_;
- NodeBoxMask *data_;
+ const NodeBoxMask *data_;
public:
BoxMaskOperation();
@@ -42,7 +42,7 @@ class BoxMaskOperation : public MultiThreadedOperation {
*/
void deinit_execution() override;
- void set_data(NodeBoxMask *data)
+ void set_data(const NodeBoxMask *data)
{
data_ = data;
}
diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc
index 764d5d64046..07d8035e615 100644
--- a/source/blender/compositor/operations/COM_BrightnessOperation.cc
+++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc
@@ -47,7 +47,7 @@ void BrightnessOperation::execute_pixel_sampled(float output[4],
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
- * Extracted of OpenCV demhist.c
+ * Extracted of OpenCV `demhist.c`.
*/
if (contrast > 0) {
a = 1.0f - delta * 2.0f;
@@ -84,7 +84,7 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output,
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
- * Extracted of OpenCV demhist.c
+ * Extracted of OpenCV `demhist.c`.
*/
float a, b;
if (contrast > 0) {
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc
index e8b61ff229e..c0cc3fa1ba4 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.cc
+++ b/source/blender/compositor/operations/COM_CompositorOperation.cc
@@ -5,6 +5,7 @@
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_scene.h"
#include "RE_pipeline.h"
@@ -164,8 +165,8 @@ void CompositorOperation::execute_region(rcti *rect, unsigned int /*tile_number*
* Full frame
*/
- int full_width = rd->xsch * rd->size / 100;
- int full_height = rd->ysch * rd->size / 100;
+ int full_width, full_height;
+ BKE_render_resolution(rd, false, &full_width, &full_height);
dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f;
dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f;
@@ -214,8 +215,8 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp
void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area)
{
- int width = rd_->xsch * rd_->size / 100;
- int height = rd_->ysch * rd_->size / 100;
+ int width, height;
+ BKE_render_resolution(rd_, false, &width, &height);
/* Check actual render resolution with cropping it may differ with cropped border.rendering
* Fix for T31777 Border Crop gives black (easy). */
diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc
index e92a2f08ed4..e7cbf0d28d5 100644
--- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc
+++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc
@@ -33,7 +33,7 @@ void CurveBaseOperation::deinit_execution()
}
}
-void CurveBaseOperation::set_curve_mapping(CurveMapping *mapping)
+void CurveBaseOperation::set_curve_mapping(const CurveMapping *mapping)
{
/* duplicate the curve to avoid glitches while drawing, see bug T32374. */
if (curve_mapping_) {
diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h
index d30c64e3282..4a745c34f24 100644
--- a/source/blender/compositor/operations/COM_CurveBaseOperation.h
+++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h
@@ -26,7 +26,7 @@ class CurveBaseOperation : public MultiThreadedOperation {
void init_execution() override;
void deinit_execution() override;
- void set_curve_mapping(CurveMapping *mapping);
+ void set_curve_mapping(const CurveMapping *mapping);
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc
index 1a199ba2eab..3f32eced0f8 100644
--- a/source/blender/compositor/operations/COM_DenoiseOperation.cc
+++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc
@@ -163,7 +163,7 @@ void DenoiseOperation::deinit_execution()
SingleThreadedOperation::deinit_execution();
}
-static bool are_guiding_passes_noise_free(NodeDenoise *settings)
+static bool are_guiding_passes_noise_free(const NodeDenoise *settings)
{
switch (settings->prefilter) {
case CMP_NODE_DENOISE_PREFILTER_NONE:
@@ -201,7 +201,7 @@ void DenoiseOperation::generate_denoise(MemoryBuffer *output,
MemoryBuffer *input_color,
MemoryBuffer *input_normal,
MemoryBuffer *input_albedo,
- NodeDenoise *settings)
+ const NodeDenoise *settings)
{
BLI_assert(input_color->get_buffer());
if (!input_color->get_buffer()) {
diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h
index 2b170bb9bb7..709b8843b93 100644
--- a/source/blender/compositor/operations/COM_DenoiseOperation.h
+++ b/source/blender/compositor/operations/COM_DenoiseOperation.h
@@ -37,7 +37,7 @@ class DenoiseOperation : public DenoiseBaseOperation {
/**
* \brief settings of the denoise node.
*/
- NodeDenoise *settings_;
+ const NodeDenoise *settings_;
public:
DenoiseOperation();
@@ -51,7 +51,7 @@ class DenoiseOperation : public DenoiseBaseOperation {
*/
void deinit_execution() override;
- void set_denoise_settings(NodeDenoise *settings)
+ void set_denoise_settings(const NodeDenoise *settings)
{
settings_ = settings;
}
@@ -66,7 +66,7 @@ class DenoiseOperation : public DenoiseBaseOperation {
MemoryBuffer *input_color,
MemoryBuffer *input_normal,
MemoryBuffer *input_albedo,
- NodeDenoise *settings);
+ const NodeDenoise *settings);
MemoryBuffer *create_memory_buffer(rcti *rect) override;
};
diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
index e209bac2305..4e0b072dec9 100644
--- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
+++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h
@@ -11,7 +11,7 @@ namespace blender::compositor {
class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper {
private:
SocketReader *input_program_;
- NodeDBlurData *data_;
+ const NodeDBlurData *data_;
float center_x_pix_, center_y_pix_;
float tx_, ty_;
@@ -39,7 +39,7 @@ class DirectionalBlurOperation : public MultiThreadedOperation, public QualitySt
ReadBufferOperation *read_operation,
rcti *output) override;
- void set_data(NodeDBlurData *data)
+ void set_data(const NodeDBlurData *data)
{
data_ = data;
}
diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
index 2cb21be43ad..7b1a38618fd 100644
--- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
+++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h
@@ -13,7 +13,7 @@ namespace blender::compositor {
*/
class DistanceRGBMatteOperation : public MultiThreadedOperation {
protected:
- NodeChroma *settings_;
+ const NodeChroma *settings_;
SocketReader *input_image_program_;
SocketReader *input_key_program_;
@@ -33,7 +33,7 @@ class DistanceRGBMatteOperation : public MultiThreadedOperation {
void init_execution() override;
void deinit_execution() override;
- void set_settings(NodeChroma *node_chroma)
+ void set_settings(const NodeChroma *node_chroma)
{
settings_ = node_chroma;
}
diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
index ea156cd19db..fed11d59b50 100644
--- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
+++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc
@@ -812,7 +812,7 @@ static void do_allEdgeDetection(unsigned int t,
if (!limask[a]) { /* If the inner mask is empty. */
if (lomask[a]) { /* If the outer mask is full. */
/*
- * Next we test all 4 directions around the current pixel: next/prev/up/down
+ * Next we test all 4 directions around the current pixel: next/previous/up/down
* The test ensures that the outer mask is empty and that the inner mask
* is also empty. If both conditions are true for any one of the 4 adjacent pixels
* then the current pixel is counted as being a true outer edge pixel.
@@ -882,7 +882,7 @@ static void do_adjacentEdgeDetection(unsigned int t,
if (!limask[a]) { /* If the inner mask is empty. */
if (lomask[a]) { /* If the outer mask is full. */
/*
- * Next we test all 4 directions around the current pixel: next/prev/up/down
+ * Next we test all 4 directions around the current pixel: next/previous/up/down
* The test ensures that the outer mask is empty and that the inner mask
* is also empty. If both conditions are true for any one of the 4 adjacent pixels
* then the current pixel is counted as being a true outer edge pixel.
diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h
index 4750086a119..0a2c5507a76 100644
--- a/source/blender/compositor/operations/COM_GlareBaseOperation.h
+++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h
@@ -32,7 +32,7 @@ class GlareBaseOperation : public SingleThreadedOperation {
/**
* \brief settings of the glare node.
*/
- NodeGlare *settings_;
+ const NodeGlare *settings_;
bool is_output_rendered_;
@@ -47,7 +47,7 @@ class GlareBaseOperation : public SingleThreadedOperation {
*/
void deinit_execution() override;
- void set_glare_settings(NodeGlare *settings)
+ void set_glare_settings(const NodeGlare *settings)
{
settings_ = settings;
}
@@ -64,7 +64,9 @@ class GlareBaseOperation : public SingleThreadedOperation {
protected:
GlareBaseOperation();
- virtual void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) = 0;
+ virtual void generate_glare(float *data,
+ MemoryBuffer *input_tile,
+ const NodeGlare *settings) = 0;
MemoryBuffer *create_memory_buffer(rcti *rect) override;
};
diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
index 8b52123b2f0..ade3f11a8b3 100644
--- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc
@@ -391,7 +391,7 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2)
void GlareFogGlowOperation::generate_glare(float *data,
MemoryBuffer *input_tile,
- NodeGlare *settings)
+ const NodeGlare *settings)
{
int x, y;
float scale, u, v, r, w, d;
diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
index 5e19f2ab2aa..2a74aeef048 100644
--- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
+++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h
@@ -16,7 +16,7 @@ class GlareFogGlowOperation : public GlareBaseOperation {
}
protected:
- void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override;
+ void generate_glare(float *data, MemoryBuffer *input_tile, const 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 e863f74d98a..13b7af2329e 100644
--- a/source/blender/compositor/operations/COM_GlareGhostOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cc
@@ -20,7 +20,7 @@ static float smooth_mask(float x, float y)
void GlareGhostOperation::generate_glare(float *data,
MemoryBuffer *input_tile,
- NodeGlare *settings)
+ const NodeGlare *settings)
{
const int qt = 1 << settings->quality;
const float s1 = 4.0f / (float)qt, s2 = 2.0f * s1;
diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.h b/source/blender/compositor/operations/COM_GlareGhostOperation.h
index 644c2975676..db79358034a 100644
--- a/source/blender/compositor/operations/COM_GlareGhostOperation.h
+++ b/source/blender/compositor/operations/COM_GlareGhostOperation.h
@@ -16,7 +16,7 @@ class GlareGhostOperation : public GlareBaseOperation {
}
protected:
- void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override;
+ void generate_glare(float *data, MemoryBuffer *input_tile, const 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 a656eb3c706..69182b56bee 100644
--- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc
@@ -7,7 +7,7 @@ namespace blender::compositor {
void GlareSimpleStarOperation::generate_glare(float *data,
MemoryBuffer *input_tile,
- NodeGlare *settings)
+ const NodeGlare *settings)
{
int i, x, y, ym, yp, xm, xp;
float c[4] = {0, 0, 0, 0}, tc[4] = {0, 0, 0, 0};
diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
index 3c2d2fe2d0f..470af780eb2 100644
--- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
+++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h
@@ -16,7 +16,7 @@ class GlareSimpleStarOperation : public GlareBaseOperation {
}
protected:
- void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override;
+ void generate_glare(float *data, MemoryBuffer *input_tile, const 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 233eb8caf38..e4f06eb0e50 100644
--- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc
+++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc
@@ -7,7 +7,7 @@ namespace blender::compositor {
void GlareStreaksOperation::generate_glare(float *data,
MemoryBuffer *input_tile,
- NodeGlare *settings)
+ const NodeGlare *settings)
{
int x, y, n;
unsigned int nump = 0;
diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.h b/source/blender/compositor/operations/COM_GlareStreaksOperation.h
index 09ef30c339d..56afe3d8462 100644
--- a/source/blender/compositor/operations/COM_GlareStreaksOperation.h
+++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.h
@@ -16,7 +16,7 @@ class GlareStreaksOperation : public GlareBaseOperation {
}
protected:
- void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override;
+ void generate_glare(float *data, MemoryBuffer *input_tile, const NodeGlare *settings) override;
};
} // namespace blender::compositor
diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h
index 90870e3cca9..7930e32eda7 100644
--- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h
+++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h
@@ -18,7 +18,7 @@ class GlareThresholdOperation : public MultiThreadedOperation {
/**
* \brief settings of the glare node.
*/
- NodeGlare *settings_;
+ const NodeGlare *settings_;
public:
GlareThresholdOperation();
@@ -38,7 +38,7 @@ class GlareThresholdOperation : public MultiThreadedOperation {
*/
void deinit_execution() override;
- void set_glare_settings(NodeGlare *settings)
+ void set_glare_settings(const NodeGlare *settings)
{
settings_ = settings;
}
diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc
index 1353e0391a3..f55d394baa8 100644
--- a/source/blender/compositor/operations/COM_MapValueOperation.cc
+++ b/source/blender/compositor/operations/COM_MapValueOperation.cc
@@ -25,7 +25,7 @@ void MapValueOperation::execute_pixel_sampled(float output[4],
{
float src[4];
input_operation_->read_sampled(src, x, y, sampler);
- TexMapping *texmap = settings_;
+ const TexMapping *texmap = settings_;
float value = (src[0] + texmap->loc[0]) * texmap->size[0];
if (texmap->flag & TEXMAP_CLIP_MIN) {
if (value < texmap->min[0]) {
@@ -52,7 +52,7 @@ void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output,
{
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
const float input = *it.in(0);
- TexMapping *texmap = settings_;
+ const TexMapping *texmap = settings_;
float value = (input + texmap->loc[0]) * texmap->size[0];
if (texmap->flag & TEXMAP_CLIP_MIN) {
if (value < texmap->min[0]) {
diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h
index 4c384e52c95..5c1f425c5bd 100644
--- a/source/blender/compositor/operations/COM_MapValueOperation.h
+++ b/source/blender/compositor/operations/COM_MapValueOperation.h
@@ -18,7 +18,7 @@ class MapValueOperation : public MultiThreadedOperation {
* Cached reference to the input_program
*/
SocketReader *input_operation_;
- TexMapping *settings_;
+ const TexMapping *settings_;
public:
/**
@@ -44,7 +44,7 @@ class MapValueOperation : public MultiThreadedOperation {
/**
* \brief set the TexMapping settings
*/
- void set_settings(TexMapping *settings)
+ void set_settings(const TexMapping *settings)
{
settings_ = settings;
}
diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc
index d7cf41cf422..b62d972e807 100644
--- a/source/blender/compositor/operations/COM_MovieClipOperation.cc
+++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc
@@ -74,7 +74,7 @@ void MovieClipBaseOperation::execute_pixel_sampled(float output[4],
zero_v4(output);
}
else if (ibuf->rect == nullptr && ibuf->rect_float == nullptr) {
- /* Happens for multilayer exr, i.e. */
+ /* Happens for multi-layer EXR, i.e. */
zero_v4(output);
}
else {
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
index 341541b4cdd..760ed94f882 100644
--- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc
@@ -21,7 +21,7 @@ OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOpe
const RenderData *rd,
const bNodeTree *tree,
DataType datatype,
- ImageFormatData *format,
+ const ImageFormatData *format,
const char *path,
const char *view_name,
const bool save_as_render)
@@ -243,7 +243,7 @@ OutputStereoOperation::OutputStereoOperation(const Scene *scene,
const RenderData *rd,
const bNodeTree *tree,
DataType datatype,
- ImageFormatData *format,
+ const ImageFormatData *format,
const char *path,
const char *name,
const char *view_name,
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
index 69f4011d340..e36999e5cf1 100644
--- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
@@ -22,7 +22,7 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera
const RenderData *rd,
const bNodeTree *tree,
DataType datatype,
- ImageFormatData *format,
+ const ImageFormatData *format,
const char *path,
const char *view_name,
bool save_as_render);
@@ -57,7 +57,7 @@ class OutputStereoOperation : public OutputSingleLayerOperation {
const RenderData *rd,
const bNodeTree *tree,
DataType datatype,
- struct ImageFormatData *format,
+ const struct ImageFormatData *format,
const char *path,
const char *name,
const char *view_name,
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc
index 49de275c256..1d22f3e8cd2 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.cc
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc
@@ -204,7 +204,7 @@ OutputSingleLayerOperation::OutputSingleLayerOperation(const Scene *scene,
const RenderData *rd,
const bNodeTree *tree,
DataType datatype,
- ImageFormatData *format,
+ const ImageFormatData *format,
const char *path,
const char *view_name,
const bool save_as_render)
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h
index 875defe00e9..df1d68838d9 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.h
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.h
@@ -35,7 +35,7 @@ class OutputSingleLayerOperation : public MultiThreadedOperation {
const RenderData *rd,
const bNodeTree *tree,
DataType datatype,
- ImageFormatData *format,
+ const ImageFormatData *format,
const char *path,
const char *view_name,
bool save_as_render);
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc
index 522086658f8..40f2187b27b 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.cc
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc
@@ -109,8 +109,8 @@ void RenderLayersProg::execute_pixel_sampled(float output[4],
/* see comment in execute_region describing coordinate mapping,
* here it simply goes other way around
*/
- int full_width = rd->xsch * rd->size / 100;
- int full_height = rd->ysch * rd->size / 100;
+ int full_width, full_height;
+ BKE_render_resolution(rd, false, &full_width, &full_height);
dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f;
dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f;
diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc
index 11e51e81ef0..261426b31e2 100644
--- a/source/blender/compositor/operations/COM_SMAAOperation.cc
+++ b/source/blender/compositor/operations/COM_SMAAOperation.cc
@@ -12,7 +12,7 @@ extern "C" {
namespace blender::compositor {
/*
- * An implementation of Enhanced Subpixel Morphological Antialiasing (SMAA)
+ * An implementation of Enhanced Sub-pixel Morphological Anti-aliasing (SMAA)
*
* The algorithm was proposed by:
* Jorge Jimenez, Jose I. Echevarria, Tiago Sousa, Diego Gutierrez
diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc
index 1957c5eb5fc..2a2aff31893 100644
--- a/source/blender/compositor/operations/COM_ScaleOperation.cc
+++ b/source/blender/compositor/operations/COM_ScaleOperation.cc
@@ -7,7 +7,7 @@
namespace blender::compositor {
#define USE_FORCE_BILINEAR
-/* XXX(campbell): ignore input and use default from old compositor,
+/* XXX(@campbellbarton): ignore input and use default from old compositor,
* could become an option like the transform node.
*
* NOTE: use bilinear because bicubic makes fuzzy even when not scaling at all (1:1)
diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc
index 8b27c3aded4..c1c8db2ae3f 100644
--- a/source/blender/compositor/operations/COM_TextureOperation.cc
+++ b/source/blender/compositor/operations/COM_TextureOperation.cc
@@ -6,6 +6,7 @@
#include "BKE_image.h"
#include "BKE_node.h"
+#include "BKE_scene.h"
#include "NOD_texture.h"
@@ -59,8 +60,8 @@ void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_
{
r_area = preferred_area;
if (BLI_rcti_is_empty(&preferred_area)) {
- int width = rd_->xsch * rd_->size / 100;
- int height = rd_->ysch * rd_->size / 100;
+ int width, height;
+ BKE_render_resolution(rd_, false, &width, &height);
r_area.xmax = preferred_area.xmin + width;
r_area.ymax = preferred_area.ymin + height;
}
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc
index fa40cd36f4c..714625e483d 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.cc
+++ b/source/blender/compositor/operations/COM_TonemapOperation.cc
@@ -46,7 +46,7 @@ void TonemapOperation::execute_pixel(float output[4], int x, int y, void *data)
void PhotoreceptorTonemapOperation::execute_pixel(float output[4], int x, int y, void *data)
{
AvgLogLum *avg = (AvgLogLum *)data;
- NodeTonemap *ntm = data_;
+ const NodeTonemap *ntm = data_;
const float f = expf(-data_->f);
const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f));
@@ -233,7 +233,7 @@ void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *o
Span<MemoryBuffer *> inputs)
{
AvgLogLum *avg = cached_instance_;
- NodeTonemap *ntm = data_;
+ const NodeTonemap *ntm = data_;
const float f = expf(-data_->f);
const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f));
const float ic = 1.0f - ntm->c;
diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h
index 7868aa140dc..4e68d432985 100644
--- a/source/blender/compositor/operations/COM_TonemapOperation.h
+++ b/source/blender/compositor/operations/COM_TonemapOperation.h
@@ -21,7 +21,7 @@ typedef struct AvgLogLum {
} AvgLogLum;
/**
- * \brief base class of tonemap, implementing the simple tonemap
+ * \brief base class of tone-map, implementing the simple tone-map
* \ingroup operation
*/
class TonemapOperation : public MultiThreadedOperation {
@@ -32,9 +32,9 @@ class TonemapOperation : public MultiThreadedOperation {
SocketReader *image_reader_;
/**
- * \brief settings of the Tonemap
+ * \brief settings of the Tone-map
*/
- NodeTonemap *data_;
+ const NodeTonemap *data_;
/**
* \brief temporarily cache of the execution storage
@@ -62,7 +62,7 @@ class TonemapOperation : public MultiThreadedOperation {
*/
void deinit_execution() override;
- void set_data(NodeTonemap *data)
+ void set_data(const NodeTonemap *data)
{
data_ = data;
}
diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc
index 71c61a6e588..8add3924033 100644
--- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc
+++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc
@@ -190,7 +190,10 @@ struct ZSpan {
float clipcrop;
};
-/* each zbuffer has coordinates transformed to local rect coordinates, so we can simply clip */
+/**
+ * Each Z-buffer has coordinates transformed to local rectangle coordinates,
+ * so we can simply clip.
+ */
void zbuf_alloc_span(ZSpan *zspan, int rectx, int recty, float clipcrop)
{
memset(zspan, 0, sizeof(ZSpan));
@@ -528,7 +531,7 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove)
}
}
- /* last: pixels with 0 we fill in zbuffer, with 1 we skip for mask */
+ /* last: pixels with 0 we fill in Z-buffer, with 1 we skip for mask */
for (y = 2; y < ysize; y++) {
/* setup rows */
row1 = rectmove + (y - 2) * xsize;
diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h
index 9c83c0645c2..0c4d215b6d3 100644
--- a/source/blender/compositor/operations/COM_VectorBlurOperation.h
+++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h
@@ -25,7 +25,7 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper {
/**
* \brief settings of the glare node.
*/
- NodeBlurData *settings_;
+ const NodeBlurData *settings_;
float *cached_instance_;
@@ -49,7 +49,7 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper {
void *initialize_tile_data(rcti *rect) override;
- void set_vector_blur_settings(NodeBlurData *settings)
+ void set_vector_blur_settings(const NodeBlurData *settings)
{
settings_ = settings;
}
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc
index 58392b8a638..3bd5fa4ad14 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.cc
+++ b/source/blender/compositor/operations/COM_ViewerOperation.cc
@@ -104,8 +104,8 @@ void ViewerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/)
void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
{
- const int scene_render_width = rd_->xsch * rd_->size / 100;
- const int scene_render_height = rd_->ysch * rd_->size / 100;
+ int scene_render_width, scene_render_height;
+ BKE_render_resolution(rd_, false, &scene_render_width, &scene_render_height);
rcti local_preferred = preferred_area;
local_preferred.xmax = local_preferred.xmin + scene_render_width;
@@ -156,7 +156,7 @@ void ViewerOperation::init_image()
ibuf->y = display_height_;
/* zero size can happen if no image buffers exist to define a sensible resolution */
if (ibuf->x > 0 && ibuf->y > 0) {
- imb_addrectfloatImBuf(ibuf);
+ imb_addrectfloatImBuf(ibuf, 4);
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt
new file mode 100644
index 00000000000..1f1333332f5
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+set(INC
+ .
+ ../../blenkernel
+ ../../blenlib
+ ../../gpu
+ ../../imbuf
+ ../../makesdna
+ ../../makesrna
+ ../../nodes
+ ../../gpu/intern
+ ../../../../intern/guardedalloc
+)
+
+
+set(SRC
+ intern/compile_state.cc
+ intern/context.cc
+ intern/conversion_operation.cc
+ intern/domain.cc
+ intern/evaluator.cc
+ intern/input_single_value_operation.cc
+ intern/node_operation.cc
+ intern/operation.cc
+ intern/realize_on_domain_operation.cc
+ intern/reduce_to_single_value_operation.cc
+ intern/result.cc
+ intern/scheduler.cc
+ intern/shader_node.cc
+ intern/shader_operation.cc
+ intern/simple_operation.cc
+ intern/static_shader_manager.cc
+ intern/texture_pool.cc
+ intern/utilities.cc
+
+ COM_compile_state.hh
+ COM_context.hh
+ COM_conversion_operation.hh
+ COM_domain.hh
+ COM_evaluator.hh
+ COM_input_descriptor.hh
+ COM_input_single_value_operation.hh
+ COM_node_operation.hh
+ COM_operation.hh
+ COM_realize_on_domain_operation.hh
+ COM_reduce_to_single_value_operation.hh
+ COM_result.hh
+ COM_scheduler.hh
+ COM_shader_node.hh
+ COM_shader_operation.hh
+ COM_simple_operation.hh
+ COM_static_shader_manager.hh
+ COM_texture_pool.hh
+ COM_utilities.hh
+)
+
+set(LIB
+ bf_gpu
+ bf_nodes
+ bf_imbuf
+ bf_blenlib
+ bf_blenkernel
+)
+
+blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/compositor/realtime_compositor/COM_compile_state.hh b/source/blender/compositor/realtime_compositor/COM_compile_state.hh
new file mode 100644
index 00000000000..924919bbef6
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_compile_state.hh
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_map.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_domain.hh"
+#include "COM_node_operation.hh"
+#include "COM_scheduler.hh"
+#include "COM_shader_operation.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Compile State
+ *
+ * The compile state is a utility class used to track the state of compilation when compiling the
+ * node tree. In particular, it tracks two important pieces of information, each of which is
+ * described in one of the following sections.
+ *
+ * First, it stores a mapping between all nodes and the operations they were compiled into. The
+ * mapping are stored independently depending on the type of the operation in the node_operations_
+ * and shader_operations_ maps. So those two maps are mutually exclusive. The compiler should call
+ * the map_node_to_node_operation and map_node_to_shader_operation methods to populate those maps
+ * as soon as it compiles a node or multiple nodes into an operation. Those maps are used to
+ * retrieve the results of outputs linked to the inputs of operations. For more details, see the
+ * get_result_from_output_socket method. For the node tree shown below, nodes 1, 2, and 6 are
+ * mapped to their compiled operations in the node_operation_ map. While nodes 3 and 4 are both
+ * mapped to the first shader operation, and node 5 is mapped to the second shader operation in the
+ * shader_operations_ map.
+ *
+ * Shader Operation 1 Shader Operation 2
+ * +-----------------------------------+ +------------------+
+ * .------------. | .------------. .------------. | | .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |---|-----|--| |--|--| |
+ * | | .-|--| | | | | .--|--| | | | |
+ * '------------' | | '------------' '------------' | | | '------------' | '------------'
+ * | +-----------------------------------+ | +------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'----------------------------------------'
+ * | |
+ * '------------'
+ *
+ * Second, it stores the shader compile unit as well as its domain. One should first go over the
+ * discussion in COM_evaluator.hh for a high level description of the mechanism of the compile
+ * unit. The one important detail in this class is the should_compile_shader_compile_unit method,
+ * which implements the criteria of whether the compile unit should be compiled given the node
+ * currently being processed as an argument. Those criteria are described as follows. If the
+ * compile unit is empty as is the case when processing nodes 1, 2, and 3, then it plainly
+ * shouldn't be compiled. If the given node is not a shader node, then it can't be added to the
+ * compile unit and the unit is considered complete and should be compiled, as is the case when
+ * processing node 6. If the computed domain of the given node is not compatible with the domain of
+ * the compiled unit, then it can't be added to the unit and the unit is considered complete and
+ * should be compiled, as is the case when processing node 5, more on this in the next section.
+ * Otherwise, the given node is compatible with the compile unit and can be added to it, so the
+ * unit shouldn't be compiled just yet, as is the case when processing node 4.
+ *
+ * Special attention should be given to the aforementioned domain compatibility criterion. One
+ * should first go over the discussion in COM_domain.hh for more information on domains. When a
+ * compile unit gets eventually compiled to a shader operation, that operation will have a certain
+ * operation domain, and any node that gets added to the compile unit should itself have a computed
+ * node domain that is compatible with that operation domain, otherwise, had the node been compiled
+ * into its own operation separately, the result would have been be different. For instance,
+ * consider the above node tree where node 1 outputs a 100x100 result, node 2 outputs a 50x50
+ * result, the first input in node 3 has the highest domain priority, and the second input in node
+ * 5 has the highest domain priority. In this case, shader operation 1 will output a 100x100
+ * result, and shader operation 2 will output a 50x50 result, because that's the computed operation
+ * domain for each of them. So node 6 will get a 50x50 result. Now consider the same node tree, but
+ * where all three nodes 3, 4, and 5 were compiled into a single shader operation as shown the node
+ * tree below. In that case, shader operation 1 will output a 100x100 result, because that's its
+ * computed operation domain. So node 6 will get a 100x100 result. As can be seen, the final result
+ * is different even though the node tree is the same. That's why the compiler can decide to
+ * compile the compile unit early even though further nodes can still be technically added to it.
+ *
+ * Shader Operation 1
+ * +------------------------------------------------------+
+ * .------------. | .------------. .------------. .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |------| |--|--| |
+ * | | .-|--| | | | .---| | | | |
+ * '------------' | | '------------' '------------' | '------------' | '------------'
+ * | +----------------------------------|-------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'------------------------------------'
+ * | |
+ * '------------'
+ *
+ * To check for the domain compatibility between the compile unit and the node being processed,
+ * the domain of the compile unit is assumed to be the domain of the first node whose computed
+ * domain is not an identity domain. Identity domains corresponds to single value results, so those
+ * are always compatible with any domain. The domain of the compile unit is computed and set in
+ * the add_node_to_shader_compile_unit method. When processing a node, the computed domain of node
+ * is compared to the compile unit domain in the should_compile_shader_compile_unit method, noting
+ * that identity domains are always compatible. Node domains are computed in the
+ * compute_shader_node_domain method, which is analogous to Operation::compute_domain for nodes
+ * that are not yet compiled. */
+class CompileState {
+ private:
+ /* A reference to the node execution schedule that is being compiled. */
+ const Schedule &schedule_;
+ /* Those two maps associate each node with the operation it was compiled into. Each node is
+ * either compiled into a node operation and added to node_operations, or compiled into a shader
+ * operation and added to shader_operations. Those maps are used to retrieve the results of
+ * outputs linked to the inputs of operations. See the get_result_from_output_socket method for
+ * more information. */
+ Map<DNode, NodeOperation *> node_operations_;
+ Map<DNode, ShaderOperation *> shader_operations_;
+ /* A contiguous subset of the node execution schedule that contains the group of nodes that will
+ * be compiled together into a Shader Operation. See the discussion in COM_evaluator.hh for
+ * more information. */
+ ShaderCompileUnit shader_compile_unit_;
+ /* The domain of the shader compile unit. */
+ Domain shader_compile_unit_domain_ = Domain::identity();
+
+ public:
+ /* Construct a compile state from the node execution schedule being compiled. */
+ CompileState(const Schedule &schedule);
+
+ /* Get a reference to the node execution schedule being compiled. */
+ const Schedule &get_schedule();
+
+ /* Add an association between the given node and the give node operation that the node was
+ * compiled into in the node_operations_ map. */
+ void map_node_to_node_operation(DNode node, NodeOperation *operation);
+
+ /* Add an association between the given node and the give shader operation that the node was
+ * compiled into in the shader_operations_ map. */
+ void map_node_to_shader_operation(DNode node, ShaderOperation *operation);
+
+ /* Returns a reference to the result of the operation corresponding to the given output that the
+ * given output's node was compiled to. */
+ Result &get_result_from_output_socket(DOutputSocket output);
+
+ /* Add the given node to the compile unit. And if the domain of the compile unit is not yet
+ * determined or was determined to be an identity domain, update it to the computed domain for
+ * the give node. */
+ void add_node_to_shader_compile_unit(DNode node);
+
+ /* Get a reference to the shader compile unit. */
+ ShaderCompileUnit &get_shader_compile_unit();
+
+ /* Clear the compile unit. This should be called once the compile unit is compiled to ready it to
+ * track the next potential compile unit. */
+ void reset_shader_compile_unit();
+
+ /* Determines if the compile unit should be compiled based on a number of criteria give the node
+ * currently being processed. Those criteria are as follows:
+ * - If compile unit is empty, then it can't and shouldn't be compiled.
+ * - If the given node is not a shader node, then it can't be added to the compile unit
+ * and the unit is considered complete and should be compiled.
+ * - If the computed domain of the given node is not compatible with the domain of the compile
+ * unit, then it can't be added to it and the unit is considered complete and should be
+ * compiled. */
+ bool should_compile_shader_compile_unit(DNode node);
+
+ private:
+ /* Compute the node domain of the given shader node. This is analogous to the
+ * Operation::compute_domain method, except it is computed from the node itself as opposed to a
+ * compiled operation. See the discussion in COM_domain.hh for more information. */
+ Domain compute_shader_node_domain(DNode node);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh
new file mode 100644
index 00000000000..b5c8cea641f
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_context.hh
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_math_vec_types.hh"
+#include "BLI_string_ref.hh"
+
+#include "DNA_scene_types.h"
+
+#include "GPU_texture.h"
+
+#include "COM_static_shader_manager.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Context
+ *
+ * A Context is an abstract class that is implemented by the caller of the evaluator to provide the
+ * necessary data and functionalities for the correct operation of the evaluator. This includes
+ * providing input data like render passes and the active scene, as well as references to the data
+ * where the output of the evaluator will be written. The class also provides a reference to the
+ * texture pool which should be implemented by the caller and provided during construction.
+ * Finally, the class have an instance of a static shader manager for convenient shader
+ * acquisition. */
+class Context {
+ private:
+ /* A texture pool that can be used to allocate textures for the compositor efficiently. */
+ TexturePool &texture_pool_;
+ /* A static shader manager that can be used to acquire shaders for the compositor efficiently. */
+ StaticShaderManager shader_manager_;
+
+ public:
+ Context(TexturePool &texture_pool);
+
+ /* Get the active compositing scene. */
+ virtual const Scene *get_scene() const = 0;
+
+ /* Get the dimensions of the output. */
+ virtual int2 get_output_size() = 0;
+
+ /* Get the texture representing the output where the result of the compositor should be
+ * written. This should be called by output nodes to get their target texture. */
+ virtual GPUTexture *get_output_texture() = 0;
+
+ /* Get the texture where the given render pass is stored. This should be called by the Render
+ * Layer node to populate its outputs. */
+ virtual GPUTexture *get_input_texture(int view_layer, eScenePassType pass_type) = 0;
+
+ /* Get the name of the view currently being rendered. */
+ virtual StringRef get_view_name() = 0;
+
+ /* Set an info message. This is called by the compositor evaluator to inform or warn the user
+ * about something, typically an error. The implementation should display the message in an
+ * appropriate place, which can be directly in the UI or just logged to the output stream. */
+ virtual void set_info_message(StringRef message) const = 0;
+
+ /* Get the current frame number of the active scene. */
+ int get_frame_number() const;
+
+ /* Get the current time in seconds of the active scene. */
+ float get_time() const;
+
+ /* Get a reference to the texture pool of this context. */
+ TexturePool &texture_pool();
+
+ /* Get a reference to the static shader manager of this context. */
+ StaticShaderManager &shader_manager();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh
new file mode 100644
index 00000000000..310333aea5a
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "GPU_shader.h"
+
+#include "COM_context.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+
+namespace blender::realtime_compositor {
+
+/* -------------------------------------------------------------------- */
+/** \name Conversion Operation
+ *
+ * A simple operation that converts a result from a certain type to another. See the derived
+ * classes for more details.
+ * \{ */
+
+class ConversionOperation : public SimpleOperation {
+ public:
+ using SimpleOperation::SimpleOperation;
+
+ /* If the input result is a single value, execute_single is called. Otherwise, the shader
+ * provided by get_conversion_shader is dispatched. */
+ void execute() override;
+
+ /* Determine if a conversion operation is needed for the input with the given result and
+ * descriptor. If it is not needed, return a null pointer. If it is needed, return an instance of
+ * the appropriate conversion operation. */
+ static SimpleOperation *construct_if_needed(Context &context,
+ const Result &input_result,
+ const InputDescriptor &input_descriptor);
+
+ protected:
+ /* Convert the input single value result to the output single value result. */
+ virtual void execute_single(const Result &input, Result &output) = 0;
+
+ /* Get the shader the will be used for conversion. */
+ virtual GPUShader *get_conversion_shader() const = 0;
+
+ /** \} */
+
+}; // namespace blender::realtime_compositorclassConversionOperation:publicSimpleOperation
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Float to Vector Operation
+ *
+ * Takes a float result and outputs a vector result. All three components of the output are filled
+ * with the input float.
+ * \{ */
+
+class ConvertFloatToVectorOperation : public ConversionOperation {
+ public:
+ ConvertFloatToVectorOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Float to Color Operation
+ *
+ * Takes a float result and outputs a color result. All three color channels of the output are
+ * filled with the input float and the alpha channel is set to 1.
+ * \{ */
+
+class ConvertFloatToColorOperation : public ConversionOperation {
+ public:
+ ConvertFloatToColorOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Color to Float Operation
+ *
+ * Takes a color result and outputs a float result. The output is the average of the three color
+ * channels, the alpha channel is ignored.
+ * \{ */
+
+class ConvertColorToFloatOperation : public ConversionOperation {
+ public:
+ ConvertColorToFloatOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Color to Vector Operation
+ *
+ * Takes a color result and outputs a vector result. The output is a copy of the three color
+ * channels to the three vector components.
+ * \{ */
+
+class ConvertColorToVectorOperation : public ConversionOperation {
+ public:
+ ConvertColorToVectorOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Vector to Float Operation
+ *
+ * Takes a vector result and outputs a float result. The output is the average of the three
+ * components.
+ * \{ */
+
+/*
+ *
+ * */
+class ConvertVectorToFloatOperation : public ConversionOperation {
+ public:
+ ConvertVectorToFloatOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Vector to Color Operation
+ *
+ * Takes a vector result and outputs a color result. The output is a copy of the three vector
+ * components to the three color channels with the alpha channel set to 1.
+ * \{ */
+
+class ConvertVectorToColorOperation : public ConversionOperation {
+ public:
+ ConvertVectorToColorOperation(Context &context);
+
+ void execute_single(const Result &input, Result &output) override;
+
+ GPUShader *get_conversion_shader() const override;
+};
+
+/** \} */
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_domain.hh b/source/blender/compositor/realtime_compositor/COM_domain.hh
new file mode 100644
index 00000000000..54d712f7578
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_domain.hh
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <cstdint>
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
+namespace blender::realtime_compositor {
+
+/* Possible interpolations to use when realizing an input result of some domain on another domain.
+ * See the RealizationOptions struct for more information. */
+enum class Interpolation : uint8_t {
+ Nearest,
+ Bilinear,
+ Bicubic,
+};
+
+/* ------------------------------------------------------------------------------------------------
+ * Realization Options
+ *
+ * The options that describe how an input result prefer to be realized on some other domain. This
+ * is used by the Realize On Domain Operation to identify the appropriate method of realization.
+ * See the Domain class for more information. */
+struct RealizationOptions {
+ /* The interpolation method that should be used when performing realization. Since realizing a
+ * result involves projecting it on a different domain, which in turn, involves sampling the
+ * result at arbitrary locations, the interpolation identifies the method used for computing the
+ * value at those arbitrary locations. */
+ Interpolation interpolation = Interpolation::Nearest;
+ /* If true, the result will be repeated infinitely along the horizontal axis when realizing the
+ * result. If false, regions outside of bounds of the result along the horizontal axis will be
+ * filled with zeros. */
+ bool repeat_x = false;
+ /* If true, the result will be repeated infinitely along the vertical axis when realizing the
+ * result. If false, regions outside of bounds of the result along the vertical axis will be
+ * filled with zeros. */
+ bool repeat_y = false;
+};
+
+/* ------------------------------------------------------------------------------------------------
+ * Domain
+ *
+ * The compositor is designed in such a way as to allow compositing in an infinite virtual
+ * compositing space. Consequently, any result of an operation is not only represented by its image
+ * output, but also by its transformation in that virtual space. The transformation of the result
+ * together with the dimension of its image is stored and represented by a Domain. In the figure
+ * below, two results of different domains are illustrated on the virtual compositing space. One of
+ * the results is centered in space with an image dimension of 800px × 600px, and the other result
+ * is scaled down and translated such that it lies in the upper right quadrant of the space with an
+ * image dimension of 800px × 400px. The position of the domain is in pixel space, and the domain
+ * is considered centered if it has an identity transformation. Note that both results have the
+ * same resolution, but occupy different areas of the virtual compositing space.
+ *
+ * y
+ * ^
+ * 800px x 600px |
+ * .---------------------|---------------------.
+ * | | 800px x 600px |
+ * | | .-------------. |
+ * | | | | |
+ * | | '-------------' |
+ * ------|---------------------|---------------------|------> x
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * '---------------------|---------------------'
+ * |
+ *
+ * By default, results have domains of identity transformations, that is, they are centered in
+ * space, but a transformation operation like the rotate, translate, or transform operations will
+ * adjust the transformation to make the result reside somewhere different in space. The domain of
+ * a single value result is irrelevant and always set to an identity domain.
+ *
+ * An operation is typically only concerned about a subset of the virtual compositing space, this
+ * subset is represented by a domain which is called the Operation Domain. It follows that before
+ * the operation itself is executed, inputs will typically be realized on the operation domain to
+ * be in the same domain and have the same dimension as that of the operation domain. This process
+ * is called Domain Realization and is implemented using an operation called the Realize On Domain
+ * Operation. Realization involves projecting the result onto the target domain, copying the area
+ * of the result that intersects the target domain, and filling the rest with zeros or repetitions
+ * of the result depending on the realization options that can be set by the user. Consequently,
+ * operations can generally expect their inputs to have the same dimension and can operate on them
+ * directly and transparently. For instance, if an operation takes both results illustrated in
+ * the figure above, and the operation has an operation domain that matches the bigger domain, the
+ * result with the bigger domain will not need to be realized because it already has a domain that
+ * matches that of the operation domain, but the result with the smaller domain will have to be
+ * realized into a new result that has the same domain as the domain of the bigger result. Assuming
+ * no repetition, the output of the realization will be an all zeros image with dimension 800px ×
+ * 600px with a small scaled version of the smaller result copied into the upper right quadrant of
+ * the image. The following figure illustrates the realization process on a different set of
+ * results
+ *
+ * Realized Result
+ * +-------------+ +-------------+
+ * | Operation | | |
+ * | Domain | | Zeros |
+ * | | ----> | |
+ * +-----|-----+ | |-----+ |
+ * | | C | | | C | |
+ * | +-----|-------+ +-----'-------+
+ * | Domain Of |
+ * | Input |
+ * +-----------+
+ *
+ * An operation can operate in an arbitrary operation domain, but in most cases, the operation
+ * domain is inferred from the inputs of the operation. In particular, one of the inputs is said to
+ * be the Domain Input of the operation and the operation domain is inferred from its domain. It
+ * follows that this particular input will not need realization, because it already has the correct
+ * domain. The domain input selection mechanism is as follows. Each of the inputs are assigned a
+ * value by the developer called the Domain Priority, the domain input is then chosen as the
+ * non-single value input with the highest domain priority, zero being the highest priority. See
+ * Operation::compute_domain for more information.
+ *
+ * The aforementioned logic for operation domain computation is only a default that works for most
+ * cases, but an operation can override the compute_domain method to implement a different logic.
+ * For instance, output nodes have an operation domain the same size as the viewport and with an
+ * identity transformation, their operation domain doesn't depend on the inputs at all.
+ *
+ * For instance, a filter operation has two inputs, a factor and a color, the latter of which is
+ * assigned a domain priority of 0 and the former is assigned a domain priority of 1. If the color
+ * input is not a single value input, then the color input is considered to be the domain input of
+ * the operation and the operation domain is computed to be the same domain as the color input,
+ * because it has the highest priority. It follows that if the factor input has a different domain
+ * than the computed domain of the operation, it will be projected and realized on it to have the
+ * same domain as described above. On the other hand, if the color input is a single value input,
+ * then the factor input is considered to be the domain input and the operation domain will be the
+ * same as the domain of the factor input, because it has the second highest domain priority.
+ * Finally, if both inputs are single value inputs, the operation domain will be an identity domain
+ * and is irrelevant, because the output will be a domain-less single value. */
+class Domain {
+ public:
+ /* The size of the domain in pixels. */
+ int2 size;
+ /* The 2D transformation of the domain defining its translation in pixels, rotation, and scale in
+ * the virtual compositing space. */
+ float3x3 transformation;
+ /* The options that describe how this domain prefer to be realized on some other domain. See the
+ * RealizationOptions struct for more information. */
+ RealizationOptions realization_options;
+
+ public:
+ /* A size only constructor that sets the transformation to identity. */
+ Domain(int2 size);
+
+ Domain(int2 size, float3x3 transformation);
+
+ /* Transform the domain by the given transformation. This effectively pre-multiply the given
+ * transformation by the current transformation of the domain. */
+ void transform(const float3x3 &input_transformation);
+
+ /* Returns a domain of size 1x1 and an identity transformation. */
+ static Domain identity();
+};
+
+/* Compare the size and transformation of the domain. The realization_options are not compared
+ * because they only describe the method of realization on another domain, which is not technically
+ * a property of the domain itself. */
+bool operator==(const Domain &a, const Domain &b);
+
+/* Inverse of the above equality operator. */
+bool operator!=(const Domain &a, const Domain &b);
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_evaluator.hh b/source/blender/compositor/realtime_compositor/COM_evaluator.hh
new file mode 100644
index 00000000000..258a2a038c4
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_evaluator.hh
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <memory>
+
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_compile_state.hh"
+#include "COM_context.hh"
+#include "COM_node_operation.hh"
+#include "COM_operation.hh"
+#include "COM_shader_operation.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Evaluator
+ *
+ * The evaluator is the main class of the compositor and the entry point of its execution. The
+ * evaluator compiles the compositor node tree and evaluates it to compute its output. It is
+ * constructed from a compositor node tree and a compositor context. Upon calling the evaluate
+ * method, the evaluator will check if the node tree is already compiled into an operations stream,
+ * and if it is, it will go over it and evaluate the operations in order. It is then the
+ * responsibility of the caller to call the reset method when the node tree changes to invalidate
+ * the operations stream. A reset is also required if the resources used by the node tree change,
+ * for instances, when the dimensions of an image used by the node tree changes. This is necessary
+ * because the evaluator compiles the node tree into an operations stream that is specifically
+ * optimized for the structure of the resources used by the node tree.
+ *
+ * Otherwise, if the node tree is not yet compiled, the evaluator will compile it into an
+ * operations stream, evaluating the operations in the process. It should be noted that operations
+ * are evaluated as soon as they are compiled, as opposed to compiling the whole operations stream
+ * and then evaluating it in a separate step. This is important because, as mentioned before, the
+ * operations stream is optimized specifically for the structure of the resources used by the node
+ * tree, which is only known after the operations are evaluated. In other words, the evaluator uses
+ * the evaluated results of previously compiled operations to compile the operations that follow
+ * them in an optimized manner.
+ *
+ * Compilation starts by computing an optimized node execution schedule by calling the
+ * compute_schedule function, see the discussion in COM_scheduler.hh for more details. For the node
+ * tree shown below, the execution schedule is denoted by the node numbers. The compiler then goes
+ * over the execution schedule in order and compiles each node into either a Node Operation or a
+ * Shader Operation, depending on the node type, see the is_shader_node function. A Shader
+ * operation is constructed from a group of nodes forming a contiguous subset of the node execution
+ * schedule. For instance, in the node tree shown below, nodes 3 and 4 are compiled together into a
+ * shader operation and node 5 is compiled into its own shader operation, both of which are
+ * contiguous subsets of the node execution schedule. This process is described in details in the
+ * following section.
+ *
+ * Shader Operation 1 Shader Operation 2
+ * +-----------------------------------+ +------------------+
+ * .------------. | .------------. .------------. | | .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |---|-----|--| |--|--| |
+ * | | .-|--| | | | | .--|--| | | | |
+ * '------------' | | '------------' '------------' | | | '------------' | '------------'
+ * | +-----------------------------------+ | +------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'----------------------------------------'
+ * | |
+ * '------------'
+ *
+ * For non shader nodes, the compilation process is straight forward, the compiler instantiates a
+ * node operation from the node, map its inputs to the results of the outputs they are linked to,
+ * and evaluates the operations. However, for shader nodes, since a group of nodes can be compiled
+ * together into a shader operation, the compilation process is a bit involved. The compiler uses
+ * an instance of the Compile State class to keep track of the compilation process. The compiler
+ * state stores the so called "shader compile unit", which is the current group of nodes that will
+ * eventually be compiled together into a shader operation. While going over the schedule, the
+ * compiler adds the shader nodes to the compile unit until it decides that the compile unit is
+ * complete and should be compiled. This is typically decided when the current node is not
+ * compatible with the compile unit and can't be added to it, only then it compiles the compile
+ * unit into a shader operation and resets it to ready it to track the next potential group of
+ * nodes that will form a shader operation. This decision is made based on various criteria in the
+ * should_compile_shader_compile_unit function. See the discussion in COM_compile_state.hh for more
+ * details of those criteria, but perhaps the most evident of which is whether the node is actually
+ * a shader node, if it isn't, then it evidently can't be added to the compile unit and the compile
+ * unit is should be compiled.
+ *
+ * For the node tree above, the compilation process is as follows. The compiler goes over the node
+ * execution schedule in order considering each node. Nodes 1 and 2 are not shader node so they are
+ * compiled into node operations and added to the operations stream. The current compile unit is
+ * empty, so it is not compiled. Node 3 is a shader node, and since the compile unit is currently
+ * empty, it is unconditionally added to it. Node 4 is a shader node, it was decided---for the sake
+ * of the demonstration---that it is compatible with the compile unit and can be added to it. Node
+ * 5 is a shader node, but it was decided---for the sake of the demonstration---that it is not
+ * compatible with the compile unit, so the compile unit is considered complete and is compiled
+ * first, adding the first shader operation to the operations stream and resetting the compile
+ * unit. Node 5 is then added to the now empty compile unit similar to node 3. Node 6 is not a
+ * shader node, so the compile unit is considered complete and is compiled first, adding the first
+ * shader operation to the operations stream and resetting the compile unit. Finally, node 6 is
+ * compiled into a node operation similar to nodes 1 and 2 and added to the operations stream. */
+class Evaluator {
+ private:
+ /* A reference to the compositor context. */
+ Context &context_;
+ /* A reference to the compositor node tree. */
+ bNodeTree &node_tree_;
+ std::unique_ptr<DerivedNodeTree> derived_node_tree_;
+ /* The compiled operations stream. This contains ordered pointers to the operations that were
+ * compiled. This is initialized when the node tree is compiled and freed when the evaluator
+ * resets. The is_compiled_ member indicates whether the operation stream can be used or needs to
+ * be compiled first. Note that the operations stream can be empty even when compiled, this can
+ * happen when the node tree is empty or invalid for instance. */
+ Vector<std::unique_ptr<Operation>> operations_stream_;
+ /* True if the node tree is already compiled into an operations stream that can be evaluated
+ * directly. False if the node tree is not compiled yet and needs to be compiled. */
+ bool is_compiled_ = false;
+
+ public:
+ /* Construct an evaluator from a compositor node tree and a context. */
+ Evaluator(Context &context, bNodeTree &node_tree);
+
+ /* Evaluate the compositor node tree. If the node tree is already compiled into an operations
+ * stream, that stream will be evaluated directly. Otherwise, the node tree will be compiled and
+ * evaluated. */
+ void evaluate();
+
+ /* Invalidate the operations stream that was compiled for the node tree. This should be called
+ * when the node tree changes or the structure of any of the resources used by it changes. By
+ * structure, we mean things like the dimensions of the used images, while changes to their
+ * contents do not necessitate a reset. */
+ void reset();
+
+ private:
+ /* Check if the compositor node tree is valid by checking if it has:
+ * - Cyclic links.
+ * - Undefined nodes or sockets.
+ * - Unsupported nodes.
+ * If the node tree is valid, true is returned. Otherwise, false is returned, and an appropriate
+ * error message is set by calling the context's set_info_message method. */
+ bool validate_node_tree();
+
+ /* Compile the node tree into an operations stream and evaluate it. */
+ void compile_and_evaluate();
+
+ /* Compile the given node into a node operation, map each input to the result of the output
+ * linked to it, update the compile state, add the newly created operation to the operations
+ * stream, and evaluate the operation. */
+ void compile_and_evaluate_node(DNode node, CompileState &compile_state);
+
+ /* Map each input of the node operation to the result of the output linked to it. Unlinked inputs
+ * are mapped to the result of a newly created Input Single Value Operation, which is added to
+ * the operations stream and evaluated. Since this method might add operations to the operations
+ * stream, the actual node operation should only be added to the stream once this method is
+ * called. */
+ void map_node_operation_inputs_to_their_results(DNode node,
+ NodeOperation *operation,
+ CompileState &compile_state);
+
+ /* Compile the shader compile unit into a shader operation, map each input of the operation to
+ * the result of the output linked to it, update the compile state, add the newly created
+ * operation to the operations stream, evaluate the operation, and finally reset the shader
+ * compile unit. */
+ void compile_and_evaluate_shader_compile_unit(CompileState &compile_state);
+
+ /* Map each input of the shader operation to the result of the output linked to it. */
+ void map_shader_operation_inputs_to_their_results(ShaderOperation *operation,
+ CompileState &compile_state);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh
new file mode 100644
index 00000000000..215a92ab3b4
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Input Descriptor
+ *
+ * A class that describes an input of an operation. */
+class InputDescriptor {
+ public:
+ /* The type of input. This may be different that the type of result that the operation will
+ * receive for the input, in which case, an implicit conversion operation will be added as an
+ * input processor to convert it to the required type. */
+ ResultType type;
+ /* If true, then the input does not need to be realized on the domain of the operation before its
+ * execution. See the discussion in COM_domain.hh for more information. */
+ bool skip_realization = false;
+ /* The priority of the input for determining the operation domain. The non-single value input
+ * with the highest priority will be used to infer the operation domain, the highest priority
+ * being zero. See the discussion in COM_domain.hh for more information. */
+ int domain_priority = 0;
+ /* If true, the input expects a single value, and if a non-single value is provided, a default
+ * single value will be used instead, see the get_<type>_value_default methods in the Result
+ * class. It follows that this also implies skip_realization, because we don't need to realize a
+ * result that will be discarded anyways. If false, the input can work with both single and
+ * non-single values. */
+ bool expects_single_value = false;
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh
new file mode 100644
index 00000000000..bbcd336ee11
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_context.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Input Single Value Operation
+ *
+ * An input single value operation is an operation that outputs a single value result whose value
+ * is the value of an unlinked input socket. This is typically used to initialize the values of
+ * unlinked node input sockets. */
+class InputSingleValueOperation : public Operation {
+ private:
+ /* The identifier of the output. */
+ static const StringRef output_identifier_;
+ /* The input socket whose value will be computed as the operation's result. */
+ DInputSocket input_socket_;
+
+ public:
+ InputSingleValueOperation(Context &context, DInputSocket input_socket);
+
+ /* Allocate a single value result and set its value to the default value of the input socket. */
+ void execute() override;
+
+ /* Get a reference to the output result of the operation, this essentially calls the super
+ * get_result with the output identifier of the operation. */
+ Result &get_result();
+
+ private:
+ /* Populate the result of the operation, this essentially calls the super populate_result method
+ * with the output identifier of the operation. */
+ void populate_result(Result result);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_node_operation.hh b/source/blender/compositor/realtime_compositor/COM_node_operation.hh
new file mode 100644
index 00000000000..94bc4dfd39d
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_node_operation.hh
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_context.hh"
+#include "COM_operation.hh"
+#include "COM_scheduler.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Node Operation
+ *
+ * A node operation is a subclass of operation that nodes should implement and instantiate in the
+ * get_compositor_operation function of bNodeType, passing the inputs given to that function to the
+ * constructor. This class essentially just implements a default constructor that populates output
+ * results for all outputs of the node as well as input descriptors for all inputs of the nodes
+ * based on their socket declaration. The class also provides some utility methods for easier
+ * implementation of nodes. */
+class NodeOperation : public Operation {
+ private:
+ /* The node that this operation represents. */
+ DNode node_;
+
+ public:
+ /* Populate the output results based on the node outputs and populate the input descriptors based
+ * on the node inputs. */
+ NodeOperation(Context &context, DNode node);
+
+ /* Compute and set the initial reference counts of all the results of the operation. The
+ * reference counts of the results are the number of operations that use those results, which is
+ * computed as the number of inputs whose node is part of the schedule and is linked to the
+ * output corresponding to each result. The node execution schedule is given as an input. */
+ void compute_results_reference_counts(const Schedule &schedule);
+
+ protected:
+ /* Returns a reference to the derived node that this operation represents. */
+ const DNode &node() const;
+
+ /* Returns a reference to the node that this operation represents. */
+ const bNode &bnode() const;
+
+ /* Returns true if the output identified by the given identifier is needed and should be
+ * computed, otherwise returns false. */
+ bool should_compute_output(StringRef identifier);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_operation.hh b/source/blender/compositor/realtime_compositor/COM_operation.hh
new file mode 100644
index 00000000000..38518c00c04
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_operation.hh
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "COM_context.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+#include "COM_static_shader_manager.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+class SimpleOperation;
+
+/* A type representing a vector of simple operations that store the input processors for a
+ * particular input. */
+using ProcessorsVector = Vector<std::unique_ptr<SimpleOperation>>;
+
+/* ------------------------------------------------------------------------------------------------
+ * Operation
+ *
+ * The operation is the basic unit of the compositor. The evaluator compiles the compositor node
+ * tree into an ordered stream of operations which are then executed in order during evaluation.
+ * The operation class can be sub-classed to implement a new operation. Operations have a number of
+ * inputs and outputs that are declared during construction and are identified by string
+ * identifiers. Inputs are declared by calling declare_input_descriptor providing an appropriate
+ * descriptor. Those inputs are mapped to the results computed by other operations whose outputs
+ * are linked to the inputs. Such mappings are established by the compiler during compilation by
+ * calling the map_input_to_result method. Outputs are populated by calling the populate_result
+ * method, providing a result of an appropriate type. Upon execution, the operation allocates a
+ * result for each of its outputs and computes their value based on its inputs and options.
+ *
+ * Each input may have one or more input processors, which are simple operations that process the
+ * inputs before the operation is executed, see the discussion in COM_simple_operation.hh for more
+ * information. And thus the effective input of the operation is the result of the last input
+ * processor if one exists. Input processors are added and evaluated by calling the
+ * add_and_evaluate_input_processors method, which provides a default implementation that does
+ * things like implicit conversion, domain realization, and more. This default implementation can,
+ * however, be overridden, extended, or removed. Once the input processors are added and evaluated
+ * for the first time, they are stored in the operation and future evaluations can evaluate them
+ * directly without having to add them again.
+ *
+ * The operation is evaluated by calling the evaluate method, which first adds the input processors
+ * if they weren't added already and evaluates them, then it resets the results of the operation,
+ * then it calls the execute method of the operation, and finally it releases the results mapped to
+ * the inputs to declare that they are no longer needed. */
+class Operation {
+ private:
+ /* A reference to the compositor context. This member references the same object in all
+ * operations but is included in the class for convenience. */
+ Context &context_;
+ /* A mapping between each output of the operation identified by its identifier and the result for
+ * that output. A result for each output of the operation should be constructed and added to the
+ * map during operation construction by calling the populate_result method. The results should be
+ * allocated and their contents should be computed in the execute method. */
+ Map<std::string, Result> results_;
+ /* A mapping between each input of the operation identified by its identifier and its input
+ * descriptor. Those descriptors should be declared during operation construction by calling the
+ * declare_input_descriptor method. */
+ Map<std::string, InputDescriptor> input_descriptors_;
+ /* A mapping between each input of the operation identified by its identifier and a pointer to
+ * the computed result providing its data. The mapped result is either one that was computed by
+ * another operation or one that was internally computed in the operation by the last input
+ * processor for that input. It is the responsibility of the evaluator to map the inputs to their
+ * linked results before evaluating the operation by calling the map_input_to_result method. */
+ Map<StringRef, Result *> results_mapped_to_inputs_;
+ /* A mapping between each input of the operation identified by its identifier and an ordered list
+ * of simple operations to process that input. This is initialized the first time the input
+ * processors are evaluated by calling the add_and_evaluate_input_processors method. Further
+ * evaluations will evaluate the processors directly without the need to add them again. The
+ * input_processors_added_ member indicates whether the processors were already added and can be
+ * evaluated directly or need to be added and evaluated. */
+ Map<StringRef, ProcessorsVector> input_processors_;
+ /* True if the input processors were already added and can be evaluated directly. False if the
+ * input processors are not yet added and needs to be added. */
+ bool input_processors_added_ = false;
+
+ public:
+ Operation(Context &context);
+
+ virtual ~Operation();
+
+ /* Evaluate the operation by:
+ * 1. Evaluating the input processors.
+ * 2. Resetting the results of the operation.
+ * 3. Calling the execute method of the operation.
+ * 4. Releasing the results mapped to the inputs. */
+ void evaluate();
+
+ /* Get a reference to the output result identified by the given identifier. */
+ Result &get_result(StringRef identifier);
+
+ /* Map the input identified by the given identifier to the result providing its data. See
+ * results_mapped_to_inputs_ for more details. This should be called by the evaluator to
+ * establish links between different operations. */
+ void map_input_to_result(StringRef identifier, Result *result);
+
+ protected:
+ /* Compute the operation domain of this operation. By default, this implements a default logic
+ * that infers the operation domain from the inputs, which may be overridden for a different
+ * logic. See the discussion in COM_domain.hh for the inference logic and more information. */
+ virtual Domain compute_domain();
+
+ /* Add and evaluate any needed input processors, which essentially just involves calling the
+ * add_and_evaluate_input_processor method with the needed processors. This is called before
+ * executing the operation to prepare its inputs. The class defines a default implementation
+ * which adds typically needed processors, but derived classes can override the method to have
+ * a different implementation, extend the implementation, or remove it entirely. */
+ virtual void add_and_evaluate_input_processors();
+
+ /* Given the identifier of an input of the operation and a processor operation:
+ * - Add the given processor to the list of input processors for the input.
+ * - Map the input of the processor to be the result of the last input processor or the result
+ * mapped to the input if no previous processors exists.
+ * - Switch the result mapped to the input to be the output result of the processor.
+ * - Evaluate the processor. */
+ void add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor);
+
+ /* This method should allocate the operation results, execute the operation, and compute the
+ * output results. */
+ virtual void execute() = 0;
+
+ /* Get a reference to the result connected to the input identified by the given identifier. */
+ Result &get_input(StringRef identifier) const;
+
+ /* Switch the result mapped to the input identified by the given identifier with the given
+ * result. */
+ void switch_result_mapped_to_input(StringRef identifier, Result *result);
+
+ /* Add the given result to the results_ map identified by the given output identifier. This
+ * should be called during operation construction for all outputs. The provided result shouldn't
+ * be allocated or initialized, this will happen later during execution. */
+ void populate_result(StringRef identifier, Result result);
+
+ /* Declare the descriptor of the input identified by the given identifier to be the given
+ * descriptor. Adds the given descriptor to the input_descriptors_ map identified by the given
+ * input identifier. This should be called during operation constructor for all inputs. */
+ void declare_input_descriptor(StringRef identifier, InputDescriptor descriptor);
+
+ /* Get a reference to the descriptor of the input identified by the given identified. */
+ InputDescriptor &get_input_descriptor(StringRef identifier);
+
+ /* Returns a reference to the compositor context. */
+ Context &context();
+
+ /* Returns a reference to the texture pool of the compositor context. */
+ TexturePool &texture_pool() const;
+
+ /* Returns a reference to the shader manager of the compositor context. */
+ StaticShaderManager &shader_manager() const;
+
+ private:
+ /* Evaluate the input processors. If the input processors were already added they will be
+ * evaluated directly. Otherwise, the input processors will be added and evaluated. */
+ void evaluate_input_processors();
+
+ /* Resets the results of the operation. See the reset method in the Result class for more
+ * information. */
+ void reset_results();
+
+ /* Release the results that are mapped to the inputs of the operation. This is called after the
+ * evaluation of the operation to declare that the results are no longer needed by this
+ * operation. */
+ void release_inputs();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh
new file mode 100644
index 00000000000..5a842e16008
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "GPU_shader.h"
+
+#include "COM_context.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Realize On Domain Operation
+ *
+ * A simple operation that projects the input on a certain target domain, copies the area of the
+ * input that intersects the target domain, and fill the rest with zeros or repetitions of the
+ * input depending on the realization options of the target domain. See the discussion in
+ * COM_domain.hh for more information. */
+class RealizeOnDomainOperation : public SimpleOperation {
+ private:
+ /* The target domain to realize the input on. */
+ Domain domain_;
+
+ public:
+ RealizeOnDomainOperation(Context &context, Domain domain, ResultType type);
+
+ void execute() override;
+
+ /* Determine if a realize on domain operation is needed for the input with the given result and
+ * descriptor in an operation with the given operation domain. If it is not needed, return a null
+ * pointer. If it is needed, return an instance of the operation. */
+ static SimpleOperation *construct_if_needed(Context &context,
+ const Result &input_result,
+ const InputDescriptor &input_descriptor,
+ const Domain &operation_domain);
+
+ protected:
+ /* The operation domain is just the target domain. */
+ Domain compute_domain() override;
+
+ private:
+ /* Get the realization shader of the appropriate type. */
+ GPUShader *get_realization_shader();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh
new file mode 100644
index 00000000000..2f5a82c79b7
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "COM_context.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Reduce To Single Value Operation
+ *
+ * A simple operation that reduces its input result into a single value output result. The input is
+ * assumed to be a texture result of size 1x1, that is, a texture composed of a single pixel, the
+ * value of which shall serve as the single value of the output result. */
+class ReduceToSingleValueOperation : public SimpleOperation {
+ public:
+ ReduceToSingleValueOperation(Context &context, ResultType type);
+
+ /* Download the input pixel from the GPU texture and set its value to the value of the allocated
+ * single value output result. */
+ void execute() override;
+
+ /* Determine if a reduce to single value operation is needed for the input with the
+ * given result. If it is not needed, return a null pointer. If it is needed, return an instance
+ * of the operation. */
+ static SimpleOperation *construct_if_needed(Context &context, const Result &input_result);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_result.hh b/source/blender/compositor/realtime_compositor/COM_result.hh
new file mode 100644
index 00000000000..a16d68bb92d
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_result.hh
@@ -0,0 +1,234 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_domain.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+/* Possible data types that operations can operate on. They either represent the base type of the
+ * result texture or a single value result. */
+enum class ResultType : uint8_t {
+ Float,
+ Vector,
+ Color,
+};
+
+/* ------------------------------------------------------------------------------------------------
+ * Result
+ *
+ * A result represents the computed value of an output of an operation. A result can either
+ * represent an image or a single value. A result is typed, and can be of type color, vector, or
+ * float. Single value results are stored in 1x1 textures to make them easily accessible in
+ * shaders. But the same value is also stored in the value union member of the result for any
+ * host-side processing. The texture of the result is allocated from the texture pool referenced by
+ * the result.
+ *
+ * Results are reference counted and their textures are released once their reference count reaches
+ * zero. After constructing a result, the set_initial_reference_count method is called to declare
+ * the number of operations that needs this result. Once each operation that needs the result no
+ * longer needs it, the release method is called and the reference count is decremented, until it
+ * reaches zero, where the result's texture is then released. Since results are eventually
+ * decremented to zero by the end of every evaluation, the reference count is restored before every
+ * evaluation to its initial reference count by calling the reset method, which is why a separate
+ * member initial_reference_count_ is stored to keep track of the initial value.
+ *
+ * A result not only represents an image, but also the area it occupies in the virtual compositing
+ * space. This area is called the Domain of the result, see the discussion in COM_domain.hh for
+ * more information.
+ *
+ * A result can be a proxy result that merely wraps another master result, in which case, it shares
+ * its values and delegates all reference counting to it. While a proxy result shares the value of
+ * the master result, it can have a different domain. Consequently, transformation operations are
+ * implemented using proxy results, where their results are proxy results of their inputs but with
+ * their domains transformed based on their options. Moreover, proxy results can also be used as
+ * the results of identity operations, that is, operations that do nothing to their inputs in
+ * certain configurations. In which case, the proxy result is left as is with no extra
+ * transformation on its domain whatsoever. Proxy results can be created by calling the
+ * pass_through method, see that method for more details. */
+class Result {
+ private:
+ /* The base type of the texture or the type of the single value. */
+ ResultType type_;
+ /* If true, the result is a single value, otherwise, the result is a texture. */
+ bool is_single_value_;
+ /* A GPU texture storing the result data. This will be a 1x1 texture if the result is a single
+ * value, the value of which will be identical to that of the value member. See class description
+ * for more information. */
+ GPUTexture *texture_ = nullptr;
+ /* The texture pool used to allocate the texture of the result, this should be initialized during
+ * construction. */
+ TexturePool *texture_pool_ = nullptr;
+ /* The number of operations that currently needs this result. At the time when the result is
+ * computed, this member will have a value that matches initial_reference_count_. Once each
+ * operation that needs the result no longer needs it, the release method is called and the
+ * reference count is decremented, until it reaches zero, where the result's texture is then
+ * released. If this result have a master result, then this reference count is irrelevant and
+ * shadowed by the reference count of the master result. */
+ int reference_count_;
+ /* The number of operations that reference and use this result at the time when it was initially
+ * computed. Since reference_count_ is decremented and always becomes zero at the end of the
+ * evaluation, this member is used to reset the reference count of the results for later
+ * evaluations by calling the reset method. This member is also used to determine if this result
+ * should be computed by calling the should_compute method. */
+ int initial_reference_count_;
+ /* If the result is a single value, this member stores the value of the result, the value of
+ * which will be identical to that stored in the texture member. The active union member depends
+ * on the type of the result. This member is uninitialized and should not be used if the result
+ * is a texture. */
+ union {
+ float float_value_;
+ float3 vector_value_;
+ float4 color_value_;
+ };
+ /* The domain of the result. This only matters if the result was a texture. See the discussion in
+ * COM_domain.hh for more information. */
+ Domain domain_ = Domain::identity();
+ /* If not nullptr, then this result wraps and shares the value of another master result. In this
+ * case, calls to texture-related methods like increment_reference_count and release should
+ * operate on the master result as opposed to this result. This member is typically set upon
+ * calling the pass_through method, which sets this result to be the master of a target result.
+ * See that method for more information. */
+ Result *master_ = nullptr;
+
+ public:
+ /* Construct a result of the given type with the given texture pool that will be used to allocate
+ * and release the result's texture. */
+ Result(ResultType type, TexturePool &texture_pool);
+
+ /* Declare the result to be a texture result, allocate a texture of an appropriate type with
+ * the size of the given domain from the result's texture pool, and set the domain of the result
+ * to the given domain. */
+ void allocate_texture(Domain domain);
+
+ /* Declare the result to be a single value result, allocate a texture of an appropriate
+ * type with size 1x1 from the result's texture pool, and set the domain to be an identity
+ * domain. See class description for more information. */
+ void allocate_single_value();
+
+ /* Allocate a single value result and set its value to zero. This is called for results whose
+ * value can't be computed and are considered invalid. */
+ void allocate_invalid();
+
+ /* Bind the texture of the result to the texture image unit with the given name in the currently
+ * bound given shader. This also inserts a memory barrier for texture fetches to ensure any prior
+ * writes to the texture are reflected before reading from it. */
+ void bind_as_texture(GPUShader *shader, const char *texture_name) const;
+
+ /* Bind the texture of the result to the image unit with the given name in the currently bound
+ * given shader. */
+ void bind_as_image(GPUShader *shader, const char *image_name) const;
+
+ /* Unbind the texture which was previously bound using bind_as_texture. */
+ void unbind_as_texture() const;
+
+ /* Unbind the texture which was previously bound using bind_as_image. */
+ void unbind_as_image() const;
+
+ /* Pass this result through to a target result, in which case, the target result becomes a proxy
+ * result with this result as its master result. This is done by making the target result a copy
+ * of this result, essentially having identical values between the two and consequently sharing
+ * the underlying texture. An exception is the initial reference count, whose value is retained
+ * and not copied, because it is a property of the original result and is needed for correctly
+ * resetting the result before the next evaluation. Additionally, this result is set to be the
+ * master of the target result, by setting the master member of the target. Finally, the
+ * reference count of the result is incremented by the reference count of the target result. See
+ * the discussion above for more information. */
+ void pass_through(Result &target);
+
+ /* Transform the result by the given transformation. This effectively pre-multiply the given
+ * transformation by the current transformation of the domain of the result. */
+ void transform(const float3x3 &transformation);
+
+ /* Get a reference to the realization options of this result. See the RealizationOptions struct
+ * for more information. */
+ RealizationOptions &get_realization_options();
+
+ /* If the result is a single value result of type float, return its float value. Otherwise, an
+ * uninitialized value is returned. */
+ float get_float_value() const;
+
+ /* If the result is a single value result of type vector, return its vector value. Otherwise, an
+ * uninitialized value is returned. */
+ float3 get_vector_value() const;
+
+ /* If the result is a single value result of type color, return its color value. Otherwise, an
+ * uninitialized value is returned. */
+ float4 get_color_value() const;
+
+ /* Same as get_float_value but returns a default value if the result is not a single value. */
+ float get_float_value_default(float default_value) const;
+
+ /* Same as get_vector_value but returns a default value if the result is not a single value. */
+ float3 get_vector_value_default(const float3 &default_value) const;
+
+ /* Same as get_color_value but returns a default value if the result is not a single value. */
+ float4 get_color_value_default(const float4 &default_value) const;
+
+ /* If the result is a single value result of type float, set its float value and upload it to the
+ * texture. Otherwise, an undefined behavior is invoked. */
+ void set_float_value(float value);
+
+ /* If the result is a single value result of type vector, set its vector value and upload it to
+ * the texture. Otherwise, an undefined behavior is invoked. */
+ void set_vector_value(const float3 &value);
+
+ /* If the result is a single value result of type color, set its color value and upload it to the
+ * texture. Otherwise, an undefined behavior is invoked. */
+ void set_color_value(const float4 &value);
+
+ /* Set the value of initial_reference_count_, see that member for more details. This should be
+ * called after constructing the result to declare the number of operations that needs it. */
+ void set_initial_reference_count(int count);
+
+ /* Reset the result to prepare it for a new evaluation. This should be called before evaluating
+ * the operation that computes this result. First, set the value of reference_count_ to the value
+ * of initial_reference_count_ since reference_count_ may have already been decremented to zero
+ * in a previous evaluation. Second, set master_ to nullptr because the result may have been
+ * turned into a proxy result in a previous evaluation. Other fields don't need to be reset
+ * because they are runtime and overwritten during evaluation. */
+ void reset();
+
+ /* Increment the reference count of the result by the given count. If this result have a master
+ * result, the reference count of the master result is incremented instead. */
+ void increment_reference_count(int count = 1);
+
+ /* Decrement the reference count of the result and release the its texture back into the texture
+ * pool if the reference count reaches zero. This should be called when an operation that used
+ * this result no longer needs it. If this result have a master result, the master result is
+ * released instead. */
+ void release();
+
+ /* Returns true if this result should be computed and false otherwise. The result should be
+ * computed if its reference count is not zero, that is, its result is used by at least one
+ * operation. */
+ bool should_compute();
+
+ /* Returns the type of the result. */
+ ResultType type() const;
+
+ /* Returns true if the result is a texture and false of it is a single value. */
+ bool is_texture() const;
+
+ /* Returns true if the result is a single value and false of it is a texture. */
+ bool is_single_value() const;
+
+ /* Returns the allocated GPU texture of the result. */
+ GPUTexture *texture() const;
+
+ /* Returns the reference count of the result. If this result have a master result, then the
+ * reference count of the master result is returned instead. */
+ int reference_count() const;
+
+ /* Returns a reference to the domain of the result. See the Domain class. */
+ const Domain &domain() const;
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_scheduler.hh b/source/blender/compositor/realtime_compositor/COM_scheduler.hh
new file mode 100644
index 00000000000..4f778b32145
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_scheduler.hh
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_vector_set.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* A type representing the ordered set of nodes defining the schedule of node execution. */
+using Schedule = VectorSet<DNode>;
+
+/* Computes the execution schedule of the node tree. This is essentially a post-order depth first
+ * traversal of the node tree from the output node to the leaf input nodes, with informed order of
+ * traversal of dependencies based on a heuristic estimation of the number of needed buffers. */
+Schedule compute_schedule(DerivedNodeTree &tree);
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_shader_node.hh b/source/blender/compositor/realtime_compositor/COM_shader_node.hh
new file mode 100644
index 00000000000..50337935d03
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_shader_node.hh
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+
+#include "GPU_material.h"
+
+#include "NOD_derived_node_tree.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* ------------------------------------------------------------------------------------------------
+ * Shader Node
+ *
+ * A shader node encapsulates a compositor node tree that is capable of being used together with
+ * other shader nodes to construct a Shader Operation using the GPU material compiler. A GPU node
+ * stack for each of the node inputs and outputs is stored and populated during construction in
+ * order to represent the node as a GPU node inside the GPU material graph, see GPU_material.h for
+ * more information. Derived classes should implement the compile method to add the node and link
+ * it to the GPU material given to the method. The compiler is expected to initialize the input
+ * links of the node before invoking the compile method. See the discussion in
+ * COM_shader_operation.hh for more information. */
+class ShaderNode {
+ private:
+ /* The node that this operation represents. */
+ DNode node_;
+ /* The GPU node stacks of the inputs of the node. Those are populated during construction in the
+ * populate_inputs method. The links of the inputs are initialized by the GPU material compiler
+ * prior to calling the compile method. There is an extra stack at the end to mark the end of the
+ * array, as this is what the GPU module functions expect. */
+ Vector<GPUNodeStack> inputs_;
+ /* The GPU node stacks of the outputs of the node. Those are populated during construction in the
+ * populate_outputs method. There is an extra stack at the end to mark the end of the array, as
+ * this is what the GPU module functions expect. */
+ Vector<GPUNodeStack> outputs_;
+
+ public:
+ /* Construct the node by populating both its inputs and outputs. */
+ ShaderNode(DNode node);
+
+ virtual ~ShaderNode() = default;
+
+ /* Compile the node by adding the appropriate GPU material graph nodes and linking the
+ * appropriate resources. */
+ virtual void compile(GPUMaterial *material) = 0;
+
+ /* Returns a contiguous array containing the GPU node stacks of each input. */
+ GPUNodeStack *get_inputs_array();
+
+ /* Returns a contiguous array containing the GPU node stacks of each output. */
+ GPUNodeStack *get_outputs_array();
+
+ /* Returns the GPU node stack of the input with the given identifier. */
+ GPUNodeStack &get_input(StringRef identifier);
+
+ /* Returns the GPU node stack of the output with the given identifier. */
+ GPUNodeStack &get_output(StringRef identifier);
+
+ /* Returns the GPU node link of the input with the given identifier, if the input is not linked,
+ * a uniform link carrying the value of the input will be created a returned. It is expected that
+ * the caller will use the returned link in a GPU material, otherwise, the link may not be
+ * properly freed. */
+ GPUNodeLink *get_input_link(StringRef identifier);
+
+ protected:
+ /* Returns a reference to the derived node that this operation represents. */
+ const DNode &node() const;
+
+ /* Returns a reference to the node this operations represents. */
+ const bNode &bnode() const;
+
+ private:
+ /* Populate the inputs of the node. The input link is set to nullptr and is expected to be
+ * initialized by the GPU material compiler before calling the compile method. */
+ void populate_inputs();
+ /* Populate the outputs of the node. The output link is set to nullptr and is expected to be
+ * initialized by the compile method. */
+ void populate_outputs();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_shader_operation.hh b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh
new file mode 100644
index 00000000000..d03e52ac8f2
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <memory>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector_set.hh"
+
+#include "GPU_material.h"
+#include "GPU_shader.h"
+
+#include "gpu_shader_create_info.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_context.hh"
+#include "COM_operation.hh"
+#include "COM_scheduler.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* A type representing a contiguous subset of the node execution schedule that will be compiled
+ * into a Shader Operation. */
+using ShaderCompileUnit = VectorSet<DNode>;
+
+/* ------------------------------------------------------------------------------------------------
+ * Shader Operation
+ *
+ * An operation that evaluates a shader compiled from a contiguous subset of the node execution
+ * schedule using the GPU material compiler, see GPU_material.h for more information. The subset
+ * of the node execution schedule is called a shader compile unit, see the discussion in
+ * COM_compile_state.hh for more information.
+ *
+ * Consider the following node graph with a node execution schedule denoted by the number on each
+ * node. The compiler may decide to compile a subset of the execution schedule into a shader
+ * operation, in this case, the nodes from 3 to 5 were compiled together into a shader operation.
+ * This subset is called the shader compile unit. See the discussion in COM_evaluator.hh for more
+ * information on the compilation process. Each of the nodes inside the compile unit implements a
+ * Shader Node which is instantiated, stored in shader_nodes_, and used during compilation. See the
+ * discussion in COM_shader_node.hh for more information. Links that are internal to the shader
+ * operation are established between the input and outputs of the shader nodes, for instance, the
+ * links between nodes 3 and 4 as well as those between nodes 4 and 5. However, links that cross
+ * the boundary of the shader operation needs special handling.
+ *
+ * Shader Operation
+ * +------------------------------------------------------+
+ * .------------. | .------------. .------------. .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |------| |--|--| |
+ * | | .-|--| | | | .---| | | | |
+ * '------------' | | '------------' '------------' | '------------' | '------------'
+ * | +----------------------------------|-------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'------------------------------------'
+ * | |
+ * '------------'
+ *
+ * Links from nodes that are not part of the shader operation to nodes that are part of the shader
+ * operation are considered inputs of the operation itself and are declared as such. For instance,
+ * the link from node 1 to node 3 is declared as an input to the operation, and the same applies
+ * for the links from node 2 to nodes 3 and 5. Note, however, that only one input is declared for
+ * each distinct output socket, so both links from node 2 share the same input of the operation.
+ * An input to the operation is declared for a distinct output socket as follows:
+ *
+ * - A texture is added to the shader, which will be bound to the result of the output socket
+ * during evaluation.
+ * - A GPU attribute is added to the GPU material for that output socket and is linked to the GPU
+ * input stack of the inputs linked to the output socket.
+ * - Code is emitted to initialize the values of the attributes by sampling the textures
+ * corresponding to each of the inputs.
+ * - The newly added attribute is mapped to the output socket in output_to_material_attribute_map_
+ * to share that same attributes for all inputs linked to the same output socket.
+ *
+ * Links from nodes that are part of the shader operation to nodes that are not part of the shader
+ * operation are considered outputs of the operation itself and are declared as such. For instance,
+ * the link from node 5 to node 6 is declared as an output to the operation. An output to the
+ * operation is declared for an output socket as follows:
+ *
+ * - An image is added in the shader where the output value will be written.
+ * - A storer GPU material node that stores the value of the output is added and linked to the GPU
+ * output stack of the output. The storer will store the value in the image identified by the
+ * index of the output given to the storer.
+ * - The storer functions are generated dynamically to map each index with its appropriate image.
+ *
+ * The GPU material code generator source is used to construct a compute shader that is then
+ * dispatched during operation evaluation after binding the inputs, outputs, and any necessary
+ * resources. */
+class ShaderOperation : public Operation {
+ private:
+ /* The compile unit that will be compiled into this shader operation. */
+ ShaderCompileUnit compile_unit_;
+ /* The GPU material backing the operation. This is created and compiled during construction and
+ * freed during destruction. */
+ GPUMaterial *material_;
+ /* A map that associates each node in the compile unit with an instance of its shader node. */
+ Map<DNode, std::unique_ptr<ShaderNode>> shader_nodes_;
+ /* A map that associates the identifier of each input of the operation with the output socket it
+ * is linked to. This is needed to help the compiler establish links between operations. */
+ Map<std::string, DOutputSocket> inputs_to_linked_outputs_map_;
+ /* A map that associates the output socket that provides the result of an output of the operation
+ * with the identifier of that output. This is needed to help the compiler establish links
+ * between operations. */
+ Map<DOutputSocket, std::string> output_sockets_to_output_identifiers_map_;
+ /* A map that associates the output socket of a node that is not part of the shader operation to
+ * the attribute that was created for it. This is used to share the same attribute with all
+ * inputs that are linked to the same output socket. */
+ Map<DOutputSocket, GPUNodeLink *> output_to_material_attribute_map_;
+
+ public:
+ /* Construct and compile a GPU material from the given shader compile unit by calling
+ * GPU_material_from_callbacks with the appropriate callbacks. */
+ ShaderOperation(Context &context, ShaderCompileUnit &compile_unit);
+
+ /* Free the GPU material. */
+ ~ShaderOperation();
+
+ /* Allocate the output results, bind the shader and all its needed resources, then dispatch the
+ * shader. */
+ void execute() override;
+
+ /* Get the identifier of the operation output corresponding to the given output socket. This is
+ * called by the compiler to identify the operation output that provides the result for an input
+ * by providing the output socket that the input is linked to. See
+ * output_sockets_to_output_identifiers_map_ for more information. */
+ StringRef get_output_identifier_from_output_socket(DOutputSocket output_socket);
+
+ /* Get a reference to the inputs to linked outputs map of the operation. This is called by the
+ * compiler to identify the output that each input of the operation is linked to for correct
+ * input mapping. See inputs_to_linked_outputs_map_ for more information. */
+ Map<std::string, DOutputSocket> &get_inputs_to_linked_outputs_map();
+
+ /* Compute and set the initial reference counts of all the results of the operation. The
+ * reference counts of the results are the number of operations that use those results, which is
+ * computed as the number of inputs whose node is part of the schedule and is linked to the
+ * output corresponding to each of the results of the operation. The node execution schedule is
+ * given as an input. */
+ void compute_results_reference_counts(const Schedule &schedule);
+
+ private:
+ /* Bind the uniform buffer of the GPU material as well as any color band textures needed by the
+ * GPU material. The compiled shader of the material is given as an argument and assumed to be
+ * bound. */
+ void bind_material_resources(GPUShader *shader);
+
+ /* Bind the input results of the operation to the appropriate textures in the GPU material. The
+ * attributes stored in output_to_material_attribute_map_ have names that match the texture
+ * samplers in the shader as well as the identifiers of the operation inputs that they correspond
+ * to. The compiled shader of the material is given as an argument and assumed to be bound. */
+ void bind_inputs(GPUShader *shader);
+
+ /* Bind the output results of the operation to the appropriate images in the GPU material. The
+ * name of the images in the shader match the identifier of their corresponding outputs. The
+ * compiled shader of the material is given as an argument and assumed to be bound. */
+ void bind_outputs(GPUShader *shader);
+
+ /* A static callback method of interface ConstructGPUMaterialFn that is passed to
+ * GPU_material_from_callbacks to construct the GPU material graph. The thunk parameter will be a
+ * pointer to the instance of ShaderOperation that is being compiled. The method goes over the
+ * compile unit and does the following for each node:
+ *
+ * - Instantiate a ShaderNode from the node and add it to shader_nodes_.
+ * - Link the inputs of the node if needed. The inputs are either linked to other nodes in the
+ * GPU material graph or are exposed as inputs to the shader operation itself if they are
+ * linked to nodes that are not part of the shader operation.
+ * - Call the compile method of the shader node to actually add and link the GPU material graph
+ * nodes.
+ * - If any of the outputs of the node are linked to nodes that are not part of the shader
+ * operation, they are exposed as outputs to the shader operation itself. */
+ static void construct_material(void *thunk, GPUMaterial *material);
+
+ /* Link the inputs of the node if needed. Unlinked inputs are ignored as they will be linked by
+ * the node compile method. If the input is linked to a node that is not part of the shader
+ * operation, the input will be exposed as an input to the shader operation and linked to it.
+ * While if the input is linked to a node that is part of the shader operation, then it is linked
+ * to that node in the GPU material node graph. */
+ void link_node_inputs(DNode node, GPUMaterial *material);
+
+ /* Given the input socket of a node that is part of the shader operation which is linked to the
+ * given output socket of a node that is also part of the shader operation, just link the output
+ * link of the GPU node stack of the output socket to the input link of the GPU node stack of the
+ * input socket. This essentially establishes the needed links in the GPU material node graph. */
+ void link_node_input_internal(DInputSocket input_socket, DOutputSocket output_socket);
+
+ /* Given the input socket of a node that is part of the shader operation which is linked to the
+ * given output socket of a node that is not part of the shader operation, declare a new
+ * operation input and link it to the input link of the GPU node stack of the input socket. An
+ * operation input is only declared if no input was already declared for that same output socket
+ * before. */
+ void link_node_input_external(DInputSocket input_socket,
+ DOutputSocket output_socket,
+ GPUMaterial *material);
+
+ /* Given the input socket of a node that is part of the shader operation which is linked to the
+ * given output socket of a node that is not part of the shader operation, declare a new input to
+ * the operation that is represented in the GPU material by a newly created GPU attribute. It is
+ * assumed that no operation input was declared for this same output socket before. In the
+ * generate_code_for_inputs method, a texture will be added in the shader for each of the
+ * declared inputs, having the same name as the attribute. Additionally, code will be emitted to
+ * initialize the attributes by sampling their corresponding textures. */
+ void declare_operation_input(DInputSocket input_socket,
+ DOutputSocket output_socket,
+ GPUMaterial *material);
+
+ /* Populate the output results of the shader operation for output sockets of the given node that
+ * are linked to nodes outside of the shader operation. */
+ void populate_results_for_node(DNode node, GPUMaterial *material);
+
+ /* Given the output socket of a node that is part of the shader operation which is linked to an
+ * input socket of a node that is not part of the shader operation, declare a new output to the
+ * operation and link it to an output storer passing in the index of the output. In the
+ * generate_code_for_outputs method, an image will be added in the shader for each of the
+ * declared outputs. Additionally, code will be emitted to define the storer functions that store
+ * the value in the appropriate image identified by the given index. */
+ void populate_operation_result(DOutputSocket output_socket, GPUMaterial *material);
+
+ /* A static callback method of interface GPUCodegenCallbackFn that is passed to
+ * GPU_material_from_callbacks to create the shader create info of the GPU material. The thunk
+ * parameter will be a pointer to the instance of ShaderOperation that is being compiled.
+ *
+ * This method first generates the necessary code to load the inputs and store the outputs. Then,
+ * it creates a compute shader from the generated sources. Finally, it adds the necessary GPU
+ * resources to the shader. */
+ static void generate_code(void *thunk, GPUMaterial *material, GPUCodegenOutput *code_generator);
+
+ /* Add an image in the shader for each of the declared outputs. Additionally, emit code to define
+ * the storer functions that store the given value in the appropriate image identified by the
+ * given index. */
+ void generate_code_for_outputs(gpu::shader::ShaderCreateInfo &shader_create_info);
+
+ /* Add a texture will in the shader for each of the declared inputs/attributes in the operation,
+ * having the same name as the attribute. Additionally, emit code to initialize the attributes by
+ * sampling their corresponding textures. */
+ void generate_code_for_inputs(GPUMaterial *material,
+ gpu::shader::ShaderCreateInfo &shader_create_info);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_simple_operation.hh b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh
new file mode 100644
index 00000000000..0061986ce42
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_string_ref.hh"
+
+#include "COM_operation.hh"
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Simple Operation
+ *
+ * A simple operation is an operation that takes exactly one input and computes exactly one output.
+ * Moreover, the output is guaranteed to only have a single user, that is, its reference count will
+ * be one. Such operations can be attached to the inputs of operations to pre-process the inputs to
+ * prepare them before the operation is executed. */
+class SimpleOperation : public Operation {
+ private:
+ /* The identifier of the output. This is constant for all operations. */
+ static const StringRef output_identifier_;
+ /* The identifier of the input. This is constant for all operations. */
+ static const StringRef input_identifier_;
+
+ public:
+ using Operation::Operation;
+
+ /* Get a reference to the output result of the operation, this essentially calls the super
+ * get_result method with the output identifier of the operation. */
+ Result &get_result();
+
+ /* Map the input of the operation to the given result, this essentially calls the super
+ * map_input_to_result method with the input identifier of the operation. */
+ void map_input_to_result(Result *result);
+
+ protected:
+ /* Simple operations don't need input processors, so override with an empty implementation. */
+ void add_and_evaluate_input_processors() override;
+
+ /* Get a reference to the input result of the operation, this essentially calls the super
+ * get_result method with the input identifier of the operation. */
+ Result &get_input();
+
+ /* Switch the result mapped to the input with the given result, this essentially calls the super
+ * switch_result_mapped_to_input method with the input identifier of the operation. */
+ void switch_result_mapped_to_input(Result *result);
+
+ /* Populate the result of the operation, this essentially calls the super populate_result method
+ * with the output identifier of the operation and sets the initial reference count of the result
+ * to 1, since the result of an operation is guaranteed to have a single user. */
+ void populate_result(Result result);
+
+ /* Declare the descriptor of the input of the operation to be the given descriptor, this
+ * essentially calls the super declare_input_descriptor method with the input identifier of the
+ * operation. */
+ void declare_input_descriptor(InputDescriptor descriptor);
+
+ /* Get a reference to the descriptor of the input, this essentially calls the super
+ * get_input_descriptor method with the input identifier of the operation. */
+ InputDescriptor &get_input_descriptor();
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh
new file mode 100644
index 00000000000..161a80862a0
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+
+#include "GPU_shader.h"
+
+namespace blender::realtime_compositor {
+
+/* -------------------------------------------------------------------------------------------------
+ * Static Shader Manager
+ *
+ * A static shader manager is a map of shaders identified by their info name that can be acquired
+ * and reused throughout the evaluation of the compositor and are only freed when the shader
+ * manager is destroyed. Once a shader is acquired for the first time, it will be cached in the
+ * manager to be potentially acquired later if needed without the shader creation overhead. */
+class StaticShaderManager {
+ private:
+ /* The set of shaders identified by their info name that are currently available in the manager
+ * to be acquired. */
+ Map<StringRef, GPUShader *> shaders_;
+
+ public:
+ ~StaticShaderManager();
+
+ /* Check if there is an available shader with the given info name in the manager, if such shader
+ * exists, return it, otherwise, return a newly created shader and add it to the manager. */
+ GPUShader *get(const char *info_name);
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_texture_pool.hh b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh
new file mode 100644
index 00000000000..cc6641d288f
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include <cstdint>
+
+#include "BLI_map.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_vector.hh"
+
+#include "GPU_texture.h"
+
+namespace blender::realtime_compositor {
+
+/* ------------------------------------------------------------------------------------------------
+ * Texture Pool Key
+ *
+ * A key used to identify a texture specification in a texture pool. Defines a hash and an equality
+ * operator for use in a hash map. */
+class TexturePoolKey {
+ public:
+ int2 size;
+ eGPUTextureFormat format;
+
+ /* Construct a key from the given texture size and format. */
+ TexturePoolKey(int2 size, eGPUTextureFormat format);
+
+ /* Construct a key from the size and format of the given texture. */
+ TexturePoolKey(const GPUTexture *texture);
+
+ uint64_t hash() const;
+};
+
+bool operator==(const TexturePoolKey &a, const TexturePoolKey &b);
+
+/* ------------------------------------------------------------------------------------------------
+ * Texture Pool
+ *
+ * A texture pool allows the allocation and reuse of textures throughout the execution of the
+ * compositor to avoid memory fragmentation and texture allocation overheads. The texture pool
+ * delegates the actual texture allocation to an allocate_texture method that should be implemented
+ * by the caller of the compositor evaluator, allowing a more agnostic and flexible execution that
+ * can be controlled by the caller. If the compositor is expected to execute frequently, like on
+ * every redraw, then the allocation method should use a persistent texture pool to allow
+ * cross-evaluation texture pooling, for instance, by using the DRWTexturePool. But if the
+ * evaluator is expected to execute infrequently, the allocated textures can just be freed when the
+ * evaluator is done, that is, when the pool is destructed. */
+class TexturePool {
+ private:
+ /* The set of textures in the pool that are available to acquire for each distinct texture
+ * specification. */
+ Map<TexturePoolKey, Vector<GPUTexture *>> textures_;
+
+ public:
+ /* Check if there is an available texture with the given specification in the pool, if such
+ * texture exists, return it, otherwise, return a newly allocated texture. Expect the texture to
+ * be uncleared and possibly contains garbage data. */
+ GPUTexture *acquire(int2 size, eGPUTextureFormat format);
+
+ /* Shorthand for acquire with GPU_RGBA16F format. */
+ GPUTexture *acquire_color(int2 size);
+
+ /* Shorthand for acquire with GPU_RGBA16F format. Identical to acquire_color because vectors
+ * are stored in RGBA textures, due to the limited support for RGB textures. */
+ GPUTexture *acquire_vector(int2 size);
+
+ /* Shorthand for acquire with GPU_R16F format. */
+ GPUTexture *acquire_float(int2 size);
+
+ /* Put the texture back into the pool, potentially to be acquired later by another user. Expects
+ * the texture to be one that was acquired using the same texture pool. */
+ void release(GPUTexture *texture);
+
+ /* Reset the texture pool by clearing all available textures without freeing the textures. If the
+ * textures will no longer be needed, they should be freed in the destructor. This should be
+ * called after the compositor is done evaluating. */
+ void reset();
+
+ private:
+ /* Returns a newly allocated texture with the given specification. This method should be
+ * implemented by the caller of the compositor evaluator. See the class description for more
+ * information. */
+ virtual GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) = 0;
+};
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/COM_utilities.hh b/source/blender/compositor/realtime_compositor/COM_utilities.hh
new file mode 100644
index 00000000000..efd1bc2b6b0
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/COM_utilities.hh
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_function_ref.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "GPU_shader.h"
+
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/**
+ * Get the origin socket of the given node input. If the input is not linked, the socket itself is
+ * returned. If the input is linked, the socket that is linked to it is returned, which could
+ * either be an input or an output. An input socket is returned when the given input is connected
+ * to an unlinked input of a group input node.
+ */
+DSocket get_input_origin_socket(DInputSocket input);
+
+/**
+ * Get the output socket linked to the given node input. If the input is not linked to an output,
+ * a null output is returned.
+ */
+DOutputSocket get_output_linked_to_input(DInputSocket input);
+
+/** Get the result type that corresponds to the type of the given socket. */
+ResultType get_node_socket_result_type(const bNodeSocket *socket);
+
+/**
+ * Returns true if any of the nodes linked to the given output satisfies the given condition,
+ * and false otherwise.
+ */
+bool is_output_linked_to_node_conditioned(DOutputSocket output,
+ FunctionRef<bool(DNode)> condition);
+
+/** Returns the number of inputs linked to the given output that satisfy the given condition. */
+int number_of_inputs_linked_to_output_conditioned(DOutputSocket output,
+ FunctionRef<bool(DInputSocket)> condition);
+
+/** A node is a shader node if it defines a method to get a shader node operation. */
+bool is_shader_node(DNode node);
+
+/**
+ * Returns true if the given node is supported, that is, have an implementation.
+ * Returns false otherwise.
+ */
+bool is_node_supported(DNode node);
+
+/** Get the input descriptor of the given input socket. */
+InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket);
+
+/**
+ * Dispatch the given compute shader in a 2D compute space such that the number of threads in both
+ * dimensions is as small as possible but at least covers the entirety of threads_range assuming
+ * the shader has a local group size given by local_size. That means that the number of threads
+ * might be a bit larger than threads_range, so shaders has to put that into consideration. A
+ * default local size of 16x16 is assumed, which is the optimal local size for many image
+ * processing shaders.
+ */
+void compute_dispatch_threads_at_least(GPUShader *shader,
+ int2 threads_range,
+ int2 local_size = int2(16));
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/compile_state.cc b/source/blender/compositor/realtime_compositor/intern/compile_state.cc
new file mode 100644
index 00000000000..5fa2fc9d544
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/compile_state.cc
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <limits>
+
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_compile_state.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_node_operation.hh"
+#include "COM_result.hh"
+#include "COM_scheduler.hh"
+#include "COM_shader_operation.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+CompileState::CompileState(const Schedule &schedule) : schedule_(schedule)
+{
+}
+
+const Schedule &CompileState::get_schedule()
+{
+ return schedule_;
+}
+
+void CompileState::map_node_to_node_operation(DNode node, NodeOperation *operations)
+{
+ return node_operations_.add_new(node, operations);
+}
+
+void CompileState::map_node_to_shader_operation(DNode node, ShaderOperation *operations)
+{
+ return shader_operations_.add_new(node, operations);
+}
+
+Result &CompileState::get_result_from_output_socket(DOutputSocket output)
+{
+ /* The output belongs to a node that was compiled into a standard node operation, so return a
+ * reference to the result from that operation using the output identifier. */
+ if (node_operations_.contains(output.node())) {
+ NodeOperation *operation = node_operations_.lookup(output.node());
+ return operation->get_result(output->identifier);
+ }
+
+ /* Otherwise, the output belongs to a node that was compiled into a shader operation, so
+ * retrieve the internal identifier of that output and return a reference to the result from
+ * that operation using the retrieved identifier. */
+ ShaderOperation *operation = shader_operations_.lookup(output.node());
+ return operation->get_result(operation->get_output_identifier_from_output_socket(output));
+}
+
+void CompileState::add_node_to_shader_compile_unit(DNode node)
+{
+ shader_compile_unit_.add_new(node);
+
+ /* If the domain of the shader compile unit is not yet determined or was determined to be
+ * an identity domain, update it to be the computed domain of the node. */
+ if (shader_compile_unit_domain_ == Domain::identity()) {
+ shader_compile_unit_domain_ = compute_shader_node_domain(node);
+ }
+}
+
+ShaderCompileUnit &CompileState::get_shader_compile_unit()
+{
+ return shader_compile_unit_;
+}
+
+void CompileState::reset_shader_compile_unit()
+{
+ return shader_compile_unit_.clear();
+}
+
+bool CompileState::should_compile_shader_compile_unit(DNode node)
+{
+ /* If the shader compile unit is empty, then it can't be compiled yet. */
+ if (shader_compile_unit_.is_empty()) {
+ return false;
+ }
+
+ /* If the node is not a shader node, then it can't be added to the shader compile unit and the
+ * shader compile unit is considered complete and should be compiled. */
+ if (!is_shader_node(node)) {
+ return true;
+ }
+
+ /* If the computed domain of the node doesn't matches the domain of the shader compile unit, then
+ * it can't be added to the shader compile unit and the shader compile unit is considered
+ * complete and should be compiled. Identity domains are an exception as they are always
+ * compatible because they represents single values. */
+ if (shader_compile_unit_domain_ != Domain::identity() &&
+ shader_compile_unit_domain_ != compute_shader_node_domain(node)) {
+ return true;
+ }
+
+ /* Otherwise, the node is compatible and can be added to the compile unit and it shouldn't be
+ * compiled just yet. */
+ return false;
+}
+
+Domain CompileState::compute_shader_node_domain(DNode node)
+{
+ /* Default to an identity domain in case no domain input was found, most likely because all
+ * inputs are single values. */
+ Domain node_domain = Domain::identity();
+ int current_domain_priority = std::numeric_limits<int>::max();
+
+ /* Go over the inputs and find the domain of the non single value input with the highest domain
+ * priority. */
+ for (const bNodeSocket *input : node->input_sockets()) {
+ const DInputSocket dinput{node.context(), input};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked, so skip
+ * it. */
+ const DOutputSocket output = get_output_linked_to_input(dinput);
+ if (!output) {
+ continue;
+ }
+
+ const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input);
+
+ /* If the output belongs to a node that is part of the shader compile unit, then the domain of
+ * the input is the domain of the compile unit itself. */
+ if (shader_compile_unit_.contains(output.node())) {
+ /* Single value inputs can't be domain inputs. */
+ if (shader_compile_unit_domain_.size == int2(1)) {
+ continue;
+ }
+
+ /* Notice that the lower the domain priority value is, the higher the priority is, hence the
+ * less than comparison. */
+ if (input_descriptor.domain_priority < current_domain_priority) {
+ node_domain = shader_compile_unit_domain_;
+ current_domain_priority = input_descriptor.domain_priority;
+ }
+ continue;
+ }
+
+ const Result &result = get_result_from_output_socket(output);
+
+ /* A single value input can't be a domain input. */
+ if (result.is_single_value() || input_descriptor.expects_single_value) {
+ continue;
+ }
+
+ /* An input that skips realization can't be a domain input. */
+ if (input_descriptor.skip_realization) {
+ continue;
+ }
+
+ /* Notice that the lower the domain priority value is, the higher the priority is, hence the
+ * less than comparison. */
+ if (input_descriptor.domain_priority < current_domain_priority) {
+ node_domain = result.domain();
+ current_domain_priority = input_descriptor.domain_priority;
+ }
+ }
+
+ return node_domain;
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/context.cc b/source/blender/compositor/realtime_compositor/intern/context.cc
new file mode 100644
index 00000000000..64ac29af3d1
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/context.cc
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "COM_context.hh"
+#include "COM_static_shader_manager.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+Context::Context(TexturePool &texture_pool) : texture_pool_(texture_pool)
+{
+}
+
+int Context::get_frame_number() const
+{
+ return get_scene()->r.cfra;
+}
+
+float Context::get_time() const
+{
+ const float frame_number = static_cast<float>(get_frame_number());
+ const float frame_rate = static_cast<float>(get_scene()->r.frs_sec) /
+ static_cast<float>(get_scene()->r.frs_sec_base);
+ return frame_number / frame_rate;
+}
+
+TexturePool &Context::texture_pool()
+{
+ return texture_pool_;
+}
+
+StaticShaderManager &Context::shader_manager()
+{
+ return shader_manager_;
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc
new file mode 100644
index 00000000000..3743b9bba87
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc
@@ -0,0 +1,239 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_math_vec_types.hh"
+
+#include "GPU_shader.h"
+
+#include "COM_context.hh"
+#include "COM_conversion_operation.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_result.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+/* -------------------------------------------------------------------- */
+/** \name Conversion Operation
+ * \{ */
+
+void ConversionOperation::execute()
+{
+ Result &result = get_result();
+ const Result &input = get_input();
+
+ if (input.is_single_value()) {
+ result.allocate_single_value();
+ execute_single(input, result);
+ return;
+ }
+
+ result.allocate_texture(input.domain());
+
+ GPUShader *shader = get_conversion_shader();
+ GPU_shader_bind(shader);
+
+ input.bind_as_texture(shader, "input_tx");
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, input.domain().size);
+
+ input.unbind_as_texture();
+ result.unbind_as_image();
+ GPU_shader_unbind();
+}
+
+SimpleOperation *ConversionOperation::construct_if_needed(Context &context,
+ const Result &input_result,
+ const InputDescriptor &input_descriptor)
+{
+ ResultType result_type = input_result.type();
+ ResultType expected_type = input_descriptor.type;
+
+ /* If the result type differs from the expected type, return an instance of an appropriate
+ * conversion operation. Otherwise, return a null pointer. */
+
+ if (result_type == ResultType::Float && expected_type == ResultType::Vector) {
+ return new ConvertFloatToVectorOperation(context);
+ }
+
+ if (result_type == ResultType::Float && expected_type == ResultType::Color) {
+ return new ConvertFloatToColorOperation(context);
+ }
+
+ if (result_type == ResultType::Color && expected_type == ResultType::Float) {
+ return new ConvertColorToFloatOperation(context);
+ }
+
+ if (result_type == ResultType::Color && expected_type == ResultType::Vector) {
+ return new ConvertColorToVectorOperation(context);
+ }
+
+ if (result_type == ResultType::Vector && expected_type == ResultType::Float) {
+ return new ConvertVectorToFloatOperation(context);
+ }
+
+ if (result_type == ResultType::Vector && expected_type == ResultType::Color) {
+ return new ConvertVectorToColorOperation(context);
+ }
+
+ return nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Float to Vector Operation
+ * \{ */
+
+ConvertFloatToVectorOperation::ConvertFloatToVectorOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Float;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Vector, texture_pool()));
+}
+
+void ConvertFloatToVectorOperation::execute_single(const Result &input, Result &output)
+{
+ output.set_vector_value(float3(input.get_float_value()));
+}
+
+GPUShader *ConvertFloatToVectorOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_float_to_vector");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Float to Color Operation
+ * \{ */
+
+ConvertFloatToColorOperation::ConvertFloatToColorOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Float;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Color, texture_pool()));
+}
+
+void ConvertFloatToColorOperation::execute_single(const Result &input, Result &output)
+{
+ float4 color = float4(input.get_float_value());
+ color[3] = 1.0f;
+ output.set_color_value(color);
+}
+
+GPUShader *ConvertFloatToColorOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_float_to_color");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Color to Float Operation
+ * \{ */
+
+ConvertColorToFloatOperation::ConvertColorToFloatOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Color;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Float, texture_pool()));
+}
+
+void ConvertColorToFloatOperation::execute_single(const Result &input, Result &output)
+{
+ float4 color = input.get_color_value();
+ output.set_float_value((color[0] + color[1] + color[2]) / 3.0f);
+}
+
+GPUShader *ConvertColorToFloatOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_color_to_float");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Color to Vector Operation
+ * \{ */
+
+ConvertColorToVectorOperation::ConvertColorToVectorOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Color;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Vector, texture_pool()));
+}
+
+void ConvertColorToVectorOperation::execute_single(const Result &input, Result &output)
+{
+ float4 color = input.get_color_value();
+ output.set_vector_value(float3(color));
+}
+
+GPUShader *ConvertColorToVectorOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_color_to_vector");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Vector to Float Operation
+ * \{ */
+
+ConvertVectorToFloatOperation::ConvertVectorToFloatOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Vector;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Float, texture_pool()));
+}
+
+void ConvertVectorToFloatOperation::execute_single(const Result &input, Result &output)
+{
+ float3 vector = input.get_vector_value();
+ output.set_float_value((vector[0] + vector[1] + vector[2]) / 3.0f);
+}
+
+GPUShader *ConvertVectorToFloatOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_vector_to_float");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert Vector to Color Operation
+ * \{ */
+
+ConvertVectorToColorOperation::ConvertVectorToColorOperation(Context &context)
+ : ConversionOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = ResultType::Vector;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(ResultType::Color, texture_pool()));
+}
+
+void ConvertVectorToColorOperation::execute_single(const Result &input, Result &output)
+{
+ output.set_color_value(float4(input.get_vector_value(), 1.0f));
+}
+
+GPUShader *ConvertVectorToColorOperation::get_conversion_shader() const
+{
+ return shader_manager().get("compositor_convert_vector_to_color");
+}
+
+/** \} */
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/domain.cc b/source/blender/compositor/realtime_compositor/intern/domain.cc
new file mode 100644
index 00000000000..31b297c212e
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/domain.cc
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "COM_domain.hh"
+
+namespace blender::realtime_compositor {
+
+Domain::Domain(int2 size) : size(size), transformation(float3x3::identity())
+{
+}
+
+Domain::Domain(int2 size, float3x3 transformation) : size(size), transformation(transformation)
+{
+}
+
+void Domain::transform(const float3x3 &input_transformation)
+{
+ transformation = input_transformation * transformation;
+}
+
+Domain Domain::identity()
+{
+ return Domain(int2(1), float3x3::identity());
+}
+
+bool operator==(const Domain &a, const Domain &b)
+{
+ return a.size == b.size && a.transformation == b.transformation;
+}
+
+bool operator!=(const Domain &a, const Domain &b)
+{
+ return !(a == b);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/evaluator.cc b/source/blender/compositor/realtime_compositor/intern/evaluator.cc
new file mode 100644
index 00000000000..48457bec199
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/evaluator.cc
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <string>
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "COM_compile_state.hh"
+#include "COM_context.hh"
+#include "COM_evaluator.hh"
+#include "COM_input_single_value_operation.hh"
+#include "COM_node_operation.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_scheduler.hh"
+#include "COM_shader_operation.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+Evaluator::Evaluator(Context &context, bNodeTree &node_tree)
+ : context_(context), node_tree_(node_tree)
+{
+}
+
+void Evaluator::evaluate()
+{
+ context_.texture_pool().reset();
+
+ if (!is_compiled_) {
+ compile_and_evaluate();
+ is_compiled_ = true;
+ return;
+ }
+
+ for (const std::unique_ptr<Operation> &operation : operations_stream_) {
+ operation->evaluate();
+ }
+}
+
+void Evaluator::reset()
+{
+ operations_stream_.clear();
+ derived_node_tree_.reset();
+
+ is_compiled_ = false;
+}
+
+bool Evaluator::validate_node_tree()
+{
+ if (derived_node_tree_->has_link_cycles()) {
+ context_.set_info_message("Compositor node tree has cyclic links!");
+ return false;
+ }
+
+ if (derived_node_tree_->has_undefined_nodes_or_sockets()) {
+ context_.set_info_message("Compositor node tree has undefined nodes or sockets!");
+ return false;
+ }
+
+ return true;
+}
+
+void Evaluator::compile_and_evaluate()
+{
+ derived_node_tree_ = std::make_unique<DerivedNodeTree>(node_tree_);
+
+ if (!validate_node_tree()) {
+ return;
+ }
+
+ const Schedule schedule = compute_schedule(*derived_node_tree_);
+
+ CompileState compile_state(schedule);
+
+ for (const DNode &node : schedule) {
+ if (compile_state.should_compile_shader_compile_unit(node)) {
+ compile_and_evaluate_shader_compile_unit(compile_state);
+ }
+
+ if (is_shader_node(node)) {
+ compile_state.add_node_to_shader_compile_unit(node);
+ }
+ else {
+ compile_and_evaluate_node(node, compile_state);
+ }
+ }
+}
+
+void Evaluator::compile_and_evaluate_node(DNode node, CompileState &compile_state)
+{
+ NodeOperation *operation = node->typeinfo->get_compositor_operation(context_, node);
+
+ compile_state.map_node_to_node_operation(node, operation);
+
+ map_node_operation_inputs_to_their_results(node, operation, compile_state);
+
+ /* This has to be done after input mapping because the method may add Input Single Value
+ * Operations to the operations stream, which needs to be evaluated before the operation itself
+ * is evaluated. */
+ operations_stream_.append(std::unique_ptr<Operation>(operation));
+
+ operation->compute_results_reference_counts(compile_state.get_schedule());
+
+ operation->evaluate();
+}
+
+void Evaluator::map_node_operation_inputs_to_their_results(DNode node,
+ NodeOperation *operation,
+ CompileState &compile_state)
+{
+ for (const bNodeSocket *input : node->input_sockets()) {
+ const DInputSocket dinput{node.context(), input};
+
+ DSocket dorigin = get_input_origin_socket(dinput);
+
+ /* The origin socket is an output, which means the input is linked. So map the input to the
+ * result we get from the output. */
+ if (dorigin->is_output()) {
+ Result &result = compile_state.get_result_from_output_socket(DOutputSocket(dorigin));
+ operation->map_input_to_result(input->identifier, &result);
+ continue;
+ }
+
+ /* Otherwise, the origin socket is an input, which either means the input is unlinked and the
+ * origin is the input socket itself or the input is connected to an unlinked input of a group
+ * input node and the origin is the input of the group input node. So map the input to the
+ * result of a newly created Input Single Value Operation. */
+ auto *input_operation = new InputSingleValueOperation(context_, DInputSocket(dorigin));
+ operation->map_input_to_result(input->identifier, &input_operation->get_result());
+
+ operations_stream_.append(std::unique_ptr<InputSingleValueOperation>(input_operation));
+
+ input_operation->evaluate();
+ }
+}
+
+void Evaluator::compile_and_evaluate_shader_compile_unit(CompileState &compile_state)
+{
+ ShaderCompileUnit &compile_unit = compile_state.get_shader_compile_unit();
+ ShaderOperation *operation = new ShaderOperation(context_, compile_unit);
+
+ for (DNode node : compile_unit) {
+ compile_state.map_node_to_shader_operation(node, operation);
+ }
+
+ map_shader_operation_inputs_to_their_results(operation, compile_state);
+
+ operations_stream_.append(std::unique_ptr<Operation>(operation));
+
+ operation->compute_results_reference_counts(compile_state.get_schedule());
+
+ operation->evaluate();
+
+ compile_state.reset_shader_compile_unit();
+}
+
+void Evaluator::map_shader_operation_inputs_to_their_results(ShaderOperation *operation,
+ CompileState &compile_state)
+{
+ for (const auto &item : operation->get_inputs_to_linked_outputs_map().items()) {
+ Result &result = compile_state.get_result_from_output_socket(item.value);
+ operation->map_input_to_result(item.key, &result);
+ }
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc
new file mode 100644
index 00000000000..b3cc86b5f79
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_math_vec_types.hh"
+
+#include "COM_input_single_value_operation.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+const StringRef InputSingleValueOperation::output_identifier_ = StringRef("Output");
+
+InputSingleValueOperation::InputSingleValueOperation(Context &context, DInputSocket input_socket)
+ : Operation(context), input_socket_(input_socket)
+{
+ const ResultType result_type = get_node_socket_result_type(input_socket_.bsocket());
+ Result result = Result(result_type, texture_pool());
+
+ /* The result of an input single value operation is guaranteed to have a single user. */
+ result.set_initial_reference_count(1);
+
+ populate_result(result);
+}
+
+void InputSingleValueOperation::execute()
+{
+ /* Allocate a single value for the result. */
+ Result &result = get_result();
+ result.allocate_single_value();
+
+ const bNodeSocket *bsocket = input_socket_.bsocket();
+
+ /* Set the value of the result to the default value of the input socket. */
+ switch (result.type()) {
+ case ResultType::Float:
+ result.set_float_value(bsocket->default_value_typed<bNodeSocketValueFloat>()->value);
+ break;
+ case ResultType::Vector:
+ result.set_vector_value(
+ float3(bsocket->default_value_typed<bNodeSocketValueVector>()->value));
+ break;
+ case ResultType::Color:
+ result.set_color_value(float4(bsocket->default_value_typed<bNodeSocketValueRGBA>()->value));
+ break;
+ }
+}
+
+Result &InputSingleValueOperation::get_result()
+{
+ return Operation::get_result(output_identifier_);
+}
+
+void InputSingleValueOperation::populate_result(Result result)
+{
+ Operation::populate_result(output_identifier_, result);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/node_operation.cc b/source/blender/compositor/realtime_compositor/intern/node_operation.cc
new file mode 100644
index 00000000000..1c20c967ddb
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/node_operation.cc
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <memory>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_declaration.hh"
+
+#include "COM_context.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_node_operation.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_scheduler.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+NodeOperation::NodeOperation(Context &context, DNode node) : Operation(context), node_(node)
+{
+ for (const bNodeSocket *output : node->output_sockets()) {
+ const ResultType result_type = get_node_socket_result_type(output);
+ const Result result = Result(result_type, texture_pool());
+ populate_result(output->identifier, result);
+ }
+
+ for (const bNodeSocket *input : node->input_sockets()) {
+ const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input);
+ declare_input_descriptor(input->identifier, input_descriptor);
+ }
+}
+
+void NodeOperation::compute_results_reference_counts(const Schedule &schedule)
+{
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ const DOutputSocket doutput{node().context(), output};
+
+ const int reference_count = number_of_inputs_linked_to_output_conditioned(
+ doutput, [&](DInputSocket input) { return schedule.contains(input.node()); });
+
+ get_result(doutput->identifier).set_initial_reference_count(reference_count);
+ }
+}
+
+const DNode &NodeOperation::node() const
+{
+ return node_;
+}
+
+const bNode &NodeOperation::bnode() const
+{
+ return *node_;
+}
+
+bool NodeOperation::should_compute_output(StringRef identifier)
+{
+ return get_result(identifier).should_compute();
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/operation.cc b/source/blender/compositor/realtime_compositor/intern/operation.cc
new file mode 100644
index 00000000000..832196cc5ef
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/operation.cc
@@ -0,0 +1,206 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <limits>
+#include <memory>
+
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+
+#include "COM_context.hh"
+#include "COM_conversion_operation.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_operation.hh"
+#include "COM_realize_on_domain_operation.hh"
+#include "COM_reduce_to_single_value_operation.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+#include "COM_static_shader_manager.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+Operation::Operation(Context &context) : context_(context)
+{
+}
+
+Operation::~Operation() = default;
+
+void Operation::evaluate()
+{
+ evaluate_input_processors();
+
+ reset_results();
+
+ execute();
+
+ release_inputs();
+}
+
+Result &Operation::get_result(StringRef identifier)
+{
+ return results_.lookup(identifier);
+}
+
+void Operation::map_input_to_result(StringRef identifier, Result *result)
+{
+ results_mapped_to_inputs_.add_new(identifier, result);
+}
+
+Domain Operation::compute_domain()
+{
+ /* Default to an identity domain in case no domain input was found, most likely because all
+ * inputs are single values. */
+ Domain operation_domain = Domain::identity();
+ int current_domain_priority = std::numeric_limits<int>::max();
+
+ /* Go over the inputs and find the domain of the non single value input with the highest domain
+ * priority. */
+ for (StringRef identifier : input_descriptors_.keys()) {
+ const Result &result = get_input(identifier);
+ const InputDescriptor &descriptor = get_input_descriptor(identifier);
+
+ /* A single value input can't be a domain input. */
+ if (result.is_single_value() || descriptor.expects_single_value) {
+ continue;
+ }
+
+ /* An input that skips realization can't be a domain input. */
+ if (descriptor.skip_realization) {
+ continue;
+ }
+
+ /* Notice that the lower the domain priority value is, the higher the priority is, hence the
+ * less than comparison. */
+ if (descriptor.domain_priority < current_domain_priority) {
+ operation_domain = result.domain();
+ current_domain_priority = descriptor.domain_priority;
+ }
+ }
+
+ return operation_domain;
+}
+
+void Operation::add_and_evaluate_input_processors()
+{
+ /* Each input processor type is added to all inputs entirely before the next type. This is done
+ * because the construction of the input processors may depend on the result of previous input
+ * processors for all inputs. For instance, the realize on domain input processor considers the
+ * value of all inputs, so previous input processors for all inputs needs to be added and
+ * evaluated first. */
+
+ for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
+ SimpleOperation *single_value = ReduceToSingleValueOperation::construct_if_needed(
+ context(), get_input(identifier));
+ add_and_evaluate_input_processor(identifier, single_value);
+ }
+
+ for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
+ SimpleOperation *conversion = ConversionOperation::construct_if_needed(
+ context(), get_input(identifier), get_input_descriptor(identifier));
+ add_and_evaluate_input_processor(identifier, conversion);
+ }
+
+ for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
+ SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed(
+ context(), get_input(identifier), get_input_descriptor(identifier), compute_domain());
+ add_and_evaluate_input_processor(identifier, realize_on_domain);
+ }
+}
+
+void Operation::add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor)
+{
+ /* Allow null inputs to facilitate construct_if_needed pattern of addition. For instance, see the
+ * implementation of the add_and_evaluate_input_processors method. */
+ if (!processor) {
+ return;
+ }
+
+ ProcessorsVector &processors = input_processors_.lookup_or_add_default(identifier);
+
+ /* Get the result that should serve as the input for the processor. This is either the result
+ * mapped to the input or the result of the last processor depending on whether this is the first
+ * processor or not. */
+ Result &result = processors.is_empty() ? get_input(identifier) : processors.last()->get_result();
+
+ /* Map the input result of the processor and add it to the processors vector. */
+ processor->map_input_to_result(&result);
+ processors.append(std::unique_ptr<SimpleOperation>(processor));
+
+ /* Switch the result mapped to the input to be the output result of the processor. */
+ switch_result_mapped_to_input(identifier, &processor->get_result());
+
+ processor->evaluate();
+}
+
+Result &Operation::get_input(StringRef identifier) const
+{
+ return *results_mapped_to_inputs_.lookup(identifier);
+}
+
+void Operation::switch_result_mapped_to_input(StringRef identifier, Result *result)
+{
+ results_mapped_to_inputs_.lookup(identifier) = result;
+}
+
+void Operation::populate_result(StringRef identifier, Result result)
+{
+ results_.add_new(identifier, result);
+}
+
+void Operation::declare_input_descriptor(StringRef identifier, InputDescriptor descriptor)
+{
+ input_descriptors_.add_new(identifier, descriptor);
+}
+
+InputDescriptor &Operation::get_input_descriptor(StringRef identifier)
+{
+ return input_descriptors_.lookup(identifier);
+}
+
+Context &Operation::context()
+{
+ return context_;
+}
+
+TexturePool &Operation::texture_pool() const
+{
+ return context_.texture_pool();
+}
+
+StaticShaderManager &Operation::shader_manager() const
+{
+ return context_.shader_manager();
+}
+
+void Operation::evaluate_input_processors()
+{
+ if (!input_processors_added_) {
+ add_and_evaluate_input_processors();
+ input_processors_added_ = true;
+ return;
+ }
+
+ for (const ProcessorsVector &processors : input_processors_.values()) {
+ for (const std::unique_ptr<SimpleOperation> &processor : processors) {
+ processor->evaluate();
+ }
+ }
+}
+
+void Operation::reset_results()
+{
+ for (Result &result : results_.values()) {
+ result.reset();
+ }
+}
+
+void Operation::release_inputs()
+{
+ for (Result *result : results_mapped_to_inputs_.values()) {
+ result->release();
+ }
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc
new file mode 100644
index 00000000000..817293c0fa6
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_utildefines.h"
+
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_context.hh"
+#include "COM_domain.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_realize_on_domain_operation.hh"
+#include "COM_result.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context,
+ Domain domain,
+ ResultType type)
+ : SimpleOperation(context), domain_(domain)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = type;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(type, texture_pool()));
+}
+
+void RealizeOnDomainOperation::execute()
+{
+ Result &input = get_input();
+ Result &result = get_result();
+
+ result.allocate_texture(domain_);
+
+ GPUShader *shader = get_realization_shader();
+ GPU_shader_bind(shader);
+
+ /* Transform the input space into the domain space. */
+ const float3x3 local_transformation = input.domain().transformation *
+ domain_.transformation.inverted();
+
+ /* Set the origin of the transformation to be the center of the domain. */
+ const float3x3 transformation = float3x3::from_origin_transformation(
+ local_transformation, float2(domain_.size) / 2.0f);
+
+ /* Invert the transformation because the shader transforms the domain coordinates instead of the
+ * input image itself and thus expect the inverse. */
+ const float3x3 inverse_transformation = transformation.inverted();
+
+ GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr());
+
+ /* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
+ * cases, as the logic used by the bicubic realization shader expects textures to use bilinear
+ * interpolation. */
+ const bool use_bilinear = ELEM(input.get_realization_options().interpolation,
+ Interpolation::Bilinear,
+ Interpolation::Bicubic);
+ GPU_texture_filter_mode(input.texture(), use_bilinear);
+
+ /* Make out-of-bound texture access return zero by clamping to border color. And make texture
+ * wrap appropriately if the input repeats. */
+ const bool repeats = input.get_realization_options().repeat_x ||
+ input.get_realization_options().repeat_y;
+ GPU_texture_wrap_mode(input.texture(), repeats, false);
+
+ input.bind_as_texture(shader, "input_tx");
+ result.bind_as_image(shader, "domain_img");
+
+ compute_dispatch_threads_at_least(shader, domain_.size);
+
+ input.unbind_as_texture();
+ result.unbind_as_image();
+ GPU_shader_unbind();
+}
+
+GPUShader *RealizeOnDomainOperation::get_realization_shader()
+{
+ switch (get_result().type()) {
+ case ResultType::Color:
+ return shader_manager().get("compositor_realize_on_domain_color");
+ case ResultType::Vector:
+ return shader_manager().get("compositor_realize_on_domain_vector");
+ case ResultType::Float:
+ return shader_manager().get("compositor_realize_on_domain_float");
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+Domain RealizeOnDomainOperation::compute_domain()
+{
+ return domain_;
+}
+
+SimpleOperation *RealizeOnDomainOperation::construct_if_needed(
+ Context &context,
+ const Result &input_result,
+ const InputDescriptor &input_descriptor,
+ const Domain &operation_domain)
+{
+ /* This input wants to skip realization, the operation is not needed. */
+ if (input_descriptor.skip_realization) {
+ return nullptr;
+ }
+
+ /* The input expects a single value and if no single value is provided, it will be ignored and a
+ * default value will be used, so no need to realize it and the operation is not needed. */
+ if (input_descriptor.expects_single_value) {
+ return nullptr;
+ }
+
+ /* Input result is a single value and does not need realization, the operation is not needed. */
+ if (input_result.is_single_value()) {
+ return nullptr;
+ }
+
+ /* The input have an identical domain to the operation domain, so no need to realize it and the
+ * operation is not needed. */
+ if (input_result.domain() == operation_domain) {
+ return nullptr;
+ }
+
+ /* Otherwise, realization is needed. */
+ return new RealizeOnDomainOperation(context, operation_domain, input_descriptor.type);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc
new file mode 100644
index 00000000000..acc9b4ab7d6
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "COM_context.hh"
+#include "COM_input_descriptor.hh"
+#include "COM_reduce_to_single_value_operation.hh"
+#include "COM_result.hh"
+
+namespace blender::realtime_compositor {
+
+ReduceToSingleValueOperation::ReduceToSingleValueOperation(Context &context, ResultType type)
+ : SimpleOperation(context)
+{
+ InputDescriptor input_descriptor;
+ input_descriptor.type = type;
+ declare_input_descriptor(input_descriptor);
+ populate_result(Result(type, texture_pool()));
+}
+
+void ReduceToSingleValueOperation::execute()
+{
+ /* Make sure any prior writes to the texture are reflected before downloading it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ const Result &input = get_input();
+ float *pixel = static_cast<float *>(GPU_texture_read(input.texture(), GPU_DATA_FLOAT, 0));
+
+ Result &result = get_result();
+ result.allocate_single_value();
+ switch (result.type()) {
+ case ResultType::Color:
+ result.set_color_value(pixel);
+ break;
+ case ResultType::Vector:
+ result.set_vector_value(pixel);
+ break;
+ case ResultType::Float:
+ result.set_float_value(*pixel);
+ break;
+ }
+
+ MEM_freeN(pixel);
+}
+
+SimpleOperation *ReduceToSingleValueOperation::construct_if_needed(Context &context,
+ const Result &input_result)
+{
+ /* Input result is already a single value, the operation is not needed. */
+ if (input_result.is_single_value()) {
+ return nullptr;
+ }
+
+ /* The input is a full sized texture and can't be reduced to a single value, the operation is not
+ * needed. */
+ if (input_result.domain().size != int2(1)) {
+ return nullptr;
+ }
+
+ /* The input is a texture of a single pixel and can be reduced to a single value. */
+ return new ReduceToSingleValueOperation(context, input_result.type());
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/result.cc b/source/blender/compositor/realtime_compositor/intern/result.cc
new file mode 100644
index 00000000000..8059367d211
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/result.cc
@@ -0,0 +1,257 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_domain.hh"
+#include "COM_result.hh"
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+Result::Result(ResultType type, TexturePool &texture_pool)
+ : type_(type), texture_pool_(&texture_pool)
+{
+}
+
+void Result::allocate_texture(Domain domain)
+{
+ is_single_value_ = false;
+ switch (type_) {
+ case ResultType::Float:
+ texture_ = texture_pool_->acquire_float(domain.size);
+ break;
+ case ResultType::Vector:
+ texture_ = texture_pool_->acquire_vector(domain.size);
+ break;
+ case ResultType::Color:
+ texture_ = texture_pool_->acquire_color(domain.size);
+ break;
+ }
+ domain_ = domain;
+}
+
+void Result::allocate_single_value()
+{
+ is_single_value_ = true;
+ /* Single values are stored in 1x1 textures as well as the single value members. */
+ const int2 texture_size{1, 1};
+ switch (type_) {
+ case ResultType::Float:
+ texture_ = texture_pool_->acquire_float(texture_size);
+ break;
+ case ResultType::Vector:
+ texture_ = texture_pool_->acquire_vector(texture_size);
+ break;
+ case ResultType::Color:
+ texture_ = texture_pool_->acquire_color(texture_size);
+ break;
+ }
+ domain_ = Domain::identity();
+}
+
+void Result::allocate_invalid()
+{
+ allocate_single_value();
+ switch (type_) {
+ case ResultType::Float:
+ set_float_value(0.0f);
+ break;
+ case ResultType::Vector:
+ set_vector_value(float3(0.0f));
+ break;
+ case ResultType::Color:
+ set_color_value(float4(0.0f));
+ break;
+ }
+}
+
+void Result::bind_as_texture(GPUShader *shader, const char *texture_name) const
+{
+ /* Make sure any prior writes to the texture are reflected before reading from it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(texture_, texture_image_unit);
+}
+
+void Result::bind_as_image(GPUShader *shader, const char *image_name) const
+{
+ const int image_unit = GPU_shader_get_texture_binding(shader, image_name);
+ GPU_texture_image_bind(texture_, image_unit);
+}
+
+void Result::unbind_as_texture() const
+{
+ GPU_texture_unbind(texture_);
+}
+
+void Result::unbind_as_image() const
+{
+ GPU_texture_image_unbind(texture_);
+}
+
+void Result::pass_through(Result &target)
+{
+ /* Increment the reference count of the master by the original reference count of the target. */
+ increment_reference_count(target.reference_count());
+
+ /* Make the target an exact copy of this result, but keep the initial reference count, as this is
+ * a property of the original result and is needed for correctly resetting the result before the
+ * next evaluation. */
+ const int initial_reference_count = target.initial_reference_count_;
+ target = *this;
+ target.initial_reference_count_ = initial_reference_count;
+
+ target.master_ = this;
+}
+
+void Result::transform(const float3x3 &transformation)
+{
+ domain_.transform(transformation);
+}
+
+RealizationOptions &Result::get_realization_options()
+{
+ return domain_.realization_options;
+}
+
+float Result::get_float_value() const
+{
+ return float_value_;
+}
+
+float3 Result::get_vector_value() const
+{
+ return vector_value_;
+}
+
+float4 Result::get_color_value() const
+{
+ return color_value_;
+}
+
+float Result::get_float_value_default(float default_value) const
+{
+ if (is_single_value()) {
+ return get_float_value();
+ }
+ return default_value;
+}
+
+float3 Result::get_vector_value_default(const float3 &default_value) const
+{
+ if (is_single_value()) {
+ return get_vector_value();
+ }
+ return default_value;
+}
+
+float4 Result::get_color_value_default(const float4 &default_value) const
+{
+ if (is_single_value()) {
+ return get_color_value();
+ }
+ return default_value;
+}
+
+void Result::set_float_value(float value)
+{
+ float_value_ = value;
+ GPU_texture_update(texture_, GPU_DATA_FLOAT, &float_value_);
+}
+
+void Result::set_vector_value(const float3 &value)
+{
+ vector_value_ = value;
+ GPU_texture_update(texture_, GPU_DATA_FLOAT, vector_value_);
+}
+
+void Result::set_color_value(const float4 &value)
+{
+ color_value_ = value;
+ GPU_texture_update(texture_, GPU_DATA_FLOAT, color_value_);
+}
+
+void Result::set_initial_reference_count(int count)
+{
+ initial_reference_count_ = count;
+}
+
+void Result::reset()
+{
+ master_ = nullptr;
+ reference_count_ = initial_reference_count_;
+}
+
+void Result::increment_reference_count(int count)
+{
+ /* If there is a master result, increment its reference count instead. */
+ if (master_) {
+ master_->increment_reference_count(count);
+ return;
+ }
+
+ reference_count_ += count;
+}
+
+void Result::release()
+{
+ /* If there is a master result, release it instead. */
+ if (master_) {
+ master_->release();
+ return;
+ }
+
+ /* Decrement the reference count, and if it reaches zero, release the texture back into the
+ * texture pool. */
+ reference_count_--;
+ if (reference_count_ == 0) {
+ texture_pool_->release(texture_);
+ }
+}
+
+bool Result::should_compute()
+{
+ return initial_reference_count_ != 0;
+}
+
+ResultType Result::type() const
+{
+ return type_;
+}
+
+bool Result::is_texture() const
+{
+ return !is_single_value_;
+}
+
+bool Result::is_single_value() const
+{
+ return is_single_value_;
+}
+
+GPUTexture *Result::texture() const
+{
+ return texture_;
+}
+
+int Result::reference_count() const
+{
+ /* If there is a master result, return its reference count instead. */
+ if (master_) {
+ return master_->reference_count();
+ }
+ return reference_count_;
+}
+
+const Domain &Result::domain() const
+{
+ return domain_;
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/scheduler.cc b/source/blender/compositor/realtime_compositor/intern/scheduler.cc
new file mode 100644
index 00000000000..ac5cc55a73f
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/scheduler.cc
@@ -0,0 +1,314 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_map.hh"
+#include "BLI_set.hh"
+#include "BLI_stack.hh"
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "BKE_node_runtime.hh"
+
+#include "COM_scheduler.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+/* Compute the output node whose result should be computed. The output node is the node marked as
+ * NODE_DO_OUTPUT. If multiple types of output nodes are marked, then the preference will be
+ * CMP_NODE_COMPOSITE > CMP_NODE_VIEWER > CMP_NODE_SPLITVIEWER. If no output node exists, a null
+ * node will be returned. */
+static DNode compute_output_node(DerivedNodeTree &tree)
+{
+ const bNodeTree &root_tree = tree.root_context().btree();
+
+ for (const bNode *node : root_tree.nodes_by_type("CompositorNodeComposite")) {
+ if (node->flag & NODE_DO_OUTPUT) {
+ return DNode(&tree.root_context(), node);
+ }
+ }
+
+ for (const bNode *node : root_tree.nodes_by_type("CompositorNodeViewer")) {
+ if (node->flag & NODE_DO_OUTPUT) {
+ return DNode(&tree.root_context(), node);
+ }
+ }
+
+ for (const bNode *node : root_tree.nodes_by_type("CompositorNodeSplitViewer")) {
+ if (node->flag & NODE_DO_OUTPUT) {
+ return DNode(&tree.root_context(), node);
+ }
+ }
+
+ /* No output node found, return a null node. */
+ return DNode();
+}
+
+/* A type representing a mapping that associates each node with a heuristic estimation of the
+ * number of intermediate buffers needed to compute it and all of its dependencies. See the
+ * compute_number_of_needed_buffers function for more information. */
+using NeededBuffers = Map<DNode, int>;
+
+/* Compute a heuristic estimation of the number of intermediate buffers needed to compute each node
+ * and all of its dependencies for all nodes that the given node depends on. The output is a map
+ * that maps each node with the number of intermediate buffers needed to compute it and all of its
+ * dependencies.
+ *
+ * Consider a node that takes n number of buffers as an input from a number of node dependencies,
+ * which we shall call the input nodes. The node also computes and outputs m number of buffers.
+ * In order for the node to compute its output, a number of intermediate buffers will be needed.
+ * Since the node takes n buffers and outputs m buffers, then the number of buffers directly
+ * needed by the node is (n + m). But each of the input buffers are computed by a node that, in
+ * turn, needs a number of buffers to compute its output. So the total number of buffers needed
+ * to compute the output of the node is max(n + m, d) where d is the number of buffers needed by
+ * the input node that needs the largest number of buffers. We only consider the input node that
+ * needs the largest number of buffers, because those buffers can be reused by any input node
+ * that needs a lesser number of buffers.
+ *
+ * Shader nodes, however, are a special case because links between two shader nodes inside the same
+ * shader operation don't pass a buffer, but a single value in the compiled shader. So for shader
+ * nodes, only inputs and outputs linked to nodes that are not shader nodes should be considered.
+ * Note that this might not actually be true, because the compiler may decide to split a shader
+ * operation into multiples ones that will pass buffers, but this is not something that can be
+ * known at scheduling-time. See the discussion in COM_compile_state.hh, COM_evaluator.hh, and
+ * COM_shader_operation.hh for more information. In the node tree shown below, node 4 will have
+ * exactly the same number of needed buffers by node 3, because its inputs and outputs are all
+ * internally linked in the shader operation.
+ *
+ * Shader Operation
+ * +------------------------------------------------------+
+ * .------------. | .------------. .------------. .------------. | .------------.
+ * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 |
+ * | |----|--| |--| |------| |--|--| |
+ * | | .-|--| | | | .---| | | | |
+ * '------------' | | '------------' '------------' | '------------' | '------------'
+ * | +----------------------------------|-------------------+
+ * .------------. | |
+ * | Node 2 | | |
+ * | |--'------------------------------------'
+ * | |
+ * '------------'
+ *
+ * Note that the computed output is not guaranteed to be accurate, and will not be in most cases.
+ * The computation is merely a heuristic estimation that works well in most cases. This is due to a
+ * number of reasons:
+ * - The node tree is actually a graph that allows output sharing, which is not something that was
+ * taken into consideration in this implementation because it is difficult to correctly consider.
+ * - Each node may allocate any number of internal buffers, which is not taken into account in this
+ * implementation because it rarely affects the output and is done by very few nodes.
+ * - The compiler may decide to compiler the schedule differently depending on runtime information
+ * which we can merely speculate at scheduling-time as described above. */
+static NeededBuffers compute_number_of_needed_buffers(DNode output_node)
+{
+ NeededBuffers needed_buffers;
+
+ /* A stack of nodes used to traverse the node tree starting from the output node. */
+ Stack<DNode> node_stack = {output_node};
+
+ /* Traverse the node tree in a post order depth first manner and compute the number of needed
+ * buffers for each node. Post order traversal guarantee that all the node dependencies of each
+ * node are computed before it. This is done by pushing all the uncomputed node dependencies to
+ * the node stack first and only popping and computing the node when all its node dependencies
+ * were computed. */
+ while (!node_stack.is_empty()) {
+ /* Do not pop the node immediately, as it may turn out that we can't compute its number of
+ * needed buffers just yet because its dependencies weren't computed, it will be popped later
+ * when needed. */
+ DNode &node = node_stack.peek();
+
+ /* Go over the node dependencies connected to the inputs of the node and push them to the node
+ * stack if they were not computed already. */
+ Set<DNode> pushed_nodes;
+ for (const bNodeSocket *input : node->input_sockets()) {
+ const DInputSocket dinput{node.context(), input};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked and
+ * has no dependency node. */
+ const DOutputSocket doutput = get_output_linked_to_input(dinput);
+ if (!doutput) {
+ continue;
+ }
+
+ /* The node dependency was already computed or pushed before, so skip it. */
+ if (needed_buffers.contains(doutput.node()) || pushed_nodes.contains(doutput.node())) {
+ continue;
+ }
+
+ /* The output node needs to be computed, push the node dependency to the node stack and
+ * indicate that it was pushed. */
+ node_stack.push(doutput.node());
+ pushed_nodes.add_new(doutput.node());
+ }
+
+ /* If any of the node dependencies were pushed, that means that not all of them were computed
+ * and consequently we can't compute the number of needed buffers for this node just yet. */
+ if (!pushed_nodes.is_empty()) {
+ continue;
+ }
+
+ /* We don't need to store the result of the pop because we already peeked at it before. */
+ node_stack.pop();
+
+ /* Compute the number of buffers that the node takes as an input as well as the number of
+ * buffers needed to compute the most demanding of the node dependencies. */
+ int number_of_input_buffers = 0;
+ int buffers_needed_by_dependencies = 0;
+ for (const bNodeSocket *input : node->input_sockets()) {
+ const DInputSocket dinput{node.context(), input};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked.
+ * Unlinked inputs do not take a buffer, so skip those inputs. */
+ const DOutputSocket doutput = get_output_linked_to_input(dinput);
+ if (!doutput) {
+ continue;
+ }
+
+ /* Since this input is linked, if the link is not between two shader nodes, it means that the
+ * node takes a buffer through this input and so we increment the number of input buffers. */
+ if (!is_shader_node(node) || !is_shader_node(doutput.node())) {
+ number_of_input_buffers++;
+ }
+
+ /* If the number of buffers needed by the node dependency is more than the total number of
+ * buffers needed by the dependencies, then update the latter to be the former. This is
+ * computing the "d" in the aforementioned equation "max(n + m, d)". */
+ const int buffers_needed_by_dependency = needed_buffers.lookup(doutput.node());
+ if (buffers_needed_by_dependency > buffers_needed_by_dependencies) {
+ buffers_needed_by_dependencies = buffers_needed_by_dependency;
+ }
+ }
+
+ /* Compute the number of buffers that will be computed/output by this node. */
+ int number_of_output_buffers = 0;
+ for (const bNodeSocket *output : node->output_sockets()) {
+ const DOutputSocket doutput{node.context(), output};
+
+ /* The output is not linked, it outputs no buffer. */
+ if (!output->is_logically_linked()) {
+ continue;
+ }
+
+ /* If any of the links is not between two shader nodes, it means that the node outputs
+ * a buffer through this output and so we increment the number of output buffers. */
+ if (!is_output_linked_to_node_conditioned(doutput, is_shader_node) ||
+ !is_shader_node(node)) {
+ number_of_output_buffers++;
+ }
+ }
+
+ /* Compute the heuristic estimation of the number of needed intermediate buffers to compute
+ * this node and all of its dependencies. This is computing the aforementioned equation
+ * "max(n + m, d)". */
+ const int total_buffers = MAX2(number_of_input_buffers + number_of_output_buffers,
+ buffers_needed_by_dependencies);
+ needed_buffers.add(node, total_buffers);
+ }
+
+ return needed_buffers;
+}
+
+/* There are multiple different possible orders of evaluating a node graph, each of which needs
+ * to allocate a number of intermediate buffers to store its intermediate results. It follows
+ * that we need to find the evaluation order which uses the least amount of intermediate buffers.
+ * For instance, consider a node that takes two input buffers A and B. Each of those buffers is
+ * computed through a number of nodes constituting a sub-graph whose root is the node that
+ * outputs that buffer. Suppose the number of intermediate buffers needed to compute A and B are
+ * N(A) and N(B) respectively and N(A) > N(B). Then evaluating the sub-graph computing A would be
+ * a better option than that of B, because had B was computed first, its outputs will need to be
+ * stored in extra buffers in addition to the buffers needed by A. The number of buffers needed by
+ * each node is estimated as described in the compute_number_of_needed_buffers function.
+ *
+ * This is a heuristic generalization of the Sethi–Ullman algorithm, a generalization that
+ * doesn't always guarantee an optimal evaluation order, as the optimal evaluation order is very
+ * difficult to compute, however, this method works well in most cases. Moreover it assumes that
+ * all buffers will have roughly the same size, which may not always be the case. */
+Schedule compute_schedule(DerivedNodeTree &tree)
+{
+ Schedule schedule;
+
+ /* Compute the output node whose result should be computed. */
+ const DNode output_node = compute_output_node(tree);
+
+ /* No output node, the node tree has no effect, return an empty schedule. */
+ if (!output_node) {
+ return schedule;
+ }
+
+ /* Compute the number of buffers needed by each node connected to the output. */
+ const NeededBuffers needed_buffers = compute_number_of_needed_buffers(output_node);
+
+ /* A stack of nodes used to traverse the node tree starting from the output node. */
+ Stack<DNode> node_stack = {output_node};
+
+ /* Traverse the node tree in a post order depth first manner, scheduling the nodes in an order
+ * informed by the number of buffers needed by each node. Post order traversal guarantee that all
+ * the node dependencies of each node are scheduled before it. This is done by pushing all the
+ * unscheduled node dependencies to the node stack first and only popping and scheduling the node
+ * when all its node dependencies were scheduled. */
+ while (!node_stack.is_empty()) {
+ /* Do not pop the node immediately, as it may turn out that we can't schedule it just yet
+ * because its dependencies weren't scheduled, it will be popped later when needed. */
+ DNode &node = node_stack.peek();
+
+ /* Compute the nodes directly connected to the node inputs sorted by their needed buffers such
+ * that the node with the lowest number of needed buffers comes first. Note that we actually
+ * want the node with the highest number of needed buffers to be schedule first, but since
+ * those are pushed to the traversal stack, we need to push them in reverse order. */
+ Vector<DNode> sorted_dependency_nodes;
+ for (const bNodeSocket *input : node->input_sockets()) {
+ const DInputSocket dinput{node.context(), input};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked and
+ * has no dependency node, so skip it. */
+ const DOutputSocket doutput = get_output_linked_to_input(dinput);
+ if (!doutput) {
+ continue;
+ }
+
+ /* The dependency node was added before, so skip it. The number of dependency nodes is very
+ * small, typically less than 3, so a linear search is okay. */
+ if (sorted_dependency_nodes.contains(doutput.node())) {
+ continue;
+ }
+
+ /* The dependency node was already schedule, so skip it. */
+ if (schedule.contains(doutput.node())) {
+ continue;
+ }
+
+ /* Sort in ascending order on insertion, the number of dependency nodes is very small,
+ * typically less than 3, so insertion sort is okay. */
+ int insertion_position = 0;
+ for (int i = 0; i < sorted_dependency_nodes.size(); i++) {
+ if (needed_buffers.lookup(doutput.node()) >
+ needed_buffers.lookup(sorted_dependency_nodes[i])) {
+ insertion_position++;
+ }
+ else {
+ break;
+ }
+ }
+ sorted_dependency_nodes.insert(insertion_position, doutput.node());
+ }
+
+ /* Push the sorted dependency nodes to the node stack in order. */
+ for (const DNode &dependency_node : sorted_dependency_nodes) {
+ node_stack.push(dependency_node);
+ }
+
+ /* If there are no sorted dependency nodes, that means they were all already scheduled or that
+ * none exists in the first place, so we can pop and schedule the node now. */
+ if (sorted_dependency_nodes.is_empty()) {
+ /* The node might have already been scheduled, so we don't use add_new here and simply don't
+ * add it if it was already scheduled. */
+ schedule.add(node_stack.pop());
+ }
+ }
+
+ return schedule;
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/shader_node.cc b/source/blender/compositor/realtime_compositor/intern/shader_node.cc
new file mode 100644
index 00000000000..96dd50790c3
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/shader_node.cc
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_assert.h"
+#include "BLI_math_vector.h"
+#include "BLI_string_ref.hh"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+ShaderNode::ShaderNode(DNode node) : node_(node)
+{
+ populate_inputs();
+ populate_outputs();
+}
+
+GPUNodeStack *ShaderNode::get_inputs_array()
+{
+ return inputs_.data();
+}
+
+GPUNodeStack *ShaderNode::get_outputs_array()
+{
+ return outputs_.data();
+}
+
+GPUNodeStack &ShaderNode::get_input(StringRef identifier)
+{
+ return inputs_[node_.input_by_identifier(identifier)->index()];
+}
+
+GPUNodeStack &ShaderNode::get_output(StringRef identifier)
+{
+ return outputs_[node_.output_by_identifier(identifier)->index()];
+}
+
+GPUNodeLink *ShaderNode::get_input_link(StringRef identifier)
+{
+ GPUNodeStack &input = get_input(identifier);
+ if (input.link) {
+ return input.link;
+ }
+ return GPU_uniform(input.vec);
+}
+
+const DNode &ShaderNode::node() const
+{
+ return node_;
+}
+
+const bNode &ShaderNode::bnode() const
+{
+ return *node_;
+}
+
+static eGPUType gpu_type_from_socket_type(eNodeSocketDatatype type)
+{
+ switch (type) {
+ case SOCK_FLOAT:
+ return GPU_FLOAT;
+ case SOCK_VECTOR:
+ return GPU_VEC3;
+ case SOCK_RGBA:
+ return GPU_VEC4;
+ default:
+ BLI_assert_unreachable();
+ return GPU_NONE;
+ }
+}
+
+static void gpu_stack_vector_from_socket(float *vector, const bNodeSocket *socket)
+{
+ switch (socket->type) {
+ case SOCK_FLOAT:
+ vector[0] = socket->default_value_typed<bNodeSocketValueFloat>()->value;
+ return;
+ case SOCK_VECTOR:
+ copy_v3_v3(vector, socket->default_value_typed<bNodeSocketValueVector>()->value);
+ return;
+ case SOCK_RGBA:
+ copy_v4_v4(vector, socket->default_value_typed<bNodeSocketValueRGBA>()->value);
+ return;
+ default:
+ BLI_assert_unreachable();
+ }
+}
+
+static void populate_gpu_node_stack(DSocket socket, GPUNodeStack &stack)
+{
+ /* Make sure this stack is not marked as the end of the stack array. */
+ stack.end = false;
+ /* This will be initialized later by the GPU material compiler or the compile method. */
+ stack.link = nullptr;
+
+ stack.sockettype = socket->type;
+ stack.type = gpu_type_from_socket_type((eNodeSocketDatatype)socket->type);
+
+ if (socket->is_input()) {
+ const DInputSocket input(socket);
+
+ DSocket origin = get_input_origin_socket(input);
+
+ /* The input is linked if the origin socket is an output socket. Had it been an input socket,
+ * then it is an unlinked input of a group input node. */
+ stack.hasinput = origin->is_output();
+
+ /* Get the socket value from the origin if it is an input, because then it would either be an
+ * unlinked input or an unlinked input of a group input node that the socket is linked to,
+ * otherwise, get the value from the socket itself. */
+ if (origin->is_input()) {
+ gpu_stack_vector_from_socket(stack.vec, origin.bsocket());
+ }
+ else {
+ gpu_stack_vector_from_socket(stack.vec, socket.bsocket());
+ }
+ }
+ else {
+ stack.hasoutput = socket->is_logically_linked();
+ }
+}
+
+void ShaderNode::populate_inputs()
+{
+ /* Reserve a stack for each input in addition to an extra stack at the end to mark the end of the
+ * array, as this is what the GPU module functions expect. */
+ const int num_input_sockets = node_->input_sockets().size();
+ inputs_.resize(num_input_sockets + 1);
+ inputs_.last().end = true;
+
+ for (int i = 0; i < num_input_sockets; i++) {
+ populate_gpu_node_stack(node_.input(i), inputs_[i]);
+ }
+}
+
+void ShaderNode::populate_outputs()
+{
+ /* Reserve a stack for each output in addition to an extra stack at the end to mark the end of
+ * the array, as this is what the GPU module functions expect. */
+ const int num_output_sockets = node_->output_sockets().size();
+ outputs_.resize(num_output_sockets + 1);
+ outputs_.last().end = true;
+
+ for (int i = 0; i < num_output_sockets; i++) {
+ populate_gpu_node_stack(node_.output(i), outputs_[i]);
+ }
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/shader_operation.cc b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc
new file mode 100644
index 00000000000..8e52baf63ec
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc
@@ -0,0 +1,526 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <memory>
+#include <string>
+
+#include "BLI_listbase.h"
+#include "BLI_map.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_utildefines.h"
+
+#include "DNA_customdata_types.h"
+
+#include "GPU_material.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+#include "GPU_uniform_buffer.h"
+
+#include "gpu_shader_create_info.hh"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_declaration.hh"
+
+#include "COM_context.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_scheduler.hh"
+#include "COM_shader_node.hh"
+#include "COM_shader_operation.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+
+ShaderOperation::ShaderOperation(Context &context, ShaderCompileUnit &compile_unit)
+ : Operation(context), compile_unit_(compile_unit)
+{
+ material_ = GPU_material_from_callbacks(&construct_material, &generate_code, this);
+ GPU_material_status_set(material_, GPU_MAT_QUEUED);
+ GPU_material_compile(material_);
+}
+
+ShaderOperation::~ShaderOperation()
+{
+ GPU_material_free_single(material_);
+}
+
+void ShaderOperation::execute()
+{
+ const Domain domain = compute_domain();
+ for (StringRef identifier : output_sockets_to_output_identifiers_map_.values()) {
+ Result &result = get_result(identifier);
+ result.allocate_texture(domain);
+ }
+
+ GPUShader *shader = GPU_material_get_shader(material_);
+ GPU_shader_bind(shader);
+
+ bind_material_resources(shader);
+ bind_inputs(shader);
+ bind_outputs(shader);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_texture_unbind_all();
+ GPU_texture_image_unbind_all();
+ GPU_uniformbuf_unbind_all();
+ GPU_shader_unbind();
+}
+
+StringRef ShaderOperation::get_output_identifier_from_output_socket(DOutputSocket output_socket)
+{
+ return output_sockets_to_output_identifiers_map_.lookup(output_socket);
+}
+
+Map<std::string, DOutputSocket> &ShaderOperation::get_inputs_to_linked_outputs_map()
+{
+ return inputs_to_linked_outputs_map_;
+}
+
+void ShaderOperation::compute_results_reference_counts(const Schedule &schedule)
+{
+ for (const auto &item : output_sockets_to_output_identifiers_map_.items()) {
+ const int reference_count = number_of_inputs_linked_to_output_conditioned(
+ item.key, [&](DInputSocket input) { return schedule.contains(input.node()); });
+
+ get_result(item.value).set_initial_reference_count(reference_count);
+ }
+}
+
+void ShaderOperation::bind_material_resources(GPUShader *shader)
+{
+ /* Bind the uniform buffer of the material if it exists. It may not exist if the GPU material has
+ * no uniforms. */
+ GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material_);
+ if (ubo) {
+ GPU_uniformbuf_bind(ubo, GPU_shader_get_uniform_block_binding(shader, GPU_UBO_BLOCK_NAME));
+ }
+
+ /* Bind color band textures needed by curve and ramp nodes. */
+ ListBase textures = GPU_material_textures(material_);
+ LISTBASE_FOREACH (GPUMaterialTexture *, texture, &textures) {
+ if (texture->colorband) {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture->sampler_name);
+ GPU_texture_bind(*texture->colorband, texture_image_unit);
+ }
+ }
+}
+
+void ShaderOperation::bind_inputs(GPUShader *shader)
+{
+ /* Attributes represents the inputs of the operation and their names match those of the inputs of
+ * the operation as well as the corresponding texture samples in the shader. */
+ ListBase attributes = GPU_material_attributes(material_);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
+ get_input(attribute->name).bind_as_texture(shader, attribute->name);
+ }
+}
+
+void ShaderOperation::bind_outputs(GPUShader *shader)
+{
+ for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) {
+ get_result(output_identifier).bind_as_image(shader, output_identifier.c_str());
+ }
+}
+
+void ShaderOperation::construct_material(void *thunk, GPUMaterial *material)
+{
+ ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
+ for (DNode node : operation->compile_unit_) {
+ ShaderNode *shader_node = node->typeinfo->get_compositor_shader_node(node);
+ operation->shader_nodes_.add_new(node, std::unique_ptr<ShaderNode>(shader_node));
+
+ operation->link_node_inputs(node, material);
+
+ shader_node->compile(material);
+
+ operation->populate_results_for_node(node, material);
+ }
+}
+
+void ShaderOperation::link_node_inputs(DNode node, GPUMaterial *material)
+{
+ for (const bNodeSocket *input : node->input_sockets()) {
+ const DInputSocket dinput{node.context(), input};
+
+ /* Get the output linked to the input. If it is null, that means the input is unlinked.
+ * Unlinked inputs are linked by the node compile method, so skip this here. */
+ const DOutputSocket doutput = get_output_linked_to_input(dinput);
+ if (!doutput) {
+ continue;
+ }
+
+ /* If the origin node is part of the shader operation, then the link is internal to the GPU
+ * material graph and is linked appropriately. */
+ if (compile_unit_.contains(doutput.node())) {
+ link_node_input_internal(dinput, doutput);
+ continue;
+ }
+
+ /* Otherwise, the origin node is not part of the shader operation, then the link is external to
+ * the GPU material graph and an input to the shader operation must be declared and linked to
+ * the node input. */
+ link_node_input_external(dinput, doutput, material);
+ }
+}
+
+void ShaderOperation::link_node_input_internal(DInputSocket input_socket,
+ DOutputSocket output_socket)
+{
+ ShaderNode &output_node = *shader_nodes_.lookup(output_socket.node());
+ GPUNodeStack &output_stack = output_node.get_output(output_socket->identifier);
+
+ ShaderNode &input_node = *shader_nodes_.lookup(input_socket.node());
+ GPUNodeStack &input_stack = input_node.get_input(input_socket->identifier);
+
+ input_stack.link = output_stack.link;
+}
+
+void ShaderOperation::link_node_input_external(DInputSocket input_socket,
+ DOutputSocket output_socket,
+ GPUMaterial *material)
+{
+
+ ShaderNode &node = *shader_nodes_.lookup(input_socket.node());
+ GPUNodeStack &stack = node.get_input(input_socket->identifier);
+
+ /* An input was already declared for that same output socket, so no need to declare it again. */
+ if (!output_to_material_attribute_map_.contains(output_socket)) {
+ declare_operation_input(input_socket, output_socket, material);
+ }
+
+ /* Link the attribute representing the shader operation input corresponding to the given output
+ * socket. */
+ stack.link = output_to_material_attribute_map_.lookup(output_socket);
+}
+
+static const char *get_set_function_name(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "set_value";
+ case ResultType::Vector:
+ return "set_rgb";
+ case ResultType::Color:
+ return "set_rgba";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+void ShaderOperation::declare_operation_input(DInputSocket input_socket,
+ DOutputSocket output_socket,
+ GPUMaterial *material)
+{
+ const int input_index = output_to_material_attribute_map_.size();
+ std::string input_identifier = "input" + std::to_string(input_index);
+
+ /* Declare the input descriptor for this input and prefer to declare its type to be the same as
+ * the type of the output socket because doing type conversion in the shader is much cheaper. */
+ InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_socket.bsocket());
+ input_descriptor.type = get_node_socket_result_type(output_socket.bsocket());
+ declare_input_descriptor(input_identifier, input_descriptor);
+
+ /* Add a new GPU attribute representing an input to the GPU material. Instead of using the
+ * attribute directly, we link it to an appropriate set function and use its output link instead.
+ * This is needed because the `gputype` member of the attribute is only initialized if it is
+ * linked to a GPU node. */
+ GPUNodeLink *attribute_link;
+ GPU_link(material,
+ get_set_function_name(input_descriptor.type),
+ GPU_attribute(material, CD_AUTO_FROM_NAME, input_identifier.c_str()),
+ &attribute_link);
+
+ /* Map the output socket to the attribute that was created for it. */
+ output_to_material_attribute_map_.add(output_socket, attribute_link);
+
+ /* Map the identifier of the operation input to the output socket it is linked to. */
+ inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket);
+}
+
+void ShaderOperation::populate_results_for_node(DNode node, GPUMaterial *material)
+{
+ for (const bNodeSocket *output : node->output_sockets()) {
+ const DOutputSocket doutput{node.context(), output};
+
+ /* If any of the nodes linked to the output are not part of the shader operation, then an
+ * output result needs to be populated for it. */
+ const bool need_to_populate_result = is_output_linked_to_node_conditioned(
+ doutput, [&](DNode node) { return !compile_unit_.contains(node); });
+
+ if (need_to_populate_result) {
+ populate_operation_result(doutput, material);
+ }
+ }
+}
+
+static const char *get_store_function_name(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "node_compositor_store_output_float";
+ case ResultType::Vector:
+ return "node_compositor_store_output_vector";
+ case ResultType::Color:
+ return "node_compositor_store_output_color";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+void ShaderOperation::populate_operation_result(DOutputSocket output_socket, GPUMaterial *material)
+{
+ const unsigned int output_id = output_sockets_to_output_identifiers_map_.size();
+ std::string output_identifier = "output" + std::to_string(output_id);
+
+ const ResultType result_type = get_node_socket_result_type(output_socket.bsocket());
+ const Result result = Result(result_type, texture_pool());
+ populate_result(output_identifier, result);
+
+ /* Map the output socket to the identifier of the newly populated result. */
+ output_sockets_to_output_identifiers_map_.add_new(output_socket, output_identifier);
+
+ ShaderNode &node = *shader_nodes_.lookup(output_socket.node());
+ GPUNodeLink *output_link = node.get_output(output_socket->identifier).link;
+
+ /* Link the output node stack to an output storer storing in the appropriate result. The result
+ * is identified by its index in the operation and the index is encoded as a float to be passed
+ * to the GPU function. Additionally, create an output link from the storer node to declare as an
+ * output to the GPU material. This storer output link is a dummy link in the sense that its
+ * value is ignored since it is already written in the output, but it is used to track nodes that
+ * contribute to the output of the compositor node tree. */
+ GPUNodeLink *storer_output_link;
+ GPUNodeLink *id_link = GPU_constant((float *)&output_id);
+ const char *store_function_name = get_store_function_name(result_type);
+ GPU_link(material, store_function_name, id_link, output_link, &storer_output_link);
+
+ /* Declare the output link of the storer node as an output of the GPU material to help the GPU
+ * code generator to track the nodes that contribute to the output of the shader. */
+ GPU_material_add_output_link_composite(material, storer_output_link);
+}
+
+using namespace gpu::shader;
+
+void ShaderOperation::generate_code(void *thunk,
+ GPUMaterial *material,
+ GPUCodegenOutput *code_generator_output)
+{
+ ShaderOperation *operation = static_cast<ShaderOperation *>(thunk);
+ ShaderCreateInfo &shader_create_info = *reinterpret_cast<ShaderCreateInfo *>(
+ code_generator_output->create_info);
+
+ shader_create_info.local_group_size(16, 16);
+
+ /* The resources are added without explicit locations, so make sure it is done by the
+ * shader creator. */
+ shader_create_info.auto_resource_location(true);
+
+ /* Add implementation for implicit conversion operations inserted by the code generator. This
+ * file should include the functions [float|vec3|vec4]_from_[float|vec3|vec4]. */
+ shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl");
+
+ /* The source shader is a compute shader with a main function that calls the dynamically
+ * generated evaluate function. The evaluate function includes the serialized GPU material graph
+ * preceded by code that initialized the inputs of the operation. Additionally, the storer
+ * functions that writes the outputs are defined outside the evaluate function. */
+ shader_create_info.compute_source("gpu_shader_compositor_main.glsl");
+
+ /* The main function is emitted in the shader before the evaluate function, so the evaluate
+ * function needs to be forward declared here. */
+ shader_create_info.typedef_source_generated += "void evaluate();\n";
+
+ operation->generate_code_for_outputs(shader_create_info);
+
+ shader_create_info.compute_source_generated += "void evaluate()\n{\n";
+
+ operation->generate_code_for_inputs(material, shader_create_info);
+
+ shader_create_info.compute_source_generated += code_generator_output->composite;
+
+ shader_create_info.compute_source_generated += "}\n";
+}
+
+static eGPUTextureFormat texture_format_from_result_type(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return GPU_R16F;
+ case ResultType::Vector:
+ return GPU_RGBA16F;
+ case ResultType::Color:
+ return GPU_RGBA16F;
+ }
+
+ BLI_assert_unreachable();
+ return GPU_RGBA16F;
+}
+
+/* Texture storers in the shader always take a vec4 as an argument, so encode each type in a vec4
+ * appropriately. */
+static const char *glsl_store_expression_from_result_type(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "vec4(value)";
+ case ResultType::Vector:
+ return "vec4(vector, 0.0)";
+ case ResultType::Color:
+ return "color";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info)
+{
+ const std::string store_float_function_header = "void store_float(const uint id, float value)";
+ const std::string store_vector_function_header = "void store_vector(const uint id, vec3 vector)";
+ const std::string store_color_function_header = "void store_color(const uint id, vec4 color)";
+
+ /* The store functions are used by the node_compositor_store_output_[float|vector|color]
+ * functions but are only defined later as part of the compute source, so they need to be forward
+ * declared. */
+ shader_create_info.typedef_source_generated += store_float_function_header + ";\n";
+ shader_create_info.typedef_source_generated += store_vector_function_header + ";\n";
+ shader_create_info.typedef_source_generated += store_color_function_header + ";\n";
+
+ /* Each of the store functions is essentially a single switch case on the given ID, so start by
+ * opening the function with a curly bracket followed by opening a switch statement in each of
+ * the functions. */
+ std::stringstream store_float_function;
+ std::stringstream store_vector_function;
+ std::stringstream store_color_function;
+ const std::string store_function_start = "\n{\n switch (id) {\n";
+ store_float_function << store_float_function_header << store_function_start;
+ store_vector_function << store_vector_function_header << store_function_start;
+ store_color_function << store_color_function_header << store_function_start;
+
+ for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) {
+ const Result &result = get_result(output_identifier);
+
+ /* Add a write-only image for this output where its values will be written. */
+ shader_create_info.image(0,
+ texture_format_from_result_type(result.type()),
+ Qualifier::WRITE,
+ ImageType::FLOAT_2D,
+ output_identifier,
+ Frequency::BATCH);
+
+ /* Add a case for the index of this output followed by a break statement. */
+ std::stringstream case_code;
+ const std::string store_expression = glsl_store_expression_from_result_type(result.type());
+ const std::string texel = ", ivec2(gl_GlobalInvocationID.xy), ";
+ case_code << " case " << StringRef(output_identifier).drop_known_prefix("output") << ":\n"
+ << " imageStore(" << output_identifier << texel << store_expression << ");\n"
+ << " break;\n";
+
+ /* Only add the case to the function with the matching type. */
+ switch (result.type()) {
+ case ResultType::Float:
+ store_float_function << case_code.str();
+ break;
+ case ResultType::Vector:
+ store_vector_function << case_code.str();
+ break;
+ case ResultType::Color:
+ store_color_function << case_code.str();
+ break;
+ }
+ }
+
+ /* Close the previously opened switch statement as well as the function itself. */
+ const std::string store_function_end = " }\n}\n\n";
+ store_float_function << store_function_end;
+ store_vector_function << store_function_end;
+ store_color_function << store_function_end;
+
+ shader_create_info.compute_source_generated += store_float_function.str() +
+ store_vector_function.str() +
+ store_color_function.str();
+}
+
+static const char *glsl_type_from_result_type(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "float";
+ case ResultType::Vector:
+ return "vec3";
+ case ResultType::Color:
+ return "vec4";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+/* Texture loaders in the shader always return a vec4, so a swizzle is needed to retrieve the
+ * actual value for each type. */
+static const char *glsl_swizzle_from_result_type(ResultType type)
+{
+ switch (type) {
+ case ResultType::Float:
+ return "x";
+ case ResultType::Vector:
+ return "xyz";
+ case ResultType::Color:
+ return "rgba";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+void ShaderOperation::generate_code_for_inputs(GPUMaterial *material,
+ ShaderCreateInfo &shader_create_info)
+{
+ /* The attributes of the GPU material represents the inputs of the operation. */
+ ListBase attributes = GPU_material_attributes(material);
+
+ if (BLI_listbase_is_empty(&attributes)) {
+ return;
+ }
+
+ /* Add a texture sampler for each of the inputs with the same name as the attribute. */
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
+ shader_create_info.sampler(0, ImageType::FLOAT_2D, attribute->name, Frequency::BATCH);
+ }
+
+ /* Declare a struct called var_attrs that includes an appropriately typed member for each of the
+ * inputs. The names of the members should be the letter v followed by the ID of the attribute
+ * corresponding to the input. Such names are expected by the code generator. */
+ std::stringstream declare_attributes;
+ declare_attributes << "struct {\n";
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
+ const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
+ const std::string type = glsl_type_from_result_type(input_descriptor.type);
+ declare_attributes << " " << type << " v" << attribute->id << ";\n";
+ }
+ declare_attributes << "} var_attrs;\n\n";
+
+ shader_create_info.compute_source_generated += declare_attributes.str();
+
+ /* The texture loader utilities are needed to sample the input textures and initialize the
+ * attributes. */
+ shader_create_info.typedef_source("gpu_shader_compositor_texture_utilities.glsl");
+
+ /* Initialize each member of the previously declared struct by loading its corresponding texture
+ * with an appropriate swizzle for its type. */
+ std::stringstream initialize_attributes;
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) {
+ const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name);
+ const std::string swizzle = glsl_swizzle_from_result_type(input_descriptor.type);
+ initialize_attributes << "var_attrs.v" << attribute->id << " = "
+ << "texture_load(" << attribute->name
+ << ", ivec2(gl_GlobalInvocationID.xy))." << swizzle << ";\n";
+ }
+ initialize_attributes << "\n";
+
+ shader_create_info.compute_source_generated += initialize_attributes.str();
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/simple_operation.cc b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc
new file mode 100644
index 00000000000..d55a20e5c54
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "COM_input_descriptor.hh"
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_simple_operation.hh"
+
+namespace blender::realtime_compositor {
+
+const StringRef SimpleOperation::input_identifier_ = StringRef("Input");
+const StringRef SimpleOperation::output_identifier_ = StringRef("Output");
+
+Result &SimpleOperation::get_result()
+{
+ return Operation::get_result(output_identifier_);
+}
+
+void SimpleOperation::map_input_to_result(Result *result)
+{
+ Operation::map_input_to_result(input_identifier_, result);
+}
+
+void SimpleOperation::add_and_evaluate_input_processors()
+{
+}
+
+Result &SimpleOperation::get_input()
+{
+ return Operation::get_input(input_identifier_);
+}
+
+void SimpleOperation::switch_result_mapped_to_input(Result *result)
+{
+ Operation::switch_result_mapped_to_input(input_identifier_, result);
+}
+
+void SimpleOperation::populate_result(Result result)
+{
+ Operation::populate_result(output_identifier_, result);
+
+ /* The result of a simple operation is guaranteed to have a single user. */
+ get_result().set_initial_reference_count(1);
+}
+
+void SimpleOperation::declare_input_descriptor(InputDescriptor descriptor)
+{
+ Operation::declare_input_descriptor(input_identifier_, descriptor);
+}
+
+InputDescriptor &SimpleOperation::get_input_descriptor()
+{
+ return Operation::get_input_descriptor(input_identifier_);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc
new file mode 100644
index 00000000000..c9c8a056f87
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "GPU_shader.h"
+
+#include "COM_static_shader_manager.hh"
+
+namespace blender::realtime_compositor {
+
+StaticShaderManager::~StaticShaderManager()
+{
+ for (GPUShader *shader : shaders_.values()) {
+ GPU_shader_free(shader);
+ }
+}
+
+GPUShader *StaticShaderManager::get(const char *info_name)
+{
+ /* If a shader with the same info name already exists in the manager, return it, otherwise,
+ * create a new shader from the info name and return it. */
+ return shaders_.lookup_or_add_cb(
+ info_name, [info_name]() { return GPU_shader_create_from_info_name(info_name); });
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/texture_pool.cc b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc
new file mode 100644
index 00000000000..6bf2041e6ba
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <cstdint>
+
+#include "BLI_hash.hh"
+#include "BLI_map.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_vector.hh"
+
+#include "GPU_texture.h"
+
+#include "COM_texture_pool.hh"
+
+namespace blender::realtime_compositor {
+
+/* -------------------------------------------------------------------- */
+/** \name Texture Pool Key
+ * \{ */
+
+TexturePoolKey::TexturePoolKey(int2 size, eGPUTextureFormat format) : size(size), format(format)
+{
+}
+
+TexturePoolKey::TexturePoolKey(const GPUTexture *texture)
+{
+ size = int2(GPU_texture_width(texture), GPU_texture_height(texture));
+ format = GPU_texture_format(texture);
+}
+
+uint64_t TexturePoolKey::hash() const
+{
+ return get_default_hash_3(size.x, size.y, format);
+}
+
+bool operator==(const TexturePoolKey &a, const TexturePoolKey &b)
+{
+ return a.size == b.size && a.format == b.format;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Texture Pool
+ * \{ */
+
+GPUTexture *TexturePool::acquire(int2 size, eGPUTextureFormat format)
+{
+ /* Check if there is an available texture with the required specification, and if one exists,
+ * return it. */
+ const TexturePoolKey key = TexturePoolKey(size, format);
+ Vector<GPUTexture *> &available_textures = textures_.lookup_or_add_default(key);
+ if (!available_textures.is_empty()) {
+ return available_textures.pop_last();
+ }
+
+ /* Otherwise, allocate a new texture. */
+ return allocate_texture(size, format);
+}
+
+GPUTexture *TexturePool::acquire_color(int2 size)
+{
+ return acquire(size, GPU_RGBA16F);
+}
+
+GPUTexture *TexturePool::acquire_vector(int2 size)
+{
+ /* Vectors are stored in RGBA textures because RGB textures have limited support. */
+ return acquire(size, GPU_RGBA16F);
+}
+
+GPUTexture *TexturePool::acquire_float(int2 size)
+{
+ return acquire(size, GPU_R16F);
+}
+
+void TexturePool::release(GPUTexture *texture)
+{
+ textures_.lookup(TexturePoolKey(texture)).append(texture);
+}
+
+void TexturePool::reset()
+{
+ textures_.clear();
+}
+
+/** \} */
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/compositor/realtime_compositor/intern/utilities.cc b/source/blender/compositor/realtime_compositor/intern/utilities.cc
new file mode 100644
index 00000000000..1a5823b8441
--- /dev/null
+++ b/source/blender/compositor/realtime_compositor/intern/utilities.cc
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_assert.h"
+#include "BLI_function_ref.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+#include "BLI_utildefines.h"
+
+#include "DNA_node_types.h"
+
+#include "NOD_derived_node_tree.hh"
+#include "NOD_node_declaration.hh"
+
+#include "GPU_compute.h"
+#include "GPU_shader.h"
+
+#include "COM_operation.hh"
+#include "COM_result.hh"
+#include "COM_utilities.hh"
+
+namespace blender::realtime_compositor {
+
+using namespace nodes::derived_node_tree_types;
+using TargetSocketPathInfo = DOutputSocket::TargetSocketPathInfo;
+
+DSocket get_input_origin_socket(DInputSocket input)
+{
+ /* The input is unlinked. Return the socket itself. */
+ if (!input->is_logically_linked()) {
+ return input;
+ }
+
+ /* Only a single origin socket is guaranteed to exist. */
+ DSocket socket;
+ input.foreach_origin_socket([&](const DSocket origin) { socket = origin; });
+ return socket;
+}
+
+DOutputSocket get_output_linked_to_input(DInputSocket input)
+{
+ /* Get the origin socket of this input, which will be an output socket if the input is linked
+ * to an output. */
+ const DSocket origin = get_input_origin_socket(input);
+
+ /* If the origin socket is an input, that means the input is unlinked, so return a null output
+ * socket. */
+ if (origin->is_input()) {
+ return DOutputSocket();
+ }
+
+ /* Now that we know the origin is an output, return a derived output from it. */
+ return DOutputSocket(origin);
+}
+
+ResultType get_node_socket_result_type(const bNodeSocket *socket)
+{
+ switch (socket->type) {
+ case SOCK_FLOAT:
+ return ResultType::Float;
+ case SOCK_VECTOR:
+ return ResultType::Vector;
+ case SOCK_RGBA:
+ return ResultType::Color;
+ default:
+ BLI_assert_unreachable();
+ return ResultType::Float;
+ }
+}
+
+bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef<bool(DNode)> condition)
+{
+ bool condition_satisfied = false;
+ output.foreach_target_socket(
+ [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) {
+ if (condition(target.node())) {
+ condition_satisfied = true;
+ return;
+ }
+ });
+ return condition_satisfied;
+}
+
+int number_of_inputs_linked_to_output_conditioned(DOutputSocket output,
+ FunctionRef<bool(DInputSocket)> condition)
+{
+ int count = 0;
+ output.foreach_target_socket(
+ [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) {
+ if (condition(target)) {
+ count++;
+ }
+ });
+ return count;
+}
+
+bool is_shader_node(DNode node)
+{
+ return node->typeinfo->get_compositor_shader_node;
+}
+
+bool is_node_supported(DNode node)
+{
+ return node->typeinfo->get_compositor_operation || node->typeinfo->get_compositor_shader_node;
+}
+
+InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket)
+{
+ using namespace nodes;
+ InputDescriptor input_descriptor;
+ input_descriptor.type = get_node_socket_result_type(socket);
+ const NodeDeclaration *node_declaration = socket->owner_node().declaration();
+ /* Not every node have a declaration, in which case, we assume the default values for the rest of
+ * the properties. */
+ if (!node_declaration) {
+ return input_descriptor;
+ }
+ const SocketDeclarationPtr &socket_declaration = node_declaration->inputs()[socket->index()];
+ input_descriptor.domain_priority = socket_declaration->compositor_domain_priority();
+ input_descriptor.skip_realization = socket_declaration->compositor_skip_realization();
+ input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value();
+ return input_descriptor;
+}
+
+void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size)
+{
+ /* If the threads range is divisible by the local size, dispatch the number of needed groups,
+ * which is their division. If it is not divisible, then dispatch an extra group to cover the
+ * remaining invocations, which means the actual threads range of the dispatch will be a bit
+ * larger than the given one. */
+ const int2 groups_to_dispatch = math::divide_ceil(threads_range, local_size);
+ GPU_compute_dispatch(shader, groups_to_dispatch.x, groups_to_dispatch.y, 1);
+}
+
+} // namespace blender::realtime_compositor
diff --git a/source/blender/datatoc/datatoc_icon.py b/source/blender/datatoc/datatoc_icon.py
index 7373df71318..84cef0457a6 100755
--- a/source/blender/datatoc/datatoc_icon.py
+++ b/source/blender/datatoc/datatoc_icon.py
@@ -75,8 +75,8 @@ def icon_merge(file_src, pixels_canvas, canvas_w, canvas_h):
orig_x, orig_y,
w_canvas_test, h_canvas_test) = head
- assert(w_canvas_test == canvas_w)
- assert(h_canvas_test == canvas_h)
+ assert w_canvas_test == canvas_w
+ assert h_canvas_test == canvas_h
for x in range(icon_w):
for y in range(icon_h):
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index 3d539018cef..a394ea4a54a 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -26,6 +26,8 @@ set(SRC
intern/builder/deg_builder.cc
intern/builder/deg_builder_cache.cc
intern/builder/deg_builder_cycle.cc
+ intern/builder/deg_builder_key.cc
+ intern/builder/deg_builder_key.h
intern/builder/deg_builder_map.cc
intern/builder/deg_builder_nodes.cc
intern/builder/deg_builder_nodes_rig.cc
@@ -34,7 +36,6 @@ set(SRC
intern/builder/deg_builder_pchanmap.cc
intern/builder/deg_builder_relations.cc
intern/builder/deg_builder_relations_drivers.cc
- intern/builder/deg_builder_relations_keys.cc
intern/builder/deg_builder_relations_rig.cc
intern/builder/deg_builder_relations_scene.cc
intern/builder/deg_builder_relations_view_layer.cc
@@ -67,6 +68,8 @@ set(SRC
intern/eval/deg_eval_runtime_backup_sound.cc
intern/eval/deg_eval_runtime_backup_volume.cc
intern/eval/deg_eval_stats.cc
+ intern/eval/deg_eval_visibility.cc
+ intern/eval/deg_eval_visibility.h
intern/node/deg_node.cc
intern/node/deg_node_component.cc
intern/node/deg_node_factory.cc
diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h
index 00efa779c4d..a8b21e4c153 100644
--- a/source/blender/depsgraph/DEG_depsgraph.h
+++ b/source/blender/depsgraph/DEG_depsgraph.h
@@ -125,13 +125,13 @@ void DEG_tag_on_visible_update(struct Main *bmain, bool do_time);
const char *DEG_update_tag_as_string(IDRecalcFlag flag);
/** Tag given ID for an update in all the dependency graphs. */
-void DEG_id_tag_update(struct ID *id, int flag);
-void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, int flag);
+void DEG_id_tag_update(struct ID *id, unsigned int flags);
+void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, unsigned int flags);
void DEG_graph_id_tag_update(struct Main *bmain,
struct Depsgraph *depsgraph,
struct ID *id,
- int flag);
+ unsigned int flags);
/** Tag all dependency graphs when time has changed. */
void DEG_time_tag_update(struct Main *bmain);
diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h
index 763d2d29035..201a534f535 100644
--- a/source/blender/depsgraph/DEG_depsgraph_build.h
+++ b/source/blender/depsgraph/DEG_depsgraph_build.h
@@ -101,7 +101,7 @@ typedef enum eDepsObjectComponentType {
DEG_OB_COMP_ANIMATION,
/* Transform Component (Parenting/Constraints) */
DEG_OB_COMP_TRANSFORM,
- /* Geometry Component (#Mesh / #DispList). */
+ /* Geometry Component (#Mesh / #Curves, etc.). */
DEG_OB_COMP_GEOMETRY,
/* Evaluation-Related Outer Types (with Sub-data) */
@@ -161,8 +161,8 @@ void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle,
* This function will take care of checking which operation is required to
* have transformation for the modifier, taking into account possible simulation solvers.
*/
-void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle,
- const char *description);
+void DEG_add_depends_on_transform_relation(struct DepsNodeHandle *node_handle,
+ const char *description);
/**
* Adds relations from the given component of a given object to the given node
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc
index a3cd821e82f..16510d5f9a6 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder.cc
@@ -13,6 +13,7 @@
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_layer_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_stack.h"
@@ -29,6 +30,7 @@
#include "intern/depsgraph_tag.h"
#include "intern/depsgraph_type.h"
#include "intern/eval/deg_eval_copy_on_write.h"
+#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -63,7 +65,7 @@ DepsgraphBuilder::DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuild
{
}
-bool DepsgraphBuilder::need_pull_base_into_graph(Base *base)
+bool DepsgraphBuilder::need_pull_base_into_graph(const Base *base)
{
/* Simple check: enabled bases are always part of dependency graph. */
const int base_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT :
@@ -71,10 +73,15 @@ bool DepsgraphBuilder::need_pull_base_into_graph(Base *base)
if (base->flag & base_flag) {
return true;
}
+
/* More involved check: since we don't support dynamic changes in dependency graph topology and
* all visible objects are to be part of dependency graph, we pull all objects which has animated
* visibility. */
- Object *object = base->object;
+ return is_object_visibility_animated(base->object);
+}
+
+bool DepsgraphBuilder::is_object_visibility_animated(const Object *object)
+{
AnimatedPropertyID property_id;
if (graph_->mode == DAG_EVAL_VIEWPORT) {
property_id = AnimatedPropertyID(&object->id, &RNA_Object, "hide_viewport");
@@ -89,7 +96,25 @@ bool DepsgraphBuilder::need_pull_base_into_graph(Base *base)
return cache_->isPropertyAnimated(&object->id, property_id);
}
-bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel *pchan)
+bool DepsgraphBuilder::is_modifier_visibility_animated(const Object *object,
+ const ModifierData *modifier)
+{
+ AnimatedPropertyID property_id;
+ if (graph_->mode == DAG_EVAL_VIEWPORT) {
+ property_id = AnimatedPropertyID(
+ &object->id, &RNA_Modifier, (void *)modifier, "show_viewport");
+ }
+ else if (graph_->mode == DAG_EVAL_RENDER) {
+ property_id = AnimatedPropertyID(&object->id, &RNA_Modifier, (void *)modifier, "show_render");
+ }
+ else {
+ BLI_assert_msg(0, "Unknown evaluation mode.");
+ return false;
+ }
+ return cache_->isPropertyAnimated(&object->id, property_id);
+}
+
+bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan)
{
BLI_assert(object->type == OB_ARMATURE);
if (pchan == nullptr || pchan->bone == nullptr) {
@@ -109,12 +134,13 @@ bool DepsgraphBuilder::check_pchan_has_bbone(Object *object, const bPoseChannel
cache_->isPropertyAnimated(&armature->id, property_id);
}
-bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan)
+bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object,
+ const bPoseChannel *pchan)
{
return check_pchan_has_bbone(object, pchan);
}
-bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const char *bone_name)
+bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, const char *bone_name)
{
const bPoseChannel *pchan = BKE_pose_channel_find_name(object->pose, bone_name);
return check_pchan_has_bbone_segments(object, pchan);
@@ -126,97 +152,9 @@ bool DepsgraphBuilder::check_pchan_has_bbone_segments(Object *object, const char
/** \name Builder Finalizer.
* \{ */
-namespace {
-
-void deg_graph_build_flush_visibility(Depsgraph *graph)
-{
- enum {
- DEG_NODE_VISITED = (1 << 0),
- };
-
- BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
- for (IDNode *id_node : graph->id_nodes) {
- for (ComponentNode *comp_node : id_node->components.values()) {
- comp_node->affects_directly_visible |= id_node->is_directly_visible;
-
- /* Enforce "visibility" of the synchronization component.
- *
- * This component is never connected to other ID nodes, and hence can not be handled in the
- * same way as other components needed for evaluation. It is only needed for proper
- * evaluation of the ID node it belongs to.
- *
- * The design is such that the synchronization is supposed to happen whenever any part of the
- * ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag
- * flushing and scheduling will handle the component in a generic manner. */
- if (comp_node->type == NodeType::SYNCHRONIZATION) {
- comp_node->affects_directly_visible = true;
- }
- }
- }
-
- for (OperationNode *op_node : graph->operations) {
- op_node->custom_flags = 0;
- op_node->num_links_pending = 0;
- for (Relation *rel : op_node->outlinks) {
- if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
- ++op_node->num_links_pending;
- }
- }
- if (op_node->num_links_pending == 0) {
- BLI_stack_push(stack, &op_node);
- op_node->custom_flags |= DEG_NODE_VISITED;
- }
- }
-
- while (!BLI_stack_is_empty(stack)) {
- OperationNode *op_node;
- BLI_stack_pop(stack, &op_node);
- /* Flush layers to parents. */
- for (Relation *rel : op_node->inlinks) {
- if (rel->from->type == NodeType::OPERATION) {
- OperationNode *op_from = (OperationNode *)rel->from;
- ComponentNode *comp_from = op_from->owner;
- const bool target_directly_visible = op_node->owner->affects_directly_visible;
-
- /* Visibility component forces all components of the current ID to be considered as
- * affecting directly visible. */
- if (comp_from->type == NodeType::VISIBILITY) {
- if (target_directly_visible) {
- IDNode *id_node_from = comp_from->owner;
- for (ComponentNode *comp_node : id_node_from->components.values()) {
- comp_node->affects_directly_visible |= target_directly_visible;
- }
- }
- }
- else {
- comp_from->affects_directly_visible |= target_directly_visible;
- }
- }
- }
- /* Schedule parent nodes. */
- for (Relation *rel : op_node->inlinks) {
- if (rel->from->type == NodeType::OPERATION) {
- OperationNode *op_from = (OperationNode *)rel->from;
- if ((rel->flag & RELATION_FLAG_CYCLIC) == 0) {
- BLI_assert(op_from->num_links_pending > 0);
- --op_from->num_links_pending;
- }
- if ((op_from->num_links_pending == 0) && (op_from->custom_flags & DEG_NODE_VISITED) == 0) {
- BLI_stack_push(stack, &op_from);
- op_from->custom_flags |= DEG_NODE_VISITED;
- }
- }
- }
- }
- BLI_stack_free(stack);
-}
-
-} // namespace
-
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
{
- /* Make sure dependencies of visible ID data-blocks are visible. */
- deg_graph_build_flush_visibility(graph);
+ deg_graph_flush_visibility_flags(graph);
deg_graph_remove_unused_noops(graph);
/* Re-tag IDs for update if it was tagged before the relations
@@ -240,6 +178,9 @@ void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
if (GS(id_orig->name) == ID_OB) {
flag |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY;
}
+ if (GS(id_orig->name) == ID_NT) {
+ flag |= ID_RECALC_NTREE_OUTPUT;
+ }
}
/* Restore recalc flags from original ID, which could possibly contain recalc flags set by
* an operator and then were carried on by the undo system. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h
index 8fbe255ed9d..5d043f1fd3a 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder.h
@@ -10,11 +10,11 @@
struct Base;
struct ID;
struct Main;
+struct ModifierData;
struct Object;
struct bPoseChannel;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
class DepsgraphBuilderCache;
@@ -23,11 +23,14 @@ class DepsgraphBuilder {
public:
virtual ~DepsgraphBuilder() = default;
- virtual bool need_pull_base_into_graph(Base *base);
+ virtual bool need_pull_base_into_graph(const Base *base);
- virtual bool check_pchan_has_bbone(Object *object, const bPoseChannel *pchan);
- virtual bool check_pchan_has_bbone_segments(Object *object, const bPoseChannel *pchan);
- virtual bool check_pchan_has_bbone_segments(Object *object, const char *bone_name);
+ virtual bool is_object_visibility_animated(const Object *object);
+ virtual bool is_modifier_visibility_animated(const Object *object, const ModifierData *modifier);
+
+ virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan);
+ virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan);
+ virtual bool check_pchan_has_bbone_segments(const Object *object, const char *bone_name);
protected:
/* NOTE: The builder does NOT take ownership over any of those resources. */
@@ -43,5 +46,4 @@ bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig);
bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base);
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
index 7f88f54fdca..129e0093d11 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.cc
@@ -15,6 +15,8 @@
#include "BKE_animsys.h"
+#include "RNA_path.h"
+
namespace blender::deg {
/* Animated property storage. */
@@ -35,13 +37,13 @@ AnimatedPropertyID::AnimatedPropertyID(const PointerRNA &pointer_rna,
{
}
-AnimatedPropertyID::AnimatedPropertyID(ID *id, StructRNA *type, const char *property_name)
+AnimatedPropertyID::AnimatedPropertyID(const ID *id, StructRNA *type, const char *property_name)
: data(id)
{
property_rna = RNA_struct_type_find_property(type, property_name);
}
-AnimatedPropertyID::AnimatedPropertyID(ID * /*id*/,
+AnimatedPropertyID::AnimatedPropertyID(const ID * /*id*/,
StructRNA *type,
void *data,
const char *property_name)
@@ -100,13 +102,13 @@ AnimatedPropertyStorage::AnimatedPropertyStorage() : is_fully_initialized(false)
{
}
-void AnimatedPropertyStorage::initializeFromID(DepsgraphBuilderCache *builder_cache, ID *id)
+void AnimatedPropertyStorage::initializeFromID(DepsgraphBuilderCache *builder_cache, const ID *id)
{
AnimatedPropertyCallbackData data;
- RNA_id_pointer_create(id, &data.pointer_rna);
+ RNA_id_pointer_create(const_cast<ID *>(id), &data.pointer_rna);
data.animated_property_storage = this;
data.builder_cache = builder_cache;
- BKE_fcurves_id_cb(id, animated_property_cb, &data);
+ BKE_fcurves_id_cb(const_cast<ID *>(id), animated_property_cb, &data);
}
void AnimatedPropertyStorage::tagPropertyAsAnimated(const AnimatedPropertyID &property_id)
@@ -147,13 +149,14 @@ DepsgraphBuilderCache::~DepsgraphBuilderCache()
}
}
-AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(ID *id)
+AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(const ID *id)
{
return animated_property_storage_map_.lookup_or_add_cb(
id, []() { return new AnimatedPropertyStorage(); });
}
-AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage(ID *id)
+AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage(
+ const ID *id)
{
AnimatedPropertyStorage *animated_property_storage = ensureAnimatedPropertyStorage(id);
if (!animated_property_storage->is_fully_initialized) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cache.h b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
index 2d2bdeaf825..d85269c0f8b 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cache.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cache.h
@@ -17,8 +17,7 @@ struct ID;
struct PointerRNA;
struct PropertyRNA;
-namespace blender {
-namespace deg {
+namespace blender::deg {
class DepsgraphBuilderCache;
@@ -28,14 +27,14 @@ class AnimatedPropertyID {
AnimatedPropertyID();
AnimatedPropertyID(const PointerRNA *pointer_rna, const PropertyRNA *property_rna);
AnimatedPropertyID(const PointerRNA &pointer_rna, const PropertyRNA *property_rna);
- AnimatedPropertyID(ID *id, StructRNA *type, const char *property_name);
- AnimatedPropertyID(ID *id, StructRNA *type, void *data, const char *property_name);
+ AnimatedPropertyID(const ID *id, StructRNA *type, const char *property_name);
+ AnimatedPropertyID(const ID *id, StructRNA *type, void *data, const char *property_name);
uint64_t hash() const;
friend bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b);
/* Corresponds to PointerRNA.data. */
- void *data;
+ const void *data;
const PropertyRNA *property_rna;
MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyID");
@@ -45,7 +44,7 @@ class AnimatedPropertyStorage {
public:
AnimatedPropertyStorage();
- void initializeFromID(DepsgraphBuilderCache *builder_cache, ID *id);
+ void initializeFromID(DepsgraphBuilderCache *builder_cache, const ID *id);
void tagPropertyAsAnimated(const AnimatedPropertyID &property_id);
void tagPropertyAsAnimated(const PointerRNA *pointer_rna, const PropertyRNA *property_rna);
@@ -59,7 +58,7 @@ class AnimatedPropertyStorage {
bool is_fully_initialized;
/* indexed by PointerRNA.data. */
- Set<void *> animated_objects_set;
+ Set<const void *> animated_objects_set;
Set<AnimatedPropertyID> animated_properties_set;
MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyStorage");
@@ -71,8 +70,8 @@ class DepsgraphBuilderCache {
~DepsgraphBuilderCache();
/* Makes sure storage for animated properties exists and initialized for the given ID. */
- AnimatedPropertyStorage *ensureAnimatedPropertyStorage(ID *id);
- AnimatedPropertyStorage *ensureInitializedAnimatedPropertyStorage(ID *id);
+ AnimatedPropertyStorage *ensureAnimatedPropertyStorage(const ID *id);
+ AnimatedPropertyStorage *ensureInitializedAnimatedPropertyStorage(const ID *id);
/* Shortcuts to go through ensureInitializedAnimatedPropertyStorage and its
* isPropertyAnimated.
@@ -82,7 +81,7 @@ class DepsgraphBuilderCache {
*
* TODO(sergey): Technically, this makes this class something else than just a cache, but what is
* the better name? */
- template<typename... Args> bool isPropertyAnimated(ID *id, Args... args)
+ template<typename... Args> bool isPropertyAnimated(const ID *id, Args... args)
{
AnimatedPropertyStorage *animated_property_storage = ensureInitializedAnimatedPropertyStorage(
id);
@@ -96,10 +95,9 @@ class DepsgraphBuilderCache {
return animated_property_storage->isAnyPropertyAnimated(ptr);
}
- Map<ID *, AnimatedPropertyStorage *> animated_property_storage_map_;
+ Map<const ID *, AnimatedPropertyStorage *> animated_property_storage_map_;
MEM_CXX_CLASS_ALLOC_FUNCS("DepsgraphBuilderCache");
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.h b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h
index 8e94e0ae21f..83d25f8c23f 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.h
@@ -7,13 +7,11 @@
#pragma once
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
/* Detect and solve dependency cycles. */
void deg_graph_detect_cycles(Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_key.cc b/source/blender/depsgraph/intern/builder/deg_builder_key.cc
new file mode 100644
index 00000000000..741c415b5cd
--- /dev/null
+++ b/source/blender/depsgraph/intern/builder/deg_builder_key.cc
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2013 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup depsgraph
+ *
+ * Methods for constructing depsgraph
+ */
+
+#include "intern/builder/deg_builder_key.h"
+
+#include "RNA_path.h"
+
+namespace blender::deg {
+
+/* -------------------------------------------------------------------- */
+/** \name Time source
+ * \{ */
+
+string TimeSourceKey::identifier() const
+{
+ return string("TimeSourceKey");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Component
+ * \{ */
+
+string ComponentKey::identifier() const
+{
+ const char *idname = (id) ? id->name : "<None>";
+ string result = string("ComponentKey(");
+ result += idname;
+ result += ", " + string(nodeTypeAsString(type));
+ if (name[0] != '\0') {
+ result += ", '" + string(name) + "'";
+ }
+ result += ')';
+ return result;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Operation
+ * \{ */
+
+string OperationKey::identifier() const
+{
+ string result = string("OperationKey(");
+ result += "type: " + string(nodeTypeAsString(component_type));
+ result += ", component name: '" + string(component_name) + "'";
+ result += ", operation code: " + string(operationCodeAsString(opcode));
+ if (name[0] != '\0') {
+ result += ", '" + string(name) + "'";
+ }
+ result += ")";
+ return result;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name RNA path
+ * \{ */
+
+RNAPathKey::RNAPathKey(ID *id, const char *path, RNAPointerSource source) : id(id), source(source)
+{
+ /* Create ID pointer for root of path lookup. */
+ PointerRNA id_ptr;
+ RNA_id_pointer_create(id, &id_ptr);
+ /* Try to resolve path. */
+ int index;
+ if (!RNA_path_resolve_full(&id_ptr, path, &ptr, &prop, &index)) {
+ ptr = PointerRNA_NULL;
+ prop = nullptr;
+ }
+}
+
+RNAPathKey::RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop, RNAPointerSource source)
+ : id(id), ptr(ptr), prop(prop), source(source)
+{
+}
+
+string RNAPathKey::identifier() const
+{
+ const char *id_name = (id) ? id->name : "<No ID>";
+ const char *prop_name = (prop) ? RNA_property_identifier(prop) : "<No Prop>";
+ return string("RnaPathKey(") + "id: " + id_name + ", prop: '" + prop_name + "')";
+}
+
+/** \} */
+
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_key.h b/source/blender/depsgraph/intern/builder/deg_builder_key.h
new file mode 100644
index 00000000000..4f8b2dc9f8f
--- /dev/null
+++ b/source/blender/depsgraph/intern/builder/deg_builder_key.h
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2013 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#pragma once
+
+#include "intern/builder/deg_builder_rna.h"
+#include "intern/depsgraph_type.h"
+#include "intern/node/deg_node_component.h"
+#include "intern/node/deg_node_id.h"
+#include "intern/node/deg_node_operation.h"
+
+#include "DNA_ID.h"
+
+#include "RNA_access.h"
+#include "RNA_types.h"
+
+struct ID;
+struct PropertyRNA;
+
+namespace blender::deg {
+
+struct TimeSourceKey {
+ TimeSourceKey() = default;
+
+ string identifier() const;
+};
+
+struct ComponentKey {
+ ComponentKey() = default;
+
+ inline ComponentKey(const ID *id, NodeType type, const char *name = "")
+ : id(id), type(type), name(name)
+ {
+ }
+
+ string identifier() const;
+
+ const ID *id = nullptr;
+ NodeType type = NodeType::UNDEFINED;
+ const char *name = "";
+};
+
+struct OperationKey {
+ OperationKey() = default;
+
+ inline OperationKey(const ID *id, NodeType component_type, const char *name, int name_tag = -1)
+ : id(id),
+ component_type(component_type),
+ component_name(""),
+ opcode(OperationCode::OPERATION),
+ name(name),
+ name_tag(name_tag)
+ {
+ }
+
+ OperationKey(const ID *id,
+ NodeType component_type,
+ const char *component_name,
+ const char *name,
+ int name_tag)
+ : id(id),
+ component_type(component_type),
+ component_name(component_name),
+ opcode(OperationCode::OPERATION),
+ name(name),
+ name_tag(name_tag)
+ {
+ }
+
+ OperationKey(const ID *id, NodeType component_type, OperationCode opcode)
+ : id(id),
+ component_type(component_type),
+ component_name(""),
+ opcode(opcode),
+ name(""),
+ name_tag(-1)
+ {
+ }
+
+ OperationKey(const ID *id,
+ NodeType component_type,
+ const char *component_name,
+ OperationCode opcode)
+ : id(id),
+ component_type(component_type),
+ component_name(component_name),
+ opcode(opcode),
+ name(""),
+ name_tag(-1)
+ {
+ }
+
+ OperationKey(const ID *id,
+ NodeType component_type,
+ OperationCode opcode,
+ const char *name,
+ int name_tag = -1)
+ : id(id),
+ component_type(component_type),
+ component_name(""),
+ opcode(opcode),
+ name(name),
+ name_tag(name_tag)
+ {
+ }
+
+ OperationKey(const ID *id,
+ NodeType component_type,
+ const char *component_name,
+ OperationCode opcode,
+ const char *name,
+ int name_tag = -1)
+ : id(id),
+ component_type(component_type),
+ component_name(component_name),
+ opcode(opcode),
+ name(name),
+ name_tag(name_tag)
+ {
+ }
+
+ OperationKey(OperationKey &&other) noexcept = default;
+ OperationKey &operator=(OperationKey &&other) = default;
+
+ OperationKey(const OperationKey &other) = default;
+ OperationKey &operator=(const OperationKey &other) = default;
+
+ string identifier() const;
+
+ const ID *id = nullptr;
+ NodeType component_type = NodeType::UNDEFINED;
+ const char *component_name = "";
+ OperationCode opcode = OperationCode::OPERATION;
+ const char *name = "";
+ int name_tag = -1;
+};
+
+/* Similar to the the OperationKey but does not contain external references, which makes it
+ * suitable to identify operations even after the original database or graph was destroyed.
+ * The downside of this key over the OperationKey is that it performs string allocation upon
+ * the key construction. */
+struct PersistentOperationKey : public OperationKey {
+ /* Create the key which identifies the given operation node. */
+ PersistentOperationKey(const OperationNode *operation_node)
+ {
+ const ComponentNode *component_node = operation_node->owner;
+ const IDNode *id_node = component_node->owner;
+
+ /* Copy names over to our object, so that the key stays valid even after the `operation_node`
+ * is destroyed.*/
+ component_name_storage_ = component_node->name;
+ name_storage_ = operation_node->name;
+
+ /* Assign fields used by the OperationKey API. */
+ id = id_node->id_orig;
+ component_type = component_node->type;
+ component_name = component_name_storage_.c_str();
+ opcode = operation_node->opcode;
+ name = name_storage_.c_str();
+ name_tag = operation_node->name_tag;
+ }
+
+ PersistentOperationKey(PersistentOperationKey &&other) noexcept : OperationKey(other)
+ {
+ component_name_storage_ = std::move(other.component_name_storage_);
+ name_storage_ = std::move(other.name_storage_);
+
+ /* Re-assign pointers to the strings.
+ * This is needed because string content can actually change address if the string uses the
+ * small string optimization. */
+ component_name = component_name_storage_.c_str();
+ name = name_storage_.c_str();
+ }
+
+ PersistentOperationKey &operator=(PersistentOperationKey &&other) = delete;
+
+ PersistentOperationKey(const PersistentOperationKey &other) = delete;
+ PersistentOperationKey &operator=(const PersistentOperationKey &other) = delete;
+
+ private:
+ string component_name_storage_;
+ string name_storage_;
+};
+
+struct RNAPathKey {
+ RNAPathKey(ID *id, const char *path, RNAPointerSource source);
+ RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop, RNAPointerSource source);
+
+ string identifier() const;
+
+ ID *id;
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ RNAPointerSource source;
+};
+
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_map.h b/source/blender/depsgraph/intern/builder/deg_builder_map.h
index 50ebadeb382..2b2b4d089e9 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_map.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_map.h
@@ -11,8 +11,7 @@
struct ID;
-namespace blender {
-namespace deg {
+namespace blender::deg {
class BuilderMap {
public:
@@ -60,5 +59,4 @@ class BuilderMap {
Map<ID *, int> id_tags_;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 657bc3eb25c..f95c0700a47 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -26,6 +26,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
+#include "DNA_curves_types.h"
#include "DNA_effect_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
@@ -92,6 +93,7 @@
#include "BKE_world.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "RNA_types.h"
@@ -102,11 +104,13 @@
#include "SEQ_sequencer.h"
#include "intern/builder/deg_builder.h"
+#include "intern/builder/deg_builder_key.h"
#include "intern/builder/deg_builder_rna.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/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -174,24 +178,38 @@ IDNode *DepsgraphNodeBuilder::add_id_node(ID *id)
ComponentNode *comp_cow = id_node->add_component(NodeType::COPY_ON_WRITE);
OperationNode *op_cow = comp_cow->add_operation(
[id_node](::Depsgraph *depsgraph) { deg_evaluate_copy_on_write(depsgraph, id_node); },
- OperationCode::COPY_ON_WRITE,
- "",
- -1);
+ OperationCode::COPY_ON_WRITE);
graph_->operations.append(op_cow);
}
ComponentNode *visibility_component = id_node->add_component(NodeType::VISIBILITY);
- OperationNode *visibility_operation = visibility_component->add_operation(
- nullptr, OperationCode::OPERATION, "", -1);
+ OperationNode *visibility_operation;
+
+ /* Optimization: currently only objects need a special visibility evaluation. For the rest ID
+ * types keep the node as a NO-OP so that relations can still be routed, but without penalty
+ * during the graph evaluation. */
+ if (id_type == ID_OB) {
+ visibility_operation = visibility_component->add_operation(
+ [id_node](::Depsgraph *depsgraph) {
+ deg_evaluate_object_node_visibility(depsgraph, id_node);
+ },
+ OperationCode::VISIBILITY);
+ }
+ else {
+ visibility_operation = visibility_component->add_operation(nullptr,
+ OperationCode::VISIBILITY);
+ }
+
/* Pin the node so that it and its relations are preserved by the unused nodes/relations
* deletion. This is mainly to make it easier to debug visibility. */
- visibility_operation->flag |= OperationFlag::DEPSOP_FLAG_PINNED;
+ visibility_operation->flag |= (OperationFlag::DEPSOP_FLAG_PINNED |
+ OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
graph_->operations.append(visibility_operation);
}
return id_node;
}
-IDNode *DepsgraphNodeBuilder::find_id_node(ID *id)
+IDNode *DepsgraphNodeBuilder::find_id_node(const ID *id)
{
return graph_->find_id_node(id);
}
@@ -211,6 +229,17 @@ ComponentNode *DepsgraphNodeBuilder::add_component_node(ID *id,
return comp_node;
}
+ComponentNode *DepsgraphNodeBuilder::find_component_node(const ID *id,
+ const NodeType comp_type,
+ const char *comp_name)
+{
+ IDNode *id_node = find_id_node(id);
+ if (id_node == nullptr) {
+ return nullptr;
+ }
+ return id_node->find_component(comp_type, comp_name);
+}
+
OperationNode *DepsgraphNodeBuilder::add_operation_node(ComponentNode *comp_node,
OperationCode opcode,
const DepsEvalOperationCb &op,
@@ -294,23 +323,32 @@ bool DepsgraphNodeBuilder::has_operation_node(ID *id,
return find_operation_node(id, comp_type, comp_name, opcode, name, name_tag) != nullptr;
}
-OperationNode *DepsgraphNodeBuilder::find_operation_node(ID *id,
+OperationNode *DepsgraphNodeBuilder::find_operation_node(const ID *id,
NodeType comp_type,
const char *comp_name,
OperationCode opcode,
const char *name,
int name_tag)
{
- ComponentNode *comp_node = add_component_node(id, comp_type, comp_name);
+ ComponentNode *comp_node = find_component_node(id, comp_type, comp_name);
+ if (comp_node == nullptr) {
+ return nullptr;
+ }
return comp_node->find_operation(opcode, name, name_tag);
}
OperationNode *DepsgraphNodeBuilder::find_operation_node(
- ID *id, NodeType comp_type, OperationCode opcode, const char *name, int name_tag)
+ const ID *id, NodeType comp_type, OperationCode opcode, const char *name, int name_tag)
{
return find_operation_node(id, comp_type, "", opcode, name, name_tag);
}
+OperationNode *DepsgraphNodeBuilder::find_operation_node(const OperationKey &key)
+{
+ return find_operation_node(
+ key.id, key.component_type, key.component_name, key.opcode, key.name, key.name_tag);
+}
+
ID *DepsgraphNodeBuilder::get_cow_id(const ID *id_orig) const
{
return graph_->get_cow_id(id_orig);
@@ -354,17 +392,8 @@ void DepsgraphNodeBuilder::begin_build()
id_node->id_cow = nullptr;
}
- for (OperationNode *op_node : graph_->entry_tags) {
- ComponentNode *comp_node = op_node->owner;
- IDNode *id_node = comp_node->owner;
-
- SavedEntryTag entry_tag;
- entry_tag.id_orig = id_node->id_orig;
- entry_tag.component_type = comp_node->type;
- entry_tag.opcode = op_node->opcode;
- entry_tag.name = op_node->name;
- entry_tag.name_tag = op_node->name_tag;
- saved_entry_tags_.append(entry_tag);
+ for (const OperationNode *op_node : graph_->entry_tags) {
+ saved_entry_tags_.append_as(op_node);
}
/* Make sure graph has no nodes left from previous state. */
@@ -482,23 +511,15 @@ void DepsgraphNodeBuilder::update_invalid_cow_pointers()
void DepsgraphNodeBuilder::tag_previously_tagged_nodes()
{
- for (const SavedEntryTag &entry_tag : saved_entry_tags_) {
- IDNode *id_node = find_id_node(entry_tag.id_orig);
- if (id_node == nullptr) {
- continue;
- }
- ComponentNode *comp_node = id_node->find_component(entry_tag.component_type);
- if (comp_node == nullptr) {
- continue;
- }
- OperationNode *op_node = comp_node->find_operation(
- entry_tag.opcode, entry_tag.name.c_str(), entry_tag.name_tag);
- if (op_node == nullptr) {
+ for (const OperationKey &operation_key : saved_entry_tags_) {
+ OperationNode *operation_node = find_operation_node(operation_key);
+ if (operation_node == nullptr) {
continue;
}
+
/* Since the tag is coming from a saved copy of entry tags, this means
* that originally node was explicitly tagged for user update. */
- op_node->tag_update(graph_, DEG_UPDATE_SOURCE_USER_EDIT);
+ operation_node->tag_update(graph_, DEG_UPDATE_SOURCE_USER_EDIT);
}
}
@@ -654,7 +675,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
IDNode *id_node;
if (built_map_.checkIsBuiltAndTag(collection)) {
id_node = find_id_node(&collection->id);
- if (is_collection_visible && id_node->is_directly_visible == false &&
+ if (is_collection_visible && id_node->is_visible_on_build == false &&
id_node->is_collection_fully_expanded == true) {
/* Collection became visible, make sure nested collections and
* objects are poked with the new visibility flag, since they
@@ -671,7 +692,7 @@ void DepsgraphNodeBuilder::build_collection(LayerCollection *from_layer_collecti
else {
/* Collection itself. */
id_node = add_id_node(&collection->id);
- id_node->is_directly_visible = is_collection_visible;
+ id_node->is_visible_on_build = is_collection_visible;
build_idproperties(collection->id.properties);
add_operation_node(&collection->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_DONE);
@@ -718,7 +739,7 @@ void DepsgraphNodeBuilder::build_object(int base_index,
build_object_flags(base_index, object, linked_state);
}
id_node->linked_state = max(id_node->linked_state, linked_state);
- id_node->is_directly_visible |= is_visible;
+ id_node->is_visible_on_build |= is_visible;
id_node->has_base |= (base_index != -1);
/* There is no relation path which will connect current object with all the ones which come
@@ -737,10 +758,10 @@ void DepsgraphNodeBuilder::build_object(int base_index,
* Probably need to assign that to something non-nullptr, but then the logic here will still be
* somewhat weird. */
if (scene_ != nullptr && object == scene_->camera) {
- id_node->is_directly_visible = true;
+ id_node->is_visible_on_build = true;
}
else {
- id_node->is_directly_visible = is_visible;
+ id_node->is_visible_on_build = is_visible;
}
id_node->has_base |= (base_index != -1);
/* Various flags, flushing from bases/collections. */
@@ -752,11 +773,7 @@ void DepsgraphNodeBuilder::build_object(int base_index,
build_object(-1, object->parent, DEG_ID_LINKED_INDIRECTLY, is_visible);
}
/* Modifiers. */
- if (object->modifiers.first != nullptr) {
- BuilderWalkUserData data;
- data.builder = this;
- BKE_modifiers_foreach_ID_link(object, modifier_walk, &data);
- }
+ build_object_modifiers(object);
/* Grease Pencil Modifiers. */
if (object->greasepencil_modifiers.first != nullptr) {
BuilderWalkUserData data;
@@ -860,6 +877,44 @@ void DepsgraphNodeBuilder::build_object_instance_collection(Object *object, bool
is_parent_collection_visible_ = is_current_parent_collection_visible;
}
+void DepsgraphNodeBuilder::build_object_modifiers(Object *object)
+{
+ if (BLI_listbase_is_empty(&object->modifiers)) {
+ return;
+ }
+
+ const ModifierMode modifier_mode = (graph_->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime :
+ eModifierMode_Render;
+
+ IDNode *id_node = find_id_node(&object->id);
+
+ add_operation_node(&object->id,
+ NodeType::GEOMETRY,
+ OperationCode::VISIBILITY,
+ [id_node](::Depsgraph *depsgraph) {
+ deg_evaluate_object_modifiers_mode_node_visibility(depsgraph, id_node);
+ });
+
+ LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) {
+ OperationNode *modifier_node = add_operation_node(
+ &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, nullptr, modifier->name);
+
+ /* Mute modifier mode if the modifier is not enabled for the dependency graph mode.
+ * This handles static (non-animated) mode of the modifier. */
+ if ((modifier->mode & modifier_mode) == 0) {
+ modifier_node->flag |= DEPSOP_FLAG_MUTE;
+ }
+
+ if (is_modifier_visibility_animated(object, modifier)) {
+ graph_->has_animated_visibility = true;
+ }
+ }
+
+ BuilderWalkUserData data;
+ data.builder = this;
+ BKE_modifiers_foreach_ID_link(object, modifier_walk, &data);
+}
+
void DepsgraphNodeBuilder::build_object_data(Object *object)
{
if (object->data == nullptr) {
@@ -1167,12 +1222,16 @@ void DepsgraphNodeBuilder::build_driver_id_property(ID *id, const char *rna_path
/* Custom properties of bones are placed in their components to improve granularity. */
if (RNA_struct_is_a(ptr.type, &RNA_PoseBone)) {
const bPoseChannel *pchan = static_cast<const bPoseChannel *>(ptr.data);
- ensure_operation_node(
- id, NodeType::BONE, pchan->name, OperationCode::ID_PROPERTY, nullptr, prop_identifier);
+ ensure_operation_node(ptr.owner_id,
+ NodeType::BONE,
+ pchan->name,
+ OperationCode::ID_PROPERTY,
+ nullptr,
+ prop_identifier);
}
else {
ensure_operation_node(
- id, NodeType::PARAMETERS, OperationCode::ID_PROPERTY, nullptr, prop_identifier);
+ ptr.owner_id, NodeType::PARAMETERS, OperationCode::ID_PROPERTY, nullptr, prop_identifier);
}
}
@@ -1548,8 +1607,14 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata)
break;
}
case ID_CV: {
+ Curves *curves_id = reinterpret_cast<Curves *>(obdata);
+
op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
op_node->set_as_entry();
+
+ if (curves_id->surface != nullptr) {
+ build_object(-1, curves_id->surface, DEG_ID_LINKED_INDIRECTLY, false);
+ }
break;
}
case ID_PT: {
@@ -1681,6 +1746,18 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
build_animdata(&ntree->id);
/* Output update. */
add_operation_node(&ntree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT);
+ if (ntree->type == NTREE_GEOMETRY) {
+ ID *id_cow = get_cow_id(&ntree->id);
+ add_operation_node(&ntree->id,
+ NodeType::NTREE_GEOMETRY_PREPROCESS,
+ OperationCode::NTREE_GEOMETRY_PREPROCESS,
+ [id_cow](::Depsgraph * /*depsgraph*/) {
+ bNodeTree *ntree_cow = reinterpret_cast<bNodeTree *>(id_cow);
+ bke::node_tree_runtime::preprocess_geometry_node_tree_for_evaluation(
+ *ntree_cow);
+ });
+ }
+
/* nodetree's nodes... */
LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) {
build_idproperties(bnode->prop);
@@ -2057,9 +2134,10 @@ void DepsgraphNodeBuilder::build_scene_audio(Scene *scene)
});
}
-void DepsgraphNodeBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer *view_layer)
+void DepsgraphNodeBuilder::build_scene_speakers(Scene *scene, ViewLayer *view_layer)
{
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *object = base->object;
if (object->type != OB_SPEAKER || !need_pull_base_into_graph(base)) {
continue;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index be9983edf85..a8efe8fca9f 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -8,6 +8,7 @@
#pragma once
#include "intern/builder/deg_builder.h"
+#include "intern/builder/deg_builder_key.h"
#include "intern/builder/deg_builder_map.h"
#include "intern/depsgraph_type.h"
#include "intern/node/deg_node_id.h"
@@ -50,13 +51,13 @@ struct bNodeTree;
struct bPoseChannel;
struct bSound;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct ComponentNode;
struct Depsgraph;
class DepsgraphBuilderCache;
struct IDNode;
+struct OperationKey;
struct OperationNode;
struct TimeSourceNode;
@@ -93,10 +94,11 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
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);
+ IDNode *find_id_node(const ID *id);
TimeSourceNode *add_time_source();
ComponentNode *add_component_node(ID *id, NodeType comp_type, const char *comp_name = "");
+ ComponentNode *find_component_node(const ID *id, NodeType comp_type, const char *comp_name = "");
OperationNode *add_operation_node(ComponentNode *comp_node,
OperationCode opcode,
@@ -138,15 +140,20 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
const char *name = "",
int name_tag = -1);
- OperationNode *find_operation_node(ID *id,
+ OperationNode *find_operation_node(const ID *id,
NodeType comp_type,
const char *comp_name,
OperationCode opcode,
const char *name = "",
int name_tag = -1);
- OperationNode *find_operation_node(
- ID *id, NodeType comp_type, OperationCode opcode, const char *name = "", int name_tag = -1);
+ OperationNode *find_operation_node(const ID *id,
+ NodeType comp_type,
+ OperationCode opcode,
+ const char *name = "",
+ int name_tag = -1);
+
+ OperationNode *find_operation_node(const OperationKey &key);
virtual void build_id(ID *id);
@@ -175,6 +182,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_object_flags(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state);
+ virtual void build_object_modifiers(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
virtual void build_object_data_geometry(Object *object);
@@ -255,17 +263,9 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
};
protected:
- /* Allows to identify an operation which was tagged for update at the time
- * relations are being updated. We can not reuse operation node pointer
- * since it will change during dependency graph construction. */
- struct SavedEntryTag {
- ID *id_orig;
- NodeType component_type;
- OperationCode opcode;
- string name;
- int name_tag;
- };
- Vector<SavedEntryTag> saved_entry_tags_;
+ /* Entry tags from the previous state of the dependency graph.
+ * Stored before the graph is re-created so that they can be transferred over. */
+ Vector<PersistentOperationKey> saved_entry_tags_;
struct BuilderWalkUserData {
DepsgraphNodeBuilder *builder;
@@ -306,5 +306,4 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
BuilderMap built_map_;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
index 32ec94211a0..d7420b91db4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes_view_layer.cc
@@ -90,16 +90,23 @@ void DepsgraphNodeBuilder::build_view_layer(Scene *scene,
* but object is expected to be an original one. Hence we go into some
* tricks here iterating over the view layer. */
int base_index = 0;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
/* object itself */
- if (need_pull_base_into_graph(base)) {
- /* NOTE: We consider object visible even if it's currently
- * restricted by the base/restriction flags. Otherwise its drivers
- * will never be evaluated.
- *
- * TODO(sergey): Need to go more granular on visibility checks. */
- build_object(base_index, base->object, linked_state, true);
- base_index++;
+ if (!need_pull_base_into_graph(base)) {
+ continue;
+ }
+
+ /* NOTE: We consider object visible even if it's currently
+ * restricted by the base/restriction flags. Otherwise its drivers
+ * will never be evaluated.
+ *
+ * TODO(sergey): Need to go more granular on visibility checks. */
+ build_object(base_index, base->object, linked_state, true);
+ base_index++;
+
+ if (!graph_->has_animated_visibility) {
+ graph_->has_animated_visibility |= is_object_visibility_animated(base->object);
}
}
build_layer_collections(&view_layer->layer_collections);
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
index 2f98c8b419c..afb3a3c22bd 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_pchanmap.h
@@ -9,8 +9,7 @@
#include "intern/depsgraph_type.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct RootPChanMap {
/** Debug contents of map. */
@@ -30,5 +29,4 @@ struct RootPChanMap {
Map<StringRefNull, Set<StringRefNull>> map_;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 31d5308e825..e5b03bd5291 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -27,6 +27,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
+#include "DNA_curves_types.h"
#include "DNA_effect_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_key_types.h"
@@ -238,13 +239,8 @@ DepsgraphRelationBuilder::DepsgraphRelationBuilder(Main *bmain,
{
}
-TimeSourceNode *DepsgraphRelationBuilder::get_node(const TimeSourceKey &key) const
+TimeSourceNode *DepsgraphRelationBuilder::get_node(const TimeSourceKey & /*key*/) const
{
- if (key.id) {
- /* XXX TODO */
- return nullptr;
- }
-
return graph_->time_source;
}
@@ -297,12 +293,13 @@ bool DepsgraphRelationBuilder::has_node(const OperationKey &key) const
return find_node(key) != nullptr;
}
-void DepsgraphRelationBuilder::add_modifier_to_transform_relation(const DepsNodeHandle *handle,
- const char *description)
+void DepsgraphRelationBuilder::add_depends_on_transform_relation(const DepsNodeHandle *handle,
+ const char *description)
{
IDNode *id_node = handle->node->owner->owner;
ID *id = id_node->id_orig;
- ComponentKey geometry_key(id, NodeType::GEOMETRY);
+ const OperationKey geometry_key(
+ id, NodeType::GEOMETRY, OperationCode::MODIFIER, handle->node->name.c_str());
/* Wire up the actual relation. */
add_depends_on_transform_relation(id, geometry_key, description);
}
@@ -692,7 +689,7 @@ void DepsgraphRelationBuilder::build_object(Object *object)
const BuilderStack::ScopedEntry stack_entry = stack_.trace(object->id);
- /* Object Transforms */
+ /* Object Transforms. */
OperationCode base_op = (object->parent) ? OperationCode::TRANSFORM_PARENT :
OperationCode::TRANSFORM_LOCAL;
OperationKey base_op_key(&object->id, NodeType::TRANSFORM, base_op);
@@ -705,9 +702,12 @@ void DepsgraphRelationBuilder::build_object(Object *object)
OperationKey final_transform_key(
&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL);
OperationKey ob_eval_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
+
add_relation(init_transform_key, local_transform_key, "Transform Init");
+
/* Various flags, flushing from bases/collections. */
build_object_layer_component_relations(object);
+
/* Parenting. */
if (object->parent != nullptr) {
/* Make sure parent object's relations are built. */
@@ -717,30 +717,31 @@ void DepsgraphRelationBuilder::build_object(Object *object)
/* Local -> parent. */
add_relation(local_transform_key, parent_transform_key, "ObLocal -> ObParent");
}
+
/* Modifiers. */
- if (object->modifiers.first != nullptr) {
- BuilderWalkUserData data;
- data.builder = this;
- BKE_modifiers_foreach_ID_link(object, modifier_walk, &data);
- }
+ build_object_modifiers(object);
+
/* Grease Pencil Modifiers. */
if (object->greasepencil_modifiers.first != nullptr) {
BuilderWalkUserData data;
data.builder = this;
BKE_gpencil_modifiers_foreach_ID_link(object, modifier_walk, &data);
}
+
/* Shader FX. */
if (object->shader_fx.first != nullptr) {
BuilderWalkUserData data;
data.builder = this;
BKE_shaderfx_foreach_ID_link(object, modifier_walk, &data);
}
+
/* Constraints. */
if (object->constraints.first != nullptr) {
BuilderWalkUserData data;
data.builder = this;
BKE_constraints_id_loop(&object->constraints, constraint_walk, &data);
}
+
/* Object constraints. */
OperationKey object_transform_simulation_init_key(
&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_SIMULATION_INIT);
@@ -767,32 +768,48 @@ void DepsgraphRelationBuilder::build_object(Object *object)
final_transform_key,
"Simulation -> Final Transform");
}
+
build_idproperties(object->id.properties);
+
/* Animation data */
build_animdata(&object->id);
+
/* Object data. */
build_object_data(object);
+
/* Particle systems. */
if (object->particlesystem.first != nullptr) {
build_particle_systems(object);
}
+
/* Force field Texture. */
if ((object->pd != nullptr) && (object->pd->forcefield == PFIELD_TEXTURE) &&
(object->pd->tex != nullptr)) {
build_texture(object->pd->tex);
}
+
/* Object dupligroup. */
if (object->instance_collection != nullptr) {
build_collection(nullptr, object, object->instance_collection);
}
+
/* Point caches. */
build_object_pointcache(object);
+
/* Synchronization back to original object. */
OperationKey synchronize_key(
&object->id, NodeType::SYNCHRONIZATION, OperationCode::SYNCHRONIZE_TO_ORIGINAL);
add_relation(final_transform_key, synchronize_key, "Synchronize to Original");
+
/* Parameters. */
build_parameters(&object->id);
+
+ /* Visibility.
+ * Evaluate visibility node after the object's base_flags has been updated to the current state
+ * of collections restrict and object's restrict flags. */
+ const ComponentKey object_from_layer_entry_key(&object->id, NodeType::OBJECT_FROM_LAYER);
+ const ComponentKey visibility_key(&object->id, NodeType::VISIBILITY);
+ add_relation(object_from_layer_entry_key, visibility_key, "Object Visibility");
}
/* NOTE: Implies that the object has base in the current view layer. */
@@ -850,6 +867,63 @@ void DepsgraphRelationBuilder::build_object_layer_component_relations(Object *ob
add_relation(object_from_layer_exit_key, synchronize_key, "Synchronize to Original");
}
+void DepsgraphRelationBuilder::build_object_modifiers(Object *object)
+{
+ if (BLI_listbase_is_empty(&object->modifiers)) {
+ return;
+ }
+
+ const OperationKey eval_init_key(
+ &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT);
+ const OperationKey eval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+
+ const ComponentKey object_visibility_key(&object->id, NodeType::VISIBILITY);
+ const OperationKey modifier_visibility_key(
+ &object->id, NodeType::GEOMETRY, OperationCode::VISIBILITY);
+ add_relation(modifier_visibility_key,
+ object_visibility_key,
+ "modifier -> object visibility",
+ RELATION_NO_VISIBILITY_CHANGE);
+
+ add_relation(modifier_visibility_key, eval_key, "modifier visibility -> geometry eval");
+
+ ModifierUpdateDepsgraphContext ctx = {};
+ ctx.scene = scene_;
+ ctx.object = object;
+
+ OperationKey previous_key = eval_init_key;
+ LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) {
+ const OperationKey modifier_key(
+ &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, modifier->name);
+
+ /* Relation for the modifier stack chain. */
+ add_relation(previous_key, modifier_key, "Modifier");
+
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)modifier->type);
+ if (mti->updateDepsgraph) {
+ const BuilderStack::ScopedEntry stack_entry = stack_.trace(*modifier);
+
+ DepsNodeHandle handle = create_node_handle(modifier_key);
+ ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
+ mti->updateDepsgraph(modifier, &ctx);
+ }
+
+ /* Time dependency. */
+ if (BKE_modifier_depends_ontime(scene_, modifier)) {
+ const TimeSourceKey time_src_key;
+ add_relation(time_src_key, modifier_key, "Time Source -> Modifier");
+ }
+
+ previous_key = modifier_key;
+ }
+ add_relation(previous_key, eval_key, "modifier stack order");
+
+ /* Build IDs referenced by the modifiers. */
+ BuilderWalkUserData data;
+ data.builder = this;
+ BKE_modifiers_foreach_ID_link(object, modifier_walk, &data);
+}
+
void DepsgraphRelationBuilder::build_object_data(Object *object)
{
if (object->data == nullptr) {
@@ -1649,6 +1723,11 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu)
if (GS(id_ptr->name) == ID_NT) {
ComponentKey ntree_output_key(id_ptr, NodeType::NTREE_OUTPUT);
add_relation(driver_key, ntree_output_key, "Drivers -> NTree Output");
+ if (reinterpret_cast<bNodeTree *>(id_ptr)->type == NTREE_GEOMETRY) {
+ OperationKey ntree_geo_preprocess_key(
+ id, NodeType::NTREE_GEOMETRY_PREPROCESS, OperationCode::NTREE_GEOMETRY_PREPROCESS);
+ add_relation(driver_key, ntree_geo_preprocess_key, "Drivers -> NTree Geo Preprocess");
+ }
}
}
@@ -1791,18 +1870,20 @@ void DepsgraphRelationBuilder::build_driver_id_property(ID *id, const char *rna_
if (RNA_struct_is_a(ptr.type, &RNA_PoseBone)) {
const bPoseChannel *pchan = static_cast<const bPoseChannel *>(ptr.data);
id_property_key = OperationKey(
- id, NodeType::BONE, pchan->name, OperationCode::ID_PROPERTY, prop_identifier);
+ ptr.owner_id, NodeType::BONE, pchan->name, OperationCode::ID_PROPERTY, prop_identifier);
/* Create relation from the parameters component so that tagging armature for parameters update
* properly propagates updates to all properties on bones and deeper (if needed). */
- OperationKey parameters_init_key(id, NodeType::PARAMETERS, OperationCode::PARAMETERS_ENTRY);
+ OperationKey parameters_init_key(
+ ptr.owner_id, NodeType::PARAMETERS, OperationCode::PARAMETERS_ENTRY);
add_relation(
parameters_init_key, id_property_key, "Init -> ID Property", RELATION_CHECK_BEFORE_ADD);
}
else {
id_property_key = OperationKey(
- id, NodeType::PARAMETERS, OperationCode::ID_PROPERTY, prop_identifier);
+ ptr.owner_id, NodeType::PARAMETERS, OperationCode::ID_PROPERTY, prop_identifier);
}
- OperationKey parameters_exit_key(id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT);
+ OperationKey parameters_exit_key(
+ ptr.owner_id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT);
add_relation(
id_property_key, parameters_exit_key, "ID Property -> Done", RELATION_CHECK_BEFORE_ADD);
}
@@ -1952,7 +2033,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene)
void DepsgraphRelationBuilder::build_particle_systems(Object *object)
{
- TimeSourceKey time_src_key;
OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
OperationKey eval_init_key(
&object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_INIT);
@@ -2142,7 +2222,7 @@ void DepsgraphRelationBuilder::build_shapekeys(Key *key)
* ==========================
*
* The evaluation of geometry on objects is as follows:
- * - The actual evaluated of the derived geometry (e.g. Mesh, DispList)
+ * - The actual evaluated of the derived geometry (e.g. #Mesh, #Curves, etc.)
* occurs in the Geometry component of the object which references this.
* This includes modifiers, and the temporary "ubereval" for geometry.
* Therefore, each user of a piece of shared geometry data ends up evaluating
@@ -2173,28 +2253,7 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
* data mask to be used. We add relation here to ensure object is never
* evaluated prior to Scene's CoW is ready. */
OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL);
- Relation *rel = add_relation(scene_key, obdata_ubereval_key, "CoW Relation");
- rel->flag |= RELATION_FLAG_NO_FLUSH;
- /* Modifiers */
- if (object->modifiers.first != nullptr) {
- ModifierUpdateDepsgraphContext ctx = {};
- ctx.scene = scene_;
- ctx.object = object;
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
- if (mti->updateDepsgraph) {
- const BuilderStack::ScopedEntry stack_entry = stack_.trace(*md);
-
- DepsNodeHandle handle = create_node_handle(obdata_ubereval_key);
- ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle);
- mti->updateDepsgraph(md, &ctx);
- }
- if (BKE_modifier_depends_ontime(scene_, md)) {
- TimeSourceKey time_src_key;
- add_relation(time_src_key, obdata_ubereval_key, "Time Source -> Modifier");
- }
- }
- }
+ add_relation(scene_key, obdata_ubereval_key, "CoW Relation", RELATION_FLAG_NO_FLUSH);
/* Grease Pencil Modifiers. */
if (object->greasepencil_modifiers.first != nullptr) {
ModifierUpdateDepsgraphContext ctx = {};
@@ -2238,9 +2297,9 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object)
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_LATTICE)) {
// add geometry collider relations
}
- /* Make sure uber update is the last in the dependencies. */
- if (object->type != OB_ARMATURE) {
- /* Armatures does no longer require uber node. */
+ /* Make sure uber update is the last in the dependencies.
+ * Only do it here unless there are modifiers. This avoids transitive relations. */
+ if (BLI_listbase_is_empty(&object->modifiers)) {
OperationKey obdata_ubereval_key(
&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
add_relation(geom_init_key, obdata_ubereval_key, "Object Geometry UberEval");
@@ -2402,8 +2461,16 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
}
break;
}
- case ID_CV:
+ case ID_CV: {
+ Curves *curves_id = reinterpret_cast<Curves *>(obdata);
+ if (curves_id->surface != nullptr) {
+ build_object(curves_id->surface);
+
+ /* The relations between the surface and the curves are handled as part of the modifier
+ * stack building. */
+ }
break;
+ }
case ID_PT:
break;
case ID_VO: {
@@ -2459,6 +2526,13 @@ void DepsgraphRelationBuilder::build_camera(Camera *camera)
ComponentKey camera_parameters_key(&camera->id, NodeType::PARAMETERS);
ComponentKey dof_ob_key(&camera->dof.focus_object->id, NodeType::TRANSFORM);
add_relation(dof_ob_key, camera_parameters_key, "Camera DOF");
+ if (camera->dof.focus_subtarget[0]) {
+ OperationKey target_key(&camera->dof.focus_object->id,
+ NodeType::BONE,
+ camera->dof.focus_subtarget,
+ OperationCode::BONE_DONE);
+ add_relation(target_key, camera_parameters_key, "Camera DOF subtarget");
+ }
}
}
@@ -2542,6 +2616,16 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
build_animdata(&ntree->id);
build_parameters(&ntree->id);
OperationKey ntree_output_key(&ntree->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT);
+ OperationKey ntree_geo_preprocess_key(
+ &ntree->id, NodeType::NTREE_GEOMETRY_PREPROCESS, OperationCode::NTREE_GEOMETRY_PREPROCESS);
+ if (ntree->type == NTREE_GEOMETRY) {
+ OperationKey ntree_cow_key(&ntree->id, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE);
+ add_relation(ntree_cow_key, ntree_geo_preprocess_key, "COW -> Preprocess");
+ add_relation(ntree_geo_preprocess_key,
+ ntree_output_key,
+ "Preprocess -> Output",
+ RELATION_FLAG_NO_FLUSH);
+ }
/* nodetree's nodes... */
LISTBASE_FOREACH (bNode *, bnode, &ntree->nodes) {
build_idproperties(bnode->prop);
@@ -2618,6 +2702,12 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
* the output). Currently, we lack the infrastructure to check for these cases efficiently.
* That can be added later. */
add_relation(group_output_key, ntree_output_key, "Group Node");
+ if (group_ntree->type == NTREE_GEOMETRY) {
+ OperationKey group_preprocess_key(&group_ntree->id,
+ NodeType::NTREE_GEOMETRY_PREPROCESS,
+ OperationCode::NTREE_GEOMETRY_PREPROCESS);
+ add_relation(group_preprocess_key, ntree_geo_preprocess_key, "Group Node Preprocess");
+ }
}
else {
BLI_assert_msg(0, "Unknown ID type used for node");
@@ -2634,6 +2724,9 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
if (check_id_has_anim_component(&ntree->id)) {
ComponentKey animation_key(&ntree->id, NodeType::ANIMATION);
add_relation(animation_key, ntree_output_key, "NTree Shading Parameters");
+ if (ntree->type == NTREE_GEOMETRY) {
+ add_relation(animation_key, ntree_geo_preprocess_key, "NTree Animation -> Preprocess");
+ }
}
}
@@ -2981,9 +3074,10 @@ void DepsgraphRelationBuilder::build_scene_audio(Scene *scene)
}
}
-void DepsgraphRelationBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer *view_layer)
+void DepsgraphRelationBuilder::build_scene_speakers(Scene *scene, ViewLayer *view_layer)
{
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *object = base->object;
if (object->type != OB_SPEAKER || !need_pull_base_into_graph(base)) {
continue;
@@ -3058,7 +3152,6 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node)
return;
}
- TimeSourceKey time_source_key;
OperationKey copy_on_write_key(id_orig, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE);
/* XXX: This is a quick hack to make Alt-A to work. */
// add_relation(time_source_key, copy_on_write_key, "Fluxgate capacitor hack");
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index 64bdd2334d8..901e4a1acbb 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -14,13 +14,13 @@
#include "DNA_ID.h"
-#include "RNA_access.h"
-#include "RNA_types.h"
+#include "RNA_path.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "intern/builder/deg_builder.h"
+#include "intern/builder/deg_builder_key.h"
#include "intern/builder/deg_builder_map.h"
#include "intern/builder/deg_builder_rna.h"
#include "intern/builder/deg_builder_stack.h"
@@ -68,10 +68,7 @@ struct bNodeTree;
struct bPoseChannel;
struct bSound;
-struct PropertyRNA;
-
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct ComponentNode;
struct DepsNodeHandle;
@@ -84,66 +81,6 @@ struct Relation;
struct RootPChanMap;
struct TimeSourceNode;
-struct TimeSourceKey {
- TimeSourceKey();
- TimeSourceKey(ID *id);
-
- string identifier() const;
-
- ID *id;
-};
-
-struct ComponentKey {
- ComponentKey();
- ComponentKey(ID *id, NodeType type, const char *name = "");
-
- string identifier() const;
-
- ID *id;
- NodeType type;
- const char *name;
-};
-
-struct OperationKey {
- OperationKey();
- OperationKey(ID *id, NodeType component_type, const char *name, int name_tag = -1);
- OperationKey(
- ID *id, NodeType component_type, const char *component_name, const char *name, int name_tag);
-
- OperationKey(ID *id, NodeType component_type, OperationCode opcode);
- OperationKey(ID *id, NodeType component_type, const char *component_name, OperationCode opcode);
-
- OperationKey(
- ID *id, NodeType component_type, OperationCode opcode, const char *name, int name_tag = -1);
- OperationKey(ID *id,
- NodeType component_type,
- const char *component_name,
- OperationCode opcode,
- const char *name,
- int name_tag = -1);
-
- string identifier() const;
-
- ID *id;
- NodeType component_type;
- const char *component_name;
- OperationCode opcode;
- const char *name;
- int name_tag;
-};
-
-struct RNAPathKey {
- RNAPathKey(ID *id, const char *path, RNAPointerSource source);
- RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop, RNAPointerSource source);
-
- string identifier() const;
-
- ID *id;
- PointerRNA ptr;
- PropertyRNA *prop;
- RNAPointerSource source;
-};
-
class DepsgraphRelationBuilder : public DepsgraphBuilder {
public:
DepsgraphRelationBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache);
@@ -177,7 +114,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
/* Adds relation from proper transformation operation to the modifier.
* Takes care of checking for possible physics solvers modifying position
* of this object. */
- void add_modifier_to_transform_relation(const DepsNodeHandle *handle, const char *description);
+ void add_depends_on_transform_relation(const DepsNodeHandle *handle, const char *description);
void add_customdata_mask(Object *object, const DEGCustomDataMeshMasks &customdata_masks);
void add_special_eval_flag(ID *id, uint32_t flag);
@@ -203,6 +140,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_object(Object *object);
virtual void build_object_from_view_layer_base(Object *object);
virtual void build_object_layer_component_relations(Object *object);
+ virtual void build_object_modifiers(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
virtual void build_object_data_geometry(Object *object);
@@ -381,7 +319,6 @@ struct DepsNodeHandle {
const char *default_name;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
#include "intern/builder/deg_builder_relations_impl.h"
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h
index e517dd6a927..9860b17fd56 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_drivers.h
@@ -15,8 +15,7 @@
struct FCurve;
-namespace blender {
-namespace deg {
+namespace blender::deg {
/* Helper class for determining which relations are needed between driver evaluation nodes. */
class DriverDescriptor {
@@ -59,5 +58,4 @@ class DriverDescriptor {
bool resolve_rna();
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h
index aba4a011e72..76066ce97a7 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_impl.h
@@ -15,8 +15,7 @@
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
template<typename KeyType>
OperationNode *DepsgraphRelationBuilder::find_operation_node(const KeyType &key)
@@ -209,5 +208,4 @@ bool DepsgraphRelationBuilder::is_same_nodetree_node_dependency(const KeyFrom &k
return true;
}
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc
deleted file mode 100644
index eeaab623482..00000000000
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc
+++ /dev/null
@@ -1,179 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2013 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup depsgraph
- *
- * Methods for constructing depsgraph
- */
-
-#include "intern/builder/deg_builder_relations.h"
-
-namespace blender::deg {
-
-////////////////////////////////////////////////////////////////////////////////
-/* Time source. */
-
-TimeSourceKey::TimeSourceKey() : id(nullptr)
-{
-}
-
-TimeSourceKey::TimeSourceKey(ID *id) : id(id)
-{
-}
-
-string TimeSourceKey::identifier() const
-{
- return string("TimeSourceKey");
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Component.
-
-ComponentKey::ComponentKey() : id(nullptr), type(NodeType::UNDEFINED), name("")
-{
-}
-
-ComponentKey::ComponentKey(ID *id, NodeType type, const char *name)
- : id(id), type(type), name(name)
-{
-}
-
-string ComponentKey::identifier() const
-{
- const char *idname = (id) ? id->name : "<None>";
- string result = string("ComponentKey(");
- result += idname;
- result += ", " + string(nodeTypeAsString(type));
- if (name[0] != '\0') {
- result += ", '" + string(name) + "'";
- }
- result += ')';
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Operation.
-
-OperationKey::OperationKey()
- : id(nullptr),
- component_type(NodeType::UNDEFINED),
- component_name(""),
- opcode(OperationCode::OPERATION),
- name(""),
- name_tag(-1)
-{
-}
-
-OperationKey::OperationKey(ID *id, NodeType component_type, const char *name, int name_tag)
- : id(id),
- component_type(component_type),
- component_name(""),
- opcode(OperationCode::OPERATION),
- name(name),
- name_tag(name_tag)
-{
-}
-
-OperationKey::OperationKey(
- ID *id, NodeType component_type, const char *component_name, const char *name, int name_tag)
- : id(id),
- component_type(component_type),
- component_name(component_name),
- opcode(OperationCode::OPERATION),
- name(name),
- name_tag(name_tag)
-{
-}
-
-OperationKey::OperationKey(ID *id, NodeType component_type, OperationCode opcode)
- : id(id),
- component_type(component_type),
- component_name(""),
- opcode(opcode),
- name(""),
- name_tag(-1)
-{
-}
-
-OperationKey::OperationKey(ID *id,
- NodeType component_type,
- const char *component_name,
- OperationCode opcode)
- : id(id),
- component_type(component_type),
- component_name(component_name),
- opcode(opcode),
- name(""),
- name_tag(-1)
-{
-}
-
-OperationKey::OperationKey(
- ID *id, NodeType component_type, OperationCode opcode, const char *name, int name_tag)
- : id(id),
- component_type(component_type),
- component_name(""),
- opcode(opcode),
- name(name),
- name_tag(name_tag)
-{
-}
-
-OperationKey::OperationKey(ID *id,
- NodeType component_type,
- const char *component_name,
- OperationCode opcode,
- const char *name,
- int name_tag)
- : id(id),
- component_type(component_type),
- component_name(component_name),
- opcode(opcode),
- name(name),
- name_tag(name_tag)
-{
-}
-
-string OperationKey::identifier() const
-{
- string result = string("OperationKey(");
- result += "type: " + string(nodeTypeAsString(component_type));
- result += ", component name: '" + string(component_name) + "'";
- result += ", operation code: " + string(operationCodeAsString(opcode));
- if (name[0] != '\0') {
- result += ", '" + string(name) + "'";
- }
- result += ")";
- return result;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// RNA path.
-
-RNAPathKey::RNAPathKey(ID *id, const char *path, RNAPointerSource source) : id(id), source(source)
-{
- /* Create ID pointer for root of path lookup. */
- PointerRNA id_ptr;
- RNA_id_pointer_create(id, &id_ptr);
- /* Try to resolve path. */
- int index;
- if (!RNA_path_resolve_full(&id_ptr, path, &ptr, &prop, &index)) {
- ptr = PointerRNA_NULL;
- prop = nullptr;
- }
-}
-
-RNAPathKey::RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop, RNAPointerSource source)
- : id(id), ptr(ptr), prop(prop), source(source)
-{
-}
-
-string RNAPathKey::identifier() const
-{
- const char *id_name = (id) ? id->name : "<No ID>";
- const char *prop_name = (prop) ? RNA_property_identifier(prop) : "<No Prop>";
- return string("RnaPathKey(") + "id: " + id_name + ", prop: '" + prop_name + "')";
-}
-
-} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
index d723e5beb75..938c0979de9 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_view_layer.cc
@@ -75,11 +75,12 @@ void DepsgraphRelationBuilder::build_view_layer(Scene *scene,
{
/* Setup currently building context. */
scene_ = scene;
+ BKE_view_layer_synced_ensure(scene, view_layer);
/* Scene objects. */
/* NOTE: Nodes builder requires us to pass CoW base because it's being
* passed to the evaluation functions. During relations builder we only
* do nullptr-pointer check of the base, so it's fine to pass original one. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (need_pull_base_into_graph(base)) {
build_object_from_view_layer_base(base->object);
}
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h
index 922d2d7dc05..ad10eb7cd10 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_remove_noop.h
@@ -7,13 +7,11 @@
#pragma once
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
/* Remove all no-op nodes that have zero outgoing relations. */
void deg_graph_remove_unused_noops(Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
index 5202ada5408..d94746fb7fa 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc
@@ -225,6 +225,10 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
}
return node_identifier;
}
+
+ const char *prop_identifier = prop != nullptr ? RNA_property_identifier((PropertyRNA *)prop) :
+ "";
+
if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) {
const Object *object = reinterpret_cast<const Object *>(ptr->owner_id);
const bConstraint *constraint = static_cast<const bConstraint *>(ptr->data);
@@ -264,6 +268,13 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
return node_identifier;
}
}
+ else if (RNA_struct_is_a(ptr->type, &RNA_Modifier) &&
+ (contains(prop_identifier, "show_viewport") ||
+ contains(prop_identifier, "show_render"))) {
+ node_identifier.type = NodeType::GEOMETRY;
+ node_identifier.operation_code = OperationCode::VISIBILITY;
+ return node_identifier;
+ }
else if (RNA_struct_is_a(ptr->type, &RNA_Mesh) || RNA_struct_is_a(ptr->type, &RNA_Modifier) ||
RNA_struct_is_a(ptr->type, &RNA_GpencilModifier) ||
RNA_struct_is_a(ptr->type, &RNA_Spline) || RNA_struct_is_a(ptr->type, &RNA_TextBox) ||
@@ -290,7 +301,6 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
else if (ptr->type == &RNA_Object) {
/* Transforms props? */
if (prop != nullptr) {
- const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop);
/* TODO(sergey): How to optimize this? */
if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") ||
contains(prop_identifier, "matrix_channel") ||
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.h b/source/blender/depsgraph/intern/builder/deg_builder_rna.h
index 4f482d4352d..9baa956bd80 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_rna.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.h
@@ -14,8 +14,7 @@ struct ID;
struct PointerRNA;
struct PropertyRNA;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
struct Node;
@@ -94,5 +93,4 @@ class RNANodeQuery {
bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_transitive.h b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h
index 8b208610203..63016431eec 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_transitive.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_transitive.h
@@ -7,13 +7,11 @@
#pragma once
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
/* Performs a transitive reduction to remove redundant relations. */
void deg_graph_transitive_reduction(Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/pipeline.cc b/source/blender/depsgraph/intern/builder/pipeline.cc
index 540b8e173f1..815a06d03d1 100644
--- a/source/blender/depsgraph/intern/builder/pipeline.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline.cc
@@ -90,7 +90,7 @@ void AbstractBuilderPipeline::build_step_finalize()
}
#endif
/* Relations are up to date. */
- deg_graph_->need_update = false;
+ deg_graph_->need_update_relations = false;
}
unique_ptr<DepsgraphNodeBuilder> AbstractBuilderPipeline::construct_node_builder()
diff --git a/source/blender/depsgraph/intern/builder/pipeline.h b/source/blender/depsgraph/intern/builder/pipeline.h
index b106e73b97c..7568aa78106 100644
--- a/source/blender/depsgraph/intern/builder/pipeline.h
+++ b/source/blender/depsgraph/intern/builder/pipeline.h
@@ -16,8 +16,7 @@ struct Main;
struct Scene;
struct ViewLayer;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
class DepsgraphNodeBuilder;
@@ -57,5 +56,4 @@ class AbstractBuilderPipeline {
virtual void build_relations(DepsgraphRelationBuilder &relation_builder) = 0;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc
index 74d151c65d7..6bc3b59a9d6 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.cc
@@ -20,7 +20,7 @@ class AllObjectsNodeBuilder : public DepsgraphNodeBuilder {
{
}
- bool need_pull_base_into_graph(Base * /*base*/) override
+ bool need_pull_base_into_graph(const Base * /*base*/) override
{
return true;
}
@@ -33,7 +33,7 @@ class AllObjectsRelationBuilder : public DepsgraphRelationBuilder {
{
}
- bool need_pull_base_into_graph(Base * /*base*/) override
+ bool need_pull_base_into_graph(const Base * /*base*/) override
{
return true;
}
diff --git a/source/blender/depsgraph/intern/builder/pipeline_all_objects.h b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h
index 75d9605dec7..fde5e7e2163 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_all_objects.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_all_objects.h
@@ -9,8 +9,7 @@
#include "pipeline_view_layer.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
/* Builds a dependency graph that contains all objects in the view layer.
* This is contrary to the regular ViewLayerBuilderPipeline, which is limited to visible objects
@@ -24,5 +23,4 @@ class AllObjectsBuilderPipeline : public ViewLayerBuilderPipeline {
virtual unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/pipeline_compositor.h b/source/blender/depsgraph/intern/builder/pipeline_compositor.h
index 3325741c94a..304f2d4ec9a 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_compositor.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_compositor.h
@@ -11,8 +11,7 @@
struct bNodeTree;
-namespace blender {
-namespace deg {
+namespace blender::deg {
class CompositorBuilderPipeline : public AbstractBuilderPipeline {
public:
@@ -26,5 +25,4 @@ class CompositorBuilderPipeline : public AbstractBuilderPipeline {
bNodeTree *nodetree_;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
index ee10b28a306..e256c8271f2 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
+++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.cc
@@ -39,7 +39,7 @@ class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder {
{
}
- bool need_pull_base_into_graph(Base *base) override
+ bool need_pull_base_into_graph(const Base *base) override
{
if (!filter_.contains(&base->object->id)) {
return false;
@@ -61,7 +61,7 @@ class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder {
{
}
- bool need_pull_base_into_graph(Base *base) override
+ bool need_pull_base_into_graph(const Base *base) override
{
if (!filter_.contains(&base->object->id)) {
return false;
diff --git a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
index a2c75c048cb..c277d44aaad 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_from_ids.h
@@ -9,8 +9,7 @@
#include "pipeline.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
/* Optimized builders for dependency graph built from a given set of IDs.
*
@@ -37,5 +36,4 @@ class FromIDsBuilderPipeline : public AbstractBuilderPipeline {
Span<ID *> ids_;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/pipeline_render.h b/source/blender/depsgraph/intern/builder/pipeline_render.h
index cb704f84c59..7eb65168ea6 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_render.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_render.h
@@ -9,8 +9,7 @@
#include "pipeline.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
class RenderBuilderPipeline : public AbstractBuilderPipeline {
public:
@@ -21,5 +20,4 @@ class RenderBuilderPipeline : public AbstractBuilderPipeline {
virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h
index 18b9ce5d5ff..ffd6fa07776 100644
--- a/source/blender/depsgraph/intern/builder/pipeline_view_layer.h
+++ b/source/blender/depsgraph/intern/builder/pipeline_view_layer.h
@@ -9,8 +9,7 @@
#include "pipeline.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
class ViewLayerBuilderPipeline : public AbstractBuilderPipeline {
public:
@@ -21,5 +20,4 @@ class ViewLayerBuilderPipeline : public AbstractBuilderPipeline {
virtual void build_relations(DepsgraphRelationBuilder &relation_builder) override;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/debug/deg_debug.h b/source/blender/depsgraph/intern/debug/deg_debug.h
index ee6c5b046b7..8f18bd70d36 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug.h
+++ b/source/blender/depsgraph/intern/debug/deg_debug.h
@@ -14,8 +14,7 @@
#include "DEG_depsgraph_debug.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
class DepsgraphDebug {
public:
@@ -74,5 +73,4 @@ bool terminal_do_color(void);
string color_for_pointer(const void *pointer);
string color_end(void);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
index b8d0fb6c365..bd47cb77f56 100644
--- a/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
+++ b/source/blender/depsgraph/intern/debug/deg_debug_relations_graphviz.cc
@@ -408,6 +408,7 @@ static void deg_debug_graphviz_node(DotExportContext &ctx,
case NodeType::GENERIC_DATABLOCK:
case NodeType::VISIBILITY:
case NodeType::NTREE_OUTPUT:
+ case NodeType::NTREE_GEOMETRY_PREPROCESS:
case NodeType::SIMULATION: {
ComponentNode *comp_node = (ComponentNode *)node;
if (comp_node->operations.is_empty()) {
diff --git a/source/blender/depsgraph/intern/debug/deg_time_average.h b/source/blender/depsgraph/intern/debug/deg_time_average.h
index cc47f03b221..d4eb05a9ddb 100644
--- a/source/blender/depsgraph/intern/debug/deg_time_average.h
+++ b/source/blender/depsgraph/intern/debug/deg_time_average.h
@@ -7,8 +7,7 @@
#pragma once
-namespace blender {
-namespace deg {
+namespace blender::deg {
/* Utility class which takes care of calculating average of time series, such as FPS counters. */
template<int MaxSamples> class AveragedTimeSampler {
@@ -52,5 +51,4 @@ template<int MaxSamples> class AveragedTimeSampler {
int next_sample_index_;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc
index 4514084059a..316d0b615c6 100644
--- a/source/blender/depsgraph/intern/depsgraph.cc
+++ b/source/blender/depsgraph/intern/depsgraph.cc
@@ -45,9 +45,11 @@ namespace blender::deg {
Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
: time_source(nullptr),
- need_update(true),
- need_visibility_update(true),
- need_visibility_time_update(false),
+ has_animated_visibility(false),
+ need_update_relations(true),
+ need_update_nodes_visibility(true),
+ need_tag_id_on_graph_visibility_update(true),
+ need_tag_id_on_graph_visibility_time_update(false),
bmain(bmain),
scene(scene),
view_layer(view_layer),
diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h
index ca4ce058904..2f88199384d 100644
--- a/source/blender/depsgraph/intern/depsgraph.h
+++ b/source/blender/depsgraph/intern/depsgraph.h
@@ -31,8 +31,7 @@ struct ID;
struct Scene;
struct ViewLayer;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct IDNode;
struct Node;
@@ -89,13 +88,19 @@ struct Depsgraph {
/* Top-level time source node. */
TimeSourceNode *time_source;
+ /* The graph contains data-blocks whose visibility depends on evaluation (driven or animated). */
+ bool has_animated_visibility;
+
/* Indicates whether relations needs to be updated. */
- bool need_update;
+ bool need_update_relations;
+
+ /* Indicates whether indirect effect of nodes on a directly visible ones needs to be updated. */
+ bool need_update_nodes_visibility;
/* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with
* an optional tag for their animation (time) update. */
- bool need_visibility_update;
- bool need_visibility_time_update;
+ bool need_tag_id_on_graph_visibility_update;
+ bool need_tag_id_on_graph_visibility_time_update;
/* Indicates which ID types were updated. */
char id_type_updated[INDEX_ID_MAX];
@@ -162,5 +167,4 @@ struct Depsgraph {
MEM_CXX_CLASS_ALLOC_FUNCS("Depsgraph");
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc
index c64b7bc1eb7..6da290d6c4e 100644
--- a/source/blender/depsgraph/intern/depsgraph_build.cc
+++ b/source/blender/depsgraph/intern/depsgraph_build.cc
@@ -199,11 +199,11 @@ void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle,
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
}
-void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle,
- const char *description)
+void DEG_add_depends_on_transform_relation(struct DepsNodeHandle *node_handle,
+ const char *description)
{
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
- deg_node_handle->builder->add_modifier_to_transform_relation(deg_node_handle, description);
+ deg_node_handle->builder->add_depends_on_transform_relation(deg_node_handle, description);
}
void DEG_add_special_eval_flag(struct DepsNodeHandle *node_handle, ID *id, uint32_t flag)
@@ -270,7 +270,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
{
DEG_DEBUG_PRINTF(graph, TAG, "%s: Tagging relations for update.\n", __func__);
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
- deg_graph->need_update = true;
+ deg_graph->need_update_relations = true;
/* NOTE: When relations are updated, it's quite possible that
* we've got new bases in the scene. This means, we need to
* re-create flat array of bases in view layer.
@@ -286,7 +286,7 @@ void DEG_graph_tag_relations_update(Depsgraph *graph)
void DEG_graph_relations_update(Depsgraph *graph)
{
deg::Depsgraph *deg_graph = (deg::Depsgraph *)graph;
- if (!deg_graph->need_update) {
+ if (!deg_graph->need_update_relations) {
/* Graph is up to date, nothing to do. */
return;
}
diff --git a/source/blender/depsgraph/intern/depsgraph_physics.cc b/source/blender/depsgraph/intern/depsgraph_physics.cc
index 5660d5eb1bd..cf5cccac580 100644
--- a/source/blender/depsgraph/intern/depsgraph_physics.cc
+++ b/source/blender/depsgraph/intern/depsgraph_physics.cc
@@ -174,7 +174,7 @@ ListBase *build_effector_relations(Depsgraph *graph, Collection *collection)
ID *collection_id = object_id_safe(collection);
return hash->lookup_or_add_cb(collection_id, [&]() {
::Depsgraph *depsgraph = reinterpret_cast<::Depsgraph *>(graph);
- return BKE_effector_relations_create(depsgraph, graph->view_layer, collection);
+ return BKE_effector_relations_create(depsgraph, graph->scene, graph->view_layer, collection);
});
}
diff --git a/source/blender/depsgraph/intern/depsgraph_physics.h b/source/blender/depsgraph/intern/depsgraph_physics.h
index a8d37f76b09..2105eb62ce1 100644
--- a/source/blender/depsgraph/intern/depsgraph_physics.h
+++ b/source/blender/depsgraph/intern/depsgraph_physics.h
@@ -10,8 +10,7 @@
struct Collection;
struct ListBase;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -21,5 +20,4 @@ ListBase *build_collision_relations(Depsgraph *graph,
unsigned int modifier_type);
void clear_physics_relations(Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/depsgraph_query.cc b/source/blender/depsgraph/intern/depsgraph_query.cc
index 6ffc711a475..19339fa34ea 100644
--- a/source/blender/depsgraph/intern/depsgraph_query.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query.cc
@@ -23,6 +23,7 @@
#include "DNA_scene_types.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "DEG_depsgraph.h"
@@ -328,7 +329,7 @@ bool DEG_is_fully_evaluated(const struct Depsgraph *depsgraph)
{
const deg::Depsgraph *deg_graph = (const deg::Depsgraph *)depsgraph;
/* Check whether relations are up to date. */
- if (deg_graph->need_update) {
+ if (deg_graph->need_update_relations) {
return false;
}
/* Check whether IDs are up to date. */
diff --git a/source/blender/depsgraph/intern/depsgraph_query_iter.cc b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
index bcde1839a0f..bcae5de1e1e 100644
--- a/source/blender/depsgraph/intern/depsgraph_query_iter.cc
+++ b/source/blender/depsgraph/intern/depsgraph_query_iter.cc
@@ -153,10 +153,10 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data)
if (dob->no_draw) {
continue;
}
- if (obd->type == OB_MBALL) {
+ if (dob->ob_data && GS(dob->ob_data->name) == ID_MB) {
continue;
}
- if (deg_object_hide_original(data->eval_mode, dob->ob, dob)) {
+ if (obd->type != OB_MBALL && deg_object_hide_original(data->eval_mode, dob->ob, dob)) {
continue;
}
@@ -182,7 +182,7 @@ bool deg_iterator_duplis_step(DEGObjectIterData *data)
}
/* Duplicated elements shouldn't care whether their original collection is visible or not. */
- temp_dupli_object->base_flag |= BASE_VISIBLE_DEPSGRAPH;
+ temp_dupli_object->base_flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT;
int ob_visibility = BKE_object_visibility(temp_dupli_object, data->eval_mode);
if ((ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) == 0) {
@@ -219,7 +219,9 @@ bool deg_iterator_objects_step(DEGObjectIterData *data)
for (; data->id_node_index < data->num_id_nodes; data->id_node_index++) {
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
- if (!id_node->is_directly_visible) {
+ /* Use the build time visibility so that the ID is not appearing/disappearing throughout
+ * animation export. */
+ if (!id_node->is_visible_on_build) {
continue;
}
@@ -338,10 +340,13 @@ static void DEG_iterator_ids_step(BLI_Iterator *iter, deg::IDNode *id_node, bool
{
ID *id_cow = id_node->id_cow;
- if (!id_node->is_directly_visible) {
+ /* Use the build time visibility so that the ID is not appearing/disappearing throughout
+ * animation export. */
+ if (!id_node->is_visible_on_build) {
iter->skip = true;
return;
}
+
if (only_updated && !(id_cow->recalc & ID_RECALC_ALL)) {
/* Node-tree is considered part of the data-block. */
bNodeTree *ntree = ntreeFromID(id_cow);
diff --git a/source/blender/depsgraph/intern/depsgraph_registry.h b/source/blender/depsgraph/intern/depsgraph_registry.h
index 2746f17590a..4ab63dcc77d 100644
--- a/source/blender/depsgraph/intern/depsgraph_registry.h
+++ b/source/blender/depsgraph/intern/depsgraph_registry.h
@@ -11,8 +11,7 @@
struct Main;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -20,5 +19,4 @@ void register_graph(Depsgraph *depsgraph);
void unregister_graph(Depsgraph *depsgraph);
Span<Depsgraph *> get_all_registered_graphs(Main *bmain);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h
index 4d51ad85878..3f316fa84e8 100644
--- a/source/blender/depsgraph/intern/depsgraph_relation.h
+++ b/source/blender/depsgraph/intern/depsgraph_relation.h
@@ -9,8 +9,7 @@
#include "MEM_guardedalloc.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Node;
@@ -29,6 +28,8 @@ enum RelationFlag {
RELATION_FLAG_GODMODE = (1 << 4),
/* Relation will check existence before being added. */
RELATION_CHECK_BEFORE_ADD = (1 << 5),
+ /* The relation does not participate in visibility checks. */
+ RELATION_NO_VISIBILITY_CHANGE = (1 << 6),
};
/* B depends on A (A -> B) */
@@ -49,5 +50,4 @@ struct Relation {
MEM_CXX_CLASS_ALLOC_FUNCS("Relation");
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index b50081458ad..cc742b98866 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -220,17 +220,28 @@ void depsgraph_tag_to_component_opcode(const ID *id,
*component_type = NodeType::NTREE_OUTPUT;
*operation_code = OperationCode::NTREE_OUTPUT;
break;
+
+ case ID_RECALC_PROVISION_26:
+ case ID_RECALC_PROVISION_27:
+ case ID_RECALC_PROVISION_28:
+ case ID_RECALC_PROVISION_29:
+ case ID_RECALC_PROVISION_30:
+ case ID_RECALC_PROVISION_31:
+ /* Silently ignore.
+ * The bits might be passed here from ID_RECALC_ALL. This is not a code-mistake, but just the
+ * way how the recalc flags are handled. */
+ break;
}
}
void id_tag_update_ntree_special(
- Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source)
+ Main *bmain, Depsgraph *graph, ID *id, unsigned int flags, eUpdateSource update_source)
{
bNodeTree *ntree = ntreeFromID(id);
if (ntree == nullptr) {
return;
}
- graph_id_tag_update(bmain, graph, &ntree->id, flag, update_source);
+ graph_id_tag_update(bmain, graph, &ntree->id, flags, update_source);
}
void depsgraph_update_editors_tag(Main *bmain, Depsgraph *graph, ID *id)
@@ -407,13 +418,13 @@ string stringify_append_bit(const string &str, IDRecalcFlag tag)
return result;
}
-string stringify_update_bitfield(int flag)
+string stringify_update_bitfield(unsigned int flags)
{
- if (flag == 0) {
+ if (flags == 0) {
return "LEGACY_0";
}
string result;
- int current_flag = flag;
+ unsigned int current_flag = flags;
/* Special cases to avoid ALL flags form being split into
* individual bits. */
if ((current_flag & ID_RECALC_PSYS_ALL) == ID_RECALC_PSYS_ALL) {
@@ -421,7 +432,7 @@ string stringify_update_bitfield(int flag)
}
/* Handle all the rest of the flags. */
while (current_flag != 0) {
- IDRecalcFlag tag = (IDRecalcFlag)(1 << bitscan_forward_clear_i(&current_flag));
+ IDRecalcFlag tag = (IDRecalcFlag)(1 << bitscan_forward_clear_uint(&current_flag));
result = stringify_append_bit(result, tag);
}
return result;
@@ -449,7 +460,7 @@ int deg_recalc_flags_for_legacy_zero()
ID_RECALC_SOURCE | ID_RECALC_EDITORS);
}
-int deg_recalc_flags_effective(Depsgraph *graph, int flags)
+int deg_recalc_flags_effective(Depsgraph *graph, unsigned int flags)
{
if (graph != nullptr) {
if (!graph->is_active) {
@@ -494,19 +505,19 @@ void deg_graph_node_tag_zero(Main *bmain,
void graph_tag_on_visible_update(Depsgraph *graph, const bool do_time)
{
- graph->need_visibility_update = true;
- graph->need_visibility_time_update |= do_time;
+ graph->need_tag_id_on_graph_visibility_update = true;
+ graph->need_tag_id_on_graph_visibility_time_update |= do_time;
}
} /* namespace */
void graph_tag_ids_for_visible_update(Depsgraph *graph)
{
- if (!graph->need_visibility_update) {
+ if (!graph->need_tag_id_on_graph_visibility_update) {
return;
}
- const bool do_time = graph->need_visibility_time_update;
+ const bool do_time = graph->need_tag_id_on_graph_visibility_time_update;
Main *bmain = graph->bmain;
/* NOTE: It is possible to have this function called with `do_time=false` first and later (prior
@@ -520,12 +531,12 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph)
* No need bother with it to tag or anything. */
continue;
}
- int flag = 0;
+ unsigned int flags = 0;
if (!deg::deg_copy_on_write_is_expanded(id_node->id_cow)) {
- flag |= ID_RECALC_COPY_ON_WRITE;
+ flags |= ID_RECALC_COPY_ON_WRITE;
if (do_time) {
if (BKE_animdata_from_id(id_node->id_orig) != nullptr) {
- flag |= ID_RECALC_ANIMATION;
+ flags |= ID_RECALC_ANIMATION;
}
}
}
@@ -542,9 +553,9 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph)
*
* TODO(sergey): Need to generalize this somehow. */
if (id_type == ID_OB) {
- flag |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY;
+ flags |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY;
}
- graph_id_tag_update(bmain, graph, id_node->id_orig, flag, DEG_UPDATE_SOURCE_VISIBILITY);
+ graph_id_tag_update(bmain, graph, id_node->id_orig, flags, DEG_UPDATE_SOURCE_VISIBILITY);
if (id_type == ID_SCE) {
/* Make sure collection properties are up to date. */
id_node->tag_update(graph, DEG_UPDATE_SOURCE_VISIBILITY);
@@ -561,8 +572,8 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph)
id_node->previously_visible_components_mask = id_node->visible_components_mask;
}
- graph->need_visibility_update = false;
- graph->need_visibility_time_update = false;
+ graph->need_tag_id_on_graph_visibility_update = false;
+ graph->need_tag_id_on_graph_visibility_time_update = false;
}
NodeType geometry_tag_to_component(const ID *id)
@@ -614,20 +625,20 @@ NodeType geometry_tag_to_component(const ID *id)
return NodeType::UNDEFINED;
}
-void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source)
+void id_tag_update(Main *bmain, ID *id, unsigned int flags, eUpdateSource update_source)
{
- graph_id_tag_update(bmain, nullptr, id, flag, update_source);
+ graph_id_tag_update(bmain, nullptr, id, flags, update_source);
for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) {
- graph_id_tag_update(bmain, depsgraph, id, flag, update_source);
+ graph_id_tag_update(bmain, depsgraph, id, flags, update_source);
}
/* Accumulate all tags for an ID between two undo steps, so they can be
* replayed for undo. */
- id->recalc_after_undo_push |= deg_recalc_flags_effective(nullptr, flag);
+ id->recalc_after_undo_push |= deg_recalc_flags_effective(nullptr, flags);
}
void graph_id_tag_update(
- Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source)
+ Main *bmain, Depsgraph *graph, ID *id, unsigned int flags, eUpdateSource update_source)
{
const int debug_flags = (graph != nullptr) ? DEG_debug_flags_get((::Depsgraph *)graph) : G.debug;
if (graph != nullptr && graph->is_evaluating) {
@@ -640,20 +651,20 @@ void graph_id_tag_update(
printf("%s: id=%s flags=%s source=%s\n",
__func__,
id->name,
- stringify_update_bitfield(flag).c_str(),
+ stringify_update_bitfield(flags).c_str(),
update_source_as_string(update_source));
}
IDNode *id_node = (graph != nullptr) ? graph->find_id_node(id) : nullptr;
if (graph != nullptr) {
DEG_graph_id_type_tag(reinterpret_cast<::Depsgraph *>(graph), GS(id->name));
}
- if (flag == 0) {
+ if (flags == 0) {
deg_graph_node_tag_zero(bmain, graph, id_node, update_source);
}
/* Store original flag in the ID.
* Allows to have more granularity than a node-factory based flags. */
if (id_node != nullptr) {
- id_node->id_cow->recalc |= flag;
+ id_node->id_cow->recalc |= flags;
}
/* When ID is tagged for update based on an user edits store the recalc flags in the original ID.
* This way IDs in the undo steps will have this flag preserved, making it possible to restore
@@ -663,20 +674,20 @@ void graph_id_tag_update(
* usually newly created dependency graph skips animation update to avoid loss of unkeyed
* changes). */
if (update_source == DEG_UPDATE_SOURCE_USER_EDIT) {
- id->recalc |= deg_recalc_flags_effective(graph, flag);
+ id->recalc |= deg_recalc_flags_effective(graph, flags);
}
- int current_flag = flag;
+ unsigned int current_flag = flags;
while (current_flag != 0) {
- IDRecalcFlag tag = (IDRecalcFlag)(1 << bitscan_forward_clear_i(&current_flag));
+ IDRecalcFlag tag = (IDRecalcFlag)(1 << bitscan_forward_clear_uint(&current_flag));
graph_id_tag_update_single_flag(bmain, graph, id, id_node, tag, update_source);
}
/* Special case for nested node tree data-blocks. */
- id_tag_update_ntree_special(bmain, graph, id, flag, update_source);
+ id_tag_update_ntree_special(bmain, graph, id, flags, update_source);
/* Direct update tags means that something outside of simulated/cached
* physics did change and that cache is to be invalidated.
* This is only needed if data changes. If it's just a drawing, we keep the
* point cache. */
- if (update_source == DEG_UPDATE_SOURCE_USER_EDIT && flag != ID_RECALC_SHADING) {
+ if (update_source == DEG_UPDATE_SOURCE_USER_EDIT && flags != ID_RECALC_SHADING) {
graph_id_tag_update_single_flag(
bmain, graph, id, id_node, ID_RECALC_POINT_CACHE, update_source);
}
@@ -741,33 +752,45 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag)
return "TAG_FOR_UNDO";
case ID_RECALC_NTREE_OUTPUT:
return "ID_RECALC_NTREE_OUTPUT";
+
+ case ID_RECALC_PROVISION_26:
+ case ID_RECALC_PROVISION_27:
+ case ID_RECALC_PROVISION_28:
+ case ID_RECALC_PROVISION_29:
+ case ID_RECALC_PROVISION_30:
+ case ID_RECALC_PROVISION_31:
+ /* Silently return nullptr, indicating that there is no string representation.
+ *
+ * This is needed due to the way how logging for ID_RECALC_ALL works: it iterates over all
+ * bits and converts then to string. */
+ return nullptr;
}
return nullptr;
}
/* Data-Based Tagging. */
-void DEG_id_tag_update(ID *id, int flag)
+void DEG_id_tag_update(ID *id, unsigned int flags)
{
- DEG_id_tag_update_ex(G.main, id, flag);
+ DEG_id_tag_update_ex(G.main, id, flags);
}
-void DEG_id_tag_update_ex(Main *bmain, ID *id, int flag)
+void DEG_id_tag_update_ex(Main *bmain, ID *id, unsigned int flags)
{
if (id == nullptr) {
/* Ideally should not happen, but old depsgraph allowed this. */
return;
}
- deg::id_tag_update(bmain, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT);
+ deg::id_tag_update(bmain, id, flags, deg::DEG_UPDATE_SOURCE_USER_EDIT);
}
void DEG_graph_id_tag_update(struct Main *bmain,
struct Depsgraph *depsgraph,
struct ID *id,
- int flag)
+ unsigned int flags)
{
deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph;
- deg::graph_id_tag_update(bmain, graph, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT);
+ deg::graph_id_tag_update(bmain, graph, id, flags, deg::DEG_UPDATE_SOURCE_USER_EDIT);
}
void DEG_time_tag_update(struct Main *bmain)
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.h b/source/blender/depsgraph/intern/depsgraph_tag.h
index a0518420bfb..61643e6f740 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.h
+++ b/source/blender/depsgraph/intern/depsgraph_tag.h
@@ -10,8 +10,7 @@
struct ID;
struct Main;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -19,15 +18,14 @@ struct Depsgraph;
NodeType geometry_tag_to_component(const ID *id);
/* Tag given ID for an update in all registered dependency graphs. */
-void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source);
+void id_tag_update(Main *bmain, ID *id, unsigned int flags, eUpdateSource update_source);
/* Tag given ID for an update with in a given dependency graph. */
void graph_id_tag_update(
- Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source);
+ Main *bmain, Depsgraph *graph, ID *id, unsigned int flags, eUpdateSource update_source);
/* Tag IDs of the graph for the visibility update tags.
* Will do nothing if the graph is not tagged for visibility update. */
void graph_tag_ids_for_visible_update(Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/depsgraph_type.h b/source/blender/depsgraph/intern/depsgraph_type.h
index 9cf14a831bc..9e21d124f83 100644
--- a/source/blender/depsgraph/intern/depsgraph_type.h
+++ b/source/blender/depsgraph/intern/depsgraph_type.h
@@ -34,8 +34,7 @@ struct Depsgraph;
struct CustomData_MeshMasks;
-namespace blender {
-namespace deg {
+namespace blender::deg {
/* Commonly used types. */
using std::deque;
@@ -153,5 +152,4 @@ struct DEGCustomDataMeshMasks {
}
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/depsgraph_update.h b/source/blender/depsgraph/intern/depsgraph_update.h
index 18f8e6acab6..c5a72157bd2 100644
--- a/source/blender/depsgraph/intern/depsgraph_update.h
+++ b/source/blender/depsgraph/intern/depsgraph_update.h
@@ -10,12 +10,10 @@
struct DEGEditorUpdateContext;
struct ID;
-namespace blender {
-namespace deg {
+namespace blender::deg {
void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, struct ID *id);
void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, bool updated);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 2d9d40aede6..cd0015ff717 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -37,6 +37,7 @@
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/eval/deg_eval_flush.h"
#include "intern/eval/deg_eval_stats.h"
+#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.h"
#include "intern/node/deg_node_component.h"
#include "intern/node/deg_node_id.h"
@@ -69,6 +70,9 @@ enum class EvaluationStage {
* involved. */
COPY_ON_WRITE,
+ /* Evaluate actual ID nodes visibility based on the current state of animation and drivers. */
+ DYNAMIC_VISIBILITY,
+
/* Threaded evaluation of all possible operations. */
THREADED_EVALUATION,
@@ -83,7 +87,8 @@ struct DepsgraphEvalState {
Depsgraph *graph;
bool do_stats;
EvaluationStage stage;
- bool need_single_thread_pass;
+ bool need_update_pending_parents = true;
+ bool need_single_thread_pass = false;
};
void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_node)
@@ -101,6 +106,13 @@ void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_nod
else {
operation_node->evaluate(depsgraph);
}
+
+ /* Clear the flag early on, allowing partial updates without re-evaluating the same node multiple
+ * times.
+ * This is a thread-safe modification as the node's flags are only read for a non-scheduled nodes
+ * and this node has been scheduled. */
+ operation_node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE |
+ DEPSOP_FLAG_USER_MODIFIED);
}
void deg_task_run_func(TaskPool *pool, void *taskdata)
@@ -116,24 +128,31 @@ void deg_task_run_func(TaskPool *pool, void *taskdata)
schedule_children(state, operation_node, schedule_node_to_pool, pool);
}
-bool check_operation_node_visible(OperationNode *op_node)
+bool check_operation_node_visible(const DepsgraphEvalState *state, OperationNode *op_node)
{
const ComponentNode *comp_node = op_node->owner;
- /* Special exception, copy on write component is to be always evaluated,
- * to keep copied "database" in a consistent state. */
+ /* Special case for copy on write component: it is to be always evaluated, to keep copied
+ * "database" in a consistent state. */
if (comp_node->type == NodeType::COPY_ON_WRITE) {
return true;
}
- return comp_node->affects_directly_visible;
+
+ /* Special case for dynamic visibility pass: the actual visibility is not yet known, so limit to
+ * only operations which affects visibility. */
+ if (state->stage == EvaluationStage::DYNAMIC_VISIBILITY) {
+ return op_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
+ }
+
+ return comp_node->affects_visible_id;
}
-void calculate_pending_parents_for_node(OperationNode *node)
+void calculate_pending_parents_for_node(const DepsgraphEvalState *state, OperationNode *node)
{
/* Update counters, applies for both visible and invisible IDs. */
node->num_links_pending = 0;
node->scheduled = false;
/* Invisible IDs requires no pending operations. */
- if (!check_operation_node_visible(node)) {
+ if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to bother with anything if node is not tagged for update. */
@@ -147,7 +166,7 @@ void calculate_pending_parents_for_node(OperationNode *node)
* calculation, but how is it possible that visible object depends
* on an invisible? This is something what is prohibited after
* deg_graph_build_flush_layers(). */
- if (!check_operation_node_visible(from)) {
+ if (!check_operation_node_visible(state, from)) {
continue;
}
/* No need to wait for operation which is up to date. */
@@ -159,20 +178,24 @@ void calculate_pending_parents_for_node(OperationNode *node)
}
}
-void calculate_pending_parents(Depsgraph *graph)
+void calculate_pending_parents_if_needed(DepsgraphEvalState *state)
{
- for (OperationNode *node : graph->operations) {
- calculate_pending_parents_for_node(node);
+ if (!state->need_update_pending_parents) {
+ return;
}
+
+ for (OperationNode *node : state->graph->operations) {
+ calculate_pending_parents_for_node(state, node);
+ }
+
+ state->need_update_pending_parents = false;
}
void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph)
{
- const bool do_stats = state->do_stats;
- calculate_pending_parents(graph);
/* Clear tags and other things which needs to be clear. */
- for (OperationNode *node : graph->operations) {
- if (do_stats) {
+ if (state->do_stats) {
+ for (OperationNode *node : graph->operations) {
node->stats.reset_current();
}
}
@@ -197,11 +220,10 @@ bool need_evaluate_operation_at_stage(DepsgraphEvalState *state,
case EvaluationStage::COPY_ON_WRITE:
return (component_node->type == NodeType::COPY_ON_WRITE);
+ case EvaluationStage::DYNAMIC_VISIBILITY:
+ return operation_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
+
case EvaluationStage::THREADED_EVALUATION:
- /* Sanity check: copy-on-write node should be evaluated already. This will be indicated by
- * scheduled flag (we assume that scheduled operations have been actually handled by previous
- * stage). */
- BLI_assert(operation_node->scheduled || component_node->type != NodeType::COPY_ON_WRITE);
if (is_metaball_object_operation(operation_node)) {
state->need_single_thread_pass = true;
return false;
@@ -227,7 +249,7 @@ void schedule_node(DepsgraphEvalState *state,
ScheduleFunctionArgs... schedule_function_args)
{
/* No need to schedule nodes of invisible ID. */
- if (!check_operation_node_visible(node)) {
+ if (!check_operation_node_visible(state, node)) {
return;
}
/* No need to schedule operations which are not tagged for update, they are
@@ -302,8 +324,32 @@ void schedule_node_to_queue(OperationNode *node,
BLI_gsqueue_push(evaluation_queue, &node);
}
-void evaluate_graph_single_threaded(DepsgraphEvalState *state)
+/* Evaluate given stage of the dependency graph evaluation using multiple threads.
+ *
+ * NOTE: Will assign the `state->stage` to the given stage. */
+void evaluate_graph_threaded_stage(DepsgraphEvalState *state,
+ TaskPool *task_pool,
+ const EvaluationStage stage)
+{
+ state->stage = stage;
+
+ calculate_pending_parents_if_needed(state);
+
+ schedule_graph(state, schedule_node_to_pool, task_pool);
+ BLI_task_pool_work_and_wait(task_pool);
+}
+
+/* Evaluate remaining operations of the dependency graph in a single threaded manner. */
+void evaluate_graph_single_threaded_if_needed(DepsgraphEvalState *state)
{
+ if (!state->need_single_thread_pass) {
+ return;
+ }
+
+ BLI_assert(!state->need_update_pending_parents);
+
+ state->stage = EvaluationStage::SINGLE_THREADED_WORKAROUND;
+
GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *));
schedule_graph(state, schedule_node_to_queue, evaluation_queue);
@@ -334,9 +380,7 @@ void depsgraph_ensure_view_layer(Depsgraph *graph)
deg_update_copy_on_write_datablock(graph, scene_id_node);
}
-} // namespace
-
-static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state)
+TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state)
{
if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) {
return BLI_task_pool_create_no_threads(state);
@@ -345,6 +389,8 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state)
return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH);
}
+} // namespace
+
void deg_evaluate_on_refresh(Depsgraph *graph)
{
/* Nothing to update, early out. */
@@ -361,41 +407,67 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
graph->is_evaluating = true;
depsgraph_ensure_view_layer(graph);
+
/* Set up evaluation state. */
DepsgraphEvalState state;
state.graph = graph;
state.do_stats = graph->debug.do_time_debug();
- state.need_single_thread_pass = false;
+
/* Prepare all nodes for evaluation. */
initialize_execution(&state, graph);
- /* Do actual evaluation now. */
- /* First, process all Copy-On-Write nodes. */
- state.stage = EvaluationStage::COPY_ON_WRITE;
+ /* Evaluation happens in several incremental steps:
+ *
+ * - Start with the copy-on-write operations which never form dependency cycles. This will ensure
+ * that if a dependency graph has a cycle evaluation functions will always "see" valid expanded
+ * datablock. It might not be evaluated yet, but at least the datablock will be valid.
+ *
+ * - If there is potentially dynamically changing visibility in the graph update the actual
+ * nodes visibilities, so that actual heavy data evaluation can benefit from knowledge that
+ * something heavy is not currently visible.
+ *
+ * - Multi-threaded evaluation of all possible nodes.
+ * Certain operations (and their subtrees) could be ignored. For example, meta-balls are not
+ * safe from threading point of view, so the threaded evaluation will stop at the metaball
+ * operation node.
+ *
+ * - Single-threaded pass of all remaining operations. */
+
TaskPool *task_pool = deg_evaluate_task_pool_create(&state);
- schedule_graph(&state, schedule_node_to_pool, task_pool);
- BLI_task_pool_work_and_wait(task_pool);
- BLI_task_pool_free(task_pool);
- /* After that, process all other nodes. */
- state.stage = EvaluationStage::THREADED_EVALUATION;
- task_pool = deg_evaluate_task_pool_create(&state);
- schedule_graph(&state, schedule_node_to_pool, task_pool);
- BLI_task_pool_work_and_wait(task_pool);
- BLI_task_pool_free(task_pool);
+ evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE);
+
+ if (graph->has_animated_visibility || graph->need_update_nodes_visibility) {
+ /* Update pending parents including only the ones which are affecting operations which are
+ * affecting visibility. */
+ state.need_update_pending_parents = true;
+
+ evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::DYNAMIC_VISIBILITY);
+
+ deg_graph_flush_visibility_flags_if_needed(graph);
- if (state.need_single_thread_pass) {
- state.stage = EvaluationStage::SINGLE_THREADED_WORKAROUND;
- evaluate_graph_single_threaded(&state);
+ /* Update parents to an updated visibility and evaluation stage.
+ *
+ * Need to do it regardless of whether visibility is actually changed or not: current state of
+ * the pending parents are all zeroes because it was previously calculated for only visibility
+ * related nodes and those are fully evaluated by now. */
+ state.need_update_pending_parents = true;
}
+ evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::THREADED_EVALUATION);
+
+ BLI_task_pool_free(task_pool);
+
+ evaluate_graph_single_threaded_if_needed(&state);
+
/* Finalize statistics gathering. This is because we only gather single
* operation timing here, without aggregating anything to avoid any extra
* synchronization. */
if (state.do_stats) {
deg_eval_stats_aggregate(graph);
}
- /* Clear any uncleared tags - just in case. */
+
+ /* Clear any uncleared tags. */
deg_graph_clear_tags(graph);
graph->is_evaluating = false;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.h b/source/blender/depsgraph/intern/eval/deg_eval.h
index ba86e1a349d..6937231d81a 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval.h
@@ -9,8 +9,7 @@
#pragma once
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -23,5 +22,4 @@ struct Depsgraph;
*/
void deg_evaluate_on_refresh(Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
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 058f57e5a61..4e07a7b173c 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
@@ -414,13 +414,16 @@ void scene_remove_all_bases(Scene *scene_cow)
/* Makes it so given view layer only has bases corresponding to enabled
* objects. */
-void view_layer_remove_disabled_bases(const Depsgraph *depsgraph, ViewLayer *view_layer)
+void view_layer_remove_disabled_bases(const Depsgraph *depsgraph,
+ const Scene *scene,
+ ViewLayer *view_layer)
{
if (view_layer == nullptr) {
return;
}
ListBase enabled_bases = {nullptr, nullptr};
- LISTBASE_FOREACH_MUTABLE (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH_MUTABLE (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
/* TODO(sergey): Would be cool to optimize this somehow, or make it so
* builder tags bases.
*
@@ -479,7 +482,7 @@ void scene_setup_view_layers_after_remap(const Depsgraph *depsgraph,
const ViewLayer *view_layer_orig = get_original_view_layer(depsgraph, id_node);
ViewLayer *view_layer_eval = reinterpret_cast<ViewLayer *>(scene_cow->view_layers.first);
view_layer_update_orig_base_pointers(view_layer_orig, view_layer_eval);
- view_layer_remove_disabled_bases(depsgraph, view_layer_eval);
+ view_layer_remove_disabled_bases(depsgraph, scene_cow, view_layer_eval);
/* TODO(sergey): Remove objects from collections as well.
* Not a HUGE deal for now, nobody is looking into those CURRENTLY.
* Still not an excuse to have those. */
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
index 29322d58218..cbf450aa3f1 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h
@@ -27,8 +27,7 @@ struct ID;
struct Depsgraph;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
class DepsgraphNodeBuilder;
@@ -77,5 +76,4 @@ bool deg_copy_on_write_is_expanded(const struct ID *id_cow);
bool deg_copy_on_write_is_needed(const ID *id_orig);
bool deg_copy_on_write_is_needed(const ID_Type id_type);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
index de4e26aa4b5..09981eb32c5 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
@@ -129,7 +129,18 @@ inline void flush_handle_component_node(IDNode *id_node,
*
* TODO(sergey): Make this a more generic solution. */
if (!ELEM(comp_node->type, NodeType::PARTICLE_SETTINGS, NodeType::PARTICLE_SYSTEM)) {
+ const bool is_geometry_component = comp_node->type == NodeType::GEOMETRY;
for (OperationNode *op : comp_node->operations) {
+ /* Special case for the visibility operation in the geometry component.
+ *
+ * This operation is a part of the geometry component so that manual tag for geometry recalc
+ * ensures that the visibility is re-evaluated. This operation is not to be re-evaluated when
+ * an update is flushed to the geometry component via a time dependency or a driver targeting
+ * a modifier. Skipping update in this case avoids CPU time unnecessarily spent looping over
+ * modifiers and looking up operations by name in the visibility evaluation function. */
+ if (is_geometry_component && op->opcode == OperationCode::VISIBILITY) {
+ continue;
+ }
op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
}
}
@@ -378,11 +389,6 @@ void deg_graph_flush_updates(Depsgraph *graph)
void deg_graph_clear_tags(Depsgraph *graph)
{
- /* Go over all operation nodes, clearing tags. */
- for (OperationNode *node : graph->operations) {
- node->flag &= ~(DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE |
- DEPSOP_FLAG_USER_MODIFIED);
- }
/* Clear any entry tags which haven't been flushed. */
graph->entry_tags.clear();
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.h b/source/blender/depsgraph/intern/eval/deg_eval_flush.h
index 6eb232e76e5..614ca66faed 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.h
@@ -9,8 +9,7 @@
#pragma once
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -24,5 +23,4 @@ void deg_graph_flush_updates(struct Depsgraph *graph);
*/
void deg_graph_clear_tags(struct Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
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 96ab9388023..9ccd7ed447b 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
@@ -62,6 +62,7 @@ void RuntimeBackup::init_from_id(ID *id)
break;
case ID_GD:
gpencil_backup.init_from_gpencil(reinterpret_cast<bGPdata *>(id));
+ break;
default:
break;
}
@@ -104,6 +105,7 @@ void RuntimeBackup::restore_to_id(ID *id)
break;
case ID_GD:
gpencil_backup.restore_to_gpencil(reinterpret_cast<bGPdata *>(id));
+ break;
default:
break;
}
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 deb21715a28..3d9b308c5ad 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
@@ -17,8 +17,7 @@
#include "intern/eval/deg_eval_runtime_backup_sound.h"
#include "intern/eval/deg_eval_runtime_backup_volume.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -59,5 +58,4 @@ class RuntimeBackup {
GPencilBackup gpencil_backup;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
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 92847b330e8..807cc91242e 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
@@ -11,8 +11,7 @@
#include "intern/depsgraph_type.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -46,5 +45,4 @@ class AnimationBackup {
Vector<AnimationValueBackup> values_backup;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h
index 68eff01fd60..95c0ca3a2fe 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_gpencil.h
@@ -9,8 +9,7 @@
struct bGPdata;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -25,5 +24,4 @@ class GPencilBackup {
const Depsgraph *depsgraph;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h
index faec8f7c065..ee51204b24c 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_modifier.h
@@ -11,8 +11,7 @@
struct ModifierData;
-namespace blender {
-namespace deg {
+namespace blender::deg {
class ModifierDataBackup {
public:
@@ -22,5 +21,4 @@ class ModifierDataBackup {
void *runtime;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h
index 0e826e8f72a..aa13914d8c8 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_movieclip.h
@@ -11,8 +11,7 @@ struct MovieClip;
struct MovieClipCache;
struct anim;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -30,5 +29,4 @@ class MovieClipBackup {
struct MovieClipCache *cache;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h
index 3b138feec0b..c9cc167d927 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.h
@@ -17,8 +17,7 @@
struct Object;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -46,5 +45,4 @@ class ObjectRuntimeBackup {
Map<SessionUUID, bPoseChannel_Runtime> pose_channel_runtime_data;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h
index 8fd5de44001..2f6a3dd4371 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_pose.h
@@ -11,8 +11,6 @@
#include "DNA_action_types.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
-}
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h
index 17683966a22..155cb42a96d 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_scene.h
@@ -11,8 +11,7 @@
struct Scene;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -40,5 +39,4 @@ class SceneBackup {
SequencerBackup sequencer_backup;
};
-} // namespace deg
-} // namespace blender
+} // 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 95fbd2a3e4e..f97b6b200e9 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
@@ -11,8 +11,7 @@
struct Sequence;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -32,5 +31,4 @@ class SequenceBackup {
ListBase anims;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h
index 19e1a63ab4c..38fb8e81cc3 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.h
@@ -16,8 +16,7 @@
struct Scene;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -34,5 +33,4 @@ class SequencerBackup {
Map<SessionUUID, SequenceBackup> sequences_backup;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h
index c4267be1421..9e1d15251e8 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sound.h
@@ -9,8 +9,7 @@
struct bSound;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -29,5 +28,4 @@ class SoundBackup {
void *playback_handle;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h
index bc263cc58f8..60cba64f120 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h
@@ -10,8 +10,7 @@
struct Volume;
struct VolumeGridVector;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
@@ -27,5 +26,4 @@ class VolumeBackup {
char filepath[1024]; /* FILE_MAX */
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_stats.h b/source/blender/depsgraph/intern/eval/deg_eval_stats.h
index a1e3cdaca76..c24c5f07135 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_stats.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_stats.h
@@ -7,13 +7,11 @@
#pragma once
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
/* Aggregate operation timings to overall component and ID nodes timing. */
void deg_eval_stats_aggregate(Depsgraph *graph);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
new file mode 100644
index 00000000000..a056ba1dfa7
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc
@@ -0,0 +1,232 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#include "intern/eval/deg_eval_visibility.h"
+
+#include "DNA_layer_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_assert.h"
+#include "BLI_stack.h"
+
+#include "DEG_depsgraph.h"
+
+#include "intern/depsgraph.h"
+#include "intern/depsgraph_relation.h"
+#include "intern/node/deg_node.h"
+#include "intern/node/deg_node_component.h"
+#include "intern/node/deg_node_id.h"
+#include "intern/node/deg_node_operation.h"
+
+namespace blender::deg {
+
+void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node)
+{
+ BLI_assert(GS(id_node->id_cow->name) == ID_OB);
+
+ Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph);
+ const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
+
+ DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id);
+
+ const int required_flags = (graph->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT :
+ BASE_ENABLED_RENDER;
+
+ const bool is_enabled = object->base_flag & required_flags;
+
+ if (id_node->is_enabled_on_eval != is_enabled) {
+ id_node->is_enabled_on_eval = is_enabled;
+
+ /* Tag dependency graph for changed visibility, so that it is updated on all dependencies prior
+ * to a pass of an actual evaluation. */
+ graph->need_update_nodes_visibility = true;
+ }
+}
+
+void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node)
+{
+ BLI_assert(GS(id_node->id_cow->name) == ID_OB);
+
+ Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph);
+ const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
+
+ DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id);
+
+ if (BLI_listbase_is_empty(&object->modifiers)) {
+ return;
+ }
+
+ const ModifierMode modifier_mode = (graph->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime :
+ eModifierMode_Render;
+
+ const ComponentNode *geometry_component = id_node->find_component(NodeType::GEOMETRY);
+ LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) {
+ OperationNode *modifier_node = geometry_component->find_operation(OperationCode::MODIFIER,
+ modifier->name);
+
+ BLI_assert_msg(modifier_node != nullptr,
+ "Modifier node in depsgraph is not found. Likely due to missing "
+ "DEG_relations_tag_update().");
+
+ const bool modifier_enabled = modifier->mode & modifier_mode;
+ const int mute_flag = modifier_enabled ? 0 : DEPSOP_FLAG_MUTE;
+ if ((modifier_node->flag & DEPSOP_FLAG_MUTE) != mute_flag) {
+ modifier_node->flag &= ~DEPSOP_FLAG_MUTE;
+ modifier_node->flag |= mute_flag;
+
+ graph->need_update_nodes_visibility = true;
+ }
+ }
+}
+
+void deg_graph_flush_visibility_flags(Depsgraph *graph)
+{
+ enum {
+ DEG_NODE_VISITED = (1 << 0),
+ };
+
+ for (IDNode *id_node : graph->id_nodes) {
+ for (ComponentNode *comp_node : id_node->components.values()) {
+ comp_node->possibly_affects_visible_id = id_node->is_visible_on_build;
+ comp_node->affects_visible_id = id_node->is_visible_on_build && id_node->is_enabled_on_eval;
+
+ /* Visibility component is always to be considered to have the same visibility as the
+ * `id_node->is_visible_on_build`. This is because the visibility is to be evaluated
+ * regardless of its current state as it might get changed due to animation. */
+ if (comp_node->type == NodeType::VISIBILITY) {
+ comp_node->affects_visible_id = id_node->is_visible_on_build;
+ }
+
+ /* Enforce "visibility" of the synchronization component.
+ *
+ * This component is never connected to other ID nodes, and hence can not be handled in the
+ * same way as other components needed for evaluation. It is only needed for proper
+ * evaluation of the ID node it belongs to.
+ *
+ * The design is such that the synchronization is supposed to happen whenever any part of the
+ * ID changed/evaluated. Here we mark the component as "visible" so that genetic recalc flag
+ * flushing and scheduling will handle the component in a generic manner. */
+ if (comp_node->type == NodeType::SYNCHRONIZATION) {
+ comp_node->possibly_affects_visible_id = true;
+ comp_node->affects_visible_id = true;
+ }
+ }
+ }
+
+ BLI_Stack *stack = BLI_stack_new(sizeof(OperationNode *), "DEG flush layers stack");
+
+ for (OperationNode *op_node : graph->operations) {
+ op_node->custom_flags = 0;
+ op_node->num_links_pending = 0;
+ for (Relation *rel : op_node->outlinks) {
+ if ((rel->from->type == NodeType::OPERATION) && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
+ ++op_node->num_links_pending;
+ }
+ }
+ if (op_node->num_links_pending == 0) {
+ BLI_stack_push(stack, &op_node);
+ op_node->custom_flags |= DEG_NODE_VISITED;
+ }
+ }
+
+ while (!BLI_stack_is_empty(stack)) {
+ OperationNode *op_node;
+ BLI_stack_pop(stack, &op_node);
+
+ /* Flush flags to parents. */
+ for (Relation *rel : op_node->inlinks) {
+ if (rel->from->type == NodeType::OPERATION) {
+ const OperationNode *op_to = reinterpret_cast<const OperationNode *>(rel->to);
+ const ComponentNode *comp_to = op_to->owner;
+
+ /* Ignore the synchronization target.
+ * It is always visible and should not affect on other components. */
+ if (comp_to->type == NodeType::SYNCHRONIZATION) {
+ continue;
+ }
+
+ OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from);
+ ComponentNode *comp_from = op_from->owner;
+
+ op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY);
+
+ if (rel->flag & RELATION_NO_VISIBILITY_CHANGE) {
+ continue;
+ }
+
+ const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id;
+
+ bool target_affects_visible_id = comp_to->affects_visible_id;
+
+ /* This is a bit arbitrary but the idea here is following:
+ *
+ * - When another object is used by a disabled modifier we do not want that object to
+ * be considered needed for evaluation.
+ *
+ * - However, we do not want to take mute flag during visibility propagation within the
+ * same object. Otherwise drivers and transform dependencies of the geometry component
+ * entry component might not be properly handled.
+ *
+ * This code works fine for muting modifiers, but might need tweaks when mute is used for
+ * something else. */
+ if (comp_from != comp_to && (op_to->flag & DEPSOP_FLAG_MUTE)) {
+ target_affects_visible_id = false;
+ }
+
+ /* Visibility component forces all components of the current ID to be considered as
+ * affecting directly visible. */
+ if (comp_from->type == NodeType::VISIBILITY) {
+ const IDNode *id_node_from = comp_from->owner;
+ if (target_possibly_affects_visible_id) {
+ for (ComponentNode *comp_node : id_node_from->components.values()) {
+ comp_node->possibly_affects_visible_id |= target_possibly_affects_visible_id;
+ }
+ }
+ if (target_affects_visible_id) {
+ for (ComponentNode *comp_node : id_node_from->components.values()) {
+ comp_node->affects_visible_id |= target_affects_visible_id;
+ }
+ }
+ }
+ else {
+ comp_from->possibly_affects_visible_id |= target_possibly_affects_visible_id;
+ comp_from->affects_visible_id |= target_affects_visible_id;
+ }
+ }
+ }
+
+ /* Schedule parent nodes. */
+ for (Relation *rel : op_node->inlinks) {
+ if (rel->from->type == NodeType::OPERATION) {
+ OperationNode *op_from = (OperationNode *)rel->from;
+ if ((rel->flag & RELATION_FLAG_CYCLIC) == 0) {
+ BLI_assert(op_from->num_links_pending > 0);
+ --op_from->num_links_pending;
+ }
+ if ((op_from->num_links_pending == 0) && (op_from->custom_flags & DEG_NODE_VISITED) == 0) {
+ BLI_stack_push(stack, &op_from);
+ op_from->custom_flags |= DEG_NODE_VISITED;
+ }
+ }
+ }
+ }
+ BLI_stack_free(stack);
+
+ graph->need_update_nodes_visibility = false;
+}
+
+void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph)
+{
+ if (!graph->need_update_nodes_visibility) {
+ return;
+ }
+
+ deg_graph_flush_visibility_flags(graph);
+}
+
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
new file mode 100644
index 00000000000..6544654f3cf
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#pragma once
+
+struct Depsgraph;
+
+namespace blender::deg {
+
+struct Depsgraph;
+struct IDNode;
+
+/* Evaluate actual node visibility flags based on the current state of object's visibility
+ * restriction flags. */
+void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node);
+
+/* Update node visibility flags based on actual modifiers mode flags. */
+void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node);
+
+/* Flush both static and dynamic visibility flags from leaves up to the roots, making it possible
+ * to know whether a node has affect on something (potentially) visible. */
+void deg_graph_flush_visibility_flags(Depsgraph *graph);
+void deg_graph_flush_visibility_flags_if_needed(Depsgraph *graph);
+
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc
index 7a515424e06..6303b22cac3 100644
--- a/source/blender/depsgraph/intern/node/deg_node.cc
+++ b/source/blender/depsgraph/intern/node/deg_node.cc
@@ -100,6 +100,8 @@ const char *nodeTypeAsString(NodeType type)
return "SIMULATION";
case NodeType::NTREE_OUTPUT:
return "NTREE_OUTPUT";
+ case NodeType::NTREE_GEOMETRY_PREPROCESS:
+ return "NTREE_GEOMETRY_PREPROCESS";
/* Total number of meaningful node types. */
case NodeType::NUM_TYPES:
@@ -158,6 +160,7 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type)
case NodeType::CACHE:
case NodeType::SIMULATION:
case NodeType::NTREE_OUTPUT:
+ case NodeType::NTREE_GEOMETRY_PREPROCESS:
return DEG_SCENE_COMP_PARAMETERS;
case NodeType::VISIBILITY:
@@ -232,6 +235,7 @@ eDepsObjectComponentType nodeTypeToObjectComponent(NodeType type)
case NodeType::SYNCHRONIZATION:
case NodeType::SIMULATION:
case NodeType::NTREE_OUTPUT:
+ case NodeType::NTREE_GEOMETRY_PREPROCESS:
case NodeType::UNDEFINED:
case NodeType::NUM_TYPES:
return DEG_OB_COMP_PARAMETERS;
diff --git a/source/blender/depsgraph/intern/node/deg_node.h b/source/blender/depsgraph/intern/node/deg_node.h
index 509867ad47a..e31c1769a2a 100644
--- a/source/blender/depsgraph/intern/node/deg_node.h
+++ b/source/blender/depsgraph/intern/node/deg_node.h
@@ -18,8 +18,7 @@
struct ID;
struct Scene;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct Depsgraph;
struct OperationNode;
@@ -65,7 +64,7 @@ enum class NodeType {
ANIMATION,
/* Transform Component (Parenting/Constraints) */
TRANSFORM,
- /* Geometry Component (#Mesh / #DispList) */
+ /* Geometry Component (#Mesh, #Curves, etc.) */
GEOMETRY,
/* Sequencer Component (Scene Only) */
SEQUENCER,
@@ -131,6 +130,8 @@ enum class NodeType {
SIMULATION,
/* Node tree output component. */
NTREE_OUTPUT,
+ /* Preprocessing for geometry node trees before they can be evaluated. */
+ NTREE_GEOMETRY_PREPROCESS,
/* Total number of meaningful node types. */
NUM_TYPES,
@@ -217,5 +218,4 @@ struct Node {
void deg_register_base_depsnodes();
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc
index cfcbec46569..ebb4450579f 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_component.cc
@@ -67,7 +67,10 @@ uint64_t ComponentNode::OperationIDKey::hash() const
}
ComponentNode::ComponentNode()
- : entry_operation(nullptr), exit_operation(nullptr), affects_directly_visible(false)
+ : entry_operation(nullptr),
+ exit_operation(nullptr),
+ possibly_affects_visible_id(false),
+ affects_visible_id(false)
{
operations_map = new Map<ComponentNode::OperationIDKey, OperationNode *>();
}
@@ -87,10 +90,11 @@ ComponentNode::~ComponentNode()
string ComponentNode::identifier() const
{
- const string idname = this->owner->name;
- const string typebuf = "" + to_string(static_cast<int>(type)) + ")";
- return typebuf + name + " : " + idname +
- "( affects_directly_visible: " + (affects_directly_visible ? "true" : "false") + ")";
+ const string type_name = type_get_factory(type)->type_name();
+ const string name_part = name[0] ? (string(" '") + name + "'") : "";
+
+ return "[" + type_name + "]" + name_part + " : " +
+ "(affects_visible_id: " + (affects_visible_id ? "true" : "false") + ")";
}
OperationNode *ComponentNode::find_operation(OperationIDKey key) const
@@ -215,10 +219,9 @@ void ComponentNode::clear_operations()
void ComponentNode::tag_update(Depsgraph *graph, eUpdateSource source)
{
- OperationNode *entry_op = get_entry_operation();
- if (entry_op != nullptr && entry_op->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
- return;
- }
+ /* Note that the node might already be tagged for an update due invisible state of the node
+ * during previous dependency evaluation. Here the node gets re-tagged, so we need to give
+ * the evaluated clues that evaluation needs to happen again. */
for (OperationNode *op_node : operations) {
op_node->tag_update(graph, source);
}
@@ -335,6 +338,7 @@ DEG_COMPONENT_NODE_DEFINE(GenericDatablock, GENERIC_DATABLOCK, 0);
DEG_COMPONENT_NODE_DEFINE(Visibility, VISIBILITY, 0);
DEG_COMPONENT_NODE_DEFINE(Simulation, SIMULATION, 0);
DEG_COMPONENT_NODE_DEFINE(NTreeOutput, NTREE_OUTPUT, ID_RECALC_NTREE_OUTPUT);
+DEG_COMPONENT_NODE_DEFINE(NTreeGeometryPreprocess, NTREE_GEOMETRY_PREPROCESS, 0);
/** \} */
@@ -369,6 +373,7 @@ void deg_register_component_depsnodes()
register_node_typeinfo(&DNTI_VISIBILITY);
register_node_typeinfo(&DNTI_SIMULATION);
register_node_typeinfo(&DNTI_NTREE_OUTPUT);
+ register_node_typeinfo(&DNTI_NTREE_GEOMETRY_PREPROCESS);
}
/** \} */
diff --git a/source/blender/depsgraph/intern/node/deg_node_component.h b/source/blender/depsgraph/intern/node/deg_node_component.h
index 6958866af3b..44c63822ca3 100644
--- a/source/blender/depsgraph/intern/node/deg_node_component.h
+++ b/source/blender/depsgraph/intern/node/deg_node_component.h
@@ -22,10 +22,8 @@
struct ID;
struct bPoseChannel;
-namespace blender {
-namespace deg {
+namespace blender::deg {
-struct BoneComponentNode;
struct Depsgraph;
struct IDNode;
struct OperationNode;
@@ -56,32 +54,45 @@ struct ComponentNode : public Node {
virtual string identifier() const override;
- /* Find an existing operation, if requested operation does not exist
- * nullptr will be returned. */
+ /* Find an existing operation, if requested operation does not exist nullptr will be returned.
+ * See #add_operation for the meaning and examples of #name and #name_tag.
+ */
OperationNode *find_operation(OperationIDKey key) const;
- OperationNode *find_operation(OperationCode opcode, const char *name, int name_tag) const;
+ OperationNode *find_operation(OperationCode opcode,
+ const char *name = "",
+ int name_tag = -1) const;
- /* Find an existing operation, will throw an assert() if it does not exist. */
+ /* Find an existing operation, will throw an assert() if it does not exist.
+ * See #add_operation for the meaning and examples of #name and #name_tag. */
OperationNode *get_operation(OperationIDKey key) const;
- OperationNode *get_operation(OperationCode opcode, const char *name, int name_tag) const;
+ OperationNode *get_operation(OperationCode opcode,
+ const char *name = "",
+ int name_tag = -1) const;
/* Check operation exists and return it. */
bool has_operation(OperationIDKey key) const;
- bool has_operation(OperationCode opcode, const char *name, int name_tag) const;
+ bool has_operation(OperationCode opcode, const char *name = "", int name_tag = -1) const;
/**
* Create a new node for representing an operation and add this to graph
+ *
* \warning If an existing node is found, it will be modified. This helps
* when node may have been partially created earlier (e.g. parent ref before
* parent item is added)
*
* \param opcode: The operation to perform.
- * \param name: Identifier for operation - used to find/locate it again.
+ * \param name: An optional identifier for operation. It will be used to tell operation nodes
+ * with the same code apart. For example, parameter operation code will have name
+ * set to the corresponding custom property name
+ * \param name_tag: An optional integer tag for the name. Is an additional way to tell operations
+ * apart. For example, RNA path to an array property will have the same opcode
+ * of PARAMETERS, name corresponding to the property name, and name tag
+ * corresponding to the array index within the property.
*/
OperationNode *add_operation(const DepsEvalOperationCb &op,
OperationCode opcode,
- const char *name,
- int name_tag);
+ const char *name = "",
+ int name_tag = -1);
/* Entry/exit operations management.
*
@@ -125,9 +136,17 @@ struct ComponentNode : public Node {
return true;
}
- /* Denotes whether this component affects (possibly indirectly) on a
- * directly visible object. */
- bool affects_directly_visible;
+ /* The component has (possibly indirect) effect on a data-block whose node has
+ * is_visible_on_build set to true.
+ *
+ * This field is ensured to be up-to-date prior to `IDNode::finalize_build()`. */
+ bool possibly_affects_visible_id;
+
+ /* Denotes whether this component actually affects (possibly indirectly) on a directly visible
+ * object. Includes possibly run-time visibility update of ID nodes.
+ *
+ * NOTE: Is only reliable after `deg_graph_flush_visibility()`. */
+ bool affects_visible_id;
};
/* ---------------------------------------- */
@@ -186,9 +205,10 @@ DEG_COMPONENT_NODE_DECLARE_GENERIC(Synchronization);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Audio);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Armature);
DEG_COMPONENT_NODE_DECLARE_GENERIC(GenericDatablock);
-DEG_COMPONENT_NODE_DECLARE_NO_COW(Visibility);
+DEG_COMPONENT_NODE_DECLARE_NO_COW_TAG_ON_UPDATE(Visibility);
DEG_COMPONENT_NODE_DECLARE_GENERIC(Simulation);
DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeOutput);
+DEG_COMPONENT_NODE_DECLARE_GENERIC(NTreeGeometryPreprocess);
/* Bone Component */
struct BoneComponentNode : public ComponentNode {
@@ -218,5 +238,4 @@ struct ParametersComponentNode : public ComponentNode {
void deg_register_component_depsnodes();
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_factory.h b/source/blender/depsgraph/intern/node/deg_node_factory.h
index 6caf99ac9ba..ec5029e8352 100644
--- a/source/blender/depsgraph/intern/node/deg_node_factory.h
+++ b/source/blender/depsgraph/intern/node/deg_node_factory.h
@@ -14,9 +14,7 @@
struct ID;
-namespace blender {
-namespace deg {
-
+namespace blender::deg {
struct DepsNodeFactory {
virtual NodeType type() const = 0;
virtual const char *type_name() const = 0;
@@ -41,7 +39,6 @@ void register_node_typeinfo(DepsNodeFactory *factory);
/* Get typeinfo for specified type */
DepsNodeFactory *type_get_factory(NodeType type);
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
#include "intern/node/deg_node_factory_impl.h"
diff --git a/source/blender/depsgraph/intern/node/deg_node_factory_impl.h b/source/blender/depsgraph/intern/node/deg_node_factory_impl.h
index 76a91860cc1..5059368120e 100644
--- a/source/blender/depsgraph/intern/node/deg_node_factory_impl.h
+++ b/source/blender/depsgraph/intern/node/deg_node_factory_impl.h
@@ -11,8 +11,7 @@
struct ID;
-namespace blender {
-namespace deg {
+namespace blender::deg {
template<class ModeObjectType> NodeType DepsNodeFactoryImpl<ModeObjectType>::type() const
{
@@ -35,18 +34,10 @@ Node *DepsNodeFactoryImpl<ModeObjectType>::create_node(const ID *id,
const char *name) const
{
Node *node = new ModeObjectType();
- /* Populate base node settings. */
node->type = type();
- /* Set name if provided, or use default type name. */
- if (name[0] != '\0') {
- node->name = name;
- }
- else {
- node->name = type_name();
- }
+ node->name = name;
node->init(id, subdata);
return node;
}
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.cc b/source/blender/depsgraph/intern/node/deg_node_id.cc
index 99224501e98..735d606ac9e 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_id.cc
@@ -69,7 +69,8 @@ void IDNode::init(const ID *id, const char *UNUSED(subdata))
customdata_masks = DEGCustomDataMeshMasks();
previous_customdata_masks = DEGCustomDataMeshMasks();
linked_state = DEG_ID_LINKED_INDIRECTLY;
- is_directly_visible = true;
+ is_visible_on_build = true;
+ is_enabled_on_eval = true;
is_collection_fully_expanded = false;
has_base = false;
is_user_modified = false;
@@ -138,8 +139,8 @@ string IDNode::identifier() const
BLI_snprintf(orig_ptr, sizeof(orig_ptr), "%p", id_orig);
BLI_snprintf(cow_ptr, sizeof(cow_ptr), "%p", id_cow);
return string(nodeTypeAsString(type)) + " : " + name + " (orig: " + orig_ptr +
- ", eval: " + cow_ptr + ", is_directly_visible " +
- (is_directly_visible ? "true" : "false") + ")";
+ ", eval: " + cow_ptr + ", is_visible_on_build " +
+ (is_visible_on_build ? "true" : "false") + ")";
}
ComponentNode *IDNode::find_component(NodeType type, const char *name) const
@@ -188,7 +189,7 @@ IDComponentsMask IDNode::get_visible_components_mask() const
{
IDComponentsMask result = 0;
for (ComponentNode *comp_node : components.values()) {
- if (comp_node->affects_directly_visible) {
+ if (comp_node->possibly_affects_visible_id) {
const int component_type_as_int = static_cast<int>(comp_node->type);
BLI_assert(component_type_as_int < 64);
result |= (1ULL << component_type_as_int);
diff --git a/source/blender/depsgraph/intern/node/deg_node_id.h b/source/blender/depsgraph/intern/node/deg_node_id.h
index a8b6294b482..7f0a656cb8d 100644
--- a/source/blender/depsgraph/intern/node/deg_node_id.h
+++ b/source/blender/depsgraph/intern/node/deg_node_id.h
@@ -12,8 +12,7 @@
#include "DNA_ID.h"
#include "intern/node/deg_node.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct ComponentNode;
@@ -92,8 +91,21 @@ struct IDNode : public Node {
eDepsNode_LinkedState_Type linked_state;
- /* Indicates the data-block is visible in the evaluated scene. */
- bool is_directly_visible;
+ /* Indicates the data-block is to be considered visible in the evaluated scene.
+ *
+ * This flag is set during dependency graph build where check for an actual visibility might not
+ * be available yet due to driven or animated restriction flags. So it is more of an intent or,
+ * in other words, plausibility of the data-block to be visible. */
+ bool is_visible_on_build;
+
+ /* Evaluated state of whether evaluation considered this data-block "enabled".
+ *
+ * For objects this is derived from the base restriction flags, which might be animated or
+ * driven. It is set to `BASE_ENABLED_<VIEWPORT, RENDER>` (depending on the graph mode) after
+ * the object's flags from layer were evaluated.
+ *
+ * For other data-types is currently always true. */
+ bool is_enabled_on_eval;
/* For the collection type of ID, denotes whether collection was fully
* recursed into. */
@@ -117,5 +129,4 @@ struct IDNode : public Node {
DEG_DEPSNODE_DECLARE;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc
index c29aeefd9b2..d5401a6b0d1 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.cc
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc
@@ -32,6 +32,8 @@ const char *operationCodeAsString(OperationCode opcode)
return "PARAMETERS_EVAL";
case OperationCode::PARAMETERS_EXIT:
return "PARAMETERS_EXIT";
+ case OperationCode::VISIBILITY:
+ return "VISIBILITY";
/* Animation, Drivers, etc. */
case OperationCode::ANIMATION_ENTRY:
return "ANIMATION_ENTRY";
@@ -82,6 +84,8 @@ const char *operationCodeAsString(OperationCode opcode)
/* Geometry. */
case OperationCode::GEOMETRY_EVAL_INIT:
return "GEOMETRY_EVAL_INIT";
+ case OperationCode::MODIFIER:
+ return "MODIFIER";
case OperationCode::GEOMETRY_EVAL:
return "GEOMETRY_EVAL";
case OperationCode::GEOMETRY_EVAL_DONE:
@@ -169,6 +173,8 @@ const char *operationCodeAsString(OperationCode opcode)
/* Node Tree. */
case OperationCode::NTREE_OUTPUT:
return "NTREE_OUTPUT";
+ case OperationCode::NTREE_GEOMETRY_PREPROCESS:
+ return "NTREE_GEOMETRY_PREPROCESS";
/* Movie clip. */
case OperationCode::MOVIECLIP_EVAL:
return "MOVIECLIP_EVAL";
@@ -216,9 +222,23 @@ string OperationNode::full_identifier() const
void OperationNode::tag_update(Depsgraph *graph, eUpdateSource source)
{
- if ((flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
- graph->add_entry_tag(this);
+ /* Ensure that there is an entry tag for this update.
+ *
+ * Note that the node might already be tagged for an update due invisible state of the node
+ * during previous dependency evaluation. Here the node gets re-tagged, so we need to give
+ * the evaluated clues that evaluation needs to happen again. */
+ graph->add_entry_tag(this);
+
+ /* Enforce dynamic visibility code-path update.
+ * This ensures visibility flags are consistently propagated throughout the dependency graph when
+ * there is no animated visibility in the graph.
+ *
+ * For example this ensures that graph is updated properly when manually toggling non-animated
+ * modifier visibility. */
+ if (opcode == OperationCode::VISIBILITY) {
+ graph->need_update_nodes_visibility = true;
}
+
/* Tag for update, but also note that this was the source of an update. */
flag |= (DEPSOP_FLAG_NEEDS_UPDATE | DEPSOP_FLAG_DIRECTLY_MODIFIED);
switch (source) {
diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h
index d4916be1113..b0685ac0351 100644
--- a/source/blender/depsgraph/intern/node/deg_node_operation.h
+++ b/source/blender/depsgraph/intern/node/deg_node_operation.h
@@ -13,8 +13,7 @@
struct Depsgraph;
-namespace blender {
-namespace deg {
+namespace blender::deg {
struct ComponentNode;
@@ -34,6 +33,7 @@ enum class OperationCode {
PARAMETERS_ENTRY,
PARAMETERS_EVAL,
PARAMETERS_EXIT,
+ VISIBILITY,
/* Animation, Drivers, etc. --------------------------------------------- */
/* NLA + Action */
@@ -84,6 +84,8 @@ enum class OperationCode {
/* Initialize evaluation of the geometry. Is an entry operation of geometry
* component. */
GEOMETRY_EVAL_INIT,
+ /* Modifier. */
+ MODIFIER,
/* Evaluate the whole geometry, including modifiers. */
GEOMETRY_EVAL,
/* Evaluation of geometry is completely done. */
@@ -163,6 +165,7 @@ enum class OperationCode {
/* Node Tree. ----------------------------------------------------------- */
NTREE_OUTPUT,
+ NTREE_GEOMETRY_PREPROCESS,
/* Batch caches. -------------------------------------------------------- */
GEOMETRY_SELECT_UPDATE,
@@ -202,15 +205,24 @@ const char *operationCodeAsString(OperationCode opcode);
enum OperationFlag {
/* Node needs to be updated. */
DEPSOP_FLAG_NEEDS_UPDATE = (1 << 0),
+
/* Node was directly modified, causing need for update. */
DEPSOP_FLAG_DIRECTLY_MODIFIED = (1 << 1),
+
/* Node was updated due to user input. */
DEPSOP_FLAG_USER_MODIFIED = (1 << 2),
- /* Node may not be removed, even when it has no evaluation callback and no
- * outgoing relations. This is for NO-OP nodes that are purely used to indicate a
- * relation between components/IDs, and not for connecting to an operation. */
+
+ /* Node may not be removed, even when it has no evaluation callback and no outgoing relations.
+ * This is for NO-OP nodes that are purely used to indicate a relation between components/IDs,
+ * and not for connecting to an operation. */
DEPSOP_FLAG_PINNED = (1 << 3),
+ /* The operation directly or indirectly affects ID node visibility. */
+ DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4),
+
+ /* Evaluation of the node is temporarily disabled. */
+ DEPSOP_FLAG_MUTE = (1 << 5),
+
/* Set of flags which gets flushed along the relations. */
DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED),
};
@@ -268,5 +280,4 @@ struct OperationNode : public Node {
void deg_register_operation_depsnodes();
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/depsgraph/intern/node/deg_node_time.h b/source/blender/depsgraph/intern/node/deg_node_time.h
index fcfe9a0fa80..3946b63384d 100644
--- a/source/blender/depsgraph/intern/node/deg_node_time.h
+++ b/source/blender/depsgraph/intern/node/deg_node_time.h
@@ -9,8 +9,7 @@
#include "intern/node/deg_node.h"
-namespace blender {
-namespace deg {
+namespace blender::deg {
/* Time Source Node. */
struct TimeSourceNode : public Node {
@@ -25,5 +24,4 @@ struct TimeSourceNode : public Node {
DEG_DEPSNODE_DECLARE;
};
-} // namespace deg
-} // namespace blender
+} // namespace blender::deg
diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt
index 55d789f64b0..8b61d8686cd 100644
--- a/source/blender/draw/CMakeLists.txt
+++ b/source/blender/draw/CMakeLists.txt
@@ -23,11 +23,11 @@ set(INC
../nodes
../render
../render/intern
+ ../compositor/realtime_compositor
../windowmanager
../../../intern/atomic
../../../intern/clog
- ../../../intern/glew-mx
../../../intern/guardedalloc
../../../intern/opensubdiv
@@ -67,28 +67,27 @@ set(SRC
intern/mesh_extractors/extract_mesh_vbo_skin_roots.cc
intern/mesh_extractors/extract_mesh_vbo_tan.cc
intern/mesh_extractors/extract_mesh_vbo_uv.cc
- intern/mesh_extractors/extract_mesh_vbo_vcol.cc
intern/mesh_extractors/extract_mesh_vbo_weights.cc
intern/draw_attributes.cc
intern/draw_cache_impl_curve.cc
intern/draw_cache_impl_curves.cc
- intern/draw_cache_impl_displist.c
intern/draw_cache_impl_gpencil.c
intern/draw_cache_impl_lattice.c
intern/draw_cache_impl_mesh.cc
- intern/draw_cache_impl_metaball.c
intern/draw_cache_impl_particles.c
- intern/draw_cache_impl_pointcloud.c
+ intern/draw_cache_impl_pointcloud.cc
intern/draw_cache_impl_subdivision.cc
intern/draw_cache_impl_volume.c
intern/draw_color_management.cc
+ intern/draw_command.cc
intern/draw_common.c
intern/draw_curves.cc
- intern/draw_debug.c
+ intern/draw_debug.cc
intern/draw_fluid.c
intern/draw_hair.cc
intern/draw_instance_data.c
intern/draw_manager.c
+ intern/draw_manager.cc
intern/draw_manager_data.c
intern/draw_manager_exec.c
intern/draw_manager_profiling.c
@@ -104,6 +103,7 @@ set(SRC
intern/smaa_textures.c
engines/basic/basic_engine.c
engines/basic/basic_shader.c
+ engines/compositor/compositor_engine.cc
engines/image/image_engine.cc
engines/image/image_shader.cc
engines/eevee/eevee_bloom.c
@@ -135,10 +135,15 @@ set(SRC
engines/eevee/eevee_temporal_sampling.c
engines/eevee/eevee_volumes.c
engines/eevee_next/eevee_camera.cc
+ engines/eevee_next/eevee_cryptomatte.cc
+ engines/eevee_next/eevee_depth_of_field.cc
engines/eevee_next/eevee_engine.cc
engines/eevee_next/eevee_film.cc
+ engines/eevee_next/eevee_hizbuffer.cc
engines/eevee_next/eevee_instance.cc
+ engines/eevee_next/eevee_light.cc
engines/eevee_next/eevee_material.cc
+ engines/eevee_next/eevee_motion_blur.cc
engines/eevee_next/eevee_pipeline.cc
engines/eevee_next/eevee_renderbuffers.cc
engines/eevee_next/eevee_sampling.cc
@@ -171,33 +176,33 @@ set(SRC
engines/gpencil/gpencil_shader_fx.c
engines/select/select_draw_utils.c
engines/select/select_engine.c
- engines/overlay/overlay_antialiasing.c
- engines/overlay/overlay_armature.c
- engines/overlay/overlay_background.c
- engines/overlay/overlay_edit_curve.c
+ engines/overlay/overlay_antialiasing.cc
+ engines/overlay/overlay_armature.cc
+ engines/overlay/overlay_background.cc
+ engines/overlay/overlay_edit_curve.cc
engines/overlay/overlay_edit_curves.cc
- engines/overlay/overlay_edit_mesh.c
- engines/overlay/overlay_edit_text.c
- engines/overlay/overlay_edit_uv.c
- engines/overlay/overlay_engine.c
- engines/overlay/overlay_extra.c
- engines/overlay/overlay_facing.c
- engines/overlay/overlay_fade.c
- engines/overlay/overlay_gpencil.c
- engines/overlay/overlay_grid.c
- 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
- engines/overlay/overlay_particle.c
- engines/overlay/overlay_sculpt.c
+ engines/overlay/overlay_edit_mesh.cc
+ engines/overlay/overlay_edit_text.cc
+ engines/overlay/overlay_edit_uv.cc
+ engines/overlay/overlay_engine.cc
+ engines/overlay/overlay_extra.cc
+ engines/overlay/overlay_facing.cc
+ engines/overlay/overlay_fade.cc
+ engines/overlay/overlay_gpencil.cc
+ engines/overlay/overlay_grid.cc
+ engines/overlay/overlay_image.cc
+ engines/overlay/overlay_lattice.cc
+ engines/overlay/overlay_metaball.cc
+ engines/overlay/overlay_mode_transfer.cc
+ engines/overlay/overlay_motion_path.cc
+ engines/overlay/overlay_outline.cc
+ engines/overlay/overlay_paint.cc
+ engines/overlay/overlay_particle.cc
+ engines/overlay/overlay_sculpt.cc
engines/overlay/overlay_sculpt_curves.cc
- engines/overlay/overlay_shader.c
- engines/overlay/overlay_volume.c
- engines/overlay/overlay_wireframe.c
+ engines/overlay/overlay_shader.cc
+ engines/overlay/overlay_volume.cc
+ engines/overlay/overlay_wireframe.cc
DRW_engine.h
DRW_select_buffer.h
@@ -209,31 +214,58 @@ set(SRC
intern/draw_cache_impl.h
intern/draw_cache_inline.h
intern/draw_color_management.h
+ intern/draw_command.hh
intern/draw_common.h
intern/draw_common_shader_shared.h
intern/draw_curves_private.h
intern/draw_debug.h
+ intern/draw_debug.hh
intern/draw_hair_private.h
+ intern/draw_handle.hh
intern/draw_instance_data.h
intern/draw_manager.h
+ intern/draw_manager.hh
intern/draw_manager_profiling.h
intern/draw_manager_testing.h
intern/draw_manager_text.h
+ intern/draw_pass.hh
+ intern/draw_resource.cc
+ intern/draw_resource.hh
intern/draw_shader.h
intern/draw_shader_shared.h
+ intern/draw_state.h
intern/draw_subdivision.h
intern/draw_texture_pool.h
+ intern/draw_view.cc
intern/draw_view.h
+ intern/draw_view.hh
intern/draw_view_data.h
intern/mesh_extractors/extract_mesh.hh
intern/smaa_textures.h
engines/basic/basic_engine.h
engines/basic/basic_private.h
+ engines/compositor/compositor_engine.h
engines/eevee/eevee_engine.h
engines/eevee/eevee_lightcache.h
engines/eevee/eevee_lut.h
engines/eevee/eevee_private.h
+ engines/eevee_next/eevee_camera.hh
+ engines/eevee_next/eevee_depth_of_field.hh
engines/eevee_next/eevee_engine.h
+ engines/eevee_next/eevee_film.hh
+ engines/eevee_next/eevee_hizbuffer.hh
+ engines/eevee_next/eevee_instance.hh
+ engines/eevee_next/eevee_light.hh
+ engines/eevee_next/eevee_material.hh
+ engines/eevee_next/eevee_motion_blur.hh
+ engines/eevee_next/eevee_pipeline.hh
+ engines/eevee_next/eevee_renderbuffers.hh
+ engines/eevee_next/eevee_sampling.hh
+ engines/eevee_next/eevee_shader.hh
+ engines/eevee_next/eevee_sync.hh
+ engines/eevee_next/eevee_velocity.hh
+ engines/eevee_next/eevee_view.hh
+ engines/eevee_next/eevee_world.hh
engines/external/external_engine.h
engines/image/image_batches.hh
engines/image/image_buffer_cache.hh
@@ -254,12 +286,13 @@ set(SRC
engines/select/select_engine.h
engines/select/select_private.h
engines/overlay/overlay_engine.h
- engines/overlay/overlay_private.h
+ engines/overlay/overlay_private.hh
)
set(LIB
bf_blenkernel
bf_blenlib
+ bf_realtime_compositor
bf_windowmanager
)
@@ -340,6 +373,7 @@ set(GLSL_SRC
engines/eevee/shaders/raytrace_lib.glsl
engines/eevee/shaders/renderpass_lib.glsl
engines/eevee/shaders/renderpass_postprocess_frag.glsl
+ engines/eevee/shaders/cryptomatte_lib.glsl
engines/eevee/shaders/cryptomatte_frag.glsl
engines/eevee/shaders/cryptomatte_vert.glsl
engines/eevee/shaders/ltc_lib.glsl
@@ -361,21 +395,54 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_attributes_lib.glsl
engines/eevee_next/shaders/eevee_camera_lib.glsl
+ engines/eevee_next/shaders/eevee_colorspace_lib.glsl
+ engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl
+ engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl
engines/eevee_next/shaders/eevee_film_comp.glsl
+ engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
engines/eevee_next/shaders/eevee_film_frag.glsl
engines/eevee_next/shaders/eevee_film_lib.glsl
engines/eevee_next/shaders/eevee_geom_curves_vert.glsl
engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl
engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl
engines/eevee_next/shaders/eevee_geom_world_vert.glsl
+ engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl
+ engines/eevee_next/shaders/eevee_hiz_update_comp.glsl
+ engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl
+ engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl
+ engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl
+ engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl
+ engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl
+ engines/eevee_next/shaders/eevee_light_eval_lib.glsl
+ engines/eevee_next/shaders/eevee_light_iter_lib.glsl
+ engines/eevee_next/shaders/eevee_light_lib.glsl
+ engines/eevee_next/shaders/eevee_ltc_lib.glsl
+ engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl
+ engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl
+ engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl
+ engines/eevee_next/shaders/eevee_motion_blur_lib.glsl
engines/eevee_next/shaders/eevee_nodetree_lib.glsl
+ engines/eevee_next/shaders/eevee_sampling_lib.glsl
engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
engines/eevee_next/shaders/eevee_surf_lib.glsl
engines/eevee_next/shaders/eevee_surf_world_frag.glsl
engines/eevee_next/shaders/eevee_velocity_lib.glsl
- engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl
engines/eevee_next/eevee_defines.hh
engines/eevee_next/eevee_shader_shared.hh
@@ -410,22 +477,25 @@ set(GLSL_SRC
engines/workbench/workbench_shader_shared.h
+ intern/shaders/common_aabb_lib.glsl
intern/shaders/common_attribute_lib.glsl
intern/shaders/common_colormanagement_lib.glsl
+ intern/shaders/common_debug_draw_lib.glsl
+ intern/shaders/common_debug_print_lib.glsl
+ intern/shaders/common_debug_shape_lib.glsl
+ intern/shaders/common_fullscreen_vert.glsl
+ intern/shaders/common_fxaa_lib.glsl
intern/shaders/common_globals_lib.glsl
intern/shaders/common_gpencil_lib.glsl
- intern/shaders/common_pointcloud_lib.glsl
intern/shaders/common_hair_lib.glsl
- intern/shaders/common_hair_refine_vert.glsl
intern/shaders/common_hair_refine_comp.glsl
- intern/shaders/common_math_lib.glsl
+ intern/shaders/common_hair_refine_vert.glsl
+ intern/shaders/common_intersect_lib.glsl
intern/shaders/common_math_geom_lib.glsl
- intern/shaders/common_view_clipping_lib.glsl
- intern/shaders/common_view_lib.glsl
- intern/shaders/common_fxaa_lib.glsl
+ intern/shaders/common_math_lib.glsl
+ intern/shaders/common_pointcloud_lib.glsl
+ intern/shaders/common_shape_lib.glsl
intern/shaders/common_smaa_lib.glsl
- intern/shaders/common_fullscreen_vert.glsl
-
intern/shaders/common_subdiv_custom_data_interp_comp.glsl
intern/shaders/common_subdiv_ibo_lines_comp.glsl
intern/shaders/common_subdiv_ibo_tris_comp.glsl
@@ -438,8 +508,20 @@ set(GLSL_SRC
intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl
intern/shaders/common_subdiv_vbo_lnor_comp.glsl
intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl
-
+ intern/shaders/common_view_clipping_lib.glsl
+ intern/shaders/common_view_lib.glsl
+ intern/shaders/draw_command_generate_comp.glsl
+ intern/shaders/draw_debug_draw_display_frag.glsl
+ intern/shaders/draw_debug_draw_display_vert.glsl
+ intern/shaders/draw_debug_info.hh
+ intern/shaders/draw_debug_print_display_frag.glsl
+ intern/shaders/draw_debug_print_display_vert.glsl
+ intern/shaders/draw_resource_finalize_comp.glsl
+ intern/shaders/draw_visibility_comp.glsl
+
+ intern/draw_command_shared.hh
intern/draw_common_shader_shared.h
+ intern/draw_defines.h
intern/draw_shader_shared.h
engines/gpencil/shaders/gpencil_frag.glsl
@@ -644,6 +726,7 @@ add_dependencies(bf_draw bf_dna)
if(WITH_GTESTS)
if(WITH_OPENGL_DRAW_TESTS)
set(TEST_SRC
+ tests/draw_pass_test.cc
tests/draw_testing.cc
tests/shaders_test.cc
diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc
new file mode 100644
index 00000000000..f36a59a4ce6
--- /dev/null
+++ b/source/blender/draw/engines/compositor/compositor_engine.cc
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_listbase.h"
+#include "BLI_math_vec_types.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_ID_enums.h"
+#include "DNA_scene_types.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DRW_render.h"
+
+#include "IMB_colormanagement.h"
+
+#include "COM_context.hh"
+#include "COM_evaluator.hh"
+#include "COM_texture_pool.hh"
+
+#include "GPU_texture.h"
+
+namespace blender::draw::compositor {
+
+class TexturePool : public realtime_compositor::TexturePool {
+ public:
+ GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) override
+ {
+ DrawEngineType *owner = (DrawEngineType *)this;
+ return DRW_texture_pool_query_2d(size.x, size.y, format, owner);
+ }
+};
+
+class Context : public realtime_compositor::Context {
+ private:
+ /* A pointer to the info message of the compositor engine. This is a char array of size
+ * GPU_INFO_SIZE. The message is cleared prior to updating or evaluating the compositor. */
+ char *info_message_;
+
+ public:
+ Context(realtime_compositor::TexturePool &texture_pool, char *info_message)
+ : realtime_compositor::Context(texture_pool), info_message_(info_message)
+ {
+ }
+
+ const Scene *get_scene() const override
+ {
+ return DRW_context_state_get()->scene;
+ }
+
+ int2 get_output_size() override
+ {
+ return int2(float2(DRW_viewport_size_get()));
+ }
+
+ GPUTexture *get_output_texture() override
+ {
+ return DRW_viewport_texture_list_get()->color;
+ }
+
+ GPUTexture *get_input_texture(int UNUSED(view_layer), eScenePassType UNUSED(pass_type)) override
+ {
+ return get_output_texture();
+ }
+
+ StringRef get_view_name() override
+ {
+ const SceneRenderView *view = static_cast<SceneRenderView *>(
+ BLI_findlink(&get_scene()->r.views, DRW_context_state_get()->v3d->multiview_eye));
+ return view->name;
+ }
+
+ void set_info_message(StringRef message) const override
+ {
+ message.copy(info_message_, GPU_INFO_SIZE);
+ }
+};
+
+class Engine {
+ private:
+ TexturePool texture_pool_;
+ Context context_;
+ realtime_compositor::Evaluator evaluator_;
+ /* Stores the viewport size at the time the last compositor evaluation happened. See the
+ * update_viewport_size method for more information. */
+ int2 last_viewport_size_;
+
+ public:
+ Engine(char *info_message)
+ : context_(texture_pool_, info_message),
+ evaluator_(context_, node_tree()),
+ last_viewport_size_(context_.get_output_size())
+ {
+ }
+
+ /* Update the viewport size and evaluate the compositor. */
+ void draw()
+ {
+ update_viewport_size();
+ evaluator_.evaluate();
+ }
+
+ /* If the size of the viewport changed from the last time the compositor was evaluated, update
+ * the viewport size and reset the evaluator. That's because the evaluator compiles the node tree
+ * in a manner that is specifically optimized for the size of the viewport. This should be called
+ * before evaluating the compositor. */
+ void update_viewport_size()
+ {
+ if (last_viewport_size_ == context_.get_output_size()) {
+ return;
+ }
+
+ last_viewport_size_ = context_.get_output_size();
+
+ evaluator_.reset();
+ }
+
+ /* If the compositor node tree changed, reset the evaluator. */
+ void update(const Depsgraph *depsgraph)
+ {
+ if (DEG_id_type_updated(depsgraph, ID_NT)) {
+ evaluator_.reset();
+ }
+ }
+
+ /* Get a reference to the compositor node tree. */
+ static bNodeTree &node_tree()
+ {
+ return *DRW_context_state_get()->scene->nodetree;
+ }
+};
+
+} // namespace blender::draw::compositor
+
+using namespace blender::draw::compositor;
+
+struct COMPOSITOR_Data {
+ DrawEngineType *engine_type;
+ DRWViewportEmptyList *fbl;
+ DRWViewportEmptyList *txl;
+ DRWViewportEmptyList *psl;
+ DRWViewportEmptyList *stl;
+ Engine *instance_data;
+ char info[GPU_INFO_SIZE];
+};
+
+static void compositor_engine_init(void *data)
+{
+ COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data);
+
+ if (!compositor_data->instance_data) {
+ compositor_data->instance_data = new Engine(compositor_data->info);
+ }
+}
+
+static void compositor_engine_free(void *instance_data)
+{
+ Engine *engine = static_cast<Engine *>(instance_data);
+ delete engine;
+}
+
+static void compositor_engine_draw(void *data)
+{
+ const COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data);
+ compositor_data->instance_data->draw();
+}
+
+static void compositor_engine_update(void *data)
+{
+ COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data);
+
+ /* Clear any info message that was set in a previous update. */
+ compositor_data->info[0] = '\0';
+
+ if (compositor_data->instance_data) {
+ compositor_data->instance_data->update(DRW_context_state_get()->depsgraph);
+ }
+}
+
+extern "C" {
+
+static const DrawEngineDataSize compositor_data_size = DRW_VIEWPORT_DATA_SIZE(COMPOSITOR_Data);
+
+DrawEngineType draw_engine_compositor_type = {
+ nullptr, /* next */
+ nullptr, /* prev */
+ N_("Compositor"), /* idname */
+ &compositor_data_size, /* vedata_size */
+ &compositor_engine_init, /* engine_init */
+ nullptr, /* engine_free */
+ &compositor_engine_free, /* instance_free */
+ nullptr, /* cache_init */
+ nullptr, /* cache_populate */
+ nullptr, /* cache_finish */
+ &compositor_engine_draw, /* draw_scene */
+ &compositor_engine_update, /* view_update */
+ nullptr, /* id_update */
+ nullptr, /* render_to_image */
+ nullptr, /* store_metadata */
+};
+}
diff --git a/source/blender/draw/engines/compositor/compositor_engine.h b/source/blender/draw/engines/compositor/compositor_engine.h
new file mode 100644
index 00000000000..5de0de8a0b3
--- /dev/null
+++ b/source/blender/draw/engines/compositor/compositor_engine.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern DrawEngineType draw_engine_compositor_type;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 53ea66bdea0..d805a039e8f 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -25,7 +25,6 @@
* they take into account to create the render passes. When accurate mode is off the number of
* levels is used as the number of cryptomatte samples to take. When accuracy mode is on the number
* of render samples is used.
- *
*/
#include "DRW_engine.h"
@@ -249,7 +248,9 @@ void EEVEE_cryptomatte_object_curves_cache_populate(EEVEE_Data *vedata,
{
BLI_assert(ob->type == OB_CURVES);
Material *material = BKE_object_material_get_eval(ob, CURVES_MATERIAL_NR);
- eevee_cryptomatte_curves_cache_populate(vedata, sldata, ob, NULL, NULL, material);
+ DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
+ vedata, sldata, ob, material, true);
+ DRW_shgroup_curves_create_sub(ob, grp, NULL);
}
void EEVEE_cryptomatte_particle_hair_cache_populate(EEVEE_Data *vedata,
@@ -420,27 +421,31 @@ void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EE
void EEVEE_cryptomatte_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
{
+ /* NOTE: Name channels lowercase rgba so that compression rules check in OpenEXR DWA code uses
+ * lossless compression. Reportedly this naming is the only one which works good from the
+ * interoperability point of view. Using XYZW naming is not portable. */
+
char cryptomatte_pass_name[MAX_NAME];
const short num_passes = eevee_cryptomatte_passes_per_layer(view_layer);
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT) != 0) {
for (short pass = 0; pass < num_passes; pass++) {
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoObject%02d", pass);
RE_engine_register_pass(
- engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
+ engine, scene, view_layer, cryptomatte_pass_name, 4, "rgba", SOCK_RGBA);
}
}
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL) != 0) {
for (short pass = 0; pass < num_passes; pass++) {
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoMaterial%02d", pass);
RE_engine_register_pass(
- engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
+ engine, scene, view_layer, cryptomatte_pass_name, 4, "rgba", SOCK_RGBA);
}
}
if ((view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET) != 0) {
for (short pass = 0; pass < num_passes; pass++) {
BLI_snprintf_rlen(cryptomatte_pass_name, MAX_NAME, "CryptoAsset%02d", pass);
RE_engine_register_pass(
- engine, scene, view_layer, cryptomatte_pass_name, 4, "RGBA", SOCK_RGBA);
+ engine, scene, view_layer, cryptomatte_pass_name, 4, "rgba", SOCK_RGBA);
}
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c
index ffe0863fb9f..5ae4b730cfa 100644
--- a/source/blender/draw/engines/eevee/eevee_engine.c
+++ b/source/blender/draw/engines/eevee/eevee_engine.c
@@ -109,7 +109,7 @@ void EEVEE_cache_populate(void *vedata, Object *ob)
}
if (DRW_object_is_renderable(ob) && (ob_visibility & OB_VISIBLE_SELF)) {
- if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
+ if (ob->type == OB_MESH) {
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
}
else if (ob->type == OB_CURVES) {
@@ -366,7 +366,7 @@ static void eevee_draw_scene(void *vedata)
static void eevee_view_update(void *vedata)
{
EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl;
- if (stl->g_data) {
+ if (stl && stl->g_data) {
stl->g_data->view_updated = true;
}
}
diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c
index 7f722ff1764..614ea0b0892 100644
--- a/source/blender/draw/engines/eevee/eevee_lightcache.c
+++ b/source/blender/draw/engines/eevee/eevee_lightcache.c
@@ -849,7 +849,7 @@ static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake)
DRW_opengl_context_enable();
}
- /* XXX Free the resources contained in the viewlayer data
+ /* XXX: Free the resources contained in the view-layer data
* to be able to free the context before deleting the depsgraph. */
if (lbake->sldata) {
EEVEE_view_layer_data_free(lbake->sldata);
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index efd27c19654..94f29d64628 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -806,7 +806,7 @@ void EEVEE_materials_cache_populate(EEVEE_Data *vedata,
!DRW_state_is_image_render();
/* First get materials for this mesh. */
- if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
+ if (ELEM(ob->type, OB_MESH, OB_SURF)) {
const int materials_len = DRW_cache_object_material_count_get(ob);
EeveeMaterialCache *matcache = BLI_array_alloca(matcache, materials_len);
diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h
index 8d47d80987c..88e56bdc01c 100644
--- a/source/blender/draw/engines/eevee/eevee_private.h
+++ b/source/blender/draw/engines/eevee/eevee_private.h
@@ -815,7 +815,7 @@ typedef struct EEVEE_EffectsInfo {
struct GPUTexture *dof_reduce_input_color_tx;
/* Other */
float prev_persmat[4][4];
- /* Size used by all fullscreen buffers using mipmaps. */
+ /* Size used by all full-screen buffers using mipmaps. */
int hiz_size[2];
/* Lookdev */
int sphere_size;
@@ -1015,7 +1015,7 @@ typedef struct EEVEE_PrivateData {
struct GHash *material_hash;
float background_alpha; /* TODO: find a better place for this. */
bool disable_ligthprobes;
- /* Chosen lightcache: can come from Lookdev or the viewlayer. */
+ /** Chosen light-cache: can come from Lookdev or the view-layer. */
struct LightCache *light_cache;
/* For planar probes */
float planar_texel_size[2];
diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c
index 82944f237ea..c3b909f5fb9 100644
--- a/source/blender/draw/engines/eevee/eevee_render.c
+++ b/source/blender/draw/engines/eevee/eevee_render.c
@@ -224,7 +224,7 @@ void EEVEE_render_cache(void *vedata,
}
if (ob_visibility & OB_VISIBLE_SELF) {
- if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL)) {
+ if (ob->type == OB_MESH) {
EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow);
if (do_cryptomatte) {
EEVEE_cryptomatte_cache_populate(data, sldata, ob);
diff --git a/source/blender/draw/engines/eevee/eevee_sampling.c b/source/blender/draw/engines/eevee/eevee_sampling.c
index a1a3e98f34f..34d3cd74b36 100644
--- a/source/blender/draw/engines/eevee/eevee_sampling.c
+++ b/source/blender/draw/engines/eevee/eevee_sampling.c
@@ -74,7 +74,8 @@ void EEVEE_sample_ellipse(int sample_ofs,
BLI_halton_2d(ht_primes, ht_offset, sample_ofs, ht_point);
- /* Decorelate AA and shadow samples. (see T68594) */
+ /* Decorrelate AA and shadow samples. (see T68594) */
+
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
@@ -97,7 +98,7 @@ void EEVEE_random_rotation_m4(int sample_ofs, float scale, float r_mat[4][4])
BLI_halton_3d(ht_primes, ht_offset, sample_ofs, ht_point);
- /* Decorelate AA and shadow samples. (see T68594) */
+ /* Decorrelate AA and shadow samples. (see T68594) */
ht_point[0] = fmod(ht_point[0] * 1151.0, 1.0);
ht_point[1] = fmod(ht_point[1] * 1069.0, 1.0);
ht_point[2] = fmod(ht_point[2] * 1151.0, 1.0);
diff --git a/source/blender/draw/engines/eevee/eevee_shaders.c b/source/blender/draw/engines/eevee/eevee_shaders.c
index 5709621fc05..04d1168a30d 100644
--- a/source/blender/draw/engines/eevee/eevee_shaders.c
+++ b/source/blender/draw/engines/eevee/eevee_shaders.c
@@ -181,6 +181,7 @@ extern char datatoc_closure_type_lib_glsl[];
extern char datatoc_closure_eval_volume_lib_glsl[];
extern char datatoc_common_uniforms_lib_glsl[];
extern char datatoc_common_utiltex_lib_glsl[];
+extern char datatoc_cryptomatte_lib_glsl[];
extern char datatoc_cryptomatte_frag_glsl[];
extern char datatoc_cryptomatte_vert_glsl[];
extern char datatoc_cubemap_lib_glsl[];
@@ -304,6 +305,7 @@ static void eevee_shader_library_ensure(void)
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_refraction_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_surface_lib);
DRW_SHADER_LIB_ADD(e_data.lib, closure_eval_volume_lib);
+ DRW_SHADER_LIB_ADD(e_data.lib, cryptomatte_lib);
DRW_SHADER_LIB_ADD(e_data.lib, surface_vert);
e_data.surface_lit_frag = DRW_shader_library_create_shader_string(e_data.lib,
@@ -1190,8 +1192,8 @@ Material *EEVEE_material_default_diffuse_get(void)
if (!e_data.diffuse_mat) {
Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default diffuse");
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ NULL, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
ma->use_nodes = true;
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_DIFFUSE);
@@ -1217,8 +1219,8 @@ Material *EEVEE_material_default_glossy_get(void)
if (!e_data.glossy_mat) {
Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default metal");
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ NULL, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
ma->use_nodes = true;
bNode *bsdf = nodeAddStaticNode(NULL, ntree, SH_NODE_BSDF_GLOSSY);
@@ -1246,8 +1248,8 @@ Material *EEVEE_material_default_error_get(void)
if (!e_data.error_mat) {
Material *ma = BKE_id_new_nomain(ID_MA, "EEVEEE default error");
- bNodeTree *ntree = ntreeAddTree(NULL, "Shader Nodetree", ntreeType_Shader->idname);
- ma->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ NULL, &ma->id, "Shader Nodetree", ntreeType_Shader->idname);
ma->use_nodes = true;
/* Use emission and output material to be compatible with both World and Material. */
diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
index 536242f67d8..a3ab4cdb830 100644
--- a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
+++ b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c
@@ -357,7 +357,7 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo,
mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat);
#ifdef DEBUG_CSM
- DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true);
+ DRW_debug_m4_as_bbox(viewprojmat, true, dbg_col);
#endif
}
diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c
index 8223df22ee9..2d96cffb4ba 100644
--- a/source/blender/draw/engines/eevee/eevee_volumes.c
+++ b/source/blender/draw/engines/eevee/eevee_volumes.c
@@ -30,6 +30,7 @@
#include "DEG_depsgraph_query.h"
#include "GPU_capabilities.h"
+#include "GPU_context.h"
#include "GPU_material.h"
#include "GPU_texture.h"
#include "eevee_private.h"
@@ -82,6 +83,13 @@ void EEVEE_volumes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata)
tex_size[1] = (int)ceilf(fmaxf(1.0f, viewport_size[1] / (float)tile_size));
tex_size[2] = max_ii(scene_eval->eevee.volumetric_samples, 1);
+ /* Clamp 3D texture size based on device maximum. */
+ int maxSize = GPU_max_texture_3d_size();
+ BLI_assert(tex_size[0] <= maxSize);
+ tex_size[0] = tex_size[0] > maxSize ? maxSize : tex_size[0];
+ tex_size[1] = tex_size[1] > maxSize ? maxSize : tex_size[1];
+ tex_size[2] = tex_size[2] > maxSize ? maxSize : tex_size[2];
+
common_data->vol_coord_scale[0] = viewport_size[0] / (float)(tile_size * tex_size[0]);
common_data->vol_coord_scale[1] = viewport_size[1] / (float)(tile_size * tex_size[1]);
common_data->vol_coord_scale[2] = 1.0f / viewport_size[0];
@@ -316,12 +324,13 @@ void EEVEE_volumes_cache_object_add(EEVEE_ViewLayerData *sldata,
DRWShadingGroup *grp = DRW_shgroup_create(sh, vedata->psl->volumetric_objects_ps);
grp = DRW_shgroup_volume_create_sub(scene, ob, grp, mat);
- DRW_shgroup_add_material_resources(grp, mat);
if (grp == NULL) {
return;
}
+ DRW_shgroup_add_material_resources(grp, mat);
+
/* TODO(fclem): remove those "unnecessary" UBOs */
DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo);
DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo);
diff --git a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
index 4070ede116b..eeccb393a5c 100644
--- a/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/closure_type_lib.glsl
@@ -6,8 +6,8 @@
#ifndef VOLUMETRICS
-uniform int outputSsrId; /*Default = 1;*/
-uniform int outputSssId; /*Default = 1;*/
+uniform int outputSsrId; /* Default = 1; */
+uniform int outputSssId; /* Default = 1; */
#endif
diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_lib.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_lib.glsl
new file mode 100644
index 00000000000..0f8810ff7ac
--- /dev/null
+++ b/source/blender/draw/engines/eevee/shaders/cryptomatte_lib.glsl
@@ -0,0 +1,19 @@
+/* NOTE: this lib is included in the cryptomatte vertex shader to work around the issue that eevee
+ * cannot use create infos for its static shaders. Keep in sync with draw_shader_shared.h */
+#ifdef HAIR_SHADER
+/* Define the maximum number of attribute we allow in a curves UBO.
+ * This should be kept in sync with `GPU_ATTR_MAX` */
+# define DRW_ATTRIBUTE_PER_CURVES_MAX 15
+
+struct CurvesInfos {
+ /* Per attribute scope, follows loading order.
+ * NOTE: uint as bool in GLSL is 4 bytes.
+ * NOTE: GLSL pad arrays of scalar to 16 bytes (std140). */
+ uvec4 is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX];
+};
+layout(std140) uniform drw_curves
+{
+ CurvesInfos _drw_curves;
+};
+# define drw_curves (_drw_curves)
+#endif
diff --git a/source/blender/draw/engines/eevee/shaders/cryptomatte_vert.glsl b/source/blender/draw/engines/eevee/shaders/cryptomatte_vert.glsl
index f8dbc4772e9..14fbc98469a 100644
--- a/source/blender/draw/engines/eevee/shaders/cryptomatte_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/cryptomatte_vert.glsl
@@ -3,4 +3,5 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_attribute_lib.glsl)
+#pragma BLENDER_REQUIRE(cryptomatte_lib.glsl)
#pragma BLENDER_REQUIRE(surface_vert.glsl)
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 688ae4915e1..7dec30a96b1 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
@@ -124,7 +124,7 @@ void dof_slight_focus_gather(float radius, out vec4 out_color, out float out_wei
dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion);
dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion);
- /* Fix weighting issues on perfectly focus > slight focus transitionning areas. */
+ /* Fix weighting issues on perfectly focus > slight focus transitioning areas. */
if (abs(center_data.coc) < 0.5) {
bg_col = center_data.color;
bg_weight = 1.0;
diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl
index 06dcbeaed66..7230758a93f 100644
--- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl
@@ -67,7 +67,7 @@ void main(void)
/* Occlude the sprite with geometry from the same field
* using a VSM like chebychev test (slide 85). */
float mean = occlusion_data.x;
- float variance = occlusion_data.x;
+ float variance = occlusion_data.y;
shapes *= variance * safe_rcp(variance + sqr(max(cocs * correction_fac - mean, 0.0)));
}
diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
index 9ecc50d9df5..c7f6687d2e2 100644
--- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl
@@ -100,7 +100,7 @@ void main()
coef = 0.315392 * (3.0 * cubevec.y * cubevec.y - 1.0) * 1.0 / 4.0;
}
else if (comp == 7) {
- coef = 1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0;
+ coef = -1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0;
}
else { /* (comp == 8) */
coef = 0.546274 * (cubevec.x * cubevec.x - cubevec.z * cubevec.z) * 1.0 / 4.0;
diff --git a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
index 15c68dc5829..87e944a2ac0 100644
--- a/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/prepass_frag.glsl
@@ -91,3 +91,17 @@ void main()
}
#endif
}
+
+/* Passthrough. */
+float attr_load_temperature_post(float attr)
+{
+ return attr;
+}
+vec4 attr_load_color_post(vec4 attr)
+{
+ return attr;
+}
+vec4 attr_load_uniform(vec4 attr, const uint attr_hash)
+{
+ return attr;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
index 73c4b521b05..8bd60573078 100644
--- a/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/raytrace_lib.glsl
@@ -51,7 +51,8 @@ void raytrace_screenspace_ray_finalize(inout ScreenSpaceRay ray)
}
float ray_len_sqr = len_squared(ray.direction.xyz);
/* Make ray.direction cover one pixel. */
- bool is_more_vertical = abs(ray.direction.x) < abs(ray.direction.y);
+ bool is_more_vertical = abs(ray.direction.x / ssrPixelSize.x) <
+ abs(ray.direction.y / ssrPixelSize.y);
ray.direction /= (is_more_vertical) ? abs(ray.direction.y) : abs(ray.direction.x);
ray.direction *= (is_more_vertical) ? ssrPixelSize.y : ssrPixelSize.x;
/* Clip to segment's end. */
diff --git a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
index 57d70334651..062a40f35c2 100644
--- a/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/shadow_vert.glsl
@@ -152,3 +152,7 @@ vec4 attr_load_color_post(vec4 attr)
{
return attr;
}
+vec4 attr_load_uniform(vec4 attr, const uint attr_hash)
+{
+ return attr;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
index 2a212b757c2..88755705a53 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_frag.glsl
@@ -153,7 +153,7 @@ void main()
vec3 attr_load_orco(vec4 orco)
{
/* Retain precision better than g_data.P (see T99128). */
- return transform_direction(ViewMatrixInverse, normalize(viewPosition));
+ return -normal_view_to_world(viewCameraVec(viewPosition));
}
/* Unsupported. */
vec4 attr_load_tangent(vec4 tangent)
@@ -182,3 +182,7 @@ vec4 attr_load_color_post(vec4 attr)
{
return attr;
}
+vec4 attr_load_uniform(vec4 attr, const uint attr_hash)
+{
+ return attr;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
index 8e1bafe8d92..69762027643 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_lib.glsl
@@ -97,11 +97,12 @@ GlobalData init_globals(void)
GlobalData surf;
# if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE)
- surf.P = -cameraVec(worldPosition);
- surf.N = surf.Ng = -surf.P;
+ surf.P = transform_direction(ViewMatrixInverse, -viewCameraVec(viewPosition));
+ surf.N = surf.Ng = surf.Ni = -surf.P;
surf.ray_length = 0.0;
# else
surf.P = worldPosition;
+ surf.Ni = worldNormal;
surf.N = safe_normalize(worldNormal);
surf.Ng = safe_normalize(cross(dFdx(surf.P), dFdy(surf.P)));
surf.ray_length = distance(surf.P, cameraPos);
@@ -109,6 +110,7 @@ GlobalData init_globals(void)
surf.barycentric_coords = vec2(0.0);
surf.barycentric_dists = vec3(0.0);
surf.N = (FrontFacing) ? surf.N : -surf.N;
+ surf.Ni = (FrontFacing) ? surf.Ni : -surf.Ni;
# ifdef HAIR_SHADER
vec3 V = cameraVec(surf.P);
/* Shade as a cylinder. */
@@ -123,7 +125,7 @@ GlobalData init_globals(void)
cos_theta = hairThickTime / hairThickness;
}
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
- surf.N = safe_normalize(worldNormal * sin_theta + B * cos_theta);
+ surf.N = surf.Ni = safe_normalize(worldNormal * sin_theta + B * cos_theta);
surf.curve_T = -hairTangent;
/* Costly, but follows cycles per pixel tangent space (not following curve shape). */
surf.curve_B = cross(V, surf.curve_T);
diff --git a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
index 4a3a91b8534..54aad7891dc 100644
--- a/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/surface_vert.glsl
@@ -165,3 +165,7 @@ vec4 attr_load_color_post(vec4 attr)
{
return attr;
}
+vec4 attr_load_uniform(vec4 attr, const uint attr_hash)
+{
+ return attr;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
index c6de723ac25..9ed21fc0bf5 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_frag.glsl
@@ -178,3 +178,7 @@ vec4 attr_load_color_post(vec4 attr)
#endif
return attr;
}
+vec4 attr_load_uniform(vec4 attr, const uint attr_hash)
+{
+ return attr;
+}
diff --git a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
index b3b9c7af19c..2d51fbd9edc 100644
--- a/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
+++ b/source/blender/draw/engines/eevee/shaders/volumetric_vert.glsl
@@ -87,3 +87,8 @@ vec4 attr_load_color_post(vec4 attr)
{
return attr;
}
+
+vec4 attr_load_uniform(vec4 attr, const uint attr_hash)
+{
+ return attr;
+}
diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.cc b/source/blender/draw/engines/eevee_next/eevee_camera.cc
index 1f65f887d46..b9040f0f3ab 100644
--- a/source/blender/draw/engines/eevee_next/eevee_camera.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_camera.cc
@@ -29,10 +29,8 @@ namespace blender::eevee {
void Camera::init()
{
const Object *camera_eval = inst_.camera_eval_object;
- synced_ = false;
- data_.swap();
- CameraData &data = data_.current();
+ CameraData &data = data_;
if (camera_eval) {
const ::Camera *cam = reinterpret_cast<const ::Camera *>(camera_eval->data);
@@ -78,9 +76,7 @@ void Camera::sync()
{
const Object *camera_eval = inst_.camera_eval_object;
- data_.swap();
-
- CameraData &data = data_.current();
+ CameraData &data = data_;
if (inst_.drw_view) {
DRW_view_viewmat_get(inst_.drw_view, data.viewmat.ptr(), false);
@@ -142,14 +138,8 @@ void Camera::sync()
data.equirect_scale = float2(0.0f);
}
- data_.current().push_update();
-
- synced_ = true;
-
- /* Detect changes in parameters. */
- if (data_.current() != data_.previous()) {
- inst_.sampling.reset();
- }
+ data_.initialized = true;
+ data_.push_update();
}
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.hh b/source/blender/draw/engines/eevee_next/eevee_camera.hh
index 3b3586190c4..49f9b14e11b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_camera.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_camera.hh
@@ -82,10 +82,7 @@ class Camera {
private:
Instance &inst_;
- /** Double buffered to detect changes and have history for re-projection. */
- SwapChain<CameraDataBuf, 2> data_;
- /** Detects wrong usage. */
- bool synced_ = false;
+ CameraDataBuf data_;
public:
Camera(Instance &inst) : inst_(inst){};
@@ -99,28 +96,32 @@ class Camera {
**/
const CameraData &data_get() const
{
- BLI_assert(synced_);
- return data_.current();
+ BLI_assert(data_.initialized);
+ return data_;
}
const GPUUniformBuf *ubo_get() const
{
- return data_.current();
+ return data_;
}
bool is_panoramic() const
{
- return eevee::is_panoramic(data_.current().type);
+ return eevee::is_panoramic(data_.type);
}
bool is_orthographic() const
{
- return data_.current().type == CAMERA_ORTHO;
+ return data_.type == CAMERA_ORTHO;
+ }
+ bool is_perspective() const
+ {
+ return data_.type == CAMERA_PERSP;
}
const float3 &position() const
{
- return *reinterpret_cast<const float3 *>(data_.current().viewinv[3]);
+ return *reinterpret_cast<const float3 *>(data_.viewinv[3]);
}
const float3 &forward() const
{
- return *reinterpret_cast<const float3 *>(data_.current().viewinv[2]);
+ return *reinterpret_cast<const float3 *>(data_.viewinv[2]);
}
};
diff --git a/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc
new file mode 100644
index 00000000000..10be121f533
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.cc
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+#include "BKE_cryptomatte.hh"
+
+#include "GPU_material.h"
+
+#include "eevee_cryptomatte.hh"
+#include "eevee_instance.hh"
+#include "eevee_renderbuffers.hh"
+
+namespace blender::eevee {
+
+void Cryptomatte::begin_sync()
+{
+ const eViewLayerEEVEEPassType enabled_passes = static_cast<eViewLayerEEVEEPassType>(
+ inst_.film.enabled_passes_get() &
+ (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET |
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET));
+
+ session_.reset();
+ object_layer_ = nullptr;
+ asset_layer_ = nullptr;
+ material_layer_ = nullptr;
+
+ if (enabled_passes && !inst_.is_viewport()) {
+ session_.reset(BKE_cryptomatte_init_from_view_layer(inst_.view_layer));
+
+ for (const std::string &layer_name :
+ bke::cryptomatte::BKE_cryptomatte_layer_names_get(*session_)) {
+ StringRef layer_name_ref = layer_name;
+ bke::cryptomatte::CryptomatteLayer *layer = bke::cryptomatte::BKE_cryptomatte_layer_get(
+ *session_, layer_name);
+ if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_OBJECT)) {
+ object_layer_ = layer;
+ }
+ else if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_ASSET)) {
+ asset_layer_ = layer;
+ }
+ else if (layer_name_ref.endswith(RE_PASSNAME_CRYPTOMATTE_MATERIAL)) {
+ material_layer_ = layer;
+ }
+ }
+ }
+
+ if (!(enabled_passes &
+ (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET))) {
+ cryptomatte_object_buf.resize(16);
+ }
+}
+
+void Cryptomatte::sync_object(Object *ob, ResourceHandle res_handle)
+{
+ const eViewLayerEEVEEPassType enabled_passes = inst_.film.enabled_passes_get();
+ if (!(enabled_passes &
+ (EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT | EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET))) {
+ return;
+ }
+
+ uint32_t resource_id = res_handle.resource_index();
+ float2 object_hashes(0.0f, 0.0f);
+
+ if (enabled_passes & EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT) {
+ object_hashes[0] = register_id(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT, ob->id);
+ }
+
+ if (enabled_passes & EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET) {
+ Object *asset = ob;
+ while (asset->parent) {
+ asset = asset->parent;
+ }
+ object_hashes[1] = register_id(EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET, asset->id);
+ }
+ cryptomatte_object_buf.get_or_resize(resource_id) = object_hashes;
+}
+
+void Cryptomatte::sync_material(const ::Material *material)
+{
+ /* Material crypto hashes are generated during shader codegen stage. We only need to register
+ * them to store inside the metadata. */
+ if (material_layer_ && material) {
+ material_layer_->add_ID(material->id);
+ }
+}
+
+void Cryptomatte::end_sync()
+{
+ cryptomatte_object_buf.push_update();
+
+ object_layer_ = nullptr;
+ asset_layer_ = nullptr;
+ material_layer_ = nullptr;
+}
+
+float Cryptomatte::register_id(const eViewLayerEEVEEPassType layer, const ID &id) const
+{
+ BLI_assert(ELEM(layer,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL));
+
+ uint32_t cryptomatte_hash = 0;
+ if (session_) {
+ if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT) {
+ BLI_assert(object_layer_);
+ cryptomatte_hash = object_layer_->add_ID(id);
+ }
+ else if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET) {
+ BLI_assert(asset_layer_);
+ cryptomatte_hash = asset_layer_->add_ID(id);
+ }
+ else if (layer == EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL) {
+ BLI_assert(material_layer_);
+ cryptomatte_hash = material_layer_->add_ID(id);
+ }
+ }
+ else {
+ const char *name = &id.name[2];
+ const int name_len = BLI_strnlen(name, MAX_NAME - 2);
+ cryptomatte_hash = BKE_cryptomatte_hash(name, name_len);
+ }
+
+ return BKE_cryptomatte_hash_to_float(cryptomatte_hash);
+}
+
+void Cryptomatte::store_metadata(RenderResult *render_result)
+{
+ if (session_) {
+ BKE_cryptomatte_store_metadata(&*session_, render_result, inst_.view_layer);
+ }
+}
+
+} // namespace blender::eevee \ No newline at end of file
diff --git a/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh
new file mode 100644
index 00000000000..86ab3d97b4b
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_cryptomatte.hh
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Cryptomatte.
+ *
+ * During rasterization, cryptomatte hashes are stored into a single array texture.
+ * The film pass then resamples this texture using pixel filter weighting.
+ * Each cryptomatte layer can hold N samples. These are stored in sequential layers
+ * of the array texture. The samples are sorted and merged only for final rendering.
+ */
+
+#pragma once
+
+#include "eevee_shader_shared.hh"
+
+#include "BKE_cryptomatte.hh"
+
+extern "C" {
+struct Material;
+struct CryptomatteSession;
+}
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Cryptomatte
+ * \{ */
+
+class Cryptomatte {
+ private:
+ class Instance &inst_;
+
+ bke::cryptomatte::CryptomatteSessionPtr session_;
+
+ /* Cached pointer to the cryptomatte layer instances. */
+ bke::cryptomatte::CryptomatteLayer *object_layer_ = nullptr;
+ bke::cryptomatte::CryptomatteLayer *asset_layer_ = nullptr;
+ bke::cryptomatte::CryptomatteLayer *material_layer_ = nullptr;
+
+ /** Contains per object hashes (object and asset hash). Indexed by resource ID. */
+ CryptomatteObjectBuf cryptomatte_object_buf;
+
+ public:
+ Cryptomatte(Instance &inst) : inst_(inst){};
+
+ void begin_sync();
+ void sync_object(Object *ob, ResourceHandle res_handle);
+ void sync_material(const ::Material *material);
+ void end_sync();
+
+ template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
+ {
+ pass->bind_ssbo(CRYPTOMATTE_BUF_SLOT, &cryptomatte_object_buf);
+ }
+
+ /* Register ID to use inside cryptomatte layer and returns associated hash as float. */
+ float register_id(const eViewLayerEEVEEPassType layer, const ID &id) const;
+ void store_metadata(RenderResult *render_result);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh
index 1e7979b594e..248dfae6df9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_defines.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh
@@ -11,12 +11,18 @@
#pragma once
-/**
- * Number of items in a culling batch. Needs to be Power of 2. Must be <= to 65536.
- * Current limiting factor is the sorting phase which is single pass and only sort within a
- * thread-group which maximum size is 1024.
- */
-#define CULLING_BATCH_SIZE 1024
+/* Hierarchical Z down-sampling. */
+#define HIZ_MIP_COUNT 8
+/* NOTE: The shader is written to update 5 mipmaps using LDS. */
+#define HIZ_GROUP_SIZE 32
+
+/* Avoid too much overhead caused by resizing the light buffers too many time. */
+#define LIGHT_CHUNK 256
+
+#define CULLING_SELECT_GROUP_SIZE 256
+#define CULLING_SORT_GROUP_SIZE 256
+#define CULLING_ZBIN_GROUP_SIZE 1024
+#define CULLING_TILE_GROUP_SIZE 1024
/**
* IMPORTANT: Some data packing are tweaked for these values.
@@ -34,14 +40,68 @@
#define SHADOW_MAX_PAGE 4096
#define SHADOW_PAGE_PER_ROW 64
-#define HIZ_MIP_COUNT 6u
-/* Group size is 2x smaller because we simply copy the level 0. */
-#define HIZ_GROUP_SIZE 1u << (HIZ_MIP_COUNT - 2u)
-
+/* Ray-tracing. */
#define RAYTRACE_GROUP_SIZE 16
#define RAYTRACE_MAX_TILES (16384 / RAYTRACE_GROUP_SIZE) * (16384 / RAYTRACE_GROUP_SIZE)
/* Minimum visibility size. */
#define LIGHTPROBE_FILTER_VIS_GROUP_SIZE 16
+/* Film. */
#define FILM_GROUP_SIZE 16
+
+/* Motion Blur. */
+#define MOTION_BLUR_GROUP_SIZE 32
+#define MOTION_BLUR_DILATE_GROUP_SIZE 512
+
+/* Depth Of Field. */
+#define DOF_TILES_SIZE 8
+#define DOF_TILES_FLATTEN_GROUP_SIZE DOF_TILES_SIZE
+#define DOF_TILES_DILATE_GROUP_SIZE 8
+#define DOF_BOKEH_LUT_SIZE 32
+#define DOF_MAX_SLIGHT_FOCUS_RADIUS 5
+#define DOF_SLIGHT_FOCUS_SAMPLE_MAX 16
+#define DOF_MIP_COUNT 4
+#define DOF_REDUCE_GROUP_SIZE (1 << (DOF_MIP_COUNT - 1))
+#define DOF_DEFAULT_GROUP_SIZE 32
+#define DOF_STABILIZE_GROUP_SIZE 16
+#define DOF_FILTER_GROUP_SIZE 8
+#define DOF_GATHER_GROUP_SIZE DOF_TILES_SIZE
+#define DOF_RESOLVE_GROUP_SIZE (DOF_TILES_SIZE * 2)
+
+/* Resource bindings. */
+
+/* Texture. */
+#define RBUFS_UTILITY_TEX_SLOT 14
+
+/* Images. */
+#define RBUFS_NORMAL_SLOT 0
+#define RBUFS_LIGHT_SLOT 1
+#define RBUFS_DIFF_COLOR_SLOT 2
+#define RBUFS_SPEC_COLOR_SLOT 3
+#define RBUFS_EMISSION_SLOT 4
+#define RBUFS_AOV_COLOR_SLOT 5
+#define RBUFS_AOV_VALUE_SLOT 6
+#define RBUFS_CRYPTOMATTE_SLOT 7
+
+/* Uniform Buffers. */
+/* Only during prepass. */
+#define VELOCITY_CAMERA_PREV_BUF 3
+#define VELOCITY_CAMERA_CURR_BUF 4
+#define VELOCITY_CAMERA_NEXT_BUF 5
+
+/* Storage Buffers. */
+#define LIGHT_CULL_BUF_SLOT 0
+#define LIGHT_BUF_SLOT 1
+#define LIGHT_ZBIN_BUF_SLOT 2
+#define LIGHT_TILE_BUF_SLOT 3
+#define RBUFS_AOV_BUF_SLOT 5
+#define SAMPLING_BUF_SLOT 6
+#define CRYPTOMATTE_BUF_SLOT 7
+
+/* Only during pre-pass. */
+#define VELOCITY_OBJ_PREV_BUF_SLOT 0
+#define VELOCITY_OBJ_NEXT_BUF_SLOT 1
+#define VELOCITY_GEO_PREV_BUF_SLOT 2
+#define VELOCITY_GEO_NEXT_BUF_SLOT 3
+#define VELOCITY_INDIRECTION_BUF_SLOT 4
diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc
new file mode 100644
index 00000000000..bc0891ceb92
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc
@@ -0,0 +1,761 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Depth of field post process effect.
+ *
+ * There are 2 methods to achieve this effect.
+ * - The first uses projection matrix offsetting and sample accumulation to give
+ * reference quality depth of field. But this needs many samples to hide the
+ * under-sampling.
+ * - The second one is a post-processing based one. It follows the
+ * implementation described in the presentation
+ * "Life of a Bokeh - Siggraph 2018" from Guillaume Abadie.
+ * There are some difference with our actual implementation that prioritize quality.
+ */
+
+#include "DRW_render.h"
+
+#include "BKE_camera.h"
+#include "DNA_camera_types.h"
+
+#include "GPU_platform.h"
+#include "GPU_texture.h"
+#include "GPU_uniform_buffer.h"
+
+#include "eevee_camera.hh"
+#include "eevee_instance.hh"
+#include "eevee_sampling.hh"
+#include "eevee_shader.hh"
+#include "eevee_shader_shared.hh"
+
+#include "eevee_depth_of_field.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Depth of field
+ * \{ */
+
+void DepthOfField::init()
+{
+ const SceneEEVEE &sce_eevee = inst_.scene->eevee;
+ const Object *camera_object_eval = inst_.camera_eval_object;
+ const ::Camera *camera = (camera_object_eval) ?
+ reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
+ nullptr;
+ if (camera == nullptr) {
+ /* Set to invalid value for update detection */
+ data_.scatter_color_threshold = -1.0f;
+ return;
+ }
+ /* Reminder: These are parameters not interpolated by motion blur. */
+ int update = 0;
+ int sce_flag = sce_eevee.flag;
+ update += assign_if_different(do_jitter_, (sce_flag & SCE_EEVEE_DOF_JITTER) != 0);
+ update += assign_if_different(user_overblur_, sce_eevee.bokeh_overblur / 100.0f);
+ update += assign_if_different(fx_max_coc_, sce_eevee.bokeh_max_size);
+ update += assign_if_different(data_.scatter_color_threshold, sce_eevee.bokeh_threshold);
+ update += assign_if_different(data_.scatter_neighbor_max_color, sce_eevee.bokeh_neighbor_max);
+ update += assign_if_different(data_.bokeh_blades, float(camera->dof.aperture_blades));
+ if (update > 0) {
+ inst_.sampling.reset();
+ }
+}
+
+void DepthOfField::sync()
+{
+ const Camera &camera = inst_.camera;
+ const Object *camera_object_eval = inst_.camera_eval_object;
+ const ::Camera *camera_data = (camera_object_eval) ?
+ reinterpret_cast<const ::Camera *>(camera_object_eval->data) :
+ nullptr;
+
+ int update = 0;
+
+ if (camera_data == nullptr || (camera_data->dof.flag & CAM_DOF_ENABLED) == 0) {
+ update += assign_if_different(jitter_radius_, 0.0f);
+ update += assign_if_different(fx_radius_, 0.0f);
+ if (update > 0) {
+ inst_.sampling.reset();
+ }
+ return;
+ }
+
+ float2 anisotropic_scale = {clamp_f(1.0f / camera_data->dof.aperture_ratio, 1e-5f, 1.0f),
+ clamp_f(camera_data->dof.aperture_ratio, 1e-5f, 1.0f)};
+ update += assign_if_different(data_.bokeh_anisotropic_scale, anisotropic_scale);
+ update += assign_if_different(data_.bokeh_rotation, camera_data->dof.aperture_rotation);
+ update += assign_if_different(focus_distance_,
+ BKE_camera_object_dof_distance(camera_object_eval));
+ data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale;
+
+ float fstop = max_ff(camera_data->dof.aperture_fstop, 1e-5f);
+
+ if (update) {
+ inst_.sampling.reset();
+ }
+
+ float aperture = 1.0f / (2.0f * fstop);
+ if (camera.is_perspective()) {
+ aperture *= camera_data->lens * 1e-3f;
+ }
+
+ if (camera.is_orthographic()) {
+ /* FIXME: Why is this needed? Some kind of implicit unit conversion? */
+ aperture *= 0.04f;
+ /* Really strange behavior from Cycles but replicating. */
+ focus_distance_ += camera.data_get().clip_near;
+ }
+
+ if (camera.is_panoramic()) {
+ /* FIXME: Eyeballed. */
+ aperture *= 0.185f;
+ }
+
+ if (camera_data->dof.aperture_ratio < 1.0) {
+ /* If ratio is scaling the bokeh outwards, we scale the aperture so that
+ * the gather kernel size will encompass the maximum axis. */
+ aperture /= max_ff(camera_data->dof.aperture_ratio, 1e-5f);
+ }
+
+ float jitter_radius, fx_radius;
+
+ /* Balance blur radius between fx dof and jitter dof. */
+ if (do_jitter_ && (inst_.sampling.dof_ring_count_get() > 0) && !camera.is_panoramic() &&
+ !inst_.is_viewport()) {
+ /* Compute a minimal overblur radius to fill the gaps between the samples.
+ * This is just the simplified form of dividing the area of the bokeh by
+ * the number of samples. */
+ float minimal_overblur = 1.0f / sqrtf(inst_.sampling.dof_sample_count_get());
+
+ fx_radius = (minimal_overblur + user_overblur_) * aperture;
+ /* Avoid dilating the shape. Over-blur only soften. */
+ jitter_radius = max_ff(0.0f, aperture - fx_radius);
+ }
+ else {
+ jitter_radius = 0.0f;
+ fx_radius = aperture;
+ }
+
+ /* Disable post fx if result wouldn't be noticeable. */
+ if (fx_max_coc_ <= 0.5f) {
+ fx_radius = 0.0f;
+ }
+
+ update += assign_if_different(jitter_radius_, jitter_radius);
+ update += assign_if_different(fx_radius_, fx_radius);
+ if (update > 0) {
+ inst_.sampling.reset();
+ }
+
+ if (fx_radius_ == 0.0f) {
+ return;
+ }
+
+ /* TODO(fclem): Once we render into multiple view, we will need to use the maximum resolution. */
+ int2 max_render_res = inst_.film.render_extent_get();
+ int2 half_res = math::divide_ceil(max_render_res, int2(2));
+ int2 reduce_size = math::ceil_to_multiple(half_res, int2(DOF_REDUCE_GROUP_SIZE));
+
+ data_.gather_uv_fac = 1.0f / float2(reduce_size);
+
+ /* Now that we know the maximum render resolution of every view, using depth of field, allocate
+ * the reduced buffers. Color needs to be signed format here. See note in shader for
+ * explanation. Do not use texture pool because of needs mipmaps. */
+ reduced_color_tx_.ensure_2d(GPU_RGBA16F, reduce_size, nullptr, DOF_MIP_COUNT);
+ reduced_coc_tx_.ensure_2d(GPU_R16F, reduce_size, nullptr, DOF_MIP_COUNT);
+ reduced_color_tx_.ensure_mip_views();
+ reduced_coc_tx_.ensure_mip_views();
+
+ /* Resize the scatter list to contain enough entry to cover half the screen with sprites (which
+ * is unlikely due to local contrast test). */
+ data_.scatter_max_rect = (reduced_color_tx_.pixel_count() / 4) / 2;
+ scatter_fg_list_buf_.resize(data_.scatter_max_rect);
+ scatter_bg_list_buf_.resize(data_.scatter_max_rect);
+
+ bokeh_lut_pass_sync();
+ setup_pass_sync();
+ stabilize_pass_sync();
+ downsample_pass_sync();
+ reduce_pass_sync();
+ tiles_flatten_pass_sync();
+ tiles_dilate_pass_sync();
+ gather_pass_sync();
+ filter_pass_sync();
+ scatter_pass_sync();
+ hole_fill_pass_sync();
+ resolve_pass_sync();
+}
+
+void DepthOfField::jitter_apply(float4x4 &winmat, float4x4 &viewmat)
+{
+ if (jitter_radius_ == 0.0f) {
+ return;
+ }
+
+ float radius, theta;
+ inst_.sampling.dof_disk_sample_get(&radius, &theta);
+
+ if (data_.bokeh_blades >= 3.0f) {
+ theta = circle_to_polygon_angle(data_.bokeh_blades, theta);
+ radius *= circle_to_polygon_radius(data_.bokeh_blades, theta);
+ }
+ radius *= jitter_radius_;
+ theta += data_.bokeh_rotation;
+
+ /* Sample in View Space. */
+ float2 sample = float2(radius * cosf(theta), radius * sinf(theta));
+ sample *= data_.bokeh_anisotropic_scale;
+ /* Convert to NDC Space. */
+ float3 jitter = float3(UNPACK2(sample), -focus_distance_);
+ float3 center = float3(0.0f, 0.0f, -focus_distance_);
+ mul_project_m4_v3(winmat.ptr(), jitter);
+ mul_project_m4_v3(winmat.ptr(), center);
+
+ const bool is_ortho = (winmat[2][3] != -1.0f);
+ if (is_ortho) {
+ sample *= focus_distance_;
+ }
+ /* Translate origin. */
+ sub_v2_v2(viewmat[3], sample);
+ /* Skew winmat Z axis. */
+ add_v2_v2(winmat[2], center - jitter);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Passes setup.
+ * \{ */
+
+void DepthOfField::bokeh_lut_pass_sync()
+{
+ const bool has_anisotropy = data_.bokeh_anisotropic_scale != float2(1.0f);
+ if (!has_anisotropy && (data_.bokeh_blades == 0.0)) {
+ /* No need for LUTs in these cases. */
+ use_bokeh_lut_ = false;
+ return;
+ }
+ use_bokeh_lut_ = true;
+
+ /* Precompute bokeh texture. */
+ bokeh_lut_ps_.init();
+ bokeh_lut_ps_.shader_set(inst_.shaders.static_shader_get(DOF_BOKEH_LUT));
+ bokeh_lut_ps_.bind_ubo("dof_buf", data_);
+ bokeh_lut_ps_.bind_image("out_gather_lut_img", &bokeh_gather_lut_tx_);
+ bokeh_lut_ps_.bind_image("out_scatter_lut_img", &bokeh_scatter_lut_tx_);
+ bokeh_lut_ps_.bind_image("out_resolve_lut_img", &bokeh_resolve_lut_tx_);
+ bokeh_lut_ps_.dispatch(int3(1, 1, 1));
+}
+
+void DepthOfField::setup_pass_sync()
+{
+ RenderBuffers &render_buffers = inst_.render_buffers;
+
+ setup_ps_.init();
+ setup_ps_.shader_set(inst_.shaders.static_shader_get(DOF_SETUP));
+ setup_ps_.bind_texture("color_tx", &input_color_tx_, no_filter);
+ setup_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
+ setup_ps_.bind_ubo("dof_buf", data_);
+ setup_ps_.bind_image("out_color_img", &setup_color_tx_);
+ setup_ps_.bind_image("out_coc_img", &setup_coc_tx_);
+ setup_ps_.dispatch(&dispatch_setup_size_);
+ setup_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
+}
+
+void DepthOfField::stabilize_pass_sync()
+{
+ RenderBuffers &render_buffers = inst_.render_buffers;
+ VelocityModule &velocity = inst_.velocity;
+
+ stabilize_ps_.init();
+ stabilize_ps_.shader_set(inst_.shaders.static_shader_get(DOF_STABILIZE));
+ stabilize_ps_.bind_ubo("camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS]));
+ stabilize_ps_.bind_ubo("camera_curr", &(*velocity.camera_steps[STEP_CURRENT]));
+ /* This is only for temporal stability. The next step is not needed. */
+ stabilize_ps_.bind_ubo("camera_next", &(*velocity.camera_steps[STEP_PREVIOUS]));
+ stabilize_ps_.bind_texture("coc_tx", &setup_coc_tx_, no_filter);
+ stabilize_ps_.bind_texture("color_tx", &setup_color_tx_, no_filter);
+ stabilize_ps_.bind_texture("velocity_tx", &render_buffers.vector_tx, no_filter);
+ stabilize_ps_.bind_texture("in_history_tx", &stabilize_input_, with_filter);
+ stabilize_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
+ stabilize_ps_.bind_ubo("dof_buf", data_);
+ stabilize_ps_.push_constant("use_history", &stabilize_valid_history_, 1);
+ stabilize_ps_.bind_image("out_coc_img", reduced_coc_tx_.mip_view(0));
+ stabilize_ps_.bind_image("out_color_img", reduced_color_tx_.mip_view(0));
+ stabilize_ps_.bind_image("out_history_img", &stabilize_output_tx_);
+ stabilize_ps_.dispatch(&dispatch_stabilize_size_);
+ stabilize_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
+}
+
+void DepthOfField::downsample_pass_sync()
+{
+ downsample_ps_.init();
+ downsample_ps_.shader_set(inst_.shaders.static_shader_get(DOF_DOWNSAMPLE));
+ downsample_ps_.bind_texture("color_tx", reduced_color_tx_.mip_view(0), no_filter);
+ downsample_ps_.bind_texture("coc_tx", reduced_coc_tx_.mip_view(0), no_filter);
+ downsample_ps_.bind_image("out_color_img", &downsample_tx_);
+ downsample_ps_.dispatch(&dispatch_downsample_size_);
+ downsample_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
+}
+
+void DepthOfField::reduce_pass_sync()
+{
+ reduce_ps_.init();
+ reduce_ps_.shader_set(inst_.shaders.static_shader_get(DOF_REDUCE));
+ reduce_ps_.bind_ubo("dof_buf", data_);
+ reduce_ps_.bind_texture("downsample_tx", &downsample_tx_, no_filter);
+ reduce_ps_.bind_ssbo("scatter_fg_list_buf", scatter_fg_list_buf_);
+ reduce_ps_.bind_ssbo("scatter_bg_list_buf", scatter_bg_list_buf_);
+ reduce_ps_.bind_ssbo("scatter_fg_indirect_buf", scatter_fg_indirect_buf_);
+ reduce_ps_.bind_ssbo("scatter_bg_indirect_buf", scatter_bg_indirect_buf_);
+ reduce_ps_.bind_image("inout_color_lod0_img", reduced_color_tx_.mip_view(0));
+ reduce_ps_.bind_image("out_color_lod1_img", reduced_color_tx_.mip_view(1));
+ reduce_ps_.bind_image("out_color_lod2_img", reduced_color_tx_.mip_view(2));
+ reduce_ps_.bind_image("out_color_lod3_img", reduced_color_tx_.mip_view(3));
+ reduce_ps_.bind_image("in_coc_lod0_img", reduced_coc_tx_.mip_view(0));
+ reduce_ps_.bind_image("out_coc_lod1_img", reduced_coc_tx_.mip_view(1));
+ reduce_ps_.bind_image("out_coc_lod2_img", reduced_coc_tx_.mip_view(2));
+ reduce_ps_.bind_image("out_coc_lod3_img", reduced_coc_tx_.mip_view(3));
+ reduce_ps_.dispatch(&dispatch_reduce_size_);
+ /* NOTE: Command buffer barrier is done automatically by the GPU backend. */
+ reduce_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_STORAGE);
+}
+
+void DepthOfField::tiles_flatten_pass_sync()
+{
+ tiles_flatten_ps_.init();
+ tiles_flatten_ps_.shader_set(inst_.shaders.static_shader_get(DOF_TILES_FLATTEN));
+ /* NOTE(fclem): We should use the reduced_coc_tx_ as it is stable, but we need the slight focus
+ * flag from the setup pass. A better way would be to do the brute-force in focus gather without
+ * this. */
+ tiles_flatten_ps_.bind_texture("coc_tx", &setup_coc_tx_, no_filter);
+ tiles_flatten_ps_.bind_image("out_tiles_fg_img", &tiles_fg_tx_.current());
+ tiles_flatten_ps_.bind_image("out_tiles_bg_img", &tiles_bg_tx_.current());
+ tiles_flatten_ps_.dispatch(&dispatch_tiles_flatten_size_);
+ tiles_flatten_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
+}
+
+void DepthOfField::tiles_dilate_pass_sync()
+{
+ for (int pass = 0; pass < 2; pass++) {
+ PassSimple &drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_;
+ eShaderType sh_type = (pass == 0) ? DOF_TILES_DILATE_MINMAX : DOF_TILES_DILATE_MINABS;
+ drw_pass.init();
+ drw_pass.shader_set(inst_.shaders.static_shader_get(sh_type));
+ drw_pass.bind_image("in_tiles_fg_img", &tiles_fg_tx_.previous());
+ drw_pass.bind_image("in_tiles_bg_img", &tiles_bg_tx_.previous());
+ drw_pass.bind_image("out_tiles_fg_img", &tiles_fg_tx_.current());
+ drw_pass.bind_image("out_tiles_bg_img", &tiles_bg_tx_.current());
+ drw_pass.push_constant("ring_count", &tiles_dilate_ring_count_, 1);
+ drw_pass.push_constant("ring_width_multiplier", &tiles_dilate_ring_width_mul_, 1);
+ drw_pass.dispatch(&dispatch_tiles_dilate_size_);
+ drw_pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ }
+}
+
+void DepthOfField::gather_pass_sync()
+{
+ for (int pass = 0; pass < 2; pass++) {
+ PassSimple &drw_pass = (pass == 0) ? gather_fg_ps_ : gather_bg_ps_;
+ SwapChain<TextureFromPool, 2> &color_chain = (pass == 0) ? color_fg_tx_ : color_bg_tx_;
+ SwapChain<TextureFromPool, 2> &weight_chain = (pass == 0) ? weight_fg_tx_ : weight_bg_tx_;
+ eShaderType sh_type = (pass == 0) ?
+ (use_bokeh_lut_ ? DOF_GATHER_FOREGROUND_LUT :
+ DOF_GATHER_FOREGROUND) :
+ (use_bokeh_lut_ ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND);
+ drw_pass.init();
+ inst_.sampling.bind_resources(&drw_pass);
+ drw_pass.shader_set(inst_.shaders.static_shader_get(sh_type));
+ drw_pass.bind_ubo("dof_buf", data_);
+ drw_pass.bind_texture("color_bilinear_tx", reduced_color_tx_, gather_bilinear);
+ drw_pass.bind_texture("color_tx", reduced_color_tx_, gather_nearest);
+ drw_pass.bind_texture("coc_tx", reduced_coc_tx_, gather_nearest);
+ drw_pass.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current());
+ drw_pass.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current());
+ drw_pass.bind_image("out_color_img", &color_chain.current());
+ drw_pass.bind_image("out_weight_img", &weight_chain.current());
+ drw_pass.bind_image("out_occlusion_img", &occlusion_tx_);
+ drw_pass.bind_texture("bokeh_lut_tx", &bokeh_gather_lut_tx_);
+ drw_pass.dispatch(&dispatch_gather_size_);
+ drw_pass.barrier(GPU_BARRIER_TEXTURE_FETCH);
+ }
+}
+
+void DepthOfField::filter_pass_sync()
+{
+ for (int pass = 0; pass < 2; pass++) {
+ PassSimple &drw_pass = (pass == 0) ? filter_fg_ps_ : filter_bg_ps_;
+ SwapChain<TextureFromPool, 2> &color_chain = (pass == 0) ? color_fg_tx_ : color_bg_tx_;
+ SwapChain<TextureFromPool, 2> &weight_chain = (pass == 0) ? weight_fg_tx_ : weight_bg_tx_;
+ drw_pass.init();
+ drw_pass.shader_set(inst_.shaders.static_shader_get(DOF_FILTER));
+ drw_pass.bind_texture("color_tx", &color_chain.previous());
+ drw_pass.bind_texture("weight_tx", &weight_chain.previous());
+ drw_pass.bind_image("out_color_img", &color_chain.current());
+ drw_pass.bind_image("out_weight_img", &weight_chain.current());
+ drw_pass.dispatch(&dispatch_filter_size_);
+ drw_pass.barrier(GPU_BARRIER_TEXTURE_FETCH);
+ }
+}
+
+void DepthOfField::scatter_pass_sync()
+{
+ for (int pass = 0; pass < 2; pass++) {
+ PassSimple &drw_pass = (pass == 0) ? scatter_fg_ps_ : scatter_bg_ps_;
+ drw_pass.init();
+ drw_pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL);
+ drw_pass.shader_set(inst_.shaders.static_shader_get(DOF_SCATTER));
+ drw_pass.push_constant("use_bokeh_lut", use_bokeh_lut_);
+ drw_pass.bind_texture("bokeh_lut_tx", &bokeh_scatter_lut_tx_);
+ drw_pass.bind_texture("occlusion_tx", &occlusion_tx_);
+ if (pass == 0) {
+ drw_pass.bind_ssbo("scatter_list_buf", scatter_fg_list_buf_);
+ drw_pass.draw_procedural_indirect(GPU_PRIM_TRI_STRIP, scatter_fg_indirect_buf_);
+ /* Avoid background gather pass writing to the occlusion_tx mid pass. */
+ drw_pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ }
+ else {
+ drw_pass.bind_ssbo("scatter_list_buf", scatter_bg_list_buf_);
+ drw_pass.draw_procedural_indirect(GPU_PRIM_TRI_STRIP, scatter_bg_indirect_buf_);
+ }
+ }
+}
+
+void DepthOfField::hole_fill_pass_sync()
+{
+ hole_fill_ps_.init();
+ inst_.sampling.bind_resources(&hole_fill_ps_);
+ hole_fill_ps_.shader_set(inst_.shaders.static_shader_get(DOF_GATHER_HOLE_FILL));
+ hole_fill_ps_.bind_ubo("dof_buf", data_);
+ hole_fill_ps_.bind_texture("color_bilinear_tx", reduced_color_tx_, gather_bilinear);
+ hole_fill_ps_.bind_texture("color_tx", reduced_color_tx_, gather_nearest);
+ hole_fill_ps_.bind_texture("coc_tx", reduced_coc_tx_, gather_nearest);
+ hole_fill_ps_.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current());
+ hole_fill_ps_.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current());
+ hole_fill_ps_.bind_image("out_color_img", &hole_fill_color_tx_);
+ hole_fill_ps_.bind_image("out_weight_img", &hole_fill_weight_tx_);
+ hole_fill_ps_.dispatch(&dispatch_gather_size_);
+ hole_fill_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
+}
+
+void DepthOfField::resolve_pass_sync()
+{
+ eGPUSamplerState with_filter = GPU_SAMPLER_FILTER;
+ RenderBuffers &render_buffers = inst_.render_buffers;
+ eShaderType sh_type = use_bokeh_lut_ ? DOF_RESOLVE_LUT : DOF_RESOLVE;
+
+ resolve_ps_.init();
+ inst_.sampling.bind_resources(&resolve_ps_);
+ resolve_ps_.shader_set(inst_.shaders.static_shader_get(sh_type));
+ resolve_ps_.bind_ubo("dof_buf", data_);
+ resolve_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
+ resolve_ps_.bind_texture("color_tx", &input_color_tx_, no_filter);
+ resolve_ps_.bind_texture("stable_color_tx", &resolve_stable_color_tx_, no_filter);
+ resolve_ps_.bind_texture("color_bg_tx", &color_bg_tx_.current(), with_filter);
+ resolve_ps_.bind_texture("color_fg_tx", &color_fg_tx_.current(), with_filter);
+ resolve_ps_.bind_image("in_tiles_fg_img", &tiles_fg_tx_.current());
+ resolve_ps_.bind_image("in_tiles_bg_img", &tiles_bg_tx_.current());
+ resolve_ps_.bind_texture("weight_bg_tx", &weight_bg_tx_.current());
+ resolve_ps_.bind_texture("weight_fg_tx", &weight_fg_tx_.current());
+ resolve_ps_.bind_texture("color_hole_fill_tx", &hole_fill_color_tx_);
+ resolve_ps_.bind_texture("weight_hole_fill_tx", &hole_fill_weight_tx_);
+ resolve_ps_.bind_texture("bokeh_lut_tx", &bokeh_resolve_lut_tx_);
+ resolve_ps_.bind_image("out_color_img", &output_color_tx_);
+ resolve_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
+ resolve_ps_.dispatch(&dispatch_resolve_size_);
+ resolve_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Post-FX Rendering.
+ * \{ */
+
+/* Similar to Film::update_sample_table() but with constant filter radius and constant sample
+ * count. */
+void DepthOfField::update_sample_table()
+{
+ float2 subpixel_offset = inst_.film.pixel_jitter_get();
+ /* Since the film jitter is in full-screen res, divide by 2 to get the jitter in half res. */
+ subpixel_offset *= 0.5;
+
+ /* Same offsets as in dof_spatial_filtering(). */
+ const std::array<int2, 4> plus_offsets = {int2(-1, 0), int2(0, -1), int2(1, 0), int2(0, 1)};
+
+ const float radius = 1.5f;
+ int i = 0;
+ for (int2 offset : plus_offsets) {
+ float2 pixel_ofs = float2(offset) - subpixel_offset;
+ data_.filter_samples_weight[i++] = film_filter_weight(radius, math::length_squared(pixel_ofs));
+ }
+ data_.filter_center_weight = film_filter_weight(radius, math::length_squared(subpixel_offset));
+}
+
+void DepthOfField::render(View &view,
+ GPUTexture **input_tx,
+ GPUTexture **output_tx,
+ DepthOfFieldBuffer &dof_buffer)
+{
+ if (fx_radius_ == 0.0f) {
+ return;
+ }
+
+ input_color_tx_ = *input_tx;
+ output_color_tx_ = *output_tx;
+ extent_ = {GPU_texture_width(input_color_tx_), GPU_texture_height(input_color_tx_)};
+
+ {
+ const CameraData &cam_data = inst_.camera.data_get();
+ data_.camera_type = cam_data.type;
+ /* OPTI(fclem) Could be optimized. */
+ float3 jitter = float3(fx_radius_, 0.0f, -focus_distance_);
+ float3 center = float3(0.0f, 0.0f, -focus_distance_);
+ mul_project_m4_v3(cam_data.winmat.ptr(), jitter);
+ mul_project_m4_v3(cam_data.winmat.ptr(), center);
+ /* Simplify CoC calculation to a simple MADD. */
+ if (inst_.camera.is_orthographic()) {
+ data_.coc_mul = (center[0] - jitter[0]) * 0.5f * extent_[0];
+ data_.coc_bias = focus_distance_ * data_.coc_mul;
+ }
+ else {
+ data_.coc_bias = -(center[0] - jitter[0]) * 0.5f * extent_[0];
+ data_.coc_mul = focus_distance_ * data_.coc_bias;
+ }
+
+ float min_fg_coc = coc_radius_from_camera_depth(data_, -cam_data.clip_near);
+ float max_bg_coc = coc_radius_from_camera_depth(data_, -cam_data.clip_far);
+ if (data_.camera_type != CAMERA_ORTHO) {
+ /* Background is at infinity so maximum CoC is the limit of coc_radius_from_camera_depth
+ * at -inf. We only do this for perspective camera since orthographic coc limit is inf. */
+ max_bg_coc = data_.coc_bias;
+ }
+ /* Clamp with user defined max. */
+ data_.coc_abs_max = min_ff(max_ff(fabsf(min_fg_coc), fabsf(max_bg_coc)), fx_max_coc_);
+ /* TODO(fclem): Make this dependent of the quality of the gather pass. */
+ data_.scatter_coc_threshold = 4.0f;
+
+ update_sample_table();
+
+ data_.push_update();
+ }
+
+ int2 half_res = math::divide_ceil(extent_, int2(2));
+ int2 quarter_res = math::divide_ceil(extent_, int2(4));
+ int2 tile_res = math::divide_ceil(half_res, int2(DOF_TILES_SIZE));
+
+ dispatch_setup_size_ = int3(math::divide_ceil(half_res, int2(DOF_DEFAULT_GROUP_SIZE)), 1);
+ dispatch_stabilize_size_ = int3(math::divide_ceil(half_res, int2(DOF_STABILIZE_GROUP_SIZE)), 1);
+ dispatch_downsample_size_ = int3(math::divide_ceil(quarter_res, int2(DOF_DEFAULT_GROUP_SIZE)),
+ 1);
+ dispatch_reduce_size_ = int3(math::divide_ceil(half_res, int2(DOF_REDUCE_GROUP_SIZE)), 1);
+ dispatch_tiles_flatten_size_ = int3(math::divide_ceil(half_res, int2(DOF_TILES_SIZE)), 1);
+ dispatch_tiles_dilate_size_ = int3(
+ math::divide_ceil(tile_res, int2(DOF_TILES_DILATE_GROUP_SIZE)), 1);
+ dispatch_gather_size_ = int3(math::divide_ceil(half_res, int2(DOF_GATHER_GROUP_SIZE)), 1);
+ dispatch_filter_size_ = int3(math::divide_ceil(half_res, int2(DOF_FILTER_GROUP_SIZE)), 1);
+ dispatch_resolve_size_ = int3(math::divide_ceil(extent_, int2(DOF_RESOLVE_GROUP_SIZE)), 1);
+
+ if (GPU_type_matches_ex(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) {
+ /* On Mesa, there is a sync bug which can make a portion of the main pass (usually one shader)
+ * leave blocks of un-initialized memory. Doing a flush seems to alleviate the issue. */
+ GPU_flush();
+ }
+
+ DRW_stats_group_start("Depth of Field");
+
+ Manager &drw = *inst_.manager;
+
+ {
+ DRW_stats_group_start("Setup");
+ {
+ bokeh_gather_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_RG16F);
+ bokeh_scatter_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_R16F);
+ bokeh_resolve_lut_tx_.acquire(int2(DOF_MAX_SLIGHT_FOCUS_RADIUS * 2 + 1), GPU_R16F);
+
+ if (use_bokeh_lut_) {
+ drw.submit(bokeh_lut_ps_, view);
+ }
+ }
+ {
+ setup_color_tx_.acquire(half_res, GPU_RGBA16F);
+ setup_coc_tx_.acquire(half_res, GPU_R16F);
+
+ drw.submit(setup_ps_, view);
+ }
+ {
+ stabilize_output_tx_.acquire(half_res, GPU_RGBA16F);
+ stabilize_valid_history_ = !dof_buffer.stabilize_history_tx_.ensure_2d(GPU_RGBA16F,
+ half_res);
+
+ if (stabilize_valid_history_ == false) {
+ /* Avoid uninitialized memory that can contain NaNs. */
+ dof_buffer.stabilize_history_tx_.clear(float4(0.0f));
+ }
+
+ stabilize_input_ = dof_buffer.stabilize_history_tx_;
+ /* Outputs to reduced_*_tx_ mip 0. */
+ drw.submit(stabilize_ps_, view);
+
+ /* WATCH(fclem): Swap Texture an TextureFromPool internal GPUTexture in order to reuse
+ * the one that we just consumed. */
+ TextureFromPool::swap(stabilize_output_tx_, dof_buffer.stabilize_history_tx_);
+
+ /* Used by stabilize pass. */
+ stabilize_output_tx_.release();
+ setup_color_tx_.release();
+ }
+ {
+ DRW_stats_group_start("Tile Prepare");
+
+ /* WARNING: If format changes, make sure dof_tile_* GLSL constants are properly encoded. */
+ tiles_fg_tx_.previous().acquire(tile_res, GPU_R11F_G11F_B10F);
+ tiles_bg_tx_.previous().acquire(tile_res, GPU_R11F_G11F_B10F);
+ tiles_fg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F);
+ tiles_bg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F);
+
+ drw.submit(tiles_flatten_ps_, view);
+
+ /* Used by tile_flatten and stabilize_ps pass. */
+ setup_coc_tx_.release();
+
+ /* Error introduced by gather center jittering. */
+ const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f);
+ int dilation_end_radius = ceilf((fx_max_coc_ * error_multiplier) / (DOF_TILES_SIZE * 2));
+
+ /* Run dilation twice. One for minmax and one for minabs. */
+ for (int pass = 0; pass < 2; pass++) {
+ /* This algorithm produce the exact dilation radius by dividing it in multiple passes. */
+ int dilation_radius = 0;
+ while (dilation_radius < dilation_end_radius) {
+ int remainder = dilation_end_radius - dilation_radius;
+ /* Do not step over any unvisited tile. */
+ int max_multiplier = dilation_radius + 1;
+
+ int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / (float)max_multiplier));
+ int multiplier = min_ii(max_multiplier, floorf(remainder / (float)ring_count));
+
+ dilation_radius += ring_count * multiplier;
+
+ tiles_dilate_ring_count_ = ring_count;
+ tiles_dilate_ring_width_mul_ = multiplier;
+
+ tiles_fg_tx_.swap();
+ tiles_bg_tx_.swap();
+
+ drw.submit((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_, view);
+ }
+ }
+
+ tiles_fg_tx_.previous().release();
+ tiles_bg_tx_.previous().release();
+
+ DRW_stats_group_end();
+ }
+
+ downsample_tx_.acquire(quarter_res, GPU_RGBA16F);
+
+ drw.submit(downsample_ps_, view);
+
+ scatter_fg_indirect_buf_.clear_to_zero();
+ scatter_bg_indirect_buf_.clear_to_zero();
+
+ drw.submit(reduce_ps_, view);
+
+ /* Used by reduce pass. */
+ downsample_tx_.release();
+
+ DRW_stats_group_end();
+ }
+
+ for (int is_background = 0; is_background < 2; is_background++) {
+ DRW_stats_group_start(is_background ? "Background Convolution" : "Foreground Convolution");
+
+ SwapChain<TextureFromPool, 2> &color_tx = is_background ? color_bg_tx_ : color_fg_tx_;
+ SwapChain<TextureFromPool, 2> &weight_tx = is_background ? weight_bg_tx_ : weight_fg_tx_;
+ Framebuffer &scatter_fb = is_background ? scatter_bg_fb_ : scatter_fg_fb_;
+ PassSimple &gather_ps = is_background ? gather_bg_ps_ : gather_fg_ps_;
+ PassSimple &filter_ps = is_background ? filter_bg_ps_ : filter_fg_ps_;
+ PassSimple &scatter_ps = is_background ? scatter_bg_ps_ : scatter_fg_ps_;
+
+ color_tx.current().acquire(half_res, GPU_RGBA16F);
+ weight_tx.current().acquire(half_res, GPU_R16F);
+ occlusion_tx_.acquire(half_res, GPU_RG16F);
+
+ drw.submit(gather_ps, view);
+
+ {
+ /* Filtering pass. */
+ color_tx.swap();
+ weight_tx.swap();
+
+ color_tx.current().acquire(half_res, GPU_RGBA16F);
+ weight_tx.current().acquire(half_res, GPU_R16F);
+
+ drw.submit(filter_ps, view);
+
+ color_tx.previous().release();
+ weight_tx.previous().release();
+ }
+
+ GPU_memory_barrier(GPU_BARRIER_FRAMEBUFFER);
+
+ scatter_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_tx.current()));
+
+ GPU_framebuffer_bind(scatter_fb);
+ drw.submit(scatter_ps, view);
+
+ /* Used by scatter pass. */
+ occlusion_tx_.release();
+
+ DRW_stats_group_end();
+ }
+ {
+ DRW_stats_group_start("Hole Fill");
+
+ bokeh_gather_lut_tx_.release();
+ bokeh_scatter_lut_tx_.release();
+
+ hole_fill_color_tx_.acquire(half_res, GPU_RGBA16F);
+ hole_fill_weight_tx_.acquire(half_res, GPU_R16F);
+
+ drw.submit(hole_fill_ps_, view);
+
+ /* NOTE: We do not filter the hole-fill pass as effect is likely to not be noticeable. */
+
+ DRW_stats_group_end();
+ }
+ {
+ DRW_stats_group_start("Resolve");
+
+ resolve_stable_color_tx_ = dof_buffer.stabilize_history_tx_;
+
+ drw.submit(resolve_ps_, view);
+
+ color_bg_tx_.current().release();
+ color_fg_tx_.current().release();
+ weight_bg_tx_.current().release();
+ weight_fg_tx_.current().release();
+ tiles_fg_tx_.current().release();
+ tiles_bg_tx_.current().release();
+ hole_fill_color_tx_.release();
+ hole_fill_weight_tx_.release();
+ bokeh_resolve_lut_tx_.release();
+
+ DRW_stats_group_end();
+ }
+
+ DRW_stats_group_end();
+
+ /* Swap buffers so that next effect has the right input. */
+ SWAP(GPUTexture *, *input_tx, *output_tx);
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh
new file mode 100644
index 00000000000..bac0e394d66
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Depth of field post process effect.
+ *
+ * There are 2 methods to achieve this effect.
+ * - The first uses projection matrix offsetting and sample accumulation to give
+ * reference quality depth of field. But this needs many samples to hide the
+ * under-sampling.
+ * - The second one is a post-processing based one. It follows the
+ * implementation described in the presentation
+ * "Life of a Bokeh - Siggraph 2018" from Guillaume Abadie.
+ * There are some difference with our actual implementation that prioritize quality.
+ */
+
+#pragma once
+
+#include "eevee_shader_shared.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Depth of field
+ * \{ */
+
+struct DepthOfFieldBuffer {
+ /**
+ * Per view history texture for stabilize pass.
+ * Swapped with stabilize_output_tx_ in order to reuse the previous history during DoF
+ * processing.
+ * Note this should be private as its inner working only concerns the Depth Of Field
+ * implementation. The view itself should not touch it.
+ */
+ Texture stabilize_history_tx_ = {"dof_taa"};
+};
+
+class DepthOfField {
+ private:
+ class Instance &inst_;
+
+ /** Samplers */
+ static constexpr eGPUSamplerState gather_bilinear = GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER;
+ static constexpr eGPUSamplerState gather_nearest = GPU_SAMPLER_MIPMAP;
+
+ /** Input/Output texture references. */
+ GPUTexture *input_color_tx_ = nullptr;
+ GPUTexture *output_color_tx_ = nullptr;
+
+ /** Bokeh LUT precompute pass. */
+ TextureFromPool bokeh_gather_lut_tx_ = {"dof_bokeh_gather_lut"};
+ TextureFromPool bokeh_resolve_lut_tx_ = {"dof_bokeh_resolve_lut"};
+ TextureFromPool bokeh_scatter_lut_tx_ = {"dof_bokeh_scatter_lut"};
+ PassSimple bokeh_lut_ps_ = {"BokehLut"};
+
+ /** Outputs half-resolution color and Circle Of Confusion. */
+ TextureFromPool setup_coc_tx_ = {"dof_setup_coc"};
+ TextureFromPool setup_color_tx_ = {"dof_setup_color"};
+ int3 dispatch_setup_size_ = int3(-1);
+ PassSimple setup_ps_ = {"Setup"};
+
+ /** Allocated because we need mip chain. Which isn't supported by TextureFromPool. */
+ Texture reduced_coc_tx_ = {"dof_reduced_coc"};
+ Texture reduced_color_tx_ = {"dof_reduced_color"};
+
+ /** Stabilization (flicker attenuation) of Color and CoC output of the setup pass. */
+ TextureFromPool stabilize_output_tx_ = {"dof_taa"};
+ GPUTexture *stabilize_input_ = nullptr;
+ bool1 stabilize_valid_history_ = false;
+ int3 dispatch_stabilize_size_ = int3(-1);
+ PassSimple stabilize_ps_ = {"Stabilize"};
+
+ /** 1/4th res color buffer used to speedup the local contrast test in the first reduce pass. */
+ TextureFromPool downsample_tx_ = {"dof_downsample"};
+ int3 dispatch_downsample_size_ = int3(-1);
+ PassSimple downsample_ps_ = {"Downsample"};
+
+ /** Create mip-mapped color & COC textures for gather passes as well as scatter rect list. */
+ DepthOfFieldScatterListBuf scatter_fg_list_buf_;
+ DepthOfFieldScatterListBuf scatter_bg_list_buf_;
+ DrawIndirectBuf scatter_fg_indirect_buf_;
+ DrawIndirectBuf scatter_bg_indirect_buf_;
+ int3 dispatch_reduce_size_ = int3(-1);
+ PassSimple reduce_ps_ = {"Reduce"};
+
+ /** Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution). */
+ SwapChain<TextureFromPool, 2> tiles_fg_tx_;
+ SwapChain<TextureFromPool, 2> tiles_bg_tx_;
+ int3 dispatch_tiles_flatten_size_ = int3(-1);
+ PassSimple tiles_flatten_ps_ = {"TilesFlatten"};
+
+ /** Dilates the min & max CoCs to cover maximum COC values. */
+ int tiles_dilate_ring_count_ = -1;
+ int tiles_dilate_ring_width_mul_ = -1;
+ int3 dispatch_tiles_dilate_size_ = int3(-1);
+ PassSimple tiles_dilate_minmax_ps_ = {"TilesDilateMinmax"};
+ PassSimple tiles_dilate_minabs_ps_ = {"TilesDilateMinabs"};
+
+ /** Gather convolution for low intensity pixels and low contrast areas. */
+ SwapChain<TextureFromPool, 2> color_bg_tx_;
+ SwapChain<TextureFromPool, 2> color_fg_tx_;
+ SwapChain<TextureFromPool, 2> weight_bg_tx_;
+ SwapChain<TextureFromPool, 2> weight_fg_tx_;
+ TextureFromPool occlusion_tx_ = {"dof_occlusion"};
+ int3 dispatch_gather_size_ = int3(-1);
+ PassSimple gather_fg_ps_ = {"GatherFg"};
+ PassSimple gather_bg_ps_ = {"GatherBg"};
+
+ /** Hole-fill convolution: Gather pass meant to fill areas of foreground dis-occlusion. */
+ TextureFromPool hole_fill_color_tx_ = {"dof_color_hole_fill"};
+ TextureFromPool hole_fill_weight_tx_ = {"dof_weight_hole_fill"};
+ PassSimple hole_fill_ps_ = {"HoleFill"};
+
+ /** Small Filter pass to reduce noise out of gather passes. */
+ int3 dispatch_filter_size_ = int3(-1);
+ PassSimple filter_fg_ps_ = {"FilterFg"};
+ PassSimple filter_bg_ps_ = {"FilterBg"};
+
+ /** Scatter convolution: A quad is emitted for every 4 bright enough half pixels. */
+ Framebuffer scatter_fg_fb_ = {"dof_scatter_fg"};
+ Framebuffer scatter_bg_fb_ = {"dof_scatter_bg"};
+ PassSimple scatter_fg_ps_ = {"ScatterFg"};
+ PassSimple scatter_bg_ps_ = {"ScatterBg"};
+
+ /** Recombine the results and also perform a slight out of focus gather. */
+ GPUTexture *resolve_stable_color_tx_ = nullptr;
+ int3 dispatch_resolve_size_ = int3(-1);
+ PassSimple resolve_ps_ = {"Resolve"};
+
+ DepthOfFieldDataBuf data_;
+
+ /** Scene settings that are immutable. */
+ float user_overblur_;
+ float fx_max_coc_;
+ /** Use jittered depth of field where we randomize camera location. */
+ bool do_jitter_;
+ /** Enable bokeh lookup texture. */
+ bool use_bokeh_lut_;
+
+ /** Circle of Confusion radius for FX DoF passes. Is in view X direction in [0..1] range. */
+ float fx_radius_;
+ /** Circle of Confusion radius for jittered DoF. Is in view X direction in [0..1] range. */
+ float jitter_radius_;
+ /** Focus distance in view space. */
+ float focus_distance_;
+ /** Extent of the input buffer. */
+ int2 extent_;
+
+ public:
+ DepthOfField(Instance &inst) : inst_(inst){};
+ ~DepthOfField(){};
+
+ void init();
+
+ void sync();
+
+ /**
+ * Apply Depth Of Field jittering to the view and projection matrices..
+ */
+ void jitter_apply(float4x4 &winmat, float4x4 &viewmat);
+
+ /**
+ * Will swap input and output texture if rendering happens. The actual output of this function
+ * is in input_tx.
+ */
+ void render(View &view,
+ GPUTexture **input_tx,
+ GPUTexture **output_tx,
+ DepthOfFieldBuffer &dof_buffer);
+
+ bool postfx_enabled() const
+ {
+ return fx_radius_ > 0.0f;
+ }
+
+ private:
+ void bokeh_lut_pass_sync();
+ void setup_pass_sync();
+ void stabilize_pass_sync();
+ void downsample_pass_sync();
+ void reduce_pass_sync();
+ void tiles_flatten_pass_sync();
+ void tiles_dilate_pass_sync();
+ void gather_pass_sync();
+ void filter_pass_sync();
+ void scatter_pass_sync();
+ void hole_fill_pass_sync();
+ void resolve_pass_sync();
+
+ void update_sample_table();
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_engine.cc b/source/blender/draw/engines/eevee_next/eevee_engine.cc
index 37b4bde324e..5ef198838c9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_engine.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_engine.cc
@@ -140,7 +140,7 @@ static void eevee_instance_free(void *instance)
delete reinterpret_cast<eevee::Instance *>(instance);
}
-static void eevee_render_to_image(void *UNUSED(vedata),
+static void eevee_render_to_image(void *vedata,
struct RenderEngine *engine,
struct RenderLayer *layer,
const struct rcti *UNUSED(rect))
@@ -164,7 +164,23 @@ static void eevee_render_to_image(void *UNUSED(vedata),
instance->init(size, &rect, engine, depsgraph, nullptr, camera_original_ob, layer);
instance->render_frame(layer, viewname);
+ EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata);
+ if (ved->instance) {
+ delete ved->instance;
+ }
+ ved->instance = instance;
+}
+
+static void eevee_store_metadata(void *vedata, struct RenderResult *render_result)
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ EEVEE_Data *ved = static_cast<EEVEE_Data *>(vedata);
+ eevee::Instance *instance = ved->instance;
+ instance->store_metadata(render_result);
delete instance;
+ ved->instance = nullptr;
}
static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
@@ -172,7 +188,7 @@ static void eevee_render_update_passes(RenderEngine *engine, Scene *scene, ViewL
if (!GPU_shader_storage_buffer_objects_support()) {
return;
}
- UNUSED_VARS(engine, scene, view_layer);
+ eevee::Instance::update_passes(engine, scene, view_layer);
}
static const DrawEngineDataSize eevee_data_size = DRW_VIEWPORT_DATA_SIZE(EEVEE_Data);
@@ -194,7 +210,7 @@ DrawEngineType draw_engine_eevee_next_type = {
nullptr,
nullptr,
&eevee_render_to_image,
- nullptr,
+ &eevee_store_metadata,
};
RenderEngineType DRW_engine_viewport_eevee_next_type = {
diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc
index 1fd4c278c88..244eb1e54ef 100644
--- a/source/blender/draw/engines/eevee_next/eevee_film.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_film.cc
@@ -5,7 +5,7 @@
/** \file
* \ingroup eevee
*
- * A film is a fullscreen buffer (usually at output extent)
+ * A film is a full-screen buffer (usually at output extent)
* that will be able to accumulate sample in any distorted camera_type
* using a pixel filter.
*
@@ -146,8 +146,9 @@ void Film::sync_mist()
inline bool operator==(const FilmData &a, const FilmData &b)
{
- return (a.extent == b.extent) && (a.offset == b.offset) && (a.filter_size == b.filter_size) &&
- (a.scaling_factor == b.scaling_factor);
+ return (a.extent == b.extent) && (a.offset == b.offset) &&
+ (a.filter_radius == b.filter_radius) && (a.scaling_factor == b.scaling_factor) &&
+ (a.background_opacity == b.background_opacity);
}
inline bool operator!=(const FilmData &a, const FilmData &b)
@@ -161,13 +162,56 @@ inline bool operator!=(const FilmData &a, const FilmData &b)
/** \name Film
* \{ */
+static eViewLayerEEVEEPassType enabled_passes(const ViewLayer *view_layer)
+{
+ eViewLayerEEVEEPassType result = eViewLayerEEVEEPassType(view_layer->eevee.render_passes);
+
+#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \
+ SET_FLAG_FROM_TEST(result, \
+ (view_layer->passflag & SCE_PASS_##name_legacy) != 0, \
+ EEVEE_RENDER_PASS_##name_eevee);
+
+ ENABLE_FROM_LEGACY(COMBINED, COMBINED)
+ ENABLE_FROM_LEGACY(Z, Z)
+ ENABLE_FROM_LEGACY(MIST, MIST)
+ ENABLE_FROM_LEGACY(NORMAL, NORMAL)
+ ENABLE_FROM_LEGACY(SHADOW, SHADOW)
+ ENABLE_FROM_LEGACY(AO, AO)
+ ENABLE_FROM_LEGACY(EMIT, EMIT)
+ ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
+ ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR)
+ ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR)
+ ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
+ ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT)
+ ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
+ ENABLE_FROM_LEGACY(VECTOR, VECTOR)
+
+#undef ENABLE_FROM_LEGACY
+
+ SET_FLAG_FROM_TEST(result,
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_OBJECT,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT);
+ SET_FLAG_FROM_TEST(result,
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_ASSET,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET);
+ SET_FLAG_FROM_TEST(result,
+ view_layer->cryptomatte_flag & VIEW_LAYER_CRYPTOMATTE_MATERIAL,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
+
+ return result;
+}
+
void Film::init(const int2 &extent, const rcti *output_rect)
{
+ Sampling &sampling = inst_.sampling;
+ Scene &scene = *inst_.scene;
+ SceneEEVEE &scene_eevee = scene.eevee;
+
init_aovs();
{
/* Enable passes that need to be rendered. */
- eViewLayerEEVEEPassType render_passes;
+ eViewLayerEEVEEPassType render_passes = eViewLayerEEVEEPassType(0);
if (inst_.is_viewport()) {
/* Viewport Case. */
@@ -181,37 +225,20 @@ void Film::init(const int2 &extent, const rcti *output_rect)
}
else {
/* Render Case. */
- render_passes = eViewLayerEEVEEPassType(inst_.view_layer->eevee.render_passes);
-
- render_passes |= EEVEE_RENDER_PASS_COMBINED;
-
-#define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \
- SET_FLAG_FROM_TEST(render_passes, \
- (inst_.view_layer->passflag & SCE_PASS_##name_legacy) != 0, \
- EEVEE_RENDER_PASS_##name_eevee);
-
- ENABLE_FROM_LEGACY(Z, Z)
- ENABLE_FROM_LEGACY(MIST, MIST)
- ENABLE_FROM_LEGACY(NORMAL, NORMAL)
- ENABLE_FROM_LEGACY(SHADOW, SHADOW)
- ENABLE_FROM_LEGACY(AO, AO)
- ENABLE_FROM_LEGACY(EMIT, EMIT)
- ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
- ENABLE_FROM_LEGACY(DIFFUSE_COLOR, DIFFUSE_COLOR)
- ENABLE_FROM_LEGACY(GLOSSY_COLOR, SPECULAR_COLOR)
- ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT)
- ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT)
- ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT)
-
-#undef ENABLE_FROM_LEGACY
+ render_passes = enabled_passes(inst_.view_layer);
}
/* Filter obsolete passes. */
render_passes &= ~(EEVEE_RENDER_PASS_UNUSED_8 | EEVEE_RENDER_PASS_BLOOM);
+ if (scene_eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) {
+ /* Disable motion vector pass if motion blur is enabled. */
+ render_passes &= ~EEVEE_RENDER_PASS_VECTOR;
+ }
+
/* TODO(@fclem): Can't we rely on depsgraph update notification? */
if (assign_if_different(enabled_passes_, render_passes)) {
- inst_.sampling.reset();
+ sampling.reset();
}
}
{
@@ -224,14 +251,23 @@ void Film::init(const int2 &extent, const rcti *output_rect)
FilmData data = data_;
data.extent = int2(BLI_rcti_size_x(output_rect), BLI_rcti_size_y(output_rect));
data.offset = int2(output_rect->xmin, output_rect->ymin);
- data.filter_size = clamp_f(inst_.scene->r.gauss, 0.0f, 100.0f);
+ data.extent_inv = 1.0f / float2(data.extent);
+ /* Disable filtering if sample count is 1. */
+ data.filter_radius = (sampling.sample_count() == 1) ? 0.0f :
+ clamp_f(scene.r.gauss, 0.0f, 100.0f);
/* TODO(fclem): parameter hidden in experimental.
* We need to figure out LOD bias first in order to preserve texture crispiness. */
data.scaling_factor = 1;
+ data.cryptomatte_samples_len = inst_.view_layer->cryptomatte_levels;
+
+ data.background_opacity = (scene.r.alphamode == R_ALPHAPREMUL) ? 0.0f : 1.0f;
+ if (inst_.is_viewport() && false /* TODO(fclem): StudioLight */) {
+ data.background_opacity = inst_.v3d->shading.studiolight_background;
+ }
FilmData &data_prev_ = data_;
if (assign_if_different(data_prev_, data)) {
- inst_.sampling.reset();
+ sampling.reset();
}
const eViewLayerEEVEEPassType data_passes = EEVEE_RENDER_PASS_Z | EEVEE_RENDER_PASS_NORMAL |
@@ -246,16 +282,17 @@ void Film::init(const int2 &extent, const rcti *output_rect)
EEVEE_RENDER_PASS_MIST |
EEVEE_RENDER_PASS_SHADOW | EEVEE_RENDER_PASS_AO;
- data_.exposure = 1.0f /* TODO */;
+ data_.exposure_scale = pow2f(scene.view_settings.exposure);
data_.has_data = (enabled_passes_ & data_passes) != 0;
data_.any_render_pass_1 = (enabled_passes_ & color_passes_1) != 0;
data_.any_render_pass_2 = (enabled_passes_ & color_passes_2) != 0;
}
{
- /* Set pass offsets. */
+ /* Set pass offsets. */
data_.display_id = aovs_info.display_id;
- data_.display_is_value = aovs_info.display_is_value;
+ data_.display_storage_type = aovs_info.display_is_value ? PASS_STORAGE_VALUE :
+ PASS_STORAGE_COLOR;
/* Combined is in a separate buffer. */
data_.combined_id = (enabled_passes_ & EEVEE_RENDER_PASS_COMBINED) ? 0 : -1;
@@ -266,13 +303,13 @@ void Film::init(const int2 &extent, const rcti *output_rect)
data_.value_len = 0;
auto pass_index_get = [&](eViewLayerEEVEEPassType pass_type) {
- bool is_value = pass_is_value(pass_type);
+ ePassStorageType storage_type = pass_storage_type(pass_type);
int index = (enabled_passes_ & pass_type) ?
- (is_value ? data_.value_len : data_.color_len)++ :
+ (storage_type == PASS_STORAGE_VALUE ? data_.value_len : data_.color_len)++ :
-1;
if (inst_.is_viewport() && inst_.v3d->shading.render_pass == pass_type) {
data_.display_id = index;
- data_.display_is_value = is_value;
+ data_.display_storage_type = storage_type;
}
return index;
};
@@ -298,17 +335,36 @@ void Film::init(const int2 &extent, const rcti *output_rect)
data_.color_len += data_.aov_color_len;
data_.value_len += data_.aov_value_len;
+
+ int cryptomatte_id = 0;
+ auto cryptomatte_index_get = [&](eViewLayerEEVEEPassType pass_type) {
+ int index = -1;
+ if (enabled_passes_ & pass_type) {
+ index = cryptomatte_id;
+ cryptomatte_id += data_.cryptomatte_samples_len / 2;
+
+ if (inst_.is_viewport() && inst_.v3d->shading.render_pass == pass_type) {
+ data_.display_id = index;
+ data_.display_storage_type = PASS_STORAGE_CRYPTOMATTE;
+ }
+ }
+ return index;
+ };
+ data_.cryptomatte_object_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT);
+ data_.cryptomatte_asset_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET);
+ data_.cryptomatte_material_id = cryptomatte_index_get(EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
}
{
/* TODO(@fclem): Over-scans. */
- render_extent_ = math::divide_ceil(extent, int2(data_.scaling_factor));
+ data_.render_extent = math::divide_ceil(extent, int2(data_.scaling_factor));
int2 weight_extent = inst_.camera.is_panoramic() ? data_.extent : int2(data_.scaling_factor);
eGPUTextureFormat color_format = GPU_RGBA16F;
eGPUTextureFormat float_format = GPU_R16F;
eGPUTextureFormat weight_format = GPU_R32F;
eGPUTextureFormat depth_format = GPU_R32F;
+ eGPUTextureFormat cryptomatte_format = GPU_RGBA32F;
int reset = 0;
reset += depth_tx_.ensure_2d(depth_format, data_.extent);
@@ -323,9 +379,15 @@ void Film::init(const int2 &extent, const rcti *output_rect)
reset += value_accum_tx_.ensure_2d_array(float_format,
(data_.value_len > 0) ? data_.extent : int2(1),
(data_.value_len > 0) ? data_.value_len : 1);
+ /* Divided by two as two cryptomatte samples fit in pixel (RG, BA). */
+ int cryptomatte_array_len = cryptomatte_layer_len_get() * data_.cryptomatte_samples_len / 2;
+ reset += cryptomatte_tx_.ensure_2d_array(cryptomatte_format,
+ (cryptomatte_array_len > 0) ? data_.extent : int2(1),
+ (cryptomatte_array_len > 0) ? cryptomatte_array_len :
+ 1);
if (reset > 0) {
- inst_.sampling.reset();
+ sampling.reset();
data_.use_history = 0;
data_.use_reprojection = 0;
@@ -335,8 +397,11 @@ void Film::init(const int2 &extent, const rcti *output_rect)
combined_tx_.current().clear(float4(0.0f));
weight_tx_.current().clear(float4(0.0f));
depth_tx_.clear(float4(0.0f));
+ cryptomatte_tx_.clear(float4(0.0f));
}
}
+
+ force_disable_reprojection_ = (scene_eevee.flag & SCE_EEVEE_TAA_REPROJECTION) == 0;
}
void Film::sync()
@@ -349,59 +414,82 @@ void Film::sync()
/* TODO(fclem): Shader variation for panoramic & scaled resolution. */
RenderBuffers &rbuffers = inst_.render_buffers;
-
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS;
- accumulate_ps_ = DRW_pass_create("Film.Accumulate", state);
- GPUShader *sh = inst_.shaders.static_shader_get(shader);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, accumulate_ps_);
- DRW_shgroup_uniform_block_ref(grp, "film_buf", &data_);
- DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &rbuffers.depth_tx);
- DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &rbuffers.combined_tx);
- DRW_shgroup_uniform_texture_ref(grp, "normal_tx", &rbuffers.normal_tx);
- DRW_shgroup_uniform_texture_ref(grp, "vector_tx", &rbuffers.vector_tx);
- DRW_shgroup_uniform_texture_ref(grp, "diffuse_light_tx", &rbuffers.diffuse_light_tx);
- DRW_shgroup_uniform_texture_ref(grp, "diffuse_color_tx", &rbuffers.diffuse_color_tx);
- DRW_shgroup_uniform_texture_ref(grp, "specular_light_tx", &rbuffers.specular_light_tx);
- DRW_shgroup_uniform_texture_ref(grp, "specular_color_tx", &rbuffers.specular_color_tx);
- DRW_shgroup_uniform_texture_ref(grp, "volume_light_tx", &rbuffers.volume_light_tx);
- DRW_shgroup_uniform_texture_ref(grp, "emission_tx", &rbuffers.emission_tx);
- DRW_shgroup_uniform_texture_ref(grp, "environment_tx", &rbuffers.environment_tx);
- DRW_shgroup_uniform_texture_ref(grp, "shadow_tx", &rbuffers.shadow_tx);
- DRW_shgroup_uniform_texture_ref(grp, "ambient_occlusion_tx", &rbuffers.ambient_occlusion_tx);
- DRW_shgroup_uniform_texture_ref(grp, "aov_color_tx", &rbuffers.aov_color_tx);
- DRW_shgroup_uniform_texture_ref(grp, "aov_value_tx", &rbuffers.aov_value_tx);
+ VelocityModule &velocity = inst_.velocity;
+
+ eGPUSamplerState filter = GPU_SAMPLER_FILTER;
+
+ /* For viewport, only previous motion is supported.
+ * Still bind previous step to avoid undefined behavior. */
+ eVelocityStep step_next = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT;
+
+ accumulate_ps_.init();
+ accumulate_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS);
+ accumulate_ps_.shader_set(inst_.shaders.static_shader_get(shader));
+ accumulate_ps_.bind_ubo("film_buf", &data_);
+ accumulate_ps_.bind_ubo("camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS]));
+ accumulate_ps_.bind_ubo("camera_curr", &(*velocity.camera_steps[STEP_CURRENT]));
+ accumulate_ps_.bind_ubo("camera_next", &(*velocity.camera_steps[step_next]));
+ accumulate_ps_.bind_texture("depth_tx", &rbuffers.depth_tx);
+ accumulate_ps_.bind_texture("combined_tx", &combined_final_tx_);
+ accumulate_ps_.bind_texture("normal_tx", &rbuffers.normal_tx);
+ accumulate_ps_.bind_texture("vector_tx", &rbuffers.vector_tx);
+ accumulate_ps_.bind_texture("light_tx", &rbuffers.light_tx);
+ accumulate_ps_.bind_texture("diffuse_color_tx", &rbuffers.diffuse_color_tx);
+ accumulate_ps_.bind_texture("specular_color_tx", &rbuffers.specular_color_tx);
+ accumulate_ps_.bind_texture("volume_light_tx", &rbuffers.volume_light_tx);
+ accumulate_ps_.bind_texture("emission_tx", &rbuffers.emission_tx);
+ accumulate_ps_.bind_texture("environment_tx", &rbuffers.environment_tx);
+ accumulate_ps_.bind_texture("shadow_tx", &rbuffers.shadow_tx);
+ accumulate_ps_.bind_texture("ambient_occlusion_tx", &rbuffers.ambient_occlusion_tx);
+ accumulate_ps_.bind_texture("aov_color_tx", &rbuffers.aov_color_tx);
+ accumulate_ps_.bind_texture("aov_value_tx", &rbuffers.aov_value_tx);
+ accumulate_ps_.bind_texture("cryptomatte_tx", &rbuffers.cryptomatte_tx);
/* NOTE(@fclem): 16 is the max number of sampled texture in many implementations.
* If we need more, we need to pack more of the similar passes in the same textures as arrays or
* use image binding instead. */
- DRW_shgroup_uniform_image_ref(grp, "in_weight_img", &weight_tx_.current());
- DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_tx_.next());
- DRW_shgroup_uniform_image_ref(grp, "in_combined_img", &combined_tx_.current());
- DRW_shgroup_uniform_image_ref(grp, "out_combined_img", &combined_tx_.next());
- DRW_shgroup_uniform_image_ref(grp, "depth_img", &depth_tx_);
- DRW_shgroup_uniform_image_ref(grp, "color_accum_img", &color_accum_tx_);
- DRW_shgroup_uniform_image_ref(grp, "value_accum_img", &value_accum_tx_);
- /* Sync with rendering passes. */
- DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH);
+ accumulate_ps_.bind_image("in_weight_img", &weight_tx_.current());
+ accumulate_ps_.bind_image("out_weight_img", &weight_tx_.next());
+ accumulate_ps_.bind_texture("in_combined_tx", &combined_tx_.current(), filter);
+ accumulate_ps_.bind_image("out_combined_img", &combined_tx_.next());
+ accumulate_ps_.bind_image("depth_img", &depth_tx_);
+ accumulate_ps_.bind_image("color_accum_img", &color_accum_tx_);
+ accumulate_ps_.bind_image("value_accum_img", &value_accum_tx_);
+ accumulate_ps_.bind_image("cryptomatte_img", &cryptomatte_tx_);
/* Sync with rendering passes. */
- DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ accumulate_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
if (use_compute) {
- int2 dispatch_size = math::divide_ceil(data_.extent, int2(FILM_GROUP_SIZE));
- DRW_shgroup_call_compute(grp, UNPACK2(dispatch_size), 1);
+ accumulate_ps_.dispatch(int3(math::divide_ceil(data_.extent, int2(FILM_GROUP_SIZE)), 1));
}
else {
- DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
+ accumulate_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
+ }
+
+ const int cryptomatte_layer_count = cryptomatte_layer_len_get();
+ const bool is_cryptomatte_pass_enabled = cryptomatte_layer_count > 0;
+ const bool do_cryptomatte_sorting = inst_.is_viewport() == false;
+ cryptomatte_post_ps_.init();
+ if (is_cryptomatte_pass_enabled && do_cryptomatte_sorting) {
+ cryptomatte_post_ps_.state_set(DRW_STATE_NO_DRAW);
+ cryptomatte_post_ps_.shader_set(inst_.shaders.static_shader_get(FILM_CRYPTOMATTE_POST));
+ cryptomatte_post_ps_.bind_image("cryptomatte_img", &cryptomatte_tx_);
+ cryptomatte_post_ps_.bind_image("weight_img", &weight_tx_.current());
+ cryptomatte_post_ps_.push_constant("cryptomatte_layer_len", cryptomatte_layer_count);
+ cryptomatte_post_ps_.push_constant("cryptomatte_samples_per_layer",
+ inst_.view_layer->cryptomatte_levels);
+ int2 dispatch_size = math::divide_ceil(int2(cryptomatte_tx_.size()), int2(FILM_GROUP_SIZE));
+ cryptomatte_post_ps_.dispatch(int3(UNPACK2(dispatch_size), 1));
}
}
void Film::end_sync()
{
- if (inst_.sampling.is_reset()) {
- data_.use_history = 0;
- }
+ data_.use_reprojection = inst_.sampling.interactive_mode();
- // if (camera.changed_type) {
- // data_.use_reprojection = false;
- // }
+ /* Just bypass the reprojection and reset the accumulation. */
+ if (force_disable_reprojection_ && inst_.sampling.is_reset()) {
+ data_.use_reprojection = false;
+ data_.use_history = false;
+ }
aovs_info.push_update();
@@ -412,16 +500,16 @@ float2 Film::pixel_jitter_get() const
{
float2 jitter = inst_.sampling.rng_2d_get(SAMPLING_FILTER_U);
- if (data_.filter_size < M_SQRT1_2 && !inst_.camera.is_panoramic()) {
+ if (!use_box_filter && data_.filter_radius < M_SQRT1_2 && !inst_.camera.is_panoramic()) {
/* For filter size less than a pixel, change sampling strategy and use a uniform disk
* distribution covering the filter shape. This avoids putting samples in areas without any
* weights. */
/* TODO(fclem): Importance sampling could be a better option here. */
- jitter = Sampling::sample_disk(jitter) * data_.filter_size;
+ jitter = Sampling::sample_disk(jitter) * data_.filter_radius;
}
else {
- /* Jitter the size of a whole pixel. */
- jitter = jitter * 2.0f - 1.0f;
+ /* Jitter the size of a whole pixel. [-0.5..0.5] */
+ jitter -= 0.5f;
}
/* TODO(fclem): Mixed-resolution rendering: We need to offset to each of the target pixel covered
* by a render pixel, ideally, by choosing one randomly using another sampling dimension, or by
@@ -429,40 +517,72 @@ float2 Film::pixel_jitter_get() const
return jitter;
}
+eViewLayerEEVEEPassType Film::enabled_passes_get() const
+{
+ if (inst_.is_viewport() && data_.use_reprojection) {
+ /* Enable motion vector rendering but not the accumulation buffer. */
+ return enabled_passes_ | EEVEE_RENDER_PASS_VECTOR;
+ }
+ return enabled_passes_;
+}
+
+int Film::cryptomatte_layer_len_get() const
+{
+ int result = 0;
+ result += data_.cryptomatte_object_id == -1 ? 0 : 1;
+ result += data_.cryptomatte_asset_id == -1 ? 0 : 1;
+ result += data_.cryptomatte_material_id == -1 ? 0 : 1;
+ return result;
+}
+
+int Film::cryptomatte_layer_max_get() const
+{
+ if (data_.cryptomatte_material_id != -1) {
+ return 3;
+ }
+ if (data_.cryptomatte_asset_id != -1) {
+ return 2;
+ }
+ if (data_.cryptomatte_object_id != -1) {
+ return 1;
+ }
+ return 0;
+}
+
void Film::update_sample_table()
{
data_.subpixel_offset = pixel_jitter_get();
- int filter_size_ceil = ceilf(data_.filter_size);
- float filter_size_sqr = square_f(data_.filter_size);
+ int filter_radius_ceil = ceilf(data_.filter_radius);
+ float filter_radius_sqr = square_f(data_.filter_radius);
data_.samples_len = 0;
- if (data_.filter_size < 0.01f) {
- /* Disable filtering. */
+ if (use_box_filter || data_.filter_radius < 0.01f) {
+ /* Disable gather filtering. */
data_.samples[0].texel = int2(0, 0);
data_.samples[0].weight = 1.0f;
data_.samples_weight_total = 1.0f;
data_.samples_len = 1;
}
- /* NOTE: Threshold determined by hand until we don't hit the assert bellow. */
- else if (data_.filter_size < 2.20f) {
+ /* NOTE: Threshold determined by hand until we don't hit the assert below. */
+ else if (data_.filter_radius < 2.20f) {
/* Small filter Size. */
int closest_index = 0;
float closest_distance = FLT_MAX;
data_.samples_weight_total = 0.0f;
/* TODO(fclem): For optimization, could try Z-tile ordering. */
- for (int y = -filter_size_ceil; y <= filter_size_ceil; y++) {
- for (int x = -filter_size_ceil; x <= filter_size_ceil; x++) {
+ for (int y = -filter_radius_ceil; y <= filter_radius_ceil; y++) {
+ for (int x = -filter_radius_ceil; x <= filter_radius_ceil; x++) {
float2 pixel_offset = float2(x, y) - data_.subpixel_offset;
float distance_sqr = math::length_squared(pixel_offset);
- if (distance_sqr < filter_size_sqr) {
+ if (distance_sqr < filter_radius_sqr) {
if (data_.samples_len >= FILM_PRECOMP_SAMPLE_MAX) {
BLI_assert_msg(0, "Precomputed sample table is too small.");
break;
}
FilmSample &sample = data_.samples[data_.samples_len];
sample.texel = int2(x, y);
- sample.weight = film_filter_weight(data_.filter_size, distance_sqr);
+ sample.weight = film_filter_weight(data_.filter_radius, distance_sqr);
data_.samples_weight_total += sample.weight;
if (distance_sqr < closest_distance) {
@@ -490,37 +610,46 @@ void Film::update_sample_table()
int i = 0;
for (FilmSample &sample : sample_table) {
/* TODO(fclem): Own RNG. */
- float2 random_2d = inst_.sampling.rng_2d_get(SAMPLING_FILTER_U);
+ float2 random_2d = inst_.sampling.rng_2d_get(SAMPLING_SSS_U);
/* This randomization makes sure we converge to the right result but also makes nearest
* neighbor filtering not converging rapidly. */
random_2d.x = (random_2d.x + i) / float(FILM_PRECOMP_SAMPLE_MAX);
- float2 pixel_offset = math::floor(Sampling::sample_spiral(random_2d) * data_.filter_size);
+ float2 pixel_offset = math::floor(Sampling::sample_spiral(random_2d) * data_.filter_radius);
sample.texel = int2(pixel_offset);
float distance_sqr = math::length_squared(pixel_offset - data_.subpixel_offset);
- sample.weight = film_filter_weight(data_.filter_size, distance_sqr);
+ sample.weight = film_filter_weight(data_.filter_radius, distance_sqr);
data_.samples_weight_total += sample.weight;
i++;
}
}
}
-void Film::accumulate(const DRWView *view)
+void Film::accumulate(const DRWView *view, GPUTexture *combined_final_tx)
{
if (inst_.is_viewport()) {
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
+ DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
GPU_framebuffer_bind(dfbl->default_fb);
+ /* Clear when using render borders. */
+ if (data_.extent != int2(GPU_texture_width(dtxl->color), GPU_texture_height(dtxl->color))) {
+ float4 clear_color = {0.0f, 0.0f, 0.0f, 0.0f};
+ GPU_framebuffer_clear_color(dfbl->default_fb, clear_color);
+ }
GPU_framebuffer_viewport_set(dfbl->default_fb, UNPACK2(data_.offset), UNPACK2(data_.extent));
}
update_sample_table();
+ combined_final_tx_ = combined_final_tx;
+
data_.display_only = false;
data_.push_update();
- DRW_view_set_active(view);
- DRW_draw_pass(accumulate_ps_);
+ draw::View drw_view("MainView", view);
+
+ DRW_manager_get()->submit(accumulate_ps_, drw_view);
combined_tx_.swap();
weight_tx_.swap();
@@ -528,7 +657,6 @@ void Film::accumulate(const DRWView *view)
/* Use history after first sample. */
if (data_.use_history == 0) {
data_.use_history = 1;
- data_.use_reprojection = 1;
}
}
@@ -537,41 +665,61 @@ void Film::display()
BLI_assert(inst_.is_viewport());
/* Acquire dummy render buffers for correct binding. They will not be used. */
- inst_.render_buffers.acquire(int2(1), (void *)this);
+ inst_.render_buffers.acquire(int2(1));
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_bind(dfbl->default_fb);
GPU_framebuffer_viewport_set(dfbl->default_fb, UNPACK2(data_.offset), UNPACK2(data_.extent));
+ combined_final_tx_ = inst_.render_buffers.combined_tx;
+
data_.display_only = true;
data_.push_update();
- DRW_view_set_active(nullptr);
- DRW_draw_pass(accumulate_ps_);
+ draw::View drw_view("MainView", DRW_view_default_get());
+
+ DRW_manager_get()->submit(accumulate_ps_, drw_view);
inst_.render_buffers.release();
/* IMPORTANT: Do not swap! No accumulation has happened. */
}
-float *Film::read_pass(eViewLayerEEVEEPassType pass_type)
+void Film::cryptomatte_sort()
{
+ DRW_manager_get()->submit(cryptomatte_post_ps_);
+}
+
+float *Film::read_pass(eViewLayerEEVEEPassType pass_type, int layer_offset)
+{
+ ePassStorageType storage_type = pass_storage_type(pass_type);
+ const bool is_value = storage_type == PASS_STORAGE_VALUE;
+ const bool is_cryptomatte = storage_type == PASS_STORAGE_CRYPTOMATTE;
- bool is_value = pass_is_value(pass_type);
Texture &accum_tx = (pass_type == EEVEE_RENDER_PASS_COMBINED) ?
combined_tx_.current() :
(pass_type == EEVEE_RENDER_PASS_Z) ?
depth_tx_ :
- (is_value ? value_accum_tx_ : color_accum_tx_);
+ (is_cryptomatte ? cryptomatte_tx_ :
+ (is_value ? value_accum_tx_ : color_accum_tx_));
accum_tx.ensure_layer_views();
int index = pass_id_get(pass_type);
- GPUTexture *pass_tx = accum_tx.layer_view(index);
+ GPUTexture *pass_tx = accum_tx.layer_view(index + layer_offset);
GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
- return (float *)GPU_texture_read(pass_tx, GPU_DATA_FLOAT, 0);
+ float *result = (float *)GPU_texture_read(pass_tx, GPU_DATA_FLOAT, 0);
+
+ if (pass_is_float3(pass_type)) {
+ /* Convert result in place as we cannot do this conversion on GPU. */
+ for (auto px : IndexRange(accum_tx.width() * accum_tx.height())) {
+ *(reinterpret_cast<float3 *>(result) + px) = *(reinterpret_cast<float3 *>(result + px * 4));
+ }
+ }
+
+ return result;
}
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh
index 7ffbd4e45c6..5478c20aff2 100644
--- a/source/blender/draw/engines/eevee_next/eevee_film.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_film.hh
@@ -8,6 +8,12 @@
* The film class handles accumulation of samples with any distorted camera_type
* using a pixel filter. Inputs needs to be jittered so that the filter converges to the right
* result.
+ *
+ * In viewport, we switch between 2 accumulation mode depending on the scene state.
+ * - For static scene, we use a classic weighted accumulation.
+ * - For dynamic scene (if an update is detected), we use a more temporally stable accumulation
+ * following the Temporal Anti-Aliasing method (a.k.a. Temporal Super-Sampling). This does
+ * history reprojection and rectification to avoid most of the flickering.
*/
#pragma once
@@ -28,23 +34,34 @@ class Film {
public:
/** Stores indirection table of AOVs based on their name hash and their type. */
AOVsInfoDataBuf aovs_info;
+ /** For debugging purpose but could be a user option in the future. */
+ static constexpr bool use_box_filter = false;
private:
Instance &inst_;
- /** Main accumulation textures containing every render-pass except depth and combined. */
+ /** Incoming combined buffer with post FX applied (motion blur + depth of field). */
+ GPUTexture *combined_final_tx_ = nullptr;
+
+ /**
+ * Main accumulation textures containing every render-pass except depth, cryptomatte and
+ * combined.
+ */
Texture color_accum_tx_;
Texture value_accum_tx_;
/** Depth accumulation texture. Separated because using a different format. */
Texture depth_tx_;
+ /** Cryptomatte texture. Separated because it requires full floats. */
+ Texture cryptomatte_tx_;
/** Combined "Color" buffer. Double buffered to allow re-projection. */
SwapChain<Texture, 2> combined_tx_;
/** Weight buffers. Double buffered to allow updating it during accumulation. */
SwapChain<Texture, 2> weight_tx_;
- /** Extent used by the render buffers when rendering the main views. */
- int2 render_extent_ = int2(-1);
+ /** User setting to disable reprojection. Useful for debugging or have a more precise render. */
+ bool force_disable_reprojection_ = false;
- DRWPass *accumulate_ps_ = nullptr;
+ PassSimple accumulate_ps_ = {"Film.Accumulate"};
+ PassSimple cryptomatte_post_ps_ = {"Film.Cryptomatte.Post"};
FilmDataBuf data_;
@@ -60,33 +77,62 @@ class Film {
void end_sync();
/** Accumulate the newly rendered sample contained in #RenderBuffers and blit to display. */
- void accumulate(const DRWView *view);
+ void accumulate(const DRWView *view, GPUTexture *combined_final_tx);
+
+ /** Sort and normalize cryptomatte samples. */
+ void cryptomatte_sort();
/** Blit to display. No rendered sample needed. */
void display();
- float *read_pass(eViewLayerEEVEEPassType pass_type);
+ float *read_pass(eViewLayerEEVEEPassType pass_type, int layer_offset);
float *read_aov(ViewLayerAOV *aov);
+ /** Returns shading views internal resolution. */
int2 render_extent_get() const
{
- return render_extent_;
+ return data_.render_extent;
}
float2 pixel_jitter_get() const;
- eViewLayerEEVEEPassType enabled_passes_get() const
+ float background_opacity_get() const
{
- return enabled_passes_;
+ return data_.background_opacity;
}
- static bool pass_is_value(eViewLayerEEVEEPassType pass_type)
+ eViewLayerEEVEEPassType enabled_passes_get() const;
+ int cryptomatte_layer_max_get() const;
+ int cryptomatte_layer_len_get() const;
+
+ static ePassStorageType pass_storage_type(eViewLayerEEVEEPassType pass_type)
{
switch (pass_type) {
case EEVEE_RENDER_PASS_Z:
case EEVEE_RENDER_PASS_MIST:
case EEVEE_RENDER_PASS_SHADOW:
case EEVEE_RENDER_PASS_AO:
+ return PASS_STORAGE_VALUE;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT:
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET:
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL:
+ return PASS_STORAGE_CRYPTOMATTE;
+ default:
+ return PASS_STORAGE_COLOR;
+ }
+ }
+
+ static bool pass_is_float3(eViewLayerEEVEEPassType pass_type)
+ {
+ switch (pass_type) {
+ case EEVEE_RENDER_PASS_NORMAL:
+ case EEVEE_RENDER_PASS_DIFFUSE_LIGHT:
+ case EEVEE_RENDER_PASS_DIFFUSE_COLOR:
+ case EEVEE_RENDER_PASS_SPECULAR_LIGHT:
+ case EEVEE_RENDER_PASS_SPECULAR_COLOR:
+ case EEVEE_RENDER_PASS_VOLUME_LIGHT:
+ case EEVEE_RENDER_PASS_EMIT:
+ case EEVEE_RENDER_PASS_ENVIRONMENT:
return true;
default:
return false;
@@ -123,8 +169,12 @@ class Film {
return data_.shadow_id;
case EEVEE_RENDER_PASS_AO:
return data_.ambient_occlusion_id;
- case EEVEE_RENDER_PASS_CRYPTOMATTE:
- return -1; /* TODO */
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT:
+ return data_.cryptomatte_object_id;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET:
+ return data_.cryptomatte_asset_id;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL:
+ return data_.cryptomatte_material_id;
case EEVEE_RENDER_PASS_VECTOR:
return data_.vector_id;
default:
@@ -132,44 +182,80 @@ class Film {
}
}
- static const char *pass_to_render_pass_name(eViewLayerEEVEEPassType pass_type)
+ static const Vector<std::string> pass_to_render_pass_names(eViewLayerEEVEEPassType pass_type,
+ const ViewLayer *view_layer)
{
+ Vector<std::string> result;
+
+ auto build_cryptomatte_passes = [&](const char *pass_name) {
+ const int num_cryptomatte_passes = (view_layer->cryptomatte_levels + 1) / 2;
+ for (int pass = 0; pass < num_cryptomatte_passes; pass++) {
+ std::stringstream ss;
+ ss.fill('0');
+ ss << pass_name;
+ ss.width(2);
+ ss << pass;
+ result.append(ss.str());
+ }
+ };
+
switch (pass_type) {
case EEVEE_RENDER_PASS_COMBINED:
- return RE_PASSNAME_COMBINED;
+ result.append(RE_PASSNAME_COMBINED);
+ break;
case EEVEE_RENDER_PASS_Z:
- return RE_PASSNAME_Z;
+ result.append(RE_PASSNAME_Z);
+ break;
case EEVEE_RENDER_PASS_MIST:
- return RE_PASSNAME_MIST;
+ result.append(RE_PASSNAME_MIST);
+ break;
case EEVEE_RENDER_PASS_NORMAL:
- return RE_PASSNAME_NORMAL;
+ result.append(RE_PASSNAME_NORMAL);
+ break;
case EEVEE_RENDER_PASS_DIFFUSE_LIGHT:
- return RE_PASSNAME_DIFFUSE_DIRECT;
+ result.append(RE_PASSNAME_DIFFUSE_DIRECT);
+ break;
case EEVEE_RENDER_PASS_DIFFUSE_COLOR:
- return RE_PASSNAME_DIFFUSE_COLOR;
+ result.append(RE_PASSNAME_DIFFUSE_COLOR);
+ break;
case EEVEE_RENDER_PASS_SPECULAR_LIGHT:
- return RE_PASSNAME_GLOSSY_DIRECT;
+ result.append(RE_PASSNAME_GLOSSY_DIRECT);
+ break;
case EEVEE_RENDER_PASS_SPECULAR_COLOR:
- return RE_PASSNAME_GLOSSY_COLOR;
+ result.append(RE_PASSNAME_GLOSSY_COLOR);
+ break;
case EEVEE_RENDER_PASS_VOLUME_LIGHT:
- return RE_PASSNAME_VOLUME_LIGHT;
+ result.append(RE_PASSNAME_VOLUME_LIGHT);
+ break;
case EEVEE_RENDER_PASS_EMIT:
- return RE_PASSNAME_EMIT;
+ result.append(RE_PASSNAME_EMIT);
+ break;
case EEVEE_RENDER_PASS_ENVIRONMENT:
- return RE_PASSNAME_ENVIRONMENT;
+ result.append(RE_PASSNAME_ENVIRONMENT);
+ break;
case EEVEE_RENDER_PASS_SHADOW:
- return RE_PASSNAME_SHADOW;
+ result.append(RE_PASSNAME_SHADOW);
+ break;
case EEVEE_RENDER_PASS_AO:
- return RE_PASSNAME_AO;
- case EEVEE_RENDER_PASS_CRYPTOMATTE:
- BLI_assert_msg(0, "Cryptomatte is not implemented yet.");
- return ""; /* TODO */
+ result.append(RE_PASSNAME_AO);
+ break;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT:
+ build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_OBJECT);
+ break;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET:
+ build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_ASSET);
+ break;
+ case EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL:
+ build_cryptomatte_passes(RE_PASSNAME_CRYPTOMATTE_MATERIAL);
+ break;
case EEVEE_RENDER_PASS_VECTOR:
- return RE_PASSNAME_VECTOR;
+ result.append(RE_PASSNAME_VECTOR);
+ break;
default:
BLI_assert(0);
- return "";
+ break;
}
+ return result;
}
private:
diff --git a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc
new file mode 100644
index 00000000000..cf9049da514
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.cc
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation.
+ */
+
+#include "BKE_global.h"
+
+#include "eevee_instance.hh"
+
+#include "eevee_hizbuffer.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name Hierarchical-Z buffer
+ *
+ * \{ */
+
+void HiZBuffer::sync()
+{
+ RenderBuffers &render_buffers = inst_.render_buffers;
+
+ int2 render_extent = inst_.film.render_extent_get();
+ /* Padding to avoid complexity during down-sampling and screen tracing. */
+ int2 hiz_extent = math::ceil_to_multiple(render_extent, int2(1u << (HIZ_MIP_COUNT - 1)));
+ int2 dispatch_size = math::divide_ceil(hiz_extent, int2(HIZ_GROUP_SIZE));
+
+ hiz_tx_.ensure_2d(GPU_R32F, hiz_extent, nullptr, HIZ_MIP_COUNT);
+ hiz_tx_.ensure_mip_views();
+ GPU_texture_mipmap_mode(hiz_tx_, true, false);
+
+ data_.uv_scale = float2(render_extent) / float2(hiz_extent);
+ data_.push_update();
+
+ {
+ hiz_update_ps_.init();
+ hiz_update_ps_.shader_set(inst_.shaders.static_shader_get(HIZ_UPDATE));
+ hiz_update_ps_.bind_ssbo("finished_tile_counter", atomic_tile_counter_);
+ hiz_update_ps_.bind_texture("depth_tx", &render_buffers.depth_tx, with_filter);
+ hiz_update_ps_.bind_image("out_mip_0", hiz_tx_.mip_view(0));
+ hiz_update_ps_.bind_image("out_mip_1", hiz_tx_.mip_view(1));
+ hiz_update_ps_.bind_image("out_mip_2", hiz_tx_.mip_view(2));
+ hiz_update_ps_.bind_image("out_mip_3", hiz_tx_.mip_view(3));
+ hiz_update_ps_.bind_image("out_mip_4", hiz_tx_.mip_view(4));
+ hiz_update_ps_.bind_image("out_mip_5", hiz_tx_.mip_view(5));
+ hiz_update_ps_.bind_image("out_mip_6", hiz_tx_.mip_view(6));
+ hiz_update_ps_.bind_image("out_mip_7", hiz_tx_.mip_view(7));
+ /* TODO(@fclem): There might be occasions where we might not want to
+ * copy mip 0 for performance reasons if there is no need for it. */
+ hiz_update_ps_.push_constant("update_mip_0", true);
+ hiz_update_ps_.dispatch(int3(dispatch_size, 1));
+ hiz_update_ps_.barrier(GPU_BARRIER_TEXTURE_FETCH);
+ }
+
+ if (inst_.debug_mode == eDebugMode::DEBUG_HIZ_VALIDATION) {
+ debug_draw_ps_.init();
+ debug_draw_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
+ debug_draw_ps_.shader_set(inst_.shaders.static_shader_get(HIZ_DEBUG));
+ this->bind_resources(&debug_draw_ps_);
+ debug_draw_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
+ }
+}
+
+void HiZBuffer::update()
+{
+ if (!is_dirty_) {
+ return;
+ }
+
+ /* Bind another framebuffer in order to avoid triggering the feedback loop check.
+ * This is safe because we only use compute shaders in this section of the code.
+ * Ideally the check should be smarter. */
+ GPUFrameBuffer *fb = GPU_framebuffer_active_get();
+ if (G.debug & G_DEBUG_GPU) {
+ GPU_framebuffer_restore();
+ }
+
+ inst_.manager->submit(hiz_update_ps_);
+
+ if (G.debug & G_DEBUG_GPU) {
+ GPU_framebuffer_bind(fb);
+ }
+}
+
+void HiZBuffer::debug_draw(View &view, GPUFrameBuffer *view_fb)
+{
+ if (inst_.debug_mode == eDebugMode::DEBUG_HIZ_VALIDATION) {
+ inst_.info =
+ "Debug Mode: HiZ Validation\n"
+ " - Red: pixel in front of HiZ tile value.\n"
+ " - Blue: No error.";
+ inst_.hiz_buffer.update();
+ GPU_framebuffer_bind(view_fb);
+ inst_.manager->submit(debug_draw_ps_, view);
+ }
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh
new file mode 100644
index 00000000000..8b8e4de55b1
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_hizbuffer.hh
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The Hierarchical-Z buffer is texture containing a copy of the depth buffer with mipmaps.
+ * Each mip contains the maximum depth of each 4 pixels on the upper level.
+ * The size of the texture is padded to avoid messing with the mipmap pixels alignments.
+ */
+
+#pragma once
+
+#include "DRW_render.h"
+
+#include "eevee_shader_shared.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Hierarchical-Z buffer
+ * \{ */
+
+class HiZBuffer {
+ private:
+ Instance &inst_;
+
+ /** The texture containing the hiz mip chain. */
+ Texture hiz_tx_ = {"hiz_tx_"};
+ /**
+ * Atomic counter counting the number of tile that have finished down-sampling.
+ * The last one will process the last few mip level.
+ */
+ draw::StorageBuffer<uint4, true> atomic_tile_counter_ = {"atomic_tile_counter"};
+ /** Single pass recursive downsample. */
+ PassSimple hiz_update_ps_ = {"HizUpdate"};
+ /** Debug pass. */
+ PassSimple debug_draw_ps_ = {"HizUpdate.Debug"};
+ /** Dirty flag to check if the update is necessary. */
+ bool is_dirty_ = true;
+
+ HiZDataBuf data_;
+
+ public:
+ HiZBuffer(Instance &inst) : inst_(inst)
+ {
+ atomic_tile_counter_.clear_to_zero();
+ };
+
+ void sync();
+
+ /**
+ * Tag the buffer for update if needed.
+ */
+ void set_dirty()
+ {
+ is_dirty_ = true;
+ }
+
+ /**
+ * Update the content of the HiZ buffer with the depth render target.
+ * Noop if the buffer has not been tagged as dirty.
+ * Should be called before each passes that needs to read the hiz buffer.
+ */
+ void update();
+
+ void debug_draw(View &view, GPUFrameBuffer *view_fb);
+
+ void bind_resources(DRWShadingGroup *grp)
+ {
+ DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", &hiz_tx_);
+ DRW_shgroup_uniform_block_ref(grp, "hiz_buf", &data_);
+ }
+
+ /* TODO(fclem): Hardcoded bind slots. */
+ template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
+ {
+ pass->bind_texture("hiz_tx", &hiz_tx_);
+ pass->bind_ubo("hiz_buf", &data_);
+ }
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc
index 6098d78b81c..8005b27c30e 100644
--- a/source/blender/draw/engines/eevee_next/eevee_instance.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc
@@ -52,6 +52,11 @@ void Instance::init(const int2 &output_res,
drw_view = drw_view_;
v3d = v3d_;
rv3d = rv3d_;
+ manager = DRW_manager_get();
+
+ if (assign_if_different(debug_mode, (eDebugMode)G.debug_value)) {
+ sampling.reset();
+ }
info = "";
@@ -60,6 +65,9 @@ void Instance::init(const int2 &output_res,
sampling.init(scene);
camera.init();
film.init(output_res, output_rect);
+ velocity.init();
+ depth_of_field.init();
+ motion_blur.init();
main_view.init();
}
@@ -92,21 +100,24 @@ void Instance::update_eval_members()
void Instance::begin_sync()
{
materials.begin_sync();
- velocity.begin_sync();
+ velocity.begin_sync(); /* NOTE: Also syncs camera. */
+ lights.begin_sync();
+ cryptomatte.begin_sync();
gpencil_engine_enabled = false;
- render_buffers.sync();
+ depth_of_field.sync();
+ motion_blur.sync();
+ hiz_buffer.sync();
pipelines.sync();
main_view.sync();
world.sync();
- camera.sync();
film.sync();
}
void Instance::object_sync(Object *ob)
{
- const bool is_renderable_type = ELEM(ob->type, OB_CURVES, OB_GPENCIL, OB_MESH);
+ const bool is_renderable_type = ELEM(ob->type, OB_CURVES, OB_GPENCIL, OB_MESH, OB_LAMP);
const int ob_visibility = DRW_object_visibility_in_active_context(ob);
const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 &&
(ob->type == OB_MESH);
@@ -117,12 +128,16 @@ void Instance::object_sync(Object *ob)
return;
}
+ /* TODO cleanup. */
+ ObjectRef ob_ref = DRW_object_ref_get(ob);
+ ResourceHandle res_handle = manager->resource_handle(ob_ref);
+
ObjectHandle &ob_handle = sync.sync_object(ob);
if (partsys_is_visible && ob != DRW_context_state_get()->object_edit) {
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
if (md->type == eModifierType_ParticleSystem) {
- sync.sync_curves(ob, ob_handle, md);
+ sync.sync_curves(ob, ob_handle, res_handle, md);
}
}
}
@@ -130,22 +145,18 @@ void Instance::object_sync(Object *ob)
if (object_is_visible) {
switch (ob->type) {
case OB_LAMP:
+ lights.sync_light(ob, ob_handle);
break;
case OB_MESH:
- case OB_CURVES_LEGACY:
- case OB_SURF:
- case OB_FONT:
- case OB_MBALL: {
- sync.sync_mesh(ob, ob_handle);
+ sync.sync_mesh(ob, ob_handle, res_handle, ob_ref);
break;
- }
case OB_VOLUME:
break;
case OB_CURVES:
- sync.sync_curves(ob, ob_handle);
+ sync.sync_curves(ob, ob_handle, res_handle);
break;
case OB_GPENCIL:
- sync.sync_gpencil(ob, ob_handle);
+ sync.sync_gpencil(ob, ob_handle, res_handle);
break;
default:
break;
@@ -169,8 +180,10 @@ void Instance::object_sync_render(void *instance_,
void Instance::end_sync()
{
velocity.end_sync();
+ lights.end_sync();
sampling.end_sync();
film.end_sync();
+ cryptomatte.end_sync();
}
void Instance::render_sync()
@@ -199,7 +212,7 @@ void Instance::render_sync()
**/
void Instance::render_sample()
{
- if (sampling.finished()) {
+ if (sampling.finished_viewport()) {
film.display();
return;
}
@@ -212,22 +225,12 @@ void Instance::render_sample()
sampling.step();
main_view.render();
-}
-
-/** \} */
-/* -------------------------------------------------------------------- */
-/** \name Interface
- * \{ */
+ motion_blur.step();
+}
-void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
+void Instance::render_read_result(RenderLayer *render_layer, const char *view_name)
{
- while (!sampling.finished()) {
- this->render_sample();
- /* TODO(fclem) print progression. */
- }
-
- /* Read Results. */
eViewLayerEEVEEPassType pass_bits = film.enabled_passes_get();
for (auto i : IndexRange(EEVEE_RENDER_PASS_MAX_BIT)) {
eViewLayerEEVEEPassType pass_type = eViewLayerEEVEEPassType(pass_bits & (1 << i));
@@ -235,12 +238,16 @@ void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
continue;
}
- const char *pass_name = Film::pass_to_render_pass_name(pass_type);
- RenderPass *rp = RE_pass_find_by_name(render_layer, pass_name, view_name);
- if (rp) {
- float *result = film.read_pass(pass_type);
+ Vector<std::string> pass_names = Film::pass_to_render_pass_names(pass_type, view_layer);
+ for (int64_t pass_offset : IndexRange(pass_names.size())) {
+ RenderPass *rp = RE_pass_find_by_name(
+ render_layer, pass_names[pass_offset].c_str(), view_name);
+ if (!rp) {
+ continue;
+ }
+ float *result = film.read_pass(pass_type, pass_offset);
+
if (result) {
- std::cout << "read " << pass_name << std::endl;
BLI_mutex_lock(&render->update_render_passes_mutex);
/* WORKAROUND: We use texture read to avoid using a framebuffer to get the render result.
* However, on some implementation, we need a buffer with a few extra bytes for the read to
@@ -252,6 +259,50 @@ void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
}
}
}
+
+ /* The vector pass is initialized to weird values. Set it to neutral value if not rendered. */
+ if ((pass_bits & EEVEE_RENDER_PASS_VECTOR) == 0) {
+ for (std::string vector_pass_name :
+ Film::pass_to_render_pass_names(EEVEE_RENDER_PASS_VECTOR, view_layer)) {
+ RenderPass *vector_rp = RE_pass_find_by_name(
+ render_layer, vector_pass_name.c_str(), view_name);
+ if (vector_rp) {
+ memset(vector_rp->rect, 0, sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty);
+ }
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Interface
+ * \{ */
+
+void Instance::render_frame(RenderLayer *render_layer, const char *view_name)
+{
+ while (!sampling.finished()) {
+ this->render_sample();
+
+ /* TODO(fclem) print progression. */
+#if 0
+ /* TODO(fclem): Does not currently work. But would be better to just display to 2D view like
+ * cycles does. */
+ if (G.background == false && first_read) {
+ /* Allow to preview the first sample. */
+ /* TODO(fclem): Might want to not do this during animation render to avoid too much stall. */
+ this->render_read_result(render_layer, view_name);
+ first_read = false;
+ DRW_render_context_disable(render->re);
+ /* Allow the 2D viewport to grab the ticket mutex to display the render. */
+ DRW_render_context_enable(render->re);
+ }
+#endif
+ }
+
+ this->film.cryptomatte_sort();
+
+ this->render_read_result(render_layer, view_name);
}
void Instance::draw_viewport(DefaultFramebufferList *dfbl)
@@ -260,7 +311,10 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl)
render_sample();
velocity.step_swap();
- if (!sampling.finished_viewport()) {
+ /* Do not request redraw during viewport animation to lock the framerate to the animation
+ * playback rate. This is in order to preserve motion blur aspect and also to avoid TAA reset
+ * that can show flickering. */
+ if (!sampling.finished_viewport() && !DRW_state_is_playback()) {
DRW_viewport_request_redraw();
}
@@ -271,6 +325,76 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl)
}
}
+void Instance::store_metadata(RenderResult *render_result)
+{
+ cryptomatte.store_metadata(render_result);
+}
+
+void Instance::update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer)
+{
+ RE_engine_register_pass(engine, scene, view_layer, RE_PASSNAME_COMBINED, 4, "RGBA", SOCK_RGBA);
+
+#define CHECK_PASS_LEGACY(name, type, channels, chanid) \
+ if (view_layer->passflag & (SCE_PASS_##name)) { \
+ RE_engine_register_pass( \
+ engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
+ } \
+ ((void)0)
+#define CHECK_PASS_EEVEE(name, type, channels, chanid) \
+ if (view_layer->eevee.render_passes & (EEVEE_RENDER_PASS_##name)) { \
+ RE_engine_register_pass( \
+ engine, scene, view_layer, RE_PASSNAME_##name, channels, chanid, type); \
+ } \
+ ((void)0)
+
+ CHECK_PASS_LEGACY(Z, SOCK_FLOAT, 1, "Z");
+ CHECK_PASS_LEGACY(MIST, SOCK_FLOAT, 1, "Z");
+ CHECK_PASS_LEGACY(NORMAL, SOCK_VECTOR, 3, "XYZ");
+ CHECK_PASS_LEGACY(DIFFUSE_DIRECT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(DIFFUSE_COLOR, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(GLOSSY_DIRECT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(GLOSSY_COLOR, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_EEVEE(VOLUME_LIGHT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(EMIT, SOCK_RGBA, 3, "RGB");
+ CHECK_PASS_LEGACY(ENVIRONMENT, SOCK_RGBA, 3, "RGB");
+ /* TODO: CHECK_PASS_LEGACY(SHADOW, SOCK_RGBA, 3, "RGB");
+ * CHECK_PASS_LEGACY(AO, SOCK_RGBA, 3, "RGB");
+ * When available they should be converted from Value textures to RGB. */
+
+ LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) {
+ if ((aov->flag & AOV_CONFLICT) != 0) {
+ continue;
+ }
+ switch (aov->type) {
+ case AOV_TYPE_COLOR:
+ RE_engine_register_pass(engine, scene, view_layer, aov->name, 4, "RGBA", SOCK_RGBA);
+ break;
+ case AOV_TYPE_VALUE:
+ RE_engine_register_pass(engine, scene, view_layer, aov->name, 1, "X", SOCK_FLOAT);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* NOTE: Name channels lowercase `rgba` so that compression rules check in OpenEXR DWA code uses
+ * lossless compression. Reportedly this naming is the only one which works good from the
+ * interoperability point of view. Using `xyzw` naming is not portable. */
+ auto register_cryptomatte_passes = [&](eViewLayerCryptomatteFlags cryptomatte_layer,
+ eViewLayerEEVEEPassType eevee_pass) {
+ if (view_layer->cryptomatte_flag & cryptomatte_layer) {
+ for (std::string pass_name : Film::pass_to_render_pass_names(eevee_pass, view_layer)) {
+ RE_engine_register_pass(
+ engine, scene, view_layer, pass_name.c_str(), 4, "rgba", SOCK_RGBA);
+ }
+ }
+ };
+ register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_OBJECT, EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT);
+ register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_ASSET, EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET);
+ register_cryptomatte_passes(VIEW_LAYER_CRYPTOMATTE_MATERIAL,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL);
+}
+
/** \} */
} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh
index f47d4f20363..c8eecbd812d 100644
--- a/source/blender/draw/engines/eevee_next/eevee_instance.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh
@@ -16,8 +16,13 @@
#include "DRW_render.h"
#include "eevee_camera.hh"
+#include "eevee_cryptomatte.hh"
+#include "eevee_depth_of_field.hh"
#include "eevee_film.hh"
+#include "eevee_hizbuffer.hh"
+#include "eevee_light.hh"
#include "eevee_material.hh"
+#include "eevee_motion_blur.hh"
#include "eevee_pipeline.hh"
#include "eevee_renderbuffers.hh"
#include "eevee_sampling.hh"
@@ -34,13 +39,19 @@ namespace blender::eevee {
*/
class Instance {
friend VelocityModule;
+ friend MotionBlurModule;
public:
ShaderModule &shaders;
SyncModule sync;
MaterialModule materials;
PipelineModule pipelines;
+ LightModule lights;
VelocityModule velocity;
+ MotionBlurModule motion_blur;
+ DepthOfField depth_of_field;
+ Cryptomatte cryptomatte;
+ HiZBuffer hiz_buffer;
Sampling sampling;
Camera camera;
Film film;
@@ -50,6 +61,7 @@ class Instance {
/** Input data. */
Depsgraph *depsgraph;
+ Manager *manager;
/** Evaluated IDs. */
Scene *scene;
ViewLayer *view_layer;
@@ -66,8 +78,10 @@ class Instance {
/** True if the grease pencil engine might be running. */
bool gpencil_engine_enabled;
- /* Info string displayed at the top of the render / viewport. */
+ /** Info string displayed at the top of the render / viewport. */
std::string info = "";
+ /** Debug mode from debug value. */
+ eDebugMode debug_mode = eDebugMode::DEBUG_NONE;
public:
Instance()
@@ -75,7 +89,12 @@ class Instance {
sync(*this),
materials(*this),
pipelines(*this),
+ lights(*this),
velocity(*this),
+ motion_blur(*this),
+ depth_of_field(*this),
+ cryptomatte(*this),
+ hiz_buffer(*this),
sampling(*this),
camera(*this),
film(*this),
@@ -101,9 +120,12 @@ class Instance {
void render_sync();
void render_frame(RenderLayer *render_layer, const char *view_name);
+ void store_metadata(RenderResult *render_result);
void draw_viewport(DefaultFramebufferList *dfbl);
+ static void update_passes(RenderEngine *engine, Scene *scene, ViewLayer *view_layer);
+
bool is_viewport() const
{
return render == nullptr;
@@ -111,7 +133,7 @@ class Instance {
bool overlays_enabled() const
{
- return (!v3d) || ((v3d->flag & V3D_HIDE_OVERLAYS) == 0);
+ return v3d && ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0);
}
bool use_scene_lights() const
@@ -138,6 +160,7 @@ class Instance {
RenderEngine *engine,
Depsgraph *depsgraph);
void render_sample();
+ void render_read_result(RenderLayer *render_layer, const char *view_name);
void mesh_sync(Object *ob, ObjectHandle &ob_handle);
diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc
new file mode 100644
index 00000000000..b60246fa3ab
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_light.cc
@@ -0,0 +1,488 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The light module manages light data buffers and light culling system.
+ */
+
+#include "draw_debug.hh"
+
+#include "eevee_instance.hh"
+
+#include "eevee_light.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name LightData
+ * \{ */
+
+static eLightType to_light_type(short blender_light_type, short blender_area_type)
+{
+ switch (blender_light_type) {
+ default:
+ case LA_LOCAL:
+ return LIGHT_POINT;
+ case LA_SUN:
+ return LIGHT_SUN;
+ case LA_SPOT:
+ return LIGHT_SPOT;
+ case LA_AREA:
+ return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Light Object
+ * \{ */
+
+void Light::sync(/* ShadowModule &shadows , */ const Object *ob, float threshold)
+{
+ const ::Light *la = (const ::Light *)ob->data;
+ float scale[3];
+
+ float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f);
+ float surface_max_power = max_ff(la->diff_fac, la->spec_fac) * max_power;
+ float volume_max_power = la->volume_fac * max_power;
+
+ float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power);
+ float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power);
+
+ this->influence_radius_max = max_ff(influence_radius_surface, influence_radius_volume);
+ this->influence_radius_invsqr_surface = 1.0f / square_f(max_ff(influence_radius_surface, 1e-8f));
+ this->influence_radius_invsqr_volume = 1.0f / square_f(max_ff(influence_radius_volume, 1e-8f));
+
+ this->color = float3(&la->r) * la->energy;
+ normalize_m4_m4_ex(this->object_mat.ptr(), ob->obmat, scale);
+ /* Make sure we have consistent handedness (in case of negatively scaled Z axis). */
+ float3 cross = math::cross(float3(this->_right), float3(this->_up));
+ if (math::dot(cross, float3(this->_back)) < 0.0f) {
+ negate_v3(this->_up);
+ }
+
+ shape_parameters_set(la, scale);
+
+ float shape_power = shape_power_get(la);
+ float point_power = point_power_get(la);
+ this->diffuse_power = la->diff_fac * shape_power;
+ this->transmit_power = la->diff_fac * point_power;
+ this->specular_power = la->spec_fac * shape_power;
+ this->volume_power = la->volume_fac * point_power;
+
+ eLightType new_type = to_light_type(la->type, la->area_shape);
+ if (this->type != new_type) {
+ /* shadow_discard_safe(shadows); */
+ this->type = new_type;
+ }
+
+#if 0
+ if (la->mode & LA_SHADOW) {
+ if (la->type == LA_SUN) {
+ if (this->shadow_id == LIGHT_NO_SHADOW) {
+ this->shadow_id = shadows.directionals.alloc();
+ }
+
+ ShadowDirectional &shadow = shadows.directionals[this->shadow_id];
+ shadow.sync(this->object_mat, la->bias * 0.05f, 1.0f);
+ }
+ else {
+ float cone_aperture = DEG2RAD(360.0);
+ if (la->type == LA_SPOT) {
+ cone_aperture = min_ff(DEG2RAD(179.9), la->spotsize);
+ }
+ else if (la->type == LA_AREA) {
+ cone_aperture = DEG2RAD(179.9);
+ }
+
+ if (this->shadow_id == LIGHT_NO_SHADOW) {
+ this->shadow_id = shadows.punctuals.alloc();
+ }
+
+ ShadowPunctual &shadow = shadows.punctuals[this->shadow_id];
+ shadow.sync(this->type,
+ this->object_mat,
+ cone_aperture,
+ la->clipsta,
+ this->influence_radius_max,
+ la->bias * 0.05f);
+ }
+ }
+ else {
+ shadow_discard_safe(shadows);
+ }
+#endif
+
+ this->initialized = true;
+}
+
+#if 0
+void Light::shadow_discard_safe(ShadowModule &shadows)
+{
+ if (shadow_id != LIGHT_NO_SHADOW) {
+ if (this->type != LIGHT_SUN) {
+ shadows.punctuals.free(shadow_id);
+ }
+ else {
+ shadows.directionals.free(shadow_id);
+ }
+ shadow_id = LIGHT_NO_SHADOW;
+ }
+}
+#endif
+
+/* Returns attenuation radius inverted & squared for easy bound checking inside the shader. */
+float Light::attenuation_radius_get(const ::Light *la, float light_threshold, float light_power)
+{
+ if (la->type == LA_SUN) {
+ return (light_power > 1e-5f) ? 1e16f : 0.0f;
+ }
+
+ if (la->mode & LA_CUSTOM_ATTENUATION) {
+ return la->att_dist;
+ }
+ /* Compute the distance (using the inverse square law)
+ * at which the light power reaches the light_threshold. */
+ /* TODO take area light scale into account. */
+ return sqrtf(light_power / light_threshold);
+}
+
+void Light::shape_parameters_set(const ::Light *la, const float scale[3])
+{
+ if (la->type == LA_AREA) {
+ float area_size_y = (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) ? la->area_sizey :
+ la->area_size;
+ _area_size_x = max_ff(0.003f, la->area_size * scale[0] * 0.5f);
+ _area_size_y = max_ff(0.003f, area_size_y * scale[1] * 0.5f);
+ /* For volume point lighting. */
+ radius_squared = max_ff(0.001f, hypotf(_area_size_x, _area_size_y) * 0.5f);
+ radius_squared = square_f(radius_squared);
+ }
+ else {
+ if (la->type == LA_SPOT) {
+ /* Spot size & blend */
+ spot_size_inv[0] = scale[2] / scale[0];
+ spot_size_inv[1] = scale[2] / scale[1];
+ float spot_size = cosf(la->spotsize * 0.5f);
+ float spot_blend = (1.0f - spot_size) * la->spotblend;
+ _spot_mul = 1.0f / max_ff(1e-8f, spot_blend);
+ _spot_bias = -spot_size * _spot_mul;
+ spot_tan = tanf(min_ff(la->spotsize * 0.5f, M_PI_2 - 0.0001f));
+ }
+
+ if (la->type == LA_SUN) {
+ _area_size_x = tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f);
+ }
+ else {
+ _area_size_x = la->area_size;
+ }
+ _area_size_y = _area_size_x = max_ff(0.001f, _area_size_x);
+ radius_squared = square_f(_area_size_x);
+ }
+}
+
+float Light::shape_power_get(const ::Light *la)
+{
+ /* Make illumination power constant */
+ switch (la->type) {
+ case LA_AREA: {
+ float area = _area_size_x * _area_size_y;
+ float power = 1.0f / (area * 4.0f * float(M_PI));
+ /* FIXME : Empirical, Fit cycles power */
+ power *= 0.8f;
+ if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) {
+ /* Scale power to account for the lower area of the ellipse compared to the surrounding
+ * rectangle. */
+ power *= 4.0f / M_PI;
+ }
+ return power;
+ }
+ case LA_SPOT:
+ case LA_LOCAL: {
+ return 1.0f / (4.0f * square_f(_radius) * float(M_PI * M_PI));
+ }
+ default:
+ case LA_SUN: {
+ float power = 1.0f / (square_f(_radius) * float(M_PI));
+ /* 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. */
+ /* Simplification of: power *= 1 + r²/2 */
+ power += 1.0f / (2.0f * M_PI);
+
+ return power;
+ }
+ }
+}
+
+float Light::point_power_get(const ::Light *la)
+{
+ /* Volume light is evaluated as point lights. Remove the shape power. */
+ switch (la->type) {
+ case LA_AREA: {
+ /* Match cycles. Empirical fit... must correspond to some constant. */
+ float power = 0.0792f * M_PI;
+
+ /* This corrects for area light most representative point trick. The fit was found by
+ * reducing the average error compared to cycles. */
+ float area = _area_size_x * _area_size_y;
+ float tmp = M_PI_2 / (M_PI_2 + sqrtf(area));
+ /* Lerp between 1.0 and the limit (1 / pi). */
+ power *= tmp + (1.0f - tmp) * M_1_PI;
+
+ return power;
+ }
+ case LA_SPOT:
+ case LA_LOCAL: {
+ /* Match cycles. Empirical fit... must correspond to some constant. */
+ return 0.0792f;
+ }
+ default:
+ case LA_SUN: {
+ return 1.0f;
+ }
+ }
+}
+
+void Light::debug_draw()
+{
+#ifdef DEBUG
+ drw_debug_sphere(_position, influence_radius_max, float4(0.8f, 0.3f, 0.0f, 1.0f));
+#endif
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name LightModule
+ * \{ */
+
+void LightModule::begin_sync()
+{
+ use_scene_lights_ = inst_.use_scene_lights();
+
+ /* In begin_sync so it can be animated. */
+ if (assign_if_different(light_threshold_, max_ff(1e-16f, inst_.scene->eevee.light_threshold))) {
+ inst_.sampling.reset();
+ }
+
+ sun_lights_len_ = 0;
+ local_lights_len_ = 0;
+}
+
+void LightModule::sync_light(const Object *ob, ObjectHandle &handle)
+{
+ if (use_scene_lights_ == false) {
+ return;
+ }
+ Light &light = light_map_.lookup_or_add_default(handle.object_key);
+ light.used = true;
+ if (handle.recalc != 0 || !light.initialized) {
+ light.sync(/* inst_.shadows, */ ob, light_threshold_);
+ }
+ sun_lights_len_ += int(light.type == LIGHT_SUN);
+ local_lights_len_ += int(light.type != LIGHT_SUN);
+}
+
+void LightModule::end_sync()
+{
+ // ShadowModule &shadows = inst_.shadows;
+
+ /* NOTE: We resize this buffer before removing deleted lights. */
+ int lights_allocated = ceil_to_multiple_u(max_ii(light_map_.size(), 1), LIGHT_CHUNK);
+ light_buf_.resize(lights_allocated);
+
+ /* Track light deletion. */
+ Vector<ObjectKey, 0> deleted_keys;
+ /* Indices inside GPU data array. */
+ int sun_lights_idx = 0;
+ int local_lights_idx = sun_lights_len_;
+
+ /* Fill GPU data with scene data. */
+ for (auto item : light_map_.items()) {
+ Light &light = item.value;
+
+ if (!light.used) {
+ /* Deleted light. */
+ deleted_keys.append(item.key);
+ // light.shadow_discard_safe(shadows);
+ continue;
+ }
+
+ int dst_idx = (light.type == LIGHT_SUN) ? sun_lights_idx++ : local_lights_idx++;
+ /* Put all light data into global data SSBO. */
+ light_buf_[dst_idx] = light;
+
+#if 0
+ if (light.shadow_id != LIGHT_NO_SHADOW) {
+ if (light.type == LIGHT_SUN) {
+ light_buf_[dst_idx].shadow_data = shadows.directionals[light.shadow_id];
+ }
+ else {
+ light_buf_[dst_idx].shadow_data = shadows.punctuals[light.shadow_id];
+ }
+ }
+#endif
+ /* Untag for next sync. */
+ light.used = false;
+ }
+ /* This scene data buffer is then immutable after this point. */
+ light_buf_.push_update();
+
+ for (auto &key : deleted_keys) {
+ light_map_.remove(key);
+ }
+
+ /* Update sampling on deletion or un-hiding (use_scene_lights). */
+ if (assign_if_different(light_map_size_, light_map_.size())) {
+ inst_.sampling.reset();
+ }
+
+ /* If exceeding the limit, just trim off the excess to avoid glitchy rendering. */
+ if (sun_lights_len_ + local_lights_len_ > CULLING_MAX_ITEM) {
+ sun_lights_len_ = min_ii(sun_lights_len_, CULLING_MAX_ITEM);
+ local_lights_len_ = min_ii(local_lights_len_, CULLING_MAX_ITEM - sun_lights_len_);
+ inst_.info = "Error: Too many lights in the scene.";
+ }
+ lights_len_ = sun_lights_len_ + local_lights_len_;
+
+ /* Resize to the actual number of lights after pruning. */
+ lights_allocated = ceil_to_multiple_u(max_ii(lights_len_, 1), LIGHT_CHUNK);
+ culling_key_buf_.resize(lights_allocated);
+ culling_zdist_buf_.resize(lights_allocated);
+ culling_light_buf_.resize(lights_allocated);
+
+ {
+ /* Compute tile size and total word count. */
+ uint word_per_tile = divide_ceil_u(max_ii(lights_len_, 1), 32);
+ int2 render_extent = inst_.film.render_extent_get();
+ int2 tiles_extent;
+ /* Default to 32 as this is likely to be the maximum
+ * tile size used by hardware or compute shading. */
+ uint tile_size = 16;
+ do {
+ tile_size *= 2;
+ tiles_extent = math::divide_ceil(render_extent, int2(tile_size));
+ uint tile_count = tiles_extent.x * tiles_extent.y;
+ if (tile_count > max_tile_count_threshold) {
+ continue;
+ }
+ total_word_count_ = tile_count * word_per_tile;
+
+ } while (total_word_count_ > max_word_count_threshold);
+ /* Keep aligned with storage buffer requirements. */
+ total_word_count_ = ceil_to_multiple_u(total_word_count_, 32);
+
+ culling_data_buf_.tile_word_len = word_per_tile;
+ culling_data_buf_.tile_size = tile_size;
+ culling_data_buf_.tile_x_len = tiles_extent.x;
+ culling_data_buf_.tile_y_len = tiles_extent.y;
+ culling_data_buf_.items_count = lights_len_;
+ culling_data_buf_.local_lights_len = local_lights_len_;
+ culling_data_buf_.sun_lights_len = sun_lights_len_;
+ }
+ culling_tile_buf_.resize(total_word_count_);
+
+ culling_pass_sync();
+ debug_pass_sync();
+}
+
+void LightModule::culling_pass_sync()
+{
+ uint safe_lights_len = max_ii(lights_len_, 1);
+ uint culling_select_dispatch_size = divide_ceil_u(safe_lights_len, CULLING_SELECT_GROUP_SIZE);
+ uint culling_sort_dispatch_size = divide_ceil_u(safe_lights_len, CULLING_SORT_GROUP_SIZE);
+ uint culling_tile_dispatch_size = divide_ceil_u(total_word_count_, CULLING_TILE_GROUP_SIZE);
+
+ /* NOTE: We reference the buffers that may be resized or updated later. */
+
+ culling_ps_.init();
+ {
+ auto &sub = culling_ps_.sub("Select");
+ sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_SELECT));
+ sub.bind_ssbo("light_cull_buf", &culling_data_buf_);
+ sub.bind_ssbo("in_light_buf", light_buf_);
+ sub.bind_ssbo("out_light_buf", culling_light_buf_);
+ sub.bind_ssbo("out_zdist_buf", culling_zdist_buf_);
+ sub.bind_ssbo("out_key_buf", culling_key_buf_);
+ sub.dispatch(int3(culling_select_dispatch_size, 1, 1));
+ sub.barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+ {
+ auto &sub = culling_ps_.sub("Sort");
+ sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_SORT));
+ sub.bind_ssbo("light_cull_buf", &culling_data_buf_);
+ sub.bind_ssbo("in_light_buf", light_buf_);
+ sub.bind_ssbo("out_light_buf", culling_light_buf_);
+ sub.bind_ssbo("in_zdist_buf", culling_zdist_buf_);
+ sub.bind_ssbo("in_key_buf", culling_key_buf_);
+ sub.dispatch(int3(culling_sort_dispatch_size, 1, 1));
+ sub.barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+ {
+ auto &sub = culling_ps_.sub("Zbin");
+ sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_ZBIN));
+ sub.bind_ssbo("light_cull_buf", &culling_data_buf_);
+ sub.bind_ssbo("light_buf", culling_light_buf_);
+ sub.bind_ssbo("out_zbin_buf", culling_zbin_buf_);
+ sub.dispatch(int3(1, 1, 1));
+ sub.barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+ {
+ auto &sub = culling_ps_.sub("Tiles");
+ sub.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_TILE));
+ sub.bind_ssbo("light_cull_buf", &culling_data_buf_);
+ sub.bind_ssbo("light_buf", culling_light_buf_);
+ sub.bind_ssbo("out_light_tile_buf", culling_tile_buf_);
+ sub.dispatch(int3(culling_tile_dispatch_size, 1, 1));
+ sub.barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+}
+
+void LightModule::debug_pass_sync()
+{
+ if (inst_.debug_mode == eDebugMode::DEBUG_LIGHT_CULLING) {
+ debug_draw_ps_.init();
+ debug_draw_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM);
+ debug_draw_ps_.shader_set(inst_.shaders.static_shader_get(LIGHT_CULLING_DEBUG));
+ inst_.hiz_buffer.bind_resources(&debug_draw_ps_);
+ debug_draw_ps_.bind_ssbo("light_buf", &culling_light_buf_);
+ debug_draw_ps_.bind_ssbo("light_cull_buf", &culling_data_buf_);
+ debug_draw_ps_.bind_ssbo("light_zbin_buf", &culling_zbin_buf_);
+ debug_draw_ps_.bind_ssbo("light_tile_buf", &culling_tile_buf_);
+ debug_draw_ps_.bind_texture("depth_tx", &inst_.render_buffers.depth_tx);
+ debug_draw_ps_.draw_procedural(GPU_PRIM_TRIS, 1, 3);
+ }
+}
+
+void LightModule::set_view(View &view, const int2 extent)
+{
+ float far_z = view.far_clip();
+ float near_z = view.near_clip();
+
+ culling_data_buf_.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z);
+ culling_data_buf_.zbin_bias = -near_z * culling_data_buf_.zbin_scale;
+ culling_data_buf_.tile_to_uv_fac = (culling_data_buf_.tile_size / float2(extent));
+ culling_data_buf_.visible_count = 0;
+ culling_data_buf_.push_update();
+
+ inst_.manager->submit(culling_ps_, view);
+}
+
+void LightModule::debug_draw(View &view, GPUFrameBuffer *view_fb)
+{
+ if (inst_.debug_mode == eDebugMode::DEBUG_LIGHT_CULLING) {
+ inst_.info = "Debug Mode: Light Culling Validation";
+ inst_.hiz_buffer.update();
+ GPU_framebuffer_bind(view_fb);
+ inst_.manager->submit(debug_draw_ps_, view);
+ }
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_light.hh b/source/blender/draw/engines/eevee_next/eevee_light.hh
new file mode 100644
index 00000000000..9bacc180ea8
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_light.hh
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * The light module manages light data buffers and light culling system.
+ *
+ * The culling follows the principles of Tiled Culling + Z binning from:
+ * "Improved Culling for Tiled and Clustered Rendering"
+ * by Michal Drobot
+ * http://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf
+ *
+ * The culling is separated in 4 compute phases:
+ * - View Culling (select pass): Create a z distance and a index buffer of visible lights.
+ * - Light sorting: Outputs visible lights sorted by Z distance.
+ * - Z binning: Compute the Z bins min/max light indices.
+ * - Tile intersection: Fine grained 2D culling of each lights outputting a bitmap per tile.
+ */
+
+#pragma once
+
+#include "BLI_bitmap.h"
+#include "BLI_vector.hh"
+#include "DNA_light_types.h"
+
+#include "eevee_camera.hh"
+#include "eevee_sampling.hh"
+#include "eevee_shader.hh"
+#include "eevee_shader_shared.hh"
+#include "eevee_sync.hh"
+
+namespace blender::eevee {
+
+class Instance;
+
+/* -------------------------------------------------------------------- */
+/** \name Light Object
+ * \{ */
+
+struct Light : public LightData {
+ public:
+ bool initialized = false;
+ bool used = false;
+
+ public:
+ Light()
+ {
+ shadow_id = LIGHT_NO_SHADOW;
+ }
+
+ void sync(/* ShadowModule &shadows, */ const Object *ob, float threshold);
+
+ // void shadow_discard_safe(ShadowModule &shadows);
+
+ void debug_draw();
+
+ private:
+ float attenuation_radius_get(const ::Light *la, float light_threshold, float light_power);
+ void shape_parameters_set(const ::Light *la, const float scale[3]);
+ float shape_power_get(const ::Light *la);
+ float point_power_get(const ::Light *la);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name LightModule
+ * \{ */
+
+/**
+ * The light module manages light data buffers and light culling system.
+ */
+class LightModule {
+ // friend ShadowModule;
+
+ private:
+ /* Keep tile count reasonable for memory usage and 2D culling performance. */
+ static constexpr uint max_memory_threshold = 32 * 1024 * 1024; /* 32 MiB */
+ static constexpr uint max_word_count_threshold = max_memory_threshold / sizeof(uint);
+ static constexpr uint max_tile_count_threshold = 8192;
+
+ Instance &inst_;
+
+ /** Map of light objects data. Converted to flat array each frame. */
+ Map<ObjectKey, Light> light_map_;
+ /** Flat array sent to GPU, populated from light_map_. Source buffer for light culling. */
+ LightDataBuf light_buf_ = {"Lights_no_cull"};
+ /** Recorded size of light_map_ (after pruning) to detect deletion. */
+ int64_t light_map_size_ = 0;
+ /** Luminous intensity to consider the light boundary at. Used for culling. */
+ float light_threshold_ = 0.01f;
+ /** If false, will prevent all scene light from being synced. */
+ bool use_scene_lights_ = false;
+ /** Number of sun lights synced during the last sync. Used as offset. */
+ int sun_lights_len_ = 0;
+ int local_lights_len_ = 0;
+ /** Sun plus local lights count for convenience. */
+ int lights_len_ = 0;
+
+ /**
+ * Light Culling
+ */
+
+ /** LightData buffer used for rendering. Filled by the culling pass. */
+ LightDataBuf culling_light_buf_ = {"Lights_culled"};
+ /** Culling infos. */
+ LightCullingDataBuf culling_data_buf_ = {"LightCull_data"};
+ /** Z-distance matching the key for each visible lights. Used for sorting. */
+ LightCullingZdistBuf culling_zdist_buf_ = {"LightCull_zdist"};
+ /** Key buffer containing only visible lights indices. Used for sorting. */
+ LightCullingKeyBuf culling_key_buf_ = {"LightCull_key"};
+ /** Zbins containing min and max light index for each Z bin. */
+ LightCullingZbinBuf culling_zbin_buf_ = {"LightCull_zbin"};
+ /** Bitmap of lights touching each tiles. */
+ LightCullingTileBuf culling_tile_buf_ = {"LightCull_tile"};
+ /** Culling compute passes. */
+ PassSimple culling_ps_ = {"LightCulling"};
+ /** Total number of words the tile buffer needs to contain for the render resolution. */
+ uint total_word_count_ = 0;
+
+ /** Debug Culling visualization. */
+ PassSimple debug_draw_ps_ = {"LightCulling.Debug"};
+
+ public:
+ LightModule(Instance &inst) : inst_(inst){};
+ ~LightModule(){};
+
+ void begin_sync();
+ void sync_light(const Object *ob, ObjectHandle &handle);
+ void end_sync();
+
+ /**
+ * Update acceleration structure for the given view.
+ */
+ void set_view(View &view, const int2 extent);
+
+ void debug_draw(View &view, GPUFrameBuffer *view_fb);
+
+ void bind_resources(DRWShadingGroup *grp)
+ {
+ DRW_shgroup_storage_block_ref(grp, "light_buf", &culling_light_buf_);
+ DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_);
+ DRW_shgroup_storage_block_ref(grp, "light_zbin_buf", &culling_zbin_buf_);
+ DRW_shgroup_storage_block_ref(grp, "light_tile_buf", &culling_tile_buf_);
+#if 0
+ DRW_shgroup_uniform_texture(grp, "shadow_atlas_tx", inst_.shadows.atlas_tx_get());
+ DRW_shgroup_uniform_texture(grp, "shadow_tilemaps_tx", inst_.shadows.tilemap_tx_get());
+#endif
+ }
+
+ template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
+ {
+ /* Storage Buf. */
+ pass->bind_ssbo(LIGHT_CULL_BUF_SLOT, &culling_data_buf_);
+ pass->bind_ssbo(LIGHT_BUF_SLOT, &culling_light_buf_);
+ pass->bind_ssbo(LIGHT_ZBIN_BUF_SLOT, &culling_zbin_buf_);
+ pass->bind_ssbo(LIGHT_TILE_BUF_SLOT, &culling_tile_buf_);
+ }
+
+ private:
+ void culling_pass_sync();
+ void debug_pass_sync();
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_material.cc b/source/blender/draw/engines/eevee_next/eevee_material.cc
index b3161a67092..a92f96e8c70 100644
--- a/source/blender/draw/engines/eevee_next/eevee_material.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_material.cc
@@ -72,10 +72,9 @@ bNodeTree *DefaultSurfaceNodeTree::nodetree_get(::Material *ma)
MaterialModule::MaterialModule(Instance &inst) : inst_(inst)
{
{
- bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
-
diffuse_mat = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse");
- diffuse_mat->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ nullptr, &diffuse_mat->id, "Shader Nodetree", ntreeType_Shader->idname);
diffuse_mat->use_nodes = true;
/* To use the forward pipeline. */
diffuse_mat->blend_method = MA_BM_BLEND;
@@ -95,10 +94,9 @@ MaterialModule::MaterialModule(Instance &inst) : inst_(inst)
nodeSetActive(ntree, output);
}
{
- bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
-
glossy_mat = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal");
- glossy_mat->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ nullptr, &glossy_mat->id, "Shader Nodetree", ntreeType_Shader->idname);
glossy_mat->use_nodes = true;
/* To use the forward pipeline. */
glossy_mat->blend_method = MA_BM_BLEND;
@@ -120,10 +118,9 @@ MaterialModule::MaterialModule(Instance &inst) : inst_(inst)
nodeSetActive(ntree, output);
}
{
- bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
-
error_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default error");
- error_mat_->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ nullptr, &error_mat_->id, "Shader Nodetree", ntreeType_Shader->idname);
error_mat_->use_nodes = true;
/* Use emission and output material to be compatible with both World and Material. */
@@ -145,9 +142,6 @@ MaterialModule::MaterialModule(Instance &inst) : inst_(inst)
MaterialModule::~MaterialModule()
{
- for (Material *mat : material_map_.values()) {
- delete mat;
- }
BKE_id_free(nullptr, glossy_mat);
BKE_id_free(nullptr, diffuse_mat);
BKE_id_free(nullptr, error_mat_);
@@ -157,13 +151,12 @@ void MaterialModule::begin_sync()
{
queued_shaders_count = 0;
- for (Material *mat : material_map_.values()) {
- mat->init = false;
- }
+ material_map_.clear();
shader_map_.clear();
}
-MaterialPass MaterialModule::material_pass_get(::Material *blender_mat,
+MaterialPass MaterialModule::material_pass_get(Object *ob,
+ ::Material *blender_mat,
eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type)
{
@@ -203,35 +196,34 @@ MaterialPass MaterialModule::material_pass_get(::Material *blender_mat,
pipeline_type = MAT_PIPE_FORWARD;
}
- if ((pipeline_type == MAT_PIPE_FORWARD) &&
+ if (ELEM(pipeline_type,
+ MAT_PIPE_FORWARD,
+ MAT_PIPE_FORWARD_PREPASS,
+ MAT_PIPE_FORWARD_PREPASS_VELOCITY) &&
GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_TRANSPARENT)) {
- /* Transparent needs to use one shgroup per object to support reordering. */
- matpass.shgrp = inst_.pipelines.material_add(blender_mat, matpass.gpumat, pipeline_type);
+ /* Transparent pass is generated later. */
+ matpass.sub_pass = nullptr;
}
else {
ShaderKey shader_key(matpass.gpumat, geometry_type, pipeline_type);
- auto add_cb = [&]() -> DRWShadingGroup * {
- /* First time encountering this shader. Create a shading group. */
- return inst_.pipelines.material_add(blender_mat, matpass.gpumat, pipeline_type);
- };
- DRWShadingGroup *grp = shader_map_.lookup_or_add_cb(shader_key, add_cb);
-
- if (grp != nullptr) {
- /* Shading group for this shader already exists. Create a sub one for this material. */
- /* IMPORTANT: We always create a subgroup so that all subgroups are inserted after the
- * first "empty" shgroup. This avoids messing the order of subgroups when there is more
- * nested subgroup (i.e: hair drawing). */
- /* TODO(@fclem): Remove material resource binding from the first group creation. */
- matpass.shgrp = DRW_shgroup_create_sub(grp);
- DRW_shgroup_add_material_resources(matpass.shgrp, matpass.gpumat);
+ PassMain::Sub *shader_sub = shader_map_.lookup_or_add_cb(shader_key, [&]() {
+ /* First time encountering this shader. Create a sub that will contain materials using it. */
+ return inst_.pipelines.material_add(ob, blender_mat, matpass.gpumat, pipeline_type);
+ });
+
+ if (shader_sub != nullptr) {
+ /* Create a sub for this material as `shader_sub` is for sharing shader between materials. */
+ matpass.sub_pass = &shader_sub->sub(GPU_material_get_name(matpass.gpumat));
+ matpass.sub_pass->material_set(*inst_.manager, matpass.gpumat);
}
}
return matpass;
}
-Material &MaterialModule::material_sync(::Material *blender_mat,
+Material &MaterialModule::material_sync(Object *ob,
+ ::Material *blender_mat,
eMaterialGeometry geometry_type,
bool has_motion)
{
@@ -249,27 +241,32 @@ Material &MaterialModule::material_sync(::Material *blender_mat,
MaterialKey material_key(blender_mat, geometry_type, surface_pipe);
- /* TODO: allocate in blocks to avoid memory fragmentation. */
- auto add_cb = [&]() { return new Material(); };
- Material &mat = *material_map_.lookup_or_add_cb(material_key, add_cb);
-
- /* Forward pipeline needs to use one shgroup per object. */
- if (mat.init == false || (surface_pipe == MAT_PIPE_FORWARD)) {
- mat.init = true;
+ Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() {
+ Material mat;
/* Order is important for transparent. */
- mat.prepass = material_pass_get(blender_mat, prepass_pipe, geometry_type);
- mat.shading = material_pass_get(blender_mat, surface_pipe, geometry_type);
+ mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type);
+ mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type);
if (blender_mat->blend_shadow == MA_BS_NONE) {
mat.shadow = MaterialPass();
}
else {
- mat.shadow = material_pass_get(blender_mat, MAT_PIPE_SHADOW, geometry_type);
+ mat.shadow = material_pass_get(ob, blender_mat, MAT_PIPE_SHADOW, geometry_type);
}
-
mat.is_alpha_blend_transparent = (blender_mat->blend_method == MA_BM_BLEND) &&
- GPU_material_flag_get(mat.prepass.gpumat,
+ GPU_material_flag_get(mat.shading.gpumat,
GPU_MATFLAG_TRANSPARENT);
+ return mat;
+ });
+
+ if (mat.is_alpha_blend_transparent) {
+ /* Transparent needs to use one sub pass per object to support reordering.
+ * NOTE: Pre-pass needs to be created first in order to be sorted first. */
+ mat.prepass.sub_pass = inst_.pipelines.forward.prepass_transparent_add(
+ ob, blender_mat, mat.shading.gpumat);
+ mat.shading.sub_pass = inst_.pipelines.forward.material_transparent_add(
+ ob, blender_mat, mat.shading.gpumat);
}
+
return mat;
}
@@ -297,7 +294,7 @@ MaterialArray &MaterialModule::material_array_get(Object *ob, bool has_motion)
for (auto i : IndexRange(materials_len)) {
::Material *blender_mat = material_from_slot(ob, i);
- Material &mat = material_sync(blender_mat, to_material_geometry(ob), has_motion);
+ Material &mat = material_sync(ob, blender_mat, to_material_geometry(ob), has_motion);
material_array_.materials.append(&mat);
material_array_.gpu_materials.append(mat.shading.gpumat);
}
@@ -310,7 +307,7 @@ Material &MaterialModule::material_get(Object *ob,
eMaterialGeometry geometry_type)
{
::Material *blender_mat = material_from_slot(ob, mat_nr);
- Material &mat = material_sync(blender_mat, geometry_type, has_motion);
+ Material &mat = material_sync(ob, blender_mat, geometry_type, has_motion);
return mat;
}
diff --git a/source/blender/draw/engines/eevee_next/eevee_material.hh b/source/blender/draw/engines/eevee_next/eevee_material.hh
index 23165a741b9..ad0c293926b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_material.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_material.hh
@@ -203,12 +203,11 @@ class DefaultSurfaceNodeTree {
* \{ */
struct MaterialPass {
- GPUMaterial *gpumat = nullptr;
- DRWShadingGroup *shgrp = nullptr;
+ GPUMaterial *gpumat;
+ PassMain::Sub *sub_pass;
};
struct Material {
- bool init = false;
bool is_alpha_blend_transparent;
MaterialPass shadow, shading, prepass;
};
@@ -228,8 +227,8 @@ class MaterialModule {
private:
Instance &inst_;
- Map<MaterialKey, Material *> material_map_;
- Map<ShaderKey, DRWShadingGroup *> shader_map_;
+ Map<MaterialKey, Material> material_map_;
+ Map<ShaderKey, PassMain::Sub *> shader_map_;
MaterialArray material_array_;
@@ -254,13 +253,15 @@ class MaterialModule {
Material &material_get(Object *ob, bool has_motion, int mat_nr, eMaterialGeometry geometry_type);
private:
- Material &material_sync(::Material *blender_mat,
+ Material &material_sync(Object *ob,
+ ::Material *blender_mat,
eMaterialGeometry geometry_type,
bool has_motion);
/** Return correct material or empty default material if slot is empty. */
::Material *material_from_slot(Object *ob, int slot);
- MaterialPass material_pass_get(::Material *blender_mat,
+ MaterialPass material_pass_get(Object *ob,
+ ::Material *blender_mat,
eMaterialPipeline pipeline_type,
eMaterialGeometry geometry_type);
};
diff --git a/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc b/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc
new file mode 100644
index 00000000000..f68abafa3d4
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc
@@ -0,0 +1,256 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2021 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ */
+
+// #include "BLI_map.hh"
+#include "DEG_depsgraph_query.h"
+
+#include "eevee_instance.hh"
+#include "eevee_motion_blur.hh"
+// #include "eevee_sampling.hh"
+// #include "eevee_shader_shared.hh"
+// #include "eevee_velocity.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name MotionBlurModule
+ *
+ * \{ */
+
+void MotionBlurModule::init()
+{
+ const Scene *scene = inst_.scene;
+
+ enabled_ = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
+
+ if (!enabled_) {
+ motion_blur_fx_enabled_ = false;
+ return;
+ }
+
+ /* Take into account the steps needed for fx motion blur. */
+ int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1;
+
+ time_steps_.resize(steps_count);
+
+ initial_frame_ = scene->r.cfra;
+ initial_subframe_ = scene->r.subframe;
+ frame_time_ = initial_frame_ + initial_subframe_;
+ shutter_position_ = scene->eevee.motion_blur_position;
+ shutter_time_ = scene->eevee.motion_blur_shutter;
+
+ data_.depth_scale = scene->eevee.motion_blur_depth_scale;
+ motion_blur_fx_enabled_ = true; /* TODO(fclem): UI option. */
+
+ /* Viewport stops here. We only do Post-FX motion blur. */
+ if (inst_.is_viewport()) {
+ enabled_ = false;
+ return;
+ }
+
+ /* Without this there is the possibility of the curve table not being allocated. */
+ BKE_curvemapping_changed((struct CurveMapping *)&scene->r.mblur_shutter_curve, false);
+
+ Vector<float> cdf(CM_TABLE);
+ Sampling::cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf);
+ Sampling::cdf_invert(cdf, time_steps_);
+
+ for (float &time : time_steps_) {
+ time = this->shutter_time_to_scene_time(time);
+ }
+
+ step_id_ = 1;
+
+ if (motion_blur_fx_enabled_) {
+ /* A bit weird but we have to sync the first 2 steps here because the step()
+ * function is only called after rendering a sample. */
+ inst_.velocity.step_sync(STEP_PREVIOUS, time_steps_[0]);
+ inst_.velocity.step_sync(STEP_NEXT, time_steps_[2]);
+ }
+ inst_.set_time(time_steps_[1]);
+}
+
+/* Runs after rendering a sample. */
+void MotionBlurModule::step()
+{
+ if (!enabled_) {
+ return;
+ }
+
+ if (inst_.sampling.finished()) {
+ /* Restore original frame number. This is because the render pipeline expects it. */
+ RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_);
+ }
+ else if (inst_.sampling.do_render_sync()) {
+ /* Time to change motion step. */
+ BLI_assert(time_steps_.size() > step_id_ + 2);
+ step_id_ += 2;
+
+ if (motion_blur_fx_enabled_) {
+ inst_.velocity.step_swap();
+ inst_.velocity.step_sync(eVelocityStep::STEP_NEXT, time_steps_[step_id_ + 1]);
+ }
+ inst_.set_time(time_steps_[step_id_]);
+ }
+}
+
+float MotionBlurModule::shutter_time_to_scene_time(float time)
+{
+ switch (shutter_position_) {
+ case SCE_EEVEE_MB_START:
+ /* No offset. */
+ break;
+ case SCE_EEVEE_MB_CENTER:
+ time -= 0.5f;
+ break;
+ case SCE_EEVEE_MB_END:
+ time -= 1.0;
+ break;
+ default:
+ BLI_assert(!"Invalid motion blur position enum!");
+ break;
+ }
+ time *= shutter_time_;
+ time += frame_time_;
+ return time;
+}
+
+void MotionBlurModule::sync()
+{
+ /* Disable motion blur in viewport when changing camera projection type.
+ * Avoids really high velocities. */
+ if (inst_.velocity.camera_changed_projection()) {
+ motion_blur_fx_enabled_ = false;
+ }
+
+ if (!motion_blur_fx_enabled_) {
+ return;
+ }
+
+ eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+ RenderBuffers &render_buffers = inst_.render_buffers;
+
+ motion_blur_ps_.init();
+ inst_.velocity.bind_resources(&motion_blur_ps_);
+ inst_.sampling.bind_resources(&motion_blur_ps_);
+ {
+ /* Create max velocity tiles. */
+ PassSimple::Sub &sub = motion_blur_ps_.sub("TilesFlatten");
+ eShaderType shader = (inst_.is_viewport()) ? MOTION_BLUR_TILE_FLATTEN_VIEWPORT :
+ MOTION_BLUR_TILE_FLATTEN_RENDER;
+ sub.shader_set(inst_.shaders.static_shader_get(shader));
+ sub.bind_ubo("motion_blur_buf", data_);
+ sub.bind_texture("depth_tx", &render_buffers.depth_tx);
+ sub.bind_image("velocity_img", &render_buffers.vector_tx);
+ sub.bind_image("out_tiles_img", &tiles_tx_);
+ sub.dispatch(&dispatch_flatten_size_);
+ sub.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH);
+ }
+ {
+ /* Expand max velocity tiles by spreading them in their neighborhood. */
+ PassSimple::Sub &sub = motion_blur_ps_.sub("TilesDilate");
+ sub.shader_set(inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE));
+ sub.bind_ssbo("tile_indirection_buf", tile_indirection_buf_);
+ sub.bind_image("in_tiles_img", &tiles_tx_);
+ sub.dispatch(&dispatch_dilate_size_);
+ sub.barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+ {
+ /* Do the motion blur gather algorithm. */
+ PassSimple::Sub &sub = motion_blur_ps_.sub("ConvolveGather");
+ sub.shader_set(inst_.shaders.static_shader_get(MOTION_BLUR_GATHER));
+ sub.bind_ubo("motion_blur_buf", data_);
+ sub.bind_ssbo("tile_indirection_buf", tile_indirection_buf_);
+ sub.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
+ sub.bind_texture("velocity_tx", &render_buffers.vector_tx, no_filter);
+ sub.bind_texture("in_color_tx", &input_color_tx_, no_filter);
+ sub.bind_image("in_tiles_img", &tiles_tx_);
+ sub.bind_image("out_color_img", &output_color_tx_);
+
+ sub.dispatch(&dispatch_gather_size_);
+ sub.barrier(GPU_BARRIER_TEXTURE_FETCH);
+ }
+}
+
+void MotionBlurModule::render(View &view, GPUTexture **input_tx, GPUTexture **output_tx)
+{
+ if (!motion_blur_fx_enabled_) {
+ return;
+ }
+
+ const Texture &depth_tx = inst_.render_buffers.depth_tx;
+
+ int2 extent = {depth_tx.width(), depth_tx.height()};
+ int2 tiles_extent = math::divide_ceil(extent, int2(MOTION_BLUR_TILE_SIZE));
+
+ if (inst_.is_viewport()) {
+ float frame_delta = fabsf(inst_.velocity.step_time_delta_get(STEP_PREVIOUS, STEP_CURRENT));
+ /* Avoid highly disturbing blurs, during navigation with high shutter time. */
+ if (frame_delta > 0.0f && !DRW_state_is_navigating()) {
+ /* Rescale motion blur intensity to be shutter time relative and avoid long streak when we
+ * have frame skipping. Always try to stick to what the render frame would look like. */
+ data_.motion_scale = float2(shutter_time_ / frame_delta);
+ }
+ else {
+ /* There is no time change. Motion only comes from viewport navigation and object transform.
+ * Apply motion blur as smoothing and only blur towards last frame. */
+ data_.motion_scale = float2(1.0f, 0.0f);
+
+ if (was_navigating_ != DRW_state_is_navigating()) {
+ /* Special case for navigation events that only last for one frame (for instance mouse
+ * scroll for zooming). For this case we have to wait for the next frame before enabling
+ * the navigation motion blur. */
+ was_navigating_ = DRW_state_is_navigating();
+ return;
+ }
+ }
+ was_navigating_ = DRW_state_is_navigating();
+
+ /* Change texture swizzling to avoid complexity in gather pass shader. */
+ GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgrg");
+ }
+ else {
+ data_.motion_scale = float2(1.0f);
+ }
+ /* Second motion vector is stored inverted. */
+ data_.motion_scale.y = -data_.motion_scale.y;
+ data_.target_size_inv = 1.0f / float2(extent);
+ data_.push_update();
+
+ input_color_tx_ = *input_tx;
+ output_color_tx_ = *output_tx;
+
+ dispatch_flatten_size_ = int3(tiles_extent, 1);
+ dispatch_dilate_size_ = int3(math::divide_ceil(tiles_extent, int2(MOTION_BLUR_GROUP_SIZE)), 1);
+ dispatch_gather_size_ = int3(math::divide_ceil(extent, int2(MOTION_BLUR_GROUP_SIZE)), 1);
+
+ DRW_stats_group_start("Motion Blur");
+
+ tiles_tx_.acquire(tiles_extent, GPU_RGBA16F);
+
+ GPU_storagebuf_clear_to_zero(tile_indirection_buf_);
+
+ inst_.manager->submit(motion_blur_ps_, view);
+
+ tiles_tx_.release();
+
+ DRW_stats_group_end();
+
+ if (inst_.is_viewport()) {
+ /* Reset swizzle since this texture might be reused in other places. */
+ GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgba");
+ }
+
+ /* Swap buffers so that next effect has the right input. */
+ *input_tx = output_color_tx_;
+ *output_tx = input_color_tx_;
+}
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh b/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh
new file mode 100644
index 00000000000..056c2e323d5
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup eevee
+ *
+ * Motion blur is done by accumulating scene samples over shutter time.
+ * Since the number of step is discrete, quite low, and not per pixel randomized,
+ * we couple this with a post processing motion blur.
+ *
+ * The post-fx motion blur is done in two directions, from the previous step and to the next.
+ *
+ * For a scene with 3 motion steps, a flat shutter curve and shutter time of 2 frame
+ * centered on frame we have:
+ *
+ * |--------------------|--------------------|
+ * -1 0 1 Frames
+ *
+ * |-------------|-------------|-------------|
+ * 1 2 3 Motion steps
+ *
+ * |------|------|------|------|------|------|
+ * 0 1 2 4 5 6 7 Time Steps
+ *
+ * |-------------| One motion step blurs this range.
+ * -1 | +1 Objects and geometry steps are recorded here.
+ * 0 Scene is rendered here.
+ *
+ * Since motion step N and N+1 share one time step we reuse it to avoid an extra scene evaluation.
+ *
+ * Note that we have to evaluate -1 and +1 time steps before rendering so eval order is -1, +1, 0.
+ * This is because all GPUBatches from the DRWCache are being free when changing a frame.
+ *
+ * For viewport, we only have the current and previous step data to work with. So we center the
+ * blur on the current frame and extrapolate the motion.
+ *
+ * The Post-FX motion blur is based on:
+ * "A Fast and Stable Feature-Aware Motion Blur Filter"
+ * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
+ */
+
+#pragma once
+
+#include "BLI_map.hh"
+#include "DEG_depsgraph_query.h"
+
+#include "eevee_sampling.hh"
+#include "eevee_shader_shared.hh"
+#include "eevee_velocity.hh"
+
+namespace blender::eevee {
+
+/* -------------------------------------------------------------------- */
+/** \name MotionBlur
+ *
+ * \{ */
+
+/**
+ * Manages time-steps evaluations and accumulation Motion blur.
+ * Also handles Post process motion blur.
+ */
+class MotionBlurModule {
+ private:
+ Instance &inst_;
+
+ /**
+ * Array containing all steps (in scene time) we need to evaluate (not render).
+ * Only odd steps are rendered. The even ones are evaluated for fx motion blur.
+ */
+ Vector<float> time_steps_;
+
+ /** Copy of input frame and sub-frame to restore after render. */
+ int initial_frame_;
+ float initial_subframe_;
+ /** Time of the frame we are rendering. */
+ float frame_time_;
+ /** Enum controlling when the shutter opens. See SceneEEVEE.motion_blur_position. */
+ int shutter_position_;
+ /** Time in scene frame the shutter is open. Controls the amount of blur. */
+ float shutter_time_;
+
+ /** True if motion blur is enabled as a module. */
+ bool enabled_ = false;
+ /** True if motion blur post-fx is enabled. */
+ float motion_blur_fx_enabled_ = false;
+ /** True if last viewport redraw state was already in navigation state. */
+ bool was_navigating_ = false;
+
+ int step_id_ = 0;
+
+ /** Velocity tiles used to guide and speedup the gather pass. */
+ TextureFromPool tiles_tx_;
+
+ GPUTexture *input_color_tx_ = nullptr;
+ GPUTexture *output_color_tx_ = nullptr;
+
+ PassSimple motion_blur_ps_ = {"MotionBlur"};
+
+ MotionBlurTileIndirectionBuf tile_indirection_buf_;
+ MotionBlurDataBuf data_;
+ /** Dispatch size for full-screen passes. */
+ int3 dispatch_flatten_size_ = int3(0);
+ int3 dispatch_dilate_size_ = int3(0);
+ int3 dispatch_gather_size_ = int3(0);
+
+ public:
+ MotionBlurModule(Instance &inst) : inst_(inst){};
+ ~MotionBlurModule(){};
+
+ void init();
+
+ void step();
+
+ void sync();
+
+ bool postfx_enabled() const
+ {
+ return motion_blur_fx_enabled_;
+ }
+
+ void render(View &view, GPUTexture **input_tx, GPUTexture **output_tx);
+
+ private:
+ float shutter_time_to_scene_time(float time);
+};
+
+/** \} */
+
+} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
index 7b3cfbf5899..33978518ffc 100644
--- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc
@@ -24,36 +24,36 @@ namespace blender::eevee {
void WorldPipeline::sync(GPUMaterial *gpumat)
{
+ Manager &manager = *inst_.manager;
RenderBuffers &rbufs = inst_.render_buffers;
- DRWState state = DRW_STATE_WRITE_COLOR;
- world_ps_ = DRW_pass_create("World", state);
+ ResourceHandle handle = manager.resource_handle(float4x4::identity().ptr());
- /* Push a matrix at the same location as the camera. */
- float4x4 camera_mat = float4x4::identity();
- // copy_v3_v3(camera_mat[3], inst_.camera.data_get().viewinv[3]);
-
- DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, world_ps_);
- DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
- DRW_shgroup_call_obmat(grp, DRW_cache_fullscreen_quad_get(), camera_mat.ptr());
+ world_ps_.init();
+ world_ps_.state_set(DRW_STATE_WRITE_COLOR);
+ world_ps_.material_set(manager, gpumat);
+ world_ps_.push_constant("world_opacity_fade", inst_.film.background_opacity_get());
+ world_ps_.bind_texture("utility_tx", inst_.pipelines.utility_tx);
/* AOVs. */
- DRW_shgroup_uniform_image_ref(grp, "aov_color_img", &rbufs.aov_color_tx);
- DRW_shgroup_uniform_image_ref(grp, "aov_value_img", &rbufs.aov_value_tx);
- DRW_shgroup_storage_block_ref(grp, "aov_buf", &inst_.film.aovs_info);
+ world_ps_.bind_image("aov_color_img", &rbufs.aov_color_tx);
+ world_ps_.bind_image("aov_value_img", &rbufs.aov_value_tx);
+ world_ps_.bind_ssbo("aov_buf", &inst_.film.aovs_info);
/* RenderPasses. Cleared by background (even if bad practice). */
- DRW_shgroup_uniform_image_ref(grp, "rp_normal_img", &rbufs.normal_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_light_img", &rbufs.diffuse_light_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_color_img", &rbufs.diffuse_color_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_specular_light_img", &rbufs.specular_light_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_specular_color_img", &rbufs.specular_color_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_emission_img", &rbufs.emission_tx);
+ world_ps_.bind_image("rp_normal_img", &rbufs.normal_tx);
+ world_ps_.bind_image("rp_light_img", &rbufs.light_tx);
+ world_ps_.bind_image("rp_diffuse_color_img", &rbufs.diffuse_color_tx);
+ world_ps_.bind_image("rp_specular_color_img", &rbufs.specular_color_tx);
+ world_ps_.bind_image("rp_emission_img", &rbufs.emission_tx);
+ world_ps_.bind_image("rp_cryptomatte_img", &rbufs.cryptomatte_tx);
+
+ world_ps_.draw(DRW_cache_fullscreen_quad_get(), handle);
/* To allow opaque pass rendering over it. */
- DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS);
+ world_ps_.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
}
-void WorldPipeline::render()
+void WorldPipeline::render(View &view)
{
- DRW_draw_pass(world_ps_);
+ inst_.manager->submit(world_ps_, view);
}
/** \} */
@@ -66,195 +66,167 @@ void WorldPipeline::render()
void ForwardPipeline::sync()
{
+ camera_forward_ = inst_.camera.forward();
+
+ DRWState state_depth_only = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
+ DRWState state_depth_color = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS |
+ DRW_STATE_WRITE_COLOR;
{
- DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS;
- prepass_ps_ = DRW_pass_create("Forward.Opaque.Prepass", state);
- prepass_velocity_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Velocity",
- state | DRW_STATE_WRITE_COLOR);
+ prepass_ps_.init();
- state |= DRW_STATE_CULL_BACK;
- prepass_culled_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Culled", state);
- prepass_culled_velocity_ps_ = DRW_pass_create("Forward.Opaque.Prepass.Velocity",
- state | DRW_STATE_WRITE_COLOR);
+ {
+ /* Common resources. */
- DRW_pass_link(prepass_ps_, prepass_velocity_ps_);
- DRW_pass_link(prepass_velocity_ps_, prepass_culled_ps_);
- DRW_pass_link(prepass_culled_ps_, prepass_culled_velocity_ps_);
- }
- {
- DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL;
- opaque_ps_ = DRW_pass_create("Forward.Opaque", state);
+ /* Textures. */
+ prepass_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
- state |= DRW_STATE_CULL_BACK;
- opaque_culled_ps_ = DRW_pass_create("Forward.Opaque.Culled", state);
+ inst_.velocity.bind_resources(&prepass_ps_);
+ inst_.sampling.bind_resources(&prepass_ps_);
+ }
+
+ prepass_double_sided_static_ps_ = &prepass_ps_.sub("DoubleSided.Static");
+ prepass_double_sided_static_ps_->state_set(state_depth_only);
+
+ prepass_single_sided_static_ps_ = &prepass_ps_.sub("SingleSided.Static");
+ prepass_single_sided_static_ps_->state_set(state_depth_only | DRW_STATE_CULL_BACK);
+
+ prepass_double_sided_moving_ps_ = &prepass_ps_.sub("DoubleSided.Moving");
+ prepass_double_sided_moving_ps_->state_set(state_depth_color);
- DRW_pass_link(opaque_ps_, opaque_culled_ps_);
+ prepass_single_sided_moving_ps_ = &prepass_ps_.sub("SingleSided.Moving");
+ prepass_single_sided_moving_ps_->state_set(state_depth_color | DRW_STATE_CULL_BACK);
}
{
- DRWState state = DRW_STATE_DEPTH_LESS_EQUAL;
- transparent_ps_ = DRW_pass_create("Forward.Transparent", state);
+ opaque_ps_.init();
+
+ {
+ /* Common resources. */
+
+ /* RenderPasses. */
+ opaque_ps_.bind_image(RBUFS_NORMAL_SLOT, &inst_.render_buffers.normal_tx);
+ opaque_ps_.bind_image(RBUFS_LIGHT_SLOT, &inst_.render_buffers.light_tx);
+ opaque_ps_.bind_image(RBUFS_DIFF_COLOR_SLOT, &inst_.render_buffers.diffuse_color_tx);
+ opaque_ps_.bind_image(RBUFS_SPEC_COLOR_SLOT, &inst_.render_buffers.specular_color_tx);
+ opaque_ps_.bind_image(RBUFS_EMISSION_SLOT, &inst_.render_buffers.emission_tx);
+ /* AOVs. */
+ opaque_ps_.bind_image(RBUFS_AOV_COLOR_SLOT, &inst_.render_buffers.aov_color_tx);
+ opaque_ps_.bind_image(RBUFS_AOV_VALUE_SLOT, &inst_.render_buffers.aov_value_tx);
+ /* Cryptomatte. */
+ opaque_ps_.bind_image(RBUFS_CRYPTOMATTE_SLOT, &inst_.render_buffers.cryptomatte_tx);
+ /* Storage Buf. */
+ opaque_ps_.bind_ssbo(RBUFS_AOV_BUF_SLOT, &inst_.film.aovs_info);
+ /* Textures. */
+ opaque_ps_.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
+
+ inst_.lights.bind_resources(&opaque_ps_);
+ inst_.sampling.bind_resources(&opaque_ps_);
+ inst_.cryptomatte.bind_resources(&opaque_ps_);
+ }
+
+ opaque_single_sided_ps_ = &opaque_ps_.sub("SingleSided");
+ opaque_single_sided_ps_->state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL |
+ DRW_STATE_CULL_BACK);
+
+ opaque_double_sided_ps_ = &opaque_ps_.sub("DoubleSided");
+ opaque_double_sided_ps_->state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL);
}
-}
+ {
+ transparent_ps_.init();
+ /* Workaround limitation of PassSortable. Use dummy pass that will be sorted first in all
+ * circumstances. */
+ PassMain::Sub &sub = transparent_ps_.sub("ResourceBind", -FLT_MAX);
-DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
-{
- RenderBuffers &rbufs = inst_.render_buffers;
- DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_;
- // LightModule &lights = inst_.lights;
- // LightProbeModule &lightprobes = inst_.lightprobes;
- // RaytracingModule &raytracing = inst_.raytracing;
- // eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
- DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
- // lights.shgroup_resources(grp);
- // DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
- // DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
- // DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
- // DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
- // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
- // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
- DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
- /* AOVs. */
- DRW_shgroup_uniform_image_ref(grp, "aov_color_img", &rbufs.aov_color_tx);
- DRW_shgroup_uniform_image_ref(grp, "aov_value_img", &rbufs.aov_value_tx);
- DRW_shgroup_storage_block_ref(grp, "aov_buf", &inst_.film.aovs_info);
- /* RenderPasses. */
- DRW_shgroup_uniform_image_ref(grp, "rp_normal_img", &rbufs.normal_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_light_img", &rbufs.diffuse_light_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_color_img", &rbufs.diffuse_color_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_specular_light_img", &rbufs.specular_light_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_specular_color_img", &rbufs.specular_color_tx);
- DRW_shgroup_uniform_image_ref(grp, "rp_emission_img", &rbufs.emission_tx);
-
- /* TODO(fclem): Make this only needed if material uses it ... somehow. */
- // if (true) {
- // DRW_shgroup_uniform_texture_ref(
- // grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
- // }
- // if (raytracing.enabled()) {
- // DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_data);
- // DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_data);
- // DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_data);
- // DRW_shgroup_uniform_texture_ref_ex(grp, "radiance_tx", &input_screen_radiance_tx_,
- // no_interp);
- // }
- // if (raytracing.enabled()) {
- // DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
- // DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", inst_.hiz_front.texture_ref_get());
- // }
- return grp;
-}
+ /* Common resources. */
-DRWShadingGroup *ForwardPipeline::prepass_opaque_add(::Material *blender_mat,
- GPUMaterial *gpumat,
- bool has_motion)
-{
- DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
- (has_motion ? prepass_culled_velocity_ps_ : prepass_culled_ps_) :
- (has_motion ? prepass_velocity_ps_ : prepass_ps_);
- DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass);
- if (has_motion) {
- inst_.velocity.bind_resources(grp);
+ /* Textures. */
+ sub.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
+
+ inst_.lights.bind_resources(&sub);
+ inst_.sampling.bind_resources(&sub);
}
- return grp;
}
-DRWShadingGroup *ForwardPipeline::material_transparent_add(::Material *blender_mat,
- GPUMaterial *gpumat)
+PassMain::Sub *ForwardPipeline::prepass_opaque_add(::Material *blender_mat,
+ GPUMaterial *gpumat,
+ bool has_motion)
{
- // LightModule &lights = inst_.lights;
- // LightProbeModule &lightprobes = inst_.lightprobes;
- // RaytracingModule &raytracing = inst_.raytracing;
- // eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT;
- DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
- // lights.shgroup_resources(grp);
- // DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get());
- // DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get());
- // DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get());
- // DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get());
- // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get());
- // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get());
- // DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx);
- /* TODO(fclem): Make this only needed if material uses it ... somehow. */
- // if (true) {
- // DRW_shgroup_uniform_texture_ref(
- // grp, "sss_transmittance_tx", inst_.subsurface.transmittance_ref_get());
- // }
- // if (raytracing.enabled()) {
- // DRW_shgroup_uniform_block(grp, "rt_diffuse_buf", raytracing.diffuse_data);
- // DRW_shgroup_uniform_block(grp, "rt_reflection_buf", raytracing.reflection_data);
- // DRW_shgroup_uniform_block(grp, "rt_refraction_buf", raytracing.refraction_data);
- // DRW_shgroup_uniform_texture_ref_ex(
- // grp, "rt_radiance_tx", &input_screen_radiance_tx_, no_interp);
- // }
- // if (raytracing.enabled()) {
- // DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get());
- // DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", inst_.hiz_front.texture_ref_get());
- // }
+ PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ?
+ (has_motion ? prepass_single_sided_moving_ps_ :
+ prepass_single_sided_static_ps_) :
+ (has_motion ? prepass_double_sided_moving_ps_ :
+ prepass_double_sided_static_ps_);
+ return &pass->sub(GPU_material_get_name(gpumat));
+}
- DRWState state_disable = DRW_STATE_WRITE_DEPTH;
- DRWState state_enable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
- if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
- state_enable |= DRW_STATE_CULL_BACK;
- }
- DRW_shgroup_state_disable(grp, state_disable);
- DRW_shgroup_state_enable(grp, state_enable);
- return grp;
+PassMain::Sub *ForwardPipeline::material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat)
+{
+ PassMain::Sub *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_single_sided_ps_ :
+ opaque_double_sided_ps_;
+ return &pass->sub(GPU_material_get_name(gpumat));
}
-DRWShadingGroup *ForwardPipeline::prepass_transparent_add(::Material *blender_mat,
- GPUMaterial *gpumat)
+PassMain::Sub *ForwardPipeline::prepass_transparent_add(const Object *ob,
+ ::Material *blender_mat,
+ GPUMaterial *gpumat)
{
if ((blender_mat->blend_flag & MA_BL_HIDE_BACKFACE) == 0) {
return nullptr;
}
+ DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
+ if ((blender_mat->blend_flag & MA_BL_CULL_BACKFACE)) {
+ state |= DRW_STATE_CULL_BACK;
+ }
+ float sorting_value = math::dot(float3(ob->obmat[3]), camera_forward_);
+ PassMain::Sub *pass = &transparent_ps_.sub(GPU_material_get_name(gpumat), sorting_value);
+ pass->state_set(state);
+ pass->material_set(*inst_.manager, gpumat);
+ return pass;
+}
- DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_);
-
- DRWState state_disable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM;
- DRWState state_enable = DRW_STATE_WRITE_DEPTH;
- if (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) {
- state_enable |= DRW_STATE_CULL_BACK;
+PassMain::Sub *ForwardPipeline::material_transparent_add(const Object *ob,
+ ::Material *blender_mat,
+ GPUMaterial *gpumat)
+{
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM | DRW_STATE_DEPTH_LESS_EQUAL;
+ if ((blender_mat->blend_flag & MA_BL_CULL_BACKFACE)) {
+ state |= DRW_STATE_CULL_BACK;
}
- DRW_shgroup_state_disable(grp, state_disable);
- DRW_shgroup_state_enable(grp, state_enable);
- return grp;
+ float sorting_value = math::dot(float3(ob->obmat[3]), camera_forward_);
+ PassMain::Sub *pass = &transparent_ps_.sub(GPU_material_get_name(gpumat), sorting_value);
+ pass->state_set(state);
+ pass->material_set(*inst_.manager, gpumat);
+ return pass;
}
-void ForwardPipeline::render(const DRWView *view,
+void ForwardPipeline::render(View &view,
Framebuffer &prepass_fb,
Framebuffer &combined_fb,
- GPUTexture *depth_tx,
GPUTexture *UNUSED(combined_tx))
{
- UNUSED_VARS(view, depth_tx, prepass_fb, combined_fb);
- // HiZBuffer &hiz = inst_.hiz_front;
+ UNUSED_VARS(view);
- DRW_stats_group_start("ForwardOpaque");
+ DRW_stats_group_start("Forward.Opaque");
GPU_framebuffer_bind(prepass_fb);
- DRW_draw_pass(prepass_ps_);
+ inst_.manager->submit(prepass_ps_, view);
- // hiz.set_dirty();
+ // if (!DRW_pass_is_empty(prepass_ps_)) {
+ inst_.hiz_buffer.set_dirty();
+ // }
// if (inst_.raytracing.enabled()) {
// rt_buffer.radiance_copy(combined_tx);
- // hiz.update(depth_tx);
+ // inst_.hiz_buffer.update();
// }
// inst_.shadows.set_view(view, depth_tx);
GPU_framebuffer_bind(combined_fb);
- DRW_draw_pass(opaque_ps_);
+ inst_.manager->submit(opaque_ps_, view);
DRW_stats_group_end();
- DRW_stats_group_start("ForwardTransparent");
- /* TODO(fclem) This is suboptimal. We could sort during sync. */
- /* FIXME(fclem) This wont work for panoramic, where we need
- * to sort by distance to camera, not by z. */
- DRW_pass_sort_shgroup_z(transparent_ps_);
- DRW_draw_pass(transparent_ps_);
- DRW_stats_group_end();
+ inst_.manager->submit(transparent_ps_, view);
// if (inst_.raytracing.enabled()) {
// gbuffer.ray_radiance_tx.release();
diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh
index 3bdc718767b..0614a963dec 100644
--- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh
@@ -13,6 +13,7 @@
#pragma once
#include "DRW_render.h"
+#include "draw_shader_shared.h"
/* TODO(fclem): Move it to GPU/DRAW. */
#include "../eevee/eevee_lut.h"
@@ -31,13 +32,13 @@ class WorldPipeline {
private:
Instance &inst_;
- DRWPass *world_ps_ = nullptr;
+ PassSimple world_ps_ = {"World.Background"};
public:
WorldPipeline(Instance &inst) : inst_(inst){};
void sync(GPUMaterial *gpumat);
- void render();
+ void render(View &view);
};
/** \} */
@@ -52,13 +53,18 @@ class ForwardPipeline {
private:
Instance &inst_;
- DRWPass *prepass_ps_ = nullptr;
- DRWPass *prepass_velocity_ps_ = nullptr;
- DRWPass *prepass_culled_ps_ = nullptr;
- DRWPass *prepass_culled_velocity_ps_ = nullptr;
- DRWPass *opaque_ps_ = nullptr;
- DRWPass *opaque_culled_ps_ = nullptr;
- DRWPass *transparent_ps_ = nullptr;
+ PassMain prepass_ps_ = {"Prepass"};
+ PassMain::Sub *prepass_single_sided_static_ps_ = nullptr;
+ PassMain::Sub *prepass_single_sided_moving_ps_ = nullptr;
+ PassMain::Sub *prepass_double_sided_static_ps_ = nullptr;
+ PassMain::Sub *prepass_double_sided_moving_ps_ = nullptr;
+
+ PassMain opaque_ps_ = {"Shading"};
+ PassMain::Sub *opaque_single_sided_ps_ = nullptr;
+ PassMain::Sub *opaque_double_sided_ps_ = nullptr;
+
+ PassSortable transparent_ps_ = {"Forward.Transparent"};
+ float3 camera_forward_;
// GPUTexture *input_screen_radiance_tx_ = nullptr;
@@ -67,31 +73,19 @@ class ForwardPipeline {
void sync();
- DRWShadingGroup *material_add(::Material *blender_mat, GPUMaterial *gpumat)
- {
- return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ?
- material_transparent_add(blender_mat, gpumat) :
- material_opaque_add(blender_mat, gpumat);
- }
+ PassMain::Sub *prepass_opaque_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion);
+ PassMain::Sub *material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat);
- DRWShadingGroup *prepass_add(::Material *blender_mat, GPUMaterial *gpumat, bool has_motion)
- {
- return (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) ?
- prepass_transparent_add(blender_mat, gpumat) :
- prepass_opaque_add(blender_mat, gpumat, has_motion);
- }
-
- DRWShadingGroup *material_opaque_add(::Material *blender_mat, GPUMaterial *gpumat);
- DRWShadingGroup *prepass_opaque_add(::Material *blender_mat,
- GPUMaterial *gpumat,
- bool has_motion);
- DRWShadingGroup *material_transparent_add(::Material *blender_mat, GPUMaterial *gpumat);
- DRWShadingGroup *prepass_transparent_add(::Material *blender_mat, GPUMaterial *gpumat);
+ PassMain::Sub *prepass_transparent_add(const Object *ob,
+ ::Material *blender_mat,
+ GPUMaterial *gpumat);
+ PassMain::Sub *material_transparent_add(const Object *ob,
+ ::Material *blender_mat,
+ GPUMaterial *gpumat);
- void render(const DRWView *view,
+ void render(View &view,
Framebuffer &prepass_fb,
Framebuffer &combined_fb,
- GPUTexture *depth_tx,
GPUTexture *combined_tx);
};
@@ -193,26 +187,36 @@ class PipelineModule {
// velocity.sync();
}
- DRWShadingGroup *material_add(::Material *blender_mat,
- GPUMaterial *gpumat,
- eMaterialPipeline pipeline_type)
+ PassMain::Sub *material_add(Object *ob,
+ ::Material *blender_mat,
+ GPUMaterial *gpumat,
+ eMaterialPipeline pipeline_type)
{
switch (pipeline_type) {
case MAT_PIPE_DEFERRED_PREPASS:
// return deferred.prepass_add(blender_mat, gpumat, false);
- break;
+ case MAT_PIPE_FORWARD_PREPASS:
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
+ return forward.prepass_transparent_add(ob, blender_mat, gpumat);
+ }
+ return forward.prepass_opaque_add(blender_mat, gpumat, false);
+
case MAT_PIPE_DEFERRED_PREPASS_VELOCITY:
// return deferred.prepass_add(blender_mat, gpumat, true);
- break;
- case MAT_PIPE_FORWARD_PREPASS:
- return forward.prepass_add(blender_mat, gpumat, false);
case MAT_PIPE_FORWARD_PREPASS_VELOCITY:
- return forward.prepass_add(blender_mat, gpumat, true);
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
+ return forward.prepass_transparent_add(ob, blender_mat, gpumat);
+ }
+ return forward.prepass_opaque_add(blender_mat, gpumat, true);
+
case MAT_PIPE_DEFERRED:
// return deferred.material_add(blender_mat, gpumat);
- break;
case MAT_PIPE_FORWARD:
- return forward.material_add(blender_mat, gpumat);
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
+ return forward.material_transparent_add(ob, blender_mat, gpumat);
+ }
+ return forward.material_opaque_add(blender_mat, gpumat);
+
case MAT_PIPE_VOLUME:
/* TODO(fclem) volume pass. */
return nullptr;
diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
index 6e30ba989df..f5638cc5160 100644
--- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc
@@ -5,7 +5,7 @@
/** \file
* \ingroup eevee
*
- * A film is a fullscreen buffer (usually at output extent)
+ * A film is a full-screen buffer (usually at output extent)
* that will be able to accumulate sample in any distorted camera_type
* using a pixel filter.
*
@@ -24,55 +24,68 @@
namespace blender::eevee {
-void RenderBuffers::sync()
+void RenderBuffers::acquire(int2 extent)
{
- depth_tx.sync();
- combined_tx.sync();
-
- normal_tx.sync();
- vector_tx.sync();
- diffuse_light_tx.sync();
- diffuse_color_tx.sync();
- specular_light_tx.sync();
- specular_color_tx.sync();
- volume_light_tx.sync();
- emission_tx.sync();
- environment_tx.sync();
- shadow_tx.sync();
- ambient_occlusion_tx.sync();
-}
+ const eViewLayerEEVEEPassType enabled_passes = inst_.film.enabled_passes_get();
-void RenderBuffers::acquire(int2 extent, void *owner)
-{
auto pass_extent = [&](eViewLayerEEVEEPassType pass_bit) -> int2 {
/* Use dummy texture for disabled passes. Allows correct bindings. */
- return (inst_.film.enabled_passes_get() & pass_bit) ? extent : int2(1);
+ return (enabled_passes & pass_bit) ? extent : int2(1);
};
eGPUTextureFormat color_format = GPU_RGBA16F;
eGPUTextureFormat float_format = GPU_R16F;
/* Depth and combined are always needed. */
- depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8, owner);
- combined_tx.acquire(extent, color_format, owner);
-
- normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format, owner);
- vector_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VECTOR), color_format, owner);
- diffuse_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_LIGHT), color_format, owner);
- diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format, owner);
- specular_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_LIGHT), color_format, owner);
- specular_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_COLOR), color_format, owner);
- volume_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VOLUME_LIGHT), color_format, owner);
- emission_tx.acquire(pass_extent(EEVEE_RENDER_PASS_EMIT), color_format, owner);
- environment_tx.acquire(pass_extent(EEVEE_RENDER_PASS_ENVIRONMENT), color_format, owner);
- shadow_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SHADOW), float_format, owner);
- ambient_occlusion_tx.acquire(pass_extent(EEVEE_RENDER_PASS_AO), float_format, owner);
+ depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8);
+ combined_tx.acquire(extent, color_format);
+
+ bool do_vector_render_pass = (enabled_passes & EEVEE_RENDER_PASS_VECTOR) ||
+ (inst_.motion_blur.postfx_enabled() && !inst_.is_viewport());
+ uint32_t max_light_color_layer = max_ii(enabled_passes & EEVEE_RENDER_PASS_DIFFUSE_LIGHT ?
+ (int)RENDER_PASS_LAYER_DIFFUSE_LIGHT :
+ -1,
+ enabled_passes & EEVEE_RENDER_PASS_SPECULAR_LIGHT ?
+ (int)RENDER_PASS_LAYER_SPECULAR_LIGHT :
+ -1) +
+ 1;
+ /* Only RG16F when only doing only reprojection or motion blur. */
+ eGPUTextureFormat vector_format = do_vector_render_pass ? GPU_RGBA16F : GPU_RG16F;
+ /* TODO(fclem): Make vector pass allocation optional if no TAA or motion blur is needed. */
+ vector_tx.acquire(extent, vector_format);
+
+ normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format);
+ diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format);
+ specular_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_COLOR), color_format);
+ volume_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VOLUME_LIGHT), color_format);
+ emission_tx.acquire(pass_extent(EEVEE_RENDER_PASS_EMIT), color_format);
+ environment_tx.acquire(pass_extent(EEVEE_RENDER_PASS_ENVIRONMENT), color_format);
+ shadow_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SHADOW), float_format);
+ ambient_occlusion_tx.acquire(pass_extent(EEVEE_RENDER_PASS_AO), float_format);
+
+ light_tx.ensure_2d_array(color_format,
+ max_light_color_layer > 0 ? extent : int2(1),
+ max_ii(1, max_light_color_layer));
const AOVsInfoData &aovs = inst_.film.aovs_info;
aov_color_tx.ensure_2d_array(
color_format, (aovs.color_len > 0) ? extent : int2(1), max_ii(1, aovs.color_len));
aov_value_tx.ensure_2d_array(
float_format, (aovs.value_len > 0) ? extent : int2(1), max_ii(1, aovs.value_len));
+
+ eGPUTextureFormat cryptomatte_format = GPU_R32F;
+ const int cryptomatte_layer_len = inst_.film.cryptomatte_layer_max_get();
+ if (cryptomatte_layer_len == 2) {
+ cryptomatte_format = GPU_RG32F;
+ }
+ else if (cryptomatte_layer_len == 3) {
+ cryptomatte_format = GPU_RGBA32F;
+ }
+ cryptomatte_tx.acquire(
+ pass_extent(static_cast<eViewLayerEEVEEPassType>(EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT |
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET |
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL)),
+ cryptomatte_format);
}
void RenderBuffers::release()
@@ -82,15 +95,14 @@ void RenderBuffers::release()
normal_tx.release();
vector_tx.release();
- diffuse_light_tx.release();
diffuse_color_tx.release();
- specular_light_tx.release();
specular_color_tx.release();
volume_light_tx.release();
emission_tx.release();
environment_tx.release();
shadow_tx.release();
ambient_occlusion_tx.release();
+ cryptomatte_tx.release();
}
} // namespace blender::eevee
diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh
index 8c91fed2f0f..ae5d7fbae5c 100644
--- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh
@@ -28,17 +28,16 @@ class RenderBuffers {
// TextureFromPool mist_tx; /* Derived from depth_tx during accumulation. */
TextureFromPool normal_tx;
TextureFromPool vector_tx;
- TextureFromPool diffuse_light_tx;
TextureFromPool diffuse_color_tx;
- TextureFromPool specular_light_tx;
TextureFromPool specular_color_tx;
TextureFromPool volume_light_tx;
TextureFromPool emission_tx;
TextureFromPool environment_tx;
TextureFromPool shadow_tx;
TextureFromPool ambient_occlusion_tx;
- // TextureFromPool cryptomatte_tx; /* TODO */
+ TextureFromPool cryptomatte_tx;
/* TODO(fclem): Use texture from pool once they support texture array. */
+ Texture light_tx;
Texture aov_color_tx;
Texture aov_value_tx;
@@ -48,9 +47,8 @@ class RenderBuffers {
public:
RenderBuffers(Instance &inst) : inst_(inst){};
- void sync();
/* Acquires (also ensures) the render buffer before rendering to them. */
- void acquire(int2 extent, void *owner);
+ void acquire(int2 extent);
void release();
};
diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc
index 2f180e58a0b..76a0e98638b 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc
@@ -57,22 +57,28 @@ void Sampling::end_sync()
{
if (reset_) {
viewport_sample_ = 0;
- if (inst_.is_viewport()) {
- interactive_mode_ = true;
- }
}
- if (interactive_mode_) {
- int interactive_sample_count = min_ii(interactive_sample_max_, sample_count_);
+ if (inst_.is_viewport()) {
- if (viewport_sample_ < interactive_sample_count) {
- /* Loop over the same starting samples. */
- sample_ = sample_ % interactive_sample_count;
- }
- else {
- /* Break out of the loop and resume normal pattern. */
- sample_ = interactive_sample_count;
+ interactive_mode_ = viewport_sample_ < interactive_mode_threshold;
+
+ bool interactive_mode_disabled = (inst_.scene->eevee.flag & SCE_EEVEE_TAA_REPROJECTION) == 0;
+ if (interactive_mode_disabled) {
interactive_mode_ = false;
+ sample_ = viewport_sample_;
+ }
+ else if (interactive_mode_) {
+ int interactive_sample_count = min_ii(interactive_sample_max_, sample_count_);
+
+ if (viewport_sample_ < interactive_sample_count) {
+ /* Loop over the same starting samples. */
+ sample_ = sample_ % interactive_sample_count;
+ }
+ else {
+ /* Break out of the loop and resume normal pattern. */
+ sample_ = interactive_sample_count;
+ }
}
}
}
@@ -138,8 +144,6 @@ void Sampling::step()
viewport_sample_++;
sample_++;
- std::cout << sample_ << " " << viewport_sample_ << std::endl;
-
reset_ = false;
}
@@ -173,7 +177,7 @@ float2 Sampling::sample_disk(const float2 &rand)
float2 Sampling::sample_spiral(const float2 &rand)
{
/* Fibonacci spiral. */
- float omega = M_PI * (1.0f + sqrtf(5.0f)) * rand.x;
+ float omega = 4.0f * M_PI * (1.0f + sqrtf(5.0f)) * rand.x;
float r = sqrtf(rand.x);
/* Random rotation. */
omega += rand.y * 2.0f * M_PI;
@@ -218,7 +222,7 @@ void Sampling::dof_disk_sample_get(float *r_radius, float *r_theta) const
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Sampling patterns
+/** \name Cumulative Distribution Function (CDF)
* \{ */
/* Creates a discrete cumulative distribution function table from a given curvemapping.
@@ -228,7 +232,7 @@ void Sampling::cdf_from_curvemapping(const CurveMapping &curve, Vector<float> &c
BLI_assert(cdf.size() > 1);
cdf[0] = 0.0f;
/* Actual CDF evaluation. */
- for (int u : cdf.index_range()) {
+ for (int u : IndexRange(cdf.size() - 1)) {
float x = (float)(u + 1) / (float)(cdf.size() - 1);
cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x);
}
diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.hh b/source/blender/draw/engines/eevee_next/eevee_sampling.hh
index 11daa21629a..c2bf23d20fc 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sampling.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_sampling.hh
@@ -27,13 +27,13 @@ class Sampling {
Instance &inst_;
/* Number of samples in the first ring of jittered depth of field. */
- constexpr static uint64_t dof_web_density_ = 6;
+ static constexpr uint64_t dof_web_density_ = 6;
/* High number of sample for viewport infinite rendering. */
- constexpr static uint64_t infinite_sample_count_ = 0xFFFFFFu;
+ static constexpr uint64_t infinite_sample_count_ = 0xFFFFFFu;
/* During interactive rendering, loop over the first few samples. */
- constexpr static uint64_t interactive_sample_max_ = 8;
+ static constexpr uint64_t interactive_sample_max_ = 8;
- /** 0 based current sample. */
+ /** 0 based current sample. Might not increase sequentially in viewport. */
uint64_t sample_ = 0;
/** Target sample count. */
uint64_t sample_count_ = 64;
@@ -43,7 +43,7 @@ class Sampling {
uint64_t dof_sample_count_ = 1;
/** Motion blur steps. */
uint64_t motion_blur_steps_ = 1;
- /** Increases if the view and the scene is static. */
+ /** Increases if the view and the scene is static. Does increase sequentially. */
int64_t viewport_sample_ = 0;
/** Tag to reset sampling for the next sample. */
bool reset_ = false;
@@ -52,6 +52,12 @@ class Sampling {
* In interactive mode, image stability is prioritized over quality.
*/
bool interactive_mode_ = false;
+ /**
+ * Sample count after which we use the static accumulation.
+ * Interactive sampling from sample 0 to (interactive_mode_threshold - 1).
+ * Accumulation sampling from sample interactive_mode_threshold to sample_count_.
+ */
+ static constexpr int interactive_mode_threshold = 3;
SamplingDataBuf data_;
@@ -81,6 +87,12 @@ class Sampling {
DRW_shgroup_storage_block_ref(grp, "sampling_buf", &data_);
}
+ template<typename T> void bind_resources(draw::detail::PassBase<T> *pass)
+ {
+ /* Storage Buf. */
+ pass->bind_ssbo(SAMPLING_BUF_SLOT, &data_);
+ }
+
/* Returns a pseudo random number in [0..1] range. Each dimension are de-correlated. */
float rng_get(eSamplingDimension dimension) const
{
@@ -102,13 +114,24 @@ class Sampling {
/* Returns true if rendering has finished. */
bool finished() const
{
- return (sample_ >= sample_count_ - 1);
+ return (sample_ >= sample_count_);
}
/* Returns true if viewport smoothing and sampling has finished. */
bool finished_viewport() const
{
- return finished() && (viewport_sample_ >= interactive_sample_max_);
+ return (viewport_sample_ >= sample_count_) && !interactive_mode_;
+ }
+
+ /* Returns true if viewport renderer is in interactive mode and should use TAA. */
+ bool interactive_mode() const
+ {
+ return interactive_mode_;
+ }
+
+ uint64_t sample_count() const
+ {
+ return sample_count_;
}
/* Return true if we are starting a new motion blur step. We need to run sync again since
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc
index f5d4af2914e..64b1d4891a9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc
@@ -9,6 +9,8 @@
* and static shader usage.
*/
+#include "GPU_capabilities.h"
+
#include "gpu_shader_create_info.hh"
#include "eevee_shader.hh"
@@ -82,8 +84,64 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_film_frag";
case FILM_COMP:
return "eevee_film_comp";
- case VELOCITY_RESOLVE:
- return "eevee_velocity_resolve";
+ case FILM_CRYPTOMATTE_POST:
+ return "eevee_film_cryptomatte_post";
+ case HIZ_DEBUG:
+ return "eevee_hiz_debug";
+ case HIZ_UPDATE:
+ return "eevee_hiz_update";
+ case MOTION_BLUR_GATHER:
+ return "eevee_motion_blur_gather";
+ case MOTION_BLUR_TILE_DILATE:
+ return "eevee_motion_blur_tiles_dilate";
+ case MOTION_BLUR_TILE_FLATTEN_RENDER:
+ return "eevee_motion_blur_tiles_flatten_render";
+ case MOTION_BLUR_TILE_FLATTEN_VIEWPORT:
+ return "eevee_motion_blur_tiles_flatten_viewport";
+ case DOF_BOKEH_LUT:
+ return "eevee_depth_of_field_bokeh_lut";
+ case DOF_DOWNSAMPLE:
+ return "eevee_depth_of_field_downsample";
+ case DOF_FILTER:
+ return "eevee_depth_of_field_filter";
+ case DOF_GATHER_FOREGROUND_LUT:
+ return "eevee_depth_of_field_gather_foreground_lut";
+ case DOF_GATHER_FOREGROUND:
+ return "eevee_depth_of_field_gather_foreground_no_lut";
+ case DOF_GATHER_BACKGROUND_LUT:
+ return "eevee_depth_of_field_gather_background_lut";
+ case DOF_GATHER_BACKGROUND:
+ return "eevee_depth_of_field_gather_background_no_lut";
+ case DOF_GATHER_HOLE_FILL:
+ return "eevee_depth_of_field_hole_fill";
+ case DOF_REDUCE:
+ return "eevee_depth_of_field_reduce";
+ case DOF_RESOLVE:
+ return "eevee_depth_of_field_resolve_no_lut";
+ case DOF_RESOLVE_LUT:
+ return "eevee_depth_of_field_resolve_lut";
+ case DOF_SETUP:
+ return "eevee_depth_of_field_setup";
+ case DOF_SCATTER:
+ return "eevee_depth_of_field_scatter";
+ case DOF_STABILIZE:
+ return "eevee_depth_of_field_stabilize";
+ case DOF_TILES_DILATE_MINABS:
+ return "eevee_depth_of_field_tiles_dilate_minabs";
+ case DOF_TILES_DILATE_MINMAX:
+ return "eevee_depth_of_field_tiles_dilate_minmax";
+ case DOF_TILES_FLATTEN:
+ return "eevee_depth_of_field_tiles_flatten";
+ case LIGHT_CULLING_DEBUG:
+ return "eevee_light_culling_debug";
+ case LIGHT_CULLING_SELECT:
+ return "eevee_light_culling_select";
+ case LIGHT_CULLING_SORT:
+ return "eevee_light_culling_sort";
+ case LIGHT_CULLING_TILE:
+ return "eevee_light_culling_tile";
+ case LIGHT_CULLING_ZBIN:
+ return "eevee_light_culling_zbin";
/* To avoid compiler warning about missing case. */
case MAX_SHADER_TYPE:
return "";
@@ -126,11 +184,41 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
GPUCodegenOutput &codegen = *codegen_;
ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info);
- info.auto_resource_location(true);
+ /* WORKAROUND: Replace by new ob info. */
+ int64_t ob_info_index = info.additional_infos_.first_index_of_try("draw_object_infos");
+ if (ob_info_index != -1) {
+ info.additional_infos_[ob_info_index] = "draw_object_infos_new";
+ }
+
+ /* WORKAROUND: Add new ob attr buffer. */
+ if (GPU_material_uniform_attributes(gpumat) != nullptr) {
+ info.additional_info("draw_object_attribute_new");
+ }
+
+ /* WORKAROUND: Avoid utility texture merge error. TODO: find a cleaner fix. */
+ for (auto &resource : info.batch_resources_) {
+ if (resource.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
+ if (resource.slot == RBUFS_UTILITY_TEX_SLOT) {
+ resource.slot = GPU_max_textures_frag() - 1;
+ }
+ }
+ }
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT)) {
info.define("MAT_TRANSPARENT");
+ /* Transparent material do not have any velocity specific pipeline. */
+ if (pipeline_type == MAT_PIPE_FORWARD_PREPASS_VELOCITY) {
+ pipeline_type = MAT_PIPE_FORWARD_PREPASS;
+ }
}
+
+ if (GPU_material_flag_get(gpumat, GPU_MATFLAG_TRANSPARENT) == false &&
+ pipeline_type == MAT_PIPE_FORWARD) {
+ /* Opaque forward do support AOVs and render pass. */
+ info.additional_info("eevee_aov_out");
+ info.additional_info("eevee_render_pass_out");
+ }
+
if (GPU_material_flag_get(gpumat, GPU_MATFLAG_BARYCENTRIC)) {
switch (geometry_type) {
case MAT_GEOM_MESH:
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh
index 7a0867820af..88538557c07 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh
@@ -28,8 +28,39 @@ namespace blender::eevee {
enum eShaderType {
FILM_FRAG = 0,
FILM_COMP,
-
- VELOCITY_RESOLVE,
+ FILM_CRYPTOMATTE_POST,
+
+ DOF_BOKEH_LUT,
+ DOF_DOWNSAMPLE,
+ DOF_FILTER,
+ DOF_GATHER_BACKGROUND_LUT,
+ DOF_GATHER_BACKGROUND,
+ DOF_GATHER_FOREGROUND_LUT,
+ DOF_GATHER_FOREGROUND,
+ DOF_GATHER_HOLE_FILL,
+ DOF_REDUCE,
+ DOF_RESOLVE_LUT,
+ DOF_RESOLVE,
+ DOF_SCATTER,
+ DOF_SETUP,
+ DOF_STABILIZE,
+ DOF_TILES_DILATE_MINABS,
+ DOF_TILES_DILATE_MINMAX,
+ DOF_TILES_FLATTEN,
+
+ HIZ_UPDATE,
+ HIZ_DEBUG,
+
+ LIGHT_CULLING_DEBUG,
+ LIGHT_CULLING_SELECT,
+ LIGHT_CULLING_SORT,
+ LIGHT_CULLING_TILE,
+ LIGHT_CULLING_ZBIN,
+
+ MOTION_BLUR_GATHER,
+ MOTION_BLUR_TILE_DILATE,
+ MOTION_BLUR_TILE_FLATTEN_RENDER,
+ MOTION_BLUR_TILE_FLATTEN_VIEWPORT,
MAX_SHADER_TYPE,
};
diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
index 62eb5a2b965..8e96445d6b9 100644
--- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh
@@ -12,22 +12,75 @@
# include "BLI_memory_utils.hh"
# include "DRW_gpu_wrapper.hh"
+# include "draw_manager.hh"
+# include "draw_pass.hh"
+
# include "eevee_defines.hh"
# include "GPU_shader_shared.h"
namespace blender::eevee {
-using draw::Framebuffer;
-using draw::SwapChain;
-using draw::Texture;
-using draw::TextureFromPool;
+using namespace draw;
+
+constexpr eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
+constexpr eGPUSamplerState with_filter = GPU_SAMPLER_FILTER;
#endif
#define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14
/* -------------------------------------------------------------------- */
+/** \name Debug Mode
+ * \{ */
+
+/** These are just to make more sense of G.debug_value's values. Reserved range is 1-30. */
+enum eDebugMode : uint32_t {
+ DEBUG_NONE = 0u,
+ /**
+ * Gradient showing light evaluation hot-spots.
+ */
+ DEBUG_LIGHT_CULLING = 1u,
+ /**
+ * Show incorrectly downsample tiles in red.
+ */
+ DEBUG_HIZ_VALIDATION = 2u,
+ /**
+ * Tile-maps to screen. Is also present in other modes.
+ * - Black pixels, no pages allocated.
+ * - Green pixels, pages cached.
+ * - Red pixels, pages allocated.
+ */
+ DEBUG_SHADOW_TILEMAPS = 10u,
+ /**
+ * Random color per pages. Validates page density allocation and sampling.
+ */
+ DEBUG_SHADOW_PAGES = 11u,
+ /**
+ * Outputs random color per tile-map (or tile-map level). Validates tile-maps coverage.
+ * Black means not covered by any tile-maps LOD of the shadow.
+ */
+ DEBUG_SHADOW_LOD = 12u,
+ /**
+ * Outputs white pixels for pages allocated and black pixels for unused pages.
+ * This needs DEBUG_SHADOW_PAGE_ALLOCATION_ENABLED defined in order to work.
+ */
+ DEBUG_SHADOW_PAGE_ALLOCATION = 13u,
+ /**
+ * Outputs the tile-map atlas. Default tile-map is too big for the usual screen resolution.
+ * Try lowering SHADOW_TILEMAP_PER_ROW and SHADOW_MAX_TILEMAP before using this option.
+ */
+ DEBUG_SHADOW_TILE_ALLOCATION = 14u,
+ /**
+ * Visualize linear depth stored in the atlas regions of the active light.
+ * This way, one can check if the rendering, the copying and the shadow sampling functions works.
+ */
+ DEBUG_SHADOW_SHADOW_DEPTH = 15u
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Sampling
* \{ */
@@ -124,7 +177,12 @@ struct CameraData {
float clip_far;
eCameraType type;
- int _pad0;
+ bool1 initialized;
+
+#ifdef __cplusplus
+ /* Small constructor to allow detecting new buffers. */
+ CameraData() : initialized(false){};
+#endif
};
BLI_STATIC_ASSERT_ALIGN(CameraData, 16)
@@ -136,6 +194,17 @@ BLI_STATIC_ASSERT_ALIGN(CameraData, 16)
#define FILM_PRECOMP_SAMPLE_MAX 16
+enum eFilmWeightLayerIndex : uint32_t {
+ FILM_WEIGHT_LAYER_ACCUMULATION = 0u,
+ FILM_WEIGHT_LAYER_DISTANCE = 1u,
+};
+
+enum ePassStorageType : uint32_t {
+ PASS_STORAGE_COLOR = 0u,
+ PASS_STORAGE_VALUE = 1u,
+ PASS_STORAGE_CRYPTOMATTE = 2u,
+};
+
struct FilmSample {
int2 texel;
float weight;
@@ -149,6 +218,8 @@ struct FilmData {
int2 extent;
/** Offset of the film in the full-res frame, in pixels. */
int2 offset;
+ /** Extent used by the render buffers when rendering the main views. */
+ int2 render_extent;
/** Sub-pixel offset applied to the window matrix.
* NOTE: In final film pixel unit.
* NOTE: Positive values makes the view translate in the negative axes direction.
@@ -156,6 +227,8 @@ struct FilmData {
* pixel if using scaled resolution rendering.
*/
float2 subpixel_offset;
+ /** Scaling factor to convert texel to uvs. */
+ float2 extent_inv;
/** Is true if history is valid and can be sampled. Bypass history to resets accumulation. */
bool1 use_history;
/** Is true if combined buffer is valid and can be re-projected to reduce variance. */
@@ -165,7 +238,9 @@ struct FilmData {
/** Is true if accumulation of filtered passes is needed. */
bool1 any_render_pass_1;
bool1 any_render_pass_2;
- int _pad0, _pad1;
+ /** Controlled by user in lookdev mode or by render settings. */
+ float background_opacity;
+ float _pad0;
/** Output counts per type. */
int color_len, value_len;
/** Index in color_accum_img or value_accum_img of each pass. -1 if pass is not enabled. */
@@ -186,21 +261,27 @@ struct FilmData {
int combined_id;
/** Id of the render-pass to be displayed. -1 for combined. */
int display_id;
- /** True if the render-pass to be displayed is from the value accum buffer. */
- bool1 display_is_value;
+ /** Storage type of the render-pass to be displayed. */
+ ePassStorageType display_storage_type;
/** True if we bypass the accumulation and directly output the accumulation buffer. */
bool1 display_only;
/** Start of AOVs and number of aov. */
int aov_color_id, aov_color_len;
int aov_value_id, aov_value_len;
+ /** Start of cryptomatte per layer (-1 if pass is not enabled). */
+ int cryptomatte_object_id;
+ int cryptomatte_asset_id;
+ int cryptomatte_material_id;
+ /** Max number of samples stored per layer (is even number). */
+ int cryptomatte_samples_len;
/** Settings to render mist pass */
float mist_scale, mist_bias, mist_exponent;
/** Scene exposure used for better noise reduction. */
- float exposure;
+ float exposure_scale;
/** Scaling factor for scaled resolution rendering. */
int scaling_factor;
/** Film pixel filter radius. */
- float filter_size;
+ float filter_radius;
/** Precomputed samples. First in the table is the closest one. The rest is unordered. */
int samples_len;
/** Sum of the weights of all samples in the sample table. */
@@ -209,17 +290,17 @@ struct FilmData {
};
BLI_STATIC_ASSERT_ALIGN(FilmData, 16)
-static inline float film_filter_weight(float filter_size, float sample_distance_sqr)
+static inline float film_filter_weight(float filter_radius, float sample_distance_sqr)
{
#if 1 /* Faster */
/* Gaussian fitted to Blackman-Harris. */
- float r = sample_distance_sqr / (filter_size * filter_size);
+ float r = sample_distance_sqr / (filter_radius * filter_radius);
const float sigma = 0.284;
const float fac = -0.5 / (sigma * sigma);
float weight = expf(fac * r);
#else
/* Blackman-Harris filter. */
- float r = M_2PI * saturate(0.5 + sqrtf(sample_distance_sqr) / (2.0 * filter_size));
+ float r = M_2PI * saturate(0.5 + sqrtf(sample_distance_sqr) / (2.0 * filter_radius));
float weight = 0.35875 - 0.48829 * cosf(r) + 0.14128 * cosf(2.0 * r) - 0.01168 * cosf(3.0 * r);
#endif
return weight;
@@ -228,6 +309,17 @@ static inline float film_filter_weight(float filter_size, float sample_distance_
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Render passes
+ * \{ */
+
+enum eRenderPassLayerIndex : uint32_t {
+ RENDER_PASS_LAYER_DIFFUSE_LIGHT = 0u,
+ RENDER_PASS_LAYER_SPECULAR_LIGHT = 1u,
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Arbitrary Output Variables
* \{ */
@@ -301,6 +393,272 @@ BLI_STATIC_ASSERT_ALIGN(VelocityGeometryIndex, 16)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Motion Blur
+ * \{ */
+
+#define MOTION_BLUR_TILE_SIZE 32
+#define MOTION_BLUR_MAX_TILE 512 /* 16384 / MOTION_BLUR_TILE_SIZE */
+struct MotionBlurData {
+ /** As the name suggests. Used to avoid a division in the sampling. */
+ float2 target_size_inv;
+ /** Viewport motion scaling factor. Make blur relative to frame time not render time. */
+ float2 motion_scale;
+ /** Depth scaling factor. Avoid blurring background behind moving objects. */
+ float depth_scale;
+
+ float _pad0, _pad1, _pad2;
+};
+BLI_STATIC_ASSERT_ALIGN(MotionBlurData, 16)
+
+/* For some reasons some GLSL compilers do not like this struct.
+ * So we declare it as a uint array instead and do indexing ourselves. */
+#ifdef __cplusplus
+struct MotionBlurTileIndirection {
+ /**
+ * Stores indirection to the tile with the highest velocity covering each tile.
+ * This is stored using velocity in the MSB to be able to use atomicMax operations.
+ */
+ uint prev[MOTION_BLUR_MAX_TILE][MOTION_BLUR_MAX_TILE];
+ uint next[MOTION_BLUR_MAX_TILE][MOTION_BLUR_MAX_TILE];
+};
+BLI_STATIC_ASSERT_ALIGN(MotionBlurTileIndirection, 16)
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Depth of field
+ * \{ */
+
+/* 5% error threshold. */
+#define DOF_FAST_GATHER_COC_ERROR 0.05
+#define DOF_GATHER_RING_COUNT 5
+#define DOF_DILATE_RING_COUNT 3
+
+struct DepthOfFieldData {
+ /** Size of the render targets for gather & scatter passes. */
+ int2 extent;
+ /** Size of a pixel in uv space (1.0 / extent). */
+ float2 texel_size;
+ /** Scale factor for anisotropic bokeh. */
+ float2 bokeh_anisotropic_scale;
+ float2 bokeh_anisotropic_scale_inv;
+ /* Correction factor to align main target pixels with the filtered mipmap chain texture. */
+ float2 gather_uv_fac;
+ /** Scatter parameters. */
+ float scatter_coc_threshold;
+ float scatter_color_threshold;
+ float scatter_neighbor_max_color;
+ int scatter_sprite_per_row;
+ /** Number of side the bokeh shape has. */
+ float bokeh_blades;
+ /** Rotation of the bokeh shape. */
+ float bokeh_rotation;
+ /** Multiplier and bias to apply to linear depth to Circle of confusion (CoC). */
+ float coc_mul, coc_bias;
+ /** Maximum absolute allowed Circle of confusion (CoC). Min of computed max and user max. */
+ float coc_abs_max;
+ /** Copy of camera type. */
+ eCameraType camera_type;
+ /** Weights of spatial filtering in stabilize pass. Not array to avoid alignment restriction. */
+ float4 filter_samples_weight;
+ float filter_center_weight;
+ /** Max number of sprite in the scatter pass for each ground. */
+ int scatter_max_rect;
+
+ int _pad0, _pad1;
+};
+BLI_STATIC_ASSERT_ALIGN(DepthOfFieldData, 16)
+
+struct ScatterRect {
+ /** Color and CoC of the 4 pixels the scatter sprite represents. */
+ float4 color_and_coc[4];
+ /** Rect center position in half pixel space. */
+ float2 offset;
+ /** Rect half extent in half pixel space. */
+ float2 half_extent;
+};
+BLI_STATIC_ASSERT_ALIGN(ScatterRect, 16)
+
+/** WORKAROUND(@fclem): This is because this file is included before common_math_lib.glsl. */
+#ifndef M_PI
+# define EEVEE_PI
+# define M_PI 3.14159265358979323846 /* pi */
+#endif
+
+static inline float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth)
+{
+ depth = (dof.camera_type != CAMERA_ORTHO) ? 1.0f / depth : depth;
+ return dof.coc_mul * depth + dof.coc_bias;
+}
+
+static inline float regular_polygon_side_length(float sides_count)
+{
+ return 2.0f * sinf(M_PI / sides_count);
+}
+
+/* Returns intersection ratio between the radius edge at theta and the regular polygon edge.
+ * Start first corners at theta == 0. */
+static inline float circle_to_polygon_radius(float sides_count, float theta)
+{
+ /* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide
+ * 36). */
+ float side_angle = (2.0f * M_PI) / sides_count;
+ return cosf(side_angle * 0.5f) /
+ cosf(theta - side_angle * floorf((sides_count * theta + M_PI) / (2.0f * M_PI)));
+}
+
+/* Remap input angle to have homogenous spacing of points along a polygon edge.
+ * Expects theta to be in [0..2pi] range. */
+static inline float circle_to_polygon_angle(float sides_count, float theta)
+{
+ float side_angle = (2.0f * M_PI) / sides_count;
+ float halfside_angle = side_angle * 0.5f;
+ float side = floorf(theta / side_angle);
+ /* Length of segment from center to the middle of polygon side. */
+ float adjacent = circle_to_polygon_radius(sides_count, 0.0f);
+
+ /* This is the relative position of the sample on the polygon half side. */
+ float local_theta = theta - side * side_angle;
+ float ratio = (local_theta - halfside_angle) / halfside_angle;
+
+ float halfside_len = regular_polygon_side_length(sides_count) * 0.5f;
+ float opposite = ratio * halfside_len;
+
+ /* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */
+ float final_local_theta = atanf(opposite / adjacent);
+
+ return side * side_angle + final_local_theta;
+}
+
+#ifdef EEVEE_PI
+# undef M_PI
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Light Culling
+ * \{ */
+
+/* Number of items we can cull. Limited by how we store CullingZBin. */
+#define CULLING_MAX_ITEM 65536
+/* Fine grained subdivision in the Z direction. Limited by the LDS in z-binning compute shader. */
+#define CULLING_ZBIN_COUNT 4096
+/* Max tile map resolution per axes. */
+#define CULLING_TILE_RES 16
+
+struct LightCullingData {
+ /** Scale applied to tile pixel coordinates to get target UV coordinate. */
+ float2 tile_to_uv_fac;
+ /** Scale and bias applied to linear Z to get zbin. */
+ float zbin_scale;
+ float zbin_bias;
+ /** Valid item count in the source data array. */
+ uint items_count;
+ /** Items that are processed by the 2.5D culling. */
+ uint local_lights_len;
+ /** Items that are **NOT** processed by the 2.5D culling (i.e: Sun Lights). */
+ uint sun_lights_len;
+ /** Number of items that passes the first culling test. */
+ uint visible_count;
+ /** Extent of one square tile in pixels. */
+ float tile_size;
+ /** Number of tiles on the X/Y axis. */
+ uint tile_x_len;
+ uint tile_y_len;
+ /** Number of word per tile. Depends on the maximum number of lights. */
+ uint tile_word_len;
+};
+BLI_STATIC_ASSERT_ALIGN(LightCullingData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Lights
+ * \{ */
+
+#define LIGHT_NO_SHADOW -1
+
+enum eLightType : uint32_t {
+ LIGHT_SUN = 0u,
+ LIGHT_POINT = 1u,
+ LIGHT_SPOT = 2u,
+ LIGHT_RECT = 3u,
+ LIGHT_ELLIPSE = 4u
+};
+
+static inline bool is_area_light(eLightType type)
+{
+ return type >= LIGHT_RECT;
+}
+
+struct LightData {
+ /** Normalized object matrix. Last column contains data accessible using the following macros. */
+ float4x4 object_mat;
+ /** Packed data in the last column of the object_mat. */
+#define _area_size_x object_mat[0][3]
+#define _area_size_y object_mat[1][3]
+#define _radius _area_size_x
+#define _spot_mul object_mat[2][3]
+#define _spot_bias object_mat[3][3]
+ /** Aliases for axes. */
+#ifndef USE_GPU_SHADER_CREATE_INFO
+# define _right object_mat[0]
+# define _up object_mat[1]
+# define _back object_mat[2]
+# define _position object_mat[3]
+#else
+# define _right object_mat[0].xyz
+# define _up object_mat[1].xyz
+# define _back object_mat[2].xyz
+# define _position object_mat[3].xyz
+#endif
+ /** Influence radius (inverted and squared) adjusted for Surface / Volume power. */
+ float influence_radius_invsqr_surface;
+ float influence_radius_invsqr_volume;
+ /** Maximum influence radius. Used for culling. */
+ float influence_radius_max;
+ /** Index of the shadow struct on CPU. -1 means no shadow. */
+ int shadow_id;
+ /** NOTE: It is ok to use float3 here. A float is declared right after it.
+ * float3 is also aligned to 16 bytes. */
+ float3 color;
+ /** Power depending on shader type. */
+ float diffuse_power;
+ float specular_power;
+ float volume_power;
+ float transmit_power;
+ /** Special radius factor for point lighting. */
+ float radius_squared;
+ /** Light Type. */
+ eLightType type;
+ /** Spot angle tangent. */
+ float spot_tan;
+ /** Spot size. Aligned to size of float2. */
+ float2 spot_size_inv;
+ /** Associated shadow data. Only valid if shadow_id is not LIGHT_NO_SHADOW. */
+ // ShadowData shadow_data;
+};
+BLI_STATIC_ASSERT_ALIGN(LightData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Hierarchical-Z Buffer
+ * \{ */
+
+struct HiZData {
+ /** Scale factor to remove HiZBuffer padding. */
+ float2 uv_scale;
+
+ float2 _pad0;
+};
+BLI_STATIC_ASSERT_ALIGN(HiZData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Ray-Tracing
* \{ */
@@ -321,6 +679,34 @@ enum eClosureBits : uint32_t {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Subsurface
+ * \{ */
+
+#define SSS_SAMPLE_MAX 64
+#define SSS_BURLEY_TRUNCATE 16.0
+#define SSS_BURLEY_TRUNCATE_CDF 0.9963790093708328
+#define SSS_TRANSMIT_LUT_SIZE 64.0
+#define SSS_TRANSMIT_LUT_RADIUS 1.218
+#define SSS_TRANSMIT_LUT_SCALE ((SSS_TRANSMIT_LUT_SIZE - 1.0) / float(SSS_TRANSMIT_LUT_SIZE))
+#define SSS_TRANSMIT_LUT_BIAS (0.5 / float(SSS_TRANSMIT_LUT_SIZE))
+#define SSS_TRANSMIT_LUT_STEP_RES 64.0
+
+struct SubsurfaceData {
+ /** xy: 2D sample position [-1..1], zw: sample_bounds. */
+ /* NOTE(fclem) Using float4 for alignment. */
+ float4 samples[SSS_SAMPLE_MAX];
+ /** Sample index after which samples are not randomly rotated anymore. */
+ int jitter_threshold;
+ /** Number of samples precomputed in the set. */
+ int sample_len;
+ int _pad0;
+ int _pad1;
+};
+BLI_STATIC_ASSERT_ALIGN(SubsurfaceData, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Utility Texture
* \{ */
@@ -359,11 +745,24 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer)
using AOVsInfoDataBuf = draw::StorageBuffer<AOVsInfoData>;
using CameraDataBuf = draw::UniformBuffer<CameraData>;
+using DepthOfFieldDataBuf = draw::UniformBuffer<DepthOfFieldData>;
+using DepthOfFieldScatterListBuf = draw::StorageArrayBuffer<ScatterRect, 16, true>;
+using DrawIndirectBuf = draw::StorageBuffer<DrawCommand, true>;
using FilmDataBuf = draw::UniformBuffer<FilmData>;
+using HiZDataBuf = draw::UniformBuffer<HiZData>;
+using LightCullingDataBuf = draw::StorageBuffer<LightCullingData>;
+using LightCullingKeyBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>;
+using LightCullingTileBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>;
+using LightCullingZbinBuf = draw::StorageArrayBuffer<uint, CULLING_ZBIN_COUNT, true>;
+using LightCullingZdistBuf = draw::StorageArrayBuffer<float, LIGHT_CHUNK, true>;
+using LightDataBuf = draw::StorageArrayBuffer<LightData, LIGHT_CHUNK>;
+using MotionBlurDataBuf = draw::UniformBuffer<MotionBlurData>;
+using MotionBlurTileIndirectionBuf = draw::StorageBuffer<MotionBlurTileIndirection, true>;
using SamplingDataBuf = draw::StorageBuffer<SamplingData>;
using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>;
using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>;
using VelocityObjectBuf = draw::StorageArrayBuffer<float4x4, 16>;
+using CryptomatteObjectBuf = draw::StorageArrayBuffer<float2, 16>;
} // namespace blender::eevee
#endif
diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.cc b/source/blender/draw/engines/eevee_next/eevee_sync.cc
index e2d4b0ac1c2..09ea7c9ec3d 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sync.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_sync.cc
@@ -74,25 +74,12 @@ WorldHandle &SyncModule::sync_world(::World *world)
/** \name Common
* \{ */
-static inline void shgroup_geometry_call(DRWShadingGroup *grp,
- Object *ob,
- GPUBatch *geom,
- int v_first = -1,
- int v_count = -1,
- bool use_instancing = false)
+static inline void geometry_call(PassMain::Sub *sub_pass,
+ GPUBatch *geom,
+ ResourceHandle resource_handle)
{
- if (grp == nullptr) {
- return;
- }
-
- if (v_first == -1) {
- DRW_shgroup_call(grp, geom, ob);
- }
- else if (use_instancing) {
- DRW_shgroup_call_instance_range(grp, ob, geom, v_first, v_count);
- }
- else {
- DRW_shgroup_call_range(grp, ob, geom, v_first, v_count);
+ if (sub_pass != nullptr) {
+ sub_pass->draw(geom, resource_handle);
}
}
@@ -102,9 +89,13 @@ static inline void shgroup_geometry_call(DRWShadingGroup *grp,
/** \name Mesh
* \{ */
-void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle)
+void SyncModule::sync_mesh(Object *ob,
+ ObjectHandle &ob_handle,
+ ResourceHandle res_handle,
+ const ObjectRef &ob_ref)
{
- bool has_motion = inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc);
+ bool has_motion = inst_.velocity.step_object_sync(
+ ob, ob_handle.object_key, res_handle, ob_handle.recalc);
MaterialArray &material_array = inst_.materials.material_array_get(ob, has_motion);
@@ -123,14 +114,20 @@ void SyncModule::sync_mesh(Object *ob, ObjectHandle &ob_handle)
continue;
}
Material *material = material_array.materials[i];
- shgroup_geometry_call(material->shading.shgrp, ob, geom);
- shgroup_geometry_call(material->prepass.shgrp, ob, geom);
- shgroup_geometry_call(material->shadow.shgrp, ob, geom);
+ geometry_call(material->shading.sub_pass, geom, res_handle);
+ geometry_call(material->prepass.sub_pass, geom, res_handle);
+ geometry_call(material->shadow.sub_pass, geom, res_handle);
- is_shadow_caster = is_shadow_caster || material->shadow.shgrp != nullptr;
+ is_shadow_caster = is_shadow_caster || material->shadow.sub_pass != nullptr;
is_alpha_blend = is_alpha_blend || material->is_alpha_blend_transparent;
+
+ GPUMaterial *gpu_material = material_array.gpu_materials[i];
+ ::Material *mat = GPU_material_get_material(gpu_material);
+ inst_.cryptomatte.sync_material(mat);
}
+ inst_.manager->extract_object_attributes(res_handle, ob_ref, material_array.gpu_materials);
+ inst_.cryptomatte.sync_object(ob, res_handle);
// shadows.sync_object(ob, ob_handle, is_shadow_caster, is_alpha_blend);
}
@@ -155,11 +152,13 @@ struct gpIterData {
int vcount = 0;
bool instancing = false;
- gpIterData(Instance &inst_, Object *ob_, ObjectHandle &ob_handle)
+ gpIterData(Instance &inst_, Object *ob_, ObjectHandle &ob_handle, ResourceHandle resource_handle)
: inst(inst_),
ob(ob_),
material_array(inst_.materials.material_array_get(
- ob_, inst_.velocity.step_object_sync(ob, ob_handle.object_key, ob_handle.recalc)))
+ ob_,
+ inst_.velocity.step_object_sync(
+ ob, ob_handle.object_key, resource_handle, ob_handle.recalc)))
{
cfra = DEG_get_ctime(inst.depsgraph);
};
@@ -167,26 +166,28 @@ struct gpIterData {
static void gpencil_drawcall_flush(gpIterData &iter)
{
+#if 0 /* Incompatible with new draw manager. */
if (iter.geom != nullptr) {
- shgroup_geometry_call(iter.material->shading.shgrp,
+ geometry_call(iter.material->shading.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
- shgroup_geometry_call(iter.material->prepass.shgrp,
+ geometry_call(iter.material->prepass.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
- shgroup_geometry_call(iter.material->shadow.shgrp,
+ geometry_call(iter.material->shadow.sub_pass,
iter.ob,
iter.geom,
iter.vfirst,
iter.vcount,
iter.instancing);
}
+#endif
iter.geom = nullptr;
iter.vfirst = -1;
iter.vcount = 0;
@@ -250,21 +251,22 @@ static void gpencil_stroke_sync(bGPDlayer *UNUSED(gpl),
}
}
-void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle)
+void SyncModule::sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle)
{
/* TODO(fclem): Waiting for a user option to use the render engine instead of gpencil engine. */
if (true) {
inst_.gpencil_engine_enabled = true;
return;
}
+ UNUSED_VARS(res_handle);
- gpIterData iter(inst_, ob, ob_handle);
+ gpIterData iter(inst_, ob, ob_handle, res_handle);
BKE_gpencil_visible_stroke_iter((bGPdata *)ob->data, nullptr, gpencil_stroke_sync, &iter);
gpencil_drawcall_flush(iter);
- // bool is_caster = true; /* TODO material.shadow.shgrp. */
+ // bool is_caster = true; /* TODO material.shadow.sub_pass. */
// bool is_alpha_blend = true; /* TODO material.is_alpha_blend. */
// shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
}
@@ -280,19 +282,24 @@ static void shgroup_curves_call(MaterialPass &matpass,
ParticleSystem *part_sys = nullptr,
ModifierData *modifier_data = nullptr)
{
- if (matpass.shgrp == nullptr) {
+ UNUSED_VARS(ob, modifier_data);
+ if (matpass.sub_pass == nullptr) {
return;
}
if (part_sys != nullptr) {
- DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.shgrp, matpass.gpumat);
+ // DRW_shgroup_hair_create_sub(ob, part_sys, modifier_data, matpass.sub_pass, matpass.gpumat);
}
else {
- DRW_shgroup_curves_create_sub(ob, matpass.shgrp, matpass.gpumat);
+ // DRW_shgroup_curves_create_sub(ob, matpass.sub_pass, matpass.gpumat);
}
}
-void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data)
+void SyncModule::sync_curves(Object *ob,
+ ObjectHandle &ob_handle,
+ ResourceHandle res_handle,
+ ModifierData *modifier_data)
{
+ UNUSED_VARS(res_handle);
int mat_nr = CURVES_MATERIAL_NR;
ParticleSystem *part_sys = nullptr;
@@ -317,10 +324,16 @@ void SyncModule::sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData *
shgroup_curves_call(material.prepass, ob, part_sys, modifier_data);
shgroup_curves_call(material.shadow, ob, part_sys, modifier_data);
+ inst_.cryptomatte.sync_object(ob, res_handle);
+ GPUMaterial *gpu_material =
+ inst_.materials.material_array_get(ob, has_motion).gpu_materials[mat_nr - 1];
+ ::Material *mat = GPU_material_get_material(gpu_material);
+ inst_.cryptomatte.sync_material(mat);
+
/* TODO(fclem) Hair velocity. */
// shading_passes.velocity.gpencil_add(ob, ob_handle);
- // bool is_caster = material.shadow.shgrp != nullptr;
+ // bool is_caster = material.shadow.sub_pass != nullptr;
// bool is_alpha_blend = material.is_alpha_blend_transparent;
// shadows.sync_object(ob, ob_handle, is_caster, is_alpha_blend);
}
diff --git a/source/blender/draw/engines/eevee_next/eevee_sync.hh b/source/blender/draw/engines/eevee_next/eevee_sync.hh
index bd8147a2882..ab883ce44c2 100644
--- a/source/blender/draw/engines/eevee_next/eevee_sync.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_sync.hh
@@ -150,9 +150,15 @@ class SyncModule {
ObjectHandle &sync_object(Object *ob);
WorldHandle &sync_world(::World *world);
- void sync_mesh(Object *ob, ObjectHandle &ob_handle);
- void sync_gpencil(Object *ob, ObjectHandle &ob_handle);
- void sync_curves(Object *ob, ObjectHandle &ob_handle, ModifierData *modifier_data = nullptr);
+ void sync_mesh(Object *ob,
+ ObjectHandle &ob_handle,
+ ResourceHandle res_handle,
+ const ObjectRef &ob_ref);
+ void sync_gpencil(Object *ob, ObjectHandle &ob_handle, ResourceHandle res_handle);
+ void sync_curves(Object *ob,
+ ObjectHandle &ob_handle,
+ ResourceHandle res_handle,
+ ModifierData *modifier_data = nullptr);
};
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc
index 4bd0af8204e..7af311a8ccc 100644
--- a/source/blender/draw/engines/eevee_next/eevee_velocity.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc
@@ -9,10 +9,6 @@
* temporal re-projection or motion blur.
*
* It is the module that tracks the objects between frames updates.
- *
- * #VelocityModule contains all motion steps data and logic.
- * #VelocityPass contains the resolve pass for static geometry.
- * #VelocityView is a per view instance that contain the velocity buffer.
*/
#include "BKE_duplilist.h"
@@ -36,16 +32,21 @@ namespace blender::eevee {
void VelocityModule::init()
{
-#if 0 /* TODO renderpasses */
- if (inst_.render && (inst_.render_passes.vector != nullptr)) {
- /* No motion blur and the vector pass was requested. Do the step sync here. */
+ if (inst_.render && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR) != 0) {
+ /* No motion blur and the vector pass was requested. Do the steps sync here. */
const Scene *scene = inst_.scene;
float initial_time = scene->r.cfra + scene->r.subframe;
step_sync(STEP_PREVIOUS, initial_time - 1.0f);
step_sync(STEP_NEXT, initial_time + 1.0f);
+
inst_.set_time(initial_time);
+ step_ = STEP_CURRENT;
+ /* Let the main sync loop handle the current step. */
}
-#endif
+
+ /* For viewport, only previous motion is supported.
+ * Still bind previous step to avoid undefined behavior. */
+ next_step_ = inst_.is_viewport() ? STEP_PREVIOUS : STEP_NEXT;
}
static void step_object_sync_render(void *velocity,
@@ -54,7 +55,9 @@ static void step_object_sync_render(void *velocity,
Depsgraph *UNUSED(depsgraph))
{
ObjectKey object_key(ob);
- reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key);
+ /* NOTE: Dummy resource handle since this will not be used for drawing. */
+ ResourceHandle resource_handle(0);
+ reinterpret_cast<VelocityModule *>(velocity)->step_object_sync(ob, object_key, resource_handle);
}
void VelocityModule::step_sync(eVelocityStep step, float time)
@@ -70,10 +73,18 @@ void VelocityModule::step_camera_sync()
{
inst_.camera.sync();
*camera_steps[step_] = inst_.camera.data_get();
+ step_time[step_] = inst_.scene->r.cfra + inst_.scene->r.subframe;
+ /* Fix undefined camera steps when rendering is starting. */
+ if ((step_ == STEP_CURRENT) && (camera_steps[STEP_PREVIOUS]->initialized == false)) {
+ *camera_steps[STEP_PREVIOUS] = *static_cast<CameraData *>(camera_steps[step_]);
+ camera_steps[STEP_PREVIOUS]->initialized = true;
+ step_time[STEP_PREVIOUS] = step_time[step_];
+ }
}
bool VelocityModule::step_object_sync(Object *ob,
ObjectKey &object_key,
+ ResourceHandle resource_handle,
int /*IDRecalcFlag*/ recalc)
{
bool has_motion = object_has_velocity(ob) || (recalc & ID_RECALC_TRANSFORM);
@@ -85,8 +96,6 @@ bool VelocityModule::step_object_sync(Object *ob,
return false;
}
- uint32_t resource_id = DRW_object_resource_id_get(ob);
-
/* Object motion. */
/* FIXME(fclem) As we are using original objects pointers, there is a chance the previous
* object key matches a totally different object if the scene was changed by user or python
@@ -95,7 +104,7 @@ bool VelocityModule::step_object_sync(Object *ob,
* We live with that until we have a correct way of identifying new objects. */
VelocityObjectData &vel = velocity_map.lookup_or_add_default(object_key);
vel.obj.ofs[step_] = object_steps_usage[step_]++;
- vel.obj.resource_id = resource_id;
+ vel.obj.resource_id = resource_handle.resource_index();
vel.id = (ID *)ob->data;
object_steps[step_]->get_or_resize(vel.obj.ofs[step_]) = ob->obmat;
if (step_ == STEP_CURRENT) {
@@ -213,6 +222,7 @@ void VelocityModule::step_swap()
SWAP(VelocityObjectBuf *, object_steps[step_a], object_steps[step_b]);
SWAP(VelocityGeometryBuf *, geometry_steps[step_a], geometry_steps[step_b]);
SWAP(CameraDataBuf *, camera_steps[step_a], camera_steps[step_b]);
+ SWAP(float, step_time[step_a], step_time[step_b]);
for (VelocityObjectData &vel : velocity_map.values()) {
vel.obj.ofs[step_a] = vel.obj.ofs[step_b];
@@ -239,10 +249,7 @@ void VelocityModule::step_swap()
void VelocityModule::begin_sync()
{
- if (inst_.is_viewport()) {
- /* Viewport always evaluate current step. */
- step_ = STEP_CURRENT;
- }
+ step_ = STEP_CURRENT;
step_camera_sync();
object_steps_usage[step_] = 0;
}
@@ -255,7 +262,7 @@ void VelocityModule::end_sync()
uint32_t max_resource_id_ = 0u;
for (Map<ObjectKey, VelocityObjectData>::Item item : velocity_map.items()) {
- if (item.value.obj.resource_id == (uint)-1) {
+ if (item.value.obj.resource_id == (uint32_t)-1) {
deleted_obj.append(item.key);
}
else {
@@ -267,11 +274,15 @@ void VelocityModule::end_sync()
inst_.sampling.reset();
}
- for (auto key : deleted_obj) {
+ if (inst_.is_viewport() && camera_has_motion()) {
+ inst_.sampling.reset();
+ }
+
+ for (auto &key : deleted_obj) {
velocity_map.remove(key);
}
- indirection_buf.resize(power_of_2_max_u(max_resource_id_ + 1));
+ indirection_buf.resize(ceil_to_multiple_u(max_resource_id_, 128));
/* Avoid uploading more data to the GPU as well as an extra level of
* indirection on the GPU by copying back offsets the to VelocityIndex. */
@@ -300,19 +311,6 @@ void VelocityModule::end_sync()
camera_steps[STEP_CURRENT]->push_update();
camera_steps[STEP_NEXT]->push_update();
indirection_buf.push_update();
-
- {
- resolve_ps_ = DRW_pass_create("Velocity.Resolve", (DRWState)0);
- GPUShader *sh = inst_.shaders.static_shader_get(VELOCITY_RESOLVE);
- DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_);
- DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &input_depth_tx_);
- DRW_shgroup_uniform_image_ref(grp, "velocity_view_img", &velocity_view_tx_);
- DRW_shgroup_uniform_image_ref(grp, "velocity_camera_img", &velocity_camera_tx_);
- DRW_shgroup_uniform_block(grp, "camera_prev", *camera_steps[STEP_PREVIOUS]);
- DRW_shgroup_uniform_block(grp, "camera_curr", *camera_steps[STEP_CURRENT]);
- DRW_shgroup_uniform_block(grp, "camera_next", *camera_steps[STEP_NEXT]);
- DRW_shgroup_call_compute_ref(grp, resolve_dispatch_size_);
- }
}
bool VelocityModule::object_has_velocity(const Object *ob)
@@ -359,60 +357,30 @@ void VelocityModule::bind_resources(DRWShadingGroup *grp)
DRW_shgroup_storage_block_ref(grp, "velocity_indirection_buf", &indirection_buf);
}
-/* Resolve pass for static geometry and to camera space projection. */
-void VelocityModule::resolve_camera_motion(GPUTexture *depth_tx,
- GPUTexture *velocity_view_tx,
- GPUTexture *velocity_camera_tx)
+bool VelocityModule::camera_has_motion() const
{
- input_depth_tx_ = depth_tx;
- velocity_view_tx_ = velocity_view_tx;
- velocity_camera_tx_ = velocity_camera_tx;
-
- resolve_dispatch_size_.x = divide_ceil_u(GPU_texture_width(depth_tx), 8);
- resolve_dispatch_size_.y = divide_ceil_u(GPU_texture_height(depth_tx), 8);
-
- DRW_draw_pass(resolve_ps_);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Velocity View
- * \{ */
-
-void VelocityView::sync()
-{
- /* TODO: Remove. */
- velocity_view_tx_.sync();
- velocity_camera_tx_.sync();
-}
-
-void VelocityView::acquire(int2 extent)
-{
- /* WORKAROUND: View name should be unique and static.
- * With this, we can reuse the same texture across views. */
- DrawEngineType *owner = (DrawEngineType *)view_name_.c_str();
-
- /* Only RG16F when only doing only reprojection or motion blur. */
- eGPUTextureFormat format = inst_.is_viewport() ? GPU_RG16F : GPU_RGBA16F;
- velocity_view_tx_.acquire(extent, format, owner);
- if (false /* TODO(fclem): Panoramic camera. */) {
- velocity_camera_tx_.acquire(extent, format, owner);
- }
- else {
- velocity_camera_tx_.acquire(int2(1), format, owner);
+ /* Only valid after sync. */
+ if (inst_.is_viewport()) {
+ /* Viewport has no next step. */
+ return *camera_steps[STEP_PREVIOUS] != *camera_steps[STEP_CURRENT];
}
+ return *camera_steps[STEP_PREVIOUS] != *camera_steps[STEP_CURRENT] &&
+ *camera_steps[STEP_NEXT] != *camera_steps[STEP_CURRENT];
}
-void VelocityView::resolve(GPUTexture *depth_tx)
+bool VelocityModule::camera_changed_projection() const
{
- inst_.velocity.resolve_camera_motion(depth_tx, velocity_view_tx_, velocity_camera_tx_);
+ /* Only valid after sync. */
+ if (inst_.is_viewport()) {
+ return camera_steps[STEP_PREVIOUS]->type != camera_steps[STEP_CURRENT]->type;
+ }
+ /* Cannot happen in render mode since we set the type during the init phase. */
+ return false;
}
-void VelocityView::release()
+float VelocityModule::step_time_delta_get(eVelocityStep start, eVelocityStep end) const
{
- velocity_view_tx_.release();
- velocity_camera_tx_.release();
+ return step_time[end] - step_time[start];
}
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh
index e2606c061e1..6f18b05d476 100644
--- a/source/blender/draw/engines/eevee_next/eevee_velocity.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh
@@ -27,8 +27,6 @@ namespace blender::eevee {
/** Container for scene velocity data. */
class VelocityModule {
- friend class VelocityView;
-
public:
struct VelocityObjectData : public VelocityIndex {
/** ID to retrieve the corresponding #VelocityGeometryData after copy. */
@@ -58,6 +56,8 @@ class VelocityModule {
int3 object_steps_usage = int3(0);
/** Buffer of all #VelocityIndex used in this frame. Indexed by draw manager resource id. */
VelocityIndexBuf indirection_buf;
+ /** Frame time at which each steps were evaluated. */
+ float3 step_time;
/**
* Copies of camera data. One for previous and one for next time step.
@@ -67,16 +67,10 @@ class VelocityModule {
private:
Instance &inst_;
+ /** Step being synced. */
eVelocityStep step_ = STEP_CURRENT;
-
- DRWPass *resolve_ps_ = nullptr;
-
- /** Reference only. Not owned. */
- GPUTexture *input_depth_tx_;
- GPUTexture *velocity_view_tx_;
- GPUTexture *velocity_camera_tx_;
-
- int3 resolve_dispatch_size_ = int3(1, 1, 1);
+ /** Step referenced as next step. */
+ eVelocityStep next_step_ = STEP_NEXT;
public:
VelocityModule(Instance &inst) : inst_(inst)
@@ -111,7 +105,10 @@ class VelocityModule {
void step_sync(eVelocityStep step, float time);
/* Gather motion data. Returns true if the object **can** have motion. */
- bool step_object_sync(Object *ob, ObjectKey &object_key, int recalc = 0);
+ bool step_object_sync(Object *ob,
+ ObjectKey &object_key,
+ ResourceHandle resource_handle,
+ int recalc = 0);
/* Moves next frame data to previous frame data. Nullify next frame data. */
void step_swap();
@@ -121,56 +118,29 @@ class VelocityModule {
void bind_resources(DRWShadingGroup *grp);
- private:
- bool object_has_velocity(const Object *ob);
- bool object_is_deform(const Object *ob);
-
- void resolve_camera_motion(GPUTexture *depth_tx,
- GPUTexture *velocity_view_tx,
- GPUTexture *velocity_camera_tx);
-};
+ template<typename T> void bind_resources(draw::detail::Pass<T> *pass)
+ {
+ /* Storage Buf. */
+ pass->bind_ssbo(VELOCITY_OBJ_PREV_BUF_SLOT, &(*object_steps[STEP_PREVIOUS]));
+ pass->bind_ssbo(VELOCITY_OBJ_NEXT_BUF_SLOT, &(*object_steps[next_step_]));
+ pass->bind_ssbo(VELOCITY_GEO_PREV_BUF_SLOT, &(*geometry_steps[STEP_PREVIOUS]));
+ pass->bind_ssbo(VELOCITY_GEO_NEXT_BUF_SLOT, &(*geometry_steps[next_step_]));
+ pass->bind_ssbo(VELOCITY_INDIRECTION_BUF_SLOT, &indirection_buf);
+ /* Uniform Buf. */
+ pass->bind_ubo(VELOCITY_CAMERA_PREV_BUF, &(*camera_steps[STEP_PREVIOUS]));
+ pass->bind_ubo(VELOCITY_CAMERA_CURR_BUF, &(*camera_steps[STEP_CURRENT]));
+ pass->bind_ubo(VELOCITY_CAMERA_NEXT_BUF, &(*camera_steps[next_step_]));
+ }
-/** \} */
+ bool camera_has_motion() const;
+ bool camera_changed_projection() const;
-/* -------------------------------------------------------------------- */
-/** \name Velocity
- *
- * \{ */
+ /* Returns frame time difference between two steps. */
+ float step_time_delta_get(eVelocityStep start, eVelocityStep end) const;
-/**
- * Per view module.
- */
-class VelocityView {
private:
- Instance &inst_;
-
- StringRefNull view_name_;
-
- TextureFromPool velocity_camera_tx_ = {"velocity_camera_tx_"};
- TextureFromPool velocity_view_tx_ = {"velocity_view_tx_"};
-
- public:
- VelocityView(Instance &inst, const char *name) : inst_(inst), view_name_(name){};
- ~VelocityView(){};
-
- void sync();
-
- void acquire(int2 extent);
- void release();
-
- void resolve(GPUTexture *depth_tx);
-
- /**
- * Getters
- **/
- GPUTexture *view_vectors_get() const
- {
- return velocity_view_tx_;
- }
- GPUTexture *camera_vectors_get() const
- {
- return (velocity_camera_tx_.is_valid()) ? velocity_camera_tx_ : velocity_view_tx_;
- }
+ bool object_has_velocity(const Object *ob);
+ bool object_is_deform(const Object *ob);
};
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc
index f4dba47721d..48951c2bae7 100644
--- a/source/blender/draw/engines/eevee_next/eevee_view.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_view.cc
@@ -79,15 +79,11 @@ void ShadingView::sync()
render_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p);
// dof_.sync(winmat_p, extent_);
- // mb_.sync(extent_);
- velocity_.sync();
// rt_buffer_opaque_.sync(extent_);
// rt_buffer_refract_.sync(extent_);
// inst_.hiz_back.view_sync(extent_);
// inst_.hiz_front.view_sync(extent_);
// inst_.gbuffer.view_sync(extent_);
-
- postfx_tx_.sync();
}
void ShadingView::render()
@@ -96,25 +92,25 @@ void ShadingView::render()
return;
}
- /* Query temp textures and create framebuffers. */
- /* HACK: View name should be unique and static.
- * With this, we can reuse the same texture across views. */
- DrawEngineType *owner = (DrawEngineType *)name_;
-
+ /* Query temp textures and create frame-buffers. */
RenderBuffers &rbufs = inst_.render_buffers;
- rbufs.acquire(extent_, owner);
- velocity_.acquire(extent_);
+ rbufs.acquire(extent_);
combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx));
prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx),
- GPU_ATTACHMENT_TEXTURE(velocity_.view_vectors_get()));
+ GPU_ATTACHMENT_TEXTURE(rbufs.vector_tx));
update_view();
+ inst_.hiz_buffer.set_dirty();
+
DRW_stats_group_start(name_);
DRW_view_set_active(render_view_);
- float4 clear_velocity(VELOCITY_INVALID);
+ /* If camera has any motion, compute motion vector in the film pass. Otherwise, we avoid float
+ * precision issue by setting the motion of all static geometry to 0. */
+ float4 clear_velocity = float4(inst_.velocity.camera_has_motion() ? VELOCITY_INVALID : 0.0f);
+
GPU_framebuffer_bind(prepass_fb_);
GPU_framebuffer_clear_color(prepass_fb_, clear_velocity);
/* Alpha stores transmittance. So start at 1. */
@@ -122,7 +118,10 @@ void ShadingView::render()
GPU_framebuffer_bind(combined_fb_);
GPU_framebuffer_clear_color_depth(combined_fb_, clear_color, 1.0f);
- inst_.pipelines.world.render();
+ inst_.pipelines.world.render(render_view_new_);
+
+ /* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */
+ inst_.lights.set_view(render_view_new_, extent_);
// inst_.pipelines.deferred.render(
// render_view_, rt_buffer_opaque_, rt_buffer_refract_, depth_tx_, combined_tx_);
@@ -131,43 +130,36 @@ void ShadingView::render()
// inst_.lookdev.render_overlay(view_fb_);
- inst_.pipelines.forward.render(
- render_view_, prepass_fb_, combined_fb_, rbufs.depth_tx, rbufs.combined_tx);
+ inst_.pipelines.forward.render(render_view_new_, prepass_fb_, combined_fb_, rbufs.combined_tx);
- // inst_.lights.debug_draw(view_fb_);
- // inst_.shadows.debug_draw(view_fb_);
+ inst_.lights.debug_draw(render_view_new_, combined_fb_);
+ inst_.hiz_buffer.debug_draw(render_view_new_, combined_fb_);
- velocity_.resolve(rbufs.depth_tx);
+ GPUTexture *combined_final_tx = render_postfx(rbufs.combined_tx);
- // GPUTexture *final_radiance_tx = render_post(combined_tx_);
+ inst_.film.accumulate(sub_view_, combined_final_tx);
- inst_.film.accumulate(sub_view_);
+ // inst_.shadows.debug_draw();
rbufs.release();
+ postfx_tx_.release();
DRW_stats_group_end();
-
- postfx_tx_.release();
- velocity_.release();
}
-GPUTexture *ShadingView::render_post(GPUTexture *input_tx)
+GPUTexture *ShadingView::render_postfx(GPUTexture *input_tx)
{
-#if 0
- if (!dof_.postfx_enabled() && !mb_.enabled()) {
+ if (!inst_.depth_of_field.postfx_enabled() && !inst_.motion_blur.postfx_enabled()) {
return input_tx;
}
- /* HACK: View name should be unique and static.
- * With this, we can reuse the same texture across views. */
- postfx_tx_.acquire(extent_, GPU_RGBA16F, (void *)name_);
+ postfx_tx_.acquire(extent_, GPU_RGBA16F);
- GPUTexture *velocity_tx = velocity_.view_vectors_get();
GPUTexture *output_tx = postfx_tx_;
/* Swapping is done internally. Actual output is set to the next input. */
- dof_.render(depth_tx_, &input_tx, &output_tx);
- mb_.render(depth_tx_, velocity_tx, &input_tx, &output_tx);
-#endif
+ inst_.depth_of_field.render(render_view_new_, &input_tx, &output_tx, dof_buffer_);
+ inst_.motion_blur.render(render_view_new_, &input_tx, &output_tx);
+
return input_tx;
}
@@ -184,17 +176,18 @@ void ShadingView::update_view()
/* Anti-Aliasing / Super-Sampling jitter. */
float2 jitter = inst_.film.pixel_jitter_get() / float2(extent_);
+ /* Transform to NDC space. */
+ jitter *= 2.0f;
window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(jitter));
DRW_view_update_sub(sub_view_, viewmat.ptr(), winmat.ptr());
- /* FIXME(fclem): The offset may be is noticeably large and the culling might make object pop
+ /* FIXME(fclem): The offset may be noticeably large and the culling might make object pop
* out of the blurring radius. To fix this, use custom enlarged culling matrix. */
- // dof_.jitter_apply(winmat, viewmat);
+ inst_.depth_of_field.jitter_apply(winmat, viewmat);
DRW_view_update_sub(render_view_, viewmat.ptr(), winmat.ptr());
- // inst_.lightprobes.set_view(render_view_, extent_);
- // inst_.lights.set_view(render_view_, extent_, !inst_.use_scene_lights());
+ render_view_new_.sync(viewmat, winmat);
}
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh
index 30e06df9716..74e513357cd 100644
--- a/source/blender/draw/engines/eevee_next/eevee_view.hh
+++ b/source/blender/draw/engines/eevee_next/eevee_view.hh
@@ -41,14 +41,10 @@ class ShadingView {
/** Matrix to apply to the viewmat. */
const float (*face_matrix_)[4];
- /** Post-FX modules. */
- // DepthOfField dof_;
- // MotionBlur mb_;
- VelocityView velocity_;
-
/** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */
// RaytraceBuffer rt_buffer_opaque_;
// RaytraceBuffer rt_buffer_refract_;
+ DepthOfFieldBuffer dof_buffer_;
Framebuffer prepass_fb_;
Framebuffer combined_fb_;
@@ -61,6 +57,7 @@ class ShadingView {
DRWView *sub_view_ = nullptr;
/** Same as sub_view_ but has Depth Of Field jitter applied. */
DRWView *render_view_ = nullptr;
+ View render_view_new_;
/** Render size of the view. Can change between scene sample eval. */
int2 extent_ = {-1, -1};
@@ -69,7 +66,7 @@ class ShadingView {
public:
ShadingView(Instance &inst, const char *name, const float (*face_matrix)[4])
- : inst_(inst), name_(name), face_matrix_(face_matrix), velocity_(inst, name){};
+ : inst_(inst), name_(name), face_matrix_(face_matrix), render_view_new_(name){};
~ShadingView(){};
@@ -79,7 +76,7 @@ class ShadingView {
void render();
- GPUTexture *render_post(GPUTexture *input_tx);
+ GPUTexture *render_postfx(GPUTexture *input_tx);
private:
void update_view();
diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc
index 56cb0f127db..313c0bda42e 100644
--- a/source/blender/draw/engines/eevee_next/eevee_world.cc
+++ b/source/blender/draw/engines/eevee_next/eevee_world.cc
@@ -42,10 +42,10 @@ DefaultWorldNodeTree::~DefaultWorldNodeTree()
MEM_SAFE_FREE(ntree_);
}
-/* Configure a default nodetree with the given world. */
+/* Configure a default node-tree with the given world. */
bNodeTree *DefaultWorldNodeTree::nodetree_get(::World *wo)
{
- /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
+ /* WARNING: This function is not thread-safe. Which is not a problem for the moment. */
copy_v3_fl3(color_socket_->value, wo->horr, wo->horg, wo->horb);
return ntree_;
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
index 326481a1db6..6fe5fa01fa3 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_attributes_lib.glsl
@@ -3,6 +3,8 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+#define EEVEE_ATTRIBUTE_LIB
+
#if defined(MAT_GEOM_MESH)
/* -------------------------------------------------------------------- */
@@ -282,43 +284,3 @@ vec3 attr_load_uv(vec3 attr)
/** \} */
#endif
-
-/* -------------------------------------------------------------------- */
-/** \name Volume Attribute post
- *
- * TODO(@fclem): These implementation details should concern the DRWManager and not be a fix on
- * the engine side. But as of now, the engines are responsible for loading the attributes.
- *
- * \{ */
-
-#if defined(MAT_GEOM_VOLUME)
-
-float attr_load_temperature_post(float attr)
-{
- /* Bring the into standard range without having to modify the grid values */
- attr = (attr > 0.01) ? (attr * drw_volume.temperature_mul + drw_volume.temperature_bias) : 0.0;
- return attr;
-}
-vec4 attr_load_color_post(vec4 attr)
-{
- /* Density is premultiplied for interpolation, divide it out here. */
- attr.rgb *= safe_rcp(attr.a);
- attr.rgb *= drw_volume.color_mul.rgb;
- attr.a = 1.0;
- return attr;
-}
-
-#else /* Noop for any other surface. */
-
-float attr_load_temperature_post(float attr)
-{
- return attr;
-}
-vec4 attr_load_color_post(vec4 attr)
-{
- return attr;
-}
-
-#endif
-
-/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl
index f79e9102d76..2611f714b59 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_camera_lib.glsl
@@ -143,24 +143,10 @@ vec2 camera_uv_from_view(CameraData cam, vec3 vV)
}
}
-vec2 camera_uv_from_world(CameraData cam, vec3 V)
+vec2 camera_uv_from_world(CameraData cam, vec3 P)
{
- vec3 vV = transform_point(cam.viewmat, V);
- switch (cam.type) {
- default:
- case CAMERA_ORTHO:
- return camera_uv_from_view(cam.persmat, false, V);
- case CAMERA_PERSP:
- return camera_uv_from_view(cam.persmat, true, V);
- case CAMERA_PANO_EQUIRECT:
- return camera_equirectangular_from_direction(cam, vV);
- case CAMERA_PANO_EQUISOLID:
- /* ATTR_FALLTHROUGH; */
- case CAMERA_PANO_EQUIDISTANT:
- return camera_fisheye_from_direction(cam, vV);
- case CAMERA_PANO_MIRROR:
- return camera_mirror_ball_from_direction(cam, vV);
- }
+ vec3 vV = transform_direction(cam.viewmat, normalize(P));
+ return camera_uv_from_view(cam, vV);
}
/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl
new file mode 100644
index 00000000000..d5fdaae6fc1
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl
@@ -0,0 +1,37 @@
+
+/* -------------------------------------------------------------------- */
+/** \name YCoCg
+ * \{ */
+
+vec3 colorspace_YCoCg_from_scene_linear(vec3 rgb_color)
+{
+ const mat3 colorspace_tx = transpose(mat3(vec3(1, 2, 1), /* Y */
+ vec3(2, 0, -2), /* Co */
+ vec3(-1, 2, -1))); /* Cg */
+ return colorspace_tx * rgb_color;
+}
+
+vec4 colorspace_YCoCg_from_scene_linear(vec4 rgba_color)
+{
+ return vec4(colorspace_YCoCg_from_scene_linear(rgba_color.rgb), rgba_color.a);
+}
+
+vec3 colorspace_scene_linear_from_YCoCg(vec3 ycocg_color)
+{
+ float Y = ycocg_color.x;
+ float Co = ycocg_color.y;
+ float Cg = ycocg_color.z;
+
+ vec3 rgb_color;
+ rgb_color.r = Y + Co - Cg;
+ rgb_color.g = Y + Cg;
+ rgb_color.b = Y - Co - Cg;
+ return rgb_color * 0.25;
+}
+
+vec4 colorspace_scene_linear_from_YCoCg(vec4 ycocg_color)
+{
+ return vec4(colorspace_scene_linear_from_YCoCg(ycocg_color.rgb), ycocg_color.a);
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl
new file mode 100644
index 00000000000..e874a6b56ea
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl
@@ -0,0 +1,70 @@
+/** Storing/merging and sorting cryptomatte samples. */
+
+bool cryptomatte_can_merge_sample(vec2 dst, vec2 src)
+{
+ if (dst == vec2(0.0, 0.0)) {
+ return true;
+ }
+ if (dst.x == src.x) {
+ return true;
+ }
+ return false;
+}
+
+vec2 cryptomatte_merge_sample(vec2 dst, vec2 src)
+{
+ return vec2(src.x, dst.y + src.y);
+}
+
+vec4 cryptomatte_false_color(float hash)
+{
+ uint m3hash = floatBitsToUint(hash);
+ return vec4(hash,
+ float(m3hash << 8) / float(0xFFFFFFFFu),
+ float(m3hash << 16) / float(0xFFFFFFFFu),
+ 1.0);
+}
+
+void cryptomatte_clear_samples(FilmSample dst)
+{
+ int layer_len = imageSize(cryptomatte_img).z;
+ for (int i = 0; i < layer_len; i++) {
+ imageStore(cryptomatte_img, ivec3(dst.texel, i), vec4(0.0));
+ }
+}
+
+void cryptomatte_store_film_sample(FilmSample dst,
+ int cryptomatte_layer_id,
+ vec2 crypto_sample,
+ out vec4 out_color)
+{
+ if (crypto_sample.y == 0.0) {
+ return;
+ }
+ for (int i = 0; i < film_buf.cryptomatte_samples_len / 2; i++) {
+ ivec3 img_co = ivec3(dst.texel, cryptomatte_layer_id + i);
+ vec4 sample_pair = imageLoad(cryptomatte_img, img_co);
+ if (cryptomatte_can_merge_sample(sample_pair.xy, crypto_sample)) {
+ sample_pair.xy = cryptomatte_merge_sample(sample_pair.xy, crypto_sample);
+ /* In viewport only one layer is active. */
+ /* TODO(jbakker): we are displaying the first sample, but we should display the highest
+ * weighted one. */
+ if (cryptomatte_layer_id + i == 0) {
+ out_color = cryptomatte_false_color(sample_pair.x);
+ }
+ }
+ else if (cryptomatte_can_merge_sample(sample_pair.zw, crypto_sample)) {
+ sample_pair.zw = cryptomatte_merge_sample(sample_pair.zw, crypto_sample);
+ }
+ else if (i == film_buf.cryptomatte_samples_len / 2 - 1) {
+ /* TODO(jbakker): New hash detected, but there is no space left to store it. Currently we
+ * will ignore this sample, but ideally we could replace a sample with a lowest weight. */
+ continue;
+ }
+ else {
+ continue;
+ }
+ imageStore(cryptomatte_img, img_co, sample_pair);
+ break;
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl
new file mode 100644
index 00000000000..99a47c541e9
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl
@@ -0,0 +1,680 @@
+
+/**
+ * Depth of Field Gather accumulator.
+ * We currently have only 2 which are very similar.
+ * One is for the halfres gather passes and the other one for slight in focus regions.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+/* -------------------------------------------------------------------- */
+/** \name Options.
+ * \{ */
+
+/* Quality options */
+#ifdef DOF_HOLEFILL_PASS
+/* No need for very high density for hole_fill. */
+const int gather_ring_count = 3;
+const int gather_ring_density = 3;
+const int gather_max_density_change = 0;
+const int gather_density_change_ring = 1;
+#else
+const int gather_ring_count = DOF_GATHER_RING_COUNT;
+const int gather_ring_density = 3;
+const int gather_max_density_change = 50; /* Dictates the maximum good quality blur. */
+const int gather_density_change_ring = 1;
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Constants.
+ * \{ */
+
+const float unit_ring_radius = 1.0 / float(gather_ring_count);
+const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5);
+const float large_kernel_radius = 0.5 + float(gather_ring_count);
+const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring);
+/* NOTE(fclem) the bias is reducing issues with density change visible transition. */
+const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius;
+const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1);
+const float coc_radius_error = 2.0;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Gather common.
+ * \{ */
+
+struct DofGatherData {
+ vec4 color;
+ float weight;
+ float dist; /* TODO remove */
+ /* For scatter occlusion. */
+ float coc;
+ float coc_sqr;
+ /* For ring bucket merging. */
+ float transparency;
+
+ float layer_opacity;
+};
+
+#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
+
+/* Intersection with the center of the kernel. */
+float dof_intersection_weight(float coc, float distance_from_center, float intersection_multiplier)
+{
+ if (no_smooth_intersection) {
+ return step(0.0, (abs(coc) - distance_from_center));
+ }
+ else {
+ /* (Slide 64). */
+ return saturate((abs(coc) - distance_from_center) * intersection_multiplier + 0.5);
+ }
+}
+
+/* Returns weight of the sample for the outer bucket (containing previous
+ * rings). */
+float dof_gather_accum_weight(float coc, float bordering_radius, bool first_ring)
+{
+ /* First ring has nothing to be mixed against. */
+ if (first_ring) {
+ return 0.0;
+ }
+ return saturate(coc - bordering_radius);
+}
+
+void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight)
+{
+ sample_data.color *= weight;
+ sample_data.coc *= weight;
+ sample_data.coc_sqr *= weight;
+ sample_data.weight *= weight;
+}
+
+void dof_gather_accumulate_sample(DofGatherData sample_data,
+ float weight,
+ inout DofGatherData accum_data)
+{
+ accum_data.color += sample_data.color * weight;
+ accum_data.coc += sample_data.coc * weight;
+ accum_data.coc_sqr += sample_data.coc * (sample_data.coc * weight);
+ accum_data.weight += weight;
+}
+
+void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2],
+ float bordering_radius,
+ float intersection_multiplier,
+ bool first_ring,
+ const bool do_fast_gather,
+ const bool is_foreground,
+ inout DofGatherData ring_data,
+ inout DofGatherData accum_data)
+{
+ if (do_fast_gather) {
+ for (int i = 0; i < 2; i++) {
+ dof_gather_accumulate_sample(pair_data[i], 1.0, accum_data);
+ accum_data.layer_opacity += 1.0;
+ }
+ return;
+ }
+
+#if 0
+ const float mirroring_threshold = -dof_layer_threshold - dof_layer_offset;
+ /* TODO(fclem) Promote to parameter? dither with Noise? */
+ const float mirroring_min_distance = 15.0;
+ if (pair_data[0].coc < mirroring_threshold &&
+ (pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) {
+ pair_data[1].coc = pair_data[0].coc;
+ }
+ else if (pair_data[1].coc < mirroring_threshold &&
+ (pair_data[0].coc - mirroring_min_distance) > pair_data[1].coc) {
+ pair_data[0].coc = pair_data[1].coc;
+ }
+#endif
+
+ for (int i = 0; i < 2; i++) {
+ float sample_weight = dof_sample_weight(pair_data[i].coc);
+ float layer_weight = dof_layer_weight(pair_data[i].coc, is_foreground);
+ float inter_weight = dof_intersection_weight(
+ pair_data[i].coc, pair_data[i].dist, intersection_multiplier);
+ float weight = inter_weight * layer_weight * sample_weight;
+
+ /**
+ * If a CoC is larger than bordering radius we accumulate it to the general accumulator.
+ * If not, we accumulate to the ring bucket. This is to have more consistent sample occlusion.
+ **/
+ float accum_weight = dof_gather_accum_weight(pair_data[i].coc, bordering_radius, first_ring);
+ dof_gather_accumulate_sample(pair_data[i], weight * accum_weight, accum_data);
+ dof_gather_accumulate_sample(pair_data[i], weight * (1.0 - accum_weight), ring_data);
+
+ accum_data.layer_opacity += layer_weight;
+
+ if (is_foreground) {
+ ring_data.transparency += 1.0 - inter_weight * layer_weight;
+ }
+ else {
+ float coc = is_foreground ? -pair_data[i].coc : pair_data[i].coc;
+ ring_data.transparency += saturate(coc - bordering_radius);
+ }
+ }
+}
+
+void dof_gather_accumulate_sample_ring(DofGatherData ring_data,
+ int sample_count,
+ bool first_ring,
+ const bool do_fast_gather,
+ /* accum_data occludes the ring_data if true. */
+ const bool reversed_occlusion,
+ inout DofGatherData accum_data)
+{
+ if (do_fast_gather) {
+ /* Do nothing as ring_data contains nothing. All samples are already in
+ * accum_data. */
+ return;
+ }
+
+ if (first_ring) {
+ /* Layer opacity is directly accumulated into accum_data data. */
+ accum_data.color = ring_data.color;
+ accum_data.coc = ring_data.coc;
+ accum_data.coc_sqr = ring_data.coc_sqr;
+ accum_data.weight = ring_data.weight;
+
+ accum_data.transparency = ring_data.transparency / float(sample_count);
+ return;
+ }
+
+ if (ring_data.weight == 0.0) {
+ return;
+ }
+
+ float ring_avg_coc = ring_data.coc / ring_data.weight;
+ float accum_avg_coc = accum_data.coc / accum_data.weight;
+
+ /* Smooth test to set opacity to see if the ring average coc occludes the
+ * accumulation. Test is reversed to be multiplied against opacity. */
+ float ring_occlu = saturate(accum_avg_coc - ring_avg_coc);
+ /* The bias here is arbitrary. Seems to avoid weird looking foreground in most
+ * cases. We might need to make it a parameter or find a relative bias. */
+ float accum_occlu = saturate((ring_avg_coc - accum_avg_coc) * 0.1 - 1.0);
+
+ if (is_resolve) {
+ ring_occlu = accum_occlu = 0.0;
+ }
+
+ if (no_gather_occlusion) {
+ ring_occlu = 0.0;
+ accum_occlu = 0.0;
+ }
+
+ /* (Slide 40) */
+ float ring_opacity = saturate(1.0 - ring_data.transparency / float(sample_count));
+ float accum_opacity = 1.0 - accum_data.transparency;
+
+ if (reversed_occlusion) {
+ /* Accum_data occludes the ring. */
+ float alpha = (accum_data.weight == 0.0) ? 0.0 : accum_opacity * accum_occlu;
+ float one_minus_alpha = 1.0 - alpha;
+
+ accum_data.color += ring_data.color * one_minus_alpha;
+ accum_data.coc += ring_data.coc * one_minus_alpha;
+ accum_data.coc_sqr += ring_data.coc_sqr * one_minus_alpha;
+ accum_data.weight += ring_data.weight * one_minus_alpha;
+
+ accum_data.transparency *= 1.0 - ring_opacity;
+ }
+ else {
+ /* Ring occludes the accum_data (Same as reference). */
+ float alpha = (accum_data.weight == 0.0) ? 1.0 : (ring_opacity * ring_occlu);
+ float one_minus_alpha = 1.0 - alpha;
+
+ accum_data.color = accum_data.color * one_minus_alpha + ring_data.color;
+ accum_data.coc = accum_data.coc * one_minus_alpha + ring_data.coc;
+ accum_data.coc_sqr = accum_data.coc_sqr * one_minus_alpha + ring_data.coc_sqr;
+ accum_data.weight = accum_data.weight * one_minus_alpha + ring_data.weight;
+ }
+}
+
+/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for
+ * slightfocus gather. */
+/* This should be replaced by web_sample_count_get() but doing so is breaking other things. */
+int dof_gather_total_sample_count(const int ring_count, const int ring_density)
+{
+ return (ring_count * ring_count - ring_count) * ring_density + 1;
+}
+
+void dof_gather_accumulate_center_sample(DofGatherData center_data,
+ float bordering_radius,
+ int i_radius,
+ const bool do_fast_gather,
+ const bool is_foreground,
+ const bool is_resolve,
+ inout DofGatherData accum_data)
+{
+ float layer_weight = dof_layer_weight(center_data.coc, is_foreground);
+ float sample_weight = dof_sample_weight(center_data.coc);
+ float weight = layer_weight * sample_weight;
+ float accum_weight = dof_gather_accum_weight(center_data.coc, bordering_radius, false);
+
+ if (do_fast_gather) {
+ /* Hope for the compiler to optimize the above. */
+ layer_weight = 1.0;
+ sample_weight = 1.0;
+ accum_weight = 1.0;
+ weight = 1.0;
+ }
+
+ center_data.transparency = 1.0 - weight;
+
+ dof_gather_accumulate_sample(center_data, weight * accum_weight, accum_data);
+
+ if (!do_fast_gather) {
+ if (is_resolve) {
+ /* NOTE(fclem): Hack to smooth transition to full in-focus opacity. */
+ int total_sample_count = dof_gather_total_sample_count(i_radius + 1,
+ DOF_SLIGHT_FOCUS_DENSITY);
+ float fac = saturate(1.0 - abs(center_data.coc) / float(dof_layer_threshold));
+ accum_data.layer_opacity += float(total_sample_count) * fac * fac;
+ }
+ accum_data.layer_opacity += layer_weight;
+
+ /* Logic of dof_gather_accumulate_sample(). */
+ weight *= (1.0 - accum_weight);
+ center_data.coc_sqr = center_data.coc * (center_data.coc * weight);
+ center_data.color *= weight;
+ center_data.coc *= weight;
+ center_data.weight = weight;
+
+ if (is_foreground && !is_resolve) {
+ /* Reduce issue with closer foreground over distant foreground. */
+ float ring_area = sqr(bordering_radius);
+ dof_gather_ammend_weight(center_data, ring_area);
+ }
+
+ /* Accumulate center as its own ring. */
+ dof_gather_accumulate_sample_ring(
+ center_data, 1, false, do_fast_gather, is_foreground, accum_data);
+ }
+}
+
+int dof_gather_total_sample_count_with_density_change(const int ring_count,
+ const int ring_density,
+ int density_change)
+{
+ int sample_count_per_density_change = dof_gather_total_sample_count(ring_count, ring_density) -
+ dof_gather_total_sample_count(
+ ring_count - gather_density_change_ring, ring_density);
+
+ return dof_gather_total_sample_count(ring_count, ring_density) +
+ sample_count_per_density_change * density_change;
+}
+
+void dof_gather_accumulate_resolve(int total_sample_count,
+ DofGatherData accum_data,
+ out vec4 out_col,
+ out float out_weight,
+ out vec2 out_occlusion)
+{
+ float weight_inv = safe_rcp(accum_data.weight);
+ out_col = accum_data.color * weight_inv;
+ out_occlusion = vec2(abs(accum_data.coc), accum_data.coc_sqr) * weight_inv;
+
+ if (is_foreground) {
+ out_weight = 1.0 - accum_data.transparency;
+ }
+ else if (accum_data.weight > 0.0) {
+ out_weight = accum_data.layer_opacity / float(total_sample_count);
+ }
+ else {
+ out_weight = 0.0;
+ }
+ /* Gathering may not accumulate to 1.0 alpha because of float precision. */
+ if (out_weight > 0.99) {
+ out_weight = 1.0;
+ }
+ else if (out_weight < 0.01) {
+ out_weight = 0.0;
+ }
+ /* Same thing for alpha channel. */
+ if (out_col.a > 0.993) {
+ out_col.a = 1.0;
+ }
+ else if (out_col.a < 0.003) {
+ out_col.a = 0.0;
+ }
+}
+
+float dof_load_gather_coc(sampler2D gather_input_coc_tx, vec2 uv, float lod)
+{
+ float coc = textureLod(gather_input_coc_tx, uv, lod).r;
+ /* We gather at halfres. CoC must be divided by 2 to be compared against radii. */
+ return coc * 0.5;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Common Gather accumulator.
+ * \{ */
+
+/* Radii needs to be halfres CoC sizes. */
+bool dof_do_density_change(float base_radius, float min_intersectable_radius)
+{
+ /* Reduce artifact for very large blur. */
+ min_intersectable_radius *= 0.1;
+
+ bool need_new_density = (base_radius * unit_ring_radius > min_intersectable_radius);
+ bool larger_than_min_density = (base_radius * radius_downscale_factor >
+ float(gather_ring_count));
+
+ return need_new_density && larger_than_min_density;
+}
+
+void dof_gather_init(float base_radius,
+ vec2 noise,
+ out vec2 center_co,
+ out float lod,
+ out float intersection_multiplier)
+{
+ /* Jitter center half a ring to reduce undersampling. */
+ vec2 jitter_ofs = 0.499 * sample_disk(noise);
+ if (DOF_BOKEH_TEXTURE) {
+ jitter_ofs *= dof_buf.bokeh_anisotropic_scale;
+ }
+ vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5;
+ center_co = frag_coord + jitter_ofs * base_radius * unit_sample_radius;
+
+ /* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving out
+ * of focus shapes. */
+ const float lod_bias = -2.0;
+ lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0);
+
+ if (no_gather_mipmaps) {
+ lod = 0.0;
+ }
+ /* (Slide 64). */
+ intersection_multiplier = pow(0.5, lod);
+}
+
+void dof_gather_accumulator(sampler2D color_tx,
+ sampler2D color_bilinear_tx,
+ sampler2D coc_tx,
+ sampler2D bkh_lut_tx, /* Renamed because of ugly macro. */
+ float base_radius,
+ float min_intersectable_radius,
+ const bool do_fast_gather,
+ const bool do_density_change,
+ out vec4 out_color,
+ out float out_weight,
+ out vec2 out_occlusion)
+{
+ vec2 frag_coord = vec2(gl_GlobalInvocationID.xy);
+ vec2 noise_offset = sampling_rng_2D_get(SAMPLING_LENS_U);
+ vec2 noise = no_gather_random ? vec2(0.0, 0.0) :
+ vec2(interlieved_gradient_noise(frag_coord, 0, noise_offset.x),
+ interlieved_gradient_noise(frag_coord, 1, noise_offset.y));
+
+ if (!do_fast_gather) {
+ /* Jitter the radius to reduce noticeable density changes. */
+ base_radius += noise.x * unit_ring_radius * base_radius;
+ }
+ else {
+ /* Jittering the radius more than we need means we are going to feather the bokeh shape half a
+ * ring. So we need to compensate for fast gather that does not check CoC intersection. */
+ base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius;
+ }
+ /* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */
+ noise.x = fract(noise.x * 6.1803398875);
+
+ float lod, isect_mul;
+ vec2 center_co;
+ dof_gather_init(base_radius, noise, center_co, lod, isect_mul);
+
+ bool first_ring = true;
+
+ DofGatherData accum_data = GATHER_DATA_INIT;
+
+ int density_change = 0;
+ for (int ring = gather_ring_count; ring > 0; ring--) {
+ int sample_pair_count = gather_ring_density * ring;
+
+ float step_rot = M_PI / float(sample_pair_count);
+ mat2 step_rot_mat = rot2_from_angle(step_rot);
+
+ float angle_offset = noise.y * step_rot;
+ vec2 offset = vec2(cos(angle_offset), sin(angle_offset));
+
+ float ring_radius = float(ring) * unit_sample_radius * base_radius;
+
+ /* Slide 38. */
+ float bordering_radius = ring_radius +
+ (0.5 + coc_radius_error) * base_radius * unit_sample_radius;
+ DofGatherData ring_data = GATHER_DATA_INIT;
+ for (int sample_pair = 0; sample_pair < sample_pair_count; sample_pair++) {
+ offset = step_rot_mat * offset;
+
+ DofGatherData pair_data[2];
+ for (int i = 0; i < 2; i++) {
+ vec2 offset_co = ((i == 0) ? offset : -offset);
+ if (DOF_BOKEH_TEXTURE) {
+ /* Scaling to 0.25 for speed. Improves texture cache hit. */
+ offset_co = texture(bkh_lut_tx, offset_co * 0.25 + 0.5).rg;
+ offset_co *= (is_foreground) ? -dof_buf.bokeh_anisotropic_scale :
+ dof_buf.bokeh_anisotropic_scale;
+ }
+ vec2 sample_co = center_co + offset_co * ring_radius;
+ vec2 sample_uv = sample_co * dof_buf.gather_uv_fac;
+ if (do_fast_gather) {
+ pair_data[i].color = textureLod(color_bilinear_tx, sample_uv, lod);
+ }
+ else {
+ pair_data[i].color = textureLod(color_tx, sample_uv, lod);
+ }
+ pair_data[i].coc = dof_load_gather_coc(coc_tx, sample_uv, lod);
+ pair_data[i].dist = ring_radius;
+ }
+
+ dof_gather_accumulate_sample_pair(pair_data,
+ bordering_radius,
+ isect_mul,
+ first_ring,
+ do_fast_gather,
+ is_foreground,
+ ring_data,
+ accum_data);
+ }
+
+ if (is_foreground) {
+ /* Reduce issue with closer foreground over distant foreground. */
+ /* TODO(fclem) this seems to not be completely correct as the issue remains. */
+ float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) -
+ sqr(float(ring) - 0.5 + coc_radius_error)) *
+ sqr(base_radius * unit_sample_radius);
+ dof_gather_ammend_weight(ring_data, ring_area);
+ }
+
+ dof_gather_accumulate_sample_ring(
+ ring_data, sample_pair_count * 2, first_ring, do_fast_gather, is_foreground, accum_data);
+
+ first_ring = false;
+
+ if (do_density_change && (ring == change_density_at_ring) &&
+ (density_change < gather_max_density_change)) {
+ if (dof_do_density_change(base_radius, min_intersectable_radius)) {
+ base_radius *= radius_downscale_factor;
+ ring += gather_density_change_ring;
+ /* We need to account for the density change in the weights (slide 62).
+ * For that multiply old kernel data by its area divided by the new kernel area. */
+ const float outer_rings_weight = 1.0 / (radius_downscale_factor * radius_downscale_factor);
+ /* Samples are already weighted per ring in foreground pass. */
+ if (!is_foreground) {
+ dof_gather_ammend_weight(accum_data, outer_rings_weight);
+ }
+ /* Re-init kernel position & sampling parameters. */
+ dof_gather_init(base_radius, noise, center_co, lod, isect_mul);
+ density_change++;
+ }
+ }
+ }
+
+ {
+ /* Center sample. */
+ vec2 sample_uv = center_co * dof_buf.gather_uv_fac;
+ DofGatherData center_data;
+ if (do_fast_gather) {
+ center_data.color = textureLod(color_bilinear_tx, sample_uv, lod);
+ }
+ else {
+ center_data.color = textureLod(color_tx, sample_uv, lod);
+ }
+ center_data.coc = dof_load_gather_coc(coc_tx, sample_uv, lod);
+ center_data.dist = 0.0;
+
+ /* Slide 38. */
+ float bordering_radius = (0.5 + coc_radius_error) * base_radius * unit_sample_radius;
+
+ dof_gather_accumulate_center_sample(
+ center_data, bordering_radius, 0, do_fast_gather, is_foreground, false, accum_data);
+ }
+
+ int total_sample_count = dof_gather_total_sample_count_with_density_change(
+ gather_ring_count, gather_ring_density, density_change);
+ dof_gather_accumulate_resolve(
+ total_sample_count, accum_data, out_color, out_weight, out_occlusion);
+
+ if (debug_gather_perf && density_change > 0) {
+ float fac = saturate(float(density_change) / float(10.0));
+ out_color.rgb = avg(out_color.rgb) * neon_gradient(fac);
+ }
+ if (debug_gather_perf && do_fast_gather) {
+ out_color.rgb = avg(out_color.rgb) * vec3(0.0, 1.0, 0.0);
+ }
+ if (debug_scatter_perf) {
+ out_color.rgb = avg(out_color.rgb) * vec3(0.0, 1.0, 0.0);
+ }
+
+ /* Output premultiplied color so we can use bilinear sampler in resolve pass. */
+ out_color *= out_weight;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Slight focus accumulator.
+ *
+ * The full pixel neighborhood is gathered.
+ * \{ */
+
+void dof_slight_focus_gather(sampler2D depth_tx,
+ sampler2D color_tx,
+ sampler2D bkh_lut_tx, /* Renamed because of ugly macro job. */
+ float radius,
+ out vec4 out_color,
+ out float out_weight,
+ out float out_center_coc)
+{
+ vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5;
+ vec2 noise_offset = sampling_rng_2D_get(SAMPLING_LENS_U);
+ vec2 noise = no_gather_random ? vec2(0.0) :
+ vec2(interlieved_gradient_noise(frag_coord, 3, noise_offset.x),
+ interlieved_gradient_noise(frag_coord, 5, noise_offset.y));
+
+ DofGatherData fg_accum = GATHER_DATA_INIT;
+ DofGatherData bg_accum = GATHER_DATA_INIT;
+
+ int i_radius = clamp(int(radius), 0, int(dof_layer_threshold));
+
+ const float sample_count_max = float(DOF_SLIGHT_FOCUS_SAMPLE_MAX);
+ /* Scale by search area. */
+ float sample_count = sample_count_max * saturate(sqr(radius) / sqr(dof_layer_threshold));
+
+ bool first_ring = true;
+
+ for (float s = 0.0; s < sample_count; s++) {
+ vec2 rand2 = fract(hammersley_2d(s, sample_count) + noise);
+ vec2 offset = sample_disk(rand2);
+ float ring_dist = sqrt(rand2.y);
+
+ DofGatherData pair_data[2];
+ for (int i = 0; i < 2; i++) {
+ vec2 sample_offset = ((i == 0) ? offset : -offset);
+ /* OPTI: could precompute the factor. */
+ vec2 sample_uv = (frag_coord + sample_offset) / vec2(textureSize(depth_tx, 0));
+ float depth = textureLod(depth_tx, sample_uv, 0.0).r;
+ pair_data[i].coc = dof_coc_from_depth(dof_buf, sample_uv, depth);
+ pair_data[i].color = safe_color(textureLod(color_tx, sample_uv, 0.0));
+ pair_data[i].dist = ring_dist;
+ if (DOF_BOKEH_TEXTURE) {
+ /* Contains subpixel distance to bokeh shape. */
+ ivec2 lut_texel = ivec2(round(sample_offset)) + dof_max_slight_focus_radius;
+ pair_data[i].dist = texelFetch(bkh_lut_tx, lut_texel, 0).r;
+ }
+ pair_data[i].coc = clamp(pair_data[i].coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max);
+ }
+
+ float bordering_radius = ring_dist + 0.5;
+ const float isect_mul = 1.0;
+ DofGatherData bg_ring = GATHER_DATA_INIT;
+ dof_gather_accumulate_sample_pair(
+ pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum);
+ /* Treat each sample as a ring. */
+ dof_gather_accumulate_sample_ring(bg_ring, 2, first_ring, false, false, bg_accum);
+
+ if (DOF_BOKEH_TEXTURE) {
+ /* Swap distances in order to flip bokeh shape for foreground. */
+ float tmp = pair_data[0].dist;
+ pair_data[0].dist = pair_data[1].dist;
+ pair_data[1].dist = tmp;
+ }
+ DofGatherData fg_ring = GATHER_DATA_INIT;
+ dof_gather_accumulate_sample_pair(
+ pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum);
+ /* Treat each sample as a ring. */
+ dof_gather_accumulate_sample_ring(fg_ring, 2, first_ring, false, true, fg_accum);
+
+ first_ring = false;
+ }
+
+ /* Center sample. */
+ vec2 sample_uv = frag_coord / vec2(textureSize(depth_tx, 0));
+ DofGatherData center_data;
+ center_data.color = safe_color(textureLod(color_tx, sample_uv, 0.0));
+ center_data.coc = dof_coc_from_depth(dof_buf, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r);
+ center_data.coc = clamp(center_data.coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max);
+ center_data.dist = 0.0;
+
+ out_center_coc = center_data.coc;
+
+ /* Slide 38. */
+ float bordering_radius = 0.5;
+
+ dof_gather_accumulate_center_sample(
+ center_data, bordering_radius, i_radius, false, true, true, fg_accum);
+ dof_gather_accumulate_center_sample(
+ center_data, bordering_radius, i_radius, false, false, true, bg_accum);
+
+ vec4 bg_col, fg_col;
+ float bg_weight, fg_weight;
+ vec2 unused_occlusion;
+
+ int total_sample_count = int(sample_count) * 2 + 1;
+ dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion);
+ dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion);
+
+ /* Fix weighting issues on perfectly focus to slight focus transitioning areas. */
+ if (abs(center_data.coc) < 0.5) {
+ bg_col = center_data.color;
+ bg_weight = 1.0;
+ }
+
+ /* Alpha Over */
+ float alpha = 1.0 - fg_weight;
+ out_weight = bg_weight * alpha + fg_weight;
+ out_color = bg_col * bg_weight * alpha + fg_col * fg_weight;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl
new file mode 100644
index 00000000000..26a597b04e8
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl
@@ -0,0 +1,55 @@
+
+/**
+ * Bokeh Look Up Table: This outputs a radius multiplier to shape the sampling in gather pass or
+ * the scatter sprite appearance. This is only used if bokeh shape is either anamorphic or is not
+ * a perfect circle.
+ * We correct samples spacing for polygonal bokeh shapes. However, we do not for anamorphic bokeh
+ * as it is way more complex and expensive to do.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+void main()
+{
+ vec2 gather_uv = ((vec2(gl_GlobalInvocationID.xy) + 0.5) / float(DOF_BOKEH_LUT_SIZE));
+ /* Center uv in range [-1..1]. */
+ gather_uv = gather_uv * 2.0 - 1.0;
+
+ vec2 slight_focus_texel = vec2(gl_GlobalInvocationID.xy) - float(dof_max_slight_focus_radius);
+
+ float radius = length(gather_uv);
+
+ if (dof_buf.bokeh_blades > 0.0) {
+ /* NOTE: atan(y,x) has output range [-M_PI..M_PI], so add 2pi to avoid negative angles. */
+ float theta = atan(gather_uv.y, gather_uv.x) + M_2PI;
+ float r = length(gather_uv);
+
+ radius /= circle_to_polygon_radius(dof_buf.bokeh_blades, theta - dof_buf.bokeh_rotation);
+
+ float theta_new = circle_to_polygon_angle(dof_buf.bokeh_blades, theta);
+ float r_new = circle_to_polygon_radius(dof_buf.bokeh_blades, theta_new);
+
+ theta_new -= dof_buf.bokeh_rotation;
+
+ gather_uv = r_new * vec2(-cos(theta_new), sin(theta_new));
+
+ {
+ /* Slight focus distance */
+ slight_focus_texel *= dof_buf.bokeh_anisotropic_scale_inv;
+ float theta = atan(slight_focus_texel.y, -slight_focus_texel.x) + M_2PI;
+ slight_focus_texel /= circle_to_polygon_radius(dof_buf.bokeh_blades,
+ theta + dof_buf.bokeh_rotation);
+ }
+ }
+ else {
+ gather_uv *= safe_rcp(length(gather_uv));
+ }
+
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ /* For gather store the normalized UV. */
+ imageStore(out_gather_lut_img, texel, gather_uv.xyxy);
+ /* For scatter store distance. LUT will be scaled by COC. */
+ imageStore(out_scatter_lut_img, texel, vec4(radius));
+ /* For slight focus gather store pixel perfect distance. */
+ imageStore(out_resolve_lut_img, texel, vec4(length(slight_focus_texel)));
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl
new file mode 100644
index 00000000000..3d45f285da9
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl
@@ -0,0 +1,32 @@
+
+/**
+ * Downsample pass: CoC aware downsample to quarter resolution.
+ *
+ * Pretty much identical to the setup pass but get CoC from buffer.
+ * Also does not weight luma for the bilateral weights.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+void main()
+{
+ vec2 halfres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy);
+ /* Center uv around the 4 halfres pixels. */
+ vec2 quad_center = vec2(gl_GlobalInvocationID * 2 + 1) * halfres_texel_size;
+
+ vec4 colors[4];
+ vec4 cocs;
+ for (int i = 0; i < 4; i++) {
+ vec2 sample_uv = quad_center + quad_offsets[i] * halfres_texel_size;
+ colors[i] = textureLod(color_tx, sample_uv, 0.0);
+ cocs[i] = textureLod(coc_tx, sample_uv, 0.0).r;
+ }
+
+ vec4 weights = dof_bilateral_coc_weights(cocs);
+ /* Normalize so that the sum is 1. */
+ weights *= safe_rcp(sum(weights));
+
+ vec4 out_color = weighted_sum_array(colors, weights);
+
+ imageStore(out_color_img, ivec2(gl_GlobalInvocationID.xy), out_color);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl
new file mode 100644
index 00000000000..49c93ca63cd
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl
@@ -0,0 +1,163 @@
+
+/**
+ * Gather Filter pass: Filter the gather pass result to reduce noise.
+ *
+ * This is a simple 3x3 median filter to avoid dilating highlights with a 3x3 max filter even if
+ * cheaper.
+ */
+
+struct FilterSample {
+ vec4 color;
+ float weight;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name Pixel cache.
+ * \{ */
+
+const uint cache_size = gl_WorkGroupSize.x + 2;
+shared vec4 color_cache[cache_size][cache_size];
+shared float weight_cache[cache_size][cache_size];
+
+void cache_init()
+{
+ /**
+ * Load enough values into LDS to perform the filter.
+ *
+ * ┌──────────────────────────────┐
+ * │ │ < Border texels that needs to be loaded.
+ * │ x x x x x x x x │ ─┐
+ * │ x x x x x x x x │ │
+ * │ x x x x x x x x │ │
+ * │ x x x x x x x x │ │ Thread Group Size 8x8.
+ * │ L L L L L x x x x │ │
+ * │ L L L L L x x x x │ │
+ * │ L L L L L x x x x │ │
+ * │ L L L L L x x x x │ ─┘
+ * │ L L L L L │ < Border texels that needs to be loaded.
+ * └──────────────────────────────┘
+ * └───────────┘
+ * Load using 5x5 threads.
+ */
+
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy) - 1;
+ if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_size / 2u)))) {
+ for (int y = 0; y < 2; y++) {
+ for (int x = 0; x < 2; x++) {
+ ivec2 offset = ivec2(x, y) * ivec2(cache_size / 2u);
+ ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset;
+ ivec2 load_texel = clamp(texel + offset, ivec2(0), textureSize(color_tx, 0) - 1);
+
+ color_cache[cache_texel.y][cache_texel.x] = texelFetch(color_tx, load_texel, 0);
+ weight_cache[cache_texel.y][cache_texel.x] = texelFetch(weight_tx, load_texel, 0).r;
+ }
+ }
+ }
+ barrier();
+}
+
+FilterSample cache_sample(int x, int y)
+{
+ return FilterSample(color_cache[y][x], weight_cache[y][x]);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Median filter
+ * From:
+ * Implementing Median Filters in XC4000E FPGAs
+ * JOHN L. SMITH, Univision Technologies Inc., Billerica, MA
+ * http://users.utcluj.ro/~baruch/resources/Image/xl23_16.pdf
+ * Figure 1
+ * \{ */
+
+FilterSample filter_min(FilterSample a, FilterSample b)
+{
+ return FilterSample(min(a.color, b.color), min(a.weight, b.weight));
+}
+
+FilterSample filter_max(FilterSample a, FilterSample b)
+{
+ return FilterSample(max(a.color, b.color), max(a.weight, b.weight));
+}
+
+FilterSample filter_min(FilterSample a, FilterSample b, FilterSample c)
+{
+ return FilterSample(min(a.color, min(c.color, b.color)), min(a.weight, min(c.weight, b.weight)));
+}
+
+FilterSample filter_max(FilterSample a, FilterSample b, FilterSample c)
+{
+ return FilterSample(max(a.color, max(c.color, b.color)), max(a.weight, max(c.weight, b.weight)));
+}
+
+FilterSample filter_median(FilterSample s1, FilterSample s2, FilterSample s3)
+{
+ /* From diagram, with nodes numbered from top to bottom. */
+ FilterSample l1 = filter_min(s2, s3);
+ FilterSample h1 = filter_max(s2, s3);
+ FilterSample h2 = filter_max(s1, l1);
+ FilterSample l3 = filter_min(h2, h1);
+ return l3;
+}
+
+struct FilterLmhResult {
+ FilterSample low;
+ FilterSample median;
+ FilterSample high;
+};
+
+FilterLmhResult filter_lmh(FilterSample s1, FilterSample s2, FilterSample s3)
+{
+ /* From diagram, with nodes numbered from top to bottom. */
+ FilterSample h1 = filter_max(s2, s3);
+ FilterSample l1 = filter_min(s2, s3);
+
+ FilterSample h2 = filter_max(s1, l1);
+ FilterSample l2 = filter_min(s1, l1);
+
+ FilterSample h3 = filter_max(h2, h1);
+ FilterSample l3 = filter_min(h2, h1);
+
+ FilterLmhResult result;
+ result.low = l2;
+ result.median = l3;
+ result.high = h3;
+
+ return result;
+}
+
+/** \} */
+
+void main()
+{
+ /**
+ * NOTE: We can **NOT** optimize by discarding some tiles as the result is sampled using bilinear
+ * filtering in the resolve pass. Not outputting to a tile means that border texels have
+ * undefined value and tile border will be noticeable in the final image.
+ */
+
+ cache_init();
+
+ ivec2 texel = ivec2(gl_LocalInvocationID.xy);
+
+ FilterLmhResult rows[3];
+ for (int y = 0; y < 3; y++) {
+ rows[y] = filter_lmh(cache_sample(texel.x + 0, texel.y + y),
+ cache_sample(texel.x + 1, texel.y + y),
+ cache_sample(texel.x + 2, texel.y + y));
+ }
+ /* Left nodes. */
+ FilterSample high = filter_max(rows[0].low, rows[1].low, rows[2].low);
+ /* Right nodes. */
+ FilterSample low = filter_min(rows[0].high, rows[1].high, rows[2].high);
+ /* Center nodes. */
+ FilterSample median = filter_median(rows[0].median, rows[1].median, rows[2].median);
+ /* Last bottom nodes. */
+ median = filter_median(low, median, high);
+
+ ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy);
+ imageStore(out_color_img, out_texel, median.color);
+ imageStore(out_weight_img, out_texel, vec4(median.weight));
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl
new file mode 100644
index 00000000000..cf8dd7a36e6
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl
@@ -0,0 +1,99 @@
+
+/**
+ * Gather pass: Convolve foreground and background parts in separate passes.
+ *
+ * Using the min&max CoC tile buffer, we select the best appropriate method to blur the scene
+ *color. A fast gather path is taken if there is not many CoC variation inside the tile.
+ *
+ * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring
+ * rotation to ensure maximum coverage.
+ *
+ * Outputs:
+ * - Color * Weight, Weight, Occlusion 'CoC' Depth (mean and variance)
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl)
+
+void main()
+{
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy / DOF_TILES_SIZE);
+ CocTile coc_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, tile_co);
+ CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
+
+ float base_radius, min_radius, min_intersectable_radius;
+ bool can_early_out;
+ if (is_foreground) {
+ base_radius = -coc_tile.fg_min_coc;
+ min_radius = -coc_tile.fg_max_coc;
+ min_intersectable_radius = -coc_tile.fg_max_intersectable_coc;
+ can_early_out = !prediction.do_foreground;
+ }
+ else {
+ base_radius = coc_tile.bg_max_coc;
+ min_radius = coc_tile.bg_min_coc;
+ min_intersectable_radius = coc_tile.bg_min_intersectable_coc;
+ can_early_out = !prediction.do_background;
+ }
+
+ bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground);
+
+ /* Gather at half resolution. Divide CoC by 2. */
+ base_radius *= 0.5;
+ min_intersectable_radius *= 0.5;
+
+ bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius);
+
+ vec4 out_color;
+ float out_weight;
+ vec2 out_occlusion;
+
+ if (can_early_out) {
+ out_color = vec4(0.0);
+ out_weight = 0.0;
+ out_occlusion = vec2(0.0, 0.0);
+ }
+ else if (do_fast_gather) {
+ dof_gather_accumulator(color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ bokeh_lut_tx,
+ base_radius,
+ min_intersectable_radius,
+ true,
+ false,
+ out_color,
+ out_weight,
+ out_occlusion);
+ }
+ else if (do_density_change) {
+ dof_gather_accumulator(color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ bokeh_lut_tx,
+ base_radius,
+ min_intersectable_radius,
+ false,
+ true,
+ out_color,
+ out_weight,
+ out_occlusion);
+ }
+ else {
+ dof_gather_accumulator(color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ bokeh_lut_tx,
+ base_radius,
+ min_intersectable_radius,
+ false,
+ false,
+ out_color,
+ out_weight,
+ out_occlusion);
+ }
+
+ ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy);
+ imageStore(out_color_img, out_texel, out_color);
+ imageStore(out_weight_img, out_texel, vec4(out_weight));
+ imageStore(out_occlusion_img, out_texel, out_occlusion.xyxy);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl
new file mode 100644
index 00000000000..5cdabbc2d4b
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl
@@ -0,0 +1,70 @@
+
+/**
+ * Holefill pass: Gather background parts where foreground is present.
+ *
+ * Using the min&max CoC tile buffer, we select the best appropriate method to blur the scene
+ *color. A fast gather path is taken if there is not many CoC variation inside the tile.
+ *
+ * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring
+ * rotation to ensure maximum coverage.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl)
+
+void main()
+{
+ ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy / DOF_TILES_SIZE);
+ CocTile coc_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, tile_co);
+ CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
+
+ float base_radius = -coc_tile.fg_min_coc;
+ float min_radius = -coc_tile.fg_max_coc;
+ float min_intersectable_radius = dof_tile_large_coc;
+ bool can_early_out = !prediction.do_hole_fill;
+
+ bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground);
+
+ /* Gather at half resolution. Divide CoC by 2. */
+ base_radius *= 0.5;
+ min_intersectable_radius *= 0.5;
+
+ bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius);
+
+ vec4 out_color = vec4(0.0);
+ float out_weight = 0.0;
+ vec2 unused_occlusion = vec2(0.0, 0.0);
+
+ if (can_early_out) {
+ /* Early out. */
+ }
+ else if (do_fast_gather) {
+ dof_gather_accumulator(color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ coc_tx,
+ base_radius,
+ min_intersectable_radius,
+ true,
+ false,
+ out_color,
+ out_weight,
+ unused_occlusion);
+ }
+ else {
+ dof_gather_accumulator(color_tx,
+ color_bilinear_tx,
+ coc_tx,
+ coc_tx,
+ base_radius,
+ min_intersectable_radius,
+ false,
+ false,
+ out_color,
+ out_weight,
+ unused_occlusion);
+ }
+
+ ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy);
+ imageStore(out_color_img, out_texel, out_color);
+ imageStore(out_weight_img, out_texel, vec4(out_weight));
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl
new file mode 100644
index 00000000000..f89da641446
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl
@@ -0,0 +1,327 @@
+
+/**
+ * Depth of Field utils.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+/* -------------------------------------------------------------------- */
+/** \name Constants.
+ * \{ */
+
+#ifndef DOF_SLIGHT_FOCUS_DENSITY
+# define DOF_SLIGHT_FOCUS_DENSITY 2
+#endif
+
+#ifdef DOF_RESOLVE_PASS
+const bool is_resolve = true;
+#else
+const bool is_resolve = false;
+#endif
+#ifdef DOF_FOREGROUND_PASS
+const bool is_foreground = DOF_FOREGROUND_PASS;
+#else
+const bool is_foreground = false;
+#endif
+/* Debug options */
+const bool debug_gather_perf = false;
+const bool debug_scatter_perf = false;
+const bool debug_resolve_perf = false;
+
+const bool no_smooth_intersection = false;
+const bool no_gather_occlusion = false;
+const bool no_gather_mipmaps = false;
+const bool no_gather_random = false;
+const bool no_gather_filtering = false;
+const bool no_scatter_occlusion = false;
+const bool no_scatter_pass = false;
+const bool no_foreground_pass = false;
+const bool no_background_pass = false;
+const bool no_slight_focus_pass = false;
+const bool no_focus_pass = false;
+const bool no_hole_fill_pass = false;
+
+/* Distribute weights between near/slightfocus/far fields (slide 117). */
+const float dof_layer_threshold = 4.0;
+/* Make sure it overlaps. */
+const float dof_layer_offset_fg = 0.5 + 1.0;
+/* Extra offset for convolution layers to avoid light leaking from background. */
+const float dof_layer_offset = 0.5 + 0.5;
+
+const int dof_max_slight_focus_radius = DOF_MAX_SLIGHT_FOCUS_RADIUS;
+
+const vec2 quad_offsets[4] = vec2[4](
+ vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5));
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Weighting and downsampling utils.
+ * \{ */
+
+float dof_hdr_color_weight(vec4 color)
+{
+ /* Very fast "luma" weighting. */
+ float luma = (color.g * 2.0) + (color.r + color.b);
+ /* TODO(fclem) Pass correct exposure. */
+ const float exposure = 1.0;
+ return 1.0 / (luma * exposure + 4.0);
+}
+
+float dof_coc_select(vec4 cocs)
+{
+ /* Select biggest coc. */
+ float selected_coc = cocs.x;
+ if (abs(cocs.y) > abs(selected_coc)) {
+ selected_coc = cocs.y;
+ }
+ if (abs(cocs.z) > abs(selected_coc)) {
+ selected_coc = cocs.z;
+ }
+ if (abs(cocs.w) > abs(selected_coc)) {
+ selected_coc = cocs.w;
+ }
+ return selected_coc;
+}
+
+/* NOTE: Do not forget to normalize weights afterwards. */
+vec4 dof_bilateral_coc_weights(vec4 cocs)
+{
+ float chosen_coc = dof_coc_select(cocs);
+
+ const float scale = 4.0; /* TODO(fclem) revisit. */
+ /* NOTE: The difference between the cocs should be inside a abs() function,
+ * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */
+ return saturate(1.0 - (chosen_coc - cocs) * scale);
+}
+
+/* NOTE: Do not forget to normalize weights afterwards. */
+vec4 dof_bilateral_color_weights(vec4 colors[4])
+{
+ vec4 weights;
+ for (int i = 0; i < 4; i++) {
+ weights[i] = dof_hdr_color_weight(colors[i]);
+ }
+ return weights;
+}
+
+/* Returns signed Circle of confusion radius (in pixel) based on depth buffer value [0..1]. */
+float dof_coc_from_depth(DepthOfFieldData dof_data, vec2 uv, float depth)
+{
+ if (is_panoramic(dof_data.camera_type)) {
+ /* Use radial depth. */
+ depth = -length(get_view_space_from_depth(uv, depth));
+ }
+ else {
+ depth = get_view_z_from_depth(depth);
+ }
+ return coc_radius_from_camera_depth(dof_data, depth);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Gather & Scatter Weighting
+ * \{ */
+
+float dof_layer_weight(float coc, const bool is_foreground)
+{
+ /* NOTE: These are fullres pixel CoC value. */
+ if (is_resolve) {
+ return saturate(-abs(coc) + dof_layer_threshold + dof_layer_offset) *
+ float(is_foreground ? (coc <= 0.5) : (coc > -0.5));
+ }
+ else {
+ coc *= 2.0; /* Account for half pixel gather. */
+ float threshold = dof_layer_threshold -
+ ((is_foreground) ? dof_layer_offset_fg : dof_layer_offset);
+ return saturate(((is_foreground) ? -coc : coc) - threshold);
+ }
+}
+vec4 dof_layer_weight(vec4 coc)
+{
+ /* NOTE: Used for scatter pass which already flipped the sign correctly. */
+ coc *= 2.0; /* Account for half pixel gather. */
+ return saturate(coc - dof_layer_threshold + dof_layer_offset);
+}
+
+/* NOTE: This is halfres CoC radius. */
+float dof_sample_weight(float coc)
+{
+#if 1 /* Optimized */
+ return min(1.0, 1.0 / sqr(coc));
+#else
+ /* Full intensity if CoC radius is below the pixel footprint. */
+ const float min_coc = 1.0;
+ coc = max(min_coc, abs(coc));
+ return (M_PI * min_coc * min_coc) / (M_PI * coc * coc);
+#endif
+}
+vec4 dof_sample_weight(vec4 coc)
+{
+#if 1 /* Optimized */
+ return min(vec4(1.0), 1.0 / sqr(coc));
+#else
+ /* Full intensity if CoC radius is below the pixel footprint. */
+ const float min_coc = 1.0;
+ coc = max(vec4(min_coc), abs(coc));
+ return (M_PI * min_coc * min_coc) / (M_PI * coc * coc);
+#endif
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Circle of Confusion tiles
+ * \{ */
+
+struct CocTile {
+ float fg_min_coc;
+ float fg_max_coc;
+ float fg_max_intersectable_coc;
+ float bg_min_coc;
+ float bg_max_coc;
+ float bg_min_intersectable_coc;
+};
+
+/* WATCH: Might have to change depending on the texture format. */
+const float dof_tile_large_coc = 1024.0;
+
+/* Init a CoC tile for reduction algorithms. */
+CocTile dof_coc_tile_init()
+{
+ CocTile tile;
+ tile.fg_min_coc = 0.0;
+ tile.fg_max_coc = -dof_tile_large_coc;
+ tile.fg_max_intersectable_coc = dof_tile_large_coc;
+ tile.bg_min_coc = dof_tile_large_coc;
+ tile.bg_max_coc = 0.0;
+ tile.bg_min_intersectable_coc = dof_tile_large_coc;
+ return tile;
+}
+
+CocTile dof_coc_tile_unpack(vec3 fg, vec3 bg)
+{
+ CocTile tile;
+ tile.fg_min_coc = -fg.x;
+ tile.fg_max_coc = -fg.y;
+ tile.fg_max_intersectable_coc = -fg.z;
+ tile.bg_min_coc = bg.x;
+ tile.bg_max_coc = bg.y;
+ tile.bg_min_intersectable_coc = bg.z;
+ return tile;
+}
+
+/* WORKAROUND(fclem): GLSL compilers differs in what qualifiers are requires to pass images as
+ * parameters. Workaround by using defines. */
+#define dof_coc_tile_load(tiles_fg_img_, tiles_bg_img_, texel_) \
+ dof_coc_tile_unpack( \
+ imageLoad(tiles_fg_img_, clamp(texel_, ivec2(0), imageSize(tiles_fg_img_) - 1)).xyz, \
+ imageLoad(tiles_bg_img_, clamp(texel_, ivec2(0), imageSize(tiles_bg_img_) - 1)).xyz)
+
+void dof_coc_tile_pack(CocTile tile, out vec3 out_fg, out vec3 out_bg)
+{
+ out_fg.x = -tile.fg_min_coc;
+ out_fg.y = -tile.fg_max_coc;
+ out_fg.z = -tile.fg_max_intersectable_coc;
+ out_bg.x = tile.bg_min_coc;
+ out_bg.y = tile.bg_max_coc;
+ out_bg.z = tile.bg_min_intersectable_coc;
+}
+
+#define dof_coc_tile_store(tiles_fg_img_, tiles_bg_img_, texel_out_, tile_data_) \
+ if (true) { \
+ vec3 out_fg; \
+ vec3 out_bg; \
+ dof_coc_tile_pack(tile_data_, out_fg, out_bg); \
+ imageStore(tiles_fg_img_, texel_out_, out_fg.xyzz); \
+ imageStore(tiles_bg_img_, texel_out_, out_bg.xyzz); \
+ }
+
+bool dof_do_fast_gather(float max_absolute_coc, float min_absolute_coc, const bool is_foreground)
+{
+ float min_weight = dof_layer_weight((is_foreground) ? -min_absolute_coc : min_absolute_coc,
+ is_foreground);
+ if (min_weight < 1.0) {
+ return false;
+ }
+ /* FIXME(fclem): This is a workaround to fast gather triggering too early. Since we use custom
+ * opacity mask, the opacity is not given to be 100% even for after normal threshold. */
+ if (is_foreground && min_absolute_coc < dof_layer_threshold) {
+ return false;
+ }
+ return (max_absolute_coc - min_absolute_coc) < (DOF_FAST_GATHER_COC_ERROR * max_absolute_coc);
+}
+
+struct CocTilePrediction {
+ bool do_foreground;
+ bool do_slight_focus;
+ bool do_focus;
+ bool do_background;
+ bool do_hole_fill;
+};
+
+/**
+ * Using the tile CoC infos, predict which convolutions are required and the ones that can be
+ * skipped.
+ */
+CocTilePrediction dof_coc_tile_prediction_get(CocTile tile)
+{
+ /* Based on tile value, predict what pass we need to load. */
+ CocTilePrediction predict;
+
+ predict.do_foreground = (-tile.fg_min_coc > dof_layer_threshold - dof_layer_offset_fg);
+ bool fg_fully_opaque = predict.do_foreground &&
+ dof_do_fast_gather(-tile.fg_min_coc, -tile.fg_max_coc, true);
+ predict.do_background = !fg_fully_opaque &&
+ (tile.bg_max_coc > dof_layer_threshold - dof_layer_offset);
+ bool bg_fully_opaque = predict.do_background &&
+ dof_do_fast_gather(-tile.bg_max_coc, tile.bg_min_coc, false);
+ predict.do_hole_fill = !fg_fully_opaque && -tile.fg_min_coc > 0.0;
+ predict.do_focus = !fg_fully_opaque;
+ predict.do_slight_focus = !fg_fully_opaque;
+
+#if 0 /* Debug */
+ predict.do_foreground = predict.do_background = predict.do_hole_fill = true;
+#endif
+ return predict;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Gathering
+ * \{ */
+
+/**
+ * Generate samples in a square pattern with the ring radius. X is the center tile.
+ *
+ * Dist1 Dist2
+ * 6 5 4 3 2
+ * 3 2 1 7 1
+ * . X 0 . X 0
+ * . . . . .
+ * . . . . .
+ *
+ * Samples are expected to be mirrored to complete the pattern.
+ **/
+ivec2 dof_square_ring_sample_offset(int ring_distance, int sample_id)
+{
+ ivec2 offset;
+ if (sample_id < ring_distance) {
+ offset.x = ring_distance;
+ offset.y = sample_id;
+ }
+ else if (sample_id < ring_distance * 3) {
+ offset.x = ring_distance - sample_id + ring_distance;
+ offset.y = ring_distance;
+ }
+ else {
+ offset.x = -ring_distance;
+ offset.y = ring_distance - sample_id + 3 * ring_distance;
+ }
+ return offset;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl
new file mode 100644
index 00000000000..a6426cd06e4
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl
@@ -0,0 +1,247 @@
+
+/**
+ * Reduce copy pass: filter fireflies and split color between scatter and gather input.
+ *
+ * NOTE: The texture can end up being too big because of the mipmap padding. We correct for
+ * that during the convolution phase.
+ *
+ * Inputs:
+ * - Output of setup pass (halfres) and reduce downsample pass (quarter res).
+ * Outputs:
+ * - Halfres padded to avoid mipmap misalignment (so possibly not matching input size).
+ * - Gather input color (whole mip chain), Scatter rect list, Signed CoC (whole mip chain).
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+/* NOTE: Do not compare alpha as it is not scattered by the scatter pass. */
+float dof_scatter_neighborhood_rejection(vec3 color)
+{
+ color = min(vec3(dof_buf.scatter_neighbor_max_color), color);
+
+ float validity = 0.0;
+
+ /* Centered in the middle of 4 quarter res texel. */
+ vec2 texel_size = 1.0 / vec2(textureSize(downsample_tx, 0).xy);
+ vec2 uv = ((vec2(gl_GlobalInvocationID.xy) + 0.5) * 0.5) * texel_size;
+
+ vec3 max_diff = vec3(0.0);
+ for (int i = 0; i < 4; i++) {
+ vec2 sample_uv = uv + quad_offsets[i] * texel_size;
+ vec3 ref = textureLod(downsample_tx, sample_uv, 0.0).rgb;
+
+ ref = min(vec3(dof_buf.scatter_neighbor_max_color), ref);
+ float diff = max_v3(max(vec3(0.0), abs(ref - color)));
+
+ const float rejection_threshold = 0.7;
+ diff = saturate(diff / rejection_threshold - 1.0);
+ validity = max(validity, diff);
+ }
+
+ return validity;
+}
+
+/* This avoids Bokeh sprite popping in and out at the screen border and
+ * drawing Bokeh sprites larger than the screen. */
+float dof_scatter_screen_border_rejection(float coc, ivec2 texel)
+{
+ vec2 screen_size = vec2(imageSize(inout_color_lod0_img));
+ vec2 uv = (vec2(texel) + 0.5) / screen_size;
+ vec2 screen_pos = uv * screen_size;
+ float min_screen_border_distance = min_v2(min(screen_pos, screen_size - screen_pos));
+ /* Fullres to halfres CoC. */
+ coc *= 0.5;
+ /* Allow 10px transition. */
+ const float rejection_hardeness = 1.0 / 10.0;
+ return saturate((min_screen_border_distance - abs(coc)) * rejection_hardeness + 1.0);
+}
+
+float dof_scatter_luminosity_rejection(vec3 color)
+{
+ const float rejection_hardness = 1.0;
+ return saturate(max_v3(color - dof_buf.scatter_color_threshold) * rejection_hardness);
+}
+
+float dof_scatter_coc_radius_rejection(float coc)
+{
+ const float rejection_hardness = 0.3;
+ return saturate((abs(coc) - dof_buf.scatter_coc_threshold) * rejection_hardness);
+}
+
+float fast_luma(vec3 color)
+{
+ return (2.0 * color.g) + color.r + color.b;
+}
+
+const uint cache_size = gl_WorkGroupSize.x;
+shared vec4 color_cache[cache_size][cache_size];
+shared float coc_cache[cache_size][cache_size];
+shared float do_scatter[cache_size][cache_size];
+
+void main()
+{
+ ivec2 texel = min(ivec2(gl_GlobalInvocationID.xy), imageSize(inout_color_lod0_img) - 1);
+ uvec2 texel_local = gl_LocalInvocationID.xy;
+ /* Increase readablility. */
+#define LOCAL_INDEX texel_local.y][texel_local.x
+#define LOCAL_OFFSET(x_, y_) texel_local.y + (y_)][texel_local.x + (x_)
+
+ /* Load level 0 into cache. */
+ color_cache[LOCAL_INDEX] = imageLoad(inout_color_lod0_img, texel);
+ coc_cache[LOCAL_INDEX] = imageLoad(in_coc_lod0_img, texel).r;
+
+ /* Only scatter if luminous enough. */
+ do_scatter[LOCAL_INDEX] = dof_scatter_luminosity_rejection(color_cache[LOCAL_INDEX].rgb);
+ /* Only scatter if CoC is big enough. */
+ do_scatter[LOCAL_INDEX] *= dof_scatter_coc_radius_rejection(coc_cache[LOCAL_INDEX]);
+ /* Only scatter if CoC is not too big to avoid performance issues. */
+ do_scatter[LOCAL_INDEX] *= dof_scatter_screen_border_rejection(coc_cache[LOCAL_INDEX], texel);
+ /* Only scatter if neighborhood is different enough. */
+ do_scatter[LOCAL_INDEX] *= dof_scatter_neighborhood_rejection(color_cache[LOCAL_INDEX].rgb);
+ /* For debugging. */
+ if (no_scatter_pass) {
+ do_scatter[LOCAL_INDEX] = 0.0;
+ }
+
+ barrier();
+
+ /* Add a scatter sprite for each 2x2 pixel neighborhood passing the threshold. */
+ if (all(equal(texel_local & 1u, uvec2(0)))) {
+ vec4 do_scatter4;
+ /* Follows quad_offsets order. */
+ do_scatter4.x = do_scatter[LOCAL_OFFSET(0, 1)];
+ do_scatter4.y = do_scatter[LOCAL_OFFSET(1, 1)];
+ do_scatter4.z = do_scatter[LOCAL_OFFSET(1, 0)];
+ do_scatter4.w = do_scatter[LOCAL_OFFSET(0, 0)];
+ if (any(greaterThan(do_scatter4, vec4(0.0)))) {
+ /* Apply energy conservation to anamorphic scattered bokeh. */
+ do_scatter4 *= max_v2(dof_buf.bokeh_anisotropic_scale_inv);
+
+ /* Circle of Confusion. */
+ vec4 coc4;
+ coc4.x = coc_cache[LOCAL_OFFSET(0, 1)];
+ coc4.y = coc_cache[LOCAL_OFFSET(1, 1)];
+ coc4.z = coc_cache[LOCAL_OFFSET(1, 0)];
+ coc4.w = coc_cache[LOCAL_OFFSET(0, 0)];
+ /* We are scattering at half resolution, so divide CoC by 2. */
+ coc4 *= 0.5;
+ /* Sprite center position. Center sprite around the 4 texture taps. */
+ vec2 offset = vec2(gl_GlobalInvocationID.xy) + 1;
+ /* Add 2.5 to max_coc because the max_coc may not be centered on the sprite origin
+ * and because we smooth the bokeh shape a bit in the pixel shader. */
+ vec2 half_extent = max_v4(abs(coc4)) * dof_buf.bokeh_anisotropic_scale + 2.5;
+ /* Issue a sprite for each field if any CoC matches. */
+ if (any(lessThan(do_scatter4 * sign(coc4), vec4(0.0)))) {
+ /* Same value for all threads. Not an issue if we don't sync access to it. */
+ scatter_fg_indirect_buf.vertex_len = 4u;
+ /* Issue 1 strip instance per sprite. */
+ uint rect_id = atomicAdd(scatter_fg_indirect_buf.instance_len, 1u);
+ if (rect_id < dof_buf.scatter_max_rect) {
+
+ vec4 coc4_fg = max(vec4(0.0), -coc4);
+ vec4 fg_weights = dof_layer_weight(coc4_fg) * dof_sample_weight(coc4_fg) * do_scatter4;
+ /* Filter NaNs. */
+ fg_weights = select(fg_weights, vec4(0.0), equal(coc4_fg, vec4(0.0)));
+
+ ScatterRect rect_fg;
+ rect_fg.offset = offset;
+ /* Negate extent to flip the sprite. Mimics optical phenomenon. */
+ rect_fg.half_extent = -half_extent;
+ /* NOTE: Since we fliped the quad along (1,-1) line, we need to also swap the (1,1) and
+ * (0,0) values so that quad_offsets is in the right order in the vertex shader. */
+
+ /* Circle of Confusion absolute radius in halfres pixels. */
+ rect_fg.color_and_coc[0].a = coc4_fg[0];
+ rect_fg.color_and_coc[1].a = coc4_fg[3];
+ rect_fg.color_and_coc[2].a = coc4_fg[2];
+ rect_fg.color_and_coc[3].a = coc4_fg[1];
+ /* Apply weights. */
+ rect_fg.color_and_coc[0].rgb = color_cache[LOCAL_OFFSET(0, 1)].rgb * fg_weights[0];
+ rect_fg.color_and_coc[1].rgb = color_cache[LOCAL_OFFSET(0, 0)].rgb * fg_weights[3];
+ rect_fg.color_and_coc[2].rgb = color_cache[LOCAL_OFFSET(1, 0)].rgb * fg_weights[2];
+ rect_fg.color_and_coc[3].rgb = color_cache[LOCAL_OFFSET(1, 1)].rgb * fg_weights[1];
+
+ scatter_fg_list_buf[rect_id] = rect_fg;
+ }
+ }
+ if (any(greaterThan(do_scatter4 * sign(coc4), vec4(0.0)))) {
+ /* Same value for all threads. Not an issue if we don't sync access to it. */
+ scatter_bg_indirect_buf.vertex_len = 4u;
+ /* Issue 1 strip instance per sprite. */
+ uint rect_id = atomicAdd(scatter_bg_indirect_buf.instance_len, 1u);
+ if (rect_id < dof_buf.scatter_max_rect) {
+ vec4 coc4_bg = max(vec4(0.0), coc4);
+ vec4 bg_weights = dof_layer_weight(coc4_bg) * dof_sample_weight(coc4_bg) * do_scatter4;
+ /* Filter NaNs. */
+ bg_weights = select(bg_weights, vec4(0.0), equal(coc4_bg, vec4(0.0)));
+
+ ScatterRect rect_bg;
+ rect_bg.offset = offset;
+ rect_bg.half_extent = half_extent;
+
+ /* Circle of Confusion absolute radius in halfres pixels. */
+ rect_bg.color_and_coc[0].a = coc4_bg[0];
+ rect_bg.color_and_coc[1].a = coc4_bg[1];
+ rect_bg.color_and_coc[2].a = coc4_bg[2];
+ rect_bg.color_and_coc[3].a = coc4_bg[3];
+ /* Apply weights. */
+ rect_bg.color_and_coc[0].rgb = color_cache[LOCAL_OFFSET(0, 1)].rgb * bg_weights[0];
+ rect_bg.color_and_coc[1].rgb = color_cache[LOCAL_OFFSET(1, 1)].rgb * bg_weights[1];
+ rect_bg.color_and_coc[2].rgb = color_cache[LOCAL_OFFSET(1, 0)].rgb * bg_weights[2];
+ rect_bg.color_and_coc[3].rgb = color_cache[LOCAL_OFFSET(0, 0)].rgb * bg_weights[3];
+
+ scatter_bg_list_buf[rect_id] = rect_bg;
+ }
+ }
+ }
+ }
+
+ /* Remove scatter color from gather. */
+ color_cache[LOCAL_INDEX].rgb *= 1.0 - do_scatter[LOCAL_INDEX];
+ imageStore(inout_color_lod0_img, texel, color_cache[LOCAL_INDEX]);
+
+ /* Recursive downsample. */
+ for (uint i = 1u; i < DOF_MIP_COUNT; i++) {
+ barrier();
+ uint mask = ~(~0u << i);
+ if (all(equal(gl_LocalInvocationID.xy & mask, uvec2(0)))) {
+ uint ofs = 1u << (i - 1u);
+
+ /* TODO(fclem): Could use wave shuffle intrinsics to avoid LDS as suggested by the paper. */
+ vec4 coc4;
+ coc4.x = coc_cache[LOCAL_OFFSET(0, ofs)];
+ coc4.y = coc_cache[LOCAL_OFFSET(ofs, ofs)];
+ coc4.z = coc_cache[LOCAL_OFFSET(ofs, 0)];
+ coc4.w = coc_cache[LOCAL_OFFSET(0, 0)];
+
+ vec4 colors[4];
+ colors[0] = color_cache[LOCAL_OFFSET(0, ofs)];
+ colors[1] = color_cache[LOCAL_OFFSET(ofs, ofs)];
+ colors[2] = color_cache[LOCAL_OFFSET(ofs, 0)];
+ colors[3] = color_cache[LOCAL_OFFSET(0, 0)];
+
+ vec4 weights = dof_bilateral_coc_weights(coc4);
+ weights *= dof_bilateral_color_weights(colors);
+ /* Normalize so that the sum is 1. */
+ weights *= safe_rcp(sum(weights));
+
+ color_cache[LOCAL_INDEX] = weighted_sum_array(colors, weights);
+ coc_cache[LOCAL_INDEX] = dot(coc4, weights);
+
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy >> i);
+
+ if (i == 1) {
+ imageStore(out_color_lod1_img, texel, color_cache[LOCAL_INDEX]);
+ imageStore(out_coc_lod1_img, texel, vec4(coc_cache[LOCAL_INDEX]));
+ }
+ else if (i == 2) {
+ imageStore(out_color_lod2_img, texel, color_cache[LOCAL_INDEX]);
+ imageStore(out_coc_lod2_img, texel, vec4(coc_cache[LOCAL_INDEX]));
+ }
+ else /* if (i == 3) */ {
+ imageStore(out_color_lod3_img, texel, color_cache[LOCAL_INDEX]);
+ imageStore(out_coc_lod3_img, texel, vec4(coc_cache[LOCAL_INDEX]));
+ }
+ }
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl
new file mode 100644
index 00000000000..5123eb0c238
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl
@@ -0,0 +1,178 @@
+
+/**
+ * Recombine Pass: Load separate convolution layer and composite with self
+ * slight defocus convolution and in-focus fields.
+ *
+ * The halfres gather methods are fast but lack precision for small CoC areas.
+ * To fix this we do a bruteforce gather to have a smooth transition between
+ * in-focus and defocus regions.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl)
+
+shared uint shared_max_slight_focus_abs_coc;
+
+/**
+ * Returns The max CoC in the Slight Focus range inside this compute tile.
+ */
+float dof_slight_focus_coc_tile_get(vec2 frag_coord)
+{
+ if (all(equal(gl_LocalInvocationID, uvec3(0)))) {
+ shared_max_slight_focus_abs_coc = floatBitsToUint(0.0);
+ }
+ barrier();
+
+ float local_abs_max = 0.0;
+ /* Sample in a cross (X) pattern. This covers all pixels over the whole tile, as long as
+ * dof_max_slight_focus_radius is less than the group size. */
+ for (int i = 0; i < 4; i++) {
+ vec2 sample_uv = (frag_coord + quad_offsets[i] * 2.0 * dof_max_slight_focus_radius) /
+ vec2(textureSize(color_tx, 0));
+ float coc = dof_coc_from_depth(dof_buf, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r);
+ coc = clamp(coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max);
+ if (abs(coc) < dof_max_slight_focus_radius) {
+ local_abs_max = max(local_abs_max, abs(coc));
+ }
+ }
+ /* Use atomic reduce operation. */
+ atomicMax(shared_max_slight_focus_abs_coc, floatBitsToUint(local_abs_max));
+ /* "Broadcast" result across all threads. */
+ barrier();
+
+ return uintBitsToFloat(shared_max_slight_focus_abs_coc);
+}
+
+vec3 dof_neighborhood_clamp(vec2 frag_coord, vec3 color, float center_coc, float weight)
+{
+ /* Stabilize color by clamping with the stable half res neighborhood. */
+ vec3 neighbor_min, neighbor_max;
+ const vec2 corners[4] = vec2[4](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1));
+ for (int i = 0; i < 4; i++) {
+ /**
+ * Visit the 4 half-res texels around (and containing) the fullres texel.
+ * Here a diagram of a fullscreen texel (f) in the bottom left corner of a half res texel.
+ * We sample the stable half-resolution texture at the 4 location denoted by (h).
+ * ┌───────┬───────┐
+ * │ h │ h │
+ * │ │ │
+ * │ │ f │
+ * ├───────┼───────┤
+ * │ h │ h │
+ * │ │ │
+ * │ │ │
+ * └───────┴───────┘
+ */
+ vec2 uv_sample = ((frag_coord + corners[i]) * 0.5) / vec2(textureSize(stable_color_tx, 0));
+ /* Reminder: The content of this buffer is YCoCg + CoC. */
+ vec3 ycocg_sample = textureLod(stable_color_tx, uv_sample, 0.0).rgb;
+ neighbor_min = (i == 0) ? ycocg_sample : min(neighbor_min, ycocg_sample);
+ neighbor_max = (i == 0) ? ycocg_sample : max(neighbor_max, ycocg_sample);
+ }
+ /* Pad the bounds in the near in focus region to get back a bit of detail. */
+ float padding = 0.125 * saturate(1.0 - sqr(center_coc) / sqr(8.0));
+ neighbor_max += abs(neighbor_min) * padding;
+ neighbor_min -= abs(neighbor_min) * padding;
+ /* Progressively apply the clamp to avoid harsh transition. Also mask by weight. */
+ float fac = saturate(sqr(center_coc) * 4.0) * weight;
+ /* Clamp in YCoCg space to avoid too much color drift. */
+ color = colorspace_YCoCg_from_scene_linear(color);
+ color = mix(color, clamp(color, neighbor_min, neighbor_max), fac);
+ color = colorspace_scene_linear_from_YCoCg(color);
+ return color;
+}
+
+void main()
+{
+ vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5;
+ ivec2 tile_co = ivec2(frag_coord / float(DOF_TILES_SIZE * 2));
+
+ CocTile coc_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, tile_co);
+ CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile);
+
+ vec2 uv = frag_coord / vec2(textureSize(color_tx, 0));
+ vec2 uv_halfres = (frag_coord * 0.5) / vec2(textureSize(color_bg_tx, 0));
+
+ float slight_focus_max_coc = 0.0;
+ if (prediction.do_slight_focus) {
+ slight_focus_max_coc = dof_slight_focus_coc_tile_get(frag_coord);
+ prediction.do_slight_focus = slight_focus_max_coc >= 0.5;
+ if (prediction.do_slight_focus) {
+ prediction.do_focus = false;
+ }
+ }
+
+ if (prediction.do_focus) {
+ float center_coc = (dof_coc_from_depth(dof_buf, uv, textureLod(depth_tx, uv, 0.0).r));
+ prediction.do_focus = abs(center_coc) <= 0.5;
+ }
+
+ vec4 out_color = vec4(0.0);
+ float weight = 0.0;
+
+ vec4 layer_color;
+ float layer_weight;
+
+ if (!no_hole_fill_pass && prediction.do_hole_fill) {
+ layer_color = textureLod(color_hole_fill_tx, uv_halfres, 0.0);
+ layer_weight = textureLod(weight_hole_fill_tx, uv_halfres, 0.0).r;
+ out_color = layer_color * safe_rcp(layer_weight);
+ weight = float(layer_weight > 0.0);
+ }
+
+ if (!no_background_pass && prediction.do_background) {
+ layer_color = textureLod(color_bg_tx, uv_halfres, 0.0);
+ layer_weight = textureLod(weight_bg_tx, uv_halfres, 0.0).r;
+ /* Always prefer background to hole_fill pass. */
+ layer_color *= safe_rcp(layer_weight);
+ layer_weight = float(layer_weight > 0.0);
+ /* Composite background. */
+ out_color = out_color * (1.0 - layer_weight) + layer_color;
+ weight = weight * (1.0 - layer_weight) + layer_weight;
+ /* Fill holes with the composited background. */
+ out_color *= safe_rcp(weight);
+ weight = float(weight > 0.0);
+ }
+
+ if (!no_slight_focus_pass && prediction.do_slight_focus) {
+ float center_coc;
+ dof_slight_focus_gather(depth_tx,
+ color_tx,
+ bokeh_lut_tx,
+ slight_focus_max_coc,
+ layer_color,
+ layer_weight,
+ center_coc);
+
+ /* Composite slight defocus. */
+ out_color = out_color * (1.0 - layer_weight) + layer_color;
+ weight = weight * (1.0 - layer_weight) + layer_weight;
+
+ out_color.rgb = dof_neighborhood_clamp(frag_coord, out_color.rgb, center_coc, layer_weight);
+ }
+
+ if (!no_focus_pass && prediction.do_focus) {
+ layer_color = safe_color(textureLod(color_tx, uv, 0.0));
+ layer_weight = 1.0;
+ /* Composite in focus. */
+ out_color = out_color * (1.0 - layer_weight) + layer_color;
+ weight = weight * (1.0 - layer_weight) + layer_weight;
+ }
+
+ if (!no_foreground_pass && prediction.do_foreground) {
+ layer_color = textureLod(color_fg_tx, uv_halfres, 0.0);
+ layer_weight = textureLod(weight_fg_tx, uv_halfres, 0.0).r;
+ /* Composite foreground. */
+ out_color = out_color * (1.0 - layer_weight) + layer_color;
+ }
+
+ /* Fix float precision issue in alpha compositing. */
+ if (out_color.a > 0.99) {
+ out_color.a = 1.0;
+ }
+
+ if (debug_resolve_perf && prediction.do_slight_focus) {
+ out_color.rgb *= vec3(1.0, 0.1, 0.1);
+ }
+
+ imageStore(out_color_img, ivec2(gl_GlobalInvocationID.xy), out_color);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl
new file mode 100644
index 00000000000..cfb7fd2568b
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl
@@ -0,0 +1,62 @@
+
+/**
+ * Scatter pass: Use sprites to scatter the color of very bright pixel to have higher quality blur.
+ *
+ * We only scatter one quad per sprite and one sprite per 4 pixels to reduce vertex shader
+ * invocations and overdraw.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+#define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0))
+
+void main()
+{
+ vec4 coc4 = vec4(interp.color_and_coc1.w,
+ interp.color_and_coc2.w,
+ interp.color_and_coc3.w,
+ interp.color_and_coc4.w);
+ vec4 shapes;
+ if (use_bokeh_lut) {
+ shapes = vec4(texture(bokeh_lut_tx, interp.rect_uv1).r,
+ texture(bokeh_lut_tx, interp.rect_uv2).r,
+ texture(bokeh_lut_tx, interp.rect_uv3).r,
+ texture(bokeh_lut_tx, interp.rect_uv4).r);
+ }
+ else {
+ shapes = vec4(length(interp.rect_uv1),
+ length(interp.rect_uv2),
+ length(interp.rect_uv3),
+ length(interp.rect_uv4));
+ }
+ shapes *= interp.distance_scale;
+ /* Becomes signed distance field in pixel units. */
+ shapes -= coc4;
+ /* Smooth the edges a bit to fade out the undersampling artifacts. */
+ shapes = saturate(1.0 - linearstep(-0.8, 0.8, shapes));
+ /* Outside of bokeh shape. Try to avoid overloading ROPs. */
+ if (max_v4(shapes) == 0.0) {
+ discard;
+ }
+
+ if (!no_scatter_occlusion) {
+ /* Works because target is the same size as occlusion_tx. */
+ vec2 uv = gl_FragCoord.xy / vec2(textureSize(occlusion_tx, 0).xy);
+ vec2 occlusion_data = texture(occlusion_tx, uv).rg;
+ /* Fix tilling artifacts. (Slide 90) */
+ const float correction_fac = 1.0 - DOF_FAST_GATHER_COC_ERROR;
+ /* Occlude the sprite with geometry from the same field using a chebychev test (slide 85). */
+ float mean = occlusion_data.x;
+ float variance = occlusion_data.y;
+ shapes *= variance * safe_rcp(variance + sqr(max(coc4 * correction_fac - mean, 0.0)));
+ }
+
+ out_color = (interp.color_and_coc1 * shapes[0] + interp.color_and_coc2 * shapes[1] +
+ interp.color_and_coc3 * shapes[2] + interp.color_and_coc4 * shapes[3]);
+ /* Do not accumulate alpha. This has already been accumulated by the gather pass. */
+ out_color.a = 0.0;
+
+ if (debug_scatter_perf) {
+ out_color.rgb = avg(out_color.rgb) * vec3(1.0, 0.0, 0.0);
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl
new file mode 100644
index 00000000000..d870496a06c
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl
@@ -0,0 +1,45 @@
+
+/**
+ * Scatter pass: Use sprites to scatter the color of very bright pixel to have higher quality blur.
+ *
+ * We only scatter one triangle per sprite and one sprite per 4 pixels to reduce vertex shader
+ * invocations and overdraw.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+void main()
+{
+ ScatterRect rect = scatter_list_buf[gl_InstanceID];
+
+ interp.color_and_coc1 = rect.color_and_coc[0];
+ interp.color_and_coc2 = rect.color_and_coc[1];
+ interp.color_and_coc3 = rect.color_and_coc[2];
+ interp.color_and_coc4 = rect.color_and_coc[3];
+
+ vec2 uv = vec2(gl_VertexID & 1, gl_VertexID >> 1) * 2.0 - 1.0;
+ uv = uv * rect.half_extent;
+
+ gl_Position = vec4(uv + rect.offset, 0.0, 1.0);
+ /* NDC range [-1..1]. */
+ gl_Position.xy = (gl_Position.xy / vec2(textureSize(occlusion_tx, 0).xy)) * 2.0 - 1.0;
+
+ if (use_bokeh_lut) {
+ /* Bias scale to avoid sampling at the texture's border. */
+ interp.distance_scale = (float(DOF_BOKEH_LUT_SIZE) / float(DOF_BOKEH_LUT_SIZE - 1));
+ vec2 uv_div = 1.0 / (interp.distance_scale * abs(rect.half_extent));
+ interp.rect_uv1 = ((uv + quad_offsets[0]) * uv_div) * 0.5 + 0.5;
+ interp.rect_uv2 = ((uv + quad_offsets[1]) * uv_div) * 0.5 + 0.5;
+ interp.rect_uv3 = ((uv + quad_offsets[2]) * uv_div) * 0.5 + 0.5;
+ interp.rect_uv4 = ((uv + quad_offsets[3]) * uv_div) * 0.5 + 0.5;
+ /* Only for sampling. */
+ interp.distance_scale *= max_v2(abs(rect.half_extent));
+ }
+ else {
+ interp.distance_scale = 1.0;
+ interp.rect_uv1 = uv + quad_offsets[0];
+ interp.rect_uv2 = uv + quad_offsets[1];
+ interp.rect_uv3 = uv + quad_offsets[2];
+ interp.rect_uv4 = uv + quad_offsets[3];
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl
new file mode 100644
index 00000000000..c017a5aa965
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl
@@ -0,0 +1,46 @@
+
+/**
+ * Setup pass: CoC and luma aware downsample to half resolution of the input scene color buffer.
+ *
+ * An addition to the downsample CoC, we output the maximum slight out of focus CoC to be
+ * sure we don't miss a pixel.
+ *
+ * Input:
+ * Full-resolution color & depth buffer
+ * Output:
+ * Half-resolution Color, signed CoC (out_coc.x), and max slight focus abs CoC (out_coc.y).
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+void main()
+{
+ vec2 fullres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy);
+ /* Center uv around the 4 fullres pixels. */
+ vec2 quad_center = vec2(gl_GlobalInvocationID.xy * 2 + 1) * fullres_texel_size;
+
+ vec4 colors[4];
+ vec4 cocs;
+ for (int i = 0; i < 4; i++) {
+ vec2 sample_uv = quad_center + quad_offsets[i] * fullres_texel_size;
+ /* NOTE: We use samplers without filtering. */
+ colors[i] = safe_color(textureLod(color_tx, sample_uv, 0.0));
+ cocs[i] = dof_coc_from_depth(dof_buf, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r);
+ }
+
+ cocs = clamp(cocs, -dof_buf.coc_abs_max, dof_buf.coc_abs_max);
+
+ vec4 weights = dof_bilateral_coc_weights(cocs);
+ weights *= dof_bilateral_color_weights(colors);
+ /* Normalize so that the sum is 1. */
+ weights *= safe_rcp(sum(weights));
+
+ ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy);
+ vec4 out_color = weighted_sum_array(colors, weights);
+ imageStore(out_color_img, out_texel, out_color);
+
+ float out_coc = dot(cocs, weights);
+ imageStore(out_coc_img, out_texel, vec4(out_coc));
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl
new file mode 100644
index 00000000000..46a25b84840
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl
@@ -0,0 +1,367 @@
+
+/**
+ * Temporal Stabilization of the Depth of field input.
+ * Corresponds to the TAA pass in the paper.
+ * We actually duplicate the TAA logic but with a few changes:
+ * - We run this pass at half resolution.
+ * - We store CoC instead of Opacity in the alpha channel of the history.
+ *
+ * This is and adaption of the code found in eevee_film_lib.glsl
+ *
+ * Inputs:
+ * - Output of setup pass (halfres).
+ * Outputs:
+ * - Stabilized Color and CoC (halfres).
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
+
+struct DofSample {
+ vec4 color;
+ float coc;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name LDS Cache
+ * \{ */
+
+const uint cache_size = gl_WorkGroupSize.x + 2;
+shared vec4 color_cache[cache_size][cache_size];
+shared float coc_cache[cache_size][cache_size];
+/* Need 2 pixel border for depth. */
+const uint cache_depth_size = gl_WorkGroupSize.x + 4;
+shared float depth_cache[cache_depth_size][cache_depth_size];
+
+void dof_cache_init()
+{
+ /**
+ * Load enough values into LDS to perform the filter.
+ *
+ * ┌──────────────────────────────┐
+ * │ │ < Border texels that needs to be loaded.
+ * │ x x x x x x x x │ ─┐
+ * │ x x x x x x x x │ │
+ * │ x x x x x x x x │ │
+ * │ x x x x x x x x │ │ Thread Group Size 8x8.
+ * │ L L L L L x x x x │ │
+ * │ L L L L L x x x x │ │
+ * │ L L L L L x x x x │ │
+ * │ L L L L L x x x x │ ─┘
+ * │ L L L L L │ < Border texels that needs to be loaded.
+ * └──────────────────────────────┘
+ * └───────────┘
+ * Load using 5x5 threads.
+ */
+
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ for (int y = 0; y < 2; y++) {
+ for (int x = 0; x < 2; x++) {
+ /* 1 Pixel border. */
+ if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_size / 2u)))) {
+ ivec2 offset = ivec2(x, y) * ivec2(cache_size / 2u);
+ ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset;
+ ivec2 load_texel = clamp(texel + offset - 1, ivec2(0), textureSize(color_tx, 0) - 1);
+
+ vec4 color = texelFetch(color_tx, load_texel, 0);
+ color_cache[cache_texel.y][cache_texel.x] = colorspace_YCoCg_from_scene_linear(color);
+ coc_cache[cache_texel.y][cache_texel.x] = texelFetch(coc_tx, load_texel, 0).x;
+ }
+ /* 2 Pixels border. */
+ if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_depth_size / 2u)))) {
+ ivec2 offset = ivec2(x, y) * ivec2(cache_depth_size / 2u);
+ ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset;
+ /* Depth is fullres. Load every 2 pixels. */
+ ivec2 load_texel = clamp((texel + offset - 2) * 2, ivec2(0), textureSize(depth_tx, 0) - 1);
+
+ depth_cache[cache_texel.y][cache_texel.x] = texelFetch(depth_tx, load_texel, 0).x;
+ }
+ }
+ }
+ barrier();
+}
+
+/* NOTE: Sample color space is already in YCoCg space. */
+DofSample dof_fetch_input_sample(ivec2 offset)
+{
+ ivec2 coord = offset + 1 + ivec2(gl_LocalInvocationID.xy);
+ return DofSample(color_cache[coord.y][coord.x], coc_cache[coord.y][coord.x]);
+}
+
+float dof_fetch_half_depth(ivec2 offset)
+{
+ ivec2 coord = offset + 2 + ivec2(gl_LocalInvocationID.xy);
+ return depth_cache[coord.y][coord.x];
+}
+
+/** \} */
+
+float dof_luma_weight(float luma)
+{
+ /* Slide 20 of "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014. */
+ /* To preserve more details in dark areas, we use a bigger bias. */
+ const float exposure_scale = 1.0; /* TODO. */
+ return 1.0 / (4.0 + luma * exposure_scale);
+}
+
+float dof_bilateral_weight(float reference_coc, float sample_coc)
+{
+ /* NOTE: The difference between the cocs should be inside a abs() function,
+ * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19).
+ * Effectively bleed background into foreground.
+ * Compared to dof_bilateral_coc_weights() this saturates as 2x the reference CoC. */
+ return saturate(1.0 - (sample_coc - reference_coc) / max(1.0, abs(reference_coc)));
+}
+
+DofSample dof_spatial_filtering()
+{
+ /* Plus (+) shape offsets. */
+ const ivec2 plus_offsets[4] = ivec2[4](ivec2(-1, 0), ivec2(0, -1), ivec2(1, 0), ivec2(0, 1));
+ DofSample center = dof_fetch_input_sample(ivec2(0));
+ DofSample accum = DofSample(vec4(0.0), 0.0);
+ float accum_weight = 0.0;
+ for (int i = 0; i < 4; i++) {
+ DofSample samp = dof_fetch_input_sample(plus_offsets[i]);
+ float weight = dof_buf.filter_samples_weight[i] * dof_luma_weight(samp.color.x) *
+ dof_bilateral_weight(center.coc, samp.coc);
+
+ accum.color += samp.color * weight;
+ accum.coc += samp.coc * weight;
+ accum_weight += weight;
+ }
+ /* Accumulate center sample last as it does not need bilateral_weights. */
+ float weight = dof_buf.filter_center_weight * dof_luma_weight(center.color.x);
+ accum.color += center.color * weight;
+ accum.coc += center.coc * weight;
+ accum_weight += weight;
+
+ float rcp_weight = 1.0 / accum_weight;
+ accum.color *= rcp_weight;
+ accum.coc *= rcp_weight;
+ return accum;
+}
+
+struct DofNeighborhoodMinMax {
+ DofSample min;
+ DofSample max;
+};
+
+/* Return history clipping bounding box in YCoCg color space. */
+DofNeighborhoodMinMax dof_neighbor_boundbox()
+{
+ /* Plus (+) shape offsets. */
+ const ivec2 plus_offsets[4] = ivec2[4](ivec2(-1, 0), ivec2(0, -1), ivec2(1, 0), ivec2(0, 1));
+ /**
+ * Simple bounding box calculation in YCoCg as described in:
+ * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014
+ */
+ DofSample min_c = dof_fetch_input_sample(ivec2(0));
+ DofSample max_c = min_c;
+ for (int i = 0; i < 4; i++) {
+ DofSample samp = dof_fetch_input_sample(plus_offsets[i]);
+ min_c.color = min(min_c.color, samp.color);
+ max_c.color = max(max_c.color, samp.color);
+ min_c.coc = min(min_c.coc, samp.coc);
+ max_c.coc = max(max_c.coc, samp.coc);
+ }
+ /* (Slide 32) Simple clamp to min/max of 8 neighbors results in 3x3 box artifacts.
+ * Round bbox shape by averaging 2 different min/max from 2 different neighborhood. */
+ DofSample min_c_3x3 = min_c;
+ DofSample max_c_3x3 = max_c;
+ const ivec2 corners[4] = ivec2[4](ivec2(-1, -1), ivec2(1, -1), ivec2(-1, 1), ivec2(1, 1));
+ for (int i = 0; i < 4; i++) {
+ DofSample samp = dof_fetch_input_sample(corners[i]);
+ min_c_3x3.color = min(min_c_3x3.color, samp.color);
+ max_c_3x3.color = max(max_c_3x3.color, samp.color);
+ min_c_3x3.coc = min(min_c_3x3.coc, samp.coc);
+ max_c_3x3.coc = max(max_c_3x3.coc, samp.coc);
+ }
+ min_c.color = (min_c.color + min_c_3x3.color) * 0.5;
+ max_c.color = (max_c.color + max_c_3x3.color) * 0.5;
+ min_c.coc = (min_c.coc + min_c_3x3.coc) * 0.5;
+ max_c.coc = (max_c.coc + max_c_3x3.coc) * 0.5;
+
+ return DofNeighborhoodMinMax(min_c, max_c);
+}
+
+/* Returns motion in pixel space to retrieve the pixel history. */
+vec2 dof_pixel_history_motion_vector(ivec2 texel_sample)
+{
+ /**
+ * Dilate velocity by using the nearest pixel in a cross pattern.
+ * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 27)
+ */
+ const ivec2 corners[4] = ivec2[4](ivec2(-2, -2), ivec2(2, -2), ivec2(-2, 2), ivec2(2, 2));
+ float min_depth = dof_fetch_half_depth(ivec2(0));
+ ivec2 nearest_texel = ivec2(0);
+ for (int i = 0; i < 4; i++) {
+ float depth = dof_fetch_half_depth(corners[i]);
+ if (min_depth > depth) {
+ min_depth = depth;
+ nearest_texel = corners[i];
+ }
+ }
+ /* Convert to full resolution buffer pixel. */
+ ivec2 velocity_texel = (texel_sample + nearest_texel) * 2;
+ velocity_texel = clamp(velocity_texel, ivec2(0), textureSize(velocity_tx, 0).xy - 1);
+ vec4 vector = velocity_resolve(velocity_tx, velocity_texel, min_depth);
+ /* Transform to **half** pixel space. */
+ return vector.xy * vec2(textureSize(color_tx, 0));
+}
+
+/* Load color using a special filter to avoid losing detail.
+ * \a texel is sample position with subpixel accuracy. */
+DofSample dof_sample_history(vec2 input_texel)
+{
+#if 1 /* Bilinar. */
+ vec2 uv = vec2(input_texel + 0.5) / textureSize(in_history_tx, 0);
+ vec4 color = textureLod(in_history_tx, uv, 0.0);
+
+#else /* Catmull Rom interpolation. 5 Bilinear Taps. */
+ vec2 center_texel;
+ vec2 inter_texel = modf(input_texel, center_texel);
+ vec2 weights[4];
+ film_get_catmull_rom_weights(inter_texel, weights);
+
+ /**
+ * Use optimized version by leveraging bilinear filtering from hardware sampler and by removing
+ * corner taps.
+ * From "Filmic SMAA" by Jorge Jimenez at Siggraph 2016
+ * http://advances.realtimerendering.com/s2016/Filmic%20SMAA%20v7.pptx
+ */
+ center_texel += 0.5;
+
+ /* Slide 92. */
+ vec2 weight_12 = weights[1] + weights[2];
+ vec2 uv_12 = (center_texel + weights[2] / weight_12) * film_buf.extent_inv;
+ vec2 uv_0 = (center_texel - 1.0) * film_buf.extent_inv;
+ vec2 uv_3 = (center_texel + 2.0) * film_buf.extent_inv;
+
+ vec4 color;
+ vec4 weight_cross = weight_12.xyyx * vec4(weights[0].yx, weights[3].xy);
+ float weight_center = weight_12.x * weight_12.y;
+
+ color = textureLod(in_history_tx, uv_12, 0.0) * weight_center;
+ color += textureLod(in_history_tx, vec2(uv_12.x, uv_0.y), 0.0) * weight_cross.x;
+ color += textureLod(in_history_tx, vec2(uv_0.x, uv_12.y), 0.0) * weight_cross.y;
+ color += textureLod(in_history_tx, vec2(uv_3.x, uv_12.y), 0.0) * weight_cross.z;
+ color += textureLod(in_history_tx, vec2(uv_12.x, uv_3.y), 0.0) * weight_cross.w;
+ /* Re-normalize for the removed corners. */
+ color /= (weight_center + sum(weight_cross));
+#endif
+ /* NOTE(fclem): Opacity is wrong on purpose. Final Opacity does not rely on history. */
+ return DofSample(color.xyzz, color.w);
+}
+
+/* Modulate the history color to avoid ghosting artifact. */
+DofSample dof_amend_history(DofNeighborhoodMinMax bbox, DofSample history, DofSample src)
+{
+#if 0
+ /* Clip instead of clamping to avoid color accumulating in the AABB corners. */
+ vec3 clip_dir = src.color.rgb - history.color.rgb;
+
+ float t = line_aabb_clipping_dist(
+ history.color.rgb, clip_dir, bbox.min.color.rgb, bbox.max.color.rgb);
+ history.color.rgb += clip_dir * saturate(t);
+#else
+ /* More responsive. */
+ history.color = clamp(history.color, bbox.min.color, bbox.max.color);
+#endif
+ /* Clamp CoC to reduce convergence time. Otherwise the result is laggy. */
+ history.coc = clamp(history.coc, bbox.min.coc, bbox.max.coc);
+
+ return history;
+}
+
+float dof_history_blend_factor(
+ float velocity, vec2 texel, DofNeighborhoodMinMax bbox, DofSample src, DofSample dst)
+{
+ float luma_min = bbox.min.color.x;
+ float luma_max = bbox.max.color.x;
+ float luma_incoming = src.color.x;
+ float luma_history = dst.color.x;
+
+ /* 5% of incoming color by default. */
+ float blend = 0.05;
+ /* Blend less history if the pixel has substantial velocity. */
+ /* NOTE(fclem): velocity threshold multiplied by 2 because of half resolution. */
+ blend = mix(blend, 0.20, saturate(velocity * 0.02 * 2.0));
+ /**
+ * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 43)
+ * Bias towards history if incoming pixel is near clamping. Reduces flicker.
+ */
+ float distance_to_luma_clip = min_v2(vec2(luma_history - luma_min, luma_max - luma_history));
+ /* Divide by bbox size to get a factor. 2 factor to compensate the line above. */
+ distance_to_luma_clip *= 2.0 * safe_rcp(luma_max - luma_min);
+ /* Linearly blend when history gets below to 25% of the bbox size. */
+ blend *= saturate(distance_to_luma_clip * 4.0 + 0.1);
+ /* Progressively discard history until history CoC is twice as big as the filtered CoC.
+ * Note we use absolute diff here because we are not comparing neighbors and thus do not risk to
+ * dilate thin features like hair (slide 19). */
+ float coc_diff_ratio = saturate(abs(src.coc - dst.coc) / max(1.0, abs(src.coc)));
+ blend = mix(blend, 1.0, coc_diff_ratio);
+ /* Discard out of view history. */
+ if (any(lessThan(texel, vec2(0))) ||
+ any(greaterThanEqual(texel, vec2(imageSize(out_history_img))))) {
+ blend = 1.0;
+ }
+ /* Discard history if invalid. */
+ if (use_history == false) {
+ blend = 1.0;
+ }
+ return blend;
+}
+
+void main()
+{
+ dof_cache_init();
+
+ ivec2 src_texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /**
+ * Naming convention is taken from the film implementation.
+ * SRC is incoming new data.
+ * DST is history data.
+ */
+ DofSample src = dof_spatial_filtering();
+
+ /* Reproject by finding where this pixel was in the previous frame. */
+ vec2 motion = dof_pixel_history_motion_vector(src_texel);
+ vec2 history_texel = vec2(src_texel) + motion;
+
+ float velocity = length(motion);
+
+ DofSample dst = dof_sample_history(history_texel);
+
+ /* Get local color bounding box of source neighborhood. */
+ DofNeighborhoodMinMax bbox = dof_neighbor_boundbox();
+
+ float blend = dof_history_blend_factor(velocity, history_texel, bbox, src, dst);
+
+ dst = dof_amend_history(bbox, dst, src);
+
+ /* Luma weighted blend to reduce flickering. */
+ float weight_dst = dof_luma_weight(dst.color.x) * (1.0 - blend);
+ float weight_src = dof_luma_weight(src.color.x) * (blend);
+
+ DofSample result;
+ /* Weighted blend. */
+ result.color = vec4(dst.color.rgb, dst.coc) * weight_dst +
+ vec4(src.color.rgb, src.coc) * weight_src;
+ result.color /= weight_src + weight_dst;
+
+ /* Save history for next iteration. Still in YCoCg space with CoC in alpha. */
+ imageStore(out_history_img, src_texel, result.color);
+
+ /* Un-swizzle. */
+ result.coc = result.color.a;
+ /* Clamp opacity since we don't store it in history. */
+ result.color.a = clamp(src.color.a, bbox.min.color.a, bbox.max.color.a);
+
+ result.color = colorspace_scene_linear_from_YCoCg(result.color);
+
+ imageStore(out_color_img, src_texel, result.color);
+ imageStore(out_coc_img, src_texel, vec4(result.coc));
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl
new file mode 100644
index 00000000000..dba8b5fd79d
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl
@@ -0,0 +1,97 @@
+
+/**
+ * Tile dilate pass: Takes the 8x8 Tiles buffer and converts dilates the tiles with large CoC to
+ * their neighborhood. This pass is repeated multiple time until the maximum CoC can be covered.
+ *
+ * Input & Output:
+ * - Separated foreground and background CoC. 1/8th of half-res resolution. So 1/16th of full-res.
+ **/
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+/* Error introduced by the random offset of the gathering kernel's center. */
+const float bluring_radius_error = 1.0 + 1.0 / (float(DOF_GATHER_RING_COUNT) + 0.5);
+const float tile_to_fullres_factor = float(DOF_TILES_SIZE * 2);
+
+void main()
+{
+ ivec2 center_tile_pos = ivec2(gl_GlobalInvocationID.xy);
+
+ CocTile ring_buckets[DOF_DILATE_RING_COUNT];
+
+ for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) {
+ ring_buckets[ring] = dof_coc_tile_init();
+
+ int ring_distance = ring + 1;
+ for (int sample_id = 0; sample_id < 4 * ring_distance; sample_id++) {
+ ivec2 offset = dof_square_ring_sample_offset(ring_distance, sample_id);
+
+ offset *= ring_width_multiplier;
+
+ for (int i = 0; i < 2; i++) {
+ ivec2 adj_tile_pos = center_tile_pos + ((i == 0) ? offset : -offset);
+
+ CocTile adj_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, adj_tile_pos);
+
+ if (DILATE_MODE_MIN_MAX) {
+ /* Actually gather the "absolute" biggest coc but keeping the sign. */
+ ring_buckets[ring].fg_min_coc = min(ring_buckets[ring].fg_min_coc, adj_tile.fg_min_coc);
+ ring_buckets[ring].bg_max_coc = max(ring_buckets[ring].bg_max_coc, adj_tile.bg_max_coc);
+ }
+ else { /* DILATE_MODE_MIN_ABS */
+ ring_buckets[ring].fg_max_coc = max(ring_buckets[ring].fg_max_coc, adj_tile.fg_max_coc);
+ ring_buckets[ring].bg_min_coc = min(ring_buckets[ring].bg_min_coc, adj_tile.bg_min_coc);
+
+ /* Should be tight as possible to reduce gather overhead (see slide 61). */
+ float closest_neighbor_distance = length(max(abs(vec2(offset)) - 1.0, 0.0)) *
+ tile_to_fullres_factor;
+
+ ring_buckets[ring].fg_max_intersectable_coc = max(
+ ring_buckets[ring].fg_max_intersectable_coc,
+ adj_tile.fg_max_intersectable_coc + closest_neighbor_distance);
+ ring_buckets[ring].bg_min_intersectable_coc = min(
+ ring_buckets[ring].bg_min_intersectable_coc,
+ adj_tile.bg_min_intersectable_coc + closest_neighbor_distance);
+ }
+ }
+ }
+ }
+
+ /* Load center tile. */
+ CocTile out_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, center_tile_pos);
+
+ for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) {
+ float ring_distance = float(ring + 1);
+
+ ring_distance = (ring_distance * ring_width_multiplier - 1) * tile_to_fullres_factor;
+
+ if (DILATE_MODE_MIN_MAX) {
+ /* NOTE(fclem): Unsure if both sides of the inequalities have the same unit. */
+ if (-ring_buckets[ring].fg_min_coc * bluring_radius_error > ring_distance) {
+ out_tile.fg_min_coc = min(out_tile.fg_min_coc, ring_buckets[ring].fg_min_coc);
+ }
+
+ if (ring_buckets[ring].bg_max_coc * bluring_radius_error > ring_distance) {
+ out_tile.bg_max_coc = max(out_tile.bg_max_coc, ring_buckets[ring].bg_max_coc);
+ }
+ }
+ else { /* DILATE_MODE_MIN_ABS */
+ /* Find minimum absolute CoC radii that will be intersected for the previously
+ * computed maximum CoC values. */
+ if (-out_tile.fg_min_coc * bluring_radius_error > ring_distance) {
+ out_tile.fg_max_coc = max(out_tile.fg_max_coc, ring_buckets[ring].fg_max_coc);
+ out_tile.fg_max_intersectable_coc = max(out_tile.fg_max_intersectable_coc,
+ ring_buckets[ring].fg_max_intersectable_coc);
+ }
+
+ if (out_tile.bg_max_coc * bluring_radius_error > ring_distance) {
+ out_tile.bg_min_coc = min(out_tile.bg_min_coc, ring_buckets[ring].bg_min_coc);
+ out_tile.bg_min_intersectable_coc = min(out_tile.bg_min_intersectable_coc,
+ ring_buckets[ring].bg_min_intersectable_coc);
+ }
+ }
+ }
+
+ ivec2 texel_out = ivec2(gl_GlobalInvocationID.xy);
+ dof_coc_tile_store(out_tiles_fg_img, out_tiles_bg_img, texel_out, out_tile);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl
new file mode 100644
index 00000000000..88737ade386
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl
@@ -0,0 +1,78 @@
+
+/**
+ * Tile flatten pass: Takes the halfres CoC buffer and converts it to 8x8 tiles.
+ *
+ * Output min and max values for each tile and for both foreground & background.
+ * Also outputs min intersectable CoC for the background, which is the minimum CoC
+ * that comes from the background pixels.
+ *
+ * Input:
+ * - Half-resolution Circle of confusion. Out of setup pass.
+ * Output:
+ * - Separated foreground and background CoC. 1/8th of half-res resolution. So 1/16th of full-res.
+ */
+
+#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl)
+
+/**
+ * In order to use atomic operations, we have to use uints. But this means having to deal with the
+ * negative number ourselves. Luckily, each ground have a nicely defined range of values we can
+ * remap to positive float.
+ */
+shared uint fg_min_coc;
+shared uint fg_max_coc;
+shared uint fg_max_intersectable_coc;
+shared uint bg_min_coc;
+shared uint bg_max_coc;
+shared uint bg_min_intersectable_coc;
+
+const uint dof_tile_large_coc_uint = floatBitsToUint(dof_tile_large_coc);
+
+void main()
+{
+ if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) {
+ /* NOTE: Min/Max flipped because of inverted fg_coc sign. */
+ fg_min_coc = floatBitsToUint(0.0);
+ fg_max_coc = dof_tile_large_coc_uint;
+ fg_max_intersectable_coc = dof_tile_large_coc_uint;
+ bg_min_coc = dof_tile_large_coc_uint;
+ bg_max_coc = floatBitsToUint(0.0);
+ bg_min_intersectable_coc = dof_tile_large_coc_uint;
+ }
+ barrier();
+
+ ivec2 sample_texel = min(ivec2(gl_GlobalInvocationID.xy), textureSize(coc_tx, 0).xy - 1);
+ vec2 sample_data = texelFetch(coc_tx, sample_texel, 0).rg;
+
+ float sample_coc = sample_data.x;
+ uint fg_coc = floatBitsToUint(max(-sample_coc, 0.0));
+ /* NOTE: atomicMin/Max flipped because of inverted fg_coc sign. */
+ atomicMax(fg_min_coc, fg_coc);
+ atomicMin(fg_max_coc, fg_coc);
+ atomicMin(fg_max_intersectable_coc, (sample_coc < 0.0) ? fg_coc : dof_tile_large_coc_uint);
+
+ uint bg_coc = floatBitsToUint(max(sample_coc, 0.0));
+ atomicMin(bg_min_coc, bg_coc);
+ atomicMax(bg_max_coc, bg_coc);
+ atomicMin(bg_min_intersectable_coc, (sample_coc > 0.0) ? bg_coc : dof_tile_large_coc_uint);
+
+ barrier();
+
+ if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) {
+ if (fg_max_intersectable_coc == dof_tile_large_coc_uint) {
+ fg_max_intersectable_coc = floatBitsToUint(0.0);
+ }
+
+ CocTile tile;
+ /* Foreground sign is flipped since we compare unsigned representation. */
+ tile.fg_min_coc = -uintBitsToFloat(fg_min_coc);
+ tile.fg_max_coc = -uintBitsToFloat(fg_max_coc);
+ tile.fg_max_intersectable_coc = -uintBitsToFloat(fg_max_intersectable_coc);
+ tile.bg_min_coc = uintBitsToFloat(bg_min_coc);
+ tile.bg_max_coc = uintBitsToFloat(bg_max_coc);
+ tile.bg_min_intersectable_coc = uintBitsToFloat(bg_min_intersectable_coc);
+
+ ivec2 tile_co = ivec2(gl_WorkGroupID.xy);
+ dof_coc_tile_store(out_tiles_fg_img, out_tiles_bg_img, tile_co, tile);
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
new file mode 100644
index 00000000000..120edd9c35e
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_cryptomatte_post_comp.glsl
@@ -0,0 +1,77 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+#define CRYPTOMATTE_LEVELS_MAX 16
+
+void cryptomatte_load_samples(ivec2 texel, int layer, out vec2 samples[CRYPTOMATTE_LEVELS_MAX])
+{
+ int pass_len = divide_ceil(cryptomatte_samples_per_layer, 2);
+ int layer_id = layer * pass_len;
+
+ /* Read all samples from the cryptomatte layer. */
+ for (int p = 0; p < pass_len; p++) {
+ vec4 pass_sample = imageLoad(cryptomatte_img, ivec3(texel, p + layer_id));
+ samples[p * 2] = pass_sample.xy;
+ samples[p * 2 + 1] = pass_sample.zw;
+ }
+ for (int i = pass_len * 2; i < CRYPTOMATTE_LEVELS_MAX; i++) {
+ samples[i] = vec2(0.0);
+ }
+}
+
+void cryptomatte_sort_samples(inout vec2 samples[CRYPTOMATTE_LEVELS_MAX])
+{
+ /* Sort samples. Lame implementation, can be replaced with a more efficient algorithm. */
+ for (int i = 0; i < cryptomatte_samples_per_layer - 1 && samples[i].y != 0.0; i++) {
+ int highest_index = i;
+ float highest_weight = samples[i].y;
+ for (int j = i + 1; j < cryptomatte_samples_per_layer && samples[j].y != 0.0; j++) {
+ if (samples[j].y > highest_weight) {
+ highest_index = j;
+ highest_weight = samples[j].y;
+ }
+ };
+
+ if (highest_index != i) {
+ vec2 tmp = samples[i];
+ samples[i] = samples[highest_index];
+ samples[highest_index] = tmp;
+ }
+ }
+}
+void cryptomatte_normalize_weight(float total_weight, inout vec2 samples[CRYPTOMATTE_LEVELS_MAX])
+{
+ for (int i = 0; i < CRYPTOMATTE_LEVELS_MAX; i++) {
+ samples[i].y /= total_weight;
+ }
+}
+
+void cryptomatte_store_samples(ivec2 texel, int layer, in vec2 samples[CRYPTOMATTE_LEVELS_MAX])
+{
+ int pass_len = divide_ceil(cryptomatte_samples_per_layer, 2);
+ int layer_id = layer * pass_len;
+
+ /* Store samples back to the cryptomatte layer. */
+ for (int p = 0; p < pass_len; p++) {
+ vec4 pass_sample;
+ pass_sample.xy = samples[p * 2];
+ pass_sample.zw = samples[p * 2 + 1];
+ imageStore(cryptomatte_img, ivec3(texel, p + layer_id), pass_sample);
+ }
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ for (int layer = 0; layer < cryptomatte_layer_len; layer++) {
+ vec2 samples[CRYPTOMATTE_LEVELS_MAX];
+ cryptomatte_load_samples(texel, layer, samples);
+ cryptomatte_sort_samples(samples);
+ /* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */
+ float weight = imageLoad(
+ weight_img,
+ ivec3(texel % imageSize(weight_img).xy, FILM_WEIGHT_LAYER_ACCUMULATION))
+ .x;
+ cryptomatte_normalize_weight(weight, samples);
+ cryptomatte_store_samples(texel, layer, samples);
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
index 6716c0f126e..e2aaf9128a5 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_frag.glsl
@@ -4,26 +4,32 @@
void main()
{
- ivec2 texel_film = ivec2(gl_FragCoord.xy);
+ ivec2 texel_film = ivec2(gl_FragCoord.xy) - film_buf.offset;
float out_depth;
if (film_buf.display_only) {
out_depth = imageLoad(depth_img, texel_film).r;
if (film_buf.display_id == -1) {
- out_color = imageLoad(in_combined_img, texel_film);
+ out_color = texelFetch(in_combined_tx, texel_film, 0);
}
- else if (film_buf.display_is_value) {
+ else if (film_buf.display_storage_type == PASS_STORAGE_VALUE) {
out_color.rgb = imageLoad(value_accum_img, ivec3(texel_film, film_buf.display_id)).rrr;
out_color.a = 1.0;
}
- else {
+ else if (film_buf.display_storage_type == PASS_STORAGE_COLOR) {
out_color = imageLoad(color_accum_img, ivec3(texel_film, film_buf.display_id));
}
+ else /* PASS_STORAGE_CRYPTOMATTE */ {
+ out_color = cryptomatte_false_color(
+ imageLoad(cryptomatte_img, ivec3(texel_film, film_buf.display_id)).r);
+ }
}
else {
film_process_data(texel_film, out_color, out_depth);
}
gl_FragDepth = get_depth_from_view_z(-out_depth);
+
+ gl_FragDepth = film_display_depth_ammend(texel_film, gl_FragDepth);
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl
index 03af34f27ef..21b9a83abb9 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl
@@ -4,7 +4,11 @@
**/
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_cryptomatte_lib.glsl)
/* Return scene linear Z depth from the camera or radial depth for panoramic cameras. */
float film_depth_convert_to_scene(float depth)
@@ -16,6 +20,25 @@ float film_depth_convert_to_scene(float depth)
return abs(get_view_z_from_depth(depth));
}
+/* Load a texture sample in a specific format. Combined pass needs to use this. */
+vec4 film_texelfetch_as_YCoCg_opacity(sampler2D tx, ivec2 texel)
+{
+ vec4 color = texelFetch(combined_tx, texel, 0);
+ /* Convert transmittance to opacity. */
+ color.a = saturate(1.0 - color.a);
+ /* Transform to YCoCg for accumulation. */
+ color.rgb = colorspace_YCoCg_from_scene_linear(color.rgb);
+ return color;
+}
+
+/* Returns a weight based on Luma to reduce the flickering introduced by high energy pixels. */
+float film_luma_weight(float luma)
+{
+ /* Slide 20 of "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014. */
+ /* To preserve more details in dark areas, we use a bigger bias. */
+ return 1.0 / (4.0 + luma * film_buf.exposure_scale);
+}
+
/* -------------------------------------------------------------------- */
/** \name Filter
* \{ */
@@ -33,9 +56,9 @@ FilmSample film_sample_get(int sample_n, ivec2 texel_film)
# endif
FilmSample film_sample = film_buf.samples[sample_n];
- film_sample.texel += texel_film;
+ film_sample.texel += texel_film + film_buf.offset;
/* Use extend on borders. */
- film_sample.texel = clamp(film_sample.texel, ivec2(0, 0), film_buf.extent - 1);
+ film_sample.texel = clamp(film_sample.texel, ivec2(0, 0), film_buf.render_extent - 1);
/* TODO(fclem): Panoramic projection will need to compute the sample weight in the shader
* instead of precomputing it on CPU. */
@@ -83,12 +106,18 @@ void film_sample_accum(FilmSample samp, int pass_id, sampler2D tex, inout float
accum += texelFetch(tex, samp.texel, 0).x * samp.weight;
}
-void film_sample_accum(FilmSample samp, int pass_id, sampler2DArray tex, inout vec4 accum)
+void film_sample_accum(
+ FilmSample samp, int pass_id, uint layer, sampler2DArray tex, inout vec4 accum)
{
if (pass_id == -1) {
return;
}
- accum += texelFetch(tex, ivec3(samp.texel, pass_id), 0) * samp.weight;
+ accum += texelFetch(tex, ivec3(samp.texel, layer), 0) * samp.weight;
+}
+
+void film_sample_accum(FilmSample samp, int pass_id, sampler2DArray tex, inout vec4 accum)
+{
+ film_sample_accum(samp, pass_id, pass_id, tex, accum);
}
void film_sample_accum(FilmSample samp, int pass_id, sampler2DArray tex, inout float accum)
@@ -116,18 +145,57 @@ void film_sample_accum_mist(FilmSample samp, inout float accum)
accum += mist * samp.weight;
}
-void film_sample_accum_combined(FilmSample samp, inout vec4 accum)
+void film_sample_accum_combined(FilmSample samp, inout vec4 accum, inout float weight_accum)
{
if (film_buf.combined_id == -1) {
return;
}
- vec4 color = texelFetch(combined_tx, samp.texel, 0);
- /* Convert transmittance to opacity. */
- color.a = saturate(1.0 - color.a);
- /* TODO(fclem) Pre-expose. */
- color.rgb = log2(1.0 + color.rgb);
+ vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, samp.texel);
- accum += color * samp.weight;
+ /* Weight by luma to remove fireflies. */
+ float weight = film_luma_weight(color.x) * samp.weight;
+
+ accum += color * weight;
+ weight_accum += weight;
+}
+
+void film_sample_cryptomatte_accum(FilmSample samp,
+ int layer,
+ sampler2D tex,
+ inout vec2 crypto_samples[4])
+{
+ float hash = texelFetch(tex, samp.texel, 0)[layer];
+ /* Find existing entry. */
+ for (int i = 0; i < 4; i++) {
+ if (crypto_samples[i].x == hash) {
+ crypto_samples[i].y += samp.weight;
+ return;
+ }
+ }
+ /* Overwrite entry with less weight. */
+ for (int i = 0; i < 4; i++) {
+ if (crypto_samples[i].y < samp.weight) {
+ crypto_samples[i] = vec2(hash, samp.weight);
+ return;
+ }
+ }
+}
+
+void film_cryptomatte_layer_accum_and_store(
+ FilmSample dst, ivec2 texel_film, int pass_id, int layer_component, inout vec4 out_color)
+{
+ if (pass_id == -1) {
+ return;
+ }
+ /* x = hash, y = accumed weight. Only keep track of 4 highest weighted samples. */
+ vec2 crypto_samples[4] = vec2[4](vec2(0.0), vec2(0.0), vec2(0.0), vec2(0.0));
+ for (int i = 0; i < film_buf.samples_len; i++) {
+ FilmSample src = film_sample_get(i, texel_film);
+ film_sample_cryptomatte_accum(src, layer_component, cryptomatte_tx, crypto_samples);
+ }
+ for (int i = 0; i < 4; i++) {
+ cryptomatte_store_film_sample(dst, pass_id, crypto_samples[i], out_color);
+ }
}
/** \} */
@@ -136,19 +204,16 @@ void film_sample_accum_combined(FilmSample samp, inout vec4 accum)
/** \name Load/Store Data
* \{ */
-#define WEIGHT_lAYER_ACCUMULATION 0
-#define WEIGHT_lAYER_DISTANCE 1
-
/* Returns the distance used to store nearest interpolation data. */
float film_distance_load(ivec2 texel)
{
/* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */
texel = texel % imageSize(in_weight_img).xy;
- if (film_buf.use_history == false) {
+ if (!film_buf.use_history || film_buf.use_reprojection) {
return 1.0e16;
}
- return imageLoad(in_weight_img, ivec3(texel, WEIGHT_lAYER_DISTANCE)).x;
+ return imageLoad(in_weight_img, ivec3(texel, FILM_WEIGHT_LAYER_DISTANCE)).x;
}
float film_weight_load(ivec2 texel)
@@ -156,46 +221,297 @@ float film_weight_load(ivec2 texel)
/* Repeat texture coordinates as the weight can be optimized to a small portion of the film. */
texel = texel % imageSize(in_weight_img).xy;
- if (film_buf.use_history == false) {
+ if (!film_buf.use_history || film_buf.use_reprojection) {
return 0.0;
}
- return imageLoad(in_weight_img, ivec3(texel, WEIGHT_lAYER_ACCUMULATION)).x;
+ return imageLoad(in_weight_img, ivec3(texel, FILM_WEIGHT_LAYER_ACCUMULATION)).x;
}
-/* Return the motion in pixels. */
-void film_motion_load()
+/* Returns motion in pixel space to retrieve the pixel history. */
+vec2 film_pixel_history_motion_vector(ivec2 texel_sample)
{
- // ivec2 texel_sample = film_sample_get(0, texel_film, distance_sample);
- // vec4 vector = texelFetch(vector_tx, texel_sample);
+ /**
+ * Dilate velocity by using the nearest pixel in a cross pattern.
+ * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 27)
+ */
+ const ivec2 corners[4] = ivec2[4](ivec2(-2, -2), ivec2(2, -2), ivec2(-2, 2), ivec2(2, 2));
+ float min_depth = texelFetch(depth_tx, texel_sample, 0).x;
+ ivec2 nearest_texel = texel_sample;
+ for (int i = 0; i < 4; i++) {
+ ivec2 texel = clamp(texel_sample + corners[i], ivec2(0), textureSize(depth_tx, 0).xy - 1);
+ float depth = texelFetch(depth_tx, texel, 0).x;
+ if (min_depth > depth) {
+ min_depth = depth;
+ nearest_texel = texel;
+ }
+ }
- // vector.xy *= film_buf.extent;
+ vec4 vector = velocity_resolve(vector_tx, nearest_texel, min_depth);
+
+ /* Transform to pixel space. */
+ vector.xy *= vec2(film_buf.extent);
+
+ return vector.xy;
+}
+
+/* \a t is inter-pixel position. 0 means perfectly on a pixel center.
+ * Returns weights in both dimensions.
+ * Multiply each dimension weights to get final pixel weights. */
+void film_get_catmull_rom_weights(vec2 t, out vec2 weights[4])
+{
+ vec2 t2 = t * t;
+ vec2 t3 = t2 * t;
+ float fc = 0.5; /* Catmull-Rom. */
+
+ vec2 fct = t * fc;
+ vec2 fct2 = t2 * fc;
+ vec2 fct3 = t3 * fc;
+ weights[0] = (fct2 * 2.0 - fct3) - fct;
+ weights[1] = (t3 * 2.0 - fct3) + (-t2 * 3.0 + fct2) + 1.0;
+ weights[2] = (-t3 * 2.0 + fct3) + (t2 * 3.0 - (2.0 * fct2)) + fct;
+ weights[3] = fct3 - fct2;
+}
+
+/* Load color using a special filter to avoid losing detail.
+ * \a texel is sample position with subpixel accuracy. */
+vec4 film_sample_catmull_rom(sampler2D color_tx, vec2 input_texel)
+{
+ vec2 center_texel;
+ vec2 inter_texel = modf(input_texel, center_texel);
+ vec2 weights[4];
+ film_get_catmull_rom_weights(inter_texel, weights);
+
+#if 0 /* Reference. 16 Taps. */
+ vec4 color = vec4(0.0);
+ for (int y = 0; y < 4; y++) {
+ for (int x = 0; x < 4; x++) {
+ ivec2 texel = ivec2(center_texel) + ivec2(x, y) - 1;
+ texel = clamp(texel, ivec2(0), textureSize(color_tx, 0).xy - 1);
+ color += texelFetch(color_tx, texel, 0) * weights[x].x * weights[y].y;
+ }
+ }
+ return color;
+
+#elif 1 /* Optimize version. 5 Bilinear Taps. */
+ /**
+ * Use optimized version by leveraging bilinear filtering from hardware sampler and by removing
+ * corner taps.
+ * From "Filmic SMAA" by Jorge Jimenez at Siggraph 2016
+ * http://advances.realtimerendering.com/s2016/Filmic%20SMAA%20v7.pptx
+ */
+ center_texel += 0.5;
+
+ /* Slide 92. */
+ vec2 weight_12 = weights[1] + weights[2];
+ vec2 uv_12 = (center_texel + weights[2] / weight_12) * film_buf.extent_inv;
+ vec2 uv_0 = (center_texel - 1.0) * film_buf.extent_inv;
+ vec2 uv_3 = (center_texel + 2.0) * film_buf.extent_inv;
+
+ vec4 color;
+ vec4 weight_cross = weight_12.xyyx * vec4(weights[0].yx, weights[3].xy);
+ float weight_center = weight_12.x * weight_12.y;
+
+ color = textureLod(color_tx, uv_12, 0.0) * weight_center;
+ color += textureLod(color_tx, vec2(uv_12.x, uv_0.y), 0.0) * weight_cross.x;
+ color += textureLod(color_tx, vec2(uv_0.x, uv_12.y), 0.0) * weight_cross.y;
+ color += textureLod(color_tx, vec2(uv_3.x, uv_12.y), 0.0) * weight_cross.z;
+ color += textureLod(color_tx, vec2(uv_12.x, uv_3.y), 0.0) * weight_cross.w;
+ /* Re-normalize for the removed corners. */
+ return color / (weight_center + sum(weight_cross));
+
+#else /* Nearest interpolation for debugging. 1 Tap. */
+ ivec2 texel = ivec2(center_texel) + ivec2(greaterThan(inter_texel, vec2(0.5)));
+ texel = clamp(texel, ivec2(0), textureSize(color_tx, 0).xy - 1);
+ return texelFetch(color_tx, texel, 0);
+#endif
+}
+
+/* Return history clipping bounding box in YCoCg color space. */
+void film_combined_neighbor_boundbox(ivec2 texel, out vec4 min_c, out vec4 max_c)
+{
+ /* Plus (+) shape offsets. */
+ const ivec2 plus_offsets[5] = ivec2[5](ivec2(0, 0), /* Center */
+ ivec2(-1, 0),
+ ivec2(0, -1),
+ ivec2(1, 0),
+ ivec2(0, 1));
+#if 0
+ /**
+ * Compute Variance of neighborhood as described in:
+ * "An Excursion in Temporal Supersampling" by Marco Salvi at GDC 2016.
+ * and:
+ * "A Survey of Temporal Antialiasing Techniques" by Yang et al.
+ */
+
+ /* First 2 moments. */
+ vec4 mu1 = vec4(0), mu2 = vec4(0);
+ for (int i = 0; i < 5; i++) {
+ vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + plus_offsets[i]);
+ mu1 += color;
+ mu2 += sqr(color);
+ }
+ mu1 *= (1.0 / 5.0);
+ mu2 *= (1.0 / 5.0);
+
+ /* Extent scaling. Range [0.75..1.25].
+ * Balance between more flickering (0.75) or more ghosting (1.25). */
+ const float gamma = 1.25;
+ /* Standard deviation. */
+ vec4 sigma = sqrt(abs(mu2 - sqr(mu1)));
+ /* eq. 6 in "A Survey of Temporal Antialiasing Techniques". */
+ min_c = mu1 - gamma * sigma;
+ max_c = mu1 + gamma * sigma;
+#else
+ /**
+ * Simple bounding box calculation in YCoCg as described in:
+ * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014
+ */
+ min_c = vec4(1e16);
+ max_c = vec4(-1e16);
+ for (int i = 0; i < 5; i++) {
+ vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + plus_offsets[i]);
+ min_c = min(min_c, color);
+ max_c = max(max_c, color);
+ }
+ /* (Slide 32) Simple clamp to min/max of 8 neighbors results in 3x3 box artifacts.
+ * Round bbox shape by averaging 2 different min/max from 2 different neighborhood. */
+ vec4 min_c_3x3 = min_c;
+ vec4 max_c_3x3 = max_c;
+ const ivec2 corners[4] = ivec2[4](ivec2(-1, -1), ivec2(1, -1), ivec2(-1, 1), ivec2(1, 1));
+ for (int i = 0; i < 4; i++) {
+ vec4 color = film_texelfetch_as_YCoCg_opacity(combined_tx, texel + corners[i]);
+ min_c_3x3 = min(min_c_3x3, color);
+ max_c_3x3 = max(max_c_3x3, color);
+ }
+ min_c = (min_c + min_c_3x3) * 0.5;
+ max_c = (max_c + max_c_3x3) * 0.5;
+#endif
+}
+
+/* 1D equivalent of line_aabb_clipping_dist(). */
+float film_aabb_clipping_dist_alpha(float origin, float direction, float aabb_min, float aabb_max)
+{
+ if (abs(direction) < 1e-5) {
+ return 0.0;
+ }
+ float nearest_plane = (direction > 0.0) ? aabb_min : aabb_max;
+ return (nearest_plane - origin) / direction;
+}
+
+/* Modulate the history color to avoid ghosting artifact. */
+vec4 film_amend_combined_history(
+ vec4 min_color, vec4 max_color, vec4 color_history, vec4 src_color, ivec2 src_texel)
+{
+ /* Clip instead of clamping to avoid color accumulating in the AABB corners. */
+ vec4 clip_dir = src_color - color_history;
+
+ float t = line_aabb_clipping_dist(color_history.rgb, clip_dir.rgb, min_color.rgb, max_color.rgb);
+ color_history.rgb += clip_dir.rgb * saturate(t);
+
+ /* Clip alpha on its own to avoid interference with other channels. */
+ float t_a = film_aabb_clipping_dist_alpha(color_history.a, clip_dir.a, min_color.a, max_color.a);
+ color_history.a += clip_dir.a * saturate(t_a);
+
+ return color_history;
+}
+
+float film_history_blend_factor(float velocity,
+ vec2 texel,
+ float luma_min,
+ float luma_max,
+ float luma_incoming,
+ float luma_history)
+{
+ /* 5% of incoming color by default. */
+ float blend = 0.05;
+ /* Blend less history if the pixel has substantial velocity. */
+ blend = mix(blend, 0.20, saturate(velocity * 0.02));
+ /**
+ * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 43)
+ * Bias towards history if incoming pixel is near clamping. Reduces flicker.
+ */
+ float distance_to_luma_clip = min_v2(vec2(luma_history - luma_min, luma_max - luma_history));
+ /* Divide by bbox size to get a factor. 2 factor to compensate the line above. */
+ distance_to_luma_clip *= 2.0 * safe_rcp(luma_max - luma_min);
+ /* Linearly blend when history gets below to 25% of the bbox size. */
+ blend *= saturate(distance_to_luma_clip * 4.0 + 0.1);
+ /* Discard out of view history. */
+ if (any(lessThan(texel, vec2(0))) || any(greaterThanEqual(texel, film_buf.extent))) {
+ blend = 1.0;
+ }
+ /* Discard history if invalid. */
+ if (film_buf.use_history == false) {
+ blend = 1.0;
+ }
+ return blend;
}
/* Returns resolved final color. */
-void film_store_combined(FilmSample dst, vec4 color, inout vec4 display)
+void film_store_combined(
+ FilmSample dst, ivec2 src_texel, vec4 color, float color_weight, inout vec4 display)
{
if (film_buf.combined_id == -1) {
return;
}
- /* Could we assume safe color from earlier pass? */
- color = safe_color(color);
- if (false) {
- /* Re-projection using motion vectors. */
- // ivec2 texel_combined = texel_film + film_motion_load(texel_film);
- // float weight_combined = film_weight_load(texel_combined);
+ vec4 color_src, color_dst;
+ float weight_src, weight_dst;
+
+ /* Undo the weighting to get final spatialy-filtered color. */
+ color_src = color / color_weight;
+
+ if (film_buf.use_reprojection) {
+ /* Interactive accumulation. Do reprojection and Temporal Anti-Aliasing. */
+
+ /* Reproject by finding where this pixel was in the previous frame. */
+ vec2 motion = film_pixel_history_motion_vector(src_texel);
+ vec2 history_texel = vec2(dst.texel) + motion;
+
+ float velocity = length(motion);
+
+ /* Load weight if it is not uniform across the whole buffer (i.e: upsampling, panoramic). */
+ // dst.weight = film_weight_load(texel_combined);
+
+ color_dst = film_sample_catmull_rom(in_combined_tx, history_texel);
+ color_dst.rgb = colorspace_YCoCg_from_scene_linear(color_dst.rgb);
+
+ /* Get local color bounding box of source neighborhood. */
+ vec4 min_color, max_color;
+ film_combined_neighbor_boundbox(src_texel, min_color, max_color);
+
+ float blend = film_history_blend_factor(
+ velocity, history_texel, min_color.x, max_color.x, color_src.x, color_dst.x);
+
+ color_dst = film_amend_combined_history(min_color, max_color, color_dst, color_src, src_texel);
+
+ /* Luma weighted blend to avoid flickering. */
+ weight_dst = film_luma_weight(color_dst.x) * (1.0 - blend);
+ weight_src = film_luma_weight(color_src.x) * (blend);
}
-#ifdef USE_NEIGHBORHOOD_CLAMPING
- /* Only do that for combined pass as it has a non-negligeable performance impact. */
- // color = clamp_bbox(color, min, max);
-#endif
+ else {
+ /* Everything is static. Use render accumulation. */
+ color_dst = texelFetch(in_combined_tx, dst.texel, 0);
+ color_dst.rgb = colorspace_YCoCg_from_scene_linear(color_dst.rgb);
+
+ /* Luma weighted blend to avoid flickering. */
+ weight_dst = film_luma_weight(color_dst.x) * dst.weight;
+ weight_src = color_weight;
+ }
+ /* Weighted blend. */
+ color = color_dst * weight_dst + color_src * weight_src;
+ color /= weight_src + weight_dst;
- vec4 dst_color = imageLoad(in_combined_img, dst.texel);
+ color.rgb = colorspace_scene_linear_from_YCoCg(color.rgb);
- color = (dst_color * dst.weight + color) * dst.weight_sum_inv;
+ /* Fix alpha not accumulating to 1 because of float imprecision. */
+ if (color.a > 0.995) {
+ color.a = 1.0;
+ }
- /* TODO(fclem) undo Pre-expose. */
- // color.rgb = exp2(color.rgb) - 1.0;
+ /* Filter NaNs. */
+ if (any(isnan(color))) {
+ color = vec4(0.0, 0.0, 0.0, 1.0);
+ }
if (film_buf.display_id == -1) {
display = color;
@@ -213,6 +529,11 @@ void film_store_color(FilmSample dst, int pass_id, vec4 color, inout vec4 displa
color = (data_film * dst.weight + color) * dst.weight_sum_inv;
+ /* Filter NaNs. */
+ if (any(isnan(color))) {
+ color = vec4(0.0, 0.0, 0.0, 1.0);
+ }
+
if (film_buf.display_id == pass_id) {
display = color;
}
@@ -229,6 +550,11 @@ void film_store_value(FilmSample dst, int pass_id, float value, inout vec4 displ
value = (data_film * dst.weight + value) * dst.weight_sum_inv;
+ /* Filter NaNs. */
+ if (isnan(value)) {
+ value = 0.0;
+ }
+
if (film_buf.display_id == pass_id) {
display = vec4(value, value, value, 1.0);
}
@@ -261,12 +587,26 @@ void film_store_depth(ivec2 texel_film, float value, out float out_depth)
void film_store_distance(ivec2 texel, float value)
{
- imageStore(out_weight_img, ivec3(texel, WEIGHT_lAYER_DISTANCE), vec4(value));
+ imageStore(out_weight_img, ivec3(texel, FILM_WEIGHT_LAYER_DISTANCE), vec4(value));
}
void film_store_weight(ivec2 texel, float value)
{
- imageStore(out_weight_img, ivec3(texel, WEIGHT_lAYER_ACCUMULATION), vec4(value));
+ imageStore(out_weight_img, ivec3(texel, FILM_WEIGHT_LAYER_ACCUMULATION), vec4(value));
+}
+
+float film_display_depth_ammend(ivec2 texel, float depth)
+{
+ /* This effectively offsets the depth of the whole 2x2 region to the lowest value of the region
+ * twice. One for X and one for Y direction. */
+ /* TODO(fclem): This could be improved as it gives flickering result at depth discontinuity.
+ * But this is the quickest stable result I could come with for now. */
+#ifdef GPU_FRAGMENT_SHADER
+ depth += fwidth(depth);
+#endif
+ /* Small offset to avoid depth test lessEqual failing because of all the conversions loss. */
+ depth += 2.4e-7 * 4.0;
+ return saturate(depth);
}
/** \} */
@@ -290,16 +630,32 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
/* NOTE: We split the accumulations into separate loops to avoid using too much registers and
* maximize occupancy. */
+ if (film_buf.combined_id != -1) {
+ /* NOTE: Do weight accumulation again since we use custom weights. */
+ float weight_accum = 0.0;
+ vec4 combined_accum = vec4(0.0);
+
+ FilmSample src;
+ for (int i = film_buf.samples_len - 1; i >= 0; i--) {
+ src = film_sample_get(i, texel_film);
+ film_sample_accum_combined(src, combined_accum, weight_accum);
+ }
+ /* NOTE: src.texel is center texel in incoming data buffer. */
+ film_store_combined(dst, src.texel, combined_accum, weight_accum, out_color);
+ }
+
if (film_buf.has_data) {
- float film_weight = film_distance_load(texel_film);
+ float film_distance = film_distance_load(texel_film);
/* Get sample closest to target texel. It is always sample 0. */
FilmSample film_sample = film_sample_get(0, texel_film);
- if (film_sample.weight < film_weight) {
- float depth = texelFetch(depth_tx, film_sample.texel, 0).x;
+ if (film_buf.use_reprojection || film_sample.weight < film_distance) {
vec4 normal = texelFetch(normal_tx, film_sample.texel, 0);
- vec4 vector = texelFetch(vector_tx, film_sample.texel, 0);
+ float depth = texelFetch(depth_tx, film_sample.texel, 0).x;
+ vec4 vector = velocity_resolve(vector_tx, film_sample.texel, depth);
+ /* Transform to pixel space. */
+ vector *= vec4(film_buf.render_extent, -film_buf.render_extent);
film_store_depth(texel_film, depth, out_depth);
film_store_data(texel_film, film_buf.normal_id, normal, out_color);
@@ -311,16 +667,6 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
}
}
- if (film_buf.combined_id != -1) {
- vec4 combined_accum = vec4(0.0);
-
- for (int i = 0; i < film_buf.samples_len; i++) {
- FilmSample src = film_sample_get(i, texel_film);
- film_sample_accum_combined(src, combined_accum);
- }
- film_store_combined(dst, combined_accum, out_color);
- }
-
if (film_buf.any_render_pass_1) {
vec4 diffuse_light_accum = vec4(0.0);
vec4 specular_light_accum = vec4(0.0);
@@ -329,8 +675,16 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
for (int i = 0; i < film_buf.samples_len; i++) {
FilmSample src = film_sample_get(i, texel_film);
- film_sample_accum(src, film_buf.diffuse_light_id, diffuse_light_tx, diffuse_light_accum);
- film_sample_accum(src, film_buf.specular_light_id, specular_light_tx, specular_light_accum);
+ film_sample_accum(src,
+ film_buf.diffuse_light_id,
+ RENDER_PASS_LAYER_DIFFUSE_LIGHT,
+ light_tx,
+ diffuse_light_accum);
+ film_sample_accum(src,
+ film_buf.specular_light_id,
+ RENDER_PASS_LAYER_SPECULAR_LIGHT,
+ light_tx,
+ specular_light_accum);
film_sample_accum(src, film_buf.volume_light_id, volume_light_tx, volume_light_accum);
film_sample_accum(src, film_buf.emission_id, emission_tx, emission_accum);
}
@@ -384,4 +738,18 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth
}
film_store_value(dst, film_buf.aov_value_id + aov, aov_accum, out_color);
}
+
+ if (film_buf.cryptomatte_samples_len != 0) {
+ /* Cryptomatte passes cannot be cleared by a weighted store like other passes. */
+ if (!film_buf.use_history || film_buf.use_reprojection) {
+ cryptomatte_clear_samples(dst);
+ }
+
+ film_cryptomatte_layer_accum_and_store(
+ dst, texel_film, film_buf.cryptomatte_object_id, 0, out_color);
+ film_cryptomatte_layer_accum_and_store(
+ dst, texel_film, film_buf.cryptomatte_asset_id, 1, out_color);
+ film_cryptomatte_layer_accum_and_store(
+ dst, texel_film, film_buf.cryptomatte_material_id, 2, out_color);
+ }
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl
new file mode 100644
index 00000000000..e93d0f472fa
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_debug_frag.glsl
@@ -0,0 +1,24 @@
+
+/**
+ * Debug hiz down sampling pass.
+ * Output red if above any max pixels, blue otherwise.
+ */
+
+void main()
+{
+ ivec2 texel = ivec2(gl_FragCoord.xy);
+
+ float depth0 = texelFetch(hiz_tx, texel, 0).r;
+
+ vec4 color = vec4(0.1, 0.1, 1.0, 1.0);
+ for (int i = 1; i < HIZ_MIP_COUNT; i++) {
+ ivec2 lvl_texel = texel / ivec2(uvec2(1) << uint(i));
+ lvl_texel = min(lvl_texel, textureSize(hiz_tx, i) - 1);
+ if (texelFetch(hiz_tx, lvl_texel, i).r < depth0) {
+ color = vec4(1.0, 0.1, 0.1, 1.0);
+ break;
+ }
+ }
+ out_debug_color_add = vec4(color.rgb, 0.0) * 0.2;
+ out_debug_color_mul = color;
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl
new file mode 100644
index 00000000000..597bc73e2ad
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_hiz_update_comp.glsl
@@ -0,0 +1,121 @@
+
+/**
+ * Shader that down-sample depth buffer, creating a Hierarchical-Z buffer.
+ * Saves max value of each 2x2 texel in the mipmap above the one we are
+ * rendering to. Adapted from
+ * http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/
+ *
+ * Major simplification has been made since we pad the buffer to always be
+ * bigger than input to avoid mipmapping misalignement.
+ *
+ * Start by copying the base level by quad loading the depth.
+ * Then each thread compute it's local depth for level 1.
+ * After that we use shared variables to do inter thread comunication and
+ * downsample to max level.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+shared float local_depths[gl_WorkGroupSize.y][gl_WorkGroupSize.x];
+
+/* Load values from the previous lod level. */
+vec4 load_local_depths(ivec2 pixel)
+{
+ pixel *= 2;
+ return vec4(local_depths[pixel.y + 1][pixel.x + 0],
+ local_depths[pixel.y + 1][pixel.x + 1],
+ local_depths[pixel.y + 0][pixel.x + 1],
+ local_depths[pixel.y + 0][pixel.x + 0]);
+}
+
+void store_local_depth(ivec2 pixel, float depth)
+{
+ local_depths[pixel.y][pixel.x] = depth;
+}
+
+void main()
+{
+ ivec2 local_px = ivec2(gl_LocalInvocationID.xy);
+ /* Bottom left corner of the kernel. */
+ ivec2 kernel_origin = ivec2(gl_WorkGroupSize.xy * gl_WorkGroupID.xy);
+
+ /* Copy level 0. */
+ ivec2 src_px = ivec2(kernel_origin + local_px) * 2;
+ vec2 samp_co = (vec2(src_px) + 0.5) / vec2(textureSize(depth_tx, 0));
+ vec4 samp = textureGather(depth_tx, samp_co);
+
+ if (update_mip_0) {
+ imageStore(out_mip_0, src_px + ivec2(0, 1), samp.xxxx);
+ imageStore(out_mip_0, src_px + ivec2(1, 1), samp.yyyy);
+ imageStore(out_mip_0, src_px + ivec2(1, 0), samp.zzzz);
+ imageStore(out_mip_0, src_px + ivec2(0, 0), samp.wwww);
+ }
+
+ /* Level 1. (No load) */
+ float max_depth = max_v4(samp);
+ ivec2 dst_px = ivec2(kernel_origin + local_px);
+ imageStore(out_mip_1, dst_px, vec4(max_depth));
+ store_local_depth(local_px, max_depth);
+
+ /* Level 2-5. */
+ bool active_thread;
+ int mask_shift = 1;
+
+#define downsample_level(out_mip__, lod_) \
+ active_thread = all(lessThan(local_px, gl_WorkGroupSize.xy >> uint(mask_shift))); \
+ barrier(); /* Wait for previous writes to finish. */ \
+ if (active_thread) { \
+ max_depth = max_v4(load_local_depths(local_px)); \
+ dst_px = ivec2((kernel_origin >> mask_shift) + local_px); \
+ imageStore(out_mip__, dst_px, vec4(max_depth)); \
+ } \
+ barrier(); /* Wait for previous reads to finish. */ \
+ if (active_thread) { \
+ store_local_depth(local_px, max_depth); \
+ } \
+ mask_shift++;
+
+ downsample_level(out_mip_2, 2);
+ downsample_level(out_mip_3, 3);
+ downsample_level(out_mip_4, 4);
+ downsample_level(out_mip_5, 5);
+
+ /* Since we pad the destination texture, the mip size is equal to the dispatch size. */
+ uint tile_count = uint(imageSize(out_mip_5).x * imageSize(out_mip_5).y);
+ /* Let the last tile handle the remaining LOD. */
+ bool last_tile = atomicAdd(finished_tile_counter, 1u) + 1u < tile_count;
+ if (last_tile == false) {
+ return;
+ }
+ finished_tile_counter = 0u;
+
+ ivec2 iter = divide_ceil(imageSize(out_mip_5), ivec2(gl_WorkGroupSize * 2u));
+ ivec2 image_border = imageSize(out_mip_5) - 1;
+ for (int y = 0; y < iter.y; y++) {
+ for (int x = 0; x < iter.x; x++) {
+ /* Load result of the other work groups. */
+ kernel_origin = ivec2(gl_WorkGroupSize) * ivec2(x, y);
+ src_px = ivec2(kernel_origin + local_px) * 2;
+ vec4 samp;
+ samp.x = imageLoad(out_mip_5, min(src_px + ivec2(0, 1), image_border)).x;
+ samp.y = imageLoad(out_mip_5, min(src_px + ivec2(1, 1), image_border)).x;
+ samp.z = imageLoad(out_mip_5, min(src_px + ivec2(1, 0), image_border)).x;
+ samp.w = imageLoad(out_mip_5, min(src_px + ivec2(0, 0), image_border)).x;
+ /* Level 6. */
+ float max_depth = max_v4(samp);
+ ivec2 dst_px = ivec2(kernel_origin + local_px);
+ imageStore(out_mip_6, dst_px, vec4(max_depth));
+ store_local_depth(local_px, max_depth);
+
+ mask_shift = 1;
+
+ /* Level 7. */
+ downsample_level(out_mip_7, 7);
+
+ /* Limited by OpenGL maximum of 8 image slot. */
+ // downsample_level(out_mip_8, 8);
+ // downsample_level(out_mip_9, 9);
+ // downsample_level(out_mip_10, 10);
+ }
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl
new file mode 100644
index 00000000000..eefc024d0b8
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl
@@ -0,0 +1,54 @@
+
+/**
+ * Debug Shader outputing a gradient of orange - white - blue to mark culling hotspots.
+ * Green pixels are error pixels that are missing lights from the culling pass (i.e: when culling
+ * pass is not conservative enough).
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_FragCoord.xy);
+
+ float depth = texelFetch(hiz_tx, texel, 0).r;
+ float vP_z = get_view_z_from_depth(depth);
+ vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth);
+
+ float light_count = 0.0;
+ uint light_cull = 0u;
+ vec2 px = gl_FragCoord.xy;
+ LIGHT_FOREACH_BEGIN_LOCAL(light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx)
+ {
+ LightData light = light_buf[l_idx];
+ light_cull |= 1u << l_idx;
+ light_count += 1.0;
+ }
+ LIGHT_FOREACH_END
+
+ uint light_nocull = 0u;
+ LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx)
+ {
+ LightData light = light_buf[l_idx];
+ vec3 L;
+ float dist;
+ light_vector_get(light, P, L, dist);
+ if (light_attenuation(light, L, dist) > 0.0) {
+ light_nocull |= 1u << l_idx;
+ }
+ }
+ LIGHT_FOREACH_END
+
+ vec4 color = vec4(heatmap_gradient(light_count / 4.0), 1.0);
+
+ if ((light_cull & light_nocull) != light_nocull) {
+ /* ERROR. Some lights were culled incorrectly. */
+ color = vec4(0.0, 1.0, 0.0, 1.0);
+ }
+
+ out_debug_color_add = vec4(color.rgb, 0.0) * 0.2;
+ out_debug_color_mul = color;
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl
new file mode 100644
index 00000000000..9c12b0e50e6
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl
@@ -0,0 +1,62 @@
+
+/**
+ * Select the visible items inside the active view and put them inside the sorting buffer.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
+
+void main()
+{
+ uint l_idx = gl_GlobalInvocationID.x;
+ if (l_idx >= light_cull_buf.items_count) {
+ return;
+ }
+
+ LightData light = in_light_buf[l_idx];
+
+ /* Do not select 0 power lights. */
+ if (light.influence_radius_max < 1e-8) {
+ return;
+ }
+
+ /* Sun lights are packed at the end of the array. Perform early copy. */
+ if (light.type == LIGHT_SUN) {
+ /* NOTE: We know the index because sun lights are packed at the start of the input buffer. */
+ out_light_buf[light_cull_buf.local_lights_len + l_idx] = light;
+ return;
+ }
+
+ Sphere sphere;
+ switch (light.type) {
+ case LIGHT_SPOT:
+ /* Only for < ~170° Cone due to plane extraction precision. */
+ if (light.spot_tan < 10.0) {
+ Pyramid pyramid = shape_pyramid_non_oblique(
+ light._position,
+ light._position - light._back * light.influence_radius_max,
+ light._right * light.influence_radius_max * light.spot_tan / light.spot_size_inv.x,
+ light._up * light.influence_radius_max * light.spot_tan / light.spot_size_inv.y);
+ if (!intersect_view(pyramid)) {
+ return;
+ }
+ }
+ case LIGHT_RECT:
+ case LIGHT_ELLIPSE:
+ case LIGHT_POINT:
+ sphere = Sphere(light._position, light.influence_radius_max);
+ break;
+ }
+
+ /* TODO(fclem): HiZ culling? Could be quite beneficial given the nature of the 2.5D culling. */
+
+ /* TODO(fclem): Small light culling / fading? */
+
+ if (intersect_view(sphere)) {
+ uint index = atomicAdd(light_cull_buf.visible_count, 1u);
+
+ out_zdist_buf[index] = dot(cameraForward, light._position) - dot(cameraForward, cameraPos);
+ out_key_buf[index] = l_idx;
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl
new file mode 100644
index 00000000000..e98b170cd4c
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl
@@ -0,0 +1,57 @@
+
+/**
+ * Sort the lights by their Z distance to the camera.
+ * Outputs ordered light buffer.
+ * One thread processes one Light entity.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+shared float zdists_cache[gl_WorkGroupSize.x];
+
+void main()
+{
+ uint src_index = gl_GlobalInvocationID.x;
+ bool valid_thread = true;
+
+ if (src_index >= light_cull_buf.visible_count) {
+ /* Do not return because we use barriers later on (which need uniform control flow).
+ * Just process the same last item but avoid insertion. */
+ src_index = light_cull_buf.visible_count - 1;
+ valid_thread = false;
+ }
+
+ float local_zdist = in_zdist_buf[src_index];
+
+ int prefix_sum = 0;
+ /* Iterate over the whole key buffer. */
+ uint iter = divide_ceil(light_cull_buf.visible_count, gl_WorkGroupSize.x);
+ for (uint i = 0u; i < iter; i++) {
+ uint index = gl_WorkGroupSize.x * i + gl_LocalInvocationID.x;
+ /* NOTE: This will load duplicated values, but they will be discarded. */
+ index = min(index, light_cull_buf.visible_count - 1);
+ zdists_cache[gl_LocalInvocationID.x] = in_zdist_buf[index];
+
+ barrier();
+
+ /* Iterate over the cache line. */
+ uint line_end = min(gl_WorkGroupSize.x, light_cull_buf.visible_count - gl_WorkGroupSize.x * i);
+ for (uint j = 0u; j < line_end; j++) {
+ if (zdists_cache[j] < local_zdist) {
+ prefix_sum++;
+ }
+ else if (zdists_cache[j] == local_zdist) {
+ /* Same depth, use index to order and avoid same prefix for 2 different lights. */
+ if ((gl_WorkGroupSize.x * i + j) < src_index) {
+ prefix_sum++;
+ }
+ }
+ }
+ }
+
+ if (valid_thread) {
+ /* Copy sorted light to render light buffer. */
+ uint input_index = in_key_buf[src_index];
+ out_light_buf[prefix_sum] = in_light_buf[input_index];
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl
new file mode 100644
index 00000000000..37705e22b22
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl
@@ -0,0 +1,188 @@
+
+/**
+ * 2D Culling pass for lights.
+ * We iterate over all items and check if they intersect with the tile frustum.
+ * Dispatch one thread per word.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
+
+/* ---------------------------------------------------------------------- */
+/** \name Culling shapes extraction
+ * \{ */
+
+struct CullingTile {
+ IsectFrustum frustum;
+ vec4 bounds;
+};
+
+/* Corners are expected to be in viewspace so that the cone is starting from the origin.
+ * Corner order does not matter. */
+vec4 tile_bound_cone(vec3 v00, vec3 v01, vec3 v10, vec3 v11)
+{
+ v00 = normalize(v00);
+ v01 = normalize(v01);
+ v10 = normalize(v10);
+ v11 = normalize(v11);
+ vec3 center = normalize(v00 + v01 + v10 + v11);
+ float angle_cosine = dot(center, v00);
+ angle_cosine = max(angle_cosine, dot(center, v01));
+ angle_cosine = max(angle_cosine, dot(center, v10));
+ angle_cosine = max(angle_cosine, dot(center, v11));
+ return vec4(center, angle_cosine);
+}
+
+/* Corners are expected to be in viewspace. Returns Z-aligned bounding cylinder.
+ * Corner order does not matter. */
+vec4 tile_bound_cylinder(vec3 v00, vec3 v01, vec3 v10, vec3 v11)
+{
+ vec3 center = (v00 + v01 + v10 + v11) * 0.25;
+ vec4 corners_dist;
+ float dist_sqr = distance_squared(center, v00);
+ dist_sqr = max(dist_sqr, distance_squared(center, v01));
+ dist_sqr = max(dist_sqr, distance_squared(center, v10));
+ dist_sqr = max(dist_sqr, distance_squared(center, v11));
+ /* Return a cone. Later converted to cylinder. */
+ return vec4(center, sqrt(dist_sqr));
+}
+
+vec2 tile_to_ndc(vec2 tile_co, vec2 offset)
+{
+ /* Add a margin to prevent culling too much if the frustum becomes too much unstable. */
+ const float margin = 0.02;
+ tile_co += margin * (offset * 2.0 - 1.0);
+
+ tile_co += offset;
+ return tile_co * light_cull_buf.tile_to_uv_fac * 2.0 - 1.0;
+}
+
+CullingTile tile_culling_get(uvec2 tile_co)
+{
+ vec2 ftile = vec2(tile_co);
+ /* Culling frustum corners for this tile. */
+ vec3 corners[8];
+ /* Follow same corners order as view frustum. */
+ corners[1].xy = corners[0].xy = tile_to_ndc(ftile, vec2(0, 0));
+ corners[5].xy = corners[4].xy = tile_to_ndc(ftile, vec2(1, 0));
+ corners[6].xy = corners[7].xy = tile_to_ndc(ftile, vec2(1, 1));
+ corners[2].xy = corners[3].xy = tile_to_ndc(ftile, vec2(0, 1));
+ corners[1].z = corners[5].z = corners[6].z = corners[2].z = -1.0;
+ corners[0].z = corners[4].z = corners[7].z = corners[3].z = 1.0;
+
+ for (int i = 0; i < 8; i++) {
+ /* Culling in view space for precision. */
+ corners[i] = project_point(ProjectionMatrixInverse, corners[i]);
+ }
+
+ bool is_persp = ProjectionMatrix[3][3] == 0.0;
+ CullingTile tile;
+ tile.bounds = (is_persp) ? tile_bound_cone(corners[0], corners[4], corners[7], corners[3]) :
+ tile_bound_cylinder(corners[0], corners[4], corners[7], corners[3]);
+
+ tile.frustum = isect_data_setup(shape_frustum(corners));
+ return tile;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Intersection Tests
+ * \{ */
+
+bool intersect(CullingTile tile, Sphere sphere)
+{
+ bool isect = true;
+ /* Test tile intersection using bounding cone or bounding cylinder.
+ * This has less false positive cases when the sphere is large. */
+ if (ProjectionMatrix[3][3] == 0.0) {
+ isect = intersect(shape_cone(tile.bounds.xyz, tile.bounds.w), sphere);
+ }
+ else {
+ /* Simplify to a 2D circle test on the view Z axis plane. */
+ isect = intersect(shape_circle(tile.bounds.xy, tile.bounds.w),
+ shape_circle(sphere.center.xy, sphere.radius));
+ }
+ /* Refine using frustum test. If the sphere is small it avoids intersection
+ * with a neighbor tile. */
+ if (isect) {
+ isect = intersect(tile.frustum, sphere);
+ }
+ return isect;
+}
+
+bool intersect(CullingTile tile, Box bbox)
+{
+ return intersect(tile.frustum, bbox);
+}
+
+bool intersect(CullingTile tile, Pyramid pyramid)
+{
+ return intersect(tile.frustum, pyramid);
+}
+
+/** \} */
+
+void main()
+{
+ uint word_idx = gl_GlobalInvocationID.x % light_cull_buf.tile_word_len;
+ uint tile_idx = gl_GlobalInvocationID.x / light_cull_buf.tile_word_len;
+ uvec2 tile_co = uvec2(tile_idx % light_cull_buf.tile_x_len,
+ tile_idx / light_cull_buf.tile_x_len);
+
+ if (tile_co.y >= light_cull_buf.tile_y_len) {
+ return;
+ }
+
+ /* TODO(fclem): We could stop the tile at the HiZ depth. */
+ CullingTile tile = tile_culling_get(tile_co);
+
+ uint l_idx = word_idx * 32u;
+ uint l_end = min(l_idx + 32u, light_cull_buf.visible_count);
+ uint word = 0u;
+ for (; l_idx < l_end; l_idx++) {
+ LightData light = light_buf[l_idx];
+
+ /* Culling in view space for precision and simplicity. */
+ vec3 vP = transform_point(ViewMatrix, light._position);
+ vec3 v_right = transform_direction(ViewMatrix, light._right);
+ vec3 v_up = transform_direction(ViewMatrix, light._up);
+ vec3 v_back = transform_direction(ViewMatrix, light._back);
+ float radius = light.influence_radius_max;
+
+ Sphere sphere = shape_sphere(vP, radius);
+ bool intersect_tile = intersect(tile, sphere);
+
+ switch (light.type) {
+ case LIGHT_SPOT:
+ /* Only for < ~170° Cone due to plane extraction precision. */
+ if (light.spot_tan < 10.0) {
+ Pyramid pyramid = shape_pyramid_non_oblique(
+ vP,
+ vP - v_back * radius,
+ v_right * radius * light.spot_tan / light.spot_size_inv.x,
+ v_up * radius * light.spot_tan / light.spot_size_inv.y);
+ intersect_tile = intersect_tile && intersect(tile, pyramid);
+ break;
+ }
+ /* Fallthrough to the hemispheric case. */
+ case LIGHT_RECT:
+ case LIGHT_ELLIPSE:
+ vec3 v000 = vP - v_right * radius - v_up * radius;
+ vec3 v100 = v000 + v_right * (radius * 2.0);
+ vec3 v010 = v000 + v_up * (radius * 2.0);
+ vec3 v001 = v000 - v_back * radius;
+ Box bbox = shape_box(v000, v100, v010, v001);
+ intersect_tile = intersect_tile && intersect(tile, bbox);
+ default:
+ break;
+ }
+
+ if (intersect_tile) {
+ word |= 1u << (l_idx % 32u);
+ }
+ }
+
+ out_light_tile_buf[gl_GlobalInvocationID.x] = word;
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl
new file mode 100644
index 00000000000..ae20153f26c
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl
@@ -0,0 +1,56 @@
+
+/**
+ * Create the Zbins from Z-sorted lights.
+ * Perform min-max operation in LDS memory for speed.
+ * For this reason, we only dispatch 1 thread group.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
+
+/* Fits the limit of 32KB. */
+shared uint zbin_max[CULLING_ZBIN_COUNT];
+shared uint zbin_min[CULLING_ZBIN_COUNT];
+
+void main()
+{
+ const uint zbin_iter = CULLING_ZBIN_COUNT / gl_WorkGroupSize.x;
+ const uint zbin_local = gl_LocalInvocationID.x * zbin_iter;
+
+ uint src_index = gl_GlobalInvocationID.x;
+
+ for (uint i = 0u, l = zbin_local; i < zbin_iter; i++, l++) {
+ zbin_max[l] = 0x0u;
+ zbin_min[l] = ~0x0u;
+ }
+ barrier();
+
+ uint light_iter = divide_ceil(light_cull_buf.visible_count, gl_WorkGroupSize.x);
+ for (uint i = 0u; i < light_iter; i++) {
+ uint index = i * gl_WorkGroupSize.x + gl_LocalInvocationID.x;
+ if (index >= light_cull_buf.visible_count) {
+ continue;
+ }
+ vec3 P = light_buf[index]._position;
+ /* TODO(fclem): Could have better bounds for spot and area lights. */
+ float radius = light_buf[index].influence_radius_max;
+ float z_dist = dot(cameraForward, P) - dot(cameraForward, cameraPos);
+ int z_min = culling_z_to_zbin(
+ light_cull_buf.zbin_scale, light_cull_buf.zbin_bias, z_dist + radius);
+ int z_max = culling_z_to_zbin(
+ light_cull_buf.zbin_scale, light_cull_buf.zbin_bias, z_dist - radius);
+ z_min = clamp(z_min, 0, CULLING_ZBIN_COUNT - 1);
+ z_max = clamp(z_max, 0, CULLING_ZBIN_COUNT - 1);
+ /* Register to Z bins. */
+ for (int z = z_min; z <= z_max; z++) {
+ atomicMin(zbin_min[z], index);
+ atomicMax(zbin_max[z], index);
+ }
+ }
+ barrier();
+
+ /* Write result to zbins buffer. Pack min & max into 1 uint. */
+ for (uint i = 0u, l = zbin_local; i < zbin_iter; i++, l++) {
+ out_zbin_buf[l] = (zbin_max[l] << 16u) | (zbin_min[l] & 0xFFFFu);
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl
new file mode 100644
index 00000000000..d4abdd43aa4
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl
@@ -0,0 +1,129 @@
+
+/**
+ * The resources expected to be defined are:
+ * - light_buf
+ * - light_zbin_buf
+ * - light_cull_buf
+ * - light_tile_buf
+ * - shadow_atlas_tx
+ * - shadow_tilemaps_tx
+ * - sss_transmittance_tx
+ * - utility_tx
+ */
+
+#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl)
+
+/* TODO(fclem): We could reduce register pressure by only having static branches for sun lights. */
+void light_eval_ex(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ const bool is_directional,
+ vec3 P,
+ vec3 V,
+ float vP_z,
+ float thickness,
+ vec4 ltc_mat,
+ uint l_idx,
+ inout vec3 out_diffuse,
+ inout vec3 out_specular)
+{
+ LightData light = light_buf[l_idx];
+ vec3 L;
+ float dist;
+ light_vector_get(light, P, L, dist);
+
+ float visibility = light_attenuation(light, L, dist);
+
+#if 0 /* TODO(fclem): Shadows */
+ if ((light.shadow_id != LIGHT_NO_SHADOW) && (visibility > 0.0)) {
+ vec3 lL = light_world_to_local(light, -L) * dist;
+
+ float shadow_delta = shadow_delta_get(
+ shadow_atlas_tx, shadow_tilemaps_tx, light, light.shadow_data, lL, dist, P);
+
+# ifdef SSS_TRANSMITTANCE
+ /* Transmittance evaluation first to use initial visibility. */
+ if (diffuse.sss_id != 0u && light.diffuse_power > 0.0) {
+ float delta = max(thickness, shadow_delta);
+
+ vec3 intensity = visibility * light.transmit_power *
+ light_translucent(sss_transmittance_tx,
+ is_directional,
+ light,
+ diffuse.N,
+ L,
+ dist,
+ diffuse.sss_radius,
+ delta);
+ out_diffuse += light.color * intensity;
+ }
+# endif
+
+ visibility *= float(shadow_delta - light.shadow_data.bias <= 0.0);
+ }
+#endif
+
+ if (visibility < 1e-6) {
+ return;
+ }
+
+ if (light.diffuse_power > 0.0) {
+ float intensity = visibility * light.diffuse_power *
+ light_diffuse(utility_tx, is_directional, light, diffuse.N, V, L, dist);
+ out_diffuse += light.color * intensity;
+ }
+
+ if (light.specular_power > 0.0) {
+ float intensity = visibility * light.specular_power *
+ light_ltc(
+ utility_tx, is_directional, light, reflection.N, V, L, dist, ltc_mat);
+ out_specular += light.color * intensity;
+ }
+}
+
+void light_eval(ClosureDiffuse diffuse,
+ ClosureReflection reflection,
+ vec3 P,
+ vec3 V,
+ float vP_z,
+ float thickness,
+ inout vec3 out_diffuse,
+ inout vec3 out_specular)
+{
+ vec2 uv = vec2(reflection.roughness, safe_sqrt(1.0 - dot(reflection.N, V)));
+ uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS;
+ vec4 ltc_mat = utility_tx_sample(utility_tx, uv, UTIL_LTC_MAT_LAYER);
+
+ LIGHT_FOREACH_BEGIN_DIRECTIONAL(light_cull_buf, l_idx)
+ {
+ light_eval_ex(diffuse,
+ reflection,
+ true,
+ P,
+ V,
+ vP_z,
+ thickness,
+ ltc_mat,
+ l_idx,
+ out_diffuse,
+ out_specular);
+ }
+ LIGHT_FOREACH_END
+
+ vec2 px = gl_FragCoord.xy;
+ LIGHT_FOREACH_BEGIN_LOCAL(light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx)
+ {
+ light_eval_ex(diffuse,
+ reflection,
+ false,
+ P,
+ V,
+ vP_z,
+ thickness,
+ ltc_mat,
+ l_idx,
+ out_diffuse,
+ out_specular);
+ }
+ LIGHT_FOREACH_END
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_iter_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_iter_lib.glsl
new file mode 100644
index 00000000000..22a5f98e6c3
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_iter_lib.glsl
@@ -0,0 +1,72 @@
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+uint zbin_mask(uint word_index, uint zbin_min, uint zbin_max)
+{
+ uint word_start = word_index * 32u;
+ uint word_end = word_start + 31u;
+ uint local_min = max(zbin_min, word_start);
+ uint local_max = min(zbin_max, word_end);
+ uint mask_width = local_max - local_min + 1;
+ return bit_field_mask(mask_width, local_min);
+}
+
+int culling_z_to_zbin(float scale, float bias, float z)
+{
+ return int(z * scale + bias);
+}
+
+/* Waiting to implement extensions support. We need:
+ * - GL_KHR_shader_subgroup_ballot
+ * - GL_KHR_shader_subgroup_arithmetic
+ * or
+ * - Vulkan 1.1
+ */
+#if 1
+# define subgroupMin(a) a
+# define subgroupMax(a) a
+# define subgroupOr(a) a
+# define subgroupBroadcastFirst(a) a
+#endif
+
+#define LIGHT_FOREACH_BEGIN_DIRECTIONAL(_culling, _index) \
+ { \
+ { \
+ for (uint _index = _culling.local_lights_len; _index < _culling.items_count; _index++) {
+
+#define LIGHT_FOREACH_BEGIN_LOCAL(_culling, _zbins, _words, _pixel, _linearz, _item_index) \
+ { \
+ uvec2 tile_co = uvec2(_pixel / _culling.tile_size); \
+ uint tile_word_offset = (tile_co.x + tile_co.y * _culling.tile_x_len) * \
+ _culling.tile_word_len; \
+ int zbin_index = culling_z_to_zbin(_culling.zbin_scale, _culling.zbin_bias, _linearz); \
+ zbin_index = clamp(zbin_index, 0, CULLING_ZBIN_COUNT - 1); \
+ uint zbin_data = _zbins[zbin_index]; \
+ uint min_index = zbin_data & 0xFFFFu; \
+ uint max_index = zbin_data >> 16u; \
+ /* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
+ min_index = subgroupBroadcastFirst(subgroupMin(min_index)); \
+ max_index = subgroupBroadcastFirst(subgroupMax(max_index)); \
+ /* Same as divide by 32 but avoid interger division. */ \
+ uint word_min = min_index >> 5u; \
+ uint word_max = max_index >> 5u; \
+ for (uint word_idx = word_min; word_idx <= word_max; word_idx++) { \
+ uint word = _words[tile_word_offset + word_idx]; \
+ word &= zbin_mask(word_idx, min_index, max_index); \
+ /* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \
+ word = subgroupBroadcastFirst(subgroupOr(word)); \
+ int bit_index; \
+ while ((bit_index = findLSB(word)) != -1) { \
+ word &= ~1u << uint(bit_index); \
+ uint _item_index = word_idx * 32u + bit_index;
+
+/* No culling. Iterate over all items. */
+#define LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(_culling, _item_index) \
+ { \
+ { \
+ for (uint _item_index = 0; _item_index < _culling.visible_count; _item_index++) {
+
+#define LIGHT_FOREACH_END \
+ } \
+ } \
+ }
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl
new file mode 100644
index 00000000000..58608f6e1f0
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl
@@ -0,0 +1,209 @@
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_ltc_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
+
+/* ---------------------------------------------------------------------- */
+/** \name Light Functions
+ * \{ */
+
+void light_vector_get(LightData ld, vec3 P, out vec3 L, out float dist)
+{
+ if (ld.type == LIGHT_SUN) {
+ L = ld._back;
+ dist = 1.0;
+ }
+ else {
+ L = ld._position - P;
+ dist = inversesqrt(len_squared(L));
+ L *= dist;
+ dist = 1.0 / dist;
+ }
+}
+
+/* Rotate vector to light's local space. Does not translate. */
+vec3 light_world_to_local(LightData ld, vec3 L)
+{
+ /* Avoid relying on compiler to optimize this.
+ * vec3 lL = transpose(mat3(ld.object_mat)) * L; */
+ vec3 lL;
+ lL.x = dot(ld.object_mat[0].xyz, L);
+ lL.y = dot(ld.object_mat[1].xyz, L);
+ lL.z = dot(ld.object_mat[2].xyz, L);
+ return lL;
+}
+
+/* From Frostbite PBR Course
+ * Distance based attenuation
+ * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */
+float light_influence_attenuation(float dist, float inv_sqr_influence)
+{
+ float factor = sqr(dist) * inv_sqr_influence;
+ float fac = saturate(1.0 - sqr(factor));
+ return sqr(fac);
+}
+
+float light_spot_attenuation(LightData ld, vec3 L)
+{
+ vec3 lL = light_world_to_local(ld, L);
+ float ellipse = inversesqrt(1.0 + len_squared(lL.xy * ld.spot_size_inv / lL.z));
+ float spotmask = smoothstep(0.0, 1.0, ellipse * ld._spot_mul + ld._spot_bias);
+ return spotmask;
+}
+
+float light_attenuation(LightData ld, vec3 L, float dist)
+{
+ float vis = 1.0;
+ if (ld.type == LIGHT_SPOT) {
+ vis *= light_spot_attenuation(ld, L);
+ }
+ if (ld.type >= LIGHT_SPOT) {
+ vis *= step(0.0, -dot(L, -ld._back));
+ }
+ if (ld.type != LIGHT_SUN) {
+#ifdef VOLUME_LIGHTING
+ vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_volume);
+#else
+ vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_surface);
+#endif
+ }
+ return vis;
+}
+
+/* Cheaper alternative than evaluating the LTC.
+ * The result needs to be multiplied by BSDF or Phase Function. */
+float light_point_light(LightData ld, const bool is_directional, vec3 L, float dist)
+{
+ if (is_directional) {
+ return 1.0;
+ }
+ /**
+ * Using "Point Light Attenuation Without Singularity" from Cem Yuksel
+ * http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf
+ * http://www.cemyuksel.com/research/pointlightattenuation/
+ **/
+ float d_sqr = sqr(dist);
+ float r_sqr = ld.radius_squared;
+ /* Using reformulation that has better numerical percision. */
+ float power = 2.0 / (d_sqr + r_sqr + dist * sqrt(d_sqr + r_sqr));
+
+ if (is_area_light(ld.type)) {
+ /* Modulate by light plane orientation / solid angle. */
+ power *= saturate(dot(ld._back, L));
+ }
+ return power;
+}
+
+float light_diffuse(sampler2DArray utility_tx,
+ const bool is_directional,
+ LightData ld,
+ vec3 N,
+ vec3 V,
+ vec3 L,
+ float dist)
+{
+ if (is_directional || !is_area_light(ld.type)) {
+ float radius = ld._radius / dist;
+ return ltc_evaluate_disk_simple(utility_tx, radius, dot(N, L));
+ }
+ else if (ld.type == LIGHT_RECT) {
+ vec3 corners[4];
+ corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
+ corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y;
+ corners[2] = -corners[0];
+ corners[3] = -corners[1];
+
+ corners[0] = normalize(L * dist + corners[0]);
+ corners[1] = normalize(L * dist + corners[1]);
+ corners[2] = normalize(L * dist + corners[2]);
+ corners[3] = normalize(L * dist + corners[3]);
+
+ return ltc_evaluate_quad(utility_tx, corners, N);
+ }
+ else /* (ld.type == LIGHT_ELLIPSE) */ {
+ vec3 points[3];
+ points[0] = ld._right * -ld._area_size_x + ld._up * -ld._area_size_y;
+ points[1] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
+ points[2] = -points[0];
+
+ points[0] += L * dist;
+ points[1] += L * dist;
+ points[2] += L * dist;
+
+ return ltc_evaluate_disk(utility_tx, N, V, mat3(1.0), points);
+ }
+}
+
+float light_ltc(sampler2DArray utility_tx,
+ const bool is_directional,
+ LightData ld,
+ vec3 N,
+ vec3 V,
+ vec3 L,
+ float dist,
+ vec4 ltc_mat)
+{
+ if (is_directional || ld.type != LIGHT_RECT) {
+ vec3 Px = ld._right;
+ vec3 Py = ld._up;
+
+ if (is_directional || !is_area_light(ld.type)) {
+ make_orthonormal_basis(L, Px, Py);
+ }
+
+ vec3 points[3];
+ points[0] = Px * -ld._area_size_x + Py * -ld._area_size_y;
+ points[1] = Px * ld._area_size_x + Py * -ld._area_size_y;
+ points[2] = -points[0];
+
+ points[0] += L * dist;
+ points[1] += L * dist;
+ points[2] += L * dist;
+
+ return ltc_evaluate_disk(utility_tx, N, V, ltc_matrix(ltc_mat), points);
+ }
+ else {
+ vec3 corners[4];
+ corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y;
+ corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y;
+ corners[2] = -corners[0];
+ corners[3] = -corners[1];
+
+ corners[0] += L * dist;
+ corners[1] += L * dist;
+ corners[2] += L * dist;
+ corners[3] += L * dist;
+
+ ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners);
+
+ return ltc_evaluate_quad(utility_tx, corners, vec3(0.0, 0.0, 1.0));
+ }
+}
+
+vec3 light_translucent(sampler1D transmittance_tx,
+ const bool is_directional,
+ LightData ld,
+ vec3 N,
+ vec3 L,
+ float dist,
+ vec3 sss_radius,
+ float delta)
+{
+ /* TODO(fclem): We should compute the power at the entry point. */
+ /* NOTE(fclem): we compute the light attenuation using the light vector but the transmittance
+ * using the shadow depth delta. */
+ float power = light_point_light(ld, is_directional, L, dist);
+ /* Do not add more energy on front faces. Also apply lambertian BSDF. */
+ power *= max(0.0, dot(-N, L)) * M_1_PI;
+
+ sss_radius *= SSS_TRANSMIT_LUT_RADIUS;
+ vec3 channels_co = saturate(delta / sss_radius) * SSS_TRANSMIT_LUT_SCALE + SSS_TRANSMIT_LUT_BIAS;
+
+ vec3 translucency;
+ translucency.x = (sss_radius.x > 0.0) ? texture(transmittance_tx, channels_co.x).r : 0.0;
+ translucency.y = (sss_radius.y > 0.0) ? texture(transmittance_tx, channels_co.y).r : 0.0;
+ translucency.z = (sss_radius.z > 0.0) ? texture(transmittance_tx, channels_co.z).r : 0.0;
+ return translucency * power;
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ltc_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ltc_lib.glsl
new file mode 100644
index 00000000000..57e92b0b9b4
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ltc_lib.glsl
@@ -0,0 +1,299 @@
+
+/**
+ * Adapted from :
+ * Real-Time Polygonal-Light Shading with Linearly Transformed Cosines.
+ * Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt.
+ * ACM Transactions on Graphics (Proceedings of ACM SIGGRAPH 2016) 35(4), 2016.
+ * Project page: https://eheitzresearch.wordpress.com/415-2/
+ */
+
+/* Diffuse *clipped* sphere integral. */
+float ltc_diffuse_sphere_integral(sampler2DArray utility_tx, float avg_dir_z, float form_factor)
+{
+#if 1
+ /* use tabulated horizon-clipped sphere */
+ vec2 uv = vec2(avg_dir_z * 0.5 + 0.5, form_factor);
+ uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS;
+
+ return texture(utility_tx, vec3(uv, UTIL_DISK_INTEGRAL_LAYER))[UTIL_DISK_INTEGRAL_COMP];
+#else
+ /* Cheap approximation. Less smooth and have energy issues. */
+ return max((form_factor * form_factor + avg_dir_z) / (form_factor + 1.0), 0.0);
+#endif
+}
+
+/**
+ * An extended version of the implementation from
+ * "How to solve a cubic equation, revisited"
+ * http://momentsingraphics.de/?p=105
+ */
+vec3 ltc_solve_cubic(vec4 coefs)
+{
+ /* Normalize the polynomial */
+ coefs.xyz /= coefs.w;
+ /* Divide middle coefficients by three */
+ coefs.yz /= 3.0;
+
+ float A = coefs.w;
+ float B = coefs.z;
+ float C = coefs.y;
+ float D = coefs.x;
+
+ /* Compute the Hessian and the discriminant */
+ vec3 delta = vec3(-coefs.zy * coefs.zz + coefs.yx, dot(vec2(coefs.z, -coefs.y), coefs.xy));
+
+ /* Discriminant */
+ float discr = dot(vec2(4.0 * delta.x, -delta.y), delta.zy);
+
+ /* Clamping avoid NaN output on some platform. (see T67060) */
+ float sqrt_discr = sqrt(clamp(discr, 0.0, FLT_MAX));
+
+ vec2 xlc, xsc;
+
+ /* Algorithm A */
+ {
+ float A_a = 1.0;
+ float C_a = delta.x;
+ float D_a = -2.0 * B * delta.x + delta.y;
+
+ /* Take the cubic root of a normalized complex number */
+ float theta = atan(sqrt_discr, -D_a) / 3.0;
+
+ float _2_sqrt_C_a = 2.0 * sqrt(-C_a);
+ float x_1a = _2_sqrt_C_a * cos(theta);
+ float x_3a = _2_sqrt_C_a * cos(theta + (2.0 / 3.0) * M_PI);
+
+ float xl;
+ if ((x_1a + x_3a) > 2.0 * B) {
+ xl = x_1a;
+ }
+ else {
+ xl = x_3a;
+ }
+
+ xlc = vec2(xl - B, A);
+ }
+
+ /* Algorithm D */
+ {
+ float A_d = D;
+ float C_d = delta.z;
+ float D_d = -D * delta.y + 2.0 * C * delta.z;
+
+ /* Take the cubic root of a normalized complex number */
+ float theta = atan(D * sqrt_discr, -D_d) / 3.0;
+
+ float _2_sqrt_C_d = 2.0 * sqrt(-C_d);
+ float x_1d = _2_sqrt_C_d * cos(theta);
+ float x_3d = _2_sqrt_C_d * cos(theta + (2.0 / 3.0) * M_PI);
+
+ float xs;
+ if (x_1d + x_3d < 2.0 * C) {
+ xs = x_1d;
+ }
+ else {
+ xs = x_3d;
+ }
+
+ xsc = vec2(-D, xs + C);
+ }
+
+ float E = xlc.y * xsc.y;
+ float F = -xlc.x * xsc.y - xlc.y * xsc.x;
+ float G = xlc.x * xsc.x;
+
+ vec2 xmc = vec2(C * F - B * G, -B * F + C * E);
+
+ vec3 root = vec3(xsc.x / xsc.y, xmc.x / xmc.y, xlc.x / xlc.y);
+
+ if (root.x < root.y && root.x < root.z) {
+ root.xyz = root.yxz;
+ }
+ else if (root.z < root.x && root.z < root.y) {
+ root.xyz = root.xzy;
+ }
+
+ return root;
+}
+
+/* from Real-Time Area Lighting: a Journey from Research to Production
+ * Stephen Hill and Eric Heitz */
+vec3 ltc_edge_integral_vec(vec3 v1, vec3 v2)
+{
+ float x = dot(v1, v2);
+ float y = abs(x);
+
+ float a = 0.8543985 + (0.4965155 + 0.0145206 * y) * y;
+ float b = 3.4175940 + (4.1616724 + y) * y;
+ float v = a / b;
+
+ float theta_sintheta = (x > 0.0) ? v : 0.5 * inversesqrt(max(1.0 - x * x, 1e-7)) - v;
+
+ return cross(v1, v2) * theta_sintheta;
+}
+
+mat3 ltc_matrix(vec4 lut)
+{
+ /* Load inverse matrix. */
+ return mat3(vec3(lut.x, 0, lut.y), vec3(0, 1, 0), vec3(lut.z, 0, lut.w));
+}
+
+void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4])
+{
+ /* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */
+ V = normalize(V + 1e-8);
+
+ /* Construct orthonormal basis around N. */
+ vec3 T1, T2;
+ T1 = normalize(V - N * dot(N, V));
+ T2 = cross(N, T1);
+
+ /* Rotate area light in (T1, T2, R) basis. */
+ Minv = Minv * transpose(mat3(T1, T2, N));
+
+ /* Apply LTC inverse matrix. */
+ corners[0] = normalize(Minv * corners[0]);
+ corners[1] = normalize(Minv * corners[1]);
+ corners[2] = normalize(Minv * corners[2]);
+ corners[3] = normalize(Minv * corners[3]);
+}
+
+/* If corners have already pass through ltc_transform_quad(),
+ * then N **MUST** be vec3(0.0, 0.0, 1.0), corresponding to the Up axis of the shading basis. */
+float ltc_evaluate_quad(sampler2DArray utility_tx, vec3 corners[4], vec3 N)
+{
+ /* Approximation using a sphere of the same solid angle than the quad.
+ * Finding the clipped sphere diffuse integral is easier than clipping the quad. */
+ vec3 avg_dir;
+ avg_dir = ltc_edge_integral_vec(corners[0], corners[1]);
+ avg_dir += ltc_edge_integral_vec(corners[1], corners[2]);
+ avg_dir += ltc_edge_integral_vec(corners[2], corners[3]);
+ avg_dir += ltc_edge_integral_vec(corners[3], corners[0]);
+
+ float form_factor = length(avg_dir);
+ float avg_dir_z = dot(N, avg_dir / form_factor);
+ return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir_z, form_factor);
+}
+
+/* If disk does not need to be transformed and is already front facing. */
+float ltc_evaluate_disk_simple(sampler2DArray utility_tx, float disk_radius, float NL)
+{
+ float r_sqr = disk_radius * disk_radius;
+ float one_r_sqr = 1.0 + r_sqr;
+ float form_factor = r_sqr * inversesqrt(one_r_sqr * one_r_sqr);
+ return form_factor * ltc_diffuse_sphere_integral(utility_tx, NL, form_factor);
+}
+
+/* disk_points are WS vectors from the shading point to the disk "bounding domain" */
+float ltc_evaluate_disk(sampler2DArray utility_tx, vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3])
+{
+ /* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */
+ V = normalize(V + 1e-8);
+
+ /* construct orthonormal basis around N */
+ vec3 T1, T2;
+ T1 = normalize(V - N * dot(V, N));
+ T2 = cross(N, T1);
+
+ /* rotate area light in (T1, T2, R) basis */
+ mat3 R = transpose(mat3(T1, T2, N));
+
+ /* Intermediate step: init ellipse. */
+ vec3 L_[3];
+ L_[0] = mul(R, disk_points[0]);
+ L_[1] = mul(R, disk_points[1]);
+ L_[2] = mul(R, disk_points[2]);
+
+ vec3 C = 0.5 * (L_[0] + L_[2]);
+ vec3 V1 = 0.5 * (L_[1] - L_[2]);
+ vec3 V2 = 0.5 * (L_[1] - L_[0]);
+
+ /* Transform ellipse by Minv. */
+ C = Minv * C;
+ V1 = Minv * V1;
+ V2 = Minv * V2;
+
+ /* Compute eigenvectors of new ellipse. */
+
+ float d11 = dot(V1, V1);
+ float d22 = dot(V2, V2);
+ float d12 = dot(V1, V2);
+ float a, b; /* Eigenvalues */
+ const float threshold = 0.0007; /* Can be adjusted. Fix artifacts. */
+ if (abs(d12) / sqrt(d11 * d22) > threshold) {
+ float tr = d11 + d22;
+ float det = -d12 * d12 + d11 * d22;
+
+ /* use sqrt matrix to solve for eigenvalues */
+ det = sqrt(det);
+ float u = 0.5 * sqrt(tr - 2.0 * det);
+ float v = 0.5 * sqrt(tr + 2.0 * det);
+ float e_max = (u + v);
+ float e_min = (u - v);
+ e_max *= e_max;
+ e_min *= e_min;
+
+ vec3 V1_, V2_;
+ if (d11 > d22) {
+ V1_ = d12 * V1 + (e_max - d11) * V2;
+ V2_ = d12 * V1 + (e_min - d11) * V2;
+ }
+ else {
+ V1_ = d12 * V2 + (e_max - d22) * V1;
+ V2_ = d12 * V2 + (e_min - d22) * V1;
+ }
+
+ a = 1.0 / e_max;
+ b = 1.0 / e_min;
+ V1 = normalize(V1_);
+ V2 = normalize(V2_);
+ }
+ else {
+ a = 1.0 / d11;
+ b = 1.0 / d22;
+ V1 *= sqrt(a);
+ V2 *= sqrt(b);
+ }
+
+ /* Now find front facing ellipse with same solid angle. */
+
+ vec3 V3 = normalize(cross(V1, V2));
+ if (dot(C, V3) < 0.0) {
+ V3 *= -1.0;
+ }
+
+ float L = dot(V3, C);
+ float inv_L = 1.0 / L;
+ float x0 = dot(V1, C) * inv_L;
+ float y0 = dot(V2, C) * inv_L;
+
+ float L_sqr = L * L;
+ a *= L_sqr;
+ b *= L_sqr;
+
+ float t = 1.0 + x0 * x0;
+ float c0 = a * b;
+ float c1 = c0 * (t + y0 * y0) - a - b;
+ float c2 = (1.0 - a * t) - b * (1.0 + y0 * y0);
+ float c3 = 1.0;
+
+ vec3 roots = ltc_solve_cubic(vec4(c0, c1, c2, c3));
+ float e1 = roots.x;
+ float e2 = roots.y;
+ float e3 = roots.z;
+
+ vec3 avg_dir = vec3(a * x0 / (a - e2), b * y0 / (b - e2), 1.0);
+
+ mat3 rotate = mat3(V1, V2, V3);
+
+ avg_dir = rotate * avg_dir;
+ avg_dir = normalize(avg_dir);
+
+ /* L1, L2 are the extends of the front facing ellipse. */
+ float L1 = sqrt(-e2 / e3);
+ float L2 = sqrt(-e2 / e1);
+
+ /* Find the sphere and compute lighting. */
+ float form_factor = max(0.0, L1 * L2 * inversesqrt((1.0 + L1 * L1) * (1.0 + L2 * L2)));
+ return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir.z, form_factor);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl
new file mode 100644
index 00000000000..07139ea6a09
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl
@@ -0,0 +1,115 @@
+
+/**
+ * Dilate motion vector tiles until we covered maximum velocity.
+ * Outputs the largest intersecting motion vector in the neighborhood.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl)
+
+#define DEBUG_BYPASS_DILATION 0
+
+struct MotionRect {
+ ivec2 bottom_left;
+ ivec2 extent;
+};
+
+MotionRect compute_motion_rect(ivec2 tile, vec2 motion)
+{
+#if DEBUG_BYPASS_DILATION
+ return MotionRect(tile, ivec2(1));
+#endif
+ /* Ceil to number of tile touched. */
+ ivec2 point1 = tile + ivec2(sign(motion) * ceil(abs(motion) / float(MOTION_BLUR_TILE_SIZE)));
+ ivec2 point2 = tile;
+
+ ivec2 max_point = max(point1, point2);
+ ivec2 min_point = min(point1, point2);
+ /* Clamp to bounds. */
+ max_point = min(max_point, imageSize(in_tiles_img) - 1);
+ min_point = max(min_point, ivec2(0));
+
+ MotionRect rect;
+ rect.bottom_left = min_point;
+ rect.extent = 1 + max_point - min_point;
+ return rect;
+}
+
+struct MotionLine {
+ /** Origin of the line. */
+ vec2 origin;
+ /** Normal to the line direction. */
+ vec2 normal;
+};
+
+MotionLine compute_motion_line(ivec2 tile, vec2 motion)
+{
+ vec2 dir = safe_normalize(motion);
+
+ MotionLine line;
+ line.origin = vec2(tile);
+ /* Rotate 90° Counter-Clockwise. */
+ line.normal = vec2(-dir.y, dir.x);
+ return line;
+}
+
+bool is_inside_motion_line(ivec2 tile, MotionLine motion_line)
+{
+#if DEBUG_BYPASS_DILATION
+ return true;
+#endif
+ /* NOTE: Everything in is tile unit. */
+ float dist = point_line_projection_dist(vec2(tile), motion_line.origin, motion_line.normal);
+ /* In order to be conservative and for simplicity, we use the tiles bounding circles.
+ * Consider that both the tile and the line have bounding radius of M_SQRT1_2. */
+ return abs(dist) < M_SQRT2;
+}
+
+void main()
+{
+ ivec2 src_tile = ivec2(gl_GlobalInvocationID.xy);
+ if (any(greaterThanEqual(src_tile, imageSize(in_tiles_img)))) {
+ return;
+ }
+
+ vec4 max_motion = imageLoad(in_tiles_img, src_tile);
+
+ MotionPayload payload_prv = motion_blur_tile_indirection_pack_payload(max_motion.xy, src_tile);
+ MotionPayload payload_nxt = motion_blur_tile_indirection_pack_payload(max_motion.zw, src_tile);
+ if (true) {
+ /* Rectangular area (in tiles) where the motion vector spreads. */
+ MotionRect motion_rect = compute_motion_rect(src_tile, max_motion.xy);
+ MotionLine motion_line = compute_motion_line(src_tile, max_motion.xy);
+ /* Do a conservative rasterization of the line of the motion vector line. */
+ for (int x = 0; x < motion_rect.extent.x; x++) {
+ for (int y = 0; y < motion_rect.extent.y; y++) {
+ ivec2 tile = motion_rect.bottom_left + ivec2(x, y);
+ if (is_inside_motion_line(tile, motion_line)) {
+ motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_PREV, tile, payload_prv);
+ /* FIXME: This is a bit weird, but for some reason, we need the store the same vector in
+ * the motion next so that weighting in gather pass is better. */
+ motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_NEXT, tile, payload_nxt);
+ }
+ }
+ }
+ }
+
+ if (true) {
+ MotionPayload payload = motion_blur_tile_indirection_pack_payload(max_motion.zw, src_tile);
+ /* Rectangular area (in tiles) where the motion vector spreads. */
+ MotionRect motion_rect = compute_motion_rect(src_tile, max_motion.zw);
+ MotionLine motion_line = compute_motion_line(src_tile, max_motion.zw);
+ /* Do a conservative rasterization of the line of the motion vector line. */
+ for (int x = 0; x < motion_rect.extent.x; x++) {
+ for (int y = 0; y < motion_rect.extent.y; y++) {
+ ivec2 tile = motion_rect.bottom_left + ivec2(x, y);
+ if (is_inside_motion_line(tile, motion_line)) {
+ motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_NEXT, tile, payload_nxt);
+ /* FIXME: This is a bit weird, but for some reason, we need the store the same vector in
+ * the motion next so that weighting in gather pass is better. */
+ motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_PREV, tile, payload_prv);
+ }
+ }
+ }
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl
new file mode 100644
index 00000000000..cbbeea25d20
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl
@@ -0,0 +1,103 @@
+
+/**
+ * Shaders that down-sample velocity buffer into squared tile of MB_TILE_DIVISOR pixels wide.
+ * Outputs the largest motion vector in the tile area.
+ * Also perform velocity resolve to speedup the convolution pass.
+ *
+ * Based on:
+ * A Fast and Stable Feature-Aware Motion Blur Filter
+ * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
+ *
+ * Adapted from G3D Innovation Engine implementation.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
+
+shared uint payload_prev;
+shared uint payload_next;
+shared vec2 max_motion_prev;
+shared vec2 max_motion_next;
+
+/* Store velocity magnitude in the MSB and thread id in the LSB. */
+uint pack_payload(vec2 motion, uvec2 thread_id)
+{
+ /* NOTE: We clamp max velocity to 16k pixels. */
+ return (min(uint(ceil(length(motion))), 0xFFFFu) << 16u) | (thread_id.y << 8) | thread_id.x;
+}
+
+/* Return thread index from the payload. */
+uvec2 unpack_payload(uint payload)
+{
+ return uvec2(payload & 0xFFu, (payload >> 8) & 0xFFu);
+}
+
+void main()
+{
+ if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) {
+ payload_prev = 0u;
+ payload_next = 0u;
+ }
+ barrier();
+
+ uint local_payload_prev = 0u;
+ uint local_payload_next = 0u;
+ vec2 local_max_motion_prev;
+ vec2 local_max_motion_next;
+
+ ivec2 texel = min(ivec2(gl_GlobalInvocationID.xy), imageSize(velocity_img) - 1);
+
+ vec2 render_size = vec2(imageSize(velocity_img).xy);
+ vec2 uv = (vec2(texel) + 0.5) / render_size;
+ float depth = texelFetch(depth_tx, texel, 0).r;
+ vec4 motion = velocity_resolve(imageLoad(velocity_img, texel), uv, depth);
+#ifdef FLATTEN_VIEWPORT
+ /* imageLoad does not perform the swizzling like sampler does. Do it manually. */
+ motion = motion.xyxy;
+#endif
+
+ /* Store resolved velocity to speedup the gather pass. Out of bounds writes are ignored.
+ * Unfortunately, we cannot convert to pixel space here since it is also used by TAA and the
+ * motion blur needs to remain optional. */
+ imageStore(velocity_img, ivec2(gl_GlobalInvocationID.xy), velocity_pack(motion));
+ /* Clip velocity to viewport bounds (in NDC space). */
+ vec2 line_clip;
+ line_clip.x = line_unit_square_intersect_dist_safe(uv * 2.0 - 1.0, motion.xy * 2.0);
+ line_clip.y = line_unit_square_intersect_dist_safe(uv * 2.0 - 1.0, -motion.zw * 2.0);
+ motion *= min(line_clip, vec2(1.0)).xxyy;
+ /* Convert to pixel space. Note this is only for velocity tiles. */
+ motion *= render_size.xyxy;
+ /* Rescale to shutter relative motion for viewport. */
+ motion *= motion_blur_buf.motion_scale.xxyy;
+
+ uint sample_payload_prev = pack_payload(motion.xy, gl_LocalInvocationID.xy);
+ if (local_payload_prev < sample_payload_prev) {
+ local_payload_prev = sample_payload_prev;
+ local_max_motion_prev = motion.xy;
+ }
+
+ uint sample_payload_next = pack_payload(motion.zw, gl_LocalInvocationID.xy);
+ if (local_payload_next < sample_payload_next) {
+ local_payload_next = sample_payload_next;
+ local_max_motion_next = motion.zw;
+ }
+
+ /* Compare the local payload with the other threads. */
+ atomicMax(payload_prev, local_payload_prev);
+ atomicMax(payload_next, local_payload_next);
+ barrier();
+
+ /* Need to broadcast the result to another thread in order to issue a unique write. */
+ if (all(equal(unpack_payload(payload_prev), gl_LocalInvocationID.xy))) {
+ max_motion_prev = local_max_motion_prev;
+ }
+ if (all(equal(unpack_payload(payload_next), gl_LocalInvocationID.xy))) {
+ max_motion_next = local_max_motion_next;
+ }
+ barrier();
+
+ if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) {
+ ivec2 tile_co = ivec2(gl_WorkGroupID.xy);
+ imageStore(out_tiles_img, tile_co, vec4(max_motion_prev, max_motion_next));
+ }
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl
new file mode 100644
index 00000000000..5249e6637b6
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl
@@ -0,0 +1,221 @@
+
+/**
+ * Perform two gather blur in the 2 motion blur directions
+ * Based on:
+ * A Fast and Stable Feature-Aware Motion Blur Filter
+ * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai
+ *
+ * With modification from the presentation:
+ * Next Generation Post Processing in Call of Duty Advanced Warfare
+ * by Jorge Jimenez
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl)
+
+const int gather_sample_count = 8;
+
+/* Converts uv velocity into pixel space. Assumes velocity_tx is the same resolution as the
+ * target post-fx framebuffer. */
+vec4 motion_blur_sample_velocity(sampler2D velocity_tx, vec2 uv)
+{
+ /* We can load velocity without velocity_resolve() since we resolved during the flatten pass. */
+ vec4 velocity = velocity_unpack(texture(velocity_tx, uv));
+ return velocity * vec2(textureSize(velocity_tx, 0)).xyxy * motion_blur_buf.motion_scale.xxyy;
+}
+
+vec2 spread_compare(float center_motion_length, float sample_motion_length, float offset_length)
+{
+ return saturate(vec2(center_motion_length, sample_motion_length) - offset_length + 1.0);
+}
+
+vec2 depth_compare(float center_depth, float sample_depth)
+{
+ vec2 depth_scale = vec2(-motion_blur_buf.depth_scale, motion_blur_buf.depth_scale);
+ return saturate(0.5 + depth_scale * (sample_depth - center_depth));
+}
+
+/* Kill contribution if not going the same direction. */
+float dir_compare(vec2 offset, vec2 sample_motion, float sample_motion_length)
+{
+ if (sample_motion_length < 0.5) {
+ return 1.0;
+ }
+ return (dot(offset, sample_motion) > 0.0) ? 1.0 : 0.0;
+}
+
+/* Return background (x) and foreground (y) weights. */
+vec2 sample_weights(float center_depth,
+ float sample_depth,
+ float center_motion_length,
+ float sample_motion_length,
+ float offset_length)
+{
+ /* Classify foreground/background. */
+ vec2 depth_weight = depth_compare(center_depth, sample_depth);
+ /* Weight if sample is overlapping or under the center pixel. */
+ vec2 spread_weight = spread_compare(center_motion_length, sample_motion_length, offset_length);
+ return depth_weight * spread_weight;
+}
+
+struct Accumulator {
+ vec4 fg;
+ vec4 bg;
+ /** x: Background, y: Foreground, z: dir. */
+ vec3 weight;
+};
+
+void gather_sample(vec2 screen_uv,
+ float center_depth,
+ float center_motion_len,
+ vec2 offset,
+ float offset_len,
+ const bool next,
+ inout Accumulator accum)
+{
+ vec2 sample_uv = screen_uv - offset * motion_blur_buf.target_size_inv;
+ vec4 sample_vectors = motion_blur_sample_velocity(velocity_tx, sample_uv);
+ vec2 sample_motion = (next) ? sample_vectors.zw : sample_vectors.xy;
+ float sample_motion_len = length(sample_motion);
+ float sample_depth = texture(depth_tx, sample_uv).r;
+ vec4 sample_color = textureLod(in_color_tx, sample_uv, 0.0);
+
+ sample_depth = get_view_z_from_depth(sample_depth);
+
+ vec3 weights;
+ weights.xy = sample_weights(
+ center_depth, sample_depth, center_motion_len, sample_motion_len, offset_len);
+ weights.z = dir_compare(offset, sample_motion, sample_motion_len);
+ weights.xy *= weights.z;
+
+ accum.fg += sample_color * weights.y;
+ accum.bg += sample_color * weights.x;
+ accum.weight += weights;
+}
+
+void gather_blur(vec2 screen_uv,
+ vec2 center_motion,
+ float center_depth,
+ vec2 max_motion,
+ float ofs,
+ const bool next,
+ inout Accumulator accum)
+{
+ float center_motion_len = length(center_motion);
+ float max_motion_len = length(max_motion);
+
+ /* Tile boundaries randomization can fetch a tile where there is less motion than this pixel.
+ * Fix this by overriding the max_motion. */
+ if (max_motion_len < center_motion_len) {
+ max_motion_len = center_motion_len;
+ max_motion = center_motion;
+ }
+
+ if (max_motion_len < 0.5) {
+ return;
+ }
+
+ int i;
+ float t, inc = 1.0 / float(gather_sample_count);
+ for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) {
+ gather_sample(screen_uv,
+ center_depth,
+ center_motion_len,
+ max_motion * t,
+ max_motion_len * t,
+ next,
+ accum);
+ }
+
+ if (center_motion_len < 0.5) {
+ return;
+ }
+
+ for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) {
+ /* Also sample in center motion direction.
+ * Allow recovering motion where there is conflicting
+ * motion between foreground and background. */
+ gather_sample(screen_uv,
+ center_depth,
+ center_motion_len,
+ center_motion * t,
+ center_motion_len * t,
+ next,
+ accum);
+ }
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(depth_tx, 0).xy);
+
+ if (!in_texture_range(texel, depth_tx)) {
+ return;
+ }
+
+ /* Data of the center pixel of the gather (target). */
+ float center_depth = get_view_z_from_depth(texelFetch(depth_tx, texel, 0).r);
+ vec4 center_motion = motion_blur_sample_velocity(velocity_tx, uv);
+
+ vec4 center_color = textureLod(in_color_tx, uv, 0.0);
+
+ float noise_offset = sampling_rng_1D_get(SAMPLING_TIME);
+ /** TODO(fclem) Blue noise. */
+ vec2 rand = vec2(interlieved_gradient_noise(vec2(gl_GlobalInvocationID.xy), 0, noise_offset),
+ interlieved_gradient_noise(vec2(gl_GlobalInvocationID.xy), 1, noise_offset));
+
+ /* Randomize tile boundary to avoid ugly discontinuities. Randomize 1/4th of the tile.
+ * Note this randomize only in one direction but in practice it's enough. */
+ rand.x = rand.x * 2.0 - 1.0;
+ ivec2 tile = (texel + ivec2(rand.x * float(MOTION_BLUR_TILE_SIZE) * 0.25)) /
+ MOTION_BLUR_TILE_SIZE;
+ tile = clamp(tile, ivec2(0), imageSize(in_tiles_img) - 1);
+ /* NOTE: Tile velocity is already in pixel space and with correct zw sign. */
+ vec4 max_motion;
+ /* Load dilation result from the indirection table. */
+ ivec2 tile_prev;
+ motion_blur_tile_indirection_load(tile_indirection_buf, MOTION_PREV, tile, tile_prev);
+ max_motion.xy = imageLoad(in_tiles_img, tile_prev).xy;
+ ivec2 tile_next;
+ motion_blur_tile_indirection_load(tile_indirection_buf, MOTION_NEXT, tile, tile_next);
+ max_motion.zw = imageLoad(in_tiles_img, tile_next).zw;
+
+ Accumulator accum;
+ accum.weight = vec3(0.0, 0.0, 1.0);
+ accum.bg = vec4(0.0);
+ accum.fg = vec4(0.0);
+ /* First linear gather. time = [T - delta, T] */
+ gather_blur(uv, center_motion.xy, center_depth, max_motion.xy, rand.y, false, accum);
+ /* Second linear gather. time = [T, T + delta] */
+ gather_blur(uv, center_motion.zw, center_depth, max_motion.zw, rand.y, true, accum);
+
+#if 1 /* Own addition. Not present in reference implementation. */
+ /* Avoid division by 0.0. */
+ float w = 1.0 / (50.0 * float(gather_sample_count) * 4.0);
+ accum.bg += center_color * w;
+ accum.weight.x += w;
+ /* NOTE: In Jimenez's presentation, they used center sample.
+ * We use background color as it contains more information for foreground
+ * elements that have not enough weights.
+ * Yield better blur in complex motion. */
+ center_color = accum.bg / accum.weight.x;
+#endif
+ /* Merge background. */
+ accum.fg += accum.bg;
+ accum.weight.y += accum.weight.x;
+ /* Balance accumulation for failed samples.
+ * We replace the missing foreground by the background. */
+ float blend_fac = saturate(1.0 - accum.weight.y / accum.weight.z);
+ vec4 out_color = (accum.fg / accum.weight.z) + center_color * blend_fac;
+
+#if 0 /* For debugging. */
+ out_color.rgb = out_color.ggg;
+ out_color.rg += max_motion.xy;
+#endif
+
+ imageStore(out_color_img, texel, out_color);
+}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl
new file mode 100644
index 00000000000..436fd01795a
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl
@@ -0,0 +1,48 @@
+
+
+/* -------------------------------------------------------------------- */
+/** \name Tile indirection packing
+ * \{ */
+
+#define MotionPayload uint
+
+/* Store velocity magnitude in the MSB to be able to use it with atomicMax operations. */
+MotionPayload motion_blur_tile_indirection_pack_payload(vec2 motion, uvec2 payload)
+{
+ /* NOTE: Clamp to 16383 pixel velocity. After that, it is tile position that determine the tile
+ * to dilate over. */
+ uint velocity = min(uint(ceil(length(motion))), 0x3FFFu);
+ /* Designed for 512x512 tiles max. */
+ return (velocity << 18u) | ((payload.x & 0x1FFu) << 9u) | (payload.y & 0x1FFu);
+}
+
+/* Return thread index. */
+ivec2 motion_blur_tile_indirection_pack_payload(uint data)
+{
+ return ivec2((data >> 9u) & 0x1FFu, data & 0x1FFu);
+}
+
+uint motion_blur_tile_indirection_index(uint motion_step, uvec2 tile)
+{
+ uint index = tile.x;
+ index += tile.y * MOTION_BLUR_MAX_TILE;
+ index += motion_step * MOTION_BLUR_MAX_TILE * MOTION_BLUR_MAX_TILE;
+ return index;
+}
+
+#define MOTION_PREV 0u
+#define MOTION_NEXT 1u
+
+#define motion_blur_tile_indirection_store(table_, step_, tile, payload_) \
+ if (true) { \
+ uint index = motion_blur_tile_indirection_index(step_, tile); \
+ atomicMax(table_[index], payload_); \
+ }
+
+#define motion_blur_tile_indirection_load(table_, step_, tile_, result_) \
+ if (true) { \
+ uint index = motion_blur_tile_indirection_index(step_, tile_); \
+ result_ = motion_blur_tile_indirection_pack_payload(table_[index]); \
+ }
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
index 71921d0477a..dd047709afd 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl
@@ -39,6 +39,8 @@ bool closure_select(float weight, inout float total_weight, inout float r)
destination = candidate; \
}
+float g_closure_rand;
+
void closure_weights_reset()
{
g_diffuse_data.weight = 0.0;
@@ -58,18 +60,8 @@ void closure_weights_reset()
g_refraction_data.roughness = 0.0;
g_refraction_data.ior = 0.0;
- /* TEMP */
-#define P(x) ((x + 0.5) / 16.0)
- const vec4 dither_mat4x4[4] = vec4[4](vec4(P(0.0), P(8.0), P(2.0), P(10.0)),
- vec4(P(12.0), P(4.0), P(14.0), P(6.0)),
- vec4(P(3.0), P(11.0), P(1.0), P(9.0)),
- vec4(P(15.0), P(7.0), P(13.0), P(5.0)));
-#undef P
#if defined(GPU_FRAGMENT_SHADER)
- ivec2 pix = ivec2(gl_FragCoord.xy) % ivec2(4);
- g_diffuse_rand = dither_mat4x4[pix.x][pix.y];
- g_reflection_rand = dither_mat4x4[pix.x][pix.y];
- g_refraction_rand = dither_mat4x4[pix.x][pix.y];
+ g_diffuse_rand = g_reflection_rand = g_refraction_rand = g_closure_rand;
#else
g_diffuse_rand = 0.0;
g_reflection_rand = 0.0;
@@ -269,6 +261,10 @@ void output_aov(vec4 color, float value, uint hash)
# define nodetree_thickness() 0.1
#endif
+#ifdef GPU_VERTEX_SHADER
+# define closure_to_rgba(a) vec4(0.0)
+#endif
+
/* -------------------------------------------------------------------- */
/** \name Fragment Displacement
*
@@ -373,3 +369,71 @@ vec3 coordinate_incoming(vec3 P)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Volume Attribute post
+ *
+ * TODO(@fclem): These implementation details should concern the DRWManager and not be a fix on
+ * the engine side. But as of now, the engines are responsible for loading the attributes.
+ *
+ * \{ */
+
+#if defined(MAT_GEOM_VOLUME)
+
+float attr_load_temperature_post(float attr)
+{
+ /* Bring the into standard range without having to modify the grid values */
+ attr = (attr > 0.01) ? (attr * drw_volume.temperature_mul + drw_volume.temperature_bias) : 0.0;
+ return attr;
+}
+vec4 attr_load_color_post(vec4 attr)
+{
+ /* Density is premultiplied for interpolation, divide it out here. */
+ attr.rgb *= safe_rcp(attr.a);
+ attr.rgb *= drw_volume.color_mul.rgb;
+ attr.a = 1.0;
+ return attr;
+}
+
+#else /* Noop for any other surface. */
+
+float attr_load_temperature_post(float attr)
+{
+ return attr;
+}
+vec4 attr_load_color_post(vec4 attr)
+{
+ return attr;
+}
+
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Uniform Attributes
+ *
+ * TODO(@fclem): These implementation details should concern the DRWManager and not be a fix on
+ * the engine side. But as of now, the engines are responsible for loading the attributes.
+ *
+ * \{ */
+
+vec4 attr_load_uniform(vec4 attr, const uint attr_hash)
+{
+#if defined(OBATTR_LIB)
+ uint index = floatBitsToUint(ObjectAttributeStart);
+ for (uint i = 0; i < floatBitsToUint(ObjectAttributeLen); i++, index++) {
+ if (drw_attrs[index].hash_code == attr_hash) {
+ return vec4(drw_attrs[index].data_x,
+ drw_attrs[index].data_y,
+ drw_attrs[index].data_z,
+ drw_attrs[index].data_w);
+ }
+ }
+ return vec4(0.0);
+#else
+ return attr;
+#endif
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl
new file mode 100644
index 00000000000..0eea4a5ff33
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl
@@ -0,0 +1,104 @@
+
+/**
+ * Sampling data accessors and random number generators.
+ * Also contains some sample mapping functions.
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+/* -------------------------------------------------------------------- */
+/** \name Sampling data.
+ *
+ * Return a random values from Low Discrepancy Sequence in [0..1) range.
+ * This value is uniform (constant) for the whole scene sample.
+ * You might want to couple it with a noise function.
+ * \{ */
+
+#ifdef EEVEE_SAMPLING_DATA
+
+float sampling_rng_1D_get(const eSamplingDimension dimension)
+{
+ return sampling_buf.dimensions[dimension];
+}
+
+vec2 sampling_rng_2D_get(const eSamplingDimension dimension)
+{
+ return vec2(sampling_buf.dimensions[dimension], sampling_buf.dimensions[dimension + 1u]);
+}
+
+vec3 sampling_rng_3D_get(const eSamplingDimension dimension)
+{
+ return vec3(sampling_buf.dimensions[dimension],
+ sampling_buf.dimensions[dimension + 1u],
+ sampling_buf.dimensions[dimension + 2u]);
+}
+
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Random Number Generators.
+ * \{ */
+
+/* Interlieved gradient noise by Jorge Jimenez
+ * http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare
+ * Seeding found by Epic Game. */
+float interlieved_gradient_noise(vec2 pixel, float seed, float offset)
+{
+ pixel += seed * (vec2(47, 17) * 0.695);
+ return fract(offset + 52.9829189 * fract(0.06711056 * pixel.x + 0.00583715 * pixel.y));
+}
+
+/* From: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html */
+float van_der_corput_radical_inverse(uint bits)
+{
+#if 0 /* Reference */
+ bits = (bits << 16u) | (bits >> 16u);
+ bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+ bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+ bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+ bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+#else
+ bits = bitfieldReverse(bits);
+#endif
+ /* Same as dividing by 0x100000000. */
+ return float(bits) * 2.3283064365386963e-10;
+}
+
+vec2 hammersley_2d(float i, float sample_count)
+{
+ vec2 rand;
+ rand.x = i / sample_count;
+ rand.y = van_der_corput_radical_inverse(uint(i));
+ return rand;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Distribution mapping.
+ *
+ * Functions mapping input random numbers to sampling shapes (i.e: hemisphere).
+ * \{ */
+
+/* Given 2 random number in [0..1] range, return a random unit disk sample. */
+vec2 sample_disk(vec2 noise)
+{
+ float angle = noise.x * M_2PI;
+ return vec2(cos(angle), sin(angle)) * sqrt(noise.y);
+}
+
+/* This transform a 2d random sample (in [0..1] range) to a sample located on a cylinder of the
+ * same range. This is because the sampling functions expect such a random sample which is
+ * normally precomputed. */
+vec3 sample_cylinder(vec2 rand)
+{
+ float theta = rand.x;
+ float phi = (rand.y - 0.5) * M_2PI;
+ float cos_phi = cos(phi);
+ float sin_phi = sqrt(1.0 - sqr(cos_phi)) * sign(phi);
+ return vec3(theta, cos_phi, sin_phi);
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
index 7ddf941df7c..183aac1e546 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
@@ -6,10 +6,23 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
+vec4 closure_to_rgba(Closure cl)
+{
+ vec4 out_color;
+ out_color.rgb = g_emission;
+ out_color.a = saturate(1.0 - avg(g_transmittance));
+
+ /* Reset for the next closure tree. */
+ closure_weights_reset();
+
+ return out_color;
+}
+
/* From the paper "Hashed Alpha Testing" by Chris Wyman and Morgan McGuire. */
float hash(vec2 a)
{
@@ -61,8 +74,7 @@ void main()
nodetree_surface();
- // float noise_offset = sampling_rng_1D_get(sampling_buf, SAMPLING_TRANSPARENCY);
- float noise_offset = 0.5;
+ float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY);
float random_threshold = hashed_alpha_threshold(1.0, noise_offset, g_data.P);
float transparency = avg(g_transmittance);
@@ -72,14 +84,7 @@ void main()
#endif
#ifdef MAT_VELOCITY
- vec4 out_velocity_camera; /* TODO(fclem): Panoramic cameras. */
- velocity_camera(interp.P + motion.prev,
- interp.P,
- interp.P - motion.next,
- out_velocity_camera,
- out_velocity_view);
-
- /* For testing in viewport. */
- out_velocity_view.zw = vec2(0.0);
+ out_velocity = velocity_surface(interp.P + motion.prev, interp.P, interp.P + motion.next);
+ out_velocity = velocity_pack(out_velocity);
#endif
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
index 48ced4e5374..ab29067763d 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl
@@ -5,35 +5,36 @@
* This is used by alpha blended materials and materials using Shader to RGB nodes.
**/
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_hair_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl)
+#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_surf_lib.glsl)
-float spec_light(ClosureReflection ref)
-{
- float gloss = saturate(1.0 - ref.roughness);
- float shininess = exp2(10.0 * gloss + 1.0);
- vec3 N = ref.N;
- vec3 L = vec3(0.0, 0.0, 1.0);
- vec3 H = normalize(L + cameraVec(g_data.P));
- float spec_angle = saturate(dot(N, H));
- float normalization_factor = shininess * 0.125 + 1.0;
- float spec_light = pow(spec_angle, shininess) * saturate(dot(N, L)) * normalization_factor;
- return spec_light;
-}
-
vec4 closure_to_rgba(Closure cl)
{
+ vec3 diffuse_light = vec3(0.0);
+ vec3 reflection_light = vec3(0.0);
+ vec3 refraction_light = vec3(0.0);
+
+ float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos);
+
+ light_eval(g_diffuse_data,
+ g_reflection_data,
+ g_data.P,
+ cameraVec(g_data.P),
+ vP_z,
+ 0.01 /* TODO(fclem) thickness. */,
+ diffuse_light,
+ reflection_light);
+
vec4 out_color;
out_color.rgb = g_emission;
- out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight *
- saturate(g_diffuse_data.N.z * 0.5 + 0.5);
- out_color.rgb += g_reflection_data.color * g_reflection_data.weight *
- spec_light(g_reflection_data);
- out_color.rgb += g_refraction_data.color * g_refraction_data.weight *
- saturate(g_refraction_data.N.z * 0.5 + 0.5);
+ out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * diffuse_light;
+ out_color.rgb += g_reflection_data.color * g_reflection_data.weight * reflection_light;
+ out_color.rgb += g_refraction_data.color * g_refraction_data.weight * refraction_light;
out_color.a = saturate(1.0 - avg(g_transmittance));
@@ -47,15 +48,29 @@ void main()
{
init_globals();
+ float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r;
+ g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE));
+
fragment_displacement();
nodetree_surface();
g_holdout = saturate(g_holdout);
- vec3 diffuse_light = vec3(saturate(g_diffuse_data.N.z * 0.5 + 0.5));
- vec3 reflection_light = vec3(spec_light(g_reflection_data));
- vec3 refraction_light = vec3(saturate(g_refraction_data.N.z * 0.5 + 0.5));
+ vec3 diffuse_light = vec3(0.0);
+ vec3 reflection_light = vec3(0.0);
+ vec3 refraction_light = vec3(0.0);
+
+ float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos);
+
+ light_eval(g_diffuse_data,
+ g_reflection_data,
+ g_data.P,
+ cameraVec(g_data.P),
+ vP_z,
+ 0.01 /* TODO(fclem) thickness. */,
+ diffuse_light,
+ reflection_light);
g_diffuse_data.color *= g_diffuse_data.weight;
g_reflection_data.color *= g_reflection_data.weight;
@@ -82,13 +97,20 @@ void main()
out_normal += g_refraction_data.N * g_refraction_data.weight;
out_normal = safe_normalize(out_normal);
+#ifdef MAT_RENDER_PASS_SUPPORT
ivec2 out_texel = ivec2(gl_FragCoord.xy);
imageStore(rp_normal_img, out_texel, vec4(out_normal, 1.0));
- imageStore(rp_diffuse_light_img, out_texel, vec4(diffuse_light, 1.0));
+ imageStore(
+ rp_light_img, ivec3(out_texel, RENDER_PASS_LAYER_DIFFUSE_LIGHT), vec4(diffuse_light, 1.0));
+ imageStore(
+ rp_light_img, ivec3(out_texel, RENDER_PASS_LAYER_SPECULAR_LIGHT), vec4(specular_light, 1.0));
imageStore(rp_diffuse_color_img, out_texel, vec4(g_diffuse_data.color, 1.0));
- imageStore(rp_specular_light_img, out_texel, vec4(specular_light, 1.0));
imageStore(rp_specular_color_img, out_texel, vec4(specular_color, 1.0));
imageStore(rp_emission_img, out_texel, vec4(g_emission, 1.0));
+ imageStore(rp_cryptomatte_img,
+ out_texel,
+ vec4(cryptomatte_object_buf[resource_id], node_tree.crypto_hash, 0.0));
+#endif
out_radiance.rgb *= 1.0 - g_holdout;
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl
index 30b48edaa78..6c1fc818f41 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_lib.glsl
@@ -40,7 +40,7 @@ void init_globals_curves()
/* Shade as a cylinder. */
float cos_theta = interp.curves_time_width / interp.curves_thickness;
float sin_theta = sqrt(max(0.0, 1.0 - cos_theta * cos_theta));
- g_data.N = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta);
+ g_data.N = g_data.Ni = normalize(interp.N * sin_theta + interp.curves_binormal * cos_theta);
/* Costly, but follows cycles per pixel tangent space (not following curve shape). */
vec3 V = cameraVec(g_data.P);
@@ -60,13 +60,14 @@ void init_globals_curves()
void init_globals_gpencil()
{
/* Undo backface flip as the gpencil normal is already pointing towards the camera. */
- g_data.N = interp.N;
+ g_data.N = g_data.Ni = interp.N;
}
void init_globals()
{
/* Default values. */
g_data.P = interp.P;
+ g_data.Ni = interp.N;
g_data.N = safe_normalize(interp.N);
g_data.Ng = g_data.N;
g_data.is_strand = false;
@@ -81,6 +82,7 @@ void init_globals()
#ifdef GPU_FRAGMENT_SHADER
g_data.N = (FrontFacing) ? g_data.N : -g_data.N;
+ g_data.Ni = (FrontFacing) ? g_data.Ni : -g_data.Ni;
g_data.Ng = safe_normalize(cross(dFdx(g_data.P), dFdy(g_data.P)));
#endif
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
index b32c3c1c4eb..442c2579c84 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_world_frag.glsl
@@ -26,12 +26,18 @@ void main()
ivec2 out_texel = ivec2(gl_FragCoord.xy);
imageStore(rp_normal_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
- imageStore(rp_diffuse_light_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(
+ rp_light_img, ivec3(out_texel, RENDER_PASS_LAYER_DIFFUSE_LIGHT), vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(
+ rp_light_img, ivec3(out_texel, RENDER_PASS_LAYER_SPECULAR_LIGHT), vec4(0.0, 0.0, 0.0, 1.0));
imageStore(rp_diffuse_color_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
- imageStore(rp_specular_light_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
imageStore(rp_specular_color_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
imageStore(rp_emission_img, out_texel, vec4(0.0, 0.0, 0.0, 1.0));
+ imageStore(rp_cryptomatte_img, out_texel, vec4(0.0));
out_background.rgb = safe_color(g_emission) * (1.0 - g_holdout);
out_background.a = saturate(avg(g_transmittance)) * g_holdout;
+
+ /* World opacity. */
+ out_background = mix(vec4(0.0, 0.0, 0.0, 1.0), out_background, world_opacity_fade);
}
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl
index 435ae6658c9..8d02609fedc 100644
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl
+++ b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl
@@ -2,23 +2,38 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_camera_lib.glsl)
+vec4 velocity_pack(vec4 data)
+{
+ return data * 0.01;
+}
+
+vec4 velocity_unpack(vec4 data)
+{
+ return data * 100.0;
+}
+
#ifdef VELOCITY_CAMERA
/**
* Given a triple of position, compute the previous and next motion vectors.
- * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy)
+ * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy).
*/
-vec4 velocity_view(vec3 P_prev, vec3 P, vec3 P_next)
+vec4 velocity_surface(vec3 P_prv, vec3 P, vec3 P_nxt)
{
- vec2 prev_uv, curr_uv, next_uv;
-
- prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy;
- curr_uv = transform_point(ViewProjectionMatrix, P).xy;
- next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy;
-
- vec4 motion;
- motion.xy = prev_uv - curr_uv;
- motion.zw = curr_uv - next_uv;
+ /* NOTE: We don't use the drw_view.persmat to avoid adding the TAA jitter to the velocity. */
+ vec2 prev_uv = project_point(camera_prev.persmat, P_prv).xy;
+ vec2 curr_uv = project_point(camera_curr.persmat, P).xy;
+ vec2 next_uv = project_point(camera_next.persmat, P_nxt).xy;
+ /* Fix issue with perspective division. */
+ if (any(isnan(prev_uv))) {
+ prev_uv = curr_uv;
+ }
+ if (any(isnan(next_uv))) {
+ next_uv = curr_uv;
+ }
+ /* NOTE: We output both vectors in the same direction so we can reuse the same vector
+ * with rgrg swizzle in viewport. */
+ vec4 motion = vec4(prev_uv - curr_uv, curr_uv - next_uv);
/* Convert NDC velocity to UV velocity */
motion *= 0.5;
@@ -26,37 +41,55 @@ vec4 velocity_view(vec3 P_prev, vec3 P, vec3 P_next)
}
/**
- * Given a triple of position, compute the previous and next motion vectors.
- * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy)
- * \a velocity_camera is the motion in film UV space after camera projection.
- * \a velocity_view is the motion in ShadingView UV space. It is different
- * from velocity_camera for multi-view rendering.
+ * Given a view space view vector \a vV, compute the previous and next motion vectors for
+ * background pixels.
+ * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy).
*/
-void velocity_camera(vec3 P_prev, vec3 P, vec3 P_next, out vec4 vel_camera, out vec4 vel_view)
+vec4 velocity_background(vec3 vV)
{
- vec2 prev_uv, curr_uv, next_uv;
- prev_uv = camera_uv_from_world(camera_prev, P_prev);
- curr_uv = camera_uv_from_world(camera_curr, P);
- next_uv = camera_uv_from_world(camera_next, P_next);
-
- vel_camera.xy = prev_uv - curr_uv;
- vel_camera.zw = curr_uv - next_uv;
+ /* Only transform direction to avoid losing precision. */
+ vec3 V = transform_direction(camera_curr.viewinv, vV);
+ /* NOTE: We don't use the drw_view.winmat to avoid adding the TAA jitter to the velocity. */
+ vec2 prev_uv = project_point(camera_prev.winmat, V).xy;
+ vec2 curr_uv = project_point(camera_curr.winmat, V).xy;
+ vec2 next_uv = project_point(camera_next.winmat, V).xy;
+ /* NOTE: We output both vectors in the same direction so we can reuse the same vector
+ * with rgrg swizzle in viewport. */
+ vec4 motion = vec4(prev_uv - curr_uv, curr_uv - next_uv);
+ /* Convert NDC velocity to UV velocity */
+ motion *= 0.5;
- if (is_panoramic(camera_curr.type)) {
- /* This path is only used if using using panoramic projections. Since the views always have
- * the same 45° aperture angle, we can safely reuse the projection matrix. */
- prev_uv = transform_point(ProjectionMatrix, transform_point(camera_prev.viewmat, P_prev)).xy;
- curr_uv = transform_point(ViewProjectionMatrix, P).xy;
- next_uv = transform_point(ProjectionMatrix, transform_point(camera_next.viewmat, P_next)).xy;
+ return motion;
+}
- vel_view.xy = prev_uv - curr_uv;
- vel_view.zw = curr_uv - next_uv;
- /* Convert NDC velocity to UV velocity */
- vel_view *= 0.5;
- }
- else {
- vel_view = vel_camera;
+vec4 velocity_resolve(vec4 vector, vec2 uv, float depth)
+{
+ if (vector.x == VELOCITY_INVALID) {
+ bool is_background = (depth == 1.0);
+ if (is_background) {
+ /* NOTE: Use viewCameraVec to avoid imprecision if camera is far from origin. */
+ vec3 vV = viewCameraVec(get_view_space_from_depth(uv, 1.0));
+ return velocity_background(vV);
+ }
+ else {
+ /* Static geometry. No translation in world space. */
+ vec3 P = get_world_space_from_depth(uv, depth);
+ return velocity_surface(P, P, P);
+ }
}
+ return velocity_unpack(vector);
+}
+
+/**
+ * Load and resolve correct velocity as some pixels might still not have correct
+ * motion data for performance reasons.
+ * Returns motion vector in render UV space.
+ */
+vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth)
+{
+ vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(vector_tx, 0).xy);
+ vec4 vector = texelFetch(vector_tx, texel, 0);
+ return velocity_resolve(vector, uv, depth);
}
#endif
diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl
deleted file mode 100644
index b68b2eaf117..00000000000
--- a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_resolve_comp.glsl
+++ /dev/null
@@ -1,58 +0,0 @@
-
-/**
- * Fullscreen pass that compute motion vector for static geometry.
- * Animated geometry has already written correct motion vectors.
- */
-
-#pragma BLENDER_REQUIRE(common_view_lib.glsl)
-#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl)
-
-#define is_valid_output(img_) (imageSize(img_).x > 1)
-
-void main()
-{
- ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
- vec4 motion = imageLoad(velocity_view_img, texel);
-
- bool pixel_has_valid_motion = (motion.x != VELOCITY_INVALID);
- float depth = texelFetch(depth_tx, texel, 0).r;
- bool is_background = (depth == 1.0f);
-
- vec2 uv = vec2(texel) * drw_view.viewport_size_inverse;
- vec3 P_next, P_prev, P_curr;
-
- if (pixel_has_valid_motion) {
- /* Animated geometry. View motion already computed during prepass. Convert only to camera. */
- // P_prev = get_world_space_from_depth(uv + motion.xy, 0.5);
- // P_curr = get_world_space_from_depth(uv, 0.5);
- // P_next = get_world_space_from_depth(uv + motion.zw, 0.5);
- return;
- }
- else if (is_background) {
- /* NOTE: Use viewCameraVec to avoid imprecision if camera is far from origin. */
- vec3 vV = viewCameraVec(get_view_space_from_depth(uv, 1.0));
- vec3 V = transform_direction(ViewMatrixInverse, vV);
- /* Background has no motion under camera translation. Translate view vector with the camera. */
- /* WATCH(fclem): Might create precision issues. */
- P_next = camera_next.viewinv[3].xyz + V;
- P_curr = camera_curr.viewinv[3].xyz + V;
- P_prev = camera_prev.viewinv[3].xyz + V;
- }
- else {
- /* Static geometry. No translation in world space. */
- P_curr = get_world_space_from_depth(uv, depth);
- P_prev = P_curr;
- P_next = P_curr;
- }
-
- vec4 vel_camera, vel_view;
- velocity_camera(P_prev, P_curr, P_next, vel_camera, vel_view);
-
- if (in_texture_range(texel, depth_tx)) {
- imageStore(velocity_view_img, texel, vel_view);
-
- if (is_valid_output(velocity_camera_img)) {
- imageStore(velocity_camera_img, texel, vel_camera);
- }
- }
-}
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh
new file mode 100644
index 00000000000..b689a7f53a2
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "eevee_defines.hh"
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Setup
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_bokeh_lut)
+ .do_static_compilation(true)
+ .local_group_size(DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE)
+ .additional_info("eevee_shared", "draw_view")
+ .uniform_buf(6, "DepthOfFieldData", "dof_buf")
+ .image(0, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_gather_lut_img")
+ .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_scatter_lut_img")
+ .image(2, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_resolve_lut_img")
+ .compute_source("eevee_depth_of_field_bokeh_lut_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_setup)
+ .do_static_compilation(true)
+ .local_group_size(DOF_DEFAULT_GROUP_SIZE, DOF_DEFAULT_GROUP_SIZE)
+ .additional_info("eevee_shared", "draw_view")
+ .uniform_buf(6, "DepthOfFieldData", "dof_buf")
+ .sampler(0, ImageType::FLOAT_2D, "color_tx")
+ .sampler(1, ImageType::DEPTH_2D, "depth_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img")
+ .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img")
+ .compute_source("eevee_depth_of_field_setup_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_stabilize)
+ .do_static_compilation(true)
+ .local_group_size(DOF_STABILIZE_GROUP_SIZE, DOF_STABILIZE_GROUP_SIZE)
+ .additional_info("eevee_shared", "draw_view", "eevee_velocity_camera")
+ .uniform_buf(6, "DepthOfFieldData", "dof_buf")
+ .sampler(0, ImageType::FLOAT_2D, "coc_tx")
+ .sampler(1, ImageType::FLOAT_2D, "color_tx")
+ .sampler(2, ImageType::FLOAT_2D, "velocity_tx")
+ .sampler(3, ImageType::FLOAT_2D, "in_history_tx")
+ .sampler(4, ImageType::DEPTH_2D, "depth_tx")
+ .push_constant(Type::BOOL, "use_history")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img")
+ .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img")
+ .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_history_img")
+ .compute_source("eevee_depth_of_field_stabilize_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_downsample)
+ .do_static_compilation(true)
+ .local_group_size(DOF_DEFAULT_GROUP_SIZE, DOF_DEFAULT_GROUP_SIZE)
+ .additional_info("eevee_shared", "draw_view")
+ .sampler(0, ImageType::FLOAT_2D, "color_tx")
+ .sampler(1, ImageType::FLOAT_2D, "coc_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img")
+ .compute_source("eevee_depth_of_field_downsample_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_reduce)
+ .do_static_compilation(true)
+ .local_group_size(DOF_REDUCE_GROUP_SIZE, DOF_REDUCE_GROUP_SIZE)
+ .additional_info("eevee_shared", "draw_view")
+ .uniform_buf(6, "DepthOfFieldData", "dof_buf")
+ .sampler(0, ImageType::FLOAT_2D, "downsample_tx")
+ .storage_buf(0, Qualifier::WRITE, "ScatterRect", "scatter_fg_list_buf[]")
+ .storage_buf(1, Qualifier::WRITE, "ScatterRect", "scatter_bg_list_buf[]")
+ .storage_buf(2, Qualifier::READ_WRITE, "DrawCommand", "scatter_fg_indirect_buf")
+ .storage_buf(3, Qualifier::READ_WRITE, "DrawCommand", "scatter_bg_indirect_buf")
+ .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "inout_color_lod0_img")
+ .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_lod1_img")
+ .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_lod2_img")
+ .image(3, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_lod3_img")
+ .image(4, GPU_R16F, Qualifier::READ, ImageType::FLOAT_2D, "in_coc_lod0_img")
+ .image(5, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_lod1_img")
+ .image(6, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_lod2_img")
+ .image(7, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_lod3_img")
+ .compute_source("eevee_depth_of_field_reduce_comp.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Circle-Of-Confusion Tiles
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_flatten)
+ .do_static_compilation(true)
+ .local_group_size(DOF_TILES_FLATTEN_GROUP_SIZE, DOF_TILES_FLATTEN_GROUP_SIZE)
+ .additional_info("eevee_shared", "draw_view")
+ .sampler(0, ImageType::FLOAT_2D, "coc_tx")
+ .image(2, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_fg_img")
+ .image(3, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_bg_img")
+ .compute_source("eevee_depth_of_field_tiles_flatten_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate)
+ .additional_info("eevee_shared", "draw_view", "eevee_depth_of_field_tiles_common")
+ .local_group_size(DOF_TILES_DILATE_GROUP_SIZE, DOF_TILES_DILATE_GROUP_SIZE)
+ .image(2, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_fg_img")
+ .image(3, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_bg_img")
+ .push_constant(Type::INT, "ring_count")
+ .push_constant(Type::INT, "ring_width_multiplier")
+ .compute_source("eevee_depth_of_field_tiles_dilate_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate_minabs)
+ .do_static_compilation(true)
+ .define("DILATE_MODE_MIN_MAX", "false")
+ .additional_info("eevee_depth_of_field_tiles_dilate");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate_minmax)
+ .do_static_compilation(true)
+ .define("DILATE_MODE_MIN_MAX", "true")
+ .additional_info("eevee_depth_of_field_tiles_dilate");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_common)
+ .image(0, GPU_R11F_G11F_B10F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_fg_img")
+ .image(1, GPU_R11F_G11F_B10F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_bg_img");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Variations
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_no_lut)
+ .define("DOF_BOKEH_TEXTURE", "false")
+ /**
+ * WORKAROUND(@fclem): This is to keep the code as is for now. The bokeh_lut_tx is referenced
+ * even if not used after optimization. But we don't want to include it in the create infos.
+ */
+ .define("bokeh_lut_tx", "color_tx");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_lut)
+ .define("DOF_BOKEH_TEXTURE", "true")
+ .sampler(5, ImageType::FLOAT_2D, "bokeh_lut_tx");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_background).define("DOF_FOREGROUND_PASS", "false");
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_foreground).define("DOF_FOREGROUND_PASS", "true");
+
+#define EEVEE_DOF_FINAL_VARIATION(name, ...) \
+ GPU_SHADER_CREATE_INFO(name).additional_info(__VA_ARGS__).do_static_compilation(true);
+
+#define EEVEE_DOF_LUT_VARIATIONS(prefix, ...) \
+ EEVEE_DOF_FINAL_VARIATION(prefix##_lut, "eevee_depth_of_field_lut", __VA_ARGS__) \
+ EEVEE_DOF_FINAL_VARIATION(prefix##_no_lut, "eevee_depth_of_field_no_lut", __VA_ARGS__)
+
+#define EEVEE_DOF_GROUND_VARIATIONS(name, ...) \
+ EEVEE_DOF_LUT_VARIATIONS(name##_background, "eevee_depth_of_field_background", __VA_ARGS__) \
+ EEVEE_DOF_LUT_VARIATIONS(name##_foreground, "eevee_depth_of_field_foreground", __VA_ARGS__)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Gather
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_gather_common)
+ .additional_info("eevee_shared",
+ "draw_view",
+ "eevee_depth_of_field_tiles_common",
+ "eevee_sampling_data")
+ .uniform_buf(6, "DepthOfFieldData", "dof_buf")
+ .local_group_size(DOF_GATHER_GROUP_SIZE, DOF_GATHER_GROUP_SIZE)
+ .sampler(0, ImageType::FLOAT_2D, "color_tx")
+ .sampler(1, ImageType::FLOAT_2D, "color_bilinear_tx")
+ .sampler(2, ImageType::FLOAT_2D, "coc_tx")
+ .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img")
+ .image(3, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_weight_img");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_gather)
+ .image(4, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_occlusion_img")
+ .compute_source("eevee_depth_of_field_gather_comp.glsl")
+ .additional_info("eevee_depth_of_field_gather_common");
+
+EEVEE_DOF_GROUND_VARIATIONS(eevee_depth_of_field_gather, "eevee_depth_of_field_gather")
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_hole_fill)
+ .do_static_compilation(true)
+ .compute_source("eevee_depth_of_field_hole_fill_comp.glsl")
+ .additional_info("eevee_depth_of_field_gather_common", "eevee_depth_of_field_no_lut");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_filter)
+ .do_static_compilation(true)
+ .local_group_size(DOF_FILTER_GROUP_SIZE, DOF_FILTER_GROUP_SIZE)
+ .additional_info("eevee_shared")
+ .sampler(0, ImageType::FLOAT_2D, "color_tx")
+ .sampler(1, ImageType::FLOAT_2D, "weight_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img")
+ .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_weight_img")
+ .compute_source("eevee_depth_of_field_filter_comp.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Scatter
+ * \{ */
+
+GPU_SHADER_INTERFACE_INFO(eevee_depth_of_field_scatter_iface, "interp")
+ /** Colors, weights, and Circle of confusion radii for the 4 pixels to scatter. */
+ .flat(Type::VEC4, "color_and_coc1")
+ .flat(Type::VEC4, "color_and_coc2")
+ .flat(Type::VEC4, "color_and_coc3")
+ .flat(Type::VEC4, "color_and_coc4")
+ /** Sprite pixel position with origin at sprite center. In pixels. */
+ .no_perspective(Type::VEC2, "rect_uv1")
+ .no_perspective(Type::VEC2, "rect_uv2")
+ .no_perspective(Type::VEC2, "rect_uv3")
+ .no_perspective(Type::VEC2, "rect_uv4")
+ /** Scaling factor for the bokeh distance. */
+ .flat(Type::FLOAT, "distance_scale");
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_scatter)
+ .do_static_compilation(true)
+ .additional_info("eevee_shared", "draw_view")
+ .sampler(0, ImageType::FLOAT_2D, "occlusion_tx")
+ .sampler(1, ImageType::FLOAT_2D, "bokeh_lut_tx")
+ .storage_buf(0, Qualifier::READ, "ScatterRect", "scatter_list_buf[]")
+ .fragment_out(0, Type::VEC4, "out_color")
+ .push_constant(Type::BOOL, "use_bokeh_lut")
+ .vertex_out(eevee_depth_of_field_scatter_iface)
+ .vertex_source("eevee_depth_of_field_scatter_vert.glsl")
+ .fragment_source("eevee_depth_of_field_scatter_frag.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Resolve
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_depth_of_field_resolve)
+ .define("DOF_RESOLVE_PASS", "true")
+ .local_group_size(DOF_RESOLVE_GROUP_SIZE, DOF_RESOLVE_GROUP_SIZE)
+ .additional_info("eevee_shared",
+ "draw_view",
+ "eevee_depth_of_field_tiles_common",
+ "eevee_sampling_data")
+ .uniform_buf(6, "DepthOfFieldData", "dof_buf")
+ .sampler(0, ImageType::DEPTH_2D, "depth_tx")
+ .sampler(1, ImageType::FLOAT_2D, "color_tx")
+ .sampler(2, ImageType::FLOAT_2D, "color_bg_tx")
+ .sampler(3, ImageType::FLOAT_2D, "color_fg_tx")
+ .sampler(4, ImageType::FLOAT_2D, "color_hole_fill_tx")
+ .sampler(7, ImageType::FLOAT_2D, "weight_bg_tx")
+ .sampler(8, ImageType::FLOAT_2D, "weight_fg_tx")
+ .sampler(9, ImageType::FLOAT_2D, "weight_hole_fill_tx")
+ .sampler(10, ImageType::FLOAT_2D, "stable_color_tx")
+ .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img")
+ .compute_source("eevee_depth_of_field_resolve_comp.glsl");
+
+EEVEE_DOF_LUT_VARIATIONS(eevee_depth_of_field_resolve, "eevee_depth_of_field_resolve")
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
index eec7b8ae615..4541f14d96c 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
@@ -4,31 +4,35 @@
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(eevee_film)
- .uniform_buf(1, "FilmData", "film_buf")
+ .uniform_buf(6, "FilmData", "film_buf")
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
.sampler(1, ImageType::FLOAT_2D, "combined_tx")
.sampler(2, ImageType::FLOAT_2D, "normal_tx")
.sampler(3, ImageType::FLOAT_2D, "vector_tx")
- .sampler(4, ImageType::FLOAT_2D, "diffuse_light_tx")
+ .sampler(4, ImageType::FLOAT_2D_ARRAY, "light_tx")
.sampler(5, ImageType::FLOAT_2D, "diffuse_color_tx")
- .sampler(6, ImageType::FLOAT_2D, "specular_light_tx")
- .sampler(7, ImageType::FLOAT_2D, "specular_color_tx")
- .sampler(8, ImageType::FLOAT_2D, "volume_light_tx")
- .sampler(9, ImageType::FLOAT_2D, "emission_tx")
- .sampler(10, ImageType::FLOAT_2D, "environment_tx")
- .sampler(11, ImageType::FLOAT_2D, "shadow_tx")
- .sampler(12, ImageType::FLOAT_2D, "ambient_occlusion_tx")
- .sampler(13, ImageType::FLOAT_2D_ARRAY, "aov_color_tx")
- .sampler(14, ImageType::FLOAT_2D_ARRAY, "aov_value_tx")
- // .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx") /* TODO */
+ .sampler(6, ImageType::FLOAT_2D, "specular_color_tx")
+ .sampler(7, ImageType::FLOAT_2D, "volume_light_tx")
+ .sampler(8, ImageType::FLOAT_2D, "emission_tx")
+ .sampler(9, ImageType::FLOAT_2D, "environment_tx")
+ .sampler(10, ImageType::FLOAT_2D, "shadow_tx")
+ .sampler(11, ImageType::FLOAT_2D, "ambient_occlusion_tx")
+ .sampler(12, ImageType::FLOAT_2D_ARRAY, "aov_color_tx")
+ .sampler(13, ImageType::FLOAT_2D_ARRAY, "aov_value_tx")
+ /* Color History for TAA needs to be sampler to leverage bilinear sampling. */
+ .sampler(14, ImageType::FLOAT_2D, "in_combined_tx")
+ .sampler(15, ImageType::FLOAT_2D, "cryptomatte_tx")
.image(0, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "in_weight_img")
.image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img")
- .image(2, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_combined_img")
+ /* Color History for TAA needs to be sampler to leverage bilinear sampling. */
+ //.image(2, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_combined_img")
.image(3, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_combined_img")
.image(4, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "depth_img")
.image(5, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "color_accum_img")
.image(6, GPU_R16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "value_accum_img")
+ .image(7, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "cryptomatte_img")
.additional_info("eevee_shared")
+ .additional_info("eevee_velocity_camera")
.additional_info("draw_view");
GPU_SHADER_CREATE_INFO(eevee_film_frag)
@@ -42,3 +46,13 @@ GPU_SHADER_CREATE_INFO(eevee_film_comp)
.local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE)
.compute_source("eevee_film_comp.glsl")
.additional_info("eevee_film");
+
+GPU_SHADER_CREATE_INFO(eevee_film_cryptomatte_post)
+ .do_static_compilation(true)
+ .image(0, GPU_RGBA32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D_ARRAY, "cryptomatte_img")
+ .image(1, GPU_R32F, Qualifier::READ, ImageType::FLOAT_2D_ARRAY, "weight_img")
+ .push_constant(Type::INT, "cryptomatte_layer_len")
+ .push_constant(Type::INT, "cryptomatte_samples_per_layer")
+ .local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE)
+ .compute_source("eevee_film_cryptomatte_post_comp.glsl")
+ .additional_info("eevee_shared");
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh
new file mode 100644
index 00000000000..5e32631a8f8
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "eevee_defines.hh"
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(eevee_hiz_data)
+ .sampler(15, ImageType::FLOAT_2D, "hiz_tx")
+ .uniform_buf(5, "HiZData", "hiz_buf");
+
+GPU_SHADER_CREATE_INFO(eevee_hiz_update)
+ .do_static_compilation(true)
+ .local_group_size(FILM_GROUP_SIZE, FILM_GROUP_SIZE)
+ .storage_buf(0, Qualifier::READ_WRITE, "uint", "finished_tile_counter")
+ .sampler(0, ImageType::DEPTH_2D, "depth_tx")
+ .image(0, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_0")
+ .image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_1")
+ .image(2, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_2")
+ .image(3, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_3")
+ .image(4, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_4")
+ .image(5, GPU_R32F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "out_mip_5")
+ .image(6, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_6")
+ .image(7, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_mip_7")
+ .push_constant(Type::BOOL, "update_mip_0")
+ .compute_source("eevee_hiz_update_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_hiz_debug)
+ .do_static_compilation(true)
+ .fragment_out(0, Type::VEC4, "out_debug_color_add", DualBlend::SRC_0)
+ .fragment_out(0, Type::VEC4, "out_debug_color_mul", DualBlend::SRC_1)
+ .fragment_source("eevee_hiz_debug_frag.glsl")
+ .additional_info("eevee_shared", "eevee_hiz_data", "draw_fullscreen");
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh
new file mode 100644
index 00000000000..41602426a1d
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "eevee_defines.hh"
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Shared
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_light_data)
+ .storage_buf(LIGHT_CULL_BUF_SLOT, Qualifier::READ, "LightCullingData", "light_cull_buf")
+ .storage_buf(LIGHT_BUF_SLOT, Qualifier::READ, "LightData", "light_buf[]")
+ .storage_buf(LIGHT_ZBIN_BUF_SLOT, Qualifier::READ, "uint", "light_zbin_buf[]")
+ .storage_buf(LIGHT_TILE_BUF_SLOT, Qualifier::READ, "uint", "light_tile_buf[]");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Culling
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_light_culling_select)
+ .do_static_compilation(true)
+ .additional_info("eevee_shared", "draw_view")
+ .local_group_size(CULLING_SELECT_GROUP_SIZE)
+ .storage_buf(0, Qualifier::READ_WRITE, "LightCullingData", "light_cull_buf")
+ .storage_buf(1, Qualifier::READ, "LightData", "in_light_buf[]")
+ .storage_buf(2, Qualifier::WRITE, "LightData", "out_light_buf[]")
+ .storage_buf(3, Qualifier::WRITE, "float", "out_zdist_buf[]")
+ .storage_buf(4, Qualifier::WRITE, "uint", "out_key_buf[]")
+ .compute_source("eevee_light_culling_select_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_light_culling_sort)
+ .do_static_compilation(true)
+ .additional_info("eevee_shared", "draw_view")
+ .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
+ .storage_buf(1, Qualifier::READ, "LightData", "in_light_buf[]")
+ .storage_buf(2, Qualifier::WRITE, "LightData", "out_light_buf[]")
+ .storage_buf(3, Qualifier::READ, "float", "in_zdist_buf[]")
+ .storage_buf(4, Qualifier::READ, "uint", "in_key_buf[]")
+ .local_group_size(CULLING_SORT_GROUP_SIZE)
+ .compute_source("eevee_light_culling_sort_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_light_culling_zbin)
+ .do_static_compilation(true)
+ .additional_info("eevee_shared", "draw_view")
+ .local_group_size(CULLING_ZBIN_GROUP_SIZE)
+ .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
+ .storage_buf(1, Qualifier::READ, "LightData", "light_buf[]")
+ .storage_buf(2, Qualifier::WRITE, "uint", "out_zbin_buf[]")
+ .compute_source("eevee_light_culling_zbin_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_light_culling_tile)
+ .do_static_compilation(true)
+ .additional_info("eevee_shared", "draw_view")
+ .local_group_size(CULLING_TILE_GROUP_SIZE)
+ .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf")
+ .storage_buf(1, Qualifier::READ, "LightData", "light_buf[]")
+ .storage_buf(2, Qualifier::WRITE, "uint", "out_light_tile_buf[]")
+ .compute_source("eevee_light_culling_tile_comp.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Debug
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(eevee_light_culling_debug)
+ .do_static_compilation(true)
+ .fragment_out(0, Type::VEC4, "out_debug_color_add", DualBlend::SRC_0)
+ .fragment_out(0, Type::VEC4, "out_debug_color_mul", DualBlend::SRC_1)
+ .fragment_source("eevee_light_culling_debug_frag.glsl")
+ .additional_info(
+ "eevee_shared", "draw_view", "draw_fullscreen", "eevee_light_data", "eevee_hiz_data");
+
+/** \} */
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
index 950164f5b86..78d52d4b90e 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "eevee_defines.hh"
#include "gpu_shader_create_info.hh"
/* -------------------------------------------------------------------- */
@@ -12,8 +13,12 @@ GPU_SHADER_CREATE_INFO(eevee_shared)
.typedef_source("eevee_shader_shared.hh");
GPU_SHADER_CREATE_INFO(eevee_sampling_data)
+ .define("EEVEE_SAMPLING_DATA")
.additional_info("eevee_shared")
- .uniform_buf(14, "SamplingData", "sampling_buf");
+ .storage_buf(6, Qualifier::READ, "SamplingData", "sampling_buf");
+
+GPU_SHADER_CREATE_INFO(eevee_utility_texture)
+ .sampler(RBUFS_UTILITY_TEX_SLOT, ImageType::FLOAT_2D_ARRAY, "utility_tx");
/** \} */
@@ -27,7 +32,7 @@ GPU_SHADER_CREATE_INFO(eevee_geom_mesh)
.vertex_in(0, Type::VEC3, "pos")
.vertex_in(1, Type::VEC3, "nor")
.vertex_source("eevee_geom_mesh_vert.glsl")
- .additional_info("draw_mesh", "draw_resource_id_varying", "draw_resource_handle");
+ .additional_info("draw_modelmat_new", "draw_resource_id_varying", "draw_view");
GPU_SHADER_CREATE_INFO(eevee_geom_gpencil)
.additional_info("eevee_shared")
@@ -49,7 +54,7 @@ GPU_SHADER_CREATE_INFO(eevee_geom_world)
.define("MAT_GEOM_WORLD")
.builtins(BuiltinBits::VERTEX_ID)
.vertex_source("eevee_geom_world_vert.glsl")
- .additional_info("draw_modelmat", "draw_resource_id_varying", "draw_resource_handle");
+ .additional_info("draw_modelmat_new", "draw_resource_id_varying", "draw_view");
/** \} */
@@ -75,9 +80,21 @@ GPU_SHADER_INTERFACE_INFO(eevee_surf_iface, "interp")
GPU_SHADER_CREATE_INFO(eevee_aov_out)
.define("MAT_AOV_SUPPORT")
- .image_array_out(6, Qualifier::WRITE, GPU_RGBA16F, "aov_color_img")
- .image_array_out(7, Qualifier::WRITE, GPU_R16F, "aov_value_img")
- .storage_buf(7, Qualifier::READ, "AOVsInfoData", "aov_buf");
+ .image_array_out(RBUFS_AOV_COLOR_SLOT, Qualifier::WRITE, GPU_RGBA16F, "aov_color_img")
+ .image_array_out(RBUFS_AOV_VALUE_SLOT, Qualifier::WRITE, GPU_R16F, "aov_value_img")
+ .storage_buf(RBUFS_AOV_BUF_SLOT, Qualifier::READ, "AOVsInfoData", "aov_buf");
+
+GPU_SHADER_CREATE_INFO(eevee_render_pass_out)
+ .define("MAT_RENDER_PASS_SUPPORT")
+ .image_out(RBUFS_NORMAL_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img")
+ .image_array_out(RBUFS_LIGHT_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_light_img")
+ .image_out(RBUFS_DIFF_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img")
+ .image_out(RBUFS_SPEC_COLOR_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img")
+ .image_out(RBUFS_EMISSION_SLOT, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img");
+
+GPU_SHADER_CREATE_INFO(eevee_cryptomatte_out)
+ .storage_buf(7, Qualifier::READ, "vec2", "cryptomatte_object_buf[]", Frequency::PASS)
+ .image_out(7, Qualifier::WRITE, GPU_RGBA32F, "rp_cryptomatte_img");
GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
.vertex_out(eevee_surf_iface)
@@ -101,7 +118,6 @@ GPU_SHADER_CREATE_INFO(eevee_surf_deferred)
;
GPU_SHADER_CREATE_INFO(eevee_surf_forward)
- .auto_resource_location(true)
.vertex_out(eevee_surf_iface)
/* Early fragment test is needed for render passes support for forward surfaces. */
/* NOTE: This removes the possibility of using gl_FragDepth. */
@@ -109,42 +125,33 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward)
.fragment_out(0, Type::VEC4, "out_radiance", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "out_transmittance", DualBlend::SRC_1)
.fragment_source("eevee_surf_forward_frag.glsl")
- .image_out(0, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img")
- .image_out(1, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_light_img")
- .image_out(2, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img")
- .image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_light_img")
- .image_out(4, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img")
- .image_out(5, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img")
- .additional_info("eevee_aov_out"
- // "eevee_sampling_data",
- // "eevee_lightprobe_data",
+ .additional_info("eevee_cryptomatte_out",
+ "eevee_light_data",
+ "eevee_utility_texture",
+ "eevee_sampling_data"
+ // "eevee_lightprobe_data",
+ // "eevee_shadow_data"
/* Optionally added depending on the material. */
// "eevee_raytrace_data",
// "eevee_transmittance_data",
- // "eevee_utility_texture",
- // "eevee_light_data",
- // "eevee_shadow_data"
+ // "eevee_aov_out",
+ // "eevee_render_pass_out",
);
GPU_SHADER_CREATE_INFO(eevee_surf_depth)
.vertex_out(eevee_surf_iface)
.fragment_source("eevee_surf_depth_frag.glsl")
- // .additional_info("eevee_sampling_data", "eevee_utility_texture")
- ;
+ .additional_info("eevee_sampling_data", "eevee_utility_texture");
GPU_SHADER_CREATE_INFO(eevee_surf_world)
.vertex_out(eevee_surf_iface)
- .image_out(0, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_normal_img")
- .image_out(1, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_light_img")
- .image_out(2, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_diffuse_color_img")
- .image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_light_img")
- .image_out(4, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img")
- .image_out(5, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img")
+ .push_constant(Type::FLOAT, "world_opacity_fade")
.fragment_out(0, Type::VEC4, "out_background")
.fragment_source("eevee_surf_world_frag.glsl")
- .additional_info("eevee_aov_out"
- //"eevee_utility_texture"
- );
+ .additional_info("eevee_aov_out",
+ "eevee_cryptomatte_out",
+ "eevee_render_pass_out",
+ "eevee_utility_texture");
#undef image_out
#undef image_array_out
@@ -186,10 +193,7 @@ GPU_SHADER_CREATE_INFO(eevee_volume_deferred)
GPU_SHADER_CREATE_INFO(eevee_material_stub).define("EEVEE_MATERIAL_STUBS");
# define EEVEE_MAT_FINAL_VARIATION(name, ...) \
- GPU_SHADER_CREATE_INFO(name) \
- .additional_info(__VA_ARGS__) \
- .auto_resource_location(true) \
- .do_static_compilation(true);
+ GPU_SHADER_CREATE_INFO(name).additional_info(__VA_ARGS__).do_static_compilation(true);
# define EEVEE_MAT_GEOM_VARIATIONS(prefix, ...) \
EEVEE_MAT_FINAL_VARIATION(prefix##_world, "eevee_geom_world", __VA_ARGS__) \
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh
new file mode 100644
index 00000000000..ec302ec6770
--- /dev/null
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "eevee_defines.hh"
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten)
+ .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE)
+ .additional_info("eevee_shared", "draw_view", "eevee_velocity_camera")
+ .uniform_buf(6, "MotionBlurData", "motion_blur_buf")
+ .sampler(0, ImageType::DEPTH_2D, "depth_tx")
+ .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_img")
+ .compute_source("eevee_motion_blur_flatten_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_viewport)
+ .do_static_compilation(true)
+ .define("FLATTEN_VIEWPORT")
+ .image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_img")
+ .additional_info("eevee_motion_blur_tiles_flatten");
+
+GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_render)
+ .do_static_compilation(true)
+ .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_img")
+ .additional_info("eevee_motion_blur_tiles_flatten");
+
+GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_dilate)
+ .do_static_compilation(true)
+ .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE)
+ .additional_info("eevee_shared")
+ /* NOTE: See MotionBlurTileIndirection. */
+ .storage_buf(0, Qualifier::READ_WRITE, "uint", "tile_indirection_buf[]")
+ .image(1, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_img")
+ .compute_source("eevee_motion_blur_dilate_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(eevee_motion_blur_gather)
+ .do_static_compilation(true)
+ .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE)
+ .additional_info("eevee_shared", "draw_view", "eevee_sampling_data")
+ .uniform_buf(6, "MotionBlurData", "motion_blur_buf")
+ .sampler(0, ImageType::DEPTH_2D, "depth_tx")
+ .sampler(1, ImageType::FLOAT_2D, "velocity_tx")
+ .sampler(2, ImageType::FLOAT_2D, "in_color_tx")
+ /* NOTE: See MotionBlurTileIndirection. */
+ .storage_buf(0, Qualifier::READ, "uint", "tile_indirection_buf[]")
+ .image(0, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_img")
+ .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img")
+ .compute_source("eevee_motion_blur_gather_comp.glsl");
diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
index c6cbf9b1456..0a1c2721c61 100644
--- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
+++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "eevee_defines.hh"
#include "gpu_shader_create_info.hh"
/* -------------------------------------------------------------------- */
@@ -17,40 +18,22 @@ GPU_SHADER_INTERFACE_INFO(eevee_velocity_surface_iface, "motion")
GPU_SHADER_CREATE_INFO(eevee_velocity_camera)
.define("VELOCITY_CAMERA")
- .uniform_buf(1, "CameraData", "camera_prev")
- .uniform_buf(2, "CameraData", "camera_curr")
- .uniform_buf(3, "CameraData", "camera_next");
+ .uniform_buf(VELOCITY_CAMERA_PREV_BUF, "CameraData", "camera_prev")
+ .uniform_buf(VELOCITY_CAMERA_CURR_BUF, "CameraData", "camera_curr")
+ .uniform_buf(VELOCITY_CAMERA_NEXT_BUF, "CameraData", "camera_next");
GPU_SHADER_CREATE_INFO(eevee_velocity_geom)
.define("MAT_VELOCITY")
- .auto_resource_location(true)
- .storage_buf(4, Qualifier::READ, "mat4", "velocity_obj_prev_buf[]", Frequency::PASS)
- .storage_buf(5, Qualifier::READ, "mat4", "velocity_obj_next_buf[]", Frequency::PASS)
- .storage_buf(6, Qualifier::READ, "vec4", "velocity_geo_prev_buf[]", Frequency::PASS)
- .storage_buf(7, Qualifier::READ, "vec4", "velocity_geo_next_buf[]", Frequency::PASS)
- .storage_buf(
- 7, Qualifier::READ, "VelocityIndex", "velocity_indirection_buf[]", Frequency::PASS)
+ .storage_buf(VELOCITY_OBJ_PREV_BUF_SLOT, Qualifier::READ, "mat4", "velocity_obj_prev_buf[]")
+ .storage_buf(VELOCITY_OBJ_NEXT_BUF_SLOT, Qualifier::READ, "mat4", "velocity_obj_next_buf[]")
+ .storage_buf(VELOCITY_GEO_PREV_BUF_SLOT, Qualifier::READ, "vec4", "velocity_geo_prev_buf[]")
+ .storage_buf(VELOCITY_GEO_NEXT_BUF_SLOT, Qualifier::READ, "vec4", "velocity_geo_next_buf[]")
+ .storage_buf(VELOCITY_INDIRECTION_BUF_SLOT,
+ Qualifier::READ,
+ "VelocityIndex",
+ "velocity_indirection_buf[]")
.vertex_out(eevee_velocity_surface_iface)
- .fragment_out(0, Type::VEC4, "out_velocity_view")
+ .fragment_out(0, Type::VEC4, "out_velocity")
.additional_info("eevee_velocity_camera");
/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Velocity Resolve
- *
- * Computes velocity for static objects.
- * Also converts motion to camera space (as opposed to view space) if needed.
- * \{ */
-
-GPU_SHADER_CREATE_INFO(eevee_velocity_resolve)
- .do_static_compilation(true)
- .local_group_size(8, 8)
- .sampler(0, ImageType::DEPTH_2D, "depth_tx")
- .image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_view_img")
- .image(1, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "velocity_camera_img")
- .additional_info("eevee_shared")
- .compute_source("eevee_velocity_resolve_comp.glsl")
- .additional_info("draw_view", "eevee_velocity_camera");
-
-/** \} */
diff --git a/source/blender/draw/engines/external/external_engine.c b/source/blender/draw/engines/external/external_engine.c
index b9c09e2bc4f..3f047d8de68 100644
--- a/source/blender/draw/engines/external/external_engine.c
+++ b/source/blender/draw/engines/external/external_engine.c
@@ -236,7 +236,11 @@ static void external_draw_scene_do_v3d(void *vedata)
RegionView3D *rv3d = draw_ctx->rv3d;
ARegion *region = draw_ctx->region;
- DRW_state_reset_ex(DRW_STATE_DEFAULT & ~DRW_STATE_DEPTH_LESS_EQUAL);
+ DRW_state_reset_ex(DRW_STATE_WRITE_COLOR);
+
+ /* The external engine can use the OpenGL rendering API directly, so make sure the state is
+ * already applied. */
+ GPU_apply_state();
/* Create render engine. */
if (!rv3d->render_engine) {
@@ -332,6 +336,12 @@ static void external_draw_scene_do_image(void *UNUSED(vedata))
BLI_assert(re != NULL);
BLI_assert(engine != NULL);
+ DRW_state_reset_ex(DRW_STATE_WRITE_COLOR);
+
+ /* The external engine can use the OpenGL rendering API directly, so make sure the state is
+ * already applied. */
+ GPU_apply_state();
+
const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
/* Clear the depth buffer to the value used by the background overlay so that the overlay is not
diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
index 65ddb80ad55..e54ac99a888 100644
--- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c
+++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c
@@ -460,7 +460,7 @@ GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void)
GPENCIL_ViewLayerData **vldata = (GPENCIL_ViewLayerData **)DRW_view_layer_engine_data_ensure(
&draw_engine_gpencil_type, gpencil_view_layer_data_free);
- /* NOTE(&fclem): Putting this stuff in viewlayer means it is shared by all viewports.
+ /* NOTE(@fclem): Putting this stuff in view-layer means it is shared by all viewports.
* For now it is ok, but in the future, it could become a problem if we implement
* the caching system. */
if (*vldata == NULL) {
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c
index 4f520e61936..42c396a0d43 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.c
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.c
@@ -799,7 +799,7 @@ static void gpencil_draw_mask(GPENCIL_Data *vedata, GPENCIL_tObject *ob, GPENCIL
}
GPENCIL_tLayer *mask_layer = gpencil_layer_cache_get(ob, i);
- /* When filtering by viewlayer, the mask could be null and must be ignored. */
+ /* When filtering by view-layer, the mask could be null and must be ignored. */
if (mask_layer == NULL) {
continue;
}
diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h
index 332c7f67c64..2f9d20b3902 100644
--- a/source/blender/draw/engines/gpencil/gpencil_engine.h
+++ b/source/blender/draw/engines/gpencil/gpencil_engine.h
@@ -19,6 +19,8 @@
extern "C" {
#endif
+#define GP_LIGHT
+
#include "gpencil_defines.h"
#include "gpencil_shader_shared.h"
diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_shared.h b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h
index 50ff7e7efc7..4c621e955b9 100644
--- a/source/blender/draw/engines/gpencil/gpencil_shader_shared.h
+++ b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h
@@ -7,7 +7,9 @@
typedef struct gpMaterial gpMaterial;
typedef struct gpLight gpLight;
typedef enum gpMaterialFlag gpMaterialFlag;
+# ifdef GP_LIGHT
typedef enum gpLightType gpLightType;
+# endif
# endif
#endif
@@ -75,8 +77,9 @@ struct gpMaterial {
};
BLI_STATIC_ASSERT_ALIGN(gpMaterial, 16)
+#ifdef GP_LIGHT
struct gpLight {
-#ifndef GPU_SHADER
+# ifndef GPU_SHADER
float3 color;
gpLightType type;
float3 right;
@@ -87,7 +90,7 @@ struct gpLight {
float _pad0;
float3 position;
float _pad1;
-#else
+# else
/* Some drivers are completely messing the alignment or the fetches here.
* We are forced to pack these into vec4 otherwise we only get 0.0 as value. */
/* NOTE(@fclem): This was the case on MacOS OpenGL implementation.
@@ -97,17 +100,18 @@ struct gpLight {
float4 packed2;
float4 packed3;
float4 packed4;
-# define _color packed0.xyz
-# define _type packed0.w
-# define _right packed1.xyz
-# define _spot_size packed1.w
-# define _up packed2.xyz
-# define _spot_blend packed2.w
-# define _forward packed3.xyz
-# define _position packed4.xyz
-#endif
+# define _color packed0.xyz
+# define _type packed0.w
+# define _right packed1.xyz
+# define _spot_size packed1.w
+# define _up packed2.xyz
+# define _spot_blend packed2.w
+# define _forward packed3.xyz
+# define _position packed4.xyz
+# endif
};
BLI_STATIC_ASSERT_ALIGN(gpLight, 16)
+#endif
#ifndef GPU_SHADER
# undef gpMaterialFlag
diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl
index 5f5419bac47..b0ee059cb9d 100644
--- a/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl
+++ b/source/blender/draw/engines/gpencil/shaders/gpencil_vert.glsl
@@ -32,7 +32,7 @@ void main()
vec3 vert_N;
gpMaterial gp_mat = materials[ma1.x + gpMaterialOffset];
- gpMaterialFlag gp_flag = floatBitsToInt(gp_mat._flag);
+ gpMaterialFlag gp_flag = floatBitsToUint(gp_mat._flag);
gl_Position = gpencil_vertex(ma,
ma1,
diff --git a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh
index 3b4de704c00..ee22362e0d4 100644
--- a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh
+++ b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh
@@ -20,8 +20,8 @@ GPU_SHADER_INTERFACE_INFO(gpencil_geometry_iface, "gp_interp")
GPU_SHADER_CREATE_INFO(gpencil_geometry)
.do_static_compilation(true)
+ .define("GP_LIGHT")
.typedef_source("gpencil_defines.h")
- .typedef_source("gpencil_shader_shared.h")
.sampler(0, ImageType::FLOAT_2D, "gpFillTexture")
.sampler(1, ImageType::FLOAT_2D, "gpStrokeTexture")
.sampler(2, ImageType::DEPTH_2D, "gpSceneDepthTexture")
@@ -47,7 +47,7 @@ GPU_SHADER_CREATE_INFO(gpencil_geometry)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Fullscreen shaders
+/** \name Full-Screen Shaders
* \{ */
GPU_SHADER_CREATE_INFO(gpencil_layer_blend)
diff --git a/source/blender/draw/engines/overlay/overlay_antialiasing.c b/source/blender/draw/engines/overlay/overlay_antialiasing.cc
index 780915b7fc4..2242ad8b609 100644
--- a/source/blender/draw/engines/overlay/overlay_antialiasing.c
+++ b/source/blender/draw/engines/overlay/overlay_antialiasing.cc
@@ -34,8 +34,8 @@
* - Works without geometry shader.
* - Can inflate line thickness.
* - Coverage is very close to perfect and can even be filtered (Blackman-Harris, gaussian).
- * - Wires can "bleed" / overlap non-line objects since the filter is in screenspace.
- * - Only uses one additional lightweight fullscreen buffer (compared to MSAA/SMAA).
+ * - Wires can "bleed" / overlap non-line objects since the filter is in screen-space.
+ * - Only uses one additional lightweight full-screen buffer (compared to MSAA/SMAA).
* - No convergence time (compared to TAA).
*/
@@ -43,7 +43,7 @@
#include "ED_screen.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_antialiasing_init(OVERLAY_Data *vedata)
{
@@ -55,7 +55,8 @@ void OVERLAY_antialiasing_init(OVERLAY_Data *vedata)
/* Small texture which will have very small impact on render-time. */
if (txl->dummy_depth_tx == NULL) {
const float pixel[1] = {1.0f};
- txl->dummy_depth_tx = DRW_texture_create_2d(1, 1, GPU_DEPTH_COMPONENT24, 0, pixel);
+ txl->dummy_depth_tx = DRW_texture_create_2d(
+ 1, 1, GPU_DEPTH_COMPONENT24, DRWTextureFlag(0), pixel);
}
if (!DRW_state_is_fbo()) {
@@ -72,7 +73,7 @@ void OVERLAY_antialiasing_init(OVERLAY_Data *vedata)
if (pd->antialiasing.enabled) {
DRW_texture_ensure_fullscreen_2d(&txl->overlay_color_tx, GPU_SRGB8_A8, DRW_TEX_FILTER);
- DRW_texture_ensure_fullscreen_2d(&txl->overlay_line_tx, GPU_RGBA8, 0);
+ DRW_texture_ensure_fullscreen_2d(&txl->overlay_line_tx, GPU_RGBA8, DRWTextureFlag(0));
color_tex = txl->overlay_color_tx;
line_tex = txl->overlay_line_tx;
@@ -177,7 +178,7 @@ void OVERLAY_antialiasing_cache_finish(OVERLAY_Data *vedata)
const bool do_wireframe = pd->antialiasing.do_depth_copy ||
pd->antialiasing.do_depth_infront_copy;
if (pd->xray_enabled || do_wireframe) {
- DRW_texture_ensure_fullscreen_2d(&txl->temp_depth_tx, GPU_DEPTH24_STENCIL8, 0);
+ DRW_texture_ensure_fullscreen_2d(&txl->temp_depth_tx, GPU_DEPTH24_STENCIL8, DRWTextureFlag(0));
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.cc
index e38695c76ab..1e10fc7cd20 100644
--- a/source/blender/draw/engines/overlay/overlay_armature.c
+++ b/source/blender/draw/engines/overlay/overlay_armature.cc
@@ -37,7 +37,7 @@
#include "draw_common.h"
#include "draw_manager_text.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
#include "draw_cache_impl.h"
@@ -100,7 +100,7 @@ bool OVERLAY_armature_is_pose_mode(Object *ob, const DRWContextState *draw_ctx)
}
/* Armature parent is also handled by pose mode engine. */
- if ((active_ob != NULL) && (draw_ctx->object_mode & OB_MODE_ALL_WEIGHT_PAINT)) {
+ if ((active_ob != nullptr) && (draw_ctx->object_mode & OB_MODE_ALL_WEIGHT_PAINT)) {
if (ob == draw_ctx->object_pose) {
return true;
}
@@ -123,7 +123,7 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
pd->armature.do_pose_xray = (pd->overlay.flag & V3D_OVERLAY_BONE_SELECT) != 0;
pd->armature.do_pose_fade_geom = pd->armature.do_pose_xray &&
((draw_ctx->object_mode & OB_MODE_WEIGHT_PAINT) == 0) &&
- draw_ctx->object_pose != NULL;
+ draw_ctx->object_pose != nullptr;
const float wire_alpha = pd->overlay.bone_wire_alpha;
const bool use_wire_alpha = (wire_alpha < 1.0f);
@@ -139,16 +139,18 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
DRWShadingGroup *grp;
pd->armature_bone_select_act_grp = grp = DRW_shgroup_create(sh, psl->armature_bone_select_ps);
- DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){0.0f, 0.0f, 0.0f, alpha});
+ float4 color = {0.0f, 0.0f, 0.0f, alpha};
+ DRW_shgroup_uniform_vec4_copy(grp, "color", color);
pd->armature_bone_select_grp = grp = DRW_shgroup_create(sh, psl->armature_bone_select_ps);
- DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){0.0f, 0.0f, 0.0f, pow(alpha, 4)});
+ color = {0.0f, 0.0f, 0.0f, powf(alpha, 4)};
+ DRW_shgroup_uniform_vec4_copy(grp, "color", color);
}
for (int i = 0; i < 2; i++) {
struct GPUShader *sh;
struct GPUVertFormat *format;
- DRWShadingGroup *grp = NULL;
+ DRWShadingGroup *grp = nullptr;
OVERLAY_InstanceFormats *formats = OVERLAY_shader_instance_formats_get();
OVERLAY_ArmatureCallBuffers *cb = &pd->armature_call_buffers[i];
@@ -157,7 +159,8 @@ void OVERLAY_armature_cache_init(OVERLAY_Data *vedata)
cb->transp.custom_shapes_ghash = BLI_ghash_ptr_new(__func__);
DRWPass **p_armature_ps = &psl->armature_ps[i];
- DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : 0;
+ DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT :
+ DRWState(0);
state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_WRITE_DEPTH;
DRW_PASS_CREATE(*p_armature_ps, state | pd->clipping_state | infront_state);
DRWPass *armature_ps = *p_armature_ps;
@@ -612,15 +615,16 @@ static void drw_shgroup_bone_envelope(ArmatureDrawContext *ctx,
/* Custom (geometry) */
-extern void drw_batch_cache_validate(Object *custom);
-extern void drw_batch_cache_generate_requested_delayed(Object *custom);
+extern "C" void drw_batch_cache_validate(Object *custom);
+extern "C" void drw_batch_cache_generate_requested_delayed(Object *custom);
BLI_INLINE DRWCallBuffer *custom_bone_instance_shgroup(ArmatureDrawContext *ctx,
DRWShadingGroup *grp,
struct GPUBatch *custom_geom)
{
- DRWCallBuffer *buf = BLI_ghash_lookup(ctx->custom_shapes_ghash, custom_geom);
- if (buf == NULL) {
+ DRWCallBuffer *buf = static_cast<DRWCallBuffer *>(
+ BLI_ghash_lookup(ctx->custom_shapes_ghash, custom_geom));
+ if (buf == nullptr) {
OVERLAY_InstanceFormats *formats = OVERLAY_shader_instance_formats_get();
buf = DRW_shgroup_call_buffer_instance(grp, formats->instance_bone, custom_geom);
BLI_ghash_insert(ctx->custom_shapes_ghash, custom_geom, buf);
@@ -641,7 +645,7 @@ static void drw_shgroup_bone_custom_solid_mesh(ArmatureDrawContext *ctx,
DRW_mesh_batch_cache_validate(custom, mesh);
struct GPUBatch *surf = DRW_mesh_batch_cache_get_surface(mesh);
- struct GPUBatch *edges = DRW_mesh_batch_cache_get_edge_detection(mesh, NULL);
+ struct GPUBatch *edges = DRW_mesh_batch_cache_get_edge_detection(mesh, nullptr);
struct GPUBatch *ledges = DRW_mesh_batch_cache_get_loose_edges(mesh);
BoneInstanceData inst_data;
DRWCallBuffer *buf;
@@ -710,7 +714,7 @@ static void drw_shgroup_custom_bone_curve(ArmatureDrawContext *ctx,
/* This only handles curves without any surface. The other curve types should have been converted
* to meshes and rendered in the mesh drawing function. */
- struct GPUBatch *ledges = NULL;
+ struct GPUBatch *ledges = nullptr;
if (custom->type == OB_FONT) {
ledges = DRW_cache_text_edge_wire_get(custom);
}
@@ -744,14 +748,15 @@ static void drw_shgroup_bone_custom_solid(ArmatureDrawContext *ctx,
* other data type, but supporting all evaluated geometry components would require a much
* larger refactor of this area. */
Mesh *mesh = BKE_object_get_evaluated_mesh_no_subsurf(custom);
- if (mesh != NULL) {
+ if (mesh != nullptr) {
drw_shgroup_bone_custom_solid_mesh(
ctx, mesh, bone_mat, bone_color, hint_color, outline_color, custom);
return;
}
if (ELEM(custom->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
- drw_shgroup_custom_bone_curve(ctx, custom->data, bone_mat, outline_color, custom);
+ drw_shgroup_custom_bone_curve(
+ ctx, static_cast<Curve *>(custom->data), bone_mat, outline_color, custom);
}
}
@@ -762,13 +767,14 @@ static void drw_shgroup_bone_custom_wire(ArmatureDrawContext *ctx,
{
/* See comments in #drw_shgroup_bone_custom_solid. */
Mesh *mesh = BKE_object_get_evaluated_mesh_no_subsurf(custom);
- if (mesh != NULL) {
+ if (mesh != nullptr) {
drw_shgroup_bone_custom_mesh_wire(ctx, mesh, bone_mat, color, custom);
return;
}
if (ELEM(custom->type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
- drw_shgroup_custom_bone_curve(ctx, custom->data, bone_mat, color, custom);
+ drw_shgroup_custom_bone_curve(
+ ctx, static_cast<Curve *>(custom->data), bone_mat, color, custom);
}
}
@@ -890,14 +896,14 @@ enum {
/* This function sets the color-set for coloring a certain bone */
static void set_pchan_colorset(ArmatureDrawContext *ctx, Object *ob, bPoseChannel *pchan)
{
- bPose *pose = (ob) ? ob->pose : NULL;
- bArmature *arm = (ob) ? ob->data : NULL;
- bActionGroup *grp = NULL;
+ bPose *pose = (ob) ? ob->pose : nullptr;
+ bArmature *arm = (ob) ? static_cast<bArmature *>(ob->data) : nullptr;
+ bActionGroup *grp = nullptr;
short color_index = 0;
/* sanity check */
- if (ELEM(NULL, ob, arm, pose, pchan)) {
- ctx->bcolor = NULL;
+ if (ELEM(nullptr, ob, arm, pose, pchan)) {
+ ctx->bcolor = nullptr;
return;
}
@@ -914,7 +920,7 @@ static void set_pchan_colorset(ArmatureDrawContext *ctx, Object *ob, bPoseChanne
}
}
- /* bcolor is a pointer to the color set to use. If NULL, then the default
+ /* bcolor is a pointer to the color set to use. If nullptr, then the default
* color set (based on the theme colors for 3d-view) is used.
*/
if (color_index > 0) {
@@ -922,11 +928,11 @@ static void set_pchan_colorset(ArmatureDrawContext *ctx, Object *ob, bPoseChanne
ctx->bcolor = &btheme->tarm[(color_index - 1)];
}
else if (color_index == -1) {
- /* use the group's own custom color set (grp is always != NULL here) */
+ /* use the group's own custom color set (grp is always != nullptr here) */
ctx->bcolor = &grp->cs;
}
else {
- ctx->bcolor = NULL;
+ ctx->bcolor = nullptr;
}
}
@@ -1008,7 +1014,7 @@ static bool set_pchan_color(const ArmatureDrawContext *ctx,
return true;
}
case PCHAN_COLOR_CONSTS: {
- if ((bcolor == NULL) || (bcolor->flag & TH_WIRECOLOR_CONSTCOLS)) {
+ if ((bcolor == nullptr) || (bcolor->flag & TH_WIRECOLOR_CONSTCOLS)) {
if (constflag & PCHAN_HAS_TARGET) {
copy_v4_v4(fcolor, G_draw.block.color_bone_pose_target);
}
@@ -1192,15 +1198,15 @@ static const float *get_bone_hint_color(const ArmatureDrawContext *ctx,
static void pchan_draw_data_init(bPoseChannel *pchan)
{
- if (pchan->draw_data != NULL) {
+ if (pchan->draw_data != nullptr) {
if (pchan->draw_data->bbone_matrix_len != pchan->bone->segments) {
MEM_SAFE_FREE(pchan->draw_data);
}
}
- if (pchan->draw_data == NULL) {
- pchan->draw_data = MEM_mallocN(
- sizeof(*pchan->draw_data) + sizeof(Mat4) * pchan->bone->segments, __func__);
+ if (pchan->draw_data == nullptr) {
+ pchan->draw_data = static_cast<bPoseChannelDrawData *>(
+ MEM_mallocN(sizeof(*pchan->draw_data) + sizeof(Mat4) * pchan->bone->segments, __func__));
pchan->draw_data->bbone_matrix_len = pchan->bone->segments;
}
}
@@ -1243,11 +1249,11 @@ static void edbo_compute_bbone_child(bArmature *arm)
{
EditBone *eBone;
- for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
- eBone->bbone_child = NULL;
+ for (eBone = static_cast<EditBone *>(arm->edbo->first); eBone; eBone = eBone->next) {
+ eBone->bbone_child = nullptr;
}
- for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
+ for (eBone = static_cast<EditBone *>(arm->edbo->first); eBone; eBone = eBone->next) {
if (eBone->parent && (eBone->flag & BONE_CONNECTED)) {
eBone->parent->bbone_child = eBone;
}
@@ -1274,7 +1280,7 @@ static void ebone_spline_preview(EditBone *ebone, const float result_array[MAX_B
prev = ebone->parent;
}
else {
- prev = NULL;
+ prev = nullptr;
}
}
else {
@@ -1398,7 +1404,8 @@ static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pc
bbone_segments = eBone->segments;
}
- size_to_mat4(s, (const float[3]){xwidth, length / bbone_segments, zwidth});
+ const float3 size_vec = {xwidth, length / bbone_segments, zwidth};
+ size_to_mat4(s, size_vec);
/* Compute BBones segment matrices... */
/* Note that we need this even for one-segment bones, because box drawing need specific weirdo
@@ -1471,8 +1478,8 @@ static void draw_axes(ArmatureDrawContext *ctx,
{
float final_col[4];
const float *col = (ctx->const_color) ? ctx->const_color :
- (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? G_draw.block.color_text_hi :
- G_draw.block.color_text;
+ (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? &G_draw.block.color_text_hi.x :
+ &G_draw.block.color_text.x;
copy_v4_v4(final_col, col);
/* Mix with axes color. */
final_col[3] = (ctx->const_color) ? 1.0 : (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? 0.1 : 0.65;
@@ -1483,7 +1490,8 @@ static void draw_axes(ArmatureDrawContext *ctx,
float axis_mat[4][4];
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});
+ const float3 length_vec = {length, length, length};
+ rescale_m4(axis_mat, length_vec);
translate_m4(axis_mat, 0.0, arm->axes_position - 1.0, 0.0);
drw_shgroup_bone_axes(ctx, axis_mat, final_col);
@@ -1509,8 +1517,8 @@ static void draw_points(ArmatureDrawContext *ctx,
copy_v4_v4(col_solid_root, G_draw.block.color_bone_solid);
copy_v4_v4(col_solid_tail, G_draw.block.color_bone_solid);
- copy_v4_v4(col_wire_root, (ctx->const_color) ? ctx->const_color : G_draw.block.color_vertex);
- copy_v4_v4(col_wire_tail, (ctx->const_color) ? ctx->const_color : G_draw.block.color_vertex);
+ copy_v4_v4(col_wire_root, (ctx->const_color) ? ctx->const_color : &G_draw.block.color_vertex.x);
+ copy_v4_v4(col_wire_tail, (ctx->const_color) ? ctx->const_color : &G_draw.block.color_vertex.x);
const bool is_envelope_draw = (arm->drawtype == ARM_ENVELOPE);
const float envelope_ignore = -1.0f;
@@ -1704,7 +1712,7 @@ static void draw_bone_line(ArmatureDrawContext *ctx,
const float *col_head = no_display;
const float *col_tail = col_bone;
- if (ctx->const_color != NULL) {
+ if (ctx->const_color != nullptr) {
col_wire = no_display; /* actually shrink the display. */
col_bone = col_head = col_tail = ctx->const_color;
}
@@ -1724,7 +1732,7 @@ static void draw_bone_line(ArmatureDrawContext *ctx,
(pchan->bone->parent && (boneflag & BONE_CONNECTED)))) {
if (eBone) {
- col_head = (eBone->flag & BONE_ROOTSEL) ? G_draw.block.color_vertex_select : col_bone;
+ col_head = (eBone->flag & BONE_ROOTSEL) ? &G_draw.block.color_vertex_select.x : col_bone;
}
else {
col_head = col_bone;
@@ -1773,7 +1781,7 @@ static void draw_bone_wire(ArmatureDrawContext *ctx,
if (pchan) {
Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix;
- BLI_assert(bbones_mat != NULL);
+ BLI_assert(bbones_mat != nullptr);
for (int i = pchan->bone->segments; i--; bbones_mat++) {
drw_shgroup_bone_wire(ctx, bbones_mat->mat, col_wire);
@@ -1813,7 +1821,7 @@ static void draw_bone_box(ArmatureDrawContext *ctx,
if (pchan) {
Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix;
- BLI_assert(bbones_mat != NULL);
+ BLI_assert(bbones_mat != nullptr);
for (int i = pchan->bone->segments; i--; bbones_mat++) {
drw_shgroup_bone_box(ctx, bbones_mat->mat, col_solid, col_hint, col_wire);
@@ -1874,7 +1882,7 @@ static void draw_bone_degrees_of_freedom(ArmatureDrawContext *ctx, bPoseChannel
float xminmax[2], zminmax[2];
float color[4];
- if (ctx->dof_sphere == NULL) {
+ if (ctx->dof_sphere == nullptr) {
return;
}
@@ -1938,9 +1946,9 @@ static void pchan_draw_ik_lines(ArmatureDrawContext *ctx,
{
bConstraint *con;
bPoseChannel *parchan;
- float *line_start = NULL, *line_end = NULL;
+ float *line_start = nullptr, *line_end = nullptr;
- for (con = pchan->constraints.first; con; con = con->next) {
+ for (con = static_cast<bConstraint *>(pchan->constraints.first); con; con = con->next) {
if (con->enforce == 0.0f) {
continue;
}
@@ -2163,7 +2171,7 @@ static bool pchan_culling_test_envelope(const DRWView *view,
const Object *ob,
const bPoseChannel *pchan)
{
- const bArmature *arm = ob->data;
+ const bArmature *arm = static_cast<bArmature *>(ob->data);
BLI_assert(arm->drawtype == ARM_ENVELOPE);
UNUSED_VARS_NDEBUG(arm);
BoundSphere bsphere;
@@ -2177,7 +2185,7 @@ static bool pchan_culling_test_bbone(const DRWView *view,
const Object *ob,
const bPoseChannel *pchan)
{
- const bArmature *arm = ob->data;
+ const bArmature *arm = static_cast<bArmature *>(ob->data);
BLI_assert(arm->drawtype == ARM_B_BONE);
UNUSED_VARS_NDEBUG(arm);
const float ob_scale = mat4_to_size_max_axis(ob->obmat);
@@ -2220,15 +2228,16 @@ static void draw_armature_edit(ArmatureDrawContext *ctx)
const bool show_text = DRW_state_show_text();
const Object *ob_orig = DEG_get_original_object(ob);
- /* FIXME(campbell): We should be able to use the CoW object,
+ /* FIXME(@campbellbarton): We should be able to use the CoW object,
* however the active bone isn't updated. Long term solution is an 'EditArmature' struct.
* for now we can draw from the original armature. See: T66773. */
// bArmature *arm = ob->data;
- bArmature *arm = ob_orig->data;
+ bArmature *arm = static_cast<bArmature *>(ob_orig->data);
edbo_compute_bbone_child(arm);
- for (eBone = arm->edbo->first, index = ob_orig->runtime.select_id; eBone;
+ for (eBone = static_cast<EditBone *>(arm->edbo->first), index = ob_orig->runtime.select_id;
+ eBone;
eBone = eBone->next, index += 0x10000) {
if (eBone->layer & arm->layer) {
if ((eBone->flag & BONE_HIDDEN_A) == 0) {
@@ -2249,37 +2258,37 @@ static void draw_armature_edit(ArmatureDrawContext *ctx)
boneflag &= ~BONE_DRAW_LOCKED_WEIGHT;
if (!is_select) {
- draw_bone_relations(ctx, eBone, NULL, arm, boneflag, constflag);
+ draw_bone_relations(ctx, eBone, nullptr, arm, boneflag, constflag);
}
if (arm->drawtype == ARM_ENVELOPE) {
- draw_bone_update_disp_matrix_default(eBone, NULL);
- draw_bone_envelope(ctx, eBone, NULL, arm, boneflag, constflag, select_id);
+ draw_bone_update_disp_matrix_default(eBone, nullptr);
+ draw_bone_envelope(ctx, eBone, nullptr, arm, boneflag, constflag, select_id);
}
else if (arm->drawtype == ARM_LINE) {
- draw_bone_update_disp_matrix_default(eBone, NULL);
- draw_bone_line(ctx, eBone, NULL, arm, boneflag, constflag, select_id);
+ draw_bone_update_disp_matrix_default(eBone, nullptr);
+ draw_bone_line(ctx, eBone, nullptr, arm, boneflag, constflag, select_id);
}
else if (arm->drawtype == ARM_WIRE) {
- draw_bone_update_disp_matrix_bbone(eBone, NULL);
- draw_bone_wire(ctx, eBone, NULL, arm, boneflag, constflag, select_id);
+ draw_bone_update_disp_matrix_bbone(eBone, nullptr);
+ draw_bone_wire(ctx, eBone, nullptr, arm, boneflag, constflag, select_id);
}
else if (arm->drawtype == ARM_B_BONE) {
- draw_bone_update_disp_matrix_bbone(eBone, NULL);
- draw_bone_box(ctx, eBone, NULL, arm, boneflag, constflag, select_id);
+ draw_bone_update_disp_matrix_bbone(eBone, nullptr);
+ draw_bone_box(ctx, eBone, nullptr, arm, boneflag, constflag, select_id);
}
else {
- draw_bone_update_disp_matrix_default(eBone, NULL);
- draw_bone_octahedral(ctx, eBone, NULL, arm, boneflag, constflag, select_id);
+ draw_bone_update_disp_matrix_default(eBone, nullptr);
+ draw_bone_octahedral(ctx, eBone, nullptr, arm, boneflag, constflag, select_id);
}
if (!is_select) {
if (show_text && (arm->flag & ARM_DRAWNAMES)) {
- draw_bone_name(ctx, eBone, NULL, arm, boneflag);
+ draw_bone_name(ctx, eBone, nullptr, arm, boneflag);
}
if (arm->flag & ARM_DRAWAXES) {
- draw_axes(ctx, eBone, NULL, arm);
+ draw_axes(ctx, eBone, nullptr, arm);
}
}
}
@@ -2292,13 +2301,13 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
Object *ob = ctx->ob;
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
- bArmature *arm = ob->data;
+ bArmature *arm = static_cast<bArmature *>(ob->data);
bPoseChannel *pchan;
int index = -1;
const bool show_text = DRW_state_show_text();
bool draw_locked_weights = false;
- /* We can't safely draw non-updated pose, might contain NULL bone pointers... */
+ /* We can't safely draw non-updated pose, might contain nullptr bone pointers... */
if (ob->pose->flag & POSE_RECALC) {
return;
}
@@ -2323,7 +2332,7 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
/* Allow selection when in weight-paint mode
* (selection code ensures this won't become active). */
((draw_ctx->object_mode & OB_MODE_ALL_WEIGHT_PAINT) &&
- (draw_ctx->object_pose != NULL))))) &&
+ (draw_ctx->object_pose != nullptr))))) &&
DRW_state_is_select();
if (is_pose_select) {
@@ -2334,10 +2343,11 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
/* In weight paint mode retrieve the vertex group lock status. */
if ((draw_ctx->object_mode & OB_MODE_ALL_WEIGHT_PAINT) && (draw_ctx->object_pose == ob) &&
- (draw_ctx->obact != NULL)) {
+ (draw_ctx->obact != nullptr)) {
draw_locked_weights = true;
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ for (pchan = static_cast<bPoseChannel *>(ob->pose->chanbase.first); pchan;
+ pchan = pchan->next) {
pchan->bone->flag &= ~BONE_DRAW_LOCKED_WEIGHT;
}
@@ -2355,9 +2365,10 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
}
}
- const DRWView *view = is_pose_select ? DRW_view_default_get() : NULL;
+ const DRWView *view = is_pose_select ? DRW_view_default_get() : nullptr;
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next, index += 0x10000) {
+ for (pchan = static_cast<bPoseChannel *>(ob->pose->chanbase.first); pchan;
+ pchan = pchan->next, index += 0x10000) {
Bone *bone = pchan->bone;
const bool bone_visible = (bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)) == 0;
@@ -2392,43 +2403,43 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
}
if (!is_pose_select) {
- draw_bone_relations(ctx, NULL, pchan, arm, boneflag, constflag);
+ draw_bone_relations(ctx, nullptr, pchan, arm, boneflag, constflag);
}
if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) {
draw_bone_update_disp_matrix_custom(pchan);
if (!is_pose_select || pchan_culling_test_custom(view, ob, pchan)) {
- draw_bone_custom_shape(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ draw_bone_custom_shape(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);
}
}
else if (arm->drawtype == ARM_ENVELOPE) {
- draw_bone_update_disp_matrix_default(NULL, pchan);
+ draw_bone_update_disp_matrix_default(nullptr, pchan);
if (!is_pose_select || pchan_culling_test_envelope(view, ob, pchan)) {
- draw_bone_envelope(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ draw_bone_envelope(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);
}
}
else if (arm->drawtype == ARM_LINE) {
- draw_bone_update_disp_matrix_default(NULL, pchan);
+ draw_bone_update_disp_matrix_default(nullptr, pchan);
if (!is_pose_select || pchan_culling_test_line(view, ob, pchan)) {
- draw_bone_line(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ draw_bone_line(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);
}
}
else if (arm->drawtype == ARM_WIRE) {
- draw_bone_update_disp_matrix_bbone(NULL, pchan);
+ draw_bone_update_disp_matrix_bbone(nullptr, pchan);
if (!is_pose_select || pchan_culling_test_wire(view, ob, pchan)) {
- draw_bone_wire(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ draw_bone_wire(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);
}
}
else if (arm->drawtype == ARM_B_BONE) {
- draw_bone_update_disp_matrix_bbone(NULL, pchan);
+ draw_bone_update_disp_matrix_bbone(nullptr, pchan);
if (!is_pose_select || pchan_culling_test_bbone(view, ob, pchan)) {
- draw_bone_box(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ draw_bone_box(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);
}
}
else {
- draw_bone_update_disp_matrix_default(NULL, pchan);
+ draw_bone_update_disp_matrix_default(nullptr, pchan);
if (!is_pose_select || pchan_culling_test_octohedral(view, ob, pchan)) {
- draw_bone_octahedral(ctx, NULL, pchan, arm, boneflag, constflag, select_id);
+ draw_bone_octahedral(ctx, nullptr, pchan, arm, boneflag, constflag, select_id);
}
}
@@ -2439,11 +2450,11 @@ static void draw_armature_pose(ArmatureDrawContext *ctx)
}
if (show_text && (arm->flag & ARM_DRAWNAMES)) {
- draw_bone_name(ctx, NULL, pchan, arm, boneflag);
+ draw_bone_name(ctx, nullptr, pchan, arm, boneflag);
}
if (arm->flag & ARM_DRAWAXES) {
- draw_axes(ctx, NULL, pchan, arm);
+ draw_axes(ctx, nullptr, pchan, arm);
}
}
}
@@ -2467,7 +2478,7 @@ static void armature_context_setup(ArmatureDrawContext *ctx,
const bool draw_as_wire = (ob->dt < OB_SOLID);
const bool is_filled = (!pd->armature.transparent && !draw_as_wire) || !is_object_mode;
const bool is_transparent = pd->armature.transparent || (draw_as_wire && !is_object_mode);
- bArmature *arm = ob->data;
+ bArmature *arm = static_cast<bArmature *>(ob->data);
OVERLAY_ArmatureCallBuffers *cbo = &pd->armature_call_buffers[is_xray];
OVERLAY_ArmatureCallBuffersInner *cb = is_transparent ? &cbo->transp : &cbo->solid;
@@ -2476,8 +2487,8 @@ static void armature_context_setup(ArmatureDrawContext *ctx,
switch (arm->drawtype) {
case ARM_ENVELOPE:
ctx->envelope_outline = cb->envelope_outline;
- ctx->envelope_solid = (is_filled) ? cb->envelope_fill : NULL;
- ctx->envelope_distance = (do_envelope_dist) ? cb->envelope_distance : NULL;
+ ctx->envelope_solid = (is_filled) ? cb->envelope_fill : nullptr;
+ ctx->envelope_distance = (do_envelope_dist) ? cb->envelope_distance : nullptr;
break;
case ARM_LINE:
ctx->stick = cb->stick;
@@ -2487,20 +2498,20 @@ static void armature_context_setup(ArmatureDrawContext *ctx,
break;
case ARM_B_BONE:
ctx->outline = cb->box_outline;
- ctx->solid = (is_filled) ? cb->box_fill : NULL;
+ ctx->solid = (is_filled) ? cb->box_fill : nullptr;
break;
case ARM_OCTA:
ctx->outline = cb->octa_outline;
- ctx->solid = (is_filled) ? cb->octa_fill : NULL;
+ ctx->solid = (is_filled) ? cb->octa_fill : nullptr;
break;
}
ctx->ob = ob;
ctx->extras = &pd->extra_call_buffers[is_xray];
ctx->dof_lines = cb->dof_lines;
ctx->dof_sphere = cb->dof_sphere;
- ctx->point_solid = (is_filled) ? cb->point_fill : NULL;
+ ctx->point_solid = (is_filled) ? cb->point_fill : nullptr;
ctx->point_outline = cb->point_outline;
- ctx->custom_solid = (is_filled) ? cb->custom_fill : NULL;
+ ctx->custom_solid = (is_filled) ? cb->custom_fill : nullptr;
ctx->custom_outline = cb->custom_outline;
ctx->custom_wire = cb->custom_wire;
ctx->custom_shapes_ghash = cb->custom_shapes_ghash;
@@ -2518,7 +2529,7 @@ void OVERLAY_edit_armature_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
ArmatureDrawContext arm_ctx;
- armature_context_setup(&arm_ctx, pd, ob, true, true, false, NULL);
+ armature_context_setup(&arm_ctx, pd, ob, true, true, false, nullptr);
draw_armature_edit(&arm_ctx);
}
@@ -2526,7 +2537,7 @@ void OVERLAY_pose_armature_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
ArmatureDrawContext arm_ctx;
- armature_context_setup(&arm_ctx, pd, ob, true, false, true, NULL);
+ armature_context_setup(&arm_ctx, pd, ob, true, false, true, nullptr);
draw_armature_pose(&arm_ctx);
}
@@ -2586,8 +2597,8 @@ void OVERLAY_armature_cache_finish(OVERLAY_Data *vedata)
for (int i = 0; i < 2; i++) {
if (pd->armature_call_buffers[i].solid.custom_shapes_ghash) {
/* TODO(fclem): Do not free it for each frame but reuse it. Avoiding alloc cost. */
- BLI_ghash_free(pd->armature_call_buffers[i].solid.custom_shapes_ghash, NULL, NULL);
- BLI_ghash_free(pd->armature_call_buffers[i].transp.custom_shapes_ghash, NULL, NULL);
+ BLI_ghash_free(pd->armature_call_buffers[i].solid.custom_shapes_ghash, nullptr, nullptr);
+ BLI_ghash_free(pd->armature_call_buffers[i].transp.custom_shapes_ghash, nullptr, nullptr);
}
}
}
@@ -2604,7 +2615,7 @@ void OVERLAY_armature_in_front_draw(OVERLAY_Data *vedata)
{
OVERLAY_PassList *psl = vedata->psl;
- if (psl->armature_bone_select_ps == NULL || DRW_state_is_select()) {
+ if (psl->armature_bone_select_ps == nullptr || DRW_state_is_select()) {
DRW_draw_pass(psl->armature_transp_ps[1]);
DRW_draw_pass(psl->armature_ps[1]);
}
@@ -2615,7 +2626,7 @@ void OVERLAY_pose_draw(OVERLAY_Data *vedata)
OVERLAY_PassList *psl = vedata->psl;
OVERLAY_FramebufferList *fbl = vedata->fbl;
- if (psl->armature_bone_select_ps != NULL) {
+ if (psl->armature_bone_select_ps != nullptr) {
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_default_fb);
}
diff --git a/source/blender/draw/engines/overlay/overlay_background.c b/source/blender/draw/engines/overlay/overlay_background.cc
index 32bcd04e05b..e8bae74c70a 100644
--- a/source/blender/draw/engines/overlay/overlay_background.c
+++ b/source/blender/draw/engines/overlay/overlay_background.cc
@@ -10,7 +10,7 @@
#include "UI_resources.h"
#include "draw_manager_text.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_background_cache_init(OVERLAY_Data *vedata)
{
@@ -20,7 +20,7 @@ void OVERLAY_background_cache_init(OVERLAY_Data *vedata)
const DRWContextState *draw_ctx = DRW_context_state_get();
const Scene *scene = draw_ctx->scene;
const RegionView3D *rv3d = draw_ctx->rv3d;
- const BoundBox *bb = rv3d ? rv3d->clipbb : NULL;
+ const BoundBox *bb = rv3d ? rv3d->clipbb : nullptr;
const View3D *v3d = draw_ctx->v3d;
bool draw_clipping_bounds = (pd->clipping_state != 0);
@@ -80,7 +80,7 @@ void OVERLAY_background_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth);
DRW_shgroup_uniform_vec4_copy(grp, "colorOverride", color_override);
DRW_shgroup_uniform_int_copy(grp, "bgType", background_type);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
if (draw_clipping_bounds) {
@@ -93,10 +93,10 @@ void OVERLAY_background_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_vec3(grp, "boundbox", &bb->vec[0][0], 8);
struct GPUBatch *cube = DRW_cache_cube_get();
- DRW_shgroup_call(grp, cube, NULL);
+ DRW_shgroup_call(grp, cube, nullptr);
}
else {
- psl->clipping_frustum_ps = NULL;
+ psl->clipping_frustum_ps = nullptr;
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_edit_curve.c b/source/blender/draw/engines/overlay/overlay_edit_curve.cc
index 09e3c3410c3..b84eb9b3a43 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_curve.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_curve.cc
@@ -9,7 +9,7 @@
#include "DNA_curve_types.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_edit_curve_cache_init(OVERLAY_Data *vedata)
{
@@ -65,7 +65,7 @@ void OVERLAY_edit_curve_cache_populate(OVERLAY_Data *vedata, Object *ob)
bool draw_normals = (pd->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_NORMALS) != 0;
bool do_xray = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
- Curve *cu = ob->data;
+ Curve *cu = static_cast<Curve *>(ob->data);
struct GPUBatch *geom;
geom = DRW_cache_curve_edge_wire_get(ob);
diff --git a/source/blender/draw/engines/overlay/overlay_edit_curves.cc b/source/blender/draw/engines/overlay/overlay_edit_curves.cc
index 02e40ea0304..dab44c655a5 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_curves.cc
+++ b/source/blender/draw/engines/overlay/overlay_edit_curves.cc
@@ -11,7 +11,7 @@
#include "draw_cache_impl.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_edit_curves_init(OVERLAY_Data *vedata)
{
diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.cc
index 4d65cbc04ff..4509fd53ed8 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_mesh.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.cc
@@ -18,7 +18,7 @@
#include "draw_cache_impl.h"
#include "draw_manager_text.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
#define OVERLAY_EDIT_TEXT \
(V3D_OVERLAY_EDIT_EDGE_LEN | V3D_OVERLAY_EDIT_FACE_AREA | V3D_OVERLAY_EDIT_FACE_ANG | \
@@ -45,9 +45,9 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
OVERLAY_PassList *psl = vedata->psl;
OVERLAY_PrivateData *pd = vedata->stl->pd;
OVERLAY_ShadingData *shdata = &pd->shdata;
- DRWShadingGroup *grp = NULL;
- GPUShader *sh = NULL;
- DRWState state = 0;
+ DRWShadingGroup *grp = nullptr;
+ GPUShader *sh = nullptr;
+ DRWState state = DRWState(0);
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
@@ -109,7 +109,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
{
/* Normals */
state = DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL |
- (pd->edit_mesh.do_zbufclip ? DRW_STATE_BLEND_ALPHA : 0);
+ (pd->edit_mesh.do_zbufclip ? DRW_STATE_BLEND_ALPHA : DRWState(0));
DRW_PASS_CREATE(psl->edit_mesh_normals_ps, state | pd->clipping_state);
sh = OVERLAY_shader_edit_mesh_normal();
@@ -204,7 +204,7 @@ void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_state_enable(grp, DRW_STATE_WRITE_DEPTH);
}
else {
- pd->edit_mesh_facedots_grp[i] = NULL;
+ pd->edit_mesh_facedots_grp[i] = nullptr;
}
}
}
@@ -234,24 +234,24 @@ static void overlay_edit_mesh_add_ob_to_pass(OVERLAY_PrivateData *pd, Object *ob
pd->edit_mesh_faces_grp[in_front];
skin_roots_shgrp = pd->edit_mesh_skin_roots_grp[in_front];
- geom_edges = DRW_mesh_batch_cache_get_edit_edges(ob->data);
- geom_tris = DRW_mesh_batch_cache_get_edit_triangles(ob->data);
+ geom_edges = DRW_mesh_batch_cache_get_edit_edges(me);
+ geom_tris = DRW_mesh_batch_cache_get_edit_triangles(me);
DRW_shgroup_call_no_cull(edge_shgrp, geom_edges, ob);
DRW_shgroup_call_no_cull(face_shgrp, geom_tris, ob);
if (pd->edit_mesh.select_vert) {
- geom_verts = DRW_mesh_batch_cache_get_edit_vertices(ob->data);
+ geom_verts = DRW_mesh_batch_cache_get_edit_vertices(me);
DRW_shgroup_call_no_cull(vert_shgrp, geom_verts, ob);
if (has_skin_roots) {
circle = DRW_cache_circle_get();
- skin_roots = DRW_mesh_batch_cache_get_edit_skin_roots(ob->data);
+ skin_roots = DRW_mesh_batch_cache_get_edit_skin_roots(me);
DRW_shgroup_call_instances_with_attrs(skin_roots_shgrp, ob, circle, skin_roots);
}
}
if (fdot_shgrp) {
- geom_fcenter = DRW_mesh_batch_cache_get_edit_facedots(ob->data);
+ geom_fcenter = DRW_mesh_batch_cache_get_edit_facedots(me);
DRW_shgroup_call_no_cull(fdot_shgrp, geom_fcenter, ob);
}
}
@@ -259,7 +259,7 @@ static void overlay_edit_mesh_add_ob_to_pass(OVERLAY_PrivateData *pd, Object *ob
void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
- struct GPUBatch *geom = NULL;
+ struct GPUBatch *geom = nullptr;
bool draw_as_solid = (ob->dt > OB_WIRE);
bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
@@ -283,16 +283,17 @@ void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (vnormals_do || lnormals_do || fnormals_do) {
struct GPUBatch *normal_geom = DRW_cache_normal_arrow_get();
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (vnormals_do) {
- geom = DRW_mesh_batch_cache_get_edit_vnors(ob->data);
+ geom = DRW_mesh_batch_cache_get_edit_vnors(me);
DRW_shgroup_call_instances_with_attrs(pd->edit_mesh_normals_grp, ob, normal_geom, geom);
}
if (lnormals_do) {
- geom = DRW_mesh_batch_cache_get_edit_lnors(ob->data);
+ geom = DRW_mesh_batch_cache_get_edit_lnors(me);
DRW_shgroup_call_instances_with_attrs(pd->edit_mesh_normals_grp, ob, normal_geom, geom);
}
if (fnormals_do) {
- geom = DRW_mesh_batch_cache_get_edit_facedots(ob->data);
+ geom = DRW_mesh_batch_cache_get_edit_facedots(me);
DRW_shgroup_call_instances_with_attrs(pd->edit_mesh_normals_grp, ob, normal_geom, geom);
}
}
@@ -351,7 +352,7 @@ void OVERLAY_edit_mesh_draw(OVERLAY_Data *vedata)
DRW_view_set_active(pd->view_edit_faces_cage);
DRW_draw_pass(psl->edit_mesh_faces_cage_ps[NOT_IN_FRONT]);
- DRW_view_set_active(NULL);
+ DRW_view_set_active(nullptr);
GPU_framebuffer_bind(fbl->overlay_in_front_fb);
GPU_framebuffer_clear_depth(fbl->overlay_in_front_fb, 1.0f);
@@ -372,7 +373,7 @@ void OVERLAY_edit_mesh_draw(OVERLAY_Data *vedata)
}
if (!DRW_pass_is_empty(psl->edit_mesh_depth_ps[IN_FRONT])) {
- DRW_view_set_active(NULL);
+ DRW_view_set_active(nullptr);
DRW_draw_pass(psl->edit_mesh_depth_ps[IN_FRONT]);
}
diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.cc
index dfef5b3c241..f541619ccdd 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_text.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_text.cc
@@ -7,11 +7,13 @@
#include "DRW_render.h"
+#include "UI_resources.h"
+
#include "BKE_vfont.h"
#include "DNA_curve_types.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_edit_text_cache_init(OVERLAY_Data *vedata)
{
@@ -38,17 +40,24 @@ void OVERLAY_edit_text_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_vec4_copy(grp, "color", G_draw.block.color_wire);
}
{
+ /* Cursor (text caret). */
state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA;
- DRW_PASS_CREATE(psl->edit_text_overlay_ps, state | pd->clipping_state);
-
+ DRW_PASS_CREATE(psl->edit_text_cursor_ps, state | pd->clipping_state);
sh = OVERLAY_shader_uniform_color();
- pd->edit_text_overlay_grp = grp = DRW_shgroup_create(sh, psl->edit_text_overlay_ps);
+ pd->edit_text_cursor_grp = grp = DRW_shgroup_create(sh, psl->edit_text_cursor_ps);
+ DRW_shgroup_uniform_vec4(grp, "color", pd->edit_text.cursor_color, 1);
- DRW_shgroup_uniform_vec4(grp, "color", pd->edit_text.overlay_color, 1);
+ /* Selection boxes. */
+ state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA;
+ DRW_PASS_CREATE(psl->edit_text_selection_ps, state | pd->clipping_state);
+ sh = OVERLAY_shader_uniform_color();
+ pd->edit_text_selection_grp = grp = DRW_shgroup_create(sh, psl->edit_text_selection_ps);
+ DRW_shgroup_uniform_vec4(grp, "color", pd->edit_text.selection_color, 1);
- state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_MUL | DRW_STATE_DEPTH_GREATER_EQUAL |
+ /* Highlight text within selection boxes. */
+ state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA | DRW_STATE_DEPTH_GREATER_EQUAL |
pd->clipping_state;
- DRW_PASS_INSTANCE_CREATE(psl->edit_text_darken_ps, psl->edit_text_overlay_ps, state);
+ DRW_PASS_INSTANCE_CREATE(psl->edit_text_highlight_ps, psl->edit_text_selection_ps, state);
}
{
/* Create view which will render everything (hopefully) behind the text geometry. */
@@ -74,7 +83,7 @@ static void v2_quad_corners_to_mat4(const float corners[4][2], float r_mat[4][4]
static void edit_text_cache_populate_select(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
- const Curve *cu = ob->data;
+ const Curve *cu = static_cast<Curve *>(ob->data);
EditFont *ef = cu->editfont;
float final_mat[4][4], box[4][2];
struct GPUBatch *geom = DRW_cache_quad_get();
@@ -112,14 +121,14 @@ static void edit_text_cache_populate_select(OVERLAY_Data *vedata, Object *ob)
v2_quad_corners_to_mat4(box, final_mat);
mul_m4_m4m4(final_mat, ob->obmat, final_mat);
- DRW_shgroup_call_obmat(pd->edit_text_overlay_grp, geom, final_mat);
+ DRW_shgroup_call_obmat(pd->edit_text_selection_grp, geom, final_mat);
}
}
static void edit_text_cache_populate_cursor(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
- const Curve *cu = ob->data;
+ const Curve *cu = static_cast<Curve *>(ob->data);
EditFont *edit_font = cu->editfont;
float(*cursor)[2] = edit_font->textcurs;
float mat[4][4];
@@ -128,13 +137,13 @@ static void edit_text_cache_populate_cursor(OVERLAY_Data *vedata, Object *ob)
mul_m4_m4m4(mat, ob->obmat, mat);
struct GPUBatch *geom = DRW_cache_quad_get();
- DRW_shgroup_call_obmat(pd->edit_text_overlay_grp, geom, mat);
+ DRW_shgroup_call_obmat(pd->edit_text_cursor_grp, geom, mat);
}
static void edit_text_cache_populate_boxes(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob);
- const Curve *cu = ob->data;
+ const Curve *cu = static_cast<Curve *>(ob->data);
for (int i = 0; i < cu->totbox; i++) {
TextBox *tb = &cu->tb[i];
@@ -193,11 +202,18 @@ void OVERLAY_edit_text_draw(OVERLAY_Data *vedata)
DRW_view_set_active(pd->view_edit_text);
- /* Alpha blended. */
- copy_v4_fl4(pd->edit_text.overlay_color, 0.8f, 0.8f, 0.8f, 0.5f);
- DRW_draw_pass(psl->edit_text_overlay_ps);
+ /* Selection Boxes. */
+ UI_GetThemeColor4fv(TH_WIDGET_TEXT_SELECTION, pd->edit_text.selection_color);
+ srgb_to_linearrgb_v4(pd->edit_text.selection_color, pd->edit_text.selection_color);
+ DRW_draw_pass(psl->edit_text_selection_ps);
+
+ /* Highlight text within selection boxes. */
+ UI_GetThemeColor4fv(TH_WIDGET_TEXT_HIGHLIGHT, pd->edit_text.selection_color);
+ srgb_to_linearrgb_v4(pd->edit_text.selection_color, pd->edit_text.selection_color);
+ DRW_draw_pass(psl->edit_text_highlight_ps);
- /* Multiply previous result where depth test fail. */
- copy_v4_fl4(pd->edit_text.overlay_color, 0.0f, 0.0f, 0.0f, 1.0f);
- DRW_draw_pass(psl->edit_text_darken_ps);
+ /* Cursor (text caret). */
+ UI_GetThemeColor4fv(TH_WIDGET_TEXT_CURSOR, pd->edit_text.cursor_color);
+ srgb_to_linearrgb_v4(pd->edit_text.cursor_color, pd->edit_text.cursor_color);
+ DRW_draw_pass(psl->edit_text_cursor_ps);
}
diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.cc
index 4cfe9fcea4e..c173201b271 100644
--- a/source/blender/draw/engines/overlay/overlay_edit_uv.c
+++ b/source/blender/draw/engines/overlay/overlay_edit_uv.cc
@@ -31,7 +31,7 @@
#include "UI_interface.h"
#include "UI_resources.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
/* Forward declarations. */
static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob);
@@ -70,7 +70,7 @@ static GPUTexture *edit_uv_mask_texture(
{
const int height = (float)height_ * (aspy / aspx);
MaskRasterHandle *handle;
- float *buffer = MEM_mallocN(sizeof(float) * height * width, __func__);
+ float *buffer = static_cast<float *>(MEM_mallocN(sizeof(float) * height * width, __func__));
/* Initialize rasterization handle. */
handle = BKE_maskrasterize_handle_new();
@@ -102,10 +102,12 @@ void OVERLAY_edit_uv_init(OVERLAY_Data *vedata)
Image *image = sima->image;
/* By design no image is an image type. This so editor shows UV's by default. */
- const bool is_image_type =
- (image == NULL) || ELEM(image->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER, IMA_TYPE_UV_TEST);
+ const bool is_image_type = (image == nullptr) || ELEM(image->type,
+ IMA_TYPE_IMAGE,
+ IMA_TYPE_MULTILAYER,
+ IMA_TYPE_UV_TEST);
const bool is_uv_editor = sima->mode == SI_MODE_UV;
- const bool has_edit_object = (draw_ctx->object_edit) != NULL;
+ const bool has_edit_object = (draw_ctx->object_edit) != nullptr;
const bool is_paint_mode = sima->mode == SI_MODE_PAINT;
const bool is_view_mode = sima->mode == SI_MODE_VIEW;
const bool is_mask_mode = sima->mode == SI_MODE_MASK;
@@ -141,12 +143,13 @@ void OVERLAY_edit_uv_init(OVERLAY_Data *vedata)
((draw_ctx->object_mode & (OB_MODE_TEXTURE_PAINT)) != 0)) ||
(do_uv_overlay && (show_modified_uvs)));
- pd->edit_uv.do_mask_overlay = show_overlays && is_mask_mode && (sima->mask_info.mask != NULL) &&
+ pd->edit_uv.do_mask_overlay = show_overlays && is_mask_mode &&
+ (sima->mask_info.mask != nullptr) &&
((sima->mask_info.draw_flag & MASK_DRAWFLAG_OVERLAY) != 0);
- pd->edit_uv.mask_overlay_mode = sima->mask_info.overlay_mode;
+ pd->edit_uv.mask_overlay_mode = eMaskOverlayMode(sima->mask_info.overlay_mode);
pd->edit_uv.mask = sima->mask_info.mask ? (Mask *)DEG_get_evaluated_id(
draw_ctx->depsgraph, &sima->mask_info.mask->id) :
- NULL;
+ nullptr;
pd->edit_uv.do_uv_stretching_overlay = show_overlays && do_uvstretching_overlay;
pd->edit_uv.uv_opacity = sima->uv_opacity;
@@ -157,10 +160,9 @@ void OVERLAY_edit_uv_init(OVERLAY_Data *vedata)
pd->edit_uv.do_smooth_wire = ((U.gpu_flag & USER_GPU_FLAG_OVERLAY_SMOOTH_WIRE) != 0);
pd->edit_uv.do_stencil_overlay = show_overlays && do_stencil_overlay;
- pd->edit_uv.draw_type = sima->dt_uvstretch;
+ pd->edit_uv.draw_type = eSpaceImage_UVDT_Stretch(sima->dt_uvstretch);
BLI_listbase_clear(&pd->edit_uv.totals);
pd->edit_uv.total_area_ratio = 0.0f;
- pd->edit_uv.total_area_ratio_inv = 0.0f;
/* During engine initialization phase the `sima` isn't locked and
* we are able to retrieve the needed data.
@@ -280,8 +282,6 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_block(pd->edit_uv_stretching_grp, "globalsBlock", G_draw.block_ubo);
DRW_shgroup_uniform_float(
pd->edit_uv_stretching_grp, "totalAreaRatio", &pd->edit_uv.total_area_ratio, 1);
- DRW_shgroup_uniform_float(
- pd->edit_uv_stretching_grp, "totalAreaRatioInv", &pd->edit_uv.total_area_ratio_inv, 1);
}
}
@@ -302,7 +302,8 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->edit_uv_tiled_image_borders_ps);
DRW_shgroup_uniform_vec4_copy(grp, "color", theme_color);
- DRW_shgroup_uniform_vec3_copy(grp, "offset", (float[3]){0.0f, 0.0f, 0.0f});
+ const float3 offset = {0.0f, 0.0f, 0.0f};
+ DRW_shgroup_uniform_vec3_copy(grp, "offset", offset);
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
const int tile_x = ((tile->tile_number - 1001) % 10);
@@ -314,7 +315,8 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
/* Only mark active border when overlays are enabled. */
if (pd->edit_uv.do_tiled_image_overlay) {
/* Active tile border */
- ImageTile *active_tile = BLI_findlink(&image->tiles, image->active_tile_index);
+ ImageTile *active_tile = static_cast<ImageTile *>(
+ BLI_findlink(&image->tiles, image->active_tile_index));
if (active_tile) {
obmat[3][0] = (float)((active_tile->tile_number - 1001) % 10);
obmat[3][1] = (float)((active_tile->tile_number - 1001) / 10);
@@ -333,8 +335,9 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
char text[16];
LISTBASE_FOREACH (ImageTile *, tile, &image->tiles) {
BLI_snprintf(text, 5, "%d", tile->tile_number);
- float tile_location[3] = {
- ((tile->tile_number - 1001) % 10), ((tile->tile_number - 1001) / 10), 0.0f};
+ float tile_location[3] = {static_cast<float>((tile->tile_number - 1001) % 10),
+ static_cast<float>((tile->tile_number - 1001) / 10),
+ 0.0f};
DRW_text_cache_add(
dt, tile_location, text, strlen(text), 10, 10, DRW_TEXT_CACHE_GLOBALSPACE, color);
}
@@ -343,11 +346,12 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
if (pd->edit_uv.do_stencil_overlay) {
const Brush *brush = BKE_paint_brush(&ts->imapaint.paint);
Image *stencil_image = brush->clone.image;
- ImBuf *stencil_ibuf = BKE_image_acquire_ibuf(stencil_image, NULL, &pd->edit_uv.stencil_lock);
+ ImBuf *stencil_ibuf = BKE_image_acquire_ibuf(
+ stencil_image, nullptr, &pd->edit_uv.stencil_lock);
- if (stencil_ibuf == NULL) {
- pd->edit_uv.stencil_ibuf = NULL;
- pd->edit_uv.stencil_image = NULL;
+ if (stencil_ibuf == nullptr) {
+ pd->edit_uv.stencil_ibuf = nullptr;
+ pd->edit_uv.stencil_image = nullptr;
}
else {
DRW_PASS_CREATE(psl->edit_uv_stencil_ps,
@@ -358,16 +362,18 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->edit_uv_stencil_ps);
pd->edit_uv.stencil_ibuf = stencil_ibuf;
pd->edit_uv.stencil_image = stencil_image;
- GPUTexture *stencil_texture = BKE_image_get_gpu_texture(stencil_image, NULL, stencil_ibuf);
+ GPUTexture *stencil_texture = BKE_image_get_gpu_texture(
+ stencil_image, nullptr, stencil_ibuf);
DRW_shgroup_uniform_texture(grp, "imgTexture", stencil_texture);
DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", true);
DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true);
- DRW_shgroup_uniform_vec4_copy(
- grp, "color", (float[4]){1.0f, 1.0f, 1.0f, brush->clone.alpha});
+ const float4 color = {1.0f, 1.0f, 1.0f, brush->clone.alpha};
+ DRW_shgroup_uniform_vec4_copy(grp, "color", color);
float size_image[2];
- BKE_image_get_size_fl(image, NULL, size_image);
- float size_stencil_image[2] = {stencil_ibuf->x, stencil_ibuf->y};
+ BKE_image_get_size_fl(image, nullptr, size_image);
+ float size_stencil_image[2] = {static_cast<float>(stencil_ibuf->x),
+ static_cast<float>(stencil_ibuf->y)};
float obmat[4][4];
unit_m4(obmat);
@@ -380,8 +386,8 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
}
}
else {
- pd->edit_uv.stencil_ibuf = NULL;
- pd->edit_uv.stencil_image = NULL;
+ pd->edit_uv.stencil_ibuf = nullptr;
+ pd->edit_uv.stencil_image = nullptr;
}
if (pd->edit_uv.do_mask_overlay) {
@@ -400,8 +406,9 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
pd->edit_uv.image_aspect[1]);
pd->edit_uv.mask_texture = mask_texture;
DRW_shgroup_uniform_texture(grp, "imgTexture", mask_texture);
- DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){1.0f, 1.0f, 1.0f, 1.0f});
- DRW_shgroup_call_obmat(grp, geom, NULL);
+ const float4 color = {1.0f, 1.0f, 1.0f, 1.0f};
+ DRW_shgroup_uniform_vec4_copy(grp, "color", color);
+ DRW_shgroup_call_obmat(grp, geom, nullptr);
}
/* HACK: When editing objects that share the same mesh we should only draw the
@@ -411,7 +418,7 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata)
draw_ctx->obact->type == OB_MESH) {
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
- draw_ctx->view_layer, NULL, &objects_len, draw_ctx->object_mode);
+ draw_ctx->scene, draw_ctx->view_layer, nullptr, &objects_len, draw_ctx->object_mode);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *object_eval = DEG_get_evaluated_object(draw_ctx->depsgraph, objects[ob_index]);
DRW_mesh_batch_cache_validate(object_eval, (Mesh *)object_eval->data);
@@ -443,26 +450,26 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (has_active_edit_uvmap) {
if (pd->edit_uv.do_uv_overlay) {
- geom = DRW_mesh_batch_cache_get_edituv_edges(ob, ob->data);
+ geom = DRW_mesh_batch_cache_get_edituv_edges(ob, me);
if (geom) {
- DRW_shgroup_call_obmat(pd->edit_uv_edges_grp, geom, NULL);
+ DRW_shgroup_call_obmat(pd->edit_uv_edges_grp, geom, nullptr);
}
if (pd->edit_uv.do_verts) {
- geom = DRW_mesh_batch_cache_get_edituv_verts(ob, ob->data);
+ geom = DRW_mesh_batch_cache_get_edituv_verts(ob, me);
if (geom) {
- DRW_shgroup_call_obmat(pd->edit_uv_verts_grp, geom, NULL);
+ DRW_shgroup_call_obmat(pd->edit_uv_verts_grp, geom, nullptr);
}
}
if (pd->edit_uv.do_faces) {
- geom = DRW_mesh_batch_cache_get_edituv_faces(ob, ob->data);
+ geom = DRW_mesh_batch_cache_get_edituv_faces(ob, me);
if (geom) {
- DRW_shgroup_call_obmat(pd->edit_uv_faces_grp, geom, NULL);
+ DRW_shgroup_call_obmat(pd->edit_uv_faces_grp, geom, nullptr);
}
}
if (pd->edit_uv.do_face_dots) {
- geom = DRW_mesh_batch_cache_get_edituv_facedots(ob, ob->data);
+ geom = DRW_mesh_batch_cache_get_edituv_facedots(ob, me);
if (geom) {
- DRW_shgroup_call_obmat(pd->edit_uv_face_dots_grp, geom, NULL);
+ DRW_shgroup_call_obmat(pd->edit_uv_face_dots_grp, geom, nullptr);
}
}
}
@@ -472,23 +479,23 @@ static void overlay_edit_uv_cache_populate(OVERLAY_Data *vedata, Object *ob)
geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_angle(ob, me);
}
else /* SI_UVDT_STRETCH_AREA */ {
- OVERLAY_StretchingAreaTotals *totals = MEM_mallocN(sizeof(OVERLAY_StretchingAreaTotals),
- __func__);
+ OVERLAY_StretchingAreaTotals *totals = static_cast<OVERLAY_StretchingAreaTotals *>(
+ MEM_mallocN(sizeof(OVERLAY_StretchingAreaTotals), __func__));
BLI_addtail(&pd->edit_uv.totals, totals);
geom = DRW_mesh_batch_cache_get_edituv_faces_stretch_area(
ob, me, &totals->total_area, &totals->total_area_uv);
}
if (geom) {
- DRW_shgroup_call_obmat(pd->edit_uv_stretching_grp, geom, NULL);
+ DRW_shgroup_call_obmat(pd->edit_uv_stretching_grp, geom, nullptr);
}
}
}
if (draw_shadows && (has_active_object_uvmap || has_active_edit_uvmap)) {
if (pd->edit_uv.do_uv_shadow_overlay) {
- geom = DRW_mesh_batch_cache_get_uv_edges(ob, ob->data);
+ geom = DRW_mesh_batch_cache_get_uv_edges(ob, me);
if (geom) {
- DRW_shgroup_call_obmat(pd->edit_uv_shadow_edges_grp, geom, NULL);
+ DRW_shgroup_call_obmat(pd->edit_uv_shadow_edges_grp, geom, nullptr);
}
}
}
@@ -510,7 +517,6 @@ static void edit_uv_stretching_update_ratios(OVERLAY_Data *vedata)
if (total_area > FLT_EPSILON && total_area_uv > FLT_EPSILON) {
pd->edit_uv.total_area_ratio = total_area / total_area_uv;
- pd->edit_uv.total_area_ratio_inv = total_area_uv / total_area;
}
}
BLI_freelistN(&pd->edit_uv.totals);
@@ -534,8 +540,8 @@ static void OVERLAY_edit_uv_draw_finish(OVERLAY_Data *vedata)
if (pd->edit_uv.stencil_ibuf) {
BKE_image_release_ibuf(
pd->edit_uv.stencil_image, pd->edit_uv.stencil_ibuf, pd->edit_uv.stencil_lock);
- pd->edit_uv.stencil_image = NULL;
- pd->edit_uv.stencil_ibuf = NULL;
+ pd->edit_uv.stencil_image = nullptr;
+ pd->edit_uv.stencil_ibuf = nullptr;
}
DRW_TEXTURE_FREE_SAFE(pd->edit_uv.mask_texture);
@@ -554,7 +560,7 @@ void OVERLAY_edit_uv_draw(OVERLAY_Data *vedata)
/* Combined overlay renders in the default framebuffer and modifies the image in SRS.
* The alpha overlay renders in the overlay framebuffer. */
const bool is_combined_overlay = pd->edit_uv.mask_overlay_mode == MASK_OVERLAY_COMBINED;
- GPUFrameBuffer *previous_framebuffer = NULL;
+ GPUFrameBuffer *previous_framebuffer = nullptr;
if (is_combined_overlay) {
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
previous_framebuffer = GPU_framebuffer_active_get();
diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.cc
index 5edd68bffff..bb9f2e3a8ce 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.c
+++ b/source/blender/draw/engines/overlay/overlay_engine.cc
@@ -22,7 +22,7 @@
#include "DNA_space_types.h"
#include "overlay_engine.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
/* -------------------------------------------------------------------- */
/** \name Engine Callbacks
@@ -30,7 +30,7 @@
static void OVERLAY_engine_init(void *vedata)
{
- OVERLAY_Data *data = vedata;
+ OVERLAY_Data *data = static_cast<OVERLAY_Data *>(vedata);
OVERLAY_StorageList *stl = data->stl;
const DRWContextState *draw_ctx = DRW_context_state_get();
const RegionView3D *rv3d = draw_ctx->rv3d;
@@ -40,28 +40,29 @@ static void OVERLAY_engine_init(void *vedata)
if (!stl->pd) {
/* Allocate transient pointers. */
- stl->pd = MEM_callocN(sizeof(*stl->pd), __func__);
+ stl->pd = static_cast<OVERLAY_PrivateData *>(MEM_callocN(sizeof(*stl->pd), __func__));
}
/* Allocate instance. */
- if (data->instance == NULL) {
- data->instance = MEM_callocN(sizeof(*data->instance), __func__);
+ if (data->instance == nullptr) {
+ data->instance = static_cast<OVERLAY_Instance *>(
+ MEM_callocN(sizeof(*data->instance), __func__));
}
OVERLAY_PrivateData *pd = stl->pd;
- pd->space_type = v3d != NULL ? SPACE_VIEW3D : draw_ctx->space_data->spacetype;
+ pd->space_type = v3d != nullptr ? (int)SPACE_VIEW3D : draw_ctx->space_data->spacetype;
if (pd->space_type == SPACE_IMAGE) {
const SpaceImage *sima = (SpaceImage *)draw_ctx->space_data;
pd->hide_overlays = (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS) == 0;
- pd->clipping_state = 0;
+ pd->clipping_state = DRWState(0);
OVERLAY_grid_init(data);
OVERLAY_edit_uv_init(data);
return;
}
if (pd->space_type == SPACE_NODE) {
pd->hide_overlays = true;
- pd->clipping_state = 0;
+ pd->clipping_state = DRWState(0);
return;
}
@@ -101,98 +102,99 @@ static void OVERLAY_engine_init(void *vedata)
pd->use_in_front = (v3d->shading.type <= OB_SOLID) ||
BKE_scene_uses_blender_workbench(draw_ctx->scene);
pd->wireframe_mode = (v3d->shading.type == OB_WIRE);
- pd->clipping_state = RV3D_CLIPPING_ENABLED(v3d, rv3d) ? DRW_STATE_CLIP_PLANES : 0;
+ pd->clipping_state = RV3D_CLIPPING_ENABLED(v3d, rv3d) ? DRW_STATE_CLIP_PLANES : DRWState(0);
pd->xray_opacity = XRAY_ALPHA(v3d);
pd->xray_enabled = XRAY_ACTIVE(v3d);
pd->xray_enabled_and_not_wire = pd->xray_enabled && v3d->shading.type > OB_WIRE;
pd->clear_in_front = (v3d->shading.type != OB_SOLID);
pd->cfra = DEG_get_ctime(draw_ctx->depsgraph);
- OVERLAY_antialiasing_init(vedata);
+ OVERLAY_antialiasing_init(data);
switch (stl->pd->ctx_mode) {
case CTX_MODE_EDIT_MESH:
- OVERLAY_edit_mesh_init(vedata);
+ OVERLAY_edit_mesh_init(data);
break;
case CTX_MODE_EDIT_CURVES:
- OVERLAY_edit_curves_init(vedata);
+ OVERLAY_edit_curves_init(data);
break;
default:
/* Nothing to do. */
break;
}
- OVERLAY_facing_init(vedata);
- OVERLAY_grid_init(vedata);
- OVERLAY_image_init(vedata);
- OVERLAY_outline_init(vedata);
- OVERLAY_wireframe_init(vedata);
- OVERLAY_paint_init(vedata);
+ OVERLAY_facing_init(data);
+ OVERLAY_grid_init(data);
+ OVERLAY_image_init(data);
+ OVERLAY_outline_init(data);
+ OVERLAY_wireframe_init(data);
+ OVERLAY_paint_init(data);
}
static void OVERLAY_cache_init(void *vedata)
{
- OVERLAY_Data *data = vedata;
+ OVERLAY_Data *data = static_cast<OVERLAY_Data *>(vedata);
OVERLAY_StorageList *stl = data->stl;
OVERLAY_PrivateData *pd = stl->pd;
if (pd->space_type == SPACE_IMAGE) {
- OVERLAY_background_cache_init(vedata);
- OVERLAY_grid_cache_init(vedata);
- OVERLAY_edit_uv_cache_init(vedata);
+ OVERLAY_background_cache_init(data);
+ OVERLAY_grid_cache_init(data);
+ OVERLAY_edit_uv_cache_init(data);
return;
}
if (pd->space_type == SPACE_NODE) {
- OVERLAY_background_cache_init(vedata);
+ OVERLAY_background_cache_init(data);
return;
}
switch (pd->ctx_mode) {
- case CTX_MODE_EDIT_MESH:
- OVERLAY_edit_mesh_cache_init(vedata);
+ case CTX_MODE_EDIT_MESH: {
+ OVERLAY_edit_mesh_cache_init(data);
/* `pd->edit_mesh.flag` is valid after calling `OVERLAY_edit_mesh_cache_init`. */
const bool draw_edit_weights = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_WEIGHT);
if (draw_edit_weights) {
- OVERLAY_paint_cache_init(vedata);
+ OVERLAY_paint_cache_init(data);
}
break;
+ }
case CTX_MODE_EDIT_SURFACE:
case CTX_MODE_EDIT_CURVE:
- OVERLAY_edit_curve_cache_init(vedata);
+ OVERLAY_edit_curve_cache_init(data);
break;
case CTX_MODE_EDIT_TEXT:
- OVERLAY_edit_text_cache_init(vedata);
+ OVERLAY_edit_text_cache_init(data);
break;
case CTX_MODE_EDIT_ARMATURE:
break;
case CTX_MODE_EDIT_METABALL:
break;
case CTX_MODE_EDIT_LATTICE:
- OVERLAY_edit_lattice_cache_init(vedata);
+ OVERLAY_edit_lattice_cache_init(data);
break;
case CTX_MODE_PARTICLE:
- OVERLAY_edit_particle_cache_init(vedata);
+ OVERLAY_edit_particle_cache_init(data);
break;
case CTX_MODE_POSE:
case CTX_MODE_PAINT_WEIGHT:
case CTX_MODE_PAINT_VERTEX:
case CTX_MODE_PAINT_TEXTURE:
- OVERLAY_paint_cache_init(vedata);
+ OVERLAY_paint_cache_init(data);
break;
case CTX_MODE_SCULPT:
- OVERLAY_sculpt_cache_init(vedata);
+ OVERLAY_sculpt_cache_init(data);
break;
case CTX_MODE_EDIT_GPENCIL:
case CTX_MODE_PAINT_GPENCIL:
case CTX_MODE_SCULPT_GPENCIL:
case CTX_MODE_VERTEX_GPENCIL:
case CTX_MODE_WEIGHT_GPENCIL:
- OVERLAY_edit_gpencil_cache_init(vedata);
+ OVERLAY_edit_gpencil_cache_init(data);
break;
case CTX_MODE_EDIT_CURVES:
- OVERLAY_edit_curves_cache_init(vedata);
+ OVERLAY_edit_curves_cache_init(data);
break;
case CTX_MODE_SCULPT_CURVES:
- OVERLAY_sculpt_curves_cache_init(vedata);
+ OVERLAY_sculpt_curves_cache_init(data);
break;
case CTX_MODE_OBJECT:
break;
@@ -200,22 +202,22 @@ static void OVERLAY_cache_init(void *vedata)
BLI_assert_msg(0, "Draw mode invalid");
break;
}
- OVERLAY_antialiasing_cache_init(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);
- OVERLAY_grid_cache_init(vedata);
- OVERLAY_image_cache_init(vedata);
- OVERLAY_metaball_cache_init(vedata);
- OVERLAY_motion_path_cache_init(vedata);
- OVERLAY_outline_cache_init(vedata);
- OVERLAY_particle_cache_init(vedata);
- OVERLAY_wireframe_cache_init(vedata);
- OVERLAY_volume_cache_init(vedata);
+ OVERLAY_antialiasing_cache_init(data);
+ OVERLAY_armature_cache_init(data);
+ OVERLAY_background_cache_init(data);
+ OVERLAY_fade_cache_init(data);
+ OVERLAY_mode_transfer_cache_init(data);
+ OVERLAY_extra_cache_init(data);
+ OVERLAY_facing_cache_init(data);
+ OVERLAY_gpencil_cache_init(data);
+ OVERLAY_grid_cache_init(data);
+ OVERLAY_image_cache_init(data);
+ OVERLAY_metaball_cache_init(data);
+ OVERLAY_motion_path_cache_init(data);
+ OVERLAY_outline_cache_init(data);
+ OVERLAY_particle_cache_init(data);
+ OVERLAY_wireframe_cache_init(data);
+ OVERLAY_volume_cache_init(data);
}
BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bool *do_init)
@@ -223,12 +225,13 @@ BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bo
OVERLAY_DupliData **dupli_data = (OVERLAY_DupliData **)DRW_duplidata_get(vedata);
*do_init = false;
if (!ELEM(ob->type, OB_MESH, OB_SURF, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT)) {
- return NULL;
+ return nullptr;
}
if (dupli_data) {
- if (*dupli_data == NULL) {
- *dupli_data = MEM_callocN(sizeof(OVERLAY_DupliData), __func__);
+ if (*dupli_data == nullptr) {
+ *dupli_data = static_cast<OVERLAY_DupliData *>(
+ MEM_callocN(sizeof(OVERLAY_DupliData), __func__));
*do_init = true;
}
else if ((*dupli_data)->base_flag != ob->base_flag) {
@@ -237,7 +240,7 @@ BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bo
}
return *dupli_data;
}
- return NULL;
+ return nullptr;
}
static bool overlay_object_is_edit_mode(const OVERLAY_PrivateData *pd, const Object *ob)
@@ -289,7 +292,7 @@ static bool overlay_should_fade_object(Object *ob, Object *active_object)
static void OVERLAY_cache_populate(void *vedata, Object *ob)
{
- OVERLAY_Data *data = vedata;
+ OVERLAY_Data *data = static_cast<OVERLAY_Data *>(vedata);
OVERLAY_PrivateData *pd = data->stl->pd;
if (pd->space_type == SPACE_IMAGE) {
@@ -312,7 +315,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
(draw_ctx->object_mode & OB_MODE_ALL_PAINT);
const bool in_sculpt_curve_mode = (ob == draw_ctx->obact) &&
(draw_ctx->object_mode & OB_MODE_SCULPT_CURVES);
- const bool in_sculpt_mode = (ob == draw_ctx->obact) && (ob->sculpt != NULL) &&
+ const bool in_sculpt_mode = (ob == draw_ctx->obact) && (ob->sculpt != nullptr) &&
(ob->sculpt->mode_type == OB_MODE_SCULPT);
const bool in_curves_sculpt_mode = (ob == draw_ctx->obact) &&
(ob->mode == OB_MODE_SCULPT_CURVES);
@@ -320,7 +323,6 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
OB_MESH,
OB_CURVES_LEGACY,
OB_SURF,
- OB_MBALL,
OB_FONT,
OB_GPENCIL,
OB_CURVES,
@@ -354,108 +356,108 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
OVERLAY_DupliData *dupli = OVERLAY_duplidata_get(ob, vedata, &do_init);
if (draw_fade) {
- OVERLAY_fade_cache_populate(vedata, ob);
+ OVERLAY_fade_cache_populate(data, ob);
}
if (draw_facing) {
- OVERLAY_facing_cache_populate(vedata, ob);
+ OVERLAY_facing_cache_populate(data, ob);
}
if (draw_mode_transfer) {
- OVERLAY_mode_transfer_cache_populate(vedata, ob);
+ OVERLAY_mode_transfer_cache_populate(data, ob);
}
if (draw_wires) {
- OVERLAY_wireframe_cache_populate(vedata, ob, dupli, do_init);
+ OVERLAY_wireframe_cache_populate(data, ob, dupli, do_init);
}
if (draw_outlines) {
- OVERLAY_outline_cache_populate(vedata, ob, dupli, do_init);
+ OVERLAY_outline_cache_populate(data, ob, dupli, do_init);
}
if (draw_bone_selection) {
- OVERLAY_pose_cache_populate(vedata, ob);
+ OVERLAY_pose_cache_populate(data, ob);
}
if (ob->type == OB_VOLUME) {
- OVERLAY_volume_cache_populate(vedata, ob);
+ OVERLAY_volume_cache_populate(data, ob);
}
if (in_edit_mode && !pd->hide_overlays) {
switch (ob->type) {
case OB_MESH:
- OVERLAY_edit_mesh_cache_populate(vedata, ob);
+ OVERLAY_edit_mesh_cache_populate(data, ob);
if (draw_edit_weights) {
- OVERLAY_paint_weight_cache_populate(vedata, ob);
+ OVERLAY_paint_weight_cache_populate(data, ob);
}
break;
case OB_ARMATURE:
if (draw_bones) {
- OVERLAY_edit_armature_cache_populate(vedata, ob);
+ OVERLAY_edit_armature_cache_populate(data, ob);
}
break;
case OB_CURVES_LEGACY:
- OVERLAY_edit_curve_cache_populate(vedata, ob);
+ OVERLAY_edit_curve_cache_populate(data, ob);
break;
case OB_SURF:
- OVERLAY_edit_surf_cache_populate(vedata, ob);
+ OVERLAY_edit_surf_cache_populate(data, ob);
break;
case OB_LATTICE:
- OVERLAY_edit_lattice_cache_populate(vedata, ob);
+ OVERLAY_edit_lattice_cache_populate(data, ob);
break;
case OB_MBALL:
- OVERLAY_edit_metaball_cache_populate(vedata, ob);
+ OVERLAY_edit_metaball_cache_populate(data, ob);
break;
case OB_FONT:
- OVERLAY_edit_text_cache_populate(vedata, ob);
+ OVERLAY_edit_text_cache_populate(data, ob);
break;
case OB_CURVES:
- OVERLAY_edit_curves_cache_populate(vedata, ob);
+ OVERLAY_edit_curves_cache_populate(data, ob);
break;
}
}
else if (in_pose_mode && draw_bones) {
- OVERLAY_pose_armature_cache_populate(vedata, ob);
+ OVERLAY_pose_armature_cache_populate(data, ob);
}
else if (in_paint_mode && !pd->hide_overlays) {
switch (draw_ctx->object_mode) {
case OB_MODE_VERTEX_PAINT:
- OVERLAY_paint_vertex_cache_populate(vedata, ob);
+ OVERLAY_paint_vertex_cache_populate(data, ob);
break;
case OB_MODE_WEIGHT_PAINT:
- OVERLAY_paint_weight_cache_populate(vedata, ob);
+ OVERLAY_paint_weight_cache_populate(data, ob);
break;
case OB_MODE_TEXTURE_PAINT:
- OVERLAY_paint_texture_cache_populate(vedata, ob);
+ OVERLAY_paint_texture_cache_populate(data, ob);
break;
default:
break;
}
}
else if (in_particle_edit_mode) {
- OVERLAY_edit_particle_cache_populate(vedata, ob);
+ OVERLAY_edit_particle_cache_populate(data, ob);
}
if (in_sculpt_mode) {
- OVERLAY_sculpt_cache_populate(vedata, ob);
+ OVERLAY_sculpt_cache_populate(data, ob);
}
else if (in_curves_sculpt_mode) {
- OVERLAY_sculpt_curves_cache_populate(vedata, ob);
+ OVERLAY_sculpt_curves_cache_populate(data, ob);
}
if (draw_motion_paths) {
- OVERLAY_motion_path_cache_populate(vedata, ob);
+ OVERLAY_motion_path_cache_populate(data, ob);
}
if (!pd->hide_overlays) {
switch (ob->type) {
case OB_ARMATURE:
if (draw_bones && (is_select || (!in_edit_mode && !in_pose_mode))) {
- OVERLAY_armature_cache_populate(vedata, ob);
+ OVERLAY_armature_cache_populate(data, ob);
}
break;
case OB_MBALL:
if (!in_edit_mode) {
- OVERLAY_metaball_cache_populate(vedata, ob);
+ OVERLAY_metaball_cache_populate(data, ob);
}
break;
case OB_GPENCIL:
- OVERLAY_gpencil_cache_populate(vedata, ob);
+ OVERLAY_gpencil_cache_populate(data, ob);
break;
}
}
@@ -463,25 +465,25 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
if (draw_extras) {
switch (ob->type) {
case OB_EMPTY:
- OVERLAY_empty_cache_populate(vedata, ob);
+ OVERLAY_empty_cache_populate(data, ob);
break;
case OB_LAMP:
- OVERLAY_light_cache_populate(vedata, ob);
+ OVERLAY_light_cache_populate(data, ob);
break;
case OB_CAMERA:
- OVERLAY_camera_cache_populate(vedata, ob);
+ OVERLAY_camera_cache_populate(data, ob);
break;
case OB_SPEAKER:
- OVERLAY_speaker_cache_populate(vedata, ob);
+ OVERLAY_speaker_cache_populate(data, ob);
break;
case OB_LIGHTPROBE:
- OVERLAY_lightprobe_cache_populate(vedata, ob);
+ OVERLAY_lightprobe_cache_populate(data, ob);
break;
case OB_LATTICE: {
/* Unlike the other types above, lattices actually have a bounding box defined, so hide the
* lattice wires if only the bounding-box is requested. */
if (ob->dt > OB_BOUNDBOX) {
- OVERLAY_lattice_cache_populate(vedata, ob);
+ OVERLAY_lattice_cache_populate(data, ob);
}
break;
}
@@ -489,12 +491,12 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
}
if (!BLI_listbase_is_empty(&ob->particlesystem)) {
- OVERLAY_particle_cache_populate(vedata, ob);
+ OVERLAY_particle_cache_populate(data, ob);
}
/* Relationship, object center, bounding-box... etc. */
if (!pd->hide_overlays) {
- OVERLAY_extra_cache_populate(vedata, ob);
+ OVERLAY_extra_cache_populate(data, ob);
}
if (dupli) {
@@ -504,11 +506,11 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob)
static void OVERLAY_cache_finish(void *vedata)
{
- OVERLAY_Data *data = vedata;
+ OVERLAY_Data *data = static_cast<OVERLAY_Data *>(vedata);
OVERLAY_PrivateData *pd = data->stl->pd;
if (ELEM(pd->space_type, SPACE_IMAGE)) {
- OVERLAY_edit_uv_cache_finish(vedata);
+ OVERLAY_edit_uv_cache_finish(data);
return;
}
if (ELEM(pd->space_type, SPACE_NODE)) {
@@ -521,29 +523,30 @@ static void OVERLAY_cache_finish(void *vedata)
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0);
+ DRW_texture_ensure_fullscreen_2d(
+ &dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, DRWTextureFlag(0));
GPU_framebuffer_ensure_config(
&dfbl->in_front_fb,
{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);
+ OVERLAY_mode_transfer_cache_finish(data);
+ OVERLAY_antialiasing_cache_finish(data);
+ OVERLAY_armature_cache_finish(data);
+ OVERLAY_image_cache_finish(data);
}
static void OVERLAY_draw_scene(void *vedata)
{
- OVERLAY_Data *data = vedata;
+ OVERLAY_Data *data = static_cast<OVERLAY_Data *>(vedata);
OVERLAY_PrivateData *pd = data->stl->pd;
OVERLAY_FramebufferList *fbl = data->fbl;
DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
/* Needs to be done first as it modifies the scene color and depth buffer. */
if (pd->space_type == SPACE_VIEW3D) {
- OVERLAY_image_scene_background_draw(vedata);
+ OVERLAY_image_scene_background_draw(data);
}
if (DRW_state_is_fbo()) {
@@ -570,45 +573,45 @@ static void OVERLAY_draw_scene(void *vedata)
return;
}
- OVERLAY_image_background_draw(vedata);
- OVERLAY_background_draw(vedata);
+ OVERLAY_image_background_draw(data);
+ OVERLAY_background_draw(data);
- OVERLAY_antialiasing_start(vedata);
+ OVERLAY_antialiasing_start(data);
- DRW_view_set_active(NULL);
+ DRW_view_set_active(nullptr);
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_color_only_fb);
}
- OVERLAY_outline_draw(vedata);
- OVERLAY_xray_depth_copy(vedata);
+ OVERLAY_outline_draw(data);
+ OVERLAY_xray_depth_copy(data);
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_default_fb);
}
- 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);
+ OVERLAY_image_draw(data);
+ OVERLAY_fade_draw(data);
+ OVERLAY_facing_draw(data);
+ OVERLAY_mode_transfer_draw(data);
+ OVERLAY_extra_blend_draw(data);
+ OVERLAY_volume_draw(data);
/* These overlays are drawn here to avoid artifacts with wire-frame opacity. */
switch (pd->ctx_mode) {
case CTX_MODE_SCULPT:
- OVERLAY_sculpt_draw(vedata);
+ OVERLAY_sculpt_draw(data);
break;
case CTX_MODE_SCULPT_CURVES:
- OVERLAY_sculpt_curves_draw(vedata);
+ OVERLAY_sculpt_curves_draw(data);
break;
case CTX_MODE_EDIT_MESH:
case CTX_MODE_POSE:
case CTX_MODE_PAINT_WEIGHT:
case CTX_MODE_PAINT_VERTEX:
case CTX_MODE_PAINT_TEXTURE:
- OVERLAY_paint_draw(vedata);
+ OVERLAY_paint_draw(data);
break;
default:
break;
@@ -618,46 +621,46 @@ static void OVERLAY_draw_scene(void *vedata)
GPU_framebuffer_bind(fbl->overlay_line_fb);
}
- OVERLAY_wireframe_draw(vedata);
- OVERLAY_armature_draw(vedata);
- OVERLAY_particle_draw(vedata);
- OVERLAY_metaball_draw(vedata);
- OVERLAY_gpencil_draw(vedata);
- OVERLAY_extra_draw(vedata);
+ OVERLAY_wireframe_draw(data);
+ OVERLAY_armature_draw(data);
+ OVERLAY_particle_draw(data);
+ OVERLAY_metaball_draw(data);
+ OVERLAY_gpencil_draw(data);
+ OVERLAY_extra_draw(data);
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_color_only_fb);
}
- OVERLAY_xray_fade_draw(vedata);
- OVERLAY_grid_draw(vedata);
+ OVERLAY_xray_fade_draw(data);
+ OVERLAY_grid_draw(data);
- OVERLAY_xray_depth_infront_copy(vedata);
+ OVERLAY_xray_depth_infront_copy(data);
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_in_front_fb);
}
- OVERLAY_fade_infront_draw(vedata);
- OVERLAY_facing_infront_draw(vedata);
- OVERLAY_mode_transfer_infront_draw(vedata);
+ OVERLAY_fade_infront_draw(data);
+ OVERLAY_facing_infront_draw(data);
+ OVERLAY_mode_transfer_infront_draw(data);
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_line_in_front_fb);
}
- OVERLAY_wireframe_in_front_draw(vedata);
- OVERLAY_armature_in_front_draw(vedata);
- OVERLAY_extra_in_front_draw(vedata);
- OVERLAY_metaball_in_front_draw(vedata);
+ OVERLAY_wireframe_in_front_draw(data);
+ OVERLAY_armature_in_front_draw(data);
+ OVERLAY_extra_in_front_draw(data);
+ OVERLAY_metaball_in_front_draw(data);
if (DRW_state_is_fbo()) {
GPU_framebuffer_bind(fbl->overlay_color_only_fb);
}
- OVERLAY_image_in_front_draw(vedata);
- OVERLAY_motion_path_draw(vedata);
- OVERLAY_extra_centers_draw(vedata);
+ OVERLAY_image_in_front_draw(data);
+ OVERLAY_motion_path_draw(data);
+ OVERLAY_extra_centers_draw(data);
if (DRW_state_is_select() || DRW_state_is_depth()) {
/* Edit modes have their own selection code. */
@@ -668,41 +671,41 @@ static void OVERLAY_draw_scene(void *vedata)
switch (pd->ctx_mode) {
case CTX_MODE_EDIT_MESH:
- OVERLAY_edit_mesh_draw(vedata);
+ OVERLAY_edit_mesh_draw(data);
break;
case CTX_MODE_EDIT_SURFACE:
case CTX_MODE_EDIT_CURVE:
- OVERLAY_edit_curve_draw(vedata);
+ OVERLAY_edit_curve_draw(data);
break;
case CTX_MODE_EDIT_TEXT:
- OVERLAY_edit_text_draw(vedata);
+ OVERLAY_edit_text_draw(data);
break;
case CTX_MODE_EDIT_LATTICE:
- OVERLAY_edit_lattice_draw(vedata);
+ OVERLAY_edit_lattice_draw(data);
break;
case CTX_MODE_POSE:
- OVERLAY_pose_draw(vedata);
+ OVERLAY_pose_draw(data);
break;
case CTX_MODE_PARTICLE:
- OVERLAY_edit_particle_draw(vedata);
+ OVERLAY_edit_particle_draw(data);
break;
case CTX_MODE_EDIT_GPENCIL:
case CTX_MODE_PAINT_GPENCIL:
case CTX_MODE_SCULPT_GPENCIL:
case CTX_MODE_VERTEX_GPENCIL:
case CTX_MODE_WEIGHT_GPENCIL:
- OVERLAY_edit_gpencil_draw(vedata);
+ OVERLAY_edit_gpencil_draw(data);
break;
case CTX_MODE_SCULPT_CURVES:
break;
case CTX_MODE_EDIT_CURVES:
- OVERLAY_edit_curves_draw(vedata);
+ OVERLAY_edit_curves_draw(data);
break;
default:
break;
}
- OVERLAY_antialiasing_end(vedata);
+ OVERLAY_antialiasing_end(data);
}
static void OVERLAY_engine_free(void)
@@ -726,8 +729,8 @@ static void OVERLAY_instance_free(void *instance_)
static const DrawEngineDataSize overlay_data_size = DRW_VIEWPORT_DATA_SIZE(OVERLAY_Data);
DrawEngineType draw_engine_overlay_type = {
- NULL,
- NULL,
+ nullptr,
+ nullptr,
N_("Overlay"),
&overlay_data_size,
&OVERLAY_engine_init,
@@ -737,10 +740,10 @@ DrawEngineType draw_engine_overlay_type = {
&OVERLAY_cache_populate,
&OVERLAY_cache_finish,
&OVERLAY_draw_scene,
- NULL,
- NULL,
- NULL,
- NULL,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
};
/** \} */
diff --git a/source/blender/draw/engines/overlay/overlay_engine.h b/source/blender/draw/engines/overlay/overlay_engine.h
index 18ab7f1456e..a33ea17dc1e 100644
--- a/source/blender/draw/engines/overlay/overlay_engine.h
+++ b/source/blender/draw/engines/overlay/overlay_engine.h
@@ -7,4 +7,12 @@
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
extern DrawEngineType draw_engine_overlay_type;
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.cc
index 9d478310104..3e9855720b3 100644
--- a/source/blender/draw/engines/overlay/overlay_extra.c
+++ b/source/blender/draw/engines/overlay/overlay_extra.cc
@@ -38,7 +38,7 @@
#include "ED_view3d.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
#include "draw_common.h"
#include "draw_manager_text.h"
@@ -79,7 +79,8 @@ void OVERLAY_extra_cache_init(OVERLAY_Data *vedata)
OVERLAY_ExtraCallBuffers *cb = &pd->extra_call_buffers[i];
DRWPass **p_extra_ps = &psl->extra_ps[i];
- DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : 0;
+ DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT :
+ DRWState(0);
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
DRW_PASS_CREATE(*p_extra_ps, state | pd->clipping_state | infront_state);
@@ -352,7 +353,7 @@ static void OVERLAY_bounds(OVERLAY_ExtraCallBuffers *cb,
const BoundBox *bb = BKE_object_boundbox_get(ob);
BoundBox bb_local;
- if (bb == NULL) {
+ if (bb == nullptr) {
const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f};
BKE_boundbox_init_from_minmax(&bb_local, min, max);
bb = &bb_local;
@@ -443,17 +444,17 @@ static void OVERLAY_collision(OVERLAY_ExtraCallBuffers *cb, Object *ob, const fl
static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, const float *color)
{
- if (ob->data == NULL) {
+ if (ob->data == nullptr) {
return;
}
- ID *ob_data = ob->data;
- float *texcoloc = NULL;
- float *texcosize = NULL;
+ ID *ob_data = static_cast<ID *>(ob->data);
+ float *texcoloc = nullptr;
+ float *texcosize = nullptr;
switch (GS(ob_data->name)) {
case ID_ME:
- BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize);
+ BKE_mesh_texspace_get_reference((Mesh *)ob_data, nullptr, &texcoloc, &texcosize);
break;
case ID_CU_LEGACY: {
Curve *cu = (Curve *)ob_data;
@@ -480,7 +481,7 @@ static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, cons
float mat[4][4];
- if (texcoloc != NULL && texcosize != NULL) {
+ if (texcoloc != nullptr && texcosize != nullptr) {
size_to_mat4(mat, texcosize);
copy_v3_v3(mat[3], texcoloc);
}
@@ -495,10 +496,10 @@ static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, cons
static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLayer *view_layer)
{
- int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL);
+ int theme_id = DRW_object_wire_theme_get(ob, view_layer, nullptr);
float *color = DRW_color_background_blend_get(theme_id);
PartDeflect *pd = ob->pd;
- Curve *cu = (ob->type == OB_CURVES_LEGACY) ? ob->data : NULL;
+ Curve *cu = (ob->type == OB_CURVES_LEGACY) ? static_cast<Curve *>(ob->data) : nullptr;
union {
float mat[4][4];
@@ -529,12 +530,12 @@ static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLay
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];
- BKE_where_on_path(ob, 0.0f, pos, NULL, NULL, NULL, NULL);
+ BKE_where_on_path(ob, 0.0f, pos, nullptr, nullptr, nullptr, nullptr);
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);
- BKE_where_on_path(ob, 1.0f, pos, NULL, NULL, NULL, NULL);
+ BKE_where_on_path(ob, 1.0f, pos, nullptr, nullptr, nullptr, nullptr);
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);
@@ -600,7 +601,7 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
const DRWContextState *draw_ctx = DRW_context_state_get();
ViewLayer *view_layer = draw_ctx->view_layer;
- Light *la = ob->data;
+ Light *la = static_cast<Light *>(ob->data);
float *color_p;
DRW_object_wire_theme_get(ob, view_layer, &color_p);
/* Remove the alpha. */
@@ -644,7 +645,8 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob)
else if (la->type == LA_SPOT) {
/* Previous implementation was using the clipend distance as cone size.
* We cannot do this anymore so we use a fixed size of 10. (see T72871) */
- rescale_m4(instdata.mat, (float[3]){10.0f, 10.0f, 10.0f});
+ const float3 scale_vec = {10.0f, 10.0f, 10.0f};
+ rescale_m4(instdata.mat, scale_vec);
/* For cycles and eevee the spot attenuation is
* y = (1/(1 + x^2) - a)/((1 - a) b)
* We solve the case where spot attenuation y = 1 and y = 0
@@ -756,7 +758,7 @@ void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob)
uint cell_count = prb->grid_resolution_x * prb->grid_resolution_y * prb->grid_resolution_z;
DRWShadingGroup *grp = DRW_shgroup_create_sub(vedata->stl->pd->extra_grid_grp);
DRW_shgroup_uniform_mat4_copy(grp, "gridModelMatrix", instdata.mat);
- DRW_shgroup_call_procedural_points(grp, NULL, cell_count);
+ DRW_shgroup_call_procedural_points(grp, nullptr, cell_count);
}
break;
case LIGHTPROBE_TYPE_PLANAR:
@@ -849,7 +851,7 @@ static void camera_view3d_reconstruction(
const bool is_select = DRW_state_is_select();
MovieClip *clip = BKE_object_movieclip_get(scene, ob, false);
- if (clip == NULL) {
+ if (clip == nullptr) {
return;
}
@@ -985,7 +987,7 @@ static float camera_offaxis_shiftx_get(Scene *scene,
const OVERLAY_CameraInstanceData *instdata,
bool right_eye)
{
- Camera *cam = ob->data;
+ Camera *cam = static_cast<Camera *>(ob->data);
if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) {
const char *viewnames[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
const float shiftx = BKE_camera_multiview_shift_x(&scene->r, ob, viewnames[right_eye]);
@@ -1007,7 +1009,7 @@ static void camera_stereoscopy_extra(OVERLAY_ExtraCallBuffers *cb,
const OVERLAY_CameraInstanceData *instdata)
{
OVERLAY_CameraInstanceData stereodata = *instdata;
- Camera *cam = ob->data;
+ Camera *cam = static_cast<Camera *>(ob->data);
const bool is_select = DRW_state_is_select();
const char *viewnames[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
@@ -1111,7 +1113,7 @@ void OVERLAY_camera_cache_populate(OVERLAY_Data *vedata, Object *ob)
Scene *scene = draw_ctx->scene;
RegionView3D *rv3d = draw_ctx->rv3d;
- Camera *cam = ob->data;
+ Camera *cam = static_cast<Camera *>(ob->data);
Object *camera_object = DEG_get_evaluated_object(draw_ctx->depsgraph, v3d->camera);
const bool is_select = DRW_state_is_select();
const bool is_active = (ob == camera_object);
@@ -1257,7 +1259,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
}
/* Drawing the hook lines. */
- for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
+ for (ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first); md; md = md->next) {
if (md->type == eModifierType_Hook) {
HookModifierData *hmd = (HookModifierData *)md;
float center[3];
@@ -1286,14 +1288,14 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
bConstraintOb *cob;
ListBase *list = &ob->constraints;
- cob = BKE_constraints_make_evalob(depsgraph, scene, ob, NULL, CONSTRAINT_OBTYPE_OBJECT);
+ cob = BKE_constraints_make_evalob(depsgraph, scene, ob, nullptr, CONSTRAINT_OBTYPE_OBJECT);
- for (curcon = list->first; curcon; curcon = curcon->next) {
+ for (curcon = static_cast<bConstraint *>(list->first); curcon; curcon = curcon->next) {
if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_OBJECTSOLVER)) {
/* special case for object solver and follow track constraints because they don't fill
* constraint targets properly (design limitation -- scene is needed for their target
* but it can't be accessed from get_targets callback) */
- Object *camob = NULL;
+ Object *camob = nullptr;
if (curcon->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
bFollowTrackConstraint *data = (bFollowTrackConstraint *)curcon->data;
@@ -1310,14 +1312,19 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
}
else {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
- ListBase targets = {NULL, NULL};
+ ListBase targets = {nullptr, nullptr};
if ((curcon->ui_expand_flag & (1 << 0)) && BKE_constraint_targets_get(curcon, &targets)) {
bConstraintTarget *ct;
- for (ct = targets.first; ct; ct = ct->next) {
+ BKE_constraint_custom_object_space_init(cob, curcon);
+
+ for (ct = static_cast<bConstraintTarget *>(targets.first); ct; ct = ct->next) {
/* calculate target's matrix */
- if (cti->get_target_matrix) {
+ if (ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE) {
+ copy_m4_m4(ct->matrix, cob->space_obj_world_matrix);
+ }
+ else if (cti->get_target_matrix) {
cti->get_target_matrix(depsgraph, curcon, cob, ct, DEG_get_ctime(depsgraph));
}
else {
@@ -1330,7 +1337,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
}
}
}
- /* NOTE: Don't use BKE_constraints_clear_evalob here as that will reset ob->constinv. */
+ /* NOTE: Don't use #BKE_constraints_clear_evalob here as that will reset `ob->constinv`. */
MEM_freeN(cob);
}
}
@@ -1389,7 +1396,7 @@ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb,
if (fds->axis_slice_method == AXIS_SLICE_SINGLE) {
float viewinv[4][4];
- DRW_view_viewmat_get(NULL, viewinv, true);
+ DRW_view_viewmat_get(nullptr, viewinv, true);
const int axis = (fds->slice_axis == SLICE_AXIS_AUTO) ? axis_dominant_v3_single(viewinv[2]) :
fds->slice_axis - 1;
@@ -1480,11 +1487,12 @@ static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb,
static void OVERLAY_object_center(OVERLAY_ExtraCallBuffers *cb,
Object *ob,
OVERLAY_PrivateData *pd,
+ const Scene *scene,
ViewLayer *view_layer)
{
const bool is_library = ID_REAL_USERS(&ob->id) > 1 || ID_IS_LINKED(ob);
-
- if (ob == OBACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob == BKE_view_layer_active_object_get(view_layer)) {
DRW_buffer_add_entry(cb->center_active, ob->obmat[3]);
}
else if (ob->base_flag & BASE_SELECTED) {
@@ -1521,7 +1529,7 @@ void OVERLAY_extra_cache_populate(OVERLAY_Data *vedata, Object *ob)
const DRWContextState *draw_ctx = DRW_context_state_get();
ViewLayer *view_layer = draw_ctx->view_layer;
Scene *scene = draw_ctx->scene;
- ModifierData *md = NULL;
+ ModifierData *md = nullptr;
const bool is_select_mode = DRW_state_is_select();
const bool is_paint_mode = (draw_ctx->object_mode &
@@ -1545,7 +1553,7 @@ void OVERLAY_extra_cache_populate(OVERLAY_Data *vedata, Object *ob)
const bool draw_volume = !from_dupli &&
(md = BKE_modifiers_findby_type(ob, eModifierType_Fluid)) &&
(BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) &&
- (((FluidModifierData *)md)->domain != NULL) &&
+ (((FluidModifierData *)md)->domain != nullptr) &&
(scene->r.cfra >=
(((FluidModifierData *)md)->domain->cache_frame_start)) &&
(scene->r.cfra <= (((FluidModifierData *)md)->domain->cache_frame_end));
@@ -1568,7 +1576,7 @@ void OVERLAY_extra_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* don't show object extras in set's */
if (!from_dupli) {
if (draw_obcenters) {
- OVERLAY_object_center(cb, ob, pd, view_layer);
+ OVERLAY_object_center(cb, ob, pd, scene, view_layer);
}
if (draw_relations) {
OVERLAY_relationship_lines(cb, draw_ctx->depsgraph, draw_ctx->scene, ob);
@@ -1579,7 +1587,7 @@ void OVERLAY_extra_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (draw_texspace) {
OVERLAY_texture_space(cb, ob, color);
}
- if (ob->rigidbody_object != NULL) {
+ if (ob->rigidbody_object != nullptr) {
OVERLAY_collision(cb, ob, color);
}
if (ob->dtx & OB_AXIS) {
diff --git a/source/blender/draw/engines/overlay/overlay_facing.c b/source/blender/draw/engines/overlay/overlay_facing.cc
index bf06600eb33..9a501c8f1bb 100644
--- a/source/blender/draw/engines/overlay/overlay_facing.c
+++ b/source/blender/draw/engines/overlay/overlay_facing.cc
@@ -8,7 +8,7 @@
#include "BKE_paint.h"
#include "DRW_render.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_facing_init(OVERLAY_Data *UNUSED(vedata))
{
diff --git a/source/blender/draw/engines/overlay/overlay_fade.c b/source/blender/draw/engines/overlay/overlay_fade.cc
index 056e02a04e1..138226c3e2e 100644
--- a/source/blender/draw/engines/overlay/overlay_fade.c
+++ b/source/blender/draw/engines/overlay/overlay_fade.cc
@@ -10,7 +10,7 @@
#include "ED_view3d.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_fade_init(OVERLAY_Data *UNUSED(vedata))
{
diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.cc
index 9531b0dd983..f72bf81bf57 100644
--- a/source/blender/draw/engines/overlay/overlay_gpencil.c
+++ b/source/blender/draw/engines/overlay/overlay_gpencil.cc
@@ -17,7 +17,7 @@
#include "ED_view3d.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
#include "draw_common.h"
#include "draw_manager_text.h"
@@ -30,22 +30,22 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
DRWShadingGroup *grp;
/* Default: Display nothing. */
- pd->edit_gpencil_points_grp = NULL;
- pd->edit_gpencil_wires_grp = NULL;
- psl->edit_gpencil_ps = NULL;
+ pd->edit_gpencil_points_grp = nullptr;
+ pd->edit_gpencil_wires_grp = nullptr;
+ psl->edit_gpencil_ps = nullptr;
- pd->edit_gpencil_curve_handle_grp = NULL;
- pd->edit_gpencil_curve_points_grp = NULL;
- psl->edit_gpencil_curve_ps = NULL;
+ pd->edit_gpencil_curve_handle_grp = nullptr;
+ pd->edit_gpencil_curve_points_grp = nullptr;
+ psl->edit_gpencil_curve_ps = nullptr;
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
Object *ob = draw_ctx->obact;
- bGPdata *gpd = ob ? (bGPdata *)ob->data : NULL;
+ bGPdata *gpd = ob ? (bGPdata *)ob->data : nullptr;
Scene *scene = draw_ctx->scene;
ToolSettings *ts = scene->toolsettings;
- if (gpd == NULL || ob->type != OB_GPENCIL) {
+ if (gpd == nullptr || ob->type != OB_GPENCIL) {
return;
}
@@ -169,14 +169,14 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
sh = OVERLAY_shader_edit_gpencil_guide_point();
grp = DRW_shgroup_create(sh, psl->edit_gpencil_gizmos_ps);
- if (gpd->runtime.cp_points != NULL) {
+ if (gpd->runtime.cp_points != nullptr) {
for (int i = 0; i < gpd->runtime.tot_cp_points; i++) {
bGPDcontrolpoint *cp = &gpd->runtime.cp_points[i];
grp = DRW_shgroup_create_sub(grp);
DRW_shgroup_uniform_vec3_copy(grp, "pPosition", &cp->x);
DRW_shgroup_uniform_float_copy(grp, "pSize", cp->size * 0.8f * G_draw.block.size_pixel);
DRW_shgroup_uniform_vec4_copy(grp, "pColor", cp->color);
- DRW_shgroup_call_procedural_points(grp, NULL, 1);
+ DRW_shgroup_call_procedural_points(grp, nullptr, 1);
}
}
@@ -187,7 +187,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_vec3_copy(grp, "pPosition", ts->gp_sculpt.guide.location);
}
else if (ts->gp_sculpt.guide.reference_point == GP_GUIDE_REF_OBJECT &&
- ts->gp_sculpt.guide.reference_object != NULL) {
+ ts->gp_sculpt.guide.reference_object != nullptr) {
UI_GetThemeColor4fv(TH_GIZMO_SECONDARY, color);
DRW_shgroup_uniform_vec3_copy(grp, "pPosition", ts->gp_sculpt.guide.reference_object->loc);
}
@@ -197,7 +197,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
}
DRW_shgroup_uniform_vec4_copy(grp, "pColor", color);
DRW_shgroup_uniform_float_copy(grp, "pSize", 8.0f * G_draw.block.size_pixel);
- DRW_shgroup_call_procedural_points(grp, NULL, 1);
+ DRW_shgroup_call_procedural_points(grp, nullptr, 1);
}
}
}
@@ -210,12 +210,12 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
DRWShadingGroup *grp;
/* Default: Display nothing. */
- psl->gpencil_canvas_ps = NULL;
+ psl->gpencil_canvas_ps = nullptr;
const DRWContextState *draw_ctx = DRW_context_state_get();
View3D *v3d = draw_ctx->v3d;
Object *ob = draw_ctx->obact;
- bGPdata *gpd = ob ? (bGPdata *)ob->data : NULL;
+ bGPdata *gpd = ob ? (bGPdata *)ob->data : nullptr;
Scene *scene = draw_ctx->scene;
ToolSettings *ts = scene->toolsettings;
const View3DCursor *cursor = &scene->cursor;
@@ -223,7 +223,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
pd->edit_curve.show_handles = v3d->overlay.handle_display != CURVE_HANDLE_NONE;
pd->edit_curve.handle_display = v3d->overlay.handle_display;
- if (gpd == NULL || ob->type != OB_GPENCIL) {
+ if (gpd == nullptr || ob->type != OB_GPENCIL) {
return;
}
@@ -234,7 +234,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
const bool grid_xray = (v3d->gp_flag & V3D_GP_SHOW_GRID_XRAY);
if (show_grid && show_overlays) {
- const char *grid_unit = NULL;
+ const char *grid_unit = nullptr;
float mat[4][4];
float col_grid[4];
float size[2];
@@ -247,7 +247,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
/* Rotate and scale except align to cursor. */
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
- if (gpl != NULL) {
+ if (gpl != nullptr) {
if (ts->gp_sculpt.lock_axis != GP_LOCKAXIS_CURSOR) {
float matrot[3][3];
copy_m3_m4(matrot, gpl->layer_mat);
@@ -267,12 +267,14 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
case GP_LOCKAXIS_Z:
/* Default. */
break;
- case GP_LOCKAXIS_CURSOR:
- loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, (float[3]){1, 1, 1});
+ case GP_LOCKAXIS_CURSOR: {
+ const float3 size_vec = {1.0f, 1.0f, 1.0f};
+ loc_eul_size_to_mat4(mat, cursor->location, cursor->rotation_euler, size_vec);
break;
+ }
case GP_LOCKAXIS_VIEW:
/* view aligned */
- DRW_view_viewmat_get(NULL, viewinv, true);
+ DRW_view_viewmat_get(nullptr, viewinv, true);
copy_v3_v3(mat[0], viewinv[0]);
copy_v3_v3(mat[1], viewinv[1]);
break;
@@ -289,10 +291,11 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
translate_m4(mat, gpd->grid.offset[0], gpd->grid.offset[1], 0.0f);
mul_v2_v2fl(size, gpd->grid.scale, 2.0f * ED_scene_grid_scale(scene, &grid_unit));
- rescale_m4(mat, (float[3]){size[0], size[1], 0.0f});
+ const float3 scale_vec = {size[0], size[1], 0.0f};
+ rescale_m4(mat, scale_vec);
/* Apply layer loc transform, except cursor mode. */
- if ((gpl != NULL) && (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) {
+ if ((gpl != nullptr) && (ts->gpencil_v3d_align & GP_PROJECT_CURSOR) == 0) {
add_v3_v3(mat[3], gpl->layer_mat[3]);
}
@@ -312,7 +315,7 @@ void OVERLAY_gpencil_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_vec3_copy(grp, "yAxis", mat[1]);
DRW_shgroup_uniform_vec3_copy(grp, "origin", mat[3]);
DRW_shgroup_uniform_int_copy(grp, "halfLineCount", line_count / 2);
- DRW_shgroup_call_procedural_lines(grp, NULL, line_count);
+ DRW_shgroup_call_procedural_lines(grp, nullptr, line_count);
}
}
@@ -368,12 +371,12 @@ static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl),
{
Object *ob = (Object *)thunk;
Material *ma = BKE_object_material_get_eval(ob, gps->mat_nr + 1);
- if (ma == NULL) {
+ if (ma == nullptr) {
return;
}
MaterialGPencilStyle *gp_style = ma->gp_style;
/* skip stroke if it doesn't have any valid data */
- if ((gps->points == NULL) || (gps->totpoints < 1) || (gp_style == NULL)) {
+ if ((gps->points == nullptr) || (gps->totpoints < 1) || (gp_style == nullptr)) {
return;
}
/* check if the color is visible */
@@ -389,7 +392,7 @@ static void overlay_gpencil_draw_stroke_color_name(bGPDlayer *UNUSED(gpl),
const DRWContextState *draw_ctx = DRW_context_state_get();
ViewLayer *view_layer = draw_ctx->view_layer;
- int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL);
+ int theme_id = DRW_object_wire_theme_get(ob, view_layer, nullptr);
uchar color[4];
UI_GetThemeColor4ubv(theme_id, color);
@@ -417,7 +420,7 @@ static void OVERLAY_gpencil_color_names(Object *ob)
int cfra = DEG_get_ctime(draw_ctx->depsgraph);
BKE_gpencil_visible_stroke_advanced_iter(
- NULL, ob, NULL, overlay_gpencil_draw_stroke_color_name, ob, false, cfra);
+ nullptr, ob, nullptr, overlay_gpencil_draw_stroke_color_name, ob, false, cfra);
}
void OVERLAY_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob)
@@ -426,7 +429,7 @@ void OVERLAY_gpencil_cache_populate(OVERLAY_Data *vedata, Object *ob)
View3D *v3d = draw_ctx->v3d;
bGPdata *gpd = (bGPdata *)ob->data;
- if (gpd == NULL) {
+ if (gpd == nullptr) {
return;
}
diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.cc
index e424f49455b..89fc6ec2eb7 100644
--- a/source/blender/draw/engines/overlay/overlay_grid.c
+++ b/source/blender/draw/engines/overlay/overlay_grid.cc
@@ -17,7 +17,7 @@
#include "UI_resources.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
BLI_STATIC_ASSERT(SI_GRID_STEPS_LEN == OVERLAY_GRID_STEPS_LEN, "")
@@ -31,10 +31,12 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
float *zplane_axes = pd->grid.zplane_axes;
float grid_steps[SI_GRID_STEPS_LEN] = {
0.001f, 0.01f, 0.1f, 1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f};
- OVERLAY_GridBits grid_flag = 0, zneg_flag = 0, zpos_flag = 0;
+ float grid_steps_y[SI_GRID_STEPS_LEN] = {0.0f}; /* When zero, use value from grid_steps. */
+ OVERLAY_GridBits grid_flag = OVERLAY_GridBits(0), zneg_flag = OVERLAY_GridBits(0),
+ zpos_flag = OVERLAY_GridBits(0);
grid->line_size = max_ff(0.0f, U.pixelsize - 1.0f) * 0.5f;
/* Default, nothing is drawn. */
- pd->grid.grid_flag = pd->grid.zneg_flag = pd->grid.zpos_flag = 0;
+ pd->grid.grid_flag = pd->grid.zneg_flag = pd->grid.zpos_flag = OVERLAY_GridBits(0);
if (pd->space_type == SPACE_IMAGE) {
SpaceImage *sima = (SpaceImage *)draw_ctx->space_data;
@@ -49,6 +51,9 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
true;
if (background_enabled) {
grid_flag = GRID_BACK | PLANE_IMAGE;
+ if (sima->flag & SI_GRID_OVER_IMAGE) {
+ grid_flag = PLANE_IMAGE;
+ }
}
const bool draw_grid = is_uv_edit || !ED_space_image_has_buffer(sima);
@@ -67,7 +72,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
}
grid->zoom_factor = ED_space_image_zoom_level(v2d, SI_GRID_STEPS_LEN);
- ED_space_image_grid_steps(sima, grid_steps, SI_GRID_STEPS_LEN);
+ ED_space_image_grid_steps(sima, grid_steps, grid_steps_y, SI_GRID_STEPS_LEN);
}
else {
/* SPACE_VIEW3D */
@@ -88,10 +93,10 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
float viewinv[4][4], wininv[4][4];
float viewmat[4][4], winmat[4][4];
- DRW_view_winmat_get(NULL, winmat, false);
- DRW_view_winmat_get(NULL, wininv, true);
- DRW_view_viewmat_get(NULL, viewmat, false);
- DRW_view_viewmat_get(NULL, viewinv, true);
+ DRW_view_winmat_get(nullptr, winmat, false);
+ DRW_view_winmat_get(nullptr, wininv, true);
+ DRW_view_viewmat_get(nullptr, viewmat, false);
+ DRW_view_viewmat_get(nullptr, viewinv, true);
/* If perspective view or non-axis aligned view. */
if (winmat[3][3] == 0.0f || rv3d->view == RV3D_VIEW_USER) {
@@ -196,6 +201,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata)
/* Convert to UBO alignment. */
for (int i = 0; i < SI_GRID_STEPS_LEN; i++) {
grid->steps[i][0] = grid_steps[i];
+ grid->steps[i][1] = (grid_steps_y[i] != 0.0f) ? grid_steps_y[i] : grid_steps[i];
}
pd->grid.grid_flag = grid_flag;
pd->grid.zneg_flag = zneg_flag;
@@ -211,13 +217,13 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *ved)
OVERLAY_PassList *psl = ved->psl;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- psl->grid_ps = NULL;
+ psl->grid_ps = nullptr;
if ((pd->grid.grid_flag == 0 && pd->grid.zpos_flag == 0) || !DRW_state_is_fbo()) {
return;
}
- if (ved->instance->grid_ubo == NULL) {
+ if (ved->instance->grid_ubo == nullptr) {
ved->instance->grid_ubo = GPU_uniformbuf_create(sizeof(OVERLAY_GridData));
}
GPU_uniformbuf_update(ved->instance->grid_ubo, &pd->grid_data);
@@ -257,21 +263,21 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *ved)
DRW_shgroup_uniform_int_copy(grp, "grid_flag", pd->grid.zneg_flag);
DRW_shgroup_uniform_vec3_copy(grp, "plane_axes", pd->grid.zplane_axes);
if (pd->grid.zneg_flag & SHOW_AXIS_Z) {
- DRW_shgroup_call(grp, geom, NULL);
+ DRW_shgroup_call(grp, geom, nullptr);
}
grp = DRW_shgroup_create_sub(grp);
DRW_shgroup_uniform_int_copy(grp, "grid_flag", pd->grid.grid_flag);
DRW_shgroup_uniform_vec3_copy(grp, "plane_axes", pd->grid.grid_axes);
if (pd->grid.grid_flag) {
- DRW_shgroup_call(grp, geom, NULL);
+ DRW_shgroup_call(grp, geom, nullptr);
}
grp = DRW_shgroup_create_sub(grp);
DRW_shgroup_uniform_int_copy(grp, "grid_flag", pd->grid.zpos_flag);
DRW_shgroup_uniform_vec3_copy(grp, "plane_axes", pd->grid.zplane_axes);
if (pd->grid.zpos_flag & SHOW_AXIS_Z) {
- DRW_shgroup_call(grp, geom, NULL);
+ DRW_shgroup_call(grp, geom, nullptr);
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_image.c b/source/blender/draw/engines/overlay/overlay_image.cc
index 3e9c05bb59f..dff30eed620 100644
--- a/source/blender/draw/engines/overlay/overlay_image.c
+++ b/source/blender/draw/engines/overlay/overlay_image.cc
@@ -23,7 +23,7 @@
#include "IMB_imbuf_types.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_image_init(OVERLAY_Data *vedata)
{
@@ -99,10 +99,10 @@ static eStereoViews camera_background_images_stereo_eye(const Scene *scene, cons
}
if (v3d->stereo3d_camera != STEREO_3D_ID) {
/* show only left or right camera */
- return v3d->stereo3d_camera;
+ return eStereoViews(v3d->stereo3d_camera);
}
- return v3d->multiview_eye;
+ return eStereoViews(v3d->multiview_eye);
}
static void camera_background_images_stereo_setup(const Scene *scene,
@@ -130,8 +130,8 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
void *lock;
Image *image = bgpic->ima;
ImageUser *iuser = &bgpic->iuser;
- MovieClip *clip = NULL;
- GPUTexture *tex = NULL;
+ MovieClip *clip = nullptr;
+ GPUTexture *tex = nullptr;
Scene *scene = draw_ctx->scene;
float aspect_x, aspect_y;
int width, height;
@@ -140,9 +140,9 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
*r_use_view_transform = false;
switch (bgpic->source) {
- case CAM_BGIMG_SOURCE_IMAGE:
- if (image == NULL) {
- return NULL;
+ case CAM_BGIMG_SOURCE_IMAGE: {
+ if (image == nullptr) {
+ return nullptr;
}
*r_use_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL);
*r_use_view_transform = (image->flag & IMA_VIEW_AS_RENDER) != 0;
@@ -150,33 +150,34 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
BKE_image_user_frame_calc(image, iuser, ctime);
if (image->source == IMA_SRC_SEQUENCE && !(iuser->flag & IMA_USER_FRAME_IN_RANGE)) {
/* Frame is out of range, don't show. */
- return NULL;
+ return nullptr;
}
camera_background_images_stereo_setup(scene, draw_ctx->v3d, image, iuser);
iuser->scene = draw_ctx->scene;
ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, &lock);
- if (ibuf == NULL) {
+ if (ibuf == nullptr) {
BKE_image_release_ibuf(image, ibuf, lock);
- iuser->scene = NULL;
- return NULL;
+ iuser->scene = nullptr;
+ return nullptr;
}
width = ibuf->x;
height = ibuf->y;
tex = BKE_image_get_gpu_texture(image, iuser, ibuf);
BKE_image_release_ibuf(image, ibuf, lock);
- iuser->scene = NULL;
+ iuser->scene = nullptr;
- if (tex == NULL) {
- return NULL;
+ if (tex == nullptr) {
+ return nullptr;
}
aspect_x = bgpic->ima->aspx;
aspect_y = bgpic->ima->aspy;
break;
+ }
- case CAM_BGIMG_SOURCE_MOVIE:
+ case CAM_BGIMG_SOURCE_MOVIE: {
if (bgpic->flag & CAM_BGIMG_FLAG_CAMERACLIP) {
if (scene->camera) {
clip = BKE_object_movieclip_get(scene, scene->camera, true);
@@ -186,14 +187,14 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
clip = bgpic->clip;
}
- if (clip == NULL) {
- return NULL;
+ if (clip == nullptr) {
+ return nullptr;
}
BKE_movieclip_user_set_frame(&bgpic->cuser, ctime);
tex = BKE_movieclip_get_gpu_texture(clip, &bgpic->cuser);
- if (tex == NULL) {
- return NULL;
+ if (tex == nullptr) {
+ return nullptr;
}
aspect_x = clip->aspx;
@@ -205,10 +206,11 @@ static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgp
/* Save for freeing. */
BLI_addtail(&pd->bg_movie_clips, BLI_genericNodeN(clip));
break;
+ }
default:
/* Unsupported type. */
- return NULL;
+ return nullptr;
}
*r_aspect = (width * aspect_x) / (height * aspect_y);
@@ -219,7 +221,7 @@ static void OVERLAY_image_free_movieclips_textures(OVERLAY_Data *data)
{
/* Free Movie clip textures after rendering */
LinkData *link;
- while ((link = BLI_pophead(&data->stl->pd->bg_movie_clips))) {
+ while ((link = static_cast<LinkData *>(BLI_pophead(&data->stl->pd->bg_movie_clips)))) {
MovieClip *clip = (MovieClip *)link->data;
BKE_movieclip_free_gputexture(clip);
MEM_freeN(link);
@@ -299,7 +301,7 @@ void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob)
const DRWContextState *draw_ctx = DRW_context_state_get();
const View3D *v3d = draw_ctx->v3d;
const Scene *scene = draw_ctx->scene;
- Camera *cam = ob->data;
+ Camera *cam = static_cast<Camera *>(ob->data);
const bool show_frame = BKE_object_empty_image_frame_is_visible_in_view3d(ob, draw_ctx->rv3d);
@@ -333,7 +335,7 @@ void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* Alpha is clamped just below 1.0 to fix background images to interfere with foreground
* images. Without this a background image with 1.0 will be rendered on top of a transparent
* foreground image due to the different blending modes they use. */
- const float color_premult_alpha[4] = {1.0f, 1.0f, 1.0f, MIN2(bgpic->alpha, 0.999999)};
+ const float color_premult_alpha[4] = {1.0f, 1.0f, 1.0f, std::min(bgpic->alpha, 0.999999f)};
DRWPass *pass = is_foreground ? (use_view_transform ? psl->image_foreground_scene_ps :
psl->image_foreground_ps) :
@@ -358,8 +360,8 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob)
OVERLAY_PassList *psl = vedata->psl;
const DRWContextState *draw_ctx = DRW_context_state_get();
const RegionView3D *rv3d = draw_ctx->rv3d;
- GPUTexture *tex = NULL;
- Image *ima = ob->data;
+ GPUTexture *tex = nullptr;
+ Image *ima = static_cast<Image *>(ob->data);
float mat[4][4];
const bool show_frame = BKE_object_empty_image_frame_is_visible_in_view3d(ob, rv3d);
@@ -375,10 +377,10 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* Calling 'BKE_image_get_size' may free the texture. Get the size from 'tex' instead,
* see: T59347 */
int size[2] = {0};
- if (ima != NULL) {
+ if (ima != nullptr) {
ImageUser iuser = *ob->iuser;
camera_background_images_stereo_setup(draw_ctx->scene, draw_ctx->v3d, ima, &iuser);
- tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
+ tex = BKE_image_get_gpu_texture(ima, &iuser, nullptr);
if (tex) {
size[0] = GPU_texture_orig_width(tex);
size[1] = GPU_texture_orig_height(tex);
@@ -388,7 +390,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob)
CLAMP_MIN(size[1], 1);
float image_aspect[2];
- overlay_image_calc_aspect(ob->data, size, image_aspect);
+ overlay_image_calc_aspect(ima, size, image_aspect);
copy_m4_m4(mat, ob->obmat);
mul_v3_fl(mat[0], image_aspect[0] * 0.5f * ob->empty_drawsize);
@@ -399,7 +401,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* Use the actual depth if we are doing depth tests to determine the distance to the object */
char depth_mode = DRW_state_is_depth() ? OB_EMPTY_IMAGE_DEPTH_DEFAULT : ob->empty_image_depth;
- DRWPass *pass = NULL;
+ DRWPass *pass = nullptr;
if ((ob->dtx & OB_DRAW_IN_FRONT) != 0) {
/* Object In Front overrides image empty depth mode. */
pass = psl->image_empties_front_ps;
@@ -479,7 +481,7 @@ void OVERLAY_image_draw(OVERLAY_Data *vedata)
DRW_draw_pass(psl->image_empties_ps);
DRW_draw_pass(psl->image_empties_blend_ps);
- DRW_view_set_active(NULL);
+ DRW_view_set_active(nullptr);
}
void OVERLAY_image_in_front_draw(OVERLAY_Data *vedata)
@@ -492,7 +494,7 @@ void OVERLAY_image_in_front_draw(OVERLAY_Data *vedata)
DRW_draw_pass(psl->image_empties_front_ps);
DRW_draw_pass(psl->image_foreground_ps);
- DRW_view_set_active(NULL);
+ DRW_view_set_active(nullptr);
OVERLAY_image_free_movieclips_textures(vedata);
}
diff --git a/source/blender/draw/engines/overlay/overlay_lattice.c b/source/blender/draw/engines/overlay/overlay_lattice.cc
index 2035a9a9d3b..7b59aa78c89 100644
--- a/source/blender/draw/engines/overlay/overlay_lattice.c
+++ b/source/blender/draw/engines/overlay/overlay_lattice.cc
@@ -7,7 +7,7 @@
#include "DRW_render.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_edit_lattice_cache_init(OVERLAY_Data *vedata)
{
diff --git a/source/blender/draw/engines/overlay/overlay_metaball.c b/source/blender/draw/engines/overlay/overlay_metaball.cc
index f024f5dfac8..38de273028b 100644
--- a/source/blender/draw/engines/overlay/overlay_metaball.c
+++ b/source/blender/draw/engines/overlay/overlay_metaball.cc
@@ -15,7 +15,7 @@
#include "ED_mball.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_metaball_cache_init(OVERLAY_Data *vedata)
{
@@ -27,7 +27,8 @@ void OVERLAY_metaball_cache_init(OVERLAY_Data *vedata)
#define BUF_INSTANCE DRW_shgroup_call_buffer_instance
for (int i = 0; i < 2; i++) {
- DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : 0;
+ DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT :
+ DRWState(0);
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
DRW_PASS_CREATE(psl->metaball_ps[i], state | pd->clipping_state | infront_state);
@@ -57,7 +58,7 @@ void OVERLAY_edit_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob)
const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
const bool is_select = DRW_state_is_select();
OVERLAY_PrivateData *pd = vedata->stl->pd;
- MetaBall *mb = ob->data;
+ MetaBall *mb = static_cast<MetaBall *>(ob->data);
const float *color;
const float *col_radius = G_draw.block.color_mball_radius;
@@ -103,7 +104,7 @@ void OVERLAY_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
const bool do_in_front = (ob->dtx & OB_DRAW_IN_FRONT) != 0;
OVERLAY_PrivateData *pd = vedata->stl->pd;
- MetaBall *mb = ob->data;
+ MetaBall *mb = static_cast<MetaBall *>(ob->data);
const DRWContextState *draw_ctx = DRW_context_state_get();
float *color;
diff --git a/source/blender/draw/engines/overlay/overlay_mode_transfer.c b/source/blender/draw/engines/overlay/overlay_mode_transfer.cc
index e7b2008dee0..d4bc5655da3 100644
--- a/source/blender/draw/engines/overlay/overlay_mode_transfer.c
+++ b/source/blender/draw/engines/overlay/overlay_mode_transfer.cc
@@ -13,7 +13,7 @@
#include "PIL_time.h"
#include "UI_resources.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_mode_transfer_cache_init(OVERLAY_Data *vedata)
{
diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.cc
index 58825923f37..00b2a8665fd 100644
--- a/source/blender/draw/engines/overlay/overlay_motion_path.c
+++ b/source/blender/draw/engines/overlay/overlay_motion_path.cc
@@ -20,7 +20,7 @@
#include "draw_manager_text.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_motion_path_cache_init(OVERLAY_Data *vedata)
{
@@ -63,7 +63,7 @@ static GPUVertBuf *mpath_vbo_get(bMotionPath *mpath)
static GPUBatch *mpath_batch_line_get(bMotionPath *mpath)
{
if (!mpath->batch_line) {
- mpath->batch_line = GPU_batch_create(GPU_PRIM_LINE_STRIP, mpath_vbo_get(mpath), NULL);
+ mpath->batch_line = GPU_batch_create(GPU_PRIM_LINE_STRIP, mpath_vbo_get(mpath), nullptr);
}
return mpath->batch_line;
}
@@ -71,7 +71,7 @@ static GPUBatch *mpath_batch_line_get(bMotionPath *mpath)
static GPUBatch *mpath_batch_points_get(bMotionPath *mpath)
{
if (!mpath->batch_points) {
- mpath->batch_points = GPU_batch_create(GPU_PRIM_POINTS, mpath_vbo_get(mpath), NULL);
+ mpath->batch_points = GPU_batch_create(GPU_PRIM_POINTS, mpath_vbo_get(mpath), nullptr);
}
return mpath->batch_points;
}
@@ -143,7 +143,7 @@ static void motion_path_cache(OVERLAY_Data *vedata,
DRW_shgroup_uniform_bool_copy(grp, "selected", selected);
DRW_shgroup_uniform_vec3_copy(grp, "customColor", color);
/* Only draw the required range. */
- DRW_shgroup_call_range(grp, NULL, mpath_batch_line_get(mpath), start_index, len);
+ DRW_shgroup_call_range(grp, nullptr, mpath_batch_line_get(mpath), start_index, len);
}
/* Draw points. */
@@ -155,7 +155,7 @@ static void motion_path_cache(OVERLAY_Data *vedata,
DRW_shgroup_uniform_bool_copy(grp, "showKeyFrames", show_keyframes);
DRW_shgroup_uniform_vec3_copy(grp, "customColor", color);
/* Only draw the required range. */
- DRW_shgroup_call_range(grp, NULL, mpath_batch_points_get(mpath), start_index, len);
+ DRW_shgroup_call_range(grp, nullptr, mpath_batch_points_get(mpath), start_index, len);
}
/* Draw frame numbers at each frame-step value. */
@@ -208,7 +208,7 @@ void OVERLAY_motion_path_cache_populate(OVERLAY_Data *vedata, Object *ob)
}
if (ob->mpath) {
- motion_path_cache(vedata, ob, NULL, &ob->avs, ob->mpath);
+ motion_path_cache(vedata, ob, nullptr, &ob->avs, ob->mpath);
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_outline.c b/source/blender/draw/engines/overlay/overlay_outline.cc
index f2e2acc98a9..e308775dc6e 100644
--- a/source/blender/draw/engines/overlay/overlay_outline.c
+++ b/source/blender/draw/engines/overlay/overlay_outline.cc
@@ -16,14 +16,14 @@
#include "UI_resources.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
/* Returns the normal plane in NDC space. */
static void gpencil_depth_plane(Object *ob, float r_plane[4])
{
/* TODO: put that into private data. */
float viewinv[4][4];
- DRW_view_viewmat_get(NULL, viewinv, true);
+ DRW_view_viewmat_get(nullptr, viewinv, true);
float *camera_z_axis = viewinv[2];
float *camera_pos = viewinv[3];
@@ -47,7 +47,7 @@ static void gpencil_depth_plane(Object *ob, float r_plane[4])
/* BBox center in world space. */
copy_v3_v3(center, mat[3]);
/* View Vector. */
- if (DRW_view_is_persp_get(NULL)) {
+ if (DRW_view_is_persp_get(nullptr)) {
/* BBox center to camera vector. */
sub_v3_v3v3(r_plane, camera_pos, mat[3]);
}
@@ -78,8 +78,8 @@ void OVERLAY_outline_init(OVERLAY_Data *vedata)
if (DRW_state_is_fbo()) {
/* TODO: only alloc if needed. */
- DRW_texture_ensure_fullscreen_2d(&txl->temp_depth_tx, GPU_DEPTH24_STENCIL8, 0);
- DRW_texture_ensure_fullscreen_2d(&txl->outlines_id_tx, GPU_R16UI, 0);
+ DRW_texture_ensure_fullscreen_2d(&txl->temp_depth_tx, GPU_DEPTH24_STENCIL8, DRWTextureFlag(0));
+ DRW_texture_ensure_fullscreen_2d(&txl->outlines_id_tx, GPU_R16UI, DRWTextureFlag(0));
GPU_framebuffer_ensure_config(
&fbl->outlines_prepass_fb,
@@ -109,7 +109,7 @@ void OVERLAY_outline_cache_init(OVERLAY_Data *vedata)
OVERLAY_TextureList *txl = vedata->txl;
OVERLAY_PrivateData *pd = vedata->stl->pd;
DefaultTextureList *dtxl = DRW_viewport_texture_list_get();
- DRWShadingGroup *grp = NULL;
+ DRWShadingGroup *grp = nullptr;
const float outline_width = UI_GetThemeValuef(TH_OUTLINE_WIDTH);
const bool do_expand = (U.pixelsize > 1.0) || (outline_width > 2.0f);
@@ -161,7 +161,7 @@ void OVERLAY_outline_cache_init(OVERLAY_Data *vedata)
DRW_shgroup_uniform_texture_ref(grp, "sceneDepth", &dtxl->depth);
DRW_shgroup_uniform_texture_ref(grp, "outlineDepth", &txl->temp_depth_tx);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
- DRW_shgroup_call_procedural_triangles(grp, NULL, 1);
+ DRW_shgroup_call_procedural_triangles(grp, nullptr, 1);
}
}
@@ -240,18 +240,17 @@ static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob)
return;
}
- iterData iter = {
- .ob = ob,
- .stroke_grp = pd->outlines_gpencil_grp,
- .fill_grp = DRW_shgroup_create_sub(pd->outlines_gpencil_grp),
- .cfra = pd->cfra,
- };
+ iterData iter{};
+ iter.ob = ob;
+ iter.stroke_grp = pd->outlines_gpencil_grp;
+ iter.fill_grp = DRW_shgroup_create_sub(pd->outlines_gpencil_grp);
+ iter.cfra = pd->cfra;
if (gpd->draw_mode == GP_DRAWMODE_2D) {
gpencil_depth_plane(ob, iter.plane);
}
- BKE_gpencil_visible_stroke_advanced_iter(NULL,
+ BKE_gpencil_visible_stroke_advanced_iter(nullptr,
ob,
gpencil_layer_cache_populate,
gpencil_stroke_cache_populate,
@@ -263,7 +262,7 @@ static void OVERLAY_outline_gpencil(OVERLAY_PrivateData *pd, Object *ob)
static void OVERLAY_outline_volume(OVERLAY_PrivateData *pd, Object *ob)
{
struct GPUBatch *geom = DRW_cache_volume_selection_surface_get(ob);
- if (geom == NULL) {
+ if (geom == nullptr) {
return;
}
@@ -274,7 +273,7 @@ static void OVERLAY_outline_volume(OVERLAY_PrivateData *pd, Object *ob)
static void OVERLAY_outline_curves(OVERLAY_PrivateData *pd, Object *ob)
{
DRWShadingGroup *shgroup = pd->outlines_curves_grp;
- DRW_shgroup_curves_create_sub(ob, shgroup, NULL);
+ DRW_shgroup_curves_create_sub(ob, shgroup, nullptr);
}
void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
@@ -285,7 +284,7 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
OVERLAY_PrivateData *pd = vedata->stl->pd;
const DRWContextState *draw_ctx = DRW_context_state_get();
struct GPUBatch *geom;
- DRWShadingGroup *shgroup = NULL;
+ DRWShadingGroup *shgroup = nullptr;
const bool draw_outline = ob->dt > OB_BOUNDBOX;
/* Early exit: outlines of bounding boxes are not drawn. */
@@ -326,7 +325,7 @@ void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata,
DRW_object_axis_orthogonal_to_view(ob, flat_axis));
if (pd->xray_enabled_and_not_wire || is_flat_object_viewed_from_side) {
- geom = DRW_cache_object_edge_detection_get(ob, NULL);
+ geom = DRW_cache_object_edge_detection_get(ob, nullptr);
}
else {
geom = DRW_cache_object_surface_get(ob);
@@ -359,7 +358,7 @@ void OVERLAY_outline_draw(OVERLAY_Data *vedata)
OVERLAY_PassList *psl = vedata->psl;
const float clearcol[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- bool do_outlines = psl->outlines_prepass_ps != NULL &&
+ bool do_outlines = psl->outlines_prepass_ps != nullptr &&
!DRW_pass_is_empty(psl->outlines_prepass_ps);
if (DRW_state_is_fbo() && do_outlines) {
diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.cc
index c730c702a8d..d2573d1db0b 100644
--- a/source/blender/draw/engines/overlay/overlay_paint.c
+++ b/source/blender/draw/engines/overlay/overlay_paint.cc
@@ -13,7 +13,7 @@
#include "DEG_depsgraph_query.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
/* Check if the given object is rendered (partially) transparent */
static bool paint_object_is_rendered_transparent(View3D *v3d, Object *ob)
@@ -31,7 +31,7 @@ static bool paint_object_is_rendered_transparent(View3D *v3d, Object *ob)
}
if (ob && ob->type == OB_MESH && ob->data &&
v3d->shading.color_type == V3D_SHADING_MATERIAL_COLOR) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
for (int i = 0; i < me->totcol; i++) {
Material *mat = BKE_object_material_get_eval(ob, i + 1);
if (mat && mat->a < 1.0f) {
@@ -74,8 +74,8 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata)
const bool draw_contours = !is_edit_mode &&
(pd->overlay.wpaint_flag & V3D_OVERLAY_WPAINT_CONTOURS) != 0;
float opacity = 0.0f;
- pd->paint_depth_grp = NULL;
- psl->paint_depth_ps = NULL;
+ pd->paint_depth_grp = nullptr;
+ psl->paint_depth_ps = nullptr;
switch (pd->ctx_mode) {
case CTX_MODE_POSE:
@@ -130,14 +130,14 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata)
case CTX_MODE_PAINT_TEXTURE: {
const ImagePaintSettings *imapaint = &draw_ctx->scene->toolsettings->imapaint;
const bool mask_enabled = imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL &&
- imapaint->stencil != NULL;
+ imapaint->stencil != nullptr;
opacity = mask_enabled ? pd->overlay.texture_paint_mode_opacity : 0.0f;
if (opacity > 0.0f) {
state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state);
- GPUTexture *tex = BKE_image_get_gpu_texture(imapaint->stencil, NULL, NULL);
+ GPUTexture *tex = BKE_image_get_gpu_texture(imapaint->stencil, nullptr, nullptr);
const bool mask_premult = (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL);
const bool mask_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0;
@@ -158,8 +158,8 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata)
}
if (opacity <= 0.0f) {
- psl->paint_color_ps = NULL;
- pd->paint_surf_grp = NULL;
+ psl->paint_color_ps = nullptr;
+ pd->paint_surf_grp = nullptr;
}
{
@@ -167,7 +167,8 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata)
DRW_PASS_CREATE(psl->paint_overlay_ps, state | pd->clipping_state);
sh = OVERLAY_shader_paint_face();
pd->paint_face_grp = grp = DRW_shgroup_create(sh, psl->paint_overlay_ps);
- DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){1.0f, 1.0f, 1.0f, 0.2f});
+ const float4 color = {1.0f, 1.0f, 1.0f, 0.2f};
+ DRW_shgroup_uniform_vec4_copy(grp, "color", color);
DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA);
sh = OVERLAY_shader_paint_wire();
@@ -190,9 +191,9 @@ void OVERLAY_paint_cache_init(OVERLAY_Data *vedata)
void OVERLAY_paint_texture_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
- struct GPUBatch *geom = NULL;
+ struct GPUBatch *geom = nullptr;
- const Mesh *me_orig = DEG_get_original_object(ob)->data;
+ const Mesh *me_orig = static_cast<Mesh *>(DEG_get_original_object(ob)->data);
const bool use_face_sel = (me_orig->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
if (pd->paint_surf_grp) {
@@ -209,9 +210,9 @@ void OVERLAY_paint_texture_cache_populate(OVERLAY_Data *vedata, Object *ob)
void OVERLAY_paint_vertex_cache_populate(OVERLAY_Data *vedata, Object *ob)
{
OVERLAY_PrivateData *pd = vedata->stl->pd;
- struct GPUBatch *geom = NULL;
+ struct GPUBatch *geom = nullptr;
- const Mesh *me_orig = DEG_get_original_object(ob)->data;
+ const Mesh *me_orig = static_cast<Mesh *>(DEG_get_original_object(ob)->data);
const bool is_edit_mode = (pd->ctx_mode == CTX_MODE_EDIT_MESH);
const bool use_wire = !is_edit_mode && (pd->overlay.paint_flag & V3D_OVERLAY_PAINT_WIRE);
const bool use_face_sel = !is_edit_mode && (me_orig->editflag & ME_EDIT_PAINT_FACE_SEL);
diff --git a/source/blender/draw/engines/overlay/overlay_particle.c b/source/blender/draw/engines/overlay/overlay_particle.cc
index b985bfb24bf..6f77a777ba0 100644
--- a/source/blender/draw/engines/overlay/overlay_particle.c
+++ b/source/blender/draw/engines/overlay/overlay_particle.cc
@@ -15,7 +15,7 @@
#include "ED_particle.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
/* -------------------------------------------------------------------- */
/** \name Edit Particles
@@ -64,7 +64,7 @@ void OVERLAY_edit_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
*/
Object *ob_orig = DEG_get_original_object(ob);
PTCacheEdit *edit = PE_create_current(draw_ctx->depsgraph, scene_orig, ob_orig);
- if (edit == NULL) {
+ if (edit == nullptr) {
/* Happens when trying to edit particles in EMITTER mode without
* having them cached.
*/
@@ -73,14 +73,14 @@ void OVERLAY_edit_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* NOTE: We need to pass evaluated particle system, which we need
* to find first.
*/
- ParticleSystem *psys = ob->particlesystem.first;
+ ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
LISTBASE_FOREACH (ParticleSystem *, psys_orig, &ob_orig->particlesystem) {
if (PE_get_current_from_psys(psys_orig) == edit) {
break;
}
psys = psys->next;
}
- if (psys == NULL) {
+ if (psys == nullptr) {
printf("Error getting evaluated particle system for edit.\n");
return;
}
@@ -88,17 +88,17 @@ void OVERLAY_edit_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
struct GPUBatch *geom;
{
geom = DRW_cache_particles_get_edit_strands(ob, psys, edit, pd->edit_particle.use_weight);
- DRW_shgroup_call(pd->edit_particle_strand_grp, geom, NULL);
+ DRW_shgroup_call(pd->edit_particle_strand_grp, geom, nullptr);
}
if (pd->edit_particle.select_mode == SCE_SELECT_POINT) {
geom = DRW_cache_particles_get_edit_inner_points(ob, psys, edit);
- DRW_shgroup_call(pd->edit_particle_point_grp, geom, NULL);
+ DRW_shgroup_call(pd->edit_particle_point_grp, geom, nullptr);
}
if (ELEM(pd->edit_particle.select_mode, SCE_SELECT_POINT, SCE_SELECT_END)) {
geom = DRW_cache_particles_get_edit_tip_points(ob, psys, edit);
- DRW_shgroup_call(pd->edit_particle_point_grp, geom, NULL);
+ DRW_shgroup_call(pd->edit_particle_point_grp, geom, nullptr);
}
}
@@ -165,7 +165,7 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (!ELEM(draw_as, PART_DRAW_NOT, PART_DRAW_OB, PART_DRAW_GR)) {
struct GPUBatch *geom = DRW_cache_particles_get_dots(ob, psys);
- struct GPUBatch *shape = NULL;
+ struct GPUBatch *shape = nullptr;
DRWShadingGroup *grp;
/* TODO(fclem): Here would be a good place for preemptive culling. */
@@ -173,7 +173,7 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
/* NOTE(fclem): Is color even useful in our modern context? */
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) {
+ if (ma != nullptr) {
copy_v3_v3(color, &ma->r);
}
@@ -182,7 +182,7 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
case PART_DRAW_DOT:
grp = DRW_shgroup_create_sub(pd->particle_dots_grp);
DRW_shgroup_uniform_vec4_copy(grp, "color", color);
- DRW_shgroup_call(grp, geom, NULL);
+ DRW_shgroup_call(grp, geom, nullptr);
break;
case PART_DRAW_AXIS:
case PART_DRAW_CIRC:
@@ -190,7 +190,7 @@ void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob)
grp = DRW_shgroup_create_sub(pd->particle_shapes_grp);
DRW_shgroup_uniform_vec4_copy(grp, "color", color);
shape = DRW_cache_particles_get_prim(draw_as);
- DRW_shgroup_call_instances_with_attrs(grp, NULL, shape, geom);
+ DRW_shgroup_call_instances_with_attrs(grp, nullptr, shape, geom);
break;
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.hh
index 7d216ca54cf..0a783c44029 100644
--- a/source/blender/draw/engines/overlay/overlay_private.h
+++ b/source/blender/draw/engines/overlay/overlay_private.hh
@@ -78,8 +78,9 @@ typedef struct OVERLAY_PassList {
DRWPass *edit_mesh_analysis_ps;
DRWPass *edit_mesh_normals_ps;
DRWPass *edit_particle_ps;
- DRWPass *edit_text_overlay_ps;
- DRWPass *edit_text_darken_ps;
+ DRWPass *edit_text_cursor_ps;
+ DRWPass *edit_text_selection_ps;
+ DRWPass *edit_text_highlight_ps;
DRWPass *edit_text_wire_ps[2];
DRWPass *edit_uv_edges_ps;
DRWPass *edit_uv_verts_ps;
@@ -252,7 +253,8 @@ typedef struct OVERLAY_PrivateData {
DRWShadingGroup *edit_mesh_analysis_grp;
DRWShadingGroup *edit_particle_strand_grp;
DRWShadingGroup *edit_particle_point_grp;
- DRWShadingGroup *edit_text_overlay_grp;
+ DRWShadingGroup *edit_text_cursor_grp;
+ DRWShadingGroup *edit_text_selection_grp;
DRWShadingGroup *edit_text_wire_grp[2];
DRWShadingGroup *edit_uv_verts_grp;
DRWShadingGroup *edit_uv_edges_grp;
@@ -338,7 +340,8 @@ typedef struct OVERLAY_PrivateData {
int handle_display;
} edit_curve;
struct {
- float overlay_color[4];
+ float cursor_color[4];
+ float selection_color[4];
} edit_text;
struct {
bool do_zbufclip;
@@ -384,7 +387,6 @@ typedef struct OVERLAY_PrivateData {
eSpaceImage_UVDT_Stretch draw_type;
ListBase totals;
float total_area_ratio;
- float total_area_ratio_inv;
/* stencil overlay */
struct Image *stencil_image;
diff --git a/source/blender/draw/engines/overlay/overlay_sculpt.c b/source/blender/draw/engines/overlay/overlay_sculpt.cc
index 4a6477b3f6c..ddad1f06537 100644
--- a/source/blender/draw/engines/overlay/overlay_sculpt.c
+++ b/source/blender/draw/engines/overlay/overlay_sculpt.cc
@@ -8,7 +8,7 @@
#include "DRW_render.h"
#include "draw_cache_impl.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
#include "BKE_paint.h"
#include "BKE_pbvh.h"
@@ -56,7 +56,7 @@ void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob)
DRW_shgroup_call_sculpt(pd->sculpt_mask_grp, ob, false, true);
}
else {
- sculpt_overlays = DRW_mesh_batch_cache_get_sculpt_overlays(ob->data);
+ sculpt_overlays = DRW_mesh_batch_cache_get_sculpt_overlays(static_cast<Mesh *>(ob->data));
if (sculpt_overlays) {
DRW_shgroup_call(pd->sculpt_mask_grp, sculpt_overlays, ob);
}
diff --git a/source/blender/draw/engines/overlay/overlay_sculpt_curves.cc b/source/blender/draw/engines/overlay/overlay_sculpt_curves.cc
index b8021124f27..1bba2a366a5 100644
--- a/source/blender/draw/engines/overlay/overlay_sculpt_curves.cc
+++ b/source/blender/draw/engines/overlay/overlay_sculpt_curves.cc
@@ -8,7 +8,7 @@
#include "DRW_render.h"
#include "draw_cache_impl.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
#include "BKE_curves.hh"
diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.cc
index 2373363ab9d..b0a6926a57f 100644
--- a/source/blender/draw/engines/overlay/overlay_shader.c
+++ b/source/blender/draw/engines/overlay/overlay_shader.cc
@@ -11,7 +11,7 @@
#include "UI_resources.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
typedef struct OVERLAY_Shaders {
GPUShader *antialiasing;
@@ -106,7 +106,7 @@ typedef struct OVERLAY_Shaders {
static struct {
OVERLAY_Shaders sh_data[GPU_SHADER_CFG_LEN];
-} e_data = {{{NULL}}};
+} e_data = {{{nullptr}}};
GPUShader *OVERLAY_shader_antialiasing(void)
{
@@ -162,7 +162,7 @@ GPUShader *OVERLAY_shader_edit_mesh_edge(bool use_flat_interp)
const DRWContextState *draw_ctx = DRW_context_state_get();
OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
GPUShader **sh = use_flat_interp ? &sh_data->edit_mesh_edge_flat : &sh_data->edit_mesh_edge;
- if (*sh == NULL) {
+ if (*sh == nullptr) {
*sh = GPU_shader_create_from_info_name(
draw_ctx->sh_cfg ?
(use_flat_interp ? "overlay_edit_mesh_edge_flat_clipped" :
@@ -486,7 +486,7 @@ GPUShader *OVERLAY_shader_extra_wire(bool use_object, bool is_select)
OVERLAY_Shaders *sh_data = &e_data.sh_data[draw_ctx->sh_cfg];
GPUShader **sh = (is_select) ? &sh_data->extra_wire_select : &sh_data->extra_wire[use_object];
if (!*sh) {
- const char *info_name = NULL;
+ const char *info_name = nullptr;
if (draw_ctx->sh_cfg) {
if (is_select) {
info_name = "overlay_extra_wire_select_clipped";
@@ -996,7 +996,7 @@ GPUShader *OVERLAY_shader_edit_uv_tiled_image_borders_get(void)
/** \} */
-static OVERLAY_InstanceFormats g_formats = {NULL};
+static OVERLAY_InstanceFormats g_formats = {nullptr};
OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void)
{
diff --git a/source/blender/draw/engines/overlay/overlay_shader_shared.h b/source/blender/draw/engines/overlay/overlay_shader_shared.h
index 339b6f02e1a..739e5be6c2f 100644
--- a/source/blender/draw/engines/overlay/overlay_shader_shared.h
+++ b/source/blender/draw/engines/overlay/overlay_shader_shared.h
@@ -38,6 +38,9 @@ enum OVERLAY_GridBits {
PLANE_IMAGE = (1u << 11u),
CUSTOM_GRID = (1u << 12u),
};
+#ifndef GPU_SHADER
+ENUM_OPERATORS(OVERLAY_GridBits, CUSTOM_GRID)
+#endif
/* Match: #SI_GRID_STEPS_LEN */
#define OVERLAY_GRID_STEPS_LEN 8
diff --git a/source/blender/draw/engines/overlay/overlay_volume.c b/source/blender/draw/engines/overlay/overlay_volume.cc
index ee0d80734ab..daf76c394b3 100644
--- a/source/blender/draw/engines/overlay/overlay_volume.c
+++ b/source/blender/draw/engines/overlay/overlay_volume.cc
@@ -9,7 +9,7 @@
#include "DRW_render.h"
#include "GPU_shader.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_volume_cache_init(OVERLAY_Data *vedata)
{
@@ -25,8 +25,8 @@ void OVERLAY_volume_cache_init(OVERLAY_Data *vedata)
pd->volume_selection_surface_grp = grp;
}
else {
- psl->volume_ps = NULL;
- pd->volume_selection_surface_grp = NULL;
+ psl->volume_ps = nullptr;
+ pd->volume_selection_surface_grp = nullptr;
}
}
@@ -37,7 +37,7 @@ void OVERLAY_volume_cache_populate(OVERLAY_Data *vedata, Object *ob)
if (is_select) {
struct GPUBatch *geom = DRW_cache_volume_selection_surface_get(ob);
- if (geom != NULL) {
+ if (geom != nullptr) {
DRW_shgroup_call(pd->volume_selection_surface_grp, geom, ob);
}
}
diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.cc
index a5628559cfd..edaa96651b2 100644
--- a/source/blender/draw/engines/overlay/overlay_wireframe.c
+++ b/source/blender/draw/engines/overlay/overlay_wireframe.cc
@@ -27,7 +27,7 @@
#include "ED_view3d.h"
-#include "overlay_private.h"
+#include "overlay_private.hh"
void OVERLAY_wireframe_init(OVERLAY_Data *vedata)
{
@@ -43,7 +43,7 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata)
OVERLAY_TextureList *txl = vedata->txl;
OVERLAY_PrivateData *pd = vedata->stl->pd;
const DRWContextState *draw_ctx = DRW_context_state_get();
- DRWShadingGroup *grp = NULL;
+ DRWShadingGroup *grp = nullptr;
View3DShading *shading = &draw_ctx->v3d->shading;
@@ -112,7 +112,7 @@ void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata)
pd->wires_hair_grp[1][use_coloring] = pd->wires_hair_grp[0][use_coloring];
}
pd->wires_sculpt_grp[1] = pd->wires_sculpt_grp[0];
- psl->wireframe_xray_ps = NULL;
+ psl->wireframe_xray_ps = nullptr;
}
}
@@ -125,11 +125,11 @@ static void wireframe_hair_cache_populate(OVERLAY_Data *vedata, Object *ob, Part
DupliObject *dupli_object = DRW_object_get_dupli(ob);
float dupli_mat[4][4];
- if ((dupli_parent != NULL) && (dupli_object != NULL)) {
+ if ((dupli_parent != nullptr) && (dupli_object != nullptr)) {
if (dupli_object->type & OB_DUPLICOLLECTION) {
unit_m4(dupli_mat);
Collection *collection = dupli_parent->instance_collection;
- if (collection != NULL) {
+ if (collection != nullptr) {
sub_v3_v3(dupli_mat[3], collection->instance_offset);
}
mul_m4_m4m4(dupli_mat, dupli_parent->obmat, dupli_mat);
@@ -144,7 +144,7 @@ static void wireframe_hair_cache_populate(OVERLAY_Data *vedata, Object *ob, Part
unit_m4(dupli_mat);
}
- struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL);
+ struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, nullptr);
const bool use_coloring = true;
DRWShadingGroup *shgrp = DRW_shgroup_create_sub(pd->wires_hair_grp[is_xray][use_coloring]);
@@ -167,7 +167,7 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
bool is_mesh_verts_only = false;
if (is_mesh) {
/* TODO: Should be its own function. */
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (is_edit_mode) {
BLI_assert(me->edit_mesh);
Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob);
@@ -184,7 +184,9 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
(ob->dtx & OB_DRAWWIRE) || (ob->dt == OB_WIRE));
if (use_wire && pd->wireframe_mode && ob->particlesystem.first) {
- for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) {
+ for (ParticleSystem *psys = static_cast<ParticleSystem *>(ob->particlesystem.first);
+ psys != nullptr;
+ psys = psys->next) {
if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) {
continue;
}
@@ -201,7 +203,7 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
float *color;
DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color);
- struct GPUBatch *geom = NULL;
+ struct GPUBatch *geom = nullptr;
switch (ob->type) {
case OB_CURVES_LEGACY:
geom = DRW_cache_curve_edge_wire_get(ob);
@@ -251,7 +253,7 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
bool draw_as_points = true;
if (ob->type == OB_VOLUME) {
/* Volume object as points exception. */
- Volume *volume = ob->data;
+ Volume *volume = static_cast<Volume *>(ob->data);
draw_as_points = volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS;
}
@@ -268,12 +270,12 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata,
}
}
- DRWShadingGroup *shgrp = NULL;
- struct GPUBatch *geom = NULL;
+ DRWShadingGroup *shgrp = nullptr;
+ struct GPUBatch *geom = nullptr;
/* 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);
+ const bool is_sculpt_mode = ((ob->mode & OB_MODE_SCULPT) != 0) && (ob->sculpt != nullptr);
const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) &&
!DRW_state_is_image_render();
const bool is_instance = (ob->base_flag & BASE_FROM_DUPLI);
@@ -343,7 +345,7 @@ void OVERLAY_wireframe_draw(OVERLAY_Data *data)
DRW_view_set_active(pd->view_wires);
DRW_draw_pass(psl->wireframe_ps);
- DRW_view_set_active(NULL);
+ DRW_view_set_active(nullptr);
}
void OVERLAY_wireframe_in_front_draw(OVERLAY_Data *data)
@@ -355,6 +357,6 @@ void OVERLAY_wireframe_in_front_draw(OVERLAY_Data *data)
DRW_view_set_active(pd->view_wires);
DRW_draw_pass(psl->wireframe_xray_ps);
- DRW_view_set_active(NULL);
+ DRW_view_set_active(nullptr);
}
}
diff --git a/source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh b/source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh
index 58f96110887..9396a6d3f2f 100644
--- a/source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh
+++ b/source/blender/draw/engines/overlay/shaders/infos/overlay_edit_mode_info.hh
@@ -293,7 +293,6 @@ GPU_SHADER_CREATE_INFO(overlay_edit_uv_stretching_area)
.do_static_compilation(true)
.vertex_in(1, Type::FLOAT, "ratio")
.push_constant(Type::FLOAT, "totalAreaRatio")
- .push_constant(Type::FLOAT, "totalAreaRatioInv")
.additional_info("overlay_edit_uv_stretching");
GPU_SHADER_CREATE_INFO(overlay_edit_uv_stretching_angle)
diff --git a/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl
index f28a809fdab..606292bbe83 100644
--- a/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl
@@ -96,7 +96,7 @@ void main()
float dist_raw = texelFetch(lineTex, center_texel, 0).b;
float dist = decode_line_dist(dist_raw);
- /* TODO: Opti: use textureGather. */
+ /* TODO: Optimization: use textureGather. */
vec4 neightbor_col0 = texelFetchOffset(colorTex, center_texel, 0, ivec2(1, 0));
vec4 neightbor_col1 = texelFetchOffset(colorTex, center_texel, 0, ivec2(-1, 0));
vec4 neightbor_col2 = texelFetchOffset(colorTex, center_texel, 0, ivec2(0, 1));
diff --git a/source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl
index 612ce8c6300..ca5a6aff2ca 100644
--- a/source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl
@@ -130,7 +130,7 @@ void main()
gl_Position = p1;
/* compute position from 3 vertex because the change in direction
- * can happen very quicky and lead to very thin edges. */
+ * can happen very quickly and lead to very thin edges. */
vec2 ss0 = proj(p0);
vec2 ss1 = proj(p1);
vec2 ss2 = proj(p2);
diff --git a/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_stretching_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_stretching_vert.glsl
index bb086e8d9f5..9a3036d5940 100644
--- a/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_stretching_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/overlay_edit_uv_stretching_vert.glsl
@@ -55,9 +55,9 @@ float angle_normalized_v2v2(vec2 v1, vec2 v2)
return (q) ? a : M_PI - a;
}
-float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio)
+float area_ratio_to_stretch(float ratio, float tot_ratio)
{
- ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio;
+ ratio *= tot_ratio;
return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
}
@@ -74,7 +74,7 @@ void main()
stretch = stretch;
stretch = 1.0 - stretch * stretch;
#else
- float stretch = 1.0 - area_ratio_to_stretch(ratio, totalAreaRatio, totalAreaRatioInv);
+ float stretch = 1.0 - area_ratio_to_stretch(ratio, totalAreaRatio);
#endif
diff --git a/source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl
index 54a4231590e..2c81966fe50 100644
--- a/source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl
+++ b/source/blender/draw/engines/overlay/shaders/overlay_grid_frag.glsl
@@ -1,6 +1,6 @@
/**
* Infinite grid:
- * Draw antialiazed grid and axes of different sizes with smooth blending between Level of details.
+ * Draw antialiased grid and axes of different sizes with smooth blending between levels of detail.
* We draw multiple triangles to avoid float precision issues due to perspective interpolation.
**/
@@ -8,29 +8,33 @@
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
/**
- * We want to know how much a pixel is covered by a line.
- * We replace the square pixel with acircle of the same area and try to find the intersection area.
- * The area we search is the circular segment. https://en.wikipedia.org/wiki/Circular_segment
- * The formula for the area uses inverse trig function and is quite complexe. Instead,
- * we approximate it by using the smoothstep function and a 1.05 factor to the disc radius.
+ * We want to know how much of a pixel is covered by a line.
+ * Here, we imagine the square pixel is a circle with the same area and try to find the
+ * intersection area. The overlap area is a circular segment.
+ * https://en.wikipedia.org/wiki/Circular_segment The formula for the area uses inverse trig
+ * function and is quite complex. Instead, we approximate it by using the smoothstep function and
+ * a 1.05 factor to the disc radius.
+ *
+ * For an alternate approach, see:
+ * https://developer.nvidia.com/gpugems/gpugems2/part-iii-high-quality-rendering/chapter-22-fast-prefiltered-lines
*/
#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */
#define DISC_RADIUS (M_1_SQRTPI * 1.05)
-#define GRID_LINE_SMOOTH_START (0.5 - DISC_RADIUS)
-#define GRID_LINE_SMOOTH_END (0.5 + DISC_RADIUS)
+#define GRID_LINE_SMOOTH_START (0.5 + DISC_RADIUS)
+#define GRID_LINE_SMOOTH_END (0.5 - DISC_RADIUS)
#define GRID_LINE_STEP(dist) smoothstep(GRID_LINE_SMOOTH_START, GRID_LINE_SMOOTH_END, dist)
-float get_grid(vec2 co, vec2 fwidthCos, float grid_scale)
+float get_grid(vec2 co, vec2 fwidthCos, vec2 grid_scale)
{
- float half_size = grid_scale / 2.0;
+ vec2 half_size = grid_scale / 2.0;
/* Triangular wave pattern, amplitude is [0, half_size]. */
- vec2 grid_domain = abs(mod(co + half_size, vec2(grid_scale)) - half_size);
+ vec2 grid_domain = abs(mod(co + half_size, grid_scale) - half_size);
/* Modulate by the absolute rate of change of the coordinates
* (make line have the same width under perspective). */
grid_domain /= fwidthCos;
/* Collapse waves. */
float line_dist = min(grid_domain.x, grid_domain.y);
- return 1.0 - GRID_LINE_STEP(line_dist - grid_buf.line_size);
+ return GRID_LINE_STEP(line_dist - grid_buf.line_size);
}
vec3 get_axes(vec3 co, vec3 fwidthCos, float line_size)
@@ -39,7 +43,7 @@ vec3 get_axes(vec3 co, vec3 fwidthCos, float line_size)
/* Modulate by the absolute rate of change of the coordinates
* (make line have the same width under perspective). */
axes_domain /= fwidthCos;
- return 1.0 - GRID_LINE_STEP(axes_domain - (line_size + grid_buf.line_size));
+ return GRID_LINE_STEP(axes_domain - (line_size + grid_buf.line_size));
}
#define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0))
@@ -106,49 +110,30 @@ void main()
grid_res = grid_buf.zoom_factor;
}
- /* From biggest to smallest. */
- vec4 scale;
-#define grid_step(a) grid_buf.steps[a].x
-#if 0 /* Inefficient. */
- int step_id = 0;
- scale[0] = 0.0;
- scale[1] = grid_step(0);
- while (scale[1] < grid_res && step_id != STEPS_LEN - 1) {
- scale[0] = scale[1];
- scale[1] = grid_step(++step_id);
- }
- scale[2] = grid_step(min(step_id + 1, STEPS_LEN - 1));
- scale[3] = grid_step(min(step_id + 2, STEPS_LEN - 1));
-#else
- /* For more efficiency, unroll the loop above. */
- if (grid_step(0) > grid_res) {
- scale = vec4(0.0, grid_step(0), grid_step(1), grid_step(2));
- }
- else if (grid_step(1) > grid_res) {
- scale = vec4(grid_step(0), grid_step(1), grid_step(2), grid_step(3));
- }
- else if (grid_step(2) > grid_res) {
- scale = vec4(grid_step(1), grid_step(2), grid_step(3), grid_step(4));
- }
- else if (grid_step(3) > grid_res) {
- scale = vec4(grid_step(2), grid_step(3), grid_step(4), grid_step(5));
- }
- else if (grid_step(4) > grid_res) {
- scale = vec4(grid_step(3), grid_step(4), grid_step(5), grid_step(6));
- }
- else if (grid_step(5) > grid_res) {
- scale = vec4(grid_step(4), grid_step(5), grid_step(6), grid_step(7));
- }
- else if (grid_step(6) > grid_res) {
- scale = vec4(grid_step(5), grid_step(6), grid_step(7), grid_step(7));
- }
- else {
- scale = vec4(grid_step(6), grid_step(7), grid_step(7), grid_step(7));
+/** Keep in sync with `SI_GRID_STEPS_LEN` in `DNA_space_types.h`. */
+#define STEPS_LEN 8
+ int step_id_x = STEPS_LEN - 1;
+ int step_id_y = STEPS_LEN - 1;
+
+ /* Loop backwards a compile-time-constant number of steps. */
+ for (int i = STEPS_LEN - 2; i >= 0; --i) {
+ step_id_x = (grid_res < grid_buf.steps[i].x) ? i : step_id_x; /* Branchless. */
+ step_id_y = (grid_res < grid_buf.steps[i].y) ? i : step_id_y;
}
-#endif
-#undef grid_step
- float blend = 1.0 - linearstep(scale[0], scale[1], grid_res);
+ /* From biggest to smallest. */
+ float scale0x = step_id_x > 0 ? grid_buf.steps[step_id_x - 1].x : 0.0;
+ float scaleAx = grid_buf.steps[step_id_x].x;
+ float scaleBx = grid_buf.steps[min(step_id_x + 1, STEPS_LEN - 1)].x;
+ float scaleCx = grid_buf.steps[min(step_id_x + 2, STEPS_LEN - 1)].x;
+
+ float scale0y = step_id_y > 0 ? grid_buf.steps[step_id_y - 1].y : 0.0;
+ float scaleAy = grid_buf.steps[step_id_y].y;
+ float scaleBy = grid_buf.steps[min(step_id_y + 1, STEPS_LEN - 1)].y;
+ float scaleCy = grid_buf.steps[min(step_id_y + 2, STEPS_LEN - 1)].y;
+
+ /* Subtract from 1.0 to fix blending when `scale0x == scaleAx`. */
+ float blend = 1.0 - linearstep(scale0x + scale0y, scaleAx + scaleAy, grid_res + grid_res);
blend = blend * blend * blend;
vec2 grid_pos, grid_fwidth;
@@ -165,9 +150,9 @@ void main()
grid_fwidth = fwidthPos.xy;
}
- float gridA = get_grid(grid_pos, grid_fwidth, scale[1]);
- float gridB = get_grid(grid_pos, grid_fwidth, scale[2]);
- float gridC = get_grid(grid_pos, grid_fwidth, scale[3]);
+ float gridA = get_grid(grid_pos, grid_fwidth, vec2(scaleAx, scaleAy));
+ float gridB = get_grid(grid_pos, grid_fwidth, vec2(scaleBx, scaleBy));
+ float gridC = get_grid(grid_pos, grid_fwidth, vec2(scaleCx, scaleCy));
out_color = colorGrid;
out_color.a *= gridA * blend;
diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c
index 88ae5ac707e..026a1f52ac1 100644
--- a/source/blender/draw/engines/select/select_engine.c
+++ b/source/blender/draw/engines/select/select_engine.c
@@ -201,7 +201,7 @@ static void select_cache_populate(void *vedata, Object *ob)
if (!e_data.context.is_dirty && sel_data && sel_data->is_drawn) {
/* The object indices have already been drawn. Fill depth pass.
- * Opti: Most of the time this depth pass is not used. */
+ * Optimization: Most of the time this depth pass is not used. */
struct Mesh *me = ob->data;
if (e_data.context.select_mode & SCE_SELECT_FACE) {
struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(me);
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 9eb35c25bf4..a0459a967f3 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -409,7 +409,7 @@ void workbench_cache_populate(void *ved, Object *ob)
return;
}
- if (ELEM(ob->type, OB_MESH, OB_SURF, OB_MBALL, OB_POINTCLOUD)) {
+ if (ELEM(ob->type, OB_MESH, OB_POINTCLOUD)) {
bool use_sculpt_pbvh, use_texpaint_mode, draw_shadow, has_transp_mat = false;
eV3DShadingColorType color_type = workbench_color_type_get(
wpd, ob, &use_sculpt_pbvh, &use_texpaint_mode, &draw_shadow);
diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh
index 8e61c25be71..d53c5fbeaa5 100644
--- a/source/blender/draw/intern/DRW_gpu_wrapper.hh
+++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh
@@ -31,6 +31,10 @@
* discarding all data inside it.
* Data can be accessed using the [] operator.
*
+ * `draw::StorageVectorBuffer<T, len>`
+ * Same as `StorageArrayBuffer` but has a length counter and act like a `blender::Vector` you can
+ * clear and append to.
+ *
* `draw::StorageBuffer<T>`
* A storage buffer object class inheriting from T.
* Data can be accessed just like a normal T object.
@@ -50,13 +54,13 @@
*
* `draw::Framebuffer`
* Simple wrapper to #GPUFramebuffer that can be moved.
- *
*/
#include "DRW_render.h"
#include "MEM_guardedalloc.h"
+#include "draw_manager.h"
#include "draw_texture_pool.h"
#include "BLI_math_vec_types.hh"
@@ -182,7 +186,7 @@ class UniformCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable
GPU_uniformbuf_free(ubo_);
}
- void push_update(void)
+ void push_update()
{
GPU_uniformbuf_update(ubo_, this->data_);
}
@@ -227,12 +231,22 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable
GPU_storagebuf_free(ssbo_);
}
- void push_update(void)
+ void push_update()
{
BLI_assert(device_only == false);
GPU_storagebuf_update(ssbo_, this->data_);
}
+ void clear_to_zero()
+ {
+ GPU_storagebuf_clear_to_zero(ssbo_);
+ }
+
+ void read()
+ {
+ GPU_storagebuf_read(ssbo_, this->data_);
+ }
+
operator GPUStorageBuf *() const
{
return ssbo_;
@@ -303,8 +317,8 @@ class UniformBuffer : public T, public detail::UniformCommon<T, 1, false> {
template<
/** Type of the values stored in this uniform buffer. */
typename T,
- /** The number of values that can be stored in this uniform buffer. */
- int64_t len,
+ /** The number of values that can be stored in this storage buffer at creation. */
+ int64_t len = 16u / sizeof(T),
/** True if created on device and no memory host memory is allocated. */
bool device_only = false>
class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> {
@@ -319,6 +333,7 @@ class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> {
MEM_freeN(this->data_);
}
+ /* Resize to \a new_size elements. */
void resize(int64_t new_size)
{
BLI_assert(new_size > 0);
@@ -346,6 +361,71 @@ class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> {
}
return this->data_[index];
}
+
+ int64_t size() const
+ {
+ return this->len_;
+ }
+};
+
+template<
+ /** Type of the values stored in this uniform buffer. */
+ typename T,
+ /** The number of values that can be stored in this storage buffer at creation. */
+ int64_t len = 16u / sizeof(T)>
+class StorageVectorBuffer : public StorageArrayBuffer<T, len, false> {
+ private:
+ /* Number of items, not the allocated length. */
+ int64_t item_len_ = 0;
+
+ public:
+ StorageVectorBuffer(const char *name = nullptr) : StorageArrayBuffer<T, len, false>(name){};
+ ~StorageVectorBuffer(){};
+
+ /**
+ * Set item count to zero but does not free memory or resize the buffer.
+ */
+ void clear()
+ {
+ item_len_ = 0;
+ }
+
+ /**
+ * Insert a new element at the end of the vector.
+ * This might cause a reallocation with the capacity is exceeded.
+ *
+ * This is similar to std::vector::push_back.
+ */
+ void append(const T &value)
+ {
+ this->append_as(value);
+ }
+ void append(T &&value)
+ {
+ this->append_as(std::move(value));
+ }
+ template<typename... ForwardT> void append_as(ForwardT &&...value)
+ {
+ if (item_len_ >= this->len_) {
+ size_t size = power_of_2_max_u(item_len_ + 1);
+ this->resize(size);
+ }
+ T *ptr = &this->data_[item_len_++];
+ new (ptr) T(std::forward<ForwardT>(value)...);
+ }
+
+ int64_t size() const
+ {
+ return item_len_;
+ }
+
+ bool is_empty() const
+ {
+ return this->size() == 0;
+ }
+
+ /* Avoid confusion with the other clear. */
+ void clear_to_zero() = delete;
};
template<
@@ -392,10 +472,10 @@ class Texture : NonCopyable {
int extent,
float *data = nullptr,
bool cubemap = false,
- int mips = 1)
+ int mip_len = 1)
: name_(name)
{
- tx_ = create(extent, 0, 0, mips, format, data, false, cubemap);
+ tx_ = create(extent, 0, 0, mip_len, format, data, false, cubemap);
}
Texture(const char *name,
@@ -404,17 +484,20 @@ class Texture : NonCopyable {
int layers,
float *data = nullptr,
bool cubemap = false,
- int mips = 1)
+ int mip_len = 1)
: name_(name)
{
- tx_ = create(extent, layers, 0, mips, format, data, true, cubemap);
+ tx_ = create(extent, layers, 0, mip_len, format, data, true, cubemap);
}
- Texture(
- const char *name, eGPUTextureFormat format, int2 extent, float *data = nullptr, int mips = 1)
+ Texture(const char *name,
+ eGPUTextureFormat format,
+ int2 extent,
+ float *data = nullptr,
+ int mip_len = 1)
: name_(name)
{
- tx_ = create(UNPACK2(extent), 0, mips, format, data, false, false);
+ tx_ = create(UNPACK2(extent), 0, mip_len, format, data, false, false);
}
Texture(const char *name,
@@ -422,17 +505,20 @@ class Texture : NonCopyable {
int2 extent,
int layers,
float *data = nullptr,
- int mips = 1)
+ int mip_len = 1)
: name_(name)
{
- tx_ = create(UNPACK2(extent), layers, mips, format, data, true, false);
+ tx_ = create(UNPACK2(extent), layers, mip_len, format, data, true, false);
}
- Texture(
- const char *name, eGPUTextureFormat format, int3 extent, float *data = nullptr, int mips = 1)
+ Texture(const char *name,
+ eGPUTextureFormat format,
+ int3 extent,
+ float *data = nullptr,
+ int mip_len = 1)
: name_(name)
{
- tx_ = create(UNPACK3(extent), mips, format, data, false, false);
+ tx_ = create(UNPACK3(extent), mip_len, format, data, false, false);
}
~Texture()
@@ -467,9 +553,9 @@ class Texture : NonCopyable {
* Ensure the texture has the correct properties. Recreating it if needed.
* Return true if a texture has been created.
*/
- bool ensure_1d(eGPUTextureFormat format, int extent, float *data = nullptr, int mips = 1)
+ bool ensure_1d(eGPUTextureFormat format, int extent, float *data = nullptr, int mip_len = 1)
{
- return ensure_impl(extent, 0, 0, mips, format, data, false, false);
+ return ensure_impl(extent, 0, 0, mip_len, format, data, false, false);
}
/**
@@ -477,18 +563,18 @@ class Texture : NonCopyable {
* Return true if a texture has been created.
*/
bool ensure_1d_array(
- eGPUTextureFormat format, int extent, int layers, float *data = nullptr, int mips = 1)
+ eGPUTextureFormat format, int extent, int layers, float *data = nullptr, int mip_len = 1)
{
- return ensure_impl(extent, layers, 0, mips, format, data, true, false);
+ return ensure_impl(extent, layers, 0, mip_len, format, data, true, false);
}
/**
* Ensure the texture has the correct properties. Recreating it if needed.
* Return true if a texture has been created.
*/
- bool ensure_2d(eGPUTextureFormat format, int2 extent, float *data = nullptr, int mips = 1)
+ bool ensure_2d(eGPUTextureFormat format, int2 extent, float *data = nullptr, int mip_len = 1)
{
- return ensure_impl(UNPACK2(extent), 0, mips, format, data, false, false);
+ return ensure_impl(UNPACK2(extent), 0, mip_len, format, data, false, false);
}
/**
@@ -496,27 +582,27 @@ class Texture : NonCopyable {
* Return true if a texture has been created.
*/
bool ensure_2d_array(
- eGPUTextureFormat format, int2 extent, int layers, float *data = nullptr, int mips = 1)
+ eGPUTextureFormat format, int2 extent, int layers, float *data = nullptr, int mip_len = 1)
{
- return ensure_impl(UNPACK2(extent), layers, mips, format, data, true, false);
+ return ensure_impl(UNPACK2(extent), layers, mip_len, format, data, true, false);
}
/**
* Ensure the texture has the correct properties. Recreating it if needed.
* Return true if a texture has been created.
*/
- bool ensure_3d(eGPUTextureFormat format, int3 extent, float *data = nullptr, int mips = 1)
+ bool ensure_3d(eGPUTextureFormat format, int3 extent, float *data = nullptr, int mip_len = 1)
{
- return ensure_impl(UNPACK3(extent), mips, format, data, false, false);
+ return ensure_impl(UNPACK3(extent), mip_len, format, data, false, false);
}
/**
* Ensure the texture has the correct properties. Recreating it if needed.
* Return true if a texture has been created.
*/
- bool ensure_cube(eGPUTextureFormat format, int extent, float *data = nullptr, int mips = 1)
+ bool ensure_cube(eGPUTextureFormat format, int extent, float *data = nullptr, int mip_len = 1)
{
- return ensure_impl(extent, extent, 0, mips, format, data, false, true);
+ return ensure_impl(extent, extent, 0, mip_len, format, data, false, true);
}
/**
@@ -524,9 +610,9 @@ class Texture : NonCopyable {
* Return true if a texture has been created.
*/
bool ensure_cube_array(
- eGPUTextureFormat format, int extent, int layers, float *data = nullptr, int mips = 1)
+ eGPUTextureFormat format, int extent, int layers, float *data = nullptr, int mip_len = 1)
{
- return ensure_impl(extent, extent, layers, mips, format, data, false, true);
+ return ensure_impl(extent, extent, layers, mip_len, format, data, false, true);
}
/**
@@ -555,6 +641,11 @@ class Texture : NonCopyable {
return mip_views_[miplvl];
}
+ int mip_count() const
+ {
+ return GPU_texture_mip_count(tx_);
+ }
+
/**
* Ensure the availability of mipmap views.
* Layer views covers all layers of array textures.
@@ -595,42 +686,47 @@ class Texture : NonCopyable {
/**
* Returns true if the texture has been allocated or acquired from the pool.
*/
- bool is_valid(void) const
+ bool is_valid() const
{
return tx_ != nullptr;
}
- int width(void) const
+ int width() const
{
return GPU_texture_width(tx_);
}
- int height(void) const
+ int height() const
{
return GPU_texture_height(tx_);
}
- bool depth(void) const
+ int pixel_count() const
+ {
+ return GPU_texture_width(tx_) * GPU_texture_height(tx_);
+ }
+
+ bool depth() const
{
return GPU_texture_depth(tx_);
}
- bool is_stencil(void) const
+ bool is_stencil() const
{
return GPU_texture_stencil(tx_);
}
- bool is_integer(void) const
+ bool is_integer() const
{
return GPU_texture_integer(tx_);
}
- bool is_cube(void) const
+ bool is_cube() const
{
return GPU_texture_cube(tx_);
}
- bool is_array(void) const
+ bool is_array() const
{
return GPU_texture_array(tx_);
}
@@ -709,7 +805,7 @@ class Texture : NonCopyable {
bool ensure_impl(int w,
int h = 0,
int d = 0,
- int mips = 1,
+ int mip_len = 1,
eGPUTextureFormat format = GPU_RGBA8,
float *data = nullptr,
bool layered = false,
@@ -722,11 +818,11 @@ class Texture : NonCopyable {
int3 size = this->size();
if (size != int3(w, h, d) || GPU_texture_format(tx_) != format ||
GPU_texture_cube(tx_) != cubemap || GPU_texture_array(tx_) != layered) {
- GPU_TEXTURE_FREE_SAFE(tx_);
+ free();
}
}
if (tx_ == nullptr) {
- tx_ = create(w, h, d, mips, format, data, layered, cubemap);
+ tx_ = create(w, h, d, mip_len, format, data, layered, cubemap);
return true;
}
return false;
@@ -735,87 +831,82 @@ class Texture : NonCopyable {
GPUTexture *create(int w,
int h,
int d,
- int mips,
+ int mip_len,
eGPUTextureFormat format,
float *data,
bool layered,
bool cubemap)
{
if (h == 0) {
- return GPU_texture_create_1d(name_, w, mips, format, data);
+ return GPU_texture_create_1d(name_, w, mip_len, format, data);
}
else if (cubemap) {
if (layered) {
- return GPU_texture_create_cube_array(name_, w, d, mips, format, data);
+ return GPU_texture_create_cube_array(name_, w, d, mip_len, format, data);
}
else {
- return GPU_texture_create_cube(name_, w, mips, format, data);
+ return GPU_texture_create_cube(name_, w, mip_len, format, data);
}
}
else if (d == 0) {
if (layered) {
- return GPU_texture_create_1d_array(name_, w, h, mips, format, data);
+ return GPU_texture_create_1d_array(name_, w, h, mip_len, format, data);
}
else {
- return GPU_texture_create_2d(name_, w, h, mips, format, data);
+ return GPU_texture_create_2d(name_, w, h, mip_len, format, data);
}
}
else {
if (layered) {
- return GPU_texture_create_2d_array(name_, w, h, d, mips, format, data);
+ return GPU_texture_create_2d_array(name_, w, h, d, mip_len, format, data);
}
else {
- return GPU_texture_create_3d(name_, w, h, d, mips, format, GPU_DATA_FLOAT, data);
+ return GPU_texture_create_3d(name_, w, h, d, mip_len, format, GPU_DATA_FLOAT, data);
}
}
}
};
class TextureFromPool : public Texture, NonMovable {
- private:
- GPUTexture *tx_tmp_saved_ = nullptr;
-
public:
TextureFromPool(const char *name = "gpu::Texture") : Texture(name){};
- /* Always use `release()` after rendering and `sync()` in sync phase. */
- void acquire(int2 extent, eGPUTextureFormat format, void *owner_)
+ /* Always use `release()` after rendering. */
+ void acquire(int2 extent, eGPUTextureFormat format)
{
BLI_assert(this->tx_ == nullptr);
- if (this->tx_ != nullptr) {
- return;
- }
- if (tx_tmp_saved_ != nullptr) {
- if (GPU_texture_width(tx_tmp_saved_) != extent.x ||
- GPU_texture_height(tx_tmp_saved_) != extent.y ||
- GPU_texture_format(tx_tmp_saved_) != format) {
- this->tx_tmp_saved_ = nullptr;
- }
- else {
- this->tx_ = tx_tmp_saved_;
- return;
- }
- }
- DrawEngineType *owner = (DrawEngineType *)owner_;
- this->tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), format, owner);
+
+ this->tx_ = DRW_texture_pool_texture_acquire(
+ DST.vmempool->texture_pool, UNPACK2(extent), format);
}
- void release(void)
+ void release()
{
/* Allows multiple release. */
- if (this->tx_ != nullptr) {
- tx_tmp_saved_ = this->tx_;
- this->tx_ = nullptr;
+ if (this->tx_ == nullptr) {
+ return;
}
+ DRW_texture_pool_texture_release(DST.vmempool->texture_pool, this->tx_);
+ this->tx_ = nullptr;
}
/**
- * Clears any reference. Workaround for pool texture not being able to release on demand.
- * Needs to be called at during the sync phase.
+ * Swap the content of the two textures.
+ * Also change ownership accordingly if needed.
*/
- void sync(void)
+ static void swap(TextureFromPool &a, Texture &b)
+ {
+ Texture::swap(a, b);
+ DRW_texture_pool_give_texture_ownership(DST.vmempool->texture_pool, a);
+ DRW_texture_pool_take_texture_ownership(DST.vmempool->texture_pool, b);
+ }
+ static void swap(Texture &a, TextureFromPool &b)
{
- tx_tmp_saved_ = nullptr;
+ swap(b, a);
+ }
+ static void swap(TextureFromPool &a, TextureFromPool &b)
+ {
+ Texture::swap(a, b);
}
/** Remove methods that are forbidden with this type of textures. */
@@ -833,6 +924,33 @@ class TextureFromPool : public Texture, NonMovable {
GPUTexture *stencil_view() = delete;
};
+/**
+ * Dummy type to bind texture as image.
+ * It is just a GPUTexture in disguise.
+ */
+class Image {
+};
+
+static inline Image *as_image(GPUTexture *tex)
+{
+ return reinterpret_cast<Image *>(tex);
+}
+
+static inline Image **as_image(GPUTexture **tex)
+{
+ return reinterpret_cast<Image **>(tex);
+}
+
+static inline GPUTexture *as_texture(Image *img)
+{
+ return reinterpret_cast<GPUTexture *>(img);
+}
+
+static inline GPUTexture **as_texture(Image **img)
+{
+ return reinterpret_cast<GPUTexture **>(img);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -902,45 +1020,47 @@ class Framebuffer : NonCopyable {
template<typename T, int64_t len> class SwapChain {
private:
+ BLI_STATIC_ASSERT(len > 1, "A swap-chain needs more than 1 unit in length.");
std::array<T, len> chain_;
- int64_t index_ = 0;
public:
void swap()
{
- index_ = (index_ + 1) % len;
+ for (auto i : IndexRange(len - 1)) {
+ T::swap(chain_[i], chain_[(i + 1) % len]);
+ }
}
T &current()
{
- return chain_[index_];
+ return chain_[0];
}
T &previous()
{
/* Avoid modulo operation with negative numbers. */
- return chain_[(index_ + len - 1) % len];
+ return chain_[(0 + len - 1) % len];
}
T &next()
{
- return chain_[(index_ + 1) % len];
+ return chain_[(0 + 1) % len];
}
const T &current() const
{
- return chain_[index_];
+ return chain_[0];
}
const T &previous() const
{
/* Avoid modulo operation with negative numbers. */
- return chain_[(index_ + len - 1) % len];
+ return chain_[(0 + len - 1) % len];
}
const T &next() const
{
- return chain_[(index_ + 1) % len];
+ return chain_[(0 + 1) % len];
}
};
diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h
index bb913c53500..7b80ffd2b88 100644
--- a/source/blender/draw/intern/DRW_render.h
+++ b/source/blender/draw/intern/DRW_render.h
@@ -41,6 +41,7 @@
#include "draw_debug.h"
#include "draw_manager_profiling.h"
+#include "draw_state.h"
#include "draw_view_data.h"
#include "MEM_guardedalloc.h"
@@ -288,83 +289,6 @@ void DRW_shader_library_free(DRWShaderLibrary *lib);
/* Batches */
-/**
- * DRWState is a bit-mask that stores the current render state and the desired render state. Based
- * on the differences the minimum state changes can be invoked to setup the desired render state.
- *
- * The Write Stencil, Stencil test, Depth test and Blend state options are mutual exclusive
- * therefore they aren't ordered as a bit mask.
- */
-typedef enum {
- /** To be used for compute passes. */
- DRW_STATE_NO_DRAW = 0,
- /** Write mask */
- DRW_STATE_WRITE_DEPTH = (1 << 0),
- DRW_STATE_WRITE_COLOR = (1 << 1),
- /* Write Stencil. These options are mutual exclusive and packed into 2 bits */
- DRW_STATE_WRITE_STENCIL = (1 << 2),
- DRW_STATE_WRITE_STENCIL_SHADOW_PASS = (2 << 2),
- DRW_STATE_WRITE_STENCIL_SHADOW_FAIL = (3 << 2),
- /** Depth test. These options are mutual exclusive and packed into 3 bits */
- DRW_STATE_DEPTH_ALWAYS = (1 << 4),
- DRW_STATE_DEPTH_LESS = (2 << 4),
- DRW_STATE_DEPTH_LESS_EQUAL = (3 << 4),
- DRW_STATE_DEPTH_EQUAL = (4 << 4),
- DRW_STATE_DEPTH_GREATER = (5 << 4),
- DRW_STATE_DEPTH_GREATER_EQUAL = (6 << 4),
- /** Culling test */
- DRW_STATE_CULL_BACK = (1 << 7),
- DRW_STATE_CULL_FRONT = (1 << 8),
- /** Stencil test. These options are mutually exclusive and packed into 2 bits. */
- DRW_STATE_STENCIL_ALWAYS = (1 << 9),
- DRW_STATE_STENCIL_EQUAL = (2 << 9),
- DRW_STATE_STENCIL_NEQUAL = (3 << 9),
-
- /** Blend state. These options are mutual exclusive and packed into 4 bits */
- DRW_STATE_BLEND_ADD = (1 << 11),
- /** Same as additive but let alpha accumulate without pre-multiply. */
- DRW_STATE_BLEND_ADD_FULL = (2 << 11),
- /** Standard alpha blending. */
- DRW_STATE_BLEND_ALPHA = (3 << 11),
- /** Use that if color is already pre-multiply by alpha. */
- DRW_STATE_BLEND_ALPHA_PREMUL = (4 << 11),
- DRW_STATE_BLEND_BACKGROUND = (5 << 11),
- DRW_STATE_BLEND_OIT = (6 << 11),
- DRW_STATE_BLEND_MUL = (7 << 11),
- DRW_STATE_BLEND_SUB = (8 << 11),
- /** Use dual source blending. WARNING: Only one color buffer allowed. */
- DRW_STATE_BLEND_CUSTOM = (9 << 11),
- DRW_STATE_LOGIC_INVERT = (10 << 11),
- DRW_STATE_BLEND_ALPHA_UNDER_PREMUL = (11 << 11),
-
- DRW_STATE_IN_FRONT_SELECT = (1 << 27),
- DRW_STATE_SHADOW_OFFSET = (1 << 28),
- DRW_STATE_CLIP_PLANES = (1 << 29),
- DRW_STATE_FIRST_VERTEX_CONVENTION = (1 << 30),
- /** DO NOT USE. Assumed always enabled. Only used internally. */
- DRW_STATE_PROGRAM_POINT_SIZE = (1u << 31),
-} DRWState;
-
-ENUM_OPERATORS(DRWState, DRW_STATE_PROGRAM_POINT_SIZE);
-
-#define DRW_STATE_DEFAULT \
- (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL)
-#define DRW_STATE_BLEND_ENABLED \
- (DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_ALPHA | \
- DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_OIT | \
- DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_SUB | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT)
-#define DRW_STATE_RASTERIZER_ENABLED \
- (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL | \
- DRW_STATE_WRITE_STENCIL_SHADOW_PASS | DRW_STATE_WRITE_STENCIL_SHADOW_FAIL)
-#define DRW_STATE_DEPTH_TEST_ENABLED \
- (DRW_STATE_DEPTH_ALWAYS | DRW_STATE_DEPTH_LESS | DRW_STATE_DEPTH_LESS_EQUAL | \
- DRW_STATE_DEPTH_EQUAL | DRW_STATE_DEPTH_GREATER | DRW_STATE_DEPTH_GREATER_EQUAL)
-#define DRW_STATE_STENCIL_TEST_ENABLED \
- (DRW_STATE_STENCIL_ALWAYS | DRW_STATE_STENCIL_EQUAL | DRW_STATE_STENCIL_NEQUAL)
-#define DRW_STATE_WRITE_STENCIL_ENABLED \
- (DRW_STATE_WRITE_STENCIL | DRW_STATE_WRITE_STENCIL_SHADOW_PASS | \
- DRW_STATE_WRITE_STENCIL_SHADOW_FAIL)
-
typedef enum {
DRW_ATTR_INT,
DRW_ATTR_FLOAT,
@@ -409,7 +333,7 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup,
void *user_data);
/**
- * If ob is NULL, unit modelmatrix is assumed and culling is bypassed.
+ * If ob is NULL, unit model-matrix is assumed and culling is bypassed.
*/
#define DRW_shgroup_call(shgroup, geom, ob) \
DRW_shgroup_call_ex(shgroup, ob, NULL, geom, false, NULL)
@@ -420,8 +344,8 @@ void DRW_shgroup_call_ex(DRWShadingGroup *shgroup,
#define DRW_shgroup_call_obmat(shgroup, geom, obmat) \
DRW_shgroup_call_ex(shgroup, NULL, obmat, geom, false, NULL)
-/* TODO(fclem): remove this when we have DRWView */
-/* user_data is used by DRWCallVisibilityFn defined in DRWView. */
+/* TODO(fclem): remove this when we have #DRWView */
+/* user_data is used by #DRWCallVisibilityFn defined in #DRWView. */
#define DRW_shgroup_call_with_callback(shgroup, geom, ob, user_data) \
DRW_shgroup_call_ex(shgroup, ob, NULL, geom, false, user_data)
@@ -454,6 +378,10 @@ void DRW_shgroup_call_compute_indirect(DRWShadingGroup *shgroup, GPUStorageBuf *
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);
+void DRW_shgroup_call_procedural_indirect(DRWShadingGroup *shgroup,
+ GPUPrimType primitive_type,
+ Object *ob,
+ GPUStorageBuf *indirect_buf);
/**
* \warning Only use with Shaders that have `IN_PLACE_INSTANCES` defined.
* TODO: Should be removed.
@@ -791,7 +719,7 @@ bool DRW_culling_box_test(const DRWView *view, const BoundBox *bbox);
bool DRW_culling_plane_test(const DRWView *view, const float plane[4]);
/**
* Return True if the given box intersect the current view frustum.
- * This function will have to be replaced when world space bb per objects is implemented.
+ * This function will have to be replaced when world space bounding-box per objects is implemented.
*/
bool DRW_culling_min_max_test(const DRWView *view, float obmat[4][4], float min[3], float max[3]);
@@ -887,7 +815,6 @@ bool DRW_object_is_in_edit_mode(const struct Object *ob);
* we are rendering or drawing in the viewport.
*/
int DRW_object_visibility_in_active_context(const struct Object *ob);
-bool DRW_object_is_flat_normal(const struct Object *ob);
bool DRW_object_use_hide_faces(const struct Object *ob);
bool DRW_object_is_visible_psys_in_active_context(const struct Object *object,
@@ -981,7 +908,7 @@ typedef struct DRWContextState {
struct ViewLayer *view_layer; /* 'CTX_data_view_layer(C)' */
/* Use 'object_edit' for edit-mode */
- struct Object *obact; /* 'OBACT' */
+ struct Object *obact;
struct RenderEngineType *engine_type;
diff --git a/source/blender/draw/intern/draw_attributes.cc b/source/blender/draw/intern/draw_attributes.cc
index 3f187aef8e6..011d72e9e8f 100644
--- a/source/blender/draw/intern/draw_attributes.cc
+++ b/source/blender/draw/intern/draw_attributes.cc
@@ -88,7 +88,7 @@ bool drw_custom_data_match_attribute(const CustomData *custom_data,
int *r_layer_index,
eCustomDataType *r_type)
{
- const eCustomDataType possible_attribute_types[7] = {
+ const eCustomDataType possible_attribute_types[8] = {
CD_PROP_BOOL,
CD_PROP_INT8,
CD_PROP_INT32,
@@ -96,6 +96,7 @@ bool drw_custom_data_match_attribute(const CustomData *custom_data,
CD_PROP_FLOAT2,
CD_PROP_FLOAT3,
CD_PROP_COLOR,
+ CD_PROP_BYTE_COLOR,
};
for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) {
diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c
index f846251c66b..6537490c06c 100644
--- a/source/blender/draw/intern/draw_cache.c
+++ b/source/blender/draw/intern/draw_cache.c
@@ -90,6 +90,7 @@ static struct DRWShapeCache {
GPUBatch *drw_procedural_verts;
GPUBatch *drw_procedural_lines;
GPUBatch *drw_procedural_tris;
+ GPUBatch *drw_procedural_tri_strips;
GPUBatch *drw_cursor;
GPUBatch *drw_cursor_only_circle;
GPUBatch *drw_fullscreen_quad;
@@ -208,6 +209,21 @@ GPUBatch *drw_cache_procedural_triangles_get(void)
return SHC.drw_procedural_tris;
}
+GPUBatch *drw_cache_procedural_triangle_strips_get()
+{
+ if (!SHC.drw_procedural_tri_strips) {
+ /* TODO(fclem): get rid of this dummy VBO. */
+ GPUVertFormat format = {0};
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, 1);
+
+ SHC.drw_procedural_tri_strips = GPU_batch_create_ex(
+ GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
+ }
+ return SHC.drw_procedural_tri_strips;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -764,6 +780,39 @@ GPUBatch *DRW_cache_normal_arrow_get(void)
return SHC.drw_normal_arrow;
}
+void DRW_vertbuf_create_wiredata(GPUVertBuf *vbo, const int vert_len)
+{
+ static GPUVertFormat format = {0};
+ static struct {
+ uint wd;
+ } attr_id;
+ if (format.attr_len == 0) {
+ /* initialize vertex format */
+ if (!GPU_crappy_amd_driver()) {
+ /* Some AMD drivers strangely crash with a vbo with this format. */
+ attr_id.wd = GPU_vertformat_attr_add(
+ &format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+ else {
+ attr_id.wd = GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
+ }
+ }
+
+ GPU_vertbuf_init_with_format(vbo, &format);
+ GPU_vertbuf_data_alloc(vbo, vert_len);
+
+ if (GPU_vertbuf_get_format(vbo)->stride == 1) {
+ memset(GPU_vertbuf_get_data(vbo), 0xFF, (size_t)vert_len);
+ }
+ else {
+ GPUVertBufRaw wd_step;
+ GPU_vertbuf_attr_get_raw_data(vbo, attr_id.wd, &wd_step);
+ for (int i = 0; i < vert_len; i++) {
+ *((float *)GPU_vertbuf_raw_step(&wd_step)) = 1.0f;
+ }
+ }
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -777,7 +826,8 @@ GPUBatch *DRW_gpencil_dummy_buffer_get(void)
{
if (SHC.drw_gpencil_dummy_quad == NULL) {
GPUVertFormat format = {0};
- GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT);
+ /* NOTE: Use GPU_COMP_U32 to satisfy minimum 4-byte vertex stride for Metal backend. */
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U32, 1, GPU_FETCH_INT);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, 4);
@@ -802,7 +852,6 @@ GPUBatch *DRW_cache_object_all_edges_get(Object *ob)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_all_edges_get(ob);
-
/* TODO: should match #DRW_cache_object_surface_get. */
default:
return NULL;
@@ -814,20 +863,6 @@ GPUBatch *DRW_cache_object_edge_detection_get(Object *ob, bool *r_is_manifold)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_edge_detection_get(ob, r_is_manifold);
- case OB_CURVES_LEGACY:
- return NULL;
- case OB_SURF:
- return NULL;
- case OB_FONT:
- return NULL;
- case OB_MBALL:
- return DRW_cache_mball_edge_detection_get(ob, r_is_manifold);
- case OB_CURVES:
- return NULL;
- case OB_POINTCLOUD:
- return NULL;
- case OB_VOLUME:
- return NULL;
default:
return NULL;
}
@@ -838,23 +873,12 @@ GPUBatch *DRW_cache_object_face_wireframe_get(Object *ob)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_face_wireframe_get(ob);
- case OB_CURVES_LEGACY:
- return NULL;
- case OB_SURF:
- return NULL;
- case OB_FONT:
- return NULL;
- case OB_MBALL:
- return DRW_cache_mball_face_wireframe_get(ob);
- case OB_CURVES:
- return NULL;
case OB_POINTCLOUD:
return DRW_pointcloud_batch_cache_get_dots(ob);
case OB_VOLUME:
return DRW_cache_volume_face_wireframe_get(ob);
- case OB_GPENCIL: {
+ case OB_GPENCIL:
return DRW_cache_gpencil_face_wireframe_get(ob);
- }
default:
return NULL;
}
@@ -865,20 +889,6 @@ GPUBatch *DRW_cache_object_loose_edges_get(struct Object *ob)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_loose_edges_get(ob);
- case OB_CURVES_LEGACY:
- return NULL;
- case OB_SURF:
- return NULL;
- case OB_FONT:
- return NULL;
- case OB_MBALL:
- return NULL;
- case OB_CURVES:
- return NULL;
- case OB_POINTCLOUD:
- return NULL;
- case OB_VOLUME:
- return NULL;
default:
return NULL;
}
@@ -889,20 +899,8 @@ GPUBatch *DRW_cache_object_surface_get(Object *ob)
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_surface_get(ob);
- case OB_CURVES_LEGACY:
- return NULL;
- case OB_SURF:
- return NULL;
- case OB_FONT:
- return NULL;
- case OB_MBALL:
- return DRW_cache_mball_surface_get(ob);
- case OB_CURVES:
- return NULL;
case OB_POINTCLOUD:
return DRW_cache_pointcloud_surface_get(ob);
- case OB_VOLUME:
- return NULL;
default:
return NULL;
}
@@ -916,18 +914,6 @@ GPUVertBuf *DRW_cache_object_pos_vertbuf_get(Object *ob)
switch (type) {
case OB_MESH:
return DRW_mesh_batch_cache_pos_vertbuf_get((me != NULL) ? me : ob->data);
- case OB_CURVES_LEGACY:
- case OB_SURF:
- case OB_FONT:
- return NULL;
- case OB_MBALL:
- return DRW_mball_batch_cache_pos_vertbuf_get(ob);
- case OB_CURVES:
- return NULL;
- case OB_POINTCLOUD:
- return NULL;
- case OB_VOLUME:
- return NULL;
default:
return NULL;
}
@@ -952,8 +938,6 @@ int DRW_cache_object_material_count_get(struct Object *ob)
case OB_SURF:
case OB_FONT:
return DRW_curve_material_count_get(ob->data);
- case OB_MBALL:
- return DRW_metaball_material_count_get(ob->data);
case OB_CURVES:
return DRW_curves_material_count_get(ob->data);
case OB_POINTCLOUD:
@@ -975,20 +959,8 @@ GPUBatch **DRW_cache_object_surface_material_get(struct Object *ob,
switch (ob->type) {
case OB_MESH:
return DRW_cache_mesh_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
- case OB_CURVES_LEGACY:
- return NULL;
- case OB_SURF:
- return NULL;
- case OB_FONT:
- return NULL;
- case OB_MBALL:
- return DRW_cache_mball_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
- case OB_CURVES:
- return NULL;
case OB_POINTCLOUD:
return DRW_cache_pointcloud_surface_shaded_get(ob, gpumat_array, gpumat_array_len);
- case OB_VOLUME:
- return NULL;
default:
return NULL;
}
@@ -2956,39 +2928,6 @@ GPUBatch *DRW_cache_curve_vert_overlay_get(Object *ob)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name MetaBall
- * \{ */
-
-GPUBatch *DRW_cache_mball_surface_get(Object *ob)
-{
- BLI_assert(ob->type == OB_MBALL);
- return DRW_metaball_batch_cache_get_triangles_with_normals(ob);
-}
-
-GPUBatch *DRW_cache_mball_edge_detection_get(Object *ob, bool *r_is_manifold)
-{
- BLI_assert(ob->type == OB_MBALL);
- return DRW_metaball_batch_cache_get_edge_detection(ob, r_is_manifold);
-}
-
-GPUBatch *DRW_cache_mball_face_wireframe_get(Object *ob)
-{
- BLI_assert(ob->type == OB_MBALL);
- return DRW_metaball_batch_cache_get_wireframes_face(ob);
-}
-
-GPUBatch **DRW_cache_mball_surface_shaded_get(Object *ob,
- struct GPUMaterial **gpumat_array,
- uint gpumat_array_len)
-{
- BLI_assert(ob->type == OB_MBALL);
- MetaBall *mb = ob->data;
- return DRW_metaball_batch_cache_get_surface_shaded(ob, mb, gpumat_array, gpumat_array_len);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Font
* \{ */
@@ -3306,9 +3245,6 @@ void drw_batch_cache_validate(Object *ob)
case OB_SURF:
DRW_curve_batch_cache_validate((Curve *)ob->data);
break;
- case OB_MBALL:
- DRW_mball_batch_cache_validate((MetaBall *)ob->data);
- break;
case OB_LATTICE:
DRW_lattice_batch_cache_validate((Lattice *)ob->data);
break;
diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h
index a107eb7c75c..4e8788ada08 100644
--- a/source/blender/draw/intern/draw_cache.h
+++ b/source/blender/draw/intern/draw_cache.h
@@ -213,15 +213,6 @@ struct GPUBatch *DRW_cache_particles_get_edit_tip_points(struct Object *object,
struct PTCacheEdit *edit);
struct GPUBatch *DRW_cache_particles_get_prim(int type);
-/* Metaball */
-
-struct GPUBatch *DRW_cache_mball_surface_get(struct Object *ob);
-struct GPUBatch **DRW_cache_mball_surface_shaded_get(struct Object *ob,
- struct GPUMaterial **gpumat_array,
- uint gpumat_array_len);
-struct GPUBatch *DRW_cache_mball_face_wireframe_get(struct Object *ob);
-struct GPUBatch *DRW_cache_mball_edge_detection_get(struct Object *ob, bool *r_is_manifold);
-
/* Curves */
struct GPUBatch *DRW_cache_curves_surface_get(struct Object *ob);
diff --git a/source/blender/draw/intern/draw_cache_extract.hh b/source/blender/draw/intern/draw_cache_extract.hh
index c7127d169e1..203da22406c 100644
--- a/source/blender/draw/intern/draw_cache_extract.hh
+++ b/source/blender/draw/intern/draw_cache_extract.hh
@@ -55,7 +55,6 @@ enum {
struct DRW_MeshCDMask {
uint32_t uv : 8;
uint32_t tan : 8;
- uint32_t vcol : 8;
uint32_t orco : 1;
uint32_t tan_orco : 1;
uint32_t sculpt_overlays : 1;
@@ -111,7 +110,6 @@ struct MeshBufferList {
GPUVertBuf *weights; /* extend */
GPUVertBuf *uv;
GPUVertBuf *tan;
- GPUVertBuf *vcol;
GPUVertBuf *sculpt_data;
GPUVertBuf *orco;
/* Only for edit mode. */
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc
index 380736ef656..b1d1631cb6d 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc
@@ -619,7 +619,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph,
EXTRACT_ADD_REQUESTED(vbo, lnor);
EXTRACT_ADD_REQUESTED(vbo, uv);
EXTRACT_ADD_REQUESTED(vbo, tan);
- EXTRACT_ADD_REQUESTED(vbo, vcol);
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);
EXTRACT_ADD_REQUESTED(vbo, orco);
EXTRACT_ADD_REQUESTED(vbo, edge_fac);
@@ -848,7 +847,6 @@ void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache,
EXTRACT_ADD_REQUESTED(vbo, edituv_stretch_angle);
EXTRACT_ADD_REQUESTED(ibo, lines_paint_mask);
EXTRACT_ADD_REQUESTED(ibo, lines_adjacency);
- EXTRACT_ADD_REQUESTED(vbo, vcol);
EXTRACT_ADD_REQUESTED(vbo, weights);
EXTRACT_ADD_REQUESTED(vbo, sculpt_data);
diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc
index 77cbb5efa12..eea19cbebf3 100644
--- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc
+++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc
@@ -14,6 +14,7 @@
#include "BLI_math.h"
#include "BLI_task.h"
+#include "BKE_attribute.hh"
#include "BKE_editmesh.h"
#include "BKE_editmesh_cache.h"
#include "BKE_mesh.h"
@@ -228,10 +229,10 @@ static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCa
}
}
else {
- const MPoly *mp = &mr->mpoly[0];
- for (int i = 0; i < mr->poly_len; i++, mp++) {
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- const int mat = min_ii(mp->mat_nr, mat_last);
+ for (int i = 0; i < mr->poly_len; i++) {
+ if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[i])) {
+ const MPoly *mp = &mr->mpoly[i];
+ const int mat = min_ii(mr->material_indices ? mr->material_indices[i] : 0, mat_last);
tri_first_index[i] = mat_tri_offs[mat];
mat_tri_offs[mat] += mp->totloop - 2;
}
@@ -269,8 +270,8 @@ static void mesh_render_data_mat_tri_len_mesh_range_fn(void *__restrict userdata
int *mat_tri_len = static_cast<int *>(tls->userdata_chunk);
const MPoly *mp = &mr->mpoly[iter];
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
- int mat = min_ii(mp->mat_nr, mr->mat_len - 1);
+ if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[iter])) {
+ int mat = min_ii(mr->material_indices ? mr->material_indices[iter] : 0, mr->mat_len - 1);
mat_tri_len[mat] += mp->totloop - 2;
}
}
@@ -332,15 +333,15 @@ void mesh_render_data_update_looptris(MeshRenderData *mr,
if (mr->extract_type != MR_EXTRACT_BMESH) {
/* Mesh */
if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) {
- /* NOTE(campbell): It's possible to skip allocating tessellation,
+ /* NOTE(@campbellbarton): It's possible to skip allocating tessellation,
* the tessellation can be calculated as part of the iterator, see: P2188.
* The overall advantage is small (around 1%), so keep this as-is. */
mr->mlooptri = static_cast<MLoopTri *>(
MEM_mallocN(sizeof(*mr->mlooptri) * mr->tri_len, "MR_DATATYPE_LOOPTRI"));
if (mr->poly_normals != nullptr) {
- BKE_mesh_recalc_looptri_with_normals(me->mloop,
- me->mpoly,
- me->mvert,
+ BKE_mesh_recalc_looptri_with_normals(mr->mloop,
+ mr->mpoly,
+ mr->mvert,
me->totloop,
me->totpoly,
mr->mlooptri,
@@ -348,7 +349,7 @@ void mesh_render_data_update_looptris(MeshRenderData *mr,
}
else {
BKE_mesh_recalc_looptri(
- me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mr->mlooptri);
+ mr->mloop, mr->mpoly, mr->mvert, me->totloop, me->totpoly, mr->mlooptri);
}
}
}
@@ -378,15 +379,15 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_
MEM_mallocN(sizeof(*mr->loop_normals) * mr->loop_len, __func__));
short(*clnors)[2] = static_cast<short(*)[2]>(
CustomData_get_layer(&mr->me->ldata, CD_CUSTOMLOOPNORMAL));
- BKE_mesh_normals_loop_split(mr->me->mvert,
+ BKE_mesh_normals_loop_split(mr->mvert,
mr->vert_normals,
mr->vert_len,
- mr->me->medge,
+ mr->medge,
mr->edge_len,
- mr->me->mloop,
+ mr->mloop,
mr->loop_normals,
mr->loop_len,
- mr->me->mpoly,
+ mr->mpoly,
mr->poly_normals,
mr->poly_len,
is_auto_smooth,
@@ -431,6 +432,30 @@ void mesh_render_data_update_normals(MeshRenderData *mr, const eMRDataType data_
}
}
+static void retrieve_active_attribute_names(MeshRenderData &mr,
+ const Object &object,
+ const Mesh &mesh)
+{
+ const Mesh *mesh_final = editmesh_final_or_this(&object, &mesh);
+ const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(mesh_final);
+ const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(mesh_final);
+
+ /* Necessary because which attributes are active/default is stored in #CustomData. */
+ Mesh me_query = blender::dna::shallow_zero_initialize();
+ BKE_id_attribute_copy_domains_temp(
+ ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id);
+
+ mr.active_color_name = nullptr;
+ mr.default_color_name = nullptr;
+
+ if (const CustomDataLayer *active = BKE_id_attributes_active_color_get(&me_query.id)) {
+ mr.active_color_name = active->name;
+ }
+ if (const CustomDataLayer *render = BKE_id_attributes_render_color_get(&me_query.id)) {
+ mr.default_color_name = render->name;
+ }
+}
+
MeshRenderData *mesh_render_data_create(Object *object,
Mesh *me,
const bool is_editmode,
@@ -470,17 +495,6 @@ MeshRenderData *mesh_render_data_create(Object *object,
mr->bm_poly_centers = mr->edit_data->polyCos;
}
- /* A subdivision wrapper may be created in edit mode when X-ray is turned on to ensure that the
- * topology seen by the user matches the one used for the selection routines. This wrapper
- * seemingly takes precedence over the MDATA one, however the mesh we use for rendering is not
- * the subdivided one, but the one where the MDATA wrapper would have been added. So consider
- * the subdivision wrapper as well for the `has_mdata` case. */
- bool has_mdata = is_mode_active && ELEM(mr->me->runtime.wrapper_type,
- ME_WRAPPER_TYPE_MDATA,
- ME_WRAPPER_TYPE_SUBD);
- 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);
@@ -499,43 +513,51 @@ MeshRenderData *mesh_render_data_create(Object *object,
mr->freestyle_face_ofs = CustomData_get_offset(&mr->bm->pdata, CD_FREESTYLE_FACE);
#endif
- if (use_mapped) {
- mr->v_origindex = static_cast<const int *>(
- CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX));
- mr->e_origindex = static_cast<const int *>(
- CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX));
- mr->p_origindex = static_cast<const int *>(
- CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX));
-
- use_mapped = (mr->v_origindex || mr->e_origindex || mr->p_origindex);
+ /* Use bmesh directly when the object is in edit mode unchanged by any modifiers.
+ * For non-final UVs, always use original bmesh since the UV editor does not support
+ * using the cage mesh with deformed coordinates. */
+ if ((is_mode_active && mr->me->runtime.is_original_bmesh &&
+ mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH) ||
+ (do_uvedit && !do_final)) {
+ mr->extract_type = MR_EXTRACT_BMESH;
}
-
- 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 (use_mapped && do_final && editmesh_eval_final != editmesh_eval_cage) {
- // mr->edit_bmesh = nullptr;
+ else {
mr->extract_type = MR_EXTRACT_MESH;
+
+ /* Use mapping from final to original mesh when the object is in edit mode. */
+ if (is_mode_active && do_final) {
+ mr->v_origindex = static_cast<const int *>(
+ CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX));
+ mr->e_origindex = static_cast<const int *>(
+ CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX));
+ mr->p_origindex = static_cast<const int *>(
+ CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX));
+ }
+ else {
+ mr->v_origindex = nullptr;
+ mr->e_origindex = nullptr;
+ mr->p_origindex = nullptr;
+ }
}
}
else {
mr->me = me;
mr->edit_bmesh = nullptr;
+ mr->extract_type = MR_EXTRACT_MESH;
- bool use_mapped = is_paint_mode && mr->me && !mr->me->runtime.is_original;
- if (use_mapped) {
+ if (is_paint_mode && mr->me) {
mr->v_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX));
mr->e_origindex = static_cast<const int *>(
CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX));
mr->p_origindex = static_cast<const int *>(
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;
+ else {
+ mr->v_origindex = nullptr;
+ mr->e_origindex = nullptr;
+ mr->p_origindex = nullptr;
+ }
}
if (mr->extract_type != MR_EXTRACT_BMESH) {
@@ -546,14 +568,24 @@ MeshRenderData *mesh_render_data_create(Object *object,
mr->poly_len = mr->me->totpoly;
mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
- mr->mvert = static_cast<MVert *>(CustomData_get_layer(&mr->me->vdata, CD_MVERT));
- mr->medge = static_cast<MEdge *>(CustomData_get_layer(&mr->me->edata, CD_MEDGE));
- mr->mloop = static_cast<MLoop *>(CustomData_get_layer(&mr->me->ldata, CD_MLOOP));
- mr->mpoly = static_cast<MPoly *>(CustomData_get_layer(&mr->me->pdata, CD_MPOLY));
+ mr->mvert = BKE_mesh_verts(mr->me);
+ mr->medge = BKE_mesh_edges(mr->me);
+ mr->mpoly = BKE_mesh_polys(mr->me);
+ mr->mloop = BKE_mesh_loops(mr->me);
mr->v_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX));
mr->e_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX));
mr->p_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX));
+
+ mr->material_indices = static_cast<const int *>(
+ CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, "material_index"));
+
+ mr->hide_vert = static_cast<const bool *>(
+ CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"));
+ mr->hide_edge = static_cast<const bool *>(
+ CustomData_get_layer_named(&me->edata, CD_PROP_BOOL, ".hide_edge"));
+ mr->hide_poly = static_cast<const bool *>(
+ CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly"));
}
else {
/* #BMesh */
@@ -566,6 +598,8 @@ MeshRenderData *mesh_render_data_create(Object *object,
mr->tri_len = poly_to_tri_count(mr->poly_len, mr->loop_len);
}
+ retrieve_active_attribute_names(*mr, *object, *me);
+
return mr;
}
diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h
index 91dbef8d7b1..7f7d0a7613f 100644
--- a/source/blender/draw/intern/draw_cache_impl.h
+++ b/source/blender/draw/intern/draw_cache_impl.h
@@ -36,10 +36,6 @@ extern "C" {
/** \name 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);
-void DRW_mball_batch_cache_free(struct MetaBall *mb);
-
void DRW_curve_batch_cache_dirty_tag(struct Curve *cu, int mode);
void DRW_curve_batch_cache_validate(struct Curve *cu);
void DRW_curve_batch_cache_free(struct Curve *cu);
@@ -111,39 +107,6 @@ struct GPUBatch *DRW_curve_batch_cache_get_edit_verts(struct Curve *cu);
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Metaball
- * \{ */
-
-int DRW_metaball_material_count_get(struct MetaBall *mb);
-
-struct GPUBatch *DRW_metaball_batch_cache_get_triangles_with_normals(struct Object *ob);
-struct GPUBatch **DRW_metaball_batch_cache_get_surface_shaded(struct Object *ob,
- struct MetaBall *mb,
- struct GPUMaterial **gpumat_array,
- uint gpumat_array_len);
-struct GPUBatch *DRW_metaball_batch_cache_get_wireframes_face(struct Object *ob);
-struct GPUBatch *DRW_metaball_batch_cache_get_edge_detection(struct Object *ob,
- bool *r_is_manifold);
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name DispList
- * \{ */
-
-void DRW_displist_vertbuf_create_pos_and_nor(struct ListBase *lb,
- struct GPUVertBuf *vbo,
- const struct Scene *scene);
-void DRW_displist_vertbuf_create_wiredata(struct ListBase *lb, struct GPUVertBuf *vbo);
-void DRW_displist_indexbuf_create_lines_in_order(struct ListBase *lb, struct GPUIndexBuf *ibo);
-void DRW_displist_indexbuf_create_triangles_in_order(struct ListBase *lb, struct GPUIndexBuf *ibo);
-void DRW_displist_indexbuf_create_edges_adjacency_lines(struct ListBase *lb,
- struct GPUIndexBuf *ibo,
- bool *r_is_manifold);
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Lattice
* \{ */
@@ -309,7 +272,6 @@ struct GPUBatch *DRW_mesh_batch_cache_get_edit_mesh_analysis(struct Mesh *me);
* \{ */
struct GPUVertBuf *DRW_mesh_batch_cache_pos_vertbuf_get(struct Mesh *me);
-struct GPUVertBuf *DRW_mball_batch_cache_pos_vertbuf_get(struct Object *ob);
int DRW_mesh_material_count_get(const struct Object *object, const struct Mesh *me);
diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc
index d5f188402d6..3bca17d9c56 100644
--- a/source/blender/draw/intern/draw_cache_impl_curves.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curves.cc
@@ -75,13 +75,14 @@ static void curves_batch_cache_init(Curves &curves)
if (!cache) {
cache = MEM_cnew<CurvesBatchCache>(__func__);
- BLI_mutex_init(&cache->render_mutex);
curves.batch_cache = cache;
}
else {
memset(cache, 0, sizeof(*cache));
}
+ BLI_mutex_init(&cache->render_mutex);
+
cache->is_dirty = false;
}
@@ -268,7 +269,8 @@ static void curves_batch_cache_ensure_procedural_pos(const Curves &curves,
GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
GPU_vertformat_alias_add(&format, "pos");
- cache.proc_point_buf = GPU_vertbuf_create_with_format(&format);
+ cache.proc_point_buf = GPU_vertbuf_create_with_format_ex(
+ &format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache.proc_point_buf, cache.point_len);
MutableSpan posTime_data{
@@ -278,7 +280,8 @@ static void curves_batch_cache_ensure_procedural_pos(const Curves &curves,
GPUVertFormat length_format = {0};
GPU_vertformat_attr_add(&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- cache.proc_length_buf = GPU_vertbuf_create_with_format(&length_format);
+ cache.proc_length_buf = GPU_vertbuf_create_with_format_ex(
+ &length_format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache.proc_length_buf, cache.strands_len);
MutableSpan hairLength_data{
@@ -318,8 +321,8 @@ static void curves_batch_cache_ensure_procedural_final_attr(CurvesEvalCache &cac
const char *name)
{
CurvesEvalFinalCache &final_cache = cache.final[subdiv];
- final_cache.attributes_buf[index] = GPU_vertbuf_create_with_format_ex(format,
- GPU_USAGE_DEVICE_ONLY);
+ final_cache.attributes_buf[index] = GPU_vertbuf_create_with_format_ex(
+ format, GPU_USAGE_DEVICE_ONLY | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
/* Create a destination buffer for the transform feedback. Sized appropriately */
/* Those are points! not line segments. */
@@ -350,7 +353,8 @@ static void curves_batch_ensure_attribute(const Curves &curves,
/* All attributes use vec4, see comment below. */
GPU_vertformat_attr_add(&format, sampler_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- cache.proc_attributes_buf[index] = GPU_vertbuf_create_with_format(&format);
+ cache.proc_attributes_buf[index] = GPU_vertbuf_create_with_format_ex(
+ &format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPUVertBuf *attr_vbo = cache.proc_attributes_buf[index];
GPU_vertbuf_data_alloc(attr_vbo,
@@ -396,10 +400,10 @@ static void curves_batch_cache_fill_strands_data(const Curves &curves_id,
curves_id.geometry);
for (const int i : IndexRange(curves.curves_num())) {
- const IndexRange curve_range = curves.points_for_curve(i);
+ const IndexRange points = curves.points_for_curve(i);
- *(uint *)GPU_vertbuf_raw_step(&data_step) = curve_range.start();
- *(ushort *)GPU_vertbuf_raw_step(&seg_step) = curve_range.size() - 1;
+ *(uint *)GPU_vertbuf_raw_step(&data_step) = points.start();
+ *(ushort *)GPU_vertbuf_raw_step(&seg_step) = points.size() - 1;
}
}
@@ -415,11 +419,13 @@ static void curves_batch_cache_ensure_procedural_strand_data(Curves &curves,
uint seg_id = GPU_vertformat_attr_add(&format_seg, "data", GPU_COMP_U16, 1, GPU_FETCH_INT);
/* Curve Data. */
- cache.proc_strand_buf = GPU_vertbuf_create_with_format(&format_data);
+ cache.proc_strand_buf = GPU_vertbuf_create_with_format_ex(
+ &format_data, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache.proc_strand_buf, cache.strands_len);
GPU_vertbuf_attr_get_raw_data(cache.proc_strand_buf, data_id, &data_step);
- cache.proc_strand_seg_buf = GPU_vertbuf_create_with_format(&format_seg);
+ cache.proc_strand_seg_buf = GPU_vertbuf_create_with_format_ex(
+ &format_seg, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache.proc_strand_seg_buf, cache.strands_len);
GPU_vertbuf_attr_get_raw_data(cache.proc_strand_seg_buf, seg_id, &seg_step);
@@ -440,7 +446,8 @@ static void curves_batch_cache_ensure_procedural_final_points(CurvesEvalCache &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_ex(&format, GPU_USAGE_DEVICE_ONLY);
+ cache.final[subdiv].proc_buf = GPU_vertbuf_create_with_format_ex(
+ &format, GPU_USAGE_DEVICE_ONLY | GPU_USAGE_FLAG_BUFFER_TEXTURE_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_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c
deleted file mode 100644
index 96c088c3ee9..00000000000
--- a/source/blender/draw/intern/draw_cache_impl_displist.c
+++ /dev/null
@@ -1,354 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2017 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup draw
- *
- * \brief DispList API for render engines
- *
- * \note DispList may be removed soon! This is a utility for object types that use render.
- */
-
-#include "BLI_edgehash.h"
-#include "BLI_listbase.h"
-#include "BLI_math_vector.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_curve_types.h"
-#include "DNA_scene_types.h"
-
-#include "BKE_displist.h"
-
-#include "GPU_batch.h"
-#include "GPU_capabilities.h"
-
-#include "draw_cache_inline.h"
-
-#include "draw_cache_impl.h" /* own include */
-
-static int dl_vert_len(const DispList *dl)
-{
- switch (dl->type) {
- case DL_INDEX3:
- case DL_INDEX4:
- return dl->nr;
- case DL_SURF:
- return dl->parts * dl->nr;
- }
- return 0;
-}
-
-static int dl_tri_len(const DispList *dl)
-{
- switch (dl->type) {
- case DL_INDEX3:
- return dl->parts;
- case DL_INDEX4:
- return dl->parts * 2;
- case DL_SURF:
- return dl->totindex * 2;
- }
- return 0;
-}
-
-/* see: displist_vert_coords_alloc */
-static int curve_render_surface_vert_len_get(const ListBase *lb)
-{
- int vert_len = 0;
- LISTBASE_FOREACH (const DispList *, dl, lb) {
- vert_len += dl_vert_len(dl);
- }
- return vert_len;
-}
-
-static int curve_render_surface_tri_len_get(const ListBase *lb)
-{
- int tri_len = 0;
- LISTBASE_FOREACH (const DispList *, dl, lb) {
- tri_len += dl_tri_len(dl);
- }
- return tri_len;
-}
-
-typedef void(SetTriIndicesFn)(void *thunk, uint v1, uint v2, uint v3);
-
-static void displist_indexbufbuilder_set(
- SetTriIndicesFn *set_tri_indices,
- SetTriIndicesFn *set_quad_tri_indices, /* meh, find a better solution. */
- void *thunk,
- const DispList *dl,
- const int ofs)
-{
- if (ELEM(dl->type, DL_INDEX3, DL_INDEX4, DL_SURF)) {
- const int *idx = dl->index;
- if (dl->type == DL_INDEX3) {
- const int i_end = dl->parts;
- for (int i = 0; i < i_end; i++, idx += 3) {
- set_tri_indices(thunk, idx[0] + ofs, idx[2] + ofs, idx[1] + ofs);
- }
- }
- else if (dl->type == DL_SURF) {
- const int i_end = dl->totindex;
- for (int i = 0; i < i_end; i++, idx += 4) {
- set_quad_tri_indices(thunk, idx[0] + ofs, idx[2] + ofs, idx[1] + ofs);
- set_quad_tri_indices(thunk, idx[2] + ofs, idx[0] + ofs, idx[3] + ofs);
- }
- }
- else {
- BLI_assert(dl->type == DL_INDEX4);
- const int i_end = dl->parts;
- for (int i = 0; i < i_end; i++, idx += 4) {
- if (idx[2] != idx[3]) {
- set_quad_tri_indices(thunk, idx[2] + ofs, idx[0] + ofs, idx[1] + ofs);
- set_quad_tri_indices(thunk, idx[0] + ofs, idx[2] + ofs, idx[3] + ofs);
- }
- else {
- set_tri_indices(thunk, idx[2] + ofs, idx[0] + ofs, idx[1] + ofs);
- }
- }
- }
- }
-}
-
-void DRW_displist_vertbuf_create_pos_and_nor(ListBase *lb, GPUVertBuf *vbo, const Scene *scene)
-{
- const bool do_hq_normals = (scene->r.perf_flag & SCE_PERF_HQ_NORMALS) != 0 ||
- GPU_use_hq_normals_workaround();
-
- static GPUVertFormat format = {0};
- static GPUVertFormat format_hq = {0};
- static struct {
- uint pos, nor;
- uint pos_hq, nor_hq;
- } attr_id;
- if (format.attr_len == 0) {
- /* initialize vertex format */
- attr_id.pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- attr_id.nor = GPU_vertformat_attr_add(
- &format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- /* initialize vertex format */
- attr_id.pos_hq = GPU_vertformat_attr_add(&format_hq, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- attr_id.nor_hq = GPU_vertformat_attr_add(
- &format_hq, "nor", GPU_COMP_I16, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
-
- uint pos_id = do_hq_normals ? attr_id.pos_hq : attr_id.pos;
- uint nor_id = do_hq_normals ? attr_id.nor_hq : attr_id.nor;
-
- GPU_vertbuf_init_with_format(vbo, do_hq_normals ? &format_hq : &format);
- GPU_vertbuf_data_alloc(vbo, curve_render_surface_vert_len_get(lb));
-
- BKE_displist_normals_add(lb);
-
- int vbo_len_used = 0;
- LISTBASE_FOREACH (const DispList *, dl, lb) {
- const bool ndata_is_single = dl->type == DL_INDEX3;
- if (ELEM(dl->type, DL_INDEX3, DL_INDEX4, DL_SURF)) {
- const float *fp_co = dl->verts;
- const float *fp_no = dl->nors;
- const int vbo_end = vbo_len_used + dl_vert_len(dl);
- while (vbo_len_used < vbo_end) {
- GPU_vertbuf_attr_set(vbo, pos_id, vbo_len_used, fp_co);
- if (fp_no) {
- GPUNormal vnor_pack;
- GPU_normal_convert_v3(&vnor_pack, fp_no, do_hq_normals);
- GPU_vertbuf_attr_set(vbo, nor_id, vbo_len_used, &vnor_pack);
- if (ndata_is_single == false) {
- fp_no += 3;
- }
- }
- fp_co += 3;
- vbo_len_used += 1;
- }
- }
- }
-}
-
-void DRW_vertbuf_create_wiredata(GPUVertBuf *vbo, const int vert_len)
-{
- static GPUVertFormat format = {0};
- static struct {
- uint wd;
- } attr_id;
- if (format.attr_len == 0) {
- /* initialize vertex format */
- if (!GPU_crappy_amd_driver()) {
- /* Some AMD drivers strangely crash with a vbo with this format. */
- attr_id.wd = GPU_vertformat_attr_add(
- &format, "wd", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
- }
- else {
- attr_id.wd = GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- }
- }
-
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, vert_len);
-
- if (GPU_vertbuf_get_format(vbo)->stride == 1) {
- memset(GPU_vertbuf_get_data(vbo), 0xFF, (size_t)vert_len);
- }
- else {
- GPUVertBufRaw wd_step;
- GPU_vertbuf_attr_get_raw_data(vbo, attr_id.wd, &wd_step);
- for (int i = 0; i < vert_len; i++) {
- *((float *)GPU_vertbuf_raw_step(&wd_step)) = 1.0f;
- }
- }
-}
-
-void DRW_displist_vertbuf_create_wiredata(ListBase *lb, GPUVertBuf *vbo)
-{
- const int vert_len = curve_render_surface_vert_len_get(lb);
- DRW_vertbuf_create_wiredata(vbo, vert_len);
-}
-
-void DRW_displist_indexbuf_create_triangles_in_order(ListBase *lb, GPUIndexBuf *ibo)
-{
- const int tri_len = curve_render_surface_tri_len_get(lb);
- const int vert_len = curve_render_surface_vert_len_get(lb);
-
- GPUIndexBufBuilder elb;
- GPU_indexbuf_init(&elb, GPU_PRIM_TRIS, tri_len, vert_len);
-
- int ofs = 0;
- LISTBASE_FOREACH (const DispList *, dl, lb) {
- displist_indexbufbuilder_set((SetTriIndicesFn *)GPU_indexbuf_add_tri_verts,
- (SetTriIndicesFn *)GPU_indexbuf_add_tri_verts,
- &elb,
- dl,
- ofs);
- ofs += dl_vert_len(dl);
- }
-
- GPU_indexbuf_build_in_place(&elb, ibo);
-}
-
-static void set_overlay_wires_tri_indices(void *thunk, uint v1, uint v2, uint v3)
-{
- GPUIndexBufBuilder *eld = (GPUIndexBufBuilder *)thunk;
- GPU_indexbuf_add_line_verts(eld, v1, v2);
- GPU_indexbuf_add_line_verts(eld, v2, v3);
- GPU_indexbuf_add_line_verts(eld, v3, v1);
-}
-
-static void set_overlay_wires_quad_tri_indices(void *thunk, uint v1, uint v2, uint v3)
-{
- GPUIndexBufBuilder *eld = (GPUIndexBufBuilder *)thunk;
- GPU_indexbuf_add_line_verts(eld, v1, v3);
- GPU_indexbuf_add_line_verts(eld, v3, v2);
-}
-
-void DRW_displist_indexbuf_create_lines_in_order(ListBase *lb, GPUIndexBuf *ibo)
-{
- const int tri_len = curve_render_surface_tri_len_get(lb);
- const int vert_len = curve_render_surface_vert_len_get(lb);
-
- GPUIndexBufBuilder elb;
- GPU_indexbuf_init(&elb, GPU_PRIM_LINES, tri_len * 3, vert_len);
-
- int ofs = 0;
- LISTBASE_FOREACH (const DispList *, dl, lb) {
- displist_indexbufbuilder_set(
- set_overlay_wires_tri_indices, set_overlay_wires_quad_tri_indices, &elb, dl, ofs);
- ofs += dl_vert_len(dl);
- }
-
- GPU_indexbuf_build_in_place(&elb, ibo);
-}
-
-/* Edge detection/adjacency. */
-#define NO_EDGE INT_MAX
-static void set_edge_adjacency_lines_indices(
- EdgeHash *eh, GPUIndexBufBuilder *elb, bool *r_is_manifold, uint v1, uint v2, uint v3)
-{
- bool inv_indices = (v2 > v3);
- void **pval;
- bool value_is_init = BLI_edgehash_ensure_p(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
- * edgehash sort the keys and we need to compare winding later. */
- int value = (int)v1 + 1; /* Int 0 bm_looptricannot be signed */
- *pval = POINTER_FROM_INT((inv_indices) ? -value : value);
- }
- 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 v_opposite = (uint)abs(v_data) - 1;
-
- if (inv_opposite == inv_indices) {
- /* Don't share edge if triangles have non matching winding. */
- GPU_indexbuf_add_line_adj_verts(elb, v1, v2, v3, v1);
- GPU_indexbuf_add_line_adj_verts(elb, v_opposite, v2, v3, v_opposite);
- *r_is_manifold = false;
- }
- else {
- GPU_indexbuf_add_line_adj_verts(elb, v1, v2, v3, v_opposite);
- }
- }
-}
-
-static void set_edges_adjacency_lines_indices(void *thunk, uint v1, uint v2, uint v3)
-{
- void **packed = (void **)thunk;
- GPUIndexBufBuilder *elb = (GPUIndexBufBuilder *)packed[0];
- EdgeHash *eh = (EdgeHash *)packed[1];
- bool *r_is_manifold = (bool *)packed[2];
-
- set_edge_adjacency_lines_indices(eh, elb, r_is_manifold, v1, v2, v3);
- set_edge_adjacency_lines_indices(eh, elb, r_is_manifold, v2, v3, v1);
- set_edge_adjacency_lines_indices(eh, elb, r_is_manifold, v3, v1, v2);
-}
-
-void DRW_displist_indexbuf_create_edges_adjacency_lines(struct ListBase *lb,
- struct GPUIndexBuf *ibo,
- bool *r_is_manifold)
-{
- const int tri_len = curve_render_surface_tri_len_get(lb);
- const int vert_len = curve_render_surface_vert_len_get(lb);
-
- *r_is_manifold = true;
-
- /* Allocate max but only used indices are sent to GPU. */
- GPUIndexBufBuilder elb;
- GPU_indexbuf_init(&elb, GPU_PRIM_LINES_ADJ, tri_len * 3, vert_len);
-
- EdgeHash *eh = BLI_edgehash_new_ex(__func__, tri_len * 3);
-
- /* pack values to pass to `set_edges_adjacency_lines_indices` function. */
- void *thunk[3] = {&elb, eh, r_is_manifold};
- int v_idx = 0;
- LISTBASE_FOREACH (const DispList *, dl, lb) {
- displist_indexbufbuilder_set((SetTriIndicesFn *)set_edges_adjacency_lines_indices,
- (SetTriIndicesFn *)set_edges_adjacency_lines_indices,
- thunk,
- dl,
- v_idx);
- v_idx += dl_vert_len(dl);
- }
-
- /* Create edges for remaining non manifold edges. */
- EdgeHashIterator *ehi;
- for (ehi = BLI_edgehashIterator_new(eh); BLI_edgehashIterator_isDone(ehi) == false;
- BLI_edgehashIterator_step(ehi)) {
- uint v1, v2;
- int v_data = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
- if (v_data == NO_EDGE) {
- continue;
- }
- BLI_edgehashIterator_getKey(ehi, &v1, &v2);
- uint v0 = (uint)abs(v_data) - 1;
- if (v_data < 0) { /* inv_opposite */
- SWAP(uint, v1, v2);
- }
- GPU_indexbuf_add_line_adj_verts(&elb, v0, v1, v2, v0);
- *r_is_manifold = false;
- }
- BLI_edgehashIterator_free(ehi);
- BLI_edgehash_free(eh, NULL);
-
- GPU_indexbuf_build_in_place(&elb, ibo);
-}
-#undef NO_EDGE
diff --git a/source/blender/draw/intern/draw_cache_impl_lattice.c b/source/blender/draw/intern/draw_cache_impl_lattice.c
index cb621c6ceb9..0f12e78d60e 100644
--- a/source/blender/draw/intern/draw_cache_impl_lattice.c
+++ b/source/blender/draw/intern/draw_cache_impl_lattice.c
@@ -27,12 +27,6 @@
#define SELECT 1
-/**
- * TODO
- * - 'DispList' is currently not used
- * (we could avoid using since it will be removed)
- */
-
static void lattice_batch_cache_clear(Lattice *lt);
/* ---------------------------------------------------------------------- */
diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc
index e93b1a66b66..c22382b3e09 100644
--- a/source/blender/draw/intern/draw_cache_impl_mesh.cc
+++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc
@@ -21,6 +21,7 @@
#include "BLI_math_vector.h"
#include "BLI_span.hh"
#include "BLI_string.h"
+#include "BLI_string_ref.hh"
#include "BLI_task.h"
#include "BLI_utildefines.h"
@@ -67,6 +68,7 @@
using blender::IndexRange;
using blender::Map;
using blender::Span;
+using blender::StringRefNull;
/* ---------------------------------------------------------------------- */
/** \name Dependencies between buffer and batch
@@ -115,8 +117,6 @@ static constexpr DRWBatchFlag batches_that_use_buffer(const int buffer_index)
MBC_SURFACE_PER_MAT;
case BUFFER_INDEX(vbo.tan):
return MBC_SURFACE_PER_MAT;
- case BUFFER_INDEX(vbo.vcol):
- return MBC_SURFACE | MBC_SURFACE_PER_MAT;
case BUFFER_INDEX(vbo.sculpt_data):
return MBC_SCULPT_OVERLAYS;
case BUFFER_INDEX(vbo.orco):
@@ -236,87 +236,11 @@ BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a)
*((uint32_t *)a) = 0;
}
-BLI_INLINE const Mesh *editmesh_final_or_this(const Object *object, const Mesh *me)
-{
- if (me->edit_mesh != nullptr) {
- Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object);
- if (editmesh_eval_final != nullptr) {
- return editmesh_eval_final;
- }
- }
-
- return me;
-}
-
static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *cd_used)
{
cd_used->edit_uv = 1;
}
-BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me)
-{
- switch ((eMeshWrapperType)me->runtime.wrapper_type) {
- case ME_WRAPPER_TYPE_SUBD:
- case ME_WRAPPER_TYPE_MDATA:
- return &me->ldata;
- break;
- case ME_WRAPPER_TYPE_BMESH:
- return &me->edit_mesh->bm->ldata;
- break;
- }
-
- BLI_assert(0);
- return &me->ldata;
-}
-
-BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me)
-{
- switch ((eMeshWrapperType)me->runtime.wrapper_type) {
- case ME_WRAPPER_TYPE_SUBD:
- case ME_WRAPPER_TYPE_MDATA:
- return &me->pdata;
- break;
- case ME_WRAPPER_TYPE_BMESH:
- return &me->edit_mesh->bm->pdata;
- break;
- }
-
- BLI_assert(0);
- return &me->pdata;
-}
-
-BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me)
-{
- switch ((eMeshWrapperType)me->runtime.wrapper_type) {
- case ME_WRAPPER_TYPE_SUBD:
- case ME_WRAPPER_TYPE_MDATA:
- return &me->edata;
- break;
- case ME_WRAPPER_TYPE_BMESH:
- return &me->edit_mesh->bm->edata;
- break;
- }
-
- BLI_assert(0);
- return &me->edata;
-}
-
-BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me)
-{
- switch ((eMeshWrapperType)me->runtime.wrapper_type) {
- case ME_WRAPPER_TYPE_SUBD:
- case ME_WRAPPER_TYPE_MDATA:
- return &me->vdata;
- break;
- case ME_WRAPPER_TYPE_BMESH:
- return &me->edit_mesh->bm->vdata;
- break;
- }
-
- BLI_assert(0);
- return &me->vdata;
-}
-
static void mesh_cd_calc_active_uv_layer(const Object *object,
const Mesh *me,
DRW_MeshCDMask *cd_used)
@@ -341,75 +265,6 @@ static void mesh_cd_calc_active_mask_uv_layer(const Object *object,
}
}
-static void mesh_cd_calc_active_mloopcol_layer(const Object *object,
- const Mesh *me,
- DRW_MeshCDMask *cd_used)
-{
- const Mesh *me_final = editmesh_final_or_this(object, me);
- Mesh me_query = blender::dna::shallow_zero_initialize();
-
- const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
- const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
-
- BKE_id_attribute_copy_domains_temp(
- ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id);
-
- const CustomDataLayer *layer = BKE_id_attributes_active_color_get(&me_query.id);
- int layer_i = BKE_id_attribute_to_index(
- &me_query.id, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
-
- if (layer_i != -1) {
- cd_used->vcol |= (1UL << (uint)layer_i);
- }
-}
-
-static uint mesh_cd_calc_gpu_layers_vcol_used(const Mesh *me_query,
- const CustomData *cd_vdata,
- const CustomData *cd_ldata,
- const char name[])
-{
- const CustomDataLayer *layer = nullptr;
- eAttrDomain domain;
-
- if (name[0]) {
- int layer_i = 0;
-
- domain = ATTR_DOMAIN_POINT;
- layer_i = CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, name);
- layer_i = layer_i == -1 ?
- CustomData_get_named_layer_index(cd_vdata, CD_PROP_BYTE_COLOR, name) :
- layer_i;
-
- if (layer_i == -1) {
- domain = ATTR_DOMAIN_CORNER;
- layer_i = layer_i == -1 ? CustomData_get_named_layer_index(cd_ldata, CD_PROP_COLOR, name) :
- layer_i;
- layer_i = layer_i == -1 ?
- CustomData_get_named_layer_index(cd_ldata, CD_PROP_BYTE_COLOR, name) :
- layer_i;
- }
-
- /* NOTE: this is not the same as the layer_i below. */
- if (layer_i != -1) {
- layer = (domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata)->layers + layer_i;
- }
- }
- else {
- layer = BKE_id_attributes_render_color_get(&me_query->id);
- }
-
- if (!layer) {
- return -1;
- }
-
- /* NOTE: this is the logical index into the color attribute list,
- * not the customdata index. */
- int vcol_i = BKE_id_attribute_to_index(
- (ID *)me_query, layer, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
-
- return vcol_i;
-}
-
static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
const Mesh *me,
struct GPUMaterial **gpumat_array,
@@ -433,56 +288,33 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
DRW_MeshCDMask cd_used;
mesh_cd_layers_type_clear(&cd_used);
+ const CustomDataLayer *default_color = BKE_id_attributes_render_color_get(&me_query.id);
+ const StringRefNull default_color_name = default_color ? default_color->name : "";
+
for (int i = 0; i < gpumat_array_len; i++) {
GPUMaterial *gpumat = gpumat_array[i];
- if (gpumat) {
- ListBase gpu_attrs = GPU_material_attributes(gpumat);
- LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
- const char *name = gpu_attr->name;
- eCustomDataType type = static_cast<eCustomDataType>(gpu_attr->type);
- int layer = -1;
- std::optional<eAttrDomain> domain;
-
- if (type == CD_AUTO_FROM_NAME) {
- /* We need to deduce what exact layer is used.
- *
- * We do it based on the specified name.
- */
- if (name[0] != '\0') {
- layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name);
- type = CD_MTFACE;
-
- if (layer == -1) {
- layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name);
- if (layer != -1) {
- type = CD_PROP_COLOR;
- domain = ATTR_DOMAIN_POINT;
- }
- }
-
- if (layer == -1) {
- layer = CustomData_get_named_layer(cd_ldata, CD_PROP_COLOR, name);
- if (layer != -1) {
- type = CD_PROP_COLOR;
- domain = ATTR_DOMAIN_CORNER;
- }
- }
-
- if (layer == -1) {
- layer = CustomData_get_named_layer(cd_vdata, CD_PROP_BYTE_COLOR, name);
- if (layer != -1) {
- type = CD_PROP_BYTE_COLOR;
- domain = ATTR_DOMAIN_POINT;
- }
- }
+ if (gpumat == nullptr) {
+ continue;
+ }
+ ListBase gpu_attrs = GPU_material_attributes(gpumat);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) {
+ const char *name = gpu_attr->name;
+ eCustomDataType type = static_cast<eCustomDataType>(gpu_attr->type);
+ int layer = -1;
+ std::optional<eAttrDomain> domain;
+
+ if (gpu_attr->is_default_color) {
+ name = default_color_name.c_str();
+ }
- if (layer == -1) {
- layer = CustomData_get_named_layer(cd_ldata, CD_PROP_BYTE_COLOR, name);
- if (layer != -1) {
- type = CD_PROP_BYTE_COLOR;
- domain = ATTR_DOMAIN_CORNER;
- }
- }
+ if (type == CD_AUTO_FROM_NAME) {
+ /* We need to deduce what exact layer is used.
+ *
+ * We do it based on the specified name.
+ */
+ if (name[0] != '\0') {
+ layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name);
+ type = CD_MTFACE;
#if 0 /* Tangents are always from UV's - this will never happen. */
if (layer == -1) {
@@ -490,108 +322,87 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object,
type = CD_TANGENT;
}
#endif
- if (layer == -1) {
- /* Try to match a generic attribute, we use the first attribute domain with a
- * matching name. */
- if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) {
- domain = ATTR_DOMAIN_POINT;
- }
- else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) {
- domain = ATTR_DOMAIN_CORNER;
- }
- else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) {
- domain = ATTR_DOMAIN_FACE;
- }
- else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) {
- domain = ATTR_DOMAIN_EDGE;
- }
- else {
- layer = -1;
- }
+ if (layer == -1) {
+ /* Try to match a generic attribute, we use the first attribute domain with a
+ * matching name. */
+ if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_POINT;
}
-
- if (layer == -1) {
- continue;
+ else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_CORNER;
}
- }
- else {
- /* Fall back to the UV layer, which matches old behavior. */
- type = CD_MTFACE;
- }
- }
-
- switch (type) {
- case CD_MTFACE: {
- if (layer == -1) {
- layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) :
- CustomData_get_render_layer(cd_ldata, CD_MLOOPUV);
+ else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_FACE;
}
- if (layer != -1) {
- cd_used.uv |= (1 << layer);
- }
- break;
- }
- case CD_TANGENT: {
- if (layer == -1) {
- layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) :
- CustomData_get_render_layer(cd_ldata, CD_MLOOPUV);
-
- /* Only fallback to orco (below) when we have no UV layers, see: T56545 */
- if (layer == -1 && name[0] != '\0') {
- layer = CustomData_get_render_layer(cd_ldata, CD_MLOOPUV);
- }
- }
- if (layer != -1) {
- cd_used.tan |= (1 << layer);
+ else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) {
+ domain = ATTR_DOMAIN_EDGE;
}
else {
- /* no UV layers at all => requesting orco */
- cd_used.tan_orco = 1;
- cd_used.orco = 1;
+ layer = -1;
}
- break;
}
- case CD_ORCO: {
- cd_used.orco = 1;
- break;
+ if (layer == -1) {
+ continue;
}
+ }
+ else {
+ /* Fall back to the UV layer, which matches old behavior. */
+ type = CD_MTFACE;
+ }
+ }
- /* NOTE: attr->type will always be CD_PROP_COLOR even for
- * CD_PROP_BYTE_COLOR layers, see node_shader_gpu_vertex_color in
- * node_shader_vertex_color.cc.
- */
- case CD_MCOL:
- case CD_PROP_BYTE_COLOR:
- case CD_PROP_COLOR: {
- /* First check Color attributes, when not found check mesh attributes. Geometry nodes
- * can generate those layers. */
- int vcol_bit = mesh_cd_calc_gpu_layers_vcol_used(&me_query, cd_vdata, cd_ldata, name);
-
- if (vcol_bit != -1) {
- cd_used.vcol |= 1UL << (uint)vcol_bit;
- break;
- }
-
- if (layer != -1 && domain.has_value()) {
- drw_attributes_add_request(attributes, name, type, layer, *domain);
- }
- break;
+ switch (type) {
+ case CD_MTFACE: {
+ if (layer == -1) {
+ layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) :
+ CustomData_get_render_layer(cd_ldata, CD_MLOOPUV);
+ }
+ if (layer != -1) {
+ cd_used.uv |= (1 << layer);
}
- case CD_PROP_FLOAT3:
- case CD_PROP_BOOL:
- case CD_PROP_INT8:
- case CD_PROP_INT32:
- case CD_PROP_FLOAT:
- case CD_PROP_FLOAT2: {
- if (layer != -1 && domain.has_value()) {
- drw_attributes_add_request(attributes, name, type, layer, *domain);
+ break;
+ }
+ case CD_TANGENT: {
+ if (layer == -1) {
+ layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) :
+ CustomData_get_render_layer(cd_ldata, CD_MLOOPUV);
+
+ /* Only fallback to orco (below) when we have no UV layers, see: T56545 */
+ if (layer == -1 && name[0] != '\0') {
+ layer = CustomData_get_render_layer(cd_ldata, CD_MLOOPUV);
}
- break;
}
- default:
- break;
+ if (layer != -1) {
+ cd_used.tan |= (1 << layer);
+ }
+ else {
+ /* no UV layers at all => requesting orco */
+ cd_used.tan_orco = 1;
+ cd_used.orco = 1;
+ }
+ break;
}
+
+ case CD_ORCO: {
+ cd_used.orco = 1;
+ break;
+ }
+ case CD_PROP_BYTE_COLOR:
+ case CD_PROP_COLOR:
+ case CD_PROP_FLOAT3:
+ case CD_PROP_BOOL:
+ case CD_PROP_INT8:
+ case CD_PROP_INT32:
+ case CD_PROP_FLOAT:
+ case CD_PROP_FLOAT2: {
+ if (layer != -1 && domain.has_value()) {
+ drw_attributes_add_request(attributes, name, type, layer, *domain);
+ }
+ break;
+ }
+ default:
+ break;
}
}
}
@@ -745,8 +556,7 @@ static bool mesh_batch_cache_valid(Object *object, Mesh *me)
}
if (object->sculpt && object->sculpt->pbvh) {
- if (cache->pbvh_is_drawing != BKE_pbvh_is_drawing(object->sculpt->pbvh) ||
- BKE_pbvh_draw_cache_invalid(object->sculpt->pbvh)) {
+ if (cache->pbvh_is_drawing != BKE_pbvh_is_drawing(object->sculpt->pbvh)) {
return false;
}
@@ -863,10 +673,9 @@ static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
FOREACH_MESH_BUFFER_CACHE (cache, mbc) {
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv);
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.tan);
- GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol);
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.orco);
}
- DRWBatchFlag batch_map = BATCH_MAP(vbo.uv, vbo.tan, vbo.vcol, vbo.orco);
+ DRWBatchFlag batch_map = BATCH_MAP(vbo.uv, vbo.tan, vbo.orco);
mesh_batch_cache_discard_batch(cache, batch_map);
mesh_cd_layers_type_clear(&cache->cd_used);
}
@@ -1070,42 +879,35 @@ static void texpaint_request_active_uv(MeshBatchCache *cache, Object *object, Me
mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
}
-static void texpaint_request_active_vcol(MeshBatchCache *cache, Object *object, Mesh *me)
+static void request_active_and_default_color_attributes(const Object &object,
+ const Mesh &mesh,
+ DRW_Attributes &attributes)
{
- DRW_MeshCDMask cd_needed;
- mesh_cd_layers_type_clear(&cd_needed);
- mesh_cd_calc_active_mloopcol_layer(object, me, &cd_needed);
-
- BLI_assert(cd_needed.vcol != 0 &&
- "No MLOOPCOL layer available in vertpaint, but batches requested anyway!");
-
- mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed);
-}
-
-static void sculpt_request_active_vcol(MeshBatchCache *cache, Object *object, Mesh *me)
-{
- const Mesh *me_final = editmesh_final_or_this(object, me);
+ const Mesh *me_final = editmesh_final_or_this(&object, &mesh);
const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final);
const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final);
+ /* Necessary because which attributes are active/default is stored in #CustomData. */
Mesh me_query = blender::dna::shallow_zero_initialize();
BKE_id_attribute_copy_domains_temp(
ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id);
- const CustomDataLayer *active = BKE_id_attributes_active_color_get(&me_query.id);
- const CustomDataLayer *render = BKE_id_attributes_render_color_get(&me_query.id);
-
- int active_i = BKE_id_attribute_to_index(
- &me_query.id, active, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
- int render_i = BKE_id_attribute_to_index(
- &me_query.id, render, ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL);
+ auto request_color_attribute = [&](const char *name) {
+ int layer_index;
+ eCustomDataType type;
+ if (drw_custom_data_match_attribute(cd_vdata, name, &layer_index, &type)) {
+ drw_attributes_add_request(&attributes, name, type, layer_index, ATTR_DOMAIN_POINT);
+ }
+ else if (drw_custom_data_match_attribute(cd_ldata, name, &layer_index, &type)) {
+ drw_attributes_add_request(&attributes, name, type, layer_index, ATTR_DOMAIN_CORNER);
+ }
+ };
- if (active_i >= 0) {
- cache->cd_needed.vcol |= 1UL << (uint)active_i;
+ if (const CustomDataLayer *active = BKE_id_attributes_active_color_get(&me_query.id)) {
+ request_color_attribute(active->name);
}
-
- if (render_i >= 0) {
- cache->cd_needed.vcol |= 1UL << (uint)render_i;
+ if (const CustomDataLayer *render = BKE_id_attributes_render_color_get(&me_query.id)) {
+ request_color_attribute(render->name);
}
}
@@ -1214,7 +1016,13 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Object *object, Mesh
GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- texpaint_request_active_vcol(cache, object, me);
+
+ DRW_Attributes attrs_needed{};
+ request_active_and_default_color_attributes(*object, *me, attrs_needed);
+
+ ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex;
+ drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex);
+
mesh_batch_cache_request_surface_batches(cache);
return cache->batch.surface;
}
@@ -1222,7 +1030,13 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_vertpaint(Object *object, Mesh *me)
GPUBatch *DRW_mesh_batch_cache_get_surface_sculpt(Object *object, Mesh *me)
{
MeshBatchCache *cache = mesh_batch_cache_get(me);
- sculpt_request_active_vcol(cache, object, me);
+
+ DRW_Attributes attrs_needed{};
+ request_active_and_default_color_attributes(*object, *me, attrs_needed);
+
+ ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex;
+ drw_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex);
+
mesh_batch_cache_request_surface_batches(cache);
return cache->batch.surface;
}
@@ -1621,9 +1435,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) {
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data);
}
- if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) {
- GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol);
- }
if (!drw_attributes_overlap(&cache->attr_used, &cache->attr_needed)) {
for (int i = 0; i < GPU_MAX_ATTR; i++) {
GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]);
@@ -1697,12 +1508,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
cache->batch_ready |= batch_requested;
bool do_cage = false, do_uvcage = false;
- if (is_editmode) {
+ if (is_editmode && is_mode_active) {
Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(ob);
Mesh *editmesh_eval_cage = BKE_object_get_editmesh_eval_cage(ob);
do_cage = editmesh_eval_final != editmesh_eval_cage;
- do_uvcage = !editmesh_eval_final->runtime.is_original;
+ do_uvcage = !(editmesh_eval_final->runtime.is_original_bmesh &&
+ editmesh_eval_final->runtime.wrapper_type == ME_WRAPPER_TYPE_BMESH);
}
const bool do_subdivision = BKE_subsurf_modifier_has_gpu_subdiv(me);
@@ -1710,15 +1522,26 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
MeshBufferList *mbuflist = &cache->final.buff;
/* Initialize batches and request VBO's & IBO's. */
- assert_deps_valid(
- MBC_SURFACE,
- {BUFFER_INDEX(ibo.tris), BUFFER_INDEX(vbo.lnor), BUFFER_INDEX(vbo.pos_nor),
- BUFFER_INDEX(vbo.uv), BUFFER_INDEX(vbo.vcol), BUFFER_INDEX(vbo.attr[0]),
- BUFFER_INDEX(vbo.attr[1]), BUFFER_INDEX(vbo.attr[2]), BUFFER_INDEX(vbo.attr[3]),
- BUFFER_INDEX(vbo.attr[4]), BUFFER_INDEX(vbo.attr[5]), BUFFER_INDEX(vbo.attr[6]),
- BUFFER_INDEX(vbo.attr[7]), BUFFER_INDEX(vbo.attr[8]), BUFFER_INDEX(vbo.attr[9]),
- BUFFER_INDEX(vbo.attr[10]), BUFFER_INDEX(vbo.attr[11]), BUFFER_INDEX(vbo.attr[12]),
- BUFFER_INDEX(vbo.attr[13]), BUFFER_INDEX(vbo.attr[14])});
+ assert_deps_valid(MBC_SURFACE,
+ {BUFFER_INDEX(ibo.tris),
+ BUFFER_INDEX(vbo.lnor),
+ BUFFER_INDEX(vbo.pos_nor),
+ BUFFER_INDEX(vbo.uv),
+ BUFFER_INDEX(vbo.attr[0]),
+ BUFFER_INDEX(vbo.attr[1]),
+ BUFFER_INDEX(vbo.attr[2]),
+ BUFFER_INDEX(vbo.attr[3]),
+ BUFFER_INDEX(vbo.attr[4]),
+ BUFFER_INDEX(vbo.attr[5]),
+ BUFFER_INDEX(vbo.attr[6]),
+ BUFFER_INDEX(vbo.attr[7]),
+ BUFFER_INDEX(vbo.attr[8]),
+ BUFFER_INDEX(vbo.attr[9]),
+ BUFFER_INDEX(vbo.attr[10]),
+ BUFFER_INDEX(vbo.attr[11]),
+ BUFFER_INDEX(vbo.attr[12]),
+ BUFFER_INDEX(vbo.attr[13]),
+ BUFFER_INDEX(vbo.attr[14])});
if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) {
DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris);
/* Order matters. First ones override latest VBO's attributes. */
@@ -1727,9 +1550,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if (cache->cd_used.uv != 0) {
DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv);
}
- if (cache->cd_used.vcol != 0) {
- DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol);
- }
drw_add_attributes_vbo(cache->batch.surface, mbuflist, &cache->attr_used);
}
assert_deps_valid(MBC_ALL_VERTS, {BUFFER_INDEX(vbo.pos_nor)});
@@ -1807,12 +1627,12 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
assert_deps_valid(
MBC_SURFACE_PER_MAT,
{BUFFER_INDEX(vbo.lnor), BUFFER_INDEX(vbo.pos_nor), BUFFER_INDEX(vbo.uv),
- BUFFER_INDEX(vbo.tan), BUFFER_INDEX(vbo.vcol), BUFFER_INDEX(vbo.orco),
- BUFFER_INDEX(vbo.attr[0]), BUFFER_INDEX(vbo.attr[1]), BUFFER_INDEX(vbo.attr[2]),
- BUFFER_INDEX(vbo.attr[3]), BUFFER_INDEX(vbo.attr[4]), BUFFER_INDEX(vbo.attr[5]),
- BUFFER_INDEX(vbo.attr[6]), BUFFER_INDEX(vbo.attr[7]), BUFFER_INDEX(vbo.attr[8]),
- BUFFER_INDEX(vbo.attr[9]), BUFFER_INDEX(vbo.attr[10]), BUFFER_INDEX(vbo.attr[11]),
- BUFFER_INDEX(vbo.attr[12]), BUFFER_INDEX(vbo.attr[13]), BUFFER_INDEX(vbo.attr[14])});
+ BUFFER_INDEX(vbo.tan), BUFFER_INDEX(vbo.orco), BUFFER_INDEX(vbo.attr[0]),
+ BUFFER_INDEX(vbo.attr[1]), BUFFER_INDEX(vbo.attr[2]), BUFFER_INDEX(vbo.attr[3]),
+ BUFFER_INDEX(vbo.attr[4]), BUFFER_INDEX(vbo.attr[5]), BUFFER_INDEX(vbo.attr[6]),
+ BUFFER_INDEX(vbo.attr[7]), BUFFER_INDEX(vbo.attr[8]), BUFFER_INDEX(vbo.attr[9]),
+ BUFFER_INDEX(vbo.attr[10]), BUFFER_INDEX(vbo.attr[11]), BUFFER_INDEX(vbo.attr[12]),
+ BUFFER_INDEX(vbo.attr[13]), BUFFER_INDEX(vbo.attr[14])});
assert_deps_valid(MBC_SURFACE_PER_MAT, {TRIS_PER_MAT_INDEX});
for (int i = 0; i < cache->mat_len; i++) {
if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) {
@@ -1826,9 +1646,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) {
DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan);
}
- if (cache->cd_used.vcol != 0) {
- DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol);
- }
if (cache->cd_used.orco != 0) {
DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco);
}
@@ -1994,7 +1811,6 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
assert_final_deps_valid(BUFFER_INDEX(vbo.lnor));
assert_final_deps_valid(BUFFER_INDEX(vbo.pos_nor));
assert_final_deps_valid(BUFFER_INDEX(vbo.uv));
- assert_final_deps_valid(BUFFER_INDEX(vbo.vcol));
assert_final_deps_valid(BUFFER_INDEX(vbo.sculpt_data));
assert_final_deps_valid(BUFFER_INDEX(vbo.weights));
assert_final_deps_valid(BUFFER_INDEX(vbo.edge_fac));
@@ -2078,6 +1894,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph,
ob->obmat,
true,
false,
+ do_cage,
ts,
use_hide);
}
diff --git a/source/blender/draw/intern/draw_cache_impl_metaball.c b/source/blender/draw/intern/draw_cache_impl_metaball.c
deleted file mode 100644
index 1408dc91069..00000000000
--- a/source/blender/draw/intern/draw_cache_impl_metaball.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2017 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup draw
- *
- * \brief MetaBall API for render engines
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "BLI_math_base.h"
-#include "BLI_utildefines.h"
-
-#include "DNA_meta_types.h"
-#include "DNA_object_types.h"
-
-#include "BKE_curve.h"
-#include "BKE_mball.h"
-
-#include "GPU_batch.h"
-
-#include "DRW_render.h"
-#include "draw_cache_impl.h" /* own include */
-
-static void metaball_batch_cache_clear(MetaBall *mb);
-
-/* -------------------------------------------------------------------- */
-/** \name MetaBall GPUBatch Cache
- * \{ */
-
-typedef struct MetaBallBatchCache {
- GPUBatch *batch;
- GPUBatch **shaded_triangles;
-
- int mat_len;
-
- /* Shared */
- GPUVertBuf *pos_nor_in_order;
-
- /* Wireframe */
- struct {
- GPUBatch *batch;
- } face_wire;
-
- /* Edge detection */
- GPUBatch *edge_detection;
- GPUIndexBuf *edges_adj_lines;
-
- /* settings to determine if cache is invalid */
- bool is_dirty;
-
- /* Valid only if edge_detection is up to date. */
- bool is_manifold;
-} MetaBallBatchCache;
-
-/* GPUBatch cache management. */
-
-static bool metaball_batch_cache_valid(MetaBall *mb)
-{
- MetaBallBatchCache *cache = mb->batch_cache;
-
- if (cache == NULL) {
- return false;
- }
-
- return cache->is_dirty == false;
-}
-
-static void metaball_batch_cache_init(MetaBall *mb)
-{
- MetaBallBatchCache *cache = mb->batch_cache;
-
- if (!cache) {
- cache = mb->batch_cache = MEM_mallocN(sizeof(*cache), __func__);
- }
- cache->batch = NULL;
- cache->mat_len = 0;
- cache->shaded_triangles = NULL;
- cache->is_dirty = false;
- cache->pos_nor_in_order = NULL;
- cache->face_wire.batch = NULL;
- cache->edge_detection = NULL;
- cache->edges_adj_lines = NULL;
- cache->is_manifold = false;
-}
-
-void DRW_mball_batch_cache_validate(MetaBall *mb)
-{
- if (!metaball_batch_cache_valid(mb)) {
- metaball_batch_cache_clear(mb);
- metaball_batch_cache_init(mb);
- }
-}
-
-static MetaBallBatchCache *metaball_batch_cache_get(MetaBall *mb)
-{
- return mb->batch_cache;
-}
-
-void DRW_mball_batch_cache_dirty_tag(MetaBall *mb, int mode)
-{
- MetaBallBatchCache *cache = mb->batch_cache;
- if (cache == NULL) {
- return;
- }
- switch (mode) {
- case BKE_MBALL_BATCH_DIRTY_ALL:
- cache->is_dirty = true;
- break;
- default:
- BLI_assert(0);
- }
-}
-
-static void metaball_batch_cache_clear(MetaBall *mb)
-{
- MetaBallBatchCache *cache = mb->batch_cache;
- if (!cache) {
- return;
- }
-
- GPU_BATCH_DISCARD_SAFE(cache->face_wire.batch);
- GPU_BATCH_DISCARD_SAFE(cache->batch);
- GPU_BATCH_DISCARD_SAFE(cache->edge_detection);
- GPU_VERTBUF_DISCARD_SAFE(cache->pos_nor_in_order);
- GPU_INDEXBUF_DISCARD_SAFE(cache->edges_adj_lines);
- /* NOTE: shaded_triangles[0] is already freed by `cache->batch`. */
- MEM_SAFE_FREE(cache->shaded_triangles);
- cache->mat_len = 0;
- cache->is_manifold = false;
-}
-
-void DRW_mball_batch_cache_free(MetaBall *mb)
-{
- metaball_batch_cache_clear(mb);
- MEM_SAFE_FREE(mb->batch_cache);
-}
-
-static GPUVertBuf *mball_batch_cache_get_pos_and_normals(Object *ob,
- MetaBallBatchCache *cache,
- const struct Scene *scene)
-{
- if (cache->pos_nor_in_order == NULL) {
- ListBase *lb = &ob->runtime.curve_cache->disp;
- cache->pos_nor_in_order = GPU_vertbuf_calloc();
- DRW_displist_vertbuf_create_pos_and_nor(lb, cache->pos_nor_in_order, scene);
- }
- return cache->pos_nor_in_order;
-}
-
-static GPUIndexBuf *mball_batch_cache_get_edges_adj_lines(Object *ob, MetaBallBatchCache *cache)
-{
- if (cache->edges_adj_lines == NULL) {
- ListBase *lb = &ob->runtime.curve_cache->disp;
- cache->edges_adj_lines = GPU_indexbuf_calloc();
- DRW_displist_indexbuf_create_edges_adjacency_lines(
- lb, cache->edges_adj_lines, &cache->is_manifold);
- }
- return cache->edges_adj_lines;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Public Object/MetaBall API
- * \{ */
-
-GPUBatch *DRW_metaball_batch_cache_get_triangles_with_normals(Object *ob)
-{
- if (!BKE_mball_is_basis(ob)) {
- return NULL;
- }
-
- MetaBall *mb = ob->data;
- MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const struct Scene *scene = draw_ctx->scene;
-
- if (cache->batch == NULL) {
- ListBase *lb = &ob->runtime.curve_cache->disp;
- GPUIndexBuf *ibo = GPU_indexbuf_calloc();
- DRW_displist_indexbuf_create_triangles_in_order(lb, ibo);
- cache->batch = GPU_batch_create_ex(GPU_PRIM_TRIS,
- mball_batch_cache_get_pos_and_normals(ob, cache, scene),
- ibo,
- GPU_BATCH_OWNS_INDEX);
- }
-
- return cache->batch;
-}
-
-GPUBatch **DRW_metaball_batch_cache_get_surface_shaded(Object *ob,
- MetaBall *mb,
- struct GPUMaterial **UNUSED(gpumat_array),
- uint gpumat_array_len)
-{
- if (!BKE_mball_is_basis(ob)) {
- return NULL;
- }
-
- BLI_assert(gpumat_array_len == DRW_metaball_material_count_get(mb));
-
- MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
- if (cache->shaded_triangles == NULL) {
- cache->mat_len = gpumat_array_len;
- cache->shaded_triangles = MEM_callocN(sizeof(*cache->shaded_triangles) * cache->mat_len,
- __func__);
- cache->shaded_triangles[0] = DRW_metaball_batch_cache_get_triangles_with_normals(ob);
- for (int i = 1; i < cache->mat_len; i++) {
- cache->shaded_triangles[i] = NULL;
- }
- }
- return cache->shaded_triangles;
-}
-
-GPUBatch *DRW_metaball_batch_cache_get_wireframes_face(Object *ob)
-{
- if (!BKE_mball_is_basis(ob)) {
- return NULL;
- }
-
- MetaBall *mb = ob->data;
- MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const struct Scene *scene = draw_ctx->scene;
-
- if (cache->face_wire.batch == NULL) {
- ListBase *lb = &ob->runtime.curve_cache->disp;
-
- GPUVertBuf *vbo_wiredata = GPU_vertbuf_calloc();
- DRW_displist_vertbuf_create_wiredata(lb, vbo_wiredata);
-
- GPUIndexBuf *ibo = GPU_indexbuf_calloc();
- DRW_displist_indexbuf_create_lines_in_order(lb, ibo);
-
- cache->face_wire.batch = GPU_batch_create_ex(
- GPU_PRIM_LINES,
- mball_batch_cache_get_pos_and_normals(ob, cache, scene),
- ibo,
- GPU_BATCH_OWNS_INDEX);
-
- GPU_batch_vertbuf_add_ex(cache->face_wire.batch, vbo_wiredata, true);
- }
-
- return cache->face_wire.batch;
-}
-
-struct GPUBatch *DRW_metaball_batch_cache_get_edge_detection(struct Object *ob,
- bool *r_is_manifold)
-{
- if (!BKE_mball_is_basis(ob)) {
- return NULL;
- }
-
- MetaBall *mb = ob->data;
- MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const struct Scene *scene = draw_ctx->scene;
-
- if (cache->edge_detection == NULL) {
- cache->edge_detection = GPU_batch_create(
- GPU_PRIM_LINES_ADJ,
- mball_batch_cache_get_pos_and_normals(ob, cache, scene),
- mball_batch_cache_get_edges_adj_lines(ob, cache));
- }
-
- if (r_is_manifold) {
- *r_is_manifold = cache->is_manifold;
- }
-
- return cache->edge_detection;
-}
-
-struct GPUVertBuf *DRW_mball_batch_cache_pos_vertbuf_get(Object *ob)
-{
- if (!BKE_mball_is_basis(ob)) {
- return NULL;
- }
-
- MetaBall *mb = ob->data;
- MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
- const DRWContextState *draw_ctx = DRW_context_state_get();
- const struct Scene *scene = draw_ctx->scene;
-
- return mball_batch_cache_get_pos_and_normals(ob, cache, scene);
-}
-
-int DRW_metaball_material_count_get(MetaBall *mb)
-{
- return max_ii(1, mb->totcol);
-}
-
-/** \} */
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
index dee7a8cec37..9c1784b1de2 100644
--- a/source/blender/draw/intern/draw_cache_impl_particles.c
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -11,6 +11,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_alloca.h"
#include "BLI_ghash.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
@@ -31,6 +32,8 @@
#include "ED_particle.h"
#include "GPU_batch.h"
+#include "GPU_capabilities.h"
+#include "GPU_context.h"
#include "GPU_material.h"
#include "DEG_depsgraph_query.h"
@@ -182,10 +185,11 @@ static void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_uv_buf[i]);
DRW_TEXTURE_FREE_SAFE(hair_cache->uv_tex[i]);
}
- for (int i = 0; i < MAX_MCOL; i++) {
+ for (int i = 0; i < hair_cache->num_col_layers; i++) {
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_col_buf[i]);
DRW_TEXTURE_FREE_SAFE(hair_cache->col_tex[i]);
}
+
for (int i = 0; i < MAX_HAIR_SUBDIV; i++) {
GPU_VERTBUF_DISCARD_SAFE(hair_cache->final[i].proc_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->final[i].proc_tex);
@@ -218,9 +222,24 @@ static void particle_batch_cache_clear(ParticleSystem *psys)
GPU_VERTBUF_DISCARD_SAFE(cache->edit_tip_pos);
}
+static void particle_batch_cache_free_hair(ParticleHairCache *hair)
+{
+ MEM_SAFE_FREE(hair->proc_col_buf);
+ MEM_SAFE_FREE(hair->col_tex);
+ MEM_SAFE_FREE(hair->col_layer_names);
+}
+
void DRW_particle_batch_cache_free(ParticleSystem *psys)
{
particle_batch_cache_clear(psys);
+
+ ParticleBatchCache *cache = psys->batch_cache;
+
+ if (cache) {
+ particle_batch_cache_free_hair(&cache->hair);
+ particle_batch_cache_free_hair(&cache->edit_hair);
+ }
+
MEM_SAFE_FREE(psys->batch_cache);
}
@@ -296,7 +315,8 @@ static void particle_calculate_parent_uvs(ParticleSystem *psys,
}
}
if (!ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
- MFace *mface = &psmd->mesh_final->mface[num];
+ MFace *mfaces = CustomData_get_layer(&psmd->mesh_final->fdata, CD_MFACE);
+ MFace *mface = &mfaces[num];
for (int j = 0; j < num_uv_layers; j++) {
psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, r_uv[j]);
}
@@ -325,7 +345,8 @@ static void particle_calculate_parent_mcol(ParticleSystem *psys,
}
}
if (!ELEM(num, DMCACHE_NOTFOUND, DMCACHE_ISCHILD)) {
- MFace *mface = &psmd->mesh_final->mface[num];
+ MFace *mfaces = CustomData_get_layer(&psmd->mesh_final->fdata, CD_MFACE);
+ MFace *mface = &mfaces[num];
for (int j = 0; j < num_col_layers; j++) {
/* CustomDataLayer CD_MCOL has 4 structs per face. */
psys_interpolate_mcol(mcols[j] + num * 4, mface->v4, particle->fuv, &r_mcol[j]);
@@ -351,7 +372,8 @@ static void particle_interpolate_children_uvs(ParticleSystem *psys,
ChildParticle *particle = &psys->child[child_index];
int num = particle->num;
if (num != DMCACHE_NOTFOUND) {
- MFace *mface = &psmd->mesh_final->mface[num];
+ MFace *mfaces = CustomData_get_layer(&psmd->mesh_final->fdata, CD_MFACE);
+ MFace *mface = &mfaces[num];
for (int j = 0; j < num_uv_layers; j++) {
psys_interpolate_uvs(mtfaces[j] + num, mface->v4, particle->fuv, r_uv[j]);
}
@@ -375,7 +397,8 @@ static void particle_interpolate_children_mcol(ParticleSystem *psys,
ChildParticle *particle = &psys->child[child_index];
int num = particle->num;
if (num != DMCACHE_NOTFOUND) {
- MFace *mface = &psmd->mesh_final->mface[num];
+ MFace *mfaces = CustomData_get_layer(&psmd->mesh_final->fdata, CD_MFACE);
+ MFace *mface = &mfaces[num];
for (int j = 0; j < num_col_layers; j++) {
/* CustomDataLayer CD_MCOL has 4 structs per face. */
psys_interpolate_mcol(mcols[j] + num * 4, mface->v4, particle->fuv, &r_mcol[j]);
@@ -791,7 +814,10 @@ static void particle_batch_cache_ensure_procedural_final_points(ParticleHairCach
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);
+ /* Transform feedback buffer only needs to be resident in device memory. */
+ GPUUsageType type = GPU_transform_feedback_support() ? GPU_USAGE_DEVICE_ONLY : GPU_USAGE_STATIC;
+ cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format_ex(
+ &format, type | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
/* Create a destination buffer for the transform feedback. Sized appropriately */
/* Those are points! not line segments. */
@@ -833,10 +859,10 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit
GPUVertBufRaw data_step, seg_step;
GPUVertBufRaw uv_step[MAX_MTFACE];
- GPUVertBufRaw col_step[MAX_MCOL];
+ GPUVertBufRaw *col_step = BLI_array_alloca(col_step, cache->num_col_layers);
const MTFace *mtfaces[MAX_MTFACE] = {NULL};
- const MCol *mcols[MAX_MCOL] = {NULL};
+ const MCol **mcols = BLI_array_alloca(mcols, cache->num_col_layers);
float(**parent_uvs)[2] = NULL;
MCol **parent_mcol = NULL;
@@ -854,20 +880,22 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit
&format_col, "col", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
memset(cache->uv_layer_names, 0, sizeof(cache->uv_layer_names));
- memset(cache->col_layer_names, 0, sizeof(cache->col_layer_names));
/* Strand Data */
- cache->proc_strand_buf = GPU_vertbuf_create_with_format(&format_data);
+ cache->proc_strand_buf = GPU_vertbuf_create_with_format_ex(
+ &format_data, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache->proc_strand_buf, cache->strands_len);
GPU_vertbuf_attr_get_raw_data(cache->proc_strand_buf, data_id, &data_step);
- cache->proc_strand_seg_buf = GPU_vertbuf_create_with_format(&format_seg);
+ cache->proc_strand_seg_buf = GPU_vertbuf_create_with_format_ex(
+ &format_seg, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache->proc_strand_seg_buf, cache->strands_len);
GPU_vertbuf_attr_get_raw_data(cache->proc_strand_seg_buf, seg_id, &seg_step);
/* UV layers */
for (int i = 0; i < cache->num_uv_layers; i++) {
- cache->proc_uv_buf[i] = GPU_vertbuf_create_with_format(&format_uv);
+ cache->proc_uv_buf[i] = GPU_vertbuf_create_with_format_ex(
+ &format_uv, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache->proc_uv_buf[i], cache->strands_len);
GPU_vertbuf_attr_get_raw_data(cache->proc_uv_buf[i], uv_id, &uv_step[i]);
@@ -885,9 +913,20 @@ static void particle_batch_cache_ensure_procedural_strand_data(PTCacheEdit *edit
BLI_strncpy(cache->uv_layer_names[i][n++], "a", MAX_LAYER_NAME_LEN);
}
}
+
+ MEM_SAFE_FREE(cache->proc_col_buf);
+ MEM_SAFE_FREE(cache->col_tex);
+ MEM_SAFE_FREE(cache->col_layer_names);
+
+ cache->proc_col_buf = MEM_calloc_arrayN(cache->num_col_layers, sizeof(void *), "proc_col_buf");
+ cache->col_tex = MEM_calloc_arrayN(cache->num_col_layers, sizeof(void *), "col_tex");
+ cache->col_layer_names = MEM_calloc_arrayN(
+ cache->num_col_layers, sizeof(*cache->col_layer_names), "col_layer_names");
+
/* Vertex colors */
for (int i = 0; i < cache->num_col_layers; i++) {
- cache->proc_col_buf[i] = GPU_vertbuf_create_with_format(&format_col);
+ cache->proc_col_buf[i] = GPU_vertbuf_create_with_format_ex(
+ &format_col, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache->proc_col_buf[i], cache->strands_len);
GPU_vertbuf_attr_get_raw_data(cache->proc_col_buf[i], col_id, &col_step[i]);
@@ -1033,8 +1072,9 @@ static void particle_batch_cache_ensure_procedural_indices(PTCacheEdit *edit,
static GPUVertFormat format = {0};
GPU_vertformat_clear(&format);
- /* initialize vertex format */
- GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ /* NOTE: initialize vertex format. Using GPU_COMP_U32 to satisfy Metal's 4-byte minimum
+ * stride requirement. */
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U32, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, 1);
@@ -1075,7 +1115,8 @@ static void particle_batch_cache_ensure_procedural_pos(PTCacheEdit *edit,
uint pos_id = GPU_vertformat_attr_add(
&pos_format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- cache->proc_point_buf = GPU_vertbuf_create_with_format(&pos_format);
+ cache->proc_point_buf = GPU_vertbuf_create_with_format_ex(
+ &pos_format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
GPUVertBufRaw pos_step;
@@ -1085,7 +1126,8 @@ static void particle_batch_cache_ensure_procedural_pos(PTCacheEdit *edit,
uint length_id = GPU_vertformat_attr_add(
&length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format);
+ cache->proc_length_buf = GPU_vertbuf_create_with_format_ex(
+ &length_format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len);
GPUVertBufRaw length_step;
diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc
index d715899e291..a43b23c8969 100644
--- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c
+++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc
@@ -13,23 +13,23 @@
#include "BLI_math_base.h"
#include "BLI_math_vector.h"
+#include "BLI_task.hh"
#include "BLI_utildefines.h"
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
+#include "BKE_attribute.hh"
#include "BKE_pointcloud.h"
#include "GPU_batch.h"
#include "draw_cache_impl.h" /* own include */
-static void pointcloud_batch_cache_clear(PointCloud *pointcloud);
-
/* ---------------------------------------------------------------------- */
/* PointCloud GPUBatch Cache */
-typedef struct PointCloudBatchCache {
+struct PointCloudBatchCache {
GPUVertBuf *pos; /* Position and radius. */
GPUVertBuf *geom; /* Instanced geometry for each point in the cloud (small sphere). */
GPUIndexBuf *geom_indices;
@@ -42,58 +42,51 @@ typedef struct PointCloudBatchCache {
bool is_dirty;
int mat_len;
-} PointCloudBatchCache;
+};
/* GPUBatch cache management. */
-static bool pointcloud_batch_cache_valid(PointCloud *pointcloud)
+static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud &pointcloud)
+{
+ return static_cast<PointCloudBatchCache *>(pointcloud.batch_cache);
+}
+
+static bool pointcloud_batch_cache_valid(PointCloud &pointcloud)
{
- PointCloudBatchCache *cache = pointcloud->batch_cache;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
- if (cache == NULL) {
+ if (cache == nullptr) {
return false;
}
- if (cache->mat_len != DRW_pointcloud_material_count_get(pointcloud)) {
+ if (cache->mat_len != DRW_pointcloud_material_count_get(&pointcloud)) {
return false;
}
return cache->is_dirty == false;
}
-static void pointcloud_batch_cache_init(PointCloud *pointcloud)
+static void pointcloud_batch_cache_init(PointCloud &pointcloud)
{
- PointCloudBatchCache *cache = pointcloud->batch_cache;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
if (!cache) {
- cache = pointcloud->batch_cache = MEM_callocN(sizeof(*cache), __func__);
+ cache = MEM_cnew<PointCloudBatchCache>(__func__);
+ pointcloud.batch_cache = cache;
}
else {
memset(cache, 0, sizeof(*cache));
}
- cache->mat_len = DRW_pointcloud_material_count_get(pointcloud);
- cache->surface_per_mat = MEM_callocN(sizeof(GPUBatch *) * cache->mat_len,
- "pointcloud suface_per_mat");
+ cache->mat_len = DRW_pointcloud_material_count_get(&pointcloud);
+ cache->surface_per_mat = static_cast<GPUBatch **>(
+ MEM_callocN(sizeof(GPUBatch *) * cache->mat_len, __func__));
cache->is_dirty = false;
}
-void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud)
-{
- if (!pointcloud_batch_cache_valid(pointcloud)) {
- pointcloud_batch_cache_clear(pointcloud);
- pointcloud_batch_cache_init(pointcloud);
- }
-}
-
-static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud *pointcloud)
-{
- return pointcloud->batch_cache;
-}
-
void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode)
{
- PointCloudBatchCache *cache = pointcloud->batch_cache;
- if (cache == NULL) {
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud);
+ if (cache == nullptr) {
return;
}
switch (mode) {
@@ -105,9 +98,9 @@ void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode)
}
}
-static void pointcloud_batch_cache_clear(PointCloud *pointcloud)
+static void pointcloud_batch_cache_clear(PointCloud &pointcloud)
{
- PointCloudBatchCache *cache = pointcloud->batch_cache;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
if (!cache) {
return;
}
@@ -126,50 +119,65 @@ static void pointcloud_batch_cache_clear(PointCloud *pointcloud)
MEM_SAFE_FREE(cache->surface_per_mat);
}
+void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud)
+{
+ if (!pointcloud_batch_cache_valid(*pointcloud)) {
+ pointcloud_batch_cache_clear(*pointcloud);
+ pointcloud_batch_cache_init(*pointcloud);
+ }
+}
+
void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud)
{
- pointcloud_batch_cache_clear(pointcloud);
+ pointcloud_batch_cache_clear(*pointcloud);
MEM_SAFE_FREE(pointcloud->batch_cache);
}
-static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache *cache)
+static void pointcloud_batch_cache_ensure_pos(const PointCloud &pointcloud,
+ PointCloudBatchCache &cache)
{
- if (cache->pos != NULL) {
+ using namespace blender;
+ if (cache.pos != nullptr) {
return;
}
- PointCloud *pointcloud = ob->data;
- const bool has_radius = pointcloud->radius != NULL;
-
- static GPUVertFormat format = {0};
- static GPUVertFormat format_no_radius = {0};
- static uint pos;
- if (format.attr_len == 0) {
- /* initialize vertex format */
- /* From the opengl wiki:
- * Note that size does not have to exactly match the size used by the vertex shader. If the
- * vertex shader has fewer components than the attribute provides, then the extras are ignored.
- * If the vertex shader has more components than the array provides, the extras are given
- * values from the vector (0, 0, 0, 1) for the missing XYZW components.
- */
- pos = GPU_vertformat_attr_add(&format_no_radius, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- }
-
- cache->pos = GPU_vertbuf_create_with_format(has_radius ? &format : &format_no_radius);
- GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint);
-
- if (has_radius) {
- float(*vbo_data)[4] = (float(*)[4])GPU_vertbuf_get_data(cache->pos);
- for (int i = 0; i < pointcloud->totpoint; i++) {
- copy_v3_v3(vbo_data[i], pointcloud->co[i]);
- /* TODO(fclem): remove multiplication here.
- * Here only for keeping the size correct for now. */
- vbo_data[i][3] = pointcloud->radius[i] * 100.0f;
+ const bke::AttributeAccessor attributes = pointcloud.attributes();
+ const VArraySpan<float3> positions = attributes.lookup<float3>("position", ATTR_DOMAIN_POINT);
+ const VArray<float> radii = attributes.lookup<float>("radius", ATTR_DOMAIN_POINT);
+ /* From the opengl wiki:
+ * Note that size does not have to exactly match the size used by the vertex shader. If the
+ * vertex shader has fewer components than the attribute provides, then the extras are ignored.
+ * If the vertex shader has more components than the array provides, the extras are given
+ * values from the vector (0, 0, 0, 1) for the missing XYZW components. */
+ if (radii) {
+ static GPUVertFormat format = {0};
+ if (format.attr_len == 0) {
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
}
+ cache.pos = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache.pos, positions.size());
+ const VArraySpan<float> radii_span(radii);
+ MutableSpan<float4> vbo_data{static_cast<float4 *>(GPU_vertbuf_get_data(cache.pos)),
+ pointcloud.totpoint};
+ threading::parallel_for(vbo_data.index_range(), 4096, [&](IndexRange range) {
+ for (const int i : range) {
+ vbo_data[i].x = positions[i].x;
+ vbo_data[i].y = positions[i].y;
+ vbo_data[i].z = positions[i].z;
+ /* TODO(fclem): remove multiplication. Here only for keeping the size correct for now. */
+ vbo_data[i].w = radii_span[i] * 100.0f;
+ }
+ });
}
else {
- GPU_vertbuf_attr_fill(cache->pos, pos, pointcloud->co);
+ static GPUVertFormat format = {0};
+ static uint pos;
+ if (format.attr_len == 0) {
+ pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ }
+ cache.pos = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache.pos, positions.size());
+ GPU_vertbuf_attr_fill(cache.pos, pos, positions.data());
}
}
@@ -188,24 +196,23 @@ static const uint half_octahedron_tris[4][3] = {
{0, 4, 1},
};
-static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBatchCache *cache)
+static void pointcloud_batch_cache_ensure_geom(PointCloudBatchCache &cache)
{
- if (cache->geom != NULL) {
+ if (cache.geom != nullptr) {
return;
}
static GPUVertFormat format = {0};
static uint pos;
if (format.attr_len == 0) {
- /* initialize vertex format */
pos = GPU_vertformat_attr_add(&format, "pos_inst", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
GPU_vertformat_alias_add(&format, "nor");
}
- cache->geom = GPU_vertbuf_create_with_format(&format);
- GPU_vertbuf_data_alloc(cache->geom, ARRAY_SIZE(half_octahedron_normals));
+ cache.geom = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache.geom, ARRAY_SIZE(half_octahedron_normals));
- GPU_vertbuf_attr_fill(cache->geom, pos, half_octahedron_normals);
+ GPU_vertbuf_attr_fill(cache.geom, pos, half_octahedron_normals);
GPUIndexBufBuilder builder;
GPU_indexbuf_init(&builder,
@@ -217,17 +224,17 @@ static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBat
GPU_indexbuf_add_tri_verts(&builder, UNPACK3(half_octahedron_tris[i]));
}
- cache->geom_indices = GPU_indexbuf_build(&builder);
+ cache.geom_indices = GPU_indexbuf_build(&builder);
}
GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
{
- PointCloud *pointcloud = ob->data;
+ PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
- if (cache->dots == NULL) {
- pointcloud_batch_cache_ensure_pos(ob, cache);
- cache->dots = GPU_batch_create(GPU_PRIM_POINTS, cache->pos, NULL);
+ if (cache->dots == nullptr) {
+ pointcloud_batch_cache_ensure_pos(pointcloud, *cache);
+ cache->dots = GPU_batch_create(GPU_PRIM_POINTS, cache->pos, nullptr);
}
return cache->dots;
@@ -235,12 +242,12 @@ GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
GPUBatch *DRW_pointcloud_batch_cache_get_surface(Object *ob)
{
- PointCloud *pointcloud = ob->data;
+ PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
- if (cache->surface == NULL) {
- pointcloud_batch_cache_ensure_pos(ob, cache);
- pointcloud_batch_cache_ensure_geom(ob, cache);
+ if (cache->surface == nullptr) {
+ pointcloud_batch_cache_ensure_pos(pointcloud, *cache);
+ pointcloud_batch_cache_ensure_geom(*cache);
cache->surface = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices);
GPU_batch_instbuf_add_ex(cache->surface, cache->pos, false);
@@ -253,14 +260,14 @@ GPUBatch **DRW_cache_pointcloud_surface_shaded_get(Object *ob,
struct GPUMaterial **UNUSED(gpumat_array),
uint gpumat_array_len)
{
- PointCloud *pointcloud = ob->data;
+ PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data);
PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
BLI_assert(cache->mat_len == gpumat_array_len);
UNUSED_VARS(gpumat_array_len);
- if (cache->surface_per_mat[0] == NULL) {
- pointcloud_batch_cache_ensure_pos(ob, cache);
- pointcloud_batch_cache_ensure_geom(ob, cache);
+ if (cache->surface_per_mat[0] == nullptr) {
+ pointcloud_batch_cache_ensure_pos(pointcloud, *cache);
+ pointcloud_batch_cache_ensure_geom(*cache);
cache->surface_per_mat[0] = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices);
GPU_batch_instbuf_add_ex(cache->surface_per_mat[0], cache->pos, false);
diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
index 29ab814cfa4..c57406fe460 100644
--- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc
+++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc
@@ -7,8 +7,10 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "BKE_attribute.hh"
#include "BKE_editmesh.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_scene.h"
@@ -19,8 +21,8 @@
#include "BKE_subdiv_modifier.h"
#include "BLI_linklist.h"
-
#include "BLI_string.h"
+#include "BLI_virtual_array.hh"
#include "PIL_time.h"
@@ -44,6 +46,8 @@
#include "draw_cache_inline.h"
#include "mesh_extractors/extract_mesh.hh"
+using blender::Span;
+
extern "C" char datatoc_common_subdiv_custom_data_interp_comp_glsl[];
extern "C" char datatoc_common_subdiv_ibo_lines_comp_glsl[];
extern "C" char datatoc_common_subdiv_ibo_tris_comp_glsl[];
@@ -631,17 +635,9 @@ void draw_subdiv_cache_free(DRWSubdivCache *cache)
SUBDIV_COARSE_FACE_FLAG_ACTIVE | SUBDIV_COARSE_FACE_FLAG_HIDDEN) \
<< SUBDIV_COARSE_FACE_FLAG_OFFSET)
-static uint32_t compute_coarse_face_flag(BMFace *f, BMFace *efa_act)
+static uint32_t compute_coarse_face_flag_bm(BMFace *f, BMFace *efa_act)
{
- if (f == nullptr) {
- /* May happen during mapped extraction. */
- return 0;
- }
-
uint32_t flag = 0;
- if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
- flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
- }
if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
flag |= SUBDIV_COARSE_FACE_FLAG_SELECT;
}
@@ -651,8 +647,7 @@ static uint32_t compute_coarse_face_flag(BMFace *f, BMFace *efa_act)
if (f == efa_act) {
flag |= SUBDIV_COARSE_FACE_FLAG_ACTIVE;
}
- const int loopstart = BM_elem_index_get(f->l_first);
- return (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
+ return flag;
}
static void draw_subdiv_cache_extra_coarse_face_data_bm(BMesh *bm,
@@ -664,24 +659,32 @@ static void draw_subdiv_cache_extra_coarse_face_data_bm(BMesh *bm,
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
const int index = BM_elem_index_get(f);
- flags_data[index] = compute_coarse_face_flag(f, efa_act);
+ uint32_t flag = compute_coarse_face_flag_bm(f, efa_act);
+ if (BM_elem_flag_test(f, BM_ELEM_SMOOTH)) {
+ flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
+ }
+ const int loopstart = BM_elem_index_get(f->l_first);
+ flags_data[index] = (uint)(loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
}
}
-static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t *flags_data)
+static void draw_subdiv_cache_extra_coarse_face_data_mesh(const MeshRenderData *mr,
+ Mesh *mesh,
+ uint32_t *flags_data)
{
- for (int i = 0; i < mesh->totpoly; i++) {
+ const Span<MPoly> polys = mesh->polys();
+ for (const int i : polys.index_range()) {
uint32_t flag = 0;
- if ((mesh->mpoly[i].flag & ME_SMOOTH) != 0) {
+ if ((polys[i].flag & ME_SMOOTH) != 0) {
flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
}
- if ((mesh->mpoly[i].flag & ME_FACE_SEL) != 0) {
+ if ((polys[i].flag & ME_FACE_SEL) != 0) {
flag |= SUBDIV_COARSE_FACE_FLAG_SELECT;
}
- if ((mesh->mpoly[i].flag & ME_HIDE) != 0) {
+ if (mr->hide_poly && mr->hide_poly[i]) {
flag |= SUBDIV_COARSE_FACE_FLAG_HIDDEN;
}
- flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
+ flags_data[i] = (uint)(polys[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
}
}
@@ -691,13 +694,20 @@ static void draw_subdiv_cache_extra_coarse_face_data_mapped(Mesh *mesh,
uint32_t *flags_data)
{
if (bm == nullptr) {
- draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data);
+ draw_subdiv_cache_extra_coarse_face_data_mesh(mr, mesh, flags_data);
return;
}
- for (int i = 0; i < mesh->totpoly; i++) {
+ const Span<MPoly> polys = mesh->polys();
+ for (const int i : polys.index_range()) {
BMFace *f = bm_original_face_get(mr, i);
- flags_data[i] = compute_coarse_face_flag(f, mr->efa_act);
+ /* Selection and hiding from bmesh. */
+ uint32_t flag = (f) ? compute_coarse_face_flag_bm(f, mr->efa_act) : 0;
+ /* Smooth from mesh. */
+ if ((polys[i].flag & ME_SMOOTH) != 0) {
+ flag |= SUBDIV_COARSE_FACE_FLAG_SMOOTH;
+ }
+ flags_data[i] = (uint)(polys[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET);
}
}
@@ -722,11 +732,11 @@ static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cach
if (mr->extract_type == MR_EXTRACT_BMESH) {
draw_subdiv_cache_extra_coarse_face_data_bm(cache->bm, mr->efa_act, flags_data);
}
- else if (mr->extract_type == MR_EXTRACT_MAPPED) {
+ else if (mr->p_origindex != nullptr) {
draw_subdiv_cache_extra_coarse_face_data_mapped(mesh, cache->bm, mr, flags_data);
}
else {
- draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data);
+ draw_subdiv_cache_extra_coarse_face_data_mesh(mr, mesh, flags_data);
}
/* Make sure updated data is re-uploaded. */
@@ -801,15 +811,15 @@ struct DRWCacheBuildingContext {
};
static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_context,
- const int num_vertices,
+ const int num_verts,
const int num_edges,
const int num_loops,
- const int num_polygons,
+ const int num_polys,
const int *subdiv_polygon_offset)
{
/* num_loops does not take into account meshes with only loose geometry, which might be meshes
- * used as custom bone shapes, so let's check the num_vertices also. */
- if (num_vertices == 0 && num_loops == 0) {
+ * used as custom bone shapes, so let's check the num_verts also. */
+ if (num_verts == 0 && num_loops == 0) {
return false;
}
@@ -820,12 +830,12 @@ static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_con
if (num_loops != 0) {
cache->num_subdiv_edges = (uint)num_edges;
cache->num_subdiv_loops = (uint)num_loops;
- cache->num_subdiv_verts = (uint)num_vertices;
- cache->num_subdiv_quads = (uint)num_polygons;
+ cache->num_subdiv_verts = (uint)num_verts;
+ cache->num_subdiv_quads = (uint)num_polys;
cache->subdiv_polygon_offset = static_cast<int *>(MEM_dupallocN(subdiv_polygon_offset));
}
- cache->may_have_loose_geom = num_vertices != 0 || num_edges != 0;
+ cache->may_have_loose_geom = num_verts != 0 || num_edges != 0;
/* Initialize cache buffers, prefer dynamic usage so we can reuse memory on the host even after
* it was sent to the device, since we may use the data while building other buffers on the CPU
@@ -876,7 +886,7 @@ static bool draw_subdiv_topology_info_cb(const SubdivForeachContext *foreach_con
if (cache->num_subdiv_verts) {
ctx->vert_origindex_map = static_cast<int *>(
MEM_mallocN(cache->num_subdiv_verts * sizeof(int), "subdiv_vert_origindex_map"));
- for (int i = 0; i < num_vertices; i++) {
+ for (int i = 0; i < num_verts; i++) {
ctx->vert_origindex_map[i] = -1;
}
}
@@ -1089,6 +1099,7 @@ static bool draw_subdiv_build_cache(DRWSubdivCache *cache,
}
/* Only build polygon related data if we have polygons. */
+ const Span<MPoly> polys = mesh_eval->polys();
if (cache->num_subdiv_loops != 0) {
/* Build buffers for the PatchMap. */
draw_patch_map_build(&cache->gpu_patch_map, subdiv);
@@ -1102,7 +1113,7 @@ static bool draw_subdiv_build_cache(DRWSubdivCache *cache,
GPU_vertbuf_get_data(cache->fdots_patch_coords);
for (int i = 0; i < mesh_eval->totpoly; i++) {
const int ptex_face_index = cache->face_ptex_offset[i];
- if (mesh_eval->mpoly[i].totloop == 4) {
+ if (polys[i].totloop == 4) {
/* For quads, the center coordinate of the coarse face has `u = v = 0.5`. */
blender_fdots_patch_coords[i] = make_patch_coord(ptex_face_index, 0.5f, 0.5f);
}
@@ -1115,16 +1126,16 @@ static bool draw_subdiv_build_cache(DRWSubdivCache *cache,
}
cache->subdiv_polygon_offset_buffer = draw_subdiv_build_origindex_buffer(
- cache->subdiv_polygon_offset, mesh_eval->totpoly);
+ cache->subdiv_polygon_offset, polys.size());
cache->face_ptex_offset_buffer = draw_subdiv_build_origindex_buffer(cache->face_ptex_offset,
- mesh_eval->totpoly + 1);
+ polys.size() + 1);
build_vertex_face_adjacency_maps(cache);
}
cache->resolution = to_mesh_settings.resolution;
- cache->num_coarse_poly = mesh_eval->totpoly;
+ cache->num_coarse_poly = polys.size();
/* To avoid floating point precision issues when evaluating patches at patch boundaries,
* ensure that all loops sharing a vertex use the same patch coordinate. This could cause
@@ -1960,17 +1971,19 @@ static void draw_subdiv_cache_ensure_mat_offsets(DRWSubdivCache *cache,
return;
}
+ const blender::VArraySpan<int> material_indices = mesh_eval->attributes().lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+
/* Count number of subdivided polygons for each material. */
int *mat_start = static_cast<int *>(MEM_callocN(sizeof(int) * mat_len, "subdiv mat_start"));
int *subdiv_polygon_offset = cache->subdiv_polygon_offset;
/* TODO: parallel_reduce? */
for (int i = 0; i < mesh_eval->totpoly; i++) {
- const MPoly *mpoly = &mesh_eval->mpoly[i];
const int next_offset = (i == mesh_eval->totpoly - 1) ? number_of_quads :
subdiv_polygon_offset[i + 1];
const int quad_count = next_offset - subdiv_polygon_offset[i];
- const int mat_index = mpoly->mat_nr;
+ const int mat_index = material_indices[i];
mat_start[mat_index] += quad_count;
}
@@ -1989,8 +2002,7 @@ static void draw_subdiv_cache_ensure_mat_offsets(DRWSubdivCache *cache,
MEM_mallocN(sizeof(int) * mesh_eval->totpoly, "per_polygon_mat_offset"));
for (int i = 0; i < mesh_eval->totpoly; i++) {
- const MPoly *mpoly = &mesh_eval->mpoly[i];
- const int mat_index = mpoly->mat_nr;
+ const int mat_index = material_indices[i];
const int single_material_index = subdiv_polygon_offset[i];
const int material_offset = mat_end[mat_index];
const int next_offset = (i == mesh_eval->totpoly - 1) ? number_of_quads :
@@ -2019,6 +2031,7 @@ static bool draw_subdiv_create_requested_buffers(Object *ob,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
+ const bool do_cage,
const ToolSettings *ts,
const bool use_hide,
OpenSubdiv_EvaluatorCache *evaluator_cache)
@@ -2045,7 +2058,7 @@ static bool draw_subdiv_create_requested_buffers(Object *ob,
draw_subdiv_invalidate_evaluator_for_orco(subdiv, mesh_eval);
if (!BKE_subdiv_eval_begin_from_mesh(
- subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GLSL_COMPUTE, evaluator_cache)) {
+ subdiv, mesh_eval, nullptr, SUBDIV_EVALUATOR_TYPE_GPU, evaluator_cache)) {
/* This could happen in two situations:
* - OpenSubdiv is disabled.
* - Something totally bad happened, and OpenSubdiv rejected our
@@ -2062,9 +2075,8 @@ static bool draw_subdiv_create_requested_buffers(Object *ob,
return false;
}
- /* Edges which do not come from coarse edges should not be drawn in edit mode, only in object
- * mode when optimal display in turned off. */
- const bool optimal_display = runtime_data->use_optimal_display || is_editmode;
+ /* Edges which do not come from coarse edges should not be drawn in edit cage mode. */
+ const bool optimal_display = runtime_data->use_optimal_display || (is_editmode && !do_cage);
draw_cache->bm = bm;
draw_cache->mesh = mesh_eval;
@@ -2147,9 +2159,20 @@ void DRW_subdivide_loose_geom(DRWSubdivCache *subdiv_cache, MeshBufferCache *cac
int subd_vert_offset = 0;
/* Subdivide each loose coarse edge. */
+ const Span<MVert> coarse_verts = coarse_mesh->verts();
+ const Span<MEdge> coarse_edges = coarse_mesh->edges();
+
+ int *vert_to_edge_buffer;
+ MeshElemMap *vert_to_edge_map;
+ BKE_mesh_vert_edge_map_create(&vert_to_edge_map,
+ &vert_to_edge_buffer,
+ coarse_edges.data(),
+ coarse_mesh->totvert,
+ coarse_edges.size());
+
for (int i = 0; i < coarse_loose_edge_len; i++) {
const int coarse_edge_index = cache->loose_geom.edges[i];
- const MEdge *coarse_edge = &coarse_mesh->medge[cache->loose_geom.edges[i]];
+ const MEdge *coarse_edge = &coarse_edges[cache->loose_geom.edges[i]];
/* Perform interpolation of each vertex. */
for (int i = 0; i < resolution - 1; i++, subd_edge_offset++) {
@@ -2160,8 +2183,13 @@ void DRW_subdivide_loose_geom(DRWSubdivCache *subdiv_cache, MeshBufferCache *cac
DRWSubdivLooseVertex &subd_v1 = loose_subd_verts[subd_vert_offset];
subd_v1.coarse_vertex_index = (i == 0) ? coarse_edge->v1 : -1u;
const float u1 = i * inv_resolution_1;
- BKE_subdiv_mesh_interpolate_position_on_edge(
- coarse_mesh, coarse_edge, is_simple, u1, subd_v1.co);
+ BKE_subdiv_mesh_interpolate_position_on_edge(coarse_verts.data(),
+ coarse_edges.data(),
+ vert_to_edge_map,
+ coarse_edge_index,
+ is_simple,
+ u1,
+ subd_v1.co);
subd_edge.loose_subdiv_v1_index = subd_vert_offset++;
@@ -2169,17 +2197,25 @@ void DRW_subdivide_loose_geom(DRWSubdivCache *subdiv_cache, MeshBufferCache *cac
DRWSubdivLooseVertex &subd_v2 = loose_subd_verts[subd_vert_offset];
subd_v2.coarse_vertex_index = ((i + 1) == resolution - 1) ? coarse_edge->v2 : -1u;
const float u2 = (i + 1) * inv_resolution_1;
- BKE_subdiv_mesh_interpolate_position_on_edge(
- coarse_mesh, coarse_edge, is_simple, u2, subd_v2.co);
+ BKE_subdiv_mesh_interpolate_position_on_edge(coarse_verts.data(),
+ coarse_edges.data(),
+ vert_to_edge_map,
+ coarse_edge_index,
+ is_simple,
+ u2,
+ subd_v2.co);
subd_edge.loose_subdiv_v2_index = subd_vert_offset++;
}
}
+ MEM_freeN(vert_to_edge_buffer);
+ MEM_freeN(vert_to_edge_map);
+
/* Copy the remaining loose_verts. */
for (int i = 0; i < coarse_loose_vert_len; i++) {
const int coarse_vertex_index = cache->loose_geom.verts[i];
- const MVert &coarse_vertex = coarse_mesh->mvert[coarse_vertex_index];
+ const MVert &coarse_vertex = coarse_verts[coarse_vertex_index];
DRWSubdivLooseVertex &subd_v = loose_subd_verts[subd_vert_offset++];
subd_v.coarse_vertex_index = cache->loose_geom.verts[i];
@@ -2216,11 +2252,12 @@ void DRW_create_subdivision(Object *ob,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
+ const bool do_cage,
const ToolSettings *ts,
const bool use_hide)
{
if (g_evaluator_cache == nullptr) {
- g_evaluator_cache = openSubdiv_createEvaluatorCache(OPENSUBDIV_EVALUATOR_GLSL_COMPUTE);
+ g_evaluator_cache = openSubdiv_createEvaluatorCache(OPENSUBDIV_EVALUATOR_GPU);
}
#undef TIME_SUBDIV
@@ -2239,6 +2276,7 @@ void DRW_create_subdivision(Object *ob,
obmat,
do_final,
do_uvedit,
+ do_cage,
ts,
use_hide,
g_evaluator_cache)) {
diff --git a/source/blender/draw/intern/draw_color_management.cc b/source/blender/draw/intern/draw_color_management.cc
index bb11f1ab3ad..eab86226be5 100644
--- a/source/blender/draw/intern/draw_color_management.cc
+++ b/source/blender/draw/intern/draw_color_management.cc
@@ -169,7 +169,7 @@ void DRW_transform_none(GPUTexture *tex)
/* Draw as texture for final render (without immediate mode). */
GPUBatch *geom = DRW_cache_fullscreen_quad_get();
- GPU_batch_program_set_builtin(geom, GPU_SHADER_2D_IMAGE_COLOR);
+ GPU_batch_program_set_builtin(geom, GPU_SHADER_3D_IMAGE_COLOR);
GPU_batch_uniform_4f(geom, "color", 1.0f, 1.0f, 1.0f, 1.0f);
GPU_batch_texture_bind(geom, "image", tex);
diff --git a/source/blender/draw/intern/draw_command.cc b/source/blender/draw/intern/draw_command.cc
new file mode 100644
index 00000000000..ff69885b3b6
--- /dev/null
+++ b/source/blender/draw/intern/draw_command.cc
@@ -0,0 +1,600 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "GPU_batch.h"
+#include "GPU_capabilities.h"
+#include "GPU_compute.h"
+#include "GPU_debug.h"
+
+#include "draw_command.hh"
+#include "draw_shader.h"
+#include "draw_view.hh"
+
+#include <bitset>
+#include <sstream>
+
+namespace blender::draw::command {
+
+/* -------------------------------------------------------------------- */
+/** \name Commands Execution
+ * \{ */
+
+void ShaderBind::execute(RecordingState &state) const
+{
+ if (assign_if_different(state.shader, shader)) {
+ GPU_shader_bind(shader);
+ }
+}
+
+void ResourceBind::execute() const
+{
+ if (slot == -1) {
+ return;
+ }
+ switch (type) {
+ case ResourceBind::Type::Sampler:
+ GPU_texture_bind_ex(is_reference ? *texture_ref : texture, sampler, slot, false);
+ break;
+ case ResourceBind::Type::Image:
+ GPU_texture_image_bind(is_reference ? *texture_ref : texture, slot);
+ break;
+ case ResourceBind::Type::UniformBuf:
+ GPU_uniformbuf_bind(is_reference ? *uniform_buf_ref : uniform_buf, slot);
+ break;
+ case ResourceBind::Type::StorageBuf:
+ GPU_storagebuf_bind(is_reference ? *storage_buf_ref : storage_buf, slot);
+ break;
+ }
+}
+
+void PushConstant::execute(RecordingState &state) const
+{
+ if (location == -1) {
+ return;
+ }
+ switch (type) {
+ case PushConstant::Type::IntValue:
+ GPU_shader_uniform_vector_int(state.shader, location, comp_len, array_len, int4_value);
+ break;
+ case PushConstant::Type::IntReference:
+ GPU_shader_uniform_vector_int(state.shader, location, comp_len, array_len, int_ref);
+ break;
+ case PushConstant::Type::FloatValue:
+ GPU_shader_uniform_vector(state.shader, location, comp_len, array_len, float4_value);
+ break;
+ case PushConstant::Type::FloatReference:
+ GPU_shader_uniform_vector(state.shader, location, comp_len, array_len, float_ref);
+ break;
+ }
+}
+
+void Draw::execute(RecordingState &state) const
+{
+ state.front_facing_set(handle.has_inverted_handedness());
+
+ if (GPU_shader_draw_parameters_support() == false) {
+ GPU_batch_resource_id_buf_set(batch, state.resource_id_buf);
+ }
+
+ GPU_batch_set_shader(batch, state.shader);
+ GPU_batch_draw_advanced(batch, vertex_first, vertex_len, 0, instance_len);
+}
+
+void DrawMulti::execute(RecordingState &state) const
+{
+ DrawMultiBuf::DrawCommandBuf &indirect_buf = multi_draw_buf->command_buf_;
+ DrawMultiBuf::DrawGroupBuf &groups = multi_draw_buf->group_buf_;
+
+ uint group_index = this->group_first;
+ while (group_index != (uint)-1) {
+ const DrawGroup &group = groups[group_index];
+
+ if (group.vertex_len > 0) {
+ if (GPU_shader_draw_parameters_support() == false) {
+ GPU_batch_resource_id_buf_set(group.gpu_batch, state.resource_id_buf);
+ }
+
+ GPU_batch_set_shader(group.gpu_batch, state.shader);
+
+ constexpr intptr_t stride = sizeof(DrawCommand);
+ /* We have 2 indirect command reserved per draw group. */
+ intptr_t offset = stride * group_index * 2;
+
+ /* Draw negatively scaled geometry first. */
+ if (group.len - group.front_facing_len > 0) {
+ state.front_facing_set(true);
+ GPU_batch_draw_indirect(group.gpu_batch, indirect_buf, offset);
+ }
+
+ if (group.front_facing_len > 0) {
+ state.front_facing_set(false);
+ GPU_batch_draw_indirect(group.gpu_batch, indirect_buf, offset + stride);
+ }
+ }
+
+ group_index = group.next;
+ }
+}
+
+void DrawIndirect::execute(RecordingState &state) const
+{
+ state.front_facing_set(handle.has_inverted_handedness());
+
+ GPU_batch_draw_indirect(batch, *indirect_buf, 0);
+}
+
+void Dispatch::execute(RecordingState &state) const
+{
+ if (is_reference) {
+ GPU_compute_dispatch(state.shader, size_ref->x, size_ref->y, size_ref->z);
+ }
+ else {
+ GPU_compute_dispatch(state.shader, size.x, size.y, size.z);
+ }
+}
+
+void DispatchIndirect::execute(RecordingState &state) const
+{
+ GPU_compute_dispatch_indirect(state.shader, *indirect_buf);
+}
+
+void Barrier::execute() const
+{
+ GPU_memory_barrier(type);
+}
+
+void Clear::execute() const
+{
+ GPUFrameBuffer *fb = GPU_framebuffer_active_get();
+ GPU_framebuffer_clear(fb, (eGPUFrameBufferBits)clear_channels, color, depth, stencil);
+}
+
+void StateSet::execute(RecordingState &recording_state) const
+{
+ /**
+ * Does not support locked state for the moment and never should.
+ * Better implement a less hacky selection!
+ */
+ BLI_assert(DST.state_lock == 0);
+
+ if (!assign_if_different(recording_state.pipeline_state, new_state)) {
+ return;
+ }
+
+ /* Keep old API working. Keep the state tracking in sync. */
+ /* TODO(fclem): Move at the end of a pass. */
+ DST.state = new_state;
+
+ GPU_state_set(to_write_mask(new_state),
+ to_blend(new_state),
+ to_face_cull_test(new_state),
+ to_depth_test(new_state),
+ to_stencil_test(new_state),
+ to_stencil_op(new_state),
+ to_provoking_vertex(new_state));
+
+ if (new_state & DRW_STATE_SHADOW_OFFSET) {
+ GPU_shadow_offset(true);
+ }
+ else {
+ GPU_shadow_offset(false);
+ }
+
+ /* TODO: this should be part of shader state. */
+ if (new_state & DRW_STATE_CLIP_PLANES) {
+ GPU_clip_distances(recording_state.view_clip_plane_count);
+ }
+ else {
+ GPU_clip_distances(0);
+ }
+
+ if (new_state & DRW_STATE_IN_FRONT_SELECT) {
+ /* XXX `GPU_depth_range` is not a perfect solution
+ * since very distant geometries can still be occluded.
+ * Also the depth test precision of these geometries is impaired.
+ * However, it solves the selection for the vast majority of cases. */
+ GPU_depth_range(0.0f, 0.01f);
+ }
+ else {
+ GPU_depth_range(0.0f, 1.0f);
+ }
+
+ if (new_state & DRW_STATE_PROGRAM_POINT_SIZE) {
+ GPU_program_point_size(true);
+ }
+ else {
+ GPU_program_point_size(false);
+ }
+}
+
+void StencilSet::execute() const
+{
+ GPU_stencil_write_mask_set(write_mask);
+ GPU_stencil_compare_mask_set(compare_mask);
+ GPU_stencil_reference_set(reference);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Commands Serialization for debugging
+ * \{ */
+
+std::string ShaderBind::serialize() const
+{
+ return std::string(".shader_bind(") + GPU_shader_get_name(shader) + ")";
+}
+
+std::string ResourceBind::serialize() const
+{
+ switch (type) {
+ case Type::Sampler:
+ return std::string(".bind_texture") + (is_reference ? "_ref" : "") + "(" +
+ std::to_string(slot) +
+ (sampler != GPU_SAMPLER_MAX ? ", sampler=" + std::to_string(sampler) : "") + ")";
+ case Type::Image:
+ return std::string(".bind_image") + (is_reference ? "_ref" : "") + "(" +
+ std::to_string(slot) + ")";
+ case Type::UniformBuf:
+ return std::string(".bind_uniform_buf") + (is_reference ? "_ref" : "") + "(" +
+ std::to_string(slot) + ")";
+ case Type::StorageBuf:
+ return std::string(".bind_storage_buf") + (is_reference ? "_ref" : "") + "(" +
+ std::to_string(slot) + ")";
+ default:
+ BLI_assert_unreachable();
+ return "";
+ }
+}
+
+std::string PushConstant::serialize() const
+{
+ std::stringstream ss;
+ for (int i = 0; i < array_len; i++) {
+ switch (comp_len) {
+ case 1:
+ switch (type) {
+ case Type::IntValue:
+ ss << int1_value;
+ break;
+ case Type::IntReference:
+ ss << int_ref[i];
+ break;
+ case Type::FloatValue:
+ ss << float1_value;
+ break;
+ case Type::FloatReference:
+ ss << float_ref[i];
+ break;
+ }
+ break;
+ case 2:
+ switch (type) {
+ case Type::IntValue:
+ ss << int2_value;
+ break;
+ case Type::IntReference:
+ ss << int2_ref[i];
+ break;
+ case Type::FloatValue:
+ ss << float2_value;
+ break;
+ case Type::FloatReference:
+ ss << float2_ref[i];
+ break;
+ }
+ break;
+ case 3:
+ switch (type) {
+ case Type::IntValue:
+ ss << int3_value;
+ break;
+ case Type::IntReference:
+ ss << int3_ref[i];
+ break;
+ case Type::FloatValue:
+ ss << float3_value;
+ break;
+ case Type::FloatReference:
+ ss << float3_ref[i];
+ break;
+ }
+ break;
+ case 4:
+ switch (type) {
+ case Type::IntValue:
+ ss << int4_value;
+ break;
+ case Type::IntReference:
+ ss << int4_ref[i];
+ break;
+ case Type::FloatValue:
+ ss << float4_value;
+ break;
+ case Type::FloatReference:
+ ss << float4_ref[i];
+ break;
+ }
+ break;
+ case 16:
+ switch (type) {
+ case Type::IntValue:
+ case Type::IntReference:
+ BLI_assert_unreachable();
+ break;
+ case Type::FloatValue:
+ ss << *reinterpret_cast<const float4x4 *>(&float4_value);
+ break;
+ case Type::FloatReference:
+ ss << *float4x4_ref;
+ break;
+ }
+ break;
+ }
+ if (i < array_len - 1) {
+ ss << ", ";
+ }
+ }
+
+ return std::string(".push_constant(") + std::to_string(location) + ", data=" + ss.str() + ")";
+}
+
+std::string Draw::serialize() const
+{
+ std::string inst_len = (instance_len == (uint)-1) ? "from_batch" : std::to_string(instance_len);
+ std::string vert_len = (vertex_len == (uint)-1) ? "from_batch" : std::to_string(vertex_len);
+ std::string vert_first = (vertex_first == (uint)-1) ? "from_batch" :
+ std::to_string(vertex_first);
+ return std::string(".draw(inst_len=") + inst_len + ", vert_len=" + vert_len +
+ ", vert_first=" + vert_first + ", res_id=" + std::to_string(handle.resource_index()) +
+ ")";
+}
+
+std::string DrawMulti::serialize(std::string line_prefix) const
+{
+ DrawMultiBuf::DrawGroupBuf &groups = multi_draw_buf->group_buf_;
+
+ MutableSpan<DrawPrototype> prototypes(multi_draw_buf->prototype_buf_.data(),
+ multi_draw_buf->prototype_count_);
+
+ /* This emulates the GPU sorting but without the unstable draw order. */
+ std::sort(
+ prototypes.begin(), prototypes.end(), [](const DrawPrototype &a, const DrawPrototype &b) {
+ return (a.group_id < b.group_id) ||
+ (a.group_id == b.group_id && a.resource_handle > b.resource_handle);
+ });
+
+ /* Compute prefix sum to have correct offsets. */
+ uint prefix_sum = 0u;
+ for (DrawGroup &group : groups) {
+ group.start = prefix_sum;
+ prefix_sum += group.front_proto_len + group.back_proto_len;
+ }
+
+ std::stringstream ss;
+
+ uint group_len = 0;
+ uint group_index = this->group_first;
+ while (group_index != (uint)-1) {
+ const DrawGroup &grp = groups[group_index];
+
+ ss << std::endl << line_prefix << " .group(id=" << group_index << ", len=" << grp.len << ")";
+
+ intptr_t offset = grp.start;
+
+ if (grp.back_proto_len > 0) {
+ for (DrawPrototype &proto : prototypes.slice({offset, grp.back_proto_len})) {
+ BLI_assert(proto.group_id == group_index);
+ ResourceHandle handle(proto.resource_handle);
+ BLI_assert(handle.has_inverted_handedness());
+ ss << std::endl
+ << line_prefix << " .proto(instance_len=" << std::to_string(proto.instance_len)
+ << ", resource_id=" << std::to_string(handle.resource_index()) << ", back_face)";
+ }
+ offset += grp.back_proto_len;
+ }
+
+ if (grp.front_proto_len > 0) {
+ for (DrawPrototype &proto : prototypes.slice({offset, grp.front_proto_len})) {
+ BLI_assert(proto.group_id == group_index);
+ ResourceHandle handle(proto.resource_handle);
+ BLI_assert(!handle.has_inverted_handedness());
+ ss << std::endl
+ << line_prefix << " .proto(instance_len=" << std::to_string(proto.instance_len)
+ << ", resource_id=" << std::to_string(handle.resource_index()) << ", front_face)";
+ }
+ }
+
+ group_index = grp.next;
+ group_len++;
+ }
+
+ ss << std::endl;
+
+ return line_prefix + ".draw_multi(" + std::to_string(group_len) + ")" + ss.str();
+}
+
+std::string DrawIndirect::serialize() const
+{
+ return std::string(".draw_indirect()");
+}
+
+std::string Dispatch::serialize() const
+{
+ int3 sz = is_reference ? *size_ref : size;
+ return std::string(".dispatch") + (is_reference ? "_ref" : "") + "(" + std::to_string(sz.x) +
+ ", " + std::to_string(sz.y) + ", " + std::to_string(sz.z) + ")";
+}
+
+std::string DispatchIndirect::serialize() const
+{
+ return std::string(".dispatch_indirect()");
+}
+
+std::string Barrier::serialize() const
+{
+ /* TODO(@fclem): Better serialization... */
+ return std::string(".barrier(") + std::to_string(type) + ")";
+}
+
+std::string Clear::serialize() const
+{
+ std::stringstream ss;
+ if (eGPUFrameBufferBits(clear_channels) & GPU_COLOR_BIT) {
+ ss << "color=" << color;
+ if (eGPUFrameBufferBits(clear_channels) & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) {
+ ss << ", ";
+ }
+ }
+ if (eGPUFrameBufferBits(clear_channels) & GPU_DEPTH_BIT) {
+ ss << "depth=" << depth;
+ if (eGPUFrameBufferBits(clear_channels) & GPU_STENCIL_BIT) {
+ ss << ", ";
+ }
+ }
+ if (eGPUFrameBufferBits(clear_channels) & GPU_STENCIL_BIT) {
+ ss << "stencil=0b" << std::bitset<8>(stencil) << ")";
+ }
+ return std::string(".clear(") + ss.str() + ")";
+}
+
+std::string StateSet::serialize() const
+{
+ /* TODO(@fclem): Better serialization... */
+ return std::string(".state_set(") + std::to_string(new_state) + ")";
+}
+
+std::string StencilSet::serialize() const
+{
+ std::stringstream ss;
+ ss << ".stencil_set(write_mask=0b" << std::bitset<8>(write_mask) << ", compare_mask=0b"
+ << std::bitset<8>(compare_mask) << ", reference=0b" << std::bitset<8>(reference);
+ return ss.str();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Commands buffers binding / command / resource ID generation
+ * \{ */
+
+void DrawCommandBuf::bind(RecordingState &state,
+ Vector<Header, 0> &headers,
+ Vector<Undetermined, 0> &commands)
+{
+ UNUSED_VARS(headers, commands);
+
+ resource_id_count_ = 0;
+
+ for (const Header &header : headers) {
+ if (header.type != Type::Draw) {
+ continue;
+ }
+
+ Draw &cmd = commands[header.index].draw;
+
+ int batch_vert_len, batch_vert_first, batch_base_index, batch_inst_len;
+ /* Now that GPUBatches are guaranteed to be finished, extract their parameters. */
+ GPU_batch_draw_parameter_get(
+ cmd.batch, &batch_vert_len, &batch_vert_first, &batch_base_index, &batch_inst_len);
+ /* Instancing attributes are not supported using the new pipeline since we use the base
+ * instance to set the correct resource_id. Workaround is a storage_buf + gl_InstanceID. */
+ BLI_assert(batch_inst_len == 1);
+
+ if (cmd.vertex_len == (uint)-1) {
+ cmd.vertex_len = batch_vert_len;
+ }
+
+ if (cmd.handle.raw > 0) {
+ /* Save correct offset to start of resource_id buffer region for this draw. */
+ uint instance_first = resource_id_count_;
+ resource_id_count_ += cmd.instance_len;
+ /* Ensure the buffer is big enough. */
+ resource_id_buf_.get_or_resize(resource_id_count_ - 1);
+
+ /* Copy the resource id for all instances. */
+ uint index = cmd.handle.resource_index();
+ for (int i = instance_first; i < (instance_first + cmd.instance_len); i++) {
+ resource_id_buf_[i] = index;
+ }
+ }
+ }
+
+ resource_id_buf_.push_update();
+
+ if (GPU_shader_draw_parameters_support() == false) {
+ state.resource_id_buf = resource_id_buf_;
+ }
+ else {
+ GPU_storagebuf_bind(resource_id_buf_, DRW_RESOURCE_ID_SLOT);
+ }
+}
+
+void DrawMultiBuf::bind(RecordingState &state,
+ Vector<Header, 0> &headers,
+ Vector<Undetermined, 0> &commands,
+ VisibilityBuf &visibility_buf)
+{
+ UNUSED_VARS(headers, commands);
+
+ GPU_debug_group_begin("DrawMultiBuf.bind");
+
+ resource_id_count_ = 0u;
+ for (DrawGroup &group : MutableSpan<DrawGroup>(group_buf_.data(), group_count_)) {
+ /* Compute prefix sum of all instance of previous group. */
+ group.start = resource_id_count_;
+ resource_id_count_ += group.len;
+
+ int batch_inst_len;
+ /* Now that GPUBatches are guaranteed to be finished, extract their parameters. */
+ GPU_batch_draw_parameter_get(group.gpu_batch,
+ &group.vertex_len,
+ &group.vertex_first,
+ &group.base_index,
+ &batch_inst_len);
+
+ /* Instancing attributes are not supported using the new pipeline since we use the base
+ * instance to set the correct resource_id. Workaround is a storage_buf + gl_InstanceID. */
+ BLI_assert(batch_inst_len == 1);
+ UNUSED_VARS_NDEBUG(batch_inst_len);
+
+ /* Now that we got the batch information, we can set the counters to 0. */
+ group.total_counter = group.front_facing_counter = group.back_facing_counter = 0;
+ }
+
+ group_buf_.push_update();
+ prototype_buf_.push_update();
+ /* Allocate enough for the expansion pass. */
+ resource_id_buf_.get_or_resize(resource_id_count_);
+ /* Two command per group. */
+ command_buf_.get_or_resize(group_count_ * 2);
+
+ if (prototype_count_ > 0) {
+ GPUShader *shader = DRW_shader_draw_command_generate_get();
+ GPU_shader_bind(shader);
+ GPU_shader_uniform_1i(shader, "prototype_len", prototype_count_);
+ GPU_storagebuf_bind(group_buf_, GPU_shader_get_ssbo(shader, "group_buf"));
+ GPU_storagebuf_bind(visibility_buf, GPU_shader_get_ssbo(shader, "visibility_buf"));
+ GPU_storagebuf_bind(prototype_buf_, GPU_shader_get_ssbo(shader, "prototype_buf"));
+ GPU_storagebuf_bind(command_buf_, GPU_shader_get_ssbo(shader, "command_buf"));
+ GPU_storagebuf_bind(resource_id_buf_, DRW_RESOURCE_ID_SLOT);
+ GPU_compute_dispatch(shader, divide_ceil_u(prototype_count_, DRW_COMMAND_GROUP_SIZE), 1, 1);
+ if (GPU_shader_draw_parameters_support() == false) {
+ GPU_memory_barrier(GPU_BARRIER_VERTEX_ATTRIB_ARRAY);
+ state.resource_id_buf = resource_id_buf_;
+ }
+ else {
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+ }
+
+ GPU_debug_group_end();
+}
+
+/** \} */
+
+}; // namespace blender::draw::command
diff --git a/source/blender/draw/intern/draw_command.hh b/source/blender/draw/intern/draw_command.hh
new file mode 100644
index 00000000000..46a9199a267
--- /dev/null
+++ b/source/blender/draw/intern/draw_command.hh
@@ -0,0 +1,534 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+#pragma once
+
+/** \file
+ * \ingroup draw
+ *
+ * Commands stored inside draw passes. Converted into GPU commands upon pass submission.
+ *
+ * Draw calls (primitive rendering commands) are managed by either `DrawCommandBuf` or
+ * `DrawMultiBuf`. See implementation details at their definition.
+ */
+
+#include "BKE_global.h"
+#include "BLI_map.hh"
+#include "DRW_gpu_wrapper.hh"
+
+#include "draw_command_shared.hh"
+#include "draw_handle.hh"
+#include "draw_state.h"
+#include "draw_view.hh"
+
+namespace blender::draw::command {
+
+class DrawCommandBuf;
+class DrawMultiBuf;
+
+/* -------------------------------------------------------------------- */
+/** \name Recording State
+ * \{ */
+
+/**
+ * Command recording state.
+ * Keep track of several states and avoid redundant state changes.
+ */
+struct RecordingState {
+ GPUShader *shader = nullptr;
+ bool front_facing = true;
+ bool inverted_view = false;
+ DRWState pipeline_state = DRW_STATE_NO_DRAW;
+ int view_clip_plane_count = 0;
+ /** Used for gl_BaseInstance workaround. */
+ GPUStorageBuf *resource_id_buf = nullptr;
+
+ void front_facing_set(bool facing)
+ {
+ /* Facing is inverted if view is not in expected handedness. */
+ facing = this->inverted_view == facing;
+ /* Remove redundant changes. */
+ if (assign_if_different(this->front_facing, facing)) {
+ GPU_front_facing(!facing);
+ }
+ }
+
+ void cleanup()
+ {
+ if (front_facing == false) {
+ GPU_front_facing(false);
+ }
+
+ if (G.debug & G_DEBUG_GPU) {
+ GPU_storagebuf_unbind_all();
+ GPU_texture_image_unbind_all();
+ GPU_texture_unbind_all();
+ GPU_uniformbuf_unbind_all();
+ }
+ }
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Regular Commands
+ * \{ */
+
+enum class Type : uint8_t {
+ /**
+ * None Type commands are either uninitialized or are repurposed as data storage.
+ * They are skipped during submission.
+ */
+ None = 0,
+
+ /** Commands stored as Undetermined in regular command buffer. */
+ Barrier,
+ Clear,
+ Dispatch,
+ DispatchIndirect,
+ Draw,
+ DrawIndirect,
+ PushConstant,
+ ResourceBind,
+ ShaderBind,
+ StateSet,
+ StencilSet,
+
+ /** Special commands stored in separate buffers. */
+ SubPass,
+ DrawMulti,
+};
+
+/**
+ * The index of the group is implicit since it is known by the one who want to
+ * access it. This also allows to have an indexed object to split the command
+ * stream.
+ */
+struct Header {
+ /** Command type. */
+ Type type;
+ /** Command index in command heap of this type. */
+ uint index;
+};
+
+struct ShaderBind {
+ GPUShader *shader;
+
+ void execute(RecordingState &state) const;
+ std::string serialize() const;
+};
+
+struct ResourceBind {
+ eGPUSamplerState sampler;
+ int slot;
+ bool is_reference;
+
+ enum class Type : uint8_t {
+ Sampler = 0,
+ Image,
+ UniformBuf,
+ StorageBuf,
+ } type;
+
+ union {
+ /** TODO: Use draw::Texture|StorageBuffer|UniformBuffer as resources as they will give more
+ * debug info. */
+ GPUUniformBuf *uniform_buf;
+ GPUUniformBuf **uniform_buf_ref;
+ GPUStorageBuf *storage_buf;
+ GPUStorageBuf **storage_buf_ref;
+ /** NOTE: Texture is used for both Sampler and Image binds. */
+ GPUTexture *texture;
+ GPUTexture **texture_ref;
+ };
+
+ ResourceBind() = default;
+
+ ResourceBind(int slot_, GPUUniformBuf *res)
+ : slot(slot_), is_reference(false), type(Type::UniformBuf), uniform_buf(res){};
+ ResourceBind(int slot_, GPUUniformBuf **res)
+ : slot(slot_), is_reference(true), type(Type::UniformBuf), uniform_buf_ref(res){};
+ ResourceBind(int slot_, GPUStorageBuf *res)
+ : slot(slot_), is_reference(false), type(Type::StorageBuf), storage_buf(res){};
+ ResourceBind(int slot_, GPUStorageBuf **res)
+ : slot(slot_), is_reference(true), type(Type::StorageBuf), storage_buf_ref(res){};
+ ResourceBind(int slot_, draw::Image *res)
+ : slot(slot_), is_reference(false), type(Type::Image), texture(draw::as_texture(res)){};
+ ResourceBind(int slot_, draw::Image **res)
+ : slot(slot_), is_reference(true), type(Type::Image), texture_ref(draw::as_texture(res)){};
+ ResourceBind(int slot_, GPUTexture *res, eGPUSamplerState state)
+ : sampler(state), slot(slot_), is_reference(false), type(Type::Sampler), texture(res){};
+ ResourceBind(int slot_, GPUTexture **res, eGPUSamplerState state)
+ : sampler(state), slot(slot_), is_reference(true), type(Type::Sampler), texture_ref(res){};
+
+ void execute() const;
+ std::string serialize() const;
+};
+
+struct PushConstant {
+ int location;
+ uint8_t array_len;
+ uint8_t comp_len;
+ enum class Type : uint8_t {
+ IntValue = 0,
+ FloatValue,
+ IntReference,
+ FloatReference,
+ } type;
+ /**
+ * IMPORTANT: Data is at the end of the struct as it can span over the next commands.
+ * These next commands are not real commands but just memory to hold the data and are not
+ * referenced by any Command::Header.
+ * This is a hack to support float4x4 copy.
+ */
+ union {
+ int int1_value;
+ int2 int2_value;
+ int3 int3_value;
+ int4 int4_value;
+ float float1_value;
+ float2 float2_value;
+ float3 float3_value;
+ float4 float4_value;
+ const int *int_ref;
+ const int2 *int2_ref;
+ const int3 *int3_ref;
+ const int4 *int4_ref;
+ const float *float_ref;
+ const float2 *float2_ref;
+ const float3 *float3_ref;
+ const float4 *float4_ref;
+ const float4x4 *float4x4_ref;
+ };
+
+ PushConstant() = default;
+
+ PushConstant(int loc, const float &val)
+ : location(loc), array_len(1), comp_len(1), type(Type::FloatValue), float1_value(val){};
+ PushConstant(int loc, const float2 &val)
+ : location(loc), array_len(1), comp_len(2), type(Type::FloatValue), float2_value(val){};
+ PushConstant(int loc, const float3 &val)
+ : location(loc), array_len(1), comp_len(3), type(Type::FloatValue), float3_value(val){};
+ PushConstant(int loc, const float4 &val)
+ : location(loc), array_len(1), comp_len(4), type(Type::FloatValue), float4_value(val){};
+
+ PushConstant(int loc, const int &val)
+ : location(loc), array_len(1), comp_len(1), type(Type::IntValue), int1_value(val){};
+ PushConstant(int loc, const int2 &val)
+ : location(loc), array_len(1), comp_len(2), type(Type::IntValue), int2_value(val){};
+ PushConstant(int loc, const int3 &val)
+ : location(loc), array_len(1), comp_len(3), type(Type::IntValue), int3_value(val){};
+ PushConstant(int loc, const int4 &val)
+ : location(loc), array_len(1), comp_len(4), type(Type::IntValue), int4_value(val){};
+
+ PushConstant(int loc, const float *val, int arr)
+ : location(loc), array_len(arr), comp_len(1), type(Type::FloatReference), float_ref(val){};
+ PushConstant(int loc, const float2 *val, int arr)
+ : location(loc), array_len(arr), comp_len(2), type(Type::FloatReference), float2_ref(val){};
+ PushConstant(int loc, const float3 *val, int arr)
+ : location(loc), array_len(arr), comp_len(3), type(Type::FloatReference), float3_ref(val){};
+ PushConstant(int loc, const float4 *val, int arr)
+ : location(loc), array_len(arr), comp_len(4), type(Type::FloatReference), float4_ref(val){};
+ PushConstant(int loc, const float4x4 *val)
+ : location(loc), array_len(1), comp_len(16), type(Type::FloatReference), float4x4_ref(val){};
+
+ PushConstant(int loc, const int *val, int arr)
+ : location(loc), array_len(arr), comp_len(1), type(Type::IntReference), int_ref(val){};
+ PushConstant(int loc, const int2 *val, int arr)
+ : location(loc), array_len(arr), comp_len(2), type(Type::IntReference), int2_ref(val){};
+ PushConstant(int loc, const int3 *val, int arr)
+ : location(loc), array_len(arr), comp_len(3), type(Type::IntReference), int3_ref(val){};
+ PushConstant(int loc, const int4 *val, int arr)
+ : location(loc), array_len(arr), comp_len(4), type(Type::IntReference), int4_ref(val){};
+
+ void execute(RecordingState &state) const;
+ std::string serialize() const;
+};
+
+struct Draw {
+ GPUBatch *batch;
+ uint instance_len;
+ uint vertex_len;
+ uint vertex_first;
+ ResourceHandle handle;
+
+ void execute(RecordingState &state) const;
+ std::string serialize() const;
+};
+
+struct DrawMulti {
+ GPUBatch *batch;
+ DrawMultiBuf *multi_draw_buf;
+ uint group_first;
+ uint uuid;
+
+ void execute(RecordingState &state) const;
+ std::string serialize(std::string line_prefix) const;
+};
+
+struct DrawIndirect {
+ GPUBatch *batch;
+ GPUStorageBuf **indirect_buf;
+ ResourceHandle handle;
+
+ void execute(RecordingState &state) const;
+ std::string serialize() const;
+};
+
+struct Dispatch {
+ bool is_reference;
+ union {
+ int3 size;
+ int3 *size_ref;
+ };
+
+ Dispatch() = default;
+
+ Dispatch(int3 group_len) : is_reference(false), size(group_len){};
+ Dispatch(int3 *group_len) : is_reference(true), size_ref(group_len){};
+
+ void execute(RecordingState &state) const;
+ std::string serialize() const;
+};
+
+struct DispatchIndirect {
+ GPUStorageBuf **indirect_buf;
+
+ void execute(RecordingState &state) const;
+ std::string serialize() const;
+};
+
+struct Barrier {
+ eGPUBarrier type;
+
+ void execute() const;
+ std::string serialize() const;
+};
+
+struct Clear {
+ uint8_t clear_channels; /* #eGPUFrameBufferBits. But want to save some bits. */
+ uint8_t stencil;
+ float depth;
+ float4 color;
+
+ void execute() const;
+ std::string serialize() const;
+};
+
+struct StateSet {
+ DRWState new_state;
+
+ void execute(RecordingState &state) const;
+ std::string serialize() const;
+};
+
+struct StencilSet {
+ uint write_mask;
+ uint compare_mask;
+ uint reference;
+
+ void execute() const;
+ std::string serialize() const;
+};
+
+union Undetermined {
+ ShaderBind shader_bind;
+ ResourceBind resource_bind;
+ PushConstant push_constant;
+ Draw draw;
+ DrawMulti draw_multi;
+ DrawIndirect draw_indirect;
+ Dispatch dispatch;
+ DispatchIndirect dispatch_indirect;
+ Barrier barrier;
+ Clear clear;
+ StateSet state_set;
+ StencilSet stencil_set;
+};
+
+/** Try to keep the command size as low as possible for performance. */
+BLI_STATIC_ASSERT(sizeof(Undetermined) <= 24, "One of the command type is too large.")
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw Commands
+ *
+ * A draw command buffer used to issue single draw commands without instance merging or any
+ * other optimizations.
+ *
+ * It still uses a ResourceIdBuf to keep the same shader interface as multi draw commands.
+ *
+ * \{ */
+
+class DrawCommandBuf {
+ friend Manager;
+
+ private:
+ using ResourceIdBuf = StorageArrayBuffer<uint, 128, false>;
+
+ /** Array of resource id. One per instance. Generated on GPU and send to GPU. */
+ ResourceIdBuf resource_id_buf_;
+ /** Used items in the resource_id_buf_. Not it's allocated length. */
+ uint resource_id_count_ = 0;
+
+ public:
+ void clear(){};
+
+ void append_draw(Vector<Header, 0> &headers,
+ Vector<Undetermined, 0> &commands,
+ GPUBatch *batch,
+ uint instance_len,
+ uint vertex_len,
+ uint vertex_first,
+ ResourceHandle handle)
+ {
+ vertex_first = vertex_first != -1 ? vertex_first : 0;
+ instance_len = instance_len != -1 ? instance_len : 1;
+
+ int64_t index = commands.append_and_get_index({});
+ headers.append({Type::Draw, static_cast<uint>(index)});
+ commands[index].draw = {batch, instance_len, vertex_len, vertex_first, handle};
+ }
+
+ void bind(RecordingState &state, Vector<Header, 0> &headers, Vector<Undetermined, 0> &commands);
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Multi Draw Commands
+ *
+ * For efficient rendering of large scene we strive to minimize the number of draw call and state
+ * changes. To this end, we group many rendering commands and sort them per render state using
+ * `DrawGroup` as a container. This is done automatically for any successive commands with the
+ * same state.
+ *
+ * A `DrawGroup` is the combination of a `GPUBatch` (VBO state) and a `command::DrawMulti`
+ * (Pipeline State).
+ *
+ * Inside each `DrawGroup` all instances of a same `GPUBatch` is merged into a single indirect
+ * command.
+ *
+ * To support this arbitrary reordering, we only need to know the offset of all the commands for a
+ * specific `DrawGroup`. This is done on CPU by doing a simple prefix sum. The result is pushed to
+ * GPU and used on CPU to issue the right command indirect.
+ *
+ * Each draw command is stored in an unsorted array of `DrawPrototype` and sent directly to the
+ * GPU.
+ *
+ * A command generation compute shader then go over each `DrawPrototype`. For each it adds it (or
+ * not depending on visibility) to the correct draw command using the offset of the `DrawGroup`
+ * computed on CPU. After that, it also outputs one resource ID for each instance inside a
+ * `DrawPrototype`.
+ *
+ * \{ */
+
+class DrawMultiBuf {
+ friend Manager;
+ friend DrawMulti;
+
+ private:
+ using DrawGroupBuf = StorageArrayBuffer<DrawGroup, 16>;
+ using DrawPrototypeBuf = StorageArrayBuffer<DrawPrototype, 16>;
+ using DrawCommandBuf = StorageArrayBuffer<DrawCommand, 16, true>;
+ using ResourceIdBuf = StorageArrayBuffer<uint, 128, true>;
+
+ using DrawGroupKey = std::pair<uint, GPUBatch *>;
+ using DrawGroupMap = Map<DrawGroupKey, uint>;
+ /** Maps a DrawMulti command and a gpu batch to their unique DrawGroup command. */
+ DrawGroupMap group_ids_;
+
+ /** DrawGroup Command heap. Uploaded to GPU for sorting. */
+ DrawGroupBuf group_buf_ = {"DrawGroupBuf"};
+ /** Command Prototypes. Unsorted */
+ DrawPrototypeBuf prototype_buf_ = {"DrawPrototypeBuf"};
+ /** Command list generated by the sorting / compaction steps. Lives on GPU. */
+ DrawCommandBuf command_buf_ = {"DrawCommandBuf"};
+ /** Array of resource id. One per instance. Lives on GPU. */
+ ResourceIdBuf resource_id_buf_ = {"ResourceIdBuf"};
+ /** Give unique ID to each header so we can use that as hash key. */
+ uint header_id_counter_ = 0;
+ /** Number of groups inside group_buf_. */
+ uint group_count_ = 0;
+ /** Number of prototype command inside prototype_buf_. */
+ uint prototype_count_ = 0;
+ /** Used items in the resource_id_buf_. Not it's allocated length. */
+ uint resource_id_count_ = 0;
+
+ public:
+ void clear()
+ {
+ header_id_counter_ = 0;
+ group_count_ = 0;
+ prototype_count_ = 0;
+ group_ids_.clear();
+ }
+
+ void append_draw(Vector<Header, 0> &headers,
+ Vector<Undetermined, 0> &commands,
+ GPUBatch *batch,
+ uint instance_len,
+ uint vertex_len,
+ uint vertex_first,
+ ResourceHandle handle)
+ {
+ /* Unsupported for now. Use PassSimple. */
+ BLI_assert(vertex_first == 0 || vertex_first == -1);
+ BLI_assert(vertex_len == -1);
+ UNUSED_VARS_NDEBUG(vertex_len, vertex_first);
+
+ instance_len = instance_len != -1 ? instance_len : 1;
+
+ /* If there was some state changes since previous call, we have to create another command. */
+ if (headers.is_empty() || headers.last().type != Type::DrawMulti) {
+ uint index = commands.append_and_get_index({});
+ headers.append({Type::DrawMulti, index});
+ commands[index].draw_multi = {batch, this, (uint)-1, header_id_counter_++};
+ }
+
+ DrawMulti &cmd = commands.last().draw_multi;
+
+ uint &group_id = group_ids_.lookup_or_add(DrawGroupKey(cmd.uuid, batch), (uint)-1);
+
+ bool inverted = handle.has_inverted_handedness();
+
+ if (group_id == (uint)-1) {
+ uint new_group_id = group_count_++;
+
+ DrawGroup &group = group_buf_.get_or_resize(new_group_id);
+ group.next = cmd.group_first;
+ group.len = instance_len;
+ group.front_facing_len = inverted ? 0 : instance_len;
+ group.gpu_batch = batch;
+ group.front_proto_len = 0;
+ group.back_proto_len = 0;
+ /* For serialization only. */
+ (inverted ? group.back_proto_len : group.front_proto_len)++;
+ /* Append to list. */
+ cmd.group_first = new_group_id;
+ group_id = new_group_id;
+ }
+ else {
+ DrawGroup &group = group_buf_[group_id];
+ group.len += instance_len;
+ group.front_facing_len += inverted ? 0 : instance_len;
+ /* For serialization only. */
+ (inverted ? group.back_proto_len : group.front_proto_len)++;
+ }
+
+ DrawPrototype &draw = prototype_buf_.get_or_resize(prototype_count_++);
+ draw.group_id = group_id;
+ draw.resource_handle = handle.raw;
+ draw.instance_len = instance_len;
+ }
+
+ void bind(RecordingState &state,
+ Vector<Header, 0> &headers,
+ Vector<Undetermined, 0> &commands,
+ VisibilityBuf &visibility_buf);
+};
+
+/** \} */
+
+}; // namespace blender::draw::command
diff --git a/source/blender/draw/intern/draw_command_shared.hh b/source/blender/draw/intern/draw_command_shared.hh
new file mode 100644
index 00000000000..9fbbe23f0ce
--- /dev/null
+++ b/source/blender/draw/intern/draw_command_shared.hh
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ */
+
+#ifndef GPU_SHADER
+# include "BLI_span.hh"
+# include "GPU_shader_shared_utils.h"
+
+namespace blender::draw::command {
+
+struct RecordingState;
+
+#endif
+
+/* -------------------------------------------------------------------- */
+/** \name Multi Draw
+ * \{ */
+
+/**
+ * A DrawGroup allow to split the command stream into batch-able chunks of commands with
+ * the same render state.
+ */
+struct DrawGroup {
+ /** Index of next #DrawGroup from the same header. */
+ uint next;
+
+ /** Index of the first instances after sorting. */
+ uint start;
+ /** Total number of instances (including inverted facing). Needed to issue the draw call. */
+ uint len;
+ /** Number of non inverted scaling instances in this Group. */
+ uint front_facing_len;
+
+ /** #GPUBatch values to be copied to #DrawCommand after sorting (if not overridden). */
+ int vertex_len;
+ int vertex_first;
+ int base_index;
+
+ /** Atomic counters used during command sorting. */
+ uint total_counter;
+
+#ifndef GPU_SHADER
+ /* NOTE: Union just to make sure the struct has always the same size on all platform. */
+ union {
+ struct {
+ /** For debug printing only. */
+ uint front_proto_len;
+ uint back_proto_len;
+ /** Needed to create the correct draw call. */
+ GPUBatch *gpu_batch;
+ };
+ struct {
+#endif
+ uint front_facing_counter;
+ uint back_facing_counter;
+ uint _pad0, _pad1;
+#ifndef GPU_SHADER
+ };
+ };
+#endif
+};
+BLI_STATIC_ASSERT_ALIGN(DrawGroup, 16)
+
+/**
+ * Representation of a future draw call inside a DrawGroup. This #DrawPrototype is then
+ * converted into #DrawCommand on GPU after visibility and compaction. Multiple
+ * #DrawPrototype might get merged into the same final #DrawCommand.
+ */
+struct DrawPrototype {
+ /* Reference to parent DrawGroup to get the GPUBatch vertex / instance count. */
+ uint group_id;
+ /* Resource handle associated with this call. Also reference visibility. */
+ uint resource_handle;
+ /* Number of instances. */
+ uint instance_len;
+ uint _pad0;
+};
+BLI_STATIC_ASSERT_ALIGN(DrawPrototype, 16)
+
+/** \} */
+
+#ifndef GPU_SHADER
+}; // namespace blender::draw::command
+#endif
diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c
index 0f330dbb519..c1b4c3c1f81 100644
--- a/source/blender/draw/intern/draw_common.c
+++ b/source/blender/draw/intern/draw_common.c
@@ -280,10 +280,11 @@ int DRW_object_wire_theme_get(Object *ob, ViewLayer *view_layer, float **r_color
{
const DRWContextState *draw_ctx = DRW_context_state_get();
const bool is_edit = (draw_ctx->object_mode & OB_MODE_EDIT) && (ob->mode & OB_MODE_EDIT);
- const bool active = view_layer->basact &&
- ((ob->base_flag & BASE_FROM_DUPLI) ?
- (DRW_object_get_dupli_parent(ob) == view_layer->basact->object) :
- (view_layer->basact->object == ob));
+ BKE_view_layer_synced_ensure(draw_ctx->scene, view_layer);
+ const Base *base = BKE_view_layer_active_base_get(view_layer);
+ const bool active = base && ((ob->base_flag & BASE_FROM_DUPLI) ?
+ (DRW_object_get_dupli_parent(ob) == base->object) :
+ (base->object == ob));
/* confusing logic here, there are 2 methods of setting the color
* 'colortab[colindex]' and 'theme_id', colindex overrides theme_id.
@@ -417,7 +418,6 @@ bool DRW_object_is_flat(Object *ob, int *r_axis)
OB_CURVES_LEGACY,
OB_SURF,
OB_FONT,
- OB_MBALL,
OB_CURVES,
OB_POINTCLOUD,
OB_VOLUME)) {
diff --git a/source/blender/draw/intern/draw_common_shader_shared.h b/source/blender/draw/intern/draw_common_shader_shared.h
index c9819d9da87..57cb7880ce6 100644
--- a/source/blender/draw/intern/draw_common_shader_shared.h
+++ b/source/blender/draw/intern/draw_common_shader_shared.h
@@ -19,7 +19,7 @@ typedef struct GlobalsUboStorage GlobalsUboStorage;
#define UBO_LAST_COLOR color_uv_shadow
/* Used as ubo but colors can be directly referenced as well */
-/* NOTE: Also keep all color as vec4 and between #UBO_FIRST_COLOR and #UBO_LAST_COLOR. */
+/* \note Also keep all color as vec4 and between #UBO_FIRST_COLOR and #UBO_LAST_COLOR. */
struct GlobalsUboStorage {
/* UBOs data needs to be 16 byte aligned (size of vec4) */
float4 color_wire;
diff --git a/source/blender/draw/intern/draw_curves.cc b/source/blender/draw/intern/draw_curves.cc
index 233af08c363..a61769e7a63 100644
--- a/source/blender/draw/intern/draw_curves.cc
+++ b/source/blender/draw/intern/draw_curves.cc
@@ -33,25 +33,17 @@
#include "draw_manager.h"
#include "draw_shader.h"
-#ifndef __APPLE__
-# define USE_TRANSFORM_FEEDBACK
-# define USE_COMPUTE_SHADERS
-#endif
-
BLI_INLINE eParticleRefineShaderType drw_curves_shader_type_get()
{
-#ifdef USE_COMPUTE_SHADERS
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) {
return PART_REFINE_SHADER_COMPUTE;
}
-#endif
-#ifdef USE_TRANSFORM_FEEDBACK
- return PART_REFINE_SHADER_TRANSFORM_FEEDBACK;
-#endif
+ if (GPU_transform_feedback_support()) {
+ return PART_REFINE_SHADER_TRANSFORM_FEEDBACK;
+ }
return PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND;
}
-#ifndef USE_TRANSFORM_FEEDBACK
struct CurvesEvalCall {
struct CurvesEvalCall *next;
GPUVertBuf *vbo;
@@ -63,7 +55,6 @@ static CurvesEvalCall *g_tf_calls = nullptr;
static int g_tf_id_offset;
static int g_tf_target_width;
static int g_tf_target_height;
-#endif
static GPUVertBuf *g_dummy_vbo = nullptr;
static GPUTexture *g_dummy_texture = nullptr;
@@ -106,18 +97,20 @@ void DRW_curves_init(DRWData *drw_data)
CurvesUniformBufPool *pool = drw_data->curves_ubos;
pool->reset();
-#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS)
- g_tf_pass = DRW_pass_create("Update Curves Pass", (DRWState)0);
-#else
- g_tf_pass = DRW_pass_create("Update Curves Pass", DRW_STATE_WRITE_COLOR);
-#endif
+ if (GPU_transform_feedback_support() || GPU_compute_shader_support()) {
+ g_tf_pass = DRW_pass_create("Update Curves Pass", (DRWState)0);
+ }
+ else {
+ g_tf_pass = DRW_pass_create("Update Curves Pass", DRW_STATE_WRITE_COLOR);
+ }
if (g_dummy_vbo == nullptr) {
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- g_dummy_vbo = GPU_vertbuf_create_with_format(&format);
+ g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
+ &format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
@@ -201,21 +194,24 @@ static void drw_curves_cache_update_transform_feedback(CurvesEvalCache *cache,
{
GPUShader *tf_shader = curves_eval_shader_get(CURVES_EVAL_CATMULL_ROM);
-#ifdef USE_TRANSFORM_FEEDBACK
- DRWShadingGroup *tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass, vbo);
-#else
- DRWShadingGroup *tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
-
- CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__);
- pr_call->next = g_tf_calls;
- pr_call->vbo = vbo;
- 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
+ DRWShadingGroup *tf_shgrp = nullptr;
+ if (GPU_transform_feedback_support()) {
+ tf_shgrp = DRW_shgroup_transform_feedback_create(tf_shader, g_tf_pass, vbo);
+ }
+ else {
+ tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
+
+ CurvesEvalCall *pr_call = MEM_new<CurvesEvalCall>(__func__);
+ pr_call->next = g_tf_calls;
+ pr_call->vbo = vbo;
+ 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);
+ }
+ BLI_assert(tf_shgrp != nullptr);
drw_curves_cache_shgrp_attach_resources(tf_shgrp, cache, tex, subdiv);
DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len);
@@ -411,82 +407,118 @@ void DRW_curves_update()
/* Update legacy hair too, to avoid verbosity in callers. */
DRW_hair_update();
-#ifndef USE_TRANSFORM_FEEDBACK
- /**
- * Workaround to transform feedback not working on mac.
- * On some system it crashes (see T58489) and on some other it renders garbage (see T60171).
- *
- * So instead of using transform feedback we render to a texture,
- * read back the result to system memory and re-upload as VBO data.
- * It is really not ideal performance wise, but it is the simplest
- * and the most local workaround that still uses the power of the GPU.
- */
-
- if (g_tf_calls == nullptr) {
- return;
- }
+ if (!GPU_transform_feedback_support()) {
+ /**
+ * Workaround to transform feedback not working on mac.
+ * On some system it crashes (see T58489) and on some other it renders garbage (see T60171).
+ *
+ * So instead of using transform feedback we render to a texture,
+ * read back the result to system memory and re-upload as VBO data.
+ * It is really not ideal performance wise, but it is the simplest
+ * and the most local workaround that still uses the power of the GPU.
+ */
+
+ if (g_tf_calls == nullptr) {
+ return;
+ }
- /* Search ideal buffer size. */
- uint max_size = 0;
- for (CurvesEvalCall *pr_call = g_tf_calls; pr_call; pr_call = pr_call->next) {
- max_size = max_ii(max_size, pr_call->vert_len);
- }
+ /* Search ideal buffer size. */
+ uint max_size = 0;
+ for (CurvesEvalCall *pr_call = g_tf_calls; pr_call; pr_call = pr_call->next) {
+ max_size = max_ii(max_size, pr_call->vert_len);
+ }
+
+ /* Create target Texture / Frame-buffer */
+ /* Don't use max size as it can be really heavy and fail.
+ * Do chunks of maximum 2048 * 2048 hair points. */
+ int width = 2048;
+ int height = min_ii(width, 1 + max_size / width);
+ GPUTexture *tex = DRW_texture_pool_query_2d(
+ width, height, GPU_RGBA32F, (DrawEngineType *)DRW_curves_update);
+ g_tf_target_height = height;
+ g_tf_target_width = width;
+
+ GPUFrameBuffer *fb = nullptr;
+ GPU_framebuffer_ensure_config(&fb,
+ {
+ GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(tex),
+ });
+
+ float *data = static_cast<float *>(
+ MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"));
+
+ GPU_framebuffer_bind(fb);
+ while (g_tf_calls != nullptr) {
+ CurvesEvalCall *pr_call = g_tf_calls;
+ g_tf_calls = g_tf_calls->next;
+
+ g_tf_id_offset = 0;
+ while (pr_call->vert_len > 0) {
+ int max_read_px_len = min_ii(width * height, pr_call->vert_len);
+
+ DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp);
+ /* Read back result to main memory. */
+ GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data);
+ /* Upload back to VBO. */
+ GPU_vertbuf_use(pr_call->vbo);
+ GPU_vertbuf_update_sub(pr_call->vbo,
+ sizeof(float[4]) * g_tf_id_offset,
+ sizeof(float[4]) * max_read_px_len,
+ data);
+
+ g_tf_id_offset += max_read_px_len;
+ pr_call->vert_len -= max_read_px_len;
+ }
- /* Create target Texture / Frame-buffer */
- /* Don't use max size as it can be really heavy and fail.
- * Do chunks of maximum 2048 * 2048 hair points. */
- int width = 2048;
- int height = min_ii(width, 1 + max_size / width);
- GPUTexture *tex = DRW_texture_pool_query_2d(
- width, height, GPU_RGBA32F, (DrawEngineType *)DRW_curves_update);
- g_tf_target_height = height;
- g_tf_target_width = width;
-
- GPUFrameBuffer *fb = nullptr;
- GPU_framebuffer_ensure_config(&fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(tex),
- });
-
- float *data = static_cast<float *>(
- MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer"));
-
- GPU_framebuffer_bind(fb);
- while (g_tf_calls != nullptr) {
- CurvesEvalCall *pr_call = g_tf_calls;
- g_tf_calls = g_tf_calls->next;
-
- g_tf_id_offset = 0;
- while (pr_call->vert_len > 0) {
- int max_read_px_len = min_ii(width * height, pr_call->vert_len);
-
- DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp);
- /* Read back result to main memory. */
- GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data);
- /* Upload back to VBO. */
- GPU_vertbuf_use(pr_call->vbo);
- GPU_vertbuf_update_sub(pr_call->vbo,
- sizeof(float[4]) * g_tf_id_offset,
- sizeof(float[4]) * max_read_px_len,
- data);
-
- g_tf_id_offset += max_read_px_len;
- pr_call->vert_len -= max_read_px_len;
+ MEM_freeN(pr_call);
}
- MEM_freeN(pr_call);
+ MEM_freeN(data);
+ GPU_framebuffer_free(fb);
}
+ else {
+ /* NOTE(Metal): If compute is not supported, bind a temporary frame-buffer to avoid
+ * side-effects from rendering in the active buffer.
+ * We also need to guarantee that a Frame-buffer is active to perform any rendering work,
+ * even if there is no output */
+ GPUFrameBuffer *temp_fb = nullptr;
+ GPUFrameBuffer *prev_fb = nullptr;
+ if (GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_METAL)) {
+ if (!GPU_compute_shader_support()) {
+ prev_fb = GPU_framebuffer_active_get();
+ char errorOut[256];
+ /* if the frame-buffer is invalid we need a dummy frame-buffer to be bound. */
+ if (!GPU_framebuffer_check_valid(prev_fb, errorOut)) {
+ int width = 64;
+ int height = 64;
+ GPUTexture *tex = DRW_texture_pool_query_2d(
+ width, height, GPU_DEPTH_COMPONENT32F, (DrawEngineType *)DRW_hair_update);
+ g_tf_target_height = height;
+ g_tf_target_width = width;
+
+ GPU_framebuffer_ensure_config(&temp_fb, {GPU_ATTACHMENT_TEXTURE(tex)});
+
+ GPU_framebuffer_bind(temp_fb);
+ }
+ }
+ }
- MEM_freeN(data);
- GPU_framebuffer_free(fb);
-#else
- /* Just render the pass when using compute shaders or transform feedback. */
- DRW_draw_pass(g_tf_pass);
- if (drw_curves_shader_type_get() == PART_REFINE_SHADER_COMPUTE) {
- GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+ /* Just render the pass when using compute shaders or transform feedback. */
+ DRW_draw_pass(g_tf_pass);
+ if (drw_curves_shader_type_get() == PART_REFINE_SHADER_COMPUTE) {
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+
+ /* Release temporary frame-buffer. */
+ if (temp_fb != nullptr) {
+ GPU_framebuffer_free(temp_fb);
+ }
+ /* Rebind existing frame-buffer */
+ if (prev_fb != nullptr) {
+ GPU_framebuffer_bind(prev_fb);
+ }
}
-#endif
}
void DRW_curves_free()
diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c
deleted file mode 100644
index b568119627e..00000000000
--- a/source/blender/draw/intern/draw_debug.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2018 Blender Foundation. */
-
-/** \file
- * \ingroup draw
- *
- * \brief Simple API to draw debug shapes in the viewport.
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "DNA_object_types.h"
-
-#include "BKE_object.h"
-
-#include "BLI_link_utils.h"
-
-#include "GPU_immediate.h"
-#include "GPU_matrix.h"
-
-#include "draw_debug.h"
-#include "draw_manager.h"
-
-/* --------- Register --------- */
-
-/* Matrix applied to all points before drawing. Could be a stack if needed. */
-static float g_modelmat[4][4];
-
-void DRW_debug_modelmat_reset(void)
-{
- unit_m4(g_modelmat);
-}
-
-void DRW_debug_modelmat(const float modelmat[4][4])
-{
- copy_m4_m4(g_modelmat, modelmat);
-}
-
-void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4])
-{
- DRWDebugLine *line = MEM_mallocN(sizeof(DRWDebugLine), "DRWDebugLine");
- mul_v3_m4v3(line->pos[0], g_modelmat, v1);
- mul_v3_m4v3(line->pos[1], g_modelmat, v2);
- copy_v4_v4(line->color, color);
- BLI_LINKS_PREPEND(DST.debug.lines, line);
-}
-
-void DRW_debug_polygon_v3(const float (*v)[3], const int vert_len, const float color[4])
-{
- BLI_assert(vert_len > 1);
-
- for (int i = 0; i < vert_len; i++) {
- DRW_debug_line_v3v3(v[i], v[(i + 1) % vert_len], color);
- }
-}
-
-void DRW_debug_m4(const float m[4][4])
-{
- float v0[3] = {0.0f, 0.0f, 0.0f};
- float v1[3] = {1.0f, 0.0f, 0.0f};
- float v2[3] = {0.0f, 1.0f, 0.0f};
- float v3[3] = {0.0f, 0.0f, 1.0f};
-
- mul_m4_v3(m, v0);
- mul_m4_v3(m, v1);
- mul_m4_v3(m, v2);
- mul_m4_v3(m, v3);
-
- DRW_debug_line_v3v3(v0, v1, (float[4]){1.0f, 0.0f, 0.0f, 1.0f});
- DRW_debug_line_v3v3(v0, v2, (float[4]){0.0f, 1.0f, 0.0f, 1.0f});
- DRW_debug_line_v3v3(v0, v3, (float[4]){0.0f, 0.0f, 1.0f, 1.0f});
-}
-
-void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
-{
- DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[1], color);
- DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[2], color);
- DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[3], color);
- DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[0], color);
-
- DRW_debug_line_v3v3(bbox->vec[4], bbox->vec[5], color);
- DRW_debug_line_v3v3(bbox->vec[5], bbox->vec[6], color);
- DRW_debug_line_v3v3(bbox->vec[6], bbox->vec[7], color);
- DRW_debug_line_v3v3(bbox->vec[7], bbox->vec[4], color);
-
- DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[4], color);
- DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[5], color);
- DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[6], color);
- DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[7], color);
-}
-
-void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], const bool invert)
-{
- BoundBox bb;
- const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f};
- float project_matrix[4][4];
- if (invert) {
- invert_m4_m4(project_matrix, m);
- }
- else {
- copy_m4_m4(project_matrix, m);
- }
-
- BKE_boundbox_init_from_minmax(&bb, min, max);
- for (int i = 0; i < 8; i++) {
- mul_project_m4_v3(project_matrix, bb.vec[i]);
- }
- DRW_debug_bbox(&bb, color);
-}
-
-void DRW_debug_sphere(const float center[3], const float radius, const float color[4])
-{
- float size_mat[4][4];
- DRWDebugSphere *sphere = MEM_mallocN(sizeof(DRWDebugSphere), "DRWDebugSphere");
- /* Bake all transform into a Matrix4 */
- scale_m4_fl(size_mat, radius);
- copy_m4_m4(sphere->mat, g_modelmat);
- translate_m4(sphere->mat, center[0], center[1], center[2]);
- mul_m4_m4m4(sphere->mat, sphere->mat, size_mat);
-
- copy_v4_v4(sphere->color, color);
- BLI_LINKS_PREPEND(DST.debug.spheres, sphere);
-}
-
-/* --------- Render --------- */
-
-static void drw_debug_draw_lines(void)
-{
- int count = BLI_linklist_count((LinkNode *)DST.debug.lines);
-
- if (count == 0) {
- return;
- }
-
- GPUVertFormat *vert_format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(vert_format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
-
- immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
-
- immBegin(GPU_PRIM_LINES, count * 2);
-
- while (DST.debug.lines) {
- void *next = DST.debug.lines->next;
-
- immAttr4fv(col, DST.debug.lines->color);
- immVertex3fv(pos, DST.debug.lines->pos[0]);
-
- immAttr4fv(col, DST.debug.lines->color);
- immVertex3fv(pos, DST.debug.lines->pos[1]);
-
- MEM_freeN(DST.debug.lines);
- DST.debug.lines = next;
- }
- immEnd();
-
- immUnbindProgram();
-}
-
-static void drw_debug_draw_spheres(void)
-{
- int count = BLI_linklist_count((LinkNode *)DST.debug.spheres);
-
- if (count == 0) {
- return;
- }
-
- float persmat[4][4];
- DRW_view_persmat_get(NULL, persmat, false);
-
- GPUBatch *empty_sphere = DRW_cache_empty_sphere_get();
- GPU_batch_program_set_builtin(empty_sphere, GPU_SHADER_3D_UNIFORM_COLOR);
- while (DST.debug.spheres) {
- void *next = DST.debug.spheres->next;
- float MVP[4][4];
-
- mul_m4_m4m4(MVP, persmat, DST.debug.spheres->mat);
- GPU_batch_uniform_mat4(empty_sphere, "ModelViewProjectionMatrix", MVP);
- GPU_batch_uniform_4fv(empty_sphere, "color", DST.debug.spheres->color);
- GPU_batch_draw(empty_sphere);
-
- MEM_freeN(DST.debug.spheres);
- DST.debug.spheres = next;
- }
-}
-
-void drw_debug_draw(void)
-{
- drw_debug_draw_lines();
- drw_debug_draw_spheres();
-}
-
-void drw_debug_init(void)
-{
- DRW_debug_modelmat_reset();
-}
diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc
new file mode 100644
index 00000000000..811185395fd
--- /dev/null
+++ b/source/blender/draw/intern/draw_debug.cc
@@ -0,0 +1,739 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2018 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Simple API to draw debug shapes in the viewport.
+ */
+
+#include "BKE_object.h"
+#include "BLI_link_utils.h"
+#include "GPU_batch.h"
+#include "GPU_capabilities.h"
+#include "GPU_debug.h"
+
+#include "draw_debug.h"
+#include "draw_debug.hh"
+#include "draw_manager.h"
+#include "draw_shader.h"
+#include "draw_shader_shared.h"
+
+#include <iomanip>
+
+#ifdef DEBUG
+# define DRAW_DEBUG
+#else
+/* Uncomment to forcibly enable debug draw in release mode. */
+//#define DRAW_DEBUG
+#endif
+
+namespace blender::draw {
+
+/* -------------------------------------------------------------------- */
+/** \name Init and state
+ * \{ */
+
+DebugDraw::DebugDraw()
+{
+ constexpr int circle_resolution = 16;
+ for (auto axis : IndexRange(3)) {
+ for (auto edge : IndexRange(circle_resolution)) {
+ for (auto vert : IndexRange(2)) {
+ const float angle = (2 * M_PI) * (edge + vert) / float(circle_resolution);
+ float point[3] = {cosf(angle), sinf(angle), 0.0f};
+ sphere_verts_.append(
+ float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3]));
+ }
+ }
+ }
+
+ constexpr int point_resolution = 4;
+ for (auto axis : IndexRange(3)) {
+ for (auto edge : IndexRange(point_resolution)) {
+ for (auto vert : IndexRange(2)) {
+ const float angle = (2 * M_PI) * (edge + vert) / float(point_resolution);
+ float point[3] = {cosf(angle), sinf(angle), 0.0f};
+ point_verts_.append(
+ float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3]));
+ }
+ }
+ }
+};
+
+void DebugDraw::init()
+{
+ cpu_print_buf_.command.vertex_len = 0;
+ cpu_print_buf_.command.vertex_first = 0;
+ cpu_print_buf_.command.instance_len = 1;
+ cpu_print_buf_.command.instance_first_array = 0;
+
+ cpu_draw_buf_.command.vertex_len = 0;
+ cpu_draw_buf_.command.vertex_first = 0;
+ cpu_draw_buf_.command.instance_len = 1;
+ cpu_draw_buf_.command.instance_first_array = 0;
+
+ gpu_print_buf_.command.vertex_len = 0;
+ gpu_print_buf_.command.vertex_first = 0;
+ gpu_print_buf_.command.instance_len = 1;
+ gpu_print_buf_.command.instance_first_array = 0;
+ gpu_print_buf_used = false;
+
+ gpu_draw_buf_.command.vertex_len = 0;
+ gpu_draw_buf_.command.vertex_first = 0;
+ gpu_draw_buf_.command.instance_len = 1;
+ gpu_draw_buf_.command.instance_first_array = 0;
+ gpu_draw_buf_used = false;
+
+ print_col_ = 0;
+ print_row_ = 0;
+
+ modelmat_reset();
+}
+
+void DebugDraw::modelmat_reset()
+{
+ model_mat_ = float4x4::identity();
+}
+
+void DebugDraw::modelmat_set(const float modelmat[4][4])
+{
+ model_mat_ = modelmat;
+}
+
+GPUStorageBuf *DebugDraw::gpu_draw_buf_get()
+{
+ BLI_assert(GPU_shader_storage_buffer_objects_support());
+ if (!gpu_draw_buf_used) {
+ gpu_draw_buf_used = true;
+ gpu_draw_buf_.push_update();
+ }
+ return gpu_draw_buf_;
+}
+
+GPUStorageBuf *DebugDraw::gpu_print_buf_get()
+{
+ BLI_assert(GPU_shader_storage_buffer_objects_support());
+ if (!gpu_print_buf_used) {
+ gpu_print_buf_used = true;
+ gpu_print_buf_.push_update();
+ }
+ return gpu_print_buf_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw functions
+ * \{ */
+
+void DebugDraw::draw_line(float3 v1, float3 v2, float4 color)
+{
+ draw_line(v1, v2, color_pack(color));
+}
+
+void DebugDraw::draw_polygon(Span<float3> poly_verts, float4 color)
+{
+ BLI_assert(!poly_verts.is_empty());
+
+ uint col = color_pack(color);
+ float3 v0 = model_mat_ * poly_verts.last();
+ for (auto vert : poly_verts) {
+ float3 v1 = model_mat_ * vert;
+ draw_line(v0, v1, col);
+ v0 = v1;
+ }
+}
+
+void DebugDraw::draw_matrix(const float4x4 m4)
+{
+ float3 v0 = float3(0.0f, 0.0f, 0.0f);
+ float3 v1 = float3(1.0f, 0.0f, 0.0f);
+ float3 v2 = float3(0.0f, 1.0f, 0.0f);
+ float3 v3 = float3(0.0f, 0.0f, 1.0f);
+
+ mul_project_m4_v3(m4.ptr(), v0);
+ mul_project_m4_v3(m4.ptr(), v1);
+ mul_project_m4_v3(m4.ptr(), v2);
+ mul_project_m4_v3(m4.ptr(), v3);
+
+ draw_line(v0, v1, float4(1.0f, 0.0f, 0.0f, 1.0f));
+ draw_line(v0, v2, float4(0.0f, 1.0f, 0.0f, 1.0f));
+ draw_line(v0, v3, float4(0.0f, 0.0f, 1.0f, 1.0f));
+}
+
+void DebugDraw::draw_bbox(const BoundBox &bbox, const float4 color)
+{
+ uint col = color_pack(color);
+ draw_line(bbox.vec[0], bbox.vec[1], col);
+ draw_line(bbox.vec[1], bbox.vec[2], col);
+ draw_line(bbox.vec[2], bbox.vec[3], col);
+ draw_line(bbox.vec[3], bbox.vec[0], col);
+
+ draw_line(bbox.vec[4], bbox.vec[5], col);
+ draw_line(bbox.vec[5], bbox.vec[6], col);
+ draw_line(bbox.vec[6], bbox.vec[7], col);
+ draw_line(bbox.vec[7], bbox.vec[4], col);
+
+ draw_line(bbox.vec[0], bbox.vec[4], col);
+ draw_line(bbox.vec[1], bbox.vec[5], col);
+ draw_line(bbox.vec[2], bbox.vec[6], col);
+ draw_line(bbox.vec[3], bbox.vec[7], col);
+}
+
+void DebugDraw::draw_matrix_as_bbox(float4x4 mat, const float4 color)
+{
+ BoundBox bb;
+ const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f};
+ BKE_boundbox_init_from_minmax(&bb, min, max);
+ for (auto i : IndexRange(8)) {
+ mul_project_m4_v3(mat.ptr(), bb.vec[i]);
+ }
+ draw_bbox(bb, color);
+}
+
+void DebugDraw::draw_sphere(const float3 center, float radius, const float4 color)
+{
+ uint col = color_pack(color);
+ for (auto i : IndexRange(sphere_verts_.size() / 2)) {
+ float3 v0 = sphere_verts_[i * 2] * radius + center;
+ float3 v1 = sphere_verts_[i * 2 + 1] * radius + center;
+ draw_line(v0, v1, col);
+ }
+}
+
+void DebugDraw::draw_point(const float3 center, float radius, const float4 color)
+{
+ uint col = color_pack(color);
+ for (auto i : IndexRange(point_verts_.size() / 2)) {
+ float3 v0 = point_verts_[i * 2] * radius + center;
+ float3 v1 = point_verts_[i * 2 + 1] * radius + center;
+ draw_line(v0, v1, col);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Print functions
+ * \{ */
+
+template<> void DebugDraw::print_value<uint>(const uint &value)
+{
+ print_value_uint(value, false, false, true);
+}
+template<> void DebugDraw::print_value<int>(const int &value)
+{
+ print_value_uint(uint(abs(value)), false, (value < 0), false);
+}
+template<> void DebugDraw::print_value<bool>(const bool &value)
+{
+ print_string(value ? "true " : "false");
+}
+template<> void DebugDraw::print_value<float>(const float &val)
+{
+ std::stringstream ss;
+ ss << std::setw(12) << std::to_string(val);
+ print_string(ss.str());
+}
+template<> void DebugDraw::print_value<double>(const double &val)
+{
+ print_value(float(val));
+}
+
+template<> void DebugDraw::print_value_hex<uint>(const uint &value)
+{
+ print_value_uint(value, true, false, false);
+}
+template<> void DebugDraw::print_value_hex<int>(const int &value)
+{
+ print_value_uint(uint(value), true, false, false);
+}
+template<> void DebugDraw::print_value_hex<float>(const float &value)
+{
+ print_value_uint(*reinterpret_cast<const uint *>(&value), true, false, false);
+}
+template<> void DebugDraw::print_value_hex<double>(const double &val)
+{
+ print_value_hex(float(val));
+}
+
+template<> void DebugDraw::print_value_binary<uint>(const uint &value)
+{
+ print_value_binary(value);
+}
+template<> void DebugDraw::print_value_binary<int>(const int &value)
+{
+ print_value_binary(uint(value));
+}
+template<> void DebugDraw::print_value_binary<float>(const float &value)
+{
+ print_value_binary(*reinterpret_cast<const uint *>(&value));
+}
+template<> void DebugDraw::print_value_binary<double>(const double &val)
+{
+ print_value_binary(float(val));
+}
+
+template<> void DebugDraw::print_value<float2>(const float2 &value)
+{
+ print_no_endl("float2(", value[0], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<float3>(const float3 &value)
+{
+ print_no_endl("float3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<float4>(const float4 &value)
+{
+ print_no_endl("float4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+template<> void DebugDraw::print_value<int2>(const int2 &value)
+{
+ print_no_endl("int2(", value[0], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<int3>(const int3 &value)
+{
+ print_no_endl("int3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<int4>(const int4 &value)
+{
+ print_no_endl("int4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+template<> void DebugDraw::print_value<uint2>(const uint2 &value)
+{
+ print_no_endl("uint2(", value[0], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<uint3>(const uint3 &value)
+{
+ print_no_endl("uint3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+template<> void DebugDraw::print_value<uint4>(const uint4 &value)
+{
+ print_no_endl("uint4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internals
+ *
+ * IMPORTANT: All of these are copied from the shader libs (common_debug_draw_lib.glsl &
+ * common_debug_print_lib.glsl). They need to be kept in sync to write the same data.
+ * \{ */
+
+void DebugDraw::draw_line(float3 v1, float3 v2, uint color)
+{
+ DebugDrawBuf &buf = cpu_draw_buf_;
+ uint index = buf.command.vertex_len;
+ if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) {
+ buf.verts[index + 0] = vert_pack(model_mat_ * v1, color);
+ buf.verts[index + 1] = vert_pack(model_mat_ * v2, color);
+ buf.command.vertex_len += 2;
+ }
+}
+
+/* Keep in sync with drw_debug_color_pack(). */
+uint DebugDraw::color_pack(float4 color)
+{
+ color = math::clamp(color, 0.0f, 1.0f);
+ uint result = 0;
+ result |= uint(color.x * 255.0f) << 0u;
+ result |= uint(color.y * 255.0f) << 8u;
+ result |= uint(color.z * 255.0f) << 16u;
+ result |= uint(color.w * 255.0f) << 24u;
+ return result;
+}
+
+DRWDebugVert DebugDraw::vert_pack(float3 pos, uint color)
+{
+ DRWDebugVert vert;
+ vert.pos0 = *reinterpret_cast<uint32_t *>(&pos.x);
+ vert.pos1 = *reinterpret_cast<uint32_t *>(&pos.y);
+ vert.pos2 = *reinterpret_cast<uint32_t *>(&pos.z);
+ vert.color = color;
+ return vert;
+}
+
+void DebugDraw::print_newline()
+{
+ print_col_ = 0u;
+ print_row_ = ++cpu_print_buf_.command.instance_first_array;
+}
+
+void DebugDraw::print_string_start(uint len)
+{
+ /* Break before word. */
+ if (print_col_ + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
+ print_newline();
+ }
+}
+
+/* Copied from gpu_shader_dependency. */
+void DebugDraw::print_string(std::string str)
+{
+ size_t len_before_pad = str.length();
+ /* Pad string to uint size to avoid out of bound reads. */
+ while (str.length() % 4 != 0) {
+ str += " ";
+ }
+
+ print_string_start(len_before_pad);
+ for (size_t i = 0; i < len_before_pad; i += 4) {
+ union {
+ uint8_t chars[4];
+ uint32_t word;
+ };
+
+ chars[0] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0);
+ chars[1] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1);
+ chars[2] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2);
+ chars[3] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3);
+
+ if (i + 4 > len_before_pad) {
+ chars[len_before_pad - i] = '\0';
+ }
+ print_char4(word);
+ }
+}
+
+/* Keep in sync with shader. */
+void DebugDraw::print_char4(uint data)
+{
+ /* Convert into char stream. */
+ for (; data != 0u; data >>= 8u) {
+ uint char1 = data & 0xFFu;
+ /* Check for null terminator. */
+ if (char1 == 0x00) {
+ break;
+ }
+ /* NOTE: Do not skip the header manually like in GPU. */
+ uint cursor = cpu_print_buf_.command.vertex_len++;
+ if (cursor < DRW_DEBUG_PRINT_MAX) {
+ /* For future usage. (i.e: Color) */
+ uint flags = 0u;
+ uint col = print_col_++;
+ uint print_header = (flags << 24u) | (print_row_ << 16u) | (col << 8u);
+ cpu_print_buf_.char_array[cursor] = print_header | char1;
+ /* Break word. */
+ if (print_col_ > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
+ print_newline();
+ }
+ }
+ }
+}
+
+void DebugDraw::print_append_char(uint char1, uint &char4)
+{
+ char4 = (char4 << 8u) | char1;
+}
+
+void DebugDraw::print_append_digit(uint digit, uint &char4)
+{
+ const uint char_A = 0x41u;
+ const uint char_0 = 0x30u;
+ bool is_hexadecimal = digit > 9u;
+ char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit));
+}
+
+void DebugDraw::print_append_space(uint &char4)
+{
+ char4 = (char4 << 8u) | 0x20u;
+}
+
+void DebugDraw::print_value_binary(uint value)
+{
+ print_string("0b");
+ print_string_start(10u * 4u);
+ uint digits[10] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
+ uint digit = 0u;
+ for (uint i = 0u; i < 32u; i++) {
+ print_append_digit(((value >> i) & 1u), digits[digit / 4u]);
+ digit++;
+ if ((i % 4u) == 3u) {
+ print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 9; j >= 0; j--) {
+ print_char4(digits[j]);
+ }
+}
+
+void DebugDraw::print_value_uint(uint value,
+ const bool hex,
+ bool is_negative,
+ const bool is_unsigned)
+{
+ print_string_start(3u * 4u);
+ const uint blank_value = hex ? 0x30303030u : 0x20202020u;
+ const uint prefix = hex ? 0x78302020u : 0x20202020u;
+ uint digits[3] = {blank_value, blank_value, prefix};
+ const uint base = hex ? 16u : 10u;
+ uint digit = 0u;
+ /* Add `u` suffix. */
+ if (is_unsigned) {
+ print_append_char('u', digits[digit / 4u]);
+ digit++;
+ }
+ /* Number's digits. */
+ for (; value != 0u || digit == uint(is_unsigned); value /= base) {
+ print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Add negative sign. */
+ if (is_negative) {
+ print_append_char('-', digits[digit / 4u]);
+ digit++;
+ }
+ /* Need to pad to uint alignment because we are issuing chars in "reverse". */
+ for (uint i = digit % 4u; i < 4u && i > 0u; i++) {
+ print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 2; j >= 0; j--) {
+ print_char4(digits[j]);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Display
+ * \{ */
+
+void DebugDraw::display_lines()
+{
+ if (cpu_draw_buf_.command.vertex_len == 0 && gpu_draw_buf_used == false) {
+ return;
+ }
+ GPU_debug_group_begin("Lines");
+ cpu_draw_buf_.push_update();
+
+ float4x4 persmat;
+ const DRWView *view = DRW_view_get_active();
+ DRW_view_persmat_get(view, persmat.ptr(), false);
+
+ drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS);
+
+ GPUBatch *batch = drw_cache_procedural_lines_get();
+ GPUShader *shader = DRW_shader_debug_draw_display_get();
+ GPU_batch_set_shader(batch, shader);
+ int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS);
+ GPU_shader_uniform_mat4(shader, "persmat", persmat.ptr());
+
+ if (gpu_draw_buf_used) {
+ GPU_debug_group_begin("GPU");
+ GPU_storagebuf_bind(gpu_draw_buf_, slot);
+ GPU_batch_draw_indirect(batch, gpu_draw_buf_, 0);
+ GPU_storagebuf_unbind(gpu_draw_buf_);
+ GPU_debug_group_end();
+ }
+
+ GPU_debug_group_begin("CPU");
+ GPU_storagebuf_bind(cpu_draw_buf_, slot);
+ GPU_batch_draw_indirect(batch, cpu_draw_buf_, 0);
+ GPU_storagebuf_unbind(cpu_draw_buf_);
+ GPU_debug_group_end();
+
+ GPU_debug_group_end();
+}
+
+void DebugDraw::display_prints()
+{
+ if (cpu_print_buf_.command.vertex_len == 0 && gpu_print_buf_used == false) {
+ return;
+ }
+ GPU_debug_group_begin("Prints");
+ cpu_print_buf_.push_update();
+
+ drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_PROGRAM_POINT_SIZE);
+
+ GPUBatch *batch = drw_cache_procedural_points_get();
+ GPUShader *shader = DRW_shader_debug_print_display_get();
+ GPU_batch_set_shader(batch, shader);
+ int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT);
+
+ if (gpu_print_buf_used) {
+ GPU_debug_group_begin("GPU");
+ GPU_storagebuf_bind(gpu_print_buf_, slot);
+ GPU_batch_draw_indirect(batch, gpu_print_buf_, 0);
+ GPU_storagebuf_unbind(gpu_print_buf_);
+ GPU_debug_group_end();
+ }
+
+ GPU_debug_group_begin("CPU");
+ GPU_storagebuf_bind(cpu_print_buf_, slot);
+ GPU_batch_draw_indirect(batch, cpu_print_buf_, 0);
+ GPU_storagebuf_unbind(cpu_print_buf_);
+ GPU_debug_group_end();
+
+ GPU_debug_group_end();
+}
+
+void DebugDraw::display_to_view()
+{
+ GPU_debug_group_begin("DebugDraw");
+
+ display_lines();
+ /* Print 3D shapes before text to avoid overlaps. */
+ display_prints();
+ /* Init again so we don't draw the same thing twice. */
+ init();
+
+ GPU_debug_group_end();
+}
+
+} // namespace blender::draw
+
+blender::draw::DebugDraw *DRW_debug_get()
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return nullptr;
+ }
+ return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name C-API private
+ * \{ */
+
+void drw_debug_draw()
+{
+#ifdef DRAW_DEBUG
+ if (!GPU_shader_storage_buffer_objects_support() || DST.debug == nullptr) {
+ return;
+ }
+ /* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->display_to_view();
+#endif
+}
+
+/**
+ * NOTE: Init is once per draw manager cycle.
+ */
+void drw_debug_init()
+{
+ /* Module should not be used in release builds. */
+ /* TODO(@fclem): Hide the functions declarations without using `ifdefs` everywhere. */
+#ifdef DRAW_DEBUG
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ /* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */
+ if (DST.debug == nullptr) {
+ DST.debug = reinterpret_cast<DRWDebugModule *>(new blender::draw::DebugDraw());
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->init();
+#endif
+}
+
+void drw_debug_module_free(DRWDebugModule *module)
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ if (module != nullptr) {
+ delete reinterpret_cast<blender::draw::DebugDraw *>(module);
+ }
+}
+
+GPUStorageBuf *drw_debug_gpu_draw_buf_get()
+{
+ return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_draw_buf_get();
+}
+
+GPUStorageBuf *drw_debug_gpu_print_buf_get()
+{
+ return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_print_buf_get();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name C-API public
+ * \{ */
+
+void DRW_debug_modelmat_reset()
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_reset();
+}
+
+void DRW_debug_modelmat(const float modelmat[4][4])
+{
+#ifdef DRAW_DEBUG
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_set(modelmat);
+#else
+ UNUSED_VARS(modelmat);
+#endif
+}
+
+void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_line(v1, v2, color);
+}
+
+void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_polygon(
+ blender::Span<float3>((float3 *)v, vert_len), color);
+}
+
+void DRW_debug_m4(const float m[4][4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix(m);
+}
+
+void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ blender::float4x4 m4 = m;
+ if (invert) {
+ m4 = m4.inverted();
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix_as_bbox(m4, color);
+}
+
+void DRW_debug_bbox(const BoundBox *bbox, const float color[4])
+{
+#ifdef DRAW_DEBUG
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_bbox(*bbox, color);
+#else
+ UNUSED_VARS(bbox, color);
+#endif
+}
+
+void DRW_debug_sphere(const float center[3], float radius, const float color[4])
+{
+ if (!GPU_shader_storage_buffer_objects_support()) {
+ return;
+ }
+ reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_sphere(center, radius, color);
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_debug.h b/source/blender/draw/intern/draw_debug.h
index 333d734edb9..9a56a12242e 100644
--- a/source/blender/draw/intern/draw_debug.h
+++ b/source/blender/draw/intern/draw_debug.h
@@ -3,21 +3,38 @@
/** \file
* \ingroup draw
+ *
+ * \brief Simple API to draw debug shapes in the viewport.
+ * IMPORTANT: This is the legacy API for C. Use draw_debug.hh instead in new C++ code.
*/
#pragma once
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct DRWDebugModule DRWDebugModule;
+
struct BoundBox;
void DRW_debug_modelmat_reset(void);
void DRW_debug_modelmat(const float modelmat[4][4]);
+/**
+ * IMPORTANT: For now there is a limit of DRW_DEBUG_DRAW_VERT_MAX that can be drawn
+ * using all the draw functions.
+ */
void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]);
void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4]);
/**
* \note g_modelmat is still applied on top.
*/
void DRW_debug_m4(const float m[4][4]);
-void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], bool invert);
+void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4]);
void DRW_debug_bbox(const BoundBox *bbox, const float color[4]);
void DRW_debug_sphere(const float center[3], float radius, const float color[4]);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/draw/intern/draw_debug.hh b/source/blender/draw/intern/draw_debug.hh
new file mode 100644
index 00000000000..c83936bf1af
--- /dev/null
+++ b/source/blender/draw/intern/draw_debug.hh
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Simple API to draw debug shapes and log in the viewport.
+ *
+ * Both CPU and GPU implementation are supported and symmetrical (meaning GPU shader can use it
+ * too, see common_debug_print/draw_lib.glsl).
+ *
+ * NOTE: CPU logging will overlap GPU logging on screen as it is drawn after.
+ */
+
+#pragma once
+
+#include "BLI_math_vec_types.hh"
+#include "BLI_string_ref.hh"
+#include "BLI_vector.hh"
+#include "DNA_object_types.h"
+#include "DRW_gpu_wrapper.hh"
+
+namespace blender::draw {
+
+/* Shortcuts to avoid boilerplate code and match shader API. */
+#define drw_debug_line(...) DRW_debug_get()->draw_line(__VA_ARGS__)
+#define drw_debug_polygon(...) DRW_debug_get()->draw_polygon(__VA_ARGS__)
+#define drw_debug_bbox(...) DRW_debug_get()->draw_bbox(__VA_ARGS__)
+#define drw_debug_sphere(...) DRW_debug_get()->draw_sphere(__VA_ARGS__)
+#define drw_debug_point(...) DRW_debug_get()->draw_point(__VA_ARGS__)
+#define drw_debug_matrix(...) DRW_debug_get()->draw_matrix(__VA_ARGS__)
+#define drw_debug_matrix_as_bbox(...) DRW_debug_get()->draw_matrix_as_bbox(__VA_ARGS__)
+#define drw_print(...) DRW_debug_get()->print(__VA_ARGS__)
+#define drw_print_hex(...) DRW_debug_get()->print_hex(__VA_ARGS__)
+#define drw_print_binary(...) DRW_debug_get()->print_binary(__VA_ARGS__)
+#define drw_print_no_endl(...) DRW_debug_get()->print_no_endl(__VA_ARGS__)
+
+/* Will log variable along with its name, like the shader version of print(). */
+#define drw_print_id(v_) DRW_debug_get()->print(#v_, "= ", v_)
+#define drw_print_id_no_endl(v_) DRW_debug_get()->print_no_endl(#v_, "= ", v_)
+
+class DebugDraw {
+ private:
+ using DebugDrawBuf = StorageBuffer<DRWDebugDrawBuffer>;
+ using DebugPrintBuf = StorageBuffer<DRWDebugPrintBuffer>;
+
+ /** Data buffers containing all verts or chars to draw. */
+ DebugDrawBuf cpu_draw_buf_ = {"DebugDrawBuf-CPU"};
+ DebugDrawBuf gpu_draw_buf_ = {"DebugDrawBuf-GPU"};
+ DebugPrintBuf cpu_print_buf_ = {"DebugPrintBuf-CPU"};
+ DebugPrintBuf gpu_print_buf_ = {"DebugPrintBuf-GPU"};
+ /** True if the gpu buffer have been requested and may contain data to draw. */
+ bool gpu_print_buf_used = false;
+ bool gpu_draw_buf_used = false;
+ /** Matrix applied to all points before drawing. Could be a stack if needed. */
+ float4x4 model_mat_;
+ /** Precomputed shapes verts. */
+ Vector<float3> sphere_verts_;
+ Vector<float3> point_verts_;
+ /** Cursor position for print functionality. */
+ uint print_col_ = 0;
+ uint print_row_ = 0;
+
+ public:
+ DebugDraw();
+ ~DebugDraw(){};
+
+ /**
+ * Resets all buffers and reset model matrix state.
+ * Not to be called by user.
+ */
+ void init();
+
+ /**
+ * Resets model matrix state to identity.
+ */
+ void modelmat_reset();
+ /**
+ * Sets model matrix transform to apply to any vertex passed to drawing functions.
+ */
+ void modelmat_set(const float modelmat[4][4]);
+
+ /**
+ * Drawing functions that will draw wire-frames with the given color.
+ */
+ void draw_line(float3 v1, float3 v2, float4 color = {1, 0, 0, 1});
+ void draw_polygon(Span<float3> poly_verts, float4 color = {1, 0, 0, 1});
+ void draw_bbox(const BoundBox &bbox, const float4 color = {1, 0, 0, 1});
+ void draw_sphere(const float3 center, float radius, const float4 color = {1, 0, 0, 1});
+ void draw_point(const float3 center, float radius = 0.01f, const float4 color = {1, 0, 0, 1});
+ /**
+ * Draw a matrix transformation as 3 colored axes.
+ */
+ void draw_matrix(const float4x4 m4);
+ /**
+ * Draw a matrix as a 2 units length bounding box, centered on origin.
+ */
+ void draw_matrix_as_bbox(float4x4 mat, const float4 color = {1, 0, 0, 1});
+
+ /**
+ * Will draw all debug shapes and text cached up until now to the current view / frame-buffer.
+ * Draw buffers will be emptied and ready for new debug data.
+ */
+ void display_to_view();
+
+ /**
+ * Log variable or strings inside the viewport.
+ * Using a unique non string argument will print the variable name with it.
+ * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`.
+ */
+ template<typename... Ts> void print(StringRefNull str, Ts... args)
+ {
+ print_no_endl(str, args...);
+ print_newline();
+ }
+ template<typename T> void print(const T &value)
+ {
+ print_value(value);
+ print_newline();
+ }
+ template<typename T> void print_hex(const T &value)
+ {
+ print_value_hex(value);
+ print_newline();
+ }
+ template<typename T> void print_binary(const T &value)
+ {
+ print_value_binary(value);
+ print_newline();
+ }
+
+ /**
+ * Same as `print()` but does not finish the line.
+ */
+ void print_no_endl(std::string arg)
+ {
+ print_string(arg);
+ }
+ void print_no_endl(StringRef arg)
+ {
+ print_string(arg);
+ }
+ void print_no_endl(StringRefNull arg)
+ {
+ print_string(arg);
+ }
+ void print_no_endl(char const *arg)
+ {
+ print_string(StringRefNull(arg));
+ }
+ template<typename T> void print_no_endl(T arg)
+ {
+ print_value(arg);
+ }
+ template<typename T, typename... Ts> void print_no_endl(T arg, Ts... args)
+ {
+ print_no_endl(arg);
+ print_no_endl(args...);
+ }
+
+ /**
+ * Not to be called by user. Should become private.
+ */
+ GPUStorageBuf *gpu_draw_buf_get();
+ GPUStorageBuf *gpu_print_buf_get();
+
+ private:
+ uint color_pack(float4 color);
+ DRWDebugVert vert_pack(float3 pos, uint color);
+
+ void draw_line(float3 v1, float3 v2, uint color);
+
+ void print_newline();
+ void print_string_start(uint len);
+ void print_string(std::string str);
+ void print_char4(uint data);
+ void print_append_char(uint char1, uint &char4);
+ void print_append_digit(uint digit, uint &char4);
+ void print_append_space(uint &char4);
+ void print_value_binary(uint value);
+ void print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned);
+
+ template<typename T> void print_value(const T &value);
+ template<typename T> void print_value_hex(const T &value);
+ template<typename T> void print_value_binary(const T &value);
+
+ void display_lines();
+ void display_prints();
+};
+
+} // namespace blender::draw
+
+/**
+ * Ease of use function to get the debug module.
+ * TODO(fclem): Should be removed once DRWManager is no longer global.
+ * IMPORTANT: Can return nullptr if storage buffer is not supported.
+ */
+blender::draw::DebugDraw *DRW_debug_get();
diff --git a/source/blender/draw/intern/draw_defines.h b/source/blender/draw/intern/draw_defines.h
new file mode 100644
index 00000000000..3df7e47cffb
--- /dev/null
+++ b/source/blender/draw/intern/draw_defines.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * List of defines that are shared with the GPUShaderCreateInfos. We do this to avoid
+ * dragging larger headers into the createInfo pipeline which would cause problems.
+ */
+
+#pragma once
+
+#define DRW_VIEW_UBO_SLOT 0
+
+#define DRW_RESOURCE_ID_SLOT 11
+#define DRW_OBJ_MAT_SLOT 10
+#define DRW_OBJ_INFOS_SLOT 9
+#define DRW_OBJ_ATTR_SLOT 8
+
+#define DRW_DEBUG_PRINT_SLOT 15
+#define DRW_DEBUG_DRAW_SLOT 14
+
+#define DRW_COMMAND_GROUP_SIZE 64
+#define DRW_FINALIZE_GROUP_SIZE 64
+/* Must be multiple of 32. Set to 32 for shader simplicity. */
+#define DRW_VISIBILITY_GROUP_SIZE 32
diff --git a/source/blender/draw/intern/draw_hair.cc b/source/blender/draw/intern/draw_hair.cc
index dc791314333..ceee1c7cb48 100644
--- a/source/blender/draw/intern/draw_hair.cc
+++ b/source/blender/draw/intern/draw_hair.cc
@@ -22,6 +22,7 @@
#include "GPU_batch.h"
#include "GPU_capabilities.h"
#include "GPU_compute.h"
+#include "GPU_context.h"
#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
@@ -33,25 +34,17 @@
#include "draw_shader.h"
#include "draw_shader_shared.h"
-#ifndef __APPLE__
-# define USE_TRANSFORM_FEEDBACK
-# define USE_COMPUTE_SHADERS
-#endif
-
BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get()
{
-#ifdef USE_COMPUTE_SHADERS
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) {
return PART_REFINE_SHADER_COMPUTE;
}
-#endif
-#ifdef USE_TRANSFORM_FEEDBACK
- return PART_REFINE_SHADER_TRANSFORM_FEEDBACK;
-#endif
+ if (GPU_transform_feedback_support()) {
+ return PART_REFINE_SHADER_TRANSFORM_FEEDBACK;
+ }
return PART_REFINE_SHADER_TRANSFORM_FEEDBACK_WORKAROUND;
}
-#ifndef USE_TRANSFORM_FEEDBACK
struct ParticleRefineCall {
struct ParticleRefineCall *next;
GPUVertBuf *vbo;
@@ -63,11 +56,10 @@ static ParticleRefineCall *g_tf_calls = nullptr;
static int g_tf_id_offset;
static int g_tf_target_width;
static int g_tf_target_height;
-#endif
static GPUVertBuf *g_dummy_vbo = nullptr;
static GPUTexture *g_dummy_texture = nullptr;
-static DRWPass *g_tf_pass; /* XXX can be a problem with multiple DRWManager in the future */
+static DRWPass *g_tf_pass; /* XXX can be a problem with multiple #DRWManager in the future */
static blender::draw::UniformBuffer<CurvesInfos> *g_dummy_curves_info = nullptr;
static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement)
@@ -77,23 +69,25 @@ static GPUShader *hair_refine_shader_get(ParticleRefineShader refinement)
void DRW_hair_init(void)
{
-#if defined(USE_TRANSFORM_FEEDBACK) || defined(USE_COMPUTE_SHADERS)
- g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_NO_DRAW);
-#else
- g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR);
-#endif
+ if (GPU_transform_feedback_support() || GPU_compute_shader_support()) {
+ g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_NO_DRAW);
+ }
+ else {
+ g_tf_pass = DRW_pass_create("Update Hair Pass", DRW_STATE_WRITE_COLOR);
+ }
if (g_dummy_vbo == nullptr) {
/* initialize vertex format */
GPUVertFormat format = {0};
uint dummy_id = GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- g_dummy_vbo = GPU_vertbuf_create_with_format(&format);
+ g_dummy_vbo = GPU_vertbuf_create_with_format_ex(
+ &format, GPU_USAGE_STATIC | GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
const float vert[4] = {0.0f, 0.0f, 0.0f, 0.0f};
GPU_vertbuf_data_alloc(g_dummy_vbo, 1);
GPU_vertbuf_attr_fill(g_dummy_vbo, dummy_id, vert);
- /* Create vbo immediately to bind to texture buffer. */
+ /* Create VBO immediately to bind to texture buffer. */
GPU_vertbuf_use(g_dummy_vbo);
g_dummy_texture = GPU_texture_create_from_vertbuf("hair_dummy_attr", g_dummy_vbo);
@@ -146,22 +140,25 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache
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 = (ParticleRefineCall *)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
+ DRWShadingGroup *tf_shgrp = nullptr;
+ if (GPU_transform_feedback_support()) {
+ tf_shgrp = DRW_shgroup_transform_feedback_create(
+ tf_shader, g_tf_pass, cache->final[subdiv].proc_buf);
+ }
+ else {
+ tf_shgrp = DRW_shgroup_create(tf_shader, g_tf_pass);
+
+ ParticleRefineCall *pr_call = (ParticleRefineCall *)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);
+ }
+ BLI_assert(tf_shgrp != nullptr);
drw_hair_particle_cache_shgrp_attach_resources(tf_shgrp, cache, subdiv);
DRW_shgroup_call_procedural_points(tf_shgrp, nullptr, final_points_len);
@@ -250,7 +247,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent);
- /* TODO: optimize this. Only bind the ones GPUMaterial needs. */
+ /* TODO: optimize this. Only bind the ones #GPUMaterial needs. */
for (int i = 0; i < hair_cache->num_uv_layers; i++) {
for (int n = 0; n < MAX_LAYER_NAME_CT && hair_cache->uv_layer_names[i][n][0] != '\0'; n++) {
DRW_shgroup_uniform_texture(shgrp, hair_cache->uv_layer_names[i][n], hair_cache->uv_tex[i]);
@@ -306,81 +303,117 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
void DRW_hair_update()
{
-#ifndef USE_TRANSFORM_FEEDBACK
- /**
- * Workaround to transform feedback not working on mac.
- * On some system it crashes (see T58489) and on some other it renders garbage (see T60171).
- *
- * So instead of using transform feedback we render to a texture,
- * read back the result to system memory and re-upload as VBO data.
- * It is really not ideal performance wise, but it is the simplest
- * and the most local workaround that still uses the power of the GPU.
- */
-
- if (g_tf_calls == nullptr) {
- return;
- }
+ if (!GPU_transform_feedback_support()) {
+ /**
+ * Workaround to transform feedback not working on mac.
+ * On some system it crashes (see T58489) and on some other it renders garbage (see T60171).
+ *
+ * So instead of using transform feedback we render to a texture,
+ * read back the result to system memory and re-upload as VBO data.
+ * It is really not ideal performance wise, but it is the simplest
+ * and the most local workaround that still uses the power of the GPU.
+ */
+
+ if (g_tf_calls == nullptr) {
+ return;
+ }
- /* Search ideal buffer size. */
- uint max_size = 0;
- for (ParticleRefineCall *pr_call = g_tf_calls; pr_call; pr_call = pr_call->next) {
- max_size = max_ii(max_size, pr_call->vert_len);
- }
+ /* Search ideal buffer size. */
+ uint max_size = 0;
+ for (ParticleRefineCall *pr_call = g_tf_calls; pr_call; pr_call = pr_call->next) {
+ max_size = max_ii(max_size, pr_call->vert_len);
+ }
+
+ /* Create target Texture / Frame-buffer */
+ /* Don't use max size as it can be really heavy and fail.
+ * Do chunks of maximum 2048 * 2048 hair points. */
+ int width = 2048;
+ int height = min_ii(width, 1 + max_size / width);
+ GPUTexture *tex = DRW_texture_pool_query_2d(
+ width, height, GPU_RGBA32F, (DrawEngineType *)DRW_hair_update);
+ g_tf_target_height = height;
+ g_tf_target_width = width;
+
+ GPUFrameBuffer *fb = nullptr;
+ GPU_framebuffer_ensure_config(&fb,
+ {
+ GPU_ATTACHMENT_NONE,
+ GPU_ATTACHMENT_TEXTURE(tex),
+ });
+
+ float *data = (float *)MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer");
+
+ GPU_framebuffer_bind(fb);
+ while (g_tf_calls != nullptr) {
+ ParticleRefineCall *pr_call = g_tf_calls;
+ g_tf_calls = g_tf_calls->next;
+
+ g_tf_id_offset = 0;
+ while (pr_call->vert_len > 0) {
+ int max_read_px_len = min_ii(width * height, pr_call->vert_len);
+
+ DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp);
+ /* Read back result to main memory. */
+ GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data);
+ /* Upload back to VBO. */
+ GPU_vertbuf_use(pr_call->vbo);
+ GPU_vertbuf_update_sub(pr_call->vbo,
+ sizeof(float[4]) * g_tf_id_offset,
+ sizeof(float[4]) * max_read_px_len,
+ data);
+
+ g_tf_id_offset += max_read_px_len;
+ pr_call->vert_len -= max_read_px_len;
+ }
- /* Create target Texture / Frame-buffer */
- /* Don't use max size as it can be really heavy and fail.
- * Do chunks of maximum 2048 * 2048 hair points. */
- int width = 2048;
- int height = min_ii(width, 1 + max_size / width);
- GPUTexture *tex = DRW_texture_pool_query_2d(
- width, height, GPU_RGBA32F, (DrawEngineType *)DRW_hair_update);
- g_tf_target_height = height;
- g_tf_target_width = width;
-
- GPUFrameBuffer *fb = nullptr;
- GPU_framebuffer_ensure_config(&fb,
- {
- GPU_ATTACHMENT_NONE,
- GPU_ATTACHMENT_TEXTURE(tex),
- });
-
- float *data = (float *)MEM_mallocN(sizeof(float[4]) * width * height, "tf fallback buffer");
-
- GPU_framebuffer_bind(fb);
- while (g_tf_calls != nullptr) {
- ParticleRefineCall *pr_call = g_tf_calls;
- g_tf_calls = g_tf_calls->next;
-
- g_tf_id_offset = 0;
- while (pr_call->vert_len > 0) {
- int max_read_px_len = min_ii(width * height, pr_call->vert_len);
-
- DRW_draw_pass_subset(g_tf_pass, pr_call->shgrp, pr_call->shgrp);
- /* Read back result to main memory. */
- GPU_framebuffer_read_color(fb, 0, 0, width, height, 4, 0, GPU_DATA_FLOAT, data);
- /* Upload back to VBO. */
- GPU_vertbuf_use(pr_call->vbo);
- GPU_vertbuf_update_sub(pr_call->vbo,
- sizeof(float[4]) * g_tf_id_offset,
- sizeof(float[4]) * max_read_px_len,
- data);
-
- g_tf_id_offset += max_read_px_len;
- pr_call->vert_len -= max_read_px_len;
+ MEM_freeN(pr_call);
}
- MEM_freeN(pr_call);
+ MEM_freeN(data);
+ GPU_framebuffer_free(fb);
}
+ else {
+ /* NOTE(Metal): If compute is not supported, bind a temporary frame-buffer to avoid
+ * side-effects from rendering in the active buffer.
+ * We also need to guarantee that a frame-buffer is active to perform any rendering work,
+ * even if there is no output. */
+ GPUFrameBuffer *temp_fb = nullptr;
+ GPUFrameBuffer *prev_fb = nullptr;
+ if (GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_METAL)) {
+ if (!GPU_compute_shader_support()) {
+ prev_fb = GPU_framebuffer_active_get();
+ char errorOut[256];
+ /* if the frame-buffer is invalid we need a dummy frame-buffer to be bound. */
+ if (!GPU_framebuffer_check_valid(prev_fb, errorOut)) {
+ int width = 64;
+ int height = 64;
+ GPUTexture *tex = DRW_texture_pool_query_2d(
+ width, height, GPU_DEPTH_COMPONENT32F, (DrawEngineType *)DRW_hair_update);
+ g_tf_target_height = height;
+ g_tf_target_width = width;
+
+ GPU_framebuffer_ensure_config(&temp_fb, {GPU_ATTACHMENT_TEXTURE(tex)});
+
+ GPU_framebuffer_bind(temp_fb);
+ }
+ }
+ }
- MEM_freeN(data);
- GPU_framebuffer_free(fb);
-#else
- /* Just render the pass when using compute shaders or transform feedback. */
- DRW_draw_pass(g_tf_pass);
- if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) {
- GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+ /* Just render the pass when using compute shaders or transform feedback. */
+ DRW_draw_pass(g_tf_pass);
+ if (drw_hair_shader_type_get() == PART_REFINE_SHADER_COMPUTE) {
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+
+ /* Release temporary frame-buffer. */
+ if (temp_fb != nullptr) {
+ GPU_framebuffer_free(temp_fb);
+ }
+ /* Rebind existing frame-buffer */
+ if (prev_fb != nullptr) {
+ GPU_framebuffer_bind(prev_fb);
+ }
}
-#endif
}
void DRW_hair_free(void)
diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h
index 5d84c8863f2..c7e9e1e22de 100644
--- a/source/blender/draw/intern/draw_hair_private.h
+++ b/source/blender/draw/intern/draw_hair_private.h
@@ -61,9 +61,9 @@ typedef struct ParticleHairCache {
GPUTexture *uv_tex[MAX_MTFACE];
char uv_layer_names[MAX_MTFACE][MAX_LAYER_NAME_CT][MAX_LAYER_NAME_LEN];
- GPUVertBuf *proc_col_buf[MAX_MCOL];
- GPUTexture *col_tex[MAX_MCOL];
- char col_layer_names[MAX_MCOL][MAX_LAYER_NAME_CT][MAX_LAYER_NAME_LEN];
+ GPUVertBuf **proc_col_buf;
+ GPUTexture **col_tex;
+ char (*col_layer_names)[MAX_LAYER_NAME_CT][MAX_LAYER_NAME_LEN];
int num_uv_layers;
int num_col_layers;
diff --git a/source/blender/draw/intern/draw_handle.hh b/source/blender/draw/intern/draw_handle.hh
new file mode 100644
index 00000000000..5f96bfa5dcd
--- /dev/null
+++ b/source/blender/draw/intern/draw_handle.hh
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+#pragma once
+
+/** \file
+ * \ingroup draw
+ *
+ * A unique identifier for each object component.
+ * It is used to access each component data such as matrices and object attributes.
+ * It is valid only for the current draw, it is not persistent.
+ *
+ * The most significant bit is used to encode if the object needs to invert the front face winding
+ * because of its object matrix handedness. This is handy because this means sorting inside
+ * #DrawGroup command will put all inverted commands last.
+ *
+ * Default value of 0 points toward an non-cull-able object with unit bounding box centered at
+ * the origin.
+ */
+
+#include "draw_shader_shared.h"
+
+struct Object;
+struct DupliObject;
+
+namespace blender::draw {
+
+struct ResourceHandle {
+ uint raw;
+
+ ResourceHandle() = default;
+ ResourceHandle(uint raw_) : raw(raw_){};
+ ResourceHandle(uint index, bool inverted_handedness)
+ {
+ raw = index;
+ SET_FLAG_FROM_TEST(raw, inverted_handedness, 0x80000000u);
+ }
+
+ bool has_inverted_handedness() const
+ {
+ return (raw & 0x80000000u) != 0;
+ }
+
+ uint resource_index() const
+ {
+ return (raw & 0x7FFFFFFFu);
+ }
+};
+
+/* TODO(fclem): Move to somewhere more appropriated after cleaning up the header dependencies. */
+struct ObjectRef {
+ Object *object;
+ /** Dupli object that corresponds to the current object. */
+ DupliObject *dupli_object;
+ /** Object that created the dupli-list the current object is part of. */
+ Object *dupli_parent;
+};
+
+}; // namespace blender::draw
diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c
index 0e4e67f3320..ac2aea4524d 100644
--- a/source/blender/draw/intern/draw_instance_data.c
+++ b/source/blender/draw/intern/draw_instance_data.c
@@ -27,6 +27,7 @@
#include "BKE_duplilist.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "BLI_bitmap.h"
#include "BLI_memblock.h"
@@ -563,7 +564,8 @@ typedef struct DRWUniformAttrBuf {
struct DRWUniformAttrBuf *next_empty;
} DRWUniformAttrBuf;
-static DRWUniformAttrBuf *drw_uniform_attrs_pool_ensure(GHash *table, GPUUniformAttrList *key)
+static DRWUniformAttrBuf *drw_uniform_attrs_pool_ensure(GHash *table,
+ const GPUUniformAttrList *key)
{
void **pkey, **pval;
@@ -641,23 +643,16 @@ static void drw_uniform_attribute_lookup(GPUUniformAttr *attr,
{
copy_v4_fl(r_data, 0);
- char idprop_name[(sizeof(attr->name) * 2) + 4];
- {
- char attr_name_esc[sizeof(attr->name) * 2];
- BLI_str_escape(attr_name_esc, attr->name, sizeof(attr_name_esc));
- SNPRINTF(idprop_name, "[\"%s\"]", attr_name_esc);
- }
-
/* If requesting instance data, check the parent particle system and object. */
if (attr->use_dupli) {
if (dupli_source && dupli_source->particle_system) {
ParticleSettings *settings = dupli_source->particle_system->part;
- if (drw_uniform_property_lookup((ID *)settings, idprop_name, r_data) ||
+ if (drw_uniform_property_lookup((ID *)settings, attr->name_id_prop, r_data) ||
drw_uniform_property_lookup((ID *)settings, attr->name, r_data)) {
return;
}
}
- if (drw_uniform_property_lookup((ID *)dupli_parent, idprop_name, r_data) ||
+ if (drw_uniform_property_lookup((ID *)dupli_parent, attr->name_id_prop, r_data) ||
drw_uniform_property_lookup((ID *)dupli_parent, attr->name, r_data)) {
return;
}
@@ -665,9 +660,9 @@ static void drw_uniform_attribute_lookup(GPUUniformAttr *attr,
/* Check the object and mesh. */
if (ob) {
- if (drw_uniform_property_lookup((ID *)ob, idprop_name, r_data) ||
+ if (drw_uniform_property_lookup((ID *)ob, attr->name_id_prop, r_data) ||
drw_uniform_property_lookup((ID *)ob, attr->name, r_data) ||
- drw_uniform_property_lookup((ID *)ob->data, idprop_name, r_data) ||
+ drw_uniform_property_lookup((ID *)ob->data, attr->name_id_prop, r_data) ||
drw_uniform_property_lookup((ID *)ob->data, attr->name, r_data)) {
return;
}
@@ -675,7 +670,7 @@ static void drw_uniform_attribute_lookup(GPUUniformAttr *attr,
}
void drw_uniform_attrs_pool_update(GHash *table,
- GPUUniformAttrList *key,
+ const GPUUniformAttrList *key,
DRWResourceHandle *handle,
Object *ob,
Object *dupli_parent,
@@ -696,7 +691,8 @@ void drw_uniform_attrs_pool_update(GHash *table,
}
}
-DRWSparseUniformBuf *DRW_uniform_attrs_pool_find_ubo(GHash *table, struct GPUUniformAttrList *key)
+DRWSparseUniformBuf *DRW_uniform_attrs_pool_find_ubo(GHash *table,
+ const struct GPUUniformAttrList *key)
{
DRWUniformAttrBuf *buffer = BLI_ghash_lookup(table, key);
return buffer ? &buffer->ubos : NULL;
diff --git a/source/blender/draw/intern/draw_instance_data.h b/source/blender/draw/intern/draw_instance_data.h
index 4b5cf63bb3b..9053544d98a 100644
--- a/source/blender/draw/intern/draw_instance_data.h
+++ b/source/blender/draw/intern/draw_instance_data.h
@@ -106,4 +106,4 @@ struct GHash *DRW_uniform_attrs_pool_new(void);
void DRW_uniform_attrs_pool_flush_all(struct GHash *table);
void DRW_uniform_attrs_pool_clear_all(struct GHash *table);
struct DRWSparseUniformBuf *DRW_uniform_attrs_pool_find_ubo(struct GHash *table,
- struct GPUUniformAttrList *key);
+ const struct GPUUniformAttrList *key);
diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c
index bc9d0a3d02a..e1bee89db60 100644
--- a/source/blender/draw/intern/draw_manager.c
+++ b/source/blender/draw/intern/draw_manager.c
@@ -43,6 +43,7 @@
#include "DNA_camera_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_userdef_types.h"
#include "DNA_world_types.h"
#include "ED_gpencil.h"
@@ -84,6 +85,7 @@
#include "draw_cache_impl.h"
#include "engines/basic/basic_engine.h"
+#include "engines/compositor/compositor_engine.h"
#include "engines/eevee/eevee_engine.h"
#include "engines/eevee_next/eevee_engine.h"
#include "engines/external/external_engine.h"
@@ -179,7 +181,7 @@ static void drw_task_graph_deinit(void)
bool DRW_object_is_renderable(const Object *ob)
{
- BLI_assert((ob->base_flag & BASE_VISIBLE_DEPSGRAPH) != 0);
+ BLI_assert((ob->base_flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0);
if (ob->type == OB_MESH) {
if ((ob == DST.draw_ctx.object_edit) || DRW_object_is_in_edit_mode(ob)) {
@@ -212,17 +214,6 @@ int DRW_object_visibility_in_active_context(const Object *ob)
return BKE_object_visibility(ob, mode);
}
-bool DRW_object_is_flat_normal(const Object *ob)
-{
- if (ob->type == OB_MESH) {
- const Mesh *me = ob->data;
- if (me->mpoly && me->mpoly[0].flag & ME_SMOOTH) {
- return false;
- }
- }
- return true;
-}
-
bool DRW_object_use_hide_faces(const struct Object *ob)
{
if (ob->type == OB_MESH) {
@@ -235,7 +226,7 @@ bool DRW_object_use_hide_faces(const struct Object *ob)
return (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
case OB_MODE_VERTEX_PAINT:
case OB_MODE_WEIGHT_PAINT:
- return (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)) != 0;
+ return true;
}
}
@@ -1010,6 +1001,8 @@ static void drw_engines_init(void)
static void drw_engines_cache_init(void)
{
+ DRW_manager_begin_sync();
+
DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) {
if (data->text_draw_cache) {
DRW_text_cache_destroy(data->text_draw_cache);
@@ -1081,6 +1074,8 @@ static void drw_engines_cache_finish(void)
engine->cache_finish(data);
}
}
+
+ DRW_manager_end_sync();
}
static void drw_engines_draw_scene(void)
@@ -1225,6 +1220,31 @@ static void drw_engines_enable_editors(void)
}
}
+static bool is_compositor_enabled(void)
+{
+ if (!U.experimental.use_realtime_compositor) {
+ return false;
+ }
+
+ if (!(DST.draw_ctx.v3d->shading.flag & V3D_SHADING_COMPOSITOR)) {
+ return false;
+ }
+
+ if (!(DST.draw_ctx.v3d->shading.type >= OB_MATERIAL)) {
+ return false;
+ }
+
+ if (!DST.draw_ctx.scene->use_nodes) {
+ return false;
+ }
+
+ if (!DST.draw_ctx.scene->nodetree) {
+ return false;
+ }
+
+ return true;
+}
+
static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
RenderEngineType *engine_type,
bool gpencil_engine_needed)
@@ -1237,6 +1257,11 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer),
if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) {
use_drw_engine(&draw_engine_gpencil_type);
}
+
+ if (is_compositor_enabled()) {
+ use_drw_engine(&draw_engine_compositor_type);
+ }
+
drw_engines_enable_overlays();
#ifdef WITH_DRAW_DEBUG
@@ -1299,13 +1324,14 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx)
/* Reset before using it. */
drw_state_prepare_clean_for_draw(&DST);
+ BKE_view_layer_synced_ensure(scene, view_layer);
DST.draw_ctx = (DRWContextState){
.region = region,
.rv3d = rv3d,
.v3d = v3d,
.scene = scene,
.view_layer = view_layer,
- .obact = OBACT(view_layer),
+ .obact = BKE_view_layer_active_object_get(view_layer),
.engine_type = engine_type,
.depsgraph = depsgraph,
.object_mode = OB_MODE_OBJECT,
@@ -1323,11 +1349,7 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx)
drw_engines_enable(view_layer, engine_type, gpencil_engine_needed);
drw_engines_data_validate();
- DRW_ENABLED_ENGINE_ITER (DST.view_data_active, draw_engine, data) {
- if (draw_engine->view_update) {
- draw_engine->view_update(data);
- }
- }
+ DRW_view_data_engines_view_update(DST.view_data_active);
drw_engines_disable();
}
@@ -1356,13 +1378,14 @@ static void drw_notify_view_update_offscreen(struct Depsgraph *depsgraph,
/* Reset before using it. */
drw_state_prepare_clean_for_draw(&DST);
+ BKE_view_layer_synced_ensure(scene, view_layer);
DST.draw_ctx = (DRWContextState){
.region = region,
.rv3d = rv3d,
.v3d = v3d,
.scene = scene,
.view_layer = view_layer,
- .obact = OBACT(view_layer),
+ .obact = BKE_view_layer_active_object_get(view_layer),
.engine_type = engine_type,
.depsgraph = depsgraph,
};
@@ -1379,11 +1402,7 @@ static void drw_notify_view_update_offscreen(struct Depsgraph *depsgraph,
drw_engines_enable(view_layer, engine_type, gpencil_engine_needed);
drw_engines_data_validate();
- DRW_ENABLED_ENGINE_ITER (DST.view_data_active, draw_engine, data) {
- if (draw_engine->view_update) {
- draw_engine->view_update(data);
- }
- }
+ DRW_view_data_engines_view_update(DST.view_data_active);
drw_engines_disable();
}
@@ -1485,7 +1504,7 @@ void DRW_draw_callbacks_post_scene(void)
DRW_draw_region_info();
- /* annotations - temporary drawing buffer (screenspace) */
+ /* Annotations - temporary drawing buffer (screen-space). */
/* XXX: Or should we use a proper draw/overlay engine for this case? */
if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (do_annotations)) {
GPU_depth_test(GPU_DEPTH_NONE);
@@ -1608,11 +1627,11 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
GPUViewport *viewport,
const bContext *evil_C)
{
-
Scene *scene = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
RegionView3D *rv3d = region->regiondata;
+ BKE_view_layer_synced_ensure(scene, view_layer);
DST.draw_ctx.evil_C = evil_C;
DST.draw_ctx = (DRWContextState){
.region = region,
@@ -1620,7 +1639,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph,
.v3d = v3d,
.scene = scene,
.view_layer = view_layer,
- .obact = OBACT(view_layer),
+ .obact = BKE_view_layer_active_object_get(view_layer),
.engine_type = engine_type,
.depsgraph = depsgraph,
@@ -2127,12 +2146,13 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph,
Scene *scene = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
+ BKE_view_layer_synced_ensure(scene, view_layer);
DST.draw_ctx.evil_C = evil_C;
DST.draw_ctx = (DRWContextState){
.region = region,
.scene = scene,
.view_layer = view_layer,
- .obact = OBACT(view_layer),
+ .obact = BKE_view_layer_active_object_get(view_layer),
.depsgraph = depsgraph,
.space_data = CTX_wm_space_data(evil_C),
@@ -2333,7 +2353,9 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
Scene *scene = DEG_get_evaluated_scene(depsgraph);
RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->shading.type);
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
- Object *obact = OBACT(view_layer);
+
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
Object *obedit = use_obedit_skip ? NULL : OBEDIT_FROM_OBACT(obact);
#ifndef USE_GPU_SELECT
UNUSED_VARS(scene, view_layer, v3d, region, rect);
@@ -2442,7 +2464,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
drw_engines_world_update(scene);
if (use_obedit) {
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, object_type, object_mode, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, object_type, object_mode, ob_iter) {
drw_engines_cache_populate(ob_iter);
}
FOREACH_OBJECT_IN_MODE_END;
@@ -2463,7 +2485,7 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph,
}
if (use_pose_exception && (ob->mode & OB_MODE_POSE)) {
- if ((ob->base_flag & BASE_VISIBLE_VIEWLAYER) == 0) {
+ if ((ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) {
continue;
}
}
@@ -2564,13 +2586,14 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph,
DST.options.is_depth = true;
/* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
+ BKE_view_layer_synced_ensure(scene, view_layer);
DST.draw_ctx = (DRWContextState){
.region = region,
.rv3d = rv3d,
.v3d = v3d,
.scene = scene,
.view_layer = view_layer,
- .obact = OBACT(view_layer),
+ .obact = BKE_view_layer_active_object_get(view_layer),
.engine_type = engine_type,
.depsgraph = depsgraph,
};
@@ -2683,7 +2706,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons
GPUViewport *viewport = WM_draw_region_get_viewport(region);
if (!viewport) {
/* Selection engine requires a viewport.
- * TODO(germano): This should be done internally in the engine. */
+ * TODO(@germano): This should be done internally in the engine. */
sel_ctx->is_dirty = true;
sel_ctx->objects_drawn_len = 0;
sel_ctx->index_drawn_len = 1;
@@ -2697,13 +2720,14 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons
drw_state_prepare_clean_for_draw(&DST);
/* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */
+ BKE_view_layer_synced_ensure(scene, view_layer);
DST.draw_ctx = (DRWContextState){
.region = region,
.rv3d = region->regiondata,
.v3d = v3d,
.scene = scene,
.view_layer = view_layer,
- .obact = OBACT(view_layer),
+ .obact = BKE_view_layer_active_object_get(view_layer),
.depsgraph = depsgraph,
};
drw_task_graph_init();
@@ -2959,6 +2983,7 @@ void DRW_engines_register(void)
DRW_engine_register(&draw_engine_overlay_type);
DRW_engine_register(&draw_engine_select_type);
DRW_engine_register(&draw_engine_basic_type);
+ DRW_engine_register(&draw_engine_compositor_type);
#ifdef WITH_DRAW_DEBUG
DRW_engine_register(&draw_engine_debug_select_type);
#endif
@@ -2968,9 +2993,6 @@ void DRW_engines_register(void)
/* setup callbacks */
{
- BKE_mball_batch_cache_dirty_tag_cb = DRW_mball_batch_cache_dirty_tag;
- BKE_mball_batch_cache_free_cb = DRW_mball_batch_cache_free;
-
BKE_curve_batch_cache_dirty_tag_cb = DRW_curve_batch_cache_dirty_tag;
BKE_curve_batch_cache_free_cb = DRW_curve_batch_cache_free;
@@ -3039,6 +3061,9 @@ void DRW_engines_free(void)
DRW_stats_free();
DRW_globals_free();
+ drw_debug_module_free(DST.debug);
+ DST.debug = NULL;
+
DRW_UBO_FREE_SAFE(G_draw.block_ubo);
DRW_UBO_FREE_SAFE(G_draw.view_ubo);
DRW_TEXTURE_FREE_SAFE(G_draw.ramp);
diff --git a/source/blender/draw/intern/draw_manager.cc b/source/blender/draw/intern/draw_manager.cc
new file mode 100644
index 00000000000..169d86b2ea1
--- /dev/null
+++ b/source/blender/draw/intern/draw_manager.cc
@@ -0,0 +1,214 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "BKE_global.h"
+#include "GPU_compute.h"
+
+#include "draw_debug.hh"
+#include "draw_defines.h"
+#include "draw_manager.h"
+#include "draw_manager.hh"
+#include "draw_pass.hh"
+#include "draw_shader.h"
+
+namespace blender::draw {
+
+Manager::~Manager()
+{
+ for (GPUTexture *texture : acquired_textures) {
+ /* Decrease refcount and free if 0. */
+ GPU_texture_free(texture);
+ }
+}
+
+void Manager::begin_sync()
+{
+ /* TODO: This means the reference is kept until further redraw or manager tear-down. Instead,
+ * they should be released after each draw loop. But for now, mimics old DRW behavior. */
+ for (GPUTexture *texture : acquired_textures) {
+ /* Decrease refcount and free if 0. */
+ GPU_texture_free(texture);
+ }
+
+ acquired_textures.clear();
+
+#ifdef DEBUG
+ /* Detect uninitialized data. */
+ memset(matrix_buf.data(), 0xF0, resource_len_ * sizeof(*matrix_buf.data()));
+ memset(bounds_buf.data(), 0xF0, resource_len_ * sizeof(*bounds_buf.data()));
+ memset(infos_buf.data(), 0xF0, resource_len_ * sizeof(*infos_buf.data()));
+#endif
+ resource_len_ = 0;
+ attribute_len_ = 0;
+ /* TODO(fclem): Resize buffers if too big, but with an hysteresis threshold. */
+
+ object_active = DST.draw_ctx.obact;
+
+ /* Init the 0 resource. */
+ resource_handle(float4x4::identity());
+}
+
+void Manager::end_sync()
+{
+ GPU_debug_group_begin("Manager.end_sync");
+
+ matrix_buf.push_update();
+ bounds_buf.push_update();
+ infos_buf.push_update();
+ attributes_buf.push_update();
+ attributes_buf_legacy.push_update();
+
+ /* Useful for debugging the following resource finalize. But will trigger the drawing of the GPU
+ * debug draw/print buffers for every frame. Not nice for performance. */
+ // debug_bind();
+
+ /* Dispatch compute to finalize the resources on GPU. Save a bit of CPU time. */
+ uint thread_groups = divide_ceil_u(resource_len_, DRW_FINALIZE_GROUP_SIZE);
+ GPUShader *shader = DRW_shader_draw_resource_finalize_get();
+ GPU_shader_bind(shader);
+ GPU_shader_uniform_1i(shader, "resource_len", resource_len_);
+ GPU_storagebuf_bind(matrix_buf, GPU_shader_get_ssbo(shader, "matrix_buf"));
+ GPU_storagebuf_bind(bounds_buf, GPU_shader_get_ssbo(shader, "bounds_buf"));
+ GPU_storagebuf_bind(infos_buf, GPU_shader_get_ssbo(shader, "infos_buf"));
+ GPU_compute_dispatch(shader, thread_groups, 1, 1);
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+
+ GPU_debug_group_end();
+}
+
+void Manager::debug_bind()
+{
+#ifdef DEBUG
+ if (DST.debug == nullptr) {
+ return;
+ }
+ GPU_storagebuf_bind(drw_debug_gpu_draw_buf_get(), DRW_DEBUG_DRAW_SLOT);
+ GPU_storagebuf_bind(drw_debug_gpu_print_buf_get(), DRW_DEBUG_PRINT_SLOT);
+# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER
+ /* Add a barrier to allow multiple shader writing to the same buffer. */
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+# endif
+#endif
+}
+
+void Manager::resource_bind()
+{
+ GPU_storagebuf_bind(matrix_buf, DRW_OBJ_MAT_SLOT);
+ GPU_storagebuf_bind(infos_buf, DRW_OBJ_INFOS_SLOT);
+ GPU_storagebuf_bind(attributes_buf, DRW_OBJ_ATTR_SLOT);
+ /* 2 is the hardcoded location of the uniform attr UBO. */
+ /* TODO(@fclem): Remove this workaround. */
+ GPU_uniformbuf_bind(attributes_buf_legacy, 2);
+}
+
+void Manager::submit(PassSimple &pass, View &view)
+{
+ view.bind();
+
+ debug_bind();
+
+ command::RecordingState state;
+ state.inverted_view = view.is_inverted();
+
+ pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_);
+
+ resource_bind();
+
+ pass.submit(state);
+
+ state.cleanup();
+}
+
+void Manager::submit(PassMain &pass, View &view)
+{
+ view.bind();
+
+ debug_bind();
+
+ bool freeze_culling = (U.experimental.use_viewport_debug && DST.draw_ctx.v3d &&
+ (DST.draw_ctx.v3d->debug_flag & V3D_DEBUG_FREEZE_CULLING) != 0);
+
+ view.compute_visibility(bounds_buf, resource_len_, freeze_culling);
+
+ command::RecordingState state;
+ state.inverted_view = view.is_inverted();
+
+ pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_, view.visibility_buf_);
+
+ resource_bind();
+
+ pass.submit(state);
+
+ state.cleanup();
+}
+
+void Manager::submit(PassSortable &pass, View &view)
+{
+ pass.sort();
+
+ this->submit(static_cast<PassMain &>(pass), view);
+}
+
+void Manager::submit(PassSimple &pass)
+{
+ debug_bind();
+
+ command::RecordingState state;
+
+ pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_);
+
+ resource_bind();
+
+ pass.submit(state);
+
+ state.cleanup();
+}
+
+Manager::SubmitDebugOutput Manager::submit_debug(PassSimple &pass, View &view)
+{
+ submit(pass, view);
+
+ pass.draw_commands_buf_.resource_id_buf_.read();
+
+ Manager::SubmitDebugOutput output;
+ output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
+ pass.draw_commands_buf_.resource_id_count_};
+ /* There is no visibility data for PassSimple. */
+ output.visibility = {(uint *)view.visibility_buf_.data(), 0};
+ return output;
+}
+
+Manager::SubmitDebugOutput Manager::submit_debug(PassMain &pass, View &view)
+{
+ submit(pass, view);
+
+ GPU_finish();
+
+ pass.draw_commands_buf_.resource_id_buf_.read();
+ view.visibility_buf_.read();
+
+ Manager::SubmitDebugOutput output;
+ output.resource_id = {pass.draw_commands_buf_.resource_id_buf_.data(),
+ pass.draw_commands_buf_.resource_id_count_};
+ output.visibility = {(uint *)view.visibility_buf_.data(), divide_ceil_u(resource_len_, 32)};
+ return output;
+}
+
+Manager::DataDebugOutput Manager::data_debug()
+{
+ matrix_buf.read();
+ bounds_buf.read();
+ infos_buf.read();
+
+ Manager::DataDebugOutput output;
+ output.matrices = {matrix_buf.data(), resource_len_};
+ output.bounds = {bounds_buf.data(), resource_len_};
+ output.infos = {infos_buf.data(), resource_len_};
+ return output;
+}
+
+} // namespace blender::draw
diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h
index 6d384c599d8..4f71e665390 100644
--- a/source/blender/draw/intern/draw_manager.h
+++ b/source/blender/draw/intern/draw_manager.h
@@ -188,6 +188,7 @@ typedef enum {
DRW_CMD_DRAW_INSTANCE = 2,
DRW_CMD_DRAW_INSTANCE_RANGE = 3,
DRW_CMD_DRAW_PROCEDURAL = 4,
+ DRW_CMD_DRAW_INDIRECT = 5,
/* Compute Commands. */
DRW_CMD_COMPUTE = 8,
@@ -203,7 +204,7 @@ typedef enum {
/* Needs to fit in 4bits */
} eDRWCommandType;
-#define DRW_MAX_DRAW_CMD_TYPE DRW_CMD_DRAW_PROCEDURAL
+#define DRW_MAX_DRAW_CMD_TYPE DRW_CMD_DRAW_INDIRECT
typedef struct DRWCommandDraw {
GPUBatch *batch;
@@ -232,6 +233,12 @@ typedef struct DRWCommandDrawInstanceRange {
uint inst_count;
} DRWCommandDrawInstanceRange;
+typedef struct DRWCommandDrawIndirect {
+ GPUBatch *batch;
+ DRWResourceHandle handle;
+ GPUStorageBuf *indirect_buf;
+} DRWCommandDrawIndirect;
+
typedef struct DRWCommandCompute {
int groups_x_len;
int groups_y_len;
@@ -286,6 +293,7 @@ typedef union DRWCommand {
DRWCommandDrawInstance instance;
DRWCommandDrawInstanceRange instance_range;
DRWCommandDrawProcedural procedural;
+ DRWCommandDrawIndirect draw_indirect;
DRWCommandCompute compute;
DRWCommandComputeRef compute_ref;
DRWCommandComputeIndirect compute_indirect;
@@ -369,7 +377,7 @@ struct DRWUniform {
/* DRW_UNIFORM_INT_COPY */
int ivalue[4];
/* DRW_UNIFORM_BLOCK_OBATTRS */
- struct GPUUniformAttrList *uniform_attrs;
+ const struct GPUUniformAttrList *uniform_attrs;
};
int location; /* Uniform location or binding point for textures and UBO's. */
uint8_t type; /* #DRWUniformType */
@@ -395,7 +403,7 @@ struct DRWShadingGroup {
DRWResourceHandle pass_handle; /* Memblock key to parent pass. */
/* Set of uniform attributes used by this shader. */
- struct GPUUniformAttrList *uniform_attrs;
+ const struct GPUUniformAttrList *uniform_attrs;
};
/* This struct is used after cache populate if using the Z sorting.
* It will not conflict with the above struct. */
@@ -493,20 +501,6 @@ typedef struct DRWCommandSmallChunk {
BLI_STATIC_ASSERT_ALIGN(DRWCommandChunk, 16);
#endif
-/* ------------- DRAW DEBUG ------------ */
-
-typedef struct DRWDebugLine {
- struct DRWDebugLine *next; /* linked list */
- float pos[2][3];
- float color[4];
-} DRWDebugLine;
-
-typedef struct DRWDebugSphere {
- struct DRWDebugSphere *next; /* linked list */
- float mat[4][4];
- float color[4];
-} DRWDebugSphere;
-
/* ------------- Memory Pools ------------ */
/* Contains memory pools information */
@@ -533,10 +527,12 @@ typedef struct DRWData {
void *volume_grids_ubos; /* VolumeUniformBufPool */
/** List of smoke textures to free after drawing. */
ListBase smoke_textures;
- /** Texture pool to reuse temp texture across engines. */
- /* TODO(@fclem): The pool could be shared even between view-ports. */
+ /**
+ * Texture pool to reuse temp texture across engines.
+ * TODO(@fclem): The pool could be shared even between view-ports.
+ */
struct DRWTexturePool *texture_pool;
- /** Per stereo view data. Contains engine data and default framebuffers. */
+ /** Per stereo view data. Contains engine data and default frame-buffers. */
struct DRWViewData *view_data[2];
/** Per draw-call curves object data. */
struct CurvesUniformBufPool *curves_ubos;
@@ -646,11 +642,7 @@ typedef struct DRWManager {
GPUDrawList *draw_list;
- struct {
- /* TODO(@fclem): optimize: use chunks. */
- DRWDebugLine *lines;
- DRWDebugSphere *spheres;
- } debug;
+ DRWDebugModule *debug;
} DRWManager;
extern DRWManager DST; /* TODO: get rid of this and allow multi-threaded rendering. */
@@ -665,6 +657,9 @@ void drw_state_set(DRWState state);
void drw_debug_draw(void);
void drw_debug_init(void);
+void drw_debug_module_free(DRWDebugModule *module);
+GPUStorageBuf *drw_debug_gpu_draw_buf_get(void);
+GPUStorageBuf *drw_debug_gpu_print_buf_get(void);
eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index);
@@ -683,9 +678,10 @@ void drw_resource_buffer_finish(DRWData *vmempool);
GPUBatch *drw_cache_procedural_points_get(void);
GPUBatch *drw_cache_procedural_lines_get(void);
GPUBatch *drw_cache_procedural_triangles_get(void);
+GPUBatch *drw_cache_procedural_triangle_strips_get(void);
void drw_uniform_attrs_pool_update(struct GHash *table,
- struct GPUUniformAttrList *key,
+ const struct GPUUniformAttrList *key,
DRWResourceHandle *handle,
struct Object *ob,
struct Object *dupli_parent,
@@ -698,6 +694,9 @@ bool drw_engine_data_engines_data_validate(GPUViewport *viewport, void **engine_
void drw_engine_data_cache_release(GPUViewport *viewport);
void drw_engine_data_free(GPUViewport *viewport);
+void DRW_manager_begin_sync(void);
+void DRW_manager_end_sync(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/draw/intern/draw_manager.hh b/source/blender/draw/intern/draw_manager.hh
new file mode 100644
index 00000000000..fbd3d28d3f4
--- /dev/null
+++ b/source/blender/draw/intern/draw_manager.hh
@@ -0,0 +1,237 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+#pragma once
+
+/** \file
+ * \ingroup draw
+ *
+ * `draw::Manager` is the interface between scene data and viewport engines.
+ *
+ * It holds per component data (`ObjectInfo`, `ObjectMatrices`, ...) indexed per `ResourceHandle`.
+ *
+ * \note It is currently work in progress and should replace the old global draw manager.
+ */
+
+#include "BLI_listbase_wrapper.hh"
+#include "BLI_sys_types.h"
+#include "GPU_material.h"
+
+#include "draw_resource.hh"
+#include "draw_view.hh"
+
+#include <string>
+
+namespace blender::draw {
+
+/* Forward declarations. */
+
+namespace detail {
+template<typename T> class Pass;
+} // namespace detail
+
+namespace command {
+class DrawCommandBuf;
+class DrawMultiBuf;
+} // namespace command
+
+using PassSimple = detail::Pass<command::DrawCommandBuf>;
+using PassMain = detail::Pass<command::DrawMultiBuf>;
+class PassSortable;
+
+class Manager {
+ using ObjectMatricesBuf = StorageArrayBuffer<ObjectMatrices, 128>;
+ using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>;
+ using ObjectInfosBuf = StorageArrayBuffer<ObjectInfos, 128>;
+ using ObjectAttributeBuf = StorageArrayBuffer<ObjectAttribute, 128>;
+ /**
+ * TODO(@fclem): Remove once we get rid of old EEVEE code-base.
+ * `DRW_RESOURCE_CHUNK_LEN = 512`.
+ */
+ using ObjectAttributeLegacyBuf = UniformArrayBuffer<float4, 8 * 512>;
+
+ public:
+ struct SubmitDebugOutput {
+ /** Indexed by resource id. */
+ Span<uint32_t> visibility;
+ /** Indexed by drawn instance. */
+ Span<uint32_t> resource_id;
+ };
+
+ struct DataDebugOutput {
+ /** Indexed by resource id. */
+ Span<ObjectMatrices> matrices;
+ /** Indexed by resource id. */
+ Span<ObjectBounds> bounds;
+ /** Indexed by resource id. */
+ Span<ObjectInfos> infos;
+ };
+
+ /**
+ * Buffers containing all object data. Referenced by resource index.
+ * Exposed as public members for shader access after sync.
+ */
+ ObjectMatricesBuf matrix_buf;
+ ObjectBoundsBuf bounds_buf;
+ ObjectInfosBuf infos_buf;
+
+ /**
+ * Object Attributes are reference by indirection data inside ObjectInfos.
+ * This is because attribute list is arbitrary.
+ */
+ ObjectAttributeBuf attributes_buf;
+ /**
+ * TODO(@fclem): Remove once we get rid of old EEVEE code-base.
+ * Only here to satisfy bindings.
+ */
+ ObjectAttributeLegacyBuf attributes_buf_legacy;
+
+ /**
+ * List of textures coming from Image data-blocks.
+ * They need to be reference-counted in order to avoid being freed in another thread.
+ */
+ Vector<GPUTexture *> acquired_textures;
+
+ private:
+ /** Number of resource handle recorded. */
+ uint resource_len_ = 0;
+ /** Number of object attribute recorded. */
+ uint attribute_len_ = 0;
+
+ Object *object_active = nullptr;
+
+ public:
+ Manager(){};
+ ~Manager();
+
+ /**
+ * Create a new resource handle for the given object. Can be called multiple time with the
+ * same object **successively** without duplicating the data.
+ */
+ ResourceHandle resource_handle(const ObjectRef ref);
+ /**
+ * Get resource id for a loose matrix. The draw-calls for this resource handle won't be culled
+ * and there won't be any associated object info / bounds. Assumes correct handedness / winding.
+ */
+ ResourceHandle resource_handle(const float4x4 &model_matrix);
+ /**
+ * Get resource id for a loose matrix with bounds. The draw-calls for this resource handle will
+ * be culled bute there won't be any associated object info / bounds. Assumes correct handedness
+ * / winding.
+ */
+ ResourceHandle resource_handle(const float4x4 &model_matrix,
+ const float3 &bounds_center,
+ const float3 &bounds_half_extent);
+
+ /**
+ * Populate additional per resource data on demand.
+ */
+ void extract_object_attributes(ResourceHandle handle,
+ const ObjectRef &ref,
+ Span<GPUMaterial *> materials);
+
+ /**
+ * Submit a pass for drawing. All resource reference will be dereferenced and commands will be
+ * sent to GPU.
+ */
+ void submit(PassSimple &pass, View &view);
+ void submit(PassMain &pass, View &view);
+ void submit(PassSortable &pass, View &view);
+ /**
+ * Variant without any view. Must not contain any shader using `draw_view` create info.
+ */
+ void submit(PassSimple &pass);
+
+ /**
+ * Submit a pass for drawing but read back all data buffers for inspection.
+ */
+ SubmitDebugOutput submit_debug(PassSimple &pass, View &view);
+ SubmitDebugOutput submit_debug(PassMain &pass, View &view);
+
+ /**
+ * Check data buffers of the draw manager. Only to be used after end_sync().
+ */
+ DataDebugOutput data_debug();
+
+ /**
+ * Will acquire the texture using ref counting and release it after drawing. To be used for
+ * texture coming from blender Image.
+ */
+ void acquire_texture(GPUTexture *texture)
+ {
+ GPU_texture_ref(texture);
+ acquired_textures.append(texture);
+ }
+
+ /** TODO(fclem): The following should become private at some point. */
+ void begin_sync();
+ void end_sync();
+
+ void debug_bind();
+ void resource_bind();
+};
+
+inline ResourceHandle Manager::resource_handle(const ObjectRef ref)
+{
+ bool is_active_object = (ref.dupli_object ? ref.dupli_parent : ref.object) == object_active;
+ matrix_buf.get_or_resize(resource_len_).sync(*ref.object);
+ bounds_buf.get_or_resize(resource_len_).sync(*ref.object);
+ infos_buf.get_or_resize(resource_len_).sync(ref, is_active_object);
+ return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
+}
+
+inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix)
+{
+ matrix_buf.get_or_resize(resource_len_).sync(model_matrix);
+ bounds_buf.get_or_resize(resource_len_).sync();
+ infos_buf.get_or_resize(resource_len_).sync();
+ return ResourceHandle(resource_len_++, false);
+}
+
+inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix,
+ const float3 &bounds_center,
+ const float3 &bounds_half_extent)
+{
+ matrix_buf.get_or_resize(resource_len_).sync(model_matrix);
+ bounds_buf.get_or_resize(resource_len_).sync(bounds_center, bounds_half_extent);
+ infos_buf.get_or_resize(resource_len_).sync();
+ return ResourceHandle(resource_len_++, false);
+}
+
+inline void Manager::extract_object_attributes(ResourceHandle handle,
+ const ObjectRef &ref,
+ Span<GPUMaterial *> materials)
+{
+ ObjectInfos &infos = infos_buf.get_or_resize(handle.resource_index());
+ infos.object_attrs_offset = attribute_len_;
+
+ /* Simple cache solution to avoid duplicates. */
+ Vector<uint32_t, 4> hash_cache;
+
+ for (const GPUMaterial *mat : materials) {
+ const GPUUniformAttrList *attr_list = GPU_material_uniform_attributes(mat);
+ if (attr_list == nullptr) {
+ continue;
+ }
+
+ LISTBASE_FOREACH (const GPUUniformAttr *, attr, &attr_list->list) {
+ /** WATCH: Linear Search. Avoid duplicate attributes across materials. */
+ if ((mat != materials.first()) && (hash_cache.first_index_of_try(attr->hash_code) != -1)) {
+ /* Attribute has already been added to the attribute buffer by another material. */
+ continue;
+ }
+ hash_cache.append(attr->hash_code);
+ if (attributes_buf.get_or_resize(attribute_len_).sync(ref, *attr)) {
+ infos.object_attrs_len++;
+ attribute_len_++;
+ }
+ }
+ }
+}
+
+} // namespace blender::draw
+
+/* TODO(@fclem): This is for testing. The manager should be passed to the engine through the
+ * callbacks. */
+blender::draw::Manager *DRW_manager_get();
+blender::draw::ObjectRef DRW_object_ref_get(Object *object);
diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c
index 188d9114cd7..820242720c8 100644
--- a/source/blender/draw/intern/draw_manager_data.c
+++ b/source/blender/draw/intern/draw_manager_data.c
@@ -17,9 +17,14 @@
#include "BKE_pbvh.h"
#include "BKE_volume.h"
+/* For debug cursor position. */
+#include "WM_api.h"
+#include "wm_window.h"
+
#include "DNA_curve_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
+#include "DNA_screen_types.h"
#include "BLI_alloca.h"
#include "BLI_hash.h"
@@ -39,6 +44,16 @@
#include "intern/gpu_codegen.h"
+/**
+ * IMPORTANT:
+ * In order to be able to write to the same print buffer sequentially, we add a barrier to allow
+ * multiple shader calls writing to the same buffer.
+ * However, this adds explicit synchronization events which might change the rest of the
+ * application behavior and hide some bugs. If you know you are using shader debug print in only
+ * one shader pass, you can comment this out to remove the aforementioned barrier.
+ */
+#define DISABLE_DEBUG_SHADER_PRINT_BARRIER
+
/* -------------------------------------------------------------------- */
/** \name Uniform Buffer Object (DRW_uniformbuffer)
* \{ */
@@ -878,6 +893,17 @@ static void drw_command_draw_procedural(DRWShadingGroup *shgroup,
cmd->vert_count = vert_count;
}
+static void drw_command_draw_indirect(DRWShadingGroup *shgroup,
+ GPUBatch *batch,
+ DRWResourceHandle handle,
+ GPUStorageBuf *indirect_buf)
+{
+ DRWCommandDrawIndirect *cmd = drw_command_create(shgroup, DRW_CMD_DRAW_INDIRECT);
+ cmd->batch = batch;
+ cmd->handle = handle;
+ cmd->indirect_buf = indirect_buf;
+}
+
static void drw_command_set_select_id(DRWShadingGroup *shgroup, GPUVertBuf *buf, uint select_id)
{
/* Only one can be valid. */
@@ -1005,6 +1031,7 @@ void DRW_shgroup_call_compute_indirect(DRWShadingGroup *shgroup, GPUStorageBuf *
drw_command_compute_indirect(shgroup, indirect_buf);
}
+
void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type)
{
BLI_assert(GPU_compute_shader_support());
@@ -1044,6 +1071,38 @@ void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *shgroup, Object *ob,
drw_shgroup_call_procedural_add_ex(shgroup, geom, ob, tri_count * 3);
}
+void DRW_shgroup_call_procedural_indirect(DRWShadingGroup *shgroup,
+ GPUPrimType primitive_type,
+ Object *ob,
+ GPUStorageBuf *indirect_buf)
+{
+ struct GPUBatch *geom = NULL;
+ switch (primitive_type) {
+ case GPU_PRIM_POINTS:
+ geom = drw_cache_procedural_points_get();
+ break;
+ case GPU_PRIM_LINES:
+ geom = drw_cache_procedural_lines_get();
+ break;
+ case GPU_PRIM_TRIS:
+ geom = drw_cache_procedural_triangles_get();
+ break;
+ case GPU_PRIM_TRI_STRIP:
+ geom = drw_cache_procedural_triangle_strips_get();
+ break;
+ default:
+ BLI_assert_msg(0,
+ "Unsupported primitive type in DRW_shgroup_call_procedural_indirect. Add new "
+ "one as needed.");
+ break;
+ }
+ if (G.f & G_FLAG_PICKSEL) {
+ drw_command_set_select_id(shgroup, NULL, DST.select_id);
+ }
+ DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob);
+ drw_command_draw_indirect(shgroup, geom, handle, indirect_buf);
+}
+
void DRW_shgroup_call_instances(DRWShadingGroup *shgroup,
Object *ob,
struct GPUBatch *geom,
@@ -1129,16 +1188,15 @@ static void sculpt_draw_cb(DRWSculptCallbackData *scd, GPU_PBVH_Buffers *buffers
DRW_shgroup_uniform_vec3(
shgrp, "materialDiffuseColor", SCULPT_DEBUG_COLOR(scd->debug_node_nr++), 1);
}
+
/* DRW_shgroup_call_no_cull reuses matrices calculations for all the drawcalls of this
* object. */
DRW_shgroup_call_no_cull(shgrp, geom, scd->ob);
}
}
-static void sculpt_debug_cb(void *user_data,
- const float bmin[3],
- const float bmax[3],
- PBVHNodeFlags flag)
+static void sculpt_debug_cb(
+ PBVHNode *node, void *user_data, const float bmin[3], const float bmax[3], PBVHNodeFlags flag)
{
int *debug_node_nr = (int *)user_data;
BoundBox bb;
@@ -1153,7 +1211,10 @@ static void sculpt_debug_cb(void *user_data,
}
#else /* Color coded leaf bounds. */
if (flag & PBVH_Leaf) {
- DRW_debug_bbox(&bb, SCULPT_DEBUG_COLOR((*debug_node_nr)++));
+ int color = (*debug_node_nr)++;
+ color += BKE_pbvh_debug_draw_gen_get(node);
+
+ DRW_debug_bbox(&bb, SCULPT_DEBUG_COLOR(color));
}
#endif
}
@@ -1246,8 +1307,8 @@ static void drw_sculpt_generate_calls(DRWSculptCallbackData *scd)
DRW_debug_modelmat(scd->ob->obmat);
BKE_pbvh_draw_debug_cb(
pbvh,
- (void (*)(
- void *d, const float min[3], const float max[3], PBVHNodeFlags f))sculpt_debug_cb,
+ (void (*)(PBVHNode * n, void *d, const float min[3], const float max[3], PBVHNodeFlags f))
+ sculpt_debug_cb,
&debug_node_nr);
}
}
@@ -1466,6 +1527,27 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader)
shgroup, view_ubo_location, DRW_UNIFORM_BLOCK, G_draw.view_ubo, 0, 0, 1);
}
+#ifdef DEBUG
+ int debug_print_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT);
+ if (debug_print_location != -1) {
+ GPUStorageBuf *buf = drw_debug_gpu_print_buf_get();
+ drw_shgroup_uniform_create_ex(
+ shgroup, debug_print_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1);
+# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER
+ /* Add a barrier to allow multiple shader writing to the same buffer. */
+ DRW_shgroup_barrier(shgroup, GPU_BARRIER_SHADER_STORAGE);
+# endif
+ }
+
+ int debug_draw_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS);
+ if (debug_draw_location != -1) {
+ GPUStorageBuf *buf = drw_debug_gpu_draw_buf_get();
+ drw_shgroup_uniform_create_ex(
+ shgroup, debug_draw_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1);
+ /* NOTE(fclem): No barrier as ordering is not important. */
+ }
+#endif
+
/* Not supported. */
BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV) == -1);
BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW) == -1);
@@ -1549,6 +1631,10 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial
/* Color Ramp */
DRW_shgroup_uniform_texture(grp, tex->sampler_name, *tex->colorband);
}
+ else if (tex->sky) {
+ /* Sky */
+ DRW_shgroup_uniform_texture_ex(grp, tex->sampler_name, *tex->sky, tex->sampler_state);
+ }
}
GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material);
@@ -1556,7 +1642,7 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial
DRW_shgroup_uniform_block(grp, GPU_UBO_BLOCK_NAME, ubo);
}
- GPUUniformAttrList *uattrs = GPU_material_uniform_attributes(material);
+ const GPUUniformAttrList *uattrs = GPU_material_uniform_attributes(material);
if (uattrs != NULL) {
int loc = GPU_shader_get_uniform_block_binding(grp->shader, GPU_ATTRIBUTE_UBO_BLOCK_NAME);
drw_shgroup_uniform_create_ex(grp, loc, DRW_UNIFORM_BLOCK_OBATTRS, uattrs, 0, 0, 1);
@@ -1942,6 +2028,13 @@ DRWView *DRW_view_create(const float viewmat[4][4],
copy_v4_fl4(view->storage.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f);
+ if (DST.draw_ctx.evil_C && DST.draw_ctx.region) {
+ int region_origin[2] = {DST.draw_ctx.region->winrct.xmin, DST.draw_ctx.region->winrct.ymin};
+ struct wmWindow *win = CTX_wm_window(DST.draw_ctx.evil_C);
+ wm_cursor_position_get(win, &view->storage.mouse_pixel[0], &view->storage.mouse_pixel[1]);
+ sub_v2_v2v2_int(view->storage.mouse_pixel, view->storage.mouse_pixel, region_origin);
+ }
+
DRW_view_update(view, viewmat, winmat, culling_viewmat, culling_winmat);
return view;
@@ -2041,6 +2134,14 @@ void DRW_view_update(DRWView *view,
draw_frustum_bound_sphere_calc(
&view->frustum_corners, viewinv, winmat, wininv, &view->frustum_bsphere);
+ /* TODO(fclem): Deduplicate. */
+ for (int i = 0; i < 8; i++) {
+ copy_v3_v3(view->storage.frustum_corners[i], view->frustum_corners.vec[i]);
+ }
+ for (int i = 0; i < 6; i++) {
+ copy_v4_v4(view->storage.frustum_planes[i], view->frustum_planes[i]);
+ }
+
#ifdef DRW_DEBUG_CULLING
if (G.debug_value != 0) {
DRW_debug_sphere(
diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c
index e7e0e0ce41f..0e39cc1d3b9 100644
--- a/source/blender/draw/intern/draw_manager_exec.c
+++ b/source/blender/draw/intern/draw_manager_exec.c
@@ -318,6 +318,7 @@ void DRW_state_reset(void)
DRW_state_reset_ex(DRW_STATE_DEFAULT);
GPU_texture_unbind_all();
+ GPU_texture_image_unbind_all();
GPU_uniformbuf_unbind_all();
GPU_storagebuf_unbind_all();
@@ -874,6 +875,25 @@ static void draw_call_single_do(DRWShadingGroup *shgroup,
state->baseinst_loc);
}
+/* Not to be mistaken with draw_indirect_call which does batch many drawcalls together. This one
+ * only execute an indirect drawcall with user indirect buffer. */
+static void draw_call_indirect(DRWShadingGroup *shgroup,
+ DRWCommandsState *state,
+ GPUBatch *batch,
+ DRWResourceHandle handle,
+ GPUStorageBuf *indirect_buf)
+{
+ draw_call_batching_flush(shgroup, state);
+ draw_call_resource_bind(state, &handle);
+
+ if (G.f & G_FLAG_PICKSEL) {
+ GPU_select_load_id(state->select_id);
+ }
+
+ GPU_batch_set_shader(batch, shgroup->shader);
+ GPU_batch_draw_indirect(batch, indirect_buf, 0);
+}
+
static void draw_call_batching_start(DRWCommandsState *state)
{
state->neg_scale = false;
@@ -970,6 +990,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
/* Unbinding can be costly. Skip in normal condition. */
if (G.debug & G_DEBUG_GPU) {
GPU_texture_unbind_all();
+ GPU_texture_image_unbind_all();
GPU_uniformbuf_unbind_all();
GPU_storagebuf_unbind_all();
}
@@ -996,12 +1017,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
while ((cmd = draw_command_iter_step(&iter, &cmd_type))) {
switch (cmd_type) {
+ case DRW_CMD_DRAW_PROCEDURAL:
case DRW_CMD_DRWSTATE:
case DRW_CMD_STENCIL:
draw_call_batching_flush(shgroup, &state);
break;
case DRW_CMD_DRAW:
- case DRW_CMD_DRAW_PROCEDURAL:
+ case DRW_CMD_DRAW_INDIRECT:
case DRW_CMD_DRAW_INSTANCE:
if (draw_call_is_culled(&cmd->instance.handle, DST.view_active)) {
continue;
@@ -1055,6 +1077,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state)
1,
true);
break;
+ case DRW_CMD_DRAW_INDIRECT:
+ draw_call_indirect(shgroup,
+ &state,
+ cmd->draw_indirect.batch,
+ cmd->draw_indirect.handle,
+ cmd->draw_indirect.indirect_buf);
+ break;
case DRW_CMD_DRAW_INSTANCE:
draw_call_single_do(shgroup,
&state,
diff --git a/source/blender/draw/intern/draw_pass.hh b/source/blender/draw/intern/draw_pass.hh
new file mode 100644
index 00000000000..e1a0a6652ac
--- /dev/null
+++ b/source/blender/draw/intern/draw_pass.hh
@@ -0,0 +1,1005 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+#pragma once
+
+/** \file
+ * \ingroup draw
+ *
+ * Passes record draw commands. Commands are executed only when a pass is submitted for execution.
+ *
+ * `PassMain`:
+ * Should be used on heavy load passes such as ones that may contain scene objects. Draw call
+ * submission is optimized for large number of draw calls. But has a significant overhead per
+ * #Pass. Use many #PassSub along with a main #Pass to reduce the overhead and allow groupings of
+ * commands. \note The draw call order inside a batch of multiple draw with the exact same state is
+ * not guaranteed and is not even deterministic. Use a #PassSimple or #PassSortable if ordering is
+ * needed. \note As of now, it is also quite limited in the type of draw command it can record
+ * (no custom vertex count, no custom first vertex).
+ *
+ * `PassSimple`:
+ * Does not have the overhead of #PassMain but does not have the culling and batching optimization.
+ * It should be used for passes that needs a few commands or that needs guaranteed draw call order.
+ *
+ * `Pass<T>::Sub`:
+ * A lightweight #Pass that lives inside a main #Pass. It can only be created from #Pass.sub()
+ * and is auto managed. This mean it can be created, filled and thrown away. A #PassSub reference
+ * is valid until the next #Pass.init() of the parent pass. Commands recorded inside a #PassSub are
+ * inserted inside the parent #Pass where the sub have been created during submission.
+ *
+ * `PassSortable`:
+ * This is a sort of `PassMain` augmented with a per sub-pass sorting value. They can't directly
+ * contain draw command, everything needs to be inside sub-passes. Sub-passes are automatically
+ * sorted before submission.
+ *
+ * \note A pass can be recorded once and resubmitted any number of time. This can be a good
+ * optimization for passes that are always the same for each frame. The only thing to be aware of
+ * is the life time of external resources. If a pass contains draw-calls with non default
+ * #ResourceHandle (not 0) or a reference to any non static resources
+ * (#GPUBatch, #PushConstant ref, #ResourceBind ref) it will have to be re-recorded
+ * if any of these reference becomes invalid.
+ */
+
+#include "BKE_image.h"
+#include "BLI_vector.hh"
+#include "DRW_gpu_wrapper.hh"
+#include "GPU_debug.h"
+#include "GPU_material.h"
+
+#include "draw_command.hh"
+#include "draw_handle.hh"
+#include "draw_manager.hh"
+#include "draw_pass.hh"
+#include "draw_shader_shared.h"
+#include "draw_state.h"
+
+#include "intern/gpu_codegen.h"
+
+namespace blender::draw {
+
+using namespace blender::draw;
+using namespace blender::draw::command;
+
+class Manager;
+
+/* -------------------------------------------------------------------- */
+/** \name Pass API
+ * \{ */
+
+namespace detail {
+
+/**
+ * Special container that never moves allocated items and has fast indexing.
+ */
+template<typename T,
+ /** Numbers of element of type T to allocate together. */
+ int64_t block_size = 16>
+class SubPassVector {
+ private:
+ Vector<std::unique_ptr<Vector<T, block_size>>, 0> blocks_;
+
+ public:
+ void clear()
+ {
+ blocks_.clear();
+ }
+
+ int64_t append_and_get_index(T &&elem)
+ {
+ /* Do not go over the inline size so that existing members never move. */
+ if (blocks_.is_empty() || blocks_.last()->size() == block_size) {
+ blocks_.append(std::make_unique<Vector<T, block_size>>());
+ }
+ return blocks_.last()->append_and_get_index(std::move(elem)) +
+ (blocks_.size() - 1) * block_size;
+ }
+
+ T &operator[](int64_t index)
+ {
+ return (*blocks_[index / block_size])[index % block_size];
+ }
+
+ const T &operator[](int64_t index) const
+ {
+ return (*blocks_[index / block_size])[index % block_size];
+ }
+};
+
+/**
+ * Public API of a draw pass.
+ */
+template<
+ /** Type of command buffer used to create the draw calls. */
+ typename DrawCommandBufType>
+class PassBase {
+ friend Manager;
+
+ /** Will use texture own sampler state. */
+ static constexpr eGPUSamplerState sampler_auto = GPU_SAMPLER_MAX;
+
+ protected:
+ /** Highest level of the command stream. Split command stream in different command types. */
+ Vector<command::Header, 0> headers_;
+ /** Commands referenced by headers (which contains their types). */
+ Vector<command::Undetermined, 0> commands_;
+ /* Reference to draw commands buffer. Either own or from parent pass. */
+ DrawCommandBufType &draw_commands_buf_;
+ /* Reference to sub-pass commands buffer. Either own or from parent pass. */
+ SubPassVector<PassBase<DrawCommandBufType>> &sub_passes_;
+ /** Currently bound shader. Used for interface queries. */
+ GPUShader *shader_;
+
+ public:
+ const char *debug_name;
+
+ PassBase(const char *name,
+ DrawCommandBufType &draw_command_buf,
+ SubPassVector<PassBase<DrawCommandBufType>> &sub_passes,
+ GPUShader *shader = nullptr)
+ : draw_commands_buf_(draw_command_buf),
+ sub_passes_(sub_passes),
+ shader_(shader),
+ debug_name(name){};
+
+ /**
+ * Reset the pass command pool.
+ * \note Implemented in derived class. Not a virtual function to avoid indirection. Here only for
+ * API readability listing.
+ */
+ void init();
+
+ /**
+ * Create a sub-pass inside this pass.
+ */
+ PassBase<DrawCommandBufType> &sub(const char *name);
+
+ /**
+ * Changes the fixed function pipeline state.
+ * Starts as DRW_STATE_NO_DRAW at the start of a Pass submission.
+ * SubPass inherit previous pass state.
+ *
+ * IMPORTANT: This does not set the stencil mask/reference values. Add a call to state_stencil()
+ * to ensure correct behavior of stencil aware draws.
+ */
+ void state_set(DRWState state);
+
+ /**
+ * Clear the current frame-buffer.
+ */
+ void clear_color(float4 color);
+ void clear_depth(float depth);
+ void clear_stencil(uint8_t stencil);
+ void clear_depth_stencil(float depth, uint8_t stencil);
+ void clear_color_depth_stencil(float4 color, float depth, uint8_t stencil);
+
+ /**
+ * Reminders:
+ * - (compare_mask & reference) is what is tested against (compare_mask & stencil_value)
+ * stencil_value being the value stored in the stencil buffer.
+ * - (write-mask & reference) is what gets written if the test condition is fulfilled.
+ */
+ void state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask);
+
+ /**
+ * Bind a shader. Any following bind() or push_constant() call will use its interface.
+ */
+ void shader_set(GPUShader *shader);
+
+ /**
+ * Bind a material shader along with its associated resources. Any following bind() or
+ * push_constant() call will use its interface.
+ * IMPORTANT: Assumes material is compiled and can be used (no compilation error).
+ */
+ void material_set(Manager &manager, GPUMaterial *material);
+
+ /**
+ * Record a draw call.
+ * \note Setting the count or first to -1 will use the values from the batch.
+ * \note An instance or vertex count of 0 will discard the draw call. It will not be recorded.
+ */
+ void draw(GPUBatch *batch,
+ uint instance_len = -1,
+ uint vertex_len = -1,
+ uint vertex_first = -1,
+ ResourceHandle handle = {0});
+
+ /**
+ * Shorter version for the common case.
+ * \note Implemented in derived class. Not a virtual function to avoid indirection.
+ */
+ void draw(GPUBatch *batch, ResourceHandle handle);
+
+ /**
+ * Record a procedural draw call. Geometry is **NOT** source from a GPUBatch.
+ * \note An instance or vertex count of 0 will discard the draw call. It will not be recorded.
+ */
+ void draw_procedural(GPUPrimType primitive,
+ uint instance_len,
+ uint vertex_len,
+ uint vertex_first = -1,
+ ResourceHandle handle = {0});
+
+ /**
+ * Indirect variants.
+ * \note If needed, the resource id need to also be set accordingly in the DrawCommand.
+ */
+ void draw_indirect(GPUBatch *batch,
+ StorageBuffer<DrawCommand, true> &indirect_buffer,
+ ResourceHandle handle = {0});
+ void draw_procedural_indirect(GPUPrimType primitive,
+ StorageBuffer<DrawCommand, true> &indirect_buffer,
+ ResourceHandle handle = {0});
+
+ /**
+ * Record a compute dispatch call.
+ */
+ void dispatch(int3 group_len);
+ void dispatch(int3 *group_len);
+ void dispatch(StorageBuffer<DispatchCommand> &indirect_buffer);
+
+ /**
+ * Record a barrier call to synchronize arbitrary load/store operation between draw calls.
+ */
+ void barrier(eGPUBarrier type);
+
+ /**
+ * Bind a shader resource.
+ *
+ * Reference versions are to be used when the resource might be resize / realloc or even change
+ * between the time it is referenced and the time it is dereferenced for drawing.
+ *
+ * IMPORTANT: Will keep a reference to the data and dereference it upon drawing. Make sure data
+ * still alive until pass submission.
+ *
+ * \note Variations using slot will not query a shader interface and can be used before
+ * binding a shader.
+ */
+ void bind_image(const char *name, GPUTexture *image);
+ void bind_image(const char *name, GPUTexture **image);
+ void bind_image(int slot, GPUTexture *image);
+ void bind_image(int slot, GPUTexture **image);
+ void bind_texture(const char *name, GPUTexture *texture, eGPUSamplerState state = sampler_auto);
+ void bind_texture(const char *name, GPUTexture **texture, eGPUSamplerState state = sampler_auto);
+ void bind_texture(int slot, GPUTexture *texture, eGPUSamplerState state = sampler_auto);
+ void bind_texture(int slot, GPUTexture **texture, eGPUSamplerState state = sampler_auto);
+ void bind_ssbo(const char *name, GPUStorageBuf *buffer);
+ void bind_ssbo(const char *name, GPUStorageBuf **buffer);
+ void bind_ssbo(int slot, GPUStorageBuf *buffer);
+ void bind_ssbo(int slot, GPUStorageBuf **buffer);
+ void bind_ubo(const char *name, GPUUniformBuf *buffer);
+ void bind_ubo(const char *name, GPUUniformBuf **buffer);
+ void bind_ubo(int slot, GPUUniformBuf *buffer);
+ void bind_ubo(int slot, GPUUniformBuf **buffer);
+
+ /**
+ * Update a shader constant.
+ *
+ * Reference versions are to be used when the resource might change between the time it is
+ * referenced and the time it is dereferenced for drawing.
+ *
+ * IMPORTANT: Will keep a reference to the data and dereference it upon drawing. Make sure data
+ * still alive until pass submission.
+ *
+ * \note bool reference version is expected to take bool1 reference which is aliased to int.
+ */
+ void push_constant(const char *name, const float &data);
+ void push_constant(const char *name, const float2 &data);
+ void push_constant(const char *name, const float3 &data);
+ void push_constant(const char *name, const float4 &data);
+ void push_constant(const char *name, const int &data);
+ void push_constant(const char *name, const int2 &data);
+ void push_constant(const char *name, const int3 &data);
+ void push_constant(const char *name, const int4 &data);
+ void push_constant(const char *name, const bool &data);
+ void push_constant(const char *name, const float4x4 &data);
+ void push_constant(const char *name, const float *data, int array_len = 1);
+ void push_constant(const char *name, const float2 *data, int array_len = 1);
+ void push_constant(const char *name, const float3 *data, int array_len = 1);
+ void push_constant(const char *name, const float4 *data, int array_len = 1);
+ void push_constant(const char *name, const int *data, int array_len = 1);
+ void push_constant(const char *name, const int2 *data, int array_len = 1);
+ void push_constant(const char *name, const int3 *data, int array_len = 1);
+ void push_constant(const char *name, const int4 *data, int array_len = 1);
+ void push_constant(const char *name, const float4x4 *data);
+
+ /**
+ * Turn the pass into a string for inspection.
+ */
+ std::string serialize(std::string line_prefix = "") const;
+
+ friend std::ostream &operator<<(std::ostream &stream, const PassBase &pass)
+ {
+ return stream << pass.serialize();
+ }
+
+ protected:
+ /**
+ * Internal Helpers
+ */
+
+ int push_constant_offset(const char *name);
+
+ void clear(eGPUFrameBufferBits planes, float4 color, float depth, uint8_t stencil);
+
+ GPUBatch *procedural_batch_get(GPUPrimType primitive);
+
+ /**
+ * Return a new command recorded with the given type.
+ */
+ command::Undetermined &create_command(command::Type type);
+
+ void submit(command::RecordingState &state) const;
+};
+
+template<typename DrawCommandBufType> class Pass : public detail::PassBase<DrawCommandBufType> {
+ public:
+ using Sub = detail::PassBase<DrawCommandBufType>;
+
+ private:
+ /** Sub-passes referenced by headers. */
+ SubPassVector<detail::PassBase<DrawCommandBufType>> sub_passes_main_;
+ /** Draws are recorded as indirect draws for compatibility with the multi-draw pipeline. */
+ DrawCommandBufType draw_commands_buf_main_;
+
+ public:
+ Pass(const char *name)
+ : detail::PassBase<DrawCommandBufType>(name, draw_commands_buf_main_, sub_passes_main_){};
+
+ void init()
+ {
+ this->headers_.clear();
+ this->commands_.clear();
+ this->sub_passes_.clear();
+ this->draw_commands_buf_.clear();
+ }
+}; // namespace blender::draw
+
+} // namespace detail
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Pass types
+ * \{ */
+
+/**
+ * Normal pass type. No visibility or draw-call optimization.
+ */
+// using PassSimple = detail::Pass<DrawCommandBuf>;
+
+/**
+ * Main pass type.
+ * Optimized for many draw calls and sub-pass.
+ *
+ * IMPORTANT: To be used only for passes containing lots of draw calls since it has a potentially
+ * high overhead due to batching and culling optimizations.
+ */
+// using PassMain = detail::Pass<DrawMultiBuf>;
+
+/**
+ * Special pass type for rendering transparent objects.
+ * The base level can only be composed of sub passes that will be ordered by a sorting value.
+ */
+class PassSortable : public PassMain {
+ friend Manager;
+
+ private:
+ /** Sorting value associated with each sub pass. */
+ Vector<float> sorting_values_;
+
+ bool sorted_ = false;
+
+ public:
+ PassSortable(const char *name_) : PassMain(name_){};
+
+ void init()
+ {
+ sorting_values_.clear();
+ sorted_ = false;
+ PassMain::init();
+ }
+
+ PassMain::Sub &sub(const char *name, float sorting_value)
+ {
+ int64_t index = sub_passes_.append_and_get_index(
+ PassBase(name, draw_commands_buf_, sub_passes_, shader_));
+ headers_.append({Type::SubPass, static_cast<uint>(index)});
+ sorting_values_.append(sorting_value);
+ return sub_passes_[index];
+ }
+
+ std::string serialize(std::string line_prefix = "") const
+ {
+ if (sorted_ == false) {
+ const_cast<PassSortable *>(this)->sort();
+ }
+ return PassMain::serialize(line_prefix);
+ }
+
+ protected:
+ void sort()
+ {
+ if (sorted_ == false) {
+ std::sort(headers_.begin(), headers_.end(), [&](Header &a, Header &b) {
+ BLI_assert(a.type == Type::SubPass && b.type == Type::SubPass);
+ float a_val = sorting_values_[a.index];
+ float b_val = sorting_values_[b.index];
+ return a_val < b_val || (a_val == b_val && a.index < b.index);
+ });
+ sorted_ = true;
+ }
+ }
+};
+
+/** \} */
+
+namespace detail {
+
+/* -------------------------------------------------------------------- */
+/** \name PassBase Implementation
+ * \{ */
+
+template<class T> inline command::Undetermined &PassBase<T>::create_command(command::Type type)
+{
+ int64_t index = commands_.append_and_get_index({});
+ headers_.append({type, static_cast<uint>(index)});
+ return commands_[index];
+}
+
+template<class T>
+inline void PassBase<T>::clear(eGPUFrameBufferBits planes,
+ float4 color,
+ float depth,
+ uint8_t stencil)
+{
+ create_command(command::Type::Clear).clear = {(uint8_t)planes, stencil, depth, color};
+}
+
+template<class T> inline GPUBatch *PassBase<T>::procedural_batch_get(GPUPrimType primitive)
+{
+ switch (primitive) {
+ case GPU_PRIM_POINTS:
+ return drw_cache_procedural_points_get();
+ case GPU_PRIM_LINES:
+ return drw_cache_procedural_lines_get();
+ case GPU_PRIM_TRIS:
+ return drw_cache_procedural_triangles_get();
+ case GPU_PRIM_TRI_STRIP:
+ return drw_cache_procedural_triangle_strips_get();
+ default:
+ /* Add new one as needed. */
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+}
+
+template<class T> inline PassBase<T> &PassBase<T>::sub(const char *name)
+{
+ int64_t index = sub_passes_.append_and_get_index(
+ PassBase(name, draw_commands_buf_, sub_passes_, shader_));
+ headers_.append({command::Type::SubPass, static_cast<uint>(index)});
+ return sub_passes_[index];
+}
+
+template<class T> void PassBase<T>::submit(command::RecordingState &state) const
+{
+ GPU_debug_group_begin(debug_name);
+
+ for (const command::Header &header : headers_) {
+ switch (header.type) {
+ default:
+ case Type::None:
+ break;
+ case Type::SubPass:
+ sub_passes_[header.index].submit(state);
+ break;
+ case command::Type::ShaderBind:
+ commands_[header.index].shader_bind.execute(state);
+ break;
+ case command::Type::ResourceBind:
+ commands_[header.index].resource_bind.execute();
+ break;
+ case command::Type::PushConstant:
+ commands_[header.index].push_constant.execute(state);
+ break;
+ case command::Type::Draw:
+ commands_[header.index].draw.execute(state);
+ break;
+ case command::Type::DrawMulti:
+ commands_[header.index].draw_multi.execute(state);
+ break;
+ case command::Type::DrawIndirect:
+ commands_[header.index].draw_indirect.execute(state);
+ break;
+ case command::Type::Dispatch:
+ commands_[header.index].dispatch.execute(state);
+ break;
+ case command::Type::DispatchIndirect:
+ commands_[header.index].dispatch_indirect.execute(state);
+ break;
+ case command::Type::Barrier:
+ commands_[header.index].barrier.execute();
+ break;
+ case command::Type::Clear:
+ commands_[header.index].clear.execute();
+ break;
+ case command::Type::StateSet:
+ commands_[header.index].state_set.execute(state);
+ break;
+ case command::Type::StencilSet:
+ commands_[header.index].stencil_set.execute();
+ break;
+ }
+ }
+
+ GPU_debug_group_end();
+}
+
+template<class T> std::string PassBase<T>::serialize(std::string line_prefix) const
+{
+ std::stringstream ss;
+ ss << line_prefix << "." << debug_name << std::endl;
+ line_prefix += " ";
+ for (const command::Header &header : headers_) {
+ switch (header.type) {
+ default:
+ case Type::None:
+ break;
+ case Type::SubPass:
+ ss << sub_passes_[header.index].serialize(line_prefix);
+ break;
+ case Type::ShaderBind:
+ ss << line_prefix << commands_[header.index].shader_bind.serialize() << std::endl;
+ break;
+ case Type::ResourceBind:
+ ss << line_prefix << commands_[header.index].resource_bind.serialize() << std::endl;
+ break;
+ case Type::PushConstant:
+ ss << line_prefix << commands_[header.index].push_constant.serialize() << std::endl;
+ break;
+ case Type::Draw:
+ ss << line_prefix << commands_[header.index].draw.serialize() << std::endl;
+ break;
+ case Type::DrawMulti:
+ ss << commands_[header.index].draw_multi.serialize(line_prefix);
+ break;
+ case Type::DrawIndirect:
+ ss << line_prefix << commands_[header.index].draw_indirect.serialize() << std::endl;
+ break;
+ case Type::Dispatch:
+ ss << line_prefix << commands_[header.index].dispatch.serialize() << std::endl;
+ break;
+ case Type::DispatchIndirect:
+ ss << line_prefix << commands_[header.index].dispatch_indirect.serialize() << std::endl;
+ break;
+ case Type::Barrier:
+ ss << line_prefix << commands_[header.index].barrier.serialize() << std::endl;
+ break;
+ case Type::Clear:
+ ss << line_prefix << commands_[header.index].clear.serialize() << std::endl;
+ break;
+ case Type::StateSet:
+ ss << line_prefix << commands_[header.index].state_set.serialize() << std::endl;
+ break;
+ case Type::StencilSet:
+ ss << line_prefix << commands_[header.index].stencil_set.serialize() << std::endl;
+ break;
+ }
+ }
+ return ss.str();
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw calls
+ * \{ */
+
+template<class T>
+inline void PassBase<T>::draw(
+ GPUBatch *batch, uint instance_len, uint vertex_len, uint vertex_first, ResourceHandle handle)
+{
+ if (instance_len == 0 || vertex_len == 0) {
+ return;
+ }
+ BLI_assert(shader_);
+ draw_commands_buf_.append_draw(
+ headers_, commands_, batch, instance_len, vertex_len, vertex_first, handle);
+}
+
+template<class T> inline void PassBase<T>::draw(GPUBatch *batch, ResourceHandle handle)
+{
+ this->draw(batch, -1, -1, -1, handle);
+}
+
+template<class T>
+inline void PassBase<T>::draw_procedural(GPUPrimType primitive,
+ uint instance_len,
+ uint vertex_len,
+ uint vertex_first,
+ ResourceHandle handle)
+{
+ this->draw(procedural_batch_get(primitive), instance_len, vertex_len, vertex_first, handle);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Indirect draw calls
+ * \{ */
+
+template<class T>
+inline void PassBase<T>::draw_indirect(GPUBatch *batch,
+ StorageBuffer<DrawCommand, true> &indirect_buffer,
+ ResourceHandle handle)
+{
+ BLI_assert(shader_);
+ create_command(Type::DrawIndirect).draw_indirect = {batch, &indirect_buffer, handle};
+}
+
+template<class T>
+inline void PassBase<T>::draw_procedural_indirect(
+ GPUPrimType primitive,
+ StorageBuffer<DrawCommand, true> &indirect_buffer,
+ ResourceHandle handle)
+{
+ this->draw_indirect(procedural_batch_get(primitive), indirect_buffer, handle);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Compute Dispatch Implementation
+ * \{ */
+
+template<class T> inline void PassBase<T>::dispatch(int3 group_len)
+{
+ BLI_assert(shader_);
+ create_command(Type::Dispatch).dispatch = {group_len};
+}
+
+template<class T> inline void PassBase<T>::dispatch(int3 *group_len)
+{
+ BLI_assert(shader_);
+ create_command(Type::Dispatch).dispatch = {group_len};
+}
+
+template<class T>
+inline void PassBase<T>::dispatch(StorageBuffer<DispatchCommand> &indirect_buffer)
+{
+ BLI_assert(shader_);
+ create_command(Type::DispatchIndirect).dispatch_indirect = {&indirect_buffer};
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Clear Implementation
+ * \{ */
+
+template<class T> inline void PassBase<T>::clear_color(float4 color)
+{
+ this->clear(GPU_COLOR_BIT, color, 0.0f, 0);
+}
+
+template<class T> inline void PassBase<T>::clear_depth(float depth)
+{
+ this->clear(GPU_DEPTH_BIT, float4(0.0f), depth, 0);
+}
+
+template<class T> inline void PassBase<T>::clear_stencil(uint8_t stencil)
+{
+ this->clear(GPU_STENCIL_BIT, float4(0.0f), 0.0f, stencil);
+}
+
+template<class T> inline void PassBase<T>::clear_depth_stencil(float depth, uint8_t stencil)
+{
+ this->clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT, float4(0.0f), depth, stencil);
+}
+
+template<class T>
+inline void PassBase<T>::clear_color_depth_stencil(float4 color, float depth, uint8_t stencil)
+{
+ this->clear(GPU_DEPTH_BIT | GPU_STENCIL_BIT | GPU_COLOR_BIT, color, depth, stencil);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Barrier Implementation
+ * \{ */
+
+template<class T> inline void PassBase<T>::barrier(eGPUBarrier type)
+{
+ create_command(Type::Barrier).barrier = {type};
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name State Implementation
+ * \{ */
+
+template<class T> inline void PassBase<T>::state_set(DRWState state)
+{
+ create_command(Type::StateSet).state_set = {state};
+}
+
+template<class T>
+inline void PassBase<T>::state_stencil(uint8_t write_mask, uint8_t reference, uint8_t compare_mask)
+{
+ create_command(Type::StencilSet).stencil_set = {write_mask, reference, compare_mask};
+}
+
+template<class T> inline void PassBase<T>::shader_set(GPUShader *shader)
+{
+ shader_ = shader;
+ create_command(Type::ShaderBind).shader_bind = {shader};
+}
+
+template<class T> inline void PassBase<T>::material_set(Manager &manager, GPUMaterial *material)
+{
+ GPUPass *gpupass = GPU_material_get_pass(material);
+ shader_set(GPU_pass_shader_get(gpupass));
+
+ /* Bind all textures needed by the material. */
+ ListBase textures = GPU_material_textures(material);
+ for (GPUMaterialTexture *tex : ListBaseWrapper<GPUMaterialTexture>(textures)) {
+ if (tex->ima) {
+ /* Image */
+ ImageUser *iuser = tex->iuser_available ? &tex->iuser : nullptr;
+ if (tex->tiled_mapping_name[0]) {
+ GPUTexture *tiles = BKE_image_get_gpu_tiles(tex->ima, iuser, nullptr);
+ manager.acquire_texture(tiles);
+ bind_texture(tex->sampler_name, tiles, (eGPUSamplerState)tex->sampler_state);
+
+ GPUTexture *tile_map = BKE_image_get_gpu_tilemap(tex->ima, iuser, nullptr);
+ manager.acquire_texture(tile_map);
+ bind_texture(tex->tiled_mapping_name, tile_map, (eGPUSamplerState)tex->sampler_state);
+ }
+ else {
+ GPUTexture *texture = BKE_image_get_gpu_texture(tex->ima, iuser, nullptr);
+ manager.acquire_texture(texture);
+ bind_texture(tex->sampler_name, texture, (eGPUSamplerState)tex->sampler_state);
+ }
+ }
+ else if (tex->colorband) {
+ /* Color Ramp */
+ bind_texture(tex->sampler_name, *tex->colorband);
+ }
+ }
+
+ GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material);
+ if (ubo != nullptr) {
+ bind_ubo(GPU_UBO_BLOCK_NAME, ubo);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Resource bind Implementation
+ * \{ */
+
+template<class T> inline int PassBase<T>::push_constant_offset(const char *name)
+{
+ return GPU_shader_get_uniform(shader_, name);
+}
+
+template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUStorageBuf *buffer)
+{
+ this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer);
+}
+
+template<class T> inline void PassBase<T>::bind_ubo(const char *name, GPUUniformBuf *buffer)
+{
+ this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer);
+}
+
+template<class T>
+inline void PassBase<T>::bind_texture(const char *name,
+ GPUTexture *texture,
+ eGPUSamplerState state)
+{
+ this->bind_texture(GPU_shader_get_texture_binding(shader_, name), texture, state);
+}
+
+template<class T> inline void PassBase<T>::bind_image(const char *name, GPUTexture *image)
+{
+ this->bind_image(GPU_shader_get_texture_binding(shader_, name), image);
+}
+
+template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUStorageBuf *buffer)
+{
+ create_command(Type::ResourceBind).resource_bind = {slot, buffer};
+}
+
+template<class T> inline void PassBase<T>::bind_ubo(int slot, GPUUniformBuf *buffer)
+{
+ create_command(Type::ResourceBind).resource_bind = {slot, buffer};
+}
+
+template<class T>
+inline void PassBase<T>::bind_texture(int slot, GPUTexture *texture, eGPUSamplerState state)
+{
+ create_command(Type::ResourceBind).resource_bind = {slot, texture, state};
+}
+
+template<class T> inline void PassBase<T>::bind_image(int slot, GPUTexture *image)
+{
+ create_command(Type::ResourceBind).resource_bind = {slot, as_image(image)};
+}
+
+template<class T> inline void PassBase<T>::bind_ssbo(const char *name, GPUStorageBuf **buffer)
+{
+ this->bind_ssbo(GPU_shader_get_ssbo(shader_, name), buffer);
+}
+
+template<class T> inline void PassBase<T>::bind_ubo(const char *name, GPUUniformBuf **buffer)
+{
+ this->bind_ubo(GPU_shader_get_uniform_block_binding(shader_, name), buffer);
+}
+
+template<class T>
+inline void PassBase<T>::bind_texture(const char *name,
+ GPUTexture **texture,
+ eGPUSamplerState state)
+{
+ this->bind_texture(GPU_shader_get_texture_binding(shader_, name), texture, state);
+}
+
+template<class T> inline void PassBase<T>::bind_image(const char *name, GPUTexture **image)
+{
+ this->bind_image(GPU_shader_get_texture_binding(shader_, name), image);
+}
+
+template<class T> inline void PassBase<T>::bind_ssbo(int slot, GPUStorageBuf **buffer)
+{
+
+ create_command(Type::ResourceBind).resource_bind = {slot, buffer};
+}
+
+template<class T> inline void PassBase<T>::bind_ubo(int slot, GPUUniformBuf **buffer)
+{
+ create_command(Type::ResourceBind).resource_bind = {slot, buffer};
+}
+
+template<class T>
+inline void PassBase<T>::bind_texture(int slot, GPUTexture **texture, eGPUSamplerState state)
+{
+ create_command(Type::ResourceBind).resource_bind = {slot, texture, state};
+}
+
+template<class T> inline void PassBase<T>::bind_image(int slot, GPUTexture **image)
+{
+ create_command(Type::ResourceBind).resource_bind = {slot, as_image(image)};
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Push Constant Implementation
+ * \{ */
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const float &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const float2 &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const float3 &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const float4 &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const int &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const int2 &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const int3 &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const int4 &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const bool &data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T>
+inline void PassBase<T>::push_constant(const char *name, const float *data, int array_len)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len};
+}
+
+template<class T>
+inline void PassBase<T>::push_constant(const char *name, const float2 *data, int array_len)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len};
+}
+
+template<class T>
+inline void PassBase<T>::push_constant(const char *name, const float3 *data, int array_len)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len};
+}
+
+template<class T>
+inline void PassBase<T>::push_constant(const char *name, const float4 *data, int array_len)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len};
+}
+
+template<class T>
+inline void PassBase<T>::push_constant(const char *name, const int *data, int array_len)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len};
+}
+
+template<class T>
+inline void PassBase<T>::push_constant(const char *name, const int2 *data, int array_len)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len};
+}
+
+template<class T>
+inline void PassBase<T>::push_constant(const char *name, const int3 *data, int array_len)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len};
+}
+
+template<class T>
+inline void PassBase<T>::push_constant(const char *name, const int4 *data, int array_len)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data, array_len};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const float4x4 *data)
+{
+ create_command(Type::PushConstant).push_constant = {push_constant_offset(name), data};
+}
+
+template<class T> inline void PassBase<T>::push_constant(const char *name, const float4x4 &data)
+{
+ /* WORKAROUND: Push 3 consecutive commands to hold the 64 bytes of the float4x4.
+ * This assumes that all commands are always stored in flat array of memory. */
+ Undetermined commands[3];
+
+ PushConstant &cmd = commands[0].push_constant;
+ cmd.location = push_constant_offset(name);
+ cmd.array_len = 1;
+ cmd.comp_len = 16;
+ cmd.type = PushConstant::Type::FloatValue;
+ /* Copy overrides the next 2 commands. We append them as Type::None to not evaluate them. */
+ *reinterpret_cast<float4x4 *>(&cmd.float4_value) = data;
+
+ create_command(Type::PushConstant) = commands[0];
+ create_command(Type::None) = commands[1];
+ create_command(Type::None) = commands[2];
+}
+
+/** \} */
+
+} // namespace detail
+
+} // namespace blender::draw
diff --git a/source/blender/draw/intern/draw_resource.cc b/source/blender/draw/intern/draw_resource.cc
new file mode 100644
index 00000000000..689df4edb31
--- /dev/null
+++ b/source/blender/draw/intern/draw_resource.cc
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "DNA_particle_types.h"
+#include "RNA_access.h"
+#include "RNA_path.h"
+#include "RNA_types.h"
+
+#include "draw_handle.hh"
+#include "draw_manager.hh"
+#include "draw_shader_shared.h"
+
+/* -------------------------------------------------------------------- */
+/** \name ObjectAttributes
+ * \{ */
+
+/**
+ * Extract object attribute from RNA property.
+ * Returns true if the attribute was correctly extracted.
+ * This function mirrors lookup_property in cycles/blender/blender_object.cpp
+ */
+bool ObjectAttribute::id_property_lookup(ID *id, const char *name)
+{
+ PointerRNA ptr, id_ptr;
+ PropertyRNA *prop;
+
+ if (id == nullptr) {
+ return false;
+ }
+
+ RNA_id_pointer_create(id, &id_ptr);
+
+ if (!RNA_path_resolve(&id_ptr, name, &ptr, &prop)) {
+ return false;
+ }
+
+ if (prop == nullptr) {
+ return false;
+ }
+
+ PropertyType type = RNA_property_type(prop);
+ int array_len = RNA_property_array_length(&ptr, prop);
+
+ if (array_len == 0) {
+ float value;
+
+ if (type == PROP_FLOAT) {
+ value = RNA_property_float_get(&ptr, prop);
+ }
+ else if (type == PROP_INT) {
+ value = RNA_property_int_get(&ptr, prop);
+ }
+ else {
+ return false;
+ }
+
+ *reinterpret_cast<float4 *>(&data_x) = float4(value, value, value, 1.0f);
+ return true;
+ }
+
+ if (type == PROP_FLOAT && array_len <= 4) {
+ *reinterpret_cast<float4 *>(&data_x) = float4(0.0f, 0.0f, 0.0f, 1.0f);
+ RNA_property_float_get_array(&ptr, prop, &data_x);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Go through all possible source of the given object uniform attribute.
+ * Returns true if the attribute was correctly filled.
+ * This function mirrors lookup_instance_property in cycles/blender/blender_object.cpp
+ */
+bool ObjectAttribute::sync(const blender::draw::ObjectRef &ref, const GPUUniformAttr &attr)
+{
+ hash_code = attr.hash_code;
+
+ /* If requesting instance data, check the parent particle system and object. */
+ if (attr.use_dupli) {
+ if ((ref.dupli_object != nullptr) && (ref.dupli_object->particle_system != nullptr)) {
+ ParticleSettings *settings = ref.dupli_object->particle_system->part;
+ if (this->id_property_lookup((ID *)settings, attr.name_id_prop) ||
+ this->id_property_lookup((ID *)settings, attr.name)) {
+ return true;
+ }
+ }
+ if (this->id_property_lookup((ID *)ref.dupli_parent, attr.name_id_prop) ||
+ this->id_property_lookup((ID *)ref.dupli_parent, attr.name)) {
+ return true;
+ }
+ }
+
+ /* Check the object and mesh. */
+ if (ref.object != nullptr) {
+ if (this->id_property_lookup((ID *)ref.object, attr.name_id_prop) ||
+ this->id_property_lookup((ID *)ref.object, attr.name) ||
+ this->id_property_lookup((ID *)ref.object->data, attr.name_id_prop) ||
+ this->id_property_lookup((ID *)ref.object->data, attr.name)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_resource.hh b/source/blender/draw/intern/draw_resource.hh
new file mode 100644
index 00000000000..2df38e32ed2
--- /dev/null
+++ b/source/blender/draw/intern/draw_resource.hh
@@ -0,0 +1,206 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+#pragma once
+
+/** \file
+ * \ingroup draw
+ *
+ * Component / Object level resources like object attributes, matrices, visibility etc...
+ * Each of them are reference by resource index (#ResourceHandle).
+ */
+
+#include "BKE_curve.h"
+#include "BKE_duplilist.h"
+#include "BKE_mesh.h"
+#include "BKE_object.h"
+#include "BKE_volume.h"
+#include "BLI_hash.h"
+#include "DNA_curve_types.h"
+#include "DNA_layer_types.h"
+#include "DNA_meta_types.h"
+#include "DNA_object_types.h"
+
+#include "draw_handle.hh"
+#include "draw_manager.hh"
+#include "draw_shader_shared.h"
+
+/* -------------------------------------------------------------------- */
+/** \name ObjectMatrices
+ * \{ */
+
+inline void ObjectMatrices::sync(const Object &object)
+{
+ model = object.obmat;
+ model_inverse = object.imat;
+}
+
+inline void ObjectMatrices::sync(const float4x4 &model_matrix)
+{
+ model = model_matrix;
+ model_inverse = model_matrix.inverted();
+}
+
+inline std::ostream &operator<<(std::ostream &stream, const ObjectMatrices &matrices)
+{
+ stream << "ObjectMatrices(" << std::endl;
+ stream << "model=" << matrices.model << ", " << std::endl;
+ stream << "model_inverse=" << matrices.model_inverse << ")" << std::endl;
+ return stream;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ObjectInfos
+ * \{ */
+
+ENUM_OPERATORS(eObjectInfoFlag, OBJECT_NEGATIVE_SCALE)
+
+inline void ObjectInfos::sync()
+{
+ object_attrs_len = 0;
+ object_attrs_offset = 0;
+
+ flag = eObjectInfoFlag::OBJECT_NO_INFO;
+}
+
+inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active_object)
+{
+ object_attrs_len = 0;
+ object_attrs_offset = 0;
+
+ color = ref.object->color;
+ index = ref.object->index;
+ SET_FLAG_FROM_TEST(flag, is_active_object, eObjectInfoFlag::OBJECT_ACTIVE);
+ SET_FLAG_FROM_TEST(
+ flag, ref.object->base_flag & BASE_SELECTED, eObjectInfoFlag::OBJECT_SELECTED);
+ SET_FLAG_FROM_TEST(
+ flag, ref.object->base_flag & BASE_FROM_DUPLI, eObjectInfoFlag::OBJECT_FROM_DUPLI);
+ SET_FLAG_FROM_TEST(
+ flag, ref.object->base_flag & BASE_FROM_SET, eObjectInfoFlag::OBJECT_FROM_SET);
+ SET_FLAG_FROM_TEST(
+ flag, ref.object->transflag & OB_NEG_SCALE, eObjectInfoFlag::OBJECT_NEGATIVE_SCALE);
+
+ if (ref.dupli_object == nullptr) {
+ /* TODO(fclem): this is rather costly to do at draw time. Maybe we can
+ * put it in ob->runtime and make depsgraph ensure it is up to date. */
+ random = BLI_hash_int_2d(BLI_hash_string(ref.object->id.name + 2), 0) *
+ (1.0f / (float)0xFFFFFFFF);
+ }
+ else {
+ random = ref.dupli_object->random_id * (1.0f / (float)0xFFFFFFFF);
+ }
+ /* Default values. Set if needed. */
+ random = 0.0f;
+
+ if (ref.object->data == nullptr) {
+ orco_add = float3(0.0f);
+ orco_mul = float3(1.0f);
+ return;
+ }
+
+ switch (GS(reinterpret_cast<ID *>(ref.object->data)->name)) {
+ case ID_VO: {
+ BoundBox &bbox = *BKE_volume_boundbox_get(ref.object);
+ orco_add = (float3(bbox.vec[6]) + float3(bbox.vec[0])) * 0.5f; /* Center. */
+ orco_mul = float3(bbox.vec[6]) - float3(bbox.vec[0]); /* Size. */
+ break;
+ }
+ case ID_ME: {
+ BKE_mesh_texspace_get(static_cast<Mesh *>(ref.object->data), orco_add, orco_mul);
+ break;
+ }
+ case ID_CU_LEGACY: {
+ Curve &cu = *static_cast<Curve *>(ref.object->data);
+ BKE_curve_texspace_ensure(&cu);
+ orco_add = cu.loc;
+ orco_mul = cu.size;
+ break;
+ }
+ case ID_MB: {
+ MetaBall &mb = *static_cast<MetaBall *>(ref.object->data);
+ orco_add = mb.loc;
+ orco_mul = mb.size;
+ break;
+ }
+ default:
+ orco_add = float3(0.0f);
+ orco_mul = float3(1.0f);
+ break;
+ }
+}
+
+inline std::ostream &operator<<(std::ostream &stream, const ObjectInfos &infos)
+{
+ stream << "ObjectInfos(";
+ if (infos.flag == eObjectInfoFlag::OBJECT_NO_INFO) {
+ stream << "skipped)" << std::endl;
+ return stream;
+ }
+ stream << "orco_add=" << infos.orco_add << ", ";
+ stream << "orco_mul=" << infos.orco_mul << ", ";
+ stream << "color=" << infos.color << ", ";
+ stream << "index=" << infos.index << ", ";
+ stream << "random=" << infos.random << ", ";
+ stream << "flag=" << infos.flag << ")" << std::endl;
+ return stream;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ObjectBounds
+ * \{ */
+
+inline void ObjectBounds::sync()
+{
+ bounding_sphere.w = -1.0f; /* Disable test. */
+}
+
+inline void ObjectBounds::sync(Object &ob)
+{
+ const BoundBox *bbox = BKE_object_boundbox_get(&ob);
+ if (bbox == nullptr) {
+ bounding_sphere.w = -1.0f; /* Disable test. */
+ return;
+ }
+ *reinterpret_cast<float3 *>(&bounding_corners[0]) = bbox->vec[0];
+ *reinterpret_cast<float3 *>(&bounding_corners[1]) = bbox->vec[4];
+ *reinterpret_cast<float3 *>(&bounding_corners[2]) = bbox->vec[3];
+ *reinterpret_cast<float3 *>(&bounding_corners[3]) = bbox->vec[1];
+ bounding_sphere.w = 0.0f; /* Enable test. */
+}
+
+inline void ObjectBounds::sync(const float3 &center, const float3 &size)
+{
+ *reinterpret_cast<float3 *>(&bounding_corners[0]) = center - size;
+ *reinterpret_cast<float3 *>(&bounding_corners[1]) = center + float3(+size.x, -size.y, -size.z);
+ *reinterpret_cast<float3 *>(&bounding_corners[2]) = center + float3(-size.x, +size.y, -size.z);
+ *reinterpret_cast<float3 *>(&bounding_corners[3]) = center + float3(-size.x, -size.y, +size.z);
+ bounding_sphere.w = 0.0; /* Enable test. */
+}
+
+inline std::ostream &operator<<(std::ostream &stream, const ObjectBounds &bounds)
+{
+ stream << "ObjectBounds(";
+ if (bounds.bounding_sphere.w == -1.0f) {
+ stream << "skipped)" << std::endl;
+ return stream;
+ }
+ stream << std::endl;
+ stream << ".bounding_corners[0]"
+ << *reinterpret_cast<const float3 *>(&bounds.bounding_corners[0]) << std::endl;
+ stream << ".bounding_corners[1]"
+ << *reinterpret_cast<const float3 *>(&bounds.bounding_corners[1]) << std::endl;
+ stream << ".bounding_corners[2]"
+ << *reinterpret_cast<const float3 *>(&bounds.bounding_corners[2]) << std::endl;
+ stream << ".bounding_corners[3]"
+ << *reinterpret_cast<const float3 *>(&bounds.bounding_corners[3]) << std::endl;
+ stream << ".sphere=(pos=" << float3(bounds.bounding_sphere)
+ << ", rad=" << bounds.bounding_sphere.w << std::endl;
+ stream << ")" << std::endl;
+ return stream;
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_shader.cc b/source/blender/draw/intern/draw_shader.cc
index 001ceb0ae8d..960348b4a94 100644
--- a/source/blender/draw/intern/draw_shader.cc
+++ b/source/blender/draw/intern/draw_shader.cc
@@ -17,13 +17,15 @@
#include "draw_shader.h"
extern "C" char datatoc_common_hair_lib_glsl[];
-
extern "C" char datatoc_common_hair_refine_vert_glsl[];
-extern "C" char datatoc_common_hair_refine_comp_glsl[];
-extern "C" char datatoc_gpu_shader_3D_smooth_color_frag_glsl[];
static struct {
struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER];
+ struct GPUShader *debug_print_display_sh;
+ struct GPUShader *debug_draw_display_sh;
+ struct GPUShader *draw_visibility_compute_sh;
+ struct GPUShader *draw_resource_finalize_sh;
+ struct GPUShader *draw_command_generate_sh;
} e_data = {{nullptr}};
/* -------------------------------------------------------------------- */
@@ -109,6 +111,47 @@ GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineSh
return e_data.hair_refine_sh[type];
}
+GPUShader *DRW_shader_debug_print_display_get()
+{
+ if (e_data.debug_print_display_sh == nullptr) {
+ e_data.debug_print_display_sh = GPU_shader_create_from_info_name("draw_debug_print_display");
+ }
+ return e_data.debug_print_display_sh;
+}
+
+GPUShader *DRW_shader_debug_draw_display_get()
+{
+ if (e_data.debug_draw_display_sh == nullptr) {
+ e_data.debug_draw_display_sh = GPU_shader_create_from_info_name("draw_debug_draw_display");
+ }
+ return e_data.debug_draw_display_sh;
+}
+
+GPUShader *DRW_shader_draw_visibility_compute_get()
+{
+ if (e_data.draw_visibility_compute_sh == nullptr) {
+ e_data.draw_visibility_compute_sh = GPU_shader_create_from_info_name(
+ "draw_visibility_compute");
+ }
+ return e_data.draw_visibility_compute_sh;
+}
+
+GPUShader *DRW_shader_draw_resource_finalize_get()
+{
+ if (e_data.draw_resource_finalize_sh == nullptr) {
+ e_data.draw_resource_finalize_sh = GPU_shader_create_from_info_name("draw_resource_finalize");
+ }
+ return e_data.draw_resource_finalize_sh;
+}
+
+GPUShader *DRW_shader_draw_command_generate_get()
+{
+ if (e_data.draw_command_generate_sh == nullptr) {
+ e_data.draw_command_generate_sh = GPU_shader_create_from_info_name("draw_command_generate");
+ }
+ return e_data.draw_command_generate_sh;
+}
+
/** \} */
void DRW_shaders_free()
@@ -116,4 +159,9 @@ void DRW_shaders_free()
for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) {
DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]);
}
+ DRW_SHADER_FREE_SAFE(e_data.debug_print_display_sh);
+ DRW_SHADER_FREE_SAFE(e_data.debug_draw_display_sh);
+ DRW_SHADER_FREE_SAFE(e_data.draw_visibility_compute_sh);
+ DRW_SHADER_FREE_SAFE(e_data.draw_resource_finalize_sh);
+ DRW_SHADER_FREE_SAFE(e_data.draw_command_generate_sh);
}
diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h
index 63d755cc334..3b8c0425fa9 100644
--- a/source/blender/draw/intern/draw_shader.h
+++ b/source/blender/draw/intern/draw_shader.h
@@ -30,6 +30,12 @@ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement,
struct GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type,
eParticleRefineShaderType sh_type);
+struct GPUShader *DRW_shader_debug_print_display_get(void);
+struct GPUShader *DRW_shader_debug_draw_display_get(void);
+struct GPUShader *DRW_shader_draw_visibility_compute_get(void);
+struct GPUShader *DRW_shader_draw_resource_finalize_get(void);
+struct GPUShader *DRW_shader_draw_command_generate_get(void);
+
void DRW_shaders_free(void);
#ifdef __cplusplus
diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h
index e8944442607..bedbedcf438 100644
--- a/source/blender/draw/intern/draw_shader_shared.h
+++ b/source/blender/draw/intern/draw_shader_shared.h
@@ -1,14 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef GPU_SHADER
+# pragma once
+
# include "GPU_shader.h"
# include "GPU_shader_shared_utils.h"
+# include "draw_defines.h"
typedef struct ViewInfos ViewInfos;
typedef struct ObjectMatrices ObjectMatrices;
typedef struct ObjectInfos ObjectInfos;
+typedef struct ObjectBounds ObjectBounds;
typedef struct VolumeInfos VolumeInfos;
typedef struct CurvesInfos CurvesInfos;
+typedef struct ObjectAttribute ObjectAttribute;
+typedef struct DrawCommand DrawCommand;
+typedef struct DispatchCommand DispatchCommand;
+typedef struct DRWDebugPrintBuffer DRWDebugPrintBuffer;
+typedef struct DRWDebugVert DRWDebugVert;
+typedef struct DRWDebugDrawBuffer DRWDebugDrawBuffer;
+
+# ifdef __cplusplus
+/* C++ only forward declarations. */
+struct Object;
+struct ID;
+struct GPUUniformAttr;
+
+namespace blender::draw {
+
+struct ObjectRef;
+
+} // namespace blender::draw
+
+# else /* __cplusplus */
+/* C only forward declarations. */
+typedef enum eObjectInfoFlag eObjectInfoFlag;
+
+# endif
#endif
#define DRW_SHADER_SHARED_H
@@ -40,9 +68,18 @@ struct ViewInfos {
float2 viewport_size_inverse;
/** Frustum culling data. */
- /** NOTE: vec3 arrays are padded to vec4. */
+ /** \note vec3 array padded to vec4. */
float4 frustum_corners[8];
float4 frustum_planes[6];
+ float4 frustum_bound_sphere;
+
+ /** For debugging purpose */
+ /* Mouse pixel. */
+ int2 mouse_pixel;
+
+ /** True if facing needs to be inverted. */
+ bool1 is_inverted;
+ int _pad0;
};
BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
@@ -60,23 +97,89 @@ BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
# define CameraTexCoFactors drw_view.viewcamtexcofac
#endif
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Debug draw shapes
+ * \{ */
+
struct ObjectMatrices {
- float4x4 drw_modelMatrix;
- float4x4 drw_modelMatrixInverse;
+ float4x4 model;
+ float4x4 model_inverse;
+
+#if !defined(GPU_SHADER) && defined(__cplusplus)
+ void sync(const Object &object);
+ void sync(const float4x4 &model_matrix);
+#endif
+};
+BLI_STATIC_ASSERT_ALIGN(ObjectMatrices, 16)
+
+enum eObjectInfoFlag {
+ OBJECT_SELECTED = (1u << 0u),
+ OBJECT_FROM_DUPLI = (1u << 1u),
+ OBJECT_FROM_SET = (1u << 2u),
+ OBJECT_ACTIVE = (1u << 3u),
+ OBJECT_NEGATIVE_SCALE = (1u << 4u),
+ /* Avoid skipped info to change culling. */
+ OBJECT_NO_INFO = ~OBJECT_NEGATIVE_SCALE
};
-BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
struct ObjectInfos {
- float4 drw_OrcoTexCoFactors[2];
- float4 drw_ObjectColor;
- float4 drw_Infos;
+#if defined(GPU_SHADER) && !defined(DRAW_FINALIZE_SHADER)
+ /* TODO Rename to struct member for glsl too. */
+ float4 orco_mul_bias[2];
+ float4 color;
+ float4 infos;
+#else
+ /** Uploaded as center + size. Converted to mul+bias to local coord. */
+ float3 orco_add;
+ uint object_attrs_offset;
+ float3 orco_mul;
+ uint object_attrs_len;
+
+ float4 color;
+ uint index;
+ uint _pad2;
+ float random;
+ eObjectInfoFlag flag;
+#endif
+
+#if !defined(GPU_SHADER) && defined(__cplusplus)
+ void sync();
+ void sync(const blender::draw::ObjectRef ref, bool is_active_object);
+#endif
};
-BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16)
+BLI_STATIC_ASSERT_ALIGN(ObjectInfos, 16)
+
+struct ObjectBounds {
+ /**
+ * Uploaded as vertex (0, 4, 3, 1) of the bbox in local space, matching XYZ axis order.
+ * Then processed by GPU and stored as (0, 4-0, 3-0, 1-0) in world space for faster culling.
+ */
+ float4 bounding_corners[4];
+ /** Bounding sphere derived from the bounding corner. Computed on GPU. */
+ float4 bounding_sphere;
+ /** Radius of the inscribed sphere derived from the bounding corner. Computed on GPU. */
+#define _inner_sphere_radius bounding_corners[3].w
+
+#if !defined(GPU_SHADER) && defined(__cplusplus)
+ void sync();
+ void sync(Object &ob);
+ void sync(const float3 &center, const float3 &size);
+#endif
+};
+BLI_STATIC_ASSERT_ALIGN(ObjectBounds, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Object attributes
+ * \{ */
struct VolumeInfos {
- /* Object to grid-space. */
+ /** Object to grid-space. */
float4x4 grids_xform[DRW_GRID_PER_VOLUME_MAX];
- /* NOTE: vec4 for alignment. Only float3 needed. */
+ /** \note vec4 for alignment. Only float3 needed. */
float4 color_mul;
float density_scale;
float temperature_mul;
@@ -86,13 +189,127 @@ struct VolumeInfos {
BLI_STATIC_ASSERT_ALIGN(VolumeInfos, 16)
struct CurvesInfos {
- /* Per attribute scope, follows loading order.
- * NOTE: uint as bool in GLSL is 4 bytes.
- * NOTE: GLSL pad arrays of scalar to 16 bytes (std140). */
+ /** Per attribute scope, follows loading order.
+ * \note uint as bool in GLSL is 4 bytes.
+ * \note GLSL pad arrays of scalar to 16 bytes (std140). */
uint4 is_point_attribute[DRW_ATTRIBUTE_PER_CURVES_MAX];
};
BLI_STATIC_ASSERT_ALIGN(CurvesInfos, 16)
-#define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors)
-#define ObjectInfo (drw_infos[resource_id].drw_Infos)
-#define ObjectColor (drw_infos[resource_id].drw_ObjectColor)
+#pragma pack(push, 4)
+struct ObjectAttribute {
+ /* Workaround the padding cost from alignment requirements.
+ * (see GL spec : 7.6.2.2 Standard Uniform Block Layout) */
+ float data_x, data_y, data_z, data_w;
+ uint hash_code;
+
+#if !defined(GPU_SHADER) && defined(__cplusplus)
+ bool sync(const blender::draw::ObjectRef &ref, const GPUUniformAttr &attr);
+ bool id_property_lookup(ID *id, const char *name);
+#endif
+};
+#pragma pack(pop)
+/** \note we only align to 4 bytes and fetch data manually so make sure
+ * C++ compiler gives us the same size. */
+BLI_STATIC_ASSERT_ALIGN(ObjectAttribute, 20)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Indirect commands structures.
+ * \{ */
+
+struct DrawCommand {
+ /* TODO(fclem): Rename */
+ uint vertex_len;
+ uint instance_len;
+ uint vertex_first;
+#if defined(GPU_SHADER)
+ uint base_index;
+ /** \note base_index is i_first for non-indexed draw-calls. */
+# define _instance_first_array base_index
+#else
+ union {
+ uint base_index;
+ /* Use this instead of instance_first_indexed for non indexed draw calls. */
+ uint instance_first_array;
+ };
+#endif
+
+ uint instance_first_indexed;
+
+ uint _pad0, _pad1, _pad2;
+};
+BLI_STATIC_ASSERT_ALIGN(DrawCommand, 16)
+
+struct DispatchCommand {
+ uint num_groups_x;
+ uint num_groups_y;
+ uint num_groups_z;
+ uint _pad0;
+};
+BLI_STATIC_ASSERT_ALIGN(DispatchCommand, 16)
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Debug print
+ * \{ */
+
+/* Take the header (DrawCommand) into account. */
+#define DRW_DEBUG_PRINT_MAX (8 * 1024) - 4
+/** \note Cannot be more than 255 (because of column encoding). */
+#define DRW_DEBUG_PRINT_WORD_WRAP_COLUMN 120u
+
+/* The debug print buffer is laid-out as the following struct.
+ * But we use plain array in shader code instead because of driver issues. */
+struct DRWDebugPrintBuffer {
+ DrawCommand command;
+ /** Each character is encoded as 3 `uchar` with char_index, row and column position. */
+ uint char_array[DRW_DEBUG_PRINT_MAX];
+};
+BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16)
+
+/* Use number of char as vertex count. Equivalent to `DRWDebugPrintBuffer.command.v_count`. */
+#define drw_debug_print_cursor drw_debug_print_buf[0]
+/* Reuse first instance as row index as we don't use instancing. Equivalent to
+ * `DRWDebugPrintBuffer.command.i_first`. */
+#define drw_debug_print_row_shared drw_debug_print_buf[3]
+/** Offset to the first data. Equal to: `sizeof(DrawCommand) / sizeof(uint)`.
+ * This is needed because we bind the whole buffer as a `uint` array. */
+#define drw_debug_print_offset 8
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Debug draw shapes
+ * \{ */
+
+struct DRWDebugVert {
+ /* This is a weird layout, but needed to be able to use DRWDebugVert as
+ * a DrawCommand and avoid alignment issues. See drw_debug_verts_buf[] definition. */
+ uint pos0;
+ uint pos1;
+ uint pos2;
+ uint color;
+};
+BLI_STATIC_ASSERT_ALIGN(DRWDebugVert, 16)
+
+/* Take the header (DrawCommand) into account. */
+#define DRW_DEBUG_DRAW_VERT_MAX (64 * 1024) - 1
+
+/* The debug draw buffer is laid-out as the following struct.
+ * But we use plain array in shader code instead because of driver issues. */
+struct DRWDebugDrawBuffer {
+ DrawCommand command;
+ DRWDebugVert verts[DRW_DEBUG_DRAW_VERT_MAX];
+};
+BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16)
+
+/* Equivalent to `DRWDebugDrawBuffer.command.v_count`. */
+#define drw_debug_draw_v_count drw_debug_verts_buf[0].pos0
+/** Offset to the first data. Equal to: `sizeof(DrawCommand) / sizeof(DRWDebugVert)`.
+ * This is needed because we bind the whole buffer as a `DRWDebugVert` array. */
+#define drw_debug_draw_offset 2
+
+/** \} */
diff --git a/source/blender/draw/intern/draw_state.h b/source/blender/draw/intern/draw_state.h
new file mode 100644
index 00000000000..bf1e63e0852
--- /dev/null
+++ b/source/blender/draw/intern/draw_state.h
@@ -0,0 +1,225 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file
+ * \ingroup draw
+ *
+ * Internal Pipeline State tracking. It is higher level than GPU state as everything fits a single
+ * enum.
+ */
+
+/**
+ * DRWState is a bit-mask that stores the current render state and the desired render state. Based
+ * on the differences the minimum state changes can be invoked to setup the desired render state.
+ *
+ * The Write Stencil, Stencil test, Depth test and Blend state options are mutual exclusive
+ * therefore they aren't ordered as a bit mask.
+ */
+typedef enum {
+ /** To be used for compute passes. */
+ DRW_STATE_NO_DRAW = 0,
+ /** Write mask */
+ DRW_STATE_WRITE_DEPTH = (1 << 0),
+ DRW_STATE_WRITE_COLOR = (1 << 1),
+ /* Write Stencil. These options are mutual exclusive and packed into 2 bits */
+ DRW_STATE_WRITE_STENCIL = (1 << 2),
+ DRW_STATE_WRITE_STENCIL_SHADOW_PASS = (2 << 2),
+ DRW_STATE_WRITE_STENCIL_SHADOW_FAIL = (3 << 2),
+ /** Depth test. These options are mutual exclusive and packed into 3 bits */
+ DRW_STATE_DEPTH_ALWAYS = (1 << 4),
+ DRW_STATE_DEPTH_LESS = (2 << 4),
+ DRW_STATE_DEPTH_LESS_EQUAL = (3 << 4),
+ DRW_STATE_DEPTH_EQUAL = (4 << 4),
+ DRW_STATE_DEPTH_GREATER = (5 << 4),
+ DRW_STATE_DEPTH_GREATER_EQUAL = (6 << 4),
+ /** Culling test */
+ DRW_STATE_CULL_BACK = (1 << 7),
+ DRW_STATE_CULL_FRONT = (1 << 8),
+ /** Stencil test. These options are mutually exclusive and packed into 2 bits. */
+ DRW_STATE_STENCIL_ALWAYS = (1 << 9),
+ DRW_STATE_STENCIL_EQUAL = (2 << 9),
+ DRW_STATE_STENCIL_NEQUAL = (3 << 9),
+
+ /** Blend state. These options are mutual exclusive and packed into 4 bits */
+ DRW_STATE_BLEND_ADD = (1 << 11),
+ /** Same as additive but let alpha accumulate without pre-multiply. */
+ DRW_STATE_BLEND_ADD_FULL = (2 << 11),
+ /** Standard alpha blending. */
+ DRW_STATE_BLEND_ALPHA = (3 << 11),
+ /** Use that if color is already pre-multiply by alpha. */
+ DRW_STATE_BLEND_ALPHA_PREMUL = (4 << 11),
+ DRW_STATE_BLEND_BACKGROUND = (5 << 11),
+ DRW_STATE_BLEND_OIT = (6 << 11),
+ DRW_STATE_BLEND_MUL = (7 << 11),
+ DRW_STATE_BLEND_SUB = (8 << 11),
+ /** Use dual source blending. WARNING: Only one color buffer allowed. */
+ DRW_STATE_BLEND_CUSTOM = (9 << 11),
+ DRW_STATE_LOGIC_INVERT = (10 << 11),
+ DRW_STATE_BLEND_ALPHA_UNDER_PREMUL = (11 << 11),
+
+ DRW_STATE_IN_FRONT_SELECT = (1 << 27),
+ DRW_STATE_SHADOW_OFFSET = (1 << 28),
+ DRW_STATE_CLIP_PLANES = (1 << 29),
+ DRW_STATE_FIRST_VERTEX_CONVENTION = (1 << 30),
+ /** DO NOT USE. Assumed always enabled. Only used internally. */
+ DRW_STATE_PROGRAM_POINT_SIZE = (1u << 31),
+} DRWState;
+
+ENUM_OPERATORS(DRWState, DRW_STATE_PROGRAM_POINT_SIZE);
+
+#define DRW_STATE_DEFAULT \
+ (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL)
+#define DRW_STATE_BLEND_ENABLED \
+ (DRW_STATE_BLEND_ADD | DRW_STATE_BLEND_ADD_FULL | DRW_STATE_BLEND_ALPHA | \
+ DRW_STATE_BLEND_ALPHA_PREMUL | DRW_STATE_BLEND_BACKGROUND | DRW_STATE_BLEND_OIT | \
+ DRW_STATE_BLEND_MUL | DRW_STATE_BLEND_SUB | DRW_STATE_BLEND_CUSTOM | DRW_STATE_LOGIC_INVERT)
+#define DRW_STATE_RASTERIZER_ENABLED \
+ (DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL | \
+ DRW_STATE_WRITE_STENCIL_SHADOW_PASS | DRW_STATE_WRITE_STENCIL_SHADOW_FAIL)
+#define DRW_STATE_DEPTH_TEST_ENABLED \
+ (DRW_STATE_DEPTH_ALWAYS | DRW_STATE_DEPTH_LESS | DRW_STATE_DEPTH_LESS_EQUAL | \
+ DRW_STATE_DEPTH_EQUAL | DRW_STATE_DEPTH_GREATER | DRW_STATE_DEPTH_GREATER_EQUAL)
+#define DRW_STATE_STENCIL_TEST_ENABLED \
+ (DRW_STATE_STENCIL_ALWAYS | DRW_STATE_STENCIL_EQUAL | DRW_STATE_STENCIL_NEQUAL)
+#define DRW_STATE_WRITE_STENCIL_ENABLED \
+ (DRW_STATE_WRITE_STENCIL | DRW_STATE_WRITE_STENCIL_SHADOW_PASS | \
+ DRW_STATE_WRITE_STENCIL_SHADOW_FAIL)
+
+#ifdef __cplusplus
+}
+#endif
+
+#ifdef __cplusplus
+
+namespace blender::draw {
+
+/* -------------------------------------------------------------------- */
+/** \name DRWState to GPU state conversion
+ * \{ */
+
+static inline eGPUWriteMask to_write_mask(DRWState state)
+{
+ eGPUWriteMask write_mask = GPU_WRITE_NONE;
+ if (state & DRW_STATE_WRITE_DEPTH) {
+ write_mask |= GPU_WRITE_DEPTH;
+ }
+ if (state & DRW_STATE_WRITE_COLOR) {
+ write_mask |= GPU_WRITE_COLOR;
+ }
+ if (state & DRW_STATE_WRITE_STENCIL_ENABLED) {
+ write_mask |= GPU_WRITE_STENCIL;
+ }
+ return write_mask;
+}
+
+static inline eGPUFaceCullTest to_face_cull_test(DRWState state)
+{
+ switch (state & (DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT)) {
+ case DRW_STATE_CULL_BACK:
+ return GPU_CULL_BACK;
+ case DRW_STATE_CULL_FRONT:
+ return GPU_CULL_FRONT;
+ default:
+ return GPU_CULL_NONE;
+ }
+}
+
+static inline eGPUDepthTest to_depth_test(DRWState state)
+{
+ switch (state & DRW_STATE_DEPTH_TEST_ENABLED) {
+ case DRW_STATE_DEPTH_LESS:
+ return GPU_DEPTH_LESS;
+ case DRW_STATE_DEPTH_LESS_EQUAL:
+ return GPU_DEPTH_LESS_EQUAL;
+ case DRW_STATE_DEPTH_EQUAL:
+ return GPU_DEPTH_EQUAL;
+ case DRW_STATE_DEPTH_GREATER:
+ return GPU_DEPTH_GREATER;
+ case DRW_STATE_DEPTH_GREATER_EQUAL:
+ return GPU_DEPTH_GREATER_EQUAL;
+ case DRW_STATE_DEPTH_ALWAYS:
+ return GPU_DEPTH_ALWAYS;
+ default:
+ return GPU_DEPTH_NONE;
+ }
+}
+
+static inline eGPUStencilOp to_stencil_op(DRWState state)
+{
+ switch (state & DRW_STATE_WRITE_STENCIL_ENABLED) {
+ case DRW_STATE_WRITE_STENCIL:
+ return GPU_STENCIL_OP_REPLACE;
+ case DRW_STATE_WRITE_STENCIL_SHADOW_PASS:
+ return GPU_STENCIL_OP_COUNT_DEPTH_PASS;
+ case DRW_STATE_WRITE_STENCIL_SHADOW_FAIL:
+ return GPU_STENCIL_OP_COUNT_DEPTH_FAIL;
+ default:
+ return GPU_STENCIL_OP_NONE;
+ }
+}
+
+static inline eGPUStencilTest to_stencil_test(DRWState state)
+{
+ switch (state & DRW_STATE_STENCIL_TEST_ENABLED) {
+ case DRW_STATE_STENCIL_ALWAYS:
+ return GPU_STENCIL_ALWAYS;
+ case DRW_STATE_STENCIL_EQUAL:
+ return GPU_STENCIL_EQUAL;
+ case DRW_STATE_STENCIL_NEQUAL:
+ return GPU_STENCIL_NEQUAL;
+ default:
+ return GPU_STENCIL_NONE;
+ }
+}
+
+static inline eGPUBlend to_blend(DRWState state)
+{
+ switch (state & DRW_STATE_BLEND_ENABLED) {
+ case DRW_STATE_BLEND_ADD:
+ return GPU_BLEND_ADDITIVE;
+ case DRW_STATE_BLEND_ADD_FULL:
+ return GPU_BLEND_ADDITIVE_PREMULT;
+ case DRW_STATE_BLEND_ALPHA:
+ return GPU_BLEND_ALPHA;
+ case DRW_STATE_BLEND_ALPHA_PREMUL:
+ return GPU_BLEND_ALPHA_PREMULT;
+ case DRW_STATE_BLEND_BACKGROUND:
+ return GPU_BLEND_BACKGROUND;
+ case DRW_STATE_BLEND_OIT:
+ return GPU_BLEND_OIT;
+ case DRW_STATE_BLEND_MUL:
+ return GPU_BLEND_MULTIPLY;
+ case DRW_STATE_BLEND_SUB:
+ return GPU_BLEND_SUBTRACT;
+ case DRW_STATE_BLEND_CUSTOM:
+ return GPU_BLEND_CUSTOM;
+ case DRW_STATE_LOGIC_INVERT:
+ return GPU_BLEND_INVERT;
+ case DRW_STATE_BLEND_ALPHA_UNDER_PREMUL:
+ return GPU_BLEND_ALPHA_UNDER_PREMUL;
+ default:
+ return GPU_BLEND_NONE;
+ }
+}
+
+static inline eGPUProvokingVertex to_provoking_vertex(DRWState state)
+{
+ switch (state & DRW_STATE_FIRST_VERTEX_CONVENTION) {
+ case DRW_STATE_FIRST_VERTEX_CONVENTION:
+ return GPU_VERTEX_FIRST;
+ default:
+ return GPU_VERTEX_LAST;
+ }
+}
+
+/** \} */
+
+}; // namespace blender::draw
+
+#endif
diff --git a/source/blender/draw/intern/draw_subdivision.h b/source/blender/draw/intern/draw_subdivision.h
index ef580fc116a..37b025e761d 100644
--- a/source/blender/draw/intern/draw_subdivision.h
+++ b/source/blender/draw/intern/draw_subdivision.h
@@ -199,6 +199,7 @@ void DRW_create_subdivision(struct Object *ob,
const float obmat[4][4],
const bool do_final,
const bool do_uvedit,
+ const bool do_cage,
const ToolSettings *ts,
const bool use_hide);
diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc
index b36cb5c809e..017ecec7be2 100644
--- a/source/blender/draw/intern/draw_texture_pool.cc
+++ b/source/blender/draw/intern/draw_texture_pool.cc
@@ -160,6 +160,19 @@ void DRW_texture_pool_texture_release(DRWTexturePool *pool, GPUTexture *tmp_tex)
pool->tmp_tex_released.append(tmp_tex);
}
+void DRW_texture_pool_take_texture_ownership(DRWTexturePool *pool, GPUTexture *tex)
+{
+ pool->tmp_tex_acquired.remove_first_occurrence_and_reorder(tex);
+}
+
+void DRW_texture_pool_give_texture_ownership(DRWTexturePool *pool, GPUTexture *tex)
+{
+ BLI_assert(pool->tmp_tex_acquired.first_index_of_try(tex) == -1 &&
+ pool->tmp_tex_released.first_index_of_try(tex) == -1 &&
+ pool->tmp_tex_pruned.first_index_of_try(tex) == -1);
+ pool->tmp_tex_acquired.append(tex);
+}
+
void DRW_texture_pool_reset(DRWTexturePool *pool)
{
pool->last_user_id = -1;
diff --git a/source/blender/draw/intern/draw_texture_pool.h b/source/blender/draw/intern/draw_texture_pool.h
index 1c30ea88552..9fbbf630833 100644
--- a/source/blender/draw/intern/draw_texture_pool.h
+++ b/source/blender/draw/intern/draw_texture_pool.h
@@ -26,6 +26,7 @@ void DRW_texture_pool_free(DRWTexturePool *pool);
/**
* Try to find a texture corresponding to params into the texture pool.
* If no texture was found, create one and add it to the pool.
+ * DEPRECATED: Use DRW_texture_pool_texture_acquire instead and do it just before rendering.
*/
GPUTexture *DRW_texture_pool_query(
DRWTexturePool *pool, int width, int height, eGPUTextureFormat format, void *user);
@@ -40,6 +41,22 @@ GPUTexture *DRW_texture_pool_texture_acquire(DRWTexturePool *pool,
* Releases a previously acquired texture.
*/
void DRW_texture_pool_texture_release(DRWTexturePool *pool, GPUTexture *tmp_tex);
+
+/**
+ * This effectively remove a texture from the texture pool, giving full ownership to the caller.
+ * The given texture needs to be been acquired through DRW_texture_pool_texture_acquire().
+ * IMPORTANT: This removes the need for a DRW_texture_pool_texture_release() call on this texture.
+ */
+void DRW_texture_pool_take_texture_ownership(DRWTexturePool *pool, GPUTexture *tex);
+/**
+ * This Inserts a texture into the texture pool, giving full ownership to the texture pool.
+ * The texture needs not to be in the pool already.
+ * The texture may be reused in a latter call to DRW_texture_pool_texture_acquire();
+ * IMPORTANT: DRW_texture_pool_texture_release() still needs to be called on this texture
+ * after usage.
+ */
+void DRW_texture_pool_give_texture_ownership(DRWTexturePool *pool, GPUTexture *tex);
+
/**
* Resets the user bits for each texture in the pool and delete unused ones.
*/
diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c
index 817f97cbea4..35ff8891a0f 100644
--- a/source/blender/draw/intern/draw_view.c
+++ b/source/blender/draw/intern/draw_view.c
@@ -175,7 +175,7 @@ void DRW_draw_cursor(void)
GPU_matrix_scale_2f(U.widget_unit, U.widget_unit);
GPUBatch *cursor_batch = DRW_cache_cursor_get(is_aligned);
- GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR);
+ GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR);
GPU_batch_set_shader(cursor_batch, shader);
GPU_batch_draw(cursor_batch);
@@ -241,7 +241,7 @@ void DRW_draw_cursor_2d_ex(const ARegion *region, const float cursor[2])
GPUBatch *cursor_batch = DRW_cache_cursor_get(true);
- GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR);
+ GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR);
GPU_batch_set_shader(cursor_batch, shader);
GPU_batch_draw(cursor_batch);
diff --git a/source/blender/draw/intern/draw_view.cc b/source/blender/draw/intern/draw_view.cc
new file mode 100644
index 00000000000..cb0e1370c28
--- /dev/null
+++ b/source/blender/draw/intern/draw_view.cc
@@ -0,0 +1,334 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "BLI_math_geom.h"
+#include "GPU_compute.h"
+#include "GPU_debug.h"
+
+#include "draw_debug.hh"
+#include "draw_shader.h"
+#include "draw_view.hh"
+
+namespace blender::draw {
+
+void View::sync(const float4x4 &view_mat, const float4x4 &win_mat)
+{
+ data_.viewmat = view_mat;
+ data_.viewinv = view_mat.inverted();
+ data_.winmat = win_mat;
+ data_.wininv = win_mat.inverted();
+ data_.persmat = data_.winmat * data_.viewmat;
+ data_.persinv = data_.persmat.inverted();
+ /* Should not be used anymore. */
+ data_.viewcamtexcofac = float4(1.0f, 1.0f, 0.0f, 0.0f);
+
+ data_.is_inverted = (is_negative_m4(view_mat.ptr()) == is_negative_m4(win_mat.ptr()));
+
+ update_view_vectors();
+
+ BoundBox &bound_box = *reinterpret_cast<BoundBox *>(&data_.frustum_corners);
+ BoundSphere &bound_sphere = *reinterpret_cast<BoundSphere *>(&data_.frustum_bound_sphere);
+ frustum_boundbox_calc(bound_box);
+ frustum_culling_planes_calc();
+ frustum_culling_sphere_calc(bound_box, bound_sphere);
+
+ dirty_ = true;
+}
+
+void View::frustum_boundbox_calc(BoundBox &bbox)
+{
+ /* Extract the 8 corners from a Projection Matrix. */
+#if 0 /* Equivalent to this but it has accuracy problems. */
+ BKE_boundbox_init_from_minmax(&bbox, float3(-1.0f),float3(1.0f));
+ for (int i = 0; i < 8; i++) {
+ mul_project_m4_v3(data_.wininv.ptr(), bbox.vec[i]);
+ }
+#endif
+
+ float left, right, bottom, top, near, far;
+ bool is_persp = data_.winmat[3][3] == 0.0f;
+
+ projmat_dimensions(data_.winmat.ptr(), &left, &right, &bottom, &top, &near, &far);
+
+ bbox.vec[0][2] = bbox.vec[3][2] = bbox.vec[7][2] = bbox.vec[4][2] = -near;
+ bbox.vec[0][0] = bbox.vec[3][0] = left;
+ bbox.vec[4][0] = bbox.vec[7][0] = right;
+ bbox.vec[0][1] = bbox.vec[4][1] = bottom;
+ bbox.vec[7][1] = bbox.vec[3][1] = top;
+
+ /* Get the coordinates of the far plane. */
+ if (is_persp) {
+ float sca_far = far / near;
+ left *= sca_far;
+ right *= sca_far;
+ bottom *= sca_far;
+ top *= sca_far;
+ }
+
+ bbox.vec[1][2] = bbox.vec[2][2] = bbox.vec[6][2] = bbox.vec[5][2] = -far;
+ bbox.vec[1][0] = bbox.vec[2][0] = left;
+ bbox.vec[6][0] = bbox.vec[5][0] = right;
+ bbox.vec[1][1] = bbox.vec[5][1] = bottom;
+ bbox.vec[2][1] = bbox.vec[6][1] = top;
+
+ /* Transform into world space. */
+ for (int i = 0; i < 8; i++) {
+ mul_m4_v3(data_.viewinv.ptr(), bbox.vec[i]);
+ }
+}
+
+void View::frustum_culling_planes_calc()
+{
+ planes_from_projmat(data_.persmat.ptr(),
+ data_.frustum_planes[0],
+ data_.frustum_planes[5],
+ data_.frustum_planes[1],
+ data_.frustum_planes[3],
+ data_.frustum_planes[4],
+ data_.frustum_planes[2]);
+
+ /* Normalize. */
+ for (int p = 0; p < 6; p++) {
+ data_.frustum_planes[p].w /= normalize_v3(data_.frustum_planes[p]);
+ }
+}
+
+void View::frustum_culling_sphere_calc(const BoundBox &bbox, BoundSphere &bsphere)
+{
+ /* Extract Bounding Sphere */
+ if (data_.winmat[3][3] != 0.0f) {
+ /* Orthographic */
+ /* The most extreme points on the near and far plane. (normalized device coords). */
+ const float *nearpoint = bbox.vec[0];
+ const float *farpoint = bbox.vec[6];
+
+ /* just use median point */
+ mid_v3_v3v3(bsphere.center, farpoint, nearpoint);
+ bsphere.radius = len_v3v3(bsphere.center, farpoint);
+ }
+ else if (data_.winmat[2][0] == 0.0f && data_.winmat[2][1] == 0.0f) {
+ /* Perspective with symmetrical frustum. */
+
+ /* We obtain the center and radius of the circumscribed circle of the
+ * isosceles trapezoid composed by the diagonals of the near and far clipping plane */
+
+ /* center of each clipping plane */
+ float mid_min[3], mid_max[3];
+ mid_v3_v3v3(mid_min, bbox.vec[3], bbox.vec[4]);
+ mid_v3_v3v3(mid_max, bbox.vec[2], bbox.vec[5]);
+
+ /* square length of the diagonals of each clipping plane */
+ float a_sq = len_squared_v3v3(bbox.vec[3], bbox.vec[4]);
+ float b_sq = len_squared_v3v3(bbox.vec[2], bbox.vec[5]);
+
+ /* distance squared between clipping planes */
+ float h_sq = len_squared_v3v3(mid_min, mid_max);
+
+ float fac = (4 * h_sq + b_sq - a_sq) / (8 * h_sq);
+
+ /* The goal is to get the smallest sphere,
+ * not the sphere that passes through each corner */
+ CLAMP(fac, 0.0f, 1.0f);
+
+ interp_v3_v3v3(bsphere.center, mid_min, mid_max, fac);
+
+ /* distance from the center to one of the points of the far plane (1, 2, 5, 6) */
+ bsphere.radius = len_v3v3(bsphere.center, bbox.vec[1]);
+ }
+ else {
+ /* Perspective with asymmetrical frustum. */
+
+ /* We put the sphere center on the line that goes from origin
+ * to the center of the far clipping plane. */
+
+ /* Detect which of the corner of the far clipping plane is the farthest to the origin */
+ float nfar[4]; /* most extreme far point in NDC space */
+ float farxy[2]; /* far-point projection onto the near plane */
+ float farpoint[3] = {0.0f}; /* most extreme far point in camera coordinate */
+ float nearpoint[3]; /* most extreme near point in camera coordinate */
+ float farcenter[3] = {0.0f}; /* center of far clipping plane in camera coordinate */
+ float F = -1.0f, N; /* square distance of far and near point to origin */
+ float f, n; /* distance of far and near point to z axis. f is always > 0 but n can be < 0 */
+ float e, s; /* far and near clipping distance (<0) */
+ float c; /* slope of center line = distance of far clipping center
+ * to z axis / far clipping distance. */
+ float z; /* projection of sphere center on z axis (<0) */
+
+ /* Find farthest corner and center of far clip plane. */
+ float corner[3] = {1.0f, 1.0f, 1.0f}; /* in clip space */
+ for (int i = 0; i < 4; i++) {
+ float point[3];
+ mul_v3_project_m4_v3(point, data_.wininv.ptr(), corner);
+ float len = len_squared_v3(point);
+ if (len > F) {
+ copy_v3_v3(nfar, corner);
+ copy_v3_v3(farpoint, point);
+ F = len;
+ }
+ add_v3_v3(farcenter, point);
+ /* rotate by 90 degree to walk through the 4 points of the far clip plane */
+ float tmp = corner[0];
+ corner[0] = -corner[1];
+ corner[1] = tmp;
+ }
+
+ /* the far center is the average of the far clipping points */
+ mul_v3_fl(farcenter, 0.25f);
+ /* the extreme near point is the opposite point on the near clipping plane */
+ copy_v3_fl3(nfar, -nfar[0], -nfar[1], -1.0f);
+ mul_v3_project_m4_v3(nearpoint, data_.wininv.ptr(), nfar);
+ /* this is a frustum projection */
+ N = len_squared_v3(nearpoint);
+ e = farpoint[2];
+ s = nearpoint[2];
+ /* distance to view Z axis */
+ f = len_v2(farpoint);
+ /* get corresponding point on the near plane */
+ mul_v2_v2fl(farxy, farpoint, s / e);
+ /* this formula preserve the sign of n */
+ sub_v2_v2(nearpoint, farxy);
+ n = f * s / e - len_v2(nearpoint);
+ c = len_v2(farcenter) / e;
+ /* the big formula, it simplifies to (F-N)/(2(e-s)) for the symmetric case */
+ z = (F - N) / (2.0f * (e - s + c * (f - n)));
+
+ bsphere.center[0] = farcenter[0] * z / e;
+ bsphere.center[1] = farcenter[1] * z / e;
+ bsphere.center[2] = z;
+
+ /* For XR, the view matrix may contain a scale factor. Then, transforming only the center
+ * into world space after calculating the radius will result in incorrect behavior. */
+ mul_m4_v3(data_.viewinv.ptr(), bsphere.center); /* Transform to world space. */
+ mul_m4_v3(data_.viewinv.ptr(), farpoint);
+ bsphere.radius = len_v3v3(bsphere.center, farpoint);
+ }
+}
+
+void View::set_clip_planes(Span<float4> planes)
+{
+ BLI_assert(planes.size() <= ARRAY_SIZE(data_.clip_planes));
+ int i = 0;
+ for (const auto &plane : planes) {
+ data_.clip_planes[i++] = plane;
+ }
+}
+
+void View::update_viewport_size()
+{
+ float4 viewport;
+ GPU_viewport_size_get_f(viewport);
+ float2 viewport_size = float2(viewport.z, viewport.w);
+ if (assign_if_different(data_.viewport_size, viewport_size)) {
+ dirty_ = true;
+ }
+}
+
+void View::update_view_vectors()
+{
+ bool is_persp = data_.winmat[3][3] == 0.0f;
+
+ /* Near clip distance. */
+ data_.viewvecs[0][3] = (is_persp) ? -data_.winmat[3][2] / (data_.winmat[2][2] - 1.0f) :
+ -(data_.winmat[3][2] + 1.0f) / data_.winmat[2][2];
+
+ /* Far clip distance. */
+ data_.viewvecs[1][3] = (is_persp) ? -data_.winmat[3][2] / (data_.winmat[2][2] + 1.0f) :
+ -(data_.winmat[3][2] - 1.0f) / data_.winmat[2][2];
+
+ /* View vectors for the corners of the view frustum.
+ * Can be used to recreate the world space position easily */
+ float3 view_vecs[4] = {
+ {-1.0f, -1.0f, -1.0f},
+ {1.0f, -1.0f, -1.0f},
+ {-1.0f, 1.0f, -1.0f},
+ {-1.0f, -1.0f, 1.0f},
+ };
+
+ /* Convert the view vectors to view space */
+ for (int i = 0; i < 4; i++) {
+ mul_project_m4_v3(data_.wininv.ptr(), view_vecs[i]);
+ /* Normalized trick see:
+ * http://www.derschmale.com/2014/01/26/reconstructing-positions-from-the-depth-buffer */
+ if (is_persp) {
+ view_vecs[i].x /= view_vecs[i].z;
+ view_vecs[i].y /= view_vecs[i].z;
+ }
+ }
+
+ /**
+ * - If orthographic:
+ * `view_vecs[0]` is the near-bottom-left corner of the frustum and
+ * `view_vecs[1]` is the vector going from the near-bottom-left corner to
+ * the far-top-right corner.
+ * - If perspective:
+ * `view_vecs[0].xy` and `view_vecs[1].xy` are respectively the bottom-left corner
+ * when `Z = 1`, and top-left corner if `Z = 1`.
+ * `view_vecs[0].z` the near clip distance and `view_vecs[1].z` is the (signed)
+ * distance from the near plane to the far clip plane.
+ */
+ copy_v3_v3(data_.viewvecs[0], view_vecs[0]);
+
+ /* we need to store the differences */
+ data_.viewvecs[1][0] = view_vecs[1][0] - view_vecs[0][0];
+ data_.viewvecs[1][1] = view_vecs[2][1] - view_vecs[0][1];
+ data_.viewvecs[1][2] = view_vecs[3][2] - view_vecs[0][2];
+}
+
+void View::bind()
+{
+ update_viewport_size();
+
+ if (dirty_) {
+ dirty_ = false;
+ data_.push_update();
+ }
+
+ GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT);
+}
+
+void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze)
+{
+ if (debug_freeze && frozen_ == false) {
+ data_freeze_ = static_cast<ViewInfos>(data_);
+ data_freeze_.push_update();
+ }
+#ifdef DEBUG
+ if (debug_freeze) {
+ drw_debug_matrix_as_bbox(data_freeze_.persinv, float4(0, 1, 0, 1));
+ }
+#endif
+ frozen_ = debug_freeze;
+
+ GPU_debug_group_begin("View.compute_visibility");
+
+ /* TODO(fclem): Early out if visibility hasn't changed. */
+ /* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */
+ visibility_buf_.resize(divide_ceil_u(resource_len, 128));
+
+ uint32_t data = 0xFFFFFFFFu;
+ GPU_storagebuf_clear(visibility_buf_, GPU_R32UI, GPU_DATA_UINT, &data);
+
+ if (do_visibility_) {
+ GPUShader *shader = DRW_shader_draw_visibility_compute_get();
+ GPU_shader_bind(shader);
+ GPU_shader_uniform_1i(shader, "resource_len", resource_len);
+ GPU_storagebuf_bind(bounds, GPU_shader_get_ssbo(shader, "bounds_buf"));
+ GPU_storagebuf_bind(visibility_buf_, GPU_shader_get_ssbo(shader, "visibility_buf"));
+ GPU_uniformbuf_bind((frozen_) ? data_freeze_ : data_, DRW_VIEW_UBO_SLOT);
+ GPU_compute_dispatch(shader, divide_ceil_u(resource_len, DRW_VISIBILITY_GROUP_SIZE), 1, 1);
+ GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
+ }
+
+ if (frozen_) {
+ /* Bind back the non frozen data. */
+ GPU_uniformbuf_bind(data_, DRW_VIEW_UBO_SLOT);
+ }
+
+ GPU_debug_group_end();
+}
+
+} // namespace blender::draw
diff --git a/source/blender/draw/intern/draw_view.hh b/source/blender/draw/intern/draw_view.hh
new file mode 100644
index 00000000000..27e7a7a0028
--- /dev/null
+++ b/source/blender/draw/intern/draw_view.hh
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. */
+
+#pragma once
+
+/** \file
+ * \ingroup draw
+ */
+
+#include "DRW_gpu_wrapper.hh"
+#include "DRW_render.h"
+
+#include "draw_shader_shared.h"
+
+namespace blender::draw {
+
+class Manager;
+
+/* TODO: de-duplicate. */
+using ObjectBoundsBuf = StorageArrayBuffer<ObjectBounds, 128>;
+/** \note Using uint4 for declaration but bound as uint. */
+using VisibilityBuf = StorageArrayBuffer<uint4, 1, true>;
+
+class View {
+ friend Manager;
+
+ private:
+ UniformBuffer<ViewInfos> data_;
+ /** Frozen version of data_ used for debugging culling. */
+ UniformBuffer<ViewInfos> data_freeze_;
+ /** Result of the visibility computation. 1 bit per resource ID. */
+ VisibilityBuf visibility_buf_;
+
+ const char *debug_name_;
+
+ bool do_visibility_ = true;
+ bool dirty_ = true;
+ bool frozen_ = false;
+
+ public:
+ View(const char *name) : visibility_buf_(name), debug_name_(name){};
+ /* For compatibility with old system. Will be removed at some point. */
+ View(const char *name, const DRWView *view) : visibility_buf_(name), debug_name_(name)
+ {
+ float4x4 view_mat, win_mat;
+ DRW_view_viewmat_get(view, view_mat.ptr(), false);
+ DRW_view_winmat_get(view, win_mat.ptr(), false);
+ this->sync(view_mat, win_mat);
+ }
+
+ void set_clip_planes(Span<float4> planes);
+
+ void sync(const float4x4 &view_mat, const float4x4 &win_mat);
+
+ bool is_persp() const
+ {
+ return data_.winmat[3][3] == 0.0f;
+ }
+
+ bool is_inverted() const
+ {
+ return data_.is_inverted;
+ }
+
+ float far_clip() const
+ {
+ if (is_persp()) {
+ return -data_.winmat[3][2] / (data_.winmat[2][2] + 1.0f);
+ }
+ return -(data_.winmat[3][2] - 1.0f) / data_.winmat[2][2];
+ }
+
+ float near_clip() const
+ {
+ if (is_persp()) {
+ return -data_.winmat[3][2] / (data_.winmat[2][2] - 1.0f);
+ }
+ return -(data_.winmat[3][2] + 1.0f) / data_.winmat[2][2];
+ }
+
+ private:
+ /** Called from draw manager. */
+ void bind();
+ void compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool debug_freeze);
+
+ void update_view_vectors();
+ void update_viewport_size();
+
+ void frustum_boundbox_calc(BoundBox &bbox);
+ void frustum_culling_planes_calc();
+ void frustum_culling_sphere_calc(const BoundBox &bbox, BoundSphere &bsphere);
+};
+
+} // namespace blender::draw
diff --git a/source/blender/draw/intern/draw_view_data.cc b/source/blender/draw/intern/draw_view_data.cc
index 3dc28dc9a9a..58d826e0218 100644
--- a/source/blender/draw/intern/draw_view_data.cc
+++ b/source/blender/draw/intern/draw_view_data.cc
@@ -7,6 +7,7 @@
#include "BLI_vector.hh"
+#include "GPU_capabilities.h"
#include "GPU_viewport.h"
#include "DRW_render.h"
@@ -16,6 +17,7 @@
#include "draw_manager_text.h"
#include "draw_manager.h"
+#include "draw_manager.hh"
#include "draw_view_data.h"
using namespace blender;
@@ -33,6 +35,22 @@ struct DRWViewData {
Vector<ViewportEngineData> engines;
Vector<ViewportEngineData *> enabled_engines;
+
+ /** New per view/viewport manager. Null if not supported by current hardware. */
+ draw::Manager *manager = nullptr;
+
+ DRWViewData()
+ {
+ /* Only for GL >= 4.3 implementation for now. */
+ if (GPU_shader_storage_buffer_objects_support() && GPU_compute_shader_support()) {
+ manager = new draw::Manager();
+ }
+ };
+
+ ~DRWViewData()
+ {
+ delete manager;
+ };
};
DRWViewData *DRW_view_data_create(ListBase *engine_types)
@@ -197,6 +215,16 @@ void DRW_view_data_free_unused(DRWViewData *view_data)
}
}
+void DRW_view_data_engines_view_update(DRWViewData *view_data)
+{
+ for (ViewportEngineData &engine_data : view_data->engines) {
+ DrawEngineType *draw_engine = engine_data.engine_type->draw_engine;
+ if (draw_engine->view_update) {
+ draw_engine->view_update(&engine_data);
+ }
+ }
+}
+
double *DRW_view_data_cache_time_get(DRWViewData *view_data)
{
return &view_data->cache_time;
@@ -227,3 +255,31 @@ ViewportEngineData *DRW_view_data_enabled_engine_iter_step(DRWEngineIterator *it
ViewportEngineData *engine = iterator->engines[iterator->id++];
return engine;
}
+
+draw::Manager *DRW_manager_get()
+{
+ BLI_assert(DST.view_data_active->manager);
+ return reinterpret_cast<draw::Manager *>(DST.view_data_active->manager);
+}
+
+draw::ObjectRef DRW_object_ref_get(Object *object)
+{
+ BLI_assert(DST.view_data_active->manager);
+ return {object, DST.dupli_source, DST.dupli_parent};
+}
+
+void DRW_manager_begin_sync()
+{
+ if (DST.view_data_active->manager == nullptr) {
+ return;
+ }
+ reinterpret_cast<draw::Manager *>(DST.view_data_active->manager)->begin_sync();
+}
+
+void DRW_manager_end_sync()
+{
+ if (DST.view_data_active->manager == nullptr) {
+ return;
+ }
+ reinterpret_cast<draw::Manager *>(DST.view_data_active->manager)->end_sync();
+}
diff --git a/source/blender/draw/intern/draw_view_data.h b/source/blender/draw/intern/draw_view_data.h
index 918b9e81f87..f2c34c15f08 100644
--- a/source/blender/draw/intern/draw_view_data.h
+++ b/source/blender/draw/intern/draw_view_data.h
@@ -107,6 +107,7 @@ ViewportEngineData *DRW_view_data_engine_data_get_ensure(DRWViewData *view_data,
void DRW_view_data_use_engine(DRWViewData *view_data, struct DrawEngineType *engine_type);
void DRW_view_data_reset(DRWViewData *view_data);
void DRW_view_data_free_unused(DRWViewData *view_data);
+void DRW_view_data_engines_view_update(DRWViewData *view_data);
double *DRW_view_data_cache_time_get(DRWViewData *view_data);
DefaultFramebufferList *DRW_view_data_default_framebuffer_list_get(DRWViewData *view_data);
DefaultTextureList *DRW_view_data_default_texture_list_get(DRWViewData *view_data);
diff --git a/source/blender/draw/intern/draw_volume.cc b/source/blender/draw/intern/draw_volume.cc
index c4e58ab24cb..8f4383a98d8 100644
--- a/source/blender/draw/intern/draw_volume.cc
+++ b/source/blender/draw/intern/draw_volume.cc
@@ -89,6 +89,10 @@ void DRW_volume_free(void)
static GPUTexture *grid_default_texture(eGPUDefaultValue default_value)
{
+ if (g_data.dummy_one == nullptr) {
+ drw_volume_globals_init();
+ }
+
switch (default_value) {
case GPU_DEFAULT_0:
return g_data.dummy_zero;
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh
index 8052b277d45..10b94291e35 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh
@@ -29,7 +29,6 @@ struct DRWSubdivCache;
enum eMRExtractType {
MR_EXTRACT_BMESH,
- MR_EXTRACT_MAPPED,
MR_EXTRACT_MESH,
};
@@ -81,11 +80,18 @@ struct MeshRenderData {
BMFace *efa_act_uv;
/* Data created on-demand (usually not for #BMesh based data). */
MLoopTri *mlooptri;
+ const int *material_indices;
const float (*vert_normals)[3];
const float (*poly_normals)[3];
+ const bool *hide_vert;
+ const bool *hide_edge;
+ const bool *hide_poly;
float (*loop_normals)[3];
int *lverts, *ledges;
+ const char *active_color_name;
+ const char *default_color_name;
+
struct {
int *tri_first_index;
int *mat_tri_len;
@@ -93,6 +99,82 @@ struct MeshRenderData {
} poly_sorted;
};
+BLI_INLINE const Mesh *editmesh_final_or_this(const Object *object, const Mesh *me)
+{
+ if (me->edit_mesh != nullptr) {
+ Mesh *editmesh_eval_final = BKE_object_get_editmesh_eval_final(object);
+ if (editmesh_eval_final != nullptr) {
+ return editmesh_eval_final;
+ }
+ }
+
+ return me;
+}
+
+BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me)
+{
+ switch ((eMeshWrapperType)me->runtime.wrapper_type) {
+ case ME_WRAPPER_TYPE_SUBD:
+ case ME_WRAPPER_TYPE_MDATA:
+ return &me->ldata;
+ break;
+ case ME_WRAPPER_TYPE_BMESH:
+ return &me->edit_mesh->bm->ldata;
+ break;
+ }
+
+ BLI_assert(0);
+ return &me->ldata;
+}
+
+BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me)
+{
+ switch ((eMeshWrapperType)me->runtime.wrapper_type) {
+ case ME_WRAPPER_TYPE_SUBD:
+ case ME_WRAPPER_TYPE_MDATA:
+ return &me->pdata;
+ break;
+ case ME_WRAPPER_TYPE_BMESH:
+ return &me->edit_mesh->bm->pdata;
+ break;
+ }
+
+ BLI_assert(0);
+ return &me->pdata;
+}
+
+BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me)
+{
+ switch ((eMeshWrapperType)me->runtime.wrapper_type) {
+ case ME_WRAPPER_TYPE_SUBD:
+ case ME_WRAPPER_TYPE_MDATA:
+ return &me->edata;
+ break;
+ case ME_WRAPPER_TYPE_BMESH:
+ return &me->edit_mesh->bm->edata;
+ break;
+ }
+
+ BLI_assert(0);
+ return &me->edata;
+}
+
+BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me)
+{
+ switch ((eMeshWrapperType)me->runtime.wrapper_type) {
+ case ME_WRAPPER_TYPE_SUBD:
+ case ME_WRAPPER_TYPE_MDATA:
+ return &me->vdata;
+ break;
+ case ME_WRAPPER_TYPE_BMESH:
+ return &me->edit_mesh->bm->vdata;
+ break;
+ }
+
+ BLI_assert(0);
+ return &me->vdata;
+}
+
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) ?
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
index 2ff093d0bd8..2f2e59c8c3b 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc
@@ -59,13 +59,11 @@ static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr,
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]);
+ const BMFace *efa = bm_original_face_get(mr, mlt->poly);
+ const bool mp_hidden = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_HIDDEN) : true;
+ const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
+
+ edituv_tri_add(data, mp_hidden, mp_select, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
}
static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr),
@@ -117,7 +115,7 @@ static void extract_edituv_tris_iter_subdiv_bm(const DRWSubdivCache *UNUSED(subd
}
static void extract_edituv_tris_iter_subdiv_mesh(const DRWSubdivCache *UNUSED(subdiv_cache),
- const MeshRenderData *UNUSED(mr),
+ const MeshRenderData *mr,
void *_data,
uint subdiv_quad_index,
const MPoly *coarse_quad)
@@ -125,19 +123,12 @@ static void extract_edituv_tris_iter_subdiv_mesh(const DRWSubdivCache *UNUSED(su
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
const uint loop_idx = subdiv_quad_index * 4;
- edituv_tri_add(data,
- (coarse_quad->flag & ME_HIDE) != 0,
- (coarse_quad->flag & ME_FACE_SEL) != 0,
- loop_idx,
- loop_idx + 1,
- loop_idx + 2);
+ const BMFace *efa = bm_original_face_get(mr, coarse_quad - mr->mpoly);
+ const bool mp_hidden = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_HIDDEN) : true;
+ const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
- edituv_tri_add(data,
- (coarse_quad->flag & ME_HIDE) != 0,
- (coarse_quad->flag & ME_FACE_SEL) != 0,
- loop_idx,
- loop_idx + 2,
- loop_idx + 3);
+ edituv_tri_add(data, mp_hidden, mp_select, loop_idx, loop_idx + 1, loop_idx + 2);
+ edituv_tri_add(data, mp_hidden, mp_select, loop_idx, loop_idx + 2, loop_idx + 3);
}
static void extract_edituv_tris_finish_subdiv(const struct DRWSubdivCache *UNUSED(subdiv_cache),
@@ -214,12 +205,24 @@ static void extract_edituv_lines_iter_poly_bm(const MeshRenderData *UNUSED(mr),
static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
const MPoly *mp,
- const int UNUSED(mp_index),
+ const int 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;
+
+ bool mp_hidden, mp_select;
+ if (mr->bm) {
+ const BMFace *efa = bm_original_face_get(mr, mp_index);
+ mp_hidden = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_HIDDEN) : true;
+ mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
+ }
+ else {
+ mp_hidden = (mr->hide_poly) ? mr->hide_poly[mp_index] : false;
+ mp_select = (mp->flag & ME_FACE_SEL) != 0;
+ }
+
for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) {
const MLoop *ml = &mloop[ml_index];
@@ -227,11 +230,7 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr,
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);
+ edituv_edge_add(data, mp_hidden || !real_edge, mp_select, ml_index, ml_index_next);
}
}
@@ -266,6 +265,9 @@ static void extract_edituv_lines_iter_subdiv_bm(const DRWSubdivCache *subdiv_cac
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
int *subdiv_loop_edge_index = (int *)GPU_vertbuf_get_data(subdiv_cache->edges_orig_index);
+ const bool mp_hidden = BM_elem_flag_test_bool(coarse_poly, BM_ELEM_HIDDEN);
+ const bool mp_select = BM_elem_flag_test_bool(coarse_poly, BM_ELEM_SELECT);
+
uint start_loop_idx = subdiv_quad_index * 4;
uint end_loop_idx = (subdiv_quad_index + 1) * 4;
for (uint loop_idx = start_loop_idx; loop_idx < end_loop_idx; loop_idx++) {
@@ -274,8 +276,8 @@ static void extract_edituv_lines_iter_subdiv_bm(const DRWSubdivCache *subdiv_cac
(mr->e_origindex == nullptr ||
mr->e_origindex[edge_origindex] != ORIGINDEX_NONE));
edituv_edge_add(data,
- BM_elem_flag_test_bool(coarse_poly, BM_ELEM_HIDDEN) != 0 || !real_edge,
- BM_elem_flag_test_bool(coarse_poly, BM_ELEM_SELECT) != 0,
+ mp_hidden || !real_edge,
+ mp_select,
loop_idx,
(loop_idx + 1 == end_loop_idx) ? start_loop_idx : (loop_idx + 1));
}
@@ -290,6 +292,17 @@ static void extract_edituv_lines_iter_subdiv_mesh(const DRWSubdivCache *subdiv_c
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
int *subdiv_loop_edge_index = (int *)GPU_vertbuf_get_data(subdiv_cache->edges_orig_index);
+ bool mp_hidden, mp_select;
+ if (mr->bm) {
+ const BMFace *efa = bm_original_face_get(mr, coarse_poly - mr->mpoly);
+ mp_hidden = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_HIDDEN) : true;
+ mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
+ }
+ else {
+ mp_hidden = (mr->hide_poly) ? mr->hide_poly[coarse_poly - mr->mpoly] : false;
+ mp_select = (coarse_poly->flag & ME_FACE_SEL) != 0;
+ }
+
uint start_loop_idx = subdiv_quad_index * 4;
uint end_loop_idx = (subdiv_quad_index + 1) * 4;
for (uint loop_idx = start_loop_idx; loop_idx < end_loop_idx; loop_idx++) {
@@ -298,8 +311,8 @@ static void extract_edituv_lines_iter_subdiv_mesh(const DRWSubdivCache *subdiv_c
(mr->e_origindex == nullptr ||
mr->e_origindex[edge_origindex] != ORIGINDEX_NONE));
edituv_edge_add(data,
- (coarse_poly->flag & ME_HIDE) != 0 || !real_edge,
- (coarse_poly->flag & ME_FACE_SEL) != 0,
+ mp_hidden || !real_edge,
+ mp_select,
loop_idx,
(loop_idx + 1 == end_loop_idx) ? start_loop_idx : (loop_idx + 1));
}
@@ -378,18 +391,22 @@ static void extract_edituv_points_iter_poly_bm(const MeshRenderData *UNUSED(mr),
static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr,
const MPoly *mp,
- const int UNUSED(mp_index),
+ const int mp_index,
void *_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+
+ const BMFace *efa = bm_original_face_get(mr, mp_index);
+ const bool mp_hidden = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_HIDDEN) : true;
+ const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
+
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->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);
+ edituv_point_add(data, mp_hidden || !real_vert, mp_select, ml_index);
}
}
@@ -444,16 +461,17 @@ static void extract_edituv_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
int *subdiv_loop_vert_index = (int *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index);
+ const BMFace *efa = bm_original_face_get(mr, coarse_quad - mr->mpoly);
+ const bool mp_hidden = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_HIDDEN) : true;
+ const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
+
uint start_loop_idx = subdiv_quad_index * 4;
uint end_loop_idx = (subdiv_quad_index + 1) * 4;
for (uint i = start_loop_idx; i < end_loop_idx; i++) {
const int vert_origindex = subdiv_loop_vert_index[i];
const bool real_vert = !mr->v_origindex || (vert_origindex != -1 &&
mr->v_origindex[vert_origindex] != ORIGINDEX_NONE);
- edituv_point_add(data,
- ((coarse_quad->flag & ME_HIDE) != 0) || !real_vert,
- (coarse_quad->flag & ME_FACE_SEL) != 0,
- i);
+ edituv_point_add(data, mp_hidden || !real_vert, mp_select, i);
}
}
@@ -533,6 +551,11 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data);
+
+ const BMFace *efa = bm_original_face_get(mr, mp_index);
+ const bool mp_hidden = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_HIDDEN) : true;
+ const bool mp_select = (efa) ? BM_elem_flag_test_bool(efa, BM_ELEM_SELECT) : false;
+
if (mr->use_subsurf_fdots) {
const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags;
@@ -543,16 +566,12 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr,
const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE);
const bool subd_fdot = BLI_BITMAP_TEST(facedot_tags, ml->v);
- edituv_facedot_add(data,
- ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot,
- (mp->flag & ME_FACE_SEL) != 0,
- mp_index);
+ edituv_facedot_add(data, mp_hidden || !real_fdot || !subd_fdot, mp_select, mp_index);
}
}
else {
const bool real_fdot = !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);
+ edituv_facedot_add(data, mp_hidden || !real_fdot, mp_select, mp_index);
}
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
index cc0b383f12b..8dc00617039 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc
@@ -42,6 +42,8 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
const int mp_index,
void *_userdata)
{
+ const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mp - mr->mpoly];
+
GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata);
if (mr->use_subsurf_fdots) {
const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags;
@@ -50,7 +52,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
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 (BLI_BITMAP_TEST(facedot_tags, ml->v) && !(mr->use_hide && (mp->flag & ME_HIDE))) {
+ if (BLI_BITMAP_TEST(facedot_tags, ml->v) && !hidden) {
GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
return;
}
@@ -58,7 +60,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr,
GPU_indexbuf_set_point_restart(elb, mp_index);
}
else {
- if (!(mr->use_hide && (mp->flag & ME_HIDE))) {
+ if (!hidden) {
GPU_indexbuf_set_point_vert(elb, mp_index, mp_index);
}
else {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
index e6c0d815963..9c564c2cdda 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc
@@ -58,16 +58,13 @@ static void extract_lines_iter_poly_mesh(const MeshRenderData *mr,
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)) {
+ if (mr->use_hide || (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)))) {
+ if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[ml->e]) ||
+ ((mr->e_origindex) && (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) {
GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next);
}
else {
@@ -111,9 +108,8 @@ static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr,
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)))) {
+ if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[med - mr->medge]) ||
+ ((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);
}
@@ -185,32 +181,43 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache,
switch (mr->extract_type) {
case MR_EXTRACT_MESH: {
- const MEdge *medge = mr->medge;
- for (DRWSubdivLooseEdge edge : loose_edges) {
- *flags_data++ = (medge[edge.coarse_edge_index].flag & ME_HIDE) != 0;
- }
- break;
- }
- case MR_EXTRACT_MAPPED: {
- if (mr->bm) {
- for (DRWSubdivLooseEdge edge : loose_edges) {
- const BMEdge *bm_edge = bm_original_edge_get(mr, edge.coarse_edge_index);
- *flags_data++ = BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) != 0;
+ if (mr->e_origindex == nullptr) {
+ const bool *hide_edge = mr->hide_edge;
+ if (hide_edge) {
+ for (DRWSubdivLooseEdge edge : loose_edges) {
+ *flags_data++ = hide_edge[edge.coarse_edge_index];
+ }
+ }
+ else {
+ MutableSpan<uint>(flags_data, loose_edges.size()).fill(0);
}
}
else {
- for (DRWSubdivLooseEdge edge : loose_edges) {
- int e = edge.coarse_edge_index;
-
- if (mr->e_origindex && mr->e_origindex[e] != ORIGINDEX_NONE) {
- *flags_data++ = (mr->medge[mr->e_origindex[e]].flag & ME_HIDE) != 0;
+ if (mr->bm) {
+ for (DRWSubdivLooseEdge edge : loose_edges) {
+ const BMEdge *bm_edge = bm_original_edge_get(mr, edge.coarse_edge_index);
+ *flags_data++ = BM_elem_flag_test_bool(bm_edge, BM_ELEM_HIDDEN) != 0;
+ }
+ }
+ else {
+ const bool *hide_edge = mr->hide_edge;
+ if (hide_edge) {
+ for (DRWSubdivLooseEdge edge : loose_edges) {
+ int e = edge.coarse_edge_index;
+
+ if (mr->e_origindex && mr->e_origindex[e] != ORIGINDEX_NONE) {
+ *flags_data++ = hide_edge[edge.coarse_edge_index];
+ }
+ else {
+ *flags_data++ = false;
+ }
+ }
}
else {
- *flags_data++ = false;
+ MutableSpan<uint>(flags_data, loose_edges.size()).fill(0);
}
}
}
-
break;
}
case MR_EXTRACT_BMESH: {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
index c2cfb66ec28..d6c246c51a9 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc
@@ -119,16 +119,17 @@ static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr,
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);
+ const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mlt->poly];
+ if (hidden) {
+ return;
}
+ 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),
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
index 11c71d61775..31e5c515129 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc
@@ -47,10 +47,8 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr,
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)))) {
+ if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[e_index]) ||
+ ((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);
@@ -122,10 +120,8 @@ static void extract_lines_paint_mask_iter_subdiv_mesh(const DRWSubdivCache *subd
GPU_indexbuf_set_line_restart(&data->elb, subdiv_edge_index);
}
else {
- const MEdge *me = &mr->medge[coarse_edge_index];
- if (!((mr->use_hide && (me->flag & ME_HIDE)) ||
- ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) &&
- (mr->e_origindex[coarse_edge_index] == ORIGINDEX_NONE)))) {
+ if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[coarse_edge_index]) ||
+ ((mr->e_origindex) && (mr->e_origindex[coarse_edge_index] == ORIGINDEX_NONE)))) {
const uint ml_index_other = (loop_idx == (end_loop_idx - 1)) ? start_loop_idx :
loop_idx + 1;
if (coarse_quad->flag & ME_FACE_SEL) {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
index f7c5505422b..48eeb86e5ee 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc
@@ -43,10 +43,9 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb,
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)))) {
+ const bool hidden = mr->use_hide && mr->hide_vert && mr->hide_vert[v_index];
+
+ if (!(hidden || ((mr->v_origindex) && (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) {
GPU_indexbuf_set_point_vert(elb, v_index, l_index);
}
else {
@@ -181,8 +180,7 @@ static void extract_points_iter_subdiv_common(GPUIndexBufBuilder *elb,
}
}
else {
- const MVert *mv = &mr->mvert[coarse_vertex_index];
- if (mr->use_hide && (mv->flag & ME_HIDE)) {
+ if (mr->use_hide && mr->hide_vert && mr->hide_vert[coarse_vertex_index]) {
GPU_indexbuf_set_point_restart(elb, coarse_vertex_index);
continue;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
index 9fc18620d11..2e3e6c7b6b1 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc
@@ -189,12 +189,12 @@ static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr,
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]);
+ const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mlt->poly];
+ if (hidden) {
+ GPU_indexbuf_set_tri_restart(elb, mlt_index);
}
else {
- GPU_indexbuf_set_tri_restart(elb, mlt_index);
+ GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]);
}
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
index b98af3be17f..64ade020418 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc
@@ -9,6 +9,7 @@
#include <functional>
+#include "BLI_color.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_string.h"
@@ -57,7 +58,6 @@ template<typename AttributeType, typename VBOType> struct AttributeTypeConverter
}
};
-/* Similar to the one in #extract_mesh_vcol_vbo.cc */
struct gpuMeshCol {
ushort r, g, b, a;
};
@@ -74,6 +74,18 @@ template<> struct AttributeTypeConverter<MPropCol, gpuMeshCol> {
}
};
+template<> struct AttributeTypeConverter<ColorGeometry4b, gpuMeshCol> {
+ static gpuMeshCol convert_value(ColorGeometry4b value)
+ {
+ gpuMeshCol result;
+ result.r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[value.r]);
+ result.g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[value.g]);
+ result.b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[value.b]);
+ result.a = unit_float_to_ushort_clamp(value.a * (1.0f / 255.0f));
+ return result;
+ }
+};
+
/* Return the number of component for the attribute's value type, or 0 if is it unsupported. */
static uint gpu_component_size_for_attribute_type(eCustomDataType type)
{
@@ -90,6 +102,7 @@ static uint gpu_component_size_for_attribute_type(eCustomDataType type)
case CD_PROP_FLOAT3:
return 3;
case CD_PROP_COLOR:
+ case CD_PROP_BYTE_COLOR:
return 4;
default:
return 0;
@@ -102,6 +115,7 @@ static GPUVertFetchMode get_fetch_mode_for_type(eCustomDataType type)
case CD_PROP_INT32:
return GPU_FETCH_INT_TO_FLOAT;
case CD_PROP_COLOR:
+ case CD_PROP_BYTE_COLOR:
return GPU_FETCH_INT_TO_FLOAT_UNIT;
default:
return GPU_FETCH_FLOAT;
@@ -114,13 +128,15 @@ static GPUVertCompType get_comp_type_for_type(eCustomDataType type)
case CD_PROP_INT32:
return GPU_COMP_I32;
case CD_PROP_COLOR:
+ case CD_PROP_BYTE_COLOR:
return GPU_COMP_U16;
default:
return GPU_COMP_F32;
}
}
-static void init_vbo_for_attribute(GPUVertBuf *vbo,
+static void init_vbo_for_attribute(const MeshRenderData &mr,
+ GPUVertBuf *vbo,
const DRW_AttributeRequest &request,
bool build_on_device,
uint32_t len)
@@ -140,6 +156,13 @@ static void init_vbo_for_attribute(GPUVertBuf *vbo,
GPU_vertformat_deinterleave(&format);
GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode);
+ if (mr.active_color_name && STREQ(request.attribute_name, mr.active_color_name)) {
+ GPU_vertformat_alias_add(&format, "ac");
+ }
+ if (mr.default_color_name && STREQ(request.attribute_name, mr.default_color_name)) {
+ GPU_vertformat_alias_add(&format, "c");
+ }
+
if (build_on_device) {
GPU_vertbuf_init_build_on_device(vbo, &format, len);
}
@@ -262,7 +285,7 @@ static void extract_attr_init(
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
- init_vbo_for_attribute(vbo, request, false, static_cast<uint32_t>(mr->loop_len));
+ init_vbo_for_attribute(*mr, vbo, request, false, static_cast<uint32_t>(mr->loop_len));
/* TODO(@kevindietrich): float3 is used for scalar attributes as the implicit conversion done by
* OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the
@@ -290,6 +313,9 @@ static void extract_attr_init(
case CD_PROP_COLOR:
extract_attr_generic<MPropCol, gpuMeshCol>(mr, vbo, request);
break;
+ case CD_PROP_BYTE_COLOR:
+ extract_attr_generic<ColorGeometry4b, gpuMeshCol>(mr, vbo, request);
+ break;
default:
BLI_assert_unreachable();
}
@@ -338,12 +364,15 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache,
case CD_PROP_COLOR:
extract_attr_generic<MPropCol, gpuMeshCol>(mr, src_data, request);
break;
+ case CD_PROP_BYTE_COLOR:
+ extract_attr_generic<ColorGeometry4b, gpuMeshCol>(mr, src_data, request);
+ break;
default:
BLI_assert_unreachable();
}
GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer);
- init_vbo_for_attribute(dst_buffer, request, true, subdiv_cache->num_subdiv_loops);
+ init_vbo_for_attribute(*mr, dst_buffer, request, true, subdiv_cache->num_subdiv_loops);
/* Ensure data is uploaded properly. */
GPU_vertbuf_tag_dirty(src_data);
@@ -352,7 +381,7 @@ static void extract_attr_init_subdiv(const DRWSubdivCache *subdiv_cache,
dst_buffer,
static_cast<int>(dimensions),
0,
- request.cd_type == CD_PROP_COLOR);
+ ELEM(request.cd_type, CD_PROP_COLOR, CD_PROP_BYTE_COLOR));
GPU_vertbuf_discard(src_data);
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
index eb6e800023a..50c37f6397c 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc
@@ -174,7 +174,7 @@ static void extract_edge_fac_finish(const MeshRenderData *mr,
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
MeshExtract_EdgeFac_Data *data = static_cast<MeshExtract_EdgeFac_Data *>(_data);
- if (GPU_crappy_amd_driver()) {
+ if (GPU_crappy_amd_driver() || GPU_minimum_per_vertex_stride() > 1) {
/* 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. */
@@ -206,7 +206,7 @@ static GPUVertFormat *get_subdiv_edge_fac_format()
{
static GPUVertFormat format = {0};
if (format.attr_len == 0) {
- if (GPU_crappy_amd_driver()) {
+ if (GPU_crappy_amd_driver() || GPU_minimum_per_vertex_stride() > 1) {
GPU_vertformat_attr_add(&format, "wd", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
}
else {
@@ -268,7 +268,7 @@ static void extract_edge_fac_loose_geom_subdiv(const DRWSubdivCache *subdiv_cach
uint offset = subdiv_cache->num_subdiv_loops;
for (int i = 0; i < loose_geom.edge_len; i++) {
- if (GPU_crappy_amd_driver()) {
+ if (GPU_crappy_amd_driver() || GPU_minimum_per_vertex_stride() > 1) {
float loose_edge_fac[2] = {1.0f, 1.0f};
GPU_vertbuf_update_sub(vbo, offset * sizeof(float), sizeof(loose_edge_fac), loose_edge_fac);
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
index 969ff9f6f09..e4714aabf34 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_angle.cc
@@ -97,7 +97,7 @@ static void extract_edituv_stretch_angle_init(const MeshRenderData *mr,
data->cd_ofs = CustomData_get_offset(&mr->bm->ldata, CD_MLOOPUV);
}
else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ BLI_assert(mr->extract_type == MR_EXTRACT_MESH);
data->luv = (const MLoopUV *)CustomData_get_layer(&mr->me->ldata, CD_MLOOPUV);
}
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
index 2bb786303c4..9679c0523f8 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_edituv_stretch_area.cc
@@ -27,7 +27,7 @@ static void extract_edituv_stretch_area_init(const MeshRenderData *mr,
GPUVertBuf *vbo = static_cast<GPUVertBuf *>(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_vertformat_attr_add(&format, "ratio", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
}
GPU_vertbuf_init_with_format(vbo, &format);
@@ -37,15 +37,14 @@ static void extract_edituv_stretch_area_init(const MeshRenderData *mr,
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 uvarea / area;
}
return 0.0f;
}
-BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_tot_ratio)
+BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio)
{
- ratio *= (ratio > 0.0f) ? tot_ratio : -inv_tot_ratio;
+ ratio *= tot_ratio;
return (ratio > 1.0f) ? (1.0f / ratio) : ratio;
}
@@ -72,7 +71,7 @@ static void compute_area_ratio(const MeshRenderData *mr,
}
}
else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ BLI_assert(mr->extract_type == MR_EXTRACT_MESH);
const MLoopUV *uv_data = (const MLoopUV *)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++) {
@@ -97,14 +96,8 @@ static void extract_edituv_stretch_area_finish(const MeshRenderData *mr,
float *area_ratio = static_cast<float *>(MEM_mallocN(sizeof(float) * mr->poly_len, __func__));
compute_area_ratio(mr, area_ratio, cache->tot_area, cache->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);
+ float *loop_stretch = (float *)GPU_vertbuf_get_data(vbo);
if (mr->extract_type == MR_EXTRACT_BMESH) {
BMFace *efa;
@@ -112,16 +105,16 @@ static void extract_edituv_stretch_area_finish(const MeshRenderData *mr,
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];
+ loop_stretch[l_index] = area_ratio[f];
}
}
}
else {
- BLI_assert(ELEM(mr->extract_type, MR_EXTRACT_MAPPED, MR_EXTRACT_MESH));
+ BLI_assert(mr->extract_type == 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];
+ loop_stretch[l_index] = area_ratio[mp_index];
}
}
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
index c2af7f2c9bd..c47cde63630 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_fdots_nor.cc
@@ -48,8 +48,7 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr,
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)) {
+ if (is_face_hidden || (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;
}
@@ -66,8 +65,7 @@ static void extract_fdots_nor_finish(const MeshRenderData *mr,
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)) {
+ if (is_face_hidden || (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;
}
@@ -130,8 +128,7 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
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)) {
+ if (is_face_hidden || (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;
}
@@ -148,8 +145,7 @@ static void extract_fdots_nor_hq_finish(const MeshRenderData *mr,
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)) {
+ if (is_face_hidden || (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;
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
index ac517269e7d..01d07fa5f83 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc
@@ -62,6 +62,8 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
const int mp_index,
void *data)
{
+ const bool hidden = mr->hide_poly && mr->hide_poly[mp_index];
+
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) {
@@ -78,10 +80,10 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr,
}
/* Flag for paint mode overlay.
- * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
+ * Only use origindex 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)) {
+ if (hidden ||
+ (mr->edit_bmesh && (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
lnor_data->w = -1;
}
else if (mp->flag & ME_FACE_SEL) {
@@ -185,6 +187,8 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
const int mp_index,
void *data)
{
+ const bool hidden = mr->hide_poly && mr->hide_poly[mp_index];
+
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) {
@@ -201,10 +205,10 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr,
}
/* Flag for paint mode overlay.
- * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals.
+ * Only use origindex 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)) {
+ if (hidden ||
+ (mr->edit_bmesh && (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) {
lnor_data->w = -1;
}
else if (mp->flag & ME_FACE_SEL) {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
index 951990466d0..fe2a02b6b63 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_mesh_analysis.cc
@@ -259,7 +259,8 @@ static void statvis_calc_thickness(const MeshRenderData *mr, float *r_thickness)
}
struct BVHTree_OverlapData {
- const Mesh *me;
+ const MVert *verts;
+ const MLoop *loops;
const MLoopTri *mlooptri;
float epsilon;
};
@@ -267,7 +268,6 @@ struct BVHTree_OverlapData {
static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(thread))
{
struct BVHTree_OverlapData *data = static_cast<struct BVHTree_OverlapData *>(userdata);
- const Mesh *me = data->me;
const MLoopTri *tri_a = &data->mlooptri[index_a];
const MLoopTri *tri_b = &data->mlooptri[index_b];
@@ -276,12 +276,12 @@ static bool bvh_overlap_cb(void *userdata, int index_a, int index_b, int UNUSED(
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};
+ const float *tri_a_co[3] = {data->verts[data->loops[tri_a->tri[0]].v].co,
+ data->verts[data->loops[tri_a->tri[1]].v].co,
+ data->verts[data->loops[tri_a->tri[2]].v].co};
+ const float *tri_b_co[3] = {data->verts[data->loops[tri_b->tri[0]].v].co,
+ data->verts[data->loops[tri_b->tri[1]].v].co,
+ data->verts[data->loops[tri_b->tri[2]].v].co};
float ix_pair[2][3];
int verts_shared = 0;
@@ -342,7 +342,8 @@ static void statvis_calc_intersect(const MeshRenderData *mr, float *r_intersect)
BVHTree *tree = BKE_bvhtree_from_mesh_get(&treeData, mr->me, BVHTREE_FROM_LOOPTRI, 4);
struct BVHTree_OverlapData data = {nullptr};
- data.me = mr->me;
+ data.verts = mr->mvert;
+ data.loops = mr->mloop;
data.mlooptri = mr->mlooptri;
data.epsilon = BLI_bvhtree_get_epsilon(tree);
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
index 9788beabeb5..a822845c688 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc
@@ -83,10 +83,11 @@ static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr,
static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
const MPoly *mp,
- const int UNUSED(mp_index),
+ const int mp_index,
void *_data)
{
MeshExtract_PosNor_Data *data = static_cast<MeshExtract_PosNor_Data *>(_data);
+ const bool poly_hidden = mr->hide_poly && mr->hide_poly[mp_index];
const MLoop *mloop = mr->mloop;
const int ml_index_end = mp->loopstart + mp->totloop;
@@ -95,12 +96,12 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr,
PosNorLoop *vert = &data->vbo_data[ml_index];
const MVert *mv = &mr->mvert[ml->v];
+ const bool vert_hidden = mr->hide_vert && mr->hide_vert[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))) {
+ if (poly_hidden || vert_hidden ||
+ ((mr->v_origindex) && (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
vert->nor.w = -1;
}
else if (mv->flag & SELECT) {
@@ -432,20 +433,22 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr,
void *_data)
{
MeshExtract_PosNorHQ_Data *data = static_cast<MeshExtract_PosNorHQ_Data *>(_data);
+ const bool poly_hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly];
+
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 vert_hidden = mr->hide_vert && mr->hide_vert[ml->v];
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))) {
+ if (poly_hidden || vert_hidden ||
+ ((mr->v_origindex) && (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) {
vert->nor[3] = -1;
}
else if (mv->flag & SELECT) {
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
index 492721b4853..6202fdd312d 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_sculpt_data.cc
@@ -9,6 +9,7 @@
#include "BLI_string.h"
+#include "BKE_mesh.h"
#include "BKE_paint.h"
#include "draw_subdivision.h"
@@ -128,6 +129,9 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache,
GPUVertBuf *subdiv_mask_vbo = nullptr;
const float *cd_mask = (const float *)CustomData_get_layer(cd_vdata, CD_PAINT_MASK);
+ const Span<MPoly> coarse_polys = coarse_mesh->polys();
+ const Span<MLoop> coarse_loops = coarse_mesh->loops();
+
if (cd_mask) {
GPUVertFormat mask_format = {0};
GPU_vertformat_attr_add(&mask_format, "msk", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
@@ -138,11 +142,11 @@ static void extract_sculpt_data_init_subdiv(const DRWSubdivCache *subdiv_cache,
float *v_mask = static_cast<float *>(GPU_vertbuf_get_data(mask_vbo));
for (int i = 0; i < coarse_mesh->totpoly; i++) {
- const MPoly *mpoly = &coarse_mesh->mpoly[i];
+ const MPoly *mpoly = &coarse_polys[i];
for (int loop_index = mpoly->loopstart; loop_index < mpoly->loopstart + mpoly->totloop;
loop_index++) {
- const MLoop *ml = &coarse_mesh->mloop[loop_index];
+ const MLoop *ml = &coarse_loops[loop_index];
*v_mask++ = cd_mask[ml->v];
}
}
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
deleted file mode 100644
index 419cbb0267f..00000000000
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc
+++ /dev/null
@@ -1,387 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2021 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup draw
- */
-
-#include "MEM_guardedalloc.h"
-
-#include "BKE_attribute.h"
-#include "BLI_string.h"
-#include "BLI_vector.hh"
-
-#include "draw_subdivision.h"
-#include "extract_mesh.hh"
-
-namespace blender::draw {
-
-struct VColRef {
- const CustomDataLayer *layer;
- eAttrDomain domain;
-};
-
-/** Get all vcol layers as AttributeRefs.
- *
- * \param vcol_layers: bitmask to filter vcol layers by, each bit
- * corresponds to the integer position of the attribute
- * within the global color attribute list.
- */
-static Vector<VColRef> get_vcol_refs(const CustomData *cd_vdata,
- const CustomData *cd_ldata,
- const uint vcol_layers)
-{
- Vector<VColRef> refs;
- uint layeri = 0;
-
- auto buildList = [&](const CustomData *cdata, eAttrDomain domain) {
- for (int i = 0; i < cdata->totlayer; i++) {
- const CustomDataLayer *layer = cdata->layers + i;
-
- if (!(CD_TYPE_AS_MASK(layer->type) & CD_MASK_COLOR_ALL)) {
- continue;
- }
-
- if (layer->flag & CD_FLAG_TEMPORARY) {
- continue;
- }
-
- if (!(vcol_layers & (1UL << layeri))) {
- layeri++;
- continue;
- }
-
- VColRef ref = {};
- ref.domain = domain;
- ref.layer = layer;
-
- refs.append(ref);
- layeri++;
- }
- };
-
- buildList(cd_vdata, ATTR_DOMAIN_POINT);
- buildList(cd_ldata, ATTR_DOMAIN_CORNER);
-
- return refs;
-}
-
-/* ---------------------------------------------------------------------- */
-/** \name Extract VCol
- * \{ */
-
-/* Initialize the common vertex format for vcol for coarse and subdivided meshes. */
-static void init_vcol_format(GPUVertFormat *format,
- const MeshBatchCache *cache,
- const CustomData *cd_vdata,
- const CustomData *cd_ldata,
- const CustomDataLayer *active,
- const CustomDataLayer *render)
-{
- GPU_vertformat_deinterleave(format);
-
- const uint32_t vcol_layers = cache->cd_used.vcol;
-
- Vector<VColRef> refs = get_vcol_refs(cd_vdata, cd_ldata, vcol_layers);
-
- for (const VColRef &ref : refs) {
- char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME];
-
- GPU_vertformat_safe_attr_name(ref.layer->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME);
-
- /* VCol layer name. */
- BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name);
- GPU_vertformat_attr_add(format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
-
- /* Active layer name. */
- if (ref.layer == active) {
- GPU_vertformat_alias_add(format, "ac");
- }
-
- /* Active render layer name. */
- if (ref.layer == render) {
- GPU_vertformat_alias_add(format, "c");
- }
- }
-}
-
-/* Vertex format for vertex colors, only used during the coarse data upload for the subdivision
- * case. */
-static GPUVertFormat *get_coarse_vcol_format()
-{
- static GPUVertFormat format = {0};
- if (format.attr_len == 0) {
- GPU_vertformat_attr_add(&format, "cCol", GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- GPU_vertformat_alias_add(&format, "c");
- GPU_vertformat_alias_add(&format, "ac");
- }
- return &format;
-}
-
-struct gpuMeshVcol {
- ushort r, g, b, a;
-};
-
-static void extract_vcol_init(const MeshRenderData *mr,
- MeshBatchCache *cache,
- void *buf,
- void *UNUSED(tls_data))
-{
- GPUVertBuf *vbo = static_cast<GPUVertBuf *>(buf);
- GPUVertFormat format = {0};
-
- const CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata :
- &mr->me->vdata;
- const CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata :
- &mr->me->ldata;
-
- Mesh me_query = blender::dna::shallow_zero_initialize();
-
- BKE_id_attribute_copy_domains_temp(
- ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id);
-
- const CustomDataLayer *active_color = BKE_id_attributes_active_color_get(&me_query.id);
- const CustomDataLayer *render_color = BKE_id_attributes_render_color_get(&me_query.id);
-
- const uint32_t vcol_layers = cache->cd_used.vcol;
- init_vcol_format(&format, cache, cd_vdata, cd_ldata, active_color, render_color);
-
- GPU_vertbuf_init_with_format(vbo, &format);
- GPU_vertbuf_data_alloc(vbo, mr->loop_len);
-
- gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo);
-
- Vector<VColRef> refs = get_vcol_refs(cd_vdata, cd_ldata, vcol_layers);
-
- for (const VColRef &ref : refs) {
- const CustomData *cdata = ref.domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata;
-
- if (mr->extract_type == MR_EXTRACT_BMESH) {
- int cd_ofs = ref.layer->offset;
-
- if (cd_ofs == -1) {
- vcol_data += ref.domain == ATTR_DOMAIN_POINT ? mr->bm->totvert : mr->bm->totloop;
- continue;
- }
-
- BMIter iter;
- const bool is_byte = ref.layer->type == CD_PROP_BYTE_COLOR;
- const bool is_point = ref.domain == ATTR_DOMAIN_POINT;
-
- BMFace *f;
- BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) {
- const BMLoop *l_iter = f->l_first;
- do {
- const BMElem *elem = is_point ? reinterpret_cast<const BMElem *>(l_iter->v) :
- reinterpret_cast<const BMElem *>(l_iter);
- if (is_byte) {
- const MLoopCol *mloopcol = (const MLoopCol *)BM_ELEM_CD_GET_VOID_P(elem, 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++;
- }
- else {
- const MPropCol *mpcol = (const MPropCol *)BM_ELEM_CD_GET_VOID_P(elem, cd_ofs);
- vcol_data->r = unit_float_to_ushort_clamp(mpcol->color[0]);
- vcol_data->g = unit_float_to_ushort_clamp(mpcol->color[1]);
- vcol_data->b = unit_float_to_ushort_clamp(mpcol->color[2]);
- vcol_data->a = unit_float_to_ushort_clamp(mpcol->color[3]);
- vcol_data++;
- }
- } while ((l_iter = l_iter->next) != f->l_first);
- }
- }
- else {
- int totloop = mr->loop_len;
- const int idx = CustomData_get_named_layer_index(cdata, ref.layer->type, ref.layer->name);
-
- const MLoopCol *mcol = nullptr;
- const MPropCol *pcol = nullptr;
- const MLoop *mloop = mr->mloop;
-
- if (ref.layer->type == CD_PROP_COLOR) {
- pcol = static_cast<const MPropCol *>(cdata->layers[idx].data);
- }
- else {
- mcol = static_cast<const MLoopCol *>(cdata->layers[idx].data);
- }
-
- const bool is_corner = ref.domain == ATTR_DOMAIN_CORNER;
-
- for (int i = 0; i < totloop; i++, mloop++) {
- const int v_i = is_corner ? i : mloop->v;
-
- if (mcol) {
- vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[v_i].r]);
- vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[v_i].g]);
- vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[v_i].b]);
- vcol_data->a = unit_float_to_ushort_clamp(mcol[v_i].a * (1.0f / 255.0f));
- vcol_data++;
- }
- else if (pcol) {
- vcol_data->r = unit_float_to_ushort_clamp(pcol[v_i].color[0]);
- vcol_data->g = unit_float_to_ushort_clamp(pcol[v_i].color[1]);
- vcol_data->b = unit_float_to_ushort_clamp(pcol[v_i].color[2]);
- vcol_data->a = unit_float_to_ushort_clamp(pcol[v_i].color[3]);
- vcol_data++;
- }
- }
- }
- }
-}
-
-static void extract_vcol_init_subdiv(const DRWSubdivCache *subdiv_cache,
- const MeshRenderData *mr,
- MeshBatchCache *cache,
- void *buffer,
- void *UNUSED(data))
-{
- GPUVertBuf *dst_buffer = static_cast<GPUVertBuf *>(buffer);
- const Mesh *coarse_mesh = subdiv_cache->mesh;
-
- bool extract_bmesh = mr->extract_type == MR_EXTRACT_BMESH;
-
- const CustomData *cd_vdata = extract_bmesh ? &coarse_mesh->edit_mesh->bm->vdata :
- &coarse_mesh->vdata;
- const CustomData *cd_ldata = extract_bmesh ? &coarse_mesh->edit_mesh->bm->ldata :
- &coarse_mesh->ldata;
- const int totloop = extract_bmesh ? coarse_mesh->edit_mesh->bm->totloop : coarse_mesh->totloop;
-
- Mesh me_query = blender::dna::shallow_copy(*coarse_mesh);
- BKE_id_attribute_copy_domains_temp(
- ID_ME, cd_vdata, nullptr, cd_ldata, nullptr, nullptr, &me_query.id);
-
- const CustomDataLayer *active_color = BKE_id_attributes_active_color_get(&me_query.id);
- const CustomDataLayer *render_color = BKE_id_attributes_render_color_get(&me_query.id);
-
- GPUVertFormat format = {0};
- init_vcol_format(
- &format, cache, &coarse_mesh->vdata, &coarse_mesh->ldata, active_color, render_color);
-
- GPU_vertbuf_init_build_on_device(dst_buffer, &format, subdiv_cache->num_subdiv_loops);
-
- GPUVertBuf *src_data = GPU_vertbuf_calloc();
- /* Dynamic as we upload and interpolate layers one at a time. */
- GPU_vertbuf_init_with_format_ex(src_data, get_coarse_vcol_format(), GPU_USAGE_DYNAMIC);
-
- GPU_vertbuf_data_alloc(src_data, totloop);
-
- gpuMeshVcol *mesh_vcol = (gpuMeshVcol *)GPU_vertbuf_get_data(src_data);
-
- const uint vcol_layers = cache->cd_used.vcol;
-
- Vector<VColRef> refs = get_vcol_refs(cd_vdata, cd_ldata, vcol_layers);
-
- /* Index of the vertex color layer in the compact buffer. Used vertex color layers are stored in
- * a single buffer. */
- int pack_layer_index = 0;
- for (const VColRef &ref : refs) {
- /* Include stride in offset, we use a stride of 2 since colors are packed into 2 uints. */
- const int dst_offset = (int)subdiv_cache->num_subdiv_loops * 2 * pack_layer_index++;
-
- const CustomData *cdata = ref.domain == ATTR_DOMAIN_POINT ? cd_vdata : cd_ldata;
- int layer_i = CustomData_get_named_layer_index(cdata, ref.layer->type, ref.layer->name);
-
- if (layer_i == -1) {
- printf("%s: missing color layer %s\n", __func__, ref.layer->name);
- continue;
- }
-
- gpuMeshVcol *vcol = mesh_vcol;
-
- const bool is_vert = ref.domain == ATTR_DOMAIN_POINT;
-
- if (extract_bmesh) {
- BMesh *bm = coarse_mesh->edit_mesh->bm;
- BMIter iter;
- BMFace *f;
- int cd_ofs = cdata->layers[layer_i].offset;
- const bool is_byte = ref.layer->type == CD_PROP_BYTE_COLOR;
-
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- const BMLoop *l_iter = f->l_first;
-
- do {
- const BMElem *elem = is_vert ? reinterpret_cast<const BMElem *>(l_iter->v) :
- reinterpret_cast<const BMElem *>(l_iter);
-
- if (is_byte) {
- const MLoopCol *mcol2 = static_cast<const MLoopCol *>(
- BM_ELEM_CD_GET_VOID_P(elem, cd_ofs));
-
- vcol->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol2->r]);
- vcol->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol2->g]);
- vcol->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol2->b]);
- vcol->a = unit_float_to_ushort_clamp(mcol2->a * (1.0f / 255.0f));
- }
- else {
- const MPropCol *pcol2 = static_cast<const MPropCol *>(
- BM_ELEM_CD_GET_VOID_P(elem, cd_ofs));
-
- vcol->r = unit_float_to_ushort_clamp(pcol2->color[0]);
- vcol->g = unit_float_to_ushort_clamp(pcol2->color[1]);
- vcol->b = unit_float_to_ushort_clamp(pcol2->color[2]);
- vcol->a = unit_float_to_ushort_clamp(pcol2->color[3]);
- }
-
- vcol++;
- } while ((l_iter = l_iter->next) != f->l_first);
- }
- }
- else {
- const MLoop *ml = coarse_mesh->mloop;
- const MLoopCol *mcol = nullptr;
- const MPropCol *pcol = nullptr;
-
- if (ref.layer->type == CD_PROP_COLOR) {
- pcol = static_cast<const MPropCol *>(cdata->layers[layer_i].data);
- }
- else {
- mcol = static_cast<const MLoopCol *>(cdata->layers[layer_i].data);
- }
-
- for (int ml_index = 0; ml_index < coarse_mesh->totloop; ml_index++, vcol++, ml++) {
- int idx = is_vert ? ml->v : ml_index;
-
- if (mcol) {
- vcol->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[idx].r]);
- vcol->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[idx].g]);
- vcol->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol[idx].b]);
- vcol->a = unit_float_to_ushort_clamp(mcol[idx].a * (1.0f / 255.0f));
- }
- else if (pcol) {
- vcol->r = unit_float_to_ushort_clamp(pcol[idx].color[0]);
- vcol->g = unit_float_to_ushort_clamp(pcol[idx].color[1]);
- vcol->b = unit_float_to_ushort_clamp(pcol[idx].color[2]);
- vcol->a = unit_float_to_ushort_clamp(pcol[idx].color[3]);
- }
- }
- }
-
- /* Ensure data is uploaded properly. */
- GPU_vertbuf_tag_dirty(src_data);
- draw_subdiv_interp_custom_data(subdiv_cache, src_data, dst_buffer, 4, dst_offset, true);
- }
-
- GPU_vertbuf_discard(src_data);
-}
-
-constexpr MeshExtract create_extractor_vcol()
-{
- MeshExtract extractor = {nullptr};
- extractor.init = extract_vcol_init;
- extractor.init_subdiv = extract_vcol_init_subdiv;
- extractor.data_type = MR_DATA_NONE;
- extractor.data_size = 0;
- extractor.use_threading = false;
- extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.vcol);
- return extractor;
-}
-
-/** \} */
-
-} // namespace blender::draw
-
-const MeshExtract extract_vcol = blender::draw::create_extractor_vcol();
diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
index ba194a4b167..4db5a8c23a4 100644
--- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
+++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_weights.cc
@@ -8,6 +8,7 @@
#include "MEM_guardedalloc.h"
#include "BKE_deform.h"
+#include "BKE_mesh.h"
#include "draw_subdivision.h"
#include "extract_mesh.hh"
@@ -105,7 +106,7 @@ static void extract_weights_init(const MeshRenderData *mr,
data->cd_ofs = CustomData_get_offset(&mr->bm->vdata, CD_MDEFORMVERT);
}
else {
- data->dvert = (const MDeformVert *)CustomData_get_layer(&mr->me->vdata, CD_MDEFORMVERT);
+ data->dvert = mr->me->deform_verts().data();
data->cd_ofs = -1;
}
}
@@ -171,8 +172,9 @@ static void extract_weights_init_subdiv(const DRWSubdivCache *subdiv_cache,
extract_weights_init(mr, cache, coarse_weights, _data);
if (mr->extract_type != MR_EXTRACT_BMESH) {
- for (int i = 0; i < coarse_mesh->totpoly; i++) {
- const MPoly *mpoly = &coarse_mesh->mpoly[i];
+ const Span<MPoly> coarse_polys = coarse_mesh->polys();
+ for (const int i : coarse_polys.index_range()) {
+ const MPoly *mpoly = &coarse_polys[i];
extract_weights_iter_poly_mesh(mr, mpoly, i, _data);
}
}
diff --git a/source/blender/draw/intern/shaders/common_aabb_lib.glsl b/source/blender/draw/intern/shaders/common_aabb_lib.glsl
new file mode 100644
index 00000000000..b5f664a6779
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_aabb_lib.glsl
@@ -0,0 +1,59 @@
+
+#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
+
+/* ---------------------------------------------------------------------- */
+/** \name Axis Aligned Bound Box
+ * \{ */
+
+struct AABB {
+ vec3 min, max;
+};
+
+AABB aabb_init_min_max()
+{
+ AABB aabb;
+ aabb.min = vec3(1.0e30);
+ aabb.max = vec3(-1.0e30);
+ return aabb;
+}
+
+void aabb_merge(inout AABB aabb, vec3 v)
+{
+ aabb.min = min(aabb.min, v);
+ aabb.max = max(aabb.max, v);
+}
+
+/**
+ * Return true if there is any intersection.
+ */
+bool aabb_intersect(AABB a, AABB b)
+{
+ return all(greaterThanEqual(min(a.max, b.max), max(a.min, b.min)));
+}
+
+/**
+ * Compute intersect intersection volume of \a a and \a b.
+ * Return true if the resulting volume is not empty.
+ */
+bool aabb_clip(AABB a, AABB b, out AABB c)
+{
+ c.min = max(a.min, b.min);
+ c.max = min(a.max, b.max);
+ return all(greaterThanEqual(c.max, c.min));
+}
+
+Box aabb_to_box(AABB aabb)
+{
+ Box box;
+ box.corners[0] = aabb.min;
+ box.corners[1] = vec3(aabb.max.x, aabb.min.y, aabb.min.z);
+ box.corners[2] = vec3(aabb.max.x, aabb.max.y, aabb.min.z);
+ box.corners[3] = vec3(aabb.min.x, aabb.max.y, aabb.min.z);
+ box.corners[4] = vec3(aabb.min.x, aabb.min.y, aabb.max.z);
+ box.corners[5] = vec3(aabb.max.x, aabb.min.y, aabb.max.z);
+ box.corners[6] = aabb.max;
+ box.corners[7] = vec3(aabb.min.x, aabb.max.y, aabb.max.z);
+ return box;
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/common_attribute_lib.glsl b/source/blender/draw/intern/shaders/common_attribute_lib.glsl
index ce5e49c7f63..6b5b6fcc846 100644
--- a/source/blender/draw/intern/shaders/common_attribute_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_attribute_lib.glsl
@@ -25,3 +25,4 @@ float attr_load_float(sampler3D tex);
float attr_load_temperature_post(float attr);
vec4 attr_load_color_post(vec4 attr);
+vec4 attr_load_uniform(vec4 attr, const uint attr_hash);
diff --git a/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl
new file mode 100644
index 00000000000..3287897e73c
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl
@@ -0,0 +1,215 @@
+
+/**
+ * Debugging drawing library
+ *
+ * Quick way to draw debug geometry. All input should be in world space and
+ * will be rendered in the default view. No additional setup required.
+ **/
+
+/** Global switch option. */
+bool drw_debug_draw_enable = true;
+const vec4 drw_debug_default_color = vec4(1.0, 0.0, 0.0, 1.0);
+
+/* -------------------------------------------------------------------- */
+/** \name Internals
+ * \{ */
+
+uint drw_debug_start_draw(uint v_needed)
+{
+ uint vertid = atomicAdd(drw_debug_draw_v_count, v_needed);
+ vertid += drw_debug_draw_offset;
+ return vertid;
+}
+
+uint drw_debug_color_pack(vec4 color)
+{
+ color = clamp(color, 0.0, 1.0);
+ uint result = 0;
+ result |= uint(color.x * 255.0) << 0u;
+ result |= uint(color.y * 255.0) << 8u;
+ result |= uint(color.z * 255.0) << 16u;
+ result |= uint(color.w * 255.0) << 24u;
+ return result;
+}
+
+void drw_debug_line(inout uint vertid, vec3 v1, vec3 v2, uint color)
+{
+ drw_debug_verts_buf[vertid++] = DRWDebugVert(
+ floatBitsToUint(v1.x), floatBitsToUint(v1.y), floatBitsToUint(v1.z), color);
+ drw_debug_verts_buf[vertid++] = DRWDebugVert(
+ floatBitsToUint(v2.x), floatBitsToUint(v2.y), floatBitsToUint(v2.z), color);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name API
+ * \{ */
+
+/**
+ * Draw a line.
+ */
+void drw_debug_line(vec3 v1, vec3 v2, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ const uint v_needed = 2;
+ uint vertid = drw_debug_start_draw(v_needed);
+ if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
+ drw_debug_line(vertid, v1, v2, drw_debug_color_pack(color));
+ }
+}
+void drw_debug_line(vec3 v1, vec3 v2)
+{
+ drw_debug_line(v1, v2, drw_debug_default_color);
+}
+
+/**
+ * Draw a quad contour.
+ */
+void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ const uint v_needed = 8;
+ uint vertid = drw_debug_start_draw(v_needed);
+ if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
+ uint pcolor = drw_debug_color_pack(color);
+ drw_debug_line(vertid, v1, v2, pcolor);
+ drw_debug_line(vertid, v2, v3, pcolor);
+ drw_debug_line(vertid, v3, v4, pcolor);
+ drw_debug_line(vertid, v4, v1, pcolor);
+ }
+}
+void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4)
+{
+ drw_debug_quad(v1, v2, v3, v4, drw_debug_default_color);
+}
+
+/**
+ * Draw a point as octahedron wireframe.
+ */
+void drw_debug_point(vec3 p, float radius, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ vec3 c = vec3(radius, -radius, 0);
+ vec3 v1 = p + c.xzz;
+ vec3 v2 = p + c.zxz;
+ vec3 v3 = p + c.yzz;
+ vec3 v4 = p + c.zyz;
+ vec3 v5 = p + c.zzx;
+ vec3 v6 = p + c.zzy;
+
+ const uint v_needed = 12 * 2;
+ uint vertid = drw_debug_start_draw(v_needed);
+ if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
+ uint pcolor = drw_debug_color_pack(color);
+ drw_debug_line(vertid, v1, v2, pcolor);
+ drw_debug_line(vertid, v2, v3, pcolor);
+ drw_debug_line(vertid, v3, v4, pcolor);
+ drw_debug_line(vertid, v4, v1, pcolor);
+ drw_debug_line(vertid, v1, v5, pcolor);
+ drw_debug_line(vertid, v2, v5, pcolor);
+ drw_debug_line(vertid, v3, v5, pcolor);
+ drw_debug_line(vertid, v4, v5, pcolor);
+ drw_debug_line(vertid, v1, v6, pcolor);
+ drw_debug_line(vertid, v2, v6, pcolor);
+ drw_debug_line(vertid, v3, v6, pcolor);
+ drw_debug_line(vertid, v4, v6, pcolor);
+ }
+}
+void drw_debug_point(vec3 p, float radius)
+{
+ drw_debug_point(p, radius, drw_debug_default_color);
+}
+void drw_debug_point(vec3 p)
+{
+ drw_debug_point(p, 0.01);
+}
+
+/**
+ * Draw a sphere wireframe as 3 axes circle.
+ */
+void drw_debug_sphere(vec3 p, float radius, vec4 color)
+{
+ if (!drw_debug_draw_enable) {
+ return;
+ }
+ const int circle_resolution = 16;
+ const uint v_needed = circle_resolution * 2 * 3;
+ uint vertid = drw_debug_start_draw(v_needed);
+ if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) {
+ uint pcolor = drw_debug_color_pack(color);
+ for (int axis = 0; axis < 3; axis++) {
+ for (int edge = 0; edge < circle_resolution; edge++) {
+ float angle1 = (2.0 * 3.141592) * float(edge + 0) / float(circle_resolution);
+ vec3 p1 = vec3(cos(angle1), sin(angle1), 0.0) * radius;
+ p1 = vec3(p1[(0 + axis) % 3], p1[(1 + axis) % 3], p1[(2 + axis) % 3]);
+
+ float angle2 = (2.0 * 3.141592) * float(edge + 1) / float(circle_resolution);
+ vec3 p2 = vec3(cos(angle2), sin(angle2), 0.0) * radius;
+ p2 = vec3(p2[(0 + axis) % 3], p2[(1 + axis) % 3], p2[(2 + axis) % 3]);
+
+ drw_debug_line(vertid, p + p1, p + p2, pcolor);
+ }
+ }
+ }
+}
+void drw_debug_sphere(vec3 p, float radius)
+{
+ drw_debug_sphere(p, radius, drw_debug_default_color);
+}
+
+/**
+ * Draw a matrix transformation as 3 colored axes.
+ */
+void drw_debug_matrix(mat4 mat, vec4 color)
+{
+ vec4 p[4] = vec4[4](vec4(0, 0, 0, 1), vec4(1, 0, 0, 1), vec4(0, 1, 0, 1), vec4(0, 0, 1, 1));
+ for (int i = 0; i < 4; i++) {
+ p[i] = mat * p[i];
+ p[i].xyz /= p[i].w;
+ }
+ drw_debug_line(p[0].xyz, p[0].xyz, vec4(1, 0, 0, 1));
+ drw_debug_line(p[0].xyz, p[1].xyz, vec4(0, 1, 0, 1));
+ drw_debug_line(p[0].xyz, p[2].xyz, vec4(0, 0, 1, 1));
+}
+void drw_debug_matrix(mat4 mat)
+{
+ drw_debug_matrix(mat, drw_debug_default_color);
+}
+
+/**
+ * Draw a matrix as a 2 units length bounding box, centered on origin.
+ */
+void drw_debug_matrix_as_bbox(mat4 mat, vec4 color)
+{
+ vec4 p[8] = vec4[8](vec4(-1, -1, -1, 1),
+ vec4(1, -1, -1, 1),
+ vec4(1, 1, -1, 1),
+ vec4(-1, 1, -1, 1),
+ vec4(-1, -1, 1, 1),
+ vec4(1, -1, 1, 1),
+ vec4(1, 1, 1, 1),
+ vec4(-1, 1, 1, 1));
+ for (int i = 0; i < 8; i++) {
+ p[i] = mat * p[i];
+ p[i].xyz /= p[i].w;
+ }
+ drw_debug_quad(p[0].xyz, p[1].xyz, p[2].xyz, p[3].xyz, color);
+ drw_debug_line(p[0].xyz, p[4].xyz, color);
+ drw_debug_line(p[1].xyz, p[5].xyz, color);
+ drw_debug_line(p[2].xyz, p[6].xyz, color);
+ drw_debug_line(p[3].xyz, p[7].xyz, color);
+ drw_debug_quad(p[4].xyz, p[5].xyz, p[6].xyz, p[7].xyz, color);
+}
+void drw_debug_matrix_as_bbox(mat4 mat)
+{
+ drw_debug_matrix_as_bbox(mat, drw_debug_default_color);
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl
new file mode 100644
index 00000000000..89d1729b52d
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl
@@ -0,0 +1,388 @@
+
+/**
+ * Debug print implementation for shaders.
+ *
+ * `print()`:
+ * Log variable or strings inside the viewport.
+ * Using a unique non string argument will print the variable name with it.
+ * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`.
+ * `drw_print_no_endl()`:
+ * Same as `print()` but does not finish the line.
+ * `drw_print_value()`:
+ * Display only the value of a variable. Does not finish the line.
+ * `drw_print_value_hex()`:
+ * Display only the hex representation of a variable. Does not finish the line.
+ * `drw_print_value_binary()`: Display only the binary representation of a
+ * variable. Does not finish the line.
+ *
+ * IMPORTANT: As it is now, it is not yet thread safe. Only print from one thread. You can use the
+ * IS_DEBUG_MOUSE_FRAGMENT macro in fragment shader to filter using mouse position or
+ * IS_FIRST_INVOCATION in compute shaders.
+ *
+ * NOTE: Floating point representation might not be very precise (see drw_print_value(float)).
+ *
+ * IMPORTANT: Multipler drawcalls can write to the buffer in sequence (if they are from different
+ * shgroups). However, we add barriers to support this case and it might change the application
+ * behavior. Uncomment DISABLE_DEBUG_SHADER_drw_print_BARRIER to remove the barriers if that
+ * happens. But then you are limited to a single invocation output.
+ *
+ * IMPORTANT: All of these are copied to the CPU debug libs (draw_debug.cc). They need to be kept
+ * in sync to write the same data.
+ */
+
+/** Global switch option when you want to silence all prints from all shaders at once. */
+bool drw_debug_print_enable = true;
+
+/* Set drw_print_col to max value so we will start by creating a new line and get the correct
+ * threadsafe row. */
+uint drw_print_col = DRW_DEBUG_PRINT_WORD_WRAP_COLUMN;
+uint drw_print_row = 0u;
+
+void drw_print_newline()
+{
+ if (!drw_debug_print_enable) {
+ return;
+ }
+ drw_print_col = 0u;
+ drw_print_row = atomicAdd(drw_debug_print_row_shared, 1u) + 1u;
+}
+
+void drw_print_string_start(uint len)
+{
+ if (!drw_debug_print_enable) {
+ return;
+ }
+ /* Break before word. */
+ if (drw_print_col + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
+ drw_print_newline();
+ }
+}
+
+void drw_print_char4(uint data)
+{
+ if (!drw_debug_print_enable) {
+ return;
+ }
+ /* Convert into char stream. */
+ for (; data != 0u; data >>= 8u) {
+ uint char1 = data & 0xFFu;
+ /* Check for null terminator. */
+ if (char1 == 0x00) {
+ break;
+ }
+ uint cursor = atomicAdd(drw_debug_print_cursor, 1u);
+ cursor += drw_debug_print_offset;
+ if (cursor < DRW_DEBUG_PRINT_MAX) {
+ /* For future usage. (i.e: Color) */
+ uint flags = 0u;
+ uint col = drw_print_col++;
+ uint drw_print_header = (flags << 24u) | (drw_print_row << 16u) | (col << 8u);
+ drw_debug_print_buf[cursor] = drw_print_header | char1;
+ /* Break word. */
+ if (drw_print_col > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) {
+ drw_print_newline();
+ }
+ }
+ }
+}
+
+/**
+ * NOTE(fclem): Strange behavior emerge when trying to increment the digit
+ * counter inside the append function. It looks like the compiler does not see
+ * it is referenced as an index for char4 and thus do not capture the right
+ * reference. I do not know if this is undefined behavior. As a matter of
+ * precaution, we implement all the append function separately. This behavior
+ * was observed on both Mesa & amdgpu-pro.
+ */
+/* Using ascii char code. Expect char1 to be less or equal to 0xFF. Appends chars to the right. */
+void drw_print_append_char(uint char1, inout uint char4)
+{
+ char4 = (char4 << 8u) | char1;
+}
+
+void drw_print_append_digit(uint digit, inout uint char4)
+{
+ const uint char_A = 0x41u;
+ const uint char_0 = 0x30u;
+ bool is_hexadecimal = digit > 9u;
+ char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit));
+}
+
+void drw_print_append_space(inout uint char4)
+{
+ char4 = (char4 << 8u) | 0x20u;
+}
+
+void drw_print_value_binary(uint value)
+{
+ drw_print_no_endl("0b");
+ drw_print_string_start(10u * 4u);
+ uint digits[10] = uint[10](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u);
+ uint digit = 0u;
+ for (uint i = 0u; i < 32u; i++) {
+ drw_print_append_digit(((value >> i) & 1u), digits[digit / 4u]);
+ digit++;
+ if ((i % 4u) == 3u) {
+ drw_print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 9; j >= 0; j--) {
+ drw_print_char4(digits[j]);
+ }
+}
+
+void drw_print_value_binary(int value)
+{
+ drw_print_value_binary(uint(value));
+}
+
+void drw_print_value_binary(float value)
+{
+ drw_print_value_binary(floatBitsToUint(value));
+}
+
+void drw_print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned)
+{
+ drw_print_string_start(3u * 4u);
+ const uint blank_value = hex ? 0x30303030u : 0x20202020u;
+ const uint prefix = hex ? 0x78302020u : 0x20202020u;
+ uint digits[3] = uint[3](blank_value, blank_value, prefix);
+ const uint base = hex ? 16u : 10u;
+ uint digit = 0u;
+ /* Add `u` suffix. */
+ if (is_unsigned) {
+ drw_print_append_char('u', digits[digit / 4u]);
+ digit++;
+ }
+ /* Number's digits. */
+ for (; value != 0u || digit == uint(is_unsigned); value /= base) {
+ drw_print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Add negative sign. */
+ if (is_negative) {
+ drw_print_append_char('-', digits[digit / 4u]);
+ digit++;
+ }
+ /* Need to pad to uint alignment because we are issuing chars in "reverse". */
+ for (uint i = digit % 4u; i < 4u && i > 0u; i++) {
+ drw_print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 2; j >= 0; j--) {
+ drw_print_char4(digits[j]);
+ }
+}
+
+void drw_print_value_hex(uint value)
+{
+ drw_print_value_uint(value, true, false, false);
+}
+
+void drw_print_value_hex(int value)
+{
+ drw_print_value_uint(uint(value), true, false, false);
+}
+
+void drw_print_value_hex(float value)
+{
+ drw_print_value_uint(floatBitsToUint(value), true, false, false);
+}
+
+void drw_print_value(uint value)
+{
+ drw_print_value_uint(value, false, false, true);
+}
+
+void drw_print_value(int value)
+{
+ drw_print_value_uint(uint(abs(value)), false, (value < 0), false);
+}
+
+void drw_print_value(bool value)
+{
+ if (value) {
+ drw_print_no_endl("true ");
+ }
+ else {
+ drw_print_no_endl("false");
+ }
+}
+
+/* NOTE(@fclem): This is homebrew and might not be 100% accurate (accuracy has
+ * not been tested and might dependent on compiler implementation). If unsure,
+ * use drw_print_value_hex and transcribe the value manually with another tool. */
+void drw_print_value(float val)
+{
+ /* We pad the string to match normal float values length. */
+ if (isnan(val)) {
+ drw_print_no_endl(" NaN");
+ return;
+ }
+ if (isinf(val)) {
+ if (sign(val) < 0.0) {
+ drw_print_no_endl(" -Inf");
+ }
+ else {
+ drw_print_no_endl(" Inf");
+ }
+ return;
+ }
+
+ /* Adjusted for significant digits (6) with sign (1), decimal separator (1)
+ * and exponent (4). */
+ const float significant_digits = 6.0;
+ drw_print_string_start(3u * 4u);
+ uint digits[3] = uint[3](0x20202020u, 0x20202020u, 0x20202020u);
+
+ float exponent = floor(log(abs(val)) / log(10.0));
+ bool display_exponent = exponent >= (significant_digits) ||
+ exponent <= (-significant_digits + 1.0);
+
+ float int_significant_digits = min(exponent + 1.0, significant_digits);
+ float dec_significant_digits = max(0.0, significant_digits - int_significant_digits);
+ /* Power to get to the rounding point. */
+ float rounding_power = dec_significant_digits;
+
+ if (val == 0.0 || isinf(exponent)) {
+ display_exponent = false;
+ int_significant_digits = dec_significant_digits = 1.0;
+ }
+ /* Remap to keep significant numbers count. */
+ if (display_exponent) {
+ int_significant_digits = 1.0;
+ dec_significant_digits = significant_digits - int_significant_digits;
+ rounding_power = -exponent + dec_significant_digits;
+ }
+ /* Round at the last significant digit. */
+ val = round(val * pow(10.0, rounding_power));
+ /* Get back to final exponent. */
+ val *= pow(10.0, -dec_significant_digits);
+
+ float int_part;
+ float dec_part = modf(val, int_part);
+
+ dec_part *= pow(10.0, dec_significant_digits);
+
+ const uint base = 10u;
+ uint digit = 0u;
+ /* Exponent */
+ uint value = uint(abs(exponent));
+ if (display_exponent) {
+ for (int i = 0; value != 0u || i == 0; i++, value /= base) {
+ drw_print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Exponent sign. */
+ uint sign_char = (exponent < 0.0) ? '-' : '+';
+ drw_print_append_char(sign_char, digits[digit / 4u]);
+ digit++;
+ /* Exponent `e` suffix. */
+ drw_print_append_char(0x65u, digits[digit / 4u]);
+ digit++;
+ }
+ /* Decimal part. */
+ value = uint(abs(dec_part));
+#if 0 /* We don't do that because it makes unstable values really hard to \
+ read. */
+ /* Trim trailing zeros. */
+ while ((value % base) == 0u) {
+ value /= base;
+ if (value == 0u) {
+ break;
+ }
+ }
+#endif
+ if (value != 0u) {
+ for (int i = 0; value != 0u || i == 0; i++, value /= base) {
+ drw_print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Point separator. */
+ drw_print_append_char('.', digits[digit / 4u]);
+ digit++;
+ }
+ /* Integer part. */
+ value = uint(abs(int_part));
+ for (int i = 0; value != 0u || i == 0; i++, value /= base) {
+ drw_print_append_digit(value % base, digits[digit / 4u]);
+ digit++;
+ }
+ /* Negative sign. */
+ if (val < 0.0) {
+ drw_print_append_char('-', digits[digit / 4u]);
+ digit++;
+ }
+ /* Need to pad to uint alignment because we are issuing chars in "reverse". */
+ for (uint i = digit % 4u; i < 4u && i > 0u; i++) {
+ drw_print_append_space(digits[digit / 4u]);
+ digit++;
+ }
+ /* Numbers are written from right to left. So we need to reverse the order. */
+ for (int j = 2; j >= 0; j--) {
+ drw_print_char4(digits[j]);
+ }
+}
+
+void drw_print_value(vec2 value)
+{
+ drw_print_no_endl("vec2(", value[0], ", ", value[1], ")");
+}
+
+void drw_print_value(vec3 value)
+{
+ drw_print_no_endl("vec3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+
+void drw_print_value(vec4 value)
+{
+ drw_print_no_endl("vec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+void drw_print_value(ivec2 value)
+{
+ drw_print_no_endl("ivec2(", value[0], ", ", value[1], ")");
+}
+
+void drw_print_value(ivec3 value)
+{
+ drw_print_no_endl("ivec3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+
+void drw_print_value(ivec4 value)
+{
+ drw_print_no_endl("ivec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+void drw_print_value(uvec2 value)
+{
+ drw_print_no_endl("uvec2(", value[0], ", ", value[1], ")");
+}
+
+void drw_print_value(uvec3 value)
+{
+ drw_print_no_endl("uvec3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+
+void drw_print_value(uvec4 value)
+{
+ drw_print_no_endl("uvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
+
+void drw_print_value(bvec2 value)
+{
+ drw_print_no_endl("bvec2(", value[0], ", ", value[1], ")");
+}
+
+void drw_print_value(bvec3 value)
+{
+ drw_print_no_endl("bvec3(", value[0], ", ", value[1], ", ", value[1], ")");
+}
+
+void drw_print_value(bvec4 value)
+{
+ drw_print_no_endl("bvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")");
+}
diff --git a/source/blender/draw/intern/shaders/common_debug_shape_lib.glsl b/source/blender/draw/intern/shaders/common_debug_shape_lib.glsl
new file mode 100644
index 00000000000..538c55ce544
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_debug_shape_lib.glsl
@@ -0,0 +1,57 @@
+
+/**
+ * Debug drawing of shapes.
+ */
+
+#pragma BLENDER_REQUIRE(common_debug_draw_lib.glsl)
+#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
+
+void drw_debug(Box shape, vec4 color)
+{
+ drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color);
+ drw_debug_line(shape.corners[0], shape.corners[4], color);
+ drw_debug_line(shape.corners[1], shape.corners[5], color);
+ drw_debug_line(shape.corners[2], shape.corners[6], color);
+ drw_debug_line(shape.corners[3], shape.corners[7], color);
+ drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color);
+}
+void drw_debug(Box shape)
+{
+ drw_debug(shape, drw_debug_default_color);
+}
+
+void drw_debug(Frustum shape, vec4 color)
+{
+ drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color);
+ drw_debug_line(shape.corners[0], shape.corners[4], color);
+ drw_debug_line(shape.corners[1], shape.corners[5], color);
+ drw_debug_line(shape.corners[2], shape.corners[6], color);
+ drw_debug_line(shape.corners[3], shape.corners[7], color);
+ drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color);
+}
+void drw_debug(Frustum shape)
+{
+ drw_debug(shape, drw_debug_default_color);
+}
+
+void drw_debug(Pyramid shape, vec4 color)
+{
+ drw_debug_line(shape.corners[0], shape.corners[1], color);
+ drw_debug_line(shape.corners[0], shape.corners[2], color);
+ drw_debug_line(shape.corners[0], shape.corners[3], color);
+ drw_debug_line(shape.corners[0], shape.corners[4], color);
+ drw_debug_quad(shape.corners[1], shape.corners[2], shape.corners[3], shape.corners[4], color);
+}
+void drw_debug(Pyramid shape)
+{
+ drw_debug(shape, drw_debug_default_color);
+}
+
+void drw_debug(Sphere shape, vec4 color)
+{
+ drw_debug_sphere(shape.center, shape.radius, color);
+}
+void drw_debug(Sphere shape)
+{
+ drw_debug(shape, drw_debug_default_color);
+}
diff --git a/source/blender/draw/intern/shaders/common_intersect_lib.glsl b/source/blender/draw/intern/shaders/common_intersect_lib.glsl
new file mode 100644
index 00000000000..83223f89277
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_intersect_lib.glsl
@@ -0,0 +1,466 @@
+
+/**
+ * Intersection library used for culling.
+ * Results are meant to be conservative.
+ */
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+#pragma BLENDER_REQUIRE(common_shape_lib.glsl)
+
+/* ---------------------------------------------------------------------- */
+/** \name Plane extraction functions.
+ * \{ */
+
+/** \a v1 and \a v2 are vectors on the plane. \a p is a point on the plane. */
+vec4 isect_plane_setup(vec3 p, vec3 v1, vec3 v2)
+{
+ vec3 normal_to_plane = normalize(cross(v1, v2));
+ return vec4(normal_to_plane, -dot(normal_to_plane, p));
+}
+
+struct IsectPyramid {
+ vec3 corners[5];
+ vec4 planes[5];
+};
+
+IsectPyramid isect_data_setup(Pyramid shape)
+{
+ vec3 A1 = shape.corners[1] - shape.corners[0];
+ vec3 A2 = shape.corners[2] - shape.corners[0];
+ vec3 A3 = shape.corners[3] - shape.corners[0];
+ vec3 A4 = shape.corners[4] - shape.corners[0];
+ vec3 S4 = shape.corners[4] - shape.corners[1];
+ vec3 S2 = shape.corners[2] - shape.corners[1];
+
+ IsectPyramid data;
+ data.planes[0] = isect_plane_setup(shape.corners[0], A2, A1);
+ data.planes[1] = isect_plane_setup(shape.corners[0], A3, A2);
+ data.planes[2] = isect_plane_setup(shape.corners[0], A4, A3);
+ data.planes[3] = isect_plane_setup(shape.corners[0], A1, A4);
+ data.planes[4] = isect_plane_setup(shape.corners[1], S2, S4);
+ for (int i = 0; i < 5; i++) {
+ data.corners[i] = shape.corners[i];
+ }
+ return data;
+}
+
+struct IsectBox {
+ vec3 corners[8];
+ vec4 planes[6];
+};
+
+IsectBox isect_data_setup(Box shape)
+{
+ vec3 A1 = shape.corners[1] - shape.corners[0];
+ vec3 A3 = shape.corners[3] - shape.corners[0];
+ vec3 A4 = shape.corners[4] - shape.corners[0];
+
+ IsectBox data;
+ data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1);
+ data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3);
+ data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4);
+ /* Assumes that the box is actually a box! */
+ data.planes[3] = vec4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, shape.corners[6]));
+ data.planes[4] = vec4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, shape.corners[6]));
+ data.planes[5] = vec4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, shape.corners[6]));
+ for (int i = 0; i < 8; i++) {
+ data.corners[i] = shape.corners[i];
+ }
+ return data;
+}
+
+/* Construct box from 1 corner point + 3 side vectors. */
+IsectBox isect_data_setup(vec3 origin, vec3 side_x, vec3 side_y, vec3 side_z)
+{
+ IsectBox data;
+ data.corners[0] = origin;
+ data.corners[1] = origin + side_x;
+ data.corners[2] = origin + side_y + side_x;
+ data.corners[3] = origin + side_y;
+ data.corners[4] = data.corners[0] + side_z;
+ data.corners[5] = data.corners[1] + side_z;
+ data.corners[6] = data.corners[2] + side_z;
+ data.corners[7] = data.corners[3] + side_z;
+
+ data.planes[0] = isect_plane_setup(data.corners[0], side_y, side_z);
+ data.planes[1] = isect_plane_setup(data.corners[0], side_x, side_y);
+ data.planes[2] = isect_plane_setup(data.corners[0], side_z, side_x);
+ /* Assumes that the box is actually a box! */
+ data.planes[3] = vec4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, data.corners[6]));
+ data.planes[4] = vec4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, data.corners[6]));
+ data.planes[5] = vec4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, data.corners[6]));
+
+ return data;
+}
+
+struct IsectFrustum {
+ vec3 corners[8];
+ vec4 planes[6];
+};
+
+IsectFrustum isect_data_setup(Frustum shape)
+{
+ vec3 A1 = shape.corners[1] - shape.corners[0];
+ vec3 A3 = shape.corners[3] - shape.corners[0];
+ vec3 A4 = shape.corners[4] - shape.corners[0];
+ vec3 B5 = shape.corners[5] - shape.corners[6];
+ vec3 B7 = shape.corners[7] - shape.corners[6];
+ vec3 B2 = shape.corners[2] - shape.corners[6];
+
+ IsectFrustum data;
+ data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1);
+ data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3);
+ data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4);
+ data.planes[3] = isect_plane_setup(shape.corners[6], B7, B5);
+ data.planes[4] = isect_plane_setup(shape.corners[6], B5, B2);
+ data.planes[5] = isect_plane_setup(shape.corners[6], B2, B7);
+ for (int i = 0; i < 8; i++) {
+ data.corners[i] = shape.corners[i];
+ }
+ return data;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name View Intersection functions.
+ * \{ */
+
+bool intersect_view(Pyramid pyramid)
+{
+ bool intersects = true;
+
+ /* Do Pyramid vertices vs Frustum planes. */
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 5; ++v) {
+ float test = dot(drw_view.frustum_planes[p], vec4(pyramid.corners[v], 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ if (!intersects) {
+ return intersects;
+ }
+
+ /* Now do Frustum vertices vs Pyramid planes. */
+ IsectPyramid i_pyramid = isect_data_setup(pyramid);
+ for (int p = 0; p < 5; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(i_pyramid.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+ return intersects;
+}
+
+bool intersect_view(Box box)
+{
+ bool intersects = true;
+
+ /* Do Box vertices vs Frustum planes. */
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(drw_view.frustum_planes[p], vec4(box.corners[v], 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ if (!intersects) {
+ return intersects;
+ }
+
+ /* Now do Frustum vertices vs Box planes. */
+ IsectBox i_box = isect_data_setup(box);
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(i_box.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ return intersects;
+}
+
+bool intersect_view(IsectBox i_box)
+{
+ bool intersects = true;
+
+ /* Do Box vertices vs Frustum planes. */
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(drw_view.frustum_planes[p], vec4(i_box.corners[v], 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ if (!intersects) {
+ return intersects;
+ }
+
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(i_box.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ return intersects;
+}
+
+bool intersect_view(Sphere sphere)
+{
+ bool intersects = true;
+
+ for (int p = 0; p < 6 && intersects; ++p) {
+ float dist_to_plane = dot(drw_view.frustum_planes[p], vec4(sphere.center, 1.0));
+ if (dist_to_plane < -sphere.radius) {
+ intersects = false;
+ }
+ }
+ /* TODO reject false positive. */
+ return intersects;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Shape vs. Shape Intersection functions.
+ * \{ */
+
+bool intersect(IsectPyramid i_pyramid, Box box)
+{
+ bool intersects = true;
+
+ /* Do Box vertices vs Pyramid planes. */
+ for (int p = 0; p < 5; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(i_pyramid.planes[p], vec4(box.corners[v], 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ if (!intersects) {
+ return intersects;
+ }
+
+ /* Now do Pyramid vertices vs Box planes. */
+ IsectBox i_box = isect_data_setup(box);
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 5; ++v) {
+ float test = dot(i_box.planes[p], vec4(i_pyramid.corners[v], 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+ return intersects;
+}
+
+bool intersect(IsectFrustum i_frustum, Pyramid pyramid)
+{
+ bool intersects = true;
+
+ /* Do Pyramid vertices vs Frustum planes. */
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 5; ++v) {
+ float test = dot(i_frustum.planes[p], vec4(pyramid.corners[v], 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ if (!intersects) {
+ return intersects;
+ }
+
+ /* Now do Frustum vertices vs Pyramid planes. */
+ IsectPyramid i_pyramid = isect_data_setup(pyramid);
+ for (int p = 0; p < 5; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(i_pyramid.planes[p], vec4(i_frustum.corners[v].xyz, 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+ return intersects;
+}
+
+bool intersect(IsectFrustum i_frustum, Box box)
+{
+ bool intersects = true;
+
+ /* Do Box vertices vs Frustum planes. */
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(i_frustum.planes[p], vec4(box.corners[v], 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ if (!intersects) {
+ return intersects;
+ }
+
+ /* Now do Frustum vertices vs Box planes. */
+ IsectBox i_box = isect_data_setup(box);
+ for (int p = 0; p < 6; ++p) {
+ bool is_any_vertex_on_positive_side = false;
+ for (int v = 0; v < 8; ++v) {
+ float test = dot(i_box.planes[p], vec4(i_frustum.corners[v].xyz, 1.0));
+ if (test > 0.0) {
+ is_any_vertex_on_positive_side = true;
+ break;
+ }
+ }
+ bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side;
+ if (all_vertex_on_negative_side) {
+ intersects = false;
+ break;
+ }
+ }
+
+ return intersects;
+}
+
+bool intersect(IsectFrustum i_frustum, Sphere sphere)
+{
+ bool intersects = true;
+ for (int p = 0; p < 6; ++p) {
+ float dist_to_plane = dot(i_frustum.planes[p], vec4(sphere.center, 1.0));
+ if (dist_to_plane < -sphere.radius) {
+ intersects = false;
+ break;
+ }
+ }
+ return intersects;
+}
+
+bool intersect(Cone cone, Sphere sphere)
+{
+ /**
+ * Following "Improve Tile-based Light Culling with Spherical-sliced Cone"
+ * by Eric Zhang
+ * https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html
+ */
+ float sphere_distance = length(sphere.center);
+ float sphere_distance_rcp = safe_rcp(sphere_distance);
+ float sphere_sin = saturate(sphere.radius * sphere_distance_rcp);
+ float sphere_cos = sqrt(1.0 - sphere_sin * sphere_sin);
+ float cone_aperture_sin = sqrt(1.0 - cone.angle_cos * cone.angle_cos);
+
+ float cone_sphere_center_cos = dot(sphere.center * sphere_distance_rcp, cone.direction);
+ /* cos(A+B) = cos(A) * cos(B) - sin(A) * sin(B). */
+ float cone_sphere_angle_sum_cos = (sphere.radius > sphere_distance) ?
+ -1.0 :
+ (cone.angle_cos * sphere_cos -
+ cone_aperture_sin * sphere_sin);
+ /* Comparing cosines instead of angles since we are interested
+ * only in the monotonic region [0 .. M_PI / 2]. This saves costly acos() calls. */
+ bool intersects = (cone_sphere_center_cos >= cone_sphere_angle_sum_cos);
+
+ return intersects;
+}
+
+bool intersect(Circle circle_a, Circle circle_b)
+{
+ return distance_squared(circle_a.center, circle_b.center) <
+ sqr(circle_a.radius + circle_b.radius);
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
index 6d4452c18c8..71460c39285 100644
--- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl
@@ -5,63 +5,88 @@
/** \name Math intersection & projection functions.
* \{ */
-float point_plane_projection_dist(vec3 lineorigin, vec3 planeorigin, vec3 planenormal)
+vec4 plane_from_quad(vec3 v0, vec3 v1, vec3 v2, vec3 v3)
{
- return dot(planenormal, planeorigin - lineorigin);
+ vec3 nor = normalize(cross(v2 - v1, v0 - v1) + cross(v0 - v3, v2 - v3));
+ return vec4(nor, -dot(nor, v2));
}
-float line_plane_intersect_dist(vec3 lineorigin,
- vec3 linedirection,
- vec3 planeorigin,
- vec3 planenormal)
+vec4 plane_from_tri(vec3 v0, vec3 v1, vec3 v2)
{
- return dot(planenormal, planeorigin - lineorigin) / dot(planenormal, linedirection);
+ vec3 nor = normalize(cross(v2 - v1, v0 - v1));
+ return vec4(nor, -dot(nor, v2));
}
-float line_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec4 plane)
+float point_plane_projection_dist(vec3 line_origin, vec3 plane_origin, vec3 plane_normal)
+{
+ return dot(plane_normal, plane_origin - line_origin);
+}
+
+float point_line_projection_dist(vec2 point, vec2 line_origin, vec2 line_normal)
+{
+ return dot(line_normal, line_origin - point);
+}
+
+float line_plane_intersect_dist(vec3 line_origin,
+ vec3 line_direction,
+ vec3 plane_origin,
+ vec3 plane_normal)
+{
+ return dot(plane_normal, plane_origin - line_origin) / dot(plane_normal, line_direction);
+}
+
+float line_plane_intersect_dist(vec3 line_origin, vec3 line_direction, vec4 plane)
{
vec3 plane_co = plane.xyz * (-plane.w / len_squared(plane.xyz));
- vec3 h = lineorigin - plane_co;
- return -dot(plane.xyz, h) / dot(plane.xyz, linedirection);
+ vec3 h = line_origin - plane_co;
+ return -dot(plane.xyz, h) / dot(plane.xyz, line_direction);
}
-vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin, vec3 planenormal)
+vec3 line_plane_intersect(vec3 line_origin,
+ vec3 line_direction,
+ vec3 plane_origin,
+ vec3 plane_normal)
{
- float dist = line_plane_intersect_dist(lineorigin, linedirection, planeorigin, planenormal);
- return lineorigin + linedirection * dist;
+ float dist = line_plane_intersect_dist(line_origin, line_direction, plane_origin, plane_normal);
+ return line_origin + line_direction * dist;
}
-vec3 line_plane_intersect(vec3 lineorigin, vec3 linedirection, vec4 plane)
+vec3 line_plane_intersect(vec3 line_origin, vec3 line_direction, vec4 plane)
{
- float dist = line_plane_intersect_dist(lineorigin, linedirection, plane);
- return lineorigin + linedirection * dist;
+ float dist = line_plane_intersect_dist(line_origin, line_direction, plane);
+ return line_origin + line_direction * dist;
}
-float line_aligned_plane_intersect_dist(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
+float line_aligned_plane_intersect_dist(vec3 line_origin, vec3 line_direction, vec3 plane_origin)
{
/* aligned plane normal */
- vec3 L = planeorigin - lineorigin;
- float diskdist = length(L);
- vec3 planenormal = -normalize(L);
- return -diskdist / dot(planenormal, linedirection);
+ vec3 L = plane_origin - line_origin;
+ float disk_dist = length(L);
+ vec3 plane_normal = -normalize(L);
+ return -disk_dist / dot(plane_normal, line_direction);
}
-vec3 line_aligned_plane_intersect(vec3 lineorigin, vec3 linedirection, vec3 planeorigin)
+vec3 line_aligned_plane_intersect(vec3 line_origin, vec3 line_direction, vec3 plane_origin)
{
- float dist = line_aligned_plane_intersect_dist(lineorigin, linedirection, planeorigin);
+ float dist = line_aligned_plane_intersect_dist(line_origin, line_direction, plane_origin);
if (dist < 0) {
/* if intersection is behind we fake the intersection to be
* really far and (hopefully) not inside the radius of interest */
dist = 1e16;
}
- return lineorigin + linedirection * dist;
+ return line_origin + line_direction * dist;
}
-float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection)
+/**
+ * Returns intersection distance between the unit sphere and the line
+ * with the assumption that \a line_origin is contained in the unit sphere.
+ * It will always returns the farthest intersection.
+ */
+float line_unit_sphere_intersect_dist(vec3 line_origin, vec3 line_direction)
{
- float a = dot(linedirection, linedirection);
- float b = dot(linedirection, lineorigin);
- float c = dot(lineorigin, lineorigin) - 1;
+ float a = dot(line_direction, line_direction);
+ float b = dot(line_direction, line_origin);
+ float c = dot(line_origin, line_origin) - 1;
float dist = 1e15;
float determinant = b * b - a * c;
@@ -72,22 +97,63 @@ float line_unit_sphere_intersect_dist(vec3 lineorigin, vec3 linedirection)
return dist;
}
-float line_unit_box_intersect_dist(vec3 lineorigin, vec3 linedirection)
+/**
+ * Returns minimum intersection distance between the unit box and the line
+ * with the assumption that \a line_origin is contained in the unit box.
+ * In other words, it will always returns the farthest intersection.
+ */
+float line_unit_box_intersect_dist(vec3 line_origin, vec3 line_direction)
{
/* https://seblagarde.wordpress.com/2012/09/29/image-based-lighting-approaches-and-parallax-corrected-cubemap/
*/
- vec3 firstplane = (vec3(1.0) - lineorigin) / linedirection;
- vec3 secondplane = (vec3(-1.0) - lineorigin) / linedirection;
- vec3 furthestplane = max(firstplane, secondplane);
+ vec3 first_plane = (vec3(1.0) - line_origin) / line_direction;
+ vec3 second_plane = (vec3(-1.0) - line_origin) / line_direction;
+ vec3 farthest_plane = max(first_plane, second_plane);
+
+ return min_v3(farthest_plane);
+}
+
+float line_unit_box_intersect_dist_safe(vec3 line_origin, vec3 line_direction)
+{
+ vec3 safe_line_direction = max(vec3(1e-8), abs(line_direction)) *
+ select(vec3(1.0), -vec3(1.0), lessThan(line_direction, vec3(0.0)));
+ return line_unit_box_intersect_dist(line_origin, safe_line_direction);
+}
+
+/**
+ * Same as line_unit_box_intersect_dist but for 2D case.
+ */
+float line_unit_square_intersect_dist(vec2 line_origin, vec2 line_direction)
+{
+ vec2 first_plane = (vec2(1.0) - line_origin) / line_direction;
+ vec2 second_plane = (vec2(-1.0) - line_origin) / line_direction;
+ vec2 farthest_plane = max(first_plane, second_plane);
- return min_v3(furthestplane);
+ return min_v2(farthest_plane);
}
-float line_unit_box_intersect_dist_safe(vec3 lineorigin, vec3 linedirection)
+float line_unit_square_intersect_dist_safe(vec2 line_origin, vec2 line_direction)
{
- vec3 safe_linedirection = max(vec3(1e-8), abs(linedirection)) *
- select(vec3(1.0), -vec3(1.0), lessThan(linedirection, vec3(0.0)));
- return line_unit_box_intersect_dist(lineorigin, safe_linedirection);
+ vec2 safe_line_direction = max(vec2(1e-8), abs(line_direction)) *
+ select(vec2(1.0), -vec2(1.0), lessThan(line_direction, vec2(0.0)));
+ return line_unit_square_intersect_dist(line_origin, safe_line_direction);
+}
+
+/**
+ * Returns clipping distance (intersection with the nearest plane) with the given axis-aligned
+ * bound box along \a line_direction.
+ * Safe even if \a line_direction is degenerate.
+ * It assumes that an intersection exists (i.e: that \a line_direction points towards the AABB).
+ */
+float line_aabb_clipping_dist(vec3 line_origin, vec3 line_direction, vec3 aabb_min, vec3 aabb_max)
+{
+ vec3 safe_dir = select(line_direction, vec3(1e-5), lessThan(abs(line_direction), vec3(1e-5)));
+ vec3 dir_inv = 1.0 / safe_dir;
+
+ vec3 first_plane = (aabb_min - line_origin) * dir_inv;
+ vec3 second_plane = (aabb_max - line_origin) * dir_inv;
+ vec3 nearest_plane = min(first_plane, second_plane);
+ return max_v3(nearest_plane);
}
/** \} */
@@ -98,8 +164,8 @@ float line_unit_box_intersect_dist_safe(vec3 lineorigin, vec3 linedirection)
void make_orthonormal_basis(vec3 N, out vec3 T, out vec3 B)
{
- vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
- T = normalize(cross(UpVector, N));
+ vec3 up_vector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
+ T = normalize(cross(up_vector, N));
B = cross(N, T);
}
diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl
index 51f3c890df8..5842df424be 100644
--- a/source/blender/draw/intern/shaders/common_math_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_math_lib.glsl
@@ -17,6 +17,7 @@
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
#define FLT_MAX 3.402823e+38
+#define FLT_MIN 1.175494e-38
vec3 mul(mat3 m, vec3 v)
{
@@ -116,8 +117,8 @@ bool flag_test(int flag, int val) { return (flag & val) != 0; }
void set_flag_from_test(inout uint value, bool test, uint flag) { if (test) { value |= flag; } else { value &= ~flag; } }
void set_flag_from_test(inout int value, bool test, int flag) { if (test) { value |= flag; } else { value &= ~flag; } }
-#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights)));
-#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights)));
+#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights)))
+#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights)))
/* clang-format on */
@@ -130,12 +131,17 @@ void set_flag_from_test(inout int value, bool test, int flag) { if (test) { valu
#define in_texture_range(texel, tex) \
(all(greaterThanEqual(texel, ivec2(0))) && all(lessThan(texel, textureSize(tex, 0).xy)))
-uint divide_ceil_u(uint visible_count, uint divisor)
+uint divide_ceil(uint visible_count, uint divisor)
{
return (visible_count + (divisor - 1u)) / divisor;
}
-int divide_ceil_i(int visible_count, int divisor)
+int divide_ceil(int visible_count, int divisor)
+{
+ return (visible_count + (divisor - 1)) / divisor;
+}
+
+ivec2 divide_ceil(ivec2 visible_count, ivec2 divisor)
{
return (visible_count + (divisor - 1)) / divisor;
}
diff --git a/source/blender/draw/intern/shaders/common_shape_lib.glsl b/source/blender/draw/intern/shaders/common_shape_lib.glsl
new file mode 100644
index 00000000000..f2c8bf0faaf
--- /dev/null
+++ b/source/blender/draw/intern/shaders/common_shape_lib.glsl
@@ -0,0 +1,202 @@
+
+#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl)
+
+/**
+ * Geometric shape structures.
+ * Some constructors might seems redundant but are here to make the API cleaner and
+ * allow for more than one constructor per type.
+ */
+
+/* ---------------------------------------------------------------------- */
+/** \name Circle
+ * \{ */
+
+struct Circle {
+ vec2 center;
+ float radius;
+};
+
+Circle shape_circle(vec2 center, float radius)
+{
+ return Circle(center, radius);
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Sphere
+ * \{ */
+
+struct Sphere {
+ vec3 center;
+ float radius;
+};
+
+Sphere shape_sphere(vec3 center, float radius)
+{
+ return Sphere(center, radius);
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Box
+ * \{ */
+
+struct Box {
+ vec3 corners[8];
+};
+
+/* Construct box from 4 basis points. */
+Box shape_box(vec3 v000, vec3 v100, vec3 v010, vec3 v001)
+{
+ v100 -= v000;
+ v010 -= v000;
+ v001 -= v000;
+ Box box;
+ box.corners[0] = v000;
+ box.corners[1] = v000 + v100;
+ box.corners[2] = v000 + v010 + v100;
+ box.corners[3] = v000 + v010;
+ box.corners[4] = box.corners[0] + v001;
+ box.corners[5] = box.corners[1] + v001;
+ box.corners[6] = box.corners[2] + v001;
+ box.corners[7] = box.corners[3] + v001;
+ return box;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Square Pyramid
+ * \{ */
+
+struct Pyramid {
+ /* Apex is the first. Base vertices are in clockwise order from front view. */
+ vec3 corners[5];
+};
+
+/**
+ * Regular Square Pyramid (can be oblique).
+ * Use this corner order.
+ * (Top-Down View of the pyramid)
+ * <pre>
+ *
+ * Y
+ * |
+ * |
+ * .-----X
+ *
+ * 4-----------3
+ * | \ / |
+ * | \ / |
+ * | 0 |
+ * | / \ |
+ * | / \ |
+ * 1-----------2
+ * </pre>
+ * base_corner_00 is vertex 1
+ * base_corner_01 is vertex 2
+ * base_corner_10 is vertex 4
+ */
+Pyramid shape_pyramid(vec3 apex, vec3 base_corner_00, vec3 base_corner_01, vec3 base_corner_10)
+{
+ Pyramid pyramid;
+ pyramid.corners[0] = apex;
+ pyramid.corners[1] = base_corner_00;
+ pyramid.corners[2] = base_corner_01;
+ pyramid.corners[3] = base_corner_10 + (base_corner_01 - base_corner_00);
+ pyramid.corners[4] = base_corner_10;
+ return pyramid;
+}
+
+/**
+ * Regular Square Pyramid.
+ * <pre>
+ *
+ * Y
+ * |
+ * |
+ * .-----X
+ *
+ * 4-----Y-----3
+ * | \ | / |
+ * | \ | / |
+ * | 0-----X
+ * | / \ |
+ * | / \ |
+ * 1-----------2
+ * </pre>
+ * base_center_pos_x is vector from base center to X
+ * base_center_pos_y is vector from base center to Y
+ */
+Pyramid shape_pyramid_non_oblique(vec3 apex,
+ vec3 base_center,
+ vec3 base_center_pos_x,
+ vec3 base_center_pos_y)
+{
+ Pyramid pyramid;
+ pyramid.corners[0] = apex;
+ pyramid.corners[1] = base_center - base_center_pos_x - base_center_pos_y;
+ pyramid.corners[2] = base_center + base_center_pos_x - base_center_pos_y;
+ pyramid.corners[3] = base_center + base_center_pos_x + base_center_pos_y;
+ pyramid.corners[4] = base_center - base_center_pos_x + base_center_pos_y;
+ return pyramid;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Frustum
+ * \{ */
+
+struct Frustum {
+ vec3 corners[8];
+};
+
+/**
+ * Use this corner order.
+ * <pre>
+ *
+ * Z Y
+ * | /
+ * |/
+ * .-----X
+ * 2----------6
+ * /| /|
+ * / | / |
+ * 1----------5 |
+ * | | | |
+ * | 3-------|--7
+ * | / | /
+ * |/ |/
+ * 0----------4
+ * </pre>
+ */
+Frustum shape_frustum(vec3 corners[8])
+{
+ Frustum frustum;
+ for (int i = 0; i < 8; i++) {
+ frustum.corners[i] = corners[i];
+ }
+ return frustum;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Cone
+ * \{ */
+
+/* Cone at orign with no height. */
+struct Cone {
+ vec3 direction;
+ float angle_cos;
+};
+
+Cone shape_cone(vec3 direction, float angle_cosine)
+{
+ return Cone(direction, angle_cosine);
+}
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl
index 8eecaa46b58..6521476c3a7 100644
--- a/source/blender/draw/intern/shaders/common_view_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_view_lib.glsl
@@ -37,6 +37,9 @@ layout(std140) uniform viewBlock
# endif
#endif
+#define IS_DEBUG_MOUSE_FRAGMENT (ivec2(gl_FragCoord) == drw_view.mouse_pixel)
+#define IS_FIRST_INVOCATION (gl_GlobalInvocationID == uvec3(0))
+
#define ViewNear (ViewVecs[0].w)
#define ViewFar (ViewVecs[1].w)
@@ -152,7 +155,11 @@ uniform int drw_ResourceID;
# define PASS_RESOURCE_ID
# elif defined(GPU_VERTEX_SHADER)
-# define resource_id gpu_InstanceIndex
+# if defined(UNIFORM_RESOURCE_ID_NEW)
+# define resource_id drw_ResourceID
+# else
+# define resource_id gpu_InstanceIndex
+# endif
# define PASS_RESOURCE_ID drw_ResourceID_iface.resource_index = resource_id;
# elif defined(GPU_GEOMETRY_SHADER)
@@ -200,8 +207,8 @@ flat in int resourceIDFrag;
# ifndef DRW_SHADER_SHARED_H
struct ObjectMatrices {
- mat4 drw_modelMatrix;
- mat4 drw_modelMatrixInverse;
+ mat4 model;
+ mat4 model_inverse;
};
# endif /* DRW_SHADER_SHARED_H */
@@ -211,8 +218,8 @@ layout(std140) uniform modelBlock
ObjectMatrices drw_matrices[DRW_RESOURCE_CHUNK_LEN];
};
-# define ModelMatrix (drw_matrices[resource_id].drw_modelMatrix)
-# define ModelMatrixInverse (drw_matrices[resource_id].drw_modelMatrixInverse)
+# define ModelMatrix (drw_matrices[resource_id].model)
+# define ModelMatrixInverse (drw_matrices[resource_id].model_inverse)
# endif /* USE_GPU_SHADER_CREATE_INFO */
#else /* GPU_INTEL */
diff --git a/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl b/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl
new file mode 100644
index 00000000000..3e640540777
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_command_generate_comp.glsl
@@ -0,0 +1,84 @@
+
+/**
+ * Convert DrawPrototype into draw commands.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+#define atomicAddAndGet(dst, val) (atomicAdd(dst, val) + val)
+
+/* This is only called by the last thread executed over the group's prototype draws. */
+void write_draw_call(DrawGroup group, uint group_id)
+{
+ DrawCommand cmd;
+ cmd.vertex_len = group.vertex_len;
+ cmd.vertex_first = group.vertex_first;
+ if (group.base_index != -1) {
+ cmd.base_index = group.base_index;
+ cmd.instance_first_indexed = group.start;
+ }
+ else {
+ cmd._instance_first_array = group.start;
+ }
+ /* Back-facing command. */
+ cmd.instance_len = group_buf[group_id].back_facing_counter;
+ command_buf[group_id * 2 + 0] = cmd;
+ /* Front-facing command. */
+ cmd.instance_len = group_buf[group_id].front_facing_counter;
+ command_buf[group_id * 2 + 1] = cmd;
+
+ /* Reset the counters for a next command gen dispatch. Avoids resending the whole data just
+ * for this purpose. Only the last thread will execute this so it is thread-safe. */
+ group_buf[group_id].front_facing_counter = 0u;
+ group_buf[group_id].back_facing_counter = 0u;
+ group_buf[group_id].total_counter = 0u;
+}
+
+void main()
+{
+ uint proto_id = gl_GlobalInvocationID.x;
+ if (proto_id >= prototype_len) {
+ return;
+ }
+
+ DrawPrototype proto = prototype_buf[proto_id];
+ uint group_id = proto.group_id;
+ bool is_inverted = (proto.resource_handle & 0x80000000u) != 0;
+ uint resource_index = (proto.resource_handle & 0x7FFFFFFFu);
+
+ /* Visibility test result. */
+ bool is_visible = ((visibility_buf[resource_index / 32u] & (1u << (resource_index % 32u)))) != 0;
+
+ DrawGroup group = group_buf[group_id];
+
+ if (!is_visible) {
+ /* Skip the draw but still count towards the completion. */
+ if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) {
+ write_draw_call(group, group_id);
+ }
+ return;
+ }
+
+ uint back_facing_len = group.len - group.front_facing_len;
+ uint front_facing_len = group.front_facing_len;
+ uint dst_index = group.start;
+ if (is_inverted) {
+ uint offset = atomicAdd(group_buf[group_id].back_facing_counter, proto.instance_len);
+ dst_index += offset;
+ if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) {
+ write_draw_call(group, group_id);
+ }
+ }
+ else {
+ uint offset = atomicAdd(group_buf[group_id].front_facing_counter, proto.instance_len);
+ dst_index += back_facing_len + offset;
+ if (atomicAddAndGet(group_buf[group_id].total_counter, proto.instance_len) == group.len) {
+ write_draw_call(group, group_id);
+ }
+ }
+
+ for (uint i = dst_index; i < dst_index + proto.instance_len; i++) {
+ /* Fill resource_id buffer for each instance of this draw */
+ resource_id_buf[i] = resource_index;
+ }
+}
diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl
new file mode 100644
index 00000000000..3fc5294b024
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl
@@ -0,0 +1,9 @@
+
+/**
+ * Display debug edge list.
+ **/
+
+void main()
+{
+ out_color = interp.color;
+}
diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl
new file mode 100644
index 00000000000..4061dda5d1c
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl
@@ -0,0 +1,15 @@
+
+/**
+ * Display debug edge list.
+ **/
+
+void main()
+{
+ /* Skip the first vertex containing header data. */
+ DRWDebugVert vert = drw_debug_verts_buf[gl_VertexID + 2];
+ vec3 pos = uintBitsToFloat(uvec3(vert.pos0, vert.pos1, vert.pos2));
+ vec4 col = vec4((uvec4(vert.color) >> uvec4(0, 8, 16, 24)) & 0xFFu) / 255.0;
+
+ interp.color = col;
+ gl_Position = persmat * vec4(pos, 1.0);
+}
diff --git a/source/blender/draw/intern/shaders/draw_debug_info.hh b/source/blender/draw/intern/shaders/draw_debug_info.hh
new file mode 100644
index 00000000000..ce450bb1210
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_info.hh
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "draw_defines.h"
+#include "gpu_shader_create_info.hh"
+
+/* -------------------------------------------------------------------- */
+/** \name Debug print
+ *
+ * Allows print() function to have logging support inside shaders.
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(draw_debug_print)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(DRW_DEBUG_PRINT_SLOT, Qualifier::READ_WRITE, "uint", "drw_debug_print_buf[]");
+
+GPU_SHADER_INTERFACE_INFO(draw_debug_print_display_iface, "").flat(Type::UINT, "char_index");
+
+GPU_SHADER_CREATE_INFO(draw_debug_print_display)
+ .do_static_compilation(true)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(7, Qualifier::READ, "uint", "drw_debug_print_buf[]")
+ .vertex_out(draw_debug_print_display_iface)
+ .fragment_out(0, Type::VEC4, "out_color")
+ .vertex_source("draw_debug_print_display_vert.glsl")
+ .fragment_source("draw_debug_print_display_frag.glsl")
+ .additional_info("draw_view");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Debug draw shapes
+ *
+ * Allows to draw lines and points just like the DRW_debug module functions.
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(draw_debug_draw)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(DRW_DEBUG_DRAW_SLOT,
+ Qualifier::READ_WRITE,
+ "DRWDebugVert",
+ "drw_debug_verts_buf[]");
+
+GPU_SHADER_INTERFACE_INFO(draw_debug_draw_display_iface, "interp").flat(Type::VEC4, "color");
+
+GPU_SHADER_CREATE_INFO(draw_debug_draw_display)
+ .do_static_compilation(true)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(6, Qualifier::READ, "DRWDebugVert", "drw_debug_verts_buf[]")
+ .vertex_out(draw_debug_draw_display_iface)
+ .fragment_out(0, Type::VEC4, "out_color")
+ .push_constant(Type::MAT4, "persmat")
+ .vertex_source("draw_debug_draw_display_vert.glsl")
+ .fragment_source("draw_debug_draw_display_frag.glsl")
+ .additional_info("draw_view");
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl
new file mode 100644
index 00000000000..4e0d980637f
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl
@@ -0,0 +1,133 @@
+
+/**
+ * Display characters using an ascii table.
+ **/
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+bool char_intersect(uvec2 bitmap_position)
+{
+ /* Using 8x8 = 64bits = uvec2. */
+ uvec2 ascii_bitmap[96] = uvec2[96](uvec2(0x00000000u, 0x00000000u),
+ uvec2(0x18001800u, 0x183c3c18u),
+ uvec2(0x00000000u, 0x36360000u),
+ uvec2(0x7f363600u, 0x36367f36u),
+ uvec2(0x301f0c00u, 0x0c3e031eu),
+ uvec2(0x0c666300u, 0x00633318u),
+ uvec2(0x3b336e00u, 0x1c361c6eu),
+ uvec2(0x00000000u, 0x06060300u),
+ uvec2(0x060c1800u, 0x180c0606u),
+ uvec2(0x180c0600u, 0x060c1818u),
+ uvec2(0x3c660000u, 0x00663cffu),
+ uvec2(0x0c0c0000u, 0x000c0c3fu),
+ uvec2(0x000c0c06u, 0x00000000u),
+ uvec2(0x00000000u, 0x0000003fu),
+ uvec2(0x000c0c00u, 0x00000000u),
+ uvec2(0x06030100u, 0x6030180cu),
+ uvec2(0x6f673e00u, 0x3e63737bu),
+ uvec2(0x0c0c3f00u, 0x0c0e0c0cu),
+ uvec2(0x06333f00u, 0x1e33301cu),
+ uvec2(0x30331e00u, 0x1e33301cu),
+ uvec2(0x7f307800u, 0x383c3633u),
+ uvec2(0x30331e00u, 0x3f031f30u),
+ uvec2(0x33331e00u, 0x1c06031fu),
+ uvec2(0x0c0c0c00u, 0x3f333018u),
+ uvec2(0x33331e00u, 0x1e33331eu),
+ uvec2(0x30180e00u, 0x1e33333eu),
+ uvec2(0x000c0c00u, 0x000c0c00u),
+ uvec2(0x000c0c06u, 0x000c0c00u),
+ uvec2(0x060c1800u, 0x180c0603u),
+ uvec2(0x003f0000u, 0x00003f00u),
+ uvec2(0x180c0600u, 0x060c1830u),
+ uvec2(0x0c000c00u, 0x1e333018u),
+ uvec2(0x7b031e00u, 0x3e637b7bu),
+ uvec2(0x3f333300u, 0x0c1e3333u),
+ uvec2(0x66663f00u, 0x3f66663eu),
+ uvec2(0x03663c00u, 0x3c660303u),
+ uvec2(0x66361f00u, 0x1f366666u),
+ uvec2(0x16467f00u, 0x7f46161eu),
+ uvec2(0x16060f00u, 0x7f46161eu),
+ uvec2(0x73667c00u, 0x3c660303u),
+ uvec2(0x33333300u, 0x3333333fu),
+ uvec2(0x0c0c1e00u, 0x1e0c0c0cu),
+ uvec2(0x33331e00u, 0x78303030u),
+ uvec2(0x36666700u, 0x6766361eu),
+ uvec2(0x46667f00u, 0x0f060606u),
+ uvec2(0x6b636300u, 0x63777f7fu),
+ uvec2(0x73636300u, 0x63676f7bu),
+ uvec2(0x63361c00u, 0x1c366363u),
+ uvec2(0x06060f00u, 0x3f66663eu),
+ uvec2(0x3b1e3800u, 0x1e333333u),
+ uvec2(0x36666700u, 0x3f66663eu),
+ uvec2(0x38331e00u, 0x1e33070eu),
+ uvec2(0x0c0c1e00u, 0x3f2d0c0cu),
+ uvec2(0x33333f00u, 0x33333333u),
+ uvec2(0x331e0c00u, 0x33333333u),
+ uvec2(0x7f776300u, 0x6363636bu),
+ uvec2(0x1c366300u, 0x6363361cu),
+ uvec2(0x0c0c1e00u, 0x3333331eu),
+ uvec2(0x4c667f00u, 0x7f633118u),
+ uvec2(0x06061e00u, 0x1e060606u),
+ uvec2(0x30604000u, 0x03060c18u),
+ uvec2(0x18181e00u, 0x1e181818u),
+ uvec2(0x00000000u, 0x081c3663u),
+ uvec2(0x000000ffu, 0x00000000u),
+ uvec2(0x00000000u, 0x0c0c1800u),
+ uvec2(0x3e336e00u, 0x00001e30u),
+ uvec2(0x66663b00u, 0x0706063eu),
+ uvec2(0x03331e00u, 0x00001e33u),
+ uvec2(0x33336e00u, 0x3830303eu),
+ uvec2(0x3f031e00u, 0x00001e33u),
+ uvec2(0x06060f00u, 0x1c36060fu),
+ uvec2(0x333e301fu, 0x00006e33u),
+ uvec2(0x66666700u, 0x0706366eu),
+ uvec2(0x0c0c1e00u, 0x0c000e0cu),
+ uvec2(0x3033331eu, 0x30003030u),
+ uvec2(0x1e366700u, 0x07066636u),
+ uvec2(0x0c0c1e00u, 0x0e0c0c0cu),
+ uvec2(0x7f6b6300u, 0x0000337fu),
+ uvec2(0x33333300u, 0x00001f33u),
+ uvec2(0x33331e00u, 0x00001e33u),
+ uvec2(0x663e060fu, 0x00003b66u),
+ uvec2(0x333e3078u, 0x00006e33u),
+ uvec2(0x66060f00u, 0x00003b6eu),
+ uvec2(0x1e301f00u, 0x00003e03u),
+ uvec2(0x0c2c1800u, 0x080c3e0cu),
+ uvec2(0x33336e00u, 0x00003333u),
+ uvec2(0x331e0c00u, 0x00003333u),
+ uvec2(0x7f7f3600u, 0x0000636bu),
+ uvec2(0x1c366300u, 0x00006336u),
+ uvec2(0x333e301fu, 0x00003333u),
+ uvec2(0x0c263f00u, 0x00003f19u),
+ uvec2(0x0c0c3800u, 0x380c0c07u),
+ uvec2(0x18181800u, 0x18181800u),
+ uvec2(0x0c0c0700u, 0x070c0c38u),
+ uvec2(0x00000000u, 0x6e3b0000u),
+ uvec2(0x00000000u, 0x00000000u));
+
+ if (!in_range_inclusive(bitmap_position, uvec2(0), uvec2(7))) {
+ return false;
+ }
+ uint char_bits = ascii_bitmap[char_index][bitmap_position.y >> 2u & 1u];
+ char_bits = (char_bits >> ((bitmap_position.y & 3u) * 8u + bitmap_position.x));
+ return (char_bits & 1u) != 0u;
+}
+
+void main()
+{
+ uvec2 bitmap_position = uvec2(gl_PointCoord.xy * 8.0);
+ /* Point coord start from top left corner. But layout is from bottom to top. */
+ bitmap_position.y = 7 - bitmap_position.y;
+
+ if (char_intersect(bitmap_position)) {
+ out_color = vec4(1);
+ }
+ else if (char_intersect(bitmap_position + uvec2(0, 1))) {
+ /* Shadow */
+ out_color = vec4(0, 0, 0, 1);
+ }
+ else {
+ /* Transparent Background for ease of read. */
+ out_color = vec4(0, 0, 0, 0.2);
+ }
+}
diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl
new file mode 100644
index 00000000000..cb379056e2b
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl
@@ -0,0 +1,29 @@
+
+/**
+ * Display characters using an ascii table. Outputs one point per character.
+ **/
+
+#pragma BLENDER_REQUIRE(common_view_lib.glsl)
+
+void main()
+{
+ /* Skip first 4 chars containing header data. */
+ uint char_data = drw_debug_print_buf[gl_VertexID + 8];
+ char_index = (char_data & 0xFFu) - 0x20u;
+
+ /* Discard invalid chars. */
+ if (char_index >= 96u) {
+ gl_Position = vec4(-1);
+ gl_PointSize = 0.0;
+ return;
+ }
+ uint row = (char_data >> 16u) & 0xFFu;
+ uint col = (char_data >> 8u) & 0xFFu;
+
+ float char_size = 16.0;
+ /* Change anchor point to the top left. */
+ vec2 pos_on_screen = char_size * vec2(col, row) + char_size * 4;
+ gl_Position = vec4(
+ pos_on_screen * drw_view.viewport_size_inverse * vec2(2.0, -2.0) - vec2(1.0, -1.0), 0, 1);
+ gl_PointSize = char_size;
+}
diff --git a/source/blender/draw/intern/shaders/draw_object_infos_info.hh b/source/blender/draw/intern/shaders/draw_object_infos_info.hh
index 8fd55ea351f..31fee018fbc 100644
--- a/source/blender/draw/intern/shaders/draw_object_infos_info.hh
+++ b/source/blender/draw/intern/shaders/draw_object_infos_info.hh
@@ -1,10 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "draw_defines.h"
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(draw_object_infos)
.typedef_source("draw_shader_shared.h")
.define("OBINFO_LIB")
+ .define("OrcoTexCoFactors", "(drw_infos[resource_id].orco_mul_bias)")
+ .define("ObjectInfo", "(drw_infos[resource_id].infos)")
+ .define("ObjectColor", "(drw_infos[resource_id].color)")
.uniform_buf(1, "ObjectInfos", "drw_infos[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH);
GPU_SHADER_CREATE_INFO(draw_volume_infos)
@@ -14,3 +18,19 @@ GPU_SHADER_CREATE_INFO(draw_volume_infos)
GPU_SHADER_CREATE_INFO(draw_curves_infos)
.typedef_source("draw_shader_shared.h")
.uniform_buf(2, "CurvesInfos", "drw_curves", Frequency::BATCH);
+
+GPU_SHADER_CREATE_INFO(draw_object_infos_new)
+ .typedef_source("draw_shader_shared.h")
+ .define("OBINFO_LIB")
+ .define("OrcoTexCoFactors", "(drw_infos[resource_id].orco_mul_bias)")
+ .define("ObjectInfo", "(drw_infos[resource_id].infos)")
+ .define("ObjectColor", "(drw_infos[resource_id].color)")
+ .storage_buf(DRW_OBJ_INFOS_SLOT, Qualifier::READ, "ObjectInfos", "drw_infos[]");
+
+/** \note Requires draw_object_infos_new. */
+GPU_SHADER_CREATE_INFO(draw_object_attribute_new)
+ .define("OBATTR_LIB")
+ .define("ObjectAttributeStart", "(drw_infos[resource_id].orco_mul_bias[0].w)")
+ .define("ObjectAttributeLen", "(drw_infos[resource_id].orco_mul_bias[1].w)")
+ .storage_buf(DRW_OBJ_ATTR_SLOT, Qualifier::READ, "ObjectAttribute", "drw_attrs[]")
+ .additional_info("draw_object_infos_new");
diff --git a/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl b/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl
new file mode 100644
index 00000000000..511d4e49651
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_resource_finalize_comp.glsl
@@ -0,0 +1,64 @@
+
+/**
+ * Finish computation of a few draw resource after sync.
+ */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+
+void main()
+{
+ uint resource_id = gl_GlobalInvocationID.x;
+ if (resource_id >= resource_len) {
+ return;
+ }
+
+ mat4 model_mat = matrix_buf[resource_id].model;
+ ObjectInfos infos = infos_buf[resource_id];
+ ObjectBounds bounds = bounds_buf[resource_id];
+
+ if (bounds.bounding_sphere.w != -1.0) {
+ /* Convert corners to origin + sides in world space. */
+ vec3 p0 = bounds.bounding_corners[0].xyz;
+ vec3 p01 = bounds.bounding_corners[1].xyz - p0;
+ vec3 p02 = bounds.bounding_corners[2].xyz - p0;
+ vec3 p03 = bounds.bounding_corners[3].xyz - p0;
+ /* Avoid flat box. */
+ p01.x = max(p01.x, 1e-4);
+ p02.y = max(p02.y, 1e-4);
+ p03.z = max(p03.z, 1e-4);
+ vec3 diagonal = p01 + p02 + p03;
+ vec3 center = p0 + diagonal * 0.5;
+ float min_axis = min_v3(abs(diagonal));
+ bounds_buf[resource_id].bounding_sphere.xyz = transform_point(model_mat, center);
+ /* We have to apply scaling to the diagonal. */
+ bounds_buf[resource_id].bounding_sphere.w = length(transform_direction(model_mat, diagonal)) *
+ 0.5;
+ bounds_buf[resource_id]._inner_sphere_radius = min_axis;
+ bounds_buf[resource_id].bounding_corners[0].xyz = transform_point(model_mat, p0);
+ bounds_buf[resource_id].bounding_corners[1].xyz = transform_direction(model_mat, p01);
+ bounds_buf[resource_id].bounding_corners[2].xyz = transform_direction(model_mat, p02);
+ bounds_buf[resource_id].bounding_corners[3].xyz = transform_direction(model_mat, p03);
+ /* Always have correct handedness in the corners vectors. */
+ if (flag_test(infos.flag, OBJECT_NEGATIVE_SCALE)) {
+ bounds_buf[resource_id].bounding_corners[0].xyz +=
+ bounds_buf[resource_id].bounding_corners[1].xyz;
+ bounds_buf[resource_id].bounding_corners[1].xyz =
+ -bounds_buf[resource_id].bounding_corners[1].xyz;
+ }
+
+ /* TODO: Bypass test for very large objects (see T67319). */
+ if (bounds_buf[resource_id].bounding_sphere.w > 1e12) {
+ bounds_buf[resource_id].bounding_sphere.w = -1.0;
+ }
+ }
+
+ vec3 loc = infos.orco_add; /* Box center. */
+ vec3 size = infos.orco_mul; /* Box half-extent. */
+ /* This is what the original computation looks like.
+ * Simplify to a nice MADD in shading code. */
+ // orco = (pos - loc) / size;
+ // orco = pos * (1.0 / size) + (-loc / size);
+ vec3 size_inv = safe_rcp(size);
+ infos_buf[resource_id].orco_add = -loc * size_inv;
+ infos_buf[resource_id].orco_mul = size_inv;
+}
diff --git a/source/blender/draw/intern/shaders/draw_view_info.hh b/source/blender/draw/intern/shaders/draw_view_info.hh
index 0400521c53d..c522c607791 100644
--- a/source/blender/draw/intern/shaders/draw_view_info.hh
+++ b/source/blender/draw/intern/shaders/draw_view_info.hh
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "draw_defines.h"
#include "gpu_shader_create_info.hh"
/* -------------------------------------------------------------------- */
@@ -44,13 +45,13 @@ GPU_SHADER_CREATE_INFO(draw_resource_handle)
* \{ */
GPU_SHADER_CREATE_INFO(draw_view)
- .uniform_buf(0, "ViewInfos", "drw_view", Frequency::PASS)
+ .uniform_buf(DRW_VIEW_UBO_SLOT, "ViewInfos", "drw_view", Frequency::PASS)
.typedef_source("draw_shader_shared.h");
GPU_SHADER_CREATE_INFO(draw_modelmat)
.uniform_buf(8, "ObjectMatrices", "drw_matrices[DRW_RESOURCE_CHUNK_LEN]", Frequency::BATCH)
- .define("ModelMatrix", "(drw_matrices[resource_id].drw_modelMatrix)")
- .define("ModelMatrixInverse", "(drw_matrices[resource_id].drw_modelMatrixInverse)")
+ .define("ModelMatrix", "(drw_matrices[resource_id].model)")
+ .define("ModelMatrixInverse", "(drw_matrices[resource_id].model_inverse)")
.additional_info("draw_view");
GPU_SHADER_CREATE_INFO(draw_modelmat_legacy)
@@ -136,3 +137,77 @@ GPU_SHADER_CREATE_INFO(draw_gpencil)
.additional_info("draw_modelmat", "draw_resource_id_uniform", "draw_object_infos");
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal Draw Manager usage
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(draw_resource_finalize)
+ .do_static_compilation(true)
+ .typedef_source("draw_shader_shared.h")
+ .define("DRAW_FINALIZE_SHADER")
+ .local_group_size(DRW_FINALIZE_GROUP_SIZE)
+ .storage_buf(0, Qualifier::READ, "ObjectMatrices", "matrix_buf[]")
+ .storage_buf(1, Qualifier::READ_WRITE, "ObjectBounds", "bounds_buf[]")
+ .storage_buf(2, Qualifier::READ_WRITE, "ObjectInfos", "infos_buf[]")
+ .push_constant(Type::INT, "resource_len")
+ .compute_source("draw_resource_finalize_comp.glsl");
+
+GPU_SHADER_CREATE_INFO(draw_visibility_compute)
+ .do_static_compilation(true)
+ .local_group_size(DRW_VISIBILITY_GROUP_SIZE)
+ .storage_buf(0, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
+ .storage_buf(1, Qualifier::READ_WRITE, "uint", "visibility_buf[]")
+ .push_constant(Type::INT, "resource_len")
+ .compute_source("draw_visibility_comp.glsl")
+ .additional_info("draw_view");
+
+GPU_SHADER_CREATE_INFO(draw_command_generate)
+ .do_static_compilation(true)
+ .typedef_source("draw_shader_shared.h")
+ .typedef_source("draw_command_shared.hh")
+ .local_group_size(DRW_COMMAND_GROUP_SIZE)
+ .storage_buf(0, Qualifier::READ_WRITE, "DrawGroup", "group_buf[]")
+ .storage_buf(1, Qualifier::READ, "uint", "visibility_buf[]")
+ .storage_buf(2, Qualifier::READ, "DrawPrototype", "prototype_buf[]")
+ .storage_buf(3, Qualifier::WRITE, "DrawCommand", "command_buf[]")
+ .storage_buf(DRW_RESOURCE_ID_SLOT, Qualifier::WRITE, "uint", "resource_id_buf[]")
+ .push_constant(Type::INT, "prototype_len")
+ .compute_source("draw_command_generate_comp.glsl");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw Resource ID
+ * New implementation using gl_BaseInstance and storage buffers.
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(draw_resource_id_new)
+ .define("UNIFORM_RESOURCE_ID_NEW")
+ .storage_buf(DRW_RESOURCE_ID_SLOT, Qualifier::READ, "int", "resource_id_buf[]")
+ .define("drw_ResourceID", "resource_id_buf[gpu_BaseInstance + gl_InstanceID]");
+
+/**
+ * Workaround the lack of gl_BaseInstance by binding the resource_id_buf as vertex buf.
+ */
+GPU_SHADER_CREATE_INFO(draw_resource_id_fallback)
+ .define("UNIFORM_RESOURCE_ID_NEW")
+ .vertex_in(15, Type::INT, "drw_ResourceID");
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Draw Object Resources
+ * \{ */
+
+GPU_SHADER_CREATE_INFO(draw_modelmat_new)
+ .typedef_source("draw_shader_shared.h")
+ .storage_buf(DRW_OBJ_MAT_SLOT, Qualifier::READ, "ObjectMatrices", "drw_matrix_buf[]")
+ .define("drw_ModelMatrixInverse", "drw_matrix_buf[resource_id].model_inverse")
+ .define("drw_ModelMatrix", "drw_matrix_buf[resource_id].model")
+ /* TODO For compatibility with old shaders. To be removed. */
+ .define("ModelMatrixInverse", "drw_ModelMatrixInverse")
+ .define("ModelMatrix", "drw_ModelMatrix")
+ .additional_info("draw_resource_id_new");
+
+/** \} */
diff --git a/source/blender/draw/intern/shaders/draw_visibility_comp.glsl b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl
new file mode 100644
index 00000000000..86add2d1fe2
--- /dev/null
+++ b/source/blender/draw/intern/shaders/draw_visibility_comp.glsl
@@ -0,0 +1,46 @@
+
+/**
+ * Compute visibility of each resource bounds for a given view.
+ */
+/* TODO(fclem): This could be augmented by a 2 pass occlusion culling system. */
+
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
+
+shared uint shared_result;
+
+void mask_visibility_bit()
+{
+ uint bit = 1u << gl_LocalInvocationID.x;
+ atomicAnd(visibility_buf[gl_WorkGroupID.x], ~bit);
+}
+
+void main()
+{
+ if (gl_GlobalInvocationID.x >= resource_len) {
+ return;
+ }
+
+ ObjectBounds bounds = bounds_buf[gl_GlobalInvocationID.x];
+
+ if (bounds.bounding_sphere.w != -1.0) {
+ IsectBox box = isect_data_setup(bounds.bounding_corners[0].xyz,
+ bounds.bounding_corners[1].xyz,
+ bounds.bounding_corners[2].xyz,
+ bounds.bounding_corners[3].xyz);
+ Sphere bounding_sphere = Sphere(bounds.bounding_sphere.xyz, bounds.bounding_sphere.w);
+ Sphere inscribed_sphere = Sphere(bounds.bounding_sphere.xyz, bounds._inner_sphere_radius);
+
+ if (intersect_view(inscribed_sphere) == true) {
+ /* Visible. */
+ }
+ else if (intersect_view(bounding_sphere) == false) {
+ /* Not visible. */
+ mask_visibility_bit();
+ }
+ else if (intersect_view(box) == false) {
+ /* Not visible. */
+ mask_visibility_bit();
+ }
+ }
+}
diff --git a/source/blender/draw/tests/draw_pass_test.cc b/source/blender/draw/tests/draw_pass_test.cc
new file mode 100644
index 00000000000..394ca8bd3cf
--- /dev/null
+++ b/source/blender/draw/tests/draw_pass_test.cc
@@ -0,0 +1,441 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "testing/testing.h"
+
+#include "draw_manager.hh"
+#include "draw_pass.hh"
+#include "draw_shader.h"
+#include "draw_testing.hh"
+
+#include <bitset>
+
+namespace blender::draw {
+
+static void test_draw_pass_all_commands()
+{
+ Texture tex;
+ tex.ensure_2d(GPU_RGBA16, int2(1));
+
+ UniformBuffer<uint4> ubo;
+ ubo.push_update();
+
+ StorageBuffer<uint4> ssbo;
+ ssbo.push_update();
+
+ float4 color(1.0f, 1.0f, 1.0f, 0.0f);
+ int3 dispatch_size(1);
+
+ PassSimple pass = {"test.all_commands"};
+ pass.init();
+ pass.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_STENCIL);
+ pass.clear_color_depth_stencil(float4(0.25f, 0.5f, 100.0f, -2000.0f), 0.5f, 0xF0);
+ pass.state_stencil(0x80, 0x0F, 0x8F);
+ pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR));
+ pass.bind_texture("image", tex);
+ pass.bind_texture("image", &tex);
+ pass.bind_image("missing_image", tex); /* Should not crash. */
+ pass.bind_image("missing_image", &tex); /* Should not crash. */
+ pass.bind_ubo("missing_ubo", ubo); /* Should not crash. */
+ pass.bind_ubo("missing_ubo", &ubo); /* Should not crash. */
+ pass.bind_ssbo("missing_ssbo", ssbo); /* Should not crash. */
+ pass.bind_ssbo("missing_ssbo", &ssbo); /* Should not crash. */
+ pass.push_constant("color", color);
+ pass.push_constant("color", &color);
+ pass.push_constant("ModelViewProjectionMatrix", float4x4::identity());
+ pass.draw_procedural(GPU_PRIM_TRIS, 1, 3);
+
+ /* Should not crash even if shader is not a compute. This is because we only serialize. */
+ /* TODO(fclem): Use real compute shader. */
+ pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR));
+ pass.dispatch(dispatch_size);
+ pass.dispatch(&dispatch_size);
+ pass.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS);
+
+ /* Change references. */
+ color[3] = 1.0f;
+ dispatch_size = int3(2);
+
+ std::string result = pass.serialize();
+ std::stringstream expected;
+ expected << ".test.all_commands" << std::endl;
+ expected << " .state_set(6)" << std::endl;
+ expected << " .clear(color=(0.25, 0.5, 100, -2000), depth=0.5, stencil=0b11110000))"
+ << std::endl;
+ expected << " .stencil_set(write_mask=0b10000000, compare_mask=0b00001111, reference=0b10001111"
+ << std::endl;
+ expected << " .shader_bind(gpu_shader_3D_image_color)" << std::endl;
+ expected << " .bind_texture(0)" << std::endl;
+ expected << " .bind_texture_ref(0)" << std::endl;
+ expected << " .bind_image(-1)" << std::endl;
+ expected << " .bind_image_ref(-1)" << std::endl;
+ expected << " .bind_uniform_buf(-1)" << std::endl;
+ expected << " .bind_uniform_buf_ref(-1)" << std::endl;
+ expected << " .bind_storage_buf(-1)" << std::endl;
+ expected << " .bind_storage_buf_ref(-1)" << std::endl;
+ expected << " .push_constant(1, data=(1, 1, 1, 0))" << std::endl;
+ expected << " .push_constant(1, data=(1, 1, 1, 1))" << std::endl;
+ expected << " .push_constant(0, data=(" << std::endl;
+ expected << "( 1.000000, 0.000000, 0.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 1.000000, 0.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 0.000000, 1.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 0.000000, 0.000000, 1.000000)" << std::endl;
+ expected << ")" << std::endl;
+ expected << ")" << std::endl;
+ expected << " .draw(inst_len=1, vert_len=3, vert_first=0, res_id=0)" << std::endl;
+ expected << " .shader_bind(gpu_shader_3D_image_color)" << std::endl;
+ expected << " .dispatch(1, 1, 1)" << std::endl;
+ expected << " .dispatch_ref(2, 2, 2)" << std::endl;
+ expected << " .barrier(4)" << std::endl;
+
+ EXPECT_EQ(result, expected.str());
+
+ DRW_shape_cache_free();
+}
+DRAW_TEST(draw_pass_all_commands)
+
+static void test_draw_pass_sub_ordering()
+{
+ PassSimple pass = {"test.sub_ordering"};
+ pass.init();
+ pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR));
+ pass.push_constant("test_pass", 1);
+
+ PassSimple::Sub &sub1 = pass.sub("Sub1");
+ sub1.push_constant("test_sub1", 11);
+
+ PassSimple::Sub &sub2 = pass.sub("Sub2");
+ sub2.push_constant("test_sub2", 21);
+
+ /* Will execute after both sub. */
+ pass.push_constant("test_pass", 2);
+
+ /* Will execute after sub1. */
+ sub2.push_constant("test_sub2", 22);
+
+ /* Will execute before sub2. */
+ sub1.push_constant("test_sub1", 12);
+
+ /* Will execute before end of pass. */
+ sub2.push_constant("test_sub2", 23);
+
+ std::string result = pass.serialize();
+ std::stringstream expected;
+ expected << ".test.sub_ordering" << std::endl;
+ expected << " .shader_bind(gpu_shader_3D_image_color)" << std::endl;
+ expected << " .push_constant(-1, data=1)" << std::endl;
+ expected << " .Sub1" << std::endl;
+ expected << " .push_constant(-1, data=11)" << std::endl;
+ expected << " .push_constant(-1, data=12)" << std::endl;
+ expected << " .Sub2" << std::endl;
+ expected << " .push_constant(-1, data=21)" << std::endl;
+ expected << " .push_constant(-1, data=22)" << std::endl;
+ expected << " .push_constant(-1, data=23)" << std::endl;
+ expected << " .push_constant(-1, data=2)" << std::endl;
+
+ EXPECT_EQ(result, expected.str());
+}
+DRAW_TEST(draw_pass_sub_ordering)
+
+static void test_draw_pass_simple_draw()
+{
+ PassSimple pass = {"test.simple_draw"};
+ pass.init();
+ pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR));
+ /* Each draw procedural type uses a different batch. Groups are drawn in correct order. */
+ pass.draw_procedural(GPU_PRIM_TRIS, 1, 10, 1, {1});
+ pass.draw_procedural(GPU_PRIM_POINTS, 4, 20, 2, {2});
+ pass.draw_procedural(GPU_PRIM_TRIS, 2, 30, 3, {3});
+ pass.draw_procedural(GPU_PRIM_POINTS, 5, 40, 4, ResourceHandle(4, true));
+ pass.draw_procedural(GPU_PRIM_LINES, 1, 50, 5, {5});
+ pass.draw_procedural(GPU_PRIM_POINTS, 6, 60, 6, {5});
+ pass.draw_procedural(GPU_PRIM_TRIS, 3, 70, 7, {6});
+
+ std::string result = pass.serialize();
+ std::stringstream expected;
+ expected << ".test.simple_draw" << std::endl;
+ expected << " .shader_bind(gpu_shader_3D_image_color)" << std::endl;
+ expected << " .draw(inst_len=1, vert_len=10, vert_first=1, res_id=1)" << std::endl;
+ expected << " .draw(inst_len=4, vert_len=20, vert_first=2, res_id=2)" << std::endl;
+ expected << " .draw(inst_len=2, vert_len=30, vert_first=3, res_id=3)" << std::endl;
+ expected << " .draw(inst_len=5, vert_len=40, vert_first=4, res_id=4)" << std::endl;
+ expected << " .draw(inst_len=1, vert_len=50, vert_first=5, res_id=5)" << std::endl;
+ expected << " .draw(inst_len=6, vert_len=60, vert_first=6, res_id=5)" << std::endl;
+ expected << " .draw(inst_len=3, vert_len=70, vert_first=7, res_id=6)" << std::endl;
+
+ EXPECT_EQ(result, expected.str());
+
+ DRW_shape_cache_free();
+}
+DRAW_TEST(draw_pass_simple_draw)
+
+static void test_draw_pass_multi_draw()
+{
+ PassMain pass = {"test.multi_draw"};
+ pass.init();
+ pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR));
+ /* Each draw procedural type uses a different batch. Groups are drawn in reverse order. */
+ pass.draw_procedural(GPU_PRIM_TRIS, 1, -1, -1, {1});
+ pass.draw_procedural(GPU_PRIM_POINTS, 4, -1, -1, {2});
+ pass.draw_procedural(GPU_PRIM_TRIS, 2, -1, -1, {3});
+ pass.draw_procedural(GPU_PRIM_POINTS, 5, -1, -1, ResourceHandle(4, true));
+ pass.draw_procedural(GPU_PRIM_LINES, 1, -1, -1, {5});
+ pass.draw_procedural(GPU_PRIM_POINTS, 6, -1, -1, {5});
+ pass.draw_procedural(GPU_PRIM_TRIS, 3, -1, -1, {6});
+
+ std::string result = pass.serialize();
+ std::stringstream expected;
+ expected << ".test.multi_draw" << std::endl;
+ expected << " .shader_bind(gpu_shader_3D_image_color)" << std::endl;
+ expected << " .draw_multi(3)" << std::endl;
+ expected << " .group(id=2, len=1)" << std::endl;
+ expected << " .proto(instance_len=1, resource_id=5, front_face)" << std::endl;
+ expected << " .group(id=1, len=15)" << std::endl;
+ expected << " .proto(instance_len=5, resource_id=4, back_face)" << std::endl;
+ expected << " .proto(instance_len=6, resource_id=5, front_face)" << std::endl;
+ expected << " .proto(instance_len=4, resource_id=2, front_face)" << std::endl;
+ expected << " .group(id=0, len=6)" << std::endl;
+ expected << " .proto(instance_len=3, resource_id=6, front_face)" << std::endl;
+ expected << " .proto(instance_len=2, resource_id=3, front_face)" << std::endl;
+ expected << " .proto(instance_len=1, resource_id=1, front_face)" << std::endl;
+
+ EXPECT_EQ(result, expected.str());
+
+ DRW_shape_cache_free();
+}
+DRAW_TEST(draw_pass_multi_draw)
+
+static void test_draw_pass_sortable()
+{
+ PassSortable pass = {"test.sortable"};
+ pass.init();
+
+ pass.sub("Sub3", 3.0f);
+ pass.sub("Sub2", 2.0f);
+ pass.sub("Sub5", 4.0f);
+ pass.sub("Sub4", 3.0f);
+ pass.sub("Sub1", 1.0f);
+
+ std::string result = pass.serialize();
+ std::stringstream expected;
+ expected << ".test.sortable" << std::endl;
+ expected << " .Sub1" << std::endl;
+ expected << " .Sub2" << std::endl;
+ expected << " .Sub3" << std::endl;
+ expected << " .Sub4" << std::endl;
+ expected << " .Sub5" << std::endl;
+
+ EXPECT_EQ(result, expected.str());
+
+ DRW_shape_cache_free();
+}
+DRAW_TEST(draw_pass_sortable)
+
+static void test_draw_resource_id_gen()
+{
+ float4x4 win_mat;
+ orthographic_m4(win_mat.ptr(), -1, 1, -1, 1, -1, 1);
+
+ View view("test_view");
+ view.sync(float4x4::identity(), win_mat);
+
+ Manager drw;
+
+ float4x4 obmat_1 = float4x4::identity();
+ float4x4 obmat_2 = float4x4::identity();
+ obmat_1.apply_scale(-0.5f);
+ obmat_2.apply_scale(0.5f);
+
+ drw.begin_sync();
+ ResourceHandle handle1 = drw.resource_handle(obmat_1);
+ ResourceHandle handle2 = drw.resource_handle(obmat_1);
+ ResourceHandle handle3 = drw.resource_handle(obmat_2);
+ drw.resource_handle(obmat_2, float3(2), float3(1));
+ drw.end_sync();
+
+ StringRefNull expected = "2 1 1 1 1 3 3 1 1 1 1 1 3 2 2 2 2 2 2 1 1 1 ";
+
+ {
+ /* Computed on CPU. */
+ PassSimple pass = {"test.resource_id"};
+ pass.init();
+ pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR));
+ pass.draw_procedural(GPU_PRIM_TRIS, 1, -1, -1, handle2);
+ pass.draw_procedural(GPU_PRIM_POINTS, 4, -1, -1, handle1);
+ pass.draw_procedural(GPU_PRIM_TRIS, 2, -1, -1, handle3);
+ pass.draw_procedural(GPU_PRIM_POINTS, 5, -1, -1, handle1);
+ pass.draw_procedural(GPU_PRIM_LINES, 1, -1, -1, handle3);
+ pass.draw_procedural(GPU_PRIM_POINTS, 6, -1, -1, handle2);
+ pass.draw_procedural(GPU_PRIM_TRIS, 3, -1, -1, handle1);
+
+ Manager::SubmitDebugOutput debug = drw.submit_debug(pass, view);
+
+ std::stringstream result;
+ for (auto val : debug.resource_id) {
+ result << val << " ";
+ }
+
+ EXPECT_EQ(result.str(), expected);
+ }
+ {
+ /* Same thing with PassMain (computed on GPU) */
+ PassSimple pass = {"test.resource_id"};
+ pass.init();
+ pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR));
+ pass.draw_procedural(GPU_PRIM_TRIS, 1, -1, -1, handle2);
+ pass.draw_procedural(GPU_PRIM_POINTS, 4, -1, -1, handle1);
+ pass.draw_procedural(GPU_PRIM_TRIS, 2, -1, -1, handle3);
+ pass.draw_procedural(GPU_PRIM_POINTS, 5, -1, -1, handle1);
+ pass.draw_procedural(GPU_PRIM_LINES, 1, -1, -1, handle3);
+ pass.draw_procedural(GPU_PRIM_POINTS, 6, -1, -1, handle2);
+ pass.draw_procedural(GPU_PRIM_TRIS, 3, -1, -1, handle1);
+
+ Manager::SubmitDebugOutput debug = drw.submit_debug(pass, view);
+
+ std::stringstream result;
+ for (auto val : debug.resource_id) {
+ result << val << " ";
+ }
+
+ EXPECT_EQ(result.str(), expected);
+ }
+
+ DRW_shape_cache_free();
+ DRW_shaders_free();
+}
+DRAW_TEST(draw_resource_id_gen)
+
+static void test_draw_visibility()
+{
+ float4x4 win_mat;
+ orthographic_m4(win_mat.ptr(), -1, 1, -1, 1, -1, 1);
+
+ View view("test_view");
+ view.sync(float4x4::identity(), win_mat);
+
+ Manager drw;
+
+ float4x4 obmat_1 = float4x4::identity();
+ float4x4 obmat_2 = float4x4::identity();
+ obmat_1.apply_scale(-0.5f);
+ obmat_2.apply_scale(0.5f);
+
+ drw.begin_sync(); /* Default {0} always visible. */
+ drw.resource_handle(obmat_1); /* No bounds, always visible. */
+ drw.resource_handle(obmat_1, float3(3), float3(1)); /* Out of view. */
+ drw.resource_handle(obmat_2, float3(0), float3(1)); /* Inside view. */
+ drw.end_sync();
+
+ PassMain pass = {"test.visibility"};
+ pass.init();
+ pass.shader_set(GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR));
+ pass.draw_procedural(GPU_PRIM_TRIS, 1, -1);
+
+ Manager::SubmitDebugOutput debug = drw.submit_debug(pass, view);
+ Vector<uint32_t> expected_visibility = {0};
+
+ std::stringstream result;
+ for (auto val : debug.visibility) {
+ result << std::bitset<32>(val);
+ }
+
+ EXPECT_EQ(result.str(), "11111111111111111111111111111011");
+
+ DRW_shape_cache_free();
+ DRW_shaders_free();
+}
+DRAW_TEST(draw_visibility)
+
+static void test_draw_manager_sync()
+{
+ float4x4 obmat_1 = float4x4::identity();
+ float4x4 obmat_2 = float4x4::identity();
+ obmat_1.apply_scale(-0.5f);
+ obmat_2.apply_scale(0.5f);
+
+ /* TODO find a way to create a minimum object to test resource handle creation on it. */
+ Manager drw;
+
+ drw.begin_sync();
+ drw.resource_handle(obmat_1);
+ drw.resource_handle(obmat_2, float3(2), float3(1));
+ drw.end_sync();
+
+ Manager::DataDebugOutput debug = drw.data_debug();
+
+ std::stringstream result;
+ for (const auto &val : debug.matrices) {
+ result << val;
+ }
+ for (const auto &val : debug.bounds) {
+ result << val;
+ }
+ for (const auto &val : debug.infos) {
+ result << val;
+ }
+
+ std::stringstream expected;
+ expected << "ObjectMatrices(" << std::endl;
+ expected << "model=(" << std::endl;
+ expected << "( 1.000000, 0.000000, 0.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 1.000000, 0.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 0.000000, 1.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 0.000000, 0.000000, 1.000000)" << std::endl;
+ expected << ")" << std::endl;
+ expected << ", " << std::endl;
+ expected << "model_inverse=(" << std::endl;
+ expected << "( 1.000000, -0.000000, 0.000000, -0.000000)" << std::endl;
+ expected << "( -0.000000, 1.000000, -0.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, -0.000000, 1.000000, -0.000000)" << std::endl;
+ expected << "( -0.000000, 0.000000, -0.000000, 1.000000)" << std::endl;
+ expected << ")" << std::endl;
+ expected << ")" << std::endl;
+ expected << "ObjectMatrices(" << std::endl;
+ expected << "model=(" << std::endl;
+ expected << "( -0.500000, -0.000000, -0.000000, 0.000000)" << std::endl;
+ expected << "( -0.000000, -0.500000, -0.000000, 0.000000)" << std::endl;
+ expected << "( -0.000000, -0.000000, -0.500000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 0.000000, 0.000000, 1.000000)" << std::endl;
+ expected << ")" << std::endl;
+ expected << ", " << std::endl;
+ expected << "model_inverse=(" << std::endl;
+ expected << "( -2.000000, 0.000000, -0.000000, -0.000000)" << std::endl;
+ expected << "( 0.000000, -2.000000, 0.000000, 0.000000)" << std::endl;
+ expected << "( -0.000000, 0.000000, -2.000000, 0.000000)" << std::endl;
+ expected << "( -0.000000, -0.000000, 0.000000, 1.000000)" << std::endl;
+ expected << ")" << std::endl;
+ expected << ")" << std::endl;
+ expected << "ObjectMatrices(" << std::endl;
+ expected << "model=(" << std::endl;
+ expected << "( 0.500000, 0.000000, 0.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 0.500000, 0.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 0.000000, 0.500000, 0.000000)" << std::endl;
+ expected << "( 0.000000, 0.000000, 0.000000, 1.000000)" << std::endl;
+ expected << ")" << std::endl;
+ expected << ", " << std::endl;
+ expected << "model_inverse=(" << std::endl;
+ expected << "( 2.000000, -0.000000, 0.000000, -0.000000)" << std::endl;
+ expected << "( -0.000000, 2.000000, -0.000000, 0.000000)" << std::endl;
+ expected << "( 0.000000, -0.000000, 2.000000, -0.000000)" << std::endl;
+ expected << "( -0.000000, 0.000000, -0.000000, 1.000000)" << std::endl;
+ expected << ")" << std::endl;
+ expected << ")" << std::endl;
+ expected << "ObjectBounds(skipped)" << std::endl;
+ expected << "ObjectBounds(skipped)" << std::endl;
+ expected << "ObjectBounds(" << std::endl;
+ expected << ".bounding_corners[0](0.5, 0.5, 0.5)" << std::endl;
+ expected << ".bounding_corners[1](1, 0, 0)" << std::endl;
+ expected << ".bounding_corners[2](0, 1, 0)" << std::endl;
+ expected << ".bounding_corners[3](0, 0, 1)" << std::endl;
+ expected << ".sphere=(pos=(1, 1, 1), rad=0.866025" << std::endl;
+ expected << ")" << std::endl;
+ expected << "ObjectInfos(skipped)" << std::endl;
+ expected << "ObjectInfos(skipped)" << std::endl;
+ expected << "ObjectInfos(skipped)" << std::endl;
+
+ EXPECT_EQ(result.str(), expected.str());
+
+ DRW_shaders_free();
+}
+DRAW_TEST(draw_manager_sync)
+
+} // namespace blender::draw
diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc
index e7baac63aae..dc631483857 100644
--- a/source/blender/draw/tests/shaders_test.cc
+++ b/source/blender/draw/tests/shaders_test.cc
@@ -17,7 +17,7 @@
#include "engines/eevee/eevee_private.h"
#include "engines/gpencil/gpencil_engine.h"
#include "engines/image/image_private.hh"
-#include "engines/overlay/overlay_private.h"
+#include "engines/overlay/overlay_private.hh"
#include "engines/workbench/workbench_private.h"
#include "intern/draw_shader.h"
diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt
index 6adfab6e921..a72b2874f95 100644
--- a/source/blender/editors/animation/CMakeLists.txt
+++ b/source/blender/editors/animation/CMakeLists.txt
@@ -12,7 +12,6 @@ set(INC
../../sequencer
../../windowmanager
../../../../intern/clog
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index 729e8533d50..8edeea49cbb 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -43,6 +43,7 @@
#include "DNA_world_types.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "BKE_anim_data.h"
@@ -156,7 +157,7 @@ static void acf_generic_dataexpand_backdrop(bAnimContext *ac,
/* set backdrop drawing color */
acf->get_backdrop_color(ac, ale, color);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fv(color);
/* no rounded corner - just rectangular box */
@@ -245,7 +246,7 @@ static void acf_generic_channel_backdrop(bAnimContext *ac,
/* set backdrop drawing color */
acf->get_backdrop_color(ac, ale, color);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fv(color);
/* no rounded corners - just rectangular box */
@@ -4448,7 +4449,7 @@ void ANIM_channel_draw(
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* F-Curve channels need to have a special 'color code' box drawn,
* which is colored with whatever color the curve has stored.
@@ -4512,7 +4513,7 @@ void ANIM_channel_draw(
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* FIXME: replace hardcoded color here, and check on extents! */
immUniformColor3f(1.0f, 0.0f, 0.0f);
@@ -4548,7 +4549,7 @@ void ANIM_channel_draw(
float color[3];
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* get and set backdrop color */
acf->get_backdrop_color(ac, ale, color);
@@ -5352,8 +5353,8 @@ void ANIM_channel_draw_widgets(const bContext *C,
* and wouldn't be able to auto-keyframe.
* - Slider should start before the toggles (if they're visible)
* to keep a clean line down the side.
- * - Sliders are always drawn in Shapekey mode now. Prior to this
- * the SACTION_SLIDERS flag would be set when changing into Shapekey mode.
+ * - Sliders are always drawn in Shape-key mode now. Prior to this
+ * the SACTION_SLIDERS flag would be set when changing into shape-key mode.
*/
if (((draw_sliders) && ELEM(ale->type,
ANIMTYPE_FCURVE,
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 8464f280c29..ea631da27af 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -31,6 +31,7 @@
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_mask.h"
#include "BKE_nla.h"
@@ -1498,7 +1499,8 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op)
int filter;
/* get animdata blocks */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -1639,7 +1641,8 @@ static void animchannels_group_channels(bAnimContext *ac,
int filter;
/* find selected F-Curves to re-group */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, adt_ref, ANIMCONT_CHANNEL);
if (anim_data.first) {
@@ -1693,7 +1696,7 @@ static int animchannels_group_exec(bContext *C, wmOperator *op)
/* Handle each animdata block separately, so that the regrouping doesn't flow into blocks. */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA |
- ANIMFILTER_NODUPLIS);
+ ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -1753,7 +1756,7 @@ static int animchannels_ungroup_exec(bContext *C, wmOperator *UNUSED(op))
/* just selected F-Curves... */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_SEL |
- ANIMFILTER_NODUPLIS);
+ ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
@@ -2453,7 +2456,7 @@ static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op))
}
/* filter data */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* loop through filtered data and clean curves */
@@ -2951,6 +2954,7 @@ static int click_select_channel_object(bContext *C,
bAnimListElem *ale,
const short /* eEditKeyframes_Select or -1 */ selectmode)
{
+ Scene *scene = ac->scene;
ViewLayer *view_layer = ac->view_layer;
Base *base = (Base *)ale->data;
Object *ob = base->object;
@@ -2969,11 +2973,10 @@ static int click_select_channel_object(bContext *C,
}
}
else {
- Base *b;
-
/* deselect all */
+ BKE_view_layer_synced_ensure(scene, view_layer);
/* TODO: should this deselect all other types of channels too? */
- for (b = view_layer->object_bases.first; b; b = b->next) {
+ LISTBASE_FOREACH (Base *, b, BKE_view_layer_object_bases_get(view_layer)) {
ED_object_base_select(b, BA_DESELECT);
if (b->object->adt) {
b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
@@ -3258,10 +3261,14 @@ static int mouse_anim_channels(bContext *C,
bAnimListElem *ale;
int filter;
int notifierFlags = 0;
+ ScrArea *area = CTX_wm_area(C);
/* get the channel that was clicked on */
/* filter channels */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ if (ELEM(area->spacetype, SPACE_NLA, SPACE_GRAPH)) {
+ filter |= ANIMFILTER_FCURVESONLY;
+ }
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* get channel from index */
@@ -3453,7 +3460,8 @@ static bool select_anim_channel_keys(bAnimContext *ac, int channel_index, bool e
/* get the channel that was clicked on */
/* filter channels */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS |
+ ANIMFILTER_FCURVESONLY);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* get channel from index */
diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c
index d80eac2422e..22c14983569 100644
--- a/source/blender/editors/animation/anim_deps.c
+++ b/source/blender/editors/animation/anim_deps.c
@@ -32,6 +32,7 @@
#include "DEG_depsgraph.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "SEQ_sequencer.h"
#include "SEQ_utils.h"
@@ -356,7 +357,7 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data)
if (ale->update & ANIM_UPDATE_HANDLES) {
ale->update &= ~ANIM_UPDATE_HANDLES;
if (fcu) {
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
}
diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c
index d9dcbf1d57e..06a0077df9b 100644
--- a/source/blender/editors/animation/anim_draw.c
+++ b/source/blender/editors/animation/anim_draw.c
@@ -35,6 +35,7 @@
#include "ED_keyframes_keylist.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -59,7 +60,7 @@ void ANIM_draw_cfra(const bContext *C, View2D *v2d, short flag)
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw a light green line to indicate current frame */
immUniformThemeColor(TH_CFRAME);
@@ -86,7 +87,7 @@ void ANIM_draw_previewrange(const bContext *C, View2D *v2d, int end_frame_width)
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShadeAlpha(TH_ANIM_PREVIEW_RANGE, -25, -30);
/* XXX: Fix this hardcoded color (anim_active) */
// immUniformColor4f(0.8f, 0.44f, 0.1f, 0.2f);
@@ -117,7 +118,7 @@ void ANIM_draw_framerange(Scene *scene, View2D *v2d)
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShadeAlpha(TH_BACK, -25, -100);
if (scene->r.sfra < scene->r.efra) {
@@ -192,7 +193,7 @@ void ANIM_draw_action_framerange(
GPU_blend(GPU_BLEND_NONE);
/* Thin lines where the actual frames are. */
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShade(TH_BACK, -60);
GPU_line_width(1.0f);
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index e20932fa53e..5b4d436b0e0 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -118,11 +118,13 @@ static void animedit_get_yscale_factor(bAnimContext *ac)
/* NOTE: there's a similar function in key.c #BKE_key_from_object. */
static Key *actedit_get_shapekeys(bAnimContext *ac)
{
+ Scene *scene = ac->scene;
ViewLayer *view_layer = ac->view_layer;
Object *ob;
Key *key;
- ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ob = BKE_view_layer_active_object_get(view_layer);
if (ob == NULL) {
return NULL;
}
@@ -393,12 +395,13 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
/* get useful default context settings from context */
ac->bmain = bmain;
ac->scene = scene;
+ ac->view_layer = CTX_data_view_layer(C);
if (scene) {
ac->markers = ED_context_get_markers(C);
+ BKE_view_layer_synced_ensure(ac->scene, ac->view_layer);
}
- ac->view_layer = CTX_data_view_layer(C);
ac->depsgraph = CTX_data_depsgraph_pointer(C);
- ac->obact = (ac->view_layer->basact) ? ac->view_layer->basact->object : NULL;
+ ac->obact = BKE_view_layer_active_object_get(ac->view_layer);
ac->area = area;
ac->region = region;
ac->sl = sl;
@@ -1807,11 +1810,13 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data,
ListBase tmp_data = {NULL, NULL};
size_t tmp_items = 0;
- /* add gpencil animation channels */
- BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) {
- tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode);
+ if (!(filter_mode & ANIMFILTER_FCURVESONLY)) {
+ /* add gpencil animation channels */
+ BEGIN_ANIMFILTER_SUBCHANNELS (EXPANDED_GPD(gpd)) {
+ tmp_items += animdata_filter_gpencil_layers_data(&tmp_data, ads, gpd, filter_mode);
+ }
+ END_ANIMFILTER_SUBCHANNELS;
}
- END_ANIMFILTER_SUBCHANNELS;
/* did we find anything? */
if (tmp_items) {
@@ -1844,8 +1849,8 @@ static size_t animdata_filter_gpencil(bAnimContext *ac,
bDopeSheet *ads = ac->ads;
size_t items = 0;
+ Scene *scene = ac->scene;
ViewLayer *view_layer = (ViewLayer *)ac->view_layer;
- Base *base;
/* Include all annotation datablocks. */
if (((ads->filterflag & ADS_FILTER_ONLYSEL) == 0) ||
@@ -1857,7 +1862,8 @@ static size_t animdata_filter_gpencil(bAnimContext *ac,
}
}
/* Objects in the scene */
- for (base = view_layer->object_bases.first; base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
/* Only consider this object if it has got some GP data (saving on all the other tests) */
if (base->object && (base->object->type == OB_GPENCIL)) {
Object *ob = base->object;
@@ -1874,8 +1880,8 @@ static size_t animdata_filter_gpencil(bAnimContext *ac,
if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
/* Layer visibility - we check both object and base,
* since these may not be in sync yet. */
- if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0 ||
- (base->flag & BASE_VISIBLE_VIEWLAYER) == 0) {
+ if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0 ||
+ (base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) {
continue;
}
@@ -3087,7 +3093,8 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads,
*/
if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
/* layer visibility - we check both object and base, since these may not be in sync yet */
- if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0 || (base->flag & BASE_VISIBLE_VIEWLAYER) == 0) {
+ if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0 ||
+ (base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) {
return false;
}
@@ -3167,16 +3174,19 @@ static int ds_base_sorting_cmp(const void *base1_ptr, const void *base2_ptr)
/* Get a sorted list of all the bases - for inclusion in dopesheet (when drawing channels) */
static Base **animdata_filter_ds_sorted_bases(bDopeSheet *ads,
+ const Scene *scene,
ViewLayer *view_layer,
int filter_mode,
size_t *r_usable_bases)
{
/* Create an array with space for all the bases, but only containing the usable ones */
- size_t tot_bases = BLI_listbase_count(&view_layer->object_bases);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
+ size_t tot_bases = BLI_listbase_count(object_bases);
size_t num_bases = 0;
Base **sorted_bases = MEM_mallocN(sizeof(Base *) * tot_bases, "Dopesheet Usable Sorted Bases");
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, object_bases) {
if (animdata_filter_base_is_ok(ads, base, OB_MODE_OBJECT, filter_mode)) {
sorted_bases[num_bases++] = base;
}
@@ -3246,14 +3256,17 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac,
* - Don't do this if this behavior has been turned off (i.e. due to it being too slow)
* - Don't do this if there's just a single object
*/
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
if ((filter_mode & ANIMFILTER_LIST_CHANNELS) && !(ads->flag & ADS_FLAG_NO_DB_SORT) &&
- (view_layer->object_bases.first != view_layer->object_bases.last)) {
+ (object_bases->first != object_bases->last)) {
/* Filter list of bases (i.e. objects), sort them, then add their contents normally... */
/* TODO: Cache the old sorted order - if the set of bases hasn't changed, don't re-sort... */
Base **sorted_bases;
size_t num_bases;
- sorted_bases = animdata_filter_ds_sorted_bases(ads, view_layer, filter_mode, &num_bases);
+ sorted_bases = animdata_filter_ds_sorted_bases(
+ ads, scene, view_layer, filter_mode, &num_bases);
if (sorted_bases) {
/* Add the necessary channels for these bases... */
for (size_t i = 0; i < num_bases; i++) {
@@ -3270,9 +3283,9 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac,
/* Filter and add contents of each base (i.e. object) without them sorting first
* NOTE: This saves performance in cases where order doesn't matter
*/
- Object *obact = OBACT(view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, object_bases) {
if (animdata_filter_base_is_ok(ads, base, object_mode, filter_mode)) {
/* since we're still here, this object should be usable */
items += animdata_filter_dopesheet_ob(ac, anim_data, ads, base, filter_mode);
@@ -3404,9 +3417,8 @@ static size_t animdata_filter_remove_duplis(ListBase *anim_data)
GSet *gs;
size_t items = 0;
- /* build new hashtable to efficiently store and retrieve which entries have been
- * encountered already while searching
- */
+ /* Build new hash-table to efficiently store and retrieve which entries have been
+ * encountered already while searching. */
gs = BLI_gset_ptr_new(__func__);
/* loop through items, removing them from the list if a similar item occurs already */
diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c
index f01b3522547..93d83ff5f8e 100644
--- a/source/blender/editors/animation/anim_ipo_utils.c
+++ b/source/blender/editors/animation/anim_ipo_utils.c
@@ -22,6 +22,7 @@
#include "DNA_anim_types.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "ED_anim_api.h"
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index 03a2caf4b7d..94746837259 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -51,7 +51,6 @@
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "ED_transform.h"
-#include "ED_types.h"
#include "ED_util.h"
#include "DEG_depsgraph.h"
@@ -402,6 +401,7 @@ static void draw_marker_name(const uchar *text_color,
const uiFontStyle *fstyle,
TimeMarker *marker,
float marker_x,
+ float xmax,
float text_y)
{
const char *name = marker->name;
@@ -419,8 +419,16 @@ static void draw_marker_name(const uchar *text_color,
}
#endif
- int name_x = marker_x + UI_DPI_ICON_SIZE * 0.6;
- UI_fontstyle_draw_simple(fstyle, name_x, text_y, name, final_text_color);
+ const int icon_half_width = UI_DPI_ICON_SIZE * 0.6;
+ const struct uiFontStyleDraw_Params fs_params = {.align = UI_STYLE_TEXT_LEFT, .word_wrap = 0};
+ const struct rcti rect = {
+ .xmin = marker_x + icon_half_width,
+ .xmax = xmax - icon_half_width,
+ .ymin = text_y,
+ .ymax = text_y,
+ };
+
+ UI_fontstyle_draw(fstyle, &rect, name, strlen(name), final_text_color, &fs_params);
}
static void draw_marker_line(const uchar *color, int xpos, int ymin, int ymax)
@@ -428,7 +436,7 @@ static void draw_marker_line(const uchar *color, int xpos, int ymin, int ymax)
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -450,9 +458,7 @@ static void draw_marker_line(const uchar *color, int xpos, int ymin, int ymax)
static int marker_get_icon_id(TimeMarker *marker, int flag)
{
if (flag & DRAW_MARKERS_LOCAL) {
- return (marker->flag & ACTIVE) ? ICON_PMARKER_ACT :
- (marker->flag & SELECT) ? ICON_PMARKER_SEL :
- ICON_PMARKER;
+ return (marker->flag & SELECT) ? ICON_PMARKER_SEL : ICON_PMARKER;
}
#ifdef DURIAN_CAMERA_SWITCH
if (marker->camera) {
@@ -462,8 +468,13 @@ static int marker_get_icon_id(TimeMarker *marker, int flag)
return (marker->flag & SELECT) ? ICON_MARKER_HLT : ICON_MARKER;
}
-static void draw_marker(
- const uiFontStyle *fstyle, TimeMarker *marker, int cfra, int xpos, int flag, int region_height)
+static void draw_marker(const uiFontStyle *fstyle,
+ TimeMarker *marker,
+ int xpos,
+ int xmax,
+ int flag,
+ int region_height,
+ bool is_elevated)
{
uchar line_color[4], text_color[4];
@@ -479,18 +490,17 @@ static void draw_marker(
GPU_blend(GPU_BLEND_NONE);
float name_y = UI_DPI_FAC * 18;
- /* Give an offset to the marker name when selected,
- * or when near the current frame (5 frames range, starting from the current one). */
- if ((marker->flag & SELECT) || (cfra - 4 <= marker->frame && marker->frame <= cfra)) {
+ /* Give an offset to the marker that is elevated. */
+ if (is_elevated) {
name_y += UI_DPI_FAC * 10;
}
- draw_marker_name(text_color, fstyle, marker, xpos, name_y);
+ draw_marker_name(text_color, fstyle, marker, xpos, xmax, name_y);
}
static void draw_markers_background(rctf *rect)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
uchar shade[4];
UI_GetThemeColor4ubv(TH_TIME_SCRUB_BACKGROUND, shade);
@@ -532,6 +542,14 @@ static void get_marker_clip_frame_range(View2D *v2d, float xscale, int r_range[2
r_range[1] = v2d->cur.xmax + font_width_max;
}
+static int markers_frame_sort(const void *a, const void *b)
+{
+ const TimeMarker *marker_a = a;
+ const TimeMarker *marker_b = b;
+
+ return marker_a->frame > marker_b->frame;
+}
+
void ED_markers_draw(const bContext *C, int flag)
{
ListBase *markers = ED_context_get_markers(C);
@@ -561,22 +579,69 @@ void ED_markers_draw(const bContext *C, int flag)
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
- /* Separate loops in order to draw selected markers on top */
- LISTBASE_FOREACH (TimeMarker *, marker, markers) {
- if ((marker->flag & SELECT) == 0) {
- if (marker_is_in_frame_range(marker, clip_frame_range)) {
- draw_marker(fstyle, marker, cfra, marker->frame * xscale, flag, region->winy);
- }
+ /* Markers are not stored by frame order, so we need to sort it here. */
+ ListBase sorted_markers;
+
+ BLI_duplicatelist(&sorted_markers, markers);
+ BLI_listbase_sort(&sorted_markers, markers_frame_sort);
+
+ /**
+ * Set a temporary bit in the marker's flag to indicate that it should be elevated.
+ * This bit will be flipped back at the end of this function.
+ */
+ const int ELEVATED = 0x10;
+ LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) {
+ const bool is_elevated = (marker->flag & SELECT) ||
+ (cfra >= marker->frame &&
+ (marker->next == NULL || cfra < marker->next->frame));
+ SET_FLAG_FROM_TEST(marker->flag, is_elevated, ELEVATED);
+ }
+
+ /* Separate loops in order to draw selected markers on top. */
+
+ /**
+ * Draw non-elevated markers first.
+ * Note that unlike the elevated markers, these marker names will always be clipped by the
+ * proceeding marker. This is done because otherwise, the text overlaps with the icon of the
+ * marker itself.
+ */
+ LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) {
+ if ((marker->flag & ELEVATED) == 0 && marker_is_in_frame_range(marker, clip_frame_range)) {
+ const int xmax = marker->next ? marker->next->frame : clip_frame_range[1] + 1;
+ draw_marker(
+ fstyle, marker, marker->frame * xscale, xmax * xscale, flag, region->winy, false);
}
}
- LISTBASE_FOREACH (TimeMarker *, marker, markers) {
- if (marker->flag & SELECT) {
- if (marker_is_in_frame_range(marker, clip_frame_range)) {
- draw_marker(fstyle, marker, cfra, marker->frame * xscale, flag, region->winy);
- }
+
+ /* Now draw the elevated markers */
+ for (TimeMarker *marker = sorted_markers.first; marker != NULL;) {
+
+ /* Skip this marker if it is elevated or out of the frame range. */
+ if ((marker->flag & ELEVATED) == 0 || !marker_is_in_frame_range(marker, clip_frame_range)) {
+ marker = marker->next;
+ continue;
}
+
+ /* Find the next elevated marker. */
+ /* We use the next marker to determine how wide our text should be */
+ TimeMarker *next_marker = marker->next;
+ while (next_marker != NULL && (next_marker->flag & ELEVATED) == 0) {
+ next_marker = next_marker->next;
+ }
+
+ const int xmax = next_marker ? next_marker->frame : clip_frame_range[1] + 1;
+ draw_marker(fstyle, marker, marker->frame * xscale, xmax * xscale, flag, region->winy, true);
+
+ marker = next_marker;
+ }
+
+ /* Reset the elevated flag. */
+ LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) {
+ marker->flag &= ~ELEVATED;
}
+ BLI_freelistN(&sorted_markers);
+
GPU_matrix_pop();
}
@@ -1045,7 +1110,7 @@ static void MARKER_OT_move(wmOperatorType *ot)
RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
PropertyRNA *prop = RNA_def_boolean(
ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a click-drag event");
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
/** \} */
@@ -1190,14 +1255,14 @@ static int select_timeline_marker_frame(ListBase *markers,
deselect_markers(markers);
}
- LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_cycle_selected) {
+ LISTBASE_CIRCULAR_FORWARD_BEGIN (TimeMarker *, markers, marker, marker_cycle_selected) {
/* this way a not-extend select will always give 1 selected marker */
if (marker->frame == frame) {
marker->flag ^= SELECT;
break;
}
}
- LISTBASE_CIRCULAR_FORWARD_END(markers, marker, marker_cycle_selected);
+ LISTBASE_CIRCULAR_FORWARD_END(TimeMarker *, markers, marker, marker_cycle_selected);
}
return ret_val;
@@ -1216,7 +1281,7 @@ static void select_marker_camera_switch(
int sel = 0;
if (!extend) {
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
}
for (marker = markers->first; marker; marker = marker->next) {
@@ -1226,6 +1291,7 @@ static void select_marker_camera_switch(
}
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
for (marker = markers->first; marker; marker = marker->next) {
if (marker->camera) {
if (marker->frame == cfra) {
@@ -1313,7 +1379,7 @@ static void MARKER_OT_select(wmOperatorType *ot)
ot->modal = WM_generic_select_modal;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO;
WM_operator_properties_generic_select(ot);
prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
@@ -1413,7 +1479,7 @@ static void MARKER_OT_select_box(wmOperatorType *ot)
ot->poll = ed_markers_poll_markers_exist;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO;
/* properties */
WM_operator_properties_gesture_box(ot);
@@ -1537,8 +1603,8 @@ static void MARKER_OT_select_leftright(wmOperatorType *ot)
/* rna storage */
RNA_def_enum(
- ot->srna, "mode", prop_markers_select_leftright_modes, MARKERS_LRSEL_LEFT, "mode", "Mode");
- RNA_def_boolean(ot->srna, "extend", false, "extend", "Extend");
+ ot->srna, "mode", prop_markers_select_leftright_modes, MARKERS_LRSEL_LEFT, "Mode", "");
+ RNA_def_boolean(ot->srna, "extend", false, "Extend Select", "");
}
/** \} */
@@ -1588,12 +1654,13 @@ static void MARKER_OT_delete(wmOperatorType *ot)
ot->idname = "MARKER_OT_delete";
/* api callbacks */
- ot->invoke = WM_operator_confirm;
+ ot->invoke = WM_operator_confirm_or_exec;
ot->exec = ed_marker_delete_exec;
ot->poll = ed_markers_poll_selected_no_locked_markers;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ WM_operator_properties_confirm_or_exec(ot);
}
/** \} */
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index c6f68228807..63794caf5a7 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -39,6 +39,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "anim_intern.h"
@@ -124,7 +125,7 @@ struct FCurve *alloc_driver_fcurve(const char rna_path[],
insert_vert_fcurve(
fcu, 1.0f, 1.0f, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_FAST | INSERTKEY_NO_USERPREF);
fcu->extend = FCURVE_EXTRAPOLATE_LINEAR;
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
}
diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c
index 6f31472907b..d2f0ee622c4 100644
--- a/source/blender/editors/animation/fmodifier_ui.c
+++ b/source/blender/editors/animation/fmodifier_ui.c
@@ -1020,7 +1020,7 @@ bool ANIM_fmodifiers_paste_from_buf(ListBase *modifiers, bool replace, FCurve *c
/* adding or removing the Cycles modifier requires an update to handles */
if (curve && BKE_fcurve_is_cyclic(curve) != was_cyclic) {
- calchandles_fcurve(curve);
+ BKE_fcurve_handles_recalc(curve);
}
/* did we succeed? */
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 786204a52ed..6df9dc1e86d 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -608,7 +608,7 @@ static AnimKeylistDrawListElem *ed_keylist_draw_list_add_elem(
return draw_elem;
}
-/* *************************** Channel Drawing Funcs *************************** */
+/* *************************** Channel Drawing Functions *************************** */
void draw_summary_channel(struct AnimKeylistDrawList *draw_list,
bAnimContext *ac,
diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c
index 88207f7d514..2a94c5db439 100644
--- a/source/blender/editors/animation/keyframes_edit.c
+++ b/source/blender/editors/animation/keyframes_edit.c
@@ -218,7 +218,7 @@ static short ob_keyframes_loop(KeyframeEditData *ked,
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
- filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY;
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* Loop through each F-Curve, applying the operation as required,
@@ -267,7 +267,7 @@ static short scene_keyframes_loop(KeyframeEditData *ked,
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
- filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY;
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* Loop through each F-Curve, applying the operation as required,
@@ -444,7 +444,7 @@ void ANIM_animdata_keyframe_callback(bAnimContext *ac,
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
for (ale = anim_data.first; ale; ale = ale->next) {
- ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, callback_fn, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@@ -1303,7 +1303,8 @@ void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu,
/* Perform handle equalization if mode is 'Both' or 'Left'. */
if (mode & EQUALIZE_HANDLES_LEFT) {
- /*If left handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to 'Aligned'.*/
+ /* If left handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to 'Aligned'.
+ */
if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
bezt->h1 = HD_ALIGN;
bezt->h2 = HD_ALIGN;
@@ -1319,8 +1320,8 @@ void ANIM_fcurve_equalize_keyframes_loop(FCurve *fcu,
/* Perform handle equalization if mode is 'Both' or 'Right'. */
if (mode & EQUALIZE_HANDLES_RIGHT) {
- /*If right handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to
- * 'Aligned'.*/
+ /* If right handle type is 'Auto', 'Auto Clamped', or 'Vector', convert handles to
+ * 'Aligned'. */
if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) {
bezt->h1 = HD_ALIGN;
bezt->h2 = HD_ALIGN;
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index dd88752af14..40f0ac59b01 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -29,6 +29,7 @@
#include "RNA_access.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "ED_anim_api.h"
#include "ED_keyframes_edit.h"
@@ -47,77 +48,6 @@
/* **************************************************** */
-void delete_fcurve_key(FCurve *fcu, int index, bool do_recalc)
-{
- /* sanity check */
- if (fcu == NULL) {
- return;
- }
-
- /* verify the index:
- * 1) cannot be greater than the number of available keyframes
- * 2) negative indices are for specifying a value from the end of the array
- */
- if (abs(index) >= fcu->totvert) {
- return;
- }
- if (index < 0) {
- index += fcu->totvert;
- }
-
- /* Delete this keyframe */
- memmove(
- &fcu->bezt[index], &fcu->bezt[index + 1], sizeof(BezTriple) * (fcu->totvert - index - 1));
- fcu->totvert--;
-
- if (fcu->totvert == 0) {
- MEM_SAFE_FREE(fcu->bezt);
- }
-
- /* recalc handles - only if it won't cause problems */
- if (do_recalc) {
- calchandles_fcurve(fcu);
- }
-}
-
-bool delete_fcurve_keys(FCurve *fcu)
-{
- bool changed = false;
-
- if (fcu->bezt == NULL) { /* ignore baked curves */
- return false;
- }
-
- /* Delete selected BezTriples */
- for (int i = 0; i < fcu->totvert; i++) {
- if (fcu->bezt[i].f2 & SELECT) {
- if (i == fcu->active_keyframe_index) {
- BKE_fcurve_active_keyframe_set(fcu, NULL);
- }
- memmove(&fcu->bezt[i], &fcu->bezt[i + 1], sizeof(BezTriple) * (fcu->totvert - i - 1));
- fcu->totvert--;
- i--;
- changed = true;
- }
- }
-
- /* Free the array of BezTriples if there are not keyframes */
- if (fcu->totvert == 0) {
- clear_fcurve_keys(fcu);
- }
-
- return changed;
-}
-
-void clear_fcurve_keys(FCurve *fcu)
-{
- MEM_SAFE_FREE(fcu->bezt);
-
- fcu->totvert = 0;
-}
-
-/* ---------------- */
-
bool duplicate_fcurve_keys(FCurve *fcu)
{
bool changed = false;
@@ -282,7 +212,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo
}
if (fcu->bezt->vec[1][1] == default_value) {
- clear_fcurve_keys(fcu);
+ BKE_fcurve_delete_keys_all(fcu);
/* check if curve is really unused and if it is, return signal for deletion */
if (BKE_fcurve_is_empty(fcu)) {
@@ -679,7 +609,7 @@ void smooth_fcurve(FCurve *fcu)
}
/* recalculate handles */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* ---------------- */
@@ -762,7 +692,7 @@ void sample_fcurve(FCurve *fcu)
}
/* recalculate channel's handles? */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* **************************************************** */
@@ -1121,7 +1051,7 @@ static void paste_animedit_keys_fcurve(
case KEYFRAME_PASTE_MERGE_OVER:
/* remove all keys */
- clear_fcurve_keys(fcu);
+ BKE_fcurve_delete_keys_all(fcu);
break;
case KEYFRAME_PASTE_MERGE_OVER_RANGE:
@@ -1148,7 +1078,7 @@ static void paste_animedit_keys_fcurve(
}
/* remove frames in the range */
- delete_fcurve_keys(fcu);
+ BKE_fcurve_delete_keys_selected(fcu);
}
break;
}
@@ -1182,7 +1112,7 @@ static void paste_animedit_keys_fcurve(
}
/* recalculate F-Curve's handles? */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
const EnumPropertyItem rna_enum_keyframe_paste_offset_items[] = {
diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc
index 8dc598e6e2d..da266dd4253 100644
--- a/source/blender/editors/animation/keyframes_keylist.cc
+++ b/source/blender/editors/animation/keyframes_keylist.cc
@@ -943,7 +943,8 @@ void scene_to_keylist(bDopeSheet *ads, Scene *sce, AnimKeylist *keylist, const i
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
- const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY;
+
ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
@@ -980,7 +981,7 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, AnimKeylist *keylist, const int
ac.datatype = ANIMCONT_CHANNEL;
/* get F-Curves to take keyframes from */
- const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY;
ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
@@ -1015,7 +1016,7 @@ void cachefile_to_keylist(bDopeSheet *ads,
/* get F-Curves to take keyframes from */
ListBase anim_data = {nullptr, nullptr};
- const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE; /* curves only */
+ const eAnimFilter_Flags filter = ANIMFILTER_DATA_VISIBLE | ANIMFILTER_FCURVESONLY;
ANIM_animdata_filter(
&ac, &anim_data, filter, ac.data, static_cast<eAnimCont_Types>(ac.datatype));
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 2fa8907de71..acf53541843 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -64,6 +64,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "anim_intern.h"
@@ -639,7 +640,7 @@ int insert_vert_fcurve(
* - we may calculate twice (due to auto-handle needing to be calculated twice)
*/
if ((flag & INSERTKEY_FAST) == 0) {
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* return the index at which the keyframe was added */
@@ -1080,10 +1081,7 @@ static float *visualkey_get_values(
}
if (strstr(identifier, "rotation_quaternion")) {
- float mat3[3][3];
-
- copy_m3_m4(mat3, tmat);
- mat3_to_quat_is_ok(buffer, mat3);
+ mat4_to_quat(buffer, tmat);
*r_count = 4;
return buffer;
@@ -1282,10 +1280,12 @@ static bool insert_keyframe_value(ReportList *reports,
/* delete keyframe immediately before/after newly added */
switch (insert_mode) {
case KEYNEEDED_DELPREV:
- delete_fcurve_key(fcu, fcu->totvert - 2, 1);
+ BKE_fcurve_delete_key(fcu, fcu->totvert - 2);
+ BKE_fcurve_handles_recalc(fcu);
break;
case KEYNEEDED_DELNEXT:
- delete_fcurve_key(fcu, 1, 1);
+ BKE_fcurve_delete_key(fcu, 1);
+ BKE_fcurve_handles_recalc(fcu);
break;
}
@@ -1683,7 +1683,8 @@ static bool delete_keyframe_fcurve(AnimData *adt, FCurve *fcu, float cfra)
i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found);
if (found) {
/* delete the key at the index (will sanity check + do recalc afterwards) */
- delete_fcurve_key(fcu, i, 1);
+ BKE_fcurve_delete_key(fcu, i);
+ BKE_fcurve_handles_recalc(fcu);
/* Only delete curve too if it won't be doing anything anymore */
if (BKE_fcurve_is_empty(fcu)) {
@@ -2709,7 +2710,8 @@ static int delete_key_button_exec(bContext *C, wmOperator *op)
i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found);
if (found) {
/* delete the key at the index (will sanity check + do recalc afterwards) */
- delete_fcurve_key(fcu, i, 1);
+ BKE_fcurve_delete_key(fcu, i);
+ BKE_fcurve_handles_recalc(fcu);
changed = true;
}
}
diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c
index 97b81277008..97e90af019b 100644
--- a/source/blender/editors/animation/keyingsets.c
+++ b/source/blender/editors/animation/keyingsets.c
@@ -39,6 +39,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "anim_intern.h"
@@ -588,7 +589,7 @@ void ANIM_keyingset_info_unregister(Main *bmain, KeyingSetInfo *ksi)
/* find relevant builtin KeyingSets which use this, and remove them */
/* TODO: this isn't done now, since unregister is really only used at the moment when we
- * reload the scripts, which kindof defeats the purpose of "builtin"? */
+ * reload the scripts, which kind of defeats the purpose of "builtin"? */
for (ks = builtin_keyingsets.first; ks; ks = ksn) {
ksn = ks->next;
@@ -713,7 +714,7 @@ static void anim_keyingset_visit_for_search_impl(const bContext *C,
void *visit_user_data,
const bool use_poll)
{
- /* Poll requires context. */
+ /* Poll requires context. */
if (use_poll && (C == NULL)) {
return;
}
diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c
index 623d4e605ba..ebeac6552cd 100644
--- a/source/blender/editors/animation/time_scrub_ui.c
+++ b/source/blender/editors/animation/time_scrub_ui.c
@@ -48,7 +48,7 @@ static int get_centered_text_y(const rcti *rect)
static void draw_background(const rcti *rect)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_TIME_SCRUB_BACKGROUND);
@@ -97,7 +97,7 @@ static void draw_current_frame(const Scene *scene,
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPU_blend(GPU_BLEND_ALPHA);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Outline. */
immUniformThemeColorShadeAlpha(TH_BACK, -25, -100);
@@ -208,7 +208,7 @@ void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDope
rect.ymax = region->winy;
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_BACK);
immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
immUnbindProgram();
diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt
index 3ce5b70918d..243b2950e2e 100644
--- a/source/blender/editors/armature/CMakeLists.txt
+++ b/source/blender/editors/armature/CMakeLists.txt
@@ -14,7 +14,6 @@ set(INC
../../windowmanager
../../../../intern/clog
../../../../intern/eigen
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index 2071f056f9e..17484b2b0b7 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -541,7 +541,7 @@ static void updateDuplicateActionConstraintSettings(
}
BLI_freelistN(&ani_curves);
- /* Make deps graph aware of our changes */
+ /* Make depsgraph aware of our changes. */
DEG_id_tag_update(&act->id, ID_RECALC_ANIMATION_NO_FLUSH);
}
@@ -920,6 +920,7 @@ EditBone *duplicateEditBone(EditBone *cur_bone, const char *name, ListBase *edit
static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool do_flip_names = RNA_boolean_get(op->ptr, "do_flip_names");
@@ -930,7 +931,7 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
EditBone *ebone_iter;
/* The beginning of the duplicated bones in the edbo list */
@@ -1094,6 +1095,7 @@ static EditBone *get_symmetrized_bone(bArmature *arm, EditBone *bone)
*/
static int armature_symmetrize_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const int direction = RNA_enum_get(op->ptr, "direction");
const int axis = 0;
@@ -1105,7 +1107,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
bArmature *arm = obedit->data;
@@ -1349,13 +1351,14 @@ void ARMATURE_OT_symmetrize(wmOperatorType *ot)
/* if forked && mirror-edit: makes two bones with flipped names */
static int armature_extrude_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool forked = RNA_boolean_get(op->ptr, "forked");
bool changed_multi = false;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index 3c445f46902..81b1c096d76 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -254,6 +254,7 @@ static const EnumPropertyItem prop_calc_roll_types[] = {
static int armature_calc_roll_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob_active = CTX_data_edit_object(C);
int ret = OPERATOR_FINISHED;
@@ -267,7 +268,7 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -285,7 +286,6 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op)
invert_m3(imat);
if (type == CALC_ROLL_CURSOR) { /* Cursor */
- Scene *scene = CTX_data_scene(C);
float cursor_local[3];
const View3DCursor *cursor = &scene->cursor;
@@ -463,12 +463,13 @@ void ARMATURE_OT_calculate_roll(wmOperatorType *ot)
static int armature_roll_clear_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const float roll = RNA_float_get(op->ptr, "roll");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -712,7 +713,7 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op)
Object *obedit = NULL;
{
ViewLayer *view_layer = CTX_data_view_layer(C);
- FOREACH_OBJECT_IN_EDIT_MODE_BEGIN (view_layer, v3d, ob_iter) {
+ FOREACH_OBJECT_IN_EDIT_MODE_BEGIN (scene, view_layer, v3d, ob_iter) {
if (ob_iter->data == arm) {
obedit = ob_iter;
}
@@ -884,10 +885,11 @@ static void armature_clear_swap_done_flags(bArmature *arm)
static int armature_switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -1157,11 +1159,12 @@ void ARMATURE_OT_align(wmOperatorType *ot)
static int armature_split_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -1226,10 +1229,11 @@ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
bArmature *arm = obedit->data;
@@ -1299,13 +1303,14 @@ static bool armature_dissolve_ebone_cb(const char *bone_name, void *arm_p)
static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
EditBone *ebone, *ebone_next;
bool changed_multi = false;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
bArmature *arm = obedit->data;
@@ -1471,6 +1476,7 @@ void ARMATURE_OT_dissolve(wmOperatorType *ot)
static int armature_hide_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const int invert = RNA_boolean_get(op->ptr, "unselected") ? BONE_SELECTED : 0;
@@ -1481,7 +1487,7 @@ static int armature_hide_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
bArmature *arm = obedit->data;
@@ -1536,11 +1542,12 @@ void ARMATURE_OT_hide(wmOperatorType *ot)
static int armature_reveal_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool select = RNA_boolean_get(op->ptr, "select");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
bArmature *arm = obedit->data;
diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c
index 1f02e24666d..26ec05cc503 100644
--- a/source/blender/editors/armature/armature_naming.c
+++ b/source/blender/editors/armature/armature_naming.c
@@ -13,6 +13,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_armature_types.h"
+#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -281,6 +282,17 @@ void ED_armature_bone_rename(Main *bmain,
}
}
+ /* fix camera focus */
+ if (ob->type == OB_CAMERA) {
+ Camera *cam = (Camera *)ob->data;
+ if ((cam->dof.focus_object != NULL) && (cam->dof.focus_object->data == arm)) {
+ if (STREQ(cam->dof.focus_subtarget, oldname)) {
+ BLI_strncpy(cam->dof.focus_subtarget, newname, MAXBONENAME);
+ DEG_id_tag_update(&cam->id, ID_RECALC_COPY_ON_WRITE);
+ }
+ }
+ }
+
/* fix grease pencil modifiers and vertex groups */
if (ob->type == OB_GPENCIL) {
@@ -417,6 +429,7 @@ void ED_armature_bones_flip_names(Main *bmain,
static int armature_flip_names_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob_active = CTX_data_edit_object(C);
@@ -424,7 +437,7 @@ static int armature_flip_names_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -504,6 +517,7 @@ void ARMATURE_OT_flip_names(wmOperatorType *ot)
static int armature_autoside_names_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Main *bmain = CTX_data_main(C);
char newname[MAXBONENAME];
@@ -512,7 +526,7 @@ static int armature_autoside_names_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c
index 0825d6968c6..9f1883ccac0 100644
--- a/source/blender/editors/armature/armature_relations.c
+++ b/source/blender/editors/armature/armature_relations.c
@@ -610,7 +610,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op)
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &bases_len);
+ scene, view_layer, CTX_wm_view3d(C), &bases_len);
for (uint base_index = 0; base_index < bases_len; base_index++) {
Base *base_old = bases[base_index];
@@ -974,6 +974,7 @@ static void editbone_clear_parent(EditBone *ebone, int mode)
static int armature_parent_clear_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const int val = RNA_enum_get(op->ptr, "type");
@@ -984,7 +985,7 @@ static int armature_parent_clear_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c
index 08d5d6558e0..e490f21f16d 100644
--- a/source/blender/editors/armature/armature_select.c
+++ b/source/blender/editors/armature/armature_select.c
@@ -59,7 +59,7 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases,
const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
EditBone *ebone = NULL;
- /* TODO(campbell): optimize, eg: sort & binary search. */
+ /* TODO(@campbellbarton): optimize, eg: sort & binary search. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
if (bases[base_index]->object->runtime.select_id == hit_object) {
base = bases[base_index];
@@ -83,7 +83,7 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects,
const uint hit_object = select_id & 0xFFFF;
Object *ob = NULL;
EditBone *ebone = NULL;
- /* TODO(campbell): optimize, eg: sort & binary search. */
+ /* TODO(@campbellbarton): optimize, eg: sort & binary search. */
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
if (objects[ob_index]->runtime.select_id == hit_object) {
ob = objects[ob_index];
@@ -107,7 +107,7 @@ Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases,
const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
bPoseChannel *pchan = NULL;
- /* TODO(campbell): optimize, eg: sort & binary search. */
+ /* TODO(@campbellbarton): optimize, eg: sort & binary search. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
if (bases[base_index]->object->runtime.select_id == hit_object) {
base = bases[base_index];
@@ -339,15 +339,11 @@ static void *ed_armature_pick_bone_impl(
Base **bases;
if (vc.obedit != NULL) {
- bases = BKE_view_layer_array_from_bases_in_mode(vc.view_layer,
- vc.v3d,
- &bases_len,
- {
- .object_mode = OB_MODE_EDIT,
- });
+ bases = BKE_view_layer_array_from_bases_in_edit_mode(
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
}
else {
- bases = BKE_object_pose_base_array_get(vc.view_layer, vc.v3d, &bases_len);
+ bases = BKE_object_pose_base_array_get(vc.scene, vc.view_layer, vc.v3d, &bases_len);
}
void *bone = ed_armature_pick_bone_from_selectbuffer_impl(
@@ -499,10 +495,11 @@ static int armature_select_linked_exec(bContext *C, wmOperator *op)
const bool all_forks = RNA_boolean_get(op->ptr, "all_forks");
bool changed_multi = false;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -718,7 +715,7 @@ cache_end:
uint bases_len;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc->view_layer, vc->v3d, &bases_len);
+ vc->scene, vc->view_layer, vc->v3d, &bases_len);
/* See if there are any selected bones in this group */
if (hits > 0) {
@@ -935,7 +932,7 @@ bool ED_armature_edit_deselect_all_visible_multi(bContext *C)
ED_view3d_viewcontext_init(C, &vc, depsgraph);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &bases_len);
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
bool changed_multi = ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
return changed_multi;
@@ -953,6 +950,7 @@ bool ED_armature_edit_select_pick_bone(bContext *C,
const int selmask,
const struct SelectPick_Params *params)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
bool changed = false;
@@ -974,7 +972,7 @@ bool ED_armature_edit_select_pick_bone(bContext *C,
/* Deselect everything. */
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- view_layer, v3d, &bases_len);
+ scene, view_layer, v3d, &bases_len);
ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
changed = true;
@@ -1105,7 +1103,8 @@ bool ED_armature_edit_select_pick_bone(bContext *C,
arm->act_edbone = ebone;
}
- if (view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (BKE_view_layer_active_base_get(view_layer) != basact) {
ED_object_base_activate(C, basact);
}
@@ -1452,7 +1451,7 @@ static void armature_select_more_less(Object *ob, bool more)
bArmature *arm = (bArmature *)ob->data;
EditBone *ebone;
- /* XXX(campbell): eventually we shouldn't need this. */
+ /* XXX(@campbellbarton): eventually we shouldn't need this. */
ED_armature_edit_sync_selection(arm->edbo);
/* count bones & store selection state */
@@ -1494,10 +1493,11 @@ static void armature_select_more_less(Object *ob, bool more)
static int armature_de_select_more_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
armature_select_more_less(ob, true);
@@ -1533,10 +1533,11 @@ void ARMATURE_OT_select_more(wmOperatorType *ot)
static int armature_de_select_less_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
armature_select_more_less(ob, false);
@@ -1607,6 +1608,7 @@ static float bone_length_squared_worldspace_get(Object *ob, EditBone *ebone)
static void select_similar_length(bContext *C, const float thresh)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob_act = CTX_data_edit_object(C);
EditBone *ebone_act = CTX_data_active_bone(C);
@@ -1618,7 +1620,7 @@ static void select_similar_length(bContext *C, const float thresh)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -1657,6 +1659,7 @@ static void bone_direction_worldspace_get(Object *ob, EditBone *ebone, float *r_
static void select_similar_direction(bContext *C, const float thresh)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob_act = CTX_data_edit_object(C);
EditBone *ebone_act = CTX_data_active_bone(C);
@@ -1666,7 +1669,7 @@ static void select_similar_direction(bContext *C, const float thresh)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -1695,12 +1698,13 @@ static void select_similar_direction(bContext *C, const float thresh)
static void select_similar_layer(bContext *C)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
EditBone *ebone_act = CTX_data_active_bone(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -1725,6 +1729,7 @@ static void select_similar_layer(bContext *C)
static void select_similar_prefix(bContext *C)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
EditBone *ebone_act = CTX_data_active_bone(C);
@@ -1739,7 +1744,7 @@ static void select_similar_prefix(bContext *C)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -1767,6 +1772,7 @@ static void select_similar_prefix(bContext *C)
static void select_similar_suffix(bContext *C)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
EditBone *ebone_act = CTX_data_active_bone(C);
@@ -1781,7 +1787,7 @@ static void select_similar_suffix(bContext *C)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
@@ -2106,13 +2112,14 @@ void ARMATURE_OT_select_hierarchy(wmOperatorType *ot)
*/
static int armature_select_mirror_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool active_only = RNA_boolean_get(op->ptr, "only_active");
const bool extend = RNA_boolean_get(op->ptr, "extend");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
bArmature *arm = ob->data;
diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c
index 3a1e7419d3c..e39cc157c19 100644
--- a/source/blender/editors/armature/armature_skinning.c
+++ b/source/blender/editors/armature/armature_skinning.c
@@ -21,6 +21,7 @@
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_deform.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_iterators.h"
#include "BKE_mesh_runtime.h"
#include "BKE_modifier.h"
@@ -203,9 +204,9 @@ static void envelope_bone_weighting(Object *ob,
}
/* for each vertex in the mesh */
+ const MVert *mesh_verts = BKE_mesh_verts(mesh);
for (int i = 0; i < mesh->totvert; i++) {
-
- if (use_mask && !(mesh->mvert[i].flag & SELECT)) {
+ if (use_mask && !(mesh_verts[i].flag & SELECT)) {
continue;
}
@@ -405,9 +406,10 @@ static void add_verts_to_dgroups(ReportList *reports,
}
/* transform verts to global space */
+ const MVert *mesh_verts = BKE_mesh_verts(mesh);
for (int i = 0; i < mesh->totvert; i++) {
if (!vertsfilled) {
- copy_v3_v3(verts[i], mesh->mvert[i].co);
+ copy_v3_v3(verts[i], mesh_verts[i].co);
}
mul_m4_v3(ob->obmat, verts[i]);
}
diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c
index bcf8b7cff99..379ad4f5376 100644
--- a/source/blender/editors/armature/editarmature_undo.c
+++ b/source/blender/editors/armature/editarmature_undo.c
@@ -97,8 +97,10 @@ static void undoarm_free_data(UndoArmature *uarm)
static Object *editarm_object_from_context(bContext *C)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit && obedit->type == OB_ARMATURE) {
bArmature *arm = obedit->data;
if (arm->edbo != NULL) {
@@ -139,9 +141,10 @@ static bool armature_undosys_step_encode(struct bContext *C, struct Main *bmain,
/* Important not to use the 3D view when getting objects because all objects
* outside of this list will be moved out of edit-mode when reading back undo steps. */
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
- Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len);
+ Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
us->elems_len = objects_len;
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index 7016511111e..904e6213466 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -645,14 +645,16 @@ void heat_bone_weighting(Object *ob,
{
LaplacianSystem *sys;
MLoopTri *mlooptri;
- MPoly *mp;
- MLoop *ml;
+ const MPoly *mp;
+ const MLoop *ml;
float solution, weight;
int *vertsflipped = NULL, *mask = NULL;
int a, tris_num, j, bbone, firstsegment, lastsegment;
bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
- MVert *mvert = me->mvert;
+ const MVert *mesh_verts = BKE_mesh_verts(me);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
@@ -667,16 +669,16 @@ void heat_bone_weighting(Object *ob,
/* (added selectedVerts content for vertex mask, they used to just equal 1) */
if (use_vert_sel) {
- for (a = 0, mp = me->mpoly; a < me->totpoly; mp++, a++) {
- for (j = 0, ml = me->mloop + mp->loopstart; j < mp->totloop; j++, ml++) {
- mask[ml->v] = (mvert[ml->v].flag & SELECT) != 0;
+ for (a = 0, mp = polys; a < me->totpoly; mp++, a++) {
+ for (j = 0, ml = loops + mp->loopstart; j < mp->totloop; j++, ml++) {
+ mask[ml->v] = (mesh_verts[ml->v].flag & SELECT) != 0;
}
}
}
else if (use_face_sel) {
- for (a = 0, mp = me->mpoly; a < me->totpoly; mp++, a++) {
+ for (a = 0, mp = polys; a < me->totpoly; mp++, a++) {
if (mp->flag & ME_FACE_SEL) {
- for (j = 0, ml = me->mloop + mp->loopstart; j < mp->totloop; j++, ml++) {
+ for (j = 0, ml = loops + mp->loopstart; j < mp->totloop; j++, ml++) {
mask[ml->v] = 1;
}
}
@@ -690,10 +692,10 @@ void heat_bone_weighting(Object *ob,
sys->heat.tris_num = poly_to_tri_count(me->totpoly, me->totloop);
mlooptri = MEM_mallocN(sizeof(*sys->heat.mlooptri) * sys->heat.tris_num, __func__);
- BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mlooptri);
+ BKE_mesh_recalc_looptri(loops, polys, mesh_verts, me->totloop, me->totpoly, mlooptri);
sys->heat.mlooptri = mlooptri;
- sys->heat.mloop = me->mloop;
+ sys->heat.mloop = loops;
sys->heat.verts_num = me->totvert;
sys->heat.verts = verts;
sys->heat.root = root;
@@ -1606,8 +1608,8 @@ static void harmonic_coordinates_bind(MeshDeformModifierData *mmd, MeshDeformBin
/* initialize data from 'cagedm' for reuse */
{
Mesh *me = mdb->cagemesh;
- mdb->cagemesh_cache.mpoly = me->mpoly;
- mdb->cagemesh_cache.mloop = me->mloop;
+ mdb->cagemesh_cache.mpoly = BKE_mesh_polys(me);
+ mdb->cagemesh_cache.mloop = BKE_mesh_loops(me);
mdb->cagemesh_cache.looptri = BKE_mesh_runtime_looptri_ensure(me);
mdb->cagemesh_cache.poly_nors = BKE_mesh_poly_normals_ensure(me);
}
@@ -1743,7 +1745,7 @@ void ED_mesh_deform_bind_callback(Object *object,
MeshDeformModifierData *mmd_orig = (MeshDeformModifierData *)BKE_modifier_get_original(
object, &mmd->modifier);
MeshDeformBind mdb;
- MVert *mvert;
+ const MVert *mvert;
int a;
waitcursor(1);
@@ -1763,7 +1765,7 @@ void ED_mesh_deform_bind_callback(Object *object,
mdb.cagecos = MEM_callocN(sizeof(*mdb.cagecos) * mdb.cage_verts_num, "MeshDeformBindCos");
copy_m4_m4(mdb.cagemat, cagemat);
- mvert = mdb.cagemesh->mvert;
+ mvert = BKE_mesh_verts(mdb.cagemesh);
for (a = 0; a < mdb.cage_verts_num; a++) {
copy_v3_v3(mdb.cagecos[a], mvert[a].co);
}
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index cec83ffa0f0..6a64c70493a 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -499,11 +499,12 @@ void POSE_OT_paths_range_update(wmOperatorType *ot)
static int pose_flip_names_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
const bool do_strip_numbers = RNA_boolean_get(op->ptr, "do_strip_numbers");
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
bArmature *arm = ob->data;
ListBase bones_names = {NULL};
@@ -856,9 +857,10 @@ static int pose_bone_layers_exec(bContext *C, wmOperator *op)
struct Main *bmain = CTX_data_main(C);
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
bArmature *arm = ob_iter->data;
BKE_pose_ensure(bmain, ob_iter, arm, true);
}
@@ -1001,9 +1003,11 @@ static int hide_pose_bone_fn(Object *ob, Bone *bone, void *ptr)
/* active object is armature in posemode, poll checked */
static int pose_hide_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
- Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len);
+ Object **objects = BKE_object_pose_array_get_unique(
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
bool changed_multi = false;
const int hide_select = !RNA_boolean_get(op->ptr, "unselected");
@@ -1066,9 +1070,11 @@ static int show_pose_bone_cb(Object *ob, Bone *bone, void *data)
/* active object is armature in posemode, poll checked */
static int pose_reveal_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
- Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len);
+ Object **objects = BKE_object_pose_array_get_unique(
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
bool changed_multi = false;
const bool select = RNA_boolean_get(op->ptr, "select");
void *select_p = POINTER_FROM_INT(select);
@@ -1118,7 +1124,7 @@ static int pose_flip_quats_exec(bContext *C, wmOperator *UNUSED(op))
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
bool changed = false;
/* loop through all selected pchans, flipping and keying (as needed) */
FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) {
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index b8e7c2624fd..ff187a52154 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -24,6 +24,7 @@
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
+#include "BKE_fcurve.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
@@ -615,7 +616,8 @@ static int poselib_remove_exec(bContext *C, wmOperator *op)
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
/* check if remove */
if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) {
- delete_fcurve_key(fcu, i, 1);
+ BKE_fcurve_delete_key(fcu, i);
+ BKE_fcurve_handles_recalc(fcu);
break;
}
}
@@ -1578,7 +1580,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con
case EVT_PADMINUS:
if (pld->searchstr[0]) {
/* searching... */
- poselib_preview_handle_search(pld, event->type, event->ascii);
+ poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event));
}
else {
/* view manipulation (see above) */
@@ -1589,7 +1591,7 @@ static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, con
/* otherwise, assume that searching might be able to handle it */
default:
- poselib_preview_handle_search(pld, event->type, event->ascii);
+ poselib_preview_handle_search(pld, event->type, WM_event_utf8_to_ascii(event));
break;
}
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index b6b5d3ee495..6a31c7f1496 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -121,7 +121,8 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select)
}
}
-bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
+bool ED_armature_pose_select_pick_bone(const Scene *scene,
+ ViewLayer *view_layer,
View3D *v3d,
Object *ob,
Bone *bone,
@@ -144,7 +145,7 @@ bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
/* Deselect everything. */
/* 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) {
+ FOREACH_VISIBLE_BASE_BEGIN (scene, 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)) {
@@ -158,15 +159,16 @@ bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
}
if (found) {
- Object *ob_act = OBACT(view_layer);
- BLI_assert(OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob_act = BKE_view_layer_active_object_get(view_layer);
+ BLI_assert(BKE_view_layer_edit_object_get(view_layer) == NULL);
/* If the bone cannot be affected, don't do anything. */
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(campbell): special exception for armature mode so we can do multi-select
+ * NOTE(@campbellbarton): 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. */
if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) {
@@ -243,7 +245,8 @@ bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer,
return changed || found;
}
-bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
+bool ED_armature_pose_select_pick_with_buffer(const Scene *scene,
+ ViewLayer *view_layer,
View3D *v3d,
Base *base,
const struct GPUSelectResult *buffer,
@@ -263,13 +266,16 @@ bool ED_armature_pose_select_pick_with_buffer(ViewLayer *view_layer,
nearBone = ED_armature_pick_bone_from_selectbuffer(
&base, 1, buffer, hits, 1, do_nearest, &base_dummy);
- return ED_armature_pose_select_pick_bone(view_layer, v3d, ob, nearBone, params);
+ return ED_armature_pose_select_pick_bone(scene, view_layer, v3d, ob, nearBone, params);
}
-void ED_armature_pose_select_in_wpaint_mode(ViewLayer *view_layer, Base *base_select)
+void ED_armature_pose_select_in_wpaint_mode(const Scene *scene,
+ ViewLayer *view_layer,
+ Base *base_select)
{
BLI_assert(base_select && (base_select->object->type == OB_ARMATURE));
- Object *ob_active = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob_active = BKE_view_layer_active_object_get(view_layer);
BLI_assert(ob_active && (ob_active->mode & OB_MODE_ALL_WEIGHT_PAINT));
if (ob_active->type == OB_GPENCIL) {
@@ -401,7 +407,8 @@ bool ED_pose_deselect_all_multi(bContext *C, int select_mode, const bool ignore_
ED_view3d_viewcontext_init(C, &vc, depsgraph);
uint bases_len = 0;
- Base **bases = BKE_object_pose_base_array_get_unique(vc.view_layer, vc.v3d, &bases_len);
+ Base **bases = BKE_object_pose_base_array_get_unique(
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
bool changed_multi = ED_pose_deselect_all_multi_ex(
bases, bases_len, select_mode, ignore_visibility);
MEM_freeN(bases);
@@ -844,6 +851,7 @@ typedef enum ePose_SelectSame_Mode {
static bool pose_select_same_group(bContext *C, bool extend)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
bool *group_flags_array;
bool *group_flags = NULL;
@@ -853,7 +861,8 @@ static bool pose_select_same_group(bContext *C, bool extend)
uint ob_index;
uint objects_len = 0;
- Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len);
+ Object **objects = BKE_object_pose_array_get_unique(
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = BKE_object_pose_armature_get(objects[ob_index]);
@@ -947,6 +956,7 @@ static bool pose_select_same_group(bContext *C, bool extend)
static bool pose_select_same_layer(bContext *C, bool extend)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int *layers_array, *layers = NULL;
Object *ob_prev = NULL;
@@ -954,7 +964,8 @@ static bool pose_select_same_layer(bContext *C, bool extend)
bool changed = false;
uint objects_len = 0;
- Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len);
+ Object **objects = BKE_object_pose_array_get_unique(
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -1032,6 +1043,7 @@ cleanup:
static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool extend)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
bool changed_multi = false;
KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C));
@@ -1068,7 +1080,8 @@ static bool pose_select_same_keyingset(bContext *C, ReportList *reports, bool ex
}
uint objects_len = 0;
- Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len);
+ Object **objects = BKE_object_pose_array_get_unique(
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = BKE_object_pose_armature_get(objects[ob_index]);
@@ -1196,6 +1209,7 @@ void POSE_OT_select_grouped(wmOperatorType *ot)
*/
static int pose_select_mirror_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob_active = CTX_data_active_object(C);
@@ -1204,7 +1218,8 @@ static int pose_select_mirror_exec(bContext *C, wmOperator *op)
const bool extend = RNA_boolean_get(op->ptr, "extend");
uint objects_len = 0;
- Object **objects = BKE_object_pose_array_get_unique(view_layer, CTX_wm_view3d(C), &objects_len);
+ Object **objects = BKE_object_pose_array_get_unique(
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index 38c99c2ef60..14b3451bd80 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -53,6 +53,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "WM_api.h"
@@ -231,8 +232,11 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode)
* and set the relevant transform flags. */
poseAnim_mapping_get(C, &pso->pfLinks);
- Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
- CTX_data_view_layer(C), CTX_wm_view3d(C), &pso->objects_len, OB_MODE_POSE);
+ Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(CTX_data_scene(C),
+ CTX_data_view_layer(C),
+ CTX_wm_view3d(C),
+ &pso->objects_len,
+ OB_MODE_POSE);
pso->ob_data_array = MEM_callocN(pso->objects_len * sizeof(tPoseSlideObject),
"pose slide objects data");
@@ -285,8 +289,10 @@ static void pose_slide_exit(bContext *C, wmOperator *op)
ED_slider_destroy(C, pso->slider);
/* Hide Bone Overlay. */
- View3D *v3d = pso->area->spacedata.first;
- v3d->overlay.flag = pso->overlay_flag;
+ if (pso->area) {
+ View3D *v3d = pso->area->spacedata.first;
+ v3d->overlay.flag = pso->overlay_flag;
+ }
/* Free the temp pchan links and their data. */
poseAnim_mapping_free(&pso->pfLinks);
@@ -2078,7 +2084,7 @@ static int pose_propagate_exec(bContext *C, wmOperator *op)
}
/* Updates + notifiers. */
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
poseAnim_mapping_refresh(C, scene, ob);
}
FOREACH_OBJECT_IN_MODE_END;
diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c
index cfc6b0b6b6e..2a23615caa3 100644
--- a/source/blender/editors/armature/pose_transform.c
+++ b/source/blender/editors/armature/pose_transform.c
@@ -491,13 +491,14 @@ void POSE_OT_armature_apply(wmOperatorType *ot)
/* set the current pose as the restpose */
static int pose_visual_transform_apply_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
/* Needed to ensure #bPoseChannel.pose_mat are up to date. */
CTX_data_ensure_evaluated_depsgraph(C);
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
const bArmature *arm = ob->data;
int chanbase_len = BLI_listbase_count(&ob->pose->chanbase);
@@ -1115,7 +1116,7 @@ static void pchan_clear_rot(bPoseChannel *pchan)
}
/* Clear also Bendy Bone stuff - Roll is obvious,
- * but Curve X/Y stuff is also kindof rotational in nature... */
+ * but Curve X/Y stuff is also kind of rotational in nature... */
pchan->roll1 = 0.0f;
pchan->roll2 = 0.0f;
@@ -1168,7 +1169,7 @@ static int pose_clear_transform_generic_exec(bContext *C,
/* only clear relevant transforms for selected bones */
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
/* XXX: UGLY HACK (for auto-key + clear transforms). */
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter);
ListBase dsources = {NULL, NULL};
@@ -1347,7 +1348,7 @@ static int pose_clear_user_transforms_exec(bContext *C, wmOperator *op)
depsgraph, (float)scene->r.cfra);
const bool only_select = RNA_boolean_get(op->ptr, "only_selected");
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
if ((ob->adt) && (ob->adt->action)) {
/* XXX: this is just like this to avoid contaminating anything else;
* just pose values should change, so this should be fine
diff --git a/source/blender/editors/armature/pose_utils.c b/source/blender/editors/armature/pose_utils.c
index 032e0ec077c..57ead6a0e65 100644
--- a/source/blender/editors/armature/pose_utils.c
+++ b/source/blender/editors/armature/pose_utils.c
@@ -26,6 +26,7 @@
#include "DEG_depsgraph.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "WM_api.h"
@@ -250,7 +251,7 @@ void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks,
View3D *v3d = CTX_wm_view3d(C);
bool skip = true;
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
ob->id.tag &= ~LIB_TAG_DOIT;
ob = poseAnim_object_get(ob);
@@ -298,7 +299,7 @@ void poseAnim_mapping_autoKeyframe(bContext *C, Scene *scene, ListBase *pfLinks,
* - only do this if keyframes should have been added
* - do not calculate unless there are paths already to update...
*/
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob) {
if (ob->id.tag & LIB_TAG_DOIT) {
if (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) {
// ED_pose_clear_paths(C, ob); /* XXX for now, don't need to clear. */
diff --git a/source/blender/editors/asset/ED_asset_handle.h b/source/blender/editors/asset/ED_asset_handle.h
index b408e919f32..4a81840c40d 100644
--- a/source/blender/editors/asset/ED_asset_handle.h
+++ b/source/blender/editors/asset/ED_asset_handle.h
@@ -35,3 +35,16 @@ void ED_asset_handle_get_full_library_path(const struct bContext *C,
#ifdef __cplusplus
}
#endif
+
+#ifdef __cplusplus
+
+namespace blender::ed::asset {
+
+/** If the ID already exists in the database, return it, otherwise add it. */
+ID *get_local_id_from_asset_or_append_and_reuse(Main &bmain,
+ const AssetLibraryReference &library_ref,
+ AssetHandle asset);
+
+} // namespace blender::ed::asset
+
+#endif
diff --git a/source/blender/editors/asset/ED_asset_list.h b/source/blender/editors/asset/ED_asset_list.h
index 2dc67fc4d37..b54f81004f2 100644
--- a/source/blender/editors/asset/ED_asset_list.h
+++ b/source/blender/editors/asset/ED_asset_list.h
@@ -24,7 +24,7 @@ struct wmNotifier;
void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference,
const struct bContext *C);
void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference,
- struct bContext *C);
+ const struct bContext *C);
void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C);
bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference);
/**
diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc
index ade8b803ed3..00fffd595c0 100644
--- a/source/blender/editors/asset/intern/asset_handle.cc
+++ b/source/blender/editors/asset/intern/asset_handle.cc
@@ -13,6 +13,8 @@
#include "ED_asset_handle.h"
#include "ED_asset_list.hh"
+#include "WM_api.h"
+
const char *ED_asset_handle_get_name(const AssetHandle *asset)
{
return asset->file_data->name;
@@ -52,3 +54,31 @@ void ED_asset_handle_get_full_library_path(const bContext *C,
BLO_library_path_explode(asset_path.c_str(), r_full_lib_path, nullptr, nullptr);
}
+
+namespace blender::ed::asset {
+
+ID *get_local_id_from_asset_or_append_and_reuse(Main &bmain,
+ const AssetLibraryReference &library_ref,
+ const AssetHandle asset)
+{
+ if (ID *local_id = ED_asset_handle_get_local_id(&asset)) {
+ return local_id;
+ }
+
+ char blend_path[FILE_MAX_LIBEXTRA];
+ ED_asset_handle_get_full_library_path(nullptr, &library_ref, &asset, blend_path);
+ const char *id_name = ED_asset_handle_get_name(&asset);
+
+ return WM_file_append_datablock(&bmain,
+ nullptr,
+ nullptr,
+ nullptr,
+ blend_path,
+ ED_asset_handle_get_id_type(&asset),
+ id_name,
+ BLO_LIBLINK_APPEND_RECURSIVE |
+ BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR |
+ BLO_LIBLINK_APPEND_LOCAL_ID_REUSE);
+}
+
+} // namespace blender::ed::asset
diff --git a/source/blender/editors/asset/intern/asset_indexer.cc b/source/blender/editors/asset/intern/asset_indexer.cc
index 3cc3638c299..cc06fa80429 100644
--- a/source/blender/editors/asset/intern/asset_indexer.cc
+++ b/source/blender/editors/asset/intern/asset_indexer.cc
@@ -351,7 +351,7 @@ static void init_indexer_entry_from_value(FileIndexerEntry &indexer_entry,
{
indexer_entry.idcode = entry.get_idcode();
- const std::string &name = entry.get_name();
+ const std::string name = entry.get_name();
BLI_strncpy(
indexer_entry.datablock_info.name, name.c_str(), sizeof(indexer_entry.datablock_info.name));
@@ -359,19 +359,19 @@ static void init_indexer_entry_from_value(FileIndexerEntry &indexer_entry,
indexer_entry.datablock_info.asset_data = asset_data;
if (entry.has_description()) {
- const std::string &description = entry.get_description();
- char *description_c_str = static_cast<char *>(MEM_mallocN(description.length() + 1, __func__));
- BLI_strncpy(description_c_str, description.c_str(), description.length() + 1);
+ const StringRefNull description = entry.get_description();
+ char *description_c_str = static_cast<char *>(MEM_mallocN(description.size() + 1, __func__));
+ BLI_strncpy(description_c_str, description.c_str(), description.size() + 1);
asset_data->description = description_c_str;
}
if (entry.has_author()) {
- const std::string &author = entry.get_author();
- char *author_c_str = static_cast<char *>(MEM_mallocN(author.length() + 1, __func__));
- BLI_strncpy(author_c_str, author.c_str(), author.length() + 1);
+ const StringRefNull author = entry.get_author();
+ char *author_c_str = static_cast<char *>(MEM_mallocN(author.size() + 1, __func__));
+ BLI_strncpy(author_c_str, author.c_str(), author.size() + 1);
asset_data->author = author_c_str;
}
- const std::string &catalog_name = entry.get_catalog_name();
+ const StringRefNull catalog_name = entry.get_catalog_name();
BLI_strncpy(asset_data->catalog_simple_name,
catalog_name.c_str(),
sizeof(asset_data->catalog_simple_name));
diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc
index 67e253a4fcd..773838a54cd 100644
--- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc
+++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc
@@ -97,10 +97,8 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf(
RNA_enum_item_add_separator(&item, &totitem);
}
- int i = 0;
- for (bUserAssetLibrary *user_library = (bUserAssetLibrary *)U.asset_libraries.first;
- user_library;
- user_library = user_library->next, i++) {
+ int i;
+ LISTBASE_FOREACH_INDEX (bUserAssetLibrary *, user_library, &U.asset_libraries, i) {
/* Note that the path itself isn't checked for validity here. If an invalid library path is
* used, the Asset Browser can give a nice hint on what's wrong. */
const bool is_valid = (user_library->name[0] && user_library->path[0]);
diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc
index 55167c1ed2d..b0ff5c86520 100644
--- a/source/blender/editors/asset/intern/asset_list.cc
+++ b/source/blender/editors/asset/intern/asset_list.cc
@@ -110,7 +110,7 @@ class AssetList : NonCopyable {
void setup();
void fetch(const bContext &C);
- void ensurePreviewsJob(bContext *C);
+ void ensurePreviewsJob(const bContext *C);
void clear(bContext *C);
bool needsRefetch() const;
@@ -212,7 +212,7 @@ void AssetList::iterate(AssetListIterFn fn) const
}
}
-void AssetList::ensurePreviewsJob(bContext *C)
+void AssetList::ensurePreviewsJob(const bContext *C)
{
FileList *files = filelist_;
int numfiles = filelist_files_ensure(files);
@@ -422,7 +422,8 @@ void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference,
AssetListStorage::fetch_library(*library_reference, *C);
}
-void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, bContext *C)
+void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference,
+ const bContext *C)
{
AssetList *list = AssetListStorage::lookup_list(*library_reference);
diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index 619a873909a..ba7b56db3ec 100644
--- a/source/blender/editors/asset/intern/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -295,7 +295,7 @@ void AssetClearHelper::reportResults(const bContext *C, ReportList &reports) con
else if (stats.tot_cleared == 1) {
/* If only one data-block: Give more useful message by printing asset name. */
BKE_reportf(
- &reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats.last_id->name + 2);
+ &reports, RPT_INFO, "Data-block '%s' is not an asset anymore", stats.last_id->name + 2);
}
else {
BKE_reportf(&reports, RPT_INFO, "%i data-blocks are no assets anymore", stats.tot_cleared);
@@ -781,6 +781,7 @@ static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext *UNUSE
const EnumPropertyItem *items = ED_asset_library_reference_to_rna_enum_itemf(false);
if (!items) {
*r_free = false;
+ return nullptr;
}
*r_free = true;
diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt
index 791e28de694..0cedc05981b 100644
--- a/source/blender/editors/curve/CMakeLists.txt
+++ b/source/blender/editors/curve/CMakeLists.txt
@@ -11,7 +11,6 @@ set(INC
../../makesrna
../../windowmanager
../../../../intern/clog
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
../../../../extern/curve_fit_nd
# RNA_prototypes.h
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 24302aca59b..5e810d93115 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -47,7 +47,6 @@
#include "ED_select_utils.h"
#include "ED_transform.h"
#include "ED_transform_snap_object_context.h"
-#include "ED_types.h"
#include "ED_view3d.h"
#include "curve_intern.h"
@@ -1255,7 +1254,7 @@ void ED_curve_editnurb_load(Main *bmain, Object *obedit)
}
/* We have to pass also new copied nurbs, since we want to restore original curve
- * (without edited shapekey) on obdata, but *not* on editcurve itself
+ * (without edited shape-key) on obdata, but *not* on editcurve itself
* (ED_curve_editnurb_load call does not always implies freeing
* of editcurve, e.g. when called to generate render data). */
calc_shapeKeys(obedit, &newnurb);
@@ -1279,7 +1278,7 @@ void ED_curve_editnurb_make(Object *obedit)
if (actkey) {
// XXX strcpy(G.editModeTitleExtra, "(Key) ");
- /* TODO(campbell): undo_system: investigate why this was needed. */
+ /* TODO(@campbellbarton): undo_system: investigate why this was needed. */
#if 0
undo_editmode_clear();
#endif
@@ -1299,15 +1298,15 @@ void ED_curve_editnurb_make(Object *obedit)
BLI_addtail(&editnurb->nurbs, newnu);
}
- /* animation could be added in editmode even if there was no animdata in
- * object mode hence we always need CVs index be created */
+ /* Animation could be added in edit-mode even if there was no animdata in
+ * object mode hence we always need CVs index be created. */
init_editNurb_keyIndex(editnurb, &cu->nurb);
if (actkey) {
editnurb->shapenr = obedit->shapenr;
- /* Apply shapekey to new nurbs of editnurb, not those of original curve
+ /* Apply shape-key to new nurbs of editnurb, not those of original curve
* (and *after* we generated keyIndex), else we do not have valid 'original' data
- * to properly restore curve when leaving editmode. */
+ * to properly restore curve when leaving edit-mode. */
BKE_keyblock_convert_to_curve(actkey, cu, &editnurb->nurbs);
}
}
@@ -1344,7 +1343,7 @@ static int separate_exec(bContext *C, wmOperator *op)
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &bases_len);
+ scene, view_layer, CTX_wm_view3d(C), &bases_len);
for (uint b_index = 0; b_index < bases_len; b_index++) {
Base *oldbase = bases[b_index];
Base *newbase;
@@ -1469,6 +1468,7 @@ void CURVE_OT_separate(wmOperatorType *ot)
static int curve_split_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
bool changed = false;
@@ -1476,7 +1476,7 @@ static int curve_split_exec(bContext *C, wmOperator *op)
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -1541,67 +1541,6 @@ void CURVE_OT_split(wmOperatorType *ot)
/** \name Flag Utility Functions
* \{ */
-static bool isNurbselUV(const Nurb *nu, uint8_t flag, int *r_u, int *r_v)
-{
- /* return (u != -1): 1 row in u-direction selected. U has value between 0-pntsv
- * return (v != -1): 1 column in v-direction selected. V has value between 0-pntsu
- */
- BPoint *bp;
- int a, b, sel;
-
- *r_u = *r_v = -1;
-
- bp = nu->bp;
- for (b = 0; b < nu->pntsv; b++) {
- sel = 0;
- for (a = 0; a < nu->pntsu; a++, bp++) {
- if (bp->f1 & flag) {
- sel++;
- }
- }
- if (sel == nu->pntsu) {
- if (*r_u == -1) {
- *r_u = b;
- }
- else {
- return 0;
- }
- }
- else if (sel > 1) {
- return 0; /* because sel == 1 is still ok */
- }
- }
-
- for (a = 0; a < nu->pntsu; a++) {
- sel = 0;
- bp = &nu->bp[a];
- for (b = 0; b < nu->pntsv; b++, bp += nu->pntsu) {
- if (bp->f1 & flag) {
- sel++;
- }
- }
- if (sel == nu->pntsv) {
- if (*r_v == -1) {
- *r_v = a;
- }
- else {
- return 0;
- }
- }
- else if (sel > 1) {
- return 0;
- }
- }
-
- if (*r_u == -1 && *r_v > -1) {
- return 1;
- }
- if (*r_v == -1 && *r_u > -1) {
- return 1;
- }
- return 0;
-}
-
/* return true if U direction is selected and number of selected columns v */
static bool isNurbselU(Nurb *nu, int *v, int flag)
{
@@ -1976,119 +1915,201 @@ static void ed_curve_delete_selected(Object *obedit, View3D *v3d)
}
}
-bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag)
+static void select_bpoints(BPoint *bp,
+ const int stride,
+ const int count,
+ const bool selstatus,
+ const uint8_t flag,
+ const bool hidden)
{
- BPoint *bp, *bpn, *newbp;
- int a, u, v, len;
- bool ok = false;
+ for (int i = 0; i < count; i++) {
+ select_bpoint(bp, selstatus, flag, hidden);
+ bp += stride;
+ }
+}
- LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
- if (nu->pntsv == 1) {
- bp = nu->bp;
- a = nu->pntsu;
- while (a) {
- if (bp->f1 & flag) {
- /* pass */
- }
- else {
- break;
- }
- bp++;
- a--;
- }
- if (a == 0) {
- ok = true;
- newbp = (BPoint *)MEM_mallocN(2 * nu->pntsu * sizeof(BPoint), "extrudeNurb1");
- ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu);
- bp = newbp + nu->pntsu;
- ED_curve_bpcpy(editnurb, bp, nu->bp, nu->pntsu);
- MEM_freeN(nu->bp);
- nu->bp = newbp;
- a = nu->pntsu;
- while (a--) {
- select_bpoint(bp, SELECT, flag, HIDDEN);
- select_bpoint(newbp, DESELECT, flag, HIDDEN);
- bp++;
- newbp++;
- }
+/**
+ * Calculate and return fully selected legs along i dimension.
+ * Calculates intervals to create extrusion by duplicating existing points while copied to
+ * destination NURBS. For ex. for curve of 3 points indexed by 0..2 to extrude first and last
+ * point copy intervals would be [0, 0][0, 2][2, 2]. Representation in copy_intervals array would
+ * be [0, 0, 2, 2]. Returns -1 if selection is not valid.
+ */
+static int sel_to_copy_ints(const BPoint *bp,
+ const int next_j,
+ const int max_j,
+ const int next_i,
+ const int max_i,
+ const uint8_t flag,
+ int copy_intervals[],
+ int *interval_count,
+ bool *out_is_first_sel)
+{
+ const BPoint *bp_j = bp;
- nu->pntsv = 2;
- nu->orderv = 2;
- BKE_nurb_knot_calc_v(nu);
- }
- }
- else {
- /* which row or column is selected */
+ int selected_leg_count = 0;
+ int ins = 0;
+ int selected_in_prev_leg = -1;
+ int not_full = -1;
- if (isNurbselUV(nu, flag, &u, &v)) {
+ bool is_first_sel = false;
+ bool is_last_sel = false;
- /* deselect all */
- bp = nu->bp;
- a = nu->pntsu * nu->pntsv;
- while (a--) {
- select_bpoint(bp, DESELECT, flag, HIDDEN);
- bp++;
- }
+ for (int j = 0; j < max_j; j++, bp_j += next_j) {
+ const BPoint *bp_j_i = bp_j;
+ int selected_in_curr_leg = 0;
+ for (int i = 0; i < max_i; i++, bp_j_i += next_i) {
+ if (bp_j_i->f1 & flag) {
+ selected_in_curr_leg++;
+ }
+ }
+ if (selected_in_curr_leg == max_i) {
+ selected_leg_count++;
+ if (j == 0) {
+ is_first_sel = true;
+ }
+ else if (j + 1 == max_j) {
+ is_last_sel = true;
+ }
+ }
+ else if (not_full == -1) {
+ not_full = selected_in_curr_leg;
+ }
+ /* We have partially selected leg in opposite dimension if condition is met. */
+ else if (not_full != selected_in_curr_leg) {
+ return -1;
+ }
+ /* Extrusion area starts/ends if met. */
+ if (selected_in_prev_leg != selected_in_curr_leg) {
+ copy_intervals[ins] = selected_in_curr_leg == max_i || j == 0 ? j : j - 1;
+ ins++;
+ selected_in_prev_leg = selected_in_curr_leg;
+ }
+ copy_intervals[ins] = j;
+ }
+ if (selected_leg_count &&
+ /* Prevents leading and trailing unselected legs if all selected.
+ * Unless it is extrusion from point or curve. */
+ (selected_leg_count < max_j || max_j == 1)) {
+ /* Prepend unselected leg if more than one leg selected at the starting edge.
+ * max_j == 1 handles extrusion from point to curve and from curve to surface cases. */
+ if (is_first_sel && (copy_intervals[0] < copy_intervals[1] || max_j == 1)) {
+ memmove(copy_intervals + 1, copy_intervals, (ins + 1) * sizeof(copy_intervals[0]));
+ copy_intervals[0] = 0;
+ ins++;
+ is_first_sel = false;
+ }
+ /* Append unselected leg if more than one leg selected at the end. */
+ if (is_last_sel && copy_intervals[ins - 1] < copy_intervals[ins]) {
+ copy_intervals[ins + 1] = copy_intervals[ins];
+ ins++;
+ }
+ }
+ *interval_count = ins;
+ *out_is_first_sel = ins > 1 ? is_first_sel : false;
+ return selected_leg_count;
+}
- if (ELEM(u, 0, nu->pntsv - 1)) { /* row in u-direction selected */
- ok = true;
- newbp = (BPoint *)MEM_mallocN(nu->pntsu * (nu->pntsv + 1) * sizeof(BPoint),
- "extrudeNurb1");
- if (u == 0) {
- len = nu->pntsv * nu->pntsu;
- ED_curve_bpcpy(editnurb, newbp + nu->pntsu, nu->bp, len);
- ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu);
- bp = newbp;
- }
- else {
- len = nu->pntsv * nu->pntsu;
- ED_curve_bpcpy(editnurb, newbp, nu->bp, len);
- ED_curve_bpcpy(editnurb, newbp + len, &nu->bp[len - nu->pntsu], nu->pntsu);
- bp = newbp + len;
- }
+typedef struct NurbDim {
+ int pntsu;
+ int pntsv;
+} NurbDim;
- a = nu->pntsu;
- while (a--) {
- select_bpoint(bp, SELECT, flag, HIDDEN);
- bp++;
- }
+static NurbDim editnurb_find_max_points_num(const EditNurb *editnurb)
+{
+ NurbDim ret = {0, 0};
+ LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
+ if (nu->pntsu > ret.pntsu) {
+ ret.pntsu = nu->pntsu;
+ }
+ if (nu->pntsv > ret.pntsv) {
+ ret.pntsv = nu->pntsv;
+ }
+ }
+ return ret;
+}
- MEM_freeN(nu->bp);
- nu->bp = newbp;
- nu->pntsv++;
- BKE_nurb_knot_calc_v(nu);
- }
- else if (ELEM(v, 0, nu->pntsu - 1)) { /* column in v-direction selected */
- ok = true;
- bpn = newbp = (BPoint *)MEM_mallocN((nu->pntsu + 1) * nu->pntsv * sizeof(BPoint),
- "extrudeNurb1");
- bp = nu->bp;
+bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag)
+{
+ const NurbDim max = editnurb_find_max_points_num(editnurb);
+ /* One point induces at most one interval. Except single point case, it can give + 1.
+ * Another +1 is for first element of the first interval. */
+ int *const intvls_u = MEM_malloc_arrayN(max.pntsu + 2, sizeof(int), "extrudeNurb0");
+ int *const intvls_v = MEM_malloc_arrayN(max.pntsv + 2, sizeof(int), "extrudeNurb1");
+ bool ok = false;
- for (a = 0; a < nu->pntsv; a++) {
- if (v == 0) {
- *bpn = *bp;
- bpn->f1 |= flag;
- bpn++;
- }
- ED_curve_bpcpy(editnurb, bpn, bp, nu->pntsu);
- bp += nu->pntsu;
- bpn += nu->pntsu;
- if (v == nu->pntsu - 1) {
- *bpn = *(bp - 1);
- bpn->f1 |= flag;
- bpn++;
- }
- }
+ LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
+ int intvl_cnt_u;
+ bool is_first_sel_u;
- MEM_freeN(nu->bp);
- nu->bp = newbp;
- nu->pntsu++;
- BKE_nurb_knot_calc_u(nu);
- }
- }
+ /* Calculate selected U legs and intervals for their extrusion. */
+ const int selected_us = sel_to_copy_ints(
+ nu->bp, 1, nu->pntsu, nu->pntsu, nu->pntsv, flag, intvls_u, &intvl_cnt_u, &is_first_sel_u);
+ if (selected_us == -1) {
+ continue;
}
- }
+ int intvl_cnt_v;
+ bool is_first_sel_v;
+ const bool is_point = nu->pntsu == 1;
+ const bool is_curve = nu->pntsv == 1;
+ const bool extrude_every_u_point = selected_us == nu->pntsu;
+ if (is_point || (is_curve && !extrude_every_u_point)) {
+ intvls_v[0] = intvls_v[1] = 0;
+ intvl_cnt_v = 1;
+ is_first_sel_v = false;
+ }
+ else {
+ sel_to_copy_ints(nu->bp,
+ nu->pntsu,
+ nu->pntsv,
+ 1,
+ nu->pntsu,
+ flag,
+ intvls_v,
+ &intvl_cnt_v,
+ &is_first_sel_v);
+ }
+
+ const int new_pntsu = nu->pntsu + intvl_cnt_u - 1;
+ const int new_pntsv = nu->pntsv + intvl_cnt_v - 1;
+ BPoint *const new_bp = (BPoint *)MEM_malloc_arrayN(
+ new_pntsu * new_pntsv, sizeof(BPoint), "extrudeNurb2");
+ BPoint *new_bp_v = new_bp;
+
+ bool selected_v = is_first_sel_v;
+ for (int j = 1; j <= intvl_cnt_v; j++, selected_v = !selected_v) {
+ BPoint *old_bp_v = nu->bp + intvls_v[j - 1] * nu->pntsu;
+ for (int v_j = intvls_v[j - 1]; v_j <= intvls_v[j];
+ v_j++, new_bp_v += new_pntsu, old_bp_v += nu->pntsu) {
+ BPoint *new_bp_u_v = new_bp_v;
+ bool selected_u = is_first_sel_u;
+ for (int i = 1; i <= intvl_cnt_u; i++, selected_u = !selected_u) {
+ const int copy_from = intvls_u[i - 1];
+ const int copy_to = intvls_u[i];
+ const int copy_count = copy_to - copy_from + 1;
+ const bool sel_status = selected_u || selected_v ? true : false;
+ ED_curve_bpcpy(editnurb, new_bp_u_v, old_bp_v + copy_from, copy_count);
+ select_bpoints(new_bp_u_v, 1, copy_count, sel_status, flag, HIDDEN);
+ new_bp_u_v += copy_count;
+ }
+ }
+ }
+
+ MEM_freeN(nu->bp);
+ nu->bp = new_bp;
+ nu->pntsu = new_pntsu;
+ if (nu->pntsv == 1 && new_pntsv > 1) {
+ nu->orderv = 2;
+ }
+ nu->pntsv = new_pntsv;
+ BKE_nurb_knot_calc_u(nu);
+ BKE_nurb_knot_calc_v(nu);
+
+ ok = true;
+ }
+ MEM_freeN(intvls_u);
+ MEM_freeN(intvls_v);
return ok;
}
@@ -2133,7 +2154,7 @@ static void adduplicateflagNurb(
starta = a;
while ((bezt->f1 & flag) || (bezt->f2 & flag) || (bezt->f3 & flag)) {
if (!split) {
- select_beztriple(bezt, DESELECT, flag, HIDDEN);
+ select_beztriple(bezt, false, flag, HIDDEN);
}
enda = a;
if (a >= nu->pntsu - 1) {
@@ -2173,7 +2194,7 @@ static void adduplicateflagNurb(
}
for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) {
- select_beztriple(bezt1, SELECT, flag, HIDDEN);
+ select_beztriple(bezt1, true, flag, HIDDEN);
}
BLI_addtail(newnurb, newnu);
@@ -2191,7 +2212,7 @@ static void adduplicateflagNurb(
newnu->flagu &= ~CU_NURB_CYCLIC;
for (b = 0, bezt1 = newnu->bezt; b < newnu->pntsu; b++, bezt1++) {
- select_beztriple(bezt1, SELECT, flag, HIDDEN);
+ select_beztriple(bezt1, true, flag, HIDDEN);
}
BLI_addtail(newnurb, newnu);
@@ -2203,7 +2224,7 @@ static void adduplicateflagNurb(
starta = a;
while (bp->f1 & flag) {
if (!split) {
- select_bpoint(bp, DESELECT, flag, HIDDEN);
+ select_bpoint(bp, false, flag, HIDDEN);
}
enda = a;
if (a >= nu->pntsu - 1) {
@@ -2243,7 +2264,7 @@ static void adduplicateflagNurb(
}
for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) {
- select_bpoint(bp1, SELECT, flag, HIDDEN);
+ select_bpoint(bp1, true, flag, HIDDEN);
}
BLI_addtail(newnurb, newnu);
@@ -2261,7 +2282,7 @@ static void adduplicateflagNurb(
newnu->flagu &= ~CU_NURB_CYCLIC;
for (b = 0, bp1 = newnu->bp; b < newnu->pntsu; b++, bp1++) {
- select_bpoint(bp1, SELECT, flag, HIDDEN);
+ select_bpoint(bp1, true, flag, HIDDEN);
}
BLI_addtail(newnurb, newnu);
@@ -2481,7 +2502,7 @@ static void adduplicateflagNurb(
for (b = 0, bp1 = nu->bp; b < nu->pntsu * nu->pntsv; b++, bp1++) {
bp1->f1 &= ~SURF_SEEN;
if (!split) {
- select_bpoint(bp1, DESELECT, flag, HIDDEN);
+ select_bpoint(bp1, false, flag, HIDDEN);
}
}
}
@@ -2525,12 +2546,13 @@ static void adduplicateflagNurb(
static int switch_direction_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -2586,10 +2608,11 @@ void CURVE_OT_switch_direction(wmOperatorType *ot)
static int set_goal_weight_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -2652,10 +2675,11 @@ void CURVE_OT_spline_weight_set(wmOperatorType *ot)
static int set_radius_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -2763,10 +2787,11 @@ static void smooth_single_bp(BPoint *bp,
static int smooth_exec(bContext *C, wmOperator *UNUSED(op))
{
const float factor = 1.0f / 6.0f;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3059,10 +3084,11 @@ static void curve_smooth_value(ListBase *editnurb, const int bezt_offsetof, cons
static int curve_smooth_weight_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3102,10 +3128,11 @@ void CURVE_OT_smooth_weight(wmOperatorType *ot)
static int curve_smooth_radius_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3145,10 +3172,11 @@ void CURVE_OT_smooth_radius(wmOperatorType *ot)
static int curve_smooth_tilt_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3188,6 +3216,7 @@ void CURVE_OT_smooth_tilt(wmOperatorType *ot)
static int hide_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
@@ -3195,7 +3224,7 @@ static int hide_exec(bContext *C, wmOperator *op)
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -3216,11 +3245,11 @@ static int hide_exec(bContext *C, wmOperator *op)
sel = 0;
while (a--) {
if (invert == 0 && BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
- select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ select_beztriple(bezt, false, SELECT, HIDDEN);
bezt->hide = 1;
}
else if (invert && !BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) {
- select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ select_beztriple(bezt, false, SELECT, HIDDEN);
bezt->hide = 1;
}
if (bezt->hide) {
@@ -3238,11 +3267,11 @@ static int hide_exec(bContext *C, wmOperator *op)
sel = 0;
while (a--) {
if (invert == 0 && (bp->f1 & SELECT)) {
- select_bpoint(bp, DESELECT, SELECT, HIDDEN);
+ select_bpoint(bp, false, SELECT, HIDDEN);
bp->hide = 1;
}
else if (invert && (bp->f1 & SELECT) == 0) {
- select_bpoint(bp, DESELECT, SELECT, HIDDEN);
+ select_bpoint(bp, false, SELECT, HIDDEN);
bp->hide = 1;
}
if (bp->hide) {
@@ -3290,13 +3319,14 @@ void CURVE_OT_hide(wmOperatorType *ot)
static int reveal_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool select = RNA_boolean_get(op->ptr, "select");
bool changed_multi = false;
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ListBase *editnurb = object_editcurve_get(obedit);
@@ -3769,12 +3799,13 @@ static int subdivide_exec(bContext *C, wmOperator *op)
const int number_cuts = RNA_int_get(op->ptr, "number_cuts");
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -3827,10 +3858,11 @@ void CURVE_OT_subdivide(wmOperatorType *ot)
static int set_spline_type_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
int ret_value = OPERATOR_CANCELLED;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -3920,13 +3952,14 @@ void CURVE_OT_spline_type_set(wmOperatorType *ot)
static int set_handle_type_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
const int handle_type = RNA_enum_get(op->ptr, "type");
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -3982,6 +4015,7 @@ void CURVE_OT_handle_type_set(wmOperatorType *ot)
static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
@@ -3989,7 +4023,7 @@ static int curve_normals_make_consistent_exec(bContext *C, wmOperator *op)
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -4332,7 +4366,7 @@ static bool merge_2_nurb(Curve *cu, ListBase *editnurb, Nurb *nu1, Nurb *nu2)
keyIndex_updateBP(cu->editnurb, bp1, bp, 1);
*bp = *bp1;
bp1++;
- select_bpoint(bp, SELECT, SELECT, HIDDEN);
+ select_bpoint(bp, true, SELECT, HIDDEN);
}
else {
keyIndex_updateBP(cu->editnurb, bp2, bp, 1);
@@ -4422,6 +4456,7 @@ static int merge_nurb(View3D *v3d, Object *obedit)
static int make_segment_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
@@ -4435,7 +4470,7 @@ static int make_segment_exec(bContext *C, wmOperator *op)
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -4759,7 +4794,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
/* Deselect everything. */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &objects_len);
+ vc.scene, vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
@@ -4787,7 +4822,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
bezt->f2 |= SELECT;
}
else {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ select_beztriple(bezt, true, SELECT, HIDDEN);
}
}
else {
@@ -4801,7 +4836,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
BKE_curve_nurb_vert_active_set(cu, nu, bezt);
}
else {
- select_bpoint(bp, SELECT, SELECT, HIDDEN);
+ select_bpoint(bp, true, SELECT, HIDDEN);
BKE_curve_nurb_vert_active_set(cu, nu, bp);
}
break;
@@ -4813,7 +4848,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
bezt->f2 &= ~SELECT;
}
else {
- select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ select_beztriple(bezt, false, SELECT, HIDDEN);
}
if (bezt == vert) {
cu->actvert = CU_ACT_NONE;
@@ -4827,7 +4862,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
}
}
else {
- select_bpoint(bp, DESELECT, SELECT, HIDDEN);
+ select_bpoint(bp, false, SELECT, HIDDEN);
if (bp == vert) {
cu->actvert = CU_ACT_NONE;
}
@@ -4842,7 +4877,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
bezt->f2 &= ~SELECT;
}
else {
- select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ select_beztriple(bezt, false, SELECT, HIDDEN);
}
if (bezt == vert) {
cu->actvert = CU_ACT_NONE;
@@ -4853,7 +4888,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
bezt->f2 |= SELECT;
}
else {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ select_beztriple(bezt, true, SELECT, HIDDEN);
}
BKE_curve_nurb_vert_active_set(cu, nu, bezt);
}
@@ -4867,13 +4902,13 @@ bool ED_curve_editnurb_select_pick(bContext *C,
}
else {
if (bp->f1 & SELECT) {
- select_bpoint(bp, DESELECT, SELECT, HIDDEN);
+ select_bpoint(bp, false, SELECT, HIDDEN);
if (bp == vert) {
cu->actvert = CU_ACT_NONE;
}
}
else {
- select_bpoint(bp, SELECT, SELECT, HIDDEN);
+ select_bpoint(bp, true, SELECT, HIDDEN);
BKE_curve_nurb_vert_active_set(cu, nu, bp);
}
}
@@ -4889,7 +4924,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
bezt->f2 |= SELECT;
}
else {
- select_beztriple(bezt, SELECT, SELECT, HIDDEN);
+ select_beztriple(bezt, true, SELECT, HIDDEN);
}
}
else {
@@ -4903,7 +4938,7 @@ bool ED_curve_editnurb_select_pick(bContext *C,
BKE_curve_nurb_vert_active_set(cu, nu, bezt);
}
else {
- select_bpoint(bp, SELECT, SELECT, HIDDEN);
+ select_bpoint(bp, true, SELECT, HIDDEN);
BKE_curve_nurb_vert_active_set(cu, nu, bp);
}
break;
@@ -4925,7 +4960,8 @@ bool ED_curve_editnurb_select_pick(bContext *C,
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL);
}
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
@@ -5026,6 +5062,7 @@ bool ed_editnurb_spin(
static int spin_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = ED_view3d_context_rv3d(C);
@@ -5045,7 +5082,7 @@ static int spin_exec(bContext *C, wmOperator *op)
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = (Curve *)obedit->data;
@@ -5684,34 +5721,24 @@ void CURVE_OT_vertex_add(wmOperatorType *ot)
static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
EditNurb *editnurb = cu->editnurb;
bool changed = false;
- bool as_curve = false;
if (!ED_curve_select_check(v3d, cu->editnurb)) {
continue;
}
- /* First test: curve? */
- if (obedit->type != OB_CURVES_LEGACY) {
- LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) {
- if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) {
- as_curve = true;
- break;
- }
- }
- }
-
- if (obedit->type == OB_CURVES_LEGACY || as_curve) {
+ if (obedit->type == OB_CURVES_LEGACY) {
changed = ed_editcurve_extrude(cu, editnurb, v3d);
}
else {
@@ -5837,12 +5864,13 @@ static int toggle_cyclic_exec(bContext *C, wmOperator *op)
{
const int direction = RNA_enum_get(op->ptr, "direction");
View3D *v3d = CTX_wm_view3d(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
bool changed_multi = false;
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -5925,6 +5953,7 @@ void CURVE_OT_cyclic_toggle(wmOperatorType *ot)
static int duplicate_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
@@ -5933,7 +5962,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -6397,7 +6426,7 @@ static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split)
if (split) {
/* deselect for split operator */
for (b = 0, bezt1 = nu->bezt; b < nu->pntsu; b++, bezt1++) {
- select_beztriple(bezt1, DESELECT, SELECT, true);
+ select_beztriple(bezt1, false, SELECT, true);
}
}
@@ -6407,7 +6436,7 @@ static bool curve_delete_segments(Object *obedit, View3D *v3d, const bool split)
if (split) {
/* deselect for split operator */
for (b = 0, bp1 = nu->bp; b < nu->pntsu * nu->pntsv; b++, bp1++) {
- select_bpoint(bp1, DESELECT, SELECT, HIDDEN);
+ select_bpoint(bp1, false, SELECT, HIDDEN);
}
}
@@ -6433,10 +6462,11 @@ static int curve_delete_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
View3D *v3d = CTX_wm_view3d(C);
eCurveElem_Types type = RNA_enum_get(op->ptr, "type");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
bool changed_multi = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -6609,12 +6639,13 @@ void ed_dissolve_bez_segment(BezTriple *bezt_prev,
static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = (Curve *)obedit->data;
@@ -6703,10 +6734,11 @@ static int curve_decimate_exec(bContext *C, wmOperator *op)
float ratio = RNA_float_get(op->ptr, "ratio");
bool all_supported_multi = true;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = (Curve *)obedit->data;
@@ -6783,11 +6815,12 @@ void CURVE_OT_decimate(wmOperatorType *ot)
static int shade_smooth_exec(bContext *C, wmOperator *op)
{
View3D *v3d = CTX_wm_view3d(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int clear = (STREQ(op->idname, "CURVE_OT_shade_flat"));
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
int ret_value = OPERATOR_CANCELLED;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -6977,12 +7010,13 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op)
static int clear_tilt_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c
index ba5a7409ba7..f2cc48049d7 100644
--- a/source/blender/editors/curve/editcurve_add.c
+++ b/source/blender/editors/curve/editcurve_add.c
@@ -18,6 +18,7 @@
#include "BKE_context.h"
#include "BKE_curve.h"
+#include "BKE_layer.h"
#include "DEG_depsgraph.h"
@@ -87,6 +88,8 @@ static const char *get_surf_defname(int type)
return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCircle");
case CU_PRIM_PATCH:
return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfPatch");
+ case CU_PRIM_TUBE:
+ return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfCylinder");
case CU_PRIM_SPHERE:
return CTX_DATA_(BLT_I18NCONTEXT_ID_CURVE_LEGACY, "SurfSphere");
case CU_PRIM_DONUT:
@@ -493,7 +496,8 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf)
struct Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
ListBase *editnurb;
Nurb *nu;
bool newob = false;
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 6946c09e6f1..7632f1b1e64 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -1162,7 +1162,7 @@ static int curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
curve_draw_event_add_first(op, event);
}
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
if (cdd->state == CURVE_DRAW_PAINTING) {
const float mval_fl[2] = {UNPACK2(event->mval)};
if (len_squared_v2v2(mval_fl, cdd->prev.mval) > square_f(STROKE_SAMPLE_DIST_MIN_PX)) {
diff --git a/source/blender/editors/curve/editcurve_pen.c b/source/blender/editors/curve/editcurve_pen.c
index 729ad46877a..395053d6b1b 100644
--- a/source/blender/editors/curve/editcurve_pen.c
+++ b/source/blender/editors/curve/editcurve_pen.c
@@ -1532,7 +1532,7 @@ wmKeyMap *curve_pen_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Curve Pen Modal Map");
- /* This function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return NULL;
}
@@ -1622,7 +1622,7 @@ static int curve_pen_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
/* Check if dragging */
if (!cpd->dragging && WM_event_drag_test(event, event->prev_press_xy)) {
cpd->dragging = true;
diff --git a/source/blender/editors/curve/editcurve_query.c b/source/blender/editors/curve/editcurve_query.c
index a08dbbe6132..56268baf1f1 100644
--- a/source/blender/editors/curve/editcurve_query.c
+++ b/source/blender/editors/curve/editcurve_query.c
@@ -118,7 +118,7 @@ bool ED_curve_pick_vert_ex(ViewContext *vc,
uint bases_len;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc->view_layer, vc->v3d, &bases_len);
+ vc->scene, vc->view_layer, vc->v3d, &bases_len);
for (uint base_index = 0; base_index < bases_len; base_index++) {
Base *base = bases[base_index];
data.is_changed = false;
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
index 5a1777b7097..4015ae545da 100644
--- a/source/blender/editors/curve/editcurve_select.c
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -30,7 +30,6 @@
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
-#include "ED_types.h"
#include "ED_view3d.h"
#include "curve_intern.h"
@@ -47,7 +46,7 @@
bool select_beztriple(BezTriple *bezt, bool selstatus, uint8_t flag, eVisible_Types hidden)
{
if ((bezt->hide == 0) || (hidden == HIDDEN)) {
- if (selstatus == SELECT) { /* selects */
+ if (selstatus) { /* selects */
bezt->f1 |= flag;
bezt->f2 |= flag;
bezt->f3 |= flag;
@@ -66,7 +65,7 @@ bool select_beztriple(BezTriple *bezt, bool selstatus, uint8_t flag, eVisible_Ty
bool select_bpoint(BPoint *bp, bool selstatus, uint8_t flag, bool hidden)
{
if ((bp->hide == 0) || (hidden == 1)) {
- if (selstatus == SELECT) {
+ if (selstatus) {
bp->f1 |= flag;
return true;
}
@@ -80,17 +79,17 @@ bool select_bpoint(BPoint *bp, bool selstatus, uint8_t flag, bool hidden)
static bool swap_selection_beztriple(BezTriple *bezt)
{
if (bezt->f2 & SELECT) {
- return select_beztriple(bezt, DESELECT, SELECT, VISIBLE);
+ return select_beztriple(bezt, false, SELECT, VISIBLE);
}
- return select_beztriple(bezt, SELECT, SELECT, VISIBLE);
+ return select_beztriple(bezt, true, SELECT, VISIBLE);
}
static bool swap_selection_bpoint(BPoint *bp)
{
if (bp->f1 & SELECT) {
- return select_bpoint(bp, DESELECT, SELECT, VISIBLE);
+ return select_bpoint(bp, false, SELECT, VISIBLE);
}
- return select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ return select_bpoint(bp, true, SELECT, VISIBLE);
}
bool ED_curve_nurb_select_check(const View3D *v3d, const Nurb *nu)
@@ -260,7 +259,7 @@ bool ED_curve_deselect_all_multi(struct bContext *C)
ED_view3d_viewcontext_init(C, &vc, depsgraph);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &bases_len);
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
bool changed_multi = ED_curve_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
return changed_multi;
@@ -336,9 +335,9 @@ static void select_adjacent_cp(ListBase *editnurb,
break;
}
if ((lastsel == false) && (bezt->hide == 0) &&
- ((bezt->f2 & SELECT) || (selstatus == DESELECT))) {
+ ((bezt->f2 & SELECT) || (selstatus == false))) {
bezt += next;
- if (!(bezt->f2 & SELECT) || (selstatus == DESELECT)) {
+ if (!(bezt->f2 & SELECT) || (selstatus == false)) {
bool sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE);
if (sel && !cont) {
lastsel = true;
@@ -363,10 +362,9 @@ static void select_adjacent_cp(ListBase *editnurb,
if (a - abs(next) < 0) {
break;
}
- if ((lastsel == false) && (bp->hide == 0) &&
- ((bp->f1 & SELECT) || (selstatus == DESELECT))) {
+ if ((lastsel == false) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == false))) {
bp += next;
- if (!(bp->f1 & SELECT) || (selstatus == DESELECT)) {
+ if (!(bp->f1 & SELECT) || (selstatus == false)) {
bool sel = select_bpoint(bp, selstatus, SELECT, VISIBLE);
if (sel && !cont) {
lastsel = true;
@@ -470,14 +468,15 @@ static void selectend_nurb(Object *obedit, eEndPoint_Types selfirst, bool doswap
static int de_select_first_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
- selectend_nurb(obedit, FIRST, true, DESELECT);
+ selectend_nurb(obedit, FIRST, true, false);
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
BKE_curve_nurb_vert_active_validate(obedit->data);
@@ -503,14 +502,15 @@ void CURVE_OT_de_select_first(wmOperatorType *ot)
static int de_select_last_exec(bContext *C, wmOperator *UNUSED(op))
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
- selectend_nurb(obedit, LAST, true, DESELECT);
+ selectend_nurb(obedit, LAST, true, false);
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
BKE_curve_nurb_vert_active_validate(obedit->data);
@@ -545,11 +545,12 @@ static int de_select_all_exec(bContext *C, wmOperator *op)
{
int action = RNA_enum_get(op->ptr, "action");
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -618,12 +619,13 @@ void CURVE_OT_select_all(wmOperatorType *ot)
static int select_linked_exec(bContext *C, wmOperator *UNUSED(op))
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -780,12 +782,12 @@ static int select_row_exec(bContext *C, wmOperator *UNUSED(op))
for (b = 0; b < nu->pntsu; b++, bp++) {
if (direction) {
if (a == v) {
- select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ select_bpoint(bp, true, SELECT, VISIBLE);
}
}
else {
if (b == u) {
- select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ select_bpoint(bp, true, SELECT, VISIBLE);
}
}
}
@@ -820,10 +822,11 @@ void CURVE_OT_select_row(wmOperatorType *ot)
static int select_next_exec(bContext *C, wmOperator *UNUSED(op))
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -861,10 +864,11 @@ void CURVE_OT_select_next(wmOperatorType *ot)
static int select_previous_exec(bContext *C, wmOperator *UNUSED(op))
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -923,7 +927,7 @@ static void curve_select_more(Object *obedit)
if (a % nu->pntsu != 0) {
tempbp = bp - 1;
if (!(tempbp->f1 & SELECT)) {
- select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
+ select_bpoint(tempbp, true, SELECT, VISIBLE);
}
}
@@ -932,7 +936,7 @@ static void curve_select_more(Object *obedit)
sel = 0;
tempbp = bp + nu->pntsu;
if (!(tempbp->f1 & SELECT)) {
- sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
+ sel = select_bpoint(tempbp, true, SELECT, VISIBLE);
}
/* make sure selected bpoint is discarded */
if (sel == 1) {
@@ -944,7 +948,7 @@ static void curve_select_more(Object *obedit)
if (a + nu->pntsu < nu->pntsu * nu->pntsv) {
tempbp = bp - nu->pntsu;
if (!(tempbp->f1 & SELECT)) {
- select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
+ select_bpoint(tempbp, true, SELECT, VISIBLE);
}
}
@@ -953,7 +957,7 @@ static void curve_select_more(Object *obedit)
sel = 0;
tempbp = bp + 1;
if (!(tempbp->f1 & SELECT)) {
- sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
+ sel = select_bpoint(tempbp, true, SELECT, VISIBLE);
}
if (sel) {
bp++;
@@ -977,10 +981,11 @@ static void curve_select_more(Object *obedit)
static int curve_select_more_exec(bContext *C, wmOperator *UNUSED(op))
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
curve_select_more(obedit);
@@ -1080,7 +1085,7 @@ static void curve_select_less(Object *obedit)
}
if (sel != 4) {
- select_bpoint(bp, DESELECT, SELECT, VISIBLE);
+ select_bpoint(bp, false, SELECT, VISIBLE);
BLI_BITMAP_ENABLE(selbpoints, a);
}
}
@@ -1130,7 +1135,7 @@ static void curve_select_less(Object *obedit)
}
if (sel != 2) {
- select_beztriple(bezt, DESELECT, SELECT, VISIBLE);
+ select_beztriple(bezt, false, SELECT, VISIBLE);
lastsel = true;
}
else {
@@ -1175,7 +1180,7 @@ static void curve_select_less(Object *obedit)
}
if (sel != 2) {
- select_bpoint(bp, DESELECT, SELECT, VISIBLE);
+ select_bpoint(bp, false, SELECT, VISIBLE);
lastsel = true;
}
else {
@@ -1195,10 +1200,11 @@ static void curve_select_less(Object *obedit)
static int curve_select_less_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
curve_select_less(obedit);
@@ -1236,10 +1242,11 @@ static int curve_select_random_exec(bContext *C, wmOperator *op)
const float randfac = RNA_float_get(op->ptr, "ratio");
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -1359,7 +1366,7 @@ static void select_nth_bezt(Nurb *nu, BezTriple *bezt, const struct CheckerInter
while (a--) {
const int depth = abs(start - a);
if (!WM_operator_properties_checker_interval_test(params, depth)) {
- select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ select_beztriple(bezt, false, SELECT, HIDDEN);
}
bezt--;
@@ -1382,7 +1389,7 @@ static void select_nth_bp(Nurb *nu, BPoint *bp, const struct CheckerIntervalPara
while (a--) {
const int depth = abs(pnt - startpnt) + abs(row - startrow);
if (!WM_operator_properties_checker_interval_test(params, depth)) {
- select_bpoint(bp, DESELECT, SELECT, HIDDEN);
+ select_bpoint(bp, false, SELECT, HIDDEN);
}
pnt--;
@@ -1416,6 +1423,7 @@ static bool ed_curve_select_nth(Curve *cu, const struct CheckerIntervalParams *p
static int select_nth_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *obact = CTX_data_edit_object(C);
View3D *v3d = CTX_wm_view3d(C);
@@ -1426,7 +1434,7 @@ static int select_nth_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Curve *cu = obedit->data;
@@ -1645,7 +1653,7 @@ static bool curve_nurb_select_similar_type(Object *ob,
}
if (select) {
- select_beztriple(bezt, SELECT, SELECT, VISIBLE);
+ select_beztriple(bezt, true, SELECT, VISIBLE);
changed = true;
}
}
@@ -1690,7 +1698,7 @@ static bool curve_nurb_select_similar_type(Object *ob,
}
if (select) {
- select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ select_bpoint(bp, true, SELECT, VISIBLE);
changed = true;
}
}
@@ -1706,12 +1714,13 @@ static int curve_select_similar_exec(bContext *C, wmOperator *op)
const float thresh = RNA_float_get(op->ptr, "threshold");
const int compare = RNA_enum_get(op->ptr, "compare");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
int tot_nurbs_selected_all = 0;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -1898,10 +1907,10 @@ static void curve_select_shortest_path_curve(Nurb *nu, int vert_src, int vert_ds
i = vert_src;
while (true) {
if (nu->type & CU_BEZIER) {
- select_beztriple(&nu->bezt[i], SELECT, SELECT, HIDDEN);
+ select_beztriple(&nu->bezt[i], true, SELECT, HIDDEN);
}
else {
- select_bpoint(&nu->bp[i], SELECT, SELECT, HIDDEN);
+ select_bpoint(&nu->bp[i], true, SELECT, HIDDEN);
}
if (i == vert_dst) {
@@ -1979,10 +1988,10 @@ static void curve_select_shortest_path_surf(Nurb *nu, int vert_src, int vert_dst
int i = 0;
while (vert_curr != vert_src && i++ < vert_num) {
if (nu->type == CU_BEZIER) {
- select_beztriple(&nu->bezt[vert_curr], SELECT, SELECT, HIDDEN);
+ select_beztriple(&nu->bezt[vert_curr], true, SELECT, HIDDEN);
}
else {
- select_bpoint(&nu->bp[vert_curr], SELECT, SELECT, HIDDEN);
+ select_bpoint(&nu->bp[vert_curr], true, SELECT, HIDDEN);
}
vert_curr = data[vert_curr].vert_prev;
}
@@ -2040,7 +2049,8 @@ static int edcu_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
BKE_curve_nurb_vert_active_set(cu, nu_dst, vert_dst_p);
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c
index 888bb2169e0..3f53a88ba5d 100644
--- a/source/blender/editors/curve/editcurve_undo.c
+++ b/source/blender/editors/curve/editcurve_undo.c
@@ -160,8 +160,10 @@ static void undocurve_free_data(UndoCurve *uc)
static Object *editcurve_object_from_context(bContext *C)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit && ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
Curve *cu = obedit->data;
if (BKE_curve_editNurbs_get(cu) != NULL) {
@@ -202,9 +204,10 @@ static bool curve_undosys_step_encode(struct bContext *C, struct Main *bmain, Un
/* Important not to use the 3D view when getting objects because all objects
* outside of this list will be moved out of edit-mode when reading back undo steps. */
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
- Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len);
+ Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
us->elems_len = objects_len;
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index 611dbb2e80c..03c485a7671 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -26,12 +26,15 @@
#include "BKE_context.h"
#include "BKE_curve.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_vfont.h"
+#include "BLT_translation.h"
+
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -59,7 +62,7 @@ static int kill_selection(Object *obedit, int ins);
/** \name Internal Utilities
* \{ */
-static char32_t findaccent(char32_t char1, uint code)
+static char32_t findaccent(char32_t char1, const char code)
{
char32_t new = 0;
@@ -619,18 +622,19 @@ static void txt_add_object(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
Curve *cu;
Object *obedit;
- Base *base;
+ Object *object;
const struct TextLine *tmp;
int nchars = 0, nbytes = 0;
char *s;
int a;
const float rot[3] = {0.0f, 0.0f, 0.0f};
- obedit = BKE_object_add(bmain, view_layer, OB_FONT, NULL);
- base = view_layer->basact;
+ obedit = BKE_object_add(bmain, scene, view_layer, OB_FONT, NULL);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ object = BKE_view_layer_active_object_get(view_layer);
/* seems to assume view align ? TODO: look into this, could be an operator option. */
- ED_object_base_init_transform_on_add(base->object, NULL, rot);
+ ED_object_base_init_transform_on_add(object, NULL, rot);
BKE_object_where_is_calc(depsgraph, scene, obedit);
@@ -1638,12 +1642,11 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Object *obedit = CTX_data_edit_object(C);
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
- static int accentcode = 0;
- uintptr_t ascii = event->ascii;
+ static bool accentcode = false;
const bool alt = event->modifier & KM_ALT;
const bool shift = event->modifier & KM_SHIFT;
const bool ctrl = event->modifier & KM_CTRL;
- int event_type = event->type, event_val = event->val;
+ char32_t insert_char_override = 0;
char32_t inserted_text[2] = {0};
if (RNA_struct_property_is_set(op->ptr, "text")) {
@@ -1652,48 +1655,47 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (RNA_struct_property_is_set(op->ptr, "accent")) {
if (ef->len != 0 && ef->pos > 0) {
- accentcode = 1;
+ accentcode = true;
}
return OPERATOR_FINISHED;
}
- /* tab should exit editmode, but we allow it to be typed using modifier keys */
- if (event_type == EVT_TABKEY) {
- if ((alt || ctrl || shift) == 0) {
- return OPERATOR_PASS_THROUGH;
- }
-
- ascii = 9;
- }
-
- if (event_type == EVT_BACKSPACEKEY) {
+ if (event->type == EVT_BACKSPACEKEY) {
if (alt && ef->len != 0 && ef->pos > 0) {
- accentcode = 1;
+ accentcode = true;
}
return OPERATOR_PASS_THROUGH;
}
- if (event_val && (ascii || event->utf8_buf[0])) {
- /* handle case like TAB (== 9) */
- if ((ascii > 31 && ascii < 254 && ascii != 127) || (ELEM(ascii, 13, 10)) || (ascii == 8) ||
- (event->utf8_buf[0])) {
+ /* Tab typically exit edit-mode, but we allow it to be typed using modifier keys. */
+ if (event->type == EVT_TABKEY) {
+ if ((alt || ctrl || shift) == 0) {
+ return OPERATOR_PASS_THROUGH;
+ }
+ insert_char_override = '\t';
+ }
+ if (insert_char_override || event->utf8_buf[0]) {
+ if (insert_char_override) {
+ /* Handle case like TAB ('\t'). */
+ inserted_text[0] = insert_char_override;
+ insert_into_textbuf(obedit, insert_char_override);
+ text_update_edited(C, obedit, FO_EDIT);
+ }
+ else {
+ BLI_assert(event->utf8_buf[0]);
if (accentcode) {
if (ef->pos > 0) {
- inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], ascii);
+ inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1],
+ BLI_str_utf8_as_unicode(event->utf8_buf));
ef->textbuf[ef->pos - 1] = inserted_text[0];
}
- accentcode = 0;
+ accentcode = false;
}
else if (event->utf8_buf[0]) {
inserted_text[0] = BLI_str_utf8_as_unicode(event->utf8_buf);
- ascii = inserted_text[0];
- insert_into_textbuf(obedit, ascii);
- accentcode = 0;
- }
- else if (ascii) {
- insert_into_textbuf(obedit, ascii);
- accentcode = 0;
+ insert_into_textbuf(obedit, inserted_text[0]);
+ accentcode = false;
}
else {
BLI_assert(0);
@@ -1702,11 +1704,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
kill_selection(obedit, 1);
text_update_edited(C, obedit, FO_EDIT);
}
- else {
- inserted_text[0] = ascii;
- insert_into_textbuf(obedit, ascii);
- text_update_edited(C, obedit, FO_EDIT);
- }
}
else {
return OPERATOR_PASS_THROUGH;
@@ -1720,11 +1717,6 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
RNA_string_set(op->ptr, "text", inserted_utf8);
}
- /* reset property? */
- if (event_val == 0) {
- accentcode = 0;
- }
-
return OPERATOR_FINISHED;
}
@@ -1974,6 +1966,8 @@ static int set_case_exec(bContext *C, wmOperator *op)
void FONT_OT_case_set(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
/* identifiers */
ot->name = "Set Case";
ot->description = "Set font case";
@@ -1987,7 +1981,8 @@ void FONT_OT_case_set(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case");
+ prop = RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_TEXT);
}
/** \} */
diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c
index 09e5428b0f9..17ed75b9d10 100644
--- a/source/blender/editors/curve/editfont_undo.c
+++ b/source/blender/editors/curve/editfont_undo.c
@@ -19,6 +19,7 @@
#include "DNA_scene_types.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_undo_system.h"
#include "BKE_vfont.h"
@@ -307,8 +308,10 @@ static void undofont_free_data(UndoFont *uf)
static Object *editfont_object_from_context(bContext *C)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit && obedit->type == OB_FONT) {
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt
index 303d2fb71dc..945bba0a77c 100644
--- a/source/blender/editors/curves/CMakeLists.txt
+++ b/source/blender/editors/curves/CMakeLists.txt
@@ -13,6 +13,7 @@ set(INC
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
+ ../../bmesh
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/curves/intern/curves_add.cc b/source/blender/editors/curves/intern/curves_add.cc
index 79916253207..f234a58f439 100644
--- a/source/blender/editors/curves/intern/curves_add.cc
+++ b/source/blender/editors/curves/intern/curves_add.cc
@@ -102,10 +102,9 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi
MutableSpan<int> offsets = curves.offsets_for_write();
MutableSpan<float3> positions = curves.positions_for_write();
-
- float *radius_data = (float *)CustomData_add_layer_named(
- &curves.point_data, CD_PROP_FLOAT, CD_DEFAULT, nullptr, curves.point_num, "radius");
- MutableSpan<float> radii{radius_data, curves.points_num()};
+ bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
+ bke::SpanAttributeWriter<float> radius = attributes.lookup_or_add_for_write_only_span<float>(
+ "radius", ATTR_DOMAIN_POINT);
for (const int i : offsets.index_range()) {
offsets[i] = points_per_curve * i;
@@ -114,9 +113,9 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi
RandomNumberGenerator rng;
for (const int i : curves.curves_range()) {
- const IndexRange curve_range = curves.points_for_curve(i);
- MutableSpan<float3> curve_positions = positions.slice(curve_range);
- MutableSpan<float> curve_radii = radii.slice(curve_range);
+ const IndexRange points = curves.points_for_curve(i);
+ MutableSpan<float3> curve_positions = positions.slice(points);
+ MutableSpan<float> curve_radii = radius.span.slice(points);
const float theta = 2.0f * M_PI * rng.get_float();
const float phi = saacosf(2.0f * rng.get_float() - 1.0f);
@@ -135,6 +134,8 @@ bke::CurvesGeometry primitive_random_sphere(const int curves_size, const int poi
}
}
+ radius.finish();
+
return curves;
}
diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc
index a4492a1d516..54d91ccfc90 100644
--- a/source/blender/editors/curves/intern/curves_ops.cc
+++ b/source/blender/editors/curves/intern/curves_ops.cc
@@ -94,11 +94,53 @@ VectorSet<Curves *> get_unique_editable_curves(const bContext &C)
return unique_curves;
}
+static bool curves_poll_impl(bContext *C, const bool check_editable, const bool check_surface)
+{
+ Object *object = CTX_data_active_object(C);
+ if (object == nullptr || object->type != OB_CURVES) {
+ return false;
+ }
+ if (check_editable) {
+ if (!ED_operator_object_active_editable_ex(C, object)) {
+ return false;
+ }
+ }
+ if (check_surface) {
+ Curves &curves = *static_cast<Curves *>(object->data);
+ if (curves.surface == nullptr || curves.surface->type != OB_MESH) {
+ CTX_wm_operator_poll_msg_set(C, "Curves must have a mesh surface object set");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool editable_curves_with_surface_poll(bContext *C)
+{
+ return curves_poll_impl(C, true, true);
+}
+
+bool curves_with_surface_poll(bContext *C)
+{
+ return curves_poll_impl(C, false, true);
+}
+
+bool editable_curves_poll(bContext *C)
+{
+ return curves_poll_impl(C, false, false);
+}
+
+bool curves_poll(bContext *C)
+{
+ return curves_poll_impl(C, false, false);
+}
+
using bke::CurvesGeometry;
namespace convert_to_particle_system {
-static int find_mface_for_root_position(const Mesh &mesh,
+static int find_mface_for_root_position(const Span<MVert> verts,
+ const MFace *mface,
const Span<int> possible_mface_indices,
const float3 &root_pos)
{
@@ -110,14 +152,14 @@ static int find_mface_for_root_position(const Mesh &mesh,
int mface_i;
float best_distance_sq = FLT_MAX;
for (const int possible_mface_i : possible_mface_indices) {
- const MFace &possible_mface = mesh.mface[possible_mface_i];
+ const MFace &possible_mface = mface[possible_mface_i];
{
float3 point_in_triangle;
closest_on_tri_to_point_v3(point_in_triangle,
root_pos,
- mesh.mvert[possible_mface.v1].co,
- mesh.mvert[possible_mface.v2].co,
- mesh.mvert[possible_mface.v3].co);
+ verts[possible_mface.v1].co,
+ verts[possible_mface.v2].co,
+ verts[possible_mface.v3].co);
const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
if (distance_sq < best_distance_sq) {
best_distance_sq = distance_sq;
@@ -129,9 +171,9 @@ static int find_mface_for_root_position(const Mesh &mesh,
float3 point_in_triangle;
closest_on_tri_to_point_v3(point_in_triangle,
root_pos,
- mesh.mvert[possible_mface.v1].co,
- mesh.mvert[possible_mface.v3].co,
- mesh.mvert[possible_mface.v4].co);
+ verts[possible_mface.v1].co,
+ verts[possible_mface.v3].co,
+ verts[possible_mface.v4].co);
const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
if (distance_sq < best_distance_sq) {
best_distance_sq = distance_sq;
@@ -145,25 +187,22 @@ static int find_mface_for_root_position(const Mesh &mesh,
/**
* \return Barycentric coordinates in the #MFace.
*/
-static float4 compute_mface_weights_for_position(const Mesh &mesh,
+static float4 compute_mface_weights_for_position(const Span<MVert> verts,
const MFace &mface,
const float3 &position)
{
float4 mface_weights;
if (mface.v4) {
float mface_verts_su[4][3];
- copy_v3_v3(mface_verts_su[0], mesh.mvert[mface.v1].co);
- copy_v3_v3(mface_verts_su[1], mesh.mvert[mface.v2].co);
- copy_v3_v3(mface_verts_su[2], mesh.mvert[mface.v3].co);
- copy_v3_v3(mface_verts_su[3], mesh.mvert[mface.v4].co);
+ copy_v3_v3(mface_verts_su[0], verts[mface.v1].co);
+ copy_v3_v3(mface_verts_su[1], verts[mface.v2].co);
+ copy_v3_v3(mface_verts_su[2], verts[mface.v3].co);
+ copy_v3_v3(mface_verts_su[3], verts[mface.v4].co);
interp_weights_poly_v3(mface_weights, mface_verts_su, 4, position);
}
else {
- interp_weights_tri_v3(mface_weights,
- mesh.mvert[mface.v1].co,
- mesh.mvert[mface.v2].co,
- mesh.mvert[mface.v3].co,
- position);
+ interp_weights_tri_v3(
+ mface_weights, verts[mface.v1].co, verts[mface.v2].co, verts[mface.v3].co, position);
mface_weights[3] = 0.0f;
}
return mface_weights;
@@ -246,6 +285,9 @@ static void try_convert_single_object(Object &curves_ob,
/* Prepare transformation matrices. */
const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
+ const MFace *mfaces = (const MFace *)CustomData_get_layer(&surface_me.fdata, CD_MFACE);
+ const Span<MVert> verts = surface_me.verts();
+
for (const int new_hair_i : IndexRange(hair_num)) {
const int curve_i = new_hair_i;
const IndexRange points = curves.points_for_curve(curve_i);
@@ -264,11 +306,10 @@ static void try_convert_single_object(Object &curves_ob,
const int poly_i = looptri.poly;
const int mface_i = find_mface_for_root_position(
- surface_me, poly_to_mface_map[poly_i], root_pos_su);
- const MFace &mface = surface_me.mface[mface_i];
+ verts, mfaces, poly_to_mface_map[poly_i], root_pos_su);
+ const MFace &mface = mfaces[mface_i];
- const float4 mface_weights = compute_mface_weights_for_position(
- surface_me, mface, root_pos_su);
+ const float4 mface_weights = compute_mface_weights_for_position(verts, mface, root_pos_su);
ParticleData &particle = particles[new_hair_i];
const int num_keys = points.size();
@@ -337,16 +378,6 @@ static int curves_convert_to_particle_system_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static bool curves_convert_to_particle_system_poll(bContext *C)
-{
- Object *ob = CTX_data_active_object(C);
- if (ob == nullptr || ob->type != OB_CURVES) {
- return false;
- }
- Curves &curves = *static_cast<Curves *>(ob->data);
- return curves.surface != nullptr;
-}
-
} // namespace convert_to_particle_system
static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot)
@@ -355,7 +386,7 @@ static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot)
ot->idname = "CURVES_OT_convert_to_particle_system";
ot->description = "Add a new or update an existing hair particle system on the surface object";
- ot->poll = convert_to_particle_system::curves_convert_to_particle_system_poll;
+ ot->poll = curves_with_surface_poll;
ot->exec = convert_to_particle_system::curves_convert_to_particle_system_exec;
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
@@ -440,6 +471,7 @@ static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &p
static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNUSED(op))
{
Main &bmain = *CTX_data_main(C);
+ Scene &scene = *CTX_data_scene(C);
ViewLayer &view_layer = *CTX_data_view_layer(C);
Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
Object *ob_from_orig = ED_object_active_context(C);
@@ -464,7 +496,7 @@ static int curves_convert_from_particle_system_exec(bContext *C, wmOperator *UNU
psys_eval = psmd->psys;
}
- Object *ob_new = BKE_object_add(&bmain, &view_layer, OB_CURVES, psys_eval->name);
+ Object *ob_new = BKE_object_add(&bmain, &scene, &view_layer, OB_CURVES, psys_eval->name);
Curves *curves_id = static_cast<Curves *>(ob_new->data);
BKE_object_apply_mat4(ob_new, ob_from_orig->obmat, true, false);
bke::CurvesGeometry::wrap(curves_id->geometry) = particles_to_curves(*ob_from_eval, *psys_eval);
@@ -501,22 +533,6 @@ enum class AttachMode {
Deform,
};
-static bool snap_curves_to_surface_poll(bContext *C)
-{
- Object *ob = CTX_data_active_object(C);
- if (ob == nullptr || ob->type != OB_CURVES) {
- return false;
- }
- if (!ED_operator_object_active_editable_ex(C, ob)) {
- return false;
- }
- Curves &curves = *static_cast<Curves *>(ob->data);
- if (curves.surface == nullptr) {
- return false;
- }
- return true;
-}
-
static void snap_curves_to_surface_exec_object(Object &curves_ob,
const Object &surface_ob,
const AttachMode attach_mode,
@@ -526,14 +542,14 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob,
Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- Mesh &surface_mesh = *static_cast<Mesh *>(surface_ob.data);
-
- MeshComponent surface_mesh_component;
- surface_mesh_component.replace(&surface_mesh, GeometryOwnershipType::ReadOnly);
-
+ const Mesh &surface_mesh = *static_cast<const Mesh *>(surface_ob.data);
+ const Span<MVert> verts = surface_mesh.verts();
+ const Span<MLoop> loops = surface_mesh.loops();
+ const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh),
+ BKE_mesh_runtime_looptri_len(&surface_mesh)};
VArraySpan<float2> surface_uv_map;
if (curves_id.surface_uv_map != nullptr) {
- const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(surface_mesh);
+ const bke::AttributeAccessor surface_attributes = surface_mesh.attributes();
surface_uv_map = surface_attributes
.lookup(curves_id.surface_uv_map, ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2)
.typed<float2>();
@@ -542,9 +558,6 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob,
MutableSpan<float3> positions_cu = curves.positions_for_write();
MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
- const Span<MLoopTri> surface_looptris = {BKE_mesh_runtime_looptri_ensure(&surface_mesh),
- BKE_mesh_runtime_looptri_len(&surface_mesh)};
-
const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
switch (attach_mode) {
@@ -591,9 +604,9 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob,
const float2 &uv0 = surface_uv_map[corner0];
const float2 &uv1 = surface_uv_map[corner1];
const float2 &uv2 = surface_uv_map[corner2];
- const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[corner0].v].co;
- const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[corner1].v].co;
- const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[corner2].v].co;
+ const float3 &p0_su = verts[loops[corner0].v].co;
+ const float3 &p1_su = verts[loops[corner1].v].co;
+ const float3 &p2_su = verts[loops[corner2].v].co;
float3 bary_coords;
interp_weights_tri_v3(bary_coords, p0_su, p1_su, p2_su, new_first_point_pos_su);
const float2 uv = attribute_math::mix3(bary_coords, uv0, uv1, uv2);
@@ -627,9 +640,9 @@ static void snap_curves_to_surface_exec_object(Object &curves_ob,
const MLoopTri &looptri = *lookup_result.looptri;
const float3 &bary_coords = lookup_result.bary_weights;
- const float3 &p0_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[0]].v].co;
- const float3 &p1_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[1]].v].co;
- const float3 &p2_su = surface_mesh.mvert[surface_mesh.mloop[looptri.tri[2]].v].co;
+ const float3 &p0_su = verts[loops[looptri.tri[0]].v].co;
+ const float3 &p1_su = verts[loops[looptri.tri[1]].v].co;
+ const float3 &p2_su = verts[loops[looptri.tri[2]].v].co;
float3 new_first_point_pos_su;
interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
@@ -681,7 +694,7 @@ static int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
}
- /* Refresh the entire window to also clear eventual modifier and nodes editor warnings.*/
+ /* Refresh the entire window to also clear eventual modifier and nodes editor warnings. */
WM_event_add_notifier(C, NC_WINDOW, nullptr);
return OPERATOR_FINISHED;
@@ -697,7 +710,7 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
ot->idname = "CURVES_OT_snap_curves_to_surface";
ot->description = "Move curves so that the first point is exactly on the surface mesh";
- ot->poll = snap_curves_to_surface_poll;
+ ot->poll = editable_curves_with_surface_poll;
ot->exec = snap_curves_to_surface_exec;
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
@@ -726,21 +739,6 @@ static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
"How to find the point on the surface to attach to");
}
-bool selection_operator_poll(bContext *C)
-{
- const Object *object = CTX_data_active_object(C);
- if (object == nullptr) {
- return false;
- }
- if (object->type != OB_CURVES) {
- return false;
- }
- if (!BKE_id_is_editable(CTX_data_main(C), static_cast<const ID *>(object->data))) {
- return false;
- }
- return true;
-}
-
namespace set_selection_domain {
static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
@@ -758,6 +756,9 @@ static int curves_set_selection_domain_exec(bContext *C, wmOperator *op)
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id->geometry);
bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
+ if (curves.points_num() == 0) {
+ continue;
+ }
if (old_domain == ATTR_DOMAIN_POINT && domain == ATTR_DOMAIN_CURVE) {
VArray<float> curve_selection = curves.adapt_domain(
@@ -794,7 +795,7 @@ static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
ot->description = "Change the mode used for selection masking in curves sculpt mode";
ot->exec = set_selection_domain::curves_set_selection_domain_exec;
- ot->poll = selection_operator_poll;
+ ot->poll = editable_curves_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -830,7 +831,7 @@ static void CURVES_OT_disable_selection(wmOperatorType *ot)
ot->description = "Disable the drawing of influence of selection in sculpt mode";
ot->exec = disable_selection::curves_disable_selection_exec;
- ot->poll = selection_operator_poll;
+ ot->poll = editable_curves_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
@@ -936,7 +937,7 @@ static void SCULPT_CURVES_OT_select_all(wmOperatorType *ot)
ot->description = "(De)select all control points";
ot->exec = select_all::select_all_exec;
- ot->poll = selection_operator_poll;
+ ot->poll = editable_curves_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/geometry/CMakeLists.txt b/source/blender/editors/geometry/CMakeLists.txt
index e0c440b09b4..6e28bb3e8ec 100644
--- a/source/blender/editors/geometry/CMakeLists.txt
+++ b/source/blender/editors/geometry/CMakeLists.txt
@@ -10,6 +10,7 @@ set(INC
../../makesrna
../../windowmanager
../../../../intern/guardedalloc
+ ../../bmesh
)
set(INC_SYS
diff --git a/source/blender/editors/geometry/geometry_attributes.cc b/source/blender/editors/geometry/geometry_attributes.cc
index 4cf14334ac7..14f2f8c6af5 100644
--- a/source/blender/editors/geometry/geometry_attributes.cc
+++ b/source/blender/editors/geometry/geometry_attributes.cc
@@ -275,14 +275,14 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
ID *ob_data = static_cast<ID *>(ob->data);
- CustomDataLayer *layer = BKE_id_attributes_active_get(ob_data);
+ const CustomDataLayer *layer = BKE_id_attributes_active_get(ob_data);
const std::string name = layer->name;
const ConvertAttributeMode mode = static_cast<ConvertAttributeMode>(
RNA_enum_get(op->ptr, "mode"));
Mesh *mesh = reinterpret_cast<Mesh *>(ob_data);
- bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
/* General conversion steps are always the same:
* 1. Convert old data to right domain and data type.
@@ -305,7 +305,7 @@ static int geometry_attribute_convert_exec(bContext *C, wmOperator *op)
void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__);
src_varray.materialize_to_uninitialized(new_data);
attributes.remove(name);
- attributes.add(name, dst_domain, dst_type, blender::bke::AttributeInitMove(new_data));
+ attributes.add(name, dst_domain, dst_type, blender::bke::AttributeInitMoveArray(new_data));
break;
}
case ConvertAttributeMode::UVMap: {
@@ -405,7 +405,7 @@ void GEOMETRY_OT_color_attribute_add(wmOperatorType *ot)
prop = RNA_def_float_color(
ot->srna, "color", 4, nullptr, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f);
- RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
+ RNA_def_property_subtype(prop, PROP_COLOR);
RNA_def_property_float_array_default(prop, default_color);
}
@@ -471,11 +471,6 @@ static int geometry_color_attribute_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (GS(id->name) == ID_ME) {
- Mesh *me = static_cast<Mesh *>(ob->data);
- BKE_mesh_update_customdata_pointers(me, true);
- }
-
DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, id);
@@ -651,8 +646,7 @@ bool ED_geometry_attribute_convert(Mesh *mesh,
return false;
}
- blender::bke::MutableAttributeAccessor attributes = blender::bke::mesh_attributes_for_write(
- *mesh);
+ blender::bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
GVArray src_varray = attributes.lookup_or_default(name, new_domain, new_type);
@@ -660,7 +654,7 @@ bool ED_geometry_attribute_convert(Mesh *mesh,
void *new_data = MEM_malloc_arrayN(src_varray.size(), cpp_type.size(), __func__);
src_varray.materialize_to_uninitialized(new_data);
attributes.remove(name);
- attributes.add(name, new_domain, new_type, blender::bke::AttributeInitMove(new_data));
+ attributes.add(name, new_domain, new_type, blender::bke::AttributeInitMoveArray(new_data));
int *active_index = BKE_id_attributes_active_index_p(&mesh->id);
if (*active_index > 0) {
diff --git a/source/blender/editors/gizmo_library/CMakeLists.txt b/source/blender/editors/gizmo_library/CMakeLists.txt
index 0484c47f081..84181b5f95d 100644
--- a/source/blender/editors/gizmo_library/CMakeLists.txt
+++ b/source/blender/editors/gizmo_library/CMakeLists.txt
@@ -13,7 +13,6 @@ set(INC
../../windowmanager
../../../../intern/clog
../../../../intern/eigen
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
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 2c886230f10..6eac235a191 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/button2d_gizmo.c
@@ -217,7 +217,7 @@ static void button2d_draw_intern(const bContext *C,
GPU_batch_uniform_1f(button->shape_batch[i], "lineWidth", gz->line_width * U.pixelsize);
}
else {
- GPU_batch_program_set_builtin(button->shape_batch[i], GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_batch_program_set_builtin(button->shape_batch[i], GPU_SHADER_3D_UNIFORM_COLOR);
}
/* Invert line color for wire. */
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
index 54aa5d16941..600abaf3737 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
@@ -388,7 +388,7 @@ static void cage2d_draw_box_interaction(const float color[4],
.pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT),
.col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT),
};
- immBindBuiltinProgram(is_solid ? GPU_SHADER_2D_FLAT_COLOR : GPU_SHADER_3D_POLYLINE_FLAT_COLOR);
+ immBindBuiltinProgram(is_solid ? GPU_SHADER_3D_FLAT_COLOR : GPU_SHADER_3D_POLYLINE_FLAT_COLOR);
{
if (is_solid) {
@@ -546,7 +546,7 @@ static void cage2d_draw_circle_handles(const rctf *r,
const int resolu = 12;
const float rad[2] = {margin[0] / 3, margin[1] / 3};
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fv(color);
/* should really divide by two, but looks too bulky. */
@@ -598,7 +598,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
if (false) {
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv((const float[4]){1, 1, 1, 0.5f});
float s = 0.5f;
immRectf(pos, -s, -s, s, s);
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index 09a3cac0d48..866df16f3d6 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -12,8 +12,8 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
+ ../../bmesh
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c
index 2d613e2f433..ea34e6530fa 100644
--- a/source/blender/editors/gpencil/annotate_paint.c
+++ b/source/blender/editors/gpencil/annotate_paint.c
@@ -1715,7 +1715,7 @@ static void annotation_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_pt
if (p->paintmode == GP_PAINTMODE_ERASER) {
GPUVertFormat *format = immVertexFormat();
const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
@@ -1725,7 +1725,7 @@ static void annotation_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_pt
immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -1782,7 +1782,7 @@ static void annotation_draw_stabilizer(bContext *C, int x, int y, void *p_ptr)
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_width(1.25f);
@@ -2346,7 +2346,7 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev
return OPERATOR_RUNNING_MODAL;
}
-/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
+/* gpencil modal operator stores area, which can be removed while using it (like full-screen). */
static bool annotation_area_exists(bContext *C, ScrArea *area_test)
{
bScreen *screen = CTX_wm_screen(C);
@@ -2641,7 +2641,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
/* handle mode-specific events */
if (p->status == GP_STATUS_PAINTING) {
/* handle painting mouse-movements? */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
+ if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) {
annotation_add_missing_events(C, op, event, p);
@@ -2698,7 +2698,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve
}
}
- /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
+ /* gpencil modal operator stores area, which can be removed while using it (like full-screen). */
if (0 == annotation_area_exists(C, p->area)) {
estate = OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c
index e8e6d328804..b88b33913ac 100644
--- a/source/blender/editors/gpencil/gpencil_add_blank.c
+++ b/source/blender/editors/gpencil/gpencil_add_blank.c
@@ -19,6 +19,8 @@
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BLT_translation.h"
+
#include "DEG_depsgraph.h"
#include "ED_gpencil.h"
@@ -34,7 +36,7 @@ typedef struct ColorTemplate {
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);
+ Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, DATA_(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);
@@ -52,7 +54,7 @@ static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate
/* Color Data */
static const ColorTemplate gp_stroke_material_black = {
- "Black",
+ N_("Black"),
{0.0f, 0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c
index 964a57a8ced..2ac26c927f4 100644
--- a/source/blender/editors/gpencil/gpencil_add_lineart.c
+++ b/source/blender/editors/gpencil/gpencil_add_lineart.c
@@ -21,6 +21,8 @@
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BLT_translation.h"
+
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -40,7 +42,7 @@ static int gpencil_lineart_material(Main *bmain,
const bool fill)
{
int index;
- Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index);
+ Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, DATA_(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);
@@ -59,7 +61,7 @@ static int gpencil_lineart_material(Main *bmain,
/* Color Data */
static const ColorTemplate gp_stroke_material_black = {
- "Black",
+ N_("Black"),
{0.0f, 0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index bc046e89d21..00066d5f2b8 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -19,6 +19,8 @@
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BLT_translation.h"
+
#include "DEG_depsgraph.h"
#include "ED_gpencil.h"
@@ -54,7 +56,7 @@ static int gpencil_monkey_color(
Main *bmain, Object *ob, const ColorTemplate *pct, bool stroke, bool fill)
{
int index;
- Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index);
+ Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, DATA_(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);
@@ -781,37 +783,37 @@ static const float data27[33 * GP_PRIM_DATABUF_SIZE] = {
/* Monkey Color Data */
static const ColorTemplate gp_monkey_pct_black = {
- "Black",
+ N_("Black"),
{0.0f, 0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
static const ColorTemplate gp_monkey_pct_skin = {
- "Skin",
+ N_("Skin"),
{0.733f, 0.569f, 0.361f, 1.0f},
{0.745f, 0.502f, 0.278f, 1.0f},
};
static const ColorTemplate gp_monkey_pct_skin_light = {
- "Skin_Light",
+ N_("Skin_Light"),
{0.914f, 0.827f, 0.635f, 1.0f},
{0.913f, 0.828f, 0.637f, 0.0f},
};
static const ColorTemplate gp_monkey_pct_skin_shadow = {
- "Skin_Shadow",
+ N_("Skin_Shadow"),
{0.322f, 0.29f, 0.224f, 0.5f},
{0.32f, 0.29f, 0.223f, 0.3f},
};
static const ColorTemplate gp_monkey_pct_eyes = {
- "Eyes",
+ N_("Eyes"),
{0.553f, 0.39f, 0.266f, 0.0f},
{0.847f, 0.723f, 0.599f, 1.0f},
};
static const ColorTemplate gp_monkey_pct_pupils = {
- "Pupils",
+ N_("Pupils"),
{0.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f},
};
@@ -821,6 +823,8 @@ static const ColorTemplate gp_monkey_pct_pupils = {
void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
{
+ /* Original model created by Matias Mendiola. */
+
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
bGPdata *gpd = (bGPdata *)ob->data;
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index e24964c4832..8522c81cb39 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -19,6 +19,8 @@
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BLT_translation.h"
+
#include "DEG_depsgraph.h"
#include "ED_gpencil.h"
@@ -37,7 +39,7 @@ static int gpencil_stroke_material(Main *bmain,
const bool fill)
{
int index;
- Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index);
+ Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, DATA_(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);
@@ -150,37 +152,37 @@ static const float data0[175 * GP_PRIM_DATABUF_SIZE] = {
/* Color Data */
static const ColorTemplate gp_stroke_material_black = {
- "Black",
+ N_("Black"),
{0.0f, 0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
static const ColorTemplate gp_stroke_material_white = {
- "White",
+ N_("White"),
{1.0f, 1.0f, 1.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
static const ColorTemplate gp_stroke_material_red = {
- "Red",
+ N_("Red"),
{1.0f, 0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
static const ColorTemplate gp_stroke_material_green = {
- "Green",
+ N_("Green"),
{0.0f, 1.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
static const ColorTemplate gp_stroke_material_blue = {
- "Blue",
+ N_("Blue"),
{0.0f, 0.0f, 1.0f, 1.0f},
{0.0f, 0.0f, 0.0f, 0.0f},
};
static const ColorTemplate gp_stroke_material_grey = {
- "Grey",
+ N_("Grey"),
{0.358f, 0.358f, 0.358f, 1.0f},
{0.5f, 0.5f, 0.5f, 1.0f},
};
@@ -190,6 +192,8 @@ static const ColorTemplate gp_stroke_material_grey = {
void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
{
+ /* Original design created by Daniel M. Lara and Matias Mendiola. */
+
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
bGPdata *gpd = (bGPdata *)ob->data;
diff --git a/source/blender/editors/gpencil/gpencil_armature.c b/source/blender/editors/gpencil/gpencil_armature.c
index d389f7eb5dd..5f5a4b41b27 100644
--- a/source/blender/editors/gpencil/gpencil_armature.c
+++ b/source/blender/editors/gpencil/gpencil_armature.c
@@ -29,6 +29,7 @@
#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_modifier.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_object_deform.h"
#include "BKE_report.h"
@@ -528,6 +529,7 @@ static bool gpencil_generate_weights_poll(bContext *C)
return false;
}
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
bGPdata *gpd = (bGPdata *)ob->data;
@@ -536,7 +538,8 @@ static bool gpencil_generate_weights_poll(bContext *C)
}
/* need some armature in the view layer */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->object->type == OB_ARMATURE) {
return true;
}
@@ -548,6 +551,7 @@ static bool gpencil_generate_weights_poll(bContext *C)
static int gpencil_generate_weights_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = CTX_data_active_object(C);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
@@ -566,7 +570,8 @@ static int gpencil_generate_weights_exec(bContext *C, wmOperator *op)
/* get armature */
const int arm_idx = RNA_enum_get(op->ptr, "armature");
if (arm_idx > 0) {
- Base *base = BLI_findlink(&view_layer->object_bases, arm_idx - 1);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BLI_findlink(BKE_view_layer_object_bases_get(view_layer), arm_idx - 1);
ob_arm = base->object;
}
else {
@@ -607,6 +612,7 @@ static const EnumPropertyItem *gpencil_armatures_enum_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
EnumPropertyItem *item = NULL, item_tmp = {0};
int totitem = 0;
@@ -623,7 +629,8 @@ static const EnumPropertyItem *gpencil_armatures_enum_itemf(bContext *C,
RNA_enum_item_add(&item, &totitem, &item_tmp);
i++;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
if (ob->type == OB_ARMATURE) {
item_tmp.identifier = item_tmp.name = ob->id.name + 2;
diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.cc b/source/blender/editors/gpencil/gpencil_bake_animation.cc
index e480852a9bb..28d4e1c6d42 100644
--- a/source/blender/editors/gpencil/gpencil_bake_animation.cc
+++ b/source/blender/editors/gpencil/gpencil_bake_animation.cc
@@ -327,7 +327,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op
/* Reproject stroke. */
if (project_type != GP_REPROJECT_KEEP) {
ED_gpencil_stroke_reproject(
- depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false);
+ depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false, 0.0f);
}
else {
BKE_gpencil_stroke_geometry_update(gpd_dst, gps);
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 25b5466e260..bf78111a636 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -588,7 +588,7 @@ static void gpencil_stroke_path_animation(bContext *C,
}
/* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
@@ -1303,6 +1303,7 @@ static void gpencil_layer_to_curve(bContext *C,
ob = BKE_object_add_only_object(bmain, OB_CURVES_LEGACY, gpl->info);
cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVES_LEGACY);
BKE_collection_object_add(bmain, collection, ob);
+ BKE_view_layer_synced_ensure(scene, view_layer);
base_new = BKE_view_layer_base_find(view_layer, ob);
DEG_relations_tag_update(bmain); /* added object */
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index b7ac73b9692..340288b2d74 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -2076,6 +2076,9 @@ static void gpencil_brush_delete_mode_brushes(Main *bmain,
}
BKE_brush_delete(bmain, brush);
+ if (brush == brush_active) {
+ brush_active = NULL;
+ }
}
}
@@ -2109,8 +2112,8 @@ static int gpencil_brush_reset_all_exec(bContext *C, wmOperator *UNUSED(op))
char tool = '0';
if (paint) {
- Brush *brush_active = paint->brush;
- if (brush_active) {
+ if (paint->brush) {
+ Brush *brush_active = paint->brush;
switch (mode) {
case CTX_MODE_PAINT_GPENCIL: {
tool = brush_active->gpencil_tool;
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 06f3c169fa9..837a9390b6c 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -67,6 +67,7 @@
#include "ED_armature.h"
#include "ED_gpencil.h"
+#include "ED_keyframing.h"
#include "ED_object.h"
#include "ED_outliner.h"
#include "ED_screen.h"
@@ -1713,12 +1714,17 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
}
}
- /* Ensure we have a frame to draw into
+ /* Ensure we have a frame to draw into.
* NOTE: Since this is an op which creates strokes,
- * we are obliged to add a new frame if one
- * doesn't exist already
+ * we reuse active frame or add a new frame if one
+ * doesn't exist already depending on REC button status.
*/
- gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW);
+ if (IS_AUTOKEY_ON(scene) || (gpl->actframe == NULL)) {
+ gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_ADD_NEW);
+ }
+ else {
+ gpf = BKE_gpencil_layer_frame_get(gpl, scene->r.cfra, GP_GETFRAME_USE_PREV);
+ }
if (gpf) {
/* Create new stroke */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
@@ -3395,6 +3401,7 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
const int type = RNA_enum_get(op->ptr, "type");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* sanity checks */
if (ELEM(NULL, gpd)) {
@@ -3404,46 +3411,57 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
bool changed = false;
/* loop all selected strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- if (gpl->actframe == NULL) {
- continue;
- }
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
+ }
- /* skip strokes that are not selected or invalid for current view */
- if (((gps->flag & GP_STROKE_SELECT) == 0) || (ED_gpencil_stroke_can_use(C, gps) == false)) {
- continue;
- }
- /* skip hidden or locked colors */
- if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
- (gp_style->flag & GP_MATERIAL_LOCKED)) {
- continue;
- }
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- short prev_first = gps->caps[0];
- short prev_last = gps->caps[1];
+ /* skip strokes that are not selected or invalid for current view */
+ if (((gps->flag & GP_STROKE_SELECT) == 0) ||
+ (ED_gpencil_stroke_can_use(C, gps) == false)) {
+ continue;
+ }
+ /* skip hidden or locked colors */
+ if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
+ (gp_style->flag & GP_MATERIAL_LOCKED)) {
+ continue;
+ }
+
+ short prev_first = gps->caps[0];
+ short prev_last = gps->caps[1];
+
+ if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_START)) {
+ ++gps->caps[0];
+ if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
+ gps->caps[0] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_END)) {
+ ++gps->caps[1];
+ if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
+ gps->caps[1] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
+ gps->caps[0] = GP_STROKE_CAP_ROUND;
+ gps->caps[1] = GP_STROKE_CAP_ROUND;
+ }
- if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_START)) {
- ++gps->caps[0];
- if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
- gps->caps[0] = GP_STROKE_CAP_ROUND;
+ if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) {
+ changed = true;
+ }
}
- }
- if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_END)) {
- ++gps->caps[1];
- if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
- gps->caps[1] = GP_STROKE_CAP_ROUND;
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
}
}
- if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
- gps->caps[0] = GP_STROKE_CAP_ROUND;
- gps->caps[1] = GP_STROKE_CAP_ROUND;
- }
-
- if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) {
- changed = true;
- }
}
}
CTX_DATA_END;
@@ -3550,9 +3568,9 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
bGPdata *gpd = ED_gpencil_data_get_active(C);
bGPDlayer *activegpl = BKE_gpencil_layer_active_get(gpd);
Object *ob = CTX_data_active_object(C);
- /* Limit the number of strokes to join. It makes no sense to allow an very high number of strokes
- * for CPU time and because to have a stroke with thousands of points is unpractical, so limit
- * this number avoid to joining a full frame scene in one single stroke. */
+ /* Limit the number of strokes to join. It makes no sense to allow an very high number of
+ * strokes for CPU time and because to have a stroke with thousands of points is unpractical,
+ * so limit this number avoid to joining a full frame scene in one single stroke. */
const int max_join_strokes = 128;
const int type = RNA_enum_get(op->ptr, "type");
@@ -3638,7 +3656,7 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
}
elem = &strokes_list[i];
/* Join new_stroke and stroke B. */
- BKE_gpencil_stroke_join(gps_new, elem->gps, leave_gaps, true, false);
+ BKE_gpencil_stroke_join(gps_new, elem->gps, leave_gaps, true, false, true);
elem->used = true;
}
@@ -3709,35 +3727,44 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+
bool changed = false;
- /* read all selected strokes */
+ /* Read all selected strokes. */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = gpl->actframe;
- if (gpf == NULL) {
- continue;
- }
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* 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, gps) == false) {
+ 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) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* 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, gps) == false) {
+ continue;
+ }
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* Flip stroke. */
- BKE_gpencil_stroke_flip(gps);
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ }
+ else {
+ /* Flip stroke. */
+ BKE_gpencil_stroke_flip(gps);
+ changed = true;
+ }
+ }
}
-
- changed = true;
+ }
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
}
}
}
@@ -3770,6 +3797,101 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Stroke Start Set Operator
+ * \{ */
+
+static int gpencil_stroke_start_set_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+
+ /* sanity checks */
+ if (ELEM(NULL, ob, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ if (is_curve_edit) {
+ BKE_report(op->reports, RPT_ERROR, "Curve Edit mode not supported");
+ return OPERATOR_CANCELLED;
+ }
+
+ bool changed = false;
+ /* Read all selected strokes. */
+ 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) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* 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, gps) == false) {
+ continue;
+ }
+
+ /* Only cyclic strokes. */
+ if ((gps->flag & GP_STROKE_CYCLIC) == 0) {
+ continue;
+ }
+
+ /* Find first selected point and set start. */
+ bGPDspoint *pt;
+ for (int i = 0; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ if (pt->flag & GP_SPOINT_SELECT) {
+ BKE_gpencil_stroke_start_set(gps, i);
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
+ changed = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ if (changed) {
+ /* 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_start_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Set Start Point";
+ ot->idname = "GPENCIL_OT_stroke_start_set";
+ ot->description = "Set start point for cyclic strokes";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_start_set_exec;
+ ot->poll = gpencil_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Stroke Re-project Operator
* \{ */
@@ -3783,6 +3905,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
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);
+ const float offset = RNA_float_get(op->ptr, "offset");
/* Init snap context for geometry projection. */
SnapObjectContext *sctx = NULL;
@@ -3822,7 +3945,8 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
BKE_scene_graph_update_for_newframe(depsgraph);
}
- ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
+ ED_gpencil_stroke_reproject(
+ depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original, offset);
if (is_curve_edit && gps->editcurve != NULL) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
@@ -3862,8 +3986,29 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void gpencil_strokes_reproject_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *row;
+
+ const eGP_ReprojectModes type = RNA_enum_get(op->ptr, "type");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "type", 0, NULL, ICON_NONE);
+
+ if (type == GP_REPROJECT_SURFACE) {
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "offset", 0, NULL, ICON_NONE);
+ }
+ row = uiLayoutRow(layout, true);
+ uiItemR(row, op->ptr, "keep_original", 0, NULL, ICON_NONE);
+}
+
void GPENCIL_OT_reproject(wmOperatorType *ot)
{
+ PropertyRNA *prop;
static const EnumPropertyItem reproject_type[] = {
{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"},
@@ -3901,6 +4046,7 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
ot->invoke = WM_menu_invoke;
ot->exec = gpencil_strokes_reproject_exec;
ot->poll = gpencil_strokes_edit3d_poll;
+ ot->ui = gpencil_strokes_reproject_ui;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -3909,12 +4055,15 @@ void GPENCIL_OT_reproject(wmOperatorType *ot)
ot->prop = RNA_def_enum(
ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
- RNA_def_boolean(
+ prop = RNA_def_boolean(
ot->srna,
"keep_original",
0,
"Keep Original",
"Keep original strokes and create a copy before reprojecting instead of reproject them");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP);
+
+ RNA_def_float(ot->srna, "offset", 0.0f, 0.0f, 10.0f, "Surface Offset", "", 0.0f, 10.0f);
}
static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
@@ -3939,6 +4088,284 @@ static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_FINISHED;
}
+/* -------------------------------------------------------------------- */
+/** \name Stroke Perimeter from View Operator
+ * \{ */
+
+enum {
+ GP_PERIMETER_VIEW = 0,
+ GP_PERIMETER_FRONT = 1,
+ GP_PERIMETER_SIDE = 2,
+ GP_PERIMETER_TOP = 3,
+ GP_PERIMETER_CAMERA = 4,
+};
+
+enum {
+ GP_STROKE_USE_ACTIVE_MATERIAL = 0,
+ GP_STROKE_USE_CURRENT_MATERIAL = 1,
+ GP_STROKE_USE_NEW_MATERIAL = 2,
+};
+
+static int gpencil_stroke_outline_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)ob->data;
+ const int subdivisions = RNA_int_get(op->ptr, "subdivisions");
+ const float length = RNA_float_get(op->ptr, "length");
+ const bool keep = RNA_boolean_get(op->ptr, "keep");
+ const int thickness = RNA_int_get(op->ptr, "thickness");
+
+ const int view_mode = RNA_enum_get(op->ptr, "view_mode");
+ const int material_mode = RNA_enum_get(op->ptr, "material_mode");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bool changed = false;
+
+ float viewmat[4][4];
+ copy_m4_m4(viewmat, rv3d->viewmat);
+
+ switch (view_mode) {
+ case GP_PERIMETER_FRONT:
+ unit_m4(rv3d->viewmat);
+ viewmat[1][1] = 0.0f;
+ viewmat[1][2] = -1.0f;
+
+ viewmat[2][1] = 1.0f;
+ viewmat[2][2] = 0.0f;
+
+ viewmat[3][2] = -10.0f;
+ break;
+ case GP_PERIMETER_SIDE:
+ zero_m4(viewmat);
+ viewmat[0][2] = 1.0f;
+ viewmat[1][0] = 1.0f;
+ viewmat[2][1] = 1.0f;
+ viewmat[3][3] = 1.0f;
+ break;
+ case GP_PERIMETER_TOP:
+ unit_m4(viewmat);
+ break;
+ case GP_PERIMETER_CAMERA: {
+ Scene *scene = CTX_data_scene(C);
+ Object *cam_ob = scene->camera;
+ if (cam_ob != NULL) {
+ invert_m4_m4(viewmat, cam_ob->obmat);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* Untag strokes to be sure nothing is pending. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ gps->flag &= ~GP_STROKE_TAG;
+ }
+ }
+ }
+ /* Create a new material. */
+ int mat_idx = 0;
+ if (material_mode == GP_STROKE_USE_NEW_MATERIAL) {
+ Material *ma = BKE_gpencil_object_material_new(bmain, ob, "Material", NULL);
+ MaterialGPencilStyle *gp_style = ma->gp_style;
+
+ gp_style->flag |= GP_MATERIAL_FILL_SHOW;
+ mat_idx = ob->totcol - 1;
+ }
+
+ /* loop all selected strokes */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (gpl->flag & GP_LAYER_HIDE) {
+ continue;
+ }
+ /* Prepare transform matrix. */
+ float diff_mat[4][4];
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat);
+
+ 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;
+ }
+
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
+ if (gps->totpoints == 0) {
+ continue;
+ }
+ if (!ED_gpencil_stroke_material_visible(ob, gps)) {
+ continue;
+ }
+
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0);
+
+ if (!is_stroke) {
+ 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;
+ /* Apply object scale to thickness. */
+ gps_duplicate->thickness *= mat4_to_scale(ob->obmat);
+ CLAMP_MIN(gps_duplicate->thickness, 1.0f);
+
+ /* Stroke. */
+ const float ovr_thickness = keep ? thickness : 0.0f;
+ bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
+ viewmat, gpd, gpl, gps_duplicate, subdivisions, diff_mat, ovr_thickness);
+ gps_perimeter->flag &= ~GP_STROKE_SELECT;
+ /* Assign material. */
+ switch (material_mode) {
+ case GP_STROKE_USE_ACTIVE_MATERIAL: {
+ if (ob->actcol - 1 < 0) {
+ gps_perimeter->mat_nr = 0;
+ }
+ else {
+ gps_perimeter->mat_nr = ob->actcol - 1;
+ }
+ break;
+ }
+ case GP_STROKE_USE_CURRENT_MATERIAL:
+ gps_perimeter->mat_nr = gps->mat_nr;
+ break;
+ case GP_STROKE_USE_NEW_MATERIAL:
+ gps_perimeter->mat_nr = mat_idx;
+ break;
+ default:
+ break;
+ }
+
+ /* Sample stroke. */
+ if (length > 0.0f) {
+ BKE_gpencil_stroke_sample(gpd, gps_perimeter, length, false, 0);
+ }
+ /* Set stroke thickness. */
+ gps_perimeter->thickness = thickness;
+
+ /* Set pressure constant. */
+ bGPDspoint *pt;
+ for (int i = 0; i < gps_perimeter->totpoints; i++) {
+ pt = &gps_perimeter->points[i];
+ pt->pressure = 1.0f;
+ }
+
+ /* Add perimeter stroke to frame. */
+ BLI_insertlinkafter(&gpf->strokes, gps, gps_perimeter);
+
+ /* Tag original stroke to be removed. */
+ gps->flag |= GP_STROKE_TAG;
+
+ /* Free Temp stroke. */
+ BKE_gpencil_free_stroke(gps_duplicate);
+ changed = true;
+ }
+
+ /* If not multi-edit, exit loop. */
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+ /* Free old strokes. */
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->flag & GP_STROKE_TAG) {
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
+ }
+ }
+ }
+
+ if (changed) {
+ /* 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_outline(wmOperatorType *ot)
+{
+ static const EnumPropertyItem view_mode[] = {
+ {GP_PERIMETER_VIEW, "VIEW", 0, "View", ""},
+ {GP_PERIMETER_FRONT, "FRONT", 0, "Front", ""},
+ {GP_PERIMETER_SIDE, "SIDE", 0, "Side", ""},
+ {GP_PERIMETER_TOP, "TOP", 0, "Top", ""},
+ {GP_PERIMETER_CAMERA, "CAMERA", 0, "Camera", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+ static const EnumPropertyItem material_mode[] = {
+ {GP_STROKE_USE_ACTIVE_MATERIAL, "ACTIVE", 0, "Active Material", ""},
+ {GP_STROKE_USE_CURRENT_MATERIAL, "KEEP", 0, "Keep Material", "Keep current stroke material"},
+ {GP_STROKE_USE_NEW_MATERIAL, "NEW", 0, "New Material", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Convert Stroke to Outline";
+ ot->idname = "GPENCIL_OT_stroke_outline";
+ ot->description = "Convert stroke to perimeter";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_outline_exec;
+ ot->poll = gpencil_stroke_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "view_mode", view_mode, GP_PERIMETER_VIEW, "View", "");
+ RNA_def_enum(ot->srna,
+ "material_mode",
+ material_mode,
+ GP_STROKE_USE_ACTIVE_MATERIAL,
+ "Material Mode",
+ "");
+
+ RNA_def_int(ot->srna,
+ "thickness",
+ 1,
+ 1,
+ 1000,
+ "Thickness",
+ "Thickness of the stroke perimeter",
+ 1,
+ 1000);
+ RNA_def_boolean(ot->srna,
+ "keep",
+ true,
+ "Keep Shape",
+ "Try to keep global shape when the stroke thickness change");
+
+ RNA_def_int(ot->srna, "subdivisions", 3, 0, 10, "Subdivisions", "", 0, 10);
+
+ RNA_def_float(ot->srna, "length", 0.0f, 0.0f, 100.0f, "Sample Length", "", 0.0f, 100.0f);
+}
+
+/** \} */
+
void GPENCIL_OT_recalc_geometry(wmOperatorType *ot)
{
/* identifiers */
@@ -3980,6 +4407,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
/* TODO use `BKE_gpencil_stroke_smooth` when the weights are better used. */
bGPDstroke gps_old = *gps;
gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
+ bool need_update = false;
/* Here the iteration needs to be done outside the smooth functions,
* as there are points that don't get smoothed. */
for (int n = 0; n < repeat; n++) {
@@ -3991,6 +4419,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
/* Perform smoothing. */
if (smooth_position) {
BKE_gpencil_stroke_smooth_point(&gps_old, i, factor, 1, false, false, gps);
+ need_update = true;
}
if (smooth_strength) {
BKE_gpencil_stroke_smooth_strength(&gps_old, i, factor, 1, gps);
@@ -4000,6 +4429,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
if (smooth_uv) {
BKE_gpencil_stroke_smooth_uv(&gps_old, i, factor, 1, gps);
+ need_update = true;
}
}
if (n < repeat - 1) {
@@ -4007,6 +4437,11 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
}
MEM_freeN(gps_old.points);
+
+ if (need_update) {
+ gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_stroke_geometry_update(gpd_, gps);
+ }
}
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
@@ -4454,6 +4889,8 @@ void GPENCIL_OT_stroke_sample(wmOperatorType *ot)
/* properties */
prop = RNA_def_float(ot->srna, "length", 0.1f, 0.0f, 100.0f, "Length", "", 0.0f, 100.0f);
+ prop = RNA_def_float(
+ ot->srna, "sharp_threshold", 0.1f, 0.0f, M_PI, "Sharp Threshold", "", 0.0f, M_PI);
/* avoid re-using last var */
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 5305c764b3a..34f983151d2 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -67,6 +67,7 @@
#define LEAK_HORZ 0
#define LEAK_VERT 1
+#define FILL_LEAK 3.0f
#define MIN_WINDOW_SIZE 128
/* Set to 1 to debug filling internal image. By default, the value must be 0. */
@@ -140,6 +141,8 @@ typedef struct tGPDfill {
int fill_simplylvl;
/** boundary limits drawing mode */
int fill_draw_mode;
+ /** types of extensions **/
+ int fill_extend_mode;
/* scaling factor */
float fill_factor;
@@ -197,7 +200,8 @@ static void gpencil_delete_temp_stroke_extension(tGPDfill *tgpf, const bool all_
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* free stroke */
- if ((gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG)) {
+ if ((gps->flag & GP_STROKE_NOFILL) &&
+ (gps->flag & GP_STROKE_TAG || gps->flag & GP_STROKE_HELP)) {
BLI_remlink(&gpf->strokes, gps);
BKE_gpencil_free_stroke(gps);
}
@@ -209,6 +213,64 @@ static void gpencil_delete_temp_stroke_extension(tGPDfill *tgpf, const bool all_
}
}
+static bool extended_bbox_overlap(
+ float min1[3], float max1[3], float min2[3], float max2[3], float extend)
+{
+ for (int axis = 0; axis < 3; axis++) {
+ float intersection_min = max_ff(min1[axis], min2[axis]) - extend;
+ float intersection_max = min_ff(max1[axis], max2[axis]) + extend;
+ if (intersection_min > intersection_max) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static void add_stroke_extension(bGPDframe *gpf, bGPDstroke *gps, float p1[3], float p2[3])
+{
+ bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+ gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_new);
+
+ bGPDspoint *pt = &gps_new->points[0];
+ copy_v3_v3(&pt->x, p1);
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+
+ pt = &gps_new->points[1];
+ copy_v3_v3(&pt->x, p2);
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+}
+
+static void add_endpoint_radius_help(bGPDframe *gpf,
+ bGPDstroke *gps,
+ const float endpoint[3],
+ const float radius,
+ const bool focused)
+{
+ float circumference = 2.0f * M_PI * radius;
+ float vertex_spacing = 0.005f;
+ int num_vertices = min_ii(max_ii((int)ceilf(circumference / vertex_spacing), 3), 40);
+
+ bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, num_vertices, gps->thickness);
+ gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_CYCLIC | GP_STROKE_HELP;
+ if (focused) {
+ gps_new->flag |= GP_STROKE_TAG;
+ }
+ BLI_addtail(&gpf->strokes, gps_new);
+
+ for (int i = 0; i < num_vertices; i++) {
+ float angle = ((float)i / (float)num_vertices) * 2.0f * M_PI;
+ bGPDspoint *pt = &gps_new->points[i];
+ pt->x = endpoint[0] + radius * cosf(angle);
+ pt->y = endpoint[1];
+ pt->z = endpoint[2] + radius * sinf(angle);
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+ }
+}
+
static void extrapolate_points_by_length(bGPDspoint *a,
bGPDspoint *b,
float length,
@@ -221,6 +283,99 @@ static void extrapolate_points_by_length(bGPDspoint *a,
add_v3_v3v3(r_point, &b->x, ab);
}
+/* Cut the extended lines if collide. */
+static void gpencil_cut_extensions(tGPDfill *tgpf, const int gpl_active_index)
+{
+ bGPdata *gpd = tgpf->gpd;
+ Brush *brush = tgpf->brush;
+ BrushGpencilSettings *brush_settings = brush->gpencil_settings;
+
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (gpl->flag & GP_LAYER_HIDE) {
+ continue;
+ }
+
+ /* Decide if the strokes of layers are included or not depending on the layer mode. */
+ const int gpl_index = BLI_findindex(&gpd->layers, gpl);
+ bool skip = skip_layer_check(brush_settings->fill_layer_mode, gpl_active_index, gpl_index);
+ if (skip) {
+ continue;
+ }
+
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, GP_GETFRAME_USE_PREV);
+ if (gpf == NULL) {
+ continue;
+ }
+ int size = BLI_listbase_count(&gpf->strokes);
+ if (size == 0) {
+ continue;
+ }
+
+ float diff_mat[4][4];
+ BKE_gpencil_layer_transform_matrix_get(tgpf->depsgraph, tgpf->ob, gpl, diff_mat);
+ /* Save all extend strokes in array.*/
+ int tot_idx = 0;
+ bGPDstroke **gps_array = MEM_callocN(sizeof(bGPDstroke *) * size, __func__);
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if ((gps->flag & (GP_STROKE_NOFILL | GP_STROKE_TAG)) == 0) {
+ continue;
+ }
+ gps_array[tot_idx] = gps;
+ tot_idx++;
+ }
+
+ /* Compare all strokes. */
+ for (int i = 0; i < tot_idx; i++) {
+ bGPDstroke *gps_a = gps_array[i];
+
+ bGPDspoint pt2;
+ float a1xy[2], a2xy[2];
+ float b1xy[2], b2xy[2];
+
+ /* First stroke. */
+ bGPDspoint *pt = &gps_a->points[0];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy_fl(&tgpf->gsc, gps_a, &pt2, &a1xy[0], &a1xy[1]);
+
+ pt = &gps_a->points[1];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy_fl(&tgpf->gsc, gps_a, &pt2, &a2xy[0], &a2xy[1]);
+ bGPDspoint *extreme_a = &gps_a->points[1];
+
+ /* Loop all strokes. */
+ for (int z = 0; z < tot_idx; z++) {
+ bGPDstroke *gps_b = gps_array[z];
+ if (i == z) {
+ continue;
+ }
+ pt = &gps_b->points[0];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy_fl(&tgpf->gsc, gps_b, &pt2, &b1xy[0], &b1xy[1]);
+
+ pt = &gps_b->points[1];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt2);
+ gpencil_point_to_xy_fl(&tgpf->gsc, gps_b, &pt2, &b2xy[0], &b2xy[1]);
+ bGPDspoint *extreme_b = &gps_b->points[1];
+
+ /* Check if extensions collide and cut the overlength. */
+ if (isect_seg_seg_v2_simple(a1xy, a2xy, b1xy, b2xy)) {
+ float intersection[2];
+ isect_line_line_v2_point(a1xy, a2xy, b1xy, b2xy, intersection);
+ float intersection3D[3];
+ gpencil_point_xy_to_3d(&tgpf->gsc, tgpf->scene, intersection, intersection3D);
+ copy_v3_v3(&extreme_a->x, intersection3D);
+ copy_v3_v3(&extreme_b->x, intersection3D);
+ gps_a->flag |= GP_STROKE_COLLIDE;
+ gps_b->flag |= GP_STROKE_COLLIDE;
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(gps_array);
+ }
+}
+
/* Loop all layers create stroke extensions. */
static void gpencil_create_extensions(tGPDfill *tgpf)
{
@@ -235,6 +390,9 @@ static void gpencil_create_extensions(tGPDfill *tgpf)
const int gpl_active_index = BLI_findindex(&gpd->layers, gpl_active);
BLI_assert(gpl_active_index >= 0);
+ float connection_dist = tgpf->fill_extend_fac * 0.1f;
+ GSet *connected_endpoints = BLI_gset_ptr_new(__func__);
+
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
if (gpl->flag & GP_LAYER_HIDE) {
continue;
@@ -266,41 +424,163 @@ static void gpencil_create_extensions(tGPDfill *tgpf)
continue;
}
- /* Extend start. */
- bGPDspoint *pt0 = &gps->points[1];
- bGPDspoint *pt1 = &gps->points[0];
- bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
- gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
- BLI_addtail(&gpf->strokes, gps_new);
-
- bGPDspoint *pt = &gps_new->points[0];
- copy_v3_v3(&pt->x, &pt1->x);
- pt->strength = 1.0f;
- pt->pressure = 1.0f;
-
- pt = &gps_new->points[1];
- pt->strength = 1.0f;
- pt->pressure = 1.0f;
- extrapolate_points_by_length(pt0, pt1, tgpf->fill_extend_fac * 0.1f, &pt->x);
-
- /* Extend end. */
- pt0 = &gps->points[gps->totpoints - 2];
- pt1 = &gps->points[gps->totpoints - 1];
- gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
- gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
- BLI_addtail(&gpf->strokes, gps_new);
-
- pt = &gps_new->points[0];
- copy_v3_v3(&pt->x, &pt1->x);
- pt->strength = 1.0f;
- pt->pressure = 1.0f;
-
- pt = &gps_new->points[1];
- pt->strength = 1.0f;
- pt->pressure = 1.0f;
- extrapolate_points_by_length(pt0, pt1, tgpf->fill_extend_fac * 0.1f, &pt->x);
+ /* Find points of high curvature. */
+ float tan1[3];
+ float tan2[3];
+ float d1;
+ float d2;
+ float total_length = 0.f;
+ for (int i = 1; i < gps->totpoints; i++) {
+ if (i > 1) {
+ copy_v3_v3(tan1, tan2);
+ d1 = d2;
+ }
+ bGPDspoint *pt1 = &gps->points[i - 1];
+ bGPDspoint *pt2 = &gps->points[i];
+ sub_v3_v3v3(tan2, &pt2->x, &pt1->x);
+ d2 = normalize_v3(tan2);
+ total_length += d2;
+ if (i > 1) {
+ if (tgpf->fill_extend_mode == GP_FILL_EMODE_RADIUS) {
+ continue;
+ }
+ float curvature[3];
+ sub_v3_v3v3(curvature, tan2, tan1);
+ float k = normalize_v3(curvature);
+ k /= min_ff(d1, d2);
+ float radius = 1.f / k;
+ /*
+ * The smaller the radius of curvature, the sharper the corner.
+ * The thicker the line, the larger the radius of curvature it
+ * takes to be visually indistinguishable from an endpoint.
+ */
+ float min_radius = gps->thickness * 0.0001f;
+
+ if (radius < min_radius) {
+ /* Extend along direction of curvature. */
+ bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+ gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_new);
+
+ bGPDspoint *pt = &gps_new->points[0];
+ copy_v3_v3(&pt->x, &pt1->x);
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+
+ pt = &gps_new->points[1];
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+ mul_v3_fl(curvature, -connection_dist);
+ add_v3_v3v3(&pt->x, &pt1->x, curvature);
+ }
+ }
+ }
+
+ if (tgpf->fill_extend_mode != GP_FILL_EMODE_RADIUS) {
+ /* Extend start. */
+ bGPDspoint *pt0 = &gps->points[1];
+ bGPDspoint *pt1 = &gps->points[0];
+ bGPDstroke *gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+ gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_new);
+
+ bGPDspoint *pt = &gps_new->points[0];
+ copy_v3_v3(&pt->x, &pt1->x);
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+
+ pt = &gps_new->points[1];
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+ extrapolate_points_by_length(pt0, pt1, connection_dist, &pt->x);
+
+ /* Extend end. */
+ pt0 = &gps->points[gps->totpoints - 2];
+ pt1 = &gps->points[gps->totpoints - 1];
+ gps_new = BKE_gpencil_stroke_new(gps->mat_nr, 2, gps->thickness);
+ gps_new->flag |= GP_STROKE_NOFILL | GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_new);
+
+ pt = &gps_new->points[0];
+ copy_v3_v3(&pt->x, &pt1->x);
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+
+ pt = &gps_new->points[1];
+ pt->strength = 1.0f;
+ pt->pressure = 1.0f;
+ extrapolate_points_by_length(pt0, pt1, connection_dist, &pt->x);
+ }
+
+ /* Connect endpoints within a radius */
+ if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) {
+ continue;
+ }
+ float *stroke1_start = &gps->points[0].x;
+ float *stroke1_end = &gps->points[gps->totpoints - 1].x;
+ /* Connect the start of the stroke to its own end if the whole stroke
+ * isn't already so short that it's within that distance
+ */
+ if (len_v3v3(stroke1_start, stroke1_end) < connection_dist &&
+ total_length > connection_dist) {
+ add_stroke_extension(gpf, gps, stroke1_start, stroke1_end);
+ BLI_gset_add(connected_endpoints, stroke1_start);
+ BLI_gset_add(connected_endpoints, stroke1_end);
+ }
+ for (bGPDstroke *gps2 = (bGPDstroke *)(((Link *)gps)->next); gps2 != NULL;
+ gps2 = (bGPDstroke *)(((Link *)gps2)->next)) {
+ /* Don't check distance to temporary extensions. */
+ if ((gps2->flag & GP_STROKE_NOFILL) && (gps2->flag & GP_STROKE_TAG)) {
+ continue;
+ }
+
+ /* Don't check endpoint distances unless the bounding boxes of the strokes
+ are close enough together that they can plausibly be connected. */
+ if (!extended_bbox_overlap(gps->boundbox_min,
+ gps->boundbox_max,
+ gps2->boundbox_min,
+ gps2->boundbox_max,
+ connection_dist)) {
+ continue;
+ }
+
+ float *stroke2_start = &gps2->points[0].x;
+ float *stroke2_end = &gps2->points[gps2->totpoints - 1].x;
+ if (len_v3v3(stroke1_start, stroke2_start) < connection_dist) {
+ add_stroke_extension(gpf, gps, stroke1_start, stroke2_start);
+ BLI_gset_add(connected_endpoints, stroke1_start);
+ BLI_gset_add(connected_endpoints, stroke2_start);
+ }
+ if (len_v3v3(stroke1_start, stroke2_end) < connection_dist) {
+ add_stroke_extension(gpf, gps, stroke1_start, stroke2_end);
+ BLI_gset_add(connected_endpoints, stroke1_start);
+ BLI_gset_add(connected_endpoints, stroke2_end);
+ }
+ if (len_v3v3(stroke1_end, stroke2_start) < connection_dist) {
+ add_stroke_extension(gpf, gps, stroke1_end, stroke2_start);
+ BLI_gset_add(connected_endpoints, stroke1_end);
+ BLI_gset_add(connected_endpoints, stroke2_start);
+ }
+ if (len_v3v3(stroke1_end, stroke2_end) < connection_dist) {
+ add_stroke_extension(gpf, gps, stroke1_end, stroke2_end);
+ BLI_gset_add(connected_endpoints, stroke1_end);
+ BLI_gset_add(connected_endpoints, stroke2_end);
+ }
+ }
+
+ bool start_connected = BLI_gset_haskey(connected_endpoints, stroke1_start);
+ bool end_connected = BLI_gset_haskey(connected_endpoints, stroke1_end);
+ add_endpoint_radius_help(gpf, gps, stroke1_start, connection_dist, start_connected);
+ add_endpoint_radius_help(gpf, gps, stroke1_end, connection_dist, end_connected);
}
}
+
+ BLI_gset_free(connected_endpoints, NULL);
+
+ /* Cut overlength strokes. */
+ if (tgpf->fill_extend_mode == GP_FILL_EMODE_EXTEND) {
+ gpencil_cut_extensions(tgpf, gpl_active_index);
+ }
}
static void gpencil_update_extend(tGPDfill *tgpf)
@@ -322,15 +602,16 @@ static bool gpencil_stroke_is_drawable(tGPDfill *tgpf, bGPDstroke *gps)
const bool show_help = (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) != 0;
const bool show_extend = (tgpf->flag & GP_BRUSH_FILL_SHOW_EXTENDLINES) != 0;
const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG);
+ const bool is_extend_help = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_HELP);
if ((!show_help) && (show_extend)) {
- if (!is_extend) {
+ if (!is_extend && !is_extend_help) {
return false;
}
}
if ((show_help) && (!show_extend)) {
- if (is_extend) {
+ if (is_extend || is_extend_help) {
return false;
}
}
@@ -357,14 +638,35 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
float fpt[3];
float col[4];
const float extend_col[4] = {0.0f, 1.0f, 1.0f, 1.0f};
- const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG);
+ const float help_col[4] = {1.0f, 0.0f, 0.5f, 1.0f};
+ const bool is_extend = (gps->flag & GP_STROKE_NOFILL) && (gps->flag & GP_STROKE_TAG) &&
+ !(gps->flag & GP_STROKE_HELP);
+ const bool is_help = gps->flag & GP_STROKE_HELP;
if (!gpencil_stroke_is_drawable(tgpf, gps)) {
return;
}
- if ((is_extend) && (!tgpf->is_render)) {
- copy_v4_v4(col, extend_col);
+ if (is_help && tgpf->is_render) {
+ /* Help strokes are for display only and shouldn't render. */
+ return;
+ }
+ else if (is_help) {
+ /* Color help strokes that won't affect fill or render separately from
+ * extended strokes, as they will affect them. */
+ copy_v4_v4(col, help_col);
+
+ /* If there is contact, hide the circles to avoid noise and keep the focus
+ * in the pending gaps. */
+ col[3] = (gps->flag & GP_STROKE_TAG) ? 0.0f : 0.5f;
+ }
+ else if ((is_extend) && (!tgpf->is_render)) {
+ if ((gps->flag & GP_STROKE_COLLIDE) || (tgpf->fill_extend_mode == GP_FILL_EMODE_RADIUS)) {
+ copy_v4_v4(col, extend_col);
+ }
+ else {
+ copy_v4_v4(col, help_col);
+ }
}
else {
copy_v4_v4(col, ink);
@@ -379,7 +681,7 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
/* draw stroke curve */
- GPU_line_width((!is_extend) ? thickness : thickness * 2.0f);
+ GPU_line_width((!is_extend && !is_help) ? thickness : thickness * 2.0f);
immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add);
const bGPDspoint *pt = points;
@@ -390,7 +692,7 @@ static void gpencil_draw_basic_stroke(tGPDfill *tgpf,
CLAMP(alpha, 0.0f, 1.0f);
col[3] = alpha <= thershold ? 0.0f : 1.0f;
}
- else {
+ else if (!is_help) {
col[3] = 1.0f;
}
/* set point */
@@ -582,7 +884,8 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4])
/* Normal strokes. */
if (ELEM(tgpf->fill_draw_mode, GP_FILL_DMODE_STROKE, GP_FILL_DMODE_BOTH)) {
- if (gpencil_stroke_is_drawable(tgpf, gps) && ((gps->flag & GP_STROKE_TAG) == 0)) {
+ if (gpencil_stroke_is_drawable(tgpf, gps) && ((gps->flag & GP_STROKE_TAG) == 0) &&
+ ((gps->flag & GP_STROKE_HELP) == 0)) {
ED_gpencil_draw_fill(&tgpw);
}
}
@@ -1777,10 +2080,11 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
tgpf->fill_threshold = brush->gpencil_settings->fill_threshold;
tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl;
tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode;
+ tgpf->fill_extend_mode = brush->gpencil_settings->fill_extend_mode;
tgpf->fill_extend_fac = brush->gpencil_settings->fill_extend_fac;
tgpf->fill_factor = max_ff(GPENCIL_MIN_FILL_FAC,
min_ff(brush->gpencil_settings->fill_factor, GPENCIL_MAX_FILL_FAC));
- tgpf->fill_leak = (int)ceil((float)brush->gpencil_settings->fill_leak * tgpf->fill_factor);
+ tgpf->fill_leak = (int)ceil(FILL_LEAK * tgpf->fill_factor);
int totcol = tgpf->ob->totcol;
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index d656241c463..4d62f834d86 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -593,6 +593,7 @@ void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot);
*/
void GPENCIL_OT_stroke_caps_set(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_join(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_start_set(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_subdivide(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_simplify(struct wmOperatorType *ot);
@@ -608,6 +609,7 @@ 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_stroke_outline(struct wmOperatorType *ot);
void GPENCIL_OT_material_to_vertex_color(struct wmOperatorType *ot);
void GPENCIL_OT_extract_palette_vertex(struct wmOperatorType *ot);
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index e7a4f2fe2dc..dc63acf5136 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -1483,7 +1483,8 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
*/
static const EnumPropertyItem gpencil_interpolation_type_items[] = {
/* Interpolation. */
- RNA_ENUM_ITEM_HEADING(N_("Interpolation"), "Standard transitions between keyframes"),
+ RNA_ENUM_ITEM_HEADING(CTX_N_(BLT_I18NCONTEXT_ID_GPENCIL, "Interpolation"),
+ N_("Standard transitions between keyframes")),
{GP_IPO_LINEAR,
"LINEAR",
ICON_IPO_LINEAR,
@@ -1496,9 +1497,9 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
"Custom interpolation defined using a curve map"},
/* Easing. */
- RNA_ENUM_ITEM_HEADING(N_("Easing (by strength)"),
- "Predefined inertial transitions, useful for motion graphics "
- "(from least to most \"dramatic\")"),
+ RNA_ENUM_ITEM_HEADING(CTX_N_(BLT_I18NCONTEXT_ID_GPENCIL, "Easing (by strength)"),
+ N_("Predefined inertial transitions, useful for motion graphics "
+ "(from least to most \"dramatic\")")),
{GP_IPO_SINE,
"SINE",
ICON_IPO_SINE,
@@ -1515,7 +1516,8 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
"Circular",
"Circular easing (strongest and most dynamic)"},
- RNA_ENUM_ITEM_HEADING(N_("Dynamic Effects"), "Simple physics-inspired easing effects"),
+ RNA_ENUM_ITEM_HEADING(CTX_N_(BLT_I18NCONTEXT_ID_GPENCIL, "Dynamic Effects"),
+ N_("Simple physics-inspired easing effects")),
{GP_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"},
{GP_IPO_BOUNCE,
"BOUNCE",
@@ -1569,6 +1571,7 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
/* identifiers */
ot->name = "Interpolate Sequence";
ot->idname = "GPENCIL_OT_interpolate_sequence";
+ ot->translation_context = BLT_I18NCONTEXT_ID_GPENCIL;
ot->description = "Generate 'in-betweens' to smoothly interpolate between Grease Pencil frames";
/* api callbacks */
diff --git a/source/blender/editors/gpencil/gpencil_mesh.cc b/source/blender/editors/gpencil/gpencil_mesh.cc
index b27e1c75746..739a1b319c3 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.cc
+++ b/source/blender/editors/gpencil/gpencil_mesh.cc
@@ -213,7 +213,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
bool newob = false;
if (target == GP_TARGET_OB_SELECTED) {
- ob_gpencil = BKE_view_layer_non_active_selected_object(CTX_data_view_layer(C), v3d);
+ ob_gpencil = BKE_view_layer_non_active_selected_object(scene, CTX_data_view_layer(C), v3d);
if (ob_gpencil != nullptr) {
if (ob_gpencil->type != OB_GPENCIL) {
BKE_report(op->reports, RPT_WARNING, "Target object not a grease pencil, ignoring!");
@@ -315,7 +315,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if ((gps->flag & GP_STROKE_TAG) == 0) {
ED_gpencil_stroke_reproject(
- depsgraph, &gsc, sctx, gpl, gpf, gps, project_type, false);
+ depsgraph, &gsc, sctx, gpl, gpf, gps, project_type, false, 0.0f);
gps->flag |= GP_STROKE_TAG;
}
}
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 99e28270c3e..85cc281ca90 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -621,6 +621,7 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_stroke_caps_set);
WM_operatortype_append(GPENCIL_OT_stroke_join);
WM_operatortype_append(GPENCIL_OT_stroke_flip);
+ WM_operatortype_append(GPENCIL_OT_stroke_start_set);
WM_operatortype_append(GPENCIL_OT_stroke_subdivide);
WM_operatortype_append(GPENCIL_OT_stroke_simplify);
WM_operatortype_append(GPENCIL_OT_stroke_simplify_fixed);
@@ -635,6 +636,7 @@ void ED_operatortypes_gpencil(void)
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_stroke_outline);
WM_operatortype_append(GPENCIL_OT_material_to_vertex_color);
WM_operatortype_append(GPENCIL_OT_extract_palette_vertex);
diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c
index 8119646137c..50fbafff732 100644
--- a/source/blender/editors/gpencil/gpencil_ops_versioning.c
+++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c
@@ -92,7 +92,7 @@ static int gpencil_convert_old_files_exec(bContext *C, wmOperator *op)
if ((!is_annotation) && (view_layer != NULL)) {
Object *ob;
ob = BKE_object_add_for_data(
- bmain, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false);
+ bmain, scene, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false);
zero_v3(ob->loc);
DEG_relations_tag_update(bmain); /* added object */
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 70486138556..7446c727a0c 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -918,6 +918,67 @@ static void gpencil_stroke_unselect(bGPdata *gpd, bGPDstroke *gps)
}
}
+static bGPDstroke *gpencil_stroke_to_outline(tGPsdata *p, bGPDstroke *gps)
+{
+ bGPDlayer *gpl = p->gpl;
+ RegionView3D *rv3d = p->region->regiondata;
+ Brush *brush = p->brush;
+ BrushGpencilSettings *gpencil_settings = brush->gpencil_settings;
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(p->ob, gps->mat_nr + 1);
+ const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0);
+
+ if (!is_stroke) {
+ return gps;
+ }
+
+ /* 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;
+ /* Apply object scale to thickness. */
+ gps_duplicate->thickness *= mat4_to_scale(p->ob->obmat);
+ CLAMP_MIN(gps_duplicate->thickness, 1.0f);
+
+ /* Stroke. */
+ float diff_mat[4][4];
+ unit_m4(diff_mat);
+ const float outline_thickness = (float)brush->size * gpencil_settings->outline_fac * 0.5f;
+ bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
+ rv3d->viewmat, p->gpd, gpl, gps_duplicate, 3, diff_mat, outline_thickness);
+ /* Assign material. */
+ if (gpencil_settings->material_alt == NULL) {
+ gps_perimeter->mat_nr = gps->mat_nr;
+ }
+ else {
+ Material *ma = gpencil_settings->material_alt;
+ int mat_idx = BKE_gpencil_material_find_index_by_name_prefix(p->ob, ma->id.name + 2);
+ if (mat_idx > -1) {
+ gps_perimeter->mat_nr = mat_idx;
+ }
+ else {
+ gps_perimeter->mat_nr = gps->mat_nr;
+ }
+ }
+
+ /* Set pressure constant. */
+ gps_perimeter->thickness = max_ii((int)outline_thickness, 1);
+
+ bGPDspoint *pt;
+ for (int i = 0; i < gps_perimeter->totpoints; i++) {
+ pt = &gps_perimeter->points[i];
+ pt->pressure = 1.0f;
+ }
+
+ /* Remove original stroke. */
+ BKE_gpencil_free_stroke(gps);
+
+ /* Free Temp stroke. */
+ BKE_gpencil_free_stroke(gps_duplicate);
+
+ return gps_perimeter;
+}
+
/* make a new stroke from the buffer data */
static void gpencil_stroke_newfrombuffer(tGPsdata *p)
{
@@ -1221,6 +1282,23 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
BKE_gpencil_stroke_simplify_adaptive(gpd, gps, brush->gpencil_settings->simplify_f);
}
+ /* Set material index. */
+ gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush);
+ if (gps->mat_nr < 0) {
+ if (p->ob->actcol - 1 < 0) {
+ gps->mat_nr = 0;
+ }
+ else {
+ gps->mat_nr = p->ob->actcol - 1;
+ }
+ }
+
+ /* Convert to Outline. */
+ if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
+ (brush->gpencil_settings->flag & GP_BRUSH_OUTLINE_STROKE)) {
+ gps = gpencil_stroke_to_outline(p, gps);
+ }
+
/* reproject to plane (only in 3d space) */
gpencil_reproject_toplane(p, gps);
/* change position relative to parent object */
@@ -1235,17 +1313,6 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
}
}
- /* Save material index */
- gps->mat_nr = BKE_gpencil_object_material_get_index_from_brush(p->ob, p->brush);
- if (gps->mat_nr < 0) {
- if (p->ob->actcol - 1 < 0) {
- gps->mat_nr = 0;
- }
- else {
- gps->mat_nr = p->ob->actcol - 1;
- }
- }
-
/* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke
* is added on listbase head because the drawing order is inverse and the head stroke is the
* first to draw. This is very useful for artist when drawing the background.
@@ -2343,7 +2410,7 @@ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
if (p->paintmode == GP_PAINTMODE_ERASER) {
GPUVertFormat *format = immVertexFormat();
const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
@@ -2353,7 +2420,7 @@ static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr)
immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -3264,7 +3331,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
return OPERATOR_RUNNING_MODAL;
}
-/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
+/* gpencil modal operator stores area, which can be removed while using it (like full-screen). */
static bool gpencil_area_exists(bContext *C, ScrArea *area_test)
{
bScreen *screen = CTX_wm_screen(C);
@@ -3658,9 +3725,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- /* Exit painting mode (and/or end current stroke).
- *
- */
+ /* Exit painting mode (and/or end current stroke). */
if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, EVT_ESCKEY, EVT_SPACEKEY)) {
p->status = GP_STATUS_DONE;
@@ -3755,7 +3820,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* handle mode-specific events */
if (p->status == GP_STATUS_PAINTING) {
/* handle painting mouse-movements? */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
+ if (ISMOUSE_MOTION(event->type) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) {
/* handle drawing event */
bool is_speed_guide = ((guide->use_guide) &&
(p->brush && (p->brush->gpencil_tool == GPAINT_TOOL_DRAW)));
@@ -3823,7 +3888,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
}
- /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */
+ /* gpencil modal operator stores area, which can be removed while using it (like full-screen). */
if (0 == gpencil_area_exists(C, p->area)) {
estate = OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index befff611d58..4a4fffc9638 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1024,8 +1024,10 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
gpd->runtime.sbuffer, &gpd->runtime.sbuffer_size, &gpd->runtime.sbuffer_used, false);
/* add small offset to keep stroke over the surface */
- if ((depth_arr) && (gpd->zdepth_offset > 0.0f) && (depth_arr[i] != DEPTH_INVALID)) {
- depth_arr[i] *= (1.0f - (gpd->zdepth_offset / 1000.0f));
+ if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) {
+ if ((depth_arr) && (gpd->zdepth_offset > 0.0f) && (depth_arr[i] != DEPTH_INVALID)) {
+ depth_arr[i] *= (1.0f - (gpd->zdepth_offset / 1000.0f));
+ }
}
/* convert screen-coordinates to 3D coordinates */
@@ -1107,7 +1109,7 @@ static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive
/* Initialize mouse points. */
static void gpencil_primitive_interaction_begin(tGPDprimitive *tgpi, const wmEvent *event)
{
- copy_v2fl_v2i(tgpi->mval, event->mval);
+ WM_event_drag_start_mval_fl(event, tgpi->region, tgpi->mval);
copy_v2_v2(tgpi->origin, tgpi->mval);
copy_v2_v2(tgpi->start, tgpi->mval);
copy_v2_v2(tgpi->end, tgpi->mval);
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index e27cd255217..52e6200978c 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -516,7 +516,7 @@ static void gpencil_brush_grab_calc_dvec(tGP_BrushEditData *gso)
float mval_f[2];
- /* convert from 2D screenspace to 3D... */
+ /* Convert from 2D screen-space to 3D. */
mval_f[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
mval_f[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
@@ -700,8 +700,8 @@ static bool gpencil_brush_pinch_apply(tGP_BrushEditData *gso,
/* ----------------------------------------------- */
/* Twist Brush - Rotate Around midpoint */
-/* Take the screenspace coordinates of the point, rotate this around the brush midpoint,
- * convert the rotated point and convert it into "data" space
+/* Take the screen-space coordinates of the point, rotate this around the brush midpoint,
+ * convert the rotated point and convert it into "data" space.
*/
static bool gpencil_brush_twist_apply(tGP_BrushEditData *gso,
@@ -807,7 +807,7 @@ static bool gpencil_brush_randomize_apply(tGP_BrushEditData *gso,
/* apply random to position */
if (gso->brush->gpencil_settings->sculpt_mode_flag & GP_SCULPT_FLAGMODE_APPLY_POSITION) {
/* Jitter is applied perpendicular to the mouse movement vector
- * - We compute all effects in screenspace (since it's easier)
+ * - We compute all effects in screen-space (since it's easier)
* and then project these to get the points/distances in
* view-space as needed.
*/
@@ -989,8 +989,8 @@ static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso)
float delta[3];
size_t strokes_added = 0;
- /* Compute amount to offset the points by */
- /* NOTE: This assumes that screenspace strokes are NOT used in the 3D view... */
+ /* Compute amount to offset the points by. */
+ /* NOTE: This assumes that screen-space strokes are NOT used in the 3D view. */
gpencil_brush_calc_midpoint(gso); /* this puts the cursor location into gso->dvec */
sub_v3_v3v3(delta, gso->dvec, data->buffer_midpoint);
@@ -1063,7 +1063,7 @@ static void gpencil_brush_clone_adjust(tGP_BrushEditData *gso)
/* For each of the stored strokes, apply the offset to each point */
/* NOTE: Again this assumes that in the 3D view,
- * we only have 3d space and not screenspace strokes... */
+ * we only have 3d space and not screen-space strokes. */
for (snum = 0; snum < data->totitems; snum++) {
bGPDstroke *gps = data->new_strokes[snum];
bGPDspoint *pt;
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index a19265720e8..95f43733a36 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -2071,7 +2071,7 @@ static bool gpencil_generic_stroke_select(bContext *C,
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
- /* convert point coords to screenspace */
+ /* Convert point coords to screen-space. */
const bool is_inside = is_inside_fn(gsc.region, gpstroke_iter.diff_mat, &pt->x, user_data);
if (strokemode == false) {
const bool is_select = (pt_active->flag & GP_SPOINT_SELECT) != 0;
diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c
index f6e88e05d46..36165c6b7c0 100644
--- a/source/blender/editors/gpencil/gpencil_trace_ops.c
+++ b/source/blender/editors/gpencil/gpencil_trace_ops.c
@@ -71,6 +71,9 @@ typedef struct TraceJob {
int32_t thickness;
int32_t turnpolicy;
int32_t mode;
+ /** Frame to render to be used by python API. Not exposed in UI.
+ * This feature is only used in Studios to run custom video trace for selected frames. */
+ int32_t frame_num;
bool success;
bool was_canceled;
@@ -212,7 +215,10 @@ static void trace_start_job(void *customdata, short *stop, short *do_update, flo
(trace_job->mode == GPENCIL_TRACE_MODE_SINGLE)) {
void *lock;
ImageUser *iuser = trace_job->ob_active->iuser;
- iuser->framenr = init_frame;
+
+ iuser->framenr = ((trace_job->frame_num == 0) || (trace_job->frame_num > iuser->frames)) ?
+ init_frame :
+ trace_job->frame_num;
ImBuf *ibuf = BKE_image_acquire_ibuf(trace_job->image, iuser, &lock);
if (ibuf) {
/* Create frame. */
@@ -300,9 +306,10 @@ static int gpencil_trace_image_exec(bContext *C, wmOperator *op)
/* Create a new grease pencil object or reuse selected. */
eGP_TargetObjectMode target = RNA_enum_get(op->ptr, "target");
- job->ob_gpencil = (target == GP_TARGET_OB_SELECTED) ? BKE_view_layer_non_active_selected_object(
- CTX_data_view_layer(C), job->v3d) :
- NULL;
+ job->ob_gpencil = (target == GP_TARGET_OB_SELECTED) ?
+ BKE_view_layer_non_active_selected_object(
+ scene, CTX_data_view_layer(C), job->v3d) :
+ NULL;
if (job->ob_gpencil != NULL) {
if (job->ob_gpencil->type != OB_GPENCIL) {
@@ -324,13 +331,14 @@ static int gpencil_trace_image_exec(bContext *C, wmOperator *op)
job->thickness = RNA_int_get(op->ptr, "thickness");
job->turnpolicy = RNA_enum_get(op->ptr, "turnpolicy");
job->mode = RNA_enum_get(op->ptr, "mode");
+ job->frame_num = RNA_int_get(op->ptr, "frame_number");
trace_initialize_job_data(job);
/* Back to active base. */
ED_object_base_activate(job->C, job->base_active);
- if (job->image->source == IMA_SRC_FILE) {
+ if ((job->image->source == IMA_SRC_FILE) || (job->frame_num > 0)) {
short stop = 0, do_update = true;
float progress;
trace_start_job(job, &stop, &do_update, &progress);
@@ -364,6 +372,8 @@ static int gpencil_trace_image_invoke(bContext *C, wmOperator *op, const wmEvent
void GPENCIL_OT_trace_image(wmOperatorType *ot)
{
+ PropertyRNA *prop;
+
static const EnumPropertyItem turnpolicy_type[] = {
{POTRACE_TURNPOLICY_BLACK,
"BLACK",
@@ -475,4 +485,15 @@ void GPENCIL_OT_trace_image(wmOperatorType *ot)
true,
"Start At Current Frame",
"Trace Image starting in current image frame");
+ prop = RNA_def_int(
+ ot->srna,
+ "frame_number",
+ 0,
+ 0,
+ 9999,
+ "Trace Frame",
+ "Used to trace only one frame of the image sequence, set to zero to trace all",
+ 0,
+ 9999);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 7b659511aaa..729e8412684 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -1038,7 +1038,8 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
bGPDframe *gpf,
bGPDstroke *gps,
const eGP_ReprojectModes mode,
- const bool keep_original)
+ const bool keep_original,
+ const float offset)
{
ToolSettings *ts = gsc->scene->toolsettings;
ARegion *region = gsc->region;
@@ -1156,7 +1157,13 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
&depth,
&location[0],
&normal[0])) {
- copy_v3_v3(&pt->x, location);
+ /* Apply offset over surface. */
+ float normal_vector[3];
+ sub_v3_v3v3(normal_vector, ray_start, location);
+ normalize_v3(normal_vector);
+ mul_v3_fl(normal_vector, offset);
+
+ add_v3_v3v3(&pt->x, location, normal_vector);
}
else {
/* Default to planar */
@@ -1683,7 +1690,7 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y)
GPUVertFormat *format = immVertexFormat();
const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
@@ -1693,7 +1700,7 @@ void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y)
immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -1865,7 +1872,7 @@ static void gpencil_brush_cursor_draw(bContext *C, int x, int y, void *customdat
/* draw icon */
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
@@ -3189,7 +3196,7 @@ bGPDstroke *ED_gpencil_stroke_join_and_trim(
/* Join both strokes. */
int totpoint = gps_final->totpoints;
- BKE_gpencil_stroke_join(gps_final, gps, false, true, true);
+ BKE_gpencil_stroke_join(gps_final, gps, false, true, true, true);
/* Select the join points and merge if the distance is very small. */
pt = &gps_final->points[totpoint - 1];
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
index 865c4e360b5..41f939813e4 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_ops.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -131,7 +131,7 @@ static int gpencil_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
- * Extracted of OpenCV demhist.c
+ * Extracted of OpenCV `demhist.c`.
*/
if (contrast > 0) {
gain = 1.0f - delta * 2.0f;
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index ac3b4133007..6079aca0199 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -101,16 +101,16 @@ typedef struct bAnimContext {
/* Main Data container types */
typedef enum eAnimCont_Types {
ANIMCONT_NONE = 0, /* invalid or no data */
- ANIMCONT_ACTION = 1, /* action (bAction) */
- ANIMCONT_SHAPEKEY = 2, /* shapekey (Key) */
+ ANIMCONT_ACTION = 1, /* action (#bAction) */
+ ANIMCONT_SHAPEKEY = 2, /* shape-key (#Key) */
ANIMCONT_GPENCIL = 3, /* grease pencil (screen) */
- ANIMCONT_DOPESHEET = 4, /* dopesheet (bDopesheet) */
- ANIMCONT_FCURVES = 5, /* animation F-Curves (bDopesheet) */
- ANIMCONT_DRIVERS = 6, /* drivers (bDopesheet) */
- ANIMCONT_NLA = 7, /* nla (bDopesheet) */
- ANIMCONT_CHANNEL = 8, /* animation channel (bAnimListElem) */
- ANIMCONT_MASK = 9, /* mask dopesheet */
- ANIMCONT_TIMELINE = 10, /* "timeline" editor (bDopeSheet) */
+ ANIMCONT_DOPESHEET = 4, /* dope-sheet (#bDopesheet) */
+ ANIMCONT_FCURVES = 5, /* animation F-Curves (#bDopesheet) */
+ ANIMCONT_DRIVERS = 6, /* drivers (#bDopesheet) */
+ ANIMCONT_NLA = 7, /* NLA (#bDopesheet) */
+ ANIMCONT_CHANNEL = 8, /* animation channel (#bAnimListElem) */
+ ANIMCONT_MASK = 9, /* mask dope-sheet */
+ ANIMCONT_TIMELINE = 10, /* "timeline" editor (#bDopeSheet) */
} eAnimCont_Types;
/** \} */
@@ -334,6 +334,7 @@ typedef enum eAnimFilter_Flags {
ANIMFILTER_TMP_IGNORE_ONLYSEL = (1u << 31),
} eAnimFilter_Flags;
+ENUM_OPERATORS(eAnimFilter_Flags, ANIMFILTER_TMP_IGNORE_ONLYSEL);
/** \} */
@@ -1046,6 +1047,8 @@ void ED_keymap_anim(struct wmKeyConfig *keyconf);
void ED_operatormacros_graph(void);
/* space_action */
void ED_operatormacros_action(void);
+/* space_nla*/
+void ED_operatormacros_nla(void);
/** \} */
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index d969277fef5..8e7f728a3e7 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -312,7 +312,8 @@ void ED_pose_recalculate_paths(struct bContext *C,
/**
* \return True when pick finds an element or the selection changed.
*/
-bool ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer,
+bool ED_armature_pose_select_pick_bone(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
struct Object *ob,
struct Bone *bone,
@@ -323,7 +324,8 @@ bool ED_armature_pose_select_pick_bone(struct ViewLayer *view_layer,
*
* \return True when pick finds an element or the selection changed.
*/
-bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer,
+bool ED_armature_pose_select_pick_with_buffer(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
struct Base *base,
const struct GPUSelectResult *buffer,
@@ -338,7 +340,8 @@ bool ED_armature_pose_select_pick_with_buffer(struct ViewLayer *view_layer,
* It can't be set to the active object because we need
* to keep this set to the weight paint object.
*/
-void ED_armature_pose_select_in_wpaint_mode(struct ViewLayer *view_layer,
+void ED_armature_pose_select_in_wpaint_mode(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct Base *base_select);
bool ED_pose_deselect_all_multi_ex(struct Base **bases,
uint bases_len,
diff --git a/source/blender/editors/include/ED_curves.h b/source/blender/editors/include/ED_curves.h
index 0817241a5c2..00831ff7cc3 100644
--- a/source/blender/editors/include/ED_curves.h
+++ b/source/blender/editors/include/ED_curves.h
@@ -26,10 +26,14 @@ void ED_operatortypes_curves(void);
namespace blender::ed::curves {
bke::CurvesGeometry primitive_random_sphere(int curves_size, int points_per_curve);
-bool selection_operator_poll(bContext *C);
bool has_anything_selected(const Curves &curves_id);
VectorSet<Curves *> get_unique_editable_curves(const bContext &C);
void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob);
+bool editable_curves_with_surface_poll(bContext *C);
+bool curves_with_surface_poll(bContext *C);
+bool editable_curves_poll(bContext *C);
+bool curves_poll(bContext *C);
+
} // namespace blender::ed::curves
#endif
diff --git a/source/blender/editors/include/ED_curves_sculpt.h b/source/blender/editors/include/ED_curves_sculpt.h
index 8aab1533e25..b1c0b649d2b 100644
--- a/source/blender/editors/include/ED_curves_sculpt.h
+++ b/source/blender/editors/include/ED_curves_sculpt.h
@@ -10,8 +10,33 @@
extern "C" {
#endif
+struct Curves;
+
void ED_operatortypes_sculpt_curves(void);
#ifdef __cplusplus
}
#endif
+
+#ifdef __cplusplus
+
+# include "BLI_index_mask.hh"
+# include "BLI_vector.hh"
+
+namespace blender::ed::sculpt_paint {
+
+/**
+ * Find curves that have any point selected (a selection factor greater than zero),
+ * or curves that have their own selection factor greater than zero.
+ */
+IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
+
+/**
+ * Find points that are selected (a selection factor greater than zero),
+ * or points in curves with a selection factor greater than zero).
+ */
+IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices);
+
+} // namespace blender::ed::sculpt_paint
+
+#endif
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index e9fcd2bd5fe..9d5d8dd54cb 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -175,6 +175,14 @@ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win,
*/
struct ScrArea *ED_fileselect_handler_area_find_any_with_op(const struct wmWindow *win);
+/**
+ * If filepath property is not set on the operator, sets it to
+ * the blend file path (or untitled if file is not saved yet) with the given extension.
+ */
+void ED_fileselect_ensure_default_filepath(struct bContext *C,
+ struct wmOperator *op,
+ const char *extension);
+
/* TODO: Maybe we should move this to BLI?
* On the other hand, it's using defines from space-file area, so not sure... */
int ED_path_extension_type(const char *path);
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index b6488d6da56..45e61592424 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -403,12 +403,11 @@ void ED_gpencil_stroke_init_data(struct bGPDstroke *gps,
*/
void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]);
/**
- * Add a 2D Suzanne (original model created by Matias Mendiola).
+ * Add a 2D Suzanne.
*/
void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]);
/**
- * Add a Simple stroke with colors
- * (original design created by Daniel M. Lara and Matias Mendiola).
+ * Add a Simple stroke with colors.
*/
void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]);
/**
@@ -476,7 +475,8 @@ void ED_gpencil_stroke_reproject(struct Depsgraph *depsgraph,
struct bGPDframe *gpf,
struct bGPDstroke *gps,
eGP_ReprojectModes mode,
- bool keep_original);
+ bool keep_original,
+ const float offset);
/**
* Turn brush cursor in on/off.
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index 91ae8286531..da303f3552b 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -32,14 +32,15 @@ struct wmWindowManager;
float ED_space_image_zoom_level(const struct View2D *v2d, int grid_dimension);
void ED_space_image_grid_steps(struct SpaceImage *sima,
- float grid_steps[SI_GRID_STEPS_LEN],
+ float grid_steps_x[SI_GRID_STEPS_LEN],
+ float grid_steps_y[SI_GRID_STEPS_LEN],
int grid_dimension);
/**
* Calculate the increment snapping value for UV/image editor based on the zoom factor
* The code in here (except the offset part) is used in `grid_frag.glsl` (see `grid_res`) for
* drawing the grid overlay for the UV/Image editor.
*/
-float ED_space_image_increment_snap_value(int grid_dimesnions,
+float ED_space_image_increment_snap_value(int grid_dimensions,
const float grid_steps[SI_GRID_STEPS_LEN],
float zoom_factor);
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index 9596c3a7fed..e5bcdcdd282 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -235,7 +235,7 @@ typedef enum eKeyPasteError {
KEYFRAME_PASTE_OK,
/* Nothing was copied */
KEYFRAME_PASTE_NOTHING_TO_PASTE,
- /* No F-curves was selected to paste into*/
+ /* No F-curves was selected to paste into. */
KEYFRAME_PASTE_NOWHERE_TO_PASTE
} eKeyPasteError;
@@ -388,9 +388,6 @@ bool keyframe_region_circle_test(const KeyframeEdit_CircleData *data_circle, con
/* ************************************************ */
/* Destructive Editing API (keyframes_general.c) */
-void delete_fcurve_key(struct FCurve *fcu, int index, bool do_recalc);
-bool delete_fcurve_keys(struct FCurve *fcu);
-void clear_fcurve_keys(struct FCurve *fcu);
bool duplicate_fcurve_keys(struct FCurve *fcu);
float get_default_rna_value(struct FCurve *fcu, struct PropertyRNA *prop, struct PointerRNA *ptr);
diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h
index 30a98129ee6..26743a2bd08 100644
--- a/source/blender/editors/include/ED_mesh.h
+++ b/source/blender/editors/include/ED_mesh.h
@@ -137,14 +137,17 @@ void EDBM_update_extern(struct Mesh *me, bool do_tessellation, bool is_destructi
*/
struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm,
const struct Scene *scene,
- bool face_selected,
bool uv_selected,
bool use_winding,
+ bool use_seams,
bool do_islands);
void BM_uv_element_map_free(struct UvElementMap *element_map);
-struct UvElement *BM_uv_element_get(struct UvElementMap *map,
- struct BMFace *efa,
- struct BMLoop *l);
+struct UvElement *BM_uv_element_get(const struct UvElementMap *map,
+ const struct BMFace *efa,
+ const struct BMLoop *l);
+struct UvElement *BM_uv_element_get_head(struct UvElementMap *map, struct UvElement *child);
+
+struct UvElement **BM_uv_element_map_ensure_head_table(struct UvElementMap *element_map);
/**
* Can we edit UV's for this mesh?
@@ -181,9 +184,13 @@ void EDBM_project_snap_verts(struct bContext *C,
/* editmesh_automerge.c */
-void EDBM_automerge(struct Object *ob, bool update, char hflag, float dist);
-void EDBM_automerge_and_split(
- struct Object *ob, bool split_edges, bool split_faces, bool update, char hflag, float dist);
+void EDBM_automerge(struct Object *obedit, bool update, char hflag, float dist);
+void EDBM_automerge_and_split(struct Object *obedit,
+ bool split_edges,
+ bool split_faces,
+ bool update,
+ char hflag,
+ float dist);
/* editmesh_undo.c */
@@ -390,7 +397,10 @@ void ED_keymap_mesh(struct wmKeyConfig *keyconf);
* Copy the face flags, most importantly selection from the mesh to the final derived mesh,
* use in object mode when selecting faces (while painting).
*/
-void paintface_flush_flags(struct bContext *C, struct Object *ob, short flag);
+void paintface_flush_flags(struct bContext *C,
+ struct Object *ob,
+ bool flush_selection,
+ bool flush_hidden);
/**
* \return True when pick finds an element or the selection changed.
*/
@@ -425,6 +435,9 @@ void paintvert_select_ungrouped(struct Object *ob, bool extend, bool flush_flags
void paintvert_flush_flags(struct Object *ob);
void paintvert_tag_select_update(struct bContext *C, struct Object *ob);
+void paintvert_hide(struct bContext *C, struct Object *ob, bool unselected);
+void paintvert_reveal(struct bContext *C, struct Object *ob, bool select);
+
/* mirrtopo */
typedef struct MirrTopoStore_t {
intptr_t *index_lookup;
@@ -442,7 +455,7 @@ void ED_mesh_mirrtopo_init(struct BMEditMesh *em,
bool skip_em_vert_array_init);
void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store);
-/* object_vgroup.c */
+/* object_vgroup.cc */
#define WEIGHT_REPLACE 1
#define WEIGHT_ADD 2
@@ -550,16 +563,10 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, int layernum);
bool ED_mesh_color_ensure(struct Mesh *me, const char *name);
int ED_mesh_color_add(
struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports);
-bool ED_mesh_color_remove_index(struct Mesh *me, int n);
-bool ED_mesh_color_remove_active(struct Mesh *me);
-bool ED_mesh_color_remove_named(struct Mesh *me, const char *name);
-
-bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name);
-int ED_mesh_sculpt_color_add(
- struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports);
-bool ED_mesh_sculpt_color_remove_index(struct Mesh *me, int n);
-bool ED_mesh_sculpt_color_remove_active(struct Mesh *me);
-bool ED_mesh_sculpt_color_remove_named(struct Mesh *me, const char *name);
+int ED_mesh_sculpt_color_add(struct Mesh *me,
+ const char *name,
+ bool do_init,
+ struct ReportList *reports);
void ED_mesh_report_mirror(struct wmOperator *op, int totmirr, int totfail);
void ED_mesh_report_mirror_ex(struct wmOperator *op, int totmirr, int totfail, char selectmode);
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 39c7ad3556c..acb0e53aa55 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -112,6 +112,7 @@ struct XFormObjectSkipChild_Container;
struct XFormObjectSkipChild_Container *ED_object_xform_skip_child_container_create(void);
void ED_object_xform_skip_child_container_item_ensure_from_array(
struct XFormObjectSkipChild_Container *xcs,
+ const struct Scene *scene,
struct ViewLayer *view_layer,
struct Object **objects,
uint objects_len);
@@ -213,16 +214,20 @@ void ED_object_base_free_and_unlink(struct Main *bmain, struct Scene *scene, str
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,
+bool ED_object_base_deselect_all_ex(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct View3D *v3d,
int action,
bool *r_any_visible);
-bool ED_object_base_deselect_all(struct ViewLayer *view_layer, struct View3D *v3d, int action);
+bool ED_object_base_deselect_all(const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ struct View3D *v3d,
+ int action);
/**
* Single object duplicate, if `dupflag == 0`, fully linked, else it uses the flags given.
* Leaves selection of base/object unaltered.
- * \note don't call this within a loop since clear_* funcs loop over the entire database.
+ * \note don't call this within a loop since clear_* functions loop over the entire database.
* \note caller must do `DAG_relations_tag_update(bmain);`
* this is not done automatic since we may duplicate many objects in a batch.
*/
@@ -539,6 +544,7 @@ bool ED_object_modifier_move_to_index(struct ReportList *reports,
bool ED_object_modifier_convert_psys_to_mesh(struct ReportList *reports,
struct Main *bmain,
struct Depsgraph *depsgraph,
+ struct Scene *scene,
struct ViewLayer *view_layer,
struct Object *ob,
struct ModifierData *md);
@@ -662,7 +668,9 @@ void ED_object_check_force_modifiers(struct Main *bmain,
* If id is not already an Object, try to find an object that uses it as data.
* Prefers active, then selected, then visible/selectable.
*/
-struct Base *ED_object_find_first_by_data_id(struct ViewLayer *view_layer, struct ID *id);
+struct Base *ED_object_find_first_by_data_id(const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ struct ID *id);
/**
* Select and make the target object active in the view layer.
diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h
index ba5834fd508..048424cdee1 100644
--- a/source/blender/editors/include/ED_paint.h
+++ b/source/blender/editors/include/ED_paint.h
@@ -22,6 +22,7 @@ struct UndoType;
struct bContext;
struct wmKeyConfig;
struct wmOperator;
+typedef struct PaintTileMap PaintTileMap;
/* paint_ops.c */
@@ -76,7 +77,7 @@ void ED_image_undo_restore(struct UndoStep *us);
/** Export for ED_undo_sys. */
void ED_image_undosys_type(struct UndoType *ut);
-void *ED_image_paint_tile_find(struct ListBase *paint_tiles,
+void *ED_image_paint_tile_find(PaintTileMap *paint_tile_map,
struct Image *image,
struct ImBuf *ibuf,
struct ImageUser *iuser,
@@ -84,7 +85,7 @@ void *ED_image_paint_tile_find(struct ListBase *paint_tiles,
int y_tile,
unsigned short **r_mask,
bool validate);
-void *ED_image_paint_tile_push(struct ListBase *paint_tiles,
+void *ED_image_paint_tile_push(PaintTileMap *paint_tile_map,
struct Image *image,
struct ImBuf *ibuf,
struct ImBuf **tmpibuf,
@@ -98,7 +99,7 @@ void *ED_image_paint_tile_push(struct ListBase *paint_tiles,
void ED_image_paint_tile_lock_init(void);
void ED_image_paint_tile_lock_end(void);
-struct ListBase *ED_image_paint_tile_list_get(void);
+struct PaintTileMap *ED_image_paint_tile_map_get(void);
#define ED_IMAGE_UNDO_TILE_BITS 6
#define ED_IMAGE_UNDO_TILE_SIZE (1 << ED_IMAGE_UNDO_TILE_BITS)
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index a24c8625a63..144fa4e0b93 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -294,7 +294,7 @@ void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win);
void ED_screen_ensure_updated(struct wmWindowManager *wm,
struct wmWindow *win,
struct bScreen *screen);
-void ED_screen_do_listen(struct bContext *C, struct wmNotifier *note);
+void ED_screen_do_listen(struct bContext *C, const struct wmNotifier *note);
/**
* \brief Change the active screen.
*
@@ -353,8 +353,8 @@ struct ScrArea *ED_screen_state_toggle(struct bContext *C,
struct ScrArea *area,
short state);
/**
- * Wrapper to open a temporary space either as fullscreen space, or as separate window, as defined
- * by \a display_type.
+ * Wrapper to open a temporary space either as full-screen space, or as separate window,
+ * as defined by \a display_type.
*
* \param title: Title to set for the window, if a window is spawned.
* \param x, y: Position of the window, if a window is spawned.
diff --git a/source/blender/editors/include/ED_screen_types.h b/source/blender/editors/include/ED_screen_types.h
index 21bb412d072..bf64be9f7a7 100644
--- a/source/blender/editors/include/ED_screen_types.h
+++ b/source/blender/editors/include/ED_screen_types.h
@@ -112,7 +112,7 @@ enum {
*/
AZONE_REGION,
/**
- * Used when in editor fullscreen draw a corner to return to normal mode.
+ * Used when in editor full-screen draw a corner to return to normal mode.
*/
AZONE_FULLSCREEN,
/**
diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h
index 550040d2bc6..1e220d33ff4 100644
--- a/source/blender/editors/include/ED_sculpt.h
+++ b/source/blender/editors/include/ED_sculpt.h
@@ -20,6 +20,7 @@ struct rcti;
struct wmMsgSubscribeKey;
struct wmMsgSubscribeValue;
struct wmRegionMessageSubscribeParams;
+struct wmOperator;
/* sculpt.c */
@@ -33,7 +34,7 @@ bool ED_sculpt_mask_box_select(struct bContext *C,
/* sculpt_transform.c */
void ED_sculpt_update_modal_transform(struct bContext *C, struct Object *ob);
-void ED_sculpt_init_transform(struct bContext *C, struct Object *ob);
+void ED_sculpt_init_transform(struct bContext *C, struct Object *ob, const char *undo_name);
void ED_sculpt_end_transform(struct bContext *C, struct Object *ob);
/* sculpt_undo.c */
@@ -41,7 +42,13 @@ void ED_sculpt_end_transform(struct bContext *C, struct Object *ob);
/** Export for ED_undo_sys. */
void ED_sculpt_undosys_type(struct UndoType *ut);
-void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name);
+/**
+ * Pushes an undo step using the operator name. This is necessary for
+ * redo panels to work; operators that do not support that may use
+ * #ED_sculpt_undo_geometry_begin_ex instead if so desired.
+ */
+void ED_sculpt_undo_geometry_begin(struct Object *ob, const struct wmOperator *op);
+void ED_sculpt_undo_geometry_begin_ex(struct Object *ob, const char *name);
void ED_sculpt_undo_geometry_end(struct Object *ob);
/* Face sets. */
diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h
index c69698f3f73..07d4f43bb2b 100644
--- a/source/blender/editors/include/ED_space_api.h
+++ b/source/blender/editors/include/ED_space_api.h
@@ -50,7 +50,7 @@ void ED_spacetype_spreadsheet(void);
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Spacetype Static Data
+/** \name Space-type Static Data
* Calls for instancing and freeing space-type static data called in #WM_init_exit
* \{ */
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index 82cc518f029..d7fb108809f 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -94,7 +94,8 @@ bool BIF_createTransformOrientation(struct bContext *C,
bool overwrite);
void BIF_selectTransformOrientation(struct bContext *C, struct TransformOrientation *target);
-void ED_getTransformOrientationMatrix(struct ViewLayer *view_layer,
+void ED_getTransformOrientationMatrix(const struct Scene *scene,
+ struct ViewLayer *view_layer,
const struct View3D *v3d,
struct Object *ob,
struct Object *obedit,
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 db44d9af706..f9ca578f282 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -57,13 +57,13 @@ struct SnapObjectParams {
/* Geometry for snapping in edit mode. */
eSnapEditType edit_mode_type;
/* snap to the closest element, use when using more than one snap type */
- bool use_occlusion_test : true;
+ bool use_occlusion_test : 1;
/* exclude back facing geometry from snapping */
- bool use_backface_culling : true;
+ bool use_backface_culling : 1;
/* Break nearest face snapping into steps to improve transformations across U-shaped targets. */
short face_nearest_steps;
/* Enable to force nearest face snapping to snap to target the source was initially near. */
- bool keep_on_same_target;
+ bool keep_on_same_target : 1;
};
typedef struct SnapObjectContext SnapObjectContext;
diff --git a/source/blender/editors/include/ED_types.h b/source/blender/editors/include/ED_types.h
deleted file mode 100644
index eba93ed6744..00000000000
--- a/source/blender/editors/include/ED_types.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2008 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup editors
- */
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* **************** GENERAL EDITOR-WIDE TYPES AND DEFINES ************************** */
-
-/* old blender defines... should be deprecated? */
-#define DESELECT 0
-#define SELECT 1
-#define ACTIVE 2
-
-/* proposal = put scene pointers on function calls? */
-// #define BASACT (scene->basact)
-// #define OBACT (BASACT ? BASACT->object : NULL)
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/editors/include/ED_undo.h b/source/blender/editors/include/ED_undo.h
index 8c5f25e6b67..39bbd8adc75 100644
--- a/source/blender/editors/include/ED_undo.h
+++ b/source/blender/editors/include/ED_undo.h
@@ -15,6 +15,7 @@ extern "C" {
struct Base;
struct CLG_LogRef;
struct Object;
+struct Scene;
struct UndoStack;
struct ViewLayer;
struct bContext;
@@ -79,9 +80,12 @@ void ED_undo_object_editmode_restore_helper(struct bContext *C,
uint object_array_len,
uint object_array_stride);
-struct Object **ED_undo_editmode_objects_from_view_layer(struct ViewLayer *view_layer,
+struct Object **ED_undo_editmode_objects_from_view_layer(const struct Scene *scene,
+ struct ViewLayer *view_layer,
uint *r_len);
-struct Base **ED_undo_editmode_bases_from_view_layer(struct ViewLayer *view_layer, uint *r_len);
+struct Base **ED_undo_editmode_bases_from_view_layer(const struct Scene *scene,
+ struct ViewLayer *view_layer,
+ uint *r_len);
/**
* Ideally we won't access the stack directly,
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 80a75da27f8..38e542fc0ca 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -107,7 +107,7 @@ bool uvedit_uv_select_test(const struct Scene *scene, struct BMLoop *l, int cd_l
* Changes selection state of a single UV Face.
*/
void uvedit_face_select_set(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *em,
struct BMFace *efa,
bool select,
bool do_history,
@@ -118,7 +118,7 @@ void uvedit_face_select_set(const struct Scene *scene,
* Changes selection state of a single UV Edge.
*/
void uvedit_edge_select_set(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *em,
struct BMLoop *l,
bool select,
bool do_history,
@@ -129,7 +129,7 @@ void uvedit_edge_select_set(const struct Scene *scene,
* Changes selection state of a single UV vertex.
*/
void uvedit_uv_select_set(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *em,
struct BMLoop *l,
bool select,
bool do_history,
@@ -139,30 +139,30 @@ void uvedit_uv_select_set(const struct Scene *scene,
* use. */
void uvedit_face_select_enable(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *bm,
struct BMFace *efa,
bool do_history,
int cd_loop_uv_offset);
void uvedit_face_select_disable(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *bm,
struct BMFace *efa,
int cd_loop_uv_offset);
void uvedit_edge_select_enable(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *bm,
struct BMLoop *l,
bool do_history,
int cd_loop_uv_offset);
void uvedit_edge_select_disable(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *bm,
struct BMLoop *l,
int cd_loop_uv_offset);
void uvedit_uv_select_enable(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *bm,
struct BMLoop *l,
bool do_history,
int cd_loop_uv_offset);
void uvedit_uv_select_disable(const struct Scene *scene,
- struct BMEditMesh *em,
+ struct BMesh *bm,
struct BMLoop *l,
int cd_loop_uv_offset);
@@ -179,13 +179,13 @@ void uvedit_edge_select_set_with_sticky(const struct Scene *scene,
struct BMLoop *l,
bool select,
bool do_history,
- uint cd_loop_uv_offset);
+ int cd_loop_uv_offset);
void uvedit_uv_select_set_with_sticky(const struct Scene *scene,
struct BMEditMesh *em,
struct BMLoop *l,
bool select,
bool do_history,
- uint cd_loop_uv_offset);
+ int cd_loop_uv_offset);
/* Low level functions for sticky element selection (sticky mode independent). Type of sticky
* selection is specified explicitly (using sticky_flag, except for face selection). */
@@ -305,6 +305,29 @@ void ED_uvedit_buttons_register(struct ARegionType *art);
/* uvedit_islands.c */
+struct FaceIsland {
+ struct FaceIsland *next;
+ struct FaceIsland *prev;
+ struct BMFace **faces;
+ int faces_len;
+ rctf bounds_rect;
+ /**
+ * \note While this is duplicate information,
+ * it allows islands from multiple meshes to be stored in the same list.
+ */
+ int cd_loop_uv_offset;
+ float aspect_y;
+};
+
+int bm_mesh_calc_uv_islands(const Scene *scene,
+ struct BMesh *bm,
+ ListBase *island_list,
+ const bool only_selected_faces,
+ const bool only_selected_uvs,
+ const bool use_seams,
+ const float aspect_y,
+ const int cd_loop_uv_offset);
+
struct UVMapUDIM_Params {
const struct Image *image;
/** Copied from #SpaceImage.tile_grid_shape */
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 0298983ed26..c72f3121217 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -711,7 +711,7 @@ bool ED_view3d_win_to_segment_clipped(const struct Depsgraph *depsgraph,
float r_ray_start[3],
float r_ray_end[3],
bool do_clip_planes);
-void ED_view3d_ob_project_mat_get(const struct RegionView3D *v3d,
+void ED_view3d_ob_project_mat_get(const struct RegionView3D *rv3d,
const struct Object *ob,
float r_pmat[4][4]);
void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d,
@@ -950,7 +950,7 @@ int view3d_opengl_select_with_id_filter(struct ViewContext *vc,
eV3DSelectObjectFilter select_filter,
uint select_id);
-/* view3d_select.c */
+/* view3d_select.cc */
float ED_view3d_select_dist_px(void);
void ED_view3d_viewcontext_init(struct bContext *C,
@@ -1171,7 +1171,7 @@ void ED_view3d_camera_lock_init(const struct Depsgraph *depsgraph,
*
* Apply the 3D Viewport transformation back to the camera object.
*
- * \return true if the camera is moved.
+ * \return true if the camera (or one of it's parents) was moved.
*/
bool ED_view3d_camera_lock_sync(const struct Depsgraph *depsgraph,
struct View3D *v3d,
@@ -1196,6 +1196,36 @@ bool ED_view3d_camera_lock_autokey(struct View3D *v3d,
void ED_view3d_lock_clear(struct View3D *v3d);
+/**
+ * Check if creating an undo step should be performed if the viewport moves.
+ * \return true if #ED_view3d_camera_lock_undo_push would do an undo push.
+ */
+bool ED_view3d_camera_lock_undo_test(const View3D *v3d,
+ const RegionView3D *rv3d,
+ struct bContext *C);
+
+/**
+ * Create an undo step when the camera is locked to the view.
+ * \param str: The name of the undo step (typically #wmOperatorType.name should be used).
+ *
+ * \return true when the call to push an undo step was made.
+ */
+bool ED_view3d_camera_lock_undo_push(const char *str,
+ const View3D *v3d,
+ const struct RegionView3D *rv3d,
+ struct bContext *C);
+
+/**
+ * A version of #ED_view3d_camera_lock_undo_push that performs a grouped undo push.
+ *
+ * \note use for actions that are likely to be repeated such as mouse wheel to zoom,
+ * where adding a separate undo step each time isn't desirable.
+ */
+bool ED_view3d_camera_lock_undo_grouped_push(const char *str,
+ const View3D *v3d,
+ const struct RegionView3D *rv3d,
+ struct bContext *C);
+
#define VIEW3D_MARGIN 1.4f
#define VIEW3D_DIST_FALLBACK 1.0f
diff --git a/source/blender/editors/include/UI_abstract_view.hh b/source/blender/editors/include/UI_abstract_view.hh
index 82f81f1702b..dfddace8899 100644
--- a/source/blender/editors/include/UI_abstract_view.hh
+++ b/source/blender/editors/include/UI_abstract_view.hh
@@ -3,11 +3,16 @@
/** \file
* \ingroup editorui
*
- * Base for all views (UIs to display data sets), supporting common features.
+ * Base class for all views (UIs to display data sets) and view items, supporting common features.
* https://wiki.blender.org/wiki/Source/Interface/Views
*
* One of the most important responsibilities of the base class is managing reconstruction,
- * enabling state that is persistent over reconstructions/redraws.
+ * enabling state that is persistent over reconstructions/redraws. Other features:
+ * - Renaming
+ * - Custom context menus
+ * - Notifier listening
+ * - Drag controllers (dragging view items)
+ * - Drop controllers (dropping onto/into view items)
*/
#pragma once
@@ -15,13 +20,28 @@
#include <array>
#include <memory>
+#include "DNA_defs.h"
+
#include "BLI_span.hh"
+#include "BLI_string_ref.hh"
+struct bContext;
+struct uiBlock;
+struct uiBut;
+struct uiLayout;
+struct uiViewItemHandle;
+struct wmDrag;
struct wmNotifier;
namespace blender::ui {
+class AbstractViewItem;
+class AbstractViewItemDropController;
+class AbstractViewItemDragController;
+
class AbstractView {
+ friend class AbstractViewItem;
+
bool is_reconstructed_ = false;
/**
* Only one item can be renamed at a time. So rather than giving each item an own rename buffer
@@ -38,6 +58,12 @@ class AbstractView {
/** Listen to a notifier, returning true if a redraw is needed. */
virtual bool listen(const wmNotifier &) const;
+ /**
+ * Makes \a item valid for display in this view. Behavior is undefined for items not registered
+ * with this.
+ */
+ void register_item(AbstractViewItem &item);
+
/** Only one item can be renamed at a time. */
bool is_renaming() const;
/** \return If renaming was started successfully. */
@@ -58,7 +84,6 @@ class AbstractView {
* After this, reconstruction is complete (see #is_reconstructed()).
*/
void update_from_old(uiBlock &new_block);
-
/**
* Check if the view is fully (re-)constructed. That means, both the build function and
* #update_from_old() have finished.
@@ -66,4 +91,190 @@ class AbstractView {
bool is_reconstructed() const;
};
+class AbstractViewItem {
+ friend class AbstractView;
+ friend class ViewItemAPIWrapper;
+
+ protected:
+ /**
+ * The view this item is a part of, and was registered for using #AbstractView::register_item().
+ * If this wasn't done, the behavior of items is undefined.
+ */
+ AbstractView *view_ = nullptr;
+ bool is_active_ = false;
+ bool is_renaming_ = false;
+
+ public:
+ virtual ~AbstractViewItem() = default;
+
+ virtual void build_context_menu(bContext &C, uiLayout &column) const;
+
+ /**
+ * Queries if the view item supports renaming in principle. Renaming may still fail, e.g. if
+ * another item is already being renamed.
+ */
+ virtual bool supports_renaming() const;
+ /**
+ * Try renaming the item, or the data it represents. Can assume
+ * #AbstractViewItem::supports_renaming() returned true. Sub-classes that override this should
+ * usually call this, unless they have a custom #AbstractViewItem.matches() implementation.
+ *
+ * \return True if the renaming was successful.
+ */
+ virtual bool rename(StringRefNull new_name);
+ /**
+ * Get the string that should be used for renaming, typically the item's label. This string will
+ * not be modified, but if the renaming is canceled, the value will be reset to this.
+ */
+ virtual StringRef get_rename_string() const;
+
+ /**
+ * If an item wants to support being dragged, it has to return a drag controller here.
+ * That is an object implementing #AbstractViewItemDragController.
+ */
+ virtual std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const;
+ /**
+ * If an item wants to support dropping data into it, it has to return a drop controller here.
+ * That is an object implementing #AbstractViewItemDropController.
+ *
+ * \note This drop controller may be requested for each event. The view doesn't keep a drop
+ * controller around currently. So it can not contain persistent state.
+ */
+ virtual std::unique_ptr<AbstractViewItemDropController> create_drop_controller() const;
+
+ /** Get the view this item is registered for using #AbstractView::register_item(). */
+ AbstractView &get_view() const;
+
+ /**
+ * Requires the view to have completed reconstruction, see #is_reconstructed(). Otherwise we
+ * can't be sure about the item state.
+ */
+ bool is_active() const;
+
+ bool is_renaming() const;
+ void begin_renaming();
+ void end_renaming();
+ void rename_apply();
+
+ template<typename ToType = AbstractViewItem>
+ static ToType *from_item_handle(uiViewItemHandle *handle);
+
+ protected:
+ AbstractViewItem() = default;
+
+ /**
+ * Compare this item's identity to \a other to check if they represent the same data.
+ * Implementations can assume that the types match already (caller must check).
+ *
+ * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active,
+ * renaming, etc.).
+ */
+ virtual bool matches(const AbstractViewItem &other) const = 0;
+
+ /**
+ * Copy persistent state (e.g. active, selection, etc.) from a matching item of
+ * the last redraw to this item. If sub-classes introduce more advanced state they should
+ * override this and make it update their state accordingly.
+ *
+ * \note Always call the base class implementation when overriding this!
+ */
+ virtual void update_from_old(const AbstractViewItem &old);
+
+ /**
+ * Add a text button for renaming the item to \a block. This must be used for the built-in
+ * renaming to work. This button is meant to appear temporarily. It is removed when renaming is
+ * done.
+ */
+ void add_rename_button(uiBlock &block);
+};
+
+template<typename ToType> ToType *AbstractViewItem::from_item_handle(uiViewItemHandle *handle)
+{
+ static_assert(std::is_base_of<AbstractViewItem, ToType>::value,
+ "Type must derive from and implement the AbstractViewItem interface");
+
+ return dynamic_cast<ToType *>(reinterpret_cast<AbstractViewItem *>(handle));
+}
+
+/* ---------------------------------------------------------------------- */
+/** \name Drag 'n Drop
+ * \{ */
+
+/**
+ * Class to enable dragging a view item. An item can return a drop controller for itself by
+ * implementing #AbstractViewItem::create_drag_controller().
+ */
+class AbstractViewItemDragController {
+ protected:
+ AbstractView &view_;
+
+ public:
+ AbstractViewItemDragController(AbstractView &view);
+ virtual ~AbstractViewItemDragController() = default;
+
+ virtual int get_drag_type() const = 0;
+ virtual void *create_drag_data() const = 0;
+ virtual void on_drag_start();
+
+ /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast`
+ * exception if the view is not of the requested type. */
+ template<class ViewType> inline ViewType &get_view() const;
+};
+
+/**
+ * Class to define the behavior when dropping something onto/into a view item, plus the behavior
+ * when dragging over this item. An item can return a drop controller for itself via a custom
+ * implementation of #AbstractViewItem::create_drop_controller().
+ */
+class AbstractViewItemDropController {
+ protected:
+ AbstractView &view_;
+
+ public:
+ AbstractViewItemDropController(AbstractView &view);
+ virtual ~AbstractViewItemDropController() = default;
+
+ /**
+ * Check if the data dragged with \a drag can be dropped on the item this controller is for.
+ * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping
+ * isn't possible on this item. Shouldn't be done too aggressively, e.g.
+ * don't set this if the drag-type can't be dropped here; only if it can
+ * but there's another reason it can't be dropped.
+ * Can assume this is a non-null pointer.
+ */
+ virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0;
+ /**
+ * Custom text to display when dragging over a view item. Should explain what happens when
+ * dropping the data onto this item. Will only be used if #AbstractViewItem::can_drop()
+ * returns true, so the implementing override doesn't have to check that again.
+ * The returned value must be a translated string.
+ */
+ virtual std::string drop_tooltip(const wmDrag &drag) const = 0;
+ /**
+ * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this
+ * controller is for.
+ */
+ virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0;
+
+ /** Request the view the item is registered for as type #ViewType. Throws a `std::bad_cast`
+ * exception if the view is not of the requested type. */
+ template<class ViewType> inline ViewType &get_view() const;
+};
+
+template<class ViewType> ViewType &AbstractViewItemDragController::get_view() const
+{
+ static_assert(std::is_base_of<AbstractView, ViewType>::value,
+ "Type must derive from and implement the ui::AbstractView interface");
+ return dynamic_cast<ViewType &>(view_);
+}
+
+template<class ViewType> ViewType &AbstractViewItemDropController::get_view() const
+{
+ static_assert(std::is_base_of<AbstractView, ViewType>::value,
+ "Type must derive from and implement the ui::AbstractView interface");
+ return dynamic_cast<ViewType &>(view_);
+}
+
+/** \} */
+
} // namespace blender::ui
diff --git a/source/blender/editors/include/UI_grid_view.hh b/source/blender/editors/include/UI_grid_view.hh
index cabc49e411c..402c0c8512f 100644
--- a/source/blender/editors/include/UI_grid_view.hh
+++ b/source/blender/editors/include/UI_grid_view.hh
@@ -19,7 +19,7 @@
struct bContext;
struct PreviewImage;
struct uiBlock;
-struct uiButGridTile;
+struct uiButViewItem;
struct uiLayout;
struct View2D;
struct wmNotifier;
@@ -32,43 +32,29 @@ class AbstractGridView;
/** \name Grid-View Item Type
* \{ */
-class AbstractGridViewItem {
+class AbstractGridViewItem : public AbstractViewItem {
friend class AbstractGridView;
friend class GridViewLayoutBuilder;
- const AbstractGridView *view_;
-
- bool is_active_ = false;
-
protected:
/** Reference to a string that uniquely identifies this item in the view. */
StringRef identifier_{};
- /** Every visible item gets a button of type #UI_BTYPE_GRID_TILE during the layout building. */
- uiButGridTile *grid_tile_but_ = nullptr;
+ /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */
+ uiButViewItem *view_item_but_ = nullptr;
public:
virtual ~AbstractGridViewItem() = default;
virtual void build_grid_tile(uiLayout &layout) const = 0;
- /**
- * Compare this item's identifier to \a other to check if they represent the same data.
- * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. active,
- * renaming, etc.).
- */
- bool matches(const AbstractGridViewItem &other) const;
-
const AbstractGridView &get_view() const;
- /**
- * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we
- * can't be sure about the item state.
- */
- bool is_active() const;
-
protected:
AbstractGridViewItem(StringRef identifier);
+ /** See AbstractViewItem::matches(). */
+ virtual bool matches(const AbstractViewItem &other) const override;
+
/** Called when the item's state changes from inactive to active. */
virtual void on_activate();
/**
@@ -78,13 +64,6 @@ class AbstractGridViewItem {
virtual std::optional<bool> should_be_active() const;
/**
- * Copy persistent state (e.g. active, selection, etc.) from a matching item of
- * the last redraw to this item. If sub-classes introduce more advanced state they should
- * override this and make it update their state accordingly.
- */
- virtual void update_from_old(const AbstractGridViewItem &old);
-
- /**
* Activates this item, deactivates other items, and calls the
* #AbstractGridViewItem::on_activate() function.
* Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index f39c62d8e2b..c3376493413 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -74,10 +74,8 @@ typedef struct uiLayout uiLayout;
typedef struct uiPopupBlockHandle uiPopupBlockHandle;
/* C handle for C++ #ui::AbstractView type. */
typedef struct uiViewHandle uiViewHandle;
-/* C handle for C++ #ui::AbstractTreeViewItem type. */
-typedef struct uiTreeViewItemHandle uiTreeViewItemHandle;
-/* C handle for C++ #ui::AbstractGridViewItem type. */
-typedef struct uiGridViewItemHandle uiGridViewItemHandle;
+/* C handle for C++ #ui::AbstractViewItem type. */
+typedef struct uiViewItemHandle uiViewItemHandle;
/* Defines */
@@ -87,7 +85,7 @@ typedef struct uiGridViewItemHandle uiGridViewItemHandle;
/* Separator for text in search menus (right pointing arrow).
* keep in sync with `string_search.cc`. */
-#define UI_MENU_ARROW_SEP "\xe2\x96\xb6"
+#define UI_MENU_ARROW_SEP "\xe2\x96\xb8"
/* names */
#define UI_MAX_DRAW_STR 400
@@ -356,7 +354,7 @@ typedef enum {
UI_BTYPE_LABEL = 20 << 9,
UI_BTYPE_KEY_EVENT = 24 << 9,
UI_BTYPE_HSVCUBE = 26 << 9,
- /** menu (often used in headers), **_MENU /w different draw-type */
+ /** Menu (often used in headers), `*_MENU` with different draw-type. */
UI_BTYPE_PULLDOWN = 27 << 9,
UI_BTYPE_ROUNDBOX = 28 << 9,
UI_BTYPE_COLORBAND = 30 << 9,
@@ -391,10 +389,8 @@ typedef enum {
/** Resize handle (resize uilist). */
UI_BTYPE_GRIP = 57 << 9,
UI_BTYPE_DECORATOR = 58 << 9,
- /* An item in a tree view. Parent items may be collapsible. */
- UI_BTYPE_TREEROW = 59 << 9,
- /* An item in a grid view. */
- UI_BTYPE_GRID_TILE = 60 << 9,
+ /* An item a view (see #ui::AbstractViewItem). */
+ UI_BTYPE_VIEW_ITEM = 59 << 9,
} eButType;
#define BUTTYPE (63 << 9)
@@ -536,6 +532,7 @@ typedef struct ARegion *(*uiButSearchTooltipFn)(struct bContext *C,
const struct rcti *item_rect,
void *arg,
void *active);
+typedef void (*uiButSearchListenFn)(const struct wmRegionListenerParams *params, void *arg);
/* Must return allocated string. */
typedef char *(*uiButToolTipFunc)(struct bContext *C, void *argN, const char *tip);
@@ -1663,6 +1660,7 @@ void UI_but_func_search_set(uiBut *but,
void *active);
void UI_but_func_search_set_context_menu(uiBut *but, uiButSearchContextMenuFn context_menu_fn);
void UI_but_func_search_set_tooltip(uiBut *but, uiButSearchTooltipFn tooltip_fn);
+void UI_but_func_search_set_listen(uiBut *but, uiButSearchListenFn listen_fn);
/**
* \param search_sep_string: when not NULL, this string is used as a separator,
* showing the icon and highlighted text after the last instance of this string.
@@ -1685,8 +1683,6 @@ int UI_search_items_find_index(uiSearchItems *items, const char *name);
*/
void UI_but_hint_drawstr_set(uiBut *but, const char *string);
-void UI_but_treerow_indentation_set(uiBut *but, int indentation);
-
void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]);
void UI_but_number_step_size_set(uiBut *but, float step_size);
@@ -2488,7 +2484,7 @@ enum uiTemplateListFlags {
ENUM_OPERATORS(enum uiTemplateListFlags, UI_TEMPLATE_LIST_FLAGS_LAST);
void uiTemplateList(uiLayout *layout,
- struct bContext *C,
+ const struct bContext *C,
const char *listtype_name,
const char *list_id,
struct PointerRNA *dataptr,
@@ -2502,7 +2498,7 @@ void uiTemplateList(uiLayout *layout,
int columns,
enum uiTemplateListFlags flags);
struct uiList *uiTemplateList_ex(uiLayout *layout,
- struct bContext *C,
+ const struct bContext *C,
const char *listtype_name,
const char *list_id,
struct PointerRNA *dataptr,
@@ -2572,7 +2568,7 @@ enum {
UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY = (1 << 2),
};
void uiTemplateAssetView(struct uiLayout *layout,
- struct bContext *C,
+ const struct bContext *C,
const char *list_id,
struct PointerRNA *asset_library_dataptr,
const char *asset_library_propname,
@@ -3201,45 +3197,44 @@ void UI_interface_tag_script_reload(void);
void UI_block_views_listen(const uiBlock *block,
const struct wmRegionListenerParams *listener_params);
-bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle);
-bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item);
-bool UI_grid_view_item_matches(const uiGridViewItemHandle *a, const uiGridViewItemHandle *b);
-bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b);
+bool UI_view_item_is_active(const uiViewItemHandle *item_handle);
+bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle);
/**
- * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't
- * support dragging, i.e. it won't create a drag-controller upon request.
- * \return True if dragging started successfully, otherwise false.
+ * Can \a item_handle be renamed right now? Note that this isn't just a mere wrapper around
+ * #AbstractViewItem::supports_renaming(). This also checks if there is another item being renamed,
+ * and returns false if so.
*/
-bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_);
-bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_,
- const struct wmDrag *drag,
- const char **r_disabled_hint);
-char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag);
+bool UI_view_item_can_rename(const uiViewItemHandle *item_handle);
+void UI_view_item_begin_rename(uiViewItemHandle *item_handle);
+
+void UI_view_item_context_menu_build(struct bContext *C,
+ const uiViewItemHandle *item_handle,
+ uiLayout *column);
+
/**
- * Let a tree-view item handle a drop event.
- * \return True if the drop was handled by the tree-view item.
+ * Attempt to start dragging \a item_. This will not work if the view item doesn't
+ * support dragging, i.e. if it won't create a drag-controller upon request.
+ * \return True if dragging started successfully, otherwise false.
*/
-bool UI_tree_view_item_drop_handle(struct bContext *C,
- const uiTreeViewItemHandle *item_,
- const struct ListBase *drags);
+bool UI_view_item_drag_start(struct bContext *C, const uiViewItemHandle *item_);
+bool UI_view_item_can_drop(const uiViewItemHandle *item_,
+ const struct wmDrag *drag,
+ const char **r_disabled_hint);
+char *UI_view_item_drop_tooltip(const uiViewItemHandle *item, const struct wmDrag *drag);
/**
- * Can \a item_handle be renamed right now? Not that this isn't just a mere wrapper around
- * #AbstractTreeViewItem::can_rename(). This also checks if there is another item being renamed,
- * and returns false if so.
+ * Let a view item handle a drop event.
+ * \return True if the drop was handled by the view item.
*/
-bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle);
-void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle);
-
-void UI_tree_view_item_context_menu_build(struct bContext *C,
- const uiTreeViewItemHandle *item,
- uiLayout *column);
+bool UI_view_item_drop_handle(struct bContext *C,
+ const uiViewItemHandle *item_,
+ const struct ListBase *drags);
/**
- * \param xy: Coordinate to find a tree-row item at, in window space.
+ * \param xy: Coordinate to find a view item at, in window space.
*/
-uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region,
- const int xy[2]) ATTR_NONNULL(1, 2);
-uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region);
+uiViewItemHandle *UI_region_views_find_item_at(const struct ARegion *region, const int xy[2])
+ ATTR_NONNULL();
+uiViewItemHandle *UI_region_views_find_active_item(const struct ARegion *region);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh
index 82bfdd7e212..6c756984203 100644
--- a/source/blender/editors/include/UI_interface.hh
+++ b/source/blender/editors/include/UI_interface.hh
@@ -13,7 +13,7 @@
#include "UI_resources.h"
-namespace blender::nodes::geometry_nodes_eval_log {
+namespace blender::nodes::geo_eval_log {
struct GeometryAttributeInfo;
}
@@ -44,12 +44,11 @@ void context_path_add_generic(Vector<ContextPathItem> &path,
void template_breadcrumbs(uiLayout &layout, Span<ContextPathItem> context_path);
-void attribute_search_add_items(
- StringRefNull str,
- bool can_create_attribute,
- Span<const nodes::geometry_nodes_eval_log::GeometryAttributeInfo *> infos,
- uiSearchItems *items,
- bool is_first);
+void attribute_search_add_items(StringRefNull str,
+ bool can_create_attribute,
+ Span<const nodes::geo_eval_log::GeometryAttributeInfo *> infos,
+ uiSearchItems *items,
+ bool is_first);
} // namespace blender::ui
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index 22e747e37ad..9a46728097c 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -291,6 +291,8 @@ typedef enum ThemeColorID {
TH_WIDGET_EMBOSS,
TH_WIDGET_TEXT_CURSOR,
+ TH_WIDGET_TEXT_SELECTION,
+ TH_WIDGET_TEXT_HIGHLIGHT,
TH_EDITOR_OUTLINE,
TH_TRANSPARENT_CHECKER_PRIMARY,
diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh
index 9527df590b7..872a6085060 100644
--- a/source/blender/editors/include/UI_tree_view.hh
+++ b/source/blender/editors/include/UI_tree_view.hh
@@ -9,7 +9,6 @@
#pragma once
-#include <array>
#include <functional>
#include <memory>
#include <string>
@@ -25,18 +24,13 @@
struct bContext;
struct uiBlock;
struct uiBut;
-struct uiButTreeRow;
+struct uiButViewItem;
struct uiLayout;
-struct wmDrag;
-struct wmEvent;
-struct wmNotifier;
namespace blender::ui {
class AbstractTreeView;
class AbstractTreeViewItem;
-class AbstractTreeViewItemDropController;
-class AbstractTreeViewItemDragController;
/* ---------------------------------------------------------------------- */
/** \name Tree-View Item Container
@@ -153,7 +147,7 @@ class AbstractTreeView : public AbstractView, public TreeViewItemContainer {
* It also stores state information that needs to be persistent over redraws, like the collapsed
* state.
*/
-class AbstractTreeViewItem : public TreeViewItemContainer {
+class AbstractTreeViewItem : public AbstractViewItem, public TreeViewItemContainer {
friend class AbstractTreeView;
friend class TreeViewLayoutBuilder;
/* Higher-level API. */
@@ -161,20 +155,17 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
private:
bool is_open_ = false;
- bool is_active_ = false;
- bool is_renaming_ = false;
protected:
/** This label is used as the default way to identifying an item within its parent. */
std::string label_{};
- /** Every visible item gets a button of type #UI_BTYPE_TREEROW during the layout building. */
- uiButTreeRow *tree_row_but_ = nullptr;
+ /** Every visible item gets a button of type #UI_BTYPE_VIEW_ITEM during the layout building. */
+ uiButViewItem *view_item_but_ = nullptr;
public:
virtual ~AbstractTreeViewItem() = default;
virtual void build_row(uiLayout &row) = 0;
- virtual void build_context_menu(bContext &C, uiLayout &column) const;
AbstractTreeView &get_tree_view() const;
@@ -186,11 +177,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
* can't be sure about the item state.
*/
bool is_collapsed() const;
- /**
- * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we
- * can't be sure about the item state.
- */
- bool is_active() const;
protected:
/**
@@ -203,31 +189,21 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
*/
virtual std::optional<bool> should_be_active() const;
- /**
- * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if
- * another item is already being renamed.
- */
- virtual bool supports_renaming() const;
- /**
- * Try renaming the item, or the data it represents. Can assume
- * #AbstractTreeViewItem::supports_renaming() returned true. Sub-classes that override this
- * should usually call this, unless they have a custom #AbstractTreeViewItem.matches().
- *
- * \return True if the renaming was successful.
- */
- virtual bool rename(StringRefNull new_name);
+ /** See AbstractViewItem::get_rename_string(). */
+ virtual StringRef get_rename_string() const override;
+ /** See AbstractViewItem::rename(). */
+ virtual bool rename(StringRefNull new_name) override;
/**
* Return whether the item can be collapsed. Used to disable collapsing for items with children.
*/
virtual bool supports_collapsing() const;
- /**
- * Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of
- * the last redraw to this item. If sub-classes introduce more advanced state they should
- * override this and make it update their state accordingly.
- */
- virtual void update_from_old(const AbstractTreeViewItem &old);
+ /** See #AbstractViewItem::matches(). */
+ virtual bool matches(const AbstractViewItem &other) const override;
+
+ /** See #AbstractViewItem::update_from_old(). */
+ virtual void update_from_old(const AbstractViewItem &old) override;
/**
* Compare this item to \a other to check if they represent the same data.
@@ -235,22 +211,11 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
* open/closed, active, etc.). Items are only matched if their parents also match.
* By default this just matches the item's label (if the parents match!). If that isn't
* good enough for a sub-class, that can override it.
- */
- virtual bool matches(const AbstractTreeViewItem &other) const;
-
- /**
- * If an item wants to support being dragged, it has to return a drag controller here.
- * That is an object implementing #AbstractTreeViewItemDragController.
- */
- virtual std::unique_ptr<AbstractTreeViewItemDragController> create_drag_controller() const;
- /**
- * If an item wants to support dropping data into it, it has to return a drop controller here.
- * That is an object implementing #AbstractTreeViewItemDropController.
*
- * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop
- * controller around currently. So it can not contain persistent state.
+ * TODO #matches_single() is a rather temporary name, used to indicate that this only compares
+ * the item itself, not the parents. Item matching is expected to change quite a bit anyway.
*/
- virtual std::unique_ptr<AbstractTreeViewItemDropController> create_drop_controller() const;
+ virtual bool matches_single(const AbstractTreeViewItem &other) const;
/**
* Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate()
@@ -268,29 +233,24 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
*/
bool is_hovered() const;
bool is_collapsible() const;
- bool is_renaming() const;
void ensure_parents_uncollapsed();
- uiButTreeRow *tree_row_button();
+ uiButViewItem *view_item_button();
private:
- static void rename_button_fn(bContext *, void *, char *);
- static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but);
static void tree_row_click_fn(struct bContext *, void *, void *);
static void collapse_chevron_click_fn(bContext *, void *but_arg1, void *);
static bool is_collapse_chevron_but(const uiBut *but);
/** See #AbstractTreeView::change_state_delayed() */
void change_state_delayed();
- void end_renaming();
void add_treerow_button(uiBlock &block);
void add_indent(uiLayout &row) const;
void add_collapse_chevron(uiBlock &block) const;
void add_rename_button(uiLayout &row);
- bool matches_including_parents(const AbstractTreeViewItem &other) const;
bool has_active_child() const;
int count_parents() const;
};
@@ -298,69 +258,6 @@ class AbstractTreeViewItem : public TreeViewItemContainer {
/** \} */
/* ---------------------------------------------------------------------- */
-/** \name Drag 'n Drop
- * \{ */
-
-/**
- * Class to enable dragging a tree-item. An item can return a drop controller for itself via a
- * custom implementation of #AbstractTreeViewItem::create_drag_controller().
- */
-class AbstractTreeViewItemDragController {
- protected:
- AbstractTreeView &tree_view_;
-
- public:
- AbstractTreeViewItemDragController(AbstractTreeView &tree_view);
- virtual ~AbstractTreeViewItemDragController() = default;
-
- virtual int get_drag_type() const = 0;
- virtual void *create_drag_data() const = 0;
- virtual void on_drag_start();
-
- template<class TreeViewType> inline TreeViewType &tree_view() const;
-};
-
-/**
- * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this
- * item. An item can return a drop controller for itself via a custom implementation of
- * #AbstractTreeViewItem::create_drop_controller().
- */
-class AbstractTreeViewItemDropController {
- protected:
- AbstractTreeView &tree_view_;
-
- public:
- AbstractTreeViewItemDropController(AbstractTreeView &tree_view);
- virtual ~AbstractTreeViewItemDropController() = default;
-
- /**
- * Check if the data dragged with \a drag can be dropped on the item this controller is for.
- * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping
- * isn't possible on this item. Shouldn't be done too aggressively, e.g.
- * don't set this if the drag-type can't be dropped here; only if it can
- * but there's another reason it can't be dropped.
- * Can assume this is a non-null pointer.
- */
- virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0;
- /**
- * Custom text to display when dragging over a tree item. Should explain what happens when
- * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop()
- * returns true, so the implementing override doesn't have to check that again.
- * The returned value must be a translated string.
- */
- virtual std::string drop_tooltip(const wmDrag &drag) const = 0;
- /**
- * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this
- * controller is for.
- */
- virtual bool on_drop(struct bContext *C, const wmDrag &drag) = 0;
-
- template<class TreeViewType> inline TreeViewType &tree_view() const;
-};
-
-/** \} */
-
-/* ---------------------------------------------------------------------- */
/** \name Predefined Tree-View Item Types
*
* Common, Basic Tree-View Item Types.
@@ -431,18 +328,4 @@ inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args)
add_tree_item(std::make_unique<ItemT>(std::forward<Args>(args)...)));
}
-template<class TreeViewType> TreeViewType &AbstractTreeViewItemDragController::tree_view() const
-{
- static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value,
- "Type must derive from and implement the AbstractTreeView interface");
- return static_cast<TreeViewType &>(tree_view_);
-}
-
-template<class TreeViewType> TreeViewType &AbstractTreeViewItemDropController::tree_view() const
-{
- static_assert(std::is_base_of<AbstractTreeView, TreeViewType>::value,
- "Type must derive from and implement the AbstractTreeView interface");
- return static_cast<TreeViewType &>(tree_view_);
-}
-
} // namespace blender::ui
diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h
index 5c4eb254462..bdd830e0046 100644
--- a/source/blender/editors/include/UI_view2d.h
+++ b/source/blender/editors/include/UI_view2d.h
@@ -54,7 +54,7 @@ enum eView2D_CommonViewTypes {
#define V2D_SCROLL_HEIGHT ((0.45f * U.widget_unit) + (2.0f * U.pixelsize))
#define V2D_SCROLL_WIDTH ((0.45f * U.widget_unit) + (2.0f * U.pixelsize))
-/* Alpha of scrollbar when at minimum size. */
+/* Alpha of scroll-bar when at minimum size. */
#define V2D_SCROLL_MIN_ALPHA (0.4f)
/* Minimum size needs to include outline which varies with line width. */
@@ -84,7 +84,7 @@ enum eView2D_CommonViewTypes {
/* ------------------------------------------ */
/* Macros: */
-/* test if mouse in a scrollbar (assume that scroller availability has been tested) */
+/* Test if mouse in a scroll-bar (assume that scroller availability has been tested). */
#define IN_2D_VERT_SCROLL(v2d, co) (BLI_rcti_isect_pt_v(&v2d->vert, co))
#define IN_2D_HORIZ_SCROLL(v2d, co) (BLI_rcti_isect_pt_v(&v2d->hor, co))
@@ -309,6 +309,12 @@ float UI_view2d_view_to_region_y(const struct View2D *v2d, float y);
bool UI_view2d_view_to_region_clip(
const struct View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL();
+bool UI_view2d_view_to_region_segment_clip(const View2D *v2d,
+ const float xy_a[2],
+ const float xy_b[2],
+ int r_region_a[2],
+ int r_region_b[2]) ATTR_NONNULL();
+
/**
* Convert from 2d-view space to screen/region space
*
@@ -344,8 +350,8 @@ struct View2D *UI_view2d_fromcontext(const struct bContext *C);
struct View2D *UI_view2d_fromcontext_rwin(const struct bContext *C);
/**
- * Get scrollbar sizes of the current 2D view.
- * The size will be zero if the view has its scrollbars disabled.
+ * Get scroll-bar sizes of the current 2D view.
+ * The size will be zero if the view has its scroll-bars disabled.
*
* \param mapped: whether to use view2d_scroll_mapped which changes flags
*/
diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt
index 1bdec57ac59..6a531c88762 100644
--- a/source/blender/editors/interface/CMakeLists.txt
+++ b/source/blender/editors/interface/CMakeLists.txt
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
set(INC
+ .
../include
../../blenfont
../../blenkernel
@@ -18,37 +19,35 @@ set(INC
../../python
../../render
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
+ ../../bmesh
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
set(SRC
- abstract_view.cc
- grid_view.cc
+ eyedroppers/eyedropper_color.c
+ eyedroppers/eyedropper_colorband.c
+ eyedroppers/eyedropper_datablock.c
+ eyedroppers/eyedropper_depth.c
+ eyedroppers/eyedropper_driver.c
+ eyedroppers/eyedropper_gpencil_color.c
+ eyedroppers/interface_eyedropper.c
interface.cc
interface_align.c
- interface_anim.c
+ interface_anim.cc
interface_button_group.c
interface_context_menu.c
interface_context_path.cc
interface_drag.cc
interface_draw.c
interface_dropboxes.cc
- interface_eyedropper.c
- interface_eyedropper_color.c
- interface_eyedropper_colorband.c
- interface_eyedropper_datablock.c
- interface_eyedropper_depth.c
- interface_eyedropper_driver.c
- interface_eyedropper_gpencil_color.c
interface_handlers.c
interface_icons.c
interface_icons_event.c
interface_layout.c
- interface_ops.c
- interface_panel.c
+ interface_ops.cc
+ interface_panel.cc
interface_query.cc
interface_region_color_picker.cc
interface_region_hud.cc
@@ -57,30 +56,33 @@ set(SRC
interface_region_popover.cc
interface_region_popup.cc
interface_region_search.cc
- interface_region_tooltip.c
+ interface_region_tooltip.cc
interface_regions.cc
interface_style.cc
interface_template_asset_view.cc
interface_template_attribute_search.cc
interface_template_list.cc
interface_template_search_menu.cc
- interface_template_search_operator.c
+ interface_template_search_operator.cc
interface_templates.c
- interface_undo.c
+ interface_undo.cc
interface_utils.cc
- interface_view.cc
interface_widgets.c
resources.c
- tree_view.cc
view2d.cc
view2d_draw.cc
view2d_edge_pan.cc
view2d_gizmo_navigate.cc
view2d_ops.cc
+ views/abstract_view.cc
+ views/abstract_view_item.cc
+ views/grid_view.cc
+ views/interface_view.cc
+ views/tree_view.cc
- interface_eyedropper_intern.h
+ eyedroppers/eyedropper_intern.h
interface_intern.h
- interface_regions_intern.h
+ interface_regions_intern.hh
)
set(LIB
diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_color.c
index c015a60de89..9c430afd5f0 100644
--- a/source/blender/editors/interface/interface_eyedropper_color.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_color.c
@@ -50,7 +50,7 @@
#include "RE_pipeline.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
typedef struct Eyedropper {
struct ColorManagedDisplay *display;
@@ -479,7 +479,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
break;
}
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
if (eye->accum_start) {
/* button is pressed so keep sampling */
eyedropper_color_sample(C, eye, event->xy);
diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c
index a69c36fefbd..3f63a8020ed 100644
--- a/source/blender/editors/interface/interface_eyedropper_colorband.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_colorband.c
@@ -35,7 +35,7 @@
#include "interface_intern.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
typedef struct Colorband_RNAUpdateCb {
PointerRNA ptr;
diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c
index 01b958576b6..1710d0faa4d 100644
--- a/source/blender/editors/interface/interface_eyedropper_datablock.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_datablock.c
@@ -38,7 +38,7 @@
#include "ED_space_api.h"
#include "ED_view3d.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
#include "interface_intern.h"
/**
diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c
index 3c6f127582a..3fb5a74944b 100644
--- a/source/blender/editors/interface/interface_eyedropper_depth.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_depth.c
@@ -38,7 +38,7 @@
#include "ED_space_api.h"
#include "ED_view3d.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
#include "interface_intern.h"
/**
diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
index 0cacb55c60c..0f3062c3f61 100644
--- a/source/blender/editors/interface/interface_eyedropper_driver.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_driver.c
@@ -24,6 +24,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_path.h"
#include "UI_interface.h"
@@ -32,7 +33,7 @@
#include "ED_keyframing.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
#include "interface_intern.h"
typedef struct DriverDropper {
@@ -83,14 +84,14 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve
if (but == NULL) {
return;
}
- /* Get paths for src... */
+ /* Get paths for the source. */
PointerRNA *target_ptr = &but->rnapoin;
PropertyRNA *target_prop = but->rnaprop;
const int target_index = but->rnaindex;
char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
- /* ... and destination */
+ /* Get paths for the destination. */
char *dst_path = RNA_path_from_ID_to_property(&ddr->ptr, ddr->prop);
/* Now create driver(s) */
diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c
index f3c70e6a96a..c3879fe8bbd 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_gpencil_color.c
@@ -46,7 +46,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
-#include "interface_eyedropper_intern.h"
+#include "eyedropper_intern.h"
#include "interface_intern.h"
typedef struct EyedropperGPencil {
diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h
index 76316a85807..946f2145d1d 100644
--- a/source/blender/editors/interface/interface_eyedropper_intern.h
+++ b/source/blender/editors/interface/eyedroppers/eyedropper_intern.h
@@ -3,7 +3,7 @@
/** \file
* \ingroup edinterface
*
- * Share between interface_eyedropper_*.c files.
+ * Share between `interface/eyedropper/` files.
*/
#pragma once
@@ -35,7 +35,7 @@ void datadropper_win_area_find(const struct bContext *C,
*
* Special check for image or nodes where we MAY have HDR pixels which don't display.
*
- * \note Exposed by 'interface_eyedropper_intern.h' for use with color band picking.
+ * \note Exposed by 'eyedropper_intern.h' for use with color band picking.
*/
void eyedropper_color_sample_fl(bContext *C, const int m_xy[2], float r_col[3]);
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c
index eaec1e249b7..e49955512a1 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/eyedroppers/interface_eyedropper.c
@@ -21,7 +21,7 @@
#include "interface_intern.h"
-#include "interface_eyedropper_intern.h" /* own include */
+#include "eyedropper_intern.h" /* own include */
/* -------------------------------------------------------------------- */
/* Keymap
@@ -41,7 +41,7 @@ wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Eyedropper Modal Map");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return NULL;
}
diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc
index 3f623566807..64f7e035d3f 100644
--- a/source/blender/editors/interface/interface.cc
+++ b/source/blender/editors/interface/interface.cc
@@ -153,8 +153,8 @@ void ui_block_to_window(const ARegion *region, uiBlock *block, int *r_x, int *r_
ui_block_to_window_fl(region, block, &fx, &fy);
- *r_x = (int)(fx + 0.5f);
- *r_y = (int)(fy + 0.5f);
+ *r_x = (int)lround(fx);
+ *r_y = (int)lround(fy);
}
void ui_block_to_region_rctf(const ARegion *region,
@@ -232,8 +232,8 @@ void ui_window_to_block(const ARegion *region, uiBlock *block, int *r_x, int *r_
ui_window_to_block_fl(region, block, &fx, &fy);
- *r_x = (int)(fx + 0.5f);
- *r_y = (int)(fy + 0.5f);
+ *r_x = (int)lround(fx);
+ *r_y = (int)lround(fy);
}
void ui_window_to_region(const ARegion *region, int *r_x, int *r_y)
@@ -769,20 +769,11 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut)
return false;
}
- if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) {
- uiButTreeRow *but_treerow = (uiButTreeRow *)but;
- uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut;
- if (!but_treerow->tree_item || !oldbut_treerow->tree_item ||
- !UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) {
- return false;
- }
- }
-
- if ((but->type == UI_BTYPE_GRID_TILE) && (oldbut->type == UI_BTYPE_GRID_TILE)) {
- uiButGridTile *but_gridtile = (uiButGridTile *)but;
- uiButGridTile *oldbut_gridtile = (uiButGridTile *)oldbut;
- if (!but_gridtile->view_item || !oldbut_gridtile->view_item ||
- !UI_grid_view_item_matches(but_gridtile->view_item, oldbut_gridtile->view_item)) {
+ if ((but->type == UI_BTYPE_VIEW_ITEM) && (oldbut->type == UI_BTYPE_VIEW_ITEM)) {
+ uiButViewItem *but_item = (uiButViewItem *)but;
+ uiButViewItem *oldbut_item = (uiButViewItem *)oldbut;
+ if (!but_item->view_item || !oldbut_item->view_item ||
+ !UI_view_item_matches(but_item->view_item, oldbut_item->view_item)) {
return false;
}
}
@@ -907,16 +898,10 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but)
progress_oldbut->progress = progress_but->progress;
break;
}
- case UI_BTYPE_TREEROW: {
- uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut;
- uiButTreeRow *treerow_newbut = (uiButTreeRow *)but;
- SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item);
- break;
- }
- case UI_BTYPE_GRID_TILE: {
- uiButGridTile *gridtile_oldbut = (uiButGridTile *)oldbut;
- uiButGridTile *gridtile_newbut = (uiButGridTile *)but;
- SWAP(uiGridViewItemHandle *, gridtile_newbut->view_item, gridtile_oldbut->view_item);
+ case UI_BTYPE_VIEW_ITEM: {
+ uiButViewItem *view_item_oldbut = (uiButViewItem *)oldbut;
+ uiButViewItem *view_item_newbut = (uiButViewItem *)but;
+ SWAP(uiViewItemHandle *, view_item_newbut->view_item, view_item_oldbut->view_item);
break;
}
default:
@@ -1013,7 +998,7 @@ static bool ui_but_update_from_old_block(const bContext *C,
/* Stupid special case: The active button may be inside (as in, overlapped on top) a view-item
* button which we also want to keep highlighted then. */
- if (ui_but_is_view_item(but)) {
+ if (but->type == UI_BTYPE_VIEW_ITEM) {
flag_copy |= UI_ACTIVE;
}
@@ -1325,7 +1310,7 @@ static bool ui_but_event_operator_string_from_panel(const bContext *C,
IDP_AddToGroup(prop_panel, IDP_New(IDP_INT, &region_type_val, "region_type"));
for (int i = 0; i < 2; i++) {
- /* FIXME(campbell): We can't reasonably search all configurations - long term. */
+ /* FIXME(@campbellbarton): We can't reasonably search all configurations - long term. */
IDPropertyTemplate val = {0};
val.i = i;
@@ -2245,21 +2230,12 @@ int ui_but_is_pushed_ex(uiBut *but, double *value)
}
}
break;
- case UI_BTYPE_TREEROW: {
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
+ case UI_BTYPE_VIEW_ITEM: {
+ const uiButViewItem *view_item_but = (const uiButViewItem *)but;
is_push = -1;
- if (tree_row_but->tree_item) {
- is_push = UI_tree_view_item_is_active(tree_row_but->tree_item);
- }
- break;
- }
- case UI_BTYPE_GRID_TILE: {
- uiButGridTile *grid_tile_but = (uiButGridTile *)but;
-
- is_push = -1;
- if (grid_tile_but->view_item) {
- is_push = UI_grid_view_item_is_active(grid_tile_but->view_item);
+ if (view_item_but->view_item) {
+ is_push = UI_view_item_is_active(view_item_but->view_item);
}
break;
}
@@ -2387,9 +2363,9 @@ void ui_but_v3_set(uiBut *but, const float vec[3])
}
else if (but->pointype == UI_BUT_POIN_CHAR) {
char *cp = (char *)but->poin;
- cp[0] = (char)(0.5f + vec[0] * 255.0f);
- cp[1] = (char)(0.5f + vec[1] * 255.0f);
- cp[2] = (char)(0.5f + vec[2] * 255.0f);
+ cp[0] = (char)lround(vec[0] * 255.0f);
+ cp[1] = (char)lround(vec[1] * 255.0f);
+ cp[2] = (char)lround(vec[2] * 255.0f);
}
else if (but->pointype == UI_BUT_POIN_FLOAT) {
float *fp = (float *)but->poin;
@@ -4011,17 +3987,13 @@ static void ui_but_alloc_info(const eButType type,
alloc_size = sizeof(uiButCurveProfile);
alloc_str = "uiButCurveProfile";
break;
- case UI_BTYPE_TREEROW:
- alloc_size = sizeof(uiButTreeRow);
- alloc_str = "uiButTreeRow";
- break;
case UI_BTYPE_HOTKEY_EVENT:
alloc_size = sizeof(uiButHotkeyEvent);
alloc_str = "uiButHotkeyEvent";
break;
- case UI_BTYPE_GRID_TILE:
- alloc_size = sizeof(uiButGridTile);
- alloc_str = "uiButGridTile";
+ case UI_BTYPE_VIEW_ITEM:
+ alloc_size = sizeof(uiButViewItem);
+ alloc_str = "uiButViewItem";
break;
default:
alloc_size = sizeof(uiBut);
@@ -4214,7 +4186,6 @@ static uiBut *ui_def_but(uiBlock *block,
UI_BTYPE_BLOCK,
UI_BTYPE_BUT_MENU,
UI_BTYPE_SEARCH_MENU,
- UI_BTYPE_TREEROW,
UI_BTYPE_POPOVER)) {
but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT);
}
@@ -6350,6 +6321,13 @@ void UI_but_func_search_set_tooltip(uiBut *but, uiButSearchTooltipFn tooltip_fn)
but_search->item_tooltip_fn = tooltip_fn;
}
+void UI_but_func_search_set_listen(uiBut *but, uiButSearchListenFn listen_fn)
+{
+ uiButSearch *but_search = (uiButSearch *)but;
+ BLI_assert(but->type == UI_BTYPE_SEARCH_MENU);
+ but_search->listen_fn = listen_fn;
+}
+
void UI_but_func_search_set_results_are_suggestions(uiBut *but, const bool value)
{
uiButSearch *but_search = (uiButSearch *)but;
@@ -6469,15 +6447,6 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block,
return but;
}
-void UI_but_treerow_indentation_set(uiBut *but, int indentation)
-{
- uiButTreeRow *but_row = (uiButTreeRow *)but;
- BLI_assert(but->type == UI_BTYPE_TREEROW);
-
- but_row->indentation = indentation;
- BLI_assert(indentation >= 0);
-}
-
void UI_but_hint_drawstr_set(uiBut *but, const char *string)
{
ui_but_add_shortcut(but, string, false);
@@ -6623,7 +6592,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
MenuType *mt = UI_but_menutype_get(but);
if (mt) {
if (type == BUT_GET_RNA_LABEL) {
- tmp = BLI_strdup(mt->label);
+ tmp = BLI_strdup(CTX_TIP_(mt->translation_context, mt->label));
}
else {
/* Not all menus are from Python. */
@@ -6653,7 +6622,7 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
PanelType *pt = UI_but_paneltype_get(but);
if (pt) {
if (type == BUT_GET_RNA_LABEL) {
- tmp = BLI_strdup(pt->label);
+ tmp = BLI_strdup(CTX_TIP_(pt->translation_context, pt->label));
}
else {
/* Not all panels are from Python. */
@@ -6792,10 +6761,11 @@ void UI_but_extra_icon_string_info_get(struct bContext *C, uiButExtraOpIcon *ext
if (ui_but_extra_icon_event_operator_string(C, extra_icon, buf, sizeof(buf))) {
tmp = BLI_strdup(buf);
}
+ break;
}
+ default:
/* Other types not supported. The caller should expect that outcome, no need to message or
* assert here. */
- default:
break;
}
diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.cc
index 0e69b4bb358..4da6cefd8de 100644
--- a/source/blender/editors/interface/interface_anim.c
+++ b/source/blender/editors/interface/interface_anim.cc
@@ -4,9 +4,9 @@
* \ingroup edinterface
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -35,6 +35,7 @@
#include "UI_interface.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -48,8 +49,14 @@ static FCurve *ui_but_get_fcurve(
* but works well enough in typical cases */
const int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex;
- return BKE_fcurve_find_by_rna_context_ui(
- but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, adt, action, r_driven, r_special);
+ return BKE_fcurve_find_by_rna_context_ui(static_cast<bContext *>(but->block->evil_C),
+ &but->rnapoin,
+ but->rnaprop,
+ rnaindex,
+ adt,
+ action,
+ r_driven,
+ r_special);
}
void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
@@ -92,7 +99,7 @@ void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
}
/* XXX: this feature is totally broken and useless with NLA */
- if (adt == NULL || adt->nla_tracks.first == NULL) {
+ if (adt == nullptr || adt->nla_tracks.first == nullptr) {
const AnimationEvalContext remapped_context = BKE_animsys_eval_context_construct_at(
anim_eval_context, cfra);
if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, &remapped_context)) {
@@ -108,13 +115,13 @@ void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context)
static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but_decorate)
{
- uiBut *but_iter = NULL;
+ uiBut *but_iter = nullptr;
BLI_assert(UI_but_is_decorator(&but_decorate->but));
BLI_assert(but_decorate->rnapoin.data && but_decorate->rnaprop);
LISTBASE_CIRCULAR_BACKWARD_BEGIN (
- &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev) {
+ uiBut *, &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev) {
if (but_iter != (uiBut *)but_decorate &&
ui_but_rna_equals_ex(
but_iter, &but_decorate->rnapoin, but_decorate->rnaprop, but_decorate->rnaindex)) {
@@ -122,9 +129,9 @@ static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but_deco
}
}
LISTBASE_CIRCULAR_BACKWARD_END(
- &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev);
+ uiBut *, &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev);
- return NULL;
+ return nullptr;
}
void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but)
@@ -172,7 +179,7 @@ bool ui_but_anim_expression_get(uiBut *but, char *str, size_t maxlen)
ChannelDriver *driver;
bool driven, special;
- fcu = ui_but_get_fcurve(but, NULL, NULL, &driven, &special);
+ fcu = ui_but_get_fcurve(but, nullptr, nullptr, &driven, &special);
if (fcu && driven) {
driver = fcu->driver;
@@ -194,13 +201,13 @@ bool ui_but_anim_expression_set(uiBut *but, const char *str)
ChannelDriver *driver;
bool driven, special;
- fcu = ui_but_get_fcurve(but, NULL, NULL, &driven, &special);
+ fcu = ui_but_get_fcurve(but, nullptr, nullptr, &driven, &special);
if (fcu && driven) {
driver = fcu->driver;
if (driver && (driver->type == DRIVER_TYPE_PYTHON)) {
- bContext *C = but->block->evil_C;
+ bContext *C = static_cast<bContext *>(but->block->evil_C);
BLI_strncpy_utf8(driver->expression, str, sizeof(driver->expression));
@@ -212,7 +219,7 @@ bool ui_but_anim_expression_set(uiBut *but, const char *str)
fcu->flag &= ~FCURVE_DISABLED;
/* this notifier should update the Graph Editor and trigger depsgraph refresh? */
- WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL);
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, nullptr);
DEG_relations_tag_update(CTX_data_main(C));
@@ -225,14 +232,14 @@ bool ui_but_anim_expression_set(uiBut *but, const char *str)
bool ui_but_anim_expression_create(uiBut *but, const char *str)
{
- bContext *C = but->block->evil_C;
+ bContext *C = static_cast<bContext *>(but->block->evil_C);
ID *id;
FCurve *fcu;
char *path;
bool ok = false;
/* button must have RNA-pointer to a numeric-capable property */
- if (ELEM(NULL, but->rnapoin.data, but->rnaprop)) {
+ if (ELEM(nullptr, but->rnapoin.data, but->rnaprop)) {
if (G.debug & G_DEBUG) {
printf("ERROR: create expression failed - button has no RNA info attached\n");
}
@@ -252,7 +259,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str)
/* FIXME: until materials can be handled by depsgraph,
* don't allow drivers to be created for them */
id = but->rnapoin.owner_id;
- if ((id == NULL) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) {
+ if ((id == nullptr) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) {
if (G.debug & G_DEBUG) {
printf("ERROR: create expression failed - invalid data-block for adding drivers (%p)\n", id);
}
@@ -261,7 +268,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str)
/* get path */
path = RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop);
- if (path == NULL) {
+ if (path == nullptr) {
return false;
}
@@ -281,7 +288,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str)
/* updates */
BKE_driver_invalidate_expression(driver, true, false);
DEG_relations_tag_update(CTX_data_main(C));
- WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL);
+ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, nullptr);
ok = true;
}
}
@@ -299,26 +306,26 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra)
void ui_but_anim_copy_driver(bContext *C)
{
/* this operator calls UI_context_active_but_prop_get */
- WM_operator_name_call(C, "ANIM_OT_copy_driver_button", WM_OP_INVOKE_DEFAULT, NULL, NULL);
+ WM_operator_name_call(C, "ANIM_OT_copy_driver_button", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
}
void ui_but_anim_paste_driver(bContext *C)
{
/* this operator calls UI_context_active_but_prop_get */
- WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, NULL, NULL);
+ WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
}
void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy))
{
wmWindowManager *wm = CTX_wm_manager(C);
- uiButDecorator *but_decorate = arg_but;
+ uiButDecorator *but_decorate = static_cast<uiButDecorator *>(arg_but);
uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but_decorate);
if (!but_anim) {
return;
}
- /* FIXME(campbell), swapping active pointer is weak. */
+ /* FIXME(@campbellbarton): swapping active pointer is weak. */
SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->but.active);
wm->op_undo_depth++;
@@ -331,7 +338,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)
wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_delete_button", false);
WM_operator_properties_create_ptr(&props_ptr, ot);
RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
WM_operator_properties_free(&props_ptr);
}
else {
@@ -339,7 +346,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)
wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_insert_button", false);
WM_operator_properties_create_ptr(&props_ptr, ot);
RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1);
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
WM_operator_properties_free(&props_ptr);
}
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index e58298cdaee..7ed9488950e 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -927,11 +927,11 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
{
const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C);
- uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, event->xy);
- if (treerow_but) {
- BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW);
- UI_tree_view_item_context_menu_build(
- C, treerow_but->tree_item, uiLayoutColumn(layout, false));
+ uiButViewItem *view_item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region,
+ event->xy);
+ if (view_item_but) {
+ BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM);
+ UI_view_item_context_menu_build(C, view_item_but->view_item, uiLayoutColumn(layout, false));
uiItemS(layout);
}
}
@@ -952,6 +952,12 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
uiItemS(layout);
}
+ MenuType *mt_idtemplate_liboverride = WM_menutype_find("UI_MT_idtemplate_liboverride", true);
+ if (mt_idtemplate_liboverride && mt_idtemplate_liboverride->poll(C, mt_idtemplate_liboverride)) {
+ uiItemM_ptr(layout, mt_idtemplate_liboverride, IFACE_("Library Override"), ICON_NONE);
+ uiItemS(layout);
+ }
+
/* Pointer properties and string properties with
* prop_search support jumping to target object/bone. */
if (but->rnapoin.data && but->rnaprop) {
@@ -1224,7 +1230,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev
}
}
- MenuType *mt = WM_menutype_find("WM_MT_button_context", true);
+ MenuType *mt = WM_menutype_find("UI_MT_button_context_menu", true);
if (mt) {
UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
}
diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc
index 4c68870b2c7..4bf2dac4151 100644
--- a/source/blender/editors/interface/interface_drag.cc
+++ b/source/blender/editors/interface/interface_drag.cc
@@ -37,7 +37,7 @@ void UI_but_drag_set_asset(uiBut *but,
{
wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type);
- /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the
+ /* FIXME: This is temporary evil solution to get scene/view-layer/etc in the copy callback of the
* #wmDropBox.
* TODO: Handle link/append in operator called at the end of the drop process, and NOT in its
* copy callback.
@@ -122,7 +122,7 @@ bool ui_but_drag_is_draggable(const uiBut *but)
void ui_but_drag_start(bContext *C, uiBut *but)
{
- wmDrag *drag = WM_event_start_drag(C,
+ wmDrag *drag = WM_drag_data_create(C,
but->icon,
but->dragtype,
but->dragpoin,
@@ -130,15 +130,17 @@ void ui_but_drag_start(bContext *C, uiBut *but)
(but->dragflag & UI_BUT_DRAGPOIN_FREE) ? WM_DRAG_FREE_DATA :
WM_DRAG_NOP);
/* wmDrag has ownership over dragpoin now, stop messing with it. */
- but->dragpoin = NULL;
+ but->dragpoin = nullptr;
if (but->imb) {
WM_event_drag_image(drag, but->imb, but->imb_scale);
}
+ WM_event_start_prepared_drag(C, drag);
+
/* Special feature for assets: We add another drag item that supports multiple assets. It
* gets the assets from context. */
if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) {
- WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP);
+ WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, nullptr, 0, WM_DRAG_NOP);
}
}
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index d201820fbb6..f1a324c411a 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -171,7 +171,7 @@ void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const flo
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
immRecti(pos, pos_x, pos_y - ofs_y, pos_x + len, pos_y - ofs_y + (height * U.pixelsize));
@@ -205,7 +205,7 @@ void ui_draw_but_TAB_outline(const rcti *rect,
mul_v2_fl(vec[a], rad);
}
- immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
immBeginAtMost(GPU_PRIM_LINE_STRIP, 25);
immAttr3ubv(col, highlight);
@@ -309,7 +309,7 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region),
rgba_uchar_to_float(col, but->col);
}
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_3D_IMAGE_COLOR);
immDrawPixelsTexTiled(&state,
(float)rect->xmin,
(float)rect->ymin,
@@ -490,7 +490,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region),
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.08f);
/* draw grid lines here */
@@ -559,7 +559,7 @@ static void waveform_draw_one(float *waveform, int waveform_num, const float col
/* TODO: store the #GPUBatch inside the scope. */
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vbo, NULL, GPU_BATCH_OWNS_VBO);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
GPU_batch_uniform_4f(batch, "color", col[0], col[1], col[2], 1.0f);
GPU_batch_draw(batch);
@@ -653,7 +653,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region),
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.08f);
@@ -977,7 +977,7 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region),
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.08f);
/* draw grid elements */
@@ -1128,7 +1128,7 @@ static void ui_draw_colorband_handle(uint shdr_pos,
if (active || half_width < min_width) {
immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -1147,7 +1147,7 @@ static void ui_draw_colorband_handle(uint shdr_pos,
immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* hide handles when zoomed out too far */
if (half_width < min_width) {
@@ -1242,7 +1242,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
format = immVertexFormat();
pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
/* layer: color ramp */
GPU_blend(GPU_BLEND_ALPHA);
@@ -1298,7 +1298,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const
/* New format */
format = immVertexFormat();
pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* layer: box outline */
immUniformColor4f(0.0f, 0.0f, 0.0f, 1.0f);
@@ -1400,7 +1400,7 @@ void ui_draw_but_UNITVEC(uiBut *but,
/* AA circle */
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ubv(wcol->inner);
GPU_blend(GPU_BLEND_ALPHA);
@@ -1534,7 +1534,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* backdrop */
float color_backdrop[4] = {0, 0, 0, 1};
@@ -1657,7 +1657,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
line_range.ymax = rect->ymin + zoomy * (cmp[CM_TABLE].y - offsy - cuma->ext_out[1]);
}
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
/* Curve filled. */
@@ -1698,7 +1698,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
/* Calculate vertex colors based on text theme. */
float color_vert[4], color_vert_select[4];
@@ -1730,7 +1730,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol,
/* outline */
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ubv(wcol->outline);
imm_draw_box_wire_2d(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
@@ -1790,7 +1790,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw the backdrop. */
float color_backdrop[4] = {0, 0, 0, 1};
@@ -1948,7 +1948,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
/* Calculate vertex colors based on text theme. */
float color_vert[4], color_vert_select[4], color_sample[4];
@@ -2025,7 +2025,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region,
/* Outline */
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ubv((const uchar *)wcol->outline);
imm_draw_box_wire_2d(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
@@ -2132,7 +2132,7 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region),
color);
}
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_3D_IMAGE_COLOR);
immDrawPixelsTexTiled(&state,
rect.xmin,
rect.ymin + 1,
@@ -2152,7 +2152,7 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region),
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
UI_GetThemeColor4fv(TH_SEL_MARKER, col_sel);
UI_GetThemeColor4fv(TH_MARKER_OUTLINE, col_outline);
@@ -2288,7 +2288,7 @@ void UI_draw_box_shadow(const rctf *rect, uchar alpha)
uint color = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
immBegin(GPU_PRIM_TRIS, 54);
@@ -2307,9 +2307,6 @@ void UI_draw_box_shadow(const rctf *rect, uchar alpha)
void ui_draw_dropshadow(
const rctf *rct, float radius, float aspect, float alpha, int UNUSED(select))
{
- const float max_radius = (BLI_rctf_size_y(rct) - 10.0f) * 0.5f;
- const float rad = min_ff(radius, max_radius);
-
/* This undoes the scale of the view for higher zoom factors to clamp the shadow size. */
const float clamped_aspect = smoothminf(aspect, 1.0f, 0.5f);
@@ -2317,6 +2314,9 @@ void ui_draw_dropshadow(
const float shadow_offset = 0.5f * U.widget_unit * clamped_aspect;
const float shadow_alpha = 0.5f * alpha;
+ const float max_radius = (BLI_rctf_size_y(rct) - shadow_offset) * 0.5f;
+ const float rad = min_ff(radius, max_radius);
+
GPU_blend(GPU_BLEND_ALPHA);
uiWidgetBaseParameters widget_params = {
diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc
index 9d3c1372b15..b72d8d2c238 100644
--- a/source/blender/editors/interface/interface_dropboxes.cc
+++ b/source/blender/editors/interface/interface_dropboxes.cc
@@ -22,15 +22,14 @@
#include "UI_interface.h"
/* -------------------------------------------------------------------- */
-/** \name Tree View Drag/Drop Callbacks
+/** \name View Drag/Drop Callbacks
* \{ */
-static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
+static bool ui_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
{
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region,
- event->xy);
- if (!hovered_tree_item) {
+ const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy);
+ if (!hovered_item) {
return false;
}
@@ -39,21 +38,21 @@ static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *eve
}
drag->drop_state.free_disabled_info = false;
- return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->drop_state.disabled_info);
+ return UI_view_item_can_drop(hovered_item, drag, &drag->drop_state.disabled_info);
}
-static char *ui_tree_view_drop_tooltip(bContext *C,
- wmDrag *drag,
- const int xy[2],
- wmDropBox *UNUSED(drop))
+static char *ui_view_drop_tooltip(bContext *C,
+ wmDrag *drag,
+ const int xy[2],
+ wmDropBox *UNUSED(drop))
{
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy);
- if (!hovered_tree_item) {
+ const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, xy);
+ if (!hovered_item) {
return nullptr;
}
- return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag);
+ return UI_view_item_drop_tooltip(hovered_item, drag);
}
/** \} */
@@ -140,12 +139,7 @@ void ED_dropboxes_ui()
{
ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0);
- WM_dropbox_add(lb,
- "UI_OT_tree_view_drop",
- ui_tree_view_drop_poll,
- nullptr,
- nullptr,
- ui_tree_view_drop_tooltip);
+ WM_dropbox_add(lb, "UI_OT_view_drop", ui_view_drop_poll, nullptr, nullptr, ui_view_drop_tooltip);
WM_dropbox_add(lb,
"UI_OT_drop_name",
ui_drop_name_poll,
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index 7c00c4f1875..ea1770db6f5 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -959,7 +959,7 @@ static void ui_apply_but_undo(uiBut *but)
str = "";
}
- /* delayed, after all other funcs run, popups are closed, etc */
+ /* Delayed, after all other functions run, popups are closed, etc. */
uiAfterFunc *after = ui_afterfunc_new();
BLI_strncpy(after->undostr, str, min_zz(str_len_clip + 1, sizeof(after->undostr)));
}
@@ -991,7 +991,7 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but)
static void ui_apply_but_funcs_after(bContext *C)
{
- /* copy to avoid recursive calls */
+ /* Copy to avoid recursive calls. */
ListBase funcs = UIAfterFuncs;
BLI_listbase_clear(&UIAfterFuncs);
@@ -1151,7 +1151,10 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu
data->applied = true;
}
-static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data)
+static void ui_apply_but_VIEW_ITEM(bContext *C,
+ uiBlock *block,
+ uiBut *but,
+ uiHandleButtonData *data)
{
if (data->apply_through_extra_icon) {
/* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons.
@@ -2128,10 +2131,10 @@ static bool ui_but_drag_init(bContext *C,
return false;
}
}
- else if (but->type == UI_BTYPE_TREEROW) {
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
- if (tree_row_but->tree_item) {
- UI_tree_view_item_drag_start(C, tree_row_but->tree_item);
+ else if (but->type == UI_BTYPE_VIEW_ITEM) {
+ const uiButViewItem *view_item_but = (uiButViewItem *)but;
+ if (view_item_but->view_item) {
+ UI_view_item_drag_start(C, view_item_but->view_item);
}
}
else {
@@ -2289,11 +2292,8 @@ static void ui_apply_but(
case UI_BTYPE_ROW:
ui_apply_but_ROW(C, block, but, data);
break;
- case UI_BTYPE_GRID_TILE:
- ui_apply_but_ROW(C, block, but, data);
- break;
- case UI_BTYPE_TREEROW:
- ui_apply_but_TREEROW(C, block, but, data);
+ case UI_BTYPE_VIEW_ITEM:
+ ui_apply_but_VIEW_ITEM(C, block, but, data);
break;
case UI_BTYPE_LISTROW:
ui_apply_but_LISTROW(C, block, but, data);
@@ -3172,19 +3172,11 @@ static bool ui_textedit_insert_buf(uiBut *but,
return changed;
}
-static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, char ascii)
+static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii)
{
+ BLI_assert(isascii(ascii));
const char buf[2] = {ascii, '\0'};
-
- if (UI_but_is_utf8(but) && (BLI_str_utf8_size(buf) == -1)) {
- printf(
- "%s: entering invalid ascii char into an ascii key (%d)\n", __func__, (int)(uchar)ascii);
-
- return false;
- }
-
- /* in some cases we want to allow invalid utf8 chars */
- return ui_textedit_insert_buf(but, data, buf, 1);
+ return ui_textedit_insert_buf(but, data, buf, sizeof(buf) - 1);
}
static void ui_textedit_move(uiBut *but,
@@ -3897,30 +3889,27 @@ static void ui_do_but_textedit(
}
}
- if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)
+ if ((event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)
#ifdef WITH_INPUT_IME
&& !is_ime_composing && !WM_event_is_ime_switch(event)
#endif
) {
- char ascii = event->ascii;
+ char utf8_buf_override[2] = {'\0', '\0'};
const char *utf8_buf = event->utf8_buf;
/* Exception that's useful for number buttons, some keyboard
* numpads have a comma instead of a period. */
if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { /* Could use `data->min`. */
- if (event->type == EVT_PADPERIOD && ascii == ',') {
- ascii = '.';
- utf8_buf = NULL; /* force ascii fallback */
+ if ((event->type == EVT_PADPERIOD) && (utf8_buf[0] == ',')) {
+ utf8_buf_override[0] = '.';
+ utf8_buf = utf8_buf_override;
}
}
- if (utf8_buf && utf8_buf[0]) {
+ if (utf8_buf[0]) {
const int utf8_buf_len = BLI_str_utf8_size(utf8_buf);
BLI_assert(utf8_buf_len != -1);
- changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len);
- }
- else {
- changed = ui_textedit_insert_ascii(but, data, ascii);
+ changed = ui_textedit_insert_buf(but, data, utf8_buf, utf8_buf_len);
}
retval = WM_UI_HANDLER_BREAK;
@@ -3952,6 +3941,9 @@ static void ui_do_but_textedit(
else if (event->type == WM_IME_COMPOSITE_END) {
changed = true;
}
+#else
+ /* Prevent the function from being unused. */
+ (void)ui_textedit_insert_ascii;
#endif
if (changed) {
@@ -4352,15 +4344,18 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but,
ARegion *region,
const wmEvent *event)
{
- float xmax = but->rect.xmax;
- const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */
- int x = event->xy[0], y = event->xy[1];
+ if (BLI_listbase_is_empty(&but->extra_op_icons)) {
+ return NULL;
+ }
+ int x = event->xy[0], y = event->xy[1];
ui_window_to_block(region, but->block, &x, &y);
if (!BLI_rctf_isect_pt(&but->rect, x, y)) {
return NULL;
}
+ const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */
+ float xmax = but->rect.xmax;
/* Same as in 'widget_draw_extra_icons', icon padding from the right edge. */
xmax -= 0.2 * icon_size;
@@ -4517,7 +4512,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C,
}
}
else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
if (event->type == EVT_UNKNOWNKEY) {
@@ -4583,7 +4578,7 @@ static int ui_do_but_KEYEVT(bContext *C,
}
}
else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
@@ -4772,53 +4767,13 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_TREEROW(bContext *C,
- uiBut *but,
- uiHandleButtonData *data,
- const wmEvent *event)
-{
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
- BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW);
-
- if (data->state == BUTTON_STATE_HIGHLIGHT) {
- if (event->type == LEFTMOUSE) {
- switch (event->val) {
- case KM_PRESS:
- /* Extra icons have priority, don't mess with them. */
- if (ui_but_extra_operator_icon_mouse_over_get(but, data->region, event)) {
- return WM_UI_HANDLER_BREAK;
- }
- button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG);
- data->dragstartx = event->xy[0];
- data->dragstarty = event->xy[1];
- return WM_UI_HANDLER_CONTINUE;
-
- case KM_CLICK:
- button_activate_state(C, but, BUTTON_STATE_EXIT);
- return WM_UI_HANDLER_BREAK;
-
- case KM_DBL_CLICK:
- data->cancel = true;
- UI_tree_view_item_begin_rename(tree_row_but->tree_item);
- ED_region_tag_redraw(CTX_wm_region(C));
- return WM_UI_HANDLER_BREAK;
- }
- }
- }
- else if (data->state == BUTTON_STATE_WAIT_DRAG) {
- /* Let "default" button handling take care of the drag logic. */
- return ui_do_but_EXIT(C, but, data, event);
- }
-
- return WM_UI_HANDLER_CONTINUE;
-}
-
-static int ui_do_but_GRIDTILE(bContext *C,
- uiBut *but,
- uiHandleButtonData *data,
- const wmEvent *event)
+static int ui_do_but_VIEW_ITEM(bContext *C,
+ uiBut *but,
+ uiHandleButtonData *data,
+ const wmEvent *event)
{
- BLI_assert(but->type == UI_BTYPE_GRID_TILE);
+ uiButViewItem *view_item_but = (uiButViewItem *)but;
+ BLI_assert(view_item_but->but.type == UI_BTYPE_VIEW_ITEM);
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (event->type == LEFTMOUSE) {
@@ -4839,8 +4794,7 @@ static int ui_do_but_GRIDTILE(bContext *C,
case KM_DBL_CLICK:
data->cancel = true;
- // uiButGridTile *grid_tile_but = (uiButGridTile *)but;
- // UI_tree_view_item_begin_rename(grid_tile_but->tree_item);
+ UI_view_item_begin_rename(view_item_but->view_item);
ED_region_tag_redraw(CTX_wm_region(C));
return WM_UI_HANDLER_BREAK;
}
@@ -7988,7 +7942,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
* to spawn the context menu should also activate the item. This makes it clear which item
* will be operated on.
* Apply the button immediately, so context menu polls get the right active item. */
- if (ELEM(but->type, UI_BTYPE_TREEROW)) {
+ if (ELEM(but->type, UI_BTYPE_VIEW_ITEM)) {
ui_apply_but(C, but->block, but, but->active, true);
}
@@ -8053,11 +8007,8 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
case UI_BTYPE_ROW:
retval = ui_do_but_TOG(C, but, data, event);
break;
- case UI_BTYPE_GRID_TILE:
- retval = ui_do_but_GRIDTILE(C, but, data, event);
- break;
- case UI_BTYPE_TREEROW:
- retval = ui_do_but_TREEROW(C, but, data, event);
+ case UI_BTYPE_VIEW_ITEM:
+ retval = ui_do_but_VIEW_ITEM(C, but, data, event);
break;
case UI_BTYPE_SCROLL:
retval = ui_do_but_SCROLL(C, block, but, data, event);
@@ -8149,7 +8100,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *
#ifdef USE_DRAG_MULTINUM
data = but->active;
if (data) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) ||
+ if (ISMOUSE_MOTION(event->type) ||
/* if we started dragging, progress on any event */
(data->multi_data.init == BUTTON_MULTI_INIT_SETUP)) {
if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER) &&
@@ -8862,7 +8813,7 @@ void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo)
{
uiBut *activebut = ui_context_rna_button_active(C);
if (activebut) {
- /* TODO(campbell): look into a better way to handle the button change
+ /* TODO(@campbellbarton): look into a better way to handle the button change
* currently this is mainly so reset defaults works for the
* operator redo panel. */
uiBlock *block = activebut->block;
@@ -9487,7 +9438,7 @@ static int ui_list_activate_hovered_row(bContext *C,
}
/* Simulate click on listrow button itself (which may be overlapped by another button). Also
- * calls the custom activate operator (ui_list->custom_activate_opname). */
+ * calls the custom activate operator (#uiListDyn::custom_activate_optype). */
UI_but_execute(C, region, listrow);
((uiList *)ui_list)->dyn_data->custom_activate_optype = custom_activate_optype;
@@ -9558,13 +9509,13 @@ static void ui_list_activate_row_from_index(
uiBut *new_active_row = ui_list_row_find_from_index(region, index, listbox);
if (new_active_row) {
/* Preferred way to update the active item, also calls the custom activate operator
- * (#uiList.custom_activate_opname). */
+ * (#uiListDyn::custom_activate_optype). */
UI_but_execute(C, region, new_active_row);
}
else {
/* A bit ugly, set the active index in RNA directly. That's because a button that's
* scrolled away in the list box isn't created at all.
- * The custom activate operator (#uiList.custom_activate_opname) is not called in this case
+ * The custom activate operator (#uiListDyn::custom_activate_optype) is not called in this case
* (which may need the row button context). */
RNA_property_int_set(&listbox->rnapoin, listbox->rnaprop, index);
RNA_property_update(C, &listbox->rnapoin, listbox->rnaprop);
@@ -9733,7 +9684,7 @@ static int ui_handle_view_items_hover(const wmEvent *event, const ARegion *regio
}
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
- if (ui_but_is_view_item(but)) {
+ if (but->type == UI_BTYPE_VIEW_ITEM) {
but->flag &= ~UI_ACTIVE;
has_view_item = true;
}
@@ -9760,7 +9711,7 @@ static int ui_handle_view_item_event(bContext *C,
ARegion *region,
uiBut *view_but)
{
- BLI_assert(ui_but_is_view_item(view_but));
+ BLI_assert(view_but->type == UI_BTYPE_VIEW_ITEM);
if (event->type == LEFTMOUSE) {
/* Will free active button if there already is one. */
ui_handle_button_activate(C, region, view_but, BUTTON_ACTIVATE_OVER);
@@ -10148,8 +10099,7 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock
/* Pass, needed to click-exit outside of non-floating menus. */
ui_region_auto_open_clear(but->active->region);
}
- else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) &&
- ISMOUSE(event->type)) {
+ else if (ISMOUSE_BUTTON(event->type)) {
if (!ui_but_contains_point_px(but, but->active->region, event->xy)) {
but = NULL;
}
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index c19e842aad8..ad2c08194aa 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -360,7 +360,7 @@ static void vicon_colorset_draw(int index, int x, int y, int w, int h, float UNU
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* XXX: Include alpha into this... */
/* normal */
@@ -505,7 +505,7 @@ static void vicon_gplayer_color_draw(Icon *icon, int x, int y, int w, int h)
*/
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fv(gpl->color);
immRecti(pos, x, y, x + w - 1, y + h - 1);
@@ -1546,7 +1546,7 @@ static void icon_draw_rect(float x,
shader = GPU_SHADER_2D_IMAGE_DESATURATE_COLOR;
}
else {
- shader = GPU_SHADER_2D_IMAGE_COLOR;
+ shader = GPU_SHADER_3D_IMAGE_COLOR;
}
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(shader);
@@ -1824,7 +1824,7 @@ static void icon_draw_size(float x,
}
else if (di->type == ICON_TYPE_GEOM) {
#ifdef USE_UI_TOOLBAR_HACK
- /* TODO(campbell): scale icons up for toolbar,
+ /* TODO(@campbellbarton): scale icons up for toolbar,
* we need a way to detect larger buttons and do this automatic. */
{
float scale = (float)ICON_DEFAULT_HEIGHT_TOOLBAR / (float)ICON_DEFAULT_HEIGHT;
@@ -1839,7 +1839,7 @@ static void icon_draw_size(float x,
const bool geom_inverted = di->data.geom.inverted;
/* This could re-generate often if rendered at different sizes in the one interface.
- * TODO(campbell): support caching multiple sizes. */
+ * TODO(@campbellbarton): support caching multiple sizes. */
ImBuf *ibuf = di->data.geom.image_cache;
if ((ibuf == NULL) || (ibuf->x != w) || (ibuf->y != h) || (invert != geom_inverted)) {
if (ibuf) {
diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c
index 6ad5fe805ab..e892a989191 100644
--- a/source/blender/editors/interface/interface_icons_event.c
+++ b/source/blender/editors/interface/interface_icons_event.c
@@ -60,10 +60,8 @@
#include "interface_intern.h"
-static void icon_draw_rect_input_text(const rctf *rect,
- const float color[4],
- const char *str,
- float font_size)
+static void icon_draw_rect_input_text(
+ const rctf *rect, const float color[4], const char *str, float font_size, float v_offset)
{
BLF_batch_draw_flush();
const int font_id = BLF_default();
@@ -71,21 +69,9 @@ static void icon_draw_rect_input_text(const rctf *rect,
BLF_size(font_id, font_size * U.pixelsize, U.dpi);
float width, height;
BLF_width_and_height(font_id, str, BLF_DRAW_STR_DUMMY_MAX, &width, &height);
- const float x = rect->xmin + (((rect->xmax - rect->xmin) - width) / 2.0f);
- const float y = rect->ymin + (((rect->ymax - rect->ymin) - height) / 2.0f);
- BLF_position(font_id, x, y, 0.0f);
- BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX);
- BLF_batch_draw_flush();
-}
-
-static void icon_draw_rect_input_symbol(const rctf *rect, const float color[4], const char *str)
-{
- BLF_batch_draw_flush();
- const int font_id = blf_mono_font;
- BLF_color4fv(font_id, color);
- BLF_size(font_id, 19.0f * U.pixelsize, U.dpi);
- const float x = rect->xmin + (2.0f * U.pixelsize);
- const float y = rect->ymin + (1.0f * U.pixelsize);
+ const float x = trunc(rect->xmin + (((rect->xmax - rect->xmin) - width) / 2.0f));
+ const float y = rect->ymin + (((rect->ymax - rect->ymin) - height) / 2.0f) +
+ (v_offset * U.dpi_fac);
BLF_position(font_id, x, y, 0.0f);
BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX);
BLF_batch_draw_flush();
@@ -99,20 +85,17 @@ void icon_draw_rect_input(float x,
short event_type,
short UNUSED(event_value))
{
+ rctf rect = {
+ .xmin = (int)x - U.pixelsize,
+ .xmax = (int)(x + w + U.pixelsize),
+ .ymin = (int)(y),
+ .ymax = (int)(y + h),
+ };
float color[4];
GPU_line_width(1.0f);
UI_GetThemeColor4fv(TH_TEXT, color);
UI_draw_roundbox_corner_set(UI_CNR_ALL);
- UI_draw_roundbox_aa(
- &(const rctf){
- .xmin = (int)x - U.pixelsize,
- .xmax = (int)(x + w),
- .ymin = (int)y,
- .ymax = (int)(y + h),
- },
- false,
- 3.0f * U.pixelsize,
- color);
+ UI_draw_roundbox_aa(&rect, false, 3.0f * U.pixelsize, color);
const enum {
UNIX,
@@ -129,94 +112,89 @@ void icon_draw_rect_input(float x,
#endif
;
- const rctf rect = {
- .xmin = x,
- .ymin = y,
- .xmax = x + w,
- .ymax = y + h,
- };
-
if ((event_type >= EVT_AKEY) && (event_type <= EVT_ZKEY)) {
const char str[2] = {'A' + (event_type - EVT_AKEY), '\0'};
- icon_draw_rect_input_text(&rect, color, str, 13.0f);
+ icon_draw_rect_input_text(&rect, color, str, 13.0f, 0.0f);
}
- else if ((event_type >= EVT_F1KEY) && (event_type <= EVT_F12KEY)) {
+ else if ((event_type >= EVT_F1KEY) && (event_type <= EVT_F24KEY)) {
char str[4];
SNPRINTF(str, "F%d", 1 + (event_type - EVT_F1KEY));
- icon_draw_rect_input_text(&rect, color, str, event_type > EVT_F9KEY ? 8.0f : 10.0f);
+ icon_draw_rect_input_text(&rect, color, str, event_type > EVT_F9KEY ? 8.5f : 11.5f, 0.0f);
}
else if (event_type == EVT_LEFTSHIFTKEY) { /* Right Shift has already been converted to left. */
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x87, 0xa7, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x87, 0xa7, 0x0}, 16.0f, 0.0f);
}
else if (event_type == EVT_LEFTCTRLKEY) { /* Right Shift has already been converted to left. */
if (platform == MACOS) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0x83, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x8c, 0x83, 0x0}, 21.0f, -8.0f);
}
else {
- icon_draw_rect_input_text(&rect, color, "Ctrl", 9.0f);
+ icon_draw_rect_input_text(&rect, color, "Ctrl", 9.0f, 0.0f);
}
}
else if (event_type == EVT_LEFTALTKEY) { /* Right Alt has already been converted to left. */
if (platform == MACOS) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0xa5, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x8c, 0xa5, 0x0}, 13.0f, 0.0f);
}
else {
- icon_draw_rect_input_text(&rect, color, "Alt", 10.0f);
+ icon_draw_rect_input_text(&rect, color, "Alt", 10.0f, 0.0f);
}
}
else if (event_type == EVT_OSKEY) {
if (platform == MACOS) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8c, 0x98, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x8c, 0x98, 0x0}, 16.0f, 0.0f);
}
else if (platform == MSWIN) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x9d, 0x96, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x9d, 0x96, 0x0}, 16.0f, 0.0f);
}
else {
- icon_draw_rect_input_text(&rect, color, "OS", 10.0f);
+ icon_draw_rect_input_text(&rect, color, "OS", 10.0f, 0.0f);
}
}
else if (event_type == EVT_DELKEY) {
- icon_draw_rect_input_text(&rect, color, "Del", 9.0f);
+ icon_draw_rect_input_text(&rect, color, "Del", 9.0f, 0.0f);
}
else if (event_type == EVT_TABKEY) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0xad, 0xbe, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0xad, 0xbe, 0x0}, 18.0f, -1.5f);
}
else if (event_type == EVT_HOMEKEY) {
- icon_draw_rect_input_text(&rect, color, "Home", 6.0f);
+ icon_draw_rect_input_text(&rect, color, "Home", 6.0f, 0.0f);
}
else if (event_type == EVT_ENDKEY) {
- icon_draw_rect_input_text(&rect, color, "End", 8.0f);
+ icon_draw_rect_input_text(&rect, color, "End", 8.0f, 0.0f);
}
else if (event_type == EVT_RETKEY) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8f, 0x8e, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x8f, 0x8e, 0x0}, 17.0f, -1.0f);
}
else if (event_type == EVT_ESCKEY) {
if (platform == MACOS) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x8e, 0x8b, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x8e, 0x8b, 0x0}, 21.0f, -1.0f);
}
else {
- icon_draw_rect_input_text(&rect, color, "Esc", 8.0f);
+ icon_draw_rect_input_text(&rect, color, "Esc", 8.5f, 0.0f);
}
}
else if (event_type == EVT_PAGEUPKEY) {
- icon_draw_rect_input_text(&rect, color, (const char[]){'P', 0xe2, 0x86, 0x91, 0x0}, 8.0f);
+ icon_draw_rect_input_text(
+ &rect, color, (const char[]){'P', 0xe2, 0x86, 0x91, 0x0}, 12.0f, 0.0f);
}
else if (event_type == EVT_PAGEDOWNKEY) {
- icon_draw_rect_input_text(&rect, color, (const char[]){'P', 0xe2, 0x86, 0x93, 0x0}, 8.0f);
+ icon_draw_rect_input_text(
+ &rect, color, (const char[]){'P', 0xe2, 0x86, 0x93, 0x0}, 12.0f, 0.0f);
}
else if (event_type == EVT_LEFTARROWKEY) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x86, 0x90, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x86, 0x90, 0x0}, 18.0f, -1.5f);
}
else if (event_type == EVT_UPARROWKEY) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x86, 0x91, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x86, 0x91, 0x0}, 16.0f, 0.0f);
}
else if (event_type == EVT_RIGHTARROWKEY) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x86, 0x92, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x86, 0x92, 0x0}, 18.0f, -1.5f);
}
else if (event_type == EVT_DOWNARROWKEY) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x86, 0x93, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x86, 0x93, 0x0}, 16.0f, 0.0f);
}
else if (event_type == EVT_SPACEKEY) {
- icon_draw_rect_input_symbol(&rect, color, (const char[]){0xe2, 0x90, 0xa3, 0x0});
+ icon_draw_rect_input_text(&rect, color, (const char[]){0xe2, 0x90, 0xa3, 0x0}, 20.0f, 2.0f);
}
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index 5e0382f73a9..6ef7d346418 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -25,6 +25,7 @@ struct CurveMapping;
struct CurveProfile;
struct ID;
struct ImBuf;
+struct Main;
struct Scene;
struct bContext;
struct bContextStore;
@@ -306,6 +307,8 @@ typedef struct uiButSearch {
uiButSearchCreateFn popup_create_fn;
uiButSearchUpdateFn items_update_fn;
+ uiButSearchListenFn listen_fn;
+
void *item_active;
void *arg;
@@ -343,20 +346,12 @@ typedef struct uiButProgressbar {
float progress;
} uiButProgressbar;
-/** Derived struct for #UI_BTYPE_TREEROW. */
-typedef struct uiButTreeRow {
- uiBut but;
-
- uiTreeViewItemHandle *tree_item;
- int indentation;
-} uiButTreeRow;
-
-/** Derived struct for #UI_BTYPE_GRID_TILE. */
-typedef struct uiButGridTile {
+typedef struct uiButViewItem {
uiBut but;
- uiGridViewItemHandle *view_item;
-} uiButGridTile;
+ /* C-Handle to the view item this button was created for. */
+ uiViewItemHandle *view_item;
+} uiButViewItem;
/** Derived struct for #UI_BTYPE_HSVCUBE. */
typedef struct uiButHSVCube {
@@ -476,6 +471,7 @@ typedef enum uiButtonGroupFlag {
/** The buttons in this group are inside a panel header. */
UI_BUTTON_GROUP_PANEL_HEADER = (1 << 1),
} uiButtonGroupFlag;
+ENUM_OPERATORS(uiButtonGroupFlag, UI_BUTTON_GROUP_PANEL_HEADER);
struct uiBlock {
uiBlock *next, *prev;
@@ -1372,7 +1368,6 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *but);
bool ui_but_is_editable(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
bool ui_but_is_editable_as_text(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
bool ui_but_is_toggle(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
-bool ui_but_is_view_item(const uiBut *but) ATTR_WARN_UNUSED_RESULT;
/**
* Can we mouse over the button or is it hidden/disabled/layout.
* \note ctrl is kind of a hack currently,
@@ -1406,9 +1401,7 @@ uiBut *ui_list_row_find_from_index(const struct ARegion *region,
uiBut *listbox) ATTR_WARN_UNUSED_RESULT;
uiBut *ui_view_item_find_mouse_over(const struct ARegion *region, const int xy[2])
ATTR_NONNULL(1, 2);
-uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2])
- ATTR_NONNULL(1, 2);
-uiBut *ui_tree_row_find_active(const struct ARegion *region);
+uiBut *ui_view_item_find_active(const struct ARegion *region);
typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata);
/**
@@ -1546,13 +1539,19 @@ void ui_block_free_views(struct uiBlock *block);
uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
const uiViewHandle *new_view);
-uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
- const uiTreeViewItemHandle *new_item_handle);
+uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block(
+ const uiBlock *new_block, const uiViewItemHandle *new_item_handle);
/* interface_templates.c */
struct uiListType *UI_UL_cache_file_layers(void);
+struct ID *ui_template_id_liboverride_hierarchy_make(struct bContext *C,
+ struct Main *bmain,
+ struct ID *owner_id,
+ struct ID *id,
+ const char **r_undo_push_label);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 3465373c85d..d002dd643c3 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -694,7 +694,7 @@ static void ui_item_array(uiLayout *layout,
else {
/* Even if 'expand' is false, we expand anyway. */
- /* layout for known array subtypes */
+ /* Layout for known array sub-types. */
char str[3] = {'\0'};
if (!icon_only && show_text) {
@@ -3028,7 +3028,14 @@ void uiItemMContents(uiLayout *layout, const char *menuname)
if (WM_menutype_poll(C, mt) == false) {
return;
}
+
+ bContextStore *previous_ctx = CTX_store_get(C);
UI_menutype_draw(C, mt, layout);
+
+ /* Restore context that was cleared by `UI_menutype_draw`. */
+ if (layout->context) {
+ CTX_store_set(C, previous_ctx);
+ }
}
void uiItemDecoratorR_prop(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index)
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.cc
index aafb56119ae..603b2c96713 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.cc
@@ -5,7 +5,7 @@
* \ingroup edinterface
*/
-#include <string.h>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -21,6 +21,7 @@
#include "BLF_api.h"
#include "BLT_lang.h"
+#include "BLT_translation.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -28,6 +29,7 @@
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
+#include "BKE_lib_remap.h"
#include "BKE_material.h"
#include "BKE_node.h"
#include "BKE_report.h"
@@ -40,6 +42,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "RNA_types.h"
@@ -127,13 +130,13 @@ static int copy_data_path_button_exec(bContext *C, wmOperator *op)
/* try to create driver using property retrieved from UI */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- if (ptr.owner_id != NULL) {
+ if (ptr.owner_id != nullptr) {
if (full_path) {
if (prop) {
- path = RNA_path_full_property_py_ex(bmain, &ptr, prop, index, true);
+ path = RNA_path_full_property_py_ex(&ptr, prop, index, true);
}
else {
- path = RNA_path_full_struct_py(bmain, &ptr);
+ path = RNA_path_full_struct_py(&ptr);
}
}
else {
@@ -216,7 +219,7 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *op)
if (ptr.owner_id && ptr.data && prop) {
ID *id;
- const int dim = RNA_property_array_dimension(&ptr, prop, NULL);
+ const int dim = RNA_property_array_dimension(&ptr, prop, nullptr);
char *path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, dim, index, &id);
if (path) {
@@ -260,25 +263,25 @@ static bool copy_python_command_button_poll(bContext *C)
{
uiBut *but = UI_context_active_but_get(C);
- if (but && (but->optype != NULL)) {
- return 1;
+ if (but && (but->optype != nullptr)) {
+ return true;
}
- return 0;
+ return false;
}
static int copy_python_command_button_exec(bContext *C, wmOperator *UNUSED(op))
{
uiBut *but = UI_context_active_but_get(C);
- if (but && (but->optype != NULL)) {
+ if (but && (but->optype != nullptr)) {
PointerRNA *opptr;
char *str;
opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */
- str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr);
+ str = WM_operator_pystring_ex(C, nullptr, false, true, but->optype, opptr);
- WM_clipboard_text_set(str, 0);
+ WM_clipboard_text_set(str, false);
MEM_freeN(str);
@@ -390,7 +393,8 @@ static void UI_OT_reset_default_button(wmOperatorType *ot)
ot->flag = 0;
/* properties */
- RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
+ RNA_def_boolean(
+ ot->srna, "all", true, "All", "Reset to default values all elements of the array");
}
/** \} */
@@ -529,7 +533,7 @@ static EnumPropertyItem override_type_items[] = {
0,
"Factor",
"Store factor to linked data value (useful e.g. for scale)"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static bool override_type_set_button_poll(bContext *C)
@@ -580,16 +584,16 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op)
/* try to reset the nominated setting to its default value */
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
- BLI_assert(ptr.owner_id != NULL);
+ BLI_assert(ptr.owner_id != nullptr);
if (all) {
index = -1;
}
IDOverrideLibraryPropertyOperation *opop = RNA_property_override_property_operation_get(
- CTX_data_main(C), &ptr, prop, operation, index, true, NULL, &created);
+ CTX_data_main(C), &ptr, prop, operation, index, true, nullptr, &created);
- if (opop == NULL) {
+ if (opop == nullptr) {
/* Sometimes e.g. RNA cannot generate a path to the given property. */
BKE_reportf(op->reports, RPT_WARNING, "Failed to create the override operation");
return OPERATOR_CANCELLED;
@@ -600,7 +604,7 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op)
}
/* Outliner e.g. has to be aware of this change. */
- WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
return operator_button_property_finish(C, &ptr, prop);
}
@@ -633,7 +637,8 @@ static void UI_OT_override_type_set_button(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO;
/* properties */
- RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
+ RNA_def_boolean(
+ ot->srna, "all", true, "All", "Reset to default values all elements of the array");
ot->prop = RNA_def_enum(ot->srna,
"type",
override_type_items,
@@ -670,8 +675,8 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
ID *id = ptr.owner_id;
IDOverrideLibraryProperty *oprop = RNA_property_override_property_find(bmain, &ptr, prop, &id);
- BLI_assert(oprop != NULL);
- BLI_assert(id != NULL && id->override_library != NULL);
+ BLI_assert(oprop != nullptr);
+ BLI_assert(id != nullptr && id->override_library != nullptr);
const bool is_template = ID_IS_OVERRIDE_LIBRARY_TEMPLATE(id);
@@ -690,8 +695,8 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
/* Remove override operation for given item,
* add singular operations for the other items as needed. */
IDOverrideLibraryPropertyOperation *opop = BKE_lib_override_library_property_operation_find(
- oprop, NULL, NULL, index, index, false, &is_strict_find);
- BLI_assert(opop != NULL);
+ oprop, nullptr, nullptr, index, index, false, &is_strict_find);
+ BLI_assert(opop != nullptr);
if (!is_strict_find) {
/* No specific override operation, we have to get generic one,
* and create item-specific override operations for all but given index,
@@ -699,7 +704,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
for (int idx = RNA_property_array_length(&ptr, prop); idx--;) {
if (idx != index) {
BKE_lib_override_library_property_operation_get(
- oprop, opop->operation, NULL, NULL, idx, idx, true, NULL, NULL);
+ oprop, opop->operation, nullptr, nullptr, idx, idx, true, nullptr, nullptr);
}
}
}
@@ -720,7 +725,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op)
}
/* Outliner e.g. has to be aware of this change. */
- WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+ WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
return operator_button_property_finish(C, &ptr, prop);
}
@@ -740,7 +745,287 @@ static void UI_OT_override_remove_button(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO;
/* properties */
- RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
+ RNA_def_boolean(
+ ot->srna, "all", true, "All", "Reset to default values all elements of the array");
+}
+
+static void override_idtemplate_ids_get(
+ bContext *C, ID **r_owner_id, ID **r_id, PointerRNA *r_owner_ptr, PropertyRNA **r_prop)
+{
+ PointerRNA owner_ptr;
+ PropertyRNA *prop;
+ UI_context_active_but_prop_get_templateID(C, &owner_ptr, &prop);
+
+ if (owner_ptr.data == nullptr || prop == nullptr) {
+ *r_owner_id = *r_id = nullptr;
+ if (r_owner_ptr != nullptr) {
+ *r_owner_ptr = PointerRNA_NULL;
+ }
+ if (r_prop != nullptr) {
+ *r_prop = nullptr;
+ }
+ return;
+ }
+
+ *r_owner_id = owner_ptr.owner_id;
+ PointerRNA idptr = RNA_property_pointer_get(&owner_ptr, prop);
+ *r_id = static_cast<ID *>(idptr.data);
+ if (r_owner_ptr != nullptr) {
+ *r_owner_ptr = owner_ptr;
+ }
+ if (r_prop != nullptr) {
+ *r_prop = prop;
+ }
+}
+
+static bool override_idtemplate_poll(bContext *C, const bool is_create_op)
+{
+ ID *owner_id, *id;
+ override_idtemplate_ids_get(C, &owner_id, &id, nullptr, nullptr);
+
+ if (owner_id == nullptr || id == nullptr) {
+ return false;
+ }
+
+ if (is_create_op) {
+ if (!ID_IS_LINKED(id) && !ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ return false;
+ }
+ return true;
+ }
+
+ /* Reset/Clear operations. */
+ if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ return false;
+ }
+ return true;
+}
+
+static bool override_idtemplate_make_poll(bContext *C)
+{
+ return override_idtemplate_poll(C, true);
+}
+
+static int override_idtemplate_make_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ID *owner_id, *id;
+ PointerRNA owner_ptr;
+ PropertyRNA *prop;
+ override_idtemplate_ids_get(C, &owner_id, &id, &owner_ptr, &prop);
+ if (ELEM(nullptr, owner_id, id)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ ID *id_override = ui_template_id_liboverride_hierarchy_make(
+ C, CTX_data_main(C), owner_id, id, nullptr);
+
+ if (id_override == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ PointerRNA idptr;
+ /* `idptr` is re-assigned to owner property to ensure proper updates etc. Here we also use it
+ * to ensure remapping of the owner property from the linked data to the newly created
+ * liboverride (note that in theory this remapping has already been done by code above), but
+ * only in case owner ID was already local ID (override or pure local data).
+ *
+ * Otherwise, owner ID will also have been overridden, and remapped already to use it's
+ * override of the data too. */
+ if (!ID_IS_LINKED(owner_id)) {
+ RNA_id_pointer_create(id_override, &idptr);
+ RNA_property_pointer_set(&owner_ptr, prop, idptr, nullptr);
+ }
+ RNA_property_update(C, &owner_ptr, prop);
+
+ /* 'Security' extra tagging, since this process may also affect the owner ID and not only the
+ * used ID, relying on the property update code only is not always enough. */
+ 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, nullptr);
+ WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+static void UI_OT_override_idtemplate_make(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Make Library Override";
+ ot->idname = "UI_OT_override_idtemplate_make";
+ ot->description =
+ "Create a local override of the selected linked data-block, and its hierarchy of "
+ "dependencies";
+
+ /* callbacks */
+ ot->poll = override_idtemplate_make_poll;
+ ot->exec = override_idtemplate_make_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
+}
+
+static bool override_idtemplate_reset_poll(bContext *C)
+{
+ return override_idtemplate_poll(C, false);
+}
+
+static int override_idtemplate_reset_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ID *owner_id, *id;
+ PointerRNA owner_ptr;
+ PropertyRNA *prop;
+ override_idtemplate_ids_get(C, &owner_id, &id, &owner_ptr, &prop);
+ if (ELEM(nullptr, owner_id, id)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_lib_override_library_id_reset(CTX_data_main(C), id, false);
+
+ PointerRNA idptr;
+ /* `idptr` is re-assigned to owner property to ensure proper updates etc. */
+ RNA_id_pointer_create(id, &idptr);
+ RNA_property_pointer_set(&owner_ptr, prop, idptr, nullptr);
+ RNA_property_update(C, &owner_ptr, prop);
+
+ /* No need for 'security' extra tagging here, since this process will never affect the owner ID.
+ */
+
+ return OPERATOR_FINISHED;
+}
+
+static void UI_OT_override_idtemplate_reset(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Reset Library Override";
+ ot->idname = "UI_OT_override_idtemplate_reset";
+ ot->description = "Reset the selected local override to its linked reference values";
+
+ /* callbacks */
+ ot->poll = override_idtemplate_reset_poll;
+ ot->exec = override_idtemplate_reset_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
+}
+
+static bool override_idtemplate_clear_poll(bContext *C)
+{
+ return override_idtemplate_poll(C, false);
+}
+
+static int override_idtemplate_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ID *owner_id, *id;
+ PointerRNA owner_ptr;
+ PropertyRNA *prop;
+ override_idtemplate_ids_get(C, &owner_id, &id, &owner_ptr, &prop);
+ if (ELEM(nullptr, owner_id, id)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ID_IS_LINKED(id)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Main *bmain = CTX_data_main(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Scene *scene = CTX_data_scene(C);
+ ID *id_new = id;
+
+ if (BKE_lib_override_library_is_hierarchy_leaf(bmain, id)) {
+ id_new = id->override_library->reference;
+ bool do_remap_active = false;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (BKE_view_layer_active_object_get(view_layer) == (Object *)id) {
+ BLI_assert(GS(id->name) == ID_OB);
+ BLI_assert(GS(id_new->name) == ID_OB);
+ do_remap_active = true;
+ }
+ BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE);
+ if (do_remap_active) {
+ Object *ref_object = (Object *)id_new;
+ Base *basact = BKE_view_layer_base_find(view_layer, ref_object);
+ if (basact != nullptr) {
+ view_layer->basact = basact;
+ }
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ }
+ BKE_id_delete(bmain, id);
+ }
+ else {
+ BKE_lib_override_library_id_reset(bmain, id, true);
+ }
+
+ /* Here the affected ID may remain the same, or be replaced by its linked reference. In either
+ * case, the owner ID remains unchanged, and remapping is already handled by internal code, so
+ * calling `RNA_property_update` on it is enough to ensure proper notifiers are sent. */
+ RNA_property_update(C, &owner_ptr, prop);
+
+ /* 'Security' extra tagging, since this process may also affect the owner ID and not only the
+ * used ID, relying on the property update code only is not always enough. */
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_WINDOW, nullptr);
+ WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+static void UI_OT_override_idtemplate_clear(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Clear Library Override";
+ ot->idname = "UI_OT_override_idtemplate_clear";
+ ot->description =
+ "Delete the selected local override and relink its usages to the linked data-block if "
+ "possible, else reset it and mark it as non editable";
+
+ /* callbacks */
+ ot->poll = override_idtemplate_clear_poll;
+ ot->exec = override_idtemplate_clear_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO;
+}
+
+static bool override_idtemplate_menu_poll(const bContext *C_const, MenuType *UNUSED(mt))
+{
+ bContext *C = (bContext *)C_const;
+ ID *owner_id, *id;
+ override_idtemplate_ids_get(C, &owner_id, &id, nullptr, nullptr);
+
+ if (owner_id == nullptr || id == nullptr) {
+ return false;
+ }
+
+ if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY_REAL(id))) {
+ return false;
+ }
+ return true;
+}
+
+static void override_idtemplate_menu_draw(const bContext *UNUSED(C), Menu *menu)
+{
+ uiLayout *layout = menu->layout;
+ uiItemO(layout, IFACE_("Make"), ICON_NONE, "UI_OT_override_idtemplate_make");
+ uiItemO(layout, IFACE_("Reset"), ICON_NONE, "UI_OT_override_idtemplate_reset");
+ uiItemO(layout, IFACE_("Clear"), ICON_NONE, "UI_OT_override_idtemplate_clear");
+}
+
+static void override_idtemplate_menu()
+{
+ MenuType *mt;
+
+ mt = MEM_cnew<MenuType>(__func__);
+ strcpy(mt->idname, "UI_MT_idtemplate_liboverride");
+ strcpy(mt->label, N_("Library Override"));
+ mt->poll = override_idtemplate_menu_poll;
+ mt->draw = override_idtemplate_menu_draw;
+ WM_menutype_add(mt);
}
/** \} */
@@ -749,8 +1034,8 @@ static void UI_OT_override_remove_button(wmOperatorType *ot)
/** \name Copy To Selected Operator
* \{ */
-#define NOT_NULL(assignment) ((assignment) != NULL)
-#define NOT_RNA_NULL(assignment) ((assignment).data != NULL)
+#define NOT_NULL(assignment) ((assignment) != nullptr)
+#define NOT_RNA_NULL(assignment) ((assignment).data != nullptr)
static void ui_context_selected_bones_via_pose(bContext *C, ListBase *r_lb)
{
@@ -759,7 +1044,7 @@ static void ui_context_selected_bones_via_pose(bContext *C, ListBase *r_lb)
if (!BLI_listbase_is_empty(&lb)) {
LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) {
- bPoseChannel *pchan = link->ptr.data;
+ bPoseChannel *pchan = static_cast<bPoseChannel *>(link->ptr.data);
RNA_pointer_create(link->ptr.owner_id, &RNA_Bone, pchan->bone, &link->ptr);
}
}
@@ -775,9 +1060,9 @@ bool UI_context_copy_to_selected_list(bContext *C,
char **r_path)
{
*r_use_path_from_id = false;
- *r_path = NULL;
+ *r_path = nullptr;
/* special case for bone constraints */
- char *path_from_bone = NULL;
+ char *path_from_bone = nullptr;
/* Remove links from the collection list which don't contain 'prop'. */
bool ensure_list_items_contain_prop = false;
@@ -791,29 +1076,32 @@ bool UI_context_copy_to_selected_list(bContext *C,
*/
if (!RNA_property_is_idprop(prop) && RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
PointerRNA owner_ptr;
- char *idpath = NULL;
+ char *idpath = nullptr;
/* First, check the active PoseBone and PoseBone->Bone. */
if (NOT_RNA_NULL(
owner_ptr = CTX_data_pointer_get_type(C, "active_pose_bone", &RNA_PoseBone))) {
- if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) {
+ if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(
+ &owner_ptr, static_cast<IDProperty *>(ptr->data)))) {
*r_lb = CTX_data_collection_get(C, "selected_pose_bones");
}
else {
- bPoseChannel *pchan = owner_ptr.data;
+ bPoseChannel *pchan = static_cast<bPoseChannel *>(owner_ptr.data);
RNA_pointer_create(owner_ptr.owner_id, &RNA_Bone, pchan->bone, &owner_ptr);
- if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) {
+ if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(
+ &owner_ptr, static_cast<IDProperty *>(ptr->data)))) {
ui_context_selected_bones_via_pose(C, r_lb);
}
}
}
- if (idpath == NULL) {
+ if (idpath == nullptr) {
/* Check the active EditBone if in edit mode. */
if (NOT_RNA_NULL(
owner_ptr = CTX_data_pointer_get_type_silent(C, "active_bone", &RNA_EditBone)) &&
- NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) {
+ NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(
+ &owner_ptr, static_cast<IDProperty *>(ptr->data)))) {
*r_lb = CTX_data_collection_get(C, "selected_editable_bones");
}
@@ -866,30 +1154,30 @@ bool UI_context_copy_to_selected_list(bContext *C,
}
else if (RNA_struct_is_a(ptr->type, &RNA_Constraint) &&
(path_from_bone = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_PoseBone)) !=
- NULL) {
+ nullptr) {
*r_lb = CTX_data_collection_get(C, "selected_pose_bones");
*r_path = path_from_bone;
}
else if (RNA_struct_is_a(ptr->type, &RNA_Node) || RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) {
- ListBase lb = {NULL, NULL};
- char *path = NULL;
- bNode *node = NULL;
+ ListBase lb = {nullptr, nullptr};
+ char *path = nullptr;
+ bNode *node = nullptr;
/* Get the node we're editing */
if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) {
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
- bNodeSocket *sock = ptr->data;
- if (nodeFindNode(ntree, sock, &node, NULL)) {
- if ((path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node)) != NULL) {
+ bNodeSocket *sock = static_cast<bNodeSocket *>(ptr->data);
+ if (nodeFindNode(ntree, sock, &node, nullptr)) {
+ if ((path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node)) != nullptr) {
/* we're good! */
}
else {
- node = NULL;
+ node = nullptr;
}
}
}
else {
- node = ptr->data;
+ node = static_cast<bNode *>(ptr->data);
}
/* Now filter by type */
@@ -897,7 +1185,7 @@ bool UI_context_copy_to_selected_list(bContext *C,
lb = CTX_data_collection_get(C, "selected_nodes");
LISTBASE_FOREACH_MUTABLE (CollectionPointerLink *, link, &lb) {
- bNode *node_data = link->ptr.data;
+ bNode *node_data = static_cast<bNode *>(link->ptr.data);
if (node_data->type != node->type) {
BLI_remlink(&lb, link);
@@ -928,17 +1216,17 @@ bool UI_context_copy_to_selected_list(bContext *C,
LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) {
Object *ob = (Object *)link->ptr.owner_id;
if (ob->data) {
- ID *id_data = ob->data;
+ ID *id_data = static_cast<ID *>(ob->data);
id_data->tag |= LIB_TAG_DOIT;
}
}
LISTBASE_FOREACH_MUTABLE (CollectionPointerLink *, link, &lb) {
Object *ob = (Object *)link->ptr.owner_id;
- ID *id_data = ob->data;
+ ID *id_data = static_cast<ID *>(ob->data);
- if ((id_data == NULL) || (id_data->tag & LIB_TAG_DOIT) == 0 || ID_IS_LINKED(id_data) ||
- (GS(id_data->name) != id_code)) {
+ if ((id_data == nullptr) || (id_data->tag & LIB_TAG_DOIT) == 0 ||
+ ID_IS_LINKED(id_data) || (GS(id_data->name) != id_code)) {
BLI_remlink(&lb, link);
MEM_freeN(link);
}
@@ -960,7 +1248,8 @@ bool UI_context_copy_to_selected_list(bContext *C,
/* Sequencer's ID is scene :/ */
/* Try to recursively find an RNA_Sequence ancestor,
* to handle situations like T41062... */
- if ((*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) {
+ if ((*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence)) !=
+ nullptr) {
/* Special case when we do this for 'Sequence.lock'.
* (if the sequence is locked, it won't be in "selected_editable_sequences"). */
const char *prop_id = RNA_property_identifier(prop);
@@ -974,7 +1263,7 @@ bool UI_context_copy_to_selected_list(bContext *C,
ensure_list_items_contain_prop = true;
}
}
- return (*r_path != NULL);
+ return (*r_path != nullptr);
}
else {
return false;
@@ -1012,13 +1301,13 @@ bool UI_context_copy_to_selected_check(PointerRNA *ptr,
if (use_path_from_id) {
/* Path relative to ID. */
- lprop = NULL;
+ lprop = nullptr;
RNA_id_pointer_create(ptr_link->owner_id, &idptr);
RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
}
else if (path) {
/* Path relative to elements from list. */
- lprop = NULL;
+ lprop = nullptr;
RNA_path_resolve_property(ptr_link, path, &lptr, &lprop);
}
else {
@@ -1033,7 +1322,7 @@ bool UI_context_copy_to_selected_check(PointerRNA *ptr,
/* Skip non-existing properties on link. This was previously covered with the `lprop != prop`
* check but we are now more permissive when it comes to ID properties, see below. */
- if (lprop == NULL) {
+ if (lprop == nullptr) {
return false;
}
@@ -1104,13 +1393,13 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll)
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
/* if there is a valid property that is editable... */
- if (ptr.data == NULL || prop == NULL) {
+ if (ptr.data == nullptr || prop == nullptr) {
return false;
}
- char *path = NULL;
+ char *path = nullptr;
bool use_path_from_id;
- ListBase lb = {NULL};
+ ListBase lb = {nullptr};
if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) {
return false;
@@ -1197,7 +1486,7 @@ static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll)
/* Verify pointer type. */
char bone_name[MAXBONENAME];
- const StructRNA *target_type = NULL;
+ const StructRNA *target_type = nullptr;
if (ELEM(ptr.type, &RNA_EditBone, &RNA_PoseBone, &RNA_Bone)) {
RNA_string_get(&ptr, "name", bone_name);
@@ -1209,23 +1498,25 @@ static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll)
target_type = &RNA_Object;
}
- if (target_type == NULL) {
+ if (target_type == nullptr) {
return false;
}
/* Find the containing Object. */
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = NULL;
+ Base *base = nullptr;
const short id_type = GS(ptr.owner_id->name);
if (id_type == ID_OB) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
base = BKE_view_layer_base_find(view_layer, (Object *)ptr.owner_id);
}
else if (OB_DATA_SUPPORT_ID(id_type)) {
- base = ED_object_find_first_by_data_id(view_layer, ptr.owner_id);
+ base = ED_object_find_first_by_data_id(scene, view_layer, ptr.owner_id);
}
bool ok = false;
- if ((base == NULL) || ((target_type == &RNA_Bone) && (base->object->type != OB_ARMATURE))) {
+ if ((base == nullptr) || ((target_type == &RNA_Bone) && (base->object->type != OB_ARMATURE))) {
/* pass */
}
else if (poll) {
@@ -1277,16 +1568,22 @@ static bool jump_to_target_button(bContext *C, bool poll)
if (type == PROP_STRING) {
const uiBut *but = UI_context_active_but_get(C);
const uiButSearch *search_but = (but->type == UI_BTYPE_SEARCH_MENU) ? (uiButSearch *)but :
- NULL;
+ nullptr;
if (search_but && search_but->items_update_fn == ui_rna_collection_search_update_fn) {
- uiRNACollectionSearch *coll_search = search_but->arg;
+ uiRNACollectionSearch *coll_search = static_cast<uiRNACollectionSearch *>(search_but->arg);
char str_buf[MAXBONENAME];
- char *str_ptr = RNA_property_string_get_alloc(&ptr, prop, str_buf, sizeof(str_buf), NULL);
-
- int found = RNA_property_collection_lookup_string(
- &coll_search->search_ptr, coll_search->search_prop, str_ptr, &target_ptr);
+ char *str_ptr = RNA_property_string_get_alloc(
+ &ptr, prop, str_buf, sizeof(str_buf), nullptr);
+
+ int found = 0;
+ /* Jump to target only works with search properties currently, not search callbacks yet.
+ * See ui_but_add_search. */
+ if (coll_search->search_prop != NULL) {
+ found = RNA_property_collection_lookup_string(
+ &coll_search->search_ptr, coll_search->search_prop, str_ptr, &target_ptr);
+ }
if (str_ptr != str_buf) {
MEM_freeN(str_ptr);
@@ -1338,42 +1635,42 @@ static void UI_OT_jump_to_target_button(wmOperatorType *ot)
#ifdef WITH_PYTHON
/* ------------------------------------------------------------------------- */
-/* EditSource Utility funcs and operator,
+/* EditSource Utility functions and operator,
* NOTE: this includes utility functions and button matching checks. */
-typedef struct uiEditSourceStore {
+struct uiEditSourceStore {
uiBut but_orig;
GHash *hash;
-} uiEditSourceStore;
+};
-typedef struct uiEditSourceButStore {
+struct uiEditSourceButStore {
char py_dbg_fn[FILE_MAX];
int py_dbg_line_number;
-} uiEditSourceButStore;
+};
/* should only ever be set while the edit source operator is running */
-static struct uiEditSourceStore *ui_editsource_info = NULL;
+static uiEditSourceStore *ui_editsource_info = nullptr;
bool UI_editsource_enable_check(void)
{
- return (ui_editsource_info != NULL);
+ return (ui_editsource_info != nullptr);
}
static void ui_editsource_active_but_set(uiBut *but)
{
- BLI_assert(ui_editsource_info == NULL);
+ BLI_assert(ui_editsource_info == nullptr);
- ui_editsource_info = MEM_callocN(sizeof(uiEditSourceStore), __func__);
+ ui_editsource_info = MEM_cnew<uiEditSourceStore>(__func__);
memcpy(&ui_editsource_info->but_orig, but, sizeof(uiBut));
ui_editsource_info->hash = BLI_ghash_ptr_new(__func__);
}
-static void ui_editsource_active_but_clear(void)
+static void ui_editsource_active_but_clear()
{
- BLI_ghash_free(ui_editsource_info->hash, NULL, MEM_freeN);
+ BLI_ghash_free(ui_editsource_info->hash, nullptr, MEM_freeN);
MEM_freeN(ui_editsource_info);
- ui_editsource_info = NULL;
+ ui_editsource_info = nullptr;
}
static bool ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b)
@@ -1394,11 +1691,14 @@ static bool ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b)
return false;
}
+extern "C" {
+void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno);
+}
+
void UI_editsource_active_but_test(uiBut *but)
{
- extern void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno);
- struct uiEditSourceButStore *but_store = MEM_callocN(sizeof(uiEditSourceButStore), __func__);
+ uiEditSourceButStore *but_store = MEM_cnew<uiEditSourceButStore>(__func__);
const char *fn;
int line_number = -1;
@@ -1423,9 +1723,10 @@ void UI_editsource_active_but_test(uiBut *but)
void UI_editsource_but_replace(const uiBut *old_but, uiBut *new_but)
{
- uiEditSourceButStore *but_store = BLI_ghash_lookup(ui_editsource_info->hash, old_but);
+ uiEditSourceButStore *but_store = static_cast<uiEditSourceButStore *>(
+ BLI_ghash_lookup(ui_editsource_info->hash, old_but));
if (but_store) {
- BLI_ghash_remove(ui_editsource_info->hash, old_but, NULL, NULL);
+ BLI_ghash_remove(ui_editsource_info->hash, old_but, nullptr, nullptr);
BLI_ghash_insert(ui_editsource_info->hash, new_but, but_store);
}
}
@@ -1435,8 +1736,8 @@ static int editsource_text_edit(bContext *C,
const char filepath[FILE_MAX],
const int line)
{
- struct Main *bmain = CTX_data_main(C);
- Text *text = NULL;
+ Main *bmain = CTX_data_main(C);
+ Text *text = nullptr;
/* Developers may wish to copy-paste to an external editor. */
printf("%s:%d\n", filepath, line);
@@ -1448,11 +1749,11 @@ static int editsource_text_edit(bContext *C,
}
}
- if (text == NULL) {
+ if (text == nullptr) {
text = BKE_text_load(bmain, filepath, BKE_main_blendfile_path(bmain));
}
- if (text == NULL) {
+ if (text == nullptr) {
BKE_reportf(op->reports, RPT_WARNING, "File '%s' cannot be opened", filepath);
return OPERATOR_CANCELLED;
}
@@ -1476,7 +1777,7 @@ static int editsource_exec(bContext *C, wmOperator *op)
if (but) {
GHashIterator ghi;
- struct uiEditSourceButStore *but_store = NULL;
+ uiEditSourceButStore *but_store = nullptr;
ARegion *region = CTX_wm_region(C);
int ret;
@@ -1495,9 +1796,9 @@ static int editsource_exec(bContext *C, wmOperator *op)
for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash);
BLI_ghashIterator_done(&ghi) == false;
BLI_ghashIterator_step(&ghi)) {
- uiBut *but_key = BLI_ghashIterator_getKey(&ghi);
+ uiBut *but_key = static_cast<uiBut *>(BLI_ghashIterator_getKey(&ghi));
if (but_key && ui_editsource_uibut_match(&ui_editsource_info->but_orig, but_key)) {
- but_store = BLI_ghashIterator_getValue(&ghi);
+ but_store = static_cast<uiEditSourceButStore *>(BLI_ghashIterator_getValue(&ghi));
break;
}
}
@@ -1546,7 +1847,7 @@ static void UI_OT_editsource(wmOperatorType *ot)
* \{ */
/**
- * EditTranslation utility funcs and operator,
+ * EditTranslation utility functions and operator.
*
* \note this includes utility functions and button matching checks.
* this only works in conjunction with a Python operator!
@@ -1568,7 +1869,7 @@ static void edittranslation_find_po_file(const char *root,
/* Now try without the second iso code part (_ES in es_ES). */
{
- const char *tc = NULL;
+ const char *tc = nullptr;
size_t szt = 0;
tstr[0] = '\0';
@@ -1602,7 +1903,7 @@ 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);
- if (but == NULL) {
+ if (but == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Active button not found");
return OPERATOR_CANCELLED;
}
@@ -1613,16 +1914,16 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
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};
+ uiStringInfo but_label = {BUT_GET_LABEL, nullptr};
+ uiStringInfo rna_label = {BUT_GET_RNA_LABEL, nullptr};
+ uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, nullptr};
+ uiStringInfo but_tip = {BUT_GET_TIP, nullptr};
+ uiStringInfo rna_tip = {BUT_GET_RNA_TIP, nullptr};
+ uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, nullptr};
+ uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, nullptr};
+ uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, nullptr};
+ uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, nullptr};
+ uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, nullptr};
if (!BLI_is_dir(root)) {
BKE_report(op->reports,
@@ -1631,8 +1932,8 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
"Directory' path to a valid directory");
return OPERATOR_CANCELLED;
}
- ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0);
- if (ot == NULL) {
+ ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, false);
+ if (ot == nullptr) {
BKE_reportf(op->reports,
RPT_ERROR,
"Could not find operator '%s'! Please enable ui_translate add-on "
@@ -1661,7 +1962,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
&rna_prop,
&rna_enum,
&rna_ctxt,
- NULL);
+ nullptr);
WM_operator_properties_create_ptr(&ptr, ot);
RNA_string_set(&ptr, "lang", uilng);
@@ -1676,7 +1977,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op)
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, NULL);
+ const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
/* Clean up */
if (but_label.strinfo) {
@@ -1736,7 +2037,7 @@ static int reloadtranslation_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
{
BLT_lang_init();
BLF_cache_clear();
- BLT_lang_set(NULL);
+ BLT_lang_set(nullptr);
UI_reinit_font();
return OPERATOR_FINISHED;
}
@@ -1763,13 +2064,13 @@ static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *ev
bScreen *screen = CTX_wm_screen(C);
const bool skip_depressed = RNA_boolean_get(op->ptr, "skip_depressed");
ARegion *region_prev = CTX_wm_region(C);
- ARegion *region = screen ? BKE_screen_find_region_xy(screen, RGN_TYPE_ANY, event->xy) : NULL;
+ ARegion *region = screen ? BKE_screen_find_region_xy(screen, RGN_TYPE_ANY, event->xy) : nullptr;
- if (region == NULL) {
+ if (region == nullptr) {
region = region_prev;
}
- if (region == NULL) {
+ if (region == nullptr) {
return OPERATOR_PASS_THROUGH;
}
@@ -1777,7 +2078,7 @@ static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *ev
uiBut *but = UI_context_active_but_get(C);
CTX_wm_region_set(C, region_prev);
- if (but == NULL) {
+ if (but == nullptr) {
return OPERATOR_PASS_THROUGH;
}
if (skip_depressed && (but->flag & (UI_SELECT | UI_SELECT_DRAW))) {
@@ -1790,7 +2091,7 @@ static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *ev
UI_but_execute(C, region, but);
- but->optype = but_optype;
+ but->optype = static_cast<wmOperatorType *>(but_optype);
WM_event_add_mousemove(CTX_wm_window(C));
@@ -1806,7 +2107,7 @@ static void UI_OT_button_execute(wmOperatorType *ot)
ot->invoke = ui_button_press_invoke;
ot->flag = OPTYPE_INTERNAL;
- RNA_def_boolean(ot->srna, "skip_depressed", 0, "Skip Depressed", "");
+ RNA_def_boolean(ot->srna, "skip_depressed", false, "Skip Depressed", "");
}
/** \} */
@@ -1852,21 +2153,21 @@ bool UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(
ARegion *region = CTX_wm_region(C);
if (UI_but_active_drop_color(C)) {
- return 1;
+ return true;
}
if (sima && (sima->mode == SI_MODE_PAINT) && sima->image &&
(region && region->regiontype == RGN_TYPE_WINDOW)) {
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
void UI_drop_color_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
{
- uiDragColorHandle *drag_info = drag->poin;
+ uiDragColorHandle *drag_info = static_cast<uiDragColorHandle *>(drag->poin);
RNA_float_set_array(drop->ptr, "color", drag_info->color);
RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected);
@@ -1875,7 +2176,7 @@ void UI_drop_color_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
- uiBut *but = NULL;
+ uiBut *but = nullptr;
float color[4];
bool gamma;
@@ -1932,8 +2233,10 @@ static void UI_OT_drop_color(wmOperatorType *ot)
ot->invoke = drop_color_invoke;
ot->flag = OPTYPE_INTERNAL;
- RNA_def_float_color(ot->srna, "color", 3, NULL, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0);
- RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected");
+ RNA_def_float_color(
+ ot->srna, "color", 3, nullptr, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0);
+ RNA_def_boolean(
+ ot->srna, "gamma", false, "Gamma Corrected", "The source color is gamma corrected");
}
/** \} */
@@ -1963,7 +2266,7 @@ static bool drop_name_poll(bContext *C)
static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
uiBut *but = UI_but_active_drop_name_button(C);
- char *str = RNA_string_get_alloc(op->ptr, "string", NULL, 0, NULL);
+ char *str = RNA_string_get_alloc(op->ptr, "string", nullptr, 0, nullptr);
if (str) {
ui_but_set_string_interactive(C, but, str);
@@ -1984,7 +2287,7 @@ static void UI_OT_drop_name(wmOperatorType *ot)
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
RNA_def_string(
- ot->srna, "string", NULL, 0, "String", "The string value to drop into the button");
+ ot->srna, "string", nullptr, 0, "String", "The string value to drop into the button");
}
/** \} */
@@ -2002,7 +2305,7 @@ static bool ui_list_focused_poll(bContext *C)
const wmWindow *win = CTX_wm_window(C);
const uiList *list = UI_list_find_mouse_over(region, win->eventstate);
- return list != NULL;
+ return list != nullptr;
}
/**
@@ -2025,7 +2328,7 @@ static int ui_list_start_filter_invoke(bContext *C, wmOperator *UNUSED(op), cons
ARegion *region = CTX_wm_region(C);
uiList *list = UI_list_find_mouse_over(region, event);
/* Poll should check. */
- BLI_assert(list != NULL);
+ BLI_assert(list != nullptr);
if (ui_list_unhide_filter_options(list)) {
ui_region_redraw_immediately(C, region);
@@ -2054,40 +2357,43 @@ static void UI_OT_list_start_filter(wmOperatorType *ot)
/** \name UI Tree-View Drop Operator
* \{ */
-static bool ui_tree_view_drop_poll(bContext *C)
+static bool ui_view_drop_poll(bContext *C)
{
const wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(
- region, win->eventstate->xy);
+ if (region == nullptr) {
+ return false;
+ }
+ const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, win->eventstate->xy);
- return hovered_tree_item != NULL;
+ return hovered_item != nullptr;
}
-static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+static int ui_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
{
if (event->custom != EVT_DATA_DRAGDROP) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
const ARegion *region = CTX_wm_region(C);
- uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, event->xy);
+ uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy);
- if (!UI_tree_view_item_drop_handle(C, hovered_tree_item, event->customdata)) {
+ if (!UI_view_item_drop_handle(
+ C, hovered_item, static_cast<const ListBase *>(event->customdata))) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
return OPERATOR_FINISHED;
}
-static void UI_OT_tree_view_drop(wmOperatorType *ot)
+static void UI_OT_view_drop(wmOperatorType *ot)
{
- ot->name = "Tree View drop";
- ot->idname = "UI_OT_tree_view_drop";
- ot->description = "Drag and drop items onto a tree item";
+ ot->name = "View drop";
+ ot->idname = "UI_OT_view_drop";
+ ot->description = "Drag and drop items onto a data-set item";
- ot->invoke = ui_tree_view_drop_invoke;
- ot->poll = ui_tree_view_drop_poll;
+ ot->invoke = ui_view_drop_invoke;
+ ot->poll = ui_view_drop_poll;
ot->flag = OPTYPE_INTERNAL;
}
@@ -2095,43 +2401,45 @@ static void UI_OT_tree_view_drop(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name UI Tree-View Item Rename Operator
+/** \name UI View Item Rename Operator
*
- * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to
- * context menus for example, tree-view API users don't have to implement their own renaming
- * operators with the same logic as they already have for their #ui::AbstractTreeViewItem::rename()
- * override.
+ * General purpose renaming operator for views. Thanks to this, to add a rename button to context
+ * menus for example, view API users don't have to implement their own renaming operators with the
+ * same logic as they already have for their #ui::AbstractViewItem::rename() override.
*
* \{ */
-static bool ui_tree_view_item_rename_poll(bContext *C)
+static bool ui_view_item_rename_poll(bContext *C)
{
const ARegion *region = CTX_wm_region(C);
- const uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region);
- return active_item != NULL && UI_tree_view_item_can_rename(active_item);
+ if (region == nullptr) {
+ return false;
+ }
+ const uiViewItemHandle *active_item = UI_region_views_find_active_item(region);
+ return active_item != nullptr && UI_view_item_can_rename(active_item);
}
-static int ui_tree_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op))
+static int ui_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op))
{
ARegion *region = CTX_wm_region(C);
- uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region);
+ uiViewItemHandle *active_item = UI_region_views_find_active_item(region);
- UI_tree_view_item_begin_rename(active_item);
+ UI_view_item_begin_rename(active_item);
ED_region_tag_redraw(region);
return OPERATOR_FINISHED;
}
-static void UI_OT_tree_view_item_rename(wmOperatorType *ot)
+static void UI_OT_view_item_rename(wmOperatorType *ot)
{
- ot->name = "Rename Tree-View Item";
- ot->idname = "UI_OT_tree_view_item_rename";
- ot->description = "Rename the active item in the tree";
+ ot->name = "Rename View Item";
+ ot->idname = "UI_OT_view_item_rename";
+ ot->description = "Rename the active item in the data-set view";
- ot->exec = ui_tree_view_item_rename_exec;
- ot->poll = ui_tree_view_item_rename_poll;
+ ot->exec = ui_view_item_rename_exec;
+ ot->poll = ui_view_item_rename_poll;
/* Could get a custom tooltip via the `get_description()` callback and another overridable
- * function of the tree-view. */
+ * function of the view. */
ot->flag = OPTYPE_INTERNAL;
}
@@ -2145,8 +2453,8 @@ static void UI_OT_tree_view_item_rename(wmOperatorType *ot)
static bool ui_drop_material_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "object", &RNA_Object);
- Object *ob = ptr.data;
- if (ob == NULL) {
+ const Object *ob = static_cast<const Object *>(ptr.data);
+ if (ob == nullptr) {
return false;
}
@@ -2164,12 +2472,12 @@ static int ui_drop_material_exec(bContext *C, wmOperator *op)
Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid(
bmain, op->ptr, ID_MA);
- if (ma == NULL) {
+ if (ma == nullptr) {
return OPERATOR_CANCELLED;
}
PointerRNA ptr = CTX_data_pointer_get_type(C, "object", &RNA_Object);
- Object *ob = ptr.data;
+ Object *ob = static_cast<Object *>(ptr.data);
BLI_assert(ob);
PointerRNA mat_slot = CTX_data_pointer_get_type(C, "material_slot", &RNA_MaterialSlot);
@@ -2177,14 +2485,14 @@ static int ui_drop_material_exec(bContext *C, wmOperator *op)
const int target_slot = RNA_int_get(&mat_slot, "slot_index") + 1;
/* only drop grease pencil material on grease pencil objects */
- if ((ma->gp_style != NULL) && (ob->type != OB_GPENCIL)) {
+ if ((ma->gp_style != nullptr) && (ob->type != OB_GPENCIL)) {
return OPERATOR_CANCELLED;
}
BKE_object_material_assign(bmain, ob, ma, target_slot, BKE_MAT_ASSIGN_USERPREF);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
@@ -2218,8 +2526,6 @@ void ED_operatortypes_ui(void)
WM_operatortype_append(UI_OT_reset_default_button);
WM_operatortype_append(UI_OT_assign_default_button);
WM_operatortype_append(UI_OT_unset_property_button);
- WM_operatortype_append(UI_OT_override_type_set_button);
- WM_operatortype_append(UI_OT_override_remove_button);
WM_operatortype_append(UI_OT_copy_to_selected_button);
WM_operatortype_append(UI_OT_jump_to_target_button);
WM_operatortype_append(UI_OT_drop_color);
@@ -2235,8 +2541,15 @@ void ED_operatortypes_ui(void)
WM_operatortype_append(UI_OT_list_start_filter);
- WM_operatortype_append(UI_OT_tree_view_drop);
- WM_operatortype_append(UI_OT_tree_view_item_rename);
+ WM_operatortype_append(UI_OT_view_drop);
+ WM_operatortype_append(UI_OT_view_item_rename);
+
+ WM_operatortype_append(UI_OT_override_type_set_button);
+ WM_operatortype_append(UI_OT_override_remove_button);
+ WM_operatortype_append(UI_OT_override_idtemplate_make);
+ WM_operatortype_append(UI_OT_override_idtemplate_reset);
+ WM_operatortype_append(UI_OT_override_idtemplate_clear);
+ override_idtemplate_menu();
/* external */
WM_operatortype_append(UI_OT_eyedropper_color);
diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.cc
index 87bfb7ca0f7..745a2201dc1 100644
--- a/source/blender/editors/interface/interface_panel.c
+++ b/source/blender/editors/interface/interface_panel.cc
@@ -8,10 +8,10 @@
/* a full doc with API notes can be found in
* bf-blender/trunk/blender/doc/guides/interface_API.txt */
-#include <ctype.h>
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cctype>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -57,7 +57,7 @@
#define ANIMATION_TIME 0.30
#define ANIMATION_INTERVAL 0.02
-typedef enum uiPanelRuntimeFlag {
+enum uiPanelRuntimeFlag {
PANEL_LAST_ADDED = (1 << 0),
PANEL_ACTIVE = (1 << 2),
PANEL_WAS_ACTIVE = (1 << 3),
@@ -78,22 +78,22 @@ typedef enum uiPanelRuntimeFlag {
PANEL_IS_DRAG_DROP = (1 << 10),
/** Draw a border with the active color around the panel. */
PANEL_ACTIVE_BORDER = (1 << 11),
-} uiPanelRuntimeFlag;
+};
/* The state of the mouse position relative to the panel. */
-typedef enum uiPanelMouseState {
+enum uiPanelMouseState {
PANEL_MOUSE_OUTSIDE, /** Mouse is not in the panel. */
PANEL_MOUSE_INSIDE_CONTENT, /** Mouse is in the actual panel content. */
PANEL_MOUSE_INSIDE_HEADER, /** Mouse is in the panel header. */
-} uiPanelMouseState;
+};
-typedef enum uiHandlePanelState {
+enum uiHandlePanelState {
PANEL_STATE_DRAG,
PANEL_STATE_ANIMATION,
PANEL_STATE_EXIT,
-} uiHandlePanelState;
+};
-typedef struct uiHandlePanelData {
+struct uiHandlePanelData {
uiHandlePanelState state;
/* Animation. */
@@ -104,17 +104,17 @@ typedef struct uiHandlePanelData {
int startx, starty;
int startofsx, startofsy;
float start_cur_xmin, start_cur_ymin;
-} uiHandlePanelData;
+};
-typedef struct PanelSort {
+struct PanelSort {
Panel *panel;
int new_offset_x;
int new_offset_y;
-} PanelSort;
+};
static void panel_set_expansion_from_list_data(const bContext *C, Panel *panel);
static int get_panel_real_size_y(const Panel *panel);
-static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelState state);
+static void panel_activate_state(const bContext *C, Panel *panel, const uiHandlePanelState state);
static int compare_panel(const void *a, const void *b);
static bool panel_type_context_poll(ARegion *region,
const PanelType *panel_type,
@@ -155,7 +155,7 @@ static bool panel_active_animation_changed(ListBase *lb,
/* Detect animation. */
if (panel->activedata) {
- uiHandlePanelData *data = panel->activedata;
+ uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
if (data->state == PANEL_STATE_ANIMATION) {
*r_panel_animation = panel;
}
@@ -178,7 +178,7 @@ static bool panel_active_animation_changed(ListBase *lb,
static bool properties_space_needs_realign(const ScrArea *area, const ARegion *region)
{
if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) {
- SpaceProperties *sbuts = area->spacedata.first;
+ const SpaceProperties *sbuts = static_cast<SpaceProperties *>(area->spacedata.first);
if (sbuts->mainbo != sbuts->mainb) {
return true;
@@ -190,14 +190,14 @@ static bool properties_space_needs_realign(const ScrArea *area, const ARegion *r
static bool panels_need_realign(const ScrArea *area, ARegion *region, Panel **r_panel_animation)
{
- *r_panel_animation = NULL;
+ *r_panel_animation = nullptr;
if (properties_space_needs_realign(area, region)) {
return true;
}
/* Detect if a panel was added or removed. */
- Panel *panel_animation = NULL;
+ Panel *panel_animation = nullptr;
bool no_animation = false;
if (panel_active_animation_changed(&region->panels, &panel_animation, &no_animation)) {
return true;
@@ -225,7 +225,7 @@ static Panel *panel_add_instanced(ARegion *region,
PanelType *panel_type,
PointerRNA *custom_data)
{
- Panel *panel = MEM_callocN(sizeof(Panel), __func__);
+ Panel *panel = MEM_cnew<Panel>(__func__);
panel->type = panel_type;
BLI_strncpy(panel->panelname, panel_type->idname, sizeof(panel->panelname));
@@ -235,7 +235,7 @@ static Panel *panel_add_instanced(ARegion *region,
/* Add the panel's children too. Although they aren't instanced panels, we can still use this
* function to create them, as UI_panel_begin does other things we don't need to do. */
LISTBASE_FOREACH (LinkData *, child, &panel_type->children) {
- PanelType *child_type = child->data;
+ PanelType *child_type = static_cast<PanelType *>(child->data);
panel_add_instanced(region, &panel->children, child_type, custom_data);
}
@@ -265,12 +265,12 @@ Panel *UI_panel_add_instanced(const bContext *C,
{
ARegionType *region_type = region->type;
- PanelType *panel_type = BLI_findstring(
- &region_type->paneltypes, panel_idname, offsetof(PanelType, idname));
+ PanelType *panel_type = static_cast<PanelType *>(
+ BLI_findstring(&region_type->paneltypes, panel_idname, offsetof(PanelType, idname)));
- if (panel_type == NULL) {
+ if (panel_type == nullptr) {
printf("Panel type '%s' not found.\n", panel_idname);
- return NULL;
+ return nullptr;
}
Panel *new_panel = panel_add_instanced(region, panels, panel_type, custom_data);
@@ -314,14 +314,14 @@ void UI_panels_free_instanced(const bContext *C, ARegion *region)
{
/* Delete panels with the instanced flag. */
LISTBASE_FOREACH_MUTABLE (Panel *, panel, &region->panels) {
- if ((panel->type != NULL) && (panel->type->flag & PANEL_TYPE_INSTANCED)) {
+ if ((panel->type != nullptr) && (panel->type->flag & PANEL_TYPE_INSTANCED)) {
/* Make sure the panel's handler is removed before deleting it. */
- if (C != NULL && panel->activedata != NULL) {
+ if (C != nullptr && panel->activedata != nullptr) {
panel_activate_state(C, panel, PANEL_STATE_EXIT);
}
/* Free panel's custom data. */
- if (panel->runtime.custom_data_ptr != NULL) {
+ if (panel->runtime.custom_data_ptr != nullptr) {
MEM_freeN(panel->runtime.custom_data_ptr);
}
@@ -335,28 +335,28 @@ bool UI_panel_list_matches_data(ARegion *region,
ListBase *data,
uiListPanelIDFromDataFunc panel_idname_func)
{
- /* Check for NULL data. */
+ /* Check for nullptr data. */
int data_len = 0;
- Link *data_link = NULL;
- if (data == NULL) {
+ Link *data_link = nullptr;
+ if (data == nullptr) {
data_len = 0;
- data_link = NULL;
+ data_link = nullptr;
}
else {
data_len = BLI_listbase_count(data);
- data_link = data->first;
+ data_link = static_cast<Link *>(data->first);
}
int i = 0;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
- if (panel->type != NULL && panel->type->flag & PANEL_TYPE_INSTANCED) {
+ if (panel->type != nullptr && panel->type->flag & PANEL_TYPE_INSTANCED) {
/* The panels were reordered by drag and drop. */
if (panel->flag & PNL_INSTANCED_LIST_ORDER_CHANGED) {
return false;
}
/* We reached the last data item before the last instanced panel. */
- if (data_link == NULL) {
+ if (data_link == nullptr) {
return false;
}
@@ -383,15 +383,15 @@ bool UI_panel_list_matches_data(ARegion *region,
static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *drag_panel)
{
/* Without a type we cannot access the reorder callback. */
- if (drag_panel->type == NULL) {
+ if (drag_panel->type == nullptr) {
return;
}
/* Don't reorder if this instanced panel doesn't support drag and drop reordering. */
- if (drag_panel->type->reorder == NULL) {
+ if (drag_panel->type->reorder == nullptr) {
return;
}
- char *context = NULL;
+ char *context = nullptr;
if (!UI_panel_category_is_visible(region)) {
context = drag_panel->type->context;
}
@@ -415,7 +415,8 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr
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__);
+ PanelSort *panel_sort = static_cast<PanelSort *>(
+ MEM_callocN(list_panels_len * sizeof(*panel_sort), __func__));
PanelSort *sort_index = panel_sort;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->type) {
@@ -452,7 +453,7 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr
/* Finally, move this panel's list item to the new index in its list. */
drag_panel->type->reorder(C, drag_panel, move_to_index);
- CTX_store_set(C, NULL);
+ CTX_store_set(C, nullptr);
}
/**
@@ -480,9 +481,9 @@ static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag,
*/
static void panel_set_expansion_from_list_data(const bContext *C, Panel *panel)
{
- BLI_assert(panel->type != NULL);
+ BLI_assert(panel->type != nullptr);
BLI_assert(panel->type->flag & PANEL_TYPE_INSTANCED);
- if (panel->type->get_list_data_expand_flag == NULL) {
+ if (panel->type->get_list_data_expand_flag == nullptr) {
/* Instanced panel doesn't support loading expansion. */
return;
}
@@ -504,7 +505,7 @@ static void region_panels_set_expansion_from_list_data(const bContext *C, ARegio
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PANEL_ACTIVE) {
PanelType *panel_type = panel->type;
- if (panel_type != NULL && panel->type->flag & PANEL_TYPE_INSTANCED) {
+ if (panel_type != nullptr && panel->type->flag & PANEL_TYPE_INSTANCED) {
panel_set_expansion_from_list_data(C, panel);
}
}
@@ -537,7 +538,7 @@ static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *r
{
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
PanelType *panel_type = panel->type;
- if (panel_type == NULL) {
+ if (panel_type == nullptr) {
continue;
}
@@ -563,11 +564,11 @@ static bool panel_custom_data_active_get(const Panel *panel)
{
/* The caller should make sure the panel is active and has a type. */
BLI_assert(UI_panel_is_active(panel));
- BLI_assert(panel->type != NULL);
+ BLI_assert(panel->type != nullptr);
if (panel->type->active_property[0] != '\0') {
PointerRNA *ptr = UI_panel_custom_data_get(panel);
- if (ptr != NULL && !RNA_pointer_is_null(ptr)) {
+ if (ptr != nullptr && !RNA_pointer_is_null(ptr)) {
return RNA_boolean_get(ptr, panel->type->active_property);
}
}
@@ -579,12 +580,12 @@ static void panel_custom_data_active_set(Panel *panel)
{
/* Since the panel is interacted with, it should be active and have a type. */
BLI_assert(UI_panel_is_active(panel));
- BLI_assert(panel->type != NULL);
+ BLI_assert(panel->type != nullptr);
if (panel->type->active_property[0] != '\0') {
PointerRNA *ptr = UI_panel_custom_data_get(panel);
- BLI_assert(RNA_struct_find_property(ptr, panel->type->active_property) != NULL);
- if (ptr != NULL && !RNA_pointer_is_null(ptr)) {
+ BLI_assert(RNA_struct_find_property(ptr, panel->type->active_property) != nullptr);
+ if (ptr != nullptr && !RNA_pointer_is_null(ptr)) {
RNA_boolean_set(ptr, panel->type->active_property, true);
}
}
@@ -617,7 +618,7 @@ static void panel_set_runtime_flag_recursive(Panel *panel, short flag, bool valu
static void panels_collapse_all(ARegion *region, const Panel *from_panel)
{
const bool has_category_tabs = UI_panel_category_is_visible(region);
- const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : NULL;
+ const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : nullptr;
const PanelType *from_pt = from_panel->type;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
@@ -659,7 +660,7 @@ Panel *UI_panel_find_by_type(ListBase *lb, const PanelType *pt)
return panel;
}
}
- return NULL;
+ return nullptr;
}
Panel *UI_panel_begin(
@@ -668,10 +669,10 @@ Panel *UI_panel_begin(
Panel *panel_last;
const char *drawname = CTX_IFACE_(pt->translation_context, pt->label);
const char *idname = pt->idname;
- const bool newpanel = (panel == NULL);
+ const bool newpanel = (panel == nullptr);
if (newpanel) {
- panel = MEM_callocN(sizeof(Panel), __func__);
+ panel = MEM_cnew<Panel>(__func__);
panel->type = pt;
BLI_strncpy(panel->panelname, idname, sizeof(panel->panelname));
@@ -701,7 +702,7 @@ Panel *UI_panel_begin(
/* If a new panel is added, we insert it right after the panel that was last added.
* This way new panels are inserted in the right place between versions. */
- for (panel_last = lb->first; panel_last; panel_last = panel_last->next) {
+ for (panel_last = static_cast<Panel *>(lb->first); panel_last; panel_last = panel_last->next) {
if (panel_last->runtime_flag & PANEL_LAST_ADDED) {
BLI_remlink(lb, panel);
BLI_insertlinkafter(lb, panel_last, panel);
@@ -755,7 +756,7 @@ void UI_panel_header_buttons_end(Panel *panel)
/* A button group should always be created in #UI_panel_header_buttons_begin. */
BLI_assert(!BLI_listbase_is_empty(&block->button_groups));
- uiButtonGroup *button_group = block->button_groups.last;
+ uiButtonGroup *button_group = static_cast<uiButtonGroup *>(block->button_groups.last);
button_group->flag &= ~UI_BUTTON_GROUP_LOCK;
@@ -770,7 +771,7 @@ void UI_panel_header_buttons_end(Panel *panel)
/* Always add a new button group. Although this may result in many empty groups, without it,
* new buttons in the panel body not protected with a #ui_block_new_button_group call would
* end up in the panel header group. */
- ui_block_new_button_group(block, 0);
+ ui_block_new_button_group(block, (uiButtonGroupFlag)0);
}
}
@@ -867,7 +868,7 @@ void ui_panel_tag_search_filter_match(Panel *panel)
static void panel_matches_search_filter_recursive(const Panel *panel, bool *filter_matches)
{
- *filter_matches |= panel->runtime_flag & PANEL_SEARCH_FILTER_MATCH;
+ *filter_matches |= bool(panel->runtime_flag & PANEL_SEARCH_FILTER_MATCH);
/* If the panel has no match we need to make sure that its children are too. */
if (!*filter_matches) {
@@ -893,7 +894,7 @@ static void panel_set_expansion_from_search_filter_recursive(const bContext *C,
{
/* This has to run on inactive panels that may not have a type,
* but we can prevent running on header-less panels in some cases. */
- if (panel->type == NULL || !(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
+ if (panel->type == nullptr || !(panel->type->flag & PANEL_TYPE_NO_HEADER)) {
SET_FLAG_FROM_TEST(panel->runtime_flag, use_search_closed, PANEL_USE_CLOSED_FROM_SEARCH);
}
@@ -926,9 +927,9 @@ static void region_panels_set_expansion_from_search_filter(const bContext *C,
static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *parent_panel)
{
uiBlock *block = panel->runtime.block;
- BLI_assert(block != NULL);
+ BLI_assert(block != nullptr);
BLI_assert(block->active);
- if (parent_panel != NULL && UI_panel_is_closed(parent_panel)) {
+ if (parent_panel != nullptr && UI_panel_is_closed(parent_panel)) {
/* The parent panel is closed, so this panel can be completely removed. */
UI_block_set_search_only(block, true);
LISTBASE_FOREACH (uiBut *, but, &block->buttons) {
@@ -944,7 +945,7 @@ static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *
continue;
}
LISTBASE_FOREACH (LinkData *, link, &button_group->buttons) {
- uiBut *but = link->data;
+ uiBut *but = static_cast<uiBut *>(link->data);
but->flag |= UI_HIDDEN;
}
}
@@ -952,7 +953,7 @@ static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *
LISTBASE_FOREACH (Panel *, child_panel, &panel->children) {
if (child_panel->runtime_flag & PANEL_ACTIVE) {
- BLI_assert(child_panel->runtime.block != NULL);
+ BLI_assert(child_panel->runtime.block != nullptr);
panel_remove_invisible_layouts_recursive(child_panel, panel);
}
}
@@ -962,8 +963,8 @@ static void region_panels_remove_invisible_layouts(ARegion *region)
{
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PANEL_ACTIVE) {
- BLI_assert(panel->runtime.block != NULL);
- panel_remove_invisible_layouts_recursive(panel, NULL);
+ BLI_assert(panel->runtime.block != nullptr);
+ panel_remove_invisible_layouts_recursive(panel, nullptr);
}
}
}
@@ -1054,7 +1055,7 @@ static void panel_draw_highlight_border(const Panel *panel,
const rcti *rect,
const rcti *header_rect)
{
- const bool is_subpanel = panel->type->parent != NULL;
+ const bool is_subpanel = panel->type->parent != nullptr;
if (is_subpanel) {
return;
}
@@ -1064,18 +1065,15 @@ static void panel_draw_highlight_border(const Panel *panel,
const float radius = (btheme->tui.panel_roundness * U.widget_unit * 0.5f) / aspect;
UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ rctf box_rect;
+ box_rect.xmin = rect->xmin;
+ box_rect.xmax = rect->xmax;
+ box_rect.ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin;
+ box_rect.ymax = header_rect->ymax;
+
float color[4];
UI_GetThemeColor4fv(TH_SELECT_ACTIVE, color);
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = rect->xmin,
- .xmax = rect->xmax,
- .ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin,
- .ymax = header_rect->ymax,
- },
- false,
- radius,
- color);
+ UI_draw_roundbox_4fv(&box_rect, false, radius, color);
}
static void panel_draw_aligned_widgets(const uiStyle *style,
@@ -1086,19 +1084,18 @@ static void panel_draw_aligned_widgets(const uiStyle *style,
const bool show_background,
const bool region_search_filter_active)
{
- const bool is_subpanel = panel->type->parent != NULL;
+ const bool is_subpanel = panel->type->parent != nullptr;
const uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle;
const int header_height = BLI_rcti_size_y(header_rect);
const int scaled_unit = round_fl_to_int(UI_UNIT_X / aspect);
- /* Offset triangle and text to the right for subpanels. */
- const rcti widget_rect = {
- .xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0),
- .xmax = header_rect->xmax,
- .ymin = header_rect->ymin,
- .ymax = header_rect->ymax,
- };
+ /* Offset triangle and text to the right for sub-panels. */
+ rcti widget_rect;
+ widget_rect.xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0);
+ widget_rect.xmax = header_rect->xmax;
+ widget_rect.ymin = header_rect->ymin;
+ widget_rect.ymax = header_rect->ymax;
uchar title_color[4];
panel_title_color_get(panel, show_background, region_search_filter_active, title_color);
@@ -1121,20 +1118,16 @@ static void panel_draw_aligned_widgets(const uiStyle *style,
/* Draw text label. */
if (panel->drawname[0] != '\0') {
- const rcti title_rect = {
- .xmin = widget_rect.xmin + (panel->labelofs / aspect) + scaled_unit * 1.1f,
- .xmax = widget_rect.xmax,
- .ymin = widget_rect.ymin - 2.0f / aspect,
- .ymax = widget_rect.ymax,
- };
- UI_fontstyle_draw(fontstyle,
- &title_rect,
- panel->drawname,
- sizeof(panel->drawname),
- title_color,
- &(struct uiFontStyleDraw_Params){
- .align = UI_STYLE_TEXT_LEFT,
- });
+ rcti title_rect;
+ title_rect.xmin = widget_rect.xmin + (panel->labelofs / aspect) + scaled_unit * 1.1f;
+ title_rect.xmax = widget_rect.xmax;
+ title_rect.ymin = widget_rect.ymin - 2.0f / aspect;
+ title_rect.ymax = widget_rect.ymax;
+
+ uiFontStyleDraw_Params params{};
+ params.align = UI_STYLE_TEXT_LEFT;
+ UI_fontstyle_draw(
+ fontstyle, &title_rect, panel->drawname, sizeof(panel->drawname), title_color, &params);
}
/* Draw the pin icon. */
@@ -1167,7 +1160,7 @@ static void panel_draw_aligned_widgets(const uiStyle *style,
GPUBatch *batch = GPU_batch_preset_panel_drag_widget(
U.pixelsize, color_high, color_dark, drag_widget_size);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_FLAT_COLOR);
GPU_batch_draw(batch);
GPU_matrix_pop();
}
@@ -1177,7 +1170,7 @@ static void panel_draw_aligned_backdrop(const Panel *panel,
const rcti *rect,
const rcti *header_rect)
{
- const bool is_subpanel = panel->type->parent != NULL;
+ const bool is_subpanel = panel->type->parent != nullptr;
const bool is_open = !UI_panel_is_closed(panel);
if (is_subpanel && !is_open) {
@@ -1188,7 +1181,7 @@ static void panel_draw_aligned_backdrop(const Panel *panel,
const float aspect = panel->runtime.block->aspect;
const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f / aspect;
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
/* Panel backdrop. */
@@ -1197,16 +1190,12 @@ static void panel_draw_aligned_backdrop(const Panel *panel,
UI_draw_roundbox_corner_set(is_open ? UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT : UI_CNR_ALL);
UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor);
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = rect->xmin,
- .xmax = rect->xmax,
- .ymin = rect->ymin,
- .ymax = rect->ymax,
- },
- true,
- radius,
- panel_backcolor);
+ rctf box_rect;
+ box_rect.xmin = rect->xmin;
+ box_rect.xmax = rect->xmax;
+ box_rect.ymin = rect->ymin;
+ box_rect.ymax = rect->ymax;
+ UI_draw_roundbox_4fv(&box_rect, true, radius, panel_backcolor);
}
/* Panel header backdrops for non sub-panels. */
@@ -1217,16 +1206,12 @@ static void panel_draw_aligned_backdrop(const Panel *panel,
UI_draw_roundbox_corner_set(is_open ? UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT : UI_CNR_ALL);
/* Change the width a little bit to line up with the sides. */
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = rect->xmin,
- .xmax = rect->xmax,
- .ymin = header_rect->ymin,
- .ymax = header_rect->ymax,
- },
- true,
- radius,
- panel_headercolor);
+ rctf box_rect;
+ box_rect.xmin = rect->xmin;
+ box_rect.xmax = rect->xmax;
+ box_rect.ymin = header_rect->ymin;
+ box_rect.ymax = header_rect->ymax;
+ UI_draw_roundbox_4fv(&box_rect, true, radius, panel_headercolor);
}
GPU_blend(GPU_BLEND_NONE);
@@ -1247,7 +1232,7 @@ void ui_draw_aligned_panel(const uiStyle *style,
rect->xmin,
rect->xmax,
rect->ymax,
- rect->ymax + floor(PNL_HEADER / block->aspect + 0.001f),
+ rect->ymax + (int)floor(PNL_HEADER / block->aspect + 0.001f),
};
if (show_background) {
@@ -1300,7 +1285,7 @@ bool UI_panel_should_show_background(const ARegion *region, const PanelType *pan
void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
{
// #define USE_FLAT_INACTIVE
- const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT);
+ const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT;
View2D *v2d = &region->v2d;
const uiStyle *style = UI_style_get();
const uiFontStyle *fstyle = &style->widget;
@@ -1399,7 +1384,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw the background. */
if (is_alpha) {
@@ -1444,7 +1429,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
if (is_active == false && is_active_prev == false && pc_dyn->prev) {
pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fvAlpha(theme_col_tab_outline, 0.3f);
immRecti(pos,
is_left ? v2d->mask.xmin + (category_tabs_width / 5) :
@@ -1463,31 +1448,22 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
{
/* Draw filled rectangle and outline for tab. */
UI_draw_roundbox_corner_set(roundboxtype);
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = rct->xmin,
- .xmax = rct->xmax,
- .ymin = rct->ymin,
- .ymax = rct->ymax,
- },
- true,
- tab_curve_radius,
- is_active ? theme_col_tab_active : theme_col_tab_inactive);
- UI_draw_roundbox_4fv(
- &(const rctf){
- .xmin = rct->xmin,
- .xmax = rct->xmax,
- .ymin = rct->ymin,
- .ymax = rct->ymax,
- },
- false,
- tab_curve_radius,
- theme_col_tab_outline);
+ rctf box_rect;
+ box_rect.xmin = rct->xmin;
+ box_rect.xmax = rct->xmax;
+ box_rect.ymin = rct->ymin;
+ box_rect.ymax = rct->ymax;
+
+ UI_draw_roundbox_4fv(&box_rect,
+ true,
+ tab_curve_radius,
+ is_active ? theme_col_tab_active : theme_col_tab_inactive);
+ UI_draw_roundbox_4fv(&box_rect, false, tab_curve_radius, theme_col_tab_outline);
/* Disguise the outline on one side to join the tab to the panel. */
pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(is_active ? theme_col_tab_active : theme_col_tab_inactive);
immRecti(pos,
@@ -1502,7 +1478,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active)
if (do_scaletabs) {
category_draw_len = BLF_width_to_strlen(
- fontid, category_id_draw, category_draw_len, category_width, NULL);
+ fontid, category_id_draw, category_draw_len, category_width, nullptr);
}
BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f);
@@ -1660,7 +1636,7 @@ static bool uiAlignPanelStep(ARegion *region, const float factor, const bool dra
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PANEL_ACTIVE) {
/* These panels should have types since they are currently displayed to the user. */
- BLI_assert(panel->type != NULL);
+ BLI_assert(panel->type != nullptr);
active_panels_len++;
}
}
@@ -1669,7 +1645,8 @@ static bool uiAlignPanelStep(ARegion *region, const float factor, const bool dra
}
/* Sort panels. */
- PanelSort *panel_sort = MEM_mallocN(sizeof(PanelSort) * active_panels_len, __func__);
+ PanelSort *panel_sort = static_cast<PanelSort *>(
+ MEM_mallocN(sizeof(PanelSort) * active_panels_len, __func__));
{
PanelSort *ps = panel_sort;
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
@@ -1791,7 +1768,7 @@ static void ui_panels_size(ARegion *region, int *r_x, int *r_y)
static void ui_do_animate(bContext *C, Panel *panel)
{
- uiHandlePanelData *data = panel->activedata;
+ uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
ARegion *region = CTX_wm_region(C);
float fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME;
@@ -1856,7 +1833,7 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
LISTBASE_FOREACH (Panel *, panel, &region->panels) {
if (panel->runtime_flag & PANEL_ACTIVE) {
- BLI_assert(panel->runtime.block != NULL);
+ BLI_assert(panel->runtime.block != nullptr);
panel_calculate_size_recursive(region, panel);
}
}
@@ -1892,7 +1869,7 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y)
#define DRAG_REGION_PAD (PNL_HEADER * 0.5)
static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel)
{
- uiHandlePanelData *data = panel->activedata;
+ uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
ARegion *region = CTX_wm_region(C);
/* Keep the drag position in the region with a small pad to keep the panel visible. */
@@ -1942,14 +1919,14 @@ static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block,
return PANEL_MOUSE_OUTSIDE;
}
-typedef struct uiPanelDragCollapseHandle {
+struct uiPanelDragCollapseHandle {
bool was_first_open;
int xy_init[2];
-} uiPanelDragCollapseHandle;
+};
static void ui_panel_drag_collapse_handler_remove(bContext *UNUSED(C), void *userdata)
{
- uiPanelDragCollapseHandle *dragcol_data = userdata;
+ uiPanelDragCollapseHandle *dragcol_data = static_cast<uiPanelDragCollapseHandle *>(userdata);
MEM_freeN(dragcol_data);
}
@@ -1960,11 +1937,11 @@ static void ui_panel_drag_collapse(const bContext *C,
ARegion *region = CTX_wm_region(C);
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
- float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)};
- float xy_b_block[2] = {UNPACK2(xy_dst)};
+ float xy_a_block[2] = {(float)dragcol_data->xy_init[0], (float)dragcol_data->xy_init[1]};
+ float xy_b_block[2] = {(float)xy_dst[0], (float)xy_dst[1]};
Panel *panel = block->panel;
- if (panel == NULL || (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER))) {
+ if (panel == nullptr || (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER))) {
continue;
}
const int oldflag = panel->flag;
@@ -2006,7 +1983,7 @@ static void ui_panel_drag_collapse(const bContext *C,
static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, void *userdata)
{
wmWindow *win = CTX_wm_window(C);
- uiPanelDragCollapseHandle *dragcol_data = userdata;
+ uiPanelDragCollapseHandle *dragcol_data = static_cast<uiPanelDragCollapseHandle *>(userdata);
short retval = WM_UI_HANDLER_CONTINUE;
switch (event->type) {
@@ -2037,7 +2014,7 @@ static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was
{
wmWindow *win = CTX_wm_window(C);
const wmEvent *event = win->eventstate;
- uiPanelDragCollapseHandle *dragcol_data = MEM_mallocN(sizeof(*dragcol_data), __func__);
+ uiPanelDragCollapseHandle *dragcol_data = MEM_new<uiPanelDragCollapseHandle>(__func__);
dragcol_data->was_first_open = was_open;
copy_v2_v2_int(dragcol_data->xy_init, event->xy);
@@ -2066,10 +2043,10 @@ static void ui_handle_panel_header(const bContext *C,
Panel *panel = block->panel;
ARegion *region = CTX_wm_region(C);
- BLI_assert(panel->type != NULL);
+ BLI_assert(panel->type != nullptr);
BLI_assert(!(panel->type->flag & PANEL_TYPE_NO_HEADER));
- const bool is_subpanel = (panel->type->parent != NULL);
+ const bool is_subpanel = (panel->type->parent != nullptr);
const bool use_pin = UI_panel_category_is_visible(region) && UI_panel_can_be_pinned(panel);
const bool show_pin = use_pin && (panel->flag & PNL_PIN);
const bool show_drag = !is_subpanel;
@@ -2102,8 +2079,8 @@ static void ui_handle_panel_header(const bContext *C,
else {
/* If a panel has sub-panels and it's open, toggle the expansion
* of the sub-panels (based on the expansion of the first sub-panel). */
- Panel *first_child = panel->children.first;
- BLI_assert(first_child != NULL);
+ Panel *first_child = static_cast<Panel *>(panel->children.first);
+ BLI_assert(first_child != nullptr);
panel_set_flag_recursive(panel, PNL_CLOSED, !UI_panel_is_closed(first_child));
panel->flag |= PNL_CLOSED;
}
@@ -2115,7 +2092,7 @@ static void ui_handle_panel_header(const bContext *C,
ui_panel_drag_collapse_handler_add(C, UI_panel_is_closed(panel));
}
- /* Set panel custom data (modifier) active when expanding subpanels, but not top-level
+ /* Set panel custom data (modifier) active when expanding sub-panels, but not top-level
* panels to allow collapsing and expanding without setting the active element. */
if (is_subpanel) {
panel_custom_data_active_set(panel);
@@ -2157,13 +2134,14 @@ bool UI_panel_category_is_visible(const ARegion *region)
PanelCategoryDyn *UI_panel_category_find(const ARegion *region, const char *idname)
{
- return BLI_findstring(&region->panels_category, idname, offsetof(PanelCategoryDyn, idname));
+ return static_cast<PanelCategoryDyn *>(
+ BLI_findstring(&region->panels_category, idname, offsetof(PanelCategoryDyn, idname)));
}
PanelCategoryStack *UI_panel_category_active_find(ARegion *region, const char *idname)
{
- return BLI_findstring(
- &region->panels_category_active, idname, offsetof(PanelCategoryStack, idname));
+ return static_cast<PanelCategoryStack *>(BLI_findstring(
+ &region->panels_category_active, idname, offsetof(PanelCategoryStack, idname)));
}
static void ui_panel_category_active_set(ARegion *region, const char *idname, bool fallback)
@@ -2175,7 +2153,7 @@ static void ui_panel_category_active_set(ARegion *region, const char *idname, bo
BLI_remlink(lb, pc_act);
}
else {
- pc_act = MEM_callocN(sizeof(PanelCategoryStack), __func__);
+ pc_act = MEM_cnew<PanelCategoryStack>(__func__);
BLI_strncpy(pc_act->idname, idname, sizeof(pc_act->idname));
}
@@ -2226,14 +2204,14 @@ const char *UI_panel_category_active_get(ARegion *region, bool set_fallback)
}
if (set_fallback) {
- PanelCategoryDyn *pc_dyn = region->panels_category.first;
+ PanelCategoryDyn *pc_dyn = static_cast<PanelCategoryDyn *>(region->panels_category.first);
if (pc_dyn) {
ui_panel_category_active_set(region, pc_dyn->idname, true);
return pc_dyn->idname;
}
}
- return NULL;
+ return nullptr;
}
static PanelCategoryDyn *panel_categories_find_mouse_over(ARegion *region, const wmEvent *event)
@@ -2244,12 +2222,12 @@ static PanelCategoryDyn *panel_categories_find_mouse_over(ARegion *region, const
}
}
- return NULL;
+ return nullptr;
}
void UI_panel_category_add(ARegion *region, const char *name)
{
- PanelCategoryDyn *pc_dyn = MEM_callocN(sizeof(*pc_dyn), __func__);
+ PanelCategoryDyn *pc_dyn = MEM_cnew<PanelCategoryDyn>(__func__);
BLI_addtail(&region->panels_category, pc_dyn);
BLI_strncpy(pc_dyn->idname, name, sizeof(pc_dyn->idname));
@@ -2294,7 +2272,8 @@ static int ui_handle_panel_category_cycling(const wmEvent *event,
pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next;
if (!pc_dyn) {
/* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */
- pc_dyn = backwards ? region->panels_category.last : region->panels_category.first;
+ pc_dyn = backwards ? static_cast<PanelCategoryDyn *>(region->panels_category.last) :
+ static_cast<PanelCategoryDyn *>(region->panels_category.first);
}
}
@@ -2318,7 +2297,7 @@ int ui_handler_panel_region(bContext *C,
const uiBut *active_but)
{
/* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
return WM_UI_HANDLER_CONTINUE;
}
@@ -2359,11 +2338,11 @@ int ui_handler_panel_region(bContext *C,
return retval;
}
- const bool region_has_active_button = (ui_region_find_active_but(region) != NULL);
+ const bool region_has_active_button = (ui_region_find_active_but(region) != nullptr);
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
Panel *panel = block->panel;
- if (panel == NULL || panel->type == NULL) {
+ if (panel == nullptr || panel->type == nullptr) {
continue;
}
/* We can't expand or collapse panels without headers, they would disappear. */
@@ -2434,10 +2413,10 @@ void UI_panel_context_pointer_set(Panel *panel, const char *name, PointerRNA *pt
void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data)
{
- BLI_assert(panel->type != NULL);
+ BLI_assert(panel->type != nullptr);
/* Free the old custom data, which should be shared among all of the panel's sub-panels. */
- if (panel->runtime.custom_data_ptr != NULL) {
+ if (panel->runtime.custom_data_ptr != nullptr) {
MEM_freeN(panel->runtime.custom_data_ptr);
}
@@ -2455,7 +2434,7 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm
LISTBASE_FOREACH (uiBlock *, block, &region->uiblocks) {
Panel *panel = block->panel;
- if (panel == NULL) {
+ if (panel == nullptr) {
continue;
}
@@ -2468,12 +2447,12 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm
}
}
- return NULL;
+ return nullptr;
}
bool UI_panel_can_be_pinned(const Panel *panel)
{
- return (panel->type->parent == NULL) && !(panel->type->flag & PANEL_TYPE_INSTANCED);
+ return (panel->type->parent == nullptr) && !(panel->type->flag & PANEL_TYPE_INSTANCED);
}
/** \} */
@@ -2485,8 +2464,8 @@ bool UI_panel_can_be_pinned(const Panel *panel)
/* NOTE: this is modal handler and should not swallow events for animation. */
static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
{
- Panel *panel = userdata;
- uiHandlePanelData *data = panel->activedata;
+ Panel *panel = static_cast<Panel *>(userdata);
+ uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
/* Verify if we can stop. */
if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
@@ -2506,7 +2485,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
}
}
- data = panel->activedata;
+ data = static_cast<uiHandlePanelData *>(panel->activedata);
if (data && data->state == PANEL_STATE_ANIMATION) {
return WM_UI_HANDLER_CONTINUE;
@@ -2516,7 +2495,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata)
static void ui_handler_remove_panel(bContext *C, void *userdata)
{
- Panel *panel = userdata;
+ Panel *panel = static_cast<Panel *>(userdata);
panel_activate_state(C, panel, PANEL_STATE_EXIT);
}
@@ -2527,13 +2506,13 @@ static void panel_handle_data_ensure(const bContext *C,
Panel *panel,
const uiHandlePanelState state)
{
- if (panel->activedata == NULL) {
+ if (panel->activedata == nullptr) {
panel->activedata = MEM_callocN(sizeof(uiHandlePanelData), __func__);
WM_event_add_ui_handler(
C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, panel, 0);
}
- uiHandlePanelData *data = panel->activedata;
+ uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
data->animtimer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, ANIMATION_INTERVAL);
@@ -2554,11 +2533,11 @@ static void panel_handle_data_ensure(const bContext *C,
*/
static void panel_activate_state(const bContext *C, Panel *panel, const uiHandlePanelState state)
{
- uiHandlePanelData *data = panel->activedata;
+ uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata);
wmWindow *win = CTX_wm_window(C);
ARegion *region = CTX_wm_region(C);
- if (data != NULL && data->state == state) {
+ if (data != nullptr && data->state == state) {
return;
}
@@ -2582,15 +2561,15 @@ static void panel_activate_state(const bContext *C, Panel *panel, const uiHandle
else if (state == PANEL_STATE_EXIT) {
panel_set_runtime_flag_recursive(panel, PANEL_IS_DRAG_DROP, false);
- BLI_assert(data != NULL);
+ BLI_assert(data != nullptr);
if (data->animtimer) {
WM_event_remove_timer(CTX_wm_manager(C), win, data->animtimer);
- data->animtimer = NULL;
+ data->animtimer = nullptr;
}
MEM_freeN(data);
- panel->activedata = NULL;
+ panel->activedata = nullptr;
WM_event_remove_ui_handler(
&win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, panel, false);
diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc
index 71cf60985df..f084f3e06cb 100644
--- a/source/blender/editors/interface/interface_query.cc
+++ b/source/blender/editors/interface/interface_query.cc
@@ -54,13 +54,7 @@ bool ui_but_is_toggle(const uiBut *but)
UI_BTYPE_TOGGLE_N,
UI_BTYPE_CHECKBOX,
UI_BTYPE_CHECKBOX_N,
- UI_BTYPE_ROW,
- UI_BTYPE_TREEROW);
-}
-
-bool ui_but_is_view_item(const uiBut *but)
-{
- return ELEM(but->type, UI_BTYPE_TREEROW, UI_BTYPE_GRID_TILE);
+ UI_BTYPE_ROW);
}
bool ui_but_is_interactive_ex(const uiBut *but, const bool labeledit, const bool for_tooltip)
@@ -462,14 +456,9 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut
return ui_but_find(region, ui_but_is_listrow_at_index, &data);
}
-static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata))
-{
- return but->type == UI_BTYPE_TREEROW;
-}
-
static bool ui_but_is_view_item_fn(const uiBut *but, const void *UNUSED(customdata))
{
- return ui_but_is_view_item(but);
+ return but->type == UI_BTYPE_VIEW_ITEM;
}
uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2])
@@ -477,24 +466,19 @@ uiBut *ui_view_item_find_mouse_over(const ARegion *region, const int xy[2])
return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_view_item_fn, nullptr);
}
-uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2])
-{
- return ui_but_find_mouse_over_ex(region, xy, false, false, ui_but_is_treerow, nullptr);
-}
-
-static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata)
+static bool ui_but_is_active_view_item(const uiBut *but, const void *UNUSED(customdata))
{
- if (!ui_but_is_treerow(but, customdata)) {
+ if (but->type != UI_BTYPE_VIEW_ITEM) {
return false;
}
- const uiButTreeRow *treerow_but = (const uiButTreeRow *)but;
- return UI_tree_view_item_is_active(treerow_but->tree_item);
+ const uiButViewItem *view_item_but = (const uiButViewItem *)but;
+ return UI_view_item_is_active(view_item_but->view_item);
}
-uiBut *ui_tree_row_find_active(const ARegion *region)
+uiBut *ui_view_item_find_active(const ARegion *region)
{
- return ui_but_find(region, ui_but_is_active_treerow, nullptr);
+ return ui_but_find(region, ui_but_is_active_view_item, nullptr);
}
/** \} */
diff --git a/source/blender/editors/interface/interface_region_color_picker.cc b/source/blender/editors/interface/interface_region_color_picker.cc
index db1e5e653de..72912b8c7f7 100644
--- a/source/blender/editors/interface/interface_region_color_picker.cc
+++ b/source/blender/editors/interface/interface_region_color_picker.cc
@@ -52,10 +52,10 @@ static void ui_color_picker_rgb_round(float rgb[3])
* all color space conversions would be expensive, but for the color picker
* we can do the extra work. */
for (int i = 0; i < 3; i++) {
- if (fabsf(rgb[i]) < 1e-6f) {
+ if (fabsf(rgb[i]) < 5e-5f) {
rgb[i] = 0.0f;
}
- else if (fabsf(1.0f - rgb[i]) < 1e-6f) {
+ else if (fabsf(1.0f - rgb[i]) < 5e-5f) {
rgb[i] = 1.0f;
}
}
diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc
index b11564f09c5..becdfaf4e25 100644
--- a/source/blender/editors/interface/interface_region_menu_pie.cc
+++ b/source/blender/editors/interface/interface_region_menu_pie.cc
@@ -27,6 +27,7 @@
#include "WM_types.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
@@ -36,7 +37,7 @@
#include "ED_screen.h"
#include "interface_intern.h"
-#include "interface_regions_intern.h"
+#include "interface_regions_intern.hh"
/* -------------------------------------------------------------------- */
/** \name Pie Menu
@@ -371,7 +372,7 @@ void ui_pie_menu_level_create(uiBlock *block,
EnumPropertyItem *remaining = static_cast<EnumPropertyItem *>(
MEM_mallocN(array_size + sizeof(EnumPropertyItem), "pie_level_item_array"));
memcpy(remaining, items + totitem_parent, array_size);
- /* A nullptr terminating sentinel element is required. */
+ /* A null terminating sentinel element is required. */
memset(&remaining[totitem_remain], 0, sizeof(EnumPropertyItem));
/* yuk, static... issue is we can't reliably free this without doing dangerous changes */
diff --git a/source/blender/editors/interface/interface_region_menu_popup.cc b/source/blender/editors/interface/interface_region_menu_popup.cc
index a22f7218203..f88cabb2b70 100644
--- a/source/blender/editors/interface/interface_region_menu_popup.cc
+++ b/source/blender/editors/interface/interface_region_menu_popup.cc
@@ -39,7 +39,7 @@
#include "ED_screen.h"
#include "interface_intern.h"
-#include "interface_regions_intern.h"
+#include "interface_regions_intern.hh"
/* -------------------------------------------------------------------- */
/** \name Utility Functions
@@ -322,7 +322,7 @@ uiPopupBlockHandle *ui_popup_menu_create(
uiPopupMenu *pup = MEM_cnew<uiPopupMenu>(__func__);
pup->block = UI_block_begin(C, nullptr, __func__, UI_EMBOSS_PULLDOWN);
- pup->block->flag |= UI_BLOCK_NUMSELECT; /* default menus to numselect */
+ pup->block->flag |= UI_BLOCK_NUMSELECT; /* Default menus to numeric-selection. */
pup->layout = UI_block_layout(
pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style);
pup->slideout = but ? ui_block_is_menu(but->block) : false;
diff --git a/source/blender/editors/interface/interface_region_popover.cc b/source/blender/editors/interface/interface_region_popover.cc
index 2e10261a4f7..17c8d890755 100644
--- a/source/blender/editors/interface/interface_region_popover.cc
+++ b/source/blender/editors/interface/interface_region_popover.cc
@@ -46,7 +46,7 @@
#include "UI_interface.h"
#include "interface_intern.h"
-#include "interface_regions_intern.h"
+#include "interface_regions_intern.hh"
/* -------------------------------------------------------------------- */
/** \name Popup Menu with Callback or String
@@ -397,7 +397,7 @@ void UI_popover_end(bContext *C, uiPopover *pup, wmKeyMap *keymap)
pup->window = window;
- /* TODO(campbell): we may want to make this configurable.
+ /* TODO(@campbellbarton): we may want to make this configurable.
* The begin/end stype of calling popups doesn't allow 'can_refresh' to be set.
* For now close this style of popovers when accessed. */
UI_block_flag_disable(pup->block, UI_BLOCK_KEEP_OPEN);
diff --git a/source/blender/editors/interface/interface_region_popup.cc b/source/blender/editors/interface/interface_region_popup.cc
index 74c228e3338..daa46b150a3 100644
--- a/source/blender/editors/interface/interface_region_popup.cc
+++ b/source/blender/editors/interface/interface_region_popup.cc
@@ -31,7 +31,7 @@
#include "ED_screen.h"
#include "interface_intern.h"
-#include "interface_regions_intern.h"
+#include "interface_regions_intern.hh"
/* -------------------------------------------------------------------- */
/** \name Utility Functions
@@ -397,7 +397,7 @@ static void ui_block_region_draw(const bContext *C, ARegion *region)
static void ui_block_region_popup_window_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
switch (wmn->category) {
case NC_WINDOW: {
diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc
index 81c0c29d09a..fb7810792e9 100644
--- a/source/blender/editors/interface/interface_region_search.cc
+++ b/source/blender/editors/interface/interface_region_search.cc
@@ -10,6 +10,7 @@
#include <cstdarg>
#include <cstdlib>
#include <cstring>
+#include <iostream>
#include "DNA_ID.h"
#include "MEM_guardedalloc.h"
@@ -41,7 +42,7 @@
#include "GPU_state.h"
#include "interface_intern.h"
-#include "interface_regions_intern.h"
+#include "interface_regions_intern.hh"
#define MENU_BORDER (int)(0.3f * U.widget_unit)
@@ -86,6 +87,10 @@ struct uiSearchboxData {
* Used so we can show leading text to menu items less prominently (not related to 'use_sep').
*/
const char *sep_string;
+
+ /* Owned by uiButSearch */
+ void *search_arg;
+ uiButSearchListenFn search_listener;
};
#define SEARCH_ITEMS 10
@@ -689,6 +694,14 @@ static void ui_searchbox_region_free_fn(ARegion *region)
region->regiondata = nullptr;
}
+static void ui_searchbox_region_listen_fn(const wmRegionListenerParams *params)
+{
+ uiSearchboxData *data = static_cast<uiSearchboxData *>(params->region->regiondata);
+ if (data->search_listener) {
+ data->search_listener(params, data->search_arg);
+ }
+}
+
static ARegion *ui_searchbox_create_generic_ex(bContext *C,
ARegion *butregion,
uiButSearch *search_but,
@@ -707,21 +720,24 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C,
memset(&type, 0, sizeof(ARegionType));
type.draw = ui_searchbox_region_draw_fn;
type.free = ui_searchbox_region_free_fn;
+ type.listener = ui_searchbox_region_listen_fn;
type.regionid = RGN_TYPE_TEMPORARY;
region->type = &type;
- /* create searchbox data */
+ /* Create search-box data. */
uiSearchboxData *data = MEM_cnew<uiSearchboxData>(__func__);
+ data->search_arg = search_but->arg;
+ data->search_listener = search_but->listen_fn;
- /* set font, get bb */
+ /* Set font, get the bounding-box. */
data->fstyle = style->widget; /* copy struct */
ui_fontscale(&data->fstyle.points, aspect);
UI_fontstyle_set(&data->fstyle);
region->regiondata = data;
- /* special case, hardcoded feature, not draw backdrop when called from menus,
- * assume for design that popup already added it */
+ /* Special case, hard-coded feature, not draw backdrop when called from menus,
+ * assume for design that popup already added it. */
if (but->block->flag & UI_BLOCK_SEARCH_MENU) {
data->noback = true;
}
diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.cc
index 88fe866f717..a6e37d3f36f 100644
--- a/source/blender/editors/interface/interface_region_tooltip.c
+++ b/source/blender/editors/interface/interface_region_tooltip.cc
@@ -7,7 +7,7 @@
* ToolTip Region and Construction
*/
-/* TODO(campbell):
+/* TODO(@campbellbarton):
* We may want to have a higher level API that initializes a timer,
* checks for mouse motion and clears the tool-tip afterwards.
* We never want multiple tool-tips at once
@@ -16,9 +16,9 @@
* For now it's not a priority, so leave as-is.
*/
-#include <stdarg.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstdarg>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -39,6 +39,7 @@
#include "WM_types.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "UI_interface.h"
@@ -52,7 +53,7 @@
#include "ED_screen.h"
#include "interface_intern.h"
-#include "interface_regions_intern.h"
+#include "interface_regions_intern.hh"
#define UI_TIP_PAD_FAC 1.3f
#define UI_TIP_PADDING (int)(UI_TIP_PAD_FAC * UI_UNIT_Y)
@@ -60,59 +61,82 @@
#define UI_TIP_STR_MAX 1024
-typedef struct uiTooltipFormat {
- enum {
- UI_TIP_STYLE_NORMAL = 0,
- UI_TIP_STYLE_HEADER,
- UI_TIP_STYLE_MONO,
- } style : 3;
- enum {
- UI_TIP_LC_MAIN = 0, /* primary text */
- UI_TIP_LC_VALUE, /* the value of buttons (also shortcuts) */
- UI_TIP_LC_ACTIVE, /* titles of active enum values */
- UI_TIP_LC_NORMAL, /* regular text */
- UI_TIP_LC_PYTHON, /* Python snippet */
- UI_TIP_LC_ALERT, /* description of why operator can't run */
- } color_id : 4;
- int is_pad : 1;
-} uiTooltipFormat;
-
-typedef struct uiTooltipField {
+struct uiTooltipFormat {
+ enum class Style : int8_t {
+ Normal,
+ Header,
+ Mono,
+ };
+ enum class ColorID : int8_t {
+ /** Primary Text. */
+ Main = 0,
+ /** The value of buttons (also shortcuts). */
+ Value = 1,
+ /** Titles of active enum values. */
+ Active = 2,
+ /** Regular text. */
+ Normal = 3,
+ /** Python snippet. */
+ Python = 4,
+ /** Description of why an operator can't run. */
+ Alert = 5,
+ };
+ Style style;
+ ColorID color_id;
+ bool is_pad;
+};
+
+struct uiTooltipField {
char *text;
char *text_suffix;
struct {
- uint x_pos; /* x cursor position at the end of the last line */
- uint lines; /* number of lines, 1 or more with word-wrap */
+ /** X cursor position at the end of the last line. */
+ uint x_pos;
+ /** Number of lines, 1 or more with word-wrap. */
+ uint lines;
} geom;
uiTooltipFormat format;
+};
-} uiTooltipField;
-
-typedef struct uiTooltipData {
+struct uiTooltipData {
rcti bbox;
uiTooltipField *fields;
uint fields_len;
uiFontStyle fstyle;
int wrap_width;
int toth, lineh;
-} uiTooltipData;
+};
#define UI_TIP_LC_MAX 6
-BLI_STATIC_ASSERT(UI_TIP_LC_MAX == UI_TIP_LC_ALERT + 1, "invalid lc-max");
+BLI_STATIC_ASSERT(UI_TIP_LC_MAX == static_cast<int>(uiTooltipFormat::ColorID::Alert) + 1,
+ "invalid lc-max");
BLI_STATIC_ASSERT(sizeof(uiTooltipFormat) <= sizeof(int), "oversize");
static uiTooltipField *text_field_add_only(uiTooltipData *data)
{
data->fields_len += 1;
- data->fields = MEM_recallocN(data->fields, sizeof(*data->fields) * data->fields_len);
+ data->fields = static_cast<uiTooltipField *>(
+ MEM_recallocN(data->fields, sizeof(*data->fields) * data->fields_len));
return &data->fields[data->fields_len - 1];
}
-static uiTooltipField *text_field_add(uiTooltipData *data, const uiTooltipFormat *format)
+// static uiTooltipField *text_field_add(uiTooltipData *data, const uiTooltipFormat *format)
+// {
+// uiTooltipField *field = text_field_add_only(data);
+// field->format = *format;
+// return field;
+// }
+
+static uiTooltipField *text_field_add(uiTooltipData *data,
+ const uiTooltipFormat::Style style,
+ const uiTooltipFormat::ColorID color,
+ const bool is_pad = false)
{
uiTooltipField *field = text_field_add_only(data);
- field->format = *format;
+ field->format = {};
+ field->format.style = style;
+ field->format.color_id = color, field->format.is_pad = is_pad;
return field;
}
@@ -137,30 +161,31 @@ static void rgb_tint(float col[3], float h, float h_strength, float v, float v_s
static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region)
{
const float pad_px = UI_TIP_PADDING;
- uiTooltipData *data = region->regiondata;
+ uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata);
const uiWidgetColors *theme = ui_tooltip_get_theme();
rcti bbox = data->bbox;
float tip_colors[UI_TIP_LC_MAX][3];
uchar drawcol[4] = {0, 0, 0, 255}; /* to store color in while drawing (alpha is always 255) */
- float *main_color = tip_colors[UI_TIP_LC_MAIN]; /* the color from the theme */
- float *value_color = tip_colors[UI_TIP_LC_VALUE];
- float *active_color = tip_colors[UI_TIP_LC_ACTIVE];
- float *normal_color = tip_colors[UI_TIP_LC_NORMAL];
- float *python_color = tip_colors[UI_TIP_LC_PYTHON];
- float *alert_color = tip_colors[UI_TIP_LC_ALERT];
+ /* The color from the theme. */
+ float *main_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Main)];
+ float *value_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Value)];
+ float *active_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Active)];
+ float *normal_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Normal)];
+ float *python_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Python)];
+ float *alert_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Alert)];
float background_color[3];
wmOrtho2_region_pixelspace(region);
- /* draw background */
- ui_draw_tooltip_background(UI_style_get(), NULL, &bbox);
+ /* Draw background. */
+ ui_draw_tooltip_background(UI_style_get(), nullptr, &bbox);
/* set background_color */
rgb_uchar_to_float(background_color, theme->inner);
- /* calculate normal_color */
+ /* Calculate `normal_color`. */
rgb_uchar_to_float(main_color, theme->text);
copy_v3_v3(active_color, main_color);
copy_v3_v3(normal_color, main_color);
@@ -168,19 +193,19 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region
copy_v3_v3(alert_color, main_color);
copy_v3_v3(value_color, main_color);
- /* find the brightness difference between background and text colors */
+ /* Find the brightness difference between background and text colors. */
const float tone_bg = rgb_to_grayscale(background_color);
- /* tone_fg = rgb_to_grayscale(main_color); */
+ // tone_fg = rgb_to_grayscale(main_color);
- /* mix the colors */
+ /* Mix the colors. */
rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* Light gray. */
rgb_tint(active_color, 0.6f, 0.2f, tone_bg, 0.2f); /* Light blue. */
rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.4f); /* Gray. */
rgb_tint(python_color, 0.0f, 0.0f, tone_bg, 0.5f); /* Dark gray. */
rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* Red. */
- /* draw text */
+ /* Draw text. */
BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width);
BLF_wordwrap(blf_mono_font, data->wrap_width);
@@ -189,58 +214,58 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region
for (int i = 0; i < data->fields_len; i++) {
const uiTooltipField *field = &data->fields[i];
- const uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : NULL;
+ const uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] :
+ nullptr;
bbox.ymin = bbox.ymax - (data->lineh * field->geom.lines);
- if (field->format.style == UI_TIP_STYLE_HEADER) {
- const struct uiFontStyleDraw_Params fs_params = {
- .align = UI_STYLE_TEXT_LEFT,
- .word_wrap = true,
- };
- /* draw header and active data (is done here to be able to change color) */
- rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_MAIN]);
+ if (field->format.style == uiTooltipFormat::Style::Header) {
+ uiFontStyleDraw_Params fs_params{};
+ fs_params.align = UI_STYLE_TEXT_LEFT;
+ fs_params.word_wrap = true;
+
+ /* Draw header and active data (is done here to be able to change color). */
+ rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Main)]);
UI_fontstyle_set(&data->fstyle);
UI_fontstyle_draw(&data->fstyle, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params);
- /* offset to the end of the last line */
+ /* Offset to the end of the last line. */
if (field->text_suffix) {
const float xofs = field->geom.x_pos;
const float yofs = data->lineh * (field->geom.lines - 1);
bbox.xmin += xofs;
bbox.ymax -= yofs;
- rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]);
+ rgb_float_to_uchar(drawcol,
+ tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Active)]);
UI_fontstyle_draw(
&data->fstyle, &bbox, field->text_suffix, UI_TIP_STR_MAX, drawcol, &fs_params);
- /* undo offset */
+ /* Undo offset. */
bbox.xmin -= xofs;
bbox.ymax += yofs;
}
}
- else if (field->format.style == UI_TIP_STYLE_MONO) {
- const struct uiFontStyleDraw_Params fs_params = {
- .align = UI_STYLE_TEXT_LEFT,
- .word_wrap = true,
- };
+ else if (field->format.style == uiTooltipFormat::Style::Mono) {
+ uiFontStyleDraw_Params fs_params{};
+ fs_params.align = UI_STYLE_TEXT_LEFT;
+ fs_params.word_wrap = true;
uiFontStyle fstyle_mono = data->fstyle;
fstyle_mono.uifont_id = blf_mono_font;
UI_fontstyle_set(&fstyle_mono);
- /* XXX, needed because we don't have mono in 'U.uifonts' */
+ /* XXX: needed because we don't have mono in 'U.uifonts'. */
BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * U.pixelsize, U.dpi);
- rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]);
+ rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(field->format.color_id)]);
UI_fontstyle_draw(&fstyle_mono, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params);
}
else {
- BLI_assert(field->format.style == UI_TIP_STYLE_NORMAL);
- const struct uiFontStyleDraw_Params fs_params = {
- .align = UI_STYLE_TEXT_LEFT,
- .word_wrap = true,
- };
-
- /* draw remaining data */
- rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]);
+ BLI_assert(field->format.style == uiTooltipFormat::Style::Normal);
+ uiFontStyleDraw_Params fs_params{};
+ fs_params.align = UI_STYLE_TEXT_LEFT;
+ fs_params.word_wrap = true;
+
+ /* Draw remaining data. */
+ rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(field->format.color_id)]);
UI_fontstyle_set(&data->fstyle);
UI_fontstyle_draw(&data->fstyle, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params);
}
@@ -258,7 +283,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region
static void ui_tooltip_region_free_cb(ARegion *region)
{
- uiTooltipData *data = region->regiondata;
+ uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata);
for (int i = 0; i < data->fields_len; i++) {
const uiTooltipField *field = &data->fields[i];
@@ -269,7 +294,7 @@ static void ui_tooltip_region_free_cb(ARegion *region)
}
MEM_freeN(data->fields);
MEM_freeN(data);
- region->regiondata = NULL;
+ region->regiondata = nullptr;
}
/** \} */
@@ -280,7 +305,7 @@ static void ui_tooltip_region_free_cb(ARegion *region)
static char *ui_tooltip_text_python_from_op(bContext *C, wmOperatorType *ot, PointerRNA *opptr)
{
- char *str = WM_operator_pystring_ex(C, NULL, false, false, ot, opptr);
+ char *str = WM_operator_pystring_ex(C, nullptr, false, false, ot, opptr);
/* Avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary. */
WM_operator_pystring_abbreviate(str, 32);
@@ -303,24 +328,17 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data,
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
wmOperatorType *ot = WM_operatortype_find(kmi->idname, true);
- if (ot != NULL) {
- /* Tip */
+ if (ot != nullptr) {
+ /* Tip. */
{
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_MAIN,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Main, true);
field->text = BLI_strdup(ot->description ? ot->description : ot->name);
}
- /* Shortcut */
+ /* Shortcut. */
{
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_NORMAL,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal);
bool found = false;
if (WM_keymap_item_to_string(kmi, false, buf, sizeof(buf))) {
found = true;
@@ -328,13 +346,10 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data,
field->text = BLI_sprintfN(TIP_("Shortcut: %s"), found ? buf : "None");
}
- /* Python */
+ /* Python. */
if (U.flag & USER_TOOLTIPS_PYTHON) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_PYTHON,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Python);
char *str = ui_tooltip_text_python_from_op(C, ot, kmi->ptr);
field->text = BLI_sprintfN(TIP_("Python: %s"), str);
MEM_freeN(str);
@@ -352,17 +367,17 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data,
*/
static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is_label)
{
- if (but->optype == NULL) {
- return NULL;
+ if (but->optype == nullptr) {
+ return nullptr;
}
if (!STREQ(but->optype->idname, "WM_OT_tool_set_by_id")) {
- return NULL;
+ return nullptr;
}
/* Needed to get the space-data's type (below). */
- if (CTX_wm_space_data(C) == NULL) {
- return NULL;
+ if (CTX_wm_space_data(C) == nullptr) {
+ return nullptr;
}
char tool_id[MAX_NAME];
@@ -377,7 +392,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
const char *has_valid_context_error = IFACE_("Unsupported context");
{
ScrArea *area = CTX_wm_area(C);
- if (area == NULL) {
+ if (area == nullptr) {
has_valid_context = false;
}
else {
@@ -392,16 +407,15 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
}
/* We have a tool, now extract the info. */
- uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
+ uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__);
#ifdef WITH_PYTHON
- /* it turns out to be most simple to do this via Python since C
- * doesn't have access to information about non-active tools.
- */
+ /* It turns out to be most simple to do this via Python since C
+ * doesn't have access to information about non-active tools. */
/* Title (when icon-only). */
if (but->drawstr[0] == '\0') {
- const char *expr_imports[] = {"bpy", "bl_ui", NULL};
+ const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
char expr[256];
SNPRINTF(expr,
"bl_ui.space_toolsystem_common.item_from_id("
@@ -409,16 +423,16 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
"bpy.context.space_data.type, "
"'%s').label",
tool_id);
- char *expr_result = NULL;
+ char *expr_result = nullptr;
bool is_error = false;
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
+ else if (BPY_run_string_as_string(C, expr_imports, expr, nullptr, &expr_result)) {
if (STREQ(expr_result, "")) {
MEM_freeN(expr_result);
- expr_result = NULL;
+ expr_result = nullptr;
}
}
else {
@@ -428,7 +442,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
is_error = true;
}
- if (expr_result != NULL) {
+ if (expr_result != nullptr) {
/* NOTE: This is a very weak hack to get a valid translation most of the time...
* Proper way to do would be to get i18n context from the item, somehow. */
const char *label_str = CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, expr_result);
@@ -441,23 +455,19 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
expr_result = BLI_strdup(label_str);
}
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_MAIN,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Main, true);
field->text = expr_result;
if (UNLIKELY(is_error)) {
- field->format.color_id = UI_TIP_LC_ALERT;
+ field->format.color_id = uiTooltipFormat::ColorID::Alert;
}
}
}
/* Tip. */
if (is_label == false) {
- const char *expr_imports[] = {"bpy", "bl_ui", NULL};
+ const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
char expr[256];
SNPRINTF(expr,
"bl_ui.space_toolsystem_common.description_from_id("
@@ -466,16 +476,16 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
"'%s') + '.'",
tool_id);
- char *expr_result = NULL;
+ char *expr_result = nullptr;
bool is_error = false;
if (has_valid_context == false) {
expr_result = BLI_strdup(has_valid_context_error);
}
- else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) {
+ else if (BPY_run_string_as_string(C, expr_imports, expr, nullptr, &expr_result)) {
if (STREQ(expr_result, ".")) {
MEM_freeN(expr_result);
- expr_result = NULL;
+ expr_result = nullptr;
}
}
else {
@@ -485,17 +495,13 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
is_error = true;
}
- if (expr_result != NULL) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_MAIN,
- .is_pad = true,
- });
+ if (expr_result != nullptr) {
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Main, true);
field->text = expr_result;
if (UNLIKELY(is_error)) {
- field->format.color_id = UI_TIP_LC_ALERT;
+ field->format.color_id = uiTooltipFormat::ColorID::Alert;
}
}
}
@@ -514,18 +520,18 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
*
* Either way case it's useful to show the shortcut.
*/
- char *shortcut = NULL;
+ char *shortcut = nullptr;
{
- uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
- UI_but_string_info_get(C, but, &op_keymap, NULL);
+ uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, nullptr};
+ UI_but_string_info_get(C, but, &op_keymap, nullptr);
shortcut = op_keymap.strinfo;
}
- if (shortcut == NULL) {
+ if (shortcut == nullptr) {
const ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
- if (tool_attr != NULL) {
+ if (tool_attr != nullptr) {
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
const char *tool_id_lstrip = strrchr(tool_id, '.');
const int tool_id_offset = tool_id_lstrip ? ((tool_id_lstrip - tool_id) + 1) : 0;
@@ -542,7 +548,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (WM_key_event_operator_string(C,
ot->idname,
WM_OP_INVOKE_REGION_WIN,
- op_props.data,
+ static_cast<IDProperty *>(op_props.data),
true,
shortcut_brush,
ARRAY_SIZE(shortcut_brush))) {
@@ -553,20 +559,20 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
}
}
- if (shortcut == NULL) {
+ if (shortcut == nullptr) {
/* Check for direct access to the tool. */
char shortcut_toolbar[128] = "";
if (WM_key_event_operator_string(C,
"WM_OT_toolbar",
WM_OP_INVOKE_REGION_WIN,
- NULL,
+ nullptr,
true,
shortcut_toolbar,
ARRAY_SIZE(shortcut_toolbar))) {
/* Generate keymap in order to inspect it.
* NOTE: we could make a utility to avoid the keymap generation part of this. */
const char *expr_imports[] = {
- "bpy", "bl_keymap_utils", "bl_keymap_utils.keymap_from_toolbar", NULL};
+ "bpy", "bl_keymap_utils", "bl_keymap_utils.keymap_from_toolbar", nullptr};
const char *expr =
("getattr("
"bl_keymap_utils.keymap_from_toolbar.generate("
@@ -579,7 +585,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, NULL, &expr_result)) {
+ else if (BPY_run_string_as_intptr(C, expr_imports, expr, nullptr, &expr_result)) {
if (expr_result != 0) {
wmKeyMap *keymap = (wmKeyMap *)expr_result;
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
@@ -602,13 +608,9 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
}
}
- if (shortcut != NULL) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ if (shortcut != nullptr) {
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true);
field->text = BLI_sprintfN(TIP_("Shortcut: %s"), shortcut);
MEM_freeN(shortcut);
}
@@ -626,11 +628,11 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
* This is a little involved since the shortcut may be bound to another tool in this group,
* instead of the current tool on display. */
- char *expr_result = NULL;
+ char *expr_result = nullptr;
size_t expr_result_len;
{
- const char *expr_imports[] = {"bpy", "bl_ui", NULL};
+ const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
char expr[256];
SNPRINTF(expr,
"'\\x00'.join("
@@ -644,12 +646,12 @@ 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, NULL, &expr_result, &expr_result_len)) {
+ C, expr_imports, expr, nullptr, &expr_result, &expr_result_len)) {
/* pass. */
}
}
- if (expr_result != NULL) {
+ if (expr_result != nullptr) {
PointerRNA op_props;
WM_operator_properties_create_ptr(&op_props, but->optype);
RNA_boolean_set(&op_props, "cycle", true);
@@ -664,7 +666,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (WM_key_event_operator_string(C,
but->optype->idname,
WM_OP_INVOKE_REGION_WIN,
- op_props.data,
+ static_cast<IDProperty *>(op_props.data),
true,
shortcut,
ARRAY_SIZE(shortcut))) {
@@ -677,12 +679,8 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
MEM_freeN(expr_result);
if (shortcut[0] != '\0') {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true);
field->text = BLI_sprintfN(TIP_("Shortcut Cycle: %s"), shortcut);
}
}
@@ -690,12 +688,8 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* Python */
if ((is_label == false) && (U.flag & USER_TOOLTIPS_PYTHON)) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_PYTHON,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Python, true);
char *str = ui_tooltip_text_python_from_op(C, but->optype, but->opptr);
field->text = BLI_sprintfN(TIP_("Python: %s"), str);
MEM_freeN(str);
@@ -705,7 +699,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* This is too handy not to expose somehow, let's be sneaky for now. */
if ((is_label == false) && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
- const char *expr_imports[] = {"bpy", "bl_ui", NULL};
+ const char *expr_imports[] = {"bpy", "bl_ui", nullptr};
char expr[256];
SNPRINTF(expr,
"getattr("
@@ -721,15 +715,11 @@ 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, NULL, &expr_result)) {
+ else if (BPY_run_string_as_intptr(C, expr_imports, expr, nullptr, &expr_result)) {
if (expr_result != 0) {
{
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_NORMAL,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal, true);
field->text = BLI_strdup("Tool Keymap:");
}
wmKeyMap *keymap = (wmKeyMap *)expr_result;
@@ -746,7 +736,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
if (data->fields_len == 0) {
MEM_freeN(data);
- return NULL;
+ return nullptr;
}
return data;
}
@@ -755,26 +745,26 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
uiBut *but,
uiButExtraOpIcon *extra_icon)
{
- uiStringInfo but_label = {BUT_GET_LABEL, NULL};
- uiStringInfo but_tip = {BUT_GET_TIP, NULL};
- uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL};
- uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL};
- uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL};
- uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, NULL};
- uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL};
- uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL};
+ uiStringInfo but_label = {BUT_GET_LABEL, nullptr};
+ uiStringInfo but_tip = {BUT_GET_TIP, nullptr};
+ uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, nullptr};
+ uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, nullptr};
+ uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, nullptr};
+ uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, nullptr};
+ uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, nullptr};
+ uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, nullptr};
char buf[512];
wmOperatorType *optype = extra_icon ? UI_but_extra_operator_icon_optype_get(extra_icon) :
but->optype;
- PropertyRNA *rnaprop = extra_icon ? NULL : but->rnaprop;
+ PropertyRNA *rnaprop = extra_icon ? nullptr : but->rnaprop;
/* create tooltip data */
- uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
+ uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__);
if (extra_icon) {
- UI_but_extra_icon_string_info_get(C, extra_icon, &but_label, &but_tip, &op_keymap, NULL);
+ UI_but_extra_icon_string_info_get(C, extra_icon, &but_label, &but_tip, &op_keymap, nullptr);
}
else {
UI_but_string_info_get(C,
@@ -787,30 +777,25 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
&prop_keymap,
&rna_struct,
&rna_prop,
- NULL);
+ nullptr);
}
/* Tip Label (only for buttons not already showing the label).
* Check prefix instead of comparing because the button may include the shortcut.
- * Buttons with dynamic tooltips also don't get their default label here since they
- * can already provide more accurate and specific tooltip content. */
+ * Buttons with dynamic tool-tips also don't get their default label here since they
+ * can already provide more accurate and specific tool-tip content. */
if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo) && !but->tip_func) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_HEADER,
- .color_id = UI_TIP_LC_NORMAL,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Normal);
+
field->text = BLI_strdup(but_label.strinfo);
}
/* Tip */
if (but_tip.strinfo) {
{
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_HEADER,
- .color_id = UI_TIP_LC_NORMAL,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Normal);
if (enum_label.strinfo) {
field->text = BLI_sprintfN("%s: ", but_tip.strinfo);
field->text_suffix = BLI_strdup(enum_label.strinfo);
@@ -822,59 +807,40 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
/* special case enum rna buttons */
if ((but->type & UI_BTYPE_ROW) && rnaprop && RNA_property_flag(rnaprop) & PROP_ENUM_FLAG) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_NORMAL,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal);
field->text = BLI_strdup(TIP_("(Shift-Click/Drag to select multiple)"));
}
}
- /* Enum field label & tip */
+ /* Enum field label & tip. */
if (enum_tip.strinfo) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value);
field->text = BLI_strdup(enum_tip.strinfo);
}
- /* Op shortcut */
+ /* Operator shortcut. */
if (op_keymap.strinfo) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true);
field->text = BLI_sprintfN(TIP_("Shortcut: %s"), op_keymap.strinfo);
}
- /* Property context-toggle shortcut */
+ /* Property context-toggle shortcut. */
if (prop_keymap.strinfo) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true);
field->text = BLI_sprintfN(TIP_("Shortcut: %s"), prop_keymap.strinfo);
}
if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
- /* better not show the value of a password */
+ /* Better not show the value of a password. */
if ((rnaprop && (RNA_property_subtype(rnaprop) == PROP_PASSWORD)) == 0) {
- /* full string */
+ /* Full string. */
ui_but_string_get(but, buf, sizeof(buf));
if (buf[0]) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true);
field->text = BLI_sprintfN(TIP_("Value: %s"), buf);
}
}
@@ -889,22 +855,16 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
RNA_property_float_get_index(&but->rnapoin, rnaprop, but->rnaindex) :
RNA_property_float_get(&but->rnapoin, rnaprop);
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value);
field->text = BLI_sprintfN(TIP_("Radians: %f"), value);
}
}
if (but->flag & UI_BUT_DRIVEN) {
if (ui_but_anim_expression_get(but, buf, sizeof(buf))) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_NORMAL,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal);
field->text = BLI_sprintfN(TIP_("Expression: %s"), buf);
}
}
@@ -912,64 +872,56 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
if (but->rnapoin.owner_id) {
const ID *id = but->rnapoin.owner_id;
if (ID_IS_LINKED(id)) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_NORMAL,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal);
field->text = BLI_sprintfN(TIP_("Library: %s"), id->lib->filepath);
}
}
}
else if (optype) {
PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) :
- /* allocated when needed, the button owns it */
+ /* Allocated when needed, the button owns it. */
UI_but_operator_ptr_get(but);
- /* so the context is passed to fieldf functions (some py fieldf functions use it) */
+ /* So the context is passed to field functions (some Python field functions use it). */
WM_operator_properties_sanitize(opptr, false);
char *str = ui_tooltip_text_python_from_op(C, optype, opptr);
- /* operator info */
+ /* Operator info. */
if (U.flag & USER_TOOLTIPS_PYTHON) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_MONO,
- .color_id = UI_TIP_LC_PYTHON,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Mono, uiTooltipFormat::ColorID::Python, true);
field->text = BLI_sprintfN(TIP_("Python: %s"), str);
}
MEM_freeN(str);
}
- /* button is disabled, we may be able to tell user why */
+ /* Button is disabled, we may be able to tell user why. */
if ((but->flag & UI_BUT_DISABLED) || extra_icon) {
- const char *disabled_msg = NULL;
+ const char *disabled_msg = nullptr;
bool disabled_msg_free = false;
- /* if operator poll check failed, it can give pretty precise info why */
+ /* If operator poll check failed, it can give pretty precise info why. */
if (optype) {
const wmOperatorCallContext opcontext = extra_icon ? extra_icon->optype_params->opcontext :
but->opcontext;
+ wmOperatorCallParams call_params{};
+ call_params.optype = optype;
+ call_params.opcontext = opcontext;
CTX_wm_operator_poll_msg_clear(C);
- ui_but_context_poll_operator_ex(
- C, but, &(wmOperatorCallParams){.optype = optype, .opcontext = opcontext});
+ ui_but_context_poll_operator_ex(C, but, &call_params);
disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free);
}
- /* alternatively, buttons can store some reasoning too */
+ /* Alternatively, buttons can store some reasoning too. */
else if (!extra_icon && but->disabled_info) {
disabled_msg = TIP_(but->disabled_info);
}
if (disabled_msg && disabled_msg[0]) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_ALERT,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Alert);
field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg);
}
if (disabled_msg_free) {
@@ -979,39 +931,31 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
if ((U.flag & USER_TOOLTIPS_PYTHON) && !optype && rna_struct.strinfo) {
{
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_MONO,
- .color_id = UI_TIP_LC_PYTHON,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Mono, uiTooltipFormat::ColorID::Python, true);
if (rna_prop.strinfo) {
/* Struct and prop */
field->text = BLI_sprintfN(TIP_("Python: %s.%s"), rna_struct.strinfo, rna_prop.strinfo);
}
else {
- /* Only struct (e.g. menus) */
+ /* Only struct (e.g. menus). */
field->text = BLI_sprintfN(TIP_("Python: %s"), rna_struct.strinfo);
}
}
if (but->rnapoin.owner_id) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_MONO,
- .color_id = UI_TIP_LC_PYTHON,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Mono, uiTooltipFormat::ColorID::Python);
- /* this could get its own 'BUT_GET_...' type */
+ /* This could get its own `BUT_GET_...` type. */
/* never fails */
/* Move ownership (no need for re-allocation). */
if (rnaprop) {
- field->text = RNA_path_full_property_py_ex(
- CTX_data_main(C), &but->rnapoin, rnaprop, but->rnaindex, true);
+ field->text = RNA_path_full_property_py_ex(&but->rnapoin, rnaprop, but->rnaindex, true);
}
else {
- field->text = RNA_path_full_struct_py(CTX_data_main(C), &but->rnapoin);
+ field->text = RNA_path_full_struct_py(&but->rnapoin);
}
}
}
@@ -1044,73 +988,66 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
if (data->fields_len == 0) {
MEM_freeN(data);
- return NULL;
+ return nullptr;
}
return data;
}
static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
{
- uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
+ uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__);
- /* TODO(campbell): a way for gizmos to have their own descriptions (low priority). */
+ /* TODO(@campbellbarton): a way for gizmos to have their own descriptions (low priority). */
/* Operator Actions */
{
const bool use_drag = gz->drag_part != -1 && gz->highlight_part != gz->drag_part;
- const struct {
+ struct GizmoOpActions {
int part;
const char *prefix;
- } gzop_actions[] = {
+ };
+ GizmoOpActions gzop_actions[] = {
{
- .part = gz->highlight_part,
- .prefix = use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Click") : NULL,
+ gz->highlight_part,
+ use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Click") : nullptr,
},
{
- .part = use_drag ? gz->drag_part : -1,
- .prefix = use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Drag") : NULL,
+ use_drag ? gz->drag_part : -1,
+ use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Drag") : nullptr,
},
};
for (int i = 0; i < ARRAY_SIZE(gzop_actions); i++) {
wmGizmoOpElem *gzop = (gzop_actions[i].part != -1) ?
WM_gizmo_operator_get(gz, gzop_actions[i].part) :
- NULL;
- if (gzop != NULL) {
+ nullptr;
+ if (gzop != nullptr) {
/* Description */
char *info = WM_operatortype_description_or_name(C, gzop->type, &gzop->ptr);
- if (info != NULL) {
+ if (info != nullptr) {
char *text = info;
- if (gzop_actions[i].prefix != NULL) {
+ if (gzop_actions[i].prefix != nullptr) {
text = BLI_sprintfN("%s: %s", gzop_actions[i].prefix, info);
MEM_freeN(info);
}
- if (text != NULL) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_HEADER,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ if (text != nullptr) {
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Value, true);
field->text = text;
}
}
/* Shortcut */
{
- IDProperty *prop = gzop->ptr.data;
+ IDProperty *prop = static_cast<IDProperty *>(gzop->ptr.data);
char buf[128];
if (WM_key_event_operator_string(
C, gzop->type->idname, WM_OP_INVOKE_DEFAULT, prop, true, buf, ARRAY_SIZE(buf))) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true);
field->text = BLI_sprintfN(TIP_("Shortcut: %s"), buf);
}
}
@@ -1122,17 +1059,13 @@ static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
if (gz->type->target_property_defs_len) {
wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz);
for (int i = 0; i < gz->type->target_property_defs_len; i++) {
- /* TODO(campbell): function callback descriptions. */
+ /* TODO(@campbellbarton): function callback descriptions. */
wmGizmoProperty *gz_prop = &gz_prop_array[i];
- if (gz_prop->prop != NULL) {
+ if (gz_prop->prop != nullptr) {
const char *info = RNA_property_ui_description(gz_prop->prop);
if (info && info[0]) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true);
field->text = BLI_strdup(info);
}
}
@@ -1141,7 +1074,7 @@ static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz)
if (data->fields_len == 0) {
MEM_freeN(data);
- return NULL;
+ return nullptr;
}
return data;
}
@@ -1160,7 +1093,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
rcti rect_i;
int font_flag = 0;
- /* create area region */
+ /* Create area region. */
ARegion *region = ui_region_temp_add(CTX_wm_screen(C));
static ARegionType type;
@@ -1170,7 +1103,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
type.regionid = RGN_TYPE_TEMPORARY;
region->type = &type;
- /* set font, get bb */
+ /* Set font, get bounding-box. */
data->fstyle = style->widget; /* copy struct */
ui_fontscale(&data->fstyle.points, aspect);
@@ -1184,7 +1117,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width);
BLF_wordwrap(blf_mono_font, data->wrap_width);
- /* these defines tweaked depending on font */
+ /* These defines tweaked depending on font. */
#define TIP_BORDER_X (16.0f / aspect)
#define TIP_BORDER_Y (6.0f / aspect)
@@ -1193,18 +1126,19 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
int i, fonth, fontw;
for (i = 0, fontw = 0, fonth = 0; i < data->fields_len; i++) {
uiTooltipField *field = &data->fields[i];
- uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : NULL;
+ uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : nullptr;
struct ResultBLF info;
int w, x_pos = 0;
int font_id;
- if (field->format.style == UI_TIP_STYLE_MONO) {
+ if (field->format.style == uiTooltipFormat::Style::Mono) {
BLF_size(blf_mono_font, data->fstyle.points * U.pixelsize, U.dpi);
font_id = blf_mono_font;
}
else {
- BLI_assert(ELEM(field->format.style, UI_TIP_STYLE_NORMAL, UI_TIP_STYLE_HEADER));
+ BLI_assert(ELEM(
+ field->format.style, uiTooltipFormat::Style::Normal, uiTooltipFormat::Style::Header));
font_id = data->fstyle.uifont_id;
}
w = BLF_width_ex(font_id, field->text, UI_TIP_STR_MAX, &info);
@@ -1252,22 +1186,20 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
/* Clamp to window bounds. */
{
- /* Ensure at least 5 px above screen bounds
- * UI_UNIT_Y is just a guess to be above the menu item */
- if (init_rect_overlap != NULL) {
+ /* Ensure at least 5 px above screen bounds.
+ * #UI_UNIT_Y is just a guess to be above the menu item. */
+ if (init_rect_overlap != nullptr) {
const int pad = max_ff(1.0f, U.pixelsize) * 5;
- const rcti init_rect = {
- .xmin = init_rect_overlap->xmin - pad,
- .xmax = init_rect_overlap->xmax + pad,
- .ymin = init_rect_overlap->ymin - pad,
- .ymax = init_rect_overlap->ymax + pad,
- };
- const rcti rect_clamp = {
- .xmin = 0,
- .xmax = winx,
- .ymin = 0,
- .ymax = winy,
- };
+ rcti init_rect;
+ init_rect.xmin = init_rect_overlap->xmin - pad;
+ init_rect.xmax = init_rect_overlap->xmax + pad;
+ init_rect.ymin = init_rect_overlap->ymin - pad;
+ init_rect.ymax = init_rect_overlap->ymax + pad;
+ rcti rect_clamp;
+ rect_clamp.xmin = 0;
+ rect_clamp.xmax = winx;
+ rect_clamp.ymin = 0;
+ rect_clamp.ymax = winy;
/* try right. */
const int size_x = BLI_rcti_size_x(&rect_i);
const int size_y = BLI_rcti_size_y(&rect_i);
@@ -1347,12 +1279,11 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
}
else {
const int pad = max_ff(1.0f, U.pixelsize) * 5;
- const rcti rect_clamp = {
- .xmin = pad,
- .xmax = winx - pad,
- .ymin = pad + (UI_UNIT_Y * 2),
- .ymax = winy - pad,
- };
+ rcti rect_clamp;
+ rect_clamp.xmin = pad;
+ rect_clamp.xmax = winx - pad;
+ rect_clamp.ymin = pad + (UI_UNIT_Y * 2);
+ rect_clamp.ymax = winy - pad;
int offset_dummy[2];
BLI_rcti_clamp(&rect_i, &rect_clamp, offset_dummy);
}
@@ -1367,7 +1298,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C,
{
/* Compensate for margin offset, visually this corrects the position. */
const int margin = UI_POPUP_MARGIN;
- if (init_rect_overlap != NULL) {
+ if (init_rect_overlap != nullptr) {
BLI_rcti_translate(&rect_i, margin, margin / 2);
}
@@ -1402,29 +1333,29 @@ ARegion *UI_tooltip_create_from_button_or_extra_icon(
bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label)
{
wmWindow *win = CTX_wm_window(C);
- /* aspect values that shrink text are likely unreadable */
+ /* Aspect values that shrink text are likely unreadable. */
const float aspect = min_ff(1.0f, but->block->aspect);
float init_position[2];
if (but->drawflag & UI_BUT_NO_TOOLTIP) {
- return NULL;
+ return nullptr;
}
- uiTooltipData *data = NULL;
+ uiTooltipData *data = nullptr;
- if (data == NULL) {
+ if (data == nullptr) {
data = ui_tooltip_data_from_tool(C, but, is_label);
}
- if (data == NULL) {
+ if (data == nullptr) {
data = ui_tooltip_data_from_button_or_extra_icon(C, but, extra_icon);
}
- if (data == NULL) {
- data = ui_tooltip_data_from_button_or_extra_icon(C, but, NULL);
+ if (data == nullptr) {
+ data = ui_tooltip_data_from_button_or_extra_icon(C, but, nullptr);
}
- if (data == NULL) {
- return NULL;
+ if (data == nullptr) {
+ return nullptr;
}
const bool is_no_overlap = UI_but_has_tooltip_label(but) || UI_but_is_tool(but);
@@ -1453,31 +1384,30 @@ ARegion *UI_tooltip_create_from_button_or_extra_icon(
}
ARegion *region = ui_tooltip_create_with_data(
- C, data, init_position, is_no_overlap ? &init_rect : NULL, aspect);
+ C, data, init_position, is_no_overlap ? &init_rect : nullptr, aspect);
return region;
}
ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label)
{
- return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, NULL, is_label);
+ return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, nullptr, is_label);
}
ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
{
wmWindow *win = CTX_wm_window(C);
const float aspect = 1.0f;
- float init_position[2] = {win->eventstate->xy[0], win->eventstate->xy[1]};
+ float init_position[2] = {static_cast<float>(win->eventstate->xy[0]),
+ static_cast<float>(win->eventstate->xy[1])};
uiTooltipData *data = ui_tooltip_data_from_gizmo(C, gz);
- if (data == NULL) {
- return NULL;
+ if (data == nullptr) {
+ return nullptr;
}
- /* TODO(harley):
- * Julian preferred that the gizmo callback return the 3D bounding box
- * which we then project to 2D here. Would make a nice improvement.
- */
+ /* TODO(@harley): Julian preferred that the gizmo callback return the 3D bounding box
+ * which we then project to 2D here. Would make a nice improvement. */
if (gz->type->screen_bounds_get) {
rcti bounds;
if (gz->type->screen_bounds_get(C, gz, &bounds)) {
@@ -1486,46 +1416,34 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz)
}
}
- return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect);
+ return ui_tooltip_create_with_data(C, data, init_position, nullptr, aspect);
}
static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data(
const uiSearchItemTooltipData *item_tooltip_data)
{
- uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData");
+ uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__);
if (item_tooltip_data->description[0]) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_HEADER,
- .color_id = UI_TIP_LC_NORMAL,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Normal, true);
field->text = BLI_strdup(item_tooltip_data->description);
}
if (item_tooltip_data->name && item_tooltip_data->name[0]) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_VALUE,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true);
field->text = BLI_strdup(item_tooltip_data->name);
}
if (item_tooltip_data->hint[0]) {
- uiTooltipField *field = text_field_add(data,
- &(uiTooltipFormat){
- .style = UI_TIP_STYLE_NORMAL,
- .color_id = UI_TIP_LC_NORMAL,
- .is_pad = true,
- });
+ uiTooltipField *field = text_field_add(
+ data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal, true);
field->text = BLI_strdup(item_tooltip_data->hint);
}
if (data->fields_len == 0) {
MEM_freeN(data);
- return NULL;
+ return nullptr;
}
return data;
}
@@ -1537,8 +1455,8 @@ ARegion *UI_tooltip_create_from_search_item_generic(
const uiSearchItemTooltipData *item_tooltip_data)
{
uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(item_tooltip_data);
- if (data == NULL) {
- return NULL;
+ if (data == nullptr) {
+ return nullptr;
}
const float aspect = 1.0f;
@@ -1547,7 +1465,7 @@ ARegion *UI_tooltip_create_from_search_item_generic(
init_position[0] = win->eventstate->xy[0];
init_position[1] = item_rect->ymin + searchbox_region->winrct.ymin - (UI_POPUP_MARGIN / 2);
- return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect);
+ return ui_tooltip_create_with_data(C, data, init_position, nullptr, aspect);
}
void UI_tooltip_free(bContext *C, bScreen *screen, ARegion *region)
diff --git a/source/blender/editors/interface/interface_regions.cc b/source/blender/editors/interface/interface_regions.cc
index 1a2c1f7919c..63c4a6f9c42 100644
--- a/source/blender/editors/interface/interface_regions.cc
+++ b/source/blender/editors/interface/interface_regions.cc
@@ -21,7 +21,7 @@
#include "ED_screen.h"
-#include "interface_regions_intern.h"
+#include "interface_regions_intern.hh"
ARegion *ui_region_temp_add(bScreen *screen)
{
@@ -45,6 +45,6 @@ void ui_region_temp_remove(bContext *C, bScreen *screen, ARegion *region)
}
ED_region_exit(C, region);
- BKE_area_region_free(nullptr, region); /* nullptr: no spacetype */
+ BKE_area_region_free(nullptr, region); /* nullptr: no space-type. */
BLI_freelinkN(&screen->regionbase, region);
}
diff --git a/source/blender/editors/interface/interface_regions_intern.h b/source/blender/editors/interface/interface_regions_intern.hh
index 2ed2cb3d68b..6287a031f5c 100644
--- a/source/blender/editors/interface/interface_regions_intern.h
+++ b/source/blender/editors/interface/interface_regions_intern.hh
@@ -3,23 +3,16 @@
/** \file
* \ingroup edinterface
*
- * Share between interface_region_*.c files.
+ * Share between interface_region_*.cc files.
*/
#pragma once
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* interface_region_menu_popup.c */
+/* interface_region_menu_popup.cc */
uint ui_popup_menu_hash(const char *str);
-/* interface_regions_intern.h */
+/* interface_regions.cc */
+
ARegion *ui_region_temp_add(bScreen *screen);
void ui_region_temp_remove(struct bContext *C, bScreen *screen, ARegion *region);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc
index 8588c7cabc0..3147deb5ad1 100644
--- a/source/blender/editors/interface/interface_template_asset_view.cc
+++ b/source/blender/editors/interface/interface_template_asset_view.cc
@@ -66,7 +66,7 @@ static void asset_view_item_but_drag_set(uiBut *but,
}
static void asset_view_draw_item(uiList *ui_list,
- bContext *UNUSED(C),
+ const bContext *UNUSED(C),
uiLayout *layout,
PointerRNA *UNUSED(dataptr),
PointerRNA *itemptr,
@@ -183,7 +183,7 @@ static void asset_view_template_refresh_asset_collection(
}
void uiTemplateAssetView(uiLayout *layout,
- bContext *C,
+ const bContext *C,
const char *list_id,
PointerRNA *asset_library_dataptr,
const char *asset_library_propname,
diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc
index 0a684903f0f..55ca945671f 100644
--- a/source/blender/editors/interface/interface_template_attribute_search.cc
+++ b/source/blender/editors/interface/interface_template_attribute_search.cc
@@ -14,13 +14,15 @@
#include "BLT_translation.h"
-#include "NOD_geometry_nodes_eval_log.hh"
+#include "BKE_attribute.hh"
+
+#include "NOD_geometry_nodes_log.hh"
#include "UI_interface.h"
#include "UI_interface.hh"
#include "UI_resources.h"
-using blender::nodes::geometry_nodes_eval_log::GeometryAttributeInfo;
+using blender::nodes::geo_eval_log::GeometryAttributeInfo;
namespace blender::ui {
diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc
index e0b6bbb34c4..d8a9591a021 100644
--- a/source/blender/editors/interface/interface_template_list.cc
+++ b/source/blender/editors/interface/interface_template_list.cc
@@ -83,7 +83,7 @@ struct TemplateListVisualInfo {
};
static void uilist_draw_item_default(struct uiList *ui_list,
- struct bContext *UNUSED(C),
+ const struct bContext *UNUSED(C),
struct uiLayout *layout,
struct PointerRNA *UNUSED(dataptr),
struct PointerRNA *itemptr,
@@ -114,7 +114,7 @@ static void uilist_draw_item_default(struct uiList *ui_list,
}
static void uilist_draw_filter_default(struct uiList *ui_list,
- struct bContext *UNUSED(C),
+ const struct bContext *UNUSED(C),
struct uiLayout *layout)
{
PointerRNA listptr;
@@ -160,7 +160,7 @@ static int cmpstringp(const void *p1, const void *p2)
}
static void uilist_filter_items_default(struct uiList *ui_list,
- struct bContext *UNUSED(C),
+ const struct bContext *UNUSED(C),
struct PointerRNA *dataptr,
const char *propname)
{
@@ -434,7 +434,7 @@ static void ui_template_list_collect_items(PointerRNA *list_ptr,
/**
* Create the UI-list representation of the list items, sorted and filtered if needed.
*/
-static void ui_template_list_collect_display_items(bContext *C,
+static void ui_template_list_collect_display_items(const bContext *C,
uiList *ui_list,
TemplateListInputData *input_data,
const uiListFilterItemsFunc filter_items_fn,
@@ -601,7 +601,7 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha
/**
* \note that \a layout_type may be null.
*/
-static uiList *ui_list_ensure(bContext *C,
+static uiList *ui_list_ensure(const bContext *C,
uiListType *ui_list_type,
const char *list_id,
int layout_type,
@@ -656,7 +656,7 @@ static uiList *ui_list_ensure(bContext *C,
return ui_list;
}
-static void ui_template_list_layout_draw(bContext *C,
+static void ui_template_list_layout_draw(const bContext *C,
uiList *ui_list,
uiLayout *layout,
TemplateListInputData *input_data,
@@ -767,7 +767,7 @@ static void ui_template_list_layout_draw(bContext *C,
uiItemL(col, "", ICON_NONE);
}
- /* add scrollbar */
+ /* Add scroll-bar. */
if (items->tot_items > visual_info.visual_items) {
uiLayoutColumn(row, false);
uiDefButI(block,
@@ -916,7 +916,7 @@ static void ui_template_list_layout_draw(bContext *C,
uiItemL(subrow, "", ICON_NONE);
}
- /* add scrollbar */
+ /* Add scroll-bar. */
if (items->tot_items > visual_info.visual_items) {
/* col = */ uiLayoutColumn(row, false);
uiDefButI(block,
@@ -940,7 +940,7 @@ static void ui_template_list_layout_draw(bContext *C,
box = uiLayoutListBox(layout, ui_list, &input_data->active_dataptr, input_data->activeprop);
/* For grip button. */
glob = uiLayoutColumn(box, true);
- /* For scrollbar. */
+ /* For scroll-bar. */
row = uiLayoutRow(glob, false);
const bool show_names = (flags & UI_TEMPLATE_LIST_NO_NAMES) == 0;
@@ -1156,7 +1156,7 @@ static void ui_template_list_layout_draw(bContext *C,
}
uiList *uiTemplateList_ex(uiLayout *layout,
- bContext *C,
+ const bContext *C,
const char *listtype_name,
const char *list_id,
PointerRNA *dataptr,
@@ -1227,7 +1227,7 @@ uiList *uiTemplateList_ex(uiLayout *layout,
}
void uiTemplateList(uiLayout *layout,
- bContext *C,
+ const bContext *C,
const char *listtype_name,
const char *list_id,
PointerRNA *dataptr,
diff --git a/source/blender/editors/interface/interface_template_search_menu.cc b/source/blender/editors/interface/interface_template_search_menu.cc
index c3021028b97..c777b7834f2 100644
--- a/source/blender/editors/interface/interface_template_search_menu.cc
+++ b/source/blender/editors/interface/interface_template_search_menu.cc
@@ -918,6 +918,7 @@ static void menu_search_arg_free_fn(void *data_v)
WM_operator_properties_free(item->op.opptr);
MEM_freeN(item->op.opptr);
}
+ break;
}
case MenuSearch_Item::Type::RNA: {
break;
diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.cc
index 41de2ab197d..0d0a5f01744 100644
--- a/source/blender/editors/interface/interface_template_search_operator.c
+++ b/source/blender/editors/interface/interface_template_search_operator.cc
@@ -7,14 +7,15 @@
* accessed via the #WM_OT_search_operator operator.
*/
-#include <string.h>
+#include <cstring>
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
-#include "BLI_alloca.h"
+#include "BLI_array.hh"
#include "BLI_ghash.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -35,10 +36,10 @@
static void operator_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2)
{
- wmOperatorType *ot = arg2;
+ wmOperatorType *ot = static_cast<wmOperatorType *>(arg2);
if (ot) {
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, NULL, NULL);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
}
}
@@ -53,19 +54,20 @@ static void operator_search_update_fn(const bContext *C,
/* Prepare BLI_string_all_words_matched. */
const size_t str_len = strlen(str);
const int words_max = BLI_string_max_possible_word_count(str_len);
- int(*words)[2] = BLI_array_alloca(words, words_max);
- const int words_len = BLI_string_find_split_words(str, str_len, ' ', words, words_max);
+ blender::Array<blender::int2> words(words_max);
+ const int words_len = BLI_string_find_split_words(
+ str, str_len, ' ', (int(*)[2])words.data(), words_max);
for (WM_operatortype_iter(&iter); !BLI_ghashIterator_done(&iter);
BLI_ghashIterator_step(&iter)) {
- wmOperatorType *ot = BLI_ghashIterator_getValue(&iter);
+ wmOperatorType *ot = static_cast<wmOperatorType *>(BLI_ghashIterator_getValue(&iter));
const char *ot_ui_name = CTX_IFACE_(ot->translation_context, ot->name);
if ((ot->flag & OPTYPE_INTERNAL) && (G.debug & G_DEBUG_WM) == 0) {
continue;
}
- if (BLI_string_all_words_matched(ot_ui_name, str, words, words_len)) {
+ if (BLI_string_all_words_matched(ot_ui_name, str, (int(*)[2])words.data(), words_len)) {
if (WM_operator_poll((bContext *)C, ot)) {
char name[256];
const int len = strlen(ot_ui_name);
@@ -78,7 +80,7 @@ static void operator_search_update_fn(const bContext *C,
if (WM_key_event_operator_string(C,
ot->idname,
WM_OP_EXEC_DEFAULT,
- NULL,
+ nullptr,
true,
&name[len + 1],
sizeof(name) - len - 1)) {
@@ -105,11 +107,11 @@ void UI_but_func_operator_search(uiBut *but)
UI_but_func_search_set(but,
ui_searchbox_create_operator,
operator_search_update_fn,
- NULL,
+ nullptr,
false,
- NULL,
+ nullptr,
operator_search_exec_fn,
- NULL);
+ nullptr);
}
void uiTemplateOperatorSearch(uiLayout *layout)
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 14da5a7cd62..0b72c358dc9 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -571,8 +571,11 @@ static uiBlock *id_search_menu(bContext *C, ARegion *region, void *arg_litem)
/** \name ID Template
* \{ */
-/* This is for browsing and editing the ID-blocks used */
+static void template_id_cb(bContext *C, void *arg_litem, void *arg_event);
+/**
+ * This is for browsing and editing the ID-blocks used.
+ */
void UI_context_active_but_prop_get_templateID(bContext *C,
PointerRNA *r_ptr,
PropertyRNA **r_prop)
@@ -582,7 +585,7 @@ void UI_context_active_but_prop_get_templateID(bContext *C,
memset(r_ptr, 0, sizeof(*r_ptr));
*r_prop = NULL;
- if (but && but->func_argN) {
+ if (but && (but->funcN == template_id_cb) && but->func_argN) {
TemplateID *template_ui = but->func_argN;
*r_ptr = template_ui->ptr;
*r_prop = template_ui->prop;
@@ -650,20 +653,41 @@ static void template_id_liboverride_hierarchy_collections_tag_recursive(
}
}
-static void template_id_liboverride_hierarchy_create(bContext *C,
- Main *bmain,
- TemplateID *template_ui,
- PointerRNA *idptr,
- const char **r_undo_push_label)
+ID *ui_template_id_liboverride_hierarchy_make(
+ bContext *C, Main *bmain, ID *owner_id, ID *id, const char **r_undo_push_label)
{
- ID *id = idptr->data;
- ID *owner_id = template_ui->ptr.owner_id;
+ const char *undo_push_label;
+ if (r_undo_push_label == NULL) {
+ r_undo_push_label = &undo_push_label;
+ }
+
+ /* If this is called on an already local override, 'toggle' between user-editable state, and
+ * system override with reset. */
+ if (!ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY(id)) {
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
+ BKE_lib_override_library_get(bmain, id, NULL, &id);
+ }
+ if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) {
+ id->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ *r_undo_push_label = "Make Library Override Hierarchy Editable";
+ }
+ else {
+ BKE_lib_override_library_id_reset(bmain, id, true);
+ *r_undo_push_label = "Clear Library Override Hierarchy";
+ }
+
+ WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, NULL);
+ WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ return id;
+ }
/* Attempt to perform a hierarchy override, based on contextual data available.
* NOTE: do not attempt to perform such hierarchy override at all cost, if there is not enough
* context, better to abort than create random overrides all over the place. */
if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id)) {
- return;
+ RNA_warning("The data-block %s is not overridable", id->name);
+ return NULL;
}
Object *object_active = CTX_data_active_object(C);
@@ -741,15 +765,8 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
if (object_active != NULL) {
object_active->id.tag |= LIB_TAG_DOIT;
}
- BKE_lib_override_library_create(bmain,
- scene,
- view_layer,
- NULL,
- id,
- &collection_active->id,
- NULL,
- &id_override,
- U.experimental.use_override_new_fully_editable);
+ BKE_lib_override_library_create(
+ bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false);
}
else if (object_active != NULL && !ID_IS_LINKED(object_active) &&
&object_active->instance_collection->id == id) {
@@ -762,7 +779,7 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
&object_active->id,
&object_active->id,
&id_override,
- U.experimental.use_override_new_fully_editable);
+ false);
}
break;
case ID_OB:
@@ -772,15 +789,17 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
if (object_active != NULL) {
object_active->id.tag |= LIB_TAG_DOIT;
}
- BKE_lib_override_library_create(bmain,
- scene,
- view_layer,
- NULL,
- id,
- &collection_active->id,
- NULL,
- &id_override,
- U.experimental.use_override_new_fully_editable);
+ BKE_lib_override_library_create(
+ bmain, scene, view_layer, NULL, id, &collection_active->id, NULL, &id_override, false);
+ }
+ else {
+ if (object_active != NULL) {
+ object_active->id.tag |= LIB_TAG_DOIT;
+ }
+ BKE_lib_override_library_create(
+ bmain, scene, view_layer, NULL, id, NULL, NULL, &id_override, false);
+ BKE_scene_collections_object_remove(bmain, scene, (Object *)id, true);
+ WM_event_add_notifier(C, NC_ID | NA_REMOVED, NULL);
}
break;
case ID_ME:
@@ -795,7 +814,8 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
case ID_CV:
case ID_PT:
case ID_VO:
- if (object_active != NULL && object_active->data == id) {
+ case ID_NT: /* Essentially geometry nodes from modifier currently. */
+ if (object_active != NULL) {
if (collection_active != NULL &&
BKE_collection_has_object_recursive(collection_active, object_active)) {
template_id_liboverride_hierarchy_collections_tag_recursive(collection_active, id, true);
@@ -810,42 +830,74 @@ static void template_id_liboverride_hierarchy_create(bContext *C,
&collection_active->id,
NULL,
&id_override,
- U.experimental.use_override_new_fully_editable);
+ false);
}
else {
object_active->id.tag |= LIB_TAG_DOIT;
- BKE_lib_override_library_create(bmain,
- scene,
- view_layer,
- NULL,
- id,
- &object_active->id,
- NULL,
- &id_override,
- U.experimental.use_override_new_fully_editable);
+ BKE_lib_override_library_create(
+ bmain, scene, view_layer, NULL, id, &object_active->id, NULL, &id_override, false);
}
}
break;
case ID_MA:
case ID_TE:
case ID_IM:
+ RNA_warning("The type of data-block %s could not yet implemented", id->name);
break;
case ID_WO:
+ RNA_warning("The type of data-block %s could not yet implemented", id->name);
break;
case ID_PA:
+ RNA_warning("The type of data-block %s could not yet implemented", id->name);
break;
default:
+ RNA_warning("The type of data-block %s could not yet implemented", id->name);
break;
}
if (id_override != NULL) {
id_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
*r_undo_push_label = "Make Library Override Hierarchy";
- /* Given `idptr` is re-assigned to owner property by caller to ensure proper updates etc. Here
- * we also use it to ensure remapping of the owner property from the linked data to the newly
- * created liboverride (note that in theory this remapping has already been done by code
- * above). */
- RNA_id_pointer_create(id_override, idptr);
+ /* In theory we could rely on setting/updating the RNA ID pointer property (as done by calling
+ * code) to be enough.
+ *
+ * However, some rare ID pointers properties (like the 'active object in viewlayer' one used
+ * for the Object templateID in the Object properties) use notifiers that do not enforce a
+ * rebuild of outliner trees, leading to crashes.
+ *
+ * So for now, add some extra notifiers here. */
+ WM_event_add_notifier(C, NC_ID | NA_ADDED, NULL);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
+ }
+ return id_override;
+}
+
+static void template_id_liboverride_hierarchy_make(bContext *C,
+ Main *bmain,
+ TemplateID *template_ui,
+ PointerRNA *idptr,
+ const char **r_undo_push_label)
+{
+ ID *id = idptr->data;
+ ID *owner_id = template_ui->ptr.owner_id;
+
+ ID *id_override = ui_template_id_liboverride_hierarchy_make(
+ C, bmain, owner_id, id, r_undo_push_label);
+
+ if (id_override != NULL) {
+ /* `idptr` is re-assigned to owner property to ensure proper updates etc. Here we also use it
+ * to ensure remapping of the owner property from the linked data to the newly created
+ * liboverride (note that in theory this remapping has already been done by code above), but
+ * only in case owner ID was already local ID (override or pure local data).
+ *
+ * Otherwise, owner ID will also have been overridden, and remapped already to use it's
+ * override of the data too. */
+ if (!ID_IS_LINKED(owner_id)) {
+ RNA_id_pointer_create(id_override, idptr);
+ }
+ }
+ else {
+ RNA_warning("The data-block %s could not be overridden", id->name);
}
}
@@ -898,8 +950,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
if (id) {
Main *bmain = CTX_data_main(C);
if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
- template_id_liboverride_hierarchy_create(
- C, bmain, template_ui, &idptr, &undo_push_label);
+ template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label);
}
else {
if (BKE_lib_id_make_local(bmain, id, 0)) {
@@ -918,12 +969,18 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
break;
case UI_ID_OVERRIDE:
if (id && ID_IS_OVERRIDE_LIBRARY(id)) {
- BKE_lib_override_library_make_local(id);
- /* Reassign to get proper updates/notifiers. */
- idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
- RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
- RNA_property_update(C, &template_ui->ptr, template_ui->prop);
- undo_push_label = "Make Local";
+ Main *bmain = CTX_data_main(C);
+ if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
+ template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label);
+ }
+ else {
+ BKE_lib_override_library_make_local(id);
+ /* Reassign to get proper updates/notifiers. */
+ idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
+ RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
+ RNA_property_update(C, &template_ui->ptr, template_ui->prop);
+ undo_push_label = "Make Local";
+ }
}
break;
case UI_ID_ALONE:
@@ -1308,20 +1365,22 @@ static void template_ID(const bContext *C,
}
}
else if (ID_IS_OVERRIDE_LIBRARY(id)) {
- but = uiDefIconBut(block,
- UI_BTYPE_BUT,
- 0,
- ICON_LIBRARY_DATA_OVERRIDE,
- 0,
- 0,
- UI_UNIT_X,
- UI_UNIT_Y,
- NULL,
- 0,
- 0,
- 0,
- 0,
- TIP_("Library override of linked data-block, click to make fully local"));
+ but = uiDefIconBut(
+ block,
+ UI_BTYPE_BUT,
+ 0,
+ ICON_LIBRARY_DATA_OVERRIDE,
+ 0,
+ 0,
+ UI_UNIT_X,
+ UI_UNIT_Y,
+ NULL,
+ 0,
+ 0,
+ 0,
+ 0,
+ TIP_("Library override of linked data-block, click to make fully local, "
+ "Shift + Click to clear the library override and toggle if it can be edited"));
UI_but_funcN_set(
but, template_id_cb, MEM_dupallocN(template_ui), POINTER_FROM_INT(UI_ID_OVERRIDE));
}
@@ -5147,7 +5206,7 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp
0.0,
0.0,
0.0,
- "Reapply and update the preset, removing changes");
+ TIP_("Reapply and update the preset, removing changes"));
UI_but_funcN_set(bt, CurveProfile_buttons_reset, MEM_dupallocN(cb), profile);
}
}
@@ -6272,7 +6331,7 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C)
0,
width + UI_UNIT_X,
UI_UNIT_Y,
- "Show in Info Log");
+ TIP_("Show in Info Log"));
UI_block_emboss_set(block, previous_emboss);
}
@@ -6299,8 +6358,10 @@ void uiTemplateInputStatus(uiLayout *layout, struct bContext *C)
uiLayout *row = uiLayoutRow(col, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT);
- const char *msg = TIP_(WM_window_cursor_keymap_status_get(win, i, 0));
- const char *msg_drag = TIP_(WM_window_cursor_keymap_status_get(win, i, 1));
+ const char *msg = CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT,
+ WM_window_cursor_keymap_status_get(win, i, 0));
+ const char *msg_drag = CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT,
+ WM_window_cursor_keymap_status_get(win, i, 1));
if (msg || (msg_drag == NULL)) {
uiItemL(row, msg ? msg : "", (ICON_MOUSE_LMB + i));
@@ -6430,13 +6491,13 @@ bool uiTemplateEventFromKeymapItem(struct uiLayout *layout,
for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) {
uiItemL(layout, "", icon_mod[j]);
}
- uiItemL(layout, text, icon);
+ uiItemL(layout, CTX_TIP_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, text), icon);
ok = true;
}
else if (text_fallback) {
const char *event_text = WM_key_event_string(kmi->type, true);
uiItemL(layout, event_text, ICON_NONE);
- uiItemL(layout, text, ICON_NONE);
+ uiItemL(layout, CTX_TIP_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, text), ICON_NONE);
ok = true;
}
return ok;
@@ -6674,7 +6735,7 @@ void uiTemplateCacheFileTimeSettings(uiLayout *layout, PointerRNA *fileptr)
}
static void cache_file_layer_item(uiList *UNUSED(ui_list),
- bContext *UNUSED(C),
+ const bContext *UNUSED(C),
uiLayout *layout,
PointerRNA *UNUSED(dataptr),
PointerRNA *itemptr,
diff --git a/source/blender/editors/interface/interface_undo.c b/source/blender/editors/interface/interface_undo.cc
index e998eb6dbed..ec54b695cf7 100644
--- a/source/blender/editors/interface/interface_undo.c
+++ b/source/blender/editors/interface/interface_undo.cc
@@ -7,7 +7,7 @@
* Undo stack to use for UI widgets that manage their own editing state.
*/
-#include <string.h>
+#include <cstring>
#include "BLI_listbase.h"
@@ -21,39 +21,39 @@
/** \name Text Field Undo Stack
* \{ */
-typedef struct uiUndoStack_Text_State {
+struct uiUndoStack_Text_State {
struct uiUndoStack_Text_State *next, *prev;
int cursor_index;
char text[0];
-} uiUndoStack_Text_State;
+};
-typedef struct uiUndoStack_Text {
+struct uiUndoStack_Text {
ListBase states;
uiUndoStack_Text_State *current;
-} uiUndoStack_Text;
+};
static const char *ui_textedit_undo_impl(uiUndoStack_Text *stack, int *r_cursor_index)
{
/* Don't undo if no data has been pushed yet. */
- if (stack->current == NULL) {
- return NULL;
+ if (stack->current == nullptr) {
+ return nullptr;
}
/* Travel backwards in the stack and copy information to the caller. */
- if (stack->current->prev != NULL) {
+ if (stack->current->prev != nullptr) {
stack->current = stack->current->prev;
*r_cursor_index = stack->current->cursor_index;
return stack->current->text;
}
- return NULL;
+ return nullptr;
}
static const char *ui_textedit_redo_impl(uiUndoStack_Text *stack, int *r_cursor_index)
{
/* Don't redo if no data has been pushed yet. */
- if (stack->current == NULL) {
- return NULL;
+ if (stack->current == nullptr) {
+ return nullptr;
}
/* Only redo if new data has not been entered since the last undo. */
@@ -63,7 +63,7 @@ static const char *ui_textedit_redo_impl(uiUndoStack_Text *stack, int *r_cursor_
*r_cursor_index = stack->current->cursor_index;
return stack->current->text;
}
- return NULL;
+ return nullptr;
}
const char *ui_textedit_undo(uiUndoStack_Text *stack, int direction, int *r_cursor_index)
@@ -78,7 +78,7 @@ const char *ui_textedit_undo(uiUndoStack_Text *stack, int direction, int *r_curs
void ui_textedit_undo_push(uiUndoStack_Text *stack, const char *text, int cursor_index)
{
/* Clear all redo actions from the current state. */
- if (stack->current != NULL) {
+ if (stack->current != nullptr) {
while (stack->current->next) {
uiUndoStack_Text_State *state = stack->current->next;
BLI_remlink(&stack->states, state);
@@ -88,7 +88,8 @@ void ui_textedit_undo_push(uiUndoStack_Text *stack, const char *text, int cursor
/* Create the new state. */
const int text_size = strlen(text) + 1;
- stack->current = MEM_mallocN(sizeof(uiUndoStack_Text_State) + text_size, __func__);
+ stack->current = static_cast<uiUndoStack_Text_State *>(
+ MEM_mallocN(sizeof(uiUndoStack_Text_State) + text_size, __func__));
stack->current->cursor_index = cursor_index;
memcpy(stack->current->text, text, text_size);
BLI_addtail(&stack->states, stack->current);
@@ -96,8 +97,8 @@ void ui_textedit_undo_push(uiUndoStack_Text *stack, const char *text, int cursor
uiUndoStack_Text *ui_textedit_undo_stack_create(void)
{
- uiUndoStack_Text *stack = MEM_mallocN(sizeof(uiUndoStack_Text), __func__);
- stack->current = NULL;
+ uiUndoStack_Text *stack = MEM_new<uiUndoStack_Text>(__func__);
+ stack->current = nullptr;
BLI_listbase_clear(&stack->states);
return stack;
diff --git a/source/blender/editors/interface/interface_utils.cc b/source/blender/editors/interface/interface_utils.cc
index b7ca2d9aa11..4b94834ce97 100644
--- a/source/blender/editors/interface/interface_utils.cc
+++ b/source/blender/editors/interface/interface_utils.cc
@@ -787,7 +787,7 @@ int UI_calc_float_precision(int prec, double value)
*/
value = fabs(value);
if ((value < pow10_neg[prec]) && (value > (1.0 / max_pow))) {
- int value_i = (int)((value * max_pow) + 0.5);
+ int value_i = (int)lround(value * max_pow);
if (value_i != 0) {
const int prec_span = 3; /* show: 0.01001, 5 would allow 0.0100001 for eg. */
int test_prec;
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index e2df2d77817..53b1967d668 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -104,8 +104,7 @@ typedef enum {
UI_WTYPE_LISTITEM,
UI_WTYPE_PROGRESSBAR,
UI_WTYPE_NODESOCKET,
- UI_WTYPE_TREEROW,
- UI_WTYPE_GRID_TILE,
+ UI_WTYPE_VIEW_ITEM,
} uiWidgetTypeEnum;
/**
@@ -534,7 +533,7 @@ static void draw_anti_tria(
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(draw_color);
immBegin(GPU_PRIM_TRIS, 3 * WIDGET_AA_JITTER);
@@ -1525,11 +1524,6 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle,
const size_t max_len,
const char rpart_sep)
{
- /* Add some epsilon to OK width, avoids 'ellipsing' text that nearly fits!
- * Better to have a small piece of the last char cut out,
- * than two remaining chars replaced by an ellipsis... */
- okwidth += 1.0f + UI_DPI_FAC;
-
BLI_assert(str[0]);
/* need to set this first */
@@ -1628,7 +1622,7 @@ float UI_text_clip_middle_ex(const uiFontStyle *fstyle,
strwidth = BLF_width(fstyle->uifont_id, str, max_len);
}
- BLI_assert(strwidth <= okwidth);
+ BLI_assert((strwidth <= okwidth) || (okwidth <= 0.0f));
return strwidth;
}
@@ -1985,7 +1979,7 @@ static void widget_draw_text(const uiFontStyle *fstyle,
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
rcti selection_shape;
selection_shape.xmin = rect->xmin + selsta_draw;
@@ -2037,7 +2031,7 @@ static void widget_draw_text(const uiFontStyle *fstyle,
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_WIDGET_TEXT_CURSOR);
@@ -2780,7 +2774,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
for (int step = 1; step <= (int)radout; step++) {
const float expfac = sqrtf(step / radout);
@@ -2836,7 +2830,7 @@ static void ui_hsv_cursor(const float x, const float y, const float zoom)
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
imm_draw_circle_fill_2d(pos, x, y, radius, 8);
@@ -2936,7 +2930,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
immBegin(GPU_PRIM_TRI_FAN, tot + 2);
immAttr3fv(color, rgb_center);
@@ -2970,7 +2964,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const
format = immVertexFormat();
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
@@ -3067,7 +3061,7 @@ void ui_draw_gradient(const rcti *rect,
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
immBegin(GPU_PRIM_TRIS, steps * 3 * 6);
@@ -3232,7 +3226,7 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect)
/* outline */
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ub(0, 0, 0);
imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax));
immUnbindProgram();
@@ -3308,7 +3302,7 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol)
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
immUniformColor4ubv(col);
@@ -3672,12 +3666,11 @@ static void widget_progressbar(uiBut *but,
widgetbase_draw(&wtb_bar, wcol);
}
-static void widget_treerow_exec(uiWidgetColors *wcol,
- rcti *rect,
- const uiWidgetStateInfo *state,
- int UNUSED(roundboxalign),
- int indentation,
- const float zoom)
+static void widget_view_item(uiWidgetColors *wcol,
+ rcti *rect,
+ const uiWidgetStateInfo *state,
+ int UNUSED(roundboxalign),
+ const float zoom)
{
uiWidgetBase wtb;
widget_init(&wtb);
@@ -3690,31 +3683,6 @@ static void widget_treerow_exec(uiWidgetColors *wcol,
if ((state->but_flag & UI_ACTIVE) || (state->but_flag & UI_SELECT)) {
widgetbase_draw(&wtb, wcol);
}
-
- BLI_rcti_resize(rect, BLI_rcti_size_x(rect) - UI_UNIT_X * indentation, BLI_rcti_size_y(rect));
- BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0);
-}
-
-static void widget_treerow(uiBut *but,
- uiWidgetColors *wcol,
- rcti *rect,
- const uiWidgetStateInfo *state,
- int roundboxalign,
- const float zoom)
-{
- uiButTreeRow *tree_row = (uiButTreeRow *)but;
- BLI_assert(but->type == UI_BTYPE_TREEROW);
- widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation, zoom);
-}
-
-static void widget_gridtile(uiWidgetColors *wcol,
- rcti *rect,
- const uiWidgetStateInfo *state,
- int roundboxalign,
- const float zoom)
-{
- /* TODO Reuse tree-row drawing. */
- widget_treerow_exec(wcol, rect, state, roundboxalign, 0, zoom);
}
static void widget_nodesocket(uiBut *but,
@@ -3949,7 +3917,7 @@ static void widget_swatch(uiBut *but,
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3f(bw, bw, bw);
immBegin(GPU_PRIM_TRIS, 3);
@@ -4415,7 +4383,7 @@ static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType *
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* make mask to draw over image */
uchar col[4];
@@ -4608,14 +4576,9 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type)
wt.custom = widget_progressbar;
break;
- case UI_WTYPE_TREEROW:
- wt.wcol_theme = &btheme->tui.wcol_view_item;
- wt.custom = widget_treerow;
- break;
-
- case UI_WTYPE_GRID_TILE:
+ case UI_WTYPE_VIEW_ITEM:
wt.wcol_theme = &btheme->tui.wcol_view_item;
- wt.draw = widget_gridtile;
+ wt.draw = widget_view_item;
break;
case UI_WTYPE_NODESOCKET:
@@ -4949,13 +4912,8 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
fstyle = &style->widgetlabel;
break;
- case UI_BTYPE_TREEROW:
- wt = widget_type(UI_WTYPE_TREEROW);
- fstyle = &style->widgetlabel;
- break;
-
- case UI_BTYPE_GRID_TILE:
- wt = widget_type(UI_WTYPE_GRID_TILE);
+ case UI_BTYPE_VIEW_ITEM:
+ wt = widget_type(UI_WTYPE_VIEW_ITEM);
fstyle = &style->widgetlabel;
break;
@@ -5121,7 +5079,7 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol,
if (ELEM(direction, UI_DIR_UP, UI_DIR_DOWN)) {
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
const bool is_down = (direction == UI_DIR_DOWN);
const int sign = is_down ? 1 : -1;
@@ -5197,10 +5155,10 @@ static void draw_disk_shaded(float start,
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
if (shaded) {
col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
}
else {
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ubv(col1);
}
@@ -5311,7 +5269,7 @@ void ui_draw_pie_center(uiBlock *block)
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ubv(btheme->tui.wcol_pie_menu.outline);
imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, pie_radius_internal, subd);
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index cfdcd08df4a..93b94d42d39 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -895,6 +895,12 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid)
case TH_WIDGET_TEXT_CURSOR:
cp = btheme->tui.widget_text_cursor;
break;
+ case TH_WIDGET_TEXT_SELECTION:
+ cp = btheme->tui.wcol_text.item;
+ break;
+ case TH_WIDGET_TEXT_HIGHLIGHT:
+ cp = btheme->tui.wcol_text.text_sel;
+ break;
case TH_TRANSPARENT_CHECKER_PRIMARY:
cp = btheme->tui.transparent_checker_primary;
diff --git a/source/blender/editors/interface/view2d.cc b/source/blender/editors/interface/view2d.cc
index ee4bfd351ae..52e278cd4b0 100644
--- a/source/blender/editors/interface/view2d.cc
+++ b/source/blender/editors/interface/view2d.cc
@@ -87,11 +87,11 @@ BLI_INLINE void clamp_rctf_to_rcti(rcti *dst, const rctf *src)
* \{ */
/**
- * helper to allow scrollbars to dynamically hide
- * - returns a copy of the scrollbar settings with the flags to display
- * horizontal/vertical scrollbars removed
- * - input scroll value is the v2d->scroll var
- * - hide flags are set per region at drawtime
+ * Helper to allow scroll-bars to dynamically hide:
+ * - Returns a copy of the scroll-bar settings with the flags to display
+ * horizontal/vertical scroll-bars removed.
+ * - Input scroll value is the v2d->scroll var.
+ * - Hide flags are set per region at draw-time.
*/
static int view2d_scroll_mapped(int scroll)
{
@@ -115,7 +115,7 @@ void UI_view2d_mask_from_win(const View2D *v2d, rcti *r_mask)
/**
* Called each time #View2D.cur changes, to dynamically update masks.
*
- * \param mask_scroll: Optionally clamp scrollbars by this region.
+ * \param mask_scroll: Optionally clamp scroll-bars by this region.
*/
static void view2d_masks(View2D *v2d, const rcti *mask_scroll)
{
@@ -150,7 +150,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll)
}
/* Do not use mapped scroll here because we want to update scroller rects
- * even if they are not displayed. For init purposes. See T75003.*/
+ * even if they are not displayed. For initialization purposes. See T75003. */
scroll = v2d->scroll;
/* Scrollers are based off region-size:
@@ -177,7 +177,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll)
/* Currently, all regions that have vertical scale handles,
* also have the scrubbing area at the top.
- * So the scrollbar has to move down a bit. */
+ * So the scroll-bar has to move down a bit. */
if (scroll & V2D_SCROLL_VERTICAL_HANDLES) {
v2d->vert.ymax -= UI_TIME_SCRUB_MARGIN_Y;
}
@@ -377,7 +377,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize)
rctf *cur, *tot;
/* use mask as size of region that View2D resides in, as it takes into account
- * scrollbars already - keep in sync with zoomx/zoomy in view_zoomstep_apply_ex! */
+ * scroll-bars already - keep in sync with zoomx/zoomy in #view_zoomstep_apply_ex! */
winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1);
winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1);
@@ -1136,7 +1136,7 @@ void UI_view2d_multi_grid_draw(
GPU_line_width(1.0f);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
immBeginAtMost(GPU_PRIM_LINES, vertex_count);
for (int level = 0; level < totlevels; level++) {
@@ -1234,7 +1234,7 @@ void UI_view2d_dot_grid_draw(const View2D *v2d,
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
/* Scaling the dots fully with the zoom looks too busy, but a bit of size variation is nice. */
const float min_point_size = 2.0f * UI_DPI_FAC;
@@ -1326,8 +1326,8 @@ struct View2DScrollers {
/* focus bubbles */
/* focus bubbles */
/* focus bubbles */
- int vert_min, vert_max; /* vertical scrollbar */
- int hor_min, hor_max; /* horizontal scrollbar */
+ int vert_min, vert_max; /* vertical scroll-bar */
+ int hor_min, hor_max; /* horizontal scroll-bar */
/** Exact size of slider backdrop. */
rcti hor, vert;
@@ -1380,7 +1380,7 @@ void UI_view2d_scrollers_calc(View2D *v2d,
r_scrollers->hor = hor;
/* scroller 'buttons':
- * - These should always remain within the visible region of the scrollbar
+ * - These should always remain within the visible region of the scroll-bar
* - They represent the region of 'tot' that is visible in 'cur'
*/
@@ -1473,14 +1473,14 @@ void UI_view2d_scrollers_draw_ex(View2D *v2d, const rcti *mask_custom, bool use_
uchar scrollers_back_color[4];
- /* Color for scrollbar backs */
+ /* Color for scroll-bar backs. */
UI_GetThemeColor4ubv(TH_BACK, scrollers_back_color);
/* make copies of rects for less typing */
vert = scrollers.vert;
hor = scrollers.hor;
- /* horizontal scrollbar */
+ /* Horizontal scroll-bar. */
if (scroll & V2D_SCROLL_HORIZONTAL) {
uiWidgetColors wcol = btheme->tui.wcol_scroll;
/* 0..255 -> min...1 */
@@ -1515,7 +1515,7 @@ void UI_view2d_scrollers_draw_ex(View2D *v2d, const rcti *mask_custom, bool use_
UI_draw_widget_scroll(&wcol, &hor, &slider, state);
}
- /* vertical scrollbar */
+ /* Vertical scroll-bar. */
if (scroll & V2D_SCROLL_VERTICAL) {
uiWidgetColors wcol = btheme->tui.wcol_scroll;
rcti slider;
@@ -1695,6 +1695,41 @@ void UI_view2d_view_to_region_fl(
*r_region_y = v2d->mask.ymin + (y * BLI_rcti_size_y(&v2d->mask));
}
+bool UI_view2d_view_to_region_segment_clip(const View2D *v2d,
+ const float xy_a[2],
+ const float xy_b[2],
+ int r_region_a[2],
+ int r_region_b[2])
+{
+ rctf rect_unit;
+ rect_unit.xmin = rect_unit.ymin = 0.0f;
+ rect_unit.xmax = rect_unit.ymax = 1.0f;
+
+ /* Express given coordinates as proportional values. */
+ const float s_a[2] = {
+ (xy_a[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur),
+ (xy_a[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur),
+ };
+ const float s_b[2] = {
+ (xy_b[0] - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur),
+ (xy_b[1] - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur),
+ };
+
+ /* Set initial value in case coordinates lie outside bounds. */
+ r_region_a[0] = r_region_b[0] = r_region_a[1] = r_region_b[1] = V2D_IS_CLIPPED;
+
+ if (BLI_rctf_isect_segment(&rect_unit, s_a, s_b)) {
+ r_region_a[0] = (int)(v2d->mask.xmin + (s_a[0] * BLI_rcti_size_x(&v2d->mask)));
+ r_region_a[1] = (int)(v2d->mask.ymin + (s_a[1] * BLI_rcti_size_y(&v2d->mask)));
+ r_region_b[0] = (int)(v2d->mask.xmin + (s_b[0] * BLI_rcti_size_x(&v2d->mask)));
+ r_region_b[1] = (int)(v2d->mask.ymin + (s_b[1] * BLI_rcti_size_y(&v2d->mask)));
+
+ return true;
+ }
+
+ return false;
+}
+
void UI_view2d_view_to_region_rcti(const View2D *v2d, const rctf *rect_src, rcti *rect_dst)
{
const float cur_size[2] = {BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)};
@@ -2065,12 +2100,22 @@ void UI_view2d_text_cache_draw(ARegion *region)
col_pack_prev = v2s->col.pack;
}
- BLF_enable(font_id, BLF_CLIPPING);
- BLF_clipping(
- font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4);
- BLF_draw_default(
- v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX);
- BLF_disable(font_id, BLF_CLIPPING);
+ /* Don't use clipping if `v2s->rect` is not set. */
+ if (BLI_rcti_size_x(&v2s->rect) == 0 && BLI_rcti_size_y(&v2s->rect) == 0) {
+ BLF_draw_default((float)(v2s->mval[0] + xofs),
+ (float)(v2s->mval[1] + yofs),
+ 0.0,
+ v2s->str,
+ BLF_DRAW_STR_DUMMY_MAX);
+ }
+ else {
+ BLF_enable(font_id, BLF_CLIPPING);
+ BLF_clipping(
+ font_id, v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4);
+ BLF_draw_default(
+ v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, v2s->str, BLF_DRAW_STR_DUMMY_MAX);
+ BLF_disable(font_id, BLF_CLIPPING);
+ }
}
g_v2d_strings = nullptr;
diff --git a/source/blender/editors/interface/view2d_draw.cc b/source/blender/editors/interface/view2d_draw.cc
index d76230ba99c..ea4cf399a57 100644
--- a/source/blender/editors/interface/view2d_draw.cc
+++ b/source/blender/editors/interface/view2d_draw.cc
@@ -202,7 +202,7 @@ static void draw_parallel_lines(const ParallelLinesSet *lines,
immUniform1f("lineWidth", U.pixelsize - 1.0f);
}
else {
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
}
immUniformColor3ubv(color);
immBegin(GPU_PRIM_LINES, steps * 2);
diff --git a/source/blender/editors/interface/view2d_ops.cc b/source/blender/editors/interface/view2d_ops.cc
index ec15c4ffc9f..5f7d82e7b9a 100644
--- a/source/blender/editors/interface/view2d_ops.cc
+++ b/source/blender/editors/interface/view2d_ops.cc
@@ -1821,11 +1821,11 @@ static bool scroller_activate_poll(bContext *C)
View2D *v2d = &region->v2d;
wmEvent *event = win->eventstate;
- /* check if mouse in scrollbars, if they're enabled */
+ /* Check if mouse in scroll-bars, if they're enabled. */
return (UI_view2d_mouse_in_scrollers(region, v2d, event->xy) != 0);
}
-/* initialize customdata for scroller manipulation operator */
+/* Initialize #wmOperator.customdata for scroller manipulation operator. */
static void scroller_activate_init(bContext *C,
wmOperator *op,
const wmEvent *event,
@@ -2065,7 +2065,7 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
- /* check if mouse in scrollbars, if they're enabled */
+ /* check if mouse in scroll-bars, if they're enabled */
const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy);
/* if in a scroller, init customdata then set modal handler which will
diff --git a/source/blender/editors/interface/abstract_view.cc b/source/blender/editors/interface/views/abstract_view.cc
index dea9600fde4..077c76a08f1 100644
--- a/source/blender/editors/interface/abstract_view.cc
+++ b/source/blender/editors/interface/views/abstract_view.cc
@@ -10,6 +10,13 @@
namespace blender::ui {
+void AbstractView::register_item(AbstractViewItem &item)
+{
+ /* Actually modifies the item, not the view. But for the public API it "feels" a bit nicer to
+ * have the view base class register the items, rather than setting the view on the item. */
+ item.view_ = this;
+}
+
/* ---------------------------------------------------------------------- */
/** \name View Reconstruction
* \{ */
diff --git a/source/blender/editors/interface/views/abstract_view_item.cc b/source/blender/editors/interface/views/abstract_view_item.cc
new file mode 100644
index 00000000000..f73183d07e9
--- /dev/null
+++ b/source/blender/editors/interface/views/abstract_view_item.cc
@@ -0,0 +1,373 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup edinterface
+ */
+
+#include "BKE_context.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "WM_api.h"
+
+#include "UI_interface.h"
+#include "interface_intern.h"
+
+#include "UI_abstract_view.hh"
+
+namespace blender::ui {
+
+/* ---------------------------------------------------------------------- */
+/** \name View Reconstruction
+ * \{ */
+
+void AbstractViewItem::update_from_old(const AbstractViewItem &old)
+{
+ is_active_ = old.is_active_;
+ is_renaming_ = old.is_renaming_;
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Renaming
+ * \{ */
+
+bool AbstractViewItem::supports_renaming() const
+{
+ /* No renaming by default. */
+ return false;
+}
+bool AbstractViewItem::rename(StringRefNull /*new_name*/)
+{
+ /* No renaming by default. */
+ return false;
+}
+
+StringRef AbstractViewItem::get_rename_string() const
+{
+ /* No rename string by default. */
+ return {};
+}
+
+bool AbstractViewItem::is_renaming() const
+{
+ return is_renaming_;
+}
+
+void AbstractViewItem::begin_renaming()
+{
+ AbstractView &view = get_view();
+ if (view.is_renaming() || !supports_renaming()) {
+ return;
+ }
+
+ if (view.begin_renaming()) {
+ is_renaming_ = true;
+ }
+
+ StringRef initial_str = get_rename_string();
+ std::copy(std::begin(initial_str), std::end(initial_str), std::begin(view.get_rename_buffer()));
+}
+
+void AbstractViewItem::rename_apply()
+{
+ const AbstractView &view = get_view();
+ rename(view.get_rename_buffer().data());
+ end_renaming();
+}
+
+void AbstractViewItem::end_renaming()
+{
+ if (!is_renaming()) {
+ return;
+ }
+
+ is_renaming_ = false;
+
+ AbstractView &view = get_view();
+ view.end_renaming();
+}
+
+static AbstractViewItem *find_item_from_rename_button(const uiBut &rename_but)
+{
+ /* A minimal sanity check, can't do much more here. */
+ BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin);
+
+ LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) {
+ if (but->type != UI_BTYPE_VIEW_ITEM) {
+ continue;
+ }
+
+ uiButViewItem *view_item_but = (uiButViewItem *)but;
+ AbstractViewItem *item = reinterpret_cast<AbstractViewItem *>(view_item_but->view_item);
+ const AbstractView &view = item->get_view();
+
+ if (item->is_renaming() && (view.get_rename_buffer().data() == rename_but.poin)) {
+ return item;
+ }
+ }
+
+ return nullptr;
+}
+
+static void rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr))
+{
+ const uiBut *rename_but = static_cast<uiBut *>(arg);
+ AbstractViewItem *item = find_item_from_rename_button(*rename_but);
+ BLI_assert(item);
+ item->rename_apply();
+}
+
+void AbstractViewItem::add_rename_button(uiBlock &block)
+{
+ AbstractView &view = get_view();
+ uiBut *rename_but = uiDefBut(&block,
+ UI_BTYPE_TEXT,
+ 1,
+ "",
+ 0,
+ 0,
+ UI_UNIT_X * 10,
+ UI_UNIT_Y,
+ view.get_rename_buffer().data(),
+ 1.0f,
+ view.get_rename_buffer().size(),
+ 0,
+ 0,
+ "");
+
+ /* Gotta be careful with what's passed to the `arg1` here. Any view data will be freed once the
+ * callback is executed. */
+ UI_but_func_rename_set(rename_but, rename_button_fn, rename_but);
+ UI_but_flag_disable(rename_but, UI_BUT_UNDO);
+
+ const bContext *evil_C = reinterpret_cast<bContext *>(block.evil_C);
+ ARegion *region = CTX_wm_region(evil_C);
+ /* Returns false if the button was removed. */
+ if (UI_but_active_only(evil_C, region, &block, rename_but) == false) {
+ end_renaming();
+ }
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Context Menu
+ * \{ */
+
+void AbstractViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const
+{
+ /* No context menu by default. */
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name Drag 'n Drop
+ * \{ */
+
+std::unique_ptr<AbstractViewItemDragController> AbstractViewItem::create_drag_controller() const
+{
+ /* There's no drag controller (and hence no drag support) by default. */
+ return nullptr;
+}
+
+std::unique_ptr<AbstractViewItemDropController> AbstractViewItem::create_drop_controller() const
+{
+ /* There's no drop controller (and hence no drop support) by default. */
+ return nullptr;
+}
+
+AbstractViewItemDragController::AbstractViewItemDragController(AbstractView &view) : view_(view)
+{
+}
+
+void AbstractViewItemDragController::on_drag_start()
+{
+ /* Do nothing by default. */
+}
+
+AbstractViewItemDropController::AbstractViewItemDropController(AbstractView &view) : view_(view)
+{
+}
+
+/** \} */
+
+/* ---------------------------------------------------------------------- */
+/** \name General Getters & Setters
+ * \{ */
+
+AbstractView &AbstractViewItem::get_view() const
+{
+ if (UNLIKELY(!view_)) {
+ throw std::runtime_error(
+ "Invalid state, item must be registered through AbstractView::register_item()");
+ }
+ return *view_;
+}
+
+bool AbstractViewItem::is_active() const
+{
+ BLI_assert_msg(get_view().is_reconstructed(),
+ "State can't be queried until reconstruction is completed");
+ return is_active_;
+}
+
+/** \} */
+
+} // namespace blender::ui
+
+/* ---------------------------------------------------------------------- */
+/** \name C-API
+ * \{ */
+
+namespace blender::ui {
+
+/**
+ * Helper class to provide a higher level public (C-)API. Has access to private/protected view item
+ * members and ensures some invariants that way.
+ */
+class ViewItemAPIWrapper {
+ public:
+ static bool matches(const AbstractViewItem &a, const AbstractViewItem &b)
+ {
+ if (typeid(a) != typeid(b)) {
+ return false;
+ }
+ /* TODO should match the view as well. */
+ return a.matches(b);
+ }
+
+ static bool can_rename(const AbstractViewItem &item)
+ {
+ const AbstractView &view = item.get_view();
+ return !view.is_renaming() && item.supports_renaming();
+ }
+
+ static bool drag_start(bContext &C, const AbstractViewItem &item)
+ {
+ const std::unique_ptr<AbstractViewItemDragController> drag_controller =
+ item.create_drag_controller();
+ if (!drag_controller) {
+ return false;
+ }
+
+ WM_event_start_drag(&C,
+ ICON_NONE,
+ drag_controller->get_drag_type(),
+ drag_controller->create_drag_data(),
+ 0,
+ WM_DRAG_FREE_DATA);
+ drag_controller->on_drag_start();
+
+ return true;
+ }
+
+ static bool can_drop(const AbstractViewItem &item,
+ const wmDrag &drag,
+ const char **r_disabled_hint)
+ {
+ const std::unique_ptr<AbstractViewItemDropController> drop_controller =
+ item.create_drop_controller();
+ if (!drop_controller) {
+ return false;
+ }
+
+ return drop_controller->can_drop(drag, r_disabled_hint);
+ }
+
+ static std::string drop_tooltip(const AbstractViewItem &item, const wmDrag &drag)
+ {
+ const std::unique_ptr<AbstractViewItemDropController> drop_controller =
+ item.create_drop_controller();
+ if (!drop_controller) {
+ return {};
+ }
+
+ return drop_controller->drop_tooltip(drag);
+ }
+
+ static bool drop_handle(bContext &C, const AbstractViewItem &item, const ListBase &drags)
+ {
+ std::unique_ptr<AbstractViewItemDropController> drop_controller =
+ item.create_drop_controller();
+
+ const char *disabled_hint_dummy = nullptr;
+ LISTBASE_FOREACH (const wmDrag *, drag, &drags) {
+ if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) {
+ return drop_controller->on_drop(&C, *drag);
+ }
+ }
+
+ return false;
+ }
+};
+
+} // namespace blender::ui
+
+using namespace blender::ui;
+
+bool UI_view_item_is_active(const uiViewItemHandle *item_handle)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
+ return item.is_active();
+}
+
+bool UI_view_item_matches(const uiViewItemHandle *a_handle, const uiViewItemHandle *b_handle)
+{
+ const AbstractViewItem &a = reinterpret_cast<const AbstractViewItem &>(*a_handle);
+ const AbstractViewItem &b = reinterpret_cast<const AbstractViewItem &>(*b_handle);
+ return ViewItemAPIWrapper::matches(a, b);
+}
+
+bool UI_view_item_can_rename(const uiViewItemHandle *item_handle)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
+ return ViewItemAPIWrapper::can_rename(item);
+}
+
+void UI_view_item_begin_rename(uiViewItemHandle *item_handle)
+{
+ AbstractViewItem &item = reinterpret_cast<AbstractViewItem &>(*item_handle);
+ item.begin_renaming();
+}
+
+void UI_view_item_context_menu_build(bContext *C,
+ const uiViewItemHandle *item_handle,
+ uiLayout *column)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_handle);
+ item.build_context_menu(*C, *column);
+}
+
+bool UI_view_item_drag_start(bContext *C, const uiViewItemHandle *item_)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_);
+ return ViewItemAPIWrapper::drag_start(*C, item);
+}
+
+bool UI_view_item_can_drop(const uiViewItemHandle *item_,
+ const wmDrag *drag,
+ const char **r_disabled_hint)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_);
+ return ViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint);
+}
+
+char *UI_view_item_drop_tooltip(const uiViewItemHandle *item_, const wmDrag *drag)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_);
+
+ const std::string tooltip = ViewItemAPIWrapper::drop_tooltip(item, *drag);
+ return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str());
+}
+
+bool UI_view_item_drop_handle(bContext *C, const uiViewItemHandle *item_, const ListBase *drags)
+{
+ const AbstractViewItem &item = reinterpret_cast<const AbstractViewItem &>(*item_);
+ return ViewItemAPIWrapper::drop_handle(*C, item, *drags);
+}
+
+/** \} */
diff --git a/source/blender/editors/interface/grid_view.cc b/source/blender/editors/interface/views/grid_view.cc
index 19a2326fba1..52ff1460cbd 100644
--- a/source/blender/editors/interface/grid_view.cc
+++ b/source/blender/editors/interface/views/grid_view.cc
@@ -29,9 +29,8 @@ AbstractGridViewItem &AbstractGridView::add_item(std::unique_ptr<AbstractGridVie
items_.append(std::move(item));
AbstractGridViewItem &added_item = *items_.last();
- added_item.view_ = this;
-
item_map_.add(added_item.identifier_, &added_item);
+ register_item(added_item);
return added_item;
}
@@ -95,18 +94,19 @@ AbstractGridViewItem::AbstractGridViewItem(StringRef identifier) : identifier_(i
{
}
-bool AbstractGridViewItem::matches(const AbstractGridViewItem &other) const
+bool AbstractGridViewItem::matches(const AbstractViewItem &other) const
{
- return identifier_ == other.identifier_;
+ const AbstractGridViewItem &other_grid_item = dynamic_cast<const AbstractGridViewItem &>(other);
+ return identifier_ == other_grid_item.identifier_;
}
void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/,
void *but_arg1,
void * /*arg2*/)
{
- uiButGridTile *grid_tile_but = (uiButGridTile *)but_arg1;
+ uiButViewItem *view_item_but = (uiButViewItem *)but_arg1;
AbstractGridViewItem &grid_item = reinterpret_cast<AbstractGridViewItem &>(
- *grid_tile_but->view_item);
+ *view_item_but->view_item);
grid_item.activate();
}
@@ -114,8 +114,8 @@ void AbstractGridViewItem::grid_tile_click_fn(struct bContext * /*C*/,
void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
{
const GridViewStyle &style = get_view().get_style();
- grid_tile_but_ = (uiButGridTile *)uiDefBut(&block,
- UI_BTYPE_GRID_TILE,
+ view_item_but_ = (uiButViewItem *)uiDefBut(&block,
+ UI_BTYPE_VIEW_ITEM,
0,
"",
0,
@@ -129,15 +129,8 @@ void AbstractGridViewItem::add_grid_tile_button(uiBlock &block)
0,
"");
- grid_tile_but_->view_item = reinterpret_cast<uiGridViewItemHandle *>(this);
- UI_but_func_set(&grid_tile_but_->but, grid_tile_click_fn, grid_tile_but_, nullptr);
-}
-
-bool AbstractGridViewItem::is_active() const
-{
- BLI_assert_msg(get_view().is_reconstructed(),
- "State can't be queried until reconstruction is completed");
- return is_active_;
+ view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this);
+ UI_but_func_set(&view_item_but_->but, grid_tile_click_fn, view_item_but_, nullptr);
}
void AbstractGridViewItem::on_activate()
@@ -158,11 +151,6 @@ void AbstractGridViewItem::change_state_delayed()
}
}
-void AbstractGridViewItem::update_from_old(const AbstractGridViewItem &old)
-{
- is_active_ = old.is_active_;
-}
-
void AbstractGridViewItem::activate()
{
BLI_assert_msg(get_view().is_reconstructed(),
@@ -191,7 +179,7 @@ const AbstractGridView &AbstractGridViewItem::get_view() const
throw std::runtime_error(
"Invalid state, item must be added through AbstractGridView::add_item()");
}
- return *view_;
+ return dynamic_cast<AbstractGridView &>(*view_);
}
/* ---------------------------------------------------------------------- */
@@ -473,24 +461,3 @@ std::optional<bool> PreviewGridItem::should_be_active() const
}
} // namespace blender::ui
-
-using namespace blender::ui;
-
-/* ---------------------------------------------------------------------- */
-/* C-API */
-
-using namespace blender::ui;
-
-bool UI_grid_view_item_is_active(const uiGridViewItemHandle *item_handle)
-{
- const AbstractGridViewItem &item = reinterpret_cast<const AbstractGridViewItem &>(*item_handle);
- return item.is_active();
-}
-
-bool UI_grid_view_item_matches(const uiGridViewItemHandle *a_handle,
- const uiGridViewItemHandle *b_handle)
-{
- const AbstractGridViewItem &a = reinterpret_cast<const AbstractGridViewItem &>(*a_handle);
- const AbstractGridViewItem &b = reinterpret_cast<const AbstractGridViewItem &>(*b_handle);
- return a.matches(b);
-}
diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/views/interface_view.cc
index 70728565263..c568a8cab74 100644
--- a/source/blender/editors/interface/interface_view.cc
+++ b/source/blender/editors/interface/views/interface_view.cc
@@ -3,10 +3,16 @@
/** \file
* \ingroup edinterface
*
- * This part of the UI-View API is mostly needed to support persistent state of items within the
- * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we
- * can compare the old view items with the new view items and keep state persistent for matching
- * ones.
+ * Code to manage views as part of the regular screen hierarchy. E.g. managing ownership of views
+ * inside blocks (#uiBlock.views), looking up items in the region, passing WM notifiers to views,
+ * etc.
+ *
+ * Blocks and their contained views are reconstructed on every redraw. This file also contains
+ * functions related to this recreation of views inside blocks. For example to query state
+ * information before the view is done reconstructing (#AbstractView.is_reconstructed() returns
+ * false), it may be enough to query the previous version of the block/view/view-item. Since such
+ * queries rely on the details of the UI reconstruction process, they should remain internal to
+ * `interface/` code.
*/
#include <memory>
@@ -86,24 +92,24 @@ void UI_block_views_listen(const uiBlock *block, const wmRegionListenerParams *l
}
}
-uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2])
+uiViewItemHandle *UI_region_views_find_item_at(const ARegion *region, const int xy[2])
{
- uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy);
- if (!tree_row_but) {
+ uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_mouse_over(region, xy);
+ if (!item_but) {
return nullptr;
}
- return tree_row_but->tree_item;
+ return item_but->view_item;
}
-uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region)
+uiViewItemHandle *UI_region_views_find_active_item(const ARegion *region)
{
- uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_active(region);
- if (!tree_row_but) {
+ uiButViewItem *item_but = (uiButViewItem *)ui_view_item_find_active(region);
+ if (!item_but) {
return nullptr;
}
- return tree_row_but->tree_item;
+ return item_but->view_item;
}
static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractView &view)
@@ -151,39 +157,38 @@ uiViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block,
return reinterpret_cast<uiViewHandle *>(old_view);
}
-uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block,
- const uiTreeViewItemHandle *new_item_handle)
+uiButViewItem *ui_block_view_find_matching_view_item_but_in_old_block(
+ const uiBlock *new_block, const uiViewItemHandle *new_item_handle)
{
uiBlock *old_block = new_block->oldblock;
if (!old_block) {
return nullptr;
}
- const AbstractTreeViewItem &new_item = *reinterpret_cast<const AbstractTreeViewItem *>(
- new_item_handle);
+ const AbstractViewItem &new_item = *reinterpret_cast<const AbstractViewItem *>(new_item_handle);
const AbstractView *old_view = ui_block_view_find_matching_in_old_block_impl(
- *new_block, new_item.get_tree_view());
+ *new_block, new_item.get_view());
if (!old_view) {
return nullptr;
}
LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) {
- if (old_but->type != UI_BTYPE_TREEROW) {
+ if (old_but->type != UI_BTYPE_VIEW_ITEM) {
continue;
}
- uiButTreeRow *old_treerow_but = (uiButTreeRow *)old_but;
- if (!old_treerow_but->tree_item) {
+ uiButViewItem *old_item_but = (uiButViewItem *)old_but;
+ if (!old_item_but->view_item) {
continue;
}
- AbstractTreeViewItem &old_item = *reinterpret_cast<AbstractTreeViewItem *>(
- old_treerow_but->tree_item);
- /* Check if the row is from the expected tree-view. */
- if (&old_item.get_tree_view() != old_view) {
+ AbstractViewItem &old_item = *reinterpret_cast<AbstractViewItem *>(old_item_but->view_item);
+ /* Check if the item is from the expected view. */
+ if (&old_item.get_view() != old_view) {
continue;
}
- if (UI_tree_view_item_matches(new_item_handle, old_treerow_but->tree_item)) {
- return old_treerow_but;
+ if (UI_view_item_matches(reinterpret_cast<const uiViewItemHandle *>(&new_item),
+ reinterpret_cast<const uiViewItemHandle *>(&old_item))) {
+ return old_item_but;
}
}
diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/views/tree_view.cc
index d29cf367e59..43fdf741ac5 100644
--- a/source/blender/editors/interface/tree_view.cc
+++ b/source/blender/editors/interface/views/tree_view.cc
@@ -37,9 +37,11 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item(
if (root_ == nullptr) {
root_ = this;
}
-
+ AbstractTreeView &tree_view = static_cast<AbstractTreeView &>(*root_);
AbstractTreeViewItem &added_item = *children_.last();
added_item.root_ = root_;
+ tree_view.register_item(added_item);
+
if (root_ != this) {
/* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely
* nice to static_cast this, but well... */
@@ -95,7 +97,7 @@ AbstractTreeViewItem *AbstractTreeView::find_matching_child(
const AbstractTreeViewItem &lookup_item, const TreeViewOrItem &items)
{
for (const auto &iter_item : items.children_) {
- if (lookup_item.matches(*iter_item)) {
+ if (lookup_item.matches_single(*iter_item)) {
/* We have a matching item! */
return iter_item.get();
}
@@ -118,9 +120,8 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/,
void *but_arg1,
void * /*arg2*/)
{
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1;
- AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(
- *tree_row_but->tree_item);
+ uiButViewItem *item_but = (uiButViewItem *)but_arg1;
+ AbstractTreeViewItem &tree_item = reinterpret_cast<AbstractTreeViewItem &>(*item_but->view_item);
tree_item.activate();
/* Not only activate the item, also show its children. Maybe this should be optional, or
@@ -131,11 +132,11 @@ void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/,
void AbstractTreeViewItem::add_treerow_button(uiBlock &block)
{
/* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */
- tree_row_but_ = (uiButTreeRow *)uiDefBut(
- &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, "");
+ view_item_but_ = (uiButViewItem *)uiDefBut(
+ &block, UI_BTYPE_VIEW_ITEM, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, "");
- tree_row_but_->tree_item = reinterpret_cast<uiTreeViewItemHandle *>(this);
- UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr);
+ view_item_but_->view_item = reinterpret_cast<uiViewItemHandle *>(this);
+ UI_but_func_set(&view_item_but_->but, tree_row_click_fn, view_item_but_, nullptr);
}
void AbstractTreeViewItem::add_indent(uiLayout &row) const
@@ -167,10 +168,10 @@ void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C,
const wmWindow *win = CTX_wm_window(C);
const ARegion *region = CTX_wm_region(C);
- uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at(region,
- win->eventstate->xy);
- AbstractTreeViewItem *hovered_item = reinterpret_cast<AbstractTreeViewItem *>(
- hovered_item_handle);
+ uiViewItemHandle *hovered_item_handle = UI_region_views_find_item_at(region,
+ win->eventstate->xy);
+
+ AbstractTreeViewItem *hovered_item = from_item_handle<AbstractTreeViewItem>(hovered_item_handle);
BLI_assert(hovered_item != nullptr);
hovered_item->toggle_collapsed();
@@ -204,40 +205,6 @@ void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const
BLI_assert(is_collapse_chevron_but(but));
}
-AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button(
- const uiBut &rename_but)
-{
- /* A minimal sanity check, can't do much more here. */
- BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin);
-
- LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) {
- if (but->type != UI_BTYPE_TREEROW) {
- continue;
- }
-
- uiButTreeRow *tree_row_but = (uiButTreeRow *)but;
- AbstractTreeViewItem *item = reinterpret_cast<AbstractTreeViewItem *>(tree_row_but->tree_item);
- const AbstractTreeView &tree_view = item->get_tree_view();
-
- if (item->is_renaming() && (tree_view.get_rename_buffer().data() == rename_but.poin)) {
- return item;
- }
- }
-
- return nullptr;
-}
-
-void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr))
-{
- const uiBut *rename_but = static_cast<uiBut *>(arg);
- AbstractTreeViewItem *item = find_tree_item_from_rename_button(*rename_but);
- BLI_assert(item);
-
- const AbstractTreeView &tree_view = item->get_tree_view();
- item->rename(tree_view.get_rename_buffer().data());
- item->end_renaming();
-}
-
void AbstractTreeViewItem::add_rename_button(uiLayout &row)
{
uiBlock *block = uiLayoutGetBlock(&row);
@@ -247,33 +214,7 @@ void AbstractTreeViewItem::add_rename_button(uiLayout &row)
/* Enable emboss for the text button. */
UI_block_emboss_set(block, UI_EMBOSS);
- AbstractTreeView &tree_view = get_tree_view();
- uiBut *rename_but = uiDefBut(block,
- UI_BTYPE_TEXT,
- 1,
- "",
- 0,
- 0,
- UI_UNIT_X * 10,
- UI_UNIT_Y,
- tree_view.get_rename_buffer().data(),
- 1.0f,
- tree_view.get_rename_buffer().size(),
- 0,
- 0,
- "");
-
- /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the
- * callback is executed. */
- UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but);
- UI_but_flag_disable(rename_but, UI_BUT_UNDO);
-
- const bContext *evil_C = static_cast<bContext *>(block->evil_C);
- ARegion *region = CTX_wm_region(evil_C);
- /* Returns false if the button was removed. */
- if (UI_but_active_only(evil_C, region, block, rename_but) == false) {
- end_renaming();
- }
+ AbstractViewItem::add_rename_button(*block);
UI_block_emboss_set(block, previous_emboss);
UI_block_layout_set_current(block, &row);
@@ -306,80 +247,35 @@ bool AbstractTreeViewItem::supports_collapsing() const
return true;
}
-std::unique_ptr<AbstractTreeViewItemDragController> AbstractTreeViewItem::create_drag_controller()
- const
-{
- /* There's no drag controller (and hence no drag support) by default. */
- return nullptr;
-}
-
-std::unique_ptr<AbstractTreeViewItemDropController> AbstractTreeViewItem::create_drop_controller()
- const
-{
- /* There's no drop controller (and hence no drop support) by default. */
- return nullptr;
-}
-
-bool AbstractTreeViewItem::supports_renaming() const
+StringRef AbstractTreeViewItem::get_rename_string() const
{
- /* No renaming by default. */
- return false;
+ return label_;
}
bool AbstractTreeViewItem::rename(StringRefNull new_name)
{
- /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches()
+ /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches_single()
* recognizes the item. (It only compares labels by default.) */
label_ = new_name;
return true;
}
-void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const
+void AbstractTreeViewItem::update_from_old(const AbstractViewItem &old)
{
- /* No context menu by default. */
-}
+ AbstractViewItem::update_from_old(old);
-void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old)
-{
- is_open_ = old.is_open_;
- is_active_ = old.is_active_;
- is_renaming_ = old.is_renaming_;
+ const AbstractTreeViewItem &old_tree_item = dynamic_cast<const AbstractTreeViewItem &>(old);
+ is_open_ = old_tree_item.is_open_;
}
-bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const
+bool AbstractTreeViewItem::matches_single(const AbstractTreeViewItem &other) const
{
return label_ == other.label_;
}
-void AbstractTreeViewItem::begin_renaming()
-{
- AbstractTreeView &tree_view = get_tree_view();
- if (tree_view.is_renaming() || !supports_renaming()) {
- return;
- }
-
- if (tree_view.begin_renaming()) {
- is_renaming_ = true;
- }
-
- std::copy(std::begin(label_), std::end(label_), std::begin(tree_view.get_rename_buffer()));
-}
-
-void AbstractTreeViewItem::end_renaming()
-{
- if (!is_renaming()) {
- return;
- }
-
- is_renaming_ = false;
-
- AbstractTreeView &tree_view = get_tree_view();
- tree_view.end_renaming();
-}
-
AbstractTreeView &AbstractTreeViewItem::get_tree_view() const
{
- return static_cast<AbstractTreeView &>(*root_);
+ return dynamic_cast<AbstractTreeView &>(get_view());
}
int AbstractTreeViewItem::count_parents() const
@@ -415,26 +311,19 @@ void AbstractTreeViewItem::deactivate()
is_active_ = false;
}
-bool AbstractTreeViewItem::is_active() const
-{
- BLI_assert_msg(get_tree_view().is_reconstructed(),
- "State can't be queried until reconstruction is completed");
- return is_active_;
-}
-
bool AbstractTreeViewItem::is_hovered() const
{
BLI_assert_msg(get_tree_view().is_reconstructed(),
"State can't be queried until reconstruction is completed");
- BLI_assert_msg(tree_row_but_ != nullptr,
+ BLI_assert_msg(view_item_but_ != nullptr,
"Hovered state can't be queried before the tree row is being built");
- const uiTreeViewItemHandle *this_handle = reinterpret_cast<const uiTreeViewItemHandle *>(this);
+ const uiViewItemHandle *this_item_handle = reinterpret_cast<const uiViewItemHandle *>(this);
/* The new layout hasn't finished construction yet, so the final state of the button is unknown.
* Get the matching button from the previous redraw instead. */
- uiButTreeRow *old_treerow_but = ui_block_view_find_treerow_in_old_block(tree_row_but_->but.block,
- this_handle);
- return old_treerow_but && (old_treerow_but->but.flag & UI_ACTIVE);
+ uiButViewItem *old_item_but = ui_block_view_find_matching_view_item_but_in_old_block(
+ view_item_but_->but.block, this_item_handle);
+ return old_item_but && (old_item_but->but.flag & UI_ACTIVE);
}
bool AbstractTreeViewItem::is_collapsed() const
@@ -462,11 +351,6 @@ bool AbstractTreeViewItem::is_collapsible() const
return this->supports_collapsing();
}
-bool AbstractTreeViewItem::is_renaming() const
-{
- return is_renaming_;
-}
-
void AbstractTreeViewItem::ensure_parents_uncollapsed()
{
for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) {
@@ -474,19 +358,21 @@ void AbstractTreeViewItem::ensure_parents_uncollapsed()
}
}
-bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem &other) const
+bool AbstractTreeViewItem::matches(const AbstractViewItem &other) const
{
- if (!matches(other)) {
+ const AbstractTreeViewItem &other_tree_item = dynamic_cast<const AbstractTreeViewItem &>(other);
+
+ if (!matches_single(other_tree_item)) {
return false;
}
- if (count_parents() != other.count_parents()) {
+ if (count_parents() != other_tree_item.count_parents()) {
return false;
}
- for (AbstractTreeViewItem *parent = parent_, *other_parent = other.parent_;
+ for (AbstractTreeViewItem *parent = parent_, *other_parent = other_tree_item.parent_;
parent && other_parent;
parent = parent->parent_, other_parent = other_parent->parent_) {
- if (!parent->matches(*other_parent)) {
+ if (!parent->matches_single(*other_parent)) {
return false;
}
}
@@ -494,9 +380,9 @@ bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem
return true;
}
-uiButTreeRow *AbstractTreeViewItem::tree_row_button()
+uiButViewItem *AbstractTreeViewItem::view_item_button()
{
- return tree_row_but_;
+ return view_item_but_;
}
void AbstractTreeViewItem::change_state_delayed()
@@ -509,25 +395,6 @@ void AbstractTreeViewItem::change_state_delayed()
/* ---------------------------------------------------------------------- */
-AbstractTreeViewItemDragController::AbstractTreeViewItemDragController(AbstractTreeView &tree_view)
- : tree_view_(tree_view)
-{
-}
-
-void AbstractTreeViewItemDragController::on_drag_start()
-{
- /* Do nothing by default. */
-}
-
-/* ---------------------------------------------------------------------- */
-
-AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view)
- : tree_view_(tree_view)
-{
-}
-
-/* ---------------------------------------------------------------------- */
-
class TreeViewLayoutBuilder {
uiBlock &block_;
@@ -573,7 +440,7 @@ void TreeViewLayoutBuilder::polish_layout(const uiBlock &block)
UI_but_drawflag_enable(static_cast<uiBut *>(but->next), UI_BUT_NO_TEXT_PADDING);
}
- if (but->type == UI_BTYPE_TREEROW) {
+ if (but->type == UI_BTYPE_VIEW_ITEM) {
break;
}
}
@@ -685,154 +552,4 @@ std::optional<bool> BasicTreeViewItem::should_be_active() const
return std::nullopt;
}
-/* ---------------------------------------------------------------------- */
-
-/**
- * Helper for a public (C-)API, presenting higher level functionality. Has access to internal
- * data/functionality (friend of #AbstractTreeViewItem), which is sometimes needed when
- * functionality of the API needs to be constructed from multiple internal conditions and/or
- * functions that on their own shouldn't be part of the API.
- */
-class TreeViewItemAPIWrapper {
- public:
- static bool matches(const AbstractTreeViewItem &a, const AbstractTreeViewItem &b)
- {
- /* TODO should match the tree-view as well. */
- return a.matches_including_parents(b);
- }
-
- static bool drag_start(bContext &C, const AbstractTreeViewItem &item)
- {
- const std::unique_ptr<AbstractTreeViewItemDragController> drag_controller =
- item.create_drag_controller();
- if (!drag_controller) {
- return false;
- }
-
- WM_event_start_drag(&C,
- ICON_NONE,
- drag_controller->get_drag_type(),
- drag_controller->create_drag_data(),
- 0,
- WM_DRAG_FREE_DATA);
- drag_controller->on_drag_start();
-
- return true;
- }
-
- static bool can_drop(const AbstractTreeViewItem &item,
- const wmDrag &drag,
- const char **r_disabled_hint)
- {
- const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
- item.create_drop_controller();
- if (!drop_controller) {
- return false;
- }
-
- return drop_controller->can_drop(drag, r_disabled_hint);
- }
-
- static std::string drop_tooltip(const AbstractTreeViewItem &item, const wmDrag &drag)
- {
- const std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
- item.create_drop_controller();
- if (!drop_controller) {
- return {};
- }
-
- return drop_controller->drop_tooltip(drag);
- }
-
- static bool drop_handle(bContext &C, const AbstractTreeViewItem &item, const ListBase &drags)
- {
- std::unique_ptr<AbstractTreeViewItemDropController> drop_controller =
- item.create_drop_controller();
-
- const char *disabled_hint_dummy = nullptr;
- LISTBASE_FOREACH (const wmDrag *, drag, &drags) {
- if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) {
- return drop_controller->on_drop(&C, *drag);
- }
- }
-
- return false;
- }
-
- static bool can_rename(const AbstractTreeViewItem &item)
- {
- const AbstractTreeView &tree_view = item.get_tree_view();
- return !tree_view.is_renaming() && item.supports_renaming();
- }
-};
-
} // namespace blender::ui
-
-/* ---------------------------------------------------------------------- */
-/* C-API */
-
-using namespace blender::ui;
-
-bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
- return item.is_active();
-}
-
-bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle,
- const uiTreeViewItemHandle *b_handle)
-{
- const AbstractTreeViewItem &a = reinterpret_cast<const AbstractTreeViewItem &>(*a_handle);
- const AbstractTreeViewItem &b = reinterpret_cast<const AbstractTreeViewItem &>(*b_handle);
- return TreeViewItemAPIWrapper::matches(a, b);
-}
-
-bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
- return TreeViewItemAPIWrapper::drag_start(*C, item);
-}
-
-bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_,
- const wmDrag *drag,
- const char **r_disabled_hint)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
- return TreeViewItemAPIWrapper::can_drop(item, *drag, r_disabled_hint);
-}
-
-char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
-
- const std::string tooltip = TreeViewItemAPIWrapper::drop_tooltip(item, *drag);
- return tooltip.empty() ? nullptr : BLI_strdup(tooltip.c_str());
-}
-
-bool UI_tree_view_item_drop_handle(bContext *C,
- const uiTreeViewItemHandle *item_,
- const ListBase *drags)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_);
- return TreeViewItemAPIWrapper::drop_handle(*C, item, *drags);
-}
-
-bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
- return TreeViewItemAPIWrapper::can_rename(item);
-}
-
-void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle)
-{
- AbstractTreeViewItem &item = reinterpret_cast<AbstractTreeViewItem &>(*item_handle);
- item.begin_renaming();
-}
-
-void UI_tree_view_item_context_menu_build(bContext *C,
- const uiTreeViewItemHandle *item_handle,
- uiLayout *column)
-{
- const AbstractTreeViewItem &item = reinterpret_cast<const AbstractTreeViewItem &>(*item_handle);
- item.build_context_menu(*C, *column);
-}
diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt
index a716c00d5d9..568ece00c4c 100644
--- a/source/blender/editors/io/CMakeLists.txt
+++ b/source/blender/editors/io/CMakeLists.txt
@@ -11,9 +11,9 @@ set(INC
../../io/collada
../../io/common
../../io/gpencil
+ ../../io/stl
../../io/usd
../../io/wavefront_obj
- ../../io/stl
../../makesdna
../../makesrna
../../windowmanager
@@ -33,8 +33,8 @@ set(SRC
io_gpencil_utils.c
io_obj.c
io_ops.c
- io_usd.c
io_stl_ops.c
+ io_usd.c
io_alembic.h
io_cache.h
@@ -42,8 +42,8 @@ set(SRC
io_gpencil.h
io_obj.h
io_ops.h
- io_usd.h
io_stl_ops.h
+ io_usd.h
)
set(LIB
diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c
index 0068586730f..d4855f470ff 100644
--- a/source/blender/editors/io/io_alembic.c
+++ b/source/blender/editors/io/io_alembic.c
@@ -39,6 +39,7 @@
# include "RNA_define.h"
# include "RNA_enum_types.h"
+# include "ED_fileselect.h"
# include "ED_object.h"
# include "UI_interface.h"
@@ -75,20 +76,7 @@ static int wm_alembic_export_invoke(bContext *C, wmOperator *op, const wmEvent *
RNA_boolean_set(op->ptr, "init_scene_frame_range", true);
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- Main *bmain = CTX_data_main(C);
- char filepath[FILE_MAX];
-
- if (BKE_main_blendfile_path(bmain)[0] == '\0') {
- BLI_strncpy(filepath, "untitled", sizeof(filepath));
- }
- else {
- BLI_strncpy(filepath, BKE_main_blendfile_path(bmain), sizeof(filepath));
- }
-
- BLI_path_extension_replace(filepath, sizeof(filepath), ".abc");
- RNA_string_set(op->ptr, "filepath", filepath);
- }
+ ED_fileselect_ensure_default_filepath(C, op, ".abc");
WM_event_add_fileselect(C, op);
@@ -99,7 +87,7 @@ static int wm_alembic_export_invoke(bContext *C, wmOperator *op, const wmEvent *
static int wm_alembic_export_exec(bContext *C, wmOperator *op)
{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -619,7 +607,7 @@ static int wm_alembic_import_invoke(bContext *C, wmOperator *op, const wmEvent *
static int wm_alembic_import_exec(bContext *C, wmOperator *op)
{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -651,16 +639,16 @@ static int wm_alembic_import_exec(bContext *C, wmOperator *op)
ED_object_mode_set(C, OB_MODE_OBJECT);
}
- bool ok = ABC_import(C,
- filename,
- scale,
- is_sequence,
- set_frame_range,
- sequence_len,
- offset,
- validate_meshes,
- always_add_cache_reader,
- as_background_job);
+ struct AlembicImportParams params = {0};
+ params.global_scale = scale;
+ params.sequence_len = sequence_len;
+ params.sequence_offset = offset;
+ params.is_sequence = is_sequence;
+ params.set_frame_range = set_frame_range;
+ params.validate_meshes = validate_meshes;
+ params.always_add_cache_reader = always_add_cache_reader;
+
+ bool ok = ABC_import(C, filename, &params, as_background_job);
return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index c491e7a5815..1048d0eca32 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -19,6 +19,7 @@
# include "DEG_depsgraph.h"
+# include "ED_fileselect.h"
# include "ED_object.h"
# include "RNA_access.h"
@@ -36,22 +37,7 @@
static int wm_collada_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- Main *bmain = CTX_data_main(C);
-
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- char filepath[FILE_MAX];
- const char *blendfile_path = BKE_main_blendfile_path(bmain);
-
- if (blendfile_path[0] == '\0') {
- BLI_strncpy(filepath, "untitled", sizeof(filepath));
- }
- else {
- BLI_strncpy(filepath, blendfile_path, sizeof(filepath));
- }
-
- BLI_path_extension_replace(filepath, sizeof(filepath), ".dae");
- RNA_string_set(op->ptr, "filepath", filepath);
- }
+ ED_fileselect_ensure_default_filepath(C, op, ".dae");
WM_event_add_fileselect(C, op);
@@ -98,7 +84,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
int export_count;
int sample_animations;
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -707,15 +693,17 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op)
int min_chain_length;
int keep_bind_info;
+ int custom_normals;
ImportSettings import_settings;
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
/* Options panel */
import_units = RNA_boolean_get(op->ptr, "import_units");
+ custom_normals = RNA_boolean_get(op->ptr, "custom_normals");
find_chains = RNA_boolean_get(op->ptr, "find_chains");
auto_connect = RNA_boolean_get(op->ptr, "auto_connect");
fix_orientation = RNA_boolean_get(op->ptr, "fix_orientation");
@@ -728,6 +716,7 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op)
import_settings.filepath = filename;
import_settings.import_units = import_units != 0;
+ import_settings.custom_normals = custom_normals != 0;
import_settings.auto_connect = auto_connect != 0;
import_settings.find_chains = find_chains != 0;
import_settings.fix_orientation = fix_orientation != 0;
@@ -755,6 +744,7 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr)
uiItemL(box, IFACE_("Import Data Options"), ICON_MESH_DATA);
uiItemR(box, imfptr, "import_units", 0, NULL, ICON_NONE);
+ uiItemR(box, imfptr, "custom_normals", 0, NULL, ICON_NONE);
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Armature Options"), ICON_ARMATURE_DATA);
@@ -806,6 +796,12 @@ void WM_OT_collada_import(wmOperatorType *ot)
"otherwise use the settings from the Imported scene");
RNA_def_boolean(ot->srna,
+ "custom_normals",
+ 1,
+ "Custom Normals",
+ "Import custom normals, if available (otherwise Blender will compute them)");
+
+ RNA_def_boolean(ot->srna,
"fix_orientation",
0,
"Fix Leaf Bones",
diff --git a/source/blender/editors/io/io_gpencil_export.c b/source/blender/editors/io/io_gpencil_export.c
index 3f905dd7de0..662a372b608 100644
--- a/source/blender/editors/io/io_gpencil_export.c
+++ b/source/blender/editors/io/io_gpencil_export.c
@@ -20,6 +20,8 @@
# include "BLT_translation.h"
+# include "ED_fileselect.h"
+
# include "RNA_access.h"
# include "RNA_define.h"
@@ -71,24 +73,6 @@ static void gpencil_export_common_props_definition(wmOperatorType *ot)
"Normalize",
"Export strokes with constant thickness");
}
-
-static void set_export_filepath(bContext *C, wmOperator *op, const char *extension)
-{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- Main *bmain = CTX_data_main(C);
- char filepath[FILE_MAX];
-
- if (BKE_main_blendfile_path(bmain)[0] == '\0') {
- BLI_strncpy(filepath, "untitled", sizeof(filepath));
- }
- else {
- BLI_strncpy(filepath, BKE_main_blendfile_path(bmain), sizeof(filepath));
- }
-
- BLI_path_extension_replace(filepath, sizeof(filepath), extension);
- RNA_string_set(op->ptr, "filepath", filepath);
- }
-}
# endif
/* <-------- SVG single frame export. --------> */
@@ -109,7 +93,7 @@ static bool wm_gpencil_export_svg_common_check(bContext *UNUSED(C), wmOperator *
static int wm_gpencil_export_svg_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- set_export_filepath(C, op, ".svg");
+ ED_fileselect_ensure_default_filepath(C, op, ".svg");
WM_event_add_fileselect(C, op);
@@ -121,7 +105,7 @@ static int wm_gpencil_export_svg_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -233,7 +217,7 @@ void WM_OT_gpencil_export_svg(wmOperatorType *ot)
FILE_SAVE,
WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
- FILE_SORT_ALPHA);
+ FILE_SORT_DEFAULT);
gpencil_export_common_props_definition(ot);
@@ -264,7 +248,7 @@ static bool wm_gpencil_export_pdf_common_check(bContext *UNUSED(C), wmOperator *
static int wm_gpencil_export_pdf_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- set_export_filepath(C, op, ".pdf");
+ ED_fileselect_ensure_default_filepath(C, op, ".pdf");
WM_event_add_fileselect(C, op);
@@ -276,7 +260,7 @@ static int wm_gpencil_export_pdf_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -391,7 +375,7 @@ void WM_OT_gpencil_export_pdf(wmOperatorType *ot)
FILE_SAVE,
WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
- FILE_SORT_ALPHA);
+ FILE_SORT_DEFAULT);
static const EnumPropertyItem gpencil_export_frame_items[] = {
{GP_EXPORT_FRAME_ACTIVE, "ACTIVE", 0, "Active", "Include only active frame"},
diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c
index 9ac64407dcf..eb53f66d8b8 100644
--- a/source/blender/editors/io/io_gpencil_import.c
+++ b/source/blender/editors/io/io_gpencil_import.c
@@ -9,6 +9,8 @@
# include "BLI_path_util.h"
+# include "MEM_guardedalloc.h"
+
# include "DNA_gpencil_types.h"
# include "DNA_space_types.h"
@@ -63,7 +65,8 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false) ||
+ !(RNA_struct_find_property(op->ptr, "directory"))) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -75,9 +78,6 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op)
}
View3D *v3d = get_invoke_view3d(C);
- char filename[FILE_MAX];
- RNA_string_get(op->ptr, "filepath", filename);
-
/* Set flags. */
int flag = 0;
@@ -101,13 +101,31 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op)
.resolution = resolution,
};
- /* Do Import. */
- WM_cursor_wait(1);
- const bool done = gpencil_io_import(filename, &params);
- WM_cursor_wait(0);
-
- if (!done) {
- BKE_report(op->reports, RPT_WARNING, "Unable to import SVG");
+ /* Loop all selected files to import them. All SVG imported shared the same import
+ * parameters, but they are created in separated grease pencil objects. */
+ PropertyRNA *prop;
+ if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
+ char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL);
+
+ if ((prop = RNA_struct_find_property(op->ptr, "files"))) {
+ char file_path[FILE_MAX];
+ RNA_PROP_BEGIN (op->ptr, itemptr, prop) {
+ char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL);
+ BLI_join_dirfile(file_path, sizeof(file_path), directory, filename);
+ MEM_freeN(filename);
+
+ /* Do Import. */
+ WM_cursor_wait(1);
+ RNA_string_get(&itemptr, "name", params.filename);
+ const bool done = gpencil_io_import(file_path, &params);
+ WM_cursor_wait(0);
+ if (!done) {
+ BKE_reportf(op->reports, RPT_WARNING, "Unable to import '%s'", file_path);
+ }
+ }
+ RNA_PROP_END;
+ }
+ MEM_freeN(directory);
}
return OPERATOR_FINISHED;
@@ -149,10 +167,11 @@ void WM_OT_gpencil_import_svg(wmOperatorType *ot)
ot->check = wm_gpencil_import_svg_common_check;
WM_operator_properties_filesel(ot,
- FILE_TYPE_OBJECT_IO,
+ FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO,
FILE_BLENDER,
FILE_OPENFILE,
- WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS,
+ WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS |
+ WM_FILESEL_DIRECTORY | WM_FILESEL_FILES,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c
index 79ec7ebf2a5..cb8eafeb52d 100644
--- a/source/blender/editors/io/io_obj.c
+++ b/source/blender/editors/io/io_obj.c
@@ -18,6 +18,7 @@
# include "BLT_translation.h"
+# include "ED_fileselect.h"
# include "ED_outliner.h"
# include "MEM_guardedalloc.h"
@@ -58,20 +59,7 @@ static const EnumPropertyItem io_obj_path_mode[] = {
static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- Main *bmain = CTX_data_main(C);
- char filepath[FILE_MAX];
-
- if (BKE_main_blendfile_path(bmain)[0] == '\0') {
- BLI_strncpy(filepath, "untitled", sizeof(filepath));
- }
- else {
- BLI_strncpy(filepath, BKE_main_blendfile_path(bmain), sizeof(filepath));
- }
-
- BLI_path_extension_replace(filepath, sizeof(filepath), ".obj");
- RNA_string_set(op->ptr, "filepath", filepath);
- }
+ ED_fileselect_ensure_default_filepath(C, op, ".obj");
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
@@ -79,7 +67,7 @@ static int wm_obj_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
static int wm_obj_export_exec(bContext *C, wmOperator *op)
{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -105,6 +93,7 @@ static int wm_obj_export_exec(bContext *C, wmOperator *op)
export_params.path_mode = RNA_enum_get(op->ptr, "path_mode");
export_params.export_triangulated_mesh = RNA_boolean_get(op->ptr, "export_triangulated_mesh");
export_params.export_curves_as_nurbs = RNA_boolean_get(op->ptr, "export_curves_as_nurbs");
+ export_params.export_pbr_extensions = RNA_boolean_get(op->ptr, "export_pbr_extensions");
export_params.export_object_groups = RNA_boolean_get(op->ptr, "export_object_groups");
export_params.export_material_groups = RNA_boolean_get(op->ptr, "export_material_groups");
@@ -126,51 +115,50 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
- /* Animation options. */
- uiLayout *box = uiLayoutBox(layout);
- uiItemL(box, IFACE_("Animation"), ICON_ANIM);
- uiLayout *col = uiLayoutColumn(box, false);
- uiLayout *sub = uiLayoutColumn(col, false);
- uiItemR(sub, imfptr, "export_animation", 0, NULL, ICON_NONE);
- sub = uiLayoutColumn(sub, true);
- uiItemR(sub, imfptr, "start_frame", 0, IFACE_("Frame Start"), ICON_NONE);
- uiItemR(sub, imfptr, "end_frame", 0, IFACE_("End"), ICON_NONE);
- uiLayoutSetEnabled(sub, export_animation);
+ uiLayout *box, *col, *sub, *row;
/* Object Transform options. */
box = uiLayoutBox(layout);
- uiItemL(box, IFACE_("Object Properties"), ICON_OBJECT_DATA);
col = uiLayoutColumn(box, false);
- sub = uiLayoutColumn(col, false);
- uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE);
- uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE);
- sub = uiLayoutColumn(col, false);
+ sub = uiLayoutColumnWithHeading(col, false, IFACE_("Limit to"));
+ uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE);
uiItemR(sub, imfptr, "scaling_factor", 0, NULL, ICON_NONE);
+
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "forward_axis", UI_ITEM_R_EXPAND, IFACE_("Forward Axis"), ICON_NONE);
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "up_axis", UI_ITEM_R_EXPAND, IFACE_("Up Axis"), ICON_NONE);
+
+ col = uiLayoutColumn(box, false);
+ sub = uiLayoutColumn(col, false);
sub = uiLayoutColumnWithHeading(col, false, IFACE_("Objects"));
- uiItemR(sub, imfptr, "export_selected_objects", 0, IFACE_("Selected Only"), ICON_NONE);
uiItemR(sub, imfptr, "apply_modifiers", 0, IFACE_("Apply Modifiers"), ICON_NONE);
uiItemR(sub, imfptr, "export_eval_mode", 0, IFACE_("Properties"), ICON_NONE);
- sub = uiLayoutColumn(sub, false);
- uiLayoutSetEnabled(sub, export_materials);
- uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE);
- /* Options for what to write. */
+ /* Geometry options. */
box = uiLayoutBox(layout);
- uiItemL(box, IFACE_("Geometry Export"), ICON_EXPORT);
col = uiLayoutColumn(box, false);
- sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export"));
+ sub = uiLayoutColumnWithHeading(col, false, IFACE_("Geometry"));
uiItemR(sub, imfptr, "export_uv", 0, IFACE_("UV Coordinates"), ICON_NONE);
uiItemR(sub, imfptr, "export_normals", 0, IFACE_("Normals"), ICON_NONE);
uiItemR(sub, imfptr, "export_colors", 0, IFACE_("Colors"), ICON_NONE);
- uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Materials"), ICON_NONE);
uiItemR(sub, imfptr, "export_triangulated_mesh", 0, IFACE_("Triangulated Mesh"), ICON_NONE);
uiItemR(sub, imfptr, "export_curves_as_nurbs", 0, IFACE_("Curves as NURBS"), ICON_NONE);
+ /* Material options. */
+ box = uiLayoutBox(layout);
+ col = uiLayoutColumn(box, false);
+ sub = uiLayoutColumnWithHeading(col, false, IFACE_("Materials"));
+ uiItemR(sub, imfptr, "export_materials", 0, IFACE_("Export"), ICON_NONE);
+ sub = uiLayoutColumn(sub, false);
+ uiLayoutSetEnabled(sub, export_materials);
+ uiItemR(sub, imfptr, "export_pbr_extensions", 0, IFACE_("PBR Extensions"), ICON_NONE);
+ uiItemR(sub, imfptr, "path_mode", 0, IFACE_("Path Mode"), ICON_NONE);
+
/* Grouping options. */
box = uiLayoutBox(layout);
- uiItemL(box, IFACE_("Grouping"), ICON_GROUP);
col = uiLayoutColumn(box, false);
- sub = uiLayoutColumnWithHeading(col, false, IFACE_("Export"));
+ sub = uiLayoutColumnWithHeading(col, false, IFACE_("Grouping"));
uiItemR(sub, imfptr, "export_object_groups", 0, IFACE_("Object Groups"), ICON_NONE);
uiItemR(sub, imfptr, "export_material_groups", 0, IFACE_("Material Groups"), ICON_NONE);
uiItemR(sub, imfptr, "export_vertex_groups", 0, IFACE_("Vertex Groups"), ICON_NONE);
@@ -178,6 +166,16 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
sub = uiLayoutColumn(sub, false);
uiLayoutSetEnabled(sub, export_smooth_groups);
uiItemR(sub, imfptr, "smooth_group_bitflags", 0, IFACE_("Smooth Group Bitflags"), ICON_NONE);
+
+ /* Animation options. */
+ box = uiLayoutBox(layout);
+ col = uiLayoutColumn(box, false);
+ sub = uiLayoutColumnWithHeading(col, false, IFACE_("Animation"));
+ uiItemR(sub, imfptr, "export_animation", 0, IFACE_("Export"), ICON_NONE);
+ sub = uiLayoutColumn(sub, true);
+ uiLayoutSetEnabled(sub, export_animation);
+ uiItemR(sub, imfptr, "start_frame", 0, IFACE_("Frame Start"), ICON_NONE);
+ uiItemR(sub, imfptr, "end_frame", 0, IFACE_("End"), ICON_NONE);
}
static void wm_obj_export_draw(bContext *UNUSED(C), wmOperator *op)
@@ -223,15 +221,30 @@ static bool wm_obj_export_check(bContext *C, wmOperator *op)
RNA_int_set(op->ptr, "start_frame", start);
RNA_int_set(op->ptr, "end_frame", end);
}
+ return changed;
+}
+
+/* Both forward and up axes cannot be along the same direction. */
+static void forward_axis_update(struct Main *UNUSED(main),
+ struct Scene *UNUSED(scene),
+ struct PointerRNA *ptr)
+{
+ int forward = RNA_enum_get(ptr, "forward_axis");
+ int up = RNA_enum_get(ptr, "up_axis");
+ if ((forward % 3) == (up % 3)) {
+ RNA_enum_set(ptr, "up_axis", (up + 1) % 6);
+ }
+}
- /* Both forward and up axes cannot be the same (or same except opposite sign). */
- if (RNA_enum_get(op->ptr, "forward_axis") % TOTAL_AXES ==
- (RNA_enum_get(op->ptr, "up_axis") % TOTAL_AXES)) {
- /* TODO(@ankitm): Show a warning here. */
- RNA_enum_set(op->ptr, "up_axis", RNA_enum_get(op->ptr, "up_axis") % TOTAL_AXES + 1);
- changed = true;
+static void up_axis_update(struct Main *UNUSED(main),
+ struct Scene *UNUSED(scene),
+ struct PointerRNA *ptr)
+{
+ int forward = RNA_enum_get(ptr, "forward_axis");
+ int up = RNA_enum_get(ptr, "up_axis");
+ if ((forward % 3) == (up % 3)) {
+ RNA_enum_set(ptr, "forward_axis", (forward + 1) % 6);
}
- return changed;
}
void WM_OT_obj_export(struct wmOperatorType *ot)
@@ -256,7 +269,7 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
FILE_SAVE,
WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
- FILE_SORT_ALPHA);
+ FILE_SORT_DEFAULT);
/* Animation options. */
RNA_def_boolean(ot->srna,
@@ -283,9 +296,11 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
INT_MIN,
INT_MAX);
/* Object transform options. */
- RNA_def_enum(
+ prop = RNA_def_enum(
ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", "");
- RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
+ RNA_def_property_update_runtime(prop, (void *)forward_axis_update);
+ prop = RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
+ RNA_def_property_update_runtime(prop, (void *)up_axis_update);
RNA_def_float(ot->srna,
"scaling_factor",
1.0f,
@@ -324,6 +339,12 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
"Export Materials",
"Export MTL library. There must be a Principled-BSDF node for image textures to "
"be exported to the MTL file");
+ RNA_def_boolean(ot->srna,
+ "export_pbr_extensions",
+ false,
+ "Export Materials with PBR Extensions",
+ "Export MTL library using PBR extensions (roughness, metallic, sheen, "
+ "clearcoat, anisotropy, transmission)");
RNA_def_enum(ot->srna,
"path_mode",
io_obj_path_mode,
@@ -382,11 +403,6 @@ static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
static int wm_obj_import_exec(bContext *C, wmOperator *op)
{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- BKE_report(op->reports, RPT_ERROR, "No filename given");
- return OPERATOR_CANCELLED;
- }
-
struct OBJImportParams import_params;
RNA_string_get(op->ptr, "filepath", import_params.filepath);
import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size");
@@ -394,8 +410,36 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op)
import_params.up_axis = RNA_enum_get(op->ptr, "up_axis");
import_params.import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups");
import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes");
-
- OBJ_import(C, &import_params);
+ import_params.relative_paths = ((U.flag & USER_RELPATHS) != 0);
+ import_params.clear_selection = true;
+
+ int files_len = RNA_collection_length(op->ptr, "files");
+ if (files_len) {
+ /* Importing multiple files: loop over them and import one by one. */
+ PointerRNA fileptr;
+ PropertyRNA *prop;
+ char dir_only[FILE_MAX], file_only[FILE_MAX];
+
+ RNA_string_get(op->ptr, "directory", dir_only);
+ prop = RNA_struct_find_property(op->ptr, "files");
+ for (int i = 0; i < files_len; i++) {
+ RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr);
+ RNA_string_get(&fileptr, "name", file_only);
+ BLI_join_dirfile(
+ import_params.filepath, sizeof(import_params.filepath), dir_only, file_only);
+ import_params.clear_selection = (i == 0);
+ OBJ_import(C, &import_params);
+ }
+ }
+ else if (RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
+ /* Importing one file. */
+ RNA_string_get(op->ptr, "filepath", import_params.filepath);
+ OBJ_import(C, &import_params);
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "No filename given");
+ return OPERATOR_CANCELLED;
+ }
Scene *scene = CTX_data_scene(C);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
@@ -417,8 +461,11 @@ static void ui_obj_import_settings(uiLayout *layout, PointerRNA *imfptr)
uiLayout *sub = uiLayoutColumn(col, false);
uiItemR(sub, imfptr, "clamp_size", 0, NULL, ICON_NONE);
sub = uiLayoutColumn(col, false);
- uiItemR(sub, imfptr, "forward_axis", 0, IFACE_("Axis Forward"), ICON_NONE);
- uiItemR(sub, imfptr, "up_axis", 0, IFACE_("Up"), ICON_NONE);
+
+ uiLayout *row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "forward_axis", UI_ITEM_R_EXPAND, IFACE_("Forward Axis"), ICON_NONE);
+ row = uiLayoutRow(box, false);
+ uiItemR(row, imfptr, "up_axis", UI_ITEM_R_EXPAND, IFACE_("Up Axis"), ICON_NONE);
box = uiLayoutBox(layout);
uiItemL(box, IFACE_("Options"), ICON_EXPORT);
@@ -453,9 +500,10 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
FILE_TYPE_FOLDER,
FILE_BLENDER,
FILE_OPENFILE,
- WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS,
+ WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS |
+ WM_FILESEL_DIRECTORY | WM_FILESEL_FILES,
FILE_DEFAULTDISPLAY,
- FILE_SORT_ALPHA);
+ FILE_SORT_DEFAULT);
RNA_def_float(
ot->srna,
"clamp_size",
@@ -466,9 +514,11 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
"Resize the objects to keep bounding box under this value. Value 0 disables clamping",
0.0f,
1000.0f);
- RNA_def_enum(
+ prop = RNA_def_enum(
ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", "");
- RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
+ RNA_def_property_update_runtime(prop, (void *)forward_axis_update);
+ prop = RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
+ RNA_def_property_update_runtime(prop, (void *)up_axis_update);
RNA_def_boolean(ot->srna,
"import_vertex_groups",
false,
diff --git a/source/blender/editors/io/io_stl_ops.c b/source/blender/editors/io/io_stl_ops.c
index 7db32cd6f18..c98e5beaf3b 100644
--- a/source/blender/editors/io/io_stl_ops.c
+++ b/source/blender/editors/io/io_stl_ops.c
@@ -53,7 +53,7 @@ static int wm_stl_import_execute(bContext *C, wmOperator *op)
STL_import(C, &params);
}
}
- else if (RNA_struct_property_is_set(op->ptr, "filepath")) {
+ else if (RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
RNA_string_get(op->ptr, "filepath", params.filepath);
STL_import(C, &params);
}
@@ -104,7 +104,7 @@ void WM_OT_stl_import(struct wmOperatorType *ot)
WM_FILESEL_FILEPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY |
WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
- FILE_SORT_ALPHA);
+ FILE_SORT_DEFAULT);
RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f);
RNA_def_boolean(ot->srna,
diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c
index a59cdf60243..eb80cabcd7f 100644
--- a/source/blender/editors/io/io_usd.c
+++ b/source/blender/editors/io/io_usd.c
@@ -21,6 +21,7 @@
# include "BLT_translation.h"
+# include "ED_fileselect.h"
# include "ED_object.h"
# include "MEM_guardedalloc.h"
@@ -84,21 +85,7 @@ static int wm_usd_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
options->as_background_job = true;
op->customdata = options;
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- Main *bmain = CTX_data_main(C);
- char filepath[FILE_MAX];
- const char *main_blendfile_path = BKE_main_blendfile_path(bmain);
-
- if (main_blendfile_path[0] == '\0') {
- BLI_strncpy(filepath, "untitled", sizeof(filepath));
- }
- else {
- BLI_strncpy(filepath, main_blendfile_path, sizeof(filepath));
- }
-
- BLI_path_extension_replace(filepath, sizeof(filepath), ".usdc");
- RNA_string_set(op->ptr, "filepath", filepath);
- }
+ ED_fileselect_ensure_default_filepath(C, op, ".usdc");
WM_event_add_fileselect(C, op);
@@ -107,7 +94,7 @@ static int wm_usd_export_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
static int wm_usd_export_exec(bContext *C, wmOperator *op)
{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -204,6 +191,19 @@ static void wm_usd_export_draw(bContext *UNUSED(C), wmOperator *op)
uiItemR(box, ptr, "use_instancing", 0, NULL, ICON_NONE);
}
+static void free_operator_customdata(wmOperator *op)
+{
+ if (op->customdata) {
+ MEM_freeN(op->customdata);
+ op->customdata = NULL;
+ }
+}
+
+static void wm_usd_export_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ free_operator_customdata(op);
+}
+
static bool wm_usd_export_check(bContext *UNUSED(C), wmOperator *op)
{
char filepath[FILE_MAX];
@@ -228,6 +228,7 @@ void WM_OT_usd_export(struct wmOperatorType *ot)
ot->exec = wm_usd_export_exec;
ot->poll = WM_operator_winactive;
ot->ui = wm_usd_export_draw;
+ ot->cancel = wm_usd_export_cancel;
ot->check = wm_usd_export_check;
ot->flag = OPTYPE_REGISTER | OPTYPE_PRESET; /* No UNDO possible. */
@@ -331,7 +332,7 @@ static int wm_usd_import_invoke(bContext *C, wmOperator *op, const wmEvent *even
static int wm_usd_import_exec(bContext *C, wmOperator *op)
{
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
@@ -373,7 +374,7 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
const bool create_collection = RNA_boolean_get(op->ptr, "create_collection");
- char *prim_path_mask = malloc(1024);
+ char prim_path_mask[1024];
RNA_string_get(op->ptr, "prim_path_mask", prim_path_mask);
const bool import_guide = RNA_boolean_get(op->ptr, "import_guide");
@@ -415,7 +416,6 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
.import_materials = import_materials,
.import_meshes = import_meshes,
.import_volumes = import_volumes,
- .prim_path_mask = prim_path_mask,
.import_subdiv = import_subdiv,
.import_instance_proxies = import_instance_proxies,
.create_collection = create_collection,
@@ -429,11 +429,18 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
.light_intensity_scale = light_intensity_scale,
.mtl_name_collision_mode = mtl_name_collision_mode};
+ STRNCPY(params.prim_path_mask, prim_path_mask);
+
const bool ok = USD_import(C, filename, &params, as_background_job);
return as_background_job || ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
+static void wm_usd_import_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ free_operator_customdata(op);
+}
+
static void wm_usd_import_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
@@ -489,6 +496,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot)
ot->invoke = wm_usd_import_invoke;
ot->exec = wm_usd_import_exec;
+ ot->cancel = wm_usd_import_cancel;
ot->poll = WM_operator_winactive;
ot->ui = wm_usd_import_draw;
@@ -500,7 +508,7 @@ void WM_OT_usd_import(struct wmOperatorType *ot)
FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS,
FILE_DEFAULTDISPLAY,
- FILE_SORT_ALPHA);
+ FILE_SORT_DEFAULT);
RNA_def_float(
ot->srna,
diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c
index 54a72c7ea5d..22a9d41fcf7 100644
--- a/source/blender/editors/lattice/editlattice_select.c
+++ b/source/blender/editors/lattice/editlattice_select.c
@@ -78,7 +78,7 @@ bool ED_lattice_deselect_all_multi(struct bContext *C)
ED_view3d_viewcontext_init(C, &vc, depsgraph);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &bases_len);
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
bool changed_multi = ED_lattice_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
return changed_multi;
@@ -96,10 +96,11 @@ static int lattice_select_random_exec(bContext *C, wmOperator *op)
const float randfac = RNA_float_get(op->ptr, "ratio");
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
@@ -205,10 +206,11 @@ static int lattice_select_mirror_exec(bContext *C, wmOperator *op)
const int axis_flag = RNA_enum_get(op->ptr, "axis");
const bool extend = RNA_boolean_get(op->ptr, "extend");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -271,12 +273,13 @@ static bool lattice_test_bitmap_uvw(
static int lattice_select_more_less(bContext *C, const bool select)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
bool changed = false;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
@@ -396,12 +399,13 @@ bool ED_lattice_flags_set(Object *obedit, int flag)
static int lattice_select_all_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int action = RNA_enum_get(op->ptr, "action");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
@@ -484,13 +488,14 @@ void LATTICE_OT_select_all(wmOperatorType *ot)
static int lattice_select_ungrouped_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
const bool is_extend = RNA_boolean_get(op->ptr, "extend");
bool changed = false;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt;
@@ -592,7 +597,7 @@ static BPoint *findnearestLattvert(ViewContext *vc, bool select, Base **r_base)
uint bases_len;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc->view_layer, vc->v3d, &bases_len);
+ vc->scene, vc->view_layer, vc->v3d, &bases_len);
for (uint base_index = 0; base_index < bases_len; base_index++) {
Base *base = bases[base_index];
data.is_changed = false;
@@ -633,7 +638,7 @@ bool ED_lattice_select_pick(bContext *C, const int mval[2], const struct SelectP
/* Deselect everything. */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &objects_len);
+ vc.scene, vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
if (ED_lattice_flags_set(ob, 0)) {
@@ -680,7 +685,8 @@ bool ED_lattice_select_pick(bContext *C, const int mval[2], const struct SelectP
lt->actbp = LT_ACTBP_NONE;
}
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
diff --git a/source/blender/editors/lattice/editlattice_tools.c b/source/blender/editors/lattice/editlattice_tools.c
index bb68b244d35..cee39ff7d70 100644
--- a/source/blender/editors/lattice/editlattice_tools.c
+++ b/source/blender/editors/lattice/editlattice_tools.c
@@ -49,6 +49,7 @@ static bool make_regular_poll(bContext *C)
static int make_regular_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
const bool is_editmode = CTX_data_edit_object(C) != NULL;
@@ -56,7 +57,7 @@ static int make_regular_exec(bContext *C, wmOperator *UNUSED(op))
if (is_editmode) {
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
Lattice *lt = ob->data;
@@ -195,13 +196,14 @@ static void lattice_swap_point_pairs(
static int lattice_flip_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
bool changed = false;
const eLattice_FlipAxes axis = RNA_enum_get(op->ptr, "axis");
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Lattice *lt;
diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c
index 7615a57c8fe..b77786b2421 100644
--- a/source/blender/editors/lattice/editlattice_undo.c
+++ b/source/blender/editors/lattice/editlattice_undo.c
@@ -46,7 +46,7 @@ static CLG_LogRef LOG = {"ed.undo.lattice"};
/** \name Undo Conversion
* \{ */
-/* TODO(Campbell): this could contain an entire 'Lattice' struct. */
+/* TODO(@campbellbarton): this could contain an entire 'Lattice' struct. */
typedef struct UndoLattice {
BPoint *def;
int pntsu, pntsv, pntsw, actbp;
@@ -131,8 +131,10 @@ static int validate_undoLatt(void *data, void *edata)
static Object *editlatt_object_from_context(bContext *C)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit && obedit->type == OB_LATTICE) {
Lattice *lt = obedit->data;
if (lt->editlatt != NULL) {
@@ -173,9 +175,10 @@ static bool lattice_undosys_step_encode(struct bContext *C, Main *bmain, UndoSte
/* Important not to use the 3D view when getting objects because all objects
* outside of this list will be moved out of edit-mode when reading back undo steps. */
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
- Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len);
+ Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
us->elems_len = objects_len;
diff --git a/source/blender/editors/mask/CMakeLists.txt b/source/blender/editors/mask/CMakeLists.txt
index fdb0d13f364..593eeb6c69d 100644
--- a/source/blender/editors/mask/CMakeLists.txt
+++ b/source/blender/editors/mask/CMakeLists.txt
@@ -10,7 +10,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
)
diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c
index 4c01154ba49..3b16497f09f 100644
--- a/source/blender/editors/mask/mask_draw.c
+++ b/source/blender/editors/mask/mask_draw.c
@@ -110,7 +110,7 @@ static void draw_single_handle(const MaskLayer *mask_layer,
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const uchar rgb_gray[4] = {0x60, 0x60, 0x60, 0xff};
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ubv(rgb_gray);
/* this could be split into its own loop */
@@ -408,7 +408,7 @@ static void mask_draw_curve_type(const bContext *C,
/* TODO(merwin): use fancy line shader here
* probably better with geometry shader (after core profile switch)
*/
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_width(3.0f);
@@ -427,7 +427,7 @@ static void mask_draw_curve_type(const bContext *C,
case MASK_DT_BLACK:
case MASK_DT_WHITE:
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_width(1.0f);
if (draw_type == MASK_DT_BLACK) {
@@ -465,7 +465,7 @@ static void mask_draw_curve_type(const bContext *C,
mask_color_active_tint(rgb_tmp, rgb_black, is_active);
rgba_uchar_to_float(colors[1], rgb_tmp);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -792,7 +792,7 @@ void ED_mask_draw_frames(
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ub(255, 175, 0, 255);
immBegin(GPU_PRIM_LINES, 2 * num_lines);
diff --git a/source/blender/editors/mask/mask_query.c b/source/blender/editors/mask/mask_query.c
index 02e1524e23e..bb865e925d7 100644
--- a/source/blender/editors/mask/mask_query.c
+++ b/source/blender/editors/mask/mask_query.c
@@ -682,8 +682,7 @@ void ED_mask_get_size(ScrArea *area, int *width, int *height)
}
case SPACE_SEQ: {
// Scene *scene = CTX_data_scene(C);
- // *width = (scene->r.size * scene->r.xsch) / 100;
- // *height = (scene->r.size * scene->r.ysch) / 100;
+ // BKE_render_resolution(&scene->r, false, width, height);
break;
}
case SPACE_IMAGE: {
diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c
index 48944c081a8..77595aa0694 100644
--- a/source/blender/editors/mask/mask_shapekey.c
+++ b/source/blender/editors/mask/mask_shapekey.c
@@ -136,9 +136,9 @@ static int mask_shape_key_feather_reset_exec(bContext *C, wmOperator *UNUSED(op)
MaskLayerShape *mask_layer_shape_reset;
MaskLayerShape *mask_layer_shape;
- /* get the shapekey of the current state */
+ /* Get the shape-key of the current state. */
mask_layer_shape_reset = BKE_mask_layer_shape_alloc(mask_layer, frame);
- /* initialize from mask - as if inseting a keyframe */
+ /* Initialize from mask - as if inserting a keyframe. */
BKE_mask_layer_shape_from_mask(mask_layer, mask_layer_shape_reset);
for (mask_layer_shape = mask_layer->splines_shapes.first; mask_layer_shape;
diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt
index 28ac913a3e3..218564eaf30 100644
--- a/source/blender/editors/mesh/CMakeLists.txt
+++ b/source/blender/editors/mesh/CMakeLists.txt
@@ -17,7 +17,6 @@ set(INC
../../render
../../windowmanager
../../../../intern/clog
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc
index 69fe69fe117..f729db29b8c 100644
--- a/source/blender/editors/mesh/editface.cc
+++ b/source/blender/editors/mesh/editface.cc
@@ -17,6 +17,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "BKE_attribute.hh"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
@@ -36,14 +37,16 @@
/* own include */
-void paintface_flush_flags(bContext *C, Object *ob, short flag)
+void paintface_flush_flags(bContext *C,
+ Object *ob,
+ const bool flush_selection,
+ const bool flush_hidden)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
- MPoly *polys, *mp_orig;
const int *index_array = nullptr;
- int totpoly;
- BLI_assert((flag & ~(SELECT | ME_HIDE)) == 0);
+ BLI_assert(flush_selection || flush_hidden);
if (me == nullptr) {
return;
@@ -53,7 +56,7 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag)
/* we could call this directly in all areas that change selection,
* since this could become slow for realtime updates (circle-select for eg) */
- if (flag & SELECT) {
+ if (flush_selection) {
BKE_mesh_flush_select_from_polys(me);
}
@@ -64,40 +67,57 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag)
return;
}
+ bke::AttributeAccessor attributes_me = me->attributes();
Mesh *me_orig = (Mesh *)ob_eval->runtime.data_orig;
+ bke::MutableAttributeAccessor attributes_orig = me_orig->attributes_for_write();
Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval;
+ bke::MutableAttributeAccessor attributes_eval = me_eval->attributes_for_write();
bool updated = false;
+ const Span<MPoly> me_polys = me->polys();
if (me_orig != nullptr && me_eval != nullptr && me_orig->totpoly == me->totpoly) {
/* Update the COW copy of the mesh. */
+ MutableSpan<MPoly> orig_polys = me_orig->polys_for_write();
for (int i = 0; i < me->totpoly; i++) {
- me_orig->mpoly[i].flag = me->mpoly[i].flag;
+ orig_polys[i].flag = me_polys[i].flag;
}
-
- /* If the mesh has only deform modifiers, the evaluated mesh shares arrays. */
- if (me_eval->mpoly == me_orig->mpoly) {
- updated = true;
+ if (flush_hidden) {
+ const VArray<bool> hide_poly_me = attributes_me.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ bke::SpanAttributeWriter<bool> hide_poly_orig =
+ attributes_orig.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE);
+ hide_poly_me.materialize(hide_poly_orig.span);
+ hide_poly_orig.finish();
}
- /* Mesh polys => Final derived polys */
- else if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) {
- polys = me_eval->mpoly;
- totpoly = me_eval->totpoly;
+ /* Mesh polys => Final derived polys */
+ if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) {
+ MutableSpan<MPoly> eval_polys = me_orig->polys_for_write();
/* loop over final derived polys */
- for (int i = 0; i < totpoly; i++) {
+ for (const int i : eval_polys.index_range()) {
if (index_array[i] != ORIGINDEX_NONE) {
/* Copy flags onto the final derived poly from the original mesh poly */
- mp_orig = me->mpoly + index_array[i];
- polys[i].flag = mp_orig->flag;
+ eval_polys[i].flag = me_polys[index_array[i]].flag;
}
}
+ const VArray<bool> hide_poly_orig = attributes_orig.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ bke::SpanAttributeWriter<bool> hide_poly_eval =
+ attributes_eval.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE);
+ for (const int i : IndexRange(me_eval->totpoly)) {
+ const int orig_poly_index = index_array[i];
+ if (orig_poly_index != ORIGINDEX_NONE) {
+ hide_poly_eval.span[i] = hide_poly_orig[orig_poly_index];
+ }
+ }
+ hide_poly_eval.finish();
updated = true;
}
}
if (updated) {
- if (flag & ME_HIDE) {
+ if (flush_hidden) {
BKE_mesh_batch_cache_dirty_tag(me_eval, BKE_MESH_BATCH_DIRTY_ALL);
}
else {
@@ -115,74 +135,99 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag)
void paintface_hide(bContext *C, Object *ob, const bool unselected)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr || me->totpoly == 0) {
return;
}
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ bke::MutableAttributeAccessor attributes = me->attributes_for_write();
+ bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE);
+
for (int i = 0; i < me->totpoly; i++) {
- MPoly *mpoly = &me->mpoly[i];
- if ((mpoly->flag & ME_HIDE) == 0) {
+ MPoly *mpoly = &polys[i];
+ if (!hide_poly.span[i]) {
if (((mpoly->flag & ME_FACE_SEL) == 0) == unselected) {
- mpoly->flag |= ME_HIDE;
+ hide_poly.span[i] = true;
}
}
- if (mpoly->flag & ME_HIDE) {
+ if (hide_poly.span[i]) {
mpoly->flag &= ~ME_FACE_SEL;
}
}
+ hide_poly.finish();
+
BKE_mesh_flush_hidden_from_polys(me);
- paintface_flush_flags(C, ob, SELECT | ME_HIDE);
+ paintface_flush_flags(C, ob, true, true);
}
void paintface_reveal(bContext *C, Object *ob, const bool select)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr || me->totpoly == 0) {
return;
}
- for (int i = 0; i < me->totpoly; i++) {
- MPoly *mpoly = &me->mpoly[i];
- if (mpoly->flag & ME_HIDE) {
- SET_FLAG_FROM_TEST(mpoly->flag, select, ME_FACE_SEL);
- mpoly->flag &= ~ME_HIDE;
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ bke::MutableAttributeAccessor attributes = me->attributes_for_write();
+
+ if (select) {
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ for (int i = 0; i < me->totpoly; i++) {
+ MPoly *mpoly = &polys[i];
+ if (hide_poly[i]) {
+ mpoly->flag |= ME_FACE_SEL;
+ }
}
}
+ attributes.remove(".hide_poly");
+
BKE_mesh_flush_hidden_from_polys(me);
- paintface_flush_flags(C, ob, SELECT | ME_HIDE);
+ paintface_flush_flags(C, ob, true, true);
}
/* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */
static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select)
{
+ using namespace blender;
bool do_it = true;
bool mark = false;
BLI_bitmap *edge_tag = BLI_BITMAP_NEW(me->totedge, __func__);
BLI_bitmap *poly_tag = BLI_BITMAP_NEW(me->totpoly, __func__);
+ const Span<MEdge> edges = me->edges();
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ const Span<MLoop> loops = me->loops();
+ bke::AttributeAccessor attributes = me->attributes();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
if (index != (uint)-1) {
/* only put face under cursor in array */
- MPoly *mp = &me->mpoly[index];
- BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
+ const MPoly *mp = &polys[index];
+ BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, &loops[mp->loopstart]);
BLI_BITMAP_ENABLE(poly_tag, index);
}
else {
/* fill array by selection */
for (int i = 0; i < me->totpoly; i++) {
- MPoly *mp = &me->mpoly[i];
- if (mp->flag & ME_HIDE) {
+ MPoly *mp = &polys[i];
+ if (hide_poly[i]) {
/* pass */
}
else if (mp->flag & ME_FACE_SEL) {
- BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
+ BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, &loops[mp->loopstart]);
BLI_BITMAP_ENABLE(poly_tag, i);
}
}
@@ -193,17 +238,17 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
/* expand selection */
for (int i = 0; i < me->totpoly; i++) {
- MPoly *mp = &me->mpoly[i];
- if (mp->flag & ME_HIDE) {
+ MPoly *mp = &polys[i];
+ if (hide_poly[i]) {
continue;
}
if (!BLI_BITMAP_TEST(poly_tag, i)) {
mark = false;
- MLoop *ml = me->mloop + mp->loopstart;
+ const MLoop *ml = &loops[mp->loopstart];
for (int b = 0; b < mp->totloop; b++, ml++) {
- if ((me->medge[ml->e].flag & ME_SEAM) == 0) {
+ if ((edges[ml->e].flag & ME_SEAM) == 0) {
if (BLI_BITMAP_TEST(edge_tag, ml->e)) {
mark = true;
break;
@@ -213,7 +258,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
if (mark) {
BLI_BITMAP_ENABLE(poly_tag, i);
- BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart);
+ BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, &loops[mp->loopstart]);
do_it = true;
}
}
@@ -223,7 +268,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo
MEM_freeN(edge_tag);
for (int i = 0; i < me->totpoly; i++) {
- MPoly *mp = &me->mpoly[i];
+ MPoly *mp = &polys[index];
if (BLI_BITMAP_TEST(poly_tag, i)) {
SET_FLAG_FROM_TEST(mp->flag, select, ME_FACE_SEL);
}
@@ -249,22 +294,28 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b
select_linked_tfaces_with_seams(me, index, select);
- paintface_flush_flags(C, ob, SELECT);
+ paintface_flush_flags(C, ob, true, false);
}
bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr) {
return false;
}
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ bke::AttributeAccessor attributes = me->attributes();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
for (int i = 0; i < me->totpoly; i++) {
- MPoly *mpoly = &me->mpoly[i];
- if ((mpoly->flag & ME_HIDE) == 0 && mpoly->flag & ME_FACE_SEL) {
+ MPoly *mpoly = &polys[i];
+ if (!hide_poly[i] && mpoly->flag & ME_FACE_SEL) {
action = SEL_DESELECT;
break;
}
@@ -274,8 +325,8 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl
bool changed = false;
for (int i = 0; i < me->totpoly; i++) {
- MPoly *mpoly = &me->mpoly[i];
- if ((mpoly->flag & ME_HIDE) == 0) {
+ MPoly *mpoly = &polys[i];
+ if (!hide_poly[i]) {
switch (action) {
case SEL_SELECT:
if ((mpoly->flag & ME_FACE_SEL) == 0) {
@@ -299,7 +350,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl
if (changed) {
if (flush_flags) {
- paintface_flush_flags(C, ob, SELECT);
+ paintface_flush_flags(C, ob, true, false);
}
}
return changed;
@@ -307,26 +358,33 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl
bool paintface_minmax(Object *ob, float r_min[3], float r_max[3])
{
+ using namespace blender;
bool ok = false;
float vec[3], bmat[3][3];
const Mesh *me = BKE_mesh_from_object(ob);
- if (!me || !me->mloopuv) {
+ if (!me || !CustomData_has_layer(&me->ldata, CD_MLOOPUV)) {
return ok;
}
- const MVert *mvert = me->mvert;
copy_m3_m4(bmat, ob->obmat);
+ const Span<MVert> verts = me->verts();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
+ bke::AttributeAccessor attributes = me->attributes();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
for (int i = 0; i < me->totpoly; i++) {
- MPoly *mp = &me->mpoly[i];
- if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) {
+ const MPoly *mp = &polys[i];
+ if (hide_poly[i] || !(mp->flag & ME_FACE_SEL)) {
continue;
}
- const MLoop *ml = me->mloop + mp->loopstart;
+ const MLoop *ml = &loops[mp->loopstart];
for (int b = 0; b < mp->totloop; b++, ml++) {
- mul_v3_m3v3(vec, bmat, mvert[ml->v].co);
+ mul_v3_m3v3(vec, bmat, verts[ml->v].co);
add_v3_v3v3(vec, vec, ob->obmat[3]);
minmax_v3v3_v3(r_min, r_max, vec);
}
@@ -342,6 +400,7 @@ bool paintface_mouse_select(bContext *C,
const SelectPick_Params *params,
Object *ob)
{
+ using namespace blender;
MPoly *mpoly_sel = nullptr;
uint index;
bool changed = false;
@@ -350,10 +409,15 @@ bool paintface_mouse_select(bContext *C,
/* Get the face under the cursor */
Mesh *me = BKE_mesh_from_object(ob);
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ bke::AttributeAccessor attributes = me->attributes();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
if (index < me->totpoly) {
- mpoly_sel = me->mpoly + index;
- if ((mpoly_sel->flag & ME_HIDE) == 0) {
+ mpoly_sel = polys.data() + index;
+ if (!hide_poly[index]) {
found = true;
}
}
@@ -402,7 +466,7 @@ bool paintface_mouse_select(bContext *C,
/* image window redraw */
- paintface_flush_flags(C, ob, SELECT);
+ paintface_flush_flags(C, ob, true, false);
ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */
changed = true;
}
@@ -411,12 +475,10 @@ bool paintface_mouse_select(bContext *C,
void paintvert_flush_flags(Object *ob)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
Mesh *me_eval = BKE_object_get_evaluated_mesh(ob);
- MVert *mvert_eval, *mv;
const int *index_array = nullptr;
- int totvert;
- int i;
if (me == nullptr) {
return;
@@ -432,23 +494,21 @@ void paintvert_flush_flags(Object *ob)
index_array = (const int *)CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX);
- mvert_eval = me_eval->mvert;
- totvert = me_eval->totvert;
-
- mv = mvert_eval;
+ const Span<MVert> verts = me->verts_for_write();
+ MutableSpan<MVert> verts_eval = me_eval->verts_for_write();
if (index_array) {
int orig_index;
- for (i = 0; i < totvert; i++, mv++) {
+ for (const int i : verts_eval.index_range()) {
orig_index = index_array[i];
if (orig_index != ORIGINDEX_NONE) {
- mv->flag = me->mvert[index_array[i]].flag;
+ verts_eval[i].flag = verts[index_array[i]].flag;
}
}
}
else {
- for (i = 0; i < totvert; i++, mv++) {
- mv->flag = me->mvert[i].flag;
+ for (const int i : verts_eval.index_range()) {
+ verts_eval[i].flag = verts[i].flag;
}
}
@@ -463,17 +523,23 @@ void paintvert_tag_select_update(bContext *C, Object *ob)
bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
if (me == nullptr) {
return false;
}
+ MutableSpan<MVert> verts = me->verts_for_write();
+ bke::AttributeAccessor attributes = me->attributes();
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
for (int i = 0; i < me->totvert; i++) {
- MVert *mvert = &me->mvert[i];
- if ((mvert->flag & ME_HIDE) == 0 && mvert->flag & SELECT) {
+ MVert *mvert = &verts[i];
+ if (!hide_vert[i] && mvert->flag & SELECT) {
action = SEL_DESELECT;
break;
}
@@ -482,8 +548,8 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
bool changed = false;
for (int i = 0; i < me->totvert; i++) {
- MVert *mvert = &me->mvert[i];
- if ((mvert->flag & ME_HIDE) == 0) {
+ MVert *mvert = &verts[i];
+ if (!hide_vert[i]) {
switch (action) {
case SEL_SELECT:
if ((mvert->flag & SELECT) == 0) {
@@ -526,9 +592,13 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
{
+ using namespace blender;
Mesh *me = BKE_mesh_from_object(ob);
-
- if (me == nullptr || me->dvert == nullptr) {
+ if (me == nullptr) {
+ return;
+ }
+ const Span<MDeformVert> dverts = me->deform_verts();
+ if (dverts.is_empty()) {
return;
}
@@ -536,10 +606,15 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
}
+ MutableSpan<MVert> verts = me->verts_for_write();
+ bke::AttributeAccessor attributes = me->attributes();
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+
for (int i = 0; i < me->totvert; i++) {
- MVert *mv = &me->mvert[i];
- MDeformVert *dv = &me->dvert[i];
- if ((mv->flag & ME_HIDE) == 0) {
+ MVert *mv = &verts[i];
+ const MDeformVert *dv = &dverts[i];
+ if (!hide_vert[i]) {
if (dv->dw == nullptr) {
/* if null weight then not grouped */
mv->flag |= SELECT;
@@ -551,3 +626,65 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags)
paintvert_flush_flags(ob);
}
}
+
+void paintvert_hide(bContext *C, Object *ob, const bool unselected)
+{
+ using namespace blender;
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == nullptr || me->totvert == 0) {
+ return;
+ }
+
+ MutableSpan<MVert> verts = me->verts_for_write();
+ bke::MutableAttributeAccessor attributes = me->attributes_for_write();
+ bke::SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_span<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT);
+
+ for (const int i : verts.index_range()) {
+ MVert &vert = verts[i];
+ if (!hide_vert.span[i]) {
+ if (((vert.flag & SELECT) == 0) == unselected) {
+ hide_vert.span[i] = true;
+ }
+ }
+
+ if (hide_vert.span[i]) {
+ vert.flag &= ~SELECT;
+ }
+ }
+ hide_vert.finish();
+
+ BKE_mesh_flush_hidden_from_verts(me);
+
+ paintvert_flush_flags(ob);
+ paintvert_tag_select_update(C, ob);
+}
+
+void paintvert_reveal(bContext *C, Object *ob, const bool select)
+{
+ using namespace blender;
+ Mesh *me = BKE_mesh_from_object(ob);
+ if (me == nullptr || me->totvert == 0) {
+ return;
+ }
+
+ MutableSpan<MVert> verts = me->verts_for_write();
+ bke::MutableAttributeAccessor attributes = me->attributes_for_write();
+ const VArray<bool> hide_vert = attributes.lookup_or_default<bool>(
+ ".hide_vert", ATTR_DOMAIN_POINT, false);
+
+ for (const int i : verts.index_range()) {
+ MVert &vert = verts[i];
+ if (hide_vert[i]) {
+ SET_FLAG_FROM_TEST(vert.flag, select, SELECT);
+ }
+ }
+
+ /* Remove the hide attribute to reveal all vertices. */
+ attributes.remove(".hide_vert");
+
+ BKE_mesh_flush_hidden_from_verts(me);
+
+ paintvert_flush_flags(ob);
+ paintvert_tag_select_update(C, ob);
+}
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index e7891450bd6..0a6feeb3665 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -240,7 +240,7 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal)
{
uint ob_store_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &ob_store_len);
+ scene, view_layer, v3d, &ob_store_len);
opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__);
for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -649,7 +649,7 @@ wmKeyMap *bevel_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Bevel Modal Map");
- /* This function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return NULL;
}
diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c
index 7b251b77750..5c5a12b3e64 100644
--- a/source/blender/editors/mesh/editmesh_bisect.c
+++ b/source/blender/editors/mesh/editmesh_bisect.c
@@ -99,6 +99,7 @@ static void mesh_bisect_interactive_calc(bContext *C,
static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int valid_objects = 0;
@@ -111,7 +112,7 @@ static int mesh_bisect_invoke(bContext *C, wmOperator *op, const wmEvent *event)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -284,7 +285,7 @@ static int mesh_bisect_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);
+ CTX_data_scene(C), CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c
index 330008d92d1..55e9c32e41b 100644
--- a/source/blender/editors/mesh/editmesh_extrude.c
+++ b/source/blender/editors/mesh/editmesh_extrude.c
@@ -281,10 +281,11 @@ static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op)
mul_v3_fl(offset, scale_offset);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
float offset_local[3], tmat[3][3];
@@ -418,10 +419,11 @@ static bool edbm_extrude_mesh(Object *obedit, BMEditMesh *em, wmOperator *op)
/* extrude without transform */
static int edbm_extrude_region_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -477,10 +479,11 @@ void MESH_OT_extrude_region(wmOperatorType *ot)
/* extrude without transform */
static int edbm_extrude_context_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -531,10 +534,11 @@ void MESH_OT_extrude_context(wmOperatorType *ot)
static int edbm_extrude_verts_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -584,10 +588,11 @@ void MESH_OT_extrude_verts_indiv(wmOperatorType *ot)
static int edbm_extrude_edges_exec(bContext *C, wmOperator *op)
{
const bool use_normal_flip = RNA_boolean_get(op->ptr, "use_normal_flip");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -637,10 +642,11 @@ void MESH_OT_extrude_edges_indiv(wmOperatorType *ot)
static int edbm_extrude_faces_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -710,7 +716,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &objects_len);
+ vc.scene, vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ED_view3d_viewcontext_init_object(&vc, obedit);
@@ -785,8 +791,8 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w
/* 2D rotate by 90d while adding.
* (x, y) = (y, -x)
*
- * accumulate the screenspace normal in 2D,
- * with screenspace edge length weighting the result. */
+ * Accumulate the screen-space normal in 2D,
+ * with screen-space edge length weighting the result. */
if (line_point_side_v2(co1, co2, mval_f) >= 0.0f) {
nor[0] += (co1[1] - co2[1]);
nor[1] += -(co1[0] - co2[0]);
diff --git a/source/blender/editors/mesh/editmesh_extrude_screw.c b/source/blender/editors/mesh/editmesh_extrude_screw.c
index cc493cab0f9..be2d04b14a1 100644
--- a/source/blender/editors/mesh/editmesh_extrude_screw.c
+++ b/source/blender/editors/mesh/editmesh_extrude_screw.c
@@ -41,7 +41,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op)
int valence;
uint objects_empty_len = 0;
uint failed_axis_len = 0;
- uint failed_vertices_len = 0;
+ uint failed_verts_len = 0;
turns = RNA_int_get(op->ptr, "turns");
steps = RNA_int_get(op->ptr, "steps");
@@ -49,9 +49,10 @@ static int edbm_screw_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "axis", axis);
uint objects_len = 0;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -97,7 +98,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op)
}
if (v1 == NULL || v2 == NULL) {
- failed_vertices_len++;
+ failed_verts_len++;
continue;
}
@@ -151,7 +152,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op)
if (failed_axis_len == objects_len - objects_empty_len) {
BKE_report(op->reports, RPT_ERROR, "Invalid/unset axis");
}
- else if (failed_vertices_len == objects_len - objects_empty_len) {
+ else if (failed_verts_len == objects_len - objects_empty_len) {
BKE_report(op->reports, RPT_ERROR, "You have to select a string of connected vertices too");
}
diff --git a/source/blender/editors/mesh/editmesh_extrude_spin.c b/source/blender/editors/mesh/editmesh_extrude_spin.c
index ec04ece6569..9e2b7aa7f4d 100644
--- a/source/blender/editors/mesh/editmesh_extrude_spin.c
+++ b/source/blender/editors/mesh/editmesh_extrude_spin.c
@@ -36,6 +36,7 @@
static int edbm_spin_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
float cent[3], axis[3];
const float d[3] = {0.0f, 0.0f, 0.0f};
@@ -56,7 +57,7 @@ static int edbm_spin_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c
index ae21e6143f6..068e6215c26 100644
--- a/source/blender/editors/mesh/editmesh_inset.c
+++ b/source/blender/editors/mesh/editmesh_inset.c
@@ -132,7 +132,7 @@ static bool edbm_inset_init(bContext *C, wmOperator *op, const bool is_modal)
{
uint ob_store_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &ob_store_len);
+ scene, view_layer, CTX_wm_view3d(C), &ob_store_len);
opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__);
for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) {
Object *obedit = objects[ob_index];
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 166eb40a7db..83cefd1c09d 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -180,11 +180,12 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op)
default: /* ISECT_SEPARATE_NONE */
break;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
uint isect_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -350,11 +351,12 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op)
bool has_isect;
test_fn = use_swap ? bm_face_isect_pair_swap : bm_face_isect_pair;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
uint isect_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -815,10 +817,11 @@ static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op))
BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index 5680865ae67..156698be567 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -489,7 +489,7 @@ static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd)
wmOrtho2_region_pixelspace(kcd->region);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
char numstr[256];
float numstr_size[2];
@@ -629,7 +629,7 @@ static void knifetool_draw_angle(const KnifeTool_OpData *kcd,
uint pos_2d = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Angle as string. */
char numstr[256];
@@ -4102,7 +4102,7 @@ static void knifetool_init(ViewContext *vc,
kcd->region = vc->region;
kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- vc->view_layer, vc->v3d, &kcd->objects_len);
+ vc->scene, vc->view_layer, vc->v3d, &kcd->objects_len);
Object *ob;
BMEditMesh *em;
@@ -4300,7 +4300,7 @@ static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, Object *ob)
}
/**
- * A post version is needed to to delay recalculating tessellation after making cuts.
+ * A post version is needed to delay recalculating tessellation after making cuts.
* Without this, knife-project can't use the BVH tree to select geometry after a cut, see: T98349.
*/
static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object *ob)
@@ -4378,7 +4378,7 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Knife Tool Modal Map");
- /* This function is called for each spacetype, only needs to add map once. */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return NULL;
}
@@ -4893,12 +4893,13 @@ void MESH_OT_knife_tool(wmOperatorType *ot)
KNF_MEASUREMENT_NONE,
"Measurements",
"Visible distance and angle measurements");
- RNA_def_enum(ot->srna,
- "angle_snapping",
- angle_snapping_items,
- KNF_CONSTRAIN_ANGLE_MODE_NONE,
- "Angle Snapping",
- "Angle snapping mode");
+ prop = RNA_def_enum(ot->srna,
+ "angle_snapping",
+ angle_snapping_items,
+ KNF_CONSTRAIN_ANGLE_MODE_NONE,
+ "Angle Snapping",
+ "Angle snapping mode");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MESH);
prop = RNA_def_float(ot->srna,
"angle_snapping_increment",
diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c
index c32b1fa99c0..e27d19ab000 100644
--- a/source/blender/editors/mesh/editmesh_knife_project.c
+++ b/source/blender/editors/mesh/editmesh_knife_project.c
@@ -136,7 +136,7 @@ static int knifeproject_exec(bContext *C, wmOperator *op)
* since each knife-project runs as a separate operation. */
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &objects_len);
+ vc.scene, vc.view_layer, vc.v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
ED_view3d_viewcontext_init_object(&vc, obedit);
diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c
index 5a4b12c2209..591e06be80c 100644
--- a/source/blender/editors/mesh/editmesh_loopcut.c
+++ b/source/blender/editors/mesh/editmesh_loopcut.c
@@ -376,11 +376,12 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event)
.e_index = (uint)RNA_int_get(op->ptr, "edge_index"),
};
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint bases_len;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
- view_layer, CTX_wm_view3d(C), &bases_len);
+ scene, view_layer, CTX_wm_view3d(C), &bases_len);
if (is_interactive) {
for (uint base_index = 0; base_index < bases_len; base_index++) {
@@ -446,7 +447,6 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event)
#ifdef USE_LOOPSLIDE_HACK
/* for use in macro so we can restore, HACK */
{
- Scene *scene = CTX_data_scene(C);
ToolSettings *settings = scene->toolsettings;
const bool mesh_select_mode[3] = {
(settings->selectmode & SCE_SELECT_VERTEX) != 0,
diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c
index 7634ce6af9e..a4d41400bae 100644
--- a/source/blender/editors/mesh/editmesh_mask_extract.c
+++ b/source/blender/editors/mesh/editmesh_mask_extract.c
@@ -204,7 +204,7 @@ static int geometry_extract_apply(bContext *C,
local_view_bits = v3d->local_view_uuid;
}
Object *new_ob = ED_object_add_type(C, OB_MESH, NULL, ob->loc, ob->rot, false, local_view_bits);
- BKE_mesh_nomain_to_mesh(new_mesh, new_ob->data, new_ob, &CD_MASK_EVERYTHING, true);
+ BKE_mesh_nomain_to_mesh(new_mesh, new_ob->data, new_ob);
/* Remove the Face Sets as they need to be recreated when entering Sculpt Mode in the new object.
* TODO(pablodobarro): In the future we can try to preserve them from the original mesh. */
@@ -486,7 +486,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
Mesh *new_mesh = (Mesh *)BKE_id_copy(bmain, &mesh->id);
if (ob->mode == OB_MODE_SCULPT) {
- ED_sculpt_undo_geometry_begin(ob, "mask slice");
+ ED_sculpt_undo_geometry_begin(ob, op);
}
BMesh *bm;
@@ -548,7 +548,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
/* Remove the mask from the new object so it can be sculpted directly after slicing. */
CustomData_free_layers(&new_ob_mesh->vdata, CD_PAINT_MASK, new_ob_mesh->totvert);
- BKE_mesh_nomain_to_mesh(new_ob_mesh, new_ob->data, new_ob, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(new_ob_mesh, new_ob->data, new_ob);
BKE_mesh_copy_parameters_for_eval(new_ob->data, mesh);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, new_ob);
BKE_mesh_batch_cache_dirty_tag(new_ob->data, BKE_MESH_BATCH_DIRTY_ALL);
@@ -557,7 +557,7 @@ static int paint_mask_slice_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_GEOM | ND_DATA, new_ob->data);
}
- BKE_mesh_nomain_to_mesh(new_mesh, ob->data, ob, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(new_mesh, ob->data, ob);
if (ob->mode == OB_MODE_SCULPT) {
SculptSession *ss = ob->sculpt;
diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c
index f2e7150e791..ec8c484d890 100644
--- a/source/blender/editors/mesh/editmesh_path.c
+++ b/source/blender/editors/mesh/editmesh_path.c
@@ -21,6 +21,7 @@
#include "BLI_math.h"
#include "BKE_context.h"
+#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
#include "BKE_report.h"
@@ -348,7 +349,9 @@ static void edgetag_ensure_cd_flag(Mesh *me, const char edge_mode)
BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE);
break;
case EDGE_MODE_TAG_BEVEL:
- BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT);
+ if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) {
+ BM_data_layer_add(bm, &bm->edata, CD_BWEIGHT);
+ }
break;
#ifdef WITH_FREESTYLE
case EDGE_MODE_TAG_FREESTYLE:
@@ -679,7 +682,8 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
em_setup_viewcontext(C, &vc);
copy_v2_v2_int(vc.mval, event->mval);
- Base *basact = BASACT(vc.view_layer);
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ Base *basact = BKE_view_layer_active_base_get(vc.view_layer);
BMEditMesh *em = vc.em;
view3d_operator_needs_opengl(C);
@@ -687,7 +691,8 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
{
int base_index = -1;
uint bases_len = 0;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
if (EDBM_unified_findnearest(&vc, bases, bases_len, &base_index, &eve, &eed, &efa)) {
basact = bases[base_index];
ED_view3d_viewcontext_init_object(&vc, basact->object);
@@ -732,7 +737,8 @@ static int edbm_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmE
return OPERATOR_PASS_THROUGH;
}
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
@@ -814,7 +820,7 @@ static int edbm_shortest_path_select_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
diff --git a/source/blender/editors/mesh/editmesh_polybuild.c b/source/blender/editors/mesh/editmesh_polybuild.c
index 493e476ba4f..17580dbe7d1 100644
--- a/source/blender/editors/mesh/editmesh_polybuild.c
+++ b/source/blender/editors/mesh/editmesh_polybuild.c
@@ -53,11 +53,14 @@ static void edbm_selectmode_ensure(Scene *scene, BMEditMesh *em, short selectmod
}
/* Could make public, for now just keep here. */
-static void edbm_flag_disable_all_multi(ViewLayer *view_layer, View3D *v3d, const char hflag)
+static void edbm_flag_disable_all_multi(const Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char hflag)
{
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
@@ -84,8 +87,10 @@ static bool edbm_preselect_or_active(bContext *C, const View3D *v3d, Base **r_ba
ED_view3d_gizmo_mesh_preselect_get_active(C, gz, r_base, r_ele);
}
else {
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = view_layer->basact;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
Object *obedit = base->object;
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
@@ -128,7 +133,7 @@ static int edbm_polybuild_transform_at_cursor_invoke(bContext *C,
edbm_selectmode_ensure(vc.scene, vc.em, SCE_SELECT_VERTEX);
- edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
if (ele_act->head.htype == BM_VERT) {
BM_vert_select_set(bm, (BMVert *)ele_act, true);
@@ -147,7 +152,8 @@ static int edbm_polybuild_transform_at_cursor_invoke(bContext *C,
.is_destructive = true,
});
if (basact != NULL) {
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
}
@@ -234,7 +240,8 @@ static int edbm_polybuild_delete_at_cursor_invoke(bContext *C,
.is_destructive = true,
});
if (basact != NULL) {
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
}
@@ -292,7 +299,7 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, con
mul_m4_v3(vc.obedit->imat, center);
BMVert *v_new = BM_vert_create(bm, center, NULL, BM_CREATE_NOP);
- edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_new, true);
BM_select_history_store(bm, v_new);
changed = true;
@@ -309,7 +316,7 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, con
const float fac = line_point_factor_v3(center, e_act->v1->co, e_act->v2->co);
BMVert *v_new = BM_edge_split(bm, e_act, e_act->v1, NULL, CLAMPIS(fac, 0.0f, 1.0f));
copy_v3_v3(v_new->co, center);
- edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_new, true);
BM_select_history_store(bm, v_new);
}
@@ -322,7 +329,7 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, con
SWAP(BMVert *, v_tri[0], v_tri[1]);
}
BM_face_create_verts(bm, v_tri, 3, f_reference, BM_CREATE_NOP, true);
- edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_tri[2], true);
BM_select_history_store(bm, v_tri[2]);
}
@@ -372,7 +379,7 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, con
// BMFace *f_new =
BM_face_create_verts(bm, v_quad, 4, f_reference, BM_CREATE_NOP, true);
- edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_quad[2], true);
BM_select_history_store(bm, v_quad[2]);
changed = true;
@@ -402,7 +409,8 @@ static int edbm_polybuild_face_at_cursor_invoke(bContext *C, wmOperator *op, con
});
if (basact != NULL) {
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
}
@@ -475,7 +483,7 @@ static int edbm_polybuild_split_at_cursor_invoke(bContext *C,
BMVert *v_new = BM_edge_split(bm, e_act, e_act->v1, NULL, CLAMPIS(fac, 0.0f, 1.0f));
copy_v3_v3(v_new->co, center);
- edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
BM_vert_select_set(bm, v_new, true);
BM_select_history_store(bm, v_new);
changed = true;
@@ -495,7 +503,8 @@ static int edbm_polybuild_split_at_cursor_invoke(bContext *C,
WM_event_add_mousemove(vc.win);
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
@@ -578,7 +587,7 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C,
}
if (changed) {
- edbm_flag_disable_all_multi(vc.view_layer, vc.v3d, BM_ELEM_SELECT);
+ edbm_flag_disable_all_multi(vc.scene, vc.view_layer, vc.v3d, BM_ELEM_SELECT);
EDBM_update(vc.obedit->data,
&(const struct EDBMUpdate_Params){
@@ -587,7 +596,8 @@ static int edbm_polybuild_dissolve_at_cursor_invoke(bContext *C,
.is_destructive = true,
});
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c
index 6b4edea498e..0c137c94d57 100644
--- a/source/blender/editors/mesh/editmesh_rip.c
+++ b/source/blender/editors/mesh/editmesh_rip.c
@@ -987,10 +987,11 @@ static int edbm_rip_invoke__edge(bContext *C, const wmEvent *event, Object *obed
/* based on mouse cursor position, it defines how is being ripped */
static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
const bool do_fill = RNA_boolean_get(op->ptr, "use_fill");
bool no_vertex_selected = true;
diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c
index 85426acb905..dd4b247a06f 100644
--- a/source/blender/editors/mesh/editmesh_rip_edge.c
+++ b/source/blender/editors/mesh/editmesh_rip_edge.c
@@ -35,10 +35,11 @@ static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve
{
ARegion *region = CTX_wm_region(C);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index 9c8c5c45cb7..b66fe84e84e 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -358,6 +358,7 @@ BMVert *EDBM_vert_find_nearest_ex(ViewContext *vc,
BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
+ BKE_view_layer_synced_ensure(vc->scene, vc->view_layer);
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
return EDBM_vert_find_nearest_ex(vc, dist_px_manhattan_p, false, false, &base, 1, NULL);
}
@@ -612,6 +613,7 @@ BMEdge *EDBM_edge_find_nearest_ex(ViewContext *vc,
BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
+ BKE_view_layer_synced_ensure(vc->scene, vc->view_layer);
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
return EDBM_edge_find_nearest_ex(
vc, dist_px_manhattan_p, NULL, false, false, NULL, &base, 1, NULL);
@@ -831,6 +833,7 @@ BMFace *EDBM_face_find_nearest_ex(ViewContext *vc,
BMFace *EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
{
+ BKE_view_layer_synced_ensure(vc->scene, vc->view_layer);
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
return EDBM_face_find_nearest_ex(
vc, dist_px_manhattan_p, NULL, false, false, false, NULL, &base, 1, NULL);
@@ -1512,10 +1515,11 @@ static void walker_select(BMEditMesh *em, int walkercode, void *start, const boo
static int edbm_loop_multiselect_exec(bContext *C, wmOperator *op)
{
const bool is_ring = RNA_boolean_get(op->ptr, "ring");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -1681,7 +1685,8 @@ static bool mouse_mesh_loop(
em_original->selectmode = SCE_SELECT_EDGE;
uint bases_len;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
{
int base_index = -1;
@@ -1903,12 +1908,13 @@ void MESH_OT_edgering_select(wmOperatorType *ot)
static int edbm_select_all_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int action = RNA_enum_get(op->ptr, "action");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
@@ -1971,10 +1977,11 @@ void MESH_OT_select_all(wmOperatorType *ot)
static int edbm_faces_select_interior_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -2031,7 +2038,8 @@ bool EDBM_select_pick(bContext *C, const int mval[2], const struct SelectPick_Pa
vc.mval[1] = mval[1];
uint bases_len = 0;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
bool changed = false;
bool found = unified_findnearest(&vc, bases, bases_len, &base_index_active, &eve, &eed, &efa);
@@ -2214,7 +2222,8 @@ bool EDBM_select_pick(bContext *C, const int mval[2], const struct SelectPick_Pa
/* Changing active object is handy since it allows us to
* switch UV layers, vgroups for eg. */
- if (vc.view_layer->basact != basact) {
+ BKE_view_layer_synced_ensure(vc.scene, vc.view_layer);
+ if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
ED_object_base_activate(C, basact);
}
@@ -2488,7 +2497,7 @@ bool EDBM_selectmode_toggle_multi(bContext *C,
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
@@ -2584,7 +2593,7 @@ bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
@@ -2723,7 +2732,7 @@ bool EDBM_mesh_deselect_all_multi(struct bContext *C)
ED_view3d_viewcontext_init(C, &vc, depsgraph);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &bases_len);
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
bool changed_multi = EDBM_mesh_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
return changed_multi;
@@ -2758,7 +2767,7 @@ bool EDBM_selectmode_disable_multi(struct bContext *C,
ED_view3d_viewcontext_init(C, &vc, depsgraph);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, NULL, &bases_len);
+ vc.scene, vc.view_layer, NULL, &bases_len);
bool changed_multi = EDBM_selectmode_disable_multi_ex(
scene, bases, bases_len, selectmode_disable, selectmode_fallback);
MEM_freeN(bases);
@@ -3245,7 +3254,7 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3612,7 +3621,8 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE
em_setup_viewcontext(C, &vc);
uint bases_len;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
{
bool has_edges = false;
@@ -3663,7 +3673,7 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE
* which might not be available on redo. */
BM_mesh_elem_index_ensure(bm, ele->head.htype);
int object_index;
- index = EDBM_elem_to_index_any_multi(vc.view_layer, em, ele, &object_index);
+ index = EDBM_elem_to_index_any_multi(vc.scene, vc.view_layer, em, ele, &object_index);
BLI_assert(object_index >= 0);
RNA_int_set(op->ptr, "object_index", object_index);
RNA_int_set(op->ptr, "index", index);
@@ -3682,11 +3692,12 @@ static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op)
BMElem *ele;
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
/* Intentionally wrap negative values so the lookup fails. */
const uint object_index = (uint)RNA_int_get(op->ptr, "object_index");
const uint index = (uint)RNA_int_get(op->ptr, "index");
- ele = EDBM_elem_from_index_any_multi(view_layer, object_index, index, &obedit);
+ ele = EDBM_elem_from_index_any_multi(scene, view_layer, object_index, index, &obedit);
}
if (ele == NULL) {
@@ -3753,13 +3764,14 @@ void MESH_OT_select_linked_pick(wmOperatorType *ot)
static int edbm_select_face_by_sides_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
const bool extend = RNA_boolean_get(op->ptr, "extend");
const int numverts = RNA_int_get(op->ptr, "number");
const int type = RNA_enum_get(op->ptr, "type");
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3844,12 +3856,13 @@ void MESH_OT_select_face_by_sides(wmOperatorType *ot)
static int edbm_select_loose_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool extend = RNA_boolean_get(op->ptr, "extend");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3934,6 +3947,7 @@ void MESH_OT_select_loose(wmOperatorType *ot)
static int edbm_select_mirror_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const int axis_flag = RNA_enum_get(op->ptr, "axis");
const bool extend = RNA_boolean_get(op->ptr, "extend");
@@ -3944,7 +3958,7 @@ static int edbm_select_mirror_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -4008,12 +4022,13 @@ void MESH_OT_select_mirror(wmOperatorType *ot)
static int edbm_select_more_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -4058,12 +4073,13 @@ void MESH_OT_select_more(wmOperatorType *ot)
static int edbm_select_less_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -4296,6 +4312,7 @@ static bool edbm_deselect_nth(BMEditMesh *em, const struct CheckerIntervalParams
static int edbm_select_nth_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
struct CheckerIntervalParams op_params;
WM_operator_properties_checker_interval_from_op(op, &op_params);
@@ -4303,7 +4320,7 @@ static int edbm_select_nth_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -4374,10 +4391,11 @@ static int edbm_select_sharp_edges_exec(bContext *C, wmOperator *op)
*/
const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness"));
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -4450,10 +4468,11 @@ void MESH_OT_edges_select_sharp(wmOperatorType *ot)
static int edbm_select_linked_flat_faces_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness"));
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -4563,10 +4582,11 @@ static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op)
const bool use_non_contiguous = RNA_boolean_get(op->ptr, "use_non_contiguous");
const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -4667,11 +4687,12 @@ static int edbm_select_random_exec(bContext *C, wmOperator *op)
const float randfac = RNA_float_get(op->ptr, "ratio");
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -4797,11 +4818,12 @@ static bool edbm_select_ungrouped_poll(bContext *C)
static int edbm_select_ungrouped_exec(bContext *C, wmOperator *op)
{
const bool extend = RNA_boolean_get(op->ptr, "extend");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -4925,7 +4947,7 @@ static int edbm_select_axis_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit_iter = objects[ob_index];
BMEditMesh *em_iter = BKE_editmesh_from_object(obedit_iter);
@@ -5023,10 +5045,11 @@ void MESH_OT_select_axis(wmOperatorType *ot)
static int edbm_region_to_loop_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -5255,10 +5278,11 @@ static int edbm_loop_to_region_exec(bContext *C, wmOperator *op)
{
const bool select_bigger = RNA_boolean_get(op->ptr, "select_bigger");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index c931cb4948b..47c76b7709b 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -12,6 +12,8 @@
#include "BLI_listbase.h"
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_deform.h"
@@ -145,6 +147,7 @@ static void face_to_plane(const Object *ob, BMFace *face, float r_plane[4])
*/
static int similar_face_select_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const int type = RNA_enum_get(op->ptr, "type");
@@ -155,7 +158,7 @@ static int similar_face_select_exec(bContext *C, wmOperator *op)
int tot_faces_selected_all = 0;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -618,6 +621,7 @@ static bool edge_data_value_set(BMEdge *edge, const int hflag, int *r_value)
*/
static int similar_edge_select_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const int type = RNA_enum_get(op->ptr, "type");
@@ -629,7 +633,7 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op)
int tot_edges_selected_all = 0;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -968,6 +972,7 @@ static int similar_edge_select_exec(bContext *C, wmOperator *op)
static int similar_vert_select_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
/* get the type from RNA */
@@ -979,7 +984,7 @@ static int similar_vert_select_exec(bContext *C, wmOperator *op)
int tot_verts_selected_all = 0;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -1416,6 +1421,7 @@ void MESH_OT_select_similar(wmOperatorType *ot)
/* properties */
prop = ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, SIMVERT_NORMAL, "Type", "");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MESH);
RNA_def_enum_funcs(prop, select_similar_type_itemf);
RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 1febc429edc..9f3ef8af17d 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -93,10 +93,11 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op)
const int quad_corner_type = RNA_enum_get(op->ptr, "quadcorner");
const int seed = RNA_int_get(op->ptr, "seed");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -289,11 +290,11 @@ static void mesh_operator_edgering_props_get(wmOperator *op, struct EdgeRingOpSu
static int edbm_subdivide_edge_ring_exec(bContext *C, wmOperator *op)
{
-
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
struct EdgeRingOpSubdProps op_props;
mesh_operator_edgering_props_get(op, &op_props);
@@ -358,10 +359,11 @@ void MESH_OT_subdivide_edgering(wmOperatorType *ot)
static int edbm_unsubdivide_exec(bContext *C, wmOperator *op)
{
const int iterations = RNA_int_get(op->ptr, "iterations");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -444,11 +446,12 @@ static void edbm_report_delete_info(ReportList *reports,
static int edbm_delete_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
bool changed_multi = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -587,13 +590,14 @@ static bool bm_face_is_loose(BMFace *f)
static int edbm_delete_loose_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int totelem_old_sel[3];
int totelem_old[3];
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
EDBM_mesh_stats_multi(objects, objects_len, totelem_old, totelem_old_sel);
@@ -695,10 +699,11 @@ void MESH_OT_delete_loose(wmOperatorType *ot)
static int edbm_collapse_edge_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -928,15 +933,16 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op)
{
/* When this is used to dissolve we could avoid this, but checking isn't too slow. */
bool changed_multi = false;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
- if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totvertsel == 0)) {
+ if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
continue;
}
@@ -1049,7 +1055,7 @@ static int edbm_mark_seam_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -1129,11 +1135,12 @@ static int edbm_mark_sharp_exec(bContext *C, wmOperator *op)
BMIter iter;
const bool clear = RNA_boolean_get(op->ptr, "clear");
const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -1312,11 +1319,12 @@ static bool edbm_connect_vert_pair(BMEditMesh *em, struct Mesh *me, wmOperator *
static int edbm_vert_connect_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
uint failed_objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -1559,12 +1567,13 @@ static bool bm_vert_connect_select_history_edge_to_vert_path(BMesh *bm, ListBase
static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
uint failed_selection_order_len = 0;
uint failed_connect_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -1655,10 +1664,11 @@ void MESH_OT_vert_connect_path(wmOperatorType *ot)
static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -1706,11 +1716,12 @@ void MESH_OT_vert_connect_concave(wmOperatorType *ot)
static int edbm_vert_connect_nonplaner_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -1780,10 +1791,11 @@ void MESH_OT_vert_connect_nonplanar(wmOperatorType *ot)
static int edbm_face_make_planar_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
const int repeat = RNA_int_get(op->ptr, "repeat");
const float fac = RNA_float_get(op->ptr, "factor");
@@ -1947,10 +1959,11 @@ static int edbm_edge_split_exec(bContext *C, wmOperator *op)
{
const int type = RNA_enum_get(op->ptr, "type");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2012,10 +2025,11 @@ void MESH_OT_edge_split(wmOperatorType *ot)
static int edbm_duplicate_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
bool changed = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -2243,10 +2257,11 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op)
{
const bool only_clnors = RNA_boolean_get(op->ptr, "only_clnors");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -2309,10 +2324,11 @@ static int edbm_edge_rotate_selected_exec(bContext *C, wmOperator *op)
int tot_failed_all = 0;
bool no_selected_edges = true, invalid_selected_edges = true;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2435,12 +2451,13 @@ void MESH_OT_edge_rotate(wmOperatorType *ot)
static int edbm_hide_exec(bContext *C, wmOperator *op)
{
const bool unselected = RNA_boolean_get(op->ptr, "unselected");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
bool changed = false;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2516,11 +2533,12 @@ void MESH_OT_hide(wmOperatorType *ot)
static int edbm_reveal_exec(bContext *C, wmOperator *op)
{
const bool select = RNA_boolean_get(op->ptr, "select");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2564,12 +2582,13 @@ void MESH_OT_reveal(wmOperatorType *ot)
static int edbm_normals_make_consistent_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const bool inside = RNA_boolean_get(op->ptr, "inside");
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2645,10 +2664,11 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op)
repeat = 1;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Mesh *me = obedit->data;
@@ -2764,6 +2784,7 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot)
static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
{
int tot_unselected = 0;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const float lambda_factor = RNA_float_get(op->ptr, "lambda_factor");
@@ -2780,7 +2801,7 @@ static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2905,10 +2926,11 @@ static void mesh_set_smooth_faces(BMEditMesh *em, short smooth)
static int edbm_faces_shade_smooth_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2953,10 +2975,11 @@ void MESH_OT_faces_shade_smooth(wmOperatorType *ot)
static int edbm_faces_shade_flat_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -3004,10 +3027,11 @@ static int edbm_rotate_uvs_exec(bContext *C, wmOperator *op)
/* get the direction from RNA */
const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -3040,10 +3064,11 @@ static int edbm_rotate_uvs_exec(bContext *C, wmOperator *op)
static int edbm_reverse_uvs_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -3078,10 +3103,11 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op)
/* get the direction from RNA */
const bool use_ccw = RNA_boolean_get(op->ptr, "use_ccw");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -3131,10 +3157,11 @@ static int edbm_rotate_colors_exec(bContext *C, wmOperator *op)
static int edbm_reverse_colors_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3376,7 +3403,7 @@ static int edbm_merge_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
const int type = RNA_enum_get(op->ptr, "type");
const bool uvs = RNA_boolean_get(op->ptr, "uvs");
@@ -3540,10 +3567,11 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op)
int count_multi = 0;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3686,13 +3714,14 @@ static bool shape_propagate(BMEditMesh *em)
static int edbm_shape_propagate_to_all_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int tot_shapekeys = 0;
int tot_selected_verts_objects = 0;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Mesh *me = obedit->data;
@@ -3759,6 +3788,7 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op)
BMEditMesh *em_ref = me_ref->edit_mesh;
BMVert *eve;
BMIter iter;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
float co[3], *sco;
int totshape_ref = 0;
@@ -3787,7 +3817,7 @@ static int edbm_blend_from_shape_exec(bContext *C, wmOperator *op)
int tot_selected_verts_objects = 0;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Mesh *me = obedit->data;
@@ -3938,10 +3968,11 @@ static int edbm_solidify_exec(bContext *C, wmOperator *op)
{
const float thickness = RNA_float_get(op->ptr, "thickness");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -4686,7 +4717,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
uint bases_len = 0;
uint empty_selection_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &bases_len);
+ scene, view_layer, CTX_wm_view3d(C), &bases_len);
for (uint bs_index = 0; bs_index < bases_len; bs_index++) {
Base *base = bases[bs_index];
BMEditMesh *em = BKE_editmesh_from_object(base->object);
@@ -4835,10 +4866,11 @@ static int edbm_fill_exec(bContext *C, wmOperator *op)
bool has_selected_edges = false, has_faces_filled = false;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -5090,10 +5122,11 @@ static int edbm_fill_grid_exec(bContext *C, wmOperator *op)
{
const bool use_interp_simple = RNA_boolean_get(op->ptr, "use_interp_simple");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -5231,10 +5264,11 @@ static int edbm_fill_holes_exec(bContext *C, wmOperator *op)
{
const int sides = RNA_int_get(op->ptr, "sides");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -5294,10 +5328,11 @@ void MESH_OT_fill_holes(wmOperatorType *ot)
static int edbm_beautify_fill_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
const float angle_max = M_PI;
const float angle_limit = RNA_float_get(op->ptr, "angle_limit");
@@ -5392,10 +5427,11 @@ static int edbm_poke_face_exec(bContext *C, wmOperator *op)
const bool use_relative_offset = RNA_boolean_get(op->ptr, "use_relative_offset");
const int center_mode = RNA_enum_get(op->ptr, "center_mode");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -5488,11 +5524,12 @@ static int edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op)
{
const int quad_method = RNA_enum_get(op->ptr, "quad_method");
const int ngon_method = RNA_enum_get(op->ptr, "ngon_method");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -5582,11 +5619,12 @@ void MESH_OT_quads_convert_to_tris(wmOperatorType *ot)
static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
const bool do_seam = RNA_boolean_get(op->ptr, "seam");
const bool do_sharp = RNA_boolean_get(op->ptr, "sharp");
@@ -5743,10 +5781,11 @@ static int edbm_decimate_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -5960,10 +5999,11 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op)
const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -6027,10 +6067,11 @@ static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op)
const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -6092,10 +6133,11 @@ void MESH_OT_dissolve_edges(wmOperatorType *ot)
static int edbm_dissolve_faces_exec(bContext *C, wmOperator *op)
{
const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -6208,10 +6250,11 @@ static int edbm_dissolve_limited_exec(bContext *C, wmOperator *op)
const int delimit = RNA_enum_get(op->ptr, "delimit");
char dissolve_flag;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -6329,13 +6372,14 @@ void MESH_OT_dissolve_limited(wmOperatorType *ot)
static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int totelem_old[3] = {0, 0, 0};
int totelem_new[3] = {0, 0, 0};
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -6413,11 +6457,12 @@ void MESH_OT_dissolve_degenerate(wmOperatorType *ot)
static int edbm_delete_edgeloop_exec(bContext *C, wmOperator *op)
{
const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -6497,10 +6542,11 @@ void MESH_OT_delete_edgeloop(wmOperatorType *ot)
static int edbm_split_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -7092,7 +7138,7 @@ static int edbm_sort_elements_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -7396,11 +7442,12 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op)
const bool use_merge = RNA_boolean_get(op->ptr, "use_merge");
const float merge_factor = RNA_float_get(op->ptr, "merge_factor");
const int twist_offset = RNA_int_get(op->ptr, "twist_offset");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -7476,10 +7523,11 @@ static int edbm_wireframe_exec(bContext *C, wmOperator *op)
const float thickness = RNA_float_get(op->ptr, "thickness");
const float offset = RNA_float_get(op->ptr, "offset");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -7587,7 +7635,7 @@ static int edbm_offset_edgeloop_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &bases_len);
+ scene, view_layer, CTX_wm_view3d(C), &bases_len);
for (uint base_index = 0; base_index < bases_len; base_index++) {
Object *obedit = bases[base_index]->object;
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -7676,10 +7724,11 @@ static int edbm_convex_hull_exec(bContext *C, wmOperator *op)
float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold");
float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -7809,10 +7858,11 @@ void MESH_OT_convex_hull(wmOperatorType *ot)
static int mesh_symmetrize_exec(bContext *C, wmOperator *op)
{
const float thresh = RNA_float_get(op->ptr, "threshold");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -7908,10 +7958,11 @@ static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op)
int axis = axis_dir % 3;
bool axis_sign = axis != axis_dir;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -8074,11 +8125,12 @@ static int edbm_mark_freestyle_edge_exec(bContext *C, wmOperator *op)
BMIter iter;
FreestyleEdge *fed;
const bool clear = RNA_boolean_get(op->ptr, "clear");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -8154,11 +8206,12 @@ static int edbm_mark_freestyle_face_exec(bContext *C, wmOperator *op)
BMIter iter;
FreestyleFace *ffa;
const bool clear = RNA_boolean_get(op->ptr, "clear");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -8692,7 +8745,7 @@ static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *
* Free the data here, then use #point_normals_ensure to add it back on demand. */
if (ret == OPERATOR_PASS_THROUGH) {
/* Don't free on mouse-move, causes creation/freeing of the loop data in an inefficient way. */
- if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (!ISMOUSE_MOTION(event->type)) {
point_normals_free(op);
}
}
@@ -8952,10 +9005,11 @@ static void normals_split(BMesh *bm)
static int normals_split_merge(bContext *C, const bool do_merge)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -9081,10 +9135,11 @@ static EnumPropertyItem average_method_items[] = {
static int edbm_average_normals_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
const int average_type = RNA_enum_get(op->ptr, "average_type");
const float absweight = (float)RNA_int_get(op->ptr, "weight");
const float threshold = RNA_float_get(op->ptr, "threshold");
@@ -9332,7 +9387,7 @@ static int edbm_normals_tools_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
const int mode = RNA_enum_get(op->ptr, "mode");
const bool absolute = RNA_boolean_get(op->ptr, "absolute");
float *normal_vector = scene->toolsettings->normal_vector;
@@ -9547,10 +9602,11 @@ void MESH_OT_normals_tools(struct wmOperatorType *ot)
static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -9662,10 +9718,11 @@ void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot)
static int edbm_smooth_normals_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -9783,10 +9840,11 @@ void MESH_OT_smooth_normals(struct wmOperatorType *ot)
static int edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index d75c92f963f..44fab751de2 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -361,8 +361,6 @@ static void um_arraystore_compact_ex(UndoMesh *um, const UndoMesh *um_ref, bool
if (create) {
um_arraystore.users += 1;
}
-
- BKE_mesh_update_customdata_pointers(me, false);
}
/**
@@ -465,9 +463,6 @@ static void um_arraystore_expand(UndoMesh *um)
BLI_assert(me->totselect == (state_len / stride));
UNUSED_VARS_NDEBUG(stride);
}
-
- /* not essential, but prevents accidental dangling pointer access */
- BKE_mesh_update_customdata_pointers(me, false);
}
static void um_arraystore_free(UndoMesh *um)
@@ -594,6 +589,10 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo
/* Uncomment for troubleshooting. */
// BM_mesh_validate(em->bm);
+ /* Copy the ID name characters to the mesh so code that depends on accessing the ID type can work
+ * on it. Necessary to use the attribute API. */
+ strcpy(um->me.id.name, "MEundomesh_from_editmesh");
+
BM_mesh_bm_to_me(
NULL,
em->bm,
@@ -731,8 +730,10 @@ static void undomesh_free_data(UndoMesh *um)
static Object *editmesh_object_from_context(bContext *C)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit && obedit->type == OB_MESH) {
Mesh *me = obedit->data;
if (me->edit_mesh != NULL) {
@@ -772,10 +773,11 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und
/* Important not to use the 3D view when getting objects because all objects
* outside of this list will be moved out of edit-mode when reading back undo steps. */
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
ToolSettings *ts = CTX_data_tool_settings(C);
uint objects_len = 0;
- Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len);
+ Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
us->elems_len = objects_len;
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 83968955583..5c8ff930eb8 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -593,45 +593,393 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v)
return vmap->vert[v];
}
+struct UvElement **BM_uv_element_map_ensure_head_table(struct UvElementMap *element_map)
+{
+ if (element_map->head_table) {
+ return element_map->head_table;
+ }
+
+ /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */
+ element_map->head_table = MEM_mallocN(sizeof(*element_map->head_table) * element_map->total_uvs,
+ "uv_element_map_head_table");
+ UvElement **head_table = element_map->head_table;
+ for (int i = 0; i < element_map->total_uvs; i++) {
+ UvElement *head = element_map->storage + i;
+ if (head->separate) {
+ UvElement *element = head;
+ while (element) {
+ head_table[element - element_map->storage] = head;
+ element = element->next;
+ if (element && element->separate) {
+ break;
+ }
+ }
+ }
+ }
+ return element_map->head_table;
+}
+
+#define INVALID_ISLAND ((unsigned int)-1)
+
+static void bm_uv_assign_island(UvElementMap *element_map,
+ UvElement *element,
+ int nisland,
+ uint *map,
+ UvElement *islandbuf,
+ int islandbufsize)
+{
+ element->island = nisland;
+ map[element - element_map->storage] = islandbufsize;
+
+ /* Copy *element to islandbuf[islandbufsize]. */
+ islandbuf[islandbufsize].l = element->l;
+ islandbuf[islandbufsize].separate = element->separate;
+ islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index;
+ islandbuf[islandbufsize].island = element->island;
+ islandbuf[islandbufsize].flag = element->flag;
+}
+
+static int bm_uv_edge_select_build_islands(UvElementMap *element_map,
+ const Scene *scene,
+ UvElement *islandbuf,
+ uint *map,
+ bool uv_selected,
+ const int cd_loop_uv_offset)
+{
+ BM_uv_element_map_ensure_head_table(element_map);
+
+ int total_uvs = element_map->total_uvs;
+
+ /* Depth first search the graph, building islands as we go. */
+ int nislands = 0;
+ int islandbufsize = 0;
+ int stack_upper_bound = total_uvs;
+ UvElement **stack_uv = MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound,
+ "uv_island_element_stack");
+ int stacksize_uv = 0;
+ for (int i = 0; i < total_uvs; i++) {
+ UvElement *element = element_map->storage + i;
+ if (element->island != INVALID_ISLAND) {
+ /* Unique UV (element and all it's children) are already part of an island. */
+ continue;
+ }
+
+ /* Create a new island, i.e. nislands++. */
+
+ BLI_assert(element->separate); /* Ensure we're the head of this unique UV. */
+
+ /* Seed the graph search. */
+ stack_uv[stacksize_uv++] = element;
+ while (element) {
+ bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
+ element = element->next;
+ if (element && element->separate) {
+ break;
+ }
+ }
+
+ /* Traverse the graph. */
+ while (stacksize_uv) {
+ BLI_assert(stacksize_uv < stack_upper_bound);
+ element = stack_uv[--stacksize_uv];
+ while (element) {
+
+ /* Scan forwards around the BMFace that contains element->l. */
+ if (!uv_selected || uvedit_edge_select_test(scene, element->l, cd_loop_uv_offset)) {
+ UvElement *next = BM_uv_element_get(element_map, element->l->next->f, element->l->next);
+ if (next->island == INVALID_ISLAND) {
+ UvElement *tail = element_map->head_table[next - element_map->storage];
+ stack_uv[stacksize_uv++] = tail;
+ while (tail) {
+ bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
+ tail = tail->next;
+ if (tail && tail->separate) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* Scan backwards around the BMFace that contains element->l. */
+ if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, cd_loop_uv_offset)) {
+ UvElement *prev = BM_uv_element_get(element_map, element->l->prev->f, element->l->prev);
+ if (prev->island == INVALID_ISLAND) {
+ UvElement *tail = element_map->head_table[prev - element_map->storage];
+ stack_uv[stacksize_uv++] = tail;
+ while (tail) {
+ bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++);
+ tail = tail->next;
+ if (tail && tail->separate) {
+ break;
+ }
+ }
+ }
+ }
+
+ /* The same for all the UvElements in this unique UV. */
+ element = element->next;
+ if (element && element->separate) {
+ break;
+ }
+ }
+ }
+ nislands++;
+ }
+ BLI_assert(islandbufsize == total_uvs);
+
+ MEM_SAFE_FREE(stack_uv);
+ MEM_SAFE_FREE(element_map->head_table);
+
+ return nislands;
+}
+
+static void bm_uv_build_islands(UvElementMap *element_map,
+ BMesh *bm,
+ const Scene *scene,
+ bool uv_selected)
+{
+ int totuv = element_map->total_uvs;
+ int nislands = 0;
+ int islandbufsize = 0;
+
+ /* map holds the map from current vmap->buf to the new, sorted map */
+ uint *map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap");
+ BMFace **stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack");
+ UvElement *islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer");
+ /* Island number for BMFaces. */
+ int *island_number = MEM_callocN(sizeof(*island_number) * bm->totface, "uv_island_number_face");
+ copy_vn_i(island_number, bm->totface, INVALID_ISLAND);
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ?
+ scene->toolsettings->selectmode & SCE_SELECT_EDGE :
+ scene->toolsettings->uv_selectmode & UV_SELECT_EDGE;
+ if (use_uv_edge_connectivity) {
+ nislands = bm_uv_edge_select_build_islands(
+ element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset);
+ islandbufsize = totuv;
+ }
+
+ for (int i = 0; i < totuv; i++) {
+ if (element_map->storage[i].island == INVALID_ISLAND) {
+ int stacksize = 0;
+ element_map->storage[i].island = nislands;
+ stack[0] = element_map->storage[i].l->f;
+ island_number[BM_elem_index_get(stack[0])] = nislands;
+ stacksize = 1;
+
+ while (stacksize > 0) {
+ BMFace *efa = stack[--stacksize];
+
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ continue;
+ }
+
+ UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)];
+
+ for (UvElement *element = initelement; element; element = element->next) {
+ if (element->separate) {
+ initelement = element;
+ }
+
+ if (element->l->f == efa) {
+ /* found the uv corresponding to our face and vertex.
+ * Now fill it to the buffer */
+ bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++);
+
+ for (element = initelement; element; element = element->next) {
+ if (element->separate && element != initelement) {
+ break;
+ }
+
+ if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) {
+ stack[stacksize++] = element->l->f;
+ island_number[BM_elem_index_get(element->l->f)] = nislands;
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ nislands++;
+ }
+ }
+
+ MEM_SAFE_FREE(island_number);
+
+ /* remap */
+ for (int i = 0; i < bm->totvert; i++) {
+ /* important since we may do selection only. Some of these may be NULL */
+ if (element_map->vertex[i]) {
+ element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]];
+ }
+ }
+
+ element_map->island_indices = MEM_callocN(sizeof(*element_map->island_indices) * nislands,
+ __func__);
+ element_map->island_total_uvs = MEM_callocN(sizeof(*element_map->island_total_uvs) * nislands,
+ __func__);
+ element_map->island_total_unique_uvs = MEM_callocN(
+ sizeof(*element_map->island_total_unique_uvs) * nislands, __func__);
+ int j = 0;
+ for (int i = 0; i < totuv; i++) {
+ UvElement *next = element_map->storage[i].next;
+ islandbuf[map[i]].next = next ? &islandbuf[map[next - element_map->storage]] : NULL;
+
+ if (islandbuf[i].island != j) {
+ j++;
+ element_map->island_indices[j] = i;
+ }
+ BLI_assert(islandbuf[i].island == j);
+ element_map->island_total_uvs[j]++;
+ if (islandbuf[i].separate) {
+ element_map->island_total_unique_uvs[j]++;
+ }
+ }
+
+ MEM_SAFE_FREE(element_map->storage);
+ element_map->storage = islandbuf;
+ islandbuf = NULL;
+ element_map->total_islands = nislands;
+ MEM_SAFE_FREE(stack);
+ MEM_SAFE_FREE(map);
+}
+
+/* return true if `loop` has UV co-ordinates which match `luv_a` and `luv_b` */
+static bool loop_uv_match(BMLoop *loop, MLoopUV *luv_a, MLoopUV *luv_b, int cd_loop_uv_offset)
+{
+ MLoopUV *luv_c = BM_ELEM_CD_GET_VOID_P(loop, cd_loop_uv_offset);
+ MLoopUV *luv_d = BM_ELEM_CD_GET_VOID_P(loop->next, cd_loop_uv_offset);
+ return compare_v2v2(luv_a->uv, luv_c->uv, STD_UV_CONNECT_LIMIT) &&
+ compare_v2v2(luv_b->uv, luv_d->uv, STD_UV_CONNECT_LIMIT);
+}
+
+/* Given `anchor` and `edge`, return true if there are edges that fan between them that are
+ * seam-free. */
+static bool seam_connected_recursive(BMVert *anchor,
+ BMEdge *edge,
+ MLoopUV *luv_anchor,
+ MLoopUV *luv_fan,
+ BMLoop *needle,
+ GSet *visited,
+ int cd_loop_uv_offset)
+{
+ BLI_assert(edge->v1 == anchor || edge->v2 == anchor);
+ BLI_assert(needle->v == anchor || needle->next->v == anchor);
+
+ if (BM_elem_flag_test(edge, BM_ELEM_SEAM)) {
+ return false; /* Edge is a seam, don't traverse. */
+ }
+
+ if (!BLI_gset_add(visited, edge)) {
+ return false; /* Already visited. */
+ }
+
+ BMLoop *loop;
+ BMIter liter;
+ BM_ITER_ELEM (loop, &liter, edge, BM_LOOPS_OF_EDGE) {
+ if (loop->v == anchor) {
+ if (!loop_uv_match(loop, luv_anchor, luv_fan, cd_loop_uv_offset)) {
+ continue; /* `loop` is disjoint in UV space. */
+ }
+
+ if (loop->prev == needle) {
+ return true; /* Success. */
+ }
+
+ MLoopUV *luv_far = BM_ELEM_CD_GET_VOID_P(loop->prev, cd_loop_uv_offset);
+ if (seam_connected_recursive(
+ anchor, loop->prev->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset)) {
+ return true;
+ }
+ }
+ else {
+ BLI_assert(loop->next->v == anchor);
+ if (!loop_uv_match(loop, luv_fan, luv_anchor, cd_loop_uv_offset)) {
+ continue; /* `loop` is disjoint in UV space. */
+ }
+
+ if (loop->next == needle) {
+ return true; /* Success. */
+ }
+
+ MLoopUV *luv_far = BM_ELEM_CD_GET_VOID_P(loop->next->next, cd_loop_uv_offset);
+ if (seam_connected_recursive(
+ anchor, loop->next->e, luv_anchor, luv_far, needle, visited, cd_loop_uv_offset)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* Given `loop_a` and `loop_b` originate from the same vertex and share a UV,
+ * return true if there are edges that fan between them that are seam-free.
+ * return false otherwise.
+ */
+static bool seam_connected(BMLoop *loop_a, BMLoop *loop_b, GSet *visited, int cd_loop_uv_offset)
+{
+ BLI_assert(loop_a && loop_b);
+ BLI_assert(loop_a != loop_b);
+ BLI_assert(loop_a->v == loop_b->v);
+
+ BLI_gset_clear(visited, NULL);
+
+ MLoopUV *luv_anchor = BM_ELEM_CD_GET_VOID_P(loop_a, cd_loop_uv_offset);
+ MLoopUV *luv_fan = BM_ELEM_CD_GET_VOID_P(loop_a->next, cd_loop_uv_offset);
+ const bool result = seam_connected_recursive(
+ loop_a->v, loop_a->e, luv_anchor, luv_fan, loop_b, visited, cd_loop_uv_offset);
+ return result;
+}
+
UvElementMap *BM_uv_element_map_create(BMesh *bm,
const Scene *scene,
- const bool face_selected,
const bool uv_selected,
const bool use_winding,
+ const bool use_seams,
const bool do_islands)
{
+ /* In uv sync selection, all UVs are visible. */
+ const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
+
BMVert *ev;
BMFace *efa;
- BMLoop *l;
BMIter iter, liter;
- /* vars from original func */
- UvElementMap *element_map;
- UvElement *buf;
- bool *winding = NULL;
BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE);
-
MLoopUV *luv;
- int totverts, totfaces, i, totuv, j;
-
const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ if (cd_loop_uv_offset < 0) {
+ return NULL;
+ }
BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE);
- totfaces = bm->totface;
- totverts = bm->totvert;
- totuv = 0;
-
- /* generate UvElement array */
+ /* Count total uvs. */
+ int totuv = 0;
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- if (!uv_selected) {
- totuv += efa->len;
- }
- else {
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- totuv++;
- }
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+
+ if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ continue;
+ }
+
+ if (!uv_selected) {
+ totuv += efa->len;
+ }
+ else {
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ totuv++;
}
}
}
@@ -641,93 +989,114 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
return NULL;
}
- element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap");
- element_map->totalUVs = totuv;
- element_map->vert = (UvElement **)MEM_callocN(sizeof(*element_map->vert) * totverts,
- "UvElementVerts");
- buf = element_map->buf = (UvElement *)MEM_callocN(sizeof(*element_map->buf) * totuv,
- "UvElement");
+ UvElementMap *element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap");
+ element_map->total_uvs = totuv;
+ element_map->vertex = (UvElement **)MEM_callocN(sizeof(*element_map->vertex) * bm->totvert,
+ "UvElementVerts");
+ element_map->storage = (UvElement *)MEM_callocN(sizeof(*element_map->storage) * totuv,
+ "UvElement");
- if (use_winding) {
- winding = MEM_mallocN(sizeof(*winding) * totfaces, "winding");
- }
+ bool *winding = use_winding ? MEM_callocN(sizeof(*winding) * bm->totface, "winding") : NULL;
+ UvElement *buf = element_map->storage;
+ int j;
BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) {
- if (use_winding) {
- winding[j] = false;
+ if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
+ continue;
}
- if (!face_selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
- float(*tf_uv)[2] = NULL;
-
- if (use_winding) {
- tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len);
- }
+ if (face_selected && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
+ continue;
+ }
- BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
- if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- continue;
- }
+ float(*tf_uv)[2] = NULL;
- buf->l = l;
- buf->separate = 0;
- buf->island = INVALID_ISLAND;
- buf->loop_of_poly_index = i;
+ if (use_winding) {
+ tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len);
+ }
- buf->next = element_map->vert[BM_elem_index_get(l->v)];
- element_map->vert[BM_elem_index_get(l->v)] = buf;
+ int i;
+ BMLoop *l;
+ BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
+ if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+ continue;
+ }
- if (use_winding) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- copy_v2_v2(tf_uv[i], luv->uv);
- }
+ buf->l = l;
+ buf->island = INVALID_ISLAND;
+ buf->loop_of_poly_index = i;
- buf++;
- }
+ /* Insert to head of linked list associated with BMVert. */
+ buf->next = element_map->vertex[BM_elem_index_get(l->v)];
+ element_map->vertex[BM_elem_index_get(l->v)] = buf;
if (use_winding) {
- winding[j] = cross_poly_v2(tf_uv, efa->len) > 0;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ copy_v2_v2(tf_uv[i], luv->uv);
}
+
+ buf++;
+ }
+
+ if (winding) {
+ winding[j] = cross_poly_v2(tf_uv, efa->len) > 0;
}
}
+ BLI_buffer_free(&tf_uv_buf);
- /* sort individual uvs for each vert */
- BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, i) {
- UvElement *newvlist = NULL, *vlist = element_map->vert[i];
- UvElement *iterv, *v, *lastv, *next;
- const float *uv, *uv2;
- bool uv_vert_sel, uv2_vert_sel;
+ GSet *seam_visited_gset = use_seams ? BLI_gset_ptr_new(__func__) : NULL;
+ /* For each BMVert, sort associated linked list into unique uvs. */
+ int ev_index;
+ BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) {
+ UvElement *newvlist = NULL;
+ UvElement *vlist = element_map->vertex[ev_index];
while (vlist) {
- v = vlist;
+
+ /* Detach head from unsorted list. */
+ UvElement *v = vlist;
vlist = vlist->next;
v->next = newvlist;
newvlist = v;
- l = v->l;
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- uv = luv->uv;
- uv_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
+ luv = BM_ELEM_CD_GET_VOID_P(v->l, cd_loop_uv_offset);
+ const float *uv = luv->uv;
+ bool uv_vert_sel = uvedit_uv_select_test(scene, v->l, cd_loop_uv_offset);
- lastv = NULL;
- iterv = vlist;
+ UvElement *lastv = NULL;
+ UvElement *iterv = vlist;
+ /* Scan through unsorted list, finding UvElements which are connected to `v`. */
while (iterv) {
- next = iterv->next;
+ UvElement *next = iterv->next;
+ luv = BM_ELEM_CD_GET_VOID_P(iterv->l, cd_loop_uv_offset);
- l = iterv->l;
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- uv2 = luv->uv;
- uv2_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
+ bool connected = true; /* Assume connected unless we can prove otherwise. */
+
+ if (connected) {
+ /* Are the two UVs close together? */
+ const float *uv2 = luv->uv;
+ connected = compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT);
+ }
+
+ if (connected) {
+ /* 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). */
+ const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, cd_loop_uv_offset);
+ connected = (uv_vert_sel == uv2_vert_sel);
+ }
+
+ if (connected && use_winding) {
+ connected = winding[BM_elem_index_get(iterv->l->f)] ==
+ winding[BM_elem_index_get(v->l->f)];
+ }
- /* 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). */
- const bool connected = (uv_vert_sel == uv2_vert_sel) &&
- compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT);
+ if (connected && use_seams) {
+ connected = seam_connected(iterv->l, v->l, seam_visited_gset, cd_loop_uv_offset);
+ }
- if (connected && (!use_winding || winding[BM_elem_index_get(iterv->l->f)] ==
- winding[BM_elem_index_get(v->l->f)])) {
+ if (connected) {
if (lastv) {
lastv->next = next;
}
@@ -744,126 +1113,34 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
iterv = next;
}
- newvlist->separate = 1;
+ element_map->total_unique_uvs++;
+ newvlist->separate = true;
}
- element_map->vert[i] = newvlist;
+ /* Write back sorted list. */
+ element_map->vertex[ev_index] = newvlist;
}
- if (use_winding) {
- MEM_freeN(winding);
+ if (seam_visited_gset) {
+ BLI_gset_free(seam_visited_gset, NULL);
+ seam_visited_gset = NULL;
}
+ MEM_SAFE_FREE(winding);
+ /* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
+ * Now we should sort uv's in islands. */
if (do_islands) {
- uint *map;
- BMFace **stack;
- int stacksize = 0;
- UvElement *islandbuf;
- /* island number for faces */
- int *island_number = NULL;
-
- int nislands = 0, islandbufsize = 0;
-
- /* map holds the map from current vmap->buf to the new, sorted map */
- map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap");
- stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack");
- islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer");
- island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face");
- copy_vn_i(island_number, totfaces, INVALID_ISLAND);
-
- /* at this point, every UvElement in vert points to a UvElement sharing the same vertex.
- * Now we should sort uv's in islands. */
- for (i = 0; i < totuv; i++) {
- if (element_map->buf[i].island == INVALID_ISLAND) {
- element_map->buf[i].island = nislands;
- stack[0] = element_map->buf[i].l->f;
- island_number[BM_elem_index_get(stack[0])] = nislands;
- stacksize = 1;
-
- while (stacksize > 0) {
- efa = stack[--stacksize];
-
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
- continue;
- }
-
- UvElement *element, *initelement = element_map->vert[BM_elem_index_get(l->v)];
-
- for (element = initelement; element; element = element->next) {
- if (element->separate) {
- initelement = element;
- }
-
- if (element->l->f == efa) {
- /* found the uv corresponding to our face and vertex.
- * Now fill it to the buffer */
- element->island = nislands;
- map[element - element_map->buf] = islandbufsize;
- islandbuf[islandbufsize].l = element->l;
- islandbuf[islandbufsize].separate = element->separate;
- islandbuf[islandbufsize].loop_of_poly_index = element->loop_of_poly_index;
- islandbuf[islandbufsize].island = nislands;
- islandbufsize++;
-
- for (element = initelement; element; element = element->next) {
- if (element->separate && element != initelement) {
- break;
- }
-
- if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) {
- stack[stacksize++] = element->l->f;
- island_number[BM_elem_index_get(element->l->f)] = nislands;
- }
- }
- break;
- }
- }
- }
- }
-
- nislands++;
- }
- }
-
- MEM_freeN(island_number);
-
- /* remap */
- for (i = 0; i < bm->totvert; i++) {
- /* important since we may do selection only. Some of these may be NULL */
- if (element_map->vert[i]) {
- element_map->vert[i] = &islandbuf[map[element_map->vert[i] - element_map->buf]];
- }
- }
-
- element_map->islandIndices = MEM_callocN(sizeof(*element_map->islandIndices) * nislands,
- "UvElementMap_island_indices");
- j = 0;
- for (i = 0; i < totuv; i++) {
- UvElement *element = element_map->buf[i].next;
- if (element == NULL) {
- islandbuf[map[i]].next = NULL;
- }
- else {
- islandbuf[map[i]].next = &islandbuf[map[element - element_map->buf]];
- }
+ bm_uv_build_islands(element_map, bm, scene, uv_selected);
+ }
- if (islandbuf[i].island != j) {
- j++;
- element_map->islandIndices[j] = i;
- }
+ /* TODO: Confirm element_map->total_unique_uvs doesn't require recalculating. */
+ element_map->total_unique_uvs = 0;
+ for (int i = 0; i < element_map->total_uvs; i++) {
+ if (element_map->storage[i].separate) {
+ element_map->total_unique_uvs++;
}
-
- MEM_freeN(element_map->buf);
-
- element_map->buf = islandbuf;
- element_map->totalIslands = nislands;
- MEM_freeN(stack);
- MEM_freeN(map);
}
- BLI_buffer_free(&tf_uv_buf);
-
return element_map;
}
@@ -883,30 +1160,38 @@ void BM_uv_vert_map_free(UvVertMap *vmap)
void BM_uv_element_map_free(UvElementMap *element_map)
{
if (element_map) {
- if (element_map->vert) {
- MEM_freeN(element_map->vert);
- }
- if (element_map->buf) {
- MEM_freeN(element_map->buf);
- }
- if (element_map->islandIndices) {
- MEM_freeN(element_map->islandIndices);
- }
- MEM_freeN(element_map);
+ MEM_SAFE_FREE(element_map->storage);
+ MEM_SAFE_FREE(element_map->vertex);
+ MEM_SAFE_FREE(element_map->head_table);
+ MEM_SAFE_FREE(element_map->island_indices);
+ MEM_SAFE_FREE(element_map->island_total_uvs);
+ MEM_SAFE_FREE(element_map->island_total_unique_uvs);
+ MEM_SAFE_FREE(element_map);
}
}
-UvElement *BM_uv_element_get(UvElementMap *map, BMFace *efa, BMLoop *l)
+UvElement *BM_uv_element_get(const UvElementMap *element_map, const BMFace *efa, const BMLoop *l)
{
- for (UvElement *element = map->vert[BM_elem_index_get(l->v)]; element; element = element->next) {
+ UvElement *element = element_map->vertex[BM_elem_index_get(l->v)];
+ while (element) {
if (element->l->f == efa) {
return element;
}
+ element = element->next;
}
return NULL;
}
+UvElement *BM_uv_element_get_head(UvElementMap *element_map, UvElement *child)
+{
+ if (!child) {
+ return NULL;
+ }
+
+ return element_map->vertex[BM_elem_index_get(child->l->v)];
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1376,7 +1661,7 @@ void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params)
}
if (params->is_destructive) {
- /* TODO(campbell): we may be able to remove this now! */
+ /* TODO(@campbellbarton): we may be able to remove this now! */
// BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP);
}
else {
@@ -1488,15 +1773,13 @@ BMElem *EDBM_elem_from_index_any(BMEditMesh *em, uint index)
return NULL;
}
-int EDBM_elem_to_index_any_multi(ViewLayer *view_layer,
- BMEditMesh *em,
- BMElem *ele,
- int *r_object_index)
+int EDBM_elem_to_index_any_multi(
+ const Scene *scene, ViewLayer *view_layer, BMEditMesh *em, BMElem *ele, int *r_object_index)
{
uint bases_len;
int elem_index = -1;
*r_object_index = -1;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(view_layer, NULL, &bases_len);
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, NULL, &bases_len);
for (uint base_index = 0; base_index < bases_len; base_index++) {
Base *base_iter = bases[base_index];
if (BKE_editmesh_from_object(base_iter->object) == em) {
@@ -1509,13 +1792,14 @@ int EDBM_elem_to_index_any_multi(ViewLayer *view_layer,
return elem_index;
}
-BMElem *EDBM_elem_from_index_any_multi(ViewLayer *view_layer,
+BMElem *EDBM_elem_from_index_any_multi(const Scene *scene,
+ ViewLayer *view_layer,
uint object_index,
uint elem_index,
Object **r_obedit)
{
uint bases_len;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(view_layer, NULL, &bases_len);
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(scene, view_layer, NULL, &bases_len);
*r_obedit = NULL;
Object *obedit = (object_index < bases_len) ? bases[object_index]->object : NULL;
MEM_freeN(bases);
diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc
index 67834bf05ce..e362501d86c 100644
--- a/source/blender/editors/mesh/mesh_data.cc
+++ b/source/blender/editors/mesh/mesh_data.cc
@@ -18,6 +18,7 @@
#include "BLI_utildefines.h"
#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
@@ -44,6 +45,8 @@
#include "mesh_intern.h" /* own include */
using blender::Array;
+using blender::MutableSpan;
+using blender::Span;
static CustomData *mesh_customdata_get_type(Mesh *me, const char htype, int *r_tot)
{
@@ -128,7 +131,6 @@ static void delete_customdata_layer(Mesh *me, CustomDataLayer *layer)
}
else {
CustomData_free_layer(data, type, tot, layer_index + n);
- BKE_mesh_update_customdata_pointers(me, true);
}
}
@@ -186,7 +188,7 @@ static void mesh_uv_reset_bmface(BMFace *f, const int cd_loop_uv_offset)
mesh_uv_reset_array(fuv.data(), f->len);
}
-static void mesh_uv_reset_mface(MPoly *mp, MLoopUV *mloopuv)
+static void mesh_uv_reset_mface(const MPoly *mp, MLoopUV *mloopuv)
{
Array<float *, BM_DEFAULT_NGON_STACK_SIZE> fuv(mp->totloop);
@@ -208,7 +210,7 @@ void ED_mesh_uv_loop_reset_ex(Mesh *me, const int layernum)
BMFace *efa;
BMIter iter;
- BLI_assert(cd_loop_uv_offset != -1);
+ BLI_assert(cd_loop_uv_offset >= 0);
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
@@ -223,8 +225,9 @@ void ED_mesh_uv_loop_reset_ex(Mesh *me, const int layernum)
BLI_assert(CustomData_has_layer(&me->ldata, CD_MLOOPUV));
MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, layernum);
+ const MPoly *polys = BKE_mesh_polys(me);
for (int i = 0; i < me->totpoly; i++) {
- mesh_uv_reset_mface(&me->mpoly[i], mloopuv);
+ mesh_uv_reset_mface(&polys[i], mloopuv);
}
}
@@ -280,20 +283,23 @@ int ED_mesh_uv_add(
return -1;
}
- if (me->mloopuv && do_init) {
- CustomData_add_layer_named(
- &me->ldata, CD_MLOOPUV, CD_DUPLICATE, me->mloopuv, me->totloop, name);
+ if (CustomData_has_layer(&me->ldata, CD_MLOOPUV) && do_init) {
+ CustomData_add_layer_named(&me->ldata,
+ CD_MLOOPUV,
+ CD_DUPLICATE,
+ CustomData_get_layer(&me->ldata, CD_MLOOPUV),
+ me->totloop,
+ name);
is_init = true;
}
else {
- CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, me->totloop, name);
+ CustomData_add_layer_named(
+ &me->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, me->totloop, name);
}
if (active_set || layernum_dst == 0) {
CustomData_set_layer_active(&me->ldata, CD_MLOOPUV, layernum_dst);
}
-
- BKE_mesh_update_customdata_pointers(me, true);
}
/* don't overwrite our copied coords */
@@ -368,8 +374,11 @@ bool ED_mesh_uv_remove_named(Mesh *me, const char *name)
return false;
}
-int ED_mesh_color_add(
- Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports)
+int ED_mesh_color_add(Mesh *me,
+ const char *name,
+ const bool active_set,
+ const bool do_init,
+ ReportList *UNUSED(reports))
{
/* NOTE: keep in sync with #ED_mesh_uv_add. */
@@ -380,10 +389,6 @@ int ED_mesh_color_add(
em = me->edit_mesh;
layernum = CustomData_number_of_layers(&em->bm->ldata, CD_PROP_BYTE_COLOR);
- if (layernum >= MAX_MCOL) {
- BKE_reportf(reports, RPT_WARNING, "Cannot add more than %i vertex color layers", MAX_MCOL);
- return -1;
- }
/* CD_PROP_BYTE_COLOR */
BM_data_layer_add_named(em->bm, &em->bm->ldata, CD_PROP_BYTE_COLOR, name);
@@ -398,25 +403,25 @@ int ED_mesh_color_add(
}
else {
layernum = CustomData_number_of_layers(&me->ldata, CD_PROP_BYTE_COLOR);
- if (layernum >= MAX_MCOL) {
- BKE_reportf(reports, RPT_WARNING, "Cannot add more than %i vertex color layers", MAX_MCOL);
- return -1;
- }
- if (me->mloopcol && do_init) {
- CustomData_add_layer_named(
- &me->ldata, CD_PROP_BYTE_COLOR, CD_DUPLICATE, me->mloopcol, me->totloop, name);
+ if (CustomData_get_active_layer(&me->ldata, CD_PROP_BYTE_COLOR) != -1 && do_init) {
+ CustomData_add_layer_named(&me->ldata,
+ CD_PROP_BYTE_COLOR,
+ CD_DUPLICATE,
+ CustomData_get_layer(&me->ldata, CD_PROP_BYTE_COLOR),
+ me->totloop,
+ name);
}
else {
CustomData_add_layer_named(
- &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name);
+ &me->ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, me->totloop, name);
}
if (active_set || layernum == 0) {
CustomData_set_layer_active(&me->ldata, CD_PROP_BYTE_COLOR, layernum);
}
- BKE_mesh_update_customdata_pointers(me, true);
+ BKE_mesh_tessface_clear(me);
}
DEG_id_tag_update(&me->id, 0);
@@ -432,11 +437,11 @@ bool ED_mesh_color_ensure(Mesh *me, const char *name)
if (!layer) {
CustomData_add_layer_named(
- &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, name);
+ &me->ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, me->totloop, name);
layer = me->ldata.layers + CustomData_get_layer_index(&me->ldata, CD_PROP_BYTE_COLOR);
BKE_id_attributes_active_color_set(&me->id, layer);
- BKE_mesh_update_customdata_pointers(me, true);
+ BKE_mesh_tessface_clear(me);
}
DEG_id_tag_update(&me->id, 0);
@@ -444,44 +449,6 @@ bool ED_mesh_color_ensure(Mesh *me, const char *name)
return (layer != nullptr);
}
-bool ED_mesh_color_remove_index(Mesh *me, const int n)
-{
- CustomData *ldata = GET_CD_DATA(me, ldata);
- CustomDataLayer *cdl;
- int index;
-
- index = CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, n);
- cdl = (index == -1) ? nullptr : &ldata->layers[index];
-
- if (!cdl) {
- return false;
- }
-
- delete_customdata_layer(me, cdl);
- DEG_id_tag_update(&me->id, 0);
- WM_main_add_notifier(NC_GEOM | ND_DATA, me);
-
- return true;
-}
-bool ED_mesh_color_remove_active(Mesh *me)
-{
- CustomData *ldata = GET_CD_DATA(me, ldata);
- const int n = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR);
- if (n != -1) {
- return ED_mesh_color_remove_index(me, n);
- }
- return false;
-}
-bool ED_mesh_color_remove_named(Mesh *me, const char *name)
-{
- CustomData *ldata = GET_CD_DATA(me, ldata);
- const int n = CustomData_get_named_layer(ldata, CD_PROP_BYTE_COLOR, name);
- if (n != -1) {
- return ED_mesh_color_remove_index(me, n);
- }
- return false;
-}
-
/*********************** General poll ************************/
static bool layers_poll(bContext *C)
@@ -494,25 +461,10 @@ static bool layers_poll(bContext *C)
/*********************** Sculpt Vertex colors operators ************************/
-static bool sculpt_vertex_color_remove_poll(bContext *C)
-{
- if (!layers_poll(C)) {
- return false;
- }
-
- Object *ob = ED_object_context(C);
- Mesh *me = static_cast<Mesh *>(ob->data);
- CustomData *vdata = GET_CD_DATA(me, vdata);
- const int active = CustomData_get_active_layer(vdata, CD_PROP_COLOR);
- if (active != -1) {
- return true;
- }
-
- return false;
-}
-
-int ED_mesh_sculpt_color_add(
- Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports)
+int ED_mesh_sculpt_color_add(Mesh *me,
+ const char *name,
+ const bool do_init,
+ ReportList *UNUSED(reports))
{
/* NOTE: keep in sync with #ED_mesh_uv_add. */
@@ -523,11 +475,6 @@ int ED_mesh_sculpt_color_add(
em = me->edit_mesh;
layernum = CustomData_number_of_layers(&em->bm->vdata, CD_PROP_COLOR);
- if (layernum >= MAX_MCOL) {
- BKE_reportf(
- reports, RPT_WARNING, "Cannot add more than %i sculpt vertex color layers", MAX_MCOL);
- return -1;
- }
/* CD_PROP_COLOR */
BM_data_layer_add_named(em->bm, &em->bm->vdata, CD_PROP_COLOR, name);
@@ -536,17 +483,12 @@ int ED_mesh_sculpt_color_add(
const int layernum_dst = CustomData_get_active_layer(&em->bm->vdata, CD_PROP_COLOR);
BM_data_layer_copy(em->bm, &em->bm->vdata, CD_PROP_COLOR, layernum_dst, layernum);
}
- if (active_set || layernum == 0) {
+ if (layernum == 0) {
CustomData_set_layer_active(&em->bm->vdata, CD_PROP_COLOR, layernum);
}
}
else {
layernum = CustomData_number_of_layers(&me->vdata, CD_PROP_COLOR);
- if (layernum >= MAX_MCOL) {
- BKE_reportf(
- reports, RPT_WARNING, "Cannot add more than %i sculpt vertex color layers", MAX_MCOL);
- return -1;
- }
if (CustomData_has_layer(&me->vdata, CD_PROP_COLOR) && do_init) {
const MPropCol *color_data = (const MPropCol *)CustomData_get_layer(&me->vdata,
@@ -556,14 +498,14 @@ int ED_mesh_sculpt_color_add(
}
else {
CustomData_add_layer_named(
- &me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name);
+ &me->vdata, CD_PROP_COLOR, CD_SET_DEFAULT, nullptr, me->totvert, name);
}
- if (active_set || layernum == 0) {
+ if (layernum == 0) {
CustomData_set_layer_active(&me->vdata, CD_PROP_COLOR, layernum);
}
- BKE_mesh_update_customdata_pointers(me, true);
+ BKE_mesh_tessface_clear(me);
}
DEG_id_tag_update(&me->id, 0);
@@ -572,58 +514,6 @@ int ED_mesh_sculpt_color_add(
return layernum;
}
-bool ED_mesh_sculpt_color_ensure(Mesh *me, const char *name)
-{
- BLI_assert(me->edit_mesh == nullptr);
-
- if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) {
- CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name);
- BKE_mesh_update_customdata_pointers(me, true);
- }
-
- DEG_id_tag_update(&me->id, 0);
-
- return (me->mloopcol != nullptr);
-}
-
-bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n)
-{
- CustomData *vdata = GET_CD_DATA(me, vdata);
- CustomDataLayer *cdl;
- int index;
-
- index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n);
- cdl = (index == -1) ? nullptr : &vdata->layers[index];
-
- if (!cdl) {
- return false;
- }
-
- delete_customdata_layer(me, cdl);
- DEG_id_tag_update(&me->id, 0);
- WM_main_add_notifier(NC_GEOM | ND_DATA, me);
-
- return true;
-}
-bool ED_mesh_sculpt_color_remove_active(Mesh *me)
-{
- CustomData *vdata = GET_CD_DATA(me, vdata);
- const int n = CustomData_get_active_layer(vdata, CD_PROP_COLOR);
- if (n != -1) {
- return ED_mesh_sculpt_color_remove_index(me, n);
- }
- return false;
-}
-bool ED_mesh_sculpt_color_remove_named(Mesh *me, const char *name)
-{
- CustomData *vdata = GET_CD_DATA(me, vdata);
- const int n = CustomData_get_named_layer(vdata, CD_PROP_COLOR, name);
- if (n != -1) {
- return ED_mesh_sculpt_color_remove_index(me, n);
- }
- return false;
-}
-
/*********************** UV texture operators ************************/
static bool uv_texture_remove_poll(bContext *C)
@@ -709,135 +599,6 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-/*********************** vertex color operators ************************/
-
-static bool vertex_color_remove_poll(bContext *C)
-{
- if (!layers_poll(C)) {
- return false;
- }
-
- Object *ob = ED_object_context(C);
- Mesh *me = static_cast<Mesh *>(ob->data);
- CustomData *ldata = GET_CD_DATA(me, ldata);
- const int active = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR);
- if (active != -1) {
- return true;
- }
-
- return false;
-}
-
-static int mesh_vertex_color_add_exec(bContext *C, wmOperator *op)
-{
- Object *ob = ED_object_context(C);
- Mesh *me = static_cast<Mesh *>(ob->data);
-
- if (ED_mesh_color_add(me, nullptr, true, true, op->reports) == -1) {
- return OPERATOR_CANCELLED;
- }
-
- return OPERATOR_FINISHED;
-}
-
-void MESH_OT_vertex_color_add(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Add Vertex Color";
- ot->description = "Add vertex color layer";
- ot->idname = "MESH_OT_vertex_color_add";
-
- /* api callbacks */
- ot->poll = layers_poll;
- ot->exec = mesh_vertex_color_add_exec;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-static int mesh_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = ED_object_context(C);
- Mesh *me = static_cast<Mesh *>(ob->data);
-
- if (!ED_mesh_color_remove_active(me)) {
- return OPERATOR_CANCELLED;
- }
-
- return OPERATOR_FINISHED;
-}
-
-void MESH_OT_vertex_color_remove(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Remove Vertex Color";
- ot->description = "Remove vertex color layer";
- ot->idname = "MESH_OT_vertex_color_remove";
-
- /* api callbacks */
- ot->exec = mesh_vertex_color_remove_exec;
- ot->poll = vertex_color_remove_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/*********************** Sculpt Vertex Color Operators ************************/
-
-static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *op)
-{
- Object *ob = ED_object_context(C);
- Mesh *me = static_cast<Mesh *>(ob->data);
-
- if (ED_mesh_sculpt_color_add(me, nullptr, true, true, op->reports) == -1) {
- return OPERATOR_CANCELLED;
- }
-
- return OPERATOR_FINISHED;
-}
-
-void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Add Sculpt Vertex Color";
- ot->description = "Add vertex color layer";
- ot->idname = "MESH_OT_sculpt_vertex_color_add";
-
- /* api callbacks */
- ot->poll = layers_poll;
- ot->exec = mesh_sculpt_vertex_color_add_exec;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = ED_object_context(C);
- Mesh *me = static_cast<Mesh *>(ob->data);
-
- if (!ED_mesh_sculpt_color_remove_active(me)) {
- return OPERATOR_CANCELLED;
- }
-
- return OPERATOR_FINISHED;
-}
-
-void MESH_OT_sculpt_vertex_color_remove(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Remove Sculpt Vertex Color";
- ot->description = "Remove vertex color layer";
- ot->idname = "MESH_OT_sculpt_vertex_color_remove";
-
- /* api callbacks */
- ot->exec = mesh_sculpt_vertex_color_remove_exec;
- ot->poll = sculpt_vertex_color_remove_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
/* *** CustomData clear functions, we need an operator for each *** */
static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int type)
@@ -865,6 +626,28 @@ static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int typ
return OPERATOR_CANCELLED;
}
+static int mesh_customdata_add_exec__internal(bContext *C, char htype, int type)
+{
+ Mesh *mesh = ED_mesh_context(C);
+
+ int tot;
+ CustomData *data = mesh_customdata_get_type(mesh, htype, &tot);
+
+ BLI_assert(CustomData_layertype_is_singleton(type) == true);
+
+ if (mesh->edit_mesh) {
+ BM_data_layer_add(mesh->edit_mesh->bm, data, type);
+ }
+ else {
+ CustomData_add_layer(data, type, CD_SET_DEFAULT, NULL, tot);
+ }
+
+ DEG_id_tag_update(&mesh->id, 0);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh);
+
+ return CustomData_has_layer(data, type) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+}
+
/* Clear Mask */
static bool mesh_customdata_mask_clear_poll(bContext *C)
{
@@ -1015,19 +798,24 @@ static int mesh_customdata_custom_splitnormals_add_exec(bContext *C, wmOperator
/* Tag edges as sharp according to smooth threshold if needed,
* to preserve autosmooth shading. */
if (me->flag & ME_AUTOSMOOTH) {
- BKE_edges_sharp_from_angle_set(me->mvert,
- me->totvert,
- me->medge,
- me->totedge,
- me->mloop,
- me->totloop,
- me->mpoly,
+ const Span<MVert> verts = me->verts();
+ MutableSpan<MEdge> edges = me->edges_for_write();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
+
+ BKE_edges_sharp_from_angle_set(verts.data(),
+ verts.size(),
+ edges.data(),
+ edges.size(),
+ loops.data(),
+ loops.size(),
+ polys.data(),
BKE_mesh_poly_normals_ensure(me),
- me->totpoly,
+ polys.size(),
me->smoothresh);
}
- CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_DEFAULT, nullptr, me->totloop);
+ CustomData_add_layer(data, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, nullptr, me->totloop);
}
DEG_id_tag_update(&me->id, 0);
@@ -1082,6 +870,126 @@ void MESH_OT_customdata_custom_splitnormals_clear(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* Vertex bevel weight. */
+
+static int mesh_customdata_bevel_weight_vertex_state(bContext *C)
+{
+ const Object *object = ED_object_context(C);
+
+ if (object && object->type == OB_MESH) {
+ const Mesh *mesh = static_cast<Mesh *>(object->data);
+ if (!ID_IS_LINKED(mesh)) {
+ const CustomData *data = GET_CD_DATA(mesh, vdata);
+ return CustomData_has_layer(data, CD_BWEIGHT);
+ }
+ }
+ return -1;
+}
+
+static bool mesh_customdata_bevel_weight_vertex_add_poll(bContext *C)
+{
+ return mesh_customdata_bevel_weight_vertex_state(C) == 0;
+}
+
+static int mesh_customdata_bevel_weight_vertex_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ return mesh_customdata_add_exec__internal(C, BM_VERT, CD_BWEIGHT);
+}
+
+void MESH_OT_customdata_bevel_weight_vertex_add(wmOperatorType *ot)
+{
+ ot->name = "Add Vertex Bevel Weight";
+ ot->idname = "MESH_OT_customdata_bevel_weight_vertex_add";
+ ot->description = "Add a vertex bevel weight layer";
+
+ ot->exec = mesh_customdata_bevel_weight_vertex_add_exec;
+ ot->poll = mesh_customdata_bevel_weight_vertex_add_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static bool mesh_customdata_bevel_weight_vertex_clear_poll(bContext *C)
+{
+ return (mesh_customdata_bevel_weight_vertex_state(C) == 1);
+}
+
+static int mesh_customdata_bevel_weight_vertex_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ return mesh_customdata_clear_exec__internal(C, BM_VERT, CD_BWEIGHT);
+}
+
+void MESH_OT_customdata_bevel_weight_vertex_clear(wmOperatorType *ot)
+{
+ ot->name = "Clear Vertex Bevel Weight";
+ ot->idname = "MESH_OT_customdata_bevel_weight_vertex_clear";
+ ot->description = "Clear the vertex bevel weight layer";
+
+ ot->exec = mesh_customdata_bevel_weight_vertex_clear_exec;
+ ot->poll = mesh_customdata_bevel_weight_vertex_clear_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* Edge bevel weight. */
+
+static int mesh_customdata_bevel_weight_edge_state(bContext *C)
+{
+ const Object *ob = ED_object_context(C);
+
+ if (ob && ob->type == OB_MESH) {
+ const Mesh *mesh = static_cast<Mesh *>(ob->data);
+ if (!ID_IS_LINKED(mesh)) {
+ const CustomData *data = GET_CD_DATA(mesh, edata);
+ return CustomData_has_layer(data, CD_BWEIGHT);
+ }
+ }
+ return -1;
+}
+
+static bool mesh_customdata_bevel_weight_edge_add_poll(bContext *C)
+{
+ return mesh_customdata_bevel_weight_edge_state(C) == 0;
+}
+
+static int mesh_customdata_bevel_weight_edge_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ return mesh_customdata_add_exec__internal(C, BM_EDGE, CD_BWEIGHT);
+}
+
+void MESH_OT_customdata_bevel_weight_edge_add(wmOperatorType *ot)
+{
+ ot->name = "Add Edge Bevel Weight";
+ ot->idname = "MESH_OT_customdata_bevel_weight_edge_add";
+ ot->description = "Add an edge bevel weight layer";
+
+ ot->exec = mesh_customdata_bevel_weight_edge_add_exec;
+ ot->poll = mesh_customdata_bevel_weight_edge_add_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static bool mesh_customdata_bevel_weight_edge_clear_poll(bContext *C)
+{
+ return mesh_customdata_bevel_weight_edge_state(C) == 1;
+}
+
+static int mesh_customdata_bevel_weight_edge_clear_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ return mesh_customdata_clear_exec__internal(C, BM_EDGE, CD_BWEIGHT);
+}
+
+void MESH_OT_customdata_bevel_weight_edge_clear(wmOperatorType *ot)
+{
+ ot->name = "Clear Edge Bevel Weight";
+ ot->idname = "MESH_OT_customdata_bevel_weight_edge_clear";
+ ot->description = "Clear the edge bevel weight layer";
+
+ ot->exec = mesh_customdata_bevel_weight_edge_clear_exec;
+ ot->poll = mesh_customdata_bevel_weight_edge_clear_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/************************** Add Geometry Layers *************************/
void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_loose)
@@ -1112,36 +1020,31 @@ static void mesh_add_verts(Mesh *mesh, int len)
int totvert = mesh->totvert + len;
CustomData vdata;
- CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH.vmask, CD_DEFAULT, totvert);
+ CustomData_copy(&mesh->vdata, &vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, totvert);
CustomData_copy_data(&mesh->vdata, &vdata, 0, 0, mesh->totvert);
if (!CustomData_has_layer(&vdata, CD_MVERT)) {
- CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert);
+ CustomData_add_layer(&vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, totvert);
}
CustomData_free(&mesh->vdata, mesh->totvert);
mesh->vdata = vdata;
- BKE_mesh_update_customdata_pointers(mesh, false);
BKE_mesh_runtime_clear_cache(mesh);
- /* scan the input list and insert the new vertices */
+ const int old_vertex_num = mesh->totvert;
+ mesh->totvert = totvert;
- /* set default flags */
- MVert *mvert = &mesh->mvert[mesh->totvert];
- for (int i = 0; i < len; i++, mvert++) {
- mvert->flag |= SELECT;
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ for (MVert &vert : verts.drop_front(old_vertex_num)) {
+ vert.flag = SELECT;
}
-
- /* set final vertex list size */
- mesh->totvert = totvert;
}
static void mesh_add_edges(Mesh *mesh, int len)
{
CustomData edata;
- MEdge *medge;
- int i, totedge;
+ int totedge;
if (len == 0) {
return;
@@ -1150,26 +1053,25 @@ static void mesh_add_edges(Mesh *mesh, int len)
totedge = mesh->totedge + len;
/* Update custom-data. */
- CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_DEFAULT, totedge);
+ CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
if (!CustomData_has_layer(&edata, CD_MEDGE)) {
- CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge);
+ CustomData_add_layer(&edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, totedge);
}
CustomData_free(&mesh->edata, mesh->totedge);
mesh->edata = edata;
- BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */
BKE_mesh_runtime_clear_cache(mesh);
- /* set default flags */
- medge = &mesh->medge[mesh->totedge];
- for (i = 0; i < len; i++, medge++) {
- medge->flag = ME_EDGEDRAW | ME_EDGERENDER | SELECT;
- }
-
+ const int old_edges_num = mesh->totedge;
mesh->totedge = totedge;
+
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ for (MEdge &edge : edges.drop_front(old_edges_num)) {
+ edge.flag = ME_EDGEDRAW | ME_EDGERENDER | SELECT;
+ }
}
static void mesh_add_loops(Mesh *mesh, int len)
@@ -1184,18 +1086,17 @@ static void mesh_add_loops(Mesh *mesh, int len)
totloop = mesh->totloop + len; /* new face count */
/* update customdata */
- CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH.lmask, CD_DEFAULT, totloop);
+ CustomData_copy(&mesh->ldata, &ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, totloop);
CustomData_copy_data(&mesh->ldata, &ldata, 0, 0, mesh->totloop);
if (!CustomData_has_layer(&ldata, CD_MLOOP)) {
- CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop);
+ CustomData_add_layer(&ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, totloop);
}
BKE_mesh_runtime_clear_cache(mesh);
CustomData_free(&mesh->ldata, mesh->totloop);
mesh->ldata = ldata;
- BKE_mesh_update_customdata_pointers(mesh, true);
mesh->totloop = totloop;
}
@@ -1203,8 +1104,7 @@ static void mesh_add_loops(Mesh *mesh, int len)
static void mesh_add_polys(Mesh *mesh, int len)
{
CustomData pdata;
- MPoly *mpoly;
- int i, totpoly;
+ int totpoly;
if (len == 0) {
return;
@@ -1213,26 +1113,25 @@ static void mesh_add_polys(Mesh *mesh, int len)
totpoly = mesh->totpoly + len; /* new face count */
/* update customdata */
- CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH.pmask, CD_DEFAULT, totpoly);
+ CustomData_copy(&mesh->pdata, &pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, totpoly);
CustomData_copy_data(&mesh->pdata, &pdata, 0, 0, mesh->totpoly);
if (!CustomData_has_layer(&pdata, CD_MPOLY)) {
- CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly);
+ CustomData_add_layer(&pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, totpoly);
}
CustomData_free(&mesh->pdata, mesh->totpoly);
mesh->pdata = pdata;
- BKE_mesh_update_customdata_pointers(mesh, true);
BKE_mesh_runtime_clear_cache(mesh);
- /* set default flags */
- mpoly = &mesh->mpoly[mesh->totpoly];
- for (i = 0; i < len; i++, mpoly++) {
- mpoly->flag = ME_FACE_SEL;
- }
-
+ const int old_polys_num = mesh->totpoly;
mesh->totpoly = totpoly;
+
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ for (MPoly &poly : polys.drop_front(old_polys_num)) {
+ poly.flag = ME_FACE_SEL;
+ }
}
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 303234df48c..75f63ed5d6f 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -84,11 +84,13 @@ struct BMElem *EDBM_elem_from_selectmode(struct BMEditMesh *em,
int EDBM_elem_to_index_any(struct BMEditMesh *em, struct BMElem *ele);
struct BMElem *EDBM_elem_from_index_any(struct BMEditMesh *em, uint index);
-int EDBM_elem_to_index_any_multi(struct ViewLayer *view_layer,
+int EDBM_elem_to_index_any_multi(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct BMEditMesh *em,
struct BMElem *ele,
int *r_object_index);
-struct BMElem *EDBM_elem_from_index_any_multi(struct ViewLayer *view_layer,
+struct BMElem *EDBM_elem_from_index_any_multi(const struct Scene *scene,
+ struct ViewLayer *view_layer,
uint object_index,
uint elem_index,
struct Object **r_obedit);
@@ -308,15 +310,15 @@ void MESH_OT_mark_freestyle_face(struct wmOperatorType *ot);
void MESH_OT_uv_texture_add(struct wmOperatorType *ot);
void MESH_OT_uv_texture_remove(struct wmOperatorType *ot);
-void MESH_OT_vertex_color_add(struct wmOperatorType *ot);
-void MESH_OT_vertex_color_remove(struct wmOperatorType *ot);
-void MESH_OT_sculpt_vertex_color_add(struct wmOperatorType *ot);
-void MESH_OT_sculpt_vertex_color_remove(struct wmOperatorType *ot);
void MESH_OT_customdata_mask_clear(struct wmOperatorType *ot);
void MESH_OT_customdata_skin_add(struct wmOperatorType *ot);
void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot);
void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot);
void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot);
+void MESH_OT_customdata_bevel_weight_vertex_add(struct wmOperatorType *ot);
+void MESH_OT_customdata_bevel_weight_vertex_clear(struct wmOperatorType *ot);
+void MESH_OT_customdata_bevel_weight_edge_add(struct wmOperatorType *ot);
+void MESH_OT_customdata_bevel_weight_edge_clear(struct wmOperatorType *ot);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/mesh/mesh_mirror.c b/source/blender/editors/mesh/mesh_mirror.c
index 82e77a88a57..ad5a5d362f1 100644
--- a/source/blender/editors/mesh/mesh_mirror.c
+++ b/source/blender/editors/mesh/mesh_mirror.c
@@ -55,11 +55,9 @@ void ED_mesh_mirror_spatial_table_begin(Object *ob, BMEditMesh *em, Mesh *me_eva
}
}
else {
- MVert *mvert = me_eval ? me_eval->mvert : me->mvert;
- int i;
-
- for (i = 0; i < totvert; i++, mvert++) {
- BLI_kdtree_3d_insert(MirrKdStore.tree, i, mvert->co);
+ const MVert *verts = BKE_mesh_verts(me_eval ? me_eval : me);
+ for (int i = 0; i < totvert; i++) {
+ BLI_kdtree_3d_insert(MirrKdStore.tree, i, verts[i].co);
}
}
@@ -164,7 +162,7 @@ void ED_mesh_mirrtopo_init(BMEditMesh *em,
BLI_assert(me == NULL);
}
const bool is_editmode = (em != NULL);
- MEdge *medge = NULL, *med;
+ const MEdge *medge = NULL, *med;
/* Edit-mode variables. */
BMEdge *eed;
@@ -210,8 +208,7 @@ void ED_mesh_mirrtopo_init(BMEditMesh *em,
}
else {
totedge = me->totedge;
- medge = me->medge;
-
+ medge = BKE_mesh_edges(me);
for (a = 0, med = medge; a < totedge; a++, med++) {
const uint i1 = med->v1, i2 = med->v2;
topo_hash[i1]++;
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index be7f60b0da0..01c92a59fc9 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -134,15 +134,15 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_uv_texture_add);
WM_operatortype_append(MESH_OT_uv_texture_remove);
- WM_operatortype_append(MESH_OT_vertex_color_add);
- WM_operatortype_append(MESH_OT_vertex_color_remove);
- WM_operatortype_append(MESH_OT_sculpt_vertex_color_add);
- WM_operatortype_append(MESH_OT_sculpt_vertex_color_remove);
WM_operatortype_append(MESH_OT_customdata_mask_clear);
WM_operatortype_append(MESH_OT_customdata_skin_add);
WM_operatortype_append(MESH_OT_customdata_skin_clear);
WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_add);
WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_clear);
+ WM_operatortype_append(MESH_OT_customdata_bevel_weight_vertex_add);
+ WM_operatortype_append(MESH_OT_customdata_bevel_weight_vertex_clear);
+ WM_operatortype_append(MESH_OT_customdata_bevel_weight_edge_add);
+ WM_operatortype_append(MESH_OT_customdata_bevel_weight_edge_clear);
WM_operatortype_append(MESH_OT_edgering_select);
WM_operatortype_append(MESH_OT_loopcut);
diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc
index 9e28e1bafdd..831ab858b1c 100644
--- a/source/blender/editors/mesh/meshtools.cc
+++ b/source/blender/editors/mesh/meshtools.cc
@@ -10,6 +10,8 @@
#include "MEM_guardedalloc.h"
+#include "BLI_virtual_array.hh"
+
#include "DNA_key_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
@@ -21,6 +23,7 @@
#include "DNA_view3d_types.h"
#include "DNA_workspace_types.h"
+#include "BKE_attribute.hh"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_deform.h"
@@ -52,6 +55,9 @@
#include "WM_api.h"
#include "WM_types.h"
+using blender::MutableSpan;
+using blender::Span;
+
/* * ********************** no editmode!!! *********** */
/*********************** JOIN ***************************/
@@ -100,7 +106,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
((Mesh *)ob_dst->data)->cd_flag |= me->cd_flag;
/* standard data */
- CustomData_merge(&me->vdata, vdata, CD_MASK_MESH.vmask, CD_DEFAULT, totvert);
+ CustomData_merge(&me->vdata, vdata, CD_MASK_MESH.vmask, CD_SET_DEFAULT, totvert);
CustomData_copy_data_named(&me->vdata, vdata, 0, *vertofs, me->totvert);
/* vertex groups */
@@ -199,7 +205,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
}
if (me->totedge) {
- CustomData_merge(&me->edata, edata, CD_MASK_MESH.emask, CD_DEFAULT, totedge);
+ CustomData_merge(&me->edata, edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_data_named(&me->edata, edata, 0, *edgeofs, me->totedge);
for (a = 0; a < me->totedge; a++, medge++) {
@@ -220,7 +226,7 @@ static void join_mesh_single(Depsgraph *depsgraph,
}
}
- CustomData_merge(&me->ldata, ldata, CD_MASK_MESH.lmask, CD_DEFAULT, totloop);
+ CustomData_merge(&me->ldata, ldata, CD_MASK_MESH.lmask, CD_SET_DEFAULT, totloop);
CustomData_copy_data_named(&me->ldata, ldata, 0, *loopofs, me->totloop);
for (a = 0; a < me->totloop; a++, mloop++) {
@@ -244,12 +250,25 @@ static void join_mesh_single(Depsgraph *depsgraph,
}
}
- CustomData_merge(&me->pdata, pdata, CD_MASK_MESH.pmask, CD_DEFAULT, totpoly);
+ CustomData_merge(&me->pdata, pdata, CD_MASK_MESH.pmask, CD_SET_DEFAULT, totpoly);
CustomData_copy_data_named(&me->pdata, pdata, 0, *polyofs, me->totpoly);
+ /* Apply matmap. In case we don't have material indices yet, create them if more than one
+ * material is the result of joining. */
+ int *material_indices = static_cast<int *>(
+ CustomData_get_layer_named(pdata, CD_PROP_INT32, "material_index"));
+ if (!material_indices && totcol > 1) {
+ material_indices = (int *)CustomData_add_layer_named(
+ pdata, CD_PROP_INT32, CD_SET_DEFAULT, NULL, totpoly, "material_index");
+ }
+ if (material_indices) {
+ for (a = 0; a < me->totpoly; a++) {
+ material_indices[a + *polyofs] = matmap ? matmap[material_indices[a + *polyofs]] : 0;
+ }
+ }
+
for (a = 0; a < me->totpoly; a++, mpoly++) {
mpoly->loopstart += *loopofs;
- mpoly->mat_nr = matmap ? matmap[mpoly->mat_nr] : 0;
}
/* Face maps. */
@@ -298,15 +317,10 @@ static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset
for (int f = 0; f < mesh->totpoly; f++) {
/* As face sets encode the visibility in the integer sign, the offset needs to be added or
* subtracted depending on the initial sign of the integer to get the new ID. */
- if (abs(face_sets[f]) <= *face_set_offset) {
- if (face_sets[f] > 0) {
- face_sets[f] += *face_set_offset;
- }
- else {
- face_sets[f] -= *face_set_offset;
- }
+ if (face_sets[f] <= *face_set_offset) {
+ face_sets[f] += *face_set_offset;
}
- max_face_set = max_ii(max_face_set, abs(face_sets[f]));
+ max_face_set = max_ii(max_face_set, face_sets[f]);
}
*face_set_offset = max_face_set;
}
@@ -328,7 +342,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
int totloop = 0, totpoly = 0, vertofs, *matmap = nullptr;
int i, haskey = 0, edgeofs, loopofs, polyofs;
bool ok = false, join_parent = false;
- CustomData vdata, edata, fdata, ldata, pdata;
+ CustomData vdata, edata, ldata, pdata;
if (ob->mode & OB_MODE_EDIT) {
BKE_report(op->reports, RPT_WARNING, "Cannot join while in edit mode");
@@ -567,14 +581,13 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
/* setup new data for destination mesh */
CustomData_reset(&vdata);
CustomData_reset(&edata);
- CustomData_reset(&fdata);
CustomData_reset(&ldata);
CustomData_reset(&pdata);
- mvert = (MVert *)CustomData_add_layer(&vdata, CD_MVERT, CD_CALLOC, nullptr, totvert);
- medge = (MEdge *)CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge);
- mloop = (MLoop *)CustomData_add_layer(&ldata, CD_MLOOP, CD_CALLOC, nullptr, totloop);
- mpoly = (MPoly *)CustomData_add_layer(&pdata, CD_MPOLY, CD_CALLOC, nullptr, totpoly);
+ mvert = (MVert *)CustomData_add_layer(&vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, totvert);
+ medge = (MEdge *)CustomData_add_layer(&edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, totedge);
+ mloop = (MLoop *)CustomData_add_layer(&ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, totloop);
+ mpoly = (MPoly *)CustomData_add_layer(&pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, totpoly);
vertofs = 0;
edgeofs = 0;
@@ -678,9 +691,6 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
me->ldata = ldata;
me->pdata = pdata;
- /* tessface data removed above, no need to update */
- BKE_mesh_update_customdata_pointers(me, false);
-
/* Tag normals dirty because vertex positions could be changed from the original. */
BKE_mesh_normals_tag_dirty(me);
@@ -893,13 +903,13 @@ static bool ed_mesh_mirror_topo_table_update(Object *ob, Mesh *me_eval)
static int mesh_get_x_mirror_vert_spatial(Object *ob, Mesh *me_eval, int index)
{
Mesh *me = static_cast<Mesh *>(ob->data);
- MVert *mvert = me_eval ? me_eval->mvert : me->mvert;
+ const Span<MVert> verts = me_eval ? me_eval->verts() : me->verts();
+
float vec[3];
- mvert = &mvert[index];
- vec[0] = -mvert->co[0];
- vec[1] = mvert->co[1];
- vec[2] = mvert->co[2];
+ vec[0] = -verts[index].co[0];
+ vec[1] = verts[index].co[1];
+ vec[2] = verts[index].co[2];
return ED_mesh_mirror_spatial_table_lookup(ob, nullptr, me_eval, vec);
}
@@ -1115,8 +1125,8 @@ static bool mirror_facecmp(const void *a, const void *b)
int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval)
{
Mesh *me = static_cast<Mesh *>(ob->data);
- MVert *mv, *mvert;
- MFace mirrormf, *mf, *hashmf, *mface;
+ const MVert *mv;
+ MFace mirrormf, *mf, *hashmf;
GHash *fhash;
int *mirrorverts, *mirrorfaces;
@@ -1130,12 +1140,12 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em, Mesh *me_eval)
mirrorverts = static_cast<int *>(MEM_callocN(sizeof(int) * totvert, "MirrorVerts"));
mirrorfaces = static_cast<int *>(MEM_callocN(sizeof(int[2]) * totface, "MirrorFaces"));
- mvert = me_eval ? me_eval->mvert : me->mvert;
- mface = me_eval ? me_eval->mface : me->mface;
+ const Span<MVert> verts = me_eval ? me_eval->verts() : me->verts();
+ MFace *mface = (MFace *)CustomData_get_layer(&(me_eval ? me_eval : me)->fdata, CD_MFACE);
ED_mesh_mirror_spatial_table_begin(ob, em, me_eval);
- for (a = 0, mv = mvert; a < totvert; a++, mv++) {
+ for (a = 0, mv = verts.data(); a < totvert; a++, mv++) {
mirrorverts[a] = mesh_get_x_mirror_vert(ob, me_eval, a, use_topology);
}
@@ -1262,42 +1272,28 @@ bool ED_mesh_pick_face_vert(
const float mval_f[2] = {(float)mval[0], (float)mval[1]};
float len_best = FLT_MAX;
- MPoly *me_eval_mpoly;
- MLoop *me_eval_mloop;
- MVert *me_eval_mvert;
- uint me_eval_mpoly_len;
-
- me_eval_mpoly = me_eval->mpoly;
- me_eval_mloop = me_eval->mloop;
- me_eval_mvert = me_eval->mvert;
-
- me_eval_mpoly_len = me_eval->totpoly;
+ const Span<MVert> verts = me_eval->verts();
+ const Span<MPoly> polys = me_eval->polys();
+ const Span<MLoop> loops = me_eval->loops();
const int *index_mp_to_orig = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX);
/* tag all verts using this face */
if (index_mp_to_orig) {
- uint i;
-
- for (i = 0; i < me_eval_mpoly_len; i++) {
+ for (const int i : polys.index_range()) {
if (index_mp_to_orig[i] == poly_index) {
- ed_mesh_pick_face_vert__mpoly_find(region,
- mval_f,
- &me_eval_mpoly[i],
- me_eval_mvert,
- me_eval_mloop,
- &len_best,
- &v_idx_best);
+ ed_mesh_pick_face_vert__mpoly_find(
+ region, mval_f, &polys[i], verts.data(), loops.data(), &len_best, &v_idx_best);
}
}
}
else {
- if (poly_index < me_eval_mpoly_len) {
+ if (poly_index < polys.size()) {
ed_mesh_pick_face_vert__mpoly_find(region,
mval_f,
- &me_eval_mpoly[poly_index],
- me_eval_mvert,
- me_eval_mloop,
+ &polys[poly_index],
+ verts.data(),
+ loops.data(),
&len_best,
&v_idx_best);
}
@@ -1329,6 +1325,7 @@ bool ED_mesh_pick_face_vert(
*/
struct VertPickData {
const MVert *mvert;
+ const bool *hide_vert;
const float *mval_f; /* [2] */
ARegion *region;
@@ -1343,16 +1340,16 @@ static void ed_mesh_pick_vert__mapFunc(void *userData,
const float UNUSED(no[3]))
{
VertPickData *data = static_cast<VertPickData *>(userData);
- if ((data->mvert[index].flag & ME_HIDE) == 0) {
- float sco[2];
-
- if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) ==
- V3D_PROJ_RET_OK) {
- const float len = len_manhattan_v2v2(data->mval_f, sco);
- if (len < data->len_best) {
- data->len_best = len;
- data->v_idx_best = index;
- }
+ if (data->hide_vert && data->hide_vert[index]) {
+ return;
+ }
+ float sco[2];
+ if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) ==
+ V3D_PROJ_RET_OK) {
+ const float len = len_manhattan_v2v2(data->mval_f, sco);
+ if (len < data->len_best) {
+ data->len_best = len;
+ data->v_idx_best = index;
}
}
}
@@ -1411,11 +1408,14 @@ bool ED_mesh_pick_vert(
}
/* setup data */
- data.mvert = me->mvert;
+ const Span<MVert> verts = me->verts();
+ data.mvert = verts.data();
data.region = region;
data.mval_f = mval_f;
data.len_best = FLT_MAX;
data.v_idx_best = -1;
+ data.hide_vert = (const bool *)CustomData_get_layer_named(
+ &me_eval->vdata, CD_PROP_BOOL, ".hide_vert");
BKE_mesh_foreach_mapped_vert(me_eval, ed_mesh_pick_vert__mapFunc, &data, MESH_FOREACH_NOP);
@@ -1463,10 +1463,11 @@ MDeformVert *ED_mesh_active_dvert_get_ob(Object *ob, int *r_index)
if (r_index) {
*r_index = index;
}
- if (index == -1 || me->dvert == nullptr) {
+ if (index == -1 || me->deform_verts().is_empty()) {
return nullptr;
}
- return me->dvert + index;
+ MutableSpan<MDeformVert> dverts = me->deform_verts_for_write();
+ return &dverts[index];
}
MDeformVert *ED_mesh_active_dvert_get_only(Object *ob)
diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c
index 5f13e822f84..0f9049ad70c 100644
--- a/source/blender/editors/metaball/editmball_undo.c
+++ b/source/blender/editors/metaball/editmball_undo.c
@@ -109,8 +109,10 @@ static void undomball_free_data(UndoMBall *umb)
static Object *editmball_object_from_context(bContext *C)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit && obedit->type == OB_MBALL) {
MetaBall *mb = obedit->data;
if (mb->editelems != NULL) {
@@ -150,9 +152,10 @@ static bool mball_undosys_step_encode(struct bContext *C, struct Main *bmain, Un
/* Important not to use the 3D view when getting objects because all objects
* outside of this list will be moved out of edit-mode when reading back undo steps. */
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
- Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len);
+ Object **objects = ED_undo_editmode_objects_from_view_layer(scene, view_layer, &objects_len);
us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__);
us->elems_len = objects_len;
diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c
index 06a649e5b6c..9515306a26c 100644
--- a/source/blender/editors/metaball/mball_edit.c
+++ b/source/blender/editors/metaball/mball_edit.c
@@ -90,7 +90,7 @@ bool ED_mball_deselect_all_multi(bContext *C)
ED_view3d_viewcontext_init(C, &vc, depsgraph);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc.view_layer, vc.v3d, &bases_len);
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
bool changed_multi = BKE_mball_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
return changed_multi;
@@ -145,10 +145,11 @@ static int mball_select_all_exec(bContext *C, wmOperator *op)
{
int action = RNA_enum_get(op->ptr, "action");
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &bases_len);
+ scene, view_layer, CTX_wm_view3d(C), &bases_len);
if (action == SEL_TOGGLE) {
action = BKE_mball_is_any_selected_multi(bases, bases_len) ? SEL_DESELECT : SEL_SELECT;
@@ -330,10 +331,11 @@ static int mball_select_similar_exec(bContext *C, wmOperator *op)
const float thresh = RNA_float_get(op->ptr, "threshold");
int tot_mball_selected_all = 0;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &bases_len);
+ scene, view_layer, CTX_wm_view3d(C), &bases_len);
tot_mball_selected_all = BKE_mball_select_count_multi(bases, bases_len);
@@ -463,10 +465,11 @@ static int select_random_metaelems_exec(bContext *C, wmOperator *op)
const float randfac = RNA_float_get(op->ptr, "ratio");
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
MetaBall *mb = (MetaBall *)obedit->data;
@@ -529,10 +532,11 @@ void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot)
/* Duplicate selected MetaElements */
static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
MetaBall *mb = (MetaBall *)obedit->data;
@@ -586,10 +590,11 @@ void MBALL_OT_duplicate_metaelems(wmOperatorType *ot)
static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
MetaBall *mb = (MetaBall *)obedit->data;
@@ -744,7 +749,7 @@ Base *ED_mball_base_and_elem_from_select_buffer(Base **bases,
const uint hit_object = select_id & 0xFFFF;
Base *base = NULL;
MetaElem *ml = NULL;
- /* TODO(campbell): optimize, eg: sort & binary search. */
+ /* TODO(@campbellbarton): optimize, eg: sort & binary search. */
for (uint base_index = 0; base_index < bases_len; base_index++) {
if (bases[base_index]->object->runtime.select_id == hit_object) {
base = bases[base_index];
@@ -790,7 +795,8 @@ static bool ed_mball_findnearest_metaelem(bContext *C,
}
uint bases_len = 0;
- Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(vc.view_layer, vc.v3d, &bases_len);
+ Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
+ vc.scene, vc.view_layer, vc.v3d, &bases_len);
int hit_cycle_offset = 0;
if (use_cycle) {
@@ -900,7 +906,7 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], const struct SelectPic
break;
}
}
-
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
MetaBall *mb = (MetaBall *)base->object->data;
mb->lastelem = ml;
@@ -908,7 +914,8 @@ bool ED_mball_select_pick(bContext *C, const int mval[2], const struct SelectPic
DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
- if (view_layer->basact != base) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (BKE_view_layer_active_base_get(view_layer) != base) {
ED_object_base_activate(C, base);
}
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 97376a495c1..17365cc5488 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -21,7 +21,6 @@ set(INC
../../shader_fx
../../windowmanager
../../../../intern/clog
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# dna_type_offsets.h in BLO_read_write.h
@@ -53,7 +52,7 @@ set(SRC
object_shapekey.c
object_transform.cc
object_utils.c
- object_vgroup.c
+ object_vgroup.cc
object_volume.c
object_warp.c
@@ -76,7 +75,6 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
- add_definitions(-DWITH_NEW_CURVES_TYPE)
endif()
blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc
index 66e76addd6f..9d06a6d5f40 100644
--- a/source/blender/editors/object/object_add.cc
+++ b/source/blender/editors/object/object_add.cc
@@ -609,7 +609,8 @@ Object *ED_object_add_type_with_obdata(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
{
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit != nullptr) {
ED_object_editmode_exit_ex(bmain, scene, obedit, EM_FREEDATA);
}
@@ -619,17 +620,18 @@ Object *ED_object_add_type_with_obdata(bContext *C,
Object *ob;
if (obdata != nullptr) {
BLI_assert(type == BKE_object_obdata_to_type(obdata));
- ob = BKE_object_add_for_data(bmain, view_layer, type, name, obdata, true);
+ ob = BKE_object_add_for_data(bmain, scene, view_layer, type, name, obdata, true);
const short *materials_len_p = BKE_id_material_len_p(obdata);
if (materials_len_p && *materials_len_p > 0) {
BKE_object_materials_test(bmain, ob, static_cast<ID *>(ob->data));
}
}
else {
- ob = BKE_object_add(bmain, view_layer, type, name);
+ ob = BKE_object_add(bmain, scene, view_layer, type, name);
}
- Base *ob_base_act = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *ob_base_act = BKE_view_layer_active_base_get(view_layer);
/* While not getting a valid base is not a good thing, it can happen in convoluted corner cases,
* better not crash on it in releases. */
BLI_assert(ob_base_act != nullptr);
@@ -657,8 +659,7 @@ Object *ED_object_add_type_with_obdata(bContext *C,
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
- /* TODO(sergey): Use proper flag for tagging here. */
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
ED_outliner_select_sync_from_object_tag(C);
@@ -991,7 +992,8 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op)
}
bool newob = false;
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit == nullptr || obedit->type != OB_MBALL) {
obedit = ED_object_add_type(C, OB_MBALL, nullptr, loc, rot, true, local_view_bits);
newob = true;
@@ -1100,7 +1102,8 @@ static int object_armature_add_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
bool newob = false;
@@ -1335,21 +1338,21 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op)
const char *ob_name = nullptr;
switch (type) {
case GP_EMPTY: {
- ob_name = "GPencil";
+ ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "GPencil");
break;
}
case GP_MONKEY: {
- ob_name = "Suzanne";
+ ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Suzanne");
break;
}
case GP_STROKE: {
- ob_name = "Stroke";
+ ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "Stroke");
break;
}
case GP_LRT_OBJECT:
case GP_LRT_SCENE:
case GP_LRT_COLLECTION: {
- ob_name = "Line Art";
+ ob_name = CTX_DATA_(BLT_I18NCONTEXT_ID_GPENCIL, "LineArt");
break;
}
default: {
@@ -2071,16 +2074,17 @@ static int object_curves_empty_hair_add_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ushort local_view_bits;
- blender::float3 loc, rot;
if (!ED_object_add_generic_get_opts(
- C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr)) {
+ C, op, 'Z', nullptr, nullptr, nullptr, nullptr, &local_view_bits, nullptr)) {
return OPERATOR_CANCELLED;
}
Object *surface_ob = CTX_data_active_object(C);
BLI_assert(surface_ob != nullptr);
- Object *curves_ob = ED_object_add_type(C, OB_CURVES, nullptr, loc, rot, false, local_view_bits);
+ Object *curves_ob = ED_object_add_type(
+ C, OB_CURVES, nullptr, nullptr, nullptr, false, local_view_bits);
+ BKE_object_apply_mat4(curves_ob, surface_ob->obmat, false, false);
/* Set surface object. */
Curves *curves_id = static_cast<Curves *>(curves_ob->data);
@@ -2534,6 +2538,7 @@ static void make_object_duplilist_real(bContext *C,
}
BKE_collection_object_add_from(bmain, scene, base->object, ob_dst);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base_dst = BKE_view_layer_base_find(view_layer, ob_dst);
BLI_assert(base_dst != nullptr);
@@ -2770,25 +2775,6 @@ static const EnumPropertyItem convert_target_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
-static void object_data_convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob)
-{
- if (ob->runtime.curve_cache == nullptr) {
- /* Force creation. This is normally not needed but on operator
- * redo we might end up with an object which isn't evaluated yet.
- * Also happens in case we are working on a copy of the object
- * (all its caches have been nuked then).
- */
- if (ELEM(ob->type, OB_SURF, OB_CURVES_LEGACY, OB_FONT)) {
- /* We need 'for render' ON here, to enable computing bevel #DispList if needed.
- * Also makes sense anyway, we would not want e.g. to lose hidden parts etc. */
- BKE_displist_make_curveTypes(depsgraph, scene, ob, true);
- }
- else if (ob->type == OB_MBALL) {
- BKE_displist_make_mball(depsgraph, scene, ob);
- }
- }
-}
-
static void object_data_convert_curve_to_mesh(Main *bmain, Depsgraph *depsgraph, Object *ob)
{
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
@@ -2850,6 +2836,7 @@ static Base *duplibase_for_convert(
DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
BKE_collection_object_add_from(bmain, scene, ob, obn);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *basen = BKE_view_layer_base_find(view_layer, obn);
ED_object_base_select(basen, BA_SELECT);
ED_object_base_select(base, BA_DESELECT);
@@ -2907,7 +2894,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
const bool use_faces = RNA_boolean_get(op->ptr, "faces");
const float offset = RNA_float_get(op->ptr, "offset");
- int a, mballConverted = 0;
+ int mballConverted = 0;
bool gpencilConverted = false;
bool gpencilCurveConverted = false;
@@ -3170,14 +3157,14 @@ static int object_convert_exec(bContext *C, wmOperator *op)
BKE_mesh_edges_set_draw_render(me_eval);
BKE_object_material_from_eval_data(bmain, newob, &me_eval->id);
Mesh *new_mesh = (Mesh *)newob->data;
- BKE_mesh_nomain_to_mesh(me_eval, new_mesh, newob, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(me_eval, new_mesh, newob);
if (do_merge_customdata) {
BKE_mesh_merge_customdata_for_apply_modifier(new_mesh);
}
/* Anonymous attributes shouldn't be available on the applied geometry. */
- blender::bke::mesh_attributes_for_write(*new_mesh).remove_anonymous();
+ new_mesh->attributes_for_write().remove_anonymous();
BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */
}
@@ -3255,7 +3242,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* No assumption should be made that the resulting objects is a mesh, as conversion can
* fail. */
object_data_convert_curve_to_mesh(bmain, depsgraph, newob);
- /* meshes doesn't use displist */
+ /* Meshes doesn't use the "curve cache". */
BKE_object_free_curve_cache(newob);
}
else if (target == OB_GPENCIL) {
@@ -3290,7 +3277,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* No assumption should be made that the resulting objects is a mesh, as conversion can
* fail. */
object_data_convert_curve_to_mesh(bmain, depsgraph, newob);
- /* meshes doesn't use displist */
+ /* Meshes don't use the "curve cache". */
BKE_object_free_curve_cache(newob);
}
else if (target == OB_GPENCIL) {
@@ -3320,7 +3307,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
baseob = BKE_mball_basis_find(scene, ob);
if (ob != baseob) {
- /* if motherball is converting it would be marked as done later */
+ /* If mother-ball is converting it would be marked as done later. */
ob->flag |= OB_DONE;
}
@@ -3331,21 +3318,13 @@ static int object_convert_exec(bContext *C, wmOperator *op)
MetaBall *mb = static_cast<MetaBall *>(newob->data);
id_us_min(&mb->id);
- newob->data = BKE_mesh_add(bmain, "Mesh");
- newob->type = OB_MESH;
-
- Mesh *me = static_cast<Mesh *>(newob->data);
- me->totcol = mb->totcol;
- if (newob->totcol) {
- me->mat = static_cast<Material **>(MEM_dupallocN(mb->mat));
- for (a = 0; a < newob->totcol; a++) {
- id_us_plus((ID *)me->mat[a]);
- }
- }
+ /* Find the evaluated mesh of the basis metaball object. */
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, baseob);
+ Mesh *mesh = BKE_mesh_new_from_object_to_bmain(bmain, depsgraph, object_eval, true);
- object_data_convert_ensure_curve_cache(depsgraph, scene, baseob);
- BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp,
- static_cast<Mesh *>(newob->data));
+ id_us_plus(&mesh->id);
+ newob->data = mesh;
+ newob->type = OB_MESH;
if (obact->type == OB_MBALL) {
basact = basen;
@@ -3394,7 +3373,7 @@ static int object_convert_exec(bContext *C, wmOperator *op)
/* If the original object is active then make this object active */
if (basen) {
if (ob == obact) {
- /* store new active base to update BASACT */
+ /* Store new active base to update view layer. */
basact = basen;
}
@@ -3468,11 +3447,15 @@ static int object_convert_exec(bContext *C, wmOperator *op)
if (basact) {
/* active base was changed */
ED_object_base_activate(C, basact);
- BASACT(view_layer) = basact;
+ view_layer->basact = basact;
}
- else if (BASACT(view_layer)->object->flag & OB_DONE) {
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, BASACT(view_layer)->object);
- WM_event_add_notifier(C, NC_OBJECT | ND_DATA, BASACT(view_layer)->object);
+ else {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *object = BKE_view_layer_active_object_get(view_layer);
+ if (object->flag & OB_DONE) {
+ WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DATA, object);
+ }
}
DEG_relations_tag_update(bmain);
@@ -3527,11 +3510,12 @@ void OBJECT_OT_convert(wmOperatorType *ot)
/* properties */
ot->prop = RNA_def_enum(
ot->srna, "target", convert_target_items, OB_MESH, "Target", "Type of object to convert to");
- RNA_def_boolean(ot->srna,
- "keep_original",
- false,
- "Keep Original",
- "Keep original objects instead of replacing them");
+ prop = RNA_def_boolean(ot->srna,
+ "keep_original",
+ false,
+ "Keep Original",
+ "Keep original objects instead of replacing them");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_OBJECT);
RNA_def_boolean(
ot->srna,
@@ -3599,8 +3583,9 @@ static Base *object_add_duplicate_internal(Main *bmain,
}
DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ BKE_view_layer_synced_ensure(scene, view_layer);
base = BKE_view_layer_base_find(view_layer, ob);
- if ((base != nullptr) && (base->flag & BASE_VISIBLE_DEPSGRAPH)) {
+ if ((base != nullptr) && (base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT)) {
BKE_collection_object_add_from(bmain, scene, ob, obn);
}
else {
@@ -3608,6 +3593,7 @@ static Base *object_add_duplicate_internal(Main *bmain,
BKE_collection_object_add(bmain, layer_collection->collection, obn);
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
basen = BKE_view_layer_base_find(view_layer, obn);
if (base != nullptr && basen != nullptr) {
basen->local_view_bits = base->local_view_bits;
@@ -3691,7 +3677,7 @@ static int duplicate_exec(bContext *C, wmOperator *op)
Object *ob_new_active = nullptr;
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
- Object *ob_new = NULL;
+ Object *ob_new = nullptr;
object_add_duplicate_internal(bmain,
scene,
view_layer,
@@ -3709,23 +3695,26 @@ static int duplicate_exec(bContext *C, wmOperator *op)
ED_object_base_select(base, BA_DESELECT);
/* new object will become active */
- if (BASACT(view_layer) == base) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (BKE_view_layer_active_base_get(view_layer) == base) {
ob_new_active = ob_new;
}
}
CTX_DATA_END;
+ BKE_layer_collection_resync_allow();
if (source_bases_new_objects.is_empty()) {
return OPERATOR_CANCELLED;
}
+
/* Sync the collection now, after everything is duplicated. */
- BKE_layer_collection_resync_allow();
BKE_main_collection_sync(bmain);
/* After sync we can get to the new Base data, process it here. */
for (const auto &item : source_bases_new_objects) {
Object *ob_new = item.second;
Base *base_source = item.first;
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base_new = BKE_view_layer_base_find(view_layer, ob_new);
if (base_new == nullptr) {
continue;
@@ -3837,7 +3826,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, nullptr, SEL_DESELECT);
+ ED_object_base_deselect_all(scene, view_layer, nullptr, SEL_DESELECT);
ED_object_base_select(basen, BA_SELECT);
ED_object_base_activate(C, basen);
@@ -3914,13 +3903,15 @@ void OBJECT_OT_add_named(wmOperatorType *ot)
static int object_transform_to_mouse_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = reinterpret_cast<Object *>(
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_OB));
if (!ob) {
- ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ob = BKE_view_layer_active_object_get(view_layer);
}
if (ob == nullptr) {
@@ -3992,7 +3983,7 @@ void OBJECT_OT_transform_to_mouse(wmOperatorType *ot)
/* api callbacks */
ot->invoke = object_add_drop_xy_generic_invoke;
ot->exec = object_transform_to_mouse_exec;
- ot->poll = ED_operator_objectmode;
+ ot->poll = ED_operator_objectmode_poll_msg;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c
index effbde41c38..8d505bbca3e 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -155,15 +155,16 @@ static bool multiresbake_check(bContext *C, wmOperator *op)
break;
}
- if (!me->mloopuv) {
+ if (!CustomData_has_layer(&me->ldata, CD_MLOOPUV)) {
BKE_report(op->reports, RPT_ERROR, "Mesh should be unwrapped before multires data baking");
ok = false;
}
else {
+ const int *material_indices = BKE_mesh_material_indices(me);
a = me->totpoly;
while (ok && a--) {
- Image *ima = bake_object_image_get(ob, me->mpoly[a].mat_nr);
+ Image *ima = bake_object_image_get(ob, material_indices ? material_indices[a] : 0);
if (!ima) {
BKE_report(
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 58daf753679..bdaa3523402 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -24,6 +24,7 @@
#include "BKE_attribute.h"
#include "BKE_callbacks.h"
#include "BKE_context.h"
+#include "BKE_editmesh.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_image_format.h"
@@ -324,7 +325,7 @@ static bool write_external_bake_pixels(const char *filepath,
const int height,
const int margin,
const int margin_type,
- ImageFormatData *im_format,
+ ImageFormatData const *im_format,
const bool is_noncolor,
Mesh const *mesh_eval,
char const *uv_layer,
@@ -418,11 +419,13 @@ static bool is_noncolor_pass(eScenePassType pass_type)
}
/* if all is good tag image and return true */
-static bool bake_object_check(ViewLayer *view_layer,
+static bool bake_object_check(const Scene *scene,
+ ViewLayer *view_layer,
Object *ob,
const eBakeTarget target,
ReportList *reports)
{
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base == NULL) {
@@ -590,6 +593,7 @@ static bool bake_pass_filter_check(eScenePassType pass_type,
/* before even getting in the bake function we check for some basic errors */
static bool bake_objects_check(Main *bmain,
+ const Scene *scene,
ViewLayer *view_layer,
Object *ob,
ListBase *selected_objects,
@@ -605,7 +609,7 @@ static bool bake_objects_check(Main *bmain,
if (is_selected_to_active) {
int tot_objects = 0;
- if (!bake_object_check(view_layer, ob, target, reports)) {
+ if (!bake_object_check(scene, view_layer, ob, target, reports)) {
return false;
}
@@ -639,7 +643,7 @@ static bool bake_objects_check(Main *bmain,
}
for (link = selected_objects->first; link; link = link->next) {
- if (!bake_object_check(view_layer, link->ptr.data, target, reports)) {
+ if (!bake_object_check(scene, view_layer, link->ptr.data, target, reports)) {
return false;
}
}
@@ -933,7 +937,10 @@ static bool bake_targets_output_external(const BakeAPIRender *bkr,
/* Vertex Color Bake Targets */
-static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, ReportList *reports)
+static bool bake_targets_init_vertex_colors(Main *bmain,
+ BakeTargets *targets,
+ Object *ob,
+ ReportList *reports)
{
if (ob->type != OB_MESH) {
BKE_report(reports, RPT_ERROR, "Color attribute baking is only supported for mesh objects");
@@ -946,6 +953,9 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re
return false;
}
+ /* Ensure mesh and editmesh topology are in sync. */
+ ED_object_editmode_load(bmain, ob);
+
targets->images = MEM_callocN(sizeof(BakeImage), "BakeTargets.images");
targets->images_num = 1;
@@ -964,7 +974,8 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re
return true;
}
-static int find_original_loop(const Mesh *me_orig,
+static int find_original_loop(const MPoly *orig_polys,
+ const MLoop *orig_loops,
const int *vert_origindex,
const int *poly_origindex,
const int poly_eval,
@@ -980,8 +991,8 @@ static int find_original_loop(const Mesh *me_orig,
}
/* 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;
+ const MPoly *mpoly_orig = orig_polys + poly_orig;
+ const MLoop *mloop_orig = orig_loops + 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;
@@ -1018,23 +1029,31 @@ static void bake_targets_populate_pixels_color_attributes(BakeTargets *targets,
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_eval->mloop, me_eval->mpoly, me_eval->mvert, me_eval->totloop, me_eval->totpoly, looptri);
+ const MLoop *loops = BKE_mesh_loops(me_eval);
+ BKE_mesh_recalc_looptri(loops,
+ BKE_mesh_polys(me_eval),
+ BKE_mesh_verts(me_eval),
+ 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);
+ const MPoly *orig_polys = BKE_mesh_polys(me);
+ const MLoop *orig_loops = BKE_mesh_loops(me);
for (int i = 0; i < tottri; i++) {
const MLoopTri *lt = &looptri[i];
for (int j = 0; j < 3; j++) {
unsigned int l = lt->tri[j];
- unsigned int v = me_eval->mloop[l].v;
+ unsigned int v = loops[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);
+ l = find_original_loop(
+ orig_polys, orig_loops, vert_origindex, poly_origindex, lt->poly, v);
if (l == ORIGINDEX_NONE || l >= me->totloop) {
continue;
}
@@ -1109,6 +1128,7 @@ static void convert_float_color_to_byte_color(const MPropCol *float_colors,
static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob)
{
Mesh *me = ob->data;
+ BMEditMesh *em = me->edit_mesh;
CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&me->id);
BLI_assert(active_color_layer != NULL);
const eAttrDomain domain = BKE_id_attribute_domain(&me->id, active_color_layer);
@@ -1121,15 +1141,13 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob)
const int totvert = me->totvert;
const int totloop = me->totloop;
- MPropCol *mcol = active_color_layer->type == CD_PROP_COLOR ?
- active_color_layer->data :
- MEM_malloc_arrayN(totvert, sizeof(MPropCol), __func__);
+ MPropCol *mcol = MEM_malloc_arrayN(totvert, sizeof(MPropCol), __func__);
/* Accumulate float vertex colors in scene linear color space. */
int *num_loops_for_vertex = MEM_callocN(sizeof(int) * me->totvert, "num_loops_for_vertex");
memset(mcol, 0, sizeof(MPropCol) * me->totvert);
- MLoop *mloop = me->mloop;
+ const MLoop *mloop = BKE_mesh_loops(me);
for (int i = 0; i < totloop; i++, mloop++) {
const int v = mloop->v;
bake_result_add_to_rgba(mcol[v].color, &result[i * channels_num], channels_num);
@@ -1143,24 +1161,75 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob)
}
}
- if (mcol != active_color_layer->data) {
- convert_float_color_to_byte_color(mcol, totvert, is_noncolor, active_color_layer->data);
- MEM_freeN(mcol);
+ if (em) {
+ /* Copy to bmesh. */
+ const int active_color_offset = CustomData_get_offset_named(
+ &em->bm->vdata, active_color_layer->type, active_color_layer->name);
+ BMVert *v;
+ BMIter viter;
+ int i = 0;
+ BM_ITER_MESH (v, &viter, em->bm, BM_VERTS_OF_MESH) {
+ void *data = BM_ELEM_CD_GET_VOID_P(v, active_color_offset);
+ if (active_color_layer->type == CD_PROP_COLOR) {
+ memcpy(data, &mcol[i], sizeof(MPropCol));
+ }
+ else {
+ convert_float_color_to_byte_color(&mcol[i], 1, is_noncolor, data);
+ }
+ i++;
+ }
+ }
+ else {
+ /* Copy to mesh. */
+ if (active_color_layer->type == CD_PROP_COLOR) {
+ memcpy(active_color_layer->data, mcol, sizeof(MPropCol) * me->totvert);
+ }
+ else {
+ convert_float_color_to_byte_color(mcol, totvert, is_noncolor, active_color_layer->data);
+ }
}
+ MEM_freeN(mcol);
+
MEM_SAFE_FREE(num_loops_for_vertex);
}
else if (domain == ATTR_DOMAIN_CORNER) {
- switch (active_color_layer->type) {
- case CD_PROP_COLOR: {
+ if (em) {
+ /* Copy to bmesh. */
+ const int active_color_offset = CustomData_get_offset_named(
+ &em->bm->ldata, active_color_layer->type, active_color_layer->name);
+ BMFace *f;
+ BMIter fiter;
+ int i = 0;
+ BM_ITER_MESH (f, &fiter, em->bm, BM_FACES_OF_MESH) {
+ BMLoop *l;
+ BMIter liter;
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ MPropCol color;
+ zero_v4(color.color);
+ bake_result_add_to_rgba(color.color, &result[i * channels_num], channels_num);
+ i++;
+
+ void *data = BM_ELEM_CD_GET_VOID_P(l, active_color_offset);
+ if (active_color_layer->type == CD_PROP_COLOR) {
+ memcpy(data, &color, sizeof(MPropCol));
+ }
+ else {
+ convert_float_color_to_byte_color(&color, 1, is_noncolor, data);
+ }
+ }
+ }
+ }
+ else {
+ /* Copy to mesh. */
+ if (active_color_layer->type == CD_PROP_COLOR) {
MPropCol *colors = active_color_layer->data;
for (int i = 0; i < me->totloop; i++) {
zero_v4(colors[i].color);
bake_result_add_to_rgba(colors[i].color, &result[i * channels_num], channels_num);
}
- break;
}
- case CD_PROP_BYTE_COLOR: {
+ else {
MLoopCol *colors = active_color_layer->data;
for (int i = 0; i < me->totloop; i++) {
MPropCol color;
@@ -1168,10 +1237,7 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob)
bake_result_add_to_rgba(color.color, &result[i * channels_num], channels_num);
convert_float_color_to_byte_color(&color, 1, is_noncolor, &colors[i]);
}
- break;
}
- default:
- BLI_assert_unreachable();
}
}
@@ -1201,7 +1267,7 @@ static bool bake_targets_init(const BakeAPIRender *bkr,
}
}
else if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) {
- if (!bake_targets_init_vertex_colors(targets, ob, reports)) {
+ if (!bake_targets_init_vertex_colors(bkr->main, targets, ob, reports)) {
return false;
}
}
@@ -1346,7 +1412,8 @@ static int bake(const BakeAPIRender *bkr,
else {
ob_cage_eval = DEG_get_evaluated_object(depsgraph, ob_cage);
ob_cage_eval->visibility_flag |= OB_HIDE_RENDER;
- ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
+ ob_cage_eval->base_flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT |
+ BASE_ENABLED_RENDER);
}
}
}
@@ -1446,7 +1513,8 @@ static int bake(const BakeAPIRender *bkr,
highpoly[i].ob = ob_iter;
highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter);
highpoly[i].ob_eval->visibility_flag &= ~OB_HIDE_RENDER;
- highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
+ highpoly[i].ob_eval->base_flag |= (BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT |
+ BASE_ENABLED_RENDER);
highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false, false);
/* Low-poly to high-poly transformation matrix. */
@@ -1462,10 +1530,11 @@ static int bake(const BakeAPIRender *bkr,
if (ob_cage != NULL) {
ob_cage_eval->visibility_flag |= OB_HIDE_RENDER;
- ob_cage_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
+ ob_cage_eval->base_flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT |
+ BASE_ENABLED_RENDER);
}
ob_low_eval->visibility_flag |= OB_HIDE_RENDER;
- ob_low_eval->base_flag &= ~(BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER);
+ ob_low_eval->base_flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_ENABLED_RENDER);
/* populate the pixel arrays with the corresponding face data for each high poly object */
pixel_array_high = MEM_mallocN(sizeof(BakePixel) * targets.pixels_num,
@@ -1746,6 +1815,7 @@ static int bake_exec(bContext *C, wmOperator *op)
}
if (!bake_objects_check(bkr.main,
+ bkr.scene,
bkr.view_layer,
bkr.ob,
&bkr.selected_objects,
@@ -1799,6 +1869,7 @@ static void bake_startjob(void *bkv, short *UNUSED(stop), short *do_update, floa
}
if (!bake_objects_check(bkr->main,
+ bkr->scene,
bkr->view_layer,
bkr->ob,
&bkr->selected_objects,
diff --git a/source/blender/editors/object/object_collection.c b/source/blender/editors/object/object_collection.c
index 39951c2ab6e..53e1a75cba0 100644
--- a/source/blender/editors/object/object_collection.c
+++ b/source/blender/editors/object/object_collection.c
@@ -16,6 +16,7 @@
#include "BKE_collection.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_object.h"
@@ -202,7 +203,8 @@ static int objects_remove_active_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
int single_collection_index = RNA_enum_get(op->ptr, "collection");
Collection *single_collection = collection_object_active_find_index(
bmain, scene, ob, single_collection_index);
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index bf3b71178e8..3c3b66b4b1d 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -31,6 +31,7 @@
#include "BKE_constraint.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_report.h"
@@ -50,6 +51,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "ED_keyframing.h"
@@ -295,10 +297,9 @@ static void test_constraint(
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = con->data;
- /* bad: we need a separate set of checks here as poletarget is
- * optional... otherwise poletarget must exist too or else
- * the constraint is deemed invalid
- */
+ /* Bad: we need a separate set of checks here as pole-target is optional...
+ * otherwise pole-target must exist too or else the constraint is deemed invalid. */
+
/* default IK check ... */
if (BKE_object_exists_check(bmain, data->tar) == 0) {
data->tar = NULL;
@@ -2312,12 +2313,14 @@ static bool get_new_constraint_target(
/* if still not found, add a new empty to act as a target (if allowed) */
if ((found == false) && (add)) {
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
Object *obt;
/* add new target object */
- obt = BKE_object_add(bmain, view_layer, OB_EMPTY, NULL);
+ obt = BKE_object_add(bmain, scene, view_layer, OB_EMPTY, NULL);
/* transform cent to global coords for loc */
if (pchanact) {
@@ -2335,7 +2338,7 @@ static bool get_new_constraint_target(
}
/* restore, BKE_object_add sets active */
- BASACT(view_layer) = base;
+ view_layer->basact = base;
ED_object_base_select(base, BA_SELECT);
/* make our new target the new object */
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 4837b538bf6..78b059d5514 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -45,7 +45,7 @@
* Note some are 'fake' ones, i.e. they are not hold by real CDLayers. */
/* Not shared with modifier, since we use a usual enum here, not a multi-choice one. */
static const EnumPropertyItem DT_layer_items[] = {
- RNA_ENUM_ITEM_HEADING("Vertex Data", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Vertex Data"), NULL),
{DT_TYPE_MDEFORMVERT,
"VGROUP_WEIGHTS",
0,
@@ -61,7 +61,7 @@ static const EnumPropertyItem DT_layer_items[] = {
#endif
{DT_TYPE_BWEIGHT_VERT, "BEVEL_WEIGHT_VERT", 0, "Bevel Weight", "Transfer bevel weights"},
- RNA_ENUM_ITEM_HEADING("Edge Data", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Edge Data"), NULL),
{DT_TYPE_SHARP_EDGE, "SHARP_EDGE", 0, "Sharp", "Transfer sharp mark"},
{DT_TYPE_SEAM, "SEAM", 0, "UV Seam", "Transfer UV seam mark"},
{DT_TYPE_CREASE, "CREASE", 0, "Subdivision Crease", "Transfer crease values"},
@@ -72,12 +72,12 @@ static const EnumPropertyItem DT_layer_items[] = {
"Freestyle Mark",
"Transfer Freestyle edge mark"},
- RNA_ENUM_ITEM_HEADING("Face Corner Data", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Face Corner Data"), NULL),
{DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"},
{DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP, "VCOL", 0, "Colors", "Color Attributes"},
{DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"},
- RNA_ENUM_ITEM_HEADING("Face Data", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Face Data"), NULL),
{DT_TYPE_SHARP_FACE, "SMOOTH", 0, "Smooth", "Transfer flat/smooth mark"},
{DT_TYPE_FREESTYLE_FACE,
"FREESTYLE_FACE",
diff --git a/source/blender/editors/object/object_data_transform.c b/source/blender/editors/object/object_data_transform.c
index 63513eac965..cb66010c497 100644
--- a/source/blender/editors/object/object_data_transform.c
+++ b/source/blender/editors/object/object_data_transform.c
@@ -7,7 +7,7 @@
* Use to transform object origins only.
*
* This is a small API to store & apply transformations to object data,
- * where a transformation matrix can be continually applied ontop of the original values
+ * where a transformation matrix can be continually applied on top of the original values
* so we don't lose precision over time.
*/
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index e65b5a61299..c3482b13db6 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -140,8 +140,10 @@ Object **ED_object_array_in_mode_or_selected(bContext *C,
uint *r_objects_len)
{
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob_active = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob_active = BKE_view_layer_active_object_get(view_layer);
ID *id_pin = NULL;
const bool use_objects_in_mode = (ob_active != NULL) &&
(ob_active->mode & (OB_MODE_EDIT | OB_MODE_POSE));
@@ -194,13 +196,13 @@ Object **ED_object_array_in_mode_or_selected(bContext *C,
/* When in a mode that supports multiple active objects, use "objects in mode"
* instead of the object's selection. */
if (use_objects_in_mode) {
- objects = BKE_view_layer_array_from_objects_in_mode(view_layer,
- v3d,
- r_objects_len,
- {.object_mode = ob_active->mode,
- .no_dup_data = true,
- .filter_fn = filter_fn,
- .filter_userdata = filter_user_data});
+ struct ObjectsInModeParams params = {0};
+ params.object_mode = ob_active->mode;
+ params.no_dup_data = true;
+ params.filter_fn = filter_fn;
+ params.filter_userdata = filter_user_data;
+ objects = BKE_view_layer_array_from_objects_in_mode_params(
+ scene, view_layer, v3d, r_objects_len, &params);
}
else {
objects = BKE_view_layer_array_selected_objects(
@@ -234,7 +236,8 @@ static int object_hide_view_clear_exec(bContext *C, wmOperator *op)
const bool select = RNA_boolean_get(op->ptr, "select");
bool changed = false;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->flag & BASE_HIDDEN) {
base->flag &= ~BASE_HIDDEN;
changed = true;
@@ -252,7 +255,7 @@ static int object_hide_view_clear_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_event_add_notifier(C, NC_SCENE | ND_OB_VISIBLE, scene);
@@ -286,8 +289,9 @@ static int object_hide_view_set_exec(bContext *C, wmOperator *op)
bool changed = false;
/* Hide selected or unselected objects. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
- if (!(base->flag & BASE_VISIBLE_VIEWLAYER)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
+ if (!(base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT)) {
continue;
}
@@ -310,7 +314,7 @@ static int object_hide_view_set_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_event_add_notifier(C, NC_SCENE | ND_OB_VISIBLE, scene);
@@ -362,10 +366,10 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op)
}
if (toggle) {
lc->local_collections_bits ^= v3d->local_collections_uuid;
- BKE_layer_collection_local_sync(view_layer, v3d);
+ BKE_layer_collection_local_sync(scene, view_layer, v3d);
}
else {
- BKE_layer_collection_isolate_local(view_layer, v3d, lc, extend);
+ BKE_layer_collection_isolate_local(scene, view_layer, v3d, lc, extend);
}
}
else {
@@ -381,6 +385,7 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op)
void ED_collection_hide_menu_draw(const bContext *C, uiLayout *layout)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
LayerCollection *lc_scene = view_layer->layer_collections.first;
@@ -399,7 +404,7 @@ void ED_collection_hide_menu_draw(const bContext *C, uiLayout *layout)
}
int icon = ICON_NONE;
- if (BKE_layer_collection_has_selected_objects(view_layer, lc)) {
+ if (BKE_layer_collection_has_selected_objects(scene, view_layer, lc)) {
icon = ICON_LAYER_ACTIVE;
}
else if (lc->runtime_flag & LAYER_COLLECTION_HAS_OBJECTS) {
@@ -701,14 +706,16 @@ bool ED_object_editmode_free_ex(Main *bmain, Object *obedit)
bool ED_object_editmode_exit_multi_ex(Main *bmain, Scene *scene, ViewLayer *view_layer, int flag)
{
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit == NULL) {
return false;
}
bool changed = false;
const short obedit_type = obedit->type;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
if ((ob->type == obedit_type) && (ob->mode & OB_MODE_EDIT)) {
changed |= ED_object_editmode_exit_ex(bmain, scene, base->object, flag);
@@ -841,7 +848,8 @@ static int editmode_toggle_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
const int mode_flag = OB_MODE_EDIT;
const bool is_mode_set = (obact->mode & mode_flag) != 0;
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
@@ -867,7 +875,7 @@ static int editmode_toggle_exec(bContext *C, wmOperator *op)
ED_object_editmode_exit_ex(bmain, scene, obact, EM_FREEDATA);
if ((obact->mode & mode_flag) == 0) {
- FOREACH_OBJECT_BEGIN (view_layer, ob) {
+ FOREACH_OBJECT_BEGIN (scene, view_layer, ob) {
if ((ob != obact) && (ob->type == obact->type)) {
ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
}
@@ -889,13 +897,13 @@ static bool editmode_toggle_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- /* covers proxies too */
+ /* Covers liboverrides too. */
if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED(ob->data) || ID_IS_OVERRIDE_LIBRARY(ob) ||
ID_IS_OVERRIDE_LIBRARY(ob->data)) {
return false;
}
- /* if hidden but in edit mode, we still display */
+ /* If hidden but in edit mode, we still display. */
if ((ob->visibility_flag & OB_HIDE_VIEWPORT) && !(ob->mode & OB_MODE_EDIT)) {
return false;
}
@@ -953,7 +961,8 @@ static int posemode_exec(bContext *C, wmOperator *op)
}
{
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obact == obedit) {
ED_object_editmode_exit_ex(bmain, scene, obedit, EM_FREEDATA);
is_mode_set = false;
@@ -963,7 +972,7 @@ static int posemode_exec(bContext *C, wmOperator *op)
if (is_mode_set) {
bool ok = ED_object_posemode_exit(C, obact);
if (ok) {
- FOREACH_OBJECT_BEGIN (view_layer, ob) {
+ FOREACH_OBJECT_BEGIN (scene, view_layer, ob) {
if ((ob != obact) && (ob->type == OB_ARMATURE) && (ob->mode & mode_flag)) {
ED_object_posemode_exit_ex(bmain, ob);
}
@@ -1469,8 +1478,6 @@ void OBJECT_OT_paths_clear(wmOperatorType *ot)
static int shade_smooth_exec(bContext *C, wmOperator *op)
{
const bool use_smooth = STREQ(op->idname, "OBJECT_OT_shade_smooth");
- const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth");
- const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle");
bool changed_multi = false;
bool has_linked_data = false;
@@ -1479,8 +1486,10 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
/* For modes that only use an active object, don't handle the whole selection. */
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && ((obact->mode & OB_MODE_ALL_PAINT))) {
ctx_ob_single_active.ptr.data = obact;
BLI_addtail(&ctx_objects, &ctx_ob_single_active);
@@ -1518,7 +1527,11 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
bool changed = false;
if (ob->type == OB_MESH) {
BKE_mesh_smooth_flag_set(ob->data, use_smooth);
- BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle);
+ if (use_smooth) {
+ const bool use_auto_smooth = RNA_boolean_get(op->ptr, "use_auto_smooth");
+ const float auto_smooth_angle = RNA_float_get(op->ptr, "auto_smooth_angle");
+ BKE_mesh_auto_smooth_flag_set(ob->data, use_auto_smooth, auto_smooth_angle);
+ }
BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
changed = true;
}
@@ -1548,8 +1561,10 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
static bool shade_poll(bContext *C)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact != NULL) {
/* Doesn't handle edit-data, sculpt dynamic-topology, or their undo systems. */
if (obact->mode & (OB_MODE_EDIT | OB_MODE_SCULPT) || obact->data == NULL ||
diff --git a/source/blender/editors/object/object_facemap_ops.c b/source/blender/editors/object/object_facemap_ops.c
index dddf5e40e87..4364375a4e3 100644
--- a/source/blender/editors/object/object_facemap_ops.c
+++ b/source/blender/editors/object/object_facemap_ops.c
@@ -53,7 +53,7 @@ void ED_object_facemap_face_add(Object *ob, bFaceMap *fmap, int facenum)
/* if there's is no facemap layer then create one */
if ((facemap = CustomData_get_layer(&me->pdata, CD_FACEMAP)) == NULL) {
- facemap = CustomData_add_layer(&me->pdata, CD_FACEMAP, CD_DEFAULT, NULL, me->totpoly);
+ facemap = CustomData_add_layer(&me->pdata, CD_FACEMAP, CD_SET_DEFAULT, NULL, me->totpoly);
}
facemap[facenum] = fmap_nr;
diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c
index 573f048e6b6..42ac6d166b4 100644
--- a/source/blender/editors/object/object_gpencil_modifier.c
+++ b/source/blender/editors/object/object_gpencil_modifier.c
@@ -680,8 +680,7 @@ static int gpencil_modifier_move_to_index_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_active_context(C);
GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0);
int index = RNA_int_get(op->ptr, "index");
-
- if (!ED_object_gpencil_modifier_move_to_index(op->reports, ob, md, index)) {
+ if (!(md && ED_object_gpencil_modifier_move_to_index(op->reports, ob, md, index))) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c
index b3f62f3fc0f..27659042f50 100644
--- a/source/blender/editors/object/object_hook.c
+++ b/source/blender/editors/object/object_hook.c
@@ -484,22 +484,22 @@ static bool hook_op_edit_poll(bContext *C)
return false;
}
-static Object *add_hook_object_new(Main *bmain, ViewLayer *view_layer, View3D *v3d, Object *obedit)
+static Object *add_hook_object_new(
+ Main *bmain, Scene *scene, ViewLayer *view_layer, View3D *v3d, Object *obedit)
{
Base *basedit;
Object *ob;
-
- ob = BKE_object_add(bmain, view_layer, OB_EMPTY, NULL);
-
- basedit = BKE_view_layer_base_find(view_layer, obedit);
- BLI_assert(view_layer->basact->object == ob);
-
+ ob = BKE_object_add(bmain, scene, view_layer, OB_EMPTY, NULL);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *basact = BKE_view_layer_active_base_get(view_layer);
+ BLI_assert(basact->object == ob);
if (v3d && v3d->localvd) {
- view_layer->basact->local_view_bits |= v3d->local_view_uuid;
+ basact->local_view_bits |= v3d->local_view_uuid;
}
/* icky, BKE_object_add sets new base as active.
* so set it back to the original edit object */
+ basedit = BKE_view_layer_base_find(view_layer, obedit);
view_layer->basact = basedit;
return ob;
@@ -532,7 +532,7 @@ static int add_hook_object(const bContext *C,
if (mode == OBJECT_ADDHOOK_NEWOB && !ob) {
- ob = add_hook_object_new(bmain, view_layer, v3d, obedit);
+ ob = add_hook_object_new(bmain, scene, view_layer, v3d, obedit);
/* transform cent to global coords for loc */
mul_v3_m4v3(ob->loc, obedit->obmat, cent);
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index b5862d4d957..63f010cd526 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -49,10 +49,14 @@ void OBJECT_OT_vertex_parent_set(struct wmOperatorType *ot);
void OBJECT_OT_track_set(struct wmOperatorType *ot);
void OBJECT_OT_track_clear(struct wmOperatorType *ot);
void OBJECT_OT_make_local(struct wmOperatorType *ot);
-void OBJECT_OT_make_override_library(struct wmOperatorType *ot);
void OBJECT_OT_make_single_user(struct wmOperatorType *ot);
void OBJECT_OT_make_links_scene(struct wmOperatorType *ot);
void OBJECT_OT_make_links_data(struct wmOperatorType *ot);
+
+void OBJECT_OT_make_override_library(struct wmOperatorType *ot);
+void OBJECT_OT_reset_override_library(struct wmOperatorType *ot);
+void OBJECT_OT_clear_override_library(struct wmOperatorType *ot);
+
/**
* Used for drop-box.
* Assigns to object under cursor, only first material slot.
@@ -259,7 +263,7 @@ void CONSTRAINT_OT_objectsolver_set_inverse(struct wmOperatorType *ot);
void CONSTRAINT_OT_objectsolver_clear_inverse(struct wmOperatorType *ot);
void CONSTRAINT_OT_followpath_path_animate(struct wmOperatorType *ot);
-/* object_vgroup.c */
+/* object_vgroup.cc */
void OBJECT_OT_vertex_group_add(struct wmOperatorType *ot);
void OBJECT_OT_vertex_group_remove(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c
index 0055cdf9ea1..6525f2d6027 100644
--- a/source/blender/editors/object/object_modes.c
+++ b/source/blender/editors/object/object_modes.c
@@ -190,8 +190,11 @@ bool ED_object_mode_compat_set(bContext *C, Object *ob, eObjectMode mode, Report
bool ED_object_mode_set_ex(bContext *C, eObjectMode mode, bool use_undo, ReportList *reports)
{
wmWindowManager *wm = CTX_wm_manager(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob == NULL) {
return (mode == OB_MODE_OBJECT);
}
@@ -327,9 +330,11 @@ static void ed_object_posemode_set_for_weight_paint_ex(bContext *C,
const bool is_mode_set)
{
View3D *v3d = CTX_wm_view3d(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
if (ob_arm != NULL) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
const Base *base_arm = BKE_view_layer_base_find(view_layer, ob_arm);
if (base_arm && BASE_VISIBLE(v3d, base_arm)) {
if (is_mode_set) {
@@ -464,8 +469,9 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base
if (ED_object_mode_set_ex(C, OB_MODE_OBJECT, true, op->reports)) {
Object *ob_dst_orig = DEG_get_original_object(ob_dst);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob_dst_orig);
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
BKE_view_layer_base_select_and_set_active(view_layer, base);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc
index 69edd00ae24..f82c0938b51 100644
--- a/source/blender/editors/object/object_modifier.cc
+++ b/source/blender/editors/object/object_modifier.cc
@@ -47,9 +47,11 @@
#include "BKE_gpencil_modifier.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
+#include "BKE_mball.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
@@ -92,6 +94,8 @@
#include "object_intern.h"
+using blender::Span;
+
static void modifier_skin_customdata_delete(struct Object *ob);
/* ------------------------------------------------------------------- */
@@ -111,7 +115,7 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object *
BKE_lattice_modifiers_calc(depsgraph, scene_eval, ob_eval);
}
else if (ob->type == OB_MBALL) {
- BKE_displist_make_mball(depsgraph, scene_eval, ob_eval);
+ BKE_mball_data_update(depsgraph, scene_eval, ob_eval);
}
else if (ELEM(ob->type, OB_CURVES_LEGACY, OB_SURF, OB_FONT)) {
BKE_displist_make_curveTypes(depsgraph, scene_eval, ob_eval, false);
@@ -486,6 +490,9 @@ bool ED_object_modifier_move_to_index(ReportList *reports,
}
}
+ /* NOTE: Dependency graph only uses modifier nodes for visibility updates, and exact order of
+ * modifier nodes in the graph does not matter. */
+
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob);
@@ -518,6 +525,7 @@ void ED_object_modifier_copy_to_object(bContext *C,
bool ED_object_modifier_convert_psys_to_mesh(ReportList *UNUSED(reports),
Main *bmain,
Depsgraph *depsgraph,
+ Scene *scene,
ViewLayer *view_layer,
Object *ob,
ModifierData *md)
@@ -576,18 +584,20 @@ bool ED_object_modifier_convert_psys_to_mesh(ReportList *UNUSED(reports),
}
/* add new mesh */
- Object *obn = BKE_object_add(bmain, view_layer, OB_MESH, nullptr);
+ Object *obn = BKE_object_add(bmain, scene, view_layer, OB_MESH, nullptr);
Mesh *me = static_cast<Mesh *>(obn->data);
me->totvert = verts_num;
me->totedge = edges_num;
- me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, verts_num);
- me->medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_CALLOC, nullptr, edges_num);
- me->mface = (MFace *)CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, nullptr, 0);
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, verts_num);
+ CustomData_add_layer(&me->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, edges_num);
+ CustomData_add_layer(&me->fdata, CD_MFACE, CD_SET_DEFAULT, nullptr, 0);
- MVert *mvert = me->mvert;
- MEdge *medge = me->medge;
+ blender::MutableSpan<MVert> verts = me->verts_for_write();
+ blender::MutableSpan<MEdge> edges = me->edges_for_write();
+ MVert *mvert = verts.data();
+ MEdge *medge = edges.data();
/* copy coordinates */
cache = psys_eval->pathcache;
@@ -754,10 +764,10 @@ static bool modifier_apply_obdata(
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);
+ BKE_mesh_nomain_to_mesh(mesh_applied, me, ob);
/* Anonymous attributes shouldn't be available on the applied geometry. */
- blender::bke::mesh_attributes_for_write(*me).remove_anonymous();
+ me->attributes_for_write().remove_anonymous();
if (md_eval->type == eModifierType_Multires) {
multires_customdata_delete(me);
@@ -818,7 +828,7 @@ static bool modifier_apply_obdata(
/* Create a temporary geometry set and component. */
GeometrySet geometry_set;
geometry_set.get_component_for_write<CurveComponent>().replace(
- &curves, GeometryOwnershipType::Editable);
+ &curves, GeometryOwnershipType::ReadOnly);
ModifierEvalContext mectx = {depsgraph, ob, (ModifierApplyFlag)0};
mti->modifyGeometrySet(md_eval, &mectx, &geometry_set);
@@ -833,14 +843,11 @@ static bool modifier_apply_obdata(
.attributes_for_write()
.remove_anonymous();
- /* If the modifier's output is a different curves data-block, copy the relevant information to
- * the original. */
- if (&curves_eval != &curves) {
- blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move(
- blender::bke::CurvesGeometry::wrap(curves_eval.geometry));
- Main *bmain = DEG_get_bmain(depsgraph);
- BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
- }
+ /* Copy the relevant information to the original. */
+ blender::bke::CurvesGeometry::wrap(curves.geometry) = std::move(
+ blender::bke::CurvesGeometry::wrap(curves_eval.geometry));
+ Main *bmain = DEG_get_bmain(depsgraph);
+ BKE_object_material_from_eval_data(bmain, ob, &curves_eval.id);
}
else {
/* TODO: implement for point clouds and volumes. */
@@ -1226,7 +1233,8 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
/* if cloth/softbody was removed, particle mode could be cleared */
if (mode_orig & OB_MODE_PARTICLE_EDIT) {
if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
- if (ob == OBACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob == BKE_view_layer_active_object_get(view_layer)) {
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, nullptr);
}
}
@@ -1366,7 +1374,7 @@ static int modifier_move_to_index_exec(bContext *C, wmOperator *op)
ModifierData *md = edit_modifier_property_get(op, ob, 0);
int index = RNA_int_get(op->ptr, "index");
- if (!ED_object_modifier_move_to_index(op->reports, ob, md, index)) {
+ if (!(md && ED_object_modifier_move_to_index(op->reports, ob, md, index))) {
return OPERATOR_CANCELLED;
}
@@ -1439,7 +1447,6 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
- const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
const bool do_report = RNA_boolean_get(op->ptr, "report");
const bool do_single_user = RNA_boolean_get(op->ptr, "single_user");
const bool do_merge_customdata = RNA_boolean_get(op->ptr, "merge_customdata");
@@ -1448,6 +1455,8 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
return OPERATOR_CANCELLED;
}
+ const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
+
if (do_single_user && ID_REAL_USERS(ob->data) > 1) {
ED_object_single_obdata_user(bmain, scene, ob);
BKE_main_id_newptr_and_tag_clear(bmain);
@@ -1547,7 +1556,7 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
-/** \name Apply Modifier As Shapekey Operator
+/** \name Apply Modifier As Shape-Key Operator
* \{ */
static bool modifier_apply_as_shapekey_poll(bContext *C)
@@ -1614,12 +1623,13 @@ static int modifier_convert_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);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
if (!md || !ED_object_modifier_convert_psys_to_mesh(
- op->reports, bmain, depsgraph, view_layer, ob, md)) {
+ op->reports, bmain, depsgraph, scene, view_layer, ob, md)) {
return OPERATOR_CANCELLED;
}
@@ -1670,6 +1680,7 @@ static int modifier_copy_exec(bContext *C, wmOperator *op)
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
@@ -2585,8 +2596,8 @@ void OBJECT_OT_skin_radii_equalize(wmOperatorType *ot)
}
static void skin_armature_bone_create(Object *skin_ob,
- MVert *mvert,
- MEdge *medge,
+ const MVert *mvert,
+ const MEdge *medge,
bArmature *arm,
BLI_bitmap *edges_visited,
const MeshElemMap *emap,
@@ -2631,18 +2642,22 @@ static void skin_armature_bone_create(Object *skin_ob,
static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, Object *skin_ob)
{
Mesh *me = static_cast<Mesh *>(skin_ob->data);
+ const Span<MVert> me_verts = me->verts();
+ const Span<MEdge> me_edges = me->edges();
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, skin_ob);
- Mesh *me_eval_deform = mesh_get_eval_deform(depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
- MVert *mvert = me_eval_deform->mvert;
+ const Mesh *me_eval_deform = mesh_get_eval_deform(
+ depsgraph, scene_eval, ob_eval, &CD_MASK_BAREMESH);
+ const Span<MVert> verts_eval = me_eval_deform->verts();
/* add vertex weights to original mesh */
- CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, me->totvert);
+ CustomData_add_layer(&me->vdata, CD_MDEFORMVERT, CD_SET_DEFAULT, nullptr, me->totvert);
+ Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
- Object *arm_ob = BKE_object_add(bmain, view_layer, OB_ARMATURE, nullptr);
+ Object *arm_ob = BKE_object_add(bmain, scene, view_layer, OB_ARMATURE, nullptr);
BKE_object_transform_copy(arm_ob, skin_ob);
bArmature *arm = static_cast<bArmature *>(arm_ob->data);
arm->layer = 1;
@@ -2654,7 +2669,7 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain,
CustomData_get_layer(&me->vdata, CD_MVERT_SKIN));
int *emap_mem;
MeshElemMap *emap;
- BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge);
+ BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me_edges.data(), me->totvert, me->totedge);
BLI_bitmap *edges_visited = BLI_BITMAP_NEW(me->totedge, "edge_visited");
@@ -2670,15 +2685,16 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain,
if (emap[v].count > 1) {
bone = ED_armature_ebone_add(arm, "Bone");
- copy_v3_v3(bone->head, me->mvert[v].co);
- copy_v3_v3(bone->tail, me->mvert[v].co);
+ copy_v3_v3(bone->head, me_verts[v].co);
+ copy_v3_v3(bone->tail, me_verts[v].co);
bone->head[1] = 1.0f;
bone->rad_head = bone->rad_tail = 0.25;
}
if (emap[v].count >= 1) {
- skin_armature_bone_create(skin_ob, mvert, me->medge, arm, edges_visited, emap, bone, v);
+ skin_armature_bone_create(
+ skin_ob, verts_eval.data(), me_edges.data(), arm, edges_visited, emap, bone, v);
}
}
}
@@ -3346,6 +3362,7 @@ void OBJECT_OT_geometry_nodes_input_attribute_toggle(wmOperatorType *ot)
ot->idname = "OBJECT_OT_geometry_nodes_input_attribute_toggle";
ot->exec = geometry_nodes_input_attribute_toggle_exec;
+ ot->poll = ED_operator_object_active;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
@@ -3363,9 +3380,8 @@ static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator *UNUSED(o
{
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
-
ModifierData *md = BKE_object_active_modifier(ob);
- if (md->type != eModifierType_Nodes) {
+ if (!(md && md->type == eModifierType_Nodes)) {
return OPERATOR_CANCELLED;
}
@@ -3385,6 +3401,7 @@ static int geometry_node_tree_copy_assign_exec(bContext *C, wmOperator *UNUSED(o
nmd->node_group = new_tree;
id_us_min(&tree->id);
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
@@ -3397,6 +3414,7 @@ void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot)
ot->idname = "OBJECT_OT_geometry_node_tree_copy_assign";
ot->exec = geometry_node_tree_copy_assign_exec;
+ ot->poll = ED_operator_object_active;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 8a0d380ff2f..24a4556b075 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -58,11 +58,14 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_track_set);
WM_operatortype_append(OBJECT_OT_track_clear);
WM_operatortype_append(OBJECT_OT_make_local);
- WM_operatortype_append(OBJECT_OT_make_override_library);
WM_operatortype_append(OBJECT_OT_make_single_user);
WM_operatortype_append(OBJECT_OT_make_links_scene);
WM_operatortype_append(OBJECT_OT_make_links_data);
+ WM_operatortype_append(OBJECT_OT_make_override_library);
+ WM_operatortype_append(OBJECT_OT_reset_override_library);
+ WM_operatortype_append(OBJECT_OT_clear_override_library);
+
WM_operatortype_append(OBJECT_OT_select_random);
WM_operatortype_append(OBJECT_OT_select_all);
WM_operatortype_append(OBJECT_OT_select_same_collection);
diff --git a/source/blender/editors/object/object_random.c b/source/blender/editors/object/object_random.c
index 7d670d3f452..3117cbb0166 100644
--- a/source/blender/editors/object/object_random.c
+++ b/source/blender/editors/object/object_random.c
@@ -9,6 +9,7 @@
#include "DNA_layer_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BLI_math.h"
#include "BLI_rand.h"
@@ -77,6 +78,7 @@ static bool object_rand_transverts(TransVertStore *tvs,
static int object_rand_verts_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob_active = CTX_data_edit_object(C);
const int ob_mode = ob_active->mode;
@@ -89,7 +91,7 @@ static int object_rand_verts_exec(bContext *C, wmOperator *op)
bool changed_multi = false;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len, ob_mode);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len, ob_mode);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 01042824aac..4a523997473 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -262,8 +262,8 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op)
}
else {
Object workob;
-
- ob->parent = BASACT(view_layer)->object;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ob->parent = BKE_view_layer_active_object_get(view_layer);
if (par3 != INDEX_UNSET) {
ob->partype = PARVERT3;
ob->par1 = par1;
@@ -1830,6 +1830,11 @@ static void single_obdata_users(
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
switch (ob->type) {
+ case OB_EMPTY:
+ ob->data = ID_NEW_SET(
+ ob->data,
+ BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS));
+ break;
case OB_LAMP:
ob->data = la = ID_NEW_SET(
ob->data,
@@ -2068,6 +2073,7 @@ static void tag_localizable_objects(bContext *C, const int mode)
* otherwise they're lost on reload, see T40595.
*/
static bool make_local_all__instance_indirect_unused(Main *bmain,
+ const Scene *scene,
ViewLayer *view_layer,
Collection *collection)
{
@@ -2081,6 +2087,7 @@ static bool make_local_all__instance_indirect_unused(Main *bmain,
id_us_plus(&ob->id);
BKE_collection_object_add(bmain, collection, ob);
+ BKE_view_layer_synced_ensure(scene, view_layer);
base = BKE_view_layer_base_find(view_layer, ob);
ED_object_base_select(base, BA_SELECT);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
@@ -2148,15 +2155,16 @@ static int make_local_exec(bContext *C, wmOperator *op)
/* NOTE: we (ab)use LIB_TAG_PRE_EXISTING to cherry pick which ID to make local... */
if (mode == MAKE_LOCAL_ALL) {
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Collection *collection = CTX_data_collection(C);
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
/* De-select so the user can differentiate newly instanced from existing objects. */
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
- if (make_local_all__instance_indirect_unused(bmain, view_layer, collection)) {
+ if (make_local_all__instance_indirect_unused(bmain, scene, view_layer, collection)) {
BKE_report(op->reports,
RPT_INFO,
"Orphan library objects added to the current scene to avoid loss");
@@ -2275,12 +2283,6 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
ID *id_root = NULL;
bool is_override_instancing_object = false;
- const bool do_fully_editable = U.experimental.use_override_new_fully_editable;
-
- GSet *user_overrides_objects_uids = do_fully_editable ? NULL :
- BLI_gset_new(BLI_ghashutil_inthash_p,
- BLI_ghashutil_intcmp,
- __func__);
bool user_overrides_from_selected_objects = false;
if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
@@ -2320,6 +2322,21 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
user_overrides_from_selected_objects = true;
}
+ const bool do_fully_editable = !user_overrides_from_selected_objects;
+
+ GSet *user_overrides_objects_uids = do_fully_editable ? NULL :
+ BLI_gset_new(BLI_ghashutil_inthash_p,
+ BLI_ghashutil_intcmp,
+ __func__);
+
+ /* Make already existing selected liboverrides editable. */
+ FOREACH_SELECTED_OBJECT_BEGIN (view_layer, CTX_wm_view3d(C), ob_iter) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(ob_iter) && !ID_IS_LINKED(ob_iter)) {
+ ob_iter->id.override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ }
+ }
+ FOREACH_SELECTED_OBJECT_END;
+
if (do_fully_editable) {
/* Pass. */
}
@@ -2342,6 +2359,25 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ /* For the time being, replace selected linked objects by their overrides in all collections.
+ * While this may not be the absolute best behavior in all cases, in most common one this should
+ * match the expected result. */
+ if (user_overrides_objects_uids != NULL) {
+ LISTBASE_FOREACH (Collection *, coll_iter, &bmain->collections) {
+ if (ID_IS_LINKED(coll_iter)) {
+ continue;
+ }
+ LISTBASE_FOREACH (CollectionObject *, coll_ob_iter, &coll_iter->gobject) {
+ if (BLI_gset_haskey(user_overrides_objects_uids,
+ POINTER_FROM_UINT(coll_ob_iter->ob->id.session_uuid))) {
+ /* Tag for remapping when creating overrides. */
+ coll_iter->id.tag |= LIB_TAG_DOIT;
+ break;
+ }
+ }
+ }
+ }
+
ID *id_root_override;
const bool success = BKE_lib_override_library_create(bmain,
scene,
@@ -2406,6 +2442,8 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
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);
+ WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
@@ -2430,6 +2468,9 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve
}
if (!ID_IS_LINKED(obact)) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(obact)) {
+ return make_override_library_exec(C, op);
+ }
BKE_report(op->reports, RPT_ERROR, "Cannot make library override from a local object");
return OPERATOR_CANCELLED;
}
@@ -2468,17 +2509,20 @@ static bool make_override_library_poll(bContext *C)
Object *obact = CTX_data_active_object(C);
/* Object must be directly linked to be overridable. */
- return (ED_operator_objectmode(C) && obact != NULL &&
- (ID_IS_LINKED(obact) || (obact->instance_collection != NULL &&
- ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection) &&
- !ID_IS_OVERRIDE_LIBRARY(obact))));
+ return (
+ ED_operator_objectmode(C) && obact != NULL &&
+ (ID_IS_LINKED(obact) || ID_IS_OVERRIDE_LIBRARY(obact) ||
+ (obact->instance_collection != NULL &&
+ ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection) && !ID_IS_OVERRIDE_LIBRARY(obact))));
}
void OBJECT_OT_make_override_library(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Make Library Override";
- ot->description = "Make a local override of this library linked data-block";
+ ot->description =
+ "Create a local override of the selected linked objects, and their hierarchy of "
+ "dependencies";
ot->idname = "OBJECT_OT_make_override_library";
/* api callbacks */
@@ -2508,6 +2552,130 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
+/** \name Reset Library Override Operator
+ * \{ */
+
+static bool reset_clear_override_library_poll(bContext *C)
+{
+ Object *obact = CTX_data_active_object(C);
+
+ /* Object must be local and an override. */
+ return (ED_operator_objectmode(C) && obact != NULL && !ID_IS_LINKED(obact) &&
+ ID_IS_OVERRIDE_LIBRARY(obact));
+}
+
+static int reset_override_library_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+
+ /* Make already existing selected liboverrides editable. */
+ FOREACH_SELECTED_OBJECT_BEGIN (CTX_data_view_layer(C), CTX_wm_view3d(C), ob_iter) {
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(ob_iter) && !ID_IS_LINKED(ob_iter)) {
+ BKE_lib_override_library_id_reset(bmain, &ob_iter->id, false);
+ }
+ }
+ FOREACH_SELECTED_OBJECT_END;
+
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+ WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_reset_override_library(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Reset Library Override";
+ ot->description = "Reset the selected local overrides to their linked references values";
+ ot->idname = "OBJECT_OT_reset_override_library";
+
+ /* api callbacks */
+ ot->exec = reset_override_library_exec;
+ ot->poll = reset_clear_override_library_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
+/** \name Clear Library Override Operator
+ * \{ */
+
+static int clear_override_library_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Main *bmain = CTX_data_main(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Scene *scene = CTX_data_scene(C);
+ LinkNode *todo_objects = NULL, *todo_object_iter;
+
+ /* Make already existing selected liboverrides editable. */
+ FOREACH_SELECTED_OBJECT_BEGIN (view_layer, CTX_wm_view3d(C), ob_iter) {
+ if (ID_IS_LINKED(ob_iter)) {
+ continue;
+ }
+ BLI_linklist_prepend_alloca(&todo_objects, ob_iter);
+ }
+ FOREACH_SELECTED_OBJECT_END;
+
+ for (todo_object_iter = todo_objects; todo_object_iter != NULL;
+ todo_object_iter = todo_object_iter->next) {
+ Object *ob_iter = todo_object_iter->link;
+ if (BKE_lib_override_library_is_hierarchy_leaf(bmain, &ob_iter->id)) {
+ bool do_remap_active = false;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (BKE_view_layer_active_object_get(view_layer) == ob_iter) {
+ do_remap_active = true;
+ }
+ BKE_libblock_remap(bmain,
+ &ob_iter->id,
+ ob_iter->id.override_library->reference,
+ ID_REMAP_SKIP_INDIRECT_USAGE);
+ if (do_remap_active) {
+ Object *ref_object = (Object *)ob_iter->id.override_library->reference;
+ Base *basact = BKE_view_layer_base_find(view_layer, ref_object);
+ if (basact != NULL) {
+ view_layer->basact = basact;
+ }
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
+ }
+ BKE_id_delete(bmain, &ob_iter->id);
+ }
+ else {
+ BKE_lib_override_library_id_reset(bmain, &ob_iter->id, true);
+ }
+ }
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+ WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_clear_override_library(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Clear Library Override";
+ ot->description =
+ "Delete the selected local overrides and relink their usages to the linked data-blocks if "
+ "possible, else reset them and mark them as non editable";
+ ot->idname = "OBJECT_OT_clear_override_library";
+
+ /* api callbacks */
+ ot->exec = clear_override_library_exec;
+ ot->poll = reset_clear_override_library_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
/** \name Make Single User Operator
* \{ */
@@ -2528,7 +2696,7 @@ static int make_single_user_exec(bContext *C, wmOperator *op)
if (RNA_boolean_get(op->ptr, "object")) {
if (flag == SELECT) {
- BKE_view_layer_selected_objects_tag(view_layer, OB_DONE);
+ BKE_view_layer_selected_objects_tag(scene, view_layer, OB_DONE);
single_object_users(bmain, scene, v3d, OB_DONE, copy_collections);
}
else {
diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc
index 8a7138b25ac..adc07d0b411 100644
--- a/source/blender/editors/object/object_remesh.cc
+++ b/source/blender/editors/object/object_remesh.cc
@@ -73,6 +73,9 @@
#include "object_intern.h" /* own include */
+using blender::IndexRange;
+using blender::Span;
+
/* TODO(sebpa): unstable, can lead to unrecoverable errors. */
// #define USE_MESH_CURVATURE
@@ -128,7 +131,8 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
}
/* Output mesh will be all smooth or all flat shading. */
- const bool smooth_normals = mesh->mpoly[0].flag & ME_SMOOTH;
+ const Span<MPoly> polys = mesh->polys();
+ const bool smooth_normals = polys.first().flag & ME_SMOOTH;
float isovalue = 0.0f;
if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) {
@@ -144,7 +148,7 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
}
if (ob->mode == OB_MODE_SCULPT) {
- ED_sculpt_undo_geometry_begin(ob, op->type->name);
+ ED_sculpt_undo_geometry_begin(ob, op);
}
if (mesh->flag & ME_REMESH_FIX_POLES && mesh->remesh_voxel_adaptivity <= 0.0f) {
@@ -175,14 +179,13 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op)
BKE_remesh_reproject_vertex_paint(new_mesh, mesh);
}
- BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob);
if (smooth_normals) {
BKE_mesh_smooth_flag_set(static_cast<Mesh *>(ob->data), true);
}
if (ob->mode == OB_MODE_SCULPT) {
- BKE_sculpt_ensure_orig_mesh_data(CTX_data_scene(C), ob);
ED_sculpt_undo_geometry_end(ob);
}
@@ -577,10 +580,18 @@ static int voxel_size_edit_invoke(bContext *C, wmOperator *op, const wmEvent *ev
/* Use the Bounding Box face normal as the basis Z. */
normal_tri_v3(cd->text_mat[2], cd->preview_plane[0], cd->preview_plane[1], cd->preview_plane[2]);
+ /* Invert object scale. */
+ float scale[3];
+ mat4_to_size(scale, active_object->obmat);
+ invert_v3(scale);
+ size_to_mat4(scale_mat, scale);
+
+ mul_m4_m4_pre(cd->text_mat, scale_mat);
+
/* Write the text position into the matrix. */
copy_v3_v3(cd->text_mat[3], text_pos);
- /* Scale the text. */
+ /* Scale the text to constant viewport size. */
float text_pos_word_space[3];
mul_v3_m4v3(text_pos_word_space, active_object->obmat, text_pos);
const float pixelsize = ED_view3d_pixel_size(rv3d, text_pos_word_space);
@@ -646,6 +657,7 @@ struct QuadriFlowJob {
short *stop, *do_update;
float *progress;
+ const struct wmOperator *op;
Scene *scene;
int target_faces;
int seed;
@@ -669,9 +681,11 @@ static bool mesh_is_manifold_consistent(Mesh *mesh)
* check that the direction of the faces are consistent and doesn't suddenly
* flip
*/
+ const Span<MVert> verts = mesh->verts();
+ const Span<MEdge> edges = mesh->edges();
+ const Span<MLoop> loops = mesh->loops();
bool is_manifold_consistent = true;
- const MLoop *mloop = mesh->mloop;
char *edge_faces = (char *)MEM_callocN(mesh->totedge * sizeof(char), "remesh_manifold_check");
int *edge_vert = (int *)MEM_malloc_arrayN(
mesh->totedge, sizeof(uint), "remesh_consistent_check");
@@ -680,18 +694,17 @@ static bool mesh_is_manifold_consistent(Mesh *mesh)
edge_vert[i] = -1;
}
- for (uint loop_idx = 0; loop_idx < mesh->totloop; loop_idx++) {
- const MLoop *loop = &mloop[loop_idx];
- edge_faces[loop->e] += 1;
- if (edge_faces[loop->e] > 2) {
+ for (const MLoop &loop : loops) {
+ edge_faces[loop.e] += 1;
+ if (edge_faces[loop.e] > 2) {
is_manifold_consistent = false;
break;
}
- if (edge_vert[loop->e] == -1) {
- edge_vert[loop->e] = loop->v;
+ if (edge_vert[loop.e] == -1) {
+ edge_vert[loop.e] = loop.v;
}
- else if (edge_vert[loop->e] == loop->v) {
+ else if (edge_vert[loop.e] == loop.v) {
/* Mesh has flips in the surface so it is non consistent */
is_manifold_consistent = false;
break;
@@ -699,16 +712,16 @@ static bool mesh_is_manifold_consistent(Mesh *mesh)
}
if (is_manifold_consistent) {
- for (uint i = 0; i < mesh->totedge; i++) {
+ for (const int i : edges.index_range()) {
/* Check for wire edges. */
if (edge_faces[i] == 0) {
is_manifold_consistent = false;
break;
}
/* Check for zero length edges */
- MVert *v1 = &mesh->mvert[mesh->medge[i].v1];
- MVert *v2 = &mesh->mvert[mesh->medge[i].v2];
- if (compare_v3v3(v1->co, v2->co, 1e-4f)) {
+ const MVert &v1 = verts[edges[i].v1];
+ const MVert &v2 = verts[edges[i].v2];
+ if (compare_v3v3(v1.co, v2.co, 1e-4f)) {
is_manifold_consistent = false;
break;
}
@@ -883,7 +896,7 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
new_mesh = remesh_symmetry_mirror(qj->owner, new_mesh, qj->symmetry_axes);
if (ob->mode == OB_MODE_SCULPT) {
- ED_sculpt_undo_geometry_begin(ob, "QuadriFlow Remesh");
+ ED_sculpt_undo_geometry_begin(ob, qj->op);
}
if (qj->preserve_paint_mask) {
@@ -891,14 +904,13 @@ static void quadriflow_start_job(void *customdata, short *stop, short *do_update
BKE_mesh_remesh_reproject_paint_mask(new_mesh, mesh);
}
- BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob);
if (qj->smooth_normals) {
BKE_mesh_smooth_flag_set(static_cast<Mesh *>(ob->data), true);
}
if (ob->mode == OB_MODE_SCULPT) {
- BKE_sculpt_ensure_orig_mesh_data(qj->scene, ob);
ED_sculpt_undo_geometry_end(ob);
}
@@ -941,6 +953,7 @@ static int quadriflow_remesh_exec(bContext *C, wmOperator *op)
{
QuadriFlowJob *job = (QuadriFlowJob *)MEM_mallocN(sizeof(QuadriFlowJob), "QuadriFlowJob");
+ job->op = op;
job->owner = CTX_data_active_object(C);
job->scene = CTX_data_scene(C);
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index c3d8fb9cfe5..43867877fdb 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -117,29 +117,28 @@ void ED_object_base_activate(bContext *C, Base *base)
void ED_object_base_activate_with_mode_exit_if_needed(bContext *C, Base *base)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
/* Currently we only need to be concerned with edit-mode. */
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit) {
Object *ob = base->object;
if (((ob->mode & OB_MODE_EDIT) == 0) || (obedit->type != ob->type)) {
Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
ED_object_editmode_exit_multi_ex(bmain, scene, view_layer, EM_FREEDATA);
}
}
ED_object_base_activate(C, base);
}
-bool ED_object_base_deselect_all_ex(ViewLayer *view_layer,
- View3D *v3d,
- int action,
- bool *r_any_visible)
+bool ED_object_base_deselect_all_ex(
+ const Scene *scene, ViewLayer *view_layer, View3D *v3d, int action, bool *r_any_visible)
{
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
- FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base) {
+ FOREACH_VISIBLE_BASE_BEGIN (scene, view_layer, v3d, base) {
if (v3d && ((v3d->object_type_exclude_select & (1 << base->object->type)) != 0)) {
continue;
}
@@ -153,7 +152,7 @@ bool ED_object_base_deselect_all_ex(ViewLayer *view_layer,
bool any_visible = false;
bool changed = false;
- FOREACH_VISIBLE_BASE_BEGIN (view_layer, v3d, base) {
+ FOREACH_VISIBLE_BASE_BEGIN (scene, view_layer, v3d, base) {
if (v3d && ((v3d->object_type_exclude_select & (1 << base->object->type)) != 0)) {
continue;
}
@@ -190,9 +189,12 @@ bool ED_object_base_deselect_all_ex(ViewLayer *view_layer,
return changed;
}
-bool ED_object_base_deselect_all(ViewLayer *view_layer, View3D *v3d, int action)
+bool ED_object_base_deselect_all(const Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ int action)
{
- return ED_object_base_deselect_all_ex(view_layer, v3d, action, NULL);
+ return ED_object_base_deselect_all_ex(scene, view_layer, v3d, action, NULL);
}
/** \} */
@@ -203,7 +205,7 @@ bool ED_object_base_deselect_all(ViewLayer *view_layer, View3D *v3d, int action)
static int get_base_select_priority(Base *base)
{
- if (base->flag & BASE_VISIBLE_DEPSGRAPH) {
+ if (base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) {
if (base->flag & BASE_SELECTABLE) {
return 3;
}
@@ -212,12 +214,13 @@ static int get_base_select_priority(Base *base)
return 1;
}
-Base *ED_object_find_first_by_data_id(ViewLayer *view_layer, ID *id)
+Base *ED_object_find_first_by_data_id(const Scene *scene, ViewLayer *view_layer, ID *id)
{
BLI_assert(OB_DATA_SUPPORT_ID(GS(id->name)));
/* Try active object. */
- Base *basact = view_layer->basact;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *basact = BKE_view_layer_active_base_get(view_layer);
if (basact && basact->object && basact->object->data == id) {
return basact;
@@ -227,7 +230,7 @@ Base *ED_object_find_first_by_data_id(ViewLayer *view_layer, ID *id)
Base *base_best = NULL;
int priority_best = 0;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->object && base->object->data == id) {
if (base->flag & BASE_SELECTED) {
return base;
@@ -247,8 +250,10 @@ Base *ED_object_find_first_by_data_id(ViewLayer *view_layer, ID *id)
bool ED_object_jump_to_object(bContext *C, Object *ob, const bool UNUSED(reveal_hidden))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base == NULL) {
@@ -257,10 +262,10 @@ bool ED_object_jump_to_object(bContext *C, Object *ob, const bool UNUSED(reveal_
/* TODO: use 'reveal_hidden', as is done with bones. */
- if (view_layer->basact != base || !(base->flag & BASE_SELECTED)) {
+ if (BKE_view_layer_active_base_get(view_layer) != base || !(base->flag & BASE_SELECTED)) {
/* Select if not selected. */
if (!(base->flag & BASE_SELECTED)) {
- ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
+ ED_object_base_deselect_all(scene, view_layer, v3d, SEL_DESELECT);
if (BASE_VISIBLE(v3d, base)) {
ED_object_base_select(base, BA_SELECT);
@@ -382,6 +387,7 @@ static bool objects_selectable_poll(bContext *C)
static int object_select_by_type_exec(bContext *C, wmOperator *op)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
short obtype, extend;
@@ -390,7 +396,7 @@ static int object_select_by_type_exec(bContext *C, wmOperator *op)
extend = RNA_boolean_get(op->ptr, "extend");
if (extend == 0) {
- ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
+ ED_object_base_deselect_all(scene, view_layer, v3d, SEL_DESELECT);
}
CTX_DATA_BEGIN (C, Base *, base, visible_bases) {
@@ -400,7 +406,6 @@ static int object_select_by_type_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
- Scene *scene = CTX_data_scene(C);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
@@ -623,10 +628,11 @@ static int object_select_linked_exec(bContext *C, wmOperator *op)
extend = RNA_boolean_get(op->ptr, "extend");
if (extend == 0) {
- ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
+ ED_object_base_deselect_all(scene, view_layer, v3d, SEL_DESELECT);
}
- ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ob = BKE_view_layer_active_object_get(view_layer);
if (ob == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active object");
return OPERATOR_CANCELLED;
@@ -777,17 +783,21 @@ static bool select_grouped_children(bContext *C, Object *ob, const bool recursiv
return changed;
}
-static bool select_grouped_parent(bContext *C) /* Makes parent active and de-selected OBACT */
+/* Makes parent active and de-selected BKE_view_layer_active_object_get. */
+static bool select_grouped_parent(bContext *C)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
Base *baspar, *basact = CTX_data_active_base(C);
bool changed = false;
if (!basact || !(basact->object->parent)) {
- return 0; /* we know OBACT is valid */
+ /* We know BKE_view_layer_active_object_get is valid. */
+ return 0;
}
+ BKE_view_layer_synced_ensure(scene, view_layer);
baspar = BKE_view_layer_base_find(view_layer, basact->object->parent);
/* can be NULL if parent in other scene */
@@ -856,6 +866,7 @@ static bool select_grouped_collection(bContext *C, Object *ob)
static bool select_grouped_object_hooks(bContext *C, Object *ob)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
@@ -868,6 +879,7 @@ static bool select_grouped_object_hooks(bContext *C, Object *ob)
if (md->type == eModifierType_Hook) {
hmd = (HookModifierData *)md;
if (hmd->object) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
base = BKE_view_layer_base_find(view_layer, hmd->object);
if (base && ((base->flag & BASE_SELECTED) == 0) && (BASE_SELECTABLE(v3d, base))) {
ED_object_base_select(base, BA_SELECT);
@@ -1018,10 +1030,11 @@ static int object_select_grouped_exec(bContext *C, wmOperator *op)
extend = RNA_boolean_get(op->ptr, "extend");
if (extend == 0) {
- changed = ED_object_base_deselect_all(view_layer, v3d, SEL_DESELECT);
+ changed = ED_object_base_deselect_all(scene, view_layer, v3d, SEL_DESELECT);
}
- ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ob = BKE_view_layer_active_object_get(view_layer);
if (ob == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active object");
return OPERATOR_CANCELLED;
@@ -1111,15 +1124,15 @@ void OBJECT_OT_select_grouped(wmOperatorType *ot)
static int object_select_all_exec(bContext *C, wmOperator *op)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
int action = RNA_enum_get(op->ptr, "action");
bool any_visible = false;
- bool changed = ED_object_base_deselect_all_ex(view_layer, v3d, action, &any_visible);
+ bool changed = ED_object_base_deselect_all_ex(scene, view_layer, v3d, action, &any_visible);
if (changed) {
- Scene *scene = CTX_data_scene(C);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
@@ -1128,7 +1141,7 @@ static int object_select_all_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
if (any_visible == false) {
- /* TODO(campbell): Looks like we could remove this,
+ /* TODO(@campbellbarton): Looks like we could remove this,
* if not comment should say why its needed. */
return OPERATOR_PASS_THROUGH;
}
@@ -1238,6 +1251,7 @@ static int object_select_mirror_exec(bContext *C, wmOperator *op)
if (!STREQ(name_flip, primbase->object->id.name + 2)) {
Object *ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name_flip);
if (ob) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *secbase = BKE_view_layer_base_find(view_layer, ob);
if (secbase) {
@@ -1289,9 +1303,11 @@ void OBJECT_OT_select_mirror(wmOperatorType *ot)
static bool object_select_more_less(bContext *C, const bool select)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
ob->flag &= ~OB_DONE;
ob->id.tag &= ~LIB_TAG_DOIT;
diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c
index dd7fc192dc1..4b721cb65a1 100644
--- a/source/blender/editors/object/object_shader_fx.c
+++ b/source/blender/editors/object/object_shader_fx.c
@@ -481,12 +481,15 @@ static int shaderfx_remove_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Object *ob = ED_object_active_context(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
+ if (!fx) {
+ return OPERATOR_CANCELLED;
+ }
/* Store name temporarily for report. */
char name[MAX_NAME];
strcpy(name, fx->name);
- if (!fx || !ED_object_shaderfx_remove(op->reports, bmain, ob, fx)) {
+ if (!ED_object_shaderfx_remove(op->reports, bmain, ob, fx)) {
return OPERATOR_CANCELLED;
}
@@ -671,7 +674,9 @@ static int shaderfx_copy_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_active_context(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
-
+ if (!fx) {
+ return OPERATOR_CANCELLED;
+ }
ShaderFxData *nfx = BKE_shaderfx_new(fx->type);
if (!nfx) {
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c
index ebcf8573ccd..0328f6a6230 100644
--- a/source/blender/editors/object/object_shapekey.c
+++ b/source/blender/editors/object/object_shapekey.c
@@ -20,6 +20,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
@@ -112,14 +114,13 @@ static bool object_shape_key_mirror(
if (ob->type == OB_MESH) {
Mesh *me = ob->data;
- MVert *mv;
int i1, i2;
float *fp1, *fp2;
float tvec[3];
ED_mesh_mirror_spatial_table_begin(ob, NULL, NULL);
- for (i1 = 0, mv = me->mvert; i1 < me->totvert; i1++, mv++) {
+ for (i1 = 0; i1 < me->totvert; i1++) {
i2 = mesh_get_x_mirror_vert(ob, NULL, i1, use_topology);
if (i2 == i1) {
fp1 = ((float *)kb->data) + i1 * 3;
@@ -299,6 +300,10 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op)
bool changed = false;
if (RNA_boolean_get(op->ptr, "all")) {
+ if (RNA_boolean_get(op->ptr, "apply_mix")) {
+ float *arr = BKE_key_evaluate_object_ex(ob, NULL, NULL, 0, ob->data);
+ MEM_freeN(arr);
+ }
changed = BKE_object_shapekey_free(bmain, ob);
}
else {
@@ -315,6 +320,34 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+static bool shape_key_remove_poll_property(const bContext *UNUSED(C),
+ wmOperator *op,
+ const PropertyRNA *prop)
+{
+ const char *prop_id = RNA_property_identifier(prop);
+ const bool do_all = RNA_enum_get(op->ptr, "all");
+
+ /* Only show seed for randomize action! */
+ if (STREQ(prop_id, "apply_mix") && !do_all) {
+ return false;
+ }
+ return true;
+}
+
+static char *shape_key_remove_get_description(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(ot),
+ PointerRNA *ptr)
+{
+ const bool do_apply_mix = RNA_boolean_get(ptr, "apply_mix");
+
+ if (do_apply_mix) {
+ return BLI_strdup(
+ TIP_("Apply current visible shape to the object data, and delete all shape keys"));
+ }
+
+ return NULL;
+}
+
void OBJECT_OT_shape_key_remove(wmOperatorType *ot)
{
/* identifiers */
@@ -325,12 +358,19 @@ void OBJECT_OT_shape_key_remove(wmOperatorType *ot)
/* api callbacks */
ot->poll = shape_key_mode_exists_poll;
ot->exec = shape_key_remove_exec;
+ ot->poll_property = shape_key_remove_poll_property;
+ ot->get_description = shape_key_remove_get_description;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
- RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all shape keys");
+ RNA_def_boolean(ot->srna, "all", false, "All", "Remove all shape keys");
+ RNA_def_boolean(ot->srna,
+ "apply_mix",
+ false,
+ "Apply Mix",
+ "Apply current mix of shape keys to the geometry before removing them");
}
/** \} */
diff --git a/source/blender/editors/object/object_transform.cc b/source/blender/editors/object/object_transform.cc
index 70c3eed3768..0a86ae28b3e 100644
--- a/source/blender/editors/object/object_transform.cc
+++ b/source/blender/editors/object/object_transform.cc
@@ -321,7 +321,7 @@ static int object_clear_transform_generic_exec(bContext *C,
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
xcs = ED_object_xform_skip_child_container_create();
ED_object_xform_skip_child_container_item_ensure_from_array(
- xcs, view_layer, objects.data(), objects.size());
+ xcs, scene, view_layer, objects.data(), objects.size());
}
if (use_transform_data_origin) {
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
@@ -1033,7 +1033,9 @@ static int apply_objects_internal(bContext *C,
zero_v3(ob->rot);
zero_v3(ob->drot);
unit_qt(ob->quat);
+ unit_qt(ob->dquat);
unit_axis_angle(ob->rotAxis, &ob->rotAngle);
+ unit_axis_angle(ob->drotAxis, &ob->drotAngle);
}
}
@@ -1601,6 +1603,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
}
+ BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
}
@@ -2222,7 +2225,7 @@ static int object_transform_axis_target_modal(bContext *C, wmOperator *op, const
bool is_finished = false;
- if (ISMOUSE(xfd->init_event)) {
+ if (ISMOUSE_BUTTON(xfd->init_event)) {
if ((event->type == xfd->init_event) && (event->val == KM_RELEASE)) {
is_finished = true;
}
diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c
index cb9c8a92abe..50ba5b8af5f 100644
--- a/source/blender/editors/object/object_utils.c
+++ b/source/blender/editors/object/object_utils.c
@@ -22,6 +22,7 @@
#include "BKE_armature.h"
#include "BKE_editmesh.h"
#include "BKE_lattice.h"
+#include "BKE_layer.h"
#include "BKE_object.h"
#include "BKE_scene.h"
@@ -169,6 +170,7 @@ struct XFormObjectSkipChild_Container *ED_object_xform_skip_child_container_crea
void ED_object_xform_skip_child_container_item_ensure_from_array(
struct XFormObjectSkipChild_Container *xcs,
+ const Scene *scene,
ViewLayer *view_layer,
Object **objects,
uint objects_len)
@@ -178,8 +180,9 @@ void ED_object_xform_skip_child_container_item_ensure_from_array(
Object *ob = objects[ob_index];
BLI_gset_add(objects_in_transdata, ob);
}
-
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
+ LISTBASE_FOREACH (Base *, base, object_bases) {
Object *ob = base->object;
if (ob->parent != NULL) {
if (!BLI_gset_haskey(objects_in_transdata, ob)) {
@@ -209,7 +212,7 @@ void ED_object_xform_skip_child_container_item_ensure_from_array(
}
}
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, object_bases) {
Object *ob = base->object;
if (BLI_gset_haskey(objects_in_transdata, ob)) {
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.cc
index 17b7fe7fe5e..1d1263494c7 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.cc
@@ -5,9 +5,9 @@
* \ingroup edobj
*/
-#include <math.h>
-#include <stddef.h>
-#include <string.h>
+#include <cmath>
+#include <cstddef>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -21,14 +21,15 @@
#include "DNA_scene_types.h"
#include "DNA_workspace_types.h"
-#include "BLI_alloca.h"
#include "BLI_array.h"
+#include "BLI_array.hh"
#include "BLI_bitmap.h"
#include "BLI_blenlib.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLI_utildefines_stack.h"
+#include "BLI_vector.hh"
#include "BKE_context.h"
#include "BKE_customdata.h"
@@ -66,6 +67,9 @@
#include "object_intern.h"
+using blender::MutableSpan;
+using blender::Span;
+
static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob);
/* -------------------------------------------------------------------- */
@@ -74,7 +78,7 @@ static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob);
static bool object_array_for_wpaint_filter(const Object *ob, void *user_data)
{
- bContext *C = user_data;
+ bContext *C = static_cast<bContext *>(user_data);
if (vertex_group_supported_poll_ex(C, ob)) {
return true;
}
@@ -100,7 +104,7 @@ static bool vertex_group_use_vert_sel(Object *ob)
static Lattice *vgroup_edit_lattice(Object *ob)
{
- Lattice *lt = ob->data;
+ Lattice *lt = static_cast<Lattice *>(ob->data);
BLI_assert(ob->type == OB_LATTICE);
return (lt->editlatt) ? lt->editlatt->latt : lt;
}
@@ -115,7 +119,7 @@ bool ED_vgroup_sync_from_pose(Object *ob)
{
Object *armobj = BKE_object_pose_armature_get(ob);
if (armobj && (armobj->mode & OB_MODE_POSE)) {
- struct bArmature *arm = armobj->data;
+ bArmature *arm = static_cast<bArmature *>(armobj->data);
if (arm->act_bone) {
int def_num = BKE_object_defgroup_name_index(ob, arm->act_bone->name);
if (def_num != -1) {
@@ -151,7 +155,7 @@ bool ED_vgroup_parray_alloc(ID *id,
const bool use_vert_sel)
{
*dvert_tot = 0;
- *dvert_arr = NULL;
+ *dvert_arr = nullptr;
if (id) {
switch (GS(id->name)) {
@@ -172,42 +176,45 @@ bool ED_vgroup_parray_alloc(ID *id,
i = em->bm->totvert;
- *dvert_arr = MEM_mallocN(sizeof(void *) * i, "vgroup parray from me");
+ *dvert_arr = static_cast<MDeformVert **>(MEM_mallocN(sizeof(void *) * i, __func__));
*dvert_tot = i;
i = 0;
if (use_vert_sel) {
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
(*dvert_arr)[i] = BM_elem_flag_test(eve, BM_ELEM_SELECT) ?
- BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset) :
- NULL;
+ static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)) :
+ nullptr;
i++;
}
}
else {
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
- (*dvert_arr)[i] = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
+ (*dvert_arr)[i] = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
i++;
}
}
return true;
}
- if (me->dvert) {
- MVert *mvert = me->mvert;
- MDeformVert *dvert = me->dvert;
+ if (!me->deform_verts().is_empty()) {
+ const Span<MVert> verts = me->verts();
+ MutableSpan<MDeformVert> dverts = me->deform_verts_for_write();
*dvert_tot = me->totvert;
- *dvert_arr = MEM_mallocN(sizeof(void *) * me->totvert, "vgroup parray from me");
+ *dvert_arr = static_cast<MDeformVert **>(
+ MEM_mallocN(sizeof(void *) * me->totvert, __func__));
if (use_vert_sel) {
for (int i = 0; i < me->totvert; i++) {
- (*dvert_arr)[i] = (mvert[i].flag & SELECT) ? &dvert[i] : NULL;
+ (*dvert_arr)[i] = (verts[i].flag & SELECT) ? &dverts[i] : nullptr;
}
}
else {
for (int i = 0; i < me->totvert; i++) {
- (*dvert_arr)[i] = me->dvert + i;
+ (*dvert_arr)[i] = &dverts[i];
}
}
@@ -222,11 +229,12 @@ bool ED_vgroup_parray_alloc(ID *id,
if (lt->dvert) {
BPoint *def = lt->def;
*dvert_tot = lt->pntsu * lt->pntsv * lt->pntsw;
- *dvert_arr = MEM_mallocN(sizeof(void *) * (*dvert_tot), "vgroup parray from me");
+ *dvert_arr = static_cast<MDeformVert **>(
+ MEM_mallocN(sizeof(void *) * (*dvert_tot), __func__));
if (use_vert_sel) {
for (int i = 0; i < *dvert_tot; i++) {
- (*dvert_arr)[i] = (def->f1 & SELECT) ? &lt->dvert[i] : NULL;
+ (*dvert_arr)[i] = (def->f1 & SELECT) ? &lt->dvert[i] : nullptr;
}
}
else {
@@ -255,11 +263,12 @@ void ED_vgroup_parray_mirror_sync(Object *ob,
const int vgroup_tot)
{
BMEditMesh *em = BKE_editmesh_from_object(ob);
- MDeformVert **dvert_array_all = NULL;
+ MDeformVert **dvert_array_all = nullptr;
int dvert_tot_all;
/* get an array of all verts, not only selected */
- if (ED_vgroup_parray_alloc(ob->data, &dvert_array_all, &dvert_tot_all, false) == false) {
+ if (ED_vgroup_parray_alloc(
+ static_cast<ID *>(ob->data), &dvert_array_all, &dvert_tot_all, false) == false) {
BLI_assert(0);
return;
}
@@ -268,13 +277,13 @@ void ED_vgroup_parray_mirror_sync(Object *ob,
}
int flip_map_len;
- const int *flip_map = BKE_object_defgroup_flip_map(ob, &flip_map_len, true);
+ const int *flip_map = BKE_object_defgroup_flip_map(ob, true, &flip_map_len);
for (int i_src = 0; i_src < dvert_tot; i_src++) {
- if (dvert_array[i_src] != NULL) {
+ if (dvert_array[i_src] != nullptr) {
/* its selected, check if its mirror exists */
int i_dst = ED_mesh_mirror_get_vert(ob, i_src);
- if (i_dst != -1 && dvert_array_all[i_dst] != NULL) {
+ if (i_dst != -1 && dvert_array_all[i_dst] != nullptr) {
/* we found a match! */
const MDeformVert *dv_src = dvert_array[i_src];
MDeformVert *dv_dst = dvert_array_all[i_dst];
@@ -294,11 +303,12 @@ void ED_vgroup_parray_mirror_sync(Object *ob,
void ED_vgroup_parray_mirror_assign(Object *ob, MDeformVert **dvert_array, const int dvert_tot)
{
BMEditMesh *em = BKE_editmesh_from_object(ob);
- MDeformVert **dvert_array_all = NULL;
+ MDeformVert **dvert_array_all = nullptr;
int dvert_tot_all;
/* get an array of all verts, not only selected */
- if (ED_vgroup_parray_alloc(ob->data, &dvert_array_all, &dvert_tot_all, false) == false) {
+ if (ED_vgroup_parray_alloc(
+ static_cast<ID *>(ob->data), &dvert_array_all, &dvert_tot_all, false) == false) {
BLI_assert(0);
return;
}
@@ -308,7 +318,7 @@ void ED_vgroup_parray_mirror_assign(Object *ob, MDeformVert **dvert_array, const
}
for (int i = 0; i < dvert_tot; i++) {
- if (dvert_array[i] == NULL) {
+ if (dvert_array[i] == nullptr) {
/* its unselected, check if its mirror is */
int i_sel = ED_mesh_mirror_get_vert(ob, i);
if ((i_sel != -1) && (i_sel != i) && (dvert_array[i_sel])) {
@@ -357,8 +367,8 @@ void ED_vgroup_parray_remove_zero(MDeformVert **dvert_array,
bool ED_vgroup_array_copy(Object *ob, Object *ob_from)
{
- MDeformVert **dvert_array_from = NULL, **dvf;
- MDeformVert **dvert_array = NULL, **dv;
+ MDeformVert **dvert_array_from = nullptr, **dvf;
+ MDeformVert **dvert_array = nullptr, **dv;
int dvert_tot_from;
int dvert_tot;
int i;
@@ -378,17 +388,18 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from)
/* In case we copy vgroup between two objects using same data,
* we only have to care about object side of things. */
if (ob->data != ob_from->data) {
- ED_vgroup_parray_alloc(ob_from->data, &dvert_array_from, &dvert_tot_from, false);
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false);
+ ED_vgroup_parray_alloc(
+ static_cast<ID *>(ob_from->data), &dvert_array_from, &dvert_tot_from, false);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, false);
- if ((dvert_array == NULL) && (dvert_array_from != NULL) &&
- BKE_object_defgroup_data_create(ob->data)) {
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false);
+ if ((dvert_array == nullptr) && (dvert_array_from != nullptr) &&
+ BKE_object_defgroup_data_create(static_cast<ID *>(ob->data))) {
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, false);
new_vgroup = true;
}
- if (dvert_tot == 0 || (dvert_tot != dvert_tot_from) || dvert_array_from == NULL ||
- dvert_array == NULL) {
+ if (dvert_tot == 0 || (dvert_tot != dvert_tot_from) || dvert_array_from == nullptr ||
+ dvert_array == nullptr) {
if (dvert_array) {
MEM_freeN(dvert_array);
}
@@ -413,7 +424,7 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from)
if (defbase_tot_from < defbase_tot) {
/* correct vgroup indices because the number of vgroups is being reduced. */
- int *remap = MEM_mallocN(sizeof(int) * (defbase_tot + 1), __func__);
+ blender::Array<int> remap(defbase_tot + 1);
for (i = 0; i <= defbase_tot_from; i++) {
remap[i] = i;
}
@@ -421,11 +432,10 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from)
remap[i] = 0; /* can't use these, so disable */
}
- BKE_object_defgroup_remap_update_users(ob, remap);
- MEM_freeN(remap);
+ BKE_object_defgroup_remap_update_users(ob, remap.data());
}
- if (dvert_array_from != NULL && dvert_array != NULL) {
+ if (dvert_array_from != nullptr && dvert_array != nullptr) {
dvf = dvert_array_from;
dv = dvert_array;
@@ -434,7 +444,7 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from)
*(*dv) = *(*dvf);
if ((*dv)->dw) {
- (*dv)->dw = MEM_dupallocN((*dv)->dw);
+ (*dv)->dw = static_cast<MDeformWeight *>(MEM_dupallocN((*dv)->dw));
}
}
@@ -496,14 +506,14 @@ static void mesh_defvert_mirror_update_internal(Object *ob,
if (def_nr == -1) {
/* All vgroups, add groups where needed. */
int flip_map_len;
- int *flip_map = BKE_object_defgroup_flip_map(ob, &flip_map_len, true);
+ int *flip_map = BKE_object_defgroup_flip_map_unlocked(ob, true, &flip_map_len);
BKE_defvert_sync_mapped(dvert_dst, dvert_src, flip_map, flip_map_len, true);
MEM_freeN(flip_map);
}
else {
/* Single vgroup. */
MDeformWeight *dw = BKE_defvert_ensure_index(dvert_dst,
- BKE_object_defgroup_flip_index(ob, def_nr, 1));
+ BKE_object_defgroup_flip_index(ob, def_nr, true));
if (dw) {
dw->weight = BKE_defvert_find_weight(dvert_src, def_nr);
}
@@ -513,7 +523,7 @@ static void mesh_defvert_mirror_update_internal(Object *ob,
static void ED_mesh_defvert_mirror_update_em(
Object *ob, BMVert *eve, int def_nr, int vidx, const int cd_dvert_offset)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BMEditMesh *em = me->edit_mesh;
BMVert *eve_mirr;
bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
@@ -521,8 +531,10 @@ static void ED_mesh_defvert_mirror_update_em(
eve_mirr = editbmesh_get_x_mirror_vert(ob, em, eve, eve->co, vidx, use_topology);
if (eve_mirr && eve_mirr != eve) {
- MDeformVert *dvert_src = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
- MDeformVert *dvert_dst = BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset);
+ MDeformVert *dvert_src = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
+ MDeformVert *dvert_dst = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset));
mesh_defvert_mirror_update_internal(ob, dvert_dst, dvert_src, def_nr);
}
}
@@ -530,25 +542,26 @@ static void ED_mesh_defvert_mirror_update_em(
static void ED_mesh_defvert_mirror_update_ob(Object *ob, int def_nr, int vidx)
{
int vidx_mirr;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
if (vidx == -1) {
return;
}
- vidx_mirr = mesh_get_x_mirror_vert(ob, NULL, vidx, use_topology);
+ vidx_mirr = mesh_get_x_mirror_vert(ob, nullptr, vidx, use_topology);
+ MutableSpan<MDeformVert> dverts = me->deform_verts_for_write();
if ((vidx_mirr) >= 0 && (vidx_mirr != vidx)) {
- MDeformVert *dvert_src = &me->dvert[vidx];
- MDeformVert *dvert_dst = &me->dvert[vidx_mirr];
+ MDeformVert *dvert_src = &dverts[vidx];
+ MDeformVert *dvert_dst = &dverts[vidx_mirr];
mesh_defvert_mirror_update_internal(ob, dvert_dst, dvert_src, def_nr);
}
}
void ED_vgroup_vert_active_mirror(Object *ob, int def_nr)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BMEditMesh *em = me->edit_mesh;
MDeformVert *dvert_act;
@@ -584,7 +597,7 @@ static void vgroup_remove_weight(Object *ob, const int def_nr)
static bool vgroup_normalize_active_vertex(Object *ob, eVGroupSelect subset_type)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BMEditMesh *em = me->edit_mesh;
BMVert *eve_act;
int v_act;
@@ -599,7 +612,7 @@ static bool vgroup_normalize_active_vertex(Object *ob, eVGroupSelect subset_type
dvert_act = ED_mesh_active_dvert_get_ob(ob, &v_act);
}
- if (dvert_act == NULL) {
+ if (dvert_act == nullptr) {
return false;
}
@@ -623,7 +636,7 @@ static bool vgroup_normalize_active_vertex(Object *ob, eVGroupSelect subset_type
static void vgroup_copy_active_to_sel(Object *ob, eVGroupSelect subset_type)
{
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BMEditMesh *em = me->edit_mesh;
MDeformVert *dvert_act;
int i, vgroup_tot, subset_count;
@@ -639,7 +652,8 @@ static void vgroup_copy_active_to_sel(Object *ob, eVGroupSelect subset_type)
if (dvert_act) {
BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && eve != eve_act) {
- MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
+ MDeformVert *dv = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
BKE_defvert_copy_subset(dv, dvert_act, vgroup_validmap, vgroup_tot);
if (me->symmetry & ME_SYMMETRY_X) {
ED_mesh_defvert_mirror_update_em(ob, eve, -1, i, cd_dvert_offset);
@@ -649,15 +663,15 @@ static void vgroup_copy_active_to_sel(Object *ob, eVGroupSelect subset_type)
}
}
else {
- MDeformVert *dv;
+ const Span<MVert> verts = me->verts();
int v_act;
dvert_act = ED_mesh_active_dvert_get_ob(ob, &v_act);
if (dvert_act) {
- dv = me->dvert;
- for (i = 0; i < me->totvert; i++, dv++) {
- if ((me->mvert[i].flag & SELECT) && dv != dvert_act) {
- BKE_defvert_copy_subset(dv, dvert_act, vgroup_validmap, vgroup_tot);
+ MutableSpan<MDeformVert> dverts = me->deform_verts_for_write();
+ for (i = 0; i < me->totvert; i++) {
+ if ((verts[i].flag & SELECT) && &dverts[i] != dvert_act) {
+ BKE_defvert_copy_subset(&dverts[i], dvert_act, vgroup_validmap, vgroup_tot);
if (me->symmetry & ME_SYMMETRY_X) {
ED_mesh_defvert_mirror_update_ob(ob, -1, i);
}
@@ -688,7 +702,7 @@ static const EnumPropertyItem WT_vertex_group_select_item[] = {
"Deform Pose Bones",
"All Vertex Groups assigned to Deform Bones"},
{WT_VGROUP_ALL, "ALL", 0, "All Groups", "All Vertex Groups"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *C,
@@ -698,10 +712,10 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *
const uint selection_mask)
{
Object *ob;
- EnumPropertyItem *item = NULL;
+ EnumPropertyItem *item = nullptr;
int totitem = 0;
- if (C == NULL) {
+ if (C == nullptr) {
/* needed for docs and i18n tools */
return WT_vertex_group_select_item;
}
@@ -797,13 +811,13 @@ static void ED_vgroup_nr_vert_add(
Object *ob, const int def_nr, const int vertnum, const float weight, const int assignmode)
{
/* Add the vert to the deform group with the specified number. */
- MDeformVert *dvert = NULL;
+ MDeformVert *dvert = nullptr;
int tot;
/* Get the vert. */
- BKE_object_defgroup_array_get(ob->data, &dvert, &tot);
+ BKE_object_defgroup_array_get(static_cast<ID *>(ob->data), &dvert, &tot);
- if (dvert == NULL) {
+ if (dvert == nullptr) {
return;
}
@@ -865,7 +879,7 @@ void ED_vgroup_vert_add(Object *ob, bDeformGroup *dg, int vertnum, float weight,
const ListBase *defbase = BKE_object_defgroup_list(ob);
const int def_nr = BLI_findindex(defbase, dg);
- MDeformVert *dv = NULL;
+ MDeformVert *dv = nullptr;
int tot;
/* get the deform group number, exit if
@@ -875,8 +889,8 @@ void ED_vgroup_vert_add(Object *ob, bDeformGroup *dg, int vertnum, float weight,
/* if there's no deform verts then create some,
*/
- if (BKE_object_defgroup_array_get(ob->data, &dv, &tot) && dv == NULL) {
- BKE_object_defgroup_data_create(ob->data);
+ if (BKE_object_defgroup_array_get(static_cast<ID *>(ob->data), &dv, &tot) && dv == nullptr) {
+ BKE_object_defgroup_data_create(static_cast<ID *>(ob->data));
}
/* call another function to do the work
@@ -891,37 +905,37 @@ void ED_vgroup_vert_remove(Object *ob, bDeformGroup *dg, int vertnum)
* deform group.
*/
- /* TODO(campbell): This is slow in a loop, better pass def_nr directly,
+ /* TODO(@campbellbarton): This is slow in a loop, better pass def_nr directly,
* but leave for later. */
const ListBase *defbase = BKE_object_defgroup_list(ob);
const int def_nr = BLI_findindex(defbase, dg);
if (def_nr != -1) {
- MDeformVert *dvert = NULL;
+ MDeformVert *dvert = nullptr;
int tot;
/* get the deform vertices corresponding to the
* vertnum
*/
- BKE_object_defgroup_array_get(ob->data, &dvert, &tot);
+ BKE_object_defgroup_array_get(static_cast<ID *>(ob->data), &dvert, &tot);
if (dvert) {
MDeformVert *dv = &dvert[vertnum];
MDeformWeight *dw;
dw = BKE_defvert_find_index(dv, def_nr);
- BKE_defvert_remove_group(dv, dw); /* dw can be NULL */
+ BKE_defvert_remove_group(dv, dw); /* dw can be nullptr */
}
}
}
static float get_vert_def_nr(Object *ob, const int def_nr, const int vertnum)
{
- MDeformVert *dv = NULL;
+ const MDeformVert *dv = nullptr;
/* get the deform vertices corresponding to the vertnum */
if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (me->edit_mesh) {
BMEditMesh *em = me->edit_mesh;
@@ -932,18 +946,19 @@ static float get_vert_def_nr(Object *ob, const int def_nr, const int vertnum)
BMVert *eve;
BM_mesh_elem_table_ensure(em->bm, BM_VERT);
eve = BM_vert_at_index(em->bm, vertnum);
- dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
+ dv = static_cast<const MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
}
else {
return 0.0f;
}
}
else {
- if (me->dvert) {
+ const Span<MDeformVert> dverts = me->deform_verts();
+ if (!dverts.is_empty()) {
if (vertnum >= me->totvert) {
return 0.0f;
}
- dv = &me->dvert[vertnum];
+ dv = &dverts[vertnum];
}
}
}
@@ -1004,7 +1019,7 @@ static void vgroup_select_verts(Object *ob, int select)
}
if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (me->edit_mesh) {
BMEditMesh *em = me->edit_mesh;
@@ -1016,7 +1031,8 @@ static void vgroup_select_verts(Object *ob, int select)
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
- MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
+ MDeformVert *dv = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
if (BKE_defvert_find_index(dv, def_nr)) {
BM_vert_select_set(em->bm, eve, select);
}
@@ -1033,17 +1049,18 @@ static void vgroup_select_verts(Object *ob, int select)
}
}
else {
- if (me->dvert) {
+ const Span<MDeformVert> dverts = me->deform_verts();
+ if (!dverts.is_empty()) {
+ const bool *hide_vert = (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert");
MVert *mv;
- MDeformVert *dv;
int i;
- mv = me->mvert;
- dv = me->dvert;
+ mv = me->verts_for_write().data();
- for (i = 0; i < me->totvert; i++, mv++, dv++) {
- if (!(mv->flag & ME_HIDE)) {
- if (BKE_defvert_find_index(dv, def_nr)) {
+ for (i = 0; i < me->totvert; i++, mv++) {
+ if (!(hide_vert != nullptr && hide_vert[i])) {
+ if (BKE_defvert_find_index(&dverts[i], def_nr)) {
if (select) {
mv->flag |= SELECT;
}
@@ -1091,12 +1108,13 @@ static void vgroup_duplicate(Object *ob)
bDeformGroup *dg, *cdg;
char name[sizeof(dg->name)];
MDeformWeight *dw_org, *dw_cpy;
- MDeformVert **dvert_array = NULL;
+ MDeformVert **dvert_array = nullptr;
int i, idg, icdg, dvert_tot = 0;
ListBase *defbase = BKE_object_defgroup_list_mutable(ob);
- dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1);
+ dg = static_cast<bDeformGroup *>(
+ BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1));
if (!dg) {
return;
}
@@ -1118,8 +1136,8 @@ static void vgroup_duplicate(Object *ob)
BKE_object_defgroup_active_index_set(ob, BLI_listbase_count(defbase));
icdg = BKE_object_defgroup_active_index_get(ob) - 1;
- /* TODO(campbell): we might want to allow only copy selected verts here? */
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false);
+ /* TODO(@campbellbarton): we might want to allow only copy selected verts here? */
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, false);
if (dvert_array) {
for (i = 0; i < dvert_tot; i++) {
@@ -1140,7 +1158,7 @@ static void vgroup_duplicate(Object *ob)
static bool vgroup_normalize(Object *ob)
{
MDeformWeight *dw;
- MDeformVert *dv, **dvert_array = NULL;
+ MDeformVert *dv, **dvert_array = nullptr;
int dvert_tot = 0;
const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1;
@@ -1151,7 +1169,7 @@ static bool vgroup_normalize(Object *ob)
return false;
}
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel);
if (dvert_array) {
float weight_max = 0.0f;
@@ -1198,21 +1216,18 @@ static bool vgroup_normalize(Object *ob)
/* This finds all of the vertices face-connected to vert by an edge and returns a
* MEM_allocated array of indices of size count.
* count is an int passed by reference so it can be assigned the value of the length here. */
-static int *getSurroundingVerts(Mesh *me, int vert, int *count)
+static blender::Vector<int> getSurroundingVerts(Mesh *me, int vert)
{
- MPoly *mp = me->mpoly;
+ const MPoly *mp = me->polys().data();
+ const MLoop *loops = me->loops().data();
int i = me->totpoly;
- /* Instead of looping twice on all polys and loops, and use a temp array, let's rather
- * use a BLI_array, with a reasonable starting/reserved size (typically, there are not
- * many vertices face-linked to another one, even 8 might be too high...). */
- int *verts = NULL;
- BLI_array_declare(verts);
- BLI_array_reserve(verts, 8);
+ blender::Vector<int> verts;
+
while (i--) {
int j = mp->totloop;
int first_l = mp->totloop - 1;
- MLoop *ml = &me->mloop[mp->loopstart];
+ const MLoop *ml = &loops[mp->loopstart];
while (j--) {
/* XXX This assume a vert can only be once in a poly, even though
* it seems logical to me, not totally sure of that. */
@@ -1226,7 +1241,7 @@ static int *getSurroundingVerts(Mesh *me, int vert, int *count)
else if (!j) {
/* We are on the last corner. */
a = (ml - 1)->v;
- b = me->mloop[mp->loopstart].v;
+ b = loops[mp->loopstart].v;
}
else {
a = (ml - 1)->v;
@@ -1234,7 +1249,7 @@ static int *getSurroundingVerts(Mesh *me, int vert, int *count)
}
/* Append a and b verts to array, if not yet present. */
- k = BLI_array_len(verts);
+ k = verts.size();
/* XXX Maybe a == b is enough? */
while (k-- && !(a == b && a == -1)) {
if (verts[k] == a) {
@@ -1245,10 +1260,10 @@ static int *getSurroundingVerts(Mesh *me, int vert, int *count)
}
}
if (a != -1) {
- BLI_array_append(verts, a);
+ verts.append(a);
}
if (b != -1) {
- BLI_array_append(verts, b);
+ verts.append(b);
}
/* Vert found in this poly, we can go to next one! */
@@ -1259,8 +1274,6 @@ static int *getSurroundingVerts(Mesh *me, int vert, int *count)
mp++;
}
- /* Do not free the array! */
- *count = BLI_array_len(verts);
return verts;
}
@@ -1311,14 +1324,15 @@ static void getVerticalAndHorizontalChange(const float norm[3],
changes[index][1] = len_v3v3(projA, projB);
}
-/* by changing nonzero weights, try to move a vertex in me->mverts with index 'index' to
- * distToBe distance away from the provided plane strength can change distToBe so that it moves
- * towards distToBe by that percentage cp changes how much the weights are adjusted
+/**
+ * By changing nonzero weights, try to move a vertex in `me->mverts` with index 'index' to
+ * `distToBe` distance away from the provided plane strength can change `distToBe` so that it moves
+ * towards `distToBe` by that percentage `cp` changes how much the weights are adjusted
* to check the distance
*
- * index is the index of the vertex being moved
- * norm and d are the plane's properties for the equation: ax + by + cz + d = 0
- * coord is a point on the plane
+ * `index` is the index of the vertex being moved.
+ * `norm` and `d` are the plane's properties for the equation: `ax + by + cz + d = 0`.
+ * `coord` is a point on the plane.
*/
static void moveCloserToDistanceFromPlane(Depsgraph *depsgraph,
Scene *UNUSED(scene),
@@ -1339,20 +1353,21 @@ static void moveCloserToDistanceFromPlane(Depsgraph *depsgraph,
Mesh *me_deform;
MDeformWeight *dw, *dw_eval;
MVert m;
- MDeformVert *dvert = me->dvert + index;
- MDeformVert *dvert_eval = mesh_eval->dvert + index;
+ MDeformVert *dvert = me->deform_verts_for_write().data() + index;
+ MDeformVert *dvert_eval = mesh_eval->deform_verts_for_write().data() + index;
int totweight = dvert->totweight;
float oldw = 0;
float oldPos[3] = {0};
float vc, hc, dist = 0.0f;
int i, k;
- float(*changes)[2] = MEM_mallocN(sizeof(float[2]) * totweight, "vertHorzChange");
- float *dists = MEM_mallocN(sizeof(float) * totweight, "distance");
+ float(*changes)[2] = static_cast<float(*)[2]>(
+ MEM_mallocN(sizeof(float[2]) * totweight, "vertHorzChange"));
+ float *dists = static_cast<float *>(MEM_mallocN(sizeof(float) * totweight, "distance"));
/* track if up or down moved it closer for each bone */
- bool *upDown = MEM_callocN(sizeof(bool) * totweight, "upDownTracker");
+ bool *upDown = static_cast<bool *>(MEM_callocN(sizeof(bool) * totweight, "upDownTracker"));
- int *dwIndices = MEM_callocN(sizeof(int) * totweight, "dwIndexTracker");
+ int *dwIndices = static_cast<int *>(MEM_callocN(sizeof(int) * totweight, "dwIndexTracker"));
float distToStart;
int bestIndex = 0;
bool wasChange;
@@ -1362,7 +1377,8 @@ static void moveCloserToDistanceFromPlane(Depsgraph *depsgraph,
do {
wasChange = false;
me_deform = mesh_get_eval_deform(depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH);
- m = me_deform->mvert[index];
+ const Span<MVert> verts = me_deform->verts();
+ m = verts[index];
copy_v3_v3(oldPos, m.co);
distToStart = dot_v3v3(norm, oldPos) + d;
@@ -1404,7 +1420,7 @@ static void moveCloserToDistanceFromPlane(Depsgraph *depsgraph,
}
dw_eval->weight = dw->weight;
me_deform = mesh_get_eval_deform(depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH);
- m = me_deform->mvert[index];
+ m = verts[index];
getVerticalAndHorizontalChange(
norm, d, coord, oldPos, distToStart, m.co, changes, dists, i);
dw->weight = oldw;
@@ -1508,25 +1524,26 @@ static void vgroup_fix(
Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
int i;
- Mesh *me = ob->data;
- MVert *mvert = me->mvert;
- int *verts = NULL;
+ Mesh *me = static_cast<Mesh *>(ob->data);
+ MVert *mvert = me->verts_for_write().data();
if (!(me->editflag & ME_EDIT_PAINT_VERT_SEL)) {
return;
}
for (i = 0; i < me->totvert && mvert; i++, mvert++) {
if (mvert->flag & SELECT) {
- int count = 0;
- if ((verts = getSurroundingVerts(me, i, &count))) {
+ blender::Vector<int> verts = getSurroundingVerts(me, i);
+ const int count = verts.size();
+ if (!verts.is_empty()) {
MVert m;
- MVert *p = MEM_callocN(sizeof(MVert) * (count), "deformedPoints");
+ MVert *p = static_cast<MVert *>(MEM_callocN(sizeof(MVert) * (count), "deformedPoints"));
int k;
Mesh *me_deform = mesh_get_eval_deform(
depsgraph, scene_eval, object_eval, &CD_MASK_BAREMESH);
+ const Span<MVert> verts_deform = me_deform->verts();
k = count;
while (k--) {
- p[k] = me_deform->mvert[verts[k]];
+ p[k] = verts_deform[verts[k]];
}
if (count >= 3) {
@@ -1534,7 +1551,7 @@ static void vgroup_fix(
float coord[3];
float norm[3];
getSingleCoordinate(p, count, coord);
- m = me_deform->mvert[i];
+ m = verts_deform[i];
sub_v3_v3v3(norm, m.co, coord);
mag = normalize_v3(norm);
if (mag) { /* zeros fix */
@@ -1545,7 +1562,6 @@ static void vgroup_fix(
}
}
- MEM_freeN(verts);
MEM_freeN(p);
}
}
@@ -1560,7 +1576,7 @@ static void vgroup_levels_subset(Object *ob,
const float gain)
{
MDeformWeight *dw;
- MDeformVert *dv, **dvert_array = NULL;
+ MDeformVert *dv, **dvert_array = nullptr;
int dvert_tot = 0;
const bool use_vert_sel = vertex_group_use_vert_sel(ob);
@@ -1568,7 +1584,7 @@ static void vgroup_levels_subset(Object *ob,
(((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 :
false;
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel);
if (dvert_array) {
@@ -1606,7 +1622,7 @@ static bool vgroup_normalize_all(Object *ob,
const bool lock_active,
ReportList *reports)
{
- MDeformVert *dv, **dvert_array = NULL;
+ MDeformVert *dv, **dvert_array = nullptr;
int i, dvert_tot = 0;
const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1;
@@ -1617,7 +1633,7 @@ static bool vgroup_normalize_all(Object *ob,
return false;
}
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel);
if (dvert_array) {
const ListBase *defbase = BKE_object_defgroup_list(ob);
@@ -1625,7 +1641,7 @@ static bool vgroup_normalize_all(Object *ob,
bool *lock_flags = BKE_object_defgroup_lock_flags_get(ob, defbase_tot);
bool changed = false;
- if ((lock_active == true) && (lock_flags != NULL) && (def_nr < defbase_tot)) {
+ if ((lock_active == true) && (lock_flags != nullptr) && (def_nr < defbase_tot)) {
lock_flags[def_nr] = true;
}
@@ -1688,7 +1704,7 @@ static const EnumPropertyItem vgroup_lock_actions[] = {
{VGROUP_LOCK, "LOCK", 0, "Lock", "Lock all vertex groups"},
{VGROUP_UNLOCK, "UNLOCK", 0, "Unlock", "Unlock all vertex groups"},
{VGROUP_INVERT, "INVERT", 0, "Invert", "Invert the lock state of all vertex groups"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
enum {
@@ -1707,7 +1723,7 @@ static const EnumPropertyItem vgroup_lock_mask[] = {
0,
"Invert Unselected",
"Apply the opposite of Lock/Unlock to unselected vertex groups"},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static bool *vgroup_selected_get(Object *ob)
@@ -1726,7 +1742,7 @@ static bool *vgroup_selected_get(Object *ob)
}
}
else {
- mask = MEM_callocN(defbase_tot * sizeof(bool), __func__);
+ mask = static_cast<bool *>(MEM_callocN(defbase_tot * sizeof(bool), __func__));
}
const int actdef = BKE_object_defgroup_active_index_get(ob);
@@ -1740,7 +1756,7 @@ static bool *vgroup_selected_get(Object *ob)
static void vgroup_lock_all(Object *ob, int action, int mask)
{
bDeformGroup *dg;
- bool *selected = NULL;
+ bool *selected = nullptr;
int i;
if (mask != VGROUP_MASK_ALL) {
@@ -1751,7 +1767,7 @@ static void vgroup_lock_all(Object *ob, int action, int mask)
if (action == VGROUP_TOGGLE) {
action = VGROUP_LOCK;
- for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) {
+ for (dg = static_cast<bDeformGroup *>(defbase->first), i = 0; dg; dg = dg->next, i++) {
switch (mask) {
case VGROUP_MASK_INVERT_UNSELECTED:
case VGROUP_MASK_SELECTED:
@@ -1764,7 +1780,8 @@ static void vgroup_lock_all(Object *ob, int action, int mask)
continue;
}
break;
- default:;
+ default:
+ break;
}
if (dg->flag & DG_LOCK_WEIGHT) {
@@ -1774,7 +1791,7 @@ static void vgroup_lock_all(Object *ob, int action, int mask)
}
}
- for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) {
+ for (dg = static_cast<bDeformGroup *>(defbase->first), i = 0; dg; dg = dg->next, i++) {
switch (mask) {
case VGROUP_MASK_SELECTED:
if (!selected[i]) {
@@ -1786,7 +1803,8 @@ static void vgroup_lock_all(Object *ob, int action, int mask)
continue;
}
break;
- default:;
+ default:
+ break;
}
switch (action) {
@@ -1819,14 +1837,14 @@ static void vgroup_invert_subset(Object *ob,
const bool auto_remove)
{
MDeformWeight *dw;
- MDeformVert *dv, **dvert_array = NULL;
+ MDeformVert *dv, **dvert_array = nullptr;
int dvert_tot = 0;
const bool use_vert_sel = vertex_group_use_vert_sel(ob);
const bool use_mirror = (ob->type == OB_MESH) ?
(((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 :
false;
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel);
if (dvert_array) {
for (int i = 0; i < dvert_tot; i++) {
@@ -1876,10 +1894,10 @@ static void vgroup_smooth_subset(Object *ob,
const float fac_expand)
{
const float ifac = 1.0f - fac;
- MDeformVert **dvert_array = NULL;
+ MDeformVert **dvert_array = nullptr;
int dvert_tot = 0;
- int *vgroup_subset_map = BLI_array_alloca(vgroup_subset_map, subset_count);
- float *vgroup_subset_weights = BLI_array_alloca(vgroup_subset_weights, subset_count);
+ blender::Array<int, 32> vgroup_subset_map(subset_count);
+ blender::Array<float, 32> vgroup_subset_weights(subset_count);
const bool use_mirror = (ob->type == OB_MESH) ?
(((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 :
false;
@@ -1891,8 +1909,8 @@ static void vgroup_smooth_subset(Object *ob,
const float iexpand = 1.0f - expand;
BMEditMesh *em = BKE_editmesh_from_object(ob);
- BMesh *bm = em ? em->bm : NULL;
- Mesh *me = em ? NULL : ob->data;
+ BMesh *bm = em ? em->bm : nullptr;
+ Mesh *me = em ? nullptr : static_cast<Mesh *>(ob->data);
MeshElemMap *emap;
int *emap_mem;
@@ -1906,31 +1924,37 @@ static void vgroup_smooth_subset(Object *ob,
uint *verts_used;
STACK_DECLARE(verts_used);
- BKE_object_defgroup_subset_to_index_array(vgroup_validmap, vgroup_tot, vgroup_subset_map);
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false);
- memset(vgroup_subset_weights, 0, sizeof(*vgroup_subset_weights) * subset_count);
+ BKE_object_defgroup_subset_to_index_array(vgroup_validmap, vgroup_tot, vgroup_subset_map.data());
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, false);
+ vgroup_subset_weights.fill(0.0f);
if (bm) {
BM_mesh_elem_table_ensure(bm, BM_VERT);
BM_mesh_elem_index_ensure(bm, BM_VERT);
- emap = NULL;
- emap_mem = NULL;
+ emap = nullptr;
+ emap_mem = nullptr;
}
else {
- BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge);
+ BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->edges().data(), me->totvert, me->totedge);
}
- weight_accum_prev = MEM_mallocN(sizeof(*weight_accum_prev) * dvert_tot, __func__);
- weight_accum_curr = MEM_mallocN(sizeof(*weight_accum_curr) * dvert_tot, __func__);
+ weight_accum_prev = static_cast<float *>(
+ MEM_mallocN(sizeof(*weight_accum_prev) * dvert_tot, __func__));
+ weight_accum_curr = static_cast<float *>(
+ MEM_mallocN(sizeof(*weight_accum_curr) * dvert_tot, __func__));
- verts_used = MEM_mallocN(sizeof(*verts_used) * dvert_tot, __func__);
+ verts_used = static_cast<uint *>(MEM_mallocN(sizeof(*verts_used) * dvert_tot, __func__));
STACK_INIT(verts_used, dvert_tot);
#define IS_BM_VERT_READ(v) (use_hide ? (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == 0) : true)
#define IS_BM_VERT_WRITE(v) (use_select ? (BM_elem_flag_test(v, BM_ELEM_SELECT) != 0) : true)
-#define IS_ME_VERT_READ(v) (use_hide ? (((v)->flag & ME_HIDE) == 0) : true)
+ const bool *hide_vert = me ? (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert") :
+ nullptr;
+
+#define IS_ME_VERT_READ(v) (use_hide ? !(hide_vert && hide_vert[v]) : true)
#define IS_ME_VERT_WRITE(v) (use_select ? (((v)->flag & SELECT) != 0) : true)
/* initialize used verts */
@@ -1951,13 +1975,15 @@ static void vgroup_smooth_subset(Object *ob,
}
}
else {
+ const Span<MVert> verts = me->verts();
+ const blender::Span<MEdge> edges = me->edges();
for (int i = 0; i < dvert_tot; i++) {
- const MVert *v = &me->mvert[i];
+ const MVert *v = &verts[i];
if (IS_ME_VERT_WRITE(v)) {
for (int j = 0; j < emap[i].count; j++) {
- const MEdge *e = &me->medge[emap[i].indices[j]];
- const MVert *v_other = &me->mvert[(e->v1 == i) ? e->v2 : e->v1];
- if (IS_ME_VERT_READ(v_other)) {
+ const MEdge *e = &edges[emap[i].indices[j]];
+ const int i_other = (e->v1 == i) ? e->v2 : e->v1;
+ if (IS_ME_VERT_READ(i_other)) {
STACK_PUSH(verts_used, i);
break;
}
@@ -2024,16 +2050,15 @@ static void vgroup_smooth_subset(Object *ob,
}
else {
int j;
+ const blender::Span<MEdge> edges = me->edges();
/* checked already */
- BLI_assert(IS_ME_VERT_WRITE(&me->mvert[i]));
+ BLI_assert(IS_ME_VERT_WRITE(&me->verts()[i]));
for (j = 0; j < emap[i].count; j++) {
- MEdge *e = &me->medge[emap[i].indices[j]];
+ const MEdge *e = &edges[emap[i].indices[j]];
const int i_other = (e->v1 == i ? e->v2 : e->v1);
- MVert *v_other = &me->mvert[i_other];
-
- if (IS_ME_VERT_READ(v_other)) {
+ if (IS_ME_VERT_READ(i_other)) {
WEIGHT_ACCUMULATE;
}
}
@@ -2078,9 +2103,9 @@ static void vgroup_smooth_subset(Object *ob,
MEM_freeN(dvert_array);
}
- /* not so efficient to get 'dvert_array' again just so unselected verts are NULL'd */
+ /* not so efficient to get 'dvert_array' again just so unselected verts are nullptr'd */
if (use_mirror) {
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, true);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, true);
ED_vgroup_parray_mirror_sync(ob, dvert_array, dvert_tot, vgroup_validmap, vgroup_tot);
if (dvert_array) {
MEM_freeN(dvert_array);
@@ -2096,7 +2121,8 @@ static int inv_cmp_mdef_vert_weights(const void *a1, const void *a2)
* less than, equal to, or greater than zero corresponding to whether its first argument is
* considered less than, equal to, or greater than its second argument.
* This does the opposite. */
- const struct MDeformWeight *dw1 = a1, *dw2 = a2;
+ const MDeformWeight *dw1 = static_cast<const MDeformWeight *>(a1);
+ const MDeformWeight *dw2 = static_cast<const MDeformWeight *>(a2);
if (dw1->weight < dw2->weight) {
return 1;
@@ -2120,12 +2146,12 @@ static int vgroup_limit_total_subset(Object *ob,
const int subset_count,
const int max_weights)
{
- MDeformVert *dv, **dvert_array = NULL;
+ MDeformVert *dv, **dvert_array = nullptr;
int i, dvert_tot = 0;
const bool use_vert_sel = vertex_group_use_vert_sel(ob);
int remove_tot = 0;
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel);
if (dvert_array) {
int num_to_drop = 0;
@@ -2147,7 +2173,8 @@ static int vgroup_limit_total_subset(Object *ob,
if (num_to_drop > 0) {
/* re-pack dw array so that non-bone weights are first, bone-weighted verts at end
* sort the tail, then copy only the truncated array back to dv->dw */
- dw_temp = MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, __func__);
+ dw_temp = static_cast<MDeformWeight *>(
+ MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, __func__));
bone_count = 0;
non_bone_count = 0;
for (j = 0; j < dv->totweight; j++) {
@@ -2170,7 +2197,8 @@ static int vgroup_limit_total_subset(Object *ob,
dv->totweight -= num_to_drop;
/* Do we want to clean/normalize here? */
MEM_freeN(dv->dw);
- dv->dw = MEM_reallocN(dw_temp, sizeof(MDeformWeight) * dv->totweight);
+ dv->dw = static_cast<MDeformWeight *>(
+ MEM_reallocN(dw_temp, sizeof(MDeformWeight) * dv->totweight));
remove_tot += num_to_drop;
}
else {
@@ -2191,14 +2219,14 @@ static void vgroup_clean_subset(Object *ob,
const float epsilon,
const bool keep_single)
{
- MDeformVert **dvert_array = NULL;
+ MDeformVert **dvert_array = nullptr;
int dvert_tot = 0;
const bool use_vert_sel = vertex_group_use_vert_sel(ob);
const bool use_mirror = (ob->type == OB_MESH) ?
(((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 :
false;
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel);
if (dvert_array) {
if (use_mirror && use_vert_sel) {
@@ -2221,13 +2249,13 @@ static void vgroup_quantize_subset(Object *ob,
const int UNUSED(subset_count),
const int steps)
{
- MDeformVert **dvert_array = NULL;
+ MDeformVert **dvert_array = nullptr;
int dvert_tot = 0;
const bool use_vert_sel = vertex_group_use_vert_sel(ob);
const bool use_mirror = (ob->type == OB_MESH) ?
(((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 :
false;
- ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel);
+ ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel);
if (dvert_array) {
const float steps_fl = steps;
@@ -2348,9 +2376,9 @@ void ED_vgroup_mirror(Object *ob,
def_nr)
BMVert *eve, *eve_mirr;
- MDeformVert *dvert, *dvert_mirr;
+ MDeformVert *dvert_mirr;
char sel, sel_mirr;
- int *flip_map = NULL, flip_map_len;
+ int *flip_map = nullptr, flip_map_len;
const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1;
int totmirr = 0, totfail = 0;
@@ -2359,29 +2387,29 @@ void ED_vgroup_mirror(Object *ob,
const ListBase *defbase = BKE_object_defgroup_list(ob);
if ((mirror_weights == false && flip_vgroups == false) ||
- (BLI_findlink(defbase, def_nr) == NULL)) {
+ (BLI_findlink(defbase, def_nr) == nullptr)) {
return;
}
if (flip_vgroups) {
- flip_map = all_vgroups ? BKE_object_defgroup_flip_map(ob, &flip_map_len, false) :
- BKE_object_defgroup_flip_map_single(ob, &flip_map_len, false, def_nr);
+ flip_map = all_vgroups ? BKE_object_defgroup_flip_map(ob, false, &flip_map_len) :
+ BKE_object_defgroup_flip_map_single(ob, false, def_nr, &flip_map_len);
- BLI_assert(flip_map != NULL);
+ BLI_assert(flip_map != nullptr);
- if (flip_map == NULL) {
+ if (flip_map == nullptr) {
/* something went wrong!, possibly no groups */
return;
}
}
else {
- flip_map = NULL;
+ flip_map = nullptr;
flip_map_len = 0;
}
/* only the active group */
if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BMEditMesh *em = me->edit_mesh;
if (em) {
@@ -2406,8 +2434,10 @@ void ED_vgroup_mirror(Object *ob,
sel_mirr = BM_elem_flag_test(eve_mirr, BM_ELEM_SELECT);
if ((sel || sel_mirr) && (eve != eve_mirr)) {
- dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
- dvert_mirr = BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset);
+ MDeformVert *dvert = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
+ dvert_mirr = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset));
VGROUP_MIRR_OP;
totmirr++;
@@ -2428,11 +2458,11 @@ void ED_vgroup_mirror(Object *ob,
}
else {
/* object mode / weight paint */
- MVert *mv, *mv_mirr;
+ const MVert *mv, *mv_mirr;
int vidx, vidx_mirr;
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
- if (me->dvert == NULL) {
+ if (me->deform_verts().is_empty()) {
goto cleanup;
}
@@ -2441,12 +2471,14 @@ void ED_vgroup_mirror(Object *ob,
}
BLI_bitmap *vert_tag = BLI_BITMAP_NEW(me->totvert, __func__);
+ const MVert *verts = me->verts().data();
+ MutableSpan<MDeformVert> dverts = me->deform_verts_for_write();
- for (vidx = 0, mv = me->mvert; vidx < me->totvert; vidx++, mv++) {
+ for (vidx = 0, mv = verts; vidx < me->totvert; vidx++, mv++) {
if (!BLI_BITMAP_TEST(vert_tag, vidx)) {
- if ((vidx_mirr = mesh_get_x_mirror_vert(ob, NULL, vidx, use_topology)) != -1) {
+ if ((vidx_mirr = mesh_get_x_mirror_vert(ob, nullptr, vidx, use_topology)) != -1) {
if (vidx != vidx_mirr) {
- mv_mirr = &me->mvert[vidx_mirr];
+ mv_mirr = &verts[vidx_mirr];
if (!BLI_BITMAP_TEST(vert_tag, vidx_mirr)) {
if (use_vert_sel) {
@@ -2455,8 +2487,8 @@ void ED_vgroup_mirror(Object *ob,
}
if (sel || sel_mirr) {
- dvert = &me->dvert[vidx];
- dvert_mirr = &me->dvert[vidx_mirr];
+ MDeformVert *dvert = &dverts[vidx];
+ dvert_mirr = &dvert[vidx_mirr];
VGROUP_MIRR_OP;
totmirr++;
@@ -2483,7 +2515,7 @@ void ED_vgroup_mirror(Object *ob,
int pntsu_half;
/* half but found up odd value */
- if (lt->pntsu == 1 || lt->dvert == NULL) {
+ if (lt->pntsu == 1 || lt->dvert == nullptr) {
goto cleanup;
}
@@ -2509,7 +2541,7 @@ void ED_vgroup_mirror(Object *ob,
sel_mirr = bp_mirr->f1 & SELECT;
if (sel || sel_mirr) {
- dvert = &lt->dvert[i1];
+ MDeformVert *dvert = &lt->dvert[i1];
dvert_mirr = &lt->dvert[i2];
VGROUP_MIRR_OP;
@@ -2543,7 +2575,8 @@ cleanup:
static void vgroup_delete_active(Object *ob)
{
const ListBase *defbase = BKE_object_defgroup_list(ob);
- bDeformGroup *dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1);
+ bDeformGroup *dg = static_cast<bDeformGroup *>(
+ BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1));
if (!dg) {
return;
}
@@ -2562,7 +2595,7 @@ static void vgroup_assign_verts(Object *ob, const float weight)
}
if (ob->type == OB_MESH) {
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
if (me->edit_mesh) {
BMEditMesh *em = me->edit_mesh;
@@ -2582,7 +2615,8 @@ static void vgroup_assign_verts(Object *ob, const float weight)
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
MDeformVert *dv;
MDeformWeight *dw;
- dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); /* can be NULL */
+ dv = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); /* can be nullptr */
dw = BKE_defvert_ensure_index(dv, def_nr);
if (dw) {
dw->weight = weight;
@@ -2591,14 +2625,12 @@ static void vgroup_assign_verts(Object *ob, const float weight)
}
}
else {
- if (!me->dvert) {
- BKE_object_defgroup_data_create(&me->id);
- }
+ const Span<MVert> verts = me->verts();
+ MutableSpan<MDeformVert> dverts = me->deform_verts_for_write();
+ MDeformVert *dv = dverts.data();
- MVert *mv = me->mvert;
- MDeformVert *dv = me->dvert;
-
- for (int i = 0; i < me->totvert; i++, mv++, dv++) {
+ for (int i = 0; i < me->totvert; i++, dv++) {
+ const MVert *mv = &verts[i];
if (mv->flag & SELECT) {
MDeformWeight *dw;
dw = BKE_defvert_ensure_index(dv, def_nr);
@@ -2615,7 +2647,7 @@ static void vgroup_assign_verts(Object *ob, const float weight)
BPoint *bp;
int a, tot;
- if (lt->dvert == NULL) {
+ if (lt->dvert == nullptr) {
BKE_object_defgroup_data_create(&lt->id);
}
@@ -2654,8 +2686,8 @@ static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob)
}
/* Data checks. */
- const ID *data = ob->data;
- if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) {
+ const ID *data = static_cast<const ID *>(ob->data);
+ if (data == nullptr || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) {
CTX_wm_operator_poll_msg_set(C, "Object type \"%s\" does not have editable data");
return false;
}
@@ -2711,8 +2743,8 @@ static bool vertex_group_mesh_with_dvert_poll(bContext *C)
return false;
}
- Mesh *me = ob->data;
- if (me->dvert == NULL) {
+ Mesh *me = static_cast<Mesh *>(ob->data);
+ if (me->deform_verts().is_empty()) {
CTX_wm_operator_poll_msg_set(C, "The active mesh object has no vertex group data");
return false;
}
@@ -2802,7 +2834,7 @@ static bool vertex_group_vert_select_unlocked_poll(bContext *C)
const int def_nr = BKE_object_defgroup_active_index_get(ob);
if (def_nr != 0) {
const ListBase *defbase = BKE_object_defgroup_list(ob);
- const bDeformGroup *dg = BLI_findlink(defbase, def_nr - 1);
+ const bDeformGroup *dg = static_cast<const bDeformGroup *>(BLI_findlink(defbase, def_nr - 1));
if (dg) {
return !(dg->flag & DG_LOCK_WEIGHT);
}
@@ -2906,10 +2938,10 @@ void OBJECT_OT_vertex_group_remove(wmOperatorType *ot)
ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO;
/* properties */
- PropertyRNA *prop = RNA_def_boolean(ot->srna, "all", 0, "All", "Remove all vertex groups");
+ PropertyRNA *prop = RNA_def_boolean(ot->srna, "all", false, "All", "Remove all vertex groups");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(
- ot->srna, "all_unlocked", 0, "All Unlocked", "Remove all unlocked vertex groups");
+ ot->srna, "all_unlocked", false, "All Unlocked", "Remove all unlocked vertex groups");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@@ -3004,8 +3036,9 @@ static int vertex_group_remove_from_exec(bContext *C, wmOperator *op)
}
else {
const ListBase *defbase = BKE_object_defgroup_list(ob);
- bDeformGroup *dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1);
- if ((dg == NULL) || (BKE_object_defgroup_clear(ob, dg, !use_all_verts) == false)) {
+ bDeformGroup *dg = static_cast<bDeformGroup *>(
+ BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1));
+ if ((dg == nullptr) || (BKE_object_defgroup_clear(ob, dg, !use_all_verts) == false)) {
return OPERATOR_CANCELLED;
}
}
@@ -3035,9 +3068,11 @@ void OBJECT_OT_vertex_group_remove_from(wmOperatorType *ot)
ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO;
/* properties */
- prop = RNA_def_boolean(ot->srna, "use_all_groups", 0, "All Groups", "Remove from all groups");
+ prop = RNA_def_boolean(
+ ot->srna, "use_all_groups", false, "All Groups", "Remove from all groups");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "use_all_verts", 0, "All Vertices", "Clear the active group");
+ prop = RNA_def_boolean(
+ ot->srna, "use_all_verts", false, "All Vertices", "Clear the active group");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@@ -3056,7 +3091,7 @@ static int vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op))
}
vgroup_select_verts(ob, 1);
- DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
return OPERATOR_FINISHED;
@@ -3088,7 +3123,7 @@ static int vertex_group_deselect_exec(bContext *C, wmOperator *UNUSED(op))
Object *ob = ED_object_context(C);
vgroup_select_verts(ob, 0);
- DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
return OPERATOR_FINISHED;
@@ -3155,7 +3190,8 @@ static int vertex_group_levels_exec(bContext *C, wmOperator *op)
float offset = RNA_float_get(op->ptr, "offset");
float gain = RNA_float_get(op->ptr, "gain");
- eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ eVGroupSelect subset_type = static_cast<eVGroupSelect>(
+ RNA_enum_get(op->ptr, "group_select_mode"));
int subset_count, vgroup_tot;
@@ -3242,7 +3278,8 @@ static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op)
{
Object *ob = ED_object_context(C);
bool lock_active = RNA_boolean_get(op->ptr, "lock_active");
- eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ eVGroupSelect subset_type = static_cast<eVGroupSelect>(
+ RNA_enum_get(op->ptr, "group_select_mode"));
bool changed;
int subset_count, vgroup_tot;
const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type(
@@ -3302,7 +3339,7 @@ static int vertex_group_fix_exec(bContext *C, wmOperator *op)
float distToBe = RNA_float_get(op->ptr, "dist");
float strength = RNA_float_get(op->ptr, "strength");
float cp = RNA_float_get(op->ptr, "accuracy");
- ModifierData *md = ob->modifiers.first;
+ ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first);
while (md) {
if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) {
@@ -3391,9 +3428,9 @@ static int vertex_group_lock_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static char *vertex_group_lock_description(struct bContext *UNUSED(C),
- struct wmOperatorType *UNUSED(op),
- struct PointerRNA *params)
+static char *vertex_group_lock_description(bContext *UNUSED(C),
+ wmOperatorType *UNUSED(op),
+ PointerRNA *params)
{
int action = RNA_enum_get(params, "action");
int mask = RNA_enum_get(params, "mask");
@@ -3414,7 +3451,7 @@ static char *vertex_group_lock_description(struct bContext *UNUSED(C),
action_str = TIP_("Invert locks of");
break;
default:
- return NULL;
+ return nullptr;
}
switch (mask) {
@@ -3443,7 +3480,7 @@ static char *vertex_group_lock_description(struct bContext *UNUSED(C),
}
break;
default:
- return NULL;
+ return nullptr;
}
return BLI_sprintfN(TIP_("%s %s vertex groups of the active object"), action_str, target_str);
@@ -3491,7 +3528,8 @@ static int vertex_group_invert_exec(bContext *C, wmOperator *op)
bool auto_assign = RNA_boolean_get(op->ptr, "auto_assign");
bool auto_remove = RNA_boolean_get(op->ptr, "auto_remove");
- eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ eVGroupSelect subset_type = static_cast<eVGroupSelect>(
+ RNA_enum_get(op->ptr, "group_select_mode"));
int subset_count, vgroup_tot;
@@ -3544,7 +3582,8 @@ static int vertex_group_smooth_exec(bContext *C, wmOperator *op)
{
const float fac = RNA_float_get(op->ptr, "factor");
const int repeat = RNA_int_get(op->ptr, "repeat");
- const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ const eVGroupSelect subset_type = static_cast<eVGroupSelect>(
+ RNA_enum_get(op->ptr, "group_select_mode"));
const float fac_expand = RNA_float_get(op->ptr, "expand");
uint objects_len;
@@ -3609,7 +3648,8 @@ static int vertex_group_clean_exec(bContext *C, wmOperator *op)
{
const float limit = RNA_float_get(op->ptr, "limit");
const bool keep_single = RNA_boolean_get(op->ptr, "keep_single");
- const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ const eVGroupSelect subset_type = static_cast<eVGroupSelect>(
+ RNA_enum_get(op->ptr, "group_select_mode"));
uint objects_len;
Object **objects = object_array_for_wpaint(C, &objects_len);
@@ -3676,7 +3716,8 @@ static int vertex_group_quantize_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_context(C);
const int steps = RNA_int_get(op->ptr, "steps");
- eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ eVGroupSelect subset_type = static_cast<eVGroupSelect>(
+ RNA_enum_get(op->ptr, "group_select_mode"));
int subset_count, vgroup_tot;
@@ -3719,7 +3760,8 @@ void OBJECT_OT_vertex_group_quantize(wmOperatorType *ot)
static int vertex_group_limit_total_exec(bContext *C, wmOperator *op)
{
const int limit = RNA_int_get(op->ptr, "limit");
- const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode");
+ const eVGroupSelect subset_type = static_cast<eVGroupSelect>(
+ RNA_enum_get(op->ptr, "group_select_mode"));
int remove_multi_count = 0;
uint objects_len;
@@ -3830,7 +3872,7 @@ void OBJECT_OT_vertex_group_mirror(wmOperatorType *ot)
RNA_def_boolean(
ot->srna,
"use_topology",
- 0,
+ false,
"Topology Mirror",
"Use topology based mirroring (for when both sides of mesh have matching, unique topology)");
}
@@ -3914,13 +3956,13 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
- if (C == NULL) {
+ if (C == nullptr) {
return DummyRNA_NULL_items;
}
Object *ob = ED_object_context(C);
EnumPropertyItem tmp = {0, "", 0, "", ""};
- EnumPropertyItem *item = NULL;
+ EnumPropertyItem *item = nullptr;
bDeformGroup *def;
int a, totitem = 0;
@@ -3929,7 +3971,7 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C,
}
const ListBase *defbase = BKE_object_defgroup_list(ob);
- for (a = 0, def = defbase->first; def; def = def->next, a++) {
+ for (a = 0, def = static_cast<bDeformGroup *>(defbase->first); def; def = def->next, a++) {
tmp.value = a;
tmp.icon = ICON_GROUP_VERTEX;
tmp.identifier = def->name;
@@ -3980,11 +4022,13 @@ static char *vgroup_init_remap(Object *ob)
{
const ListBase *defbase = BKE_object_defgroup_list(ob);
int defbase_tot = BLI_listbase_count(defbase);
- char *name_array = MEM_mallocN(MAX_VGROUP_NAME * sizeof(char) * defbase_tot, "sort vgroups");
+ char *name_array = static_cast<char *>(
+ MEM_mallocN(MAX_VGROUP_NAME * sizeof(char) * defbase_tot, "sort vgroups"));
char *name;
name = name_array;
- for (const bDeformGroup *def = defbase->first; def; def = def->next) {
+ for (const bDeformGroup *def = static_cast<const bDeformGroup *>(defbase->first); def;
+ def = def->next) {
BLI_strncpy(name, def->name, MAX_VGROUP_NAME);
name += MAX_VGROUP_NAME;
}
@@ -3994,20 +4038,21 @@ static char *vgroup_init_remap(Object *ob)
static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op)
{
- MDeformVert *dvert = NULL;
+ MDeformVert *dvert = nullptr;
const bDeformGroup *def;
const ListBase *defbase = BKE_object_defgroup_list(ob);
int defbase_tot = BLI_listbase_count(defbase);
/* Needs a dummy index at the start. */
- int *sort_map_update = MEM_mallocN(sizeof(int) * (defbase_tot + 1), "sort vgroups");
+ int *sort_map_update = static_cast<int *>(
+ MEM_mallocN(sizeof(int) * (defbase_tot + 1), __func__));
int *sort_map = sort_map_update + 1;
const char *name;
int i;
name = name_array;
- for (def = defbase->first, i = 0; def; def = def->next, i++) {
+ for (def = static_cast<const bDeformGroup *>(defbase->first), i = 0; def; def = def->next, i++) {
sort_map[i] = BLI_findstringindex(defbase, name, offsetof(bDeformGroup, name));
name += MAX_VGROUP_NAME;
@@ -4024,7 +4069,7 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op)
BMVert *eve;
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
- dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
+ dvert = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
if (dvert->totweight) {
BKE_defvert_remap(dvert, sort_map, defbase_tot);
}
@@ -4042,7 +4087,7 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op)
/* Grease pencil stores vertex groups separately for each stroke,
* so remap each stroke's weights separately. */
if (ob->type == OB_GPENCIL) {
- bGPdata *gpd = ob->data;
+ bGPdata *gpd = static_cast<bGPdata *>(ob->data);
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
@@ -4061,7 +4106,7 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op)
}
}
else {
- BKE_object_defgroup_array_get(ob->data, &dvert, &dvert_tot);
+ BKE_object_defgroup_array_get(static_cast<ID *>(ob->data), &dvert, &dvert_tot);
/* Create as necessary. */
if (dvert) {
@@ -4094,8 +4139,8 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op)
static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr)
{
- const bDeformGroup *def_a = def_a_ptr;
- const bDeformGroup *def_b = def_b_ptr;
+ const bDeformGroup *def_a = static_cast<const bDeformGroup *>(def_a_ptr);
+ const bDeformGroup *def_b = static_cast<const bDeformGroup *>(def_b_ptr);
return BLI_strcasecmp_natural(def_a->name, def_b->name);
}
@@ -4106,22 +4151,22 @@ static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr)
*/
static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase)
{
- if (bonebase == NULL) {
+ if (bonebase == nullptr) {
Object *armobj = BKE_modifiers_is_deformed_by_armature(ob);
- if (armobj != NULL) {
- bArmature *armature = armobj->data;
+ if (armobj != nullptr) {
+ bArmature *armature = static_cast<bArmature *>(armobj->data);
bonebase = &armature->bonebase;
}
}
ListBase *defbase = BKE_object_defgroup_list_mutable(ob);
- if (bonebase != NULL) {
+ if (bonebase != nullptr) {
Bone *bone;
- for (bone = bonebase->last; bone; bone = bone->prev) {
+ for (bone = static_cast<Bone *>(bonebase->last); bone; bone = bone->prev) {
bDeformGroup *dg = BKE_object_defgroup_find_name(ob, bone->name);
vgroup_sort_bone_hierarchy(ob, &bone->childbase);
- if (dg != NULL) {
+ if (dg != nullptr) {
BLI_remlink(defbase, dg);
BLI_addhead(defbase, dg);
}
@@ -4152,7 +4197,7 @@ static int vertex_group_sort_exec(bContext *C, wmOperator *op)
BLI_listbase_sort(defbase, vgroup_sort_name);
break;
case SORT_TYPE_BONEHIERARCHY:
- vgroup_sort_bone_hierarchy(ob, NULL);
+ vgroup_sort_bone_hierarchy(ob, nullptr);
break;
}
@@ -4176,7 +4221,7 @@ void OBJECT_OT_vertex_group_sort(wmOperatorType *ot)
static const EnumPropertyItem vgroup_sort_type[] = {
{SORT_TYPE_NAME, "NAME", 0, "Name", ""},
{SORT_TYPE_BONEHIERARCHY, "BONE_HIERARCHY", 0, "Bone Hierarchy", ""},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
ot->name = "Sort Vertex Groups";
@@ -4209,7 +4254,8 @@ static int vgroup_move_exec(bContext *C, wmOperator *op)
ListBase *defbase = BKE_object_defgroup_list_mutable(ob);
- def = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1);
+ def = static_cast<bDeformGroup *>(
+ BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1));
if (!def) {
return OPERATOR_CANCELLED;
}
@@ -4237,7 +4283,7 @@ void OBJECT_OT_vertex_group_move(wmOperatorType *ot)
static const EnumPropertyItem vgroup_slot_move[] = {
{-1, "UP", 0, "Up", ""},
{1, "DOWN", 0, "Down", ""},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
@@ -4270,7 +4316,7 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr)
{
MDeformVert *dvert_act;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
BMEditMesh *em = me->edit_mesh;
int i;
@@ -4280,13 +4326,14 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr)
BMVert *eve, *eve_act;
dvert_act = ED_mesh_active_dvert_get_em(ob, &eve_act);
- if (dvert_act == NULL) {
+ if (dvert_act == nullptr) {
return;
}
BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && (eve != eve_act)) {
- MDeformVert *dvert_dst = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
+ MDeformVert *dvert_dst = static_cast<MDeformVert *>(
+ BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
BKE_defvert_copy_index(dvert_dst, def_nr, dvert_act, def_nr);
@@ -4305,13 +4352,16 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr)
int v_act;
dvert_act = ED_mesh_active_dvert_get_ob(ob, &v_act);
- if (dvert_act == NULL) {
+ if (dvert_act == nullptr) {
return;
}
- dv = me->dvert;
+ const Span<MVert> verts = me->verts();
+ MutableSpan<MDeformVert> dverts = me->deform_verts_for_write();
+
+ dv = dverts.data();
for (i = 0; i < me->totvert; i++, dv++) {
- if ((me->mvert[i].flag & SELECT) && (dv != dvert_act)) {
+ if ((verts[i].flag & SELECT) && (dv != dvert_act)) {
BKE_defvert_copy_index(dv, def_nr, dvert_act, def_nr);
@@ -4330,7 +4380,7 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr)
static bool check_vertex_group_accessible(wmOperator *op, Object *ob, int def_nr)
{
const ListBase *defbase = BKE_object_defgroup_list(ob);
- bDeformGroup *dg = BLI_findlink(defbase, def_nr);
+ bDeformGroup *dg = static_cast<bDeformGroup *>(BLI_findlink(defbase, def_nr));
if (!dg) {
BKE_report(op->reports, RPT_ERROR, "Invalid vertex group index");
@@ -4387,7 +4437,7 @@ void OBJECT_OT_vertex_weight_paste(wmOperatorType *ot)
"Index of source weight in active vertex group",
-1,
INT_MAX);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN));
}
/** \} */
@@ -4437,7 +4487,7 @@ void OBJECT_OT_vertex_weight_delete(wmOperatorType *ot)
"Index of source weight in active vertex group",
-1,
INT_MAX);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN));
}
/** \} */
@@ -4484,7 +4534,7 @@ void OBJECT_OT_vertex_weight_set_active(wmOperatorType *ot)
"Index of source weight in active vertex group",
-1,
INT_MAX);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN));
}
/** \} */
@@ -4497,7 +4547,7 @@ static int vertex_weight_normalize_active_vertex_exec(bContext *C, wmOperator *U
{
Object *ob = ED_object_context(C);
ToolSettings *ts = CTX_data_tool_settings(C);
- eVGroupSelect subset_type = ts->vgroupsubset;
+ eVGroupSelect subset_type = static_cast<eVGroupSelect>(ts->vgroupsubset);
bool changed;
changed = vgroup_normalize_active_vertex(ob, subset_type);
@@ -4536,7 +4586,7 @@ static int vertex_weight_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
ToolSettings *ts = CTX_data_tool_settings(C);
- eVGroupSelect subset_type = ts->vgroupsubset;
+ eVGroupSelect subset_type = static_cast<eVGroupSelect>(ts->vgroupsubset);
vgroup_copy_active_to_sel(ob, subset_type);
diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt
index ee59efbc925..e56d58c2135 100644
--- a/source/blender/editors/physics/CMakeLists.txt
+++ b/source/blender/editors/physics/CMakeLists.txt
@@ -11,7 +11,6 @@ set(INC
../../makesrna
../../windowmanager
../../../../intern/clog
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
../../../../intern/mantaflow/extern
# RNA_prototypes.h
diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c
index e8ceb97ed7a..1ce90849a88 100644
--- a/source/blender/editors/physics/dynamicpaint_ops.c
+++ b/source/blender/editors/physics/dynamicpaint_ops.c
@@ -21,6 +21,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "BKE_attribute.h"
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_dynamicpaint.h"
@@ -233,7 +234,7 @@ static int output_toggle_exec(bContext *C, wmOperator *op)
ED_mesh_color_add(ob->data, name, true, true, op->reports);
}
else {
- ED_mesh_color_remove_named(ob->data, name);
+ BKE_id_attribute_remove(ob->data, name, NULL);
}
}
/* Vertex Weight Layer */
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 03f9b4eb867..99e42710b49 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -30,6 +30,7 @@
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_mesh_legacy_convert.h"
@@ -168,7 +169,8 @@ void PE_free_ptcache_edit(PTCacheEdit *edit)
int PE_minmax(
Depsgraph *depsgraph, Scene *scene, ViewLayer *view_layer, float min[3], float max[3])
{
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
ParticleSystem *psys;
ParticleSystemModifierData *psmd_eval = NULL;
@@ -1450,26 +1452,27 @@ void recalc_emitter_field(Depsgraph *UNUSED(depsgraph), Object *UNUSED(ob), Part
vec = edit->emitter_cosnos;
nor = vec + 3;
+ const MVert *verts = BKE_mesh_verts(mesh);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
-
+ MFace *mfaces = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
for (i = 0; i < totface; i++, vec += 6, nor += 6) {
- MFace *mface = &mesh->mface[i];
- MVert *mvert;
+ MFace *mface = &mfaces[i];
+ const MVert *mvert;
- mvert = &mesh->mvert[mface->v1];
+ mvert = &verts[mface->v1];
copy_v3_v3(vec, mvert->co);
copy_v3_v3(nor, vert_normals[mface->v1]);
- mvert = &mesh->mvert[mface->v2];
+ mvert = &verts[mface->v2];
add_v3_v3v3(vec, vec, mvert->co);
add_v3_v3(nor, vert_normals[mface->v2]);
- mvert = &mesh->mvert[mface->v3];
+ mvert = &verts[mface->v3];
add_v3_v3v3(vec, vec, mvert->co);
add_v3_v3(nor, vert_normals[mface->v3]);
if (mface->v4) {
- mvert = &mesh->mvert[mface->v4];
+ mvert = &verts[mface->v4];
add_v3_v3v3(vec, vec, mvert->co);
add_v3_v3(nor, vert_normals[mface->v4]);
@@ -3391,7 +3394,7 @@ static void brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)
if (brush) {
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ub(255, 255, 255, 128);
@@ -3567,7 +3570,9 @@ static void PE_mirror_x(Depsgraph *depsgraph, Scene *scene, Object *ob, int tagg
}
if (newtotpart != psys->totpart) {
- MFace *mtessface = use_dm_final_indices ? psmd_eval->mesh_final->mface : me->mface;
+ MFace *mtessface = use_dm_final_indices ?
+ (MFace *)CustomData_get_layer(&psmd_eval->mesh_final->fdata, CD_MFACE) :
+ (MFace *)CustomData_get_layer(&me->fdata, CD_MFACE);
/* allocate new arrays and copy existing */
new_pars = MEM_callocN(newtotpart * sizeof(ParticleData), "ParticleData new");
@@ -3983,7 +3988,7 @@ static void brush_puff(PEData *data, int point_index, float mouse_distance)
/* Translate (not rotate) the rest of the hair if its not selected. */
{
/* NOLINTNEXTLINE: readability-redundant-preprocessor */
-# if 0 /* kindof works but looks worse than what's below */
+# if 0 /* Kind of works but looks worse than what's below. */
/* Move the unselected point on a vector based on the
* hair direction and the offset */
@@ -4175,8 +4180,8 @@ static int particle_intersect_mesh(Depsgraph *depsgraph,
}
totface = mesh->totface;
- mface = mesh->mface;
- mvert = mesh->mvert;
+ mface = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ mvert = BKE_mesh_verts_for_write(mesh);
/* lets intersect the faces */
for (i = 0; i < totface; i++, mface++) {
diff --git a/source/blender/editors/physics/particle_edit_undo.c b/source/blender/editors/physics/particle_edit_undo.c
index 54d28b49e0c..bc9a90b5b3f 100644
--- a/source/blender/editors/physics/particle_edit_undo.c
+++ b/source/blender/editors/physics/particle_edit_undo.c
@@ -21,6 +21,7 @@
#include "BLI_utildefines.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "BKE_undo_system.h"
@@ -211,7 +212,8 @@ static bool particle_undosys_poll(struct bContext *C)
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
return (edit != NULL);
@@ -225,7 +227,8 @@ static bool particle_undosys_step_encode(struct bContext *C,
ParticleUndoStep *us = (ParticleUndoStep *)us_p;
ViewLayer *view_layer = CTX_data_view_layer(C);
us->scene_ref.ptr = CTX_data_scene(C);
- us->object_ref.ptr = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(us->scene_ref.ptr, view_layer);
+ us->object_ref.ptr = BKE_view_layer_active_object_get(view_layer);
PTCacheEdit *edit = PE_get_current(depsgraph, us->scene_ref.ptr, us->object_ref.ptr);
undoptcache_from_editcache(&us->data, edit);
return true;
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 96aea0ededf..08db03db0e9 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -23,6 +23,7 @@
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
@@ -118,7 +119,8 @@ static int particle_system_remove_exec(bContext *C, wmOperator *UNUSED(op))
*/
if (mode_orig & OB_MODE_PARTICLE_EDIT) {
if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
- if (view_layer->basact && view_layer->basact->object == ob) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (BKE_view_layer_active_object_get(view_layer) == ob) {
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, NULL);
}
}
@@ -704,7 +706,7 @@ static bool remap_hair_emitter(Depsgraph *depsgraph,
PTCacheEditKey *ekey;
BVHTreeFromMesh bvhtree = {NULL};
MFace *mface = NULL, *mf;
- MEdge *medge = NULL, *me;
+ const MEdge *medge = NULL, *me;
MVert *mvert;
Mesh *mesh, *target_mesh;
int numverts;
@@ -750,7 +752,7 @@ static bool remap_hair_emitter(Depsgraph *depsgraph,
BKE_mesh_tessface_ensure(mesh);
numverts = mesh->totvert;
- mvert = mesh->mvert;
+ mvert = BKE_mesh_verts_for_write(mesh);
/* convert to global coordinates */
for (int i = 0; i < numverts; i++) {
@@ -758,11 +760,11 @@ static bool remap_hair_emitter(Depsgraph *depsgraph,
}
if (mesh->totface != 0) {
- mface = mesh->mface;
+ mface = CustomData_get_layer(&mesh->fdata, CD_MFACE);
BKE_bvhtree_from_mesh_get(&bvhtree, mesh, BVHTREE_FROM_FACES, 2);
}
else if (mesh->totedge != 0) {
- medge = mesh->medge;
+ medge = BKE_mesh_edges(mesh);
BKE_bvhtree_from_mesh_get(&bvhtree, mesh, BVHTREE_FROM_EDGES, 2);
}
else {
diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c
index 80de8fae072..1d3cf7c36af 100644
--- a/source/blender/editors/physics/physics_fluid.c
+++ b/source/blender/editors/physics/physics_fluid.c
@@ -502,6 +502,7 @@ static void fluid_free_startjob(void *customdata, short *stop, short *do_update,
BKE_fluid_cache_free(fds, job->ob, cache_map);
#else
UNUSED_VARS(fds);
+ UNUSED_VARS(cache_map);
#endif
*do_update = true;
diff --git a/source/blender/editors/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c
index 66ae2d323fd..10d97b02066 100644
--- a/source/blender/editors/physics/rigidbody_constraint.c
+++ b/source/blender/editors/physics/rigidbody_constraint.c
@@ -16,6 +16,7 @@
#include "BKE_collection.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_report.h"
@@ -88,7 +89,7 @@ bool ED_rigidbody_constraint_add(
/* create constraint group if it doesn't already exits */
if (rbw->constraints == NULL) {
rbw->constraints = BKE_collection_add(bmain, NULL, "RigidBodyConstraints");
- id_fake_user_set(&rbw->constraints->id);
+ id_us_plus(&rbw->constraints->id);
}
/* make rigidbody constraint settings */
ob->rigidbody_constraint = BKE_rigidbody_create_constraint(scene, ob, type);
@@ -122,7 +123,8 @@ static int rigidbody_con_add_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
int type = RNA_enum_get(op->ptr, "type");
bool changed;
@@ -174,7 +176,8 @@ static int rigidbody_con_remove_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
/* apply to active object */
if (ELEM(NULL, ob, ob->rigidbody_constraint)) {
diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt
index 4b644ae826f..a91a63201c4 100644
--- a/source/blender/editors/render/CMakeLists.txt
+++ b/source/blender/editors/render/CMakeLists.txt
@@ -17,7 +17,6 @@ set(INC
../../render
../../sequencer
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/render/render_internal.cc b/source/blender/editors/render/render_internal.cc
index 157c9bc7222..7f6a14126e0 100644
--- a/source/blender/editors/render/render_internal.cc
+++ b/source/blender/editors/render/render_internal.cc
@@ -32,6 +32,7 @@
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_image_format.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
@@ -856,7 +857,7 @@ static void screen_render_cancel(bContext *C, wmOperator *op)
static void clean_viewport_memory_base(Base *base)
{
- if ((base->flag & BASE_VISIBLE_DEPSGRAPH) == 0) {
+ if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0) {
return;
}
@@ -885,9 +886,10 @@ static void clean_viewport_memory(Main *bmain, Scene *scene)
wm = static_cast<wmWindowManager *>(wm->id.next)) {
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ BKE_view_layer_synced_ensure(scene, view_layer);
- for (base = static_cast<Base *>(view_layer->object_bases.first); base; base = base->next) {
- clean_viewport_memory_base(base);
+ LISTBASE_FOREACH (Base *, b, BKE_view_layer_object_bases_get(view_layer)) {
+ clean_viewport_memory_base(b);
}
}
}
diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc
index 7bd9812a178..e91bffce2c2 100644
--- a/source/blender/editors/render/render_opengl.cc
+++ b/source/blender/editors/render/render_opengl.cc
@@ -278,19 +278,10 @@ static void screen_opengl_views_setup(OGLRender *oglrender)
static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, RenderResult *rr)
{
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = oglrender->scene;
- ARegion *region = oglrender->region;
- View3D *v3d = oglrender->v3d;
- RegionView3D *rv3d = oglrender->rv3d;
Object *camera = nullptr;
int sizex = oglrender->sizex;
int sizey = oglrender->sizey;
- const short view_context = (v3d != nullptr);
- bool draw_sky = (scene->r.alphamode == R_ADDSKY);
- float *rectf = nullptr;
- uchar *rect = nullptr;
- const char *viewname = RE_GetActiveRenderView(oglrender->re);
ImBuf *ibuf_result = nullptr;
if (oglrender->is_sequencer) {
@@ -301,7 +292,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
ImBuf *ibuf = oglrender->seq_data.ibufs_arr[oglrender->view_id];
if (ibuf) {
- ImBuf *out = IMB_dupImBuf(ibuf);
+ ibuf_result = IMB_dupImBuf(ibuf);
IMB_freeImBuf(ibuf);
/* OpenGL render is considered to be preview and should be
* as fast as possible. So currently we're making sure sequencer
@@ -310,25 +301,21 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
* TODO(sergey): In the case of output to float container (EXR)
* it actually makes sense to keep float buffer instead.
*/
- if (out->rect_float != nullptr) {
- IMB_rect_from_float(out);
- imb_freerectfloatImBuf(out);
+ if (ibuf_result->rect_float != nullptr) {
+ IMB_rect_from_float(ibuf_result);
+ imb_freerectfloatImBuf(ibuf_result);
}
- BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y));
- RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id);
- IMB_freeImBuf(out);
+ BLI_assert((sizex == ibuf->x) && (sizey == ibuf->y));
}
else if (gpd) {
/* If there are no strips, Grease Pencil still needs a buffer to draw on */
- ImBuf *out = IMB_allocImBuf(oglrender->sizex, oglrender->sizey, 32, IB_rect);
- RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id);
- IMB_freeImBuf(out);
+ ibuf_result = IMB_allocImBuf(sizex, sizey, 32, IB_rect);
}
if (gpd) {
int i;
uchar *gp_rect;
- uchar *render_rect = (uchar *)RE_RenderViewGetById(rr, oglrender->view_id)->rect32;
+ uchar *render_rect = (uchar *)ibuf_result->rect;
DRW_opengl_context_enable();
GPU_offscreen_bind(oglrender->ofs, true);
@@ -359,10 +346,16 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
}
else {
/* shouldn't suddenly give errors mid-render but possible */
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
char err_out[256] = "unknown";
ImBuf *ibuf_view;
+ bool draw_sky = (scene->r.alphamode == R_ADDSKY);
const int alpha_mode = (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL;
- if (view_context) {
+ const char *viewname = RE_GetActiveRenderView(oglrender->re);
+ View3D *v3d = oglrender->v3d;
+
+ if (v3d != nullptr) {
+ ARegion *region = oglrender->region;
ibuf_view = ED_view3d_draw_offscreen_imbuf(depsgraph,
scene,
static_cast<eDrawType>(v3d->shading.type),
@@ -378,7 +371,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
err_out);
/* for stamp only */
- if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
+ if (oglrender->rv3d->persp == RV3D_CAMOB && v3d->camera) {
camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname);
}
}
@@ -388,8 +381,8 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
nullptr,
OB_SOLID,
scene->camera,
- oglrender->sizex,
- oglrender->sizey,
+ sizex,
+ sizey,
IB_rectfloat,
V3D_OFSDRAW_SHOW_ANNOTATION,
alpha_mode,
@@ -401,12 +394,6 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
if (ibuf_view) {
ibuf_result = ibuf_view;
- if (ibuf_view->rect_float) {
- rectf = ibuf_view->rect_float;
- }
- else {
- rect = (uchar *)ibuf_view->rect;
- }
}
else {
fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out);
@@ -415,6 +402,14 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R
if (ibuf_result != nullptr) {
if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) {
+ float *rectf = nullptr;
+ uchar *rect = nullptr;
+ if (ibuf_result->rect_float) {
+ rectf = ibuf_result->rect_float;
+ }
+ else {
+ rect = (uchar *)ibuf_result->rect;
+ }
BKE_image_stamp_buf(scene, camera, nullptr, rect, rectf, rr->rectx, rr->recty, 4);
}
RE_render_result_rect_from_ibuf(rr, ibuf_result, oglrender->view_id);
@@ -758,8 +753,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
WM_jobs_kill_all_except(wm, CTX_wm_screen(C));
/* create offscreen buffer */
- sizex = (scene->r.size * scene->r.xsch) / 100;
- sizey = (scene->r.size * scene->r.ysch) / 100;
+ BKE_render_resolution(&scene->r, false, &sizex, &sizey);
/* corrects render size with actual size, not every card supports non-power-of-two dimensions */
DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */
diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc
index 97bbcaa102f..10de7063bbc 100644
--- a/source/blender/editors/render/render_preview.cc
+++ b/source/blender/editors/render/render_preview.cc
@@ -307,7 +307,8 @@ static void switch_preview_floor_visibility(Main *pr_main,
const ePreviewRenderMethod pr_method)
{
/* Hide floor for icon renders. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (STREQ(base->object->id.name + 2, "Floor")) {
base->object->visibility_flag &= ~OB_HIDE_RENDER;
if (pr_method == PR_ICON_RENDER) {
@@ -533,8 +534,8 @@ static Scene *preview_prepare_scene(
else {
sce->display.render_aa = SCE_DISPLAY_AA_OFF;
}
-
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->object->id.name[2] == 'p') {
/* copy over object color, in case material uses it */
copy_v4_v4(base->object->color, sp->color);
@@ -550,7 +551,7 @@ static Scene *preview_prepare_scene(
}
}
else if (base->object->type == OB_LAMP) {
- base->flag |= BASE_VISIBLE_DEPSGRAPH;
+ base->flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT;
}
}
}
@@ -586,7 +587,8 @@ static Scene *preview_prepare_scene(
sce->world->horb = 0.0f;
}
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->object->id.name[2] == 'p') {
if (base->object->type == OB_LAMP) {
base->object->data = la;
@@ -679,7 +681,7 @@ static bool ed_preview_draw_rect(ScrArea *area, int split, int first, rcti *rect
/* material preview only needs monoscopy (view 0) */
RE_AcquiredResultGet32(re, &rres, (uint *)rect_byte, 0);
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_3D_IMAGE_COLOR);
immDrawPixelsTexTiled(&state,
fx,
fy,
@@ -775,10 +777,11 @@ static bool object_preview_is_type_supported(const Object *ob)
}
static Object *object_preview_camera_create(Main *preview_main,
+ Scene *scene,
ViewLayer *view_layer,
Object *preview_object)
{
- Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera");
+ Object *camera = BKE_object_add(preview_main, scene, view_layer, OB_CAMERA, "Preview Camera");
float rotmat[3][3];
float dummyscale[3];
@@ -817,13 +820,14 @@ 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->pr_main, scene, view_layer, preview_data->object);
scene->camera = camera_object;
scene->r.xsch = preview_data->sizex;
scene->r.ysch = preview_data->sizey;
scene->r.size = 100;
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *preview_base = BKE_view_layer_base_find(view_layer, preview_data->object);
/* For 'view selected' below. */
preview_base->flag |= BASE_SELECTED;
@@ -1304,41 +1308,33 @@ static void shader_preview_free(void *customdata)
static ImBuf *icon_preview_imbuf_from_brush(Brush *brush)
{
- static const int flags = IB_rect | IB_multilayer | IB_metadata;
+ if (!brush->icon_imbuf && (brush->flag & BRUSH_CUSTOM_ICON) && brush->icon_filepath[0]) {
+ const int flags = IB_rect | IB_multilayer | IB_metadata;
- char filepath[FILE_MAX];
- const char *folder;
+ /* First use the path directly to try and load the file. */
+ char filepath[FILE_MAX];
- if (!(brush->icon_imbuf)) {
- if (brush->flag & BRUSH_CUSTOM_ICON) {
+ BLI_strncpy(filepath, brush->icon_filepath, sizeof(brush->icon_filepath));
+ BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&brush->id));
- if (brush->icon_filepath[0]) {
- /* First use the path directly to try and load the file. */
+ /* Use default color-spaces for brushes. */
+ brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr);
- BLI_strncpy(filepath, brush->icon_filepath, sizeof(brush->icon_filepath));
- BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&brush->id));
+ /* Otherwise lets try to find it in other directories. */
+ if (!(brush->icon_imbuf)) {
+ const char *brushicons_dir = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons");
+ /* Expected to be found, but don't crash if it's not. */
+ if (brushicons_dir) {
+ BLI_join_dirfile(filepath, sizeof(filepath), brushicons_dir, brush->icon_filepath);
- /* Use default color-spaces for brushes. */
+ /* Use default color spaces. */
brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr);
-
- /* otherwise lets try to find it in other directories */
- if (!(brush->icon_imbuf)) {
- folder = BKE_appdir_folder_id(BLENDER_DATAFILES, "brushicons");
-
- BLI_make_file_string(
- BKE_main_blendfile_path_from_global(), filepath, folder, brush->icon_filepath);
-
- if (filepath[0]) {
- /* Use default color spaces. */
- brush->icon_imbuf = IMB_loadiffname(filepath, flags, nullptr);
- }
- }
-
- if (brush->icon_imbuf) {
- BKE_icon_changed(BKE_icon_id_ensure(&brush->id));
- }
}
}
+
+ if (brush->icon_imbuf) {
+ BKE_icon_changed(BKE_icon_id_ensure(&brush->id));
+ }
}
if (!(brush->icon_imbuf)) {
@@ -1771,7 +1767,7 @@ PreviewLoadJob &PreviewLoadJob::ensure_job(wmWindowManager *wm, wmWindow *win)
WM_jobs_start(wm, wm_job);
}
- return *reinterpret_cast<PreviewLoadJob *>(WM_jobs_customdata_get(wm_job));
+ return *static_cast<PreviewLoadJob *>(WM_jobs_customdata_get(wm_job));
}
void PreviewLoadJob::load_jobless(PreviewImage *preview, const eIconSizes icon_size)
@@ -1807,11 +1803,11 @@ void PreviewLoadJob::run_fn(void *customdata,
short *do_update,
float *UNUSED(progress))
{
- PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata);
+ PreviewLoadJob *job_data = static_cast<PreviewLoadJob *>(customdata);
IMB_thumb_locks_acquire();
- while (RequestedPreview *request = reinterpret_cast<RequestedPreview *>(
+ while (RequestedPreview *request = static_cast<RequestedPreview *>(
BLI_thread_queue_pop_timeout(job_data->todo_queue_, 100))) {
if (*stop) {
break;
@@ -1864,7 +1860,7 @@ void PreviewLoadJob::finish_request(RequestedPreview &request)
void PreviewLoadJob::update_fn(void *customdata)
{
- PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata);
+ PreviewLoadJob *job_data = static_cast<PreviewLoadJob *>(customdata);
for (auto request_it = job_data->requested_previews_.begin();
request_it != job_data->requested_previews_.end();) {
@@ -1884,7 +1880,7 @@ void PreviewLoadJob::update_fn(void *customdata)
void PreviewLoadJob::end_fn(void *customdata)
{
- PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata);
+ PreviewLoadJob *job_data = static_cast<PreviewLoadJob *>(customdata);
/* Finish any possibly remaining queued previews. */
for (RequestedPreview &request : job_data->requested_previews_) {
@@ -1895,7 +1891,7 @@ void PreviewLoadJob::end_fn(void *customdata)
void PreviewLoadJob::free_fn(void *customdata)
{
- MEM_delete(reinterpret_cast<PreviewLoadJob *>(customdata));
+ MEM_delete(static_cast<PreviewLoadJob *>(customdata));
}
static void icon_preview_free(void *customdata)
diff --git a/source/blender/editors/render/render_shading.cc b/source/blender/editors/render/render_shading.cc
index da2290f7372..f784346ec8f 100644
--- a/source/blender/editors/render/render_shading.cc
+++ b/source/blender/editors/render/render_shading.cc
@@ -934,7 +934,7 @@ static int view_layer_add_exec(bContext *C, wmOperator *op)
WM_window_set_active_view_layer(win, view_layer_new);
}
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
@@ -1039,7 +1039,7 @@ static int view_layer_add_aov_exec(bContext *C, wmOperator *UNUSED(op))
ntreeCompositUpdateRLayers(scene->nodetree);
}
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
@@ -1091,7 +1091,7 @@ static int view_layer_remove_aov_exec(bContext *C, wmOperator *UNUSED(op))
ntreeCompositUpdateRLayers(scene->nodetree);
}
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
@@ -1143,7 +1143,7 @@ static int view_layer_add_lightgroup_exec(bContext *C, wmOperator *op)
ntreeCompositUpdateRLayers(scene->nodetree);
}
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
@@ -1193,7 +1193,7 @@ static int view_layer_remove_lightgroup_exec(bContext *C, wmOperator *UNUSED(op)
ntreeCompositUpdateRLayers(scene->nodetree);
}
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
@@ -1257,7 +1257,7 @@ static int view_layer_add_used_lightgroups_exec(bContext *C, wmOperator *UNUSED(
ntreeCompositUpdateRLayers(scene->nodetree);
}
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
@@ -1301,7 +1301,7 @@ static int view_layer_remove_unused_lightgroups_exec(bContext *C, wmOperator *UN
ntreeCompositUpdateRLayers(scene->nodetree);
}
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, scene);
@@ -1692,7 +1692,7 @@ static int freestyle_module_remove_exec(bContext *C, wmOperator *UNUSED(op))
BKE_freestyle_module_delete(&view_layer->freestyle_config, module);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
return OPERATOR_FINISHED;
@@ -1722,7 +1722,7 @@ static int freestyle_module_move_exec(bContext *C, wmOperator *op)
int dir = RNA_enum_get(op->ptr, "direction");
if (BKE_freestyle_module_move(&view_layer->freestyle_config, module, dir)) {
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
}
@@ -1778,7 +1778,7 @@ static int freestyle_lineset_add_exec(bContext *C, wmOperator *UNUSED(op))
BKE_freestyle_lineset_add(bmain, &view_layer->freestyle_config, nullptr);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
return OPERATOR_FINISHED;
@@ -1852,7 +1852,7 @@ static int freestyle_lineset_paste_exec(bContext *C, wmOperator *UNUSED(op))
FRS_paste_active_lineset(&view_layer->freestyle_config);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
return OPERATOR_FINISHED;
@@ -1886,7 +1886,7 @@ static int freestyle_lineset_remove_exec(bContext *C, wmOperator *UNUSED(op))
FRS_delete_active_lineset(&view_layer->freestyle_config);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
return OPERATOR_FINISHED;
@@ -1920,7 +1920,7 @@ static int freestyle_lineset_move_exec(bContext *C, wmOperator *op)
int dir = RNA_enum_get(op->ptr, "direction");
if (FRS_move_active_lineset(&view_layer->freestyle_config, dir)) {
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
}
diff --git a/source/blender/editors/render/render_update.cc b/source/blender/editors/render/render_update.cc
index 3d26e764211..7cefcf9815e 100644
--- a/source/blender/editors/render/render_update.cc
+++ b/source/blender/editors/render/render_update.cc
@@ -95,20 +95,20 @@ void ED_render_view3d_update(Depsgraph *depsgraph,
CTX_free(C);
}
- else {
- RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->shading.type);
- if (updated) {
- DRWUpdateContext drw_context = {nullptr};
- drw_context.bmain = bmain;
- drw_context.depsgraph = depsgraph;
- drw_context.scene = scene;
- drw_context.view_layer = view_layer;
- drw_context.region = region;
- drw_context.v3d = v3d;
- drw_context.engine_type = engine_type;
- DRW_notify_view_update(&drw_context);
- }
+
+ if (!updated) {
+ continue;
}
+
+ DRWUpdateContext drw_context = {nullptr};
+ drw_context.bmain = bmain;
+ drw_context.depsgraph = depsgraph;
+ drw_context.scene = scene;
+ drw_context.view_layer = view_layer;
+ drw_context.region = region;
+ drw_context.v3d = v3d;
+ drw_context.engine_type = ED_view3d_engine_type(scene, v3d->shading.type);
+ DRW_notify_view_update(&drw_context);
}
}
diff --git a/source/blender/editors/render/render_view.cc b/source/blender/editors/render/render_view.cc
index a7ff2aad05a..7569b3600a1 100644
--- a/source/blender/editors/render/render_view.cc
+++ b/source/blender/editors/render/render_view.cc
@@ -19,6 +19,7 @@
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_report.h"
+#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BLT_translation.h"
@@ -73,7 +74,7 @@ static ScrArea *find_area_showing_r_result(bContext *C, Scene *scene, wmWindow *
ScrArea *area = nullptr;
SpaceImage *sima;
- /* find an imagewindow showing render result */
+ /* find an image-window showing render result */
for (*win = static_cast<wmWindow *>(wm->windows.first); *win; *win = (*win)->next) {
if (WM_window_get_active_scene(*win) == scene) {
const bScreen *screen = WM_window_get_active_screen(*win);
@@ -101,7 +102,7 @@ static ScrArea *find_area_image_empty(bContext *C)
ScrArea *area;
SpaceImage *sima;
- /* find an imagewindow showing render result */
+ /* find an image-window showing render result */
for (area = static_cast<ScrArea *>(screen->areabase.first); area; area = area->next) {
if (area->spacetype == SPACE_IMAGE) {
sima = static_cast<SpaceImage *>(area->spacedata.first);
@@ -130,8 +131,11 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports)
}
if (U.render_display_type == USER_RENDER_DISPLAY_WINDOW) {
- int sizex = 30 * UI_DPI_FAC + (scene->r.xsch * scene->r.size) / 100;
- int sizey = 60 * UI_DPI_FAC + (scene->r.ysch * scene->r.size) / 100;
+ int sizex, sizey;
+ BKE_render_resolution(&scene->r, false, &sizex, &sizey);
+
+ sizex += 30 * UI_DPI_FAC;
+ sizey += 60 * UI_DPI_FAC;
/* arbitrary... miniature image window views don't make much sense */
if (sizex < 320) {
diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c
index 57a9e6be917..07a93d3907a 100644
--- a/source/blender/editors/scene/scene_edit.c
+++ b/source/blender/editors/scene/scene_edit.c
@@ -229,7 +229,7 @@ bool ED_scene_view_layer_delete(Main *bmain, Scene *scene, ViewLayer *layer, Rep
BKE_view_layer_free(layer);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(bmain);
WM_main_add_notifier(NC_SCENE | ND_LAYER | NA_REMOVED, scene);
diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt
index f9b1e2b5d4c..119758f3335 100644
--- a/source/blender/editors/screen/CMakeLists.txt
+++ b/source/blender/editors/screen/CMakeLists.txt
@@ -15,7 +15,6 @@ set(INC
../../makesrna
../../sequencer
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index def6c38f5ca..25e16bee558 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -16,6 +16,7 @@
#include "BLI_linklist_stack.h"
#include "BLI_math.h"
#include "BLI_rand.h"
+#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
#include "BKE_context.h"
@@ -83,7 +84,7 @@ static void region_draw_emboss(const ARegion *region, const rcti *scirct, int si
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
immBeginAtMost(GPU_PRIM_LINES, 8);
@@ -127,7 +128,7 @@ void ED_region_pixelspace(const ARegion *region)
void ED_region_do_listen(wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *notifier = params->notifier;
+ const wmNotifier *notifier = params->notifier;
/* generic notes first */
switch (notifier->category) {
@@ -174,7 +175,7 @@ void ED_area_do_refresh(bContext *C, ScrArea *area)
}
/**
- * \brief Corner widget use for quitting fullscreen.
+ * \brief Corner widget use for quitting full-screen.
*/
static void area_draw_azone_fullscreen(
short UNUSED(x1), short UNUSED(y1), short x2, short y2, float alpha)
@@ -238,8 +239,6 @@ static void draw_azone_arrow(float x1, float y1, float x2, float y2, AZEdge edge
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPU_blend(GPU_BLEND_ALPHA);
- /* NOTE(fclem): There is something strange going on with Mesa and GPU_SHADER_2D_UNIFORM_COLOR
- * that causes a crash on some GPUs (see T76113). Using 3D variant avoid the issue. */
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(0.8f, 0.8f, 0.8f, 0.4f);
@@ -563,7 +562,7 @@ void ED_region_do_draw(bContext *C, ARegion *region)
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(BLI_thread_frand(0), BLI_thread_frand(0), BLI_thread_frand(0), 0.1f);
immRectf(pos,
region->drawrct.xmin - region->winrct.xmin,
@@ -593,7 +592,7 @@ void ED_region_do_draw(bContext *C, ARegion *region)
UI_GetThemeColor3fv(TH_EDITOR_OUTLINE, color);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
GPU_line_width(1.0f);
imm_draw_box_wire_2d(pos,
@@ -846,7 +845,7 @@ void ED_workspace_status_text(bContext *C, const char *str)
static void area_azone_init(wmWindow *win, const bScreen *screen, ScrArea *area)
{
- /* reinitialize entirely, regions and fullscreen add azones too */
+ /* reinitialize entirely, regions and full-screen add azones too */
BLI_freelistN(&area->actionzones);
if (screen->state != SCREENNORMAL) {
@@ -1903,6 +1902,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area)
{
WorkSpace *workspace = WM_window_get_active_workspace(win);
const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
if (ED_area_is_global(area) && (area->global->flag & GLOBAL_AREA_IS_HIDDEN)) {
@@ -1967,7 +1967,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area)
/* Avoid re-initializing tools while resizing the window. */
if ((G.moving & G_TRANSFORM_WM) == 0) {
if ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) {
- WM_toolsystem_refresh_screen_area(workspace, view_layer, area);
+ WM_toolsystem_refresh_screen_area(workspace, scene, view_layer, area);
area->flag |= AREA_FLAG_ACTIVE_TOOL_UPDATE;
}
else {
@@ -2578,8 +2578,8 @@ void ED_area_prevspace(bContext *C, ScrArea *area)
/* no change */
return;
}
- /* If this is a stacked fullscreen, changing to previous area exits it (meaning we're still in a
- * fullscreen, but not in a stacked one). */
+ /* If this is a stacked full-screen, changing to previous area exits it (meaning we're still in a
+ * full-screen, but not in a stacked one). */
area->flag &= ~AREA_FLAG_STACKED_FULLSCREEN;
ED_area_tag_redraw(area);
@@ -2689,12 +2689,13 @@ static void ed_panel_draw(const bContext *C,
const uiStyle *style = UI_style_get_dpi();
/* Draw panel. */
-
char block_name[BKE_ST_MAXNAME + INSTANCED_PANEL_UNIQUE_STR_LEN];
- strncpy(block_name, pt->idname, BKE_ST_MAXNAME);
- if (unique_panel_str != NULL) {
+ if (unique_panel_str) {
/* Instanced panels should have already been added at this point. */
- strncat(block_name, unique_panel_str, INSTANCED_PANEL_UNIQUE_STR_LEN);
+ BLI_string_join(block_name, sizeof(block_name), pt->idname, unique_panel_str);
+ }
+ else {
+ STRNCPY(block_name, pt->idname);
}
uiBlock *block = UI_block_begin(C, region, block_name, UI_EMBOSS);
@@ -3558,7 +3559,7 @@ void ED_region_info_draw_multiline(ARegion *region,
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(fill_color);
immRecti(pos, rect.xmin, rect.ymin, rect.xmax + 1, rect.ymax + 1);
immUnbindProgram();
@@ -3629,7 +3630,7 @@ void ED_region_grid_draw(ARegion *region, float zoomx, float zoomy, float x0, fl
float gridcolor[4];
UI_GetThemeColor4fv(TH_GRID, gridcolor);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* To fake alpha-blending, color shading is reduced when alpha is nearing 0. */
immUniformThemeColorBlendShade(TH_BACK, TH_GRID, gridcolor[3], 20 * gridcolor[3]);
immRectf(pos, x1, y1, x2, y2);
@@ -3666,7 +3667,7 @@ void ED_region_grid_draw(ARegion *region, float zoomx, float zoomy, float x0, fl
pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
immBegin(GPU_PRIM_LINES, 4 * count_fine + 4 * count_large);
float theme_color[3];
@@ -3779,7 +3780,7 @@ void ED_region_cache_draw_background(ARegion *region)
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ub(128, 128, 255, 64);
immRecti(pos, 0, region_bottom, region->winx, region_bottom + 8 * UI_DPI_FAC);
immUnbindProgram();
@@ -3800,7 +3801,7 @@ void ED_region_cache_draw_curfra_label(const int framenr, const float x, const f
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_CFRAME);
immRecti(pos, x, y, x + font_dims[0] + 6.0f, y + font_dims[1] + 4.0f);
immUnbindProgram();
@@ -3820,7 +3821,7 @@ void ED_region_cache_draw_cached_segments(
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ub(128, 128, 255, 128);
for (int a = 0; a < num_segments; a++) {
diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c
index 8a84f4cf079..4382fd3d1c2 100644
--- a/source/blender/editors/screen/glutil.c
+++ b/source/blender/editors/screen/glutil.c
@@ -77,9 +77,10 @@ void immDrawPixelsTexScaledFullSize(const IMMDrawPixelsTexState *state,
* filtering results. Mipmaps can be used to get better results (i.e. #GL_LINEAR_MIPMAP_LINEAR),
* so always use mipmaps when filtering. */
const bool use_mipmap = use_filter && ((draw_width < img_w) || (draw_height < img_h));
- const int mips = use_mipmap ? 9999 : 1;
+ const int mip_len = use_mipmap ? 9999 : 1;
- GPUTexture *tex = GPU_texture_create_2d("immDrawPixels", img_w, img_h, mips, gpu_format, NULL);
+ GPUTexture *tex = GPU_texture_create_2d(
+ "immDrawPixels", img_w, img_h, mip_len, gpu_format, NULL);
const bool use_float_data = ELEM(gpu_format, GPU_RGBA16F, GPU_RGB16F, GPU_R16F);
eGPUDataFormat gpu_data_format = (use_float_data) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
@@ -514,7 +515,7 @@ void ED_draw_imbuf_clipping(ImBuf *ibuf,
ibuf, view_settings, display_settings, &cache_handle);
if (display_buffer) {
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_3D_IMAGE_COLOR);
immDrawPixelsTexTiled_clipping(&state,
x,
y,
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 239113c28a4..ffd76e70eb8 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -100,6 +100,7 @@ const char *screen_context_dir[] = {
"active_gpencil_frame",
"active_annotation_layer",
"active_operator",
+ "active_action",
"selected_visible_actions",
"selected_editable_actions",
"visible_fcurves",
@@ -128,9 +129,11 @@ static eContextResult screen_ctx_visible_objects(const bContext *C, bContextData
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ BKE_view_layer_synced_ensure(scene, view_layer);
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_VISIBLE(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
}
@@ -142,9 +145,11 @@ static eContextResult screen_ctx_selectable_objects(const bContext *C, bContextD
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ BKE_view_layer_synced_ensure(scene, view_layer);
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_SELECTABLE(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
}
@@ -156,9 +161,11 @@ static eContextResult screen_ctx_selected_objects(const bContext *C, bContextDat
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ BKE_view_layer_synced_ensure(scene, view_layer);
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_SELECTED(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
}
@@ -171,9 +178,11 @@ static eContextResult screen_ctx_selected_editable_objects(const bContext *C,
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ BKE_view_layer_synced_ensure(scene, view_layer);
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_SELECTED_EDITABLE(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
}
@@ -185,10 +194,12 @@ static eContextResult screen_ctx_editable_objects(const bContext *C, bContextDat
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ BKE_view_layer_synced_ensure(scene, view_layer);
/* Visible + Editable, but not necessarily selected */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_EDITABLE(v3d, base)) {
CTX_data_id_list_add(result, &base->object->id);
}
@@ -200,11 +211,13 @@ static eContextResult screen_ctx_objects_in_mode(const bContext *C, bContextData
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && (obact->mode != OB_MODE_OBJECT)) {
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, obact->type, obact->mode, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, obact->type, obact->mode, ob_iter) {
CTX_data_id_list_add(result, &ob_iter->id);
}
FOREACH_OBJECT_IN_MODE_END;
@@ -217,15 +230,17 @@ static eContextResult screen_ctx_objects_in_mode_unique_data(const bContext *C,
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && (obact->mode != OB_MODE_OBJECT)) {
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, obact->type, obact->mode, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, obact->type, obact->mode, ob_iter) {
ob_iter->id.tag |= LIB_TAG_DOIT;
}
FOREACH_OBJECT_IN_MODE_END;
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, obact->type, obact->mode, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, obact->type, obact->mode, ob_iter) {
if (ob_iter->id.tag & LIB_TAG_DOIT) {
ob_iter->id.tag &= ~LIB_TAG_DOIT;
CTX_data_id_list_add(result, &ob_iter->id);
@@ -241,8 +256,10 @@ static eContextResult screen_ctx_visible_or_editable_bones_(const bContext *C,
const bool editable_bones)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
EditBone *flipbone = NULL;
@@ -250,7 +267,7 @@ static eContextResult screen_ctx_visible_or_editable_bones_(const bContext *C,
if (arm && arm->edbo) {
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint i = 0; i < objects_len; i++) {
Object *ob = objects[i];
arm = ob->data;
@@ -312,15 +329,17 @@ static eContextResult screen_ctx_selected_bones_(const bContext *C,
const bool selected_editable_bones)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL;
EditBone *flipbone = NULL;
if (arm && arm->edbo) {
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint i = 0; i < objects_len; i++) {
Object *ob = objects[i];
arm = ob->data;
@@ -382,8 +401,10 @@ static eContextResult screen_ctx_visible_pose_bones(const bContext *C, bContextD
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
Object *obpose = BKE_object_pose_armature_get(obact);
if (obpose && obpose->pose && obpose->data) {
if (obpose != obact) {
@@ -393,7 +414,7 @@ static eContextResult screen_ctx_visible_pose_bones(const bContext *C, bContextD
FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
}
else if (obact->mode & OB_MODE_POSE) {
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
FOREACH_PCHAN_VISIBLE_IN_OBJECT_BEGIN (ob_iter, pchan) {
CTX_data_list_add(result, &ob_iter->id, &RNA_PoseBone, pchan);
}
@@ -410,8 +431,9 @@ static eContextResult screen_ctx_selected_pose_bones(const bContext *C, bContext
{
wmWindow *win = CTX_wm_window(C);
View3D *v3d = CTX_wm_view3d(C); /* This may be NULL in a lot of cases. */
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
Object *obpose = BKE_object_pose_armature_get(obact);
if (obpose && obpose->pose && obpose->data) {
if (obpose != obact) {
@@ -421,7 +443,7 @@ static eContextResult screen_ctx_selected_pose_bones(const bContext *C, bContext
FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
}
else if (obact->mode & OB_MODE_POSE) {
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (scene, view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) {
FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob_iter, pchan) {
CTX_data_list_add(result, &ob_iter->id, &RNA_PoseBone, pchan);
}
@@ -438,8 +460,10 @@ static eContextResult screen_ctx_selected_pose_bones_from_active_object(const bC
bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
Object *obpose = BKE_object_pose_armature_get(obact);
if (obpose && obpose->pose && obpose->data) {
if (obpose != obact) {
@@ -462,8 +486,10 @@ static eContextResult screen_ctx_selected_pose_bones_from_active_object(const bC
static eContextResult screen_ctx_active_bone(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && obact->type == OB_ARMATURE) {
bArmature *arm = obact->data;
if (arm->edbo) {
@@ -484,8 +510,10 @@ static eContextResult screen_ctx_active_bone(const bContext *C, bContextDataResu
static eContextResult screen_ctx_active_pose_bone(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
Object *obpose = BKE_object_pose_armature_get(obact);
bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(obpose);
@@ -498,8 +526,10 @@ static eContextResult screen_ctx_active_pose_bone(const bContext *C, bContextDat
static eContextResult screen_ctx_active_object(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact) {
CTX_data_id_pointer_set(result, &obact->id);
@@ -510,8 +540,10 @@ static eContextResult screen_ctx_active_object(const bContext *C, bContextDataRe
static eContextResult screen_ctx_object(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact) {
CTX_data_id_pointer_set(result, &obact->id);
@@ -522,8 +554,10 @@ static eContextResult screen_ctx_object(const bContext *C, bContextDataResult *r
static eContextResult screen_ctx_edit_object(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
/* convenience for now, 1 object per scene in editmode */
if (obedit) {
CTX_data_id_pointer_set(result, &obedit->id);
@@ -534,8 +568,10 @@ static eContextResult screen_ctx_edit_object(const bContext *C, bContextDataResu
static eContextResult screen_ctx_sculpt_object(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && (obact->mode & OB_MODE_SCULPT)) {
CTX_data_id_pointer_set(result, &obact->id);
@@ -546,8 +582,10 @@ static eContextResult screen_ctx_sculpt_object(const bContext *C, bContextDataRe
static eContextResult screen_ctx_vertex_paint_object(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && (obact->mode & OB_MODE_VERTEX_PAINT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
@@ -557,8 +595,10 @@ static eContextResult screen_ctx_vertex_paint_object(const bContext *C, bContext
static eContextResult screen_ctx_weight_paint_object(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
@@ -568,8 +608,10 @@ static eContextResult screen_ctx_weight_paint_object(const bContext *C, bContext
static eContextResult screen_ctx_image_paint_object(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && (obact->mode & OB_MODE_TEXTURE_PAINT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
@@ -580,8 +622,10 @@ static eContextResult screen_ctx_particle_edit_object(const bContext *C,
bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) {
CTX_data_id_pointer_set(result, &obact->id);
}
@@ -591,8 +635,10 @@ static eContextResult screen_ctx_particle_edit_object(const bContext *C,
static eContextResult screen_ctx_pose_object(const bContext *C, bContextDataResult *result)
{
wmWindow *win = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
Object *obpose = BKE_object_pose_armature_get(obact);
if (obpose) {
CTX_data_id_pointer_set(result, &obpose->id);
@@ -735,8 +781,10 @@ static eContextResult screen_ctx_gpencil_data(const bContext *C, bContextDataRes
{
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
/* FIXME: for some reason, CTX_data_active_object(C) returns NULL when called from these
* situations (as outlined above - see Campbell's #ifdefs).
* That causes the get_active function to fail when called from context.
@@ -754,8 +802,10 @@ static eContextResult screen_ctx_gpencil_data_owner(const bContext *C, bContextD
{
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
/* Pointer to which data/datablock owns the reference to the Grease Pencil data being used
* (as gpencil_data). */
@@ -805,8 +855,10 @@ static eContextResult screen_ctx_active_gpencil_layer(const bContext *C,
{
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
@@ -843,8 +895,10 @@ static eContextResult screen_ctx_active_gpencil_frame(const bContext *C,
{
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
@@ -862,8 +916,10 @@ static eContextResult screen_ctx_visible_gpencil_layers(const bContext *C,
{
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
@@ -882,8 +938,10 @@ static eContextResult screen_ctx_editable_gpencil_layers(const bContext *C,
{
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
if (gpd) {
@@ -902,8 +960,10 @@ static eContextResult screen_ctx_editable_gpencil_strokes(const bContext *C,
{
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
@@ -969,6 +1029,7 @@ static eContextResult screen_ctx_active_operator(const bContext *C, bContextData
}
static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
bContextDataResult *result,
+ bool active_only,
bool editable)
{
bAnimContext ac;
@@ -978,11 +1039,17 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
SpaceAction *saction = (SpaceAction *)ac.sl;
if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY)) {
- if (saction->action && !(editable && ID_IS_LINKED(saction->action))) {
- CTX_data_id_list_add(result, &saction->action->id);
+ if (active_only) {
+ CTX_data_id_pointer_set(result, (ID *)saction->action);
+ }
+ else {
+ if (saction->action && !(editable && ID_IS_LINKED(saction->action))) {
+ CTX_data_id_list_add(result, &saction->action->id);
+ }
+
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
}
- CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
return CTX_RESULT_OK;
}
}
@@ -995,7 +1062,8 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
switch (ac.spacetype) {
case SPACE_GRAPH:
- filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_SEL;
+ filter |= ANIMFILTER_FCURVESONLY | ANIMFILTER_CURVE_VISIBLE |
+ (active_only ? ANIMFILTER_ACTIVE : ANIMFILTER_SEL);
break;
case SPACE_ACTION:
@@ -1006,7 +1074,7 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
- GSet *seen_set = BLI_gset_ptr_new("seen actions");
+ GSet *seen_set = active_only ? NULL : BLI_gset_ptr_new("seen actions");
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
/* In dopesheet check selection status of individual items, skipping
@@ -1019,6 +1087,10 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
bAction *action = ANIM_channel_action_get(ale);
if (action) {
+ if (active_only) {
+ CTX_data_id_pointer_set(result, (ID *)action);
+ break;
+ }
if (editable && ID_IS_LINKED(action)) {
continue;
}
@@ -1030,25 +1102,31 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C,
}
}
- BLI_gset_free(seen_set, NULL);
-
ANIM_animdata_freelist(&anim_data);
- CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+ if (!active_only) {
+ BLI_gset_free(seen_set, NULL);
+
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+ }
+
return CTX_RESULT_OK;
}
return CTX_RESULT_NO_DATA;
}
-
+static eContextResult screen_ctx_active_action(const bContext *C, bContextDataResult *result)
+{
+ return screen_ctx_sel_actions_impl(C, result, true, false);
+}
static eContextResult screen_ctx_selected_visible_actions(const bContext *C,
bContextDataResult *result)
{
- return screen_ctx_sel_actions_impl(C, result, false);
+ return screen_ctx_sel_actions_impl(C, result, false, false);
}
static eContextResult screen_ctx_selected_editable_actions(const bContext *C,
bContextDataResult *result)
{
- return screen_ctx_sel_actions_impl(C, result, true);
+ return screen_ctx_sel_actions_impl(C, result, false, true);
}
static eContextResult screen_ctx_sel_edit_fcurves_(const bContext *C,
bContextDataResult *result,
@@ -1262,6 +1340,7 @@ static void ensure_ed_screen_context_functions(void)
register_context_function("editable_gpencil_layers", screen_ctx_editable_gpencil_layers);
register_context_function("editable_gpencil_strokes", screen_ctx_editable_gpencil_strokes);
register_context_function("active_operator", screen_ctx_active_operator);
+ register_context_function("active_action", screen_ctx_active_action);
register_context_function("selected_visible_actions", screen_ctx_selected_visible_actions);
register_context_function("selected_editable_actions", screen_ctx_selected_editable_actions);
register_context_function("editable_fcurves", screen_ctx_editable_fcurves);
diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c
index 6406b0d9d52..065cb3a61a2 100644
--- a/source/blender/editors/screen/screen_draw.c
+++ b/source/blender/editors/screen/screen_draw.c
@@ -236,7 +236,7 @@ void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2)
uint pos_id = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
/* Highlight source (sa1) within combined area. */
@@ -302,7 +302,7 @@ void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2)
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);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Split-point. */
GPU_blend(GPU_BLEND_ALPHA);
@@ -380,7 +380,7 @@ static void screen_preview_draw_areas(const bScreen *screen,
const float ofs_h = ofs_between_areas * 0.5f;
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(col);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 08c8c863729..bc7006d2ac7 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -110,7 +110,7 @@ ScrArea *area_split(const wmWindow *win,
return NULL;
}
- /* NOTE(campbell): regarding (fac > 0.5f) checks below.
+ /* NOTE(@campbellbarton): regarding (fac > 0.5f) checks below.
* 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. */
@@ -580,7 +580,7 @@ static void region_cursor_set(wmWindow *win, bool swin_changed)
}
}
-void ED_screen_do_listen(bContext *C, wmNotifier *note)
+void ED_screen_do_listen(bContext *C, const wmNotifier *note)
{
wmWindow *win = CTX_wm_window(C);
bScreen *screen = CTX_wm_screen(C);
@@ -1163,8 +1163,9 @@ static void screen_set_3dview_camera(Scene *scene,
/* fix any cameras that are used in the 3d view but not in the scene */
BKE_screen_view3d_sync(v3d, scene);
+ BKE_view_layer_synced_ensure(scene, view_layer);
if (!v3d->camera || !BKE_view_layer_base_find(view_layer, v3d->camera)) {
- v3d->camera = BKE_view_layer_camera_find(view_layer);
+ v3d->camera = BKE_view_layer_camera_find(scene, view_layer);
// XXX if (screen == curscreen) handle_view3d_lock();
if (!v3d->camera) {
ListBase *regionbase;
@@ -1212,10 +1213,10 @@ void ED_screen_scene_change(bContext *C,
/* Mode Syncing. */
if (view_layer_old) {
WorkSpace *workspace = CTX_wm_workspace(C);
- Object *obact_new = OBACT(view_layer);
+ Object *obact_new = BKE_view_layer_active_object_get(view_layer);
UNUSED_VARS(obact_new);
eObjectMode object_mode_old = workspace->object_mode;
- Object *obact_old = OBACT(view_layer_old);
+ Object *obact_old = BKE_view_layer_active_object_get(view_layer_old);
UNUSED_VARS(obact_old, object_mode_old);
}
#endif
@@ -1274,7 +1275,7 @@ void ED_screen_full_prevspace(bContext *C, ScrArea *area)
BLI_assert(area->full);
if (area->flag & AREA_FLAG_STACKED_FULLSCREEN) {
- /* stacked fullscreen -> only go back to previous area and don't toggle out of fullscreen */
+ /* Stacked full-screen -> only go back to previous area and don't toggle out of full-screen. */
ED_area_prevspace(C, area);
}
else {
@@ -1305,8 +1306,8 @@ void ED_screen_full_restore(bContext *C, ScrArea *area)
bScreen *screen = CTX_wm_screen(C);
short state = (screen ? screen->state : SCREENMAXIMIZED);
- /* if fullscreen area has a temporary space (such as a file browser or fullscreen render
- * overlaid on top of an existing setup) then return to the previous space */
+ /* If full-screen area has a temporary space (such as a file browser or full-screen render
+ * overlaid on top of an existing setup) then return to the previous space. */
if (sl->next) {
if (sl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) {
diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c
index 3486ea8b466..3ad3fa7892c 100644
--- a/source/blender/editors/screen/screen_geometry.c
+++ b/source/blender/editors/screen/screen_geometry.c
@@ -284,7 +284,7 @@ short screen_geom_find_area_split_point(const ScrArea *area,
{
const int cur_area_width = screen_geom_area_width(area);
const int cur_area_height = screen_geom_area_height(area);
- const short area_min_x = AREAMINX;
+ const short area_min_x = AREAMINX * U.dpi_fac;
const short area_min_y = ED_area_headersize();
/* area big enough? */
diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h
index 4c2d94e2018..feda68a51a7 100644
--- a/source/blender/editors/screen/screen_intern.h
+++ b/source/blender/editors/screen/screen_intern.h
@@ -53,7 +53,7 @@ typedef enum eScreenAxis {
/* area.c */
/**
- * we swap spaces for fullscreen to keep all allocated data area vertices were set
+ * We swap spaces for full-screen to keep all allocated data area vertices were set.
*/
void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, bool do_free);
void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src);
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index c616ca2b5eb..29f78b2a0ef 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -723,15 +723,16 @@ typedef struct sActionzoneData {
static bool actionzone_area_poll(bContext *C)
{
wmWindow *win = CTX_wm_window(C);
- bScreen *screen = WM_window_get_active_screen(win);
-
- if (screen && win && win->eventstate) {
- const int *xy = &win->eventstate->xy[0];
+ if (win && win->eventstate) {
+ bScreen *screen = WM_window_get_active_screen(win);
+ if (screen) {
+ const int *xy = &win->eventstate->xy[0];
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- LISTBASE_FOREACH (AZone *, az, &area->actionzones) {
- if (BLI_rcti_isect_pt_v(&az->rect, xy)) {
- return true;
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (AZone *, az, &area->actionzones) {
+ if (BLI_rcti_isect_pt_v(&az->rect, xy)) {
+ return true;
+ }
}
}
}
@@ -858,7 +859,7 @@ static AZone *area_actionzone_refresh_xy(ScrArea *area, const int xy[2], const b
/* Check if we even have scroll bars. */
if (((az->direction == AZ_SCROLL_HOR) && !(scroll_flag & V2D_SCROLL_HORIZONTAL)) ||
((az->direction == AZ_SCROLL_VERT) && !(scroll_flag & V2D_SCROLL_VERTICAL))) {
- /* no scrollbars, do nothing. */
+ /* No scroll-bars, do nothing. */
}
else if (test_only) {
if (isect_value != 0) {
@@ -1638,7 +1639,7 @@ static void area_move_set_limits(wmWindow *win,
}
}
else {
- int areamin = AREAMINX;
+ int areamin = AREAMINX * U.dpi_fac;
if (area->v1->vec.x > window_rect.xmin) {
areamin += U.pixelsize;
@@ -2061,7 +2062,7 @@ static bool area_split_allowed(const ScrArea *area, const eScreenAxis dir_axis)
return false;
}
- if ((dir_axis == SCREEN_AXIS_V && area->winx <= 2 * AREAMINX) ||
+ if ((dir_axis == SCREEN_AXIS_V && area->winx <= 2 * AREAMINX * U.dpi_fac) ||
(dir_axis == SCREEN_AXIS_H && area->winy <= 2 * ED_area_headersize())) {
/* Must be at least double minimum sizes to split into two. */
return false;
@@ -3266,7 +3267,7 @@ static int screen_maximize_area_exec(bContext *C, wmOperator *op)
BLI_assert(!screen->temp);
- /* search current screen for 'fullscreen' areas */
+ /* search current screen for 'full-screen' areas */
/* prevents restoring info header, when mouse is over it */
LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) {
if (area_iter->full) {
@@ -4718,7 +4719,7 @@ static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), con
#endif
}
- /* since we follow drawflags, we can't send notifier but tag regions ourselves */
+ /* Since we follow draw-flags, we can't send notifier but tag regions ourselves. */
if (depsgraph != NULL) {
ED_update_for_newframe(bmain, depsgraph);
}
diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c
index 1156452310c..01c208bf48d 100644
--- a/source/blender/editors/screen/screen_user_menu.c
+++ b/source/blender/editors/screen/screen_user_menu.c
@@ -34,6 +34,7 @@
#include "UI_resources.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
/* -------------------------------------------------------------------- */
@@ -214,7 +215,14 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu)
wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false);
if (ot != NULL) {
IDProperty *prop = umi_op->prop ? IDP_CopyProperty(umi_op->prop) : NULL;
- uiItemFullO_ptr(menu->layout, ot, ui_name, ICON_NONE, prop, umi_op->opcontext, 0, NULL);
+ uiItemFullO_ptr(menu->layout,
+ ot,
+ CTX_IFACE_(ot->translation_context, ui_name),
+ ICON_NONE,
+ prop,
+ umi_op->opcontext,
+ 0,
+ NULL);
is_empty = false;
}
else {
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index 5464d0a347d..38a9d8ba7ab 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -54,13 +54,12 @@ static int screenshot_data_create(bContext *C, wmOperator *op, ScrArea *area)
{
int dumprect_size[2];
- wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
/* do redraw so we don't show popups/menus */
WM_redraw_windows(C);
- uint *dumprect = WM_window_pixels_read(wm, win, dumprect_size);
+ uint *dumprect = WM_window_pixels_read_offscreen(C, win, dumprect_size);
if (dumprect) {
ScreenshotData *scd = MEM_callocN(sizeof(ScreenshotData), "screenshot");
diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c
index cb29f15420c..9a6bdc98d76 100644
--- a/source/blender/editors/screen/workspace_edit.c
+++ b/source/blender/editors/screen/workspace_edit.c
@@ -220,7 +220,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo
workspace_new->order = workspace_old->order;
BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids);
- /* TODO(campbell): tools */
+ /* TODO(@campbellbarton): tools */
LISTBASE_FOREACH (WorkSpaceLayout *, layout_old, &workspace_old->layouts) {
WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate(
@@ -359,6 +359,12 @@ static int workspace_append_activate_exec(bContext *C, wmOperator *op)
BLO_LIBLINK_APPEND_RECURSIVE);
if (appended_workspace) {
+ if (BLT_translate_new_dataname()) {
+ /* Translate workspace name */
+ BKE_libblock_rename(
+ bmain, &appended_workspace->id, CTX_DATA_(BLT_I18NCONTEXT_ID_WORKSPACE, idname));
+ }
+
/* Set defaults. */
BLO_update_defaults_workspace(appended_workspace, NULL);
@@ -441,8 +447,14 @@ static void workspace_append_button(uiLayout *layout,
BLI_assert(STREQ(ot_append->idname, "WORKSPACE_OT_append_activate"));
PointerRNA opptr;
- uiItemFullO_ptr(
- layout, ot_append, workspace->id.name + 2, ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0, &opptr);
+ uiItemFullO_ptr(layout,
+ ot_append,
+ CTX_DATA_(BLT_I18NCONTEXT_ID_WORKSPACE, workspace->id.name + 2),
+ ICON_NONE,
+ NULL,
+ WM_OP_EXEC_DEFAULT,
+ 0,
+ &opptr);
RNA_string_set(&opptr, "idname", id->name + 2);
RNA_string_set(&opptr, "filepath", filepath);
}
@@ -495,7 +507,8 @@ static void workspace_add_menu(bContext *UNUSED(C), uiLayout *layout, void *temp
static int workspace_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- uiPopupMenu *pup = UI_popup_menu_begin(C, op->type->name, ICON_ADD);
+ uiPopupMenu *pup = UI_popup_menu_begin(
+ C, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, op->type->name), ICON_ADD);
uiLayout *layout = UI_popup_menu_layout(pup);
uiItemMenuF(layout, IFACE_("General"), ICON_NONE, workspace_add_menu, NULL);
@@ -507,7 +520,7 @@ static int workspace_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
char *template = link->data;
char display_name[FILE_MAX];
- BLI_path_to_display_name(display_name, sizeof(display_name), template);
+ BLI_path_to_display_name(display_name, sizeof(display_name), IFACE_(template));
/* Steals ownership of link data string. */
uiItemMenuFN(layout, display_name, ICON_NONE, workspace_add_menu, template);
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index edb0f1cda4d..2709ac3fd91 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -21,7 +21,6 @@ set(INC
../../../../intern/atomic
../../../../intern/clog
../../../../intern/eigen
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@@ -37,8 +36,8 @@ set(SRC
curves_sculpt_ops.cc
curves_sculpt_pinch.cc
curves_sculpt_puff.cc
- curves_sculpt_selection_paint.cc
curves_sculpt_selection.cc
+ curves_sculpt_selection_paint.cc
curves_sculpt_slide.cc
curves_sculpt_smooth.cc
curves_sculpt_snake_hook.cc
@@ -69,7 +68,7 @@ set(SRC
sculpt_detail.c
sculpt_dyntopo.c
sculpt_expand.c
- sculpt_face_set.c
+ sculpt_face_set.cc
sculpt_filter_color.c
sculpt_filter_mask.c
sculpt_filter_mesh.c
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
index 26145a386f5..b5d739ae08e 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_add.cc
@@ -23,6 +23,8 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_report.h"
@@ -42,6 +44,8 @@
#include "WM_api.h"
+#include "DEG_depsgraph_query.h"
+
/**
* The code below uses a suffix naming convention to indicate the coordinate space:
* cu: Local space of the curves object that is being edited.
@@ -80,13 +84,17 @@ struct AddOperationExecutor {
AddOperation *self_ = nullptr;
CurvesSculptCommonContext ctx_;
- Object *object_ = nullptr;
- Curves *curves_id_ = nullptr;
- CurvesGeometry *curves_ = nullptr;
+ Object *curves_ob_orig_ = nullptr;
+ Curves *curves_id_orig_ = nullptr;
+ CurvesGeometry *curves_orig_ = nullptr;
- Object *surface_ob_ = nullptr;
- Mesh *surface_ = nullptr;
- Span<MLoopTri> surface_looptris_;
+ Object *surface_ob_eval_ = nullptr;
+ Mesh *surface_eval_ = nullptr;
+ Span<MVert> surface_verts_eval_;
+ Span<MLoop> surface_loops_eval_;
+ Span<MLoopTri> surface_looptris_eval_;
+ VArraySpan<float2> surface_uv_map_eval_;
+ BVHTreeFromMesh surface_bvh_eval_;
const CurvesSculpt *curves_sculpt_ = nullptr;
const Brush *brush_ = nullptr;
@@ -99,14 +107,6 @@ struct AddOperationExecutor {
CurvesSurfaceTransforms transforms_;
- BVHTreeFromMesh surface_bvh_;
-
- struct AddedPoints {
- Vector<float3> positions_cu;
- Vector<float3> bary_coords;
- Vector<int> looptri_indices;
- };
-
AddOperationExecutor(const bContext &C) : ctx_(C)
{
}
@@ -114,19 +114,40 @@ struct AddOperationExecutor {
void execute(AddOperation &self, const bContext &C, const StrokeExtension &stroke_extension)
{
self_ = &self;
- object_ = CTX_data_active_object(&C);
+ curves_ob_orig_ = CTX_data_active_object(&C);
- curves_id_ = static_cast<Curves *>(object_->data);
- curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+ curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
+ curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry);
- if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) {
+ if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
+ report_missing_surface(stroke_extension.reports);
return;
}
- transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
+ transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
- surface_ob_ = curves_id_->surface;
- surface_ = static_cast<Mesh *>(surface_ob_->data);
+ Object &surface_ob_orig = *curves_id_orig_->surface;
+ Mesh &surface_orig = *static_cast<Mesh *>(surface_ob_orig.data);
+ if (surface_orig.totpoly == 0) {
+ report_empty_original_surface(stroke_extension.reports);
+ return;
+ }
+
+ surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, &surface_ob_orig);
+ if (surface_ob_eval_ == nullptr) {
+ return;
+ }
+ surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
+ if (surface_eval_->totpoly == 0) {
+ report_empty_evaluated_surface(stroke_extension.reports);
+ return;
+ }
+ surface_verts_eval_ = surface_eval_->verts();
+ surface_loops_eval_ = surface_eval_->loops();
+ surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_),
+ BKE_mesh_runtime_looptri_len(surface_eval_)};
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
@@ -143,56 +164,63 @@ struct AddOperationExecutor {
return;
}
+ /* Find UV map. */
+ VArraySpan<float2> surface_uv_map;
+ if (curves_id_orig_->surface_uv_map != nullptr) {
+ surface_uv_map = surface_orig.attributes().lookup<float2>(curves_id_orig_->surface_uv_map,
+ ATTR_DOMAIN_CORNER);
+ surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>(
+ curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER);
+ }
+
+ if (surface_uv_map.is_empty()) {
+ report_missing_uv_map_on_original_surface(stroke_extension.reports);
+ return;
+ }
+ if (surface_uv_map_eval_.is_empty()) {
+ report_missing_uv_map_on_evaluated_surface(stroke_extension.reports);
+ return;
+ }
+
const double time = PIL_check_seconds_timer() * 1000000.0;
/* Use a pointer cast to avoid overflow warnings. */
RandomNumberGenerator rng{*(uint32_t *)(&time)};
- BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
-
- surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
- BKE_mesh_runtime_looptri_len(surface_)};
-
/* Sample points on the surface using one of multiple strategies. */
- AddedPoints added_points;
+ Vector<float2> sampled_uvs;
if (add_amount_ == 1) {
- this->sample_in_center_with_symmetry(added_points);
+ this->sample_in_center_with_symmetry(sampled_uvs);
}
else if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
- this->sample_projected_with_symmetry(rng, added_points);
+ this->sample_projected_with_symmetry(rng, sampled_uvs);
}
else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
- this->sample_spherical_with_symmetry(rng, added_points);
+ this->sample_spherical_with_symmetry(rng, sampled_uvs);
}
else {
BLI_assert_unreachable();
}
- if (added_points.bary_coords.is_empty()) {
+ if (sampled_uvs.is_empty()) {
/* No new points have been added. */
return;
}
- /* Find UV map. */
- VArraySpan<float2> surface_uv_map;
- if (curves_id_->surface_uv_map != nullptr) {
- const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_);
- surface_uv_map = surface_attributes.lookup<float2>(curves_id_->surface_uv_map,
- ATTR_DOMAIN_CORNER);
- }
+ const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(&surface_orig),
+ BKE_mesh_runtime_looptri_len(&surface_orig)};
/* Find normals. */
- if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) {
- BKE_mesh_calc_normals_split(surface_);
+ if (!CustomData_has_layer(&surface_orig.ldata, CD_NORMAL)) {
+ BKE_mesh_calc_normals_split(&surface_orig);
}
const Span<float3> corner_normals_su = {
- reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
- surface_->totloop};
+ reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig.ldata, CD_NORMAL)),
+ surface_orig.totloop};
+
+ const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig};
geometry::AddCurvesOnMeshInputs add_inputs;
- add_inputs.root_positions_cu = added_points.positions_cu;
- add_inputs.bary_coords = added_points.bary_coords;
- add_inputs.looptri_indices = added_points.looptri_indices;
+ add_inputs.uvs = sampled_uvs;
add_inputs.interpolate_length = brush_settings_->flag &
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH;
add_inputs.interpolate_shape = brush_settings_->flag &
@@ -201,13 +229,10 @@ struct AddOperationExecutor {
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT;
add_inputs.fallback_curve_length = brush_settings_->curve_length;
add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
- add_inputs.surface = surface_;
- add_inputs.surface_bvh = &surface_bvh_;
- add_inputs.surface_looptris = surface_looptris_;
- add_inputs.surface_uv_map = surface_uv_map;
+ add_inputs.transforms = &transforms_;
+ add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
+ add_inputs.surface = &surface_orig;
add_inputs.corner_normals_su = corner_normals_su;
- add_inputs.curves_to_surface_mat = transforms_.curves_to_surface;
- add_inputs.surface_to_curves_normal_mat = transforms_.surface_to_curves_normal;
if (add_inputs.interpolate_length || add_inputs.interpolate_shape ||
add_inputs.interpolate_point_count) {
@@ -215,17 +240,22 @@ struct AddOperationExecutor {
add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_;
}
- geometry::add_curves_on_mesh(*curves_, add_inputs);
+ const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
+ *curves_orig_, add_inputs);
+
+ if (add_outputs.uv_error) {
+ report_invalid_uv_map(stroke_extension.reports);
+ }
- DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
ED_region_tag_redraw(ctx_.region);
}
/**
* Sample a single point exactly at the mouse position.
*/
- void sample_in_center_with_symmetry(AddedPoints &r_added_points)
+ void sample_in_center_with_symmetry(Vector<float2> &r_sampled_uvs)
{
float3 ray_start_wo, ray_end_wo;
ED_view3d_win_to_segment_clipped(
@@ -234,15 +264,15 @@ struct AddOperationExecutor {
const float3 ray_end_cu = transforms_.world_to_curves * ray_end_wo;
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
const float4x4 transform = transforms_.curves_to_surface * brush_transform;
- this->sample_in_center(r_added_points, transform * ray_start_cu, transform * ray_end_cu);
+ this->sample_in_center(r_sampled_uvs, transform * ray_start_cu, transform * ray_end_cu);
}
}
- void sample_in_center(AddedPoints &r_added_points,
+ void sample_in_center(Vector<float2> &r_sampled_uvs,
const float3 &ray_start_su,
const float3 &ray_end_su)
{
@@ -251,58 +281,61 @@ struct AddOperationExecutor {
BVHTreeRayHit ray_hit;
ray_hit.dist = FLT_MAX;
ray_hit.index = -1;
- BLI_bvhtree_ray_cast(surface_bvh_.tree,
+ BLI_bvhtree_ray_cast(surface_bvh_eval_.tree,
ray_start_su,
ray_direction_su,
0.0f,
&ray_hit,
- surface_bvh_.raycast_callback,
- &surface_bvh_);
+ surface_bvh_eval_.raycast_callback,
+ &surface_bvh_eval_);
if (ray_hit.index == -1) {
return;
}
const int looptri_index = ray_hit.index;
+ const MLoopTri &looptri = surface_looptris_eval_[looptri_index];
const float3 brush_pos_su = ray_hit.co;
const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
- *surface_, surface_looptris_[looptri_index], brush_pos_su);
+ surface_verts_eval_, surface_loops_eval_, looptri, brush_pos_su);
- const float3 brush_pos_cu = transforms_.surface_to_curves * brush_pos_su;
-
- r_added_points.positions_cu.append(brush_pos_cu);
- r_added_points.bary_coords.append(bary_coords);
- r_added_points.looptri_indices.append(looptri_index);
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords, looptri, surface_uv_map_eval_);
+ r_sampled_uvs.append(uv);
}
/**
* Sample points by shooting rays within the brush radius in the 3D view.
*/
- void sample_projected_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points)
+ void sample_projected_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs)
{
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
- this->sample_projected(rng, r_added_points, brush_transform);
+ this->sample_projected(rng, r_sampled_uvs, brush_transform);
}
}
void sample_projected(RandomNumberGenerator &rng,
- AddedPoints &r_added_points,
+ Vector<float2> &r_sampled_uvs,
const float4x4 &brush_transform)
{
- const int old_amount = r_added_points.bary_coords.size();
+ const int old_amount = r_sampled_uvs.size();
const int max_iterations = 100;
int current_iteration = 0;
- while (r_added_points.bary_coords.size() < old_amount + add_amount_) {
+ while (r_sampled_uvs.size() < old_amount + add_amount_) {
if (current_iteration++ >= max_iterations) {
break;
}
- const int missing_amount = add_amount_ + old_amount - r_added_points.bary_coords.size();
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+ Vector<float3> positions_su;
+
+ const int missing_amount = add_amount_ + old_amount - r_sampled_uvs.size();
const int new_points = bke::mesh_surface_sample::sample_surface_points_projected(
rng,
- *surface_,
- surface_bvh_,
+ *surface_eval_,
+ surface_bvh_eval_,
brush_pos_re_,
brush_radius_re_,
[&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
@@ -317,11 +350,14 @@ struct AddOperationExecutor {
use_front_face_,
add_amount_,
missing_amount,
- r_added_points.bary_coords,
- r_added_points.looptri_indices,
- r_added_points.positions_cu);
- for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) {
- pos = transforms_.surface_to_curves * pos;
+ bary_coords,
+ looptri_indices,
+ positions_su);
+
+ for (const int i : IndexRange(new_points)) {
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_);
+ r_sampled_uvs.append(uv);
}
}
}
@@ -329,13 +365,13 @@ struct AddOperationExecutor {
/**
* Sample points in a 3D sphere around the surface position that the mouse hovers over.
*/
- void sample_spherical_with_symmetry(RandomNumberGenerator &rng, AddedPoints &r_added_points)
+ void sample_spherical_with_symmetry(RandomNumberGenerator &rng, Vector<float2> &r_sampled_uvs)
{
const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
*ctx_.region,
*ctx_.v3d,
transforms_,
- surface_bvh_,
+ surface_bvh_eval_,
brush_pos_re_,
brush_radius_re_);
if (!brush_3d.has_value()) {
@@ -355,7 +391,7 @@ struct AddOperationExecutor {
const float3 view_ray_end_cu = transforms_.world_to_curves * view_ray_end_wo;
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
const float4x4 transform = transforms_.curves_to_surface * brush_transform;
@@ -365,13 +401,12 @@ struct AddOperationExecutor {
const float brush_radius_su = transform_brush_radius(
transform, brush_3d->position_cu, brush_3d->radius_cu);
- this->sample_spherical(
- rng, r_added_points, brush_pos_su, brush_radius_su, view_direction_su);
+ this->sample_spherical(rng, r_sampled_uvs, brush_pos_su, brush_radius_su, view_direction_su);
}
}
void sample_spherical(RandomNumberGenerator &rng,
- AddedPoints &r_added_points,
+ Vector<float2> &r_sampled_uvs,
const float3 &brush_pos_su,
const float brush_radius_su,
const float3 &view_direction_su)
@@ -379,32 +414,32 @@ struct AddOperationExecutor {
const float brush_radius_sq_su = pow2f(brush_radius_su);
/* Find surface triangles within brush radius. */
- Vector<int> looptri_indices;
+ Vector<int> selected_looptri_indices;
if (use_front_face_) {
BLI_bvhtree_range_query_cpp(
- *surface_bvh_.tree,
+ *surface_bvh_eval_.tree,
brush_pos_su,
brush_radius_su,
[&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) {
- const MLoopTri &looptri = surface_looptris_[index];
- const float3 v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co;
- const float3 v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co;
- const float3 v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co;
+ const MLoopTri &looptri = surface_looptris_eval_[index];
+ const float3 v0_su = surface_verts_eval_[surface_loops_eval_[looptri.tri[0]].v].co;
+ const float3 v1_su = surface_verts_eval_[surface_loops_eval_[looptri.tri[1]].v].co;
+ const float3 v2_su = surface_verts_eval_[surface_loops_eval_[looptri.tri[2]].v].co;
float3 normal_su;
normal_tri_v3(normal_su, v0_su, v1_su, v2_su);
if (math::dot(normal_su, view_direction_su) >= 0.0f) {
return;
}
- looptri_indices.append(index);
+ selected_looptri_indices.append(index);
});
}
else {
BLI_bvhtree_range_query_cpp(
- *surface_bvh_.tree,
+ *surface_bvh_eval_.tree,
brush_pos_su,
brush_radius_su,
[&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) {
- looptri_indices.append(index);
+ selected_looptri_indices.append(index);
});
}
@@ -418,42 +453,45 @@ struct AddOperationExecutor {
const int max_iterations = 5;
int current_iteration = 0;
- const int old_amount = r_added_points.bary_coords.size();
- while (r_added_points.bary_coords.size() < old_amount + add_amount_) {
+ const int old_amount = r_sampled_uvs.size();
+ while (r_sampled_uvs.size() < old_amount + add_amount_) {
if (current_iteration++ >= max_iterations) {
break;
}
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
+ Vector<float3> positions_su;
const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical(
rng,
- *surface_,
- looptri_indices,
+ *surface_eval_,
+ selected_looptri_indices,
brush_pos_su,
brush_radius_su,
approximate_density_su,
- r_added_points.bary_coords,
- r_added_points.looptri_indices,
- r_added_points.positions_cu);
- for (float3 &pos : r_added_points.positions_cu.as_mutable_span().take_back(new_points)) {
- pos = transforms_.surface_to_curves * pos;
+ bary_coords,
+ looptri_indices,
+ positions_su);
+ for (const int i : IndexRange(new_points)) {
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_);
+ r_sampled_uvs.append(uv);
}
}
/* Remove samples when there are too many. */
- while (r_added_points.bary_coords.size() > old_amount + add_amount_) {
+ while (r_sampled_uvs.size() > old_amount + add_amount_) {
const int index_to_remove = rng.get_int32(add_amount_) + old_amount;
- r_added_points.bary_coords.remove_and_reorder(index_to_remove);
- r_added_points.looptri_indices.remove_and_reorder(index_to_remove);
- r_added_points.positions_cu.remove_and_reorder(index_to_remove);
+ r_sampled_uvs.remove_and_reorder(index_to_remove);
}
}
void ensure_curve_roots_kdtree()
{
if (self_->curve_roots_kdtree_ == nullptr) {
- self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num());
- for (const int curve_i : curves_->curves_range()) {
- const int root_point_i = curves_->offsets()[curve_i];
- const float3 &root_pos_cu = curves_->positions()[root_point_i];
+ self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num());
+ for (const int curve_i : curves_orig_->curves_range()) {
+ const int root_point_i = curves_orig_->offsets()[curve_i];
+ const float3 &root_pos_cu = curves_orig_->positions()[root_point_i];
BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu);
}
BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
@@ -467,17 +505,8 @@ void AddOperation::on_stroke_extended(const bContext &C, const StrokeExtension &
executor.execute(*this, C, stroke_extension);
}
-std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C,
- ReportList *reports)
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation()
{
- const Object &ob_active = *CTX_data_active_object(&C);
- BLI_assert(ob_active.type == OB_CURVES);
- const Curves &curves_id = *static_cast<Curves *>(ob_active.data);
- if (curves_id.surface == nullptr || curves_id.surface->type != OB_MESH) {
- BKE_report(reports, RPT_WARNING, "Can not use Add brush when there is no surface mesh");
- return {};
- }
-
return std::make_unique<AddOperation>();
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
index 10564942ab9..02bf7aacd93 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_brush.cc
@@ -8,6 +8,9 @@
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
#include "ED_view3d.h"
@@ -20,6 +23,10 @@
#include "BLI_length_parameterize.hh"
#include "BLI_task.hh"
+#include "DEG_depsgraph_query.h"
+
+#include "BLT_translation.h"
+
/**
* The code below uses a prefix naming convention to indicate the coordinate space:
* cu: Local space of the curves object that is being edited.
@@ -48,7 +55,8 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
const float brush_radius_re,
const ARegion &region,
const RegionView3D &rv3d,
- const Object &object)
+ const Object &object,
+ const Span<float3> positions)
{
/* This value might have to be adjusted based on user feedback. */
const float brush_inner_radius_re = std::min<float>(brush_radius_re, (float)UI_UNIT_X / 3.0f);
@@ -88,8 +96,6 @@ static std::optional<float3> find_curves_brush_position(const CurvesGeometry &cu
}
};
- const Span<float3> positions = curves.positions();
-
BrushPositionCandidate best_candidate = threading::parallel_reduce(
curves.curves_range(),
128,
@@ -175,20 +181,21 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
{
const Curves &curves_id = *static_cast<Curves *>(curves_object.data);
const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- const Object *surface_object = curves_id.surface;
+ Object *surface_object = curves_id.surface;
+ Object *surface_object_eval = DEG_get_evaluated_object(&depsgraph, surface_object);
float3 center_ray_start_wo, center_ray_end_wo;
ED_view3d_win_to_segment_clipped(
&depsgraph, &region, &v3d, brush_pos_re, center_ray_start_wo, center_ray_end_wo, true);
/* Shorten ray when the surface object is hit. */
- if (surface_object != nullptr) {
+ if (surface_object_eval != nullptr) {
const float4x4 surface_to_world_mat = surface_object->obmat;
const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
- Mesh &surface = *static_cast<Mesh *>(surface_object->data);
+ Mesh *surface_eval = BKE_object_get_evaluated_mesh(surface_object_eval);
BVHTreeFromMesh surface_bvh;
- BKE_bvhtree_from_mesh_get(&surface_bvh, &surface, BVHTREE_FROM_LOOPTRI, 2);
+ BKE_bvhtree_from_mesh_get(&surface_bvh, surface_eval, BVHTREE_FROM_LOOPTRI, 2);
BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
const float3 center_ray_start_su = world_to_surface_mat * center_ray_start_wo;
@@ -222,6 +229,9 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
const float3 center_ray_start_cu = world_to_curves_mat * center_ray_start_wo;
const float3 center_ray_end_cu = world_to_curves_mat * center_ray_end_wo;
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_object);
+
const std::optional<float3> brush_position_optional_cu = find_curves_brush_position(
curves,
center_ray_start_cu,
@@ -229,7 +239,8 @@ std::optional<CurvesBrush3D> sample_curves_3d_brush(const Depsgraph &depsgraph,
brush_radius_re,
region,
rv3d,
- curves_object);
+ curves_object,
+ deformation.positions);
if (!brush_position_optional_cu.has_value()) {
/* Nothing found. */
return std::nullopt;
@@ -341,33 +352,37 @@ float transform_brush_radius(const float4x4 &transform,
return math::distance(new_position, new_offset_position);
}
-void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position)
+void move_last_point_and_resample(MoveAndResampleBuffers &buffer,
+ MutableSpan<float3> positions,
+ const float3 &new_last_position)
{
/* Find the accumulated length of each point in the original curve,
* treating it as a poly curve for performance reasons and simplicity. */
- Array<float> orig_lengths(length_parameterize::segments_num(positions.size(), false));
- length_parameterize::accumulate_lengths<float3>(positions, false, orig_lengths);
- const float orig_total_length = orig_lengths.last();
+ buffer.orig_lengths.reinitialize(length_parameterize::segments_num(positions.size(), false));
+ length_parameterize::accumulate_lengths<float3>(positions, false, buffer.orig_lengths);
+ const float orig_total_length = buffer.orig_lengths.last();
/* Find the factor by which the new curve is shorter or longer than the original. */
const float new_last_segment_length = math::distance(positions.last(1), new_last_position);
- const float new_total_length = orig_lengths.last(1) + new_last_segment_length;
+ const float new_total_length = buffer.orig_lengths.last(1) + new_last_segment_length;
const float length_factor = safe_divide(new_total_length, orig_total_length);
/* Calculate the lengths to sample the original curve with by scaling the original lengths. */
- Array<float> new_lengths(positions.size() - 1);
- new_lengths.first() = 0.0f;
- for (const int i : new_lengths.index_range().drop_front(1)) {
- new_lengths[i] = orig_lengths[i - 1] * length_factor;
+ buffer.new_lengths.reinitialize(positions.size() - 1);
+ buffer.new_lengths.first() = 0.0f;
+ for (const int i : buffer.new_lengths.index_range().drop_front(1)) {
+ buffer.new_lengths[i] = buffer.orig_lengths[i - 1] * length_factor;
}
- Array<int> indices(positions.size() - 1);
- Array<float> factors(positions.size() - 1);
- length_parameterize::sample_at_lengths(orig_lengths, new_lengths, indices, factors);
+ buffer.sample_indices.reinitialize(positions.size() - 1);
+ buffer.sample_factors.reinitialize(positions.size() - 1);
+ length_parameterize::sample_at_lengths(
+ buffer.orig_lengths, buffer.new_lengths, buffer.sample_indices, buffer.sample_factors);
- Array<float3> new_positions(positions.size() - 1);
- length_parameterize::linear_interpolation<float3>(positions, indices, factors, new_positions);
- positions.drop_back(1).copy_from(new_positions);
+ buffer.new_positions.reinitialize(positions.size() - 1);
+ length_parameterize::interpolate<float3>(
+ positions, buffer.sample_indices, buffer.sample_factors, buffer.new_positions);
+ positions.drop_back(1).copy_from(buffer.new_positions);
positions.last() = new_last_position;
}
@@ -380,4 +395,36 @@ CurvesSculptCommonContext::CurvesSculptCommonContext(const bContext &C)
this->rv3d = CTX_wm_region_view3d(&C);
}
+void report_empty_original_surface(ReportList *reports)
+{
+ BKE_report(reports, RPT_WARNING, TIP_("Original surface mesh is empty"));
+}
+
+void report_empty_evaluated_surface(ReportList *reports)
+{
+ BKE_report(reports, RPT_WARNING, TIP_("Evaluated surface mesh is empty"));
+}
+
+void report_missing_surface(ReportList *reports)
+{
+ BKE_report(reports, RPT_WARNING, TIP_("Missing surface mesh"));
+}
+
+void report_missing_uv_map_on_original_surface(ReportList *reports)
+{
+ BKE_report(
+ reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on original surface"));
+}
+
+void report_missing_uv_map_on_evaluated_surface(ReportList *reports)
+{
+ BKE_report(
+ reports, RPT_WARNING, TIP_("Missing UV map for attaching curves on evaluated surface"));
+}
+
+void report_invalid_uv_map(ReportList *reports)
+{
+ BKE_report(reports, RPT_WARNING, TIP_("Invalid UV map: UV islands must not overlap"));
+}
+
} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
index 449f1786167..52f2ddc6550 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_comb.cc
@@ -13,12 +13,15 @@
#include "PIL_time.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
#include "BKE_attribute_math.hh"
#include "BKE_brush.h"
#include "BKE_bvhutils.h"
#include "BKE_context.h"
+#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
+#include "BKE_geometry_set.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_paint.h"
@@ -88,9 +91,9 @@ struct CombOperationExecutor {
eBrushFalloffShape falloff_shape_;
- Object *object_ = nullptr;
- Curves *curves_id_ = nullptr;
- CurvesGeometry *curves_ = nullptr;
+ Object *curves_ob_orig_ = nullptr;
+ Curves *curves_id_orig_ = nullptr;
+ CurvesGeometry *curves_orig_ = nullptr;
VArray<float> point_factors_;
Vector<int64_t> selected_curve_indices_;
@@ -112,7 +115,12 @@ struct CombOperationExecutor {
BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = stroke_extension.mouse_position; });
- object_ = CTX_data_active_object(&C);
+ curves_ob_orig_ = CTX_data_active_object(&C);
+ curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
+ curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry);
+ if (curves_orig_->curves_num() == 0) {
+ return;
+ }
curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
@@ -122,16 +130,10 @@ struct CombOperationExecutor {
falloff_shape_ = static_cast<eBrushFalloffShape>(brush_->falloff_shape);
- curves_id_ = static_cast<Curves *>(object_->data);
- curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
- if (curves_->curves_num() == 0) {
- return;
- }
-
- transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
+ transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
- point_factors_ = get_point_selection(*curves_id_);
- curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+ point_factors_ = get_point_selection(*curves_id_orig_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_);
brush_pos_prev_re_ = self_->brush_pos_last_re_;
brush_pos_re_ = stroke_extension.mouse_position;
@@ -160,9 +162,9 @@ struct CombOperationExecutor {
this->restore_segment_lengths(changed_curves);
- curves_->tag_positions_changed();
- DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ curves_orig_->tag_positions_changed();
+ DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
ED_region_tag_redraw(ctx_.region);
}
@@ -172,7 +174,7 @@ struct CombOperationExecutor {
void comb_projected_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves)
{
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
this->comb_projected(r_changed_curves, brush_transform);
}
@@ -183,10 +185,12 @@ struct CombOperationExecutor {
{
const float4x4 brush_transform_inv = brush_transform.inverted();
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ MutableSpan<float3> positions_cu_orig = curves_orig_->positions_for_write();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
float4x4 projection;
- ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
const float brush_radius_sq_re = pow2f(brush_radius_re);
@@ -195,16 +199,18 @@ struct CombOperationExecutor {
Vector<int> &local_changed_curves = r_changed_curves.local();
for (const int curve_i : curve_selection_.slice(range)) {
bool curve_changed = false;
- const IndexRange points = curves_->points_for_curve(curve_i);
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
- const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i];
+ const float3 old_pos_cu = deformation.positions[point_i];
+ const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
/* Find the position of the point in screen space. */
- float2 old_pos_re;
- ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values);
+ float2 old_symm_pos_re;
+ ED_view3d_project_float_v2_m4(
+ ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values);
const float distance_to_brush_sq_re = dist_squared_to_line_segment_v2(
- old_pos_re, brush_pos_prev_re_, brush_pos_re_);
+ old_symm_pos_re, brush_pos_prev_re_, brush_pos_re_);
if (distance_to_brush_sq_re > brush_radius_sq_re) {
/* Ignore the point because it's too far away. */
continue;
@@ -219,16 +225,20 @@ struct CombOperationExecutor {
/* Offset the old point position in screen space and transform it back into 3D space.
*/
- const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight;
- float3 new_position_wo;
+ const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight;
+ float3 new_symm_pos_wo;
ED_view3d_win_to_3d(ctx_.v3d,
ctx_.region,
- transforms_.curves_to_world * old_pos_cu,
- new_position_re,
- new_position_wo);
- const float3 new_position_cu = brush_transform *
- (transforms_.world_to_curves * new_position_wo);
- positions_cu[point_i] = new_position_cu;
+ transforms_.curves_to_world * old_symm_pos_cu,
+ new_symm_pos_re,
+ new_symm_pos_wo);
+ const float3 new_pos_cu = brush_transform *
+ (transforms_.world_to_curves * new_symm_pos_wo);
+
+ const float3 translation_eval = new_pos_cu - old_pos_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ point_i, translation_eval);
+ positions_cu_orig[point_i] += translation_orig;
curve_changed = true;
}
@@ -245,7 +255,7 @@ struct CombOperationExecutor {
void comb_spherical_with_symmetry(EnumerableThreadSpecific<Vector<int>> &r_changed_curves)
{
float4x4 projection;
- ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
float3 brush_start_wo, brush_end_wo;
ED_view3d_win_to_3d(ctx_.v3d,
@@ -264,7 +274,7 @@ struct CombOperationExecutor {
const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
this->comb_spherical(r_changed_curves,
brush_transform * brush_start_cu,
@@ -278,17 +288,20 @@ struct CombOperationExecutor {
const float3 &brush_end_cu,
const float brush_radius_cu)
{
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ MutableSpan<float3> positions_cu = curves_orig_->positions_for_write();
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
+
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
Vector<int> &local_changed_curves = r_changed_curves.local();
for (const int curve_i : curve_selection_.slice(range)) {
bool curve_changed = false;
- const IndexRange points = curves_->points_for_curve(curve_i);
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
- const float3 pos_old_cu = positions_cu[point_i];
+ const float3 pos_old_cu = deformation.positions[point_i];
/* Compute distance to the brush. */
const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
@@ -306,8 +319,12 @@ struct CombOperationExecutor {
/* Combine the falloff and brush strength. */
const float weight = brush_strength_ * radius_falloff * point_factors_[point_i];
+ const float3 translation_eval_cu = weight * brush_diff_cu;
+ const float3 translation_orig_cu = deformation.translation_from_deformed_to_original(
+ point_i, translation_eval_cu);
+
/* Update the point position. */
- positions_cu[point_i] = pos_old_cu + weight * brush_diff_cu;
+ positions_cu[point_i] += translation_orig_cu;
curve_changed = true;
}
if (curve_changed) {
@@ -326,7 +343,7 @@ struct CombOperationExecutor {
*ctx_.region,
*ctx_.v3d,
*ctx_.rv3d,
- *object_,
+ *curves_ob_orig_,
brush_pos_re_,
brush_radius_base_re_);
if (brush_3d.has_value()) {
@@ -340,11 +357,11 @@ struct CombOperationExecutor {
*/
void initialize_segment_lengths()
{
- const Span<float3> positions_cu = curves_->positions();
- self_->segment_lengths_cu_.reinitialize(curves_->points_num());
- threading::parallel_for(curves_->curves_range(), 128, [&](const IndexRange range) {
+ const Span<float3> positions_cu = curves_orig_->positions();
+ self_->segment_lengths_cu_.reinitialize(curves_orig_->points_num());
+ threading::parallel_for(curves_orig_->curves_range(), 128, [&](const IndexRange range) {
for (const int curve_i : range) {
- const IndexRange points = curves_->points_for_curve(curve_i);
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
for (const int point_i : points.drop_back(1)) {
const float3 &p1_cu = positions_cu[point_i];
const float3 &p2_cu = positions_cu[point_i + 1];
@@ -361,12 +378,12 @@ struct CombOperationExecutor {
void restore_segment_lengths(EnumerableThreadSpecific<Vector<int>> &changed_curves)
{
const Span<float> expected_lengths_cu = self_->segment_lengths_cu_;
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
+ MutableSpan<float3> positions_cu = curves_orig_->positions_for_write();
threading::parallel_for_each(changed_curves, [&](const Vector<int> &changed_curves) {
threading::parallel_for(changed_curves.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : changed_curves.as_span().slice(range)) {
- const IndexRange points = curves_->points_for_curve(curve_i);
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
for (const int segment_i : points.drop_back(1)) {
const float3 &p1_cu = positions_cu[segment_i];
float3 &p2_cu = positions_cu[segment_i + 1];
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
index 777ebd16110..a44499ce133 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_delete.cc
@@ -51,6 +51,12 @@ using blender::bke::CurvesGeometry;
class DeleteOperation : public CurvesSculptStrokeOperation {
private:
CurvesBrush3D brush_3d_;
+ /**
+ * Need to store those in case the brush is evaluated more than once before the curves are
+ * evaluated again. This can happen when the mouse is moved quickly and the brush spacing is
+ * small.
+ */
+ Vector<float3> deformed_positions_;
friend struct DeleteOperationExecutor;
@@ -109,6 +115,9 @@ struct DeleteOperationExecutor {
if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
this->initialize_spherical_brush_reference_point();
}
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+ self_->deformed_positions_ = deformation.positions;
}
Array<bool> curves_to_delete(curves_->curves_num(), false);
@@ -123,12 +132,22 @@ struct DeleteOperationExecutor {
}
Vector<int64_t> indices;
- const IndexMask mask = index_mask_ops::find_indices_based_on_predicate(
+ const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate(
curves_->curves_range(), 4096, indices, [&](const int curve_i) {
return curves_to_delete[curve_i];
});
- curves_->remove_curves(mask);
+ /* Remove deleted curves from the stored deformed positions. */
+ const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert(
+ curves_->curves_range());
+ Vector<float3> new_deformed_positions;
+ for (const IndexRange curves_range : ranges_to_keep) {
+ new_deformed_positions.extend(
+ self_->deformed_positions_.as_span().slice(curves_->points_for_curves(curves_range)));
+ }
+ self_->deformed_positions_ = std::move(new_deformed_positions);
+
+ curves_->remove_curves(mask_to_delete);
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
@@ -151,8 +170,6 @@ struct DeleteOperationExecutor {
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
- Span<float3> positions_cu = curves_->positions();
-
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
const float brush_radius_sq_re = pow2f(brush_radius_re);
@@ -160,7 +177,7 @@ struct DeleteOperationExecutor {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
if (points.size() == 1) {
- const float3 pos_cu = brush_transform_inv * positions_cu[points.first()];
+ const float3 pos_cu = brush_transform_inv * self_->deformed_positions_[points.first()];
float2 pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
@@ -171,8 +188,8 @@ struct DeleteOperationExecutor {
}
for (const int segment_i : points.drop_back(1)) {
- const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i];
- const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1];
+ const float3 pos1_cu = brush_transform_inv * self_->deformed_positions_[segment_i];
+ const float3 pos2_cu = brush_transform_inv * self_->deformed_positions_[segment_i + 1];
float2 pos1_re, pos2_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos1_cu, pos1_re, projection.values);
@@ -212,8 +229,6 @@ struct DeleteOperationExecutor {
void delete_spherical(const float3 &brush_cu, MutableSpan<bool> curves_to_delete)
{
- Span<float3> positions_cu = curves_->positions();
-
const float brush_radius_cu = self_->brush_3d_.radius_cu * brush_radius_factor_;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
@@ -222,7 +237,7 @@ struct DeleteOperationExecutor {
const IndexRange points = curves_->points_for_curve(curve_i);
if (points.size() == 1) {
- const float3 &pos_cu = positions_cu[points.first()];
+ const float3 &pos_cu = self_->deformed_positions_[points.first()];
const float distance_sq_cu = math::distance_squared(pos_cu, brush_cu);
if (distance_sq_cu < brush_radius_sq_cu) {
curves_to_delete[curve_i] = true;
@@ -231,8 +246,8 @@ struct DeleteOperationExecutor {
}
for (const int segment_i : points.drop_back(1)) {
- const float3 &pos1_cu = positions_cu[segment_i];
- const float3 &pos2_cu = positions_cu[segment_i + 1];
+ const float3 &pos1_cu = self_->deformed_positions_[segment_i];
+ const float3 &pos2_cu = self_->deformed_positions_[segment_i + 1];
const float distance_sq_cu = dist_squared_to_line_segment_v3(brush_cu, pos1_cu, pos2_cu);
if (distance_sq_cu > brush_radius_sq_cu) {
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc
index e211a568705..a37eb4bb560 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_density.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_density.cc
@@ -2,6 +2,7 @@
#include <numeric>
+#include "BKE_attribute_math.hh"
#include "BKE_brush.h"
#include "BKE_bvhutils.h"
#include "BKE_context.h"
@@ -9,11 +10,15 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
#include "BLI_index_mask_ops.hh"
#include "BLI_kdtree.h"
@@ -36,7 +41,11 @@ namespace blender::ed::sculpt_paint {
class DensityAddOperation : public CurvesSculptStrokeOperation {
private:
/** Used when some data should be interpolated from existing curves. */
- KDTree_3d *curve_roots_kdtree_ = nullptr;
+ KDTree_3d *original_curve_roots_kdtree_ = nullptr;
+ /** Contains curve roots of all curves that existed before the brush started. */
+ KDTree_3d *deformed_curve_roots_kdtree_ = nullptr;
+ /** Root positions of curves that have been added in the current brush stroke. */
+ Vector<float3> new_deformed_root_positions_;
int original_curve_num_ = 0;
friend struct DensityAddOperationExecutor;
@@ -44,8 +53,11 @@ class DensityAddOperation : public CurvesSculptStrokeOperation {
public:
~DensityAddOperation() override
{
- if (curve_roots_kdtree_ != nullptr) {
- BLI_kdtree_3d_free(curve_roots_kdtree_);
+ if (original_curve_roots_kdtree_ != nullptr) {
+ BLI_kdtree_3d_free(original_curve_roots_kdtree_);
+ }
+ if (deformed_curve_roots_kdtree_ != nullptr) {
+ BLI_kdtree_3d_free(deformed_curve_roots_kdtree_);
}
}
@@ -56,14 +68,18 @@ struct DensityAddOperationExecutor {
DensityAddOperation *self_ = nullptr;
CurvesSculptCommonContext ctx_;
- Object *object_ = nullptr;
- Curves *curves_id_ = nullptr;
- CurvesGeometry *curves_ = nullptr;
+ Object *curves_ob_orig_ = nullptr;
+ Curves *curves_id_orig_ = nullptr;
+ CurvesGeometry *curves_orig_ = nullptr;
+
+ Object *surface_ob_orig_ = nullptr;
+ Mesh *surface_orig_ = nullptr;
- Object *surface_ob_ = nullptr;
- Mesh *surface_ = nullptr;
- Span<MLoopTri> surface_looptris_;
- Span<float3> corner_normals_su_;
+ Object *surface_ob_eval_ = nullptr;
+ Mesh *surface_eval_ = nullptr;
+ Span<MLoopTri> surface_looptris_eval_;
+ VArraySpan<float2> surface_uv_map_eval_;
+ BVHTreeFromMesh surface_bvh_eval_;
const CurvesSculpt *curves_sculpt_ = nullptr;
const Brush *brush_ = nullptr;
@@ -75,8 +91,6 @@ struct DensityAddOperationExecutor {
CurvesSurfaceTransforms transforms_;
- BVHTreeFromMesh surface_bvh_;
-
DensityAddOperationExecutor(const bContext &C) : ctx_(C)
{
}
@@ -86,32 +100,58 @@ struct DensityAddOperationExecutor {
const StrokeExtension &stroke_extension)
{
self_ = &self;
- object_ = CTX_data_active_object(&C);
- curves_id_ = static_cast<Curves *>(object_->data);
- curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
+ curves_ob_orig_ = CTX_data_active_object(&C);
+ curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
+ curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry);
if (stroke_extension.is_first) {
- self_->original_curve_num_ = curves_->curves_num();
+ self_->original_curve_num_ = curves_orig_->curves_num();
}
- if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) {
+ if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
+ report_missing_surface(stroke_extension.reports);
return;
}
- surface_ob_ = curves_id_->surface;
- surface_ = static_cast<Mesh *>(surface_ob_->data);
-
- surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
- BKE_mesh_runtime_looptri_len(surface_)};
+ surface_ob_orig_ = curves_id_orig_->surface;
+ surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
+ if (surface_orig_->totpoly == 0) {
+ report_empty_original_surface(stroke_extension.reports);
+ return;
+ }
- transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
+ surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_);
+ if (surface_ob_eval_ == nullptr) {
+ return;
+ }
+ surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
+ if (surface_eval_->totpoly == 0) {
+ report_empty_evaluated_surface(stroke_extension.reports);
+ return;
+ }
- if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) {
- BKE_mesh_calc_normals_split(surface_);
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
+ surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_),
+ BKE_mesh_runtime_looptri_len(surface_eval_)};
+ /* Find UV map. */
+ VArraySpan<float2> surface_uv_map;
+ if (curves_id_orig_->surface_uv_map != nullptr) {
+ surface_uv_map = surface_orig_->attributes().lookup<float2>(curves_id_orig_->surface_uv_map,
+ ATTR_DOMAIN_CORNER);
+ surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>(
+ curves_id_orig_->surface_uv_map, ATTR_DOMAIN_CORNER);
+ }
+ if (surface_uv_map.is_empty()) {
+ report_missing_uv_map_on_original_surface(stroke_extension.reports);
+ return;
+ }
+ if (surface_uv_map_eval_.is_empty()) {
+ report_missing_uv_map_on_evaluated_surface(stroke_extension.reports);
+ return;
}
- corner_normals_su_ = {
- reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
- surface_->totloop};
+
+ transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
@@ -123,23 +163,17 @@ struct DensityAddOperationExecutor {
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
brush_->falloff_shape);
- BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
-
- Vector<float3> new_bary_coords;
- Vector<int> new_looptri_indices;
Vector<float3> new_positions_cu;
+ Vector<float2> new_uvs;
const double time = PIL_check_seconds_timer() * 1000000.0;
RandomNumberGenerator rng{*(uint32_t *)(&time)};
/* Find potential new curve root points. */
if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
- this->sample_projected_with_symmetry(
- rng, new_bary_coords, new_looptri_indices, new_positions_cu);
+ this->sample_projected_with_symmetry(rng, new_uvs, new_positions_cu);
}
else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
- this->sample_spherical_with_symmetry(
- rng, new_bary_coords, new_looptri_indices, new_positions_cu);
+ this->sample_spherical_with_symmetry(rng, new_uvs, new_positions_cu);
}
else {
BLI_assert_unreachable();
@@ -148,9 +182,11 @@ struct DensityAddOperationExecutor {
pos = transforms_.surface_to_curves * pos;
}
- this->ensure_curve_roots_kdtree();
+ if (stroke_extension.is_first) {
+ this->prepare_curve_roots_kdtrees();
+ }
- const int already_added_curves = curves_->curves_num() - self_->original_curve_num_;
+ const int already_added_curves = self_->new_deformed_root_positions_.size();
KDTree_3d *new_roots_kdtree = BLI_kdtree_3d_new(already_added_curves +
new_positions_cu.size());
BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(new_roots_kdtree); });
@@ -159,17 +195,15 @@ struct DensityAddOperationExecutor {
* curves. */
Array<bool> new_curve_skipped(new_positions_cu.size(), false);
threading::parallel_invoke(
+ 512 < already_added_curves + new_positions_cu.size(),
/* Build kdtree from root points created by the current stroke. */
[&]() {
- const Span<float3> positions_cu = curves_->positions();
- for (const int curve_i : curves_->curves_range().take_back(already_added_curves)) {
- const float3 &root_pos_cu = positions_cu[curves_->offsets()[curve_i]];
- BLI_kdtree_3d_insert(new_roots_kdtree, curve_i, root_pos_cu);
+ for (const int i : IndexRange(already_added_curves)) {
+ BLI_kdtree_3d_insert(new_roots_kdtree, -1, self_->new_deformed_root_positions_[i]);
}
for (const int new_i : new_positions_cu.index_range()) {
- const int index_in_kdtree = curves_->curves_num() + new_i;
const float3 &root_pos_cu = new_positions_cu[new_i];
- BLI_kdtree_3d_insert(new_roots_kdtree, index_in_kdtree, root_pos_cu);
+ BLI_kdtree_3d_insert(new_roots_kdtree, new_i, root_pos_cu);
}
BLI_kdtree_3d_balance(new_roots_kdtree);
},
@@ -183,7 +217,7 @@ struct DensityAddOperationExecutor {
KDTreeNearest_3d nearest;
nearest.dist = FLT_MAX;
BLI_kdtree_3d_find_nearest(
- self_->curve_roots_kdtree_, new_root_pos_cu, &nearest);
+ self_->deformed_curve_roots_kdtree_, new_root_pos_cu, &nearest);
if (nearest.dist < brush_settings_->minimum_distance) {
new_curve_skipped[new_i] = true;
}
@@ -201,12 +235,11 @@ struct DensityAddOperationExecutor {
new_roots_kdtree,
root_pos_cu,
brush_settings_->minimum_distance,
- [&](const int other_i, const float *UNUSED(co), float UNUSED(dist_sq)) {
- if (other_i < curves_->curves_num()) {
+ [&](const int other_new_i, const float *UNUSED(co), float UNUSED(dist_sq)) {
+ if (other_new_i == -1) {
new_curve_skipped[new_i] = true;
return false;
}
- const int other_new_i = other_i - curves_->curves_num();
if (new_i == other_new_i) {
return true;
}
@@ -219,31 +252,25 @@ struct DensityAddOperationExecutor {
for (int64_t i = new_positions_cu.size() - 1; i >= 0; i--) {
if (new_curve_skipped[i]) {
new_positions_cu.remove_and_reorder(i);
- new_bary_coords.remove_and_reorder(i);
- new_looptri_indices.remove_and_reorder(i);
+ new_uvs.remove_and_reorder(i);
}
}
-
- /* Find UV map. */
- VArraySpan<float2> surface_uv_map;
- if (curves_id_->surface_uv_map != nullptr) {
- bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_);
- surface_uv_map = surface_attributes.lookup<float2>(curves_id_->surface_uv_map,
- ATTR_DOMAIN_CORNER);
- }
+ self_->new_deformed_root_positions_.extend(new_positions_cu);
/* Find normals. */
- if (!CustomData_has_layer(&surface_->ldata, CD_NORMAL)) {
- BKE_mesh_calc_normals_split(surface_);
+ if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) {
+ BKE_mesh_calc_normals_split(surface_orig_);
}
const Span<float3> corner_normals_su = {
- reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
- surface_->totloop};
+ reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)),
+ surface_orig_->totloop};
+
+ const Span<MLoopTri> surface_looptris_orig = {BKE_mesh_runtime_looptri_ensure(surface_orig_),
+ BKE_mesh_runtime_looptri_len(surface_orig_)};
+ const geometry::ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_looptris_orig};
geometry::AddCurvesOnMeshInputs add_inputs;
- add_inputs.root_positions_cu = new_positions_cu;
- add_inputs.bary_coords = new_bary_coords;
- add_inputs.looptri_indices = new_looptri_indices;
+ add_inputs.uvs = new_uvs;
add_inputs.interpolate_length = brush_settings_->flag &
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_LENGTH;
add_inputs.interpolate_shape = brush_settings_->flag &
@@ -252,53 +279,73 @@ struct DensityAddOperationExecutor {
BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_POINT_COUNT;
add_inputs.fallback_curve_length = brush_settings_->curve_length;
add_inputs.fallback_point_count = std::max(2, brush_settings_->points_per_curve);
- add_inputs.surface = surface_;
- add_inputs.surface_bvh = &surface_bvh_;
- add_inputs.surface_looptris = surface_looptris_;
- add_inputs.surface_uv_map = surface_uv_map;
+ add_inputs.transforms = &transforms_;
+ add_inputs.surface = surface_orig_;
add_inputs.corner_normals_su = corner_normals_su;
- add_inputs.curves_to_surface_mat = transforms_.curves_to_surface;
- add_inputs.surface_to_curves_normal_mat = transforms_.surface_to_curves_normal;
- add_inputs.old_roots_kdtree = self_->curve_roots_kdtree_;
+ add_inputs.reverse_uv_sampler = &reverse_uv_sampler;
+ add_inputs.old_roots_kdtree = self_->original_curve_roots_kdtree_;
- geometry::add_curves_on_mesh(*curves_, add_inputs);
+ const geometry::AddCurvesOnMeshOutputs add_outputs = geometry::add_curves_on_mesh(
+ *curves_orig_, add_inputs);
- DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ if (add_outputs.uv_error) {
+ report_invalid_uv_map(stroke_extension.reports);
+ }
+
+ DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
ED_region_tag_redraw(ctx_.region);
}
- void ensure_curve_roots_kdtree()
+ void prepare_curve_roots_kdtrees()
{
- if (self_->curve_roots_kdtree_ == nullptr) {
- self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_->curves_num());
- for (const int curve_i : curves_->curves_range()) {
- const int root_point_i = curves_->offsets()[curve_i];
- const float3 &root_pos_cu = curves_->positions()[root_point_i];
- BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu);
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
+ const Span<int> curve_offsets = curves_orig_->offsets();
+ const Span<float3> original_positions = curves_orig_->positions();
+ const Span<float3> deformed_positions = deformation.positions;
+ BLI_assert(original_positions.size() == deformed_positions.size());
+
+ auto roots_kdtree_from_positions = [&](const Span<float3> positions) {
+ KDTree_3d *kdtree = BLI_kdtree_3d_new(curves_orig_->curves_num());
+ for (const int curve_i : curves_orig_->curves_range()) {
+ const int root_point_i = curve_offsets[curve_i];
+ BLI_kdtree_3d_insert(kdtree, curve_i, positions[root_point_i]);
}
- BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
- }
+ BLI_kdtree_3d_balance(kdtree);
+ return kdtree;
+ };
+
+ threading::parallel_invoke(
+ 1024 < original_positions.size() + deformed_positions.size(),
+ [&]() {
+ self_->original_curve_roots_kdtree_ = roots_kdtree_from_positions(original_positions);
+ },
+ [&]() {
+ self_->deformed_curve_roots_kdtree_ = roots_kdtree_from_positions(deformed_positions);
+ });
}
void sample_projected_with_symmetry(RandomNumberGenerator &rng,
- Vector<float3> &r_bary_coords,
- Vector<int> &r_looptri_indices,
+ Vector<float2> &r_uvs,
Vector<float3> &r_positions_su)
{
float4x4 projection;
- ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
const float4x4 brush_transform_inv = brush_transform.inverted();
const float4x4 transform = transforms_.curves_to_surface * brush_transform *
transforms_.world_to_curves;
+ Vector<float3> positions_su;
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
const int new_points = bke::mesh_surface_sample::sample_surface_points_projected(
rng,
- *surface_,
- surface_bvh_,
+ *surface_eval_,
+ surface_bvh_eval_,
brush_pos_re_,
brush_radius_re_,
[&](const float2 &pos_re, float3 &r_start_su, float3 &r_end_su) {
@@ -311,14 +358,13 @@ struct DensityAddOperationExecutor {
true,
brush_settings_->density_add_attempts,
brush_settings_->density_add_attempts,
- r_bary_coords,
- r_looptri_indices,
- r_positions_su);
+ bary_coords,
+ looptri_indices,
+ positions_su);
/* Remove some sampled points randomly based on the brush falloff and strength. */
- const int old_points = r_bary_coords.size() - new_points;
- for (int i = r_bary_coords.size() - 1; i >= old_points; i--) {
- const float3 pos_su = r_positions_su[i];
+ for (int i = new_points - 1; i >= 0; i--) {
+ const float3 pos_su = positions_su[i];
const float3 pos_cu = brush_transform_inv * transforms_.surface_to_curves * pos_su;
float2 pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
@@ -327,24 +373,30 @@ struct DensityAddOperationExecutor {
brush_, dist_to_brush_re, brush_radius_re_);
const float weight = brush_strength_ * radius_falloff;
if (rng.get_float() > weight) {
- r_bary_coords.remove_and_reorder(i);
- r_looptri_indices.remove_and_reorder(i);
- r_positions_su.remove_and_reorder(i);
+ bary_coords.remove_and_reorder(i);
+ looptri_indices.remove_and_reorder(i);
+ positions_su.remove_and_reorder(i);
}
}
+
+ for (const int i : bary_coords.index_range()) {
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_);
+ r_uvs.append(uv);
+ }
+ r_positions_su.extend(positions_su);
}
}
void sample_spherical_with_symmetry(RandomNumberGenerator &rng,
- Vector<float3> &r_bary_coords,
- Vector<int> &r_looptri_indices,
+ Vector<float2> &r_uvs,
Vector<float3> &r_positions_su)
{
const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
*ctx_.region,
*ctx_.v3d,
transforms_,
- surface_bvh_,
+ surface_bvh_eval_,
brush_pos_re_,
brush_radius_re_);
if (!brush_3d.has_value()) {
@@ -352,7 +404,7 @@ struct DensityAddOperationExecutor {
}
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
const float3 brush_pos_cu = brush_transform * brush_3d->position_cu;
const float3 brush_pos_su = transforms_.curves_to_surface * brush_pos_cu;
@@ -360,45 +412,54 @@ struct DensityAddOperationExecutor {
transforms_.curves_to_surface, brush_pos_cu, brush_3d->radius_cu);
const float brush_radius_sq_su = pow2f(brush_radius_su);
- Vector<int> looptri_indices;
+ Vector<int> selected_looptri_indices;
BLI_bvhtree_range_query_cpp(
- *surface_bvh_.tree,
+ *surface_bvh_eval_.tree,
brush_pos_su,
brush_radius_su,
[&](const int index, const float3 &UNUSED(co), const float UNUSED(dist_sq)) {
- looptri_indices.append(index);
+ selected_looptri_indices.append(index);
});
const float brush_plane_area_su = M_PI * brush_radius_sq_su;
const float approximate_density_su = brush_settings_->density_add_attempts /
brush_plane_area_su;
+ Vector<float3> positions_su;
+ Vector<float3> bary_coords;
+ Vector<int> looptri_indices;
const int new_points = bke::mesh_surface_sample::sample_surface_points_spherical(
rng,
- *surface_,
- looptri_indices,
+ *surface_eval_,
+ selected_looptri_indices,
brush_pos_su,
brush_radius_su,
approximate_density_su,
- r_bary_coords,
- r_looptri_indices,
- r_positions_su);
+ bary_coords,
+ looptri_indices,
+ positions_su);
/* Remove some sampled points randomly based on the brush falloff and strength. */
- const int old_points = r_bary_coords.size() - new_points;
- for (int i = r_bary_coords.size() - 1; i >= old_points; i--) {
- const float3 pos_su = r_positions_su[i];
+ for (int i = new_points - 1; i >= 0; i--) {
+ const float3 pos_su = positions_su[i];
const float3 pos_cu = transforms_.surface_to_curves * pos_su;
const float dist_to_brush_cu = math::distance(pos_cu, brush_pos_cu);
const float radius_falloff = BKE_brush_curve_strength(
brush_, dist_to_brush_cu, brush_3d->radius_cu);
const float weight = brush_strength_ * radius_falloff;
if (rng.get_float() > weight) {
- r_bary_coords.remove_and_reorder(i);
- r_looptri_indices.remove_and_reorder(i);
- r_positions_su.remove_and_reorder(i);
+ bary_coords.remove_and_reorder(i);
+ looptri_indices.remove_and_reorder(i);
+ positions_su.remove_and_reorder(i);
}
}
+
+ for (const int i : bary_coords.index_range()) {
+ const float2 uv = bke::mesh_surface_sample::sample_corner_attrribute_with_bary_coords(
+ bary_coords[i], surface_looptris_eval_[looptri_indices[i]], surface_uv_map_eval_);
+ r_uvs.append(uv);
+ }
+ r_positions_su.extend(positions_su);
}
}
};
@@ -414,6 +475,13 @@ class DensitySubtractOperation : public CurvesSculptStrokeOperation {
private:
friend struct DensitySubtractOperationExecutor;
+ /**
+ * Deformed root positions of curves that still exist. This has to be stored in case the brush is
+ * executed more than once before the curves are evaluated again. This can happen when the mouse
+ * is moved quickly and the brush spacing is small.
+ */
+ Vector<float3> deformed_root_positions_;
+
public:
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
};
@@ -433,8 +501,12 @@ struct DensitySubtractOperationExecutor {
Vector<int64_t> selected_curve_indices_;
IndexMask curve_selection_;
- Object *surface_ob_ = nullptr;
- Mesh *surface_ = nullptr;
+ Object *surface_ob_orig_ = nullptr;
+ Mesh *surface_orig_ = nullptr;
+
+ Object *surface_ob_eval_ = nullptr;
+ Mesh *surface_eval_ = nullptr;
+ BVHTreeFromMesh surface_bvh_eval_;
const CurvesSculpt *curves_sculpt_ = nullptr;
const Brush *brush_ = nullptr;
@@ -446,7 +518,6 @@ struct DensitySubtractOperationExecutor {
float minimum_distance_;
CurvesSurfaceTransforms transforms_;
- BVHTreeFromMesh surface_bvh_;
KDTree_3d *root_points_kdtree_;
@@ -468,11 +539,20 @@ struct DensitySubtractOperationExecutor {
return;
}
- surface_ob_ = curves_id_->surface;
- if (surface_ob_ == nullptr) {
+ surface_ob_orig_ = curves_id_->surface;
+ if (surface_ob_orig_ == nullptr) {
+ return;
+ }
+ surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
+
+ surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_);
+ if (surface_ob_eval_ == nullptr) {
return;
}
- surface_ = static_cast<Mesh *>(surface_ob_->data);
+ surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
+
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
@@ -488,16 +568,20 @@ struct DensitySubtractOperationExecutor {
transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
const eBrushFalloffShape falloff_shape = static_cast<eBrushFalloffShape>(
brush_->falloff_shape);
- BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
- const Span<float3> positions_cu = curves_->positions();
+ if (stroke_extension.is_first) {
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+ for (const int curve_i : curves_->curves_range()) {
+ const int first_point_i = curves_->offsets()[curve_i];
+ self_->deformed_root_positions_.append(deformation.positions[first_point_i]);
+ }
+ }
root_points_kdtree_ = BLI_kdtree_3d_new(curve_selection_.size());
BLI_SCOPED_DEFER([&]() { BLI_kdtree_3d_free(root_points_kdtree_); });
for (const int curve_i : curve_selection_) {
- const int first_point_i = curves_->offsets()[curve_i];
- const float3 &pos_cu = positions_cu[first_point_i];
+ const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
BLI_kdtree_3d_insert(root_points_kdtree_, curve_i, pos_cu);
}
BLI_kdtree_3d_balance(root_points_kdtree_);
@@ -515,12 +599,23 @@ struct DensitySubtractOperationExecutor {
}
Vector<int64_t> indices;
- const IndexMask mask = index_mask_ops::find_indices_based_on_predicate(
+ const IndexMask mask_to_delete = index_mask_ops::find_indices_based_on_predicate(
curves_->curves_range(), 4096, indices, [&](const int curve_i) {
return curves_to_delete[curve_i];
});
- curves_->remove_curves(mask);
+ /* Remove deleted curves from the stored deformed root positions. */
+ const Vector<IndexRange> ranges_to_keep = mask_to_delete.extract_ranges_invert(
+ curves_->curves_range());
+ BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
+ Vector<float3> new_deformed_positions;
+ for (const IndexRange range : ranges_to_keep) {
+ new_deformed_positions.extend(self_->deformed_root_positions_.as_span().slice(range));
+ }
+ self_->deformed_root_positions_ = std::move(new_deformed_positions);
+
+ curves_->remove_curves(mask_to_delete);
+ BLI_assert(curves_->curves_num() == self_->deformed_root_positions_.size());
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
@@ -539,15 +634,12 @@ struct DensitySubtractOperationExecutor {
void reduce_density_projected(const float4x4 &brush_transform,
MutableSpan<bool> curves_to_delete)
{
- const Span<float3> positions_cu = curves_->positions();
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
const float brush_radius_sq_re = pow2f(brush_radius_re);
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
- const Span<int> offsets = curves_->offsets();
-
/* Randomly select the curves that are allowed to be removed, based on the brush radius and
* strength. */
Array<bool> allow_remove_curve(curves_->curves_num(), false);
@@ -559,8 +651,7 @@ struct DensitySubtractOperationExecutor {
allow_remove_curve[curve_i] = true;
continue;
}
- const int first_point_i = offsets[curve_i];
- const float3 pos_cu = brush_transform * positions_cu[first_point_i];
+ const float3 pos_cu = brush_transform * self_->deformed_root_positions_[curve_i];
float2 pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
@@ -586,8 +677,7 @@ struct DensitySubtractOperationExecutor {
if (!allow_remove_curve[curve_i]) {
continue;
}
- const int first_point_i = offsets[curve_i];
- const float3 orig_pos_cu = positions_cu[first_point_i];
+ const float3 orig_pos_cu = self_->deformed_root_positions_[curve_i];
const float3 pos_cu = brush_transform * orig_pos_cu;
float2 pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
@@ -618,7 +708,7 @@ struct DensitySubtractOperationExecutor {
*ctx_.region,
*ctx_.v3d,
transforms_,
- surface_bvh_,
+ surface_bvh_eval_,
brush_pos_re_,
brush_radius_re);
if (!brush_3d.has_value()) {
@@ -638,8 +728,6 @@ struct DensitySubtractOperationExecutor {
MutableSpan<bool> curves_to_delete)
{
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
- const Span<float3> positions_cu = curves_->positions();
- const Span<int> offsets = curves_->offsets();
/* Randomly select the curves that are allowed to be removed, based on the brush radius and
* strength. */
@@ -652,8 +740,7 @@ struct DensitySubtractOperationExecutor {
allow_remove_curve[curve_i] = true;
continue;
}
- const int first_point_i = offsets[curve_i];
- const float3 pos_cu = positions_cu[first_point_i];
+ const float3 pos_cu = self_->deformed_root_positions_[curve_i];
const float dist_to_brush_sq_cu = math::distance_squared(brush_pos_cu, pos_cu);
if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
@@ -677,8 +764,7 @@ struct DensitySubtractOperationExecutor {
if (!allow_remove_curve[curve_i]) {
continue;
}
- const int first_point_i = offsets[curve_i];
- const float3 &pos_cu = positions_cu[first_point_i];
+ const float3 &pos_cu = self_->deformed_root_positions_[curve_i];
const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
continue;
@@ -717,6 +803,10 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode,
{
const Scene &scene = *CTX_data_scene(&C);
const Brush &brush = *BKE_paint_brush_for_read(&scene.toolsettings->curves_sculpt->paint);
+ const Depsgraph &depsgraph = *CTX_data_depsgraph_on_load(&C);
+ const ARegion &region = *CTX_wm_region(&C);
+ const View3D &v3d = *CTX_wm_view3d(&C);
+
const eBrushCurvesSculptDensityMode density_mode = static_cast<eBrushCurvesSculptDensityMode>(
brush.curves_sculpt_settings->density_mode);
const bool use_invert = brush_mode == BRUSH_STROKE_INVERT;
@@ -728,26 +818,29 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode,
return use_invert;
}
- const Object &curves_ob = *CTX_data_active_object(&C);
- const Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
- const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- if (curves_id.surface == nullptr) {
- /* The brush won't do anything in this case anyway. */
+ const Object &curves_ob_orig = *CTX_data_active_object(&C);
+ const Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data);
+ Object *surface_ob_orig = curves_id_orig.surface;
+ if (surface_ob_orig == nullptr) {
+ return true;
+ }
+ Object *surface_ob_eval = DEG_get_evaluated_object(&depsgraph, surface_ob_orig);
+ if (surface_ob_eval == nullptr) {
return true;
}
+ const CurvesGeometry &curves = CurvesGeometry::wrap(curves_id_orig.geometry);
if (curves.curves_num() <= 1) {
return true;
}
+ const Mesh *surface_mesh_eval = BKE_object_get_evaluated_mesh(surface_ob_eval);
+ if (surface_mesh_eval == nullptr) {
+ return true;
+ }
- const CurvesSurfaceTransforms transforms(curves_ob, curves_id.surface);
- BVHTreeFromMesh surface_bvh;
- BKE_bvhtree_from_mesh_get(
- &surface_bvh, static_cast<const Mesh *>(curves_id.surface->data), BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
-
- const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
- const ARegion &region = *CTX_wm_region(&C);
- const View3D &v3d = *CTX_wm_view3d(&C);
+ const CurvesSurfaceTransforms transforms(curves_ob_orig, curves_id_orig.surface);
+ BVHTreeFromMesh surface_bvh_eval;
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_mesh_eval, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); });
const float2 brush_pos_re = stroke_start.mouse_position;
/* Reduce radius so that only an inner circle is used to determine the existing density. */
@@ -755,7 +848,7 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode,
/* Find the surface point under the brush. */
const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(
- depsgraph, region, v3d, transforms, surface_bvh, brush_pos_re, brush_radius_re);
+ depsgraph, region, v3d, transforms, surface_bvh_eval, brush_pos_re, brush_radius_re);
if (!brush_3d.has_value()) {
return true;
}
@@ -764,8 +857,9 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode,
const float brush_radius_cu = brush_3d->radius_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(depsgraph, curves_ob_orig);
const Span<int> offsets = curves.offsets();
- const Span<float3> positions_cu = curves.positions();
/* Compute distance from brush to curve roots. */
Array<std::pair<float, int>> distances_sq_to_brush(curves.curves_num());
@@ -774,7 +868,7 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode,
int &valid_curve_count = valid_curve_count_by_thread.local();
for (const int curve_i : range) {
const int root_point_i = offsets[curve_i];
- const float3 &root_pos_cu = positions_cu[root_point_i];
+ const float3 &root_pos_cu = deformation.positions[root_point_i];
const float dist_sq_cu = math::distance_squared(root_pos_cu, brush_pos_cu);
if (dist_sq_cu < brush_radius_sq_cu) {
distances_sq_to_brush[curve_i] = {math::distance_squared(root_pos_cu, brush_pos_cu),
@@ -799,9 +893,9 @@ static bool use_add_density_mode(const BrushStrokeMode brush_mode,
* center. */
float min_dist_sq_cu = FLT_MAX;
for (const int i : IndexRange(check_curve_count)) {
- const float3 &pos_i = positions_cu[offsets[distances_sq_to_brush[i].second]];
+ const float3 &pos_i = deformation.positions[offsets[distances_sq_to_brush[i].second]];
for (int j = i + 1; j < check_curve_count; j++) {
- const float3 &pos_j = positions_cu[offsets[distances_sq_to_brush[j].second]];
+ const float3 &pos_j = deformation.positions[offsets[distances_sq_to_brush[j].second]];
const float dist_sq_cu = math::distance_squared(pos_i, pos_j);
math::min_inplace(min_dist_sq_cu, dist_sq_cu);
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
index 709ecc79967..bc354ed66f4 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_grow_shrink.cc
@@ -4,8 +4,7 @@
#include "BLI_enumerable_thread_specific.hh"
#include "BLI_float4x4.hh"
-#include "BLI_kdtree.h"
-#include "BLI_rand.hh"
+#include "BLI_length_parameterize.hh"
#include "BLI_vector.hh"
#include "PIL_time.h"
@@ -14,19 +13,13 @@
#include "BKE_attribute_math.hh"
#include "BKE_brush.h"
-#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
-#include "BKE_mesh.h"
-#include "BKE_mesh_runtime.h"
#include "BKE_paint.h"
-#include "BKE_spline.hh"
#include "DNA_brush_enums.h"
#include "DNA_brush_types.h"
#include "DNA_curves_types.h"
-#include "DNA_mesh_types.h"
-#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -70,6 +63,24 @@ class ShrinkCurvesEffect : public CurvesEffect {
private:
const Brush &brush_;
+ /** Storage of per-curve parameterization data to avoid reallocation. */
+ struct ParameterizationBuffers {
+ Array<float3> old_positions;
+ Array<float> old_lengths;
+ Array<float> sample_lengths;
+ Array<int> indices;
+ Array<float> factors;
+
+ void reinitialize(const int points_num)
+ {
+ this->old_positions.reinitialize(points_num);
+ this->old_lengths.reinitialize(length_parameterize::segments_num(points_num, false));
+ this->sample_lengths.reinitialize(points_num);
+ this->indices.reinitialize(points_num);
+ this->factors.reinitialize(points_num);
+ }
+ };
+
public:
ShrinkCurvesEffect(const Brush &brush) : brush_(brush)
{
@@ -81,46 +92,42 @@ class ShrinkCurvesEffect : public CurvesEffect {
{
MutableSpan<float3> positions_cu = curves.positions_for_write();
threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
+ ParameterizationBuffers data;
for (const int influence_i : range) {
const int curve_i = curve_indices[influence_i];
const float move_distance_cu = move_distances_cu[influence_i];
- const IndexRange curve_points = curves.points_for_curve(curve_i);
- this->shrink_curve(positions_cu, curve_points, move_distance_cu);
+ const IndexRange points = curves.points_for_curve(curve_i);
+ this->shrink_curve(positions_cu.slice(points), move_distance_cu, data);
}
});
}
+ private:
void shrink_curve(MutableSpan<float3> positions,
- const IndexRange curve_points,
- const float shrink_length) const
+ const float shrink_length,
+ ParameterizationBuffers &data) const
{
- PolySpline spline;
- spline.resize(curve_points.size());
- MutableSpan<float3> spline_positions = spline.positions();
- spline_positions.copy_from(positions.slice(curve_points));
- spline.mark_cache_invalid();
+ namespace lp = length_parameterize;
+ data.reinitialize(positions.size());
+
+ /* Copy the old positions to facilitate mixing from neighbors for the resulting curve. */
+ data.old_positions.as_mutable_span().copy_from(positions);
+
+ lp::accumulate_lengths<float3>(data.old_positions, false, data.old_lengths);
+
const float min_length = brush_.curves_sculpt_settings->minimum_length;
- const float old_length = spline.length();
+ const float old_length = data.old_lengths.last();
const float new_length = std::max(min_length, old_length - shrink_length);
const float length_factor = std::clamp(new_length / old_length, 0.0f, 1.0f);
- Vector<float> old_point_lengths;
- old_point_lengths.append(0.0f);
- for (const int i : spline_positions.index_range().drop_back(1)) {
- const float3 &p1 = spline_positions[i];
- const float3 &p2 = spline_positions[i + 1];
- const float length = math::distance(p1, p2);
- old_point_lengths.append(old_point_lengths.last() + length);
+ data.sample_lengths.first() = 0.0f;
+ for (const int i : data.old_lengths.index_range()) {
+ data.sample_lengths[i + 1] = data.old_lengths[i] * length_factor;
}
- for (const int i : spline_positions.index_range()) {
- const float eval_length = old_point_lengths[i] * length_factor;
- const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length);
- const float index_factor = lookup.evaluated_index + lookup.factor;
- float3 p;
- spline.sample_with_index_factors<float3>(spline_positions, {&index_factor, 1}, {&p, 1});
- positions[curve_points[i]] = p;
- }
+ lp::sample_at_lengths(data.old_lengths, data.sample_lengths, data.indices, data.factors);
+
+ lp::interpolate<float3>(data.old_positions, data.indices, data.factors, positions);
}
};
@@ -134,24 +141,23 @@ class ExtrapolateCurvesEffect : public CurvesEffect {
{
MutableSpan<float3> positions_cu = curves.positions_for_write();
threading::parallel_for(curve_indices.index_range(), 256, [&](const IndexRange range) {
+ MoveAndResampleBuffers resample_buffer;
for (const int influence_i : range) {
const int curve_i = curve_indices[influence_i];
const float move_distance_cu = move_distances_cu[influence_i];
- const IndexRange curve_points = curves.points_for_curve(curve_i);
-
- if (curve_points.size() <= 1) {
+ const IndexRange points = curves.points_for_curve(curve_i);
+ if (points.size() <= 1) {
continue;
}
- const float3 old_last_pos_cu = positions_cu[curve_points.last()];
+ const float3 old_last_pos_cu = positions_cu[points.last()];
/* Use some point within the curve rather than the end point to smooth out some random
* variation. */
- const float3 direction_reference_point =
- positions_cu[curve_points[curve_points.size() / 2]];
+ const float3 direction_reference_point = positions_cu[points[points.size() / 2]];
const float3 direction = math::normalize(old_last_pos_cu - direction_reference_point);
const float3 new_last_pos_cu = old_last_pos_cu + direction * move_distance_cu;
- move_last_point_and_resample(positions_cu.slice(curve_points), new_last_pos_cu);
+ move_last_point_and_resample(resample_buffer, positions_cu.slice(points), new_last_pos_cu);
}
});
}
@@ -335,7 +341,8 @@ struct CurvesEffectOperationExecutor {
void gather_influences_projected(
threading::EnumerableThreadSpecific<Influences> &influences_for_thread)
{
- const Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
@@ -343,7 +350,7 @@ struct CurvesEffectOperationExecutor {
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
Vector<float4x4> symmetry_brush_transforms_inv;
- for (const float4x4 brush_transform : symmetry_brush_transforms) {
+ for (const float4x4 &brush_transform : symmetry_brush_transforms) {
symmetry_brush_transforms_inv.append(brush_transform.inverted());
}
@@ -361,8 +368,8 @@ struct CurvesEffectOperationExecutor {
float max_move_distance_cu = 0.0f;
for (const float4x4 &brush_transform_inv : symmetry_brush_transforms_inv) {
for (const int segment_i : points.drop_back(1)) {
- const float3 p1_cu = brush_transform_inv * positions_cu[segment_i];
- const float3 p2_cu = brush_transform_inv * positions_cu[segment_i + 1];
+ const float3 p1_cu = brush_transform_inv * deformation.positions[segment_i];
+ const float3 p2_cu = brush_transform_inv * deformation.positions[segment_i + 1];
float2 p1_re, p2_re;
ED_view3d_project_float_v2_m4(ctx_.region, p1_cu, p1_re, projection.values);
@@ -423,7 +430,8 @@ struct CurvesEffectOperationExecutor {
void gather_influences_spherical(
threading::EnumerableThreadSpecific<Influences> &influences_for_thread)
{
- const Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
float3 brush_pos_start_wo, brush_pos_end_wo;
ED_view3d_win_to_3d(ctx_.v3d,
@@ -461,8 +469,8 @@ struct CurvesEffectOperationExecutor {
const float3 brush_pos_end_transformed_cu = brush_transform * brush_pos_end_cu;
for (const int segment_i : points.drop_back(1)) {
- const float3 &p1_cu = positions_cu[segment_i];
- const float3 &p2_cu = positions_cu[segment_i + 1];
+ const float3 &p1_cu = deformation.positions[segment_i];
+ const float3 &p2_cu = deformation.positions[segment_i + 1];
float3 closest_on_segment_cu;
float3 closest_on_brush_cu;
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
index c31bba2fe1e..61e2559f303 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_intern.hh
@@ -12,8 +12,11 @@
#include "BLI_virtual_array.hh"
#include "BKE_attribute.h"
+#include "BKE_crazyspace.hh"
#include "BKE_curves.hh"
+#include "ED_curves_sculpt.h"
+
struct ARegion;
struct RegionView3D;
struct Depsgraph;
@@ -22,6 +25,7 @@ struct Object;
struct Brush;
struct Scene;
struct BVHTreeFromMesh;
+struct ReportList;
namespace blender::ed::sculpt_paint {
@@ -32,6 +36,7 @@ struct StrokeExtension {
bool is_first;
float2 mouse_position;
float pressure;
+ ReportList *reports = nullptr;
};
float brush_radius_factor(const Brush &brush, const StrokeExtension &stroke_extension);
@@ -53,8 +58,7 @@ class CurvesSculptStrokeOperation {
virtual void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) = 0;
};
-std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation(const bContext &C,
- ReportList *reports);
+std::unique_ptr<CurvesSculptStrokeOperation> new_add_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_comb_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_delete_operation();
std::unique_ptr<CurvesSculptStrokeOperation> new_snake_hook_operation();
@@ -98,13 +102,23 @@ VArray<float> get_curves_selection(const Curves &curves_id);
*/
VArray<float> get_point_selection(const Curves &curves_id);
+/** See #move_last_point_and_resample. */
+struct MoveAndResampleBuffers {
+ Array<float> orig_lengths;
+ Array<float> new_lengths;
+
+ Array<int> sample_indices;
+ Array<float> sample_factors;
+
+ Array<float3> new_positions;
+};
+
/**
- * Find curves that have any point selected (a selection factor greater than zero),
- * or curves that have their own selection factor greater than zero.
+ * \param buffer: Reused memory to avoid reallocations when the function is called many times.
*/
-IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_indices);
-
-void move_last_point_and_resample(MutableSpan<float3> positions, const float3 &new_last_position);
+void move_last_point_and_resample(MoveAndResampleBuffers &buffer,
+ MutableSpan<float3> positions,
+ const float3 &new_last_position);
class CurvesSculptCommonContext {
public:
@@ -130,4 +144,11 @@ float transform_brush_radius(const float4x4 &transform,
const float3 &brush_position,
const float old_radius);
+void report_empty_original_surface(ReportList *reports);
+void report_empty_evaluated_surface(ReportList *reports);
+void report_missing_surface(ReportList *reports);
+void report_missing_uv_map_on_original_surface(ReportList *reports);
+void report_missing_uv_map_on_evaluated_surface(ReportList *reports);
+void report_invalid_uv_map(ReportList *reports);
+
} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
index 5c73c7a37d3..6e1ac24e21b 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc
@@ -9,6 +9,8 @@
#include "BKE_bvhutils.h"
#include "BKE_context.h"
#include "BKE_curves.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "BKE_paint.h"
#include "WM_api.h"
@@ -24,6 +26,7 @@
#include "ED_view3d.h"
#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
#include "DNA_brush_types.h"
#include "DNA_curves_types.h"
@@ -122,7 +125,7 @@ static std::unique_ptr<CurvesSculptStrokeOperation> start_brush_operation(
case CURVES_SCULPT_TOOL_SNAKE_HOOK:
return new_snake_hook_operation();
case CURVES_SCULPT_TOOL_ADD:
- return new_add_operation(C, op.reports);
+ return new_add_operation();
case CURVES_SCULPT_TOOL_GROW_SHRINK:
return new_grow_shrink_operation(mode, C);
case CURVES_SCULPT_TOOL_SELECTION_PAINT:
@@ -147,7 +150,10 @@ struct SculptCurvesBrushStrokeData {
PaintStroke *stroke;
};
-static bool stroke_get_location(bContext *C, float out[3], const float mouse[2])
+static bool stroke_get_location(bContext *C,
+ float out[3],
+ const float mouse[2],
+ bool UNUSED(force_original))
{
out[0] = mouse[0];
out[1] = mouse[1];
@@ -173,6 +179,7 @@ static void stroke_update_step(bContext *C,
StrokeExtension stroke_extension;
RNA_float_get_array(stroke_element, "mouse", stroke_extension.mouse_position);
stroke_extension.pressure = RNA_float_get(stroke_element, "pressure");
+ stroke_extension.reports = op->reports;
if (!op_data->operation) {
stroke_extension.is_first = true;
@@ -262,18 +269,6 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot)
/** \name * CURVES_OT_sculptmode_toggle
* \{ */
-static bool curves_sculptmode_toggle_poll(bContext *C)
-{
- const Object *ob = CTX_data_active_object(C);
- if (ob == nullptr) {
- return false;
- }
- if (ob->type != OB_CURVES) {
- return false;
- }
- return true;
-}
-
static void curves_sculptmode_enter(bContext *C)
{
Scene *scene = CTX_data_scene(C);
@@ -335,7 +330,7 @@ static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
ot->description = "Enter/Exit sculpt mode for curves";
ot->exec = curves_sculptmode_toggle_exec;
- ot->poll = curves_sculptmode_toggle_poll;
+ ot->poll = curves::curves_poll;
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
}
@@ -478,7 +473,7 @@ static void SCULPT_CURVES_OT_select_random(wmOperatorType *ot)
ot->description = "Randomizes existing selection or create new random selection";
ot->exec = select_random::select_random_exec;
- ot->poll = curves::selection_operator_poll;
+ ot->poll = curves::editable_curves_poll;
ot->ui = select_random::select_random_ui;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -522,7 +517,7 @@ static void SCULPT_CURVES_OT_select_random(wmOperatorType *ot)
namespace select_end {
static bool select_end_poll(bContext *C)
{
- if (!curves::selection_operator_poll(C)) {
+ if (!curves::editable_curves_poll(C)) {
return false;
}
const Curves *curves_id = static_cast<const Curves *>(CTX_data_active_object(C)->data);
@@ -739,6 +734,8 @@ static void select_grow_invoke_per_curve(Curves &curves_id,
}
threading::parallel_invoke(
+ 1024 < curve_op_data.selected_point_indices.size() +
+ curve_op_data.unselected_point_indices.size(),
[&]() {
/* Build KD-tree for the selected points. */
KDTree_3d *kdtree = BLI_kdtree_3d_new(curve_op_data.selected_point_indices.size());
@@ -904,7 +901,7 @@ static void SCULPT_CURVES_OT_select_grow(wmOperatorType *ot)
ot->invoke = select_grow::select_grow_invoke;
ot->modal = select_grow::select_grow_modal;
- ot->poll = curves::selection_operator_poll;
+ ot->poll = curves::editable_curves_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -925,16 +922,7 @@ namespace min_distance_edit {
static bool min_distance_edit_poll(bContext *C)
{
- Object *ob = CTX_data_active_object(C);
- if (ob == nullptr) {
- return false;
- }
- if (ob->type != OB_CURVES) {
- return false;
- }
- Curves *curves_id = static_cast<Curves *>(ob->data);
- if (curves_id->surface == nullptr || curves_id->surface->type != OB_MESH) {
- CTX_wm_operator_poll_msg_set(C, "Curves must have a mesh surface object set");
+ if (!curves::curves_with_surface_poll(C)) {
return false;
}
Scene *scene = CTX_data_scene(C);
@@ -1110,7 +1098,7 @@ static void min_distance_edit_draw(bContext *C, int UNUSED(x), int UNUSED(y), vo
GPUVertFormat *format = immVertexFormat();
uint pos2d = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fvAlpha(circle_col, circle_alpha);
imm_draw_circle_wire_2d(pos2d, 0.0f, 0.0f, brush_radius_re, 80);
@@ -1126,14 +1114,21 @@ static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *
View3D *v3d = CTX_wm_view3d(C);
Scene *scene = CTX_data_scene(C);
- Object &curves_ob = *CTX_data_active_object(C);
- Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
- Object &surface_ob = *curves_id.surface;
- Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
+ Object &curves_ob_orig = *CTX_data_active_object(C);
+ Curves &curves_id_orig = *static_cast<Curves *>(curves_ob_orig.data);
+ Object &surface_ob_orig = *curves_id_orig.surface;
+ Object *surface_ob_eval = DEG_get_evaluated_object(depsgraph, &surface_ob_orig);
+ if (surface_ob_eval == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+ Mesh *surface_me_eval = BKE_object_get_evaluated_mesh(surface_ob_eval);
+ if (surface_me_eval == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
- BVHTreeFromMesh surface_bvh;
- BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_me, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
+ BVHTreeFromMesh surface_bvh_eval;
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval, surface_me_eval, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval); });
const int2 mouse_pos_int_re{event->mval};
const float2 mouse_pos_re{mouse_pos_int_re};
@@ -1142,23 +1137,22 @@ static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *
ED_view3d_win_to_segment_clipped(
depsgraph, region, v3d, mouse_pos_re, ray_start_wo, ray_end_wo, true);
- const float4x4 surface_to_world_mat = surface_ob.obmat;
- const float4x4 world_to_surface_mat = surface_to_world_mat.inverted();
+ const CurvesSurfaceTransforms transforms{curves_ob_orig, &surface_ob_orig};
- const float3 ray_start_su = world_to_surface_mat * ray_start_wo;
- const float3 ray_end_su = world_to_surface_mat * ray_end_wo;
+ const float3 ray_start_su = transforms.world_to_surface * ray_start_wo;
+ const float3 ray_end_su = transforms.world_to_surface * ray_end_wo;
const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
BVHTreeRayHit ray_hit;
ray_hit.dist = FLT_MAX;
ray_hit.index = -1;
- BLI_bvhtree_ray_cast(surface_bvh.tree,
+ BLI_bvhtree_ray_cast(surface_bvh_eval.tree,
ray_start_su,
ray_direction_su,
0.0f,
&ray_hit,
- surface_bvh.raycast_callback,
- &surface_bvh);
+ surface_bvh_eval.raycast_callback,
+ &surface_bvh_eval);
if (ray_hit.index == -1) {
WM_report(RPT_ERROR, "Cursor must be over the surface mesh");
return OPERATOR_CANCELLED;
@@ -1166,16 +1160,13 @@ static int min_distance_edit_invoke(bContext *C, wmOperator *op, const wmEvent *
const float3 hit_pos_su = ray_hit.co;
const float3 hit_normal_su = ray_hit.no;
- const float4x4 curves_to_world_mat = curves_ob.obmat;
- const float4x4 world_to_curves_mat = curves_to_world_mat.inverted();
- const float4x4 surface_to_curves_mat = world_to_curves_mat * surface_to_world_mat;
- const float4x4 surface_to_curves_normal_mat = surface_to_curves_mat.inverted().transposed();
- const float3 hit_pos_cu = surface_to_curves_mat * hit_pos_su;
- const float3 hit_normal_cu = math::normalize(surface_to_curves_normal_mat * hit_normal_su);
+ const float3 hit_pos_cu = transforms.surface_to_curves * hit_pos_su;
+ const float3 hit_normal_cu = math::normalize(transforms.surface_to_curves_normal *
+ hit_normal_su);
MinDistanceEditData *op_data = MEM_new<MinDistanceEditData>(__func__);
- op_data->curves_to_world_mat = curves_to_world_mat;
+ op_data->curves_to_world_mat = transforms.curves_to_world;
op_data->normal_cu = hit_normal_cu;
op_data->pos_cu = hit_pos_cu;
op_data->initial_mouse = event->xy;
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc
index 689b7d22e5e..3e43b1a6361 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_pinch.cc
@@ -157,6 +157,9 @@ struct PinchOperationExecutor {
{
const float4x4 brush_transform_inv = brush_transform.inverted();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
MutableSpan<float3> positions_cu = curves_->positions_for_write();
@@ -167,11 +170,13 @@ struct PinchOperationExecutor {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
- const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i];
- float2 old_pos_re;
- ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values);
+ const float3 old_pos_cu = deformation.positions[point_i];
+ const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
+ float2 old_symm_pos_re;
+ ED_view3d_project_float_v2_m4(
+ ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values);
- const float dist_to_brush_sq_re = math::distance_squared(old_pos_re, brush_pos_re_);
+ const float dist_to_brush_sq_re = math::distance_squared(old_symm_pos_re, brush_pos_re_);
if (dist_to_brush_sq_re > brush_radius_sq_re) {
continue;
}
@@ -182,14 +187,21 @@ struct PinchOperationExecutor {
const float weight = invert_factor_ * 0.1f * brush_strength_ * radius_falloff *
point_factors_[point_i];
- const float2 new_pos_re = math::interpolate(old_pos_re, brush_pos_re_, weight);
-
- const float3 old_pos_wo = transforms_.curves_to_world * old_pos_cu;
- float3 new_pos_wo;
- ED_view3d_win_to_3d(ctx_.v3d, ctx_.region, old_pos_wo, new_pos_re, new_pos_wo);
-
- const float3 new_pos_cu = transforms_.world_to_curves * new_pos_wo;
- positions_cu[point_i] = brush_transform * new_pos_cu;
+ const float2 new_symm_pos_re = math::interpolate(old_symm_pos_re, brush_pos_re_, weight);
+
+ float3 new_symm_pos_wo;
+ ED_view3d_win_to_3d(ctx_.v3d,
+ ctx_.region,
+ transforms_.curves_to_world * old_symm_pos_cu,
+ new_symm_pos_re,
+ new_symm_pos_wo);
+
+ const float3 new_pos_cu = brush_transform * transforms_.world_to_curves *
+ new_symm_pos_wo;
+ const float3 translation_eval = new_pos_cu - old_pos_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ point_i, translation_eval);
+ positions_cu[point_i] += translation_orig;
r_changed_curves[curve_i] = true;
}
}
@@ -221,11 +233,14 @@ struct PinchOperationExecutor {
MutableSpan<float3> positions_cu = curves_->positions_for_write();
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
- const float3 old_pos_cu = positions_cu[point_i];
+ const float3 old_pos_cu = deformation.positions[point_i];
const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu);
if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
@@ -239,7 +254,10 @@ struct PinchOperationExecutor {
point_factors_[point_i];
const float3 new_pos_cu = math::interpolate(old_pos_cu, brush_pos_cu, weight);
- positions_cu[point_i] = new_pos_cu;
+ const float3 translation_eval = new_pos_cu - old_pos_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ point_i, translation_eval);
+ positions_cu[point_i] += translation_orig;
r_changed_curves[curve_i] = true;
}
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc
index 83cfda6dc00..ec69aae372c 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_puff.cc
@@ -4,6 +4,7 @@
#include "BKE_brush.h"
#include "BKE_bvhutils.h"
#include "BKE_context.h"
+#include "BKE_crazyspace.hh"
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -20,6 +21,8 @@
#include "BLI_length_parameterize.hh"
+#include "GEO_add_curves_on_mesh.hh"
+
#include "curves_sculpt_intern.hh"
namespace blender::ed::sculpt_paint {
@@ -38,23 +41,6 @@ class PuffOperation : public CurvesSculptStrokeOperation {
void on_stroke_extended(const bContext &C, const StrokeExtension &stroke_extension) override;
};
-static float3 compute_surface_point_normal(const MLoopTri &looptri,
- const float3 &bary_coord,
- const Span<float3> corner_normals)
-{
- const int l0 = looptri.tri[0];
- const int l1 = looptri.tri[1];
- const int l2 = looptri.tri[2];
-
- const float3 &l0_normal = corner_normals[l0];
- const float3 &l1_normal = corner_normals[l1];
- const float3 &l2_normal = corner_normals[l2];
-
- const float3 normal = math::normalize(
- attribute_math::mix3(bary_coord, l0_normal, l1_normal, l2_normal));
- return normal;
-}
-
/**
* Utility class that actually executes the update when the stroke is updated. That's useful
* because it avoids passing a very large number of parameters between functions.
@@ -84,6 +70,8 @@ struct PuffOperationExecutor {
Object *surface_ob_ = nullptr;
Mesh *surface_ = nullptr;
+ Span<MVert> surface_verts_;
+ Span<MLoop> surface_loops_;
Span<MLoopTri> surface_looptris_;
Span<float3> corner_normals_su_;
BVHTreeFromMesh surface_bvh_;
@@ -131,11 +119,12 @@ struct PuffOperationExecutor {
reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_->ldata, CD_NORMAL)),
surface_->totloop};
- BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
-
+ surface_verts_ = surface_->verts();
+ surface_loops_ = surface_->loops();
surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
BKE_mesh_runtime_looptri_len(surface_)};
+ BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
if (stroke_extension.is_first) {
this->initialize_segment_lengths();
@@ -183,8 +172,6 @@ struct PuffOperationExecutor {
void find_curve_weights_projected(const float4x4 &brush_transform,
MutableSpan<float> r_curve_weights)
{
- Span<float3> positions_cu = curves_->positions();
-
const float4x4 brush_transform_inv = brush_transform.inverted();
float4x4 projection;
@@ -193,15 +180,18 @@ struct PuffOperationExecutor {
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
const float brush_radius_sq_re = pow2f(brush_radius_re);
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_selection_i : range) {
const int curve_i = curve_selection_[curve_selection_i];
const IndexRange points = curves_->points_for_curve(curve_i);
- const float3 first_pos_cu = brush_transform_inv * positions_cu[points[0]];
+ const float3 first_pos_cu = brush_transform_inv * deformation.positions[points[0]];
float2 prev_pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, prev_pos_re, projection.values);
for (const int point_i : points.drop_front(1)) {
- const float3 pos_cu = brush_transform_inv * positions_cu[point_i];
+ const float3 pos_cu = brush_transform_inv * deformation.positions[point_i];
float2 pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
BLI_SCOPED_DEFER([&]() { prev_pos_re = pos_re; });
@@ -248,16 +238,18 @@ struct PuffOperationExecutor {
const float brush_radius_cu,
MutableSpan<float> r_curve_weights)
{
- const Span<float3> positions_cu = curves_->positions();
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
for (const int curve_selection_i : range) {
const int curve_i = curve_selection_[curve_selection_i];
const IndexRange points = curves_->points_for_curve(curve_i);
for (const int point_i : points.drop_front(1)) {
- const float3 &prev_pos_cu = positions_cu[point_i - 1];
- const float3 &pos_cu = positions_cu[point_i];
+ const float3 &prev_pos_cu = deformation.positions[point_i - 1];
+ const float3 &pos_cu = deformation.positions[point_i];
const float dist_to_brush_sq_cu = dist_squared_to_line_segment_v3(
brush_pos_cu, prev_pos_cu, pos_cu);
if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
@@ -300,12 +292,12 @@ struct PuffOperationExecutor {
const MLoopTri &looptri = surface_looptris_[nearest.index];
const float3 closest_pos_su = nearest.co;
- const float3 &v0_su = surface_->mvert[surface_->mloop[looptri.tri[0]].v].co;
- const float3 &v1_su = surface_->mvert[surface_->mloop[looptri.tri[1]].v].co;
- const float3 &v2_su = surface_->mvert[surface_->mloop[looptri.tri[2]].v].co;
+ const float3 &v0_su = surface_verts_[surface_loops_[looptri.tri[0]].v].co;
+ const float3 &v1_su = surface_verts_[surface_loops_[looptri.tri[1]].v].co;
+ const float3 &v2_su = surface_verts_[surface_loops_[looptri.tri[2]].v].co;
float3 bary_coords;
interp_weights_tri_v3(bary_coords, v0_su, v1_su, v2_su, closest_pos_su);
- const float3 normal_su = compute_surface_point_normal(
+ const float3 normal_su = geometry::compute_surface_point_normal(
looptri, bary_coords, corner_normals_su_);
const float3 normal_cu = math::normalize(transforms_.surface_to_curves_normal * normal_su);
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
index f620fed5761..a955a074df2 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc
@@ -6,6 +6,8 @@
#include "curves_sculpt_intern.hh"
+#include "ED_curves_sculpt.h"
+
namespace blender::ed::sculpt_paint {
static VArray<float> get_curves_selection(const CurvesGeometry &curves, const eAttrDomain domain)
@@ -62,13 +64,14 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
case ATTR_DOMAIN_POINT: {
const VArray<float> selection = curves.selection_point_float();
if (selection.is_single()) {
- return selection.get_internal_single() == 0.0f ? IndexMask(0) :
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
+ const Span<float> point_selection_span = selection.get_internal_span();
return index_mask_ops::find_indices_based_on_predicate(
curves.curves_range(), 512, r_indices, [&](const int curve_i) {
for (const int i : curves.points_for_curve(curve_i)) {
- if (selection[i] > 0.0f) {
+ if (point_selection_span[i] > 0.0f) {
return true;
}
}
@@ -78,7 +81,7 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves,
case ATTR_DOMAIN_CURVE: {
const VArray<float> selection = curves.selection_curve_float();
if (selection.is_single()) {
- return selection.get_internal_single() == 0.0f ? IndexMask(0) :
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
IndexMask(curves.curves_num());
}
return index_mask_ops::find_indices_based_on_predicate(
@@ -102,4 +105,49 @@ IndexMask retrieve_selected_curves(const Curves &curves_id, Vector<int64_t> &r_i
r_indices);
}
+static IndexMask retrieve_selected_points(const CurvesGeometry &curves,
+ const eAttrDomain domain,
+ Vector<int64_t> &r_indices)
+{
+ switch (domain) {
+ case ATTR_DOMAIN_POINT: {
+ const VArray<float> selection = curves.selection_point_float();
+ if (selection.is_single()) {
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
+ IndexMask(curves.points_num());
+ }
+ return index_mask_ops::find_indices_based_on_predicate(
+ curves.points_range(), 2048, r_indices, [&](const int i) {
+ return selection[i] > 0.0f;
+ });
+ }
+ case ATTR_DOMAIN_CURVE: {
+ const VArray<float> selection = curves.selection_curve_float();
+ if (selection.is_single()) {
+ return selection.get_internal_single() <= 0.0f ? IndexMask(0) :
+ IndexMask(curves.points_num());
+ }
+ const VArray<float> point_selection = curves.adapt_domain(
+ selection, ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ return index_mask_ops::find_indices_based_on_predicate(
+ curves.points_range(), 2048, r_indices, [&](const int i) {
+ return point_selection[i] > 0.0f;
+ });
+ }
+ default:
+ BLI_assert_unreachable();
+ return {};
+ }
+}
+
+IndexMask retrieve_selected_points(const Curves &curves_id, Vector<int64_t> &r_indices)
+{
+ if (!(curves_id.flag & CV_SCULPT_SELECTION_ENABLED)) {
+ return CurvesGeometry::wrap(curves_id.geometry).points_range();
+ }
+ return retrieve_selected_points(CurvesGeometry::wrap(curves_id.geometry),
+ eAttrDomain(curves_id.selection_domain),
+ r_indices);
+}
+
} // namespace blender::ed::sculpt_paint
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc
index 399d2c73ec3..cc5a5e7ae8a 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection_paint.cc
@@ -161,14 +161,15 @@ struct SelectionPaintOperationExecutor {
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
- Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
const float brush_radius_sq_re = pow2f(brush_radius_re);
threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) {
for (const int point_i : point_range) {
- const float3 pos_cu = brush_transform_inv * positions_cu[point_i];
+ const float3 pos_cu = brush_transform_inv * deformation.positions[point_i];
/* Find the position of the point in screen space. */
float2 pos_re;
@@ -215,14 +216,15 @@ struct SelectionPaintOperationExecutor {
void paint_point_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu)
{
- Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const float brush_radius_cu = self_->brush_3d_.radius_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
threading::parallel_for(curves_->points_range(), 1024, [&](const IndexRange point_range) {
for (const int i : point_range) {
- const float3 pos_old_cu = positions_cu[i];
+ const float3 pos_old_cu = deformation.positions[i];
/* Compute distance to the brush. */
const float distance_to_brush_sq_cu = math::distance_squared(pos_old_cu, brush_cu);
@@ -256,9 +258,11 @@ struct SelectionPaintOperationExecutor {
void paint_curve_selection_projected(const float4x4 &brush_transform,
MutableSpan<float> selection)
{
- const Span<float3> positions_cu = curves_->positions();
const float4x4 brush_transform_inv = brush_transform.inverted();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
@@ -274,8 +278,8 @@ struct SelectionPaintOperationExecutor {
[&](const IndexRange segment_range, const float init) {
float max_weight = init;
for (const int segment_i : segment_range) {
- const float3 pos1_cu = brush_transform_inv * positions_cu[segment_i];
- const float3 pos2_cu = brush_transform_inv * positions_cu[segment_i + 1];
+ const float3 pos1_cu = brush_transform_inv * deformation.positions[segment_i];
+ const float3 pos2_cu = brush_transform_inv * deformation.positions[segment_i + 1];
float2 pos1_re;
float2 pos2_re;
@@ -323,7 +327,8 @@ struct SelectionPaintOperationExecutor {
void paint_curve_selection_spherical(MutableSpan<float> selection, const float3 &brush_cu)
{
- const Span<float3> positions_cu = curves_->positions();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
const float brush_radius_cu = self_->brush_3d_.radius_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
@@ -337,8 +342,8 @@ struct SelectionPaintOperationExecutor {
[&](const IndexRange segment_range, const float init) {
float max_weight = init;
for (const int segment_i : segment_range) {
- const float3 &pos1_cu = positions_cu[segment_i];
- const float3 &pos2_cu = positions_cu[segment_i + 1];
+ const float3 &pos1_cu = deformation.positions[segment_i];
+ const float3 &pos2_cu = deformation.positions[segment_i + 1];
const float distance_sq_cu = dist_squared_to_line_segment_v3(
brush_cu, pos1_cu, pos2_cu);
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc
index aabe6fd93e4..1108f5c72a9 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_slide.cc
@@ -4,6 +4,7 @@
#include "curves_sculpt_intern.hh"
+#include "BLI_float3x3.hh"
#include "BLI_float4x4.hh"
#include "BLI_vector.hh"
@@ -20,14 +21,16 @@
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
#include "BKE_paint.h"
+#include "BKE_report.h"
#include "DNA_brush_enums.h"
#include "DNA_brush_types.h"
#include "DNA_curves_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
-#include "DNA_object_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -38,13 +41,27 @@
#include "WM_api.h"
+#include "DEG_depsgraph_query.h"
+
+#include "GEO_add_curves_on_mesh.hh"
+#include "GEO_reverse_uv_sampler.hh"
+
+#include "BLT_translation.h"
+
namespace blender::ed::sculpt_paint {
+using geometry::ReverseUVSampler;
+
struct SlideCurveInfo {
/** Index of the curve to slide. */
int curve_i;
/** A weight based on the initial distance to the brush. */
float radius_falloff;
+ /**
+ * Normal of the surface where the curve was attached. This is used to rotate the curve if it is
+ * moved to a place with a different normal.
+ */
+ float3 initial_normal_cu;
};
struct SlideInfo {
@@ -55,10 +72,13 @@ struct SlideInfo {
class SlideOperation : public CurvesSculptStrokeOperation {
private:
- /** Last mouse position. */
- float2 brush_pos_last_re_;
+ float2 initial_brush_pos_re_;
/** Information about which curves to slide. This is initialized when the brush starts. */
Vector<SlideInfo> slide_info_;
+ /** Positions of all curve points at the start of sliding. */
+ Array<float3> initial_positions_cu_;
+ /** Deformed positions of all curve points at the start of sliding. */
+ Array<float3> initial_deformed_positions_cu_;
friend struct SlideOperationExecutor;
@@ -80,27 +100,33 @@ struct SlideOperationExecutor {
float brush_radius_factor_;
float brush_strength_;
- Object *object_ = nullptr;
- Curves *curves_id_ = nullptr;
- CurvesGeometry *curves_ = nullptr;
+ Object *curves_ob_orig_ = nullptr;
+ Curves *curves_id_orig_ = nullptr;
+ CurvesGeometry *curves_orig_ = nullptr;
- Object *surface_ob_ = nullptr;
- Mesh *surface_ = nullptr;
- Span<MLoopTri> surface_looptris_;
- VArraySpan<float2> surface_uv_map_;
+ Object *surface_ob_orig_ = nullptr;
+ Mesh *surface_orig_ = nullptr;
+ Span<MLoopTri> surface_looptris_orig_;
+ VArraySpan<float2> surface_uv_map_orig_;
+ Span<float3> corner_normals_orig_su_;
+
+ Object *surface_ob_eval_ = nullptr;
+ Mesh *surface_eval_ = nullptr;
+ Span<MVert> surface_verts_eval_;
+ Span<MLoop> surface_loops_eval_;
+ Span<MLoopTri> surface_looptris_eval_;
+ VArraySpan<float2> surface_uv_map_eval_;
+ BVHTreeFromMesh surface_bvh_eval_;
VArray<float> curve_factors_;
- VArray<float> point_factors_;
Vector<int64_t> selected_curve_indices_;
IndexMask curve_selection_;
- float2 brush_pos_prev_re_;
float2 brush_pos_re_;
- float2 brush_pos_diff_re_;
CurvesSurfaceTransforms transforms_;
- BVHTreeFromMesh surface_bvh_;
+ std::atomic<bool> found_invalid_uv_mapping_{false};
SlideOperationExecutor(const bContext &C) : ctx_(C)
{
@@ -111,15 +137,27 @@ struct SlideOperationExecutor {
UNUSED_VARS(C, stroke_extension);
self_ = &self;
- object_ = CTX_data_active_object(&C);
- curves_id_ = static_cast<Curves *>(object_->data);
- curves_ = &CurvesGeometry::wrap(curves_id_->geometry);
- if (curves_id_->surface == nullptr || curves_id_->surface->type != OB_MESH) {
+ curves_ob_orig_ = CTX_data_active_object(&C);
+ curves_id_orig_ = static_cast<Curves *>(curves_ob_orig_->data);
+ curves_orig_ = &CurvesGeometry::wrap(curves_id_orig_->geometry);
+ if (curves_id_orig_->surface == nullptr || curves_id_orig_->surface->type != OB_MESH) {
+ report_missing_surface(stroke_extension.reports);
+ return;
+ }
+ if (curves_orig_->curves_num() == 0) {
+ return;
+ }
+ if (curves_id_orig_->surface_uv_map == nullptr) {
+ report_missing_uv_map_on_original_surface(stroke_extension.reports);
return;
}
- if (curves_->curves_num() == 0) {
+ if (curves_orig_->surface_uv_coords().is_empty()) {
+ BKE_report(stroke_extension.reports,
+ RPT_WARNING,
+ TIP_("Curves do not have surface attachment information"));
return;
}
+ const StringRefNull uv_map_name = curves_id_orig_->surface_uv_map;
curves_sculpt_ = ctx_.scene->toolsettings->curves_sculpt;
brush_ = BKE_paint_brush_for_read(&curves_sculpt_->paint);
@@ -127,169 +165,322 @@ struct SlideOperationExecutor {
brush_radius_factor_ = brush_radius_factor(*brush_, stroke_extension);
brush_strength_ = brush_strength_get(*ctx_.scene, *brush_, stroke_extension);
- curve_factors_ = get_curves_selection(*curves_id_);
- point_factors_ = get_point_selection(*curves_id_);
- curve_selection_ = retrieve_selected_curves(*curves_id_, selected_curve_indices_);
+ curve_factors_ = get_curves_selection(*curves_id_orig_);
+ curve_selection_ = retrieve_selected_curves(*curves_id_orig_, selected_curve_indices_);
- brush_pos_prev_re_ = self_->brush_pos_last_re_;
brush_pos_re_ = stroke_extension.mouse_position;
- brush_pos_diff_re_ = brush_pos_re_ - brush_pos_prev_re_;
- BLI_SCOPED_DEFER([&]() { self_->brush_pos_last_re_ = brush_pos_re_; });
-
- transforms_ = CurvesSurfaceTransforms(*object_, curves_id_->surface);
- surface_ob_ = curves_id_->surface;
- surface_ = static_cast<Mesh *>(surface_ob_->data);
+ transforms_ = CurvesSurfaceTransforms(*curves_ob_orig_, curves_id_orig_->surface);
- BKE_bvhtree_from_mesh_get(&surface_bvh_, surface_, BVHTREE_FROM_LOOPTRI, 2);
- BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_); });
-
- surface_looptris_ = {BKE_mesh_runtime_looptri_ensure(surface_),
- BKE_mesh_runtime_looptri_len(surface_)};
+ surface_ob_orig_ = curves_id_orig_->surface;
+ surface_orig_ = static_cast<Mesh *>(surface_ob_orig_->data);
+ if (surface_orig_->totpoly == 0) {
+ report_empty_original_surface(stroke_extension.reports);
+ return;
+ }
+ surface_looptris_orig_ = {BKE_mesh_runtime_looptri_ensure(surface_orig_),
+ BKE_mesh_runtime_looptri_len(surface_orig_)};
+ surface_uv_map_orig_ = surface_orig_->attributes().lookup<float2>(uv_map_name,
+ ATTR_DOMAIN_CORNER);
+ if (surface_uv_map_orig_.is_empty()) {
+ report_missing_uv_map_on_original_surface(stroke_extension.reports);
+ return;
+ }
+ if (!CustomData_has_layer(&surface_orig_->ldata, CD_NORMAL)) {
+ BKE_mesh_calc_normals_split(surface_orig_);
+ }
+ corner_normals_orig_su_ = {
+ reinterpret_cast<const float3 *>(CustomData_get_layer(&surface_orig_->ldata, CD_NORMAL)),
+ surface_orig_->totloop};
- if (curves_id_->surface_uv_map != nullptr) {
- const bke::AttributeAccessor surface_attributes = bke::mesh_attributes(*surface_);
- surface_uv_map_ = surface_attributes.lookup<float2>(curves_id_->surface_uv_map,
- ATTR_DOMAIN_CORNER);
+ surface_ob_eval_ = DEG_get_evaluated_object(ctx_.depsgraph, surface_ob_orig_);
+ if (surface_ob_eval_ == nullptr) {
+ return;
+ }
+ surface_eval_ = BKE_object_get_evaluated_mesh(surface_ob_eval_);
+ if (surface_eval_ == nullptr) {
+ return;
+ }
+ if (surface_eval_->totpoly == 0) {
+ report_empty_evaluated_surface(stroke_extension.reports);
+ return;
+ }
+ surface_looptris_eval_ = {BKE_mesh_runtime_looptri_ensure(surface_eval_),
+ BKE_mesh_runtime_looptri_len(surface_eval_)};
+ surface_verts_eval_ = surface_eval_->verts();
+ surface_loops_eval_ = surface_eval_->loops();
+ surface_uv_map_eval_ = surface_eval_->attributes().lookup<float2>(uv_map_name,
+ ATTR_DOMAIN_CORNER);
+ if (surface_uv_map_eval_.is_empty()) {
+ report_missing_uv_map_on_evaluated_surface(stroke_extension.reports);
+ return;
}
+ BKE_bvhtree_from_mesh_get(&surface_bvh_eval_, surface_eval_, BVHTREE_FROM_LOOPTRI, 2);
+ BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh_eval_); });
if (stroke_extension.is_first) {
- const Vector<float4x4> brush_transforms = get_symmetry_brush_transforms(
- eCurvesSymmetryType(curves_id_->symmetry));
- for (const float4x4 &brush_transform : brush_transforms) {
- this->detect_curves_to_slide(brush_transform);
- }
+ self_->initial_brush_pos_re_ = brush_pos_re_;
+ /* Remember original and deformed positions of all points. Otherwise this information is lost
+ * when sliding starts, but it's still used. */
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *curves_ob_orig_);
+ self_->initial_positions_cu_ = curves_orig_->positions();
+ self_->initial_deformed_positions_cu_ = deformation.positions;
+
+ /* First find all curves to slide. When the mouse moves, only those curves will be moved. */
+ this->find_curves_to_slide_with_symmetry();
return;
}
- this->slide_projected();
+ this->slide_with_symmetry();
- curves_->tag_positions_changed();
- DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
- WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
+ if (found_invalid_uv_mapping_) {
+ BKE_report(
+ stroke_extension.reports, RPT_WARNING, TIP_("UV map or surface attachment is invalid"));
+ }
+
+ curves_orig_->tag_positions_changed();
+ DEG_id_tag_update(&curves_id_orig_->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_orig_->id);
ED_region_tag_redraw(ctx_.region);
}
- void detect_curves_to_slide(const float4x4 &brush_transform)
+ void find_curves_to_slide_with_symmetry()
{
- const float4x4 brush_transform_inv = brush_transform.inverted();
-
+ const Vector<float4x4> brush_transforms = get_symmetry_brush_transforms(
+ eCurvesSymmetryType(curves_id_orig_->symmetry));
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
- const float brush_radius_sq_re = pow2f(brush_radius_re);
-
- const Span<float3> positions_cu = curves_->positions();
-
- float4x4 projection;
- ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ const std::optional<CurvesBrush3D> brush_3d = sample_curves_surface_3d_brush(*ctx_.depsgraph,
+ *ctx_.region,
+ *ctx_.v3d,
+ transforms_,
+ surface_bvh_eval_,
+ brush_pos_re_,
+ brush_radius_re);
+ if (!brush_3d.has_value()) {
+ return;
+ }
+ const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_};
+ for (const float4x4 &brush_transform : brush_transforms) {
+ self_->slide_info_.append_as();
+ SlideInfo &slide_info = self_->slide_info_.last();
+ slide_info.brush_transform = brush_transform;
+ this->find_curves_to_slide(brush_transform * brush_3d->position_cu,
+ brush_3d->radius_cu,
+ reverse_uv_sampler_orig,
+ slide_info.curves_to_slide);
+ }
+ }
- self_->slide_info_.append({brush_transform});
- Vector<SlideCurveInfo> &curves_to_slide = self_->slide_info_.last().curves_to_slide;
+ void find_curves_to_slide(const float3 &brush_pos_cu,
+ const float brush_radius_cu,
+ const ReverseUVSampler &reverse_uv_sampler_orig,
+ Vector<SlideCurveInfo> &r_curves_to_slide)
+ {
+ const Span<float2> surface_uv_coords = curves_orig_->surface_uv_coords();
+ const float brush_radius_sq_cu = pow2f(brush_radius_cu);
- /* Find curves in brush radius that should be moved. */
+ const Span<int> offsets = curves_orig_->offsets();
for (const int curve_i : curve_selection_) {
- const int first_point_i = curves_->offsets()[curve_i];
- const float3 &first_pos_cu = brush_transform_inv * positions_cu[first_point_i];
-
- float2 first_pos_re;
- ED_view3d_project_float_v2_m4(ctx_.region, first_pos_cu, first_pos_re, projection.values);
-
- const float dist_to_brush_sq_re = math::distance_squared(first_pos_re, brush_pos_re_);
- if (dist_to_brush_sq_re > brush_radius_sq_re) {
+ const int first_point_i = offsets[curve_i];
+ const float3 old_pos_cu = self_->initial_deformed_positions_cu_[first_point_i];
+ const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu);
+ if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
+ /* Root point is too far away from curve center. */
continue;
}
- const float dist_to_brush_re = std::sqrt(dist_to_brush_sq_re);
+ const float dist_to_brush_cu = std::sqrt(dist_to_brush_sq_cu);
const float radius_falloff = BKE_brush_curve_strength(
- brush_, dist_to_brush_re, brush_radius_re);
- curves_to_slide.append({curve_i, radius_falloff});
+ brush_, dist_to_brush_cu, brush_radius_cu);
+
+ const float2 uv = surface_uv_coords[curve_i];
+ ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv);
+ if (result.type != ReverseUVSampler::ResultType::Ok) {
+ /* The curve does not have a valid surface attachment. */
+ found_invalid_uv_mapping_.store(true);
+ continue;
+ }
+ /* Compute the normal at the initial surface position. */
+ const float3 normal_cu = math::normalize(
+ transforms_.surface_to_curves_normal *
+ geometry::compute_surface_point_normal(
+ *result.looptri, result.bary_weights, corner_normals_orig_su_));
+
+ r_curves_to_slide.append({curve_i, radius_falloff, normal_cu});
}
}
- void slide_projected()
+ void slide_with_symmetry()
{
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
-
- MutableSpan<float2> surface_uv_coords;
- if (!surface_uv_map_.is_empty()) {
- surface_uv_coords = curves_->surface_uv_coords_for_write();
+ const ReverseUVSampler reverse_uv_sampler_orig{surface_uv_map_orig_, surface_looptris_orig_};
+ for (const SlideInfo &slide_info : self_->slide_info_) {
+ this->slide(slide_info.curves_to_slide, reverse_uv_sampler_orig, slide_info.brush_transform);
}
+ }
- float4x4 projection;
- ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ void slide(const Span<SlideCurveInfo> slide_curves,
+ const ReverseUVSampler &reverse_uv_sampler_orig,
+ const float4x4 &brush_transform)
+ {
+ const float4x4 brush_transform_inv = brush_transform.inverted();
- for (const SlideInfo &slide_info : self_->slide_info_) {
- const float4x4 &brush_transform = slide_info.brush_transform;
- const float4x4 brush_transform_inv = brush_transform.inverted();
- const Span<SlideCurveInfo> curves_to_slide = slide_info.curves_to_slide;
-
- threading::parallel_for(curves_to_slide.index_range(), 256, [&](const IndexRange range) {
- for (const SlideCurveInfo &curve_slide_info : curves_to_slide.slice(range)) {
- const int curve_i = curve_slide_info.curve_i;
- const IndexRange points = curves_->points_for_curve(curve_i);
- const int first_point_i = points.first();
- const float3 old_first_pos_cu = brush_transform_inv * positions_cu[first_point_i];
-
- float2 old_first_pos_re;
- ED_view3d_project_float_v2_m4(
- ctx_.region, old_first_pos_cu, old_first_pos_re, projection.values);
- const float first_point_weight = brush_strength_ * curve_slide_info.radius_falloff;
-
- /* Slide root position in region space and then project it back onto the surface. */
- const float2 new_first_pos_re = old_first_pos_re +
- first_point_weight * brush_pos_diff_re_;
-
- float3 ray_start_wo, ray_end_wo;
- ED_view3d_win_to_segment_clipped(ctx_.depsgraph,
- ctx_.region,
- ctx_.v3d,
- new_first_pos_re,
- ray_start_wo,
- ray_end_wo,
- true);
- const float3 ray_start_su = transforms_.world_to_surface * ray_start_wo;
- const float3 ray_end_su = transforms_.world_to_surface * ray_end_wo;
-
- const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
- BVHTreeRayHit hit;
- hit.dist = FLT_MAX;
- hit.index = -1;
- BLI_bvhtree_ray_cast(surface_bvh_.tree,
- ray_start_su,
- ray_direction_su,
- 0.0f,
- &hit,
- surface_bvh_.raycast_callback,
- &surface_bvh_);
- if (hit.index == -1) {
- continue;
- }
+ const Span<MVert> verts_orig_su = surface_orig_->verts();
+ const Span<MLoop> loops_orig = surface_orig_->loops();
- const int looptri_index = hit.index;
- const float3 attached_pos_su = hit.co;
+ MutableSpan<float3> positions_orig_cu = curves_orig_->positions_for_write();
+ MutableSpan<float2> surface_uv_coords = curves_orig_->surface_uv_coords_for_write();
- const float3 attached_pos_cu = transforms_.surface_to_curves * attached_pos_su;
- const float3 pos_offset_cu = brush_transform * (attached_pos_cu - old_first_pos_cu);
+ float4x4 projection;
+ ED_view3d_ob_project_mat_get(ctx_.rv3d, curves_ob_orig_, projection.values);
+
+ const float2 brush_pos_diff_re = brush_pos_re_ - self_->initial_brush_pos_re_;
+
+ /* The brush transformation has to be applied in curves space. */
+ const float4x4 world_to_surface_with_symmetry_mat = transforms_.curves_to_surface *
+ brush_transform *
+ transforms_.world_to_curves;
+
+ threading::parallel_for(slide_curves.index_range(), 256, [&](const IndexRange range) {
+ for (const SlideCurveInfo &slide_curve_info : slide_curves.slice(range)) {
+ const int curve_i = slide_curve_info.curve_i;
+ const IndexRange points = curves_orig_->points_for_curve(curve_i);
+ const int first_point_i = points[0];
+
+ const float3 old_first_pos_eval_cu = self_->initial_deformed_positions_cu_[first_point_i];
+ const float3 old_first_symm_pos_eval_cu = brush_transform_inv * old_first_pos_eval_cu;
+ const float3 old_first_pos_eval_su = transforms_.curves_to_surface * old_first_pos_eval_cu;
+
+ float2 old_first_symm_pos_eval_re;
+ ED_view3d_project_float_v2_m4(ctx_.region,
+ old_first_symm_pos_eval_cu,
+ old_first_symm_pos_eval_re,
+ projection.values);
+
+ const float radius_falloff = slide_curve_info.radius_falloff;
+ const float curve_weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
+ const float2 new_first_symm_pos_eval_re = old_first_symm_pos_eval_re +
+ curve_weight * brush_pos_diff_re;
+
+ /* Compute the ray that will be used to find the new position on the surface. */
+ float3 ray_start_wo, ray_end_wo;
+ ED_view3d_win_to_segment_clipped(ctx_.depsgraph,
+ ctx_.region,
+ ctx_.v3d,
+ new_first_symm_pos_eval_re,
+ ray_start_wo,
+ ray_end_wo,
+ true);
+ const float3 ray_start_su = world_to_surface_with_symmetry_mat * ray_start_wo;
+ const float3 ray_end_su = world_to_surface_with_symmetry_mat * ray_end_wo;
+ const float3 ray_direction_su = math::normalize(ray_end_su - ray_start_su);
+
+ /* Find the ray hit that is closest to the initial curve root position. */
+ int looptri_index_eval;
+ float3 hit_pos_eval_su;
+ if (!this->find_closest_ray_hit(ray_start_su,
+ ray_direction_su,
+ old_first_pos_eval_su,
+ looptri_index_eval,
+ hit_pos_eval_su)) {
+ continue;
+ }
- /* Update positions. The first point doesn't have an additional weight here, because then
- * it wouldn't be attached to the surface anymore. */
- positions_cu[first_point_i] += pos_offset_cu;
- for (const int point_i : points.drop_front(1)) {
- const float weight = point_factors_[point_i];
- positions_cu[point_i] += weight * pos_offset_cu;
- }
+ /* Compute the uv of the new surface position on the evaluated mesh. */
+ const MLoopTri &looptri_eval = surface_looptris_eval_[looptri_index_eval];
+ const float3 bary_weights_eval = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
+ surface_verts_eval_, surface_loops_eval_, looptri_eval, hit_pos_eval_su);
+ const float2 uv = attribute_math::mix3(bary_weights_eval,
+ surface_uv_map_eval_[looptri_eval.tri[0]],
+ surface_uv_map_eval_[looptri_eval.tri[1]],
+ surface_uv_map_eval_[looptri_eval.tri[2]]);
+
+ /* Try to find the same uv on the original surface. */
+ const ReverseUVSampler::Result result = reverse_uv_sampler_orig.sample(uv);
+ if (result.type != ReverseUVSampler::ResultType::Ok) {
+ found_invalid_uv_mapping_.store(true);
+ continue;
+ }
+ const MLoopTri &looptri_orig = *result.looptri;
+ const float3 &bary_weights_orig = result.bary_weights;
+
+ /* Gather old and new surface normal. */
+ const float3 &initial_normal_cu = slide_curve_info.initial_normal_cu;
+ const float3 new_normal_cu = math::normalize(
+ transforms_.surface_to_curves_normal *
+ geometry::compute_surface_point_normal(
+ *result.looptri, result.bary_weights, corner_normals_orig_su_));
+
+ /* Gather old and new surface position. */
+ const float3 old_first_pos_orig_cu = self_->initial_positions_cu_[first_point_i];
+ const float3 new_first_pos_orig_cu =
+ transforms_.surface_to_curves *
+ attribute_math::mix3<float3>(bary_weights_orig,
+ verts_orig_su[loops_orig[looptri_orig.tri[0]].v].co,
+ verts_orig_su[loops_orig[looptri_orig.tri[1]].v].co,
+ verts_orig_su[loops_orig[looptri_orig.tri[2]].v].co);
+
+ /* Actually transform curve points. */
+ const float4x4 slide_transform = this->get_slide_transform(
+ old_first_pos_orig_cu, new_first_pos_orig_cu, initial_normal_cu, new_normal_cu);
+ for (const int point_i : points) {
+ positions_orig_cu[point_i] = slide_transform * self_->initial_positions_cu_[point_i];
+ }
+ surface_uv_coords[curve_i] = uv;
+ }
+ });
+ }
- /* Update surface attachment information if necessary. */
- if (!surface_uv_map_.is_empty()) {
- const MLoopTri &looptri = surface_looptris_[looptri_index];
- const float3 bary_coord = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
- *surface_, looptri, attached_pos_su);
- const float2 &uv0 = surface_uv_map_[looptri.tri[0]];
- const float2 &uv1 = surface_uv_map_[looptri.tri[1]];
- const float2 &uv2 = surface_uv_map_[looptri.tri[2]];
- const float2 uv = attribute_math::mix3(bary_coord, uv0, uv1, uv2);
- surface_uv_coords[curve_i] = uv;
+ bool find_closest_ray_hit(const float3 &ray_start_su,
+ const float3 &ray_direction_su,
+ const float3 &point_su,
+ int &r_looptri_index,
+ float3 &r_hit_pos)
+ {
+ float best_dist_sq_su = FLT_MAX;
+ int best_looptri_index_eval;
+ float3 best_hit_pos_su;
+ BLI_bvhtree_ray_cast_all_cpp(
+ *surface_bvh_eval_.tree,
+ ray_start_su,
+ ray_direction_su,
+ 0.0f,
+ FLT_MAX,
+ [&](const int looptri_index, const BVHTreeRay &ray, BVHTreeRayHit &hit) {
+ surface_bvh_eval_.raycast_callback(&surface_bvh_eval_, looptri_index, &ray, &hit);
+ if (hit.index < 0) {
+ return;
}
- }
- });
+ const float3 &hit_pos_su = hit.co;
+ const float dist_sq_su = math::distance_squared(hit_pos_su, point_su);
+ if (dist_sq_su < best_dist_sq_su) {
+ best_dist_sq_su = dist_sq_su;
+ best_hit_pos_su = hit_pos_su;
+ best_looptri_index_eval = hit.index;
+ }
+ });
+
+ if (best_dist_sq_su == FLT_MAX) {
+ return false;
}
+ r_looptri_index = best_looptri_index_eval;
+ r_hit_pos = best_hit_pos_su;
+ return true;
+ }
+
+ float4x4 get_slide_transform(const float3 &old_root_pos,
+ const float3 &new_root_pos,
+ const float3 &old_normal,
+ const float3 &new_normal)
+ {
+ float3x3 rotation_3x3;
+ rotation_between_vecs_to_mat3(rotation_3x3.values, old_normal, new_normal);
+ float4x4 rotation_4x4;
+ copy_m4_m3(rotation_4x4.values, rotation_3x3.values);
+
+ float4x4 transform = float4x4::identity();
+ sub_v3_v3(transform.values[3], old_root_pos);
+ mul_m4_m4_pre(transform.values, rotation_4x4.values);
+ add_v3_v3(transform.values[3], new_root_pos);
+ return transform;
}
};
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc
index f874a9fc255..37a7f1c83ed 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_smooth.cc
@@ -2,6 +2,7 @@
#include "BKE_brush.h"
#include "BKE_context.h"
+#include "BKE_crazyspace.hh"
#include "ED_screen.h"
#include "ED_view3d.h"
@@ -95,60 +96,56 @@ struct SmoothOperationExecutor {
}
}
+ Array<float> point_smooth_factors(curves_->points_num(), 0.0f);
+
if (falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
- this->smooth_projected_with_symmetry();
+ this->find_projected_smooth_factors_with_symmetry(point_smooth_factors);
}
else if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) {
- this->smooth_spherical_with_symmetry();
+ this->find_spherical_smooth_factors_with_symmetry(point_smooth_factors);
}
else {
BLI_assert_unreachable();
}
+ this->smooth(point_smooth_factors);
curves_->tag_positions_changed();
DEG_id_tag_update(&curves_id_->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, &curves_id_->id);
ED_region_tag_redraw(ctx_.region);
}
- void smooth_projected_with_symmetry()
+ void find_projected_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors)
{
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
- this->smooth_projected(brush_transform);
+ this->find_projected_smooth_factors(brush_transform, r_point_smooth_factors);
}
}
- void smooth_projected(const float4x4 &brush_transform)
+ void find_projected_smooth_factors(const float4x4 &brush_transform,
+ MutableSpan<float> r_point_smooth_factors)
{
const float4x4 brush_transform_inv = brush_transform.inverted();
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
const float brush_radius_re = brush_radius_base_re_ * brush_radius_factor_;
const float brush_radius_sq_re = pow2f(brush_radius_re);
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
- Vector<float2> old_curve_positions_re;
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
-
- /* Find position of control points in screen space. */
- old_curve_positions_re.clear();
- old_curve_positions_re.reserve(points.size());
for (const int point_i : points) {
- const float3 &pos_cu = brush_transform_inv * positions_cu[point_i];
+ const float3 &pos_cu = brush_transform_inv * deformation.positions[point_i];
float2 pos_re;
ED_view3d_project_float_v2_m4(ctx_.region, pos_cu, pos_re, projection.values);
- old_curve_positions_re.append_unchecked(pos_re);
- }
- for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) {
- const int point_i = points[i];
- const float2 &old_pos_re = old_curve_positions_re[i];
- const float dist_to_brush_sq_re = math::distance_squared(old_pos_re, brush_pos_re_);
+ const float dist_to_brush_sq_re = math::distance_squared(pos_re, brush_pos_re_);
if (dist_to_brush_sq_re > brush_radius_sq_re) {
continue;
}
@@ -161,27 +158,13 @@ struct SmoothOperationExecutor {
const float weight_factor = 0.1f;
const float weight = weight_factor * brush_strength_ * radius_falloff *
point_factors_[point_i];
-
- /* Move points towards the middle of their neighbors. */
- const float2 &old_pos_prev_re = old_curve_positions_re[i - 1];
- const float2 &old_pos_next_re = old_curve_positions_re[i + 1];
- const float2 goal_pos_re = math::midpoint(old_pos_prev_re, old_pos_next_re);
- const float2 new_pos_re = math::interpolate(old_pos_re, goal_pos_re, weight);
- const float3 old_pos_cu = brush_transform_inv * positions_cu[point_i];
- float3 new_pos_wo;
- ED_view3d_win_to_3d(ctx_.v3d,
- ctx_.region,
- transforms_.curves_to_world * old_pos_cu,
- new_pos_re,
- new_pos_wo);
- const float3 new_pos_cu = brush_transform * (transforms_.world_to_curves * new_pos_wo);
- positions_cu[point_i] = new_pos_cu;
+ math::max_inplace(r_point_smooth_factors[point_i], weight);
}
}
});
}
- void smooth_spherical_with_symmetry()
+ void find_spherical_smooth_factors_with_symmetry(MutableSpan<float> r_point_smooth_factors)
{
float4x4 projection;
ED_view3d_ob_project_mat_get(ctx_.rv3d, object_, projection.values);
@@ -198,27 +181,25 @@ struct SmoothOperationExecutor {
const Vector<float4x4> symmetry_brush_transforms = get_symmetry_brush_transforms(
eCurvesSymmetryType(curves_id_->symmetry));
for (const float4x4 &brush_transform : symmetry_brush_transforms) {
- this->smooth_spherical(brush_transform * brush_pos_cu, brush_radius_cu);
+ this->find_spherical_smooth_factors(
+ brush_transform * brush_pos_cu, brush_radius_cu, r_point_smooth_factors);
}
}
- void smooth_spherical(const float3 &brush_pos_cu, const float brush_radius_cu)
+ void find_spherical_smooth_factors(const float3 &brush_pos_cu,
+ const float brush_radius_cu,
+ MutableSpan<float> r_point_smooth_factors)
{
- MutableSpan<float3> positions_cu = curves_->positions_for_write();
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
- Vector<float3> old_curve_positions_cu;
for (const int curve_i : curve_selection_.slice(range)) {
const IndexRange points = curves_->points_for_curve(curve_i);
- /* Remember original positions so that we don't smooth based on already smoothed points
- * below. */
- old_curve_positions_cu.clear();
- old_curve_positions_cu.extend(positions_cu.slice(points));
- for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) {
- const int point_i = points[i];
- const float3 &old_pos_cu = old_curve_positions_cu[i];
- const float dist_to_brush_sq_cu = math::distance_squared(old_pos_cu, brush_pos_cu);
+ for (const int point_i : points) {
+ const float3 &pos_cu = deformation.positions[point_i];
+ const float dist_to_brush_sq_cu = math::distance_squared(pos_cu, brush_pos_cu);
if (dist_to_brush_sq_cu > brush_radius_sq_cu) {
continue;
}
@@ -231,13 +212,34 @@ struct SmoothOperationExecutor {
const float weight_factor = 0.1f;
const float weight = weight_factor * brush_strength_ * radius_falloff *
point_factors_[point_i];
+ math::max_inplace(r_point_smooth_factors[point_i], weight);
+ }
+ }
+ });
+ }
- /* Move points towards the middle of their neighbors. */
- const float3 &old_pos_prev_cu = old_curve_positions_cu[i - 1];
- const float3 &old_pos_next_cu = old_curve_positions_cu[i + 1];
- const float3 goal_pos_cu = math::midpoint(old_pos_prev_cu, old_pos_next_cu);
- const float3 new_pos_cu = math::interpolate(old_pos_cu, goal_pos_cu, weight);
- positions_cu[point_i] = new_pos_cu;
+ void smooth(const Span<float> point_smooth_factors)
+ {
+ MutableSpan<float3> positions = curves_->positions_for_write();
+ threading::parallel_for(curve_selection_.index_range(), 256, [&](const IndexRange range) {
+ Vector<float3> old_positions;
+ for (const int curve_i : curve_selection_.slice(range)) {
+ const IndexRange points = curves_->points_for_curve(curve_i);
+ old_positions.clear();
+ old_positions.extend(positions.slice(points));
+ for (const int i : IndexRange(points.size()).drop_front(1).drop_back(1)) {
+ const int point_i = points[i];
+ const float smooth_factor = point_smooth_factors[point_i];
+ if (smooth_factor == 0.0f) {
+ continue;
+ }
+ /* Move towards the middle of the neighboring points. */
+ const float3 old_pos = old_positions[i];
+ const float3 &prev_pos = old_positions[i - 1];
+ const float3 &next_pos = old_positions[i + 1];
+ const float3 goal_pos = math::midpoint(prev_pos, next_pos);
+ const float3 new_pos = math::interpolate(old_pos, goal_pos, smooth_factor);
+ positions[point_i] = new_pos;
}
}
});
diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
index ec0e8ff45e5..67757ce5f4a 100644
--- a/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
+++ b/source/blender/editors/sculpt_paint/curves_sculpt_snake_hook.cc
@@ -176,6 +176,8 @@ struct SnakeHookOperatorExecutor {
void projected_snake_hook(const float4x4 &brush_transform)
{
const float4x4 brush_transform_inv = brush_transform.inverted();
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
MutableSpan<float3> positions_cu = curves_->positions_for_write();
@@ -186,15 +188,18 @@ struct SnakeHookOperatorExecutor {
const float brush_radius_sq_re = pow2f(brush_radius_re);
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ MoveAndResampleBuffers resample_buffer;
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const int last_point_i = points.last();
- const float3 old_pos_cu = brush_transform_inv * positions_cu[last_point_i];
+ const float3 old_pos_cu = deformation.positions[last_point_i];
+ const float3 old_symm_pos_cu = brush_transform_inv * old_pos_cu;
- float2 old_pos_re;
- ED_view3d_project_float_v2_m4(ctx_.region, old_pos_cu, old_pos_re, projection.values);
+ float2 old_symm_pos_re;
+ ED_view3d_project_float_v2_m4(
+ ctx_.region, old_symm_pos_cu, old_symm_pos_re, projection.values);
- const float distance_to_brush_sq_re = math::distance_squared(old_pos_re,
+ const float distance_to_brush_sq_re = math::distance_squared(old_symm_pos_re,
brush_pos_prev_re_);
if (distance_to_brush_sq_re > brush_radius_sq_re) {
continue;
@@ -204,17 +209,21 @@ struct SnakeHookOperatorExecutor {
brush_, std::sqrt(distance_to_brush_sq_re), brush_radius_re);
const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
- const float2 new_position_re = old_pos_re + brush_pos_diff_re_ * weight;
- float3 new_position_wo;
+ const float2 new_symm_pos_re = old_symm_pos_re + brush_pos_diff_re_ * weight;
+ float3 new_symm_pos_wo;
ED_view3d_win_to_3d(ctx_.v3d,
ctx_.region,
- transforms_.curves_to_world * old_pos_cu,
- new_position_re,
- new_position_wo);
- const float3 new_position_cu = brush_transform *
- (transforms_.world_to_curves * new_position_wo);
-
- move_last_point_and_resample(positions_cu.slice(points), new_position_cu);
+ transforms_.curves_to_world * old_symm_pos_cu,
+ new_symm_pos_re,
+ new_symm_pos_wo);
+ const float3 new_pos_cu = brush_transform *
+ (transforms_.world_to_curves * new_symm_pos_wo);
+ const float3 translation_eval = new_pos_cu - old_pos_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ last_point_i, translation_eval);
+
+ const float3 last_point_cu = positions_cu[last_point_i] + translation_orig;
+ move_last_point_and_resample(resample_buffer, positions_cu.slice(points), last_point_cu);
}
});
}
@@ -252,15 +261,19 @@ struct SnakeHookOperatorExecutor {
const float3 &brush_end_cu,
const float brush_radius_cu)
{
+ const bke::crazyspace::GeometryDeformation deformation =
+ bke::crazyspace::get_evaluated_curves_deformation(*ctx_.depsgraph, *object_);
+
MutableSpan<float3> positions_cu = curves_->positions_for_write();
const float3 brush_diff_cu = brush_end_cu - brush_start_cu;
const float brush_radius_sq_cu = pow2f(brush_radius_cu);
threading::parallel_for(curves_->curves_range(), 256, [&](const IndexRange curves_range) {
+ MoveAndResampleBuffers resample_buffer;
for (const int curve_i : curves_range) {
const IndexRange points = curves_->points_for_curve(curve_i);
const int last_point_i = points.last();
- const float3 old_pos_cu = positions_cu[last_point_i];
+ const float3 old_pos_cu = deformation.positions[last_point_i];
const float distance_to_brush_sq_cu = dist_squared_to_line_segment_v3(
old_pos_cu, brush_start_cu, brush_end_cu);
@@ -274,9 +287,12 @@ struct SnakeHookOperatorExecutor {
brush_, distance_to_brush_cu, brush_radius_cu);
const float weight = brush_strength_ * radius_falloff * curve_factors_[curve_i];
- const float3 new_pos_cu = old_pos_cu + weight * brush_diff_cu;
+ const float3 translation_eval = weight * brush_diff_cu;
+ const float3 translation_orig = deformation.translation_from_deformed_to_original(
+ last_point_i, translation_eval);
- move_last_point_and_resample(positions_cu.slice(points), new_pos_cu);
+ const float3 last_point_cu = positions_cu[last_point_i] + translation_orig;
+ move_last_point_and_resample(resample_buffer, positions_cu.slice(points), last_point_cu);
}
});
}
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index c5ebcf870a3..164e13ac3c9 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -628,7 +628,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups,
/* Premultiplied alpha blending. */
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR);
float final_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
if (!col) {
@@ -720,7 +720,7 @@ static bool paint_draw_cursor_overlay(
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR);
float final_color[4] = {UNPACK3(U.sculpt_paint_overlay_col), 1.0f};
mul_v4_fl(final_color, brush->cursor_overlay_alpha * 0.01f);
@@ -915,7 +915,7 @@ static void paint_draw_curve_cursor(Brush *brush, ViewContext *vc)
/* Draw the bezier handles and the curve segment between the current and next point. */
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
float selec_col[4], handle_col[4], pivot_col[4];
UI_GetThemeColorType4fv(TH_VERTEX_SELECT, SPACE_VIEW3D, selec_col);
@@ -1145,11 +1145,10 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr,
}
GPU_line_width(1.0f);
- if (ss->preview_vert_index_count > 0) {
- immBegin(GPU_PRIM_LINES, ss->preview_vert_index_count);
- for (int i = 0; i < ss->preview_vert_index_count; i++) {
- immVertex3fv(gpuattr,
- SCULPT_vertex_co_for_grab_active_get(ss, ss->preview_vert_index_list[i]));
+ if (ss->preview_vert_count > 0) {
+ immBegin(GPU_PRIM_LINES, ss->preview_vert_count);
+ for (int i = 0; i < ss->preview_vert_count; i++) {
+ immVertex3fv(gpuattr, SCULPT_vertex_co_for_grab_active_get(ss, ss->preview_vert_list[i]));
}
immEnd();
}
@@ -1209,7 +1208,7 @@ typedef struct PaintCursorContext {
/* Sculpt related data. */
Sculpt *sd;
SculptSession *ss;
- int prev_active_vertex_index;
+ PBVHVertRef prev_active_vertex;
bool is_stroke_active;
bool is_cursor_over_mesh;
bool is_multires;
@@ -1366,7 +1365,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon
/* This updates the active vertex, which is needed for most of the Sculpt/Vertex Colors tools to
* work correctly */
- pcontext->prev_active_vertex_index = ss->active_vertex_index;
+ pcontext->prev_active_vertex = ss->active_vertex;
if (!ups->stroke_active) {
pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update(
C, &gi, mval_fl, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE));
@@ -1549,7 +1548,7 @@ static void paint_cursor_preview_boundary_data_update(PaintCursorContext *pconte
}
ss->boundary_preview = SCULPT_boundary_data_init(
- pcontext->vc.obact, pcontext->brush, ss->active_vertex_index, pcontext->radius);
+ pcontext->vc.obact, pcontext->brush, ss->active_vertex, pcontext->radius);
}
static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *pcontext)
@@ -1575,8 +1574,8 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *
paint_cursor_update_object_space_radius(pcontext);
- const bool update_previews = pcontext->prev_active_vertex_index !=
- SCULPT_active_vertex_get(pcontext->ss);
+ const bool update_previews = pcontext->prev_active_vertex.i !=
+ SCULPT_active_vertex_get(pcontext->ss).i;
/* Setup drawing. */
wmViewport(&pcontext->region->winrct);
@@ -1870,7 +1869,7 @@ static void paint_cursor_setup_2D_drawing(PaintCursorContext *pcontext)
GPU_line_smooth(true);
pcontext->pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
}
static void paint_cursor_setup_3D_drawing(PaintCursorContext *pcontext)
diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c
index 944b3f953a0..9e435ee0748 100644
--- a/source/blender/editors/sculpt_paint/paint_hide.c
+++ b/source/blender/editors/sculpt_paint/paint_hide.c
@@ -78,6 +78,12 @@ static void partialvis_update_mesh(Object *ob,
BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK);
+ bool *hide_vert = CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert");
+ if (hide_vert == NULL) {
+ hide_vert = CustomData_add_layer_named(
+ &me->vdata, CD_PROP_BOOL, CD_SET_DEFAULT, NULL, me->totvert, ".hide_vert");
+ }
+
SCULPT_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN);
for (i = 0; i < totvert; i++) {
@@ -86,16 +92,11 @@ static void partialvis_update_mesh(Object *ob,
/* Hide vertex if in the hide volume. */
if (is_effected(area, planes, v->co, vmask)) {
- if (action == PARTIALVIS_HIDE) {
- v->flag |= ME_HIDE;
- }
- else {
- v->flag &= ~ME_HIDE;
- }
+ hide_vert[vert_indices[i]] = (action == PARTIALVIS_HIDE);
any_changed = true;
}
- if (!(v->flag & ME_HIDE)) {
+ if (!hide_vert[vert_indices[i]]) {
any_visible = true;
}
}
@@ -350,10 +351,10 @@ static int hide_show_exec(bContext *C, wmOperator *op)
/* Start undo. */
switch (action) {
case PARTIALVIS_HIDE:
- SCULPT_undo_push_begin(ob, "Hide area");
+ SCULPT_undo_push_begin_ex(ob, "Hide area");
break;
case PARTIALVIS_SHOW:
- SCULPT_undo_push_begin(ob, "Show area");
+ SCULPT_undo_push_begin_ex(ob, "Show area");
break;
}
@@ -382,10 +383,9 @@ static int hide_show_exec(bContext *C, wmOperator *op)
* sculpt but it looks wrong when entering editmode otherwise). */
if (pbvh_type == PBVH_FACES) {
BKE_mesh_flush_hidden_from_verts(me);
+ BKE_pbvh_update_hide_attributes_from_mesh(pbvh);
}
- SCULPT_visibility_sync_all_vertex_to_face_sets(ob->sculpt);
-
DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
ED_region_tag_redraw(region);
diff --git a/source/blender/editors/sculpt_paint/paint_image.cc b/source/blender/editors/sculpt_paint/paint_image.cc
index 56cd4751881..c852fd25bc4 100644
--- a/source/blender/editors/sculpt_paint/paint_image.cc
+++ b/source/blender/editors/sculpt_paint/paint_image.cc
@@ -125,7 +125,7 @@ void ED_imapaint_dirty_region(
imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh);
- ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ PaintTileMap *undo_tiles = ED_image_paint_tile_map_get();
for (ty = tiley; ty <= tileh; ty++) {
for (tx = tilex; tx <= tilew; tx++) {
@@ -158,11 +158,21 @@ void imapaint_image_update(
imapaintpartial.dirty_region.xmax,
imapaintpartial.dirty_region.ymax);
+ /* When buffer is partial updated the planes should be set to a larger value than 8. This will
+ * make sure that partial updating is working but uses more GPU memory as the gpu texture will
+ * have 4 channels. When so the whole texture needs to be reuploaded to the GPU using the new
+ * texture format. */
+ if (ibuf != nullptr && ibuf->planes == 8) {
+ ibuf->planes = 32;
+ BKE_image_partial_update_mark_full_update(image);
+ return;
+ }
+
/* TODO: should set_tpage create ->rect? */
if (texpaint || (sima && sima->lock)) {
const int w = BLI_rcti_size_x(&imapaintpartial.dirty_region);
const int h = BLI_rcti_size_y(&imapaintpartial.dirty_region);
- /* Testing with partial update in uv editor too */
+ /* Testing with partial update in uv editor too. */
BKE_image_update_gputexture(
image, iuser, imapaintpartial.dirty_region.xmin, imapaintpartial.dirty_region.ymin, w, h);
}
@@ -276,10 +286,11 @@ static bool image_paint_poll_ex(bContext *C, bool check_tool)
(ID_IS_LINKED(sima->image) || ID_IS_OVERRIDE_LIBRARY(sima->image))) {
return false;
}
- ARegion *region = CTX_wm_region(C);
-
- if ((sima->mode == SI_MODE_PAINT) && region->regiontype == RGN_TYPE_WINDOW) {
- return true;
+ if (sima->mode == SI_MODE_PAINT) {
+ const ARegion *region = CTX_wm_region(C);
+ if (region->regiontype == RGN_TYPE_WINDOW) {
+ return true;
+ }
}
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index fae2e6863fa..5df65e596b9 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -1196,7 +1196,7 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
ImBuf tmpbuf;
IMB_initImBuf(&tmpbuf, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, 0);
- ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ PaintTileMap *undo_tiles = ED_image_paint_tile_map_get();
for (int ty = tiley; ty <= tileh; ty++) {
for (int tx = tilex; tx <= tilew; tx++) {
diff --git a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc
index a671c24c514..5d50cdb4d1f 100644
--- a/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc
+++ b/source/blender/editors/sculpt_paint/paint_image_ops_paint.cc
@@ -13,6 +13,7 @@
#include "BKE_brush.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_paint.h"
#include "BKE_undo_system.h"
@@ -252,7 +253,7 @@ static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customda
ARegion *region = pop->vc.region;
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_line_width(4.0);
immUniformColor4ub(0, 0, 0, 255);
@@ -293,7 +294,8 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const flo
copy_v2_v2(pop->startmouse, mouse);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
/* initialize from context */
if (CTX_wm_region_view3d(C)) {
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 50480b8aef0..946a59d0001 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -56,6 +56,7 @@
#include "BKE_global.h"
#include "BKE_idprop.h"
#include "BKE_image.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -414,6 +415,7 @@ typedef struct ProjPaintState {
const float (*vert_normals)[3];
const MEdge *medge_eval;
const MPoly *mpoly_eval;
+ const int *material_indices;
const MLoop *mloop_eval;
const MLoopTri *mlooptri_eval;
@@ -542,8 +544,8 @@ static int project_paint_face_paint_tile(Image *ima, const float *uv)
static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index)
{
- const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index);
- Material *ma = ps->mat_array[mp->mat_nr];
+ const int poly_i = ps->mlooptri_eval[tri_index].poly;
+ Material *ma = ps->mat_array[ps->material_indices == NULL ? 0 : ps->material_indices[poly_i]];
return ma ? ma->texpaintslot + ma->paint_active_slot : NULL;
}
@@ -553,23 +555,23 @@ static Image *project_paint_face_paint_image(const ProjPaintState *ps, int tri_i
return ps->stencil_ima;
}
- const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index);
- Material *ma = ps->mat_array[mp->mat_nr];
+ const int poly_i = ps->mlooptri_eval[tri_index].poly;
+ Material *ma = ps->mat_array[ps->material_indices == NULL ? 0 : ps->material_indices[poly_i]];
TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL;
return slot ? slot->ima : ps->canvas_ima;
}
static TexPaintSlot *project_paint_face_clone_slot(const ProjPaintState *ps, int tri_index)
{
- const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index);
- Material *ma = ps->mat_array[mp->mat_nr];
+ const int poly_i = ps->mlooptri_eval[tri_index].poly;
+ Material *ma = ps->mat_array[ps->material_indices == NULL ? 0 : ps->material_indices[poly_i]];
return ma ? ma->texpaintslot + ma->paint_clone_slot : NULL;
}
static Image *project_paint_face_clone_image(const ProjPaintState *ps, int tri_index)
{
- const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index);
- Material *ma = ps->mat_array[mp->mat_nr];
+ const int poly_i = ps->mlooptri_eval[tri_index].poly;
+ Material *ma = ps->mat_array[ps->material_indices == NULL ? 0 : ps->material_indices[poly_i]];
TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_clone_slot : NULL;
return slot ? slot->ima : ps->clone_ima;
}
@@ -1223,20 +1225,20 @@ static VertSeam *find_adjacent_seam(const ProjPaintState *ps,
/* Circulate through the (sorted) vert seam array, in the direction of the seam normal,
* until we find the first opposing seam, matching in UV space. */
if (seam->normal_cw) {
- LISTBASE_CIRCULAR_BACKWARD_BEGIN (vert_seams, adjacent, seam) {
+ LISTBASE_CIRCULAR_BACKWARD_BEGIN (VertSeam *, vert_seams, adjacent, seam) {
if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) {
break;
}
}
- LISTBASE_CIRCULAR_BACKWARD_END(vert_seams, adjacent, seam);
+ LISTBASE_CIRCULAR_BACKWARD_END(VertSeam *, vert_seams, adjacent, seam);
}
else {
- LISTBASE_CIRCULAR_FORWARD_BEGIN (vert_seams, adjacent, seam) {
+ LISTBASE_CIRCULAR_FORWARD_BEGIN (VertSeam *, vert_seams, adjacent, seam) {
if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) {
break;
}
}
- LISTBASE_CIRCULAR_FORWARD_END(vert_seams, adjacent, seam);
+ LISTBASE_CIRCULAR_FORWARD_END(VertSeam *, vert_seams, adjacent, seam);
}
BLI_assert(adjacent);
@@ -1521,7 +1523,7 @@ static void project_face_seams_init(const ProjPaintState *ps,
static void screen_px_from_ortho(const float uv[2],
const float v1co[3],
const float v2co[3],
- const float v3co[3], /* Screenspace coords */
+ const float v3co[3], /* Screen-space coords */
const float uv1co[2],
const float uv2co[2],
const float uv3co[2],
@@ -1813,7 +1815,7 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
}
if (generate_tile) {
- ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ struct PaintTileMap *undo_tiles = ED_image_paint_tile_map_get();
volatile void *undorect;
if (tinf->masked) {
undorect = ED_image_paint_tile_push(undo_tiles,
@@ -4053,13 +4055,15 @@ static bool proj_paint_state_mesh_eval_init(const bContext *C, ProjPaintState *p
}
ps->mat_array[totmat - 1] = NULL;
- ps->mvert_eval = ps->me_eval->mvert;
+ ps->mvert_eval = BKE_mesh_verts(ps->me_eval);
ps->vert_normals = BKE_mesh_vertex_normals_ensure(ps->me_eval);
if (ps->do_mask_cavity) {
- ps->medge_eval = ps->me_eval->medge;
+ ps->medge_eval = BKE_mesh_edges(ps->me_eval);
}
- ps->mloop_eval = ps->me_eval->mloop;
- ps->mpoly_eval = ps->me_eval->mpoly;
+ ps->mloop_eval = BKE_mesh_loops(ps->me_eval);
+ ps->mpoly_eval = BKE_mesh_polys(ps->me_eval);
+ ps->material_indices = (const int *)CustomData_get_layer_named(
+ &ps->me_eval->pdata, CD_PROP_INT32, "material_index");
ps->totvert_eval = ps->me_eval->totvert;
ps->totedge_eval = ps->me_eval->totedge;
@@ -4151,7 +4155,7 @@ static void proj_paint_face_lookup_init(const ProjPaintState *ps, ProjPaintFaceL
memset(face_lookup, 0, sizeof(*face_lookup));
if (ps->do_face_sel) {
face_lookup->index_mp_to_orig = CustomData_get_layer(&ps->me_eval->pdata, CD_ORIGINDEX);
- face_lookup->mpoly_orig = ((Mesh *)ps->ob->data)->mpoly;
+ face_lookup->mpoly_orig = BKE_mesh_polys((Mesh *)ps->ob->data);
}
}
@@ -6038,7 +6042,8 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op)
int orig_brush_size;
IDProperty *idgroup;
IDProperty *view_data = NULL;
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
bool uvs, mat, tex;
if (ob == NULL || ob->type != OB_MESH) {
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index ea17114efa5..99c25953d50 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -46,7 +46,10 @@ typedef struct CoNo {
/* paint_stroke.c */
-typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]);
+typedef bool (*StrokeGetLocation)(struct bContext *C,
+ float location[3],
+ const float mouse[2],
+ bool force_original);
typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]);
typedef void (*StrokeUpdateStep)(struct bContext *C,
struct wmOperator *op,
@@ -370,10 +373,12 @@ void PAINT_OT_face_select_linked(struct wmOperatorType *ot);
void PAINT_OT_face_select_linked_pick(struct wmOperatorType *ot);
void PAINT_OT_face_select_all(struct wmOperatorType *ot);
void PAINT_OT_face_select_hide(struct wmOperatorType *ot);
-void PAINT_OT_face_select_reveal(struct wmOperatorType *ot);
+
+void PAINT_OT_face_vert_reveal(struct wmOperatorType *ot);
void PAINT_OT_vert_select_all(struct wmOperatorType *ot);
void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot);
+void PAINT_OT_vert_select_hide(struct wmOperatorType *ot);
bool vert_paint_poll(struct bContext *C);
bool mask_paint_poll(struct bContext *C);
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 89bbf2a3c92..437ff7506ba 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -134,6 +134,7 @@ static void mask_flood_fill_task_cb(void *__restrict userdata,
static int mask_flood_fill_exec(bContext *C, wmOperator *op)
{
+ const Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
PaintMaskFloodMode mode;
@@ -146,13 +147,16 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op)
mode = RNA_enum_get(op->ptr, "mode");
value = RNA_float_get(op->ptr, "value");
+ MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
+ BKE_sculpt_mask_layers_ensure(ob, mmd);
+
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false);
pbvh = ob->sculpt->pbvh;
multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
- SCULPT_undo_push_begin(ob, "Mask flood fill");
+ SCULPT_undo_push_begin(ob, op);
MaskTaskData data = {
.ob = ob,
@@ -663,7 +667,7 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co
static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd)
{
float vertex_normal[3];
- SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal);
+ SCULPT_vertex_normal_get(sgcontext->ss, vd->vertex, vertex_normal);
float dot = dot_v3v3(sgcontext->view_normal, vertex_normal);
const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f);
@@ -687,10 +691,10 @@ static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, P
return false;
}
-static void sculpt_gesture_apply(bContext *C, SculptGestureContext *sgcontext)
+static void sculpt_gesture_apply(bContext *C, SculptGestureContext *sgcontext, wmOperator *op)
{
SculptGestureOperation *operation = sgcontext->operation;
- SCULPT_undo_push_begin(CTX_data_active_object(C), "Sculpt Gesture Apply");
+ SCULPT_undo_push_begin(CTX_data_active_object(C), op);
operation->sculpt_gesture_begin(C, sgcontext);
@@ -743,7 +747,7 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) {
- SCULPT_vertex_face_set_set(sgcontext->ss, vd.index, face_set_operation->new_face_set_id);
+ SCULPT_vertex_face_set_set(sgcontext->ss, vd.vertex, face_set_operation->new_face_set_id);
any_updated = true;
}
}
@@ -774,6 +778,8 @@ static void sculpt_gesture_init_face_set_properties(SculptGestureContext *sgcont
struct Mesh *mesh = BKE_mesh_from_object(sgcontext->vc.obact);
sgcontext->operation = MEM_callocN(sizeof(SculptGestureFaceSetOperation), "Face Set Operation");
+ sgcontext->ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
+
SculptGestureFaceSetOperation *face_set_operation = (SculptGestureFaceSetOperation *)
sgcontext->operation;
@@ -817,7 +823,7 @@ static void mask_gesture_apply_task_cb(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) {
- float prevmask = *vd.mask;
+ float prevmask = vd.mask ? *vd.mask : 0.0f;
if (!any_masked) {
any_masked = true;
@@ -863,6 +869,10 @@ static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext,
SculptGestureMaskOperation *mask_operation = (SculptGestureMaskOperation *)sgcontext->operation;
+ Object *object = sgcontext->vc.obact;
+ MultiresModifierData *mmd = BKE_sculpt_multires_active(sgcontext->vc.scene, object);
+ BKE_sculpt_mask_layers_ensure(sgcontext->vc.obact, mmd);
+
mask_operation->op.sculpt_gesture_begin = sculpt_gesture_mask_begin;
mask_operation->op.sculpt_gesture_apply_for_symmetry_pass =
sculpt_gesture_mask_apply_for_symmetry_pass;
@@ -1025,7 +1035,9 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext)
trim_operation->depth_back = -FLT_MAX;
for (int i = 0; i < totvert; i++) {
- const float *vco = SCULPT_vertex_co_get(ss, i);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ const float *vco = SCULPT_vertex_co_get(ss, vertex);
/* Convert the coordinates to world space to calculate the depth. When generating the trimming
* mesh, coordinates are first calculated in world space, then converted to object space to
* store them. */
@@ -1121,6 +1133,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
const float(*ob_imat)[4] = vc->obact->imat;
/* Write vertices coordinates for the front face. */
+ MVert *verts = BKE_mesh_verts_for_write(trim_operation->mesh);
float depth_point[3];
madd_v3_v3v3fl(depth_point, shape_origin, shape_normal, depth_front);
for (int i = 0; i < tot_screen_points; i++) {
@@ -1132,7 +1145,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
ED_view3d_win_to_3d_on_plane(region, shape_plane, screen_points[i], false, new_point);
madd_v3_v3fl(new_point, shape_normal, depth_front);
}
- mul_v3_m4v3(trim_operation->mesh->mvert[i].co, ob_imat, new_point);
+ mul_v3_m4v3(verts[i].co, ob_imat, new_point);
mul_v3_m4v3(trim_operation->true_mesh_co[i], ob_imat, new_point);
}
@@ -1147,7 +1160,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
ED_view3d_win_to_3d_on_plane(region, shape_plane, screen_points[i], false, new_point);
madd_v3_v3fl(new_point, shape_normal, depth_back);
}
- mul_v3_m4v3(trim_operation->mesh->mvert[i + tot_screen_points].co, ob_imat, new_point);
+ mul_v3_m4v3(verts[i + tot_screen_points].co, ob_imat, new_point);
mul_v3_m4v3(trim_operation->true_mesh_co[i + tot_screen_points], ob_imat, new_point);
}
@@ -1157,10 +1170,12 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
BLI_polyfill_calc(screen_points, tot_screen_points, 0, r_tris);
/* Write the front face triangle indices. */
- MPoly *mp = trim_operation->mesh->mpoly;
- MLoop *ml = trim_operation->mesh->mloop;
+ MPoly *polys = BKE_mesh_polys_for_write(trim_operation->mesh);
+ MLoop *loops = BKE_mesh_loops_for_write(trim_operation->mesh);
+ MPoly *mp = polys;
+ MLoop *ml = loops;
for (int i = 0; i < tot_tris_face; i++, mp++, ml += 3) {
- mp->loopstart = (int)(ml - trim_operation->mesh->mloop);
+ mp->loopstart = (int)(ml - loops);
mp->totloop = 3;
ml[0].v = r_tris[i][0];
ml[1].v = r_tris[i][1];
@@ -1169,7 +1184,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
/* Write the back face triangle indices. */
for (int i = 0; i < tot_tris_face; i++, mp++, ml += 3) {
- mp->loopstart = (int)(ml - trim_operation->mesh->mloop);
+ mp->loopstart = (int)(ml - loops);
mp->totloop = 3;
ml[0].v = r_tris[i][0] + tot_screen_points;
ml[1].v = r_tris[i][1] + tot_screen_points;
@@ -1180,7 +1195,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
/* Write the indices for the lateral triangles. */
for (int i = 0; i < tot_screen_points; i++, mp++, ml += 3) {
- mp->loopstart = (int)(ml - trim_operation->mesh->mloop);
+ mp->loopstart = (int)(ml - loops);
mp->totloop = 3;
int current_index = i;
int next_index = current_index + 1;
@@ -1193,7 +1208,7 @@ static void sculpt_gesture_trim_geometry_generate(SculptGestureContext *sgcontex
}
for (int i = 0; i < tot_screen_points; i++, mp++, ml += 3) {
- mp->loopstart = (int)(ml - trim_operation->mesh->mloop);
+ mp->loopstart = (int)(ml - loops);
mp->totloop = 3;
int current_index = i;
int next_index = current_index + 1;
@@ -1310,8 +1325,7 @@ static void sculpt_gesture_apply_trim(SculptGestureContext *sgcontext)
}),
sculpt_mesh);
BM_mesh_free(bm);
- BKE_mesh_nomain_to_mesh(
- result, sgcontext->vc.obact->data, sgcontext->vc.obact, &CD_MASK_MESH, true);
+ BKE_mesh_nomain_to_mesh(result, sgcontext->vc.obact->data, sgcontext->vc.obact);
}
static void sculpt_gesture_trim_begin(bContext *C, SculptGestureContext *sgcontext)
@@ -1328,8 +1342,9 @@ static void sculpt_gesture_trim_apply_for_symmetry_pass(bContext *UNUSED(C),
{
SculptGestureTrimOperation *trim_operation = (SculptGestureTrimOperation *)sgcontext->operation;
Mesh *trim_mesh = trim_operation->mesh;
+ MVert *verts = BKE_mesh_verts_for_write(trim_mesh);
for (int i = 0; i < trim_mesh->totvert; i++) {
- flip_v3_v3(trim_mesh->mvert[i].co, trim_operation->true_mesh_co[i], sgcontext->symmpass);
+ flip_v3_v3(verts[i].co, trim_operation->true_mesh_co[i], sgcontext->symmpass);
}
sculpt_gesture_trim_normals_update(sgcontext);
sculpt_gesture_apply_trim(sgcontext);
@@ -1437,7 +1452,7 @@ static void project_line_gesture_apply_task_cb(void *__restrict userdata,
}
add_v3_v3(vd.co, disp);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(sgcontext->ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(sgcontext->ss->pbvh, vd.vertex);
}
any_updated = true;
}
@@ -1500,7 +1515,7 @@ static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_mask_properties(sgcontext, op);
- sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_apply(C, sgcontext, op);
sculpt_gesture_context_free(sgcontext);
return OPERATOR_FINISHED;
}
@@ -1512,7 +1527,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_mask_properties(sgcontext, op);
- sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_apply(C, sgcontext, op);
sculpt_gesture_context_free(sgcontext);
return OPERATOR_FINISHED;
}
@@ -1524,7 +1539,7 @@ static int paint_mask_gesture_line_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_mask_properties(sgcontext, op);
- sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_apply(C, sgcontext, op);
sculpt_gesture_context_free(sgcontext);
return OPERATOR_FINISHED;
}
@@ -1536,7 +1551,7 @@ static int face_set_gesture_box_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_face_set_properties(sgcontext, op);
- sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_apply(C, sgcontext, op);
sculpt_gesture_context_free(sgcontext);
return OPERATOR_FINISHED;
}
@@ -1548,7 +1563,7 @@ static int face_set_gesture_lasso_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_face_set_properties(sgcontext, op);
- sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_apply(C, sgcontext, op);
sculpt_gesture_context_free(sgcontext);
return OPERATOR_FINISHED;
}
@@ -1573,7 +1588,7 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op)
}
sculpt_gesture_init_trim_properties(sgcontext, op);
- sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_apply(C, sgcontext, op);
sculpt_gesture_context_free(sgcontext);
return OPERATOR_FINISHED;
}
@@ -1614,7 +1629,7 @@ static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_trim_properties(sgcontext, op);
- sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_apply(C, sgcontext, op);
sculpt_gesture_context_free(sgcontext);
return OPERATOR_FINISHED;
}
@@ -1643,7 +1658,7 @@ static int project_gesture_line_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
sculpt_gesture_init_project_properties(sgcontext, op);
- sculpt_gesture_apply(C, sgcontext);
+ sculpt_gesture_apply(C, sgcontext, op);
sculpt_gesture_context_free(sgcontext);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index ce6b397af15..b78c60e7964 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -361,7 +361,6 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *scene = CTX_data_scene(C);
Paint *paint = BKE_paint_get_active_from_context(C);
- Brush *brush = paint->brush;
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
Palette *palette = paint->palette;
PaletteColor *color;
@@ -369,17 +368,20 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op))
color = BKE_palette_color_add(palette);
palette->active_color = BLI_listbase_count(&palette->colors) - 1;
- if (ELEM(mode,
- PAINT_MODE_TEXTURE_3D,
- PAINT_MODE_TEXTURE_2D,
- PAINT_MODE_VERTEX,
- PAINT_MODE_SCULPT)) {
- copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush));
- color->value = 0.0;
- }
- else if (mode == PAINT_MODE_WEIGHT) {
- zero_v3(color->rgb);
- color->value = brush->weight;
+ if (paint->brush) {
+ const Brush *brush = paint->brush;
+ if (ELEM(mode,
+ PAINT_MODE_TEXTURE_3D,
+ PAINT_MODE_TEXTURE_2D,
+ PAINT_MODE_VERTEX,
+ PAINT_MODE_SCULPT)) {
+ copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush));
+ color->value = 0.0;
+ }
+ else if (mode == PAINT_MODE_WEIGHT) {
+ zero_v3(color->rgb);
+ color->value = brush->weight;
+ }
}
return OPERATOR_FINISHED;
@@ -1454,6 +1456,7 @@ void ED_operatortypes_paint(void)
/* vertex selection */
WM_operatortype_append(PAINT_OT_vert_select_all);
WM_operatortype_append(PAINT_OT_vert_select_ungrouped);
+ WM_operatortype_append(PAINT_OT_vert_select_hide);
/* vertex */
WM_operatortype_append(PAINT_OT_vertex_paint_toggle);
@@ -1472,7 +1475,8 @@ void ED_operatortypes_paint(void)
WM_operatortype_append(PAINT_OT_face_select_linked_pick);
WM_operatortype_append(PAINT_OT_face_select_all);
WM_operatortype_append(PAINT_OT_face_select_hide);
- WM_operatortype_append(PAINT_OT_face_select_reveal);
+
+ WM_operatortype_append(PAINT_OT_face_vert_reveal);
/* partial visibility */
WM_operatortype_append(PAINT_OT_hide_show);
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index 1ee26935dc9..73d52febfc6 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -79,6 +79,8 @@ typedef struct PaintStroke {
float last_mouse_position[2];
float last_world_space_position[3];
+ float last_scene_spacing_delta[3];
+
bool stroke_over_mesh;
/* space distance covered so far */
float stroke_distance;
@@ -120,6 +122,8 @@ typedef struct PaintStroke {
StrokeUpdateStep update_step;
StrokeRedraw redraw;
StrokeDone done;
+
+ bool original; /* Ray-cast original mesh at start of stroke. */
} PaintStroke;
/*** Cursors ***/
@@ -136,7 +140,7 @@ static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata
ARegion *region = stroke->vc.region;
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ubv(paint->paint_cursor_col);
immBegin(GPU_PRIM_LINES, 2);
@@ -164,7 +168,7 @@ static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata)
uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -243,6 +247,11 @@ static bool paint_stroke_use_scene_spacing(Brush *brush, ePaintMode mode)
return false;
}
+static bool paint_tool_raycast_original(Brush *brush, ePaintMode UNUSED(mode))
+{
+ return brush->flag & BRUSH_ANCHORED;
+}
+
static bool paint_tool_require_inbetween_mouse_events(Brush *brush, ePaintMode mode)
{
if (brush->flag & BRUSH_ANCHORED) {
@@ -392,7 +401,7 @@ static bool paint_brush_update(bContext *C,
halfway[1] = dy * 0.5f + stroke->initial_mouse[1];
if (stroke->get_location) {
- if (stroke->get_location(C, r_location, halfway)) {
+ if (stroke->get_location(C, r_location, halfway, stroke->original)) {
hit = true;
location_sampled = true;
location_success = true;
@@ -466,7 +475,7 @@ static bool paint_brush_update(bContext *C,
if (!location_sampled) {
if (stroke->get_location) {
- if (stroke->get_location(C, r_location, mouse)) {
+ if (stroke->get_location(C, r_location, mouse, stroke->original)) {
location_success = true;
*r_location_is_set = true;
}
@@ -550,8 +559,16 @@ static void paint_brush_stroke_add_step(
stroke->last_pressure = pressure;
if (paint_stroke_use_scene_spacing(brush, mode)) {
- SCULPT_stroke_get_location(C, stroke->last_world_space_position, stroke->last_mouse_position);
- mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
+ float world_space_position[3];
+
+ if (SCULPT_stroke_get_location(
+ C, world_space_position, stroke->last_mouse_position, stroke->original)) {
+ copy_v3_v3(stroke->last_world_space_position, world_space_position);
+ mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
+ }
+ else {
+ add_v3_v3(stroke->last_world_space_position, stroke->last_scene_spacing_delta);
+ }
}
if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) {
@@ -698,7 +715,7 @@ static float paint_space_stroke_spacing(bContext *C,
spacing *= stroke->zoom_2d;
if (paint_stroke_use_scene_spacing(brush, mode)) {
- return max_ff(0.001f, size_clamp * spacing / 50.0f);
+ return size_clamp * spacing / 50.0f;
}
return max_ff(stroke->zoom_2d, size_clamp * spacing / 50.0f);
}
@@ -807,7 +824,7 @@ static int paint_space_stroke(bContext *C,
if (use_scene_spacing) {
float world_space_position[3];
- bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse);
+ bool hit = SCULPT_stroke_get_location(C, world_space_position, final_mouse, stroke->original);
mul_m4_v3(stroke->vc.obact->obmat, world_space_position);
if (hit && stroke->stroke_over_mesh) {
sub_v3_v3v3(d_world_space_position, world_space_position, stroke->last_world_space_position);
@@ -838,6 +855,8 @@ static int paint_space_stroke(bContext *C,
stroke->last_world_space_position,
final_world_space_position);
ED_view3d_project_v2(region, final_world_space_position, mouse);
+
+ mul_v3_v3fl(stroke->last_scene_spacing_delta, d_world_space_position, spacing);
}
else {
mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing;
@@ -896,6 +915,8 @@ PaintStroke *paint_stroke_new(bContext *C,
stroke->ups = ups;
stroke->stroke_mode = RNA_enum_get(op->ptr, "mode");
+ stroke->original = paint_tool_raycast_original(br, BKE_paintmode_get_active_from_context(C));
+
get_imapaint_zoom(C, &zoomx, &zoomy);
stroke->zoom_2d = max_ff(zoomx, zoomy);
@@ -1119,7 +1140,7 @@ struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf)
struct wmKeyMap *keymap = WM_modalkeymap_find(keyconf, name);
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (!keymap) {
keymap = WM_modalkeymap_ensure(keyconf, name, modal_items);
}
@@ -1191,8 +1212,10 @@ static void paint_line_strokes_spacing(bContext *C,
copy_v2_v2(stroke->last_mouse_position, old_pos);
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);
+ bool hit_old = SCULPT_stroke_get_location(
+ C, world_space_position_old, old_pos, stroke->original);
+ bool hit_new = SCULPT_stroke_get_location(
+ C, world_space_position_new, new_pos, stroke->original);
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) {
@@ -1336,7 +1359,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
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);
+ C, stroke->last_world_space_position, data + 2 * j, stroke->original);
mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
}
@@ -1468,7 +1491,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
if (paint_stroke_use_scene_spacing(br, mode)) {
stroke->stroke_over_mesh = SCULPT_stroke_get_location(
- C, stroke->last_world_space_position, sample_average.mouse);
+ C, stroke->last_world_space_position, sample_average.mouse, stroke->original);
mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position);
}
stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
@@ -1527,8 +1550,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
paint_stroke_line_constrain(stroke, mouse);
- if (stroke->stroke_started &&
- (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) {
+ if (stroke->stroke_started && (first_modal || ISMOUSE_MOTION(event->type))) {
if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) ||
(br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position);
@@ -1538,7 +1560,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintS
}
else if (first_modal ||
/* regular dabs */
- (!(br->flag & BRUSH_AIRBRUSH) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) ||
+ (!(br->flag & BRUSH_AIRBRUSH) && ISMOUSE_MOTION(event->type)) ||
/* airbrush */
((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER &&
event->customdata == stroke->timer)) {
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index 3f26f590b70..cb981a3bfb1 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -28,7 +28,9 @@
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_image.h"
+#include "BKE_layer.h"
#include "BKE_material.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_paint.h"
#include "BKE_report.h"
@@ -286,9 +288,8 @@ static void imapaint_pick_uv(
const MLoopTri *lt = BKE_mesh_runtime_looptri_ensure(me_eval);
const int tottri = me_eval->runtime.looptris.len;
- const MVert *mvert = me_eval->mvert;
- const MPoly *mpoly = me_eval->mpoly;
- const MLoop *mloop = me_eval->mloop;
+ const MVert *mvert = BKE_mesh_verts(me_eval);
+ const MLoop *mloop = BKE_mesh_loops(me_eval);
const int *index_mp_to_orig = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX);
/* get the needed opengl matrices */
@@ -302,6 +303,9 @@ static void imapaint_pick_uv(
minabsw = 1e10;
uv[0] = uv[1] = 0.0;
+ const int *material_indices = (const int *)CustomData_get_layer_named(
+ &me_eval->pdata, CD_PROP_INT32, "material_index");
+
/* test all faces in the derivedmesh with the original index of the picked face */
/* face means poly here, not triangle, indeed */
for (i = 0; i < tottri; i++, lt++) {
@@ -309,7 +313,6 @@ static void imapaint_pick_uv(
if (findex == faceindex) {
const MLoopUV *mloopuv;
- const MPoly *mp = &mpoly[lt->poly];
const MLoopUV *tri_uv[3];
float tri_co[3][3];
@@ -321,7 +324,8 @@ static void imapaint_pick_uv(
const Material *ma;
const TexPaintSlot *slot;
- ma = BKE_object_material_get(ob_eval, mp->mat_nr + 1);
+ ma = BKE_object_material_get(
+ ob_eval, material_indices == NULL ? 1 : material_indices[lt->poly] + 1);
slot = &ma->texpaintslot[ma->paint_active_slot];
if (!(slot && slot->uvname &&
@@ -400,7 +404,8 @@ void paint_sample_color(
if (v3d && texpaint_proj) {
/* first try getting a color directly from the mesh faces if possible */
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
ImagePaintSettings *imapaint = &scene->toolsettings->imapaint;
bool use_material = (imapaint->mode == IMAGEPAINT_MODE_MATERIAL);
@@ -410,6 +415,8 @@ void paint_sample_color(
cddata_masks.pmask |= CD_MASK_ORIGINDEX;
Mesh *me = (Mesh *)ob->data;
Mesh *me_eval = mesh_get_eval_final(depsgraph, scene, ob_eval, &cddata_masks);
+ const int *material_indices = (const int *)CustomData_get_layer_named(
+ &me_eval->pdata, CD_PROP_INT32, "material_index");
ViewContext vc;
const int mval[2] = {x, y};
@@ -427,8 +434,8 @@ void paint_sample_color(
if (use_material) {
/* Image and texture interpolation from material. */
- MPoly *mp = me_eval->mpoly + faceindex;
- Material *ma = BKE_object_material_get(ob_eval, mp->mat_nr + 1);
+ Material *ma = BKE_object_material_get(
+ ob_eval, material_indices ? material_indices[faceindex] + 1 : 1);
/* Force refresh since paint slots are not updated when changing interpolation. */
BKE_texpaint_slot_refresh_cache(scene, ma, ob);
@@ -697,7 +704,7 @@ static int vert_select_ungrouped_exec(bContext *C, wmOperator *op)
Object *ob = CTX_data_active_object(C);
Mesh *me = ob->data;
- if (BLI_listbase_is_empty(&me->vertex_group_names) || (me->dvert == NULL)) {
+ if (BLI_listbase_is_empty(&me->vertex_group_names) || (BKE_mesh_deform_verts(me) == NULL)) {
BKE_report(op->reports, RPT_ERROR, "No weights/vertex groups on object");
return OPERATOR_CANCELLED;
}
@@ -749,25 +756,68 @@ void PAINT_OT_face_select_hide(wmOperatorType *ot)
ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected objects");
}
-static int face_select_reveal_exec(bContext *C, wmOperator *op)
+static int vert_select_hide_exec(bContext *C, wmOperator *op)
+{
+ const bool unselected = RNA_boolean_get(op->ptr, "unselected");
+ Object *ob = CTX_data_active_object(C);
+ paintvert_hide(C, ob, unselected);
+ ED_region_tag_redraw(CTX_wm_region(C));
+ return OPERATOR_FINISHED;
+}
+
+void PAINT_OT_vert_select_hide(wmOperatorType *ot)
+{
+ ot->name = "Vertex Select Hide";
+ ot->description = "Hide selected vertices";
+ ot->idname = "PAINT_OT_vert_select_hide";
+
+ ot->exec = vert_select_hide_exec;
+ ot->poll = vert_paint_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(
+ ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected vertices");
+}
+
+static int face_vert_reveal_exec(bContext *C, wmOperator *op)
{
const bool select = RNA_boolean_get(op->ptr, "select");
Object *ob = CTX_data_active_object(C);
- paintface_reveal(C, ob, select);
+
+ if (BKE_paint_select_vert_test(ob)) {
+ paintvert_reveal(C, ob, select);
+ }
+ else {
+ paintface_reveal(C, ob, select);
+ }
+
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
-void PAINT_OT_face_select_reveal(wmOperatorType *ot)
+static bool face_vert_reveal_poll(bContext *C)
{
- ot->name = "Face Select Reveal";
- ot->description = "Reveal hidden faces";
- ot->idname = "PAINT_OT_face_select_reveal";
+ Object *ob = CTX_data_active_object(C);
- ot->exec = face_select_reveal_exec;
- ot->poll = facemask_paint_poll;
+ /* Allow using this operator when no selection is enabled but hiding is applied. */
+ return BKE_paint_select_elem_test(ob) || BKE_paint_always_hide_test(ob);
+}
+
+void PAINT_OT_face_vert_reveal(wmOperatorType *ot)
+{
+ ot->name = "Reveal Faces/Vertices";
+ ot->description = "Reveal hidden faces and vertices";
+ ot->idname = "PAINT_OT_face_vert_reveal";
+
+ ot->exec = face_vert_reveal_exec;
+ ot->poll = face_vert_reveal_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_boolean(ot->srna, "select", true, "Select", "");
+ RNA_def_boolean(ot->srna,
+ "select",
+ true,
+ "Select",
+ "Specifies whether the newly revealed geometry should be selected");
}
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.cc b/source/blender/editors/sculpt_paint/paint_vertex.cc
index 6dc8375bb0d..c38a79cb6bb 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.cc
+++ b/source/blender/editors/sculpt_paint/paint_vertex.cc
@@ -773,6 +773,8 @@ struct WeightPaintGroupData {
* paint stroke update - campbell */
struct WeightPaintInfo {
+ MutableSpan<MDeformVert> dvert;
+
int defbase_tot;
/* both must add up to 'defbase_tot' */
@@ -815,7 +817,7 @@ static void do_weight_paint_vertex_single(
float paintweight)
{
Mesh *me = (Mesh *)ob->data;
- MDeformVert *dv = &me->dvert[index];
+ MDeformVert *dv = &wpi->dvert[index];
bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
MDeformWeight *dw;
@@ -875,7 +877,7 @@ static void do_weight_paint_vertex_single(
/* get the mirror def vars */
if (index_mirr != -1) {
- dv_mirr = &me->dvert[index_mirr];
+ dv_mirr = &wpi->dvert[index_mirr];
if (wp->flag & VP_FLAG_VGROUP_RESTRICT) {
dw_mirr = BKE_defvert_find_index(dv_mirr, vgroup_mirr);
@@ -915,9 +917,9 @@ static void do_weight_paint_vertex_single(
if (!brush_use_accumulate(wp)) {
MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev;
- MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index);
+ MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi->dvert.data(), index);
if (index_mirr != -1) {
- defweight_prev_init(dvert_prev, me->dvert, index_mirr);
+ defweight_prev_init(dvert_prev, wpi->dvert.data(), index_mirr);
}
weight_prev = BKE_defvert_find_weight(dv_prev, wpi->active.index);
@@ -1028,7 +1030,7 @@ static void do_weight_paint_vertex_multi(
float paintweight)
{
Mesh *me = (Mesh *)ob->data;
- MDeformVert *dv = &me->dvert[index];
+ MDeformVert *dv = &wpi->dvert[index];
bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
/* mirror vars */
@@ -1044,7 +1046,7 @@ static void do_weight_paint_vertex_multi(
index_mirr = mesh_get_x_mirror_vert(ob, nullptr, index, topology);
if (!ELEM(index_mirr, -1, index)) {
- dv_mirr = &me->dvert[index_mirr];
+ dv_mirr = &wpi->dvert[index_mirr];
}
else {
index_mirr = -1;
@@ -1071,9 +1073,9 @@ static void do_weight_paint_vertex_multi(
if (!brush_use_accumulate(wp)) {
MDeformVert *dvert_prev = ob->sculpt->mode.wpaint.dvert_prev;
- MDeformVert *dv_prev = defweight_prev_init(dvert_prev, me->dvert, index);
+ MDeformVert *dv_prev = defweight_prev_init(dvert_prev, wpi->dvert.data(), index);
if (index_mirr != -1) {
- defweight_prev_init(dvert_prev, me->dvert, index_mirr);
+ defweight_prev_init(dvert_prev, wpi->dvert.data(), index_mirr);
}
oldw = BKE_defvert_multipaint_collective_weight(
@@ -1235,6 +1237,8 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
}
Mesh *me = (Mesh *)ob->data;
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
if (gmap->vert_to_loop == nullptr) {
gmap->vert_map_mem = nullptr;
@@ -1243,15 +1247,15 @@ static void vertex_paint_init_session_data(const ToolSettings *ts, Object *ob)
gmap->vert_to_poly = nullptr;
BKE_mesh_vert_loop_map_create(&gmap->vert_to_loop,
&gmap->vert_map_mem,
- me->mpoly,
- me->mloop,
+ polys.data(),
+ loops.data(),
me->totvert,
me->totpoly,
me->totloop);
BKE_mesh_vert_poly_map_create(&gmap->vert_to_poly,
&gmap->poly_map_mem,
- me->mpoly,
- me->mloop,
+ polys.data(),
+ loops.data(),
me->totvert,
me->totpoly,
me->totloop);
@@ -1900,7 +1904,7 @@ static void do_wpaint_precompute_weight_cb_ex(void *__restrict userdata,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = (SculptThreadedTaskData *)userdata;
- const MDeformVert *dv = &data->me->dvert[n];
+ const MDeformVert *dv = &data->wpi->dvert[n];
data->wpd->precomputed_weight[n] = wpaint_get_active_weight(dv, data->wpi);
}
@@ -1961,10 +1965,9 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
- const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
- vd.vert_indices[vd.i];
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
- const char v_flag = data->me->mvert[v_index].flag;
+ const char v_flag = ss->mvert[v_index].flag;
/* If the vertex is selected */
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
/* Get the average poly weight */
@@ -1972,12 +1975,12 @@ static void do_wpaint_brush_blur_task_cb_ex(void *__restrict userdata,
float weight_final = 0.0f;
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
const int p_index = gmap->vert_to_poly[v_index].indices[j];
- const MPoly *mp = &data->me->mpoly[p_index];
+ const MPoly *mp = &ss->mpoly[p_index];
total_hit_loops += mp->totloop;
for (int k = 0; k < mp->totloop; k++) {
const int l_index = mp->loopstart + k;
- const MLoop *ml = &data->me->mloop[l_index];
+ const MLoop *ml = &ss->mloop[l_index];
weight_final += data->wpd->precomputed_weight[ml->v];
}
}
@@ -2057,10 +2060,9 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
- const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
- vd.vert_indices[vd.i];
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
- const MVert *mv_curr = &data->me->mvert[v_index];
+ const MVert *mv_curr = &ss->mvert[v_index];
/* If the vertex is selected */
if (!(use_face_sel || use_vert_sel) || mv_curr->flag & SELECT) {
@@ -2082,12 +2084,12 @@ static void do_wpaint_brush_smear_task_cb_ex(void *__restrict userdata,
float weight_final = 0.0;
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
const int p_index = gmap->vert_to_poly[v_index].indices[j];
- const MPoly *mp = &data->me->mpoly[p_index];
- const MLoop *ml_other = &data->me->mloop[mp->loopstart];
+ const MPoly *mp = &ss->mpoly[p_index];
+ const MLoop *ml_other = &ss->mloop[mp->loopstart];
for (int k = 0; k < mp->totloop; k++, ml_other++) {
const uint v_other_index = ml_other->v;
if (v_other_index != v_index) {
- const MVert *mv_other = &data->me->mvert[v_other_index];
+ const MVert *mv_other = &ss->mvert[v_other_index];
/* Get the direction from the selected vert to the neighbor. */
float other_dir[3];
@@ -2164,11 +2166,10 @@ static void do_wpaint_brush_draw_task_cb_ex(void *__restrict userdata,
/* NOTE: grids are 1:1 with corners (aka loops).
* For multires, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
- const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
- vd.vert_indices[vd.i];
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
- const char v_flag = data->me->mvert[v_index].flag;
+ const char v_flag = ss->mvert[v_index].flag;
/* If the vertex is selected */
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
float brush_strength = cache->bstrength;
@@ -2232,13 +2233,12 @@ static void do_wpaint_brush_calc_average_weight_cb_ex(
1.0f;
if (angle_cos > 0.0 &&
BKE_brush_curve_strength(data->brush, sqrtf(test.dist), cache->radius) > 0.0) {
- const int v_index = has_grids ? data->me->mloop[vd.grid_indices[vd.g]].v :
- vd.vert_indices[vd.i];
- const char v_flag = data->me->mvert[v_index].flag;
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v : vd.vert_indices[vd.i];
+ const char v_flag = ss->mvert[v_index].flag;
/* If the vertex is selected. */
if (!(use_face_sel || use_vert_sel) || v_flag & SELECT) {
- const MDeformVert *dv = &data->me->dvert[v_index];
+ const MDeformVert *dv = &data->wpi->dvert[v_index];
accum->len += 1;
accum->value += wpaint_get_active_weight(dv, data->wpi);
}
@@ -2510,7 +2510,11 @@ static void wpaint_stroke_update_step(bContext *C,
/* load projection matrix */
mul_m4_m4m4(mat, vc->rv3d->persmat, ob->obmat);
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
+
/* *** setup WeightPaintInfo - pass onto do_weight_paint_vertex *** */
+ wpi.dvert = mesh->deform_verts_for_write();
+
wpi.defbase_tot = wpd->defbase_tot;
wpi.defbase_sel = wpd->defbase_sel;
wpi.defbase_tot_sel = wpd->defbase_tot_sel;
@@ -2532,7 +2536,7 @@ static void wpaint_stroke_update_step(bContext *C,
/* *** done setting up WeightPaintInfo *** */
if (wpd->precomputed_weight) {
- precompute_weight_values(C, ob, brush, wpd, &wpi, (Mesh *)ob->data);
+ precompute_weight_values(C, ob, brush, wpd, &wpi, mesh);
}
wpaint_do_symmetrical_brush_actions(C, ob, wp, sd, wpd, &wpi);
@@ -2545,9 +2549,9 @@ static void wpaint_stroke_update_step(bContext *C,
mul_v3_m4v3(loc_world, ob->obmat, ss->cache->true_location);
paint_last_stroke_update(scene, loc_world);
- BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
+ BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL);
- DEG_id_tag_update((ID *)ob->data, 0);
+ DEG_id_tag_update(&mesh->id, 0);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
swap_m4m4(wpd->vc.rv3d->persmat, mat);
@@ -2980,10 +2984,10 @@ static void do_vpaint_brush_blur_loops(bContext *C,
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
- const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v :
vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
- const MVert *mv = &me->mvert[v_index];
+ const MVert *mv = &ss->mvert[v_index];
/* If the vertex is selected for painting. */
if (!use_vert_sel || mv->flag & SELECT) {
@@ -3006,7 +3010,7 @@ static void do_vpaint_brush_blur_loops(bContext *C,
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
int p_index = gmap->vert_to_poly[v_index].indices[j];
- const MPoly *mp = &me->mpoly[p_index];
+ const MPoly *mp = &ss->mpoly[p_index];
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
total_hit_loops += mp->totloop;
for (int k = 0; k < mp->totloop; k++) {
@@ -3040,8 +3044,8 @@ static void do_vpaint_brush_blur_loops(bContext *C,
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
const int p_index = gmap->vert_to_poly[v_index].indices[j];
const int l_index = gmap->vert_to_loop[v_index].indices[j];
- BLI_assert(me->mloop[l_index].v == v_index);
- const MPoly *mp = &me->mpoly[p_index];
+ BLI_assert(ss->mloop[l_index].v == v_index);
+ const MPoly *mp = &ss->mpoly[p_index];
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */
@@ -3122,10 +3126,10 @@ static void do_vpaint_brush_blur_verts(bContext *C,
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
- const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v :
vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
- const MVert *mv = &me->mvert[v_index];
+ const MVert *mv = &ss->mvert[v_index];
/* If the vertex is selected for painting. */
if (!use_vert_sel || mv->flag & SELECT) {
@@ -3148,12 +3152,12 @@ static void do_vpaint_brush_blur_verts(bContext *C,
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
int p_index = gmap->vert_to_poly[v_index].indices[j];
- const MPoly *mp = &me->mpoly[p_index];
+ const MPoly *mp = &ss->mpoly[p_index];
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
total_hit_loops += mp->totloop;
for (int k = 0; k < mp->totloop; k++) {
const uint l_index = mp->loopstart + k;
- const uint v_index = me->mloop[l_index].v;
+ const uint v_index = ss->mloop[l_index].v;
Color *col = lcol + v_index;
@@ -3184,9 +3188,9 @@ static void do_vpaint_brush_blur_verts(bContext *C,
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
const int p_index = gmap->vert_to_poly[v_index].indices[j];
- BLI_assert(me->mloop[gmap->vert_to_loop[v_index].indices[j]].v == v_index);
+ BLI_assert(ss->mloop[gmap->vert_to_loop[v_index].indices[j]].v == v_index);
- const MPoly *mp = &me->mpoly[p_index];
+ const MPoly *mp = &ss->mpoly[p_index];
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */
@@ -3273,10 +3277,10 @@ static void do_vpaint_brush_smear(bContext *C,
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
/* For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
- const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v :
vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
- const MVert *mv_curr = &me->mvert[v_index];
+ const MVert *mv_curr = &ss->mvert[v_index];
/* if the vertex is selected for painting. */
if (!use_vert_sel || mv_curr->flag & SELECT) {
@@ -3305,15 +3309,15 @@ static void do_vpaint_brush_smear(bContext *C,
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
const int p_index = gmap->vert_to_poly[v_index].indices[j];
const int l_index = gmap->vert_to_loop[v_index].indices[j];
- BLI_assert(me->mloop[l_index].v == v_index);
+ BLI_assert(ss->mloop[l_index].v == v_index);
UNUSED_VARS_NDEBUG(l_index);
- const MPoly *mp = &me->mpoly[p_index];
+ const MPoly *mp = &ss->mpoly[p_index];
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
- const MLoop *ml_other = &me->mloop[mp->loopstart];
+ const MLoop *ml_other = &ss->mloop[mp->loopstart];
for (int k = 0; k < mp->totloop; k++, ml_other++) {
const uint v_other_index = ml_other->v;
if (v_other_index != v_index) {
- const MVert *mv_other = &me->mvert[v_other_index];
+ const MVert *mv_other = &ss->mvert[v_other_index];
/* Get the direction from the
* selected vert to the neighbor. */
@@ -3359,10 +3363,10 @@ static void do_vpaint_brush_smear(bContext *C,
else {
const int l_index = gmap->vert_to_loop[v_index].indices[j];
elem_index = l_index;
- BLI_assert(me->mloop[l_index].v == v_index);
+ BLI_assert(ss->mloop[l_index].v == v_index);
}
- const MPoly *mp = &me->mpoly[p_index];
+ const MPoly *mp = &ss->mpoly[p_index];
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
/* Get the previous element color */
Color color_orig(0, 0, 0, 0); /* unused when array is nullptr */
@@ -3435,11 +3439,11 @@ static void calculate_average_color(VPaintData<Color, Traits, domain> *vpd,
BKE_pbvh_vertex_iter_begin (ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) {
/* Test to see if the vertex coordinates are within the spherical brush region. */
if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v :
vd.vert_indices[vd.i];
if (BKE_brush_curve_strength(brush, 0.0, cache->radius) > 0.0) {
/* If the vertex is selected for painting. */
- const MVert *mv = &me->mvert[v_index];
+ const MVert *mv = &ss->mvert[v_index];
if (!use_vert_sel || mv->flag & SELECT) {
accum2->len += gmap->vert_to_loop[v_index].count;
/* if a vertex is within the brush region, then add its color to the blend. */
@@ -3555,10 +3559,10 @@ static void vpaint_do_draw(bContext *C,
/* NOTE: Grids are 1:1 with corners (aka loops).
* For grid based pbvh, take the vert whose loop corresponds to the current grid.
* Otherwise, take the current vert. */
- const int v_index = has_grids ? me->mloop[vd.grid_indices[vd.g]].v :
+ const int v_index = has_grids ? ss->mloop[vd.grid_indices[vd.g]].v :
vd.vert_indices[vd.i];
const float grid_alpha = has_grids ? 1.0f / vd.gridsize : 1.0f;
- const MVert *mv = &me->mvert[v_index];
+ const MVert *mv = &ss->mvert[v_index];
/* If the vertex is selected for painting. */
if (!use_vert_sel || mv->flag & SELECT) {
@@ -3612,8 +3616,8 @@ static void vpaint_do_draw(bContext *C,
for (int j = 0; j < gmap->vert_to_poly[v_index].count; j++) {
const int p_index = gmap->vert_to_poly[v_index].indices[j];
const int l_index = gmap->vert_to_loop[v_index].indices[j];
- BLI_assert(me->mloop[l_index].v == v_index);
- const MPoly *mp = &me->mpoly[p_index];
+ BLI_assert(ss->mloop[l_index].v == v_index);
+ const MPoly *mp = &ss->mpoly[p_index];
if (!use_face_sel || mp->flag & ME_FACE_SEL) {
Color color_orig = Color(0, 0, 0, 0); /* unused when array is nullptr */
@@ -3957,7 +3961,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
BKE_pbvh_ensure_node_loops(ob->sculpt->pbvh);
}
- SCULPT_undo_push_begin(ob, "Vertex Paint");
+ SCULPT_undo_push_begin_ex(ob, "Vertex Paint");
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
paint_stroke_free(C, op, (PaintStroke *)op->customdata);
@@ -4084,27 +4088,30 @@ static bool vertex_color_set(Object *ob, ColorPaint4f paintcol_in, CustomDataLay
}
else {
Color *color_layer = static_cast<Color *>(layer->data);
+ const Span<MVert> verts = me->verts();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
- const MPoly *mp = me->mpoly;
- for (int i = 0; i < me->totpoly; i++, mp++) {
- if (use_face_sel && !(mp->flag & ME_FACE_SEL)) {
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ if (use_face_sel && !(poly.flag & ME_FACE_SEL)) {
continue;
}
int j = 0;
do {
- uint vidx = me->mloop[mp->loopstart + j].v;
+ uint vidx = loops[poly.loopstart + j].v;
- if (!(use_vert_sel && !(me->mvert[vidx].flag & SELECT))) {
+ if (!(use_vert_sel && !(verts[vidx].flag & SELECT))) {
if constexpr (domain == ATTR_DOMAIN_CORNER) {
- color_layer[mp->loopstart + j] = paintcol;
+ color_layer[poly.loopstart + j] = paintcol;
}
else {
color_layer[vidx] = paintcol;
}
}
j++;
- } while (j < mp->totloop);
+ } while (j < poly.totloop);
}
/* remove stale me->mcol, will be added later */
@@ -4134,7 +4141,7 @@ static bool paint_object_attributes_active_color_fill_ex(Object *ob,
if (!layer) {
return false;
}
- /* Store original #Mesh.editflag.*/
+ /* Store original #Mesh.editflag. */
const decltype(me->editflag) editflag = me->editflag;
if (!only_selected) {
me->editflag &= ~ME_EDIT_PAINT_FACE_SEL;
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
index 8b726c7b942..a8e64462a2a 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
+++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.cc
@@ -50,7 +50,7 @@ static bool vertex_weight_paint_mode_poll(bContext *C)
Object *ob = CTX_data_active_object(C);
Mesh *me = BKE_mesh_from_object(ob);
return (ob && (ELEM(ob->mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT))) &&
- (me && me->totpoly && me->dvert);
+ (me && me->totpoly && !me->deform_verts().is_empty());
}
static void tag_object_after_update(Object *object)
@@ -92,7 +92,7 @@ static bool vertex_paint_from_weight(Object *ob)
return false;
}
- bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me);
+ bke::MutableAttributeAccessor attributes = me->attributes_for_write();
bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name);
if (!color_attribute) {
@@ -159,15 +159,15 @@ static IndexMask get_selected_indices(const Mesh &mesh,
Vector<int64_t> &indices)
{
using namespace blender;
- Span<MVert> verts(mesh.mvert, mesh.totvert);
- Span<MPoly> faces(mesh.mpoly, mesh.totpoly);
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
- bke::AttributeAccessor attributes = bke::mesh_attributes(mesh);
+ bke::AttributeAccessor attributes = mesh.attributes();
if (mesh.editflag & ME_EDIT_PAINT_FACE_SEL) {
const VArray<bool> selection = attributes.adapt_domain(
- VArray<bool>::ForFunc(faces.size(),
- [&](const int i) { return faces[i].flag & ME_FACE_SEL; }),
+ VArray<bool>::ForFunc(polys.size(),
+ [&](const int i) { return polys[i].flag & ME_FACE_SEL; }),
ATTR_DOMAIN_FACE,
domain);
@@ -186,7 +186,7 @@ static IndexMask get_selected_indices(const Mesh &mesh,
return IndexMask(attributes.domain_size(domain));
}
-static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask selection)
+static void face_corner_color_equalize_verts(Mesh &mesh, const IndexMask selection)
{
using namespace blender;
@@ -196,7 +196,7 @@ static void face_corner_color_equalize_vertices(Mesh &mesh, const IndexMask sele
return;
}
- bke::AttributeAccessor attributes = bke::mesh_attributes(mesh);
+ bke::AttributeAccessor attributes = mesh.attributes();
if (attributes.lookup_meta_data(active_color_layer->name)->domain == ATTR_DOMAIN_POINT) {
return;
@@ -221,7 +221,7 @@ static bool vertex_color_smooth(Object *ob)
Vector<int64_t> indices;
const IndexMask selection = get_selected_indices(*me, ATTR_DOMAIN_CORNER, indices);
- face_corner_color_equalize_vertices(*me, selection);
+ face_corner_color_equalize_verts(*me, selection);
tag_object_after_update(ob);
@@ -270,7 +270,7 @@ static bool transform_active_color(Mesh &mesh, const TransformFn &transform_fn)
return false;
}
- bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh);
+ bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
bke::GAttributeWriter color_attribute = attributes.lookup_for_write(active_color_layer->name);
if (!color_attribute) {
@@ -320,7 +320,7 @@ static int vertex_color_brightness_contrast_exec(bContext *C, wmOperator *op)
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
- * Extracted of OpenCV demhist.c
+ * Extracted of OpenCV `demhist.c`.
*/
if (contrast > 0) {
gain = 1.0f - delta * 2.0f;
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 d98660d8939..0a0d7cff214 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c
@@ -168,8 +168,9 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
ED_view3d_viewcontext_init(C, &vc, depsgraph);
me = BKE_mesh_from_object(vc.obact);
+ const MDeformVert *dvert = BKE_mesh_deform_verts(me);
- if (me && me->dvert && vc.v3d && vc.rv3d && (me->vertex_group_active_index != 0)) {
+ if (me && dvert && vc.v3d && vc.rv3d && (me->vertex_group_active_index != 0)) {
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
int v_idx_best = -1;
uint index;
@@ -200,7 +201,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
ToolSettings *ts = vc.scene->toolsettings;
Brush *brush = BKE_paint_brush(&ts->wpaint->paint);
const int vgroup_active = me->vertex_group_active_index - 1;
- float vgroup_weight = BKE_defvert_find_weight(&me->dvert[v_idx_best], vgroup_active);
+ float vgroup_weight = BKE_defvert_find_weight(&dvert[v_idx_best], vgroup_active);
const int defbase_tot = BLI_listbase_count(&me->vertex_group_names);
bool use_lock_relative = ts->wpaint_lock_relative;
bool *defbase_locked = NULL, *defbase_unlocked = NULL;
@@ -232,7 +233,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
bool is_normalized = ts->auto_normalize || use_lock_relative;
vgroup_weight = BKE_defvert_multipaint_collective_weight(
- &me->dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, is_normalized);
+ &dvert[v_idx_best], defbase_tot, defbase_sel, defbase_tot_sel, is_normalized);
}
MEM_freeN(defbase_sel);
@@ -243,7 +244,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even
defbase_tot, defbase_locked, defbase_unlocked, defbase_locked, defbase_unlocked);
vgroup_weight = BKE_defvert_lock_relative_weight(
- vgroup_weight, &me->dvert[v_idx_best], defbase_tot, defbase_locked, defbase_unlocked);
+ vgroup_weight, &dvert[v_idx_best], defbase_tot, defbase_locked, defbase_unlocked);
}
MEM_SAFE_FREE(defbase_locked);
@@ -316,8 +317,11 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C,
ED_view3d_viewcontext_init(C, &vc, depsgraph);
me = BKE_mesh_from_object(vc.obact);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+ const MDeformVert *dverts = BKE_mesh_deform_verts(me);
- if (me && me->dvert && vc.v3d && vc.rv3d && me->vertex_group_names.first) {
+ if (me && dverts && vc.v3d && vc.rv3d && me->vertex_group_names.first) {
const int defbase_tot = BLI_listbase_count(&me->vertex_group_names);
const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0;
int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups");
@@ -334,17 +338,17 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C,
if (use_vert_sel) {
if (ED_mesh_pick_vert(C, vc.obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, true, &index)) {
- MDeformVert *dvert = &me->dvert[index];
+ const MDeformVert *dvert = &dverts[index];
found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
}
}
else {
if (ED_mesh_pick_face(C, vc.obact, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) {
- const MPoly *mp = &me->mpoly[index];
+ const MPoly *mp = &polys[index];
uint fidx = mp->totloop - 1;
do {
- MDeformVert *dvert = &me->dvert[me->mloop[mp->loopstart + fidx].v];
+ const MDeformVert *dvert = &dverts[loops[mp->loopstart + fidx].v];
found |= weight_paint_sample_enum_itemf__helper(dvert, defbase_tot, groups);
} while (fidx--);
}
@@ -441,7 +445,12 @@ static bool weight_paint_set(Object *ob, float paintweight)
/* mutually exclusive, could be made into a */
const short paint_selmode = ME_EDIT_PAINT_SEL_MODE(me);
- if (me->totpoly == 0 || me->dvert == NULL || !me->mpoly) {
+ const MVert *verts = BKE_mesh_verts(me);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(me);
+
+ if (me->totpoly == 0 || dvert == NULL) {
return false;
}
@@ -453,9 +462,9 @@ static bool weight_paint_set(Object *ob, float paintweight)
}
struct WPaintPrev wpp;
- wpaint_prev_create(&wpp, me->dvert, me->totvert);
+ wpaint_prev_create(&wpp, dvert, me->totvert);
- for (index = 0, mp = me->mpoly; index < me->totpoly; index++, mp++) {
+ for (index = 0, mp = polys; index < me->totpoly; index++, mp++) {
uint fidx = mp->totloop - 1;
if ((paint_selmode == SCE_SELECT_FACE) && !(mp->flag & ME_FACE_SEL)) {
@@ -463,14 +472,14 @@ static bool weight_paint_set(Object *ob, float paintweight)
}
do {
- uint vidx = me->mloop[mp->loopstart + fidx].v;
+ uint vidx = loops[mp->loopstart + fidx].v;
- if (!me->dvert[vidx].flag) {
- if ((paint_selmode == SCE_SELECT_VERTEX) && !(me->mvert[vidx].flag & SELECT)) {
+ if (!dvert[vidx].flag) {
+ if ((paint_selmode == SCE_SELECT_VERTEX) && !(verts[vidx].flag & SELECT)) {
continue;
}
- dw = BKE_defvert_ensure_index(&me->dvert[vidx], vgroup_active);
+ dw = BKE_defvert_ensure_index(&dvert[vidx], vgroup_active);
if (dw) {
dw_prev = BKE_defvert_ensure_index(wpp.wpaint_prev + vidx, vgroup_active);
dw_prev->weight = dw->weight; /* set the undo weight */
@@ -482,11 +491,11 @@ static bool weight_paint_set(Object *ob, float paintweight)
if (j >= 0) {
/* copy, not paint again */
if (vgroup_mirror != -1) {
- dw = BKE_defvert_ensure_index(me->dvert + j, vgroup_mirror);
+ dw = BKE_defvert_ensure_index(dvert + j, vgroup_mirror);
dw_prev = BKE_defvert_ensure_index(wpp.wpaint_prev + j, vgroup_mirror);
}
else {
- dw = BKE_defvert_ensure_index(me->dvert + j, vgroup_active);
+ dw = BKE_defvert_ensure_index(dvert + j, vgroup_active);
dw_prev = BKE_defvert_ensure_index(wpp.wpaint_prev + j, vgroup_active);
}
dw_prev->weight = dw->weight; /* set the undo weight */
@@ -494,14 +503,14 @@ static bool weight_paint_set(Object *ob, float paintweight)
}
}
}
- me->dvert[vidx].flag = 1;
+ dvert[vidx].flag = 1;
}
} while (fidx--);
}
{
- MDeformVert *dv = me->dvert;
+ MDeformVert *dv = dvert;
for (index = me->totvert; index != 0; index--, dv++) {
dv->flag = 0;
}
@@ -574,6 +583,7 @@ typedef struct WPGradient_userData {
struct ARegion *region;
Scene *scene;
Mesh *me;
+ MDeformVert *dvert;
Brush *brush;
const float *sco_start; /* [2] */
const float *sco_end; /* [2] */
@@ -593,7 +603,6 @@ typedef struct WPGradient_userData {
static void gradientVert_update(WPGradient_userData *grad_data, int index)
{
- Mesh *me = grad_data->me;
WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
/* Optionally restrict to assigned vertices only. */
@@ -617,7 +626,7 @@ static void gradientVert_update(WPGradient_userData *grad_data, int index)
alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f);
if (alpha != 0.0f) {
- MDeformVert *dv = &me->dvert[index];
+ MDeformVert *dv = &grad_data->dvert[index];
MDeformWeight *dw = BKE_defvert_ensure_index(dv, grad_data->def_nr);
// dw->weight = alpha; // testing
int tool = grad_data->brush->blend;
@@ -631,7 +640,7 @@ static void gradientVert_update(WPGradient_userData *grad_data, int index)
vs->flag |= VGRAD_STORE_IS_MODIFIED;
}
else {
- MDeformVert *dv = &me->dvert[index];
+ MDeformVert *dv = &grad_data->dvert[index];
if (vs->flag & VGRAD_STORE_DW_EXIST) {
/* normally we NULL check, but in this case we know it exists */
MDeformWeight *dw = BKE_defvert_find_index(dv, grad_data->def_nr);
@@ -669,10 +678,9 @@ static void gradientVertInit__mapFunc(void *userData,
const float UNUSED(no[3]))
{
WPGradient_userData *grad_data = userData;
- Mesh *me = grad_data->me;
WPGradient_vertStore *vs = &grad_data->vert_cache->elem[index];
- if (grad_data->use_select && !(me->mvert[index].flag & SELECT)) {
+ if (grad_data->use_select && !(grad_data->dvert[index].flag & SELECT)) {
copy_v2_fl(vs->sco, FLT_MAX);
return;
}
@@ -693,7 +701,7 @@ static void gradientVertInit__mapFunc(void *userData,
return;
}
- MDeformVert *dv = &me->dvert[index];
+ MDeformVert *dv = &grad_data->dvert[index];
const MDeformWeight *dw = BKE_defvert_find_index(dv, grad_data->def_nr);
if (dw) {
vs->weight_orig = dw->weight;
@@ -727,8 +735,9 @@ static int paint_weight_gradient_modal(bContext *C, wmOperator *op, const wmEven
if (vert_cache != NULL) {
Mesh *me = ob->data;
if (vert_cache->wpp.wpaint_prev) {
- BKE_defvert_array_free_elems(me->dvert, me->totvert);
- BKE_defvert_array_copy(me->dvert, vert_cache->wpp.wpaint_prev, me->totvert);
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(me);
+ BKE_defvert_array_free_elems(dvert, me->totvert);
+ BKE_defvert_array_copy(dvert, vert_cache->wpp.wpaint_prev, me->totvert);
wpaint_prev_destroy(&vert_cache->wpp);
}
MEM_freeN(vert_cache);
@@ -753,6 +762,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Object *ob = CTX_data_active_object(C);
Mesh *me = ob->data;
+ MDeformVert *dverts = BKE_mesh_deform_verts_for_write(me);
int x_start = RNA_int_get(op->ptr, "xstart");
int y_start = RNA_int_get(op->ptr, "ystart");
int x_end = RNA_int_get(op->ptr, "xend");
@@ -774,7 +784,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
data.is_init = true;
wpaint_prev_create(
- &((WPGradient_vertStoreBase *)gesture->user_data.data)->wpp, me->dvert, me->totvert);
+ &((WPGradient_vertStoreBase *)gesture->user_data.data)->wpp, dverts, me->totvert);
/* On initialization only, convert face -> vert sel. */
if (me->editflag & ME_EDIT_PAINT_FACE_SEL) {
@@ -797,6 +807,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
data.region = region;
data.scene = scene;
data.me = ob->data;
+ data.dvert = dverts;
data.sco_start = sco_start;
data.sco_end = sco_end;
data.sco_line_div = 1.0f / len_v2v2(sco_start, sco_end);
@@ -851,7 +862,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op)
const int vgroup_num = BLI_listbase_count(&me->vertex_group_names);
bool *vgroup_validmap = BKE_object_defgroup_validmap_get(ob, vgroup_num);
if (vgroup_validmap != NULL) {
- MDeformVert *dvert = me->dvert;
+ MDeformVert *dvert = dverts;
for (int i = 0; i < me->totvert; i++) {
if ((data.vert_cache->elem[i].flag & VGRAD_STORE_IS_MODIFIED) != 0) {
BKE_defvert_normalize_lock_single(&dvert[i], vgroup_validmap, vgroup_num, data.def_nr);
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 5a63af4149a..ac16631f115 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_utils.c
@@ -59,7 +59,7 @@ bool ED_wpaint_ensure_data(bContext *C,
}
/* if nothing was added yet, we make dverts and a vertex deform group */
- if (!me->dvert) {
+ if (BKE_mesh_deform_verts(me) == NULL) {
BKE_object_defgroup_data_create(&me->id);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, me);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 906c4fd35fe..688573d78a6 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -116,28 +116,28 @@ int SCULPT_vertex_count_get(SculptSession *ss)
case PBVH_BMESH:
return BM_mesh_elem_count(BKE_pbvh_get_bmesh(ss->pbvh), BM_VERT);
case PBVH_GRIDS:
- return BKE_pbvh_get_grid_num_vertices(ss->pbvh);
+ return BKE_pbvh_get_grid_num_verts(ss->pbvh);
}
return 0;
}
-const float *SCULPT_vertex_co_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_co_get(SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
if (ss->shapekey_active || ss->deform_modifiers_active) {
const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
- return mverts[index].co;
+ return mverts[vertex.i].co;
}
- return ss->mvert[index].co;
+ return ss->mvert[vertex.i].co;
}
case PBVH_BMESH:
- return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co;
+ return ((BMVert *)vertex.i)->co;
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
+ const int vertex_index = vertex.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index));
}
@@ -158,31 +158,33 @@ bool SCULPT_has_colors(const SculptSession *ss)
return ss->vcol || ss->mcol;
}
-void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4])
+void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4])
{
- BKE_pbvh_vertex_color_get(ss->pbvh, index, r_color);
+ BKE_pbvh_vertex_color_get(ss->pbvh, vertex, r_color);
}
-void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4])
+void SCULPT_vertex_color_set(SculptSession *ss, PBVHVertRef vertex, const float color[4])
{
- BKE_pbvh_vertex_color_set(ss->pbvh, index, color);
+ BKE_pbvh_vertex_color_set(ss->pbvh, vertex, color);
}
-void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
+void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
const float(*vert_normals)[3] = BKE_pbvh_get_vert_normals(ss->pbvh);
- copy_v3_v3(no, vert_normals[index]);
+ copy_v3_v3(no, vert_normals[vertex.i]);
break;
}
- case PBVH_BMESH:
- copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no);
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)vertex.i;
+ copy_v3_v3(no, v->no);
break;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
+ const int vertex_index = vertex.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
copy_v3_v3(no, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index)));
break;
@@ -190,42 +192,43 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3])
}
}
-const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex)
{
- if (ss->persistent_base) {
- return ss->persistent_base[index].co;
+ if (ss->attrs.persistent_co) {
+ return (const float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co);
}
- return SCULPT_vertex_co_get(ss, index);
+
+ return SCULPT_vertex_co_get(ss, vertex);
}
-const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index)
+const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, PBVHVertRef vertex)
{
- /* Always grab active shape key if the sculpt happens on shapekey. */
- if (ss->shapekey_active) {
- const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
- return mverts[index].co;
- }
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
+ /* Always grab active shape key if the sculpt happens on shapekey. */
+ if (ss->shapekey_active) {
+ const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh);
+ return mverts[vertex.i].co;
+ }
- /* Sculpting on the base mesh. */
- if (ss->mvert) {
- return ss->mvert[index].co;
+ /* Sculpting on the base mesh. */
+ return ss->mvert[vertex.i].co;
}
/* Everything else, such as sculpting on multires. */
- return SCULPT_vertex_co_get(ss, index);
+ return SCULPT_vertex_co_get(ss, vertex);
}
-void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3])
+void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, float r_co[3])
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_BMESH:
- copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, vertex));
break;
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
+ const int vertex_index = vertex.i - grid_index * key->grid_area;
SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
@@ -236,30 +239,31 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]
}
}
-void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3])
+void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3])
{
- if (ss->persistent_base) {
- copy_v3_v3(no, ss->persistent_base[index].no);
+ if (ss->attrs.persistent_no) {
+ copy_v3_v3(no, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no));
return;
}
- SCULPT_vertex_normal_get(ss, index, no);
+ SCULPT_vertex_normal_get(ss, vertex, no);
}
-float SCULPT_vertex_mask_get(SculptSession *ss, int index)
+float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex)
{
- BMVert *v;
- float *mask;
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- return ss->vmask[index];
- case PBVH_BMESH:
- v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index);
- mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK));
- return *mask;
+ return ss->vmask ? ss->vmask[vertex.i] : 0.0f;
+ case PBVH_BMESH: {
+ BMVert *v;
+ int cd_mask = CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK);
+
+ v = (BMVert *)vertex.i;
+ return cd_mask != -1 ? BM_ELEM_CD_GET_FLOAT(v, cd_mask) : 0.0f;
+ }
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
+ const int vertex_index = vertex.i - grid_index * key->grid_area;
CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index];
return *CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index));
}
@@ -268,12 +272,13 @@ float SCULPT_vertex_mask_get(SculptSession *ss, int index)
return 0.0f;
}
-int SCULPT_active_vertex_get(SculptSession *ss)
+PBVHVertRef SCULPT_active_vertex_get(SculptSession *ss)
{
if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) {
- return ss->active_vertex_index;
+ return ss->active_vertex;
}
- return 0;
+
+ return BKE_pbvh_make_vref(PBVH_REF_NONE);
}
const float *SCULPT_active_vertex_co_get(SculptSession *ss)
@@ -326,8 +331,14 @@ int SCULPT_active_face_set_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
+ if (!ss->face_sets) {
+ return SCULPT_FACE_SET_NONE;
+ }
return ss->face_sets[ss->active_face_index];
case PBVH_GRIDS: {
+ if (!ss->face_sets) {
+ return SCULPT_FACE_SET_NONE;
+ }
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg,
ss->active_grid_index);
return ss->face_sets[face_index];
@@ -338,32 +349,37 @@ int SCULPT_active_face_set_get(SculptSession *ss)
return SCULPT_FACE_SET_NONE;
}
-void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible)
+void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible)
{
switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- SET_FLAG_FROM_TEST(ss->mvert[index].flag, !visible, ME_HIDE);
- BKE_pbvh_vert_mark_update(ss->pbvh, index);
+ case PBVH_FACES: {
+ bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh);
+ hide_vert[vertex.i] = visible;
break;
- case PBVH_BMESH:
- BM_elem_flag_set(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN, !visible);
+ }
+ case PBVH_BMESH: {
+ BMVert *v = (BMVert *)vertex.i;
+ BM_elem_flag_set(v, BM_ELEM_HIDDEN, !visible);
break;
+ }
case PBVH_GRIDS:
break;
}
}
-bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES:
- return !(ss->mvert[index].flag & ME_HIDE);
+ case PBVH_FACES: {
+ const bool *hide_vert = BKE_pbvh_get_vert_hide(ss->pbvh);
+ return hide_vert == NULL || !hide_vert[vertex.i];
+ }
case PBVH_BMESH:
- return !BM_elem_flag_test(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN);
+ return !BM_elem_flag_test((BMVert *)vertex.i, BM_ELEM_HIDDEN);
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
+ const int vertex_index = vertex.i - grid_index * key->grid_area;
BLI_bitmap **grid_hidden = BKE_pbvh_get_grid_visibility(ss->pbvh);
if (grid_hidden && grid_hidden[grid_index]) {
return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index);
@@ -375,19 +391,16 @@ bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible)
{
+ BLI_assert(ss->face_sets != NULL);
+ BLI_assert(ss->hide_poly != NULL);
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS:
for (int i = 0; i < ss->totfaces; i++) {
- if (abs(ss->face_sets[i]) != face_set) {
+ if (ss->face_sets[i] != face_set) {
continue;
}
- if (visible) {
- ss->face_sets[i] = abs(ss->face_sets[i]);
- }
- else {
- ss->face_sets[i] = -abs(ss->face_sets[i]);
- }
+ ss->hide_poly[i] = !visible;
}
break;
case PBVH_BMESH:
@@ -395,13 +408,15 @@ void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visibl
}
}
-void SCULPT_face_sets_visibility_invert(SculptSession *ss)
+void SCULPT_face_visibility_all_invert(SculptSession *ss)
{
+ BLI_assert(ss->face_sets != NULL);
+ BLI_assert(ss->hide_poly != NULL);
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS:
for (int i = 0; i < ss->totfaces; i++) {
- ss->face_sets[i] *= -1;
+ ss->hide_poly[i] = !ss->hide_poly[i];
}
break;
case PBVH_BMESH:
@@ -409,40 +424,29 @@ void SCULPT_face_sets_visibility_invert(SculptSession *ss)
}
}
-void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
+void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS:
- for (int i = 0; i < ss->totfaces; i++) {
-
- /* This can run on geometry without a face set assigned, so its ID sign can't be changed to
- * modify the visibility. Force that geometry to the ID 1 to enable changing the visibility
- * here. */
- if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
- ss->face_sets[i] = 1;
- }
-
- if (visible) {
- ss->face_sets[i] = abs(ss->face_sets[i]);
- }
- else {
- ss->face_sets[i] = -abs(ss->face_sets[i]);
- }
- }
+ BLI_assert(ss->hide_poly != NULL);
+ memset(ss->hide_poly, !visible, sizeof(bool) * ss->totfaces);
break;
case PBVH_BMESH:
break;
}
}
-bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
- if (ss->face_sets[vert_map->indices[j]] > 0) {
+ if (!ss->hide_poly) {
+ return true;
+ }
+ MeshElemMap *vert_map = &ss->pmap[vertex.i];
+ for (int j = 0; j < ss->pmap[vertex.i].count; j++) {
+ if (!ss->hide_poly[vert_map->indices[j]]) {
return true;
}
}
@@ -456,13 +460,16 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
return true;
}
-bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index)
+bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
- if (ss->face_sets[vert_map->indices[j]] < 0) {
+ if (!ss->hide_poly) {
+ return true;
+ }
+ MeshElemMap *vert_map = &ss->pmap[vertex.i];
+ for (int j = 0; j < ss->pmap[vertex.i].count; j++) {
+ if (ss->hide_poly[vert_map->indices[j]]) {
return false;
}
}
@@ -471,47 +478,61 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index)
case PBVH_BMESH:
return true;
case PBVH_GRIDS: {
+ if (!ss->hide_poly) {
+ return true;
+ }
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
- return ss->face_sets[face_index] > 0;
+ return !ss->hide_poly[face_index];
}
}
return true;
}
-void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
+void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int j = 0; j < ss->pmap[index].count; j++) {
- if (ss->face_sets[vert_map->indices[j]] > 0) {
- ss->face_sets[vert_map->indices[j]] = abs(face_set);
+ BLI_assert(ss->face_sets != NULL);
+ MeshElemMap *vert_map = &ss->pmap[vertex.i];
+ for (int j = 0; j < ss->pmap[vertex.i].count; j++) {
+ const int poly_index = vert_map->indices[j];
+ if (ss->hide_poly && ss->hide_poly[poly_index]) {
+ /* Skip hidden faces connected to the vertex. */
+ continue;
}
+ ss->face_sets[poly_index] = face_set;
}
- } break;
+ break;
+ }
case PBVH_BMESH:
break;
case PBVH_GRIDS: {
+ BLI_assert(ss->face_sets != NULL);
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
- if (ss->face_sets[face_index] > 0) {
- ss->face_sets[face_index] = abs(face_set);
+ if (ss->hide_poly && ss->hide_poly[face_index]) {
+ /* Skip the vertex if it's in a hidden face. */
+ return;
}
-
- } break;
+ ss->face_sets[face_index] = face_set;
+ break;
+ }
}
}
-int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
+int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
+ if (!ss->face_sets) {
+ return SCULPT_FACE_SET_NONE;
+ }
+ MeshElemMap *vert_map = &ss->pmap[vertex.i];
int face_set = 0;
- for (int i = 0; i < ss->pmap[index].count; i++) {
+ for (int i = 0; i < ss->pmap[vertex.i].count; i++) {
if (ss->face_sets[vert_map->indices[i]] > face_set) {
face_set = abs(ss->face_sets[vert_map->indices[i]]);
}
@@ -521,8 +542,11 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
case PBVH_BMESH:
return 0;
case PBVH_GRIDS: {
+ if (!ss->face_sets) {
+ return SCULPT_FACE_SET_NONE;
+ }
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index];
}
@@ -530,12 +554,15 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index)
return 0;
}
-bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
+bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- for (int i = 0; i < ss->pmap[index].count; i++) {
+ if (!ss->face_sets) {
+ return face_set == SCULPT_FACE_SET_NONE;
+ }
+ MeshElemMap *vert_map = &ss->pmap[vertex.i];
+ for (int i = 0; i < ss->pmap[vertex.i].count; i++) {
if (ss->face_sets[vert_map->indices[i]] == face_set) {
return true;
}
@@ -545,8 +572,11 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
case PBVH_BMESH:
return true;
case PBVH_GRIDS: {
+ if (!ss->face_sets) {
+ return face_set == SCULPT_FACE_SET_NONE;
+ }
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index);
return ss->face_sets[face_index] == face_set;
}
@@ -554,18 +584,23 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set)
return true;
}
-void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
+void SCULPT_visibility_sync_all_from_faces(Object *ob)
{
SculptSession *ss = ob->sculpt;
Mesh *mesh = BKE_object_get_original_mesh(ob);
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- BKE_sculpt_sync_face_sets_visibility_to_base_mesh(mesh);
+ /* We may have adjusted the ".hide_poly" attribute, now make the hide status attributes for
+ * vertices and edges consistent. */
+ BKE_mesh_flush_hidden_from_polys(mesh);
+ BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh);
break;
}
case PBVH_GRIDS: {
- BKE_sculpt_sync_face_sets_visibility_to_base_mesh(mesh);
- BKE_sculpt_sync_face_sets_visibility_to_grids(mesh, ss->subdiv_ccg);
+ /* In addition to making the hide status of the base mesh consistent, we also have to
+ * propagate the status to the Multires grids. */
+ BKE_mesh_flush_hidden_from_polys(mesh);
+ BKE_sculpt_sync_face_visibility_to_grids(mesh, ss->subdiv_ccg);
break;
}
case PBVH_BMESH:
@@ -573,54 +608,19 @@ void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob)
}
}
-static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSession *ss,
- int index)
-{
- MeshElemMap *vert_map = &ss->pmap[index];
- const bool visible = SCULPT_vertex_visible_get(ss, index);
- for (int i = 0; i < ss->pmap[index].count; i++) {
- if (visible) {
- ss->face_sets[vert_map->indices[i]] = abs(ss->face_sets[vert_map->indices[i]]);
- }
- else {
- ss->face_sets[vert_map->indices[i]] = -abs(ss->face_sets[vert_map->indices[i]]);
- }
- }
- BKE_pbvh_vert_mark_update(ss->pbvh, index);
-}
-
-void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
-{
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
- for (int i = 0; i < ss->totfaces; i++) {
- MPoly *poly = &ss->mpoly[i];
- bool poly_visible = true;
- for (int l = 0; l < poly->totloop; l++) {
- MLoop *loop = &ss->mloop[poly->loopstart + l];
- if (!SCULPT_vertex_visible_get(ss, (int)loop->v)) {
- poly_visible = false;
- }
- }
- if (poly_visible) {
- ss->face_sets[i] = abs(ss->face_sets[i]);
- }
- else {
- ss->face_sets[i] = -abs(ss->face_sets[i]);
- }
- }
- }
-}
-
static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index)
{
+ if (!ss->face_sets) {
+ return true;
+ }
MeshElemMap *vert_map = &ss->pmap[index];
int face_set = -1;
for (int i = 0; i < ss->pmap[index].count; i++) {
if (face_set == -1) {
- face_set = abs(ss->face_sets[vert_map->indices[i]]);
+ face_set = ss->face_sets[vert_map->indices[i]];
}
else {
- if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) {
+ if (ss->face_sets[vert_map->indices[i]] != face_set) {
return false;
}
}
@@ -637,9 +637,9 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss
MeshElemMap *vert_map = &ss->pmap[v1];
int p1 = -1, p2 = -1;
for (int i = 0; i < ss->pmap[v1].count; i++) {
- MPoly *p = &ss->mpoly[vert_map->indices[i]];
+ const MPoly *p = &ss->mpoly[vert_map->indices[i]];
for (int l = 0; l < p->totloop; l++) {
- MLoop *loop = &ss->mloop[p->loopstart + l];
+ const MLoop *loop = &ss->mloop[p->loopstart + l];
if (loop->v == v2) {
if (p1 == -1) {
p1 = vert_map->indices[i];
@@ -660,18 +660,21 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss
return true;
}
-bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
+bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- return sculpt_check_unique_face_set_in_base_mesh(ss, index);
+ return sculpt_check_unique_face_set_in_base_mesh(ss, vertex.i);
}
case PBVH_BMESH:
return true;
case PBVH_GRIDS: {
+ if (!ss->face_sets) {
+ return true;
+ }
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
+ const int vertex_index = vertex.i - grid_index * key->grid_area;
const SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
.y = vertex_index / key->grid_size};
@@ -696,10 +699,13 @@ int SCULPT_face_set_next_available_get(SculptSession *ss)
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
case PBVH_GRIDS: {
+ if (!ss->face_sets) {
+ return 0;
+ }
int next_face_set = 0;
for (int i = 0; i < ss->totfaces; i++) {
- if (abs(ss->face_sets[i]) > next_face_set) {
- next_face_set = abs(ss->face_sets[i]);
+ if (ss->face_sets[i] > next_face_set) {
+ next_face_set = ss->face_sets[i];
}
}
next_face_set++;
@@ -715,10 +721,12 @@ int SCULPT_face_set_next_available_get(SculptSession *ss)
#define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256
-static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index)
+static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter,
+ PBVHVertRef neighbor,
+ int neighbor_index)
{
for (int i = 0; i < iter->size; i++) {
- if (iter->neighbors[i] == neighbor_index) {
+ if (iter->neighbors[i].i == neighbor.i) {
return;
}
}
@@ -727,63 +735,75 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neigh
iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
if (iter->neighbors == iter->neighbors_fixed) {
- iter->neighbors = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
- memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(int) * iter->size);
+ iter->neighbors = MEM_mallocN(iter->capacity * sizeof(PBVHVertRef), "neighbor array");
+ memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(PBVHVertRef) * iter->size);
}
else {
iter->neighbors = MEM_reallocN_id(
- iter->neighbors, iter->capacity * sizeof(int), "neighbor array");
+ iter->neighbors, iter->capacity * sizeof(PBVHVertRef), "neighbor array");
+ }
+
+ if (iter->neighbor_indices == iter->neighbor_indices_fixed) {
+ iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array");
+ memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size);
+ }
+ else {
+ iter->neighbor_indices = MEM_reallocN_id(
+ iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array");
}
}
- iter->neighbors[iter->size] = neighbor_index;
+ iter->neighbors[iter->size] = neighbor;
+ iter->neighbor_indices[iter->size] = neighbor_index;
iter->size++;
}
-static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss,
- int index,
- SculptVertexNeighborIter *iter)
+static void sculpt_vertex_neighbors_get_bmesh(PBVHVertRef vertex, SculptVertexNeighborIter *iter)
{
- BMVert *v = BM_vert_at_index(ss->bm, index);
+ BMVert *v = (BMVert *)vertex.i;
BMIter liter;
BMLoop *l;
iter->size = 0;
iter->num_duplicates = 0;
iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
iter->neighbors = iter->neighbors_fixed;
+ iter->neighbor_indices = iter->neighbor_indices_fixed;
BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
const BMVert *adj_v[2] = {l->prev->v, l->next->v};
for (int i = 0; i < ARRAY_SIZE(adj_v); i++) {
const BMVert *v_other = adj_v[i];
- if (BM_elem_index_get(v_other) != (int)index) {
- sculpt_vertex_neighbor_add(iter, BM_elem_index_get(v_other));
+ if (v_other != v) {
+ sculpt_vertex_neighbor_add(
+ iter, BKE_pbvh_make_vref((intptr_t)v_other), BM_elem_index_get(v_other));
}
}
}
}
static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
- int index,
+ PBVHVertRef vertex,
SculptVertexNeighborIter *iter)
{
- MeshElemMap *vert_map = &ss->pmap[index];
+ MeshElemMap *vert_map = &ss->pmap[vertex.i];
iter->size = 0;
iter->num_duplicates = 0;
iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
iter->neighbors = iter->neighbors_fixed;
+ iter->neighbor_indices = iter->neighbor_indices_fixed;
+ const bool *hide_poly = BKE_pbvh_get_vert_hide(ss->pbvh);
- for (int i = 0; i < ss->pmap[index].count; i++) {
- if (ss->face_sets[vert_map->indices[i]] < 0) {
+ for (int i = 0; i < ss->pmap[vertex.i].count; i++) {
+ if (hide_poly && hide_poly[vert_map->indices[i]]) {
/* 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) {
+ if (poly_get_adj_loops_from_vert(p, ss->mloop, vertex.i, f_adj_v) != -1) {
for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
- if (f_adj_v[j] != index) {
- sculpt_vertex_neighbor_add(iter, f_adj_v[j]);
+ if (f_adj_v[j] != vertex.i) {
+ sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]);
}
}
}
@@ -791,14 +811,17 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss,
if (ss->fake_neighbors.use_fake_neighbors) {
BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
- if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
- sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) {
+ sculpt_vertex_neighbor_add(
+ iter,
+ BKE_pbvh_make_vref(ss->fake_neighbors.fake_neighbor_index[vertex.i]),
+ ss->fake_neighbors.fake_neighbor_index[vertex.i]);
}
}
}
static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
- const int index,
+ const PBVHVertRef vertex,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
{
@@ -806,8 +829,8 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
* maybe provide coordinate and mask pointers directly rather than converting
* back and forth between #CCGElem and global index. */
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
+ const int vertex_index = vertex.i - grid_index * key->grid_area;
SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
@@ -820,17 +843,20 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
iter->num_duplicates = neighbors.num_duplicates;
iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY;
iter->neighbors = iter->neighbors_fixed;
+ iter->neighbor_indices = iter->neighbor_indices_fixed;
for (int i = 0; i < neighbors.size; i++) {
- sculpt_vertex_neighbor_add(iter,
- neighbors.coords[i].grid_index * key->grid_area +
- neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x);
+ int v = neighbors.coords[i].grid_index * key->grid_area +
+ neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x;
+
+ sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v);
}
if (ss->fake_neighbors.use_fake_neighbors) {
BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL);
- if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) {
- sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]);
+ if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) {
+ int v = ss->fake_neighbors.fake_neighbor_index[vertex.i];
+ sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v);
}
}
@@ -840,19 +866,19 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss,
}
void SCULPT_vertex_neighbors_get(SculptSession *ss,
- const int index,
+ const PBVHVertRef vertex,
const bool include_duplicates,
SculptVertexNeighborIter *iter)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- sculpt_vertex_neighbors_get_faces(ss, index, iter);
+ sculpt_vertex_neighbors_get_faces(ss, vertex, iter);
return;
case PBVH_BMESH:
- sculpt_vertex_neighbors_get_bmesh(ss, index, iter);
+ sculpt_vertex_neighbors_get_bmesh(vertex, iter);
return;
case PBVH_GRIDS:
- sculpt_vertex_neighbors_get_grids(ss, index, include_duplicates, iter);
+ sculpt_vertex_neighbors_get_grids(ss, vertex, include_duplicates, iter);
return;
}
}
@@ -863,24 +889,24 @@ static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, c
return BLI_BITMAP_TEST(ss->vertex_info.boundary, index);
}
-bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index)
+bool SCULPT_vertex_is_boundary(const SculptSession *ss, const PBVHVertRef vertex)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) {
+ if (!SCULPT_vertex_all_faces_visible_get(ss, vertex)) {
return true;
}
- return sculpt_check_boundary_vertex_in_base_mesh(ss, index);
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, vertex.i);
}
case PBVH_BMESH: {
- BMVert *v = BM_vert_at_index(ss->bm, index);
+ BMVert *v = (BMVert *)vertex.i;
return BM_vert_is_boundary(v);
}
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
- const int grid_index = index / key->grid_area;
- const int vertex_index = index - grid_index * key->grid_area;
+ const int grid_index = vertex.i / key->grid_area;
+ const int vertex_index = vertex.i - grid_index * key->grid_area;
const SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
.y = vertex_index / key->grid_size};
@@ -941,7 +967,7 @@ bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3],
}
typedef struct NearestVertexTLSData {
- int nearest_vertex_index;
+ PBVHVertRef nearest_vertex;
float nearest_vertex_distance_squared;
} NearestVertexTLSData;
@@ -958,7 +984,7 @@ static void do_nearest_vertex_get_task_cb(void *__restrict userdata,
float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co);
if (distance_squared < nvtd->nearest_vertex_distance_squared &&
distance_squared < data->max_distance_squared) {
- nvtd->nearest_vertex_index = vd.index;
+ nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared;
}
}
@@ -971,17 +997,17 @@ static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata),
{
NearestVertexTLSData *join = chunk_join;
NearestVertexTLSData *nvtd = chunk;
- if (join->nearest_vertex_index == -1) {
- join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ if (join->nearest_vertex.i == PBVH_REF_NONE) {
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
- join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
-int SCULPT_nearest_vertex_get(
+PBVHVertRef SCULPT_nearest_vertex_get(
Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original)
{
SculptSession *ss = ob->sculpt;
@@ -996,7 +1022,7 @@ int SCULPT_nearest_vertex_get(
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
if (totnode == 0) {
- return -1;
+ return BKE_pbvh_make_vref(PBVH_REF_NONE);
}
SculptThreadedTaskData task_data = {
@@ -1008,7 +1034,7 @@ int SCULPT_nearest_vertex_get(
copy_v3_v3(task_data.nearest_vertex_search_co, co);
NearestVertexTLSData nvtd;
- nvtd.nearest_vertex_index = -1;
+ nvtd.nearest_vertex.i = PBVH_REF_NONE;
nvtd.nearest_vertex_distance_squared = FLT_MAX;
TaskParallelSettings settings;
@@ -1020,7 +1046,7 @@ int SCULPT_nearest_vertex_get(
MEM_SAFE_FREE(nodes);
- return nvtd.nearest_vertex_index;
+ return nvtd.nearest_vertex;
}
bool SCULPT_is_symmetry_iteration_valid(char i, char symm)
@@ -1075,23 +1101,27 @@ void SCULPT_floodfill_init(SculptSession *ss, SculptFloodFill *flood)
int vertex_count = SCULPT_vertex_count_get(ss);
SCULPT_vertex_random_access_ensure(ss);
- flood->queue = BLI_gsqueue_new(sizeof(int));
- flood->visited_vertices = BLI_BITMAP_NEW(vertex_count, "visited vertices");
+ flood->queue = BLI_gsqueue_new(sizeof(intptr_t));
+ flood->visited_verts = BLI_BITMAP_NEW(vertex_count, "visited verts");
}
-void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index)
+void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex)
{
- BLI_gsqueue_push(flood->queue, &index);
+ BLI_gsqueue_push(flood->queue, &vertex);
}
-void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index)
+void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex)
{
- BLI_gsqueue_push(flood->queue, &index);
- BLI_BITMAP_ENABLE(flood->visited_vertices, index);
+ BLI_gsqueue_push(flood->queue, &vertex);
+ BLI_BITMAP_ENABLE(flood->visited_verts, vertex.i);
}
-void SCULPT_floodfill_add_initial_with_symmetry(
- Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius)
+void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd,
+ Object *ob,
+ SculptSession *ss,
+ SculptFloodFill *flood,
+ PBVHVertRef vertex,
+ float radius)
{
/* Add active vertex and symmetric vertices to the queue. */
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
@@ -1099,18 +1129,19 @@ void SCULPT_floodfill_add_initial_with_symmetry(
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
- int v = -1;
+ PBVHVertRef v = {PBVH_REF_NONE};
+
if (i == 0) {
- v = index;
+ v = vertex;
}
else if (radius > 0.0f) {
float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius;
float location[3];
- flip_v3_v3(location, SCULPT_vertex_co_get(ss, index), i);
+ flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i);
v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false);
}
- if (v != -1) {
+ if (v.i != PBVH_REF_NONE) {
SCULPT_floodfill_add_initial(flood, v);
}
}
@@ -1125,7 +1156,9 @@ void SCULPT_floodfill_add_active(
if (!SCULPT_is_symmetry_iteration_valid(i, symm)) {
continue;
}
- int v = -1;
+
+ PBVHVertRef v = {PBVH_REF_NONE};
+
if (i == 0) {
v = SCULPT_active_vertex_get(ss);
}
@@ -1135,26 +1168,31 @@ void SCULPT_floodfill_add_active(
v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false);
}
- if (v != -1) {
+ if (v.i != PBVH_REF_NONE) {
SCULPT_floodfill_add_initial(flood, v);
}
}
}
-void SCULPT_floodfill_execute(
- SculptSession *ss,
- SculptFloodFill *flood,
- bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata),
- void *userdata)
+void SCULPT_floodfill_execute(SculptSession *ss,
+ SculptFloodFill *flood,
+ bool (*func)(SculptSession *ss,
+ PBVHVertRef from_v,
+ PBVHVertRef to_v,
+ bool is_duplicate,
+ void *userdata),
+ void *userdata)
{
while (!BLI_gsqueue_is_empty(flood->queue)) {
- int from_v;
+ PBVHVertRef from_v;
+
BLI_gsqueue_pop(flood->queue, &from_v);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
- const int to_v = ni.index;
+ const PBVHVertRef to_v = ni.vertex;
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
- if (BLI_BITMAP_TEST(flood->visited_vertices, to_v)) {
+ if (BLI_BITMAP_TEST(flood->visited_verts, to_v_i)) {
continue;
}
@@ -1162,7 +1200,7 @@ void SCULPT_floodfill_execute(
continue;
}
- BLI_BITMAP_ENABLE(flood->visited_vertices, to_v);
+ BLI_BITMAP_ENABLE(flood->visited_verts, BKE_pbvh_vertex_to_index(ss->pbvh, to_v));
if (func(ss, from_v, to_v, ni.is_duplicate, userdata)) {
BLI_gsqueue_push(flood->queue, &to_v);
@@ -1174,7 +1212,7 @@ void SCULPT_floodfill_execute(
void SCULPT_floodfill_free(SculptFloodFill *flood)
{
- MEM_SAFE_FREE(flood->visited_vertices);
+ MEM_SAFE_FREE(flood->visited_verts);
BLI_gsqueue_free(flood->queue);
flood->queue = NULL;
}
@@ -1368,16 +1406,13 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
switch (data->brush->sculpt_tool) {
case SCULPT_TOOL_MASK:
type = SCULPT_UNDO_MASK;
- BKE_pbvh_node_mark_update_mask(data->nodes[n]);
break;
case SCULPT_TOOL_PAINT:
case SCULPT_TOOL_SMEAR:
type = SCULPT_UNDO_COLOR;
- BKE_pbvh_node_mark_update_color(data->nodes[n]);
break;
default:
type = SCULPT_UNDO_COORDS;
- BKE_pbvh_node_mark_update(data->nodes[n]);
break;
}
@@ -1392,6 +1427,20 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
return;
}
+ switch (type) {
+ case SCULPT_UNDO_MASK:
+ BKE_pbvh_node_mark_update_mask(data->nodes[n]);
+ break;
+ case SCULPT_UNDO_COLOR:
+ BKE_pbvh_node_mark_update_color(data->nodes[n]);
+ break;
+ case SCULPT_UNDO_COORDS:
+ BKE_pbvh_node_mark_update(data->nodes[n]);
+ break;
+ default:
+ break;
+ }
+
PBVHVertexIter vd;
SculptOrigVertData orig_data;
@@ -1408,16 +1457,15 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata,
else {
copy_v3_v3(vd.fno, orig_data.no);
}
+ if (vd.mvert) {
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
+ }
}
else if (orig_data.unode->type == SCULPT_UNDO_MASK) {
*vd.mask = orig_data.mask;
}
else if (orig_data.unode->type == SCULPT_UNDO_COLOR) {
- SCULPT_vertex_color_set(ss, vd.index, orig_data.col);
- }
-
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ SCULPT_vertex_color_set(ss, vd.vertex, orig_data.col);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2361,12 +2409,12 @@ static float brush_strength(const Sculpt *sd,
float SCULPT_brush_strength_factor(SculptSession *ss,
const Brush *br,
const float brush_point[3],
- const float len,
+ float len,
const float vno[3],
const float fno[3],
- const float mask,
- const int vertex_index,
- const int thread_id)
+ float mask,
+ const PBVHVertRef vertex,
+ int thread_id)
{
StrokeCache *cache = ss->cache;
const Scene *scene = cache->vc->scene;
@@ -2450,7 +2498,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss,
avg *= 1.0f - mask;
/* Auto-masking. */
- avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex_index);
+ avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex);
return avg;
}
@@ -2819,7 +2867,7 @@ typedef struct {
float depth;
bool original;
- int active_vertex_index;
+ PBVHVertRef active_vertex;
float *face_normal;
int active_face_grid_index;
@@ -3039,13 +3087,13 @@ static void do_gravity_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -3114,10 +3162,10 @@ void SCULPT_vertcos_to_key(Object *ob, KeyBlock *kb, const float (*vertCos)[3])
/* Modifying of basis key should update mesh. */
if (kb == me->key->refkey) {
- MVert *mvert = me->mvert;
+ MVert *verts = BKE_mesh_verts_for_write(me);
- for (a = 0; a < me->totvert; a++, mvert++) {
- copy_v3_v3(mvert->co, vertCos[a]);
+ for (a = 0; a < me->totvert; a++) {
+ copy_v3_v3(verts[a].co, vertCos[a]);
}
BKE_mesh_tag_coords_changed(me);
}
@@ -3362,7 +3410,7 @@ static void do_brush_action(Sculpt *sd,
if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) {
if (!ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss, 1.0f, 0.0f, 0.0f, false, true);
+ ob, 1.0f, 0.0f, 0.0f, false, true);
SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim);
}
SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim);
@@ -3541,8 +3589,9 @@ static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd)
copy_v3_v3(ss->deform_cos[index], vd->co);
copy_v3_v3(ss->orig_cos[index], newco);
+ MVert *verts = BKE_mesh_verts_for_write(me);
if (!ss->shapekey_active) {
- copy_v3_v3(me->mvert[index].co, newco);
+ copy_v3_v3(verts[index].co, newco);
}
}
@@ -4744,7 +4793,7 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin)
srd->ray_normal,
&srd->isect_precalc,
&srd->depth,
- &srd->active_vertex_index,
+ &srd->active_vertex,
&srd->active_face_grid_index,
srd->face_normal)) {
srd->hit = true;
@@ -4878,7 +4927,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
}
/* Update the active vertex of the SculptSession. */
- ss->active_vertex_index = srd.active_vertex_index;
+ ss->active_vertex = srd.active_vertex;
SCULPT_vertex_random_access_ensure(ss);
copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss));
@@ -4952,7 +5001,10 @@ bool SCULPT_cursor_geometry_info_update(bContext *C,
return true;
}
-bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2])
+bool SCULPT_stroke_get_location(bContext *C,
+ float out[3],
+ const float mval[2],
+ bool force_original)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob;
@@ -4968,7 +5020,7 @@ bool SCULPT_stroke_get_location(bContext *C, float out[3], const float mval[2])
ss = ob->sculpt;
cache = ss->cache;
- original = (cache) ? cache->original : false;
+ original = force_original || ((cache) ? cache->original : false);
const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
@@ -5263,6 +5315,8 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateColor);
}
+ BKE_sculpt_attributes_destroy_temporary_stroke(ob);
+
if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
BKE_pbvh_bmesh_after_stroke(ss->pbvh);
}
@@ -5284,7 +5338,7 @@ void SCULPT_flush_update_done(const bContext *C, Object *ob, SculptUpdateType up
static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), const float mval[2])
{
float co_dummy[3];
- return SCULPT_stroke_get_location(C, co_dummy, mval);
+ return SCULPT_stroke_get_location(C, co_dummy, mval, false);
}
bool SCULPT_handles_colors_report(SculptSession *ss, ReportList *reports)
@@ -5342,7 +5396,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
ED_image_undo_push_begin(op->type->name, PAINT_MODE_SCULPT);
}
else {
- SCULPT_undo_push_begin(ob, sculpt_tool_name(sd));
+ SCULPT_undo_push_begin_ex(ob, sculpt_tool_name(sd));
}
return true;
@@ -5352,7 +5406,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
static void sculpt_stroke_update_step(bContext *C,
wmOperator *UNUSED(op),
- struct PaintStroke *UNUSED(stroke),
+ struct PaintStroke *stroke,
PointerRNA *itemptr)
{
UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
@@ -5361,6 +5415,8 @@ static void sculpt_stroke_update_step(bContext *C,
SculptSession *ss = ob->sculpt;
const Brush *brush = BKE_paint_brush(&sd->paint);
ToolSettings *tool_settings = CTX_data_tool_settings(C);
+ StrokeCache *cache = ss->cache;
+ cache->stroke_distance = paint_stroke_distance_get(stroke);
SCULPT_stroke_modifiers_check(C, ob, brush);
sculpt_update_cache_variants(C, sd, ob, itemptr);
@@ -5499,17 +5555,39 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent
struct PaintStroke *stroke;
int ignore_background_click;
int retval;
+ Object *ob = CTX_data_active_object(C);
+
+ /* Test that ob is visible; otherwise we won't be able to get evaluated data
+ * from the depsgraph. We do this here instead of SCULPT_mode_poll
+ * to avoid falling through to the translate operator in the
+ * global view3d keymap.
+ *
+ * NOTE: #BKE_object_is_visible_in_viewport is not working here (it returns false
+ * if the object is in local view); instead, test for OB_HIDE_VIEWPORT directly.
+ */
+
+ if (ob->visibility_flag & OB_HIDE_VIEWPORT) {
+ return OPERATOR_CANCELLED;
+ }
sculpt_brush_stroke_init(C, op);
- Object *ob = CTX_data_active_object(C);
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
if (SCULPT_tool_is_paint(brush->sculpt_tool) &&
!SCULPT_handles_colors_report(ob->sculpt, op->reports)) {
return OPERATOR_CANCELLED;
}
+ if (SCULPT_tool_is_mask(brush->sculpt_tool)) {
+ MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob);
+ BKE_sculpt_mask_layers_ensure(ob, mmd);
+ }
+ if (SCULPT_tool_is_face_sets(brush->sculpt_tool)) {
+ Mesh *mesh = BKE_object_get_original_mesh(ob);
+ ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
+ }
stroke = paint_stroke_new(C,
op,
@@ -5590,6 +5668,10 @@ static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent
return paint_stroke_modal(C, op, event, (struct PaintStroke **)&op->customdata);
}
+static void sculpt_redo_empty_ui(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+}
+
void SCULPT_OT_brush_stroke(wmOperatorType *ot)
{
/* Identifiers. */
@@ -5603,9 +5685,10 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot)
ot->exec = sculpt_brush_stroke_exec;
ot->poll = SCULPT_poll;
ot->cancel = sculpt_brush_stroke_cancel;
+ ot->ui = sculpt_redo_empty_ui;
/* Flags (sculpt does own undo? (ton)). */
- ot->flag = OPTYPE_BLOCKING;
+ ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO;
/* Properties. */
@@ -5645,10 +5728,10 @@ enum {
SCULPT_TOPOLOGY_ID_DEFAULT,
};
-static int SCULPT_vertex_get_connected_component(SculptSession *ss, int index)
+static int SCULPT_vertex_get_connected_component(SculptSession *ss, PBVHVertRef vertex)
{
if (ss->vertex_info.connected_component) {
- return ss->vertex_info.connected_component[index];
+ return ss->vertex_info.connected_component[vertex.i];
}
return SCULPT_TOPOLOGY_ID_DEFAULT;
}
@@ -5665,8 +5748,11 @@ static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist)
ss->fake_neighbors.current_max_distance = max_dist;
}
-static void SCULPT_fake_neighbor_add(SculptSession *ss, int v_index_a, int v_index_b)
+static void SCULPT_fake_neighbor_add(SculptSession *ss, PBVHVertRef v_a, PBVHVertRef v_b)
{
+ int v_index_a = BKE_pbvh_vertex_to_index(ss->pbvh, v_a);
+ int v_index_b = BKE_pbvh_vertex_to_index(ss->pbvh, v_b);
+
if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) {
ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b;
ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a;
@@ -5679,7 +5765,7 @@ static void sculpt_pose_fake_neighbors_free(SculptSession *ss)
}
typedef struct NearestVertexFakeNeighborTLSData {
- int nearest_vertex_index;
+ PBVHVertRef nearest_vertex;
float nearest_vertex_distance_squared;
int current_topology_id;
} NearestVertexFakeNeighborTLSData;
@@ -5694,13 +5780,13 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index);
+ int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.vertex);
if (vd_topology_id != nvtd->current_topology_id &&
ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) {
float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co);
if (distance_squared < nvtd->nearest_vertex_distance_squared &&
distance_squared < data->max_distance_squared) {
- nvtd->nearest_vertex_index = vd.index;
+ nvtd->nearest_vertex = vd.vertex;
nvtd->nearest_vertex_distance_squared = distance_squared;
}
}
@@ -5714,17 +5800,20 @@ static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata),
{
NearestVertexFakeNeighborTLSData *join = chunk_join;
NearestVertexFakeNeighborTLSData *nvtd = chunk;
- if (join->nearest_vertex_index == -1) {
- join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ if (join->nearest_vertex.i == PBVH_REF_NONE) {
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) {
- join->nearest_vertex_index = nvtd->nearest_vertex_index;
+ join->nearest_vertex = nvtd->nearest_vertex;
join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared;
}
}
-static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, float max_distance)
+static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd,
+ Object *ob,
+ const PBVHVertRef vertex,
+ float max_distance)
{
SculptSession *ss = ob->sculpt;
PBVHNode **nodes = NULL;
@@ -5734,12 +5823,12 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
.sd = sd,
.radius_squared = max_distance * max_distance,
.original = false,
- .center = SCULPT_vertex_co_get(ss, index),
+ .center = SCULPT_vertex_co_get(ss, vertex),
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
if (totnode == 0) {
- return -1;
+ return BKE_pbvh_make_vref(PBVH_REF_NONE);
}
SculptThreadedTaskData task_data = {
@@ -5749,12 +5838,12 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
.max_distance_squared = max_distance * max_distance,
};
- copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, vertex));
NearestVertexFakeNeighborTLSData nvtd;
- nvtd.nearest_vertex_index = -1;
+ nvtd.nearest_vertex.i = -1;
nvtd.nearest_vertex_distance_squared = FLT_MAX;
- nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, index);
+ nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, vertex);
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
@@ -5765,19 +5854,26 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index,
MEM_SAFE_FREE(nodes);
- return nvtd.nearest_vertex_index;
+ return nvtd.nearest_vertex;
}
typedef struct SculptTopologyIDFloodFillData {
int next_id;
} SculptTopologyIDFloodFillData;
-static bool SCULPT_connected_components_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool SCULPT_connected_components_floodfill_cb(SculptSession *ss,
+ PBVHVertRef from_v,
+ PBVHVertRef to_v,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
SculptTopologyIDFloodFillData *data = userdata;
- ss->vertex_info.connected_component[from_v] = data->next_id;
- ss->vertex_info.connected_component[to_v] = data->next_id;
+
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
+ ss->vertex_info.connected_component[from_v_i] = data->next_id;
+ ss->vertex_info.connected_component[to_v_i] = data->next_id;
return true;
}
@@ -5801,10 +5897,12 @@ void SCULPT_connected_components_ensure(Object *ob)
int next_id = 0;
for (int i = 0; i < totvert; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
if (ss->vertex_info.connected_component[i] == SCULPT_TOPOLOGY_ID_NONE) {
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
- SCULPT_floodfill_add_initial(&flood, i);
+ SCULPT_floodfill_add_initial(&flood, vertex);
SculptTopologyIDFloodFillData data;
data.next_id = next_id;
SCULPT_floodfill_execute(ss, &flood, SCULPT_connected_components_floodfill_cb, &data);
@@ -5822,21 +5920,25 @@ void SCULPT_boundary_info_ensure(Object *object)
}
Mesh *base_mesh = BKE_mesh_from_object(object);
+ const MEdge *edges = BKE_mesh_edges(base_mesh);
+ const MPoly *polys = BKE_mesh_polys(base_mesh);
+ const MLoop *loops = BKE_mesh_loops(base_mesh);
+
ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info");
int *adjacent_faces_edge_count = MEM_calloc_arrayN(
base_mesh->totedge, sizeof(int), "Adjacent face edge count");
for (int p = 0; p < base_mesh->totpoly; p++) {
- MPoly *poly = &base_mesh->mpoly[p];
+ const MPoly *poly = &polys[p];
for (int l = 0; l < poly->totloop; l++) {
- MLoop *loop = &base_mesh->mloop[l + poly->loopstart];
+ const MLoop *loop = &loops[l + poly->loopstart];
adjacent_faces_edge_count[loop->e]++;
}
}
for (int e = 0; e < base_mesh->totedge; e++) {
if (adjacent_faces_edge_count[e] < 2) {
- MEdge *edge = &base_mesh->medge[e];
+ const MEdge *edge = &edges[e];
BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true);
BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true);
}
@@ -5862,12 +5964,12 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
SCULPT_fake_neighbor_init(ss, max_dist);
for (int i = 0; i < totvert; i++) {
- const int from_v = i;
+ const PBVHVertRef from_v = BKE_pbvh_index_to_vertex(ss->pbvh, i);
/* This vertex does not have a fake neighbor yet, search one for it. */
- if (ss->fake_neighbors.fake_neighbor_index[from_v] == FAKE_NEIGHBOR_NONE) {
- const int to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist);
- if (to_v != -1) {
+ if (ss->fake_neighbors.fake_neighbor_index[i] == FAKE_NEIGHBOR_NONE) {
+ const PBVHVertRef to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist);
+ if (to_v.i != PBVH_REF_NONE) {
/* Add the fake neighbor if available. */
SCULPT_fake_neighbor_add(ss, from_v, to_v);
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc
index bb101717c9b..f4da70faad7 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc
@@ -114,16 +114,19 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush
return false;
}
-float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert)
+float SCULPT_automasking_factor_get(AutomaskingCache *automasking,
+ SculptSession *ss,
+ PBVHVertRef vert)
{
if (!automasking) {
return 1.0f;
}
+
/* If the cache is initialized with valid info, use the cache. This is used when the
* automasking information can't be computed in real time per vertex and needs to be
* initialized for the whole mesh when the stroke starts. */
- if (automasking->factor) {
- return automasking->factor[vert];
+ if (ss->attrs.automasking_factor) {
+ return *(float *)SCULPT_vertex_attr_get(vert, ss->attrs.automasking_factor);
}
if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
@@ -153,7 +156,6 @@ void SCULPT_automasking_cache_free(AutomaskingCache *automasking)
return;
}
- MEM_SAFE_FREE(automasking->factor);
MEM_SAFE_FREE(automasking);
}
@@ -171,38 +173,42 @@ static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
}
struct AutomaskFloodFillData {
- float *automask_factor;
float radius;
bool use_radius;
float location[3];
char symm;
};
-static bool automask_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool automask_floodfill_cb(SculptSession *ss,
+ PBVHVertRef from_v,
+ PBVHVertRef to_v,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata;
- data->automask_factor[to_v] = 1.0f;
- data->automask_factor[from_v] = 1.0f;
+ *(float *)SCULPT_vertex_attr_get(to_v, ss->attrs.automasking_factor) = 1.0f;
+ *(float *)SCULPT_vertex_attr_get(from_v, ss->attrs.automasking_factor) = 1.0f;
return (!data->use_radius ||
SCULPT_is_vertex_inside_brush_radius_symm(
SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm));
}
-static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+static void SCULPT_topology_automasking_init(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "Topology masking: pmap missing");
- return nullptr;
+ return;
}
const int totvert = SCULPT_vertex_count_get(ss);
for (int i : IndexRange(totvert)) {
- automask_factor[i] = 0.0f;
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f;
}
/* Flood fill automask to connected vertices. Limited to vertices inside
@@ -212,9 +218,8 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
const float radius = ss->cache ? ss->cache->radius : FLT_MAX;
SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
- AutomaskFloodFillData fdata = {nullptr};
+ AutomaskFloodFillData fdata = {0};
- fdata.automask_factor = automask_factor;
fdata.radius = radius;
fdata.use_radius = ss->cache && sculpt_automasking_is_constrained_by_radius(brush);
fdata.symm = SCULPT_mesh_symmetry_xyz_get(ob);
@@ -222,62 +227,61 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
copy_v3_v3(fdata.location, SCULPT_active_vertex_co_get(ss));
SCULPT_floodfill_execute(ss, &flood, automask_floodfill_cb, &fdata);
SCULPT_floodfill_free(&flood);
-
- return automask_factor;
}
-static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+static void sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
if (!SCULPT_is_automasking_enabled(sd, ss, brush)) {
- return nullptr;
+ return;
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
BLI_assert_msg(0, "Face Sets automasking: pmap missing");
- return nullptr;
+ return;
}
int tot_vert = SCULPT_vertex_count_get(ss);
int active_face_set = SCULPT_active_face_set_get(ss);
for (int i : IndexRange(tot_vert)) {
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
- automask_factor[i] *= 0.0f;
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) {
+ *(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor) = 0.0f;
}
}
-
- return automask_factor;
}
#define EDGE_DISTANCE_INF -1
-float *SCULPT_boundary_automasking_init(Object *ob,
- eBoundaryAutomaskMode mode,
- int propagation_steps,
- float *automask_factor)
+static void SCULPT_boundary_automasking_init(Object *ob,
+ eBoundaryAutomaskMode mode,
+ int propagation_steps)
{
SculptSession *ss = ob->sculpt;
if (!ss->pmap) {
BLI_assert_msg(0, "Boundary Edges masking: pmap missing");
- return nullptr;
+ return;
}
const int totvert = SCULPT_vertex_count_get(ss);
int *edge_distance = (int *)MEM_callocN(sizeof(int) * totvert, "automask_factor");
for (int i : IndexRange(totvert)) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
edge_distance[i] = EDGE_DISTANCE_INF;
switch (mode) {
case AUTOMASK_INIT_BOUNDARY_EDGES:
- if (SCULPT_vertex_is_boundary(ss, i)) {
+ if (SCULPT_vertex_is_boundary(ss, vertex)) {
edge_distance[i] = 0;
}
break;
case AUTOMASK_INIT_BOUNDARY_FACE_SETS:
- if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
+ if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) {
edge_distance[i] = 0;
}
break;
@@ -286,11 +290,13 @@ float *SCULPT_boundary_automasking_init(Object *ob,
for (int propagation_it : IndexRange(propagation_steps)) {
for (int i : IndexRange(totvert)) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
if (edge_distance[i] != EDGE_DISTANCE_INF) {
continue;
}
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
if (edge_distance[ni.index] == propagation_it) {
edge_distance[i] = propagation_it + 1;
}
@@ -300,16 +306,19 @@ float *SCULPT_boundary_automasking_init(Object *ob,
}
for (int i : IndexRange(totvert)) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
if (edge_distance[i] == EDGE_DISTANCE_INF) {
continue;
}
const float p = 1.0f - ((float)edge_distance[i] / (float)propagation_steps);
const float edge_boundary_automask = pow2f(p);
- automask_factor[i] *= (1.0f - edge_boundary_automask);
+
+ *(float *)SCULPT_vertex_attr_get(
+ vertex, ss->attrs.automasking_factor) *= (1.0f - edge_boundary_automask);
}
MEM_SAFE_FREE(edge_distance);
- return automask_factor;
}
static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automasking,
@@ -339,9 +348,16 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
return automasking;
}
- automasking->factor = (float *)MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
+ SculptAttributeParams params = {0};
+ params.stroke_only = true;
+
+ ss->attrs.automasking_factor = BKE_sculpt_attribute_ensure(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(automasking_factor), &params);
+
for (int i : IndexRange(totvert)) {
- automasking->factor[i] = 1.0f;
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.automasking_factor)) = 0.0f;
}
const int boundary_propagation_steps = brush ?
@@ -350,22 +366,21 @@ AutomaskingCache *SCULPT_automasking_cache_init(Sculpt *sd, Brush *brush, Object
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
SCULPT_vertex_random_access_ensure(ss);
- SCULPT_topology_automasking_init(sd, ob, automasking->factor);
+ SCULPT_topology_automasking_init(sd, ob);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
- sculpt_face_sets_automasking_init(sd, ob, automasking->factor);
+ sculpt_face_sets_automasking_init(sd, ob);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
SCULPT_vertex_random_access_ensure(ss);
- SCULPT_boundary_automasking_init(
- ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps, automasking->factor);
+ SCULPT_boundary_automasking_init(ob, AUTOMASK_INIT_BOUNDARY_EDGES, boundary_propagation_steps);
}
if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_automasking_init(
- ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps, automasking->factor);
+ ob, AUTOMASK_INIT_BOUNDARY_FACE_SETS, boundary_propagation_steps);
}
return automasking;
diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c
index 44e2dfae480..005892b88a0 100644
--- a/source/blender/editors/sculpt_paint/sculpt_boundary.c
+++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c
@@ -46,32 +46,38 @@
#define BOUNDARY_STEPS_NONE -1
typedef struct BoundaryInitialVertexFloodFillData {
- int initial_vertex;
+ PBVHVertRef initial_vertex;
+ int initial_vertex_i;
int boundary_initial_vertex_steps;
- int boundary_initial_vertex;
+ PBVHVertRef boundary_initial_vertex;
+ int boundary_initial_vertex_i;
int *floodfill_steps;
float radius_sq;
} BoundaryInitialVertexFloodFillData;
static bool boundary_initial_vertex_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
BoundaryInitialVertexFloodFillData *data = userdata;
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
if (!SCULPT_vertex_visible_get(ss, to_v)) {
return false;
}
if (!is_duplicate) {
- data->floodfill_steps[to_v] = data->floodfill_steps[from_v] + 1;
+ data->floodfill_steps[to_v_i] = data->floodfill_steps[from_v_i] + 1;
}
else {
- data->floodfill_steps[to_v] = data->floodfill_steps[from_v];
+ data->floodfill_steps[to_v_i] = data->floodfill_steps[from_v_i];
}
if (SCULPT_vertex_is_boundary(ss, to_v)) {
- if (data->floodfill_steps[to_v] < data->boundary_initial_vertex_steps) {
- data->boundary_initial_vertex_steps = data->floodfill_steps[to_v];
+ if (data->floodfill_steps[to_v_i] < data->boundary_initial_vertex_steps) {
+ data->boundary_initial_vertex_steps = data->floodfill_steps[to_v_i];
+ data->boundary_initial_vertex_i = to_v_i;
data->boundary_initial_vertex = to_v;
}
}
@@ -83,9 +89,9 @@ static bool boundary_initial_vertex_floodfill_cb(
/* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside
* the given radius, if it exists. */
-static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
- const int initial_vertex,
- const float radius)
+static PBVHVertRef sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
+ const PBVHVertRef initial_vertex,
+ const float radius)
{
if (SCULPT_vertex_is_boundary(ss, initial_vertex)) {
@@ -98,7 +104,7 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
BoundaryInitialVertexFloodFillData fdata = {
.initial_vertex = initial_vertex,
- .boundary_initial_vertex = BOUNDARY_VERTEX_NONE,
+ .boundary_initial_vertex = {BOUNDARY_VERTEX_NONE},
.boundary_initial_vertex_steps = INT_MAX,
.radius_sq = radius * radius,
};
@@ -119,34 +125,38 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss,
static int BOUNDARY_INDICES_BLOCK_SIZE = 300;
static void sculpt_boundary_index_add(SculptBoundary *boundary,
+ const PBVHVertRef new_vertex,
const int new_index,
const float distance,
- GSet *included_vertices)
+ GSet *included_verts)
{
- boundary->vertices[boundary->num_vertices] = new_index;
+ boundary->verts[boundary->verts_num] = new_vertex;
+
if (boundary->distance) {
boundary->distance[new_index] = distance;
}
- if (included_vertices) {
- BLI_gset_add(included_vertices, POINTER_FROM_INT(new_index));
+ if (included_verts) {
+ BLI_gset_add(included_verts, POINTER_FROM_INT(new_index));
}
- boundary->num_vertices++;
- if (boundary->num_vertices >= boundary->vertices_capacity) {
- boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
- boundary->vertices = MEM_reallocN_id(
- boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices");
+ boundary->verts_num++;
+ if (boundary->verts_num >= boundary->verts_capacity) {
+ boundary->verts_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
+ boundary->verts = MEM_reallocN_id(
+ boundary->verts, boundary->verts_capacity * sizeof(PBVHVertRef), "boundary indices");
}
};
-static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2)
+static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary,
+ const PBVHVertRef v1,
+ const PBVHVertRef v2)
{
- boundary->edges[boundary->num_edges].v1 = v1;
- boundary->edges[boundary->num_edges].v2 = v2;
- boundary->num_edges++;
+ boundary->edges[boundary->edges_num].v1 = v1;
+ boundary->edges[boundary->edges_num].v2 = v2;
+ boundary->edges_num++;
- if (boundary->num_edges >= boundary->edges_capacity) {
+ if (boundary->edges_num >= boundary->edges_capacity) {
boundary->edges_capacity += BOUNDARY_INDICES_BLOCK_SIZE;
boundary->edges = MEM_reallocN_id(boundary->edges,
boundary->edges_capacity * sizeof(SculptBoundaryPreviewEdge),
@@ -159,7 +169,7 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int
* as well as to check if the initial vertex is valid.
*/
static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
- const int initial_vertex)
+ const PBVHVertRef initial_vertex)
{
if (!SCULPT_vertex_visible_get(ss, initial_vertex)) {
@@ -170,9 +180,9 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
int boundary_vertex_count = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vertex, ni) {
- if (SCULPT_vertex_visible_get(ss, ni.index)) {
+ if (SCULPT_vertex_visible_get(ss, ni.vertex)) {
neighbor_count++;
- if (SCULPT_vertex_is_boundary(ss, ni.index)) {
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
boundary_vertex_count++;
}
}
@@ -199,16 +209,19 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss,
typedef struct BoundaryFloodFillData {
SculptBoundary *boundary;
- GSet *included_vertices;
+ GSet *included_verts;
EdgeSet *preview_edges;
- int last_visited_vertex;
+ PBVHVertRef last_visited_vertex;
} BoundaryFloodFillData;
static bool boundary_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
BoundaryFloodFillData *data = userdata;
SculptBoundary *boundary = data->boundary;
if (!SCULPT_vertex_is_boundary(ss, to_v)) {
@@ -217,9 +230,10 @@ static bool boundary_floodfill_cb(
const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v),
SCULPT_vertex_co_get(ss, to_v));
const float distance_boundary_to_dst = boundary->distance ?
- boundary->distance[from_v] + edge_len :
+ boundary->distance[from_v_i] + edge_len :
0.0f;
- sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices);
+ sculpt_boundary_index_add(
+ boundary, to_v, to_v_i, distance_boundary_to_dst, data->included_verts);
if (!is_duplicate) {
sculpt_boundary_preview_edge_add(boundary, from_v, to_v);
}
@@ -229,32 +243,38 @@ static bool boundary_floodfill_cb(
static void sculpt_boundary_indices_init(SculptSession *ss,
SculptBoundary *boundary,
const bool init_boundary_distances,
- const int initial_boundary_index)
+ const PBVHVertRef initial_boundary_vertex)
{
const int totvert = SCULPT_vertex_count_get(ss);
- boundary->vertices = MEM_malloc_arrayN(
- BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices");
+ boundary->verts = MEM_malloc_arrayN(
+ BOUNDARY_INDICES_BLOCK_SIZE, sizeof(PBVHVertRef), "boundary indices");
+
if (init_boundary_distances) {
boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances");
}
boundary->edges = MEM_malloc_arrayN(
BOUNDARY_INDICES_BLOCK_SIZE, sizeof(SculptBoundaryPreviewEdge), "boundary edges");
- GSet *included_vertices = BLI_gset_int_new_ex("included vertices", BOUNDARY_INDICES_BLOCK_SIZE);
+ GSet *included_verts = BLI_gset_int_new_ex("included verts", BOUNDARY_INDICES_BLOCK_SIZE);
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
- boundary->initial_vertex = initial_boundary_index;
+ int initial_boundary_index = BKE_pbvh_vertex_to_index(ss->pbvh, initial_boundary_vertex);
+
+ boundary->initial_vertex = initial_boundary_vertex;
+ boundary->initial_vertex_i = initial_boundary_index;
+
copy_v3_v3(boundary->initial_vertex_position,
SCULPT_vertex_co_get(ss, boundary->initial_vertex));
- sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices);
- SCULPT_floodfill_add_initial(&flood, initial_boundary_index);
+ sculpt_boundary_index_add(
+ boundary, initial_boundary_vertex, initial_boundary_index, 0.0f, included_verts);
+ SCULPT_floodfill_add_initial(&flood, boundary->initial_vertex);
BoundaryFloodFillData fdata = {
.boundary = boundary,
- .included_vertices = included_vertices,
- .last_visited_vertex = BOUNDARY_VERTEX_NONE,
+ .included_verts = included_verts,
+ .last_visited_vertex = {BOUNDARY_VERTEX_NONE},
};
@@ -262,20 +282,20 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
SCULPT_floodfill_free(&flood);
/* Check if the boundary loops into itself and add the extra preview edge to close the loop. */
- if (fdata.last_visited_vertex != BOUNDARY_VERTEX_NONE &&
+ if (fdata.last_visited_vertex.i != BOUNDARY_VERTEX_NONE &&
sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) {
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) {
- if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) &&
- sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) {
- sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index);
+ if (BLI_gset_haskey(included_verts, POINTER_FROM_INT(ni.index)) &&
+ sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.vertex)) {
+ sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.vertex);
boundary->forms_loop = true;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
}
- BLI_gset_free(included_vertices, NULL);
+ BLI_gset_free(included_verts, NULL);
}
/**
@@ -286,7 +306,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss,
*/
static void sculpt_boundary_edit_data_init(SculptSession *ss,
SculptBoundary *boundary,
- const int initial_vertex,
+ const PBVHVertRef initial_vertex,
const float radius)
{
const int totvert = SCULPT_vertex_count_get(ss);
@@ -297,72 +317,78 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info");
for (int i = 0; i < totvert; i++) {
- boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE;
- boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE;
+ boundary->edit_info[i].original_vertex_i = BOUNDARY_VERTEX_NONE;
+ boundary->edit_info[i].propagation_steps_num = BOUNDARY_STEPS_NONE;
}
- GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int));
- GSQueue *next_iteration = BLI_gsqueue_new(sizeof(int));
+ GSQueue *current_iteration = BLI_gsqueue_new(sizeof(PBVHVertRef));
+ GSQueue *next_iteration = BLI_gsqueue_new(sizeof(PBVHVertRef));
/* Initialized the first iteration with the vertices already in the boundary. This is propagation
* step 0. */
- BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
- for (int i = 0; i < boundary->num_vertices; i++) {
- boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i];
- boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0;
+ BLI_bitmap *visited_verts = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_verts");
+ for (int i = 0; i < boundary->verts_num; i++) {
+ int index = BKE_pbvh_vertex_to_index(ss->pbvh, boundary->verts[i]);
+
+ boundary->edit_info[index].original_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh,
+ boundary->verts[i]);
+ boundary->edit_info[index].propagation_steps_num = 0;
/* This ensures that all duplicate vertices in the boundary have the same original_vertex
* index, so the deformation for them will be the same. */
if (has_duplicates) {
SculptVertexNeighborIter ni_duplis;
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) {
+ SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->verts[i], ni_duplis) {
if (ni_duplis.is_duplicate) {
- boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i];
+ boundary->edit_info[ni_duplis.index].original_vertex_i = BKE_pbvh_vertex_to_index(
+ ss->pbvh, boundary->verts[i]);
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
}
- BLI_gsqueue_push(current_iteration, &boundary->vertices[i]);
+ BLI_gsqueue_push(current_iteration, &boundary->verts[i]);
}
- int num_propagation_steps = 0;
+ int propagation_steps_num = 0;
float accum_distance = 0.0f;
while (true) {
/* Stop adding steps to edit info. This happens when a steps is further away from the boundary
* than the brush radius or when the entire mesh was already processed. */
if (accum_distance > radius || BLI_gsqueue_is_empty(current_iteration)) {
- boundary->max_propagation_steps = num_propagation_steps;
+ boundary->max_propagation_steps = propagation_steps_num;
break;
}
while (!BLI_gsqueue_is_empty(current_iteration)) {
- int from_v;
+ PBVHVertRef from_v;
BLI_gsqueue_pop(current_iteration, &from_v);
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+
SculptVertexNeighborIter ni;
SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
- const bool is_visible = SCULPT_vertex_visible_get(ss, ni.index);
+ const bool is_visible = SCULPT_vertex_visible_get(ss, ni.vertex);
if (!is_visible ||
- boundary->edit_info[ni.index].num_propagation_steps != BOUNDARY_STEPS_NONE) {
+ boundary->edit_info[ni.index].propagation_steps_num != BOUNDARY_STEPS_NONE) {
continue;
}
- boundary->edit_info[ni.index].original_vertex =
- boundary->edit_info[from_v].original_vertex;
+ boundary->edit_info[ni.index].original_vertex_i =
+ boundary->edit_info[from_v_i].original_vertex_i;
- BLI_BITMAP_ENABLE(visited_vertices, ni.index);
+ BLI_BITMAP_ENABLE(visited_verts, ni.index);
if (ni.is_duplicate) {
/* Grids duplicates handling. */
- boundary->edit_info[ni.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps;
+ boundary->edit_info[ni.index].propagation_steps_num =
+ boundary->edit_info[from_v_i].propagation_steps_num;
}
else {
- boundary->edit_info[ni.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps + 1;
+ boundary->edit_info[ni.index].propagation_steps_num =
+ boundary->edit_info[from_v_i].propagation_steps_num + 1;
- BLI_gsqueue_push(next_iteration, &ni.index);
+ BLI_gsqueue_push(next_iteration, &ni.vertex);
/* When copying the data to the neighbor for the next iteration, it has to be copied to
* all its duplicates too. This is because it is not possible to know if the updated
@@ -370,12 +396,12 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
* copy the data in the from_v neighbor iterator. */
if (has_duplicates) {
SculptVertexNeighborIter ni_duplis;
- SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) {
+ SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni_duplis) {
if (ni_duplis.is_duplicate) {
- boundary->edit_info[ni_duplis.index].original_vertex =
- boundary->edit_info[from_v].original_vertex;
- boundary->edit_info[ni_duplis.index].num_propagation_steps =
- boundary->edit_info[from_v].num_propagation_steps + 1;
+ boundary->edit_info[ni_duplis.index].original_vertex_i =
+ boundary->edit_info[from_v_i].original_vertex_i;
+ boundary->edit_info[ni_duplis.index].propagation_steps_num =
+ boundary->edit_info[from_v_i].propagation_steps_num + 1;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis);
@@ -383,11 +409,12 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* Check the distance using the vertex that was propagated from the initial vertex that
* was used to initialize the boundary. */
- if (boundary->edit_info[from_v].original_vertex == initial_vertex) {
- boundary->pivot_vertex = ni.index;
- copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index));
+ if (boundary->edit_info[from_v_i].original_vertex_i ==
+ BKE_pbvh_vertex_to_index(ss->pbvh, initial_vertex)) {
+ boundary->pivot_vertex = ni.vertex;
+ copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.vertex));
accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v),
- SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_vertex_co_get(ss, ni.vertex));
}
}
}
@@ -396,15 +423,15 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss,
/* Copy the new vertices to the queue to be processed in the next iteration. */
while (!BLI_gsqueue_is_empty(next_iteration)) {
- int next_v;
+ PBVHVertRef next_v;
BLI_gsqueue_pop(next_iteration, &next_v);
BLI_gsqueue_push(current_iteration, &next_v);
}
- num_propagation_steps++;
+ propagation_steps_num++;
}
- MEM_SAFE_FREE(visited_vertices);
+ MEM_SAFE_FREE(visited_verts);
BLI_gsqueue_free(current_iteration);
BLI_gsqueue_free(next_iteration);
@@ -422,12 +449,13 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
BKE_curvemapping_init(brush->curve);
for (int i = 0; i < totvert; i++) {
- if (boundary->edit_info[i].num_propagation_steps != -1) {
+ if (boundary->edit_info[i].propagation_steps_num != -1) {
boundary->edit_info[i].strength_factor = BKE_brush_curve_strength(
- brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps);
+ brush, boundary->edit_info[i].propagation_steps_num, boundary->max_propagation_steps);
}
- if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) {
+ if (boundary->edit_info[i].original_vertex_i ==
+ BKE_pbvh_vertex_to_index(ss->pbvh, boundary->initial_vertex)) {
/* All vertices that are propagated from the original vertex won't be affected by the
* boundary falloff, so there is no need to calculate anything else. */
continue;
@@ -439,7 +467,7 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
continue;
}
- const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex];
+ const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex_i];
float falloff_distance = 0.0f;
float direction = 1.0f;
@@ -473,22 +501,22 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss,
SculptBoundary *SCULPT_boundary_data_init(Object *object,
Brush *brush,
- const int initial_vertex,
+ const PBVHVertRef initial_vertex,
const float radius)
{
SculptSession *ss = object->sculpt;
- if (initial_vertex == BOUNDARY_VERTEX_NONE) {
+ if (initial_vertex.i == PBVH_REF_NONE) {
return NULL;
}
SCULPT_vertex_random_access_ensure(ss);
SCULPT_boundary_info_ensure(object);
- const int boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex(
+ const PBVHVertRef boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex(
ss, initial_vertex, radius);
- if (boundary_initial_vertex == BOUNDARY_VERTEX_NONE) {
+ if (boundary_initial_vertex.i == BOUNDARY_VERTEX_NONE) {
return NULL;
}
@@ -514,7 +542,7 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object,
void SCULPT_boundary_data_free(SculptBoundary *boundary)
{
- MEM_SAFE_FREE(boundary->vertices);
+ MEM_SAFE_FREE(boundary->verts);
MEM_SAFE_FREE(boundary->edges);
MEM_SAFE_FREE(boundary->distance);
MEM_SAFE_FREE(boundary->edit_info);
@@ -536,30 +564,35 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo
boundary->bend.pivot_positions = MEM_calloc_arrayN(totvert, sizeof(float[3]), "pivot positions");
for (int i = 0; i < totvert; i++) {
- if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
+ if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) {
continue;
}
+
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
float dir[3];
float normal[3];
- SCULPT_vertex_normal_get(ss, i, normal);
- sub_v3_v3v3(dir,
- SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
- SCULPT_vertex_co_get(ss, i));
+ SCULPT_vertex_normal_get(ss, vertex, normal);
+ sub_v3_v3v3(
+ dir,
+ SCULPT_vertex_co_get(
+ ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)),
+ SCULPT_vertex_co_get(ss, vertex));
cross_v3_v3v3(
- boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex], dir, normal);
- normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
- copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex],
- SCULPT_vertex_co_get(ss, i));
+ boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i], dir, normal);
+ normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]);
+ copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i],
+ SCULPT_vertex_co_get(ss, vertex));
}
for (int i = 0; i < totvert; i++) {
- if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) {
+ if (boundary->edit_info[i].propagation_steps_num == BOUNDARY_STEPS_NONE) {
continue;
}
copy_v3_v3(boundary->bend.pivot_positions[i],
- boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]);
+ boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]);
copy_v3_v3(boundary->bend.pivot_rotation_axis[i],
- boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]);
+ boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]);
}
}
@@ -569,36 +602,37 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b
boundary->slide.directions = MEM_calloc_arrayN(totvert, sizeof(float[3]), "slide directions");
for (int i = 0; i < totvert; i++) {
- if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) {
+ if (boundary->edit_info[i].propagation_steps_num != boundary->max_propagation_steps) {
continue;
}
- sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex],
- SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex),
- SCULPT_vertex_co_get(ss, i));
- normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]);
+ sub_v3_v3v3(
+ boundary->slide.directions[boundary->edit_info[i].original_vertex_i],
+ SCULPT_vertex_co_get(
+ ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)),
+ SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, i)));
+ normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i]);
}
for (int i = 0; i < totvert; i++) {
- if (boundary->edit_info[i].num_propagation_steps == BOUNDARY_STEPS_NONE) {
+ if (boundary->edit_info[i].propagation_steps_num == BOUNDARY_STEPS_NONE) {
continue;
}
copy_v3_v3(boundary->slide.directions[i],
- boundary->slide.directions[boundary->edit_info[i].original_vertex]);
+ boundary->slide.directions[boundary->edit_info[i].original_vertex_i]);
}
}
static void sculpt_boundary_twist_data_init(SculptSession *ss, SculptBoundary *boundary)
{
zero_v3(boundary->twist.pivot_position);
- float(*poly_verts)[3] = MEM_malloc_arrayN(
- boundary->num_vertices, sizeof(float[3]), "poly verts");
- for (int i = 0; i < boundary->num_vertices; i++) {
- add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->vertices[i]));
- copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->vertices[i]));
+ float(*poly_verts)[3] = MEM_malloc_arrayN(boundary->verts_num, sizeof(float[3]), "poly verts");
+ for (int i = 0; i < boundary->verts_num; i++) {
+ add_v3_v3(boundary->twist.pivot_position, SCULPT_vertex_co_get(ss, boundary->verts[i]));
+ copy_v3_v3(poly_verts[i], SCULPT_vertex_co_get(ss, boundary->verts[i]));
}
- mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->num_vertices);
+ mul_v3_fl(boundary->twist.pivot_position, 1.0f / boundary->verts_num);
if (boundary->forms_loop) {
- normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->num_vertices);
+ normal_poly_v3(boundary->twist.rotation_axis, poly_verts, boundary->verts_num);
}
else {
sub_v3_v3v3(boundary->twist.rotation_axis,
@@ -649,7 +683,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
const float angle = angle_factor * M_PI;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
+ if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@@ -660,7 +694,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float t_orig_co[3];
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]);
@@ -671,7 +705,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata,
add_v3_v3(target_co, boundary->bend.pivot_positions[vd.index]);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -697,7 +731,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
+ if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@@ -708,7 +742,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@@ -717,7 +751,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata,
strength);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -743,7 +777,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
const float disp = sculpt_boundary_displacement_from_grab_delta_get(ss, boundary);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
+ if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@@ -754,7 +788,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@@ -763,7 +797,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata,
strength);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -787,7 +821,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
+ if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@@ -798,7 +832,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
madd_v3_v3v3fl(target_co,
orig_data.co,
@@ -806,7 +840,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata,
boundary->edit_info[vd.index].strength_factor * mask * automask * strength);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -838,7 +872,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
const float angle = angle_factor * M_PI;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
+ if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@@ -849,7 +883,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
}
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
float t_orig_co[3];
float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd);
sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position);
@@ -860,7 +894,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata,
add_v3_v3(target_co, boundary->twist.pivot_position);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -884,7 +918,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (boundary->edit_info[vd.index].num_propagation_steps == -1) {
+ if (boundary->edit_info[vd.index].propagation_steps_num == -1) {
continue;
}
@@ -896,11 +930,11 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
float coord_accum[3] = {0.0f, 0.0f, 0.0f};
int total_neighbors = 0;
- const int current_propagation_steps = boundary->edit_info[vd.index].num_propagation_steps;
+ const int current_propagation_steps = boundary->edit_info[vd.index].propagation_steps_num;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
- if (current_propagation_steps == boundary->edit_info[ni.index].num_propagation_steps) {
- add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
+ if (current_propagation_steps == boundary->edit_info[ni.index].propagation_steps_num) {
+ add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.vertex));
total_neighbors++;
}
}
@@ -919,7 +953,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata,
target_co, vd.co, disp, boundary->edit_info[vd.index].strength_factor * mask * strength);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -933,7 +967,8 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn
const int symm_area = ss->cache->mirror_symmetry_pass;
if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
- int initial_vertex;
+ PBVHVertRef initial_vertex;
+
if (ss->cache->mirror_symmetry_pass == 0) {
initial_vertex = SCULPT_active_vertex_get(ss);
}
@@ -1017,8 +1052,8 @@ void SCULPT_boundary_edges_preview_draw(const uint gpuattr,
}
immUniformColor3fvAlpha(outline_col, outline_alpha);
GPU_line_width(2.0f);
- immBegin(GPU_PRIM_LINES, ss->boundary_preview->num_edges * 2);
- for (int i = 0; i < ss->boundary_preview->num_edges; i++) {
+ immBegin(GPU_PRIM_LINES, ss->boundary_preview->edges_num * 2);
+ for (int i = 0; i < ss->boundary_preview->edges_num; i++) {
immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->edges[i].v1));
immVertex3fv(gpuattr, SCULPT_vertex_co_get(ss, ss->boundary_preview->edges[i].v2));
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.c b/source/blender/editors/sculpt_paint/sculpt_brush_types.c
index cba97f9b968..00ad77e48cf 100644
--- a/source/blender/editors/sculpt_paint/sculpt_brush_types.c
+++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.c
@@ -314,13 +314,13 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -412,13 +412,13 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -510,13 +510,13 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -628,13 +628,13 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -783,13 +783,13 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
}
@@ -940,13 +940,13 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1066,13 +1066,13 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1219,7 +1219,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
}
@@ -1267,12 +1267,12 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
if (vd.mask) {
mul_v3_fl(disp, 1.0f - *vd.mask);
}
- mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
+ mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex));
copy_v3_v3(proxy[vd.i], disp);
}
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1352,13 +1352,13 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], cono, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1426,7 +1426,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
sub_v3_v3v3(vec, orig_data.co, ss->cache->location);
@@ -1436,7 +1436,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata,
sub_v3_v3(proxy[vd.i], orig_data.co);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1472,7 +1472,8 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
Sculpt *sd = data->sd;
const Brush *brush = data->brush;
- const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT;
+ const bool use_persistent_base = !ss->bm && ss->attrs.persistent_co &&
+ brush->flag & BRUSH_PERSISTENT;
PBVHVertexIter vd;
SculptOrigVertData orig_data;
@@ -1497,13 +1498,13 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
const int vi = vd.index;
float *disp_factor;
if (use_persistent_base) {
- disp_factor = &ss->persistent_base[vi].disp;
+ disp_factor = (float *)SCULPT_vertex_attr_get(vd.vertex, ss->attrs.persistent_disp);
}
else {
disp_factor = &ss->cache->layer_displacement_factor[vi];
@@ -1533,9 +1534,10 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
float normal[3];
if (use_persistent_base) {
- SCULPT_vertex_persistent_normal_get(ss, vi, normal);
+ SCULPT_vertex_persistent_normal_get(ss, vd.vertex, normal);
mul_v3_fl(normal, brush->height);
- madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor);
+ madd_v3_v3v3fl(
+ final_co, SCULPT_vertex_persistent_co_get(ss, vd.vertex), normal, *disp_factor);
}
else {
copy_v3_v3(normal, orig_data.no);
@@ -1551,7 +1553,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata,
SCULPT_clip(sd, ss, vd.co, final_co);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1609,7 +1611,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float val[3];
@@ -1624,7 +1626,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1677,13 +1679,13 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], cono, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1756,7 +1758,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float val1[3];
float val2[3];
@@ -1777,7 +1779,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata,
add_v3_v3v3(proxy[vd.i], val1, val2);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1872,7 +1874,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp_center[3];
float x_disp[3];
@@ -1896,7 +1898,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata,
mul_v3_v3fl(proxy[vd.i], disp_center, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -1988,7 +1990,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (grab_silhouette) {
@@ -2005,7 +2007,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata,
mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2108,12 +2110,12 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
mul_v3_fl(final_disp, 1.0f - *vd.mask);
}
- mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
+ mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex));
copy_v3_v3(proxy[vd.i], final_disp);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2185,13 +2187,13 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], offset, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2268,7 +2270,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
float current_disp_norm[3];
@@ -2290,10 +2292,10 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
- sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co);
normalize_v3_v3(vertex_disp_norm, vertex_disp);
if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) {
madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp));
@@ -2304,7 +2306,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata,
mul_v3_v3fl(proxy[vd.i], final_disp, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2323,31 +2325,31 @@ void SCULPT_relax_vertex(SculptSession *ss,
int neighbor_count = 0;
zero_v3(smooth_pos);
zero_v3(boundary_normal);
- const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index);
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) {
neighbor_count++;
if (!filter_boundary_face_sets ||
- (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) {
+ (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.vertex))) {
/* When the vertex to relax is boundary, use only connected boundary vertices for the average
* position. */
if (is_boundary) {
- if (!SCULPT_vertex_is_boundary(ss, ni.index)) {
+ if (!SCULPT_vertex_is_boundary(ss, ni.vertex)) {
continue;
}
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex));
avg_count++;
/* Calculate a normal for the constraint plane using the edges of the boundary. */
float to_neighbor[3];
- sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co);
+ sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.vertex), vd->co);
normalize_v3(to_neighbor);
add_v3_v3(boundary_normal, to_neighbor);
}
else {
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex));
avg_count++;
}
}
@@ -2376,7 +2378,7 @@ void SCULPT_relax_vertex(SculptSession *ss,
normalize_v3_v3(vno, boundary_normal);
}
else {
- SCULPT_vertex_normal_get(ss, vd->index, vno);
+ SCULPT_vertex_normal_get(ss, vd->vertex, vno);
}
if (is_zero_v3(vno)) {
@@ -2425,12 +2427,12 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata,
orig_data.no,
NULL,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2501,17 +2503,17 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float limit_co[3];
float disp[3];
- SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co);
+ SCULPT_vertex_limit_surface_get(ss, vd.vertex, limit_co);
sub_v3_v3v3(disp, limit_co, vd.co);
mul_v3_v3fl(proxy[vd.i], disp, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2567,7 +2569,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
@@ -2594,11 +2596,11 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
float weights_accum = 1.0f;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vertex_disp[3];
float vertex_disp_norm[3];
float neighbor_limit_co[3];
- SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co);
+ SCULPT_vertex_limit_surface_get(ss, ni.vertex, neighbor_limit_co);
sub_v3_v3v3(vertex_disp,
ss->cache->limit_surface_co[ni.index],
ss->cache->limit_surface_co[vd.index]);
@@ -2623,7 +2625,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
interp_v3_v3v3(vd.co, vd.co, new_co, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2638,7 +2640,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex(
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
sub_v3_v3v3(ss->cache->prev_displacement[vd.index],
- SCULPT_vertex_co_get(ss, vd.index),
+ SCULPT_vertex_co_get(ss, vd.vertex),
ss->cache->limit_surface_co[vd.index]);
}
BKE_pbvh_vertex_iter_end;
@@ -2657,9 +2659,11 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes
totvert, sizeof(float[3]), "prev displacement");
ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co");
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_limit_surface_get(ss, vertex, ss->cache->limit_surface_co[i]);
sub_v3_v3v3(ss->cache->prev_displacement[i],
- SCULPT_vertex_co_get(ss, i),
+ SCULPT_vertex_co_get(ss, vertex),
ss->cache->limit_surface_co[i]);
}
}
@@ -2722,7 +2726,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
const float fade =
bstrength *
SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) *
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) *
ss->cache->pressure;
float avg[3], val[3];
@@ -2736,7 +2740,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
SCULPT_clip(sd, ss, vd.co, val);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -2799,7 +2803,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
}
const float fade = SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id);
if (bstrength > 0.0f) {
(*vd.mask) += fade * bstrength * (1.0f - *vd.mask);
@@ -2808,12 +2812,8 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
(*vd.mask) += fade * bstrength * (*vd.mask);
}
*vd.mask = clamp_f(*vd.mask, 0.0f, 1.0f);
-
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
- BKE_pbvh_vertex_iter_end;
}
+ BKE_pbvh_vertex_iter_end;
}
void SCULPT_do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 9d231f2ccd2..691dfa21851 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -220,13 +220,16 @@ static void cloth_brush_add_length_constraint(SculptSession *ss,
length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL;
+ PBVHVertRef vertex1 = BKE_pbvh_index_to_vertex(ss->pbvh, v1);
+ PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2);
+
if (use_persistent) {
- length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1),
- SCULPT_vertex_persistent_co_get(ss, v2));
+ length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, vertex1),
+ SCULPT_vertex_persistent_co_get(ss, vertex2));
}
else {
- length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1),
- SCULPT_vertex_co_get(ss, v2));
+ length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, vertex1),
+ SCULPT_vertex_co_get(ss, vertex2));
}
length_constraint->strength = 1.0f;
@@ -370,7 +373,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex(
int tot_indices = 0;
build_indices[tot_indices] = vd.index;
tot_indices++;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
build_indices[tot_indices] = ni.index;
tot_indices++;
}
@@ -540,7 +543,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float brush_disp[3];
@@ -611,13 +614,17 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_end;
}
-static ListBase *cloth_brush_collider_cache_create(Depsgraph *depsgraph)
+static ListBase *cloth_brush_collider_cache_create(Object *object, Depsgraph *depsgraph)
{
ListBase *cache = NULL;
DEG_OBJECT_ITER_BEGIN (depsgraph,
ob,
DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_VISIBLE |
DEG_ITER_OBJECT_FLAG_DUPLI) {
+ if (STREQ(object->id.name, ob->id.name)) {
+ continue;
+ }
+
CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type(
ob, eModifierType_Collision);
if (!cmd) {
@@ -780,7 +787,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor);
const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) *
- SCULPT_automasking_factor_get(automasking, ss, vd.index);
+ SCULPT_automasking_factor_get(automasking, ss, vd.vertex);
madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v);
madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v);
@@ -798,7 +805,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
copy_v3_v3(vd.co, cloth_sim->pos[vd.index]);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -848,10 +855,13 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss,
mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f);
- const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) *
- SCULPT_automasking_factor_get(automasking, ss, v1);
- const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) *
- SCULPT_automasking_factor_get(automasking, ss, v2);
+ PBVHVertRef vertex1 = BKE_pbvh_index_to_vertex(ss->pbvh, v1);
+ PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2);
+
+ const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, vertex1)) *
+ SCULPT_automasking_factor_get(automasking, ss, vertex1);
+ const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, vertex2)) *
+ SCULPT_automasking_factor_get(automasking, ss, vertex2);
float sim_location[3];
cloth_brush_simulation_location_get(ss, brush, sim_location);
@@ -1029,13 +1039,14 @@ static void cloth_sim_initialize_default_node_state(SculptSession *ss,
MEM_SAFE_FREE(nodes);
}
-SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
+SculptClothSimulation *SCULPT_cloth_brush_simulation_create(Object *ob,
const float cloth_mass,
const float cloth_damping,
const float cloth_softbody_strength,
const bool use_collisions,
const bool needs_deform_coords)
{
+ SculptSession *ss = ob->sculpt;
const int totverts = SCULPT_vertex_count_get(ss);
SculptClothSimulation *cloth_sim;
@@ -1073,7 +1084,7 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss,
cloth_sim->softbody_strength = cloth_softbody_strength;
if (use_collisions) {
- cloth_sim->collider_list = cloth_brush_collider_cache_create(ss->depsgraph);
+ cloth_sim->collider_list = cloth_brush_collider_cache_create(ob, ss->depsgraph);
}
cloth_sim_initialize_default_node_state(ss, cloth_sim);
@@ -1124,15 +1135,17 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation
const bool has_deformation_pos = cloth_sim->deformation_pos != NULL;
const bool has_softbody_pos = cloth_sim->softbody_pos != NULL;
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i));
- copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i));
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, vertex));
+ copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, vertex));
+ copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, vertex));
if (has_deformation_pos) {
- copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, vertex));
cloth_sim->deformation_strength[i] = 1.0f;
}
if (has_softbody_pos) {
- copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, i));
+ copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, vertex));
}
}
}
@@ -1141,7 +1154,9 @@ void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSim
{
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -1184,7 +1199,7 @@ void SCULPT_do_cloth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
/* The simulation structure only needs to be created on the first symmetry pass. */
if (SCULPT_stroke_is_first_brush_step(ss->cache) || !ss->cache->cloth_sim) {
ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss,
+ ob,
brush->cloth_mass,
brush->cloth_damping,
brush->cloth_constraint_softbody_strength,
@@ -1422,13 +1437,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
float fade = vd.mask ? *vd.mask : 0.0f;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
fade = 1.0f - fade;
float force[3] = {0.0f, 0.0f, 0.0f};
float disp[3], temp[3], transform[3][3];
if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) {
- if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) {
+ if (!SCULPT_vertex_has_face_set(ss, vd.vertex, ss->filter_cache->active_face_set)) {
continue;
}
}
@@ -1447,7 +1462,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata,
break;
case CLOTH_FILTER_INFLATE: {
float normal[3];
- SCULPT_vertex_normal_get(ss, vd.index, normal);
+ SCULPT_vertex_normal_get(ss, vd.vertex, normal);
mul_v3_v3fl(force, normal, fade * data->filter_strength);
} break;
case CLOTH_FILTER_EXPAND:
@@ -1512,7 +1527,9 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent
const int totverts = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totverts; i++) {
- copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i));
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex));
}
SculptThreadedTaskData data = {
@@ -1562,7 +1579,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
/* Needs mask data to be available as it is used when solving the constraints. */
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
- SCULPT_undo_push_begin(ob, "Cloth filter");
+ SCULPT_undo_push_begin(ob, op);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
ss->filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
@@ -1571,7 +1588,7 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent
const float cloth_damping = RNA_float_get(op->ptr, "cloth_damping");
const bool use_collisions = RNA_boolean_get(op->ptr, "use_collisions");
ss->filter_cache->cloth_sim = SCULPT_cloth_brush_simulation_create(
- ss,
+ ob,
cloth_mass,
cloth_damping,
0.0f,
diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index 00503087e39..8f87cd1b6ed 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -76,7 +76,7 @@ static bool sculpt_and_dynamic_topology_poll(bContext *C)
/** \name Detail Flood Fill
* \{ */
-static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op))
+static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *op)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
Object *ob = CTX_data_active_object(C);
@@ -106,7 +106,7 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op))
float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat));
BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail);
- SCULPT_undo_push_begin(ob, "Dynamic topology flood fill");
+ SCULPT_undo_push_begin(ob, op);
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS);
while (BKE_pbvh_bmesh_update_topology(
@@ -174,13 +174,13 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2])
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
/* Average the edge length of the connected edges to the active vertex. */
- int active_vertex = SCULPT_active_vertex_get(ss);
+ PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
const float *active_vertex_co = SCULPT_active_vertex_co_get(ss);
float edge_length = 0.0f;
int tot = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
- edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.index));
+ edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.vertex));
tot += 1;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -578,14 +578,14 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob,
DyntopoDetailSizeEditCustomData *cd)
{
SculptSession *ss = ob->sculpt;
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
float len_accum = 0;
int num_neighbors = 0;
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) {
len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex),
- SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_vertex_co_get(ss, ni.vertex));
num_neighbors++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
index 4f884420401..d69633bd05c 100644
--- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c
@@ -88,41 +88,6 @@ void SCULPT_pbvh_clear(Object *ob)
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
-void SCULPT_dyntopo_node_layers_add(SculptSession *ss)
-{
- int cd_node_layer_index;
-
- char layer_id[] = "_dyntopo_node_id";
-
- cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT32, layer_id);
- if (cd_node_layer_index == -1) {
- BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, layer_id);
- cd_node_layer_index = CustomData_get_named_layer_index(
- &ss->bm->vdata, CD_PROP_INT32, layer_id);
- }
-
- ss->cd_vert_node_offset = CustomData_get_n_offset(
- &ss->bm->vdata,
- CD_PROP_INT32,
- cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT32));
-
- ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
-
- cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT32, layer_id);
- if (cd_node_layer_index == -1) {
- BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, layer_id);
- cd_node_layer_index = CustomData_get_named_layer_index(
- &ss->bm->pdata, CD_PROP_INT32, layer_id);
- }
-
- ss->cd_face_node_offset = CustomData_get_n_offset(
- &ss->bm->pdata,
- CD_PROP_INT32,
- cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT32));
-
- ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
-}
-
void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
{
SculptSession *ss = ob->sculpt;
@@ -152,8 +117,9 @@ void SCULPT_dynamic_topology_enable_ex(Main *bmain, Depsgraph *depsgraph, Scene
.active_shapekey = ob->shapenr,
}));
SCULPT_dynamic_topology_triangulate(ss->bm);
+
BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK);
- SCULPT_dyntopo_node_layers_add(ss);
+
/* Make sure the data for existing faces are initialized. */
if (me->totpoly != ss->bm->totface) {
BM_mesh_normals_update(ss->bm);
@@ -181,6 +147,9 @@ static void SCULPT_dynamic_topology_disable_ex(
SculptSession *ss = ob->sculpt;
Mesh *me = ob->data;
+ BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_vertex);
+ BKE_sculpt_attribute_destroy(ob, ss->attrs.dyntopo_node_id_face);
+
SCULPT_pbvh_clear(ob);
if (unode) {
@@ -206,25 +175,18 @@ static void SCULPT_dynamic_topology_disable_ex(
&geometry->ldata, &me->ldata, CD_MASK_MESH.lmask, CD_DUPLICATE, geometry->totloop);
CustomData_copy(
&geometry->pdata, &me->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly);
-
- BKE_mesh_update_customdata_pointers(me, false);
}
else {
BKE_sculptsession_bm_to_me(ob, true);
/* Reset Face Sets as they are no longer valid. */
- if (!CustomData_has_layer(&me->pdata, CD_SCULPT_FACE_SETS)) {
- CustomData_add_layer(&me->pdata, CD_SCULPT_FACE_SETS, CD_CALLOC, NULL, me->totpoly);
- }
- ss->face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
- for (int i = 0; i < me->totpoly; i++) {
- ss->face_sets[i] = 1;
- }
+ CustomData_free_layers(&me->pdata, CD_SCULPT_FACE_SETS, me->totpoly);
me->face_sets_color_default = 1;
/* Sync the visibility to vertices manually as the pmap is still not initialized. */
- for (int i = 0; i < me->totvert; i++) {
- me->mvert[i].flag &= ~ME_HIDE;
+ bool *hide_vert = (bool *)CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert");
+ if (hide_vert != NULL) {
+ memset(hide_vert, 0, sizeof(bool) * me->totvert);
}
}
@@ -269,7 +231,7 @@ void sculpt_dynamic_topology_disable_with_undo(Main *bmain,
/* May be false in background mode. */
const bool use_undo = G.background ? (ED_undo_stack_get() != NULL) : true;
if (use_undo) {
- SCULPT_undo_push_begin(ob, "Dynamic topology disable");
+ SCULPT_undo_push_begin_ex(ob, "Dynamic topology disable");
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_END);
}
SCULPT_dynamic_topology_disable_ex(bmain, depsgraph, scene, ob, NULL);
@@ -289,7 +251,7 @@ static void sculpt_dynamic_topology_enable_with_undo(Main *bmain,
/* May be false in background mode. */
const bool use_undo = G.background ? (ED_undo_stack_get() != NULL) : true;
if (use_undo) {
- SCULPT_undo_push_begin(ob, "Dynamic topology enable");
+ SCULPT_undo_push_begin_ex(ob, "Dynamic topology enable");
}
SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob);
if (use_undo) {
diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c
index 9648f558049..72b0b3a97fe 100644
--- a/source/blender/editors/sculpt_paint/sculpt_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_expand.c
@@ -17,6 +17,7 @@
#include "DNA_brush_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BKE_brush.h"
@@ -140,10 +141,12 @@ enum {
*/
static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const PBVHVertRef v)
{
+ int v_i = BKE_pbvh_vertex_to_index(ss->pbvh, v);
+
for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
- if (ss->vertex_info.connected_component[v] == expand_cache->active_connected_components[i]) {
+ if (ss->vertex_info.connected_component[v_i] == expand_cache->active_connected_components[i]) {
return true;
}
}
@@ -158,7 +161,7 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
const int f)
{
const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart];
- return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v);
+ return sculpt_expand_is_vert_in_active_component(ss, expand_cache, BKE_pbvh_make_vref(loop->v));
}
/**
@@ -167,14 +170,16 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss,
*/
static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const PBVHVertRef v)
{
+ int v_i = BKE_pbvh_vertex_to_index(ss->pbvh, v);
+
if (expand_cache->texture_distortion_strength == 0.0f) {
- return expand_cache->vert_falloff[v];
+ return expand_cache->vert_falloff[v_i];
}
if (!expand_cache->brush->mtex.tex) {
- return expand_cache->vert_falloff[v];
+ return expand_cache->vert_falloff[v_i];
}
float rgba[4];
@@ -184,7 +189,7 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss,
const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength *
expand_cache->max_vert_falloff;
- return expand_cache->vert_falloff[v] + distortion;
+ return expand_cache->vert_falloff[v_i] + distortion;
}
/**
@@ -209,7 +214,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache)
* Main function to get the state of a vertex for the current state and settings of a #ExpandCache.
* Returns true when the target data should be modified by expand.
*/
-static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v)
+static bool sculpt_expand_state_get(SculptSession *ss,
+ ExpandCache *expand_cache,
+ const PBVHVertRef v)
{
if (!SCULPT_vertex_visible_get(ss, v)) {
return false;
@@ -303,7 +310,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_
*/
static float sculpt_expand_gradient_value_get(SculptSession *ss,
ExpandCache *expand_cache,
- const int v)
+ const PBVHVertRef v)
{
if (!expand_cache->falloff_gradient) {
return 1.0f;
@@ -345,12 +352,13 @@ static float sculpt_expand_gradient_value_get(SculptSession *ss,
static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCache *expand_cache)
{
const int totvert = SCULPT_vertex_count_get(ss);
- BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices");
+ BLI_bitmap *enabled_verts = BLI_BITMAP_NEW(totvert, "enabled verts");
for (int i = 0; i < totvert; i++) {
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, i);
- BLI_BITMAP_SET(enabled_vertices, i, enabled);
+ const bool enabled = sculpt_expand_state_get(
+ ss, expand_cache, BKE_pbvh_index_to_vertex(ss->pbvh, i));
+ BLI_BITMAP_SET(enabled_verts, i, enabled);
}
- return enabled_vertices;
+ return enabled_verts;
}
/**
@@ -359,33 +367,35 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa
* vertex that is not enabled.
*/
static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
- const BLI_bitmap *enabled_vertices,
+ const BLI_bitmap *enabled_verts,
const bool use_mesh_boundary)
{
const int totvert = SCULPT_vertex_count_get(ss);
- BLI_bitmap *boundary_vertices = BLI_BITMAP_NEW(totvert, "boundary vertices");
+ BLI_bitmap *boundary_verts = BLI_BITMAP_NEW(totvert, "boundary verts");
for (int i = 0; i < totvert; i++) {
- if (!BLI_BITMAP_TEST(enabled_vertices, i)) {
+ if (!BLI_BITMAP_TEST(enabled_verts, i)) {
continue;
}
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
bool is_expand_boundary = false;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
- if (!BLI_BITMAP_TEST(enabled_vertices, ni.index)) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ if (!BLI_BITMAP_TEST(enabled_verts, ni.index)) {
is_expand_boundary = true;
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, i)) {
+ if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, vertex)) {
is_expand_boundary = true;
}
- BLI_BITMAP_SET(boundary_vertices, i, is_expand_boundary);
+ BLI_BITMAP_SET(boundary_verts, i, is_expand_boundary);
}
- return boundary_vertices;
+ return boundary_verts;
}
/* Functions implementing different algorithms for initializing falloff values. */
@@ -394,12 +404,12 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss,
* Utility function to get the closet vertex after flipping an original vertex position based on
* an symmetry pass iteration index.
*/
-static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob,
- const char symm_it,
- const int original_vertex)
+static PBVHVertRef sculpt_expand_get_vertex_index_for_symmetry_pass(
+ Object *ob, const char symm_it, const PBVHVertRef original_vertex)
{
SculptSession *ss = ob->sculpt;
- int symm_vertex = SCULPT_EXPAND_VERTEX_NONE;
+ PBVHVertRef symm_vertex = {SCULPT_EXPAND_VERTEX_NONE};
+
if (symm_it == 0) {
symm_vertex = original_vertex;
}
@@ -415,7 +425,7 @@ static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob,
* Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking
* symmetry into account.
*/
-static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v)
+static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const PBVHVertRef v)
{
return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX);
}
@@ -432,20 +442,23 @@ typedef struct ExpandFloodFillData {
} ExpandFloodFillData;
static bool expand_topology_floodfill_cb(
- SculptSession *UNUSED(ss), int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
ExpandFloodFillData *data = userdata;
if (!is_duplicate) {
- const float to_it = data->dists[from_v] + 1.0f;
- data->dists[to_v] = to_it;
+ const float to_it = data->dists[from_v_i] + 1.0f;
+ data->dists[to_v_i] = to_it;
}
else {
- data->dists[to_v] = data->dists[from_v];
+ data->dists[to_v_i] = data->dists[from_v_i];
}
return true;
}
-static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int v)
+static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const PBVHVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -470,23 +483,26 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons
* This creates falloff patterns that follow and snap to the hard edges of the object.
*/
static bool mask_expand_normal_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
ExpandFloodFillData *data = userdata;
if (!is_duplicate) {
float current_normal[3], prev_normal[3];
SCULPT_vertex_normal_get(ss, to_v, current_normal);
SCULPT_vertex_normal_get(ss, from_v, prev_normal);
- const float from_edge_factor = data->edge_factor[from_v];
- data->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * from_edge_factor;
- data->dists[to_v] = dot_v3v3(data->original_normal, current_normal) *
- powf(from_edge_factor, data->edge_sensitivity);
- CLAMP(data->dists[to_v], 0.0f, 1.0f);
+ const float from_edge_factor = data->edge_factor[from_v_i];
+ data->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) * from_edge_factor;
+ data->dists[to_v_i] = dot_v3v3(data->original_normal, current_normal) *
+ powf(from_edge_factor, data->edge_sensitivity);
+ CLAMP(data->dists[to_v_i], 0.0f, 1.0f);
}
else {
/* PBVH_GRIDS duplicate handling. */
- data->edge_factor[to_v] = data->edge_factor[from_v];
- data->dists[to_v] = data->dists[from_v];
+ data->edge_factor[to_v_i] = data->edge_factor[from_v_i];
+ data->dists[to_v_i] = data->dists[from_v_i];
}
return true;
@@ -494,7 +510,7 @@ static bool mask_expand_normal_floodfill_cb(
static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
Object *ob,
- const int v,
+ const PBVHVertRef v,
const float edge_sensitivity)
{
SculptSession *ss = ob->sculpt;
@@ -520,9 +536,11 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
for (int repeat = 0; repeat < 2; repeat++) {
for (int i = 0; i < totvert; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
float avg = 0.0f;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
avg += dists[ni.index];
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -539,7 +557,7 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd,
* Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into
* account.
*/
-static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_spherical_falloff_create(Object *ob, const PBVHVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -554,11 +572,14 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
- if (symm_vertex != -1) {
+ const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
+ if (symm_vertex.i != SCULPT_EXPAND_VERTEX_NONE) {
const float *co = SCULPT_vertex_co_get(ss, symm_vertex);
for (int i = 0; i < totvert; i++) {
- dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i)));
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, vertex)));
}
}
}
@@ -571,13 +592,13 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v)
* boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it
* stays parallel to the boundary, increasing the falloff value by 1 on each step.
*/
-static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const PBVHVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "spherical dist");
- BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
- GSQueue *queue = BLI_gsqueue_new(sizeof(int));
+ BLI_bitmap *visited_verts = BLI_BITMAP_NEW(totvert, "visited verts");
+ GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef));
/* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
@@ -586,16 +607,17 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
+ const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX);
if (!boundary) {
continue;
}
- for (int i = 0; i < boundary->num_vertices; i++) {
- BLI_gsqueue_push(queue, &boundary->vertices[i]);
- BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]);
+ for (int i = 0; i < boundary->verts_num; i++) {
+ BLI_gsqueue_push(queue, &boundary->verts[i]);
+ BLI_BITMAP_ENABLE(visited_verts, BKE_pbvh_vertex_to_index(ss->pbvh, boundary->verts[i]));
}
SCULPT_boundary_data_free(boundary);
}
@@ -607,23 +629,25 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
/* Propagate the values from the boundaries to the rest of the mesh. */
while (!BLI_gsqueue_is_empty(queue)) {
- int v_next;
+ PBVHVertRef v_next;
+
BLI_gsqueue_pop(queue, &v_next);
+ int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_next, ni) {
- if (BLI_BITMAP_TEST(visited_vertices, ni.index)) {
+ if (BLI_BITMAP_TEST(visited_verts, ni.index)) {
continue;
}
- dists[ni.index] = dists[v_next] + 1.0f;
- BLI_BITMAP_ENABLE(visited_vertices, ni.index);
- BLI_gsqueue_push(queue, &ni.index);
+ dists[ni.index] = dists[v_next_i] + 1.0f;
+ BLI_BITMAP_ENABLE(visited_verts, ni.index);
+ BLI_gsqueue_push(queue, &ni.vertex);
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
}
BLI_gsqueue_free(queue);
- MEM_freeN(visited_vertices);
+ MEM_freeN(visited_verts);
return dists;
}
@@ -632,7 +656,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i
* the base mesh faces when checking a vertex neighbor. For this reason, this is not implement
* using the general flood-fill and sculpt neighbors accessors.
*/
-static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
+static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertRef v)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
@@ -646,18 +670,20 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
}
/* Search and mask as visited the initial vertices using the enabled symmetry passes. */
- BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices");
- GSQueue *queue = BLI_gsqueue_new(sizeof(int));
+ BLI_bitmap *visited_verts = BLI_BITMAP_NEW(totvert, "visited verts");
+ GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef));
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char symm_it = 0; symm_it <= symm; symm_it++) {
if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) {
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v);
+ const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ ob, symm_it, v);
+ int symm_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, symm_vertex);
BLI_gsqueue_push(queue, &symm_vertex);
- BLI_BITMAP_ENABLE(visited_vertices, symm_vertex);
+ BLI_BITMAP_ENABLE(visited_verts, symm_vertex_i);
}
if (BLI_gsqueue_is_empty(queue)) {
@@ -665,26 +691,28 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v)
}
/* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */
- Mesh *mesh = ob->data;
while (!BLI_gsqueue_is_empty(queue)) {
- int v_next;
+ PBVHVertRef v_next;
BLI_gsqueue_pop(queue, &v_next);
- for (int j = 0; j < ss->pmap[v_next].count; j++) {
- MPoly *p = &ss->mpoly[ss->pmap[v_next].indices[j]];
+
+ int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next);
+
+ for (int j = 0; j < ss->pmap[v_next_i].count; j++) {
+ const MPoly *p = &ss->mpoly[ss->pmap[v_next_i].indices[j]];
for (int l = 0; l < p->totloop; l++) {
- const int neighbor_v = mesh->mloop[p->loopstart + l].v;
- if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) {
+ const PBVHVertRef neighbor_v = BKE_pbvh_make_vref(ss->mloop[p->loopstart + l].v);
+ if (BLI_BITMAP_TEST(visited_verts, neighbor_v.i)) {
continue;
}
- dists[neighbor_v] = dists[v_next] + 1.0f;
- BLI_BITMAP_ENABLE(visited_vertices, neighbor_v);
+ dists[neighbor_v.i] = dists[v_next_i] + 1.0f;
+ BLI_BITMAP_ENABLE(visited_verts, neighbor_v.i);
BLI_gsqueue_push(queue, &neighbor_v);
}
}
}
BLI_gsqueue_free(queue);
- MEM_freeN(visited_vertices);
+ MEM_freeN(visited_verts);
return dists;
}
@@ -701,11 +729,13 @@ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss,
const int totvert = SCULPT_vertex_count_get(ss);
expand_cache->max_vert_falloff = -FLT_MAX;
for (int i = 0; i < totvert; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
if (expand_cache->vert_falloff[i] == FLT_MAX) {
continue;
}
- if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
+ if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, vertex)) {
continue;
}
@@ -747,11 +777,11 @@ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss,
Mesh *mesh,
ExpandCache *expand_cache)
{
-
+ const MPoly *polys = BKE_mesh_polys(mesh);
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
for (int p = 0; p < mesh->totpoly; p++) {
- MPoly *poly = &mesh->mpoly[p];
+ const MPoly *poly = &polys[p];
float accum = 0.0f;
for (int l = 0; l < poly->totloop; l++) {
const int grid_loop_index = (poly->loopstart + l) * key->grid_area;
@@ -765,11 +795,14 @@ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss,
static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expand_cache)
{
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
+
for (int p = 0; p < mesh->totpoly; p++) {
- MPoly *poly = &mesh->mpoly[p];
+ const MPoly *poly = &polys[p];
float accum = 0.0f;
for (int l = 0; l < poly->totloop; l++) {
- MLoop *loop = &mesh->mloop[l + poly->loopstart];
+ const MLoop *loop = &loops[l + poly->loopstart];
accum += expand_cache->vert_falloff[loop->v];
}
expand_cache->face_falloff[p] = accum / poly->totloop;
@@ -810,27 +843,27 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s
*/
static void sculpt_expand_geodesics_from_state_boundary(Object *ob,
ExpandCache *expand_cache,
- BLI_bitmap *enabled_vertices)
+ BLI_bitmap *enabled_verts)
{
SculptSession *ss = ob->sculpt;
BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES);
- GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
- BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false);
+ GSet *initial_verts = BLI_gset_int_new("initial_verts");
+ BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled(ss, enabled_verts, false);
const int totvert = SCULPT_vertex_count_get(ss);
for (int i = 0; i < totvert; i++) {
- if (!BLI_BITMAP_TEST(boundary_vertices, i)) {
+ if (!BLI_BITMAP_TEST(boundary_verts, i)) {
continue;
}
- BLI_gset_add(initial_vertices, POINTER_FROM_INT(i));
+ BLI_gset_add(initial_verts, POINTER_FROM_INT(i));
}
- MEM_freeN(boundary_vertices);
+ MEM_freeN(boundary_verts);
MEM_SAFE_FREE(expand_cache->vert_falloff);
MEM_SAFE_FREE(expand_cache->face_falloff);
- expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX);
- BLI_gset_free(initial_vertices, NULL);
+ expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_verts, FLT_MAX);
+ BLI_gset_free(initial_verts, NULL);
}
/**
@@ -839,7 +872,7 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob,
*/
static void sculpt_expand_topology_from_state_boundary(Object *ob,
ExpandCache *expand_cache,
- BLI_bitmap *enabled_vertices)
+ BLI_bitmap *enabled_verts)
{
MEM_SAFE_FREE(expand_cache->vert_falloff);
MEM_SAFE_FREE(expand_cache->face_falloff);
@@ -848,17 +881,19 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob,
const int totvert = SCULPT_vertex_count_get(ss);
float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "topology dist");
- BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false);
+ BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled(ss, enabled_verts, false);
SculptFloodFill flood;
SCULPT_floodfill_init(ss, &flood);
for (int i = 0; i < totvert; i++) {
- if (!BLI_BITMAP_TEST(boundary_vertices, i)) {
+ if (!BLI_BITMAP_TEST(boundary_verts, i)) {
continue;
}
- SCULPT_floodfill_add_and_skip_initial(&flood, i);
+
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+ SCULPT_floodfill_add_and_skip_initial(&flood, vertex);
}
- MEM_freeN(boundary_vertices);
+ MEM_freeN(boundary_verts);
ExpandFloodFillData fdata;
fdata.dists = dists;
@@ -880,7 +915,7 @@ static void sculpt_expand_resursion_step_add(Object *ob,
return;
}
- BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache);
+ BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache);
/* Each time a new recursion step is created, reset the distortion strength. This is the expected
* result from the recursion, as otherwise the new falloff will render with undesired distortion
@@ -889,10 +924,10 @@ static void sculpt_expand_resursion_step_add(Object *ob,
switch (recursion_type) {
case SCULPT_EXPAND_RECURSION_GEODESICS:
- sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices);
+ sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_verts);
break;
case SCULPT_EXPAND_RECURSION_TOPOLOGY:
- sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_vertices);
+ sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_verts);
break;
}
@@ -902,7 +937,7 @@ static void sculpt_expand_resursion_step_add(Object *ob,
sculpt_expand_update_max_face_falloff_factor(ss, expand_cache);
}
- MEM_freeN(enabled_vertices);
+ MEM_freeN(enabled_verts);
}
/* Face Set Boundary falloff. */
@@ -919,30 +954,34 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
- BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices");
+ BLI_bitmap *enabled_verts = BLI_BITMAP_NEW(totvert, "enabled verts");
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_unique_face_set(ss, i)) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) {
continue;
}
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
+ if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) {
continue;
}
- BLI_BITMAP_ENABLE(enabled_vertices, i);
+ BLI_BITMAP_ENABLE(enabled_verts, i);
}
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
- sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices);
+ sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_verts);
}
else {
- sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_vertices);
+ sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_verts);
}
- MEM_freeN(enabled_vertices);
+ MEM_freeN(enabled_verts);
if (internal_falloff) {
for (int i = 0; i < totvert; i++) {
- if (!(SCULPT_vertex_has_face_set(ss, i, active_face_set) &&
- SCULPT_vertex_has_unique_face_set(ss, i))) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (!(SCULPT_vertex_has_face_set(ss, vertex, active_face_set) &&
+ SCULPT_vertex_has_unique_face_set(ss, vertex))) {
continue;
}
expand_cache->vert_falloff[i] *= -1.0f;
@@ -960,7 +999,9 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob,
}
else {
for (int i = 0; i < totvert; i++) {
- if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) {
continue;
}
expand_cache->vert_falloff[i] = 0.0f;
@@ -976,7 +1017,7 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create(
ExpandCache *expand_cache,
Sculpt *sd,
Object *ob,
- const int v,
+ const PBVHVertRef v,
eSculptExpandFalloffType falloff_type)
{
MEM_SAFE_FREE(expand_cache->vert_falloff);
@@ -1046,7 +1087,7 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss,
expand_cache->snap = false;
expand_cache->invert = false;
- BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache);
+ BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache);
const int totface = ss->totfaces;
for (int i = 0; i < totface; i++) {
@@ -1055,11 +1096,11 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss,
}
for (int p = 0; p < totface; p++) {
- MPoly *poly = &ss->mpoly[p];
+ const MPoly *poly = &ss->mpoly[p];
bool any_disabled = false;
for (int l = 0; l < poly->totloop; l++) {
- MLoop *loop = &ss->mloop[l + poly->loopstart];
- if (!BLI_BITMAP_TEST(enabled_vertices, loop->v)) {
+ const MLoop *loop = &ss->mloop[l + poly->loopstart];
+ if (!BLI_BITMAP_TEST(enabled_verts, loop->v)) {
any_disabled = true;
break;
}
@@ -1070,7 +1111,7 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss,
}
}
- MEM_freeN(enabled_vertices);
+ MEM_freeN(enabled_verts);
expand_cache->snap = prev_snap_state;
expand_cache->invert = prev_invert_state;
}
@@ -1128,7 +1169,7 @@ static void sculpt_expand_restore_color_data(SculptSession *ss, ExpandCache *exp
PBVHNode *node = nodes[n];
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) {
- SCULPT_vertex_color_set(ss, vd.index, expand_cache->original_colors[vd.index]);
+ SCULPT_vertex_color_set(ss, vd.vertex, expand_cache->original_colors[vd.index]);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_redraw(node);
@@ -1215,12 +1256,12 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
const float initial_mask = *vd.mask;
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index);
+ const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex);
float new_mask;
if (enabled) {
- new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
+ new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex);
}
else {
new_mask = 0.0f;
@@ -1236,9 +1277,6 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata,
*vd.mask = clamp_f(new_mask, 0.0f, 1.0f);
any_changed = true;
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
}
BKE_pbvh_vertex_iter_end;
if (any_changed) {
@@ -1287,13 +1325,13 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
float initial_color[4];
- SCULPT_vertex_color_get(ss, vd.index, initial_color);
+ SCULPT_vertex_color_get(ss, vd.vertex, initial_color);
- const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index);
+ const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex);
float fade;
if (enabled) {
- fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index);
+ fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex);
}
else {
fade = 0.0f;
@@ -1314,12 +1352,9 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata,
continue;
}
- SCULPT_vertex_color_set(ss, vd.index, final_color);
+ SCULPT_vertex_color_set(ss, vd.vertex, final_color);
any_changed = true;
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
}
BKE_pbvh_vertex_iter_end;
if (any_changed) {
@@ -1356,22 +1391,32 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c
/* Face Sets are always stored as they are needed for snapping. */
expand_cache->initial_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "initial face set");
expand_cache->original_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "original face set");
- for (int i = 0; i < totface; i++) {
- expand_cache->initial_face_sets[i] = ss->face_sets[i];
- expand_cache->original_face_sets[i] = ss->face_sets[i];
+ if (ss->face_sets) {
+ for (int i = 0; i < totface; i++) {
+ expand_cache->initial_face_sets[i] = ss->face_sets[i];
+ expand_cache->original_face_sets[i] = ss->face_sets[i];
+ }
+ }
+ else {
+ memset(expand_cache->initial_face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * totface);
+ memset(expand_cache->original_face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * totface);
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) {
expand_cache->original_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask");
for (int i = 0; i < totvert; i++) {
- expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, i);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, vertex);
}
}
if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) {
expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors");
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_color_get(ss, i, expand_cache->original_colors[i]);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_color_get(ss, vertex, expand_cache->original_colors[i]);
}
}
}
@@ -1394,14 +1439,16 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa
}
}
-static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int vertex)
+static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHVertRef vertex)
{
SculptSession *ss = ob->sculpt;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
ExpandCache *expand_cache = ss->expand_cache;
+ int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex);
+
/* Update the active factor in the cache. */
- if (vertex == SCULPT_EXPAND_VERTEX_NONE) {
+ if (vertex.i == SCULPT_EXPAND_VERTEX_NONE) {
/* This means that the cursor is not over the mesh, so a valid active falloff can't be
* determined. In this situations, don't evaluate enabled states and default all vertices in
* connected components to enabled. */
@@ -1409,7 +1456,7 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
expand_cache->all_enabled = true;
}
else {
- expand_cache->active_falloff = expand_cache->vert_falloff[vertex];
+ expand_cache->active_falloff = expand_cache->vert_falloff[vertex_i];
expand_cache->all_enabled = false;
}
@@ -1450,14 +1497,16 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v
* Updates the #SculptSession cursor data and gets the active vertex
* if the cursor is over the mesh.
*/
-static int sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, const float mval[2])
+static PBVHVertRef sculpt_expand_target_vertex_update_and_get(bContext *C,
+ Object *ob,
+ const float mval[2])
{
SculptSession *ss = ob->sculpt;
SculptCursorGeometryInfo sgi;
if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) {
return SCULPT_active_vertex_get(ss);
}
- return SCULPT_EXPAND_VERTEX_NONE;
+ return BKE_pbvh_make_vref(SCULPT_EXPAND_VERTEX_NONE);
}
/**
@@ -1472,7 +1521,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
const bool initial_invert_state = expand_cache->invert;
expand_cache->invert = false;
- BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache);
+ BLI_bitmap *enabled_verts = sculpt_expand_bitmap_from_enabled(ss, expand_cache);
/* For boundary topology, position the pivot using only the boundary of the enabled vertices,
* without taking mesh boundary into account. This allows to create deformations like bending the
@@ -1480,8 +1529,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
const float use_mesh_boundary = expand_cache->falloff_type !=
SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY;
- BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(
- ss, enabled_vertices, use_mesh_boundary);
+ BLI_bitmap *boundary_verts = sculpt_expand_boundary_from_enabled(
+ ss, enabled_verts, use_mesh_boundary);
/* Ignore invert state, as this is the expected behavior in most cases and mask are created in
* inverted state by default. */
@@ -1493,15 +1542,17 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
const float *expand_init_co = SCULPT_vertex_co_get(ss, expand_cache->initial_active_vertex);
for (int i = 0; i < totvert; i++) {
- if (!BLI_BITMAP_TEST(boundary_vertices, i)) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (!BLI_BITMAP_TEST(boundary_verts, i)) {
continue;
}
- if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) {
+ if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, vertex)) {
continue;
}
- const float *vertex_co = SCULPT_vertex_co_get(ss, i);
+ const float *vertex_co = SCULPT_vertex_co_get(ss, vertex);
if (!SCULPT_check_vertex_pivot_symmetry(vertex_co, expand_init_co, symm)) {
continue;
@@ -1511,8 +1562,8 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache
total++;
}
- MEM_freeN(enabled_vertices);
- MEM_freeN(boundary_vertices);
+ MEM_freeN(enabled_verts);
+ MEM_freeN(boundary_verts);
if (total > 0) {
mul_v3_v3fl(ss->pivot_pos, avg, 1.0f / total);
@@ -1556,9 +1607,8 @@ static void sculpt_expand_finish(bContext *C)
* Finds and stores in the #ExpandCache the sculpt connected component index for each symmetry pass
* needed for expand.
*/
-static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
- ExpandCache *expand_cache,
- const int initial_vertex)
+static void sculpt_expand_find_active_connected_components_from_vert(
+ Object *ob, ExpandCache *expand_cache, const PBVHVertRef initial_vertex)
{
SculptSession *ss = ob->sculpt;
for (int i = 0; i < EXPAND_SYMM_AREAS; i++) {
@@ -1571,11 +1621,13 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob,
continue;
}
- const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
+ const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(
ob, symm_it, initial_vertex);
+ int symm_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, symm_vertex);
+
expand_cache->active_connected_components[(int)symm_it] =
- ss->vertex_info.connected_component[symm_vertex];
+ ss->vertex_info.connected_component[symm_vertex_i];
}
}
@@ -1589,14 +1641,20 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C,
const float mval[2])
{
SculptSession *ss = ob->sculpt;
- int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval);
- if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) {
+
+ PBVHVertRef initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval);
+
+ if (initial_vertex.i == SCULPT_EXPAND_VERTEX_NONE) {
/* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active
* vertex in the sculpt session. */
initial_vertex = SCULPT_active_vertex_get(ss);
}
+
+ int initial_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, initial_vertex);
+
copy_v2_v2(ss->expand_cache->initial_mouse, mval);
expand_cache->initial_active_vertex = initial_vertex;
+ expand_cache->initial_active_vertex_i = initial_vertex_i;
expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss);
if (expand_cache->next_face_set == SCULPT_FACE_SET_NONE) {
@@ -1696,7 +1754,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event
/* Update and get the active vertex (and face) from the cursor. */
const float mval_fl[2] = {UNPACK2(event->mval)};
- const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval_fl);
+ const PBVHVertRef target_expand_vertex = sculpt_expand_target_vertex_update_and_get(
+ C, ob, mval_fl);
/* Handle the modal keymap state changes. */
ExpandCache *expand_cache = ss->expand_cache;
@@ -1888,6 +1947,8 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets,
{
const int totface = ss->totfaces;
MeshElemMap *pmap = ss->pmap;
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
/* Check that all the face sets IDs in the mesh are not equal to `delete_id`
* before attempting to delete it. */
@@ -1922,9 +1983,9 @@ static void sculpt_expand_delete_face_set_id(int *r_face_sets,
while (BLI_LINKSTACK_SIZE(queue)) {
const int f_index = POINTER_AS_INT(BLI_LINKSTACK_POP(queue));
int other_id = delete_id;
- const MPoly *c_poly = &mesh->mpoly[f_index];
+ const MPoly *c_poly = &polys[f_index];
for (int l = 0; l < c_poly->totloop; l++) {
- const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l];
+ const MLoop *c_loop = &loops[c_poly->loopstart + l];
const MeshElemMap *vert_map = &pmap[c_loop->v];
for (int i = 0; i < vert_map->count; i++) {
@@ -2064,6 +2125,16 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
return OPERATOR_CANCELLED;
}
+ if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) {
+ Mesh *mesh = ob->data;
+ ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
+ }
+
+ if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_MASK) {
+ MultiresModifierData *mmd = BKE_sculpt_multires_active(ss->scene, ob);
+ BKE_sculpt_mask_layers_ensure(ob, mmd);
+ }
+
/* Face Set operations are not supported in dyntopo. */
if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS &&
BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
@@ -2074,7 +2145,7 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even
sculpt_expand_ensure_sculptsession_data(ob);
/* Initialize undo. */
- SCULPT_undo_push_begin(ob, "expand");
+ SCULPT_undo_push_begin(ob, op);
sculpt_expand_undo_push(ob, ss->expand_cache);
/* Set the initial element for expand from the event position. */
@@ -2173,7 +2244,7 @@ void sculpt_expand_modal_keymap(wmKeyConfig *keyconf)
static const char *name = "Sculpt Expand Modal";
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, name);
- /* This function is called for each spacetype, only needs to add map once. */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.cc
index ce704e619ea..485375a5cb1 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc
@@ -5,11 +5,18 @@
* \ingroup edsculpt
*/
+#include <cmath>
+#include <cstdlib>
+#include <queue>
+
#include "MEM_guardedalloc.h"
-#include "BLI_blenlib.h"
+#include "BLI_bit_vector.hh"
+#include "BLI_function_ref.hh"
#include "BLI_hash.h"
#include "BLI_math.h"
+#include "BLI_math_vector.hh"
+#include "BLI_span.hh"
#include "BLI_task.h"
#include "DNA_brush_types.h"
@@ -19,6 +26,7 @@
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "BKE_attribute.hh"
#include "BKE_brush.h"
#include "BKE_ccg.h"
#include "BKE_colortools.h"
@@ -53,21 +61,19 @@
#include "bmesh.h"
-#include <math.h>
-#include <stdlib.h>
-
/* Utils. */
int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
{
- const int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ const int *face_sets = static_cast<const int *>(
+ CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
if (!face_sets) {
return SCULPT_FACE_SET_NONE;
}
int next_face_set_id = 0;
for (int i = 0; i < mesh->totpoly; i++) {
- next_face_set_id = max_ii(next_face_set_id, abs(face_sets[i]));
+ next_face_set_id = max_ii(next_face_set_id, face_sets[i]);
}
next_face_set_id++;
@@ -76,7 +82,7 @@ int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, const int new_id)
{
- int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ int *face_sets = static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
if (!face_sets) {
return;
}
@@ -109,7 +115,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
- SculptThreadedTaskData *data = userdata;
+ SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
const float bstrength = ss->cache->bstrength;
@@ -135,6 +141,10 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
if (!sculpt_brush_test_sq_fn(&test, poly_center)) {
continue;
}
+ const bool face_hidden = ss->hide_poly && ss->hide_poly[vert_map->indices[j]];
+ if (face_hidden) {
+ continue;
+ }
const float fade = bstrength * SCULPT_brush_strength_factor(ss,
brush,
vd.co,
@@ -142,11 +152,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
- if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) {
- ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
+ if (fade > 0.05f) {
+ ss->face_sets[vert_map->indices[j]] = ss->cache->paint_face_set;
}
}
}
@@ -161,11 +171,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
if (fade > 0.05f) {
- SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
+ SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set);
}
}
}
@@ -176,7 +186,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
{
- SculptThreadedTaskData *data = userdata;
+ SculptThreadedTaskData *data = static_cast<SculptThreadedTaskData *>(userdata);
SculptSession *ss = data->ob->sculpt;
const Brush *brush = data->brush;
float bstrength = ss->cache->bstrength;
@@ -199,7 +209,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
continue;
}
- if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
+ if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) {
continue;
}
@@ -210,12 +220,12 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -229,12 +239,11 @@ void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
BKE_curvemapping_init(brush->curve);
/* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
+ SculptThreadedTaskData data{};
+ data.sd = sd;
+ data.ob = ob;
+ data.brush = brush;
+ data.nodes = nodes;
TaskParallelSettings settings;
BKE_pbvh_parallel_range_settings(&settings, true, totnode);
@@ -287,7 +296,7 @@ static EnumPropertyItem prop_sculpt_face_set_create_types[] = {
"Face Set from Edit Mode Selection",
"Create an Face Set corresponding to the Edit Mode face selection",
},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
@@ -303,6 +312,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
+ ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
+
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
@@ -311,21 +323,24 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
int totnode;
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
if (!nodes) {
return OPERATOR_CANCELLED;
}
- SCULPT_undo_push_begin(ob, "face set change");
+ SCULPT_undo_push_begin(ob, op);
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
const int next_face_set = SCULPT_face_set_next_available_get(ss);
if (mode == SCULPT_FACE_SET_MASKED) {
for (int i = 0; i < tot_vert; i++) {
- if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (SCULPT_vertex_mask_get(ss, vertex) >= threshold &&
+ SCULPT_vertex_visible_get(ss, vertex)) {
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
}
@@ -337,47 +352,48 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
* sets and the performance hit of rendering the overlay. */
bool all_visible = true;
for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_visible_get(ss, i)) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_visible_get(ss, vertex)) {
all_visible = false;
break;
}
}
if (all_visible) {
- Mesh *mesh = ob->data;
mesh->face_sets_color_default = next_face_set;
BKE_pbvh_face_sets_color_set(
ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
}
for (int i = 0; i < tot_vert; i++) {
- if (SCULPT_vertex_visible_get(ss, i)) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (SCULPT_vertex_visible_get(ss, vertex)) {
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
}
if (mode == SCULPT_FACE_SET_ALL) {
for (int i = 0; i < tot_vert; i++) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_face_set_set(ss, vertex, next_face_set);
}
}
if (mode == SCULPT_FACE_SET_SELECTION) {
- Mesh *mesh = ob->data;
BMesh *bm;
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- .calc_vert_normal = true,
- }));
+ BMeshCreateParams create_params{};
+ create_params.use_toolflags = true;
+ bm = BM_mesh_create(&allocsize, &create_params);
+
+ BMeshFromMeshParams convert_params{};
+ convert_params.calc_vert_normal = true;
+ convert_params.calc_face_normal = true;
+ BM_mesh_bm_from_me(bm, mesh, &convert_params);
BMIter iter;
BMFace *f;
@@ -496,186 +512,103 @@ static EnumPropertyItem prop_sculpt_face_sets_init_types[] = {
"Create a Face Set per isolated Face Set",
},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
-typedef bool (*face_sets_flood_fill_test)(
- BMesh *bm, BMFace *from_f, BMEdge *from_e, BMFace *to_f, const float threshold);
+using FaceSetsFloodFillFn = blender::FunctionRef<bool(int from_face, int edge, int to_face)>;
-static bool sculpt_face_sets_init_loose_parts_test(BMesh *UNUSED(bm),
- BMFace *UNUSED(from_f),
- BMEdge *UNUSED(from_e),
- BMFace *UNUSED(to_f),
- const float UNUSED(threshold))
-{
- return true;
-}
-
-static bool sculpt_face_sets_init_normals_test(
- BMesh *UNUSED(bm), BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float threshold)
-{
- return fabsf(dot_v3v3(from_f->no, to_f->no)) > threshold;
-}
-
-static bool sculpt_face_sets_init_uv_seams_test(BMesh *UNUSED(bm),
- BMFace *UNUSED(from_f),
- BMEdge *from_e,
- BMFace *UNUSED(to_f),
- const float UNUSED(threshold))
-{
- return !BM_elem_flag_test(from_e, BM_ELEM_SEAM);
-}
-
-static bool sculpt_face_sets_init_crease_test(
- BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
-{
- return BM_elem_float_data_get(&bm->edata, from_e, CD_CREASE) < threshold;
-}
-
-static bool sculpt_face_sets_init_bevel_weight_test(
- BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
-{
- return BM_elem_float_data_get(&bm->edata, from_e, CD_BWEIGHT) < threshold;
-}
-
-static bool sculpt_face_sets_init_sharp_edges_test(BMesh *UNUSED(bm),
- BMFace *UNUSED(from_f),
- BMEdge *from_e,
- BMFace *UNUSED(to_f),
- const float UNUSED(threshold))
-{
- return BM_elem_flag_test(from_e, BM_ELEM_SMOOTH);
-}
-
-static bool sculpt_face_sets_init_face_set_boundary_test(
- BMesh *bm, BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float UNUSED(threshold))
-{
- const int cd_face_sets_offset = CustomData_get_offset(&bm->pdata, CD_SCULPT_FACE_SETS);
- return BM_ELEM_CD_GET_INT(from_f, cd_face_sets_offset) ==
- BM_ELEM_CD_GET_INT(to_f, cd_face_sets_offset);
-}
-
-static void sculpt_face_sets_init_flood_fill(Object *ob,
- face_sets_flood_fill_test test,
- const float threshold)
+static void sculpt_face_sets_init_flood_fill(Object *ob, const FaceSetsFloodFillFn &test_fn)
{
+ using namespace blender;
SculptSession *ss = ob->sculpt;
- Mesh *mesh = ob->data;
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- .calc_vert_normal = true,
- }));
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
- BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces");
- const int totfaces = mesh->totpoly;
+ BitVector<> visited_faces(mesh->totpoly, false);
int *face_sets = ss->face_sets;
- BM_mesh_elem_table_init(bm, BM_FACE);
- BM_mesh_elem_table_ensure(bm, BM_FACE);
+ const Span<MEdge> edges = mesh->edges();
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
+
+ if (!ss->epmap) {
+ BKE_mesh_edge_poly_map_create(&ss->epmap,
+ &ss->epmap_mem,
+ edges.data(),
+ edges.size(),
+ polys.data(),
+ polys.size(),
+ loops.data(),
+ loops.size());
+ }
int next_face_set = 1;
- for (int i = 0; i < totfaces; i++) {
- if (BLI_BITMAP_TEST(visited_faces, i)) {
+ for (const int i : polys.index_range()) {
+ if (visited_faces[i]) {
continue;
}
- GSQueue *queue;
- queue = BLI_gsqueue_new(sizeof(int));
+ std::queue<int> queue;
face_sets[i] = next_face_set;
- BLI_BITMAP_ENABLE(visited_faces, i);
- BLI_gsqueue_push(queue, &i);
-
- while (!BLI_gsqueue_is_empty(queue)) {
- int from_f;
- BLI_gsqueue_pop(queue, &from_f);
-
- BMFace *f, *f_neighbor;
- BMEdge *ed;
- BMIter iter_a, iter_b;
-
- f = BM_face_at_index(bm, from_f);
-
- BM_ITER_ELEM (ed, &iter_a, f, BM_EDGES_OF_FACE) {
- BM_ITER_ELEM (f_neighbor, &iter_b, ed, BM_FACES_OF_EDGE) {
- if (f_neighbor == f) {
+ visited_faces[i].set(true);
+ queue.push(i);
+
+ while (!queue.empty()) {
+ const int poly_i = queue.front();
+ const MPoly &poly = polys[poly_i];
+ queue.pop();
+
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ const int edge_i = loop.e;
+ const Span<int> neighbor_polys(ss->epmap[edge_i].indices, ss->epmap[edge_i].count);
+ for (const int neighbor_i : neighbor_polys) {
+ if (neighbor_i == poly_i) {
continue;
}
- int neighbor_face_index = BM_elem_index_get(f_neighbor);
- if (BLI_BITMAP_TEST(visited_faces, neighbor_face_index)) {
+ if (visited_faces[neighbor_i]) {
continue;
}
- if (!test(bm, f, ed, f_neighbor, threshold)) {
+ if (!test_fn(poly_i, edge_i, neighbor_i)) {
continue;
}
- face_sets[neighbor_face_index] = next_face_set;
- BLI_BITMAP_ENABLE(visited_faces, neighbor_face_index);
- BLI_gsqueue_push(queue, &neighbor_face_index);
+ face_sets[neighbor_i] = next_face_set;
+ visited_faces[neighbor_i].set(true);
+ queue.push(neighbor_i);
}
}
}
next_face_set += 1;
-
- BLI_gsqueue_free(queue);
}
-
- MEM_SAFE_FREE(visited_faces);
-
- BM_mesh_free(bm);
}
static void sculpt_face_sets_init_loop(Object *ob, const int mode)
{
- Mesh *mesh = ob->data;
+ using namespace blender;
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
SculptSession *ss = ob->sculpt;
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- .calc_vert_normal = true,
- }));
- BMIter iter;
- BMFace *f;
-
- const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
- ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1);
+ if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
+ const bke::AttributeAccessor attributes = mesh->attributes();
+ const VArraySpan<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ for (const int i : IndexRange(mesh->totpoly)) {
+ ss->face_sets[i] = material_indices[i] + 1;
}
- else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
- if (cd_fmaps_offset != -1) {
- ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2;
- }
- else {
- ss->face_sets[BM_elem_index_get(f)] = 1;
- }
+ }
+ else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
+ const int *face_maps = static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_FACEMAP));
+ for (const int i : IndexRange(mesh->totpoly)) {
+ ss->face_sets[i] = face_maps ? face_maps[i] : 1;
}
}
- BM_mesh_free(bm);
}
static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
{
+ using namespace blender;
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
@@ -692,52 +625,95 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
int totnode;
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
if (!nodes) {
return OPERATOR_CANCELLED;
}
- SCULPT_undo_push_begin(ob, "face set change");
+ SCULPT_undo_push_begin(ob, op);
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
const float threshold = RNA_float_get(op->ptr, "threshold");
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
+ ss->face_sets = BKE_sculpt_face_sets_ensure(mesh);
+ const bke::AttributeAccessor attributes = mesh->attributes();
+
switch (mode) {
- case SCULPT_FACE_SETS_FROM_LOOSE_PARTS:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold);
+ case SCULPT_FACE_SETS_FROM_LOOSE_PARTS: {
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ sculpt_face_sets_init_flood_fill(
+ ob, [&](const int from_face, const int /*edge*/, const int to_face) {
+ return hide_poly[from_face] == hide_poly[to_face];
+ });
break;
- case SCULPT_FACE_SETS_FROM_MATERIALS:
+ }
+ case SCULPT_FACE_SETS_FROM_MATERIALS: {
sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_MATERIALS);
break;
- case SCULPT_FACE_SETS_FROM_NORMALS:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_normals_test, threshold);
+ }
+ case SCULPT_FACE_SETS_FROM_NORMALS: {
+ const Span<float3> poly_normals(
+ reinterpret_cast<const float3 *>(BKE_mesh_poly_normals_ensure(mesh)), mesh->totpoly);
+ sculpt_face_sets_init_flood_fill(
+ ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool {
+ return std::abs(math::dot(poly_normals[from_face], poly_normals[to_face])) > threshold;
+ });
break;
- case SCULPT_FACE_SETS_FROM_UV_SEAMS:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_uv_seams_test, threshold);
+ }
+ case SCULPT_FACE_SETS_FROM_UV_SEAMS: {
+ const Span<MEdge> edges = mesh->edges();
+ sculpt_face_sets_init_flood_fill(
+ ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
+ return (edges[edge].flag & ME_SEAM) == 0;
+ });
break;
- case SCULPT_FACE_SETS_FROM_CREASES:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_crease_test, threshold);
+ }
+ case SCULPT_FACE_SETS_FROM_CREASES: {
+ const Span<MEdge> edges = mesh->edges();
+ sculpt_face_sets_init_flood_fill(
+ ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
+ return edges[edge].crease / 255.0f < threshold;
+ });
break;
- case SCULPT_FACE_SETS_FROM_SHARP_EDGES:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_sharp_edges_test, threshold);
+ }
+ case SCULPT_FACE_SETS_FROM_SHARP_EDGES: {
+ const Span<MEdge> edges = mesh->edges();
+ sculpt_face_sets_init_flood_fill(
+ ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
+ return (edges[edge].flag & ME_SHARP) == 0;
+ });
break;
- case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_bevel_weight_test, threshold);
+ }
+ case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT: {
+ const float *bevel_weights = static_cast<const float *>(
+ CustomData_get_layer(&mesh->edata, CD_BWEIGHT));
+ sculpt_face_sets_init_flood_fill(
+ ob, [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
+ return bevel_weights ? bevel_weights[edge] / 255.0f < threshold : true;
+ });
break;
- case SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES:
+ }
+ case SCULPT_FACE_SETS_FROM_FACE_SET_BOUNDARIES: {
+ Array<int> face_sets_copy(Span<int>(ss->face_sets, mesh->totpoly));
sculpt_face_sets_init_flood_fill(
- ob, sculpt_face_sets_init_face_set_boundary_test, threshold);
+ ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool {
+ return face_sets_copy[from_face] == face_sets_copy[to_face];
+ });
break;
- case SCULPT_FACE_SETS_FROM_FACE_MAPS:
+ }
+ case SCULPT_FACE_SETS_FROM_FACE_MAPS: {
sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_FACE_MAPS);
break;
+ }
}
SCULPT_undo_push_end(ob);
/* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
- SCULPT_visibility_sync_all_face_sets_to_vertices(ob);
+ SCULPT_visibility_sync_all_from_faces(ob);
for (int i = 0; i < totnode; i++) {
BKE_pbvh_node_mark_update_visibility(nodes[i]);
@@ -748,7 +724,7 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
MEM_SAFE_FREE(nodes);
if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
- BKE_mesh_flush_hidden_from_verts(ob->data);
+ BKE_mesh_flush_hidden_from_verts(mesh);
}
SCULPT_tag_update_overlays(C);
@@ -827,7 +803,7 @@ static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = {
"Show All Face Sets",
"Show All Face Sets",
},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
@@ -841,19 +817,25 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ if (!ss->face_sets) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Mesh *mesh = BKE_object_get_original_mesh(ob);
+
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
const int tot_vert = SCULPT_vertex_count_get(ss);
const int mode = RNA_enum_get(op->ptr, "mode");
const int active_face_set = SCULPT_active_face_set_get(ss);
- SCULPT_undo_push_begin(ob, "Hide area");
+ SCULPT_undo_push_begin(ob, op);
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
int totnode;
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
if (totnode == 0) {
MEM_SAFE_FREE(nodes);
@@ -869,44 +851,58 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
* be synced from face sets to non-manifold vertices. */
if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_visible_get(ss, i)) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (!SCULPT_vertex_visible_get(ss, vertex)) {
hidden_vertex = true;
break;
}
}
}
- for (int i = 0; i < ss->totfaces; i++) {
- if (ss->face_sets[i] <= 0) {
- hidden_vertex = true;
- break;
+ if (ss->hide_poly) {
+ for (int i = 0; i < ss->totfaces; i++) {
+ if (ss->hide_poly[i]) {
+ hidden_vertex = true;
+ break;
+ }
}
}
+ ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
+
if (hidden_vertex) {
- SCULPT_face_sets_visibility_all_set(ss, true);
+ SCULPT_face_visibility_all_set(ss, true);
}
else {
- SCULPT_face_sets_visibility_all_set(ss, false);
+ SCULPT_face_visibility_all_set(ss, false);
SCULPT_face_set_visibility_set(ss, active_face_set, true);
}
}
if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) {
- SCULPT_face_sets_visibility_all_set(ss, true);
+ /* As an optimization, free the hide attribute when making all geometry visible. This allows
+ * reduced memory usage without manually clearing it later, and allows sculpt operations to
+ * avoid checking element's hide status. */
+ CustomData_free_layer_named(&mesh->pdata, ".hide_poly", mesh->totpoly);
+ ss->hide_poly = nullptr;
+ BKE_pbvh_update_hide_attributes_from_mesh(pbvh);
}
if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) {
- SCULPT_face_sets_visibility_all_set(ss, false);
+ ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
+ SCULPT_face_visibility_all_set(ss, false);
SCULPT_face_set_visibility_set(ss, active_face_set, true);
}
if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) {
+ ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
SCULPT_face_set_visibility_set(ss, active_face_set, false);
}
if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) {
- SCULPT_face_sets_visibility_invert(ss);
+ ss->hide_poly = BKE_sculpt_hide_poly_ensure(mesh);
+ SCULPT_face_visibility_all_invert(ss);
}
/* For modes that use the cursor active vertex, update the rotation origin for viewport
@@ -922,7 +918,7 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
}
/* Sync face sets visibility and vertex visibility. */
- SCULPT_visibility_sync_all_face_sets_to_vertices(ob);
+ SCULPT_visibility_sync_all_from_faces(ob);
SCULPT_undo_push_end(ob);
@@ -949,7 +945,7 @@ static int sculpt_face_sets_change_visibility_invoke(bContext *C,
/* Update the active vertex and Face Set using the cursor position to avoid relying on the paint
* cursor updates. */
SculptCursorGeometryInfo sgi;
- const float mval_fl[2] = {UNPACK2(event->mval)};
+ const float mval_fl[2] = {(float)event->mval[0], (float)event->mval[1]};
SCULPT_vertex_random_access_ensure(ss);
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
@@ -989,10 +985,14 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE
return OPERATOR_CANCELLED;
}
+ if (!ss->face_sets) {
+ return OPERATOR_CANCELLED;
+ }
+
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
int totnode;
- Mesh *mesh = ob->data;
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
mesh->face_sets_color_seed += 1;
if (ss->face_sets) {
@@ -1003,7 +1003,7 @@ static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSE
}
BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
for (int i = 0; i < totnode; i++) {
BKE_pbvh_node_mark_redraw(nodes[i]);
}
@@ -1075,7 +1075,7 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
"Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
"vertex tangents",
},
- {0, NULL, 0, NULL, NULL},
+ {0, nullptr, 0, nullptr, nullptr},
};
static void sculpt_face_set_grow(Object *ob,
@@ -1085,13 +1085,16 @@ static void sculpt_face_set_grow(Object *ob,
const bool modify_hidden)
{
Mesh *mesh = BKE_mesh_from_object(ob);
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
+
for (int p = 0; p < mesh->totpoly; p++) {
if (!modify_hidden && prev_face_sets[p] <= 0) {
continue;
}
- const MPoly *c_poly = &mesh->mpoly[p];
+ const MPoly *c_poly = &polys[p];
for (int l = 0; l < c_poly->totloop; l++) {
- const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l];
+ const MLoop *c_loop = &loops[c_poly->loopstart + l];
const MeshElemMap *vert_map = &ss->pmap[c_loop->v];
for (int i = 0; i < vert_map->count; i++) {
const int neighbor_face_index = vert_map->indices[i];
@@ -1113,14 +1116,16 @@ static void sculpt_face_set_shrink(Object *ob,
const bool modify_hidden)
{
Mesh *mesh = BKE_mesh_from_object(ob);
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
for (int p = 0; p < mesh->totpoly; p++) {
if (!modify_hidden && prev_face_sets[p] <= 0) {
continue;
}
if (abs(prev_face_sets[p]) == active_face_set_id) {
- const MPoly *c_poly = &mesh->mpoly[p];
+ const MPoly *c_poly = &polys[p];
for (int l = 0; l < c_poly->totloop; l++) {
- const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l];
+ const MLoop *c_loop = &loops[c_poly->loopstart + l];
const MeshElemMap *vert_map = &ss->pmap[c_loop->v];
for (int i = 0; i < vert_map->count; i++) {
const int neighbor_face_index = vert_map->indices[i];
@@ -1138,18 +1143,21 @@ static void sculpt_face_set_shrink(Object *ob,
static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only)
{
-
+ if (face_sets == nullptr) {
+ return true;
+ }
int first_face_set = SCULPT_FACE_SET_NONE;
if (check_visible_only) {
for (int f = 0; f < ss->totfaces; f++) {
- if (face_sets[f] > 0) {
- first_face_set = face_sets[f];
- break;
+ if (ss->hide_poly && ss->hide_poly[f]) {
+ continue;
}
+ first_face_set = face_sets[f];
+ break;
}
}
else {
- first_face_set = abs(face_sets[0]);
+ first_face_set = face_sets[0];
}
if (first_face_set == SCULPT_FACE_SET_NONE) {
@@ -1157,8 +1165,10 @@ static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool
}
for (int f = 0; f < ss->totfaces; f++) {
- const int face_set_id = check_visible_only ? face_sets[f] : abs(face_sets[f]);
- if (face_set_id != first_face_set) {
+ if (check_visible_only && ss->hide_poly && ss->hide_poly[f]) {
+ continue;
+ }
+ if (face_sets[f] != first_face_set) {
return false;
}
}
@@ -1171,19 +1181,16 @@ static void sculpt_face_set_delete_geometry(Object *ob,
const bool modify_hidden)
{
- Mesh *mesh = ob->data;
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- BMesh *bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- .calc_vert_normal = true,
- }));
+ BMeshCreateParams create_params{};
+ create_params.use_toolflags = true;
+ BMesh *bm = BM_mesh_create(&allocsize, &create_params);
+
+ BMeshFromMeshParams convert_params{};
+ convert_params.calc_vert_normal = true;
+ convert_params.calc_face_normal = true;
+ BM_mesh_bm_from_me(bm, mesh, &convert_params);
BM_mesh_elem_table_init(bm, BM_FACE);
BM_mesh_elem_table_ensure(bm, BM_FACE);
@@ -1192,44 +1199,45 @@ static void sculpt_face_set_delete_geometry(Object *ob,
BMFace *f;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
const int face_index = BM_elem_index_get(f);
- const int face_set_id = modify_hidden ? abs(ss->face_sets[face_index]) :
- ss->face_sets[face_index];
- BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id);
+ if (!modify_hidden && ss->hide_poly && ss->hide_poly[face_index]) {
+ continue;
+ }
+ BM_elem_flag_set(f, BM_ELEM_TAG, ss->face_sets[face_index] == active_face_set_id);
}
BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES);
BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
- BM_mesh_bm_to_me(NULL,
- bm,
- ob->data,
- (&(struct BMeshToMeshParams){
- .calc_object_remap = false,
- }));
+ BMeshToMeshParams bmesh_to_mesh_params{};
+ bmesh_to_mesh_params.calc_object_remap = false;
+ BM_mesh_bm_to_me(nullptr, bm, mesh, &bmesh_to_mesh_params);
BM_mesh_free(bm);
}
static void sculpt_face_set_edit_fair_face_set(Object *ob,
const int active_face_set_id,
- const int fair_order)
+ const eMeshFairingDepth fair_order)
{
SculptSession *ss = ob->sculpt;
const int totvert = SCULPT_vertex_count_get(ss);
- Mesh *mesh = ob->data;
- bool *fair_vertices = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices");
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
+ bool *fair_verts = static_cast<bool *>(
+ MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"));
SCULPT_boundary_info_ensure(ob);
for (int i = 0; i < totvert; i++) {
- fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) &&
- SCULPT_vertex_has_face_set(ss, i, active_face_set_id) &&
- SCULPT_vertex_has_unique_face_set(ss, i);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ fair_verts[i] = !SCULPT_vertex_is_boundary(ss, vertex) &&
+ SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) &&
+ SCULPT_vertex_has_unique_face_set(ss, vertex);
}
MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
- BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order);
- MEM_freeN(fair_vertices);
+ BKE_mesh_prefair_and_fair_verts(mesh, mvert, fair_verts, fair_order);
+ MEM_freeN(fair_verts);
}
static void sculpt_face_set_apply_edit(Object *ob,
@@ -1241,13 +1249,13 @@ static void sculpt_face_set_apply_edit(Object *ob,
switch (mode) {
case SCULPT_FACE_SET_EDIT_GROW: {
- int *prev_face_sets = MEM_dupallocN(ss->face_sets);
+ int *prev_face_sets = static_cast<int *>(MEM_dupallocN(ss->face_sets));
sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
MEM_SAFE_FREE(prev_face_sets);
break;
}
case SCULPT_FACE_SET_EDIT_SHRINK: {
- int *prev_face_sets = MEM_dupallocN(ss->face_sets);
+ int *prev_face_sets = static_cast<int *>(MEM_dupallocN(ss->face_sets));
sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
MEM_SAFE_FREE(prev_face_sets);
break;
@@ -1304,23 +1312,26 @@ static void sculpt_face_set_edit_modify_geometry(bContext *C,
Object *ob,
const int active_face_set,
const eSculptFaceSetEditMode mode,
- const bool modify_hidden)
+ const bool modify_hidden,
+ wmOperator *op)
{
- ED_sculpt_undo_geometry_begin(ob, "edit face set delete geometry");
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
+ ED_sculpt_undo_geometry_begin(ob, op);
sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden);
ED_sculpt_undo_geometry_end(ob);
- BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
+ BKE_mesh_batch_cache_dirty_tag(mesh, BKE_MESH_BATCH_DIRTY_ALL);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh);
}
static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ss->pbvh;
+ Mesh *mesh = static_cast<Mesh *>(ob->data);
/* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
- SCULPT_visibility_sync_all_face_sets_to_vertices(ob);
+ SCULPT_visibility_sync_all_from_faces(ob);
for (int i = 0; i < totnode; i++) {
BKE_pbvh_node_mark_update_visibility(nodes[i]);
@@ -1329,24 +1340,25 @@ static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **node
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
- BKE_mesh_flush_hidden_from_verts(ob->data);
+ BKE_mesh_flush_hidden_from_verts(mesh);
}
}
static void sculpt_face_set_edit_modify_face_sets(Object *ob,
const int active_face_set,
const eSculptFaceSetEditMode mode,
- const bool modify_hidden)
+ const bool modify_hidden,
+ wmOperator *op)
{
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode **nodes;
int totnode;
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
if (!nodes) {
return;
}
- SCULPT_undo_push_begin(ob, "face set edit");
+ SCULPT_undo_push_begin(ob, op);
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden);
SCULPT_undo_push_end(ob);
@@ -1357,15 +1369,16 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob,
static void sculpt_face_set_edit_modify_coordinates(bContext *C,
Object *ob,
const int active_face_set,
- const eSculptFaceSetEditMode mode)
+ const eSculptFaceSetEditMode mode,
+ wmOperator *op)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = ob->sculpt;
PBVH *pbvh = ss->pbvh;
PBVHNode **nodes;
int totnode;
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
- SCULPT_undo_push_begin(ob, "face set edit");
+ BKE_pbvh_search_gather(pbvh, nullptr, nullptr, &nodes, &totnode);
+ SCULPT_undo_push_begin(ob, op);
for (int i = 0; i < totnode; i++) {
BKE_pbvh_node_mark_update(nodes[i]);
SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS);
@@ -1387,19 +1400,21 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
SculptSession *ss = ob->sculpt;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- const int mode = RNA_enum_get(op->ptr, "mode");
+ const eSculptFaceSetEditMode mode = static_cast<eSculptFaceSetEditMode>(
+ RNA_enum_get(op->ptr, "mode"));
const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
if (!sculpt_face_set_edit_is_operation_valid(ss, mode, modify_hidden)) {
return OPERATOR_CANCELLED;
}
+ ss->face_sets = BKE_sculpt_face_sets_ensure(BKE_mesh_from_object(ob));
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
/* Update the current active Face Set and Vertex as the operator can be used directly from the
* tool without brush cursor. */
SculptCursorGeometryInfo sgi;
- const float mval_fl[2] = {UNPACK2(event->mval)};
+ const float mval_fl[2] = {(float)event->mval[0], (float)event->mval[1]};
if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
/* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */
return OPERATOR_CANCELLED;
@@ -1408,15 +1423,15 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
switch (mode) {
case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY:
- sculpt_face_set_edit_modify_geometry(C, ob, active_face_set, mode, modify_hidden);
+ sculpt_face_set_edit_modify_geometry(C, ob, active_face_set, mode, modify_hidden, op);
break;
case SCULPT_FACE_SET_EDIT_GROW:
case SCULPT_FACE_SET_EDIT_SHRINK:
- sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden);
+ sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden, op);
break;
case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
- sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode);
+ sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode, op);
break;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index a5d9f5306e2..161fc563950 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -93,7 +93,7 @@ static void color_filter_task_cb(void *__restrict userdata,
const int mode = data->filter_type;
SculptOrigVertData orig_data;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COORDS);
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n], SCULPT_UNDO_COLOR);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
@@ -104,7 +104,7 @@ static void color_filter_task_cb(void *__restrict userdata,
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f) {
continue;
}
@@ -189,10 +189,10 @@ static void color_filter_task_cb(void *__restrict userdata,
case COLOR_FILTER_SMOOTH: {
fade = clamp_f(fade, -1.0f, 1.0f);
float smooth_color[4];
- SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex);
float col[4];
- SCULPT_vertex_color_get(ss, vd.index, col);
+ SCULPT_vertex_color_get(ss, vd.vertex, col);
if (fade < 0.0f) {
interp_v4_v4v4(smooth_color, smooth_color, col, 0.5f);
@@ -224,11 +224,7 @@ static void color_filter_task_cb(void *__restrict userdata,
}
}
- SCULPT_vertex_color_set(ss, vd.index, final_color);
-
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
+ SCULPT_vertex_color_set(ss, vd.vertex, final_color);
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_color(data->nodes[n]);
@@ -244,7 +240,8 @@ static void sculpt_color_presmooth_init(SculptSession *ss)
}
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_color_get(ss, i, ss->filter_cache->pre_smoothed_color[i]);
+ SCULPT_vertex_color_get(
+ ss, BKE_pbvh_index_to_vertex(ss->pbvh, i), ss->filter_cache->pre_smoothed_color[i]);
}
for (int iteration = 0; iteration < 2; iteration++) {
@@ -253,7 +250,7 @@ static void sculpt_color_presmooth_init(SculptSession *ss)
int total = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, BKE_pbvh_index_to_vertex(ss->pbvh, i), ni) {
float col[4] = {0};
copy_v4_v4(col, ss->filter_cache->pre_smoothed_color[ni.index]);
@@ -349,7 +346,7 @@ static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
}
- SCULPT_undo_push_begin(ob, "color filter");
+ SCULPT_undo_push_begin(ob, op);
BKE_sculpt_color_layer_create_if_needed(ob);
/* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
index ea3f56d0859..bb27e4f1e9e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
@@ -14,6 +14,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
@@ -103,7 +104,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
switch (mode) {
case MASK_FILTER_SMOOTH:
case MASK_FILTER_SHARPEN: {
- float val = SCULPT_neighbor_mask_average(ss, vd.index);
+ float val = SCULPT_neighbor_mask_average(ss, vd.vertex);
val -= *vd.mask;
@@ -123,7 +124,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
}
case MASK_FILTER_GROW:
max = 0.0f;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
if (vmask_f > max) {
max = vmask_f;
@@ -134,7 +135,7 @@ static void mask_filter_task_cb(void *__restrict userdata,
break;
case MASK_FILTER_SHRINK:
min = 1.0f;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
if (vmask_f < min) {
min = vmask_f;
@@ -162,9 +163,6 @@ static void mask_filter_task_cb(void *__restrict userdata,
if (*vd.mask != prev_val) {
update = true;
}
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
}
BKE_pbvh_vertex_iter_end;
@@ -177,11 +175,15 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ const Scene *scene = CTX_data_scene(C);
PBVHNode **nodes;
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
int totnode;
int filter_type = RNA_enum_get(op->ptr, "filter_type");
+ MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
+ BKE_sculpt_mask_layers_ensure(ob, mmd);
+
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
SculptSession *ss = ob->sculpt;
@@ -196,7 +198,7 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
int num_verts = SCULPT_vertex_count_get(ss);
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
- SCULPT_undo_push_begin(ob, "Mask Filter");
+ SCULPT_undo_push_begin(ob, op);
for (int i = 0; i < totnode; i++) {
SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK);
@@ -217,7 +219,8 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op)
if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) {
prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask");
for (int j = 0; j < num_verts; j++) {
- prev_mask[j] = SCULPT_vertex_mask_get(ss, j);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, j);
+ prev_mask[j] = SCULPT_vertex_mask_get(ss, vertex);
}
}
@@ -308,9 +311,9 @@ static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd)
zero_v3(avg);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) {
float normalized[3];
- sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.index), vd->co);
+ sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.vertex), vd->co);
normalize_v3(normalized);
add_v3_v3(avg, normalized);
total++;
@@ -386,10 +389,6 @@ static void dirty_mask_apply_task_cb(void *__restrict userdata,
mask = fminf(mask, 0.5f) * 2.0f;
}
*vd.mask = CLAMPIS(mask, 0.0f, 1.0f);
-
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
}
BKE_pbvh_vertex_iter_end;
BKE_pbvh_node_mark_update_mask(node);
@@ -415,7 +414,7 @@ static int sculpt_dirty_mask_exec(bContext *C, wmOperator *op)
}
BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
- SCULPT_undo_push_begin(ob, "Dirty Mask");
+ SCULPT_undo_push_begin(ob, op);
for (int i = 0; i < totnode; i++) {
SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK);
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index c0adf5aef20..e576cfda3af 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -296,7 +296,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) {
/* Surface Smooth can't skip the loop for this vertex as it needs to calculate its
@@ -314,7 +314,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
}
if (filter_type == MESH_FILTER_RELAX_FACE_SETS) {
- if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
+ if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) {
continue;
}
}
@@ -322,7 +322,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
switch (filter_type) {
case MESH_FILTER_SMOOTH:
fade = clamp_f(fade, -1.0f, 1.0f);
- SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex);
sub_v3_v3v3(val, avg, orig_co);
madd_v3_v3v3fl(val, orig_co, val, fade);
sub_v3_v3v3(disp, val, orig_co);
@@ -385,7 +385,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
disp,
vd.co,
ss->filter_cache->surface_smooth_laplacian_disp,
- vd.index,
+ vd.vertex,
orig_data.co,
ss->filter_cache->surface_smooth_shape_preservation);
break;
@@ -399,10 +399,10 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float disp_sharpen[3] = {0.0f, 0.0f, 0.0f};
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float disp_n[3];
sub_v3_v3v3(
- disp_n, SCULPT_vertex_co_get(ss, ni.index), SCULPT_vertex_co_get(ss, vd.index));
+ disp_n, SCULPT_vertex_co_get(ss, ni.vertex), SCULPT_vertex_co_get(ss, vd.vertex));
mul_v3_fl(disp_n, ss->filter_cache->sharpen_factor[ni.index]);
add_v3_v3(disp_sharpen, disp_n);
}
@@ -412,7 +412,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
float disp_avg[3];
float avg_co[3];
- SCULPT_neighbor_coords_average(ss, avg_co, vd.index);
+ SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex);
sub_v3_v3v3(disp_avg, avg_co, vd.co);
mul_v3_v3fl(
disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index]));
@@ -457,7 +457,7 @@ static void mesh_filter_task_cb(void *__restrict userdata,
}
copy_v3_v3(vd.co, final_pos);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -473,9 +473,11 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss)
filter_cache->detail_directions = MEM_malloc_arrayN(
totvert, sizeof(float[3]), "detail directions");
for (int i = 0; i < totvert; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SCULPT_neighbor_coords_average(ss, avg, vertex);
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -500,7 +502,9 @@ static void mesh_filter_init_limit_surface_co(SculptSession *ss)
filter_cache->limit_surface_co = MEM_malloc_arrayN(
totvert, sizeof(float[3]), "limit surface co");
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_limit_surface_get(ss, vertex, filter_cache->limit_surface_co[i]);
}
}
@@ -520,9 +524,11 @@ static void mesh_filter_sharpen_init(SculptSession *ss,
totvert, sizeof(float[3]), "sharpen detail direction");
for (int i = 0; i < totvert; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SCULPT_neighbor_coords_average(ss, avg, vertex);
+ sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex));
filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]);
}
@@ -544,12 +550,14 @@ static void mesh_filter_sharpen_init(SculptSession *ss,
smooth_iterations < filter_cache->sharpen_curvature_smooth_iterations;
smooth_iterations++) {
for (int i = 0; i < totvert; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
float direction_avg[3] = {0.0f, 0.0f, 0.0f};
float sharpen_avg = 0;
int total = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]);
sharpen_avg += filter_cache->sharpen_factor[ni.index];
total++;
@@ -576,7 +584,7 @@ static void mesh_filter_surface_smooth_displace_task_cb(
float fade = vd.mask ? *vd.mask : 0.0f;
fade = 1.0f - fade;
fade *= data->filter_strength;
- fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
+ fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex);
if (fade == 0.0f) {
continue;
}
@@ -584,7 +592,7 @@ static void mesh_filter_surface_smooth_displace_task_cb(
SCULPT_surface_smooth_displace_step(ss,
vd.co,
ss->filter_cache->surface_smooth_laplacian_disp,
- vd.index,
+ vd.vertex,
ss->filter_cache->surface_smooth_current_vertex,
clamp_f(fade, 0.0f, 1.0f));
}
@@ -686,7 +694,7 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent
SCULPT_boundary_info_ensure(ob);
}
- SCULPT_undo_push_begin(ob, "Mesh Filter");
+ SCULPT_undo_push_begin(ob, op);
SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS);
diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c
index 1beb5d48961..5dd602bc36d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c
+++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c
@@ -37,7 +37,6 @@
#include "DEG_depsgraph.h"
#include "WM_api.h"
-#include "WM_message.h"
#include "WM_toolsystem.h"
#include "WM_types.h"
@@ -62,9 +61,9 @@
/* Propagate distance from v1 and v2 to v0. */
static bool sculpt_geodesic_mesh_test_dist_add(
- MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_vertices)
+ MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_verts)
{
- if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) {
+ if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(v0))) {
return false;
}
@@ -97,7 +96,7 @@ static bool sculpt_geodesic_mesh_test_dist_add(
}
static float *SCULPT_geodesic_mesh_create(Object *ob,
- GSet *initial_vertices,
+ GSet *initial_verts,
const float limit_radius)
{
SculptSession *ss = ob->sculpt;
@@ -108,8 +107,10 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
const float limit_radius_sq = limit_radius * limit_radius;
- MEdge *edges = mesh->medge;
MVert *verts = SCULPT_mesh_deformed_mverts_get(ss);
+ const MEdge *edges = BKE_mesh_edges(mesh);
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag");
@@ -117,16 +118,15 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
if (!ss->epmap) {
BKE_mesh_edge_poly_map_create(&ss->epmap,
&ss->epmap_mem,
- mesh->medge,
+ edges,
mesh->totedge,
- mesh->mpoly,
+ polys,
mesh->totpoly,
- mesh->mloop,
+ loops,
mesh->totloop);
}
if (!ss->vemap) {
- BKE_mesh_vert_edge_map_create(
- &ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge);
+ BKE_mesh_vert_edge_map_create(&ss->vemap, &ss->vemap_mem, edges, mesh->totvert, mesh->totedge);
}
/* Both contain edge indices encoded as *void. */
@@ -137,7 +137,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
BLI_LINKSTACK_INIT(queue_next);
for (int i = 0; i < totvert; i++) {
- if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) {
+ if (BLI_gset_haskey(initial_verts, POINTER_FROM_INT(i))) {
dists[i] = 0.0f;
}
else {
@@ -159,7 +159,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
/* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When
* this optimization is needed, it is expected for the tool to request the distance to a low
* number of vertices (usually just 1 or 2). */
- GSET_ITER (gs_iter, initial_vertices) {
+ GSET_ITER (gs_iter, initial_verts) {
const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
float *v_co = verts[v].co;
for (int i = 0; i < totvert; i++) {
@@ -193,25 +193,24 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
SWAP(int, v1, v2);
}
sculpt_geodesic_mesh_test_dist_add(
- verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_vertices);
+ verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_verts);
}
if (ss->epmap[e].count != 0) {
for (int poly_map_index = 0; poly_map_index < ss->epmap[e].count; poly_map_index++) {
const int poly = ss->epmap[e].indices[poly_map_index];
- if (ss->face_sets[poly] <= 0) {
+ if (ss->hide_poly && ss->hide_poly[poly]) {
continue;
}
- const MPoly *mpoly = &mesh->mpoly[poly];
+ const MPoly *mpoly = &polys[poly];
for (int loop_index = 0; loop_index < mpoly->totloop; loop_index++) {
- const MLoop *mloop = &mesh->mloop[loop_index + mpoly->loopstart];
+ const MLoop *mloop = &loops[loop_index + mpoly->loopstart];
const int v_other = mloop->v;
if (ELEM(v_other, v1, v2)) {
continue;
}
- if (sculpt_geodesic_mesh_test_dist_add(
- verts, v_other, v1, v2, dists, initial_vertices)) {
+ if (sculpt_geodesic_mesh_test_dist_add(verts, v_other, v1, v2, dists, initial_verts)) {
for (int edge_map_index = 0; edge_map_index < ss->vemap[v_other].count;
edge_map_index++) {
const int e_other = ss->vemap[v_other].indices[edge_map_index];
@@ -258,7 +257,7 @@ static float *SCULPT_geodesic_mesh_create(Object *ob,
/* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the
* distance to each vertex. In this case, only one of the initial vertices will be used to
* calculate the distance. */
-static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices)
+static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_verts)
{
SculptSession *ss = ob->sculpt;
@@ -267,7 +266,7 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances");
int first_affected = SCULPT_GEODESIC_VERTEX_NONE;
GSetIterator gs_iter;
- GSET_ITER (gs_iter, initial_vertices) {
+ GSET_ITER (gs_iter, initial_verts) {
first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
break;
}
@@ -279,25 +278,26 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices
return dists;
}
- const float *first_affected_co = SCULPT_vertex_co_get(ss, first_affected);
+ const float *first_affected_co = SCULPT_vertex_co_get(
+ ss, BKE_pbvh_index_to_vertex(ss->pbvh, first_affected));
for (int i = 0; i < totvert; i++) {
- dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, i));
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, vertex));
}
return dists;
}
-float *SCULPT_geodesic_distances_create(Object *ob,
- GSet *initial_vertices,
- const float limit_radius)
+float *SCULPT_geodesic_distances_create(Object *ob, GSet *initial_verts, const float limit_radius)
{
SculptSession *ss = ob->sculpt;
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
- return SCULPT_geodesic_mesh_create(ob, initial_vertices, limit_radius);
+ return SCULPT_geodesic_mesh_create(ob, initial_verts, limit_radius);
case PBVH_BMESH:
case PBVH_GRIDS:
- return SCULPT_geodesic_fallback_create(ob, initial_vertices);
+ return SCULPT_geodesic_fallback_create(ob, initial_verts);
}
BLI_assert(false);
return NULL;
@@ -305,16 +305,17 @@ float *SCULPT_geodesic_distances_create(Object *ob,
float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
Object *ob,
- const int vertex,
+ const PBVHVertRef vertex,
const float limit_radius)
{
SculptSession *ss = ob->sculpt;
- GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
+ GSet *initial_verts = BLI_gset_int_new("initial_verts");
const char symm = SCULPT_mesh_symmetry_xyz_get(ob);
for (char i = 0; i <= symm; ++i) {
if (SCULPT_is_symmetry_iteration_valid(i, symm)) {
- int v = -1;
+ PBVHVertRef v = {PBVH_REF_NONE};
+
if (i == 0) {
v = vertex;
}
@@ -323,22 +324,23 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd,
flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i);
v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false);
}
- if (v != -1) {
- BLI_gset_add(initial_vertices, POINTER_FROM_INT(v));
+ if (v.i != PBVH_REF_NONE) {
+ BLI_gset_add(initial_verts, POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ss->pbvh, v)));
}
}
}
- float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius);
- BLI_gset_free(initial_vertices, NULL);
+ float *dists = SCULPT_geodesic_distances_create(ob, initial_verts, limit_radius);
+ BLI_gset_free(initial_verts, NULL);
return dists;
}
-float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius)
+float *SCULPT_geodesic_from_vertex(Object *ob, const PBVHVertRef vertex, const float limit_radius)
{
- GSet *initial_vertices = BLI_gset_int_new("initial_vertices");
- BLI_gset_add(initial_vertices, POINTER_FROM_INT(vertex));
- float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius);
- BLI_gset_free(initial_vertices, NULL);
+ GSet *initial_verts = BLI_gset_int_new("initial_verts");
+ BLI_gset_add(initial_verts,
+ POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ob->sculpt->pbvh, vertex)));
+ float *dists = SCULPT_geodesic_distances_create(ob, initial_verts, limit_radius);
+ BLI_gset_free(initial_verts, NULL);
return dists;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 8485c7fcbf9..cdfa9c2586f 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -59,10 +59,13 @@ typedef struct SculptCursorGeometryInfo {
typedef struct SculptVertexNeighborIter {
/* Storage */
- int *neighbors;
+ PBVHVertRef *neighbors;
+ int *neighbor_indices;
int size;
int capacity;
- int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY];
+
+ PBVHVertRef neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY];
+ int neighbor_indices_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY];
/* Internal iterator. */
int num_duplicates;
@@ -70,6 +73,7 @@ typedef struct SculptVertexNeighborIter {
/* Public */
int index;
+ PBVHVertRef vertex;
bool is_duplicate;
} SculptVertexNeighborIter;
@@ -93,7 +97,7 @@ typedef struct {
/* Flood Fill. */
typedef struct {
GSQueue *queue;
- BLI_bitmap *visited_vertices;
+ BLI_bitmap *visited_verts;
} SculptFloodFill;
typedef enum eBoundaryAutomaskMode {
@@ -318,7 +322,7 @@ typedef struct SculptThreadedTaskData {
bool mask_by_color_preserve_mask;
/* Index of the vertex that is going to be used as a reference for the colors. */
- int mask_by_color_vertex;
+ PBVHVertRef mask_by_color_vertex;
float *mask_by_color_floodfill;
int face_set;
@@ -398,9 +402,6 @@ typedef struct AutomaskingSettings {
typedef struct AutomaskingCache {
AutomaskingSettings settings;
- /* Precomputed auto-mask factor indexed by vertex, owned by the auto-masking system and
- * initialized in #SCULPT_automasking_cache_init when needed. */
- float *factor;
} AutomaskingCache;
typedef struct FilterCache {
@@ -485,6 +486,7 @@ typedef struct StrokeCache {
float true_last_location[3];
float location[3];
float last_location[3];
+ float stroke_distance;
/* Used for alternating between deformation in brushes that need to apply different ones to
* achieve certain effects. */
@@ -690,7 +692,8 @@ typedef struct ExpandCache {
* during the execution of Expand by moving the origin. */
float initial_mouse_move[2];
float initial_mouse[2];
- int initial_active_vertex;
+ PBVHVertRef initial_active_vertex;
+ int initial_active_vertex_i;
int initial_active_face_set;
/* Maximum number of vertices allowed in the SculptSession for previewing the falloff using
@@ -845,7 +848,10 @@ void SCULPT_tag_update_overlays(bContext *C);
* (This allows us to ignore the GL depth buffer)
* Returns 0 if the ray doesn't hit the mesh, non-zero otherwise.
*/
-bool SCULPT_stroke_get_location(struct bContext *C, float out[3], const float mouse[2]);
+bool SCULPT_stroke_get_location(struct bContext *C,
+ float out[3],
+ const float mouse[2],
+ bool force_original);
/**
* Gets the normal, location and active vertex location of the geometry under the cursor. This also
* updates the active vertex and cursor related data of the SculptSession using the mouse position
@@ -895,14 +901,14 @@ bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cach
void SCULPT_vertex_random_access_ensure(struct SculptSession *ss);
int SCULPT_vertex_count_get(struct SculptSession *ss);
-const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index);
+const float *SCULPT_vertex_co_get(struct SculptSession *ss, PBVHVertRef vertex);
/** Get the normal for a given sculpt vertex; do not modify the result */
-void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]);
+void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]);
-float SCULPT_vertex_mask_get(struct SculptSession *ss, int index);
-void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]);
-void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]);
+float SCULPT_vertex_mask_get(struct SculptSession *ss, PBVHVertRef vertex);
+void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]);
+void SCULPT_vertex_color_set(SculptSession *ss, PBVHVertRef vertex, const float color[4]);
/** Returns true if a color attribute exists in the current sculpt session. */
bool SCULPT_has_colors(const SculptSession *ss);
@@ -910,19 +916,19 @@ bool SCULPT_has_colors(const SculptSession *ss);
/** Returns true if the active color attribute is on loop (ATTR_DOMAIN_CORNER) domain. */
bool SCULPT_has_loop_colors(const struct Object *ob);
-const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index);
-void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]);
+const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex);
+void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]);
/**
* Coordinates used for manipulating the base mesh when Grab Active Vertex is enabled.
*/
-const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index);
+const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, PBVHVertRef vertex);
/**
* Returns the info of the limit surface when multi-res is available,
* otherwise it returns the current coordinate of the vertex.
*/
-void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]);
+void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, float r_co[3]);
/**
* Returns the pointer to the coordinates that should be edited from a brush tool iterator
@@ -933,7 +939,7 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss,
PBVHVertexIter *iter);
void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
- int index,
+ PBVHVertRef vertex,
bool include_duplicates,
SculptVertexNeighborIter *iter);
@@ -942,7 +948,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \
for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \
neighbor_iterator.i++) { \
- neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i];
+ neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \
+ neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i];
/** Iterate over neighboring and duplicate vertices (for PBVH_GRIDS). Duplicates come
* first since they are nearest for floodfill. */
@@ -950,7 +957,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
SCULPT_vertex_neighbors_get(ss, v_index, true, &neighbor_iterator); \
for (neighbor_iterator.i = neighbor_iterator.size - 1; neighbor_iterator.i >= 0; \
neighbor_iterator.i--) { \
- neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i]; \
+ neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \
+ neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; \
neighbor_iterator.is_duplicate = (neighbor_iterator.i >= \
neighbor_iterator.size - neighbor_iterator.num_duplicates);
@@ -961,7 +969,7 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
} \
((void)0)
-int SCULPT_active_vertex_get(SculptSession *ss);
+PBVHVertRef SCULPT_active_vertex_get(SculptSession *ss);
const float *SCULPT_active_vertex_co_get(SculptSession *ss);
void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]);
@@ -981,7 +989,7 @@ void SCULPT_fake_neighbors_free(struct Object *ob);
/* Vertex Info. */
void SCULPT_boundary_info_ensure(Object *object);
/* Boundary Info needs to be initialized in order to use this function. */
-bool SCULPT_vertex_is_boundary(const SculptSession *ss, int index);
+bool SCULPT_vertex_is_boundary(const SculptSession *ss, PBVHVertRef vertex);
void SCULPT_connected_components_ensure(Object *ob);
@@ -991,11 +999,15 @@ void SCULPT_connected_components_ensure(Object *ob);
/** \name Sculpt Visibility API
* \{ */
-void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible);
-bool SCULPT_vertex_visible_get(SculptSession *ss, int index);
+void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible);
+bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex);
+bool SCULPT_vertex_all_faces_visible_get(const SculptSession *ss, PBVHVertRef vertex);
+bool SCULPT_vertex_any_face_visible_get(SculptSession *ss, PBVHVertRef vertex);
+
+void SCULPT_face_visibility_all_invert(SculptSession *ss);
+void SCULPT_face_visibility_all_set(SculptSession *ss, bool visible);
-void SCULPT_visibility_sync_all_face_sets_to_vertices(struct Object *ob);
-void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
+void SCULPT_visibility_sync_all_from_faces(struct Object *ob);
/** \} */
@@ -1004,20 +1016,15 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
* \{ */
int SCULPT_active_face_set_get(SculptSession *ss);
-int SCULPT_vertex_face_set_get(SculptSession *ss, int index);
-void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set);
+int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex);
+void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set);
-bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set);
-bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index);
+bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set);
+bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex);
int SCULPT_face_set_next_available_get(SculptSession *ss);
void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible);
-bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index);
-bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index);
-
-void SCULPT_face_sets_visibility_invert(SculptSession *ss);
-void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible);
/** \} */
@@ -1100,11 +1107,11 @@ void SCULPT_calc_area_normal_and_center(
void SCULPT_calc_area_center(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]);
-int SCULPT_nearest_vertex_get(struct Sculpt *sd,
- struct Object *ob,
- const float co[3],
- float max_distance,
- bool use_original);
+PBVHVertRef SCULPT_nearest_vertex_get(struct Sculpt *sd,
+ struct Object *ob,
+ const float co[3],
+ float max_distance,
+ bool use_original);
int SCULPT_plane_point_side(const float co[3], const float plane[4]);
int SCULPT_plane_trim(const struct StrokeCache *cache,
@@ -1183,7 +1190,7 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss,
const float vno[3],
const float fno[3],
float mask,
- int vertex_index,
+ const PBVHVertRef vertex,
int thread_id);
/**
@@ -1214,15 +1221,18 @@ void SCULPT_floodfill_add_initial_with_symmetry(struct Sculpt *sd,
struct Object *ob,
struct SculptSession *ss,
SculptFloodFill *flood,
- int index,
+ PBVHVertRef vertex,
float radius);
-void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index);
-void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index);
-void SCULPT_floodfill_execute(
- struct SculptSession *ss,
- SculptFloodFill *flood,
- bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata),
- void *userdata);
+void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex);
+void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex);
+void SCULPT_floodfill_execute(struct SculptSession *ss,
+ SculptFloodFill *flood,
+ bool (*func)(SculptSession *ss,
+ PBVHVertRef from_v,
+ PBVHVertRef to_v,
+ bool is_duplicate,
+ void *userdata),
+ void *userdata);
void SCULPT_floodfill_free(SculptFloodFill *flood);
/** \} */
@@ -1260,7 +1270,6 @@ void sculpt_dynamic_topology_disable_with_undo(struct Main *bmain,
bool SCULPT_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush);
void SCULPT_dynamic_topology_triangulate(struct BMesh *bm);
-void SCULPT_dyntopo_node_layers_add(struct SculptSession *ss);
enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob);
@@ -1272,7 +1281,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob);
float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking,
SculptSession *ss,
- int vert);
+ PBVHVertRef vertex);
/* Returns the automasking cache depending on the active tool. Used for code that can run both for
* brushes and filter. */
@@ -1305,9 +1314,9 @@ float *SCULPT_geodesic_distances_create(struct Object *ob,
float limit_radius);
float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd,
struct Object *ob,
- int vertex,
+ PBVHVertRef vertex,
float limit_radius);
-float *SCULPT_geodesic_from_vertex(Object *ob, int vertex, float limit_radius);
+float *SCULPT_geodesic_from_vertex(Object *ob, PBVHVertRef vertex, float limit_radius);
/** \} */
/* -------------------------------------------------------------------- */
@@ -1341,7 +1350,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim);
/* Public functions. */
-struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct SculptSession *ss,
+struct SculptClothSimulation *SCULPT_cloth_brush_simulation_create(struct Object *ob,
float cloth_mass,
float cloth_damping,
float cloth_softbody_strength,
@@ -1413,14 +1422,16 @@ BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush)
*/
void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct BMVert *v);
-void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index);
-float SCULPT_neighbor_mask_average(SculptSession *ss, int index);
-void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index);
+void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef vertex);
+float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex);
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef vertex);
/**
* Mask the mesh boundaries smoothing only the mesh surface without using auto-masking.
*/
-void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index);
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
+ float result[3],
+ PBVHVertRef vertex);
void SCULPT_smooth(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength, bool smooth_mask);
@@ -1432,11 +1443,15 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
float *disp,
const float co[3],
float (*laplacian_disp)[3],
- int v_index,
+ PBVHVertRef vertex,
const float origco[3],
float alpha);
-void SCULPT_surface_smooth_displace_step(
- SculptSession *ss, float *co, float (*laplacian_disp)[3], int v_index, float beta, float fade);
+void SCULPT_surface_smooth_displace_step(SculptSession *ss,
+ float *co,
+ float (*laplacian_disp)[3],
+ PBVHVertRef vertex,
+ float beta,
+ float fade);
void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
/* Slide/Relax */
@@ -1474,10 +1489,17 @@ SculptUndoNode *SCULPT_undo_get_node(PBVHNode *node, SculptUndoType type);
SculptUndoNode *SCULPT_undo_get_first_node(void);
/**
- * NOTE: `name` must match operator name for
- * redo panels to work.
+ * Pushes an undo step using the operator name. This is necessary for
+ * redo panels to work; operators that do not support that may use
+ * #SCULPT_undo_push_begin_ex instead if so desired.
*/
-void SCULPT_undo_push_begin(struct Object *ob, const char *name);
+void SCULPT_undo_push_begin(struct Object *ob, const struct wmOperator *op);
+
+/**
+ * NOTE: #SCULPT_undo_push_begin is preferred since `name`
+ * must match operator name for redo panels to work.
+ */
+void SCULPT_undo_push_begin_ex(struct Object *ob, const char *name);
void SCULPT_undo_push_end(struct Object *ob);
void SCULPT_undo_push_end_ex(struct Object *ob, const bool use_nested_undo);
@@ -1642,7 +1664,7 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain);
*/
struct SculptBoundary *SCULPT_boundary_data_init(Object *object,
Brush *brush,
- int initial_vertex,
+ PBVHVertRef initial_vertex,
float radius);
void SCULPT_boundary_data_free(struct SculptBoundary *boundary);
/* Main Brush Function. */
@@ -1810,6 +1832,21 @@ BLI_INLINE bool SCULPT_tool_is_paint(int tool)
return ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR);
}
+BLI_INLINE bool SCULPT_tool_is_mask(int tool)
+{
+ return ELEM(tool, SCULPT_TOOL_MASK);
+}
+
+BLI_INLINE bool SCULPT_tool_is_face_sets(int tool)
+{
+ return ELEM(tool, SCULPT_TOOL_DRAW_FACE_SETS);
+}
+
#ifdef __cplusplus
}
#endif
+
+/* Make SCULPT_ alias to a few blenkernel sculpt methods. */
+
+#define SCULPT_vertex_attr_get BKE_sculpt_vertex_attr_get
+#define SCULPT_face_attr_get BKE_sculpt_face_attr_get
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
index 4593c6a8b60..ec246cd3788 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c
@@ -97,11 +97,14 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
int update_it = data->mask_expand_update_it;
+ PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
+ int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex);
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) {
int vi = vd.index;
float final_mask = *vd.mask;
if (data->mask_expand_use_normals) {
- if (ss->filter_cache->normal_factor[SCULPT_active_vertex_get(ss)] <
+ if (ss->filter_cache->normal_factor[active_vertex_i] <
ss->filter_cache->normal_factor[vd.index]) {
final_mask = 1.0f;
}
@@ -121,7 +124,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
if (data->mask_expand_create_face_set) {
if (final_mask == 1.0f) {
- SCULPT_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set);
+ SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set);
}
BKE_pbvh_node_mark_redraw(node);
}
@@ -136,9 +139,6 @@ static void sculpt_expand_task_cb(void *__restrict userdata,
}
if (*vd.mask != final_mask) {
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
*vd.mask = final_mask;
BKE_pbvh_node_mark_update_mask(node);
}
@@ -167,10 +167,13 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *
if (RNA_boolean_get(op->ptr, "use_cursor")) {
SculptCursorGeometryInfo sgi;
+
const float mval_fl[2] = {UNPACK2(event->mval)};
if (SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
+ int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, SCULPT_active_vertex_get(ss));
+
/* The cursor is over the mesh, get the update iteration from the updated active vertex. */
- mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)];
+ mask_expand_update_it = ss->filter_cache->mask_update_it[active_vertex_i];
}
else {
/* When the cursor is outside the mesh, affect the entire connected component. */
@@ -291,13 +294,16 @@ typedef struct MaskExpandFloodFillData {
} MaskExpandFloodFillData;
static bool mask_expand_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
MaskExpandFloodFillData *data = userdata;
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
if (!is_duplicate) {
- int to_it = ss->filter_cache->mask_update_it[from_v] + 1;
- ss->filter_cache->mask_update_it[to_v] = to_it;
+ int to_it = ss->filter_cache->mask_update_it[from_v_i] + 1;
+ ss->filter_cache->mask_update_it[to_v_i] = to_it;
if (to_it > ss->filter_cache->mask_update_last_it) {
ss->filter_cache->mask_update_last_it = to_it;
}
@@ -306,20 +312,20 @@ static bool mask_expand_floodfill_cb(
float current_normal[3], prev_normal[3];
SCULPT_vertex_normal_get(ss, to_v, current_normal);
SCULPT_vertex_normal_get(ss, from_v, prev_normal);
- const float from_edge_factor = ss->filter_cache->edge_factor[from_v];
- ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) *
- from_edge_factor;
- ss->filter_cache->normal_factor[to_v] = dot_v3v3(data->original_normal, current_normal) *
- powf(from_edge_factor, data->edge_sensitivity);
- CLAMP(ss->filter_cache->normal_factor[to_v], 0.0f, 1.0f);
+ const float from_edge_factor = ss->filter_cache->edge_factor[from_v_i];
+ ss->filter_cache->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) *
+ from_edge_factor;
+ ss->filter_cache->normal_factor[to_v_i] = dot_v3v3(data->original_normal, current_normal) *
+ powf(from_edge_factor, data->edge_sensitivity);
+ CLAMP(ss->filter_cache->normal_factor[to_v_i], 0.0f, 1.0f);
}
}
else {
/* PBVH_GRIDS duplicate handling. */
- ss->filter_cache->mask_update_it[to_v] = ss->filter_cache->mask_update_it[from_v];
+ ss->filter_cache->mask_update_it[to_v_i] = ss->filter_cache->mask_update_it[from_v_i];
if (data->use_normals) {
- ss->filter_cache->edge_factor[to_v] = ss->filter_cache->edge_factor[from_v];
- ss->filter_cache->normal_factor[to_v] = ss->filter_cache->normal_factor[from_v];
+ ss->filter_cache->edge_factor[to_v_i] = ss->filter_cache->edge_factor[from_v_i];
+ ss->filter_cache->normal_factor[to_v_i] = ss->filter_cache->normal_factor[from_v_i];
}
}
@@ -355,7 +361,7 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
BKE_pbvh_search_gather(pbvh, NULL, NULL, &ss->filter_cache->nodes, &ss->filter_cache->totnode);
- SCULPT_undo_push_begin(ob, "Mask Expand");
+ SCULPT_undo_push_begin(ob, op);
if (create_face_set) {
SCULPT_undo_push_node(ob, ss->filter_cache->nodes[0], SCULPT_UNDO_FACE_SETS);
@@ -385,20 +391,24 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
if (create_face_set) {
ss->filter_cache->prev_face_set = MEM_callocN(sizeof(float) * ss->totfaces, "prev face mask");
for (int i = 0; i < ss->totfaces; i++) {
- ss->filter_cache->prev_face_set[i] = ss->face_sets[i];
+ ss->filter_cache->prev_face_set[i] = ss->face_sets ? ss->face_sets[i] : 0;
}
ss->filter_cache->new_face_set = SCULPT_face_set_next_available_get(ss);
}
else {
ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask");
for (int i = 0; i < vertex_count; i++) {
- ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, i);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, vertex);
}
}
+ int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, SCULPT_active_vertex_get(ss));
+
ss->filter_cache->mask_update_last_it = 1;
ss->filter_cache->mask_update_current_it = 1;
- ss->filter_cache->mask_update_it[SCULPT_active_vertex_get(ss)] = 0;
+ ss->filter_cache->mask_update_it[active_vertex_i] = 0;
copy_v3_v3(ss->filter_cache->mask_expand_initial_co, SCULPT_active_vertex_co_get(ss));
@@ -417,9 +427,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent
if (use_normals) {
for (int repeat = 0; repeat < 2; repeat++) {
for (int i = 0; i < vertex_count; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
float avg = 0.0f;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
avg += ss->filter_cache->normal_factor[ni.index];
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c
index 025f34ab2d7..b9b889ab2ce 100644
--- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c
+++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c
@@ -99,7 +99,7 @@ static void mask_init_task_cb(void *__restrict userdata,
*vd.mask = BLI_hash_int_01(vd.index + seed);
break;
case SCULPT_MASK_INIT_RANDOM_PER_FACE_SET: {
- const int face_set = SCULPT_vertex_face_set_get(ss, vd.index);
+ const int face_set = SCULPT_vertex_face_set_get(ss, vd.vertex);
*vd.mask = BLI_hash_int_01(face_set + seed);
break;
}
@@ -131,7 +131,7 @@ static int sculpt_mask_init_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- SCULPT_undo_push_begin(ob, "init mask");
+ SCULPT_undo_push_begin(ob, op);
if (mode == SCULPT_MASK_INIT_RANDOM_PER_LOOSE_PART) {
SCULPT_connected_components_ensure(ob);
diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
index ddc1a0e1db0..1e8731e54c0 100644
--- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
+++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c
@@ -86,7 +86,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
/* Sample the normal and area of the +X and -X axis individually. */
@@ -194,13 +194,13 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
mul_v3_v3fl(proxy[vd.i], val, fade);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c
index f16763be735..52bfa61cd95 100644
--- a/source/blender/editors/sculpt_paint/sculpt_ops.c
+++ b/source/blender/editors/sculpt_paint/sculpt_ops.c
@@ -47,6 +47,7 @@
#include "BKE_image.h"
#include "BKE_kelvinlet.h"
#include "BKE_key.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
@@ -116,22 +117,33 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op))
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
- if (!ss) {
+ /* Do not allow in DynTopo just yet. */
+ if (!ss || (ss && ss->bm)) {
return OPERATOR_FINISHED;
}
SCULPT_vertex_random_access_ensure(ss);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
- MEM_SAFE_FREE(ss->persistent_base);
+ SculptAttributeParams params = {0};
+ params.permanent = true;
+
+ ss->attrs.persistent_co = BKE_sculpt_attribute_ensure(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_co), &params);
+ ss->attrs.persistent_no = BKE_sculpt_attribute_ensure(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, SCULPT_ATTRIBUTE_NAME(persistent_no), &params);
+ ss->attrs.persistent_disp = BKE_sculpt_attribute_ensure(
+ ob, ATTR_DOMAIN_POINT, CD_PROP_FLOAT, SCULPT_ATTRIBUTE_NAME(persistent_disp), &params);
const int totvert = SCULPT_vertex_count_get(ss);
- ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert,
- "layer persistent base");
for (int i = 0; i < totvert; i++) {
- copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i));
- SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no);
- ss->persistent_base[i].disp = 0.0f;
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ copy_v3_v3((float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_co),
+ SCULPT_vertex_co_get(ss, vertex));
+ SCULPT_vertex_normal_get(
+ ss, vertex, (float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_no));
+ (*(float *)SCULPT_vertex_attr_get(vertex, ss->attrs.persistent_disp)) = 0.0f;
}
return OPERATOR_FINISHED;
@@ -213,7 +225,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
* as deleted, then after symmetrize operation all BMesh elements
* are logged as added (as opposed to attempting to store just the
* parts that symmetrize modifies). */
- SCULPT_undo_push_begin(ob, "Dynamic topology symmetrize");
+ SCULPT_undo_push_begin(ob, op);
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE);
BM_log_before_all_removed(ss->bm, ss->bm_log);
@@ -240,7 +252,7 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
break;
case PBVH_FACES:
/* Mesh Symmetrize. */
- ED_sculpt_undo_geometry_begin(ob, "mesh symmetrize");
+ ED_sculpt_undo_geometry_begin(ob, op);
Mesh *mesh = ob->data;
BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist);
@@ -297,28 +309,34 @@ static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene,
ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
ob->sculpt->mode_type = OB_MODE_SCULPT;
- BKE_sculpt_ensure_orig_mesh_data(scene, ob);
+ /* Trigger evaluation of modifier stack to ensure
+ * multires modifier sets .runtime.ccg in
+ * the evaluated mesh.
+ */
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
/* This function expects a fully evaluated depsgraph. */
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
- /* Here we can detect geometry that was just added to Sculpt Mode as it has the
- * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
- /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not
- * initialized, which is used is some operators that modify the mesh topology to perform certain
- * actions in the new polys. After these operations are finished, all polys should have a valid
- * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility
- * correctly. */
- /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new
- * objects, like moving the transform pivot position to the new area or masking existing
- * geometry. */
SculptSession *ss = ob->sculpt;
- const int new_face_set = SCULPT_face_set_next_available_get(ss);
- for (int i = 0; i < ss->totfaces; i++) {
- if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
- ss->face_sets[i] = new_face_set;
+ if (ss->face_sets) {
+ /* Here we can detect geometry that was just added to Sculpt Mode as it has the
+ * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
+ /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not
+ * initialized, which is used is some operators that modify the mesh topology to perform
+ * certain actions in the new polys. After these operations are finished, all polys should have
+ * a valid face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their
+ * visibility correctly. */
+ /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new
+ * objects, like moving the transform pivot position to the new area or masking existing
+ * geometry. */
+ const int new_face_set = SCULPT_face_set_next_available_get(ss);
+ for (int i = 0; i < ss->totfaces; i++) {
+ if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
+ ss->face_sets[i] = new_face_set;
+ }
}
}
}
@@ -392,7 +410,7 @@ void ED_object_sculptmode_enter_ex(Main *bmain,
bool has_undo = wm->undo_stack != NULL;
/* Undo push is needed to prevent memory leak. */
if (has_undo) {
- SCULPT_undo_push_begin(ob, "Dynamic topology enable");
+ SCULPT_undo_push_begin_ex(ob, "Dynamic topology enable");
}
SCULPT_dynamic_topology_enable_ex(bmain, depsgraph, scene, ob);
if (has_undo) {
@@ -416,7 +434,8 @@ void ED_object_sculptmode_enter(struct bContext *C, Depsgraph *depsgraph, Report
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports);
}
@@ -468,7 +487,8 @@ void ED_object_sculptmode_exit(bContext *C, Depsgraph *depsgraph)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
ED_object_sculptmode_exit_ex(bmain, depsgraph, scene, ob);
}
@@ -480,7 +500,8 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
const int mode_flag = OB_MODE_SCULPT;
const bool is_mode_set = (ob->mode & mode_flag) != 0;
@@ -508,7 +529,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
* while it works it causes lag when undoing the first undo step, see T71564. */
wmWindowManager *wm = CTX_wm_manager(C);
if (wm->op_undo_depth <= 1) {
- SCULPT_undo_push_begin(ob, op->type->name);
+ SCULPT_undo_push_begin(ob, op);
SCULPT_undo_push_end(ob);
}
}
@@ -543,7 +564,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob = CTX_data_active_object(C);
- ss->preview_vert_index_count = 0;
+ ss->preview_vert_count = 0;
int totpoints = 0;
/* This function is called from the cursor drawing code, so the PBVH may not be build yet. */
@@ -568,193 +589,50 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float
float brush_co[3];
copy_v3_v3(brush_co, SCULPT_active_vertex_co_get(ss));
- BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
+ BLI_bitmap *visited_verts = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_verts");
/* Assuming an average of 6 edges per vertex in a triangulated mesh. */
- const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2;
+ const int max_preview_verts = SCULPT_vertex_count_get(ss) * 3 * 2;
- if (ss->preview_vert_index_list == NULL) {
- ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines");
+ if (ss->preview_vert_list == NULL) {
+ ss->preview_vert_list = MEM_callocN(max_preview_verts * sizeof(PBVHVertRef), "preview lines");
}
- GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int));
- int active_v = SCULPT_active_vertex_get(ss);
- BLI_gsqueue_push(not_visited_vertices, &active_v);
+ GSQueue *non_visited_verts = BLI_gsqueue_new(sizeof(PBVHVertRef));
+ PBVHVertRef active_v = SCULPT_active_vertex_get(ss);
+ BLI_gsqueue_push(non_visited_verts, &active_v);
+
+ while (!BLI_gsqueue_is_empty(non_visited_verts)) {
+ PBVHVertRef from_v;
- while (!BLI_gsqueue_is_empty(not_visited_vertices)) {
- int from_v;
- BLI_gsqueue_pop(not_visited_vertices, &from_v);
+ BLI_gsqueue_pop(non_visited_verts, &from_v);
SculptVertexNeighborIter ni;
SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
- if (totpoints + (ni.size * 2) < max_preview_vertices) {
- int to_v = ni.index;
- ss->preview_vert_index_list[totpoints] = from_v;
+ if (totpoints + (ni.size * 2) < max_preview_verts) {
+ PBVHVertRef to_v = ni.vertex;
+ int to_v_i = ni.index;
+ ss->preview_vert_list[totpoints] = from_v;
totpoints++;
- ss->preview_vert_index_list[totpoints] = to_v;
+ ss->preview_vert_list[totpoints] = to_v;
totpoints++;
- if (BLI_BITMAP_TEST(visited_vertices, to_v)) {
+ if (BLI_BITMAP_TEST(visited_verts, to_v_i)) {
continue;
}
- BLI_BITMAP_ENABLE(visited_vertices, to_v);
+ BLI_BITMAP_ENABLE(visited_verts, to_v_i);
const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v);
if (len_squared_v3v3(brush_co, co) < radius * radius) {
- BLI_gsqueue_push(not_visited_vertices, &to_v);
+ BLI_gsqueue_push(non_visited_verts, &to_v);
}
}
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
}
- BLI_gsqueue_free(not_visited_vertices);
-
- MEM_freeN(visited_vertices);
-
- ss->preview_vert_index_count = totpoints;
-}
-
-static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_active_object(C);
-
- ID *data;
- data = ob->data;
- if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) {
- return OPERATOR_CANCELLED;
- }
-
- if (ob->type != OB_MESH) {
- return OPERATOR_CANCELLED;
- }
-
- Mesh *mesh = ob->data;
-
- const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR);
- if (mloopcol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n);
-
- const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
- if (MPropCol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- const MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
-
- const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
- const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
-
- for (int i = 0; i < mesh->totpoly; i++) {
- const MPoly *c_poly = &polys[i];
- for (int j = 0; j < c_poly->totloop; j++) {
- int loop_index = c_poly->loopstart + j;
- const MLoop *c_loop = &loops[c_poly->loopstart + j];
- float srgb_color[4];
- linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color);
- loopcols[loop_index].r = (char)(srgb_color[0] * 255);
- loopcols[loop_index].g = (char)(srgb_color[1] * 255);
- loopcols[loop_index].b = (char)(srgb_color[2] * 255);
- loopcols[loop_index].a = (char)(srgb_color[3] * 255);
- }
- }
-
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
-
- return OPERATOR_FINISHED;
-}
-
-static bool sculpt_colors_poll(bContext *C)
-{
- if (!SCULPT_mode_poll(C)) {
- return false;
- }
-
- Object *ob = CTX_data_active_object(C);
-
- if (!ob->sculpt || !ob->sculpt->pbvh || BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_FACES) {
- return false;
- }
-
- return SCULPT_has_colors(ob->sculpt);
-}
-
-static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Sculpt Vertex Color to Vertex Color";
- ot->description = "Copy the Sculpt Vertex Color to a regular color layer";
- ot->idname = "SCULPT_OT_vertex_to_loop_colors";
-
- /* api callbacks */
- ot->poll = sculpt_colors_poll;
- ot->exec = vertex_to_loop_colors_exec;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_active_object(C);
-
- ID *data;
- data = ob->data;
- if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) {
- return OPERATOR_CANCELLED;
- }
-
- if (ob->type != OB_MESH) {
- return OPERATOR_CANCELLED;
- }
-
- Mesh *mesh = ob->data;
-
- const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR);
- if (mloopcol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- const MLoopCol *loopcols = CustomData_get_layer_n(
- &mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n);
-
- const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
- if (MPropCol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
-
- const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
- const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
-
- for (int i = 0; i < mesh->totpoly; i++) {
- const MPoly *c_poly = &polys[i];
- for (int j = 0; j < c_poly->totloop; j++) {
- int loop_index = c_poly->loopstart + j;
- const MLoop *c_loop = &loops[c_poly->loopstart + j];
- vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
- vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
- vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
- vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f);
- srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color);
- }
- }
-
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Vertex Color to Sculpt Vertex Color";
- ot->description = "Copy the active loop color layer to the vertex color";
- ot->idname = "SCULPT_OT_loop_to_vertex_colors";
+ BLI_gsqueue_free(non_visited_verts);
- /* api callbacks */
- ot->poll = sculpt_colors_poll;
- ot->exec = loop_to_vertex_colors_exec;
+ MEM_freeN(visited_verts);
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ss->preview_vert_count = totpoints;
}
static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e))
@@ -764,7 +642,7 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent
Object *ob = CTX_data_active_object(C);
Brush *brush = BKE_paint_brush(&sd->paint);
SculptSession *ss = ob->sculpt;
- int active_vertex = SCULPT_active_vertex_get(ss);
+ PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
float active_vertex_color[4];
if (!SCULPT_handles_colors_report(ss, op->reports)) {
@@ -882,9 +760,6 @@ static void do_mask_by_color_contiguous_update_nodes_cb(
continue;
}
update_node = true;
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
}
BKE_pbvh_vertex_iter_end;
if (update_node) {
@@ -893,8 +768,11 @@ static void do_mask_by_color_contiguous_update_nodes_cb(
}
static bool sculpt_mask_by_color_contiguous_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
MaskByColorContiguousFloodFillData *data = userdata;
float current_color[4];
@@ -902,10 +780,10 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb(
float new_vertex_mask = sculpt_mask_by_color_delta_get(
current_color, data->initial_color, data->threshold, data->invert);
- data->new_mask[to_v] = new_vertex_mask;
+ data->new_mask[to_v_i] = new_vertex_mask;
if (is_duplicate) {
- data->new_mask[to_v] = data->new_mask[from_v];
+ data->new_mask[to_v_i] = data->new_mask[from_v_i];
}
float len = len_v3v3(current_color, data->initial_color);
@@ -914,7 +792,7 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb(
}
static void sculpt_mask_by_color_contiguous(Object *object,
- const int vertex,
+ const PBVHVertRef vertex,
const float threshold,
const bool invert,
const bool preserve_mask)
@@ -991,7 +869,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
float col[4];
- SCULPT_vertex_color_get(ss, vd.index, col);
+ SCULPT_vertex_color_get(ss, vd.vertex, col);
const float current_mask = *vd.mask;
const float new_mask = sculpt_mask_by_color_delta_get(active_color, col, threshold, invert);
@@ -1001,18 +879,15 @@ static void do_mask_by_color_task_cb(void *__restrict userdata,
continue;
}
update_node = true;
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
}
BKE_pbvh_vertex_iter_end;
if (update_node) {
- BKE_pbvh_node_mark_redraw(data->nodes[n]);
+ BKE_pbvh_node_mark_update_mask(data->nodes[n]);
}
}
static void sculpt_mask_by_color_full_mesh(Object *object,
- const int vertex,
+ const PBVHVertRef vertex,
const float threshold,
const bool invert,
const bool preserve_mask)
@@ -1064,10 +939,10 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
const float mval_fl[2] = {UNPACK2(event->mval)};
SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
- SCULPT_undo_push_begin(ob, "Mask by color");
+ SCULPT_undo_push_begin(ob, op);
BKE_sculpt_color_layer_create_if_needed(ob);
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
const float threshold = RNA_float_get(op->ptr, "threshold");
const bool invert = RNA_boolean_get(op->ptr, "invert");
const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
@@ -1156,8 +1031,6 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_project_line_gesture);
WM_operatortype_append(SCULPT_OT_sample_color);
- WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
- WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
WM_operatortype_append(SCULPT_OT_color_filter);
WM_operatortype_append(SCULPT_OT_mask_by_color);
WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit);
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
index 7e813590e21..c494c71f1eb 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -17,6 +17,7 @@
#include "DNA_meshdata_types.h"
#include "BKE_brush.h"
+#include "BKE_colorband.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_mesh.h"
@@ -80,20 +81,16 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float smooth_color[4];
- SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
+ SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex);
float col[4];
- SCULPT_vertex_color_get(ss, vd.index, col);
+ SCULPT_vertex_color_get(ss, vd.vertex, col);
blend_color_interpolate_float(col, col, smooth_color, fade);
- SCULPT_vertex_color_set(ss, vd.index, col);
-
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
+ SCULPT_vertex_color_set(ss, vd.vertex, col);
}
BKE_pbvh_vertex_iter_end;
}
@@ -121,11 +118,31 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
const int thread_id = BLI_task_parallel_thread_id(tls);
float brush_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+
copy_v3_v3(brush_color,
ss->cache->invert ? BKE_brush_secondary_color_get(ss->scene, brush) :
BKE_brush_color_get(ss->scene, brush));
+
IMB_colormanagement_srgb_to_scene_linear_v3(brush_color, brush_color);
+ if (brush->flag & BRUSH_USE_GRADIENT) {
+ switch (brush->gradient_stroke_mode) {
+ case BRUSH_GRADIENT_PRESSURE:
+ BKE_colorband_evaluate(brush->gradient, ss->cache->pressure, brush_color);
+ break;
+ case BRUSH_GRADIENT_SPACING_REPEAT: {
+ float coord = fmod(ss->cache->stroke_distance / brush->gradient_spacing, 1.0);
+ BKE_colorband_evaluate(brush->gradient, coord, brush_color);
+ break;
+ }
+ case BRUSH_GRADIENT_SPACING_CLAMP: {
+ BKE_colorband_evaluate(
+ brush->gradient, ss->cache->stroke_distance / brush->gradient_spacing, brush_color);
+ break;
+ }
+ }
+ }
+
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
SCULPT_orig_vert_data_update(&orig_data, &vd);
@@ -151,7 +168,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
/* Density. */
@@ -182,14 +199,10 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata,
mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha);
float col[4];
- SCULPT_vertex_color_get(ss, vd.index, col);
+ SCULPT_vertex_color_get(ss, vd.vertex, col);
IMB_blend_color_float(col, orig_data.col, buffer_color, brush->blend);
CLAMP4(col, 0.0f, 1.0f);
- SCULPT_vertex_color_set(ss, vd.index, col);
-
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
+ SCULPT_vertex_color_set(ss, vd.vertex, col);
}
BKE_pbvh_vertex_iter_end;
}
@@ -221,7 +234,7 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata,
}
float col[4];
- SCULPT_vertex_color_get(ss, vd.index, col);
+ SCULPT_vertex_color_get(ss, vd.vertex, col);
add_v4_v4(swptd->color, col);
swptd->tot_samples++;
@@ -400,7 +413,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float current_disp[3];
@@ -409,7 +422,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]);
float no[3];
- SCULPT_vertex_normal_get(ss, vd.index, no);
+ SCULPT_vertex_normal_get(ss, vd.vertex, no);
switch (brush->smear_deform_type) {
case BRUSH_SMEAR_DEFORM_DRAG:
@@ -442,11 +455,11 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
*/
SculptVertexNeighborIter ni2;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni2) {
- const float *nco = SCULPT_vertex_co_get(ss, ni2.index);
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni2) {
+ const float *nco = SCULPT_vertex_co_get(ss, ni2.vertex);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.vertex, ni) {
if (ni.index == vd.index) {
continue;
}
@@ -454,13 +467,13 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
float vertex_disp[3];
float vertex_disp_norm[3];
- sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co);
/* Weight by how close we are to our target distance from vd.co. */
float w = (1.0f + fabsf(len_v3(vertex_disp) / bstrength - 1.0f));
/* TODO: use cotangents (or at least face areas) here. */
- float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.index), nco);
+ float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.vertex), nco);
if (len > 0.0f) {
len = bstrength / len;
}
@@ -502,13 +515,9 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
blend_color_mix_float(interp_color, interp_color, accum);
float col[4];
- SCULPT_vertex_color_get(ss, vd.index, col);
+ SCULPT_vertex_color_get(ss, vd.vertex, col);
blend_color_interpolate_float(col, ss->cache->prev_colors[vd.index], interp_color, fade);
- SCULPT_vertex_color_set(ss, vd.index, col);
-
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
- }
+ SCULPT_vertex_color_set(ss, vd.vertex, col);
}
BKE_pbvh_vertex_iter_end;
}
@@ -522,7 +531,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata,
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- SCULPT_vertex_color_get(ss, vd.index, ss->cache->prev_colors[vd.index]);
+ SCULPT_vertex_color_get(ss, vd.vertex, ss->cache->prev_colors[vd.index]);
}
BKE_pbvh_vertex_iter_end;
}
@@ -541,7 +550,9 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode
if (!ss->cache->prev_colors) {
ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors");
for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]);
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ SCULPT_vertex_color_get(ss, vertex, ss->cache->prev_colors[i]);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
index 975a8f21aaf..8a3a3fe7adc 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc
@@ -172,7 +172,15 @@ template<typename ImageBuffer> class PaintingKernel {
const float3 face_normal(0.0f, 0.0f, 0.0f);
const float mask = 0.0f;
const float falloff_strength = SCULPT_brush_strength_factor(
- ss, brush, pixel_pos, sqrtf(test.dist), normal, face_normal, mask, 0, thread_id);
+ ss,
+ brush,
+ pixel_pos,
+ sqrtf(test.dist),
+ normal,
+ face_normal,
+ mask,
+ BKE_pbvh_make_vref(PBVH_REF_NONE),
+ thread_id);
float4 paint_color = brush_color * falloff_strength * brush_strength;
float4 buffer_color;
blend_color_mix_float(buffer_color, color, paint_color);
@@ -383,7 +391,7 @@ static void push_undo(const NodeData &node_data,
continue;
}
int tilex, tiley, tilew, tileh;
- ListBase *undo_tiles = ED_image_paint_tile_list_get();
+ PaintTileMap *undo_tiles = ED_image_paint_tile_map_get();
undo_region_tiles(&image_buffer,
tile_undo.region.xmin,
tile_undo.region.ymin,
diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c
index 479ed43c6bf..d1418c8dc35 100644
--- a/source/blender/editors/sculpt_paint/sculpt_pose.c
+++ b/source/blender/editors/sculpt_paint/sculpt_pose.c
@@ -182,7 +182,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
/* Apply the vertex mask to the displacement. */
const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f;
- const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index);
+ const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex);
mul_v3_fl(disp, mask * automask);
/* Accumulate the displacement. */
@@ -196,7 +196,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
copy_v3_v3(target_co, final_pos);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -221,7 +221,7 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata,
float max = 0.0f;
/* Grow the factor. */
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
float vmask_f = data->prev_mask[ni.index];
max = MAX2(vmask_f, max);
}
@@ -367,7 +367,7 @@ typedef struct PoseFloodFillData {
int current_face_set;
int next_face_set;
int prev_face_set;
- int next_vertex;
+ PBVHVertRef next_vertex;
bool next_face_set_found;
@@ -397,14 +397,19 @@ typedef struct PoseFloodFillData {
int target_face_set;
} PoseFloodFillData;
-static bool pose_topology_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata)
+static bool pose_topology_floodfill_cb(SculptSession *ss,
+ PBVHVertRef UNUSED(from_v),
+ PBVHVertRef to_v,
+ bool is_duplicate,
+ void *userdata)
{
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
PoseFloodFillData *data = userdata;
const float *co = SCULPT_vertex_co_get(ss, to_v);
if (data->pose_factor) {
- data->pose_factor[to_v] = 1.0f;
+ data->pose_factor[to_v_i] = 1.0f;
}
if (len_squared_v3v3(data->pose_initial_co, data->fallback_floodfill_origin) <
@@ -426,15 +431,19 @@ static bool pose_topology_floodfill_cb(
return false;
}
-static bool pose_face_sets_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata)
+static bool pose_face_sets_floodfill_cb(SculptSession *ss,
+ PBVHVertRef UNUSED(from_v),
+ PBVHVertRef to_v,
+ bool is_duplicate,
+ void *userdata)
{
PoseFloodFillData *data = userdata;
- const int index = to_v;
+ const int index = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+ const PBVHVertRef vertex = to_v;
bool visit_next = false;
- const float *co = SCULPT_vertex_co_get(ss, index);
+ const float *co = SCULPT_vertex_co_get(ss, vertex);
const bool symmetry_check = SCULPT_check_vertex_pivot_symmetry(
co, data->pose_initial_co, data->symm) &&
!is_duplicate;
@@ -448,11 +457,11 @@ static bool pose_face_sets_floodfill_cb(
if (sculpt_pose_brush_is_vertex_inside_brush_radius(
co, data->pose_initial_co, data->radius, data->symm)) {
- const int visited_face_set = SCULPT_vertex_face_set_get(ss, index);
+ const int visited_face_set = SCULPT_vertex_face_set_get(ss, vertex);
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(visited_face_set));
}
else if (symmetry_check) {
- data->current_face_set = SCULPT_vertex_face_set_get(ss, index);
+ data->current_face_set = SCULPT_vertex_face_set_get(ss, vertex);
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(data->current_face_set));
}
return true;
@@ -466,11 +475,11 @@ static bool pose_face_sets_floodfill_cb(
GSetIterator gs_iter;
GSET_ITER (gs_iter, data->visited_face_sets) {
const int visited_face_set = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter));
- is_vertex_valid |= SCULPT_vertex_has_face_set(ss, index, visited_face_set);
+ is_vertex_valid |= SCULPT_vertex_has_face_set(ss, vertex, visited_face_set);
}
}
else {
- is_vertex_valid = SCULPT_vertex_has_face_set(ss, index, data->current_face_set);
+ is_vertex_valid = SCULPT_vertex_has_face_set(ss, vertex, data->current_face_set);
}
if (!is_vertex_valid) {
@@ -485,11 +494,11 @@ static bool pose_face_sets_floodfill_cb(
/* Fallback origin accumulation. */
if (symmetry_check) {
- add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, index));
+ add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, vertex));
data->fallback_count++;
}
- if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, index)) {
+ if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, vertex)) {
return visit_next;
}
@@ -498,15 +507,15 @@ static bool pose_face_sets_floodfill_cb(
bool count_as_boundary = false;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.index);
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.vertex);
/* Check if we can get a valid face set for the next iteration from this neighbor. */
- if (SCULPT_vertex_has_unique_face_set(ss, ni.index) &&
+ if (SCULPT_vertex_has_unique_face_set(ss, ni.vertex) &&
!BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(next_face_set_candidate))) {
if (!data->next_face_set_found) {
data->next_face_set = next_face_set_candidate;
- data->next_vertex = ni.index;
+ data->next_vertex = ni.vertex;
data->next_face_set_found = true;
}
count_as_boundary = true;
@@ -516,7 +525,7 @@ static bool pose_face_sets_floodfill_cb(
/* Origin accumulation. */
if (count_as_boundary) {
- add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, index));
+ add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, vertex));
data->tot_co++;
}
return visit_next;
@@ -585,7 +594,7 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata,
SculptVertexNeighborIter ni;
float avg = 0.0f;
int total = 0;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) {
avg += data->pose_factor[ni.index];
total++;
}
@@ -660,7 +669,8 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd,
float next_chain_segment_target[3];
int totvert = SCULPT_vertex_count_get(ss);
- int nearest_vertex_index = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true);
+ PBVHVertRef nearest_vertex = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true);
+ int nearest_vertex_index = BKE_pbvh_vertex_to_index(ss->pbvh, nearest_vertex);
/* Init the buffers used to keep track of the changes in the pose factors as more segments are
* added to the IK chain. */
@@ -745,7 +755,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
int current_face_set = SCULPT_FACE_SET_NONE;
int prev_face_set = SCULPT_FACE_SET_NONE;
- int current_vertex = SCULPT_active_vertex_get(ss);
+ PBVHVertRef current_vertex = SCULPT_active_vertex_get(ss);
for (int s = 0; s < ik_chain->tot_segments; s++) {
@@ -801,15 +811,18 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
}
static bool pose_face_sets_fk_find_masked_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+ SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata)
{
PoseFloodFillData *data = userdata;
+ int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v);
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
if (!is_duplicate) {
- data->floodfill_it[to_v] = data->floodfill_it[from_v] + 1;
+ data->floodfill_it[to_v_i] = data->floodfill_it[from_v_i] + 1;
}
else {
- data->floodfill_it[to_v] = data->floodfill_it[from_v];
+ data->floodfill_it[to_v_i] = data->floodfill_it[from_v_i];
}
const int to_face_set = SCULPT_vertex_face_set_get(ss, to_v);
@@ -820,9 +833,9 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb(
BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(to_face_set));
- if (data->floodfill_it[to_v] >= data->masked_face_set_it) {
+ if (data->floodfill_it[to_v_i] >= data->masked_face_set_it) {
data->masked_face_set = to_face_set;
- data->masked_face_set_it = data->floodfill_it[to_v];
+ data->masked_face_set_it = data->floodfill_it[to_v_i];
}
if (data->target_face_set == SCULPT_FACE_SET_NONE) {
@@ -834,11 +847,17 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb(
return SCULPT_vertex_has_face_set(ss, to_v, data->initial_face_set);
}
-static bool pose_face_sets_fk_set_weights_floodfill_cb(
- SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata)
+static bool pose_face_sets_fk_set_weights_floodfill_cb(SculptSession *ss,
+ PBVHVertRef UNUSED(from_v),
+ PBVHVertRef to_v,
+ bool UNUSED(is_duplicate),
+ void *userdata)
{
PoseFloodFillData *data = userdata;
- data->fk_weights[to_v] = 1.0f;
+
+ int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v);
+
+ data->fk_weights[to_v_i] = 1.0f;
return !SCULPT_vertex_has_face_set(ss, to_v, data->masked_face_set);
}
@@ -849,7 +868,9 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
SculptPoseIKChain *ik_chain = pose_ik_chain_new(1, totvert);
- const int active_vertex = SCULPT_active_vertex_get(ss);
+ const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss);
+ int active_vertex_index = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex);
+
const int active_face_set = SCULPT_active_face_set_get(ss);
SculptFloodFill flood;
@@ -857,7 +878,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
SCULPT_floodfill_add_initial(&flood, active_vertex);
PoseFloodFillData fdata;
fdata.floodfill_it = MEM_calloc_arrayN(totvert, sizeof(int), "floodfill iteration");
- fdata.floodfill_it[active_vertex] = 1;
+ fdata.floodfill_it[active_vertex_index] = 1;
fdata.initial_face_set = active_face_set;
fdata.masked_face_set = SCULPT_FACE_SET_NONE;
fdata.target_face_set = SCULPT_FACE_SET_NONE;
@@ -870,9 +891,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
int origin_count = 0;
float origin_acc[3] = {0.0f};
for (int i = 0; i < totvert; i++) {
- if (fdata.floodfill_it[i] != 0 && SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) &&
- SCULPT_vertex_has_face_set(ss, i, fdata.masked_face_set)) {
- add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, i));
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
+ if (fdata.floodfill_it[i] != 0 &&
+ SCULPT_vertex_has_face_set(ss, vertex, fdata.initial_face_set) &&
+ SCULPT_vertex_has_face_set(ss, vertex, fdata.masked_face_set)) {
+ add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, vertex));
origin_count++;
}
}
@@ -881,10 +905,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
float target_acc[3] = {0.0f};
if (fdata.target_face_set != fdata.masked_face_set) {
for (int i = 0; i < totvert; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
if (fdata.floodfill_it[i] != 0 &&
- SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) &&
- SCULPT_vertex_has_face_set(ss, i, fdata.target_face_set)) {
- add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, i));
+ SCULPT_vertex_has_face_set(ss, vertex, fdata.initial_face_set) &&
+ SCULPT_vertex_has_face_set(ss, vertex, fdata.target_face_set)) {
+ add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, vertex));
target_count++;
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c
index c31863d892f..2ef3c28ba0c 100644
--- a/source/blender/editors/sculpt_paint/sculpt_smooth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c
@@ -46,26 +46,28 @@
#include <math.h>
#include <stdlib.h>
-void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index)
+void SCULPT_neighbor_coords_average_interior(SculptSession *ss,
+ float result[3],
+ PBVHVertRef vertex)
{
float avg[3] = {0.0f, 0.0f, 0.0f};
int total = 0;
int neighbor_count = 0;
- const bool is_boundary = SCULPT_vertex_is_boundary(ss, index);
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex);
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
neighbor_count++;
if (is_boundary) {
/* Boundary vertices use only other boundary vertices. */
- if (SCULPT_vertex_is_boundary(ss, ni.index)) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ if (SCULPT_vertex_is_boundary(ss, ni.vertex)) {
+ add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex));
total++;
}
}
else {
/* Interior vertices use all neighbors. */
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex));
total++;
}
}
@@ -73,13 +75,13 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3],
/* Do not modify corner vertices. */
if (neighbor_count <= 2 && is_boundary) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
return;
}
/* Avoid division by 0 when there are no neighbors. */
if (total == 0) {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
return;
}
@@ -134,14 +136,14 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert
/* Generic functions for laplacian smoothing. These functions do not take boundary vertices into
* account. */
-void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index)
+void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef vertex)
{
float avg[3] = {0.0f, 0.0f, 0.0f};
int total = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index));
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex));
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -150,18 +152,18 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int inde
mul_v3_v3fl(result, avg, 1.0f / total);
}
else {
- copy_v3_v3(result, SCULPT_vertex_co_get(ss, index));
+ copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex));
}
}
-float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
+float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex)
{
float avg = 0.0f;
int total = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
- avg += SCULPT_vertex_mask_get(ss, ni.index);
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
+ avg += SCULPT_vertex_mask_get(ss, ni.vertex);
total++;
}
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
@@ -169,19 +171,19 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index)
if (total > 0) {
return avg / total;
}
- return SCULPT_vertex_mask_get(ss, index);
+ return SCULPT_vertex_mask_get(ss, vertex);
}
-void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index)
+void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef vertex)
{
float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f};
int total = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
float tmp[4] = {0};
- SCULPT_vertex_color_get(ss, ni.index, tmp);
+ SCULPT_vertex_color_get(ss, ni.vertex, tmp);
add_v4_v4(avg, tmp);
total++;
@@ -192,7 +194,7 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index
mul_v4_v4fl(result, avg, 1.0f / total);
}
else {
- SCULPT_vertex_color_get(ss, index, result);
+ SCULPT_vertex_color_get(ss, vertex, result);
}
}
@@ -227,7 +229,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp[3];
@@ -235,7 +237,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata,
SCULPT_clip(sd, ss, vd.co, disp);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -258,9 +260,11 @@ static void SCULPT_enhance_details_brush(Sculpt *sd,
totvert, sizeof(float[3]), "details directions");
for (int i = 0; i < totvert; i++) {
+ PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i);
+
float avg[3];
- SCULPT_neighbor_coords_average(ss, avg, i);
- sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i));
+ SCULPT_neighbor_coords_average(ss, avg, vertex);
+ sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex));
}
}
@@ -309,23 +313,23 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata,
vd.no,
vd.fno,
smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f),
- vd.index,
+ vd.vertex,
thread_id);
if (smooth_mask) {
- float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask;
+ float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask;
val *= fade * bstrength;
*vd.mask += val;
CLAMP(*vd.mask, 0.0f, 1.0f);
}
else {
float avg[3], val[3];
- SCULPT_neighbor_coords_average_interior(ss, avg, vd.index);
+ SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex);
sub_v3_v3v3(val, avg, vd.co);
madd_v3_v3v3fl(val, vd.co, val, fade);
SCULPT_clip(sd, ss, vd.co, val);
- }
- if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ if (vd.mvert) {
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
+ }
}
}
BKE_pbvh_vertex_iter_end;
@@ -403,13 +407,15 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
float *disp,
const float co[3],
float (*laplacian_disp)[3],
- const int v_index,
+ const PBVHVertRef vertex,
const float origco[3],
const float alpha)
{
float laplacian_smooth_co[3];
float weigthed_o[3], weigthed_q[3], d[3];
- SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index);
+ int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex);
+
+ SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex);
mul_v3_v3fl(weigthed_o, origco, alpha);
mul_v3_v3fl(weigthed_q, co, 1.0f - alpha);
@@ -422,7 +428,7 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss,
void SCULPT_surface_smooth_displace_step(SculptSession *ss,
float *co,
float (*laplacian_disp)[3],
- const int v_index,
+ const PBVHVertRef vertex,
const float beta,
const float fade)
{
@@ -430,12 +436,15 @@ void SCULPT_surface_smooth_displace_step(SculptSession *ss,
float b_current_vertex[3];
int total = 0;
SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_index, ni) {
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) {
add_v3_v3(b_avg, laplacian_disp[ni.index]);
total++;
}
+
SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
if (total > 0) {
+ int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex);
+
mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total);
madd_v3_v3fl(b_current_vertex, laplacian_disp[v_index], beta);
mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f));
@@ -474,15 +483,15 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex(
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
float disp[3];
SCULPT_surface_smooth_laplacian_step(
- ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, orig_data.co, alpha);
+ ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, orig_data.co, alpha);
madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f));
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -515,10 +524,10 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex(
vd.no,
vd.fno,
vd.mask ? *vd.mask : 0.0f,
- vd.index,
+ vd.vertex,
thread_id);
SCULPT_surface_smooth_displace_step(
- ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade);
+ ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade);
}
BKE_pbvh_vertex_iter_end;
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c
index 365000ab163..dfaa0bd4daa 100644
--- a/source/blender/editors/sculpt_paint/sculpt_transform.c
+++ b/source/blender/editors/sculpt_paint/sculpt_transform.c
@@ -46,7 +46,7 @@
#include <math.h>
#include <stdlib.h>
-void ED_sculpt_init_transform(struct bContext *C, Object *ob)
+void ED_sculpt_init_transform(struct bContext *C, Object *ob, const char *undo_name)
{
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
SculptSession *ss = ob->sculpt;
@@ -60,7 +60,7 @@ void ED_sculpt_init_transform(struct bContext *C, Object *ob)
copy_v4_v4(ss->prev_pivot_rot, ss->pivot_rot);
copy_v3_v3(ss->prev_pivot_scale, ss->pivot_scale);
- SCULPT_undo_push_begin(ob, "Transform");
+ SCULPT_undo_push_begin_ex(ob, undo_name);
BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
ss->pivot_rot[3] = 1.0f;
@@ -179,7 +179,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata,
add_v3_v3v3(vd.co, start_co, disp);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -253,7 +253,7 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata,
copy_v3_v3(proxy[vd.i], final_disp);
if (vd.mvert) {
- BKE_pbvh_vert_mark_update(ss->pbvh, vd.index);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex);
}
}
BKE_pbvh_vertex_iter_end;
@@ -289,9 +289,6 @@ static void sculpt_transform_radius_elastic(Sculpt *sd, Object *ob, const float
flip_v3_v3(data.elastic_transform_pivot, ss->pivot_pos, symmpass);
flip_v3_v3(data.elastic_transform_pivot_init, ss->init_pivot_pos, symmpass);
- printf(
- "%.2f %.2f %.2f\n", ss->init_pivot_pos[0], ss->init_pivot_pos[1], ss->init_pivot_pos[2]);
-
const int symm_area = SCULPT_get_vertex_symm_area(data.elastic_transform_pivot);
copy_m4_m4(data.elastic_transform_mat, data.transform_mats[symm_area]);
BLI_task_parallel_range(
@@ -354,11 +351,6 @@ void ED_sculpt_end_transform(struct bContext *C, Object *ob)
if (ss->filter_cache) {
SCULPT_filter_cache_free(ss);
}
- /* Force undo push to happen even inside transform operator, since the sculpt
- * undo system works separate from regular undo and this is require to properly
- * finish an undo step also when canceling. */
- const bool use_nested_undo = true;
- SCULPT_undo_push_end_ex(ob, use_nested_undo);
SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
}
@@ -426,7 +418,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op)
RNA_float_get(op->ptr, "mouse_x"),
RNA_float_get(op->ptr, "mouse_y"),
};
- if (SCULPT_stroke_get_location(C, stroke_location, mval)) {
+ if (SCULPT_stroke_get_location(C, stroke_location, mval, false)) {
copy_v3_v3(ss->pivot_pos, stroke_location);
}
}
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c
index 1e050fedf8e..19e4120db41 100644
--- a/source/blender/editors/sculpt_paint/sculpt_undo.c
+++ b/source/blender/editors/sculpt_paint/sculpt_undo.c
@@ -4,6 +4,29 @@
/** \file
* \ingroup edsculpt
* Implements the Sculpt Mode tools.
+ *
+ * Usage Guide
+ * ===========
+ *
+ * The sculpt undo system is a delta-based system. Each undo step stores
+ * the difference with the prior one.
+ *
+ * To use the sculpt undo system, you must call SCULPT_undo_push_begin
+ * inside an operator exec or invoke callback (ED_sculpt_undo_geometry_begin
+ * may be called if you wish to save a non-delta copy of the entire mesh).
+ * This will initialize the sculpt undo stack and set up an undo step.
+ *
+ * At the end of the operator you should call SCULPT_undo_push_end.
+ *
+ * SCULPT_undo_push_end and ED_sculpt_undo_geometry_begin both take a
+ * #wmOperatorType as an argument. There are _ex versions that allow a custom
+ * name; try to avoid using them. These can break the redo panel since it requires
+ * the undo push have the same name as the calling operator.
+ *
+ * NOTE: Sculpt undo steps are not appended to the global undo stack until
+ * the operator finishes. We use BKE_undosys_step_push_init_with_type to build
+ * a tentative undo step with is appended later when the operator ends.
+ * Operators must have the OPTYPE_UNDO flag set for this to work properly.
*/
#include <stddef.h>
@@ -30,6 +53,7 @@
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_key.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
@@ -144,6 +168,9 @@ struct PartialUpdateData {
PBVH *pbvh;
bool rebuild;
char *modified_grids;
+ bool *modified_hidden_verts;
+ bool *modified_mask_verts;
+ bool *modified_color_verts;
};
/**
@@ -167,8 +194,39 @@ static void update_cb_partial(PBVHNode *node, void *userdata)
}
}
else {
- if (BKE_pbvh_node_vert_update_check_any(data->pbvh, node)) {
- update_cb(node, &(data->rebuild));
+ if (BKE_pbvh_node_has_vert_with_normal_update_tag(data->pbvh, node)) {
+ BKE_pbvh_node_mark_update(node);
+ }
+ int verts_num;
+ const int *vert_indices;
+ BKE_pbvh_node_num_verts(data->pbvh, node, NULL, &verts_num);
+ BKE_pbvh_node_get_verts(data->pbvh, node, &vert_indices, NULL);
+ if (data->modified_mask_verts != NULL) {
+ for (int i = 0; i < verts_num; i++) {
+ if (data->modified_mask_verts[vert_indices[i]]) {
+ BKE_pbvh_node_mark_update_mask(node);
+ break;
+ }
+ }
+ }
+ if (data->modified_color_verts != NULL) {
+ for (int i = 0; i < verts_num; i++) {
+ if (data->modified_color_verts[vert_indices[i]]) {
+ BKE_pbvh_node_mark_update_color(node);
+ break;
+ }
+ }
+ }
+ if (data->modified_hidden_verts != NULL) {
+ for (int i = 0; i < verts_num; i++) {
+ if (data->modified_hidden_verts[vert_indices[i]]) {
+ if (data->rebuild) {
+ BKE_pbvh_node_mark_update_visibility(node);
+ }
+ BKE_pbvh_node_fully_hidden_set(node, 0);
+ break;
+ }
+ }
}
}
}
@@ -195,8 +253,10 @@ static bool sculpt_undo_restore_deformed(
static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, SculptUndoNode *unode)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
SculptSession *ss = ob->sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
MVert *mvert;
@@ -263,20 +323,20 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
if (ss->deform_modifiers_active) {
for (int i = 0; i < unode->totvert; i++) {
sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co);
- BKE_pbvh_vert_mark_update(ss->pbvh, index[i]);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i]));
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
swap_v3_v3(mvert[index[i]].co, unode->orig_co[i]);
- BKE_pbvh_vert_mark_update(ss->pbvh, index[i]);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i]));
}
}
}
else {
for (int i = 0; i < unode->totvert; i++) {
swap_v3_v3(mvert[index[i]].co, unode->co[i]);
- BKE_pbvh_vert_mark_update(ss->pbvh, index[i]);
+ BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i]));
}
}
}
@@ -305,22 +365,24 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt
return true;
}
-static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode)
+static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool *modified_vertices)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
SculptSession *ss = ob->sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
- if (unode->maxvert) {
- MVert *mvert = ss->mvert;
+ bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh);
+ if (unode->maxvert) {
for (int i = 0; i < unode->totvert; i++) {
- MVert *v = &mvert[unode->index[i]];
- if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) {
+ const int vert_index = unode->index[i];
+ if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != hide_vert[vert_index]) {
BLI_BITMAP_FLIP(unode->vert_hidden, i);
- v->flag ^= ME_HIDE;
- BKE_pbvh_vert_mark_update(ss->pbvh, unode->index[i]);
+ hide_vert[vert_index] = !hide_vert[vert_index];
+ modified_vertices[vert_index] = true;
}
}
}
@@ -335,10 +397,12 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode)
return true;
}
-static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
+static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode, bool *modified_vertices)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
SculptSession *ss = ob->sculpt;
bool modified = false;
@@ -360,17 +424,19 @@ static bool sculpt_undo_restore_color(bContext *C, SculptUndoNode *unode)
if (modified) {
for (int i = 0; i < unode->totvert; i++) {
- BKE_pbvh_vert_mark_update(ss->pbvh, unode->index[i]);
+ modified_vertices[unode->index[i]] = true;
}
}
return modified;
}
-static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
+static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode, bool *modified_vertices)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
SculptSession *ss = ob->sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
float *vmask;
@@ -385,7 +451,7 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
for (int i = 0; i < unode->totvert; i++) {
if (vmask[index[i]] != unode->mask[i]) {
SWAP(float, vmask[index[i]], unode->mask[i]);
- BKE_pbvh_vert_mark_update(ss->pbvh, index[i]);
+ modified_vertices[index[i]] = true;
}
}
}
@@ -415,12 +481,15 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode)
static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Mesh *me = BKE_object_get_original_mesh(ob);
- int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
+ int *face_sets = CustomData_add_layer(
+ &me->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, NULL, me->totpoly);
for (int i = 0; i < me->totpoly; i++) {
- face_sets[i] = unode->face_sets[i];
+ SWAP(int, face_sets[i], unode->face_sets[i]);
}
return false;
}
@@ -478,7 +547,7 @@ static void sculpt_undo_bmesh_enable(Object *ob, SculptUndoNode *unode)
.use_toolflags = false,
}));
BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK);
- SCULPT_dyntopo_node_layers_add(ss);
+
me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
/* Restore the BMLog using saved entries. */
@@ -569,8 +638,6 @@ static void sculpt_undo_geometry_restore_data(SculptUndoNodeGeometry *geometry,
CustomData_copy(
&geometry->pdata, &mesh->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly);
- BKE_mesh_update_customdata_pointers(mesh, false);
-
BKE_mesh_runtime_clear_cache(mesh);
}
@@ -665,7 +732,8 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
SculptSession *ss = ob->sculpt;
SubdivCCG *subdiv_ccg = ss->subdiv_ccg;
SculptUndoNode *unode;
@@ -699,7 +767,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask, false);
- SCULPT_visibility_sync_all_face_sets_to_vertices(ob);
+ SCULPT_visibility_sync_all_from_faces(ob);
BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
@@ -731,6 +799,12 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
}
}
+ /* The PBVH already keeps track of which vertices need updated normals, but it doesn't keep track
+ * of other updated. In order to tell the corresponding PBVH nodes to update, keep track of which
+ * elements were updated for specific layers. */
+ bool *modified_hidden_verts = NULL;
+ bool *modified_mask_verts = NULL;
+ bool *modified_color_verts = NULL;
char *undo_modified_grids = NULL;
bool use_multires_undo = false;
@@ -763,13 +837,19 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
}
break;
case SCULPT_UNDO_HIDDEN:
- if (sculpt_undo_restore_hidden(C, unode)) {
+ if (modified_hidden_verts == NULL) {
+ modified_hidden_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__);
+ }
+ if (sculpt_undo_restore_hidden(C, unode, modified_hidden_verts)) {
rebuild = true;
update_visibility = true;
}
break;
case SCULPT_UNDO_MASK:
- if (sculpt_undo_restore_mask(C, unode)) {
+ if (modified_mask_verts == NULL) {
+ modified_mask_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__);
+ }
+ if (sculpt_undo_restore_mask(C, unode, modified_mask_verts)) {
update = true;
update_mask = true;
}
@@ -777,7 +857,10 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
case SCULPT_UNDO_FACE_SETS:
break;
case SCULPT_UNDO_COLOR:
- if (sculpt_undo_restore_color(C, unode)) {
+ if (modified_color_verts == NULL) {
+ modified_color_verts = MEM_calloc_arrayN(ss->totvert, sizeof(bool), __func__);
+ }
+ if (sculpt_undo_restore_color(C, unode, modified_color_verts)) {
update = true;
}
@@ -828,6 +911,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
.rebuild = rebuild,
.pbvh = ss->pbvh,
.modified_grids = undo_modified_grids,
+ .modified_hidden_verts = modified_hidden_verts,
+ .modified_mask_verts = modified_mask_verts,
+ .modified_color_verts = modified_color_verts,
};
BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data);
BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw);
@@ -837,7 +923,6 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
}
if (update_visibility) {
- SCULPT_visibility_sync_all_vertex_to_face_sets(ss);
BKE_pbvh_update_visibility(ss->pbvh);
}
@@ -873,6 +958,9 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase
}
}
+ MEM_SAFE_FREE(modified_hidden_verts);
+ MEM_SAFE_FREE(modified_mask_verts);
+ MEM_SAFE_FREE(modified_color_verts);
MEM_SAFE_FREE(undo_modified_grids);
}
@@ -944,7 +1032,7 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
SculptUndoNode *unode;
unode = lb->first;
@@ -1087,8 +1175,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt
unode->co = MEM_callocN(alloc_size, "SculptUndoNode.co");
usculpt->undo_size += alloc_size;
- /* FIXME: Should explain why this is allocated here, to be freed in
- * `SCULPT_undo_push_end_ex()`? */
+ /* Needed for original data lookup. */
alloc_size = sizeof(*unode->no) * (size_t)allvert;
unode->no = MEM_callocN(alloc_size, "SculptUndoNode.no");
usculpt->undo_size += alloc_size;
@@ -1191,6 +1278,11 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode)
PBVH *pbvh = ob->sculpt->pbvh;
PBVHNode *node = unode->node;
+ const bool *hide_vert = BKE_pbvh_get_vert_hide(pbvh);
+ if (hide_vert == NULL) {
+ return;
+ }
+
if (unode->grids) {
/* Already stored during allocation. */
}
@@ -1202,7 +1294,7 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode)
BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert);
BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert);
for (int i = 0; i < allvert; i++) {
- BLI_BITMAP_SET(unode->vert_hidden, i, mvert[vert_indices[i]].flag & ME_HIDE);
+ BLI_BITMAP_SET(unode->vert_hidden, i, hide_vert[vert_indices[i]]);
}
}
}
@@ -1273,8 +1365,13 @@ static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType typ
unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets");
const int *face_sets = CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS);
- for (int i = 0; i < me->totpoly; i++) {
- unode->face_sets[i] = face_sets[i];
+ if (face_sets) {
+ for (int i = 0; i < me->totpoly; i++) {
+ unode->face_sets[i] = face_sets[i];
+ }
+ }
+ else {
+ memset(unode->face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * me->totpoly);
}
BLI_addtail(&usculpt->nodes, unode);
@@ -1432,7 +1529,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType
sculpt_undo_store_hidden(ob, unode);
break;
case SCULPT_UNDO_MASK:
- sculpt_undo_store_mask(ob, unode);
+ if (pbvh_has_mask(ss->pbvh)) {
+ sculpt_undo_store_mask(ob, unode);
+ }
break;
case SCULPT_UNDO_COLOR:
sculpt_undo_store_color(ob, unode);
@@ -1486,7 +1585,12 @@ static void sculpt_save_active_attribute(Object *ob, SculptAttrRef *attr)
attr->was_set = true;
}
-void SCULPT_undo_push_begin(Object *ob, const char *name)
+void SCULPT_undo_push_begin(Object *ob, const wmOperator *op)
+{
+ SCULPT_undo_push_begin_ex(ob, op->type->name);
+}
+
+void SCULPT_undo_push_begin_ex(Object *ob, const char *name)
{
UndoStack *ustack = ED_undo_stack_get();
@@ -1582,11 +1686,12 @@ static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr
*/
if (!layer) {
layer = BKE_id_attribute_search(&me->id, attr->name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
- eAttrDomain domain = layer ? BKE_id_attribute_domain(&me->id, layer) : ATTR_DOMAIN_NUM;
-
- if (layer && ED_geometry_attribute_convert(
- me, attr->name, layer->type, domain, attr->type, attr->domain)) {
- layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain);
+ if (layer) {
+ const eAttrDomain domain = BKE_id_attribute_domain(&me->id, layer);
+ if (ED_geometry_attribute_convert(
+ me, attr->name, layer->type, domain, attr->type, attr->domain)) {
+ layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain);
+ }
}
}
@@ -1595,7 +1700,7 @@ static void sculpt_undo_set_active_layer(struct bContext *C, SculptAttrRef *attr
CustomData *cdata = attr->domain == ATTR_DOMAIN_POINT ? &me->vdata : &me->ldata;
int totelem = attr->domain == ATTR_DOMAIN_POINT ? me->totvert : me->totloop;
- CustomData_add_layer_named(cdata, attr->type, CD_DEFAULT, NULL, totelem, attr->name);
+ CustomData_add_layer_named(cdata, attr->type, CD_SET_DEFAULT, NULL, totelem, attr->name);
layer = BKE_id_attribute_find(&me->id, attr->name, attr->type, attr->domain);
}
@@ -1728,7 +1833,8 @@ static void sculpt_undosys_step_decode(
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob && (ob->type == OB_MESH)) {
if (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT)) {
/* Pass. */
@@ -1774,9 +1880,15 @@ static void sculpt_undosys_step_free(UndoStep *us_p)
sculpt_undo_free_list(&us->data.nodes);
}
-void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name)
+void ED_sculpt_undo_geometry_begin(struct Object *ob, const wmOperator *op)
+{
+ SCULPT_undo_push_begin(ob, op);
+ SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY);
+}
+
+void ED_sculpt_undo_geometry_begin_ex(struct Object *ob, const char *name)
{
- SCULPT_undo_push_begin(ob, name);
+ SCULPT_undo_push_begin_ex(ob, name);
SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_GEOMETRY);
}
@@ -1889,7 +2001,7 @@ void ED_sculpt_undo_push_multires_mesh_begin(bContext *C, const char *str)
Object *object = CTX_data_active_object(C);
- SCULPT_undo_push_begin(object, str);
+ SCULPT_undo_push_begin_ex(object, str);
SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY);
geometry_unode->geometry_clear_pbvh = false;
diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c
index 8e1f4f4d495..4739fa52674 100644
--- a/source/blender/editors/sculpt_paint/sculpt_uv.c
+++ b/source/blender/editors/sculpt_paint/sculpt_uv.c
@@ -9,7 +9,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_ghash.h"
-#include "BLI_math.h"
+#include "BLI_math_base_safe.h"
#include "BLI_utildefines.h"
#include "DNA_brush_types.h"
@@ -22,6 +22,7 @@
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
+#include "BKE_image.h"
#include "BKE_mesh_mapping.h"
#include "BKE_paint.h"
@@ -30,6 +31,7 @@
#include "ED_image.h"
#include "ED_mesh.h"
#include "ED_screen.h"
+#include "ED_uvedit.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -42,6 +44,9 @@
#include "UI_view2d.h"
+/* When set, the UV element is on the boundary of the graph.
+ * i.e. Instead of a 2-dimensional laplace operator, use a 1-dimensional version.
+ * Visually, UV elements on the graph boundary appear as borders of the UV Island. */
#define MARK_BOUNDARY 1
typedef struct UvAdjacencyElement {
@@ -49,16 +54,17 @@ typedef struct UvAdjacencyElement {
UvElement *element;
/* uv pointer for convenience. Caution, this points to the original UVs! */
float *uv;
- /* general use flag (Used to check if Element is boundary here) */
- char flag;
+ /* Are we on locked in place? */
+ bool is_locked;
+ /* Are we on the boundary? */
+ bool is_boundary;
} UvAdjacencyElement;
typedef struct UvEdge {
uint uv1;
uint uv2;
- /* general use flag
- * (Used to check if edge is boundary here, and propagates to adjacency elements) */
- char flag;
+ /* Are we in the interior? */
+ bool is_interior;
} UvEdge;
typedef struct UVInitialStrokeElement {
@@ -90,13 +96,13 @@ typedef struct UvSculptData {
* to their coincident UV's */
UvAdjacencyElement *uv;
- /* ...Is what it says */
+ /* Total number of unique UVs. */
int totalUniqueUvs;
/* Edges used for adjacency info, used with laplacian smoothing */
UvEdge *uvedges;
- /* need I say more? */
+ /* Total number of #UvEdge. */
int totalUvEdges;
/* data for initial stroke, used by tools like grab */
@@ -116,8 +122,25 @@ typedef struct UvSculptData {
/* store invert flag here */
char invert;
+
+ /* Is constrain to image bounds active? */
+ bool constrain_to_bounds;
+
+ /* Base for constrain_to_bounds. */
+ float uv_base_offset[2];
} UvSculptData;
+static void apply_sculpt_data_constraints(UvSculptData *sculptdata, float uv[2])
+{
+ if (!sculptdata->constrain_to_bounds) {
+ return;
+ }
+ float u = sculptdata->uv_base_offset[0];
+ float v = sculptdata->uv_base_offset[1];
+ uv[0] = clamp_f(uv[0], u, u + 1.0f);
+ uv[1] = clamp_f(uv[1], v, v + 1.0f);
+}
+
/*********** Improved Laplacian Relaxation Operator ************************/
/* original code by Raul Fernandez Hernandez "farsthary" *
* adapted to uv smoothing by Antony Riakiatakis *
@@ -170,17 +193,14 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em,
}
for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
- float dist;
- /* This is supposed to happen only if "Pin Edges" is on,
- * since we have initialization on stroke start.
- * If ever uv brushes get their own mode we should check for toolsettings option too. */
- if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
+ if (sculptdata->uv[i].is_locked) {
continue;
}
sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord);
diff[1] /= aspectRatio;
- if ((dist = dot_v2v2(diff, diff)) <= radius) {
+ float dist = dot_v2v2(diff, diff);
+ if (dist <= radius) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
@@ -196,6 +216,8 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em,
0.5f * (tmp_uvdata[i].b[1] +
tmp_uvdata[i].sum_b[1] / tmp_uvdata[i].ncounter));
+ apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv);
+
for (element = sculptdata->uv[i].element; element; element = element->next) {
MLoopUV *luv;
BMLoop *l;
@@ -211,9 +233,16 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em,
}
}
- MEM_freeN(tmp_uvdata);
+ MEM_SAFE_FREE(tmp_uvdata);
}
+/* Legacy version which only does laplacian relaxation.
+ * Probably a little faster as it caches UvEdges.
+ * Mostly preserved for comparison with `HC_relaxation_iteration_uv`.
+ * Once the HC method has been merged into `relaxation_iteration_uv`,
+ * all the `HC_*` and `laplacian_*` specific functions can probably be removed.
+ */
+
static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
UvSculptData *sculptdata,
const float mouse_coord[2],
@@ -233,14 +262,19 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
/* counting neighbors */
for (i = 0; i < sculptdata->totalUvEdges; i++) {
UvEdge *tmpedge = sculptdata->uvedges + i;
- tmp_uvdata[tmpedge->uv1].ncounter++;
- tmp_uvdata[tmpedge->uv2].ncounter++;
-
- add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv);
- add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
+ bool code1 = sculptdata->uv[sculptdata->uvedges[i].uv1].is_boundary;
+ bool code2 = sculptdata->uv[sculptdata->uvedges[i].uv2].is_boundary;
+ if (code1 || (code1 == code2)) {
+ tmp_uvdata[tmpedge->uv2].ncounter++;
+ add_v2_v2(tmp_uvdata[tmpedge->uv2].sum_co, sculptdata->uv[tmpedge->uv1].uv);
+ }
+ if (code2 || (code1 == code2)) {
+ tmp_uvdata[tmpedge->uv1].ncounter++;
+ add_v2_v2(tmp_uvdata[tmpedge->uv1].sum_co, sculptdata->uv[tmpedge->uv2].uv);
+ }
}
- /* Original Lacplacian algorithm included removal of normal component of translation.
+ /* Original Laplacian algorithm included removal of normal component of translation.
* here it is not needed since we translate along the UV plane always. */
for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
copy_v2_v2(tmp_uvdata[i].p, tmp_uvdata[i].sum_co);
@@ -248,17 +282,14 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
}
for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
- float dist;
- /* This is supposed to happen only if "Pin Edges" is on,
- * since we have initialization on stroke start.
- * If ever uv brushes get their own mode we should check for toolsettings option too. */
- if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
+ if (sculptdata->uv[i].is_locked) {
continue;
}
sub_v2_v2v2(diff, sculptdata->uv[i].uv, mouse_coord);
diff[1] /= aspectRatio;
- if ((dist = dot_v2v2(diff, diff)) <= radius) {
+ float dist = dot_v2v2(diff, diff);
+ if (dist <= radius) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
@@ -268,6 +299,8 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] +
strength * tmp_uvdata[i].p[1];
+ apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv);
+
for (element = sculptdata->uv[i].element; element; element = element->next) {
MLoopUV *luv;
BMLoop *l;
@@ -283,7 +316,154 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em,
}
}
- MEM_freeN(tmp_uvdata);
+ MEM_SAFE_FREE(tmp_uvdata);
+}
+
+static void add_weighted_edge(float (*delta_buf)[3],
+ const UvElement *storage,
+ const UvElement *ele_next,
+ const UvElement *ele_prev,
+ const MLoopUV *luv_next,
+ const MLoopUV *luv_prev,
+ const float weight)
+{
+ float delta[2];
+ sub_v2_v2v2(delta, luv_next->uv, luv_prev->uv);
+
+ bool code1 = (ele_prev->flag & MARK_BOUNDARY);
+ bool code2 = (ele_next->flag & MARK_BOUNDARY);
+ if (code1 || (code1 == code2)) {
+ int index_next = ele_next - storage;
+ delta_buf[index_next][0] -= delta[0] * weight;
+ delta_buf[index_next][1] -= delta[1] * weight;
+ delta_buf[index_next][2] += fabsf(weight);
+ }
+ if (code2 || (code1 == code2)) {
+ int index_prev = ele_prev - storage;
+ delta_buf[index_prev][0] += delta[0] * weight;
+ delta_buf[index_prev][1] += delta[1] * weight;
+ delta_buf[index_prev][2] += fabsf(weight);
+ }
+}
+
+static float tri_weight_v3(int method, const float *v1, const float *v2, const float *v3)
+{
+ switch (method) {
+ case UV_SCULPT_TOOL_RELAX_LAPLACIAN:
+ case UV_SCULPT_TOOL_RELAX_HC:
+ return 1.0f;
+ case UV_SCULPT_TOOL_RELAX_COTAN:
+ return cotangent_tri_weight_v3(v1, v2, v3);
+ default:
+ BLI_assert_unreachable();
+ }
+ return 0.0f;
+}
+
+static void relaxation_iteration_uv(BMEditMesh *em,
+ UvSculptData *sculptdata,
+ const float mouse_coord[2],
+ const float alpha,
+ const float radius_squared,
+ const float aspect_ratio,
+ const int method)
+{
+ if (method == UV_SCULPT_TOOL_RELAX_HC) {
+ HC_relaxation_iteration_uv(em, sculptdata, mouse_coord, alpha, radius_squared, aspect_ratio);
+ return;
+ }
+ if (method == UV_SCULPT_TOOL_RELAX_LAPLACIAN) {
+ laplacian_relaxation_iteration_uv(
+ em, sculptdata, mouse_coord, alpha, radius_squared, aspect_ratio);
+ return;
+ }
+
+ struct UvElement **head_table = BM_uv_element_map_ensure_head_table(sculptdata->elementMap);
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ BLI_assert(cd_loop_uv_offset >= 0);
+
+ const int total_uvs = sculptdata->elementMap->total_uvs;
+ float(*delta_buf)[3] = (float(*)[3])MEM_callocN(total_uvs * sizeof(float[3]), __func__);
+
+ const UvElement *storage = sculptdata->elementMap->storage;
+ for (int j = 0; j < total_uvs; j++) {
+ const UvElement *ele_curr = storage + j;
+ const BMFace *efa = ele_curr->l->f;
+ const UvElement *ele_next = BM_uv_element_get(sculptdata->elementMap, efa, ele_curr->l->next);
+ const UvElement *ele_prev = BM_uv_element_get(sculptdata->elementMap, efa, ele_curr->l->prev);
+
+ const float *v_curr_co = ele_curr->l->v->co;
+ const float *v_prev_co = ele_prev->l->v->co;
+ const float *v_next_co = ele_next->l->v->co;
+
+ const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(ele_curr->l, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(ele_next->l, cd_loop_uv_offset);
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(ele_prev->l, cd_loop_uv_offset);
+
+ const UvElement *head_curr = head_table[ele_curr - sculptdata->elementMap->storage];
+ const UvElement *head_next = head_table[ele_next - sculptdata->elementMap->storage];
+ const UvElement *head_prev = head_table[ele_prev - sculptdata->elementMap->storage];
+
+ /* If the mesh is triangulated with no boundaries, only one edge is required. */
+ const float weight_curr = tri_weight_v3(method, v_curr_co, v_prev_co, v_next_co);
+ add_weighted_edge(delta_buf, storage, head_next, head_prev, luv_next, luv_prev, weight_curr);
+
+ /* Triangulated with a boundary? We need the incoming edges to solve the boundary. */
+ const float weight_prev = tri_weight_v3(method, v_prev_co, v_curr_co, v_next_co);
+ add_weighted_edge(delta_buf, storage, head_next, head_curr, luv_next, luv_curr, weight_prev);
+
+ if (method == UV_SCULPT_TOOL_RELAX_LAPLACIAN) {
+ /* Laplacian method has zero weights on virtual edges. */
+ continue;
+ }
+
+ /* Meshes with quads (or other n-gons) need "virtual" edges too. */
+ const float weight_next = tri_weight_v3(method, v_next_co, v_curr_co, v_prev_co);
+ add_weighted_edge(delta_buf, storage, head_prev, head_curr, luv_prev, luv_curr, weight_next);
+ }
+
+ Brush *brush = BKE_paint_brush(sculptdata->uvsculpt);
+ for (int i = 0; i < sculptdata->totalUniqueUvs; i++) {
+ UvAdjacencyElement *adj_el = &sculptdata->uv[i];
+ if (adj_el->is_locked) {
+ continue; /* Locked UVs can't move. */
+ }
+
+ /* Is UV within brush's influence? */
+ float diff[2];
+ sub_v2_v2v2(diff, adj_el->uv, mouse_coord);
+ diff[1] /= aspect_ratio;
+ const float dist_squared = len_squared_v2(diff);
+ if (dist_squared > radius_squared) {
+ continue;
+ }
+ const float strength = alpha * BKE_brush_curve_strength_clamped(
+ brush, sqrtf(dist_squared), sqrtf(radius_squared));
+
+ const float *delta_sum = delta_buf[adj_el->element - storage];
+
+ {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(adj_el->element->l, cd_loop_uv_offset);
+ BLI_assert(adj_el->uv == luv->uv); /* Only true for head. */
+ adj_el->uv[0] = luv->uv[0] + strength * safe_divide(delta_sum[0], delta_sum[2]);
+ adj_el->uv[1] = luv->uv[1] + strength * safe_divide(delta_sum[1], delta_sum[2]);
+ apply_sculpt_data_constraints(sculptdata, adj_el->uv);
+ }
+
+ /* Copy UV co-ordinates to all UvElements. */
+ UvElement *tail = adj_el->element;
+ while (tail) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(tail->l, cd_loop_uv_offset);
+ copy_v2_v2(luv->uv, adj_el->uv);
+ tail = tail->next;
+ if (tail && tail->separate) {
+ break;
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(delta_buf);
}
static void uv_sculpt_stroke_apply(bContext *C,
@@ -327,17 +507,15 @@ static void uv_sculpt_stroke_apply(bContext *C,
int i;
alpha *= invert;
for (i = 0; i < sculptdata->totalUniqueUvs; i++) {
- float dist, diff[2];
- /* This is supposed to happen only if "Lock Borders" is on,
- * since we have initialization on stroke start.
- * If ever uv brushes get their own mode we should check for toolsettings option too. */
- if (sculptdata->uv[i].flag & MARK_BOUNDARY) {
+ if (sculptdata->uv[i].is_locked) {
continue;
}
+ float diff[2];
sub_v2_v2v2(diff, sculptdata->uv[i].uv, co);
diff[1] /= aspectRatio;
- if ((dist = dot_v2v2(diff, diff)) <= radius) {
+ float dist = dot_v2v2(diff, diff);
+ if (dist <= radius) {
UvElement *element;
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
@@ -346,6 +524,8 @@ static void uv_sculpt_stroke_apply(bContext *C,
sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f;
sculptdata->uv[i].uv[1] -= strength * diff[1] * 0.001f;
+ apply_sculpt_data_constraints(sculptdata, sculptdata->uv[i].uv);
+
for (element = sculptdata->uv[i].element; element; element = element->next) {
MLoopUV *luv;
BMLoop *l;
@@ -363,16 +543,11 @@ static void uv_sculpt_stroke_apply(bContext *C,
}
/*
- * Smooth Tool
+ * Relax Tool
*/
else if (tool == UV_SCULPT_TOOL_RELAX) {
- uint method = toolsettings->uv_relax_method;
- if (method == UV_SCULPT_TOOL_RELAX_HC) {
- HC_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio);
- }
- else {
- laplacian_relaxation_iteration_uv(em, sculptdata, co, alpha, radius, aspectRatio);
- }
+ relaxation_iteration_uv(
+ em, sculptdata, co, alpha, radius, aspectRatio, toolsettings->uv_relax_method);
}
/*
@@ -392,6 +567,8 @@ static void uv_sculpt_stroke_apply(bContext *C,
sculptdata->uv[uvindex].uv[1] =
sculptdata->initial_stroke->initialSelection[i].initial_uv[1] + strength * diff[1];
+ apply_sculpt_data_constraints(sculptdata, sculptdata->uv[uvindex].uv);
+
for (element = sculptdata->uv[uvindex].element; element; element = element->next) {
MLoopUV *luv;
BMLoop *l;
@@ -405,32 +582,32 @@ static void uv_sculpt_stroke_apply(bContext *C,
copy_v2_v2(luv->uv, sculptdata->uv[uvindex].uv);
}
}
+ if (sima->flag & SI_LIVE_UNWRAP) {
+ ED_uvedit_live_unwrap_re_solve();
+ }
}
}
static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op)
{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ if (sima->flag & SI_LIVE_UNWRAP) {
+ ED_uvedit_live_unwrap_end(false);
+ }
UvSculptData *data = op->customdata;
if (data->timer) {
WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), data->timer);
}
- if (data->elementMap) {
- BM_uv_element_map_free(data->elementMap);
- }
- if (data->uv) {
- MEM_freeN(data->uv);
- }
- if (data->uvedges) {
- MEM_freeN(data->uvedges);
- }
+ BM_uv_element_map_free(data->elementMap);
+ data->elementMap = NULL;
+ MEM_SAFE_FREE(data->uv);
+ MEM_SAFE_FREE(data->uvedges);
if (data->initial_stroke) {
- if (data->initial_stroke->initialSelection) {
- MEM_freeN(data->initial_stroke->initialSelection);
- }
- MEM_freeN(data->initial_stroke);
+ MEM_SAFE_FREE(data->initial_stroke->initialSelection);
+ MEM_SAFE_FREE(data->initial_stroke);
}
- MEM_freeN(data);
+ MEM_SAFE_FREE(data);
op->customdata = NULL;
}
@@ -441,7 +618,7 @@ static int uv_element_offset_from_face_get(
if (!element || (doIslands && element->island != island_index)) {
return -1;
}
- return element - map->buf;
+ return element - map->storage;
}
static uint uv_edge_hash(const void *key)
@@ -461,6 +638,17 @@ static bool uv_edge_compare(const void *a, const void *b)
return true;
}
+static void set_element_flag(UvElement *element, const int flag)
+{
+ while (element) {
+ element->flag |= flag;
+ element = element->next;
+ if (!element || element->separate) {
+ break;
+ }
+ }
+}
+
static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
@@ -475,7 +663,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
BKE_curvemapping_init(ts->uvsculpt->paint.brush->curve);
if (data) {
- int counter = 0, i;
ARegion *region = CTX_wm_region(C);
float co[2];
BMFace *efa;
@@ -489,8 +676,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
bool do_island_optimization = !(ts->uv_sculpt_settings & UV_SCULPT_ALL_ISLANDS);
int island_index = 0;
- /* Holds, for each UvElement in elementMap, a pointer to its unique UV. */
- int *uniqueUv;
data->tool = (RNA_enum_get(op->ptr, "mode") == BRUSH_STROKE_SMOOTH) ?
UV_SCULPT_TOOL_RELAX :
ts->uvsculpt->paint.brush->uv_sculpt_tool;
@@ -498,23 +683,13 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
data->uvsculpt = &ts->uvsculpt->paint;
- if (do_island_optimization) {
- /* We will need island information */
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, true);
- }
- else {
- data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, true);
- }
- }
- else {
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- data->elementMap = BM_uv_element_map_create(bm, scene, false, false, true, false);
- }
- else {
- data->elementMap = BM_uv_element_map_create(bm, scene, true, false, true, false);
- }
- }
+ /* Winding was added to island detection in 5197aa04c6bd
+ * However the sculpt tools can flip faces, potentially creating orphaned islands.
+ * See T100132 */
+ const bool use_winding = false;
+ const bool use_seams = true;
+ data->elementMap = BM_uv_element_map_create(
+ bm, scene, false, use_winding, use_seams, do_island_optimization);
if (!data->elementMap) {
uv_sculpt_stroke_exit(C, op);
@@ -535,27 +710,22 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
}
/* Count 'unique' UV's */
- for (i = 0; i < data->elementMap->totalUVs; i++) {
- if (data->elementMap->buf[i].separate &&
- (!do_island_optimization || data->elementMap->buf[i].island == island_index)) {
- counter++;
- }
+ int unique_uvs = data->elementMap->total_unique_uvs;
+ if (do_island_optimization) {
+ unique_uvs = data->elementMap->island_total_unique_uvs[island_index];
}
/* Allocate the unique uv buffers */
- data->uv = MEM_mallocN(sizeof(*data->uv) * counter, "uv_brush_unique_uvs");
- uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->totalUVs,
- "uv_brush_unique_uv_map");
+ data->uv = MEM_callocN(sizeof(*data->uv) * unique_uvs, "uv_brush_unique_uvs");
+ /* Holds, for each UvElement in elementMap, an index of its unique UV. */
+ int *uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->total_uvs,
+ "uv_brush_unique_uv_map");
edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash");
/* we have at most totalUVs edges */
- edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges");
+ edges = MEM_callocN(sizeof(*edges) * data->elementMap->total_uvs, "uv_brush_all_edges");
if (!data->uv || !uniqueUv || !edgeHash || !edges) {
- if (edges) {
- MEM_freeN(edges);
- }
- if (uniqueUv) {
- MEM_freeN(uniqueUv);
- }
+ MEM_SAFE_FREE(edges);
+ MEM_SAFE_FREE(uniqueUv);
if (edgeHash) {
BLI_ghash_free(edgeHash, NULL, NULL);
}
@@ -563,12 +733,12 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
return NULL;
}
- data->totalUniqueUvs = counter;
- /* So that we can use this as index for the UvElements */
- counter = -1;
+ data->totalUniqueUvs = unique_uvs;
+ /* Index for the UvElements. */
+ int counter = -1;
/* initialize the unique UVs */
- for (i = 0; i < bm->totvert; i++) {
- UvElement *element = data->elementMap->vert[i];
+ for (int i = 0; i < bm->totvert; i++) {
+ UvElement *element = data->elementMap->vertex[i];
for (; element; element = element->next) {
if (element->separate) {
if (do_island_optimization && (element->island != island_index)) {
@@ -584,13 +754,18 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
counter++;
data->uv[counter].element = element;
- data->uv[counter].flag = 0;
data->uv[counter].uv = luv->uv;
+ if (data->tool != UV_SCULPT_TOOL_GRAB) {
+ if (luv->flag & MLOOPUV_PINNED) {
+ data->uv[counter].is_locked = true;
+ }
+ }
}
/* Pointer arithmetic to the rescue, as always :). */
- uniqueUv[element - data->elementMap->buf] = counter;
+ uniqueUv[element - data->elementMap->storage] = counter;
}
}
+ BLI_assert(counter + 1 == unique_uvs);
/* Now, on to generate our uv connectivity data */
counter = 0;
@@ -600,7 +775,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
data->elementMap, efa, l, island_index, do_island_optimization);
int offset2, itmp2 = uv_element_offset_from_face_get(
data->elementMap, efa, l->next, island_index, do_island_optimization);
- char *flag;
/* Skip edge if not found(unlikely) or not on valid island */
if (itmp1 == -1 || itmp2 == -1) {
@@ -610,7 +784,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
offset1 = uniqueUv[itmp1];
offset2 = uniqueUv[itmp2];
- edges[counter].flag = 0;
/* Using an order policy, sort UV's according to address space.
* This avoids having two different UvEdges with the same UV's on different positions. */
if (offset1 < offset2) {
@@ -621,58 +794,65 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
edges[counter].uv1 = offset2;
edges[counter].uv2 = offset1;
}
- /* Hack! Set the value of the key to its flag.
- * Now we can set the flag when an edge exists twice :) */
- flag = BLI_ghash_lookup(edgeHash, &edges[counter]);
- if (flag) {
- *flag = 1;
+ UvEdge *prev_edge = BLI_ghash_lookup(edgeHash, &edges[counter]);
+ if (prev_edge) {
+ prev_edge->is_interior = true;
+ edges[counter].is_interior = true;
}
else {
- /* Hack mentioned */
- BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter].flag);
+ BLI_ghash_insert(edgeHash, &edges[counter], &edges[counter]);
}
counter++;
}
}
- MEM_freeN(uniqueUv);
+ MEM_SAFE_FREE(uniqueUv);
/* Allocate connectivity data, we allocate edges once */
- data->uvedges = MEM_mallocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash),
+ data->uvedges = MEM_callocN(sizeof(*data->uvedges) * BLI_ghash_len(edgeHash),
"uv_brush_edge_connectivity_data");
if (!data->uvedges) {
BLI_ghash_free(edgeHash, NULL, NULL);
- MEM_freeN(edges);
+ MEM_SAFE_FREE(edges);
uv_sculpt_stroke_exit(C, op);
return NULL;
}
/* fill the edges with data */
- i = 0;
- GHASH_ITER (gh_iter, edgeHash) {
- data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter));
+ {
+ int i = 0;
+ GHASH_ITER (gh_iter, edgeHash) {
+ data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter));
+ }
+ data->totalUvEdges = BLI_ghash_len(edgeHash);
}
- data->totalUvEdges = BLI_ghash_len(edgeHash);
/* cleanup temporary stuff */
BLI_ghash_free(edgeHash, NULL, NULL);
- MEM_freeN(edges);
+ MEM_SAFE_FREE(edges);
/* transfer boundary edge property to UV's */
- if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) {
- for (i = 0; i < data->totalUvEdges; i++) {
- if (!data->uvedges[i].flag) {
- data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY;
- data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY;
+ for (int i = 0; i < data->totalUvEdges; i++) {
+ if (!data->uvedges[i].is_interior) {
+ data->uv[data->uvedges[i].uv1].is_boundary = true;
+ data->uv[data->uvedges[i].uv2].is_boundary = true;
+ if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) {
+ data->uv[data->uvedges[i].uv1].is_locked = true;
+ data->uv[data->uvedges[i].uv2].is_locked = true;
}
+ set_element_flag(data->uv[data->uvedges[i].uv1].element, MARK_BOUNDARY);
+ set_element_flag(data->uv[data->uvedges[i].uv2].element, MARK_BOUNDARY);
}
}
+ SpaceImage *sima = CTX_wm_space_image(C);
+ data->constrain_to_bounds = (sima->flag & SI_CLIP_UV);
+ BKE_image_find_nearest_tile_with_offset(sima->image, co, data->uv_base_offset);
+
/* Allocate initial selection for grab tool */
if (data->tool == UV_SCULPT_TOOL_GRAB) {
float radius, radius_root;
UvSculptData *sculptdata = (UvSculptData *)op->customdata;
- SpaceImage *sima;
int width, height;
float aspectRatio;
float alpha, zoomx, zoomy;
@@ -681,7 +861,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
alpha = BKE_brush_alpha_get(scene, brush);
radius = BKE_brush_size_get(scene, brush);
- sima = CTX_wm_space_image(C);
ED_space_image_get_size(sima, &width, &height);
ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
@@ -706,16 +885,16 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
copy_v2_v2(data->initial_stroke->init_coord, co);
counter = 0;
-
- for (i = 0; i < data->totalUniqueUvs; i++) {
- float dist, diff[2];
- if (data->uv[i].flag & MARK_BOUNDARY) {
+ for (int i = 0; i < data->totalUniqueUvs; i++) {
+ if (data->uv[i].is_locked) {
continue;
}
+ float diff[2];
sub_v2_v2v2(diff, data->uv[i].uv, co);
diff[1] /= aspectRatio;
- if ((dist = dot_v2v2(diff, diff)) <= radius) {
+ float dist = dot_v2v2(diff, diff);
+ if (dist <= radius) {
float strength;
strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root);
@@ -727,6 +906,9 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
}
data->initial_stroke->totalInitialSelected = counter;
+ if (sima->flag & SI_LIVE_UNWRAP) {
+ ED_uvedit_live_unwrap_begin(scene, obedit);
+ }
}
}
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 2a8a2be8b65..08b795db0c3 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -340,7 +340,8 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op)
AUD_DeviceSpecs specs;
AUD_Container container;
AUD_Codec codec;
- const char *result;
+ int result;
+ char error_message[1024] = {'\0'};
sound_bake_animation_exec(C, op);
@@ -372,7 +373,9 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op)
codec,
bitrate,
NULL,
- NULL);
+ NULL,
+ error_message,
+ sizeof(error_message));
}
else {
result = AUD_mixdown(scene_eval->sound_scene,
@@ -385,13 +388,15 @@ static int sound_mixdown_exec(bContext *C, wmOperator *op)
codec,
bitrate,
NULL,
- NULL);
+ NULL,
+ error_message,
+ sizeof(error_message));
}
BKE_sound_reset_scene_specs(scene_eval);
- if (result) {
- BKE_report(op->reports, RPT_ERROR, result);
+ if (!result) {
+ BKE_report(op->reports, RPT_ERROR, error_message);
return OPERATOR_CANCELLED;
}
#else /* WITH_AUDASPACE */
diff --git a/source/blender/editors/space_action/CMakeLists.txt b/source/blender/editors/space_action/CMakeLists.txt
index 841bd5cf91b..b9e27c4de49 100644
--- a/source/blender/editors/space_action/CMakeLists.txt
+++ b/source/blender/editors/space_action/CMakeLists.txt
@@ -10,7 +10,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c
index 8f97a58451e..79047b171ef 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -169,7 +169,7 @@ static bool action_new_poll(bContext *C)
SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C);
Object *ob = CTX_data_active_object(C);
- /* For now, actions are only for the active object, and on object and shapekey levels... */
+ /* For now, actions are only for the active object, and on object and shape-key levels... */
if (saction->mode == SACTCONT_ACTION) {
/* XXX: This assumes that actions are assigned to the active object in this mode */
if (ob) {
@@ -460,7 +460,8 @@ static bool action_stash_create_poll(bContext *C)
Scene *scene = CTX_data_scene(C);
if (!(scene->flag & SCE_NLA_EDIT_ON)) {
- /* For now, actions are only for the active object, and on object and shapekey levels... */
+ /* For now, actions are only for the active object, and on object and shape-key levels...
+ */
return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY);
}
}
diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c
index 7bdf2478862..eb56c6c4b54 100644
--- a/source/blender/editors/space_action/action_draw.c
+++ b/source/blender/editors/space_action/action_draw.c
@@ -206,7 +206,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
@@ -617,7 +617,7 @@ void timeline_draw_cache(SpaceAction *saction, Object *ob, Scene *scene)
uint pos_id = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index e97b666810c..0803c5dc575 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -1006,7 +1006,7 @@ static bool delete_action_keys(bAnimContext *ac)
AnimData *adt = ale->adt;
/* delete selected keyframes only */
- changed = delete_fcurve_keys(fcu);
+ changed = BKE_fcurve_delete_keys_selected(fcu);
/* Only delete curve too if it won't be doing anything anymore */
if (BKE_fcurve_is_empty(fcu)) {
@@ -1326,7 +1326,7 @@ static int actkeys_expo_exec(bContext *C, wmOperator *op)
void ACTION_OT_extrapolation_type(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Set Keyframe Extrapolation";
+ ot->name = "Set F-Curve Extrapolation";
ot->idname = "ACTION_OT_extrapolation_type";
ot->description = "Set extrapolation mode for selected F-Curves";
@@ -1364,7 +1364,7 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op)
/* set handle type */
ANIM_animdata_keyframe_callback(&ac,
- (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE |
+ (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS |
ANIMFILTER_FCURVESONLY),
ANIM_editkeyframes_ipo(mode));
@@ -1414,7 +1414,7 @@ static int actkeys_easing_exec(bContext *C, wmOperator *op)
/* set handle type */
ANIM_animdata_keyframe_callback(&ac,
- (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE |
+ (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE |
ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS |
ANIMFILTER_FCURVESONLY),
ANIM_editkeyframes_easing(mode));
@@ -1473,7 +1473,7 @@ static void sethandles_action_keys(bAnimContext *ac, short mode)
/* any selected keyframes for editing? */
if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) {
/* change type of selected handles */
- ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@@ -1790,11 +1790,11 @@ static void snap_action_keys(bAnimContext *ac, short mode)
}
else if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
}
else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
}
ale->update |= ANIM_UPDATE_DEFAULT;
@@ -1914,11 +1914,11 @@ static void mirror_action_keys(bAnimContext *ac, short mode)
}
else if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
}
else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
}
ale->update |= ANIM_UPDATE_DEFAULT;
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index d1a8592ae9d..9d45a2a3b89 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -449,7 +449,7 @@ static void box_select_action(bAnimContext *ac, const rcti rect, short mode, sho
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- /* Get beztriple editing/validation funcs. */
+ /* Get beztriple editing/validation functions. */
sel_data.select_cb = ANIM_editkeyframes_select(selectmode);
if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) {
@@ -585,7 +585,7 @@ void ACTION_OT_select_box(wmOperatorType *ot)
ot->poll = ED_operator_action_active;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO;
/* rna */
ot->prop = RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
@@ -693,7 +693,7 @@ static void region_select_action_keys(
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- /* Get beztriple editing/validation funcs. */
+ /* Get beztriple editing/validation functions. */
sel_data.select_cb = ANIM_editkeyframes_select(selectmode);
sel_data.ok_cb = ANIM_editkeyframes_ok(mode);
@@ -919,7 +919,7 @@ static const EnumPropertyItem prop_column_select_types[] = {
/* ------------------- */
/* Selects all visible keyframes between the specified markers */
-/* TODO(campbell): this is almost an _exact_ duplicate of a function of the same name in
+/* TODO(@campbellbarton): this is almost an _exact_ duplicate of a function of the same name in
* graph_select.c should de-duplicate. */
static void markers_selectkeys_between(bAnimContext *ac)
{
@@ -936,7 +936,7 @@ static void markers_selectkeys_between(bAnimContext *ac)
min -= 0.5f;
max += 0.5f;
- /* get editing funcs + data */
+ /* Get editing functions + data. */
ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
select_cb = ANIM_editkeyframes_select(SELECT_ADD);
@@ -1129,6 +1129,7 @@ void ACTION_OT_select_column(wmOperatorType *ot)
/* props */
ot->prop = RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
+ RNA_def_property_flag(ot->prop, PROP_HIDDEN);
}
/* ******************** Select Linked Operator *********************** */
diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c
index 3e507f73d1a..b54750accb0 100644
--- a/source/blender/editors/space_action/space_action.c
+++ b/source/blender/editors/space_action/space_action.c
@@ -90,7 +90,6 @@ static SpaceLink *action_create(const ScrArea *area, const Scene *scene)
BLI_addtail(&saction->regionbase, region);
region->regiontype = RGN_TYPE_UI;
region->alignment = RGN_ALIGN_RIGHT;
- region->flag = RGN_FLAG_HIDDEN;
/* main region */
region = MEM_callocN(sizeof(ARegion), "main region for action");
@@ -308,7 +307,7 @@ static void action_header_region_draw(const bContext *C, ARegion *region)
static void action_channel_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -402,7 +401,7 @@ static void saction_channel_region_message_subscribe(const wmRegionMessageSubscr
static void action_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -500,7 +499,7 @@ static void saction_main_region_message_subscribe(const wmRegionMessageSubscribe
static void action_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
SpaceAction *saction = (SpaceAction *)area->spacedata.first;
/* context changes */
@@ -654,7 +653,7 @@ static void action_header_region_listener(const wmRegionListenerParams *params)
{
ScrArea *area = params->area;
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
SpaceAction *saction = (SpaceAction *)area->spacedata.first;
/* context changes */
@@ -729,7 +728,7 @@ static void action_buttons_area_draw(const bContext *C, ARegion *region)
static void action_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -841,7 +840,7 @@ void ED_spacetype_action(void)
ARegionType *art;
st->spaceid = SPACE_ACTION;
- strncpy(st->name, "Action", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Action");
st->create = action_create;
st->free = action_free;
@@ -906,5 +905,8 @@ void ED_spacetype_action(void)
action_buttons_register(art);
+ art = ED_area_type_hud(st->spaceid);
+ BLI_addhead(&st->regiontypes, art);
+
BKE_spacetype_register(st);
}
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index d53fe2efb03..3d964a95bc0 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -163,6 +163,7 @@ void ED_spacemacros_init(void)
ED_operatormacros_sequencer();
ED_operatormacros_paint();
ED_operatormacros_gpencil();
+ ED_operatormacros_nla();
/* Register dropboxes (can use macros). */
ED_dropboxes_ui();
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index e2f1df74446..d0ad510f5cf 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -9,8 +9,8 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
+ ../../bmesh
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
@@ -35,7 +35,6 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
add_definitions(-DWITH_POINT_CLOUD)
- add_definitions(-DWITH_NEW_CURVES_TYPE)
endif()
blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 5780b0c9df7..3dc522ffcb9 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -155,7 +155,8 @@ static bool buttons_context_path_collection(const bContext *C,
/* if we have a view layer, use the view layer's active collection */
if (buttons_context_path_view_layer(path, window)) {
ViewLayer *view_layer = path->ptr[path->len - 1].data;
- Collection *c = view_layer->active_collection->collection;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Collection *c = BKE_view_layer_active_collection_get(view_layer)->collection;
/* Do not show collection tab for master collection. */
if (c == scene->master_collection) {
@@ -209,7 +210,7 @@ static bool buttons_context_path_object(ButsContextPath *path)
}
ViewLayer *view_layer = ptr->data;
- Object *ob = (view_layer->basact) ? view_layer->basact->object : NULL;
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob) {
RNA_id_pointer_create(&ob->id, &path->ptr[path->len]);
@@ -258,11 +259,9 @@ static bool buttons_context_path_data(ButsContextPath *path, int type)
if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) {
return true;
}
-#ifdef WITH_NEW_CURVES_TYPE
if (RNA_struct_is_a(ptr->type, &RNA_Curves) && (ELEM(type, -1, OB_CURVES))) {
return true;
}
-#endif
#ifdef WITH_POINT_CLOUD
if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (ELEM(type, -1, OB_POINTCLOUD))) {
return true;
@@ -644,8 +643,10 @@ static bool buttons_context_path(
static bool buttons_shading_context(const bContext *C, int mainb)
{
wmWindow *window = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(window);
ViewLayer *view_layer = WM_window_get_active_view_layer(window);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ELEM(mainb, BCONTEXT_MATERIAL, BCONTEXT_WORLD, BCONTEXT_TEXTURE)) {
return true;
@@ -660,8 +661,10 @@ static bool buttons_shading_context(const bContext *C, int mainb)
static int buttons_shading_new_context(const bContext *C, int flag)
{
wmWindow *window = CTX_wm_window(C);
+ const Scene *scene = WM_window_get_active_scene(window);
ViewLayer *view_layer = WM_window_get_active_view_layer(window);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (flag & (1 << BCONTEXT_MATERIAL)) {
return BCONTEXT_MATERIAL;
@@ -830,9 +833,7 @@ const char *buttons_context_dir[] = {
"line_style",
"collection",
"gpencil",
-#ifdef WITH_NEW_CURVES_TYPE
"curves",
-#endif
#ifdef WITH_POINT_CLOUD
"pointcloud",
#endif
@@ -926,12 +927,10 @@ int /*eContextResult*/ buttons_context(const bContext *C,
set_pointer_type(path, result, &RNA_LightProbe);
return CTX_RESULT_OK;
}
-#ifdef WITH_NEW_CURVES_TYPE
if (CTX_data_equals(member, "curves")) {
set_pointer_type(path, result, &RNA_Curves);
return CTX_RESULT_OK;
}
-#endif
#ifdef WITH_POINT_CLOUD
if (CTX_data_equals(member, "pointcloud")) {
set_pointer_type(path, result, &RNA_PointCloud);
diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h
index 520d3a7c38d..1430cd4a8e8 100644
--- a/source/blender/editors/space_buttons/buttons_intern.h
+++ b/source/blender/editors/space_buttons/buttons_intern.h
@@ -26,7 +26,7 @@ struct SpaceProperties_Runtime {
/** For filtering properties displayed in the space. */
char search_string[UI_MAX_NAME_STR];
/**
- * Bitfield (in the same order as the tabs) for whether each tab has properties
+ * Bit-field (in the same order as the tabs) for whether each tab has properties
* that match the search filter. Only valid when #search_string is set.
*/
BLI_bitmap *tab_search_results;
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index 10fb008049d..a9ce9a3d723 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -337,6 +337,12 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
RNA_string_set(op->ptr, path_prop, str);
MEM_freeN(str);
+ PropertyRNA *prop_check_existing = RNA_struct_find_property(op->ptr, "check_existing");
+ if (!RNA_property_is_set(op->ptr, prop_check_existing)) {
+ const bool is_output_path = (RNA_property_flag(prop) & PROP_PATH_OUTPUT) != 0;
+ RNA_property_boolean_set(op->ptr, prop_check_existing, is_output_path);
+ }
+
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c
index a5cb9170f98..d4e456272f9 100644
--- a/source/blender/editors/space_buttons/buttons_texture.c
+++ b/source/blender/editors/space_buttons/buttons_texture.c
@@ -281,7 +281,8 @@ static void buttons_texture_users_from_context(ListBase *users,
brush = BKE_paint_brush(BKE_paint_get_active_from_context(C));
linestyle = BKE_linestyle_active_from_view_layer(view_layer);
- ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ob = BKE_view_layer_active_object_get(view_layer);
}
/* fill users */
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index e60946b8f66..209d60d4f58 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -507,7 +507,7 @@ static void buttons_main_region_layout(const bContext *C, ARegion *region)
static void buttons_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -603,7 +603,7 @@ static void buttons_navigation_bar_region_draw(const bContext *C, ARegion *regio
}
ED_region_panels_layout(C, region);
- /* ED_region_panels_layout adds vertical scrollbars, we don't want them. */
+ /* #ED_region_panels_layout adds vertical scroll-bars, we don't want them. */
region->v2d.scroll &= ~V2D_SCROLL_VERTICAL;
ED_region_panels_draw(C, region);
}
@@ -645,7 +645,7 @@ static void buttons_area_redraw(ScrArea *area, short buttons)
static void buttons_area_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
SpaceProperties *sbuts = area->spacedata.first;
/* context changes */
@@ -917,7 +917,7 @@ void ED_spacetype_buttons(void)
ARegionType *art;
st->spaceid = SPACE_PROPERTIES;
- strncpy(st->name, "Buttons", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Buttons");
st->create = buttons_create;
st->free = buttons_free;
@@ -978,8 +978,7 @@ void ED_spacetype_buttons(void)
/* regions: navigation bar */
art = MEM_callocN(sizeof(ARegionType), "spacetype nav buttons region");
art->regionid = RGN_TYPE_NAV_BAR;
- art->prefsizex = AREAMINX - 3; /* XXX Works and looks best,
- * should we update AREAMINX accordingly? */
+ art->prefsizex = AREAMINX;
art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES | ED_KEYMAP_NAVBAR;
art->init = buttons_navigation_bar_region_init;
art->draw = buttons_navigation_bar_region_draw;
diff --git a/source/blender/editors/space_clip/CMakeLists.txt b/source/blender/editors/space_clip/CMakeLists.txt
index eddf1780d8b..8cb5299df6d 100644
--- a/source/blender/editors/space_clip/CMakeLists.txt
+++ b/source/blender/editors/space_clip/CMakeLists.txt
@@ -13,7 +13,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# dna_type_offsets.h
diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c
index 72df2b74b11..3a4a2faa5f7 100644
--- a/source/blender/editors/space_clip/clip_buttons.c
+++ b/source/blender/editors/space_clip/clip_buttons.c
@@ -57,7 +57,7 @@ static void metadata_panel_context_draw(const bContext *C, Panel *panel)
SpaceClip *space_clip = CTX_wm_space_clip(C);
/* NOTE: This might not be exactly the same image buffer as shown in the
* clip editor itself, since that might be coming from proxy, or being
- * postprocessed (stabilized or undistored).
+ * postprocessed (stabilized or undistorted).
* Ideally we need to query metadata from an original image or movie without
* reading actual pixels to speed up the process. */
ImBuf *ibuf = ED_space_clip_get_buffer(space_clip);
diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c
index 858fa229992..8f876f6a8a3 100644
--- a/source/blender/editors/space_clip/clip_dopesheet_draw.c
+++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c
@@ -111,7 +111,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene)
GPUVertFormat *format = immVertexFormat();
uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* don't use totrect set, as the width stays the same
* (NOTE: this is ok here, the configuration is pretty straightforward)
@@ -313,7 +313,7 @@ void clip_draw_dopesheet_channels(const bContext *C, ARegion *region)
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
MovieTrackingDopesheetChannel *channel;
for (channel = dopesheet->channels.first; channel; channel = channel->next) {
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index 78174160eb8..b9bd97260ef 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -149,7 +149,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* track */
if (act_track || act_plane_track) {
@@ -241,7 +241,7 @@ static void draw_movieclip_cache(SpaceClip *sc, ARegion *region, MovieClip *clip
ED_region_cache_draw_curfra_label(sc->user.framenr, x, 8.0f * UI_DPI_FAC);
pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* solver keyframes */
immUniformColor4ub(175, 255, 0, 255);
@@ -286,7 +286,7 @@ static void draw_movieclip_muted(ARegion *region, int width, int height, float z
int x, y;
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* find window pixel coordinates of origin */
UI_view2d_view_to_region(&region->v2d, 0.0f, 0.0f, &x, &y);
@@ -364,7 +364,7 @@ static void draw_stabilization_border(
GPU_matrix_scale_2f(zoomx, zoomy);
GPU_matrix_mul(sc->stabmat);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -521,7 +521,7 @@ static void draw_track_path(SpaceClip *sc, MovieClip *UNUSED(clip), MovieTrackin
const uint position_attribute = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw path outline. */
if (!tiny) {
@@ -738,7 +738,7 @@ static void draw_marker_areas(SpaceClip *sc,
* just always go with dashed shader. */
immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -865,7 +865,7 @@ static void draw_marker_areas(SpaceClip *sc,
BLI_assert(pos == shdr_pos);
UNUSED_VARS_NDEBUG(pos);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
}
static float get_shortest_pattern_side(MovieTrackingMarker *marker)
@@ -1210,10 +1210,10 @@ static void draw_plane_marker_image(Scene *scene,
imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
/* Use 3D image for correct display of planar tracked images. */
- immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR);
immBindTexture("image", texture);
- immUniform1f("alpha", plane_track->image_opacity);
+ immUniformColor4f(1.0f, 1.0f, 1.0f, plane_track->image_opacity);
immBegin(GPU_PRIM_TRI_FAN, 4);
@@ -1278,7 +1278,7 @@ static void draw_plane_marker_ex(SpaceClip *sc,
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -1358,7 +1358,7 @@ static void draw_plane_marker_ex(SpaceClip *sc,
/* Draw sliders. */
if (is_selected_track) {
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
if (draw_outline) {
immUniformThemeColor(TH_MARKER_OUTLINE);
@@ -1525,7 +1525,7 @@ static void draw_tracking_tracks(SpaceClip *sc,
uint position = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* markers outline and non-selected areas */
track = tracksbase->first;
@@ -1720,7 +1720,7 @@ static void draw_distortion(SpaceClip *sc,
uint position = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* grid */
if (sc->flag & SC_SHOW_GRID) {
diff --git a/source/blender/editors/space_clip/clip_graph_draw.c b/source/blender/editors/space_clip/clip_graph_draw.c
index 7236e7bcee0..5e3171d03c9 100644
--- a/source/blender/editors/space_clip/clip_graph_draw.c
+++ b/source/blender/editors/space_clip/clip_graph_draw.c
@@ -259,7 +259,7 @@ void clip_draw_graph(SpaceClip *sc, ARegion *region, Scene *scene)
if (clip) {
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_point_size(3.0f);
diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c
index 221b87a8b5a..04b87cb4c83 100644
--- a/source/blender/editors/space_clip/clip_utils.c
+++ b/source/blender/editors/space_clip/clip_utils.c
@@ -614,7 +614,7 @@ void clip_draw_sfra_efra(View2D *v2d, Scene *scene)
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(0.0f, 0.0f, 0.0f, 0.4f);
immRectf(pos, v2d->cur.xmin, v2d->cur.ymin, (float)scene->r.sfra, v2d->cur.ymax);
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index ce6409a7784..ab952470757 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -314,7 +314,7 @@ static SpaceLink *clip_duplicate(SpaceLink *sl)
static void clip_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
const Scene *scene = params->scene;
/* context changes */
@@ -811,8 +811,8 @@ static void clip_main_region_draw(const bContext *C, ARegion *region)
int width, height;
bool show_cursor = false;
- /* if tracking is in progress, we should synchronize framenr from clipuser
- * so latest tracked frame would be shown */
+ /* If tracking is in progress, we should synchronize the frame from the clip-user
+ * (#MovieClipUser.framenr) so latest tracked frame would be shown. */
if (clip && clip->tracking_context) {
BKE_autotrack_context_sync_user(clip->tracking_context, &sc->user);
}
@@ -919,7 +919,7 @@ static void clip_main_region_draw(const bContext *C, ARegion *region)
static void clip_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -1118,7 +1118,7 @@ static void clip_header_region_draw(const bContext *C, ARegion *region)
static void clip_header_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -1160,7 +1160,7 @@ static void clip_tools_region_draw(const bContext *C, ARegion *region)
static void clip_props_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -1212,7 +1212,7 @@ static void clip_properties_region_draw(const bContext *C, ARegion *region)
static void clip_properties_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -1251,7 +1251,7 @@ void ED_spacetype_clip(void)
ARegionType *art;
st->spaceid = SPACE_CLIP;
- strncpy(st->name, "Clip", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Clip");
st->create = clip_create;
st->free = clip_free;
diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c
index cba4157d044..0f5207b39c2 100644
--- a/source/blender/editors/space_clip/tracking_ops.c
+++ b/source/blender/editors/space_clip/tracking_ops.c
@@ -1535,6 +1535,7 @@ void CLIP_OT_average_tracks(wmOperatorType *ot)
PropertyRNA *prop;
prop = RNA_def_boolean(ot->srna, "keep_original", 1, "Keep Original", "Keep original tracks");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MOVIECLIP);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
diff --git a/source/blender/editors/space_clip/tracking_ops_orient.c b/source/blender/editors/space_clip/tracking_ops_orient.c
index d3b818fba43..c6d4a6ad104 100644
--- a/source/blender/editors/space_clip/tracking_ops_orient.c
+++ b/source/blender/editors/space_clip/tracking_ops_orient.c
@@ -72,7 +72,8 @@ static Object *get_orientation_object(bContext *C)
object = get_camera_with_movieclip(scene, clip);
}
else {
- object = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ object = BKE_view_layer_active_object_get(view_layer);
}
if (object != NULL && object->parent != NULL) {
@@ -86,6 +87,7 @@ static bool set_orientation_poll(bContext *C)
{
SpaceClip *sc = CTX_wm_space_clip(C);
if (sc != NULL) {
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
if (clip != NULL) {
@@ -94,7 +96,8 @@ static bool set_orientation_poll(bContext *C)
if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
return true;
}
- return OBACT(view_layer) != NULL;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ return BKE_view_layer_active_object_get(view_layer) != NULL;
}
}
return false;
diff --git a/source/blender/editors/space_console/CMakeLists.txt b/source/blender/editors/space_console/CMakeLists.txt
index 841c21f12e7..345ab8b0970 100644
--- a/source/blender/editors/space_console/CMakeLists.txt
+++ b/source/blender/editors/space_console/CMakeLists.txt
@@ -9,7 +9,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
)
diff --git a/source/blender/editors/space_console/console_draw.c b/source/blender/editors/space_console/console_draw.c
index 140dc4c1d40..6710d4ce0e7 100644
--- a/source/blender/editors/space_console/console_draw.c
+++ b/source/blender/editors/space_console/console_draw.c
@@ -150,7 +150,7 @@ static void console_textview_draw_cursor(TextViewContext *tvc, int cwidth, int c
/* cursor */
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_CONSOLE_CURSOR);
immRectf(pos, pen[0] - U.pixelsize, pen[1], pen[0] + U.pixelsize, pen[1] + tvc->lheight);
diff --git a/source/blender/editors/space_console/console_intern.h b/source/blender/editors/space_console/console_intern.h
index deaaff05a33..8e2ebc9b8e9 100644
--- a/source/blender/editors/space_console/console_intern.h
+++ b/source/blender/editors/space_console/console_intern.h
@@ -15,7 +15,7 @@ struct wmOperatorType;
/* console_draw.c */
void console_textview_main(struct SpaceConsole *sc, const struct ARegion *region);
-/* needed to calculate the scrollbar */
+/* Needed to calculate the scroll-bar. */
int console_textview_height(struct SpaceConsole *sc, const struct ARegion *region);
int console_char_pick(struct SpaceConsole *sc, const struct ARegion *region, const int mval[2]);
diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c
index 17fbef23eac..11234d446da 100644
--- a/source/blender/editors/space_console/console_ops.c
+++ b/source/blender/editors/space_console/console_ops.c
@@ -280,7 +280,7 @@ static bool console_line_column_from_index(
return false;
}
-/* static funcs for text editing */
+/* Static functions for text editing. */
/* similar to the text editor, with some not used. keep compatible */
static const EnumPropertyItem console_move_type_items[] = {
@@ -413,16 +413,8 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve
}
char str[BLI_UTF8_MAX + 1];
- size_t len;
-
- if (event->utf8_buf[0]) {
- len = BLI_str_utf8_size_safe(event->utf8_buf);
- memcpy(str, event->utf8_buf, len);
- }
- else {
- /* in theory, ghost can set value to extended ascii here */
- len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1);
- }
+ const size_t len = BLI_str_utf8_size_safe(event->utf8_buf);
+ memcpy(str, event->utf8_buf, len);
str[len] = '\0';
RNA_string_set(op->ptr, "text", str);
}
diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c
index c69b73e377d..8838b5d341f 100644
--- a/source/blender/editors/space_console/space_console.c
+++ b/source/blender/editors/space_console/space_console.c
@@ -20,6 +20,7 @@
#include "ED_space_api.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -124,6 +125,10 @@ static void console_main_region_init(wmWindowManager *wm, ARegion *region)
keymap = WM_keymap_ensure(wm->defaultconf, "Console", SPACE_CONSOLE, 0);
WM_event_add_keymap_handler_v2d_mask(&region->handlers, keymap);
+ /* Include after "Console" so cursor motion keys such as "Home" isn't overridden. */
+ keymap = WM_keymap_ensure(wm->defaultconf, "View2D Buttons List", 0, 0);
+ WM_event_add_keymap_handler(&region->handlers, keymap);
+
/* add drop boxes */
lb = WM_dropboxmap_find("Console", SPACE_CONSOLE, RGN_TYPE_WINDOW);
@@ -154,7 +159,7 @@ static void id_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
ID *id = WM_drag_get_local_ID(drag, 0);
/* copy drag path to properties */
- char *text = RNA_path_full_ID_py(G_MAIN, id);
+ char *text = RNA_path_full_ID_py(id);
RNA_string_set(drop->ptr, "text", text);
MEM_freeN(text);
}
@@ -256,7 +261,7 @@ static void console_main_region_listener(const wmRegionListenerParams *params)
{
ScrArea *area = params->area;
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -285,7 +290,7 @@ void ED_spacetype_console(void)
ARegionType *art;
st->spaceid = SPACE_CONSOLE;
- strncpy(st->name, "Console", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Console");
st->create = console_create;
st->free = console_free;
diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt
index b8c28e354da..fb9d5ab9b13 100644
--- a/source/blender/editors/space_file/CMakeLists.txt
+++ b/source/blender/editors/space_file/CMakeLists.txt
@@ -15,7 +15,6 @@ set(INC
../../render
../../windowmanager
../../../../intern/atomic
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@@ -28,8 +27,9 @@ set(SRC
file_ops.c
file_panels.c
file_utils.c
- filelist.c
+ filelist.cc
filesel.c
+ folder_history.cc
fsmenu.c
space_file.c
diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc
index 87595ecdb88..1829f19bfd6 100644
--- a/source/blender/editors/space_file/asset_catalog_tree_view.cc
+++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc
@@ -86,12 +86,12 @@ class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem {
bool rename(StringRefNull new_name) override;
/** Add drag support for catalog items. */
- std::unique_ptr<ui::AbstractTreeViewItemDragController> create_drag_controller() const override;
+ std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override;
/** Add dropping support for catalog items. */
- std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+ std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override;
};
-class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController {
+class AssetCatalogDragController : public ui::AbstractViewItemDragController {
AssetCatalogTreeItem &catalog_item_;
public:
@@ -103,7 +103,7 @@ class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController
void on_drag_start() override;
};
-class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController {
+class AssetCatalogDropController : public ui::AbstractViewItemDropController {
AssetCatalogTreeItem &catalog_item_;
public:
@@ -142,7 +142,7 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
void build_row(uiLayout &row) override;
- struct DropController : public ui::AbstractTreeViewItemDropController {
+ struct DropController : public ui::AbstractViewItemDropController {
DropController(AssetCatalogTreeView &tree_view);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
@@ -150,13 +150,13 @@ class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem {
bool on_drop(struct bContext *C, const wmDrag &drag) override;
};
- std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+ std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override;
};
class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
using BasicTreeViewItem::BasicTreeViewItem;
- struct DropController : public ui::AbstractTreeViewItemDropController {
+ struct DropController : public ui::AbstractViewItemDropController {
DropController(AssetCatalogTreeView &tree_view);
bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
@@ -164,7 +164,7 @@ class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem {
bool on_drop(struct bContext *C, const wmDrag &drag) override;
};
- std::unique_ptr<ui::AbstractTreeViewItemDropController> create_drop_controller() const override;
+ std::unique_ptr<ui::AbstractViewItemDropController> create_drop_controller() const override;
};
/* ---------------------------------------------------------------------- */
@@ -272,11 +272,11 @@ void AssetCatalogTreeViewItem::build_row(uiLayout &row)
return;
}
- uiButTreeRow *tree_row_but = tree_row_button();
+ uiButViewItem *view_item_but = view_item_button();
PointerRNA *props;
props = UI_but_extra_operator_icon_add(
- (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
+ (uiBut *)view_item_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str());
}
@@ -305,7 +305,7 @@ void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column)
0,
&props);
RNA_string_set(&props, "catalog_id", catalog_id_str_buffer);
- uiItemO(&column, "Rename", ICON_NONE, "UI_OT_tree_view_item_rename");
+ uiItemO(&column, "Rename", ICON_NONE, "UI_OT_view_item_rename");
/* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done
* in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on
@@ -333,14 +333,14 @@ bool AssetCatalogTreeViewItem::rename(StringRefNull new_name)
return true;
}
-std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewItem::
+std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewItem::
create_drop_controller() const
{
return std::make_unique<AssetCatalogDropController>(
static_cast<AssetCatalogTreeView &>(get_tree_view()), catalog_item_);
}
-std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem::
+std::unique_ptr<ui::AbstractViewItemDragController> AssetCatalogTreeViewItem::
create_drag_controller() const
{
return std::make_unique<AssetCatalogDragController>(
@@ -351,7 +351,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDragController> AssetCatalogTreeViewItem
AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view,
AssetCatalogTreeItem &catalog_item)
- : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item)
+ : ui::AbstractViewItemDropController(tree_view), catalog_item_(catalog_item)
{
}
@@ -422,10 +422,10 @@ bool AssetCatalogDropController::on_drop(struct bContext *C, const wmDrag &drag)
{
if (drag.type == WM_DRAG_ASSET_CATALOG) {
return drop_asset_catalog_into_catalog(
- drag, tree_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id());
+ drag, get_view<AssetCatalogTreeView>(), catalog_item_.get_catalog_id());
}
return drop_assets_into_catalog(C,
- tree_view<AssetCatalogTreeView>(),
+ get_view<AssetCatalogTreeView>(),
drag,
catalog_item_.get_catalog_id(),
catalog_item_.get_simple_name());
@@ -512,14 +512,14 @@ bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag,
::AssetLibrary &AssetCatalogDropController::get_asset_library() const
{
- return *tree_view<AssetCatalogTreeView>().asset_library_;
+ return *get_view<AssetCatalogTreeView>().asset_library_;
}
/* ---------------------------------------------------------------------- */
AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeView &tree_view,
AssetCatalogTreeItem &catalog_item)
- : ui::AbstractTreeViewItemDragController(tree_view), catalog_item_(catalog_item)
+ : ui::AbstractViewItemDragController(tree_view), catalog_item_(catalog_item)
{
}
@@ -538,7 +538,7 @@ void *AssetCatalogDragController::create_drag_data() const
void AssetCatalogDragController::on_drag_start()
{
- AssetCatalogTreeView &tree_view_ = tree_view<AssetCatalogTreeView>();
+ AssetCatalogTreeView &tree_view_ = get_view<AssetCatalogTreeView>();
tree_view_.activate_catalog_by_id(catalog_item_.get_catalog_id());
}
@@ -551,15 +551,15 @@ void AssetCatalogTreeViewAllItem::build_row(uiLayout &row)
PointerRNA *props;
UI_but_extra_operator_icon_add(
- (uiBut *)tree_row_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK);
+ (uiBut *)view_item_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK);
props = UI_but_extra_operator_icon_add(
- (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
+ (uiBut *)view_item_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD);
/* No parent path to use the root level. */
RNA_string_set(props, "parent_path", nullptr);
}
-std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewAllItem::
+std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewAllItem::
create_drop_controller() const
{
return std::make_unique<AssetCatalogTreeViewAllItem::DropController>(
@@ -567,7 +567,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewAllI
}
AssetCatalogTreeViewAllItem::DropController::DropController(AssetCatalogTreeView &tree_view)
- : ui::AbstractTreeViewItemDropController(tree_view)
+ : ui::AbstractViewItemDropController(tree_view)
{
}
@@ -579,7 +579,7 @@ bool AssetCatalogTreeViewAllItem::DropController::can_drop(const wmDrag &drag,
}
const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog(
- drag, *tree_view<AssetCatalogTreeView>().asset_library_);
+ drag, *get_view<AssetCatalogTreeView>().asset_library_);
if (drag_catalog->path.parent() == "") {
*r_disabled_hint = "Catalog is already placed at the highest level";
return false;
@@ -592,7 +592,7 @@ std::string AssetCatalogTreeViewAllItem::DropController::drop_tooltip(const wmDr
{
BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
const AssetCatalog *drag_catalog = AssetCatalogDropController::get_drag_catalog(
- drag, *tree_view<AssetCatalogTreeView>().asset_library_);
+ drag, *get_view<AssetCatalogTreeView>().asset_library_);
return std::string(TIP_("Move Catalog")) + " '" + drag_catalog->path.name() + "' " +
TIP_("to the top level of the tree");
@@ -604,14 +604,14 @@ bool AssetCatalogTreeViewAllItem::DropController::on_drop(struct bContext *UNUSE
BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG);
return AssetCatalogDropController::drop_asset_catalog_into_catalog(
drag,
- tree_view<AssetCatalogTreeView>(),
+ get_view<AssetCatalogTreeView>(),
/* No value to drop into the root level. */
std::nullopt);
}
/* ---------------------------------------------------------------------- */
-std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnassignedItem::
+std::unique_ptr<ui::AbstractViewItemDropController> AssetCatalogTreeViewUnassignedItem::
create_drop_controller() const
{
return std::make_unique<AssetCatalogTreeViewUnassignedItem::DropController>(
@@ -619,7 +619,7 @@ std::unique_ptr<ui::AbstractTreeViewItemDropController> AssetCatalogTreeViewUnas
}
AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view)
- : ui::AbstractTreeViewItemDropController(tree_view)
+ : ui::AbstractViewItemDropController(tree_view)
{
}
@@ -647,7 +647,7 @@ bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(struct bContext
{
/* Assign to nil catalog ID. */
return AssetCatalogDropController::drop_assets_into_catalog(
- C, tree_view<AssetCatalogTreeView>(), drag, CatalogID{});
+ C, get_view<AssetCatalogTreeView>(), drag, CatalogID{});
}
} // namespace blender::ed::asset_browser
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index f3359336b14..93eb5938301 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -378,7 +378,7 @@ static void file_draw_preview(const SpaceFile *sfile,
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
}
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_3D_IMAGE_COLOR);
immDrawPixelsTexTiled_scaling(&state,
(float)xco,
(float)yco,
@@ -463,7 +463,7 @@ static void file_draw_preview(const SpaceFile *sfile,
if (show_outline) {
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
float border_color[4] = {1.0f, 1.0f, 1.0f, 0.4f};
float bgcolor[4];
UI_GetThemeColor4fv(TH_BACK, bgcolor);
@@ -581,7 +581,7 @@ static void draw_background(FileLayout *layout, View2D *v2d)
int sy;
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
float col_alternating[4];
UI_GetThemeColor4fv(TH_ROW_ALTERNATE, col_alternating);
immUniformThemeColorBlend(TH_BACK, TH_ROW_ALTERNATE, col_alternating[3]);
@@ -631,7 +631,7 @@ static void draw_dividers(FileLayout *layout, View2D *v2d)
uint color = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
immBegin(GPU_PRIM_LINES, vertex_len);
sx = (int)v2d->tot.xmin;
@@ -660,7 +660,7 @@ static void draw_columnheader_background(const FileLayout *layout, const View2D
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShade(TH_BACK, 11);
immRectf(pos,
@@ -712,7 +712,7 @@ static void draw_columnheader_columns(const FileSelectParams *params,
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShade(TH_BACK, -10);
immBegin(GPU_PRIM_LINES, 2);
immVertex2f(pos, sx - 1, sy - divider_pad);
@@ -727,7 +727,7 @@ static void draw_columnheader_columns(const FileSelectParams *params,
/* Vertical separator lines line */
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShade(TH_BACK, -10);
immBegin(GPU_PRIM_LINES, 4);
immVertex2f(pos, v2d->cur.xmin, sy);
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index 655a7983e2b..676fdff268e 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -95,11 +95,15 @@ int file_highlight_set(struct SpaceFile *sfile, struct ARegion *region, int mx,
* Use to set the file selector path from some arbitrary source.
*/
void file_sfile_filepath_set(struct SpaceFile *sfile, const char *filepath);
-void file_sfile_to_operator_ex(struct Main *bmain,
+void file_sfile_to_operator_ex(struct bContext *C,
+ struct Main *bmain,
struct wmOperator *op,
struct SpaceFile *sfile,
char *filepath);
-void file_sfile_to_operator(struct Main *bmain, struct wmOperator *op, struct SpaceFile *sfile);
+void file_sfile_to_operator(struct bContext *C,
+ struct Main *bmain,
+ struct wmOperator *op,
+ struct SpaceFile *sfile);
void file_operator_to_sfile(struct Main *bmain, struct SpaceFile *sfile, struct wmOperator *op);
@@ -113,7 +117,7 @@ void fileselect_refresh_params(struct SpaceFile *sfile);
/**
* Sets #FileSelectParams.file (name of selected file)
*/
-void fileselect_file_set(SpaceFile *sfile, int index);
+void fileselect_file_set(struct bContext *C, SpaceFile *sfile, int index);
bool file_attribute_column_type_enabled(const FileSelectParams *params,
FileAttributeColumnType column);
/**
@@ -181,6 +185,19 @@ void file_on_reload_callback_register(struct SpaceFile *sfile,
onReloadFn callback,
onReloadFnData custom_data);
+/* folder_history.cc */
+
+/* not listbase itself */
+void folderlist_free(struct ListBase *folderlist);
+void folderlist_popdir(struct ListBase *folderlist, char *dir);
+void folderlist_pushdir(struct ListBase *folderlist, const char *dir);
+const char *folderlist_peeklastdir(struct ListBase *folderlist);
+int folderlist_clear_next(struct SpaceFile *sfile);
+
+void folder_history_list_ensure_for_active_browse_mode(struct SpaceFile *sfile);
+void folder_history_list_free(struct SpaceFile *sfile);
+struct ListBase folder_history_list_duplicate(struct ListBase *listbase);
+
/* file_panels.c */
void file_tool_props_region_panels_register(struct ARegionType *art);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index 62bdd583bc1..3f671fd44a1 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -213,7 +213,7 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen)
else {
retval = FILE_SELECT_FILE;
}
- fileselect_file_set(sfile, selected_idx);
+ fileselect_file_set(C, sfile, selected_idx);
}
return retval;
}
@@ -367,6 +367,39 @@ static FileSelect file_select(
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Bookmark Utilities
+ * \{ */
+
+/**
+ * Local utility to write #BLENDER_BOOKMARK_FILE, reporting an error on failure.
+ */
+static bool fsmenu_write_file_and_refresh_or_report_error(struct FSMenu *fsmenu,
+ ScrArea *area,
+ ReportList *reports)
+{
+ /* NOTE: use warning instead of error here, because the bookmark operation may be part of
+ * other actions which should not cause the operator to fail entirely. */
+ const char *cfgdir = BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL);
+ if (UNLIKELY(!cfgdir)) {
+ BKE_report(reports, RPT_ERROR, "Unable to create configuration directory to write bookmarks");
+ return false;
+ }
+
+ char filepath[FILE_MAX];
+ BLI_join_dirfile(filepath, sizeof(filepath), cfgdir, BLENDER_BOOKMARK_FILE);
+ if (UNLIKELY(!fsmenu_write_file(fsmenu, filepath))) {
+ BKE_reportf(reports, RPT_ERROR, "Unable to open or write bookmark file \"%s\"", filepath);
+ return false;
+ }
+
+ ED_area_tag_refresh(area);
+ ED_area_tag_redraw(area);
+ return true;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Box Select Operator
* \{ */
@@ -451,7 +484,7 @@ static int file_box_select_modal(bContext *C, wmOperator *op, const wmEvent *eve
else {
params->highlight_file = -1;
params->sel_first = params->sel_last = -1;
- fileselect_file_set(sfile, params->active_file);
+ fileselect_file_set(C, sfile, params->active_file);
file_select_deselect_all(sfile, FILE_SEL_HIGHLIGHTED);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
}
@@ -669,7 +702,8 @@ void FILE_OT_select(wmOperatorType *ot)
/**
* \returns true if selection has changed
*/
-static bool file_walk_select_selection_set(wmWindow *win,
+static bool file_walk_select_selection_set(struct bContext *C,
+ wmWindow *win,
ARegion *region,
SpaceFile *sfile,
const int direction,
@@ -775,7 +809,7 @@ static bool file_walk_select_selection_set(wmWindow *win,
}
BLI_assert(IN_RANGE(active, -1, numfiles));
- fileselect_file_set(sfile, params->active_file);
+ fileselect_file_set(C, sfile, params->active_file);
/* ensure newly selected file is inside viewbounds */
file_ensure_inside_viewbounds(region, sfile, params->active_file);
@@ -856,7 +890,8 @@ static bool file_walk_select_do(bContext *C,
}
}
- return file_walk_select_selection_set(win,
+ return file_walk_select_selection_set(C,
+ win,
region,
sfile,
direction,
@@ -1053,19 +1088,17 @@ static int bookmark_select_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceFile *sfile = CTX_wm_space_file(C);
- PropertyRNA *prop;
- if ((prop = RNA_struct_find_property(op->ptr, "dir"))) {
- FileSelectParams *params = ED_fileselect_get_active_params(sfile);
- char entry[256];
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "dir");
+ FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ char entry[256];
- RNA_property_string_get(op->ptr, prop, entry);
- BLI_strncpy(params->dir, entry, sizeof(params->dir));
- BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir);
- ED_file_change_dir(C);
+ RNA_property_string_get(op->ptr, prop, entry);
+ BLI_strncpy(params->dir, entry, sizeof(params->dir));
+ BLI_path_normalize_dir(BKE_main_blendfile_path(bmain), params->dir);
+ ED_file_change_dir(C);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
- }
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
return OPERATOR_FINISHED;
}
@@ -1095,7 +1128,7 @@ void FILE_OT_select_bookmark(wmOperatorType *ot)
/** \name Add Bookmark Operator
* \{ */
-static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op))
+static int bookmark_add_exec(bContext *C, wmOperator *op)
{
ScrArea *area = CTX_wm_area(C);
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -1103,19 +1136,11 @@ static int bookmark_add_exec(bContext *C, wmOperator *UNUSED(op))
struct FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params->dir[0] != '\0') {
- char name[FILE_MAX];
fsmenu_insert_entry(
fsmenu, FS_CATEGORY_BOOKMARKS, params->dir, NULL, ICON_FILE_FOLDER, FS_INSERT_SAVE);
- BLI_join_dirfile(name,
- sizeof(name),
- BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
- BLENDER_BOOKMARK_FILE);
- fsmenu_write_file(fsmenu, name);
+ fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports);
}
-
- ED_area_tag_refresh(area);
- ED_area_tag_redraw(area);
return OPERATOR_FINISHED;
}
@@ -1146,27 +1171,11 @@ static int bookmark_delete_exec(bContext *C, wmOperator *op)
int nentries = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "index");
-
- if (prop) {
- int index;
- if (RNA_property_is_set(op->ptr, prop)) {
- index = RNA_property_int_get(op->ptr, prop);
- }
- else { /* if index unset, use active bookmark... */
- index = sfile->bookmarknr;
- }
- if ((index > -1) && (index < nentries)) {
- char name[FILE_MAX];
-
- fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index);
- BLI_join_dirfile(name,
- sizeof(name),
- BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
- BLENDER_BOOKMARK_FILE);
- fsmenu_write_file(fsmenu, name);
- ED_area_tag_refresh(area);
- ED_area_tag_redraw(area);
- }
+ const int index = RNA_property_is_set(op->ptr, prop) ? RNA_property_int_get(op->ptr, prop) :
+ sfile->bookmarknr;
+ if ((index > -1) && (index < nentries)) {
+ fsmenu_remove_entry(fsmenu, FS_CATEGORY_BOOKMARKS, index);
+ fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports);
}
return OPERATOR_FINISHED;
@@ -1197,7 +1206,7 @@ void FILE_OT_bookmark_delete(wmOperatorType *ot)
/** \name Cleanup Bookmark Operator
* \{ */
-static int bookmark_cleanup_exec(bContext *C, wmOperator *UNUSED(op))
+static int bookmark_cleanup_exec(bContext *C, wmOperator *op)
{
ScrArea *area = CTX_wm_area(C);
struct FSMenu *fsmenu = ED_fsmenu_get();
@@ -1218,16 +1227,8 @@ static int bookmark_cleanup_exec(bContext *C, wmOperator *UNUSED(op))
}
if (changed) {
- char name[FILE_MAX];
-
- BLI_join_dirfile(name,
- sizeof(name),
- BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
- BLENDER_BOOKMARK_FILE);
- fsmenu_write_file(fsmenu, name);
+ fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports);
fsmenu_refresh_bookmarks_status(CTX_wm_manager(C), fsmenu);
- ED_area_tag_refresh(area);
- ED_area_tag_redraw(area);
}
return OPERATOR_FINISHED;
@@ -1269,8 +1270,6 @@ static int bookmark_move_exec(bContext *C, wmOperator *op)
struct FSMenuEntry *fsmentry = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS);
const struct FSMenuEntry *fsmentry_org = fsmentry;
- char fname[FILE_MAX];
-
const int direction = RNA_enum_get(op->ptr, "direction");
const int totitems = ED_fsmenu_get_nentries(fsmenu, FS_CATEGORY_BOOKMARKS);
const int act_index = sfile->bookmarknr;
@@ -1306,13 +1305,8 @@ static int bookmark_move_exec(bContext *C, wmOperator *op)
/* Need to update active bookmark number. */
sfile->bookmarknr = new_index;
- BLI_join_dirfile(fname,
- sizeof(fname),
- BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
- BLENDER_BOOKMARK_FILE);
- fsmenu_write_file(fsmenu, fname);
+ fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports);
- ED_area_tag_redraw(area);
return OPERATOR_FINISHED;
}
@@ -1352,21 +1346,16 @@ void FILE_OT_bookmark_move(wmOperatorType *ot)
/** \name Reset Recent Blend Files Operator
* \{ */
-static int reset_recent_exec(bContext *C, wmOperator *UNUSED(op))
+static int reset_recent_exec(bContext *C, wmOperator *op)
{
ScrArea *area = CTX_wm_area(C);
- char name[FILE_MAX];
struct FSMenu *fsmenu = ED_fsmenu_get();
while (ED_fsmenu_get_entry(fsmenu, FS_CATEGORY_RECENT, 0) != NULL) {
fsmenu_remove_entry(fsmenu, FS_CATEGORY_RECENT, 0);
}
- BLI_join_dirfile(name,
- sizeof(name),
- BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
- BLENDER_BOOKMARK_FILE);
- fsmenu_write_file(fsmenu, name);
- ED_area_tag_redraw(area);
+
+ fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports);
return OPERATOR_FINISHED;
}
@@ -1568,7 +1557,8 @@ void FILE_OT_cancel(struct wmOperatorType *ot)
/** \name Operator Utilities
* \{ */
-void file_sfile_to_operator_ex(Main *bmain, wmOperator *op, SpaceFile *sfile, char *filepath)
+void file_sfile_to_operator_ex(
+ bContext *C, Main *bmain, wmOperator *op, SpaceFile *sfile, char *filepath)
{
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
PropertyRNA *prop;
@@ -1582,14 +1572,27 @@ void file_sfile_to_operator_ex(Main *bmain, wmOperator *op, SpaceFile *sfile, ch
}
}
+ char value[FILE_MAX];
if ((prop = RNA_struct_find_property(op->ptr, "filename"))) {
+ RNA_property_string_get(op->ptr, prop, value);
RNA_property_string_set(op->ptr, prop, params->file);
+ if (RNA_property_update_check(prop) && !STREQ(params->file, value)) {
+ RNA_property_update(C, op->ptr, prop);
+ }
}
if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
+ RNA_property_string_get(op->ptr, prop, value);
RNA_property_string_set(op->ptr, prop, params->dir);
+ if (RNA_property_update_check(prop) && !STREQ(params->dir, value)) {
+ RNA_property_update(C, op->ptr, prop);
+ }
}
if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
+ RNA_property_string_get(op->ptr, prop, value);
RNA_property_string_set(op->ptr, prop, filepath);
+ if (RNA_property_update_check(prop) && !STREQ(filepath, value)) {
+ RNA_property_update(C, op->ptr, prop);
+ }
}
/* some ops have multiple files to select */
@@ -1643,11 +1646,11 @@ void file_sfile_to_operator_ex(Main *bmain, wmOperator *op, SpaceFile *sfile, ch
}
}
}
-void file_sfile_to_operator(Main *bmain, wmOperator *op, SpaceFile *sfile)
+void file_sfile_to_operator(bContext *C, Main *bmain, wmOperator *op, SpaceFile *sfile)
{
char filepath_dummy[FILE_MAX];
- file_sfile_to_operator_ex(bmain, op, sfile, filepath_dummy);
+ file_sfile_to_operator_ex(C, bmain, op, sfile, filepath_dummy);
}
void file_operator_to_sfile(Main *bmain, SpaceFile *sfile, wmOperator *op)
@@ -1708,7 +1711,7 @@ void file_draw_check_ex(bContext *C, ScrArea *area)
if (op) { /* fail on reload */
if (op->type->check) {
Main *bmain = CTX_data_main(C);
- file_sfile_to_operator(bmain, op, sfile);
+ file_sfile_to_operator(C, bmain, op, sfile);
/* redraw */
if (op->type->check(C, op)) {
@@ -1793,17 +1796,19 @@ static bool file_execute(bContext *C, SpaceFile *sfile)
}
ED_file_change_dir(C);
}
- /* opening file - sends events now, so things get handled on windowqueue level */
+ /* Opening file, sends events now, so things get handled on window-queue level. */
else if (sfile->op) {
+ ScrArea *area = CTX_wm_area(C);
+ struct FSMenu *fsmenu = ED_fsmenu_get();
wmOperator *op = sfile->op;
char filepath[FILE_MAX];
sfile->op = NULL;
- file_sfile_to_operator_ex(bmain, op, sfile, filepath);
+ file_sfile_to_operator_ex(C, bmain, op, sfile, filepath);
if (BLI_exists(params->dir)) {
- fsmenu_insert_entry(ED_fsmenu_get(),
+ fsmenu_insert_entry(fsmenu,
FS_CATEGORY_RECENT,
params->dir,
NULL,
@@ -1811,11 +1816,8 @@ static bool file_execute(bContext *C, SpaceFile *sfile)
FS_INSERT_SAVE | FS_INSERT_FIRST);
}
- BLI_join_dirfile(filepath,
- sizeof(filepath),
- BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL),
- BLENDER_BOOKMARK_FILE);
- fsmenu_write_file(ED_fsmenu_get(), filepath);
+ fsmenu_write_file_and_refresh_or_report_error(fsmenu, area, op->reports);
+
WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC);
}
@@ -2268,7 +2270,7 @@ static int filepath_drop_exec(bContext *C, wmOperator *op)
file_sfile_filepath_set(sfile, filepath);
if (sfile->op) {
- file_sfile_to_operator(bmain, sfile->op, sfile);
+ file_sfile_to_operator(C, bmain, sfile->op, sfile);
file_draw_check(C);
}
@@ -2327,7 +2329,6 @@ static int file_directory_new_exec(bContext *C, wmOperator *op)
char name[FILE_MAXFILE];
char path[FILE_MAX];
bool generate_name = true;
- PropertyRNA *prop;
wmWindowManager *wm = CTX_wm_manager(C);
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -2341,7 +2342,8 @@ static int file_directory_new_exec(bContext *C, wmOperator *op)
path[0] = '\0';
- if ((prop = RNA_struct_find_property(op->ptr, "directory"))) {
+ {
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "directory");
RNA_property_string_get(op->ptr, prop, path);
if (path[0] != '\0') {
generate_name = false;
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.cc
index 24e1faaf4c9..2b9b23620ee 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.cc
@@ -7,11 +7,11 @@
/* global includes */
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
#include <sys/stat.h>
-#include <time.h>
#ifndef WIN32
# include <unistd.h>
@@ -79,183 +79,14 @@
#define FILEDIR_NBR_ENTRIES_UNSET -1
-/* ----------------- FOLDERLIST (previous/next) -------------- */
-
-typedef struct FolderList {
- struct FolderList *next, *prev;
- char *foldername;
-} FolderList;
-
-void folderlist_popdir(struct ListBase *folderlist, char *dir)
-{
- const char *prev_dir;
- struct FolderList *folder;
- folder = folderlist->last;
-
- if (folder) {
- /* remove the current directory */
- MEM_freeN(folder->foldername);
- BLI_freelinkN(folderlist, folder);
-
- folder = folderlist->last;
- if (folder) {
- prev_dir = folder->foldername;
- BLI_strncpy(dir, prev_dir, FILE_MAXDIR);
- }
- }
- /* delete the folder next or use setdir directly before PREVIOUS OP */
-}
-
-void folderlist_pushdir(ListBase *folderlist, const char *dir)
-{
- if (!dir[0]) {
- return;
- }
-
- struct FolderList *folder, *previous_folder;
- previous_folder = folderlist->last;
-
- /* check if already exists */
- if (previous_folder && previous_folder->foldername) {
- if (BLI_path_cmp(previous_folder->foldername, dir) == 0) {
- return;
- }
- }
-
- /* create next folder element */
- folder = MEM_mallocN(sizeof(*folder), __func__);
- folder->foldername = BLI_strdup(dir);
-
- /* add it to the end of the list */
- BLI_addtail(folderlist, folder);
-}
-
-const char *folderlist_peeklastdir(ListBase *folderlist)
-{
- struct FolderList *folder;
-
- if (!folderlist->last) {
- return NULL;
- }
-
- folder = folderlist->last;
- return folder->foldername;
-}
-
-int folderlist_clear_next(struct SpaceFile *sfile)
-{
- const FileSelectParams *params = ED_fileselect_get_active_params(sfile);
- struct FolderList *folder;
-
- /* if there is no folder_next there is nothing we can clear */
- if (BLI_listbase_is_empty(sfile->folders_next)) {
- return 0;
- }
-
- /* if previous_folder, next_folder or refresh_folder operators are executed
- * it doesn't clear folder_next */
- folder = sfile->folders_prev->last;
- if ((!folder) || (BLI_path_cmp(folder->foldername, params->dir) == 0)) {
- return 0;
- }
-
- /* eventually clear flist->folders_next */
- return 1;
-}
-
-void folderlist_free(ListBase *folderlist)
-{
- if (folderlist) {
- FolderList *folder;
- for (folder = folderlist->first; folder; folder = folder->next) {
- MEM_freeN(folder->foldername);
- }
- BLI_freelistN(folderlist);
- }
-}
-
-static ListBase folderlist_duplicate(ListBase *folderlist)
-{
- ListBase folderlistn = {NULL};
-
- BLI_duplicatelist(&folderlistn, folderlist);
-
- for (FolderList *folder = folderlistn.first; folder; folder = folder->next) {
- folder->foldername = MEM_dupallocN(folder->foldername);
- }
- return folderlistn;
-}
-
-/* ----------------- Folder-History (wraps/owns file list above) -------------- */
-
-static FileFolderHistory *folder_history_find(const SpaceFile *sfile, eFileBrowse_Mode browse_mode)
-{
- LISTBASE_FOREACH (FileFolderHistory *, history, &sfile->folder_histories) {
- if (history->browse_mode == browse_mode) {
- return history;
- }
- }
-
- return NULL;
-}
-
-void folder_history_list_ensure_for_active_browse_mode(SpaceFile *sfile)
-{
- FileFolderHistory *history = folder_history_find(sfile, sfile->browse_mode);
-
- if (!history) {
- history = MEM_callocN(sizeof(*history), __func__);
- history->browse_mode = sfile->browse_mode;
- BLI_addtail(&sfile->folder_histories, history);
- }
-
- sfile->folders_next = &history->folders_next;
- sfile->folders_prev = &history->folders_prev;
-}
-
-static void folder_history_entry_free(SpaceFile *sfile, FileFolderHistory *history)
-{
- if (sfile->folders_prev == &history->folders_prev) {
- sfile->folders_prev = NULL;
- }
- if (sfile->folders_next == &history->folders_next) {
- sfile->folders_next = NULL;
- }
- folderlist_free(&history->folders_prev);
- folderlist_free(&history->folders_next);
- BLI_freelinkN(&sfile->folder_histories, history);
-}
-
-void folder_history_list_free(SpaceFile *sfile)
-{
- LISTBASE_FOREACH_MUTABLE (FileFolderHistory *, history, &sfile->folder_histories) {
- folder_history_entry_free(sfile, history);
- }
-}
-
-ListBase folder_history_list_duplicate(ListBase *listbase)
-{
- ListBase histories = {NULL};
-
- LISTBASE_FOREACH (FileFolderHistory *, history, listbase) {
- FileFolderHistory *history_new = MEM_dupallocN(history);
- history_new->folders_prev = folderlist_duplicate(&history->folders_prev);
- history_new->folders_next = folderlist_duplicate(&history->folders_next);
- BLI_addtail(&histories, history_new);
- }
-
- return histories;
-}
-
/* ------------------FILELIST------------------------ */
-typedef struct FileListInternEntry {
- struct FileListInternEntry *next, *prev;
+struct FileListInternEntry {
+ FileListInternEntry *next, *prev;
FileUID uid;
- /** eFileSel_File_Types */
- int typeflag;
+ eFileSel_File_Types typeflag;
/** ID type, in case typeflag has FILE_TYPE_BLENDERLIB set. */
int blentype;
@@ -287,18 +118,18 @@ typedef struct FileListInternEntry {
/** Defined in BLI_fileops.h */
eFileAttributes attributes;
BLI_stat_t st;
-} FileListInternEntry;
+};
-typedef struct FileListIntern {
+struct FileListIntern {
/** FileListInternEntry items. */
ListBase entries;
FileListInternEntry **filtered;
FileUID curr_uid; /* Used to generate UID during internal listing. */
-} FileListIntern;
+};
#define FILELIST_ENTRYCACHESIZE_DEFAULT 1024 /* Keep it a power of two! */
-typedef struct FileListEntryCache {
+struct FileListEntryCache {
size_t size; /* The size of the cache... */
int flags;
@@ -327,7 +158,7 @@ typedef struct FileListEntryCache {
* previews either in `previews_pool` or `previews_done`. #filelist_cache_previews_update() makes
* previews in `preview_done` ready for display, so the counter is decremented there. */
int previews_todo_count;
-} FileListEntryCache;
+};
/** #FileListCache.flags */
enum {
@@ -335,21 +166,21 @@ enum {
FLC_PREVIEWS_ACTIVE = 1 << 1,
};
-typedef struct FileListEntryPreview {
+struct FileListEntryPreview {
char filepath[FILE_MAX];
uint flags;
int index;
int attributes; /* from FileDirEntry. */
int icon_id;
-} FileListEntryPreview;
+};
/* Dummy wrapper around FileListEntryPreview to ensure we do not access freed memory when freeing
* tasks' data (see T74609). */
-typedef struct FileListEntryPreviewTaskData {
+struct FileListEntryPreviewTaskData {
FileListEntryPreview *preview;
-} FileListEntryPreviewTaskData;
+};
-typedef struct FileListFilter {
+struct FileListFilter {
uint64_t filter;
uint64_t filter_id;
char filter_glob[FILE_MAXFILE];
@@ -357,7 +188,7 @@ typedef struct FileListFilter {
short flags;
FileAssetCatalogFilterSettingsHandle *asset_catalog_filter;
-} FileListFilter;
+};
/** #FileListFilter.flags */
enum {
@@ -369,13 +200,13 @@ enum {
};
struct FileListReadJob;
-typedef struct FileList {
+struct FileList {
FileDirEntryArr filelist;
eFileSelectType type;
/* The library this list was created for. Stored here so we know when to re-read. */
AssetLibraryReference *asset_library_ref;
- struct AssetLibrary *asset_library; /* Non-owning pointer. */
+ AssetLibrary *asset_library; /* Non-owning pointer. */
short flags;
@@ -386,11 +217,11 @@ typedef struct FileList {
/**
* File indexer to use. Attribute is always set.
*/
- const struct FileIndexerType *indexer;
+ const FileIndexerType *indexer;
- struct FileListIntern filelist_intern;
+ FileListIntern filelist_intern;
- struct FileListEntryCache filelist_cache;
+ FileListEntryCache filelist_cache;
/* We need to keep those info outside of actual filelist items,
* because those are no more persistent
@@ -403,7 +234,7 @@ typedef struct FileList {
short max_recursion;
short recursion_level;
- struct BlendHandle *libfiledata;
+ BlendHandle *libfiledata;
/* Set given path as root directory,
* if last bool is true may change given string in place to a valid value.
@@ -419,7 +250,7 @@ typedef struct FileList {
void (*prepare_filter_fn)(const struct FileList *, FileListFilter *);
short tags; /* FileListTags */
-} FileList;
+};
/** #FileList.flags */
enum {
@@ -459,23 +290,23 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
-static void filelist_readjob_main(struct FileListReadJob *job_params,
+static void filelist_readjob_main(FileListReadJob *job_params,
short *stop,
short *do_update,
float *progress);
-static void filelist_readjob_lib(struct FileListReadJob *job_params,
+static void filelist_readjob_lib(FileListReadJob *job_params,
short *stop,
short *do_update,
float *progress);
-static void filelist_readjob_dir(struct FileListReadJob *job_params,
+static void filelist_readjob_dir(FileListReadJob *job_params,
short *stop,
short *do_update,
float *progress);
-static void filelist_readjob_asset_library(struct FileListReadJob *job_params,
+static void filelist_readjob_asset_library(FileListReadJob *job_params,
short *stop,
short *do_update,
float *progress);
-static void filelist_readjob_main_assets(struct FileListReadJob *job_params,
+static void filelist_readjob_main_assets(FileListReadJob *job_params,
short *stop,
short *do_update,
float *progress);
@@ -493,7 +324,7 @@ struct FileSortData {
bool inverted;
};
-static int compare_apply_inverted(int val, const struct FileSortData *sort_data)
+static int compare_apply_inverted(int val, const FileSortData *sort_data)
{
return sort_data->inverted ? -val : val;
}
@@ -602,9 +433,9 @@ static int compare_direntry_generic(const FileListInternEntry *entry1,
static int compare_name(void *user_data, const void *a1, const void *a2)
{
- const FileListInternEntry *entry1 = a1;
- const FileListInternEntry *entry2 = a2;
- const struct FileSortData *sort_data = user_data;
+ const FileListInternEntry *entry1 = static_cast<const FileListInternEntry *>(a1);
+ const FileListInternEntry *entry2 = static_cast<const FileListInternEntry *>(a2);
+ const FileSortData *sort_data = static_cast<const FileSortData *>(user_data);
int ret;
if ((ret = compare_direntry_generic(entry1, entry2))) {
@@ -616,9 +447,9 @@ static int compare_name(void *user_data, const void *a1, const void *a2)
static int compare_date(void *user_data, const void *a1, const void *a2)
{
- const FileListInternEntry *entry1 = a1;
- const FileListInternEntry *entry2 = a2;
- const struct FileSortData *sort_data = user_data;
+ const FileListInternEntry *entry1 = static_cast<const FileListInternEntry *>(a1);
+ const FileListInternEntry *entry2 = static_cast<const FileListInternEntry *>(a2);
+ const FileSortData *sort_data = static_cast<const FileSortData *>(user_data);
int64_t time1, time2;
int ret;
@@ -640,9 +471,9 @@ static int compare_date(void *user_data, const void *a1, const void *a2)
static int compare_size(void *user_data, const void *a1, const void *a2)
{
- const FileListInternEntry *entry1 = a1;
- const FileListInternEntry *entry2 = a2;
- const struct FileSortData *sort_data = user_data;
+ const FileListInternEntry *entry1 = static_cast<const FileListInternEntry *>(a1);
+ const FileListInternEntry *entry2 = static_cast<const FileListInternEntry *>(a2);
+ const FileSortData *sort_data = static_cast<const FileSortData *>(user_data);
uint64_t size1, size2;
int ret;
@@ -664,9 +495,9 @@ static int compare_size(void *user_data, const void *a1, const void *a2)
static int compare_extension(void *user_data, const void *a1, const void *a2)
{
- const FileListInternEntry *entry1 = a1;
- const FileListInternEntry *entry2 = a2;
- const struct FileSortData *sort_data = user_data;
+ const FileListInternEntry *entry1 = static_cast<const FileListInternEntry *>(a1);
+ const FileListInternEntry *entry2 = static_cast<const FileListInternEntry *>(a2);
+ const FileSortData *sort_data = static_cast<const FileSortData *>(user_data);
int ret;
if ((ret = compare_direntry_generic(entry1, entry2))) {
@@ -720,7 +551,7 @@ static int compare_extension(void *user_data, const void *a1, const void *a2)
void filelist_sort(struct FileList *filelist)
{
if (filelist->flags & FL_NEED_SORTING) {
- void *sort_cb = NULL;
+ int (*sort_cb)(void *, const void *, const void *) = nullptr;
switch (filelist->sort) {
case FILE_SORT_ALPHA:
@@ -740,10 +571,10 @@ void filelist_sort(struct FileList *filelist)
BLI_assert(0);
break;
}
- BLI_listbase_sort_r(
- &filelist->filelist_intern.entries,
- sort_cb,
- &(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0});
+
+ FileSortData sort_data{0};
+ sort_data.inverted = (filelist->flags & FL_SORT_INVERT) != 0;
+ BLI_listbase_sort_r(&filelist->filelist_intern.entries, sort_cb, &sort_data);
filelist_tag_needs_filtering(filelist);
filelist->flags &= ~FL_NEED_SORTING;
@@ -1062,7 +893,7 @@ void filelist_filter(FileList *filelist)
{
int num_filtered = 0;
const int num_files = filelist->filelist.entries_num;
- FileListInternEntry **filtered_tmp, *file;
+ FileListInternEntry **filtered_tmp;
if (ELEM(filelist->filelist.entries_num, FILEDIR_NBR_ENTRIES_UNSET, 0)) {
return;
@@ -1087,10 +918,11 @@ void filelist_filter(FileList *filelist)
filelist->prepare_filter_fn(filelist, &filelist->filter_data);
}
- filtered_tmp = MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__);
+ filtered_tmp = static_cast<FileListInternEntry **>(
+ MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__));
/* Filter remap & count how many files are left after filter in a single loop. */
- for (file = filelist->filelist_intern.entries.first; file; file = file->next) {
+ LISTBASE_FOREACH (FileListInternEntry *, file, &filelist->filelist_intern.entries) {
if (filelist->filter_fn(file, filelist->filelist.root, &filelist->filter_data)) {
filtered_tmp[num_filtered++] = file;
}
@@ -1099,8 +931,8 @@ void filelist_filter(FileList *filelist)
if (filelist->filelist_intern.filtered) {
MEM_freeN(filelist->filelist_intern.filtered);
}
- filelist->filelist_intern.filtered = MEM_mallocN(
- sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered, __func__);
+ filelist->filelist_intern.filtered = static_cast<FileListInternEntry **>(
+ MEM_mallocN(sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered, __func__));
memcpy(filelist->filelist_intern.filtered,
filtered_tmp,
sizeof(*filelist->filelist_intern.filtered) * (size_t)num_filtered);
@@ -1228,8 +1060,7 @@ void filelist_setlibrary(FileList *filelist, const AssetLibraryReference *asset_
}
if (!filelist->asset_library_ref) {
- filelist->asset_library_ref = MEM_mallocN(sizeof(*filelist->asset_library_ref),
- "filelist asset library");
+ filelist->asset_library_ref = MEM_new<AssetLibraryReference>("filelist asset library");
*filelist->asset_library_ref = *asset_library_ref;
filelist->flags |= FL_FORCE_RESET;
@@ -1335,7 +1166,7 @@ static int filelist_geticon_ex(const FileDirEntry *file,
const bool is_main,
const bool ignore_libdir)
{
- const eFileSel_File_Types typeflag = file->typeflag;
+ const eFileSel_File_Types typeflag = (eFileSel_File_Types)file->typeflag;
if ((typeflag & FILE_TYPE_DIR) &&
!(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) {
@@ -1581,10 +1412,7 @@ static void filelist_intern_entry_free(FileListInternEntry *entry)
static void filelist_intern_free(FileListIntern *filelist_intern)
{
- FileListInternEntry *entry, *entry_next;
-
- for (entry = filelist_intern->entries.first; entry; entry = entry_next) {
- entry_next = entry->next;
+ LISTBASE_FOREACH_MUTABLE (FileListInternEntry *, entry, &filelist_intern->entries) {
filelist_intern_entry_free(entry);
}
BLI_listbase_clear(&filelist_intern->entries);
@@ -1614,11 +1442,14 @@ static int filelist_intern_free_main_files(FileListIntern *filelist_intern)
static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata)
{
- FileListEntryCache *cache = BLI_task_pool_user_data(pool);
- FileListEntryPreviewTaskData *preview_taskdata = taskdata;
+ FileListEntryCache *cache = static_cast<FileListEntryCache *>(BLI_task_pool_user_data(pool));
+ FileListEntryPreviewTaskData *preview_taskdata = static_cast<FileListEntryPreviewTaskData *>(
+ taskdata);
FileListEntryPreview *preview = preview_taskdata->preview;
- ThumbSource source = 0;
+ /* XXX #THB_SOURCE_IMAGE for "historic" reasons. The case of an undefined source should be
+ * handled better. */
+ ThumbSource source = THB_SOURCE_IMAGE;
// printf("%s: Start (%d)...\n", __func__, threadid);
@@ -1662,7 +1493,8 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void *taskdata)
{
- FileListEntryPreviewTaskData *preview_taskdata = taskdata;
+ FileListEntryPreviewTaskData *preview_taskdata = static_cast<FileListEntryPreviewTaskData *>(
+ taskdata);
/* In case the preview wasn't moved to the "done" queue yet. */
if (preview_taskdata->preview) {
@@ -1689,7 +1521,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache)
BLI_task_pool_cancel(cache->previews_pool);
FileListEntryPreview *preview;
- while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) {
+ while ((preview = static_cast<FileListEntryPreview *>(
+ BLI_thread_queue_pop_timeout(cache->previews_done, 0)))) {
// printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path,
// preview->img);
if (preview->icon_id) {
@@ -1749,7 +1582,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
filelist_cache_preview_ensure_running(cache);
entry->flags |= FILE_ENTRY_PREVIEW_LOADING;
- FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
+ FileListEntryPreview *preview = MEM_new<FileListEntryPreview>(__func__);
preview->index = index;
preview->flags = entry->typeflag;
preview->attributes = entry->attributes;
@@ -1775,8 +1608,8 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
}
// printf("%s: %d - %s\n", __func__, preview->index, preview->filepath);
- FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata),
- __func__);
+ FileListEntryPreviewTaskData *preview_taskdata = MEM_new<FileListEntryPreviewTaskData>(
+ __func__);
preview_taskdata->preview = preview;
BLI_task_pool_push(cache->previews_pool,
filelist_cache_preview_runf,
@@ -1793,11 +1626,12 @@ static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
cache->block_cursor = cache->block_start_index = cache->block_center_index =
cache->block_end_index = 0;
- cache->block_entries = MEM_mallocN(sizeof(*cache->block_entries) * cache_size, __func__);
+ cache->block_entries = static_cast<FileDirEntry **>(
+ MEM_mallocN(sizeof(*cache->block_entries) * cache_size, __func__));
cache->misc_entries = BLI_ghash_ptr_new_ex(__func__, cache_size);
- cache->misc_entries_indices = MEM_mallocN(sizeof(*cache->misc_entries_indices) * cache_size,
- __func__);
+ cache->misc_entries_indices = static_cast<int *>(
+ MEM_mallocN(sizeof(*cache->misc_entries_indices) * cache_size, __func__));
copy_vn_i(cache->misc_entries_indices, cache_size, -1);
cache->misc_cursor = 0;
@@ -1815,8 +1649,6 @@ static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size)
static void filelist_cache_free(FileListEntryCache *cache)
{
- FileDirEntry *entry, *entry_next;
-
if (!(cache->flags & FLC_IS_INIT)) {
return;
}
@@ -1830,8 +1662,7 @@ static void filelist_cache_free(FileListEntryCache *cache)
BLI_ghash_free(cache->uids, NULL, NULL);
- for (entry = cache->cached_entries.first; entry; entry = entry_next) {
- entry_next = entry->next;
+ LISTBASE_FOREACH_MUTABLE (FileDirEntry *, entry, &cache->cached_entries) {
filelist_entry_free(entry);
}
BLI_listbase_clear(&cache->cached_entries);
@@ -1839,8 +1670,6 @@ static void filelist_cache_free(FileListEntryCache *cache)
static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
{
- FileDirEntry *entry, *entry_next;
-
if (!(cache->flags & FLC_IS_INIT)) {
return;
}
@@ -1850,14 +1679,14 @@ static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
cache->block_cursor = cache->block_start_index = cache->block_center_index =
cache->block_end_index = 0;
if (new_size != cache->size) {
- cache->block_entries = MEM_reallocN(cache->block_entries,
- sizeof(*cache->block_entries) * new_size);
+ cache->block_entries = static_cast<FileDirEntry **>(
+ MEM_reallocN(cache->block_entries, sizeof(*cache->block_entries) * new_size));
}
BLI_ghash_clear_ex(cache->misc_entries, NULL, NULL, new_size);
if (new_size != cache->size) {
- cache->misc_entries_indices = MEM_reallocN(cache->misc_entries_indices,
- sizeof(*cache->misc_entries_indices) * new_size);
+ cache->misc_entries_indices = static_cast<int *>(MEM_reallocN(
+ cache->misc_entries_indices, sizeof(*cache->misc_entries_indices) * new_size));
}
copy_vn_i(cache->misc_entries_indices, new_size, -1);
@@ -1865,8 +1694,7 @@ static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
cache->size = new_size;
- for (entry = cache->cached_entries.first; entry; entry = entry_next) {
- entry_next = entry->next;
+ LISTBASE_FOREACH_MUTABLE (FileDirEntry *, entry, &cache->cached_entries) {
filelist_entry_free(entry);
}
BLI_listbase_clear(&cache->cached_entries);
@@ -1874,7 +1702,7 @@ static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size)
FileList *filelist_new(short type)
{
- FileList *p = MEM_callocN(sizeof(*p), __func__);
+ FileList *p = MEM_cnew<FileList>(__func__);
filelist_cache_init(&p->filelist_cache, FILELIST_ENTRYCACHESIZE_DEFAULT);
@@ -1891,7 +1719,7 @@ void filelist_settype(FileList *filelist, short type)
return;
}
- filelist->type = type;
+ filelist->type = (eFileSelectType)type;
filelist->tags = 0;
filelist->indexer = &file_indexer_noop;
switch (filelist->type) {
@@ -2181,7 +2009,7 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
FileListEntryCache *cache = &filelist->filelist_cache;
FileDirEntry *ret;
- ret = MEM_callocN(sizeof(*ret), __func__);
+ ret = MEM_cnew<FileDirEntry>(__func__);
ret->size = (uint64_t)entry->st.st_size;
ret->time = (int64_t)entry->st.st_mtime;
@@ -2240,7 +2068,8 @@ FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const
return cache->block_entries[idx];
}
- if ((ret = BLI_ghash_lookup(cache->misc_entries, POINTER_FROM_INT(index)))) {
+ if ((ret = static_cast<FileDirEntry *>(
+ BLI_ghash_lookup(cache->misc_entries, POINTER_FROM_INT(index))))) {
return ret;
}
@@ -2253,7 +2082,8 @@ FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const
/* Else, we have to add new entry to 'misc' cache - and possibly make room for it first! */
ret = filelist_file_create_entry(filelist, index);
old_index = cache->misc_entries_indices[cache->misc_cursor];
- if ((old = BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(old_index), NULL))) {
+ if ((old = static_cast<FileDirEntry *>(
+ BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(old_index), NULL)))) {
BLI_ghash_remove(cache->uids, POINTER_FROM_UINT(old->uid), NULL, NULL);
filelist_file_release_entry(filelist, old);
}
@@ -2371,7 +2201,8 @@ static bool filelist_file_cache_block_create(FileList *filelist,
FileDirEntry *entry;
/* That entry might have already been requested and stored in misc cache... */
- if ((entry = BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(idx), NULL)) == NULL) {
+ if ((entry = static_cast<FileDirEntry *>(
+ BLI_ghash_popkey(cache->misc_entries, POINTER_FROM_INT(idx), NULL))) == NULL) {
entry = filelist_file_create_entry(filelist, idx);
BLI_ghash_insert(cache->uids, POINTER_FROM_UINT(entry->uid), entry);
}
@@ -2660,7 +2491,8 @@ bool filelist_cache_previews_update(FileList *filelist)
// printf("%s: Update Previews...\n", __func__);
while (!BLI_thread_queue_is_empty(cache->previews_done)) {
- FileListEntryPreview *preview = BLI_thread_queue_pop(cache->previews_done);
+ FileListEntryPreview *preview = static_cast<FileListEntryPreview *>(
+ BLI_thread_queue_pop(cache->previews_done));
FileDirEntry *entry;
/* Paranoid (should never happen currently
@@ -3050,8 +2882,8 @@ static int filelist_readjob_list_dir(const char *root,
continue;
}
- entry = MEM_callocN(sizeof(*entry), __func__);
- entry->relpath = MEM_dupallocN(files[i].relname);
+ entry = MEM_cnew<FileListInternEntry>(__func__);
+ entry->relpath = static_cast<char *>(MEM_dupallocN(files[i].relname));
entry->st = files[i].s;
BLI_join_dirfile(full_path, FILE_MAX, root, entry->relpath);
@@ -3069,14 +2901,14 @@ static int filelist_readjob_list_dir(const char *root,
/* Is this a file that points to another file? */
if (entry->attributes & FILE_ATTR_ALIAS) {
- entry->redirection_path = MEM_callocN(FILE_MAXDIR, __func__);
+ entry->redirection_path = MEM_cnew_array<char>(FILE_MAXDIR, __func__);
if (BLI_file_alias_target(full_path, entry->redirection_path)) {
if (BLI_is_dir(entry->redirection_path)) {
entry->typeflag = FILE_TYPE_DIR;
BLI_path_slash_ensure(entry->redirection_path);
}
else {
- entry->typeflag = ED_path_extension_type(entry->redirection_path);
+ entry->typeflag = (eFileSel_File_Types)ED_path_extension_type(entry->redirection_path);
}
target = entry->redirection_path;
#ifdef WIN32
@@ -3101,7 +2933,7 @@ static int filelist_readjob_list_dir(const char *root,
}
}
else {
- entry->typeflag = ED_path_extension_type(target);
+ entry->typeflag = (eFileSel_File_Types)ED_path_extension_type(target);
if (filter_glob[0] && BLI_path_extension_check_glob(target, filter_glob)) {
entry->typeflag |= FILE_TYPE_OPERATOR;
}
@@ -3124,6 +2956,8 @@ static int filelist_readjob_list_dir(const char *root,
}
typedef enum ListLibOptions {
+ LIST_LIB_OPTION_NONE = 0,
+
/* Will read both the groups + actual ids from the library. Reduces the amount of times that
* a library needs to be opened. */
LIST_LIB_RECURSIVE = (1 << 0),
@@ -3134,11 +2968,12 @@ typedef enum ListLibOptions {
/* Add given root as result. */
LIST_LIB_ADD_PARENT = (1 << 2),
} ListLibOptions;
+ENUM_OPERATORS(ListLibOptions, LIST_LIB_ADD_PARENT);
static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idcode,
const char *group_name)
{
- FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
+ FileListInternEntry *entry = MEM_cnew<FileListInternEntry>(__func__);
entry->relpath = BLI_strdup(group_name);
entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR;
entry->blentype = idcode;
@@ -3151,7 +2986,7 @@ static void filelist_readjob_list_lib_add_datablock(ListBase *entries,
const int idcode,
const char *group_name)
{
- FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
+ FileListInternEntry *entry = MEM_cnew<FileListInternEntry>(__func__);
if (prefix_relpath_with_group_name) {
entry->relpath = BLI_sprintfN("%s/%s", group_name, datablock_info->name);
}
@@ -3175,7 +3010,7 @@ static void filelist_readjob_list_lib_add_datablocks(ListBase *entries,
const char *group_name)
{
for (LinkNode *ln = datablock_infos; ln; ln = ln->next) {
- struct BLODataBlockInfo *datablock_info = ln->link;
+ struct BLODataBlockInfo *datablock_info = static_cast<BLODataBlockInfo *>(ln->link);
filelist_readjob_list_lib_add_datablock(
entries, datablock_info, prefix_relpath_with_group_name, idcode, group_name);
}
@@ -3199,7 +3034,7 @@ static void filelist_readjob_list_lib_add_from_indexer_entries(
static FileListInternEntry *filelist_readjob_list_lib_navigate_to_parent_entry_create(void)
{
- FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
+ FileListInternEntry *entry = MEM_cnew<FileListInternEntry>(__func__);
entry->relpath = BLI_strdup(FILENAME_PARENT);
entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR);
return entry;
@@ -3279,7 +3114,7 @@ static int filelist_readjob_list_lib(const char *root,
}
/* Open the library file. */
- BlendFileReadReport bf_reports = {.reports = NULL};
+ BlendFileReadReport bf_reports{};
libfiledata = BLO_blendhandle_from_file(dir, &bf_reports);
if (libfiledata == NULL) {
return 0;
@@ -3310,7 +3145,7 @@ static int filelist_readjob_list_lib(const char *root,
group_len = BLI_linklist_count(groups);
for (LinkNode *ln = groups; ln; ln = ln->next) {
- const char *group_name = ln->link;
+ const char *group_name = static_cast<char *>(ln->link);
const int idcode = groupname_to_code(group_name);
FileListInternEntry *group_entry = filelist_readjob_list_lib_group_create(idcode,
group_name);
@@ -3615,7 +3450,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
int dirs_done_count = 0, dirs_todo_count = 1;
todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
- td_dir = BLI_stack_push_r(todo_dirs);
+ td_dir = static_cast<TodoDir *>(BLI_stack_push_r(todo_dirs));
td_dir->level = 1;
BLI_strncpy(dir, filelist->filelist.root, sizeof(dir));
@@ -3625,13 +3460,13 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
td_dir->dir = BLI_strdup(dir);
/* Init the file indexer. */
- FileIndexer indexer_runtime = {.callbacks = filelist->indexer};
+ FileIndexer indexer_runtime{};
+ indexer_runtime.callbacks = filelist->indexer;
if (indexer_runtime.callbacks->init_user_data) {
indexer_runtime.user_data = indexer_runtime.callbacks->init_user_data(dir, sizeof(dir));
}
while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
- FileListInternEntry *entry;
int entries_num = 0;
char *subdir;
@@ -3639,7 +3474,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
int recursion_level;
bool skip_currpar;
- td_dir = BLI_stack_peek(todo_dirs);
+ td_dir = static_cast<TodoDir *>(BLI_stack_peek(todo_dirs));
subdir = td_dir->dir;
recursion_level = td_dir->level;
skip_currpar = (recursion_level > 1);
@@ -3657,7 +3492,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
bool is_lib = false;
if (do_lib) {
- ListLibOptions list_lib_options = 0;
+ ListLibOptions list_lib_options = LIST_LIB_OPTION_NONE;
if (!skip_currpar) {
list_lib_options |= LIST_LIB_ADD_PARENT;
}
@@ -3684,7 +3519,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar);
}
- for (entry = entries.first; entry; entry = entry->next) {
+ LISTBASE_FOREACH (FileListInternEntry *, entry, &entries) {
entry->uid = filelist_uid_generate(filelist);
/* When loading entries recursive, the rel_path should be relative from the root dir.
@@ -3701,7 +3536,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
/* We have a directory we want to list, add it to todo list! */
BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
BLI_path_normalize_dir(job_params->main_name, dir);
- td_dir = BLI_stack_push_r(todo_dirs);
+ td_dir = static_cast<TodoDir *>(BLI_stack_push_r(todo_dirs));
td_dir->level = recursion_level + 1;
td_dir->dir = BLI_strdup(dir);
dirs_todo_count++;
@@ -3727,7 +3562,7 @@ static void filelist_readjob_recursive_dir_add_items(const bool do_lib,
/* If we were interrupted by stop, stack may not be empty and we need to free
* pending dir paths. */
while (!BLI_stack_is_empty(todo_dirs)) {
- td_dir = BLI_stack_peek(todo_dirs);
+ td_dir = static_cast<TodoDir *>(BLI_stack_peek(todo_dirs));
MEM_freeN(td_dir->dir);
BLI_stack_discard(todo_dirs);
}
@@ -3831,7 +3666,7 @@ static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params,
const char *id_code_name = BKE_idtype_idcode_to_name(GS(id_iter->name));
- entry = MEM_callocN(sizeof(*entry), __func__);
+ entry = MEM_cnew<FileListInternEntry>(__func__);
entry->relpath = BLI_strdup(id_code_name);
entry->name = id_iter->name + 2;
entry->free_name = false;
@@ -3935,7 +3770,7 @@ static bool filelist_readjob_is_partial_read(const FileListReadJob *read_job)
*/
static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress)
{
- FileListReadJob *flrj = flrjv;
+ FileListReadJob *flrj = static_cast<FileListReadJob *>(flrjv);
// printf("START filelist reading (%d files, main thread: %d)\n",
// flrj->filelist->filelist.entries_num, BLI_thread_is_main());
@@ -3944,7 +3779,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
BLI_assert((flrj->tmp_filelist == NULL) && flrj->filelist);
- flrj->tmp_filelist = MEM_dupallocN(flrj->filelist);
+ flrj->tmp_filelist = static_cast<FileList *>(MEM_dupallocN(flrj->filelist));
BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries);
flrj->tmp_filelist->filelist.entries_num = FILEDIR_NBR_ENTRIES_UNSET;
@@ -3976,7 +3811,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
*/
static void filelist_readjob_update(void *flrjv)
{
- FileListReadJob *flrj = flrjv;
+ FileListReadJob *flrj = static_cast<FileListReadJob *>(flrjv);
FileListIntern *fl_intern = &flrj->filelist->filelist_intern;
ListBase new_entries = {NULL};
int entries_num, new_entries_num = 0;
@@ -4019,7 +3854,7 @@ static void filelist_readjob_update(void *flrjv)
static void filelist_readjob_endjob(void *flrjv)
{
- FileListReadJob *flrj = flrjv;
+ FileListReadJob *flrj = static_cast<FileListReadJob *>(flrjv);
/* In case there would be some dangling update... */
filelist_readjob_update(flrjv);
@@ -4030,7 +3865,7 @@ static void filelist_readjob_endjob(void *flrjv)
static void filelist_readjob_free(void *flrjv)
{
- FileListReadJob *flrj = flrjv;
+ FileListReadJob *flrj = static_cast<FileListReadJob *>(flrjv);
// printf("END filelist reading (%d files)\n", flrj->filelist->filelist.entries_num);
@@ -4060,7 +3895,7 @@ void filelist_readjob_start(FileList *filelist, const int space_notifier, const
}
/* prepare job data */
- flrj = MEM_callocN(sizeof(*flrj), __func__);
+ flrj = MEM_cnew<FileListReadJob>(__func__);
flrj->filelist = filelist;
flrj->current_main = bmain;
BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name));
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 4c1ae79eb22..89831483fdf 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -35,17 +35,6 @@ typedef enum FileCheckType {
CHECK_ALL = 3,
} FileCheckType;
-/* not listbase itself */
-void folderlist_free(struct ListBase *folderlist);
-void folderlist_popdir(struct ListBase *folderlist, char *dir);
-void folderlist_pushdir(struct ListBase *folderlist, const char *dir);
-const char *folderlist_peeklastdir(struct ListBase *folderlist);
-int folderlist_clear_next(struct SpaceFile *sfile);
-
-void folder_history_list_ensure_for_active_browse_mode(struct SpaceFile *sfile);
-void folder_history_list_free(struct SpaceFile *sfile);
-struct ListBase folder_history_list_duplicate(struct ListBase *listbase);
-
void filelist_setsorting(struct FileList *filelist, short sort, bool invert_sort);
void filelist_sort(struct FileList *filelist);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index e42e1e98660..93aa5cf992d 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -665,12 +665,17 @@ void ED_fileselect_params_to_userdef(SpaceFile *sfile,
}
}
-void fileselect_file_set(SpaceFile *sfile, const int index)
+void fileselect_file_set(struct bContext *C, SpaceFile *sfile, const int index)
{
const struct FileDirEntry *file = filelist_file(sfile->files, index);
if (file && file->relpath && file->relpath[0] && !(file->typeflag & FILE_TYPE_DIR)) {
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
BLI_strncpy(params->file, file->relpath, FILE_MAXFILE);
+ if (sfile->op) {
+ /* Update the filepath properties of the operator. */
+ Main *bmain = CTX_data_main(C);
+ file_sfile_to_operator(C, bmain, sfile->op, sfile);
+ }
}
}
@@ -1041,7 +1046,7 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region)
layout->attribute_column_header_h = 0;
layout->offset_top = layout->attribute_column_header_h;
layout->height = (int)(BLI_rctf_size_y(&v2d->cur) - 2 * layout->tile_border_y);
- /* Padding by full scrollbar H is too much, can overlap tile border Y. */
+ /* Padding by full scroll-bar H is too much, can overlap tile border Y. */
layout->rows = (layout->height - V2D_SCROLL_HEIGHT + layout->tile_border_y) /
(layout->tile_h + 2 * layout->tile_border_y);
layout->tile_w = VERTLIST_MAJORCOLUMN_WIDTH;
@@ -1385,3 +1390,24 @@ ScrArea *ED_fileselect_handler_area_find_any_with_op(const wmWindow *win)
return NULL;
}
+
+void ED_fileselect_ensure_default_filepath(struct bContext *C,
+ struct wmOperator *op,
+ const char *extension)
+{
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
+ struct Main *bmain = CTX_data_main(C);
+ char filepath[FILE_MAX];
+ const char *blendfile_path = BKE_main_blendfile_path(bmain);
+
+ if (blendfile_path[0] == '\0') {
+ BLI_strncpy(filepath, DATA_("untitled"), sizeof(filepath));
+ }
+ else {
+ BLI_strncpy(filepath, blendfile_path, sizeof(filepath));
+ }
+
+ BLI_path_extension_replace(filepath, sizeof(filepath), extension);
+ RNA_string_set(op->ptr, "filepath", filepath);
+ }
+}
diff --git a/source/blender/editors/space_file/folder_history.cc b/source/blender/editors/space_file/folder_history.cc
new file mode 100644
index 00000000000..9aa1d181584
--- /dev/null
+++ b/source/blender/editors/space_file/folder_history.cc
@@ -0,0 +1,191 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2007 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup spfile
+ *
+ * Storage for a list of folders for history backward and forward navigation.
+ */
+
+#include <cstring>
+
+#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "BKE_context.h"
+
+#include "DNA_space_types.h"
+
+#include "ED_fileselect.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "file_intern.h"
+
+/* ----------------- FOLDERLIST (previous/next) -------------- */
+
+typedef struct FolderList {
+ struct FolderList *next, *prev;
+ char *foldername;
+} FolderList;
+
+void folderlist_popdir(struct ListBase *folderlist, char *dir)
+{
+ const char *prev_dir;
+ struct FolderList *folder;
+ folder = static_cast<FolderList *>(folderlist->last);
+
+ if (folder) {
+ /* remove the current directory */
+ MEM_freeN(folder->foldername);
+ BLI_freelinkN(folderlist, folder);
+
+ folder = static_cast<FolderList *>(folderlist->last);
+ if (folder) {
+ prev_dir = folder->foldername;
+ BLI_strncpy(dir, prev_dir, FILE_MAXDIR);
+ }
+ }
+ /* delete the folder next or use setdir directly before PREVIOUS OP */
+}
+
+void folderlist_pushdir(ListBase *folderlist, const char *dir)
+{
+ if (!dir[0]) {
+ return;
+ }
+
+ struct FolderList *folder, *previous_folder;
+ previous_folder = static_cast<FolderList *>(folderlist->last);
+
+ /* check if already exists */
+ if (previous_folder && previous_folder->foldername) {
+ if (BLI_path_cmp(previous_folder->foldername, dir) == 0) {
+ return;
+ }
+ }
+
+ /* create next folder element */
+ folder = MEM_new<FolderList>(__func__);
+ folder->foldername = BLI_strdup(dir);
+
+ /* add it to the end of the list */
+ BLI_addtail(folderlist, folder);
+}
+
+const char *folderlist_peeklastdir(ListBase *folderlist)
+{
+ struct FolderList *folder;
+
+ if (!folderlist->last) {
+ return NULL;
+ }
+
+ folder = static_cast<FolderList *>(folderlist->last);
+ return folder->foldername;
+}
+
+int folderlist_clear_next(struct SpaceFile *sfile)
+{
+ const FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+ struct FolderList *folder;
+
+ /* if there is no folder_next there is nothing we can clear */
+ if (BLI_listbase_is_empty(sfile->folders_next)) {
+ return 0;
+ }
+
+ /* if previous_folder, next_folder or refresh_folder operators are executed
+ * it doesn't clear folder_next */
+ folder = static_cast<FolderList *>(sfile->folders_prev->last);
+ if ((!folder) || (BLI_path_cmp(folder->foldername, params->dir) == 0)) {
+ return 0;
+ }
+
+ /* eventually clear flist->folders_next */
+ return 1;
+}
+
+void folderlist_free(ListBase *folderlist)
+{
+ if (folderlist) {
+ LISTBASE_FOREACH (FolderList *, folder, folderlist) {
+ MEM_freeN(folder->foldername);
+ }
+ BLI_freelistN(folderlist);
+ }
+}
+
+static ListBase folderlist_duplicate(ListBase *folderlist)
+{
+ ListBase folderlistn = {NULL};
+
+ BLI_duplicatelist(&folderlistn, folderlist);
+
+ LISTBASE_FOREACH (FolderList *, folder, &folderlistn) {
+ folder->foldername = (char *)MEM_dupallocN(folder->foldername);
+ }
+ return folderlistn;
+}
+
+/* ----------------- Folder-History (wraps/owns file list above) -------------- */
+
+static FileFolderHistory *folder_history_find(const SpaceFile *sfile, eFileBrowse_Mode browse_mode)
+{
+ LISTBASE_FOREACH (FileFolderHistory *, history, &sfile->folder_histories) {
+ if (history->browse_mode == browse_mode) {
+ return history;
+ }
+ }
+
+ return NULL;
+}
+
+void folder_history_list_ensure_for_active_browse_mode(SpaceFile *sfile)
+{
+ FileFolderHistory *history = folder_history_find(sfile, (eFileBrowse_Mode)sfile->browse_mode);
+
+ if (!history) {
+ history = MEM_cnew<FileFolderHistory>(__func__);
+ history->browse_mode = sfile->browse_mode;
+ BLI_addtail(&sfile->folder_histories, history);
+ }
+
+ sfile->folders_next = &history->folders_next;
+ sfile->folders_prev = &history->folders_prev;
+}
+
+static void folder_history_entry_free(SpaceFile *sfile, FileFolderHistory *history)
+{
+ if (sfile->folders_prev == &history->folders_prev) {
+ sfile->folders_prev = NULL;
+ }
+ if (sfile->folders_next == &history->folders_next) {
+ sfile->folders_next = NULL;
+ }
+ folderlist_free(&history->folders_prev);
+ folderlist_free(&history->folders_next);
+ BLI_freelinkN(&sfile->folder_histories, history);
+}
+
+void folder_history_list_free(SpaceFile *sfile)
+{
+ LISTBASE_FOREACH_MUTABLE (FileFolderHistory *, history, &sfile->folder_histories) {
+ folder_history_entry_free(sfile, history);
+ }
+}
+
+ListBase folder_history_list_duplicate(ListBase *listbase)
+{
+ ListBase histories = {NULL};
+
+ LISTBASE_FOREACH (FileFolderHistory *, history, listbase) {
+ FileFolderHistory *history_new = static_cast<FileFolderHistory *>(MEM_dupallocN(history));
+ history_new->folders_prev = folderlist_duplicate(&history->folders_prev);
+ history_new->folders_next = folderlist_duplicate(&history->folders_next);
+ BLI_addtail(&histories, history_new);
+ }
+
+ return histories;
+}
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index 310c688383b..35ce7ef364c 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -443,7 +443,7 @@ void fsmenu_insert_entry(struct FSMenu *fsmenu,
if (STREQ(tfsm->path, fsm_iter->path)) {
icon = tfsm->icon;
if (tfsm->name[0] && (!name || !name[0])) {
- name = tfsm->name;
+ name = DATA_(tfsm->name);
}
break;
}
@@ -519,7 +519,7 @@ void fsmenu_remove_entry(struct FSMenu *fsmenu, FSMenuCategory category, int idx
}
}
-void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath)
+bool fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath)
{
FSMenuEntry *fsm_iter = NULL;
char fsm_name[FILE_MAX];
@@ -527,33 +527,36 @@ void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath)
FILE *fp = BLI_fopen(filepath, "w");
if (!fp) {
- return;
+ return false;
}
- fprintf(fp, "[Bookmarks]\n");
+ bool has_error = false;
+ has_error |= (fprintf(fp, "[Bookmarks]\n") < 0);
for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_BOOKMARKS); fsm_iter;
fsm_iter = fsm_iter->next) {
if (fsm_iter->path && fsm_iter->save) {
fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
- fprintf(fp, "!%s\n", fsm_iter->name);
+ has_error |= (fprintf(fp, "!%s\n", fsm_iter->name) < 0);
}
- fprintf(fp, "%s\n", fsm_iter->path);
+ has_error |= (fprintf(fp, "%s\n", fsm_iter->path) < 0);
}
}
- fprintf(fp, "[Recent]\n");
+ has_error = (fprintf(fp, "[Recent]\n") < 0);
for (fsm_iter = ED_fsmenu_get_category(fsmenu, FS_CATEGORY_RECENT);
fsm_iter && (nwritten < FSMENU_RECENT_MAX);
fsm_iter = fsm_iter->next, nwritten++) {
if (fsm_iter->path && fsm_iter->save) {
fsmenu_entry_generate_name(fsm_iter, fsm_name, sizeof(fsm_name));
if (fsm_iter->name[0] && !STREQ(fsm_iter->name, fsm_name)) {
- fprintf(fp, "!%s\n", fsm_iter->name);
+ has_error |= (fprintf(fp, "!%s\n", fsm_iter->name) < 0);
}
- fprintf(fp, "%s\n", fsm_iter->path);
+ has_error |= (fprintf(fp, "%s\n", fsm_iter->path) < 0);
}
}
fclose(fp);
+
+ return !has_error;
}
void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath)
diff --git a/source/blender/editors/space_file/fsmenu.h b/source/blender/editors/space_file/fsmenu.h
index 6e980a326fc..f4f0dafbc73 100644
--- a/source/blender/editors/space_file/fsmenu.h
+++ b/source/blender/editors/space_file/fsmenu.h
@@ -37,8 +37,11 @@ short fsmenu_can_save(struct FSMenu *fsmenu, enum FSMenuCategory category, int i
/** Removes the fsmenu entry at the given \a index. */
void fsmenu_remove_entry(struct FSMenu *fsmenu, enum FSMenuCategory category, int idx);
-/** saves the 'bookmarks' to the specified file */
-void fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath);
+/**
+ * Saves the 'bookmarks' to the specified file.
+ * \return true on success.
+ */
+bool fsmenu_write_file(struct FSMenu *fsmenu, const char *filepath);
/** reads the 'bookmarks' from the specified file */
void fsmenu_read_bookmarks(struct FSMenu *fsmenu, const char *filepath);
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index a462476aae0..bba0c27bb4d 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -426,7 +426,7 @@ static void file_reset_filelist_showing_main_data(ScrArea *area, SpaceFile *sfil
static void file_listener(const wmSpaceTypeListenerParams *listener_params)
{
ScrArea *area = listener_params->area;
- wmNotifier *wmn = listener_params->notifier;
+ const wmNotifier *wmn = listener_params->notifier;
SpaceFile *sfile = (SpaceFile *)area->spacedata.first;
/* context changes */
@@ -514,7 +514,7 @@ static void file_main_region_init(wmWindowManager *wm, ARegion *region)
static void file_main_region_listener(const wmRegionListenerParams *listener_params)
{
ARegion *region = listener_params->region;
- wmNotifier *wmn = listener_params->notifier;
+ const wmNotifier *wmn = listener_params->notifier;
/* context changes */
switch (wmn->category) {
@@ -820,7 +820,7 @@ static void file_execution_region_draw(const bContext *C, ARegion *region)
static void file_ui_region_listener(const wmRegionListenerParams *listener_params)
{
ARegion *region = listener_params->region;
- wmNotifier *wmn = listener_params->notifier;
+ const wmNotifier *wmn = listener_params->notifier;
/* context changes */
switch (wmn->category) {
@@ -992,7 +992,7 @@ void ED_spacetype_file(void)
ARegionType *art;
st->spaceid = SPACE_FILE;
- strncpy(st->name, "File", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "File");
st->create = file_create;
st->free = file_free;
diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt
index ebcbf59be5f..39878debc39 100644
--- a/source/blender/editors/space_graph/CMakeLists.txt
+++ b/source/blender/editors/space_graph/CMakeLists.txt
@@ -10,7 +10,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index bc2705df314..edba3c39042 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -41,6 +41,7 @@
#include "WM_types.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "ED_anim_api.h"
@@ -225,15 +226,15 @@ static void graph_panel_properties(const bContext *C, Panel *panel)
/* color settings */
col = uiLayoutColumn(layout, true);
- uiItemR(col, &fcu_ptr, "color_mode", 0, "Display Color", ICON_NONE);
+ uiItemR(col, &fcu_ptr, "color_mode", 0, IFACE_("Display Color"), ICON_NONE);
if (fcu->color_mode == FCURVE_COLOR_CUSTOM) {
- uiItemR(col, &fcu_ptr, "color", 0, "Color", ICON_NONE);
+ uiItemR(col, &fcu_ptr, "color", 0, IFACE_("Color"), ICON_NONE);
}
/* smoothing setting */
col = uiLayoutColumn(layout, true);
- uiItemR(col, &fcu_ptr, "auto_smoothing", 0, "Handle Smoothing", ICON_NONE);
+ uiItemR(col, &fcu_ptr, "auto_smoothing", 0, IFACE_("Handle Smoothing"), ICON_NONE);
MEM_freeN(ale);
}
@@ -277,7 +278,7 @@ static void graphedit_activekey_update_cb(bContext *UNUSED(C),
/* make sure F-Curve and its handles are still valid after this editing */
sort_time_fcurve(fcu);
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
/* update callback for active keyframe properties - handle-editing wrapper */
@@ -640,7 +641,7 @@ static void do_graph_region_driver_buttons(bContext *C, void *id_v, int event)
ID *id = id_v;
AnimData *adt = BKE_animdata_from_id(id);
- /* rebuild depsgraph for the new deps, and ensure COW copies get flushed. */
+ /* Rebuild depsgraph for the new dependencies, and ensure COW copies get flushed. */
DEG_relations_tag_update(bmain);
DEG_id_tag_update_ex(bmain, id, ID_RECALC_COPY_ON_WRITE);
if (adt != NULL) {
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index 608a1f4d73e..41a8368152d 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -80,7 +80,7 @@ static void draw_fcurve_modifier_controls_envelope(FModifier *fcm,
GPU_line_width(1.0f);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -107,7 +107,7 @@ static void draw_fcurve_modifier_controls_envelope(FModifier *fcm,
/* set size of vertices (non-adjustable for now) */
GPU_point_size(2.0f);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* for now, point color is fixed, and is white */
immUniformColor3f(1.0f, 1.0f, 1.0f);
@@ -408,7 +408,7 @@ static void draw_fcurve_handles(SpaceGraph *sipo, FCurve *fcu)
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint color = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
if ((sipo->flag & SIPO_BEAUTYDRAW_OFF) == 0) {
GPU_line_smooth(true);
}
@@ -540,7 +540,7 @@ static void draw_fcurve_samples(SpaceGraph *sipo, ARegion *region, FCurve *fcu)
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor((fcu->flag & FCURVE_SELECTED) ? TH_TEXT_HI : TH_TEXT);
@@ -1045,7 +1045,7 @@ static void draw_fcurve(bAnimContext *ac, SpaceGraph *sipo, ARegion *region, bAn
if (BKE_fcurve_is_protected(fcu)) {
/* Protected curves (non editable) are drawn with dotted lines. */
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
immUniform1i("colors_len", 0); /* Simple dashes. */
immUniform1f("dash_width", 4.0f);
@@ -1190,7 +1190,7 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu)
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -1268,7 +1268,7 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu)
immUnbindProgram();
/* GPU_PRIM_POINTS do not survive dashed line geometry shader... */
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* x marks the spot .................................................... */
/* -> outer frame */
@@ -1310,7 +1310,7 @@ void graph_draw_ghost_curves(bAnimContext *ac, SpaceGraph *sipo, ARegion *region
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index 7e8bca88744..cab491fb8d2 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -723,7 +723,7 @@ static bool delete_graph_keys(bAnimContext *ac)
bool changed;
/* Delete selected keyframes only. */
- changed = delete_fcurve_keys(fcu);
+ changed = BKE_fcurve_delete_keys_selected(fcu);
if (changed) {
ale->update |= ANIM_UPDATE_DEFAULT;
@@ -1448,7 +1448,7 @@ static int graphkeys_expo_exec(bContext *C, wmOperator *op)
void GRAPH_OT_extrapolation_type(wmOperatorType *ot)
{
/* Identifiers */
- ot->name = "Set Keyframe Extrapolation";
+ ot->name = "Set F-Curve Extrapolation";
ot->idname = "GRAPH_OT_extrapolation_type";
ot->description = "Set extrapolation mode for selected F-Curves";
@@ -1488,7 +1488,7 @@ static void setipo_graph_keys(bAnimContext *ac, short mode)
* Currently that's not necessary here.
*/
for (ale = anim_data.first; ale; ale = ale->next) {
- ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES;
}
@@ -1566,7 +1566,7 @@ static void seteasing_graph_keys(bAnimContext *ac, short mode)
* Currently that's not necessary here.
*/
for (ale = anim_data.first; ale; ale = ale->next) {
- ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES;
}
@@ -1649,7 +1649,7 @@ static void sethandles_graph_keys(bAnimContext *ac, short mode)
/* Any selected keyframes for editing? */
if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) {
/* Change type of selected handles. */
- ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, BKE_fcurve_handles_recalc);
ale->update |= ANIM_UPDATE_DEFAULT;
}
@@ -2295,11 +2295,11 @@ static void snap_graph_keys(bAnimContext *ac, short mode)
/* Perform snapping. */
if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
}
else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
}
ale->update |= ANIM_UPDATE_DEFAULT;
@@ -2555,11 +2555,11 @@ static void mirror_graph_keys(bAnimContext *ac, short mode)
/* Perform actual mirroring. */
if (adt) {
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0);
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 0);
}
else {
- ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve);
+ ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, BKE_fcurve_handles_recalc);
}
ale->update |= ANIM_UPDATE_DEFAULT;
@@ -3020,7 +3020,7 @@ static int graph_driver_vars_paste_exec(bContext *C, wmOperator *op)
/* Successful or not? */
if (ok) {
- /* Rebuild depsgraph, now that there are extra deps here. */
+ /* Rebuild depsgraph, now that there are extra dependencies here. */
DEG_relations_tag_update(CTX_data_main(C));
/* Set notifier that keyframes have changed. */
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index a36bd5c1461..932ed417f21 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -585,7 +585,7 @@ static bool box_select_graphkeys(bAnimContext *ac,
initialize_box_select_key_editing_data(
sipo, incl_handles, mode, ac, data, &scaled_rectf, &ked, &mapping_flag);
- /* Get beztriple editing/validation funcs. */
+ /* Get beztriple editing/validation functions. */
const KeyframeEditFunc select_cb = ANIM_editkeyframes_select(selectmode);
const KeyframeEditFunc ok_cb = ANIM_editkeyframes_ok(mode);
@@ -893,7 +893,7 @@ void GRAPH_OT_select_box(wmOperatorType *ot)
ot->poll = graphop_visible_keyframes_poll;
/* Flags. */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO;
/* Properties. */
ot->prop = RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
@@ -1128,7 +1128,7 @@ static const EnumPropertyItem prop_column_select_types[] = {
/* ------------------- */
/* Selects all visible keyframes between the specified markers */
-/* TODO(campbell): this is almost an _exact_ duplicate of a function of the same name in
+/* TODO(@campbellbarton): this is almost an _exact_ duplicate of a function of the same name in
* action_select.c should de-duplicate. */
static void markers_selectkeys_between(bAnimContext *ac)
{
@@ -1145,7 +1145,7 @@ static void markers_selectkeys_between(bAnimContext *ac)
min -= 0.5f;
max += 0.5f;
- /* get editing funcs + data */
+ /* Get editing functions + data. */
ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
select_cb = ANIM_editkeyframes_select(SELECT_ADD);
@@ -1295,6 +1295,7 @@ void GRAPH_OT_select_column(wmOperatorType *ot)
/* props */
ot->prop = RNA_def_enum(ot->srna, "mode", prop_column_select_types, 0, "Mode", "");
+ RNA_def_property_flag(ot->prop, PROP_HIDDEN);
}
/** \} */
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 43621d74e79..1434f204ee5 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -90,7 +90,6 @@ static SpaceLink *graph_create(const ScrArea *UNUSED(area), const Scene *scene)
BLI_addtail(&sipo->regionbase, region);
region->regiontype = RGN_TYPE_UI;
region->alignment = RGN_ALIGN_RIGHT;
- region->flag = RGN_FLAG_HIDDEN;
/* main region */
region = MEM_callocN(sizeof(ARegion), "main region for graphedit");
@@ -225,7 +224,7 @@ static void graph_main_region_draw(const bContext *C, ARegion *region)
if (((sipo->flag & SIPO_NODRAWCURSOR) == 0)) {
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* horizontal component of value-cursor (value line before the current frame line) */
float y = sipo->cursorVal;
@@ -394,7 +393,7 @@ static void graph_buttons_region_draw(const bContext *C, ARegion *region)
static void graph_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -530,7 +529,7 @@ static void graph_region_message_subscribe(const wmRegionMessageSubscribeParams
static void graph_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
SpaceGraph *sipo = (SpaceGraph *)area->spacedata.first;
/* context changes */
@@ -626,7 +625,7 @@ static void graph_refresh_fcurve_colors(const bContext *C)
* - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a
* mismatch between channel-colors and the drawn curves
*/
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS | ANIMFILTER_FCURVESONLY);
items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
/* loop over F-Curves, assigning colors */
@@ -811,7 +810,7 @@ void ED_spacetype_ipo(void)
ARegionType *art;
st->spaceid = SPACE_GRAPH;
- strncpy(st->name, "Graph", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Graph");
st->create = graph_create;
st->free = graph_free;
diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt
index 39fb41245bf..4284d0f76af 100644
--- a/source/blender/editors/space_image/CMakeLists.txt
+++ b/source/blender/editors/space_image/CMakeLists.txt
@@ -16,7 +16,6 @@ set(INC
../../render
../../windowmanager
../../../../intern/clog
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@@ -28,7 +27,7 @@ set(SRC
image_edit.c
image_ops.c
image_sequence.c
- image_undo.c
+ image_undo.cc
space_image.c
image_intern.h
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 0a774ee679c..bc367a99d6b 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -869,7 +869,8 @@ void uiTemplateImage(uiLayout *layout,
uiItemS(col);
uiItemR(col, &imaptr, "generated_type", UI_ITEM_R_EXPAND, IFACE_("Type"), ICON_NONE);
- if (ima->gen_type == IMA_GENTYPE_BLANK) {
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ if (base_tile->gen_type == IMA_GENTYPE_BLANK) {
uiItemR(col, &imaptr, "generated_color", 0, NULL, ICON_NONE);
}
}
@@ -921,7 +922,7 @@ void uiTemplateImage(uiLayout *layout,
}
}
- /* Colorspace and alpha */
+ /* Color-space and alpha. */
{
uiItemS(layout);
@@ -1211,6 +1212,11 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i
ofs += BLI_strncpy_rlen(str + ofs, TIP_(" + Z"), len - ofs);
}
+ eGPUTextureFormat texture_format = IMB_gpu_get_texture_format(
+ ibuf, ima->flag & IMA_HIGH_BITDEPTH, ibuf->planes >= 8);
+ const char *texture_format_description = GPU_texture_format_description(texture_format);
+ ofs += BLI_snprintf_rlen(str + ofs, len - ofs, TIP_(", %s"), texture_format_description);
+
uiItemL(col, str, ICON_NONE);
}
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index f6f9428213f..8a934396229 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -98,7 +98,7 @@ static void draw_render_info(
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_FACE_SELECT);
GPU_line_width(1.0f);
@@ -158,7 +158,7 @@ void ED_image_draw_info(Scene *scene,
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* noisy, high contrast make impossible to read if lower alpha is used. */
immUniformColor4ub(0, 0, 0, 190);
@@ -338,7 +338,7 @@ void ED_image_draw_info(Scene *scene,
/* BLF uses immediate mode too, so we must reset our vertex format */
pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
if (channels == 4) {
rcti color_rect_half;
@@ -381,7 +381,7 @@ void ED_image_draw_info(Scene *scene,
/* draw outline */
pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ub(128, 128, 128);
imm_draw_box_wire_2d(pos, color_rect.xmin, color_rect.ymin, color_rect.xmax, color_rect.ymax);
immUnbindProgram();
@@ -448,7 +448,7 @@ void draw_image_sample_line(SpaceImage *sima)
uint shdr_dashed_pos = GPU_vertformat_attr_add(
format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -557,7 +557,7 @@ void draw_image_cache(const bContext *C, ARegion *region)
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_CFRAME);
immRecti(pos, x, region_bottom, x + ceilf(framelen), region_bottom + 8 * UI_DPI_FAC);
immUnbindProgram();
@@ -585,34 +585,35 @@ float ED_space_image_zoom_level(const View2D *v2d, const int grid_dimension)
}
void ED_space_image_grid_steps(SpaceImage *sima,
- float grid_steps[SI_GRID_STEPS_LEN],
+ float grid_steps_x[SI_GRID_STEPS_LEN],
+ float grid_steps_y[SI_GRID_STEPS_LEN],
const int grid_dimension)
{
- if (sima->flag & SI_CUSTOM_GRID) {
- for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
- grid_steps[step] = powf(1, step) * (1.0f / ((float)sima->custom_grid_subdiv));
+ const int flag = sima->flag;
+ for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
+ if (flag & SI_CUSTOM_GRID) {
+ grid_steps_x[step] = 1.0f / sima->custom_grid_subdiv[0];
+ grid_steps_y[step] = 1.0f / sima->custom_grid_subdiv[1];
}
- }
- else {
- for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
- grid_steps[step] = powf(grid_dimension, step) *
- (1.0f / (powf(grid_dimension, SI_GRID_STEPS_LEN)));
+ else {
+ grid_steps_x[step] = powf(grid_dimension, step - SI_GRID_STEPS_LEN);
+ grid_steps_y[step] = powf(grid_dimension, step - SI_GRID_STEPS_LEN);
}
}
}
-float ED_space_image_increment_snap_value(const int grid_dimesnions,
+float ED_space_image_increment_snap_value(const int grid_dimensions,
const float grid_steps[SI_GRID_STEPS_LEN],
const float zoom_factor)
{
/* Small offset on each grid_steps[] so that snapping value doesn't change until grid lines are
* significantly visible.
- * `Offset = 3/4 * (grid_steps[i] - (grid_steps[i] / grid_dimesnsions))`
+ * `Offset = 3/4 * (grid_steps[i] - (grid_steps[i] / grid_dimensions))`
*
* Refer `grid_frag.glsl` to find out when grid lines actually start appearing */
for (int step = 0; step < SI_GRID_STEPS_LEN; step++) {
- float offset = (3.0f / 4.0f) * (grid_steps[step] - (grid_steps[step] / grid_dimesnions));
+ float offset = (3.0f / 4.0f) * (grid_steps[step] - (grid_steps[step] / grid_dimensions));
if ((grid_steps[step] - offset) > zoom_factor) {
return grid_steps[step];
diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c
index 950acd77f6a..fa0fdb01bdf 100644
--- a/source/blender/editors/space_image/image_edit.c
+++ b/source/blender/editors/space_image/image_edit.c
@@ -18,8 +18,10 @@
#include "BKE_editmesh.h"
#include "BKE_global.h"
#include "BKE_image.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_scene.h"
#include "IMB_imbuf_types.h"
@@ -212,13 +214,7 @@ void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
}
else if (sima->image && sima->image->type == IMA_TYPE_R_RESULT && scene) {
/* not very important, just nice */
- *r_width = (scene->r.xsch * scene->r.size) / 100;
- *r_height = (scene->r.ysch * scene->r.size) / 100;
-
- if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) {
- *r_width *= BLI_rctf_size_x(&scene->r.border);
- *r_height *= BLI_rctf_size_y(&scene->r.border);
- }
+ BKE_render_resolution(&scene->r, true, r_width, r_height);
}
/* I know a bit weak... but preview uses not actual image size */
// XXX else if (image_preview_active(sima, r_width, r_height));
@@ -475,8 +471,10 @@ bool ED_space_image_maskedit_poll(bContext *C)
SpaceImage *sima = CTX_wm_space_image(C);
if (sima) {
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
return ED_space_image_check_show_maskedit(sima, obedit);
}
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 49489b696ef..dec4055c737 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -45,6 +45,7 @@
#include "BKE_main.h"
#include "BKE_packedFile.h"
#include "BKE_report.h"
+#include "BKE_scene.h"
#include "DEG_depsgraph.h"
@@ -940,7 +941,7 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op))
if (ED_space_image_show_uvedit(sima, obedit)) {
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
bool success = ED_uvedit_minmax_multi(scene, objects, objects_len, min, max);
MEM_freeN(objects);
if (!success) {
@@ -1830,7 +1831,7 @@ static void image_save_options_from_op(Main *bmain, ImageSaveOptions *opts, wmOp
}
static bool save_image_op(
- Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, ImageSaveOptions *opts)
+ Main *bmain, Image *ima, ImageUser *iuser, wmOperator *op, const ImageSaveOptions *opts)
{
WM_cursor_wait(true);
@@ -3732,38 +3733,52 @@ static int render_border_exec(bContext *C, wmOperator *op)
ARegion *region = CTX_wm_region(C);
Scene *scene = CTX_data_scene(C);
Render *re = RE_GetSceneRender(scene);
- RenderData *rd;
- rctf border;
+ SpaceImage *sima = CTX_wm_space_image(C);
if (re == NULL) {
/* Shouldn't happen, but better be safe close to the release. */
return OPERATOR_CANCELLED;
}
- rd = RE_engine_get_render_data(re);
- if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) {
- BKE_report(op->reports, RPT_INFO, "Can not set border from a cropped render");
- return OPERATOR_CANCELLED;
- }
+ /* Get information about the previous render, or current scene if no render yet. */
+ int width, height;
+ BKE_render_resolution(&scene->r, false, &width, &height);
+ const RenderData *rd = ED_space_image_has_buffer(sima) ? RE_engine_get_render_data(re) :
+ &scene->r;
- /* get rectangle from operator */
+ /* Get rectangle from the operator. */
+ rctf border;
WM_operator_properties_border_to_rctf(op, &border);
UI_view2d_region_to_view_rctf(&region->v2d, &border, &border);
- /* actually set border */
+ /* Adjust for cropping. */
+ if ((rd->mode & (R_BORDER | R_CROP)) == (R_BORDER | R_CROP)) {
+ border.xmin = rd->border.xmin + border.xmin * (rd->border.xmax - rd->border.xmin);
+ border.xmax = rd->border.xmin + border.xmax * (rd->border.xmax - rd->border.xmin);
+ border.ymin = rd->border.ymin + border.ymin * (rd->border.ymax - rd->border.ymin);
+ border.ymax = rd->border.ymin + border.ymax * (rd->border.ymax - rd->border.ymin);
+ }
+
CLAMP(border.xmin, 0.0f, 1.0f);
CLAMP(border.ymin, 0.0f, 1.0f);
CLAMP(border.xmax, 0.0f, 1.0f);
CLAMP(border.ymax, 0.0f, 1.0f);
- scene->r.border = border;
- /* drawing a border surrounding the entire camera view switches off border rendering
- * or the border covers no pixels */
+ /* Drawing a border surrounding the entire camera view switches off border rendering
+ * or the border covers no pixels. */
if ((border.xmin <= 0.0f && border.xmax >= 1.0f && border.ymin <= 0.0f && border.ymax >= 1.0f) ||
(border.xmin == border.xmax || border.ymin == border.ymax)) {
scene->r.mode &= ~R_BORDER;
}
else {
+ /* Snap border to pixel boundaries, so drawing a border within a pixel selects that pixel. */
+ border.xmin = floorf(border.xmin * width) / width;
+ border.xmax = ceilf(border.xmax * width) / width;
+ border.ymin = floorf(border.ymin * height) / height;
+ border.ymax = ceilf(border.ymax * height) / height;
+
+ /* Set border. */
+ scene->r.border = border;
scene->r.mode |= R_BORDER;
}
@@ -3832,15 +3847,16 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot)
static bool do_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile)
{
- float color[4];
- RNA_float_get_array(ptr, "color", color);
- int gen_type = RNA_enum_get(ptr, "generated_type");
- int width = RNA_int_get(ptr, "width");
- int height = RNA_int_get(ptr, "height");
+ RNA_float_get_array(ptr, "color", tile->gen_color);
+ tile->gen_type = RNA_enum_get(ptr, "generated_type");
+ tile->gen_x = RNA_int_get(ptr, "width");
+ tile->gen_y = RNA_int_get(ptr, "height");
bool is_float = RNA_boolean_get(ptr, "float");
- int planes = RNA_boolean_get(ptr, "alpha") ? 32 : 24;
- return BKE_image_fill_tile(ima, tile, width, height, color, gen_type, planes, is_float);
+ tile->gen_flag = is_float ? IMA_GEN_FLOAT : 0;
+ tile->gen_depth = RNA_boolean_get(ptr, "alpha") ? 32 : 24;
+
+ return BKE_image_fill_tile(ima, tile);
}
static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout)
diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.cc
index a7a8bde1115..8f144264824 100644
--- a/source/blender/editors/space_image/image_undo.c
+++ b/source/blender/editors/space_image/image_undo.cc
@@ -22,6 +22,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_map.hh"
#include "BLI_math.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
@@ -82,14 +83,31 @@ void ED_image_paint_tile_lock_end(void)
*
* \{ */
-static ImBuf *imbuf_alloc_temp_tile(void)
+static ImBuf *imbuf_alloc_temp_tile()
{
return IMB_allocImBuf(
ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE, 32, IB_rectfloat | IB_rect);
}
-typedef struct PaintTile {
- struct PaintTile *next, *prev;
+struct PaintTileKey {
+ int x_tile, y_tile;
+ Image *image;
+ ImBuf *ibuf;
+ /* Copied from iuser.tile in PaintTile. */
+ int iuser_tile;
+
+ uint64_t hash() const
+ {
+ return blender::get_default_hash_4(x_tile, y_tile, image, ibuf);
+ }
+ bool operator==(const PaintTileKey &other) const
+ {
+ return x_tile == other.x_tile && y_tile == other.y_tile && image == other.image &&
+ ibuf == other.ibuf && iuser_tile == other.iuser_tile;
+ }
+};
+
+struct PaintTile {
Image *image;
ImBuf *ibuf;
/* For 2D image painting the ImageUser uses most of the values.
@@ -99,14 +117,14 @@ typedef struct PaintTile {
ImageUser iuser;
union {
float *fp;
- uint *uint;
+ uint32_t *uint;
void *pt;
} rect;
- ushort *mask;
+ uint16_t *mask;
bool valid;
bool use_float;
int x_tile, y_tile;
-} PaintTile;
+};
static void ptile_free(PaintTile *ptile)
{
@@ -119,23 +137,25 @@ static void ptile_free(PaintTile *ptile)
MEM_freeN(ptile);
}
-static void ptile_free_list(ListBase *paint_tiles)
-{
- for (PaintTile *ptile = paint_tiles->first, *ptile_next; ptile; ptile = ptile_next) {
- ptile_next = ptile->next;
- ptile_free(ptile);
+struct PaintTileMap {
+ blender::Map<PaintTileKey, PaintTile *> map;
+
+ ~PaintTileMap()
+ {
+ for (PaintTile *ptile : map.values()) {
+ ptile_free(ptile);
+ }
}
- BLI_listbase_clear(paint_tiles);
-}
+};
-static void ptile_invalidate_list(ListBase *paint_tiles)
+static void ptile_invalidate_map(PaintTileMap *paint_tile_map)
{
- LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) {
+ for (PaintTile *ptile : paint_tile_map->map.values()) {
ptile->valid = false;
}
}
-void *ED_image_paint_tile_find(ListBase *paint_tiles,
+void *ED_image_paint_tile_find(PaintTileMap *paint_tile_map,
Image *image,
ImBuf *ibuf,
ImageUser *iuser,
@@ -144,28 +164,32 @@ void *ED_image_paint_tile_find(ListBase *paint_tiles,
ushort **r_mask,
bool validate)
{
- LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) {
- if (ptile->x_tile == x_tile && ptile->y_tile == y_tile) {
- if (ptile->image == image && ptile->ibuf == ibuf && ptile->iuser.tile == iuser->tile) {
- if (r_mask) {
- /* allocate mask if requested. */
- if (!ptile->mask) {
- ptile->mask = MEM_callocN(sizeof(ushort) * square_i(ED_IMAGE_UNDO_TILE_SIZE),
- "UndoImageTile.mask");
- }
- *r_mask = ptile->mask;
- }
- if (validate) {
- ptile->valid = true;
- }
- return ptile->rect.pt;
- }
+ PaintTileKey key;
+ key.ibuf = ibuf;
+ key.image = image;
+ key.iuser_tile = iuser->tile;
+ key.x_tile = x_tile;
+ key.y_tile = y_tile;
+ PaintTile **pptile = paint_tile_map->map.lookup_ptr(key);
+ if (pptile == nullptr) {
+ return nullptr;
+ }
+ PaintTile *ptile = *pptile;
+ if (r_mask) {
+ /* allocate mask if requested. */
+ if (!ptile->mask) {
+ ptile->mask = static_cast<uint16_t *>(
+ MEM_callocN(sizeof(uint16_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), "UndoImageTile.mask"));
}
+ *r_mask = ptile->mask;
}
- return NULL;
+ if (validate) {
+ ptile->valid = true;
+ }
+ return ptile->rect.pt;
}
-void *ED_image_paint_tile_push(ListBase *paint_tiles,
+void *ED_image_paint_tile_push(PaintTileMap *paint_tile_map,
Image *image,
ImBuf *ibuf,
ImBuf **tmpibuf,
@@ -177,37 +201,43 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
bool use_thread_lock,
bool find_prev)
{
- const bool has_float = (ibuf->rect_float != NULL);
+ if (use_thread_lock) {
+ BLI_spin_lock(&paint_tiles_lock);
+ }
+ const bool has_float = (ibuf->rect_float != nullptr);
/* check if tile is already pushed */
/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
if (find_prev) {
void *data = ED_image_paint_tile_find(
- paint_tiles, image, ibuf, iuser, x_tile, y_tile, r_mask, true);
+ paint_tile_map, image, ibuf, iuser, x_tile, y_tile, r_mask, true);
if (data) {
+ if (use_thread_lock) {
+ BLI_spin_unlock(&paint_tiles_lock);
+ }
return data;
}
}
- if (*tmpibuf == NULL) {
+ if (*tmpibuf == nullptr) {
*tmpibuf = imbuf_alloc_temp_tile();
}
- PaintTile *ptile = MEM_callocN(sizeof(PaintTile), "PaintTile");
+ PaintTile *ptile = static_cast<PaintTile *>(MEM_callocN(sizeof(PaintTile), "PaintTile"));
ptile->image = image;
ptile->ibuf = ibuf;
ptile->iuser = *iuser;
- ptile->iuser.scene = NULL;
+ ptile->iuser.scene = nullptr;
ptile->x_tile = x_tile;
ptile->y_tile = y_tile;
/* add mask explicitly here */
if (r_mask) {
- *r_mask = ptile->mask = MEM_callocN(sizeof(ushort) * square_i(ED_IMAGE_UNDO_TILE_SIZE),
- "PaintTile.mask");
+ *r_mask = ptile->mask = static_cast<uint16_t *>(
+ MEM_callocN(sizeof(uint16_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), "PaintTile.mask"));
}
ptile->rect.pt = MEM_callocN((ibuf->rect_float ? sizeof(float[4]) : sizeof(char[4])) *
@@ -234,13 +264,24 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
SWAP(float *, ptile->rect.fp, (*tmpibuf)->rect_float);
}
else {
- SWAP(uint *, ptile->rect.uint, (*tmpibuf)->rect);
+ SWAP(uint32_t *, ptile->rect.uint, (*tmpibuf)->rect);
}
- if (use_thread_lock) {
- BLI_spin_lock(&paint_tiles_lock);
+ PaintTileKey key = {};
+ key.ibuf = ibuf;
+ key.image = image;
+ key.iuser_tile = iuser->tile;
+ key.x_tile = x_tile;
+ key.y_tile = y_tile;
+ PaintTile *existing_tile = nullptr;
+ paint_tile_map->map.add_or_modify(
+ key,
+ [&](PaintTile **pptile) { *pptile = ptile; },
+ [&](PaintTile **pptile) { existing_tile = *pptile; });
+ if (existing_tile) {
+ ptile_free(ptile);
+ ptile = existing_tile;
}
- BLI_addtail(paint_tiles, ptile);
if (use_thread_lock) {
BLI_spin_unlock(&paint_tiles_lock);
@@ -248,20 +289,20 @@ void *ED_image_paint_tile_push(ListBase *paint_tiles,
return ptile->rect.pt;
}
-static void ptile_restore_runtime_list(ListBase *paint_tiles)
+static void ptile_restore_runtime_map(PaintTileMap *paint_tile_map)
{
ImBuf *tmpibuf = imbuf_alloc_temp_tile();
- LISTBASE_FOREACH (PaintTile *, ptile, paint_tiles) {
+ for (PaintTile *ptile : paint_tile_map->map.values()) {
Image *image = ptile->image;
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, &ptile->iuser, NULL);
- const bool has_float = (ibuf->rect_float != NULL);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, &ptile->iuser, nullptr);
+ const bool has_float = (ibuf->rect_float != nullptr);
if (has_float) {
SWAP(float *, ptile->rect.fp, tmpibuf->rect_float);
}
else {
- SWAP(uint *, ptile->rect.uint, tmpibuf->rect);
+ SWAP(uint32_t *, ptile->rect.uint, tmpibuf->rect);
}
IMB_rectcpy(ibuf,
@@ -277,7 +318,7 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
SWAP(float *, ptile->rect.fp, tmpibuf->rect_float);
}
else {
- SWAP(uint *, ptile->rect.uint, tmpibuf->rect);
+ SWAP(uint32_t *, ptile->rect.uint, tmpibuf->rect);
}
/* Force OpenGL reload (maybe partial update will operate better?) */
@@ -291,7 +332,7 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
- BKE_image_release_ibuf(image, ibuf, NULL);
+ BKE_image_release_ibuf(image, ibuf, nullptr);
}
IMB_freeImBuf(tmpibuf);
@@ -303,35 +344,38 @@ static void ptile_restore_runtime_list(ListBase *paint_tiles)
/** \name Image Undo Tile
* \{ */
-static uint index_from_xy(uint tile_x, uint tile_y, const uint tiles_dims[2])
+static uint32_t index_from_xy(uint32_t tile_x, uint32_t tile_y, const uint32_t tiles_dims[2])
{
BLI_assert(tile_x < tiles_dims[0] && tile_y < tiles_dims[1]);
return (tile_y * tiles_dims[0]) + tile_x;
}
-typedef struct UndoImageTile {
+struct UndoImageTile {
union {
float *fp;
- uint *uint;
+ uint32_t *uint_ptr;
void *pt;
} rect;
int users;
-} UndoImageTile;
+};
static UndoImageTile *utile_alloc(bool has_float)
{
- UndoImageTile *utile = MEM_callocN(sizeof(*utile), "ImageUndoTile");
+ UndoImageTile *utile = static_cast<UndoImageTile *>(
+ MEM_callocN(sizeof(*utile), "ImageUndoTile"));
if (has_float) {
- utile->rect.fp = MEM_mallocN(sizeof(float[4]) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__);
+ utile->rect.fp = static_cast<float *>(
+ MEM_mallocN(sizeof(float[4]) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__));
}
else {
- utile->rect.uint = MEM_mallocN(sizeof(uint) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__);
+ utile->rect.uint_ptr = static_cast<uint32_t *>(
+ MEM_mallocN(sizeof(uint32_t) * square_i(ED_IMAGE_UNDO_TILE_SIZE), __func__));
}
return utile;
}
static void utile_init_from_imbuf(
- UndoImageTile *utile, const uint x, const uint y, const ImBuf *ibuf, ImBuf *tmpibuf)
+ UndoImageTile *utile, const uint32_t x, const uint32_t y, const ImBuf *ibuf, ImBuf *tmpibuf)
{
const bool has_float = ibuf->rect_float;
@@ -339,7 +383,7 @@ static void utile_init_from_imbuf(
SWAP(float *, utile->rect.fp, tmpibuf->rect_float);
}
else {
- SWAP(uint *, utile->rect.uint, tmpibuf->rect);
+ SWAP(uint32_t *, utile->rect.uint_ptr, tmpibuf->rect);
}
IMB_rectcpy(tmpibuf, ibuf, 0, 0, x, y, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE);
@@ -348,7 +392,7 @@ static void utile_init_from_imbuf(
SWAP(float *, utile->rect.fp, tmpibuf->rect_float);
}
else {
- SWAP(uint *, utile->rect.uint, tmpibuf->rect);
+ SWAP(uint32_t *, utile->rect.uint_ptr, tmpibuf->rect);
}
}
@@ -357,13 +401,13 @@ static void utile_restore(
{
const bool has_float = ibuf->rect_float;
float *prev_rect_float = tmpibuf->rect_float;
- uint *prev_rect = tmpibuf->rect;
+ uint32_t *prev_rect = tmpibuf->rect;
if (has_float) {
tmpibuf->rect_float = utile->rect.fp;
}
else {
- tmpibuf->rect = utile->rect.uint;
+ tmpibuf->rect = utile->rect.uint_ptr;
}
IMB_rectcpy(ibuf, tmpibuf, x, y, 0, 0, ED_IMAGE_UNDO_TILE_SIZE, ED_IMAGE_UNDO_TILE_SIZE);
@@ -378,7 +422,7 @@ static void utile_decref(UndoImageTile *utile)
BLI_assert(utile->users >= 0);
if (utile->users == 0) {
MEM_freeN(utile->rect.pt);
- MEM_freeN(utile);
+ MEM_delete(utile);
}
}
@@ -388,7 +432,7 @@ static void utile_decref(UndoImageTile *utile)
/** \name Image Undo Buffer
* \{ */
-typedef struct UndoImageBuf {
+struct UndoImageBuf {
struct UndoImageBuf *next, *prev;
/**
@@ -401,23 +445,21 @@ typedef struct UndoImageBuf {
UndoImageTile **tiles;
/** Can calculate these from dims, just for convenience. */
- uint tiles_len;
- uint tiles_dims[2];
+ uint32_t tiles_len;
+ uint32_t tiles_dims[2];
- uint image_dims[2];
+ uint32_t image_dims[2];
/** Store variables from the image. */
struct {
short source;
bool use_float;
- char gen_type;
} image_state;
-
-} UndoImageBuf;
+};
static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf)
{
- UndoImageBuf *ubuf = MEM_callocN(sizeof(*ubuf), __func__);
+ UndoImageBuf *ubuf = static_cast<UndoImageBuf *>(MEM_callocN(sizeof(*ubuf), __func__));
ubuf->image_dims[0] = ibuf->x;
ubuf->image_dims[1] = ibuf->y;
@@ -426,12 +468,12 @@ static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf)
ubuf->tiles_dims[1] = ED_IMAGE_UNDO_TILE_NUMBER(ubuf->image_dims[1]);
ubuf->tiles_len = ubuf->tiles_dims[0] * ubuf->tiles_dims[1];
- ubuf->tiles = MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__);
+ ubuf->tiles = static_cast<UndoImageTile **>(
+ MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__));
BLI_strncpy(ubuf->ibuf_name, ibuf->name, sizeof(ubuf->ibuf_name));
- ubuf->image_state.gen_type = image->gen_type;
ubuf->image_state.source = image->source;
- ubuf->image_state.use_float = ibuf->rect_float != NULL;
+ ubuf->image_state.use_float = ibuf->rect_float != nullptr;
return ubuf;
}
@@ -447,7 +489,7 @@ static void ubuf_from_image_all_tiles(UndoImageBuf *ubuf, const ImBuf *ibuf)
for (uint x_tile = 0; x_tile < ubuf->tiles_dims[0]; x_tile += 1) {
uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS;
- BLI_assert(ubuf->tiles[i] == NULL);
+ BLI_assert(ubuf->tiles[i] == nullptr);
UndoImageTile *utile = utile_alloc(has_float);
utile->users = 1;
utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf);
@@ -467,7 +509,7 @@ static void ubuf_ensure_compat_ibuf(const UndoImageBuf *ubuf, ImBuf *ibuf)
{
/* We could have both float and rect buffers,
* in this case free the float buffer if it's unused. */
- if ((ibuf->rect_float != NULL) && (ubuf->image_state.use_float == false)) {
+ if ((ibuf->rect_float != nullptr) && (ubuf->image_state.use_float == false)) {
imb_freerectfloatImBuf(ibuf);
}
@@ -480,7 +522,7 @@ static void ubuf_ensure_compat_ibuf(const UndoImageBuf *ubuf, ImBuf *ibuf)
IMB_rect_size_set(ibuf, ubuf->image_dims);
if (ubuf->image_state.use_float) {
- imb_addrectfloatImBuf(ibuf);
+ imb_addrectfloatImBuf(ibuf, 4);
}
else {
imb_addrectImBuf(ibuf);
@@ -507,7 +549,7 @@ static void ubuf_free(UndoImageBuf *ubuf)
/** \name Image Undo Handle
* \{ */
-typedef struct UndoImageHandle {
+struct UndoImageHandle {
struct UndoImageHandle *next, *prev;
/** Each undo handle refers to a single image which may have multiple buffers. */
@@ -522,8 +564,7 @@ typedef struct UndoImageHandle {
* List of #UndoImageBuf's to support multiple buffers per image.
*/
ListBase buffers;
-
-} UndoImageHandle;
+};
static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
{
@@ -533,8 +574,8 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
/* Tiles only added to second set of tiles. */
Image *image = uh->image_ref.ptr;
- ImBuf *ibuf = BKE_image_acquire_ibuf(image, &uh->iuser, NULL);
- if (UNLIKELY(ibuf == NULL)) {
+ ImBuf *ibuf = BKE_image_acquire_ibuf(image, &uh->iuser, nullptr);
+ if (UNLIKELY(ibuf == nullptr)) {
CLOG_ERROR(&LOG, "Unable to get buffer for image '%s'", image->id.name + 2);
continue;
}
@@ -557,20 +598,20 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init)
if (changed) {
BKE_image_mark_dirty(image, ibuf);
- /* TODO(jbakker): only mark areas that are actually updated to improve performance. */
+ /* TODO(@jbakker): only mark areas that are actually updated to improve performance. */
BKE_image_partial_update_mark_full_update(image);
if (ibuf->rect_float) {
- ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
+ ibuf->userflags |= IB_RECT_INVALID; /* Force recreate of char `rect` */
}
if (ibuf->mipmap[0]) {
- ibuf->userflags |= IB_MIPMAP_INVALID; /* force mip-map recreation. */
+ ibuf->userflags |= IB_MIPMAP_INVALID; /* Force MIP-MAP recreation. */
}
ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
DEG_id_tag_update(&image->id, 0);
}
- BKE_image_release_ibuf(image, ibuf, NULL);
+ BKE_image_release_ibuf(image, ibuf, nullptr);
}
IMB_freeImBuf(tmpibuf);
@@ -604,16 +645,16 @@ static UndoImageBuf *uhandle_lookup_ubuf(UndoImageHandle *uh,
return ubuf;
}
}
- return NULL;
+ return nullptr;
}
static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf)
{
- BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->name) == NULL);
+ BLI_assert(uhandle_lookup_ubuf(uh, image, ibuf->name) == nullptr);
UndoImageBuf *ubuf = ubuf_from_image_no_tiles(image, ibuf);
BLI_addtail(&uh->buffers, ubuf);
- ubuf->post = NULL;
+ ubuf->post = nullptr;
return ubuf;
}
@@ -621,7 +662,7 @@ static UndoImageBuf *uhandle_add_ubuf(UndoImageHandle *uh, Image *image, ImBuf *
static UndoImageBuf *uhandle_ensure_ubuf(UndoImageHandle *uh, Image *image, ImBuf *ibuf)
{
UndoImageBuf *ubuf = uhandle_lookup_ubuf(uh, image, ibuf->name);
- if (ubuf == NULL) {
+ if (ubuf == nullptr) {
ubuf = uhandle_add_ubuf(uh, image, ibuf);
}
return ubuf;
@@ -636,7 +677,7 @@ static UndoImageHandle *uhandle_lookup_by_name(ListBase *undo_handles,
return uh;
}
}
- return NULL;
+ return nullptr;
}
static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *image, int tile_number)
@@ -646,16 +687,16 @@ static UndoImageHandle *uhandle_lookup(ListBase *undo_handles, const Image *imag
return uh;
}
}
- return NULL;
+ return nullptr;
}
static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageUser *iuser)
{
- BLI_assert(uhandle_lookup(undo_handles, image, iuser->tile) == NULL);
- UndoImageHandle *uh = MEM_callocN(sizeof(*uh), __func__);
+ BLI_assert(uhandle_lookup(undo_handles, image, iuser->tile) == nullptr);
+ UndoImageHandle *uh = static_cast<UndoImageHandle *>(MEM_callocN(sizeof(*uh), __func__));
uh->image_ref.ptr = image;
uh->iuser = *iuser;
- uh->iuser.scene = NULL;
+ uh->iuser.scene = nullptr;
BLI_addtail(undo_handles, uh);
return uh;
}
@@ -663,7 +704,7 @@ static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageU
static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image, ImageUser *iuser)
{
UndoImageHandle *uh = uhandle_lookup(undo_handles, image, iuser->tile);
- if (uh == NULL) {
+ if (uh == nullptr) {
uh = uhandle_add(undo_handles, image, iuser);
}
return uh;
@@ -675,7 +716,7 @@ static UndoImageHandle *uhandle_ensure(ListBase *undo_handles, Image *image, Ima
/** \name Implements ED Undo System
* \{ */
-typedef struct ImageUndoStep {
+struct ImageUndoStep {
UndoStep step;
/** #UndoImageHandle */
@@ -685,12 +726,11 @@ typedef struct ImageUndoStep {
* #PaintTile
* Run-time only data (active during a paint stroke).
*/
- ListBase paint_tiles;
+ PaintTileMap *paint_tile_map;
bool is_encode_init;
ePaintMode paint_mode;
-
-} ImageUndoStep;
+};
/**
* Find the previous undo buffer from this one.
@@ -703,7 +743,7 @@ static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev,
{
/* Use name lookup because the pointer is cleared for previous steps. */
UndoImageHandle *uh_prev = uhandle_lookup_by_name(&us_prev->handles, image, tile_number);
- if (uh_prev != NULL) {
+ if (uh_prev != nullptr) {
UndoImageBuf *ubuf_reference = uhandle_lookup_ubuf(uh_prev, image, ubuf->ibuf_name);
if (ubuf_reference) {
ubuf_reference = ubuf_reference->post;
@@ -713,7 +753,7 @@ static UndoImageBuf *ubuf_lookup_from_reference(ImageUndoStep *us_prev,
}
}
}
- return NULL;
+ return nullptr;
}
static bool image_undosys_poll(bContext *C)
@@ -737,11 +777,11 @@ static bool image_undosys_poll(bContext *C)
static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
{
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
/* dummy, memory is cleared anyway. */
us->is_encode_init = true;
BLI_listbase_clear(&us->handles);
- BLI_listbase_clear(&us->paint_tiles);
+ us->paint_tile_map = MEM_new<PaintTileMap>(__func__);
}
static bool image_undosys_step_encode(struct bContext *C,
@@ -753,7 +793,7 @@ static bool image_undosys_step_encode(struct bContext *C,
*
* This function ensures there are previous and current states of the image in the undo buffer.
*/
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
BLI_assert(us->step.data_size == 0);
@@ -761,39 +801,40 @@ static bool image_undosys_step_encode(struct bContext *C,
ImBuf *tmpibuf = imbuf_alloc_temp_tile();
- ImageUndoStep *us_reference = (ImageUndoStep *)ED_undo_stack_get()->step_active;
+ ImageUndoStep *us_reference = reinterpret_cast<ImageUndoStep *>(
+ ED_undo_stack_get()->step_active);
while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) {
- us_reference = (ImageUndoStep *)us_reference->step.prev;
+ us_reference = reinterpret_cast<ImageUndoStep *>(us_reference->step.prev);
}
/* Initialize undo tiles from ptiles (if they exist). */
- for (PaintTile *ptile = us->paint_tiles.first, *ptile_next; ptile; ptile = ptile_next) {
+ for (PaintTile *ptile : us->paint_tile_map->map.values()) {
if (ptile->valid) {
UndoImageHandle *uh = uhandle_ensure(&us->handles, ptile->image, &ptile->iuser);
UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, ptile->image, ptile->ibuf);
- UndoImageTile *utile = MEM_callocN(sizeof(*utile), "UndoImageTile");
+ UndoImageTile *utile = static_cast<UndoImageTile *>(
+ MEM_callocN(sizeof(*utile), "UndoImageTile"));
utile->users = 1;
utile->rect.pt = ptile->rect.pt;
- ptile->rect.pt = NULL;
+ ptile->rect.pt = nullptr;
const uint tile_index = index_from_xy(ptile->x_tile, ptile->y_tile, ubuf_pre->tiles_dims);
- BLI_assert(ubuf_pre->tiles[tile_index] == NULL);
+ BLI_assert(ubuf_pre->tiles[tile_index] == nullptr);
ubuf_pre->tiles[tile_index] = utile;
}
- ptile_next = ptile->next;
ptile_free(ptile);
}
- BLI_listbase_clear(&us->paint_tiles);
+ us->paint_tile_map->map.clear();
LISTBASE_FOREACH (UndoImageHandle *, uh, &us->handles) {
LISTBASE_FOREACH (UndoImageBuf *, ubuf_pre, &uh->buffers) {
- ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, &uh->iuser, NULL);
+ ImBuf *ibuf = BKE_image_acquire_ibuf(uh->image_ref.ptr, &uh->iuser, nullptr);
const bool has_float = ibuf->rect_float;
- BLI_assert(ubuf_pre->post == NULL);
+ BLI_assert(ubuf_pre->post == nullptr);
ubuf_pre->post = ubuf_from_image_no_tiles(uh->image_ref.ptr, ibuf);
UndoImageBuf *ubuf_post = ubuf_pre->post;
@@ -806,7 +847,7 @@ static bool image_undosys_step_encode(struct bContext *C,
UndoImageBuf *ubuf_reference =
(us_reference ? ubuf_lookup_from_reference(
us_reference, uh->image_ref.ptr, uh->iuser.tile, ubuf_post) :
- NULL);
+ nullptr);
int i = 0;
for (uint y_tile = 0; y_tile < ubuf_pre->tiles_dims[1]; y_tile += 1) {
@@ -814,34 +855,35 @@ static bool image_undosys_step_encode(struct bContext *C,
for (uint x_tile = 0; x_tile < ubuf_pre->tiles_dims[0]; x_tile += 1) {
uint x = x_tile << ED_IMAGE_UNDO_TILE_BITS;
- if ((ubuf_reference != NULL) && ((ubuf_pre->tiles[i] == NULL) ||
- /* In this case the paint stroke as has added a tile
- * which we have a duplicate reference available. */
- (ubuf_pre->tiles[i]->users == 1))) {
- if (ubuf_pre->tiles[i] != NULL) {
+ if ((ubuf_reference != nullptr) &&
+ ((ubuf_pre->tiles[i] == nullptr) ||
+ /* In this case the paint stroke as has added a tile
+ * which we have a duplicate reference available. */
+ (ubuf_pre->tiles[i]->users == 1))) {
+ if (ubuf_pre->tiles[i] != nullptr) {
/* If we have a reference, re-use this single use tile for the post state. */
BLI_assert(ubuf_pre->tiles[i]->users == 1);
ubuf_post->tiles[i] = ubuf_pre->tiles[i];
- ubuf_pre->tiles[i] = NULL;
+ ubuf_pre->tiles[i] = nullptr;
utile_init_from_imbuf(ubuf_post->tiles[i], x, y, ibuf, tmpibuf);
}
else {
- BLI_assert(ubuf_post->tiles[i] == NULL);
+ BLI_assert(ubuf_post->tiles[i] == nullptr);
ubuf_post->tiles[i] = ubuf_reference->tiles[i];
ubuf_post->tiles[i]->users += 1;
}
- BLI_assert(ubuf_pre->tiles[i] == NULL);
+ BLI_assert(ubuf_pre->tiles[i] == nullptr);
ubuf_pre->tiles[i] = ubuf_reference->tiles[i];
ubuf_pre->tiles[i]->users += 1;
- BLI_assert(ubuf_pre->tiles[i] != NULL);
- BLI_assert(ubuf_post->tiles[i] != NULL);
+ BLI_assert(ubuf_pre->tiles[i] != nullptr);
+ BLI_assert(ubuf_post->tiles[i] != nullptr);
}
else {
UndoImageTile *utile = utile_alloc(has_float);
utile_init_from_imbuf(utile, x, y, ibuf, tmpibuf);
- if (ubuf_pre->tiles[i] != NULL) {
+ if (ubuf_pre->tiles[i] != nullptr) {
ubuf_post->tiles[i] = utile;
utile->users = 1;
}
@@ -851,15 +893,15 @@ static bool image_undosys_step_encode(struct bContext *C,
utile->users = 2;
}
}
- BLI_assert(ubuf_pre->tiles[i] != NULL);
- BLI_assert(ubuf_post->tiles[i] != NULL);
+ BLI_assert(ubuf_pre->tiles[i] != nullptr);
+ BLI_assert(ubuf_post->tiles[i] != nullptr);
i += 1;
}
}
BLI_assert(i == ubuf_pre->tiles_len);
BLI_assert(i == ubuf_post->tiles_len);
}
- BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, NULL);
+ BKE_image_release_ibuf(uh->image_ref.ptr, ibuf, nullptr);
}
}
@@ -871,7 +913,7 @@ static bool image_undosys_step_encode(struct bContext *C,
}
}
else {
- BLI_assert(C != NULL);
+ BLI_assert(C != nullptr);
/* Happens when switching modes. */
ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D));
@@ -942,7 +984,7 @@ static void image_undosys_step_decode(
/* NOTE: behavior for undo/redo closely matches sculpt undo. */
BLI_assert(dir != STEP_INVALID);
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
if (dir == STEP_UNDO) {
image_undosys_step_decode_undo(us, is_final);
}
@@ -951,7 +993,7 @@ static void image_undosys_step_decode(
}
if (us->paint_mode == PAINT_MODE_TEXTURE_3D) {
- ED_object_mode_set_ex(C, OB_MODE_TEXTURE_PAINT, false, NULL);
+ ED_object_mode_set_ex(C, OB_MODE_TEXTURE_PAINT, false, nullptr);
}
/* Refresh texture slots. */
@@ -963,15 +1005,16 @@ static void image_undosys_step_free(UndoStep *us_p)
ImageUndoStep *us = (ImageUndoStep *)us_p;
uhandle_free_list(&us->handles);
- /* Typically this list will have been cleared. */
- ptile_free_list(&us->paint_tiles);
+ /* Typically this map will have been cleared. */
+ MEM_delete(us->paint_tile_map);
+ us->paint_tile_map = nullptr;
}
static void image_undosys_foreach_ID_ref(UndoStep *us_p,
UndoTypeForEachIDRefFn foreach_ID_ref_fn,
void *user_data)
{
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
LISTBASE_FOREACH (UndoImageHandle *, uh, &us->handles) {
foreach_ID_ref_fn(user_data, ((UndoRefID *)&uh->image_ref));
}
@@ -1011,12 +1054,12 @@ void ED_image_undosys_type(UndoType *ut)
* - So operators can access the pixel-data before the stroke was applied, at run-time.
* \{ */
-ListBase *ED_image_paint_tile_list_get(void)
+PaintTileMap *ED_image_paint_tile_map_get(void)
{
UndoStack *ustack = ED_undo_stack_get();
UndoStep *us_prev = ustack->step_init;
UndoStep *us_p = BKE_undosys_stack_init_or_active_with_type(ustack, BKE_UNDOSYS_TYPE_IMAGE);
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
/* We should always have an undo push started when accessing tiles,
* not doing this means we won't have paint_mode correctly set. */
BLI_assert(us_p == us_prev);
@@ -1024,24 +1067,24 @@ ListBase *ED_image_paint_tile_list_get(void)
/* Fallback value until we can be sure this never happens. */
us->paint_mode = PAINT_MODE_TEXTURE_2D;
}
- return &us->paint_tiles;
+ return us->paint_tile_map;
}
void ED_image_undo_restore(UndoStep *us)
{
- ListBase *paint_tiles = &((ImageUndoStep *)us)->paint_tiles;
- ptile_restore_runtime_list(paint_tiles);
- ptile_invalidate_list(paint_tiles);
+ PaintTileMap *paint_tile_map = reinterpret_cast<ImageUndoStep *>(us)->paint_tile_map;
+ ptile_restore_runtime_map(paint_tile_map);
+ ptile_invalidate_map(paint_tile_map);
}
static ImageUndoStep *image_undo_push_begin(const char *name, int paint_mode)
{
UndoStack *ustack = ED_undo_stack_get();
- bContext *C = NULL; /* special case, we never read from this. */
+ bContext *C = nullptr; /* special case, we never read from this. */
UndoStep *us_p = BKE_undosys_step_push_init_with_type(ustack, C, name, BKE_UNDOSYS_TYPE_IMAGE);
- ImageUndoStep *us = (ImageUndoStep *)us_p;
+ ImageUndoStep *us = reinterpret_cast<ImageUndoStep *>(us_p);
BLI_assert(ELEM(paint_mode, PAINT_MODE_TEXTURE_2D, PAINT_MODE_TEXTURE_3D, PAINT_MODE_SCULPT));
- us->paint_mode = paint_mode;
+ us->paint_mode = (ePaintMode)paint_mode;
return us;
}
@@ -1060,19 +1103,20 @@ void ED_image_undo_push_begin_with_image(const char *name,
BLI_assert(BKE_image_get_tile(image, iuser->tile));
UndoImageHandle *uh = uhandle_ensure(&us->handles, image, iuser);
UndoImageBuf *ubuf_pre = uhandle_ensure_ubuf(uh, image, ibuf);
- BLI_assert(ubuf_pre->post == NULL);
+ BLI_assert(ubuf_pre->post == nullptr);
- ImageUndoStep *us_reference = (ImageUndoStep *)ED_undo_stack_get()->step_active;
+ ImageUndoStep *us_reference = reinterpret_cast<ImageUndoStep *>(
+ ED_undo_stack_get()->step_active);
while (us_reference && us_reference->step.type != BKE_UNDOSYS_TYPE_IMAGE) {
- us_reference = (ImageUndoStep *)us_reference->step.prev;
+ us_reference = reinterpret_cast<ImageUndoStep *>(us_reference->step.prev);
}
UndoImageBuf *ubuf_reference = (us_reference ? ubuf_lookup_from_reference(
us_reference, image, iuser->tile, ubuf_pre) :
- NULL);
+ nullptr);
if (ubuf_reference) {
memcpy(ubuf_pre->tiles, ubuf_reference->tiles, sizeof(*ubuf_pre->tiles) * ubuf_pre->tiles_len);
- for (uint i = 0; i < ubuf_pre->tiles_len; i++) {
+ for (uint32_t i = 0; i < ubuf_pre->tiles_len; i++) {
UndoImageTile *utile = ubuf_pre->tiles[i];
utile->users += 1;
}
@@ -1085,7 +1129,7 @@ void ED_image_undo_push_begin_with_image(const char *name,
void ED_image_undo_push_end(void)
{
UndoStack *ustack = ED_undo_stack_get();
- BKE_undosys_step_push(ustack, NULL, NULL);
+ BKE_undosys_step_push(ustack, nullptr, nullptr);
BKE_undosys_stack_limit_steps_and_memory_defaults(ustack);
WM_file_tag_modified();
}
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index 785a5419e04..2b65267644a 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -20,6 +20,7 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_image.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
#include "BKE_screen.h"
@@ -111,7 +112,8 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED(
simage->tile_grid_shape[0] = 1;
simage->tile_grid_shape[1] = 1;
- simage->custom_grid_subdiv = 10;
+ simage->custom_grid_subdiv[0] = 10;
+ simage->custom_grid_subdiv[1] = 10;
/* header */
region = MEM_callocN(sizeof(ARegion), "header for image");
@@ -298,7 +300,7 @@ static void image_listener(const wmSpaceTypeListenerParams *params)
{
wmWindow *win = params->window;
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
SpaceImage *sima = (SpaceImage *)area->spacedata.first;
/* context changes */
@@ -350,8 +352,10 @@ static void image_listener(const wmSpaceTypeListenerParams *params)
}
break;
case NC_MASK: {
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (ED_space_image_check_show_maskedit(sima, obedit)) {
switch (wmn->data) {
case ND_SELECT:
@@ -392,8 +396,10 @@ static void image_listener(const wmSpaceTypeListenerParams *params)
switch (wmn->data) {
case ND_TRANSFORM:
case ND_MODIFIER: {
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob && (ob == wmn->reference) && (ob->mode & OB_MODE_EDIT)) {
if (sima->lock && (sima->flag & SI_DRAWSHADOW)) {
ED_area_tag_refresh(area);
@@ -713,7 +719,7 @@ static void image_main_region_listener(const wmRegionListenerParams *params)
{
ScrArea *area = params->area;
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -827,7 +833,7 @@ static void image_buttons_region_draw(const bContext *C, ARegion *region)
static void image_buttons_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -889,7 +895,7 @@ static void image_tools_region_draw(const bContext *C, ARegion *region)
static void image_tools_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -945,7 +951,7 @@ static void image_header_region_draw(const bContext *C, ARegion *region)
static void image_header_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -1028,7 +1034,7 @@ void ED_spacetype_image(void)
ARegionType *art;
st->spaceid = SPACE_IMAGE;
- strncpy(st->name, "Image", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Image");
st->create = image_create;
st->free = image_free;
diff --git a/source/blender/editors/space_info/CMakeLists.txt b/source/blender/editors/space_info/CMakeLists.txt
index febb025f5bd..4e9df2b93b0 100644
--- a/source/blender/editors/space_info/CMakeLists.txt
+++ b/source/blender/editors/space_info/CMakeLists.txt
@@ -14,7 +14,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc
index 29a7eb150a1..9b29ae737c5 100644
--- a/source/blender/editors/space_info/info_stats.cc
+++ b/source/blender/editors/space_info/info_stats.cc
@@ -130,7 +130,7 @@ static void stats_object(Object *ob,
SceneStats *stats,
GSet *objects_gset)
{
- if ((ob->base_flag & BASE_VISIBLE_VIEWLAYER) == 0) {
+ if ((ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) {
return;
}
@@ -161,42 +161,6 @@ static void stats_object(Object *ob,
stats->totlampsel++;
}
break;
- case OB_SURF:
- case OB_CURVES_LEGACY:
- case OB_FONT: {
- const Mesh *me_eval = BKE_object_get_evaluated_mesh(ob);
- if ((me_eval != nullptr) && !BLI_gset_add(objects_gset, (void *)me_eval)) {
- break;
- }
-
- if (stats_mesheval(me_eval, is_selected, stats)) {
- break;
- }
- ATTR_FALLTHROUGH; /* Fall-through to displist. */
- }
- case OB_MBALL: {
- int totv = 0, totf = 0, tottri = 0;
-
- if (ob->runtime.curve_cache && ob->runtime.curve_cache->disp.first) {
- /* NOTE: We only get the same curve_cache for instances of the same curve/font/...
- * For simple linked duplicated objects, each has its own dispList. */
- if (!BLI_gset_add(objects_gset, ob->runtime.curve_cache)) {
- break;
- }
-
- BKE_displist_count(&ob->runtime.curve_cache->disp, &totv, &totf, &tottri);
- }
-
- stats->totvert += totv;
- stats->totface += totf;
- stats->tottri += tottri;
-
- if (is_selected) {
- stats->totvertsel += totv;
- stats->totfacesel += totf;
- }
- break;
- }
case OB_GPENCIL: {
if (is_selected) {
bGPdata *gpd = (bGPdata *)ob->data;
@@ -381,7 +345,7 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats)
stats->tottri = ob->sculpt->bm->totface;
break;
case PBVH_GRIDS:
- stats->totvertsculpt = BKE_pbvh_get_grid_num_vertices(ss->pbvh);
+ stats->totvertsculpt = BKE_pbvh_get_grid_num_verts(ss->pbvh);
stats->totfacesculpt = BKE_pbvh_get_grid_num_faces(ss->pbvh);
break;
}
@@ -389,19 +353,21 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats)
/* Statistics displayed in info header. Called regularly on scene changes. */
static void stats_update(Depsgraph *depsgraph,
+ const Scene *scene,
ViewLayer *view_layer,
View3D *v3d_local,
SceneStats *stats)
{
- const Object *ob = OBACT(view_layer);
- const Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Object *ob = BKE_view_layer_active_object_get(view_layer);
+ const Object *obedit = BKE_view_layer_edit_object_get(view_layer);
memset(stats, 0x0, sizeof(*stats));
if (obedit) {
/* Edit Mode. */
- FOREACH_OBJECT_BEGIN (view_layer, ob_iter) {
- if (ob_iter->base_flag & BASE_VISIBLE_VIEWLAYER) {
+ FOREACH_OBJECT_BEGIN (scene, view_layer, ob_iter) {
+ if (ob_iter->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) {
if (ob_iter->mode & OB_MODE_EDIT) {
stats_object_edit(ob_iter, stats);
stats->totobjsel++;
@@ -420,8 +386,8 @@ static void stats_update(Depsgraph *depsgraph,
}
else if (ob && (ob->mode & OB_MODE_POSE)) {
/* Pose Mode. */
- FOREACH_OBJECT_BEGIN (view_layer, ob_iter) {
- if (ob_iter->base_flag & BASE_VISIBLE_VIEWLAYER) {
+ FOREACH_OBJECT_BEGIN (scene, view_layer, ob_iter) {
+ if (ob_iter->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) {
if (ob_iter->mode & OB_MODE_POSE) {
stats_object_pose(ob_iter, stats);
stats->totobjsel++;
@@ -439,14 +405,7 @@ static void stats_update(Depsgraph *depsgraph,
}
else if (ob && (ob->mode & OB_MODE_SCULPT)) {
/* Sculpt Mode. */
- if (stats_is_object_dynamic_topology_sculpt(ob)) {
- /* Dynamic topology. Do not count all vertices,
- * dynamic topology stats are initialized later as part of sculpt stats. */
- }
- else {
- /* When dynamic topology is not enabled both sculpt stats and scene stats are collected. */
- stats_object_sculpt(ob, stats);
- }
+ stats_object_sculpt(ob, stats);
}
else {
/* Objects. */
@@ -493,7 +452,7 @@ static bool format_stats(
}
Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
*stats_p = (SceneStats *)MEM_mallocN(sizeof(SceneStats), __func__);
- stats_update(depsgraph, view_layer, v3d_local, *stats_p);
+ stats_update(depsgraph, scene, view_layer, v3d_local, *stats_p);
}
SceneStats *stats = *stats_p;
@@ -532,13 +491,18 @@ static bool format_stats(
return true;
}
-static void get_stats_string(
- char *info, int len, size_t *ofs, ViewLayer *view_layer, SceneStatsFmt *stats_fmt)
+static void get_stats_string(char *info,
+ int len,
+ size_t *ofs,
+ const Scene *scene,
+ ViewLayer *view_layer,
+ SceneStatsFmt *stats_fmt)
{
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
eObjectMode object_mode = ob ? (eObjectMode)ob->mode : OB_MODE_OBJECT;
- LayerCollection *layer_collection = view_layer->active_collection;
+ LayerCollection *layer_collection = BKE_view_layer_active_collection_get(view_layer);
if (object_mode == OB_MODE_OBJECT) {
*ofs += BLI_snprintf_rlen(info + *ofs,
@@ -642,7 +606,7 @@ static const char *info_statusbar_string(Main *bmain,
if (statusbar_flag & STATUSBAR_SHOW_STATS) {
SceneStatsFmt stats_fmt;
if (format_stats(bmain, scene, view_layer, nullptr, &stats_fmt)) {
- get_stats_string(info + ofs, len, &ofs, view_layer, &stats_fmt);
+ get_stats_string(info + ofs, len, &ofs, scene, view_layer, &stats_fmt);
}
}
@@ -727,7 +691,8 @@ void ED_info_draw_stats(
return;
}
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
eObjectMode object_mode = ob ? (eObjectMode)ob->mode : OB_MODE_OBJECT;
const int font_id = BLF_set_default();
diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c
index 73d81c93981..63c8d74c684 100644
--- a/source/blender/editors/space_info/space_info.c
+++ b/source/blender/editors/space_info/space_info.c
@@ -186,7 +186,7 @@ static void info_header_region_draw(const bContext *C, ARegion *region)
static void info_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -202,7 +202,7 @@ static void info_main_region_listener(const wmRegionListenerParams *params)
static void info_header_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -254,7 +254,7 @@ void ED_spacetype_info(void)
ARegionType *art;
st->spaceid = SPACE_INFO;
- strncpy(st->name, "Info", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Info");
st->create = info_create;
st->free = info_free;
diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c
index bc2b539474c..9aa2b84169e 100644
--- a/source/blender/editors/space_info/textview.c
+++ b/source/blender/editors/space_info/textview.c
@@ -74,7 +74,7 @@ static void textview_draw_sel(const char *str,
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ubv(bg_sel);
immRecti(pos, xy[0] + (cwidth * sta), xy[1] + lheight, xy[0] + (cwidth * end), xy[1]);
@@ -197,7 +197,7 @@ static bool textview_draw_string(TextViewDrawState *tds,
if (bg) {
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ubv(bg);
immRecti(pos, tds->draw_rect_outer->xmin, line_bottom, tds->draw_rect_outer->xmax, line_top);
immUnbindProgram();
diff --git a/source/blender/editors/space_nla/CMakeLists.txt b/source/blender/editors/space_nla/CMakeLists.txt
index 85a2c3fd0a1..e6995085dbe 100644
--- a/source/blender/editors/space_nla/CMakeLists.txt
+++ b/source/blender/editors/space_nla/CMakeLists.txt
@@ -10,7 +10,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index 9652819404e..a46da391bdc 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -213,7 +213,9 @@ static bool nla_panel_poll(const bContext *C, PanelType *pt)
static bool nla_animdata_panel_poll(const bContext *C, PanelType *UNUSED(pt))
{
PointerRNA ptr;
- return (nla_panel_context(C, &ptr, NULL, NULL) && (ptr.data != NULL));
+ PointerRNA strip_ptr;
+ return (nla_panel_context(C, &ptr, NULL, &strip_ptr) && (ptr.data != NULL) &&
+ (ptr.owner_id != strip_ptr.owner_id));
}
static bool nla_strip_panel_poll(const bContext *C, PanelType *UNUSED(pt))
@@ -265,13 +267,18 @@ static bool nla_strip_eval_panel_poll(const bContext *C, PanelType *UNUSED(pt))
static void nla_panel_animdata(const bContext *C, Panel *panel)
{
PointerRNA adt_ptr;
+ PointerRNA strip_ptr;
/* AnimData *adt; */
uiLayout *layout = panel->layout;
uiLayout *row;
uiBlock *block;
/* check context and also validity of pointer */
- if (!nla_panel_context(C, &adt_ptr, NULL, NULL)) {
+ if (!nla_panel_context(C, &adt_ptr, NULL, &strip_ptr)) {
+ return;
+ }
+
+ if (adt_ptr.owner_id == strip_ptr.owner_id) {
return;
}
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index a0c6a29c422..3c0238806bf 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -20,6 +20,7 @@
#include "BKE_anim_data.h"
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_layer.h"
#include "BKE_nla.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -129,7 +130,8 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, int channel_index,
else {
/* deselect all */
/* TODO: should this deselect all other types of channels too? */
- LISTBASE_FOREACH (Base *, b, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(ac->scene, view_layer);
+ LISTBASE_FOREACH (Base *, b, BKE_view_layer_object_bases_get(view_layer)) {
ED_object_base_select(b, BA_DESELECT);
if (b->object->adt) {
b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE);
@@ -478,7 +480,7 @@ void NLA_OT_action_pushdown(wmOperatorType *ot)
"Index of NLA action channel to perform pushdown operation on",
0,
INT_MAX);
- RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
+ RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
/* ******************** Action Unlink ******************************** */
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index 3b108a3ba8a..f57c9fead56 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -22,6 +22,7 @@
#include "BLI_range.h"
#include "BLI_utildefines.h"
+#include "BKE_action.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_nla.h"
@@ -101,7 +102,7 @@ static void nla_action_draw_keyframes(
GPUVertFormat *format = immVertexFormat();
uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
@@ -178,7 +179,7 @@ static void nla_actionclip_draw_markers(
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
if (dashed) {
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -189,7 +190,7 @@ static void nla_actionclip_draw_markers(
immUniform1f("dash_factor", 0.5f);
}
else {
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
}
immUniformThemeColorShade(TH_STRIP_SELECT, shade);
@@ -377,7 +378,7 @@ static uint nla_draw_use_dashed_outlines(const float color[4], bool muted)
/* Note that we use dashed shader here, and make it draw solid lines if not muted... */
uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -441,7 +442,7 @@ static void nla_draw_strip(SpaceNla *snla,
nla_strip_get_color_inside(adt, strip, color);
shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* draw extrapolation info first (as backdrop)
* - but this should only be drawn if track has some contribution
@@ -502,7 +503,7 @@ static void nla_draw_strip(SpaceNla *snla,
/* restore current vertex format & program (roundbox trashes it) */
shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
}
else {
/* strip is in disabled track - make less visible */
@@ -854,10 +855,11 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- /* just draw a semi-shaded rect spanning the width of the viewable area if there's data,
- * and a second darker rect within which we draw keyframe indicator dots if there's data
+ /* just draw a semi-shaded rect spanning the width of the viewable area, based on if
+ * there's data and the action's extrapolation mode. Draw a second darker rect within
+ * which we draw keyframe indicator dots if there's data.
*/
GPU_blend(GPU_BLEND_ALPHA);
@@ -869,8 +871,26 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
/* draw slightly shifted up for greater separation from standard channels,
* but also slightly shorter for some more contrast when viewing the strips
*/
- immRectf(
- pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
+ switch (adt->act_extendmode) {
+ case NLASTRIP_EXTEND_HOLD: {
+ immRectf(pos,
+ v2d->cur.xmin,
+ ymin + NLACHANNEL_SKIP,
+ v2d->cur.xmax,
+ ymax - NLACHANNEL_SKIP);
+ break;
+ }
+ case NLASTRIP_EXTEND_HOLD_FORWARD: {
+ float r_start;
+ float r_end;
+ BKE_action_get_frame_range(ale->data, &r_start, &r_end);
+
+ immRectf(pos, r_end, ymin + NLACHANNEL_SKIP, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
+ break;
+ }
+ case NLASTRIP_EXTEND_NOTHING:
+ break;
+ }
immUnbindProgram();
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index cc45c86ddcd..9df25b1229e 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -606,6 +606,36 @@ void NLA_OT_view_frame(wmOperatorType *ot)
* (or the active block if no space in the track).
* \{ */
+/* Get a list of the editable tracks being shown in the NLA. */
+static int nlaedit_get_editable_tracks(bAnimContext *ac, ListBase *anim_data)
+{
+ const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT |
+ ANIMFILTER_FCURVESONLY);
+ return ANIM_animdata_filter(ac, anim_data, filter, ac->data, ac->datatype);
+}
+
+static int nlaedit_add_actionclip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ /* Get editor data. */
+ bAnimContext ac;
+ if (ANIM_animdata_get_context(C, &ac) == 0) {
+ return OPERATOR_CANCELLED;
+ }
+
+ ListBase anim_data = {NULL, NULL};
+ const size_t items = nlaedit_get_editable_tracks(&ac, &anim_data);
+
+ if (items == 0) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "No active track(s) to add strip to, select an existing track or add one before "
+ "trying again");
+ return OPERATOR_CANCELLED;
+ }
+
+ return WM_enum_search_invoke(C, op, event);
+}
+
/* add the specified action as new strip */
static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op)
{
@@ -615,8 +645,6 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op)
ListBase anim_data = {NULL, NULL};
bAnimListElem *ale;
- size_t items;
- int filter;
bAction *act;
@@ -654,20 +682,7 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op)
*/
nlaedit_add_tracks_empty(&ac);
- /* get a list of the editable tracks being shown in the NLA
- * - this is limited to active ones for now, but could be expanded to
- */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ACTIVE | ANIMFILTER_FOREDIT |
- ANIMFILTER_FCURVESONLY);
- items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
- if (items == 0) {
- BKE_report(op->reports,
- RPT_ERROR,
- "No active track(s) to add strip to, select an existing track or add one before "
- "trying again");
- return OPERATOR_CANCELLED;
- }
+ nlaedit_get_editable_tracks(&ac, &anim_data);
/* for every active track,
* try to add strip to free space in track or to the top of the stack if no space */
@@ -736,7 +751,7 @@ void NLA_OT_actionclip_add(wmOperatorType *ot)
"Add an Action-Clip strip (i.e. an NLA Strip referencing an Action) to the active track";
/* api callbacks */
- ot->invoke = WM_enum_search_invoke;
+ ot->invoke = nlaedit_add_actionclip_invoke;
ot->exec = nlaedit_add_actionclip_exec;
ot->poll = nlaop_poll_tweakmode_off;
@@ -1216,13 +1231,10 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
nlaedit_duplicate_exec(C, op);
- RNA_enum_set(op->ptr, "mode", TFM_TRANSLATION);
- WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr, event);
-
return OPERATOR_FINISHED;
}
@@ -1248,9 +1260,6 @@ void NLA_OT_duplicate(wmOperatorType *ot)
false,
"Linked",
"When duplicating strips, assign new copies of the actions they use");
-
- /* to give to transform */
- RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", "");
}
/** \} */
@@ -2204,8 +2213,13 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op))
/* setup iterator, and iterate over all the keyframes in the action,
* applying this scaling */
ked.data = strip;
- ANIM_animchanneldata_keyframes_loop(
- &ked, ac.ads, strip->act, ALE_ACT, NULL, bezt_apply_nlamapping, calchandles_fcurve);
+ ANIM_animchanneldata_keyframes_loop(&ked,
+ ac.ads,
+ strip->act,
+ ALE_ACT,
+ NULL,
+ bezt_apply_nlamapping,
+ BKE_fcurve_handles_recalc);
/* clear scale of strip now that it has been applied,
* and recalculate the extents of the action now that it has been scaled
diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c
index 902e7a176a3..3ae73282230 100644
--- a/source/blender/editors/space_nla/nla_ops.c
+++ b/source/blender/editors/space_nla/nla_ops.c
@@ -16,6 +16,8 @@
#include "ED_anim_api.h"
#include "ED_screen.h"
+#include "RNA_access.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -138,6 +140,28 @@ void nla_operatortypes(void)
WM_operatortype_append(NLA_OT_fmodifier_paste);
}
+void ED_operatormacros_nla()
+{
+ wmOperatorType *ot;
+ wmOperatorTypeMacro *otmacro;
+
+ ot = WM_operatortype_append_macro("NLA_OT_duplicate_move",
+ "Duplicate",
+ "Duplicate selected strips and their Actions and move them",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate");
+ RNA_boolean_set(otmacro->ptr, "linked", false);
+ WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+
+ ot = WM_operatortype_append_macro("NLA_OT_duplicate_linked_move",
+ "Duplicate Linked",
+ "Duplicate selected strips and move them",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ otmacro = WM_operatortype_macro_define(ot, "NLA_OT_duplicate");
+ RNA_boolean_set(otmacro->ptr, "linked", true);
+ WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+}
+
/* ************************** registration - keymaps **********************************/
void nla_keymap(wmKeyConfig *keyconf)
diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c
index a816f8fa4f6..ce93e36474f 100644
--- a/source/blender/editors/space_nla/nla_select.c
+++ b/source/blender/editors/space_nla/nla_select.c
@@ -401,7 +401,7 @@ void NLA_OT_select_box(wmOperatorType *ot)
ot->poll = nlaop_poll_tweakmode_off;
/* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_UNDO;
/* properties */
RNA_def_boolean(ot->srna, "axis_range", 0, "Axis Range", "");
diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c
index 13035a9d5fd..e658ddef513 100644
--- a/source/blender/editors/space_nla/space_nla.c
+++ b/source/blender/editors/space_nla/space_nla.c
@@ -79,7 +79,6 @@ static SpaceLink *nla_create(const ScrArea *area, const Scene *scene)
BLI_addtail(&snla->regionbase, region);
region->regiontype = RGN_TYPE_UI;
region->alignment = RGN_ALIGN_RIGHT;
- region->flag = RGN_FLAG_HIDDEN;
/* main region */
region = MEM_callocN(sizeof(ARegion), "main region for nla");
@@ -304,7 +303,7 @@ static void nla_buttons_region_draw(const bContext *C, ARegion *region)
static void nla_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -343,7 +342,7 @@ static void nla_region_listener(const wmRegionListenerParams *params)
static void nla_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -437,7 +436,7 @@ static void nla_main_region_message_subscribe(const wmRegionMessageSubscribePara
static void nla_channel_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -513,7 +512,7 @@ static void nla_channel_region_message_subscribe(const wmRegionMessageSubscribeP
static void nla_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -569,7 +568,7 @@ void ED_spacetype_nla(void)
ARegionType *art;
st->spaceid = SPACE_NLA;
- strncpy(st->name, "NLA", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "NLA");
st->create = nla_create;
st->free = nla_free;
@@ -629,5 +628,8 @@ void ED_spacetype_nla(void)
nla_buttons_register(art);
+ art = ED_area_type_hud(st->spaceid);
+ BLI_addhead(&st->regiontypes, art);
+
BKE_spacetype_register(st);
}
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index badcccca87b..adb03439568 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -17,7 +17,6 @@ set(INC
../../nodes
../../render
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@@ -25,6 +24,7 @@ set(INC
set(SRC
+ add_node_search.cc
drawnode.cc
link_drag_search.cc
node_add.cc
@@ -50,8 +50,8 @@ set(LIB
bf_editor_screen
)
-if(WITH_COMPOSITOR)
- add_definitions(-DWITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_OPENIMAGEDENOISE)
diff --git a/source/blender/editors/space_node/add_node_search.cc b/source/blender/editors/space_node/add_node_search.cc
new file mode 100644
index 00000000000..101517b8cfb
--- /dev/null
+++ b/source/blender/editors/space_node/add_node_search.cc
@@ -0,0 +1,312 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <optional>
+
+#include "BLI_listbase.h"
+#include "BLI_string_search.h"
+
+#include "DNA_space_types.h"
+
+#include "BKE_asset.h"
+#include "BKE_asset_catalog.hh"
+#include "BKE_asset_library.hh"
+#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_lib_id.h"
+#include "BKE_node_tree_update.h"
+#include "BKE_screen.h"
+
+#include "DEG_depsgraph_build.h"
+
+#include "BLT_translation.h"
+
+#include "RNA_access.h"
+
+#include "WM_api.h"
+
+#include "ED_asset.h"
+#include "ED_node.h"
+
+#include "node_intern.hh"
+
+struct bContext;
+
+namespace blender::ed::space_node {
+
+struct AddNodeItem {
+ std::string ui_name;
+ std::string identifier;
+ std::string description;
+ std::optional<AssetHandle> asset;
+ std::function<void(const bContext &, bNodeTree &, bNode &)> after_add_fn;
+ int weight = 0;
+};
+
+struct AddNodeSearchStorage {
+ float2 cursor;
+ bool use_transform;
+ Vector<AddNodeItem> search_add_items;
+ char search[256];
+ bool update_items_tag = true;
+};
+
+static void add_node_search_listen_fn(const wmRegionListenerParams *params, void *arg)
+{
+ AddNodeSearchStorage &storage = *static_cast<AddNodeSearchStorage *>(arg);
+ const wmNotifier *wmn = params->notifier;
+
+ switch (wmn->category) {
+ case NC_ASSET:
+ if (wmn->data == ND_ASSET_LIST_READING) {
+ storage.update_items_tag = true;
+ }
+ break;
+ }
+}
+
+static void search_items_for_asset_metadata(const bNodeTree &node_tree,
+ const AssetLibraryReference &library_ref,
+ const AssetHandle asset,
+ Vector<AddNodeItem> &search_items)
+{
+ const AssetMetaData &asset_data = *ED_asset_handle_get_metadata(&asset);
+ const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&asset_data, "type");
+ if (tree_type == nullptr || IDP_Int(tree_type) != node_tree.type) {
+ return;
+ }
+
+ AddNodeItem item{};
+ item.ui_name = ED_asset_handle_get_name(&asset);
+ item.identifier = node_tree.typeinfo->group_idname;
+ item.description = asset_data.description == nullptr ? "" : asset_data.description;
+ item.asset = asset;
+ item.after_add_fn = [asset, library_ref](const bContext &C, bNodeTree &node_tree, bNode &node) {
+ Main &bmain = *CTX_data_main(&C);
+ node.flag &= ~NODE_OPTIONS;
+ node.id = asset::get_local_id_from_asset_or_append_and_reuse(bmain, library_ref, asset);
+ id_us_plus(node.id);
+ BKE_ntree_update_tag_node_property(&node_tree, &node);
+ DEG_relations_tag_update(&bmain);
+ };
+
+ search_items.append(std::move(item));
+}
+
+static void gather_search_items_for_asset_library(const bContext &C,
+ const bNodeTree &node_tree,
+ const AssetLibraryReference &library_ref,
+ const bool skip_local,
+ Vector<AddNodeItem> &search_items)
+{
+ AssetFilterSettings filter_settings{};
+ filter_settings.id_types = FILTER_ID_NT;
+
+ ED_assetlist_storage_fetch(&library_ref, &C);
+ ED_assetlist_ensure_previews_job(&library_ref, &C);
+ ED_assetlist_iterate(library_ref, [&](AssetHandle asset) {
+ if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) {
+ return true;
+ }
+ if (skip_local && ED_asset_handle_get_local_id(&asset) != nullptr) {
+ return true;
+ }
+ search_items_for_asset_metadata(node_tree, library_ref, asset, search_items);
+ return true;
+ });
+}
+
+static void gather_search_items_for_all_assets(const bContext &C,
+ const bNodeTree &node_tree,
+ Vector<AddNodeItem> &search_items)
+{
+ int i;
+ LISTBASE_FOREACH_INDEX (const bUserAssetLibrary *, asset_library, &U.asset_libraries, i) {
+ AssetLibraryReference library_ref{};
+ library_ref.custom_library_index = i;
+ library_ref.type = ASSET_LIBRARY_CUSTOM;
+ /* Skip local assets to avoid duplicates when the asset is part of the local file library. */
+ gather_search_items_for_asset_library(C, node_tree, library_ref, true, search_items);
+ }
+
+ AssetLibraryReference library_ref{};
+ library_ref.custom_library_index = -1;
+ library_ref.type = ASSET_LIBRARY_LOCAL;
+ gather_search_items_for_asset_library(C, node_tree, library_ref, false, search_items);
+}
+
+static void gather_add_node_operations(const bContext &C,
+ bNodeTree &node_tree,
+ Vector<AddNodeItem> &r_search_items)
+{
+ NODE_TYPES_BEGIN (node_type) {
+ const char *disabled_hint;
+ if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) {
+ continue;
+ }
+
+ AddNodeItem item{};
+ item.ui_name = IFACE_(node_type->ui_name);
+ item.identifier = node_type->idname;
+ item.description = TIP_(node_type->ui_description);
+ r_search_items.append(std::move(item));
+ }
+ NODE_TYPES_END;
+
+ gather_search_items_for_all_assets(C, node_tree, r_search_items);
+}
+
+static void add_node_search_update_fn(
+ const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
+{
+ AddNodeSearchStorage &storage = *static_cast<AddNodeSearchStorage *>(arg);
+ if (storage.update_items_tag) {
+ bNodeTree *node_tree = CTX_wm_space_node(C)->edittree;
+ storage.search_add_items.clear();
+ gather_add_node_operations(*C, *node_tree, storage.search_add_items);
+ storage.update_items_tag = false;
+ }
+
+ StringSearch *search = BLI_string_search_new();
+
+ for (AddNodeItem &item : storage.search_add_items) {
+ BLI_string_search_add(search, item.ui_name.c_str(), &item, item.weight);
+ }
+
+ /* Don't filter when the menu is first opened, but still run the search
+ * so the items are in the same order they will appear in while searching. */
+ const char *string = is_first ? "" : str;
+ AddNodeItem **filtered_items;
+ const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items);
+
+ for (const int i : IndexRange(filtered_amount)) {
+ AddNodeItem &item = *filtered_items[i];
+ if (!UI_search_item_add(items, item.ui_name.c_str(), &item, ICON_NONE, 0, 0)) {
+ break;
+ }
+ }
+
+ MEM_freeN(filtered_items);
+ BLI_string_search_free(search);
+}
+
+static void add_node_search_exec_fn(bContext *C, void *arg1, void *arg2)
+{
+ Main &bmain = *CTX_data_main(C);
+ SpaceNode &snode = *CTX_wm_space_node(C);
+ bNodeTree &node_tree = *snode.edittree;
+ AddNodeSearchStorage &storage = *static_cast<AddNodeSearchStorage *>(arg1);
+ AddNodeItem *item = static_cast<AddNodeItem *>(arg2);
+ if (item == nullptr) {
+ return;
+ }
+
+ node_deselect_all(snode);
+ bNode *new_node = nodeAddNode(C, &node_tree, item->identifier.c_str());
+ BLI_assert(new_node != nullptr);
+
+ if (item->after_add_fn) {
+ item->after_add_fn(*C, node_tree, *new_node);
+ }
+
+ new_node->locx = storage.cursor.x / UI_DPI_FAC;
+ new_node->locy = storage.cursor.y / UI_DPI_FAC + 20 * UI_DPI_FAC;
+
+ nodeSetSelected(new_node, true);
+ nodeSetActive(&node_tree, new_node);
+
+ /* Ideally it would be possible to tag the node tree in some way so it updates only after the
+ * translate operation is finished, but normally moving nodes around doesn't cause updates. */
+ ED_node_tree_propagate_change(C, &bmain, &node_tree);
+
+ if (storage.use_transform) {
+ wmOperatorType *ot = WM_operatortype_find("NODE_OT_translate_attach_remove_on_cancel", true);
+ BLI_assert(ot);
+ PointerRNA ptr;
+ WM_operator_properties_create_ptr(&ptr, ot);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
+ WM_operator_properties_free(&ptr);
+ }
+}
+
+static ARegion *add_node_search_tooltip_fn(
+ bContext *C, ARegion *region, const rcti *item_rect, void * /*arg*/, void *active)
+{
+ const AddNodeItem *item = static_cast<AddNodeItem *>(active);
+
+ uiSearchItemTooltipData tooltip_data{};
+
+ BLI_strncpy(tooltip_data.description,
+ item->asset ? item->description.c_str() : TIP_(item->description.c_str()),
+ sizeof(tooltip_data.description));
+
+ return UI_tooltip_create_from_search_item_generic(C, region, item_rect, &tooltip_data);
+}
+
+static void add_node_search_free_fn(void *arg)
+{
+ AddNodeSearchStorage *storage = static_cast<AddNodeSearchStorage *>(arg);
+ delete storage;
+}
+
+static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *arg_op)
+{
+ AddNodeSearchStorage &storage = *(AddNodeSearchStorage *)arg_op;
+
+ uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
+ UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
+ UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
+
+ uiBut *but = uiDefSearchBut(block,
+ storage.search,
+ 0,
+ ICON_VIEWZOOM,
+ sizeof(storage.search),
+ 10,
+ 10,
+ UI_searchbox_size_x(),
+ UI_UNIT_Y,
+ 0,
+ 0,
+ "");
+ UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);
+ UI_but_func_search_set(but,
+ nullptr,
+ add_node_search_update_fn,
+ &storage,
+ false,
+ add_node_search_free_fn,
+ add_node_search_exec_fn,
+ nullptr);
+ UI_but_flag_enable(but, UI_BUT_ACTIVATE_ON_INIT);
+ UI_but_func_search_set_tooltip(but, add_node_search_tooltip_fn);
+ UI_but_func_search_set_listen(but, add_node_search_listen_fn);
+
+ /* Fake button to hold space for the search items. */
+ uiDefBut(block,
+ UI_BTYPE_LABEL,
+ 0,
+ "",
+ 10,
+ 10 - UI_searchbox_size_y(),
+ UI_searchbox_size_x(),
+ UI_searchbox_size_y(),
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr);
+
+ const int offset[2] = {0, -UI_UNIT_Y};
+ UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, offset);
+ return block;
+}
+
+void invoke_add_node_search_menu(bContext &C, const float2 &cursor, const bool use_transform)
+{
+ AddNodeSearchStorage *storage = new AddNodeSearchStorage{cursor, use_transform};
+ /* Use the "_ex" variant with `can_refresh` false to avoid a double free when closing Blender. */
+ UI_popup_block_invoke_ex(&C, create_search_popup_block, storage, nullptr, false);
+}
+
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index 66e07c804b6..fbbdd40e92e 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -6,6 +6,7 @@
* \brief lower level node drawing for nodes (boarders, headers etc), also node layout.
*/
+#include "BLI_color.hh"
#include "BLI_system.h"
#include "BLI_threads.h"
@@ -477,7 +478,7 @@ static void node_shader_set_butfunc(bNodeType *ntype)
case SH_NODE_RGB:
ntype->draw_buttons = node_buts_rgb;
break;
- case SH_NODE_MIX_RGB:
+ case SH_NODE_MIX_RGB_LEGACY:
ntype->draw_buttons = node_buts_mix_rgb;
break;
case SH_NODE_VALTORGB:
@@ -626,7 +627,7 @@ static void node_composit_backdrop_viewer(
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
@@ -672,7 +673,7 @@ static void node_composit_backdrop_boxmask(
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
@@ -717,7 +718,7 @@ static void node_composit_backdrop_ellipsemask(
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
@@ -1455,7 +1456,11 @@ static void std_node_socket_interface_draw(bContext *UNUSED(C), uiLayout *layout
}
case SOCK_BOOLEAN:
case SOCK_RGBA:
- case SOCK_STRING: {
+ case SOCK_STRING:
+ case SOCK_OBJECT:
+ case SOCK_COLLECTION:
+ case SOCK_TEXTURE:
+ case SOCK_MATERIAL: {
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0);
break;
}
@@ -1565,7 +1570,7 @@ void draw_nodespace_back_pix(const bContext &C,
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_ACTIVE);
immDrawBorderCorners(pos, &pixel_border, 1.0f, 1.0f);
@@ -1580,102 +1585,75 @@ void draw_nodespace_back_pix(const bContext &C,
GPU_matrix_pop();
}
-bool node_link_bezier_handles(const View2D *v2d,
- const SpaceNode *snode,
- const bNodeLink &link,
- float vec[4][2])
+static float2 socket_link_connection_location(const bNodeSocket &socket, const bNodeLink &link)
{
- float cursor[2] = {0.0f, 0.0f};
-
- /* this function can be called with snode null (via cut_links_intersect) */
- /* XXX map snode->runtime->cursor back to view space */
- if (snode) {
- cursor[0] = snode->runtime->cursor[0] * UI_DPI_FAC;
- cursor[1] = snode->runtime->cursor[1] * UI_DPI_FAC;
- }
-
- /* in v0 and v3 we put begin/end points */
- if (link.fromsock) {
- vec[0][0] = link.fromsock->locx;
- vec[0][1] = link.fromsock->locy;
- if (link.fromsock->flag & SOCK_MULTI_INPUT) {
- const float2 position = node_link_calculate_multi_input_position(
- {link.fromsock->locx, link.fromsock->locy},
- link.fromsock->total_inputs - 1,
- link.fromsock->total_inputs);
- copy_v2_v2(vec[0], position);
- }
- }
- else {
- if (snode == nullptr) {
- return false;
- }
- copy_v2_v2(vec[0], cursor);
+ const float2 socket_location(socket.locx, socket.locy);
+ if (socket.flag & SOCK_MULTI_INPUT && socket.in_out == SOCK_IN) {
+ return node_link_calculate_multi_input_position(
+ socket_location, link.multi_input_socket_index, socket.total_inputs);
}
- if (link.tosock) {
- vec[3][0] = link.tosock->locx;
- vec[3][1] = link.tosock->locy;
- if (!(link.tonode->flag & NODE_HIDDEN) && link.tosock->flag & SOCK_MULTI_INPUT) {
- const float2 position = node_link_calculate_multi_input_position(
- {link.tosock->locx, link.tosock->locy},
- link.multi_input_socket_index,
- link.tosock->total_inputs);
- copy_v2_v2(vec[3], position);
- }
- }
- else {
- if (snode == nullptr) {
- return false;
- }
- copy_v2_v2(vec[3], cursor);
- }
-
- /* may be called outside of drawing (so pass spacetype) */
- int curving = UI_GetThemeValueType(TH_NODE_CURVING, SPACE_NODE);
+ return socket_location;
+}
+static void calculate_inner_link_bezier_points(std::array<float2, 4> &points)
+{
+ const int curving = UI_GetThemeValueType(TH_NODE_CURVING, SPACE_NODE);
if (curving == 0) {
/* Straight line: align all points. */
- mid_v2_v2v2(vec[1], vec[0], vec[3]);
- mid_v2_v2v2(vec[2], vec[1], vec[3]);
- return true;
+ points[1] = math::interpolate(points[0], points[3], 1.0f / 3.0f);
+ points[2] = math::interpolate(points[0], points[3], 2.0f / 3.0f);
}
+ else {
+ const float dist = curving * 0.1f * math::distance(points[0].x, points[3].x);
- const float dist = curving * 0.10f * fabsf(vec[0][0] - vec[3][0]);
+ points[1].x = points[0].x + dist;
+ points[1].y = points[0].y;
- vec[1][0] = vec[0][0] + dist;
- vec[1][1] = vec[0][1];
+ points[2].x = points[3].x - dist;
+ points[2].y = points[3].y;
+ }
+}
- vec[2][0] = vec[3][0] - dist;
- vec[2][1] = vec[3][1];
+static std::array<float2, 4> node_link_bezier_points(const bNodeLink &link)
+{
+ std::array<float2, 4> points;
+ points[0] = socket_link_connection_location(*link.fromsock, link);
+ points[3] = socket_link_connection_location(*link.tosock, link);
+ calculate_inner_link_bezier_points(points);
+ return points;
+}
- if (v2d && min_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) > v2d->cur.xmax) {
- return false; /* clipped */
+static bool node_link_draw_is_visible(const View2D &v2d, const std::array<float2, 4> &points)
+{
+ if (min_ffff(points[0].x, points[1].x, points[2].x, points[3].x) > v2d.cur.xmax) {
+ return false;
}
- if (v2d && max_ffff(vec[0][0], vec[1][0], vec[2][0], vec[3][0]) < v2d->cur.xmin) {
- return false; /* clipped */
+ if (max_ffff(points[0].x, points[1].x, points[2].x, points[3].x) < v2d.cur.xmin) {
+ return false;
}
-
return true;
}
-bool node_link_bezier_points(const View2D *v2d,
- const SpaceNode *snode,
- const bNodeLink &link,
- float coord_array[][2],
- const int resol)
+void node_link_bezier_points_evaluated(const bNodeLink &link,
+ std::array<float2, NODE_LINK_RESOL + 1> &coords)
{
- float vec[4][2];
-
- if (node_link_bezier_handles(v2d, snode, link, vec)) {
- /* always do all three, to prevent data hanging around */
- BKE_curve_forward_diff_bezier(
- vec[0][0], vec[1][0], vec[2][0], vec[3][0], coord_array[0] + 0, resol, sizeof(float[2]));
- BKE_curve_forward_diff_bezier(
- vec[0][1], vec[1][1], vec[2][1], vec[3][1], coord_array[0] + 1, resol, sizeof(float[2]));
+ const std::array<float2, 4> points = node_link_bezier_points(link);
- return true;
- }
- return false;
+ /* The extra +1 in size is required by these functions and would be removed ideally. */
+ BKE_curve_forward_diff_bezier(points[0].x,
+ points[1].x,
+ points[2].x,
+ points[3].x,
+ &coords[0].x,
+ NODE_LINK_RESOL,
+ sizeof(float2));
+ BKE_curve_forward_diff_bezier(points[0].y,
+ points[1].y,
+ points[2].y,
+ points[3].y,
+ &coords[0].y,
+ NODE_LINK_RESOL,
+ sizeof(float2));
}
#define NODELINK_GROUP_SIZE 256
@@ -1934,187 +1912,250 @@ void nodelink_batch_end(SpaceNode &snode)
g_batch_link.enabled = false;
}
+struct NodeLinkDrawConfig {
+ int th_col1;
+ int th_col2;
+ int th_col3;
+
+ ColorTheme4f start_color;
+ ColorTheme4f end_color;
+ ColorTheme4f outline_color;
+
+ bool drawarrow;
+ bool drawmuted;
+ bool highlighted;
+
+ float dim_factor;
+ float thickness;
+ float dash_factor;
+ float dash_alpha;
+};
+
static void nodelink_batch_add_link(const SpaceNode &snode,
- const float2 &p0,
- const float2 &p1,
- const float2 &p2,
- const float2 &p3,
- int th_col1,
- int th_col2,
- int th_col3,
- const float start_color[4],
- const float end_color[4],
- bool drawarrow,
- bool drawmuted,
- float dim_factor,
- float thickness,
- float dash_factor,
- float dash_alpha)
+ const std::array<float2, 4> &points,
+ const NodeLinkDrawConfig &draw_config)
{
/* Only allow these colors. If more is needed, you need to modify the shader accordingly. */
- BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
- BLI_assert(ELEM(th_col2, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
- BLI_assert(ELEM(th_col3, TH_WIRE, TH_REDALERT, -1));
+ BLI_assert(
+ ELEM(draw_config.th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
+ BLI_assert(
+ ELEM(draw_config.th_col2, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
+ BLI_assert(ELEM(draw_config.th_col3, TH_WIRE, TH_REDALERT, -1));
g_batch_link.count++;
- 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);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p0_step), points[0]);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p1_step), points[1]);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p2_step), points[2]);
+ copy_v2_v2((float *)GPU_vertbuf_raw_step(&g_batch_link.p3_step), points[3]);
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;
- copy_v4_v4((float *)GPU_vertbuf_raw_step(&g_batch_link.start_color_step), start_color);
- copy_v4_v4((float *)GPU_vertbuf_raw_step(&g_batch_link.end_color_step), end_color);
+ colid[0] = nodelink_get_color_id(draw_config.th_col1);
+ colid[1] = nodelink_get_color_id(draw_config.th_col2);
+ colid[2] = nodelink_get_color_id(draw_config.th_col3);
+ colid[3] = draw_config.drawarrow;
+ copy_v4_v4((float *)GPU_vertbuf_raw_step(&g_batch_link.start_color_step),
+ draw_config.start_color);
+ copy_v4_v4((float *)GPU_vertbuf_raw_step(&g_batch_link.end_color_step), draw_config.end_color);
char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step);
- muted[0] = drawmuted;
- *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor;
- *(float *)GPU_vertbuf_raw_step(&g_batch_link.thickness_step) = thickness;
- *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_factor_step) = dash_factor;
- *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_alpha_step) = dash_alpha;
+ muted[0] = draw_config.drawmuted;
+ *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = draw_config.dim_factor;
+ *(float *)GPU_vertbuf_raw_step(&g_batch_link.thickness_step) = draw_config.thickness;
+ *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_factor_step) = draw_config.dash_factor;
+ *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_alpha_step) = draw_config.dash_alpha;
if (g_batch_link.count == NODELINK_GROUP_SIZE) {
nodelink_batch_draw(snode);
}
}
-void node_draw_link_bezier(const bContext &C,
- const View2D &v2d,
- const SpaceNode &snode,
- const bNodeLink &link,
- const int th_col1,
- const int th_col2,
- const int th_col3,
- const bool selected)
+static void node_draw_link_end_marker(const float2 center,
+ const float radius,
+ const ColorTheme4f &color)
{
- const float dim_factor = selected ? 1.0f : node_link_dim_factor(v2d, link);
- float thickness = 1.5f;
- float dash_factor = 1.0f;
+ rctf rect;
+ BLI_rctf_init(&rect, center.x - radius, center.x + radius, center.y - radius, center.y + radius);
+
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_4fv(&rect, true, radius, color);
+ /* Roundbox disables alpha. Reenable it for node links that are drawn after this one. */
+ GPU_blend(GPU_BLEND_ALPHA);
+}
+
+static void node_draw_link_end_markers(const bNodeLink &link,
+ const NodeLinkDrawConfig &draw_config,
+ const std::array<float2, 4> &points,
+ const bool outline)
+{
+ const float radius = (outline ? 0.65f : 0.45f) * NODE_SOCKSIZE;
+ if (link.fromsock) {
+ node_draw_link_end_marker(
+ points[0], radius, outline ? draw_config.outline_color : draw_config.start_color);
+ }
+ if (link.tosock) {
+ node_draw_link_end_marker(
+ points[3], radius, outline ? draw_config.outline_color : draw_config.end_color);
+ }
+}
+
+static bool node_link_is_field_link(const SpaceNode &snode, const bNodeLink &link)
+{
+ if (snode.edittree->type != NTREE_GEOMETRY) {
+ return false;
+ }
+ if (link.fromsock && link.fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) {
+ return true;
+ }
+ return false;
+}
+
+static NodeLinkDrawConfig nodelink_get_draw_config(const bContext &C,
+ const View2D &v2d,
+ const SpaceNode &snode,
+ const bNodeLink &link,
+ const int th_col1,
+ const int th_col2,
+ const int th_col3,
+ const bool selected)
+{
+ NodeLinkDrawConfig draw_config;
+
+ draw_config.th_col1 = th_col1;
+ draw_config.th_col2 = th_col2;
+ draw_config.th_col3 = th_col3;
+
+ draw_config.dim_factor = selected ? 1.0f : node_link_dim_factor(v2d, link);
bTheme *btheme = UI_GetTheme();
- const float dash_alpha = btheme->space_node.dash_alpha;
-
- if (snode.edittree->type == NTREE_GEOMETRY) {
- if (link.fromsock && link.fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) {
- /* Make field links a bit thinner. */
- thickness = 1.0f;
- /* Draw field as dashes. */
- dash_factor = 0.75f;
+ draw_config.dash_alpha = btheme->space_node.dash_alpha;
+
+ const bool field_link = node_link_is_field_link(snode, link);
+
+ draw_config.dash_factor = field_link ? 0.75f : 1.0f;
+
+ const float scale = UI_view2d_scale_get_x(&v2d);
+ /* Clamp the thickness to make the links more readable when zooming out. */
+ draw_config.thickness = max_ff(scale, 1.0f) * (field_link ? 0.7f : 1.0f);
+ draw_config.highlighted = link.flag & NODE_LINK_TEMP_HIGHLIGHT;
+ draw_config.drawarrow = ((link.tonode && (link.tonode->type == NODE_REROUTE)) &&
+ (link.fromnode && (link.fromnode->type == NODE_REROUTE)));
+ draw_config.drawmuted = (link.flag & NODE_LINK_MUTED);
+
+ UI_GetThemeColor4fv(th_col3, draw_config.outline_color);
+
+ if (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS &&
+ snode.overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS) {
+ PointerRNA from_node_ptr, to_node_ptr;
+ RNA_pointer_create((ID *)snode.edittree, &RNA_Node, link.fromnode, &from_node_ptr);
+ RNA_pointer_create((ID *)snode.edittree, &RNA_Node, link.tonode, &to_node_ptr);
+
+ if (link.fromsock) {
+ node_socket_color_get(
+ C, *snode.edittree, from_node_ptr, *link.fromsock, draw_config.start_color);
+ }
+ else {
+ node_socket_color_get(
+ C, *snode.edittree, to_node_ptr, *link.tosock, draw_config.start_color);
}
- }
- float vec[4][2];
- const bool highlighted = link.flag & NODE_LINK_TEMP_HIGHLIGHT;
- if (node_link_bezier_handles(&v2d, &snode, link, vec)) {
- 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 == nullptr) {
- nodelink_batch_init();
+ if (link.tosock) {
+ node_socket_color_get(C, *snode.edittree, to_node_ptr, *link.tosock, draw_config.end_color);
}
- /* Draw single link. */
- float colors[3][4] = {{0.0f}};
- if (th_col3 != -1) {
- UI_GetThemeColor4fv(th_col3, colors[0]);
+ else {
+ node_socket_color_get(
+ C, *snode.edittree, from_node_ptr, *link.fromsock, draw_config.end_color);
}
+ }
+ else {
+ UI_GetThemeColor4fv(th_col1, draw_config.start_color);
+ UI_GetThemeColor4fv(th_col2, draw_config.end_color);
+ }
+
+ /* Highlight links connected to selected nodes. */
+ if (selected) {
+ ColorTheme4f color_selected;
+ UI_GetThemeColor4fv(TH_EDGE_SELECT, color_selected);
+ const float alpha = color_selected.a;
- if (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS &&
- snode.overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS) {
- PointerRNA from_node_ptr, to_node_ptr;
- RNA_pointer_create((ID *)snode.edittree, &RNA_Node, link.fromnode, &from_node_ptr);
- RNA_pointer_create((ID *)snode.edittree, &RNA_Node, link.tonode, &to_node_ptr);
+ /* Interpolate color if highlight color is not fully transparent. */
+ if (alpha != 0.0) {
if (link.fromsock) {
- node_socket_color_get(C, *snode.edittree, from_node_ptr, *link.fromsock, colors[1]);
- }
- else {
- node_socket_color_get(C, *snode.edittree, to_node_ptr, *link.tosock, colors[1]);
+ interp_v3_v3v3(draw_config.start_color, draw_config.start_color, color_selected, alpha);
}
-
if (link.tosock) {
- node_socket_color_get(C, *snode.edittree, to_node_ptr, *link.tosock, colors[2]);
- }
- else {
- node_socket_color_get(C, *snode.edittree, from_node_ptr, *link.fromsock, colors[2]);
+ interp_v3_v3v3(draw_config.end_color, draw_config.end_color, color_selected, alpha);
}
}
- else {
- UI_GetThemeColor4fv(th_col1, colors[1]);
- UI_GetThemeColor4fv(th_col2, colors[2]);
- }
+ }
- /* Highlight links connected to selected nodes. */
- if (selected) {
- float color_selected[4];
- UI_GetThemeColor4fv(TH_EDGE_SELECT, color_selected);
- const float alpha = color_selected[3];
+ if (draw_config.highlighted) {
+ ColorTheme4f link_preselection_highlight_color;
+ UI_GetThemeColor4fv(TH_SELECT, link_preselection_highlight_color);
+ /* Multi sockets can only be inputs. So we only have to highlight the end of the link. */
+ copy_v4_v4(draw_config.end_color, link_preselection_highlight_color);
+ }
- /* Interpolate color if highlight color is not fully transparent. */
- if (alpha != 0.0) {
- if (link.fromsock) {
- interp_v3_v3v3(colors[1], colors[1], color_selected, alpha);
- }
- if (link.tosock) {
- interp_v3_v3v3(colors[2], colors[2], color_selected, alpha);
- }
- }
- }
+ return draw_config;
+}
- if (g_batch_link.enabled && !highlighted) {
- /* Add link to batch. */
- nodelink_batch_add_link(snode,
- vec[0],
- vec[1],
- vec[2],
- vec[3],
- th_col1,
- th_col2,
- th_col3,
- colors[1],
- colors[2],
- drawarrow,
- drawmuted,
- dim_factor,
- thickness,
- dash_factor,
- dash_alpha);
- }
- else {
- if (highlighted) {
- float link_preselection_highlight_color[4];
- UI_GetThemeColor4fv(TH_SELECT, link_preselection_highlight_color);
- copy_v4_v4(colors[2], link_preselection_highlight_color);
- }
+static void node_draw_link_bezier_ex(const SpaceNode &snode,
+ const NodeLinkDrawConfig &draw_config,
+ const std::array<float2, 4> &points)
+{
+ if (g_batch_link.batch == nullptr) {
+ nodelink_batch_init();
+ }
- NodeLinkData node_link_data;
- for (int i = 0; i < 4; i++) {
- copy_v2_v2(node_link_data.bezierPts[i], vec[i]);
- }
- for (int i = 0; i < 3; i++) {
- copy_v4_v4(node_link_data.colors[i], colors[i]);
- }
- node_link_data.doArrow = drawarrow;
- node_link_data.doMuted = drawmuted;
- node_link_data.dim_factor = dim_factor;
- node_link_data.thickness = thickness;
- node_link_data.dash_factor = dash_factor;
- node_link_data.dash_alpha = dash_alpha;
- node_link_data.expandSize = snode.runtime->aspect * LINK_WIDTH;
- node_link_data.arrowSize = ARROW_SIZE;
-
- GPUBatch *batch = g_batch_link.batch_single;
- GPUUniformBuf *ubo = GPU_uniformbuf_create_ex(
- sizeof(NodeLinkData), &node_link_data, __func__);
-
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK);
- GPU_batch_uniformbuf_bind(batch, "node_link_data", ubo);
- GPU_batch_draw(batch);
-
- GPU_uniformbuf_unbind(ubo);
- GPU_uniformbuf_free(ubo);
+ if (g_batch_link.enabled && !draw_config.highlighted) {
+ /* Add link to batch. */
+ nodelink_batch_add_link(snode, points, draw_config);
+ }
+ else {
+ NodeLinkData node_link_data;
+ for (const int i : IndexRange(points.size())) {
+ copy_v2_v2(node_link_data.bezierPts[i], points[i]);
}
+
+ copy_v4_v4(node_link_data.colors[0], draw_config.outline_color);
+ copy_v4_v4(node_link_data.colors[1], draw_config.start_color);
+ copy_v4_v4(node_link_data.colors[2], draw_config.end_color);
+
+ node_link_data.doArrow = draw_config.drawarrow;
+ node_link_data.doMuted = draw_config.drawmuted;
+ node_link_data.dim_factor = draw_config.dim_factor;
+ node_link_data.thickness = draw_config.thickness;
+ node_link_data.dash_factor = draw_config.dash_factor;
+ node_link_data.dash_alpha = draw_config.dash_alpha;
+ node_link_data.expandSize = snode.runtime->aspect * LINK_WIDTH;
+ node_link_data.arrowSize = ARROW_SIZE;
+
+ GPUBatch *batch = g_batch_link.batch_single;
+ GPUUniformBuf *ubo = GPU_uniformbuf_create_ex(sizeof(NodeLinkData), &node_link_data, __func__);
+
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK);
+ GPU_batch_uniformbuf_bind(batch, "node_link_data", ubo);
+ GPU_batch_draw(batch);
+
+ GPU_uniformbuf_unbind(ubo);
+ GPU_uniformbuf_free(ubo);
+ }
+}
+
+void node_draw_link_bezier(const bContext &C,
+ const View2D &v2d,
+ const SpaceNode &snode,
+ const bNodeLink &link,
+ const int th_col1,
+ const int th_col2,
+ const int th_col3,
+ const bool selected)
+{
+ const std::array<float2, 4> points = node_link_bezier_points(link);
+ if (!node_link_draw_is_visible(v2d, points)) {
+ return;
}
+ const NodeLinkDrawConfig draw_config = nodelink_get_draw_config(
+ C, v2d, snode, link, th_col1, th_col2, th_col3, selected);
+
+ node_draw_link_bezier_ex(snode, draw_config, points);
}
void node_draw_link(const bContext &C,
@@ -2129,34 +2170,29 @@ void node_draw_link(const bContext &C,
return;
}
- /* new connection */
- if (!link.fromsock || !link.tosock) {
- th_col1 = th_col2 = TH_ACTIVE;
+ /* going to give issues once... */
+ if (link.tosock->flag & SOCK_UNAVAIL) {
+ return;
+ }
+ if (link.fromsock->flag & SOCK_UNAVAIL) {
+ return;
}
- else {
- /* going to give issues once... */
- if (link.tosock->flag & SOCK_UNAVAIL) {
- return;
- }
- if (link.fromsock->flag & SOCK_UNAVAIL) {
- return;
- }
- if (link.flag & NODE_LINK_VALID) {
- /* special indicated link, on drop-node */
- if (link.flag & NODE_LINKFLAG_HILITE) {
- th_col1 = th_col2 = TH_ACTIVE;
- }
- else if (link.flag & NODE_LINK_MUTED) {
- th_col1 = th_col2 = TH_REDALERT;
- }
+ if (link.flag & NODE_LINK_VALID) {
+ /* special indicated link, on drop-node */
+ if (link.flag & NODE_LINKFLAG_HILITE) {
+ th_col1 = th_col2 = TH_ACTIVE;
}
- else {
- /* Invalid link. */
- th_col1 = th_col2 = th_col3 = TH_REDALERT;
- // th_col3 = -1; /* no shadow */
+ else if (link.flag & NODE_LINK_MUTED) {
+ th_col1 = th_col2 = TH_REDALERT;
}
}
+ else {
+ /* Invalid link. */
+ th_col1 = th_col2 = th_col3 = TH_REDALERT;
+ // th_col3 = -1; /* no shadow */
+ }
+
/* Links from field to non-field sockets are not allowed. */
if (snode.edittree->type == NTREE_GEOMETRY && !(link.flag & NODE_LINK_DRAGGED)) {
if ((link.fromsock && link.fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) &&
@@ -2168,6 +2204,38 @@ void node_draw_link(const bContext &C,
node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3, selected);
}
+static std::array<float2, 4> node_link_bezier_points_dragged(const SpaceNode &snode,
+ const bNodeLink &link)
+{
+ const float2 cursor = snode.runtime->cursor * UI_DPI_FAC;
+ std::array<float2, 4> points;
+ points[0] = link.fromsock ? socket_link_connection_location(*link.fromsock, link) : cursor;
+ points[3] = link.tosock ? socket_link_connection_location(*link.tosock, link) : cursor;
+ calculate_inner_link_bezier_points(points);
+ return points;
+}
+
+void node_draw_link_dragged(const bContext &C,
+ const View2D &v2d,
+ const SpaceNode &snode,
+ const bNodeLink &link)
+{
+ if (link.fromsock == nullptr && link.tosock == nullptr) {
+ return;
+ }
+
+ const std::array<float2, 4> points = node_link_bezier_points_dragged(snode, link);
+
+ const NodeLinkDrawConfig draw_config = nodelink_get_draw_config(
+ C, v2d, snode, link, TH_ACTIVE, TH_ACTIVE, TH_WIRE, true);
+ /* End marker outline. */
+ node_draw_link_end_markers(link, draw_config, points, true);
+ /* Link. */
+ node_draw_link_bezier_ex(snode, draw_config, points);
+ /* End marker fill. */
+ node_draw_link_end_markers(link, draw_config, points, false);
+}
+
} // namespace blender::ed::space_node
void ED_node_draw_snap(View2D *v2d, const float cent[2], float size, NodeBorder border, uint pos)
diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc
index c524de2c55d..17410937d4c 100644
--- a/source/blender/editors/space_node/link_drag_search.cc
+++ b/source/blender/editors/space_node/link_drag_search.cc
@@ -5,7 +5,12 @@
#include "DNA_space_types.h"
+#include "BKE_asset.h"
#include "BKE_context.h"
+#include "BKE_idprop.h"
+#include "BKE_lib_id.h"
+#include "BKE_node_tree_update.h"
+#include "BKE_screen.h"
#include "NOD_socket_search_link.hh"
@@ -15,6 +20,9 @@
#include "WM_api.h"
+#include "DEG_depsgraph_build.h"
+
+#include "ED_asset.h"
#include "ED_node.h"
#include "node_intern.hh"
@@ -29,6 +37,7 @@ struct LinkDragSearchStorage {
float2 cursor;
Vector<SocketLinkOperation> search_link_ops;
char search[256];
+ bool update_items_tag = true;
eNodeSocketInOut in_out() const
{
@@ -36,6 +45,20 @@ struct LinkDragSearchStorage {
}
};
+static void link_drag_search_listen_fn(const wmRegionListenerParams *params, void *arg)
+{
+ LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg);
+ const wmNotifier *wmn = params->notifier;
+
+ switch (wmn->category) {
+ case NC_ASSET:
+ if (wmn->data == ND_ASSET_LIST_READING) {
+ storage.update_items_tag = true;
+ }
+ break;
+ }
+}
+
static void add_reroute_node_fn(nodes::LinkSearchOpParams &params)
{
bNode &reroute = params.add_node("NodeReroute");
@@ -112,11 +135,137 @@ static void add_existing_group_input_fn(nodes::LinkSearchOpParams &params,
}
/**
+ * \note This could use #search_link_ops_for_socket_templates, but we have to store the inputs and
+ * outputs as IDProperties for assets because of asset indexing, so that's all we have without
+ * loading the file.
+ */
+static void search_link_ops_for_asset_metadata(const bNodeTree &node_tree,
+ const bNodeSocket &socket,
+ const AssetLibraryReference &library_ref,
+ const AssetHandle asset,
+ Vector<SocketLinkOperation> &search_link_ops)
+{
+ const AssetMetaData &asset_data = *ED_asset_handle_get_metadata(&asset);
+ const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&asset_data, "type");
+ if (tree_type == nullptr || IDP_Int(tree_type) != node_tree.type) {
+ return;
+ }
+
+ const bNodeTreeType &node_tree_type = *node_tree.typeinfo;
+ const eNodeSocketInOut in_out = socket.in_out == SOCK_OUT ? SOCK_IN : SOCK_OUT;
+
+ const IDProperty *sockets = BKE_asset_metadata_idprop_find(
+ &asset_data, in_out == SOCK_IN ? "inputs" : "outputs");
+
+ int weight = -1;
+ Set<StringRef> socket_names;
+ LISTBASE_FOREACH (IDProperty *, socket_property, &sockets->data.group) {
+ if (socket_property->type != IDP_STRING) {
+ continue;
+ }
+ const char *socket_idname = IDP_String(socket_property);
+ const bNodeSocketType *socket_type = nodeSocketTypeFind(socket_idname);
+ if (socket_type == nullptr) {
+ continue;
+ }
+ eNodeSocketDatatype from = (eNodeSocketDatatype)socket.type;
+ eNodeSocketDatatype to = (eNodeSocketDatatype)socket_type->type;
+ if (socket.in_out == SOCK_OUT) {
+ std::swap(from, to);
+ }
+ if (node_tree_type.validate_link && !node_tree_type.validate_link(from, to)) {
+ continue;
+ }
+ if (!socket_names.add(socket_property->name)) {
+ /* See comment in #search_link_ops_for_declarations. */
+ continue;
+ }
+
+ const StringRef asset_name = ED_asset_handle_get_name(&asset);
+ const StringRef socket_name = socket_property->name;
+
+ search_link_ops.append(
+ {asset_name + " " + UI_MENU_ARROW_SEP + socket_name,
+ [library_ref, asset, socket_property, in_out](nodes::LinkSearchOpParams &params) {
+ Main &bmain = *CTX_data_main(&params.C);
+
+ bNode &node = params.add_node(params.node_tree.typeinfo->group_idname);
+ node.flag &= ~NODE_OPTIONS;
+
+ node.id = asset::get_local_id_from_asset_or_append_and_reuse(bmain, library_ref, asset);
+ id_us_plus(node.id);
+ BKE_ntree_update_tag_node_property(&params.node_tree, &node);
+ DEG_relations_tag_update(&bmain);
+
+ /* Create the inputs and outputs on the new node. */
+ node.typeinfo->group_update_func(&params.node_tree, &node);
+
+ bNodeSocket *new_node_socket = bke::node_find_enabled_socket(
+ node, in_out, socket_property->name);
+ if (new_node_socket != nullptr) {
+ /* Rely on the way #nodeAddLink switches in/out if necessary. */
+ nodeAddLink(&params.node_tree, &params.node, &params.socket, &node, new_node_socket);
+ }
+ },
+ weight});
+
+ weight--;
+ }
+}
+
+static void gather_search_link_ops_for_asset_library(const bContext &C,
+ const bNodeTree &node_tree,
+ const bNodeSocket &socket,
+ const AssetLibraryReference &library_ref,
+ const bool skip_local,
+ Vector<SocketLinkOperation> &search_link_ops)
+{
+ AssetFilterSettings filter_settings{};
+ filter_settings.id_types = FILTER_ID_NT;
+
+ ED_assetlist_storage_fetch(&library_ref, &C);
+ ED_assetlist_ensure_previews_job(&library_ref, &C);
+ ED_assetlist_iterate(library_ref, [&](AssetHandle asset) {
+ if (!ED_asset_filter_matches_asset(&filter_settings, &asset)) {
+ return true;
+ }
+ if (skip_local && ED_asset_handle_get_local_id(&asset) != nullptr) {
+ return true;
+ }
+ search_link_ops_for_asset_metadata(node_tree, socket, library_ref, asset, search_link_ops);
+ return true;
+ });
+}
+
+static void gather_search_link_ops_for_all_assets(const bContext &C,
+ const bNodeTree &node_tree,
+ const bNodeSocket &socket,
+ Vector<SocketLinkOperation> &search_link_ops)
+{
+ int i;
+ LISTBASE_FOREACH_INDEX (const bUserAssetLibrary *, asset_library, &U.asset_libraries, i) {
+ AssetLibraryReference library_ref{};
+ library_ref.custom_library_index = i;
+ library_ref.type = ASSET_LIBRARY_CUSTOM;
+ /* Skip local assets to avoid duplicates when the asset is part of the local file library. */
+ gather_search_link_ops_for_asset_library(
+ C, node_tree, socket, library_ref, true, search_link_ops);
+ }
+
+ AssetLibraryReference library_ref{};
+ library_ref.custom_library_index = -1;
+ library_ref.type = ASSET_LIBRARY_LOCAL;
+ gather_search_link_ops_for_asset_library(
+ C, node_tree, socket, library_ref, false, search_link_ops);
+}
+
+/**
* Call the callback to gather compatible socket connections for all node types, and the operations
* that will actually make the connections. Also add some custom operations like connecting a group
* output node.
*/
-static void gather_socket_link_operations(bNodeTree &node_tree,
+static void gather_socket_link_operations(const bContext &C,
+ bNodeTree &node_tree,
const bNodeSocket &socket,
Vector<SocketLinkOperation> &search_link_ops)
{
@@ -156,15 +305,20 @@ static void gather_socket_link_operations(bNodeTree &node_tree,
weight--;
}
}
+
+ gather_search_link_ops_for_all_assets(C, node_tree, socket, search_link_ops);
}
-static void link_drag_search_update_fn(const bContext *UNUSED(C),
- void *arg,
- const char *str,
- uiSearchItems *items,
- const bool is_first)
+static void link_drag_search_update_fn(
+ const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
{
LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg);
+ if (storage.update_items_tag) {
+ bNodeTree *node_tree = CTX_wm_space_node(C)->edittree;
+ storage.search_link_ops.clear();
+ gather_socket_link_operations(*C, *node_tree, storage.from_socket, storage.search_link_ops);
+ storage.update_items_tag = false;
+ }
StringSearch *search = BLI_string_search_new();
@@ -214,7 +368,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
bNode *new_node = new_nodes.first();
new_node->locx = storage.cursor.x / UI_DPI_FAC;
- new_node->locy = storage.cursor.y / UI_DPI_FAC + 20 * UI_DPI_FAC;
+ new_node->locy = storage.cursor.y / UI_DPI_FAC + 20;
if (storage.in_out() == SOCK_IN) {
new_node->locx -= new_node->width;
}
@@ -227,11 +381,10 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
ED_node_tree_propagate_change(C, &bmain, snode.edittree);
/* Start translation operator with the new node. */
- wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_translate", true);
+ wmOperatorType *ot = WM_operatortype_find("NODE_OT_translate_attach_remove_on_cancel", true);
BLI_assert(ot);
PointerRNA ptr;
WM_operator_properties_create_ptr(&ptr, ot);
- RNA_boolean_set(&ptr, "view2d_edge_pan", true);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
WM_operator_properties_free(&ptr);
}
@@ -246,9 +399,6 @@ static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *ar
{
LinkDragSearchStorage &storage = *(LinkDragSearchStorage *)arg_op;
- bNodeTree *node_tree = CTX_wm_space_node(C)->nodetree;
- gather_socket_link_operations(*node_tree, storage.from_socket, storage.search_link_ops);
-
uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
@@ -266,6 +416,7 @@ static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *ar
0,
"");
UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);
+ UI_but_func_search_set_listen(but, link_drag_search_listen_fn);
UI_but_func_search_set(but,
nullptr,
link_drag_search_update_fn,
@@ -292,7 +443,7 @@ static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *ar
0,
nullptr);
- const int offset[2] = {0, -UI_UNIT_Y};
+ const int2 offset = {0, -UI_UNIT_Y};
UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, offset);
return block;
}
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index 975d4eda7e3..efe53fd6f14 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -5,6 +5,8 @@
* \ingroup spnode
*/
+#include <numeric>
+
#include "MEM_guardedalloc.h"
#include "DNA_collection_types.h"
@@ -20,6 +22,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -49,29 +52,48 @@ namespace blender::ed::space_node {
/** \name Utilities
* \{ */
-bNode *node_add_node(const bContext &C, const char *idname, int type, float locx, float locy)
+static void position_node_based_on_mouse(bNode &node, const float2 &location)
+{
+ node.locx = location.x - NODE_DY * 1.5f / UI_DPI_FAC;
+ node.locy = location.y + NODE_DY * 0.5f / UI_DPI_FAC;
+}
+
+bNode *add_node(const bContext &C, const StringRef idname, const float2 &location)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
Main &bmain = *CTX_data_main(&C);
- bNode *node = nullptr;
node_deselect_all(snode);
- if (idname) {
- node = nodeAddNode(&C, snode.edittree, idname);
- }
- else {
- node = nodeAddStaticNode(&C, snode.edittree, type);
- }
+ const std::string idname_str = idname;
+
+ bNode *node = nodeAddNode(&C, snode.edittree, idname_str.c_str());
BLI_assert(node && node->typeinfo);
- /* Position mouse in node header. */
- node->locx = locx - NODE_DY * 1.5f / UI_DPI_FAC;
- node->locy = locy + NODE_DY * 0.5f / UI_DPI_FAC;
+ position_node_based_on_mouse(*node, location);
nodeSetSelected(node, true);
+ ED_node_set_active(&bmain, &snode, snode.edittree, node, nullptr);
+ ED_node_tree_propagate_change(&C, &bmain, snode.edittree);
+ return node;
+}
+
+bNode *add_static_node(const bContext &C, int type, const float2 &location)
+{
+ SpaceNode &snode = *CTX_wm_space_node(&C);
+ Main &bmain = *CTX_data_main(&C);
+
+ node_deselect_all(snode);
+
+ bNode *node = nodeAddStaticNode(&C, snode.edittree, type);
+ BLI_assert(node && node->typeinfo);
+
+ position_node_based_on_mouse(*node, location);
+
+ nodeSetSelected(node, true);
ED_node_set_active(&bmain, &snode, snode.edittree, node, nullptr);
+
ED_node_tree_propagate_change(&C, &bmain, snode.edittree);
return node;
}
@@ -82,191 +104,113 @@ bNode *node_add_node(const bContext &C, const char *idname, int type, float locx
/** \name Add Reroute Operator
* \{ */
-static bool add_reroute_intersect_check(const bNodeLink &link,
- float mcoords[][2],
- int tot,
- float result[2])
+std::optional<float2> link_path_intersection(const bNodeLink &link, const Span<float2> path)
{
- float coord_array[NODE_LINK_RESOL + 1][2];
-
- 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(
- mcoords[i], mcoords[i + 1], coord_array[b], coord_array[b + 1], result) > 0) {
- return true;
- }
+ std::array<float2, NODE_LINK_RESOL + 1> coords;
+ node_link_bezier_points_evaluated(link, coords);
+
+ for (const int i : path.index_range().drop_back(1)) {
+ for (const int j : IndexRange(NODE_LINK_RESOL)) {
+ float2 result;
+ if (isect_seg_seg_v2_point(path[i], path[i + 1], coords[j], coords[j + 1], result) > 0) {
+ return result;
}
}
}
- return false;
-}
-struct bNodeSocketLink {
- struct bNodeSocketLink *next, *prev;
+ return std::nullopt;
+}
- struct bNodeSocket *sock;
- struct bNodeLink *link;
- float point[2];
+struct RerouteCutsForSocket {
+ /* The output socket's owner node. */
+ bNode *from_node;
+ /* Intersected links connected to the socket and their path intersection locations. */
+ Map<bNodeLink *, float2> links;
};
-static bNodeSocketLink *add_reroute_insert_socket_link(ListBase *lb,
- bNodeSocket *sock,
- bNodeLink *link,
- const float point[2])
+static int add_reroute_exec(bContext *C, wmOperator *op)
{
- bNodeSocketLink *socklink, *prev;
-
- socklink = MEM_cnew<bNodeSocketLink>("socket link");
- socklink->sock = sock;
- socklink->link = link;
- copy_v2_v2(socklink->point, point);
+ const ARegion &region = *CTX_wm_region(C);
+ SpaceNode &snode = *CTX_wm_space_node(C);
+ bNodeTree &ntree = *snode.edittree;
- for (prev = (bNodeSocketLink *)lb->last; prev; prev = prev->prev) {
- if (prev->sock == sock) {
+ Vector<float2> path;
+ RNA_BEGIN (op->ptr, itemptr, "path") {
+ float2 loc_region;
+ RNA_float_get_array(&itemptr, "loc", loc_region);
+ float2 loc_view;
+ UI_view2d_region_to_view(&region.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
+ path.append(loc_view);
+ if (path.size() >= 256) {
break;
}
}
- BLI_insertlinkafter(lb, prev, socklink);
- return socklink;
-}
-
-static bNodeSocketLink *add_reroute_do_socket_section(bContext *C,
- bNodeSocketLink *socklink,
- int in_out)
-{
- SpaceNode *snode = CTX_wm_space_node(C);
- bNodeTree *ntree = snode->edittree;
- bNode *reroute_node = nullptr;
- bNodeSocket *cursock = socklink->sock;
- float insert_point[2];
- int num_links;
-
- zero_v2(insert_point);
- num_links = 0;
-
- while (socklink && socklink->sock == cursock) {
- if (!(socklink->link->flag & NODE_LINK_TEST)) {
- socklink->link->flag |= NODE_LINK_TEST;
-
- /* create the reroute node for this cursock */
- if (!reroute_node) {
- reroute_node = nodeAddStaticNode(C, ntree, NODE_REROUTE);
-
- /* add a single link to/from the reroute node to replace multiple links */
- if (in_out == SOCK_OUT) {
- nodeAddLink(ntree,
- socklink->link->fromnode,
- socklink->link->fromsock,
- reroute_node,
- (bNodeSocket *)reroute_node->inputs.first);
- }
- else {
- nodeAddLink(ntree,
- reroute_node,
- (bNodeSocket *)reroute_node->outputs.first,
- socklink->link->tonode,
- socklink->link->tosock);
- }
- }
-
- /* insert the reroute node into the link */
- if (in_out == SOCK_OUT) {
- socklink->link->fromnode = reroute_node;
- socklink->link->fromsock = (bNodeSocket *)reroute_node->outputs.first;
- }
- else {
- socklink->link->tonode = reroute_node;
- socklink->link->tosock = (bNodeSocket *)reroute_node->inputs.first;
- }
+ RNA_END;
- add_v2_v2(insert_point, socklink->point);
- num_links++;
- }
- socklink = socklink->next;
+ if (path.is_empty()) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
- if (num_links > 0) {
- /* average cut point from shared links */
- mul_v2_fl(insert_point, 1.0f / num_links);
+ ntree.ensure_topology_cache();
+ const Vector<bNode *> frame_nodes = ntree.nodes_by_type("NodeFrame");
- reroute_node->locx = insert_point[0] / UI_DPI_FAC;
- reroute_node->locy = insert_point[1] / UI_DPI_FAC;
- }
-
- return socklink;
-}
-
-static int add_reroute_exec(bContext *C, wmOperator *op)
-{
- SpaceNode &snode = *CTX_wm_space_node(C);
- ARegion &region = *CTX_wm_region(C);
- bNodeTree &ntree = *snode.edittree;
- float mcoords[256][2];
- int i = 0;
+ ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
+ node_deselect_all(snode);
- /* Get the cut path */
- RNA_BEGIN (op->ptr, itemptr, "path") {
- float loc[2];
+ /* All link "cuts" that start at a particular output socket. Deduplicating new reroutes per
+ * output socket is useful because it allows reusing reroutes for connected intersections.
+ * Further deduplication using the second map means we only have one cut per link. */
+ Map<bNodeSocket *, RerouteCutsForSocket> cuts_per_socket;
- RNA_float_get_array(&itemptr, "loc", loc);
- UI_view2d_region_to_view(
- &region.v2d, (short)loc[0], (short)loc[1], &mcoords[i][0], &mcoords[i][1]);
- i++;
- if (i >= 256) {
- break;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
+ if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
+ continue;
}
+ const std::optional<float2> intersection = link_path_intersection(*link, path);
+ if (!intersection) {
+ continue;
+ }
+ RerouteCutsForSocket &from_cuts = cuts_per_socket.lookup_or_add_default(link->fromsock);
+ from_cuts.from_node = link->fromnode;
+ from_cuts.links.add(link, *intersection);
}
- RNA_END;
- if (i > 1) {
- ListBase output_links, input_links;
- bNodeSocketLink *socklink;
- float insert_point[2];
+ for (const auto item : cuts_per_socket.items()) {
+ const Map<bNodeLink *, float2> &cuts = item.value.links;
- /* always first */
- ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
+ bNode *reroute = nodeAddStaticNode(C, &ntree, NODE_REROUTE);
- node_deselect_all(snode);
+ nodeAddLink(&ntree,
+ item.value.from_node,
+ item.key,
+ reroute,
+ static_cast<bNodeSocket *>(reroute->inputs.first));
- /* Find cut links and sort them by sockets */
- BLI_listbase_clear(&output_links);
- BLI_listbase_clear(&input_links);
-
- LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
- if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
- continue;
- }
- if (add_reroute_intersect_check(*link, mcoords, i, insert_point)) {
- add_reroute_insert_socket_link(&output_links, link->fromsock, link, insert_point);
- add_reroute_insert_socket_link(&input_links, link->tosock, link, insert_point);
-
- /* Clear flag */
- link->flag &= ~NODE_LINK_TEST;
- }
+ /* Reconnect links from the original output socket to the new reroute. */
+ for (bNodeLink *link : cuts.keys()) {
+ link->fromnode = reroute;
+ link->fromsock = static_cast<bNodeSocket *>(reroute->outputs.first);
+ BKE_ntree_update_tag_link_changed(&ntree);
}
- /* Create reroute nodes for intersected links.
- * Only one reroute if links share the same input/output socket.
- */
- socklink = (bNodeSocketLink *)output_links.first;
- while (socklink) {
- socklink = add_reroute_do_socket_section(C, socklink, SOCK_OUT);
- }
- socklink = (bNodeSocketLink *)input_links.first;
- while (socklink) {
- socklink = add_reroute_do_socket_section(C, socklink, SOCK_IN);
+ /* Place the new reroute at the average location of all connected cuts. */
+ const float2 loc = std::accumulate(cuts.values().begin(), cuts.values().end(), float2(0)) /
+ cuts.size() / UI_DPI_FAC;
+ reroute->locx = loc.x;
+ reroute->locy = loc.y;
+
+ /* Attach the reroute node to frame nodes behind it. */
+ for (const int i : frame_nodes.index_range()) {
+ bNode *frame_node = frame_nodes.last(i);
+ if (BLI_rctf_isect_pt_v(&frame_node->totr, loc)) {
+ nodeAttachNode(reroute, frame_node);
+ break;
+ }
}
-
- BLI_freelistN(&output_links);
- BLI_freelistN(&input_links);
-
- /* always last */
- ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
- return OPERATOR_FINISHED;
}
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
+ return OPERATOR_FINISHED;
}
void NODE_OT_add_reroute(wmOperatorType *ot)
@@ -338,9 +282,9 @@ static int node_add_group_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNodeTree *node_group;
- if (!(node_group = node_add_group_get_and_poll_group_node_tree(bmain, op, ntree))) {
+ bNodeTree *node_group = node_add_group_get_and_poll_group_node_tree(bmain, op, ntree);
+ if (!node_group) {
return OPERATOR_CANCELLED;
}
@@ -352,12 +296,7 @@ static int node_add_group_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- bNode *group_node = node_add_node(*C,
- node_idname,
- (node_group->type == NTREE_CUSTOM) ? NODE_CUSTOM_GROUP :
- NODE_GROUP,
- snode->runtime->cursor[0],
- snode->runtime->cursor[1]);
+ bNode *group_node = add_node(*C, node_idname, snode->runtime->cursor);
if (!group_node) {
BKE_report(op->reports, RPT_WARNING, "Could not add node group");
return OPERATOR_CANCELLED;
@@ -382,7 +321,7 @@ static bool node_add_group_poll(bContext *C)
if (snode->edittree->type == NTREE_CUSTOM) {
CTX_wm_operator_poll_msg_set(C,
"This node editor displays a custom (Python defined) node tree. "
- "Dropping node groups isn't supported for this.");
+ "Dropping node groups isn't supported for this");
return false;
}
return true;
@@ -445,8 +384,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, nullptr, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]);
+ bNode *object_node = add_static_node(*C, GEO_NODE_OBJECT_INFO, snode->runtime->cursor);
if (!object_node) {
BKE_report(op->reports, RPT_WARNING, "Could not add node object");
return OPERATOR_CANCELLED;
@@ -522,7 +460,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
- bNodeTree *ntree = snode.edittree;
+ bNodeTree &ntree = *snode.edittree;
Collection *collection = reinterpret_cast<Collection *>(
WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR));
@@ -533,8 +471,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, nullptr, GEO_NODE_COLLECTION_INFO, snode.runtime->cursor[0], snode.runtime->cursor[1]);
+ bNode *collection_node = add_static_node(*C, GEO_NODE_COLLECTION_INFO, snode.runtime->cursor);
if (!collection_node) {
BKE_report(op->reports, RPT_WARNING, "Could not add node collection");
return OPERATOR_CANCELLED;
@@ -550,8 +487,8 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
socket_data->value = collection;
id_us_plus(&collection->id);
- nodeSetActive(ntree, collection_node);
- ED_node_tree_propagate_change(C, bmain, ntree);
+ nodeSetActive(&ntree, collection_node);
+ ED_node_tree_propagate_change(C, bmain, &ntree);
DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
@@ -617,11 +554,9 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
- bNode *node;
- Image *ima;
int type = 0;
- ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM);
+ Image *ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM);
if (!ima) {
return OPERATOR_CANCELLED;
}
@@ -645,7 +580,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, nullptr, type, snode.runtime->cursor[0], snode.runtime->cursor[1]);
+ bNode *node = add_static_node(*C, type, snode.runtime->cursor);
if (!node) {
BKE_report(op->reports, RPT_WARNING, "Could not add an image node");
@@ -690,8 +625,8 @@ static int node_add_file_invoke(bContext *C, wmOperator *op, const wmEvent *even
snode->runtime->cursor[0] /= UI_DPI_FAC;
snode->runtime->cursor[1] /= UI_DPI_FAC;
- if (RNA_struct_property_is_set(op->ptr, "filepath") ||
- RNA_struct_property_is_set(op->ptr, "name")) {
+ if (WM_operator_properties_id_lookup_is_set(op->ptr) ||
+ RNA_struct_property_is_set(op->ptr, "filepath")) {
return node_add_file_exec(C, op);
}
return WM_operator_filesel(C, op, event);
@@ -739,7 +674,6 @@ 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 = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_MSK);
if (!mask) {
@@ -748,8 +682,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, nullptr, CMP_NODE_MASK, snode.runtime->cursor[0], snode.runtime->cursor[1]);
+ bNode *node = add_static_node(*C, CMP_NODE_MASK, snode.runtime->cursor);
if (!node) {
BKE_report(op->reports, RPT_WARNING, "Could not add a mask node");
@@ -877,4 +810,37 @@ void NODE_OT_new_node_tree(wmOperatorType *ot)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Add Node Search
+ * \{ */
+
+static int node_add_search_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ const ARegion &region = *CTX_wm_region(C);
+
+ float2 cursor;
+ UI_view2d_region_to_view(&region.v2d, event->mval[0], event->mval[1], &cursor.x, &cursor.y);
+
+ invoke_add_node_search_menu(*C, cursor, RNA_boolean_get(op->ptr, "use_transform"));
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_add_search(wmOperatorType *ot)
+{
+ ot->name = "Search and Add Node";
+ ot->idname = "NODE_OT_add_search";
+ ot->description = "Search for nodes and add one to the active tree";
+
+ ot->invoke = node_add_search_invoke;
+ ot->poll = ED_operator_node_editable;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_boolean(
+ ot->srna, "use_transform", true, "Use Transform", "Start moving the node after adding it");
+}
+
+/** \} */
+
} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc
index b9bee3ed15e..4f7497b5f49 100644
--- a/source/blender/editors/space_node/node_context_path.cc
+++ b/source/blender/editors/space_node/node_context_path.cc
@@ -28,27 +28,26 @@
#include "node_intern.hh"
-struct Curve;
-struct Light;
struct Material;
-struct Mesh;
-struct World;
namespace blender::ed::space_node {
static void context_path_add_object_data(Vector<ui::ContextPathItem> &path, Object &object)
{
- if (object.type == OB_MESH && object.data) {
- Mesh *mesh = (Mesh *)object.data;
- ui::context_path_add_generic(path, RNA_Mesh, mesh);
+ if (!object.data) {
+ return;
}
- if (object.type == OB_LAMP && object.data) {
- Light *light = (Light *)object.data;
- ui::context_path_add_generic(path, RNA_Light, light);
+ if (object.type == OB_MESH) {
+ ui::context_path_add_generic(path, RNA_Mesh, object.data);
}
- if (ELEM(object.type, OB_CURVES_LEGACY, OB_FONT, OB_SURF) && object.data) {
- Curve *curve = (Curve *)object.data;
- ui::context_path_add_generic(path, RNA_Curve, curve);
+ else if (object.type == OB_CURVES) {
+ ui::context_path_add_generic(path, RNA_Curves, object.data);
+ }
+ else if (object.type == OB_LAMP) {
+ ui::context_path_add_generic(path, RNA_Light, object.data);
+ }
+ else if (ELEM(object.type, OB_CURVES_LEGACY, OB_FONT, OB_SURF)) {
+ ui::context_path_add_generic(path, RNA_Curve, object.data);
}
}
@@ -71,8 +70,7 @@ static void get_context_path_node_shader(const bContext &C,
Scene *scene = CTX_data_scene(&C);
ui::context_path_add_generic(path, RNA_Scene, scene);
if (scene != nullptr) {
- World *world = scene->world;
- ui::context_path_add_generic(path, RNA_World, world);
+ ui::context_path_add_generic(path, RNA_World, scene->world);
}
/* Skip the base node tree here, because the world contains a node tree already. */
context_path_add_node_tree_and_node_groups(snode, path, true);
@@ -95,8 +93,7 @@ static void get_context_path_node_shader(const bContext &C,
Scene *scene = CTX_data_scene(&C);
ui::context_path_add_generic(path, RNA_Scene, scene);
if (scene != nullptr) {
- World *world = scene->world;
- ui::context_path_add_generic(path, RNA_World, world);
+ ui::context_path_add_generic(path, RNA_World, scene->world);
}
}
#ifdef WITH_FREESTYLE
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index b879219e39c..937db9951b4 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -13,6 +13,7 @@
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
+#include "DNA_modifier_types.h"
#include "DNA_node_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -29,11 +30,14 @@
#include "BLT_translation.h"
+#include "BKE_compute_contexts.hh"
#include "BKE_context.h"
#include "BKE_idtype.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
+#include "BKE_node_tree_update.h"
#include "BKE_object.h"
#include "DEG_depsgraph.h"
@@ -64,7 +68,8 @@
#include "RNA_access.h"
#include "RNA_prototypes.h"
-#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_geometry_exec.hh"
+#include "NOD_geometry_nodes_log.hh"
#include "NOD_node_declaration.hh"
#include "NOD_socket_declarations_geometry.hh"
@@ -73,10 +78,11 @@
#include "node_intern.hh" /* own include */
+namespace geo_log = blender::nodes::geo_eval_log;
+
using blender::GPointer;
+using blender::Vector;
using blender::fn::GField;
-namespace geo_log = blender::nodes::geometry_nodes_eval_log;
-using geo_log::eNamedAttrUsage;
extern "C" {
/* XXX interface.h */
@@ -84,6 +90,17 @@ extern void ui_draw_dropshadow(
const rctf *rct, float radius, float aspect, float alpha, int select);
}
+/**
+ * This is passed to many functions which draw the node editor.
+ */
+struct TreeDrawContext {
+ /**
+ * Geometry nodes logs various data during execution. The logged data that corresponds to the
+ * currently drawn node tree can be retrieved from the log below.
+ */
+ geo_log::GeoTreeLog *geo_tree_log = nullptr;
+};
+
float ED_node_grid_size()
{
return U.widget_unit;
@@ -156,6 +173,12 @@ void ED_node_tag_update_id(ID *id)
namespace blender::ed::space_node {
+static void node_socket_add_tooltip_in_node_editor(TreeDrawContext * /*tree_draw_ctx*/,
+ const bNodeTree *ntree,
+ const bNode *node,
+ const bNodeSocket *sock,
+ uiLayout *layout);
+
static bool compare_nodes(const bNode *a, const bNode *b)
{
/* These tell if either the node or any of the parent nodes is selected.
@@ -248,6 +271,7 @@ void node_sort(bNodeTree &ntree)
b++;
BLI_remlink(&ntree.nodes, tmp);
BLI_insertlinkbefore(&ntree.nodes, node_a, tmp);
+ BKE_ntree_update_tag_node_reordered(&ntree);
}
}
@@ -311,7 +335,11 @@ float2 node_from_view(const bNode &node, const float2 &co)
/**
* Based on settings and sockets in node, set drawing rect info.
*/
-static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, uiBlock &block)
+static void node_update_basis(const bContext &C,
+ TreeDrawContext &tree_draw_ctx,
+ bNodeTree &ntree,
+ bNode &node,
+ uiBlock &block)
{
PointerRNA nodeptr;
RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
@@ -340,13 +368,13 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
bool add_output_space = false;
int buty;
- LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) {
- if (nodeSocketIsHidden(nsock)) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
+ if (nodeSocketIsHidden(socket)) {
continue;
}
PointerRNA sockptr;
- RNA_pointer_create(&ntree.id, &RNA_NodeSocket, nsock, &sockptr);
+ RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr);
uiLayout *layout = UI_block_layout(&block,
UI_LAYOUT_VERTICAL,
@@ -369,10 +397,10 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
/* Align output buttons to the right. */
uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
- const char *socket_label = nodeSocketLabel(nsock);
- nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
+ const char *socket_label = nodeSocketLabel(socket);
+ socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
- node_socket_add_tooltip(&ntree, &node, nsock, row);
+ node_socket_add_tooltip_in_node_editor(&tree_draw_ctx, &ntree, &node, socket, row);
UI_block_align_end(&block);
UI_block_layout_resolve(&block, nullptr, &buty);
@@ -381,11 +409,11 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
buty = min_ii(buty, dy - NODE_DY);
/* Round the socket location to stop it from jiggling. */
- nsock->locx = round(loc.x + NODE_WIDTH(node));
- nsock->locy = round(dy - NODE_DYS);
+ socket->locx = round(loc.x + NODE_WIDTH(node));
+ socket->locy = round(dy - NODE_DYS);
dy = buty;
- if (nsock->next) {
+ if (socket->next) {
dy -= NODE_SOCKDY;
}
@@ -463,20 +491,20 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
}
/* Input sockets. */
- LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) {
- if (nodeSocketIsHidden(nsock)) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
+ if (nodeSocketIsHidden(socket)) {
continue;
}
PointerRNA sockptr;
- RNA_pointer_create(&ntree.id, &RNA_NodeSocket, nsock, &sockptr);
+ RNA_pointer_create(&ntree.id, &RNA_NodeSocket, socket, &sockptr);
/* Add the half the height of a multi-input socket to cursor Y
* to account for the increased height of the taller sockets. */
float multi_input_socket_offset = 0.0f;
- if (nsock->flag & SOCK_MULTI_INPUT) {
- if (nsock->total_inputs > 2) {
- multi_input_socket_offset = (nsock->total_inputs - 2) * NODE_MULTI_INPUT_LINK_GAP;
+ if (socket->flag & SOCK_MULTI_INPUT) {
+ if (socket->total_inputs > 2) {
+ multi_input_socket_offset = (socket->total_inputs - 2) * NODE_MULTI_INPUT_LINK_GAP;
}
}
dy -= multi_input_socket_offset * 0.5f;
@@ -501,10 +529,10 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
uiLayout *row = uiLayoutRow(layout, true);
- const char *socket_label = nodeSocketLabel(nsock);
- nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
+ const char *socket_label = nodeSocketLabel(socket);
+ socket->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label));
- node_socket_add_tooltip(&ntree, &node, nsock, row);
+ node_socket_add_tooltip_in_node_editor(&tree_draw_ctx, &ntree, &node, socket, row);
UI_block_align_end(&block);
UI_block_layout_resolve(&block, nullptr, &buty);
@@ -512,12 +540,12 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
/* Ensure minimum socket height in case layout is empty. */
buty = min_ii(buty, dy - NODE_DY);
- nsock->locx = loc.x;
+ socket->locx = loc.x;
/* Round the socket vertical position to stop it from jiggling. */
- nsock->locy = round(dy - NODE_DYS);
+ socket->locy = round(dy - NODE_DYS);
dy = buty - multi_input_socket_offset * 0.5;
- if (nsock->next) {
+ if (socket->next) {
dy -= NODE_SOCKDY;
}
}
@@ -555,13 +583,13 @@ static void node_update_hidden(bNode &node, uiBlock &block)
loc.y = round(loc.y);
/* Calculate minimal radius. */
- LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) {
- if (!nodeSocketIsHidden(nsock)) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
+ if (!nodeSocketIsHidden(socket)) {
totin++;
}
}
- LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) {
- if (!nodeSocketIsHidden(nsock)) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
+ if (!nodeSocketIsHidden(socket)) {
totout++;
}
}
@@ -581,11 +609,11 @@ static void node_update_hidden(bNode &node, uiBlock &block)
float rad = (float)M_PI / (1.0f + (float)totout);
float drad = rad;
- LISTBASE_FOREACH (bNodeSocket *, nsock, &node.outputs) {
- if (!nodeSocketIsHidden(nsock)) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.outputs) {
+ if (!nodeSocketIsHidden(socket)) {
/* Round the socket location to stop it from jiggling. */
- nsock->locx = round(node.totr.xmax - hiddenrad + sinf(rad) * hiddenrad);
- nsock->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad);
+ socket->locx = round(node.totr.xmax - hiddenrad + sinf(rad) * hiddenrad);
+ socket->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad);
rad += drad;
}
}
@@ -593,11 +621,11 @@ static void node_update_hidden(bNode &node, uiBlock &block)
/* Input sockets. */
rad = drad = -(float)M_PI / (1.0f + (float)totin);
- LISTBASE_FOREACH (bNodeSocket *, nsock, &node.inputs) {
- if (!nodeSocketIsHidden(nsock)) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
+ if (!nodeSocketIsHidden(socket)) {
/* Round the socket location to stop it from jiggling. */
- nsock->locx = round(node.totr.xmin + hiddenrad + sinf(rad) * hiddenrad);
- nsock->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad);
+ socket->locx = round(node.totr.xmin + hiddenrad + sinf(rad) * hiddenrad);
+ socket->locy = round(node.totr.ymin + hiddenrad + cosf(rad) * hiddenrad);
rad += drad;
}
}
@@ -663,7 +691,9 @@ static void node_draw_mute_line(const bContext &C,
GPU_blend(GPU_BLEND_ALPHA);
LISTBASE_FOREACH (const bNodeLink *, link, &node.internal_links) {
- node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
+ if (!nodeLinkIsHidden(link)) {
+ node_draw_link_bezier(C, v2d, snode, *link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE, false);
+ }
}
GPU_blend(GPU_BLEND_NONE);
@@ -718,8 +748,7 @@ static void node_socket_draw_multi_input(const float color[4],
const float color_outline[4],
const float width,
const float height,
- const int locx,
- const int locy)
+ const float2 location)
{
/* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness
* that can be varied but always scales with the size the socket is drawn at. Using `U.dpi_fac`
@@ -729,10 +758,10 @@ static void node_socket_draw_multi_input(const float color[4],
/* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */
const rctf rect = {
- locx - width + outline_width * 0.5f,
- locx + width - outline_width * 0.5f,
- locy - height + outline_width * 0.5f,
- locy + height - outline_width * 0.5f,
+ location.x - width + outline_width * 0.5f,
+ location.x + width - outline_width * 0.5f,
+ location.y - height + outline_width * 0.5f,
+ location.y + height - outline_width * 0.5f,
};
UI_draw_roundbox_corner_set(UI_CNR_ALL);
@@ -756,6 +785,7 @@ static void node_socket_outline_color_get(const bool selected,
}
else {
UI_GetThemeColor4fv(TH_WIRE, r_outline_color);
+ r_outline_color[3] = 1.0f;
}
}
@@ -773,9 +803,9 @@ void node_socket_color_get(const bContext &C,
}
struct SocketTooltipData {
- bNodeTree *ntree;
- bNode *node;
- bNodeSocket *socket;
+ const bNodeTree *ntree;
+ const bNode *node;
+ const bNodeSocket *socket;
};
static void create_inspection_string_for_generic_value(const GPointer value, std::stringstream &ss)
@@ -819,25 +849,16 @@ static void create_inspection_string_for_generic_value(const GPointer value, std
}
}
-static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &value_log,
- std::stringstream &ss)
+static void create_inspection_string_for_field_info(const geo_log::FieldInfoLog &value_log,
+ std::stringstream &ss)
{
- const CPPType &type = value_log.type();
- const GField &field = value_log.field();
- const Span<std::string> input_tooltips = value_log.input_tooltips();
+ const CPPType &type = value_log.type;
+ const Span<std::string> input_tooltips = value_log.input_tooltips;
if (input_tooltips.is_empty()) {
- if (field) {
- BUFFER_FOR_CPP_TYPE_VALUE(type, buffer);
- blender::fn::evaluate_constant_field(field, buffer);
- create_inspection_string_for_generic_value({type, buffer}, ss);
- type.destruct(buffer);
- }
- else {
- /* Constant values should always be logged. */
- BLI_assert_unreachable();
- ss << "Value has not been logged";
- }
+ /* Should have been logged as constant value. */
+ BLI_assert_unreachable();
+ ss << "Value has not been logged";
}
else {
if (type.is<int>()) {
@@ -870,11 +891,11 @@ static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &v
}
}
-static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log,
- std::stringstream &ss,
- const nodes::decl::Geometry *geometry)
+static void create_inspection_string_for_geometry_info(const geo_log::GeometryInfoLog &value_log,
+ std::stringstream &ss,
+ const nodes::decl::Geometry *socket_decl)
{
- Span<GeometryComponentType> component_types = value_log.component_types();
+ Span<GeometryComponentType> component_types = value_log.component_types;
if (component_types.is_empty()) {
ss << TIP_("Empty Geometry");
return;
@@ -891,7 +912,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
const char *line_end = (type == component_types.last()) ? "" : ".\n";
switch (type) {
case GEO_COMPONENT_TYPE_MESH: {
- const geo_log::GeometryValueLog::MeshInfo &mesh_info = *value_log.mesh_info;
+ const geo_log::GeometryInfoLog::MeshInfo &mesh_info = *value_log.mesh_info;
char line[256];
BLI_snprintf(line,
sizeof(line),
@@ -903,7 +924,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
break;
}
case GEO_COMPONENT_TYPE_POINT_CLOUD: {
- const geo_log::GeometryValueLog::PointCloudInfo &pointcloud_info =
+ const geo_log::GeometryInfoLog::PointCloudInfo &pointcloud_info =
*value_log.pointcloud_info;
char line[256];
BLI_snprintf(line,
@@ -914,7 +935,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
- const geo_log::GeometryValueLog::CurveInfo &curve_info = *value_log.curve_info;
+ const geo_log::GeometryInfoLog::CurveInfo &curve_info = *value_log.curve_info;
char line[256];
BLI_snprintf(line,
sizeof(line),
@@ -924,7 +945,7 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
break;
}
case GEO_COMPONENT_TYPE_INSTANCES: {
- const geo_log::GeometryValueLog::InstancesInfo &instances_info = *value_log.instances_info;
+ const geo_log::GeometryInfoLog::InstancesInfo &instances_info = *value_log.instances_info;
char line[256];
BLI_snprintf(line,
sizeof(line),
@@ -937,16 +958,29 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
ss << TIP_("\u2022 Volume") << line_end;
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ if (value_log.edit_data_info.has_value()) {
+ const geo_log::GeometryInfoLog::EditDataInfo &edit_info = *value_log.edit_data_info;
+ char line[256];
+ BLI_snprintf(line,
+ sizeof(line),
+ TIP_("\u2022 Edit Curves: %s, %s"),
+ edit_info.has_deformed_positions ? TIP_("positions") : TIP_("no positions"),
+ edit_info.has_deform_matrices ? TIP_("matrices") : TIP_("no matrices"));
+ ss << line << line_end;
+ }
+ break;
+ }
}
}
/* If the geometry declaration is null, as is the case for input to group output,
* or it is an output socket don't show supported types. */
- if (geometry == nullptr || geometry->in_out() == SOCK_OUT) {
+ if (socket_decl == nullptr || socket_decl->in_out() == SOCK_OUT) {
return;
}
- Span<GeometryComponentType> supported_types = geometry->supported_types();
+ Span<GeometryComponentType> supported_types = socket_decl->supported_types();
if (supported_types.is_empty()) {
ss << ".\n\n" << TIP_("Supported: All Types");
return;
@@ -975,69 +1009,74 @@ static void create_inspection_string_for_geometry(const geo_log::GeometryValueLo
ss << TIP_("Volume");
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ break;
+ }
}
ss << ((type == supported_types.last()) ? "" : ", ");
}
}
-static std::optional<std::string> create_socket_inspection_string(bContext *C,
- bNode &node,
- bNodeSocket &socket)
+static std::optional<std::string> create_socket_inspection_string(TreeDrawContext &tree_draw_ctx,
+ const bNodeSocket &socket)
{
- SpaceNode *snode = CTX_wm_space_node(C);
- if (snode == nullptr) {
- return {};
- };
-
- const geo_log::SocketLog *socket_log = geo_log::ModifierLog::find_socket_by_node_editor_context(
- *snode, node, socket);
- if (socket_log == nullptr) {
- return {};
- }
- const geo_log::ValueLog *value_log = socket_log->value();
+ using namespace blender::nodes::geo_eval_log;
+ tree_draw_ctx.geo_tree_log->ensure_socket_values();
+ ValueLog *value_log = tree_draw_ctx.geo_tree_log->find_socket_value_log(socket);
if (value_log == nullptr) {
- return {};
+ return std::nullopt;
}
-
std::stringstream ss;
if (const geo_log::GenericValueLog *generic_value_log =
dynamic_cast<const geo_log::GenericValueLog *>(value_log)) {
- create_inspection_string_for_generic_value(generic_value_log->value(), ss);
+ create_inspection_string_for_generic_value(generic_value_log->value, ss);
}
- if (const geo_log::GFieldValueLog *gfield_value_log =
- dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) {
- create_inspection_string_for_gfield(*gfield_value_log, ss);
+ else if (const geo_log::FieldInfoLog *gfield_value_log =
+ dynamic_cast<const geo_log::FieldInfoLog *>(value_log)) {
+ create_inspection_string_for_field_info(*gfield_value_log, ss);
}
- else if (const geo_log::GeometryValueLog *geo_value_log =
- dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) {
- create_inspection_string_for_geometry(
+ else if (const geo_log::GeometryInfoLog *geo_value_log =
+ dynamic_cast<const geo_log::GeometryInfoLog *>(value_log)) {
+ create_inspection_string_for_geometry_info(
*geo_value_log,
ss,
dynamic_cast<const nodes::decl::Geometry *>(socket.runtime->declaration));
}
- return ss.str();
+ std::string str = ss.str();
+ if (str.empty()) {
+ return std::nullopt;
+ }
+ return str;
}
-static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket)
+static bool node_socket_has_tooltip(const bNodeTree &ntree, const bNodeSocket &socket)
{
- if (ntree->type == NTREE_GEOMETRY) {
+ if (ntree.type == NTREE_GEOMETRY) {
return true;
}
- if (socket->runtime->declaration != nullptr) {
- const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration;
+ if (socket.runtime->declaration != nullptr) {
+ const nodes::SocketDeclaration &socket_decl = *socket.runtime->declaration;
return !socket_decl.description().is_empty();
}
return false;
}
-static char *node_socket_get_tooltip(bContext *C,
- bNodeTree *ntree,
- bNode *node,
- bNodeSocket *socket)
+static char *node_socket_get_tooltip(const bContext *C,
+ const bNodeTree *ntree,
+ const bNode *UNUSED(node),
+ const bNodeSocket *socket)
{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ TreeDrawContext tree_draw_ctx;
+ if (snode != nullptr) {
+ if (ntree->type == NTREE_GEOMETRY) {
+ tree_draw_ctx.geo_tree_log = geo_log::GeoModifierLog::get_tree_log_for_node_editor(*snode);
+ }
+ }
+
std::stringstream output;
if (socket->runtime->declaration != nullptr) {
const blender::nodes::SocketDeclaration &socket_decl = *socket->runtime->declaration;
@@ -1047,13 +1086,13 @@ static char *node_socket_get_tooltip(bContext *C,
}
}
- if (ntree->type == NTREE_GEOMETRY) {
+ if (ntree->type == NTREE_GEOMETRY && tree_draw_ctx.geo_tree_log != nullptr) {
if (!output.str().empty()) {
output << ".\n\n";
}
std::optional<std::string> socket_inspection_str = create_socket_inspection_string(
- C, *node, *socket);
+ tree_draw_ctx, *socket);
if (socket_inspection_str.has_value()) {
output << *socket_inspection_str;
}
@@ -1069,9 +1108,13 @@ static char *node_socket_get_tooltip(bContext *C,
return BLI_strdup(output.str().c_str());
}
-void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout)
+static void node_socket_add_tooltip_in_node_editor(TreeDrawContext *UNUSED(tree_draw_ctx),
+ const bNodeTree *ntree,
+ const bNode *node,
+ const bNodeSocket *sock,
+ uiLayout *layout)
{
- if (!node_socket_has_tooltip(ntree, sock)) {
+ if (!node_socket_has_tooltip(*ntree, *sock)) {
return;
}
@@ -1091,6 +1134,14 @@ void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, u
MEM_freeN);
}
+void node_socket_add_tooltip(const bNodeTree &ntree,
+ const bNode &node,
+ const bNodeSocket &sock,
+ uiLayout &layout)
+{
+ node_socket_add_tooltip_in_node_editor(nullptr, &ntree, &node, &sock, &layout);
+}
+
static void node_socket_draw_nested(const bContext &C,
bNodeTree &ntree,
PointerRNA &node_ptr,
@@ -1104,9 +1155,10 @@ static void node_socket_draw_nested(const bContext &C,
const float size,
const bool selected)
{
+ const float2 location(sock.locx, sock.locy);
+
float color[4];
float outline_color[4];
-
node_socket_color_get(C, ntree, node_ptr, sock, color);
node_socket_outline_color_get(selected, sock.type, outline_color);
@@ -1114,15 +1166,15 @@ static void node_socket_draw_nested(const bContext &C,
color,
outline_color,
size,
- sock.locx,
- sock.locy,
+ location.x,
+ location.y,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id);
- if (!node_socket_has_tooltip(&ntree, &sock)) {
+ if (!node_socket_has_tooltip(ntree, sock)) {
return;
}
@@ -1134,8 +1186,8 @@ static void node_socket_draw_nested(const bContext &C,
UI_BTYPE_BUT,
0,
ICON_NONE,
- sock.locx - size / 2,
- sock.locy - size / 2,
+ location.x - size / 2.0f,
+ location.y - size / 2.0f,
size,
size,
nullptr,
@@ -1145,9 +1197,9 @@ static void node_socket_draw_nested(const bContext &C,
0,
nullptr);
- SocketTooltipData *data = (SocketTooltipData *)MEM_mallocN(sizeof(SocketTooltipData), __func__);
+ SocketTooltipData *data = MEM_new<SocketTooltipData>(__func__);
data->ntree = &ntree;
- data->node = (bNode *)node_ptr.data;
+ data->node = static_cast<const bNode *>(node_ptr.data);
data->socket = &sock;
UI_but_func_tooltip_set(
@@ -1266,7 +1318,7 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv)
/* Premul graphics. */
GPU_blend(GPU_BLEND_ALPHA);
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_3D_IMAGE_COLOR);
immDrawPixelsTexTiled(&state,
draw_rect.xmin,
draw_rect.ymin,
@@ -1282,14 +1334,14 @@ static void node_draw_preview(bNodePreview *preview, rctf *prv)
GPU_blend(GPU_BLEND_NONE);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShadeAlpha(TH_BACK, -15, +100);
imm_draw_box_wire_2d(pos, draw_rect.xmin, draw_rect.ymin, draw_rect.xmax, draw_rect.ymax);
immUnbindProgram();
}
/* Common handle function for operator buttons that need to select the node first. */
-static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_argv)
+static void node_toggle_button_cb(bContext *C, void *node_argv, void *op_argv)
{
bNode *node = (bNode *)node_argv;
const char *opname = (const char *)op_argv;
@@ -1508,7 +1560,8 @@ static void node_draw_sockets(const View2D &v2d,
node_socket_color_get(C, ntree, node_ptr, *socket, color);
node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color);
- node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy);
+ const float2 location(socket->locx, socket->locy);
+ node_socket_draw_multi_input(color, outline_color, width, height, location);
}
}
@@ -1582,27 +1635,26 @@ static char *node_errors_tooltip_fn(bContext *UNUSED(C), void *argN, const char
#define NODE_HEADER_ICON_SIZE (0.8f * U.widget_unit)
-static void node_add_error_message_button(
- const bContext &C, bNode &node, uiBlock &block, const rctf &rect, float &icon_offset)
+static void node_add_error_message_button(TreeDrawContext &tree_draw_ctx,
+ bNode &node,
+ uiBlock &block,
+ const rctf &rect,
+ float &icon_offset)
{
- SpaceNode *snode = CTX_wm_space_node(&C);
- const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(*snode,
- node);
- if (node_log == nullptr) {
- return;
+ Span<geo_log::NodeWarning> warnings;
+ if (tree_draw_ctx.geo_tree_log) {
+ geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.name);
+ if (node_log != nullptr) {
+ warnings = node_log->warnings;
+ }
}
-
- Span<geo_log::NodeWarning> warnings = node_log->warnings();
-
if (warnings.is_empty()) {
return;
}
- NodeErrorsTooltipData *tooltip_data = (NodeErrorsTooltipData *)MEM_mallocN(
- sizeof(NodeErrorsTooltipData), __func__);
- tooltip_data->warnings = warnings;
-
const geo_log::NodeWarningType display_type = node_error_highest_priority(warnings);
+ NodeErrorsTooltipData *tooltip_data = MEM_new<NodeErrorsTooltipData>(__func__);
+ tooltip_data->warnings = warnings;
icon_offset -= NODE_HEADER_ICON_SIZE;
UI_block_emboss_set(&block, UI_EMBOSS_NONE);
@@ -1620,90 +1672,70 @@ static void node_add_error_message_button(
0,
0,
nullptr);
- UI_but_func_tooltip_set(but, node_errors_tooltip_fn, tooltip_data, MEM_freeN);
+ UI_but_func_tooltip_set(but, node_errors_tooltip_fn, tooltip_data, [](void *arg) {
+ MEM_delete(static_cast<NodeErrorsTooltipData *>(arg));
+ });
UI_block_emboss_set(&block, UI_EMBOSS);
}
-static void get_exec_time_other_nodes(const bNode &node,
- const SpaceNode &snode,
- std::chrono::microseconds &exec_time,
- int &node_count)
+static std::optional<std::chrono::nanoseconds> node_get_execution_time(
+ TreeDrawContext &tree_draw_ctx, const bNodeTree &ntree, const bNode &node)
{
- if (node.type == NODE_GROUP) {
- const geo_log::TreeLog *root_tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context(
- snode);
- if (root_tree_log == nullptr) {
- return;
- }
- const geo_log::TreeLog *tree_log = root_tree_log->lookup_child_log(node.name);
- if (tree_log == nullptr) {
- return;
- }
- tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) {
- exec_time += node_log.execution_time();
- node_count++;
- });
+ const geo_log::GeoTreeLog *tree_log = tree_draw_ctx.geo_tree_log;
+ if (tree_log == nullptr) {
+ return std::nullopt;
}
- else {
- const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(
- snode, node);
- if (node_log) {
- exec_time += node_log->execution_time();
- node_count++;
- }
- }
-}
-
-static std::chrono::microseconds node_get_execution_time(const bNodeTree &ntree,
- const bNode &node,
- const SpaceNode &snode,
- int &node_count)
-{
- std::chrono::microseconds exec_time = std::chrono::microseconds::zero();
if (node.type == NODE_GROUP_OUTPUT) {
- const geo_log::TreeLog *tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context(
- snode);
-
- if (tree_log == nullptr) {
- return exec_time;
- }
- tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) {
- exec_time += node_log.execution_time();
- node_count++;
- });
+ return tree_log->run_time_sum;
}
- else if (node.type == NODE_FRAME) {
+ if (node.type == NODE_FRAME) {
/* Could be cached in the future if this recursive code turns out to be slow. */
+ std::chrono::nanoseconds run_time{0};
+ bool found_node = false;
LISTBASE_FOREACH (bNode *, tnode, &ntree.nodes) {
if (tnode->parent != &node) {
continue;
}
if (tnode->type == NODE_FRAME) {
- exec_time += node_get_execution_time(ntree, *tnode, snode, node_count);
+ std::optional<std::chrono::nanoseconds> sub_frame_run_time = node_get_execution_time(
+ tree_draw_ctx, ntree, *tnode);
+ if (sub_frame_run_time.has_value()) {
+ run_time += *sub_frame_run_time;
+ found_node = true;
+ }
}
else {
- get_exec_time_other_nodes(*tnode, snode, exec_time, node_count);
+ if (const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr_as(tnode->name)) {
+ found_node = true;
+ run_time += node_log->run_time;
+ }
}
}
+ if (found_node) {
+ return run_time;
+ }
+ return std::nullopt;
}
- else {
- get_exec_time_other_nodes(node, snode, exec_time, node_count);
+ if (const geo_log::GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node.name)) {
+ return node_log->run_time;
}
- return exec_time;
+ return std::nullopt;
}
-static std::string node_get_execution_time_label(const SpaceNode &snode, const bNode &node)
+static std::string node_get_execution_time_label(TreeDrawContext &tree_draw_ctx,
+ const SpaceNode &snode,
+ const bNode &node)
{
- int node_count = 0;
- std::chrono::microseconds exec_time = node_get_execution_time(
- *snode.nodetree, node, snode, node_count);
+ const std::optional<std::chrono::nanoseconds> exec_time = node_get_execution_time(
+ tree_draw_ctx, *snode.edittree, node);
- if (node_count == 0) {
+ if (!exec_time.has_value()) {
return std::string("");
}
- uint64_t exec_time_us = exec_time.count();
+ const uint64_t exec_time_us =
+ std::chrono::duration_cast<std::chrono::microseconds>(*exec_time).count();
/* Don't show time if execution time is 0 microseconds. */
if (exec_time_us == 0) {
@@ -1738,7 +1770,7 @@ struct NodeExtraInfoRow {
};
struct NamedAttributeTooltipArg {
- Map<std::string, eNamedAttrUsage> usage_by_attribute;
+ Map<StringRefNull, geo_log::NamedAttributeUsage> usage_by_attribute;
};
static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char *UNUSED(tip))
@@ -1750,7 +1782,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char
struct NameWithUsage {
StringRefNull name;
- eNamedAttrUsage usage;
+ geo_log::NamedAttributeUsage usage;
};
Vector<NameWithUsage> sorted_used_attribute;
@@ -1765,16 +1797,16 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char
for (const NameWithUsage &attribute : sorted_used_attribute) {
const StringRefNull name = attribute.name;
- const eNamedAttrUsage usage = attribute.usage;
+ const geo_log::NamedAttributeUsage usage = attribute.usage;
ss << " \u2022 \"" << name << "\": ";
Vector<std::string> usages;
- if ((usage & eNamedAttrUsage::Read) != eNamedAttrUsage::None) {
+ if ((usage & geo_log::NamedAttributeUsage::Read) != geo_log::NamedAttributeUsage::None) {
usages.append(TIP_("read"));
}
- if ((usage & eNamedAttrUsage::Write) != eNamedAttrUsage::None) {
+ if ((usage & geo_log::NamedAttributeUsage::Write) != geo_log::NamedAttributeUsage::None) {
usages.append(TIP_("write"));
}
- if ((usage & eNamedAttrUsage::Remove) != eNamedAttrUsage::None) {
+ if ((usage & geo_log::NamedAttributeUsage::Remove) != geo_log::NamedAttributeUsage::None) {
usages.append(TIP_("remove"));
}
for (const int i : usages.index_range()) {
@@ -1792,7 +1824,7 @@ static char *named_attribute_tooltip(bContext *UNUSED(C), void *argN, const char
}
static NodeExtraInfoRow row_from_used_named_attribute(
- const Map<std::string, eNamedAttrUsage> &usage_by_attribute_name)
+ const Map<StringRefNull, geo_log::NamedAttributeUsage> &usage_by_attribute_name)
{
const int attributes_num = usage_by_attribute_name.size();
@@ -1806,32 +1838,11 @@ static NodeExtraInfoRow row_from_used_named_attribute(
return row;
}
-static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const SpaceNode &snode,
- const bNode &node)
+static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(
+ TreeDrawContext &tree_draw_ctx, const bNode &node)
{
- if (node.type == NODE_GROUP) {
- const geo_log::TreeLog *root_tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context(
- snode);
- if (root_tree_log == nullptr) {
- return std::nullopt;
- }
- const geo_log::TreeLog *tree_log = root_tree_log->lookup_child_log(node.name);
- if (tree_log == nullptr) {
- return std::nullopt;
- }
-
- Map<std::string, eNamedAttrUsage> usage_by_attribute;
- tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) {
- for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) {
- usage_by_attribute.lookup_or_add_as(used_attribute.name,
- used_attribute.usage) |= used_attribute.usage;
- }
- });
- if (usage_by_attribute.is_empty()) {
- return std::nullopt;
- }
-
- return row_from_used_named_attribute(usage_by_attribute);
+ if (tree_draw_ctx.geo_tree_log == nullptr) {
+ return std::nullopt;
}
if (ELEM(node.type,
GEO_NODE_STORE_NAMED_ATTRIBUTE,
@@ -1840,31 +1851,26 @@ static std::optional<NodeExtraInfoRow> node_get_accessed_attributes_row(const Sp
/* Only show the overlay when the name is passed in from somewhere else. */
LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
if (STREQ(socket->name, "Name")) {
- if ((socket->flag & SOCK_IN_USE) == 0) {
+ if (!socket->is_directly_linked()) {
return std::nullopt;
}
}
}
- const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(
- snode, node.name);
- if (node_log == nullptr) {
- return std::nullopt;
- }
- Map<std::string, eNamedAttrUsage> usage_by_attribute;
- for (const geo_log::UsedNamedAttribute &used_attribute : node_log->used_named_attributes()) {
- usage_by_attribute.lookup_or_add_as(used_attribute.name,
- used_attribute.usage) |= used_attribute.usage;
- }
- if (usage_by_attribute.is_empty()) {
- return std::nullopt;
- }
- return row_from_used_named_attribute(usage_by_attribute);
}
-
- return std::nullopt;
+ tree_draw_ctx.geo_tree_log->ensure_used_named_attributes();
+ geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.name);
+ if (node_log == nullptr) {
+ return std::nullopt;
+ }
+ if (node_log->used_named_attributes.is_empty()) {
+ return std::nullopt;
+ }
+ return row_from_used_named_attribute(node_log->used_named_attributes);
}
-static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, const bNode &node)
+static Vector<NodeExtraInfoRow> node_get_extra_info(TreeDrawContext &tree_draw_ctx,
+ const SpaceNode &snode,
+ const bNode &node)
{
Vector<NodeExtraInfoRow> rows;
if (!(snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS)) {
@@ -1873,7 +1879,8 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, cons
if (snode.overlay.flag & SN_OVERLAY_SHOW_NAMED_ATTRIBUTES &&
snode.edittree->type == NTREE_GEOMETRY) {
- if (std::optional<NodeExtraInfoRow> row = node_get_accessed_attributes_row(snode, node)) {
+ if (std::optional<NodeExtraInfoRow> row = node_get_accessed_attributes_row(tree_draw_ctx,
+ node)) {
rows.append(std::move(*row));
}
}
@@ -1882,7 +1889,7 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, cons
(ELEM(node.typeinfo->nclass, NODE_CLASS_GEOMETRY, NODE_CLASS_GROUP, NODE_CLASS_ATTRIBUTE) ||
ELEM(node.type, NODE_FRAME, NODE_GROUP_OUTPUT))) {
NodeExtraInfoRow row;
- row.text = node_get_execution_time_label(snode, node);
+ row.text = node_get_execution_time_label(tree_draw_ctx, snode, node);
if (!row.text.empty()) {
row.tooltip = TIP_(
"The execution time from the node tree's latest evaluation. For frame and group nodes, "
@@ -1891,14 +1898,17 @@ static Vector<NodeExtraInfoRow> node_get_extra_info(const SpaceNode &snode, cons
rows.append(std::move(row));
}
}
- const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(snode,
- node);
- if (node_log != nullptr) {
- for (const std::string &message : node_log->debug_messages()) {
- NodeExtraInfoRow row;
- row.text = message;
- row.icon = ICON_INFO;
- rows.append(std::move(row));
+
+ if (snode.edittree->type == NTREE_GEOMETRY && tree_draw_ctx.geo_tree_log != nullptr) {
+ tree_draw_ctx.geo_tree_log->ensure_debug_messages();
+ const geo_log::GeoNodeLog *node_log = tree_draw_ctx.geo_tree_log->nodes.lookup_ptr(node.name);
+ if (node_log != nullptr) {
+ for (const StringRef message : node_log->debug_messages) {
+ NodeExtraInfoRow row;
+ row.text = message;
+ row.icon = ICON_INFO;
+ rows.append(std::move(row));
+ }
}
}
@@ -1963,9 +1973,12 @@ static void node_draw_extra_info_row(const bNode &node,
}
}
-static void node_draw_extra_info_panel(const SpaceNode &snode, const bNode &node, uiBlock &block)
+static void node_draw_extra_info_panel(TreeDrawContext &tree_draw_ctx,
+ const SpaceNode &snode,
+ const bNode &node,
+ uiBlock &block)
{
- Vector<NodeExtraInfoRow> extra_info_rows = node_get_extra_info(snode, node);
+ Vector<NodeExtraInfoRow> extra_info_rows = node_get_extra_info(tree_draw_ctx, snode, node);
if (extra_info_rows.size() == 0) {
return;
@@ -2021,6 +2034,7 @@ static void node_draw_extra_info_panel(const SpaceNode &snode, const bNode &node
}
static void node_draw_basis(const bContext &C,
+ TreeDrawContext &tree_draw_ctx,
const View2D &v2d,
const SpaceNode &snode,
bNodeTree &ntree,
@@ -2045,7 +2059,7 @@ static void node_draw_basis(const bContext &C,
GPU_line_width(1.0f);
- node_draw_extra_info_panel(snode, node, block);
+ node_draw_extra_info_panel(tree_draw_ctx, snode, node, block);
/* Header. */
{
@@ -2140,7 +2154,7 @@ static void node_draw_basis(const bContext &C,
UI_block_emboss_set(&block, UI_EMBOSS);
}
- node_add_error_message_button(C, node, block, rct, iconofs);
+ node_add_error_message_button(tree_draw_ctx, node, block, rct, iconofs);
/* Title. */
if (node.flag & SELECT) {
@@ -2245,6 +2259,7 @@ static void node_draw_basis(const bContext &C,
if (node.flag & NODE_MUTED) {
UI_GetThemeColor4fv(TH_WIRE, color_underline);
+ color_underline[3] = 1.0f;
}
else {
UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.2f, color_underline);
@@ -2312,6 +2327,7 @@ static void node_draw_basis(const bContext &C,
}
static void node_draw_hidden(const bContext &C,
+ TreeDrawContext &tree_draw_ctx,
const View2D &v2d,
const SpaceNode &snode,
bNodeTree &ntree,
@@ -2327,7 +2343,7 @@ static void node_draw_hidden(const bContext &C,
const int color_id = node_get_colorid(node);
- node_draw_extra_info_panel(snode, node, block);
+ node_draw_extra_info_panel(tree_draw_ctx, snode, node, block);
/* Shadow. */
node_draw_shadow(snode, node, hiddenrad, 1.0f);
@@ -2452,7 +2468,7 @@ static void node_draw_hidden(const bContext &C,
/* Scale widget thing. */
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPU_blend(GPU_BLEND_ALPHA);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShadeAlpha(TH_TEXT, -40, -180);
float dx = 0.5f * U.widget_unit;
@@ -2625,13 +2641,13 @@ static void reroute_node_prepare_for_draw(bNode &node)
const float2 loc = node_to_view(node, float2(0));
/* reroute node has exactly one input and one output, both in the same place */
- bNodeSocket *nsock = (bNodeSocket *)node.outputs.first;
- nsock->locx = loc.x;
- nsock->locy = loc.y;
+ bNodeSocket *socket = (bNodeSocket *)node.outputs.first;
+ socket->locx = loc.x;
+ socket->locy = loc.y;
- nsock = (bNodeSocket *)node.inputs.first;
- nsock->locx = loc.x;
- nsock->locy = loc.y;
+ socket = (bNodeSocket *)node.inputs.first;
+ socket->locx = loc.x;
+ socket->locy = loc.y;
const float size = 8.0f;
node.width = size * 2;
@@ -2642,6 +2658,7 @@ static void reroute_node_prepare_for_draw(bNode &node)
}
static void node_update_nodetree(const bContext &C,
+ TreeDrawContext &tree_draw_ctx,
bNodeTree &ntree,
Span<bNode *> nodes,
Span<uiBlock *> blocks)
@@ -2668,7 +2685,7 @@ static void node_update_nodetree(const bContext &C,
node_update_hidden(node, block);
}
else {
- node_update_basis(C, ntree, node, block);
+ node_update_basis(C, tree_draw_ctx, ntree, node, block);
}
}
}
@@ -2748,7 +2765,7 @@ static void frame_node_draw_label(const bNodeTree &ntree,
BLF_wordwrap(fontid, line_width);
LISTBASE_FOREACH (const TextLine *, line, &text->lines) {
- struct ResultBLF info;
+ ResultBLF info;
if (line->line[0]) {
BLF_position(fontid, x, y, 0);
BLF_draw_ex(fontid, line->line, line->len, &info);
@@ -2769,6 +2786,7 @@ static void frame_node_draw_label(const bNodeTree &ntree,
}
static void frame_node_draw(const bContext &C,
+ TreeDrawContext &tree_draw_ctx,
const ARegion &region,
const SpaceNode &snode,
bNodeTree &ntree,
@@ -2815,7 +2833,7 @@ static void frame_node_draw(const bContext &C,
/* label and text */
frame_node_draw_label(ntree, node, snode);
- node_draw_extra_info_panel(snode, node, block);
+ node_draw_extra_info_panel(tree_draw_ctx, snode, node, block);
UI_block_end(&C, &block);
UI_block_draw(&C, &block);
@@ -2869,6 +2887,7 @@ static void reroute_node_draw(
}
static void node_draw(const bContext &C,
+ TreeDrawContext &tree_draw_ctx,
ARegion &region,
const SpaceNode &snode,
bNodeTree &ntree,
@@ -2877,7 +2896,7 @@ static void node_draw(const bContext &C,
bNodeInstanceKey key)
{
if (node.type == NODE_FRAME) {
- frame_node_draw(C, region, snode, ntree, node, block);
+ frame_node_draw(C, tree_draw_ctx, region, snode, ntree, node, block);
}
else if (node.type == NODE_REROUTE) {
reroute_node_draw(C, region, ntree, node, block);
@@ -2885,10 +2904,10 @@ static void node_draw(const bContext &C,
else {
const View2D &v2d = region.v2d;
if (node.flag & NODE_HIDDEN) {
- node_draw_hidden(C, v2d, snode, ntree, node, block);
+ node_draw_hidden(C, tree_draw_ctx, v2d, snode, ntree, node, block);
}
else {
- node_draw_basis(C, v2d, snode, ntree, node, block, key);
+ node_draw_basis(C, tree_draw_ctx, v2d, snode, ntree, node, block, key);
}
}
}
@@ -2896,6 +2915,7 @@ static void node_draw(const bContext &C,
#define USE_DRAW_TOT_UPDATE
static void node_draw_nodetree(const bContext &C,
+ TreeDrawContext &tree_draw_ctx,
ARegion &region,
SpaceNode &snode,
bNodeTree &ntree,
@@ -2920,7 +2940,7 @@ static void node_draw_nodetree(const bContext &C,
}
bNodeInstanceKey key = BKE_node_instance_key(parent_key, &ntree, nodes[i]);
- node_draw(C, region, snode, ntree, *nodes[i], *blocks[i], key);
+ node_draw(C, tree_draw_ctx, region, snode, ntree, *nodes[i], *blocks[i], key);
}
/* Node lines. */
@@ -2950,7 +2970,7 @@ static void node_draw_nodetree(const bContext &C,
}
bNodeInstanceKey key = BKE_node_instance_key(parent_key, &ntree, nodes[i]);
- node_draw(C, region, snode, ntree, *nodes[i], *blocks[i], key);
+ node_draw(C, tree_draw_ctx, region, snode, ntree, *nodes[i], *blocks[i], key);
}
}
@@ -3003,13 +3023,23 @@ static void draw_nodetree(const bContext &C,
bNodeInstanceKey parent_key)
{
SpaceNode *snode = CTX_wm_space_node(&C);
+ ntree.ensure_topology_cache();
- Vector<bNode *> nodes = ntree.nodes;
+ Span<bNode *> nodes = ntree.all_nodes();
Array<uiBlock *> blocks = node_uiblocks_init(C, nodes);
- node_update_nodetree(C, ntree, nodes, blocks);
- node_draw_nodetree(C, region, *snode, ntree, nodes, blocks, parent_key);
+ TreeDrawContext tree_draw_ctx;
+ if (ntree.type == NTREE_GEOMETRY) {
+ tree_draw_ctx.geo_tree_log = geo_log::GeoModifierLog::get_tree_log_for_node_editor(*snode);
+ if (tree_draw_ctx.geo_tree_log != nullptr) {
+ tree_draw_ctx.geo_tree_log->ensure_node_warnings();
+ tree_draw_ctx.geo_tree_log->ensure_node_run_time();
+ }
+ }
+
+ node_update_nodetree(C, tree_draw_ctx, ntree, nodes, blocks);
+ node_draw_nodetree(C, tree_draw_ctx, region, *snode, ntree, nodes, blocks, parent_key);
}
/**
@@ -3084,8 +3114,8 @@ void node_draw_space(const bContext &C, ARegion &region)
}
/* Current View2D center, will be set temporarily for parent node trees. */
- float center[2];
- UI_view2d_center_get(&v2d, &center[0], &center[1]);
+ float2 center;
+ UI_view2d_center_get(&v2d, &center.x, &center.y);
/* Store new view center in path and current edit tree. */
copy_v2_v2(path->view_center, center);
@@ -3124,7 +3154,7 @@ void node_draw_space(const bContext &C, ARegion &region)
GPU_line_smooth(true);
if (snode.runtime->linkdrag) {
for (const bNodeLink *link : snode.runtime->linkdrag->links) {
- node_draw_link(C, v2d, snode, *link, true);
+ node_draw_link_dragged(C, v2d, snode, *link);
}
}
GPU_line_smooth(false);
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index ffc9befc81c..31d99eafbc1 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -29,6 +29,8 @@
#include "BKE_scene.h"
#include "BKE_workspace.h"
+#include "BLT_translation.h"
+
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
@@ -107,8 +109,7 @@ float2 node_link_calculate_multi_input_position(const float2 &socket_position,
{
const float offset = (total_inputs * NODE_MULTI_INPUT_LINK_GAP - NODE_MULTI_INPUT_LINK_GAP) *
0.5f;
- return {socket_position.x - NODE_SOCKSIZE * 0.5f,
- socket_position.y - offset + index * NODE_MULTI_INPUT_LINK_GAP};
+ return {socket_position.x, socket_position.y - offset + index * NODE_MULTI_INPUT_LINK_GAP};
}
static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
@@ -319,7 +320,7 @@ static void compo_completejob(void *cjv)
/** \name Composite Job C API
* \{ */
-void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner)
+void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_owner)
{
using namespace blender::ed::space_node;
@@ -463,22 +464,22 @@ void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo)
}
}
-bool ED_node_is_compositor(struct SpaceNode *snode)
+bool ED_node_is_compositor(SpaceNode *snode)
{
return STREQ(snode->tree_idname, ntreeType_Composite->idname);
}
-bool ED_node_is_shader(struct SpaceNode *snode)
+bool ED_node_is_shader(SpaceNode *snode)
{
return STREQ(snode->tree_idname, ntreeType_Shader->idname);
}
-bool ED_node_is_texture(struct SpaceNode *snode)
+bool ED_node_is_texture(SpaceNode *snode)
{
return STREQ(snode->tree_idname, ntreeType_Texture->idname);
}
-bool ED_node_is_geometry(struct SpaceNode *snode)
+bool ED_node_is_geometry(SpaceNode *snode)
{
return STREQ(snode->tree_idname, ntreeType_Geometry->idname);
}
@@ -505,12 +506,12 @@ void ED_node_shader_default(const bContext *C, ID *id)
}
else if (ELEM(GS(id->name), ID_WO, ID_LA)) {
/* Emission */
- bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ nullptr, id, "Shader Nodetree", ntreeType_Shader->idname);
bNode *shader, *output;
if (GS(id->name) == ID_WO) {
World *world = (World *)id;
- world->nodetree = ntree;
shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_BACKGROUND);
output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_WORLD);
@@ -524,9 +525,6 @@ void ED_node_shader_default(const bContext *C, ID *id)
copy_v3_v3(((bNodeSocketValueRGBA *)color_sock->default_value)->value, &world->horr);
}
else {
- Light *light = (Light *)id;
- light->nodetree = ntree;
-
shader = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION);
output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_LIGHT);
nodeAddLink(ntree,
@@ -549,7 +547,7 @@ void ED_node_shader_default(const bContext *C, ID *id)
}
}
-void ED_node_composit_default(const bContext *C, struct Scene *sce)
+void ED_node_composit_default(const bContext *C, Scene *sce)
{
/* but lets check it anyway */
if (sce->nodetree) {
@@ -559,7 +557,8 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
return;
}
- sce->nodetree = ntreeAddTree(nullptr, "Compositing Nodetree", ntreeType_Composite->idname);
+ sce->nodetree = ntreeAddTreeEmbedded(
+ nullptr, &sce->id, "Compositing Nodetree", ntreeType_Composite->idname);
sce->nodetree->chunksize = 256;
sce->nodetree->edit_quality = NTREE_QUALITY_HIGH;
@@ -592,7 +591,8 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
return;
}
- tex->nodetree = ntreeAddTree(nullptr, "Texture Nodetree", ntreeType_Texture->idname);
+ tex->nodetree = ntreeAddTreeEmbedded(
+ nullptr, &tex->id, "Texture Nodetree", ntreeType_Texture->idname);
bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT);
out->locx = 300.0f;
@@ -713,10 +713,12 @@ void ED_node_set_active(
/* Sync to active texpaint slot, otherwise we can end up painting on a different slot
* than we are looking at. */
if (ma->texpaintslot) {
- Image *image = (Image *)node->id;
- for (int i = 0; i < ma->tot_slots; i++) {
- if (ma->texpaintslot[i].ima == image) {
- ma->paint_active_slot = i;
+ if (node->id != nullptr && GS(node->id->name) == ID_IM) {
+ Image *image = (Image *)node->id;
+ for (int i = 0; i < ma->tot_slots; i++) {
+ if (ma->texpaintslot[i].ima == image) {
+ ma->paint_active_slot = i;
+ }
}
}
}
@@ -729,18 +731,28 @@ void ED_node_set_active(
}
}
- /* Sync to Image Editor. */
- Image *image = (Image *)node->id;
- wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
- LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
- const bScreen *screen = WM_window_get_active_screen(win);
- LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
- LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
- if (sl->spacetype == SPACE_IMAGE) {
+ /* Sync to Image Editor under the following conditions:
+ * - current image is not pinned
+ * - current image is not a Render Result or ViewerNode (want to keep looking at these) */
+ if (node->id != nullptr && GS(node->id->name) == ID_IM) {
+ Image *image = (Image *)node->id;
+ wmWindowManager *wm = (wmWindowManager *)bmain->wm.first;
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
+ const bScreen *screen = WM_window_get_active_screen(win);
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
+ LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
+ if (sl->spacetype != SPACE_IMAGE) {
+ continue;
+ }
SpaceImage *sima = (SpaceImage *)sl;
- if (!sima->pin) {
- ED_space_image_set(bmain, sima, image, true);
+ if (sima->pin) {
+ continue;
}
+ if (sima->image &&
+ ELEM(sima->image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
+ continue;
+ }
+ ED_space_image_set(bmain, sima, image, true);
}
}
}
@@ -913,15 +925,24 @@ static void edit_node_properties_get(
/** \name Node Generic
* \{ */
-/* is rct in visible part of node? */
-static bNode *visible_node(SpaceNode &snode, const rctf &rct)
+static bool socket_is_occluded(const float2 &location,
+ const bNode &node_the_socket_belongs_to,
+ const SpaceNode &snode)
{
LISTBASE_FOREACH_BACKWARD (bNode *, node, &snode.edittree->nodes) {
- if (BLI_rctf_isect(&node->totr, &rct, nullptr)) {
- return node;
+ if (node == &node_the_socket_belongs_to) {
+ /* Nodes after this one are underneath and can't occlude the socket. */
+ return false;
+ }
+
+ rctf socket_hitbox;
+ const float socket_hitbox_radius = NODE_SOCKSIZE - 0.1f * U.widget_unit;
+ BLI_rctf_init_pt_radius(&socket_hitbox, location, socket_hitbox_radius);
+ if (BLI_rctf_inside_rctf(&node->totr, &socket_hitbox)) {
+ return true;
}
}
- return nullptr;
+ return false;
}
/** \} */
@@ -939,14 +960,14 @@ struct NodeSizeWidget {
};
static void node_resize_init(
- bContext *C, wmOperator *op, const float cursor[2], const bNode *node, NodeResizeDirection dir)
+ bContext *C, wmOperator *op, const float2 &cursor, const bNode *node, NodeResizeDirection dir)
{
NodeSizeWidget *nsw = MEM_cnew<NodeSizeWidget>(__func__);
op->customdata = nsw;
- nsw->mxstart = cursor[0];
- nsw->mystart = cursor[1];
+ nsw->mxstart = cursor.x;
+ nsw->mystart = cursor.y;
/* store old */
nsw->oldlocx = node->locx;
@@ -993,12 +1014,12 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
switch (event->type) {
case MOUSEMOVE: {
- int mval[2];
+ int2 mval;
WM_event_drag_start_mval(event, region, mval);
float mx, my;
- UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &mx, &my);
- float dx = (mx - nsw->mxstart) / UI_DPI_FAC;
- float dy = (my - nsw->mystart) / UI_DPI_FAC;
+ UI_view2d_region_to_view(&region->v2d, mval.x, mval.y, &mx, &my);
+ const float dx = (mx - nsw->mxstart) / UI_DPI_FAC;
+ const float dy = (my - nsw->mystart) / UI_DPI_FAC;
if (node) {
float *pwidth = &node->width;
@@ -1080,6 +1101,10 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
break;
}
+ case EVT_ESCKEY:
+ node_resize_exit(C, op, true);
+ ED_region_tag_redraw(region);
+ return OPERATOR_CANCELLED;
}
return OPERATOR_RUNNING_MODAL;
@@ -1096,11 +1121,11 @@ static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
/* convert mouse coordinates to v2d space */
- float cursor[2];
- int mval[2];
+ float2 cursor;
+ int2 mval;
WM_event_drag_start_mval(event, region, mval);
- UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
- const NodeResizeDirection dir = node_get_resize_direction(node, cursor[0], cursor[1]);
+ UI_view2d_region_to_view(&region->v2d, mval.x, mval.y, &cursor.x, &cursor.y);
+ const NodeResizeDirection dir = node_get_resize_direction(node, cursor.x, cursor.y);
if (dir == NODE_RESIZE_NONE) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
@@ -1178,21 +1203,22 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
}
/* checks snode->mouse position, and returns found node/socket */
-static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket &socket)
+static bool cursor_isect_multi_input_socket(const float2 &cursor, const bNodeSocket &socket)
{
const float node_socket_height = node_socket_calculate_height(socket);
- rctf multi_socket_rect;
+ const float2 location(socket.locx, socket.locy);
/* `.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. */
+ rctf multi_socket_rect;
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])) {
+ location.x - NODE_SOCKSIZE * 4.0f,
+ location.x + NODE_SOCKSIZE * 2.0f,
+ location.y - node_socket_height,
+ location.y + node_socket_height);
+ if (BLI_rctf_isect_pt(&multi_socket_rect, cursor.x, cursor.y)) {
return true;
}
return false;
@@ -1212,10 +1238,8 @@ bool node_find_indicated_socket(SpaceNode &snode,
*sockp = nullptr;
/* check if we click in a socket */
- LISTBASE_FOREACH (bNode *, node, &snode.edittree->nodes) {
+ LISTBASE_FOREACH_BACKWARD (bNode *, node, &snode.edittree->nodes) {
BLI_rctf_init_pt_radius(&rect, cursor, size_sock_padded);
- rctf node_visible;
- BLI_rctf_init_pt_radius(&node_visible, cursor, size_sock_padded);
if (!(node->flag & NODE_HIDDEN)) {
/* extra padding inside and out - allow dragging on the text areas too */
@@ -1232,17 +1256,18 @@ bool node_find_indicated_socket(SpaceNode &snode,
if (in_out & SOCK_IN) {
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (!nodeSocketIsHidden(sock)) {
+ const float2 location(sock->locx, sock->locy);
if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) {
if (cursor_isect_multi_input_socket(cursor, *sock)) {
- if (node == visible_node(snode, node_visible)) {
+ if (!socket_is_occluded(location, *node, snode)) {
*nodep = node;
*sockp = sock;
return true;
}
}
}
- else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
- if (node == visible_node(snode, node_visible)) {
+ else if (BLI_rctf_isect_pt(&rect, location.x, location.y)) {
+ if (!socket_is_occluded(location, *node, snode)) {
*nodep = node;
*sockp = sock;
return true;
@@ -1254,8 +1279,9 @@ bool node_find_indicated_socket(SpaceNode &snode,
if (in_out & SOCK_OUT) {
LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (!nodeSocketIsHidden(sock)) {
- if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
- if (node == visible_node(snode, node_visible)) {
+ const float2 location(sock->locx, sock->locy);
+ if (BLI_rctf_isect_pt(&rect, location.x, location.y)) {
+ if (!socket_is_occluded(location, *node, snode)) {
*nodep = node;
*sockp = sock;
return true;
@@ -1281,11 +1307,12 @@ float node_link_dim_factor(const View2D &v2d, const bNodeLink &link)
return 1.0f;
}
+ const float2 from(link.fromsock->locx, link.fromsock->locy);
+ const float2 to(link.tosock->locx, link.tosock->locy);
+
const float min_endpoint_distance = std::min(
- std::max(BLI_rctf_length_x(&v2d.cur, link.fromsock->locx),
- BLI_rctf_length_y(&v2d.cur, link.fromsock->locy)),
- std::max(BLI_rctf_length_x(&v2d.cur, link.tosock->locx),
- BLI_rctf_length_y(&v2d.cur, link.tosock->locy)));
+ std::max(BLI_rctf_length_x(&v2d.cur, from.x), BLI_rctf_length_y(&v2d.cur, from.y)),
+ std::max(BLI_rctf_length_x(&v2d.cur, to.x), BLI_rctf_length_y(&v2d.cur, to.y)));
if (min_endpoint_distance == 0.0f) {
return 1.0f;
@@ -1344,7 +1371,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
bNode *lastnode = (bNode *)ntree->nodes.last;
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
- bNode *new_node = blender::bke::node_copy_with_mapping(
+ bNode *new_node = bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node);
changed = true;
@@ -1451,43 +1478,6 @@ void NODE_OT_duplicate(wmOperatorType *ot)
ot->srna, "keep_inputs", false, "Keep Inputs", "Keep the input links to duplicated nodes");
}
-static bool node_select_check(const ListBase *lb)
-{
- LISTBASE_FOREACH (const bNode *, node, lb) {
- if (node->flag & NODE_SELECT) {
- return true;
- }
- }
-
- return false;
-}
-
-void node_select_all(ListBase *lb, int action)
-{
- if (action == SEL_TOGGLE) {
- if (node_select_check(lb)) {
- action = SEL_DESELECT;
- }
- else {
- action = SEL_SELECT;
- }
- }
-
- LISTBASE_FOREACH (bNode *, node, lb) {
- switch (action) {
- case SEL_SELECT:
- nodeSetSelected(node, true);
- break;
- case SEL_DESELECT:
- nodeSetSelected(node, false);
- break;
- case SEL_INVERT:
- nodeSetSelected(node, !(node->flag & SELECT));
- break;
- }
- }
-}
-
/* XXX: some code needing updating to operators. */
/* goes over all scenes, reads render layers */
@@ -2149,24 +2139,23 @@ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
- bNode *node = nodeGetActive(&ntree);
- if (!node) {
+ bNode *active_node = nodeGetActive(&ntree);
+ if (!active_node) {
return OPERATOR_CANCELLED;
}
- LISTBASE_FOREACH (bNode *, node_iter, &ntree.nodes) {
- if (node_iter->flag & NODE_SELECT && node_iter != node) {
- if (node->flag & NODE_CUSTOM_COLOR) {
- node_iter->flag |= NODE_CUSTOM_COLOR;
- copy_v3_v3(node_iter->color, node->color);
+ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
+ if (node->flag & NODE_SELECT && node != active_node) {
+ if (active_node->flag & NODE_CUSTOM_COLOR) {
+ node->flag |= NODE_CUSTOM_COLOR;
+ copy_v3_v3(node->color, active_node->color);
}
else {
- node_iter->flag &= ~NODE_CUSTOM_COLOR;
+ node->flag &= ~NODE_CUSTOM_COLOR;
}
}
}
- node_sort(ntree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
@@ -2211,12 +2200,12 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
if (node->flag & SELECT) {
/* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */
- bNode *new_node = blender::bke::node_copy_with_mapping(nullptr,
- *node,
- LIB_ID_CREATE_NO_USER_REFCOUNT |
- LIB_ID_CREATE_NO_MAIN,
- false,
- socket_map);
+ bNode *new_node = bke::node_copy_with_mapping(nullptr,
+ *node,
+ LIB_ID_CREATE_NO_USER_REFCOUNT |
+ LIB_ID_CREATE_NO_MAIN,
+ false,
+ socket_map);
node_map.add_new(node, new_node);
}
}
@@ -2336,11 +2325,11 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
node_deselect_all(*snode);
/* calculate "barycenter" for placing on mouse cursor */
- float center[2] = {0.0f, 0.0f};
+ float2 center = {0.0f, 0.0f};
int num_nodes = 0;
LISTBASE_FOREACH_INDEX (bNode *, node, clipboard_nodes_lb, num_nodes) {
- center[0] += BLI_rctf_cent_x(&node->totr);
- center[1] += BLI_rctf_cent_y(&node->totr);
+ center.x += BLI_rctf_cent_x(&node->totr);
+ center.y += BLI_rctf_cent_y(&node->totr);
}
mul_v2_fl(center, 1.0 / num_nodes);
@@ -2349,7 +2338,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
/* copy nodes from clipboard */
LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
- bNode *new_node = blender::bke::node_copy_with_mapping(
+ bNode *new_node = bke::node_copy_with_mapping(
ntree, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, new_node);
}
@@ -2424,7 +2413,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
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";
+ const char *default_name = (in_out == SOCK_IN) ? DATA_("Input") : DATA_("Output");
bNodeSocket *active_sock = ntree_get_active_interface_socket(sockets);
bNodeSocket *sock;
@@ -2548,7 +2537,7 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- /* Don't handle subtypes for now. */
+ /* Don't handle sub-types for now. */
nodeModifySocketType(ntree, nullptr, iosock, socket_type->idname);
/* Need the extra update here because the loop above does not check for valid links in the node
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index e328a86b0fd..809c4b2fe59 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -14,6 +14,7 @@
#include "DNA_space_types.h"
#include "BKE_context.h"
+#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "BKE_object.h"
@@ -30,12 +31,11 @@
#include "UI_interface.hh"
#include "UI_resources.h"
-#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_geometry_nodes_log.hh"
#include "node_intern.hh"
-namespace geo_log = blender::nodes::geometry_nodes_eval_log;
-using geo_log::GeometryAttributeInfo;
+using blender::nodes::geo_eval_log::GeometryAttributeInfo;
namespace blender::ed::space_node {
@@ -50,6 +50,8 @@ BLI_STATIC_ASSERT(std::is_trivially_destructible_v<AttributeSearchData>, "");
static Vector<const GeometryAttributeInfo *> get_attribute_info_from_context(
const bContext &C, AttributeSearchData &data)
{
+ using namespace nodes::geo_eval_log;
+
SpaceNode *snode = CTX_wm_space_node(&C);
if (!snode) {
BLI_assert_unreachable();
@@ -65,41 +67,48 @@ static Vector<const GeometryAttributeInfo *> get_attribute_info_from_context(
BLI_assert_unreachable();
return {};
}
+ GeoTreeLog *tree_log = GeoModifierLog::get_tree_log_for_node_editor(*snode);
+ if (tree_log == nullptr) {
+ return {};
+ }
+ tree_log->ensure_socket_values();
/* For the attribute input node, collect attribute information from all nodes in the group. */
if (node->type == GEO_NODE_INPUT_NAMED_ATTRIBUTE) {
- const geo_log::TreeLog *tree_log = geo_log::ModifierLog::find_tree_by_node_editor_context(
- *snode);
- if (tree_log == nullptr) {
- return {};
- }
-
+ tree_log->ensure_existing_attributes();
Vector<const GeometryAttributeInfo *> attributes;
- Set<StringRef> names;
- tree_log->foreach_node_log([&](const geo_log::NodeLog &node_log) {
- for (const geo_log::SocketLog &socket_log : node_log.input_logs()) {
- const geo_log::ValueLog *value_log = socket_log.value();
- if (const geo_log::GeometryValueLog *geo_value_log =
- dynamic_cast<const geo_log::GeometryValueLog *>(value_log)) {
- for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) {
- if (bke::allow_procedural_attribute_access(attribute.name)) {
- if (names.add(attribute.name)) {
- attributes.append(&attribute);
- }
- }
- }
- }
+ for (const GeometryAttributeInfo *attribute : tree_log->existing_attributes) {
+ if (bke::allow_procedural_attribute_access(attribute->name)) {
+ attributes.append(attribute);
}
- });
+ }
return attributes;
}
-
- const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_node_editor_context(
- *snode, data.node_name);
+ GeoNodeLog *node_log = tree_log->nodes.lookup_ptr(node->name);
if (node_log == nullptr) {
return {};
}
- return node_log->lookup_available_attributes();
+ Set<StringRef> names;
+ Vector<const GeometryAttributeInfo *> attributes;
+ for (const bNodeSocket *input_socket : node->input_sockets()) {
+ if (input_socket->type != SOCK_GEOMETRY) {
+ continue;
+ }
+ const ValueLog *value_log = tree_log->find_socket_value_log(*input_socket);
+ if (value_log == nullptr) {
+ continue;
+ }
+ if (const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(value_log)) {
+ for (const GeometryAttributeInfo &attribute : geo_log->attributes) {
+ if (bke::allow_procedural_attribute_access(attribute.name)) {
+ if (names.add(attribute.name)) {
+ attributes.append(&attribute);
+ }
+ }
+ }
+ }
+ }
+ return attributes;
}
static void attribute_search_update_fn(
diff --git a/source/blender/editors/space_node/node_gizmo.cc b/source/blender/editors/space_node/node_gizmo.cc
index 4f27f9baabc..f9126556b71 100644
--- a/source/blender/editors/space_node/node_gizmo.cc
+++ b/source/blender/editors/space_node/node_gizmo.cc
@@ -49,14 +49,14 @@ static void node_gizmo_calc_matrix_space(const SpaceNode *snode,
static void node_gizmo_calc_matrix_space_with_image_dims(const SpaceNode *snode,
const ARegion *region,
- const float image_dims[2],
+ const float2 &image_dims,
float matrix_space[4][4])
{
unit_m4(matrix_space);
- mul_v3_fl(matrix_space[0], snode->zoom * image_dims[0]);
- mul_v3_fl(matrix_space[1], snode->zoom * image_dims[1]);
- matrix_space[3][0] = ((region->winx / 2) + snode->xof) - ((image_dims[0] / 2.0f) * snode->zoom);
- matrix_space[3][1] = ((region->winy / 2) + snode->yof) - ((image_dims[1] / 2.0f) * snode->zoom);
+ mul_v3_fl(matrix_space[0], snode->zoom * image_dims.x);
+ mul_v3_fl(matrix_space[1], snode->zoom * image_dims.y);
+ matrix_space[3][0] = ((region->winx / 2) + snode->xof) - ((image_dims.x / 2.0f) * snode->zoom);
+ matrix_space[3][1] = ((region->winy / 2) + snode->yof) - ((image_dims.y / 2.0f) * snode->zoom);
}
/** \} */
@@ -135,7 +135,7 @@ static void WIDGETGROUP_node_transform_refresh(const bContext *C, wmGizmoGroup *
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, nullptr, &lock);
if (ibuf) {
- const float dims[2] = {
+ const float2 dims = {
(ibuf->x > 0) ? ibuf->x : 64.0f,
(ibuf->y > 0) ? ibuf->y : 64.0f,
};
@@ -190,7 +190,7 @@ struct NodeCropWidgetGroup {
wmGizmo *border;
struct {
- float dims[2];
+ float2 dims;
} state;
struct {
@@ -206,10 +206,7 @@ static void gizmo_node_crop_update(struct NodeCropWidgetGroup *crop_group)
crop_group->update_data.context, &crop_group->update_data.ptr, crop_group->update_data.prop);
}
-static void two_xy_to_rect(const NodeTwoXYs *nxy,
- rctf *rect,
- const float dims[2],
- bool is_relative)
+static void two_xy_to_rect(const NodeTwoXYs *nxy, rctf *rect, const float2 &dims, bool is_relative)
{
if (is_relative) {
rect->xmin = nxy->fac_x1;
@@ -218,16 +215,16 @@ static void two_xy_to_rect(const NodeTwoXYs *nxy,
rect->ymax = nxy->fac_y2;
}
else {
- rect->xmin = nxy->x1 / dims[0];
- rect->xmax = nxy->x2 / dims[0];
- rect->ymin = nxy->y1 / dims[1];
- rect->ymax = nxy->y2 / dims[1];
+ rect->xmin = nxy->x1 / dims.x;
+ rect->xmax = nxy->x2 / dims.x;
+ rect->ymin = nxy->y1 / dims.y;
+ rect->ymax = nxy->y2 / dims.y;
}
}
static void two_xy_from_rect(NodeTwoXYs *nxy,
const rctf *rect,
- const float dims[2],
+ const float2 &dims,
bool is_relative)
{
if (is_relative) {
@@ -237,10 +234,10 @@ static void two_xy_from_rect(NodeTwoXYs *nxy,
nxy->fac_y2 = rect->ymax;
}
else {
- nxy->x1 = rect->xmin * dims[0];
- nxy->x2 = rect->xmax * dims[0];
- nxy->y1 = rect->ymin * dims[1];
- nxy->y2 = rect->ymax * dims[1];
+ nxy->x1 = rect->xmin * dims.x;
+ nxy->x2 = rect->xmax * dims.x;
+ nxy->y1 = rect->ymin * dims.y;
+ nxy->y2 = rect->ymax * dims.y;
}
}
@@ -321,9 +318,7 @@ static bool WIDGETGROUP_node_crop_poll(const bContext *C, wmGizmoGroupType *UNUS
static void WIDGETGROUP_node_crop_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup)
{
- struct NodeCropWidgetGroup *crop_group = (NodeCropWidgetGroup *)MEM_mallocN(
- sizeof(struct NodeCropWidgetGroup), __func__);
-
+ NodeCropWidgetGroup *crop_group = MEM_new<NodeCropWidgetGroup>(__func__);
crop_group->border = WM_gizmo_new("GIZMO_GT_cage_2d", gzgroup, nullptr);
RNA_enum_set(crop_group->border->ptr,
@@ -407,7 +402,7 @@ struct NodeSunBeamsWidgetGroup {
wmGizmo *gizmo;
struct {
- float dims[2];
+ float2 dims;
} state;
};
@@ -512,7 +507,7 @@ struct NodeCornerPinWidgetGroup {
wmGizmo *gizmos[4];
struct {
- float dims[2];
+ float2 dims;
} state;
};
diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc
index 160a379d3c6..21def1bd9d7 100644
--- a/source/blender/editors/space_node/node_group.cc
+++ b/source/blender/editors/space_node/node_group.cc
@@ -25,6 +25,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "BKE_report.h"
@@ -36,6 +37,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "WM_api.h"
@@ -44,7 +46,12 @@
#include "UI_resources.h"
#include "NOD_common.h"
+#include "NOD_composite.h"
+#include "NOD_geometry.h"
+#include "NOD_shader.h"
#include "NOD_socket.h"
+#include "NOD_texture.h"
+
#include "node_intern.hh" /* own include */
namespace blender::ed::space_node {
@@ -99,16 +106,16 @@ const char *node_group_idname(bContext *C)
SpaceNode *snode = CTX_wm_space_node(C);
if (ED_node_is_shader(snode)) {
- return "ShaderNodeGroup";
+ return ntreeType_Shader->group_idname;
}
if (ED_node_is_compositor(snode)) {
- return "CompositorNodeGroup";
+ return ntreeType_Composite->group_idname;
}
if (ED_node_is_texture(snode)) {
- return "TextureNodeGroup";
+ return ntreeType_Texture->group_idname;
}
if (ED_node_is_geometry(snode)) {
- return "GeometryNodeGroup";
+ return ntreeType_Geometry->group_idname;
}
return "";
@@ -456,8 +463,7 @@ static bool node_group_separate_selected(
bNode *newnode;
if (make_copy) {
/* make a copy */
- newnode = blender::bke::node_copy_with_mapping(
- &ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
+ newnode = bke::node_copy_with_mapping(&ngroup, *node, LIB_ID_COPY_DEFAULT, true, socket_map);
node_map.add_new(node, newnode);
}
else {
@@ -647,7 +653,7 @@ static bool node_group_make_use_node(bNode &node, bNode *gnode)
static bool node_group_make_test_selected(bNodeTree &ntree,
bNode *gnode,
const char *ntree_idname,
- struct ReportList &reports)
+ ReportList &reports)
{
int ok = true;
@@ -711,13 +717,13 @@ static int node_get_selected_minmax(
INIT_MINMAX2(min, max);
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
if (node_group_make_use_node(*node, gnode)) {
- float loc[2];
- nodeToView(node, node->offsetx, node->offsety, &loc[0], &loc[1]);
- minmax_v2v2_v2(min, max, loc);
+ float2 loc;
+ nodeToView(node, node->offsetx, node->offsety, &loc.x, &loc.y);
+ math::min_max(loc, min, max);
if (use_size) {
- loc[0] += node->width;
- loc[1] -= node->height;
- minmax_v2v2_v2(min, max, loc);
+ loc.x += node->width;
+ loc.y -= node->height;
+ math::min_max(loc, min, max);
}
totselect++;
}
@@ -831,8 +837,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
/* relink external sockets */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
- int fromselect = node_group_make_use_node(*link->fromnode, gnode);
- int toselect = node_group_make_use_node(*link->tonode, gnode);
+ const bool fromselect = node_group_make_use_node(*link->fromnode, gnode);
+ const bool toselect = node_group_make_use_node(*link->tonode, gnode);
if ((fromselect && link->tonode == gnode) || (toselect && link->fromnode == gnode)) {
/* remove all links to/from the gnode.
@@ -912,8 +918,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
/* move internal links */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree.links) {
- int fromselect = node_group_make_use_node(*link->fromnode, gnode);
- int toselect = node_group_make_use_node(*link->tonode, gnode);
+ const bool fromselect = node_group_make_use_node(*link->fromnode, gnode);
+ const bool toselect = node_group_make_use_node(*link->tonode, gnode);
if (fromselect && toselect) {
BLI_remlink(&ntree.links, link);
@@ -1036,9 +1042,6 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
nodeSetActive(&ntree, gnode);
if (ngroup) {
ED_node_tree_push(&snode, ngroup, gnode);
- LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
- sort_multi_input_socket_links(snode, *node, nullptr, nullptr);
- }
}
}
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 924537d0e8a..70ac0e48d01 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -9,6 +9,7 @@
#include "BLI_math_vector.h"
#include "BLI_math_vector.hh"
+#include "BLI_set.hh"
#include "BLI_vector.hh"
#include "BKE_node.h"
@@ -144,7 +145,10 @@ void node_socket_color_get(const bContext &C,
void node_draw_space(const bContext &C, ARegion &region);
-void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout);
+void node_socket_add_tooltip(const bNodeTree &ntree,
+ const bNode &node,
+ const bNodeSocket &sock,
+ uiLayout &layout);
/**
* Sort nodes by selection: unselected nodes first, then selected,
@@ -166,8 +170,9 @@ void node_keymap(wmKeyConfig *keyconf);
/* node_select.cc */
rctf node_frame_rect_inside(const bNode &node);
-bool node_or_socket_isect_event(bContext *C, const wmEvent *event);
+bool node_or_socket_isect_event(const bContext &C, const wmEvent &event);
+Set<bNode *> get_selected_nodes(bNodeTree &node_tree);
void node_deselect_all(SpaceNode &snode);
void node_socket_select(bNode *node, bNodeSocket &sock);
void node_socket_deselect(bNode *node, bNodeSocket &sock, bool deselect_node);
@@ -214,6 +219,10 @@ void node_draw_link(const bContext &C,
const SpaceNode &snode,
const bNodeLink &link,
bool selected);
+void node_draw_link_dragged(const bContext &C,
+ const View2D &v2d,
+ const SpaceNode &snode,
+ const bNodeLink &link);
/**
* Don't do shadows if th_col3 is -1.
*/
@@ -225,19 +234,12 @@ void node_draw_link_bezier(const bContext &C,
int th_col2,
int th_col3,
bool selected);
-/** 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,
- float coord_array[][2],
- int resol);
-/**
- * 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 &ink,
- float vec[4][2]);
+
+void node_link_bezier_points_evaluated(const bNodeLink &link,
+ std::array<float2, NODE_LINK_RESOL + 1> &coords);
+
+std::optional<float2> link_path_intersection(const bNodeLink &link, Span<float2> path);
+
void draw_nodespace_back_pix(const bContext &C,
ARegion &region,
SpaceNode &snode,
@@ -245,13 +247,11 @@ void draw_nodespace_back_pix(const bContext &C,
/* node_add.cc */
-/**
- * XXX Does some additional initialization on top of #nodeAddNode
- * Can be used with both custom and static nodes,
- * 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);
+bNode *add_node(const bContext &C, StringRef idname, const float2 &location);
+bNode *add_static_node(const bContext &C, int type, const float2 &location);
+
void NODE_OT_add_reroute(wmOperatorType *ot);
+void NODE_OT_add_search(wmOperatorType *ot);
void NODE_OT_add_group(wmOperatorType *ot);
void NODE_OT_add_object(wmOperatorType *ot);
void NODE_OT_add_collection(wmOperatorType *ot);
@@ -270,11 +270,6 @@ void NODE_OT_group_edit(wmOperatorType *ot);
/* node_relationships.cc */
-void sort_multi_input_socket_links(SpaceNode &snode,
- bNode &node,
- bNodeLink *drag_link,
- const float2 *cursor);
-
void NODE_OT_link(wmOperatorType *ot);
void NODE_OT_link_make(wmOperatorType *ot);
void NODE_OT_links_cut(wmOperatorType *ot);
@@ -296,8 +291,6 @@ float2 node_link_calculate_multi_input_position(const float2 &socket_position,
int index,
int total_inputs);
-void node_select_all(ListBase *lb, int action);
-
float node_socket_calculate_height(const bNodeSocket &socket);
void snode_set_context(const bContext &C);
@@ -383,4 +376,8 @@ void invoke_node_link_drag_add_menu(bContext &C,
bNodeSocket &socket,
const float2 &cursor);
+/* add_node_search.cc */
+
+void invoke_add_node_search_menu(bContext &C, const float2 &cursor, bool use_transform);
+
} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_ops.cc b/source/blender/editors/space_node/node_ops.cc
index ce000aba1da..f02c019359d 100644
--- a/source/blender/editors/space_node/node_ops.cc
+++ b/source/blender/editors/space_node/node_ops.cc
@@ -75,6 +75,7 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_backimage_fit);
WM_operatortype_append(NODE_OT_backimage_sample);
+ WM_operatortype_append(NODE_OT_add_search);
WM_operatortype_append(NODE_OT_add_group);
WM_operatortype_append(NODE_OT_add_object);
WM_operatortype_append(NODE_OT_add_collection);
@@ -111,7 +112,7 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_cryptomatte_layer_remove);
}
-void node_keymap(struct wmKeyConfig *keyconf)
+void node_keymap(wmKeyConfig *keyconf)
{
/* Entire Editor only ----------------- */
WM_keymap_ensure(keyconf, "Node Generic", SPACE_NODE, 0);
@@ -144,7 +145,7 @@ void ED_operatormacros_node()
WM_operatortype_macro_define(ot, "NODE_OT_attach");
WM_operatortype_macro_define(ot, "NODE_OT_insert_offset");
- /* NODE_OT_translate_attach with remove_on_canel set to true */
+ /* NODE_OT_translate_attach with remove_on_cancel set to true. */
ot = WM_operatortype_append_macro("NODE_OT_translate_attach_remove_on_cancel",
"Move and Attach",
"Move nodes and attach to frame",
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index e10bedb18f4..e12ab3191cb 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -11,6 +11,7 @@
#include "DNA_node_types.h"
#include "BLI_easing.h"
+#include "BLI_stack.hh"
#include "BKE_anim_data.h"
#include "BKE_context.h"
@@ -18,6 +19,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "BKE_screen.h"
@@ -46,19 +48,11 @@
#include "BLT_translation.h"
#include "NOD_node_declaration.hh"
-#include "NOD_node_tree_ref.hh"
#include "NOD_socket_declarations.hh"
#include "NOD_socket_declarations_geometry.hh"
#include "node_intern.hh" /* own include */
-using namespace blender::nodes::node_tree_ref_types;
-
-struct bNodeListItem {
- struct bNodeListItem *next, *prev;
- struct bNode *node;
-};
-
struct NodeInsertOfsData {
bNodeTree *ntree;
bNode *insert; /* inserted node */
@@ -79,6 +73,8 @@ static void clear_picking_highlight(ListBase *links)
namespace blender::ed::space_node {
+void update_multi_input_indices_for_removed_links(bNode &node);
+
/* -------------------------------------------------------------------- */
/** \name Add Node
* \{ */
@@ -109,11 +105,9 @@ static void pick_link(
nldrag.links.append(link);
nodeRemLink(snode.edittree, &link_to_pick);
-
+ snode.edittree->ensure_topology_cache();
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);
+ update_multi_input_indices_for_removed_links(*nldrag.last_node_hovered_while_dragging_a_link);
/* Send changed event to original link->tonode. */
if (node) {
@@ -127,10 +121,8 @@ static void pick_input_link_by_link_intersect(const bContext &C,
const float2 &cursor)
{
SpaceNode *snode = CTX_wm_space_node(&C);
- const ARegion *region = CTX_wm_region(&C);
- const View2D *v2d = &region->v2d;
- float drag_start[2];
+ float2 drag_start;
RNA_float_get_array(op.ptr, "drag_start", drag_start);
bNode *node;
bNodeSocket *socket;
@@ -139,26 +131,16 @@ static void pick_input_link_by_link_intersect(const bContext &C,
/* 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 = nullptr;
clear_picking_highlight(&snode->edittree->links);
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (link->tosock == socket) {
/* Test if the cursor is near a link. */
- float vec[4][2];
- node_link_bezier_handles(v2d, snode, *link, vec);
-
- float data[NODE_LINK_RESOL * 2 + 2];
- BKE_curve_forward_diff_bezier(
- vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2]));
- BKE_curve_forward_diff_bezier(
- vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2]));
-
- for (int i = 0; i < resolution * 2; i += 2) {
- float *l1 = &data[i];
- float *l2 = &data[i + 2];
- float distance = dist_squared_to_line_segment_v2(cursor, l1, l2);
+ std::array<float2, NODE_LINK_RESOL + 1> coords;
+ node_link_bezier_points_evaluated(*link, coords);
+
+ for (const int i : IndexRange(coords.size() - 1)) {
+ const float distance = dist_squared_to_line_segment_v2(cursor, coords[i], coords[i + 1]);
if (distance < cursor_link_touch_distance) {
link_to_pick = link;
nldrag.last_picked_multi_input_socket_link = link_to_pick;
@@ -310,35 +292,24 @@ struct LinkAndPosition {
float2 multi_socket_position;
};
-void sort_multi_input_socket_links(SpaceNode &snode,
- bNode &node,
- bNodeLink *drag_link,
- const float2 *cursor)
+static void sort_multi_input_socket_links_with_drag(bNode &node,
+ bNodeLink &drag_link,
+ const float2 &cursor)
{
- LISTBASE_FOREACH (bNodeSocket *, socket, &node.inputs) {
- if (!(socket->flag & SOCK_MULTI_INPUT)) {
+ for (bNodeSocket *socket : node.input_sockets()) {
+ if (!socket->is_multi_input()) {
continue;
}
- Vector<LinkAndPosition, 8> links;
+ const float2 &socket_location = {socket->locx, socket->locy};
- LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) {
- if (link->tosock == socket) {
- links.append(
- {link,
- node_link_calculate_multi_input_position({link->tosock->locx, link->tosock->locy},
- link->multi_input_socket_index,
- link->tosock->total_inputs)});
- }
- }
+ Vector<LinkAndPosition, 8> links;
+ for (bNodeLink *link : socket->directly_linked_links()) {
+ const float2 location = node_link_calculate_multi_input_position(
+ socket_location, link->multi_input_socket_index, link->tosock->total_inputs);
+ links.append({link, location});
+ };
- if (drag_link) {
- LinkAndPosition link_and_position{};
- link_and_position.link = drag_link;
- if (cursor) {
- link_and_position.multi_socket_position = *cursor;
- }
- links.append(link_and_position);
- }
+ links.append({&drag_link, cursor});
std::sort(links.begin(), links.end(), [](const LinkAndPosition a, const LinkAndPosition b) {
return a.multi_socket_position.y < b.multi_socket_position.y;
@@ -350,6 +321,23 @@ void sort_multi_input_socket_links(SpaceNode &snode,
}
}
+void update_multi_input_indices_for_removed_links(bNode &node)
+{
+ for (bNodeSocket *socket : node.input_sockets()) {
+ if (!socket->is_multi_input()) {
+ continue;
+ }
+ Vector<bNodeLink *, 8> links = socket->directly_linked_links();
+ std::sort(links.begin(), links.end(), [](const bNodeLink *a, const bNodeLink *b) {
+ return a->multi_input_socket_index < b->multi_input_socket_index;
+ });
+
+ for (const int i : links.index_range()) {
+ links[i]->multi_input_socket_index = i;
+ }
+ }
+}
+
static void snode_autoconnect(SpaceNode &snode, const bool allow_multiple, const bool replace)
{
bNodeTree *ntree = snode.edittree;
@@ -434,18 +422,18 @@ namespace viewer_linking {
* \{ */
/* Depending on the node tree type, different socket types are supported by viewer nodes. */
-static bool socket_can_be_viewed(const OutputSocketRef &socket)
+static bool socket_can_be_viewed(const bNodeSocket &socket)
{
- if (nodeSocketIsHidden(socket.bsocket())) {
+ if (nodeSocketIsHidden(&socket)) {
return false;
}
- if (socket.idname() == "NodeSocketVirtual") {
+ if (STREQ(socket.idname, "NodeSocketVirtual")) {
return false;
}
- if (socket.tree().btree()->type != NTREE_GEOMETRY) {
+ if (socket.owner_tree().type != NTREE_GEOMETRY) {
return true;
}
- return ELEM(socket.typeinfo()->type,
+ return ELEM(socket.typeinfo->type,
SOCK_GEOMETRY,
SOCK_FLOAT,
SOCK_VECTOR,
@@ -502,15 +490,15 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree,
return nullptr;
}
-static bool is_viewer_node(const NodeRef &node)
+static bool is_viewer_node(const bNode &node)
{
- return ELEM(node.bnode()->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER);
+ return ELEM(node.type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER);
}
-static Vector<const NodeRef *> find_viewer_nodes(const NodeTreeRef &tree)
+static Vector<const bNode *> find_viewer_nodes(const bNodeTree &tree)
{
- Vector<const NodeRef *> viewer_nodes;
- for (const NodeRef *node : tree.nodes()) {
+ Vector<const bNode *> viewer_nodes;
+ for (const bNode *node : tree.all_nodes()) {
if (is_viewer_node(*node)) {
viewer_nodes.append(node);
}
@@ -518,20 +506,20 @@ static Vector<const NodeRef *> find_viewer_nodes(const NodeTreeRef &tree)
return viewer_nodes;
}
-static bool is_viewer_socket_in_viewer(const InputSocketRef &socket)
+static bool is_viewer_socket_in_viewer(const bNodeSocket &socket)
{
- const NodeRef &node = socket.node();
+ const bNode &node = socket.owner_node();
BLI_assert(is_viewer_node(node));
- if (node.typeinfo()->type == GEO_NODE_VIEWER) {
+ if (node.typeinfo->type == GEO_NODE_VIEWER) {
return true;
}
return socket.index() == 0;
}
-static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node)
+static bool is_linked_to_viewer(const bNodeSocket &socket, const bNode &viewer_node)
{
- for (const InputSocketRef *target_socket : socket.directly_linked_sockets()) {
- if (&target_socket->node() != &viewer_node) {
+ for (const bNodeSocket *target_socket : socket.directly_linked_sockets()) {
+ if (&target_socket->owner_node() != &viewer_node) {
continue;
}
if (!target_socket->is_available()) {
@@ -561,39 +549,39 @@ static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &
}
}
-static const NodeRef *get_existing_viewer(const NodeTreeRef &tree)
+static const bNode *get_existing_viewer(const bNodeTree &tree)
{
- Vector<const NodeRef *> viewer_nodes = find_viewer_nodes(tree);
+ Vector<const bNode *> viewer_nodes = find_viewer_nodes(tree);
/* Check if there is already an active viewer node that should be used. */
- for (const NodeRef *viewer_node : viewer_nodes) {
- if (viewer_node->bnode()->flag & NODE_DO_OUTPUT) {
+ for (const bNode *viewer_node : viewer_nodes) {
+ if (viewer_node->flag & NODE_DO_OUTPUT) {
return viewer_node;
}
}
/* If no active but non-active viewers exist, make one active. */
if (!viewer_nodes.is_empty()) {
- viewer_nodes[0]->bnode()->flag |= NODE_DO_OUTPUT;
+ const_cast<bNode *>(viewer_nodes[0])->flag |= NODE_DO_OUTPUT;
return viewer_nodes[0];
}
return nullptr;
}
-static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node,
- const NodeRef &node_to_view)
+static const bNodeSocket *find_output_socket_to_be_viewed(const bNode *active_viewer_node,
+ const bNode &node_to_view)
{
/* Check if any of the output sockets is selected, which is the case when the user just clicked
* on the socket. */
- for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
- if (output_socket->bsocket()->flag & SELECT) {
+ for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
+ if (output_socket->flag & SELECT) {
return output_socket;
}
}
- const OutputSocketRef *last_socket_linked_to_viewer = nullptr;
+ const bNodeSocket *last_socket_linked_to_viewer = nullptr;
if (active_viewer_node != nullptr) {
- for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
+ for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
if (!socket_can_be_viewed(*output_socket)) {
continue;
}
@@ -604,7 +592,7 @@ static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *act
}
if (last_socket_linked_to_viewer == nullptr) {
/* If no output is connected to a viewer, use the first output that can be viewed. */
- for (const OutputSocketRef *output_socket : node_to_view.outputs()) {
+ for (const bNodeSocket *output_socket : node_to_view.output_sockets()) {
if (socket_can_be_viewed(*output_socket)) {
return output_socket;
}
@@ -612,10 +600,10 @@ static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *act
}
else {
/* Pick the next socket to be linked to the viewer. */
- const int tot_outputs = node_to_view.outputs().size();
+ const int tot_outputs = node_to_view.output_sockets().size();
for (const int offset : IndexRange(1, tot_outputs - 1)) {
const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs;
- const OutputSocketRef &output_socket = node_to_view.output(index);
+ const bNodeSocket &output_socket = node_to_view.output_socket(index);
if (!socket_can_be_viewed(output_socket)) {
continue;
}
@@ -639,8 +627,9 @@ static int link_socket_to_viewer(const bContext &C,
if (viewer_bnode == nullptr) {
/* Create a new viewer node if none exists. */
const int viewer_type = get_default_viewer_type(&C);
- viewer_bnode = node_add_node(
- C, nullptr, viewer_type, bsocket_to_view.locx + 100, bsocket_to_view.locy);
+ const float2 location{bsocket_to_view.locx / UI_DPI_FAC + 100,
+ bsocket_to_view.locy / UI_DPI_FAC};
+ viewer_bnode = add_static_node(C, viewer_type, location);
if (viewer_bnode == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -682,20 +671,15 @@ static int node_link_viewer(const bContext &C, bNode &bnode_to_view)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree *btree = snode.edittree;
+ btree->ensure_topology_cache();
- const NodeTreeRef tree{btree};
- const NodeRef &node_to_view = *tree.find_node(bnode_to_view);
- const NodeRef *active_viewer_node = get_existing_viewer(tree);
-
- const OutputSocketRef *socket_to_view = find_output_socket_to_be_viewed(active_viewer_node,
- node_to_view);
- if (socket_to_view == nullptr) {
+ bNode *active_viewer_bnode = const_cast<bNode *>(get_existing_viewer(*btree));
+ bNodeSocket *bsocket_to_view = const_cast<bNodeSocket *>(
+ find_output_socket_to_be_viewed(active_viewer_bnode, bnode_to_view));
+ if (bsocket_to_view == nullptr) {
return OPERATOR_FINISHED;
}
-
- bNodeSocket &bsocket_to_view = *socket_to_view->bsocket();
- bNode *viewer_bnode = active_viewer_node ? active_viewer_node->bnode() : nullptr;
- return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view);
+ return link_socket_to_viewer(C, active_viewer_bnode, bnode_to_view, *bsocket_to_view);
}
/** \} */
@@ -949,6 +933,7 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
if (nldrag->in_out == SOCK_OUT) {
bNode *tnode;
bNodeSocket *tsock = nullptr;
+ snode.edittree->ensure_topology_cache();
if (node_find_indicated_socket(snode, &tnode, &tsock, cursor, SOCK_IN)) {
for (bNodeLink *link : nldrag->links) {
/* skip if socket is on the same node as the fromsock */
@@ -975,19 +960,19 @@ static void node_link_find_socket(bContext &C, wmOperator &op, const float2 &cur
continue;
}
if (link->tosock && link->tosock->flag & SOCK_MULTI_INPUT) {
- sort_multi_input_socket_links(snode, *tnode, link, &cursor);
+ sort_multi_input_socket_links_with_drag(*tnode, *link, cursor);
}
}
}
else {
for (bNodeLink *link : nldrag->links) {
- if (nldrag->last_node_hovered_while_dragging_a_link) {
- sort_multi_input_socket_links(
- snode, *nldrag->last_node_hovered_while_dragging_a_link, nullptr, &cursor);
- }
link->tonode = nullptr;
link->tosock = nullptr;
}
+ if (nldrag->last_node_hovered_while_dragging_a_link) {
+ update_multi_input_indices_for_removed_links(
+ *nldrag->last_node_hovered_while_dragging_a_link);
+ }
}
}
else {
@@ -1184,7 +1169,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool detach = RNA_boolean_get(op->ptr, "detach");
- int mval[2];
+ int2 mval;
WM_event_drag_start_mval(event, &region, mval);
float2 cursor;
@@ -1324,28 +1309,6 @@ void NODE_OT_link_make(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \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(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 true;
- }
- }
- }
- }
- return false;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Cut Link Operator
* \{ */
@@ -1353,56 +1316,63 @@ static int cut_links_exec(bContext *C, wmOperator *op)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
- ARegion &region = *CTX_wm_region(C);
+ const ARegion &region = *CTX_wm_region(C);
- int i = 0;
- float mcoords[256][2];
+ Vector<float2> path;
RNA_BEGIN (op->ptr, itemptr, "path") {
- float loc[2];
-
- RNA_float_get_array(&itemptr, "loc", loc);
- UI_view2d_region_to_view(
- &region.v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]);
- i++;
- if (i >= 256) {
+ float2 loc_region;
+ RNA_float_get_array(&itemptr, "loc", loc_region);
+ float2 loc_view;
+ UI_view2d_region_to_view(&region.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
+ path.append(loc_view);
+ if (path.size() >= 256) {
break;
}
}
RNA_END;
- if (i > 1) {
- bool found = false;
+ if (path.is_empty()) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ }
- ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
+ bool found = false;
- LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode.edittree->links) {
- if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
- continue;
- }
+ ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
- if (node_links_intersect(*link, mcoords, i)) {
+ bNodeTree &node_tree = *snode.edittree;
- if (found == false) {
- /* TODO(sergey): Why did we kill jobs twice? */
- ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
- found = true;
- }
+ Set<bNode *> affected_nodes;
- bNode *to_node = link->tonode;
- nodeRemLink(snode.edittree, link);
- sort_multi_input_socket_links(snode, *to_node, nullptr, nullptr);
- }
+ LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &node_tree.links) {
+ if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
+ continue;
}
- ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
- if (found) {
- return OPERATOR_FINISHED;
+ if (link_path_intersection(*link, path)) {
+
+ if (!found) {
+ /* TODO(sergey): Why did we kill jobs twice? */
+ ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
+ found = true;
+ }
+
+ bNode *to_node = link->tonode;
+ nodeRemLink(snode.edittree, link);
+ affected_nodes.add(to_node);
}
+ }
- return OPERATOR_CANCELLED;
+ node_tree.ensure_topology_cache();
+ for (bNode *node : affected_nodes) {
+ update_multi_input_indices_for_removed_links(*node);
}
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
+ if (found) {
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED;
}
void NODE_OT_links_cut(wmOperatorType *ot)
@@ -1436,69 +1406,99 @@ void NODE_OT_links_cut(wmOperatorType *ot)
/** \name Mute Links Operator
* \{ */
+static bool all_links_muted(const bNodeSocket &socket)
+{
+ for (const bNodeLink *link : socket.directly_linked_links()) {
+ if (!(link->flag & NODE_LINK_MUTED)) {
+ return false;
+ }
+ }
+ return true;
+}
+
static int mute_links_exec(bContext *C, wmOperator *op)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
- ARegion &region = *CTX_wm_region(C);
+ const ARegion &region = *CTX_wm_region(C);
+ bNodeTree &ntree = *snode.edittree;
- int i = 0;
- float mcoords[256][2];
+ Vector<float2> path;
RNA_BEGIN (op->ptr, itemptr, "path") {
- float loc[2];
-
- RNA_float_get_array(&itemptr, "loc", loc);
- UI_view2d_region_to_view(
- &region.v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]);
- i++;
- if (i >= 256) {
+ float2 loc_region;
+ RNA_float_get_array(&itemptr, "loc", loc_region);
+ float2 loc_view;
+ UI_view2d_region_to_view(&region.v2d, loc_region.x, loc_region.y, &loc_view.x, &loc_view.y);
+ path.append(loc_view);
+ if (path.size() >= 256) {
break;
}
}
RNA_END;
- if (i > 1) {
- ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
+ if (path.is_empty()) {
+ return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ }
- /* Count intersected links and clear test flag. */
- int tot = 0;
- LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) {
- if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
- continue;
- }
- link->flag &= ~NODE_LINK_TEST;
- if (node_links_intersect(*link, mcoords, i)) {
- tot++;
- }
+ ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
+
+ ntree.ensure_topology_cache();
+
+ Set<bNodeLink *> affected_links;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
+ if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
+ continue;
}
- if (tot == 0) {
- return OPERATOR_CANCELLED;
+ if (!link_path_intersection(*link, path)) {
+ continue;
}
+ affected_links.add(link);
+ }
- /* Mute links. */
- LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) {
- if (node_link_is_hidden_or_dimmed(region.v2d, *link) || (link->flag & NODE_LINK_TEST)) {
- continue;
- }
+ if (affected_links.is_empty()) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bke::node_tree_runtime::AllowUsingOutdatedInfo allow_outdated_info{ntree};
+
+ for (bNodeLink *link : affected_links) {
+ nodeLinkSetMute(&ntree, link, !(link->flag & NODE_LINK_MUTED));
+ const bool muted = link->flag & NODE_LINK_MUTED;
- if (node_links_intersect(*link, mcoords, i)) {
- nodeMuteLinkToggle(snode.edittree, link);
+ /* Propagate mute status downstream past reroute nodes. */
+ if (link->tonode->is_reroute()) {
+ Stack<bNodeLink *> links;
+ links.push_multiple(link->tonode->output_sockets().first()->directly_linked_links());
+ while (!links.is_empty()) {
+ bNodeLink *link = links.pop();
+ nodeLinkSetMute(&ntree, link, muted);
+ if (!link->tonode->is_reroute()) {
+ continue;
+ }
+ links.push_multiple(link->tonode->output_sockets().first()->directly_linked_links());
}
}
-
- /* Clear remaining test flags. */
- LISTBASE_FOREACH (bNodeLink *, link, &snode.edittree->links) {
- if (node_link_is_hidden_or_dimmed(region.v2d, *link)) {
- continue;
+ /* Propagate mute status upstream past reroutes, but only if all outputs are muted. */
+ if (link->fromnode->is_reroute()) {
+ if (!muted || all_links_muted(*link->fromsock)) {
+ Stack<bNodeLink *> links;
+ links.push_multiple(link->fromnode->input_sockets().first()->directly_linked_links());
+ while (!links.is_empty()) {
+ bNodeLink *link = links.pop();
+ nodeLinkSetMute(&ntree, link, muted);
+ if (!link->fromnode->is_reroute()) {
+ continue;
+ }
+ if (!muted || all_links_muted(*link->fromsock)) {
+ links.push_multiple(link->fromnode->input_sockets().first()->directly_linked_links());
+ }
+ }
}
- link->flag &= ~NODE_LINK_TEST;
}
-
- ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
- return OPERATOR_FINISHED;
}
- return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
+ ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
+ return OPERATOR_FINISHED;
}
void NODE_OT_links_mute(wmOperatorType *ot)
@@ -1619,7 +1619,9 @@ void NODE_OT_parent_set(wmOperatorType *ot)
#define NODE_JOIN_DONE 1
#define NODE_JOIN_IS_DESCENDANT 2
-static void node_join_attach_recursive(bNode *node, bNode *frame)
+static void node_join_attach_recursive(bNode *node,
+ bNode *frame,
+ const Set<bNode *> &selected_nodes)
{
node->done |= NODE_JOIN_DONE;
@@ -1629,21 +1631,21 @@ static void node_join_attach_recursive(bNode *node, bNode *frame)
else if (node->parent) {
/* call recursively */
if (!(node->parent->done & NODE_JOIN_DONE)) {
- node_join_attach_recursive(node->parent, frame);
+ node_join_attach_recursive(node->parent, frame, selected_nodes);
}
/* in any case: if the parent is a descendant, so is the child */
if (node->parent->done & NODE_JOIN_IS_DESCENDANT) {
node->done |= NODE_JOIN_IS_DESCENDANT;
}
- else if (node->flag & NODE_TEST) {
+ else if (selected_nodes.contains(node)) {
/* if parent is not an descendant of the frame, reattach the node */
nodeDetachNode(node);
nodeAttachNode(node, frame);
node->done |= NODE_JOIN_IS_DESCENDANT;
}
}
- else if (node->flag & NODE_TEST) {
+ else if (selected_nodes.contains(node)) {
nodeAttachNode(node, frame);
node->done |= NODE_JOIN_IS_DESCENDANT;
}
@@ -1651,21 +1653,13 @@ static void node_join_attach_recursive(bNode *node, bNode *frame)
static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
{
+ Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
- /* XXX save selection: node_add_node call below sets the new frame as single
- * active+selected node */
- LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
- if (node->flag & NODE_SELECT) {
- node->flag |= NODE_TEST;
- }
- else {
- node->flag &= ~NODE_TEST;
- }
- }
+ const Set<bNode *> selected_nodes = get_selected_nodes(ntree);
- bNode *frame = node_add_node(*C, nullptr, NODE_FRAME, 0.0f, 0.0f);
+ bNode *frame_node = nodeAddStaticNode(C, &ntree, NODE_FRAME);
/* reset tags */
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
@@ -1674,18 +1668,12 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op))
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
if (!(node->done & NODE_JOIN_DONE)) {
- node_join_attach_recursive(node, frame);
- }
- }
-
- /* restore selection */
- LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
- if (node->flag & NODE_TEST) {
- node->flag |= NODE_SELECT;
+ node_join_attach_recursive(node, frame_node, selected_nodes);
}
}
node_sort(ntree);
+ ED_node_tree_propagate_change(C, &bmain, snode.edittree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
@@ -1714,11 +1702,11 @@ void NODE_OT_join(wmOperatorType *ot)
static bNode *node_find_frame_to_attach(ARegion &region,
const bNodeTree &ntree,
- const int mouse_xy[2])
+ const int2 mouse_xy)
{
/* convert mouse coordinates to v2d space */
- float cursor[2];
- UI_view2d_region_to_view(&region.v2d, UNPACK2(mouse_xy), &cursor[0], &cursor[1]);
+ float2 cursor;
+ UI_view2d_region_to_view(&region.v2d, mouse_xy.x, mouse_xy.y, &cursor.x, &cursor.y);
LISTBASE_FOREACH_BACKWARD (bNode *, frame, &ntree.nodes) {
/* skip selected, those are the nodes we want to attach */
@@ -1739,32 +1727,34 @@ static int node_attach_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
bNode *frame = node_find_frame_to_attach(region, ntree, event->mval);
+ if (frame == nullptr) {
+ /* Return "finished" so that auto offset operator macros can work. */
+ return OPERATOR_FINISHED;
+ }
- if (frame) {
- LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) {
- if (node->flag & NODE_SELECT) {
- if (node->parent == nullptr) {
- /* disallow moving a parent into its child */
- if (nodeAttachNodeCheck(frame, node) == false) {
- /* attach all unparented nodes */
- nodeAttachNode(node, frame);
- }
+ LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) {
+ if (node->flag & NODE_SELECT) {
+ if (node->parent == nullptr) {
+ /* disallow moving a parent into its child */
+ if (nodeAttachNodeCheck(frame, node) == false) {
+ /* attach all unparented nodes */
+ nodeAttachNode(node, frame);
}
- else {
- /* attach nodes which share parent with the frame */
- bNode *parent;
- for (parent = frame->parent; parent; parent = parent->parent) {
- if (parent == node->parent) {
- break;
- }
+ }
+ else {
+ /* attach nodes which share parent with the frame */
+ bNode *parent;
+ for (parent = frame->parent; parent; parent = parent->parent) {
+ if (parent == node->parent) {
+ break;
}
+ }
- if (parent) {
- /* disallow moving a parent into its child */
- if (nodeAttachNodeCheck(frame, node) == false) {
- nodeDetachNode(node);
- nodeAttachNode(node, frame);
- }
+ if (parent) {
+ /* disallow moving a parent into its child */
+ if (nodeAttachNodeCheck(frame, node) == false) {
+ nodeDetachNode(node);
+ nodeAttachNode(node, frame);
}
}
}
@@ -1942,6 +1932,7 @@ static bool ed_node_link_conditions(ScrArea *area,
void ED_node_link_intersect_test(ScrArea *area, int test)
{
+ using namespace blender;
using namespace blender::ed::space_node;
bNode *select;
@@ -1965,36 +1956,34 @@ void ED_node_link_intersect_test(ScrArea *area, int test)
bNodeLink *selink = nullptr;
float dist_best = FLT_MAX;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
- float coord_array[NODE_LINK_RESOL + 1][2];
if (node_link_is_hidden_or_dimmed(region->v2d, *link)) {
continue;
}
- if (node_link_bezier_points(nullptr, nullptr, *link, coord_array, NODE_LINK_RESOL)) {
- float dist = FLT_MAX;
+ std::array<float2, NODE_LINK_RESOL + 1> coords;
+ node_link_bezier_points_evaluated(*link, coords);
+ float dist = FLT_MAX;
- /* loop over link coords to find shortest dist to
- * upper left node edge of a intersected line segment */
- for (int i = 0; i < NODE_LINK_RESOL; i++) {
- /* Check if the node rectangle intersects the line from this point to next one. */
- if (BLI_rctf_isect_segment(&select->totr, coord_array[i], coord_array[i + 1])) {
- /* store the shortest distance to the upper left edge
- * of all intersections found so far */
- const float node_xy[] = {select->totr.xmin, select->totr.ymax};
+ /* loop over link coords to find shortest dist to
+ * upper left node edge of a intersected line segment */
+ for (int i = 0; i < NODE_LINK_RESOL; i++) {
+ /* Check if the node rectangle intersects the line from this point to next one. */
+ if (BLI_rctf_isect_segment(&select->totr, coords[i], coords[i + 1])) {
+ /* store the shortest distance to the upper left edge
+ * of all intersections found so far */
+ const float node_xy[] = {select->totr.xmin, select->totr.ymax};
- /* to be precise coord_array should be clipped by select->totr,
- * but not done since there's no real noticeable difference */
- dist = min_ff(
- dist_squared_to_line_segment_v2(node_xy, coord_array[i], coord_array[i + 1]), dist);
- }
+ /* to be precise coords should be clipped by select->totr,
+ * but not done since there's no real noticeable difference */
+ dist = min_ff(dist_squared_to_line_segment_v2(node_xy, coords[i], coords[i + 1]), dist);
}
+ }
- /* we want the link with the shortest distance to node center */
- if (dist < dist_best) {
- dist_best = dist;
- selink = link;
- }
+ /* we want the link with the shortest distance to node center */
+ if (dist < dist_best) {
+ dist_best = dist;
+ selink = link;
}
}
@@ -2048,7 +2037,7 @@ static bNodeSocket *get_main_socket(bNodeTree &ntree, bNode &node, eNodeSocketIn
/* Try to get the main socket based on the socket declaration. */
nodeDeclarationEnsure(&ntree, &node);
- const nodes::NodeDeclaration *node_decl = node.runtime->declaration;
+ const nodes::NodeDeclaration *node_decl = node.declaration();
if (node_decl != nullptr) {
Span<nodes::SocketDeclarationPtr> socket_decls = (in_out == SOCK_IN) ? node_decl->inputs() :
node_decl->outputs();
@@ -2323,10 +2312,10 @@ static void node_link_insert_offset_ntree(NodeInsertOfsData *iofsd,
/**
* Modal handler for insert offset animation
*/
-static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
+static int node_insert_offset_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
SpaceNode *snode = CTX_wm_space_node(C);
- NodeInsertOfsData *iofsd = snode->runtime->iofsd;
+ NodeInsertOfsData *iofsd = static_cast<NodeInsertOfsData *>(op->customdata);
bool redraw = false;
if (!snode || event->type != TIMER || iofsd == nullptr ||
@@ -2366,7 +2355,6 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w
node->anim_init_locx = node->anim_ofsx = 0.0f;
}
- snode->runtime->iofsd = nullptr;
MEM_freeN(iofsd);
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
@@ -2381,6 +2369,8 @@ static int node_insert_offset_invoke(bContext *C, wmOperator *op, const wmEvent
{
const SpaceNode *snode = CTX_wm_space_node(C);
NodeInsertOfsData *iofsd = snode->runtime->iofsd;
+ snode->runtime->iofsd = nullptr;
+ op->customdata = iofsd;
if (!iofsd || !iofsd->insert) {
return OPERATOR_CANCELLED;
@@ -2487,6 +2477,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
/* Set up insert offset data, it needs stuff from here. */
if ((snode->flag & SNODE_SKIP_INSOFFSET) == 0) {
+ BLI_assert(snode->runtime->iofsd == nullptr);
NodeInsertOfsData *iofsd = MEM_cnew<NodeInsertOfsData>(__func__);
iofsd->insert = node_to_insert;
diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc
index 9d73156edab..d93b205b1b7 100644
--- a/source/blender/editors/space_node/node_select.cc
+++ b/source/blender/editors/space_node/node_select.cc
@@ -22,6 +22,7 @@
#include "BKE_context.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
#include "BKE_workspace.h"
#include "ED_node.h" /* own include */
@@ -48,7 +49,7 @@
namespace blender::ed::space_node {
-static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event);
+static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event);
/**
* Function to detect if there is a visible view3d that uses workbench in texture mode.
@@ -100,17 +101,17 @@ rctf node_frame_rect_inside(const bNode &node)
return frame_inside;
}
-bool node_or_socket_isect_event(bContext *C, const wmEvent *event)
+bool node_or_socket_isect_event(const bContext &C, const wmEvent &event)
{
return is_event_over_node_or_socket(C, event);
}
-static bool node_frame_select_isect_mouse(bNode *node, const float2 &mouse)
+static bool node_frame_select_isect_mouse(const bNode &node, const float2 &mouse)
{
/* Frame nodes are selectable by their borders (including their whole rect - as for other nodes -
* would prevent e.g. box selection of nodes inside that frame). */
- const rctf frame_inside = node_frame_rect_inside(*node);
- if (BLI_rctf_isect_pt(&node->totr, mouse.x, mouse.y) &&
+ const rctf frame_inside = node_frame_rect_inside(node);
+ if (BLI_rctf_isect_pt(&node.totr, mouse.x, mouse.y) &&
!BLI_rctf_isect_pt(&frame_inside, mouse.x, mouse.y)) {
return true;
}
@@ -118,19 +119,18 @@ static bool node_frame_select_isect_mouse(bNode *node, const float2 &mouse)
return false;
}
-static bNode *node_under_mouse_select(bNodeTree &ntree, int mx, int my)
+static bNode *node_under_mouse_select(bNodeTree &ntree, const float2 mouse)
{
LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) {
switch (node->type) {
case NODE_FRAME: {
- const float2 mouse{(float)mx, (float)my};
- if (node_frame_select_isect_mouse(node, mouse)) {
+ if (node_frame_select_isect_mouse(*node, mouse)) {
return node;
}
break;
}
default: {
- if (BLI_rctf_isect_pt(&node->totr, mx, my)) {
+ if (BLI_rctf_isect_pt(&node->totr, int(mouse.x), int(mouse.y))) {
return node;
}
break;
@@ -140,35 +140,32 @@ static bNode *node_under_mouse_select(bNodeTree &ntree, int mx, int my)
return nullptr;
}
-static bNode *node_under_mouse_tweak(bNodeTree &ntree, const float2 &mouse)
+static bool node_under_mouse_tweak(const bNodeTree &ntree, const float2 &mouse)
{
- using namespace blender::math;
-
- LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree.nodes) {
+ LISTBASE_FOREACH_BACKWARD (const bNode *, node, &ntree.nodes) {
switch (node->type) {
case NODE_REROUTE: {
- bNodeSocket *socket = (bNodeSocket *)node->inputs.first;
- const float2 location{socket->locx, socket->locy};
- if (distance(mouse, location) < 24.0f) {
- return node;
+ const float2 location = node_to_view(*node, {node->locx, node->locy});
+ if (math::distance(mouse, location) < 24.0f) {
+ return true;
}
break;
}
case NODE_FRAME: {
- if (node_frame_select_isect_mouse(node, mouse)) {
- return node;
+ if (node_frame_select_isect_mouse(*node, mouse)) {
+ return true;
}
break;
}
default: {
if (BLI_rctf_isect_pt(&node->totr, mouse.x, mouse.y)) {
- return node;
+ return true;
}
break;
}
}
}
- return nullptr;
+ return false;
}
static bool is_position_over_node_or_socket(SpaceNode &snode, const float2 &mouse)
@@ -187,17 +184,17 @@ static bool is_position_over_node_or_socket(SpaceNode &snode, const float2 &mous
return false;
}
-static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event)
+static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event)
{
- SpaceNode *snode = CTX_wm_space_node(C);
- ARegion *region = CTX_wm_region(C);
- float2 mouse;
+ SpaceNode &snode = *CTX_wm_space_node(&C);
+ ARegion &region = *CTX_wm_region(&C);
- int mval[2];
- WM_event_drag_start_mval(event, region, mval);
+ int2 mval;
+ WM_event_drag_start_mval(&event, &region, mval);
- UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &mouse.x, &mouse.y);
- return is_position_over_node_or_socket(*snode, mouse);
+ float2 mouse;
+ UI_view2d_region_to_view(&region.v2d, mval.x, mval.y, &mouse.x, &mouse.y);
+ return is_position_over_node_or_socket(snode, mouse);
}
void node_socket_select(bNode *node, bNodeSocket &sock)
@@ -314,6 +311,17 @@ void node_deselect_all_output_sockets(SpaceNode &snode, const bool deselect_node
}
}
+Set<bNode *> get_selected_nodes(bNodeTree &node_tree)
+{
+ Set<bNode *> selected_nodes;
+ for (bNode *node : node_tree.all_nodes()) {
+ if (node->flag & NODE_SELECT) {
+ selected_nodes.add(node);
+ }
+ }
+ return selected_nodes;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -412,9 +420,7 @@ static int node_select_grouped_exec(bContext *C, wmOperator *op)
const int type = RNA_enum_get(op->ptr, "type");
if (!extend) {
- LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
- nodeSetSelected(node, false);
- }
+ node_deselect_all(snode);
}
nodeSetSelected(node_act, true);
@@ -514,8 +520,8 @@ void node_select_single(bContext &C, bNode &node)
static bool node_mouse_select(bContext *C,
wmOperator *op,
- const int mval[2],
- struct SelectPick_Params *params)
+ const int2 mval,
+ SelectPick_Params *params)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
@@ -526,7 +532,6 @@ static bool node_mouse_select(bContext *C,
bNode *node, *tnode;
bNodeSocket *sock = nullptr;
bNodeSocket *tsock;
- float cursor[2];
/* always do socket_select when extending selection. */
const bool socket_select = (params->sel_op == SEL_OP_XOR) ||
@@ -536,7 +541,8 @@ static bool node_mouse_select(bContext *C,
bool node_was_selected = false;
/* get mouse coordinates in view2d space */
- UI_view2d_region_to_view(&region.v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
+ float2 cursor;
+ UI_view2d_region_to_view(&region.v2d, mval.x, mval.y, &cursor.x, &cursor.y);
/* first do socket selection, these generally overlap with nodes. */
if (socket_select) {
@@ -593,7 +599,7 @@ static bool node_mouse_select(bContext *C,
if (!sock) {
/* find the closest visible node */
- node = node_under_mouse_select(*snode.edittree, (int)cursor[0], (int)cursor[1]);
+ node = node_under_mouse_select(*snode.edittree, cursor);
found = (node != nullptr);
node_was_selected = node && (node->flag & SELECT);
@@ -603,9 +609,7 @@ static bool node_mouse_select(bContext *C,
}
else if (found || params->deselect_all) {
/* Deselect everything. */
- for (tnode = (bNode *)snode.edittree->nodes.first; tnode; tnode = tnode->next) {
- nodeSetSelected(tnode, false);
- }
+ node_deselect_all(snode);
changed = true;
}
}
@@ -640,37 +644,38 @@ static bool node_mouse_select(bContext *C,
}
}
- /* update node order */
- if (changed || found) {
- bool active_texture_changed = false;
- bool viewer_node_changed = false;
- if ((node != nullptr) && (node_was_selected == false || params->select_passthrough == false)) {
- viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 && node->type == GEO_NODE_VIEWER;
- ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed);
- }
- else if (node != nullptr && node->type == GEO_NODE_VIEWER) {
- ED_spreadsheet_context_paths_set_geometry_node(&bmain, &snode, node);
- }
- ED_node_set_active_viewer_key(&snode);
- node_sort(*snode.edittree);
- if ((active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) ||
- viewer_node_changed) {
- DEG_id_tag_update(&snode.edittree->id, ID_RECALC_COPY_ON_WRITE);
- }
+ if (!(changed || found)) {
+ return false;
+ }
- WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
+ bool active_texture_changed = false;
+ bool viewer_node_changed = false;
+ if ((node != nullptr) && (node_was_selected == false || params->select_passthrough == false)) {
+ viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 && node->type == GEO_NODE_VIEWER;
+ ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed);
+ }
+ else if (node != nullptr && node->type == GEO_NODE_VIEWER) {
+ ED_spreadsheet_context_paths_set_geometry_node(&bmain, &snode, node);
}
+ ED_node_set_active_viewer_key(&snode);
+ node_sort(*snode.edittree);
+ if ((active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) ||
+ viewer_node_changed) {
+ DEG_id_tag_update(&snode.edittree->id, ID_RECALC_COPY_ON_WRITE);
+ }
+
+ WM_event_add_notifier(C, NC_NODE | NA_SELECTED, nullptr);
- return changed || found;
+ return true;
}
static int node_select_exec(bContext *C, wmOperator *op)
{
/* get settings from RNA properties for operator */
- int mval[2];
+ int2 mval;
RNA_int_get_array(op->ptr, "location", mval);
- struct SelectPick_Params params = {};
+ SelectPick_Params params = {};
ED_select_pick_params_from_operator(op->ptr, &params);
/* perform the select */
@@ -747,7 +752,7 @@ static int node_box_select_exec(bContext *C, wmOperator *op)
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)) {
- node_select_all(&node_tree.nodes, SEL_DESELECT);
+ node_deselect_all(snode);
}
LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
@@ -787,7 +792,7 @@ static int node_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *ev
{
const bool tweak = RNA_boolean_get(op->ptr, "tweak");
- if (tweak && is_event_over_node_or_socket(C, event)) {
+ if (tweak && is_event_over_node_or_socket(*C, *event)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
@@ -836,7 +841,7 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
bNode *node;
int x, y, radius;
- float offset[2];
+ float2 offset;
float zoom = (float)(BLI_rcti_size_x(&region->winrct)) /
(float)(BLI_rctf_size_x(&region->v2d.cur));
@@ -846,7 +851,7 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
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)) {
- node_select_all(&snode->edittree->nodes, SEL_DESELECT);
+ node_deselect_all(*snode);
}
/* get operator properties */
@@ -854,7 +859,7 @@ static int node_circleselect_exec(bContext *C, wmOperator *op)
y = RNA_int_get(op->ptr, "y");
radius = RNA_int_get(op->ptr, "radius");
- UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
+ UI_view2d_region_to_view(&region->v2d, x, y, &offset.x, &offset.y);
for (node = (bNode *)snode->edittree->nodes.first; node; node = node->next) {
switch (node->type) {
@@ -916,7 +921,7 @@ static int node_lasso_select_invoke(bContext *C, wmOperator *op, const wmEvent *
{
const bool tweak = RNA_boolean_get(op->ptr, "tweak");
- if (tweak && is_event_over_node_or_socket(C, event)) {
+ if (tweak && is_event_over_node_or_socket(*C, *event)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
@@ -938,7 +943,7 @@ static bool do_lasso_select_node(bContext *C,
const bool select = (sel_op != SEL_OP_SUB);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- node_select_all(&snode->edittree->nodes, SEL_DESELECT);
+ node_deselect_all(*snode);
changed = true;
}
@@ -968,14 +973,14 @@ static bool do_lasso_select_node(bContext *C,
break;
}
default: {
- int screen_co[2];
- const float cent[2] = {BLI_rctf_cent_x(&node->totr), BLI_rctf_cent_y(&node->totr)};
+ int2 screen_co;
+ const float2 center = {BLI_rctf_cent_x(&node->totr), BLI_rctf_cent_y(&node->totr)};
/* marker in screen coords */
if (UI_view2d_view_to_region_clip(
- &region->v2d, cent[0], cent[1], &screen_co[0], &screen_co[1]) &&
- BLI_rcti_isect_pt(&rect, screen_co[0], screen_co[1]) &&
- BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co[0], screen_co[1], INT_MAX)) {
+ &region->v2d, center.x, center.y, &screen_co.x, &screen_co.y) &&
+ BLI_rcti_isect_pt(&rect, screen_co.x, screen_co.y) &&
+ BLI_lasso_is_point_inside(mcoords, mcoords_len, screen_co.x, screen_co.y, INT_MAX)) {
nodeSetSelected(node, select);
changed = true;
}
@@ -1042,13 +1047,48 @@ void NODE_OT_select_lasso(wmOperatorType *ot)
/** \name (De)select All Operator
* \{ */
+static bool any_node_selected(const bNodeTree &node_tree)
+{
+ for (const bNode *node : node_tree.all_nodes()) {
+ if (node->flag & NODE_SELECT) {
+ return true;
+ }
+ }
+ return false;
+}
+
static int node_select_all_exec(bContext *C, wmOperator *op)
{
SpaceNode &snode = *CTX_wm_space_node(C);
- ListBase *node_lb = &snode.edittree->nodes;
+ bNodeTree &node_tree = *snode.edittree;
+
+ node_tree.ensure_topology_cache();
+
int action = RNA_enum_get(op->ptr, "action");
+ if (action == SEL_TOGGLE) {
+ if (any_node_selected(node_tree)) {
+ action = SEL_DESELECT;
+ }
+ else {
+ action = SEL_SELECT;
+ }
+ }
- node_select_all(node_lb, action);
+ switch (action) {
+ case SEL_SELECT:
+ for (bNode *node : node_tree.all_nodes()) {
+ nodeSetSelected(node, true);
+ }
+ break;
+ case SEL_DESELECT:
+ node_deselect_all(snode);
+ break;
+ case SEL_INVERT:
+ for (bNode *node : node_tree.all_nodes()) {
+ nodeSetSelected(node, !(node->flag & SELECT));
+ }
+ break;
+ }
node_sort(*snode.edittree);
@@ -1084,22 +1124,21 @@ static int node_select_linked_to_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
- LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
- node->flag &= ~NODE_TEST;
- }
+ node_tree.ensure_topology_cache();
- LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
- if (nodeLinkIsHidden(link)) {
- continue;
- }
- if (link->fromnode && link->tonode && (link->fromnode->flag & NODE_SELECT)) {
- link->tonode->flag |= NODE_TEST;
- }
- }
+ Set<bNode *> initial_selection = get_selected_nodes(node_tree);
- LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
- if (node->flag & NODE_TEST) {
- nodeSetSelected(node, true);
+ for (bNode *node : initial_selection) {
+ for (bNodeSocket *output_socket : node->output_sockets()) {
+ if (!output_socket->is_available()) {
+ continue;
+ }
+ for (bNodeSocket *input_socket : output_socket->directly_linked_sockets()) {
+ if (!input_socket->is_available()) {
+ continue;
+ }
+ nodeSetSelected(&input_socket->owner_node(), true);
+ }
}
}
@@ -1135,22 +1174,21 @@ static int node_select_linked_from_exec(bContext *C, wmOperator *UNUSED(op))
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
- LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
- node->flag &= ~NODE_TEST;
- }
+ node_tree.ensure_topology_cache();
- LISTBASE_FOREACH (bNodeLink *, link, &node_tree.links) {
- if (nodeLinkIsHidden(link)) {
- continue;
- }
- if (link->fromnode && link->tonode && (link->tonode->flag & NODE_SELECT)) {
- link->fromnode->flag |= NODE_TEST;
- }
- }
+ Set<bNode *> initial_selection = get_selected_nodes(node_tree);
- LISTBASE_FOREACH (bNode *, node, &node_tree.nodes) {
- if (node->flag & NODE_TEST) {
- nodeSetSelected(node, true);
+ for (bNode *node : initial_selection) {
+ for (bNodeSocket *input_socket : node->input_sockets()) {
+ if (!input_socket->is_available()) {
+ continue;
+ }
+ for (bNodeSocket *output_socket : input_socket->directly_linked_sockets()) {
+ if (!output_socket->is_available()) {
+ continue;
+ }
+ nodeSetSelected(&output_socket->owner_node(), true);
+ }
}
}
@@ -1298,7 +1336,7 @@ static void node_find_create_label(const bNode *node, char *str, int maxlen)
}
/* Generic search invoke. */
-static void node_find_update_fn(const struct bContext *C,
+static void node_find_update_fn(const bContext *C,
void *UNUSED(arg),
const char *str,
uiSearchItems *items,
@@ -1330,7 +1368,7 @@ static void node_find_update_fn(const struct bContext *C,
BLI_string_search_free(search);
}
-static void node_find_exec_fn(struct bContext *C, void *UNUSED(arg1), void *arg2)
+static void node_find_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNode *active = (bNode *)arg2;
diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc
index 58a313c328e..5fc194e02a4 100644
--- a/source/blender/editors/space_node/node_templates.cc
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -758,43 +758,42 @@ namespace blender::ed::space_node {
/**************************** Node Tree Layout *******************************/
static void ui_node_draw_input(
- uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth);
+ uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, bNodeSocket &input, int depth);
static void ui_node_draw_node(
- uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, int depth)
+ uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, int depth)
{
- bNodeSocket *input;
PointerRNA nodeptr;
- RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
+ RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
- if (node->typeinfo->draw_buttons) {
- if (node->type != NODE_GROUP) {
- uiLayoutSetPropSep(layout, true);
- node->typeinfo->draw_buttons(layout, C, &nodeptr);
+ if (node.typeinfo->draw_buttons) {
+ if (node.type != NODE_GROUP) {
+ uiLayoutSetPropSep(&layout, true);
+ node.typeinfo->draw_buttons(&layout, &C, &nodeptr);
}
}
- for (input = (bNodeSocket *)node->inputs.first; input; input = input->next) {
- ui_node_draw_input(layout, C, ntree, node, input, depth + 1);
+ LISTBASE_FOREACH (bNodeSocket *, input, &node.inputs) {
+ ui_node_draw_input(layout, C, ntree, node, *input, depth + 1);
}
}
static void ui_node_draw_input(
- uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth)
+ uiLayout &layout, bContext &C, bNodeTree &ntree, bNode &node, bNodeSocket &input, int depth)
{
PointerRNA inputptr, nodeptr;
- uiBlock *block = uiLayoutGetBlock(layout);
+ uiBlock *block = uiLayoutGetBlock(&layout);
uiLayout *row = nullptr;
bool dependency_loop;
- if (input->flag & SOCK_UNAVAIL) {
+ if (input.flag & SOCK_UNAVAIL) {
return;
}
/* to avoid eternal loops on cyclic dependencies */
- node->flag |= NODE_TEST;
- bNode *lnode = (input->link) ? input->link->fromnode : nullptr;
+ node.flag |= NODE_TEST;
+ bNode *lnode = (input.link) ? input.link->fromnode : nullptr;
dependency_loop = (lnode && (lnode->flag & NODE_TEST));
if (dependency_loop) {
@@ -802,10 +801,10 @@ static void ui_node_draw_input(
}
/* socket RNA pointer */
- RNA_pointer_create(&ntree->id, &RNA_NodeSocket, input, &inputptr);
- RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
+ RNA_pointer_create(&ntree.id, &RNA_NodeSocket, &input, &inputptr);
+ RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
- row = uiLayoutRow(layout, true);
+ row = uiLayoutRow(&layout, true);
/* Decorations are added manually here. */
uiLayoutSetPropDecorate(row, false);
@@ -821,8 +820,8 @@ static void ui_node_draw_input(
if (lnode &&
(lnode->inputs.first || (lnode->typeinfo->draw_buttons && lnode->type != NODE_GROUP))) {
- int icon = (input->flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT :
- ICON_DISCLOSURE_TRI_DOWN;
+ int icon = (input.flag & SOCK_COLLAPSED) ? ICON_DISCLOSURE_TRI_RIGHT :
+ ICON_DISCLOSURE_TRI_DOWN;
uiItemR(sub, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon);
}
@@ -831,7 +830,7 @@ static void ui_node_draw_input(
sub = uiLayoutRow(sub, true);
uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT);
- uiItemL(sub, IFACE_(input->name), ICON_NONE);
+ uiItemL(sub, IFACE_(input.name), ICON_NONE);
}
if (dependency_loop) {
@@ -840,28 +839,28 @@ static void ui_node_draw_input(
}
else if (lnode) {
/* input linked to a node */
- uiTemplateNodeLink(row, C, ntree, node, input);
+ uiTemplateNodeLink(row, &C, &ntree, &node, &input);
add_dummy_decorator = true;
- if (depth == 0 || !(input->flag & SOCK_COLLAPSED)) {
+ if (depth == 0 || !(input.flag & SOCK_COLLAPSED)) {
if (depth == 0) {
- uiItemS(layout);
+ uiItemS(&layout);
}
- ui_node_draw_node(layout, C, ntree, lnode, depth);
+ ui_node_draw_node(layout, C, ntree, *lnode, depth);
}
}
else {
uiLayout *sub = uiLayoutRow(row, true);
- uiTemplateNodeLink(sub, C, ntree, node, input);
+ uiTemplateNodeLink(sub, &C, &ntree, &node, &input);
- if (input->flag & SOCK_HIDE_VALUE) {
+ if (input.flag & SOCK_HIDE_VALUE) {
add_dummy_decorator = true;
}
/* input not linked, show value */
else {
- switch (input->type) {
+ switch (input.type) {
case SOCK_VECTOR:
uiItemS(sub);
sub = uiLayoutColumn(sub, true);
@@ -876,11 +875,11 @@ static void ui_node_draw_input(
break;
case SOCK_STRING: {
const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id;
- SpaceNode *snode = CTX_wm_space_node(C);
+ SpaceNode *snode = CTX_wm_space_node(&C);
if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) {
/* Only add the attribute search in the node editor, in other places there is not
* enough context. */
- node_geometry_add_attribute_search_button(*C, *node, inputptr, *sub);
+ node_geometry_add_attribute_search_button(C, node, inputptr, *sub);
}
else {
uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE);
@@ -899,10 +898,10 @@ static void ui_node_draw_input(
uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0);
}
- node_socket_add_tooltip(ntree, node, input, row);
+ node_socket_add_tooltip(ntree, node, input, *row);
/* clear */
- node->flag &= ~NODE_TEST;
+ node.flag &= ~NODE_TEST;
}
} // namespace blender::ed::space_node
@@ -924,9 +923,9 @@ void uiTemplateNodeView(
}
if (input) {
- ui_node_draw_input(layout, C, ntree, node, input, 0);
+ ui_node_draw_input(*layout, *C, *ntree, *node, *input, 0);
}
else {
- ui_node_draw_node(layout, C, ntree, node, 0);
+ ui_node_draw_node(*layout, *C, *ntree, *node, 0);
}
}
diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc
index 6f30632244b..33a75385022 100644
--- a/source/blender/editors/space_node/node_view.cc
+++ b/source/blender/editors/space_node/node_view.cc
@@ -177,7 +177,7 @@ void NODE_OT_view_selected(wmOperatorType *ot)
* \{ */
struct NodeViewMove {
- int mvalo[2];
+ int2 mvalo;
int xmin, ymin, xmax, ymax;
/** Original Offset for cancel. */
float xof_orig, yof_orig;
@@ -192,10 +192,10 @@ static int snode_bg_viewmove_modal(bContext *C, wmOperator *op, const wmEvent *e
switch (event->type) {
case MOUSEMOVE:
- snode->xof -= (nvm->mvalo[0] - event->mval[0]);
- snode->yof -= (nvm->mvalo[1] - event->mval[1]);
- nvm->mvalo[0] = event->mval[0];
- nvm->mvalo[1] = event->mval[1];
+ snode->xof -= (nvm->mvalo.x - event->mval[0]);
+ snode->yof -= (nvm->mvalo.y - event->mval[1]);
+ nvm->mvalo.x = event->mval[0];
+ nvm->mvalo.y = event->mval[1];
/* prevent dragging image outside of the window and losing it! */
CLAMP(snode->xof, nvm->xmin, nvm->xmax);
@@ -240,7 +240,7 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
NodeViewMove *nvm;
Image *ima;
ImBuf *ibuf;
- const float pad = 32.0f; /* better be bigger than scrollbars */
+ const float pad = 32.0f; /* Better be bigger than scroll-bars. */
void *lock;
@@ -252,10 +252,10 @@ static int snode_bg_viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *
return OPERATOR_CANCELLED;
}
- nvm = MEM_cnew<NodeViewMove>("NodeViewMove struct");
+ nvm = MEM_cnew<NodeViewMove>(__func__);
op->customdata = nvm;
- nvm->mvalo[0] = event->mval[0];
- nvm->mvalo[1] = event->mval[1];
+ nvm->mvalo.x = event->mval[0];
+ nvm->mvalo.y = event->mval[1];
nvm->xmin = -(region->winx / 2) - (ibuf->x * (0.5f * snode->zoom)) + pad;
nvm->xmax = (region->winx / 2) + (ibuf->x * (0.5f * snode->zoom)) - pad;
@@ -447,7 +447,7 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
} // namespace blender::ed::space_node
bool ED_space_node_get_position(
- Main *bmain, SpaceNode *snode, struct ARegion *region, const int mval[2], float fpos[2])
+ Main *bmain, SpaceNode *snode, ARegion *region, const int mval[2], float fpos[2])
{
if (!ED_node_is_compositor(snode) || (snode->flag & SNODE_BACKDRAW) == 0) {
return false;
@@ -645,7 +645,7 @@ static int sample_invoke(bContext *C, wmOperator *op, const wmEvent *event)
/* Don't handle events intended for nodes (which rely on click/drag distinction).
* which this operator would use since sampling is normally activated on press, see: T98191. */
- if (node_or_socket_isect_event(C, event)) {
+ if (node_or_socket_isect_event(*C, *event)) {
return OPERATOR_PASS_THROUGH;
}
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index 15afd024766..fae3eb1a143 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -302,7 +302,7 @@ static void node_free(SpaceLink *sl)
}
/* spacetype; init callback */
-static void node_init(struct wmWindowManager *UNUSED(wm), ScrArea *area)
+static void node_init(wmWindowManager *UNUSED(wm), ScrArea *area)
{
SpaceNode *snode = (SpaceNode *)area->spacedata.first;
@@ -362,7 +362,7 @@ static void node_area_tag_tree_recalc(SpaceNode *snode, ScrArea *area)
static void node_area_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* NOTE: #ED_area_tag_refresh will re-execute compositor. */
SpaceNode *snode = (SpaceNode *)area->spacedata.first;
@@ -511,7 +511,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
}
}
-static void node_area_refresh(const struct bContext *C, ScrArea *area)
+static void node_area_refresh(const bContext *C, ScrArea *area)
{
/* default now: refresh node is starting preview */
SpaceNode *snode = (SpaceNode *)area->spacedata.first;
@@ -526,7 +526,7 @@ static void node_area_refresh(const struct bContext *C, ScrArea *area)
if (snode->runtime->recalc_auto_compositing) {
snode->runtime->recalc_auto_compositing = false;
snode->runtime->recalc_regular_compositing = false;
- node_render_changed_exec((struct bContext *)C, nullptr);
+ node_render_changed_exec((bContext *)C, nullptr);
}
else if (snode->runtime->recalc_regular_compositing) {
snode->runtime->recalc_regular_compositing = false;
@@ -753,7 +753,7 @@ static void node_header_region_draw(const bContext *C, ARegion *region)
static void node_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
wmGizmoMap *gzmap = region->gizmo_map;
/* context changes */
@@ -973,9 +973,7 @@ static void node_id_remap_cb(ID *old_id, ID *new_id, void *user_data)
}
}
-static void node_id_remap(ScrArea *UNUSED(area),
- SpaceLink *slink,
- const struct IDRemapper *mappings)
+static void node_id_remap(ScrArea *UNUSED(area), SpaceLink *slink, const IDRemapper *mappings)
{
/* Although we should be able to perform all the mappings in a single go this lead to issues when
* running the python test cases. Somehow the nodetree/edittree weren't updated to the new
@@ -1023,7 +1021,7 @@ void ED_spacetype_node()
ARegionType *art;
st->spaceid = SPACE_NODE;
- strncpy(st->name, "Node", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Node");
st->create = node_create;
st->free = node_free;
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index 97d2957eed2..d29028dad63 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -13,7 +13,6 @@ set(INC
../../sequencer
../../windowmanager
../../../../intern/clog
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@@ -52,6 +51,7 @@ set(SRC
tree/tree_element_id.cc
tree/tree_element_id_library.cc
tree/tree_element_id_scene.cc
+ tree/tree_element_label.cc
tree/tree_element_nla.cc
tree/tree_element_overrides.cc
tree/tree_element_rna.cc
@@ -71,6 +71,7 @@ set(SRC
tree/tree_element_id.hh
tree/tree_element_id_library.hh
tree/tree_element_id_scene.hh
+ tree/tree_element_label.hh
tree/tree_element_nla.hh
tree/tree_element_overrides.hh
tree/tree_element_rna.hh
diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc
index 8ca2ffe6a9c..48e7aa381ef 100644
--- a/source/blender/editors/space_outliner/outliner_collections.cc
+++ b/source/blender/editors/space_outliner/outliner_collections.cc
@@ -38,6 +38,8 @@
#include "outliner_intern.hh" /* own include */
+namespace blender::ed::outliner {
+
/* -------------------------------------------------------------------- */
/** \name Utility API
* \{ */
@@ -72,7 +74,7 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te)
}
if (tselem->type == TSE_LAYER_COLLECTION) {
- LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata);
+ LayerCollection *lc = static_cast<LayerCollection *>(te->directdata);
return lc->collection;
}
if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
@@ -86,9 +88,9 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te)
return nullptr;
}
-TreeTraversalAction outliner_find_selected_collections(TreeElement *te, void *customdata)
+TreeTraversalAction outliner_collect_selected_collections(TreeElement *te, void *customdata)
{
- struct IDsSelectedData *data = reinterpret_cast<IDsSelectedData *>(customdata);
+ struct IDsSelectedData *data = static_cast<IDsSelectedData *>(customdata);
TreeStoreElem *tselem = TREESTORE(te);
if (outliner_is_collection_tree_element(te)) {
@@ -103,9 +105,9 @@ TreeTraversalAction outliner_find_selected_collections(TreeElement *te, void *cu
return TRAVERSE_CONTINUE;
}
-TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata)
+TreeTraversalAction outliner_collect_selected_objects(TreeElement *te, void *customdata)
{
- struct IDsSelectedData *data = reinterpret_cast<IDsSelectedData *>(customdata);
+ struct IDsSelectedData *data = static_cast<IDsSelectedData *>(customdata);
TreeStoreElem *tselem = TREESTORE(te);
if (outliner_is_collection_tree_element(te)) {
@@ -122,15 +124,19 @@ TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *custom
return TRAVERSE_CONTINUE;
}
+} // namespace blender::ed::outliner
+
void ED_outliner_selected_objects_get(const bContext *C, ListBase *objects)
{
+ using namespace blender::ed::outliner;
+
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
struct IDsSelectedData data = {{nullptr}};
outliner_tree_traverse(space_outliner,
&space_outliner->tree,
0,
TSE_SELECTED,
- outliner_find_selected_objects,
+ outliner_collect_selected_objects,
&data);
LISTBASE_FOREACH (LinkData *, link, &data.selected_array) {
TreeElement *ten_selected = (TreeElement *)link->data;
@@ -140,12 +146,16 @@ void ED_outliner_selected_objects_get(const bContext *C, ListBase *objects)
BLI_freelistN(&data.selected_array);
}
+namespace blender::ed::outliner {
+
/** \} */
/* -------------------------------------------------------------------- */
/** \name Poll Functions
* \{ */
+} // namespace blender::ed::outliner
+
bool ED_outliner_collections_editor_poll(bContext *C)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -153,6 +163,8 @@ bool ED_outliner_collections_editor_poll(bContext *C)
ELEM(space_outliner->outlinevis, SO_VIEW_LAYER, SO_SCENES, SO_LIBRARIES);
}
+namespace blender::ed::outliner {
+
static bool outliner_view_layer_collections_editor_poll(bContext *C)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -184,7 +196,7 @@ struct CollectionNewData {
static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata)
{
- struct CollectionNewData *data = reinterpret_cast<CollectionNewData *>(customdata);
+ struct CollectionNewData *data = static_cast<CollectionNewData *>(customdata);
Collection *collection = outliner_collection_from_tree_element(te);
if (!collection) {
@@ -284,9 +296,9 @@ struct CollectionEditData {
bool is_liboverride_hierarchy_root_allowed;
};
-static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *customdata)
+static TreeTraversalAction collection_collect_data_to_edit(TreeElement *te, void *customdata)
{
- CollectionEditData *data = reinterpret_cast<CollectionEditData *>(customdata);
+ CollectionEditData *data = static_cast<CollectionEditData *>(customdata);
Collection *collection = outliner_collection_from_tree_element(te);
if (!collection) {
@@ -333,13 +345,17 @@ void outliner_collection_delete(
/* We first walk over and find the Collections we actually want to delete
* (ignoring duplicates). */
- outliner_tree_traverse(
- space_outliner, &space_outliner->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data);
+ outliner_tree_traverse(space_outliner,
+ &space_outliner->tree,
+ 0,
+ TSE_SELECTED,
+ collection_collect_data_to_edit,
+ &data);
/* Effectively delete the collections. */
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- Collection *collection = reinterpret_cast<Collection *>(
+ Collection *collection = static_cast<Collection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
/* Test in case collection got deleted as part of another one. */
@@ -361,10 +377,8 @@ void outliner_collection_delete(
if (parent->flag & COLLECTION_IS_MASTER) {
BLI_assert(parent->id.flag & LIB_EMBEDDED_DATA);
- const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id);
- BLI_assert(id_type->owner_get != nullptr);
-
- ID *scene_owner = id_type->owner_get(bmain, &parent->id);
+ ID *scene_owner = BKE_id_owner_get(&parent->id);
+ BLI_assert(scene_owner != nullptr);
BLI_assert(GS(scene_owner->name) == ID_SCE);
if (ID_IS_LINKED(scene_owner) || ID_IS_OVERRIDE_LIBRARY(scene_owner)) {
skip = true;
@@ -397,7 +411,8 @@ static int collection_hierarchy_delete_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
- const Base *basact_prev = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Base *basact_prev = BKE_view_layer_active_base_get(view_layer);
outliner_collection_delete(C, bmain, scene, op->reports, true);
@@ -406,7 +421,8 @@ static int collection_hierarchy_delete_exec(bContext *C, wmOperator *op)
WM_main_add_notifier(NC_SCENE | ND_LAYER, nullptr);
- if (basact_prev != BASACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (basact_prev != BKE_view_layer_active_base_get(view_layer)) {
WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
}
@@ -444,12 +460,12 @@ struct CollectionObjectsSelectData {
static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te,
void *customdata)
{
- CollectionObjectsSelectData *data = reinterpret_cast<CollectionObjectsSelectData *>(customdata);
+ CollectionObjectsSelectData *data = static_cast<CollectionObjectsSelectData *>(customdata);
TreeStoreElem *tselem = TREESTORE(te);
switch (tselem->type) {
case TSE_LAYER_COLLECTION:
- data->layer_collection = reinterpret_cast<LayerCollection *>(te->directdata);
+ data->layer_collection = static_cast<LayerCollection *>(te->directdata);
return TRAVERSE_BREAK;
case TSE_R_LAYER:
case TSE_SCENE_COLLECTION_BASE:
@@ -477,6 +493,7 @@ static LayerCollection *outliner_active_layer_collection(bContext *C)
static int collection_objects_select_exec(bContext *C, wmOperator *op)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
LayerCollection *layer_collection = outliner_active_layer_collection(C);
bool deselect = STREQ(op->idname, "OUTLINER_OT_collection_objects_deselect");
@@ -485,9 +502,8 @@ static int collection_objects_select_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- BKE_layer_collection_objects_select(view_layer, layer_collection, deselect);
+ BKE_layer_collection_objects_select(scene, view_layer, layer_collection, deselect);
- Scene *scene = CTX_data_scene(C);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene);
ED_outliner_select_sync_from_object_tag(C);
@@ -538,7 +554,7 @@ struct CollectionDuplicateData {
static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te,
void *customdata)
{
- CollectionDuplicateData *data = reinterpret_cast<CollectionDuplicateData *>(customdata);
+ CollectionDuplicateData *data = static_cast<CollectionDuplicateData *>(customdata);
TreeStoreElem *tselem = TREESTORE(te);
switch (tselem->type) {
@@ -594,10 +610,7 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op)
else if (parent != nullptr && (parent->flag & COLLECTION_IS_MASTER) != 0) {
BLI_assert(parent->id.flag & LIB_EMBEDDED_DATA);
- const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(&parent->id);
- BLI_assert(id_type->owner_get != nullptr);
-
- Scene *scene_owner = (Scene *)id_type->owner_get(bmain, &parent->id);
+ Scene *scene_owner = reinterpret_cast<Scene *>(BKE_id_owner_get(&parent->id));
BLI_assert(scene_owner != nullptr);
BLI_assert(GS(scene_owner->id.name) == ID_SCE);
@@ -695,13 +708,17 @@ static int collection_link_exec(bContext *C, wmOperator *op)
data.collections_to_edit = BLI_gset_ptr_new(__func__);
/* We first walk over and find the Collections we actually want to link (ignoring duplicates). */
- outliner_tree_traverse(
- space_outliner, &space_outliner->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data);
+ outliner_tree_traverse(space_outliner,
+ &space_outliner->tree,
+ 0,
+ TSE_SELECTED,
+ collection_collect_data_to_edit,
+ &data);
/* Effectively link the collections. */
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- Collection *collection = reinterpret_cast<Collection *>(
+ Collection *collection = static_cast<Collection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
BKE_collection_child_add(bmain, active_collection, collection);
id_fake_user_clear(&collection->id);
@@ -754,15 +771,19 @@ static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op))
/* We first walk over and find the Collections we actually want to instance
* (ignoring duplicates). */
- outliner_tree_traverse(
- space_outliner, &space_outliner->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data);
+ outliner_tree_traverse(space_outliner,
+ &space_outliner->tree,
+ 0,
+ TSE_SELECTED,
+ collection_collect_data_to_edit,
+ &data);
/* Find an active collection to add to, that doesn't give dependency cycles. */
LayerCollection *active_lc = BKE_layer_collection_get_active(view_layer);
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- Collection *collection = reinterpret_cast<Collection *>(
+ Collection *collection = static_cast<Collection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
while (BKE_collection_cycle_find(active_lc->collection, collection)) {
@@ -772,7 +793,7 @@ static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op))
/* Effectively instance the collections. */
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- Collection *collection = reinterpret_cast<Collection *>(
+ Collection *collection = static_cast<Collection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
Object *ob = ED_object_add_type(
C, OB_EMPTY, collection->id.name + 2, scene->cursor.location, nullptr, false, 0);
@@ -812,16 +833,16 @@ void OUTLINER_OT_collection_instance(wmOperatorType *ot)
/** \name Exclude Collection
* \{ */
-static TreeTraversalAction layer_collection_find_data_to_edit(TreeElement *te, void *customdata)
+static TreeTraversalAction layer_collection_collect_data_to_edit(TreeElement *te, void *customdata)
{
- CollectionEditData *data = reinterpret_cast<CollectionEditData *>(customdata);
+ CollectionEditData *data = static_cast<CollectionEditData *>(customdata);
TreeStoreElem *tselem = TREESTORE(te);
if (!(tselem && tselem->type == TSE_LAYER_COLLECTION)) {
return TRAVERSE_CONTINUE;
}
- LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata);
+ LayerCollection *lc = static_cast<LayerCollection *>(te->directdata);
if (lc->collection->flag & COLLECTION_IS_MASTER) {
/* skip - showing warning/error message might be misleading
@@ -857,12 +878,12 @@ static bool collections_view_layer_poll(bContext *C, bool clear, int flag)
&space_outliner->tree,
0,
TSE_SELECTED,
- layer_collection_find_data_to_edit,
+ layer_collection_collect_data_to_edit,
&data);
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- LayerCollection *lc = reinterpret_cast<LayerCollection *>(
+ LayerCollection *lc = static_cast<LayerCollection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
if (clear && (lc->flag & flag)) {
@@ -929,19 +950,19 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op)
&space_outliner->tree,
0,
TSE_SELECTED,
- layer_collection_find_data_to_edit,
+ layer_collection_collect_data_to_edit,
&data);
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- LayerCollection *lc = reinterpret_cast<LayerCollection *>(
+ LayerCollection *lc = static_cast<LayerCollection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
BKE_layer_collection_set_flag(lc, flag, !clear);
}
BLI_gset_free(data.collections_to_edit, nullptr);
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_relations_tag_update(bmain);
WM_main_add_notifier(NC_SCENE | ND_LAYER, nullptr);
@@ -1063,12 +1084,12 @@ static int collection_isolate_exec(bContext *C, wmOperator *op)
&space_outliner->tree,
0,
TSE_SELECTED,
- layer_collection_find_data_to_edit,
+ layer_collection_collect_data_to_edit,
&data);
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(
+ LayerCollection *layer_collection = static_cast<LayerCollection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
if (extend) {
@@ -1090,7 +1111,7 @@ static int collection_isolate_exec(bContext *C, wmOperator *op)
}
BLI_gset_free(data.collections_to_edit, nullptr);
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, nullptr);
@@ -1163,18 +1184,18 @@ static int collection_visibility_exec(bContext *C, wmOperator *op)
&space_outliner->tree,
0,
TSE_SELECTED,
- layer_collection_find_data_to_edit,
+ layer_collection_collect_data_to_edit,
&data);
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(
+ LayerCollection *layer_collection = static_cast<LayerCollection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
- BKE_layer_collection_set_visible(view_layer, layer_collection, show, is_inside);
+ BKE_layer_collection_set_visible(scene, view_layer, layer_collection, show, is_inside);
}
BLI_gset_free(data.collections_to_edit, nullptr);
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, nullptr);
@@ -1315,11 +1336,11 @@ static int collection_flag_exec(bContext *C, wmOperator *op)
&space_outliner->tree,
0,
TSE_SELECTED,
- layer_collection_find_data_to_edit,
+ layer_collection_collect_data_to_edit,
&data);
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(
+ LayerCollection *layer_collection = static_cast<LayerCollection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
Collection *collection = layer_collection->collection;
if (!BKE_id_is_editable(bmain, &collection->id)) {
@@ -1344,11 +1365,11 @@ static int collection_flag_exec(bContext *C, wmOperator *op)
&space_outliner->tree,
0,
TSE_SELECTED,
- collection_find_data_to_edit,
+ collection_collect_data_to_edit,
&data);
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- Collection *collection = reinterpret_cast<Collection *>(
+ Collection *collection = static_cast<Collection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
if (!BKE_id_is_editable(bmain, &collection->id)) {
continue;
@@ -1364,7 +1385,7 @@ static int collection_flag_exec(bContext *C, wmOperator *op)
BLI_gset_free(data.collections_to_edit, nullptr);
}
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
if (!is_render) {
@@ -1449,9 +1470,9 @@ struct OutlinerHideEditData {
/** \name Visibility for Collection & Object Operators
* \{ */
-static TreeTraversalAction outliner_hide_find_data_to_edit(TreeElement *te, void *customdata)
+static TreeTraversalAction outliner_hide_collect_data_to_edit(TreeElement *te, void *customdata)
{
- OutlinerHideEditData *data = reinterpret_cast<OutlinerHideEditData *>(customdata);
+ OutlinerHideEditData *data = static_cast<OutlinerHideEditData *>(customdata);
TreeStoreElem *tselem = TREESTORE(te);
if (tselem == nullptr) {
@@ -1459,7 +1480,7 @@ static TreeTraversalAction outliner_hide_find_data_to_edit(TreeElement *te, void
}
if (tselem->type == TSE_LAYER_COLLECTION) {
- LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata);
+ LayerCollection *lc = static_cast<LayerCollection *>(te->directdata);
if (lc->collection->flag & COLLECTION_IS_MASTER) {
/* Skip - showing warning/error message might be misleading
@@ -1473,6 +1494,7 @@ static TreeTraversalAction outliner_hide_find_data_to_edit(TreeElement *te, void
}
else if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
Object *ob = (Object *)tselem->id;
+ BKE_view_layer_synced_ensure(data->scene, data->view_layer);
Base *base = BKE_view_layer_base_find(data->view_layer, ob);
BLI_gset_add(data->bases_to_edit, base);
}
@@ -1496,25 +1518,25 @@ static int outliner_hide_exec(bContext *C, wmOperator *UNUSED(op))
&space_outliner->tree,
0,
TSE_SELECTED,
- outliner_hide_find_data_to_edit,
+ outliner_hide_collect_data_to_edit,
&data);
GSetIterator collections_to_edit_iter;
GSET_ITER (collections_to_edit_iter, data.collections_to_edit) {
- LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(
+ LayerCollection *layer_collection = static_cast<LayerCollection *>(
BLI_gsetIterator_getKey(&collections_to_edit_iter));
- BKE_layer_collection_set_visible(view_layer, layer_collection, false, false);
+ BKE_layer_collection_set_visible(scene, view_layer, layer_collection, false, false);
}
BLI_gset_free(data.collections_to_edit, nullptr);
GSetIterator bases_to_edit_iter;
GSET_ITER (bases_to_edit_iter, data.bases_to_edit) {
- Base *base = reinterpret_cast<Base *>(BLI_gsetIterator_getKey(&bases_to_edit_iter));
+ Base *base = static_cast<Base *>(BLI_gsetIterator_getKey(&bases_to_edit_iter));
base->flag |= BASE_HIDDEN;
}
BLI_gset_free(data.bases_to_edit, nullptr);
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, nullptr);
@@ -1542,18 +1564,18 @@ static int outliner_unhide_all_exec(bContext *C, wmOperator *UNUSED(op))
ViewLayer *view_layer = CTX_data_view_layer(C);
/* Unhide all the collections. */
- LayerCollection *lc_master = reinterpret_cast<LayerCollection *>(
- view_layer->layer_collections.first);
+ LayerCollection *lc_master = static_cast<LayerCollection *>(view_layer->layer_collections.first);
LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) {
BKE_layer_collection_set_flag(lc_iter, LAYER_COLLECTION_HIDE, false);
}
/* Unhide all objects. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->flag &= ~BASE_HIDDEN;
}
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, nullptr);
@@ -1593,7 +1615,7 @@ static int outliner_color_tag_set_exec(bContext *C, wmOperator *op)
&space_outliner->tree,
0,
TSE_SELECTED,
- outliner_find_selected_collections,
+ outliner_collect_selected_collections,
&selected);
LISTBASE_FOREACH (LinkData *, link, &selected.selected_array) {
@@ -1637,3 +1659,5 @@ void OUTLINER_OT_collection_color_tag_set(wmOperatorType *ot)
}
/** \} */
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_context.cc b/source/blender/editors/space_outliner/outliner_context.cc
index 1a804cb58b8..001bda57fa2 100644
--- a/source/blender/editors/space_outliner/outliner_context.cc
+++ b/source/blender/editors/space_outliner/outliner_context.cc
@@ -14,7 +14,7 @@
#include "outliner_intern.hh"
#include "tree/tree_iterator.hh"
-using namespace blender::ed::outliner;
+namespace blender::ed::outliner {
static void outliner_context_selected_ids_recursive(const SpaceOutliner &space_outliner,
bContextDataResult *result)
@@ -55,3 +55,5 @@ int /*eContextResult*/ outliner_context(const bContext *C,
return CTX_RESULT_MEMBER_NOT_FOUND;
}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc
index c72080be811..758928fed8e 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.cc
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc
@@ -45,6 +45,8 @@
#include "outliner_intern.hh"
+namespace blender::ed::outliner {
+
static Collection *collection_parent_from_ID(ID *id);
/* -------------------------------------------------------------------- */
@@ -144,7 +146,7 @@ static TreeElement *outliner_drop_insert_find(bContext *C,
return te_hovered;
}
*r_insert_type = TE_INSERT_BEFORE;
- return reinterpret_cast<TreeElement *>(te_hovered->subtree.first);
+ return static_cast<TreeElement *>(te_hovered->subtree.first);
}
*r_insert_type = TE_INSERT_AFTER;
return te_hovered;
@@ -159,8 +161,8 @@ static TreeElement *outliner_drop_insert_find(bContext *C,
/* Mouse doesn't hover any item (ignoring x-axis),
* so it's either above list bounds or below. */
- TreeElement *first = reinterpret_cast<TreeElement *>(space_outliner->tree.first);
- TreeElement *last = reinterpret_cast<TreeElement *>(space_outliner->tree.last);
+ TreeElement *first = static_cast<TreeElement *>(space_outliner->tree.first);
+ TreeElement *last = static_cast<TreeElement *>(space_outliner->tree.last);
if (view_mval[1] < last->ys) {
*r_insert_type = TE_INSERT_AFTER;
@@ -291,6 +293,7 @@ static bool parent_drop_allowed(TreeElement *te, Object *potential_child)
* active scene and parenting them is allowed (sergey) */
if (scene) {
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
if (BKE_view_layer_base_find(view_layer, potential_child)) {
return true;
}
@@ -422,12 +425,12 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_CANCELLED;
}
- ListBase *lb = reinterpret_cast<ListBase *>(event->customdata);
- wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first);
+ ListBase *lb = static_cast<ListBase *>(event->customdata);
+ wmDrag *drag = static_cast<wmDrag *>(lb->first);
parent_drop_set_parents(C,
op->reports,
- reinterpret_cast<wmDragID *>(drag->ids.first),
+ static_cast<wmDragID *>(drag->ids.first),
par,
PAR_OBJECT,
event->modifier & KM_ALT);
@@ -505,8 +508,8 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven
return OPERATOR_CANCELLED;
}
- ListBase *lb = reinterpret_cast<ListBase *>(event->customdata);
- wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first);
+ ListBase *lb = static_cast<ListBase *>(event->customdata);
+ wmDrag *drag = static_cast<wmDrag *>(lb->first);
LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) {
if (GS(drag_id->id->name) == ID_OB) {
@@ -578,6 +581,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
BKE_collection_object_add(bmain, collection, ob);
LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base) {
ED_object_base_select(base, BA_SELECT);
@@ -849,7 +853,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event)
ARegion *region = CTX_wm_region(C);
bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
- StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin);
+ StackDropData *drop_data = static_cast<StackDropData *>(drag->poin);
if (!drop_data) {
return false;
}
@@ -887,7 +891,7 @@ static char *datastack_drop_tooltip(bContext *UNUSED(C),
const int UNUSED(xy[2]),
struct wmDropBox *UNUSED(drop))
{
- StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin);
+ StackDropData *drop_data = static_cast<StackDropData *>(drag->poin);
switch (drop_data->drop_action) {
case DATA_STACK_DROP_REORDER:
return BLI_strdup(TIP_("Reorder"));
@@ -965,14 +969,13 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data)
case TSE_MODIFIER:
if (drop_data->ob_parent->type == OB_GPENCIL && ob_dst->type == OB_GPENCIL) {
ED_object_gpencil_modifier_copy_to_object(
- ob_dst, reinterpret_cast<GpencilModifierData *>(drop_data->drag_directdata));
+ ob_dst, static_cast<GpencilModifierData *>(drop_data->drag_directdata));
}
else if (drop_data->ob_parent->type != OB_GPENCIL && ob_dst->type != OB_GPENCIL) {
- ED_object_modifier_copy_to_object(
- C,
- ob_dst,
- drop_data->ob_parent,
- reinterpret_cast<ModifierData *>(drop_data->drag_directdata));
+ ED_object_modifier_copy_to_object(C,
+ ob_dst,
+ drop_data->ob_parent,
+ static_cast<ModifierData *>(drop_data->drag_directdata));
}
break;
case TSE_CONSTRAINT:
@@ -980,12 +983,12 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data)
ED_object_constraint_copy_for_pose(
bmain,
ob_dst,
- reinterpret_cast<bPoseChannel *>(drop_data->drop_te->directdata),
- reinterpret_cast<bConstraint *>(drop_data->drag_directdata));
+ static_cast<bPoseChannel *>(drop_data->drop_te->directdata),
+ static_cast<bConstraint *>(drop_data->drag_directdata));
}
else {
ED_object_constraint_copy_for_object(
- bmain, ob_dst, reinterpret_cast<bConstraint *>(drop_data->drag_directdata));
+ bmain, ob_dst, static_cast<bConstraint *>(drop_data->drag_directdata));
}
break;
case TSE_GPENCIL_EFFECT: {
@@ -993,8 +996,7 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data)
return;
}
- ED_object_shaderfx_copy(ob_dst,
- reinterpret_cast<ShaderFxData *>(drop_data->drag_directdata));
+ ED_object_shaderfx_copy(ob_dst, static_cast<ShaderFxData *>(drop_data->drag_directdata));
break;
}
}
@@ -1021,15 +1023,12 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa
index = outliner_get_insert_index(
drag_te, drop_te, insert_type, &ob->greasepencil_modifiers);
ED_object_gpencil_modifier_move_to_index(
- reports,
- ob,
- reinterpret_cast<GpencilModifierData *>(drop_data->drag_directdata),
- index);
+ reports, ob, static_cast<GpencilModifierData *>(drop_data->drag_directdata), index);
}
else {
index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->modifiers);
ED_object_modifier_move_to_index(
- reports, ob, reinterpret_cast<ModifierData *>(drop_data->drag_directdata), index);
+ reports, ob, static_cast<ModifierData *>(drop_data->drag_directdata), index);
}
break;
case TSE_CONSTRAINT:
@@ -1041,13 +1040,13 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa
index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->constraints);
}
ED_object_constraint_move_to_index(
- ob, reinterpret_cast<bConstraint *>(drop_data->drag_directdata), index);
+ ob, static_cast<bConstraint *>(drop_data->drag_directdata), index);
break;
case TSE_GPENCIL_EFFECT:
index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->shader_fx);
ED_object_shaderfx_move_to_index(
- reports, ob, reinterpret_cast<ShaderFxData *>(drop_data->drag_directdata), index);
+ reports, ob, static_cast<ShaderFxData *>(drop_data->drag_directdata), index);
}
}
@@ -1057,9 +1056,9 @@ static int datastack_drop_invoke(bContext *C, wmOperator *op, const wmEvent *eve
return OPERATOR_CANCELLED;
}
- ListBase *lb = reinterpret_cast<ListBase *>(event->customdata);
- wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first);
- StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin);
+ ListBase *lb = static_cast<ListBase *>(event->customdata);
+ wmDrag *drag = static_cast<wmDrag *>(lb->first);
+ StackDropData *drop_data = static_cast<StackDropData *>(drag->poin);
switch (drop_data->drop_action) {
case DATA_STACK_DROP_LINK:
@@ -1143,7 +1142,7 @@ static bool collection_drop_init(bContext *C, wmDrag *drag, const int xy[2], Col
return false;
}
- wmDragID *drag_id = reinterpret_cast<wmDragID *>(drag->ids.first);
+ wmDragID *drag_id = static_cast<wmDragID *>(drag->ids.first);
if (drag_id == nullptr) {
return false;
}
@@ -1300,8 +1299,8 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE
return OPERATOR_CANCELLED;
}
- ListBase *lb = reinterpret_cast<ListBase *>(event->customdata);
- wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first);
+ ListBase *lb = static_cast<ListBase *>(event->customdata);
+ wmDrag *drag = static_cast<wmDrag *>(lb->first);
CollectionDrop data;
if (!collection_drop_init(C, drag, event->xy, &data)) {
@@ -1455,7 +1454,7 @@ static int outliner_item_drag_drop_invoke(bContext *C,
TSE_GPENCIL_EFFECT_BASE);
const int wm_drag_type = use_datastack_drag ? WM_DRAG_DATASTACK : WM_DRAG_ID;
- wmDrag *drag = WM_event_start_drag(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP);
+ wmDrag *drag = WM_drag_data_create(C, data.icon, wm_drag_type, nullptr, 0.0, WM_DRAG_NOP);
if (use_datastack_drag) {
TreeElement *te_bone = nullptr;
@@ -1479,7 +1478,7 @@ static int outliner_item_drag_drop_invoke(bContext *C,
&space_outliner->tree,
0,
TSE_SELECTED,
- outliner_find_selected_objects,
+ outliner_collect_selected_objects,
&selected);
}
else {
@@ -1487,7 +1486,7 @@ static int outliner_item_drag_drop_invoke(bContext *C,
&space_outliner->tree,
0,
TSE_SELECTED,
- outliner_find_selected_collections,
+ outliner_collect_selected_collections,
&selected);
}
@@ -1545,6 +1544,8 @@ static int outliner_item_drag_drop_invoke(bContext *C,
WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent);
}
+ WM_event_start_prepared_drag(C, drag);
+
ED_outliner_select_sync_from_outliner(C, space_outliner);
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
@@ -1595,3 +1596,5 @@ void outliner_dropboxes(void)
}
/** \} */
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc
index 753de83a10d..3c52f79a80e 100644
--- a/source/blender/editors/space_outliner/outliner_draw.cc
+++ b/source/blender/editors/space_outliner/outliner_draw.cc
@@ -37,6 +37,7 @@
#include "BKE_lib_override.h"
#include "BKE_library.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_object.h"
@@ -73,8 +74,7 @@
#include "tree/tree_element_rna.hh"
#include "tree/tree_iterator.hh"
-using namespace blender;
-using namespace blender::ed::outliner;
+namespace blender::ed::outliner {
/* -------------------------------------------------------------------- */
/** \name Tree Size Functions
@@ -276,16 +276,17 @@ static void outliner_object_set_flag_recursive_fn(bContext *C,
Object *ob_parent = ob ? ob : base->object;
- for (Object *ob_iter = reinterpret_cast<Object *>(bmain->objects.first); ob_iter;
- ob_iter = reinterpret_cast<Object *>(ob_iter->id.next)) {
+ for (Object *ob_iter = static_cast<Object *>(bmain->objects.first); ob_iter;
+ ob_iter = static_cast<Object *>(ob_iter->id.next)) {
if (BKE_object_is_child_recursive(ob_parent, ob_iter)) {
if (ob) {
RNA_id_pointer_create(&ob_iter->id, &ptr);
DEG_id_tag_update(&ob_iter->id, ID_RECALC_COPY_ON_WRITE);
}
else {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base_iter = BKE_view_layer_base_find(view_layer, ob_iter);
- /* Child can be in a collection excluded from viewlayer. */
+ /* Child can be in a collection excluded from view-layer. */
if (base_iter == nullptr) {
continue;
}
@@ -301,7 +302,7 @@ static void outliner_object_set_flag_recursive_fn(bContext *C,
DEG_relations_tag_update(bmain);
}
else {
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
}
}
@@ -311,8 +312,8 @@ static void outliner_object_set_flag_recursive_fn(bContext *C,
*/
static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void *poin2)
{
- Object *ob = reinterpret_cast<Object *>(poin);
- char *propname = reinterpret_cast<char *>(poin2);
+ Object *ob = static_cast<Object *>(poin);
+ char *propname = static_cast<char *>(poin2);
outliner_object_set_flag_recursive_fn(C, nullptr, ob, propname);
}
@@ -321,8 +322,8 @@ static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void
*/
static void outliner__base_set_flag_recursive_fn(bContext *C, void *poin, void *poin2)
{
- Base *base = reinterpret_cast<Base *>(poin);
- char *propname = reinterpret_cast<char *>(poin2);
+ Base *base = static_cast<Base *>(poin);
+ char *propname = static_cast<char *>(poin2);
outliner_object_set_flag_recursive_fn(C, base, nullptr, propname);
}
@@ -348,6 +349,7 @@ static void outliner_base_or_object_pointer_create(
RNA_id_pointer_create(&ob->id, ptr);
}
else {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
RNA_pointer_create(&scene->id, &RNA_ObjectBase, base, ptr);
}
@@ -487,7 +489,7 @@ void outliner_collection_isolate_flag(Scene *scene,
const bool is_hide = strstr(propname, "hide_") != nullptr;
LayerCollection *top_layer_collection = layer_collection ?
- reinterpret_cast<LayerCollection *>(
+ static_cast<LayerCollection *>(
view_layer->layer_collections.first) :
nullptr;
Collection *top_collection = collection ? scene->master_collection : nullptr;
@@ -558,7 +560,7 @@ void outliner_collection_isolate_flag(Scene *scene,
else {
CollectionParent *parent;
Collection *child = collection;
- while ((parent = reinterpret_cast<CollectionParent *>(child->parents.first))) {
+ while ((parent = static_cast<CollectionParent *>(child->parents.first))) {
if (parent->collection->flag & COLLECTION_IS_MASTER) {
break;
}
@@ -637,8 +639,8 @@ static void view_layer__layer_collection_set_flag_recursive_fn(bContext *C,
void *poin,
void *poin2)
{
- LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(poin);
- char *propname = reinterpret_cast<char *>(poin2);
+ LayerCollection *layer_collection = static_cast<LayerCollection *>(poin);
+ char *propname = static_cast<char *>(poin2);
outliner_collection_set_flag_recursive_fn(C, layer_collection, nullptr, propname);
}
@@ -648,8 +650,8 @@ static void view_layer__layer_collection_set_flag_recursive_fn(bContext *C,
*/
static void view_layer__collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2)
{
- LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(poin);
- char *propname = reinterpret_cast<char *>(poin2);
+ LayerCollection *layer_collection = static_cast<LayerCollection *>(poin);
+ char *propname = static_cast<char *>(poin2);
outliner_collection_set_flag_recursive_fn(
C, layer_collection, layer_collection->collection, propname);
}
@@ -660,8 +662,8 @@ static void view_layer__collection_set_flag_recursive_fn(bContext *C, void *poin
*/
static void scenes__collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2)
{
- Collection *collection = reinterpret_cast<Collection *>(poin);
- char *propname = reinterpret_cast<char *>(poin2);
+ Collection *collection = static_cast<Collection *>(poin);
+ char *propname = static_cast<char *>(poin2);
outliner_collection_set_flag_recursive_fn(C, nullptr, collection, propname);
}
@@ -671,12 +673,13 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
BLI_mempool *ts = space_outliner->treestore;
- TreeStoreElem *tselem = reinterpret_cast<TreeStoreElem *>(tsep);
+ TreeStoreElem *tselem = static_cast<TreeStoreElem *>(tsep);
if (ts && tselem) {
TreeElement *te = outliner_find_tree_element(&space_outliner->tree, tselem);
if (tselem->type == TSE_SOME_ID) {
+ BKE_main_namemap_remove_name(bmain, tselem->id, oldname);
BLI_libblock_ensure_unique_name(bmain, tselem->id->name);
WM_msg_publish_rna_prop(mbus, tselem->id, tselem->id, ID, name);
@@ -699,7 +702,6 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
if (ob->type == OB_MBALL) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
- DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
break;
}
default:
@@ -730,26 +732,31 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
lib->id.tag &= ~LIB_TAG_MISSING;
}
}
+
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
}
else {
switch (tselem->type) {
case TSE_DEFGROUP: {
Object *ob = (Object *)tselem->id;
- bDeformGroup *vg = reinterpret_cast<bDeformGroup *>(te->directdata);
+ bDeformGroup *vg = static_cast<bDeformGroup *>(te->directdata);
BKE_object_defgroup_unique_name(vg, ob);
WM_msg_publish_rna_prop(mbus, &ob->id, vg, VertexGroup, name);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
break;
}
case TSE_NLA_ACTION: {
bAction *act = (bAction *)tselem->id;
+ BKE_main_namemap_remove_name(bmain, &act->id, oldname);
BLI_libblock_ensure_unique_name(bmain, act->id.name);
WM_msg_publish_rna_prop(mbus, &act->id, &act->id, ID, name);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
break;
}
case TSE_EBONE: {
bArmature *arm = (bArmature *)tselem->id;
if (arm->edbo) {
- EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata);
+ EditBone *ebone = static_cast<EditBone *>(te->directdata);
char newname[sizeof(ebone->name)];
/* restore bone name */
@@ -758,6 +765,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
ED_armature_bone_rename(bmain, arm, oldname, newname);
WM_msg_publish_rna_prop(mbus, &arm->id, ebone, EditBone, name);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
}
break;
}
@@ -767,7 +775,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
outliner_viewcontext_init(C, &tvc);
bArmature *arm = (bArmature *)tselem->id;
- Bone *bone = reinterpret_cast<Bone *>(te->directdata);
+ Bone *bone = static_cast<Bone *>(te->directdata);
char newname[sizeof(bone->name)];
/* always make current object active */
@@ -779,6 +787,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
ED_armature_bone_rename(bmain, arm, oldname, newname);
WM_msg_publish_rna_prop(mbus, &arm->id, bone, Bone, name);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
break;
}
case TSE_POSE_CHANNEL: {
@@ -787,7 +796,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
Object *ob = (Object *)tselem->id;
bArmature *arm = (bArmature *)ob->data;
- bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata);
+ bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata);
char newname[sizeof(pchan->name)];
/* always make current pose-bone active */
@@ -798,15 +807,16 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
/* restore bone name */
BLI_strncpy(newname, pchan->name, sizeof(pchan->name));
BLI_strncpy(pchan->name, oldname, sizeof(pchan->name));
- ED_armature_bone_rename(
- bmain, reinterpret_cast<bArmature *>(ob->data), oldname, newname);
+ ED_armature_bone_rename(bmain, static_cast<bArmature *>(ob->data), oldname, newname);
WM_msg_publish_rna_prop(mbus, &arm->id, pchan->bone, Bone, name);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
break;
}
case TSE_POSEGRP: {
Object *ob = (Object *)tselem->id; /* id = object. */
- bActionGroup *grp = reinterpret_cast<bActionGroup *>(te->directdata);
+ bActionGroup *grp = static_cast<bActionGroup *>(te->directdata);
BLI_uniquename(&ob->pose->agroups,
grp,
@@ -816,11 +826,12 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
sizeof(grp->name));
WM_msg_publish_rna_prop(mbus, &ob->id, grp, ActionGroup, name);
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
break;
}
case TSE_GP_LAYER: {
bGPdata *gpd = (bGPdata *)tselem->id; /* id = GP Datablock */
- bGPDlayer *gpl = reinterpret_cast<bGPDlayer *>(te->directdata);
+ bGPDlayer *gpl = static_cast<bGPDlayer *>(te->directdata);
/* always make layer active */
BKE_gpencil_layer_active_set(gpd, gpl);
@@ -832,11 +843,12 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
WM_msg_publish_rna_prop(mbus, &gpd->id, gpl, GPencilLayer, info);
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
break;
}
case TSE_R_LAYER: {
Scene *scene = (Scene *)tselem->id;
- ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata);
+ ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata);
/* Restore old name. */
char newname[sizeof(view_layer->name)];
@@ -847,14 +859,17 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname)
BKE_view_layer_rename(bmain, scene, view_layer, newname);
WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, ViewLayer, name);
WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
break;
}
case TSE_LAYER_COLLECTION: {
/* The ID is a #Collection, not a #LayerCollection */
Collection *collection = (Collection *)tselem->id;
+ BKE_main_namemap_remove_name(bmain, &collection->id, oldname);
BLI_libblock_ensure_unique_name(bmain, collection->id.name);
WM_msg_publish_rna_prop(mbus, &collection->id, &collection->id, ID, name);
WM_event_add_notifier(C, NC_ID | NA_RENAME, nullptr);
+ DEG_id_tag_update(tselem->id, ID_RECALC_COPY_ON_WRITE);
break;
}
}
@@ -987,7 +1002,7 @@ static bool outliner_restrict_properties_collection_set(Scene *scene,
{
TreeStoreElem *tselem = TREESTORE(te);
LayerCollection *layer_collection = (tselem->type == TSE_LAYER_COLLECTION) ?
- reinterpret_cast<LayerCollection *>(te->directdata) :
+ static_cast<LayerCollection *>(te->directdata) :
nullptr;
Collection *collection = outliner_collection_from_tree_element(te);
@@ -1101,7 +1116,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) {
if (space_outliner->show_restrict_flags & SO_RESTRICT_RENDER) {
/* View layer render toggle. */
- ViewLayer *layer = reinterpret_cast<ViewLayer *>(te->directdata);
+ ViewLayer *layer = static_cast<ViewLayer *>(te->directdata);
bt = uiDefIconButBitS(block,
UI_BTYPE_ICON_TOGGLE_N,
@@ -1133,6 +1148,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
RNA_id_pointer_create(&ob->id, &ptr);
if (space_outliner->show_restrict_flags & SO_RESTRICT_HIDE) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = (te->directdata) ? (Base *)te->directdata :
BKE_view_layer_base_find(view_layer, ob);
if (base) {
@@ -1325,7 +1341,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
bPoseChannel *pchan = (bPoseChannel *)te->directdata;
Bone *bone = pchan->bone;
Object *ob = (Object *)tselem->id;
- bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
+ bArmature *arm = static_cast<bArmature *>(ob->data);
RNA_pointer_create(&arm->id, &RNA_Bone, bone, &ptr);
@@ -1475,8 +1491,7 @@ static void outliner_draw_restrictbuts(uiBlock *block,
scene, te, &collection_ptr, &layer_collection_ptr, &props, &props_active)) {
LayerCollection *layer_collection = (tselem->type == TSE_LAYER_COLLECTION) ?
- reinterpret_cast<LayerCollection *>(
- te->directdata) :
+ static_cast<LayerCollection *>(te->directdata) :
nullptr;
Collection *collection = outliner_collection_from_tree_element(te);
@@ -1802,18 +1817,17 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block,
if (!outliner_is_element_in_view(te, &region->v2d)) {
continue;
}
- if (tselem->type != TSE_LIBRARY_OVERRIDE) {
+ TreeElementOverridesProperty *override_elem = tree_element_cast<TreeElementOverridesProperty>(
+ te);
+ if (!override_elem) {
continue;
}
- TreeElementOverridesProperty &override_elem = *tree_element_cast<TreeElementOverridesProperty>(
- te);
-
- if (!override_elem.is_rna_path_valid) {
+ if (!override_elem->is_rna_path_valid) {
uiBut *but = uiDefBut(block,
UI_BTYPE_LABEL,
0,
- override_elem.rna_path.c_str(),
+ override_elem->rna_path.c_str(),
x + pad_x,
te->ys + pad_y,
item_max_width,
@@ -1828,8 +1842,28 @@ static void outliner_draw_overrides_rna_buts(uiBlock *block,
continue;
}
- PointerRNA *ptr = &override_elem.override_rna_ptr;
- PropertyRNA *prop = &override_elem.override_rna_prop;
+ if (const TreeElementOverridesPropertyOperation *override_op_elem =
+ tree_element_cast<TreeElementOverridesPropertyOperation>(te)) {
+ StringRefNull op_label = override_op_elem->getOverrideOperationLabel();
+ uiDefBut(block,
+ UI_BTYPE_LABEL,
+ 0,
+ op_label.c_str(),
+ x + pad_x,
+ te->ys + pad_y,
+ item_max_width,
+ item_height,
+ nullptr,
+ 0,
+ 0,
+ 0,
+ 0,
+ "");
+ continue;
+ }
+
+ PointerRNA *ptr = &override_elem->override_rna_ptr;
+ PropertyRNA *prop = &override_elem->override_rna_prop;
const PropertyType prop_type = RNA_property_type(prop);
uiBut *auto_but = uiDefAutoButR(block,
@@ -1927,7 +1961,7 @@ static void outliner_draw_separator(ARegion *region, const int x)
GPU_line_width(1.0f);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
immBegin(GPU_PRIM_LINES, 2);
@@ -2495,7 +2529,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
data.drag_id = tselem->id;
break;
case TSE_CONSTRAINT: {
- bConstraint *con = reinterpret_cast<bConstraint *>(te->directdata);
+ bConstraint *con = static_cast<bConstraint *>(te->directdata);
data.drag_id = tselem->id;
switch ((eBConstraint_Types)con->type) {
case CONSTRAINT_TYPE_CAMERASOLVER:
@@ -2612,9 +2646,8 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
data.drag_id = tselem->id;
if (ob->type != OB_GPENCIL) {
- ModifierData *md = reinterpret_cast<ModifierData *>(
- BLI_findlink(&ob->modifiers, tselem->nr));
- const ModifierTypeInfo *modifier_type = reinterpret_cast<const ModifierTypeInfo *>(
+ ModifierData *md = static_cast<ModifierData *>(BLI_findlink(&ob->modifiers, tselem->nr));
+ const ModifierTypeInfo *modifier_type = static_cast<const ModifierTypeInfo *>(
BKE_modifier_get_info((ModifierType)md->type));
if (modifier_type != nullptr) {
data.icon = modifier_type->icon;
@@ -2625,7 +2658,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
}
else {
/* grease pencil modifiers */
- GpencilModifierData *md = reinterpret_cast<GpencilModifierData *>(
+ GpencilModifierData *md = static_cast<GpencilModifierData *>(
BLI_findlink(&ob->greasepencil_modifiers, tselem->nr));
switch ((GpencilModifierType)md->type) {
case eGpencilModifierType_Noise:
@@ -2784,7 +2817,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
const PointerRNA &ptr = te_rna_struct->getPointerRNA();
if (RNA_struct_is_ID(ptr.type)) {
- data.drag_id = reinterpret_cast<ID *>(ptr.data);
+ data.drag_id = static_cast<ID *>(ptr.data);
data.icon = RNA_struct_ui_icon(ptr.type);
}
else {
@@ -2824,10 +2857,20 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
data.icon = tree_element_get_icon_from_id(tselem->id);
}
+ if (!te->abstract_element) {
+ /* Pass */
+ }
+ else if (auto icon = te->abstract_element->getIcon()) {
+ data.icon = *icon;
+ }
+
return data;
}
-static void tselem_draw_icon(uiBlock *block,
+/**
+ * \return true if the element has an icon that was drawn, false if it doesn't have an icon.
+ */
+static bool tselem_draw_icon(uiBlock *block,
int xmax,
float x,
float y,
@@ -2838,7 +2881,7 @@ static void tselem_draw_icon(uiBlock *block,
{
TreeElementIcon data = tree_element_get_icon(tselem, te);
if (data.icon == 0) {
- return;
+ return false;
}
const bool is_collection = outliner_is_collection_tree_element(te);
@@ -2862,7 +2905,7 @@ static void tselem_draw_icon(uiBlock *block,
0.0f,
btheme->collection_color[collection->color_tag].color,
true);
- return;
+ return true;
}
}
@@ -2894,6 +2937,8 @@ static void tselem_draw_icon(uiBlock *block,
alpha,
(data.drag_id && ID_IS_LINKED(data.drag_id)) ? data.drag_id->lib->filepath : "");
}
+
+ return true;
}
/**
@@ -3088,6 +3133,7 @@ static void outliner_draw_iconrow(bContext *C,
TSE_GP_LAYER,
TSE_LIBRARY_OVERRIDE_BASE,
TSE_LIBRARY_OVERRIDE,
+ TSE_LIBRARY_OVERRIDE_OPERATION,
TSE_BONE,
TSE_EBONE,
TSE_POSE_CHANNEL,
@@ -3170,10 +3216,12 @@ static bool element_should_draw_faded(const TreeViewContext *tvc,
case ID_OB: {
const Object *ob = (const Object *)tselem->id;
/* Lookup in view layer is logically const as it only checks a cache. */
+ BKE_view_layer_synced_ensure(tvc->scene, tvc->view_layer);
const Base *base = (te->directdata) ? (const Base *)te->directdata :
BKE_view_layer_base_find(
(ViewLayer *)tvc->view_layer, (Object *)ob);
- const bool is_visible = (base != nullptr) && (base->flag & BASE_VISIBLE_VIEWLAYER);
+ const bool is_visible = (base != nullptr) &&
+ (base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT);
if (!is_visible) {
return true;
}
@@ -3237,6 +3285,7 @@ static void outliner_draw_tree_element(bContext *C,
if (tselem->type == TSE_SOME_ID) {
if (te->idcode == ID_OB) {
Object *ob = (Object *)tselem->id;
+ BKE_view_layer_synced_ensure(tvc->scene, tvc->view_layer);
Base *base = (te->directdata) ? (Base *)te->directdata :
BKE_view_layer_base_find(tvc->view_layer, ob);
const bool is_selected = (base != nullptr) && ((base->flag & BASE_SELECTED) != 0);
@@ -3292,7 +3341,7 @@ static void outliner_draw_tree_element(bContext *C,
/* Scene collection in view layer can't expand/collapse. */
}
else if (te->subtree.first || ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_SCE)) ||
- (te->flag & TE_LAZY_CLOSED)) {
+ (te->flag & TE_PRETEND_HAS_CHILDREN)) {
/* Open/close icon, only when sub-levels, except for scene. */
int icon_x = startx;
@@ -3313,15 +3362,15 @@ static void outliner_draw_tree_element(bContext *C,
offsx += UI_UNIT_X;
/* Data-type icon. */
- if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE))) {
- tselem_draw_icon(block,
- xmax,
- (float)startx + offsx,
- (float)*starty,
- tselem,
- te,
- (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac,
- true);
+ if (!(ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE)) &&
+ tselem_draw_icon(block,
+ xmax,
+ (float)startx + offsx,
+ (float)*starty,
+ tselem,
+ te,
+ (tselem->flag & TSE_HIGHLIGHTED_ICON) ? alpha_fac + 0.5f : alpha_fac,
+ true)) {
offsx += UI_UNIT_X + 4 * ufac;
}
else {
@@ -3508,7 +3557,7 @@ static void outliner_draw_hierarchy_lines(SpaceOutliner *space_outliner,
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uchar col[4];
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -3539,7 +3588,7 @@ static void outliner_draw_struct_marks(ARegion *region,
if (tselem->type == TSE_RNA_STRUCT) {
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immThemeColorShadeAlpha(TH_BACK, -15, -200);
immRecti(pos, 0, *starty + 1, (int)region->v2d.cur.xmax, *starty + UI_UNIT_Y - 1);
immUnbindProgram();
@@ -3552,7 +3601,7 @@ static void outliner_draw_struct_marks(ARegion *region,
if (tselem->type == TSE_RNA_STRUCT) {
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immThemeColorShadeAlpha(TH_BACK, -15, -200);
immBegin(GPU_PRIM_LINES, 2);
@@ -3657,7 +3706,7 @@ static void outliner_draw_highlights(ARegion *region,
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
outliner_draw_highlights(pos,
region,
space_outliner,
@@ -3758,7 +3807,7 @@ static void outliner_back(ARegion *region)
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
float col_alternating[4];
UI_GetThemeColor4fv(TH_ROW_ALTERNATE, col_alternating);
@@ -3810,7 +3859,7 @@ static void outliner_update_viewable_area(ARegion *region,
int sizex = outliner_width(space_outliner, tree_width, right_column_width);
int sizey = tree_height;
- /* Extend size to allow for horizontal scrollbar and extra offset. */
+ /* Extend size to allow for horizontal scroll-bar and extra offset. */
sizey += V2D_SCROLL_HEIGHT + OL_Y_OFFSET;
UI_view2d_totRect_set(&region->v2d, sizex, sizey);
@@ -3950,3 +3999,5 @@ void draw_outliner(const bContext *C)
}
/** \} */
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc
index 32860bc2cff..be3c1547579 100644
--- a/source/blender/editors/space_outliner/outliner_edit.cc
+++ b/source/blender/editors/space_outliner/outliner_edit.cc
@@ -29,6 +29,7 @@
#include "BKE_blender_copybuffer.h"
#include "BKE_context.h"
#include "BKE_idtype.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_lib_query.h"
@@ -55,6 +56,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "GPU_material.h"
@@ -64,6 +66,8 @@
using namespace blender::ed::outliner;
+namespace blender::ed::outliner {
+
static void outliner_show_active(SpaceOutliner *space_outliner,
ARegion *region,
TreeElement *te,
@@ -143,14 +147,10 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot)
/** \name Toggle Open/Closed Operator
* \{ */
-void outliner_item_openclose(SpaceOutliner *space_outliner,
- TreeElement *te,
- bool open,
- bool toggle_all)
+void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all)
{
- /* Prevent opening leaf elements in the tree unless in the Data API display mode because in that
- * mode subtrees are empty unless expanded. */
- if (space_outliner->outlinevis != SO_DATA_API && BLI_listbase_is_empty(&te->subtree)) {
+ /* Only allow opening elements with children. */
+ if (!(te->flag & TE_PRETEND_HAS_CHILDREN) && BLI_listbase_is_empty(&te->subtree)) {
return;
}
@@ -197,7 +197,7 @@ static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEv
/* Only toggle openclose on the same level as the first clicked element */
if (te->xs == data->x_location) {
- outliner_item_openclose(space_outliner, te, data->open, false);
+ outliner_item_openclose(te, data->open, false);
outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region);
}
@@ -241,7 +241,7 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE
const bool open = (tselem->flag & TSE_CLOSED) ||
(toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1)));
- outliner_item_openclose(space_outliner, te, open, toggle_all);
+ outliner_item_openclose(te, open, toggle_all);
outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region);
/* Only toggle once for single click toggling */
@@ -597,9 +597,9 @@ static int outliner_id_remap_exec(bContext *C, wmOperator *op)
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
const short id_type = (short)RNA_enum_get(op->ptr, "id_type");
- ID *old_id = reinterpret_cast<ID *>(
+ ID *old_id = static_cast<ID *>(
BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id")));
- ID *new_id = reinterpret_cast<ID *>(
+ ID *new_id = static_cast<ID *>(
BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id")));
/* check for invalid states */
@@ -693,9 +693,9 @@ static const EnumPropertyItem *outliner_id_itemf(bContext *C,
int i = 0;
short id_type = (short)RNA_enum_get(ptr, "id_type");
- ID *id = reinterpret_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first);
+ ID *id = static_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first);
- for (; id; id = reinterpret_cast<ID *>(id->next)) {
+ for (; id; id = static_cast<ID *>(id->next)) {
item_tmp.identifier = item_tmp.name = id->name + 2;
item_tmp.value = i++;
RNA_enum_item_add(&item, &totitem, &item_tmp);
@@ -1262,11 +1262,13 @@ static int outliner_open_back(TreeElement *te)
/* Return element representing the active base or bone in the outliner, or NULL if none exists */
static TreeElement *outliner_show_active_get_element(bContext *C,
SpaceOutliner *space_outliner,
+ const Scene *scene,
ViewLayer *view_layer)
{
TreeElement *te;
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (!obact) {
return nullptr;
@@ -1317,11 +1319,13 @@ static void outliner_show_active(SpaceOutliner *space_outliner,
static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
- TreeElement *active_element = outliner_show_active_get_element(C, space_outliner, view_layer);
+ TreeElement *active_element = outliner_show_active_get_element(
+ C, space_outliner, scene, view_layer);
if (active_element) {
ID *id = TREESTORE(active_element)->id;
@@ -1409,129 +1413,6 @@ void OUTLINER_OT_scroll_page(wmOperatorType *ot)
/** \} */
-#if 0 /* TODO: probably obsolete now with filtering? */
-
-/* -------------------------------------------------------------------- */
-/** \name Search
- * \{ */
-
-
-/* find next element that has this name */
-static TreeElement *outliner_find_name(
- SpaceOutliner *space_outliner, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound)
-{
- TreeElement *te, *tes;
-
- for (te = lb->first; te; te = te->next) {
- int found = outliner_filter_has_name(te, name, flags);
-
- if (found) {
- /* name is right, but is element the previous one? */
- if (prev) {
- if ((te != prev) && (*prevFound)) {
- return te;
- }
- if (te == prev) {
- *prevFound = 1;
- }
- }
- else {
- return te;
- }
- }
-
- tes = outliner_find_name(space_outliner, &te->subtree, name, flags, prev, prevFound);
- if (tes) {
- return tes;
- }
- }
-
- /* nothing valid found */
- return nullptr;
-}
-
-static void outliner_find_panel(
- Scene *UNUSED(scene), ARegion *region, SpaceOutliner *space_outliner, int again, int flags)
-{
- ReportList *reports = nullptr; /* CTX_wm_reports(C); */
- TreeElement *te = nullptr;
- TreeElement *last_find;
- TreeStoreElem *tselem;
- int ytop, xdelta, prevFound = 0;
- char name[sizeof(space_outliner->search_string)];
-
- /* get last found tree-element based on stored search_tse */
- last_find = outliner_find_tse(space_outliner, &space_outliner->search_tse);
-
- /* determine which type of search to do */
- if (again && last_find) {
- /* no popup panel - previous + user wanted to search for next after previous */
- BLI_strncpy(name, space_outliner->search_string, sizeof(name));
- flags = space_outliner->search_flags;
-
- /* try to find matching element */
- te = outliner_find_name(space_outliner, &space_outliner->tree, name, flags, last_find, &prevFound);
- if (te == nullptr) {
- /* no more matches after previous, start from beginning again */
- prevFound = 1;
- te = outliner_find_name(space_outliner, &space_outliner->tree, name, flags, last_find, &prevFound);
- }
- }
- else {
- /* pop up panel - no previous, or user didn't want search after previous */
- name[0] = '\0';
- // XXX if (sbutton(name, 0, sizeof(name) - 1, "Find: ") && name[0]) {
- // te = outliner_find_name(space_outliner, &space_outliner->tree, name, flags, nullptr, &prevFound);
- // }
- // else return; XXX RETURN! XXX
- }
-
- /* do selection and reveal */
- if (te) {
- tselem = TREESTORE(te);
- if (tselem) {
- /* expand branches so that it will be visible, we need to get correct coordinates */
- if (outliner_open_back(space_outliner, te)) {
- outliner_set_coordinates(region, space_outliner);
- }
-
- /* deselect all visible, and select found element */
- outliner_flag_set(space_outliner, &space_outliner->tree, TSE_SELECTED, 0);
- tselem->flag |= TSE_SELECTED;
-
- /* Make `te->ys` center of view. */
- ytop = (int)(te->ys + BLI_rctf_size_y(&region->v2d.mask) / 2);
- if (ytop > 0) {
- ytop = 0;
- }
- region->v2d.cur.ymax = (float)ytop;
- region->v2d.cur.ymin = (float)(ytop - BLI_rctf_size_y(&region->v2d.mask));
-
- /* Make `te->xs` ==> `te->xend` center of view. */
- xdelta = (int)(te->xs - region->v2d.cur.xmin);
- region->v2d.cur.xmin += xdelta;
- region->v2d.cur.xmax += xdelta;
-
- /* store selection */
- space_outliner->search_tse = *tselem;
-
- BLI_strncpy(space_outliner->search_string, name, sizeof(space_outliner->search_string));
- space_outliner->search_flags = flags;
-
- /* redraw */
- ED_region_tag_redraw_no_rebuild(region);
- }
- }
- else {
- /* no tree-element found */
- BKE_reportf(reports, RPT_WARNING, "Not found: %s", name);
- }
-}
-
-/** \} */
-
-#endif /* if 0 */
-
/* -------------------------------------------------------------------- */
/** \name Show One Level Operator
* \{ */
@@ -1817,7 +1698,7 @@ static void tree_element_to_path(TreeElement *te,
/* ptr->data not ptr->owner_id seems to be the one we want,
* since ptr->data is sometimes the owner of this ID? */
if (RNA_struct_is_ID(ptr.type)) {
- *id = reinterpret_cast<ID *>(ptr.data);
+ *id = static_cast<ID *>(ptr.data);
/* clear path */
if (*path) {
@@ -2052,8 +1933,7 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add)
/* try to find one from scene */
if (scene->active_keyingset > 0) {
- ks = reinterpret_cast<KeyingSet *>(
- BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1));
+ ks = static_cast<KeyingSet *>(BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1));
}
/* Add if none found */
@@ -2357,3 +2237,5 @@ void OUTLINER_OT_orphans_purge(wmOperatorType *ot)
}
/** \} */
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh
index 18173b37123..ad5d653949c 100644
--- a/source/blender/editors/space_outliner/outliner_intern.hh
+++ b/source/blender/editors/space_outliner/outliner_intern.hh
@@ -14,10 +14,6 @@
/* Needed for `tree_element_cast()`. */
#include "tree/tree_element.hh"
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/* internal exports only */
struct ARegion;
@@ -27,7 +23,6 @@ struct ListBase;
struct Main;
struct Object;
struct Scene;
-struct TreeElement;
struct TreeStoreElem;
struct ViewLayer;
struct bContext;
@@ -37,47 +32,52 @@ struct View2D;
struct wmKeyConfig;
struct wmOperatorType;
+namespace blender::bke::outliner::treehash {
+class TreeHash;
+}
+
namespace blender::ed::outliner {
+
class AbstractTreeDisplay;
class AbstractTreeElement;
-} // namespace blender::ed::outliner
-namespace outliner = blender::ed::outliner;
+namespace treehash = blender::bke::outliner::treehash;
+
+struct TreeElement;
struct SpaceOutliner_Runtime {
/** Object to create and manage the tree for a specific display type (View Layers, Scenes,
* Blender File, etc.). */
- std::unique_ptr<outliner::AbstractTreeDisplay> tree_display;
+ std::unique_ptr<AbstractTreeDisplay> tree_display;
- /** Pointers to tree-store elements, grouped by `(id, type, nr)`
- * in hash-table for faster searching. */
- struct GHash *treehash;
+ /* Hash table for tree-store elements, using `(id, type, index)` as key. */
+ std::unique_ptr<treehash::TreeHash> tree_hash;
SpaceOutliner_Runtime() = default;
/** Used for copying runtime data to a duplicated space. */
SpaceOutliner_Runtime(const SpaceOutliner_Runtime &);
- ~SpaceOutliner_Runtime();
+ ~SpaceOutliner_Runtime() = default;
};
-typedef enum TreeElementInsertType {
+enum TreeElementInsertType {
TE_INSERT_BEFORE,
TE_INSERT_AFTER,
TE_INSERT_INTO,
-} TreeElementInsertType;
+};
-typedef enum TreeTraversalAction {
+enum TreeTraversalAction {
/** Continue traversal regularly, don't skip children. */
TRAVERSE_CONTINUE = 0,
/** Stop traversal. */
TRAVERSE_BREAK,
/** Continue traversal, but skip children of traversed element. */
TRAVERSE_SKIP_CHILDS,
-} TreeTraversalAction;
+};
-typedef TreeTraversalAction (*TreeTraversalFunc)(struct TreeElement *te, void *customdata);
+typedef TreeTraversalAction (*TreeTraversalFunc)(TreeElement *te, void *customdata);
-typedef struct TreeElement {
- struct TreeElement *next, *prev, *parent;
+struct TreeElement {
+ TreeElement *next, *prev, *parent;
/**
* The new inheritance based representation of the element (a derived type of base
@@ -85,7 +85,7 @@ typedef struct TreeElement {
* be moved to it and operations based on the type should become virtual methods of the class
* hierarchy.
*/
- std::unique_ptr<outliner::AbstractTreeElement> abstract_element;
+ std::unique_ptr<AbstractTreeElement> abstract_element;
ListBase subtree;
int xs, ys; /* Do selection. */
@@ -96,12 +96,12 @@ typedef struct TreeElement {
short xend; /* Width of item display, for select. */
const char *name;
void *directdata; /* Armature Bones, Base, ... */
-} TreeElement;
+};
-typedef struct TreeElementIcon {
+struct TreeElementIcon {
struct ID *drag_id, *drag_parent;
int icon;
-} TreeElementIcon;
+};
#define TREESTORE_ID_TYPE(_id) \
(ELEM(GS((_id)->name), \
@@ -153,7 +153,10 @@ enum {
/* Closed items display their children as icon within the row. TE_ICONROW is for
* these child-items that are visible but only within the row of the closed parent. */
TE_ICONROW = (1 << 1),
- TE_LAZY_CLOSED = (1 << 2),
+ /** Treat the element as if it had children, e.g. draw an icon to un-collapse it, even if it
+ * doesn't. Used where children are lazy-built only if the parent isn't collapsed (see
+ * #AbstractTreeDisplay::is_lazy_built()). */
+ TE_PRETEND_HAS_CHILDREN = (1 << 2),
TE_FREE_NAME = (1 << 3),
TE_DRAGGING = (1 << 4),
TE_CHILD_NOT_IN_COLLECTION = (1 << 6),
@@ -165,17 +168,17 @@ enum {
/* button events */
#define OL_NAMEBUTTON 1
-typedef enum {
+enum eOLDrawState {
OL_DRAWSEL_NONE = 0, /* inactive (regular black text) */
OL_DRAWSEL_NORMAL = 1, /* active object (draws white text) */
OL_DRAWSEL_ACTIVE = 2, /* active obdata (draws a circle around the icon) */
-} eOLDrawState;
+};
-typedef enum {
+enum eOLSetState {
OL_SETSEL_NONE = 0, /* don't change the selection state */
OL_SETSEL_NORMAL = 1, /* select the item */
OL_SETSEL_EXTEND = 2, /* select the item and extend (also toggles selection) */
-} eOLSetState;
+};
/* get TreeStoreElem associated with a TreeElement
* < a: (TreeElement) tree element to find stored element for
@@ -225,29 +228,29 @@ typedef enum {
* Container to avoid passing around these variables to many functions.
* Also so we can have one place to assign these variables.
*/
-typedef struct TreeViewContext {
+struct TreeViewContext {
/* Scene level. */
struct Scene *scene;
struct ViewLayer *view_layer;
/* Object level. */
- /** Avoid OBACT macro everywhere. */
+ /** Avoid `BKE_view_layer_active_object_get` everywhere. */
Object *obact;
Object *ob_edit;
/**
* The pose object may not be the active object (when in weight paint mode).
* Checking this in draw loops isn't efficient, so set only once. */
Object *ob_pose;
-} TreeViewContext;
+};
-typedef enum TreeItemSelectAction {
+enum TreeItemSelectAction {
OL_ITEM_DESELECT = 0, /* Deselect the item */
OL_ITEM_SELECT = (1 << 0), /* Select the item */
OL_ITEM_SELECT_DATA = (1 << 1), /* Select object data */
OL_ITEM_ACTIVATE = (1 << 2), /* Activate the item */
OL_ITEM_EXTEND = (1 << 3), /* Extend the current selection */
OL_ITEM_RECURSIVE = (1 << 4), /* Select recursively */
-} TreeItemSelectAction;
+};
/* outliner_tree.c ----------------------------------------------- */
@@ -270,24 +273,19 @@ void outliner_build_tree(struct Main *mainvar,
struct SpaceOutliner *space_outliner,
struct ARegion *region);
-struct TreeElement *outliner_add_collection_recursive(SpaceOutliner *space_outliner,
- struct Collection *collection,
- TreeElement *ten);
+TreeElement *outliner_add_collection_recursive(SpaceOutliner *space_outliner,
+ struct Collection *collection,
+ TreeElement *ten);
bool outliner_requires_rebuild_on_select_or_active_change(
const struct SpaceOutliner *space_outliner);
-/**
- * Check if a display mode needs a full rebuild if the open/collapsed state changes.
- * Element types in these modes don't actually add children if collapsed, so the rebuild is needed.
- */
-bool outliner_requires_rebuild_on_open_change(const struct SpaceOutliner *space_outliner);
typedef struct IDsSelectedData {
struct ListBase selected_array;
} IDsSelectedData;
-TreeTraversalAction outliner_find_selected_collections(struct TreeElement *te, void *customdata);
-TreeTraversalAction outliner_find_selected_objects(struct TreeElement *te, void *customdata);
+TreeTraversalAction outliner_collect_selected_collections(TreeElement *te, void *customdata);
+TreeTraversalAction outliner_collect_selected_objects(TreeElement *te, void *customdata);
/* outliner_draw.c ---------------------------------------------- */
@@ -349,7 +347,7 @@ struct bPoseChannel *outliner_find_parent_bone(TreeElement *te, TreeElement **r_
*/
void outliner_item_select(struct bContext *C,
struct SpaceOutliner *space_outliner,
- struct TreeElement *te,
+ TreeElement *te,
short select_flag);
/**
@@ -379,7 +377,7 @@ void outliner_item_mode_toggle(struct bContext *C,
typedef void (*outliner_operation_fn)(struct bContext *C,
struct ReportList *,
struct Scene *scene,
- struct TreeElement *,
+ TreeElement *,
struct TreeStoreElem *,
TreeStoreElem *,
void *);
@@ -408,12 +406,10 @@ int outliner_flag_is_any_test(ListBase *lb, short flag, int curlevel);
* Set or unset \a flag for all outliner elements in \a lb and sub-trees.
* \return if any flag was modified.
*/
-extern "C++" {
bool outliner_flag_set(const SpaceOutliner &space_outliner, short flag, short set);
bool outliner_flag_set(const ListBase &lb, short flag, short set);
bool outliner_flag_flip(const SpaceOutliner &space_outliner, short flag);
bool outliner_flag_flip(const ListBase &lb, short flag);
-}
void item_rename_fn(struct bContext *C,
struct ReportList *reports,
@@ -425,14 +421,14 @@ void item_rename_fn(struct bContext *C,
void lib_relocate_fn(struct bContext *C,
struct ReportList *reports,
struct Scene *scene,
- struct TreeElement *te,
+ TreeElement *te,
struct TreeStoreElem *tsep,
struct TreeStoreElem *tselem,
void *user_data);
void lib_reload_fn(struct bContext *C,
struct ReportList *reports,
struct Scene *scene,
- struct TreeElement *te,
+ TreeElement *te,
struct TreeStoreElem *tsep,
struct TreeStoreElem *tselem,
void *user_data);
@@ -440,14 +436,14 @@ void lib_reload_fn(struct bContext *C,
void id_delete_tag_fn(struct bContext *C,
struct ReportList *reports,
struct Scene *scene,
- struct TreeElement *te,
+ TreeElement *te,
struct TreeStoreElem *tsep,
struct TreeStoreElem *tselem,
void *user_data);
void id_remap_fn(struct bContext *C,
struct ReportList *reports,
struct Scene *scene,
- struct TreeElement *te,
+ TreeElement *te,
struct TreeStoreElem *tsep,
struct TreeStoreElem *tselem,
void *user_data);
@@ -461,10 +457,7 @@ void outliner_set_coordinates(const struct ARegion *region,
/**
* Open or close a tree element, optionally toggling all children recursively.
*/
-void outliner_item_openclose(struct SpaceOutliner *space_outliner,
- TreeElement *te,
- bool open,
- bool toggle_all);
+void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all);
/* outliner_dragdrop.c */
@@ -530,6 +523,8 @@ void OUTLINER_OT_operation(struct wmOperatorType *ot);
void OUTLINER_OT_scene_operation(struct wmOperatorType *ot);
void OUTLINER_OT_object_operation(struct wmOperatorType *ot);
void OUTLINER_OT_lib_operation(struct wmOperatorType *ot);
+void OUTLINER_OT_liboverride_operation(struct wmOperatorType *ot);
+void OUTLINER_OT_liboverride_troubleshoot_operation(struct wmOperatorType *ot);
void OUTLINER_OT_id_operation(struct wmOperatorType *ot);
void OUTLINER_OT_id_remap(struct wmOperatorType *ot);
void OUTLINER_OT_id_copy(struct wmOperatorType *ot);
@@ -610,10 +605,6 @@ TreeElement *outliner_find_item_at_x_in_row(const SpaceOutliner *space_outliner,
bool *r_is_merged_icon,
bool *r_is_over_icon);
/**
- * `tse` is not in the tree-store, we use its contents to find a match.
- */
-TreeElement *outliner_find_tse(struct SpaceOutliner *space_outliner, const TreeStoreElem *tse);
-/**
* Find specific item from the trees-tore.
*/
TreeElement *outliner_find_tree_element(ListBase *lb, const TreeStoreElem *store_elem);
@@ -689,12 +680,6 @@ int outliner_context(const struct bContext *C,
const char *member,
struct bContextDataResult *result);
-#ifdef __cplusplus
-}
-#endif
-
-namespace blender::ed::outliner {
-
/**
* Helper to safely "cast" a #TreeElement to its new C++ #AbstractTreeElement, if possible.
* \return nullptr if the tree-element doesn't match the requested type \a TreeElementT or the
diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc
index 8baac45666e..cf9c4834667 100644
--- a/source/blender/editors/space_outliner/outliner_ops.cc
+++ b/source/blender/editors/space_outliner/outliner_ops.cc
@@ -11,6 +11,7 @@
#include "outliner_intern.hh"
+namespace blender::ed::outliner {
/* -------------------------------------------------------------------- */
/** \name Registration
* \{ */
@@ -29,6 +30,8 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_object_operation);
WM_operatortype_append(OUTLINER_OT_lib_operation);
WM_operatortype_append(OUTLINER_OT_lib_relocate);
+ WM_operatortype_append(OUTLINER_OT_liboverride_operation);
+ WM_operatortype_append(OUTLINER_OT_liboverride_troubleshoot_operation);
WM_operatortype_append(OUTLINER_OT_id_operation);
WM_operatortype_append(OUTLINER_OT_id_delete);
WM_operatortype_append(OUTLINER_OT_id_remap);
@@ -101,3 +104,5 @@ void outliner_keymap(wmKeyConfig *keyconf)
}
/** \} */
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_query.cc b/source/blender/editors/space_outliner/outliner_query.cc
index d6483c44fce..11929cbe2f0 100644
--- a/source/blender/editors/space_outliner/outliner_query.cc
+++ b/source/blender/editors/space_outliner/outliner_query.cc
@@ -13,7 +13,7 @@
#include "outliner_intern.hh"
#include "tree/tree_display.hh"
-using namespace blender::ed::outliner;
+namespace blender::ed::outliner {
bool outliner_shows_mode_column(const SpaceOutliner &space_outliner)
{
@@ -46,3 +46,5 @@ bool outliner_has_element_warnings(const SpaceOutliner &space_outliner)
return recursive_fn(space_outliner.tree);
}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc
index 877e0fc325c..15079448317 100644
--- a/source/blender/editors/space_outliner/outliner_select.cc
+++ b/source/blender/editors/space_outliner/outliner_select.cc
@@ -70,7 +70,7 @@
#include "tree/tree_element_seq.hh"
#include "tree/tree_iterator.hh"
-using namespace blender::ed::outliner;
+namespace blender::ed::outliner {
/* -------------------------------------------------------------------- */
/** \name Internal Utilities
@@ -164,9 +164,10 @@ static void do_outliner_item_mode_toggle_generic(bContext *C, TreeViewContext *t
ED_undo_group_begin(C);
if (ED_object_mode_set(C, OB_MODE_OBJECT)) {
+ BKE_view_layer_synced_ensure(tvc->scene, tvc->view_layer);
Base *base_active = BKE_view_layer_base_find(tvc->view_layer, tvc->obact);
if (base_active != base) {
- BKE_view_layer_base_deselect_all(tvc->view_layer);
+ BKE_view_layer_base_deselect_all(tvc->scene, tvc->view_layer);
BKE_view_layer_base_select_and_set_active(tvc->view_layer, base);
DEG_id_tag_update(&tvc->scene->id, ID_RECALC_SELECT);
ED_undo_push(C, "Change Active");
@@ -188,10 +189,12 @@ void outliner_item_mode_toggle(bContext *C,
if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
Object *ob = (Object *)tselem->id;
+ BKE_view_layer_synced_ensure(tvc->scene, tvc->view_layer);
Base *base = BKE_view_layer_base_find(tvc->view_layer, ob);
/* Hidden objects can be removed from the mode. */
- if (!base || (!(base->flag & BASE_VISIBLE_DEPSGRAPH) && (ob->mode != tvc->obact->mode))) {
+ if (!base || (!(base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) &&
+ (ob->mode != tvc->obact->mode))) {
return;
}
@@ -220,7 +223,7 @@ static void tree_element_viewlayer_activate(bContext *C, TreeElement *te)
return;
}
- ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata);
+ ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata);
wmWindow *win = CTX_wm_window(C);
Scene *scene = WM_window_get_active_scene(win);
@@ -233,15 +236,15 @@ static void tree_element_viewlayer_activate(bContext *C, TreeElement *te)
/**
* Select object tree
*/
-static void do_outliner_object_select_recursive(ViewLayer *view_layer,
+static void do_outliner_object_select_recursive(const Scene *scene,
+ ViewLayer *view_layer,
Object *ob_parent,
bool select)
{
- Base *base;
-
- for (base = reinterpret_cast<Base *>(FIRSTBASE(view_layer)); base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
- if ((((base->flag & BASE_VISIBLE_DEPSGRAPH) != 0) &&
+ if ((((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0) &&
BKE_object_is_child_recursive(ob_parent, ob))) {
ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT);
}
@@ -301,7 +304,8 @@ static void tree_element_object_activate(bContext *C,
ob = (Object *)parent_tselem->id;
/* Don't return when activating children of the previous active object. */
- if (ob == OBACT(view_layer) && set == OL_SETSEL_NONE) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob == BKE_view_layer_active_object_get(view_layer) && set == OL_SETSEL_NONE) {
return;
}
}
@@ -317,11 +321,12 @@ static void tree_element_object_activate(bContext *C,
}
/* find associated base in current scene */
+ BKE_view_layer_synced_ensure(sce, view_layer);
base = BKE_view_layer_base_find(view_layer, ob);
if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
if (base != nullptr) {
- Object *obact = OBACT(view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
const eObjectMode object_mode = obact ? (eObjectMode)obact->mode : OB_MODE_OBJECT;
if (base && !BKE_object_is_mode_compat(base->object, object_mode)) {
if (object_mode == OB_MODE_OBJECT) {
@@ -362,7 +367,7 @@ static void tree_element_object_activate(bContext *C,
if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) ?
(ob->mode == OB_MODE_OBJECT) :
true) {
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
}
ED_object_base_select(base, BA_SELECT);
if (parent_tselem) {
@@ -372,7 +377,8 @@ static void tree_element_object_activate(bContext *C,
if (recursive) {
/* Recursive select/deselect for Object hierarchies */
- do_outliner_object_select_recursive(view_layer, ob, (base->flag & BASE_SELECTED) != 0);
+ do_outliner_object_select_recursive(
+ scene, view_layer, ob, (base->flag & BASE_SELECTED) != 0);
}
if (set != OL_SETSEL_NONE) {
@@ -383,12 +389,17 @@ static void tree_element_object_activate(bContext *C,
}
}
-static void tree_element_material_activate(bContext *C, ViewLayer *view_layer, TreeElement *te)
+static void tree_element_material_activate(bContext *C,
+ const Scene *scene,
+ ViewLayer *view_layer,
+ TreeElement *te)
{
/* we search for the object parent */
Object *ob = (Object *)outliner_search_back(te, ID_OB);
/* Note : ob->matbits can be nullptr when a local object points to a library mesh. */
- if (ob == nullptr || ob != OBACT(view_layer) || ob->matbits == nullptr) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob == nullptr || ob != BKE_view_layer_active_object_get(view_layer) ||
+ ob->matbits == nullptr) {
return; /* just paranoia */
}
@@ -418,7 +429,7 @@ static void tree_element_camera_activate(bContext *C, Scene *scene, TreeElement
scene->camera = ob;
Main *bmain = CTX_data_main(C);
- wmWindowManager *wm = reinterpret_cast<wmWindowManager *>(bmain->wm.first);
+ wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
WM_windows_scene_data_sync(&wm->windows, scene);
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
@@ -458,7 +469,7 @@ static void tree_element_defgroup_activate(bContext *C, TreeElement *te, TreeSto
static void tree_element_gplayer_activate(bContext *C, TreeElement *te, TreeStoreElem *tselem)
{
bGPdata *gpd = (bGPdata *)tselem->id;
- bGPDlayer *gpl = reinterpret_cast<bGPDlayer *>(te->directdata);
+ bGPDlayer *gpl = static_cast<bGPDlayer *>(te->directdata);
/* We can only have a single "active" layer at a time
* and there must always be an active layer... */
@@ -479,6 +490,7 @@ static void tree_element_posegroup_activate(bContext *C, TreeElement *te, TreeSt
}
static void tree_element_posechannel_activate(bContext *C,
+ const Scene *scene,
ViewLayer *view_layer,
TreeElement *te,
TreeStoreElem *tselem,
@@ -486,14 +498,15 @@ static void tree_element_posechannel_activate(bContext *C,
bool recursive)
{
Object *ob = (Object *)tselem->id;
- bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
- bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata);
+ bArmature *arm = static_cast<bArmature *>(ob->data);
+ bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata);
if (!(pchan->bone->flag & BONE_HIDDEN_P)) {
if (set != OL_SETSEL_EXTEND) {
/* Single select forces all other bones to get unselected. */
uint objects_len = 0;
- Object **objects = BKE_object_pose_array_get_unique(view_layer, nullptr, &objects_len);
+ Object **objects = BKE_object_pose_array_get_unique(
+ scene, view_layer, nullptr, &objects_len);
for (uint object_index = 0; object_index < objects_len; object_index++) {
Object *ob_iter = BKE_object_pose_armature_get(objects[object_index]);
@@ -508,7 +521,7 @@ static void tree_element_posechannel_activate(bContext *C,
}
if (ob != ob_iter) {
- DEG_id_tag_update(reinterpret_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
}
}
MEM_freeN(objects);
@@ -534,6 +547,7 @@ static void tree_element_posechannel_activate(bContext *C,
}
static void tree_element_bone_activate(bContext *C,
+ const Scene *scene,
ViewLayer *view_layer,
TreeElement *te,
TreeStoreElem *tselem,
@@ -541,14 +555,15 @@ static void tree_element_bone_activate(bContext *C,
bool recursive)
{
bArmature *arm = (bArmature *)tselem->id;
- Bone *bone = reinterpret_cast<Bone *>(te->directdata);
+ Bone *bone = static_cast<Bone *>(te->directdata);
if (!(bone->flag & BONE_HIDDEN_P)) {
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob) {
if (set != OL_SETSEL_EXTEND) {
/* single select forces all other bones to get unselected */
- for (Bone *bone_iter = reinterpret_cast<Bone *>(arm->bonebase.first); bone_iter != nullptr;
+ for (Bone *bone_iter = static_cast<Bone *>(arm->bonebase.first); bone_iter != nullptr;
bone_iter = bone_iter->next) {
bone_iter->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL);
do_outliner_bone_select_recursive(arm, bone_iter, false);
@@ -583,6 +598,7 @@ static void tree_element_active_ebone__sel(bContext *C, bArmature *arm, EditBone
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, CTX_data_edit_object(C));
}
static void tree_element_ebone_activate(bContext *C,
+ const Scene *scene,
ViewLayer *view_layer,
TreeElement *te,
TreeStoreElem *tselem,
@@ -590,7 +606,7 @@ static void tree_element_ebone_activate(bContext *C,
bool recursive)
{
bArmature *arm = (bArmature *)tselem->id;
- EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata);
+ EditBone *ebone = static_cast<EditBone *>(te->directdata);
if (set == OL_SETSEL_NORMAL) {
if (!(ebone->flag & BONE_HIDDEN_A)) {
@@ -601,7 +617,7 @@ static void tree_element_ebone_activate(bContext *C,
ob_params.no_dup_data = true;
Base **bases = BKE_view_layer_array_from_bases_in_mode_params(
- view_layer, nullptr, &bases_len, &ob_params);
+ scene, view_layer, nullptr, &bases_len, &ob_params);
ED_armature_edit_deselect_all_multi_ex(bases, bases_len);
MEM_freeN(bases);
@@ -648,6 +664,7 @@ static void tree_element_psys_activate(bContext *C, TreeStoreElem *tselem)
}
static void tree_element_constraint_activate(bContext *C,
+ const Scene *scene,
ViewLayer *view_layer,
TreeElement *te,
TreeStoreElem *tselem,
@@ -660,7 +677,7 @@ static void tree_element_constraint_activate(bContext *C,
while (te) {
tselem = TREESTORE(te);
if (tselem->type == TSE_POSE_CHANNEL) {
- tree_element_posechannel_activate(C, view_layer, te, tselem, set, false);
+ tree_element_posechannel_activate(C, scene, view_layer, te, tselem, set, false);
return;
}
te = te->parent;
@@ -703,7 +720,7 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED
#if 0
select_single_seq(seq, 1);
#endif
- Sequence *p = reinterpret_cast<Sequence *>(ed->seqbasep->first);
+ Sequence *p = static_cast<Sequence *>(ed->seqbasep->first);
while (p) {
if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) {
p = p->next;
@@ -722,7 +739,7 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED
static void tree_element_master_collection_activate(const bContext *C)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
- LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(
+ LayerCollection *layer_collection = static_cast<LayerCollection *>(
view_layer->layer_collections.first);
BKE_layer_collection_activate(view_layer, layer_collection);
/* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work
@@ -733,7 +750,7 @@ static void tree_element_master_collection_activate(const bContext *C)
static void tree_element_layer_collection_activate(bContext *C, TreeElement *te)
{
Scene *scene = CTX_data_scene(C);
- LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(te->directdata);
+ LayerCollection *layer_collection = static_cast<LayerCollection *>(te->directdata);
ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection);
BKE_layer_collection_activate(view_layer, layer_collection);
/* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work
@@ -765,7 +782,7 @@ void tree_element_activate(bContext *C,
}
break;
case ID_MA:
- tree_element_material_activate(C, tvc->view_layer, te);
+ tree_element_material_activate(C, tvc->scene, tvc->view_layer, te);
break;
case ID_WO:
tree_element_world_activate(C, tvc->scene, te);
@@ -792,10 +809,10 @@ void tree_element_type_active_set(bContext *C,
tree_element_defgroup_activate(C, te, tselem);
break;
case TSE_BONE:
- tree_element_bone_activate(C, tvc->view_layer, te, tselem, set, recursive);
+ tree_element_bone_activate(C, tvc->scene, tvc->view_layer, te, tselem, set, recursive);
break;
case TSE_EBONE:
- tree_element_ebone_activate(C, tvc->view_layer, te, tselem, set, recursive);
+ tree_element_ebone_activate(C, tvc->scene, tvc->view_layer, te, tselem, set, recursive);
break;
case TSE_MODIFIER:
tree_element_modifier_activate(C, te, tselem, set);
@@ -809,11 +826,12 @@ void tree_element_type_active_set(bContext *C,
case TSE_POSE_BASE:
return;
case TSE_POSE_CHANNEL:
- tree_element_posechannel_activate(C, tvc->view_layer, te, tselem, set, recursive);
+ tree_element_posechannel_activate(
+ C, tvc->scene, tvc->view_layer, te, tselem, set, recursive);
break;
case TSE_CONSTRAINT_BASE:
case TSE_CONSTRAINT:
- tree_element_constraint_activate(C, tvc->view_layer, te, tselem, set);
+ tree_element_constraint_activate(C, tvc->scene, tvc->view_layer, te, tselem, set);
break;
case TSE_R_LAYER:
tree_element_viewlayer_activate(C, te);
@@ -839,12 +857,14 @@ void tree_element_type_active_set(bContext *C,
}
}
-static eOLDrawState tree_element_defgroup_state_get(const ViewLayer *view_layer,
+static eOLDrawState tree_element_defgroup_state_get(const Scene *scene,
+ ViewLayer *view_layer,
const TreeElement *te,
const TreeStoreElem *tselem)
{
const Object *ob = (const Object *)tselem->id;
- if (ob == OBACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob == BKE_view_layer_active_object_get(view_layer)) {
if (BKE_object_defgroup_active_index_get(ob) == te->index + 1) {
return OL_DRAWSEL_NORMAL;
}
@@ -852,13 +872,15 @@ static eOLDrawState tree_element_defgroup_state_get(const ViewLayer *view_layer,
return OL_DRAWSEL_NONE;
}
-static eOLDrawState tree_element_bone_state_get(const ViewLayer *view_layer,
+static eOLDrawState tree_element_bone_state_get(const Scene *scene,
+ ViewLayer *view_layer,
const TreeElement *te,
const TreeStoreElem *tselem)
{
const bArmature *arm = (const bArmature *)tselem->id;
- const Bone *bone = reinterpret_cast<Bone *>(te->directdata);
- const Object *ob = OBACT(view_layer);
+ const Bone *bone = static_cast<Bone *>(te->directdata);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob && ob->data == arm) {
if (bone->flag & BONE_SELECTED) {
return OL_DRAWSEL_NORMAL;
@@ -869,7 +891,7 @@ static eOLDrawState tree_element_bone_state_get(const ViewLayer *view_layer,
static eOLDrawState tree_element_ebone_state_get(const TreeElement *te)
{
- const EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata);
+ const EditBone *ebone = static_cast<EditBone *>(te->directdata);
if (ebone->flag & BONE_SELECTED) {
return OL_DRAWSEL_NORMAL;
}
@@ -891,11 +913,13 @@ static eOLDrawState tree_element_object_state_get(const TreeViewContext *tvc,
return (tselem->id == (const ID *)tvc->obact) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE;
}
-static eOLDrawState tree_element_pose_state_get(const ViewLayer *view_layer,
+static eOLDrawState tree_element_pose_state_get(const Scene *scene,
+ const ViewLayer *view_layer,
const TreeStoreElem *tselem)
{
const Object *ob = (const Object *)tselem->id;
/* This will just lookup in a cache, it will not change the arguments. */
+ BKE_view_layer_synced_ensure(scene, (ViewLayer *)view_layer);
const Base *base = BKE_view_layer_base_find((ViewLayer *)view_layer, (Object *)ob);
if (base == nullptr) {
/* Armature not instantiated in current scene (e.g. inside an appended group). */
@@ -913,7 +937,7 @@ static eOLDrawState tree_element_posechannel_state_get(const Object *ob_pose,
const TreeStoreElem *tselem)
{
const Object *ob = (const Object *)tselem->id;
- const bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata);
+ const bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata);
if (ob == ob_pose && ob->pose) {
if (pchan->bone->flag & BONE_SELECTED) {
return OL_DRAWSEL_NORMAL;
@@ -929,7 +953,7 @@ static eOLDrawState tree_element_viewlayer_state_get(const bContext *C, const Tr
return OL_DRAWSEL_NONE;
}
- const ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata);
+ const ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata);
if (CTX_data_view_layer(C) == view_layer) {
return OL_DRAWSEL_NORMAL;
@@ -937,13 +961,15 @@ static eOLDrawState tree_element_viewlayer_state_get(const bContext *C, const Tr
return OL_DRAWSEL_NONE;
}
-static eOLDrawState tree_element_posegroup_state_get(const ViewLayer *view_layer,
+static eOLDrawState tree_element_posegroup_state_get(const Scene *scene,
+ ViewLayer *view_layer,
const TreeElement *te,
const TreeStoreElem *tselem)
{
const Object *ob = (const Object *)tselem->id;
- if (ob == OBACT(view_layer) && ob->pose) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob == BKE_view_layer_active_object_get(view_layer) && ob->pose) {
if (ob->pose->active_group == te->index + 1) {
return OL_DRAWSEL_NORMAL;
}
@@ -1003,13 +1029,16 @@ static eOLDrawState tree_element_layer_collection_state_get(const bContext *C,
return OL_DRAWSEL_NONE;
}
-static eOLDrawState tree_element_active_material_get(const ViewLayer *view_layer,
+static eOLDrawState tree_element_active_material_get(const Scene *scene,
+ ViewLayer *view_layer,
const TreeElement *te)
{
/* we search for the object parent */
const Object *ob = (const Object *)outliner_search_back((TreeElement *)te, ID_OB);
/* Note : ob->matbits can be nullptr when a local object points to a library mesh. */
- if (ob == nullptr || ob != OBACT(view_layer) || ob->matbits == nullptr) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob == nullptr || ob != BKE_view_layer_active_object_get(view_layer) ||
+ ob->matbits == nullptr) {
return OL_DRAWSEL_NONE; /* just paranoia */
}
@@ -1078,7 +1107,7 @@ eOLDrawState tree_element_active_state_get(const TreeViewContext *tvc,
return OL_DRAWSEL_NONE;
break;
case ID_MA:
- return tree_element_active_material_get(tvc->view_layer, te);
+ return tree_element_active_material_get(tvc->scene, tvc->view_layer, te);
case ID_WO:
return tree_element_active_world_get(tvc->scene, te);
case ID_CA:
@@ -1094,9 +1123,9 @@ eOLDrawState tree_element_type_active_state_get(const bContext *C,
{
switch (tselem->type) {
case TSE_DEFGROUP:
- return tree_element_defgroup_state_get(tvc->view_layer, te, tselem);
+ return tree_element_defgroup_state_get(tvc->scene, tvc->view_layer, te, tselem);
case TSE_BONE:
- return tree_element_bone_state_get(tvc->view_layer, te, tselem);
+ return tree_element_bone_state_get(tvc->scene, tvc->view_layer, te, tselem);
case TSE_EBONE:
return tree_element_ebone_state_get(te);
case TSE_MODIFIER:
@@ -1106,7 +1135,7 @@ eOLDrawState tree_element_type_active_state_get(const bContext *C,
case TSE_LINKED_PSYS:
return OL_DRAWSEL_NONE;
case TSE_POSE_BASE:
- return tree_element_pose_state_get(tvc->view_layer, tselem);
+ return tree_element_pose_state_get(tvc->scene, tvc->view_layer, tselem);
case TSE_POSE_CHANNEL:
return tree_element_posechannel_state_get(tvc->ob_pose, te, tselem);
case TSE_CONSTRAINT_BASE:
@@ -1115,7 +1144,7 @@ eOLDrawState tree_element_type_active_state_get(const bContext *C,
case TSE_R_LAYER:
return tree_element_viewlayer_state_get(C, te);
case TSE_POSEGRP:
- return tree_element_posegroup_state_get(tvc->view_layer, te, tselem);
+ return tree_element_posegroup_state_get(tvc->scene, tvc->view_layer, te, tselem);
case TSE_SEQUENCE:
return tree_element_sequence_state_get(tvc->scene, te);
case TSE_SEQUENCE_DUP:
@@ -1229,7 +1258,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
/* Expand the selected constraint in the properties editor. */
if (tselem->type != TSE_CONSTRAINT_BASE) {
- BKE_constraint_panel_expand(reinterpret_cast<bConstraint *>(te->directdata));
+ BKE_constraint_panel_expand(static_cast<bConstraint *>(te->directdata));
}
break;
}
@@ -1242,8 +1271,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
Object *ob = (Object *)tselem->id;
if (ob->type == OB_GPENCIL) {
- BKE_gpencil_modifier_panel_expand(
- reinterpret_cast<GpencilModifierData *>(te->directdata));
+ BKE_gpencil_modifier_panel_expand(static_cast<GpencilModifierData *>(te->directdata));
}
else {
ModifierData *md = (ModifierData *)te->directdata;
@@ -1276,12 +1304,12 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
context = BCONTEXT_SHADERFX;
if (tselem->type != TSE_GPENCIL_EFFECT_BASE) {
- BKE_shaderfx_panel_expand(reinterpret_cast<ShaderFxData *>(te->directdata));
+ BKE_shaderfx_panel_expand(static_cast<ShaderFxData *>(te->directdata));
}
break;
case TSE_BONE: {
bArmature *arm = (bArmature *)tselem->id;
- Bone *bone = reinterpret_cast<Bone *>(te->directdata);
+ Bone *bone = static_cast<Bone *>(te->directdata);
RNA_pointer_create(&arm->id, &RNA_Bone, bone, &ptr);
context = BCONTEXT_BONE;
@@ -1289,7 +1317,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
}
case TSE_EBONE: {
bArmature *arm = (bArmature *)tselem->id;
- EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata);
+ EditBone *ebone = static_cast<EditBone *>(te->directdata);
RNA_pointer_create(&arm->id, &RNA_EditBone, ebone, &ptr);
context = BCONTEXT_BONE;
@@ -1297,8 +1325,8 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
}
case TSE_POSE_CHANNEL: {
Object *ob = (Object *)tselem->id;
- bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
- bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata);
+ bArmature *arm = static_cast<bArmature *>(ob->data);
+ bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata);
RNA_pointer_create(&arm->id, &RNA_PoseBone, pchan, &ptr);
context = BCONTEXT_BONE;
@@ -1306,7 +1334,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
}
case TSE_POSE_BASE: {
Object *ob = (Object *)tselem->id;
- bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
+ bArmature *arm = static_cast<bArmature *>(ob->data);
RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr);
context = BCONTEXT_DATA;
@@ -1314,7 +1342,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
}
case TSE_R_LAYER_BASE:
case TSE_R_LAYER: {
- ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata);
+ ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata);
RNA_pointer_create(tselem->id, &RNA_ViewLayer, view_layer, &ptr);
context = BCONTEXT_VIEW_LAYER;
@@ -1323,7 +1351,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE
case TSE_POSEGRP_BASE:
case TSE_POSEGRP: {
Object *ob = (Object *)tselem->id;
- bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
+ bArmature *arm = static_cast<bArmature *>(ob->data);
RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr);
context = BCONTEXT_DATA;
@@ -1397,6 +1425,7 @@ static void do_outliner_item_activate_tree_element(bContext *C,
}
else if ((te->idcode == ID_GR) && (space_outliner->outlinevis != SO_VIEW_LAYER)) {
Collection *gr = (Collection *)tselem->id;
+ BKE_view_layer_synced_ensure(tvc->scene, tvc->view_layer);
if (extend) {
eObjectSelect_Mode sel = BA_SELECT;
@@ -1418,7 +1447,7 @@ static void do_outliner_item_activate_tree_element(bContext *C,
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
else {
- BKE_view_layer_base_deselect_all(tvc->view_layer);
+ BKE_view_layer_base_deselect_all(tvc->scene, tvc->view_layer);
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (gr, object) {
Base *base = BKE_view_layer_base_find(tvc->view_layer, object);
@@ -1570,8 +1599,10 @@ static bool outliner_is_co_within_active_mode_column(bContext *C,
SpaceOutliner *space_outliner,
const float view_mval[2])
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
return outliner_is_co_within_mode_column(space_outliner, view_mval) && obact &&
obact->mode != OB_MODE_OBJECT;
@@ -1823,7 +1854,7 @@ static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *space_o
{
while (te->subtree.last) {
if (TSELEM_OPEN(TREESTORE(te), space_outliner)) {
- te = reinterpret_cast<TreeElement *>(te->subtree.last);
+ te = static_cast<TreeElement *>(te->subtree.last);
}
else {
break;
@@ -1867,7 +1898,7 @@ static TreeElement *outliner_find_next_element(SpaceOutliner *space_outliner, Tr
TreeStoreElem *tselem = TREESTORE(te);
if (TSELEM_OPEN(tselem, space_outliner) && te->subtree.first) {
- te = reinterpret_cast<TreeElement *>(te->subtree.first);
+ te = static_cast<TreeElement *>(te->subtree.first);
}
else if (te->next) {
te = te->next;
@@ -1886,7 +1917,7 @@ static TreeElement *outliner_walk_left(SpaceOutliner *space_outliner,
TreeStoreElem *tselem = TREESTORE(te);
if (TSELEM_OPEN(tselem, space_outliner)) {
- outliner_item_openclose(space_outliner, te, false, toggle_all);
+ outliner_item_openclose(te, false, toggle_all);
}
/* Only walk up a level if the element is closed and not toggling expand */
else if (!toggle_all && te->parent) {
@@ -1904,10 +1935,10 @@ static TreeElement *outliner_walk_right(SpaceOutliner *space_outliner,
/* Only walk down a level if the element is open and not toggling expand */
if (!toggle_all && TSELEM_OPEN(tselem, space_outliner) && !BLI_listbase_is_empty(&te->subtree)) {
- te = reinterpret_cast<TreeElement *>(te->subtree.first);
+ te = static_cast<TreeElement *>(te->subtree.first);
}
else {
- outliner_item_openclose(space_outliner, te, true, toggle_all);
+ outliner_item_openclose(te, true, toggle_all);
}
return te;
@@ -1955,7 +1986,7 @@ static TreeElement *find_walk_select_start_element(SpaceOutliner *space_outliner
/* If no active element exists, use the first element in the tree */
if (!active_te) {
- active_te = reinterpret_cast<TreeElement *>(space_outliner->tree.first);
+ active_te = static_cast<TreeElement *>(space_outliner->tree.first);
*changed = true;
}
@@ -2041,3 +2072,5 @@ void OUTLINER_OT_select_walk(wmOperatorType *ot)
}
/** \} */
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_sync.cc b/source/blender/editors/space_outliner/outliner_sync.cc
index 36bc7c31b91..995c83b589d 100644
--- a/source/blender/editors/space_outliner/outliner_sync.cc
+++ b/source/blender/editors/space_outliner/outliner_sync.cc
@@ -77,8 +77,8 @@ void ED_outliner_select_sync_flag_outliners(const bContext *C)
Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
- for (bScreen *screen = reinterpret_cast<bScreen *>(bmain->screens.first); screen;
- screen = reinterpret_cast<bScreen *>(screen->id.next)) {
+ for (bScreen *screen = static_cast<bScreen *>(bmain->screens.first); screen;
+ screen = static_cast<bScreen *>(screen->id.next)) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_OUTLINER) {
@@ -94,6 +94,8 @@ void ED_outliner_select_sync_flag_outliners(const bContext *C)
wm->outliner_sync_select_dirty = 0;
}
+namespace blender::ed::outliner {
+
/**
* Outliner sync select dirty flags are not enough to determine which types to sync,
* outliner display mode also needs to be considered. This stores the types of data
@@ -223,7 +225,8 @@ static void outliner_select_sync_to_object(ViewLayer *view_layer,
}
}
-static void outliner_select_sync_to_edit_bone(ViewLayer *view_layer,
+static void outliner_select_sync_to_edit_bone(const Scene *scene,
+ ViewLayer *view_layer,
TreeElement *te,
TreeStoreElem *tselem,
GSet *selected_ebones)
@@ -248,7 +251,8 @@ static void outliner_select_sync_to_edit_bone(ViewLayer *view_layer,
/* Tag if selection changed */
if (bone_flag != ebone->flag) {
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, obedit);
}
@@ -259,7 +263,7 @@ static void outliner_select_sync_to_pose_bone(TreeElement *te,
GSet *selected_pbones)
{
Object *ob = (Object *)tselem->id;
- bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
+ bArmature *arm = static_cast<bArmature *>(ob->data);
bPoseChannel *pchan = (bPoseChannel *)te->directdata;
short bone_flag = pchan->bone->flag;
@@ -316,7 +320,8 @@ static void outliner_sync_selection_from_outliner(Scene *scene,
}
else if (tselem->type == TSE_EBONE) {
if (sync_types->edit_bone) {
- outliner_select_sync_to_edit_bone(view_layer, te, tselem, selected_items->edit_bones);
+ outliner_select_sync_to_edit_bone(
+ scene, view_layer, te, tselem, selected_items->edit_bones);
}
}
else if (tselem->type == TSE_POSE_CHANNEL) {
@@ -335,8 +340,12 @@ static void outliner_sync_selection_from_outliner(Scene *scene,
}
}
+} // namespace blender::ed::outliner
+
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
{
+ using namespace blender::ed::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,
@@ -380,12 +389,16 @@ void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_out
}
}
-static void outliner_select_sync_from_object(ViewLayer *view_layer,
+namespace blender::ed::outliner {
+
+static void outliner_select_sync_from_object(const Scene *scene,
+ ViewLayer *view_layer,
Object *obact,
TreeElement *te,
TreeStoreElem *tselem)
{
Object *ob = (Object *)tselem->id;
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = (te->directdata) ? (Base *)te->directdata :
BKE_view_layer_base_find(view_layer, ob);
const bool is_selected = (base != nullptr) && ((base->flag & BASE_SELECTED) != 0);
@@ -479,7 +492,8 @@ struct SyncSelectActiveData {
};
/** Sync select and active flags from active view layer, bones, and sequences to the outliner. */
-static void outliner_sync_selection_to_outliner(ViewLayer *view_layer,
+static void outliner_sync_selection_to_outliner(const Scene *scene,
+ ViewLayer *view_layer,
SpaceOutliner *space_outliner,
ListBase *tree,
SyncSelectActiveData *active_data,
@@ -490,7 +504,7 @@ static void outliner_sync_selection_to_outliner(ViewLayer *view_layer,
if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_OB) {
if (sync_types->object) {
- outliner_select_sync_from_object(view_layer, active_data->object, te, tselem);
+ outliner_select_sync_from_object(scene, view_layer, active_data->object, te, tselem);
}
}
else if (tselem->type == TSE_EBONE) {
@@ -514,7 +528,7 @@ static void outliner_sync_selection_to_outliner(ViewLayer *view_layer,
/* Sync subtree elements */
outliner_sync_selection_to_outliner(
- view_layer, space_outliner, &te->subtree, active_data, sync_types);
+ scene, view_layer, space_outliner, &te->subtree, active_data, sync_types);
}
}
@@ -523,7 +537,8 @@ static void get_sync_select_active_data(const bContext *C, SyncSelectActiveData
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- active_data->object = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ active_data->object = BKE_view_layer_active_object_get(view_layer);
active_data->edit_bone = CTX_data_active_bone(C);
active_data->pose_channel = CTX_data_active_pose_bone(C);
active_data->sequence = SEQ_select_active_get(scene);
@@ -537,6 +552,7 @@ void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
C, space_outliner, &sync_types);
if (sync_required) {
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
/* Store active object, bones, and sequence */
@@ -544,7 +560,7 @@ void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
get_sync_select_active_data(C, &active_data);
outliner_sync_selection_to_outliner(
- view_layer, space_outliner, &space_outliner->tree, &active_data, &sync_types);
+ scene, view_layer, space_outliner, &space_outliner->tree, &active_data, &sync_types);
/* Keep any un-synced data in the dirty flag. */
if (sync_types.object) {
@@ -561,3 +577,5 @@ void outliner_sync_selection(const bContext *C, SpaceOutliner *space_outliner)
}
}
}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc
index 7b7182eb106..1628945c4cd 100644
--- a/source/blender/editors/space_outliner/outliner_tools.cc
+++ b/source/blender/editors/space_outliner/outliner_tools.cc
@@ -31,8 +31,11 @@
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
+#include "BLI_linklist.h"
+#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#include "BKE_anim_data.h"
#include "BKE_animsys.h"
@@ -86,108 +89,111 @@
#include "tree/tree_element_seq.hh"
#include "tree/tree_iterator.hh"
+namespace blender::ed::outliner {
+
static CLG_LogRef LOG = {"ed.outliner.tools"};
using namespace blender::ed::outliner;
+using blender::Map;
using blender::Set;
+using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name ID/Library/Data Set/Un-link Utilities
* \{ */
static void get_element_operation_type(
- TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel)
+ const TreeElement *te, int *scenelevel, int *objectlevel, int *idlevel, int *datalevel)
{
- TreeStoreElem *tselem = TREESTORE(te);
- if (tselem->flag & TSE_SELECTED) {
- /* Layer collection points to collection ID. */
- if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) {
- if (*datalevel == 0) {
- *datalevel = tselem->type;
- }
- else if (*datalevel != tselem->type) {
- *datalevel = -1;
- }
- }
- else {
- const int idcode = (int)GS(tselem->id->name);
- bool is_standard_id = false;
- switch ((ID_Type)idcode) {
- case ID_SCE:
- *scenelevel = 1;
- break;
- case ID_OB:
- *objectlevel = 1;
- break;
+ *scenelevel = *objectlevel = *idlevel = *datalevel = 0;
- case ID_ME:
- case ID_CU_LEGACY:
- case ID_MB:
- case ID_LT:
- case ID_LA:
- case ID_AR:
- case ID_CA:
- case ID_SPK:
- case ID_MA:
- case ID_TE:
- case ID_IP:
- case ID_IM:
- case ID_SO:
- case ID_KE:
- case ID_WO:
- case ID_AC:
- case ID_TXT:
- case ID_GR:
- case ID_LS:
- case ID_LI:
- case ID_VF:
- case ID_NT:
- case ID_BR:
- case ID_PA:
- case ID_GD:
- case ID_MC:
- case ID_MSK:
- case ID_PAL:
- case ID_PC:
- case ID_CF:
- case ID_WS:
- case ID_LP:
- case ID_CV:
- case ID_PT:
- case ID_VO:
- case ID_SIM:
- is_standard_id = true;
- break;
- case ID_WM:
- case ID_SCR:
- /* Those are ignored here. */
- /* NOTE: while Screens should be manageable here, deleting a screen used by a workspace
- * will cause crashes when trying to use that workspace, so for now let's play minimal,
- * safe change. */
- break;
- }
- if (idcode == ID_NLA) {
- /* Fake one, not an actual ID type... */
+ const TreeStoreElem *tselem = TREESTORE(te);
+ if ((tselem->flag & TSE_SELECTED) == 0) {
+ return;
+ }
+
+ /* Layer collection points to collection ID. */
+ if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) {
+ *datalevel = tselem->type;
+ }
+ else {
+ const int idcode = (int)GS(tselem->id->name);
+ bool is_standard_id = false;
+ switch ((ID_Type)idcode) {
+ case ID_SCE:
+ *scenelevel = 1;
+ break;
+ case ID_OB:
+ *objectlevel = 1;
+ break;
+
+ case ID_ME:
+ case ID_CU_LEGACY:
+ case ID_MB:
+ case ID_LT:
+ case ID_LA:
+ case ID_AR:
+ case ID_CA:
+ case ID_SPK:
+ case ID_MA:
+ case ID_TE:
+ case ID_IP:
+ case ID_IM:
+ case ID_SO:
+ case ID_KE:
+ case ID_WO:
+ case ID_AC:
+ case ID_TXT:
+ case ID_GR:
+ case ID_LS:
+ case ID_LI:
+ case ID_VF:
+ case ID_NT:
+ case ID_BR:
+ case ID_PA:
+ case ID_GD:
+ case ID_MC:
+ case ID_MSK:
+ case ID_PAL:
+ case ID_PC:
+ case ID_CF:
+ case ID_WS:
+ case ID_LP:
+ case ID_CV:
+ case ID_PT:
+ case ID_VO:
+ case ID_SIM:
is_standard_id = true;
- }
+ break;
+ case ID_WM:
+ case ID_SCR:
+ /* Those are ignored here. */
+ /* NOTE: while Screens should be manageable here, deleting a screen used by a workspace
+ * will cause crashes when trying to use that workspace, so for now let's play minimal,
+ * safe change. */
+ break;
+ }
+ if (idcode == ID_NLA) {
+ /* Fake one, not an actual ID type... */
+ is_standard_id = true;
+ }
- if (is_standard_id) {
- if (*idlevel == 0) {
- *idlevel = idcode;
- }
- else if (*idlevel != idcode) {
- *idlevel = -1;
- }
- if (ELEM(*datalevel, TSE_VIEW_COLLECTION_BASE, TSE_SCENE_COLLECTION_BASE)) {
- *datalevel = 0;
- }
- }
+ if (is_standard_id) {
+ *idlevel = idcode;
}
}
+
+ /* Return values are exclusive, only one may be non-null. */
+ BLI_assert(((*scenelevel != 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel == 0)) ||
+ ((*scenelevel == 0) && (*objectlevel != 0) && (*idlevel == 0) && (*datalevel == 0)) ||
+ ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel != 0) && (*datalevel == 0)) ||
+ ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel != 0)) ||
+ /* All null. */
+ ((*scenelevel == 0) && (*objectlevel == 0) && (*idlevel == 0) && (*datalevel == 0)));
}
-static TreeElement *get_target_element(SpaceOutliner *space_outliner)
+static TreeElement *get_target_element(const SpaceOutliner *space_outliner)
{
TreeElement *te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE);
@@ -448,6 +454,99 @@ static void outliner_do_libdata_operation(bContext *C,
});
}
+enum eOutlinerLibOpSelectionSet {
+ /* Only selected items. */
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ /* Only content 'inside' selected items (their sub-tree). */
+ OUTLINER_LIB_LIB_SELECTIONSET_CONTENT,
+ /* Combining both options above. */
+ OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT,
+};
+
+static const EnumPropertyItem prop_lib_op_selection_set[] = {
+ {OUTLINER_LIB_SELECTIONSET_SELECTED,
+ "SELECTED",
+ 0,
+ "Selected",
+ "Apply the operation over selected data-blocks only"},
+ {OUTLINER_LIB_LIB_SELECTIONSET_CONTENT,
+ "CONTENT",
+ 0,
+ "Content",
+ "Apply the operation over content of the selected items only (the data-blocks in their "
+ "sub-tree)"},
+ {OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT,
+ "SELECTED_AND_CONTENT",
+ 0,
+ "Selected & Content",
+ "Apply the operation over selected data-blocks and all their dependencies"},
+ {0, nullptr, 0, nullptr, nullptr},
+};
+
+static void outliner_do_libdata_operation_selection_set(bContext *C,
+ ReportList *reports,
+ Scene *scene,
+ SpaceOutliner *space_outliner,
+ const ListBase &subtree,
+ const bool has_parent_selected,
+ outliner_operation_fn operation_fn,
+ eOutlinerLibOpSelectionSet selection_set,
+ void *user_data)
+{
+ const bool do_selected = ELEM(selection_set,
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT);
+ const bool do_content = ELEM(selection_set,
+ OUTLINER_LIB_LIB_SELECTIONSET_CONTENT,
+ OUTLINER_LIB_LIB_SELECTIONSET_SELECTED_AND_CONTENT);
+
+ LISTBASE_FOREACH_MUTABLE (TreeElement *, element, &subtree) {
+ /* Get needed data out in case element gets freed. */
+ TreeStoreElem *tselem = TREESTORE(element);
+ const ListBase subtree = element->subtree;
+
+ bool is_selected = tselem->flag & TSE_SELECTED;
+ if ((is_selected && do_selected) || (has_parent_selected && do_content)) {
+ if (((tselem->type == TSE_SOME_ID) && (element->idcode != 0)) ||
+ tselem->type == TSE_LAYER_COLLECTION) {
+ TreeStoreElem *tsep = element->parent ? TREESTORE(element->parent) : nullptr;
+ operation_fn(C, reports, scene, element, tsep, tselem, user_data);
+ }
+ }
+
+ /* Don't access element from now on, it may be freed. Note that the open/collapsed state may
+ * also have been changed in the visitor callback. */
+ outliner_do_libdata_operation_selection_set(C,
+ reports,
+ scene,
+ space_outliner,
+ subtree,
+ is_selected || has_parent_selected,
+ operation_fn,
+ selection_set,
+ user_data);
+ }
+}
+
+static void outliner_do_libdata_operation_selection_set(bContext *C,
+ ReportList *reports,
+ Scene *scene,
+ SpaceOutliner *space_outliner,
+ outliner_operation_fn operation_fn,
+ eOutlinerLibOpSelectionSet selection_set,
+ void *user_data)
+{
+ outliner_do_libdata_operation_selection_set(C,
+ reports,
+ scene,
+ space_outliner,
+ space_outliner->tree,
+ false,
+ operation_fn,
+ selection_set,
+ user_data);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -683,8 +782,10 @@ static void object_select_fn(bContext *C,
TreeStoreElem *tselem,
void *UNUSED(user_data))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = (Object *)tselem->id;
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base) {
@@ -721,8 +822,10 @@ static void object_deselect_fn(bContext *C,
TreeStoreElem *tselem,
void *UNUSED(user_data))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = (Object *)tselem->id;
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base) {
@@ -777,6 +880,25 @@ static void id_local_fn(bContext *C,
}
}
+struct OutlinerLiboverrideDataIDRoot {
+ /** The linked ID that was selected for override. */
+ ID *id_root_reference;
+
+ /** The root of the override hierarchy to which the override of `id_root` belongs, once
+ * known/created. */
+ ID *id_hierarchy_root_override;
+
+ /** The ID that was detected as being a good candidate as instantiation hint for newly overridden
+ * objects, may be null.
+ *
+ * \note Typically currently only used when the root ID to override is a collection instanced by
+ * an empty object. */
+ ID *id_instance_hint;
+
+ /** If this override comes from an instancing object (which would be `id_instance_hint` then). */
+ bool is_override_instancing_object;
+};
+
struct OutlinerLibOverrideData {
bool do_hierarchy;
@@ -789,35 +911,82 @@ struct OutlinerLibOverrideData {
* solving broken overrides while not losing *all* of your overrides. */
bool do_resync_hierarchy_enforce;
- /** The override hierarchy root, when known/created. */
- ID *id_hierarchy_root_override;
-
- /** A hash of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
+ /** A set of the selected tree elements' ID 'uuid'. Used to clear 'system override' flags on
* their newly-created liboverrides in post-process step of override hierarchy creation. */
Set<uint> selected_id_uid;
+
+ /** A mapping from the found hierarchy roots to a linked list of IDs to override for each of
+ * these roots.
+ *
+ * \note the key may be either linked (in which case it will be replaced by the newly created
+ * override), or an actual already existing override. */
+ Map<ID *, Vector<OutlinerLiboverrideDataIDRoot>> id_hierarchy_roots;
+
+ /** All 'session_uuid' of all hierarchy root IDs used or created by the operation. */
+ Set<uint> id_hierarchy_roots_uid;
+
+ void id_root_add(ID *id_hierarchy_root_reference,
+ ID *id_root_reference,
+ ID *id_instance_hint,
+ const bool is_override_instancing_object)
+ {
+ OutlinerLiboverrideDataIDRoot id_root_data;
+ id_root_data.id_root_reference = id_root_reference;
+ id_root_data.id_hierarchy_root_override = nullptr;
+ id_root_data.id_instance_hint = id_instance_hint;
+ id_root_data.is_override_instancing_object = is_override_instancing_object;
+
+ Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
+ id_hierarchy_root_reference);
+ value.append(id_root_data);
+ }
+ void id_root_set(ID *id_hierarchy_root_reference)
+ {
+ OutlinerLiboverrideDataIDRoot id_root_data;
+ id_root_data.id_root_reference = nullptr;
+ id_root_data.id_hierarchy_root_override = nullptr;
+ id_root_data.id_instance_hint = nullptr;
+ id_root_data.is_override_instancing_object = false;
+
+ Vector<OutlinerLiboverrideDataIDRoot> &value = id_hierarchy_roots.lookup_or_add_default(
+ id_hierarchy_root_reference);
+ if (value.is_empty()) {
+ value.append(id_root_data);
+ }
+ }
};
/* Store 'UUID' of IDs of selected elements in the Outliner tree, before generating the override
* hierarchy. */
-static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED(C),
- ReportList *UNUSED(reports),
+static void id_override_library_create_hierarchy_pre_process_fn(bContext *C,
+ ReportList *reports,
Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
+ TreeElement *te,
+ TreeStoreElem *tsep,
TreeStoreElem *tselem,
void *user_data)
{
BLI_assert(TSE_IS_REAL_ID(tselem));
- OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
+ OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data);
const bool do_hierarchy = data->do_hierarchy;
ID *id_root_reference = tselem->id;
+ if (!BKE_idtype_idcode_is_linkable(GS(id_root_reference->name)) ||
+ (id_root_reference->flag & (LIB_EMBEDDED_DATA | LIB_EMBEDDED_DATA_LIB_OVERRIDE)) != 0) {
+ return;
+ }
+
BLI_assert(do_hierarchy);
UNUSED_VARS_NDEBUG(do_hierarchy);
data->selected_id_uid.add(id_root_reference->session_uuid);
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root_reference) && !ID_IS_LINKED(id_root_reference)) {
+ id_root_reference->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ return;
+ }
+
if (GS(id_root_reference->name) == ID_GR && (tselem->flag & TSE_CLOSED) != 0) {
/* If selected element is a (closed) collection, check all of its objects recursively, and also
* consider the armature ones as 'selected' (i.e. to not become system overrides). */
@@ -829,28 +998,6 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *UNUSED
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
-}
-
-static void id_override_library_create_fn(bContext *C,
- ReportList *reports,
- Scene *scene,
- TreeElement *te,
- TreeStoreElem *tsep,
- TreeStoreElem *tselem,
- void *user_data)
-{
- BLI_assert(TSE_IS_REAL_ID(tselem));
-
- /* We can only safely apply this operation on one item at a time, so only do it on the active
- * one. */
- if ((tselem->flag & TSE_ACTIVE) == 0) {
- return;
- }
-
- ID *id_root_reference = tselem->id;
- OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
- const bool do_hierarchy = data->do_hierarchy;
- bool success = false;
ID *id_instance_hint = nullptr;
bool is_override_instancing_object = false;
@@ -866,195 +1013,259 @@ static void id_override_library_create_fn(bContext *C,
}
}
- if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) ||
- (ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
- Main *bmain = CTX_data_main(C);
+ if (!ID_IS_OVERRIDABLE_LIBRARY(id_root_reference) &&
+ !(ID_IS_LINKED(id_root_reference) && do_hierarchy)) {
+ return;
+ }
- id_root_reference->tag |= LIB_TAG_DOIT;
+ Main *bmain = CTX_data_main(C);
- /* For now, remap all local usages of linked ID to local override one here. */
- ID *id_iter;
- FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_LINKED(id_iter)) {
- id_iter->tag &= ~LIB_TAG_DOIT;
- }
- else {
- id_iter->tag |= LIB_TAG_DOIT;
+ if (do_hierarchy) {
+ /* Tag all linked parents in tree hierarchy to be also overridden. */
+ ID *id_hierarchy_root_reference = id_root_reference;
+ while ((te = te->parent) != nullptr) {
+ if (!TSE_IS_REAL_ID(te->store_elem)) {
+ continue;
}
- }
- FOREACH_MAIN_ID_END;
- if (do_hierarchy) {
- /* Tag all linked parents in tree hierarchy to be also overridden. */
- ID *id_hierarchy_root_reference = id_root_reference;
- while ((te = te->parent) != nullptr) {
- if (!TSE_IS_REAL_ID(te->store_elem)) {
+ /* Tentative hierarchy root. */
+ ID *id_current_hierarchy_root = te->store_elem->id;
+
+ /* If the parent ID is from a different library than the reference root one, we are done
+ * with upwards tree processing in any case. */
+ if (id_current_hierarchy_root->lib != id_root_reference->lib) {
+ if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
+ /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
+ * get an actual real override. */
continue;
}
- /* Tentative hierarchy root. */
- ID *id_current_hierarchy_root = te->store_elem->id;
-
- /* If the parent ID is from a different library than the reference root one, we are done
- * with upwards tree processing in any case. */
- if (id_current_hierarchy_root->lib != id_root_reference->lib) {
- if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id_current_hierarchy_root)) {
- /* Virtual overrides (i.e. embedded IDs), we can simply keep processing their parent to
- * get an actual real override. */
- continue;
- }
-
- /* If the parent ID is already an override, and is valid (i.e. local override), we can
- * access its hierarchy root directly. */
- if (!ID_IS_LINKED(id_current_hierarchy_root) &&
- ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
- id_current_hierarchy_root->override_library->reference->lib ==
- id_root_reference->lib) {
- id_hierarchy_root_reference =
- id_current_hierarchy_root->override_library->hierarchy_root;
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
- break;
- }
-
- if (ID_IS_LINKED(id_current_hierarchy_root)) {
- /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
- * would most likely generate invisible/confusing/hard to use and manage overrides. */
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
- BKE_reportf(reports,
- RPT_WARNING,
- "Invalid anchor ('%s') found, needed to create library override from "
- "data-block '%s'",
- id_current_hierarchy_root->name,
- id_root_reference->name);
- return;
- }
-
- /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
- * current `id_hierarchy_root_reference` is our best candidate. */
-
+ /* If the parent ID is already an override, and is valid (i.e. local override), we can
+ * access its hierarchy root directly. */
+ if (!ID_IS_LINKED(id_current_hierarchy_root) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_current_hierarchy_root) &&
+ id_current_hierarchy_root->override_library->reference->lib ==
+ id_root_reference->lib) {
+ id_hierarchy_root_reference =
+ id_current_hierarchy_root->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
break;
}
- /* If some element in the tree needs to be overridden, but its ID is not overridable,
- * abort. */
- if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
+ if (ID_IS_LINKED(id_current_hierarchy_root)) {
+ /* No local 'anchor' was found for the hierarchy to override, do not proceed, as this
+ * would most likely generate invisible/confusing/hard to use and manage overrides. */
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
- "Could not create library override from data-block '%s', one of its parents "
- "is not overridable ('%s')",
- id_root_reference->name,
- id_current_hierarchy_root->name);
+ "Invalid anchor ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_current_hierarchy_root->name,
+ id_root_reference->name);
return;
}
- id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
- id_hierarchy_root_reference = id_current_hierarchy_root;
+
+ /* In all other cases, `id_current_hierarchy_root` cannot be a valid hierarchy root, so
+ * current `id_hierarchy_root_reference` is our best candidate. */
+
+ break;
}
- /* That case can happen when linked data is a complex mix involving several libraries and/or
- * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
- * from another library. Do not try to support such cases for now. */
- if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
- (!ID_IS_LINKED(id_hierarchy_root_reference) &&
- ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
- id_hierarchy_root_reference->override_library->reference->lib ==
- id_root_reference->lib))) {
+ /* If some element in the tree needs to be overridden, but its ID is not overridable,
+ * abort. */
+ if (!ID_IS_OVERRIDABLE_LIBRARY_HIERARCHY(id_current_hierarchy_root)) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
BKE_reportf(reports,
RPT_WARNING,
- "Invalid hierarchy root ('%s') found, needed to create library override from "
- "data-block '%s'",
- id_hierarchy_root_reference->name,
- id_root_reference->name);
+ "Could not create library override from data-block '%s', one of its parents "
+ "is not overridable ('%s')",
+ id_root_reference->name,
+ id_current_hierarchy_root->name);
return;
}
+ id_current_hierarchy_root->tag |= LIB_TAG_DOIT;
+ id_hierarchy_root_reference = id_current_hierarchy_root;
+ }
+
+ /* That case can happen when linked data is a complex mix involving several libraries and/or
+ * linked overrides. E.g. a mix of overrides from one library, and indirectly linked data
+ * from another library. Do not try to support such cases for now. */
+ if (!((id_hierarchy_root_reference->lib == id_root_reference->lib) ||
+ (!ID_IS_LINKED(id_hierarchy_root_reference) &&
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference) &&
+ id_hierarchy_root_reference->override_library->reference->lib ==
+ id_root_reference->lib))) {
+ BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Invalid hierarchy root ('%s') found, needed to create library override from "
+ "data-block '%s'",
+ id_hierarchy_root_reference->name,
+ id_root_reference->name);
+ return;
+ }
+
+ /* While ideally this should not be needed, in practice user almost _never_ wants to actually
+ * create liboverrides for all data under a selected hierarchy node, and this has currently a
+ * dreadful consequences over performances (since it would call
+ * #BKE_lib_override_library_create over _all_ items in the hierarchy). So only the clearing of
+ * the system override flag is supported for non-selected items for now.
+ */
+ const bool is_selected = tselem->flag & TSE_SELECTED;
+ if (!is_selected && data->id_hierarchy_roots.contains(id_hierarchy_root_reference)) {
+ return;
+ }
+
+ data->id_root_add(id_hierarchy_root_reference,
+ id_root_reference,
+ id_instance_hint,
+ is_override_instancing_object);
+ }
+ else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
+ data->id_root_add(
+ id_root_reference, id_root_reference, id_instance_hint, is_override_instancing_object);
+ }
+}
+
+static void id_override_library_create_hierarchy(
+ Main &bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ OutlinerLibOverrideData &data,
+ ID *id_hierarchy_root_reference,
+ Vector<OutlinerLiboverrideDataIDRoot> &data_idroots,
+ bool &r_aggregated_success)
+{
+ BLI_assert(ID_IS_LINKED(id_hierarchy_root_reference) ||
+ ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_reference));
+
+ const bool do_hierarchy = data.do_hierarchy;
+
+ /* NOTE: This process is not the most efficient, but allows to re-use existing code.
+ * If this becomes a bottle-neck at some point, we need to implement a new
+ * `BKE_lib_override_library_hierarchy_create()` function able to process several roots inside of
+ * a same hierarchy in a single call. */
+ for (OutlinerLiboverrideDataIDRoot &data_idroot : data_idroots) {
+ /* For now, remap all local usages of linked ID to local override one here. */
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (&bmain, id_iter) {
+ if (ID_IS_LINKED(id_iter) || ID_IS_OVERRIDE_LIBRARY(id_iter)) {
+ id_iter->tag &= ~LIB_TAG_DOIT;
+ }
+ else {
+ id_iter->tag |= LIB_TAG_DOIT;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+ bool success = false;
+ if (do_hierarchy) {
ID *id_root_override = nullptr;
- success = BKE_lib_override_library_create(bmain,
- CTX_data_scene(C),
- CTX_data_view_layer(C),
+ success = BKE_lib_override_library_create(&bmain,
+ scene,
+ view_layer,
nullptr,
- id_root_reference,
+ data_idroot.id_root_reference,
id_hierarchy_root_reference,
- id_instance_hint,
+ data_idroot.id_instance_hint,
&id_root_override,
- data->do_fully_editable);
-
- BLI_assert(id_root_override != nullptr);
- BLI_assert(!ID_IS_LINKED(id_root_override));
- BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
- if (ID_IS_LINKED(id_hierarchy_root_reference)) {
- BLI_assert(
- id_root_override->override_library->hierarchy_root->override_library->reference ==
- id_hierarchy_root_reference);
- data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
- }
- else {
- BLI_assert(id_root_override->override_library->hierarchy_root ==
- id_hierarchy_root_reference);
- data->id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ data.do_fully_editable);
+
+ if (success) {
+ BLI_assert(id_root_override != nullptr);
+ BLI_assert(!ID_IS_LINKED(id_root_override));
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
+
+ ID *id_hierarchy_root_override = id_root_override->override_library->hierarchy_root;
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root_override));
+ if (ID_IS_LINKED(id_hierarchy_root_reference)) {
+ BLI_assert(id_hierarchy_root_override->override_library->reference ==
+ id_hierarchy_root_reference);
+ /* If the hierarchy root reference was a linked data, after the first iteration there is
+ * now a matching override, which shall be used for all further partial overrides with
+ * this same hierarchy. */
+ id_hierarchy_root_reference = id_hierarchy_root_override;
+ }
+ else {
+ BLI_assert(id_hierarchy_root_override == id_hierarchy_root_reference);
+ }
+ data_idroot.id_hierarchy_root_override = id_hierarchy_root_override;
+ data.id_hierarchy_roots_uid.add(id_hierarchy_root_override->session_uuid);
}
}
- else if (ID_IS_OVERRIDABLE_LIBRARY(id_root_reference)) {
- success = BKE_lib_override_library_create_from_id(bmain, id_root_reference, true) != nullptr;
+ else if (ID_IS_OVERRIDABLE_LIBRARY(data_idroot.id_root_reference)) {
+ ID *id_root_override = BKE_lib_override_library_create_from_id(
+ &bmain, data_idroot.id_root_reference, true);
+ success = id_root_override != nullptr;
+ if (success) {
+ BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root_override));
+ id_root_override->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
+ }
/* Cleanup. */
- BKE_main_id_newptr_and_tag_clear(bmain);
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+ BKE_main_id_newptr_and_tag_clear(&bmain);
+ BKE_main_id_tag_all(&bmain, LIB_TAG_DOIT, false);
+ }
+ else {
+ BLI_assert_unreachable();
}
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
- if (success && is_override_instancing_object) {
- ED_object_base_free_and_unlink(bmain, scene, (Object *)id_instance_hint);
+ if (success && data_idroot.is_override_instancing_object) {
+ BLI_assert(GS(data_idroot.id_instance_hint) == ID_OB);
+ ED_object_base_free_and_unlink(
+ &bmain, scene, reinterpret_cast<Object *>(data_idroot.id_instance_hint));
}
- }
- if (!success) {
- BKE_reportf(reports,
- RPT_WARNING,
- "Could not create library override from data-block '%s'",
- id_root_reference->name);
+
+ r_aggregated_success = r_aggregated_success && success;
}
}
/* Clear system override flag from newly created overrides which linked reference were previously
* selected in the Outliner tree. */
-static void id_override_library_create_hierarchy_post_process(bContext *C,
- OutlinerLibOverrideData *data)
+static void id_override_library_create_hierarchy_process(bContext *C,
+ ReportList *reports,
+ OutlinerLibOverrideData &data)
{
Main *bmain = CTX_data_main(C);
- ID *id_hierarchy_root_override = data->id_hierarchy_root_override;
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ const bool do_hierarchy = data.do_hierarchy;
+
+ bool success = true;
+ for (auto &&[id_hierarchy_root_reference, data_idroots] : data.id_hierarchy_roots.items()) {
+ id_override_library_create_hierarchy(
+ *bmain, scene, view_layer, data, id_hierarchy_root_reference, data_idroots, success);
+ }
+
+ if (!success) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Could not create library override from one or more of the selected data-blocks");
+ }
+
+ if (!do_hierarchy) {
+ return;
+ }
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
- if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter) ||
- id_iter->override_library->hierarchy_root != id_hierarchy_root_override) {
+ if (ID_IS_LINKED(id_iter) || !ID_IS_OVERRIDE_LIBRARY_REAL(id_iter)) {
continue;
}
- if (data->selected_id_uid.contains(id_iter->override_library->reference->session_uuid)) {
+ if (!data.id_hierarchy_roots_uid.contains(
+ id_iter->override_library->hierarchy_root->session_uuid)) {
+ continue;
+ }
+ if (data.selected_id_uid.contains(id_iter->override_library->reference->session_uuid) ||
+ data.selected_id_uid.contains(id_iter->session_uuid)) {
id_iter->override_library->flag &= ~IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED;
}
}
FOREACH_MAIN_ID_END;
}
-static void id_override_library_toggle_flag_fn(bContext *UNUSED(C),
- ReportList *UNUSED(reports),
- Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *user_data)
-{
- BLI_assert(TSE_IS_REAL_ID(tselem));
- ID *id = tselem->id;
-
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- const uint flag = POINTER_AS_UINT(user_data);
- id->override_library->flag ^= flag;
- }
-}
-
static void id_override_library_reset_fn(bContext *C,
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
@@ -1065,137 +1276,158 @@ static void id_override_library_reset_fn(bContext *C,
{
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id_root = tselem->id;
- OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
+ OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data);
const bool do_hierarchy = data->do_hierarchy;
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
- Main *bmain = CTX_data_main(C);
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) {
+ CLOG_WARN(&LOG, "Could not reset library override of data block '%s'", id_root->name);
+ return;
+ }
- if (do_hierarchy) {
- BKE_lib_override_library_id_hierarchy_reset(bmain, id_root, false);
- }
- else {
- BKE_lib_override_library_id_reset(bmain, id_root, false);
- }
+ Main *bmain = CTX_data_main(C);
- WM_event_add_notifier(C, NC_WM | ND_DATACHANGED, nullptr);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
+ if (do_hierarchy) {
+ BKE_lib_override_library_id_hierarchy_reset(bmain, id_root, false);
}
else {
- CLOG_WARN(&LOG, "Could not reset library override of data block '%s'", id_root->name);
+ BKE_lib_override_library_id_reset(bmain, id_root, false);
}
}
-static void id_override_library_resync_fn(bContext *C,
- ReportList *reports,
- Scene *scene,
- TreeElement *te,
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *user_data)
+static void id_override_library_clear_single_fn(bContext *C,
+ ReportList *reports,
+ Scene *scene,
+ TreeElement *UNUSED(te),
+ TreeStoreElem *UNUSED(tsep),
+ TreeStoreElem *tselem,
+ void *UNUSED(user_data))
{
BLI_assert(TSE_IS_REAL_ID(tselem));
- ID *id_root = tselem->id;
- OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
- const bool do_hierarchy_enforce = data->do_resync_hierarchy_enforce;
-
- if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
- Main *bmain = CTX_data_main(C);
+ Main *bmain = CTX_data_main(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ ID *id = tselem->id;
- id_root->tag |= LIB_TAG_DOIT;
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id) || ID_IS_LINKED(id)) {
+ BKE_reportf(reports,
+ RPT_WARNING,
+ "Cannot clear embedded library override id '%s', only overrides of real "
+ "data-blocks can be directly deleted",
+ id->name);
+ return;
+ }
- /* Tag all linked parents in tree hierarchy to be also overridden. */
- while ((te = te->parent) != nullptr) {
- if (!TSE_IS_REAL_ID(te->store_elem)) {
- continue;
- }
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) {
- break;
+ /* If given ID is not using any other override (it's a 'leaf' in the override hierarchy),
+ * delete it and remap its usages to its linked reference. Otherwise, keep it as a reset system
+ * override. */
+ if (BKE_lib_override_library_is_hierarchy_leaf(bmain, id)) {
+ bool do_remap_active = false;
+ BKE_view_layer_synced_ensure(CTX_data_scene(C), view_layer);
+ if (BKE_view_layer_active_object_get(view_layer) == reinterpret_cast<Object *>(id)) {
+ BLI_assert(GS(id->name) == ID_OB);
+ do_remap_active = true;
+ }
+ BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE);
+ if (do_remap_active) {
+ Object *ref_object = reinterpret_cast<Object *>(id->override_library->reference);
+ Base *basact = BKE_view_layer_base_find(view_layer, ref_object);
+ if (basact != nullptr) {
+ view_layer->basact = basact;
}
- te->store_elem->id->tag |= LIB_TAG_DOIT;
+ DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
}
-
- BlendFileReadReport report{};
- report.reports = reports;
- BKE_lib_override_library_resync(
- bmain, scene, CTX_data_view_layer(C), id_root, nullptr, do_hierarchy_enforce, &report);
-
- WM_event_add_notifier(C, NC_WINDOW, nullptr);
+ BKE_id_delete(bmain, id);
}
else {
- CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name);
+ BKE_lib_override_library_id_reset(bmain, id, true);
}
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
}
-static void id_override_library_clear_hierarchy_fn(bContext *C,
- ReportList *UNUSED(reports),
- Scene *UNUSED(scene),
- TreeElement *te,
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *UNUSED(user_data))
+static void id_override_library_resync_fn(bContext *UNUSED(C),
+ ReportList *UNUSED(reports),
+ Scene *UNUSED(scene),
+ TreeElement *UNUSED(te),
+ TreeStoreElem *UNUSED(tsep),
+ TreeStoreElem *tselem,
+ void *user_data)
{
BLI_assert(TSE_IS_REAL_ID(tselem));
ID *id_root = tselem->id;
+ OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data);
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) {
- CLOG_WARN(&LOG, "Could not delete library override of data block '%s'", id_root->name);
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) {
+ CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name);
return;
}
+ if (id_root->override_library->hierarchy_root != nullptr) {
+ id_root = id_root->override_library->hierarchy_root;
+ }
+
+ data->id_root_set(id_root);
+}
+
+/* Resync a hierarchy of library overrides. */
+static void id_override_library_resync_hierarchy_process(bContext *C,
+ ReportList *reports,
+ OutlinerLibOverrideData &data)
+{
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ const bool do_hierarchy_enforce = data.do_resync_hierarchy_enforce;
- id_root->tag |= LIB_TAG_DOIT;
+ BlendFileReadReport report{};
+ report.reports = reports;
- /* Tag all override parents in tree hierarchy to be also processed. */
- while ((te = te->parent) != nullptr) {
- if (!TSE_IS_REAL_ID(te->store_elem)) {
- continue;
- }
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) {
- break;
- }
- te->store_elem->id->tag |= LIB_TAG_DOIT;
+ for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) {
+ BKE_lib_override_library_resync(bmain,
+ scene,
+ CTX_data_view_layer(C),
+ id_hierarchy_root,
+ nullptr,
+ do_hierarchy_enforce,
+ &report);
}
- BKE_lib_override_library_delete(bmain, id_root);
-
WM_event_add_notifier(C, NC_WINDOW, nullptr);
}
-static void id_override_library_clear_single_fn(bContext *C,
- ReportList *reports,
- Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
- TreeStoreElem *UNUSED(tsep),
- TreeStoreElem *tselem,
- void *UNUSED(user_data))
+static void id_override_library_delete_hierarchy_fn(bContext *UNUSED(C),
+ ReportList *UNUSED(reports),
+ Scene *UNUSED(scene),
+ TreeElement *UNUSED(te),
+ TreeStoreElem *UNUSED(tsep),
+ TreeStoreElem *tselem,
+ void *user_data)
{
+ OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data);
+
BLI_assert(TSE_IS_REAL_ID(tselem));
- Main *bmain = CTX_data_main(C);
- ID *id = tselem->id;
+ ID *id_root = tselem->id;
- if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
- BKE_reportf(reports,
- RPT_WARNING,
- "Cannot clear embedded library override id '%s', only overrides of real "
- "data-blocks can be directly deleted",
- id->name);
+ if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root) || ID_IS_LINKED(id_root)) {
+ CLOG_WARN(&LOG, "Could not delete library override of data block '%s'", id_root->name);
return;
}
- /* If given ID is not using any other override (it's a 'leaf' in the override hierarchy),
- * delete it and remap its usages to its linked reference. Otherwise, keep it as a reset system
- * override. */
- if (BKE_lib_override_library_is_hierarchy_leaf(bmain, id)) {
- BKE_libblock_remap(bmain, id, id->override_library->reference, ID_REMAP_SKIP_INDIRECT_USAGE);
- BKE_id_delete(bmain, id);
- }
- else {
- BKE_lib_override_library_id_reset(bmain, id, true);
+ if (id_root->override_library->hierarchy_root != nullptr) {
+ id_root = id_root->override_library->hierarchy_root;
}
- WM_event_add_notifier(C, NC_WINDOW, nullptr);
+ data->id_root_set(id_root);
+}
+
+/* Clear (delete) a hierarchy of library overrides. */
+static void id_override_library_delete_hierarchy_process(bContext *C,
+ ReportList *UNUSED(reports),
+ OutlinerLibOverrideData &data)
+{
+ Main *bmain = CTX_data_main(C);
+
+ for (auto &&id_hierarchy_root : data.id_hierarchy_roots.keys()) {
+ BKE_lib_override_library_delete(bmain, id_hierarchy_root);
+ }
}
static void id_fake_user_set_fn(bContext *UNUSED(C),
@@ -1399,6 +1631,247 @@ static void refreshdrivers_animdata_fn(int UNUSED(event),
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Library Overrides Operation Menu.
+ * \{ */
+
+enum eOutlinerLibOverrideOpTypes {
+ OUTLINER_LIBOVERRIDE_OP_INVALID = 0,
+
+ OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY,
+ OUTLINER_LIBOVERRIDE_OP_RESET,
+ OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE,
+
+ OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY,
+ OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE,
+ OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY,
+};
+
+static const EnumPropertyItem prop_liboverride_op_types[] = {
+ {OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY,
+ "OVERRIDE_LIBRARY_CREATE_HIERARCHY",
+ 0,
+ "Make",
+ "Create a local override of the selected linked data-blocks, and their hierarchy of "
+ "dependencies"},
+ {OUTLINER_LIBOVERRIDE_OP_RESET,
+ "OVERRIDE_LIBRARY_RESET",
+ 0,
+ "Reset",
+ "Reset the selected local overrides to their linked references values"},
+ {OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE,
+ "OVERRIDE_LIBRARY_CLEAR_SINGLE",
+ 0,
+ "Clear",
+ "Delete the selected local overrides and relink their usages to the linked data-blocks if "
+ "possible, else reset them and mark them as non editable"},
+ {0, nullptr, 0, nullptr, nullptr},
+};
+
+static const EnumPropertyItem prop_liboverride_troubleshoot_op_types[] = {
+ {OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY,
+ "OVERRIDE_LIBRARY_RESYNC_HIERARCHY",
+ 0,
+ "Resync",
+ "Rebuild the selected local overrides from their linked references, as well as their "
+ "hierarchies of dependencies"},
+ {OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE,
+ "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE",
+ 0,
+ "Resync Enforce",
+ "Rebuild the selected local overrides from their linked references, as well as their "
+ "hierarchies of dependencies, enforcing these hierarchies to match the linked data (i.e. "
+ "ignoring existing overrides on data-blocks pointer properties)"},
+ RNA_ENUM_ITEM_SEPR,
+ {OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY,
+ "OVERRIDE_LIBRARY_DELETE_HIERARCHY",
+ 0,
+ "Delete",
+ "Delete the selected local overrides (including their hierarchies of override dependencies) "
+ "and relink their usages to the linked data-blocks"},
+ {0, nullptr, 0, nullptr, nullptr},
+};
+
+static bool outliner_liboverride_operation_poll(bContext *C)
+{
+ if (!outliner_operation_tree_element_poll(C)) {
+ return false;
+ }
+ return true;
+}
+
+static int outliner_liboverride_operation_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+
+ /* check for invalid states */
+ if (space_outliner == nullptr) {
+ return OPERATOR_CANCELLED;
+ }
+
+ const eOutlinerLibOpSelectionSet selection_set = static_cast<eOutlinerLibOpSelectionSet>(
+ RNA_enum_get(op->ptr, "selection_set"));
+ const eOutlinerLibOverrideOpTypes event = static_cast<eOutlinerLibOverrideOpTypes>(
+ RNA_enum_get(op->ptr, "type"));
+ switch (event) {
+ case OUTLINER_LIBOVERRIDE_OP_CREATE_HIERARCHY: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ override_data.do_fully_editable = false;
+
+ outliner_do_libdata_operation_selection_set(
+ C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_create_hierarchy_pre_process_fn,
+ selection_set,
+ &override_data);
+
+ id_override_library_create_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Overridden Data Hierarchy");
+ break;
+ }
+ case OUTLINER_LIBOVERRIDE_OP_RESET: {
+ OutlinerLibOverrideData override_data{};
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_reset_fn,
+ selection_set,
+ &override_data);
+ ED_undo_push(C, "Reset Overridden Data");
+ break;
+ }
+ case OUTLINER_LIBOVERRIDE_OP_CLEAR_SINGLE: {
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_clear_single_fn,
+ selection_set,
+ nullptr);
+ ED_undo_push(C, "Clear Overridden Data");
+ break;
+ }
+
+ case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_resync_fn,
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ &override_data);
+
+ id_override_library_resync_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Resync Overridden Data Hierarchy");
+ break;
+ }
+ case OUTLINER_LIBOVERRIDE_OP_RESYNC_HIERARCHY_ENFORCE: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ override_data.do_resync_hierarchy_enforce = true;
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_resync_fn,
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ &override_data);
+
+ id_override_library_resync_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Resync Overridden Data Hierarchy Enforce");
+ break;
+ }
+ case OUTLINER_LIBOVERRIDE_OP_DELETE_HIERARCHY: {
+ OutlinerLibOverrideData override_data{};
+ override_data.do_hierarchy = true;
+ outliner_do_libdata_operation_selection_set(C,
+ op->reports,
+ scene,
+ space_outliner,
+ id_override_library_delete_hierarchy_fn,
+ OUTLINER_LIB_SELECTIONSET_SELECTED,
+ nullptr);
+
+ id_override_library_delete_hierarchy_process(C, op->reports, override_data);
+
+ ED_undo_push(C, "Delete Overridden Data Hierarchy");
+ break;
+ }
+ default:
+ /* Invalid - unhandled. */
+ break;
+ }
+
+ WM_event_add_notifier(C, NC_WINDOW, nullptr);
+ WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
+
+ return OPERATOR_FINISHED;
+}
+
+void OUTLINER_OT_liboverride_operation(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Outliner Library Override Operation";
+ ot->idname = "OUTLINER_OT_liboverride_operation";
+ ot->description = "Create, reset or clear library override hierarchies";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = outliner_liboverride_operation_exec;
+ ot->poll = outliner_liboverride_operation_poll;
+
+ ot->flag = 0;
+
+ RNA_def_enum(ot->srna, "type", prop_liboverride_op_types, 0, "Library Override Operation", "");
+ ot->prop = RNA_def_enum(ot->srna,
+ "selection_set",
+ prop_lib_op_selection_set,
+ 0,
+ "Selection Set",
+ "Over which part of the tree items to apply the operation");
+}
+
+void OUTLINER_OT_liboverride_troubleshoot_operation(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Outliner Library Override Troubleshoot Operation";
+ ot->idname = "OUTLINER_OT_liboverride_troubleshoot_operation";
+ ot->description = "Advanced operations over library override to help fix broken hierarchies";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = outliner_liboverride_operation_exec;
+ ot->poll = outliner_liboverride_operation_poll;
+
+ ot->flag = 0;
+
+ ot->prop = RNA_def_enum(ot->srna,
+ "type",
+ prop_liboverride_troubleshoot_op_types,
+ 0,
+ "Library Override Troubleshoot Operation",
+ "");
+ RNA_def_enum(ot->srna,
+ "selection_set",
+ prop_lib_op_selection_set,
+ 0,
+ "Selection Set",
+ "Over which part of the tree items to apply the operation");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Object Operation Utilities
* \{ */
@@ -1542,7 +2015,7 @@ static void data_select_linked_fn(int event,
const PointerRNA &ptr = te_rna_struct->getPointerRNA();
if (RNA_struct_is_ID(ptr.type)) {
bContext *C = (bContext *)C_v;
- ID *id = reinterpret_cast<ID *>(ptr.data);
+ ID *id = static_cast<ID *>(ptr.data);
ED_object_select_linked_by_id(C, id);
}
@@ -1551,7 +2024,7 @@ static void data_select_linked_fn(int event,
static void constraint_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v)
{
- bContext *C = reinterpret_cast<bContext *>(C_v);
+ bContext *C = static_cast<bContext *>(C_v);
Main *bmain = CTX_data_main(C);
bConstraint *constraint = (bConstraint *)te->directdata;
Object *ob = (Object *)outliner_search_back(te, ID_OB);
@@ -1640,9 +2113,10 @@ static Base *outliner_batch_delete_hierarchy(
if (!base) {
return nullptr;
}
-
+ BKE_view_layer_synced_ensure(scene, view_layer);
object = base->object;
- for (child_base = reinterpret_cast<Base *>(view_layer->object_bases.first); child_base;
+ for (child_base = static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
+ child_base;
child_base = base_next) {
base_next = child_base->next;
for (parent = child_base->object->parent; parent && (parent != object);
@@ -1692,6 +2166,7 @@ static void object_batch_delete_hierarchy_fn(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *obedit = CTX_data_edit_object(C);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base) {
@@ -1863,9 +2338,9 @@ static void outliner_do_object_delete(bContext *C,
}
}
-static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void *customdata)
+static TreeTraversalAction outliner_collect_objects_to_delete(TreeElement *te, void *customdata)
{
- ObjectEditData *data = reinterpret_cast<ObjectEditData *>(customdata);
+ ObjectEditData *data = static_cast<ObjectEditData *>(customdata);
GSet *objects_to_delete = data->objects_set;
TreeStoreElem *tselem = TREESTORE(te);
@@ -1878,6 +2353,17 @@ static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void
return TRAVERSE_SKIP_CHILDS;
}
+ /* Do not allow to delete children objects of an override collection. */
+ TreeElement *te_parent = te->parent;
+ if (outliner_is_collection_tree_element(te_parent)) {
+ TreeStoreElem *tselem_parent = TREESTORE(te_parent);
+ ID *id_parent = tselem_parent->id;
+ BLI_assert(GS(id_parent->name) == ID_GR);
+ if (ID_IS_OVERRIDE_LIBRARY_REAL(id_parent)) {
+ return TRAVERSE_SKIP_CHILDS;
+ }
+ }
+
ID *id = tselem->id;
if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
@@ -1905,7 +2391,8 @@ static int outliner_delete_exec(bContext *C, wmOperator *op)
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- const Base *basact_prev = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Base *basact_prev = BKE_view_layer_active_base_get(view_layer);
const bool delete_hierarchy = RNA_boolean_get(op->ptr, "hierarchy");
@@ -1919,7 +2406,7 @@ static int outliner_delete_exec(bContext *C, wmOperator *op)
&space_outliner->tree,
0,
TSE_SELECTED,
- outliner_find_objects_to_delete,
+ outliner_collect_objects_to_delete,
&object_delete_data);
if (delete_hierarchy) {
@@ -1949,7 +2436,8 @@ static int outliner_delete_exec(bContext *C, wmOperator *op)
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(bmain);
- if (basact_prev != BASACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (basact_prev != BKE_view_layer_active_base_get(view_layer)) {
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
WM_msg_publish_rna_prop(mbus, &scene->id, view_layer, LayerObjects, active);
}
@@ -1993,15 +2481,6 @@ enum eOutlinerIdOpTypes {
OUTLINER_IDOP_UNLINK,
OUTLINER_IDOP_LOCAL,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY,
- OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE,
OUTLINER_IDOP_SINGLE,
OUTLINER_IDOP_DELETE,
OUTLINER_IDOP_REMAP,
@@ -2028,59 +2507,6 @@ static const EnumPropertyItem prop_id_op_types[] = {
"Remap Users",
"Make all users of selected data-blocks to use instead current (clicked) one"},
RNA_ENUM_ITEM_SEPR,
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
- "OVERRIDE_LIBRARY_CREATE",
- 0,
- "Make Library Override Single",
- "Make a single, out-of-hierarchy local override of this linked data-block - only applies to "
- "active Outliner item"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
- "OVERRIDE_LIBRARY_CREATE_HIERARCHY",
- 0,
- "Make Library Override Hierarchy",
- "Make a local override of this linked data-block, and its hierarchy of dependencies - only "
- "applies to active Outliner item"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE,
- "OVERRIDE_LIBRARY_MAKE_EDITABLE",
- 0,
- "Make Library Override Editable",
- "Make the library override data-block editable"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET,
- "OVERRIDE_LIBRARY_RESET",
- 0,
- "Reset Library Override Single",
- "Reset this local override to its linked values"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY,
- "OVERRIDE_LIBRARY_RESET_HIERARCHY",
- 0,
- "Reset Library Override Hierarchy",
- "Reset this local override to its linked values, as well as its hierarchy of dependencies"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY,
- "OVERRIDE_LIBRARY_RESYNC_HIERARCHY",
- 0,
- "Resync Library Override Hierarchy",
- "Rebuild this local override from its linked reference, as well as its hierarchy of "
- "dependencies"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE,
- "OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE",
- 0,
- "Resync Library Override Hierarchy Enforce",
- "Rebuild this local override from its linked reference, as well as its hierarchy of "
- "dependencies, enforcing that hierarchy to match the linked data (i.e. ignoring exiting "
- "overrides on data-blocks pointer properties)"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE,
- "OVERRIDE_LIBRARY_CLEAR_SINGLE",
- 0,
- "Clear Library Override Single",
- "Delete this local override and relink its usages to the linked data-blocks if possible, "
- "else reset it and mark it as non editable"},
- {OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY,
- "OVERRIDE_LIBRARY_CLEAR_HIERARCHY",
- 0,
- "Clear Library Override Hierarchy",
- "Delete this local override (including its hierarchy of override dependencies) and relink "
- "its usages to the linked data-blocks"},
- RNA_ENUM_ITEM_SEPR,
{OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""},
{OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""},
RNA_ENUM_ITEM_SEPR,
@@ -2113,33 +2539,6 @@ static bool outliner_id_operation_item_poll(bContext *C,
}
switch (enum_value) {
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE:
- if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) {
- return true;
- }
- return false;
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY:
- if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id) || (ID_IS_LINKED(tselem->id))) {
- return true;
- }
- return false;
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE:
- if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) {
- if (tselem->id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED) {
- return true;
- }
- }
- return false;
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY:
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE:
- if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id) && !ID_IS_LINKED(tselem->id)) {
- return true;
- }
- return false;
case OUTLINER_IDOP_SINGLE:
if (ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) {
return true;
@@ -2252,85 +2651,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
ED_undo_push(C, "Localized Data");
break;
}
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: {
- OutlinerLibOverrideData override_data{};
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
- ED_undo_push(C, "Overridden Data");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
- override_data.do_fully_editable = U.experimental.use_override_new_fully_editable;
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- id_override_library_create_hierarchy_pre_process_fn,
- &override_data);
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_create_fn, &override_data);
- id_override_library_create_hierarchy_post_process(C, &override_data);
-
- ED_undo_push(C, "Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_MAKE_EDITABLE: {
- outliner_do_libdata_operation(C,
- op->reports,
- scene,
- space_outliner,
- id_override_library_toggle_flag_fn,
- POINTER_FROM_UINT(IDOVERRIDE_LIBRARY_FLAG_SYSTEM_DEFINED));
-
- ED_undo_push(C, "Make Overridden Data Editable");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: {
- OutlinerLibOverrideData override_data{};
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data);
- ED_undo_push(C, "Reset Overridden Data");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_reset_fn, &override_data);
- ED_undo_push(C, "Reset Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data);
- ED_undo_push(C, "Resync Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY_ENFORCE: {
- OutlinerLibOverrideData override_data{};
- override_data.do_hierarchy = true;
- override_data.do_resync_hierarchy_enforce = true;
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_resync_fn, &override_data);
- ED_undo_push(C, "Resync Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_HIERARCHY: {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_clear_hierarchy_fn, nullptr);
- ED_undo_push(C, "Clear Overridden Data Hierarchy");
- break;
- }
- case OUTLINER_IDOP_OVERRIDE_LIBRARY_CLEAR_SINGLE: {
- outliner_do_libdata_operation(
- C, op->reports, scene, space_outliner, id_override_library_clear_single_fn, nullptr);
- ED_undo_push(C, "Clear Overridden Data Hierarchy");
- break;
- }
case OUTLINER_IDOP_SINGLE: {
/* make single user */
switch (idlevel) {
@@ -2442,6 +2762,7 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot)
/* identifiers */
ot->name = "Outliner ID Data Operation";
ot->idname = "OUTLINER_OT_id_operation";
+ ot->description = "General data-block management operations";
/* callbacks */
ot->invoke = WM_menu_invoke;
@@ -2489,16 +2810,12 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
- int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
/* check for invalid states */
if (space_outliner == nullptr) {
return OPERATOR_CANCELLED;
}
- TreeElement *te = get_target_element(space_outliner);
- get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
-
eOutlinerLibOpTypes event = (eOutlinerLibOpTypes)RNA_enum_get(op->ptr, "type");
switch (event) {
case OL_LIB_DELETE: {
@@ -2606,8 +2923,7 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op)
get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
/* get action to use */
- act = reinterpret_cast<bAction *>(
- BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action")));
+ act = static_cast<bAction *>(BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action")));
if (act == nullptr) {
BKE_report(op->reports, RPT_ERROR, "No valid action to add");
@@ -2878,6 +3194,24 @@ void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
/** \name Data Menu Operator
* \{ */
+static bool outliner_data_operation_poll(bContext *C)
+{
+ if (!ED_operator_outliner_active(C)) {
+ return false;
+ }
+ const SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+ const TreeElement *te = get_target_element(space_outliner);
+ int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
+ get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
+ return ELEM(datalevel,
+ TSE_POSE_CHANNEL,
+ TSE_BONE,
+ TSE_EBONE,
+ TSE_SEQUENCE,
+ TSE_GP_LAYER,
+ TSE_RNA_STRUCT);
+}
+
static int outliner_data_operation_exec(bContext *C, wmOperator *op)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -2988,7 +3322,7 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot)
/* callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = outliner_data_operation_exec;
- ot->poll = outliner_operation_tree_element_poll;
+ ot->poll = outliner_data_operation_poll;
ot->flag = 0;
@@ -3010,9 +3344,12 @@ static int outliner_operator_menu(bContext *C, const char *opname)
/* set this so the default execution context is the same as submenus */
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
- uiItemsEnumO(layout, ot->idname, RNA_property_identifier(ot->prop));
- uiItemS(layout);
+ if (WM_operator_poll(C, ot)) {
+ uiItemsEnumO(layout, ot->idname, RNA_property_identifier(ot->prop));
+
+ uiItemS(layout);
+ }
uiItemMContents(layout, "OUTLINER_MT_context_menu");
@@ -3022,7 +3359,6 @@ static int outliner_operator_menu(bContext *C, const char *opname)
}
static int do_outliner_operation_event(bContext *C,
- ReportList *reports,
ARegion *region,
SpaceOutliner *space_outliner,
TreeElement *te)
@@ -3045,10 +3381,6 @@ static int do_outliner_operation_event(bContext *C,
get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel);
if (scenelevel) {
- if (objectlevel || datalevel || idlevel) {
- BKE_report(reports, RPT_WARNING, "Mixed selection");
- return OPERATOR_CANCELLED;
- }
return outliner_operator_menu(C, "OUTLINER_OT_scene_operation");
}
if (objectlevel) {
@@ -3056,11 +3388,6 @@ static int do_outliner_operation_event(bContext *C,
return OPERATOR_FINISHED;
}
if (idlevel) {
- if (idlevel == -1 || datalevel) {
- BKE_report(reports, RPT_WARNING, "Mixed selection");
- return OPERATOR_CANCELLED;
- }
-
switch (idlevel) {
case ID_GR:
WM_menu_name_call(C, "OUTLINER_MT_collection", WM_OP_INVOKE_REGION_WIN);
@@ -3075,10 +3402,6 @@ static int do_outliner_operation_event(bContext *C,
}
}
else if (datalevel) {
- if (datalevel == -1) {
- BKE_report(reports, RPT_WARNING, "Mixed selection");
- return OPERATOR_CANCELLED;
- }
if (datalevel == TSE_ANIM_DATA) {
return outliner_operator_menu(C, "OUTLINER_OT_animdata_operation");
}
@@ -3110,7 +3433,7 @@ static int do_outliner_operation_event(bContext *C,
return OPERATOR_CANCELLED;
}
-static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event)
+static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
{
ARegion *region = CTX_wm_region(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -3131,7 +3454,7 @@ static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_PASS_THROUGH;
}
- return do_outliner_operation_event(C, op->reports, region, space_outliner, hovered_te);
+ return do_outliner_operation_event(C, region, space_outliner, hovered_te);
}
void OUTLINER_OT_operation(wmOperatorType *ot)
@@ -3146,3 +3469,5 @@ void OUTLINER_OT_operation(wmOperatorType *ot)
}
/** \} */
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc
index aa739758ecb..8a2ff8c2ece 100644
--- a/source/blender/editors/space_outliner/outliner_tree.cc
+++ b/source/blender/editors/space_outliner/outliner_tree.cc
@@ -41,6 +41,7 @@
#include "BLI_fnmatch.h"
#include "BLI_listbase.h"
#include "BLI_mempool.h"
+#include "BLI_timeit.hh"
#include "BLI_utildefines.h"
#include "BLT_translation.h"
@@ -51,7 +52,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
-#include "BKE_outliner_treehash.h"
+#include "BKE_outliner_treehash.hh"
#include "ED_screen.h"
@@ -69,7 +70,7 @@
# include "BLI_math_base.h" /* M_PI */
#endif
-using namespace blender::ed::outliner;
+namespace blender::ed::outliner {
/* prototypes */
static int outliner_exclude_filter_get(const SpaceOutliner *space_outliner);
@@ -90,7 +91,7 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner)
BLI_mempool_iter iter;
BLI_mempool_iternew(ts, &iter);
- while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
+ while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
tselem->used = 0;
}
@@ -100,7 +101,7 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner)
space_outliner->storeflag &= ~SO_TREESTORE_CLEANUP;
BLI_mempool_iternew(ts, &iter);
- while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
+ while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
if (tselem->id == nullptr) {
unused++;
}
@@ -110,34 +111,30 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner)
if (BLI_mempool_len(ts) == unused) {
BLI_mempool_destroy(ts);
space_outliner->treestore = nullptr;
- if (space_outliner->runtime->treehash) {
- BKE_outliner_treehash_free(space_outliner->runtime->treehash);
- space_outliner->runtime->treehash = nullptr;
- }
+ space_outliner->runtime->tree_hash = nullptr;
}
else {
TreeStoreElem *tsenew;
BLI_mempool *new_ts = BLI_mempool_create(
sizeof(TreeStoreElem), BLI_mempool_len(ts) - unused, 512, BLI_MEMPOOL_ALLOW_ITER);
BLI_mempool_iternew(ts, &iter);
- while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
+ while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) {
if (tselem->id) {
- tsenew = reinterpret_cast<TreeStoreElem *>(BLI_mempool_alloc(new_ts));
+ tsenew = static_cast<TreeStoreElem *>(BLI_mempool_alloc(new_ts));
*tsenew = *tselem;
}
}
BLI_mempool_destroy(ts);
space_outliner->treestore = new_ts;
- if (space_outliner->runtime->treehash) {
+ if (space_outliner->runtime->tree_hash) {
/* update hash table to fix broken pointers */
- BKE_outliner_treehash_rebuild_from_treestore(space_outliner->runtime->treehash,
- space_outliner->treestore);
+ space_outliner->runtime->tree_hash->rebuild_from_treestore(*space_outliner->treestore);
}
}
}
}
- else if (space_outliner->runtime->treehash) {
- BKE_outliner_treehash_clear_used(space_outliner->runtime->treehash);
+ else if (space_outliner->runtime->tree_hash) {
+ space_outliner->runtime->tree_hash->clear_used();
}
}
}
@@ -150,15 +147,14 @@ static void check_persistent(
space_outliner->treestore = BLI_mempool_create(
sizeof(TreeStoreElem), 1, 512, BLI_MEMPOOL_ALLOW_ITER);
}
- if (space_outliner->runtime->treehash == nullptr) {
- space_outliner->runtime->treehash = reinterpret_cast<GHash *>(
- BKE_outliner_treehash_create_from_treestore(space_outliner->treestore));
+ if (space_outliner->runtime->tree_hash == nullptr) {
+ space_outliner->runtime->tree_hash = treehash::TreeHash::create_from_treestore(
+ *space_outliner->treestore);
}
/* find any unused tree element in treestore and mark it as used
* (note that there may be multiple unused elements in case of linked objects) */
- TreeStoreElem *tselem = BKE_outliner_treehash_lookup_unused(
- space_outliner->runtime->treehash, type, nr, id);
+ TreeStoreElem *tselem = space_outliner->runtime->tree_hash->lookup_unused(type, nr, id);
if (tselem) {
te->store_elem = tselem;
tselem->used = 1;
@@ -166,14 +162,14 @@ static void check_persistent(
}
/* add 1 element to treestore */
- tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_alloc(space_outliner->treestore));
+ tselem = static_cast<TreeStoreElem *>(BLI_mempool_alloc(space_outliner->treestore));
tselem->type = type;
tselem->nr = type ? nr : 0;
tselem->id = id;
tselem->used = 0;
tselem->flag = TSE_CLOSED;
te->store_elem = tselem;
- BKE_outliner_treehash_add_element(space_outliner->runtime->treehash, tselem);
+ space_outliner->runtime->tree_hash->add_element(*tselem);
}
/** \} */
@@ -221,11 +217,6 @@ bool outliner_requires_rebuild_on_select_or_active_change(const SpaceOutliner *s
return exclude_flags & (SO_FILTER_OB_STATE_SELECTED | SO_FILTER_OB_STATE_ACTIVE);
}
-bool outliner_requires_rebuild_on_open_change(const SpaceOutliner *space_outliner)
-{
- return ELEM(space_outliner->outlinevis, SO_DATA_API);
-}
-
/* special handling of hierarchical non-lib data */
static void outliner_add_bone(SpaceOutliner *space_outliner,
ListBase *lb,
@@ -293,7 +284,7 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner,
outliner_add_element(space_outliner, &te->subtree, ob->data, te, TSE_SOME_ID, 0);
if (ob->pose) {
- bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
+ bArmature *arm = static_cast<bArmature *>(ob->data);
TreeElement *tenla = outliner_add_element(
space_outliner, &te->subtree, ob, te, TSE_POSE_BASE, 0);
tenla->name = IFACE_("Pose");
@@ -339,7 +330,7 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner,
}
}
/* make hierarchy */
- TreeElement *ten = reinterpret_cast<TreeElement *>(tenla->subtree.first);
+ TreeElement *ten = static_cast<TreeElement *>(tenla->subtree.first);
while (ten) {
TreeElement *nten = ten->next, *par;
tselem = TREESTORE(ten);
@@ -694,15 +685,15 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
ebone->temp.p = ten;
}
/* make hierarchy */
- TreeElement *ten = arm->edbo->first ? reinterpret_cast<TreeElement *>(
- ((EditBone *)arm->edbo->first)->temp.p) :
- nullptr;
+ TreeElement *ten = arm->edbo->first ?
+ static_cast<TreeElement *>(((EditBone *)arm->edbo->first)->temp.p) :
+ nullptr;
while (ten) {
TreeElement *nten = ten->next, *par;
EditBone *ebone = (EditBone *)ten->directdata;
if (ebone->parent) {
BLI_remlink(&te->subtree, ten);
- par = reinterpret_cast<TreeElement *>(ebone->parent->temp.p);
+ par = static_cast<TreeElement *>(ebone->parent->temp.p);
BLI_addtail(&par->subtree, ten);
ten->parent = par;
}
@@ -795,8 +786,6 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
}
}
-namespace blender::ed::outliner {
-
TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
ListBase *lb,
void *idv,
@@ -805,21 +794,24 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
short index,
const bool expand)
{
- ID *id = reinterpret_cast<ID *>(idv);
+ ID *id = static_cast<ID *>(idv);
if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
id = ((PointerRNA *)idv)->owner_id;
if (!id) {
- id = reinterpret_cast<ID *>(((PointerRNA *)idv)->data);
+ id = static_cast<ID *>(((PointerRNA *)idv)->data);
}
}
else if (type == TSE_GP_LAYER) {
/* idv is the layer itself */
id = TREESTORE(parent)->id;
}
+ else if (ELEM(type, TSE_GENERIC_LABEL)) {
+ id = nullptr;
+ }
/* exceptions */
- if (type == TSE_ID_BASE) {
+ if (ELEM(type, TSE_ID_BASE, TSE_GENERIC_LABEL)) {
/* pass */
}
else if (id == nullptr) {
@@ -869,7 +861,7 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
else if (ELEM(type, TSE_LAYER_COLLECTION, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) {
/* pass */
}
- else if (type == TSE_ID_BASE) {
+ else if (ELEM(type, TSE_ID_BASE, TSE_GENERIC_LABEL)) {
/* pass */
}
else if (type == TSE_SOME_ID) {
@@ -877,7 +869,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
BLI_assert_msg(0, "Expected this ID type to be ported to new Outliner tree-element design");
}
}
- else if (ELEM(type, TSE_LIBRARY_OVERRIDE_BASE, TSE_LIBRARY_OVERRIDE)) {
+ else if (ELEM(type,
+ TSE_LIBRARY_OVERRIDE_BASE,
+ TSE_LIBRARY_OVERRIDE,
+ TSE_LIBRARY_OVERRIDE_OPERATION)) {
if (!te->abstract_element) {
BLI_assert_msg(0,
"Expected override types to be ported to new Outliner tree-element design");
@@ -895,10 +890,13 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
te->idcode = GS(id->name);
}
- if (expand && te->abstract_element && te->abstract_element->isExpandValid()) {
+ if (!expand) {
+ /* Pass */
+ }
+ else if (te->abstract_element && te->abstract_element->isExpandValid()) {
tree_element_expand(*te->abstract_element, *space_outliner);
}
- else if (expand && (type == TSE_SOME_ID)) {
+ else if (type == TSE_SOME_ID) {
/* ID types not (fully) ported to new design yet. */
if (te->abstract_element->expandPoll(*space_outliner)) {
outliner_add_id_contents(space_outliner, te, tselem, id);
@@ -916,15 +914,14 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
TSE_RNA_ARRAY_ELEM,
TSE_SEQUENCE,
TSE_SEQ_STRIP,
- TSE_SEQUENCE_DUP)) {
+ TSE_SEQUENCE_DUP,
+ TSE_GENERIC_LABEL)) {
BLI_assert_msg(false, "Element type should already use new AbstractTreeElement design");
}
return te;
}
-} // namespace blender::ed::outliner
-
/* ======================================================= */
BLI_INLINE void outliner_add_collection_init(TreeElement *te, Collection *collection)
@@ -980,8 +977,8 @@ struct tTreeSort {
/* alphabetical comparator, trying to put objects first */
static int treesort_alpha_ob(const void *v1, const void *v2)
{
- const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1);
- const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2);
+ const tTreeSort *x1 = static_cast<const tTreeSort *>(v1);
+ const tTreeSort *x2 = static_cast<const tTreeSort *>(v2);
/* first put objects last (hierarchy) */
int comp = (x1->idcode == ID_OB);
@@ -1019,8 +1016,8 @@ static int treesort_alpha_ob(const void *v1, const void *v2)
/* Move children that are not in the collection to the end of the list. */
static int treesort_child_not_in_collection(const void *v1, const void *v2)
{
- const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1);
- const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2);
+ const tTreeSort *x1 = static_cast<const tTreeSort *>(v1);
+ const tTreeSort *x2 = static_cast<const tTreeSort *>(v2);
/* Among objects first come the ones in the collection, followed by the ones not on it.
* This way we can have the dashed lines in a separate style connecting the former. */
@@ -1033,8 +1030,8 @@ static int treesort_child_not_in_collection(const void *v1, const void *v2)
/* alphabetical comparator */
static int treesort_alpha(const void *v1, const void *v2)
{
- const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1);
- const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2);
+ const tTreeSort *x1 = static_cast<const tTreeSort *>(v1);
+ const tTreeSort *x2 = static_cast<const tTreeSort *>(v2);
int comp = BLI_strcasecmp_natural(x1->name, x2->name);
@@ -1091,7 +1088,7 @@ static int treesort_obtype_alpha(const void *v1, const void *v2)
/* sort happens on each subtree individual */
static void outliner_sort(ListBase *lb)
{
- TreeElement *last_te = reinterpret_cast<TreeElement *>(lb->last);
+ TreeElement *last_te = static_cast<TreeElement *>(lb->last);
if (last_te == nullptr) {
return;
}
@@ -1103,7 +1100,7 @@ static void outliner_sort(ListBase *lb)
int totelem = BLI_listbase_count(lb);
if (totelem > 1) {
- tTreeSort *tear = reinterpret_cast<tTreeSort *>(
+ tTreeSort *tear = static_cast<tTreeSort *>(
MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array"));
tTreeSort *tp = tear;
int skip = 0;
@@ -1159,7 +1156,7 @@ static void outliner_sort(ListBase *lb)
static void outliner_collections_children_sort(ListBase *lb)
{
- TreeElement *last_te = reinterpret_cast<TreeElement *>(lb->last);
+ TreeElement *last_te = static_cast<TreeElement *>(lb->last);
if (last_te == nullptr) {
return;
}
@@ -1170,7 +1167,7 @@ static void outliner_collections_children_sort(ListBase *lb)
int totelem = BLI_listbase_count(lb);
if (totelem > 1) {
- tTreeSort *tear = reinterpret_cast<tTreeSort *>(
+ tTreeSort *tear = static_cast<tTreeSort *>(
MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array"));
tTreeSort *tp = tear;
@@ -1398,7 +1395,8 @@ static int outliner_exclude_filter_get(const SpaceOutliner *space_outliner)
return exclude_filter;
}
-static bool outliner_element_visible_get(ViewLayer *view_layer,
+static bool outliner_element_visible_get(const Scene *scene,
+ ViewLayer *view_layer,
TreeElement *te,
const int exclude_filter)
{
@@ -1453,6 +1451,7 @@ static bool outliner_element_visible_get(ViewLayer *view_layer,
if (exclude_filter & SO_FILTER_OB_STATE) {
if (base == nullptr) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
base = BKE_view_layer_base_find(view_layer, ob);
if (base == nullptr) {
@@ -1462,7 +1461,7 @@ static bool outliner_element_visible_get(ViewLayer *view_layer,
bool is_visible = true;
if (exclude_filter & SO_FILTER_OB_STATE_VISIBLE) {
- if ((base->flag & BASE_VISIBLE_VIEWLAYER) == 0) {
+ if ((base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) == 0) {
is_visible = false;
}
}
@@ -1478,7 +1477,8 @@ static bool outliner_element_visible_get(ViewLayer *view_layer,
}
else {
BLI_assert(exclude_filter & SO_FILTER_OB_STATE_ACTIVE);
- if (base != BASACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (base != BKE_view_layer_active_base_get(view_layer)) {
is_visible = false;
}
}
@@ -1541,8 +1541,7 @@ static TreeElement *outliner_extract_children_from_subtree(TreeElement *element,
if (outliner_element_is_collection_or_object(element)) {
TreeElement *te_prev = nullptr;
- for (TreeElement *te = reinterpret_cast<TreeElement *>(element->subtree.last); te;
- te = te_prev) {
+ for (TreeElement *te = static_cast<TreeElement *>(element->subtree.last); te; te = te_prev) {
te_prev = te->prev;
if (!outliner_element_is_collection_or_object(te)) {
@@ -1561,6 +1560,7 @@ static TreeElement *outliner_extract_children_from_subtree(TreeElement *element,
}
static int outliner_filter_subtree(SpaceOutliner *space_outliner,
+ const Scene *scene,
ViewLayer *view_layer,
ListBase *lb,
const char *search_string,
@@ -1569,20 +1569,20 @@ static int outliner_filter_subtree(SpaceOutliner *space_outliner,
TreeElement *te, *te_next;
TreeStoreElem *tselem;
- for (te = reinterpret_cast<TreeElement *>(lb->first); te; te = te_next) {
+ for (te = static_cast<TreeElement *>(lb->first); te; te = te_next) {
te_next = te->next;
- if ((outliner_element_visible_get(view_layer, te, exclude_filter) == false)) {
+ if ((outliner_element_visible_get(scene, view_layer, te, exclude_filter) == false)) {
/* Don't free the tree, but extract the children from the parent and add to this tree. */
/* This also needs filtering the subtree prior (see T69246). */
outliner_filter_subtree(
- space_outliner, view_layer, &te->subtree, search_string, exclude_filter);
+ space_outliner, scene, view_layer, &te->subtree, search_string, exclude_filter);
te_next = outliner_extract_children_from_subtree(te, lb);
continue;
}
if ((exclude_filter & SO_FILTER_SEARCH) == 0) {
/* Filter subtree too. */
outliner_filter_subtree(
- space_outliner, view_layer, &te->subtree, search_string, exclude_filter);
+ space_outliner, scene, view_layer, &te->subtree, search_string, exclude_filter);
continue;
}
@@ -1600,7 +1600,8 @@ static int outliner_filter_subtree(SpaceOutliner *space_outliner,
if ((!TSELEM_OPEN(tselem, space_outliner)) ||
outliner_filter_subtree(
- space_outliner, view_layer, &te->subtree, search_string, exclude_filter) == 0) {
+ space_outliner, scene, view_layer, &te->subtree, search_string, exclude_filter) ==
+ 0) {
outliner_free_tree_element(te, lb);
}
}
@@ -1612,7 +1613,7 @@ static int outliner_filter_subtree(SpaceOutliner *space_outliner,
/* filter subtree too */
outliner_filter_subtree(
- space_outliner, view_layer, &te->subtree, search_string, exclude_filter);
+ space_outliner, scene, view_layer, &te->subtree, search_string, exclude_filter);
}
}
@@ -1620,7 +1621,9 @@ static int outliner_filter_subtree(SpaceOutliner *space_outliner,
return (BLI_listbase_is_empty(lb) == false);
}
-static void outliner_filter_tree(SpaceOutliner *space_outliner, ViewLayer *view_layer)
+static void outliner_filter_tree(SpaceOutliner *space_outliner,
+ const Scene *scene,
+ ViewLayer *view_layer)
{
char search_buff[sizeof(((struct SpaceOutliner *)nullptr)->search_string) + 2];
char *search_string;
@@ -1641,7 +1644,7 @@ static void outliner_filter_tree(SpaceOutliner *space_outliner, ViewLayer *view_
}
outliner_filter_subtree(
- space_outliner, view_layer, &space_outliner->tree, search_string, exclude_filter);
+ space_outliner, scene, view_layer, &space_outliner->tree, search_string, exclude_filter);
}
static void outliner_clear_newid_from_main(Main *bmain)
@@ -1675,10 +1678,9 @@ void outliner_build_tree(Main *mainvar,
space_outliner->search_flags &= ~SO_SEARCH_RECURSIVE;
}
- if (space_outliner->runtime->treehash && (space_outliner->storeflag & SO_TREESTORE_REBUILD) &&
+ if (space_outliner->runtime->tree_hash && (space_outliner->storeflag & SO_TREESTORE_REBUILD) &&
space_outliner->treestore) {
- BKE_outliner_treehash_rebuild_from_treestore(space_outliner->runtime->treehash,
- space_outliner->treestore);
+ space_outliner->runtime->tree_hash->rebuild_from_treestore(*space_outliner->treestore);
}
space_outliner->storeflag &= ~SO_TREESTORE_REBUILD;
@@ -1689,6 +1691,10 @@ void outliner_build_tree(Main *mainvar,
return;
}
+ /* Enable for benchmarking. Starts a timer, results will be printed on function exit. */
+ // SCOPED_TIMER("Outliner Rebuild");
+ // SCOPED_TIMER_AVERAGED("Outliner Rebuild");
+
OutlinerTreeElementFocus focus;
outliner_store_scrolling_position(space_outliner, region, &focus);
@@ -1715,7 +1721,7 @@ void outliner_build_tree(Main *mainvar,
outliner_collections_children_sort(&space_outliner->tree);
}
- outliner_filter_tree(space_outliner, view_layer);
+ outliner_filter_tree(space_outliner, scene, view_layer);
outliner_restore_scrolling_position(space_outliner, region, &focus);
/* `ID.newid` pointer is abused when building tree, DO NOT call #BKE_main_id_newptr_and_tag_clear
@@ -1724,3 +1730,5 @@ void outliner_build_tree(Main *mainvar,
}
/** \} */
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc
index 0db612ce6db..2deedccc29e 100644
--- a/source/blender/editors/space_outliner/outliner_utils.cc
+++ b/source/blender/editors/space_outliner/outliner_utils.cc
@@ -18,7 +18,7 @@
#include "BKE_context.h"
#include "BKE_layer.h"
#include "BKE_object.h"
-#include "BKE_outliner_treehash.h"
+#include "BKE_outliner_treehash.hh"
#include "ED_outliner.h"
#include "ED_screen.h"
@@ -27,9 +27,10 @@
#include "UI_view2d.h"
#include "outliner_intern.hh"
+#include "tree/tree_display.hh"
#include "tree/tree_iterator.hh"
-using namespace blender::ed::outliner;
+namespace blender::ed::outliner {
/* -------------------------------------------------------------------- */
/** \name Tree View Context
@@ -44,7 +45,8 @@ void outliner_viewcontext_init(const bContext *C, TreeViewContext *tvc)
tvc->view_layer = CTX_data_view_layer(C);
/* Objects. */
- tvc->obact = OBACT(tvc->view_layer);
+ BKE_view_layer_synced_ensure(tvc->scene, tvc->view_layer);
+ tvc->obact = BKE_view_layer_active_object_get(tvc->view_layer);
if (tvc->obact != nullptr) {
tvc->ob_edit = OBEDIT_FROM_OBACT(tvc->obact);
@@ -98,7 +100,7 @@ static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement *
float view_co_x,
bool *r_is_merged_icon)
{
- TreeElement *child_te = reinterpret_cast<TreeElement *>(parent_te->subtree.first);
+ TreeElement *child_te = static_cast<TreeElement *>(parent_te->subtree.first);
while (child_te) {
const bool over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend);
@@ -175,24 +177,6 @@ TreeElement *outliner_find_parent_element(ListBase *lb,
return nullptr;
}
-TreeElement *outliner_find_tse(SpaceOutliner *space_outliner, const TreeStoreElem *tse)
-{
- TreeStoreElem *tselem;
-
- if (tse->id == nullptr) {
- return nullptr;
- }
-
- /* Check if 'tse' is in tree-store. */
- tselem = BKE_outliner_treehash_lookup_any(
- space_outliner->runtime->treehash, tse->type, tse->nr, tse->id);
- if (tselem) {
- return outliner_find_tree_element(&space_outliner->tree, tselem);
- }
-
- return nullptr;
-}
-
TreeElement *outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const ID *id)
{
LISTBASE_FOREACH (TreeElement *, te, lb) {
@@ -282,8 +266,7 @@ bool outliner_tree_traverse(const SpaceOutliner *space_outliner,
TreeTraversalFunc func,
void *customdata)
{
- for (TreeElement *te = reinterpret_cast<TreeElement *>(tree->first), *te_next; te;
- te = te_next) {
+ for (TreeElement *te = static_cast<TreeElement *>(tree->first), *te_next; te; te = te_next) {
TreeTraversalAction func_retval = TRAVERSE_CONTINUE;
/* in case te is freed in callback */
TreeStoreElem *tselem = TREESTORE(te);
@@ -455,7 +438,7 @@ void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space
ARegion *region)
{
/* Avoid rebuild if possible. */
- if (outliner_requires_rebuild_on_open_change(space_outliner)) {
+ if (space_outliner->runtime->tree_display->is_lazy_built()) {
ED_region_tag_redraw(region);
}
else {
@@ -463,9 +446,14 @@ void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space
}
}
+} // namespace blender::ed::outliner
+
+using namespace blender::ed::outliner;
+
Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2])
{
ARegion *region = CTX_wm_region(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
TreeElement *te;
@@ -479,6 +467,7 @@ Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2])
TreeStoreElem *tselem = TREESTORE(te);
if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
Object *ob = (Object *)tselem->id;
+ BKE_view_layer_synced_ensure(scene, view_layer);
base = (te->directdata) ? (Base *)te->directdata : BKE_view_layer_base_find(view_layer, ob);
}
}
diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc
index 5bcd1edebc0..365bcae3f5d 100644
--- a/source/blender/editors/space_outliner/space_outliner.cc
+++ b/source/blender/editors/space_outliner/space_outliner.cc
@@ -16,7 +16,7 @@
#include "BKE_context.h"
#include "BKE_lib_remap.h"
-#include "BKE_outliner_treehash.h"
+#include "BKE_outliner_treehash.hh"
#include "BKE_screen.h"
#include "ED_screen.h"
@@ -37,16 +37,11 @@
#include "outliner_intern.hh"
#include "tree/tree_display.hh"
-SpaceOutliner_Runtime::SpaceOutliner_Runtime(const SpaceOutliner_Runtime & /*other*/)
- : tree_display(nullptr), treehash(nullptr)
-{
-}
+namespace blender::ed::outliner {
-SpaceOutliner_Runtime::~SpaceOutliner_Runtime()
+SpaceOutliner_Runtime::SpaceOutliner_Runtime(const SpaceOutliner_Runtime & /*other*/)
+ : tree_display(nullptr), tree_hash(nullptr)
{
- if (treehash) {
- BKE_outliner_treehash_free(treehash);
- }
}
static void outliner_main_region_init(wmWindowManager *wm, ARegion *region)
@@ -100,8 +95,8 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params)
{
ScrArea *area = params->area;
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
- SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first);
+ const wmNotifier *wmn = params->notifier;
+ SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first);
/* context changes */
switch (wmn->category) {
@@ -191,7 +186,7 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params)
}
break;
case NC_ID:
- if (ELEM(wmn->action, NA_RENAME, NA_ADDED)) {
+ if (ELEM(wmn->action, NA_RENAME, NA_ADDED, NA_REMOVED)) {
ED_region_tag_redraw(region);
}
break;
@@ -264,7 +259,7 @@ static void outliner_main_region_message_subscribe(const wmRegionMessageSubscrib
struct wmMsgBus *mbus = params->message_bus;
ScrArea *area = params->area;
ARegion *region = params->region;
- SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first);
+ SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first);
wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
msg_sub_value_region_tag_redraw.owner = region;
@@ -296,7 +291,7 @@ static void outliner_header_region_free(ARegion *UNUSED(region))
static void outliner_header_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -361,7 +356,7 @@ static void outliner_free(SpaceLink *sl)
/* spacetype; init callback */
static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *area)
{
- SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first);
+ SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first);
if (space_outliner->runtime == nullptr) {
space_outliner->runtime = MEM_new<SpaceOutliner_Runtime>("SpaceOutliner_Runtime");
@@ -391,8 +386,6 @@ static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRe
{
SpaceOutliner *space_outliner = (SpaceOutliner *)slink;
- BKE_id_remapper_apply(mappings, (ID **)&space_outliner->search_tse.id, ID_REMAP_APPLY_DEFAULT);
-
if (!space_outliner->treestore) {
return;
}
@@ -420,7 +413,7 @@ static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRe
/* Note that the Outliner may not be the active editor of the area, and hence not initialized.
* So runtime data might not have been created yet. */
- if (space_outliner->runtime && space_outliner->runtime->treehash && changed) {
+ if (space_outliner->runtime && space_outliner->runtime->tree_hash && changed) {
/* rebuild hash table, because it depends on ids too */
/* postpone a full rebuild because this can be called many times on-free */
space_outliner->storeflag |= SO_TREESTORE_REBUILD;
@@ -437,18 +430,22 @@ static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRe
static void outliner_deactivate(struct ScrArea *area)
{
/* Remove hover highlights */
- SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first);
+ SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first);
outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY, false);
ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW));
}
+} // namespace blender::ed::outliner
+
void ED_spacetype_outliner(void)
{
+ using namespace blender::ed::outliner;
+
SpaceType *st = MEM_cnew<SpaceType>("spacetype time");
ARegionType *art;
st->spaceid = SPACE_OUTLINER;
- strncpy(st->name, "Outliner", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Outliner");
st->create = outliner_create;
st->free = outliner_free;
diff --git a/source/blender/editors/space_outliner/tree/common.cc b/source/blender/editors/space_outliner/tree/common.cc
index 349d36e2fe6..199c80f021a 100644
--- a/source/blender/editors/space_outliner/tree/common.cc
+++ b/source/blender/editors/space_outliner/tree/common.cc
@@ -21,6 +21,8 @@
#include "common.hh"
#include "tree_display.hh"
+namespace blender::ed::outliner {
+
/* -------------------------------------------------------------------- */
/** \name ID Helpers.
* \{ */
@@ -38,7 +40,7 @@ void outliner_make_object_parent_hierarchy(ListBase *lb)
{
/* build hierarchy */
/* XXX also, set extents here... */
- TreeElement *te = reinterpret_cast<TreeElement *>(lb->first);
+ TreeElement *te = static_cast<TreeElement *>(lb->first);
while (te) {
TreeElement *ten = te->next;
TreeStoreElem *tselem = TREESTORE(te);
@@ -63,3 +65,5 @@ bool outliner_animdata_test(const AnimData *adt)
}
return false;
}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/common.hh b/source/blender/editors/space_outliner/tree/common.hh
index 96c1eb34354..ba2d1c3fab6 100644
--- a/source/blender/editors/space_outliner/tree/common.hh
+++ b/source/blender/editors/space_outliner/tree/common.hh
@@ -8,7 +8,11 @@
struct ListBase;
+namespace blender::ed::outliner {
+
const char *outliner_idcode_to_plural(short idcode);
void outliner_make_object_parent_hierarchy(ListBase *lb);
bool outliner_animdata_test(const struct AnimData *adt);
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_display.cc b/source/blender/editors/space_outliner/tree/tree_display.cc
index 6ab497b3fbb..fe4937829d6 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display.cc
@@ -50,4 +50,9 @@ bool AbstractTreeDisplay::supportsModeColumn() const
return false;
}
+bool AbstractTreeDisplay::is_lazy_built() const
+{
+ return false;
+}
+
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh
index f8e35655c26..13b46651562 100644
--- a/source/blender/editors/space_outliner/tree/tree_display.hh
+++ b/source/blender/editors/space_outliner/tree/tree_display.hh
@@ -30,11 +30,11 @@ struct Main;
struct Scene;
struct Sequence;
struct SpaceOutliner;
-struct TreeElement;
struct ViewLayer;
namespace blender::ed::outliner {
+struct TreeElement;
class TreeElementID;
/**
@@ -84,6 +84,15 @@ class AbstractTreeDisplay {
*/
virtual bool supportsModeColumn() const;
+ /**
+ * Some trees may want to skip building children of collapsed parents. This should be done if the
+ * tree type may become very complex, which could cause noticeable slowdowns.
+ * Problem: This doesn't address performance issues while searching, since all elements are
+ * constructed for that. Trees of this type have to be rebuilt for any change to the collapsed
+ * state of any element.
+ */
+ virtual bool is_lazy_built() const;
+
protected:
/** All derived classes will need a handle to this, so storing it in the base for convenience. */
SpaceOutliner &space_outliner_;
@@ -96,6 +105,7 @@ class AbstractTreeDisplay {
* \brief Tree-Display for the View Layer display mode.
*/
class TreeDisplayViewLayer final : public AbstractTreeDisplay {
+ Scene *scene_ = nullptr;
ViewLayer *view_layer_ = nullptr;
bool show_objects_ = true;
@@ -157,6 +167,8 @@ class TreeDisplayOverrideLibraryHierarchies final : public AbstractTreeDisplay {
ListBase buildTree(const TreeSourceData &source_data) override;
+ bool is_lazy_built() const override;
+
private:
ListBase build_hierarchy_for_lib_or_main(Main *bmain,
TreeElement &parent_te,
@@ -232,6 +244,8 @@ class TreeDisplayDataAPI final : public AbstractTreeDisplay {
TreeDisplayDataAPI(SpaceOutliner &space_outliner);
ListBase buildTree(const TreeSourceData &source_data) override;
+
+ bool is_lazy_built() const override;
};
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_display_data.cc b/source/blender/editors/space_outliner/tree/tree_display_data.cc
index bfeb8ce2bdc..3d9b927fbf1 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_data.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_data.cc
@@ -42,4 +42,9 @@ ListBase TreeDisplayDataAPI::buildTree(const TreeSourceData &source_data)
return tree;
}
+bool TreeDisplayDataAPI::is_lazy_built() const
+{
+ return true;
+}
+
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
index f8705c3f0ad..2150d2b211a 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_override_library_hierarchies.cc
@@ -15,6 +15,7 @@
#include "BLT_translation.h"
+#include "BKE_lib_override.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
@@ -74,12 +75,18 @@ ListBase TreeDisplayOverrideLibraryHierarchies::buildTree(const TreeSourceData &
return tree;
}
+bool TreeDisplayOverrideLibraryHierarchies::is_lazy_built() const
+{
+ return true;
+}
+
/* -------------------------------------------------------------------- */
/** \name Library override hierarchy building
* \{ */
class OverrideIDHierarchyBuilder {
SpaceOutliner &space_outliner_;
+ Main &bmain_;
MainIDRelations &id_relations_;
struct HierarchyBuildData {
@@ -93,8 +100,10 @@ class OverrideIDHierarchyBuilder {
};
public:
- OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner, MainIDRelations &id_relations)
- : space_outliner_(space_outliner), id_relations_(id_relations)
+ OverrideIDHierarchyBuilder(SpaceOutliner &space_outliner,
+ Main &bmain,
+ MainIDRelations &id_relations)
+ : space_outliner_(space_outliner), bmain_(bmain), id_relations_(id_relations)
{
}
@@ -115,7 +124,7 @@ ListBase TreeDisplayOverrideLibraryHierarchies::build_hierarchy_for_lib_or_main(
* returning. */
BKE_main_relations_create(bmain, 0);
- OverrideIDHierarchyBuilder builder(space_outliner_, *bmain->relations);
+ OverrideIDHierarchyBuilder builder(space_outliner_, *bmain, *bmain->relations);
/* Keep track over which ID base elements were already added, and expand them once added. */
Map<ID_Type, TreeElement *> id_base_te_map;
@@ -161,11 +170,16 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID(ID &override_root_id,
build_hierarchy_for_ID_recursive(override_root_id, build_data, te_to_expand);
}
+enum ForeachChildReturn {
+ FOREACH_CONTINUE,
+ FOREACH_BREAK,
+};
/* Helpers (defined below). */
static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
const ID &parent_id,
- FunctionRef<void(ID &)> fn);
-static bool id_is_in_override_hierarchy(const ID &id,
+ FunctionRef<ForeachChildReturn(ID &)> fn);
+static bool id_is_in_override_hierarchy(const Main &bmain,
+ const ID &id,
const ID &relationship_parent_id,
const ID &override_root_id);
@@ -177,20 +191,32 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &pare
build_data.parent_ids.add(&parent_id);
foreach_natural_hierarchy_child(id_relations_, parent_id, [&](ID &id) {
- if (!id_is_in_override_hierarchy(id, parent_id, build_data.override_root_id_)) {
- return;
+ /* Some IDs can use themselves, early abort. */
+ if (&id == &parent_id) {
+ return FOREACH_CONTINUE;
+ }
+ if (!id_is_in_override_hierarchy(bmain_, id, parent_id, build_data.override_root_id_)) {
+ return FOREACH_CONTINUE;
}
/* Avoid endless recursion: If there is an ancestor for this ID already, it recurses into
* itself. */
if (build_data.parent_ids.lookup_key_default(&id, nullptr)) {
- return;
+ return FOREACH_CONTINUE;
}
/* Avoid duplicates: If there is a sibling for this ID already, the same ID is just used
* multiple times by the same parent. */
if (build_data.sibling_ids.lookup_key_default(&id, nullptr)) {
- return;
+ return FOREACH_CONTINUE;
+ }
+
+ /* We only want to add children whose parent isn't collapsed. Otherwise, in complex scenes with
+ * thousands of relationships, the building can slow down tremendously. Tag the parent to allow
+ * un-collapsing, but don't actually add the children. */
+ if (!TSELEM_OPEN(TREESTORE(&te_to_expand), &space_outliner_)) {
+ te_to_expand.flag |= TE_PRETEND_HAS_CHILDREN;
+ return FOREACH_BREAK;
}
TreeElement *new_te = outliner_add_element(
@@ -204,6 +230,8 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &pare
child_build_data.parent_ids.add(&id);
child_build_data.sibling_ids.reserve(10);
build_hierarchy_for_ID_recursive(id, child_build_data, *new_te);
+
+ return FOREACH_CONTINUE;
});
}
@@ -229,7 +257,7 @@ void OverrideIDHierarchyBuilder::build_hierarchy_for_ID_recursive(const ID &pare
*/
static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
const ID &parent_id,
- FunctionRef<void(ID &)> fn)
+ FunctionRef<ForeachChildReturn(ID &)> fn)
{
const MainIDRelationsEntry *relations_of_id = static_cast<MainIDRelationsEntry *>(
BLI_ghash_lookup(id_relations.relations_from_pointers, &parent_id));
@@ -250,12 +278,16 @@ static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
if (GS(target_id.name) == ID_OB) {
const Object &potential_child_ob = reinterpret_cast<const Object &>(target_id);
if (potential_child_ob.parent) {
- fn(potential_child_ob.parent->id);
+ if (fn(potential_child_ob.parent->id) == FOREACH_BREAK) {
+ return;
+ }
continue;
}
}
- fn(target_id);
+ if (fn(target_id) == FOREACH_BREAK) {
+ return;
+ }
}
/* If the ID is an object, find and iterate over any child objects. */
@@ -268,15 +300,20 @@ static void foreach_natural_hierarchy_child(const MainIDRelations &id_relations,
continue;
}
- Object &potential_child_ob = reinterpret_cast<Object &>(potential_child_id);
- if (potential_child_ob.parent && &potential_child_ob.parent->id == &parent_id) {
- fn(potential_child_id);
+ const Object &potential_child_ob = reinterpret_cast<Object &>(potential_child_id);
+ if (!potential_child_ob.parent || &potential_child_ob.parent->id != &parent_id) {
+ continue;
+ }
+
+ if (fn(potential_child_id) == FOREACH_BREAK) {
+ return;
}
}
}
}
-static bool id_is_in_override_hierarchy(const ID &id,
+static bool id_is_in_override_hierarchy(const Main &bmain,
+ const ID &id,
const ID &relationship_parent_id,
const ID &override_root_id)
{
@@ -286,20 +323,12 @@ static bool id_is_in_override_hierarchy(const ID &id,
const ID *real_override_id = &id;
if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(&id)) {
- /* This assumes that the parent ID is always the owner of the 'embedded' one, I.e. that no
- * other ID directly uses the embedded one. Should be true, but the debug code adds some checks
- * to validate this assumption. */
- real_override_id = &relationship_parent_id;
-
-#ifndef NDEBUG
- if (GS(id.name) == ID_KE) {
- const Key *key = (Key *)&id;
- BLI_assert(real_override_id == key->from);
- }
- else {
- BLI_assert((id.flag & LIB_EMBEDDED_DATA) != 0);
- }
-#endif
+ /* In many cases, `relationship_parent_id` is the owner, but not always (e.g. there can be
+ * drivers directly between an object and a shape-key). */
+ BKE_lib_override_library_get(const_cast<Main *>(&bmain),
+ const_cast<ID *>(&id),
+ const_cast<ID *>(&relationship_parent_id),
+ const_cast<ID **>(&real_override_id));
}
if (!ID_IS_OVERRIDE_LIBRARY(real_override_id)) {
diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
index c8869d90eca..66c1fa34914 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
@@ -64,6 +64,7 @@ ListBase TreeDisplayViewLayer::buildTree(const TreeSourceData &source_data)
{
ListBase tree = {nullptr};
Scene *scene = source_data.scene;
+ scene_ = scene;
show_objects_ = !(space_outliner_.filter & SO_FILTER_NO_OBJECT);
for (auto *view_layer : ListBaseWrapper<ViewLayer>(scene->view_layers)) {
@@ -96,7 +97,8 @@ void TreeDisplayViewLayer::add_view_layer(Scene &scene, ListBase &tree, TreeElem
if (space_outliner_.filter & SO_FILTER_NO_COLLECTION) {
/* Show objects in the view layer. */
- for (Base *base : List<Base>(view_layer_->object_bases)) {
+ BKE_view_layer_synced_ensure(&scene, view_layer_);
+ for (Base *base : List<Base>(*BKE_view_layer_object_bases_get(view_layer_))) {
TreeElement *te_object = outliner_add_element(
&space_outliner_, &tree, base->object, parent, TSE_SOME_ID, 0);
te_object->directdata = base;
@@ -166,6 +168,7 @@ void TreeDisplayViewLayer::add_layer_collection_objects(ListBase &tree,
LayerCollection &lc,
TreeElement &ten)
{
+ BKE_view_layer_synced_ensure(scene_, view_layer_);
for (CollectionObject *cob : List<CollectionObject>(lc.collection->gobject)) {
Base *base = BKE_view_layer_base_find(view_layer_, cob->ob);
TreeElement *te_object = outliner_add_element(
diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc
index 94d55b70e3c..4a540c3ce87 100644
--- a/source/blender/editors/space_outliner/tree/tree_element.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element.cc
@@ -4,6 +4,9 @@
* \ingroup spoutliner
*/
+#include <string>
+#include <string_view>
+
#include "DNA_anim_types.h"
#include "DNA_listBase.h"
#include "DNA_space_types.h"
@@ -17,6 +20,7 @@
#include "tree_element_driver.hh"
#include "tree_element_gpencil_layer.hh"
#include "tree_element_id.hh"
+#include "tree_element_label.hh"
#include "tree_element_nla.hh"
#include "tree_element_overrides.hh"
#include "tree_element_rna.hh"
@@ -52,9 +56,11 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i
switch (type) {
case TSE_SOME_ID:
return TreeElementID::createFromID(legacy_te, *static_cast<ID *>(idv));
+ case TSE_GENERIC_LABEL:
+ return std::make_unique<TreeElementLabel>(legacy_te, static_cast<const char *>(idv));
case TSE_ANIM_DATA:
return std::make_unique<TreeElementAnimData>(legacy_te,
- *reinterpret_cast<IdAdtTemplate *>(idv)->adt);
+ *static_cast<IdAdtTemplate *>(idv)->adt);
case TSE_DRIVER_BASE:
return std::make_unique<TreeElementDriverBase>(legacy_te, *static_cast<AnimData *>(idv));
case TSE_NLA:
@@ -76,23 +82,24 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i
case TSE_LIBRARY_OVERRIDE:
return std::make_unique<TreeElementOverridesProperty>(
legacy_te, *static_cast<TreeElementOverridesData *>(idv));
+ case TSE_LIBRARY_OVERRIDE_OPERATION:
+ return std::make_unique<TreeElementOverridesPropertyOperation>(
+ legacy_te, *static_cast<TreeElementOverridesData *>(idv));
case TSE_RNA_STRUCT:
- return std::make_unique<TreeElementRNAStruct>(legacy_te,
- *reinterpret_cast<PointerRNA *>(idv));
+ return std::make_unique<TreeElementRNAStruct>(legacy_te, *static_cast<PointerRNA *>(idv));
case TSE_RNA_PROPERTY:
return std::make_unique<TreeElementRNAProperty>(
- legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index);
+ legacy_te, *static_cast<PointerRNA *>(idv), legacy_te.index);
case TSE_RNA_ARRAY_ELEM:
return std::make_unique<TreeElementRNAArrayElement>(
- legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index);
+ legacy_te, *static_cast<PointerRNA *>(idv), legacy_te.index);
case TSE_SEQUENCE:
- return std::make_unique<TreeElementSequence>(legacy_te, *reinterpret_cast<Sequence *>(idv));
+ return std::make_unique<TreeElementSequence>(legacy_te, *static_cast<Sequence *>(idv));
case TSE_SEQ_STRIP:
- return std::make_unique<TreeElementSequenceStrip>(legacy_te,
- *reinterpret_cast<Strip *>(idv));
+ return std::make_unique<TreeElementSequenceStrip>(legacy_te, *static_cast<Strip *>(idv));
case TSE_SEQUENCE_DUP:
- return std::make_unique<TreeElementSequenceStripDuplicate>(
- legacy_te, *reinterpret_cast<Sequence *>(idv));
+ return std::make_unique<TreeElementSequenceStripDuplicate>(legacy_te,
+ *static_cast<Sequence *>(idv));
default:
break;
}
@@ -105,6 +112,22 @@ StringRefNull AbstractTreeElement::getWarning() const
return "";
}
+std::optional<BIFIconID> AbstractTreeElement::getIcon() const
+{
+ return {};
+}
+
+void AbstractTreeElement::print_path()
+{
+ std::string path = legacy_te_.name;
+
+ for (TreeElement *parent = legacy_te_.parent; parent; parent = parent->parent) {
+ path = parent->name + std::string_view("/") + path;
+ }
+
+ std::cout << path << std::endl;
+}
+
void AbstractTreeElement::uncollapse_by_default(TreeElement *legacy_te)
{
if (!TREESTORE(legacy_te)->used) {
diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh
index 1098068d628..1b145a48daa 100644
--- a/source/blender/editors/space_outliner/tree/tree_element.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element.hh
@@ -7,15 +7,18 @@
#pragma once
#include <memory>
+#include <optional>
#include "BLI_string_ref.hh"
+#include "UI_resources.h"
struct ListBase;
struct SpaceOutliner;
-struct TreeElement;
namespace blender::ed::outliner {
+struct TreeElement;
+
/* -------------------------------------------------------------------- */
/* Tree-Display Interface */
@@ -64,6 +67,25 @@ class AbstractTreeElement {
virtual StringRefNull getWarning() const;
/**
+ * Define the icon to be displayed for this element. If this returns an icon, this will be
+ * displayed. Otherwise, #tree_element_get_icon() may still determine an icon. By default no
+ * value is returned (#std::nullopt).
+ *
+ * All elements should be ported to use this over #tree_element_get_icon().
+ */
+ virtual std::optional<BIFIconID> getIcon() const;
+
+ /**
+ * Debugging helper: Print effective path of this tree element, constructed out of the
+ * #TreeElement.name of each element. E.g.:
+ * - Lorem
+ * - ipsum dolor sit
+ * - amet
+ * will print: Lorem/ipsum dolor sit/amet.
+ */
+ void print_path();
+
+ /**
* Expand this tree element if it is displayed for the first time (as identified by its
* tree-store element).
*
diff --git a/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh b/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh
index 956cf3dec48..f3372329dd1 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_anim_data.hh
@@ -8,8 +8,6 @@
#include "tree_element.hh"
-struct TreeElement;
-
namespace blender::ed::outliner {
class TreeElementAnimData final : public AbstractTreeElement {
diff --git a/source/blender/editors/space_outliner/tree/tree_element_driver.hh b/source/blender/editors/space_outliner/tree/tree_element_driver.hh
index 053217e18ec..f0213dd39f2 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_driver.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_driver.hh
@@ -8,8 +8,6 @@
#include "tree_element.hh"
-struct TreeElement;
-
namespace blender::ed::outliner {
class TreeElementDriverBase final : public AbstractTreeElement {
diff --git a/source/blender/editors/space_outliner/tree/tree_element_label.cc b/source/blender/editors/space_outliner/tree/tree_element_label.cc
new file mode 100644
index 00000000000..32fa62c5f5e
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_element_label.cc
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include "DNA_listBase.h"
+
+#include "DNA_outliner_types.h"
+
+#include "../outliner_intern.hh"
+
+#include "tree_element_label.hh"
+
+namespace blender::ed::outliner {
+
+TreeElementLabel::TreeElementLabel(TreeElement &legacy_te, const char *label)
+ : AbstractTreeElement(legacy_te), label_(label)
+{
+ BLI_assert(legacy_te_.store_elem->type == TSE_GENERIC_LABEL);
+ /* The draw string is actually accessed via #TreeElement.name, so make sure this always points to
+ * our string. */
+ legacy_te_.name = label_.c_str();
+}
+
+void TreeElementLabel::setIcon(const BIFIconID icon)
+{
+ icon_ = icon;
+}
+
+std::optional<BIFIconID> TreeElementLabel::getIcon() const
+{
+ return icon_;
+}
+
+} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_label.hh b/source/blender/editors/space_outliner/tree/tree_element_label.hh
new file mode 100644
index 00000000000..fc730c7b8f4
--- /dev/null
+++ b/source/blender/editors/space_outliner/tree/tree_element_label.hh
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#pragma once
+
+#include <string>
+
+#include "UI_resources.h"
+
+#include "tree_element.hh"
+
+namespace blender::ed::outliner {
+
+/**
+ * A basic, general purpose tree element to just display a label and an icon. Can be used to group
+ * together items underneath as well of course.
+ *
+ * Make sure to give this a unique index, so the element can be identified uniquely. Otherwise
+ * glitches like multiple highlighted elements happen, that share all state (e.g. collapsed,
+ * selected, etc.).
+ */
+class TreeElementLabel final : public AbstractTreeElement {
+ const std::string label_;
+ BIFIconID icon_ = ICON_NONE;
+
+ public:
+ TreeElementLabel(TreeElement &legacy_te, const char *label);
+
+ void setIcon(BIFIconID icon);
+ std::optional<BIFIconID> getIcon() const override;
+};
+
+} // 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 d1babda642e..11067d37966 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc
@@ -7,30 +7,60 @@
#include "BKE_collection.h"
#include "BKE_lib_override.h"
-#include "BLI_utildefines.h"
-
+#include "BLI_function_ref.hh"
#include "BLI_listbase_wrapper.hh"
+#include "BLI_map.hh"
+#include "BLI_utildefines.h"
#include "BLT_translation.h"
#include "DNA_space_types.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "../outliner_intern.hh"
+#include "tree_element_label.hh"
#include "tree_element_overrides.hh"
namespace blender::ed::outliner {
+class OverrideRNAPathTreeBuilder {
+ SpaceOutliner &space_outliner_;
+ Map<std::string, TreeElement *> path_te_map;
+
+ public:
+ OverrideRNAPathTreeBuilder(SpaceOutliner &space_outliner);
+ void build_path(TreeElement &parent, TreeElementOverridesData &override_data, short &index);
+
+ private:
+ TreeElement &ensure_label_element_for_prop(
+ TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index);
+ TreeElement &ensure_label_element_for_ptr(TreeElement &parent,
+ StringRef elem_path,
+ PointerRNA &ptr,
+ short &index);
+ void ensure_entire_collection(TreeElement &te_to_expand,
+ const TreeElementOverridesData &override_data,
+ const char *coll_prop_path,
+ short &index);
+};
+
+/* -------------------------------------------------------------------- */
+/** \name Base Element
+ *
+ * Represents an ID that has overridden properties. The expanding will invoke building of tree
+ * elements for the full RNA path of the property.
+ *
+ * \{ */
+
TreeElementOverridesBase::TreeElementOverridesBase(TreeElement &legacy_te, ID &id)
: AbstractTreeElement(legacy_te), id(id)
{
BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_BASE);
if (legacy_te.parent != nullptr &&
- ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION))
-
- {
+ ELEM(legacy_te.parent->store_elem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) {
legacy_te.name = IFACE_("Library Overrides");
}
else {
@@ -51,21 +81,17 @@ StringRefNull TreeElementOverridesBase::getWarning() const
return {};
}
-void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
+static void iterate_properties_to_display(ID &id,
+ const bool show_system_overrides,
+ FunctionRef<void(TreeElementOverridesData &data)> fn)
{
- BLI_assert(id.override_library != nullptr);
+ PointerRNA override_rna_ptr;
+ PropertyRNA *override_rna_prop;
- 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);
- PointerRNA override_rna_ptr;
- PropertyRNA *override_rna_prop;
- short index = 0;
-
- for (auto *override_prop :
+ for (IDOverrideLibraryProperty *override_prop :
ListBaseWrapper<IDOverrideLibraryProperty>(id.override_library->properties)) {
int rnaprop_index = 0;
const bool is_rna_path_valid = BKE_lib_override_rna_property_find(
@@ -80,7 +106,7 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
/* Matching ID pointers are considered as system overrides. */
if (ELEM(override_prop->rna_prop_type, PROP_POINTER, PROP_COLLECTION) &&
RNA_struct_is_ID(RNA_property_pointer_type(&override_rna_ptr, override_rna_prop))) {
- for (auto *override_prop_op :
+ for (IDOverrideLibraryPropertyOperation *override_prop_op :
ListBaseWrapper<IDOverrideLibraryPropertyOperation>(override_prop->operations)) {
if ((override_prop_op->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) == 0) {
do_skip = false;
@@ -103,11 +129,36 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const
TreeElementOverridesData data = {
id, *override_prop, override_rna_ptr, *override_rna_prop, is_rna_path_valid};
- outliner_add_element(
- &space_outliner, &legacy_te_.subtree, &data, &legacy_te_, TSE_LIBRARY_OVERRIDE, index++);
+
+ fn(data);
}
}
+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);
+
+ OverrideRNAPathTreeBuilder path_builder(space_outliner);
+ short index = 0;
+
+ iterate_properties_to_display(id, show_system_overrides, [&](TreeElementOverridesData &data) {
+ path_builder.build_path(legacy_te_, data, index);
+ });
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Overridden Property
+ *
+ * Represents an RNA property that was overridden.
+ *
+ * \{ */
+
TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_te,
TreeElementOverridesData &override_data)
: AbstractTreeElement(legacy_te),
@@ -116,9 +167,10 @@ TreeElementOverridesProperty::TreeElementOverridesProperty(TreeElement &legacy_t
rna_path(override_data.override_property.rna_path),
is_rna_path_valid(override_data.is_rna_path_valid)
{
- BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE);
+ BLI_assert(
+ ELEM(legacy_te.store_elem->type, TSE_LIBRARY_OVERRIDE, TSE_LIBRARY_OVERRIDE_OPERATION));
- legacy_te.name = override_data.override_property.rna_path;
+ legacy_te.name = RNA_property_ui_name(&override_data.override_rna_prop);
}
StringRefNull TreeElementOverridesProperty::getWarning() const
@@ -132,4 +184,305 @@ StringRefNull TreeElementOverridesProperty::getWarning() const
return {};
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Overridden Property Operation
+ *
+ * See #TreeElementOverridesPropertyOperation.
+ * \{ */
+
+TreeElementOverridesPropertyOperation::TreeElementOverridesPropertyOperation(
+ TreeElement &legacy_te, TreeElementOverridesData &override_data)
+ : TreeElementOverridesProperty(legacy_te, override_data)
+{
+ BLI_assert(legacy_te.store_elem->type == TSE_LIBRARY_OVERRIDE_OPERATION);
+ BLI_assert_msg(RNA_property_type(&override_rna_prop) == PROP_COLLECTION,
+ "Override operations are only supported for collections right now");
+ /* Quiet Clang Static Analyzer warning by throwing instead of asserting (possible
+ * null-dereference). */
+ if (!override_data.operation) {
+ throw std::invalid_argument("missing operation");
+ }
+
+ operation_ = std::make_unique<IDOverrideLibraryPropertyOperation>(*override_data.operation);
+ /* Just for extra sanity. */
+ operation_->next = operation_->prev = nullptr;
+
+ if (std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) {
+ const char *dyn_name = RNA_struct_name_get_alloc(&*col_item_ptr, nullptr, 0, nullptr);
+ if (dyn_name) {
+ legacy_te.name = dyn_name;
+ legacy_te.flag |= TE_FREE_NAME;
+ }
+ else {
+ legacy_te.name = RNA_struct_ui_name(col_item_ptr->type);
+ }
+ }
+}
+
+StringRefNull TreeElementOverridesPropertyOperation::getOverrideOperationLabel() const
+{
+ if (ELEM(operation_->operation,
+ IDOVERRIDE_LIBRARY_OP_INSERT_AFTER,
+ IDOVERRIDE_LIBRARY_OP_INSERT_BEFORE)) {
+ return TIP_("Added through override");
+ }
+
+ BLI_assert_unreachable();
+ return {};
+}
+
+std::optional<BIFIconID> TreeElementOverridesPropertyOperation::getIcon() const
+{
+ if (const std::optional<PointerRNA> col_item_ptr = get_collection_ptr()) {
+ return (BIFIconID)RNA_struct_ui_icon(col_item_ptr->type);
+ }
+
+ return {};
+}
+
+std::optional<PointerRNA> TreeElementOverridesPropertyOperation::get_collection_ptr() const
+{
+ PointerRNA col_item_ptr;
+ if (RNA_property_collection_lookup_int(const_cast<PointerRNA *>(&override_rna_ptr),
+ &override_rna_prop,
+ operation_->subitem_local_index,
+ &col_item_ptr)) {
+ return col_item_ptr;
+ }
+
+ return {};
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Helper to build a hierarchy from an RNA path.
+ *
+ * Builds a nice hierarchy representing the nested structs of the override property's RNA path
+ * using UI names and icons. For example `animation_visualization_mothion_path.frame_end` becomes:
+ * - Animation Visualization
+ * - Motion Paths
+ * - End Frame
+ *
+ * Paths are merged so that each RNA sub-path is only represented once in the tree. So there is
+ * some finicky path building going on to create a path -> tree-element map.
+ *
+ * This is more complicated than you'd think it needs to be. Mostly because of RNA collection
+ * overrides:
+ * - A single override may add (and in future remove) multiple collection items. So all operations
+ * of the override have to be considered.
+ * - The order of collection items may matter (e.g. for modifiers), so if collection items are
+ * added/removed, we want to show all other collection items too, in the right order.
+ *
+ * - If the override is inside some collection item, the collection item has to be built, but the
+ * RNA path iterator doesn't
+ * \{ */
+
+OverrideRNAPathTreeBuilder::OverrideRNAPathTreeBuilder(SpaceOutliner &space_outliner)
+ : space_outliner_(space_outliner)
+{
+}
+
+void OverrideRNAPathTreeBuilder::build_path(TreeElement &parent,
+ TreeElementOverridesData &override_data,
+ short &index)
+{
+ PointerRNA idpoin;
+ RNA_id_pointer_create(&override_data.id, &idpoin);
+
+ ListBase path_elems = {nullptr};
+ if (!RNA_path_resolve_elements(&idpoin, override_data.override_property.rna_path, &path_elems)) {
+ return;
+ }
+
+ const char *elem_path = nullptr;
+ TreeElement *te_to_expand = &parent;
+
+ LISTBASE_FOREACH (PropertyElemRNA *, elem, &path_elems) {
+ if (!elem->next) {
+ /* The last element is added as #TSE_LIBRARY_OVERRIDE below. */
+ break;
+ }
+ const char *previous_path = elem_path;
+ const char *new_path = RNA_path_append(previous_path, &elem->ptr, elem->prop, -1, nullptr);
+
+ te_to_expand = &ensure_label_element_for_prop(
+ *te_to_expand, new_path, elem->ptr, *elem->prop, index);
+
+ /* Above the collection property was added (e.g. "Modifiers"), to get the actual collection
+ * item the path refers to, we have to peek at the following path element and add a tree
+ * element for its pointer (e.g. "My Subdiv Modifier"). */
+ if (RNA_property_type(elem->prop) == PROP_COLLECTION) {
+ const int coll_item_idx = RNA_property_collection_lookup_index(
+ &elem->ptr, elem->prop, &elem->next->ptr);
+ const char *coll_item_path = RNA_path_append(
+ previous_path, &elem->ptr, elem->prop, coll_item_idx, nullptr);
+
+ te_to_expand = &ensure_label_element_for_ptr(
+ *te_to_expand, coll_item_path, elem->next->ptr, index);
+
+ MEM_delete(new_path);
+ new_path = coll_item_path;
+ }
+
+ if (new_path) {
+ MEM_delete(elem_path);
+ elem_path = new_path;
+ }
+ }
+ BLI_freelistN(&path_elems);
+
+ /* Special case: Overriding collections, e.g. adding or removing items. In this case we add
+ * elements for all collection items to show full context, and indicate which ones were
+ * added/removed (currently added only). Note that a single collection override may add/remove
+ * multiple items. */
+ if (RNA_property_type(&override_data.override_rna_prop) == PROP_COLLECTION) {
+ /* Tree element for the actual collection item (e.g. "Modifiers"). Can just use the override
+ * ptr & prop here, since they point to the collection property (e.g. `modifiers`). */
+ te_to_expand = &ensure_label_element_for_prop(*te_to_expand,
+ override_data.override_property.rna_path,
+ override_data.override_rna_ptr,
+ override_data.override_rna_prop,
+ index);
+
+ ensure_entire_collection(*te_to_expand, override_data, elem_path, index);
+ }
+ /* Some properties have multiple operations (e.g. an array property with multiple changed
+ * values), so the element may already be present. At this point they are displayed as a single
+ * property in the tree, so don't add it multiple times here. */
+ else if (!path_te_map.contains(override_data.override_property.rna_path)) {
+ outliner_add_element(&space_outliner_,
+ &te_to_expand->subtree,
+ &override_data,
+ te_to_expand,
+ TSE_LIBRARY_OVERRIDE,
+ index++);
+ }
+
+ MEM_delete(elem_path);
+}
+
+void OverrideRNAPathTreeBuilder::ensure_entire_collection(
+ TreeElement &te_to_expand,
+ const TreeElementOverridesData &override_data,
+ /* The path of the owning collection property. */
+ const char *coll_prop_path,
+ short &index)
+{
+ BLI_assert(tree_element_cast<AbstractTreeElement>(&te_to_expand) != nullptr);
+
+ TreeElement *previous_te = nullptr;
+ int item_idx = 0;
+ RNA_PROP_BEGIN (&override_data.override_rna_ptr, itemptr, &override_data.override_rna_prop) {
+ const char *coll_item_path = RNA_path_append(coll_prop_path,
+ &override_data.override_rna_ptr,
+ &override_data.override_rna_prop,
+ item_idx,
+ nullptr);
+ IDOverrideLibraryPropertyOperation *item_operation =
+ BKE_lib_override_library_property_operation_find(
+ &override_data.override_property, nullptr, nullptr, -1, item_idx, false, nullptr);
+ TreeElement *current_te = nullptr;
+
+ TreeElement *existing_te = path_te_map.lookup_default(coll_item_path, nullptr);
+
+ if (existing_te) {
+ /* Reinsert the element to make sure the order is right. It may have been inserted by a
+ * previous override. */
+ BLI_remlink(&te_to_expand.subtree, existing_te);
+ BLI_insertlinkafter(&te_to_expand.subtree, previous_te, existing_te);
+ current_te = existing_te;
+ }
+ /* Is there an operation for this item (added or removed the item to/from the collection)? If
+ * so indicate it as override using #TSE_LIBRARY_OVERRIDE_OPERATION. Otherwise it's just a
+ * regular collection we display for context. */
+ else if (item_operation) {
+ TreeElementOverridesData override_op_data = override_data;
+ override_op_data.operation = item_operation;
+
+ current_te = outliner_add_element(&space_outliner_,
+ &te_to_expand.subtree,
+ /* Element will store a copy. */
+ &override_op_data,
+ &te_to_expand,
+ TSE_LIBRARY_OVERRIDE_OPERATION,
+ index++);
+ }
+ else {
+ current_te = &ensure_label_element_for_ptr(te_to_expand, coll_item_path, itemptr, index);
+ }
+
+ MEM_delete(coll_item_path);
+ item_idx++;
+ previous_te = current_te;
+ }
+ RNA_PROP_END;
+}
+
+static BIFIconID get_property_icon(PointerRNA &ptr, PropertyRNA &prop)
+{
+ BIFIconID icon = (BIFIconID)RNA_property_ui_icon(&prop);
+ if (icon) {
+ return icon;
+ }
+
+ /* Try if the collection item type has a dedicated icon (e.g. #ICON_MODIFIER for the
+ * #Object.modifiers property). */
+ if (RNA_property_type(&prop) == PROP_COLLECTION) {
+ const StructRNA *coll_ptr_type = RNA_property_pointer_type(&ptr, &prop);
+ icon = (BIFIconID)RNA_struct_ui_icon(coll_ptr_type);
+ if (icon != ICON_DOT) {
+ return icon;
+ }
+ }
+
+ return ICON_NONE;
+}
+
+TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_prop(
+ TreeElement &parent, StringRef elem_path, PointerRNA &ptr, PropertyRNA &prop, short &index)
+{
+ return *path_te_map.lookup_or_add_cb(elem_path, [&]() {
+ TreeElement *new_te = outliner_add_element(&space_outliner_,
+ &parent.subtree,
+ (void *)RNA_property_ui_name(&prop),
+ &parent,
+ TSE_GENERIC_LABEL,
+ index++,
+ false);
+ TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te);
+
+ te_label->setIcon(get_property_icon(ptr, prop));
+ return new_te;
+ });
+}
+
+TreeElement &OverrideRNAPathTreeBuilder::ensure_label_element_for_ptr(TreeElement &parent,
+ StringRef elem_path,
+ PointerRNA &ptr,
+ short &index)
+{
+ return *path_te_map.lookup_or_add_cb(elem_path, [&]() {
+ const char *dyn_name = RNA_struct_name_get_alloc(&ptr, nullptr, 0, nullptr);
+
+ TreeElement *new_te = outliner_add_element(
+ &space_outliner_,
+ &parent.subtree,
+ (void *)(dyn_name ? dyn_name : RNA_struct_ui_name(ptr.type)),
+ &parent,
+ TSE_GENERIC_LABEL,
+ index++);
+ TreeElementLabel *te_label = tree_element_cast<TreeElementLabel>(new_te);
+ te_label->setIcon((BIFIconID)RNA_struct_ui_icon(ptr.type));
+
+ MEM_delete(dyn_name);
+
+ return new_te;
+ });
+}
+
+/** \} */
+
} // 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 1db46d9af1d..f8ca146a4ea 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh
@@ -14,6 +14,7 @@
struct ID;
struct IDOverrideLibraryProperty;
+struct IDOverrideLibraryPropertyOperation;
namespace blender::ed::outliner {
@@ -24,6 +25,11 @@ struct TreeElementOverridesData {
PropertyRNA &override_rna_prop;
bool is_rna_path_valid;
+
+ /* In case the property references a specific operation. Only used for collection overrides
+ * currently, where a single override may add/remove multiple collection items (only add
+ * currently). */
+ IDOverrideLibraryPropertyOperation *operation = nullptr;
};
class TreeElementOverridesBase final : public AbstractTreeElement {
@@ -38,7 +44,12 @@ class TreeElementOverridesBase final : public AbstractTreeElement {
StringRefNull getWarning() const override;
};
-class TreeElementOverridesProperty final : public AbstractTreeElement {
+/**
+ * Represent a single overridden property. Collection properties may support multiple override
+ * operations, e.g. to insert/remove multiple collection items. For these multiple operation cases,
+ * use #TreeElementOverridesPropertyOperation.
+ */
+class TreeElementOverridesProperty : public AbstractTreeElement {
public:
PointerRNA override_rna_ptr;
PropertyRNA &override_rna_prop;
@@ -50,6 +61,33 @@ class TreeElementOverridesProperty final : public AbstractTreeElement {
TreeElementOverridesProperty(TreeElement &legacy_te, TreeElementOverridesData &override_data);
StringRefNull getWarning() const override;
+
+ bool isCollectionOperation() const;
+};
+
+/**
+ * Represent a single operation within an overridden property. While usually a single override
+ * property represents a single operation (changing the value), a single overridden collection
+ * property may have multiple operations, e.g. to insert or remove collection items.
+ *
+ * Inherits from the override property class since it should look/behave mostly the same.
+ */
+class TreeElementOverridesPropertyOperation final : public TreeElementOverridesProperty {
+ /** See #TreeElementOverridesData::operation. Operations are recreated as part of the diffing
+ * (e.g. on undo pushes) so store a copy of the data here. */
+ std::unique_ptr<IDOverrideLibraryPropertyOperation> operation_;
+
+ public:
+ TreeElementOverridesPropertyOperation(TreeElement &legacy_te,
+ TreeElementOverridesData &override_data);
+
+ /** Return a short string to display in the right column of the properties mode, indicating what
+ * the override operation did (e.g. added or removed a collection item). */
+ StringRefNull getOverrideOperationLabel() const;
+ std::optional<BIFIconID> getIcon() const override;
+
+ private:
+ std::optional<PointerRNA> get_collection_ptr() const;
};
} // namespace blender::ed::outliner
diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.cc b/source/blender/editors/space_outliner/tree/tree_element_rna.cc
index 914104f1f06..9e1f22b49d6 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_rna.cc
+++ b/source/blender/editors/space_outliner/tree/tree_element_rna.cc
@@ -117,14 +117,14 @@ void TreeElementRNAStruct::expand(SpaceOutliner &space_outliner) const
for (int index = 0; index < tot; index++) {
PointerRNA propptr;
RNA_property_collection_lookup_int(&ptr, iterprop, index, &propptr);
- if (!(RNA_property_flag(reinterpret_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) {
+ if (!(RNA_property_flag(static_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) {
outliner_add_element(
&space_outliner, &legacy_te_.subtree, &ptr, &legacy_te_, TSE_RNA_PROPERTY, index);
}
}
}
else if (tot) {
- legacy_te_.flag |= TE_LAZY_CLOSED;
+ legacy_te_.flag |= TE_PRETEND_HAS_CHILDREN;
}
}
@@ -146,7 +146,7 @@ TreeElementRNAProperty::TreeElementRNAProperty(TreeElement &legacy_te,
PropertyRNA *iterprop = RNA_struct_iterator_property(rna_ptr.type);
RNA_property_collection_lookup_int(&rna_ptr, iterprop, index, &propptr);
- PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(propptr.data);
+ PropertyRNA *prop = static_cast<PropertyRNA *>(propptr.data);
legacy_te_.name = RNA_property_ui_name(prop);
rna_prop_ = prop;
@@ -172,7 +172,7 @@ void TreeElementRNAProperty::expand(SpaceOutliner &space_outliner) const
&space_outliner, &legacy_te_.subtree, &pptr, &legacy_te_, TSE_RNA_STRUCT, -1);
}
else {
- legacy_te_.flag |= TE_LAZY_CLOSED;
+ legacy_te_.flag |= TE_PRETEND_HAS_CHILDREN;
}
}
}
@@ -189,7 +189,7 @@ void TreeElementRNAProperty::expand(SpaceOutliner &space_outliner) const
}
}
else if (tot) {
- legacy_te_.flag |= TE_LAZY_CLOSED;
+ legacy_te_.flag |= TE_PRETEND_HAS_CHILDREN;
}
}
else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
@@ -207,7 +207,7 @@ void TreeElementRNAProperty::expand(SpaceOutliner &space_outliner) const
}
}
else if (tot) {
- legacy_te_.flag |= TE_LAZY_CLOSED;
+ legacy_te_.flag |= TE_PRETEND_HAS_CHILDREN;
}
}
}
@@ -232,8 +232,7 @@ TreeElementRNAArrayElement::TreeElementRNAArrayElement(TreeElement &legacy_te,
char c = RNA_property_array_item_char(TreeElementRNAArrayElement::getPropertyRNA(), index);
- legacy_te_.name = reinterpret_cast<char *>(
- MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName"));
+ legacy_te_.name = static_cast<char *>(MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName"));
if (c) {
sprintf((char *)legacy_te_.name, " %c", c);
}
diff --git a/source/blender/editors/space_outliner/tree/tree_iterator.hh b/source/blender/editors/space_outliner/tree/tree_iterator.hh
index de5bcd2c462..0c94c2f95cf 100644
--- a/source/blender/editors/space_outliner/tree/tree_iterator.hh
+++ b/source/blender/editors/space_outliner/tree/tree_iterator.hh
@@ -10,9 +10,11 @@
struct ListBase;
struct SpaceOutliner;
-struct TreeElement;
namespace blender::ed::outliner {
+
+struct TreeElement;
+
namespace tree_iterator {
using VisitorFn = FunctionRef<void(TreeElement *)>;
diff --git a/source/blender/editors/space_script/CMakeLists.txt b/source/blender/editors/space_script/CMakeLists.txt
index 8486fa0e872..f7fc4e38c17 100644
--- a/source/blender/editors/space_script/CMakeLists.txt
+++ b/source/blender/editors/space_script/CMakeLists.txt
@@ -8,7 +8,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
)
diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c
index e8c7590c1fe..a32c8a3f85a 100644
--- a/source/blender/editors/space_script/script_edit.c
+++ b/source/blender/editors/space_script/script_edit.c
@@ -100,7 +100,7 @@ static int script_reload_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* TODO(campbell): this crashes on netrender and keying sets, need to look into why
+ /* TODO(@campbellbarton): this crashes on netrender and keying sets, need to look into why
* disable for now unless running in debug mode. */
/* It would be nice if we could detect when this is called from the Python
diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c
index a623b98f1b1..c35b1e00184 100644
--- a/source/blender/editors/space_script/space_script.c
+++ b/source/blender/editors/space_script/space_script.c
@@ -152,7 +152,7 @@ void ED_spacetype_script(void)
ARegionType *art;
st->spaceid = SPACE_SCRIPT;
- strncpy(st->name, "Script", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Script");
st->create = script_create;
st->free = script_free;
diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt
index 44f919ca361..deaec0136c4 100644
--- a/source/blender/editors/space_sequencer/CMakeLists.txt
+++ b/source/blender/editors/space_sequencer/CMakeLists.txt
@@ -15,7 +15,6 @@ set(INC
../../sequencer
../../windowmanager
../../../../intern/atomic
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.c b/source/blender/editors/space_sequencer/sequencer_drag_drop.c
index f6561cf07b9..c892e7d7e55 100644
--- a/source/blender/editors/space_sequencer/sequencer_drag_drop.c
+++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.c
@@ -51,7 +51,9 @@
typedef struct SeqDropCoords {
float start_frame, channel;
int strip_len, channel_len;
+ float playback_rate;
bool in_use;
+ bool has_read_mouse_pos;
bool is_intersecting;
bool use_snapping;
float snap_point_x;
@@ -63,7 +65,7 @@ typedef struct SeqDropCoords {
* preloading data on drag start.
* Therefore we will for now use a global variable for this.
*/
-static SeqDropCoords g_drop_coords = {.in_use = false};
+static SeqDropCoords g_drop_coords = {.in_use = false, .has_read_mouse_pos = false};
static void generic_poll_operations(const wmEvent *event, uint8_t type)
{
@@ -82,31 +84,134 @@ static bool image_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *ev
}
}
- return WM_drag_is_ID_type(drag, ID_IM);
+ if (WM_drag_is_ID_type(drag, ID_IM)) {
+ generic_poll_operations(event, TH_SEQ_IMAGE);
+ return true;
+ }
+
+ return false;
}
-static bool movie_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event)
+static bool is_movie(wmDrag *drag)
{
if (drag->type == WM_DRAG_PATH) {
- if (ELEM(drag->icon, 0, ICON_FILE_MOVIE, ICON_FILE_BLANK)) { /* Rule might not work? */
- generic_poll_operations(event, TH_SEQ_MOVIE);
+ if (ELEM(drag->icon, ICON_FILE_MOVIE, ICON_FILE_BLANK)) { /* Rule might not work? */
return true;
}
}
+ if (WM_drag_is_ID_type(drag, ID_MC)) {
+ return true;
+ }
+ return false;
+}
+
+static bool movie_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event)
+{
+ if (is_movie(drag)) {
+ generic_poll_operations(event, TH_SEQ_MOVIE);
+ return true;
+ }
- return WM_drag_is_ID_type(drag, ID_MC);
+ return false;
}
-static bool sound_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event)
+static bool is_sound(wmDrag *drag)
{
if (drag->type == WM_DRAG_PATH) {
if (ELEM(drag->icon, ICON_FILE_SOUND, ICON_FILE_BLANK)) { /* Rule might not work? */
- generic_poll_operations(event, TH_SEQ_AUDIO);
return true;
}
}
+ if (WM_drag_is_ID_type(drag, ID_SO)) {
+ return true;
+ }
+ return false;
+}
- return WM_drag_is_ID_type(drag, ID_SO);
+static bool sound_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event)
+{
+ if (is_sound(drag)) {
+ generic_poll_operations(event, TH_SEQ_AUDIO);
+ return true;
+ }
+
+ return false;
+}
+
+static float update_overlay_strip_position_data(bContext *C, const int mval[2])
+{
+ SeqDropCoords *coords = &g_drop_coords;
+ ARegion *region = CTX_wm_region(C);
+ Scene *scene = CTX_data_scene(C);
+ int hand;
+ View2D *v2d = &region->v2d;
+
+ /* Update the position were we would place the strip if we complete the drag and drop action.
+ */
+ UI_view2d_region_to_view(v2d, mval[0], mval[1], &coords->start_frame, &coords->channel);
+ coords->start_frame = roundf(coords->start_frame);
+ if (coords->channel < 1.0f) {
+ coords->channel = 1;
+ }
+
+ float start_frame = coords->start_frame;
+ float end_frame;
+ float strip_len;
+
+ if (coords->playback_rate != 0.0f) {
+ float scene_playback_rate = (float)scene->r.frs_sec / scene->r.frs_sec_base;
+ strip_len = coords->strip_len / (coords->playback_rate / scene_playback_rate);
+ }
+ else {
+ strip_len = coords->strip_len;
+ }
+
+ end_frame = coords->start_frame + strip_len;
+
+ if (coords->use_snapping) {
+ /* Do snapping via the existing transform code. */
+ int snap_delta;
+ float snap_frame;
+ bool valid_snap;
+
+ valid_snap = ED_transform_snap_sequencer_to_closest_strip_calc(
+ scene, region, start_frame, end_frame, &snap_delta, &snap_frame);
+
+ if (valid_snap) {
+ /* We snapped onto something! */
+ start_frame += snap_delta;
+ coords->start_frame = start_frame;
+ end_frame = start_frame + strip_len;
+ coords->snap_point_x = snap_frame;
+ }
+ else {
+ /* Nothing was snapped to, disable snap drawing. */
+ coords->use_snapping = false;
+ }
+ }
+
+ if (strip_len < 1) {
+ /* Only check if there is a strip already under the mouse cursor. */
+ coords->is_intersecting = find_nearest_seq(scene, &region->v2d, &hand, mval);
+ }
+ else {
+ /* Check if there is a strip that would intersect with the new strip(s). */
+ coords->is_intersecting = false;
+ Sequence dummy_seq = {.machine = coords->channel,
+ .start = coords->start_frame,
+ .len = coords->strip_len,
+ .speed_factor = 1.0f,
+ .media_playback_rate = coords->playback_rate,
+ .flag = SEQ_AUTO_PLAYBACK_RATE};
+ Editing *ed = SEQ_editing_ensure(scene);
+
+ for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) {
+ coords->is_intersecting = SEQ_transform_test_overlap(scene, ed->seqbasep, &dummy_seq);
+ dummy_seq.machine++;
+ }
+ }
+
+ return strip_len;
}
static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
@@ -153,93 +258,77 @@ static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
RNA_collection_add(drop->ptr, "files", &itemptr);
RNA_string_set(&itemptr, "name", file);
}
-
- if (g_drop_coords.in_use) {
- RNA_int_set(drop->ptr, "frame_start", g_drop_coords.start_frame);
- RNA_int_set(drop->ptr, "channel", g_drop_coords.channel);
- RNA_boolean_set(drop->ptr, "overlap_shuffle_override", true);
- }
- else {
- Scene *scene = CTX_data_scene(C);
- Editing *ed = SEQ_editing_get(scene);
- ListBase *seqbase = SEQ_active_seqbase_get(ed);
- ListBase *channels = SEQ_channels_displayed_get(ed);
- SpaceSeq *sseq = CTX_wm_space_seq(C);
-
- SeqCollection *strips = SEQ_query_rendered_strips(
- scene, channels, seqbase, scene->r.cfra, sseq->chanshown);
-
- /* Get the top most strip channel that is in view.*/
- Sequence *seq;
- int max_channel = -1;
- SEQ_ITERATOR_FOREACH (seq, strips) {
- max_channel = max_ii(seq->machine, max_channel);
- }
-
- if (max_channel != -1) {
- RNA_int_set(drop->ptr, "channel", max_channel);
- }
- SEQ_collection_free(strips);
- }
}
-}
-static void update_overlay_strip_poistion_data(bContext *C, const int mval[2])
-{
- SeqDropCoords *coords = &g_drop_coords;
- ARegion *region = CTX_wm_region(C);
- Scene *scene = CTX_data_scene(C);
- int hand;
- View2D *v2d = &region->v2d;
+ if (g_drop_coords.in_use) {
+ if (!g_drop_coords.has_read_mouse_pos) {
+ /* We didn't read the mouse position, so we need to do it manually here. */
+ int xy[2];
+ wmWindow *win = CTX_wm_window(C);
+ xy[0] = win->eventstate->xy[0];
+ xy[1] = win->eventstate->xy[1];
+
+ ARegion *region = CTX_wm_region(C);
+ int mval[2];
+ /* Convert mouse coordinates to region local coordinates. */
+ mval[0] = xy[0] - region->winrct.xmin;
+ mval[1] = xy[1] - region->winrct.ymin;
+
+ update_overlay_strip_position_data(C, mval);
+ }
- /* Update the position were we would place the strip if we complete the drag and drop action.
- */
- UI_view2d_region_to_view(v2d, mval[0], mval[1], &coords->start_frame, &coords->channel);
- coords->start_frame = roundf(coords->start_frame);
- if (coords->channel < 1.0f) {
- coords->channel = 1;
+ RNA_int_set(drop->ptr, "frame_start", g_drop_coords.start_frame);
+ RNA_int_set(drop->ptr, "channel", g_drop_coords.channel);
+ RNA_boolean_set(drop->ptr, "overlap_shuffle_override", true);
}
+ else {
+ /* We are dropped inside the preview region. Put the strip on top of the
+ * current displayed frame. */
+ Scene *scene = CTX_data_scene(C);
+ Editing *ed = SEQ_editing_get(scene);
+ ListBase *seqbase = SEQ_active_seqbase_get(ed);
+ ListBase *channels = SEQ_channels_displayed_get(ed);
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
- float start_frame = coords->start_frame;
- float end_frame = coords->start_frame + coords->strip_len;
-
- if (coords->use_snapping) {
- /* Do snapping via the existing transform code. */
- int snap_delta;
- float snap_frame;
- bool valid_snap;
-
- valid_snap = ED_transform_snap_sequencer_to_closest_strip_calc(
- scene, region, start_frame, end_frame, &snap_delta, &snap_frame);
+ SeqCollection *strips = SEQ_query_rendered_strips(
+ scene, channels, seqbase, scene->r.cfra, sseq->chanshown);
- if (valid_snap) {
- /* We snapped onto something! */
- start_frame += snap_delta;
- coords->start_frame = start_frame;
- end_frame = start_frame + coords->strip_len;
- coords->snap_point_x = snap_frame;
+ /* Get the top most strip channel that is in view. */
+ Sequence *seq;
+ int max_channel = -1;
+ SEQ_ITERATOR_FOREACH (seq, strips) {
+ max_channel = max_ii(seq->machine, max_channel);
}
- else {
- /* Nothing was snapped to, disable snap drawing. */
- coords->use_snapping = false;
+
+ if (max_channel != -1) {
+ RNA_int_set(drop->ptr, "channel", max_channel);
}
+ SEQ_collection_free(strips);
}
+}
- if (coords->strip_len < 1) {
- /* Only check if there is a strip already under the mouse cursor. */
- coords->is_intersecting = find_nearest_seq(scene, &region->v2d, &hand, mval);
+static void get_drag_path(wmDrag *drag, char r_path[FILE_MAX])
+{
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
+ /* ID dropped. */
+ if (id != NULL) {
+ const ID_Type id_type = GS(id->name);
+ if (id_type == ID_IM) {
+ Image *ima = (Image *)id;
+ BLI_strncpy(r_path, ima->filepath, FILE_MAX);
+ }
+ else if (id_type == ID_MC) {
+ MovieClip *clip = (MovieClip *)id;
+ BLI_strncpy(r_path, clip->filepath, FILE_MAX);
+ }
+ else if (id_type == ID_SO) {
+ bSound *sound = (bSound *)id;
+ BLI_strncpy(r_path, sound->filepath, FILE_MAX);
+ }
+ BLI_path_abs(r_path, BKE_main_blendfile_path_from_global());
}
else {
- /* Check if there is a strip that would intersect with the new strip(s). */
- coords->is_intersecting = false;
- Sequence dummy_seq = {
- .machine = coords->channel, .start = coords->start_frame, .len = coords->strip_len};
- Editing *ed = SEQ_editing_ensure(scene);
-
- for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) {
- coords->is_intersecting = SEQ_transform_test_overlap(scene, ed->seqbasep, &dummy_seq);
- dummy_seq.machine++;
- }
+ BLI_strncpy(r_path, drag->path, FILE_MAX);
}
}
@@ -256,7 +345,7 @@ static void draw_seq_in_view(bContext *C, wmWindow *UNUSED(win), wmDrag *drag, c
mval[0] = xy[0] - region->winrct.xmin;
mval[1] = xy[1] - region->winrct.ymin;
- update_overlay_strip_poistion_data(C, mval);
+ float strip_len = update_overlay_strip_position_data(C, mval);
GPU_matrix_push();
UI_view2d_view_ortho(&region->v2d);
@@ -276,11 +365,11 @@ static void draw_seq_in_view(bContext *C, wmWindow *UNUSED(win), wmDrag *drag, c
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_smooth(true);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw strips. The code here is taken from sequencer_draw. */
float x1 = coords->start_frame;
- float x2 = coords->start_frame + coords->strip_len;
+ float x2 = coords->start_frame + floorf(strip_len);
float strip_color[3];
uchar text_color[4] = {255, 255, 255, 255};
float pixelx = BLI_rctf_size_x(&region->v2d.cur) / BLI_rcti_size_x(&region->v2d.mask);
@@ -354,21 +443,22 @@ static void draw_seq_in_view(bContext *C, wmWindow *UNUSED(win), wmDrag *drag, c
const char *text_array[5];
char text_display[FILE_MAX];
char filename[FILE_MAX];
- char rel_path[FILE_MAX];
+ char path[FILE_MAX];
char strip_duration_text[16];
int len_text_arr = 0;
+ get_drag_path(drag, path);
+
if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_NAME) {
- BLI_split_file_part(drag->path, filename, FILE_MAX);
+ BLI_split_file_part(path, filename, FILE_MAX);
text_array[len_text_arr++] = filename;
}
if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) {
Main *bmain = CTX_data_main(C);
- BLI_strncpy(rel_path, drag->path, FILE_MAX);
- BLI_path_rel(rel_path, BKE_main_blendfile_path(bmain));
+ BLI_path_rel(path, BKE_main_blendfile_path(bmain));
text_array[len_text_arr++] = text_sep;
- text_array[len_text_arr++] = rel_path;
+ text_array[len_text_arr++] = path;
}
if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION) {
@@ -442,6 +532,14 @@ static void prefetch_data_fn(void *custom_data,
if (anim != NULL) {
g_drop_coords.strip_len = IMB_anim_get_duration(anim, IMB_TC_NONE);
+ short frs_sec;
+ float frs_sec_base;
+ if (IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base, true)) {
+ g_drop_coords.playback_rate = (float)frs_sec / frs_sec_base;
+ }
+ else {
+ g_drop_coords.playback_rate = 0;
+ }
IMB_free_anim(anim);
#ifdef WITH_AUDASPACE
/* Try to load sound and see if the video has a sound channel. */
@@ -464,7 +562,7 @@ static void free_prefetch_data_fn(void *custom_data)
MEM_freeN(job_data);
}
-static void start_audio_video_job(bContext *C, char *path, bool only_audio)
+static void start_audio_video_job(bContext *C, wmDrag *drag, bool only_audio)
{
g_drop_coords.strip_len = 0;
g_drop_coords.channel_len = 1;
@@ -478,8 +576,8 @@ static void start_audio_video_job(bContext *C, char *path, bool only_audio)
DropJobData *job_data = (DropJobData *)MEM_mallocN(sizeof(DropJobData),
"SeqDragDropPreviewData");
+ get_drag_path(drag, job_data->path);
- BLI_strncpy(job_data->path, path, FILE_MAX);
job_data->only_audio = only_audio;
job_data->scene_fps = FPS;
@@ -492,15 +590,15 @@ static void start_audio_video_job(bContext *C, char *path, bool only_audio)
static void video_prefetch(bContext *C, wmDrag *drag)
{
- if (drag->type == WM_DRAG_PATH && ELEM(drag->icon, ICON_FILE_MOVIE, ICON_FILE_BLANK)) {
- start_audio_video_job(C, drag->path, false);
+ if (is_movie(drag)) {
+ start_audio_video_job(C, drag, false);
}
}
static void audio_prefetch(bContext *C, wmDrag *drag)
{
- if (drag->type == WM_DRAG_PATH && ELEM(drag->icon, ICON_FILE_SOUND, ICON_FILE_BLANK)) {
- start_audio_video_job(C, drag->path, true);
+ if (is_sound(drag)) {
+ start_audio_video_job(C, drag, true);
}
}
@@ -534,6 +632,7 @@ static void sequencer_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSE
SeqDropCoords *coords = drop->draw_data;
if (coords) {
coords->in_use = false;
+ coords->has_read_mouse_pos = false;
drop->draw_data = NULL;
}
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 4b9ff1e170e..71804d29e6b 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -241,327 +241,282 @@ typedef struct WaveVizData {
float pos[2];
float rms_pos;
bool clip;
- bool end;
+ bool draw_line; /* Draw triangle otherwise. */
+ bool final_sample; /* There are no more samples. */
} WaveVizData;
-static int get_section_len(WaveVizData *start, WaveVizData *end)
+static bool seq_draw_waveforms_poll(const bContext *UNUSED(C), SpaceSeq *sseq, Sequence *seq)
{
- int len = 0;
- while (start != end) {
- len++;
- if (start->end) {
- return len;
- }
- start++;
- }
- return len;
-}
+ const bool strip_is_valid = seq->type == SEQ_TYPE_SOUND_RAM && seq->sound != NULL;
+ const bool overlays_enabled = (sseq->flag & SEQ_SHOW_OVERLAY) != 0;
+ const bool ovelay_option = ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) != 0 ||
+ (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM));
-static void draw_waveform(WaveVizData *iter, WaveVizData *end, GPUPrimType prim_type, bool use_rms)
-{
- int strip_len = get_section_len(iter, end);
- if (strip_len > 1) {
- GPU_blend(GPU_BLEND_ALPHA);
- GPUVertFormat *format = immVertexFormat();
- uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) != 0) {
+ return false;
+ }
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
- immBegin(prim_type, strip_len);
+ if (strip_is_valid && overlays_enabled && ovelay_option) {
+ return true;
+ }
- while (iter != end) {
- if (iter->clip) {
- immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f);
- }
- else if (use_rms) {
- immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f);
- }
- else {
- immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f);
- }
+ return false;
+}
- if (use_rms) {
- immVertex2f(pos, iter->pos[0], iter->rms_pos);
- }
- else {
- immVertex2f(pos, iter->pos[0], iter->pos[1]);
- }
+static void waveform_job_start_if_needed(const bContext *C, Sequence *seq)
+{
+ bSound *sound = seq->sound;
- if (iter->end) {
- /* End of line. */
- iter++;
- strip_len = get_section_len(iter, end);
- if (strip_len != 0) {
- immEnd();
- immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
- immBegin(prim_type, strip_len);
- }
- }
- else {
- iter++;
- }
+ BLI_spin_lock(sound->spinlock);
+ if (!sound->waveform) {
+ /* Load the waveform data if it hasn't been loaded and cached already. */
+ if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) {
+ /* Prevent sounds from reloading. */
+ sound->tags |= SOUND_TAGS_WAVEFORM_LOADING;
+ BLI_spin_unlock(sound->spinlock);
+ sequencer_preview_add_sound(C, seq);
+ }
+ else {
+ BLI_spin_unlock(sound->spinlock);
}
- immEnd();
- immUnbindProgram();
-
- GPU_blend(GPU_BLEND_NONE);
}
+ BLI_spin_unlock(sound->spinlock);
}
-static float clamp_frame_coord_to_pixel(float frame_coord,
- float pixel_frac,
- float frames_per_pixel)
+static size_t get_vertex_count(WaveVizData *waveform_data)
{
- float cur_pixel = (frame_coord / frames_per_pixel);
- float new_pixel = (int)(frame_coord / frames_per_pixel) + pixel_frac;
- if (cur_pixel > new_pixel) {
- new_pixel += 1.0f;
+ bool draw_line = waveform_data->draw_line;
+ size_t length = 0;
+
+ while (waveform_data->draw_line == draw_line && !waveform_data->final_sample) {
+ waveform_data++;
+ length++;
}
- return new_pixel * frames_per_pixel;
+
+ return length;
}
-/**
- * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
- * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction).
- */
-static void draw_seq_waveform_overlay(View2D *v2d,
- const bContext *C,
- SpaceSeq *sseq,
- Scene *scene,
- Sequence *seq,
- float x1,
- float y1,
- float x2,
- float y2,
- float frames_per_pixel)
+static size_t draw_waveform_segment(WaveVizData *waveform_data, bool use_rms)
{
- if (seq->sound && ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) ||
- (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) {
- /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid
- * flickering when moving around the strip.
- * To do this we figure out the fractional offset in pixel space by checking where the
- * window starts.
- * We then append this pixel offset to our strip start coordinate to ensure we are aligned to
- * the screen pixel grid. */
- float pixel_frac = v2d->cur.xmin / frames_per_pixel - floor(v2d->cur.xmin / frames_per_pixel);
- float x1_adj = clamp_frame_coord_to_pixel(x1, pixel_frac, frames_per_pixel);
-
- /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
- float x1_offset = max_ff(v2d->cur.xmin, x1_adj);
- float x2_offset = min_ff(v2d->cur.xmax, x2);
-
- /* Calculate how long the strip that is in view is in pixels. */
- int pix_strip_len = round((x2_offset - x1_offset) / frames_per_pixel);
-
- if (pix_strip_len < 2) {
- return;
- }
+ size_t vertices_done = 0;
+ size_t vertex_count = get_vertex_count(waveform_data);
- bSound *sound = seq->sound;
+ /* Not enough data to draw. */
+ if (vertex_count <= 2) {
+ return vertex_count;
+ }
- BLI_spin_lock(sound->spinlock);
- if (!sound->waveform) {
- /* Load the waveform data if it hasn't been loaded and cached already. */
- if (!(sound->tags & SOUND_TAGS_WAVEFORM_LOADING)) {
- /* Prevent sounds from reloading. */
- sound->tags |= SOUND_TAGS_WAVEFORM_LOADING;
- BLI_spin_unlock(sound->spinlock);
- sequencer_preview_add_sound(C, seq);
- }
- else {
- BLI_spin_unlock(sound->spinlock);
- }
- return; /* Nothing to draw. */
- }
- BLI_spin_unlock(sound->spinlock);
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPUVertFormat *format = immVertexFormat();
+ GPUPrimType prim_type = waveform_data->draw_line ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
+ immBegin(prim_type, vertex_count);
- SoundWaveform *waveform = sound->waveform;
+ while (vertices_done < vertex_count && !waveform_data->final_sample) {
+ /* Color. */
+ if (waveform_data->clip) {
+ immAttr4f(col, 1.0f, 0.0f, 0.0f, 0.5f);
+ }
+ else if (use_rms) {
+ immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.8f);
+ }
+ else {
+ immAttr4f(col, 1.0f, 1.0f, 1.0f, 0.5f);
+ }
- /* Waveform could not be built. */
- if (waveform->length == 0) {
- return;
+ /* Vertices. */
+ if (use_rms) {
+ immVertex2f(pos, waveform_data->pos[0], waveform_data->rms_pos);
+ }
+ else {
+ immVertex2f(pos, waveform_data->pos[0], waveform_data->pos[1]);
}
- /* F-Curve lookup is quite expensive, so do this after precondition. */
- FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
+ vertices_done++;
+ waveform_data++;
+ }
- WaveVizData *tri_strip_arr = MEM_callocN(sizeof(*tri_strip_arr) * pix_strip_len * 2,
- "tri_strip");
- WaveVizData *line_strip_arr = MEM_callocN(sizeof(*line_strip_arr) * pix_strip_len,
- "line_strip");
+ immEnd();
+ immUnbindProgram();
- WaveVizData *tri_strip_iter = tri_strip_arr;
- WaveVizData *line_strip_iter = line_strip_arr;
+ GPU_blend(GPU_BLEND_NONE);
- /* The y coordinate for the middle of the strip. */
- float y_mid = (y1 + y2) / 2.0f;
- /* The length from the middle of the strip to the top/bottom. */
- float y_scale = (y2 - y1) / 2.0f;
- float volume = seq->volume;
+ return vertices_done;
+}
- /* Value to keep track if the previous item to be drawn was a line strip. */
- int8_t was_line_strip = -1; /* -1 == no previous value. */
+static void draw_waveform(WaveVizData *waveform_data, size_t wave_data_len)
+{
+ size_t items_done = 0;
+ while (items_done < wave_data_len) {
+ if (!waveform_data[items_done].draw_line) { /* Draw RMS. */
+ draw_waveform_segment(&waveform_data[items_done], true);
+ }
+ items_done += draw_waveform_segment(&waveform_data[items_done], false);
+ }
+}
- float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
+static float align_frame_with_pixel(float frame_coord, float frames_per_pixel)
+{
+ return round_fl_to_int(frame_coord / frames_per_pixel) * frames_per_pixel;
+}
- /* How many samples do we have for each pixel? */
- float samples_per_pix = samples_per_frame * frames_per_pixel;
+static void write_waveform_data(WaveVizData *waveform_data,
+ const vec2f pos,
+ const float rms,
+ const bool is_clipping,
+ const bool draw_line)
+{
+ waveform_data->pos[0] = pos.x;
+ waveform_data->pos[1] = pos.y;
+ waveform_data->clip = is_clipping;
+ waveform_data->rms_pos = rms;
+ waveform_data->draw_line = draw_line;
+}
- float strip_start_offset = seq->startofs + seq->anim_startofs;
- float start_sample = 0;
+static size_t waveform_append_sample(WaveVizData *waveform_data,
+ vec2f pos,
+ const float value_min,
+ const float value_max,
+ const float y_mid,
+ const float y_scale,
+ const float rms,
+ const bool is_clipping,
+ const bool is_line_strip)
+{
+ size_t data_written = 0;
+ pos.y = y_mid + value_min * y_scale;
+ float rms_value = y_mid + max_ff(-rms, value_min) * y_scale;
+ write_waveform_data(&waveform_data[0], pos, rms_value, is_clipping, is_line_strip);
+ data_written++;
+
+ /* Use `value_max` as second vertex for triangle drawing. */
+ if (!is_line_strip) {
+ pos.y = y_mid + value_max * y_scale;
+ rms_value = y_mid + min_ff(rms, value_max) * y_scale;
+ write_waveform_data(&waveform_data[1], pos, rms_value, is_clipping, is_line_strip);
+ data_written++;
+ }
+ return data_written;
+}
- if (strip_start_offset != 0) {
- /* If start offset is not zero, we need to make sure that we pick the same start sample as if
- * we simply scrolled the start of the strip off-screen. Otherwise we will get flickering
- * when changing start offset as the pixel alignment will not be the same for the drawn
- * samples. */
- strip_start_offset = clamp_frame_coord_to_pixel(
- x1 - strip_start_offset, pixel_frac, frames_per_pixel);
- start_sample = fabsf(strip_start_offset - x1_adj) * samples_per_frame;
- }
+/**
+ * \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
+ * \param frames_per_pixel: The amount of pixels a whole frame takes up (x-axis direction).
+ */
+static void draw_seq_waveform_overlay(
+ const bContext *C, ARegion *region, Sequence *seq, float x1, float y1, float x2, float y2)
+{
+ const View2D *v2d = &region->v2d;
+ Scene *scene = CTX_data_scene(C);
- start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
- /* If we scrolled the start off-screen, then the start sample should be at the first visible
- * sample. */
- start_sample += (x1_offset - x1_adj) * samples_per_frame;
+ const float frames_per_pixel = BLI_rctf_size_x(&region->v2d.cur) / region->winx;
+ const float samples_per_frame = SOUND_WAVE_SAMPLES_PER_SECOND / FPS;
+ float samples_per_pixel = samples_per_frame * frames_per_pixel;
- for (int i = 0; i < pix_strip_len; i++) {
- float sample_offset = start_sample + i * samples_per_pix;
- int p = sample_offset;
+ /* Align strip start with nearest pixel to prevent waveform flickering. */
+ const float x1_aligned = align_frame_with_pixel(x1, frames_per_pixel);
+ /* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
+ const float frame_start = max_ff(v2d->cur.xmin, x1_aligned);
+ const float frame_end = min_ff(v2d->cur.xmax, x2);
+ const int pixels_to_draw = round_fl_to_int((frame_end - frame_start) / frames_per_pixel);
- if (p < 0) {
- continue;
- }
+ if (pixels_to_draw < 2) {
+ return; /* Not much to draw, exit before running job. */
+ }
- if (p >= waveform->length) {
- break;
- }
+ waveform_job_start_if_needed(C, seq);
- float value_min = waveform->data[p * 3];
- float value_max = waveform->data[p * 3 + 1];
- float rms = waveform->data[p * 3 + 2];
-
- if (p + 1 < waveform->length) {
- /* Use simple linear interpolation. */
- float f = sample_offset - p;
- value_min = (1.0f - f) * value_min + f * waveform->data[p * 3 + 3];
- value_max = (1.0f - f) * value_max + f * waveform->data[p * 3 + 4];
- rms = (1.0f - f) * rms + f * waveform->data[p * 3 + 5];
- if (samples_per_pix > 1.0f) {
- /* We need to sum up the values we skip over until the next step. */
- float next_pos = sample_offset + samples_per_pix;
- int end_idx = next_pos;
-
- for (int j = p + 1; (j < waveform->length) && (j < end_idx); j++) {
- value_min = min_ff(value_min, waveform->data[j * 3]);
- value_max = max_ff(value_max, waveform->data[j * 3 + 1]);
- rms = max_ff(rms, waveform->data[j * 3 + 2]);
- }
- }
- }
+ SoundWaveform *waveform = seq->sound->waveform;
+ if (waveform == NULL || waveform->length == 0) {
+ return; /* Waveform was not built. */
+ }
- if (fcu && !BKE_fcurve_is_empty(fcu)) {
- float evaltime = x1_offset + (i * frames_per_pixel);
- volume = evaluate_fcurve(fcu, evaltime);
- CLAMP_MIN(volume, 0.0f);
- }
+ /* F-Curve lookup is quite expensive, so do this after precondition. */
+ FCurve *fcu = id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "volume", 0, NULL);
+ WaveVizData *waveform_data = MEM_callocN(sizeof(WaveVizData) * pixels_to_draw * 3, __func__);
+ size_t wave_data_len = 0;
- value_min *= volume;
- value_max *= volume;
- rms *= volume;
+ /* Offset must be also aligned, otherwise waveform flickers when moving left handle. */
+ const float strip_offset = align_frame_with_pixel(seq->startofs + seq->anim_startofs,
+ frames_per_pixel);
+ float start_sample = strip_offset * samples_per_frame;
+ start_sample += seq->sound->offset_time * SOUND_WAVE_SAMPLES_PER_SECOND;
+ /* Add off-screen part of strip to offset. */
+ start_sample += (frame_start - x1_aligned) * samples_per_frame;
- bool clipping = false;
+ for (int i = 0; i < pixels_to_draw; i++) {
+ float sample = start_sample + i * samples_per_pixel;
+ int sample_index = round_fl_to_int(sample);
- if (value_max > 1 || value_min < -1) {
- clipping = true;
+ if (sample_index < 0) {
+ continue;
+ }
- CLAMP_MAX(value_max, 1.0f);
- CLAMP_MIN(value_min, -1.0f);
- }
+ if (sample_index >= waveform->length) {
+ break;
+ }
- bool is_line_strip = (value_max - value_min < 0.05f);
-
- if (!ELEM(was_line_strip, -1, is_line_strip)) {
- /* If the previously added strip type isn't the same as the current one,
- * add transition areas so they transition smoothly between each other. */
- if (is_line_strip) {
- /* This will be a line strip, end the tri strip. */
- tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
- tri_strip_iter->pos[1] = y_mid + value_min * y_scale;
- tri_strip_iter->clip = clipping;
- tri_strip_iter->rms_pos = tri_strip_iter->pos[1];
- tri_strip_iter->end = true;
-
- /* End of section. */
- tri_strip_iter++;
-
- /* Check if we are at the end.
- * If so, skip one point line. */
- if (i + 1 == pix_strip_len) {
- continue;
- }
- }
- else {
- /* This will be a tri strip. */
- line_strip_iter--;
- tri_strip_iter->pos[0] = line_strip_iter->pos[0];
- tri_strip_iter->pos[1] = line_strip_iter->pos[1];
- tri_strip_iter->clip = line_strip_iter->clip;
- tri_strip_iter->rms_pos = line_strip_iter->pos[1];
- tri_strip_iter++;
-
- /* Check if line had only one point. */
- line_strip_iter--;
- if (line_strip_iter < line_strip_arr || line_strip_iter->end) {
- /* Only one point, skip it. */
- line_strip_iter++;
- }
- else {
- /* End of section. */
- line_strip_iter++;
- line_strip_iter->end = true;
- line_strip_iter++;
- }
+ float value_min = waveform->data[sample_index * 3];
+ float value_max = waveform->data[sample_index * 3 + 1];
+ float rms = waveform->data[sample_index * 3 + 2];
+
+ if (sample_index + 1 < waveform->length) {
+ /* Use simple linear interpolation. */
+ float f = sample - sample_index;
+ value_min = (1.0f - f) * value_min + f * waveform->data[sample_index * 3 + 3];
+ value_max = (1.0f - f) * value_max + f * waveform->data[sample_index * 3 + 4];
+ rms = (1.0f - f) * rms + f * waveform->data[sample_index * 3 + 5];
+ if (samples_per_pixel > 1.0f) {
+ /* We need to sum up the values we skip over until the next step. */
+ float next_pos = sample + samples_per_pixel;
+ int end_idx = next_pos;
+
+ for (int j = sample_index + 1; (j < waveform->length) && (j < end_idx); j++) {
+ value_min = min_ff(value_min, waveform->data[j * 3]);
+ value_max = max_ff(value_max, waveform->data[j * 3 + 1]);
+ rms = max_ff(rms, waveform->data[j * 3 + 2]);
}
}
+ }
- was_line_strip = is_line_strip;
-
- if (is_line_strip) {
- line_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
- line_strip_iter->pos[1] = y_mid + value_min * y_scale;
- line_strip_iter->clip = clipping;
- line_strip_iter++;
- }
- else {
- tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
- tri_strip_iter->pos[1] = y_mid + value_min * y_scale;
- tri_strip_iter->clip = clipping;
- tri_strip_iter->rms_pos = y_mid + max_ff(-rms, value_min) * y_scale;
- tri_strip_iter++;
-
- tri_strip_iter->pos[0] = x1_offset + i * frames_per_pixel;
- tri_strip_iter->pos[1] = y_mid + value_max * y_scale;
- tri_strip_iter->clip = clipping;
- tri_strip_iter->rms_pos = y_mid + min_ff(rms, value_max) * y_scale;
- tri_strip_iter++;
- }
+ float volume = seq->volume;
+ if (fcu && !BKE_fcurve_is_empty(fcu)) {
+ float evaltime = frame_start + (i * frames_per_pixel);
+ volume = evaluate_fcurve(fcu, evaltime);
+ CLAMP_MIN(volume, 0.0f);
}
- WaveVizData *tri_strip_end = tri_strip_iter;
- WaveVizData *line_strip_end = line_strip_iter;
+ value_min *= volume;
+ value_max *= volume;
+ rms *= volume;
+
+ bool is_clipping = false;
- tri_strip_iter = tri_strip_arr;
- line_strip_iter = line_strip_arr;
+ if (value_max > 1 || value_min < -1) {
+ is_clipping = true;
- draw_waveform(line_strip_iter, line_strip_end, GPU_PRIM_LINE_STRIP, false);
- draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, false);
- draw_waveform(tri_strip_iter, tri_strip_end, GPU_PRIM_TRI_STRIP, true);
+ CLAMP_MAX(value_max, 1.0f);
+ CLAMP_MIN(value_min, -1.0f);
+ }
- MEM_freeN(tri_strip_arr);
- MEM_freeN(line_strip_arr);
+ bool is_line_strip = (value_max - value_min < 0.05f);
+ /* The y coordinate for the middle of the strip. */
+ float y_mid = (y1 + y2) / 2.0f;
+ /* The length from the middle of the strip to the top/bottom. */
+ float y_scale = (y2 - y1) / 2.0f;
+
+ vec2f pos = {frame_start + i * frames_per_pixel, y_mid + value_min * y_scale};
+ WaveVizData *new_data = &waveform_data[wave_data_len];
+ wave_data_len += waveform_append_sample(
+ new_data, pos, value_min, value_max, y_mid, y_scale, rms, is_clipping, is_line_strip);
}
+
+ /* Terminate array, so `get_segment_length()` can know when to stop. */
+ waveform_data[wave_data_len].final_sample = true;
+ draw_waveform(waveform_data, wave_data_len);
+ MEM_freeN(waveform_data);
}
static void drawmeta_contents(Scene *scene,
@@ -613,7 +568,7 @@ static void drawmeta_contents(Scene *scene,
col[3] = 196; /* Alpha, used for all meta children. */
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw only immediate children (1 level depth). */
for (seq = meta_seqbase->first; seq; seq = seq->next) {
@@ -1203,7 +1158,7 @@ static void draw_seq_invalid(float x1, float x2, float y2, float text_margin_y)
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(1.0f, 0.0f, 0.0f, 0.9f);
immRectf(pos, x1, y2, x2, text_margin_y);
@@ -1321,7 +1276,7 @@ static void draw_seq_fcurve_overlay(
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO);
GPU_vertbuf_data_len_set(vbo, vert_count);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
GPU_batch_uniform_4f(batch, "color", 0.0f, 0.0f, 0.0f, 0.15f);
GPU_blend(GPU_BLEND_ALPHA);
@@ -1389,7 +1344,7 @@ static void draw_seq_strip(const bContext *C,
}
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
draw_seq_background(scene, seq, pos, x1, x2, y1, y2, is_single_image, show_strip_color_tag);
@@ -1430,18 +1385,9 @@ static void draw_seq_strip(const bContext *C,
}
/* Draw sound strip waveform. */
- if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_OVERLAY)) &&
- (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0) {
- draw_seq_waveform_overlay(v2d,
- C,
- sseq,
- scene,
- seq,
- x1,
- y_threshold ? y1 + 0.05f : y1,
- x2,
- y_threshold ? text_margin_y : y2,
- BLI_rctf_size_x(&region->v2d.cur) / region->winx);
+ if (seq_draw_waveforms_poll(C, sseq, seq)) {
+ draw_seq_waveform_overlay(
+ C, region, seq, x1, y_threshold ? y1 + 0.05f : y1, x2, y_threshold ? text_margin_y : y2);
}
/* Draw locked state. */
if (SEQ_transform_is_locked(channels, seq)) {
@@ -1454,7 +1400,7 @@ static void draw_seq_strip(const bContext *C,
}
pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
if (!SEQ_transform_is_locked(channels, seq)) {
draw_seq_handle(
@@ -1496,7 +1442,7 @@ static void draw_effect_inputs_highlight(const Scene *scene, Sequence *seq)
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ub(255, 255, 255, 48);
immRectf(pos,
@@ -1703,7 +1649,7 @@ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq,
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -1762,8 +1708,7 @@ void sequencer_draw_maskedit(const bContext *C, Scene *scene, ARegion *region, S
// ED_mask_get_size(C, &width, &height);
//Scene *scene = CTX_data_scene(C);
- width = (scene->r.size * scene->r.xsch) / 100;
- height = (scene->r.size * scene->r.ysch) / 100;
+ BKE_render_resolution(&scene->r, false, &width, &height);
ED_mask_draw_region(mask,
region,
@@ -1975,7 +1920,7 @@ static void sequencer_draw_display_buffer(const bContext *C,
GPU_texture_bind(texture, 0);
if (!glsl_used) {
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
}
@@ -2164,7 +2109,7 @@ static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq,
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
GPU_line_width(2);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
float col[3];
if (is_active_seq) {
@@ -2302,7 +2247,7 @@ void sequencer_draw_preview(const bContext *C,
static void draw_seq_timeline_channels(View2D *v2d)
{
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
immUniformThemeColor(TH_ROW_ALTERNATE);
@@ -2380,7 +2325,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ub(255, 255, 255, 48);
immRectf(pos, v2d->cur.xmin, channel, v2d->cur.xmax, channel + 1);
@@ -2397,7 +2342,7 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *region)
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4ub(255, 255, 255, 48);
immRectf(pos,
@@ -2421,7 +2366,7 @@ static void seq_draw_sfra_efra(const Scene *scene, View2D *v2d)
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw overlay outside of frame range. */
immUniformThemeColorShadeAlpha(TH_BACK, -10, -100);
@@ -2463,7 +2408,7 @@ static void seq_draw_sfra_efra(const Scene *scene, View2D *v2d)
immUnbindProgram();
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorShade(TH_BACK, -40);
immBegin(GPU_PRIM_LINES, 4);
@@ -2587,7 +2532,7 @@ static void draw_cache_view_batch(
GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, NULL, GPU_BATCH_OWNS_VBO);
if (vert_count > 0) {
GPU_vertbuf_data_len_set(vbo, vert_count);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
GPU_batch_uniform_4f(batch, "color", col_r, col_g, col_b, col_a);
GPU_batch_draw(batch);
}
@@ -2606,7 +2551,7 @@ static void draw_cache_view(const bContext *C)
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
float stripe_bot, stripe_top;
float stripe_ofs_y = UI_view2d_region_to_view_y(v2d, 1.0f) - v2d->cur.ymin;
@@ -2715,7 +2660,7 @@ static void draw_overlap_frame_indicator(const struct Scene *scene, const View2D
scene->r.cfra + scene->ed->overlay_frame_ofs;
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index cb95e9a75de..415bb5898a9 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -54,6 +54,7 @@
#include "RNA_prototypes.h"
/* For menu, popup, icons, etc. */
+#include "ED_fileselect.h"
#include "ED_keyframing.h"
#include "ED_numinput.h"
#include "ED_outliner.h"
@@ -1931,7 +1932,7 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op))
SEQ_select_active_set(scene, meta_parent);
}
- // DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
+ DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
@@ -2637,12 +2638,12 @@ static int sequencer_swap_data_exec(bContext *C, wmOperator *op)
Sequence *seq_other;
const char *error_msg;
- if (SEQ_select_active_get_pair(scene, &seq_act, &seq_other) == 0) {
+ if (SEQ_select_active_get_pair(scene, &seq_act, &seq_other) == false) {
BKE_report(op->reports, RPT_ERROR, "Please select two strips");
return OPERATOR_CANCELLED;
}
- if (SEQ_edit_sequence_swap(scene, seq_act, seq_other, &error_msg) == 0) {
+ if (SEQ_edit_sequence_swap(scene, seq_act, seq_other, &error_msg) == false) {
BKE_report(op->reports, RPT_ERROR, error_msg);
return OPERATOR_CANCELLED;
}
@@ -2869,7 +2870,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "directory", directory);
if (is_relative_path) {
- /* TODO(campbell): shouldn't this already be relative from the filesel?
+ /* TODO(@campbellbarton): shouldn't this already be relative from the filesel?
* (as the 'filepath' is) for now just make relative here,
* but look into changing after 2.60. */
BLI_path_rel(directory, BKE_main_blendfile_path(bmain));
@@ -3076,7 +3077,7 @@ static int seq_cmp_time_startdisp_channel(void *thunk, const void *a, const void
int seq_a_start = SEQ_time_left_handle_frame_get(scene, seq_a);
int seq_b_start = SEQ_time_left_handle_frame_get(scene, seq_b);
- /* If strips have the same start frame favor the one with a higher channel.*/
+ /* If strips have the same start frame favor the one with a higher channel. */
if (seq_a_start == seq_b_start) {
return seq_a->machine > seq_b->machine;
}
@@ -3088,20 +3089,7 @@ static int sequencer_export_subtitles_invoke(bContext *C,
wmOperator *op,
const wmEvent *UNUSED(event))
{
- Main *bmain = CTX_data_main(C);
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
- char filepath[FILE_MAX];
-
- if (BKE_main_blendfile_path(bmain)[0] == '\0') {
- BLI_strncpy(filepath, "untitled", sizeof(filepath));
- }
- else {
- BLI_strncpy(filepath, BKE_main_blendfile_path(bmain), sizeof(filepath));
- }
-
- BLI_path_extension_replace(filepath, sizeof(filepath), ".srt");
- RNA_string_set(op->ptr, "filepath", filepath);
- }
+ ED_fileselect_ensure_default_filepath(C, op, ".srt");
WM_event_add_fileselect(C, op);
@@ -3136,7 +3124,7 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op)
FILE *file;
char filepath[FILE_MAX];
- if (!RNA_struct_property_is_set(op->ptr, "filepath")) {
+ if (!RNA_struct_property_is_set_ex(op->ptr, "filepath", false)) {
BKE_report(op->reports, RPT_ERROR, "No filename given");
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c
index 6ba1dcc5eb8..af0aa093e40 100644
--- a/source/blender/editors/space_sequencer/sequencer_scopes.c
+++ b/source/blender/editors/space_sequencer/sequencer_scopes.c
@@ -17,7 +17,7 @@
#include "sequencer_intern.h"
-/* XXX(campbell): why is this function better than BLI_math version?
+/* XXX(@campbellbarton): why is this function better than BLI_math version?
* only difference is it does some normalize after, need to double check on this. */
static void rgb_to_yuv_normalized(const float rgb[3], float yuv[3])
{
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index 4d32c00109a..78fa8c379d9 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -12,6 +12,7 @@
#include "DNA_scene_types.h"
#include "BKE_context.h"
+#include "BKE_scene.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -174,8 +175,7 @@ static int sequencer_view_all_preview_exec(bContext *C, wmOperator *UNUSED(op))
seq_reset_imageofs(sseq);
- imgwidth = (scene->r.size * scene->r.xsch) / 100;
- imgheight = (scene->r.size * scene->r.ysch) / 100;
+ BKE_render_resolution(&scene->r, false, &imgwidth, &imgheight);
/* Apply aspect, doesn't need to be that accurate. */
imgwidth = (int)(imgwidth * (scene->r.xasp / scene->r.yasp));
@@ -227,11 +227,11 @@ static int sequencer_view_zoom_ratio_exec(bContext *C, wmOperator *op)
float ratio = RNA_float_get(op->ptr, "ratio");
- float winx = (int)(rd->size * rd->xsch) / 100;
- float winy = (int)(rd->size * rd->ysch) / 100;
+ int winx, winy;
+ BKE_render_resolution(rd, false, &winx, &winy);
- float facx = BLI_rcti_size_x(&v2d->mask) / winx;
- float facy = BLI_rcti_size_y(&v2d->mask) / winy;
+ float facx = BLI_rcti_size_x(&v2d->mask) / (float)winx;
+ float facy = BLI_rcti_size_y(&v2d->mask) / (float)winy;
BLI_rctf_resize(&v2d->cur, ceilf(winx * facx / ratio + 0.5f), ceilf(winy * facy / ratio + 0.5f));
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index 0199fa81928..508f18ade5a 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -229,7 +229,7 @@ static void sequencer_free(SpaceLink *sl)
}
}
-/* Spacetype init callback. */
+/* Space-type init callback. */
static void sequencer_init(struct wmWindowManager *UNUSED(wm), ScrArea *UNUSED(area))
{
}
@@ -374,7 +374,7 @@ static SpaceLink *sequencer_duplicate(SpaceLink *sl)
static void sequencer_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* Context changes. */
switch (wmn->category) {
@@ -630,7 +630,7 @@ static void sequencer_main_region_view2d_changed(const bContext *C, ARegion *reg
static void sequencer_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* Context changes. */
switch (wmn->category) {
@@ -862,7 +862,7 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region)
static void sequencer_preview_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
WM_gizmomap_tag_refresh(region->gizmo_map);
@@ -933,7 +933,7 @@ static void sequencer_buttons_region_draw(const bContext *C, ARegion *region)
static void sequencer_buttons_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* Context changes. */
switch (wmn->category) {
@@ -997,7 +997,7 @@ void ED_spacetype_sequencer(void)
ARegionType *art;
st->spaceid = SPACE_SEQ;
- strncpy(st->name, "Sequencer", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Sequencer");
st->create = sequencer_create;
st->free = sequencer_free;
diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt
index f134cdb95c2..173d976c124 100644
--- a/source/blender/editors/space_spreadsheet/CMakeLists.txt
+++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt
@@ -14,7 +14,6 @@ set(INC
../../makesrna
../../nodes
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index 8dbb4a2ee0c..435436611c5 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -436,7 +436,7 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region)
static void spreadsheet_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
switch (wmn->category) {
case NC_SCENE: {
@@ -486,7 +486,7 @@ static void spreadsheet_header_region_free(ARegion *UNUSED(region))
static void spreadsheet_header_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
switch (wmn->category) {
case NC_SCENE: {
@@ -570,7 +570,7 @@ static void spreadsheet_footer_region_listener(const wmRegionListenerParams *UNU
static void spreadsheet_dataset_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
switch (wmn->category) {
case NC_SCENE: {
@@ -619,7 +619,7 @@ void ED_spacetype_spreadsheet()
ARegionType *art;
st->spaceid = SPACE_SPREADSHEET;
- strncpy(st->name, "Spreadsheet", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Spreadsheet");
st->create = spreadsheet_create;
st->free = spreadsheet_free;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
index 2a87c51da5d..fd2ac4d39a1 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc
@@ -4,7 +4,9 @@
#include "BLI_virtual_array.hh"
#include "BKE_attribute.hh"
+#include "BKE_compute_contexts.hh"
#include "BKE_context.h"
+#include "BKE_curves.hh"
#include "BKE_editmesh.h"
#include "BKE_geometry_fields.hh"
#include "BKE_global.h"
@@ -22,9 +24,11 @@
#include "DEG_depsgraph_query.h"
+#include "ED_curves_sculpt.h"
#include "ED_spreadsheet.h"
-#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_geometry_nodes_lazy_function.hh"
+#include "NOD_geometry_nodes_log.hh"
#include "BLT_translation.h"
@@ -38,8 +42,8 @@
#include "spreadsheet_data_source_geometry.hh"
#include "spreadsheet_intern.hh"
-namespace geo_log = blender::nodes::geometry_nodes_eval_log;
using blender::fn::GField;
+using blender::nodes::geo_eval_log::ViewerNodeLog;
namespace blender::ed::spreadsheet {
@@ -166,45 +170,49 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values(
else if (G.debug_value == 4001 && component_->type() == GEO_COMPONENT_TYPE_MESH) {
const MeshComponent &component = static_cast<const MeshComponent &>(*component_);
if (const Mesh *mesh = component.get_for_read()) {
+ const Span<MEdge> edges = mesh->edges();
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
+
if (domain_ == ATTR_DOMAIN_EDGE) {
if (STREQ(column_id.name, "Vertex 1")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<int>::ForFunc(mesh->totedge, [mesh](int64_t index) {
- return mesh->medge[index].v1;
+ column_id.name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
+ return edges[index].v1;
}));
}
if (STREQ(column_id.name, "Vertex 2")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<int>::ForFunc(mesh->totedge, [mesh](int64_t index) {
- return mesh->medge[index].v2;
+ column_id.name, VArray<int>::ForFunc(edges.size(), [edges](int64_t index) {
+ return edges[index].v2;
}));
}
}
else if (domain_ == ATTR_DOMAIN_FACE) {
if (STREQ(column_id.name, "Corner Start")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<int>::ForFunc(mesh->totpoly, [mesh](int64_t index) {
- return mesh->mpoly[index].loopstart;
+ column_id.name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
+ return polys[index].loopstart;
}));
}
if (STREQ(column_id.name, "Corner Size")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<int>::ForFunc(mesh->totpoly, [mesh](int64_t index) {
- return mesh->mpoly[index].totloop;
+ column_id.name, VArray<int>::ForFunc(polys.size(), [polys](int64_t index) {
+ return polys[index].totloop;
}));
}
}
else if (domain_ == ATTR_DOMAIN_CORNER) {
if (STREQ(column_id.name, "Vertex")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<int>::ForFunc(mesh->totloop, [mesh](int64_t index) {
- return mesh->mloop[index].v;
+ column_id.name, VArray<int>::ForFunc(loops.size(), [loops](int64_t index) {
+ return loops[index].v;
}));
}
if (STREQ(column_id.name, "Edge")) {
return std::make_unique<ColumnValues>(
- column_id.name, VArray<int>::ForFunc(mesh->totloop, [mesh](int64_t index) {
- return mesh->mloop[index].e;
+ column_id.name, VArray<int>::ForFunc(loops.size(), [loops](int64_t index) {
+ return loops[index].e;
}));
}
}
@@ -232,74 +240,108 @@ int GeometryDataSource::tot_rows() const
return attributes.domain_size(domain_);
}
-/**
- * Only data sets corresponding to mesh objects in edit mode currently support selection filtering.
- */
bool GeometryDataSource::has_selection_filter() const
{
Object *object_orig = DEG_get_original_object(object_eval_);
- if (object_orig->type != OB_MESH) {
- return false;
- }
- if (object_orig->mode != OB_MODE_EDIT) {
- return false;
- }
- if (component_->type() != GEO_COMPONENT_TYPE_MESH) {
- return false;
+ switch (component_->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ if (object_orig->type != OB_MESH) {
+ return false;
+ }
+ if (object_orig->mode != OB_MODE_EDIT) {
+ return false;
+ }
+ return true;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ if (object_orig->type != OB_CURVES) {
+ return false;
+ }
+ if (object_orig->mode != OB_MODE_SCULPT_CURVES) {
+ return false;
+ }
+ return true;
+ }
+ default:
+ return false;
}
-
- return true;
}
IndexMask GeometryDataSource::apply_selection_filter(Vector<int64_t> &indices) const
{
std::lock_guard lock{mutex_};
const IndexMask full_range(this->tot_rows());
+ if (full_range.is_empty()) {
+ return full_range;
+ }
+
+ switch (component_->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ BLI_assert(object_eval_->type == OB_MESH);
+ BLI_assert(object_eval_->mode == OB_MODE_EDIT);
+ Object *object_orig = DEG_get_original_object(object_eval_);
+ const Mesh *mesh_eval = geometry_set_.get_mesh_for_read();
+ const bke::AttributeAccessor attributes_eval = mesh_eval->attributes();
+ Mesh *mesh_orig = (Mesh *)object_orig->data;
+ BMesh *bm = mesh_orig->edit_mesh->bm;
+ BM_mesh_elem_table_ensure(bm, BM_VERT);
+
+ const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
+ if (orig_indices != nullptr) {
+ /* Use CD_ORIGINDEX layer if it exists. */
+ VArray<bool> selection = attributes_eval.adapt_domain<bool>(
+ VArray<bool>::ForFunc(mesh_eval->totvert,
+ [bm, orig_indices](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;
+ }
+ const BMVert *vert = BM_vert_at_index(bm, i_orig);
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ }),
+ ATTR_DOMAIN_POINT,
+ domain_);
+ return index_mask_ops::find_indices_from_virtual_array(
+ full_range, selection, 1024, indices);
+ }
- 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_);
- 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);
-
- const int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
- if (orig_indices != nullptr) {
- /* Use CD_ORIGINDEX layer if it exists. */
- VArray<bool> selection = mesh_component->attributes()->adapt_domain<bool>(
- VArray<bool>::ForFunc(mesh_eval->totvert,
- [bm, orig_indices](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);
- }),
- ATTR_DOMAIN_POINT,
- domain_);
- return index_mask_ops::find_indices_from_virtual_array(full_range, selection, 1024, indices);
- }
-
- if (mesh_eval->totvert == bm->totvert) {
- /* Use a simple heuristic to match original vertices to evaluated ones. */
- VArray<bool> selection = mesh_component->attributes()->adapt_domain<bool>(
- VArray<bool>::ForFunc(mesh_eval->totvert,
- [bm](int vertex_index) -> bool {
- BMVert *vert = bm->vtable[vertex_index];
- return BM_elem_flag_test(vert, BM_ELEM_SELECT);
- }),
- ATTR_DOMAIN_POINT,
- domain_);
- return index_mask_ops::find_indices_from_virtual_array(full_range, selection, 2048, indices);
- }
-
- return full_range;
+ if (mesh_eval->totvert == bm->totvert) {
+ /* Use a simple heuristic to match original vertices to evaluated ones. */
+ VArray<bool> selection = attributes_eval.adapt_domain<bool>(
+ VArray<bool>::ForFunc(mesh_eval->totvert,
+ [bm](int vertex_index) -> bool {
+ const BMVert *vert = BM_vert_at_index(bm, vertex_index);
+ return BM_elem_flag_test(vert, BM_ELEM_SELECT);
+ }),
+ ATTR_DOMAIN_POINT,
+ domain_);
+ return index_mask_ops::find_indices_from_virtual_array(
+ full_range, selection, 2048, indices);
+ }
+
+ return full_range;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ BLI_assert(object_eval_->type == OB_CURVES);
+ BLI_assert(object_eval_->mode == OB_MODE_SCULPT_CURVES);
+ const CurveComponent &component = static_cast<const CurveComponent &>(*component_);
+ const Curves &curves_id = *component.get_for_read();
+ switch (domain_) {
+ case ATTR_DOMAIN_POINT:
+ return sculpt_paint::retrieve_selected_points(curves_id, indices);
+ case ATTR_DOMAIN_CURVE:
+ return sculpt_paint::retrieve_selected_curves(curves_id, indices);
+ default:
+ BLI_assert_unreachable();
+ }
+ return full_range;
+ }
+ default:
+ return full_range;
+ }
}
void VolumeDataSource::foreach_default_column_ids(
@@ -412,7 +454,7 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
}
else {
if (object_eval->mode == OB_MODE_EDIT && object_eval->type == OB_MESH) {
- Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval);
if (mesh == nullptr) {
return geometry_set;
}
@@ -428,19 +470,10 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread
}
}
else {
- const geo_log::NodeLog *node_log =
- geo_log::ModifierLog::find_node_by_spreadsheet_editor_context(*sspreadsheet);
- if (node_log != nullptr) {
- for (const geo_log::SocketLog &input_log : node_log->input_logs()) {
- if (const geo_log::GeometryValueLog *geo_value_log =
- dynamic_cast<const geo_log::GeometryValueLog *>(input_log.value())) {
- const GeometrySet *full_geometry = geo_value_log->full_geometry();
- if (full_geometry != nullptr) {
- geometry_set = *full_geometry;
- break;
- }
- }
- }
+ if (const ViewerNodeLog *viewer_log =
+ nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_spreadsheet(
+ *sspreadsheet)) {
+ geometry_set = viewer_log->geometry;
}
}
}
@@ -458,27 +491,11 @@ static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet,
/* No viewer is currently referenced by the context path. */
return;
}
- const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_spreadsheet_editor_context(
- *sspreadsheet);
- if (node_log == nullptr) {
- return;
- }
- for (const geo_log::SocketLog &socket_log : node_log->input_logs()) {
- const geo_log::ValueLog *value_log = socket_log.value();
- if (value_log == nullptr) {
- continue;
- }
- if (const geo_log::GFieldValueLog *field_value_log =
- dynamic_cast<const geo_log::GFieldValueLog *>(value_log)) {
- const GField &field = field_value_log->field();
- if (field) {
- r_fields.add("Viewer", std::move(field));
- }
- }
- if (const geo_log::GenericValueLog *generic_value_log =
- dynamic_cast<const geo_log::GenericValueLog *>(value_log)) {
- GPointer value = generic_value_log->value();
- r_fields.add("Viewer", fn::make_constant_field(*value.type(), value.get()));
+ if (const ViewerNodeLog *viewer_log =
+ nodes::geo_eval_log::GeoModifierLog::find_viewer_node_log_for_spreadsheet(
+ *sspreadsheet)) {
+ if (viewer_log->field) {
+ r_fields.add("Viewer", viewer_log->field);
}
}
}
@@ -526,7 +543,7 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
std::make_unique<GeometryComponentCacheKey>(component));
const eAttrDomain domain = (eAttrDomain)sspreadsheet->attribute_domain;
- const int domain_num = component.attributes()->domain_size(domain);
+ const int domain_num = component.attribute_domain_size(domain);
for (const auto item : fields_to_show.items()) {
const StringRef name = item.key;
const GField &field = item.value;
@@ -535,7 +552,7 @@ static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet,
GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() {
GArray<> evaluated_array(field.cpp_type(), domain_num);
- bke::GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
fn::FieldEvaluator field_evaluator{field_context, domain_num};
field_evaluator.add_with_destination(field, evaluated_array);
field_evaluator.evaluate();
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
index 04b4f6d8d06..71bc4768949 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh
@@ -68,10 +68,6 @@ class GeometryDataSource : public DataSource {
return object_eval_;
}
- /**
- * Only data sets corresponding to mesh objects in edit mode currently support selection
- * filtering.
- */
bool has_selection_filter() const override;
IndexMask apply_selection_filter(Vector<int64_t> &indices) const;
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
index aa9b867264a..146b6091bde 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc
@@ -145,7 +145,7 @@ void GeometryDataSetTreeViewItem::build_row(uiLayout &row)
* to the right side of the number, which it didn't have with the button. */
char element_count[7];
BLI_str_format_decimal_unit(element_count, *count);
- UI_but_hint_drawstr_set((uiBut *)this->tree_row_button(), element_count);
+ UI_but_hint_drawstr_set((uiBut *)this->view_item_button(), element_count);
}
}
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
index c7170cd1da3..e1f13f05715 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc
@@ -273,7 +273,7 @@ void draw_spreadsheet_in_region(const bContext *C,
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
draw_index_column_background(pos, region, drawer);
draw_alternating_row_overlay(pos, scroll_offset_y, region, drawer);
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
index 780dd0303cd..3fe4c7c8ee0 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc
@@ -287,7 +287,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
for (const int i : values.index_range()) {
std::stringstream ss;
const float value = values[i];
- ss << std::fixed << std::setprecision(3) << value;
+ ss << " " << std::fixed << std::setprecision(3) << value;
const std::string value_str = ss.str();
uiBut *but = uiDefIconTextBut(params.block,
UI_BTYPE_LABEL,
@@ -318,7 +318,7 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer {
for (const int i : values.index_range()) {
std::stringstream ss;
const float value = values[i];
- ss << std::fixed << std::setprecision(3) << value;
+ ss << " " << std::fixed << std::setprecision(3) << value;
const std::string value_str = ss.str();
uiBut *but = uiDefIconTextBut(params.block,
UI_BTYPE_LABEL,
diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
index 6806e185cfe..03cf0116dce 100644
--- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
+++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter.cc
@@ -71,6 +71,14 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
}
}
}
+ else if (column_data.type().is<bool>()) {
+ const bool value = (row_filter.flag & SPREADSHEET_ROW_FILTER_BOOL_VALUE) != 0;
+ apply_filter_operation(
+ column_data.typed<bool>(),
+ [&](const bool cell) { return cell == value; },
+ prev_mask,
+ new_indices);
+ }
else if (column_data.type().is<int8_t>()) {
const int value = row_filter.value_int;
switch (row_filter.operation) {
@@ -274,7 +282,6 @@ static void apply_row_filter(const SpreadsheetRowFilter &row_filter,
}
else if (column_data.type().is<InstanceReference>()) {
const StringRef value = row_filter.value_string;
-
apply_filter_operation(
column_data.typed<InstanceReference>(),
[&](const InstanceReference cell) {
diff --git a/source/blender/editors/space_statusbar/CMakeLists.txt b/source/blender/editors/space_statusbar/CMakeLists.txt
index fba40c1ec26..cf0ccd4e552 100644
--- a/source/blender/editors/space_statusbar/CMakeLists.txt
+++ b/source/blender/editors/space_statusbar/CMakeLists.txt
@@ -10,7 +10,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/space_statusbar/space_statusbar.c b/source/blender/editors/space_statusbar/space_statusbar.c
index 273c0375fb0..e99e8f21364 100644
--- a/source/blender/editors/space_statusbar/space_statusbar.c
+++ b/source/blender/editors/space_statusbar/space_statusbar.c
@@ -83,7 +83,7 @@ static void statusbar_keymap(struct wmKeyConfig *UNUSED(keyconf))
static void statusbar_header_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -136,7 +136,7 @@ void ED_spacetype_statusbar(void)
ARegionType *art;
st->spaceid = SPACE_STATUSBAR;
- strncpy(st->name, "Status Bar", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Status Bar");
st->create = statusbar_create;
st->free = statusbar_free;
diff --git a/source/blender/editors/space_text/CMakeLists.txt b/source/blender/editors/space_text/CMakeLists.txt
index 6410e971a66..38787a84fce 100644
--- a/source/blender/editors/space_text/CMakeLists.txt
+++ b/source/blender/editors/space_text/CMakeLists.txt
@@ -10,7 +10,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
)
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index ea35a8c0fa7..be9bbdf109e 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -30,6 +30,7 @@
#include "UI_view2d.h"
#include "RNA_access.h"
+#include "RNA_path.h"
#include "text_format.h"
#include "text_intern.h" /* own include */
@@ -108,7 +109,7 @@ static SpaceLink *text_duplicate(SpaceLink *sl)
static void text_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
SpaceText *st = area->spacedata.first;
/* context changes */
@@ -325,7 +326,7 @@ static void text_drop_paste(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop)
ID *id = WM_drag_get_local_ID(drag, 0);
/* copy drag path to properties */
- text = RNA_path_full_ID_py(G_MAIN, id);
+ text = RNA_path_full_ID_py(id);
RNA_string_set(drop->ptr, "text", text);
MEM_freeN(text);
}
@@ -402,7 +403,7 @@ void ED_spacetype_text(void)
ARegionType *art;
st->spaceid = SPACE_TEXT;
- strncpy(st->name, "Text", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Text");
st->create = text_create;
st->free = text_free;
diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c
index 54735a4d481..461606f63aa 100644
--- a/source/blender/editors/space_text/text_autocomplete.c
+++ b/source/blender/editors/space_text/text_autocomplete.c
@@ -314,7 +314,7 @@ static int doc_scroll = 0;
static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
- /* NOTE(campbell): this code could be refactored or rewritten. */
+ /* NOTE(@campbellbarton): this code could be refactored or rewritten. */
SpaceText *st = CTX_wm_space_text(C);
ScrArea *area = CTX_wm_area(C);
ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW);
diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c
index c93ffccd477..a976bb6c34b 100644
--- a/source/blender/editors/space_text/text_draw.c
+++ b/source/blender/editors/space_text/text_draw.c
@@ -706,7 +706,7 @@ static void text_update_drawcache(SpaceText *st, ARegion *region)
drawcache->showlinenrs = st->showlinenrs;
drawcache->tabnumber = st->tabnumber;
- strncpy(drawcache->text_id, txt->id.name, MAX_ID_NAME);
+ STRNCPY(drawcache->text_id, txt->id.name);
/* clear update flag */
drawcache->update_flag = 0;
@@ -925,12 +925,12 @@ static void calc_text_rcts(SpaceText *st, ARegion *region, rcti *scroll, rcti *b
hlstart = (lhlstart * pix_available) / ltexth;
hlend = (lhlend * pix_available) / ltexth;
- /* The scrollbar is non-linear sized. */
+ /* The scroll-bar is non-linear sized. */
if (pix_bardiff > 0) {
/* the start of the highlight is in the current viewport */
if (st->runtime.viewlines && lhlstart >= st->top &&
lhlstart <= st->top + st->runtime.viewlines) {
- /* Speed the progression of the start of the highlight through the scrollbar. */
+ /* Speed the progression of the start of the highlight through the scroll-bar. */
hlstart = (((pix_available - pix_bardiff) * lhlstart) / ltexth) +
(pix_bardiff * (lhlstart - st->top) / st->runtime.viewlines);
}
@@ -951,7 +951,7 @@ static void calc_text_rcts(SpaceText *st, ARegion *region, rcti *scroll, rcti *b
/* the end of the highlight is in the current viewport */
if (st->runtime.viewlines && lhlend >= st->top &&
lhlend <= st->top + st->runtime.viewlines) {
- /* Speed the progression of the end of the highlight through the scrollbar. */
+ /* Speed the progression of the end of the highlight through the scroll-bar. */
hlend = (((pix_available - pix_bardiff) * lhlend) / ltexth) +
(pix_bardiff * (lhlend - st->top) / st->runtime.viewlines);
}
@@ -994,10 +994,10 @@ static void draw_textscroll(const SpaceText *st, rcti *scroll, rcti *back)
float col[4];
float rad;
- /* background so highlights don't go behind the scrollbar */
+ /* Background so highlights don't go behind the scroll-bar. */
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_BACK);
immRecti(pos, back->xmin, back->ymin, back->xmax, back->ymax);
immUnbindProgram();
@@ -1076,7 +1076,7 @@ static void draw_documentation(const SpaceText *st, ARegion *region)
/* Draw panel */
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_BACK);
immRecti(pos, x, y, x + boxw, y - boxh);
@@ -1206,7 +1206,7 @@ static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_SHADE1);
immRecti(pos, x - 1, y + 1, x + boxw + 1, y - boxh - 1);
@@ -1232,7 +1232,7 @@ static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc
if (item == sel) {
uint posi = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_SHADE2);
immRecti(posi, x + margin_x, y - 3, x + margin_x + w, y + lheight - 3);
@@ -1280,7 +1280,7 @@ static void draw_text_decoration(SpaceText *st, ARegion *region)
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* Draw the selection */
if (text->curl != text->sell || text->curc != text->selc) {
@@ -1663,7 +1663,7 @@ void draw_text_main(SpaceText *st, ARegion *region)
if (st->showlinenrs) {
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_GRID);
immRecti(pos, 0, 0, TXT_NUMCOL_WIDTH(st), region->winy);
immUnbindProgram();
@@ -1726,7 +1726,7 @@ void draw_text_main(SpaceText *st, ARegion *region)
if (margin_column_x >= x) {
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
float margin_color[4];
UI_GetThemeColor4fv(TH_TEXT, margin_color);
margin_color[3] = 0.2f;
diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c
index aa361f07932..28f536ffa98 100644
--- a/source/blender/editors/space_text/text_format_py.c
+++ b/source/blender/editors/space_text/text_format_py.c
@@ -59,9 +59,9 @@ static int txtfmt_py_find_builtinfunc(const char *string)
/* clang-format off */
if (STR_LITERAL_STARTSWITH(string, "and", len)) { i = len;
- } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "assert", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "async", len)) { i = len;
+ } else if (STR_LITERAL_STARTSWITH(string, "as", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "await", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "break", len)) { i = len;
} else if (STR_LITERAL_STARTSWITH(string, "case", len)) { i = len;
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 05d51cf6362..f0196bf8e00 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -273,7 +273,7 @@ static int text_new_exec(bContext *C, wmOperator *UNUSED(op))
PointerRNA ptr, idptr;
PropertyRNA *prop;
- text = BKE_text_add(bmain, "Text");
+ text = BKE_text_add(bmain, DATA_("Text"));
/* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
@@ -3396,7 +3396,8 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm
return OPERATOR_PASS_THROUGH;
}
- if (!(event->ascii >= '0' && event->ascii <= '9')) {
+ const char event_ascii = WM_event_utf8_to_ascii(event);
+ if (!(event_ascii >= '0' && event_ascii <= '9')) {
return OPERATOR_PASS_THROUGH;
}
@@ -3406,7 +3407,7 @@ static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), const wm
}
jump_to *= 10;
- jump_to += (int)(event->ascii - '0');
+ jump_to += (int)(event_ascii - '0');
txt_move_toline(text, jump_to - 1, 0);
last_jump = time;
@@ -3495,16 +3496,8 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
char str[BLI_UTF8_MAX + 1];
- size_t len;
-
- if (event->utf8_buf[0]) {
- len = BLI_str_utf8_size_safe(event->utf8_buf);
- memcpy(str, event->utf8_buf, len);
- }
- else {
- /* in theory, ghost can set value to extended ascii here */
- len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1);
- }
+ const size_t len = BLI_str_utf8_size_safe(event->utf8_buf);
+ memcpy(str, event->utf8_buf, len);
str[len] = '\0';
RNA_string_set(op->ptr, "text", str);
diff --git a/source/blender/editors/space_topbar/CMakeLists.txt b/source/blender/editors/space_topbar/CMakeLists.txt
index 26c6b796df5..f529c855e6d 100644
--- a/source/blender/editors/space_topbar/CMakeLists.txt
+++ b/source/blender/editors/space_topbar/CMakeLists.txt
@@ -10,7 +10,6 @@ set(INC
../../makesdna
../../makesrna
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c
index bc68de1dfce..e4826ed5964 100644
--- a/source/blender/editors/space_topbar/space_topbar.c
+++ b/source/blender/editors/space_topbar/space_topbar.c
@@ -116,7 +116,7 @@ static void topbar_header_region_init(wmWindowManager *UNUSED(wm), ARegion *regi
static void topbar_main_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -146,7 +146,7 @@ static void topbar_main_region_listener(const wmRegionListenerParams *params)
static void topbar_header_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -288,7 +288,7 @@ void ED_spacetype_topbar(void)
ARegionType *art;
st->spaceid = SPACE_TOPBAR;
- strncpy(st->name, "Top Bar", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Top Bar");
st->create = topbar_create;
st->free = topbar_free;
diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c
index 1cda9cc0f0c..06a4c1d8702 100644
--- a/source/blender/editors/space_userpref/space_userpref.c
+++ b/source/blender/editors/space_userpref/space_userpref.c
@@ -189,7 +189,7 @@ void ED_spacetype_userpref(void)
ARegionType *art;
st->spaceid = SPACE_USERPREF;
- strncpy(st->name, "Userpref", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "Userpref");
st->create = userpref_create;
st->free = userpref_free;
diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt
index a76cd3377bc..27a0cd8e55a 100644
--- a/source/blender/editors/space_view3d/CMakeLists.txt
+++ b/source/blender/editors/space_view3d/CMakeLists.txt
@@ -15,7 +15,6 @@ set(INC
../../makesrna
../../render
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
../../../../intern/mantaflow/extern
@@ -61,7 +60,7 @@ set(SRC
view3d_ops.c
view3d_placement.c
view3d_project.c
- view3d_select.c
+ view3d_select.cc
view3d_snap.c
view3d_utils.c
view3d_view.c
@@ -76,7 +75,7 @@ set(LIB
)
if(WITH_PYTHON)
- blender_include_dirs(../../python)
+ list(APPEND INC ../../python)
add_definitions(-DWITH_PYTHON)
endif()
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index 8add6886584..36ced74a8b7 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -16,6 +16,7 @@
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_global.h"
+#include "BKE_mesh.h"
#include "BKE_object.h"
#include "DEG_depsgraph.h"
@@ -67,9 +68,9 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
if (facemap_data) {
GPU_blend(GPU_BLEND_ALPHA);
- const MVert *mvert = me->mvert;
- const MPoly *mpoly = me->mpoly;
- const MLoop *mloop = me->mloop;
+ const MVert *verts = BKE_mesh_verts(me);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
int mpoly_len = me->totpoly;
int mloop_len = me->totloop;
@@ -95,12 +96,12 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
int i;
if (me->runtime.looptris.array) {
const MLoopTri *mlt = me->runtime.looptris.array;
- for (mp = mpoly, i = 0; i < mpoly_len; i++, mp++) {
+ for (mp = polys, i = 0; i < mpoly_len; i++, mp++) {
if (facemap_data[i] == facemap) {
for (int j = 2; j < mp->totloop; j++) {
- copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[mloop[mlt->tri[0]].v].co);
- copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[mloop[mlt->tri[1]].v].co);
- copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[mloop[mlt->tri[2]].v].co);
+ copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), verts[loops[mlt->tri[0]].v].co);
+ copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), verts[loops[mlt->tri[1]].v].co);
+ copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), verts[loops[mlt->tri[2]].v].co);
vbo_len_used += 3;
mlt++;
}
@@ -112,15 +113,15 @@ void ED_draw_object_facemap(Depsgraph *depsgraph,
}
else {
/* No tessellation data, fan-fill. */
- for (mp = mpoly, i = 0; i < mpoly_len; i++, mp++) {
+ for (mp = polys, i = 0; i < mpoly_len; i++, mp++) {
if (facemap_data[i] == facemap) {
- const MLoop *ml_start = &mloop[mp->loopstart];
+ const MLoop *ml_start = &loops[mp->loopstart];
const MLoop *ml_a = ml_start + 1;
const MLoop *ml_b = ml_start + 2;
for (int j = 2; j < mp->totloop; j++) {
- copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[ml_start->v].co);
- copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[ml_a->v].co);
- copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), mvert[ml_b->v].co);
+ copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), verts[ml_start->v].co);
+ copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), verts[ml_a->v].co);
+ copy_v3_v3(GPU_vertbuf_raw_step(&pos_step), verts[ml_b->v].co);
vbo_len_used += 3;
ml_a++;
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index a423a842019..723a5f859a6 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -742,7 +742,7 @@ static void view3d_ob_drop_copy_external_asset(bContext *UNUSED(C), wmDrag *drag
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT);
@@ -752,6 +752,7 @@ static void view3d_ob_drop_copy_external_asset(bContext *UNUSED(C), wmDrag *drag
RNA_int_set(drop->ptr, "session_uuid", id->session_uuid);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, (Object *)id);
if (base != NULL) {
BKE_view_layer_base_select_and_set_active(view_layer, base);
@@ -791,7 +792,7 @@ static void view3d_collection_drop_copy_external_asset(bContext *UNUSED(C),
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT);
Collection *collection = (Collection *)id;
@@ -804,6 +805,7 @@ static void view3d_collection_drop_copy_external_asset(bContext *UNUSED(C),
/* Make an object active, just use the first one in the collection. */
CollectionObject *cobject = collection->gobject.first;
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = cobject ? BKE_view_layer_base_find(view_layer, cobject->ob) : NULL;
if (base) {
BLI_assert((base->flag & BASE_SELECTABLE) && (base->flag & BASE_ENABLED_VIEWPORT));
@@ -960,7 +962,7 @@ static void view3d_widgets(void)
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera);
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera_view);
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_empty_image);
- /* TODO(campbell): Not working well enough, disable for now. */
+ /* TODO(@campbellbarton): Not working well enough, disable for now. */
#if 0
WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_armature_spline);
#endif
@@ -1037,7 +1039,7 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params)
wmWindow *window = params->window;
ScrArea *area = params->area;
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
const Scene *scene = params->scene;
View3D *v3d = area->spacedata.first;
RegionView3D *rv3d = region->regiondata;
@@ -1210,6 +1212,9 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params)
break;
}
break;
+ case NC_NODE:
+ ED_region_tag_redraw(region);
+ break;
case NC_WORLD:
switch (wmn->data) {
case ND_WORLD_DRAW:
@@ -1401,8 +1406,10 @@ static void view3d_main_region_message_subscribe(const wmRegionMessageSubscribeP
WM_msg_subscribe_rna_anon_type(mbus, SceneDisplay, &msg_sub_value_region_tag_redraw);
WM_msg_subscribe_rna_anon_type(mbus, ObjectDisplay, &msg_sub_value_region_tag_redraw);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact != NULL) {
switch (obact->mode) {
case OB_MODE_PARTICLE_EDIT:
@@ -1436,8 +1443,10 @@ static void view3d_main_region_cursor(wmWindow *win, ScrArea *area, ARegion *reg
return;
}
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit) {
WM_cursor_set(win, WM_CURSOR_EDIT);
}
@@ -1464,7 +1473,7 @@ static void view3d_header_region_draw(const bContext *C, ARegion *region)
static void view3d_header_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -1501,7 +1510,7 @@ static void view3d_header_region_listener(const wmRegionListenerParams *params)
break;
}
- /* From topbar, which ones are needed? split per header? */
+ /* From top-bar, which ones are needed? split per header? */
/* Disable for now, re-enable if needed, or remove - campbell. */
#if 0
/* context changes */
@@ -1681,7 +1690,7 @@ static void view3d_buttons_region_layout(const bContext *C, ARegion *region)
static void view3d_buttons_region_listener(const wmRegionListenerParams *params)
{
ARegion *region = params->region;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
/* context changes */
switch (wmn->category) {
@@ -1804,7 +1813,7 @@ static void view3d_tools_region_draw(const bContext *C, ARegion *region)
static void space_view3d_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
- wmNotifier *wmn = params->notifier;
+ const wmNotifier *wmn = params->notifier;
View3D *v3d = area->spacedata.first;
/* context changes */
@@ -1885,11 +1894,14 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes
* without showing the object.
*
* See T85532 for alternatives that were considered. */
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- if (view_layer->basact) {
- Object *ob = view_layer->basact->object;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
+ if (base) {
+ Object *ob = base->object;
/* if hidden but in edit mode, we still display, can happen with animation */
- if ((view_layer->basact->flag & BASE_VISIBLE_DEPSGRAPH) != 0 ||
+ if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0 ||
(ob->mode != OB_MODE_OBJECT)) {
CTX_data_id_pointer_set(result, &ob->id);
}
@@ -1969,7 +1981,7 @@ void ED_spacetype_view3d(void)
ARegionType *art;
st->spaceid = SPACE_VIEW3D;
- strncpy(st->name, "View3D", BKE_ST_MAXNAME);
+ STRNCPY(st->name, "View3D");
st->create = view3d_create;
st->free = view3d_free;
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index 6786bf8404e..04824097e05 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -36,6 +36,7 @@
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
+#include "BKE_layer.h"
#include "BKE_object.h"
#include "BKE_object_deform.h"
#include "BKE_report.h"
@@ -994,7 +995,9 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float
if (apply_vcos || median->bv_weight || median->v_crease || median->skin[0] ||
median->skin[1]) {
if (median->bv_weight) {
- BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT);
+ if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) {
+ BM_data_layer_add(bm, &bm->vdata, CD_BWEIGHT);
+ }
cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
BLI_assert(cd_vert_bweight_offset != -1);
@@ -1060,7 +1063,9 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float
if (median->be_weight || median->e_crease) {
if (median->be_weight) {
- BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT);
+ if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) {
+ BM_data_layer_add(bm, &bm->edata, CD_BWEIGHT);
+ }
cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
BLI_assert(cd_edge_bweight_offset != -1);
@@ -1273,8 +1278,10 @@ static void do_view3d_vgroup_buttons(bContext *C, void *UNUSED(arg), int event)
return;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = view_layer->basact->object;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
ED_vgroup_vert_active_mirror(ob, event - B_VGRP_PNL_EDIT_SINGLE);
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
@@ -1282,8 +1289,10 @@ static void do_view3d_vgroup_buttons(bContext *C, void *UNUSED(arg), int event)
static bool view3d_panel_vgroup_poll(const bContext *C, PanelType *UNUSED(pt))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob && (BKE_object_is_in_editmode_vgroup(ob) || BKE_object_is_in_wpaint_select_vert(ob))) {
MDeformVert *dvert_act = ED_mesh_active_dvert_get_only(ob);
if (dvert_act) {
@@ -1299,7 +1308,8 @@ static void view3d_panel_vgroup(const bContext *C, Panel *panel)
uiBlock *block = uiLayoutAbsoluteBlock(panel->layout);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = view_layer->basact->object;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
MDeformVert *dv;
@@ -1681,9 +1691,11 @@ static void v3d_editmetaball_buts(uiLayout *layout, Object *ob)
static void do_view3d_region_buttons(bContext *C, void *UNUSED(index), int event)
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
switch (event) {
@@ -1710,15 +1722,19 @@ static void do_view3d_region_buttons(bContext *C, void *UNUSED(index), int event
static bool view3d_panel_transform_poll(const bContext *C, PanelType *UNUSED(pt))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- return (view_layer->basact != NULL);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ return (BKE_view_layer_active_base_get(view_layer) != NULL);
}
static void view3d_panel_transform(const bContext *C, Panel *panel)
{
uiBlock *block;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = view_layer->basact->object;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
uiLayout *col;
diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c
index 4a1bd6ba945..72e1f6f46c7 100644
--- a/source/blender/editors/space_view3d/view3d_cursor_snap.c
+++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c
@@ -16,6 +16,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_scene.h"
@@ -495,6 +496,16 @@ static void v3d_cursor_eventstate_save_xy(SnapCursorDataIntern *cursor_snap,
}
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+static void v3d_cursor_eventstate_save_modifier(SnapCursorDataIntern *data_intern,
+ const wmWindowManager *wm)
+{
+ if (!wm || !wm->winactive) {
+ return;
+ }
+ const wmEvent *event = wm->winactive->eventstate;
+ data_intern->last_eventstate.modifier = event->modifier;
+}
+
static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const wmWindowManager *wm)
{
if (!wm || !wm->winactive) {
@@ -582,10 +593,14 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
{
SnapCursorDataIntern *data_intern = &g_data_intern;
V3DSnapCursorData *snap_data = &data_intern->snap_data;
- v3d_cursor_snap_context_ensure(scene);
+
+ const bool use_surface_nor = state->plane_orient == V3D_PLACE_ORIENT_SURFACE;
+ const bool use_surface_co = state->plane_depth == V3D_PLACE_DEPTH_SURFACE;
+ const bool calc_plane_omat = v3d_cursor_snap_calc_plane();
float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3];
eSnapMode snap_elem = SCE_SNAP_MODE_NONE;
+ eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene);
int snap_elem_index[3] = {-1, -1, -1};
int index = -1;
@@ -594,83 +609,91 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
zero_v3(face_nor);
unit_m3(omat);
- eSnapMode snap_elements = v3d_cursor_snap_elements(state, scene);
- data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE;
- const bool calc_plane_omat = v3d_cursor_snap_calc_plane();
- if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) {
- data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
- snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST;
- }
+ if (use_surface_nor || use_surface_co) {
+ v3d_cursor_snap_context_ensure(scene);
+
+ data_intern->snap_elem_hidden = SCE_SNAP_MODE_NONE;
+ if (calc_plane_omat && !(snap_elements & SCE_SNAP_MODE_FACE_RAYCAST)) {
+ data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
+ snap_elements |= SCE_SNAP_MODE_FACE_RAYCAST;
+ }
- snap_data->is_enabled = true;
+ snap_data->is_enabled = true;
#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
- if (!(state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) {
- snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, wm);
-
- const ToolSettings *ts = scene->toolsettings;
- if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) {
- snap_data->is_enabled = false;
- if (!calc_plane_omat) {
- snap_data->snap_elem = SCE_SNAP_MODE_NONE;
- return;
+ if (!(state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) {
+ snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, wm);
+
+ const ToolSettings *ts = scene->toolsettings;
+ if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) {
+ snap_data->is_enabled = false;
+ if (!calc_plane_omat) {
+ snap_data->snap_elem = SCE_SNAP_MODE_NONE;
+ return;
+ }
+ snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
}
- snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE_RAYCAST;
}
- }
#endif
- if (snap_elements & SCE_SNAP_MODE_GEOM) {
- float prev_co[3] = {0.0f};
- if (state->prevpoint) {
- copy_v3_v3(prev_co, state->prevpoint);
- }
- else {
- snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR;
- }
+ if (snap_elements & SCE_SNAP_MODE_GEOM) {
+ float prev_co[3] = {0.0f};
+ if (state->prevpoint) {
+ copy_v3_v3(prev_co, state->prevpoint);
+ }
+ else {
+ snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR;
+ }
- eSnapEditType edit_mode_type = (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ?
- SNAP_GEOM_FINAL :
- (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ?
- SNAP_GEOM_CAGE :
- SNAP_GEOM_EDIT;
-
- bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : true;
-
- float dist_px = 12.0f * U.pixelsize;
-
- snap_elem = ED_transform_snap_object_project_view3d_ex(
- data_intern->snap_context_v3d,
- depsgraph,
- region,
- v3d,
- snap_elements,
- &(const struct SnapObjectParams){
- .snap_target_select = SCE_SNAP_TARGET_ALL,
- .edit_mode_type = edit_mode_type,
- .use_occlusion_test = use_occlusion_test,
- },
- NULL,
- mval_fl,
- prev_co,
- &dist_px,
- co,
- no,
- &index,
- NULL,
- obmat,
- face_nor);
+ eSnapEditType edit_mode_type = (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ?
+ SNAP_GEOM_FINAL :
+ (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ?
+ SNAP_GEOM_CAGE :
+ SNAP_GEOM_EDIT;
+
+ bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false :
+ true;
+
+ float dist_px = 12.0f * U.pixelsize;
+
+ snap_elem = ED_transform_snap_object_project_view3d_ex(
+ data_intern->snap_context_v3d,
+ depsgraph,
+ region,
+ v3d,
+ snap_elements,
+ &(const struct SnapObjectParams){
+ .snap_target_select = SCE_SNAP_TARGET_ALL,
+ .edit_mode_type = edit_mode_type,
+ .use_occlusion_test = use_occlusion_test,
+ },
+ NULL,
+ mval_fl,
+ prev_co,
+ &dist_px,
+ co,
+ no,
+ &index,
+ NULL,
+ obmat,
+ face_nor);
+ }
+ }
+#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK
+ else {
+ v3d_cursor_eventstate_save_modifier(data_intern, wm);
}
+#endif
if (calc_plane_omat) {
RegionView3D *rv3d = region->regiondata;
- bool orient_surface = (snap_elem != SCE_SNAP_MODE_NONE) &&
- (state->plane_orient == V3D_PLACE_ORIENT_SURFACE);
+ bool orient_surface = use_surface_nor && (snap_elem != SCE_SNAP_MODE_NONE);
if (orient_surface) {
copy_m3_m4(omat, obmat);
}
else {
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(CTX_data_scene(C), view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
const int 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(
@@ -715,6 +738,10 @@ static void v3d_cursor_snap_update(V3DSnapCursorState *state,
}
}
+ if (!use_surface_co) {
+ snap_elem = SCE_SNAP_MODE_NONE;
+ }
+
float *co_depth = (snap_elem != SCE_SNAP_MODE_NONE) ? co : scene->cursor.location;
snap_elem &= ~data_intern->snap_elem_hidden;
if (snap_elem == SCE_SNAP_MODE_NONE) {
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 5405867be56..219c70cffae 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -567,7 +567,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region,
/* First, solid lines. */
{
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* passepartout, specified in camera edit buttons */
if (ca && (ca->flag & CAM_SHOWPASSEPARTOUT) && ca->passepartalpha > 0.000001f) {
@@ -618,7 +618,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region,
}
/* And now, the dashed lines! */
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
{
float viewport_size[4];
@@ -800,7 +800,7 @@ static void drawrenderborder(ARegion *region, View3D *v3d)
GPU_line_width(1.0f);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -982,7 +982,7 @@ static void draw_view_axis(RegionView3D *rv3d, const rcti *rect)
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
- immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
immBegin(GPU_PRIM_LINES, 6);
for (int axis_i = 0; axis_i < 3; axis_i++) {
@@ -1306,7 +1306,8 @@ static void draw_selected_name(
s += sprintf(s, "(%d)", cfra);
if ((ob == NULL) || (ob->mode == OB_MODE_OBJECT)) {
- LayerCollection *layer_collection = view_layer->active_collection;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LayerCollection *layer_collection = BKE_view_layer_active_collection_get(view_layer);
s += sprintf(s,
" %s%s",
BKE_collection_ui_name_get(layer_collection->collection),
@@ -1352,7 +1353,7 @@ static void draw_selected_name(
}
}
else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY)) {
- /* try to display active bone and active shapekey too (if they exist) */
+ /* Try to display active bone and active shape-key too (if they exist). */
if (ob->type == OB_MESH && ob->mode & OB_MODE_WEIGHT_PAINT) {
Object *armobj = BKE_object_pose_armature_get(ob);
@@ -1497,7 +1498,8 @@ void view3d_draw_region_info(const bContext *C, ARegion *region)
}
if (U.uiflag & USER_DRAWVIEWINFO) {
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
draw_selected_name(scene, view_layer, ob, xoffset, &yoffset);
}
@@ -2121,6 +2123,7 @@ bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], const
* \note Only use in object mode.
*/
static void validate_object_select_id(struct Depsgraph *depsgraph,
+ const Scene *scene,
ViewLayer *view_layer,
ARegion *region,
View3D *v3d,
@@ -2152,7 +2155,8 @@ static void validate_object_select_id(struct Depsgraph *depsgraph,
return;
}
- if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLE_DEPSGRAPH) != 0)) {
+ if (obact_eval && ((obact_eval->base_flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, obact);
DRW_select_buffer_context_create(&base, 1, -1);
}
@@ -2188,7 +2192,8 @@ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void
void ED_view3d_select_id_validate(ViewContext *vc)
{
- validate_object_select_id(vc->depsgraph, vc->view_layer, vc->region, vc->v3d, vc->obact);
+ validate_object_select_id(
+ vc->depsgraph, vc->scene, vc->view_layer, vc->region, vc->v3d, vc->obact);
}
int ED_view3d_backbuf_sample_size_clamp(ARegion *region, const float dist)
@@ -2247,16 +2252,16 @@ void view3d_depths_rect_create(ARegion *region, rcti *rect, ViewDepths *r_d)
static ViewDepths *view3d_depths_create(ARegion *region)
{
ViewDepths *d = MEM_callocN(sizeof(ViewDepths), "ViewDepths");
- d->w = region->winx;
- d->h = region->winy;
{
GPUViewport *viewport = WM_draw_region_get_viewport(region);
GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport);
uint32_t *int_depths = GPU_texture_read(depth_tx, GPU_DATA_UINT_24_8, 0);
+ d->w = GPU_texture_width(depth_tx);
+ d->h = GPU_texture_height(depth_tx);
d->depths = (float *)int_depths;
/* Convert in-place. */
- int pixel_count = GPU_texture_width(depth_tx) * GPU_texture_height(depth_tx);
+ int pixel_count = d->w * d->h;
for (int i = 0; i < pixel_count; i++) {
d->depths[i] = (int_depths[i] >> 8u) / (float)0xFFFFFF;
}
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 041663b4a00..6001f701c00 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -413,7 +413,9 @@ static void view3d_set_1_to_1_viewborder(Scene *scene,
{
RegionView3D *rv3d = region->regiondata;
float size[2];
- int im_width = (scene->r.size * scene->r.xsch) / 100;
+
+ int im_width, im_height;
+ BKE_render_resolution(&scene->r, false, &im_width, &im_height);
ED_view3d_calc_camera_border_size(scene, depsgraph, region, v3d, rv3d, size);
@@ -695,7 +697,7 @@ static int drop_world_exec(bContext *C, wmOperator *op)
id_us_plus(&world->id);
scene->world = world;
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_SCENE | ND_WORLD, scene);
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c
index 3f6167d92ca..4f73e2fada2 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c
@@ -37,7 +37,7 @@
* \{ */
/*
- * TODO(campbell): Current conversion is a approximation (usable not correct),
+ * TODO(@campbellbarton): Current conversion is a approximation (usable not correct),
* we'll need to take the next/previous bones into account to get the tangent directions.
* First last matrices from 'BKE_pchan_bbone_spline_setup' are close but also not quite accurate
* since they're not at either end-points on the curve.
@@ -113,8 +113,10 @@ static bool WIDGETGROUP_armature_spline_poll(const bContext *C, wmGizmoGroupType
return false;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
if (base && BASE_SELECTABLE(v3d, base)) {
Object *ob = BKE_object_pose_armature_get(base->object);
if (ob) {
@@ -132,8 +134,10 @@ static bool WIDGETGROUP_armature_spline_poll(const bContext *C, wmGizmoGroupType
static void WIDGETGROUP_armature_spline_setup(const bContext *C, wmGizmoGroup *gzgroup)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = BKE_object_pose_armature_get(OBACT(view_layer));
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_object_pose_armature_get(BKE_view_layer_active_object_get(view_layer));
bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
const wmGizmoType *gzt_move = WM_gizmotype_find("GIZMO_GT_move_3d", true);
@@ -165,8 +169,10 @@ static void WIDGETGROUP_armature_spline_setup(const bContext *C, wmGizmoGroup *g
static void WIDGETGROUP_armature_spline_refresh(const bContext *C, wmGizmoGroup *gzgroup)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = BKE_object_pose_armature_get(OBACT(view_layer));
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_object_pose_armature_get(BKE_view_layer_active_object_get(view_layer));
if (!gzgroup->customdata) {
return;
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_camera.c b/source/blender/editors/space_view3d/view3d_gizmo_camera.c
index 83f589a64c9..952ef56710b 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_camera.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_camera.c
@@ -55,8 +55,10 @@ static bool WIDGETGROUP_camera_poll(const bContext *C, wmGizmoGroupType *UNUSED(
return false;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
if (base && BASE_SELECTABLE(v3d, base)) {
Object *ob = base->object;
if (ob->type == OB_CAMERA) {
@@ -72,8 +74,10 @@ static bool WIDGETGROUP_camera_poll(const bContext *C, wmGizmoGroupType *UNUSED(
static void WIDGETGROUP_camera_setup(const bContext *C, wmGizmoGroup *gzgroup)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
float dir[3];
const wmGizmoType *gzt_arrow = WM_gizmotype_find("GIZMO_GT_arrow_3d", true);
@@ -124,8 +128,10 @@ static void WIDGETGROUP_camera_refresh(const bContext *C, wmGizmoGroup *gzgroup)
struct CameraWidgetGroup *cagzgroup = gzgroup->customdata;
View3D *v3d = CTX_wm_view3d(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Camera *ca = ob->data;
PointerRNA camera_ptr;
float dir[3];
@@ -151,7 +157,6 @@ static void WIDGETGROUP_camera_refresh(const bContext *C, wmGizmoGroup *gzgroup)
}
/* TODO: make focal length/ortho ob_scale_inv widget optional. */
- const Scene *scene = CTX_data_scene(C);
const float aspx = (float)scene->r.xsch * scene->r.xasp;
const float aspy = (float)scene->r.ysch * scene->r.yasp;
const bool is_ortho = (ca->type == CAM_ORTHO);
@@ -241,8 +246,10 @@ static void WIDGETGROUP_camera_message_subscribe(const bContext *C,
struct wmMsgBus *mbus)
{
ARegion *region = CTX_wm_region(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Camera *ca = ob->data;
wmMsgSubscribeValue msg_sub_value_gz_tag_refresh = {
@@ -370,7 +377,8 @@ static bool WIDGETGROUP_camera_view_poll(const bContext *C, wmGizmoGroupType *UN
* We could change the rules for when to show. */
{
ViewLayer *view_layer = CTX_data_view_layer(C);
- if (scene->camera != OBACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (scene->camera != BKE_view_layer_active_object_get(view_layer)) {
return false;
}
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_empty.c b/source/blender/editors/space_view3d/view3d_gizmo_empty.c
index f113cc60224..41a763192ce 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_empty.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_empty.c
@@ -99,8 +99,10 @@ static bool WIDGETGROUP_empty_image_poll(const bContext *C, wmGizmoGroupType *UN
return false;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
if (base && BASE_SELECTABLE(v3d, base)) {
Object *ob = base->object;
if (ob->type == OB_EMPTY) {
@@ -132,8 +134,10 @@ static void WIDGETGROUP_empty_image_refresh(const bContext *C, wmGizmoGroup *gzg
{
struct EmptyImageWidgetGroup *igzgroup = gzgroup->customdata;
wmGizmo *gz = igzgroup->gizmo;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
copy_m4_m4(gz->matrix_basis, ob->obmat);
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c b/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c
index 456e939eba7..58b43301397 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_forcefield.c
@@ -42,8 +42,10 @@ static bool WIDGETGROUP_forcefield_poll(const bContext *C, wmGizmoGroupType *UNU
return false;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
if (base && BASE_SELECTABLE(v3d, base)) {
Object *ob = base->object;
if (ob->pd && ob->pd->forcefield) {
@@ -73,8 +75,10 @@ static void WIDGETGROUP_forcefield_refresh(const bContext *C, wmGizmoGroup *gzgr
{
wmGizmoWrapper *wwrapper = gzgroup->customdata;
wmGizmo *gz = wwrapper->gizmo;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
PartDeflect *pd = ob->pd;
if (pd->forcefield == PFIELD_WIND) {
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_light.c b/source/blender/editors/space_view3d/view3d_gizmo_light.c
index b3bc0bc70cb..df653f9a6e5 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_light.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_light.c
@@ -45,8 +45,10 @@ static bool WIDGETGROUP_light_spot_poll(const bContext *C, wmGizmoGroupType *UNU
return false;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
if (base && BASE_SELECTABLE(v3d, base)) {
Object *ob = base->object;
if (ob->type == OB_LAMP) {
@@ -76,8 +78,10 @@ static void WIDGETGROUP_light_spot_refresh(const bContext *C, wmGizmoGroup *gzgr
{
wmGizmoWrapper *wwrapper = gzgroup->customdata;
wmGizmo *gz = wwrapper->gizmo;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Light *la = ob->data;
float dir[3];
@@ -156,8 +160,10 @@ static bool WIDGETGROUP_light_area_poll(const bContext *C, wmGizmoGroupType *UNU
return false;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
if (base && BASE_SELECTABLE(v3d, base)) {
Object *ob = base->object;
if (ob->type == OB_LAMP) {
@@ -186,8 +192,10 @@ static void WIDGETGROUP_light_area_setup(const bContext *UNUSED(C), wmGizmoGroup
static void WIDGETGROUP_light_area_refresh(const bContext *C, wmGizmoGroup *gzgroup)
{
wmGizmoWrapper *wwrapper = gzgroup->customdata;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Light *la = ob->data;
wmGizmo *gz = wwrapper->gizmo;
@@ -239,8 +247,10 @@ static bool WIDGETGROUP_light_target_poll(const bContext *C, wmGizmoGroupType *U
return false;
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
if (base && BASE_SELECTABLE(v3d, base)) {
Object *ob = base->object;
if (ob->type == OB_LAMP) {
@@ -280,8 +290,10 @@ static void WIDGETGROUP_light_target_setup(const bContext *UNUSED(C), wmGizmoGro
static void WIDGETGROUP_light_target_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup)
{
wmGizmoWrapper *wwrapper = gzgroup->customdata;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
wmGizmo *gz = wwrapper->gizmo;
normalize_m4_m4(gz->matrix_basis, ob->obmat);
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 a0c010a6813..d0f6ca4c922 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect_type.c
@@ -125,12 +125,15 @@ static int gizmo_preselect_elem_test_select(bContext *C, wmGizmo *gz, const int
};
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- if (((gz_ele->bases)) == NULL || (gz_ele->bases[0] != view_layer->basact)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (((gz_ele->bases)) == NULL ||
+ (gz_ele->bases[0] != BKE_view_layer_active_base_get(view_layer))) {
MEM_SAFE_FREE(gz_ele->bases);
gz_ele->bases = BKE_view_layer_array_from_bases_in_edit_mode(
- view_layer, v3d, &gz_ele->bases_len);
+ scene, view_layer, v3d, &gz_ele->bases_len);
}
}
@@ -351,12 +354,15 @@ static int gizmo_preselect_edgering_test_select(bContext *C, wmGizmo *gz, const
};
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- if (((gz_ring->bases)) == NULL || (gz_ring->bases[0] != view_layer->basact)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (((gz_ring->bases)) == NULL ||
+ (gz_ring->bases[0] != BKE_view_layer_active_base_get(view_layer))) {
MEM_SAFE_FREE(gz_ring->bases);
gz_ring->bases = BKE_view_layer_array_from_bases_in_edit_mode(
- view_layer, v3d, &gz_ring->bases_len);
+ scene, view_layer, v3d, &gz_ring->bases_len);
}
}
@@ -488,6 +494,7 @@ void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C,
Base **r_base,
BMElem **r_ele)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const int object_index = RNA_int_get(gz->ptr, "object_index");
@@ -498,7 +505,7 @@ void ED_view3d_gizmo_mesh_preselect_get_active(bContext *C,
{
uint bases_len;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
- view_layer, CTX_wm_view3d(C), &bases_len);
+ scene, view_layer, CTX_wm_view3d(C), &bases_len);
if (object_index < bases_len) {
base = bases[object_index];
obedit = base->object;
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
index 62dc461e05c..d95d49dd982 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c
@@ -17,6 +17,7 @@
#include "BKE_main.h"
#include "BKE_report.h"
+#include "BKE_layer.h"
#include "BKE_material.h"
#include "BKE_object.h"
#include "BKE_scene.h"
@@ -420,7 +421,8 @@ static bool view3d_ruler_item_mousemove(const bContext *C,
Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
RegionView3D *rv3d = ruler_info->region->regiondata;
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
@@ -783,7 +785,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
if (ruler_item->flag & RULERITEM_USE_ANGLE) {
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* capping */
{
float rot_90_vec_a[2];
@@ -885,7 +887,7 @@ static void gizmo_ruler_draw(const bContext *C, wmGizmo *gz)
}
}
else {
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
sub_v2_v2v2(dir_ruler, co_ss[0], co_ss[2]);
diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c
index 6e8d9e96abd..45f7a3a8fe9 100644
--- a/source/blender/editors/space_view3d/view3d_header.c
+++ b/source/blender/editors/space_view3d/view3d_header.c
@@ -20,6 +20,7 @@
#include "BKE_context.h"
#include "BKE_editmesh.h"
+#include "BKE_layer.h"
#include "DEG_depsgraph.h"
@@ -124,8 +125,10 @@ void uiTemplateEditModeSelection(uiLayout *layout, struct bContext *C)
static void uiTemplatePaintModeSelection(uiLayout *layout, struct bContext *C)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
/* Gizmos aren't used in paint modes */
if (!ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_PARTICLE_EDIT)) {
@@ -146,8 +149,10 @@ static void uiTemplatePaintModeSelection(uiLayout *layout, struct bContext *C)
void uiTemplateHeader3D_mode(uiLayout *layout, struct bContext *C)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = CTX_data_edit_object(C);
bGPdata *gpd = CTX_data_gpencil_data(C);
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 53fc450107a..4c9e2595023 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -9,6 +9,10 @@
#include "ED_view3d.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* internal exports only */
struct ARegion;
@@ -83,7 +87,7 @@ void view3d_depths_rect_create(struct ARegion *region, struct rcti *rect, struct
*/
float view3d_depth_near(struct ViewDepths *d);
-/* view3d_select.c */
+/* view3d_select.cc */
void VIEW3D_OT_select(struct wmOperatorType *ot);
void VIEW3D_OT_select_circle(struct wmOperatorType *ot);
@@ -241,3 +245,7 @@ void VIEW3D_GGT_placement(struct wmGizmoGroupType *gzgt);
extern uchar view3d_camera_border_hack_col[3];
extern bool view3d_camera_border_hack_test;
#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c
index 35d4746608b..34f68e87880 100644
--- a/source/blender/editors/space_view3d/view3d_iterators.c
+++ b/source/blender/editors/space_view3d/view3d_iterators.c
@@ -23,6 +23,7 @@
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_editmesh.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_iterators.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
@@ -205,6 +206,8 @@ typedef struct foreachScreenObjectVert_userData {
void (*func)(void *userData, MVert *mv, const float screen_co[2], int index);
void *userData;
ViewContext vc;
+ MVert *verts;
+ const bool *hide_vert;
eV3DProjTest clip_flag;
} foreachScreenObjectVert_userData;
@@ -244,7 +247,7 @@ typedef struct foreachScreenFace_userData {
} foreachScreenFace_userData;
/**
- * \note foreach funcs should be called while drawing or directly after
+ * \note foreach functions should be called while drawing or directly after
* if not, #ED_view3d_init_mats_rv3d() can be used for selection tools
* but would not give correct results with dupli's for eg. which don't
* use the object matrix in the usual way.
@@ -262,18 +265,19 @@ static void meshobject_foreachScreenVert__mapFunc(void *userData,
const float UNUSED(no[3]))
{
foreachScreenObjectVert_userData *data = userData;
- struct MVert *mv = &((Mesh *)(data->vc.obact->data))->mvert[index];
-
- if (!(mv->flag & ME_HIDE)) {
- float screen_co[2];
+ if (data->hide_vert && data->hide_vert[index]) {
+ return;
+ }
+ MVert *mv = &data->verts[index];
- if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) !=
- V3D_PROJ_RET_OK) {
- return;
- }
+ float screen_co[2];
- data->func(data->userData, mv, screen_co, index);
+ if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) !=
+ V3D_PROJ_RET_OK) {
+ return;
}
+
+ data->func(data->userData, mv, screen_co, index);
}
void meshobject_foreachScreenVert(
@@ -297,6 +301,9 @@ void meshobject_foreachScreenVert(
data.func = func;
data.userData = userData;
data.clip_flag = clip_flag;
+ data.verts = BKE_mesh_verts_for_write((Mesh *)vc->obact->data);
+ data.hide_vert = (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert");
if (clip_flag & V3D_PROJ_TEST_CLIP_BB) {
ED_view3d_clipping_local(vc->rv3d, vc->obact->obmat);
diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c
index 50d7626a57d..b27c65c42ef 100644
--- a/source/blender/editors/space_view3d/view3d_navigate.c
+++ b/source/blender/editors/space_view3d/view3d_navigate.c
@@ -164,9 +164,11 @@ bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
View3D *v3d = CTX_wm_view3d(C);
- Object *ob_act_eval = OBACT(view_layer_eval);
+ BKE_view_layer_synced_ensure(scene_eval, view_layer_eval);
+ Object *ob_act_eval = BKE_view_layer_active_object_get(view_layer_eval);
Object *ob_act = DEG_get_original_object(ob_act_eval);
if (ob_act && (ob_act->mode & OB_MODE_ALL_PAINT) &&
@@ -203,12 +205,11 @@ bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
}
else if (ob_act == NULL || ob_act->mode == OB_MODE_OBJECT) {
/* object mode use boundbox centers */
- Base *base_eval;
uint tot = 0;
float select_center[3];
zero_v3(select_center);
- for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) {
+ LISTBASE_FOREACH (Base *, base_eval, BKE_view_layer_object_bases_get(view_layer_eval)) {
if (BASE_SELECTED(v3d, base_eval)) {
/* use the boundbox if we can */
Object *ob_eval = base_eval->object;
@@ -495,6 +496,8 @@ static void axis_set_view(bContext *C,
.camera_old = v3d->camera,
.ofs = rv3d->ofs,
.quat = quat,
+ /* No undo because this switches to/from camera. */
+ .undo_str = NULL,
});
}
else if (orig_persp == RV3D_CAMOB && v3d->camera) {
@@ -518,6 +521,8 @@ static void axis_set_view(bContext *C,
.ofs = ofs,
.quat = quat,
.dist = &dist,
+ /* No undo because this switches to/from camera. */
+ .undo_str = NULL,
});
}
else {
@@ -540,6 +545,8 @@ static void axis_set_view(bContext *C,
&(const V3D_SmoothParams){
.quat = quat,
.dyn_ofs = dyn_ofs_pt,
+ /* No undo because this isn't a camera view. */
+ .undo_str = NULL,
});
}
}
@@ -694,6 +701,8 @@ static void view3d_from_minmax(bContext *C,
.camera_old = v3d->camera,
.ofs = new_ofs,
.dist = ok_dist ? &new_dist : NULL,
+ /* The caller needs to use undo begin/end calls. */
+ .undo_str = NULL,
});
}
else {
@@ -704,6 +713,8 @@ static void view3d_from_minmax(bContext *C,
&(const V3D_SmoothParams){
.ofs = new_ofs,
.dist = ok_dist ? &new_dist : NULL,
+ /* The caller needs to use undo begin/end calls. */
+ .undo_str = NULL,
});
}
@@ -736,13 +747,15 @@ static void view3d_from_minmax_multi(bContext *C,
static int view3d_all_exec(bContext *C, wmOperator *op)
{
+ ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
- Base *base_eval;
+
const bool use_all_regions = RNA_boolean_get(op->ptr, "use_all_regions");
const bool skip_camera = (ED_view3d_camera_lock_check(v3d, region->regiondata) ||
/* any one of the regions may be locked */
@@ -767,7 +780,8 @@ static int view3d_all_exec(bContext *C, wmOperator *op)
INIT_MINMAX(min, max);
}
- for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) {
+ BKE_view_layer_synced_ensure(scene_eval, view_layer_eval);
+ LISTBASE_FOREACH (Base *, base_eval, BKE_view_layer_object_bases_get(view_layer_eval)) {
if (BASE_VISIBLE(v3d, base_eval)) {
bool only_center = false;
Object *ob = DEG_get_original_object(base_eval->object);
@@ -802,6 +816,7 @@ static int view3d_all_exec(bContext *C, wmOperator *op)
/* This is an approximation, see function documentation for details. */
ED_view3d_clipping_clamp_minmax(rv3d, min, max);
}
+ ED_view3d_smooth_view_undo_begin(C, area);
if (use_all_regions) {
view3d_from_minmax_multi(C, v3d, min, max, true, smooth_viewtx);
@@ -810,6 +825,8 @@ static int view3d_all_exec(bContext *C, wmOperator *op)
view3d_from_minmax(C, v3d, region, min, max, true, smooth_viewtx);
}
+ ED_view3d_smooth_view_undo_end(C, area, op->type->name, false);
+
return OPERATOR_FINISHED;
}
@@ -842,13 +859,16 @@ void VIEW3D_OT_view_all(wmOperatorType *ot)
static int viewselected_exec(bContext *C, wmOperator *op)
{
+ ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = CTX_wm_region_view3d(C);
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ const Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph);
- Object *ob_eval = OBACT(view_layer_eval);
+ BKE_view_layer_synced_ensure(scene_eval, view_layer_eval);
+ Object *ob_eval = BKE_view_layer_active_object_get(view_layer_eval);
Object *obedit = CTX_data_edit_object(C);
const bGPdata *gpd_eval = ob_eval && (ob_eval->type == OB_GPENCIL) ? ob_eval->data : NULL;
const bool is_gp_edit = gpd_eval ? GPENCIL_ANY_MODE(gpd_eval) : false;
@@ -872,7 +892,8 @@ static int viewselected_exec(bContext *C, wmOperator *op)
/* this is weak code this way, we should make a generic
* active/selection callback interface once... */
Base *base_eval;
- for (base_eval = view_layer_eval->object_bases.first; base_eval; base_eval = base_eval->next) {
+ for (base_eval = BKE_view_layer_object_bases_get(view_layer_eval)->first; base_eval;
+ base_eval = base_eval->next) {
if (BASE_SELECTED_EDITABLE(v3d, base_eval)) {
if (base_eval->object->type == OB_ARMATURE) {
if (base_eval->object->mode & OB_MODE_POSE) {
@@ -922,14 +943,15 @@ static int viewselected_exec(bContext *C, wmOperator *op)
}
else if (obedit) {
/* only selected */
- FOREACH_OBJECT_IN_MODE_BEGIN (view_layer_eval, v3d, obedit->type, obedit->mode, ob_eval_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (
+ scene_eval, view_layer_eval, v3d, obedit->type, obedit->mode, ob_eval_iter) {
ok |= ED_view3d_minmax_verts(ob_eval_iter, min, max);
}
FOREACH_OBJECT_IN_MODE_END;
}
else if (ob_eval && (ob_eval->mode & OB_MODE_POSE)) {
FOREACH_OBJECT_IN_MODE_BEGIN (
- view_layer_eval, v3d, ob_eval->type, ob_eval->mode, ob_eval_iter) {
+ scene_eval, view_layer_eval, v3d, ob_eval->type, ob_eval->mode, ob_eval_iter) {
ok |= BKE_pose_minmax(ob_eval_iter, min, max, true, true);
}
FOREACH_OBJECT_IN_MODE_END;
@@ -948,8 +970,7 @@ static int viewselected_exec(bContext *C, wmOperator *op)
ok_dist = 0; /* don't zoom */
}
else {
- Base *base_eval;
- for (base_eval = FIRSTBASE(view_layer_eval); base_eval; base_eval = base_eval->next) {
+ LISTBASE_FOREACH (Base *, base_eval, BKE_view_layer_object_bases_get(view_layer_eval)) {
if (BASE_SELECTED(v3d, base_eval)) {
bool only_center = false;
Object *ob = DEG_get_original_object(base_eval->object);
@@ -971,6 +992,8 @@ static int viewselected_exec(bContext *C, wmOperator *op)
ED_view3d_clipping_clamp_minmax(rv3d, min, max);
}
+ ED_view3d_smooth_view_undo_begin(C, area);
+
if (use_all_regions) {
view3d_from_minmax_multi(C, v3d, min, max, ok_dist, smooth_viewtx);
}
@@ -978,6 +1001,8 @@ static int viewselected_exec(bContext *C, wmOperator *op)
view3d_from_minmax(C, v3d, region, min, max, ok_dist, smooth_viewtx);
}
+ ED_view3d_smooth_view_undo_end(C, area, op->type->name, false);
+
return OPERATOR_FINISHED;
}
@@ -1020,8 +1045,14 @@ static int viewcenter_cursor_exec(bContext *C, wmOperator *op)
/* non camera center */
float new_ofs[3];
negate_v3_v3(new_ofs, scene->cursor.location);
- ED_view3d_smooth_view(
- C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs});
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .ofs = new_ofs,
+ .undo_str = op->type->name,
+ });
/* Smooth view does view-lock #RV3D_BOXVIEW copy. */
}
@@ -1074,8 +1105,14 @@ static int viewcenter_pick_invoke(bContext *C, wmOperator *op, const wmEvent *ev
ED_view3d_win_to_3d_int(v3d, region, new_ofs, event->mval, new_ofs);
}
negate_v3(new_ofs);
- ED_view3d_smooth_view(
- C, v3d, region, smooth_viewtx, &(const V3D_SmoothParams){.ofs = new_ofs});
+ ED_view3d_smooth_view(C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .ofs = new_ofs,
+ .undo_str = op->type->name,
+ });
}
return OPERATOR_FINISHED;
@@ -1138,10 +1175,12 @@ static int view_axis_exec(bContext *C, wmOperator *op)
Object *obact = CTX_data_active_object(C);
if (obact != NULL) {
float twmat[3][3];
+ const Scene *scene = CTX_data_scene(C);
struct ViewLayer *view_layer = CTX_data_view_layer(C);
Object *obedit = CTX_data_edit_object(C);
/* same as transform gizmo when normal is set */
- ED_getTransformOrientationMatrix(view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat);
+ ED_getTransformOrientationMatrix(
+ scene, view_layer, v3d, obact, obedit, V3D_AROUND_ACTIVE, twmat);
align_quat = align_quat_buf;
mat3_to_quat(align_quat, twmat);
invert_qt_normalized(align_quat);
@@ -1275,7 +1314,8 @@ static int view_camera_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
if (rv3d->persp != RV3D_CAMOB) {
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (!rv3d->smooth_timer) {
/* store settings of current view before allowing overwriting with camera view
@@ -1302,7 +1342,7 @@ static int view_camera_exec(bContext *C, wmOperator *op)
}
if (v3d->camera == NULL) {
- v3d->camera = BKE_view_layer_camera_find(view_layer);
+ v3d->camera = BKE_view_layer_camera_find(scene, view_layer);
}
/* couldn't find any useful camera, bail out */
@@ -1318,17 +1358,20 @@ static int view_camera_exec(bContext *C, wmOperator *op)
/* finally do snazzy view zooming */
rv3d->persp = RV3D_CAMOB;
- ED_view3d_smooth_view(C,
- v3d,
- region,
- smooth_viewtx,
- &(const V3D_SmoothParams){
- .camera = v3d->camera,
- .ofs = rv3d->ofs,
- .quat = rv3d->viewquat,
- .dist = &rv3d->dist,
- .lens = &v3d->lens,
- });
+ ED_view3d_smooth_view(
+ C,
+ v3d,
+ region,
+ smooth_viewtx,
+ &(const V3D_SmoothParams){
+ .camera = v3d->camera,
+ .ofs = rv3d->ofs,
+ .quat = rv3d->viewquat,
+ .dist = &rv3d->dist,
+ .lens = &v3d->lens,
+ /* No undo because this changes cameras (and wont move the camera). */
+ .undo_str = NULL,
+ });
}
else {
/* return to settings of last view */
@@ -1417,7 +1460,12 @@ static int vieworbit_exec(bContext *C, wmOperator *op)
ED_view3d_smooth_view_force_finish(C, v3d, region);
if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0 || (view_opposite != RV3D_VIEW_USER)) {
- if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
+ const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d);
+ if ((rv3d->persp != RV3D_CAMOB) || is_camera_lock) {
+ if (is_camera_lock) {
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
+ }
int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
float quat_mul[4];
float quat_new[4];
@@ -1475,6 +1523,9 @@ static int vieworbit_exec(bContext *C, wmOperator *op)
&(const V3D_SmoothParams){
.quat = quat_new,
.dyn_ofs = dyn_ofs_pt,
+ /* Group as successive orbit may run by holding a key. */
+ .undo_str = op->type->name,
+ .undo_grouped = true,
});
return OPERATOR_FINISHED;
@@ -1554,6 +1605,7 @@ static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
viewmove_apply(vod, vod->prev.event_xy[0] + x, vod->prev.event_xy[1] + y);
+ ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
viewops_data_free(C, vod);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h
index fc7bc11295a..925acd90573 100644
--- a/source/blender/editors/space_view3d/view3d_navigate.h
+++ b/source/blender/editors/space_view3d/view3d_navigate.h
@@ -231,6 +231,14 @@ typedef struct V3D_SmoothParams {
/** Alternate rotation center, when set `ofs` must be NULL. */
const float *dyn_ofs;
+
+ /** When non-NULL, perform undo pushes when transforming the camera. */
+ const char *undo_str;
+ /**
+ * When true use grouped undo pushes, use for incremental viewport manipulation
+ * which are likely to be activated by holding a key or from the mouse-wheel.
+ */
+ bool undo_grouped;
} V3D_SmoothParams;
/**
@@ -252,6 +260,22 @@ void ED_view3d_smooth_view(struct bContext *C,
const V3D_SmoothParams *sview);
/**
+ * Call before multiple smooth-view operations begin to properly handle undo.
+ *
+ * \note Only use explicit undo calls when multiple calls to smooth-view are necessary
+ * or when calling #ED_view3d_smooth_view_ex.
+ * Otherwise pass in #V3D_SmoothParams.undo_str so an undo step is pushed as needed.
+ */
+void ED_view3d_smooth_view_undo_begin(struct bContext *C, const struct ScrArea *area);
+/**
+ * Run after multiple smooth-view operations have run to push undo as needed.
+ */
+void ED_view3d_smooth_view_undo_end(struct bContext *C,
+ const struct ScrArea *area,
+ const char *undo_str,
+ bool undo_grouped);
+
+/**
* Apply the smooth-view immediately, use when we need to start a new view operation.
* (so we don't end up half-applying a view operation when pressing keys quickly).
*/
diff --git a/source/blender/editors/space_view3d/view3d_navigate_dolly.c b/source/blender/editors/space_view3d/view3d_navigate_dolly.c
index d45b0c436ac..df0f4e6e94b 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_dolly.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_dolly.c
@@ -41,7 +41,7 @@ void viewdolly_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Dolly Modal");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -181,6 +181,7 @@ static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (ret & OPERATOR_FINISHED) {
+ ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
viewops_data_free(C, vod);
op->customdata = NULL;
}
diff --git a/source/blender/editors/space_view3d/view3d_navigate_fly.c b/source/blender/editors/space_view3d/view3d_navigate_fly.c
index 399f422f411..3e83f8085c7 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_fly.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_fly.c
@@ -122,7 +122,7 @@ void fly_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Fly Modal");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -247,7 +247,7 @@ static void drawFlyPixel(const struct bContext *UNUSED(C), ARegion *UNUSED(regio
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor3(TH_VIEW_OVERLAY);
@@ -1079,6 +1079,7 @@ static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event)
int exit_code;
bool do_draw = false;
FlyInfo *fly = op->customdata;
+ View3D *v3d = fly->v3d;
RegionView3D *rv3d = fly->rv3d;
Object *fly_object = ED_view3d_cameracontrol_object_get(fly->v3d_camera_control);
@@ -1102,6 +1103,9 @@ static int fly_modal(bContext *C, wmOperator *op, const wmEvent *event)
exit_code = flyEnd(C, fly);
+ if (exit_code == OPERATOR_FINISHED) {
+ ED_view3d_camera_lock_undo_push(op->type->name, v3d, rv3d, C);
+ }
if (exit_code != OPERATOR_RUNNING_MODAL) {
do_draw = true;
}
diff --git a/source/blender/editors/space_view3d/view3d_navigate_move.c b/source/blender/editors/space_view3d/view3d_navigate_move.c
index e653b349a2f..9de0a2ae4c2 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_move.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_move.c
@@ -35,7 +35,7 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Move Modal");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -140,6 +140,7 @@ static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (ret & OPERATOR_FINISHED) {
+ ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
viewops_data_free(C, op->customdata);
op->customdata = NULL;
}
diff --git a/source/blender/editors/space_view3d/view3d_navigate_ndof.c b/source/blender/editors/space_view3d/view3d_navigate_ndof.c
index 1ce9bdcb211..88abf602c26 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_ndof.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_ndof.c
@@ -373,6 +373,9 @@ static int view3d_ndof_cameraview_pan_zoom(bContext *C, const wmEvent *event)
const bool has_translate = !is_zero_v2(ndof->tvec);
const bool has_zoom = ndof->tvec[2] != 0.0f;
+ float pan_vec[3];
+ WM_event_ndof_pan_get(ndof, pan_vec, true);
+
/* NOTE(@campbellbarton): In principle rotating could pass through to regular
* non-camera NDOF behavior (exiting the camera-view and rotating).
* Disabled this block since in practice it's difficult to control NDOF devices
@@ -388,14 +391,14 @@ static int view3d_ndof_cameraview_pan_zoom(bContext *C, const wmEvent *event)
if (has_translate) {
const float speed = ndof->dt * NDOF_PIXELS_PER_SECOND;
- float event_ofs[2] = {ndof->tvec[0] * speed, ndof->tvec[1] * speed};
+ float event_ofs[2] = {pan_vec[0] * speed, pan_vec[1] * speed};
if (ED_view3d_camera_view_pan(region, event_ofs)) {
changed = true;
}
}
if (has_zoom) {
- const float scale = 1.0f + (ndof->dt * ndof->tvec[2]);
+ const float scale = 1.0f + (ndof->dt * pan_vec[2]);
if (ED_view3d_camera_view_zoom_scale(rv3d, scale)) {
changed = true;
}
diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c
index 087ca72211e..af93aa50238 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_roll.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c
@@ -15,6 +15,8 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "DEG_depsgraph_query.h"
+
#include "ED_screen.h"
#include "view3d_intern.h"
@@ -167,7 +169,13 @@ static int viewroll_exec(bContext *C, wmOperator *op)
}
rv3d = region->regiondata;
- if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
+
+ const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d);
+ if ((rv3d->persp != RV3D_CAMOB) || is_camera_lock) {
+ if (is_camera_lock) {
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
+ }
ED_view3d_smooth_view_force_finish(C, v3d, region);
@@ -202,6 +210,9 @@ static int viewroll_exec(bContext *C, wmOperator *op)
&(const V3D_SmoothParams){
.quat = quat_new,
.dyn_ofs = dyn_ofs_pt,
+ /* Group as successive roll may run by holding a key. */
+ .undo_str = op->type->name,
+ .undo_grouped = true,
});
viewops_data_free(C, op->customdata);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c
index 989fa152acc..10adf238001 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_rotate.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c
@@ -37,7 +37,7 @@ void viewrotate_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Rotate Modal");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -375,6 +375,7 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (ret & OPERATOR_FINISHED) {
+ ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
viewops_data_free(C, op->customdata);
op->customdata = NULL;
}
diff --git a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
index 48af126d8a9..6b150d1e771 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c
@@ -8,6 +8,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_context.h"
@@ -21,6 +22,115 @@
#include "view3d_intern.h"
#include "view3d_navigate.h" /* own include */
+static void view3d_smoothview_apply_with_interp(
+ bContext *C, View3D *v3d, RegionView3D *rv3d, const bool use_autokey, const float factor);
+
+/* -------------------------------------------------------------------- */
+/** \name Smooth View Undo Handling
+ *
+ * When the camera is locked to the viewport smooth-view operations
+ * may need to perform an undo push.
+ *
+ * In this case the smooth-view camera transformation is temporarily completed,
+ * undo is pushed then the change is rewound, and smooth-view completes from it's timer.
+ * In the case smooth-view executed the change immediately - an undo push is called.
+ *
+ * NOTE(@campbellbarton): While this is not ideal it's necessary as making the undo-push
+ * once smooth-view is complete because smooth-view is non-blocking and it's possible other
+ * operations are executed once smooth-view has started.
+ * \{ */
+
+void ED_view3d_smooth_view_undo_begin(bContext *C, const ScrArea *area)
+{
+ const View3D *v3d = area->spacedata.first;
+ Object *camera = v3d->camera;
+ if (!camera) {
+ return;
+ }
+
+ /* Tag the camera object so it's known smooth-view is applied to the view-ports camera
+ * (needed to detect when a locked camera is being manipulated).
+ * NOTE: It doesn't matter if the actual object being manipulated is the camera or not. */
+ camera->id.tag &= ~LIB_TAG_DOIT;
+
+ LISTBASE_FOREACH (const ARegion *, region, &area->regionbase) {
+ if (region->regiontype != RGN_TYPE_WINDOW) {
+ continue;
+ }
+ const RegionView3D *rv3d = region->regiondata;
+ if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) {
+ camera->id.tag |= LIB_TAG_DOIT;
+ break;
+ }
+ }
+}
+
+void ED_view3d_smooth_view_undo_end(bContext *C,
+ const ScrArea *area,
+ const char *undo_str,
+ const bool undo_grouped)
+{
+ View3D *v3d = area->spacedata.first;
+ Object *camera = v3d->camera;
+ if (!camera) {
+ return;
+ }
+ if (camera->id.tag & LIB_TAG_DOIT) {
+ /* Smooth view didn't touch the camera. */
+ camera->id.tag &= ~LIB_TAG_DOIT;
+ return;
+ }
+
+ if ((U.uiflag & USER_GLOBALUNDO) == 0) {
+ return;
+ }
+
+ /* NOTE(@campbellbarton): It is not possible that a single viewport references different cameras
+ * so even in the case there is a quad-view with multiple camera views set, these will all
+ * reference the same camera. In this case it doesn't matter which region is used.
+ * If in the future multiple cameras are supported, this logic can be extended. */
+ const ARegion *region_camera = NULL;
+
+ /* An undo push should be performed. */
+ bool is_interactive = false;
+ LISTBASE_FOREACH (const ARegion *, region, &area->regionbase) {
+ if (region->regiontype != RGN_TYPE_WINDOW) {
+ continue;
+ }
+ const RegionView3D *rv3d = region->regiondata;
+ if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) {
+ region_camera = region;
+ if (rv3d->sms) {
+ is_interactive = true;
+ }
+ }
+ }
+
+ if (region_camera == NULL) {
+ return;
+ }
+
+ RegionView3D *rv3d = region_camera->regiondata;
+
+ /* Fast forward, undo push, then rewind. */
+ if (is_interactive) {
+ view3d_smoothview_apply_with_interp(C, v3d, rv3d, false, 1.0f);
+ }
+
+ if (undo_grouped) {
+ ED_view3d_camera_lock_undo_grouped_push(undo_str, v3d, rv3d, C);
+ }
+ else {
+ ED_view3d_camera_lock_undo_push(undo_str, v3d, rv3d, C);
+ }
+
+ if (is_interactive) {
+ view3d_smoothview_apply_with_interp(C, v3d, rv3d, false, 0.0f);
+ }
+}
+
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Smooth View Operator & Utilities
*
@@ -86,6 +196,11 @@ void ED_view3d_smooth_view_ex(
const int smooth_viewtx,
const V3D_SmoothParams *sview)
{
+ /* In this case use #ED_view3d_smooth_view_undo_begin & end functions
+ * instead of passing in undo. */
+ BLI_assert_msg(sview->undo_str == NULL,
+ "Only the 'ED_view3d_smooth_view' version of this function handles undo!");
+
RegionView3D *rv3d = region->regiondata;
struct SmoothView3DStore sms = {{0}};
@@ -236,6 +351,13 @@ void ED_view3d_smooth_view_ex(
WM_event_add_mousemove(win);
}
+
+ if (sms.to_camera == false) {
+ /* See comments in #ED_view3d_smooth_view_undo_begin for why this is needed. */
+ if (v3d->camera) {
+ v3d->camera->id.tag &= ~LIB_TAG_DOIT;
+ }
+ }
}
void ED_view3d_smooth_view(bContext *C,
@@ -249,97 +371,129 @@ void ED_view3d_smooth_view(bContext *C,
wmWindow *win = CTX_wm_window(C);
ScrArea *area = CTX_wm_area(C);
- ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, sview);
+ /* #ED_view3d_smooth_view_ex asserts this is not set as it doesn't support undo. */
+ struct V3D_SmoothParams sview_no_undo = *sview;
+ sview_no_undo.undo_str = NULL;
+ sview_no_undo.undo_grouped = false;
+
+ const bool do_undo = (sview->undo_str != NULL);
+ if (do_undo) {
+ ED_view3d_smooth_view_undo_begin(C, area);
+ }
+
+ ED_view3d_smooth_view_ex(depsgraph, wm, win, area, v3d, region, smooth_viewtx, &sview_no_undo);
+
+ if (do_undo) {
+ ED_view3d_smooth_view_undo_end(C, area, sview->undo_str, sview->undo_grouped);
+ }
}
-/* only meant for timer usage */
-static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview)
+/**
+ * Apply with interpolation, on completion run #view3d_smoothview_apply_and_finish.
+ */
+static void view3d_smoothview_apply_with_interp(
+ bContext *C, View3D *v3d, RegionView3D *rv3d, const bool use_autokey, const float factor)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- RegionView3D *rv3d = region->regiondata;
struct SmoothView3DStore *sms = rv3d->sms;
- float step, step_inv;
- if (sms->time_allowed != 0.0) {
- step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed);
+ interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, factor);
+
+ if (sms->use_dyn_ofs) {
+ view3d_orbit_apply_dyn_ofs(
+ rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs);
}
else {
- step = 1.0f;
+ interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, factor);
}
- /* end timer */
- if (step >= 1.0f) {
- wmWindow *win = CTX_wm_window(C);
+ rv3d->dist = interpf(sms->dst.dist, sms->src.dist, factor);
+ v3d->lens = interpf(sms->dst.lens, sms->src.lens, factor);
- /* if we went to camera, store the original */
- if (sms->to_camera) {
- rv3d->persp = RV3D_CAMOB;
- view3d_smooth_view_state_restore(&sms->org, v3d, rv3d);
- }
- else {
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
-
- view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d);
-
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ if (ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d)) {
+ if (use_autokey) {
ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
}
+ }
+}
- if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
- rv3d->view = sms->org_view;
- }
-
- MEM_freeN(rv3d->sms);
- rv3d->sms = NULL;
+/**
+ * Apply the view-port transformation & free smooth-view related data.
+ */
+static void view3d_smoothview_apply_and_finish(bContext *C, View3D *v3d, RegionView3D *rv3d)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ struct SmoothView3DStore *sms = rv3d->sms;
- WM_event_remove_timer(wm, win, rv3d->smooth_timer);
- rv3d->smooth_timer = NULL;
- rv3d->rflag &= ~RV3D_NAVIGATING;
+ wmWindow *win = CTX_wm_window(C);
- /* Event handling won't know if a UI item has been moved under the pointer. */
- WM_event_add_mousemove(win);
+ /* if we went to camera, store the original */
+ if (sms->to_camera) {
+ rv3d->persp = RV3D_CAMOB;
+ view3d_smooth_view_state_restore(&sms->org, v3d, rv3d);
}
else {
- /* ease in/out */
- step = (3.0f * step * step - 2.0f * step * step * step);
-
- step_inv = 1.0f - step;
-
- interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step);
-
- if (sms->use_dyn_ofs) {
- view3d_orbit_apply_dyn_ofs(
- rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs);
- }
- else {
- interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step);
- }
+ const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv;
- v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv;
+ view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d);
- const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d);
- if (ED_screen_animation_playing(wm)) {
+ if (ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d)) {
ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true);
}
}
- if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) {
- view3d_boxview_copy(CTX_wm_area(C), region);
+ if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) {
+ rv3d->view = sms->org_view;
}
+ MEM_freeN(rv3d->sms);
+ rv3d->sms = NULL;
+
+ WM_event_remove_timer(wm, win, rv3d->smooth_timer);
+ rv3d->smooth_timer = NULL;
+ rv3d->rflag &= ~RV3D_NAVIGATING;
+
+ /* Event handling won't know if a UI item has been moved under the pointer. */
+ WM_event_add_mousemove(win);
+
/* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636,
* when switching camera in quad-view the other ortho views would zoom & reset.
*
* For now only redraw all regions when smooth-view finishes.
*/
- if (step >= 1.0f) {
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+}
+
+/* only meant for timer usage */
+
+static void view3d_smoothview_apply_from_timer(bContext *C, View3D *v3d, ARegion *region)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ RegionView3D *rv3d = region->regiondata;
+ struct SmoothView3DStore *sms = rv3d->sms;
+ float factor;
+
+ if (sms->time_allowed != 0.0) {
+ factor = (float)((rv3d->smooth_timer->duration) / sms->time_allowed);
}
else {
- ED_region_tag_redraw(region);
+ factor = 1.0f;
+ }
+ if (factor >= 1.0f) {
+ view3d_smoothview_apply_and_finish(C, v3d, rv3d);
}
+ else {
+ /* Ease in/out smoothing. */
+ factor = (3.0f * factor * factor - 2.0f * factor * factor * factor);
+ const bool use_autokey = ED_screen_animation_playing(wm);
+ view3d_smoothview_apply_with_interp(C, v3d, rv3d, use_autokey, factor);
+ }
+
+ if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
+ view3d_boxview_copy(CTX_wm_area(C), region);
+ }
+
+ ED_region_tag_redraw(region);
}
static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
@@ -353,7 +507,7 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w
return OPERATOR_PASS_THROUGH;
}
- view3d_smoothview_apply(C, v3d, region, true);
+ view3d_smoothview_apply_from_timer(C, v3d, region);
return OPERATOR_FINISHED;
}
@@ -361,12 +515,10 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w
void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region)
{
RegionView3D *rv3d = region->regiondata;
-
if (rv3d && rv3d->sms) {
- rv3d->sms->time_allowed = 0.0; /* force finishing */
- view3d_smoothview_apply(C, v3d, region, false);
+ view3d_smoothview_apply_and_finish(C, v3d, rv3d);
- /* force update of view matrix so tools that run immediately after
+ /* Force update of view matrix so tools that run immediately after
* can use them without redrawing first */
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c
index 471231b5f27..3e0ce892b5a 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_walk.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c
@@ -170,7 +170,7 @@ void walk_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Walk Modal");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -335,7 +335,7 @@ static void drawWalkPixel(const struct bContext *UNUSED(C), ARegion *region, voi
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColorAlpha(TH_VIEW_OVERLAY, 1.0f);
@@ -659,7 +659,7 @@ static void walkEvent(WalkInfo *walk, const wmEvent *event)
if (event->type == TIMER && event->customdata == walk->timer) {
walk->redraw = true;
}
- else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ else if (ISMOUSE_MOTION(event->type)) {
#ifdef USE_TABLET_SUPPORT
if ((walk->is_cursor_absolute == false) && event->tablet.is_motion_absolute) {
@@ -1386,6 +1386,7 @@ static int walk_modal(bContext *C, wmOperator *op, const wmEvent *event)
int exit_code;
bool do_draw = false;
WalkInfo *walk = op->customdata;
+ View3D *v3d = walk->v3d;
RegionView3D *rv3d = walk->rv3d;
Object *walk_object = ED_view3d_cameracontrol_object_get(walk->v3d_camera_control);
@@ -1412,6 +1413,9 @@ static int walk_modal(bContext *C, wmOperator *op, const wmEvent *event)
if (exit_code != OPERATOR_RUNNING_MODAL) {
do_draw = true;
}
+ if (exit_code == OPERATOR_FINISHED) {
+ ED_view3d_camera_lock_undo_push(op->type->name, v3d, rv3d, C);
+ }
if (do_draw) {
if (rv3d->persp == RV3D_CAMOB) {
diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c
index a67c0850ad9..40df2b1a9c9 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_zoom.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c
@@ -41,7 +41,7 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Zoom Modal");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -425,6 +425,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
if (ret & OPERATOR_FINISHED) {
+ ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
viewops_data_free(C, op->customdata);
op->customdata = NULL;
}
@@ -507,6 +508,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op)
ED_region_tag_redraw(region);
+ ED_view3d_camera_lock_undo_grouped_push(op->type->name, v3d, rv3d, C);
viewops_data_free(C, op->customdata);
op->customdata = NULL;
diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
index f834efe4a7b..7cafc3dfd42 100644
--- a/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
+++ b/source/blender/editors/space_view3d/view3d_navigate_zoom_border.c
@@ -159,11 +159,15 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
/* clamp after because we may have been zooming out */
CLAMP(new_dist, dist_range[0], dist_range[1]);
- /* TODO(campbell): 'is_camera_lock' not currently working well. */
const bool is_camera_lock = ED_view3d_camera_lock_check(v3d, rv3d);
- if ((rv3d->persp == RV3D_CAMOB) && (is_camera_lock == false)) {
+ if (rv3d->persp == RV3D_CAMOB) {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP);
+ if (is_camera_lock) {
+ ED_view3d_camera_lock_init(depsgraph, v3d, rv3d);
+ }
+ else {
+ ED_view3d_persp_switch_from_camera(depsgraph, v3d, rv3d, RV3D_PERSP);
+ }
}
ED_view3d_smooth_view(C,
@@ -173,6 +177,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op)
&(const V3D_SmoothParams){
.ofs = new_ofs,
.dist = &new_dist,
+ .undo_str = op->type->name,
});
if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) {
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.cc
index 8eff9ee472f..447bf99b000 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.cc
@@ -5,10 +5,10 @@
* \ingroup spview3d
*/
-#include <float.h>
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
+#include <cfloat>
+#include <cmath>
+#include <cstdio>
+#include <cstring>
#include "DNA_action_types.h"
#include "DNA_armature_types.h"
@@ -23,7 +23,6 @@
#include "MEM_guardedalloc.h"
-#include "BLI_array.h"
#include "BLI_bitmap.h"
#include "BLI_lasso_2d.h"
#include "BLI_linklist.h"
@@ -32,6 +31,7 @@
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BLI_vector.hh"
#ifdef __BIG_ENDIAN__
# include "BLI_endian_switch.h"
@@ -140,10 +140,11 @@ void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
/** \name Internal Object Utilities
* \{ */
-static bool object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d)
+static bool object_deselect_all_visible(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
{
bool changed = false;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->flag & BASE_SELECTED) {
if (BASE_SELECTABLE(v3d, base)) {
ED_object_base_select(base, BA_DESELECT);
@@ -155,10 +156,11 @@ static bool object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d)
}
/* deselect all except b */
-static bool object_deselect_all_except(ViewLayer *view_layer, Base *b)
+static bool object_deselect_all_except(const Scene *scene, ViewLayer *view_layer, Base *b)
{
bool changed = false;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->flag & BASE_SELECTED) {
if (b != base) {
ED_object_base_select(base, BA_DESELECT);
@@ -191,7 +193,7 @@ static void editselect_buf_cache_init(ViewContext *vc, short select_mode)
if (vc->obedit) {
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(
- vc->view_layer, vc->v3d, &bases_len);
+ vc->scene, vc->view_layer, vc->v3d, &bases_len);
DRW_select_buffer_context_create(bases, bases_len, select_mode);
MEM_freeN(bases);
@@ -199,20 +201,21 @@ static void editselect_buf_cache_init(ViewContext *vc, short select_mode)
else {
/* Use for paint modes, currently only a single object at a time. */
if (vc->obact) {
+ BKE_view_layer_synced_ensure(vc->scene, vc->view_layer);
Base *base = BKE_view_layer_base_find(vc->view_layer, vc->obact);
DRW_select_buffer_context_create(&base, 1, select_mode);
}
}
}
-static void editselect_buf_cache_free(struct EditSelectBuf_Cache *esel)
+static void editselect_buf_cache_free(EditSelectBuf_Cache *esel)
{
MEM_SAFE_FREE(esel->select_bitmap);
}
static void editselect_buf_cache_free_voidp(void *esel_voidp)
{
- editselect_buf_cache_free(esel_voidp);
+ editselect_buf_cache_free(static_cast<EditSelectBuf_Cache *>(esel_voidp));
MEM_freeN(esel_voidp);
}
@@ -220,7 +223,7 @@ static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *w
ViewContext *vc,
short select_mode)
{
- struct EditSelectBuf_Cache *esel = MEM_callocN(sizeof(*esel), __func__);
+ EditSelectBuf_Cache *esel = MEM_cnew<EditSelectBuf_Cache>(__func__);
wm_userdata->data = esel;
wm_userdata->free_fn = editselect_buf_cache_free_voidp;
wm_userdata->use_free = true;
@@ -233,7 +236,7 @@ static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *w
/** \name Internal Edit-Mesh Utilities
* \{ */
-static bool edbm_backbuf_check_and_select_verts(struct EditSelectBuf_Cache *esel,
+static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel,
Depsgraph *depsgraph,
Object *ob,
BMEditMesh *em,
@@ -265,7 +268,7 @@ static bool edbm_backbuf_check_and_select_verts(struct EditSelectBuf_Cache *esel
return changed;
}
-static bool edbm_backbuf_check_and_select_edges(struct EditSelectBuf_Cache *esel,
+static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel,
Depsgraph *depsgraph,
Object *ob,
BMEditMesh *em,
@@ -297,7 +300,7 @@ static bool edbm_backbuf_check_and_select_edges(struct EditSelectBuf_Cache *esel
return changed;
}
-static bool edbm_backbuf_check_and_select_faces(struct EditSelectBuf_Cache *esel,
+static bool edbm_backbuf_check_and_select_faces(EditSelectBuf_Cache *esel,
Depsgraph *depsgraph,
Object *ob,
BMEditMesh *em,
@@ -331,18 +334,21 @@ static bool edbm_backbuf_check_and_select_faces(struct EditSelectBuf_Cache *esel
/* object mode, edbm_ prefix is confusing here, rename? */
static bool edbm_backbuf_check_and_select_verts_obmode(Mesh *me,
- struct EditSelectBuf_Cache *esel,
+ EditSelectBuf_Cache *esel,
const eSelectOp sel_op)
{
- MVert *mv = me->mvert;
- uint index;
+ MVert *verts = BKE_mesh_verts_for_write(me);
+ MVert *mv = verts;
bool changed = false;
const BLI_bitmap *select_bitmap = esel->select_bitmap;
if (mv) {
- for (index = 0; index < me->totvert; index++, mv++) {
- if (!(mv->flag & ME_HIDE)) {
+ const bool *hide_vert = (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_vert");
+
+ for (int index = 0; index < me->totvert; index++, mv++) {
+ if (!(hide_vert && hide_vert[index])) {
const bool is_select = mv->flag & SELECT;
const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
@@ -358,23 +364,25 @@ static bool edbm_backbuf_check_and_select_verts_obmode(Mesh *me,
/* object mode, edbm_ prefix is confusing here, rename? */
static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me,
- struct EditSelectBuf_Cache *esel,
+ EditSelectBuf_Cache *esel,
const eSelectOp sel_op)
{
- MPoly *mpoly = me->mpoly;
- uint index;
+ MPoly *polys = BKE_mesh_polys_for_write(me);
bool changed = false;
const BLI_bitmap *select_bitmap = esel->select_bitmap;
- if (mpoly) {
- for (index = 0; index < me->totpoly; index++, mpoly++) {
- if (!(mpoly->flag & ME_HIDE)) {
- const bool is_select = mpoly->flag & ME_FACE_SEL;
+ if (polys) {
+ const bool *hide_poly = (const bool *)CustomData_get_layer_named(
+ &me->vdata, CD_PROP_BOOL, ".hide_poly");
+
+ for (int index = 0; index < me->totpoly; index++) {
+ if (!(hide_poly && hide_poly[index])) {
+ const bool is_select = polys[index].flag & ME_FACE_SEL;
const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
if (sel_op_result != -1) {
- SET_FLAG_FROM_TEST(mpoly->flag, sel_op_result, ME_FACE_SEL);
+ SET_FLAG_FROM_TEST(polys[index].flag, sel_op_result, ME_FACE_SEL);
changed = true;
}
}
@@ -389,7 +397,7 @@ static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me,
/** \name Lasso Select
* \{ */
-typedef struct LassoSelectUserData {
+struct LassoSelectUserData {
ViewContext *vc;
const rcti *rect;
const rctf *rect_fl;
@@ -403,7 +411,7 @@ typedef struct LassoSelectUserData {
int pass;
bool is_done;
bool is_changed;
-} LassoSelectUserData;
+};
static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data,
ViewContext *vc,
@@ -422,7 +430,7 @@ static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data,
r_data->mcoords_len = mcoords_len;
r_data->sel_op = sel_op;
/* SELECT by default, but can be changed if needed (only few cases use and respect this). */
- r_data->select_flag = SELECT;
+ r_data->select_flag = (eBezTriple_Flag)SELECT;
/* runtime */
r_data->pass = 0;
@@ -435,24 +443,24 @@ static bool view3d_selectable_data(bContext *C)
Object *ob = CTX_data_active_object(C);
if (!ED_operator_region_view3d_active(C)) {
- return 0;
+ return false;
}
if (ob) {
if (ob->mode & OB_MODE_EDIT) {
if (ob->type == OB_FONT) {
- return 0;
+ return false;
}
}
else {
if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) &&
!BKE_paint_select_elem_test(ob)) {
- return 0;
+ return false;
}
}
}
- return 1;
+ return true;
}
/* helper also for box_select */
@@ -467,21 +475,21 @@ static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2
/* check points in rect */
if (edge_fully_inside_rect(rect, v1, v2)) {
- return 1;
+ return true;
}
/* check points completely out rect */
if (v1[0] < rect->xmin && v2[0] < rect->xmin) {
- return 0;
+ return false;
}
if (v1[0] > rect->xmax && v2[0] > rect->xmax) {
- return 0;
+ return false;
}
if (v1[1] < rect->ymin && v2[1] < rect->ymin) {
- return 0;
+ return false;
}
if (v1[1] > rect->ymax && v2[1] > rect->ymax) {
- return 0;
+ return false;
}
/* simple check lines intersecting. */
@@ -491,22 +499,22 @@ static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2
d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) {
- return 0;
+ return false;
}
if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) {
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static void do_lasso_select_pose__do_tag(void *userData,
- struct bPoseChannel *pchan,
+ bPoseChannel *pchan,
const float screen_co_a[2],
const float screen_co_b[2])
{
- LassoSelectUserData *data = userData;
- const bArmature *arm = data->vc->obact->data;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
+ const bArmature *arm = static_cast<bArmature *>(data->vc->obact->data);
if (!PBONE_SELECTABLE(arm, pchan->bone)) {
return;
}
@@ -527,7 +535,7 @@ static void do_lasso_tag_pose(ViewContext *vc,
LassoSelectUserData data;
rcti rect;
- if ((ob->type != OB_ARMATURE) || (ob->pose == NULL)) {
+ if ((ob->type != OB_ARMATURE) || (ob->pose == nullptr)) {
return;
}
@@ -536,7 +544,8 @@ static void do_lasso_tag_pose(ViewContext *vc,
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
- view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, mcoords_len, 0);
+ view3d_userdata_lassoselect_init(
+ &data, vc, &rect, mcoords, mcoords_len, static_cast<eSelectOp>(0));
ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d);
@@ -553,14 +562,13 @@ static bool do_lasso_select_objects(ViewContext *vc,
const eSelectOp sel_op)
{
View3D *v3d = vc->v3d;
- Base *base;
bool changed = false;
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- changed |= object_deselect_all_visible(vc->view_layer, vc->v3d);
+ changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
}
-
- for (base = vc->view_layer->object_bases.first; base; base = base->next) {
+ BKE_view_layer_synced_ensure(vc->scene, vc->view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) {
if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */
const bool is_select = base->flag & BASE_SELECTED;
const bool is_inside = ((ED_view3d_project_base(vc->region, base) == V3D_PROJ_RET_OK) &&
@@ -584,32 +592,32 @@ static bool do_lasso_select_objects(ViewContext *vc,
/**
* Use for lasso & box select.
*/
-static Base **do_pose_tag_select_op_prepare(ViewContext *vc, uint *r_bases_len)
+static blender::Vector<Base *> do_pose_tag_select_op_prepare(ViewContext *vc)
{
- Base **bases = NULL;
- BLI_array_declare(bases);
- FOREACH_BASE_IN_MODE_BEGIN (vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter) {
+ blender::Vector<Base *> bases;
+
+ FOREACH_BASE_IN_MODE_BEGIN (
+ vc->scene, vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter) {
Object *ob_iter = base_iter->object;
- bArmature *arm = ob_iter->data;
+ bArmature *arm = static_cast<bArmature *>(ob_iter->data);
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) {
Bone *bone = pchan->bone;
bone->flag &= ~BONE_DONE;
}
arm->id.tag |= LIB_TAG_DOIT;
ob_iter->id.tag &= ~LIB_TAG_DOIT;
- BLI_array_append(bases, base_iter);
+ bases.append(base_iter);
}
FOREACH_BASE_IN_MODE_END;
- *r_bases_len = BLI_array_len(bases);
return bases;
}
-static bool do_pose_tag_select_op_exec(Base **bases, const uint bases_len, const eSelectOp sel_op)
+static bool do_pose_tag_select_op_exec(blender::MutableSpan<Base *> bases, const eSelectOp sel_op)
{
bool changed_multi = false;
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- for (int i = 0; i < bases_len; i++) {
+ for (const int i : bases.index_range()) {
Base *base_iter = bases[i];
Object *ob_iter = base_iter->object;
if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, false)) {
@@ -619,10 +627,10 @@ static bool do_pose_tag_select_op_exec(Base **bases, const uint bases_len, const
}
}
- for (int i = 0; i < bases_len; i++) {
+ for (const int i : bases.index_range()) {
Base *base_iter = bases[i];
Object *ob_iter = base_iter->object;
- bArmature *arm = ob_iter->data;
+ bArmature *arm = static_cast<bArmature *>(ob_iter->data);
/* Don't handle twice. */
if (arm->id.tag & LIB_TAG_DOIT) {
@@ -643,7 +651,7 @@ static bool do_pose_tag_select_op_exec(Base **bases, const uint bases_len, const
SET_FLAG_FROM_TEST(bone->flag, sel_op_result, BONE_SELECTED);
if (sel_op_result == 0) {
if (arm->act_bone == bone) {
- arm->act_bone = NULL;
+ arm->act_bone = nullptr;
}
}
changed = true;
@@ -663,22 +671,20 @@ static bool do_lasso_select_pose(ViewContext *vc,
const int mcoords_len,
const eSelectOp sel_op)
{
- uint bases_len;
- Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len);
+ blender::Vector<Base *> bases = do_pose_tag_select_op_prepare(vc);
- for (int i = 0; i < bases_len; i++) {
+ for (const int i : bases.index_range()) {
Base *base_iter = bases[i];
Object *ob_iter = base_iter->object;
do_lasso_tag_pose(vc, ob_iter, mcoords, mcoords_len);
}
- const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op);
+ const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
if (changed_multi) {
DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene);
}
- MEM_freeN(bases);
return changed_multi;
}
@@ -687,7 +693,7 @@ static void do_lasso_select_mesh__doSelectVert(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- LassoSelectUserData *data = userData;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
const bool is_inside =
(BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
@@ -701,7 +707,7 @@ static void do_lasso_select_mesh__doSelectVert(void *userData,
}
struct LassoSelectUserData_ForMeshEdge {
LassoSelectUserData *data;
- struct EditSelectBuf_Cache *esel;
+ EditSelectBuf_Cache *esel;
uint backbuf_offset;
};
static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data,
@@ -710,7 +716,8 @@ static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data,
const float screen_co_b[2],
int index)
{
- struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data;
+ LassoSelectUserData_ForMeshEdge *data_for_edge = static_cast<LassoSelectUserData_ForMeshEdge *>(
+ user_data);
LassoSelectUserData *data = data_for_edge->data;
bool is_visible = true;
if (data_for_edge->backbuf_offset) {
@@ -738,7 +745,8 @@ static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data,
const float screen_co_b[2],
int index)
{
- struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data;
+ LassoSelectUserData_ForMeshEdge *data_for_edge = static_cast<LassoSelectUserData_ForMeshEdge *>(
+ user_data);
LassoSelectUserData *data = data_for_edge->data;
bool is_visible = true;
if (data_for_edge->backbuf_offset) {
@@ -764,7 +772,7 @@ static void do_lasso_select_mesh__doSelectFace(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- LassoSelectUserData *data = userData;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
const bool is_inside =
(BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
@@ -808,13 +816,13 @@ static bool do_lasso_select_mesh(ViewContext *vc,
const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
if (use_zbuf) {
- if (wm_userdata->data == NULL) {
+ if (wm_userdata->data == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode);
- esel = wm_userdata->data;
+ esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
esel->select_bitmap = DRW_select_buffer_bitmap_from_poly(
- vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, NULL);
+ vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, nullptr);
}
}
@@ -830,16 +838,15 @@ static bool do_lasso_select_mesh(ViewContext *vc,
}
if (ts->selectmode & SCE_SELECT_EDGE) {
/* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
- struct LassoSelectUserData_ForMeshEdge data_for_edge = {
- .data = &data,
- .esel = use_zbuf ? esel : NULL,
- .backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem(
- vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
- 0,
- };
+ LassoSelectUserData_ForMeshEdge data_for_edge{};
+ data_for_edge.data = &data;
+ data_for_edge.esel = use_zbuf ? esel : nullptr;
+ data_for_edge.backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem(
+ vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
+ 0;
const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR |
- (use_zbuf ? 0 : V3D_PROJ_TEST_CLIP_BB);
+ (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB);
/* Fully inside. */
mesh_foreachScreenEdge_clip_bb_segment(
vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag);
@@ -878,7 +885,7 @@ static void do_lasso_select_curve__doSelect(void *userData,
bool handles_visible,
const float screen_co[2])
{
- LassoSelectUserData *data = userData;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
const bool is_inside = BLI_lasso_is_point_inside(
data->mcoords, data->mcoords_len, screen_co[0], screen_co[1], IS_CLIPPED);
@@ -944,14 +951,14 @@ static bool do_lasso_select_curve(ViewContext *vc,
}
if (data.is_changed) {
- BKE_curve_nurb_vert_active_validate(vc->obedit->data);
+ BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(vc->obedit->data));
}
return data.is_changed;
}
static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, const float screen_co[2])
{
- LassoSelectUserData *data = userData;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
const bool is_select = bp->f1 & SELECT;
const bool is_inside =
(BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
@@ -990,8 +997,8 @@ static void do_lasso_select_armature__doSelectBone(void *userData,
const float screen_co_a[2],
const float screen_co_b[2])
{
- LassoSelectUserData *data = userData;
- const bArmature *arm = data->vc->obedit->data;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
+ const bArmature *arm = static_cast<const bArmature *>(data->vc->obedit->data);
if (!EBONE_VISIBLE(arm, ebone)) {
return;
}
@@ -1039,8 +1046,8 @@ static void do_lasso_select_armature__doSelectBone_clip_content(void *userData,
const float screen_co_a[2],
const float screen_co_b[2])
{
- LassoSelectUserData *data = userData;
- bArmature *arm = data->vc->obedit->data;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
+ bArmature *arm = static_cast<bArmature *>(data->vc->obedit->data);
if (!EBONE_VISIBLE(arm, ebone)) {
return;
}
@@ -1079,7 +1086,7 @@ static bool do_lasso_select_armature(ViewContext *vc,
data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit);
}
- bArmature *arm = vc->obedit->data;
+ bArmature *arm = static_cast<bArmature *>(vc->obedit->data);
ED_armature_ebone_listbase_temp_clear(arm->edbo);
@@ -1097,7 +1104,7 @@ static bool do_lasso_select_armature(ViewContext *vc,
&data,
V3D_PROJ_TEST_CLIP_DEFAULT | V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT);
- data.is_changed |= ED_armature_edit_select_op_from_tagged(vc->obedit->data, sel_op);
+ data.is_changed |= ED_armature_edit_select_op_from_tagged(arm, sel_op);
if (data.is_changed) {
WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, vc->obedit);
@@ -1106,10 +1113,10 @@ static bool do_lasso_select_armature(ViewContext *vc,
}
static void do_lasso_select_mball__doSelectElem(void *userData,
- struct MetaElem *ml,
+ MetaElem *ml,
const float screen_co[2])
{
- LassoSelectUserData *data = userData;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
const bool is_select = ml->flag & SELECT;
const bool is_inside =
(BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
@@ -1152,7 +1159,7 @@ static void do_lasso_select_meshobject__doSelectVert(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- LassoSelectUserData *data = userData;
+ LassoSelectUserData *data = static_cast<LassoSelectUserData *>(userData);
const bool is_select = mv->flag & SELECT;
const bool is_inside =
(BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
@@ -1172,10 +1179,10 @@ static bool do_lasso_select_paintvert(ViewContext *vc,
{
const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
Object *ob = vc->obact;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
rcti rect;
- if (me == NULL || me->totvert == 0) {
+ if (me == nullptr || me->totvert == 0) {
return false;
}
@@ -1187,18 +1194,18 @@ static bool do_lasso_select_paintvert(ViewContext *vc,
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
if (use_zbuf) {
- if (wm_userdata->data == NULL) {
+ if (wm_userdata->data == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX);
- esel = wm_userdata->data;
+ esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
esel->select_bitmap = DRW_select_buffer_bitmap_from_poly(
- vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, NULL);
+ vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, nullptr);
}
}
if (use_zbuf) {
- if (esel->select_bitmap != NULL) {
+ if (esel->select_bitmap != nullptr) {
changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op);
}
}
@@ -1232,10 +1239,10 @@ static bool do_lasso_select_paintface(ViewContext *vc,
const eSelectOp sel_op)
{
Object *ob = vc->obact;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
rcti rect;
- if (me == NULL || me->totpoly == 0) {
+ if (me == nullptr || me->totpoly == 0) {
return false;
}
@@ -1247,12 +1254,12 @@ static bool do_lasso_select_paintface(ViewContext *vc,
BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
- if (esel == NULL) {
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
+ if (esel == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE);
- esel = wm_userdata->data;
+ esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
esel->select_bitmap = DRW_select_buffer_bitmap_from_poly(
- vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, NULL);
+ vc->depsgraph, vc->region, vc->v3d, mcoords, mcoords_len, &rect, nullptr);
}
if (esel->select_bitmap) {
@@ -1260,7 +1267,7 @@ static bool do_lasso_select_paintface(ViewContext *vc,
}
if (changed) {
- paintface_flush_flags(vc->C, ob, SELECT);
+ paintface_flush_flags(vc->C, ob, true, false);
}
return changed;
}
@@ -1274,10 +1281,10 @@ static bool view3d_lasso_select(bContext *C,
Object *ob = CTX_data_active_object(C);
bool changed_multi = false;
- wmGenericUserData wm_userdata_buf = {0};
+ wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
wmGenericUserData *wm_userdata = &wm_userdata_buf;
- if (vc->obedit == NULL) { /* Object Mode */
+ if (vc->obedit == nullptr) { /* Object Mode */
if (BKE_paint_select_face_test(ob)) {
changed_multi |= do_lasso_select_paintface(vc, wm_userdata, mcoords, mcoords_len, sel_op);
}
@@ -1289,7 +1296,7 @@ static bool view3d_lasso_select(bContext *C,
/* pass */
}
else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
- changed_multi |= PE_lasso_select(C, mcoords, mcoords_len, sel_op);
+ changed_multi |= PE_lasso_select(C, mcoords, mcoords_len, sel_op) != OPERATOR_CANCELLED;
}
else if (ob && (ob->mode & OB_MODE_POSE)) {
changed_multi |= do_lasso_select_pose(vc, mcoords, mcoords_len, sel_op);
@@ -1305,7 +1312,8 @@ static bool view3d_lasso_select(bContext *C,
}
}
else { /* Edit Mode */
- FOREACH_OBJECT_IN_MODE_BEGIN (vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (
+ vc->scene, vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter) {
ED_view3d_viewcontext_init_object(vc, ob_iter);
bool changed = false;
@@ -1335,7 +1343,7 @@ static bool view3d_lasso_select(bContext *C,
}
if (changed) {
- DEG_id_tag_update(vc->obedit->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc->obedit->data);
changed_multi = true;
}
@@ -1364,7 +1372,7 @@ static int view3d_lasso_select_exec(bContext *C, wmOperator *op)
/* setup view context for argument to callbacks */
ED_view3d_viewcontext_init(C, &vc, depsgraph);
- eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
+ eSelectOp sel_op = static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode"));
bool changed_multi = view3d_lasso_select(C, &vc, mcoords, mcoords_len, sel_op);
MEM_freeN((void *)mcoords);
@@ -1404,12 +1412,12 @@ void VIEW3D_OT_select_lasso(wmOperatorType *ot)
* \{ */
/* The max number of menu items in an object select menu */
-typedef struct SelMenuItemF {
+struct SelMenuItemF {
char idname[MAX_ID_NAME - 2];
int icon;
Base *base_ptr;
void *item_ptr;
-} SelMenuItemF;
+};
#define SEL_MENU_SIZE 22
static SelMenuItemF object_mouse_select_menu_data[SEL_MENU_SIZE];
@@ -1420,12 +1428,12 @@ static const EnumPropertyItem *object_select_menu_enum_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
- EnumPropertyItem *item = NULL, item_tmp = {0};
+ EnumPropertyItem *item = nullptr, item_tmp = {0};
int totitem = 0;
int i = 0;
/* Don't need context but avoid API doc-generation using this. */
- if (C == NULL || object_mouse_select_menu_data[i].idname[0] == '\0') {
+ if (C == nullptr || object_mouse_select_menu_data[i].idname[0] == '\0') {
return DummyRNA_NULL_items;
}
@@ -1453,10 +1461,12 @@ static int object_select_menu_exec(bContext *C, wmOperator *op)
const char *name = object_mouse_select_menu_data[name_index].idname;
View3D *v3d = CTX_wm_view3d(C);
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- const Base *oldbasact = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
- Base *basact = NULL;
+ Base *basact = nullptr;
CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
/* This is a bit dodgy, there should only be ONE object with this name,
* but library objects can mess this up. */
@@ -1467,7 +1477,7 @@ static int object_select_menu_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
- if (basact == NULL) {
+ if (basact == nullptr) {
return OPERATOR_CANCELLED;
}
UNUSED_VARS_NDEBUG(v3d);
@@ -1494,7 +1504,7 @@ static int object_select_menu_exec(bContext *C, wmOperator *op)
}
}
else {
- object_deselect_all_except(view_layer, basact);
+ object_deselect_all_except(scene, view_layer, basact);
ED_object_base_select(basact, BA_SELECT);
changed = true;
}
@@ -1538,14 +1548,14 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot)
/* #Object.id.name to select (dynamic enum). */
prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Object Name", "");
RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE));
ot->prop = prop;
- prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
+ prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
+ prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+ prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@@ -1557,12 +1567,12 @@ static bool object_mouse_select_menu(bContext *C,
const GPUSelectResult *buffer,
const int hits,
const int mval[2],
- const struct SelectPick_Params *params,
+ const SelectPick_Params *params,
Base **r_basact)
{
int base_count = 0;
bool ok;
- LinkNodePair linklist = {NULL, NULL};
+ LinkNodePair linklist = {nullptr, nullptr};
/* handle base->object->select_id */
CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
@@ -1599,14 +1609,14 @@ static bool object_mouse_select_menu(bContext *C,
}
CTX_DATA_END;
- *r_basact = NULL;
+ *r_basact = nullptr;
if (base_count == 0) {
return false;
}
if (base_count == 1) {
Base *base = (Base *)linklist.list->link;
- BLI_linklist_free(linklist.list, NULL);
+ BLI_linklist_free(linklist.list, nullptr);
*r_basact = base;
return false;
}
@@ -1618,7 +1628,7 @@ static bool object_mouse_select_menu(bContext *C,
memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
for (node = linklist.list, i = 0; node; node = node->next, i++) {
- Base *base = node->link;
+ Base *base = static_cast<Base *>(node->link);
Object *ob = base->object;
const char *name = ob->id.name + 2;
@@ -1633,10 +1643,10 @@ static bool object_mouse_select_menu(bContext *C,
RNA_boolean_set(&ptr, "extend", params->sel_op == SEL_OP_ADD);
RNA_boolean_set(&ptr, "deselect", params->sel_op == SEL_OP_SUB);
RNA_boolean_set(&ptr, "toggle", params->sel_op == SEL_OP_XOR);
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
WM_operator_properties_free(&ptr);
- BLI_linklist_free(linklist.list, NULL);
+ BLI_linklist_free(linklist.list, nullptr);
return true;
}
@@ -1644,17 +1654,18 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op)
{
const int name_index = RNA_enum_get(op->ptr, "name");
- const struct SelectPick_Params params = {
- .sel_op = ED_select_op_from_operator(op->ptr),
- };
+ SelectPick_Params params{};
+ params.sel_op = ED_select_op_from_operator(op->ptr);
View3D *v3d = CTX_wm_view3d(C);
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- const Base *oldbasact = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
Base *basact = object_mouse_select_menu_data[name_index].base_ptr;
- if (basact == NULL) {
+ if (basact == nullptr) {
return OPERATOR_CANCELLED;
}
@@ -1666,7 +1677,8 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op)
}
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, &params);
+ ED_armature_pose_select_pick_bone(
+ scene, view_layer, v3d, basact->object, pchan->bone, &params);
}
/* Weak but ensures we activate the menu again before using the enum. */
@@ -1690,7 +1702,7 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op)
* 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);
+ ED_armature_pose_select_in_wpaint_mode(scene, view_layer, basact);
}
else {
if (oldbasact != basact) {
@@ -1700,7 +1712,6 @@ static int bone_select_menu_exec(bContext *C, wmOperator *op)
}
/* 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);
@@ -1729,14 +1740,14 @@ void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
/* #Object.id.name to select (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);
+ RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE));
ot->prop = prop;
- prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "");
+ prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "");
+ prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
+ prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@@ -1747,19 +1758,19 @@ static bool bone_mouse_select_menu(bContext *C,
const GPUSelectResult *buffer,
const int hits,
const bool is_editmode,
- const struct SelectPick_Params *params)
+ const SelectPick_Params *params)
{
BLI_assert(buffer);
int bone_count = 0;
- LinkNodePair base_list = {NULL, NULL};
- LinkNodePair bone_list = {NULL, NULL};
+ LinkNodePair base_list = {nullptr, nullptr};
+ LinkNodePair bone_list = {nullptr, nullptr};
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;
+ void *bone_ptr = nullptr;
+ Base *bone_base = nullptr;
uint hitresult = buffer[a].id;
if (!(hitresult & BONESEL_ANY)) {
@@ -1787,8 +1798,8 @@ static bool bone_mouse_select_menu(bContext *C,
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);
+ bArmature *arm = static_cast<bArmature *>(bone_base->object->data);
+ ebone = static_cast<EditBone *>(BLI_findlink(arm->edbo, hit_bone));
if (ebone && !(ebone->flag & BONE_UNSELECTABLE)) {
bone_ptr = ebone;
}
@@ -1796,7 +1807,8 @@ static bool bone_mouse_select_menu(bContext *C,
else {
bPoseChannel *pchan;
const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
- pchan = BLI_findlink(&bone_base->object->pose->chanbase, hit_bone);
+ pchan = static_cast<bPoseChannel *>(
+ BLI_findlink(&bone_base->object->pose->chanbase, hit_bone));
if (pchan && !(pchan->bone->flag & BONE_UNSELECTABLE)) {
bone_ptr = pchan;
}
@@ -1821,14 +1833,14 @@ static bool bone_mouse_select_menu(bContext *C,
}
}
- BLI_gset_free(added_bones, NULL);
+ BLI_gset_free(added_bones, nullptr);
if (bone_count == 0) {
return false;
}
if (bone_count == 1) {
- BLI_linklist_free(base_list.list, NULL);
- BLI_linklist_free(bone_list.list, NULL);
+ BLI_linklist_free(base_list.list, nullptr);
+ BLI_linklist_free(bone_list.list, nullptr);
return false;
}
@@ -1842,15 +1854,15 @@ static bool bone_mouse_select_menu(bContext *C,
base_node = base_node->next, bone_node = bone_node->next, i++) {
char *name;
- object_mouse_select_menu_data[i].base_ptr = base_node->link;
+ object_mouse_select_menu_data[i].base_ptr = static_cast<Base *>(base_node->link);
if (is_editmode) {
- EditBone *ebone = bone_node->link;
+ EditBone *ebone = static_cast<EditBone *>(bone_node->link);
object_mouse_select_menu_data[i].item_ptr = ebone;
name = ebone->name;
}
else {
- bPoseChannel *pchan = bone_node->link;
+ bPoseChannel *pchan = static_cast<bPoseChannel *>(bone_node->link);
object_mouse_select_menu_data[i].item_ptr = pchan;
name = pchan->name;
}
@@ -1866,11 +1878,11 @@ static bool bone_mouse_select_menu(bContext *C,
RNA_boolean_set(&ptr, "extend", params->sel_op == SEL_OP_ADD);
RNA_boolean_set(&ptr, "deselect", params->sel_op == SEL_OP_SUB);
RNA_boolean_set(&ptr, "toggle", params->sel_op == SEL_OP_XOR);
- WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL);
+ WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
WM_operator_properties_free(&ptr);
- BLI_linklist_free(base_list.list, NULL);
- BLI_linklist_free(bone_list.list, NULL);
+ BLI_linklist_free(base_list.list, nullptr);
+ BLI_linklist_free(bone_list.list, nullptr);
return true;
}
@@ -1928,7 +1940,7 @@ static int mixed_bones_object_selectbuffer(ViewContext *vc,
int hits15, hits9 = 0, hits5 = 0;
bool has_bones15 = false, has_bones9 = false, has_bones5 = false;
- int select_mode = (do_nearest ? VIEW3D_SELECT_PICK_NEAREST : VIEW3D_SELECT_PICK_ALL);
+ eV3DSelectMode select_mode = (do_nearest ? VIEW3D_SELECT_PICK_NEAREST : VIEW3D_SELECT_PICK_ALL);
int hits = 0;
if (do_nearest_xray_if_supported) {
@@ -2084,7 +2096,7 @@ static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b
* that are visible but not select-able,
* since you may be in pose mode with an un-selectable object.
*
- * \return the active base or NULL.
+ * \return the active base or nullptr.
*/
static Base *mouse_select_eval_buffer(ViewContext *vc,
const GPUSelectResult *buffer,
@@ -2094,6 +2106,7 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
bool do_bones_get_priotity,
int *r_select_id_subelem)
{
+ Scene *scene = vc->scene;
ViewLayer *view_layer = vc->view_layer;
View3D *v3d = vc->v3d;
int a;
@@ -2136,7 +2149,8 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
else {
{
- GPUSelectResult *buffer_sorted = MEM_mallocN(sizeof(*buffer_sorted) * hits, __func__);
+ GPUSelectResult *buffer_sorted = static_cast<GPUSelectResult *>(
+ MEM_mallocN(sizeof(*buffer_sorted) * hits, __func__));
memcpy(buffer_sorted, buffer, sizeof(*buffer_sorted) * hits);
/* Remove non-bone objects. */
if (has_bones && do_bones_get_priotity) {
@@ -2156,8 +2170,10 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
/* It's possible there are no hits (all objects contained bones). */
if (hits > 0) {
/* Only exclude active object when it is selected. */
- if (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED)) {
- const int select_id_active = BASACT(view_layer)->object->runtime.select_id;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base = BKE_view_layer_active_base_get(view_layer);
+ if (base && (base->flag & BASE_SELECTED)) {
+ const int select_id_active = base->object->runtime.select_id;
for (int i_next = 0, i_prev = hits - 1; i_next < hits; i_prev = i_next++) {
if ((select_id_active == (buffer[i_prev].id & 0xFFFF)) &&
(select_id_active != (buffer[i_next].id & 0xFFFF))) {
@@ -2182,9 +2198,10 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
MEM_freeN((void *)buffer);
}
- Base *basact = NULL;
+ Base *basact = nullptr;
if (found) {
- for (Base *base = FIRSTBASE(view_layer); base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) {
if (base->object->runtime.select_id == select_id) {
basact = base;
@@ -2204,14 +2221,16 @@ static Base *mouse_select_eval_buffer(ViewContext *vc,
static Base *mouse_select_object_center(ViewContext *vc, Base *startbase, const int mval[2])
{
ARegion *region = vc->region;
+ Scene *scene = vc->scene;
ViewLayer *view_layer = vc->view_layer;
View3D *v3d = vc->v3d;
- Base *oldbasact = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
const float mval_fl[2] = {(float)mval[0], (float)mval[1]};
float dist = ED_view3d_select_dist_px() * 1.3333f;
- Base *basact = NULL;
+ Base *basact = nullptr;
/* Put the active object at a disadvantage to cycle through other objects. */
const float penalty_dist = 10.0f * UI_DPI_FAC;
@@ -2234,8 +2253,8 @@ static Base *mouse_select_object_center(ViewContext *vc, Base *startbase, const
}
base = base->next;
- if (base == NULL) {
- base = FIRSTBASE(view_layer);
+ if (base == nullptr) {
+ base = static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
}
if (base == startbase) {
break;
@@ -2250,7 +2269,7 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
- Base *basact = NULL;
+ Base *basact = nullptr;
GPUSelectResult buffer[MAXPICKELEMS];
/* setup view context for argument to callbacks */
@@ -2260,7 +2279,7 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
ED_view3d_viewcontext_init(C, &vc, depsgraph);
const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
- const bool do_material_slot_selection = r_material_slot != NULL;
+ const bool do_material_slot_selection = r_material_slot != nullptr;
const int hits = mixed_bones_object_selectbuffer(&vc,
buffer,
ARRAY_SIZE(buffer),
@@ -2271,7 +2290,7 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
do_material_slot_selection);
if (hits > 0) {
- const bool has_bones = (r_material_slot == NULL) && selectbuffer_has_bones(buffer, hits);
+ const bool has_bones = (r_material_slot == nullptr) && selectbuffer_has_bones(buffer, hits);
basact = mouse_select_eval_buffer(
&vc, buffer, hits, do_nearest, has_bones, true, r_material_slot);
}
@@ -2281,7 +2300,7 @@ static Base *ed_view3d_give_base_under_cursor_ex(bContext *C,
Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
{
- return ed_view3d_give_base_under_cursor_ex(C, mval, NULL);
+ return ed_view3d_give_base_under_cursor_ex(C, mval, nullptr);
}
Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2])
@@ -2290,33 +2309,33 @@ Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2])
if (base) {
return base->object;
}
- return NULL;
+ return nullptr;
}
-struct Object *ED_view3d_give_material_slot_under_cursor(struct bContext *C,
- const int mval[2],
- int *r_material_slot)
+Object *ED_view3d_give_material_slot_under_cursor(bContext *C,
+ const int mval[2],
+ int *r_material_slot)
{
Base *base = ed_view3d_give_base_under_cursor_ex(C, mval, r_material_slot);
if (base) {
return base->object;
}
- return NULL;
+ return nullptr;
}
bool ED_view3d_is_object_under_cursor(bContext *C, const int mval[2])
{
- return ED_view3d_give_object_under_cursor(C, mval) != NULL;
+ return ED_view3d_give_object_under_cursor(C, mval) != nullptr;
}
static void deselect_all_tracks(MovieTracking *tracking)
{
MovieTrackingObject *object;
- object = tracking->objects.first;
+ object = static_cast<MovieTrackingObject *>(tracking->objects.first);
while (object) {
ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object);
- MovieTrackingTrack *track = tracksbase->first;
+ MovieTrackingTrack *track = static_cast<MovieTrackingTrack *>(tracksbase->first);
while (track) {
BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
@@ -2332,16 +2351,16 @@ static bool ed_object_select_pick_camera_track(bContext *C,
Scene *scene,
Base *basact,
MovieClip *clip,
- const struct GPUSelectResult *buffer,
+ const GPUSelectResult *buffer,
const short hits,
- const struct SelectPick_Params *params)
+ const SelectPick_Params *params)
{
bool changed = false;
bool found = false;
MovieTracking *tracking = &clip->tracking;
- ListBase *tracksbase = NULL;
- MovieTrackingTrack *track = NULL;
+ ListBase *tracksbase = nullptr;
+ MovieTrackingTrack *track = nullptr;
for (int i = 0; i < hits; i++) {
const int hitresult = buffer[i].id;
@@ -2429,7 +2448,7 @@ static bool ed_object_select_pick_camera_track(bContext *C,
*/
static bool ed_object_select_pick(bContext *C,
const int mval[2],
- const struct SelectPick_Params *params,
+ const SelectPick_Params *params,
const bool center,
const bool enumerate,
const bool object_only)
@@ -2443,21 +2462,21 @@ static bool ed_object_select_pick(bContext *C,
View3D *v3d = vc.v3d;
/* Menu activation may find a base to make active (if it only finds a single item to select). */
- Base *basact_override = NULL;
+ Base *basact_override = nullptr;
- const bool is_obedit = (vc.obedit != NULL);
+ const bool is_obedit = (vc.obedit != nullptr);
if (object_only) {
/* Signal for #view3d_opengl_select to skip edit-mode objects. */
- vc.obedit = NULL;
+ vc.obedit = nullptr;
}
- /* Set for GPU depth buffer picking, leave NULL when selecting by center. */
- struct {
+ /* Set for GPU depth buffer picking, leave null when selecting by center. */
+ struct GPUData {
GPUSelectResult buffer[MAXPICKELEMS];
int hits;
bool do_nearest;
bool has_bones;
- } *gpu = NULL;
+ } *gpu = nullptr;
/* First handle menu selection, early exit if a menu opens
* since this takes ownership of the selection action.
@@ -2466,7 +2485,7 @@ static bool ed_object_select_pick(bContext *C,
* the item under the cursor. */
if (center == false) {
- gpu = MEM_mallocN(sizeof(*gpu), __func__);
+ gpu = MEM_new<GPUData>(__func__);
gpu->do_nearest = false;
gpu->has_bones = false;
@@ -2493,7 +2512,7 @@ static bool ed_object_select_pick(bContext *C,
if (enumerate) {
bool has_menu = false;
if (center) {
- if (object_mouse_select_menu(C, &vc, NULL, 0, mval, params, &basact_override)) {
+ if (object_mouse_select_menu(C, &vc, nullptr, 0, mval, params, &basact_override)) {
has_menu = true;
}
}
@@ -2511,7 +2530,7 @@ static bool ed_object_select_pick(bContext *C,
/* Let the menu handle any further actions. */
if (has_menu) {
- if (gpu != NULL) {
+ if (gpu != nullptr) {
MEM_freeN(gpu);
}
return false;
@@ -2521,14 +2540,18 @@ static bool ed_object_select_pick(bContext *C,
/* No menu, continue with selection. */
ViewLayer *view_layer = vc.view_layer;
+ BKE_view_layer_synced_ensure(scene, view_layer);
/* Don't set when the context has no active object (hidden), see: T60807. */
- const Base *oldbasact = vc.obact ? BASACT(view_layer) : NULL;
+ const Base *oldbasact = vc.obact ? BKE_view_layer_active_base_get(view_layer) : nullptr;
/* Always start list from `basact` when cycling the selection. */
- Base *startbase = (oldbasact && oldbasact->next) ? oldbasact->next : FIRSTBASE(view_layer);
+ Base *startbase = (oldbasact && oldbasact->next) ?
+ oldbasact->next :
+ static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
/* The next object's base to make active. */
- Base *basact = NULL;
- const eObjectMode object_mode = oldbasact ? oldbasact->object->mode : OB_MODE_OBJECT;
+ Base *basact = nullptr;
+ const eObjectMode object_mode = oldbasact ? static_cast<eObjectMode>(oldbasact->object->mode) :
+ OB_MODE_OBJECT;
/* When enabled, don't attempt any further selection. */
bool handled = false;
@@ -2575,8 +2598,8 @@ static bool ed_object_select_pick(bContext *C,
gpu->do_nearest,
gpu->has_bones,
do_bones_get_priotity,
- NULL) :
- NULL;
+ nullptr) :
+ nullptr;
}
/* Select pose-bones or camera-tracks. */
@@ -2586,7 +2609,7 @@ static bool ed_object_select_pick(bContext *C,
if (basact && (gpu->has_bones && (basact->object->type == OB_CAMERA))) {
MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
- if (clip != NULL) {
+ if (clip != nullptr) {
if (ed_object_select_pick_camera_track(
C, scene, basact, clip, gpu->buffer, gpu->hits, params)) {
ED_object_base_select(basact, BA_SELECT);
@@ -2599,11 +2622,12 @@ static bool ed_object_select_pick(bContext *C,
/* Fallback to regular object selection if no new bundles were selected,
* allows to select object parented to reconstruction object. */
basact = mouse_select_eval_buffer(
- &vc, gpu->buffer, gpu->hits, gpu->do_nearest, false, false, NULL);
+ &vc, gpu->buffer, gpu->hits, gpu->do_nearest, false, false, nullptr);
}
}
}
- else if (ED_armature_pose_select_pick_with_buffer(view_layer,
+ else if (ED_armature_pose_select_pick_with_buffer(scene,
+ view_layer,
v3d,
basact ? basact : (Base *)oldbasact,
gpu->buffer,
@@ -2616,7 +2640,7 @@ static bool ed_object_select_pick(bContext *C,
/* When there is no `baseact` this will have operated on `oldbasact`,
* allowing #SelectPick_Params.deselect_all work in pose-mode.
* In this case no object operations are needed. */
- if (basact != NULL) {
+ if (basact != nullptr) {
/* By convention the armature-object is selected when in pose-mode.
* While leaving it unselected will work, leaving pose-mode would leave the object
* active + unselected which isn't ideal when performing other actions on the object. */
@@ -2634,7 +2658,7 @@ static bool ed_object_select_pick(bContext *C,
* 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);
+ ED_armature_pose_select_in_wpaint_mode(scene, view_layer, basact);
handled = true;
}
@@ -2671,11 +2695,11 @@ static bool ed_object_select_pick(bContext *C,
if (is_obedit == false) {
if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
if (object_mode == OB_MODE_OBJECT) {
- struct Main *bmain = vc.bmain;
+ Main *bmain = vc.bmain;
ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
}
if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
- basact = NULL;
+ basact = nullptr;
}
}
@@ -2684,7 +2708,7 @@ static bool ed_object_select_pick(bContext *C,
if (basact && oldbasact) {
if ((oldbasact->object->mode != basact->object->mode) &&
(oldbasact->object->mode & basact->object->mode) == 0) {
- basact = NULL;
+ basact = nullptr;
}
}
}
@@ -2693,10 +2717,10 @@ static bool ed_object_select_pick(bContext *C,
/* Ensure code above doesn't change the active base. This code is already fairly involved,
* it's best if changing the active object is localized to a single place. */
- BLI_assert(oldbasact == (vc.obact ? BASACT(view_layer) : NULL));
+ BLI_assert(oldbasact == (vc.obact ? BKE_view_layer_active_base_get(view_layer) : nullptr));
- bool found = (basact != NULL);
- if ((handled == false) && (vc.obedit == NULL)) {
+ bool found = (basact != nullptr);
+ if ((handled == false) && (vc.obedit == nullptr)) {
/* Object-mode (pose mode will have been handled already). */
if (params->sel_op == SEL_OP_SET) {
if ((found && params->select_passthrough) && (basact->flag & BASE_SELECTED)) {
@@ -2704,8 +2728,8 @@ static bool ed_object_select_pick(bContext *C,
}
else if (found || params->deselect_all) {
/* Deselect everything. */
- /* `basact` may be NULL. */
- if (object_deselect_all_except(view_layer, basact)) {
+ /* `basact` may be nullptr. */
+ if (object_deselect_all_except(scene, view_layer, basact)) {
changed_object = true;
}
}
@@ -2717,7 +2741,7 @@ static bool ed_object_select_pick(bContext *C,
if (vc.obedit) {
/* Only do the select (use for setting vertex parents & hooks).
* In edit-mode do not activate. */
- object_deselect_all_except(view_layer, basact);
+ object_deselect_all_except(scene, view_layer, basact);
ED_object_base_select(basact, BA_SELECT);
changed_object = true;
@@ -2748,7 +2772,7 @@ static bool ed_object_select_pick(bContext *C,
break;
}
case SEL_OP_SET: {
- object_deselect_all_except(view_layer, basact);
+ object_deselect_all_except(scene, view_layer, basact);
ED_object_base_select(basact, BA_SELECT);
break;
}
@@ -2764,7 +2788,7 @@ static bool ed_object_select_pick(bContext *C,
/* Perform the activation even when 'handled', since this is used to ensure
* the object from the pose-bone selected is also activated. */
- if (use_activate_selected_base && (basact != NULL)) {
+ if (use_activate_selected_base && (basact != nullptr)) {
changed_object = true;
ED_object_base_activate(C, basact); /* adds notifier */
if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) {
@@ -2783,7 +2807,7 @@ static bool ed_object_select_pick(bContext *C,
ED_outliner_select_sync_from_pose_bone_tag(C);
}
- if (gpu != NULL) {
+ if (gpu != nullptr) {
MEM_freeN(gpu);
}
@@ -2798,21 +2822,23 @@ static bool ed_object_select_pick(bContext *C,
*/
static bool ed_wpaint_vertex_select_pick(bContext *C,
const int mval[2],
- const struct SelectPick_Params *params,
+ const SelectPick_Params *params,
Object *obact)
{
View3D *v3d = CTX_wm_view3d(C);
const bool use_zbuf = !XRAY_ENABLED(v3d);
- Mesh *me = obact->data; /* already checked for NULL */
+ Mesh *me = static_cast<Mesh *>(obact->data); /* already checked for nullptr */
uint index = 0;
+ MVert *verts = BKE_mesh_verts_for_write(me);
+
MVert *mv;
bool changed = false;
bool found = ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index);
if (params->sel_op == SEL_OP_SET) {
- if ((found && params->select_passthrough) && (me->mvert[index].flag & SELECT)) {
+ if ((found && params->select_passthrough) && (verts[index].flag & SELECT)) {
found = false;
}
else if (found || params->deselect_all) {
@@ -2822,7 +2848,7 @@ static bool ed_wpaint_vertex_select_pick(bContext *C,
}
if (found) {
- mv = &me->mvert[index];
+ mv = &verts[index];
switch (params->sel_op) {
case SEL_OP_ADD: {
mv->flag |= SELECT;
@@ -2873,7 +2899,7 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
Object *obedit = CTX_data_edit_object(C);
Object *obact = CTX_data_active_object(C);
- struct SelectPick_Params params = {0};
+ SelectPick_Params params{};
ED_select_pick_params_from_operator(op->ptr, &params);
const bool vert_without_handles = RNA_boolean_get(op->ptr, "vert_without_handles");
@@ -2893,14 +2919,9 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
bool changed = false;
int mval[2];
- RNA_int_get_array(op->ptr, "location", mval);
-
- view3d_operator_needs_opengl(C);
- BKE_object_update_select_id(CTX_data_main(C));
-
if (object_only) {
- obedit = NULL;
- obact = NULL;
+ obedit = nullptr;
+ obact = nullptr;
/* ack, this is incorrect but to do this correctly we would need an
* alternative edit-mode/object-mode keymap, this copies the functionality
@@ -2908,6 +2929,19 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
center = false;
}
+ if (obedit && enumerate) {
+ /* Enumerate makes no sense in edit-mode unless also explicitly picking objects or bones.
+ * Pass the event through so the event may be handled by loop-select for e.g. see: T100204. */
+ if (obedit->type != OB_ARMATURE) {
+ return OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED;
+ }
+ }
+
+ RNA_int_get_array(op->ptr, "location", mval);
+
+ view3d_operator_needs_opengl(C);
+ BKE_object_update_select_id(CTX_data_main(C));
+
if (obedit && object_only == false) {
if (obedit->type == OB_MESH) {
changed = EDBM_select_pick(C, mval, &params);
@@ -2999,21 +3033,25 @@ void VIEW3D_OT_select(wmOperatorType *ot)
prop = RNA_def_boolean(
ot->srna,
"center",
- 0,
+ false,
"Center",
"Use the object center when selecting, in edit mode used to extend object selection");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(
- ot->srna, "enumerate", 0, "Enumerate", "List objects under the mouse (object mode only)");
+ prop = RNA_def_boolean(ot->srna,
+ "enumerate",
+ false,
+ "Enumerate",
+ "List objects under the mouse (object mode only)");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- prop = RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (edit mode only)");
+ prop = RNA_def_boolean(
+ ot->srna, "object", false, "Object", "Use object selection (edit mode only)");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
/* Needed for select-through to usefully drag handles, see: T98254.
* NOTE: this option may be removed and become default behavior, see design task: T98552. */
prop = RNA_def_boolean(ot->srna,
"vert_without_handles",
- 0,
+ false,
"Control Point Without Handles",
"Only select the curve control point, not it's handles");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
@@ -3021,7 +3059,7 @@ void VIEW3D_OT_select(wmOperatorType *ot)
prop = RNA_def_int_vector(ot->srna,
"location",
2,
- NULL,
+ nullptr,
INT_MIN,
INT_MAX,
"Location",
@@ -3037,7 +3075,7 @@ void VIEW3D_OT_select(wmOperatorType *ot)
/** \name Box Select
* \{ */
-typedef struct BoxSelectUserData {
+struct BoxSelectUserData {
ViewContext *vc;
const rcti *rect;
const rctf *rect_fl;
@@ -3048,7 +3086,7 @@ typedef struct BoxSelectUserData {
/* runtime */
bool is_done;
bool is_changed;
-} BoxSelectUserData;
+};
static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data,
ViewContext *vc,
@@ -3063,7 +3101,7 @@ static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data,
r_data->sel_op = sel_op;
/* SELECT by default, but can be changed if needed (only few cases use and respect this). */
- r_data->select_flag = SELECT;
+ r_data->select_flag = (eBezTriple_Flag)SELECT;
/* runtime */
r_data->is_done = false;
@@ -3084,7 +3122,7 @@ static void do_paintvert_box_select__doSelectVert(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- BoxSelectUserData *data = userData;
+ BoxSelectUserData *data = static_cast<BoxSelectUserData *>(userData);
const bool is_select = mv->flag & SELECT;
const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
@@ -3100,11 +3138,9 @@ static bool do_paintvert_box_select(ViewContext *vc,
{
const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
- Mesh *me;
-
- me = vc->obact->data;
- if ((me == NULL) || (me->totvert == 0)) {
- return OPERATOR_CANCELLED;
+ Mesh *me = static_cast<Mesh *>(vc->obact->data);
+ if ((me == nullptr) || (me->totvert == 0)) {
+ return false;
}
bool changed = false;
@@ -3116,14 +3152,14 @@ static bool do_paintvert_box_select(ViewContext *vc,
/* pass */
}
else if (use_zbuf) {
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
- if (wm_userdata->data == NULL) {
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
+ if (wm_userdata->data == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX);
- esel = wm_userdata->data;
+ esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
esel->select_bitmap = DRW_select_buffer_bitmap_from_rect(
- vc->depsgraph, vc->region, vc->v3d, rect, NULL);
+ vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
}
- if (esel->select_bitmap != NULL) {
+ if (esel->select_bitmap != nullptr) {
changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op);
}
}
@@ -3152,13 +3188,13 @@ static bool do_paintvert_box_select(ViewContext *vc,
static bool do_paintface_box_select(ViewContext *vc,
wmGenericUserData *wm_userdata,
const rcti *rect,
- int sel_op)
+ eSelectOp sel_op)
{
Object *ob = vc->obact;
Mesh *me;
me = BKE_mesh_from_object(ob);
- if ((me == NULL) || (me->totpoly == 0)) {
+ if ((me == nullptr) || (me->totpoly == 0)) {
return false;
}
@@ -3171,20 +3207,20 @@ static bool do_paintface_box_select(ViewContext *vc,
/* pass */
}
else {
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
- if (wm_userdata->data == NULL) {
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
+ if (wm_userdata->data == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE);
- esel = wm_userdata->data;
+ esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
esel->select_bitmap = DRW_select_buffer_bitmap_from_rect(
- vc->depsgraph, vc->region, vc->v3d, rect, NULL);
+ vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
}
- if (esel->select_bitmap != NULL) {
+ if (esel->select_bitmap != nullptr) {
changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op);
}
}
if (changed) {
- paintface_flush_flags(vc->C, vc->obact, SELECT);
+ paintface_flush_flags(vc->C, vc->obact, true, false);
}
return changed;
}
@@ -3197,7 +3233,7 @@ static void do_nurbs_box_select__doSelect(void *userData,
bool handles_visible,
const float screen_co[2])
{
- BoxSelectUserData *data = userData;
+ BoxSelectUserData *data = static_cast<BoxSelectUserData *>(userData);
const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
if (bp) {
@@ -3254,14 +3290,14 @@ static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel
data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
}
- BKE_curve_nurb_vert_active_validate(vc->obedit->data);
+ BKE_curve_nurb_vert_active_validate(curve);
return data.is_changed;
}
static void do_lattice_box_select__doSelect(void *userData, BPoint *bp, const float screen_co[2])
{
- BoxSelectUserData *data = userData;
+ BoxSelectUserData *data = static_cast<BoxSelectUserData *>(userData);
const bool is_select = bp->f1 & SELECT;
const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
@@ -3292,7 +3328,7 @@ static void do_mesh_box_select__doSelectVert(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- BoxSelectUserData *data = userData;
+ BoxSelectUserData *data = static_cast<BoxSelectUserData *>(userData);
const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
@@ -3303,7 +3339,7 @@ static void do_mesh_box_select__doSelectVert(void *userData,
}
struct BoxSelectUserData_ForMeshEdge {
BoxSelectUserData *data;
- struct EditSelectBuf_Cache *esel;
+ EditSelectBuf_Cache *esel;
uint backbuf_offset;
};
/**
@@ -3312,7 +3348,8 @@ struct BoxSelectUserData_ForMeshEdge {
static void do_mesh_box_select__doSelectEdge_pass0(
void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
{
- struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData;
+ BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast<BoxSelectUserData_ForMeshEdge *>(
+ userData);
BoxSelectUserData *data = data_for_edge->data;
bool is_visible = true;
if (data_for_edge->backbuf_offset) {
@@ -3336,7 +3373,8 @@ static void do_mesh_box_select__doSelectEdge_pass0(
static void do_mesh_box_select__doSelectEdge_pass1(
void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
{
- struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData;
+ BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast<BoxSelectUserData_ForMeshEdge *>(
+ userData);
BoxSelectUserData *data = data_for_edge->data;
bool is_visible = true;
if (data_for_edge->backbuf_offset) {
@@ -3357,7 +3395,7 @@ static void do_mesh_box_select__doSelectFace(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- BoxSelectUserData *data = userData;
+ BoxSelectUserData *data = static_cast<BoxSelectUserData *>(userData);
const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
@@ -3390,13 +3428,13 @@ static bool do_mesh_box_select(ViewContext *vc,
const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
if (use_zbuf) {
- if (wm_userdata->data == NULL) {
+ if (wm_userdata->data == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode);
- esel = wm_userdata->data;
+ esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
esel->select_bitmap = DRW_select_buffer_bitmap_from_rect(
- vc->depsgraph, vc->region, vc->v3d, rect, NULL);
+ vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
}
}
@@ -3412,16 +3450,16 @@ static bool do_mesh_box_select(ViewContext *vc,
}
if (ts->selectmode & SCE_SELECT_EDGE) {
/* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
- struct BoxSelectUserData_ForMeshEdge cb_data = {
- .data = &data,
- .esel = use_zbuf ? esel : NULL,
- .backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem(
- vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
- 0,
+ struct BoxSelectUserData_ForMeshEdge cb_data {
};
+ cb_data.data = &data;
+ cb_data.esel = use_zbuf ? esel : nullptr;
+ cb_data.backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem(
+ vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
+ 0;
const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR |
- (use_zbuf ? 0 : V3D_PROJ_TEST_CLIP_BB);
+ (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB);
/* Fully inside. */
mesh_foreachScreenEdge_clip_bb_segment(
vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag);
@@ -3471,7 +3509,8 @@ static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectO
}
int metaelem_id = 0;
- for (ml = mb->editelems->first; ml; ml = ml->next, metaelem_id += 0x10000) {
+ for (ml = static_cast<MetaElem *>(mb->editelems->first); ml;
+ ml = ml->next, metaelem_id += 0x10000) {
bool is_inside_radius = false;
bool is_inside_stiff = false;
@@ -3535,7 +3574,7 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel
uint bases_len = 0;
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
- vc->view_layer, vc->v3d, &bases_len);
+ vc->scene, vc->view_layer, vc->v3d, &bases_len);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
changed |= ED_armature_edit_deselect_all_visible_multi_ex(bases, bases_len);
@@ -3545,7 +3584,7 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel
Object *obedit = bases[base_index]->object;
obedit->id.tag &= ~LIB_TAG_DOIT;
- bArmature *arm = obedit->data;
+ bArmature *arm = static_cast<bArmature *>(obedit->data);
ED_armature_ebone_listbase_temp_clear(arm->edbo);
}
@@ -3569,7 +3608,8 @@ static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSel
Object *obedit = bases[base_index]->object;
if (obedit->id.tag & LIB_TAG_DOIT) {
obedit->id.tag &= ~LIB_TAG_DOIT;
- changed |= ED_armature_edit_select_op_from_tagged(obedit->data, sel_op);
+ changed |= ED_armature_edit_select_op_from_tagged(static_cast<bArmature *>(obedit->data),
+ sel_op);
}
}
@@ -3607,33 +3647,33 @@ static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const
int totobj = MAXPICKELEMS; /* XXX solve later */
/* Selection buffer has bones potentially too, so we add #MAXPICKELEMS. */
- GPUSelectResult *buffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult),
- "selection buffer");
+ GPUSelectResult *buffer = static_cast<GPUSelectResult *>(
+ MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), __func__));
const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene,
vc->obact);
const int hits = view3d_opengl_select(
vc, buffer, (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
-
- LISTBASE_FOREACH (Base *, base, &vc->view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(vc->scene, vc->view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(vc->view_layer)) {
base->object->id.tag &= ~LIB_TAG_DOIT;
}
- Base **bases = NULL;
- BLI_array_declare(bases);
+ blender::Vector<Base *> bases;
bool changed = false;
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- changed |= object_deselect_all_visible(vc->view_layer, vc->v3d);
+ changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
}
+ ListBase *object_bases = BKE_view_layer_object_bases_get(vc->view_layer);
if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) {
goto finally;
}
- LISTBASE_FOREACH (Base *, base, &vc->view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, object_bases) {
if (BASE_SELECTABLE(v3d, base)) {
if ((base->object->runtime.select_id & 0x0000FFFF) != 0) {
- BLI_array_append(bases, base);
+ bases.append(base);
}
}
}
@@ -3645,13 +3685,13 @@ static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const
buf_iter++) {
bPoseChannel *pchan_dummy;
Base *base = ED_armature_base_and_pchan_from_select_buffer(
- bases, BLI_array_len(bases), buf_iter->id, &pchan_dummy);
- if (base != NULL) {
+ bases.data(), bases.size(), buf_iter->id, &pchan_dummy);
+ if (base != nullptr) {
base->object->id.tag |= LIB_TAG_DOIT;
}
}
- for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) {
+ for (Base *base = static_cast<Base *>(object_bases->first); base && hits; base = base->next) {
if (BASE_SELECTABLE(v3d, base)) {
const bool is_select = base->flag & BASE_SELECTED;
const bool is_inside = base->object->id.tag & LIB_TAG_DOIT;
@@ -3664,9 +3704,6 @@ static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const
}
finally:
- if (bases != NULL) {
- MEM_freeN(bases);
- }
MEM_freeN(buffer);
@@ -3679,14 +3716,13 @@ finally:
static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
{
- uint bases_len;
- Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len);
+ blender::Vector<Base *> bases = do_pose_tag_select_op_prepare(vc);
int totobj = MAXPICKELEMS; /* XXX solve later */
/* Selection buffer has bones potentially too, so add #MAXPICKELEMS. */
- GPUSelectResult *buffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult),
- "selection buffer");
+ GPUSelectResult *buffer = static_cast<GPUSelectResult *>(
+ MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(GPUSelectResult), __func__));
const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene,
vc->obact);
const int hits = view3d_opengl_select(
@@ -3710,16 +3746,16 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e
buf_iter++) {
Bone *bone;
Base *base = ED_armature_base_and_bone_from_select_buffer(
- bases, bases_len, buf_iter->id, &bone);
+ bases.data(), bases.size(), buf_iter->id, &bone);
- if (base == NULL) {
+ if (base == nullptr) {
continue;
}
/* Loop over contiguous bone hits for 'base'. */
for (; buf_iter != buf_end; buf_iter++) {
/* should never fail */
- if (bone != NULL) {
+ if (bone != nullptr) {
base->object->id.tag |= LIB_TAG_DOIT;
bone->flag |= BONE_DONE;
}
@@ -3730,28 +3766,26 @@ static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const e
if ((base->object->runtime.select_id & 0x0000FFFF) != (col_next->id & 0x0000FFFF)) {
break;
}
- if (base->object->pose != NULL) {
+ if (base->object->pose != nullptr) {
const uint hit_bone = (col_next->id & ~BONESEL_ANY) >> 16;
- bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone);
- bone = pchan ? pchan->bone : NULL;
+ bPoseChannel *pchan = static_cast<bPoseChannel *>(
+ BLI_findlink(&base->object->pose->chanbase, hit_bone));
+ bone = pchan ? pchan->bone : nullptr;
}
else {
- bone = NULL;
+ bone = nullptr;
}
}
}
}
}
- const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op);
+ const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
if (changed_multi) {
DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
}
- if (bases != NULL) {
- MEM_freeN(bases);
- }
MEM_freeN(buffer);
return changed_multi;
@@ -3764,7 +3798,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
rcti rect;
bool changed_multi = false;
- wmGenericUserData wm_userdata_buf = {0};
+ wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
wmGenericUserData *wm_userdata = &wm_userdata_buf;
view3d_operator_needs_opengl(C);
@@ -3773,12 +3807,12 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
/* setup view context for argument to callbacks */
ED_view3d_viewcontext_init(C, &vc, depsgraph);
- eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
+ eSelectOp sel_op = static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode"));
WM_operator_properties_border_to_rcti(op, &rect);
if (vc.obedit) {
FOREACH_OBJECT_IN_MODE_BEGIN (
- vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter) {
+ vc.scene, vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter) {
ED_view3d_viewcontext_init_object(&vc, ob_iter);
bool changed = false;
@@ -3787,7 +3821,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
vc.em = BKE_editmesh_from_object(vc.obedit);
changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op);
if (changed) {
- DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
}
break;
@@ -3795,14 +3829,14 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
case OB_SURF:
changed = do_nurbs_box_select(&vc, &rect, sel_op);
if (changed) {
- DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
}
break;
case OB_MBALL:
changed = do_meta_box_select(&vc, &rect, sel_op);
if (changed) {
- DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
}
break;
@@ -3817,7 +3851,7 @@ static int view3d_box_select_exec(bContext *C, wmOperator *op)
case OB_LATTICE:
changed = do_lattice_box_select(&vc, &rect, sel_op);
if (changed) {
- DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
}
break;
@@ -3889,7 +3923,7 @@ void VIEW3D_OT_select_box(wmOperatorType *ot)
/** \name Circle Select
* \{ */
-typedef struct CircleSelectUserData {
+struct CircleSelectUserData {
ViewContext *vc;
bool select;
int mval[2];
@@ -3900,7 +3934,7 @@ typedef struct CircleSelectUserData {
/* runtime */
bool is_changed;
-} CircleSelectUserData;
+};
static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data,
ViewContext *vc,
@@ -3918,7 +3952,7 @@ static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data,
r_data->radius_squared = rad * rad;
/* SELECT by default, but can be changed if needed (only few cases use and respect this). */
- r_data->select_flag = SELECT;
+ r_data->select_flag = (eBezTriple_Flag)SELECT;
/* runtime */
r_data->is_changed = false;
@@ -3929,7 +3963,7 @@ static void mesh_circle_doSelectVert(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
BM_vert_select_set(data->vc->em->bm, eve, data->select);
@@ -3942,7 +3976,7 @@ static void mesh_circle_doSelectEdge(void *userData,
const float screen_co_b[2],
int UNUSED(index))
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
BM_edge_select_set(data->vc->em->bm, eed, data->select);
@@ -3954,7 +3988,7 @@ static void mesh_circle_doSelectFace(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
BM_face_select_set(data->vc->em->bm, efa, data->select);
@@ -3991,22 +4025,22 @@ static bool mesh_circle_select(ViewContext *vc,
const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
if (use_zbuf) {
- if (wm_userdata->data == NULL) {
+ if (wm_userdata->data == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, ts->selectmode);
}
}
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
if (use_zbuf) {
- if (esel->select_bitmap == NULL) {
+ if (esel->select_bitmap == nullptr) {
esel->select_bitmap = DRW_select_buffer_bitmap_from_circle(
- vc->depsgraph, vc->region, vc->v3d, mval, (int)(rad + 1.0f), NULL);
+ vc->depsgraph, vc->region, vc->v3d, mval, (int)(rad + 1.0f), nullptr);
}
}
if (ts->selectmode & SCE_SELECT_VERTEX) {
if (use_zbuf) {
- if (esel->select_bitmap != NULL) {
+ if (esel->select_bitmap != nullptr) {
changed |= edbm_backbuf_check_and_select_verts(
esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
}
@@ -4018,7 +4052,7 @@ static bool mesh_circle_select(ViewContext *vc,
if (ts->selectmode & SCE_SELECT_EDGE) {
if (use_zbuf) {
- if (esel->select_bitmap != NULL) {
+ if (esel->select_bitmap != nullptr) {
changed |= edbm_backbuf_check_and_select_edges(
esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
}
@@ -4034,7 +4068,7 @@ static bool mesh_circle_select(ViewContext *vc,
if (ts->selectmode & SCE_SELECT_FACE) {
if (use_zbuf) {
- if (esel->select_bitmap != NULL) {
+ if (esel->select_bitmap != nullptr) {
changed |= edbm_backbuf_check_and_select_faces(
esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
}
@@ -4061,7 +4095,7 @@ static bool paint_facesel_circle_select(ViewContext *vc,
{
BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
Object *ob = vc->obact;
- Mesh *me = ob->data;
+ Mesh *me = static_cast<Mesh *>(ob->data);
bool changed = false;
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
@@ -4069,23 +4103,23 @@ static bool paint_facesel_circle_select(ViewContext *vc,
changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
}
- if (wm_userdata->data == NULL) {
+ if (wm_userdata->data == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_FACE);
}
{
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
esel->select_bitmap = DRW_select_buffer_bitmap_from_circle(
- vc->depsgraph, vc->region, vc->v3d, mval, (int)(rad + 1.0f), NULL);
- if (esel->select_bitmap != NULL) {
+ vc->depsgraph, vc->region, vc->v3d, mval, (int)(rad + 1.0f), nullptr);
+ if (esel->select_bitmap != nullptr) {
changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op);
MEM_freeN(esel->select_bitmap);
- esel->select_bitmap = NULL;
+ esel->select_bitmap = nullptr;
}
}
if (changed) {
- paintface_flush_flags(vc->C, ob, SELECT);
+ paintface_flush_flags(vc->C, ob, true, false);
}
return changed;
}
@@ -4095,7 +4129,7 @@ static void paint_vertsel_circle_select_doSelectVert(void *userData,
const float screen_co[2],
int UNUSED(index))
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
SET_FLAG_FROM_TEST(mv->flag, data->select, SELECT);
@@ -4111,8 +4145,8 @@ static bool paint_vertsel_circle_select(ViewContext *vc,
BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
Object *ob = vc->obact;
- Mesh *me = ob->data;
- /* CircleSelectUserData data = {NULL}; */ /* UNUSED */
+ Mesh *me = static_cast<Mesh *>(ob->data);
+ /* CircleSelectUserData data = {nullptr}; */ /* UNUSED */
bool changed = false;
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
@@ -4123,19 +4157,19 @@ static bool paint_vertsel_circle_select(ViewContext *vc,
const bool select = (sel_op != SEL_OP_SUB);
if (use_zbuf) {
- if (wm_userdata->data == NULL) {
+ if (wm_userdata->data == nullptr) {
editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc, SCE_SELECT_VERTEX);
}
}
if (use_zbuf) {
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
esel->select_bitmap = DRW_select_buffer_bitmap_from_circle(
- vc->depsgraph, vc->region, vc->v3d, mval, (int)(rad + 1.0f), NULL);
- if (esel->select_bitmap != NULL) {
+ vc->depsgraph, vc->region, vc->v3d, mval, (int)(rad + 1.0f), nullptr);
+ if (esel->select_bitmap != nullptr) {
changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op);
MEM_freeN(esel->select_bitmap);
- esel->select_bitmap = NULL;
+ esel->select_bitmap = nullptr;
}
}
else {
@@ -4167,7 +4201,7 @@ static void nurbscurve_circle_doSelect(void *userData,
bool UNUSED(handles_visible),
const float screen_co[2])
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
if (bp) {
@@ -4215,14 +4249,14 @@ static bool nurbscurve_circle_select(ViewContext *vc,
data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
}
- BKE_curve_nurb_vert_active_validate(vc->obedit->data);
+ BKE_curve_nurb_vert_active_validate(static_cast<Curve *>(vc->obedit->data));
return data.is_changed;
}
static void latticecurve_circle_doSelect(void *userData, BPoint *bp, const float screen_co[2])
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
@@ -4256,7 +4290,7 @@ static bool pchan_circle_doSelectJoint(void *userData,
bPoseChannel *pchan,
const float screen_co[2])
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
if (data->select) {
@@ -4265,17 +4299,17 @@ static bool pchan_circle_doSelectJoint(void *userData,
else {
pchan->bone->flag &= ~BONE_SELECTED;
}
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static void do_circle_select_pose__doSelectBone(void *userData,
- struct bPoseChannel *pchan,
+ bPoseChannel *pchan,
const float screen_co_a[2],
const float screen_co_b[2])
{
- CircleSelectUserData *data = userData;
- bArmature *arm = data->vc->obact->data;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
+ bArmature *arm = static_cast<bArmature *>(data->vc->obact->data);
if (!PBONE_SELECTABLE(arm, pchan->bone)) {
return;
}
@@ -4283,7 +4317,7 @@ static void do_circle_select_pose__doSelectBone(void *userData,
bool is_point_done = false;
int points_proj_tot = 0;
- /* project head location to screenspace */
+ /* Project head location to screen-space. */
if (screen_co_a[0] != IS_CLIPPED) {
points_proj_tot++;
if (pchan_circle_doSelectJoint(data, pchan, screen_co_a)) {
@@ -4291,7 +4325,7 @@ static void do_circle_select_pose__doSelectBone(void *userData,
}
}
- /* project tail location to screenspace */
+ /* Project tail location to screen-space. */
if (screen_co_b[0] != IS_CLIPPED) {
points_proj_tot++;
if (pchan_circle_doSelectJoint(data, pchan, screen_co_b)) {
@@ -4356,7 +4390,7 @@ static bool armature_circle_doSelectJoint(void *userData,
const float screen_co[2],
bool head)
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
if (head) {
@@ -4375,17 +4409,17 @@ static bool armature_circle_doSelectJoint(void *userData,
ebone->flag &= ~BONE_TIPSEL;
}
}
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static void do_circle_select_armature__doSelectBone(void *userData,
- struct EditBone *ebone,
+ EditBone *ebone,
const float screen_co_a[2],
const float screen_co_b[2])
{
- CircleSelectUserData *data = userData;
- const bArmature *arm = data->vc->obedit->data;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
+ const bArmature *arm = static_cast<const bArmature *>(data->vc->obedit->data);
if (!(data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone))) {
return;
}
@@ -4397,7 +4431,7 @@ static void do_circle_select_armature__doSelectBone(void *userData,
bool is_edge_done = false;
int points_proj_tot = 0;
- /* project head location to screenspace */
+ /* Project head location to screen-space. */
if (screen_co_a[0] != IS_CLIPPED) {
points_proj_tot++;
if (armature_circle_doSelectJoint(data, ebone, screen_co_a, true)) {
@@ -4405,7 +4439,7 @@ static void do_circle_select_armature__doSelectBone(void *userData,
}
}
- /* project tail location to screenspace */
+ /* Project tail location to screen-space. */
if (screen_co_b[0] != IS_CLIPPED) {
points_proj_tot++;
if (armature_circle_doSelectJoint(data, ebone, screen_co_b, false)) {
@@ -4434,19 +4468,19 @@ static void do_circle_select_armature__doSelectBone(void *userData,
data->is_changed |= is_point_done;
}
static void do_circle_select_armature__doSelectBone_clip_content(void *userData,
- struct EditBone *ebone,
+ EditBone *ebone,
const float screen_co_a[2],
const float screen_co_b[2])
{
- CircleSelectUserData *data = userData;
- bArmature *arm = data->vc->obedit->data;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
+ bArmature *arm = static_cast<bArmature *>(data->vc->obedit->data);
if (!(data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone))) {
return;
}
/* Set in the first pass, needed so circle select prioritizes joints. */
- if (ebone->temp.i == true) {
+ if (ebone->temp.i != 0) {
return;
}
@@ -4461,7 +4495,7 @@ static bool armature_circle_select(ViewContext *vc,
float rad)
{
CircleSelectUserData data;
- bArmature *arm = vc->obedit->data;
+ bArmature *arm = static_cast<bArmature *>(vc->obedit->data);
const bool select = (sel_op != SEL_OP_SUB);
@@ -4494,10 +4528,10 @@ static bool armature_circle_select(ViewContext *vc,
}
static void do_circle_select_mball__doSelectElem(void *userData,
- struct MetaElem *ml,
+ MetaElem *ml,
const float screen_co[2])
{
- CircleSelectUserData *data = userData;
+ CircleSelectUserData *data = static_cast<CircleSelectUserData *>(userData);
if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
if (data->select) {
@@ -4521,7 +4555,7 @@ static bool mball_circle_select(ViewContext *vc,
view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- data.is_changed |= BKE_mball_deselect_all(vc->obedit->data);
+ data.is_changed |= BKE_mball_deselect_all(static_cast<MetaBall *>(vc->obedit->data));
}
ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
@@ -4569,7 +4603,7 @@ static bool obedit_circle_select(bContext *C,
}
if (changed) {
- DEG_id_tag_update(vc->obact->data, ID_RECALC_SELECT);
+ DEG_id_tag_update(static_cast<ID *>(vc->obact->data), ID_RECALC_SELECT);
WM_main_add_notifier(NC_GEOM | ND_SELECT, vc->obact->data);
}
return changed;
@@ -4581,21 +4615,21 @@ static bool object_circle_select(ViewContext *vc,
float rad)
{
BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
+ Scene *scene = vc->scene;
ViewLayer *view_layer = vc->view_layer;
View3D *v3d = vc->v3d;
const float radius_squared = rad * rad;
- const float mval_fl[2] = {mval[0], mval[1]};
+ const float mval_fl[2] = {static_cast<float>(mval[0]), static_cast<float>(mval[1])};
bool changed = false;
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- changed |= object_deselect_all_visible(vc->view_layer, vc->v3d);
+ changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
}
const bool select = (sel_op != SEL_OP_SUB);
const int select_flag = select ? BASE_SELECTED : 0;
-
- Base *base;
- for (base = FIRSTBASE(view_layer); base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) {
float screen_co[2];
if (ED_view3d_project_float_global(
@@ -4615,7 +4649,7 @@ static bool object_circle_select(ViewContext *vc,
/* not a real operator, only for circle test */
static void view3d_circle_select_recalc(void *user_data)
{
- bContext *C = user_data;
+ bContext *C = static_cast<bContext *>(user_data);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
ViewContext vc;
ED_view3d_viewcontext_init(C, &vc, depsgraph);
@@ -4625,7 +4659,7 @@ static void view3d_circle_select_recalc(void *user_data)
switch (vc.obedit->type) {
case OB_MESH: {
FOREACH_OBJECT_IN_MODE_BEGIN (
- vc.view_layer, vc.v3d, vc.obact->type, vc.obact->mode, ob_iter) {
+ vc.scene, vc.view_layer, vc.v3d, vc.obact->type, vc.obact->mode, ob_iter) {
ED_view3d_viewcontext_init_object(&vc, ob_iter);
BM_mesh_select_mode_flush_ex(
vc.em->bm, vc.em->selectmode, BM_SELECT_LEN_FLUSH_RECALC_ALL);
@@ -4663,12 +4697,12 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
const int mval[2] = {RNA_int_get(op->ptr, "x"), RNA_int_get(op->ptr, "y")};
/* Allow each selection type to allocate their own data that's used between executions. */
- wmGesture *gesture = op->customdata; /* NULL when non-modal. */
- wmGenericUserData wm_userdata_buf = {0};
+ wmGesture *gesture = static_cast<wmGesture *>(op->customdata); /* nullptr when non-modal. */
+ wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
wmGenericUserData *wm_userdata = gesture ? &gesture->user_data : &wm_userdata_buf;
- const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
- WM_gesture_is_modal_first(gesture));
+ const eSelectOp sel_op = ED_select_op_modal(
+ static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode")), WM_gesture_is_modal_first(gesture));
ED_view3d_viewcontext_init(C, &vc, depsgraph);
@@ -4677,11 +4711,12 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
if (obedit || BKE_paint_select_elem_test(obact) || (obact && (obact->mode & OB_MODE_POSE))) {
view3d_operator_needs_opengl(C);
- if (obedit == NULL) {
+ if (obedit == nullptr) {
BKE_object_update_select_id(CTX_data_main(C));
}
- FOREACH_OBJECT_IN_MODE_BEGIN (vc.view_layer, vc.v3d, obact->type, obact->mode, ob_iter) {
+ FOREACH_OBJECT_IN_MODE_BEGIN (
+ vc.scene, vc.view_layer, vc.v3d, obact->type, obact->mode, ob_iter) {
ED_view3d_viewcontext_init_object(&vc, ob_iter);
obact = vc.obact;
@@ -4729,10 +4764,10 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op)
WM_generic_user_data_free(wm_userdata);
}
else {
- struct EditSelectBuf_Cache *esel = wm_userdata->data;
+ EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
if (esel && esel->select_bitmap) {
MEM_freeN(esel->select_bitmap);
- esel->select_bitmap = NULL;
+ esel->select_bitmap = nullptr;
}
}
diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c
index 2f51b2dce3b..a5ecef69ff8 100644
--- a/source/blender/editors/space_view3d/view3d_snap.c
+++ b/source/blender/editors/space_view3d/view3d_snap.c
@@ -69,7 +69,7 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -111,7 +111,7 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
else if (OBPOSE_FROM_OBACT(obact)) {
struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
uint objects_len = 0;
- Object **objects_eval = BKE_object_pose_array_get(view_layer_eval, v3d, &objects_len);
+ Object **objects_eval = BKE_object_pose_array_get(scene, view_layer_eval, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_eval = objects_eval[ob_index];
Object *ob = DEG_get_original_object(ob_eval);
@@ -203,7 +203,7 @@ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
xcs = ED_object_xform_skip_child_container_create();
ED_object_xform_skip_child_container_item_ensure_from_array(
- xcs, view_layer, objects, objects_eval_len);
+ xcs, scene, view_layer, objects, objects_eval_len);
MEM_freeN(objects);
}
if (use_transform_data_origin) {
@@ -326,7 +326,7 @@ static bool snap_selected_to_location(bContext *C,
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
obedit = objects[ob_index];
@@ -376,7 +376,7 @@ static bool snap_selected_to_location(bContext *C,
struct KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_LOCATION_ID);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
- Object **objects = BKE_object_pose_array_get(view_layer, v3d, &objects_len);
+ Object **objects = BKE_object_pose_array_get(scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -487,7 +487,7 @@ static bool snap_selected_to_location(bContext *C,
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
xcs = ED_object_xform_skip_child_container_create();
ED_object_xform_skip_child_container_item_ensure_from_array(
- xcs, view_layer, objects, objects_len);
+ xcs, scene, view_layer, objects, objects_len);
}
if (use_transform_data_origin) {
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
@@ -789,7 +789,7 @@ static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_curs
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
obedit = objects[ob_index];
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 1fabdef8da2..cb716391fb2 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -44,6 +44,7 @@
#include "ED_keyframing.h"
#include "ED_screen.h"
+#include "ED_undo.h"
#include "ED_view3d.h"
#include "UI_resources.h"
@@ -688,6 +689,60 @@ bool ED_view3d_camera_lock_autokey(View3D *v3d,
return false;
}
+bool ED_view3d_camera_lock_undo_test(const View3D *v3d,
+ const RegionView3D *rv3d,
+ struct bContext *C)
+{
+ if (ED_view3d_camera_lock_check(v3d, rv3d)) {
+ if (ED_undo_is_memfile_compatible(C)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Create a MEMFILE undo-step for locked camera movement when transforming the view.
+ * Edit and texture paint mode don't use MEMFILE undo so undo push is skipped for them.
+ * NDOF and track-pad navigation would create an undo step on every gesture and we may end up with
+ * unnecessary undo steps so undo push for them is not supported for now.
+ * Operators that use smooth view for navigation are supported via an optional parameter field,
+ * see: #V3D_SmoothParams.undo_str.
+ */
+static bool view3d_camera_lock_undo_ex(const char *str,
+ const View3D *v3d,
+ const RegionView3D *rv3d,
+ struct bContext *C,
+ const bool undo_group)
+{
+ if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) {
+ if (undo_group) {
+ ED_undo_grouped_push(C, str);
+ }
+ else {
+ ED_undo_push(C, str);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool ED_view3d_camera_lock_undo_push(const char *str,
+ const View3D *v3d,
+ const RegionView3D *rv3d,
+ bContext *C)
+{
+ return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, false);
+}
+
+bool ED_view3d_camera_lock_undo_grouped_push(const char *str,
+ const View3D *v3d,
+ const RegionView3D *rv3d,
+ bContext *C)
+{
+ return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, true);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1492,10 +1547,12 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain,
depsgraph, scene, camera_ob_eval, co, &scale, r_clip_start, r_clip_end)) {
ObjectTfmProtectedChannels obtfm;
float obmat_new[4][4];
+ bool is_ortho_camera = false;
if ((camera_ob_eval->type == OB_CAMERA) &&
(((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
((Camera *)camera_ob->data)->ortho_scale = scale;
+ is_ortho_camera = true;
}
copy_m4_m4(obmat_new, camera_ob_eval->obmat);
@@ -1508,6 +1565,9 @@ static bool view3d_camera_to_view_selected_impl(struct Main *bmain,
/* notifiers */
DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM);
+ if (is_ortho_camera) {
+ DEG_id_tag_update_ex(bmain, camera_ob->data, ID_RECALC_PARAMETERS);
+ }
return true;
}
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index fc88737ca70..d0db4de0c47 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -202,6 +202,8 @@ static void sync_viewport_camera_smoothview(bContext *C,
.quat = other_rv3d->viewquat,
.dist = &other_rv3d->dist,
.lens = &other_v3d->lens,
+ /* No undo because this switches cameras. */
+ .undo_str = NULL,
});
}
else {
@@ -256,6 +258,8 @@ static int view3d_setobjectascamera_exec(bContext *C, wmOperator *op)
.quat = rv3d->viewquat,
.dist = &rv3d->dist,
.lens = &v3d->lens,
+ /* No undo because this switches cameras. */
+ .undo_str = NULL,
});
}
@@ -549,7 +553,8 @@ int view3d_opengl_select_ex(ViewContext *vc,
ARegion *region = vc->region;
rcti rect;
int hits = 0;
- const bool use_obedit_skip = (OBEDIT_FROM_VIEW_LAYER(vc->view_layer) != NULL) &&
+ BKE_view_layer_synced_ensure(scene, vc->view_layer);
+ const bool use_obedit_skip = (BKE_view_layer_edit_object_get(vc->view_layer) != NULL) &&
(vc->obedit == NULL);
const bool is_pick_select = (U.gpu_flag & USER_GPU_FLAG_NO_DEPT_PICK) == 0;
const bool do_passes = ((is_pick_select == false) &&
@@ -597,7 +602,7 @@ int view3d_opengl_select_ex(ViewContext *vc,
goto finally;
}
- /* Important to use 'vc->obact', not 'OBACT(vc->view_layer)' below,
+ /* Important to use 'vc->obact', not 'BKE_view_layer_active_object_get(vc->view_layer)' below,
* so it will be NULL when hidden. */
struct {
DRW_ObjectFilterFn fn;
@@ -820,6 +825,7 @@ static bool view3d_localview_init(const Depsgraph *depsgraph,
wmWindowManager *wm,
wmWindow *win,
Main *bmain,
+ const Scene *scene,
ViewLayer *view_layer,
ScrArea *area,
const bool frame_selected,
@@ -827,7 +833,6 @@ static bool view3d_localview_init(const Depsgraph *depsgraph,
ReportList *reports)
{
View3D *v3d = area->spacedata.first;
- Base *base;
float min[3], max[3], box[3];
float size = 0.0f;
uint local_view_bit;
@@ -848,12 +853,14 @@ static bool view3d_localview_init(const Depsgraph *depsgraph,
ok = false;
}
else {
- Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obedit = BKE_view_layer_edit_object_get(view_layer);
if (obedit) {
- for (base = FIRSTBASE(view_layer); base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->local_view_bits &= ~local_view_bit;
}
- FOREACH_BASE_IN_EDIT_MODE_BEGIN (view_layer, v3d, base_iter) {
+ FOREACH_BASE_IN_EDIT_MODE_BEGIN (scene, view_layer, v3d, base_iter) {
BKE_object_minmax(base_iter->object, min, max, false);
base_iter->local_view_bits |= local_view_bit;
ok = true;
@@ -861,7 +868,8 @@ static bool view3d_localview_init(const Depsgraph *depsgraph,
FOREACH_BASE_IN_EDIT_MODE_END;
}
else {
- for (base = FIRSTBASE(view_layer); base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_SELECTED(v3d, base)) {
BKE_object_minmax(base->object, min, max, false);
base->local_view_bits |= local_view_bit;
@@ -939,6 +947,8 @@ static bool view3d_localview_init(const Depsgraph *depsgraph,
.quat = rv3d->viewquat,
.dist = ok_dist ? &dist_new : NULL,
.lens = &v3d->lens,
+ /* No undo because this doesn't move the camera. */
+ .undo_str = NULL,
});
}
}
@@ -950,6 +960,7 @@ static bool view3d_localview_init(const Depsgraph *depsgraph,
static void view3d_localview_exit(const Depsgraph *depsgraph,
wmWindowManager *wm,
wmWindow *win,
+ const Scene *scene,
ViewLayer *view_layer,
ScrArea *area,
const bool frame_selected,
@@ -960,8 +971,8 @@ static void view3d_localview_exit(const Depsgraph *depsgraph,
if (v3d->localvd == NULL) {
return;
}
-
- for (Base *base = FIRSTBASE(view_layer); base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->local_view_bits & v3d->local_view_uuid) {
base->local_view_bits &= ~v3d->local_view_uuid;
}
@@ -1008,6 +1019,8 @@ static void view3d_localview_exit(const Depsgraph *depsgraph,
.ofs = rv3d->localvd->ofs,
.quat = rv3d->localvd->viewquat,
.dist = &rv3d->localvd->dist,
+ /* No undo because this doesn't move the camera. */
+ .undo_str = NULL,
});
}
@@ -1032,12 +1045,21 @@ static int localview_exec(bContext *C, wmOperator *op)
bool changed;
if (v3d->localvd) {
- view3d_localview_exit(depsgraph, wm, win, view_layer, area, frame_selected, smooth_viewtx);
+ view3d_localview_exit(
+ depsgraph, wm, win, scene, view_layer, area, frame_selected, smooth_viewtx);
changed = true;
}
else {
- changed = view3d_localview_init(
- depsgraph, wm, win, bmain, view_layer, area, frame_selected, smooth_viewtx, op->reports);
+ changed = view3d_localview_init(depsgraph,
+ wm,
+ win,
+ bmain,
+ scene,
+ view_layer,
+ area,
+ frame_selected,
+ smooth_viewtx,
+ op->reports);
}
if (changed) {
@@ -1085,13 +1107,13 @@ static int localview_remove_from_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
bool changed = false;
-
- for (Base *base = FIRSTBASE(view_layer); base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_SELECTED(v3d, base)) {
base->local_view_bits &= ~v3d->local_view_uuid;
ED_object_base_select(base, BA_DESELECT);
- if (base == BASACT(view_layer)) {
+ if (base == view_layer->basact) {
view_layer->basact = NULL;
}
changed = true;
@@ -1258,7 +1280,7 @@ void ED_view3d_local_collections_reset(struct bContext *C, const bool reset_all)
else if (reset_all && (do_reset || (local_view_bit != ~(0)))) {
view3d_local_collections_reset(bmain, ~(0));
View3D v3d = {.local_collections_uuid = ~(0)};
- BKE_layer_collection_local_sync(CTX_data_view_layer(C), &v3d);
+ BKE_layer_collection_local_sync(CTX_data_scene(C), CTX_data_view_layer(C), &v3d);
DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS);
}
}
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index 68c4f4e76ca..ec6f62e0f5b 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -15,7 +15,6 @@ set(INC
../../render
../../sequencer
../../windowmanager
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@@ -39,6 +38,7 @@ set(SRC
transform_convert_mesh_edge.c
transform_convert_mesh_skin.c
transform_convert_mesh_uv.c
+ transform_convert_mesh_vert_cdata.c
transform_convert_nla.c
transform_convert_node.c
transform_convert_object.c
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index d9e23b98c66..95aa48efd84 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -19,6 +19,7 @@
#include "BKE_context.h"
#include "BKE_editmesh.h"
+#include "BKE_layer.h"
#include "BKE_mask.h"
#include "BKE_scene.h"
@@ -484,7 +485,9 @@ static void viewRedrawForce(const bContext *C, TransInfo *t)
/* XXX how to deal with lock? */
SpaceImage *sima = (SpaceImage *)t->area->spacedata.first;
if (sima->lock) {
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, OBEDIT_FROM_VIEW_LAYER(t->view_layer)->data);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ WM_event_add_notifier(
+ C, NC_GEOM | ND_DATA, BKE_view_layer_edit_object_get(t->view_layer)->data);
}
else {
ED_area_tag_redraw(t->area);
@@ -525,7 +528,8 @@ static void viewRedrawPost(bContext *C, TransInfo *t)
UVCALC_TRANSFORM_CORRECT_SLIDE :
UVCALC_TRANSFORM_CORRECT;
- if ((t->data_type == TC_MESH_VERTS) && (t->settings->uvcalc_flag & uvcalc_correct_flag)) {
+ if ((t->data_type == &TransConvertType_Mesh) &&
+ (t->settings->uvcalc_flag & uvcalc_correct_flag)) {
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
}
@@ -847,7 +851,7 @@ static bool transform_event_modal_constraint(TransInfo *t, short modal_type)
return false;
}
- if (t->data_type == TC_SEQ_IMAGE_DATA) {
+ if (t->data_type == &TransConvertType_SequencerImage) {
/* Setup the 2d msg string so it writes out the transform space. */
msg_2d = msg_3d;
@@ -1327,7 +1331,7 @@ int transformEvent(TransInfo *t, const wmEvent *event)
handled = true;
}
- if (t->redraw && !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (t->redraw && !ISMOUSE_MOTION(event->type)) {
WM_window_status_area_tag_redraw(CTX_wm_window(t->context));
}
@@ -1475,7 +1479,8 @@ static void drawTransformPixel(const struct bContext *C, ARegion *region, void *
if (region == t->region) {
Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
/* draw auto-key-framing hint in the corner
* - only draw if enabled (advanced users may be distracted/annoyed),
@@ -1535,7 +1540,8 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op)
if (!(t->options & CTX_NO_PET)) {
if ((prop = RNA_struct_find_property(op->ptr, "use_proportional_edit")) &&
!RNA_property_is_set(op->ptr, prop)) {
- const Object *obact = OBACT(t->view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ const Object *obact = BKE_view_layer_active_object_get(t->view_layer);
if (t->spacetype == SPACE_GRAPH) {
ts->proportional_fcurve = use_prop_edit;
@@ -1713,11 +1719,17 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2])
int grid_size = SI_GRID_STEPS_LEN;
float zoom_factor = ED_space_image_zoom_level(v2d, grid_size);
float grid_steps[SI_GRID_STEPS_LEN];
+ float grid_steps_y[SI_GRID_STEPS_LEN];
- ED_space_image_grid_steps(sima, grid_steps, grid_size);
+ ED_space_image_grid_steps(sima, grid_steps, grid_steps_y, grid_size);
/* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */
r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps, zoom_factor);
r_snap[1] = r_snap[0] / 2.0f;
+
+ /* TODO: Implement snapping for custom grid sizes with `grid_steps[0] != grid_steps_y[0]`.
+ * r_snap_y[0] = ED_space_image_increment_snap_value(grid_size, grid_steps_y, zoom_factor);
+ * r_snap_y[1] = r_snap_y[0] / 2.0f;
+ */
}
else if (t->spacetype == SPACE_CLIP) {
r_snap[0] = 0.125f;
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 049d1b6a90e..09fc07f57f4 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -38,6 +38,7 @@ struct ReportList;
struct Scene;
struct ScrArea;
struct SnapObjectContext;
+struct TransConvertTypeInfo;
struct TransDataContainer;
struct TransInfo;
struct TransSnap;
@@ -204,36 +205,6 @@ typedef enum {
HLP_TRACKBALL = 6,
} eTHelpline;
-typedef enum {
- TC_NONE = 0,
- TC_ACTION_DATA,
- TC_POSE,
- TC_ARMATURE_VERTS,
- TC_CURSOR_IMAGE,
- TC_CURSOR_SEQUENCER,
- TC_CURSOR_VIEW3D,
- TC_CURVE_VERTS,
- TC_GRAPH_EDIT_DATA,
- TC_GPENCIL,
- TC_LATTICE_VERTS,
- TC_MASKING_DATA,
- TC_MBALL_VERTS,
- TC_MESH_VERTS,
- TC_MESH_EDGES,
- TC_MESH_SKIN,
- TC_MESH_UV,
- TC_NLA_DATA,
- TC_NODE_DATA,
- TC_OBJECT,
- TC_OBJECT_TEXSPACE,
- TC_PAINT_CURVE_VERTS,
- TC_PARTICLE_VERTS,
- TC_SCULPT,
- TC_SEQ_DATA,
- TC_SEQ_IMAGE_DATA,
- TC_TRACKING_DATA,
-} eTConvertType;
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -384,10 +355,12 @@ typedef struct MouseInput {
/** Initial mouse position. */
int imval[2];
- bool precision;
- float precision_factor;
+ float imval_unproj[3];
float center[2];
float factor;
+ float precision_factor;
+ bool precision;
+
/** Additional data, if needed by the particular function. */
void *data;
@@ -518,7 +491,7 @@ typedef struct TransInfo {
int data_len_all;
/** TODO: It should be a member of #TransDataContainer. */
- eTConvertType data_type;
+ struct TransConvertTypeInfo *data_type;
/** Current context/options for transform. */
eTContext options;
@@ -647,6 +620,9 @@ typedef struct TransInfo {
* value of the input parameter, except when a constrain is entered. */
float values_final[4];
+ /** Cache safe value for constraints that require iteration or are slow to calculate. */
+ float values_inside_constraints[4];
+
/* Axis members for modes that use an axis separate from the orientation (rotate & shear). */
/** Primary axis, rotate only uses this. */
@@ -685,6 +661,9 @@ typedef struct TransInfo {
/** Typically for mode settings. */
TransCustomDataContainer custom;
+
+ /* Needed for sculpt transform. */
+ const char *undo_name;
} TransInfo;
/** \} */
@@ -784,6 +763,7 @@ void applyMouseInput(struct TransInfo *t,
struct MouseInput *mi,
const int mval[2],
float output[3]);
+void transform_input_update(TransInfo *t, const float fac);
void setCustomPoints(TransInfo *t, MouseInput *mi, const int start[2], const int end[2]);
void setCustomPointsFromDirection(TransInfo *t, MouseInput *mi, const float dir[2]);
@@ -832,6 +812,7 @@ void calculateCenter2D(TransInfo *t);
void calculateCenterLocal(TransInfo *t, const float center_global[3]);
void calculateCenter(TransInfo *t);
+void tranformViewUpdate(TransInfo *t);
/* API functions for getting center points */
void calculateCenterBound(TransInfo *t, float r_center[3]);
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 658901a6991..d09bd99ef57 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -177,7 +177,7 @@ static void axisProjection(const TransInfo *t,
const float in[3],
float out[3])
{
- float norm[3], vec[3], factor, angle;
+ float vec[3], factor, angle;
float t_con_center[3];
if (is_zero_v3(in)) {
@@ -214,7 +214,7 @@ static void axisProjection(const TransInfo *t,
}
else {
float v[3];
- float norm_center[3];
+ float norm[3], norm_center[3];
float plane[3];
view_vector_calc(t, t_con_center, norm_center);
@@ -337,25 +337,20 @@ static bool isPlaneProjectionViewAligned(const TransInfo *t, const float plane[4
return fabsf(factor) < eps;
}
-static void planeProjection(const TransInfo *t, const float in[3], float out[3])
+static void planeProjection(const TransInfo *t,
+ const float plane[3],
+ const float in[3],
+ float out[3])
{
- float vec[3], factor, norm[3];
- add_v3_v3v3(vec, in, t->center_global);
- view_vector_calc(t, vec, norm);
+ float pos[3], view_vec[3], factor;
- sub_v3_v3v3(vec, out, in);
+ add_v3_v3v3(pos, in, t->center_global);
+ view_vector_calc(t, pos, view_vec);
- factor = dot_v3v3(vec, norm);
- if (factor == 0.0f) {
- return; /* prevent divide by zero */
+ if (isect_ray_plane_v3(pos, view_vec, plane, &factor, false)) {
+ madd_v3_v3v3fl(out, in, view_vec, factor);
}
- factor = dot_v3v3(vec, vec) / factor;
-
- copy_v3_v3(vec, norm);
- mul_v3_fl(vec, factor);
-
- add_v3_v3v3(out, in, vec);
}
static short transform_orientation_or_default(const TransInfo *t)
@@ -397,7 +392,6 @@ static void applyAxisConstraintVec(const TransInfo *t,
copy_v3_v3(out, in);
if (!td && t->con.mode & CON_APPLY) {
bool is_snap_to_point = false, is_snap_to_edge = false, is_snap_to_face = false;
- mul_m3_v3(t->con.pmtx, out);
if (activeSnap(t)) {
if (validSnap(t)) {
@@ -410,8 +404,11 @@ static void applyAxisConstraintVec(const TransInfo *t,
}
}
- /* With snap points, a projection is alright, no adjustments needed. */
- if (!is_snap_to_point || is_snap_to_edge || is_snap_to_face) {
+ if (is_snap_to_point) {
+ /* With snap points, a projection is alright, no adjustments needed. */
+ mul_m3_v3(t->con.pmtx, out);
+ }
+ else {
const int dims = getConstraintSpaceDimension(t);
if (dims == 2) {
if (!is_zero_v3(out)) {
@@ -428,7 +425,10 @@ static void applyAxisConstraintVec(const TransInfo *t,
else {
/* View alignment correction. */
if (!isPlaneProjectionViewAligned(t, plane)) {
- planeProjection(t, in, out);
+ planeProjection(t, plane, in, out);
+ }
+ else {
+ mul_m3_v3(t->con.pmtx, out);
}
}
}
@@ -701,12 +701,12 @@ void setLocalConstraint(TransInfo *t, int mode, const char text[])
}
}
-void setUserConstraint(TransInfo *t, int mode, const char ftext[])
+void setUserConstraint(TransInfo *t, int mode, const char text_[])
{
char text[256];
const short orientation = transform_orientation_or_default(t);
const char *spacename = transform_orientations_spacename_get(t, orientation);
- BLI_snprintf(text, sizeof(text), ftext, spacename);
+ BLI_snprintf(text, sizeof(text), text_, spacename);
switch (orientation) {
case V3D_ORIENT_LOCAL:
diff --git a/source/blender/editors/transform/transform_constraints.h b/source/blender/editors/transform/transform_constraints.h
index 9182330b729..90a693b089e 100644
--- a/source/blender/editors/transform/transform_constraints.h
+++ b/source/blender/editors/transform/transform_constraints.h
@@ -34,7 +34,7 @@ void setLocalConstraint(TransInfo *t, int mode, const char text[]);
* `ftext` is a format string passed to #BLI_snprintf. It will add the name of
* the orientation where %s is (logically).
*/
-void setUserConstraint(TransInfo *t, int mode, const char text[]);
+void setUserConstraint(TransInfo *t, int mode, const char text_[]);
void drawConstraint(TransInfo *t);
/**
* Called from drawview.c, as an extra per-window draw option.
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index d9b971c5478..1e29411fe84 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -479,132 +479,6 @@ TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTr
/** \name UV Coordinates
* \{ */
-/**
- * Find the correction for the scaling factor when "Constrain to Bounds" is active.
- * \param numerator: How far the UV boundary (unit square) is from the origin of the scale.
- * \param denominator: How far the AABB is from the origin of the scale.
- * \param scale: Scale parameter to update.
- */
-static void constrain_scale_to_boundary(const float numerator,
- const float denominator,
- float *scale)
-{
- if (denominator == 0.0f) {
- /* The origin of the scale is on the edge of the boundary. */
- if (numerator < 0.0f) {
- /* Negative scale will wrap around and put us outside the boundary. */
- *scale = 0.0f; /* Hold at the boundary instead. */
- }
- return; /* Nothing else we can do without more info. */
- }
-
- const float correction = numerator / denominator;
- if (correction < 0.0f || !isfinite(correction)) {
- /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */
- return;
- }
-
- if (denominator < 0.0f) {
- /* Scale origin is outside boundary, only make scale bigger. */
- if (*scale < correction) {
- *scale = correction;
- }
- return;
- }
-
- /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */
- if (*scale > correction) {
- *scale = correction;
- }
-}
-
-bool clipUVTransform(TransInfo *t, float vec[2], const bool resize)
-{
- bool clipx = true, clipy = true;
- float min[2], max[2];
-
- /* 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) {
-
- TransData *td;
- int a;
-
- for (a = 0, td = tc->data; a < tc->data_len; a++, td++) {
- minmax_v2v2_v2(min, max, td->loc);
- }
- }
-
- if (resize) {
- /* Assume no change is required. */
- float scalex = 1.0f;
- float scaley = 1.0f;
-
- /* Update U against the left border. */
- constrain_scale_to_boundary(
- t->center_global[0] - base_offset[0], t->center_global[0] - min[0], &scalex);
- /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */
- constrain_scale_to_boundary(base_offset[0] + t->aspect[0] - t->center_global[0],
- max[0] - t->center_global[0],
- &scalex);
-
- /* Do the same for the V co-ordinate, which is called `y`. */
- constrain_scale_to_boundary(
- t->center_global[1] - base_offset[1], t->center_global[1] - min[1], &scaley);
- constrain_scale_to_boundary(base_offset[1] + t->aspect[1] - t->center_global[1],
- max[1] - t->center_global[1],
- &scaley);
-
- clipx = (scalex != 1.0f);
- clipy = (scaley != 1.0f);
- vec[0] *= scalex;
- vec[1] *= scaley;
- }
- else {
- if (min[0] < base_offset[0]) {
- vec[0] += base_offset[0] - min[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] < base_offset[1]) {
- vec[1] += base_offset[1] - min[1];
- }
- else if (max[1] > base_offset[1] + t->aspect[1]) {
- vec[1] -= max[1] - base_offset[1] - t->aspect[1];
- }
- else {
- clipy = 0;
- }
- }
-
- return (clipx || clipy);
-}
-
void clipUVData(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -769,7 +643,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand
}
else {
/* Delete Keyframe */
- delete_fcurve_key(fcu, i, 0);
+ BKE_fcurve_delete_key(fcu, i);
}
/* Update count of how many we've deleted
@@ -779,7 +653,7 @@ void posttrans_fcurve_clean(FCurve *fcu, const int sel_flag, const bool use_hand
}
else {
/* Always delete - Unselected keys don't matter */
- delete_fcurve_key(fcu, i, 0);
+ BKE_fcurve_delete_key(fcu, i);
}
/* Stop the RK search... we've found our match now */
@@ -902,62 +776,12 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
return;
}
- BLI_assert(CTX_data_main(t->context) == CTX_data_main(C));
- switch (t->data_type) {
- case TC_ACTION_DATA:
- special_aftertrans_update__actedit(C, t);
- break;
- case TC_POSE:
- special_aftertrans_update__pose(C, t);
- break;
- case TC_GRAPH_EDIT_DATA:
- special_aftertrans_update__graph(C, t);
- break;
- case TC_MASKING_DATA:
- special_aftertrans_update__mask(C, t);
- break;
- case TC_MESH_VERTS:
- case TC_MESH_EDGES:
- special_aftertrans_update__mesh(C, t);
- break;
- case TC_NLA_DATA:
- special_aftertrans_update__nla(C, t);
- break;
- case TC_NODE_DATA:
- special_aftertrans_update__node(C, t);
- break;
- case TC_OBJECT:
- special_aftertrans_update__object(C, t);
- break;
- case TC_SCULPT:
- special_aftertrans_update__sculpt(C, t);
- break;
- case TC_SEQ_DATA:
- special_aftertrans_update__sequencer(C, t);
- break;
- case TC_SEQ_IMAGE_DATA:
- special_aftertrans_update__sequencer_image(C, t);
- break;
- case TC_TRACKING_DATA:
- special_aftertrans_update__movieclip(C, t);
- break;
- case TC_ARMATURE_VERTS:
- case TC_CURSOR_IMAGE:
- case TC_CURSOR_SEQUENCER:
- case TC_CURSOR_VIEW3D:
- case TC_CURVE_VERTS:
- case TC_GPENCIL:
- case TC_LATTICE_VERTS:
- case TC_MBALL_VERTS:
- case TC_MESH_UV:
- case TC_MESH_SKIN:
- case TC_OBJECT_TEXSPACE:
- case TC_PAINT_CURVE_VERTS:
- case TC_PARTICLE_VERTS:
- case TC_NONE:
- default:
- break;
+ if (!t->data_type || !t->data_type->special_aftertrans_update) {
+ return;
}
+
+ BLI_assert(CTX_data_main(t->context) == CTX_data_main(C));
+ t->data_type->special_aftertrans_update(C, t);
}
int special_transform_moving(TransInfo *t)
@@ -1018,54 +842,44 @@ static int countAndCleanTransDataContainer(TransInfo *t)
static void init_proportional_edit(TransInfo *t)
{
- eTConvertType convert_type = t->data_type;
- switch (convert_type) {
- case TC_ACTION_DATA:
- case TC_CURVE_VERTS:
- case TC_GRAPH_EDIT_DATA:
- case TC_GPENCIL:
- case TC_LATTICE_VERTS:
- case TC_MASKING_DATA:
- case TC_MBALL_VERTS:
- case TC_MESH_VERTS:
- case TC_MESH_EDGES:
- case TC_MESH_SKIN:
- case TC_MESH_UV:
- case TC_NODE_DATA:
- case TC_OBJECT:
- case TC_PARTICLE_VERTS:
- break;
- case TC_POSE: /* Disable PET, its not usable in pose mode yet T32444. */
- case TC_ARMATURE_VERTS:
- case TC_CURSOR_IMAGE:
- case TC_CURSOR_SEQUENCER:
- case TC_CURSOR_VIEW3D:
- case TC_NLA_DATA:
- case TC_OBJECT_TEXSPACE:
- case TC_PAINT_CURVE_VERTS:
- case TC_SCULPT:
- case TC_SEQ_DATA:
- case TC_SEQ_IMAGE_DATA:
- case TC_TRACKING_DATA:
- case TC_NONE:
- default:
- t->options |= CTX_NO_PET;
- t->flag &= ~T_PROP_EDIT_ALL;
- return;
+ /* NOTE: PET is not usable in pose mode yet T32444. */
+ if (!ELEM(t->data_type,
+ &TransConvertType_Action,
+ &TransConvertType_Curve,
+ &TransConvertType_Graph,
+ &TransConvertType_GPencil,
+ &TransConvertType_Lattice,
+ &TransConvertType_Mask,
+ &TransConvertType_MBall,
+ &TransConvertType_Mesh,
+ &TransConvertType_MeshEdge,
+ &TransConvertType_MeshSkin,
+ &TransConvertType_MeshUV,
+ &TransConvertType_MeshVertCData,
+ &TransConvertType_Node,
+ &TransConvertType_Object,
+ &TransConvertType_Particle)) {
+ /* Disable PET */
+ t->options |= CTX_NO_PET;
+ t->flag &= ~T_PROP_EDIT_ALL;
+ return;
}
if (t->data_len_all && (t->flag & T_PROP_EDIT)) {
- if (convert_type == TC_OBJECT) {
+ if (t->data_type == &TransConvertType_Object) {
/* Selected objects are already first, no need to presort. */
}
else {
sort_trans_data_selected_first(t);
}
- if (ELEM(convert_type, TC_ACTION_DATA, TC_GRAPH_EDIT_DATA)) {
+ if (ELEM(t->data_type, &TransConvertType_Action, &TransConvertType_Graph)) {
/* Distance has already been set. */
}
- else if (ELEM(convert_type, TC_MESH_VERTS, TC_MESH_SKIN)) {
+ else if (ELEM(t->data_type,
+ &TransConvertType_Mesh,
+ &TransConvertType_MeshSkin,
+ &TransConvertType_MeshVertCData)) {
if (t->flag & T_PROP_CONNECTED) {
/* Already calculated by transform_convert_mesh_connectivity_distance. */
}
@@ -1073,10 +887,10 @@ static void init_proportional_edit(TransInfo *t)
set_prop_dist(t, false);
}
}
- else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) {
+ else if (t->data_type == &TransConvertType_MeshUV && t->flag & T_PROP_CONNECTED) {
/* Already calculated by uv_set_connectivity_distance. */
}
- else if (convert_type == TC_CURVE_VERTS) {
+ else if (t->data_type == &TransConvertType_Curve) {
BLI_assert(t->obedit_type == OB_CURVES_LEGACY);
set_prop_dist(t, false);
}
@@ -1099,44 +913,26 @@ static void init_TransDataContainers(TransInfo *t,
Object **objects,
uint objects_len)
{
- switch (t->data_type) {
- case TC_POSE:
- case TC_ARMATURE_VERTS:
- case TC_CURVE_VERTS:
- case TC_GPENCIL:
- case TC_LATTICE_VERTS:
- case TC_MBALL_VERTS:
- case TC_MESH_VERTS:
- case TC_MESH_EDGES:
- case TC_MESH_SKIN:
- case TC_MESH_UV:
- break;
- case TC_ACTION_DATA:
- case TC_GRAPH_EDIT_DATA:
- case TC_CURSOR_IMAGE:
- case TC_CURSOR_SEQUENCER:
- case TC_CURSOR_VIEW3D:
- case TC_MASKING_DATA:
- case TC_NLA_DATA:
- case TC_NODE_DATA:
- case TC_OBJECT:
- case TC_OBJECT_TEXSPACE:
- case TC_PAINT_CURVE_VERTS:
- case TC_PARTICLE_VERTS:
- case TC_SCULPT:
- case TC_SEQ_DATA:
- case TC_SEQ_IMAGE_DATA:
- case TC_TRACKING_DATA:
- case TC_NONE:
- default:
- /* Does not support Multi object editing. */
- return;
+ if (!ELEM(t->data_type,
+ &TransConvertType_Pose,
+ &TransConvertType_EditArmature,
+ &TransConvertType_Curve,
+ &TransConvertType_GPencil,
+ &TransConvertType_Lattice,
+ &TransConvertType_MBall,
+ &TransConvertType_Mesh,
+ &TransConvertType_MeshEdge,
+ &TransConvertType_MeshSkin,
+ &TransConvertType_MeshUV,
+ &TransConvertType_MeshVertCData)) {
+ /* Does not support Multi object editing. */
+ return;
}
const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT;
const short object_type = obact ? obact->type : -1;
- if ((object_mode & OB_MODE_EDIT) || (t->data_type == TC_GPENCIL) ||
+ if ((object_mode & OB_MODE_EDIT) || (t->data_type == &TransConvertType_GPencil) ||
((object_mode & OB_MODE_POSE) && (object_type == OB_ARMATURE))) {
if (t->data_container) {
MEM_freeN(t->data_container);
@@ -1144,15 +940,16 @@ static void init_TransDataContainers(TransInfo *t,
bool free_objects = false;
if (objects == NULL) {
- objects = BKE_view_layer_array_from_objects_in_mode(
+ struct ObjectsInModeParams params = {0};
+ params.object_mode = object_mode;
+ /* Pose transform operates on `ob->pose` so don't skip duplicate object-data. */
+ params.no_dup_data = (object_mode & OB_MODE_POSE) == 0;
+ objects = BKE_view_layer_array_from_objects_in_mode_params(
+ t->scene,
t->view_layer,
(t->spacetype == SPACE_VIEW3D) ? t->view : NULL,
&objects_len,
- {
- .object_mode = object_mode,
- /* Pose transform operates on `ob->pose` so don't skip duplicate object-data. */
- .no_dup_data = (object_mode & OB_MODE_POSE) == 0,
- });
+ &params);
free_objects = true;
}
@@ -1178,7 +975,7 @@ static void init_TransDataContainers(TransInfo *t,
tc->poseobj = objects[i];
tc->use_local_mat = true;
}
- else if (t->data_type == TC_GPENCIL) {
+ else if (t->data_type == &TransConvertType_GPencil) {
tc->use_local_mat = true;
}
@@ -1201,173 +998,132 @@ static void init_TransDataContainers(TransInfo *t,
}
}
-static eTFlag flags_from_data_type(eTConvertType data_type)
-{
- switch (data_type) {
- case TC_ACTION_DATA:
- case TC_GRAPH_EDIT_DATA:
- case TC_MASKING_DATA:
- case TC_NLA_DATA:
- case TC_NODE_DATA:
- case TC_PAINT_CURVE_VERTS:
- case TC_SEQ_DATA:
- case TC_SEQ_IMAGE_DATA:
- case TC_TRACKING_DATA:
- return T_POINTS | T_2D_EDIT;
- case TC_ARMATURE_VERTS:
- case TC_CURVE_VERTS:
- case TC_GPENCIL:
- case TC_LATTICE_VERTS:
- case TC_MBALL_VERTS:
- case TC_MESH_VERTS:
- case TC_MESH_SKIN:
- return T_EDIT | T_POINTS;
- case TC_MESH_EDGES:
- return T_EDIT;
- case TC_MESH_UV:
- return T_EDIT | T_POINTS | T_2D_EDIT;
- case TC_CURSOR_IMAGE:
- case TC_CURSOR_SEQUENCER:
- return T_2D_EDIT;
- case TC_PARTICLE_VERTS:
- return T_POINTS;
- case TC_POSE:
- case TC_CURSOR_VIEW3D:
- case TC_OBJECT:
- case TC_OBJECT_TEXSPACE:
- case TC_SCULPT:
- case TC_NONE:
- default:
- break;
- }
- return 0;
-}
-
-static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armature)
+static TransConvertTypeInfo *convert_type_get(const TransInfo *t, Object **r_obj_armature)
{
ViewLayer *view_layer = t->view_layer;
- Object *ob = OBACT(view_layer);
- eTConvertType convert_type = TC_NONE;
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
/* if tests must match recalcData for correct updates */
if (t->options & CTX_CURSOR) {
if (t->spacetype == SPACE_IMAGE) {
- convert_type = TC_CURSOR_IMAGE;
- }
- else if (t->spacetype == SPACE_SEQ) {
- convert_type = TC_CURSOR_SEQUENCER;
+ return &TransConvertType_CursorImage;
}
- else {
- convert_type = TC_CURSOR_VIEW3D;
+
+ if (t->spacetype == SPACE_SEQ) {
+ return &TransConvertType_CursorSequencer;
}
+
+ return &TransConvertType_Cursor3D;
}
- else if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && ob &&
- (ob->mode == OB_MODE_SCULPT) && ob->sculpt) {
- convert_type = TC_SCULPT;
+ if (!(t->options & CTX_PAINT_CURVE) && (t->spacetype == SPACE_VIEW3D) && ob &&
+ (ob->mode == OB_MODE_SCULPT) && ob->sculpt) {
+ return &TransConvertType_Sculpt;
}
- else if (t->options & CTX_TEXTURE_SPACE) {
- convert_type = TC_OBJECT_TEXSPACE;
+ if (t->options & CTX_TEXTURE_SPACE) {
+ return &TransConvertType_ObjectTexSpace;
}
- else if (t->options & CTX_EDGE_DATA) {
- convert_type = TC_MESH_EDGES;
+ if (t->options & CTX_EDGE_DATA) {
+ return &TransConvertType_MeshEdge;
}
- else if (t->options & CTX_GPENCIL_STROKES) {
- convert_type = TC_GPENCIL;
+ if (t->options & CTX_GPENCIL_STROKES) {
+ return &TransConvertType_GPencil;
}
- else if (t->spacetype == SPACE_IMAGE) {
+ if (t->spacetype == SPACE_IMAGE) {
if (t->options & CTX_MASK) {
- convert_type = TC_MASKING_DATA;
+ return &TransConvertType_Mask;
}
- else if (t->options & CTX_PAINT_CURVE) {
+ if (t->options & CTX_PAINT_CURVE) {
if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
- convert_type = TC_PAINT_CURVE_VERTS;
+ return &TransConvertType_PaintCurve;
}
}
else if (t->obedit_type == OB_MESH) {
- convert_type = TC_MESH_UV;
+ return &TransConvertType_MeshUV;
}
+ return NULL;
}
- else if (t->spacetype == SPACE_ACTION) {
- convert_type = TC_ACTION_DATA;
+ if (t->spacetype == SPACE_ACTION) {
+ return &TransConvertType_Action;
}
- else if (t->spacetype == SPACE_NLA) {
- convert_type = TC_NLA_DATA;
+ if (t->spacetype == SPACE_NLA) {
+ return &TransConvertType_NLA;
}
- else if (t->spacetype == SPACE_SEQ) {
+ if (t->spacetype == SPACE_SEQ) {
if (t->options & CTX_SEQUENCER_IMAGE) {
- convert_type = TC_SEQ_IMAGE_DATA;
- }
- else {
- convert_type = TC_SEQ_DATA;
+ return &TransConvertType_SequencerImage;
}
+ return &TransConvertType_Sequencer;
}
- else if (t->spacetype == SPACE_GRAPH) {
- convert_type = TC_GRAPH_EDIT_DATA;
+ if (t->spacetype == SPACE_GRAPH) {
+ return &TransConvertType_Graph;
}
- else if (t->spacetype == SPACE_NODE) {
- convert_type = TC_NODE_DATA;
+ if (t->spacetype == SPACE_NODE) {
+ return &TransConvertType_Node;
}
- else if (t->spacetype == SPACE_CLIP) {
+ if (t->spacetype == SPACE_CLIP) {
if (t->options & CTX_MOVIECLIP) {
- convert_type = TC_TRACKING_DATA;
+ return &TransConvertType_Tracking;
}
- else if (t->options & CTX_MASK) {
- convert_type = TC_MASKING_DATA;
+ if (t->options & CTX_MASK) {
+ return &TransConvertType_Mask;
}
+ return NULL;
}
- else if (t->obedit_type != -1) {
+ if (t->obedit_type != -1) {
if (t->obedit_type == OB_MESH) {
if (t->mode == TFM_SKIN_RESIZE) {
- convert_type = TC_MESH_SKIN;
+ return &TransConvertType_MeshSkin;
}
- else {
- convert_type = TC_MESH_VERTS;
+ if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) {
+ return &TransConvertType_MeshVertCData;
}
+ return &TransConvertType_Mesh;
}
- else if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) {
- convert_type = TC_CURVE_VERTS;
+ if (ELEM(t->obedit_type, OB_CURVES_LEGACY, OB_SURF)) {
+ return &TransConvertType_Curve;
}
- else if (t->obedit_type == OB_LATTICE) {
- convert_type = TC_LATTICE_VERTS;
+ if (t->obedit_type == OB_LATTICE) {
+ return &TransConvertType_Lattice;
}
- else if (t->obedit_type == OB_MBALL) {
- convert_type = TC_MBALL_VERTS;
+ if (t->obedit_type == OB_MBALL) {
+ return &TransConvertType_MBall;
}
- else if (t->obedit_type == OB_ARMATURE) {
- convert_type = TC_ARMATURE_VERTS;
+ if (t->obedit_type == OB_ARMATURE) {
+ return &TransConvertType_EditArmature;
}
+ return NULL;
}
- else if (ob && (ob->mode & OB_MODE_POSE)) {
- convert_type = TC_POSE;
+ if (ob && (ob->mode & OB_MODE_POSE)) {
+ return &TransConvertType_Pose;
}
- else if (ob && (ob->mode & OB_MODE_ALL_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) {
+ 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) {
*r_obj_armature = ob_armature;
- convert_type = TC_POSE;
+ return &TransConvertType_Pose;
}
+ return NULL;
}
- else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) &&
- PE_start_edit(PE_get_current(t->depsgraph, t->scene, ob))) {
- convert_type = TC_PARTICLE_VERTS;
+ if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) &&
+ PE_start_edit(PE_get_current(t->depsgraph, t->scene, ob))) {
+ return &TransConvertType_Particle;
}
- else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
+ if (ob && (ob->mode & OB_MODE_ALL_PAINT)) {
if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) {
- convert_type = TC_PAINT_CURVE_VERTS;
+ return &TransConvertType_PaintCurve;
}
+ return NULL;
}
- else if ((ob) && (ELEM(ob->mode,
- OB_MODE_PAINT_GPENCIL,
- OB_MODE_SCULPT_GPENCIL,
- OB_MODE_WEIGHT_GPENCIL,
- OB_MODE_VERTEX_GPENCIL))) {
+ if ((ob) && (ELEM(ob->mode,
+ OB_MODE_PAINT_GPENCIL,
+ OB_MODE_SCULPT_GPENCIL,
+ OB_MODE_WEIGHT_GPENCIL,
+ OB_MODE_VERTEX_GPENCIL))) {
/* In grease pencil all transformations must be canceled if not Object or Edit. */
+ return NULL;
}
- else {
- convert_type = TC_OBJECT;
- }
-
- return convert_type;
+ return &TransConvertType_Object;
}
void createTransData(bContext *C, TransInfo *t)
@@ -1376,133 +1132,64 @@ void createTransData(bContext *C, TransInfo *t)
Object *ob_armature = NULL;
t->data_type = convert_type_get(t, &ob_armature);
- t->flag |= flags_from_data_type(t->data_type);
+ if (t->data_type == NULL) {
+ printf("edit type not implemented!\n");
+ BLI_assert(t->data_len_all == -1);
+ t->data_len_all = 0;
+ return;
+ }
+
+ t->flag |= t->data_type->flags;
if (ob_armature) {
init_TransDataContainers(t, ob_armature, &ob_armature, 1);
}
else {
- ViewLayer *view_layer = t->view_layer;
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *ob = BKE_view_layer_active_object_get(t->view_layer);
init_TransDataContainers(t, ob, NULL, 0);
}
- switch (t->data_type) {
- case TC_ACTION_DATA:
- createTransActionData(C, t);
- break;
- case TC_POSE:
- t->options |= CTX_POSE_BONE;
+ if (t->data_type == &TransConvertType_Object) {
+ t->options |= CTX_OBJECT;
- /* XXX active-layer checking isn't done
- * as that should probably be checked through context instead. */
- createTransPose(t);
- break;
- case TC_ARMATURE_VERTS:
- createTransArmatureVerts(t);
- break;
- case TC_CURSOR_IMAGE:
- createTransCursor_image(t);
- break;
- case TC_CURSOR_SEQUENCER:
- createTransCursor_sequencer(t);
- break;
- case TC_CURSOR_VIEW3D:
- createTransCursor_view3d(t);
- break;
- case TC_CURVE_VERTS:
- createTransCurveVerts(t);
- break;
- case TC_GRAPH_EDIT_DATA:
- createTransGraphEditData(C, t);
- break;
- case TC_GPENCIL:
- createTransGPencil(C, t);
- break;
- case TC_LATTICE_VERTS:
- createTransLatticeVerts(t);
- break;
- case TC_MASKING_DATA:
- createTransMaskingData(C, t);
- break;
- case TC_MBALL_VERTS:
- createTransMBallVerts(t);
- break;
- case TC_MESH_VERTS:
- createTransEditVerts(t);
- break;
- case TC_MESH_EDGES:
- createTransEdge(t);
- break;
- case TC_MESH_SKIN:
- createTransMeshSkin(t);
- break;
- case TC_MESH_UV:
- createTransUVs(C, t);
- break;
- case TC_NLA_DATA:
- createTransNlaData(C, t);
- break;
- case TC_NODE_DATA:
- createTransNodeData(t);
- break;
- case TC_OBJECT:
- t->options |= CTX_OBJECT;
-
- /* Needed for correct Object.obmat after duplication, see: T62135. */
- BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context));
-
- if ((t->settings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) {
- t->options |= CTX_OBMODE_XFORM_OBDATA;
- }
- if ((t->settings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) {
- t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN;
- }
- createTransObject(C, t);
- /* Check if we're transforming the camera from the camera */
- if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
- View3D *v3d = t->view;
- RegionView3D *rv3d = t->region->regiondata;
- if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) {
- /* we could have a flag to easily check an object is being transformed */
- if (v3d->camera->id.tag & LIB_TAG_DOIT) {
- t->options |= CTX_CAMERA;
- }
- }
- else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) {
+ /* Needed for correct Object.obmat after duplication, see: T62135. */
+ BKE_scene_graph_evaluated_ensure(t->depsgraph, CTX_data_main(t->context));
+
+ if ((t->settings->transform_flag & SCE_XFORM_DATA_ORIGIN) != 0) {
+ t->options |= CTX_OBMODE_XFORM_OBDATA;
+ }
+ if ((t->settings->transform_flag & SCE_XFORM_SKIP_CHILDREN) != 0) {
+ t->options |= CTX_OBMODE_XFORM_SKIP_CHILDREN;
+ }
+ TransConvertType_Object.createTransData(C, t);
+ /* Check if we're transforming the camera from the camera */
+ if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ View3D *v3d = t->view;
+ RegionView3D *rv3d = t->region->regiondata;
+ if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) {
+ /* we could have a flag to easily check an object is being transformed */
+ if (v3d->camera->id.tag & LIB_TAG_DOIT) {
t->options |= CTX_CAMERA;
}
}
- break;
- case TC_OBJECT_TEXSPACE:
- createTransTexspace(t);
- break;
- case TC_PAINT_CURVE_VERTS:
- createTransPaintCurveVerts(C, t);
- break;
- case TC_PARTICLE_VERTS:
- createTransParticleVerts(t);
- break;
- case TC_SCULPT:
- createTransSculpt(C, t);
- break;
- case TC_SEQ_DATA:
- t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */
- createTransSeqData(t);
- break;
- case TC_SEQ_IMAGE_DATA:
+ else if (v3d->ob_center && v3d->ob_center->id.tag & LIB_TAG_DOIT) {
+ t->options |= CTX_CAMERA;
+ }
+ }
+ }
+ else {
+ if (t->data_type == &TransConvertType_Pose) {
+ t->options |= CTX_POSE_BONE;
+ }
+ else if (t->data_type == &TransConvertType_Sequencer) {
+ /* Sequencer has no use for floating point transform. */
+ t->num.flag |= NUM_NO_FRACTION;
+ }
+ else if (t->data_type == &TransConvertType_SequencerImage) {
t->obedit_type = -1;
- createTransSeqImageData(t);
- break;
- case TC_TRACKING_DATA:
- createTransTrackingData(C, t);
- break;
- case TC_NONE:
- default:
- printf("edit type not implemented!\n");
- BLI_assert(t->data_len_all == -1);
- t->data_len_all = 0;
- return;
+ }
+ t->data_type->createTransData(C, t);
}
countAndCleanTransDataContainer(t);
@@ -1706,89 +1393,10 @@ void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, const fl
void recalcData(TransInfo *t)
{
- switch (t->data_type) {
- case TC_ACTION_DATA:
- recalcData_actedit(t);
- break;
- case TC_POSE:
- recalcData_pose(t);
- break;
- case TC_ARMATURE_VERTS:
- recalcData_edit_armature(t);
- break;
- case TC_CURVE_VERTS:
- recalcData_curve(t);
- break;
- case TC_CURSOR_IMAGE:
- recalcData_cursor_image(t);
- break;
- case TC_CURSOR_SEQUENCER:
- recalcData_cursor_sequencer(t);
- break;
- case TC_CURSOR_VIEW3D:
- recalcData_cursor_view3d(t);
- break;
- case TC_GRAPH_EDIT_DATA:
- recalcData_graphedit(t);
- break;
- case TC_GPENCIL:
- recalcData_gpencil_strokes(t);
- break;
- case TC_MASKING_DATA:
- recalcData_mask_common(t);
- break;
- case TC_MESH_VERTS:
- recalcData_mesh(t);
- break;
- case TC_MESH_EDGES:
- recalcData_mesh_edge(t);
- break;
- case TC_MESH_SKIN:
- recalcData_mesh_skin(t);
- break;
- case TC_MESH_UV:
- recalcData_uv(t);
- break;
- case TC_NLA_DATA:
- recalcData_nla(t);
- break;
- case TC_NODE_DATA:
- flushTransNodes(t);
- break;
- case TC_OBJECT:
- recalcData_objects(t);
- break;
- case TC_OBJECT_TEXSPACE:
- recalcData_texspace(t);
- break;
- case TC_PAINT_CURVE_VERTS:
- flushTransPaintCurve(t);
- break;
- case TC_SCULPT:
- recalcData_sculpt(t);
- break;
- case TC_SEQ_DATA:
- recalcData_sequencer(t);
- break;
- case TC_SEQ_IMAGE_DATA:
- recalcData_sequencer_image(t);
- break;
- case TC_TRACKING_DATA:
- recalcData_tracking(t);
- break;
- case TC_MBALL_VERTS:
- recalcData_mball(t);
- break;
- case TC_LATTICE_VERTS:
- recalcData_lattice(t);
- break;
- case TC_PARTICLE_VERTS:
- recalcData_particles(t);
- break;
- case TC_NONE:
- default:
- break;
+ if (!t->data_type || !t->data_type->recalcData) {
+ return;
}
+ t->data_type->recalcData(t);
}
/** \} */
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 037fbe26c77..f32bff6dcff 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -21,6 +21,25 @@ struct TransDataCurveHandleFlags;
struct TransInfo;
struct bContext;
+typedef struct TransConvertTypeInfo {
+ int flags; /* eTFlag */
+
+ /**
+ * Allocate and initialize `t->data`.
+ */
+ void (*createTransData)(bContext *C, TransInfo *t);
+
+ /**
+ * Force recalculation of data during transformation.
+ */
+ void (*recalcData)(TransInfo *t);
+
+ /**
+ * Called when the operation is finished.
+ */
+ void (*special_aftertrans_update)(bContext *C, TransInfo *t);
+} TransConvertTypeInfo;
+
/* transform_convert.c */
/**
@@ -34,7 +53,6 @@ int special_transform_moving(TransInfo *t);
void special_aftertrans_update(struct bContext *C, TransInfo *t);
void sort_trans_data_dist(TransInfo *t);
void createTransData(struct bContext *C, TransInfo *t);
-bool clipUVTransform(TransInfo *t, float vec[2], bool resize);
void clipUVData(TransInfo *t);
void transform_convert_flush_handle2D(TransData *td, TransData2D *td2d, float y_fac);
/**
@@ -99,82 +117,53 @@ void animrecord_check_state(TransInfo *t, struct ID *id);
/* transform_convert_action.c */
-void createTransActionData(bContext *C, TransInfo *t);
-/* helper for recalcData() - for Action Editor transforms */
-void recalcData_actedit(TransInfo *t);
-void special_aftertrans_update__actedit(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Action;
/* transform_convert_armature.c */
+extern TransConvertTypeInfo TransConvertType_EditArmature;
+extern TransConvertTypeInfo TransConvertType_Pose;
+
/**
* Sets transform flags in the bones.
* Returns total number of bones with #BONE_TRANSFORM.
*/
void transform_convert_pose_transflags_update(Object *ob, int mode, short around);
-/**
- * When objects array is NULL, use 't->data_container' as is.
- */
-void createTransPose(TransInfo *t);
-void createTransArmatureVerts(TransInfo *t);
-void recalcData_edit_armature(TransInfo *t);
-void recalcData_pose(TransInfo *t);
-void special_aftertrans_update__pose(bContext *C, TransInfo *t);
-
/* transform_convert_cursor.c */
-void createTransCursor_image(TransInfo *t);
-void createTransCursor_sequencer(TransInfo *t);
-void createTransCursor_view3d(TransInfo *t);
-void recalcData_cursor_image(TransInfo *t);
-void recalcData_cursor_sequencer(TransInfo *t);
-void recalcData_cursor_view3d(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_CursorImage;
+extern TransConvertTypeInfo TransConvertType_CursorSequencer;
+extern TransConvertTypeInfo TransConvertType_Cursor3D;
/* transform_convert_curve.c */
-void createTransCurveVerts(TransInfo *t);
-void recalcData_curve(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Curve;
/* transform_convert_graph.c */
-/**
- * It is important to note that this doesn't always act on the selection (like it's usually done),
- * it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a
- * left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the
- * selected left or right handles accordingly.
- * The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve
- * functions may need to be made aware of this. It's ugly that these act based on selection state
- * anyway.
- */
-void createTransGraphEditData(bContext *C, TransInfo *t);
-/* helper for recalcData() - for Graph Editor transforms */
-void recalcData_graphedit(TransInfo *t);
-void special_aftertrans_update__graph(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Graph;
/* transform_convert_gpencil.c */
-void createTransGPencil(bContext *C, TransInfo *t);
-/* force recalculation of triangles during transformation */
-void recalcData_gpencil_strokes(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_GPencil;
/* transform_convert_lattice.c */
-void createTransLatticeVerts(TransInfo *t);
-void recalcData_lattice(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Lattice;
/* transform_convert_mask.c */
-void createTransMaskingData(bContext *C, TransInfo *t);
-void recalcData_mask_common(TransInfo *t);
-void special_aftertrans_update__mask(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Mask;
/* transform_convert_mball.c */
-void createTransMBallVerts(TransInfo *t);
-void recalcData_mball(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_MBall;
/* transform_convert_mesh.c */
+extern TransConvertTypeInfo TransConvertType_Mesh;
+
struct TransIslandData {
float (*center)[3];
float (*axismtx)[3][3];
@@ -233,84 +222,60 @@ void transform_convert_mesh_crazyspace_transdata_set(const float mtx[3][3],
struct TransData *r_td);
void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_crazyspace_data);
-void createTransEditVerts(TransInfo *t);
-void recalcData_mesh(TransInfo *t);
void special_aftertrans_update__mesh(bContext *C, TransInfo *t);
/* transform_convert_mesh_edge.c */
-void createTransEdge(TransInfo *t);
-void recalcData_mesh_edge(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_MeshEdge;
/* transform_convert_mesh_skin.c */
-void createTransMeshSkin(TransInfo *t);
-void recalcData_mesh_skin(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_MeshSkin;
/* transform_convert_mesh_uv.c */
-void createTransUVs(bContext *C, TransInfo *t);
-/* helper for recalcData() - for Image Editor transforms */
-void recalcData_uv(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_MeshUV;
+
+/* transform_convert_mesh_vert_cdata.c */
+
+extern TransConvertTypeInfo TransConvertType_MeshVertCData;
/* transform_convert_nla.c */
-void createTransNlaData(bContext *C, TransInfo *t);
-/* helper for recalcData() - for NLA Editor transforms */
-void recalcData_nla(TransInfo *t);
-void special_aftertrans_update__nla(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_NLA;
/* transform_convert_node.c */
-void createTransNodeData(TransInfo *t);
-void flushTransNodes(TransInfo *t);
-void special_aftertrans_update__node(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Node;
/* transform_convert_object.c */
-void createTransObject(bContext *C, TransInfo *t);
-/* helper for recalcData() - for object transforms, typically in the 3D view */
-void recalcData_objects(TransInfo *t);
-void special_aftertrans_update__object(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Object;
/* transform_convert_object_texspace.c */
-void createTransTexspace(TransInfo *t);
-/* helper for recalcData() - for object transforms, typically in the 3D view */
-void recalcData_texspace(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_ObjectTexSpace;
/* transform_convert_paintcurve.c */
-void createTransPaintCurveVerts(bContext *C, TransInfo *t);
-void flushTransPaintCurve(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_PaintCurve;
/* transform_convert_particle.c */
-void createTransParticleVerts(TransInfo *t);
-void recalcData_particles(TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Particle;
/* transform_convert_sculpt.c */
-void createTransSculpt(bContext *C, TransInfo *t);
-void recalcData_sculpt(TransInfo *t);
-void special_aftertrans_update__sculpt(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Sculpt;
/* transform_convert_sequencer.c */
-void createTransSeqData(TransInfo *t);
-/* helper for recalcData() - for sequencer transforms */
-void recalcData_sequencer(TransInfo *t);
-void special_aftertrans_update__sequencer(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Sequencer;
/* transform_convert_sequencer_image.c */
-void createTransSeqImageData(TransInfo *t);
-void recalcData_sequencer_image(TransInfo *t);
-void special_aftertrans_update__sequencer_image(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_SequencerImage;
/* transform_convert_tracking.c */
-void createTransTrackingData(bContext *C, TransInfo *t);
-/* helper for recalcData() - for Movie Clip transforms */
-void recalcData_tracking(TransInfo *t);
-void special_aftertrans_update__movieclip(bContext *C, TransInfo *t);
+extern TransConvertTypeInfo TransConvertType_Tracking;
diff --git a/source/blender/editors/transform/transform_convert_action.c b/source/blender/editors/transform/transform_convert_action.c
index 71a78321e12..8c6f2baf84a 100644
--- a/source/blender/editors/transform/transform_convert_action.c
+++ b/source/blender/editors/transform/transform_convert_action.c
@@ -18,6 +18,7 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_key.h"
+#include "BKE_layer.h"
#include "BKE_mask.h"
#include "BKE_nla.h"
@@ -289,7 +290,7 @@ static int MaskLayerToTransData(TransData *td,
return count;
}
-void createTransActionData(bContext *C, TransInfo *t)
+static void createTransActionData(bContext *C, TransInfo *t)
{
Scene *scene = t->scene;
TransData *td = NULL;
@@ -565,7 +566,7 @@ static void flushTransIntFrameActionData(TransInfo *t)
}
}
-void recalcData_actedit(TransInfo *t)
+static void recalcData_actedit(TransInfo *t)
{
ViewLayer *view_layer = t->view_layer;
SpaceAction *saction = (SpaceAction *)t->area->spacedata.first;
@@ -575,12 +576,14 @@ void recalcData_actedit(TransInfo *t)
bAnimListElem *ale;
int filter;
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+
/* initialize relevant anim-context 'context' data from TransInfo data */
/* NOTE: sync this with the code in ANIM_animdata_get_context() */
ac.bmain = CTX_data_main(t->context);
ac.scene = t->scene;
ac.view_layer = t->view_layer;
- ac.obact = OBACT(view_layer);
+ ac.obact = BKE_view_layer_active_object_get(view_layer);
ac.area = t->area;
ac.region = t->region;
ac.sl = (t->area) ? t->area->spacedata.first : NULL;
@@ -759,7 +762,7 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act)
ANIM_animdata_freelist(&anim_data);
}
-void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
+static void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
{
SpaceAction *saction = (SpaceAction *)t->area->spacedata.first;
bAnimContext ac;
@@ -901,18 +904,18 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
if (ELEM(t->frame_side, 'L', 'R')) { /* TFM_TIME_EXTEND */
/* same as below */
ED_markers_post_apply_transform(
- ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side);
+ ED_context_get_markers(C), t->scene, t->mode, t->values_final[0], t->frame_side);
}
else /* TFM_TIME_TRANSLATE */
#endif
{
ED_markers_post_apply_transform(
- ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side);
+ ED_context_get_markers(C), t->scene, t->mode, t->values_final[0], t->frame_side);
}
}
else if (t->mode == TFM_TIME_SCALE) {
ED_markers_post_apply_transform(
- ED_context_get_markers(C), t->scene, t->mode, t->values[0], t->frame_side);
+ ED_context_get_markers(C), t->scene, t->mode, t->values_final[0], t->frame_side);
}
}
@@ -926,3 +929,10 @@ void special_aftertrans_update__actedit(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Action = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransActionData,
+ /* recalcData */ recalcData_actedit,
+ /* special_aftertrans_update */ special_aftertrans_update__actedit,
+};
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 1613218ca29..d83cca15219 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -701,7 +701,7 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr
td->con = pchan->constraints.first;
}
-void createTransPose(TransInfo *t)
+static void createTransPose(bContext *UNUSED(C), TransInfo *t)
{
Main *bmain = CTX_data_main(t->context);
@@ -879,7 +879,7 @@ void createTransPose(TransInfo *t)
}
}
-void createTransArmatureVerts(TransInfo *t)
+static void createTransArmatureVerts(bContext *UNUSED(C), TransInfo *t)
{
t->data_len_all = 0;
@@ -1189,7 +1189,7 @@ static void restoreBones(TransDataContainer *tc)
}
}
-void recalcData_edit_armature(TransInfo *t)
+static void recalcData_edit_armature(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
applySnappingIndividual(t);
@@ -1356,7 +1356,7 @@ static void pose_transform_mirror_update(TransInfo *t, TransDataContainer *tc, O
}
mul_v3_m4v3(data->grabtarget, flip_mtx, td->loc);
if (pid) {
- /* TODO(germano): Relative Mirror support. */
+ /* TODO(@germano): Relative Mirror support. */
}
data->flag |= CONSTRAINT_IK_AUTO;
/* Add a temporary auto IK constraint here, as we will only temporarily active this
@@ -1412,7 +1412,7 @@ static void restoreMirrorPoseBones(TransDataContainer *tc)
}
}
-void recalcData_pose(TransInfo *t)
+static void recalcData_pose(TransInfo *t)
{
if (t->mode == TFM_BONESIZE) {
/* Handle the exception where for TFM_BONESIZE in edit mode we pretend to be
@@ -1684,7 +1684,7 @@ static void pose_grab_with_ik_clear(Main *bmain, Object *ob)
}
}
-void special_aftertrans_update__pose(bContext *C, TransInfo *t)
+static void special_aftertrans_update__pose(bContext *C, TransInfo *t)
{
Object *ob;
@@ -1768,3 +1768,17 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_EditArmature = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransArmatureVerts,
+ /* recalcData */ recalcData_edit_armature,
+ /* special_aftertrans_update */ NULL,
+};
+
+TransConvertTypeInfo TransConvertType_Pose = {
+ /* flags */ 0,
+ /* createTransData */ createTransPose,
+ /* recalcData */ recalcData_pose,
+ /* special_aftertrans_update */ special_aftertrans_update__pose,
+};
diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c
index 5e6eee192f2..0bf6f06a9f2 100644
--- a/source/blender/editors/transform/transform_convert_cursor.c
+++ b/source/blender/editors/transform/transform_convert_cursor.c
@@ -82,13 +82,13 @@ static void recalcData_cursor_2D_impl(TransInfo *t)
/** \name Image Cursor
* \{ */
-void createTransCursor_image(TransInfo *t)
+static void createTransCursor_image(bContext *UNUSED(C), TransInfo *t)
{
SpaceImage *sima = t->area->spacedata.first;
createTransCursor_2D_impl(t, sima->cursor);
}
-void recalcData_cursor_image(TransInfo *t)
+static void recalcData_cursor_image(TransInfo *t)
{
recalcData_cursor_2D_impl(t);
}
@@ -99,7 +99,7 @@ void recalcData_cursor_image(TransInfo *t)
/** \name Sequencer Cursor
* \{ */
-void createTransCursor_sequencer(TransInfo *t)
+static void createTransCursor_sequencer(bContext *UNUSED(C), TransInfo *t)
{
SpaceSeq *sseq = t->area->spacedata.first;
if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) {
@@ -108,7 +108,7 @@ void createTransCursor_sequencer(TransInfo *t)
createTransCursor_2D_impl(t, sseq->cursor);
}
-void recalcData_cursor_sequencer(TransInfo *t)
+static void recalcData_cursor_sequencer(TransInfo *t)
{
recalcData_cursor_2D_impl(t);
}
@@ -119,7 +119,7 @@ void recalcData_cursor_sequencer(TransInfo *t)
/** \name View 3D Cursor
* \{ */
-void createTransCursor_view3d(TransInfo *t)
+static void createTransCursor_view3d(bContext *UNUSED(C), TransInfo *t)
{
TransData *td;
@@ -178,9 +178,30 @@ void createTransCursor_view3d(TransInfo *t)
td->ext->rotOrder = cursor->rotation_mode;
}
-void recalcData_cursor_view3d(TransInfo *t)
+static void recalcData_cursor_view3d(TransInfo *t)
{
DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE);
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_CursorImage = {
+ /* flags */ T_2D_EDIT,
+ /* createTransData */ createTransCursor_image,
+ /* recalcData */ recalcData_cursor_image,
+ /* special_aftertrans_update */ NULL,
+};
+
+TransConvertTypeInfo TransConvertType_CursorSequencer = {
+ /* flags */ T_2D_EDIT,
+ /* createTransData */ createTransCursor_sequencer,
+ /* recalcData */ recalcData_cursor_sequencer,
+ /* special_aftertrans_update */ NULL,
+};
+
+TransConvertTypeInfo TransConvertType_Cursor3D = {
+ /* flags */ 0,
+ /* createTransData */ createTransCursor_view3d,
+ /* recalcData */ recalcData_cursor_view3d,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c
index b9581aa1e31..404b1293208 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -62,7 +62,7 @@ static int bezt_select_to_transform_triple_flag(const BezTriple *bezt, const boo
return flag;
}
-void createTransCurveVerts(TransInfo *t)
+static void createTransCurveVerts(bContext *UNUSED(C), TransInfo *t)
{
#define SEL_F1 (1 << 0)
@@ -415,7 +415,7 @@ void createTransCurveVerts(TransInfo *t)
#undef SEL_F3
}
-void recalcData_curve(TransInfo *t)
+static void recalcData_curve(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
applySnappingIndividual(t);
@@ -446,3 +446,10 @@ void recalcData_curve(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Curve = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransCurveVerts,
+ /* recalcData */ recalcData_curve,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index 4bd02b0a45b..b3d58f25ad3 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -19,6 +19,7 @@
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
+#include "BKE_layer.h"
#include "ED_gpencil.h"
#include "ED_keyframing.h"
@@ -672,7 +673,7 @@ static void createTransGPencil_strokes(bContext *C,
}
}
-void createTransGPencil(bContext *C, TransInfo *t)
+static void createTransGPencil(bContext *C, TransInfo *t)
{
if (t->data_container_len == 0) {
return;
@@ -681,7 +682,8 @@ void createTransGPencil(bContext *C, TransInfo *t)
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const Scene *scene = CTX_data_scene(C);
ToolSettings *ts = scene->toolsettings;
- Object *obact = OBACT(t->view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *obact = BKE_view_layer_active_object_get(t->view_layer);
bGPdata *gpd = obact->data;
BLI_assert(gpd != NULL);
@@ -737,7 +739,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
}
}
-void recalcData_gpencil_strokes(TransInfo *t)
+static void recalcData_gpencil_strokes(TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
GHash *strokes = BLI_ghash_ptr_new(__func__);
@@ -762,3 +764,10 @@ void recalcData_gpencil_strokes(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_GPencil = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransGPencil,
+ /* recalcData */ recalcData_gpencil_strokes,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_graph.c b/source/blender/editors/transform/transform_convert_graph.c
index d93fff72de6..27e6c8a25e1 100644
--- a/source/blender/editors/transform/transform_convert_graph.c
+++ b/source/blender/editors/transform/transform_convert_graph.c
@@ -14,6 +14,7 @@
#include "BKE_context.h"
#include "BKE_fcurve.h"
+#include "BKE_layer.h"
#include "BKE_nla.h"
#include "BKE_report.h"
@@ -199,7 +200,16 @@ static void graph_key_shortest_dist(
}
}
-void createTransGraphEditData(bContext *C, TransInfo *t)
+/**
+ * It is important to note that this doesn't always act on the selection (like it's usually done),
+ * it acts on a subset of it. E.g. the selection code may leave a hint that we just dragged on a
+ * left or right handle (SIPO_RUNTIME_FLAG_TWEAK_HANDLES_LEFT/RIGHT) and then we only transform the
+ * selected left or right handles accordingly.
+ * The points to be transformed are tagged with BEZT_FLAG_TEMP_TAG; some lower level curve
+ * functions may need to be made aware of this. It's ugly that these act based on selection state
+ * anyway.
+ */
+static void createTransGraphEditData(bContext *C, TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
Scene *scene = t->scene;
@@ -629,7 +639,7 @@ static bool fcu_test_selected(FCurve *fcu)
return 0;
}
-/* this function is called on recalcData to apply the transforms applied
+/* This function is called on recalcData to apply the transforms applied
* to the transdata on to the actual keyframe data
*/
static void flushTransGraphData(TransInfo *t)
@@ -886,7 +896,7 @@ static void remake_graph_transdata(TransInfo *t, ListBase *anim_data)
}
}
-void recalcData_graphedit(TransInfo *t)
+static void recalcData_graphedit(TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
ViewLayer *view_layer = t->view_layer;
@@ -898,12 +908,14 @@ void recalcData_graphedit(TransInfo *t)
bAnimListElem *ale;
int dosort = 0;
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+
/* initialize relevant anim-context 'context' data from TransInfo data */
/* NOTE: sync this with the code in ANIM_animdata_get_context() */
ac.bmain = CTX_data_main(t->context);
ac.scene = t->scene;
ac.view_layer = t->view_layer;
- ac.obact = OBACT(view_layer);
+ ac.obact = BKE_view_layer_active_object_get(view_layer);
ac.area = t->area;
ac.region = t->region;
ac.sl = (t->area) ? t->area->spacedata.first : NULL;
@@ -934,7 +946,7 @@ void recalcData_graphedit(TransInfo *t)
dosort++;
}
else {
- calchandles_fcurve_ex(fcu, BEZT_FLAG_TEMP_TAG);
+ BKE_fcurve_handles_recalc_ex(fcu, BEZT_FLAG_TEMP_TAG);
}
/* set refresh tags for objects using this animation,
@@ -960,7 +972,7 @@ void recalcData_graphedit(TransInfo *t)
/** \name Special After Transform Graph
* \{ */
-void special_aftertrans_update__graph(bContext *C, TransInfo *t)
+static void special_aftertrans_update__graph(bContext *C, TransInfo *t)
{
SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first;
bAnimContext ac;
@@ -1021,3 +1033,10 @@ void special_aftertrans_update__graph(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Graph = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransGraphEditData,
+ /* recalcData */ recalcData_graphedit,
+ /* special_aftertrans_update */ special_aftertrans_update__graph,
+};
diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c
index d3c34697237..b77538dc249 100644
--- a/source/blender/editors/transform/transform_convert_lattice.c
+++ b/source/blender/editors/transform/transform_convert_lattice.c
@@ -25,7 +25,7 @@
/** \name Curve/Surfaces Transform Creation
* \{ */
-void createTransLatticeVerts(TransInfo *t)
+static void createTransLatticeVerts(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -98,7 +98,7 @@ void createTransLatticeVerts(TransInfo *t)
}
}
-void recalcData_lattice(TransInfo *t)
+static void recalcData_lattice(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
applySnappingIndividual(t);
@@ -114,3 +114,10 @@ void recalcData_lattice(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Lattice = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransLatticeVerts,
+ /* recalcData */ recalcData_lattice,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c
index f035cfc7aa9..2dca59a5da1 100644
--- a/source/blender/editors/transform/transform_convert_mask.c
+++ b/source/blender/editors/transform/transform_convert_mask.c
@@ -245,7 +245,7 @@ static void MaskPointToTransData(Scene *scene,
}
}
-void createTransMaskingData(bContext *C, TransInfo *t)
+static void createTransMaskingData(bContext *C, TransInfo *t)
{
Scene *scene = CTX_data_scene(C);
Mask *mask = CTX_data_edit_mask(C);
@@ -416,7 +416,7 @@ static void flushTransMasking(TransInfo *t)
}
}
-void recalcData_mask_common(TransInfo *t)
+static void recalcData_mask_common(TransInfo *t)
{
Mask *mask = CTX_data_edit_mask(t->context);
@@ -431,7 +431,7 @@ void recalcData_mask_common(TransInfo *t)
/** \name Special After Transform Mask
* \{ */
-void special_aftertrans_update__mask(bContext *C, TransInfo *t)
+static void special_aftertrans_update__mask(bContext *C, TransInfo *t)
{
Mask *mask = NULL;
@@ -463,3 +463,10 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Mask = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransMaskingData,
+ /* recalcData */ recalcData_mask_common,
+ /* special_aftertrans_update */ special_aftertrans_update__mask,
+};
diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c
index 5b2a1f8336d..7ae93524d0b 100644
--- a/source/blender/editors/transform/transform_convert_mball.c
+++ b/source/blender/editors/transform/transform_convert_mball.c
@@ -22,7 +22,7 @@
/** \name Meta Elements Transform Creation
* \{ */
-void createTransMBallVerts(TransInfo *t)
+static void createTransMBallVerts(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
MetaBall *mb = (MetaBall *)tc->obedit->data;
@@ -119,7 +119,7 @@ void createTransMBallVerts(TransInfo *t)
/** \name Recalc Meta Ball
* \{ */
-void recalcData_mball(TransInfo *t)
+static void recalcData_mball(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
applySnappingIndividual(t);
@@ -132,3 +132,10 @@ void recalcData_mball(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_MBall = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransMBallVerts,
+ /* recalcData */ recalcData_mball,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index c8d943df3fa..f67a44703e5 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -1314,7 +1314,8 @@ void transform_convert_mesh_crazyspace_detect(TransInfo *t,
* correction with \a quats, relative to the coordinates after
* the modifiers that support deform matrices \a defcos. */
-#if 0 /* TODO(campbell): fix crazy-space & extrude so it can be enabled for general use. */
+#if 0 /* TODO(@campbellbarton): fix crazy-space & extrude so it can be enabled for general use. \
+ */
if ((totleft > 0) || (totleft == -1))
#else
if (totleft > 0)
@@ -1408,7 +1409,6 @@ static void VertsToTransData(TransInfo *t,
TransDataExtension *tx,
BMEditMesh *em,
BMVert *eve,
- float *bweight,
const struct TransIslandData *island_data,
const int island_index)
{
@@ -1449,17 +1449,13 @@ static void VertsToTransData(TransInfo *t,
td->ext = NULL;
td->val = NULL;
td->extra = eve;
- if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) {
- td->val = bweight;
- td->ival = *bweight;
- }
- else if (t->mode == TFM_SHRINKFATTEN) {
+ if (t->mode == TFM_SHRINKFATTEN) {
td->ext = tx;
tx->isize[0] = BM_vert_calc_shell_factor_ex(eve, no, BM_ELEM_SELECT);
}
}
-void createTransEditVerts(TransInfo *t)
+static void createTransEditVerts(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransDataExtension *tx = NULL;
@@ -1589,17 +1585,6 @@ void createTransEditVerts(TransInfo *t)
"TransObData ext");
}
- int cd_vert_bweight_offset = -1;
- int cd_vert_crease_offset = -1;
- if (t->mode == TFM_BWEIGHT) {
- BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT);
- cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
- }
- else if (t->mode == TFM_VERT_CREASE) {
- BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_CREASE);
- cd_vert_crease_offset = CustomData_get_offset(&bm->vdata, CD_CREASE);
- }
-
TransData *tob = tc->data;
TransDataMirror *td_mirror = tc->data_mirror;
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
@@ -1632,15 +1617,9 @@ void createTransEditVerts(TransInfo *t)
td_mirror++;
}
else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
- float *bweight = (cd_vert_bweight_offset != -1) ?
- BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) :
- (cd_vert_crease_offset != -1) ?
- BM_ELEM_CD_GET_VOID_P(eve, cd_vert_crease_offset) :
- NULL;
-
/* Do not use the island center in case we are using islands
* only to get axis for snap/rotate to normal... */
- VertsToTransData(t, tob, tx, em, eve, bweight, &island_data, island_index);
+ VertsToTransData(t, tob, tx, em, eve, &island_data, island_index);
if (tx) {
tx++;
}
@@ -2051,7 +2030,7 @@ static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc)
}
}
-void recalcData_mesh(TransInfo *t)
+static void recalcData_mesh(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
/* Apply corrections. */
@@ -2145,9 +2124,16 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
/* table needs to be created for each edit command, since vertices can move etc */
ED_mesh_mirror_spatial_table_end(tc->obedit);
- /* TODO(campbell): xform: We need support for many mirror objects at once! */
+ /* TODO(@campbellbarton): xform: We need support for many mirror objects at once! */
break;
}
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Mesh = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransEditVerts,
+ /* recalcData */ recalcData_mesh,
+ /* special_aftertrans_update */ special_aftertrans_update__mesh,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh_edge.c b/source/blender/editors/transform/transform_convert_mesh_edge.c
index 2d6c6a933d6..b1627e62f8c 100644
--- a/source/blender/editors/transform/transform_convert_mesh_edge.c
+++ b/source/blender/editors/transform/transform_convert_mesh_edge.c
@@ -23,7 +23,7 @@
/** \name Edge (for crease) Transform Creation
* \{ */
-void createTransEdge(TransInfo *t)
+static void createTransEdge(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -67,7 +67,9 @@ void createTransEdge(TransInfo *t)
/* create data we need */
if (t->mode == TFM_BWEIGHT) {
- BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_EDGE_BWEIGHT);
+ if (!CustomData_has_layer(&em->bm->edata, CD_BWEIGHT)) {
+ BM_data_layer_add(em->bm, &em->bm->edata, CD_BWEIGHT);
+ }
cd_edge_float_offset = CustomData_get_offset(&em->bm->edata, CD_BWEIGHT);
}
else { /* if (t->mode == TFM_EDGE_CREASE) { */
@@ -99,8 +101,8 @@ void createTransEdge(TransInfo *t)
td->ext = NULL;
fl_ptr = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_float_offset);
- td->val = fl_ptr;
- td->ival = *fl_ptr;
+ td->loc = fl_ptr;
+ td->iloc[0] = *fl_ptr;
td++;
}
@@ -108,7 +110,7 @@ void createTransEdge(TransInfo *t)
}
}
-void recalcData_mesh_edge(TransInfo *t)
+static void recalcData_mesh_edge(TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
@@ -116,3 +118,10 @@ void recalcData_mesh_edge(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_MeshEdge = {
+ /* flags */ T_EDIT,
+ /* createTransData */ createTransEdge,
+ /* recalcData */ recalcData_mesh_edge,
+ /* special_aftertrans_update */ special_aftertrans_update__mesh,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c
index fdc2ba59035..376e559181e 100644
--- a/source/blender/editors/transform/transform_convert_mesh_skin.c
+++ b/source/blender/editors/transform/transform_convert_mesh_skin.c
@@ -66,7 +66,7 @@ static void tc_mesh_skin_transdata_create(TransDataBasic *td,
td->extra = eve;
}
-void createTransMeshSkin(TransInfo *t)
+static void createTransMeshSkin(bContext *UNUSED(C), TransInfo *t)
{
BLI_assert(t->mode == TFM_SKIN_RESIZE);
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
@@ -271,7 +271,7 @@ static void tc_mesh_skin_apply_to_mirror(TransInfo *t)
}
}
-void recalcData_mesh_skin(TransInfo *t)
+static void recalcData_mesh_skin(TransInfo *t)
{
bool is_canceling = t->state == TRANS_CANCEL;
/* mirror modifier clipping? */
@@ -289,3 +289,10 @@ void recalcData_mesh_skin(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_MeshSkin = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransMeshSkin,
+ /* recalcData */ recalcData_mesh_skin,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c
index 18868643b6d..4f15fc240d3 100644
--- a/source/blender/editors/transform/transform_convert_mesh_uv.c
+++ b/source/blender/editors/transform/transform_convert_mesh_uv.c
@@ -74,8 +74,12 @@ static void UVsToTransData(const float aspect[2],
/**
* \param dists: Store the closest connected distance to selected vertices.
*/
-static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float aspect[2])
+static void uv_set_connectivity_distance(const ToolSettings *ts,
+ BMesh *bm,
+ float *dists,
+ const float aspect[2])
{
+#define TMP_LOOP_SELECT_TAG BM_ELEM_TAG_ALT
/* Mostly copied from #transform_convert_mesh_connectivity_distance. */
BLI_LINKSTACK_DECLARE(queue, BMLoop *);
@@ -101,15 +105,15 @@ static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float as
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
float dist;
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-
- bool uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
+ bool uv_vert_sel = uvedit_uv_select_test_ex(ts, l, cd_loop_uv_offset);
if (uv_vert_sel) {
BLI_LINKSTACK_PUSH(queue, l);
+ BM_elem_flag_enable(l, TMP_LOOP_SELECT_TAG);
dist = 0.0f;
}
else {
+ BM_elem_flag_disable(l, TMP_LOOP_SELECT_TAG);
dist = FLT_MAX;
}
@@ -164,7 +168,7 @@ static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float as
bool other_vert_sel, connected_vert_sel;
- other_vert_sel = luv_other->flag & MLOOPUV_VERTSEL;
+ other_vert_sel = BM_elem_flag_test_bool(l_other, TMP_LOOP_SELECT_TAG);
BM_ITER_ELEM (l_connected, &l_connected_iter, l_other->v, BM_LOOPS_OF_VERT) {
if (l_connected == l_other) {
@@ -176,7 +180,7 @@ static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float as
}
MLoopUV *luv_connected = BM_ELEM_CD_GET_VOID_P(l_connected, cd_loop_uv_offset);
- connected_vert_sel = luv_connected->flag & MLOOPUV_VERTSEL;
+ connected_vert_sel = BM_elem_flag_test_bool(l_connected, TMP_LOOP_SELECT_TAG);
/* Check if this loop is connected in UV space.
* If the uv loops share the same selection state (if not, they are not connected as
@@ -232,13 +236,13 @@ static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float as
BLI_LINKSTACK_FREE(queue_next);
MEM_freeN(dists_prev);
+#undef TMP_LOOP_SELECT_TAG
}
-void createTransUVs(bContext *C, TransInfo *t)
+static void createTransUVs(bContext *C, TransInfo *t)
{
SpaceImage *sima = CTX_wm_space_image(C);
Scene *scene = t->scene;
- ToolSettings *ts = CTX_data_tool_settings(C);
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
@@ -266,13 +270,12 @@ void createTransUVs(bContext *C, TransInfo *t)
/* count */
if (is_island_center) {
/* create element map with island information */
- const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0;
- elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true);
+ elementmap = BM_uv_element_map_create(em->bm, scene, true, false, true, true);
if (elementmap == NULL) {
continue;
}
- island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
+ island_center = MEM_callocN(sizeof(*island_center) * elementmap->total_islands, __func__);
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -317,9 +320,7 @@ void createTransUVs(bContext *C, TransInfo *t)
}
if (is_island_center) {
- int i;
-
- for (i = 0; i < elementmap->totalIslands; i++) {
+ for (int i = 0; i < elementmap->total_islands; i++) {
mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num);
mul_v2_v2(island_center[i].co, t->aspect);
}
@@ -341,7 +342,7 @@ void createTransUVs(bContext *C, TransInfo *t)
if (is_prop_connected) {
prop_dists = MEM_callocN(em->bm->totloop * sizeof(float), "TransObPropDists(UV Editing)");
- uv_set_connectivity_distance(em->bm, prop_dists, t->aspect);
+ uv_set_connectivity_distance(t->settings, em->bm, prop_dists, t->aspect);
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -403,8 +404,8 @@ void createTransUVs(bContext *C, TransInfo *t)
static void flushTransUVs(TransInfo *t)
{
SpaceImage *sima = t->area->spacedata.first;
- const bool use_pixel_snap = ((sima->pixel_snap_mode != SI_PIXEL_SNAP_DISABLED) &&
- (t->state != TRANS_CANCEL));
+ const bool use_pixel_round = ((sima->pixel_round_mode != SI_PIXEL_ROUND_DISABLED) &&
+ (t->state != TRANS_CANCEL));
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData2D *td;
@@ -414,7 +415,7 @@ static void flushTransUVs(TransInfo *t)
aspect_inv[0] = 1.0f / t->aspect[0];
aspect_inv[1] = 1.0f / t->aspect[1];
- if (use_pixel_snap) {
+ if (use_pixel_round) {
int size_i[2];
ED_space_image_get_size(sima, &size_i[0], &size_i[1]);
size[0] = size_i[0];
@@ -426,16 +427,16 @@ static void flushTransUVs(TransInfo *t)
td->loc2d[0] = td->loc[0] * aspect_inv[0];
td->loc2d[1] = td->loc[1] * aspect_inv[1];
- if (use_pixel_snap) {
+ if (use_pixel_round) {
td->loc2d[0] *= size[0];
td->loc2d[1] *= size[1];
- switch (sima->pixel_snap_mode) {
- case SI_PIXEL_SNAP_CENTER:
+ switch (sima->pixel_round_mode) {
+ case SI_PIXEL_ROUND_CENTER:
td->loc2d[0] = roundf(td->loc2d[0] - 0.5f) + 0.5f;
td->loc2d[1] = roundf(td->loc2d[1] - 0.5f) + 0.5f;
break;
- case SI_PIXEL_SNAP_CORNER:
+ case SI_PIXEL_ROUND_CORNER:
td->loc2d[0] = roundf(td->loc2d[0]);
td->loc2d[1] = roundf(td->loc2d[1]);
break;
@@ -448,7 +449,7 @@ static void flushTransUVs(TransInfo *t)
}
}
-void recalcData_uv(TransInfo *t)
+static void recalcData_uv(TransInfo *t)
{
SpaceImage *sima = t->area->spacedata.first;
@@ -465,3 +466,10 @@ void recalcData_uv(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_MeshUV = {
+ /* flags */ (T_EDIT | T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransUVs,
+ /* recalcData */ recalcData_uv,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c
new file mode 100644
index 00000000000..39705f87a0d
--- /dev/null
+++ b/source/blender/editors/transform/transform_convert_mesh_vert_cdata.c
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
+
+/** \file
+ * \ingroup edtransform
+ */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math.h"
+
+#include "BKE_context.h"
+#include "BKE_crazyspace.h"
+#include "BKE_editmesh.h"
+#include "BKE_modifier.h"
+#include "BKE_scene.h"
+
+#include "ED_mesh.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "transform.h"
+#include "transform_orientations.h"
+
+#include "transform_convert.h"
+
+/* -------------------------------------------------------------------- */
+/** \name Edit Mesh #CD_BWEIGHT and #CD_CREASE Transform Creation
+ * \{ */
+
+static float *tc_mesh_cdata_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];
+ }
+ return eve->co;
+}
+
+static void tc_mesh_cdata_transdata_create(TransDataBasic *td,
+ BMVert *eve,
+ float *weight,
+ const struct TransIslandData *island_data,
+ const int island_index)
+{
+ BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0);
+
+ td->loc = weight;
+ td->iloc[0] = *weight;
+
+ if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ td->flag |= TD_SELECTED;
+ }
+
+ copy_v3_v3(td->center, tc_mesh_cdata_transdata_center(island_data, island_index, eve));
+ td->extra = eve;
+}
+
+static void createTransMeshVertCData(bContext *UNUSED(C), TransInfo *t)
+{
+ BLI_assert(ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE));
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ Mesh *me = tc->obedit->data;
+ BMesh *bm = em->bm;
+ BMVert *eve;
+ BMIter iter;
+ float mtx[3][3], smtx[3][3];
+ int a;
+ const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0;
+
+ struct TransIslandData island_data = {NULL};
+ struct TransMirrorData mirror_data = {NULL};
+ struct TransMeshDataCrazySpace crazyspace_data = {NULL};
+
+ /* Support other objects using PET to adjust these, unless connected is enabled. */
+ if ((!prop_mode || (prop_mode & T_PROP_CONNECTED)) && (bm->totvertsel == 0)) {
+ continue;
+ }
+
+ int cd_offset = -1;
+ if (t->mode == TFM_BWEIGHT) {
+ if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) {
+ BM_data_layer_add(bm, &bm->vdata, CD_BWEIGHT);
+ }
+ cd_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
+ }
+ else {
+ BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_CREASE);
+ cd_offset = CustomData_get_offset(&bm->vdata, CD_CREASE);
+ }
+
+ if (cd_offset == -1) {
+ continue;
+ }
+
+ int data_len = 0;
+ if (prop_mode) {
+ BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
+ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ data_len++;
+ }
+ }
+ }
+ else {
+ data_len = bm->totvertsel;
+ }
+
+ if (data_len == 0) {
+ continue;
+ }
+
+ const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS);
+ if (is_island_center) {
+ /* In this specific case, near-by vertices will need to know
+ * the island of the nearest connected vertex. */
+ const bool calc_single_islands = ((prop_mode & T_PROP_CONNECTED) &&
+ (t->around == V3D_AROUND_LOCAL_ORIGINS) &&
+ (em->selectmode & SCE_SELECT_VERTEX));
+
+ const bool calc_island_center = false;
+ const bool calc_island_axismtx = false;
+
+ transform_convert_mesh_islands_calc(
+ em, calc_single_islands, calc_island_center, calc_island_axismtx, &island_data);
+ }
+
+ copy_m3_m4(mtx, tc->obedit->obmat);
+ /* we use a pseudo-inverse so that when one of the axes is scaled to 0,
+ * matrix inversion still works and we can still moving along the other */
+ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
+
+ /* Original index of our connected vertex when connected distances are calculated.
+ * Optional, allocate if needed. */
+ int *dists_index = NULL;
+ float *dists = NULL;
+ if (prop_mode & T_PROP_CONNECTED) {
+ dists = MEM_mallocN(bm->totvert * sizeof(float), __func__);
+ if (is_island_center) {
+ dists_index = MEM_mallocN(bm->totvert * sizeof(int), __func__);
+ }
+ transform_convert_mesh_connectivity_distance(em->bm, mtx, dists, dists_index);
+ }
+
+ /* Create TransDataMirror. */
+ if (tc->use_mirror_axis_any) {
+ bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
+ bool use_select = (t->flag & T_PROP_EDIT) == 0;
+ const bool mirror_axis[3] = {
+ tc->use_mirror_axis_x, tc->use_mirror_axis_y, tc->use_mirror_axis_z};
+ transform_convert_mesh_mirrordata_calc(
+ em, use_select, use_topology, mirror_axis, &mirror_data);
+
+ if (mirror_data.vert_map) {
+ tc->data_mirror_len = mirror_data.mirror_elem_len;
+ tc->data_mirror = MEM_mallocN(mirror_data.mirror_elem_len * sizeof(*tc->data_mirror),
+ __func__);
+
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
+ if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ if (mirror_data.vert_map[a].index != -1) {
+ data_len--;
+ }
+ }
+ }
+ }
+ }
+
+ /* Detect CrazySpace [tm]. */
+ transform_convert_mesh_crazyspace_detect(t, tc, em, &crazyspace_data);
+
+ /* Create TransData. */
+ BLI_assert(data_len >= 1);
+ tc->data_len = data_len;
+ tc->data = MEM_callocN(data_len * sizeof(TransData), "TransObData(Mesh EditMode)");
+
+ TransData *td = tc->data;
+ TransDataMirror *td_mirror = tc->data_mirror;
+ BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) {
+ if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
+ continue;
+ }
+
+ int island_index = -1;
+ if (island_data.island_vert_map) {
+ const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a;
+ island_index = island_data.island_vert_map[connected_index];
+ }
+
+ float *weight = BM_ELEM_CD_GET_VOID_P(eve, cd_offset);
+ if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) {
+ tc_mesh_cdata_transdata_create(
+ (TransDataBasic *)td_mirror, eve, weight, &island_data, island_index);
+
+ int elem_index = mirror_data.vert_map[a].index;
+ BMVert *v_src = BM_vert_at_index(bm, elem_index);
+
+ td_mirror->flag |= mirror_data.vert_map[a].flag;
+ td_mirror->loc_src = BM_ELEM_CD_GET_VOID_P(v_src, cd_offset);
+ td_mirror++;
+ }
+ else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
+ tc_mesh_cdata_transdata_create(
+ (TransDataBasic *)td, eve, weight, &island_data, island_index);
+
+ if (t->around == V3D_AROUND_LOCAL_ORIGINS) {
+ createSpaceNormal(td->axismtx, eve->no);
+ }
+ else {
+ /* Setting normals */
+ copy_v3_v3(td->axismtx[2], eve->no);
+ td->axismtx[0][0] = td->axismtx[0][1] = td->axismtx[0][2] = td->axismtx[1][0] =
+ td->axismtx[1][1] = td->axismtx[1][2] = 0.0f;
+ }
+
+ if (prop_mode) {
+ if (prop_mode & T_PROP_CONNECTED) {
+ td->dist = dists[a];
+ }
+ else {
+ td->flag |= TD_NOTCONNECTED;
+ td->dist = FLT_MAX;
+ }
+ }
+
+ /* CrazySpace */
+ transform_convert_mesh_crazyspace_transdata_set(
+ mtx,
+ smtx,
+ crazyspace_data.defmats ? crazyspace_data.defmats[a] : NULL,
+ crazyspace_data.quats && BM_elem_flag_test(eve, BM_ELEM_TAG) ?
+ crazyspace_data.quats[a] :
+ NULL,
+ td);
+
+ td++;
+ }
+ }
+
+ transform_convert_mesh_islanddata_free(&island_data);
+ transform_convert_mesh_mirrordata_free(&mirror_data);
+ transform_convert_mesh_crazyspace_free(&crazyspace_data);
+ if (dists) {
+ MEM_freeN(dists);
+ }
+ if (dists_index) {
+ MEM_freeN(dists_index);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Recalc Mesh Data
+ * \{ */
+
+static void tc_mesh_cdata_apply_to_mirror(TransInfo *t)
+{
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ if (tc->use_mirror_axis_any) {
+ TransDataMirror *td_mirror = tc->data_mirror;
+ for (int i = 0; i < tc->data_mirror_len; i++, td_mirror++) {
+ td_mirror->loc[0] = td_mirror->loc_src[0];
+ }
+ }
+ }
+}
+
+static void recalcData_mesh_cdata(TransInfo *t)
+{
+ bool is_canceling = t->state == TRANS_CANCEL;
+ /* mirror modifier clipping? */
+ if (!is_canceling) {
+ if (!(t->flag & T_NO_MIRROR)) {
+ tc_mesh_cdata_apply_to_mirror(t);
+ }
+ }
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY);
+ BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
+ BKE_editmesh_looptri_and_normals_calc(em);
+ }
+}
+
+/** \} */
+
+TransConvertTypeInfo TransConvertType_MeshVertCData = {
+ /* flags */ (T_EDIT | T_POINTS),
+ /* createTransData */ createTransMeshVertCData,
+ /* recalcData */ recalcData_mesh_cdata,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c
index 32f70fc010b..cfa933d1600 100644
--- a/source/blender/editors/transform/transform_convert_nla.c
+++ b/source/blender/editors/transform/transform_convert_nla.c
@@ -59,7 +59,7 @@ typedef struct TransDataNla {
/** \name NLA Transform Creation
* \{ */
-void createTransNlaData(bContext *C, TransInfo *t)
+static void createTransNlaData(bContext *C, TransInfo *t)
{
Scene *scene = t->scene;
SpaceNla *snla = NULL;
@@ -249,7 +249,7 @@ void createTransNlaData(bContext *C, TransInfo *t)
ANIM_animdata_freelist(&anim_data);
}
-void recalcData_nla(TransInfo *t)
+static void recalcData_nla(TransInfo *t)
{
SpaceNla *snla = (SpaceNla *)t->area->spacedata.first;
@@ -464,7 +464,7 @@ void recalcData_nla(TransInfo *t)
/** \name Special After Transform NLA
* \{ */
-void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t))
+static void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t))
{
bAnimContext ac;
@@ -506,3 +506,10 @@ void special_aftertrans_update__nla(bContext *C, TransInfo *UNUSED(t))
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_NLA = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransNlaData,
+ /* recalcData */ recalcData_nla,
+ /* special_aftertrans_update */ special_aftertrans_update__nla,
+};
diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c
index 8281052c314..ed19789fdd8 100644
--- a/source/blender/editors/transform/transform_convert_node.c
+++ b/source/blender/editors/transform/transform_convert_node.c
@@ -27,6 +27,13 @@
#include "transform_convert.h"
#include "transform_snap.h"
+struct TransCustomDataNode {
+ View2DEdgePanData edgepan_data;
+
+ /* Compare if the view has changed so we can update with `transformViewUpdate`. */
+ rctf viewrect_prev;
+};
+
/* -------------------------------------------------------------------- */
/** \name Node Transform Creation
* \{ */
@@ -89,21 +96,23 @@ static bool is_node_parent_select(bNode *node)
return false;
}
-void createTransNodeData(TransInfo *t)
+static void createTransNodeData(bContext *UNUSED(C), TransInfo *t)
{
const float dpi_fac = UI_DPI_FAC;
SpaceNode *snode = t->area->spacedata.first;
/* Custom data to enable edge panning during the node transform */
- View2DEdgePanData *customdata = MEM_callocN(sizeof(*customdata), __func__);
+ struct TransCustomDataNode *customdata = MEM_callocN(sizeof(*customdata), __func__);
UI_view2d_edge_pan_init(t->context,
- customdata,
+ &customdata->edgepan_data,
NODE_EDGE_PAN_INSIDE_PAD,
NODE_EDGE_PAN_OUTSIDE_PAD,
NODE_EDGE_PAN_SPEED_RAMP,
NODE_EDGE_PAN_MAX_SPEED,
NODE_EDGE_PAN_DELAY,
NODE_EDGE_PAN_ZOOM_INFLUENCE);
+ customdata->viewrect_prev = customdata->edgepan_data.initial_rect;
+
t->custom.type.data = customdata;
t->custom.type.use_free = true;
@@ -150,15 +159,15 @@ void createTransNodeData(TransInfo *t)
/** \name Node Transform Creation
* \{ */
-void flushTransNodes(TransInfo *t)
+static void flushTransNodes(TransInfo *t)
{
const float dpi_fac = UI_DPI_FAC;
- View2DEdgePanData *customdata = (View2DEdgePanData *)t->custom.type.data;
+ struct TransCustomDataNode *customdata = (struct TransCustomDataNode *)t->custom.type.data;
if (t->options & CTX_VIEW2D_EDGE_PAN) {
if (t->state == TRANS_CANCEL) {
- UI_view2d_edge_pan_cancel(t->context, customdata);
+ UI_view2d_edge_pan_cancel(t->context, &customdata->edgepan_data);
}
else {
/* Edge panning functions expect window coordinates, mval is relative to region */
@@ -166,13 +175,19 @@ void flushTransNodes(TransInfo *t)
t->region->winrct.xmin + t->mval[0],
t->region->winrct.ymin + t->mval[1],
};
- UI_view2d_edge_pan_apply(t->context, customdata, xy);
+ UI_view2d_edge_pan_apply(t->context, &customdata->edgepan_data, xy);
}
}
- /* Initial and current view2D rects for additional transform due to view panning and zooming */
- const rctf *rect_src = &customdata->initial_rect;
- const rctf *rect_dst = &t->region->v2d.cur;
+ float offset[2] = {0.0f, 0.0f};
+ if (t->state != TRANS_CANCEL) {
+ if (!BLI_rctf_compare(&customdata->viewrect_prev, &t->region->v2d.cur, FLT_EPSILON)) {
+ /* Additional offset due to change in view2D rect. */
+ BLI_rctf_transform_pt_v(&t->region->v2d.cur, &customdata->viewrect_prev, offset, offset);
+ tranformViewUpdate(t);
+ customdata->viewrect_prev = t->region->v2d.cur;
+ }
+ }
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
applyGridAbsolute(t);
@@ -184,10 +199,7 @@ void flushTransNodes(TransInfo *t)
bNode *node = td->extra;
float loc[2];
- copy_v2_v2(loc, td2d->loc);
-
- /* additional offset due to change in view2D rect */
- BLI_rctf_transform_pt_v(rect_dst, rect_src, loc, loc);
+ add_v2_v2v2(loc, td2d->loc, offset);
#ifdef USE_NODE_CENTER
loc[0] -= 0.5f * BLI_rctf_size_x(&node->totr);
@@ -220,7 +232,7 @@ void flushTransNodes(TransInfo *t)
/** \name Special After Transform Node
* \{ */
-void special_aftertrans_update__node(bContext *C, TransInfo *t)
+static void special_aftertrans_update__node(bContext *C, TransInfo *t)
{
struct Main *bmain = CTX_data_main(C);
const bool canceled = (t->state == TRANS_CANCEL);
@@ -249,3 +261,10 @@ void special_aftertrans_update__node(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Node = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransNodeData,
+ /* recalcData */ flushTransNodes,
+ /* special_aftertrans_update */ special_aftertrans_update__node,
+};
diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c
index 26d57ae1e58..caa11fa5db4 100644
--- a/source/blender/editors/transform/transform_convert_object.c
+++ b/source/blender/editors/transform/transform_convert_object.c
@@ -291,9 +291,10 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
}
}
-static void trans_object_base_deps_flag_prepare(ViewLayer *view_layer)
+static void trans_object_base_deps_flag_prepare(const Scene *scene, ViewLayer *view_layer)
{
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->object->id.tag &= ~LIB_TAG_DOIT;
}
}
@@ -323,11 +324,14 @@ static void flush_trans_object_base_deps_flag(Depsgraph *depsgraph, Object *obje
NULL);
}
-static void trans_object_base_deps_flag_finish(const TransInfo *t, ViewLayer *view_layer)
+static void trans_object_base_deps_flag_finish(const TransInfo *t,
+ const Scene *scene,
+ ViewLayer *view_layer)
{
if ((t->options & CTX_OBMODE_XFORM_OBDATA) == 0) {
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->object->id.tag & LIB_TAG_DOIT) {
base->flag_legacy |= BA_SNAP_FIX_DEPS_FIASCO;
}
@@ -352,13 +356,14 @@ static void set_trans_object_base_flags(TransInfo *t)
return;
}
/* Makes sure base flags and object flags are identical. */
- BKE_scene_base_flag_to_objects(t->view_layer);
+ BKE_scene_base_flag_to_objects(t->scene, t->view_layer);
/* Make sure depsgraph is here. */
DEG_graph_relations_update(depsgraph);
/* Clear all flags we need. It will be used to detect dependencies. */
- trans_object_base_deps_flag_prepare(view_layer);
+ trans_object_base_deps_flag_prepare(scene, view_layer);
/* Traverse all bases and set all possible flags. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
base->flag_legacy &= ~(BA_WAS_SEL | BA_TRANSFORM_LOCKED_IN_PLACE);
if (BASE_SELECTED_EDITABLE(v3d, base)) {
Object *ob = base->object;
@@ -392,7 +397,7 @@ static void set_trans_object_base_flags(TransInfo *t)
/* Store temporary bits in base indicating that base is being modified
* (directly or indirectly) by transforming objects.
*/
- trans_object_base_deps_flag_finish(t, view_layer);
+ trans_object_base_deps_flag_finish(t, scene, view_layer);
}
static bool mark_children(Object *ob)
@@ -420,11 +425,11 @@ static int count_proportional_objects(TransInfo *t)
Scene *scene = t->scene;
Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer);
/* Clear all flags we need. It will be used to detect dependencies. */
- trans_object_base_deps_flag_prepare(view_layer);
+ trans_object_base_deps_flag_prepare(scene, view_layer);
/* Rotations around local centers are allowed to propagate, so we take all objects. */
if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->mode, TFM_ROTATION, TFM_TRACKBALL)))) {
/* Mark all parents. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (BASE_SELECTED_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)) {
Object *parent = base->object->parent;
/* flag all parents */
@@ -435,7 +440,7 @@ static int count_proportional_objects(TransInfo *t)
}
}
/* Mark all children. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
/* all base not already selected or marked that is editable */
if ((base->object->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 &&
(base->flag & BASE_SELECTED) == 0 &&
@@ -445,7 +450,7 @@ static int count_proportional_objects(TransInfo *t)
}
}
/* Flush changed flags to all dependencies. */
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
/* If base is not selected, not a parent of selection or not a child of
* selection and it is editable and selectable.
@@ -460,16 +465,17 @@ static int count_proportional_objects(TransInfo *t)
/* Store temporary bits in base indicating that base is being modified
* (directly or indirectly) by transforming objects.
*/
- trans_object_base_deps_flag_finish(t, view_layer);
+ trans_object_base_deps_flag_finish(t, scene, view_layer);
return total;
}
static void clear_trans_object_base_flags(TransInfo *t)
{
+ Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
- Base *base;
- for (base = view_layer->object_bases.first; base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (base->flag_legacy & BA_WAS_SEL) {
ED_object_base_select(base, BA_SELECT);
}
@@ -480,7 +486,7 @@ static void clear_trans_object_base_flags(TransInfo *t)
}
}
-void createTransObject(bContext *C, TransInfo *t)
+static void createTransObject(bContext *C, TransInfo *t)
{
Main *bmain = CTX_data_main(C);
TransData *td = NULL;
@@ -559,11 +565,12 @@ void createTransObject(bContext *C, TransInfo *t)
CTX_DATA_END;
if (is_prop_edit) {
+ Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
View3D *v3d = t->view;
- Base *base;
- for (base = view_layer->object_bases.first; base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
/* if base is not selected, not a parent of selection
@@ -592,10 +599,12 @@ void createTransObject(bContext *C, TransInfo *t)
}
}
+ Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
View3D *v3d = t->view;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
/* if base is not selected, not a parent of selection
@@ -640,9 +649,11 @@ void createTransObject(bContext *C, TransInfo *t)
}
}
+ Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
if (ob->parent != NULL) {
if (ob->parent && !BLI_gset_haskey(objects_in_transdata, ob->parent) &&
@@ -672,7 +683,7 @@ void createTransObject(bContext *C, TransInfo *t)
}
}
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *ob = base->object;
if (BASE_XFORM_INDIRECT(base) || BLI_gset_haskey(objects_in_transdata, ob)) {
@@ -782,7 +793,8 @@ static void autokeyframe_object(
}
else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) {
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
- if (ob != OBACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob != BKE_view_layer_active_object_get(view_layer)) {
do_loc = true;
}
}
@@ -796,7 +808,8 @@ static void autokeyframe_object(
}
else if (tmode == TFM_RESIZE) {
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) {
- if (ob != OBACT(view_layer)) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ if (ob != BKE_view_layer_active_object_get(view_layer)) {
do_loc = true;
}
}
@@ -860,7 +873,7 @@ static bool motionpath_need_update_object(Scene *scene, Object *ob)
/** \name Recalc Data object
* \{ */
-void recalcData_objects(TransInfo *t)
+static void recalcData_objects(TransInfo *t)
{
bool motionpath_update = false;
@@ -918,7 +931,7 @@ void recalcData_objects(TransInfo *t)
/** \name Special After Transform Object
* \{ */
-void special_aftertrans_update__object(bContext *C, TransInfo *t)
+static void special_aftertrans_update__object(bContext *C, TransInfo *t)
{
BLI_assert(t->options & CTX_OBJECT);
@@ -991,3 +1004,10 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Object = {
+ /* flags */ 0,
+ /* createTransData */ createTransObject,
+ /* recalcData */ recalcData_objects,
+ /* special_aftertrans_update */ special_aftertrans_update__object,
+};
diff --git a/source/blender/editors/transform/transform_convert_object_texspace.c b/source/blender/editors/transform/transform_convert_object_texspace.c
index e5a66f8a1d2..839bf6b77b3 100644
--- a/source/blender/editors/transform/transform_convert_object_texspace.c
+++ b/source/blender/editors/transform/transform_convert_object_texspace.c
@@ -11,6 +11,7 @@
#include "BKE_animsys.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_object.h"
#include "BKE_report.h"
@@ -29,7 +30,7 @@
*
* \{ */
-void createTransTexspace(TransInfo *t)
+static void createTransTexspace(bContext *UNUSED(C), TransInfo *t)
{
ViewLayer *view_layer = t->view_layer;
TransData *td;
@@ -37,7 +38,8 @@ void createTransTexspace(TransInfo *t)
ID *id;
char *texflag;
- ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ ob = BKE_view_layer_active_object_get(view_layer);
if (ob == NULL) { /* Shouldn't logically happen, but still. */
return;
@@ -86,7 +88,7 @@ void createTransTexspace(TransInfo *t)
/** \name Recalc Data object
* \{ */
-void recalcData_texspace(TransInfo *t)
+static void recalcData_texspace(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
@@ -106,3 +108,10 @@ void recalcData_texspace(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_ObjectTexSpace = {
+ /* flags */ 0,
+ /* createTransData */ createTransTexspace,
+ /* recalcData */ recalcData_texspace,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_paintcurve.c b/source/blender/editors/transform/transform_convert_paintcurve.c
index b2566016496..61b1cb9493b 100644
--- a/source/blender/editors/transform/transform_convert_paintcurve.c
+++ b/source/blender/editors/transform/transform_convert_paintcurve.c
@@ -108,7 +108,7 @@ static void PaintCurvePointToTransData(PaintCurvePoint *pcp,
}
}
-void createTransPaintCurveVerts(bContext *C, TransInfo *t)
+static void createTransPaintCurveVerts(bContext *C, TransInfo *t)
{
Paint *paint = BKE_paint_get_active_from_context(C);
PaintCurve *pc;
@@ -188,7 +188,7 @@ void createTransPaintCurveVerts(bContext *C, TransInfo *t)
/** \name Paint Curve Transform Flush
* \{ */
-void flushTransPaintCurve(TransInfo *t)
+static void flushTransPaintCurve(TransInfo *t)
{
int i;
@@ -204,3 +204,10 @@ void flushTransPaintCurve(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_PaintCurve = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransPaintCurveVerts,
+ /* recalcData */ flushTransPaintCurve,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c
index 31e73288ad0..3e056b6a048 100644
--- a/source/blender/editors/transform/transform_convert_particle.c
+++ b/source/blender/editors/transform/transform_convert_particle.c
@@ -13,6 +13,7 @@
#include "BLI_math.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
@@ -28,13 +29,14 @@
/** \name Particle Edit Transform Creation
* \{ */
-void createTransParticleVerts(TransInfo *t)
+static void createTransParticleVerts(bContext *UNUSED(C), TransInfo *t)
{
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = NULL;
TransDataExtension *tx;
- Object *ob = OBACT(t->view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *ob = BKE_view_layer_active_object_get(t->view_layer);
ParticleEditSettings *pset = PE_settings(t->scene);
PTCacheEdit *edit = PE_get_current(t->depsgraph, t->scene, ob);
ParticleSystem *psys = NULL;
@@ -183,7 +185,8 @@ static void flushTransParticles(TransInfo *t)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
Scene *scene = t->scene;
ViewLayer *view_layer = t->view_layer;
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
PTCacheEdit *edit = PE_get_current(t->depsgraph, scene, ob);
ParticleSystem *psys = edit->psys;
PTCacheEditPoint *point;
@@ -223,7 +226,7 @@ static void flushTransParticles(TransInfo *t)
}
}
- PE_update_object(t->depsgraph, scene, OBACT(view_layer), 1);
+ PE_update_object(t->depsgraph, scene, ob, 1);
BKE_particle_batch_cache_dirty_tag(psys, BKE_PARTICLE_BATCH_DIRTY_ALL);
DEG_id_tag_update(&ob->id, ID_RECALC_PSYS_REDO);
}
@@ -235,7 +238,7 @@ static void flushTransParticles(TransInfo *t)
/** \name Recalc Transform Particles Data
* \{ */
-void recalcData_particles(TransInfo *t)
+static void recalcData_particles(TransInfo *t)
{
if (t->state != TRANS_CANCEL) {
applySnappingIndividual(t);
@@ -244,3 +247,10 @@ void recalcData_particles(TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Particle = {
+ /* flags */ T_POINTS,
+ /* createTransData */ createTransParticleVerts,
+ /* recalcData */ recalcData_particles,
+ /* special_aftertrans_update */ NULL,
+};
diff --git a/source/blender/editors/transform/transform_convert_sculpt.c b/source/blender/editors/transform/transform_convert_sculpt.c
index 5bf6bfa8644..3792cfefe06 100644
--- a/source/blender/editors/transform/transform_convert_sculpt.c
+++ b/source/blender/editors/transform/transform_convert_sculpt.c
@@ -10,6 +10,7 @@
#include "BLI_math.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_paint.h"
#include "BKE_report.h"
@@ -23,7 +24,7 @@
/** \name Sculpt Transform Creation
* \{ */
-void createTransSculpt(bContext *C, TransInfo *t)
+static void createTransSculpt(bContext *C, TransInfo *t)
{
TransData *td;
@@ -33,7 +34,8 @@ void createTransSculpt(bContext *C, TransInfo *t)
return;
}
- Object *ob = OBACT(t->view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *ob = BKE_view_layer_active_object_get(t->view_layer);
SculptSession *ss = ob->sculpt;
{
@@ -85,7 +87,7 @@ void createTransSculpt(bContext *C, TransInfo *t)
copy_m3_m4(td->axismtx, ob->obmat);
BLI_assert(!(t->options & CTX_PAINT_CURVE));
- ED_sculpt_init_transform(C, ob);
+ ED_sculpt_init_transform(C, ob, t->undo_name);
}
/** \} */
@@ -94,13 +96,14 @@ void createTransSculpt(bContext *C, TransInfo *t)
/** \name Recalc Data object
* \{ */
-void recalcData_sculpt(TransInfo *t)
+static void recalcData_sculpt(TransInfo *t)
{
- Object *ob = OBACT(t->view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *ob = BKE_view_layer_active_object_get(t->view_layer);
ED_sculpt_update_modal_transform(t->context, ob);
}
-void special_aftertrans_update__sculpt(bContext *C, TransInfo *t)
+static void special_aftertrans_update__sculpt(bContext *C, TransInfo *t)
{
Scene *scene = t->scene;
if (!BKE_id_is_editable(CTX_data_main(C), &scene->id)) {
@@ -108,9 +111,17 @@ void special_aftertrans_update__sculpt(bContext *C, TransInfo *t)
return;
}
- Object *ob = OBACT(t->view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *ob = BKE_view_layer_active_object_get(t->view_layer);
BLI_assert(!(t->options & CTX_PAINT_CURVE));
ED_sculpt_end_transform(C, ob);
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Sculpt = {
+ /* flags */ 0,
+ /* createTransData */ createTransSculpt,
+ /* recalcData */ recalcData_sculpt,
+ /* special_aftertrans_update */ special_aftertrans_update__sculpt,
+};
diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c
index dcc1739606f..ddc99caeef5 100644
--- a/source/blender/editors/transform/transform_convert_sequencer.c
+++ b/source/blender/editors/transform/transform_convert_sequencer.c
@@ -465,7 +465,7 @@ static SeqCollection *query_time_dependent_strips_strips(TransInfo *t)
return dependent;
}
-void createTransSeqData(TransInfo *t)
+static void createTransSeqData(bContext *UNUSED(C), TransInfo *t)
{
Scene *scene = t->scene;
Editing *ed = SEQ_editing_get(t->scene);
@@ -659,7 +659,7 @@ static void flushTransSeq(TransInfo *t)
SEQ_collection_free(transformed_strips);
}
-void recalcData_sequencer(TransInfo *t)
+static void recalcData_sequencer(TransInfo *t)
{
TransData *td;
int a;
@@ -689,7 +689,7 @@ void recalcData_sequencer(TransInfo *t)
/** \name Special After Transform Sequencer
* \{ */
-void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t)
+static void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t)
{
if (t->state == TRANS_CANCEL) {
return;
@@ -708,12 +708,12 @@ void special_aftertrans_update__sequencer(bContext *UNUSED(C), TransInfo *t)
if (t->mode == TFM_SEQ_SLIDE) {
if (t->frame_side == 'B') {
ED_markers_post_apply_transform(
- &t->scene->markers, t->scene, TFM_TIME_TRANSLATE, t->values[0], t->frame_side);
+ &t->scene->markers, t->scene, TFM_TIME_TRANSLATE, t->values_final[0], t->frame_side);
}
}
else if (ELEM(t->frame_side, 'L', 'R')) {
ED_markers_post_apply_transform(
- &t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values[0], t->frame_side);
+ &t->scene->markers, t->scene, TFM_TIME_EXTEND, t->values_final[0], t->frame_side);
}
}
}
@@ -734,3 +734,10 @@ void transform_convert_sequencer_channel_clamp(TransInfo *t, float r_val[2])
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Sequencer = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransSeqData,
+ /* recalcData */ recalcData_sequencer,
+ /* special_aftertrans_update */ special_aftertrans_update__sequencer,
+};
diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c
index 8be454b540c..3d0c3ddaa4c 100644
--- a/source/blender/editors/transform/transform_convert_sequencer_image.c
+++ b/source/blender/editors/transform/transform_convert_sequencer_image.c
@@ -105,7 +105,7 @@ static void freeSeqData(TransInfo *UNUSED(t),
MEM_freeN(td->extra);
}
-void createTransSeqImageData(TransInfo *t)
+static void createTransSeqImageData(bContext *UNUSED(C), TransInfo *t)
{
Editing *ed = SEQ_editing_get(t->scene);
const SpaceSeq *sseq = t->area->spacedata.first;
@@ -191,7 +191,7 @@ static bool autokeyframe_sequencer_image(bContext *C,
return changed;
}
-void recalcData_sequencer_image(TransInfo *t)
+static void recalcData_sequencer_image(TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
TransData *td = NULL;
@@ -247,7 +247,7 @@ void recalcData_sequencer_image(TransInfo *t)
}
}
-void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t)
+static void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t)
{
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
@@ -271,3 +271,10 @@ void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *
}
}
}
+
+TransConvertTypeInfo TransConvertType_SequencerImage = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransSeqImageData,
+ /* recalcData */ recalcData_sequencer_image,
+ /* special_aftertrans_update */ special_aftertrans_update__sequencer_image,
+};
diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c
index d447cd71a40..c0c660289a5 100644
--- a/source/blender/editors/transform/transform_convert_tracking.c
+++ b/source/blender/editors/transform/transform_convert_tracking.c
@@ -518,7 +518,7 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t)
}
}
-void createTransTrackingData(bContext *C, TransInfo *t)
+static void createTransTrackingData(bContext *C, TransInfo *t)
{
ARegion *region = CTX_wm_region(C);
SpaceClip *sc = CTX_wm_space_clip(C);
@@ -694,7 +694,7 @@ static void flushTransTracking(TransInfo *t)
}
}
-void recalcData_tracking(TransInfo *t)
+static void recalcData_tracking(TransInfo *t)
{
SpaceClip *sc = t->area->spacedata.first;
@@ -747,7 +747,7 @@ void recalcData_tracking(TransInfo *t)
/** \name Special After Transform Tracking
* \{ */
-void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
+static void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
{
SpaceClip *sc = t->area->spacedata.first;
MovieClip *clip = ED_space_clip_get_clip(sc);
@@ -790,3 +790,10 @@ void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
}
/** \} */
+
+TransConvertTypeInfo TransConvertType_Tracking = {
+ /* flags */ (T_POINTS | T_2D_EDIT),
+ /* createTransData */ createTransTrackingData,
+ /* recalcData */ recalcData_tracking,
+ /* special_aftertrans_update */ special_aftertrans_update__movieclip,
+};
diff --git a/source/blender/editors/transform/transform_draw_cursors.c b/source/blender/editors/transform/transform_draw_cursors.c
index 42942493dc3..b5a8decc390 100644
--- a/source/blender/editors/transform/transform_draw_cursors.c
+++ b/source/blender/editors/transform/transform_draw_cursors.c
@@ -116,7 +116,7 @@ void transform_draw_cursor_draw(bContext *UNUSED(C), int x, int y, void *customd
/* Dashed lines first. */
if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) {
GPU_line_width(DASH_WIDTH);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 0); /* "simple" mode */
immUniformThemeColor3(TH_VIEW_OVERLAY);
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index e45cac36736..03c53e1b3d2 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -176,7 +176,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
{
Scene *sce = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT;
ToolSettings *ts = CTX_data_tool_settings(C);
ARegion *region = CTX_wm_region(C);
@@ -333,7 +334,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else if (t->spacetype == SPACE_IMAGE) {
SpaceImage *sima = area->spacedata.first;
- if (ED_space_image_show_uvedit(sima, OBACT(t->view_layer))) {
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ if (ED_space_image_show_uvedit(sima, BKE_view_layer_active_object_get(t->view_layer))) {
/* UV transform */
}
else if (sima->mode == SI_MODE_MASK) {
@@ -555,7 +557,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve
}
else {
/* Release confirms preference should not affect node editor (T69288, T70504). */
- if (ISMOUSE(t->launch_event) &&
+ if (ISMOUSE_BUTTON(t->launch_event) &&
((U.flag & USER_RELEASECONFIRM) || (t->spacetype == SPACE_NODE))) {
/* Global "release confirm" on mouse bindings */
t->flag |= T_RELEASE_CONFIRM;
@@ -1066,8 +1068,8 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3])
}
}
else if (t->options & CTX_POSE_BONE) {
- ViewLayer *view_layer = t->view_layer;
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *ob = BKE_view_layer_active_object_get(t->view_layer);
if (ED_object_calc_active_center_for_posemode(ob, select_only, r_center)) {
mul_m4_v3(ob->obmat, r_center);
return true;
@@ -1083,11 +1085,10 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3])
}
else {
/* object mode */
- ViewLayer *view_layer = t->view_layer;
- Object *ob = OBACT(view_layer);
- Base *base = BASACT(view_layer);
- if (ob && ((!select_only) || ((base->flag & BASE_SELECTED) != 0))) {
- copy_v3_v3(r_center, ob->obmat[3]);
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Base *base = BKE_view_layer_active_base_get(t->view_layer);
+ if (base && ((!select_only) || ((base->flag & BASE_SELECTED) != 0))) {
+ copy_v3_v3(r_center, base->object->obmat[3]);
return true;
}
}
@@ -1132,6 +1133,33 @@ static void calculateCenter_FromAround(TransInfo *t, int around, float r_center[
}
}
+static void calculateZfac(TransInfo *t)
+{
+ /* ED_view3d_calc_zfac() defines a factor for perspective depth correction,
+ * used in ED_view3d_win_to_delta() */
+
+ /* zfac is only used convertViewVec only in cases operator was invoked in RGN_TYPE_WINDOW
+ * and never used in other cases.
+ *
+ * We need special case here as well, since ED_view3d_calc_zfac will crash when called
+ * for a region different from RGN_TYPE_WINDOW.
+ */
+ if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ t->zfac = ED_view3d_calc_zfac(t->region->regiondata, t->center_global);
+ }
+ else if (t->spacetype == SPACE_IMAGE) {
+ SpaceImage *sima = t->area->spacedata.first;
+ t->zfac = 1.0f / sima->zoom;
+ }
+ else if (t->region) {
+ View2D *v2d = &t->region->v2d;
+ /* Get zoom fac the same way as in
+ * `ui_view2d_curRect_validate_resize` - better keep in sync! */
+ const float zoomx = (float)(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur);
+ t->zfac = 1.0f / zoomx;
+ }
+}
+
void calculateCenter(TransInfo *t)
{
if ((t->flag & T_OVERRIDE_CENTER) == 0) {
@@ -1166,22 +1194,46 @@ void calculateCenter(TransInfo *t)
}
}
- if (t->spacetype == SPACE_VIEW3D) {
- /* #ED_view3d_calc_zfac() defines a factor for perspective depth correction,
- * used in #ED_view3d_win_to_delta(). */
+ calculateZfac(t);
+}
- /* NOTE: `t->zfac` is only used #convertViewVec only in cases operator was invoked in
- * #RGN_TYPE_WINDOW and never used in other cases.
- *
- * We need special case here as well, since #ED_view3d_calc_zfac will crash when called
- * for a region different from #RGN_TYPE_WINDOW. */
- if (t->region->regiontype == RGN_TYPE_WINDOW) {
- t->zfac = ED_view3d_calc_zfac(t->region->regiondata, t->center_global);
+/* Called every time the view changes due to navigation.
+ * Adjusts the mouse position relative to the object. */
+void tranformViewUpdate(TransInfo *t)
+{
+ float zoom_prev = t->zfac;
+ float zoom_new;
+ if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ if (!t->persp) {
+ zoom_prev *= len_v3(t->persinv[0]);
}
- else {
- t->zfac = 0.0f;
+
+ setTransformViewMatrices(t);
+ calculateZfac(t);
+
+ zoom_new = t->zfac;
+ if (!t->persp) {
+ zoom_new *= len_v3(t->persinv[0]);
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(t->orient); i++) {
+ if (t->orient[i].type == V3D_ORIENT_VIEW) {
+ copy_m3_m4(t->orient[i].matrix, t->viewinv);
+ normalize_m3(t->orient[i].matrix);
+ if (t->orient_curr == i) {
+ copy_m3_m3(t->spacemtx, t->orient[i].matrix);
+ invert_m3_m3_safe_ortho(t->spacemtx_inv, t->spacemtx);
+ }
+ }
}
}
+ else {
+ calculateZfac(t);
+ zoom_new = t->zfac;
+ }
+
+ calculateCenter2D(t);
+ transform_input_update(t, zoom_prev / zoom_new);
}
void calculatePropRatio(TransInfo *t)
@@ -1413,6 +1465,7 @@ Object *transform_object_deform_pose_armature_get(const TransInfo *t, Object *ob
* Lines below just check is also visible. */
Object *ob_armature = BKE_modifiers_is_deformed_by_armature(ob);
if (ob_armature && ob_armature->mode & OB_MODE_POSE) {
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
Base *base_arm = BKE_view_layer_base_find(t->view_layer, ob_armature);
if (base_arm) {
View3D *v3d = t->view;
diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c
index 426b338f8a7..a6eb25975e9 100644
--- a/source/blender/editors/transform/transform_gizmo_2d.c
+++ b/source/blender/editors/transform/transform_gizmo_2d.c
@@ -236,7 +236,7 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, NULL, &objects_len);
+ scene, view_layer, NULL, &objects_len);
if (ED_uvedit_minmax_multi(scene, objects, objects_len, r_min, r_max)) {
has_select = true;
}
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 5b749e05052..8e6a6c2c411 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -639,7 +639,8 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
(params->orientation_index - 1) :
BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Object *obedit = OBEDIT_FROM_OBACT(ob);
if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) {
Object *obpose = BKE_object_pose_armature_get(ob);
@@ -753,7 +754,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
invert_m4_m4(obedit->imat, obedit->obmat); \
uint objects_len = 0; \
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode( \
- view_layer, CTX_wm_view3d(C), &objects_len); \
+ scene, view_layer, CTX_wm_view3d(C), &objects_len); \
for (uint ob_index = 0; ob_index < objects_len; ob_index++) { \
Object *ob_iter = objects[ob_index]; \
const bool use_mat_local = (ob_iter != obedit);
@@ -941,7 +942,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
invert_m4_m4(ob->imat, ob->obmat);
uint objects_len = 0;
- Object **objects = BKE_object_pose_array_get(view_layer, v3d, &objects_len);
+ Object **objects = BKE_object_pose_array_get(scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob_iter = objects[ob_index];
@@ -1014,13 +1015,14 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
else {
/* we need the one selected object, if its not active */
- base = BASACT(view_layer);
- ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ base = BKE_view_layer_active_base_get(view_layer);
+ ob = base ? base->object : NULL;
if (base && ((base->flag & BASE_SELECTED) == 0)) {
ob = NULL;
}
- for (base = view_layer->object_bases.first; base; base = base->next) {
+ for (base = BKE_view_layer_object_bases_get(view_layer)->first; base; base = base->next) {
if (!BASE_SELECTED_EDITABLE(v3d, base)) {
continue;
}
@@ -1103,7 +1105,8 @@ static void gizmo_prepare_mat(const bContext *C,
/* pass */
}
else {
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob != NULL) {
if ((ob->mode & OB_MODE_ALL_SCULPT) && ob->sculpt) {
SculptSession *ss = ob->sculpt;
diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
index 131a7fd517f..0271f8e1988 100644
--- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c
@@ -101,7 +101,7 @@ static void gizmo_mesh_extrude_orientation_matrix_set_for_adjust(struct GizmoExt
for (int j = 0; j < 3; j++) {
copy_v3_v3(ggd->adjust[0]->matrix_basis[j], mat[j]);
}
- /* nop when (i == 2). */
+ /* NOP when (i == 2). */
swap_v3_v3(ggd->adjust[0]->matrix_basis[ggd->adjust_axis], ggd->adjust[0]->matrix_basis[2]);
}
@@ -261,7 +261,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup)
copy_m3_m3(ggd->data.normal_mat3, tbounds_normal.axis);
}
- /* TODO(campbell): run second since this modifies the 3D view, it should not. */
+ /* TODO(@campbellbarton): run second since this modifies the 3D view, it should not. */
if (!ED_transform_calc_gizmo_stats(C,
&(struct TransformCalcParams){
.orientation_index = ggd->data.orientation_index + 1,
diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c
index 3b320ff51d5..38dbe742279 100644
--- a/source/blender/editors/transform/transform_input.c
+++ b/source/blender/editors/transform/transform_input.c
@@ -8,6 +8,7 @@
#include <stdlib.h>
#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
#include "BKE_context.h"
@@ -18,6 +19,7 @@
#include "WM_types.h"
#include "transform.h"
+#include "transform_mode.h"
#include "MEM_guardedalloc.h"
@@ -251,11 +253,8 @@ void setCustomPointsFromDirection(TransInfo *t, MouseInput *mi, const float dir[
/** \name Setup & Handle Mouse Input
* \{ */
-void initMouseInput(TransInfo *UNUSED(t),
- MouseInput *mi,
- const float center[2],
- const int mval[2],
- const bool precision)
+void initMouseInput(
+ TransInfo *t, MouseInput *mi, const float center[2], const int mval[2], const bool precision)
{
mi->factor = 0;
mi->precision = precision;
@@ -266,14 +265,20 @@ void initMouseInput(TransInfo *UNUSED(t),
mi->imval[0] = mval[0];
mi->imval[1] = mval[1];
+ if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ float delta[3] = {mval[0] - center[0], mval[1] - center[1]};
+ ED_view3d_win_to_delta(t->region, delta, t->zfac, delta);
+ add_v3_v3v3(mi->imval_unproj, t->center_global, delta);
+ }
+
mi->post = NULL;
}
static void calcSpringFactor(MouseInput *mi)
{
- mi->factor = sqrtf(
- ((float)(mi->center[1] - mi->imval[1])) * ((float)(mi->center[1] - mi->imval[1])) +
- ((float)(mi->center[0] - mi->imval[0])) * ((float)(mi->center[0] - mi->imval[0])));
+ float mdir[2] = {(float)(mi->center[1] - mi->imval[1]), (float)(mi->center[0] - mi->imval[0])};
+
+ mi->factor = len_v2(mdir);
if (mi->factor == 0.0f) {
mi->factor = 1.0f; /* prevent Inf */
@@ -441,4 +446,52 @@ void applyMouseInput(TransInfo *t, MouseInput *mi, const int mval[2], float outp
}
}
+void transform_input_update(TransInfo *t, const float fac)
+{
+ MouseInput *mi = &t->mouse;
+ t->mouse.factor *= fac;
+ if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) {
+ projectIntView(t, mi->imval_unproj, mi->imval);
+ }
+ else {
+ int offset[2], center_2d_int[2] = {mi->center[0], mi->center[1]};
+ sub_v2_v2v2_int(offset, mi->imval, center_2d_int);
+ offset[0] *= fac;
+ offset[1] *= fac;
+
+ center_2d_int[0] = t->center2d[0];
+ center_2d_int[1] = t->center2d[1];
+ add_v2_v2v2_int(mi->imval, center_2d_int, offset);
+ }
+
+ float center_old[2];
+ copy_v2_v2(center_old, mi->center);
+ copy_v2_v2(mi->center, t->center2d);
+
+ if (mi->use_virtual_mval) {
+ /* Update accumulator. */
+ double mval_delta[2];
+ sub_v2_v2v2_db(mval_delta, mi->virtual_mval.accum, mi->virtual_mval.prev);
+ mval_delta[0] *= fac;
+ mval_delta[1] *= fac;
+ copy_v2_v2_db(mi->virtual_mval.accum, mi->virtual_mval.prev);
+ add_v2_v2_db(mi->virtual_mval.accum, mval_delta);
+ }
+
+ if (ELEM(mi->apply, InputAngle, InputAngleSpring)) {
+ float offset_center[2];
+ sub_v2_v2v2(offset_center, mi->center, center_old);
+ struct InputAngle_Data *data = mi->data;
+ data->mval_prev[0] += offset_center[0];
+ data->mval_prev[1] += offset_center[1];
+ }
+
+ if (t->mode == TFM_EDGE_SLIDE) {
+ transform_mode_edge_slide_reproject_input(t);
+ }
+ else if (t->mode == TFM_VERT_SLIDE) {
+ transform_mode_vert_slide_reproject_input(t);
+ }
+}
+
/** \} */
diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c
index 5ba0f08ee1c..10ea022757d 100644
--- a/source/blender/editors/transform/transform_mode.c
+++ b/source/blender/editors/transform/transform_mode.c
@@ -292,6 +292,9 @@ void constraintTransLim(const TransInfo *t, TransData *td)
continue;
}
+ /* Initialize the custom space for use in calculating the matrices. */
+ BKE_constraint_custom_object_space_init(&cob, con);
+
/* get constraint targets if needed */
BKE_constraint_targets_for_solving_get(t->depsgraph, con, &cob, &targets, ctime);
@@ -941,7 +944,11 @@ void ElementResize(const TransInfo *t,
if (td->ext && td->ext->size) {
float fsize[3];
- if (ELEM(t->data_type, TC_SCULPT, TC_OBJECT, TC_OBJECT_TEXSPACE, TC_POSE)) {
+ if (ELEM(t->data_type,
+ &TransConvertType_Sculpt,
+ &TransConvertType_Object,
+ &TransConvertType_ObjectTexSpace,
+ &TransConvertType_Pose)) {
float obsizemat[3][3];
/* Reorient the size mat to fit the oriented object. */
mul_m3_m3m3(obsizemat, tmat, td->axismtx);
@@ -1201,13 +1208,13 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode)
break;
}
- if (t->data_type == TC_MESH_VERTS) {
+ if (t->data_type == &TransConvertType_Mesh) {
/* Init Custom Data correction.
* Ideally this should be called when creating the TransData. */
transform_convert_mesh_customdatacorrect_init(t);
}
- /* TODO(germano): Some of these operations change the `t->mode`.
+ /* TODO(@germano): Some of these operations change the `t->mode`.
* This can be bad for Redo. */
// BLI_assert(t->mode == mode);
}
diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h
index eac6734ed88..063de87ebb2 100644
--- a/source/blender/editors/transform/transform_mode.h
+++ b/source/blender/editors/transform/transform_mode.h
@@ -117,6 +117,7 @@ void drawEdgeSlide(TransInfo *t);
void initEdgeSlide_ex(
TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp);
void initEdgeSlide(TransInfo *t);
+void transform_mode_edge_slide_reproject_input(TransInfo *t);
/* transform_mode_gpopacity.c */
@@ -191,3 +192,4 @@ void initTranslation(TransInfo *t);
void drawVertSlide(TransInfo *t);
void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp);
void initVertSlide(TransInfo *t);
+void transform_mode_vert_slide_reproject_input(TransInfo *t);
diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c
index acc6b20810f..a48f84ef0bc 100644
--- a/source/blender/editors/transform/transform_mode_bend.c
+++ b/source/blender/editors/transform/transform_mode_bend.c
@@ -262,7 +262,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2]))
+values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle));
}
- /* TODO(campbell): xform, compensate object center. */
+ /* TODO(@campbellbarton): xform, compensate object center. */
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
float warp_sta_local[3];
diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
index aa4d608de04..f7f9e14b8ac 100644
--- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c
@@ -64,10 +64,8 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
- *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
- if (*td->val <= 0.0f) {
- *td->val = 0.001f;
- }
+ *td->val = interpf(*td->val, td->ival, td->factor);
+ CLAMP_MIN(*td->val, 0.0f);
}
}
}
@@ -93,10 +91,6 @@ void initCurveShrinkFatten(TransInfo *t)
t->num.unit_sys = t->scene->unit.system;
t->num.unit_type[0] = B_UNIT_NONE;
-#ifdef USE_NUM_NO_ZERO
- t->num.val_flag[0] |= NUM_NO_ZERO;
-#endif
-
t->flag |= T_NO_CONSTRAINT;
}
diff --git a/source/blender/editors/transform/transform_mode_edge_bevelweight.c b/source/blender/editors/transform/transform_mode_edge_bevelweight.c
index 987d8396907..e96e74b596c 100644
--- a/source/blender/editors/transform/transform_mode_edge_bevelweight.c
+++ b/source/blender/editors/transform/transform_mode_edge_bevelweight.c
@@ -44,16 +44,11 @@ static void transdata_elem_bevel_weight(const TransInfo *UNUSED(t),
TransData *td,
const float weight)
{
- if (td->val == NULL) {
+ if (td->loc == NULL) {
return;
}
- *td->val = td->ival + weight * td->factor;
- if (*td->val < 0.0f) {
- *td->val = 0.0f;
- }
- if (*td->val > 1.0f) {
- *td->val = 1.0f;
- }
+ *td->loc = td->iloc[0] + weight * td->factor;
+ CLAMP(*td->loc, 0.0f, 1.0f);
}
static void transdata_elem_bevel_weight_fn(void *__restrict iter_data_v,
diff --git a/source/blender/editors/transform/transform_mode_edge_crease.c b/source/blender/editors/transform/transform_mode_edge_crease.c
index f1acc2a4c9a..1a3ccf30387 100644
--- a/source/blender/editors/transform/transform_mode_edge_crease.c
+++ b/source/blender/editors/transform/transform_mode_edge_crease.c
@@ -44,17 +44,12 @@ static void transdata_elem_crease(const TransInfo *UNUSED(t),
TransData *td,
const float crease)
{
- if (td->val == NULL) {
+ if (td->loc == NULL) {
return;
}
- *td->val = td->ival + crease * td->factor;
- if (*td->val < 0.0f) {
- *td->val = 0.0f;
- }
- if (*td->val > 1.0f) {
- *td->val = 1.0f;
- }
+ *td->loc = td->iloc[0] + crease * td->factor;
+ CLAMP(*td->loc, 0.0f, 1.0f);
}
static void transdata_elem_crease_fn(void *__restrict iter_data_v,
diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c
index a3c49d2362f..8a29321413e 100644
--- a/source/blender/editors/transform/transform_mode_edge_slide.c
+++ b/source/blender/editors/transform/transform_mode_edge_slide.c
@@ -292,8 +292,75 @@ static BMLoop *get_next_loop(
return NULL;
}
+static void edge_slide_projmat_get(TransInfo *t, TransDataContainer *tc, float r_projectMat[4][4])
+{
+ RegionView3D *rv3d = NULL;
+
+ if (t->spacetype == SPACE_VIEW3D) {
+ /* Background mode support. */
+ rv3d = t->region ? t->region->regiondata : NULL;
+ }
+
+ if (!rv3d) {
+ /* Ok, let's try to survive this. */
+ unit_m4(r_projectMat);
+ }
+ else {
+ ED_view3d_ob_project_mat_get(rv3d, tc->obedit, r_projectMat);
+ }
+}
+
+static void edge_slide_pair_project(TransDataEdgeSlideVert *sv,
+ ARegion *region,
+ float projectMat[4][4],
+ float r_sco_a[3],
+ float r_sco_b[3])
+{
+ BMVert *v = sv->v;
+
+ if (sv->v_side[1]) {
+ ED_view3d_project_float_v3_m4(region, sv->v_side[1]->co, r_sco_b, projectMat);
+ }
+ else {
+ add_v3_v3v3(r_sco_b, v->co, sv->dir_side[1]);
+ ED_view3d_project_float_v3_m4(region, r_sco_b, r_sco_b, projectMat);
+ }
+
+ if (sv->v_side[0]) {
+ ED_view3d_project_float_v3_m4(region, sv->v_side[0]->co, r_sco_a, projectMat);
+ }
+ else {
+ add_v3_v3v3(r_sco_a, v->co, sv->dir_side[0]);
+ ED_view3d_project_float_v3_m4(region, r_sco_a, r_sco_a, projectMat);
+ }
+}
+
+static void edge_slide_data_init_mval(MouseInput *mi, EdgeSlideData *sld, float *mval_dir)
+{
+ /* Possible all of the edge loops are pointing directly at the view. */
+ if (UNLIKELY(len_squared_v2(mval_dir) < 0.1f)) {
+ mval_dir[0] = 0.0f;
+ mval_dir[1] = 100.0f;
+ }
+
+ float mval_start[2], mval_end[2];
+
+ /* Zero out Start. */
+ zero_v2(mval_start);
+
+ /* dir holds a vector along edge loop */
+ copy_v2_v2(mval_end, mval_dir);
+ mul_v2_fl(mval_end, 0.5f);
+
+ sld->mval_start[0] = mi->imval[0] + mval_start[0];
+ sld->mval_start[1] = mi->imval[1] + mval_start[1];
+
+ sld->mval_end[0] = mi->imval[0] + mval_end[0];
+ sld->mval_end[1] = mi->imval[1] + mval_end[1];
+}
+
/**
- * Calculate screenspace `mval_start` / `mval_end`, optionally slide direction.
+ * Calculate screen-space `mval_start` / `mval_end`, optionally slide direction.
*/
static void calcEdgeSlide_mval_range(TransInfo *t,
TransDataContainer *tc,
@@ -308,29 +375,20 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
ARegion *region = t->region;
View3D *v3d = NULL;
- RegionView3D *rv3d = NULL;
float projectMat[4][4];
BMBVHTree *bmbvh;
/* only for use_calc_direction */
float(*loop_dir)[3] = NULL, *loop_maxdist = NULL;
- float mval_start[2], mval_end[2];
float mval_dir[3], dist_best_sq;
if (t->spacetype == SPACE_VIEW3D) {
/* background mode support */
v3d = t->area ? t->area->spacedata.first : NULL;
- rv3d = t->region ? t->region->regiondata : NULL;
}
- if (!rv3d) {
- /* ok, let's try to survive this */
- unit_m4(projectMat);
- }
- else {
- ED_view3d_ob_project_mat_get(rv3d, tc->obedit, projectMat);
- }
+ edge_slide_projmat_get(t, tc, projectMat);
if (use_occlude_geometry) {
bmbvh = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false);
@@ -379,21 +437,7 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
continue;
}
- if (sv->v_side[1]) {
- ED_view3d_project_float_v3_m4(region, sv->v_side[1]->co, sco_b, projectMat);
- }
- else {
- add_v3_v3v3(sco_b, v->co, sv->dir_side[1]);
- ED_view3d_project_float_v3_m4(region, sco_b, sco_b, projectMat);
- }
-
- if (sv->v_side[0]) {
- ED_view3d_project_float_v3_m4(region, sv->v_side[0]->co, sco_a, projectMat);
- }
- else {
- add_v3_v3v3(sco_a, v->co, sv->dir_side[0]);
- ED_view3d_project_float_v3_m4(region, sco_a, sco_a, projectMat);
- }
+ edge_slide_pair_project(sv, region, projectMat, sco_a, sco_b);
/* global direction */
dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a);
@@ -433,24 +477,7 @@ static void calcEdgeSlide_mval_range(TransInfo *t,
MEM_freeN(loop_maxdist);
}
- /* possible all of the edge loops are pointing directly at the view */
- if (UNLIKELY(len_squared_v2(mval_dir) < 0.1f)) {
- mval_dir[0] = 0.0f;
- mval_dir[1] = 100.0f;
- }
-
- /* zero out start */
- zero_v2(mval_start);
-
- /* dir holds a vector along edge loop */
- copy_v2_v2(mval_end, mval_dir);
- mul_v2_fl(mval_end, 0.5f);
-
- sld->mval_start[0] = t->mval[0] + mval_start[0];
- sld->mval_start[1] = t->mval[1] + mval_start[1];
-
- sld->mval_end[0] = t->mval[0] + mval_end[0];
- sld->mval_end[1] = t->mval[1] + mval_end[1];
+ edge_slide_data_init_mval(&t->mouse, sld, mval_dir);
if (bmbvh) {
BKE_bmbvh_free(bmbvh);
@@ -466,7 +493,6 @@ static void calcEdgeSlide_even(TransInfo *t,
if (sld->totsv > 0) {
ARegion *region = t->region;
- RegionView3D *rv3d = NULL;
float projectMat[4][4];
int i = 0;
@@ -475,18 +501,7 @@ static void calcEdgeSlide_even(TransInfo *t,
float dist_sq = 0;
float dist_min_sq = FLT_MAX;
- if (t->spacetype == SPACE_VIEW3D) {
- /* background mode support */
- rv3d = t->region ? t->region->regiondata : NULL;
- }
-
- if (!rv3d) {
- /* ok, let's try to survive this */
- unit_m4(projectMat);
- }
- else {
- ED_view3d_ob_project_mat_get(rv3d, tc->obedit, projectMat);
- }
+ edge_slide_projmat_get(t, tc, projectMat);
for (i = 0; i < sld->totsv; i++, sv++) {
/* Set length */
@@ -1204,7 +1219,7 @@ void drawEdgeSlide(TransInfo *t)
immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade);
immBegin(GPU_PRIM_LINES, sld->totsv * 2);
- /* TODO(campbell): Loop over all verts. */
+ /* TODO(@campbellbarton): Loop over all verts. */
sv = sld->sv;
for (i = 0; i < sld->totsv; i++, sv++) {
float a[3], b[3];
@@ -1553,3 +1568,32 @@ void initEdgeSlide(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mouse Input Utilities
+ * \{ */
+
+void transform_mode_edge_slide_reproject_input(TransInfo *t)
+{
+ ARegion *region = t->region;
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ EdgeSlideData *sld = tc->custom.mode.data;
+ if (sld) {
+ float projectMat[4][4];
+ edge_slide_projmat_get(t, tc, projectMat);
+
+ TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index];
+
+ float mval_dir[3], sco_a[3], sco_b[3];
+ edge_slide_pair_project(curr_sv, region, projectMat, sco_a, sco_b);
+ sub_v3_v3v3(mval_dir, sco_b, sco_a);
+ edge_slide_data_init_mval(&t->mouse, sld, mval_dir);
+ }
+ }
+
+ EdgeSlideData *sld = edgeSlideFirstGet(t);
+ setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start);
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index 83dce17d104..8b9431b65ea 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -74,7 +74,7 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
- *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
+ *td->val = interpf(*td->val, td->ival, td->factor);
CLAMP(*td->val, 0.0f, 1.0f);
}
}
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index 796d5c7ae9c..d8ec7d4ff50 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -74,7 +74,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
- *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
+ *td->val = interpf(*td->val, td->ival, td->factor);
if (*td->val <= 0.0f) {
*td->val = 0.001f;
}
diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
index 19a3deade63..e2ccf61796b 100644
--- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c
@@ -90,7 +90,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
}
/* apply PET */
- *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival);
+ *td->val = interpf(*td->val, td->ival, td->factor);
if (*td->val <= 0.0f) {
*td->val = 0.001f;
}
diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c
index 31d40486afc..70599c3577c 100644
--- a/source/blender/editors/transform/transform_mode_resize.c
+++ b/source/blender/editors/transform/transform_mode_resize.c
@@ -11,6 +11,7 @@
#include "BLI_task.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_unit.h"
#include "ED_screen.h"
@@ -84,6 +85,98 @@ static void ApplySnapResize(TransInfo *t, float vec[3])
}
}
+/**
+ * Find the correction for the scaling factor when "Constrain to Bounds" is active.
+ * \param numerator: How far the UV boundary (unit square) is from the origin of the scale.
+ * \param denominator: How far the AABB is from the origin of the scale.
+ * \param scale: Scale parameter to update.
+ */
+static void constrain_scale_to_boundary(const float numerator,
+ const float denominator,
+ float *scale)
+{
+ if (denominator == 0.0f) {
+ /* The origin of the scale is on the edge of the boundary. */
+ if (numerator < 0.0f) {
+ /* Negative scale will wrap around and put us outside the boundary. */
+ *scale = 0.0f; /* Hold at the boundary instead. */
+ }
+ return; /* Nothing else we can do without more info. */
+ }
+
+ const float correction = numerator / denominator;
+ if (correction < 0.0f || !isfinite(correction)) {
+ /* TODO: Correction is negative or invalid, but we lack context to fix `*scale`. */
+ return;
+ }
+
+ if (denominator < 0.0f) {
+ /* Scale origin is outside boundary, only make scale bigger. */
+ if (*scale < correction) {
+ *scale = correction;
+ }
+ return;
+ }
+
+ /* Scale origin is inside boundary, the "regular" case, limit maximum scale. */
+ if (*scale > correction) {
+ *scale = correction;
+ }
+}
+
+static bool clip_uv_transform_resize(TransInfo *t, float vec[2])
+{
+
+ /* 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. */
+ const SpaceImage *sima = t->area->spacedata.first;
+ BKE_image_find_nearest_tile_with_offset(sima->image, t->center_global, base_offset);
+
+ /* Assume no change is required. */
+ float scale = 1.0f;
+
+ /* Are we scaling U and V together, or just one axis? */
+ const bool adjust_u = !(t->con.mode & CON_AXIS1);
+ const bool adjust_v = !(t->con.mode & CON_AXIS0);
+ const bool use_local_center = transdata_check_local_center(t, t->around);
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) {
+
+ /* Get scale origin. */
+ const float *scale_origin = use_local_center ? td->center : t->center_global;
+
+ /* Alias td->loc as min and max just in case we need to optimize later. */
+ const float *min = td->loc;
+ const float *max = td->loc;
+
+ if (adjust_u) {
+ /* Update U against the left border. */
+ constrain_scale_to_boundary(
+ scale_origin[0] - base_offset[0], scale_origin[0] - min[0], &scale);
+
+ /* Now the right border, negated, because `-1.0 / -1.0 = 1.0` */
+ constrain_scale_to_boundary(
+ base_offset[0] + t->aspect[0] - scale_origin[0], max[0] - scale_origin[0], &scale);
+ }
+
+ /* Do the same for the V co-ordinate. */
+ if (adjust_v) {
+ constrain_scale_to_boundary(
+ scale_origin[1] - base_offset[1], scale_origin[1] - min[1], &scale);
+
+ constrain_scale_to_boundary(
+ base_offset[1] + t->aspect[1] - scale_origin[1], max[1] - scale_origin[1], &scale);
+ }
+ }
+ }
+ vec[0] *= scale;
+ vec[1] *= scale;
+ return scale != 1.0f;
+}
+
static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
{
float mat[3][3];
@@ -157,7 +250,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2]))
}
/* Evil hack - redo resize if clipping needed. */
- if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values_final, 1)) {
+ if (t->flag & T_CLIP_UV && clip_uv_transform_resize(t, t->values_final)) {
size_to_mat3(mat, t->values_final);
if (t->con.mode & CON_APPLY) {
diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c
index a7207b36578..f3186b21cb9 100644
--- a/source/blender/editors/transform/transform_mode_rotate.c
+++ b/source/blender/editors/transform/transform_mode_rotate.c
@@ -286,9 +286,72 @@ static void applyRotationValue(TransInfo *t,
}
}
+static bool uv_rotation_in_clip_bounds_test(const TransInfo *t, const float angle)
+{
+ const float cos_angle = cosf(angle);
+ const float sin_angle = sinf(angle);
+ const float *center = t->center_global;
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ TransData *td = tc->data;
+ for (int i = 0; i < tc->data_len; i++, td++) {
+ if (td->flag & TD_SKIP) {
+ continue;
+ }
+ if (td->factor < 1.0f) {
+ continue; /* Proportional edit, will get picked up in next phase. */
+ }
+
+ float uv[2];
+ sub_v2_v2v2(uv, td->iloc, center);
+ float pr[2];
+ pr[0] = cos_angle * uv[0] + sin_angle * uv[1];
+ pr[1] = -sin_angle * uv[0] + cos_angle * uv[1];
+ add_v2_v2(pr, center);
+ /* TODO: UDIM support. */
+ if (pr[0] < 0.0f || 1.0f < pr[0]) {
+ return false;
+ }
+ if (pr[1] < 0.0f || 1.0f < pr[1]) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static bool clip_uv_transform_rotate(const TransInfo *t, float *vec, float *vec_inside_bounds)
+{
+ float angle = vec[0];
+ if (uv_rotation_in_clip_bounds_test(t, angle)) {
+ vec_inside_bounds[0] = angle; /* Store for next iteration. */
+ return false; /* Nothing to do. */
+ }
+ float angle_inside_bounds = vec_inside_bounds[0];
+ if (!uv_rotation_in_clip_bounds_test(t, angle_inside_bounds)) {
+ return false; /* No known way to fix, may as well rotate anyway. */
+ }
+ const int max_i = 32; /* Limit iteration, mainly for debugging. */
+ for (int i = 0; i < max_i; i++) {
+ /* Binary search. */
+ const float angle_mid = (angle_inside_bounds + angle) / 2.0f;
+ if (angle_mid == angle_inside_bounds || angle_mid == angle) {
+ break; /* float precision reached. */
+ }
+ if (uv_rotation_in_clip_bounds_test(t, angle_mid)) {
+ angle_inside_bounds = angle_mid;
+ }
+ else {
+ angle = angle_mid;
+ }
+ }
+
+ vec_inside_bounds[0] = angle_inside_bounds; /* Store for next iteration. */
+ vec[0] = angle_inside_bounds; /* Update rotation angle. */
+ return true;
+}
+
static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
{
- char str[UI_MAX_DRAW_STR];
float axis_final[3];
float final = t->values[0] + t->values_modal_offset[0];
@@ -313,13 +376,27 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
t->values_final[0] = final;
- headerRotation(t, str, sizeof(str), final);
-
const bool is_large_rotation = hasNumInput(&t->num);
applyRotationValue(t, final, axis_final, is_large_rotation);
+ if (t->flag & T_CLIP_UV) {
+ if (clip_uv_transform_rotate(t, t->values_final, t->values_inside_constraints)) {
+ applyRotationValue(t, t->values_final[0], axis_final, is_large_rotation);
+ }
+
+ /* In proportional edit it can happen that */
+ /* vertices in the radius of the brush end */
+ /* outside the clipping area */
+ /* XXX HACK - dg */
+ if (t->flag & T_PROP_EDIT) {
+ clipUVData(t);
+ }
+ }
+
recalcData(t);
+ char str[UI_MAX_DRAW_STR];
+ headerRotation(t, str, sizeof(str), t->values_final[0]);
ED_area_status_text(t->area, str);
}
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 65690f9069d..8f6ec7bd98f 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -16,6 +16,7 @@
#include "BLI_task.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "BKE_report.h"
#include "BKE_unit.h"
@@ -434,6 +435,48 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
custom_data->prev.rotate_mode = rotate_mode;
}
+static bool clip_uv_transform_translation(TransInfo *t, float vec[2])
+{
+ /* 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. */
+ const SpaceImage *sima = t->area->spacedata.first;
+ BKE_image_find_nearest_tile_with_offset(sima->image, t->center_global, base_offset);
+
+ float min[2], max[2];
+ min[0] = min[1] = FLT_MAX;
+ max[0] = max[1] = -FLT_MAX;
+
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ for (TransData *td = tc->data; td < tc->data + tc->data_len; td++) {
+ minmax_v2v2_v2(min, max, td->loc);
+ }
+ }
+
+ bool result = false;
+ if (min[0] < base_offset[0]) {
+ vec[0] += base_offset[0] - min[0];
+ result = true;
+ }
+ else if (max[0] > base_offset[0] + t->aspect[0]) {
+ vec[0] -= max[0] - base_offset[0] - t->aspect[0];
+ result = true;
+ }
+
+ if (min[1] < base_offset[1]) {
+ vec[1] += base_offset[1] - min[1];
+ result = true;
+ }
+ else if (max[1] > base_offset[1] + t->aspect[1]) {
+ vec[1] -= max[1] - base_offset[1] - t->aspect[1];
+ result = true;
+ }
+
+ return result;
+}
+
static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
{
char str[UI_MAX_DRAW_STR];
@@ -498,7 +541,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
applyTranslationValue(t, global_dir);
/* evil hack - redo translation if clipping needed */
- if (t->flag & T_CLIP_UV && clipUVTransform(t, global_dir, 0)) {
+ if (t->flag & T_CLIP_UV && clip_uv_transform_translation(t, global_dir)) {
applyTranslationValue(t, global_dir);
/* In proportional edit it can happen that */
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 674ffcf17a8..d7c4d862b23 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -68,7 +68,7 @@ typedef struct VertSlideParams {
bool flipped;
} VertSlideParams;
-static void calcVertSlideCustomPoints(struct TransInfo *t)
+static void vert_slide_update_input(TransInfo *t)
{
VertSlideParams *slp = t->custom.mode.data;
VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data;
@@ -94,6 +94,11 @@ static void calcVertSlideCustomPoints(struct TransInfo *t)
else {
setCustomPoints(t, &t->mouse, mval_end, mval_start);
}
+}
+
+static void calcVertSlideCustomPoints(struct TransInfo *t)
+{
+ vert_slide_update_input(t);
/* setCustomPoints isn't normally changing as the mouse moves,
* in this case apply mouse input immediately so we don't refresh
@@ -673,3 +678,22 @@ void initVertSlide(TransInfo *t)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Mouse Input Utilities
+ * \{ */
+
+void transform_mode_vert_slide_reproject_input(TransInfo *t)
+{
+ if (t->spacetype == SPACE_VIEW3D) {
+ RegionView3D *rv3d = t->region->regiondata;
+ FOREACH_TRANS_DATA_CONTAINER (t, tc) {
+ VertSlideData *sld = tc->custom.mode.data;
+ ED_view3d_ob_project_mat_get(rv3d, tc->obedit, sld->proj_mat);
+ }
+ }
+
+ vert_slide_update_input(t);
+}
+
+/** \} */
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index a64eff8f981..99919c0ed78 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -379,6 +379,8 @@ static int transformops_data(bContext *C, wmOperator *op, const wmEvent *event)
if (op->customdata == NULL) {
TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data2");
+ t->undo_name = op->type->name;
+
int mode = transformops_mode(op);
retval = initTransform(C, t, op, event, mode);
@@ -566,6 +568,17 @@ static bool transform_poll_property(const bContext *UNUSED(C),
}
}
+ /* Snapping. */
+ {
+ PropertyRNA *prop_snap = RNA_struct_find_property(op->ptr, "snap");
+ if (prop_snap && (prop_snap != prop) &&
+ (RNA_property_boolean_get(op->ptr, prop_snap) == false)) {
+ if (STRPREFIX(prop_id, "snap") || STRPREFIX(prop_id, "use_snap")) {
+ return false;
+ }
+ }
+ }
+
return true;
}
@@ -644,28 +657,63 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
}
if (flags & P_SNAP) {
- prop = RNA_def_boolean(ot->srna, "snap", 0, "Use Snapping Options", "");
+ prop = RNA_def_boolean(ot->srna, "snap", false, "Use Snapping Options", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_enum(ot->srna,
+ "snap_elements",
+ rna_enum_snap_element_items,
+ SCE_SNAP_MODE_INCREMENT,
+ "Snap to Elements",
+ "");
+ RNA_def_property_flag(prop, PROP_ENUM_FLAG);
+
+ RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", "");
+
if (flags & P_GEO_SNAP) {
- /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid
- * previous ambiguity of "target" (now, "source" is geometry to be moved and "target" is
- * geometry to which moved geometry is snapped). Use "Source snap point" and "Point on
- * source that will snap to target" for name and description, respectively. */
- prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Target", "");
+ /* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of
+ * "target" (now, "source" is geometry to be moved and "target" is geometry to which moved
+ * geometry is snapped). Use "Source snap point" and "Point on source that will snap to
+ * target" for name and description, respectively. */
+ prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Snap With", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ /* Target selection. */
+ prop = RNA_def_boolean(ot->srna, "use_snap_self", true, "Target: Include Active", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_boolean(ot->srna, "use_snap_edit", true, "Target: Include Edit", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_boolean(ot->srna, "use_snap_nonedit", true, "Target: Include Non-Edited", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_boolean(
+ ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ /* Face Nearest options */
+ prop = RNA_def_boolean(
+ ot->srna, "use_snap_to_same_target", false, "Snap to Same Target", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
+ prop = RNA_def_int(
+ ot->srna, "snap_face_nearest_steps", 1, 1, 32767, "Face Nearest Steps", "", 1, 32767);
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+
prop = RNA_def_float_vector(
ot->srna, "snap_point", 3, NULL, -FLT_MAX, FLT_MAX, "Point", "", -FLT_MAX, FLT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
if (flags & P_ALIGN_SNAP) {
- prop = RNA_def_boolean(ot->srna, "snap_align", 0, "Align with Point Normal", "");
+ prop = RNA_def_boolean(ot->srna, "snap_align", false, "Align with Point Normal", "");
RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_float_vector(
ot->srna, "snap_normal", 3, NULL, -FLT_MAX, FLT_MAX, "Normal", "", -FLT_MAX, FLT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
}
}
+ else {
+ prop = RNA_def_boolean(
+ ot->srna, "use_snap_selectable_only", false, "Target: Exclude Non-Selectable", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN);
+ }
}
if (flags & P_GPENCIL_EDIT) {
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index c0d943e17ee..212df5978e4 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -476,7 +476,8 @@ void ED_transform_calc_orientation_from_type(const bContext *C, float r_mat[3][3
Object *obedit = CTX_data_edit_object(C);
View3D *v3d = CTX_wm_view3d(C);
RegionView3D *rv3d = region->regiondata;
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
const short orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT);
const int pivot_point = scene->toolsettings->transform_pivot_point;
@@ -515,7 +516,7 @@ short ED_transform_calc_orientation_from_type_ex(const Scene *scene,
}
case V3D_ORIENT_NORMAL: {
if (obedit || (ob && ob->mode & OB_MODE_POSE)) {
- ED_getTransformOrientationMatrix(view_layer, v3d, ob, obedit, pivot_point, r_mat);
+ ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat);
break;
}
/* No break we define 'normal' as 'local' in Object mode. */
@@ -528,7 +529,7 @@ short ED_transform_calc_orientation_from_type_ex(const Scene *scene,
* use the active pones axis for display T33575, this works as expected on a single
* bone and users who select many bones will understand what's going on and what local
* means when they start transforming. */
- ED_getTransformOrientationMatrix(view_layer, v3d, ob, obedit, pivot_point, r_mat);
+ ED_getTransformOrientationMatrix(scene, view_layer, v3d, ob, obedit, pivot_point, r_mat);
}
else {
transform_orientations_create_from_axis(r_mat, UNPACK3(ob->obmat));
@@ -744,7 +745,8 @@ static uint bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const uint n)
}
#endif
-int getTransformOrientation_ex(ViewLayer *view_layer,
+int getTransformOrientation_ex(const Scene *scene,
+ ViewLayer *view_layer,
const View3D *v3d,
struct Object *ob,
struct Object *obedit,
@@ -1252,6 +1254,7 @@ int getTransformOrientation_ex(ViewLayer *view_layer,
ok = true;
}
else {
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (UNLIKELY(base == NULL)) {
/* This is very unlikely, if it happens allow the value to be set since the caller
@@ -1282,13 +1285,15 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3])
/* dummy value, not V3D_AROUND_ACTIVE and not V3D_AROUND_LOCAL_ORIGINS */
short around = V3D_AROUND_CENTER_BOUNDS;
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
- return getTransformOrientation_ex(view_layer, v3d, obact, obedit, normal, plane, around);
+ return getTransformOrientation_ex(scene, view_layer, v3d, obact, obedit, normal, plane, around);
}
-void ED_getTransformOrientationMatrix(ViewLayer *view_layer,
+void ED_getTransformOrientationMatrix(const Scene *scene,
+ ViewLayer *view_layer,
const View3D *v3d,
Object *ob,
Object *obedit,
@@ -1300,7 +1305,7 @@ void ED_getTransformOrientationMatrix(ViewLayer *view_layer,
int type;
- type = getTransformOrientation_ex(view_layer, v3d, ob, obedit, normal, plane, around);
+ type = getTransformOrientation_ex(scene, view_layer, v3d, ob, obedit, normal, plane, around);
/* Fallback, when the plane can't be calculated. */
if (ORIENTATION_USE_PLANE(type) && is_zero_v3(plane)) {
diff --git a/source/blender/editors/transform/transform_orientations.h b/source/blender/editors/transform/transform_orientations.h
index 3ac235517a7..32093e830b0 100644
--- a/source/blender/editors/transform/transform_orientations.h
+++ b/source/blender/editors/transform/transform_orientations.h
@@ -55,7 +55,8 @@ enum {
};
#define ORIENTATION_USE_PLANE(ty) ELEM(ty, ORIENTATION_NORMAL, ORIENTATION_EDGE, ORIENTATION_FACE)
-int getTransformOrientation_ex(ViewLayer *view_layer,
+int getTransformOrientation_ex(const Scene *scene,
+ ViewLayer *view_layer,
const View3D *v3d,
struct Object *ob,
struct Object *obedit,
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 22d062a71dc..31d36fc4d92 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -51,9 +51,6 @@
static bool doForceIncrementSnap(const TransInfo *t);
-/* this should be passed as an arg for use in snap functions */
-#undef BASACT
-
/* use half of flt-max so we can scale up without an exception */
/* -------------------------------------------------------------------- */
@@ -128,15 +125,11 @@ bool activeSnap(const TransInfo *t)
bool activeSnap_SnappingIndividual(const TransInfo *t)
{
- if (activeSnap(t) && t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST) {
- return true;
- }
-
- if (!t->tsnap.project) {
+ if (!activeSnap(t) || (t->flag & T_NO_PROJECT)) {
return false;
}
- if (!activeSnap(t) || (t->flag & T_NO_PROJECT)) {
+ if (!(t->tsnap.project || (t->tsnap.mode & SCE_SNAP_MODE_FACE_NEAREST))) {
return false;
}
@@ -195,156 +188,152 @@ static bool doForceIncrementSnap(const TransInfo *t)
void drawSnapping(const struct bContext *C, TransInfo *t)
{
uchar col[4], selectedCol[4], activeCol[4];
-
if (!activeSnap(t)) {
return;
}
- if (t->spacetype == SPACE_VIEW3D) {
- bool draw_target = (t->tsnap.status & TARGET_INIT) &&
- (t->tsnap.mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR);
-
- if (draw_target || validSnap(t)) {
- UI_GetThemeColor3ubv(TH_TRANSFORM, col);
- col[3] = 128;
+ bool draw_target = (t->spacetype == SPACE_VIEW3D) && (t->tsnap.status & TARGET_INIT) &&
+ (t->tsnap.mode & SCE_SNAP_MODE_EDGE_PERPENDICULAR);
- UI_GetThemeColor3ubv(TH_SELECT, selectedCol);
- selectedCol[3] = 128;
+ if (!(draw_target || validSnap(t))) {
+ return;
+ }
- UI_GetThemeColor3ubv(TH_ACTIVE, activeCol);
- activeCol[3] = 192;
+ if (t->spacetype == SPACE_SEQ) {
+ UI_GetThemeColor3ubv(TH_SEQ_ACTIVE, col);
+ col[3] = 128;
+ }
+ else if (t->spacetype != SPACE_IMAGE) {
+ UI_GetThemeColor3ubv(TH_TRANSFORM, col);
+ col[3] = 128;
- const float *loc_cur = NULL;
- const float *loc_prev = NULL;
- const float *normal = NULL;
+ UI_GetThemeColor3ubv(TH_SELECT, selectedCol);
+ selectedCol[3] = 128;
- GPU_depth_test(GPU_DEPTH_NONE);
+ UI_GetThemeColor3ubv(TH_ACTIVE, activeCol);
+ activeCol[3] = 192;
+ }
- RegionView3D *rv3d = CTX_wm_region_view3d(C);
- if (!BLI_listbase_is_empty(&t->tsnap.points)) {
- /* Draw snap points. */
+ if (t->spacetype == SPACE_VIEW3D) {
+ const float *loc_cur = NULL;
+ const float *loc_prev = NULL;
+ const float *normal = NULL;
- float size = 2.0f * UI_GetThemeValuef(TH_VERTEX_SIZE);
- float view_inv[4][4];
- copy_m4_m4(view_inv, rv3d->viewinv);
+ GPU_depth_test(GPU_DEPTH_NONE);
- uint pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ RegionView3D *rv3d = CTX_wm_region_view3d(C);
+ if (!BLI_listbase_is_empty(&t->tsnap.points)) {
+ /* Draw snap points. */
- immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ float size = 2.0f * UI_GetThemeValuef(TH_VERTEX_SIZE);
+ float view_inv[4][4];
+ copy_m4_m4(view_inv, rv3d->viewinv);
- LISTBASE_FOREACH (TransSnapPoint *, p, &t->tsnap.points) {
- if (p == t->tsnap.selectedPoint) {
- immUniformColor4ubv(selectedCol);
- }
- else {
- immUniformColor4ubv(col);
- }
- imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size, view_inv, pos);
- }
+ uint pos = GPU_vertformat_attr_add(
+ immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
- immUnbindProgram();
- }
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- /* draw normal if needed */
- if (usingSnappingNormal(t) && validSnappingNormal(t)) {
- normal = t->tsnap.snapNormal;
+ LISTBASE_FOREACH (TransSnapPoint *, p, &t->tsnap.points) {
+ if (p == t->tsnap.selectedPoint) {
+ immUniformColor4ubv(selectedCol);
+ }
+ else {
+ immUniformColor4ubv(col);
+ }
+ imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size, view_inv, pos);
}
- if (draw_target) {
- loc_prev = t->tsnap.snapTarget;
- }
+ immUnbindProgram();
+ }
- if (validSnap(t)) {
- loc_cur = t->tsnap.snapPoint;
- }
+ /* draw normal if needed */
+ if (usingSnappingNormal(t) && validSnappingNormal(t)) {
+ normal = t->tsnap.snapNormal;
+ }
- ED_view3d_cursor_snap_draw_util(
- rv3d, loc_prev, loc_cur, normal, col, activeCol, t->tsnap.snapElem);
+ if (draw_target) {
+ loc_prev = t->tsnap.snapTarget;
+ }
- GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+ if (validSnap(t)) {
+ loc_cur = t->tsnap.snapPoint;
}
+
+ ED_view3d_cursor_snap_draw_util(
+ rv3d, loc_prev, loc_cur, normal, col, activeCol, t->tsnap.snapElem);
+
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
}
else if (t->spacetype == SPACE_IMAGE) {
- if (validSnap(t)) {
- uint pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
-
- float x, y;
- const float snap_point[2] = {
- t->tsnap.snapPoint[0] / t->aspect[0],
- t->tsnap.snapPoint[1] / t->aspect[1],
- };
- UI_view2d_view_to_region_fl(&t->region->v2d, UNPACK2(snap_point), &x, &y);
- float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize;
-
- GPU_matrix_push_projection();
- wmOrtho2_region_pixelspace(t->region);
-
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- immUniformColor3ub(255, 255, 255);
- imm_draw_circle_wire_2d(pos, x, y, radius, 8);
- immUnbindProgram();
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- GPU_matrix_pop_projection();
- }
- }
- else if (t->spacetype == SPACE_NODE) {
- if (validSnap(t)) {
- ARegion *region = CTX_wm_region(C);
- TransSnapPoint *p;
- float size;
+ float x, y;
+ const float snap_point[2] = {
+ t->tsnap.snapPoint[0] / t->aspect[0],
+ t->tsnap.snapPoint[1] / t->aspect[1],
+ };
+ UI_view2d_view_to_region_fl(&t->region->v2d, UNPACK2(snap_point), &x, &y);
+ float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE) * U.pixelsize;
- size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
+ GPU_matrix_push_projection();
+ wmOrtho2_region_pixelspace(t->region);
- GPU_blend(GPU_BLEND_ALPHA);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ immUniformColor3ub(255, 255, 255);
+ imm_draw_circle_wire_2d(pos, x, y, radius, 8);
+ immUnbindProgram();
- uint pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ GPU_matrix_pop_projection();
+ }
+ else if (t->spacetype == SPACE_NODE) {
+ ARegion *region = CTX_wm_region(C);
+ TransSnapPoint *p;
+ float size;
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ size = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE);
- for (p = t->tsnap.points.first; p; p = p->next) {
- if (p == t->tsnap.selectedPoint) {
- immUniformColor4ubv(selectedCol);
- }
- else {
- immUniformColor4ubv(col);
- }
+ GPU_blend(GPU_BLEND_ALPHA);
- ED_node_draw_snap(&region->v2d, p->co, size, 0, pos);
- }
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- if (t->tsnap.status & POINT_INIT) {
- immUniformColor4ubv(activeCol);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
- ED_node_draw_snap(&region->v2d, t->tsnap.snapPoint, size, t->tsnap.snapNodeBorder, pos);
+ for (p = t->tsnap.points.first; p; p = p->next) {
+ if (p == t->tsnap.selectedPoint) {
+ immUniformColor4ubv(selectedCol);
+ }
+ else {
+ immUniformColor4ubv(col);
}
- immUnbindProgram();
+ ED_node_draw_snap(&region->v2d, p->co, size, 0, pos);
+ }
- GPU_blend(GPU_BLEND_NONE);
+ if (t->tsnap.status & POINT_INIT) {
+ immUniformColor4ubv(activeCol);
+
+ ED_node_draw_snap(&region->v2d, t->tsnap.snapPoint, size, t->tsnap.snapNodeBorder, pos);
}
+
+ immUnbindProgram();
+
+ GPU_blend(GPU_BLEND_NONE);
}
else if (t->spacetype == SPACE_SEQ) {
- if (validSnap(t)) {
- const ARegion *region = CTX_wm_region(C);
- GPU_blend(GPU_BLEND_ALPHA);
- uint pos = GPU_vertformat_attr_add(
- immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
- UI_GetThemeColor3ubv(TH_SEQ_ACTIVE, col);
- col[3] = 128;
- immUniformColor4ubv(col);
- float pixelx = BLI_rctf_size_x(&region->v2d.cur) / BLI_rcti_size_x(&region->v2d.mask);
- immRectf(pos,
- t->tsnap.snapPoint[0] - pixelx,
- region->v2d.cur.ymax,
- t->tsnap.snapPoint[0] + pixelx,
- region->v2d.cur.ymin);
- immUnbindProgram();
- GPU_blend(GPU_BLEND_NONE);
- }
+ const ARegion *region = CTX_wm_region(C);
+ GPU_blend(GPU_BLEND_ALPHA);
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+ immUniformColor4ubv(col);
+ float pixelx = BLI_rctf_size_x(&region->v2d.cur) / BLI_rcti_size_x(&region->v2d.mask);
+ immRectf(pos,
+ t->tsnap.snapPoint[0] - pixelx,
+ region->v2d.cur.ymax,
+ t->tsnap.snapPoint[0] + pixelx,
+ region->v2d.cur.ymin);
+ immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
}
}
@@ -729,8 +718,8 @@ static eSnapMode snap_mode_from_spacetype(TransInfo *t)
static eSnapTargetSelect snap_target_select_from_spacetype(TransInfo *t)
{
- ViewLayer *view_layer = t->view_layer;
- Base *base_act = view_layer->basact;
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Base *base_act = BKE_view_layer_active_base_get(t->view_layer);
eSnapTargetSelect ret = SCE_SNAP_TARGET_ALL;
@@ -813,7 +802,7 @@ static void initSnappingMode(TransInfo *t)
t->tsnap.use_backface_culling = snap_use_backface_culling(t);
t->tsnap.object_context = ED_transform_snap_object_context_create(t->scene, 0);
- if (t->data_type == TC_MESH_VERTS) {
+ if (t->data_type == &TransConvertType_Mesh) {
/* Ignore elements being transformed. */
ED_transform_snap_object_context_set_editmesh_callbacks(
t->tsnap.object_context,
@@ -967,7 +956,8 @@ static void setSnappingCallback(TransInfo *t)
}
else if (t->spacetype == SPACE_IMAGE) {
SpaceImage *sima = t->area->spacedata.first;
- Object *obact = t->view_layer->basact ? t->view_layer->basact->object : NULL;
+ BKE_view_layer_synced_ensure(t->scene, t->view_layer);
+ Object *obact = BKE_view_layer_active_object_get(t->view_layer);
const bool is_uv_editor = sima->mode == SI_MODE_UV;
const bool has_edit_object = obact && BKE_object_is_in_editmode(obact);
@@ -1152,7 +1142,7 @@ static void snap_calc_uv_fn(TransInfo *t, float *UNUSED(vec))
if (t->tsnap.mode & SCE_SNAP_MODE_VERTEX) {
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- t->view_layer, NULL, &objects_len);
+ t->scene, t->view_layer, NULL, &objects_len);
float dist_sq = square_f((float)SNAP_MIN_DISTANCE);
if (ED_uvedit_nearest_uv_multi(&t->region->v2d,
@@ -1252,7 +1242,7 @@ static void snap_target_grid_ensure(TransInfo *t)
{
/* Only need to calculate once. */
if ((t->tsnap.status & TARGET_GRID_INIT) == 0) {
- if (t->data_type == TC_CURSOR_VIEW3D) {
+ if (t->data_type == &TransConvertType_Cursor3D) {
/* Use a fallback when transforming the cursor.
* In this case the center is _not_ derived from the cursor which is being transformed. */
copy_v3_v3(t->tsnap.snapTargetGrid, TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->data->iloc);
diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc
index 479214ee2d3..6d643ae7180 100644
--- a/source/blender/editors/transform/transform_snap_object.cc
+++ b/source/blender/editors/transform/transform_snap_object.cc
@@ -47,6 +47,7 @@
using blender::float3;
using blender::float4x4;
using blender::Map;
+using blender::Span;
/* -------------------------------------------------------------------- */
/** \name Internal Data Types
@@ -243,6 +244,11 @@ static SnapData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx,
SnapData_Mesh *sod;
bool init = false;
+ const Span<MVert> verts = me_eval->verts();
+ const Span<MEdge> edges = me_eval->edges();
+ const Span<MPoly> polys = me_eval->polys();
+ const Span<MLoop> loops = me_eval->loops();
+
if (std::unique_ptr<SnapData_Mesh> *sod_p = sctx->mesh_caches.lookup_ptr(ob_eval)) {
sod = sod_p->get();
bool is_dirty = false;
@@ -264,16 +270,16 @@ static SnapData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx,
else if (sod->treedata_mesh.looptri != me_eval->runtime.looptris.array) {
is_dirty = true;
}
- else if (sod->treedata_mesh.vert != me_eval->mvert) {
+ else if (sod->treedata_mesh.vert != verts.data()) {
is_dirty = true;
}
- else if (sod->treedata_mesh.loop != me_eval->mloop) {
+ else if (sod->treedata_mesh.loop != loops.data()) {
is_dirty = true;
}
- else if (sod->treedata_mesh.edge != me_eval->medge) {
+ else if (sod->treedata_mesh.edge != edges.data()) {
is_dirty = true;
}
- else if (sod->poly != me_eval->mpoly) {
+ else if (sod->poly != polys.data()) {
is_dirty = true;
}
@@ -303,16 +309,16 @@ static SnapData_Mesh *snap_object_data_mesh_get(SnapObjectContext *sctx,
use_hide ? BVHTREE_FROM_LOOPTRI_NO_HIDDEN : BVHTREE_FROM_LOOPTRI,
4);
- BLI_assert(sod->treedata_mesh.vert == me_eval->mvert);
- BLI_assert(!me_eval->mvert || sod->treedata_mesh.vert_normals);
- BLI_assert(sod->treedata_mesh.loop == me_eval->mloop);
- BLI_assert(!me_eval->mpoly || sod->treedata_mesh.looptri);
+ BLI_assert(sod->treedata_mesh.vert == verts.data());
+ BLI_assert(!verts.data() || sod->treedata_mesh.vert_normals);
+ BLI_assert(sod->treedata_mesh.loop == loops.data());
+ BLI_assert(!polys.data() || sod->treedata_mesh.looptri);
sod->has_looptris = sod->treedata_mesh.tree != nullptr;
/* Required for snapping with occlusion. */
- sod->treedata_mesh.edge = me_eval->medge;
- sod->poly = me_eval->mpoly;
+ sod->treedata_mesh.edge = edges.data();
+ sod->poly = polys.data();
/* Start assuming that it has each of these element types. */
sod->has_loose_edge = true;
@@ -498,7 +504,8 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx,
const bool is_edited = (base->object->mode == OB_MODE_EDIT);
const bool is_selectable = (base->flag & BASE_SELECTABLE);
/* Get attributes of state. */
- const bool is_in_object_mode = (base_act == NULL) || (base_act->object->mode == OB_MODE_OBJECT);
+ const bool is_in_object_mode = (base_act == nullptr) ||
+ (base_act->object->mode == OB_MODE_OBJECT);
if (is_in_object_mode) {
/* Handle target selection options that make sense for object mode. */
@@ -536,11 +543,13 @@ static void iter_snap_objects(SnapObjectContext *sctx,
IterSnapObjsCallback sob_callback,
void *data)
{
+ Scene *scene = DEG_get_input_scene(sctx->runtime.depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph);
const eSnapTargetSelect snap_target_select = params->snap_target_select;
- Base *base_act = view_layer->basact;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *base_act = BKE_view_layer_active_base_get(view_layer);
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
if (!snap_object_is_snappable(sctx, snap_target_select, base_act, base)) {
continue;
}
@@ -563,7 +572,7 @@ static void iter_snap_objects(SnapObjectContext *sctx,
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Ray Cast Funcs
+/** \name Ray Cast Functions
* \{ */
/* Store all ray-hits
@@ -1186,7 +1195,7 @@ static bool raycastObjects(SnapObjectContext *sctx,
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Surface Snap Funcs
+/** \name Surface Snap Functions
* \{ */
struct NearestWorldObjUserData {
@@ -3401,8 +3410,8 @@ static eSnapMode transform_snap_context_project_view3d_mixed_impl(SnapObjectCont
bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d);
- /* Note: if both face raycast and face nearest are enabled, first find result of nearest, then
- * override with raycast. */
+ /* NOTE: if both face ray-cast and face nearest are enabled, first find result of nearest, then
+ * override with ray-cast. */
if ((snap_to_flag & SCE_SNAP_MODE_FACE_NEAREST) && !has_hit) {
has_hit = nearestWorldObjects(
sctx, params, init_co, prev_co, loc, no, &index, &ob_eval, obmat);
diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c
index 5ac526b1e91..06d9bb05206 100644
--- a/source/blender/editors/transform/transform_snap_sequencer.c
+++ b/source/blender/editors/transform/transform_snap_sequencer.c
@@ -28,6 +28,7 @@
#include "SEQ_time.h"
#include "transform.h"
+#include "transform_convert.h"
#include "transform_snap.h"
typedef struct TransSeqSnapData {
@@ -244,7 +245,7 @@ static int seq_snap_threshold_get_frame_distance(const TransInfo *t)
TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t)
{
- if (t->data_type == TC_SEQ_IMAGE_DATA) {
+ if (t->data_type == &TransConvertType_SequencerImage) {
return NULL;
}
@@ -375,7 +376,7 @@ bool ED_transform_snap_sequencer_to_closest_strip_calc(Scene *scene,
t.scene = scene;
t.region = region;
t.values[0] = 0;
- t.data_type = TC_SEQ_DATA;
+ t.data_type = &TransConvertType_Sequencer;
t.tsnap.mode = SEQ_tool_settings_snap_mode_get(scene);
*r_snap_distance = transform_snap_sequencer_to_closest_strip_ex(&t, frame_1, frame_2);
diff --git a/source/blender/editors/undo/CMakeLists.txt b/source/blender/editors/undo/CMakeLists.txt
index 284b725cdf0..271d05e9c04 100644
--- a/source/blender/editors/undo/CMakeLists.txt
+++ b/source/blender/editors/undo/CMakeLists.txt
@@ -11,6 +11,7 @@ set(INC
../../windowmanager
../../../../intern/clog
../../../../intern/guardedalloc
+ ../../bmesh
)
set(SRC
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index ce14e6b180f..42563cb8f83 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -269,7 +269,7 @@ static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportLis
CLOG_INFO(&LOG, 1, "direction=%s", (step == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO");
- /* TODO(campbell): undo_system: use undo system */
+ /* TODO(@campbellbarton): undo_system: use undo system */
/* grease pencil can be can be used in plenty of spaces, so check it first */
/* FIXME: This gpencil undo effectively only supports the one step undo/redo, undo based on name
* or index is fully not implemented.
@@ -433,9 +433,11 @@ bool ED_undo_is_memfile_compatible(const bContext *C)
{
/* Some modes don't co-exist with memfile undo, disable their use: T60593
* (this matches 2.7x behavior). */
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
if (view_layer != NULL) {
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact != NULL) {
if (obact->mode & OB_MODE_EDIT) {
return false;
@@ -447,9 +449,11 @@ bool ED_undo_is_memfile_compatible(const bContext *C)
bool ED_undo_is_legacy_compatible_for_property(struct bContext *C, ID *id)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
if (view_layer != NULL) {
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact != NULL) {
if (obact->mode & OB_MODE_ALL_PAINT) {
/* Don't store property changes when painting
@@ -800,7 +804,8 @@ void ED_OT_undo_history(wmOperatorType *ot)
void ED_undo_object_set_active_or_warn(
Scene *scene, ViewLayer *view_layer, Object *ob, const char *info, CLG_LogRef *log)
{
- Object *ob_prev = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob_prev = BKE_view_layer_active_object_get(view_layer);
if (ob_prev != ob) {
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base != NULL) {
@@ -820,15 +825,15 @@ void ED_undo_object_editmode_restore_helper(struct bContext *C,
uint object_array_stride)
{
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint bases_len = 0;
/* Don't request unique data because we want to de-select objects when exiting edit-mode
* for that to be done on all objects we can't skip ones that share data. */
- Base **bases = ED_undo_editmode_bases_from_view_layer(view_layer, &bases_len);
+ Base **bases = ED_undo_editmode_bases_from_view_layer(scene, view_layer, &bases_len);
for (uint i = 0; i < bases_len; i++) {
((ID *)bases[i]->object->data)->tag |= LIB_TAG_DOIT;
}
- Scene *scene = CTX_data_scene(C);
Object **ob_p = object_array;
for (uint i = 0; i < object_array_len; i++, ob_p = POINTER_OFFSET(ob_p, object_array_stride)) {
Object *obedit = *ob_p;
@@ -859,11 +864,14 @@ void ED_undo_object_editmode_restore_helper(struct bContext *C,
* and local collections may be used.
* \{ */
-static int undo_editmode_objects_from_view_layer_prepare(ViewLayer *view_layer, Object *obact)
+static int undo_editmode_objects_from_view_layer_prepare(const Scene *scene,
+ ViewLayer *view_layer,
+ Object *obact)
{
const short object_type = obact->type;
-
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ ListBase *object_bases = BKE_view_layer_object_bases_get(view_layer);
+ LISTBASE_FOREACH (Base *, base, object_bases) {
Object *ob = base->object;
if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) {
ID *id = ob->data;
@@ -872,7 +880,7 @@ static int undo_editmode_objects_from_view_layer_prepare(ViewLayer *view_layer,
}
int len = 0;
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ LISTBASE_FOREACH (Base *, base, object_bases) {
Object *ob = base->object;
if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) {
ID *id = ob->data;
@@ -885,19 +893,23 @@ static int undo_editmode_objects_from_view_layer_prepare(ViewLayer *view_layer,
return len;
}
-Object **ED_undo_editmode_objects_from_view_layer(ViewLayer *view_layer, uint *r_len)
+Object **ED_undo_editmode_objects_from_view_layer(const Scene *scene,
+ ViewLayer *view_layer,
+ uint *r_len)
{
- Base *baseact = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *baseact = BKE_view_layer_active_base_get(view_layer);
if ((baseact == NULL) || (baseact->object->mode & OB_MODE_EDIT) == 0) {
return MEM_mallocN(0, __func__);
}
- const int len = undo_editmode_objects_from_view_layer_prepare(view_layer, baseact->object);
+ const int len = undo_editmode_objects_from_view_layer_prepare(
+ scene, view_layer, baseact->object);
const short object_type = baseact->object->type;
int i = 0;
Object **objects = MEM_malloc_arrayN(len, sizeof(*objects), __func__);
/* Base iteration, starting with the active-base to ensure it's the first item in the array.
* Looping over the active-base twice is OK as the tag check prevents it being handled twice. */
- for (Base *base = baseact, *base_next = FIRSTBASE(view_layer); base;
+ for (Base *base = baseact, *base_next = BKE_view_layer_object_bases_get(view_layer)->first; base;
base = base_next, base_next = base_next ? base_next->next : NULL) {
Object *ob = base->object;
if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) {
@@ -914,19 +926,25 @@ Object **ED_undo_editmode_objects_from_view_layer(ViewLayer *view_layer, uint *r
return objects;
}
-Base **ED_undo_editmode_bases_from_view_layer(ViewLayer *view_layer, uint *r_len)
+Base **ED_undo_editmode_bases_from_view_layer(const Scene *scene,
+ ViewLayer *view_layer,
+ uint *r_len)
{
- Base *baseact = BASACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Base *baseact = BKE_view_layer_active_base_get(view_layer);
if ((baseact == NULL) || (baseact->object->mode & OB_MODE_EDIT) == 0) {
return MEM_mallocN(0, __func__);
}
- const int len = undo_editmode_objects_from_view_layer_prepare(view_layer, baseact->object);
+ const int len = undo_editmode_objects_from_view_layer_prepare(
+ scene, view_layer, baseact->object);
const short object_type = baseact->object->type;
int i = 0;
Base **base_array = MEM_malloc_arrayN(len, sizeof(*base_array), __func__);
/* Base iteration, starting with the active-base to ensure it's the first item in the array.
* Looping over the active-base twice is OK as the tag check prevents it being handled twice. */
- for (Base *base = BASACT(view_layer), *base_next = FIRSTBASE(view_layer); base;
+ for (Base *base = BKE_view_layer_active_base_get(view_layer),
+ *base_next = BKE_view_layer_object_bases_get(view_layer)->first;
+ base;
base = base_next, base_next = base_next ? base_next->next : NULL) {
Object *ob = base->object;
if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) {
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index cdfe40c7d35..a9e6adc6e60 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -16,7 +16,6 @@ set(INC
../../sequencer
../../windowmanager
../../../../intern/clog
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@@ -83,7 +82,6 @@ set(SRC
../include/ED_transform.h
../include/ED_transform_snap_object_context.h
../include/ED_transverts.h
- ../include/ED_types.h
../include/ED_undo.h
../include/ED_userpref.h
../include/ED_util.h
diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c
index 1b6a3efe19c..7ec3d3c1ef4 100644
--- a/source/blender/editors/util/ed_draw.c
+++ b/source/blender/editors/util/ed_draw.c
@@ -95,7 +95,7 @@ static void draw_overshoot_triangle(const uint8_t color[4],
{
const uint shdr_pos_2d = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
GPU_blend(GPU_BLEND_ALPHA);
GPU_polygon_smooth(true);
immUniformColor3ubvAlpha(color, 225);
@@ -370,11 +370,13 @@ tSlider *ED_slider_create(struct bContext *C)
slider->factor = 0.5;
/* Add draw callback. Always in header. */
- LISTBASE_FOREACH (ARegion *, region, &slider->area->regionbase) {
- if (region->regiontype == RGN_TYPE_HEADER) {
- slider->region_header = region;
- slider->draw_handle = ED_region_draw_cb_activate(
- region->type, slider_draw, slider, REGION_DRAW_POST_PIXEL);
+ if (slider->area) {
+ LISTBASE_FOREACH (ARegion *, region, &slider->area->regionbase) {
+ if (region->regiontype == RGN_TYPE_HEADER) {
+ slider->region_header = region;
+ slider->draw_handle = ED_region_draw_cb_activate(
+ region->type, slider_draw, slider, REGION_DRAW_POST_PIXEL);
+ }
}
}
@@ -465,7 +467,9 @@ void ED_slider_status_string_get(const struct tSlider *slider,
void ED_slider_destroy(struct bContext *C, tSlider *slider)
{
/* Remove draw callback. */
- ED_region_draw_cb_exit(slider->region_header->type, slider->draw_handle);
+ if (slider->draw_handle) {
+ ED_region_draw_cb_exit(slider->region_header->type, slider->draw_handle);
+ }
ED_area_status_text(slider->area, NULL);
ED_workspace_status_text(C, NULL);
MEM_freeN(slider);
@@ -512,7 +516,7 @@ void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *region, void *arg_
GPU_line_width(1.0f);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -778,7 +782,7 @@ void ED_region_image_metadata_draw(
/* draw top box */
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_METADATA_BG);
immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
immUnbindProgram();
@@ -803,7 +807,7 @@ void ED_region_image_metadata_draw(
/* draw top box */
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformThemeColor(TH_METADATA_BG);
immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
immUnbindProgram();
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 3cfe6bd61a4..12e77c6ef00 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -54,22 +54,19 @@
#include "WM_api.h"
#include "WM_types.h"
-/* ********* general editor util funcs, not BKE stuff please! ********* */
+/* ********* general editor util functions, not BKE stuff please! ********* */
void ED_editors_init_for_undo(Main *bmain)
{
wmWindowManager *wm = bmain->wm.first;
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- Base *base = BASACT(view_layer);
- if (base != NULL) {
- Object *ob = base->object;
- if (ob->mode & OB_MODE_TEXTURE_PAINT) {
- Scene *scene = WM_window_get_active_scene(win);
-
- BKE_texpaint_slots_refresh_object(scene, ob);
- ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
- }
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
+ if (ob && (ob->mode & OB_MODE_TEXTURE_PAINT)) {
+ BKE_texpaint_slots_refresh_object(scene, ob);
+ ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
}
}
}
@@ -176,7 +173,7 @@ void ED_editors_init(bContext *C)
}
}
else {
- /* TODO(campbell): avoid operator calls. */
+ /* TODO(@campbellbarton): avoid operator calls. */
if (obact == ob) {
ED_object_mode_set(C, mode);
}
@@ -377,7 +374,7 @@ void unpack_menu(bContext *C,
char local_name[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
BLI_split_file_part(abs_name, fi, sizeof(fi));
- BLI_snprintf(local_name, sizeof(local_name), "//%s/%s", folder, fi);
+ BLI_path_join(local_name, sizeof(local_name), "//", folder, fi, NULL);
if (!STREQ(abs_name, local_name)) {
switch (BKE_packedfile_compare_to_file(blendfile_path, local_name, pf)) {
case PF_CMP_NOFILE:
diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c
index fc5fb9f9c28..f222f93d2b6 100644
--- a/source/blender/editors/util/ed_util_imbuf.c
+++ b/source/blender/editors/util/ed_util_imbuf.c
@@ -428,10 +428,10 @@ void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info)
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
const float color[3] = {1, 1, 1};
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fv(color);
- /* TODO(campbell): lock to pixels. */
+ /* TODO(@campbellbarton): lock to pixels. */
rctf sample_rect_fl;
BLI_rctf_init_pt_radius(
&sample_rect_fl,
diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c
index be6ac6e13e6..60cbc2a2df6 100644
--- a/source/blender/editors/util/numinput.c
+++ b/source/blender/editors/util/numinput.c
@@ -311,6 +311,7 @@ static bool editstr_is_simple_numinput(const char ascii)
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
{
const char *utf8_buf = NULL;
+ const char event_ascii = WM_event_utf8_to_ascii(event);
char ascii[2] = {'\0', '\0'};
bool updated = false;
short idx = n->idx, idx_max = n->idx_max;
@@ -321,8 +322,8 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
if (U.flag & USER_FLAG_NUMINPUT_ADVANCED)
#endif
{
- if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event->ascii != '\0') &&
- strchr("01234567890@%^&*-+/{}()[]<>.|", event->ascii)) {
+ if (((event->modifier & (KM_CTRL | KM_ALT)) == 0) && (event_ascii != '\0') &&
+ strchr("01234567890@%^&*-+/{}()[]<>.|", event_ascii)) {
if (!(n->flag & NUM_EDIT_FULL)) {
n->flag |= NUM_EDITED;
n->flag |= NUM_EDIT_FULL;
@@ -333,7 +334,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
#ifdef USE_FAKE_EDIT
/* XXX Hack around keyboards without direct access to '=' nor '*'... */
- if (ELEM(event->ascii, '=', '*')) {
+ if (ELEM(event_ascii, '=', '*')) {
if (!(n->flag & NUM_EDIT_FULL)) {
n->flag |= NUM_EDIT_FULL;
n->val_flag[idx] |= NUM_EDITED;
@@ -357,7 +358,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
else {
/* might be a char too... */
utf8_buf = event->utf8_buf;
- ascii[0] = event->ascii;
+ ascii[0] = event_ascii;
}
break;
case EVT_BACKSPACEKEY:
@@ -523,9 +524,9 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
break;
}
- if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) {
+ if (!updated && !utf8_buf && event->utf8_buf[0]) {
utf8_buf = event->utf8_buf;
- ascii[0] = event->ascii;
+ ascii[0] = event_ascii;
}
/* Up to this point, if we have a ctrl modifier, skip.
diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c
index 660afa4c3d7..b29afdb5a9f 100644
--- a/source/blender/editors/util/select_utils.c
+++ b/source/blender/editors/util/select_utils.c
@@ -10,6 +10,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_windowmanager_types.h"
#include "RNA_access.h"
@@ -161,18 +163,18 @@ const char *ED_select_pick_get_name(wmOperatorType *UNUSED(ot), PointerRNA *ptr)
ED_select_pick_params_from_operator(ptr, &params);
switch (params.sel_op) {
case SEL_OP_ADD:
- return "Select (Extend)";
+ return CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select (Extend)");
case SEL_OP_SUB:
- return "Select (Deselect)";
+ return CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select (Deselect)");
case SEL_OP_XOR:
- return "Select (Toggle)";
+ return CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select (Toggle)");
case SEL_OP_AND:
BLI_assert_unreachable();
ATTR_FALLTHROUGH;
case SEL_OP_SET:
break;
}
- return "Select";
+ return CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select");
}
const char *ED_select_circle_get_name(wmOperatorType *UNUSED(ot), PointerRNA *ptr)
@@ -181,9 +183,9 @@ const char *ED_select_circle_get_name(wmOperatorType *UNUSED(ot), PointerRNA *pt
const eSelectOp sel_op = RNA_enum_get(ptr, "mode");
switch (sel_op) {
case SEL_OP_ADD:
- return "Circle Select (Extend)";
+ return CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Circle Select (Extend)");
case SEL_OP_SUB:
- return "Circle Select (Deselect)";
+ return CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Circle Select (Deselect)");
case SEL_OP_XOR:
ATTR_FALLTHROUGH;
case SEL_OP_AND:
@@ -192,7 +194,7 @@ const char *ED_select_circle_get_name(wmOperatorType *UNUSED(ot), PointerRNA *pt
case SEL_OP_SET:
break;
}
- return "Circle Select";
+ return CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Circle Select");
}
/** \} */
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt
index 761e7cd091e..4574c745d93 100644
--- a/source/blender/editors/uvedit/CMakeLists.txt
+++ b/source/blender/editors/uvedit/CMakeLists.txt
@@ -13,7 +13,6 @@ set(INC
../../makesrna
../../windowmanager
../../../../intern/eigen
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
@@ -23,7 +22,7 @@ set(INC
set(SRC
uvedit_buttons.c
uvedit_draw.c
- uvedit_islands.c
+ uvedit_islands.cc
uvedit_ops.c
uvedit_path.c
uvedit_rip.c
diff --git a/source/blender/editors/uvedit/uvedit_buttons.c b/source/blender/editors/uvedit/uvedit_buttons.c
index 6192ae56d65..10368f7d43f 100644
--- a/source/blender/editors/uvedit/uvedit_buttons.c
+++ b/source/blender/editors/uvedit/uvedit_buttons.c
@@ -123,7 +123,7 @@ static void uvedit_vertex_buttons(const bContext *C, uiBlock *block)
int imx, imy, step, digits;
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);
+ scene, CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);
ED_space_image_get_size(sima, &imx, &imy);
@@ -211,7 +211,7 @@ static void do_uvedit_vertex(bContext *C, void *UNUSED(arg), int event)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);
+ scene, CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);
ED_space_image_get_size(sima, &imx, &imy);
uvedit_center(scene, objects, objects_len, center);
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index 141b59e0355..9deeb27b259 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -39,7 +39,7 @@ void ED_image_draw_cursor(ARegion *region, const float cursor[2])
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 04128cf378c..434bfbc64f9 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -14,9 +14,6 @@ struct Scene;
struct SpaceImage;
struct wmOperatorType;
-/* geometric utilities */
-void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len);
-
/* find nearest */
typedef struct UvNearestHit {
diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.cc
index e1752ae5a29..42415be656a 100644
--- a/source/blender/editors/uvedit/uvedit_islands.c
+++ b/source/blender/editors/uvedit/uvedit_islands.cc
@@ -46,7 +46,7 @@ static void bm_face_uv_scale_y(BMFace *f, const float scale_y, const int cd_loop
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ MLoopUV *luv = static_cast<MLoopUV *>(BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset));
luv->uv[1] *= scale_y;
} while ((l_iter = l_iter->next) != l_first);
}
@@ -61,7 +61,7 @@ static void bm_face_uv_translate_and_scale_around_pivot(BMFace *f,
BMLoop *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ MLoopUV *luv = static_cast<MLoopUV *>(BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset));
for (int i = 0; i < 2; i++) {
luv->uv[i] = offset[i] + (((luv->uv[i] - pivot[i]) * scale[i]) + pivot[i]);
}
@@ -76,9 +76,10 @@ static void bm_face_uv_translate_and_scale_around_pivot(BMFace *f,
static void bm_face_array_calc_bounds(BMFace **faces,
int faces_len,
- const uint cd_loop_uv_offset,
+ const int cd_loop_uv_offset,
rctf *r_bounds_rect)
{
+ BLI_assert(cd_loop_uv_offset >= 0);
float bounds_min[2], bounds_max[2];
INIT_MINMAX2(bounds_min, bounds_max);
for (int i = 0; i < faces_len; i++) {
@@ -96,8 +97,9 @@ static void bm_face_array_calc_bounds(BMFace **faces,
* without duplicating coordinates for loops that share a vertex.
*/
static float (*bm_face_array_calc_unique_uv_coords(
- BMFace **faces, int faces_len, const uint cd_loop_uv_offset, int *r_coords_len))[2]
+ BMFace **faces, int faces_len, const int cd_loop_uv_offset, int *r_coords_len))[2]
{
+ BLI_assert(cd_loop_uv_offset >= 0);
int coords_len_alloc = 0;
for (int i = 0; i < faces_len; i++) {
BMFace *f = faces[i];
@@ -109,7 +111,8 @@ static float (*bm_face_array_calc_unique_uv_coords(
coords_len_alloc += f->len;
}
- float(*coords)[2] = MEM_mallocN(sizeof(*coords) * coords_len_alloc, __func__);
+ float(*coords)[2] = static_cast<float(*)[2]>(
+ MEM_mallocN(sizeof(*coords) * coords_len_alloc, __func__));
int coords_len = 0;
for (int i = 0; i < faces_len; i++) {
@@ -123,7 +126,8 @@ static float (*bm_face_array_calc_unique_uv_coords(
}
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ const MLoopUV *luv = static_cast<const MLoopUV *>(
+ BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset));
copy_v2_v2(coords[coords_len++], luv->uv);
/* Un tag all connected so we don't add them twice.
@@ -138,7 +142,8 @@ static float (*bm_face_array_calc_unique_uv_coords(
do {
if (l_radial->v == l_iter->v) {
if (BM_elem_flag_test(l_radial, BM_ELEM_TAG)) {
- const MLoopUV *luv_radial = BM_ELEM_CD_GET_VOID_P(l_radial, cd_loop_uv_offset);
+ const MLoopUV *luv_radial = static_cast<const MLoopUV *>(
+ BM_ELEM_CD_GET_VOID_P(l_radial, cd_loop_uv_offset));
if (equals_v2v2(luv->uv, luv_radial->uv)) {
/* Don't add this UV when met in another face in `faces`. */
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
@@ -150,7 +155,6 @@ static float (*bm_face_array_calc_unique_uv_coords(
} while ((e = BM_DISK_EDGE_NEXT(e, v_pivot)) != e_first);
} while ((l_iter = l_iter->next) != l_first);
}
- coords = MEM_reallocN(coords, sizeof(*coords) * coords_len);
*r_coords_len = coords_len;
return coords;
}
@@ -164,7 +168,7 @@ static float (*bm_face_array_calc_unique_uv_coords(
static void bm_face_array_uv_rotate_fit_aabb(BMFace **faces,
int faces_len,
int align_to_axis,
- const uint cd_loop_uv_offset)
+ const int cd_loop_uv_offset)
{
/* Calculate unique coordinates since calculating a convex hull can be an expensive operation. */
int coords_len;
@@ -209,7 +213,7 @@ static void bm_face_array_uv_rotate_fit_aabb(BMFace **faces,
static void bm_face_array_uv_scale_y(BMFace **faces,
int faces_len,
const float scale_y,
- const uint cd_loop_uv_offset)
+ const int cd_loop_uv_offset)
{
for (int i = 0; i < faces_len; i++) {
BMFace *f = faces[i];
@@ -256,16 +260,11 @@ bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const floa
* Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number.
*/
static float uv_nearest_image_tile_distance(const Image *image,
- float coords[2],
+ const float coords[2],
float nearest_tile_co[2])
{
- int nearest_image_tile_index = BKE_image_find_nearest_tile(image, coords);
- if (nearest_image_tile_index == -1) {
- nearest_image_tile_index = 1001;
- }
+ BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co);
- nearest_tile_co[0] = (nearest_image_tile_index - 1001) % 10;
- nearest_tile_co[1] = (nearest_image_tile_index - 1001) / 10;
/* Add 0.5 to get tile center coordinates. */
float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]};
add_v2_fl(nearest_tile_center_co, 0.5f);
@@ -313,31 +312,16 @@ static float uv_nearest_grid_tile_distance(const int udim_grid[2],
/* -------------------------------------------------------------------- */
/** \name Calculate UV Islands
- *
- * \note Currently this is a private API/type, it could be made public.
* \{ */
-struct FaceIsland {
- struct FaceIsland *next, *prev;
- BMFace **faces;
- int faces_len;
- rctf bounds_rect;
- /**
- * \note While this is duplicate information,
- * it allows islands from multiple meshes to be stored in the same list.
- */
- uint cd_loop_uv_offset;
- float aspect_y;
-};
-
struct SharedUVLoopData {
- uint cd_loop_uv_offset;
+ int cd_loop_uv_offset;
bool use_seams;
};
static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, void *user_data)
{
- const struct SharedUVLoopData *data = user_data;
+ const struct SharedUVLoopData *data = static_cast<const struct SharedUVLoopData *>(user_data);
if (data->use_seams) {
if (BM_elem_flag_test(l_a->e, BM_ELEM_SEAM)) {
@@ -351,24 +335,21 @@ static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, v
/**
* Calculate islands and add them to \a island_list returning the number of items added.
*/
-static int bm_mesh_calc_uv_islands(const Scene *scene,
- BMesh *bm,
- ListBase *island_list,
- const bool only_selected_faces,
- const bool only_selected_uvs,
- const bool use_seams,
- const float aspect_y,
- const uint cd_loop_uv_offset)
+int bm_mesh_calc_uv_islands(const Scene *scene,
+ BMesh *bm,
+ ListBase *island_list,
+ const bool only_selected_faces,
+ const bool only_selected_uvs,
+ const bool use_seams,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
{
+ BLI_assert(cd_loop_uv_offset >= 0);
int island_added = 0;
BM_mesh_elem_table_ensure(bm, BM_FACE);
- struct SharedUVLoopData user_data = {
- .cd_loop_uv_offset = cd_loop_uv_offset,
- .use_seams = use_seams,
- };
-
- int *groups_array = MEM_mallocN(sizeof(*groups_array) * (size_t)bm->totface, __func__);
+ int *groups_array = static_cast<int *>(
+ MEM_mallocN(sizeof(*groups_array) * (size_t)bm->totface, __func__));
int(*group_index)[2];
@@ -393,6 +374,10 @@ static int bm_mesh_calc_uv_islands(const Scene *scene,
}
}
+ struct SharedUVLoopData user_data = {0};
+ user_data.cd_loop_uv_offset = cd_loop_uv_offset;
+ user_data.use_seams = use_seams;
+
const int group_len = BM_mesh_calc_face_groups(bm,
groups_array,
&group_index,
@@ -405,7 +390,7 @@ static int bm_mesh_calc_uv_islands(const Scene *scene,
for (int i = 0; i < group_len; i++) {
const int faces_start = group_index[i][0];
const int faces_len = group_index[i][1];
- BMFace **faces = MEM_mallocN(sizeof(*faces) * faces_len, __func__);
+ BMFace **faces = static_cast<BMFace **>(MEM_mallocN(sizeof(*faces) * faces_len, __func__));
float bounds_min[2], bounds_max[2];
INIT_MINMAX2(bounds_min, bounds_max);
@@ -414,7 +399,8 @@ static int bm_mesh_calc_uv_islands(const Scene *scene,
faces[j] = BM_face_at_index(bm, groups_array[faces_start + j]);
}
- struct FaceIsland *island = MEM_callocN(sizeof(*island), __func__);
+ struct FaceIsland *island = static_cast<struct FaceIsland *>(
+ MEM_callocN(sizeof(*island), __func__));
island->faces = faces;
island->faces_len = faces_len;
island->cd_loop_uv_offset = cd_loop_uv_offset;
@@ -483,9 +469,10 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
float margin = scene->toolsettings->uvcalc_margin;
double area = 0.0f;
- struct FaceIsland **island_array = MEM_mallocN(sizeof(*island_array) * island_list_len,
- __func__);
- BoxPack *boxarray = MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__);
+ struct FaceIsland **island_array = static_cast<struct FaceIsland **>(
+ MEM_mallocN(sizeof(*island_array) * island_list_len, __func__));
+ BoxPack *boxarray = static_cast<BoxPack *>(
+ MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__));
int index;
/* Coordinates of bounding box containing all selected UVs. */
@@ -637,7 +624,7 @@ void ED_uvedit_pack_islands_multi(const Scene *scene,
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
- DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GEOM | ND_DATA, obedit->data);
}
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 4844ff22b68..5e2d9097abd 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -189,15 +189,6 @@ void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit)
/** \name Geometric Utilities
* \{ */
-void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len)
-{
- int i;
- for (i = 0; i < len; i++) {
- uv[i][0] = uv_orig[i][0] * aspx;
- uv[i][1] = uv_orig[i][1] * aspy;
- }
-}
-
bool ED_uvedit_minmax_multi(
const Scene *scene, Object **objects_edit, uint objects_len, float r_min[2], float r_max[2])
{
@@ -329,7 +320,7 @@ bool ED_uvedit_center_from_pivot_ex(SpaceImage *sima,
if (r_has_select != NULL) {
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
*r_has_select = uvedit_select_is_any_selected_multi(scene, objects, objects_len);
MEM_freeN(objects);
}
@@ -338,7 +329,7 @@ bool ED_uvedit_center_from_pivot_ex(SpaceImage *sima,
default: {
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
changed = ED_uvedit_center_multi(scene, objects, objects_len, r_center, mode);
MEM_freeN(objects);
if (r_has_select != NULL) {
@@ -544,20 +535,17 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool)
return false;
}
- UvElementMap *element_map = BM_uv_element_map_create(bm, scene, false, true, false, true);
+ UvElementMap *element_map = BM_uv_element_map_create(bm, scene, true, false, true, true);
if (element_map == NULL) {
return false;
}
bool changed = false;
-
- /* Loop backwards to simplify logic. */
- int j1 = element_map->totalUVs;
- for (int i = element_map->totalIslands - 1; i >= 0; --i) {
- int j0 = element_map->islandIndices[i];
- changed |= uvedit_uv_straighten_elements(
- element_map->buf + j0, j1 - j0, cd_loop_uv_offset, tool);
- j1 = j0;
+ for (int i = 0; i < element_map->total_islands; i++) {
+ changed |= uvedit_uv_straighten_elements(element_map->storage + element_map->island_indices[i],
+ element_map->island_total_uvs[i],
+ cd_loop_uv_offset,
+ tool);
}
BM_uv_element_map_free(element_map);
@@ -577,7 +565,7 @@ static void uv_weld_align(bContext *C, eUVWeldAlign tool)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
if (tool == UV_ALIGN_AUTO) {
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -707,7 +695,7 @@ static int uv_remove_doubles_to_selected(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
bool *changed = MEM_callocN(sizeof(bool) * objects_len, "uv_remove_doubles_selected.changed");
@@ -851,7 +839,7 @@ static int uv_remove_doubles_to_unselected(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
/* Calculate max possible number of kdtree nodes. */
int uv_maxlen = 0;
@@ -1036,6 +1024,12 @@ static bool uv_snap_cursor_to_selection(Scene *scene,
return ED_uvedit_center_multi(scene, objects_edit, objects_len, sima->cursor, sima->around);
}
+static void uv_snap_cursor_to_origin(float uvco[2])
+{
+ uvco[0] = 0;
+ uvco[1] = 0;
+}
+
static int uv_snap_cursor_exec(bContext *C, wmOperator *op)
{
SpaceImage *sima = CTX_wm_space_image(C);
@@ -1053,11 +1047,15 @@ static int uv_snap_cursor_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
changed = uv_snap_cursor_to_selection(scene, objects, objects_len, sima);
MEM_freeN(objects);
break;
}
+ case 2:
+ uv_snap_cursor_to_origin(sima->cursor);
+ changed = true;
+ break;
}
if (!changed) {
@@ -1074,6 +1072,7 @@ static void UV_OT_snap_cursor(wmOperatorType *ot)
static const EnumPropertyItem target_items[] = {
{0, "PIXELS", 0, "Pixels", ""},
{1, "SELECTED", 0, "Selected", ""},
+ {2, "ORIGIN", 0, "Origin", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -1256,7 +1255,7 @@ static int uv_snap_selection_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
if (target == 2) {
float center[2];
@@ -1349,7 +1348,7 @@ static int uv_pin_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -1451,7 +1450,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -1498,7 +1497,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
if (bm_face_is_all_uv_sel(efa, !swap, cd_loop_uv_offset)) {
BM_face_select_set(em->bm, efa, false);
}
- uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
+ uvedit_face_select_disable(scene, em->bm, efa, cd_loop_uv_offset);
}
else {
if (bm_face_is_all_uv_sel(efa, true, cd_loop_uv_offset) == !swap) {
@@ -1515,7 +1514,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
}
}
if (!swap) {
- uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
+ uvedit_face_select_disable(scene, em->bm, efa, cd_loop_uv_offset);
}
}
}
@@ -1537,7 +1536,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
break;
}
}
- uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
+ uvedit_face_select_disable(scene, em->bm, efa, cd_loop_uv_offset);
}
else {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
@@ -1561,7 +1560,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op)
}
}
if (!swap) {
- uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
+ uvedit_face_select_disable(scene, em->bm, efa, cd_loop_uv_offset);
}
}
}
@@ -1623,7 +1622,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -1839,7 +1838,7 @@ static int uv_seams_from_islands_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *ob = objects[ob_index];
@@ -1944,7 +1943,7 @@ static int uv_mark_seam_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
bool changed = false;
diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c
index 7c6960a634a..7af6cbe942b 100644
--- a/source/blender/editors/uvedit/uvedit_path.c
+++ b/source/blender/editors/uvedit/uvedit_path.c
@@ -56,75 +56,6 @@
#include "bmesh_tools.h"
/* -------------------------------------------------------------------- */
-/** \name Local Utilities
- * \{ */
-
-/**
- * Support edge-path using vert-path calculation code.
- *
- * Cheat! Pick 2 closest loops and do vertex path,
- * in practices only obscure/contrived cases will make give noticeably worse behavior.
- *
- * While the code below is a bit awkward, it's significantly less overhead than
- * adding full edge selection which is nearly the same as vertex path in the case of UV's.
- *
- * \param use_nearest: When false use the post distant pair of loops,
- * use when filling a region as we want both verts from each edge to be included in the region.
- */
-static void bm_loop_calc_vert_pair_from_edge_pair(const bool use_nearest,
- const int cd_loop_uv_offset,
- const float aspect_y,
- BMElem **ele_src_p,
- BMElem **ele_dst_p,
- BMElem **r_ele_dst_final)
-{
- BMLoop *l_src = (BMLoop *)*ele_src_p;
- BMLoop *l_dst = (BMLoop *)*ele_dst_p;
-
- const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset);
- const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset);
- const MLoopUV *luv_dst_v1 = BM_ELEM_CD_GET_VOID_P(l_dst, cd_loop_uv_offset);
- const MLoopUV *luv_dst_v2 = BM_ELEM_CD_GET_VOID_P(l_dst->next, cd_loop_uv_offset);
-
- const float uv_src_v1[2] = {luv_src_v1->uv[0], luv_src_v1->uv[1] / aspect_y};
- const float uv_src_v2[2] = {luv_src_v2->uv[0], luv_src_v2->uv[1] / aspect_y};
- const float uv_dst_v1[2] = {luv_dst_v1->uv[0], luv_dst_v1->uv[1] / aspect_y};
- const float uv_dst_v2[2] = {luv_dst_v2->uv[0], luv_dst_v2->uv[1] / aspect_y};
-
- struct {
- int src_index;
- int dst_index;
- float len_sq;
- } tests[4] = {
- {0, 0, len_squared_v2v2(uv_src_v1, uv_dst_v1)},
- {0, 1, len_squared_v2v2(uv_src_v1, uv_dst_v2)},
- {1, 0, len_squared_v2v2(uv_src_v2, uv_dst_v1)},
- {1, 1, len_squared_v2v2(uv_src_v2, uv_dst_v2)},
- };
- int i_best = 0;
- for (int i = 1; i < ARRAY_SIZE(tests); i++) {
- if (use_nearest) {
- if (tests[i].len_sq < tests[i_best].len_sq) {
- i_best = i;
- }
- }
- else {
- if (tests[i].len_sq > tests[i_best].len_sq) {
- i_best = i;
- }
- }
- }
-
- *ele_src_p = (BMElem *)(tests[i_best].src_index ? l_src->next : l_src);
- *ele_dst_p = (BMElem *)(tests[i_best].dst_index ? l_dst->next : l_dst);
-
- /* Ensure the edge is selected, not just the vertices up until we hit it. */
- *r_ele_dst_final = (BMElem *)(tests[i_best].dst_index ? l_dst : l_dst->next);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Path Select Struct & Properties
* \{ */
@@ -140,7 +71,7 @@ struct PathSelectParams {
struct UserData_UV {
Scene *scene;
BMEditMesh *em;
- uint cd_loop_uv_offset;
+ int cd_loop_uv_offset;
};
static void path_select_properties(wmOperatorType *ot)
@@ -180,22 +111,22 @@ static void path_select_params_from_op(wmOperator *op, struct PathSelectParams *
* \{ */
/* callbacks */
-static bool looptag_filter_cb(BMLoop *l, void *user_data_v)
+static bool verttag_filter_cb(BMLoop *l, void *user_data_v)
{
struct UserData_UV *user_data = user_data_v;
return uvedit_face_visible_test(user_data->scene, l->f);
}
-static bool looptag_test_cb(BMLoop *l, void *user_data_v)
+static bool verttag_test_cb(BMLoop *l, void *user_data_v)
{
/* All connected loops are selected or we return false. */
struct UserData_UV *user_data = user_data_v;
const Scene *scene = user_data->scene;
- const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ const int cd_loop_uv_offset = user_data->cd_loop_uv_offset;
const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
BMIter iter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
- if (looptag_filter_cb(l_iter, user_data)) {
+ if (verttag_filter_cb(l_iter, user_data)) {
const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
if (equals_v2v2(luv->uv, luv_iter->uv)) {
if (!uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) {
@@ -206,20 +137,20 @@ static bool looptag_test_cb(BMLoop *l, void *user_data_v)
}
return true;
}
-static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v)
+static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v)
{
struct UserData_UV *user_data = user_data_v;
const Scene *scene = user_data->scene;
BMEditMesh *em = user_data->em;
- const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ const int cd_loop_uv_offset = user_data->cd_loop_uv_offset;
const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
BMIter iter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
- if (looptag_filter_cb(l_iter, user_data)) {
+ if (verttag_filter_cb(l_iter, user_data)) {
MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
if (equals_v2v2(luv->uv, luv_iter->uv)) {
- uvedit_uv_select_set(scene, em, l_iter, val, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l_iter, val, false, cd_loop_uv_offset);
}
}
}
@@ -233,42 +164,10 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
const float aspect_y,
const int cd_loop_uv_offset)
{
- const char uv_selectmode = ED_uvedit_select_mode_get(scene);
- /* TODO(@sidd017): Implement logic to calculate shortest path for UV edges, since we now support
- * proper edge selection for UVs (D12028).
- * Till then continue using vertex path to fake shortest path calculation for edges. */
- const bool use_fake_edge_select = (uv_selectmode & UV_SELECT_EDGE);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
int flush = 0;
- /* Variables to use when `use_fake_edge_select` is set. */
- struct {
- BMLoop *l_dst_activate;
- BMLoop *l_dst_add_to_path;
- } fake_edge_select = {NULL};
-
- if (use_fake_edge_select) {
- fake_edge_select.l_dst_activate = l_dst;
-
- /* Use most distant when doing region selection.
- * without this we get dangling edges outside the region. */
- bool use_neaerst = (op_params->use_fill == false);
- BMElem *ele_src = (BMElem *)l_src;
- BMElem *ele_dst = (BMElem *)l_dst;
- BMElem *ele_dst_final = NULL;
- bm_loop_calc_vert_pair_from_edge_pair(
- use_neaerst, cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final);
-
- if (op_params->use_fill == false) {
- /* Always activate the item under the cursor. */
- fake_edge_select.l_dst_add_to_path = (BMLoop *)ele_dst_final;
- }
-
- l_src = (BMLoop *)ele_src;
- l_dst = (BMLoop *)ele_dst;
- }
-
struct UserData_UV user_data = {
.scene = scene,
.em = em,
@@ -291,33 +190,23 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
(BMElem *)l_src,
(BMElem *)l_dst,
params.cd_loop_uv_offset,
- looptag_filter_cb,
+ verttag_filter_cb,
&user_data);
}
else {
is_path_ordered = true;
- path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, &params, looptag_filter_cb, &user_data);
+ path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, &params, verttag_filter_cb, &user_data);
}
}
BMLoop *l_dst_last = l_dst;
if (path) {
- if (use_fake_edge_select) {
- if ((fake_edge_select.l_dst_add_to_path != NULL) &&
- (BLI_linklist_index(path, fake_edge_select.l_dst_add_to_path) == -1)) {
- /* Append, this isn't optimal compared to #BLI_linklist_append, it's a one-off lookup. */
- LinkNode *path_last = BLI_linklist_find_last(path);
- BLI_linklist_insert_after(&path_last, fake_edge_select.l_dst_add_to_path);
- BLI_assert(BLI_linklist_find_last(path)->link == fake_edge_select.l_dst_add_to_path);
- }
- }
-
/* toggle the flag */
bool all_set = true;
LinkNode *node = path;
do {
- if (!looptag_test_cb((BMLoop *)node->link, &user_data)) {
+ if (!verttag_test_cb((BMLoop *)node->link, &user_data)) {
all_set = false;
break;
}
@@ -328,7 +217,7 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
do {
if ((is_path_ordered == false) ||
WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) {
- looptag_set_cb((BMLoop *)node->link, !all_set, &user_data);
+ verttag_set_cb((BMLoop *)node->link, !all_set, &user_data);
if (is_path_ordered) {
l_dst_last = node->link;
}
@@ -339,23 +228,133 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene,
flush = all_set ? -1 : 1;
}
else {
- const bool is_act = !looptag_test_cb(l_dst, &user_data);
- looptag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
+ const bool is_act = !verttag_test_cb(l_dst, &user_data);
+ verttag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
}
if (op_params->track_active) {
- /* Fake edge selection. */
- if (use_fake_edge_select) {
- BMLoop *l_dst_activate = fake_edge_select.l_dst_activate;
- /* TODO(campbell): Search for an active loop attached to 'l_dst'.
- * when `BLI_linklist_index(path, l_dst_activate) == -1`
- * In practice this rarely happens though. */
- ED_uvedit_active_edge_loop_set(bm, l_dst_activate);
+ ED_uvedit_active_vert_loop_set(bm, l_dst_last);
+ }
+ return flush;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name UV Edge Path
+ * \{ */
+
+/* callbacks */
+static bool edgetag_filter_cb(BMLoop *l, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ return uvedit_face_visible_test(user_data->scene, l->f);
+}
+static bool edgetag_test_cb(BMLoop *l, void *user_data_v)
+{
+ /* All connected loops (UV) are selected or we return false. */
+ struct UserData_UV *user_data = user_data_v;
+ const Scene *scene = user_data->scene;
+ const int cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, l->e, BM_LOOPS_OF_EDGE) {
+ if (edgetag_filter_cb(l_iter, user_data)) {
+ if (BM_loop_uv_share_edge_check(l, l_iter, cd_loop_uv_offset)) {
+ if (!uvedit_edge_select_test(scene, l_iter, cd_loop_uv_offset)) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+static void edgetag_set_cb(BMLoop *l, bool val, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ const Scene *scene = user_data->scene;
+ BMEditMesh *em = user_data->em;
+ const int cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ uvedit_edge_select_set_with_sticky(scene, em, l, val, false, cd_loop_uv_offset);
+}
+
+static int mouse_mesh_uv_shortest_path_edge(Scene *scene,
+ Object *obedit,
+ const struct PathSelectParams *op_params,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ int flush = 0;
+
+ struct UserData_UV user_data = {
+ .scene = scene,
+ .em = em,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ const struct BMCalcPathUVParams params = {
+ .use_topology_distance = op_params->use_topology_distance,
+ .use_step_face = op_params->use_face_step,
+ .aspect_y = aspect_y,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ LinkNode *path = NULL;
+ bool is_path_ordered = false;
+
+ if (l_src != l_dst) {
+ if (op_params->use_fill) {
+ path = BM_mesh_calc_path_uv_region_edge(bm,
+ (BMElem *)l_src,
+ (BMElem *)l_dst,
+ params.cd_loop_uv_offset,
+ edgetag_filter_cb,
+ &user_data);
}
else {
- ED_uvedit_active_vert_loop_set(bm, l_dst_last);
+ is_path_ordered = true;
+ path = BM_mesh_calc_path_uv_edge(bm, l_src, l_dst, &params, edgetag_filter_cb, &user_data);
}
}
+
+ BMLoop *l_dst_last = l_dst;
+
+ if (path) {
+ /* toggle the flag */
+ bool all_set = true;
+ LinkNode *node = path;
+ do {
+ if (!edgetag_test_cb((BMLoop *)node->link, &user_data)) {
+ all_set = false;
+ break;
+ }
+ } while ((node = node->next));
+
+ int depth = -1;
+ node = path;
+ do {
+ if ((is_path_ordered == false) ||
+ WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) {
+ edgetag_set_cb((BMLoop *)node->link, !all_set, &user_data);
+ if (is_path_ordered) {
+ l_dst_last = node->link;
+ }
+ }
+ } while ((void)depth++, (node = node->next));
+
+ BLI_linklist_free(path, NULL);
+ flush = all_set ? -1 : 1;
+ }
+ else {
+ const bool is_act = !edgetag_test_cb(l_dst, &user_data);
+ edgetag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
+ }
+
+ if (op_params->track_active) {
+ ED_uvedit_active_edge_loop_set(bm, l_dst_last);
+ }
return flush;
}
@@ -376,7 +375,7 @@ static bool facetag_test_cb(BMFace *f, void *user_data_v)
/* All connected loops are selected or we return false. */
struct UserData_UV *user_data = user_data_v;
const Scene *scene = user_data->scene;
- const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ const int cd_loop_uv_offset = user_data->cd_loop_uv_offset;
BMIter iter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) {
@@ -391,7 +390,7 @@ static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
struct UserData_UV *user_data = user_data_v;
const Scene *scene = user_data->scene;
BMEditMesh *em = user_data->em;
- const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ const int cd_loop_uv_offset = user_data->cd_loop_uv_offset;
uvedit_face_select_set_with_sticky(scene, em, f, val, false, cd_loop_uv_offset);
}
@@ -514,13 +513,24 @@ static bool uv_shortest_path_pick_ex(Scene *scene,
ok = true;
}
else if (ele_src->head.htype == BM_LOOP) {
- flush = mouse_mesh_uv_shortest_path_vert(scene,
- obedit,
- op_params,
- (BMLoop *)ele_src,
- (BMLoop *)ele_dst,
- aspect_y,
- cd_loop_uv_offset);
+ if (uv_selectmode & UV_SELECT_EDGE) {
+ flush = mouse_mesh_uv_shortest_path_edge(scene,
+ obedit,
+ op_params,
+ (BMLoop *)ele_src,
+ (BMLoop *)ele_dst,
+ aspect_y,
+ cd_loop_uv_offset);
+ }
+ else {
+ flush = mouse_mesh_uv_shortest_path_vert(scene,
+ obedit,
+ op_params,
+ (BMLoop *)ele_src,
+ (BMLoop *)ele_dst,
+ aspect_y,
+ cd_loop_uv_offset);
+ }
ok = true;
}
@@ -529,24 +539,9 @@ static bool uv_shortest_path_pick_ex(Scene *scene,
const bool select = (flush == 1);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
if (ts->uv_flag & UV_SYNC_SELECTION) {
- if (uv_selectmode & UV_SELECT_EDGE) {
- /* Special case as we don't use true edge selection,
- * flush the selection from the vertices. */
- BM_mesh_select_mode_flush_ex(em->bm, SCE_SELECT_VERTEX, BM_SELECT_LEN_FLUSH_RECALC_ALL);
- }
ED_uvedit_select_sync_flush(scene->toolsettings, em, select);
}
else {
- if (uv_selectmode & UV_SELECT_EDGE) {
- /* TODO(@sidd017): Remove this case when adding proper uv edge support for this operator.
- * In the meantime, this case helps ensures proper UV selection states for edge mode. */
- if (select) {
- uvedit_select_flush(scene, em);
- }
- else {
- uvedit_deselect_flush(scene, em);
- }
- }
ED_uvedit_selectmode_flush(scene, em);
}
}
@@ -804,7 +799,7 @@ static int uv_shortest_path_select_exec(bContext *C, wmOperator *op)
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
diff --git a/source/blender/editors/uvedit/uvedit_rip.c b/source/blender/editors/uvedit/uvedit_rip.c
index 545cc57e3c4..5497b9cd1e5 100644
--- a/source/blender/editors/uvedit/uvedit_rip.c
+++ b/source/blender/editors/uvedit/uvedit_rip.c
@@ -848,7 +848,7 @@ static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const
BMLoop *l_iter = BLI_gsetIterator_getKey(&gs_iter);
ULData *ul = UL(l_iter);
if (ul->side == side_from_cursor) {
- uvedit_uv_select_disable(scene, em, l_iter, cd_loop_uv_offset);
+ uvedit_uv_select_disable(scene, em->bm, l_iter, cd_loop_uv_offset);
changed = true;
}
/* Ensure we don't operate on these again. */
@@ -866,7 +866,7 @@ static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const
BMLoop *l_iter = BLI_gsetIterator_getKey(&gs_iter);
ULData *ul = UL(l_iter);
if (ul->side == side_from_cursor) {
- uvedit_uv_select_disable(scene, em, l_iter, cd_loop_uv_offset);
+ uvedit_uv_select_disable(scene, em->bm, l_iter, cd_loop_uv_offset);
changed = true;
}
/* Ensure we don't operate on these again. */
@@ -910,7 +910,7 @@ static int uv_rip_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index db834f6a0fd..6c8fb9360bd 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -81,6 +81,7 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
typedef enum {
UV_SSIM_AREA_UV = 1000,
UV_SSIM_AREA_3D,
+ UV_SSIM_FACE,
UV_SSIM_LENGTH_UV,
UV_SSIM_LENGTH_3D,
UV_SSIM_SIDES,
@@ -206,7 +207,7 @@ static void uvedit_vertex_select_tagged(BMEditMesh *em,
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
}
}
}
@@ -264,7 +265,7 @@ void uvedit_face_select_set_with_sticky(const Scene *scene,
const ToolSettings *ts = scene->toolsettings;
const char sticky = ts->uv_sticky;
if (ts->uv_flag & UV_SYNC_SELECTION) {
- uvedit_face_select_set(scene, em, efa, select, do_history, cd_loop_uv_offset);
+ uvedit_face_select_set(scene, em->bm, efa, select, do_history, cd_loop_uv_offset);
return;
}
if (!uvedit_face_visible_test(scene, efa)) {
@@ -274,7 +275,7 @@ void uvedit_face_select_set_with_sticky(const Scene *scene,
* (not part of any face selections). This now uses the sticky location mode logic instead. */
switch (sticky) {
case SI_STICKY_DISABLE: {
- uvedit_face_select_set(scene, em, efa, select, do_history, cd_loop_uv_offset);
+ uvedit_face_select_set(scene, em->bm, efa, select, do_history, cd_loop_uv_offset);
break;
}
default: {
@@ -312,32 +313,30 @@ void uvedit_face_select_shared_vert(const Scene *scene,
}
void uvedit_face_select_set(const Scene *scene,
- BMEditMesh *em,
+ BMesh *bm,
BMFace *efa,
const bool select,
const bool do_history,
const int cd_loop_uv_offset)
{
if (select) {
- uvedit_face_select_enable(scene, em, efa, do_history, cd_loop_uv_offset);
+ uvedit_face_select_enable(scene, bm, efa, do_history, cd_loop_uv_offset);
}
else {
- uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
+ uvedit_face_select_disable(scene, bm, efa, cd_loop_uv_offset);
}
}
-void uvedit_face_select_enable(const Scene *scene,
- BMEditMesh *em,
- BMFace *efa,
- const bool do_history,
- const int cd_loop_uv_offset)
+void uvedit_face_select_enable(
+ const Scene *scene, BMesh *bm, BMFace *efa, const bool do_history, const int cd_loop_uv_offset)
{
+ BLI_assert(cd_loop_uv_offset >= 0);
const ToolSettings *ts = scene->toolsettings;
if (ts->uv_flag & UV_SYNC_SELECTION) {
- BM_face_select_set(em->bm, efa, true);
+ BM_face_select_set(bm, efa, true);
if (do_history) {
- BM_select_history_store(em->bm, (BMElem *)efa);
+ BM_select_history_store(bm, (BMElem *)efa);
}
}
else {
@@ -353,14 +352,15 @@ void uvedit_face_select_enable(const Scene *scene,
}
void uvedit_face_select_disable(const Scene *scene,
- BMEditMesh *em,
+ BMesh *bm,
BMFace *efa,
const int cd_loop_uv_offset)
{
+ BLI_assert(cd_loop_uv_offset >= 0);
const ToolSettings *ts = scene->toolsettings;
if (ts->uv_flag & UV_SYNC_SELECTION) {
- BM_face_select_set(em->bm, efa, false);
+ BM_face_select_set(bm, efa, false);
}
else {
BMLoop *l;
@@ -406,11 +406,11 @@ void uvedit_edge_select_set_with_sticky(const Scene *scene,
BMLoop *l,
const bool select,
const bool do_history,
- const uint cd_loop_uv_offset)
+ const int cd_loop_uv_offset)
{
const ToolSettings *ts = scene->toolsettings;
if (ts->uv_flag & UV_SYNC_SELECTION) {
- uvedit_edge_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ uvedit_edge_select_set(scene, em->bm, l, select, do_history, cd_loop_uv_offset);
return;
}
@@ -418,7 +418,7 @@ void uvedit_edge_select_set_with_sticky(const Scene *scene,
switch (sticky) {
case SI_STICKY_DISABLE: {
if (uvedit_face_visible_test(scene, l->f)) {
- uvedit_edge_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ uvedit_edge_select_set(scene, em->bm, l, select, do_history, cd_loop_uv_offset);
}
break;
}
@@ -500,7 +500,7 @@ void uvedit_edge_select_set_noflush(const Scene *scene,
}
void uvedit_edge_select_set(const Scene *scene,
- BMEditMesh *em,
+ BMesh *bm,
BMLoop *l,
const bool select,
const bool do_history,
@@ -508,36 +508,33 @@ void uvedit_edge_select_set(const Scene *scene,
{
if (select) {
- uvedit_edge_select_enable(scene, em, l, do_history, cd_loop_uv_offset);
+ uvedit_edge_select_enable(scene, bm, l, do_history, cd_loop_uv_offset);
}
else {
- uvedit_edge_select_disable(scene, em, l, cd_loop_uv_offset);
+ uvedit_edge_select_disable(scene, bm, l, cd_loop_uv_offset);
}
}
-void uvedit_edge_select_enable(const Scene *scene,
- BMEditMesh *em,
- BMLoop *l,
- const bool do_history,
- const int cd_loop_uv_offset)
+void uvedit_edge_select_enable(
+ const Scene *scene, BMesh *bm, BMLoop *l, const bool do_history, const int cd_loop_uv_offset)
{
const ToolSettings *ts = scene->toolsettings;
if (ts->uv_flag & UV_SYNC_SELECTION) {
if (ts->selectmode & SCE_SELECT_FACE) {
- BM_face_select_set(em->bm, l->f, true);
+ BM_face_select_set(bm, l->f, true);
}
else if (ts->selectmode & SCE_SELECT_EDGE) {
- BM_edge_select_set(em->bm, l->e, true);
+ BM_edge_select_set(bm, l->e, true);
}
else {
- BM_vert_select_set(em->bm, l->e->v1, true);
- BM_vert_select_set(em->bm, l->e->v2, true);
+ BM_vert_select_set(bm, l->e->v1, true);
+ BM_vert_select_set(bm, l->e->v2, true);
}
if (do_history) {
- BM_select_history_store(em->bm, (BMElem *)l->e);
+ BM_select_history_store(bm, (BMElem *)l->e);
}
}
else {
@@ -551,7 +548,7 @@ void uvedit_edge_select_enable(const Scene *scene,
}
void uvedit_edge_select_disable(const Scene *scene,
- BMEditMesh *em,
+ BMesh *bm,
BMLoop *l,
const int cd_loop_uv_offset)
@@ -560,14 +557,14 @@ void uvedit_edge_select_disable(const Scene *scene,
if (ts->uv_flag & UV_SYNC_SELECTION) {
if (ts->selectmode & SCE_SELECT_FACE) {
- BM_face_select_set(em->bm, l->f, false);
+ BM_face_select_set(bm, l->f, false);
}
else if (ts->selectmode & SCE_SELECT_EDGE) {
- BM_edge_select_set(em->bm, l->e, false);
+ BM_edge_select_set(bm, l->e, false);
}
else {
- BM_vert_select_set(em->bm, l->e->v1, false);
- BM_vert_select_set(em->bm, l->e->v2, false);
+ BM_vert_select_set(bm, l->e->v1, false);
+ BM_vert_select_set(bm, l->e->v2, false);
}
}
else {
@@ -628,11 +625,11 @@ void uvedit_uv_select_set_with_sticky(const Scene *scene,
BMLoop *l,
const bool select,
const bool do_history,
- const uint cd_loop_uv_offset)
+ const int cd_loop_uv_offset)
{
const ToolSettings *ts = scene->toolsettings;
if (ts->uv_flag & UV_SYNC_SELECTION) {
- uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, do_history, cd_loop_uv_offset);
return;
}
@@ -640,7 +637,7 @@ void uvedit_uv_select_set_with_sticky(const Scene *scene,
switch (sticky) {
case SI_STICKY_DISABLE: {
if (uvedit_face_visible_test(scene, l->f)) {
- uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, do_history, cd_loop_uv_offset);
}
break;
}
@@ -694,7 +691,8 @@ void uvedit_uv_select_shared_vert(const Scene *scene,
}
if (do_select) {
- uvedit_uv_select_set(scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_set(
+ scene, em->bm, l_radial_iter, select, do_history, cd_loop_uv_offset);
}
}
}
@@ -703,25 +701,22 @@ void uvedit_uv_select_shared_vert(const Scene *scene,
}
void uvedit_uv_select_set(const Scene *scene,
- BMEditMesh *em,
+ BMesh *bm,
BMLoop *l,
const bool select,
const bool do_history,
const int cd_loop_uv_offset)
{
if (select) {
- uvedit_uv_select_enable(scene, em, l, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_enable(scene, bm, l, do_history, cd_loop_uv_offset);
}
else {
- uvedit_uv_select_disable(scene, em, l, cd_loop_uv_offset);
+ uvedit_uv_select_disable(scene, bm, l, cd_loop_uv_offset);
}
}
-void uvedit_uv_select_enable(const Scene *scene,
- BMEditMesh *em,
- BMLoop *l,
- const bool do_history,
- const int cd_loop_uv_offset)
+void uvedit_uv_select_enable(
+ const Scene *scene, BMesh *bm, BMLoop *l, const bool do_history, const int cd_loop_uv_offset)
{
const ToolSettings *ts = scene->toolsettings;
@@ -731,14 +726,14 @@ void uvedit_uv_select_enable(const Scene *scene,
if (ts->uv_flag & UV_SYNC_SELECTION) {
if (ts->selectmode & SCE_SELECT_FACE) {
- BM_face_select_set(em->bm, l->f, true);
+ BM_face_select_set(bm, l->f, true);
}
else {
- BM_vert_select_set(em->bm, l->v, true);
+ BM_vert_select_set(bm, l->v, true);
}
if (do_history) {
- BM_select_history_store(em->bm, (BMElem *)l->v);
+ BM_select_history_store(bm, (BMElem *)l->v);
}
}
else {
@@ -748,7 +743,7 @@ void uvedit_uv_select_enable(const Scene *scene,
}
void uvedit_uv_select_disable(const Scene *scene,
- BMEditMesh *em,
+ BMesh *bm,
BMLoop *l,
const int cd_loop_uv_offset)
{
@@ -756,10 +751,10 @@ void uvedit_uv_select_disable(const Scene *scene,
if (ts->uv_flag & UV_SYNC_SELECTION) {
if (ts->selectmode & SCE_SELECT_FACE) {
- BM_face_select_set(em->bm, l->f, false);
+ BM_face_select_set(bm, l->f, false);
}
else {
- BM_vert_select_set(em->bm, l->v, false);
+ BM_vert_select_set(bm, l->v, false);
}
}
else {
@@ -1139,7 +1134,7 @@ BMLoop *uv_find_nearest_loop_from_vert(struct Scene *scene,
const float co[2])
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
- const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
BMIter liter;
BMLoop *l;
@@ -1167,7 +1162,8 @@ BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene,
const float co[2])
{
BMEditMesh *em = BKE_editmesh_from_object(obedit);
- const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ BLI_assert(cd_loop_uv_offset >= 0);
BMIter eiter;
BMLoop *l;
@@ -1421,7 +1417,7 @@ static BMLoop *bm_select_edgeloop_single_side_next(const Scene *scene,
scene, l_step, v_from_next, cd_loop_uv_offset);
}
-/* TODO(campbell): support this in the BMesh API, as we have for clearing other types. */
+/* TODO(@campbellbarton): support this in the BMesh API, as we have for clearing other types. */
static void bm_loop_tags_clear(BMesh *bm)
{
BMIter iter;
@@ -1974,7 +1970,7 @@ static void uv_select_linked_multi(Scene *scene,
BM_face_select_set(em->bm, efa, value); \
} \
else { \
- uvedit_face_select_set(scene, em, efa, value, false, cd_loop_uv_offset); \
+ uvedit_face_select_set(scene, em->bm, efa, value, false, cd_loop_uv_offset); \
} \
(void)0
@@ -2052,7 +2048,7 @@ static int uv_select_more_less(bContext *C, const bool select)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
const bool is_uv_face_selectmode = (ts->uv_selectmode == UV_SELECT_FACE);
@@ -2407,7 +2403,7 @@ static int uv_select_all_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
uv_select_all_perform_multi(scene, objects, objects_len, action);
@@ -2669,10 +2665,11 @@ static bool uv_mouse_select_multi(bContext *C,
}
static bool uv_mouse_select(bContext *C, const float co[2], const struct SelectPick_Params *params)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
bool changed = uv_mouse_select_multi(C, objects, objects_len, co, params);
MEM_freeN(objects);
return changed;
@@ -2821,10 +2818,11 @@ static int uv_mouse_select_loop_generic(bContext *C,
const bool extend,
enum eUVLoopGenericType loop_type)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
int ret = uv_mouse_select_loop_generic_multi(C, objects, objects_len, co, extend, loop_type);
MEM_freeN(objects);
return ret;
@@ -2981,7 +2979,7 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
if (pick) {
float co[2];
@@ -3139,7 +3137,7 @@ static int uv_select_split_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -3246,7 +3244,7 @@ static void uv_select_flush_from_tag_sticky_loc_internal(const Scene *scene,
UvMapVert *start_vlist = NULL, *vlist_iter;
BMFace *efa_vlist;
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
vlist_iter = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
@@ -3264,7 +3262,6 @@ static void uv_select_flush_from_tag_sticky_loc_internal(const Scene *scene,
vlist_iter = start_vlist;
while (vlist_iter) {
-
if (vlist_iter != start_vlist && vlist_iter->separate) {
break;
}
@@ -3277,7 +3274,7 @@ static void uv_select_flush_from_tag_sticky_loc_internal(const Scene *scene,
l_other = BM_iter_at_index(
em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->loop_of_poly_index);
- uvedit_uv_select_set(scene, em, l_other, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l_other, select, false, cd_loop_uv_offset);
}
vlist_iter = vlist_iter->next;
}
@@ -3344,7 +3341,7 @@ static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, co
else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
- uvedit_face_select_set(scene, em, efa, select, false, cd_loop_uv_offset);
+ uvedit_face_select_set(scene, em->bm, efa, select, false, cd_loop_uv_offset);
}
}
}
@@ -3393,7 +3390,7 @@ static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, co
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
}
}
}
@@ -3422,7 +3419,7 @@ static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, co
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
}
}
}
@@ -3543,7 +3540,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
if (use_pre_deselect) {
uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
@@ -3582,6 +3579,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
}
}
else if (use_edge && !pinned) {
+ bool do_second_pass = true;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@@ -3596,11 +3594,35 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
uvedit_edge_select_set_with_sticky(
scene, em, l_prev, select, false, cd_loop_uv_offset);
changed = true;
+ do_second_pass = false;
}
l_prev = l;
luv_prev = luv;
}
}
+ /* Do a second pass if no complete edges could be selected.
+ * This matches wire-frame edit-mesh selection in the 3D view. */
+ if (do_second_pass) {
+ /* Second pass to check if edges partially overlap with the selection area (box). */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
+ MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (BLI_rctf_isect_segment(&rectf, luv_prev->uv, luv->uv)) {
+ uvedit_edge_select_set_with_sticky(
+ scene, em, l_prev, select, false, cd_loop_uv_offset);
+ changed = true;
+ }
+ l_prev = l;
+ luv_prev = luv;
+ }
+ }
+ }
}
else {
/* other selection modes */
@@ -3618,14 +3640,14 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION)) {
/* UV_SYNC_SELECTION - can't do pinned selection */
if (BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
has_selected = true;
}
}
else if (pinned) {
if ((luv->flag & MLOOPUV_PINNED) && BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
}
}
@@ -3692,9 +3714,9 @@ void UV_OT_select_box(wmOperatorType *ot)
/** \name Circle Select Operator
* \{ */
-static int uv_circle_select_is_point_inside(const float uv[2],
- const float offset[2],
- const float ellipse[2])
+static bool uv_circle_select_is_point_inside(const float uv[2],
+ const float offset[2],
+ const float ellipse[2])
{
/* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
const float co[2] = {
@@ -3704,10 +3726,10 @@ static int uv_circle_select_is_point_inside(const float uv[2],
return len_squared_v2(co) < 1.0f;
}
-static int uv_circle_select_is_edge_inside(const float uv_a[2],
- const float uv_b[2],
- const float offset[2],
- const float ellipse[2])
+static bool uv_circle_select_is_edge_inside(const float uv_a[2],
+ const float uv_b[2],
+ const float offset[2],
+ const float ellipse[2])
{
/* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
const float co_a[2] = {
@@ -3765,7 +3787,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
WM_gesture_is_modal_first(op->customdata));
@@ -3838,7 +3860,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (uv_circle_select_is_point_inside(luv->uv, offset, ellipse)) {
changed = true;
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
has_selected = true;
}
@@ -3920,6 +3942,24 @@ static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region,
return false;
}
+static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region,
+ const rcti *clip_rect,
+ const int mcoords[][2],
+ const int mcoords_len,
+ const float co_test_a[2],
+ const float co_test_b[2])
+{
+ int co_screen_a[2], co_screen_b[2];
+ if (UI_view2d_view_to_region_segment_clip(
+ &region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) &&
+ BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) &&
+ BLI_lasso_is_edge_inside(
+ mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) {
+ return true;
+ }
+ return false;
+}
+
static bool do_lasso_select_mesh_uv(bContext *C,
const int mcoords[][2],
const int mcoords_len,
@@ -3953,7 +3993,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
if (use_pre_deselect) {
uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
@@ -3988,6 +4028,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
}
}
else if (use_edge) {
+ bool do_second_pass = true;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@@ -4004,12 +4045,37 @@ static bool do_lasso_select_mesh_uv(bContext *C,
region, &rect, mcoords, mcoords_len, luv_prev->uv)) {
uvedit_edge_select_set_with_sticky(
scene, em, l_prev, select, false, cd_loop_uv_offset);
+ do_second_pass = false;
changed = true;
}
l_prev = l;
luv_prev = luv;
}
}
+ /* Do a second pass if no complete edges could be selected.
+ * This matches wire-frame edit-mesh selection in the 3D view. */
+ if (do_second_pass) {
+ /* Second pass to check if edges partially overlap with the selection area (lasso). */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
+ MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (do_lasso_select_mesh_uv_is_edge_inside(
+ region, &rect, mcoords, mcoords_len, luv->uv, luv_prev->uv)) {
+ uvedit_edge_select_set_with_sticky(
+ scene, em, l_prev, select, false, cd_loop_uv_offset);
+ changed = true;
+ }
+ l_prev = l;
+ luv_prev = luv;
+ }
+ }
+ }
}
else { /* Vert Selection. */
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
@@ -4024,7 +4090,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (do_lasso_select_mesh_uv_is_point_inside(
region, &rect, mcoords, mcoords_len, luv->uv)) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
changed = true;
BM_elem_flag_enable(l->v, BM_ELEM_TAG);
has_selected = true;
@@ -4126,7 +4192,7 @@ static int uv_select_pinned_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -4144,7 +4210,7 @@ static int uv_select_pinned_exec(bContext *C, wmOperator *op)
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
if (luv->flag & MLOOPUV_PINNED) {
- uvedit_uv_select_enable(scene, em, l, false, cd_loop_uv_offset);
+ uvedit_uv_select_enable(scene, em->bm, l, false, cd_loop_uv_offset);
changed = true;
}
}
@@ -4261,7 +4327,7 @@ static int uv_select_overlap(bContext *C, const bool extend)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
/* Calculate maximum number of tree nodes and prepare initial selection. */
uint uv_tri_len = 0;
@@ -4397,8 +4463,8 @@ static int uv_select_overlap(bContext *C, const bool extend)
/* Main tri-tri overlap test. */
const float endpoint_bias = -1e-4f;
if (overlap_tri_tri_uv_test(o_a->tri, o_b->tri, endpoint_bias)) {
- uvedit_face_select_enable(scene, em_a, face_a, false, cd_loop_uv_offset_a);
- uvedit_face_select_enable(scene, em_b, face_b, false, cd_loop_uv_offset_b);
+ uvedit_face_select_enable(scene, em_a->bm, face_a, false, cd_loop_uv_offset_a);
+ uvedit_face_select_enable(scene, em_b->bm, face_b, false, cd_loop_uv_offset_b);
}
}
@@ -4569,6 +4635,33 @@ static float get_uv_face_needle(const eUVSelectSimilar type,
return result;
}
+static float get_uv_island_needle(const eUVSelectSimilar type,
+ const struct FaceIsland *island,
+ const float ob_m3[3][3],
+ const int cd_loop_uv_offset)
+
+{
+ float result = 0.0f;
+ switch (type) {
+ case UV_SSIM_AREA_UV:
+ for (int i = 0; i < island->faces_len; i++) {
+ result += BM_face_calc_area_uv(island->faces[i], cd_loop_uv_offset);
+ }
+ break;
+ case UV_SSIM_AREA_3D:
+ for (int i = 0; i < island->faces_len; i++) {
+ result += BM_face_calc_area_with_mat3(island->faces[i], ob_m3);
+ }
+ break;
+ case UV_SSIM_FACE:
+ return island->faces_len;
+ default:
+ BLI_assert_unreachable();
+ return false;
+ }
+ return result;
+}
+
static int uv_select_similar_vert_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
@@ -4582,7 +4675,7 @@ static int uv_select_similar_vert_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
int max_verts_selected_all = 0;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -4667,7 +4760,7 @@ static int uv_select_similar_vert_exec(bContext *C, wmOperator *op)
const float needle = get_uv_vert_needle(type, l->v, ob_m3, luv, cd_loop_uv_offset);
bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
if (select) {
- uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
changed = true;
}
}
@@ -4695,7 +4788,7 @@ static int uv_select_similar_edge_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
int max_edges_selected_all = 0;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -4786,7 +4879,7 @@ static int uv_select_similar_edge_exec(bContext *C, wmOperator *op)
float needle = get_uv_edge_needle(type, l->e, ob_m3, luv_a, luv_b, cd_loop_uv_offset);
bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
if (select) {
- uvedit_edge_select_set(scene, em, l, select, false, cd_loop_uv_offset);
+ uvedit_edge_select_set(scene, em->bm, l, select, false, cd_loop_uv_offset);
changed = true;
}
}
@@ -4814,7 +4907,7 @@ static int uv_select_similar_face_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
int max_faces_selected_all = 0;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -4886,7 +4979,7 @@ static int uv_select_similar_face_exec(bContext *C, wmOperator *op)
bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
if (select) {
- uvedit_face_select_set(scene, em, face, select, do_history, cd_loop_uv_offset);
+ uvedit_face_select_set(scene, em->bm, face, select, do_history, cd_loop_uv_offset);
changed = true;
}
}
@@ -4900,6 +4993,136 @@ static int uv_select_similar_face_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static bool uv_island_selected(const Scene *scene, struct FaceIsland *island)
+{
+ BLI_assert(island && island->faces_len);
+ return uvedit_face_select_test(scene, island->faces[0], island->cd_loop_uv_offset);
+}
+
+static int uv_select_similar_island_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ const eUVSelectSimilar type = RNA_enum_get(op->ptr, "type");
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const eSimilarCmp compare = RNA_enum_get(op->ptr, "compare");
+
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ scene, view_layer, ((View3D *)NULL), &objects_len);
+
+ ListBase *island_list_ptr = MEM_callocN(sizeof(*island_list_ptr) * objects_len, __func__);
+ int island_list_len = 0;
+
+ const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ if (cd_loop_uv_offset == -1) {
+ continue;
+ }
+
+ float aspect_y = 1.0f; /* Placeholder value, aspect doesn't change connectivity. */
+ island_list_len += bm_mesh_calc_uv_islands(scene,
+ em->bm,
+ &island_list_ptr[ob_index],
+ face_selected,
+ false,
+ false,
+ aspect_y,
+ cd_loop_uv_offset);
+ }
+
+ struct FaceIsland **island_array = MEM_callocN(sizeof(*island_array) * island_list_len,
+ __func__);
+
+ int tree_index = 0;
+ KDTree_1d *tree_1d = BLI_kdtree_1d_new(island_list_len);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ if (cd_loop_uv_offset == -1) {
+ continue;
+ }
+
+ float ob_m3[3][3];
+ copy_m3_m4(ob_m3, obedit->obmat);
+
+ int index;
+ LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list_ptr[ob_index], index) {
+ island_array[index] = island;
+ if (!uv_island_selected(scene, island)) {
+ continue;
+ }
+ float needle = get_uv_island_needle(type, island, ob_m3, cd_loop_uv_offset);
+ if (tree_1d) {
+ BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
+ }
+ }
+ }
+
+ if (tree_1d != NULL) {
+ BLI_kdtree_1d_deduplicate(tree_1d);
+ BLI_kdtree_1d_balance(tree_1d);
+ }
+
+ int tot_island_index = 0;
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ if (cd_loop_uv_offset == -1) {
+ continue;
+ }
+ float ob_m3[3][3];
+ copy_m3_m4(ob_m3, obedit->obmat);
+
+ bool changed = false;
+ int index;
+ LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list_ptr[ob_index], index) {
+ island_array[tot_island_index++] = island; /* To deallocate later. */
+ if (uv_island_selected(scene, island)) {
+ continue;
+ }
+ float needle = get_uv_island_needle(type, island, ob_m3, cd_loop_uv_offset);
+ bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
+ if (!select) {
+ continue;
+ }
+ bool do_history = false;
+ for (int j = 0; j < island->faces_len; j++) {
+ uvedit_face_select_set(
+ scene, em->bm, island->faces[j], select, do_history, island->cd_loop_uv_offset);
+ }
+ changed = true;
+ }
+
+ if (changed) {
+ uv_select_tag_update_for_object(depsgraph, ts, obedit);
+ }
+ }
+
+ BLI_assert(tot_island_index == island_list_len);
+ for (int i = 0; i < island_list_len; i++) {
+ MEM_SAFE_FREE(island_array[i]->faces);
+ MEM_SAFE_FREE(island_array[i]);
+ }
+
+ MEM_SAFE_FREE(island_array);
+ MEM_SAFE_FREE(island_list_ptr);
+ MEM_SAFE_FREE(objects);
+ BLI_kdtree_1d_free(tree_1d);
+
+ return OPERATOR_FINISHED;
+}
+
/* Select similar UV faces/edges/verts based on current selection. */
static int uv_select_similar_exec(bContext *C, wmOperator *op)
{
@@ -4921,7 +5144,7 @@ static int uv_select_similar_exec(bContext *C, wmOperator *op)
return uv_select_similar_face_exec(C, op);
}
if (selectmode & UV_SELECT_ISLAND) {
- // return uv_select_similar_island_exec(C, op);
+ return uv_select_similar_island_exec(C, op);
}
return uv_select_similar_vert_exec(C, op);
@@ -4942,6 +5165,12 @@ static EnumPropertyItem prop_face_similar_types[] = {
{UV_SSIM_MATERIAL, "MATERIAL", 0, "Material", ""},
{0}};
+static EnumPropertyItem prop_island_similar_types[] = {
+ {UV_SSIM_AREA_UV, "AREA", 0, "Area", ""},
+ {UV_SSIM_AREA_3D, "AREA_3D", 0, "Area 3D", ""},
+ {UV_SSIM_FACE, "FACE", 0, "Amount of Faces in Island", ""},
+ {0}};
+
static EnumPropertyItem prop_similar_compare_types[] = {{SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
{SIM_CMP_GT, "GREATER", 0, "Greater", ""},
{SIM_CMP_LT, "LESS", 0, "Less", ""},
@@ -4961,6 +5190,9 @@ static const EnumPropertyItem *uv_select_similar_type_itemf(bContext *C,
if (selectmode & UV_SELECT_FACE) {
return prop_face_similar_types;
}
+ if (selectmode & UV_SELECT_ISLAND) {
+ return prop_island_similar_types;
+ }
}
return prop_vert_similar_types;
@@ -5154,12 +5386,12 @@ static void uv_isolate_selected_islands(const Scene *scene,
BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0);
BMFace *efa;
BMIter iter, liter;
- UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, true, false, false, true);
+ UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true, true);
if (elementmap == NULL) {
return;
}
- int num_islands = elementmap->totalIslands;
+ int num_islands = elementmap->total_islands;
/* Boolean array that tells if island with index i is completely selected or not. */
bool *is_island_not_selected = MEM_callocN(sizeof(bool) * (num_islands), __func__);
@@ -5251,7 +5483,7 @@ void ED_uvedit_selectmode_clean(const Scene *scene, Object *obedit)
if (uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
BM_elem_flag_enable(efa, BM_ELEM_TAG);
}
- uvedit_face_select_set(scene, em, efa, false, false, cd_loop_uv_offset);
+ uvedit_face_select_set(scene, em->bm, efa, false, false, cd_loop_uv_offset);
}
}
uv_select_flush_from_tag_face(scene, obedit, true);
@@ -5273,7 +5505,7 @@ void ED_uvedit_selectmode_clean_multi(bContext *C)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, ((View3D *)NULL), &objects_len);
+ scene, view_layer, ((View3D *)NULL), &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 55e44607f6f..05b98ab9627 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -287,14 +287,6 @@ static void stitch_update_header(StitchStateContainer *ssc, bContext *C)
}
}
-static int getNumOfIslandUvs(UvElementMap *elementMap, int island)
-{
- if (island == elementMap->totalIslands - 1) {
- return elementMap->totalUVs - elementMap->islandIndices[island];
- }
- return elementMap->islandIndices[island + 1] - elementMap->islandIndices[island];
-}
-
static void stitch_uv_rotate(const float mat[2][2],
const float medianPoint[2],
float uv[2],
@@ -419,10 +411,9 @@ static void stitch_calculate_island_snapping(StitchState *state,
int final)
{
BMesh *bm = state->em->bm;
- int i;
UvElement *element;
- for (i = 0; i < state->element_map->totalIslands; i++) {
+ for (int i = 0; i < state->element_map->total_islands; i++) {
if (island_stitch_data[i].addedForPreview) {
int numOfIslandUVs = 0, j;
int totelem = island_stitch_data[i].num_rot_elements_neg +
@@ -464,8 +455,8 @@ static void stitch_calculate_island_snapping(StitchState *state,
}
angle_to_mat2(rotation_mat, rotation);
- numOfIslandUVs = getNumOfIslandUvs(state->element_map, i);
- element = &state->element_map->buf[state->element_map->islandIndices[i]];
+ numOfIslandUVs = state->element_map->island_total_uvs[i];
+ element = &state->element_map->storage[state->element_map->island_indices[i]];
for (j = 0; j < numOfIslandUVs; j++, element++) {
/* stitchable uvs have already been processed, don't process */
if (!(element->flag & STITCH_PROCESSED)) {
@@ -527,8 +518,8 @@ static void stitch_island_calculate_edge_rotation(UvEdge *edge,
luv2 = CustomData_bmesh_get(&bm->ldata, element2->l->head.data, CD_MLOOPUV);
if (ssc->mode == STITCH_VERT) {
- index1 = uvfinal_map[element1 - state->element_map->buf];
- index2 = uvfinal_map[element2 - state->element_map->buf];
+ index1 = uvfinal_map[element1 - state->element_map->storage];
+ index2 = uvfinal_map[element2 - state->element_map->storage];
}
else {
index1 = edge->uv1;
@@ -569,27 +560,17 @@ static void stitch_island_calculate_vert_rotation(UvElement *element,
StitchState *state,
IslandStitchData *island_stitch_data)
{
- float edgecos = 1.0f, edgesin = 0.0f;
- int index;
- UvElement *element_iter;
float rotation = 0, rotation_neg = 0;
int rot_elem = 0, rot_elem_neg = 0;
- BMLoop *l;
if (element->island == ssc->static_island && !ssc->midpoints) {
return;
}
- l = element->l;
-
- index = BM_elem_index_get(l->v);
-
- element_iter = state->element_map->vert[index];
-
+ UvElement *element_iter = BM_uv_element_get_head(state->element_map, element);
for (; element_iter; element_iter = element_iter->next) {
if (element_iter->separate &&
stitch_check_uvs_state_stitchable(element, element_iter, ssc, state)) {
- int index_tmp1, index_tmp2;
float normal[2];
/* only calculate rotation against static island uv verts */
@@ -597,14 +578,14 @@ static void stitch_island_calculate_vert_rotation(UvElement *element,
continue;
}
- index_tmp1 = element_iter - state->element_map->buf;
+ int index_tmp1 = element_iter - state->element_map->storage;
index_tmp1 = state->map[index_tmp1];
- index_tmp2 = element - state->element_map->buf;
+ int index_tmp2 = element - state->element_map->storage;
index_tmp2 = state->map[index_tmp2];
negate_v2_v2(normal, state->normals + index_tmp2 * 2);
- edgecos = dot_v2v2(normal, state->normals + index_tmp1 * 2);
- edgesin = cross_v2v2(normal, state->normals + index_tmp1 * 2);
+ float edgecos = dot_v2v2(normal, state->normals + index_tmp1 * 2);
+ float edgesin = cross_v2v2(normal, state->normals + index_tmp1 * 2);
if (edgesin > 0.0f) {
rotation += acosf(max_ff(-1.0f, min_ff(1.0f, edgecos)));
rot_elem++;
@@ -653,9 +634,8 @@ static void state_delete(StitchState *state)
if (state->edges) {
MEM_freeN(state->edges);
}
- if (state->stitch_preview) {
- stitch_preview_delete(state->stitch_preview);
- }
+ stitch_preview_delete(state->stitch_preview);
+ state->stitch_preview = NULL;
if (state->edge_hash) {
BLI_ghash_free(state->edge_hash, NULL, NULL);
}
@@ -680,10 +660,7 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState *
UvEdge *edges = state->edges;
const int *map = state->map;
UvElementMap *element_map = state->element_map;
- UvElement *first_element = element_map->buf;
- int i;
-
- for (i = 0; i < state->total_separate_edges; i++) {
+ for (int i = 0; i < state->total_separate_edges; i++) {
UvEdge *edge = edges + i;
if (edge->first) {
@@ -696,7 +673,7 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState *
UvElement *element2 = state->uvs[edge->uv2];
/* Now iterate through all faces and try to find edges sharing the same vertices */
- UvElement *iter1 = element_map->vert[BM_elem_index_get(element1->l->v)];
+ UvElement *iter1 = BM_uv_element_get_head(state->element_map, element1);
UvEdge *last_set = edge;
int elemindex2 = BM_elem_index_get(element2->l->v);
@@ -714,8 +691,8 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState *
}
if (iter2) {
- int index1 = map[iter1 - first_element];
- int index2 = map[iter2 - first_element];
+ int index1 = map[iter1 - element_map->storage];
+ int index2 = map[iter2 - element_map->storage];
UvEdge edgetmp;
UvEdge *edge2, *eiter;
bool valid = true;
@@ -764,15 +741,7 @@ static void determine_uv_stitchability(UvElement *element,
StitchState *state,
IslandStitchData *island_stitch_data)
{
- int vert_index;
- UvElement *element_iter;
- BMLoop *l;
-
- l = element->l;
-
- vert_index = BM_elem_index_get(l->v);
- element_iter = state->element_map->vert[vert_index];
-
+ UvElement *element_iter = BM_uv_element_get_head(state->element_map, element);
for (; element_iter; element_iter = element_iter->next) {
if (element_iter->separate) {
if (stitch_check_uvs_stitchable(element, element_iter, ssc, state)) {
@@ -853,16 +822,7 @@ static void stitch_validate_uv_stitchability(UvElement *element,
return;
}
- UvElement *element_iter;
- int vert_index;
- BMLoop *l;
-
- l = element->l;
-
- vert_index = BM_elem_index_get(l->v);
-
- element_iter = state->element_map->vert[vert_index];
-
+ UvElement *element_iter = BM_uv_element_get_head(state->element_map, element);
for (; element_iter; element_iter = element_iter->next) {
if (element_iter->separate) {
if (element_iter == element) {
@@ -956,7 +916,7 @@ static void stitch_propagate_uv_final_position(Scene *scene,
if (final) {
copy_v2_v2(luv->uv, final_position[index].uv);
- uvedit_uv_select_enable(scene, state->em, l, false, cd_loop_uv_offset);
+ uvedit_uv_select_enable(scene, state->em->bm, l, false, cd_loop_uv_offset);
}
else {
int face_preview_pos =
@@ -1015,7 +975,7 @@ static int stitch_process_data(StitchStateContainer *ssc,
preview_position[i].data_position = STITCH_NO_PREVIEW;
}
- island_stitch_data = MEM_callocN(sizeof(*island_stitch_data) * state->element_map->totalIslands,
+ island_stitch_data = MEM_callocN(sizeof(*island_stitch_data) * state->element_map->total_islands,
"stitch_island_data");
if (!island_stitch_data) {
return 0;
@@ -1040,7 +1000,7 @@ static int stitch_process_data(StitchStateContainer *ssc,
}
/* Remember stitchable candidates as places the 'I' button will stop at. */
- for (int island_idx = 0; island_idx < state->element_map->totalIslands; island_idx++) {
+ for (int island_idx = 0; island_idx < state->element_map->total_islands; island_idx++) {
state->island_is_stitchable[island_idx] = island_stitch_data[island_idx].stitchableCandidate ?
true :
false;
@@ -1048,10 +1008,10 @@ static int stitch_process_data(StitchStateContainer *ssc,
if (is_active_state) {
/* set static island to one that is added for preview */
- ssc->static_island %= state->element_map->totalIslands;
+ ssc->static_island %= state->element_map->total_islands;
while (!(island_stitch_data[ssc->static_island].stitchableCandidate)) {
ssc->static_island++;
- ssc->static_island %= state->element_map->totalIslands;
+ ssc->static_island %= state->element_map->total_islands;
/* this is entirely possible if for example limit stitching
* with no stitchable verts or no selection */
if (ssc->static_island == previous_island) {
@@ -1172,13 +1132,11 @@ static int stitch_process_data(StitchStateContainer *ssc,
* Setup preview for stitchable islands *
****************************************/
if (ssc->snap_islands) {
- for (i = 0; i < state->element_map->totalIslands; i++) {
+ for (i = 0; i < state->element_map->total_islands; i++) {
if (island_stitch_data[i].addedForPreview) {
- int numOfIslandUVs = 0, j;
- UvElement *element;
- numOfIslandUVs = getNumOfIslandUvs(state->element_map, i);
- element = &state->element_map->buf[state->element_map->islandIndices[i]];
- for (j = 0; j < numOfIslandUVs; j++, element++) {
+ int numOfIslandUVs = state->element_map->island_total_uvs[i];
+ UvElement *element = &state->element_map->storage[state->element_map->island_indices[i]];
+ for (int j = 0; j < numOfIslandUVs; j++, element++) {
stitch_set_face_preview_buffer_position(element->l->f, preview, preview_position);
}
}
@@ -1263,7 +1221,7 @@ static int stitch_process_data(StitchStateContainer *ssc,
if (ssc->mode == STITCH_VERT) {
final_position = MEM_callocN(state->selection_size * sizeof(*final_position),
"stitch_uv_average");
- uvfinal_map = MEM_mallocN(state->element_map->totalUVs * sizeof(*uvfinal_map),
+ uvfinal_map = MEM_mallocN(state->element_map->total_uvs * sizeof(*uvfinal_map),
"stitch_uv_final_map");
}
else {
@@ -1279,12 +1237,11 @@ static int stitch_process_data(StitchStateContainer *ssc,
if (element->flag & STITCH_STITCHABLE) {
BMLoop *l;
MLoopUV *luv;
- UvElement *element_iter;
l = element->l;
luv = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MLOOPUV);
- uvfinal_map[element - state->element_map->buf] = i;
+ uvfinal_map[element - state->element_map->storage] = i;
copy_v2_v2(final_position[i].uv, luv->uv);
final_position[i].count = 1;
@@ -1293,8 +1250,7 @@ static int stitch_process_data(StitchStateContainer *ssc,
continue;
}
- element_iter = state->element_map->vert[BM_elem_index_get(l->v)];
-
+ UvElement *element_iter = state->element_map->vertex[BM_elem_index_get(l->v)];
for (; element_iter; element_iter = element_iter->next) {
if (element_iter->separate) {
if (stitch_check_uvs_state_stitchable(element, element_iter, ssc, state)) {
@@ -1542,6 +1498,7 @@ static int stitch_process_data_all(StitchStateContainer *ssc, Scene *scene, int
static uint uv_edge_hash(const void *key)
{
const UvEdge *edge = key;
+ BLI_assert(edge->uv1 < edge->uv2);
return (BLI_ghashutil_uinthash(edge->uv2) + BLI_ghashutil_uinthash(edge->uv1));
}
@@ -1549,6 +1506,8 @@ static bool uv_edge_compare(const void *a, const void *b)
{
const UvEdge *edge1 = a;
const UvEdge *edge2 = b;
+ BLI_assert(edge1->uv1 < edge1->uv2);
+ BLI_assert(edge2->uv1 < edge2->uv2);
if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) {
return 0;
@@ -1588,13 +1547,8 @@ static void stitch_select_edge(UvEdge *edge, StitchState *state, int always_sele
/* Select all common uvs */
static void stitch_select_uv(UvElement *element, StitchState *state, int always_select)
{
- BMLoop *l;
- UvElement *element_iter;
UvElement **selection_stack = (UvElement **)state->selection_stack;
-
- l = element->l;
-
- element_iter = state->element_map->vert[BM_elem_index_get(l->v)];
+ UvElement *element_iter = BM_uv_element_get_head(state->element_map, element);
/* first deselect all common uvs */
for (; element_iter; element_iter = element_iter->next) {
if (element_iter->separate) {
@@ -1710,7 +1664,7 @@ static void stitch_calculate_edge_normal(BMEditMesh *em, UvEdge *edge, float *no
static void stitch_draw_vbo(GPUVertBuf *vbo, GPUPrimType prim_type, const float col[4])
{
GPUBatch *batch = GPU_batch_create_ex(prim_type, vbo, NULL, GPU_BATCH_OWNS_VBO);
- GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_UNIFORM_COLOR);
+ GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR);
GPU_batch_uniform_4fv(batch, "color", col);
GPU_batch_draw(batch);
GPU_batch_discard(batch);
@@ -1850,8 +1804,8 @@ static UvEdge *uv_edge_get(BMLoop *l, StitchState *state)
UvElement *element1 = BM_uv_element_get(state->element_map, l->f, l);
UvElement *element2 = BM_uv_element_get(state->element_map, l->f, l->next);
- int uv1 = state->map[element1 - state->element_map->buf];
- int uv2 = state->map[element2 - state->element_map->buf];
+ int uv1 = state->map[element1 - state->element_map->storage];
+ int uv2 = state->map[element2 - state->element_map->storage];
if (uv1 < uv2) {
tmp_edge.uv1 = uv1;
@@ -1878,7 +1832,6 @@ static StitchState *stitch_init(bContext *C,
int total_edges;
/* maps uvelements to their first coincident uv */
int *map;
- int counter = 0, i;
BMFace *efa;
BMLoop *l;
BMIter iter, liter;
@@ -1902,15 +1855,7 @@ static StitchState *stitch_init(bContext *C,
* for stitch this isn't useful behavior, see T86924. */
const int selectmode_orig = scene->toolsettings->selectmode;
scene->toolsettings->selectmode = SCE_SELECT_VERTEX;
-
- /* in uv synch selection, all uv's are visible */
- if (ts->uv_flag & UV_SYNC_SELECTION) {
- state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, false, true, true);
- }
- else {
- state->element_map = BM_uv_element_map_create(state->em->bm, scene, true, false, true, true);
- }
-
+ state->element_map = BM_uv_element_map_create(state->em->bm, scene, false, true, true, true);
scene->toolsettings->selectmode = selectmode_orig;
if (!state->element_map) {
@@ -1921,45 +1866,39 @@ static StitchState *stitch_init(bContext *C,
ED_uvedit_get_aspect(obedit, &aspx, &aspy);
state->aspect = aspx / aspy;
- /* Count 'unique' uvs */
- for (i = 0; i < state->element_map->totalUVs; i++) {
- if (state->element_map->buf[i].separate) {
- counter++;
- }
- }
+ int unique_uvs = state->element_map->total_unique_uvs;
+ state->total_separate_uvs = unique_uvs;
- /* explicitly set preview to NULL,
- * to avoid deleting an invalid pointer on stitch_process_data */
- state->stitch_preview = NULL;
/* Allocate the unique uv buffers */
- state->uvs = MEM_mallocN(sizeof(*state->uvs) * counter, "uv_stitch_unique_uvs");
+ state->uvs = MEM_mallocN(sizeof(*state->uvs) * unique_uvs, "uv_stitch_unique_uvs");
/* internal uvs need no normals but it is hard and slow to keep a map of
- * normals only for boundary uvs, so allocating for all uvs */
- state->normals = MEM_callocN(sizeof(*state->normals) * counter * 2, "uv_stitch_normals");
- state->total_separate_uvs = counter;
- state->map = map = MEM_mallocN(sizeof(*map) * state->element_map->totalUVs,
+ * normals only for boundary uvs, so allocating for all uvs.
+ * Times 2 because each `float[2]` is stored as `{n[2 * i], n[2*i + 1]}`. */
+ state->normals = MEM_callocN(sizeof(*state->normals) * 2 * unique_uvs, "uv_stitch_normals");
+ state->map = map = MEM_mallocN(sizeof(*map) * state->element_map->total_uvs,
"uv_stitch_unique_map");
/* Allocate the edge stack */
edge_hash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "stitch_edge_hash");
- all_edges = MEM_mallocN(sizeof(*all_edges) * state->element_map->totalUVs, "ssc_edges");
+ all_edges = MEM_mallocN(sizeof(*all_edges) * state->element_map->total_uvs, "ssc_edges");
+ BLI_assert(!state->stitch_preview); /* Paranoia. */
if (!state->uvs || !map || !edge_hash || !all_edges) {
state_delete(state);
return NULL;
}
- /* So that we can use this as index for the UvElements */
- counter = -1;
+ /* Index for the UvElements. */
+ int counter = -1;
/* initialize the unique UVs and map */
- for (i = 0; i < em->bm->totvert; i++) {
- UvElement *element = state->element_map->vert[i];
+ for (int i = 0; i < em->bm->totvert; i++) {
+ UvElement *element = state->element_map->vertex[i];
for (; element; element = element->next) {
if (element->separate) {
counter++;
state->uvs[counter] = element;
}
/* Pointer arithmetic to the rescue, as always :). */
- map[element - state->element_map->buf] = counter;
+ map[element - state->element_map->storage] = counter;
}
}
@@ -1973,13 +1912,13 @@ static StitchState *stitch_init(bContext *C,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
UvElement *element = BM_uv_element_get(state->element_map, efa, l);
- int offset1, itmp1 = element - state->element_map->buf;
- int offset2,
- itmp2 = BM_uv_element_get(state->element_map, efa, l->next) - state->element_map->buf;
+ int itmp1 = element - state->element_map->storage;
+ int itmp2 = BM_uv_element_get(state->element_map, efa, l->next) -
+ state->element_map->storage;
UvEdge *edge;
- offset1 = map[itmp1];
- offset2 = map[itmp2];
+ int offset1 = map[itmp1];
+ int offset2 = map[itmp2];
all_edges[counter].next = NULL;
all_edges[counter].first = NULL;
@@ -2020,7 +1959,7 @@ static StitchState *stitch_init(bContext *C,
state->total_separate_edges = total_edges;
/* fill the edges with data */
- i = 0;
+ int i = 0;
GHASH_ITER (gh_iter, edge_hash) {
edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter));
}
@@ -2099,13 +2038,13 @@ static StitchState *stitch_init(bContext *C,
efa = BM_face_at_index(em->bm, faceIndex);
element = BM_uv_element_get(
state->element_map, efa, BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, elementIndex));
- uv1 = map[element - state->element_map->buf];
+ uv1 = map[element - state->element_map->storage];
element = BM_uv_element_get(
state->element_map,
efa,
BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, (elementIndex + 1) % efa->len));
- uv2 = map[element - state->element_map->buf];
+ uv2 = map[element - state->element_map->storage];
if (uv1 < uv2) {
tmp_edge.uv1 = uv1;
@@ -2170,8 +2109,8 @@ static StitchState *stitch_init(bContext *C,
/***** initialize static island preview data *****/
state->tris_per_island = MEM_mallocN(
- sizeof(*state->tris_per_island) * state->element_map->totalIslands, "stitch island tris");
- for (i = 0; i < state->element_map->totalIslands; i++) {
+ sizeof(*state->tris_per_island) * state->element_map->total_islands, "stitch island tris");
+ for (i = 0; i < state->element_map->total_islands; i++) {
state->tris_per_island[i] = 0;
}
@@ -2183,7 +2122,7 @@ static StitchState *stitch_init(bContext *C,
}
}
- state->island_is_stitchable = MEM_callocN(sizeof(bool) * state->element_map->totalIslands,
+ state->island_is_stitchable = MEM_callocN(sizeof(bool) * state->element_map->total_islands,
"stitch I stops");
if (!state->island_is_stitchable) {
state_delete(state);
@@ -2207,7 +2146,7 @@ static bool goto_next_island(StitchStateContainer *ssc)
do {
ssc->static_island++;
- if (ssc->static_island >= active_state->element_map->totalIslands) {
+ if (ssc->static_island >= active_state->element_map->total_islands) {
/* go to next object */
ssc->active_object_index++;
ssc->active_object_index %= ssc->objects_len;
@@ -2276,7 +2215,7 @@ static int stitch_init_all(bContext *C, wmOperator *op)
View3D *v3d = CTX_wm_view3d(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
if (objects_len == 0) {
MEM_freeN(objects);
@@ -2357,7 +2296,7 @@ static int stitch_init_all(bContext *C, wmOperator *op)
ssc->static_island = RNA_int_get(op->ptr, "static_island");
StitchState *state = ssc->states[ssc->active_object_index];
- ssc->static_island %= state->element_map->totalIslands;
+ ssc->static_island %= state->element_map->total_islands;
/* If the initial active object doesn't have any stitchable islands
* then no active island will be seen in the UI.
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index ae81aaffeb2..e66629f8fb0 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -798,7 +798,7 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
MEM_freeN(objects);
@@ -1116,7 +1116,7 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
/* Early exit in case no UVs are selected. */
if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
@@ -1191,7 +1191,7 @@ void UV_OT_pack_islands(wmOperatorType *ot)
/** \name Average UV Islands Scale Operator
* \{ */
-static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
+static int average_islands_scale_exec(bContext *C, wmOperator *op)
{
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1208,15 +1208,19 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) {
MEM_freeN(objects);
return OPERATOR_CANCELLED;
}
+ /* RNA props */
+ const bool scale_uv = RNA_boolean_get(op->ptr, "scale_uv");
+ const bool shear = RNA_boolean_get(op->ptr, "shear");
+
ParamHandle *handle = construct_param_handle_multi(scene, objects, objects_len, &options);
- GEO_uv_parametrizer_average(handle, false);
+ GEO_uv_parametrizer_average(handle, false, scale_uv, shear);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
@@ -1247,6 +1251,10 @@ void UV_OT_average_islands_scale(wmOperatorType *ot)
/* api callbacks */
ot->exec = average_islands_scale_exec;
ot->poll = ED_operator_uvedit;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "scale_uv", false, "Non-Uniform", "Scale U and V independently");
+ RNA_def_boolean(ot->srna, "shear", false, "Shear", "Reduce shear within islands");
}
/** \} */
@@ -1706,10 +1714,12 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot)
* such as "Unwrap" & "Smart UV Projections" will need to handle aspect correction themselves.
* For now keep using a single aspect for all faces in this case.
*/
-static void uv_map_clip_correct_multi(Object **objects,
- uint objects_len,
- wmOperator *op,
- bool per_face_aspect)
+static void uv_map_clip_correct(const Scene *scene,
+ Object **objects,
+ uint objects_len,
+ wmOperator *op,
+ bool per_face_aspect,
+ bool only_selected_uvs)
{
BMFace *efa;
BMLoop *l;
@@ -1746,6 +1756,10 @@ static void uv_map_clip_correct_multi(Object **objects,
continue;
}
+ if (only_selected_uvs && !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ continue;
+ }
+
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
minmax_v2v2_v2(min, max, luv->uv);
@@ -1759,6 +1773,10 @@ static void uv_map_clip_correct_multi(Object **objects,
continue;
}
+ if (only_selected_uvs && !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ continue;
+ }
+
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
clamp_v2(luv->uv, 0.0f, 1.0f);
@@ -1795,6 +1813,10 @@ static void uv_map_clip_correct_multi(Object **objects,
continue;
}
+ if (only_selected_uvs && !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ continue;
+ }
+
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
@@ -1806,18 +1828,13 @@ static void uv_map_clip_correct_multi(Object **objects,
}
}
-static void uv_map_clip_correct(Object *ob, wmOperator *op)
-{
- uv_map_clip_correct_multi(&ob, 1, op, true);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
/** \name UV Unwrap Operator
* \{ */
-/* Assumes UV Map exists, doesn't run update funcs. */
+/* Assumes UV Map exists, doesn't run update functions. */
static void uvedit_unwrap(const Scene *scene,
Object *obedit,
const UnwrapOptions *options,
@@ -1845,7 +1862,7 @@ static void uvedit_unwrap(const Scene *scene,
result_info ? &result_info->count_failed : NULL);
GEO_uv_parametrizer_lscm_end(handle);
- GEO_uv_parametrizer_average(handle, true);
+ GEO_uv_parametrizer_average(handle, true, false, false);
GEO_uv_parametrizer_flush(handle);
@@ -1903,11 +1920,11 @@ static int unwrap_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
UnwrapOptions options = {
.topology_from_uvs = false,
- .only_selected_faces = false,
+ .only_selected_faces = true,
.only_selected_uvs = false,
.fill_holes = RNA_boolean_get(op->ptr, "fill_holes"),
.correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"),
@@ -1918,7 +1935,6 @@ static int unwrap_exec(bContext *C, wmOperator *op)
if (CTX_wm_space_image(C)) {
/* Inside the UV Editor, only unwrap selected UVs. */
options.only_selected_uvs = true;
- options.only_selected_faces = true;
options.pin_unselected = true;
}
@@ -2238,6 +2254,12 @@ static int smart_project_exec(bContext *C, wmOperator *op)
/* May be NULL. */
View3D *v3d = CTX_wm_view3d(C);
+ bool only_selected_uvs = false;
+ if (CTX_wm_space_image(C)) {
+ /* Inside the UV Editor, only project selected UVs. */
+ only_selected_uvs = true;
+ }
+
const float project_angle_limit = RNA_float_get(op->ptr, "angle_limit");
const float island_margin = RNA_float_get(op->ptr, "island_margin");
const float area_weight = RNA_float_get(op->ptr, "area_weight");
@@ -2250,7 +2272,7 @@ static int smart_project_exec(bContext *C, wmOperator *op)
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
Object **objects_changed = MEM_mallocN(sizeof(*objects_changed) * objects_len, __func__);
uint object_changed_len = 0;
@@ -2268,7 +2290,8 @@ static int smart_project_exec(bContext *C, wmOperator *op)
continue;
}
- const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+ BLI_assert(cd_loop_uv_offset >= 0);
ThickFace *thick_faces = MEM_mallocN(sizeof(*thick_faces) * em->bm->totface, __func__);
uint thick_faces_len = 0;
@@ -2276,6 +2299,14 @@ static int smart_project_exec(bContext *C, wmOperator *op)
if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
continue;
}
+
+ if (only_selected_uvs) {
+ if (!uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ uvedit_face_select_disable(scene, em->bm, efa, cd_loop_uv_offset);
+ continue;
+ }
+ }
+
thick_faces[thick_faces_len].area = BM_face_calc_area(efa);
thick_faces[thick_faces_len].efa = efa;
thick_faces_len++;
@@ -2390,6 +2421,7 @@ static int smart_project_exec(bContext *C, wmOperator *op)
.rotate = true,
/* We could make this optional. */
.rotate_align_axis = 1,
+ .only_selected_uvs = true,
.only_selected_faces = true,
.correct_aspect = correct_aspect,
.use_seams = true,
@@ -2397,7 +2429,8 @@ static int smart_project_exec(bContext *C, wmOperator *op)
/* #ED_uvedit_pack_islands_multi only supports `per_face_aspect = false`. */
const bool per_face_aspect = false;
- uv_map_clip_correct_multi(objects_changed, object_changed_len, op, per_face_aspect);
+ uv_map_clip_correct(
+ scene, objects_changed, object_changed_len, op, per_face_aspect, only_selected_uvs);
}
MEM_freeN(objects_changed);
@@ -2504,7 +2537,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
/* NOTE: objects that aren't touched are set to NULL (to skip clipping). */
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
if (use_orthographic) {
/* Calculate average object position. */
@@ -2599,7 +2632,9 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
}
if (changed_multi) {
- uv_map_clip_correct_multi(objects, objects_len, op, true);
+ const bool per_face_aspect = true;
+ const bool only_selected_uvs = false;
+ uv_map_clip_correct(scene, objects, objects_len, op, per_face_aspect, only_selected_uvs);
}
MEM_freeN(objects);
@@ -2653,12 +2688,13 @@ void UV_OT_project_from_view(wmOperatorType *ot)
static int reset_exec(bContext *C, wmOperator *UNUSED(op))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
View3D *v3d = CTX_wm_view3d(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
Mesh *me = (Mesh *)obedit->data;
@@ -2759,10 +2795,16 @@ static int sphere_project_exec(bContext *C, wmOperator *op)
const Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
+ bool only_selected_uvs = false;
+ if (CTX_wm_space_image(C)) {
+ /* Inside the UV Editor, only project selected UVs. */
+ only_selected_uvs = true;
+ }
+
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2791,6 +2833,13 @@ static int sphere_project_exec(bContext *C, wmOperator *op)
continue;
}
+ if (only_selected_uvs) {
+ if (!uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ uvedit_face_select_disable(scene, em->bm, efa, cd_loop_uv_offset);
+ continue;
+ }
+ }
+
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
@@ -2800,7 +2849,8 @@ static int sphere_project_exec(bContext *C, wmOperator *op)
uv_map_mirror(em, efa);
}
- uv_map_clip_correct(obedit, op);
+ const bool per_face_aspect = true;
+ uv_map_clip_correct(scene, &obedit, 1, op, per_face_aspect, only_selected_uvs);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
@@ -2857,10 +2907,16 @@ static int cylinder_project_exec(bContext *C, wmOperator *op)
const Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
+ bool only_selected_uvs = false;
+ if (CTX_wm_space_image(C)) {
+ /* Inside the UV Editor, only project selected UVs. */
+ only_selected_uvs = true;
+ }
+
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -2889,16 +2945,21 @@ static int cylinder_project_exec(bContext *C, wmOperator *op)
continue;
}
+ if (only_selected_uvs && !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ uvedit_face_select_disable(scene, em->bm, efa, cd_loop_uv_offset);
+ continue;
+ }
+
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-
uv_cylinder_project(luv->uv, l->v->co, center, rotmat);
}
uv_map_mirror(em, efa);
}
- uv_map_clip_correct(obedit, op);
+ const bool per_face_aspect = true;
+ uv_map_clip_correct(scene, &obedit, 1, op, per_face_aspect, only_selected_uvs);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
@@ -2932,9 +2993,11 @@ void UV_OT_cylinder_project(wmOperatorType *ot)
/** \name Cube UV Project Operator
* \{ */
-static void uvedit_unwrap_cube_project(BMesh *bm,
+static void uvedit_unwrap_cube_project(const Scene *scene,
+ BMesh *bm,
float cube_size,
- bool use_select,
+ const bool use_select,
+ const bool only_selected_uvs,
const float center[3])
{
BMFace *efa;
@@ -2966,6 +3029,10 @@ static void uvedit_unwrap_cube_project(BMesh *bm,
if (use_select && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
continue;
}
+ if (only_selected_uvs && !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ uvedit_face_select_disable(scene, bm, efa, cd_loop_uv_offset);
+ continue;
+ }
axis_dominant_v3(&cox, &coy, efa->no);
@@ -2982,13 +3049,19 @@ static int cube_project_exec(bContext *C, wmOperator *op)
const Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
+ bool only_selected_uvs = false;
+ if (CTX_wm_space_image(C)) {
+ /* Inside the UV Editor, only cube project selected UVs. */
+ only_selected_uvs = true;
+ }
+
PropertyRNA *prop_cube_size = RNA_struct_find_property(op->ptr, "cube_size");
const float cube_size_init = RNA_property_float_get(op->ptr, prop_cube_size);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len = 0;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, v3d, &objects_len);
+ scene, view_layer, v3d, &objects_len);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -3024,9 +3097,10 @@ static int cube_project_exec(bContext *C, wmOperator *op)
}
}
- uvedit_unwrap_cube_project(em->bm, cube_size, true, center);
+ uvedit_unwrap_cube_project(scene, em->bm, cube_size, true, only_selected_uvs, center);
- uv_map_clip_correct(obedit, op);
+ const bool per_face_aspect = true;
+ uv_map_clip_correct(scene, &obedit, 1, op, per_face_aspect, only_selected_uvs);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
@@ -3093,7 +3167,7 @@ void ED_uvedit_add_simple_uvs(Main *bmain, const Scene *scene, Object *ob)
/* select all uv loops first - pack parameters needs this to make sure charts are registered */
ED_uvedit_select_all(bm);
/* A cube size of 2.0 maps [-1..1] vertex coords to [0.0..1.0] in UV coords. */
- uvedit_unwrap_cube_project(bm, 2.0, false, NULL);
+ uvedit_unwrap_cube_project(scene, bm, 2.0, false, false, NULL);
/* Set the margin really quickly before the packing operation. */
scene->toolsettings->uvcalc_margin = 0.001f;
uvedit_pack_islands(scene, ob, bm);
diff --git a/source/blender/freestyle/CMakeLists.txt b/source/blender/freestyle/CMakeLists.txt
index c2fad9fef3a..8f9e493023c 100644
--- a/source/blender/freestyle/CMakeLists.txt
+++ b/source/blender/freestyle/CMakeLists.txt
@@ -548,7 +548,6 @@ set(INC
../python/intern
../render
../render/intern
- ../../../extern/glew/include
../../../intern/guardedalloc
# RNA_prototypes.h
@@ -562,6 +561,10 @@ set(INC_SYS
add_definitions(-DWITH_FREESTYLE)
+if(WITH_PYTHON_MODULE)
+ add_definitions(-DPy_ENABLE_SHARED)
+endif()
+
if(WITH_PYTHON_SAFETY)
# For bpy_rna.h access.
add_definitions(-DWITH_PYTHON_SAFETY)
diff --git a/source/blender/freestyle/intern/application/Controller.h b/source/blender/freestyle/intern/application/Controller.h
index b5ef0fba1f7..8e59b277ff3 100644
--- a/source/blender/freestyle/intern/application/Controller.h
+++ b/source/blender/freestyle/intern/application/Controller.h
@@ -20,6 +20,10 @@
# include "MEM_guardedalloc.h"
#endif
+struct Depsgraph;
+struct Render;
+struct ViewLayer;
+
namespace Freestyle {
class AppCanvas;
diff --git a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
index e76e74b89e4..c4a633e920e 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
+++ b/source/blender/freestyle/intern/blender_interface/BlenderFileLoader.cpp
@@ -8,11 +8,14 @@
#include "BLI_utildefines.h"
+#include "BKE_attribute.hh"
#include "BKE_global.h"
#include "BKE_object.h"
#include <sstream>
+using blender::Span;
+
namespace Freestyle {
BlenderFileLoader::BlenderFileLoader(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph)
@@ -77,6 +80,11 @@ NodeGroup *BlenderFileLoader::Load()
continue;
}
+ /* Evaluated metaballs will appear as mesh objects in the iterator. */
+ if (ob->type == OB_MBALL) {
+ continue;
+ }
+
Mesh *mesh = BKE_object_to_mesh(nullptr, ob, false);
if (mesh) {
@@ -372,9 +380,12 @@ int BlenderFileLoader::testDegenerateTriangle(float v1[3], float v2[3], float v3
static bool testEdgeMark(Mesh *me, const FreestyleEdge *fed, const MLoopTri *lt, int i)
{
- MLoop *mloop = &me->mloop[lt->tri[i]];
- MLoop *mloop_next = &me->mloop[lt->tri[(i + 1) % 3]];
- MEdge *medge = &me->medge[mloop->e];
+ const Span<MEdge> edges = me->edges();
+ const Span<MLoop> loops = me->loops();
+
+ const MLoop *mloop = &loops[lt->tri[i]];
+ const MLoop *mloop_next = &loops[lt->tri[(i + 1) % 3]];
+ const MEdge *medge = &edges[mloop->e];
if (!ELEM(mloop_next->v, medge->v1, medge->v2)) {
/* Not an edge in the original mesh before triangulation. */
@@ -388,10 +399,15 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id)
{
char *name = ob->id.name + 2;
+ const Span<MVert> mesh_verts = me->verts();
+ const Span<MPoly> mesh_polys = me->polys();
+ const Span<MLoop> mesh_loops = me->loops();
+
// Compute loop triangles
int tottri = poly_to_tri_count(me->totpoly, me->totloop);
MLoopTri *mlooptri = (MLoopTri *)MEM_malloc_arrayN(tottri, sizeof(*mlooptri), __func__);
- BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mlooptri);
+ BKE_mesh_recalc_looptri(
+ mesh_loops.data(), mesh_polys.data(), mesh_verts.data(), me->totloop, me->totpoly, mlooptri);
// Compute loop normals
BKE_mesh_calc_normals_split(me);
@@ -402,9 +418,6 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id)
}
// Get other mesh data
- MVert *mvert = me->mvert;
- MLoop *mloop = me->mloop;
- MPoly *mpoly = me->mpoly;
const FreestyleEdge *fed = (FreestyleEdge *)CustomData_get_layer(&me->edata, CD_FREESTYLE_EDGE);
const FreestyleFace *ffa = (FreestyleFace *)CustomData_get_layer(&me->pdata, CD_FREESTYLE_FACE);
@@ -429,9 +442,9 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id)
for (int a = 0; a < tottri; a++) {
const MLoopTri *lt = &mlooptri[a];
- copy_v3_v3(v1, mvert[mloop[lt->tri[0]].v].co);
- copy_v3_v3(v2, mvert[mloop[lt->tri[1]].v].co);
- copy_v3_v3(v3, mvert[mloop[lt->tri[2]].v].co);
+ copy_v3_v3(v1, mesh_verts[mesh_loops[lt->tri[0]].v].co);
+ copy_v3_v3(v2, mesh_verts[mesh_loops[lt->tri[1]].v].co);
+ copy_v3_v3(v3, mesh_verts[mesh_loops[lt->tri[2]].v].co);
mul_m4_v3(obmat, v1);
mul_m4_v3(obmat, v2);
@@ -492,16 +505,19 @@ void BlenderFileLoader::insertShapeNode(Object *ob, Mesh *me, int id)
FrsMaterial tmpMat;
+ const blender::VArray<int> material_indices = me->attributes().lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+
// We parse the vlak nodes again and import meshes while applying the clipping
// by the near and far view planes.
for (int a = 0; a < tottri; a++) {
const MLoopTri *lt = &mlooptri[a];
- const MPoly *mp = &mpoly[lt->poly];
- Material *mat = BKE_object_material_get(ob, mp->mat_nr + 1);
+ const MPoly *mp = &mesh_polys[lt->poly];
+ Material *mat = BKE_object_material_get(ob, material_indices[lt->poly] + 1);
- copy_v3_v3(v1, mvert[mloop[lt->tri[0]].v].co);
- copy_v3_v3(v2, mvert[mloop[lt->tri[1]].v].co);
- copy_v3_v3(v3, mvert[mloop[lt->tri[2]].v].co);
+ copy_v3_v3(v1, mesh_verts[mesh_loops[lt->tri[0]].v].co);
+ copy_v3_v3(v2, mesh_verts[mesh_loops[lt->tri[1]].v].co);
+ copy_v3_v3(v3, mesh_verts[mesh_loops[lt->tri[2]].v].co);
mul_m4_v3(obmat, v1);
mul_m4_v3(obmat, v2);
diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
index 979673fd736..a3085768ea3 100644
--- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
+++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
@@ -32,6 +32,7 @@
#include "BKE_idprop.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h" /* free_libblock */
+#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_node.h"
@@ -127,7 +128,8 @@ BlenderStrokeRenderer::BlenderStrokeRenderer(Render *re, int render_count)
view_layer->layflag = SCE_LAY_SOLID;
// Camera
- Object *object_camera = BKE_object_add(freestyle_bmain, view_layer, OB_CAMERA, nullptr);
+ Object *object_camera = BKE_object_add(
+ freestyle_bmain, freestyle_scene, view_layer, OB_CAMERA, nullptr);
Camera *camera = (Camera *)object_camera->data;
camera->type = CAM_ORTHO;
@@ -217,12 +219,12 @@ Material *BlenderStrokeRenderer::GetStrokeShader(Main *bmain,
break;
}
}
+ ma->nodetree = ntree;
}
else {
- ntree = ntreeAddTree(nullptr, "stroke_shader", "ShaderNodeTree");
+ ntree = ntreeAddTreeEmbedded(nullptr, &ma->id, "stroke_shader", "ShaderNodeTree");
}
- ma->nodetree = ntree;
- ma->use_nodes = 1;
+ ma->use_nodes = true;
ma->blend_method = MA_BM_HASHED;
bNode *input_attr_color = nodeAddStaticNode(nullptr, ntree, SH_NODE_ATTRIBUTE);
@@ -231,7 +233,7 @@ Material *BlenderStrokeRenderer::GetStrokeShader(Main *bmain,
storage = (NodeShaderAttribute *)input_attr_color->storage;
BLI_strncpy(storage->name, "Color", sizeof(storage->name));
- bNode *mix_rgb_color = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX_RGB);
+ bNode *mix_rgb_color = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX_RGB_LEGACY);
mix_rgb_color->custom1 = MA_RAMP_BLEND; // Mix
mix_rgb_color->locx = 200.0f;
mix_rgb_color->locy = -200.0f;
@@ -245,7 +247,7 @@ Material *BlenderStrokeRenderer::GetStrokeShader(Main *bmain,
storage = (NodeShaderAttribute *)input_attr_alpha->storage;
BLI_strncpy(storage->name, "Alpha", sizeof(storage->name));
- bNode *mix_rgb_alpha = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX_RGB);
+ bNode *mix_rgb_alpha = nodeAddStaticNode(nullptr, ntree, SH_NODE_MIX_RGB_LEGACY);
mix_rgb_alpha->custom1 = MA_RAMP_BLEND; // Mix
mix_rgb_alpha->locx = 600.0f;
mix_rgb_alpha->locy = 300.0f;
@@ -575,43 +577,36 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex)
mesh->totloop = group->totloop;
mesh->totcol = group->materials.size();
- mesh->mvert = (MVert *)CustomData_add_layer(
- &mesh->vdata, CD_MVERT, CD_CALLOC, nullptr, mesh->totvert);
- mesh->medge = (MEdge *)CustomData_add_layer(
- &mesh->edata, CD_MEDGE, CD_CALLOC, nullptr, mesh->totedge);
- mesh->mpoly = (MPoly *)CustomData_add_layer(
- &mesh->pdata, CD_MPOLY, CD_CALLOC, nullptr, mesh->totpoly);
- mesh->mloop = (MLoop *)CustomData_add_layer(
- &mesh->ldata, CD_MLOOP, CD_CALLOC, nullptr, mesh->totloop);
-
- MVert *vertices = mesh->mvert;
- MEdge *edges = mesh->medge;
- MPoly *polys = mesh->mpoly;
- MLoop *loops = mesh->mloop;
+ MVert *verts = (MVert *)CustomData_add_layer(
+ &mesh->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, mesh->totvert);
+ MEdge *edges = (MEdge *)CustomData_add_layer(
+ &mesh->edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, mesh->totedge);
+ MPoly *polys = (MPoly *)CustomData_add_layer(
+ &mesh->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, mesh->totpoly);
+ MLoop *loops = (MLoop *)CustomData_add_layer(
+ &mesh->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, mesh->totloop);
+ int *material_indices = (int *)CustomData_add_layer_named(
+ &mesh->pdata, CD_PROP_INT32, CD_SET_DEFAULT, nullptr, mesh->totpoly, "material_index");
MLoopUV *loopsuv[2] = {nullptr};
if (hasTex) {
// First UV layer
- CustomData_add_layer_named(
- &mesh->ldata, CD_MLOOPUV, CD_CALLOC, nullptr, mesh->totloop, uvNames[0]);
+ loopsuv[0] = static_cast<MLoopUV *>(CustomData_add_layer_named(
+ &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh->totloop, uvNames[0]));
CustomData_set_layer_active(&mesh->ldata, CD_MLOOPUV, 0);
- BKE_mesh_update_customdata_pointers(mesh, true);
- loopsuv[0] = mesh->mloopuv;
// Second UV layer
- CustomData_add_layer_named(
- &mesh->ldata, CD_MLOOPUV, CD_CALLOC, nullptr, mesh->totloop, uvNames[1]);
+ loopsuv[1] = static_cast<MLoopUV *>(CustomData_add_layer_named(
+ &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh->totloop, uvNames[1]));
CustomData_set_layer_active(&mesh->ldata, CD_MLOOPUV, 1);
- BKE_mesh_update_customdata_pointers(mesh, true);
- loopsuv[1] = mesh->mloopuv;
}
// colors and transparency (the latter represented by grayscale colors)
MLoopCol *colors = (MLoopCol *)CustomData_add_layer_named(
- &mesh->ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, nullptr, mesh->totloop, "Color");
+ &mesh->ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, mesh->totloop, "Color");
MLoopCol *transp = (MLoopCol *)CustomData_add_layer_named(
- &mesh->ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, nullptr, mesh->totloop, "Alpha");
- mesh->mloopcol = colors;
+ &mesh->ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, mesh->totloop, "Alpha");
+ CustomData_set_layer_active(&mesh->ldata, CD_PROP_BYTE_COLOR, 0);
mesh->mat = (Material **)MEM_mallocN(sizeof(Material *) * mesh->totcol, "MaterialList");
for (const auto item : group->materials.items()) {
@@ -669,19 +664,19 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex)
else {
if (!visible) {
// first vertex
- vertices->co[0] = svRep[0]->point2d()[0];
- vertices->co[1] = svRep[0]->point2d()[1];
- vertices->co[2] = get_stroke_vertex_z();
+ verts->co[0] = svRep[0]->point2d()[0];
+ verts->co[1] = svRep[0]->point2d()[1];
+ verts->co[2] = get_stroke_vertex_z();
- ++vertices;
+ ++verts;
++vertex_index;
// second vertex
- vertices->co[0] = svRep[1]->point2d()[0];
- vertices->co[1] = svRep[1]->point2d()[1];
- vertices->co[2] = get_stroke_vertex_z();
+ verts->co[0] = svRep[1]->point2d()[0];
+ verts->co[1] = svRep[1]->point2d()[1];
+ verts->co[2] = get_stroke_vertex_z();
- ++vertices;
+ ++verts;
++vertex_index;
// first edge
@@ -693,10 +688,10 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex)
visible = true;
// vertex
- vertices->co[0] = svRep[2]->point2d()[0];
- vertices->co[1] = svRep[2]->point2d()[1];
- vertices->co[2] = get_stroke_vertex_z();
- ++vertices;
+ verts->co[0] = svRep[2]->point2d()[0];
+ verts->co[1] = svRep[2]->point2d()[1];
+ verts->co[2] = get_stroke_vertex_z();
+ ++verts;
++vertex_index;
// edges
@@ -713,7 +708,8 @@ void BlenderStrokeRenderer::GenerateStrokeMesh(StrokeGroup *group, bool hasTex)
// poly
polys->loopstart = loop_index;
polys->totloop = 3;
- polys->mat_nr = matnr;
+ *material_indices = matnr;
+ ++material_indices;
++polys;
// Even and odd loops connect triangles vertices differently
diff --git a/source/blender/freestyle/intern/geometry/FastGrid.h b/source/blender/freestyle/intern/geometry/FastGrid.h
index 3d9ec6a64ca..b835e109faa 100644
--- a/source/blender/freestyle/intern/geometry/FastGrid.h
+++ b/source/blender/freestyle/intern/geometry/FastGrid.h
@@ -12,7 +12,7 @@
namespace Freestyle {
/** Class to define a regular grid used for ray casting computations
- * We don't use a hashtable here. The grid is explicitly stored for faster computations.
+ * We don't use a hash-table here. The grid is explicitly stored for faster computations.
* However, this might result in significant increase in memory usage
* (compared to the regular grid).
*/
@@ -31,7 +31,7 @@ class FastGrid : public Grid {
/**
* clears the grid
- * Deletes all the cells, clears the hashtable, resets size, size of cell, number of cells.
+ * Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
diff --git a/source/blender/freestyle/intern/geometry/Grid.h b/source/blender/freestyle/intern/geometry/Grid.h
index c25594e620f..d66982eef52 100644
--- a/source/blender/freestyle/intern/geometry/Grid.h
+++ b/source/blender/freestyle/intern/geometry/Grid.h
@@ -187,7 +187,7 @@ class Grid {
}
/** clears the grid
- * Deletes all the cells, clears the hashtable, resets size, size of cell, number of cells.
+ * Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
diff --git a/source/blender/freestyle/intern/geometry/HashGrid.h b/source/blender/freestyle/intern/geometry/HashGrid.h
index b08334d3474..18eeb579d07 100644
--- a/source/blender/freestyle/intern/geometry/HashGrid.h
+++ b/source/blender/freestyle/intern/geometry/HashGrid.h
@@ -52,7 +52,7 @@ class HashGrid : public Grid {
}
/** clears the grid
- * Deletes all the cells, clears the hashtable, resets size, size of cell, number of cells.
+ * Deletes all the cells, clears the hash-table, resets size, size of cell, number of cells.
*/
virtual void clear();
diff --git a/source/blender/freestyle/intern/view_map/ViewMap.cpp b/source/blender/freestyle/intern/view_map/ViewMap.cpp
index b26a833b32e..d918cfec2ae 100644
--- a/source/blender/freestyle/intern/view_map/ViewMap.cpp
+++ b/source/blender/freestyle/intern/view_map/ViewMap.cpp
@@ -398,7 +398,7 @@ void TVertex::setBackEdgeB(ViewEdge *iBackEdgeB, bool incoming)
void TVertex::Replace(ViewEdge *iOld, ViewEdge *iNew)
{
- // theoritically, we only replace edges for which this
+ // theoretically, we only replace edges for which this
// view vertex is the B vertex
if ((iOld == _FrontEdgeA.first) && (_FrontEdgeA.first->B() == this)) {
_FrontEdgeA.first = iNew;
diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt
index f1298a7f5b7..3d153813425 100644
--- a/source/blender/functions/CMakeLists.txt
+++ b/source/blender/functions/CMakeLists.txt
@@ -13,6 +13,10 @@ set(INC_SYS
set(SRC
intern/cpp_types.cc
intern/field.cc
+ intern/lazy_function.cc
+ intern/lazy_function_execute.cc
+ intern/lazy_function_graph.cc
+ intern/lazy_function_graph_executor.cc
intern/multi_function.cc
intern/multi_function_builder.cc
intern/multi_function_params.cc
@@ -23,6 +27,10 @@ set(SRC
FN_field.hh
FN_field_cpp_type.hh
+ FN_lazy_function.hh
+ FN_lazy_function_execute.hh
+ FN_lazy_function_graph.hh
+ FN_lazy_function_graph_executor.hh
FN_multi_function.hh
FN_multi_function_builder.hh
FN_multi_function_context.hh
@@ -61,6 +69,7 @@ blender_add_lib(bf_functions "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
if(WITH_GTESTS)
set(TEST_SRC
tests/FN_field_test.cc
+ tests/FN_lazy_function_test.cc
tests/FN_multi_function_procedure_test.cc
tests/FN_multi_function_test.cc
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index a8136d06c5f..ca12f407e49 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -221,6 +221,17 @@ class FieldOperation : public FieldNode {
const MultiFunction &multi_function() const;
const CPPType &output_cpp_type(int output_index) const override;
+
+ static std::shared_ptr<FieldOperation> Create(std::shared_ptr<const MultiFunction> function,
+ Vector<GField> inputs = {})
+ {
+ return std::make_shared<FieldOperation>(FieldOperation(std::move(function), inputs));
+ }
+ static std::shared_ptr<FieldOperation> Create(const MultiFunction &function,
+ Vector<GField> inputs = {})
+ {
+ return std::make_shared<FieldOperation>(FieldOperation(function, inputs));
+ }
};
class FieldContext;
@@ -554,6 +565,17 @@ template<typename T> struct ValueOrField {
}
return this->value;
}
+
+ friend std::ostream &operator<<(std::ostream &stream, const ValueOrField<T> &value_or_field)
+ {
+ if (value_or_field.field) {
+ stream << "ValueOrField<T>";
+ }
+ else {
+ stream << value_or_field.value;
+ }
+ return stream;
+ }
};
/** \} */
diff --git a/source/blender/functions/FN_field_cpp_type.hh b/source/blender/functions/FN_field_cpp_type.hh
index 63a648f3202..6900a093dc6 100644
--- a/source/blender/functions/FN_field_cpp_type.hh
+++ b/source/blender/functions/FN_field_cpp_type.hh
@@ -59,7 +59,7 @@ class ValueOrFieldCPPType : public CPPType {
public:
template<typename T>
ValueOrFieldCPPType(FieldCPPTypeParam<ValueOrField<T>> /* unused */, StringRef debug_name)
- : CPPType(CPPTypeParam<ValueOrField<T>, CPPTypeFlags::None>(), debug_name),
+ : CPPType(CPPTypeParam<ValueOrField<T>, CPPTypeFlags::Printable>(), debug_name),
base_type_(CPPType::get<T>())
{
construct_from_value_ = [](void *dst, const void *value_or_field) {
diff --git a/source/blender/functions/FN_lazy_function.hh b/source/blender/functions/FN_lazy_function.hh
new file mode 100644
index 00000000000..4a539e7cbd1
--- /dev/null
+++ b/source/blender/functions/FN_lazy_function.hh
@@ -0,0 +1,442 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup fn
+ *
+ * A `LazyFunction` encapsulates a computation which has inputs, outputs and potentially side
+ * effects. Most importantly, a `LazyFunction` supports laziness in its inputs and outputs:
+ * - Only outputs that are actually used have to be computed.
+ * - Inputs can be requested lazily based on which outputs are used or what side effects the
+ * function has.
+ *
+ * A lazy-function that uses laziness may be executed more than once. The most common example is
+ * the geometry nodes switch node. Depending on a condition input, it decides which one of the
+ * other inputs is actually used. From the perspective of the switch node, its execution works as
+ * follows:
+ * 1. The switch node is first executed. It sees that the output is used. Now it requests the
+ * condition input from the caller and exits.
+ * 2. Once the caller is able to provide the condition input the switch node is executed again.
+ * This time it retrieves the condition and requests one of the other inputs. Then the node
+ * exits again, giving back control to the caller.
+ * 3. When the caller computed the second requested input the switch node executes a last time.
+ * This time it retrieves the new input and forwards it to the output.
+ *
+ * In some sense, a lazy-function can be thought of like a state machine. Every time it is
+ * executed, it advances its state until all required outputs are ready.
+ *
+ * The lazy-function interface is designed to support composition of many such functions into a new
+ * lazy-functions, all while keeping the laziness working. For example, in geometry nodes a switch
+ * node in a node group should still be able to decide whether a node in the parent group will be
+ * executed or not. This is essential to avoid doing unnecessary work.
+ *
+ * The lazy-function system consists of multiple core components:
+ * - The interface of a lazy-function itself including its calling convention.
+ * - A graph data structure that allows composing many lazy-functions by connecting their inputs
+ * and outputs.
+ * - An executor that allows multi-threaded execution or such a graph.
+ */
+
+#include "BLI_cpp_type.hh"
+#include "BLI_generic_pointer.hh"
+#include "BLI_linear_allocator.hh"
+#include "BLI_vector.hh"
+
+#include <atomic>
+#include <thread>
+
+#ifdef DEBUG
+# define FN_LAZY_FUNCTION_DEBUG_THREADS
+#endif
+
+namespace blender::fn::lazy_function {
+
+enum class ValueUsage {
+ /**
+ * The value is definitely used and therefore has to be computed.
+ */
+ Used,
+ /**
+ * It's unknown whether this value will be used or not. Computing it is ok but the result may be
+ * discarded.
+ */
+ Maybe,
+ /**
+ * The value will definitely not be used. It can still be computed but the result will be
+ * discarded in all cases.
+ */
+ Unused,
+};
+
+class LazyFunction;
+
+/**
+ * This allows passing arbitrary data into a lazy-function during execution. For that, #UserData
+ * has to be subclassed. This mainly exists because it's more type safe than passing a `void *`
+ * with no type information attached.
+ *
+ * Some lazy-functions may expect to find a certain type of user data when executed.
+ */
+class UserData {
+ public:
+ virtual ~UserData() = default;
+};
+
+/**
+ * Passed to the lazy-function when it is executed.
+ */
+struct Context {
+ /**
+ * If the lazy-function has some state (which only makes sense when it is executed more than once
+ * to finish its job), the state is stored here. This points to memory returned from
+ * #LazyFunction::init_storage.
+ */
+ void *storage;
+ /**
+ * Custom user data that can be used in the function.
+ */
+ UserData *user_data;
+};
+
+/**
+ * Defines the calling convention for a lazy-function. During execution, a lazy-function retrieves
+ * its inputs and sets the outputs through #Params.
+ */
+class Params {
+ public:
+ /**
+ * The lazy-function this #Params has been prepared for.
+ */
+ const LazyFunction &fn_;
+#ifdef FN_LAZY_FUNCTION_DEBUG_THREADS
+ std::thread::id main_thread_id_;
+ std::atomic<bool> allow_multi_threading_;
+#endif
+
+ public:
+ Params(const LazyFunction &fn, bool allow_multi_threading_initially);
+
+ /**
+ * Get a pointer to an input value if the value is available already. Otherwise null is returned.
+ *
+ * The #LazyFunction must leave returned object in an initialized state, but can move from it.
+ */
+ void *try_get_input_data_ptr(int index) const;
+
+ /**
+ * Same as #try_get_input_data_ptr, but if the data is not yet available, request it. This makes
+ * sure that the data will be available in a future execution of the #LazyFunction.
+ */
+ void *try_get_input_data_ptr_or_request(int index);
+
+ /**
+ * Get a pointer to where the output value should be stored.
+ * The value at the pointer is in an uninitialized state at first.
+ * The #LazyFunction is responsible for initializing the value.
+ * After the output has been initialized to its final value, #output_set has to be called.
+ */
+ void *get_output_data_ptr(int index);
+
+ /**
+ * Call this after the output value is initialized. After this is called, the value must not be
+ * touched anymore. It may be moved or destructed immediately.
+ */
+ void output_set(int index);
+
+ /**
+ * Allows the #LazyFunction to check whether an output was computed already without keeping
+ * track of it itself.
+ */
+ bool output_was_set(int index) const;
+
+ /**
+ * Can be used to detect which outputs have to be computed.
+ */
+ ValueUsage get_output_usage(int index) const;
+
+ /**
+ * Tell the caller of the #LazyFunction that a specific input will definitely not be used.
+ * Only an input that was not #ValueUsage::Used can become unused.
+ */
+ void set_input_unused(int index);
+
+ /**
+ * Typed utility methods that wrap the methods above.
+ */
+ template<typename T> T extract_input(int index);
+ template<typename T> const T &get_input(int index) const;
+ template<typename T> T *try_get_input_data_ptr_or_request(int index);
+ template<typename T> void set_output(int index, T &&value);
+
+ /**
+ * Utility to initialize all outputs that haven't been set yet.
+ */
+ void set_default_remaining_outputs();
+
+ /**
+ * Returns true when the lazy-function is now allowed to use multi-threading when interacting
+ * with this #Params. That means, it is allowed to call non-const methods from different threads.
+ */
+ bool try_enable_multi_threading();
+
+ private:
+ void assert_valid_thread() const;
+
+ /**
+ * Methods that need to be implemented by subclasses. Those are separate from the non-virtual
+ * methods above to make it easy to insert additional debugging logic on top of the
+ * implementations.
+ */
+ virtual void *try_get_input_data_ptr_impl(int index) const = 0;
+ virtual void *try_get_input_data_ptr_or_request_impl(int index) = 0;
+ virtual void *get_output_data_ptr_impl(int index) = 0;
+ virtual void output_set_impl(int index) = 0;
+ virtual bool output_was_set_impl(int index) const = 0;
+ virtual ValueUsage get_output_usage_impl(int index) const = 0;
+ virtual void set_input_unused_impl(int index) = 0;
+ virtual bool try_enable_multi_threading_impl();
+};
+
+/**
+ * Describes an input of a #LazyFunction.
+ */
+struct Input {
+ /**
+ * Name used for debugging purposes. The string has to be static or has to be owned by something
+ * else.
+ */
+ const char *debug_name;
+ /**
+ * Data type of this input.
+ */
+ const CPPType *type;
+ /**
+ * Can be used to indicate a caller or this function if this input is used statically before
+ * executing it the first time. This is technically not needed but can improve efficiency because
+ * a round-trip through the `execute` method can be avoided.
+ *
+ * When this is #ValueUsage::Used, the caller has to ensure that the input is definitely
+ * available when the #execute method is first called. The #execute method does not have to check
+ * whether the value is actually available.
+ */
+ ValueUsage usage;
+
+ Input(const char *debug_name, const CPPType &type, const ValueUsage usage = ValueUsage::Used)
+ : debug_name(debug_name), type(&type), usage(usage)
+ {
+ }
+};
+
+struct Output {
+ /**
+ * Name used for debugging purposes. The string has to be static or has to be owned by something
+ * else.
+ */
+ const char *debug_name;
+ /**
+ * Data type of this output.
+ */
+ const CPPType *type = nullptr;
+
+ Output(const char *debug_name, const CPPType &type) : debug_name(debug_name), type(&type)
+ {
+ }
+};
+
+/**
+ * A function that can compute outputs and request inputs lazily. For more details see the comment
+ * at the top of the file.
+ */
+class LazyFunction {
+ protected:
+ const char *debug_name_ = "<unknown>";
+ Vector<Input> inputs_;
+ Vector<Output> outputs_;
+
+ public:
+ virtual ~LazyFunction() = default;
+
+ /**
+ * Get a name of the function or an input or output. This is mainly used for debugging.
+ * These are virtual functions because the names are often not used outside of debugging
+ * workflows. This way the names are only generated when they are actually needed.
+ */
+ virtual std::string name() const;
+ virtual std::string input_name(int index) const;
+ virtual std::string output_name(int index) const;
+
+ /**
+ * Allocates storage for this function. The storage will be passed to every call to #execute.
+ * If the function does not keep track of any state, this does not have to be implemented.
+ */
+ virtual void *init_storage(LinearAllocator<> &allocator) const;
+
+ /**
+ * Destruct the storage created in #init_storage.
+ */
+ virtual void destruct_storage(void *storage) const;
+
+ /**
+ * Inputs of the function.
+ */
+ Span<Input> inputs() const;
+ /**
+ * Outputs of the function.
+ */
+ Span<Output> outputs() const;
+
+ /**
+ * During execution the function retrieves inputs and sets outputs in #params. For some
+ * functions, this method is called more than once. After execution, the function either has
+ * computed all required outputs or is waiting for more inputs.
+ */
+ void execute(Params &params, const Context &context) const;
+
+ /**
+ * Utility to check that the guarantee by #Input::usage is followed.
+ */
+ bool always_used_inputs_available(const Params &params) const;
+
+ private:
+ /**
+ * Needs to be implemented by subclasses. This is separate from #execute so that additional
+ * debugging logic can be implemented in #execute.
+ */
+ virtual void execute_impl(Params &params, const Context &context) const = 0;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name #LazyFunction Inline Methods
+ * \{ */
+
+inline Span<Input> LazyFunction::inputs() const
+{
+ return inputs_;
+}
+
+inline Span<Output> LazyFunction::outputs() const
+{
+ return outputs_;
+}
+
+inline void LazyFunction::execute(Params &params, const Context &context) const
+{
+ BLI_assert(this->always_used_inputs_available(params));
+ this->execute_impl(params, context);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #Params Inline Methods
+ * \{ */
+
+inline Params::Params(const LazyFunction &fn,
+ [[maybe_unused]] bool allow_multi_threading_initially)
+ : fn_(fn)
+#ifdef FN_LAZY_FUNCTION_DEBUG_THREADS
+ ,
+ main_thread_id_(std::this_thread::get_id()),
+ allow_multi_threading_(allow_multi_threading_initially)
+#endif
+{
+}
+
+inline void *Params::try_get_input_data_ptr(const int index) const
+{
+ return this->try_get_input_data_ptr_impl(index);
+}
+
+inline void *Params::try_get_input_data_ptr_or_request(const int index)
+{
+ this->assert_valid_thread();
+ return this->try_get_input_data_ptr_or_request_impl(index);
+}
+
+inline void *Params::get_output_data_ptr(const int index)
+{
+ this->assert_valid_thread();
+ return this->get_output_data_ptr_impl(index);
+}
+
+inline void Params::output_set(const int index)
+{
+ this->assert_valid_thread();
+ this->output_set_impl(index);
+}
+
+inline bool Params::output_was_set(const int index) const
+{
+ return this->output_was_set_impl(index);
+}
+
+inline ValueUsage Params::get_output_usage(const int index) const
+{
+ return this->get_output_usage_impl(index);
+}
+
+inline void Params::set_input_unused(const int index)
+{
+ this->assert_valid_thread();
+ this->set_input_unused_impl(index);
+}
+
+template<typename T> inline T Params::extract_input(const int index)
+{
+ this->assert_valid_thread();
+ void *data = this->try_get_input_data_ptr(index);
+ BLI_assert(data != nullptr);
+ T return_value = std::move(*static_cast<T *>(data));
+ return return_value;
+}
+
+template<typename T> inline const T &Params::get_input(const int index) const
+{
+ const void *data = this->try_get_input_data_ptr(index);
+ BLI_assert(data != nullptr);
+ return *static_cast<const T *>(data);
+}
+
+template<typename T> inline T *Params::try_get_input_data_ptr_or_request(const int index)
+{
+ this->assert_valid_thread();
+ return static_cast<T *>(this->try_get_input_data_ptr_or_request(index));
+}
+
+template<typename T> inline void Params::set_output(const int index, T &&value)
+{
+ using DecayT = std::decay_t<T>;
+ this->assert_valid_thread();
+ void *data = this->get_output_data_ptr(index);
+ new (data) DecayT(std::forward<T>(value));
+ this->output_set(index);
+}
+
+inline bool Params::try_enable_multi_threading()
+{
+ this->assert_valid_thread();
+ const bool success = this->try_enable_multi_threading_impl();
+#ifdef FN_LAZY_FUNCTION_DEBUG_THREADS
+ if (success) {
+ allow_multi_threading_ = true;
+ }
+#endif
+ return success;
+}
+
+inline void Params::assert_valid_thread() const
+{
+#ifdef FN_LAZY_FUNCTION_DEBUG_THREADS
+ if (allow_multi_threading_) {
+ return;
+ }
+ if (main_thread_id_ != std::this_thread::get_id()) {
+ BLI_assert_unreachable();
+ }
+#endif
+}
+
+/** \} */
+
+} // namespace blender::fn::lazy_function
diff --git a/source/blender/functions/FN_lazy_function_execute.hh b/source/blender/functions/FN_lazy_function_execute.hh
new file mode 100644
index 00000000000..31bbddf5baf
--- /dev/null
+++ b/source/blender/functions/FN_lazy_function_execute.hh
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup fn
+ *
+ * This file contains common utilities for actually executing a lazy-function.
+ */
+
+#include "BLI_parameter_pack_utils.hh"
+
+#include "FN_lazy_function.hh"
+
+namespace blender::fn::lazy_function {
+
+/**
+ * Most basic implementation of #Params. It does not actually implement any logic for how to
+ * retrieve inputs or set outputs. Instead, code using #BasicParams has to implement that.
+ */
+class BasicParams : public Params {
+ private:
+ const Span<GMutablePointer> inputs_;
+ const Span<GMutablePointer> outputs_;
+ MutableSpan<std::optional<ValueUsage>> input_usages_;
+ Span<ValueUsage> output_usages_;
+ MutableSpan<bool> set_outputs_;
+
+ public:
+ BasicParams(const LazyFunction &fn,
+ const Span<GMutablePointer> inputs,
+ const Span<GMutablePointer> outputs,
+ MutableSpan<std::optional<ValueUsage>> input_usages,
+ Span<ValueUsage> output_usages,
+ MutableSpan<bool> set_outputs);
+
+ void *try_get_input_data_ptr_impl(const int index) const override;
+ void *try_get_input_data_ptr_or_request_impl(const int index) override;
+ void *get_output_data_ptr_impl(const int index) override;
+ void output_set_impl(const int index) override;
+ bool output_was_set_impl(const int index) const override;
+ ValueUsage get_output_usage_impl(const int index) const override;
+ void set_input_unused_impl(const int index) override;
+ bool try_enable_multi_threading_impl() override;
+};
+
+namespace detail {
+
+/**
+ * Utility to implement #execute_lazy_function_eagerly.
+ */
+template<typename... Inputs, typename... Outputs, size_t... InIndices, size_t... OutIndices>
+inline void execute_lazy_function_eagerly_impl(
+ const LazyFunction &fn,
+ UserData *user_data,
+ std::tuple<Inputs...> &inputs,
+ std::tuple<Outputs *...> &outputs,
+ std::index_sequence<InIndices...> /* in_indices */,
+ std::index_sequence<OutIndices...> /* out_indices */)
+{
+ constexpr size_t InputsNum = sizeof...(Inputs);
+ constexpr size_t OutputsNum = sizeof...(Outputs);
+ std::array<GMutablePointer, InputsNum> input_pointers;
+ std::array<GMutablePointer, OutputsNum> output_pointers;
+ std::array<std::optional<ValueUsage>, InputsNum> input_usages;
+ std::array<ValueUsage, OutputsNum> output_usages;
+ std::array<bool, OutputsNum> set_outputs;
+ (
+ [&]() {
+ constexpr size_t I = InIndices;
+ /* Use `typedef` instead of `using` to work around a compiler bug. */
+ typedef Inputs T;
+ const CPPType &type = CPPType::get<T>();
+ input_pointers[I] = {type, &std::get<I>(inputs)};
+ }(),
+ ...);
+ (
+ [&]() {
+ constexpr size_t I = OutIndices;
+ /* Use `typedef` instead of `using` to work around a compiler bug. */
+ typedef Outputs T;
+ const CPPType &type = CPPType::get<T>();
+ output_pointers[I] = {type, std::get<I>(outputs)};
+ }(),
+ ...);
+ output_usages.fill(ValueUsage::Used);
+ set_outputs.fill(false);
+ LinearAllocator<> allocator;
+ Context context;
+ context.user_data = user_data;
+ context.storage = fn.init_storage(allocator);
+ BasicParams params{
+ fn, input_pointers, output_pointers, input_usages, output_usages, set_outputs};
+ fn.execute(params, context);
+ fn.destruct_storage(context.storage);
+}
+
+} // namespace detail
+
+/**
+ * In some cases (mainly for tests), the set of inputs and outputs for a lazy-function is known at
+ * compile time and one just wants to compute the outputs based on the inputs, without any
+ * laziness.
+ *
+ * This function does exactly that. It takes all inputs in a tuple and writes the outputs to points
+ * provided in a second tuple. Since all inputs have to be provided, the lazy-function has to
+ * compute all outputs.
+ */
+template<typename... Inputs, typename... Outputs>
+inline void execute_lazy_function_eagerly(const LazyFunction &fn,
+ UserData *user_data,
+ std::tuple<Inputs...> inputs,
+ std::tuple<Outputs *...> outputs)
+{
+ BLI_assert(fn.inputs().size() == sizeof...(Inputs));
+ BLI_assert(fn.outputs().size() == sizeof...(Outputs));
+ detail::execute_lazy_function_eagerly_impl(fn,
+ user_data,
+ inputs,
+ outputs,
+ std::make_index_sequence<sizeof...(Inputs)>(),
+ std::make_index_sequence<sizeof...(Outputs)>());
+}
+
+} // namespace blender::fn::lazy_function
diff --git a/source/blender/functions/FN_lazy_function_graph.hh b/source/blender/functions/FN_lazy_function_graph.hh
new file mode 100644
index 00000000000..4ede28c4f26
--- /dev/null
+++ b/source/blender/functions/FN_lazy_function_graph.hh
@@ -0,0 +1,421 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup fn
+ *
+ * This file contains a graph data structure that allows composing multiple lazy-functions into a
+ * combined lazy-function.
+ *
+ * There are two types of nodes in the graph:
+ * - #FunctionNode: Corresponds to a #LazyFunction. The inputs and outputs of the function become
+ * input and output sockets of the node.
+ * - #DummyNode: Is used to indicate inputs and outputs of the entire graph. It can have an
+ * arbitrary number of sockets.
+ */
+
+#include "BLI_linear_allocator.hh"
+
+#include "FN_lazy_function.hh"
+
+namespace blender::fn::lazy_function {
+
+class Socket;
+class InputSocket;
+class OutputSocket;
+class Node;
+class Graph;
+
+/**
+ * A #Socket is the interface of a #Node. Every #Socket is either an #InputSocket or #OutputSocket.
+ * Links can be created from output sockets to input sockets.
+ */
+class Socket : NonCopyable, NonMovable {
+ protected:
+ /**
+ * The node the socket belongs to.
+ */
+ Node *node_;
+ /**
+ * Data type of the socket. Only sockets with the same type can be linked.
+ */
+ const CPPType *type_;
+ /**
+ * Indicates whether this is an #InputSocket or #OutputSocket.
+ */
+ bool is_input_;
+ /**
+ * Index of the socket. E.g. 0 for the first input and the first output socket.
+ */
+ int index_in_node_;
+
+ friend Graph;
+
+ public:
+ bool is_input() const;
+ bool is_output() const;
+
+ int index() const;
+
+ InputSocket &as_input();
+ OutputSocket &as_output();
+ const InputSocket &as_input() const;
+ const OutputSocket &as_output() const;
+
+ const Node &node() const;
+ Node &node();
+
+ const CPPType &type() const;
+
+ std::string name() const;
+};
+
+class InputSocket : public Socket {
+ private:
+ /**
+ * An input can have at most one link connected to it. The linked socket is the "origin" because
+ * it's where the data is coming from. The type of the origin must be the same as the type of
+ * this socket.
+ */
+ OutputSocket *origin_;
+ /**
+ * Can be null or a non-owning pointer to a value of the type of the socket. This value will be
+ * used when the input is used but not linked.
+ *
+ * This is technically not needed, because one could just create a separate node that just
+ * outputs the value, but that would have more overhead. Especially because it's commonly the
+ * case that most inputs are unlinked.
+ */
+ const void *default_value_ = nullptr;
+
+ friend Graph;
+
+ public:
+ OutputSocket *origin();
+ const OutputSocket *origin() const;
+
+ const void *default_value() const;
+ void set_default_value(const void *value);
+};
+
+class OutputSocket : public Socket {
+ private:
+ /**
+ * An output can be linked to an arbitrary number of inputs of the same type.
+ */
+ Vector<InputSocket *> targets_;
+
+ friend Graph;
+
+ public:
+ Span<InputSocket *> targets();
+ Span<const InputSocket *> targets() const;
+};
+
+/**
+ * A #Node has input and output sockets. Every node is either a #FunctionNode or a #DummyNode.
+ */
+class Node : NonCopyable, NonMovable {
+ protected:
+ /**
+ * The function this node corresponds to. If this is null, the node is a #DummyNode.
+ * The function is not owned by this #Node nor by the #Graph.
+ */
+ const LazyFunction *fn_ = nullptr;
+ /**
+ * Input sockets of the node.
+ */
+ Span<InputSocket *> inputs_;
+ /**
+ * Output sockets of the node.
+ */
+ Span<OutputSocket *> outputs_;
+ /**
+ * An index that is set when calling #Graph::update_node_indices. This can be used to create
+ * efficient mappings from nodes to other data using just an array instead of a hash map.
+ *
+ * This is technically not necessary but has better performance than always using hash maps.
+ */
+ int index_in_graph_ = -1;
+
+ friend Graph;
+
+ public:
+ bool is_dummy() const;
+ bool is_function() const;
+ int index_in_graph() const;
+
+ Span<const InputSocket *> inputs() const;
+ Span<const OutputSocket *> outputs() const;
+ Span<InputSocket *> inputs();
+ Span<OutputSocket *> outputs();
+
+ const InputSocket &input(int index) const;
+ const OutputSocket &output(int index) const;
+ InputSocket &input(int index);
+ OutputSocket &output(int index);
+
+ std::string name() const;
+};
+
+/**
+ * A #Node that corresponds to a specific #LazyFunction.
+ */
+class FunctionNode : public Node {
+ public:
+ const LazyFunction &function() const;
+};
+
+/**
+ * A #Node that does *not* correspond to a #LazyFunction. Instead it can be used to indicate inputs
+ * and outputs of the entire graph. It can have an arbitrary number of inputs and outputs.
+ */
+class DummyNode : public Node {
+ private:
+ std::string name_;
+
+ friend Node;
+};
+
+/**
+ * A container for an arbitrary number of nodes and links between their sockets.
+ */
+class Graph : NonCopyable, NonMovable {
+ private:
+ /**
+ * Used to allocate nodes and sockets in the graph.
+ */
+ LinearAllocator<> allocator_;
+ /**
+ * Contains all nodes in the graph so that it is efficient to iterate over them.
+ */
+ Vector<Node *> nodes_;
+
+ public:
+ ~Graph();
+
+ /**
+ * Get all nodes in the graph. The index in the span corresponds to #Node::index_in_graph.
+ */
+ Span<const Node *> nodes() const;
+
+ /**
+ * Add a new function node with sockets that match the passed in #LazyFunction.
+ */
+ FunctionNode &add_function(const LazyFunction &fn);
+
+ /**
+ * Add a new dummy node with the given socket types.
+ */
+ DummyNode &add_dummy(Span<const CPPType *> input_types, Span<const CPPType *> output_types);
+
+ /**
+ * Add a link between the two given sockets.
+ * This has undefined behavior when the input is linked to something else already.
+ */
+ void add_link(OutputSocket &from, InputSocket &to);
+
+ /**
+ * Make sure that #Node::index_in_graph is up to date.
+ */
+ void update_node_indices();
+
+ /**
+ * Can be used to assert that #update_node_indices has been called.
+ */
+ bool node_indices_are_valid() const;
+
+ /**
+ * Utility to generate a dot graph string for the graph. This can be used for debugging.
+ */
+ std::string to_dot() const;
+};
+
+/* -------------------------------------------------------------------- */
+/** \name #Socket Inline Methods
+ * \{ */
+
+inline bool Socket::is_input() const
+{
+ return is_input_;
+}
+
+inline bool Socket::is_output() const
+{
+ return !is_input_;
+}
+
+inline int Socket::index() const
+{
+ return index_in_node_;
+}
+
+inline InputSocket &Socket::as_input()
+{
+ BLI_assert(this->is_input());
+ return *static_cast<InputSocket *>(this);
+}
+
+inline OutputSocket &Socket::as_output()
+{
+ BLI_assert(this->is_output());
+ return *static_cast<OutputSocket *>(this);
+}
+
+inline const InputSocket &Socket::as_input() const
+{
+ BLI_assert(this->is_input());
+ return *static_cast<const InputSocket *>(this);
+}
+
+inline const OutputSocket &Socket::as_output() const
+{
+ BLI_assert(this->is_output());
+ return *static_cast<const OutputSocket *>(this);
+}
+
+inline const Node &Socket::node() const
+{
+ return *node_;
+}
+
+inline Node &Socket::node()
+{
+ return *node_;
+}
+
+inline const CPPType &Socket::type() const
+{
+ return *type_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #InputSocket Inline Methods
+ * \{ */
+
+inline const OutputSocket *InputSocket::origin() const
+{
+ return origin_;
+}
+
+inline OutputSocket *InputSocket::origin()
+{
+ return origin_;
+}
+
+inline const void *InputSocket::default_value() const
+{
+ return default_value_;
+}
+
+inline void InputSocket::set_default_value(const void *value)
+{
+ default_value_ = value;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #OutputSocket Inline Methods
+ * \{ */
+
+inline Span<const InputSocket *> OutputSocket::targets() const
+{
+ return targets_;
+}
+
+inline Span<InputSocket *> OutputSocket::targets()
+{
+ return targets_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #Node Inline Methods
+ * \{ */
+
+inline bool Node::is_dummy() const
+{
+ return fn_ == nullptr;
+}
+
+inline bool Node::is_function() const
+{
+ return fn_ != nullptr;
+}
+
+inline int Node::index_in_graph() const
+{
+ return index_in_graph_;
+}
+
+inline Span<const InputSocket *> Node::inputs() const
+{
+ return inputs_;
+}
+
+inline Span<const OutputSocket *> Node::outputs() const
+{
+ return outputs_;
+}
+
+inline Span<InputSocket *> Node::inputs()
+{
+ return inputs_;
+}
+
+inline Span<OutputSocket *> Node::outputs()
+{
+ return outputs_;
+}
+
+inline const InputSocket &Node::input(const int index) const
+{
+ return *inputs_[index];
+}
+
+inline const OutputSocket &Node::output(const int index) const
+{
+ return *outputs_[index];
+}
+
+inline InputSocket &Node::input(const int index)
+{
+ return *inputs_[index];
+}
+
+inline OutputSocket &Node::output(const int index)
+{
+ return *outputs_[index];
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #FunctionNode Inline Methods
+ * \{ */
+
+inline const LazyFunction &FunctionNode::function() const
+{
+ BLI_assert(fn_ != nullptr);
+ return *fn_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #Graph Inline Methods
+ * \{ */
+
+inline Span<const Node *> Graph::nodes() const
+{
+ return nodes_;
+}
+
+/** \} */
+
+} // namespace blender::fn::lazy_function
diff --git a/source/blender/functions/FN_lazy_function_graph_executor.hh b/source/blender/functions/FN_lazy_function_graph_executor.hh
new file mode 100644
index 00000000000..a6ae5cac967
--- /dev/null
+++ b/source/blender/functions/FN_lazy_function_graph_executor.hh
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup fn
+ *
+ * This file provides means to create a #LazyFunction from #Graph (which could then e.g. be used in
+ * another #Graph again).
+ */
+
+#include "BLI_vector.hh"
+#include "BLI_vector_set.hh"
+
+#include "FN_lazy_function_graph.hh"
+
+namespace blender::fn::lazy_function {
+
+/**
+ * Can be implemented to log values produced during graph evaluation.
+ */
+class GraphExecutorLogger {
+ public:
+ virtual ~GraphExecutorLogger() = default;
+
+ virtual void log_socket_value(const Socket &socket,
+ GPointer value,
+ const Context &context) const;
+
+ virtual void log_before_node_execute(const FunctionNode &node,
+ const Params &params,
+ const Context &context) const;
+
+ virtual void log_after_node_execute(const FunctionNode &node,
+ const Params &params,
+ const Context &context) const;
+
+ virtual void dump_when_outputs_are_missing(const FunctionNode &node,
+ Span<const OutputSocket *> missing_sockets,
+ const Context &context) const;
+ virtual void dump_when_input_is_set_twice(const InputSocket &target_socket,
+ const OutputSocket &from_socket,
+ const Context &context) const;
+};
+
+/**
+ * Has to be implemented when some of the nodes in the graph may have side effects. The
+ * #GraphExecutor has to know about that to make sure that these nodes will be executed even though
+ * their outputs are not needed.
+ */
+class GraphExecutorSideEffectProvider {
+ public:
+ virtual ~GraphExecutorSideEffectProvider() = default;
+ virtual Vector<const FunctionNode *> get_nodes_with_side_effects(const Context &context) const;
+};
+
+class GraphExecutor : public LazyFunction {
+ public:
+ using Logger = GraphExecutorLogger;
+ using SideEffectProvider = GraphExecutorSideEffectProvider;
+
+ private:
+ /**
+ * The graph that is evaluated.
+ */
+ const Graph &graph_;
+ /**
+ * Input and output sockets of the entire graph.
+ */
+ VectorSet<const OutputSocket *> graph_inputs_;
+ VectorSet<const InputSocket *> graph_outputs_;
+ /**
+ * Optional logger for events that happen during execution.
+ */
+ const Logger *logger_;
+ /**
+ * Optional side effect provider. It knows which nodes have side effects based on the context
+ * during evaluation.
+ */
+ const SideEffectProvider *side_effect_provider_;
+
+ friend class Executor;
+
+ public:
+ GraphExecutor(const Graph &graph,
+ Span<const OutputSocket *> graph_inputs,
+ Span<const InputSocket *> graph_outputs,
+ const Logger *logger,
+ const SideEffectProvider *side_effect_provider);
+
+ void *init_storage(LinearAllocator<> &allocator) const override;
+ void destruct_storage(void *storage) const override;
+
+ private:
+ void execute_impl(Params &params, const Context &context) const override;
+};
+
+} // namespace blender::fn::lazy_function
diff --git a/source/blender/functions/FN_multi_function.hh b/source/blender/functions/FN_multi_function.hh
index 015df179ef0..accbaf899be 100644
--- a/source/blender/functions/FN_multi_function.hh
+++ b/source/blender/functions/FN_multi_function.hh
@@ -157,6 +157,7 @@ namespace multi_function_types {
using fn::MFContext;
using fn::MFContextBuilder;
using fn::MFDataType;
+using fn::MFParamCategory;
using fn::MFParams;
using fn::MFParamsBuilder;
using fn::MFParamType;
diff --git a/source/blender/functions/FN_multi_function_builder.hh b/source/blender/functions/FN_multi_function_builder.hh
index e6dc01eb539..75a2414801d 100644
--- a/source/blender/functions/FN_multi_function_builder.hh
+++ b/source/blender/functions/FN_multi_function_builder.hh
@@ -207,6 +207,7 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
(
/* Setup information for all parameters. */
[&] {
+ /* Use `typedef` instead of `using` to work around a compiler bug. */
typedef ParamTags ParamTag;
typedef typename ParamTag::base_type T;
[[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
@@ -282,6 +283,7 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
(
/* Destruct values that have been materialized before. */
[&] {
+ /* Use `typedef` instead of `using` to work around a compiler bug. */
typedef ParamTags ParamTag;
typedef typename ParamTag::base_type T;
[[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
@@ -298,6 +300,7 @@ void execute_materialized(TypeSequence<ParamTags...> /* param_tags */,
(
/* Destruct buffers for single value inputs. */
[&] {
+ /* Use `typedef` instead of `using` to work around a compiler bug. */
typedef ParamTags ParamTag;
typedef typename ParamTag::base_type T;
[[maybe_unused]] ArgInfo<ParamTags> &arg_info = std::get<I>(args_info);
@@ -347,6 +350,7 @@ template<typename... ParamTags> class CustomMF : public MultiFunction {
(
/* Get all parameters from #params and store them in #retrieved_params. */
[&]() {
+ /* Use `typedef` instead of `using` to work around a compiler bug. */
typedef typename TagsSequence::template at_index<I> ParamTag;
typedef typename ParamTag::base_type T;
@@ -402,6 +406,7 @@ template<typename... ParamTags> class CustomMF : public MultiFunction {
(
/* Loop over all parameter types and add an entry for each in the signature. */
[&] {
+ /* Use `typedef` instead of `using` to work around a compiler bug. */
typedef typename TagsSequence::template at_index<I> ParamTag;
signature.add(ParamTag(), "");
}(),
diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc
index 5c43fffdd61..f046da30994 100644
--- a/source/blender/functions/intern/cpp_types.cc
+++ b/source/blender/functions/intern/cpp_types.cc
@@ -16,3 +16,6 @@ MAKE_FIELD_CPP_TYPE(BoolField, bool);
MAKE_FIELD_CPP_TYPE(Int8Field, int8_t);
MAKE_FIELD_CPP_TYPE(Int32Field, int32_t);
MAKE_FIELD_CPP_TYPE(StringField, std::string);
+BLI_CPP_TYPE_MAKE(StringValueOrFieldVector,
+ blender::Vector<blender::fn::ValueOrField<std::string>>,
+ CPPTypeFlags::None);
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index fd5eab57d33..8fb56d7aa11 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -16,9 +16,9 @@
namespace blender::fn {
-/* --------------------------------------------------------------------
- * Field Evaluation.
- */
+/* -------------------------------------------------------------------- */
+/** \name Field Evaluation
+ * \{ */
struct FieldTreeInfo {
/**
@@ -571,16 +571,20 @@ bool IndexFieldInput::is_equal_to(const fn::FieldNode &other) const
return dynamic_cast<const IndexFieldInput *>(&other) != nullptr;
}
-/* --------------------------------------------------------------------
- * FieldNode.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #FieldNode
+ * \{ */
/* Avoid generating the destructor in every translation unit. */
FieldNode::~FieldNode() = default;
-/* --------------------------------------------------------------------
- * FieldOperation.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #FieldOperation
+ * \{ */
FieldOperation::FieldOperation(std::shared_ptr<const MultiFunction> function,
Vector<GField> inputs)
@@ -653,9 +657,11 @@ FieldOperation::FieldOperation(const MultiFunction &function, Vector<GField> inp
field_inputs_ = combine_field_inputs(inputs_);
}
-/* --------------------------------------------------------------------
- * FieldInput.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #FieldInput
+ * \{ */
FieldInput::FieldInput(const CPPType &type, std::string debug_name)
: FieldNode(FieldNodeType::Input), type_(&type), debug_name_(std::move(debug_name))
@@ -669,9 +675,11 @@ FieldInput::FieldInput(const CPPType &type, std::string debug_name)
/* Avoid generating the destructor in every translation unit. */
FieldInput::~FieldInput() = default;
-/* --------------------------------------------------------------------
- * FieldConstant.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #FieldConstant
+ * \{ */
FieldConstant::FieldConstant(const CPPType &type, const void *value)
: FieldNode(FieldNodeType::Constant), type_(type)
@@ -703,9 +711,11 @@ GPointer FieldConstant::value() const
return {type_, value_};
}
-/* --------------------------------------------------------------------
- * FieldEvaluator.
- */
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #FieldEvaluator
+ * \{ */
static IndexMask index_mask_from_selection(const IndexMask full_mask,
const VArray<bool> &selection,
@@ -800,4 +810,6 @@ IndexMask FieldEvaluator::get_evaluated_selection_as_mask()
return selection_mask_;
}
+/** \} */
+
} // namespace blender::fn
diff --git a/source/blender/functions/intern/lazy_function.cc b/source/blender/functions/intern/lazy_function.cc
new file mode 100644
index 00000000000..2d69af84f85
--- /dev/null
+++ b/source/blender/functions/intern/lazy_function.cc
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup fn
+ */
+
+#include "BLI_array.hh"
+
+#include "FN_lazy_function.hh"
+
+namespace blender::fn::lazy_function {
+
+std::string LazyFunction::name() const
+{
+ return debug_name_;
+}
+
+std::string LazyFunction::input_name(int index) const
+{
+ return inputs_[index].debug_name;
+}
+
+std::string LazyFunction::output_name(int index) const
+{
+ return outputs_[index].debug_name;
+}
+
+void *LazyFunction::init_storage(LinearAllocator<> &UNUSED(allocator)) const
+{
+ return nullptr;
+}
+
+void LazyFunction::destruct_storage(void *storage) const
+{
+ BLI_assert(storage == nullptr);
+ UNUSED_VARS_NDEBUG(storage);
+}
+
+bool LazyFunction::always_used_inputs_available(const Params &params) const
+{
+ for (const int i : inputs_.index_range()) {
+ const Input &fn_input = inputs_[i];
+ if (fn_input.usage == ValueUsage::Used) {
+ if (params.try_get_input_data_ptr(i) == nullptr) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void Params::set_default_remaining_outputs()
+{
+ for (const int i : fn_.outputs().index_range()) {
+ if (this->output_was_set(i)) {
+ continue;
+ }
+ const Output &fn_output = fn_.outputs()[i];
+ const CPPType &type = *fn_output.type;
+ void *data_ptr = this->get_output_data_ptr(i);
+ type.value_initialize(data_ptr);
+ this->output_set(i);
+ }
+}
+
+bool Params::try_enable_multi_threading_impl()
+{
+ return false;
+}
+
+} // namespace blender::fn::lazy_function
diff --git a/source/blender/functions/intern/lazy_function_execute.cc b/source/blender/functions/intern/lazy_function_execute.cc
new file mode 100644
index 00000000000..cea9b48d5bc
--- /dev/null
+++ b/source/blender/functions/intern/lazy_function_execute.cc
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup fn
+ */
+
+#include "FN_lazy_function_execute.hh"
+
+namespace blender::fn::lazy_function {
+
+BasicParams::BasicParams(const LazyFunction &fn,
+ const Span<GMutablePointer> inputs,
+ const Span<GMutablePointer> outputs,
+ MutableSpan<std::optional<ValueUsage>> input_usages,
+ Span<ValueUsage> output_usages,
+ MutableSpan<bool> set_outputs)
+ : Params(fn, true),
+ inputs_(inputs),
+ outputs_(outputs),
+ input_usages_(input_usages),
+ output_usages_(output_usages),
+ set_outputs_(set_outputs)
+{
+}
+
+void *BasicParams::try_get_input_data_ptr_impl(const int index) const
+{
+ return inputs_[index].get();
+}
+
+void *BasicParams::try_get_input_data_ptr_or_request_impl(const int index)
+{
+ void *value = inputs_[index].get();
+ if (value == nullptr) {
+ input_usages_[index] = ValueUsage::Used;
+ }
+ return value;
+}
+
+void *BasicParams::get_output_data_ptr_impl(const int index)
+{
+ return outputs_[index].get();
+}
+
+void BasicParams::output_set_impl(const int index)
+{
+ set_outputs_[index] = true;
+}
+
+bool BasicParams::output_was_set_impl(const int index) const
+{
+ return set_outputs_[index];
+}
+
+ValueUsage BasicParams::get_output_usage_impl(const int index) const
+{
+ return output_usages_[index];
+}
+
+void BasicParams::set_input_unused_impl(const int index)
+{
+ input_usages_[index] = ValueUsage::Unused;
+}
+
+bool BasicParams::try_enable_multi_threading_impl()
+{
+ return true;
+}
+
+} // namespace blender::fn::lazy_function
diff --git a/source/blender/functions/intern/lazy_function_graph.cc b/source/blender/functions/intern/lazy_function_graph.cc
new file mode 100644
index 00000000000..cc55b70d166
--- /dev/null
+++ b/source/blender/functions/intern/lazy_function_graph.cc
@@ -0,0 +1,181 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BLI_dot_export.hh"
+
+#include "FN_lazy_function_graph.hh"
+
+namespace blender::fn::lazy_function {
+
+Graph::~Graph()
+{
+ for (Node *node : nodes_) {
+ for (InputSocket *socket : node->inputs_) {
+ std::destroy_at(socket);
+ }
+ for (OutputSocket *socket : node->outputs_) {
+ std::destroy_at(socket);
+ }
+ std::destroy_at(node);
+ }
+}
+
+FunctionNode &Graph::add_function(const LazyFunction &fn)
+{
+ const Span<Input> inputs = fn.inputs();
+ const Span<Output> outputs = fn.outputs();
+
+ FunctionNode &node = *allocator_.construct<FunctionNode>().release();
+ node.fn_ = &fn;
+ node.inputs_ = allocator_.construct_elements_and_pointer_array<InputSocket>(inputs.size());
+ node.outputs_ = allocator_.construct_elements_and_pointer_array<OutputSocket>(outputs.size());
+
+ for (const int i : inputs.index_range()) {
+ InputSocket &socket = *node.inputs_[i];
+ socket.index_in_node_ = i;
+ socket.is_input_ = true;
+ socket.node_ = &node;
+ socket.type_ = inputs[i].type;
+ }
+ for (const int i : outputs.index_range()) {
+ OutputSocket &socket = *node.outputs_[i];
+ socket.index_in_node_ = i;
+ socket.is_input_ = false;
+ socket.node_ = &node;
+ socket.type_ = outputs[i].type;
+ }
+
+ nodes_.append(&node);
+ return node;
+}
+
+DummyNode &Graph::add_dummy(Span<const CPPType *> input_types, Span<const CPPType *> output_types)
+{
+ DummyNode &node = *allocator_.construct<DummyNode>().release();
+ node.fn_ = nullptr;
+ node.inputs_ = allocator_.construct_elements_and_pointer_array<InputSocket>(input_types.size());
+ node.outputs_ = allocator_.construct_elements_and_pointer_array<OutputSocket>(
+ output_types.size());
+
+ for (const int i : input_types.index_range()) {
+ InputSocket &socket = *node.inputs_[i];
+ socket.index_in_node_ = i;
+ socket.is_input_ = true;
+ socket.node_ = &node;
+ socket.type_ = input_types[i];
+ }
+ for (const int i : output_types.index_range()) {
+ OutputSocket &socket = *node.outputs_[i];
+ socket.index_in_node_ = i;
+ socket.is_input_ = false;
+ socket.node_ = &node;
+ socket.type_ = output_types[i];
+ }
+
+ nodes_.append(&node);
+ return node;
+}
+
+void Graph::add_link(OutputSocket &from, InputSocket &to)
+{
+ BLI_assert(to.origin_ == nullptr);
+ BLI_assert(from.type_ == to.type_);
+ to.origin_ = &from;
+ from.targets_.append(&to);
+}
+
+void Graph::update_node_indices()
+{
+ for (const int i : nodes_.index_range()) {
+ nodes_[i]->index_in_graph_ = i;
+ }
+}
+
+bool Graph::node_indices_are_valid() const
+{
+ for (const int i : nodes_.index_range()) {
+ if (nodes_[i]->index_in_graph_ != i) {
+ return false;
+ }
+ }
+ return true;
+}
+
+std::string Socket::name() const
+{
+ if (node_->is_function()) {
+ const FunctionNode &fn_node = static_cast<const FunctionNode &>(*node_);
+ const LazyFunction &fn = fn_node.function();
+ if (is_input_) {
+ return fn.input_name(index_in_node_);
+ }
+ return fn.output_name(index_in_node_);
+ }
+ return "Unnamed";
+}
+
+std::string Node::name() const
+{
+ if (fn_ == nullptr) {
+ return static_cast<const DummyNode *>(this)->name_;
+ }
+ return fn_->name();
+}
+
+std::string Graph::to_dot() const
+{
+ dot::DirectedGraph digraph;
+ digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
+
+ Map<const Node *, dot::NodeWithSocketsRef> dot_nodes;
+
+ for (const Node *node : nodes_) {
+ dot::Node &dot_node = digraph.new_node("");
+ if (node->is_dummy()) {
+ dot_node.set_background_color("lightblue");
+ }
+ else {
+ dot_node.set_background_color("white");
+ }
+
+ Vector<std::string> input_names;
+ Vector<std::string> output_names;
+ for (const InputSocket *socket : node->inputs()) {
+ input_names.append(socket->name());
+ }
+ for (const OutputSocket *socket : node->outputs()) {
+ output_names.append(socket->name());
+ }
+
+ dot_nodes.add_new(node,
+ dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
+ }
+
+ for (const Node *node : nodes_) {
+ for (const InputSocket *socket : node->inputs()) {
+ const dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&socket->node());
+ const dot::NodePort to_dot_port = to_dot_node.input(socket->index());
+
+ if (const OutputSocket *origin = socket->origin()) {
+ dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&origin->node());
+ digraph.new_edge(from_dot_node.output(origin->index()), to_dot_port);
+ }
+ else if (const void *default_value = socket->default_value()) {
+ const CPPType &type = socket->type();
+ std::string value_string;
+ if (type.is_printable()) {
+ value_string = type.to_string(default_value);
+ }
+ else {
+ value_string = "<" + type.name() + ">";
+ }
+ dot::Node &default_value_dot_node = digraph.new_node(value_string);
+ default_value_dot_node.set_shape(dot::Attr_shape::Ellipse);
+ digraph.new_edge(default_value_dot_node, to_dot_port);
+ }
+ }
+ }
+
+ return digraph.to_dot_string();
+}
+
+} // namespace blender::fn::lazy_function
diff --git a/source/blender/functions/intern/lazy_function_graph_executor.cc b/source/blender/functions/intern/lazy_function_graph_executor.cc
new file mode 100644
index 00000000000..4c5c3fa47a2
--- /dev/null
+++ b/source/blender/functions/intern/lazy_function_graph_executor.cc
@@ -0,0 +1,1240 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/**
+ * This file implements the evaluation of a lazy-function graph. It's main objectives are:
+ * - Only compute values that are actually used.
+ * - Stay single threaded when nodes are executed quickly.
+ * - Allow spreading the work over an arbitrary number of threads efficiently.
+ *
+ * This executor makes use of `FN_lazy_threading.hh` to enable multi-threading only when it seems
+ * beneficial. It operates in two modes: single- and multi-threaded. The use of a task pool and
+ * locks is avoided in single-threaded mode. Once multi-threading is enabled the executor starts
+ * using both. It is not possible to switch back from multi-threaded to single-threaded mode.
+ *
+ * The multi-threading design implemented in this executor requires *no* main thread that
+ * coordinates everything. Instead, one thread will trigger some initial work and then many threads
+ * coordinate themselves in a distributed fashion. In an ideal situation, every thread ends up
+ * processing a separate part of the graph which results in less communication overhead. The way
+ * TBB schedules tasks helps with that: a thread will next process the task that it added to a task
+ * pool just before.
+ *
+ * Communication between threads is synchronized by using a mutex in every node. When a thread
+ * wants to access the state of a node, its mutex has to be locked first (with some documented
+ * exceptions). The assumption here is that most nodes are only ever touched by a single thread and
+ * therefore the lock contention is reduced the more nodes there are.
+ *
+ * Similar to how a #LazyFunction can be thought of as a state machine (see `FN_lazy_function.hh`),
+ * each node can also be thought of as a state machine. The state of a node contains the evaluation
+ * state of its inputs and outputs. Every time a node is executed, it has to advance its state in
+ * some way (e.g. it requests a new input or computes a new output).
+ *
+ * When a node is executed it may send notifications to other nodes which may in turn schedule
+ * those nodes. For example, when the current node has computed one of its outputs, then the
+ * computed value is forwarded to all linked inputs, changing their node states in the process. If
+ * this input was the last missing required input, the node will be scheduled that it is executed
+ * next.
+ *
+ * When all tasks are completed, the executor gives back control to the caller which may later
+ * provide new inputs to the graph which in turn leads to new nodes being scheduled and the process
+ * starts again.
+ */
+
+#include <mutex>
+
+#include "BLI_compute_context.hh"
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_function_ref.hh"
+#include "BLI_task.h"
+#include "BLI_task.hh"
+#include "BLI_timeit.hh"
+
+#include "FN_lazy_function_graph_executor.hh"
+
+namespace blender::fn::lazy_function {
+
+enum class NodeScheduleState {
+ /**
+ * Default state of every node.
+ */
+ NotScheduled,
+ /**
+ * The node has been added to the task pool or is otherwise scheduled to be executed 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 run again.
+ * This state exists, because we don't want to add the node to the task pool twice, 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 InputState {
+ /**
+ * 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 found that it is not needed anymore.
+ *
+ * If #was_ready_for_execution is true, access does not require holding the node lock.
+ */
+ void *value = nullptr;
+ /**
+ * How the node intends to use this input. By default, all inputs may be used. Based on which
+ * outputs are used, a node can decide that an input will definitely be used or is never used.
+ * This allows freeing values early and avoids unnecessary computations.
+ */
+ ValueUsage usage = ValueUsage::Maybe;
+ /**
+ * Set to true once #value is set and will stay true afterwards. Access during execution of a
+ * node, does not require holding the node lock.
+ */
+ bool was_ready_for_execution = false;
+};
+
+struct OutputState {
+ /**
+ * Keeps track of how the output value is used. If a connected input becomes used, this output
+ * has to become used as well. The output becomes unused when it is used by no input socket
+ * anymore and it's not an output of the graph.
+ */
+ ValueUsage usage = ValueUsage::Maybe;
+ /**
+ * This is a copy of #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). Access during execution of a
+ * node, does not require holding the node lock.
+ */
+ ValueUsage usage_for_execution = ValueUsage::Maybe;
+ /**
+ * Number of linked sockets that might still use the value of this output.
+ */
+ int potential_target_sockets = 0;
+ /**
+ * Is set to true once the output has been computed and then stays true. Access does not require
+ * holding the node lock.
+ */
+ bool has_been_computed = false;
+ /**
+ * Holds the output value for a short period of time while the node is initializing it and before
+ * it's forwarded to input sockets. Access does not require holding the node lock.
+ */
+ void *value = nullptr;
+};
+
+struct NodeState {
+ /**
+ * Needs to be locked when any data in this state is accessed that is not explicitly marked as
+ * not needing the lock.
+ */
+ mutable std::mutex mutex;
+ /**
+ * States of the individual input and output sockets. One can index into these arrays without
+ * locking. However, to access data inside, a lock is needed unless noted otherwise.
+ */
+ MutableSpan<InputState> inputs;
+ MutableSpan<OutputState> outputs;
+ /**
+ * Counts the number of inputs that still have to be provided to this node, until it should run
+ * again. This is used as an optimization so that nodes are not scheduled unnecessarily in many
+ * cases.
+ */
+ int missing_required_inputs = 0;
+ /**
+ * Is set to true once the node is done with its work, i.e. when all outputs that may be used
+ * have been computed.
+ */
+ bool node_has_finished = false;
+ /**
+ * Set to true once the node is done running for the first time.
+ */
+ bool had_initialization = true;
+ /**
+ * Nodes with side effects should always be executed when their required inputs have been
+ * computed.
+ */
+ bool has_side_effects = false;
+ /**
+ * 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;
+ /**
+ * Custom storage of the node.
+ */
+ void *storage = nullptr;
+};
+
+/**
+ * Utility class that wraps a node whose state is locked. Having this is a separate class is useful
+ * because it allows methods to communicate that they expect the node to be locked.
+ */
+struct LockedNode {
+ /**
+ * This is the node that is currently locked.
+ */
+ const Node &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 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<const OutputSocket *> delayed_required_outputs;
+ Vector<const OutputSocket *> delayed_unused_outputs;
+
+ LockedNode(const Node &node, NodeState &node_state) : node(node), node_state(node_state)
+ {
+ }
+};
+
+class Executor;
+class GraphExecutorLFParams;
+
+struct CurrentTask {
+ /**
+ * Mutex used to protect #scheduled_nodes when the executor uses multi-threading.
+ */
+ std::mutex mutex;
+ /**
+ * Nodes that have been scheduled to execute next.
+ */
+ Vector<const FunctionNode *> scheduled_nodes;
+ /**
+ * Makes it cheaper to check if there are any scheduled nodes because it avoids locking the
+ * mutex.
+ */
+ std::atomic<bool> has_scheduled_nodes = false;
+};
+
+class Executor {
+ private:
+ const GraphExecutor &self_;
+ /**
+ * Remembers which inputs have been loaded from the caller already, to avoid loading them twice.
+ * Atomics are used to make sure that every input is only retrieved once.
+ */
+ Array<std::atomic<uint8_t>> loaded_inputs_;
+ /**
+ * State of every node, indexed by #Node::index_in_graph.
+ */
+ Array<NodeState *> node_states_;
+ /**
+ * Parameters provided by the caller. This is always non-null, while a node is running.
+ */
+ Params *params_ = nullptr;
+ const Context *context_ = nullptr;
+ /**
+ * Used to distribute work on separate nodes to separate threads.
+ * If this is empty, the executor is in single threaded mode.
+ */
+ std::atomic<TaskPool *> task_pool_ = nullptr;
+#ifdef FN_LAZY_FUNCTION_DEBUG_THREADS
+ std::thread::id current_main_thread_;
+#endif
+ /**
+ * A separate linear allocator for every thread. We could potentially reuse some memory, but that
+ * doesn't seem worth it yet.
+ */
+ threading::EnumerableThreadSpecific<LinearAllocator<>> local_allocators_;
+ LinearAllocator<> *main_local_allocator_ = nullptr;
+ /**
+ * Set to false when the first execution ends.
+ */
+ bool is_first_execution_ = true;
+
+ friend GraphExecutorLFParams;
+
+ public:
+ Executor(const GraphExecutor &self) : self_(self), loaded_inputs_(self.graph_inputs_.size())
+ {
+ /* The indices are necessary, because they are used as keys in #node_states_. */
+ BLI_assert(self_.graph_.node_indices_are_valid());
+ main_local_allocator_ = &local_allocators_.local();
+ }
+
+ ~Executor()
+ {
+ if (TaskPool *task_pool = task_pool_.load()) {
+ BLI_task_pool_free(task_pool);
+ }
+ threading::parallel_for(node_states_.index_range(), 1024, [&](const IndexRange range) {
+ for (const int node_index : range) {
+ const Node &node = *self_.graph_.nodes()[node_index];
+ NodeState &node_state = *node_states_[node_index];
+ this->destruct_node_state(node, node_state);
+ }
+ });
+ }
+
+ /**
+ * Main entry point to the execution of this graph.
+ */
+ void execute(Params &params, const Context &context)
+ {
+ params_ = &params;
+ context_ = &context;
+#ifdef FN_LAZY_FUNCTION_DEBUG_THREADS
+ current_main_thread_ = std::this_thread::get_id();
+#endif
+ const auto deferred_func = [&]() {
+ /* Make sure the pointers are not dangling, even when it shouldn't be accessed by anyone. */
+ params_ = nullptr;
+ context_ = nullptr;
+ is_first_execution_ = false;
+#ifdef FN_LAZY_FUNCTION_DEBUG_THREADS
+ current_main_thread_ = {};
+#endif
+ };
+ BLI_SCOPED_DEFER(deferred_func);
+
+ CurrentTask current_task;
+ if (is_first_execution_) {
+ this->initialize_node_states();
+
+ /* Initialize atomics to zero. */
+ memset(static_cast<void *>(loaded_inputs_.data()), 0, loaded_inputs_.size() * sizeof(bool));
+
+ this->set_always_unused_graph_inputs();
+ this->set_defaulted_graph_outputs();
+ this->schedule_side_effect_nodes(current_task);
+ }
+
+ this->schedule_newly_requested_outputs(current_task);
+ this->forward_newly_provided_inputs(current_task);
+
+ this->run_task(current_task);
+
+ if (TaskPool *task_pool = task_pool_.load()) {
+ BLI_task_pool_work_and_wait(task_pool);
+ }
+ }
+
+ private:
+ void initialize_node_states()
+ {
+ Span<const Node *> nodes = self_.graph_.nodes();
+ node_states_.reinitialize(nodes.size());
+
+ /* Construct all node states in parallel. */
+ threading::parallel_for(nodes.index_range(), 256, [&](const IndexRange range) {
+ LinearAllocator<> &allocator = local_allocators_.local();
+ for (const int i : range) {
+ const Node &node = *nodes[i];
+ NodeState &node_state = *allocator.construct<NodeState>().release();
+ node_states_[i] = &node_state;
+ this->construct_initial_node_state(allocator, node, node_state);
+ }
+ });
+ }
+
+ void construct_initial_node_state(LinearAllocator<> &allocator,
+ const Node &node,
+ NodeState &node_state)
+ {
+ const Span<const InputSocket *> node_inputs = node.inputs();
+ const Span<const OutputSocket *> node_outputs = node.outputs();
+
+ node_state.inputs = allocator.construct_array<InputState>(node_inputs.size());
+ node_state.outputs = allocator.construct_array<OutputState>(node_outputs.size());
+
+ for (const int i : node_outputs.index_range()) {
+ OutputState &output_state = node_state.outputs[i];
+ const OutputSocket &output_socket = *node_outputs[i];
+ output_state.potential_target_sockets = output_socket.targets().size();
+ if (output_state.potential_target_sockets == 0) {
+ output_state.usage = ValueUsage::Unused;
+ }
+ }
+ }
+
+ void destruct_node_state(const Node &node, NodeState &node_state)
+ {
+ if (node.is_function()) {
+ const LazyFunction &fn = static_cast<const FunctionNode &>(node).function();
+ if (node_state.storage != nullptr) {
+ fn.destruct_storage(node_state.storage);
+ }
+ }
+ for (const int i : node.inputs().index_range()) {
+ InputState &input_state = node_state.inputs[i];
+ const InputSocket &input_socket = node.input(i);
+ this->destruct_input_value_if_exists(input_state, input_socket.type());
+ }
+ std::destroy_at(&node_state);
+ }
+
+ void schedule_newly_requested_outputs(CurrentTask &current_task)
+ {
+ for (const int graph_output_index : self_.graph_outputs_.index_range()) {
+ if (params_->get_output_usage(graph_output_index) != ValueUsage::Used) {
+ continue;
+ }
+ if (params_->output_was_set(graph_output_index)) {
+ continue;
+ }
+ const InputSocket &socket = *self_.graph_outputs_[graph_output_index];
+ const Node &node = socket.node();
+ NodeState &node_state = *node_states_[node.index_in_graph()];
+ this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) {
+ this->set_input_required(locked_node, socket);
+ });
+ }
+ }
+
+ void set_defaulted_graph_outputs()
+ {
+ for (const int graph_output_index : self_.graph_outputs_.index_range()) {
+ const InputSocket &socket = *self_.graph_outputs_[graph_output_index];
+ if (socket.origin() != nullptr) {
+ continue;
+ }
+ const CPPType &type = socket.type();
+ const void *default_value = socket.default_value();
+ BLI_assert(default_value != nullptr);
+
+ if (self_.logger_ != nullptr) {
+ self_.logger_->log_socket_value(socket, {type, default_value}, *context_);
+ }
+
+ void *output_ptr = params_->get_output_data_ptr(graph_output_index);
+ type.copy_construct(default_value, output_ptr);
+ params_->output_set(graph_output_index);
+ }
+ }
+
+ void set_always_unused_graph_inputs()
+ {
+ for (const int i : self_.graph_inputs_.index_range()) {
+ const OutputSocket &socket = *self_.graph_inputs_[i];
+ const Node &node = socket.node();
+ const NodeState &node_state = *node_states_[node.index_in_graph()];
+ const OutputState &output_state = node_state.outputs[socket.index()];
+ if (output_state.usage == ValueUsage::Unused) {
+ params_->set_input_unused(i);
+ }
+ }
+ }
+
+ void schedule_side_effect_nodes(CurrentTask &current_task)
+ {
+ if (self_.side_effect_provider_ != nullptr) {
+ const Vector<const FunctionNode *> side_effect_nodes =
+ self_.side_effect_provider_->get_nodes_with_side_effects(*context_);
+ for (const FunctionNode *node : side_effect_nodes) {
+ NodeState &node_state = *node_states_[node->index_in_graph()];
+ node_state.has_side_effects = true;
+ this->with_locked_node(*node, node_state, current_task, [&](LockedNode &locked_node) {
+ this->schedule_node(locked_node, current_task);
+ });
+ }
+ }
+ }
+
+ void forward_newly_provided_inputs(CurrentTask &current_task)
+ {
+ LinearAllocator<> &allocator = this->get_main_or_local_allocator();
+ for (const int graph_input_index : self_.graph_inputs_.index_range()) {
+ std::atomic<uint8_t> &was_loaded = loaded_inputs_[graph_input_index];
+ if (was_loaded.load()) {
+ continue;
+ }
+ void *input_data = params_->try_get_input_data_ptr(graph_input_index);
+ if (input_data == nullptr) {
+ continue;
+ }
+ if (was_loaded.fetch_or(1)) {
+ /* The value was forwarded before. */
+ continue;
+ }
+ this->forward_newly_provided_input(current_task, allocator, graph_input_index, input_data);
+ }
+ }
+
+ void forward_newly_provided_input(CurrentTask &current_task,
+ LinearAllocator<> &allocator,
+ const int graph_input_index,
+ void *input_data)
+ {
+ const OutputSocket &socket = *self_.graph_inputs_[graph_input_index];
+ const CPPType &type = socket.type();
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ type.move_construct(input_data, buffer);
+ this->forward_value_to_linked_inputs(socket, {type, buffer}, current_task);
+ }
+
+ void notify_output_required(const OutputSocket &socket, CurrentTask &current_task)
+ {
+ const Node &node = socket.node();
+ const int index_in_node = socket.index();
+ NodeState &node_state = *node_states_[node.index_in_graph()];
+ OutputState &output_state = node_state.outputs[index_in_node];
+
+ /* The notified output socket might be an input of the entire graph. In this case, notify the
+ * caller that the input is required. */
+ if (node.is_dummy()) {
+ const int graph_input_index = self_.graph_inputs_.index_of(&socket);
+ std::atomic<uint8_t> &was_loaded = loaded_inputs_[graph_input_index];
+ if (was_loaded.load()) {
+ return;
+ }
+ void *input_data = params_->try_get_input_data_ptr_or_request(graph_input_index);
+ if (input_data == nullptr) {
+ return;
+ }
+ if (was_loaded.fetch_or(1)) {
+ /* The value was forwarded already. */
+ return;
+ }
+ this->forward_newly_provided_input(
+ current_task, this->get_main_or_local_allocator(), graph_input_index, input_data);
+ return;
+ }
+
+ BLI_assert(node.is_function());
+ this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) {
+ if (output_state.usage == ValueUsage::Used) {
+ return;
+ }
+ output_state.usage = ValueUsage::Used;
+ this->schedule_node(locked_node, current_task);
+ });
+ }
+
+ void notify_output_unused(const OutputSocket &socket, CurrentTask &current_task)
+ {
+ const Node &node = socket.node();
+ const int index_in_node = socket.index();
+ NodeState &node_state = *node_states_[node.index_in_graph()];
+ OutputState &output_state = node_state.outputs[index_in_node];
+
+ this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) {
+ output_state.potential_target_sockets -= 1;
+ if (output_state.potential_target_sockets == 0) {
+ BLI_assert(output_state.usage != ValueUsage::Unused);
+ if (output_state.usage == ValueUsage::Maybe) {
+ output_state.usage = ValueUsage::Unused;
+ if (node.is_dummy()) {
+ const int graph_input_index = self_.graph_inputs_.index_of(&socket);
+ params_->set_input_unused(graph_input_index);
+ }
+ else {
+ this->schedule_node(locked_node, current_task);
+ }
+ }
+ }
+ });
+ }
+
+ void schedule_node(LockedNode &locked_node, CurrentTask &current_task)
+ {
+ BLI_assert(locked_node.node.is_function());
+ switch (locked_node.node_state.schedule_state) {
+ case NodeScheduleState::NotScheduled: {
+ locked_node.node_state.schedule_state = NodeScheduleState::Scheduled;
+ const FunctionNode &node = static_cast<const FunctionNode &>(locked_node.node);
+ if (this->use_multi_threading()) {
+ std::lock_guard lock{current_task.mutex};
+ current_task.scheduled_nodes.append(&node);
+ }
+ else {
+ current_task.scheduled_nodes.append(&node);
+ }
+ current_task.has_scheduled_nodes.store(true, std::memory_order_relaxed);
+ break;
+ }
+ case NodeScheduleState::Scheduled: {
+ break;
+ }
+ case NodeScheduleState::Running: {
+ locked_node.node_state.schedule_state = NodeScheduleState::RunningAndRescheduled;
+ break;
+ }
+ case NodeScheduleState::RunningAndRescheduled: {
+ break;
+ }
+ }
+ }
+
+ void with_locked_node(const Node &node,
+ NodeState &node_state,
+ CurrentTask &current_task,
+ const FunctionRef<void(LockedNode &)> f)
+ {
+ BLI_assert(&node_state == node_states_[node.index_in_graph()]);
+
+ LockedNode locked_node{node, node_state};
+ if (this->use_multi_threading()) {
+ std::lock_guard lock{node_state.mutex};
+ threading::isolate_task([&]() { f(locked_node); });
+ }
+ else {
+ f(locked_node);
+ }
+
+ this->send_output_required_notifications(locked_node.delayed_required_outputs, current_task);
+ this->send_output_unused_notifications(locked_node.delayed_unused_outputs, current_task);
+ }
+
+ void send_output_required_notifications(const Span<const OutputSocket *> sockets,
+ CurrentTask &current_task)
+ {
+ for (const OutputSocket *socket : sockets) {
+ this->notify_output_required(*socket, current_task);
+ }
+ }
+
+ void send_output_unused_notifications(const Span<const OutputSocket *> sockets,
+ CurrentTask &current_task)
+ {
+ for (const OutputSocket *socket : sockets) {
+ this->notify_output_unused(*socket, current_task);
+ }
+ }
+
+ void run_task(CurrentTask &current_task)
+ {
+ while (!current_task.scheduled_nodes.is_empty()) {
+ const FunctionNode &node = *current_task.scheduled_nodes.pop_last();
+ if (current_task.scheduled_nodes.is_empty()) {
+ current_task.has_scheduled_nodes.store(false, std::memory_order_relaxed);
+ }
+ this->run_node_task(node, current_task);
+ }
+ }
+
+ void run_node_task(const FunctionNode &node, CurrentTask &current_task)
+ {
+ NodeState &node_state = *node_states_[node.index_in_graph()];
+ LinearAllocator<> &allocator = this->get_main_or_local_allocator();
+ const LazyFunction &fn = node.function();
+
+ bool node_needs_execution = false;
+ this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) {
+ BLI_assert(node_state.schedule_state == NodeScheduleState::Scheduled);
+ node_state.schedule_state = NodeScheduleState::Running;
+
+ if (node_state.node_has_finished) {
+ return;
+ }
+
+ bool required_uncomputed_output_exists = false;
+ for (OutputState &output_state : node_state.outputs) {
+ output_state.usage_for_execution = output_state.usage;
+ if (output_state.usage == ValueUsage::Used && !output_state.has_been_computed) {
+ required_uncomputed_output_exists = true;
+ }
+ }
+ if (!required_uncomputed_output_exists && !node_state.has_side_effects) {
+ return;
+ }
+
+ if (node_state.had_initialization) {
+ /* Initialize storage. */
+ node_state.storage = fn.init_storage(allocator);
+
+ /* Load unlinked inputs. */
+ for (const int input_index : node.inputs().index_range()) {
+ const InputSocket &input_socket = node.input(input_index);
+ if (input_socket.origin() != nullptr) {
+ continue;
+ }
+ InputState &input_state = node_state.inputs[input_index];
+ const CPPType &type = input_socket.type();
+ const void *default_value = input_socket.default_value();
+ BLI_assert(default_value != nullptr);
+ if (self_.logger_ != nullptr) {
+ self_.logger_->log_socket_value(input_socket, {type, default_value}, *context_);
+ }
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ type.copy_construct(default_value, buffer);
+ this->forward_value_to_input(locked_node, input_state, {type, buffer}, current_task);
+ }
+
+ /* Request linked inputs that are always needed. */
+ const Span<Input> fn_inputs = fn.inputs();
+ for (const int input_index : fn_inputs.index_range()) {
+ const Input &fn_input = fn_inputs[input_index];
+ if (fn_input.usage == ValueUsage::Used) {
+ const InputSocket &input_socket = node.input(input_index);
+ this->set_input_required(locked_node, input_socket);
+ }
+ }
+
+ node_state.had_initialization = false;
+ }
+
+ for (const int input_index : node_state.inputs.index_range()) {
+ InputState &input_state = node_state.inputs[input_index];
+ if (input_state.was_ready_for_execution) {
+ continue;
+ }
+ if (input_state.value != nullptr) {
+ input_state.was_ready_for_execution = true;
+ continue;
+ }
+ if (input_state.usage == ValueUsage::Used) {
+ return;
+ }
+ }
+
+ node_needs_execution = true;
+ });
+
+ if (node_needs_execution) {
+ /* Importantly, the node must not be locked when it is executed. That would result in locks
+ * being hold very long in some cases and results in multiple locks being hold by the same
+ * thread in the same graph which can lead to deadlocks. */
+ this->execute_node(node, node_state, current_task);
+ }
+
+ this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) {
+#ifdef DEBUG
+ if (node_needs_execution) {
+ this->assert_expected_outputs_have_been_computed(locked_node);
+ }
+#endif
+ 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_state.node_has_finished) {
+ this->schedule_node(locked_node, current_task);
+ }
+ });
+ }
+
+ void assert_expected_outputs_have_been_computed(LockedNode &locked_node)
+ {
+ const FunctionNode &node = static_cast<const FunctionNode &>(locked_node.node);
+ const NodeState &node_state = locked_node.node_state;
+
+ if (node_state.missing_required_inputs > 0) {
+ return;
+ }
+ if (node_state.schedule_state == NodeScheduleState::RunningAndRescheduled) {
+ return;
+ }
+ Vector<const OutputSocket *> missing_outputs;
+ for (const int i : node_state.outputs.index_range()) {
+ const OutputState &output_state = node_state.outputs[i];
+ if (output_state.usage_for_execution == ValueUsage::Used) {
+ if (!output_state.has_been_computed) {
+ missing_outputs.append(&node.output(i));
+ }
+ }
+ }
+ if (!missing_outputs.is_empty()) {
+ if (self_.logger_ != nullptr) {
+ self_.logger_->dump_when_outputs_are_missing(node, missing_outputs, *context_);
+ }
+ BLI_assert_unreachable();
+ }
+ }
+
+ void finish_node_if_possible(LockedNode &locked_node)
+ {
+ const Node &node = locked_node.node;
+ NodeState &node_state = locked_node.node_state;
+
+ if (node_state.node_has_finished) {
+ /* Was finished already. */
+ return;
+ }
+ /* If there are outputs that may still be used, the node is not done yet. */
+ for (const OutputState &output_state : node_state.outputs) {
+ if (output_state.usage != ValueUsage::Unused && !output_state.has_been_computed) {
+ return;
+ }
+ }
+ /* If the node is still waiting for inputs, it is not done yet. */
+ for (const InputState &input_state : node_state.inputs) {
+ if (input_state.usage == ValueUsage::Used && !input_state.was_ready_for_execution) {
+ return;
+ }
+ }
+
+ node_state.node_has_finished = true;
+
+ for (const int input_index : node_state.inputs.index_range()) {
+ const InputSocket &input_socket = node.input(input_index);
+ InputState &input_state = node_state.inputs[input_index];
+ if (input_state.usage == ValueUsage::Maybe) {
+ this->set_input_unused(locked_node, input_socket);
+ }
+ else if (input_state.usage == ValueUsage::Used) {
+ this->destruct_input_value_if_exists(input_state, input_socket.type());
+ }
+ }
+
+ if (node_state.storage != nullptr) {
+ if (node.is_function()) {
+ const FunctionNode &fn_node = static_cast<const FunctionNode &>(node);
+ fn_node.function().destruct_storage(node_state.storage);
+ }
+ node_state.storage = nullptr;
+ }
+ }
+
+ void destruct_input_value_if_exists(InputState &input_state, const CPPType &type)
+ {
+ if (input_state.value != nullptr) {
+ type.destruct(input_state.value);
+ input_state.value = nullptr;
+ }
+ }
+
+ void execute_node(const FunctionNode &node, NodeState &node_state, CurrentTask &current_task);
+
+ void set_input_unused_during_execution(const Node &node,
+ NodeState &node_state,
+ const int input_index,
+ CurrentTask &current_task)
+ {
+ const InputSocket &input_socket = node.input(input_index);
+ this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) {
+ this->set_input_unused(locked_node, input_socket);
+ });
+ }
+
+ void set_input_unused(LockedNode &locked_node, const InputSocket &input_socket)
+ {
+ NodeState &node_state = locked_node.node_state;
+ const int input_index = input_socket.index();
+ InputState &input_state = node_state.inputs[input_index];
+
+ BLI_assert(input_state.usage != ValueUsage::Used);
+ if (input_state.usage == ValueUsage::Unused) {
+ return;
+ }
+ input_state.usage = ValueUsage::Unused;
+
+ this->destruct_input_value_if_exists(input_state, input_socket.type());
+ if (input_state.was_ready_for_execution) {
+ return;
+ }
+ const OutputSocket *origin = input_socket.origin();
+ if (origin != nullptr) {
+ locked_node.delayed_unused_outputs.append(origin);
+ }
+ }
+
+ void *set_input_required_during_execution(const Node &node,
+ NodeState &node_state,
+ const int input_index,
+ CurrentTask &current_task)
+ {
+ const InputSocket &input_socket = node.input(input_index);
+ void *result;
+ this->with_locked_node(node, node_state, current_task, [&](LockedNode &locked_node) {
+ result = this->set_input_required(locked_node, input_socket);
+ });
+ return result;
+ }
+
+ void *set_input_required(LockedNode &locked_node, const InputSocket &input_socket)
+ {
+ BLI_assert(&locked_node.node == &input_socket.node());
+ NodeState &node_state = locked_node.node_state;
+ const int input_index = input_socket.index();
+ InputState &input_state = node_state.inputs[input_index];
+
+ BLI_assert(input_state.usage != ValueUsage::Unused);
+
+ if (input_state.value != nullptr) {
+ input_state.was_ready_for_execution = true;
+ return input_state.value;
+ }
+ if (input_state.usage == ValueUsage::Used) {
+ return nullptr;
+ }
+ input_state.usage = ValueUsage::Used;
+ node_state.missing_required_inputs += 1;
+
+ const OutputSocket *origin_socket = input_socket.origin();
+ /* Unlinked inputs are always loaded in advance. */
+ BLI_assert(origin_socket != nullptr);
+ locked_node.delayed_required_outputs.append(origin_socket);
+ return nullptr;
+ }
+
+ void forward_value_to_linked_inputs(const OutputSocket &from_socket,
+ GMutablePointer value_to_forward,
+ CurrentTask &current_task)
+ {
+ BLI_assert(value_to_forward.get() != nullptr);
+ LinearAllocator<> &allocator = this->get_main_or_local_allocator();
+ const CPPType &type = *value_to_forward.type();
+
+ if (self_.logger_ != nullptr) {
+ self_.logger_->log_socket_value(from_socket, value_to_forward, *context_);
+ }
+
+ const Span<const InputSocket *> targets = from_socket.targets();
+ for (const InputSocket *target_socket : targets) {
+ const Node &target_node = target_socket->node();
+ NodeState &node_state = *node_states_[target_node.index_in_graph()];
+ const int input_index = target_socket->index();
+ InputState &input_state = node_state.inputs[input_index];
+ const bool is_last_target = target_socket == targets.last();
+#ifdef DEBUG
+ if (input_state.value != nullptr) {
+ if (self_.logger_ != nullptr) {
+ self_.logger_->dump_when_input_is_set_twice(*target_socket, from_socket, *context_);
+ }
+ BLI_assert_unreachable();
+ }
+#endif
+ BLI_assert(!input_state.was_ready_for_execution);
+ BLI_assert(target_socket->type() == type);
+ BLI_assert(target_socket->origin() == &from_socket);
+
+ if (self_.logger_ != nullptr) {
+ self_.logger_->log_socket_value(*target_socket, value_to_forward, *context_);
+ }
+ if (target_node.is_dummy()) {
+ /* Forward the value to the outside of the graph. */
+ const int graph_output_index = self_.graph_outputs_.index_of_try(target_socket);
+ if (graph_output_index != -1 &&
+ params_->get_output_usage(graph_output_index) != ValueUsage::Unused) {
+ void *dst_buffer = params_->get_output_data_ptr(graph_output_index);
+ if (is_last_target) {
+ type.move_construct(value_to_forward.get(), dst_buffer);
+ }
+ else {
+ type.copy_construct(value_to_forward.get(), dst_buffer);
+ }
+ params_->output_set(graph_output_index);
+ }
+ continue;
+ }
+ this->with_locked_node(target_node, node_state, current_task, [&](LockedNode &locked_node) {
+ if (input_state.usage == ValueUsage::Unused) {
+ return;
+ }
+ if (is_last_target) {
+ /* No need to make a copy if this is the last target. */
+ this->forward_value_to_input(locked_node, input_state, value_to_forward, current_task);
+ value_to_forward = {};
+ }
+ else {
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ type.copy_construct(value_to_forward.get(), buffer);
+ this->forward_value_to_input(locked_node, input_state, {type, buffer}, current_task);
+ }
+ });
+ }
+ if (value_to_forward.get() != nullptr) {
+ value_to_forward.destruct();
+ }
+ }
+
+ void forward_value_to_input(LockedNode &locked_node,
+ InputState &input_state,
+ GMutablePointer value,
+ CurrentTask &current_task)
+ {
+ NodeState &node_state = locked_node.node_state;
+
+ BLI_assert(input_state.value == nullptr);
+ BLI_assert(!input_state.was_ready_for_execution);
+ input_state.value = value.get();
+
+ if (input_state.usage == ValueUsage::Used) {
+ node_state.missing_required_inputs -= 1;
+ if (node_state.missing_required_inputs == 0) {
+ this->schedule_node(locked_node, current_task);
+ }
+ }
+ }
+
+ bool use_multi_threading() const
+ {
+ return task_pool_.load() != nullptr;
+ }
+
+ bool try_enable_multi_threading()
+ {
+ if (this->use_multi_threading()) {
+ return true;
+ }
+#ifdef FN_LAZY_FUNCTION_DEBUG_THREADS
+ /* Only the current main thread is allowed to enabled multi-threading, because the executor is
+ * still in single-threaded mode. */
+ if (current_main_thread_ != std::this_thread::get_id()) {
+ BLI_assert_unreachable();
+ }
+#endif
+ /* Check of the caller supports multi-threading. */
+ if (!params_->try_enable_multi_threading()) {
+ return false;
+ }
+ /* Avoid using multiple threads when only one thread can be used anyway. */
+ if (BLI_system_thread_count() <= 1) {
+ return false;
+ }
+ task_pool_.store(BLI_task_pool_create(this, TASK_PRIORITY_HIGH));
+ return true;
+ }
+
+ /**
+ * Allow other threads to steal all the nodes that are currently scheduled on this thread.
+ */
+ void move_scheduled_nodes_to_task_pool(CurrentTask &current_task)
+ {
+ BLI_assert(this->use_multi_threading());
+ using FunctionNodeVector = Vector<const FunctionNode *>;
+ FunctionNodeVector *nodes = MEM_new<FunctionNodeVector>(__func__);
+ {
+ std::lock_guard lock{current_task.mutex};
+ if (current_task.scheduled_nodes.is_empty()) {
+ return;
+ }
+ *nodes = std::move(current_task.scheduled_nodes);
+ current_task.has_scheduled_nodes.store(false, std::memory_order_relaxed);
+ }
+ /* All nodes are pushed as a single task in the pool. This avoids unnecessary threading
+ * overhead when the nodes are fast to compute. */
+ BLI_task_pool_push(
+ task_pool_.load(),
+ [](TaskPool *pool, void *data) {
+ Executor &executor = *static_cast<Executor *>(BLI_task_pool_user_data(pool));
+ FunctionNodeVector &nodes = *static_cast<FunctionNodeVector *>(data);
+ CurrentTask new_current_task;
+ new_current_task.scheduled_nodes = std::move(nodes);
+ new_current_task.has_scheduled_nodes.store(true, std::memory_order_relaxed);
+ executor.run_task(new_current_task);
+ },
+ nodes,
+ true,
+ [](TaskPool * /*pool*/, void *data) {
+ MEM_delete(static_cast<FunctionNodeVector *>(data));
+ });
+ }
+
+ LinearAllocator<> &get_main_or_local_allocator()
+ {
+ if (this->use_multi_threading()) {
+ return local_allocators_.local();
+ }
+ return *main_local_allocator_;
+ }
+};
+
+class GraphExecutorLFParams final : public Params {
+ private:
+ Executor &executor_;
+ const Node &node_;
+ NodeState &node_state_;
+ CurrentTask &current_task_;
+
+ public:
+ GraphExecutorLFParams(const LazyFunction &fn,
+ Executor &executor,
+ const Node &node,
+ NodeState &node_state,
+ CurrentTask &current_task)
+ : Params(fn, executor.use_multi_threading()),
+ executor_(executor),
+ node_(node),
+ node_state_(node_state),
+ current_task_(current_task)
+ {
+ }
+
+ private:
+ void *try_get_input_data_ptr_impl(const int index) const override
+ {
+ const InputState &input_state = node_state_.inputs[index];
+ if (input_state.was_ready_for_execution) {
+ return input_state.value;
+ }
+ return nullptr;
+ }
+
+ void *try_get_input_data_ptr_or_request_impl(const int index) override
+ {
+ const InputState &input_state = node_state_.inputs[index];
+ if (input_state.was_ready_for_execution) {
+ return input_state.value;
+ }
+ return executor_.set_input_required_during_execution(node_, node_state_, index, current_task_);
+ }
+
+ void *get_output_data_ptr_impl(const int index) override
+ {
+ OutputState &output_state = node_state_.outputs[index];
+ BLI_assert(!output_state.has_been_computed);
+ if (output_state.value == nullptr) {
+ LinearAllocator<> &allocator = executor_.get_main_or_local_allocator();
+ const CPPType &type = node_.output(index).type();
+ output_state.value = allocator.allocate(type.size(), type.alignment());
+ }
+ return output_state.value;
+ }
+
+ void output_set_impl(const int index) override
+ {
+ OutputState &output_state = node_state_.outputs[index];
+ BLI_assert(!output_state.has_been_computed);
+ BLI_assert(output_state.value != nullptr);
+ const OutputSocket &output_socket = node_.output(index);
+ executor_.forward_value_to_linked_inputs(
+ output_socket, {output_socket.type(), output_state.value}, current_task_);
+ output_state.value = nullptr;
+ output_state.has_been_computed = true;
+ }
+
+ bool output_was_set_impl(const int index) const override
+ {
+ const OutputState &output_state = node_state_.outputs[index];
+ return output_state.has_been_computed;
+ }
+
+ ValueUsage get_output_usage_impl(const int index) const override
+ {
+ const OutputState &output_state = node_state_.outputs[index];
+ return output_state.usage_for_execution;
+ }
+
+ void set_input_unused_impl(const int index) override
+ {
+ executor_.set_input_unused_during_execution(node_, node_state_, index, current_task_);
+ }
+
+ bool try_enable_multi_threading_impl() override
+ {
+ return executor_.try_enable_multi_threading();
+ }
+};
+
+/**
+ * Actually execute the node.
+ *
+ * Making this `inline` results in a simpler back-trace in release builds.
+ */
+inline void Executor::execute_node(const FunctionNode &node,
+ NodeState &node_state,
+ CurrentTask &current_task)
+{
+ const LazyFunction &fn = node.function();
+ GraphExecutorLFParams node_params{fn, *this, node, node_state, current_task};
+ BLI_assert(context_ != nullptr);
+ Context fn_context = *context_;
+ fn_context.storage = node_state.storage;
+
+ if (self_.logger_ != nullptr) {
+ self_.logger_->log_before_node_execute(node, node_params, fn_context);
+ }
+
+ /* This is run when the execution of the node calls `lazy_threading::send_hint` to indicate that
+ * the execution will take a while. In this case, other tasks waiting on this thread should be
+ * allowed to be picked up by another thread. */
+ auto blocking_hint_fn = [&]() {
+ if (!current_task.has_scheduled_nodes.load()) {
+ return;
+ }
+ if (!this->try_enable_multi_threading()) {
+ return;
+ }
+ this->move_scheduled_nodes_to_task_pool(current_task);
+ };
+
+ lazy_threading::HintReceiver blocking_hint_receiver{blocking_hint_fn};
+ fn.execute(node_params, fn_context);
+
+ if (self_.logger_ != nullptr) {
+ self_.logger_->log_after_node_execute(node, node_params, fn_context);
+ }
+}
+
+GraphExecutor::GraphExecutor(const Graph &graph,
+ const Span<const OutputSocket *> graph_inputs,
+ const Span<const InputSocket *> graph_outputs,
+ const Logger *logger,
+ const SideEffectProvider *side_effect_provider)
+ : graph_(graph),
+ graph_inputs_(graph_inputs),
+ graph_outputs_(graph_outputs),
+ logger_(logger),
+ side_effect_provider_(side_effect_provider)
+{
+ for (const OutputSocket *socket : graph_inputs_) {
+ BLI_assert(socket->node().is_dummy());
+ inputs_.append({"In", socket->type(), ValueUsage::Maybe});
+ }
+ for (const InputSocket *socket : graph_outputs_) {
+ BLI_assert(socket->node().is_dummy());
+ outputs_.append({"Out", socket->type()});
+ }
+}
+
+void GraphExecutor::execute_impl(Params &params, const Context &context) const
+{
+ Executor &executor = *static_cast<Executor *>(context.storage);
+ executor.execute(params, context);
+}
+
+void *GraphExecutor::init_storage(LinearAllocator<> &allocator) const
+{
+ Executor &executor = *allocator.construct<Executor>(*this).release();
+ return &executor;
+}
+
+void GraphExecutor::destruct_storage(void *storage) const
+{
+ std::destroy_at(static_cast<Executor *>(storage));
+}
+
+void GraphExecutorLogger::log_socket_value(const Socket &socket,
+ const GPointer value,
+ const Context &context) const
+{
+ UNUSED_VARS(socket, value, context);
+}
+
+void GraphExecutorLogger::log_before_node_execute(const FunctionNode &node,
+ const Params &params,
+ const Context &context) const
+{
+ UNUSED_VARS(node, params, context);
+}
+
+void GraphExecutorLogger::log_after_node_execute(const FunctionNode &node,
+ const Params &params,
+ const Context &context) const
+{
+ UNUSED_VARS(node, params, context);
+}
+
+Vector<const FunctionNode *> GraphExecutorSideEffectProvider::get_nodes_with_side_effects(
+ const Context &context) const
+{
+ UNUSED_VARS(context);
+ return {};
+}
+
+void GraphExecutorLogger::dump_when_outputs_are_missing(const FunctionNode &node,
+ Span<const OutputSocket *> missing_sockets,
+ const Context &context) const
+{
+ UNUSED_VARS(node, missing_sockets, context);
+}
+
+void GraphExecutorLogger::dump_when_input_is_set_twice(const InputSocket &target_socket,
+ const OutputSocket &from_socket,
+ const Context &context) const
+{
+ UNUSED_VARS(target_socket, from_socket, context);
+}
+
+} // namespace blender::fn::lazy_function
diff --git a/source/blender/functions/tests/FN_lazy_function_test.cc b/source/blender/functions/tests/FN_lazy_function_test.cc
new file mode 100644
index 00000000000..8df064cd8a6
--- /dev/null
+++ b/source/blender/functions/tests/FN_lazy_function_test.cc
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: Apache-2.0 */
+
+#include "testing/testing.h"
+
+#include "FN_lazy_function_execute.hh"
+#include "FN_lazy_function_graph.hh"
+#include "FN_lazy_function_graph_executor.hh"
+
+#include "BLI_task.h"
+#include "BLI_timeit.hh"
+
+namespace blender::fn::lazy_function::tests {
+
+class AddLazyFunction : public LazyFunction {
+ public:
+ AddLazyFunction()
+ {
+ debug_name_ = "Add";
+ inputs_.append({"A", CPPType::get<int>()});
+ inputs_.append({"B", CPPType::get<int>()});
+ outputs_.append({"Result", CPPType::get<int>()});
+ }
+
+ void execute_impl(Params &params, const Context &UNUSED(context)) const override
+ {
+ const int a = params.get_input<int>(0);
+ const int b = params.get_input<int>(1);
+ params.set_output(0, a + b);
+ }
+};
+
+class StoreValueFunction : public LazyFunction {
+ private:
+ int *dst1_;
+ int *dst2_;
+
+ public:
+ StoreValueFunction(int *dst1, int *dst2) : dst1_(dst1), dst2_(dst2)
+ {
+ debug_name_ = "Store Value";
+ inputs_.append({"A", CPPType::get<int>()});
+ inputs_.append({"B", CPPType::get<int>(), ValueUsage::Maybe});
+ }
+
+ void execute_impl(Params &params, const Context &UNUSED(context)) const override
+ {
+ *dst1_ = params.get_input<int>(0);
+ if (int *value = params.try_get_input_data_ptr_or_request<int>(1)) {
+ *dst2_ = *value;
+ }
+ }
+};
+
+class SimpleSideEffectProvider : public GraphExecutor::SideEffectProvider {
+ private:
+ Vector<const FunctionNode *> side_effect_nodes_;
+
+ public:
+ SimpleSideEffectProvider(Span<const FunctionNode *> side_effect_nodes)
+ : side_effect_nodes_(side_effect_nodes)
+ {
+ }
+
+ Vector<const FunctionNode *> get_nodes_with_side_effects(
+ const Context &UNUSED(context)) const override
+ {
+ return side_effect_nodes_;
+ }
+};
+
+TEST(lazy_function, SimpleAdd)
+{
+ const AddLazyFunction add_fn;
+ int result = 0;
+ execute_lazy_function_eagerly(add_fn, nullptr, std::make_tuple(30, 5), std::make_tuple(&result));
+ EXPECT_EQ(result, 35);
+}
+
+TEST(lazy_function, SideEffects)
+{
+ BLI_task_scheduler_init();
+ int dst1 = 0;
+ int dst2 = 0;
+
+ const AddLazyFunction add_fn;
+ const StoreValueFunction store_fn{&dst1, &dst2};
+
+ Graph graph;
+ FunctionNode &add_node_1 = graph.add_function(add_fn);
+ FunctionNode &add_node_2 = graph.add_function(add_fn);
+ FunctionNode &store_node = graph.add_function(store_fn);
+ DummyNode &input_node = graph.add_dummy({}, {&CPPType::get<int>()});
+
+ graph.add_link(input_node.output(0), add_node_1.input(0));
+ graph.add_link(input_node.output(0), add_node_2.input(0));
+ graph.add_link(add_node_1.output(0), store_node.input(0));
+ graph.add_link(add_node_2.output(0), store_node.input(1));
+
+ const int value_10 = 10;
+ const int value_100 = 100;
+ add_node_1.input(1).set_default_value(&value_10);
+ add_node_2.input(1).set_default_value(&value_100);
+
+ graph.update_node_indices();
+
+ SimpleSideEffectProvider side_effect_provider{{&store_node}};
+
+ GraphExecutor executor_fn{graph, {&input_node.output(0)}, {}, nullptr, &side_effect_provider};
+ execute_lazy_function_eagerly(executor_fn, nullptr, std::make_tuple(5), std::make_tuple());
+
+ EXPECT_EQ(dst1, 15);
+ EXPECT_EQ(dst2, 105);
+}
+
+} // namespace blender::fn::lazy_function::tests
diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt
index df66a806c16..9e1929b60a8 100644
--- a/source/blender/geometry/CMakeLists.txt
+++ b/source/blender/geometry/CMakeLists.txt
@@ -16,6 +16,7 @@ set(INC
set(SRC
intern/add_curves_on_mesh.cc
+ intern/fillet_curves.cc
intern/mesh_merge_by_distance.cc
intern/mesh_primitive_cuboid.cc
intern/mesh_to_curve_convert.cc
@@ -26,9 +27,11 @@ set(SRC
intern/reverse_uv_sampler.cc
intern/set_curve_type.cc
intern/subdivide_curves.cc
- intern/uv_parametrizer.c
+ intern/trim_curves.cc
+ intern/uv_parametrizer.cc
GEO_add_curves_on_mesh.hh
+ GEO_fillet_curves.hh
GEO_mesh_merge_by_distance.hh
GEO_mesh_primitive_cuboid.hh
GEO_mesh_to_curve.hh
@@ -39,6 +42,7 @@ set(SRC
GEO_reverse_uv_sampler.hh
GEO_set_curve_type.hh
GEO_subdivide_curves.hh
+ GEO_trim_curves.hh
GEO_uv_parametrizer.h
)
diff --git a/source/blender/geometry/GEO_add_curves_on_mesh.hh b/source/blender/geometry/GEO_add_curves_on_mesh.hh
index cf60a8e8ace..2d2ba89a18f 100644
--- a/source/blender/geometry/GEO_add_curves_on_mesh.hh
+++ b/source/blender/geometry/GEO_add_curves_on_mesh.hh
@@ -13,13 +13,13 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "GEO_reverse_uv_sampler.hh"
+
namespace blender::geometry {
struct AddCurvesOnMeshInputs {
- /** Information about the root points where new curves should be generated. */
- Span<float3> root_positions_cu;
- Span<float3> bary_coords;
- Span<int> looptri_indices;
+ /** UV Coordinates at which the new curves should be added. */
+ Span<float2> uvs;
/** Determines shape of new curves. */
bool interpolate_length = false;
@@ -30,25 +30,32 @@ struct AddCurvesOnMeshInputs {
/** Information about the surface that the new curves are attached to. */
const Mesh *surface = nullptr;
- BVHTreeFromMesh *surface_bvh = nullptr;
- Span<MLoopTri> surface_looptris;
- Span<float2> surface_uv_map;
+ const ReverseUVSampler *reverse_uv_sampler = nullptr;
Span<float3> corner_normals_su;
- /** Transformation matrices. */
- float4x4 curves_to_surface_mat;
- float4x4 surface_to_curves_normal_mat;
+ bke::CurvesSurfaceTransforms *transforms = nullptr;
/**
* KD-Tree that contains the root points of existing curves. This is only necessary when
* interpolation is used.
*/
KDTree_3d *old_roots_kdtree = nullptr;
+
+ bool r_uv_error = false;
+};
+
+struct AddCurvesOnMeshOutputs {
+ bool uv_error = false;
};
/**
* Generate new curves on a mesh surface with the given inputs. Existing curves stay intact.
*/
-void add_curves_on_mesh(bke::CurvesGeometry &curves, const AddCurvesOnMeshInputs &inputs);
+AddCurvesOnMeshOutputs add_curves_on_mesh(bke::CurvesGeometry &curves,
+ const AddCurvesOnMeshInputs &inputs);
+
+float3 compute_surface_point_normal(const MLoopTri &looptri,
+ const float3 &bary_coord,
+ const Span<float3> corner_normals);
} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_fillet_curves.hh b/source/blender/geometry/GEO_fillet_curves.hh
new file mode 100644
index 00000000000..1f832f8b6cc
--- /dev/null
+++ b/source/blender/geometry/GEO_fillet_curves.hh
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_function_ref.hh"
+#include "BLI_index_mask.hh"
+
+#include "BKE_curves.hh"
+
+namespace blender::geometry {
+
+bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves,
+ IndexMask curve_selection,
+ const VArray<float> &radius,
+ const VArray<int> &counts,
+ bool limit_radius);
+
+bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves,
+ IndexMask curve_selection,
+ const VArray<float> &radius,
+ bool limit_radius);
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh
index c480e4178cf..b0f24853dbc 100644
--- a/source/blender/geometry/GEO_mesh_to_curve.hh
+++ b/source/blender/geometry/GEO_mesh_to_curve.hh
@@ -1,12 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#pragma once
+
#include "BLI_index_mask.hh"
-#pragma once
+#include "BKE_curves.hh"
struct Mesh;
-struct Curves;
-class MeshComponent;
/** \file
* \ingroup geo
@@ -15,10 +15,15 @@ class MeshComponent;
namespace blender::geometry {
/**
- * Convert the mesh into one or many poly splines. Since splines cannot have branches,
- * intersections of more than three edges will become breaks in splines. Attributes that
+ * Convert the mesh into one or many poly curves. Since curves cannot have branches,
+ * intersections of more than three edges will become breaks in curves. Attributes that
* are not built-in on meshes and not curves are transferred to the result curve.
*/
-Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection);
+bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection);
+
+bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
+ Span<int> vert_indices,
+ Span<int> curve_offsets,
+ IndexRange cyclic_curves);
} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_point_merge_by_distance.hh b/source/blender/geometry/GEO_point_merge_by_distance.hh
index 1e977cf3bdc..92d871c62ab 100644
--- a/source/blender/geometry/GEO_point_merge_by_distance.hh
+++ b/source/blender/geometry/GEO_point_merge_by_distance.hh
@@ -17,7 +17,7 @@ namespace blender::geometry {
* Merge selected points into other selected points within the \a merge_distance. The merged
* indices favor speed over accuracy, since the results will depend on the order of the points.
*/
-PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
+PointCloud *point_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const IndexMask selection);
diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh
index 97399ccb0a5..35365167eba 100644
--- a/source/blender/geometry/GEO_resample_curves.hh
+++ b/source/blender/geometry/GEO_resample_curves.hh
@@ -4,12 +4,18 @@
#include "FN_field.hh"
-#include "BKE_geometry_set.hh"
-
-struct Curves;
+#include "BKE_anonymous_attribute.hh"
+#include "BKE_curves.hh"
namespace blender::geometry {
+using bke::CurvesGeometry;
+
+struct ResampleCurvesOutputAttributeIDs {
+ bke::AttributeIDRef tangent_id;
+ bke::AttributeIDRef normal_id;
+};
+
/**
* Create new curves where the selected curves have been resampled with a number of uniform-length
* samples defined by the count field. Interpolate attributes to the result, with an accuracy that
@@ -17,23 +23,26 @@ namespace blender::geometry {
*
* \note The values provided by the #count_field are clamped to 1 or greater.
*/
-Curves *resample_to_count(const CurveComponent &src_component,
- const fn::Field<bool> &selection_field,
- const fn::Field<int> &count_field);
+CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<int> &count_field,
+ const ResampleCurvesOutputAttributeIDs &output_ids = {});
/**
* Create new curves resampled to make each segment have the length specified by the
* #segment_length field input, rounded to make the length of each segment the same.
* The accuracy will depend on the curve's resolution parameter.
*/
-Curves *resample_to_length(const CurveComponent &src_component,
- const fn::Field<bool> &selection_field,
- const fn::Field<float> &segment_length_field);
+CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<float> &segment_length_field,
+ const ResampleCurvesOutputAttributeIDs &output_ids = {});
/**
* Evaluate each selected curve to its implicit evaluated points.
*/
-Curves *resample_to_evaluated(const CurveComponent &src_component,
- const fn::Field<bool> &selection_field);
+CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
+ const fn::Field<bool> &selection_field,
+ const ResampleCurvesOutputAttributeIDs &output_ids = {});
} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_set_curve_type.hh b/source/blender/geometry/GEO_set_curve_type.hh
index 6a75450006b..f38e63b1fc8 100644
--- a/source/blender/geometry/GEO_set_curve_type.hh
+++ b/source/blender/geometry/GEO_set_curve_type.hh
@@ -2,17 +2,10 @@
#pragma once
-#include "DNA_curves_types.h"
-
#include "BLI_function_ref.hh"
#include "BLI_index_mask.hh"
-struct Curves;
-class CurveComponent;
-
-namespace blender::bke {
-class CurvesGeometry;
-}
+#include "BKE_curves.hh"
namespace blender::geometry {
@@ -27,14 +20,13 @@ namespace blender::geometry {
*/
bool try_curves_conversion_in_place(IndexMask selection,
CurveType dst_type,
- FunctionRef<Curves &()> get_writable_curves_fn);
+ FunctionRef<bke::CurvesGeometry &()> get_writable_curves_fn);
/**
* Change the types of the selected curves, potentially changing the total point count.
*/
-Curves *convert_curves(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- IndexMask selection,
- CurveType dst_type);
+bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves,
+ IndexMask selection,
+ CurveType dst_type);
} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_subdivide_curves.hh b/source/blender/geometry/GEO_subdivide_curves.hh
index 66c2eb53496..ba55118baa4 100644
--- a/source/blender/geometry/GEO_subdivide_curves.hh
+++ b/source/blender/geometry/GEO_subdivide_curves.hh
@@ -4,11 +4,10 @@
#include "BLI_function_ref.hh"
#include "BLI_index_mask.hh"
+#include "BLI_virtual_array.hh"
#include "BKE_curves.hh"
-class CurveComponent;
-
namespace blender::geometry {
/**
@@ -18,9 +17,8 @@ namespace blender::geometry {
*
* \param selection: A selection of curves to consider when subdividing.
*/
-Curves *subdivide_curves(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- IndexMask selection,
- const VArray<int> &cuts);
+bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
+ IndexMask selection,
+ const VArray<int> &cuts);
} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_trim_curves.hh b/source/blender/geometry/GEO_trim_curves.hh
new file mode 100644
index 00000000000..8275e3cbace
--- /dev/null
+++ b/source/blender/geometry/GEO_trim_curves.hh
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "BLI_span.hh"
+
+#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
+#include "BKE_geometry_set.hh"
+
+namespace blender::geometry {
+
+/*
+ * Create a new Curves instance by trimming the input curves. Copying the selected splines
+ * between the start and end points.
+ */
+bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
+ IndexMask selection,
+ Span<bke::curves::CurvePoint> start_points,
+ Span<bke::curves::CurvePoint> end_points);
+
+/**
+ * Find the point(s) and piecewise segment corresponding to the given distance along the length of
+ * the curve. Returns points on the evaluated curve for Catmull-Rom and NURBS splines.
+ *
+ * \param curves: Curve geometry to sample.
+ * \param lengths: Distance along the curve on form [0.0, length] to determine the point for.
+ * \param curve_indices: Curve index to lookup for each 'length', negative index are set to 0.
+ * \param is_normalized: If true, 'lengths' are normalized to the interval [0.0, 1.0].
+ */
+Array<bke::curves::CurvePoint, 12> lookup_curve_points(const bke::CurvesGeometry &curves,
+ Span<float> lengths,
+ Span<int64_t> curve_indices,
+ bool is_normalized);
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h
index 2181f95945e..ff110f18ffb 100644
--- a/source/blender/geometry/GEO_uv_parametrizer.h
+++ b/source/blender/geometry/GEO_uv_parametrizer.h
@@ -13,8 +13,8 @@ extern "C" {
#endif
typedef struct ParamHandle ParamHandle; /* Handle to an array of charts. */
-typedef intptr_t ParamKey; /* Key (hash) for identifying verts and faces. */
-#define PARAM_KEY_MAX INTPTR_MAX
+typedef uintptr_t ParamKey; /* Key (hash) for identifying verts and faces. */
+#define PARAM_KEY_MAX UINTPTR_MAX
/* -------------------------------------------------------------------- */
/** \name Chart Construction:
@@ -103,7 +103,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
/** \name Average area for all charts
* \{ */
-void GEO_uv_parametrizer_average(ParamHandle *handle, bool ignore_pinned);
+void GEO_uv_parametrizer_average(ParamHandle *handle,
+ bool ignore_pinned,
+ bool scale_uv,
+ bool shear);
/** \} */
diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc
index 34551bd474f..bb5e2a0a28a 100644
--- a/source/blender/geometry/intern/add_curves_on_mesh.cc
+++ b/source/blender/geometry/intern/add_curves_on_mesh.cc
@@ -1,9 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_length_parameterize.hh"
+#include "BLI_task.hh"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
#include "BKE_mesh_sample.hh"
-#include "BKE_spline.hh"
#include "GEO_add_curves_on_mesh.hh"
+#include "GEO_reverse_uv_sampler.hh"
/**
* The code below uses a suffix naming convention to indicate the coordinate space:
@@ -25,9 +30,9 @@ struct NeighborCurve {
static constexpr int max_neighbors = 5;
using NeighborCurves = Vector<NeighborCurve, max_neighbors>;
-static float3 compute_surface_point_normal(const MLoopTri &looptri,
- const float3 &bary_coord,
- const Span<float3> corner_normals)
+float3 compute_surface_point_normal(const MLoopTri &looptri,
+ const float3 &bary_coord,
+ const Span<float3> corner_normals)
{
const int l0 = looptri.tri[0];
const int l1 = looptri.tri[1];
@@ -99,8 +104,8 @@ void interpolate_from_neighbors(const Span<NeighborCurves> neighbors_per_curve,
}
}
}
+ mixer.finalize(range);
});
- mixer.finalize();
}
static void interpolate_position_without_interpolation(
@@ -134,27 +139,26 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves,
const int old_curves_num,
const Span<float> new_lengths_cu,
const Span<float3> new_normals_su,
- const float4x4 &surface_to_curves_normal_mat,
- const float4x4 &curves_to_surface_mat,
- const BVHTreeFromMesh &surface_bvh,
- const Span<MLoopTri> surface_looptris,
- const Mesh &surface,
+ const bke::CurvesSurfaceTransforms &transforms,
+ const ReverseUVSampler &reverse_uv_sampler,
const Span<float3> corner_normals_su)
{
MutableSpan<float3> positions_cu = curves.positions_for_write();
const int added_curves_num = root_positions_cu.size();
+ const Span<float2> uv_coords = curves.surface_uv_coords();
+
threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) {
- for (const int i : range) {
- const NeighborCurves &neighbors = neighbors_per_curve[i];
- const int curve_i = old_curves_num + i;
+ for (const int added_curve_i : range) {
+ const NeighborCurves &neighbors = neighbors_per_curve[added_curve_i];
+ const int curve_i = old_curves_num + added_curve_i;
const IndexRange points = curves.points_for_curve(curve_i);
- const float length_cu = new_lengths_cu[i];
- const float3 &normal_su = new_normals_su[i];
- const float3 normal_cu = math::normalize(surface_to_curves_normal_mat * normal_su);
+ const float length_cu = new_lengths_cu[added_curve_i];
+ const float3 &normal_su = new_normals_su[added_curve_i];
+ const float3 normal_cu = math::normalize(transforms.surface_to_curves_normal * normal_su);
- const float3 &root_cu = root_positions_cu[i];
+ const float3 &root_cu = root_positions_cu[added_curve_i];
if (neighbors.is_empty()) {
/* If there are no neighbors, just make a straight line. */
@@ -167,26 +171,15 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves,
for (const NeighborCurve &neighbor : neighbors) {
const int neighbor_curve_i = neighbor.index;
- const float3 &neighbor_first_pos_cu = positions_cu[curves.offsets()[neighbor_curve_i]];
- const float3 neighbor_first_pos_su = curves_to_surface_mat * neighbor_first_pos_cu;
-
- BVHTreeNearest nearest;
- nearest.dist_sq = FLT_MAX;
- BLI_bvhtree_find_nearest(surface_bvh.tree,
- neighbor_first_pos_su,
- &nearest,
- surface_bvh.nearest_callback,
- const_cast<BVHTreeFromMesh *>(&surface_bvh));
- const int neighbor_looptri_index = nearest.index;
- const MLoopTri &neighbor_looptri = surface_looptris[neighbor_looptri_index];
-
- const float3 neighbor_bary_coord =
- bke::mesh_surface_sample::compute_bary_coord_in_triangle(
- surface, neighbor_looptri, nearest.co);
+ const float2 neighbor_uv = uv_coords[neighbor_curve_i];
+ const ReverseUVSampler::Result result = reverse_uv_sampler.sample(neighbor_uv);
+ if (result.type != ReverseUVSampler::ResultType::Ok) {
+ continue;
+ }
const float3 neighbor_normal_su = compute_surface_point_normal(
- surface_looptris[neighbor_looptri_index], neighbor_bary_coord, corner_normals_su);
- const float3 neighbor_normal_cu = math::normalize(surface_to_curves_normal_mat *
+ *result.looptri, result.bary_weights, corner_normals_su);
+ const float3 neighbor_normal_cu = math::normalize(transforms.surface_to_curves_normal *
neighbor_normal_su);
/* The rotation matrix used to transform relative coordinates of the neighbor curve
@@ -197,54 +190,96 @@ static void interpolate_position_with_interpolation(CurvesGeometry &curves,
const IndexRange neighbor_points = curves.points_for_curve(neighbor_curve_i);
const float3 &neighbor_root_cu = positions_cu[neighbor_points[0]];
- /* Use a temporary #PolySpline, because that's the easiest way to resample an
- * existing curve right now. Resampling is necessary if the length of the new curve
- * does not match the length of the neighbors or the number of handle points is
- * different. */
- PolySpline neighbor_spline;
- neighbor_spline.resize(neighbor_points.size());
- neighbor_spline.positions().copy_from(positions_cu.slice(neighbor_points));
- neighbor_spline.mark_cache_invalid();
+ /* Sample the positions on neighbors and mix them into the final positions of the curve.
+ * Resampling is necessary if the length of the new curve does not match the length of the
+ * neighbors or the number of handle points is different.
+ *
+ * TODO: The lengths can be cached so they aren't recomputed if a curve is a neighbor for
+ * multiple new curves. Also, allocations could be avoided by reusing some arrays. */
+
+ const Span<float3> neighbor_positions_cu = positions_cu.slice(neighbor_points);
+ if (neighbor_positions_cu.size() == 1) {
+ /* Skip interpolating positions from neighbors with only one point. */
+ continue;
+ }
+ Array<float, 32> lengths(length_parameterize::segments_num(neighbor_points.size(), false));
+ length_parameterize::accumulate_lengths<float3>(neighbor_positions_cu, false, lengths);
+ const float neighbor_length_cu = lengths.last();
- const float neighbor_length_cu = neighbor_spline.length();
+ Array<float, 32> sample_lengths(points.size());
const float length_factor = std::min(1.0f, length_cu / neighbor_length_cu);
-
const float resample_factor = (1.0f / (points.size() - 1.0f)) * length_factor;
- for (const int j : IndexRange(points.size())) {
- const Spline::LookupResult lookup = neighbor_spline.lookup_evaluated_factor(
- j * resample_factor);
- const float index_factor = lookup.evaluated_index + lookup.factor;
- float3 p;
- neighbor_spline.sample_with_index_factors<float3>(
- neighbor_spline.positions(), {&index_factor, 1}, {&p, 1});
- const float3 relative_coord = p - neighbor_root_cu;
- float3 rotated_relative_coord = relative_coord;
+ for (const int i : sample_lengths.index_range()) {
+ sample_lengths[i] = i * resample_factor * neighbor_length_cu;
+ }
+
+ Array<int, 32> indices(points.size());
+ Array<float, 32> factors(points.size());
+ length_parameterize::sample_at_lengths(lengths, sample_lengths, indices, factors);
+
+ for (const int i : IndexRange(points.size())) {
+ const float3 sample_cu = math::interpolate(neighbor_positions_cu[indices[i]],
+ neighbor_positions_cu[indices[i] + 1],
+ factors[i]);
+ const float3 relative_to_root_cu = sample_cu - neighbor_root_cu;
+ float3 rotated_relative_coord = relative_to_root_cu;
mul_m3_v3(normal_rotation_cu, rotated_relative_coord);
- positions_cu[points[j]] += neighbor.weight * rotated_relative_coord;
+ positions_cu[points[i]] += neighbor.weight * rotated_relative_coord;
}
}
}
});
}
-void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inputs)
+AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves,
+ const AddCurvesOnMeshInputs &inputs)
{
+ AddCurvesOnMeshOutputs outputs;
+
const bool use_interpolation = inputs.interpolate_length || inputs.interpolate_point_count ||
inputs.interpolate_shape;
+ Vector<float3> root_positions_cu;
+ Vector<float3> bary_coords;
+ Vector<const MLoopTri *> looptris;
+ Vector<float2> used_uvs;
+
+ /* Find faces that the passed in uvs belong to. */
+ const Span<MVert> surface_verts = inputs.surface->verts();
+ const Span<MLoop> surface_loops = inputs.surface->loops();
+ for (const int i : inputs.uvs.index_range()) {
+ const float2 &uv = inputs.uvs[i];
+ const ReverseUVSampler::Result result = inputs.reverse_uv_sampler->sample(uv);
+ if (result.type != ReverseUVSampler::ResultType::Ok) {
+ outputs.uv_error = true;
+ continue;
+ }
+ const MLoopTri &looptri = *result.looptri;
+ bary_coords.append(result.bary_weights);
+ looptris.append(&looptri);
+ const float3 root_position_su = attribute_math::mix3<float3>(
+ result.bary_weights,
+ surface_verts[surface_loops[looptri.tri[0]].v].co,
+ surface_verts[surface_loops[looptri.tri[1]].v].co,
+ surface_verts[surface_loops[looptri.tri[2]].v].co);
+ root_positions_cu.append(inputs.transforms->surface_to_curves * root_position_su);
+ used_uvs.append(uv);
+ }
+
Array<NeighborCurves> neighbors_per_curve;
if (use_interpolation) {
BLI_assert(inputs.old_roots_kdtree != nullptr);
- neighbors_per_curve = find_curve_neighbors(inputs.root_positions_cu, *inputs.old_roots_kdtree);
+ neighbors_per_curve = find_curve_neighbors(root_positions_cu, *inputs.old_roots_kdtree);
}
- const int added_curves_num = inputs.root_positions_cu.size();
+ const int added_curves_num = root_positions_cu.size();
const int old_points_num = curves.points_num();
const int old_curves_num = curves.curves_num();
const int new_curves_num = old_curves_num + added_curves_num;
/* Grow number of curves first, so that the offsets array can be filled. */
curves.resize(old_points_num, new_curves_num);
+ const IndexRange new_curves_range = curves.curves_range().drop_front(old_curves_num);
/* Compute new curve offsets. */
MutableSpan<int> curve_offsets = curves.offsets_for_write();
@@ -259,14 +294,18 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp
else {
new_point_counts_per_curve.fill(inputs.fallback_point_count);
}
- for (const int i : IndexRange(added_curves_num)) {
- curve_offsets[old_curves_num + i + 1] += curve_offsets[old_curves_num + i];
+ for (const int i : new_curves_range) {
+ curve_offsets[i + 1] += curve_offsets[i];
}
const int new_points_num = curves.offsets().last();
curves.resize(new_points_num, new_curves_num);
MutableSpan<float3> positions_cu = curves.positions_for_write();
+ /* Initialize attachment information. */
+ MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
+ surface_uv_coords.take_back(added_curves_num).copy_from(used_uvs);
+
/* Determine length of new curves. */
Array<float> new_lengths_cu(added_curves_num);
if (inputs.interpolate_length) {
@@ -293,25 +332,11 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp
Array<float3> new_normals_su(added_curves_num);
threading::parallel_for(IndexRange(added_curves_num), 256, [&](const IndexRange range) {
for (const int i : range) {
- const int looptri_index = inputs.looptri_indices[i];
- const float3 &bary_coord = inputs.bary_coords[i];
new_normals_su[i] = compute_surface_point_normal(
- inputs.surface_looptris[looptri_index], bary_coord, inputs.corner_normals_su);
+ *looptris[i], bary_coords[i], inputs.corner_normals_su);
}
});
- /* Propagate attachment information. */
- if (!inputs.surface_uv_map.is_empty()) {
- MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
- bke::mesh_surface_sample::sample_corner_attribute(
- *inputs.surface,
- inputs.looptri_indices,
- inputs.bary_coords,
- GVArray::ForSpan(inputs.surface_uv_map),
- IndexRange(added_curves_num),
- surface_uv_coords.take_back(added_curves_num));
- }
-
/* Update selection arrays when available. */
const VArray<float> points_selection = curves.selection_point_float();
if (points_selection.is_span()) {
@@ -321,34 +346,55 @@ void add_curves_on_mesh(CurvesGeometry &curves, const AddCurvesOnMeshInputs &inp
const VArray<float> curves_selection = curves.selection_curve_float();
if (curves_selection.is_span()) {
MutableSpan<float> curves_selection_span = curves.selection_curve_float_for_write();
- curves_selection_span.drop_front(old_curves_num).fill(1.0f);
+ curves_selection_span.slice(new_curves_range).fill(1.0f);
}
/* Initialize position attribute. */
if (inputs.interpolate_shape) {
interpolate_position_with_interpolation(curves,
- inputs.root_positions_cu,
+ root_positions_cu,
neighbors_per_curve,
old_curves_num,
new_lengths_cu,
new_normals_su,
- inputs.surface_to_curves_normal_mat,
- inputs.curves_to_surface_mat,
- *inputs.surface_bvh,
- inputs.surface_looptris,
- *inputs.surface,
+ *inputs.transforms,
+ *inputs.reverse_uv_sampler,
inputs.corner_normals_su);
}
else {
interpolate_position_without_interpolation(curves,
old_curves_num,
- inputs.root_positions_cu,
+ root_positions_cu,
new_lengths_cu,
new_normals_su,
- inputs.surface_to_curves_normal_mat);
+ inputs.transforms->surface_to_curves_normal);
}
- curves.update_curve_types();
+ curves.fill_curve_types(new_curves_range, CURVE_TYPE_CATMULL_ROM);
+
+ /* Explicitly set all other attributes besides those processed above to default values. */
+ bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
+ Set<std::string> attributes_to_skip{{"position",
+ "curve_type",
+ "surface_uv_coordinate",
+ ".selection_point_float",
+ ".selection_curve_float"}};
+ attributes.for_all(
+ [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData /*meta_data*/) {
+ if (id.is_named() && attributes_to_skip.contains(id.name())) {
+ return true;
+ }
+ bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id);
+ const int new_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? new_points_num :
+ new_curves_num;
+ const CPPType &type = attribute.span.type();
+ GMutableSpan new_data = attribute.span.take_back(new_elements_num);
+ type.fill_assign_n(type.default_value(), new_data.data(), new_data.size());
+ attribute.finish();
+ return true;
+ });
+
+ return outputs;
}
} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/fillet_curves.cc b/source/blender/geometry/intern/fillet_curves.cc
new file mode 100644
index 00000000000..1bbbee6edef
--- /dev/null
+++ b/source/blender/geometry/intern/fillet_curves.cc
@@ -0,0 +1,561 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
+#include "BKE_geometry_set.hh"
+
+#include "BLI_devirtualize_parameters.hh"
+#include "BLI_math_geom.h"
+#include "BLI_math_rotation.hh"
+#include "BLI_task.hh"
+
+#include "GEO_fillet_curves.hh"
+
+namespace blender::geometry {
+
+/**
+ * Return a range used to retrieve values from an array of values stored per point, but with an
+ * extra element at the end of each curve. This is useful for offsets within curves, where it is
+ * convenient to store the first 0 and have the last offset be the total result curve size.
+ */
+static IndexRange curve_dst_offsets(const IndexRange points, const int curve_index)
+{
+ return {curve_index + points.start(), points.size() + 1};
+}
+
+template<typename T>
+static void threaded_slice_fill(const Span<T> src, const Span<int> offsets, MutableSpan<T> dst)
+{
+ threading::parallel_for(src.index_range(), 512, [&](IndexRange range) {
+ for (const int i : range) {
+ dst.slice(bke::offsets_to_range(offsets, i)).fill(src[i]);
+ }
+ });
+}
+
+template<typename T>
+static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
+ const bke::CurvesGeometry &dst_curves,
+ const IndexMask curve_selection,
+ const Span<int> point_offsets,
+ const Span<T> src,
+ MutableSpan<T> dst)
+{
+ threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) {
+ for (const int curve_i : curve_selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+ const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i));
+ threaded_slice_fill(src.slice(src_points), offsets, dst.slice(dst_points));
+ }
+ });
+}
+
+static void duplicate_fillet_point_data(const bke::CurvesGeometry &src_curves,
+ const bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<int> point_offsets,
+ const GSpan src,
+ GMutableSpan dst)
+{
+ attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) {
+ using T = decltype(dummy);
+ duplicate_fillet_point_data(
+ src_curves, dst_curves, selection, point_offsets, src.typed<T>(), dst.typed<T>());
+ });
+}
+
+static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ const Span<IndexRange> unselected_ranges,
+ const VArray<float> &radii,
+ const VArray<int> &counts,
+ const Span<bool> cyclic,
+ MutableSpan<int> dst_curve_offsets,
+ MutableSpan<int> dst_point_offsets)
+{
+ /* Fill the offsets array with the curve point counts, then accumulate them to form offsets. */
+ bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_curve_offsets);
+ threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
+ for (const int curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange offsets_range = curve_dst_offsets(src_points, curve_i);
+
+ MutableSpan<int> point_offsets = dst_point_offsets.slice(offsets_range);
+ MutableSpan<int> point_counts = point_offsets.drop_back(1);
+
+ counts.materialize_compressed(src_points, point_counts);
+ for (int &count : point_counts) {
+ /* Make sure the number of cuts is greater than zero and add one for the existing point. */
+ count = std::max(count, 0) + 1;
+ }
+ if (!cyclic[curve_i]) {
+ /* Endpoints on non-cyclic curves cannot be filleted. */
+ point_counts.first() = 1;
+ point_counts.last() = 1;
+ }
+ /* Implicitly "deselect" points with zero radius. */
+ devirtualize_varray(radii, [&](const auto radii) {
+ for (const int i : IndexRange(src_points.size())) {
+ if (radii[src_points[i]] == 0.0f) {
+ point_counts[i] = 1;
+ }
+ }
+ });
+
+ bke::curves::accumulate_counts_to_offsets(point_offsets);
+
+ dst_curve_offsets[curve_i] = point_offsets.last();
+ }
+ });
+ bke::curves::accumulate_counts_to_offsets(dst_curve_offsets);
+}
+
+static void calculate_directions(const Span<float3> positions, MutableSpan<float3> directions)
+{
+ for (const int i : positions.index_range().drop_back(1)) {
+ directions[i] = math::normalize(positions[i + 1] - positions[i]);
+ }
+ directions.last() = math::normalize(positions.first() - positions.last());
+}
+
+static void calculate_angles(const Span<float3> directions, MutableSpan<float> angles)
+{
+ angles.first() = M_PI - angle_v3v3(-directions.last(), directions.first());
+ for (const int i : directions.index_range().drop_front(1)) {
+ angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]);
+ }
+}
+
+/**
+ * Find the portion of the previous and next segments used by the current and next point fillets.
+ * If more than the total length of the segment would be used, scale the current point's radius
+ * just enough to make the two points meet in the middle.
+ */
+static float limit_radius(const float3 &position_prev,
+ const float3 &position,
+ const float3 &position_next,
+ const float angle_prev,
+ const float angle,
+ const float angle_next,
+ const float radius_prev,
+ const float radius,
+ const float radius_next)
+{
+ const float displacement = radius * std::tan(angle / 2.0f);
+
+ const float displacement_prev = radius_prev * std::tan(angle_prev / 2.0f);
+ const float segment_length_prev = math::distance(position, position_prev);
+ const float total_displacement_prev = displacement_prev + displacement;
+ const float factor_prev = std::clamp(segment_length_prev / total_displacement_prev, 0.0f, 1.0f);
+
+ const float displacement_next = radius_next * std::tan(angle_next / 2.0f);
+ const float segment_length_next = math::distance(position, position_next);
+ const float total_displacement_next = displacement_next + displacement;
+ const float factor_next = std::clamp(segment_length_next / total_displacement_next, 0.0f, 1.0f);
+
+ return radius * std::min(factor_prev, factor_next);
+}
+
+static void limit_radii(const Span<float3> positions,
+ const Span<float> angles,
+ const Span<float> radii,
+ const bool cyclic,
+ MutableSpan<float> radii_clamped)
+{
+ if (cyclic) {
+ /* First point. */
+ radii_clamped.first() = limit_radius(positions.last(),
+ positions.first(),
+ positions[1],
+ angles.last(),
+ angles.first(),
+ angles[1],
+ radii.last(),
+ radii.first(),
+ radii[1]);
+ /* All middle points. */
+ for (const int i : positions.index_range().drop_back(1).drop_front(1)) {
+ const int i_prev = i - 1;
+ const int i_next = i + 1;
+ radii_clamped[i] = limit_radius(positions[i_prev],
+ positions[i],
+ positions[i_next],
+ angles[i_prev],
+ angles[i],
+ angles[i_next],
+ radii[i_prev],
+ radii[i],
+ radii[i_next]);
+ }
+ /* Last point. */
+ radii_clamped.last() = limit_radius(positions.last(1),
+ positions.last(),
+ positions.first(),
+ angles.last(1),
+ angles.last(),
+ angles.first(),
+ radii.last(1),
+ radii.last(),
+ radii.first());
+ }
+ else {
+ const int i_last = positions.index_range().last();
+ /* First point. */
+ radii_clamped.first() = 0.0f;
+ /* All middle points. */
+ for (const int i : positions.index_range().drop_back(1).drop_front(1)) {
+ const int i_prev = i - 1;
+ const int i_next = i + 1;
+ /* Use a zero radius for the first and last points, because they don't have fillets.
+ * This logic could potentially be unrolled, but it doesn't seem worth it. */
+ const float radius_prev = i_prev == 0 ? 0.0f : radii[i_prev];
+ const float radius_next = i_next == i_last ? 0.0f : radii[i_next];
+ radii_clamped[i] = limit_radius(positions[i_prev],
+ positions[i],
+ positions[i_next],
+ angles[i_prev],
+ angles[i],
+ angles[i_next],
+ radius_prev,
+ radii[i],
+ radius_next);
+ }
+ /* Last point. */
+ radii_clamped.last() = 0.0f;
+ }
+}
+
+static void calculate_fillet_positions(const Span<float3> src_positions,
+ const Span<float> angles,
+ const Span<float> radii,
+ const Span<float3> directions,
+ const Span<int> dst_offsets,
+ MutableSpan<float3> dst)
+{
+ const int i_src_last = src_positions.index_range().last();
+ threading::parallel_for(src_positions.index_range(), 512, [&](IndexRange range) {
+ for (const int i_src : range) {
+ const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
+ const float3 &src = src_positions[i_src];
+ if (arc.size() == 1) {
+ dst[arc.first()] = src;
+ continue;
+ }
+
+ const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1;
+ const float angle = angles[i_src];
+ const float radius = radii[i_src];
+ const float displacement = radius * std::tan(angle / 2.0f);
+ const float3 prev_dir = -directions[i_src_prev];
+ const float3 &next_dir = directions[i_src];
+ const float3 arc_start = src + prev_dir * displacement;
+ const float3 arc_end = src + next_dir * displacement;
+
+ dst[arc.first()] = arc_start;
+ dst[arc.last()] = arc_end;
+
+ const IndexRange middle = arc.drop_front(1).drop_back(1);
+ if (middle.is_empty()) {
+ continue;
+ }
+
+ const float3 axis = -math::normalize(math::cross(prev_dir, next_dir));
+ const float3 center_direction = math::normalize(math::midpoint(next_dir, prev_dir));
+ const float distance_to_center = std::sqrt(pow2f(radius) + pow2f(displacement));
+ const float3 center = src + center_direction * distance_to_center;
+
+ /* Rotate each middle fillet point around the center. */
+ const float segment_angle = angle / (middle.size() + 1);
+ for (const int i : IndexRange(middle.size())) {
+ const int point_i = middle[i];
+ dst[point_i] = math::rotate_around_axis(arc_start, center, axis, segment_angle * (i + 1));
+ }
+ }
+ });
+}
+
+/**
+ * Set handles for the "Bezier" mode where we rely on setting the inner handles to approximate a
+ * circular arc. The outer (previous and next) handles outside the result fillet segment are set
+ * to vector handles.
+ */
+static void calculate_bezier_handles_bezier_mode(const Span<float3> src_handles_l,
+ const Span<float3> src_handles_r,
+ const Span<int8_t> src_types_l,
+ const Span<int8_t> src_types_r,
+ const Span<float> angles,
+ const Span<float> radii,
+ const Span<float3> directions,
+ const Span<int> dst_offsets,
+ const Span<float3> dst_positions,
+ MutableSpan<float3> dst_handles_l,
+ MutableSpan<float3> dst_handles_r,
+ MutableSpan<int8_t> dst_types_l,
+ MutableSpan<int8_t> dst_types_r)
+{
+ const int i_src_last = src_handles_l.index_range().last();
+ const int i_dst_last = dst_positions.index_range().last();
+ threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) {
+ for (const int i_src : range) {
+ const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
+ if (arc.size() == 1) {
+ dst_handles_l[arc.first()] = src_handles_l[i_src];
+ dst_handles_r[arc.first()] = src_handles_r[i_src];
+ dst_types_l[arc.first()] = src_types_l[i_src];
+ dst_types_r[arc.first()] = src_types_r[i_src];
+ continue;
+ }
+ BLI_assert(arc.size() == 2);
+ const int i_dst_a = arc.first();
+ const int i_dst_b = arc.last();
+
+ const int i_src_prev = i_src == 0 ? i_src_last : i_src - 1;
+ const float angle = angles[i_src];
+ const float radius = radii[i_src];
+ const float3 prev_dir = -directions[i_src_prev];
+ const float3 &next_dir = directions[i_src];
+
+ const float3 &arc_start = dst_positions[arc.first()];
+ const float3 &arc_end = dst_positions[arc.last()];
+
+ /* Calculate the point's handles on the outside of the fillet segment,
+ * connecting to the next or previous result points. */
+ const int i_dst_prev = i_dst_a == 0 ? i_dst_last : i_dst_a - 1;
+ const int i_dst_next = i_dst_b == i_dst_last ? 0 : i_dst_b + 1;
+ dst_handles_l[i_dst_a] = bke::curves::bezier::calculate_vector_handle(
+ dst_positions[i_dst_a], dst_positions[i_dst_prev]);
+ dst_handles_r[i_dst_b] = bke::curves::bezier::calculate_vector_handle(
+ dst_positions[i_dst_b], dst_positions[i_dst_next]);
+ dst_types_l[i_dst_a] = BEZIER_HANDLE_VECTOR;
+ dst_types_r[i_dst_b] = BEZIER_HANDLE_VECTOR;
+
+ /* The inner handles are aligned with the aligned with the outer vector
+ * handles, but have a specific length to best approximate a circle. */
+ const float handle_length = (4.0f / 3.0f) * radius * std::tan(angle / 4.0f);
+ dst_handles_r[i_dst_a] = arc_start - prev_dir * handle_length;
+ dst_handles_l[i_dst_b] = arc_end - next_dir * handle_length;
+ dst_types_r[i_dst_a] = BEZIER_HANDLE_ALIGN;
+ dst_types_l[i_dst_b] = BEZIER_HANDLE_ALIGN;
+ }
+ });
+}
+
+/**
+ * In the poly fillet mode, all the inner handles are set to vector handles, along with the "outer"
+ * (previous and next) handles at each fillet.
+ */
+static void calculate_bezier_handles_poly_mode(const Span<float3> src_handles_l,
+ const Span<float3> src_handles_r,
+ const Span<int8_t> src_types_l,
+ const Span<int8_t> src_types_r,
+ const Span<int> dst_offsets,
+ const Span<float3> dst_positions,
+ MutableSpan<float3> dst_handles_l,
+ MutableSpan<float3> dst_handles_r,
+ MutableSpan<int8_t> dst_types_l,
+ MutableSpan<int8_t> dst_types_r)
+{
+ const int i_dst_last = dst_positions.index_range().last();
+ threading::parallel_for(src_handles_l.index_range(), 512, [&](IndexRange range) {
+ for (const int i_src : range) {
+ const IndexRange arc = bke::offsets_to_range(dst_offsets, i_src);
+ if (arc.size() == 1) {
+ dst_handles_l[arc.first()] = src_handles_l[i_src];
+ dst_handles_r[arc.first()] = src_handles_r[i_src];
+ dst_types_l[arc.first()] = src_types_l[i_src];
+ dst_types_r[arc.first()] = src_types_r[i_src];
+ continue;
+ }
+
+ /* The fillet's next and previous handles are vector handles, as are the inner handles. */
+ dst_types_l.slice(arc).fill(BEZIER_HANDLE_VECTOR);
+ dst_types_r.slice(arc).fill(BEZIER_HANDLE_VECTOR);
+
+ /* Calculate the point's handles on the outside of the fillet segment. This point
+ * won't be selected for a fillet if it is the first or last in a non-cyclic curve. */
+
+ const int i_dst_prev = arc.first() == 0 ? i_dst_last : arc.one_before_start();
+ const int i_dst_next = arc.last() == i_dst_last ? 0 : arc.one_after_last();
+ dst_handles_l[arc.first()] = bke::curves::bezier::calculate_vector_handle(
+ dst_positions[arc.first()], dst_positions[i_dst_prev]);
+ dst_handles_r[arc.last()] = bke::curves::bezier::calculate_vector_handle(
+ dst_positions[arc.last()], dst_positions[i_dst_next]);
+
+ /* Set the values for the inner handles. */
+ const IndexRange middle = arc.drop_front(1).drop_back(1);
+ for (const int i : middle) {
+ dst_handles_r[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i],
+ dst_positions[i - 1]);
+ dst_handles_l[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i],
+ dst_positions[i + 1]);
+ }
+ }
+ });
+}
+
+static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves,
+ const IndexMask curve_selection,
+ const VArray<float> &radius_input,
+ const VArray<int> &counts,
+ const bool limit_radius,
+ const bool use_bezier_mode)
+{
+ const Vector<IndexRange> unselected_ranges = curve_selection.extract_ranges_invert(
+ src_curves.curves_range());
+
+ const Span<float3> positions = src_curves.positions();
+ const VArraySpan<bool> cyclic{src_curves.cyclic()};
+ const bke::AttributeAccessor src_attributes = src_curves.attributes();
+
+ bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
+ /* Stores the offset of every result point for every original point.
+ * The extra length is used in order to store an extra zero for every curve. */
+ Array<int> dst_point_offsets(src_curves.points_num() + src_curves.curves_num());
+ calculate_result_offsets(src_curves,
+ curve_selection,
+ unselected_ranges,
+ radius_input,
+ counts,
+ cyclic,
+ dst_curves.offsets_for_write(),
+ dst_point_offsets);
+ const Span<int> point_offsets = dst_point_offsets.as_span();
+
+ dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
+ bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ VArraySpan<int8_t> src_types_l;
+ VArraySpan<int8_t> src_types_r;
+ Span<float3> src_handles_l;
+ Span<float3> src_handles_r;
+ MutableSpan<int8_t> dst_types_l;
+ MutableSpan<int8_t> dst_types_r;
+ MutableSpan<float3> dst_handles_l;
+ MutableSpan<float3> dst_handles_r;
+ if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
+ src_types_l = src_curves.handle_types_left();
+ src_types_r = src_curves.handle_types_right();
+ src_handles_l = src_curves.handle_positions_left();
+ src_handles_r = src_curves.handle_positions_right();
+
+ dst_types_l = dst_curves.handle_types_left_for_write();
+ dst_types_r = dst_curves.handle_types_right_for_write();
+ dst_handles_l = dst_curves.handle_positions_left_for_write();
+ dst_handles_r = dst_curves.handle_positions_right_for_write();
+ }
+
+ threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) {
+ Array<float3> directions;
+ Array<float> angles;
+ Array<float> radii;
+ Array<float> input_radii_buffer;
+
+ for (const int curve_i : curve_selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i));
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+ const Span<float3> src_positions = positions.slice(src_points);
+
+ directions.reinitialize(src_points.size());
+ calculate_directions(src_positions, directions);
+
+ angles.reinitialize(src_points.size());
+ calculate_angles(directions, angles);
+
+ radii.reinitialize(src_points.size());
+ if (limit_radius) {
+ input_radii_buffer.reinitialize(src_points.size());
+ radius_input.materialize_compressed(src_points, input_radii_buffer);
+ limit_radii(src_positions, angles, input_radii_buffer, cyclic[curve_i], radii);
+ }
+ else {
+ radius_input.materialize_compressed(src_points, radii);
+ }
+
+ calculate_fillet_positions(positions.slice(src_points),
+ angles,
+ radii,
+ directions,
+ offsets,
+ dst_positions.slice(dst_points));
+
+ if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
+ if (use_bezier_mode) {
+ calculate_bezier_handles_bezier_mode(src_handles_l.slice(src_points),
+ src_handles_r.slice(src_points),
+ src_types_l.slice(src_points),
+ src_types_r.slice(src_points),
+ angles,
+ radii,
+ directions,
+ offsets,
+ dst_positions.slice(dst_points),
+ dst_handles_l.slice(dst_points),
+ dst_handles_r.slice(dst_points),
+ dst_types_l.slice(dst_points),
+ dst_types_r.slice(dst_points));
+ }
+ else {
+ calculate_bezier_handles_poly_mode(src_handles_l.slice(src_points),
+ src_handles_r.slice(src_points),
+ src_types_l.slice(src_points),
+ src_types_r.slice(src_points),
+ offsets,
+ dst_positions.slice(dst_points),
+ dst_handles_l.slice(dst_points),
+ dst_handles_r.slice(dst_points),
+ dst_types_l.slice(dst_points),
+ dst_types_r.slice(dst_points));
+ }
+ }
+ }
+ });
+
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ src_attributes,
+ dst_attributes,
+ ATTR_DOMAIN_MASK_POINT,
+ {"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) {
+ duplicate_fillet_point_data(
+ src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span);
+ attribute.dst.finish();
+ }
+
+ if (!unselected_ranges.is_empty()) {
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
+ attribute.dst.finish();
+ }
+ }
+
+ return dst_curves;
+}
+
+bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves,
+ const IndexMask curve_selection,
+ const VArray<float> &radius,
+ const VArray<int> &count,
+ const bool limit_radius)
+{
+ return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false);
+}
+
+bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves,
+ const IndexMask curve_selection,
+ const VArray<float> &radius,
+ const bool limit_radius)
+{
+ return fillet_curves(src_curves,
+ curve_selection,
+ radius,
+ VArray<int>::ForSingle(1, src_curves.points_num()),
+ limit_radius,
+ true);
+}
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc
index d3a5a194555..17318c277aa 100644
--- a/source/blender/geometry/intern/mesh_merge_by_distance.cc
+++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc
@@ -1164,26 +1164,26 @@ static void weld_mesh_context_create(const Mesh &mesh,
const int vert_kill_len,
WeldMesh *r_weld_mesh)
{
- Span<MEdge> medge{mesh.medge, mesh.totedge};
- Span<MPoly> mpoly{mesh.mpoly, mesh.totpoly};
- Span<MLoop> mloop{mesh.mloop, mesh.totloop};
const int mvert_len = mesh.totvert;
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
Vector<WeldVert> wvert = weld_vert_ctx_alloc_and_setup(vert_dest_map, vert_kill_len);
r_weld_mesh->vert_kill_len = vert_kill_len;
- Array<int> edge_dest_map(medge.size());
- Array<int> edge_ctx_map(medge.size());
- Vector<WeldEdge> wedge = weld_edge_ctx_alloc(medge, vert_dest_map, edge_dest_map, edge_ctx_map);
+ Array<int> edge_dest_map(edges.size());
+ Array<int> edge_ctx_map(edges.size());
+ Vector<WeldEdge> wedge = weld_edge_ctx_alloc(edges, vert_dest_map, edge_dest_map, edge_ctx_map);
Array<WeldGroup> v_links(mvert_len, {0, 0});
weld_edge_ctx_setup(v_links, edge_dest_map, wedge, &r_weld_mesh->edge_kill_len);
- weld_poly_loop_ctx_alloc(mpoly, mloop, vert_dest_map, edge_dest_map, r_weld_mesh);
+ weld_poly_loop_ctx_alloc(polys, loops, vert_dest_map, edge_dest_map, r_weld_mesh);
- weld_poly_loop_ctx_setup(mloop,
+ weld_poly_loop_ctx_setup(loops,
#ifdef USE_WELD_DEBUG
- mpoly,
+ polys,
#endif
mvert_len,
@@ -1198,7 +1198,7 @@ static void weld_mesh_context_create(const Mesh &mesh,
r_weld_mesh->vert_groups_buffer,
r_weld_mesh->vert_groups);
- weld_edge_groups_setup(medge.size(),
+ weld_edge_groups_setup(edges.size(),
r_weld_mesh->edge_kill_len,
wedge,
edge_ctx_map,
@@ -1233,7 +1233,6 @@ static void customdata_weld(
float no[3] = {0.0f, 0.0f, 0.0f};
#endif
int crease = 0;
- int bweight = 0;
short flag = 0;
/* interpolates a layer at a time */
@@ -1267,7 +1266,6 @@ static void customdata_weld(
no[1] += mv_src_no[1];
no[2] += mv_src_no[2];
#endif
- bweight += mv_src->bweight;
flag |= mv_src->flag;
}
}
@@ -1275,7 +1273,6 @@ static void customdata_weld(
for (j = 0; j < count; j++) {
MEdge *me_src = &((MEdge *)src_data)[src_indices[j]];
crease += me_src->crease;
- bweight += me_src->bweight;
flag |= me_src->flag;
}
}
@@ -1312,8 +1309,6 @@ static void customdata_weld(
if (type == CD_MVERT) {
MVert *mv = &((MVert *)layer_dst->data)[dest_index];
mul_v3_fl(co, fac);
- bweight *= fac;
- CLAMP_MAX(bweight, 255);
copy_v3_v3(mv->co, co);
#ifdef USE_WELD_NORMALS
@@ -1325,17 +1320,13 @@ static void customdata_weld(
#endif
mv->flag = (char)flag;
- mv->bweight = (char)bweight;
}
else if (type == CD_MEDGE) {
MEdge *me = &((MEdge *)layer_dst->data)[dest_index];
crease *= fac;
- bweight *= fac;
CLAMP_MAX(crease, 255);
- CLAMP_MAX(bweight, 255);
me->crease = (char)crease;
- me->bweight = (char)bweight;
me->flag = flag;
}
else if (CustomData_layer_has_interp(dest, dest_i)) {
@@ -1360,23 +1351,24 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
MutableSpan<int> vert_dest_map,
const int removed_vertex_count)
{
- Span<MPoly> mpoly{mesh.mpoly, mesh.totpoly};
- Span<MLoop> mloop{mesh.mloop, mesh.totloop};
+ const Span<MPoly> src_polys = mesh.polys();
+ const Span<MLoop> src_loops = mesh.loops();
const int totvert = mesh.totvert;
const int totedge = mesh.totedge;
- const int totloop = mesh.totloop;
- const int totpoly = mesh.totpoly;
WeldMesh weld_mesh;
weld_mesh_context_create(mesh, vert_dest_map, removed_vertex_count, &weld_mesh);
const int result_nverts = totvert - weld_mesh.vert_kill_len;
const int result_nedges = totedge - weld_mesh.edge_kill_len;
- const int result_nloops = totloop - weld_mesh.loop_kill_len;
- const int result_npolys = totpoly - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len;
+ const int result_nloops = src_loops.size() - weld_mesh.loop_kill_len;
+ const int result_npolys = src_polys.size() - weld_mesh.poly_kill_len + weld_mesh.wpoly_new_len;
Mesh *result = BKE_mesh_new_nomain_from_template(
&mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys);
+ MutableSpan<MEdge> dst_edges = result->edges_for_write();
+ MutableSpan<MPoly> dst_polys = result->polys_for_write();
+ MutableSpan<MLoop> dst_loops = result->loops_for_write();
/* Vertices. */
@@ -1431,7 +1423,7 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
}
if (count) {
CustomData_copy_data(&mesh.edata, &result->edata, source_index, dest_index, count);
- MEdge *me = &result->medge[dest_index];
+ MEdge *me = &dst_edges[dest_index];
dest_index += count;
for (; count--; me++) {
me->v1 = vert_final[me->v1];
@@ -1448,7 +1440,7 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
&weld_mesh.edge_groups_buffer[wegrp->group.ofs],
wegrp->group.len,
dest_index);
- MEdge *me = &result->medge[dest_index];
+ MEdge *me = &dst_edges[dest_index];
me->v1 = vert_final[wegrp->v1];
me->v2 = vert_final[wegrp->v2];
/* "For now, assume that all merged edges are loose. This flag will be cleared in the
@@ -1464,13 +1456,13 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
/* Polys/Loops. */
- MPoly *r_mp = &result->mpoly[0];
- MLoop *r_ml = &result->mloop[0];
+ MPoly *r_mp = dst_polys.data();
+ MLoop *r_ml = dst_loops.data();
int r_i = 0;
int loop_cur = 0;
Array<int, 64> group_buffer(weld_mesh.max_poly_len);
- for (const int i : mpoly.index_range()) {
- const MPoly &mp = mpoly[i];
+ for (const int i : src_polys.index_range()) {
+ const MPoly &mp = src_polys[i];
const int loop_start = loop_cur;
const int poly_ctx = weld_mesh.poly_map[i];
if (poly_ctx == OUT_OF_CONTEXT) {
@@ -1486,7 +1478,7 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
const WeldPoly &wp = weld_mesh.wpoly[poly_ctx];
WeldLoopOfPolyIter iter;
if (!weld_iter_loop_of_poly_begin(
- iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) {
+ iter, wp, weld_mesh.wloop, src_loops, weld_mesh.loop_map, group_buffer.data())) {
continue;
}
@@ -1503,9 +1495,9 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
r_ml++;
loop_cur++;
if (iter.type) {
- result->medge[e].flag &= ~ME_LOOSEEDGE;
+ dst_edges[e].flag &= ~ME_LOOSEEDGE;
}
- BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0);
+ BLI_assert((dst_edges[e].flag & ME_LOOSEEDGE) == 0);
}
}
@@ -1522,7 +1514,7 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
const int loop_start = loop_cur;
WeldLoopOfPolyIter iter;
if (!weld_iter_loop_of_poly_begin(
- iter, wp, weld_mesh.wloop, mloop, weld_mesh.loop_map, group_buffer.data())) {
+ iter, wp, weld_mesh.wloop, src_loops, weld_mesh.loop_map, group_buffer.data())) {
continue;
}
@@ -1538,9 +1530,9 @@ static Mesh *create_merged_mesh(const Mesh &mesh,
r_ml++;
loop_cur++;
if (iter.type) {
- result->medge[e].flag &= ~ME_LOOSEEDGE;
+ dst_edges[e].flag &= ~ME_LOOSEEDGE;
}
- BLI_assert((result->medge[e].flag & ME_LOOSEEDGE) == 0);
+ BLI_assert((dst_edges[e].flag & ME_LOOSEEDGE) == 0);
}
r_mp->loopstart = loop_start;
@@ -1569,8 +1561,9 @@ std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh,
KDTree_3d *tree = BLI_kdtree_3d_new(selection.size());
+ const Span<MVert> verts = mesh.verts();
for (const int i : selection) {
- BLI_kdtree_3d_insert(tree, i, mesh.mvert[i].co);
+ BLI_kdtree_3d_insert(tree, i, verts[i].co);
}
BLI_kdtree_3d_balance(tree);
@@ -1595,8 +1588,8 @@ std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
const float merge_distance,
const bool only_loose_edges)
{
- Span<MVert> verts{mesh.mvert, mesh.totvert};
- Span<MEdge> edges{mesh.medge, mesh.totedge};
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
int vert_kill_len = 0;
diff --git a/source/blender/geometry/intern/mesh_primitive_cuboid.cc b/source/blender/geometry/intern/mesh_primitive_cuboid.cc
index 486d8adbf39..39571f2931e 100644
--- a/source/blender/geometry/intern/mesh_primitive_cuboid.cc
+++ b/source/blender/geometry/intern/mesh_primitive_cuboid.cc
@@ -54,7 +54,7 @@ struct CuboidConfig {
}
};
-static void calculate_vertices(const CuboidConfig &config, MutableSpan<MVert> verts)
+static void calculate_verts(const CuboidConfig &config, MutableSpan<MVert> verts)
{
const float z_bottom = -config.size.z / 2.0f;
const float z_delta = config.size.z / config.edges_z;
@@ -321,7 +321,7 @@ static void calculate_polys(const CuboidConfig &config,
static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::AttributeIDRef &uv_id)
{
- bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
bke::SpanAttributeWriter<float2> uv_attribute =
attributes.lookup_or_add_for_write_only_span<float2>(uv_id, ATTR_DOMAIN_CORNER);
MutableSpan<float2> uvs = uv_attribute.span;
@@ -405,10 +405,13 @@ Mesh *create_cuboid_mesh(const float3 &size,
Mesh *mesh = BKE_mesh_new_nomain(
config.vertex_count, 0, 0, config.loop_count, config.poly_count);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
- calculate_vertices(config, {mesh->mvert, mesh->totvert});
+ calculate_verts(config, verts);
- calculate_polys(config, {mesh->mpoly, mesh->totpoly}, {mesh->mloop, mesh->totloop});
+ calculate_polys(config, polys, loops);
BKE_mesh_calc_edges(mesh, false, false);
if (uv_id) {
diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc
index 681dfd15137..c2a9b16c8b6 100644
--- a/source/blender/geometry/intern/mesh_to_curve_convert.cc
+++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_array.hh"
+#include "BLI_array_utils.hh"
#include "BLI_devirtualize_parameters.hh"
#include "BLI_set.hh"
#include "BLI_task.hh"
@@ -12,31 +13,18 @@
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
+#include "BKE_mesh.h"
#include "GEO_mesh_to_curve.hh"
namespace blender::geometry {
-template<typename T>
-static void copy_with_map(const VArray<T> &src, Span<int> map, MutableSpan<T> dst)
+bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh,
+ const Span<int> vert_indices,
+ const Span<int> curve_offsets,
+ const IndexRange cyclic_curves)
{
- devirtualize_varray(src, [&](const auto &src) {
- threading::parallel_for(map.index_range(), 1024, [&](const IndexRange range) {
- for (const int i : range) {
- const int vert_index = map[i];
- dst[i] = src[vert_index];
- }
- });
- });
-}
-
-static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_component,
- const Span<int> vert_indices,
- const Span<int> curve_offsets,
- const IndexRange cyclic_curves)
-{
- Curves *curves_id = bke::curves_new_nomain(vert_indices.size(), curve_offsets.size());
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ bke::CurvesGeometry curves(vert_indices.size(), curve_offsets.size());
curves.offsets_for_write().drop_back(1).copy_from(curve_offsets);
curves.offsets_for_write().last() = vert_indices.size();
curves.fill_curve_types(CURVE_TYPE_POLY);
@@ -44,8 +32,8 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen
curves.cyclic_for_write().fill(false);
curves.cyclic_for_write().slice(cyclic_curves).fill(true);
+ const bke::AttributeAccessor mesh_attributes = mesh.attributes();
bke::MutableAttributeAccessor curves_attributes = curves.attributes_for_write();
- const bke::AttributeAccessor mesh_attributes = *mesh_component.attributes();
Set<bke::AttributeIDRef> source_attribute_ids = mesh_attributes.all_ids();
@@ -71,12 +59,12 @@ static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_componen
using T = decltype(dummy);
bke::SpanAttributeWriter<T> attribute =
curves_attributes.lookup_or_add_for_write_only_span<T>(attribute_id, ATTR_DOMAIN_POINT);
- copy_with_map<T>(mesh_attribute.typed<T>(), vert_indices, attribute.span);
+ array_utils::gather<T>(mesh_attribute.typed<T>(), vert_indices, attribute.span);
attribute.finish();
});
}
- return curves_id;
+ return curves;
}
struct CurveFromEdgesOutput {
@@ -214,22 +202,21 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span<MVert> verts,
static Vector<std::pair<int, int>> get_selected_edges(const Mesh &mesh, const IndexMask selection)
{
Vector<std::pair<int, int>> selected_edges;
+ const Span<MEdge> edges = mesh.edges();
for (const int i : selection) {
- selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2});
+ selected_edges.append({edges[i].v1, edges[i].v2});
}
return selected_edges;
}
-Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection)
+bke::CurvesGeometry mesh_to_curve_convert(const Mesh &mesh, const IndexMask selection)
{
- const Mesh &mesh = *mesh_component.get_for_read();
- Vector<std::pair<int, int>> selected_edges = get_selected_edges(*mesh_component.get_for_read(),
- selection);
- CurveFromEdgesOutput output = edges_to_curve_point_indices({mesh.mvert, mesh.totvert},
- selected_edges);
+ Vector<std::pair<int, int>> selected_edges = get_selected_edges(mesh, selection);
+ const Span<MVert> verts = mesh.verts();
+ CurveFromEdgesOutput output = edges_to_curve_point_indices(verts, selected_edges);
return create_curve_from_vert_indices(
- mesh_component, output.vert_indices, output.curve_offsets, output.cyclic_curves);
+ mesh, output.vert_indices, output.curve_offsets, output.cyclic_curves);
}
} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/mesh_to_volume.cc b/source/blender/geometry/intern/mesh_to_volume.cc
index ae98b048a6c..b1c7be38609 100644
--- a/source/blender/geometry/intern/mesh_to_volume.cc
+++ b/source/blender/geometry/intern/mesh_to_volume.cc
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_volume.h"
@@ -15,7 +16,7 @@ namespace blender::geometry {
/* This class follows the MeshDataAdapter interface from openvdb. */
class OpenVDBMeshAdapter {
private:
- Span<MVert> vertices_;
+ Span<MVert> verts_;
Span<MLoop> loops_;
Span<MLoopTri> looptris_;
float4x4 transform_;
@@ -29,7 +30,7 @@ class OpenVDBMeshAdapter {
};
OpenVDBMeshAdapter::OpenVDBMeshAdapter(const Mesh &mesh, float4x4 transform)
- : vertices_(mesh.mvert, mesh.totvert), loops_(mesh.mloop, mesh.totloop), transform_(transform)
+ : verts_(mesh.verts()), loops_(mesh.loops()), transform_(transform)
{
/* This only updates a cache and can be considered to be logically const. */
const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(&mesh);
@@ -44,7 +45,7 @@ size_t OpenVDBMeshAdapter::polygonCount() const
size_t OpenVDBMeshAdapter::pointCount() const
{
- return static_cast<size_t>(vertices_.size());
+ return static_cast<size_t>(verts_.size());
}
size_t OpenVDBMeshAdapter::vertexCount(size_t UNUSED(polygon_index)) const
@@ -58,7 +59,7 @@ void OpenVDBMeshAdapter::getIndexSpacePoint(size_t polygon_index,
openvdb::Vec3d &pos) const
{
const MLoopTri &looptri = looptris_[polygon_index];
- const MVert &vertex = vertices_[loops_[looptri.tri[vertex_index]].v];
+ const MVert &vertex = verts_[loops_[looptri.tri[vertex_index]].v];
const float3 transformed_co = transform_ * float3(vertex.co);
pos = &transformed_co.x;
}
diff --git a/source/blender/geometry/intern/point_merge_by_distance.cc b/source/blender/geometry/intern/point_merge_by_distance.cc
index 40bc02cbd34..81f57f785a3 100644
--- a/source/blender/geometry/intern/point_merge_by_distance.cc
+++ b/source/blender/geometry/intern/point_merge_by_distance.cc
@@ -13,13 +13,14 @@
namespace blender::geometry {
-PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
+PointCloud *point_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const IndexMask selection)
{
- const PointCloud &src_pointcloud = *src_points.get_for_read();
- const int src_size = src_pointcloud.totpoint;
- Span<float3> positions{reinterpret_cast<float3 *>(src_pointcloud.co), src_size};
+ const bke::AttributeAccessor src_attributes = src_points.attributes();
+ VArraySpan<float3> positions = src_attributes.lookup_or_default<float3>(
+ "position", ATTR_DOMAIN_POINT, float3(0));
+ const int src_size = positions.size();
/* Create the KD tree based on only the selected points, to speed up merge detection and
* balancing. */
@@ -40,8 +41,7 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
/* Create the new point cloud and add it to a temporary component for the attribute API. */
const int dst_size = src_size - duplicate_count;
PointCloud *dst_pointcloud = BKE_pointcloud_new_nomain(dst_size);
- PointCloudComponent dst_points;
- dst_points.replace(dst_pointcloud, GeometryOwnershipType::Editable);
+ bke::MutableAttributeAccessor dst_attributes = dst_pointcloud->attributes_for_write();
/* By default, every point is just "merged" with itself. Then fill in the results of the merge
* finding, converting from indices into the selection to indices into the full input point
@@ -104,12 +104,10 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
point_merge_counts[dst_index]++;
}
- const bke::AttributeAccessor src_attributes = *src_points.attributes();
- bke::MutableAttributeAccessor dst_attributes = *dst_points.attributes_for_write();
- Set<bke::AttributeIDRef> attributes = src_attributes.all_ids();
+ Set<bke::AttributeIDRef> attribute_ids = src_attributes.all_ids();
/* Transfer the ID attribute if it exists, using the ID of the first merged point. */
- if (attributes.contains("id")) {
+ if (attribute_ids.contains("id")) {
VArraySpan<int> src = src_attributes.lookup_or_default<int>("id", ATTR_DOMAIN_POINT, 0);
bke::SpanAttributeWriter<int> dst = dst_attributes.lookup_or_add_for_write_only_span<int>(
"id", ATTR_DOMAIN_POINT);
@@ -122,11 +120,11 @@ PointCloud *point_merge_by_distance(const PointCloudComponent &src_points,
});
dst.finish();
- attributes.remove_contained("id");
+ attribute_ids.remove_contained("id");
}
/* Transfer all other attributes. */
- for (const bke::AttributeIDRef &id : attributes) {
+ for (const bke::AttributeIDRef &id : attribute_ids) {
if (!id.should_be_kept()) {
continue;
}
diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc
index 25bf00c5783..8133a685eb8 100644
--- a/source/blender/geometry/intern/realize_instances.cc
+++ b/source/blender/geometry/intern/realize_instances.cc
@@ -9,11 +9,13 @@
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
+#include "BLI_devirtualize_parameters.hh"
#include "BLI_noise.hh"
#include "BLI_task.hh"
#include "BKE_collection.h"
#include "BKE_curves.hh"
+#include "BKE_deform.h"
#include "BKE_geometry_set_instances.hh"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -69,6 +71,7 @@ struct PointCloudRealizeInfo {
/** Matches the order stored in #AllPointCloudsInfo.attributes. */
Array<std::optional<GVArraySpan>> attributes;
/** Id attribute on the point cloud. If there are no ids, this #Span is empty. */
+ Span<float3> positions;
Span<int> stored_ids;
};
@@ -94,12 +97,18 @@ struct MeshElementStartIndices {
struct MeshRealizeInfo {
const Mesh *mesh = nullptr;
+ Span<MVert> verts;
+ Span<MEdge> edges;
+ Span<MPoly> polys;
+ Span<MLoop> loops;
+
/** Maps old material indices to new material indices. */
Array<int> material_index_map;
/** Matches the order in #AllMeshesInfo.attributes. */
Array<std::optional<GVArraySpan>> attributes;
/** Vertex ids stored on the mesh. If there are no ids, this #Span is empty. */
Span<int> stored_vertex_ids;
+ VArray<int> material_indices;
};
struct RealizeMeshTask {
@@ -180,6 +189,7 @@ struct AllMeshesInfo {
/** Ordered materials on the output mesh. */
VectorSet<Material *> materials;
bool create_id_attribute = false;
+ bool create_material_index_attribute = false;
};
struct AllCurvesInfo {
@@ -203,7 +213,8 @@ struct GatherTasks {
/* Volumes only have very simple support currently. Only the first found volume is put into the
* output. */
- UserCounter<VolumeComponent> first_volume;
+ UserCounter<const VolumeComponent> first_volume;
+ UserCounter<const GeometryComponentEditData> first_edit_data;
};
/** Current offsets while during the gather operation. */
@@ -581,7 +592,16 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info,
const VolumeComponent *volume_component = static_cast<const VolumeComponent *>(component);
if (!gather_info.r_tasks.first_volume) {
volume_component->user_add();
- gather_info.r_tasks.first_volume = const_cast<VolumeComponent *>(volume_component);
+ gather_info.r_tasks.first_volume = volume_component;
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_EDIT: {
+ const GeometryComponentEditData *edit_component =
+ static_cast<const GeometryComponentEditData *>(component);
+ if (!gather_info.r_tasks.first_edit_data) {
+ edit_component->user_add();
+ gather_info.r_tasks.first_edit_data = edit_component;
}
break;
}
@@ -648,7 +668,7 @@ static AllPointCloudsInfo preprocess_pointclouds(const GeometrySet &geometry_set
pointcloud_info.pointcloud = pointcloud;
/* Access attributes. */
- bke::AttributeAccessor attributes = bke::pointcloud_attributes(*pointcloud);
+ bke::AttributeAccessor attributes = pointcloud->attributes();
pointcloud_info.attributes.reinitialize(info.attributes.size());
for (const int attribute_index : info.attributes.index_range()) {
const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
@@ -665,6 +685,9 @@ static AllPointCloudsInfo preprocess_pointclouds(const GeometrySet &geometry_set
pointcloud_info.stored_ids = ids_attribute.varray.get_internal_span().typed<int>();
}
}
+ const VArray<float3> position_attribute = attributes.lookup_or_default<float3>(
+ "position", ATTR_DOMAIN_POINT, float3(0));
+ pointcloud_info.positions = position_attribute.get_internal_span();
}
return info;
}
@@ -673,18 +696,16 @@ static void execute_realize_pointcloud_task(
const RealizeInstancesOptions &options,
const RealizePointCloudTask &task,
const OrderedAttributes &ordered_attributes,
- PointCloud &dst_pointcloud,
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
- MutableSpan<int> all_dst_ids)
+ MutableSpan<int> all_dst_ids,
+ MutableSpan<float3> all_dst_positions)
{
const PointCloudRealizeInfo &pointcloud_info = *task.pointcloud_info;
const PointCloud &pointcloud = *pointcloud_info.pointcloud;
- const Span<float3> src_positions{(float3 *)pointcloud.co, pointcloud.totpoint};
const IndexRange point_slice{task.start_index, pointcloud.totpoint};
- MutableSpan<float3> dst_positions{(float3 *)dst_pointcloud.co + task.start_index,
- pointcloud.totpoint};
- copy_transformed_positions(src_positions, task.transform, dst_positions);
+ copy_transformed_positions(
+ pointcloud_info.positions, task.transform, all_dst_positions.slice(point_slice));
/* Create point ids. */
if (!all_dst_ids.is_empty()) {
@@ -723,8 +744,10 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti
PointCloudComponent &dst_component =
r_realized_geometry.get_component_for_write<PointCloudComponent>();
dst_component.replace(dst_pointcloud);
- bke::MutableAttributeAccessor dst_attributes = bke::pointcloud_attributes_for_write(
- *dst_pointcloud);
+ bke::MutableAttributeAccessor dst_attributes = dst_pointcloud->attributes_for_write();
+
+ SpanAttributeWriter<float3> positions = dst_attributes.lookup_or_add_for_write_only_span<float3>(
+ "position", ATTR_DOMAIN_POINT);
/* Prepare id attribute. */
SpanAttributeWriter<int> point_ids;
@@ -748,9 +771,9 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti
execute_realize_pointcloud_task(options,
task,
ordered_attributes,
- *dst_pointcloud,
dst_attribute_writers,
- point_ids.span);
+ point_ids.span,
+ positions.span);
}
});
@@ -758,9 +781,8 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti
for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) {
dst_attribute.finish();
}
- if (point_ids) {
- point_ids.finish();
- }
+ positions.finish();
+ point_ids.finish();
}
/** \} */
@@ -770,7 +792,10 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti
* \{ */
static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
- const GeometrySet &in_geometry_set, const RealizeInstancesOptions &options, bool &r_create_id)
+ const GeometrySet &in_geometry_set,
+ const RealizeInstancesOptions &options,
+ bool &r_create_id,
+ bool &r_create_material_index)
{
Vector<GeometryComponentType> src_component_types;
src_component_types.append(GEO_COMPONENT_TYPE_MESH);
@@ -783,10 +808,10 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate(
src_component_types, GEO_COMPONENT_TYPE_MESH, true, attributes_to_propagate);
attributes_to_propagate.remove("position");
attributes_to_propagate.remove("normal");
- attributes_to_propagate.remove("material_index");
attributes_to_propagate.remove("shade_smooth");
attributes_to_propagate.remove("crease");
r_create_id = attributes_to_propagate.pop_try("id").has_value();
+ r_create_material_index = attributes_to_propagate.pop_try("material_index").has_value();
OrderedAttributes ordered_attributes;
for (const auto item : attributes_to_propagate.items()) {
ordered_attributes.ids.add_new(item.key);
@@ -816,13 +841,19 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set,
{
AllMeshesInfo info;
info.attributes = gather_generic_mesh_attributes_to_propagate(
- geometry_set, options, info.create_id_attribute);
+ geometry_set, options, info.create_id_attribute, info.create_material_index_attribute);
gather_meshes_to_realize(geometry_set, info.order);
for (const Mesh *mesh : info.order) {
- for (const int slot_index : IndexRange(mesh->totcol)) {
- Material *material = mesh->mat[slot_index];
- info.materials.add(material);
+ if (mesh->totcol == 0) {
+ /* Add an empty material slot for the default material. */
+ info.materials.add(nullptr);
+ }
+ else {
+ for (const int slot_index : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[slot_index];
+ info.materials.add(material);
+ }
}
}
info.realize_info.reinitialize(info.order.size());
@@ -830,17 +861,26 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set,
MeshRealizeInfo &mesh_info = info.realize_info[mesh_index];
const Mesh *mesh = info.order[mesh_index];
mesh_info.mesh = mesh;
+ mesh_info.verts = mesh->verts();
+ mesh_info.edges = mesh->edges();
+ mesh_info.polys = mesh->polys();
+ mesh_info.loops = mesh->loops();
/* Create material index mapping. */
- mesh_info.material_index_map.reinitialize(mesh->totcol);
- for (const int old_slot_index : IndexRange(mesh->totcol)) {
- Material *material = mesh->mat[old_slot_index];
- const int new_slot_index = info.materials.index_of(material);
- mesh_info.material_index_map[old_slot_index] = new_slot_index;
+ mesh_info.material_index_map.reinitialize(std::max<int>(mesh->totcol, 1));
+ if (mesh->totcol == 0) {
+ mesh_info.material_index_map.first() = info.materials.index_of(nullptr);
+ }
+ else {
+ for (const int old_slot_index : IndexRange(mesh->totcol)) {
+ Material *material = mesh->mat[old_slot_index];
+ const int new_slot_index = info.materials.index_of(material);
+ mesh_info.material_index_map[old_slot_index] = new_slot_index;
+ }
}
/* Access attributes. */
- bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh);
+ bke::AttributeAccessor attributes = mesh->attributes();
mesh_info.attributes.reinitialize(info.attributes.size());
for (const int attribute_index : info.attributes.index_range()) {
const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index];
@@ -857,6 +897,8 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set,
mesh_info.stored_vertex_ids = ids_attribute.varray.get_internal_span().typed<int>();
}
}
+ mesh_info.material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
}
return info;
}
@@ -864,26 +906,33 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set,
static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
const RealizeMeshTask &task,
const OrderedAttributes &ordered_attributes,
- Mesh &dst_mesh,
MutableSpan<GSpanAttributeWriter> dst_attribute_writers,
- MutableSpan<int> all_dst_vertex_ids)
+ MutableSpan<MVert> all_dst_verts,
+ MutableSpan<MEdge> all_dst_edges,
+ MutableSpan<MPoly> all_dst_polys,
+ MutableSpan<MLoop> all_dst_loops,
+ MutableSpan<int> all_dst_vertex_ids,
+ MutableSpan<int> all_dst_material_indices)
{
const MeshRealizeInfo &mesh_info = *task.mesh_info;
const Mesh &mesh = *mesh_info.mesh;
- const Span<MVert> src_verts{mesh.mvert, mesh.totvert};
- const Span<MEdge> src_edges{mesh.medge, mesh.totedge};
- const Span<MLoop> src_loops{mesh.mloop, mesh.totloop};
- const Span<MPoly> src_polys{mesh.mpoly, mesh.totpoly};
+ const Span<MVert> src_verts = mesh_info.verts;
+ const Span<MEdge> src_edges = mesh_info.edges;
+ const Span<MPoly> src_polys = mesh_info.polys;
+ const Span<MLoop> src_loops = mesh_info.loops;
- MutableSpan<MVert> dst_verts{dst_mesh.mvert + task.start_indices.vertex, mesh.totvert};
- MutableSpan<MEdge> dst_edges{dst_mesh.medge + task.start_indices.edge, mesh.totedge};
- MutableSpan<MLoop> dst_loops{dst_mesh.mloop + task.start_indices.loop, mesh.totloop};
- MutableSpan<MPoly> dst_polys{dst_mesh.mpoly + task.start_indices.poly, mesh.totpoly};
+ const IndexRange dst_vert_range(task.start_indices.vertex, src_verts.size());
+ const IndexRange dst_edge_range(task.start_indices.edge, src_edges.size());
+ const IndexRange dst_poly_range(task.start_indices.poly, src_polys.size());
+ const IndexRange dst_loop_range(task.start_indices.loop, src_loops.size());
- const Span<int> material_index_map = mesh_info.material_index_map;
+ MutableSpan<MVert> dst_verts = all_dst_verts.slice(dst_vert_range);
+ MutableSpan<MEdge> dst_edges = all_dst_edges.slice(dst_edge_range);
+ MutableSpan<MPoly> dst_polys = all_dst_polys.slice(dst_poly_range);
+ MutableSpan<MLoop> dst_loops = all_dst_loops.slice(dst_loop_range);
- threading::parallel_for(IndexRange(mesh.totvert), 1024, [&](const IndexRange vert_range) {
+ threading::parallel_for(src_verts.index_range(), 1024, [&](const IndexRange vert_range) {
for (const int i : vert_range) {
const MVert &src_vert = src_verts[i];
MVert &dst_vert = dst_verts[i];
@@ -891,7 +940,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
copy_v3_v3(dst_vert.co, task.transform * float3(src_vert.co));
}
});
- threading::parallel_for(IndexRange(mesh.totedge), 1024, [&](const IndexRange edge_range) {
+ threading::parallel_for(src_edges.index_range(), 1024, [&](const IndexRange edge_range) {
for (const int i : edge_range) {
const MEdge &src_edge = src_edges[i];
MEdge &dst_edge = dst_edges[i];
@@ -900,7 +949,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
dst_edge.v2 += task.start_indices.vertex;
}
});
- threading::parallel_for(IndexRange(mesh.totloop), 1024, [&](const IndexRange loop_range) {
+ threading::parallel_for(src_loops.index_range(), 1024, [&](const IndexRange loop_range) {
for (const int i : loop_range) {
const MLoop &src_loop = src_loops[i];
MLoop &dst_loop = dst_loops[i];
@@ -909,21 +958,39 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
dst_loop.e += task.start_indices.edge;
}
});
- threading::parallel_for(IndexRange(mesh.totpoly), 1024, [&](const IndexRange poly_range) {
+ threading::parallel_for(src_polys.index_range(), 1024, [&](const IndexRange poly_range) {
for (const int i : poly_range) {
const MPoly &src_poly = src_polys[i];
MPoly &dst_poly = dst_polys[i];
dst_poly = src_poly;
dst_poly.loopstart += task.start_indices.loop;
- if (src_poly.mat_nr >= 0 && src_poly.mat_nr < mesh.totcol) {
- dst_poly.mat_nr = material_index_map[src_poly.mat_nr];
+ }
+ });
+ if (!all_dst_material_indices.is_empty()) {
+ const Span<int> material_index_map = mesh_info.material_index_map;
+ MutableSpan<int> dst_material_indices = all_dst_material_indices.slice(dst_poly_range);
+ if (mesh.totcol == 0) {
+ /* The material index map contains the index of the null material in the result. */
+ dst_material_indices.fill(material_index_map.first());
+ }
+ else {
+ if (mesh_info.material_indices.is_single()) {
+ const int src_index = mesh_info.material_indices.get_internal_single();
+ const bool valid = IndexRange(mesh.totcol).contains(src_index);
+ dst_material_indices.fill(valid ? material_index_map[src_index] : 0);
}
else {
- /* The material index was invalid before. */
- dst_poly.mat_nr = 0;
+ VArraySpan<int> indices_span(mesh_info.material_indices);
+ threading::parallel_for(src_polys.index_range(), 1024, [&](const IndexRange poly_range) {
+ for (const int i : poly_range) {
+ const int src_index = indices_span[i];
+ const bool valid = IndexRange(mesh.totcol).contains(src_index);
+ dst_material_indices[i] = valid ? material_index_map[src_index] : 0;
+ }
+ });
}
}
- });
+ }
if (!all_dst_vertex_ids.is_empty()) {
create_result_ids(options,
@@ -939,13 +1006,13 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options,
[&](const eAttrDomain domain) {
switch (domain) {
case ATTR_DOMAIN_POINT:
- return IndexRange(task.start_indices.vertex, mesh.totvert);
+ return dst_vert_range;
case ATTR_DOMAIN_EDGE:
- return IndexRange(task.start_indices.edge, mesh.totedge);
- case ATTR_DOMAIN_CORNER:
- return IndexRange(task.start_indices.loop, mesh.totloop);
+ return dst_edge_range;
case ATTR_DOMAIN_FACE:
- return IndexRange(task.start_indices.poly, mesh.totpoly);
+ return dst_poly_range;
+ case ATTR_DOMAIN_CORNER:
+ return dst_loop_range;
default:
BLI_assert_unreachable();
return IndexRange();
@@ -975,12 +1042,19 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
Mesh *dst_mesh = BKE_mesh_new_nomain(tot_vertices, tot_edges, 0, tot_loops, tot_poly);
MeshComponent &dst_component = r_realized_geometry.get_component_for_write<MeshComponent>();
dst_component.replace(dst_mesh);
- bke::MutableAttributeAccessor dst_attributes = bke::mesh_attributes_for_write(*dst_mesh);
+ bke::MutableAttributeAccessor dst_attributes = dst_mesh->attributes_for_write();
+ MutableSpan<MVert> dst_verts = dst_mesh->verts_for_write();
+ MutableSpan<MEdge> dst_edges = dst_mesh->edges_for_write();
+ MutableSpan<MPoly> dst_polys = dst_mesh->polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh->loops_for_write();
/* Copy settings from the first input geometry set with a mesh. */
const RealizeMeshTask &first_task = tasks.first();
const Mesh &first_mesh = *first_task.mesh_info->mesh;
BKE_mesh_copy_parameters_for_eval(dst_mesh, &first_mesh);
+ /* The above line also copies vertex group names. We don't want that here because the new
+ * attributes are added explicitly below. */
+ BLI_freelistN(&dst_mesh->vertex_group_names);
/* Add materials. */
for (const int i : IndexRange(ordered_materials.size())) {
@@ -993,6 +1067,12 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
if (all_meshes_info.create_id_attribute) {
vertex_ids = dst_attributes.lookup_or_add_for_write_only_span<int>("id", ATTR_DOMAIN_POINT);
}
+ /* Prepare material indices. */
+ SpanAttributeWriter<int> material_indices;
+ if (all_meshes_info.create_material_index_attribute) {
+ material_indices = dst_attributes.lookup_or_add_for_write_only_span<int>("material_index",
+ ATTR_DOMAIN_FACE);
+ }
/* Prepare generic output attributes. */
Vector<GSpanAttributeWriter> dst_attribute_writers;
@@ -1008,8 +1088,16 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) {
for (const int task_index : task_range) {
const RealizeMeshTask &task = tasks[task_index];
- execute_realize_mesh_task(
- options, task, ordered_attributes, *dst_mesh, dst_attribute_writers, vertex_ids.span);
+ execute_realize_mesh_task(options,
+ task,
+ ordered_attributes,
+ dst_attribute_writers,
+ dst_verts,
+ dst_edges,
+ dst_polys,
+ dst_loops,
+ vertex_ids.span,
+ material_indices.span);
}
});
@@ -1017,9 +1105,8 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options,
for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) {
dst_attribute.finish();
}
- if (vertex_ids) {
- vertex_ids.finish();
- }
+ vertex_ids.finish();
+ material_indices.finish();
}
/** \} */
@@ -1238,6 +1325,11 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
dst_component.replace(dst_curves_id);
bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
+ /* Copy settings from the first input geometry set with curves. */
+ const RealizeCurveTask &first_task = tasks.first();
+ const Curves &first_curves_id = *first_task.curve_info->curves;
+ bke::curves_copy_parameters(first_curves_id, *dst_curves_id);
+
/* Prepare id attribute. */
SpanAttributeWriter<int> point_ids;
if (all_curves_info.create_id_attribute) {
@@ -1295,23 +1387,24 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options,
}
});
+ /* Type counts have to be updated eagerly. */
+ dst_curves.runtime->type_counts.fill(0);
+ for (const RealizeCurveTask &task : tasks) {
+ for (const int i : IndexRange(CURVE_TYPES_NUM)) {
+ dst_curves.runtime->type_counts[i] +=
+ task.curve_info->curves->geometry.runtime->type_counts[i];
+ }
+ }
+
/* Tag modified attributes. */
for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) {
dst_attribute.finish();
}
- if (point_ids) {
- point_ids.finish();
- }
- if (radius) {
- radius.finish();
- }
- if (resolution) {
- resolution.finish();
- }
- if (all_curves_info.create_handle_postion_attributes) {
- handle_left.finish();
- handle_right.finish();
- }
+ point_ids.finish();
+ radius.finish();
+ resolution.finish();
+ handle_left.finish();
+ handle_right.finish();
}
/** \} */
@@ -1385,6 +1478,9 @@ GeometrySet realize_instances(GeometrySet geometry_set, const RealizeInstancesOp
if (gather_info.r_tasks.first_volume) {
new_geometry_set.add(*gather_info.r_tasks.first_volume);
}
+ if (gather_info.r_tasks.first_edit_data) {
+ new_geometry_set.add(*gather_info.r_tasks.first_edit_data);
+ }
return new_geometry_set;
}
diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc
index c9b8a032ce6..a7f6ac16f8d 100644
--- a/source/blender/geometry/intern/resample_curves.cc
+++ b/source/blender/geometry/intern/resample_curves.cc
@@ -88,20 +88,20 @@ static bool interpolate_attribute_to_poly_curve(const bke::AttributeIDRef &attri
* Retrieve spans from source and result attributes.
*/
static void retrieve_attribute_spans(const Span<bke::AttributeIDRef> ids,
- const CurveComponent &src_component,
- CurveComponent &dst_component,
+ const CurvesGeometry &src_curves,
+ CurvesGeometry &dst_curves,
Vector<GSpan> &src,
Vector<GMutableSpan> &dst,
Vector<bke::GSpanAttributeWriter> &dst_attributes)
{
for (const int i : ids.index_range()) {
- GVArray src_attribute = src_component.attributes()->lookup(ids[i], ATTR_DOMAIN_POINT);
+ GVArray src_attribute = src_curves.attributes().lookup(ids[i], ATTR_DOMAIN_POINT);
BLI_assert(src_attribute);
src.append(src_attribute.get_internal_span());
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(src_attribute.type());
bke::GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
+ dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
ids[i], ATTR_DOMAIN_POINT, data_type);
dst.append(dst_attribute.span);
dst_attributes.append(std::move(dst_attribute));
@@ -116,21 +116,25 @@ struct AttributesForInterpolation : NonCopyable, NonMovable {
Vector<GSpan> src_no_interpolation;
Vector<GMutableSpan> dst_no_interpolation;
+
+ Span<float3> src_evaluated_tangents;
+ Span<float3> src_evaluated_normals;
+ MutableSpan<float3> dst_tangents;
+ MutableSpan<float3> dst_normals;
};
/**
* Gather a set of all generic attribute IDs to copy to the result curves.
*/
-static void gather_point_attributes_to_interpolate(const CurveComponent &src_component,
- CurveComponent &dst_component,
- AttributesForInterpolation &result)
+static void gather_point_attributes_to_interpolate(
+ const CurvesGeometry &src_curves,
+ CurvesGeometry &dst_curves,
+ AttributesForInterpolation &result,
+ const ResampleCurvesOutputAttributeIDs &output_ids)
{
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(
- dst_component.get_for_write()->geometry);
-
VectorSet<bke::AttributeIDRef> ids;
VectorSet<bke::AttributeIDRef> ids_no_interpolation;
- src_component.attributes()->for_all(
+ src_curves.attributes().for_all(
[&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
if (meta_data.domain != ATTR_DOMAIN_POINT) {
return true;
@@ -152,31 +156,89 @@ static void gather_point_attributes_to_interpolate(const CurveComponent &src_com
ids.remove_contained("position");
retrieve_attribute_spans(
- ids, src_component, dst_component, result.src, result.dst, result.dst_attributes);
+ ids, src_curves, dst_curves, result.src, result.dst, result.dst_attributes);
- /* Attributes that aren't interpolated like Bezier handles still have to be be copied
+ /* Attributes that aren't interpolated like Bezier handles still have to be copied
* to the result when there are any unselected curves of the corresponding type. */
retrieve_attribute_spans(ids_no_interpolation,
- src_component,
- dst_component,
+ src_curves,
+ dst_curves,
result.src_no_interpolation,
result.dst_no_interpolation,
result.dst_attributes);
- dst_curves.update_customdata_pointers();
+ bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
+ if (output_ids.tangent_id) {
+ result.src_evaluated_tangents = src_curves.evaluated_tangents();
+ bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ output_ids.tangent_id, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ result.dst_tangents = dst_attribute.span.typed<float3>();
+ result.dst_attributes.append(std::move(dst_attribute));
+ }
+ if (output_ids.normal_id) {
+ result.src_evaluated_normals = src_curves.evaluated_normals();
+ bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ output_ids.normal_id, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
+ result.dst_normals = dst_attribute.span.typed<float3>();
+ result.dst_attributes.append(std::move(dst_attribute));
+ }
+}
+
+static void copy_or_defaults_for_unselected_curves(const CurvesGeometry &src_curves,
+ const Span<IndexRange> unselected_ranges,
+ const AttributesForInterpolation &attributes,
+ CurvesGeometry &dst_curves)
+{
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ unselected_ranges,
+ src_curves.positions(),
+ dst_curves.positions_for_write());
+
+ for (const int i : attributes.src.index_range()) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
+ }
+ for (const int i : attributes.src_no_interpolation.index_range()) {
+ bke::curves::copy_point_data(src_curves,
+ dst_curves,
+ unselected_ranges,
+ attributes.src_no_interpolation[i],
+ attributes.dst_no_interpolation[i]);
+ }
+
+ if (!attributes.dst_tangents.is_empty()) {
+ bke::curves::fill_points(dst_curves, unselected_ranges, float3(0), attributes.dst_tangents);
+ }
+ if (!attributes.dst_normals.is_empty()) {
+ bke::curves::fill_points(dst_curves, unselected_ranges, float3(0), attributes.dst_normals);
+ }
+}
+
+static void normalize_span(MutableSpan<float3> data)
+{
+ for (const int i : data.index_range()) {
+ data[i] = math::normalize(data[i]);
+ }
}
-static Curves *resample_to_uniform(const CurveComponent &src_component,
- const fn::Field<bool> &selection_field,
- const fn::Field<int> &count_field)
+static void normalize_curve_point_data(const CurvesGeometry &curves,
+ const IndexMask curve_selection,
+ MutableSpan<float3> data)
{
- const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
- src_component.get_for_read()->geometry);
+ for (const int i_curve : curve_selection) {
+ normalize_span(data.slice(curves.points_for_curve(i_curve)));
+ }
+}
+static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<int> &count_field,
+ const ResampleCurvesOutputAttributeIDs &output_ids)
+{
/* Create the new curves without any points and evaluate the final count directly
* into the offsets array, in order to be accumulated into offsets later. */
- Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
+ CurvesGeometry dst_curves = CurvesGeometry(0, src_curves.curves_num());
/* Directly copy curve attributes, since they stay the same (except for curve types). */
CustomData_copy(&src_curves.curve_data,
@@ -186,7 +248,7 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
src_curves.curves_num());
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
- bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(count_field, dst_offsets);
@@ -209,9 +271,7 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
AttributesForInterpolation attributes;
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
- gather_point_attributes_to_interpolate(src_component, dst_component, attributes);
+ gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids);
src_curves.ensure_evaluated_lengths();
@@ -235,10 +295,18 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
for (const int i_curve : sliced_selection) {
const bool cyclic = curves_cyclic[i_curve];
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- length_parameterize::sample_uniform(src_curves.evaluated_lengths_for_curve(i_curve, cyclic),
- !curves_cyclic[i_curve],
- sample_indices.as_mutable_span().slice(dst_points),
- sample_factors.as_mutable_span().slice(dst_points));
+ const Span<float> lengths = src_curves.evaluated_lengths_for_curve(i_curve, cyclic);
+ if (lengths.is_empty()) {
+ /* Handle curves with only one evaluated point. */
+ sample_indices.as_mutable_span().slice(dst_points).fill(0);
+ sample_factors.as_mutable_span().slice(dst_points).fill(0.0f);
+ }
+ else {
+ length_parameterize::sample_uniform(lengths,
+ !curves_cyclic[i_curve],
+ sample_indices.as_mutable_span().slice(dst_points),
+ sample_factors.as_mutable_span().slice(dst_points));
+ }
}
/* For every attribute, evaluate attributes from every curve in the range in the original
@@ -254,10 +322,10 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
if (curve_types[i_curve] == CURVE_TYPE_POLY) {
- length_parameterize::linear_interpolation(src.slice(src_points),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst.slice(dst_points));
+ length_parameterize::interpolate(src.slice(src_points),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst.slice(dst_points));
}
else {
const int evaluated_size = src_curves.evaluated_points_for_curve(i_curve).size();
@@ -266,23 +334,36 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
MutableSpan<T> evaluated = evaluated_buffer.as_mutable_span().cast<T>();
src_curves.interpolate_to_evaluated(i_curve, src.slice(src_points), evaluated);
- length_parameterize::linear_interpolation(evaluated.as_span(),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst.slice(dst_points));
+ length_parameterize::interpolate(evaluated.as_span(),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst.slice(dst_points));
}
}
});
}
+ auto interpolate_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) {
+ for (const int i_curve : sliced_selection) {
+ const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+ length_parameterize::interpolate(src.slice(src_points),
+ sample_indices.as_span().slice(dst_points),
+ sample_factors.as_span().slice(dst_points),
+ dst.slice(dst_points));
+ }
+ };
+
/* Interpolate the evaluated positions to the resampled curves. */
- for (const int i_curve : sliced_selection) {
- const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- length_parameterize::linear_interpolation(evaluated_positions.slice(src_points),
- sample_indices.as_span().slice(dst_points),
- sample_factors.as_span().slice(dst_points),
- dst_positions.slice(dst_points));
+ interpolate_evaluated_data(evaluated_positions, dst_positions);
+
+ if (!attributes.dst_tangents.is_empty()) {
+ interpolate_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents);
+ normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_tangents);
+ }
+ if (!attributes.dst_normals.is_empty()) {
+ interpolate_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals);
+ normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_normals);
}
/* Fill the default value for non-interpolating attributes that still must be copied. */
@@ -294,53 +375,40 @@ static Curves *resample_to_uniform(const CurveComponent &src_component,
}
});
- /* Any attribute data from unselected curve points can be directly copied. */
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
- }
- for (const int i : attributes.src_no_interpolation.index_range()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- unselected_ranges,
- attributes.src_no_interpolation[i],
- attributes.dst_no_interpolation[i]);
- }
-
- /* Copy positions for unselected curves. */
- Span<float3> src_positions = src_curves.positions();
- bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
+ copy_or_defaults_for_unselected_curves(src_curves, unselected_ranges, attributes, dst_curves);
for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
attribute.finish();
}
- return dst_curves_id;
+ return dst_curves;
}
-Curves *resample_to_count(const CurveComponent &src_component,
- const fn::Field<bool> &selection_field,
- const fn::Field<int> &count_field)
+CurvesGeometry resample_to_count(const CurvesGeometry &src_curves,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<int> &count_field,
+ const ResampleCurvesOutputAttributeIDs &output_ids)
{
- return resample_to_uniform(src_component, selection_field, get_count_input_max_one(count_field));
+ return resample_to_uniform(
+ src_curves, selection_field, get_count_input_max_one(count_field), output_ids);
}
-Curves *resample_to_length(const CurveComponent &src_component,
- const fn::Field<bool> &selection_field,
- const fn::Field<float> &segment_length_field)
+CurvesGeometry resample_to_length(const CurvesGeometry &src_curves,
+ const fn::Field<bool> &selection_field,
+ const fn::Field<float> &segment_length_field,
+ const ResampleCurvesOutputAttributeIDs &output_ids)
{
return resample_to_uniform(
- src_component, selection_field, get_count_input_from_length(segment_length_field));
+ src_curves, selection_field, get_count_input_from_length(segment_length_field), output_ids);
}
-Curves *resample_to_evaluated(const CurveComponent &src_component,
- const fn::Field<bool> &selection_field)
+CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves,
+ const fn::Field<bool> &selection_field,
+ const ResampleCurvesOutputAttributeIDs &output_ids)
{
- const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
- src_component.get_for_read()->geometry);
+ src_curves.ensure_evaluated_offsets();
- bke::GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -348,8 +416,7 @@ Curves *resample_to_evaluated(const CurveComponent &src_component,
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
src_curves.curves_range(), nullptr);
- Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
+ CurvesGeometry dst_curves(0, src_curves.curves_num());
/* Directly copy curve attributes, since they stay the same (except for curve types). */
CustomData_copy(&src_curves.curve_data,
@@ -361,7 +428,7 @@ Curves *resample_to_evaluated(const CurveComponent &src_component,
dst_curves.fill_curve_types(selection, CURVE_TYPE_POLY);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
- src_curves.ensure_evaluated_offsets();
+ src_curves.ensure_can_interpolate_to_evaluated();
threading::parallel_for(selection.index_range(), 4096, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
dst_offsets[i] = src_curves.evaluated_points_for_curve(i).size();
@@ -373,13 +440,11 @@ Curves *resample_to_evaluated(const CurveComponent &src_component,
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
/* Create the correct number of uniform-length samples for every selected curve. */
- Span<float3> evaluated_positions = src_curves.evaluated_positions();
+ const Span<float3> evaluated_positions = src_curves.evaluated_positions();
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
AttributesForInterpolation attributes;
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
- gather_point_attributes_to_interpolate(src_component, dst_component, attributes);
+ gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids);
threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) {
const IndexMask sliced_selection = selection.slice(selection_range);
@@ -400,11 +465,24 @@ Curves *resample_to_evaluated(const CurveComponent &src_component,
});
}
+ auto copy_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) {
+ for (const int i_curve : sliced_selection) {
+ const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
+ const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
+ dst.slice(dst_points).copy_from(src.slice(src_points));
+ }
+ };
+
/* Copy the evaluated positions to the selected curves. */
- for (const int i_curve : sliced_selection) {
- const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve);
- const IndexRange dst_points = dst_curves.points_for_curve(i_curve);
- dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points));
+ copy_evaluated_data(evaluated_positions, dst_positions);
+
+ if (!attributes.dst_tangents.is_empty()) {
+ copy_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents);
+ normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_tangents);
+ }
+ if (!attributes.dst_normals.is_empty()) {
+ copy_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals);
+ normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_normals);
}
/* Fill the default value for non-interpolating attributes that still must be copied. */
@@ -416,29 +494,13 @@ Curves *resample_to_evaluated(const CurveComponent &src_component,
}
});
- /* Any attribute data from unselected curve points can be directly copied. */
- for (const int i : attributes.src.index_range()) {
- bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
- }
- for (const int i : attributes.src_no_interpolation.index_range()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- unselected_ranges,
- attributes.src_no_interpolation[i],
- attributes.dst_no_interpolation[i]);
- }
-
- /* Copy positions for unselected curves. */
- Span<float3> src_positions = src_curves.positions();
- bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, src_positions, dst_positions);
+ copy_or_defaults_for_unselected_curves(src_curves, unselected_ranges, attributes, dst_curves);
for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) {
attribute.finish();
}
- return dst_curves_id;
+ return dst_curves;
}
} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/reverse_uv_sampler.cc b/source/blender/geometry/intern/reverse_uv_sampler.cc
index 54df43db4ea..f66e4a3ac2e 100644
--- a/source/blender/geometry/intern/reverse_uv_sampler.cc
+++ b/source/blender/geometry/intern/reverse_uv_sampler.cc
@@ -50,6 +50,11 @@ ReverseUVSampler::Result ReverseUVSampler::sample(const float2 &query_uv) const
float3 best_bary_weights;
const MLoopTri *best_looptri;
+ /* The distance to an edge that is allowed to be inside or outside the triangle. Without this,
+ * the lookup can fail for floating point accuracy reasons when the uv is almost exact on an
+ * edge. */
+ const float edge_epsilon = 0.00001f;
+
for (const int looptri_index : looptri_indices) {
const MLoopTri &looptri = looptris_[looptri_index];
const float2 &uv_0 = uv_map_[looptri.tri[0]];
@@ -67,9 +72,13 @@ ReverseUVSampler::Result ReverseUVSampler::sample(const float2 &query_uv) const
const float z_dist = std::max(-bary_weights.z, bary_weights.z - 1.0f);
const float dist = MAX3(x_dist, y_dist, z_dist);
- if (dist <= 0.0f) {
- /* Return early if the uv coordinate is in the triangle. */
- return Result{ResultType::Ok, &looptri, bary_weights};
+ if (dist <= 0.0f && best_dist <= 0.0f) {
+ const float worse_dist = std::max(dist, best_dist);
+ /* Allow ignoring multiple triangle intersections if the uv is almost exactly on an edge. */
+ if (worse_dist < -edge_epsilon) {
+ /* The uv sample is in multiple triangles. */
+ return Result{ResultType::Multiple};
+ }
}
if (dist < best_dist) {
@@ -79,8 +88,9 @@ ReverseUVSampler::Result ReverseUVSampler::sample(const float2 &query_uv) const
}
}
- /* Allow for a small epsilon in case the uv is on th edge. */
- if (best_dist < 0.00001f) {
+ /* Allow using the closest (but not intersecting) triangle if the uv is almost exactly on an
+ * edge. */
+ if (best_dist < edge_epsilon) {
return Result{ResultType::Ok, best_looptri, math::clamp(best_bary_weights, 0.0f, 1.0f)};
}
diff --git a/source/blender/geometry/intern/set_curve_type.cc b/source/blender/geometry/intern/set_curve_type.cc
index 08888a74973..92609a45bdc 100644
--- a/source/blender/geometry/intern/set_curve_type.cc
+++ b/source/blender/geometry/intern/set_curve_type.cc
@@ -1,9 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_attribute.hh"
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
#include "BKE_curves_utils.hh"
-#include "BKE_geometry_set.hh"
#include "BLI_task.hh"
@@ -286,75 +286,16 @@ static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan<
});
}
-struct GenericAttributes : NonCopyable, NonMovable {
- Vector<GSpan> src;
- Vector<GMutableSpan> dst;
-
- Vector<bke::GSpanAttributeWriter> attributes;
-};
-
-static void retrieve_generic_point_attributes(const bke::AttributeAccessor &src_attributes,
- bke::MutableAttributeAccessor &dst_attributes,
- GenericAttributes &attributes)
-{
- src_attributes.for_all(
- [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
- if (meta_data.domain != ATTR_DOMAIN_POINT) {
- /* Curve domain attributes are all copied directly to the result in one step. */
- return true;
- }
- if (src_attributes.is_builtin(id)) {
- if (!(id.is_named() && ELEM(id, "tilt", "radius"))) {
- return true;
- }
- }
-
- GVArray src_attribute = src_attributes.lookup(id, ATTR_DOMAIN_POINT);
- BLI_assert(src_attribute);
- attributes.src.append(src_attribute.get_internal_span());
-
- bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_span(
- id, ATTR_DOMAIN_POINT, meta_data.data_type);
- attributes.dst.append(dst_attribute.span);
- attributes.attributes.append(std::move(dst_attribute));
-
- return true;
- });
-}
-
-static Curves *create_result_curves(const bke::CurvesGeometry &src_curves,
- const IndexMask selection,
- const CurveType dst_type)
-{
- Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
- /* Directly copy curve attributes, since they stay the same (except for curve types). */
- CustomData_copy(&src_curves.curve_data,
- &dst_curves.curve_data,
- CD_MASK_ALL,
- CD_DUPLICATE,
- src_curves.curves_num());
-
- dst_curves.fill_curve_types(selection, dst_type);
-
- return dst_curves_id;
-}
-
-static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- const IndexMask selection)
+static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves,
+ const IndexMask selection)
{
const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
const VArray<int8_t> src_types = src_curves.curve_types();
const VArray<bool> src_cyclic = src_curves.cyclic();
const Span<float3> src_positions = src_curves.positions();
- Curves *dst_curves_id = create_result_curves(src_curves, selection, CURVE_TYPE_BEZIER);
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
+ bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
+ dst_curves.fill_curve_types(selection, CURVE_TYPE_BEZIER);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
@@ -367,11 +308,19 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
bke::curves::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
- const bke::AttributeAccessor src_attributes = *src_component.attributes();
- bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
+ const bke::AttributeAccessor src_attributes = src_curves.attributes();
+ bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
- GenericAttributes attributes;
- retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes);
+ Vector<bke::AttributeTransferData> generic_attributes = bke::retrieve_attributes_for_transfer(
+ src_attributes,
+ dst_attributes,
+ ATTR_DOMAIN_MASK_POINT,
+ {"position",
+ "handle_type_left",
+ "handle_type_right",
+ "handle_right",
+ "handle_left",
+ "nurbs_weight"});
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
@@ -396,9 +345,9 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
}
});
- for (const int i : attributes.src.index_range()) {
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ src_curves, dst_curves, selection, attribute.src, attribute.dst.span);
}
};
@@ -407,9 +356,9 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_l);
bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_r);
dst_curves.calculate_bezier_auto_handles();
- for (const int i : attributes.src.index_range()) {
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ src_curves, dst_curves, selection, attribute.src, attribute.dst.span);
}
};
@@ -427,9 +376,9 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
dst_curves.calculate_bezier_auto_handles();
- for (const int i : attributes.src.index_range()) {
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
bke::curves::copy_point_data(
- src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]);
+ src_curves, dst_curves, selection, attribute.src, attribute.dst.span);
}
};
@@ -468,14 +417,14 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
}
});
- for (const int i_attribute : attributes.src.index_range()) {
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
- nurbs_to_bezier_assign(attributes.src[i_attribute].slice(src_points),
+ nurbs_to_bezier_assign(attribute.src.slice(src_points),
KnotsMode(src_knot_modes[i]),
- attributes.dst[i_attribute].slice(dst_points));
+ attribute.dst.span.slice(dst_points));
}
});
}
@@ -492,30 +441,27 @@ static Curves *convert_curves_to_bezier(const CurveComponent &src_component,
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
src_curves.curves_range());
- for (const int i : attributes.src.index_range()) {
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
+ src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
}
- for (bke::GSpanAttributeWriter &attribute : attributes.attributes) {
- attribute.finish();
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
+ attribute.dst.finish();
}
- return dst_curves_id;
+ return dst_curves;
}
-static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- const IndexMask selection)
+static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &src_curves,
+ const IndexMask selection)
{
const VArray<int8_t> src_types = src_curves.curve_types();
const VArray<bool> src_cyclic = src_curves.cyclic();
const Span<float3> src_positions = src_curves.positions();
- Curves *dst_curves_id = create_result_curves(src_curves, selection, CURVE_TYPE_NURBS);
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
+ bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
+ dst_curves.fill_curve_types(selection, CURVE_TYPE_NURBS);
MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
retrieve_curve_sizes(src_curves, dst_curves.offsets_for_write());
@@ -527,11 +473,19 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
bke::curves::accumulate_counts_to_offsets(dst_offsets);
dst_curves.resize(dst_offsets.last(), dst_curves.curves_num());
- const bke::AttributeAccessor src_attributes = *src_component.attributes();
- bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
+ const bke::AttributeAccessor src_attributes = src_curves.attributes();
+ bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
- GenericAttributes attributes;
- retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes);
+ Vector<bke::AttributeTransferData> generic_attributes = bke::retrieve_attributes_for_transfer(
+ src_attributes,
+ dst_attributes,
+ ATTR_DOMAIN_MASK_POINT,
+ {"position",
+ "handle_type_left",
+ "handle_type_right",
+ "handle_right",
+ "handle_left",
+ "nurbs_weight"});
MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
@@ -555,13 +509,13 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
}
});
- for (const int i_attribute : attributes.src.index_range()) {
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
- bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
- attributes.dst[i_attribute].slice(dst_points));
+ bezier_generic_to_nurbs(attribute.src.slice(src_points),
+ attribute.dst.span.slice(dst_points));
}
});
}
@@ -589,12 +543,9 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
});
}
- for (const int i_attribute : attributes.src.index_range()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- selection,
- attributes.src[i_attribute],
- attributes.dst[i_attribute]);
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, selection, attribute.src, attribute.dst.span);
}
};
@@ -617,13 +568,13 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
}
});
- for (const int i_attribute : attributes.src.index_range()) {
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
for (const int i : selection.slice(range)) {
const IndexRange src_points = src_curves.points_for_curve(i);
const IndexRange dst_points = dst_curves.points_for_curve(i);
- bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points),
- attributes.dst[i_attribute].slice(dst_points));
+ bezier_generic_to_nurbs(attribute.src.slice(src_points),
+ attribute.dst.span.slice(dst_points));
}
});
}
@@ -640,12 +591,9 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
dst_curves.nurbs_weights_for_write());
}
- for (const int i_attribute : attributes.src.index_range()) {
- bke::curves::copy_point_data(src_curves,
- dst_curves,
- selection,
- attributes.src[i_attribute],
- attributes.dst[i_attribute]);
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, selection, attribute.src, attribute.dst.span);
}
};
@@ -660,16 +608,16 @@ static Curves *convert_curves_to_nurbs(const CurveComponent &src_component,
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
src_curves.curves_range());
- for (const int i : attributes.src.index_range()) {
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
bke::curves::copy_point_data(
- src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]);
+ src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
}
- for (bke::GSpanAttributeWriter &attribute : attributes.attributes) {
- attribute.finish();
+ for (bke::AttributeTransferData &attribute : generic_attributes) {
+ attribute.dst.finish();
}
- return dst_curves_id;
+ return dst_curves;
}
static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src_curves,
@@ -682,33 +630,31 @@ static bke::CurvesGeometry convert_curves_trivial(const bke::CurvesGeometry &src
return dst_curves;
}
-Curves *convert_curves(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- const IndexMask selection,
- const CurveType dst_type)
+bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ const CurveType dst_type)
{
switch (dst_type) {
case CURVE_TYPE_CATMULL_ROM:
case CURVE_TYPE_POLY:
- return bke::curves_new_nomain(convert_curves_trivial(src_curves, selection, dst_type));
+ return convert_curves_trivial(src_curves, selection, dst_type);
case CURVE_TYPE_BEZIER:
- return convert_curves_to_bezier(src_component, src_curves, selection);
+ return convert_curves_to_bezier(src_curves, selection);
case CURVE_TYPE_NURBS:
- return convert_curves_to_nurbs(src_component, src_curves, selection);
+ return convert_curves_to_nurbs(src_curves, selection);
}
BLI_assert_unreachable();
- return nullptr;
+ return {};
}
bool try_curves_conversion_in_place(const IndexMask selection,
const CurveType dst_type,
- FunctionRef<Curves &()> get_writable_curves_fn)
+ FunctionRef<bke::CurvesGeometry &()> get_writable_curves_fn)
{
if (conversion_can_change_point_num(dst_type)) {
return false;
}
- Curves &curves_id = get_writable_curves_fn();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ bke::CurvesGeometry &curves = get_writable_curves_fn();
curves.fill_curve_types(selection, dst_type);
curves.remove_attributes_based_on_types();
return true;
diff --git a/source/blender/geometry/intern/subdivide_curves.cc b/source/blender/geometry/intern/subdivide_curves.cc
index 914174235cd..8057f029e73 100644
--- a/source/blender/geometry/intern/subdivide_curves.cc
+++ b/source/blender/geometry/intern/subdivide_curves.cc
@@ -12,26 +12,6 @@
namespace blender::geometry {
/**
- * \warning Only the curve domain of the input is copied, so the result is invalid!
- */
-static Curves *create_result_curves(const bke::CurvesGeometry &src_curves)
-{
- Curves *dst_curves_id = bke::curves_new_nomain(0, src_curves.curves_num());
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
- /* Directly copy curve attributes, since they stay the same. */
- CustomData_copy(&src_curves.curve_data,
- &dst_curves.curve_data,
- CD_MASK_ALL,
- CD_DUPLICATE,
- src_curves.curves_num());
- dst_curves.runtime->type_counts = src_curves.runtime->type_counts;
-
- return dst_curves_id;
-}
-
-/**
* Return a range used to retrieve values from an array of values stored per point, but with an
* extra element at the end of each curve. This is useful for offsets within curves, where it is
* convenient to store the first 0 and have the last offset be the total result curve size.
@@ -76,40 +56,6 @@ static void calculate_result_offsets(const bke::CurvesGeometry &src_curves,
bke::curves::accumulate_counts_to_offsets(dst_curve_offsets);
}
-struct AttributeTransferData {
- /* Expect that if an attribute exists, it is stored as a contiguous array internally anyway. */
- GVArraySpan src;
- bke::GSpanAttributeWriter dst;
-};
-
-static Vector<AttributeTransferData> retrieve_point_attributes(
- const bke::AttributeAccessor &src_attributes,
- bke::MutableAttributeAccessor &dst_attributes,
- const Set<std::string> &skip = {})
-{
- Vector<AttributeTransferData> attributes;
- src_attributes.for_all(
- [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) {
- if (meta_data.domain != ATTR_DOMAIN_POINT) {
- /* Curve domain attributes are all copied directly to the result in one step. */
- return true;
- }
- if (id.is_named() && skip.contains(id.name())) {
- return true;
- }
-
- GVArray src = src_attributes.lookup(id, ATTR_DOMAIN_POINT);
- BLI_assert(src);
- bke::GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
- id, ATTR_DOMAIN_POINT, meta_data.data_type);
- BLI_assert(dst);
- attributes.append({std::move(src), std::move(dst)});
-
- return true;
- });
- return attributes;
-}
-
template<typename T>
static inline void linear_interpolation(const T &a, const T &b, MutableSpan<T> dst)
{
@@ -145,7 +91,8 @@ static void subdivide_attribute_linear(const bke::CurvesGeometry &src_curves,
}
});
- const IndexRange dst_last_segment = bke::offsets_to_range(offsets, src_points.size() - 1);
+ const IndexRange dst_last_segment = dst_points.slice(
+ bke::offsets_to_range(offsets, src_points.size() - 1));
linear_interpolation(curve_src.last(), curve_src.first(), dst.slice(dst_last_segment));
}
});
@@ -342,10 +289,9 @@ static void subdivide_bezier_positions(const Span<float3> src_positions,
cyclic, dst_types_l, dst_types_r, dst_positions, dst_handles_l, dst_handles_r);
}
-Curves *subdivide_curves(const CurveComponent &src_component,
- const bke::CurvesGeometry &src_curves,
- const IndexMask selection,
- const VArray<int> &cuts)
+bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ const VArray<int> &cuts)
{
const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert(
src_curves.curves_range());
@@ -353,10 +299,7 @@ Curves *subdivide_curves(const CurveComponent &src_component,
/* Cyclic is accessed a lot, it's probably worth it to make sure it's a span. */
const VArraySpan<bool> cyclic{src_curves.cyclic()};
- Curves *dst_curves_id = create_result_curves(src_curves);
- bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry);
- CurveComponent dst_component;
- dst_component.replace(dst_curves_id, GeometryOwnershipType::Editable);
+ bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves);
/* For each point, this contains the point offset in the corresponding result curve,
* starting at zero. For example for two curves with four points each, the values might
@@ -385,11 +328,12 @@ Curves *subdivide_curves(const CurveComponent &src_component,
dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
- const bke::AttributeAccessor src_attributes = *src_component.attributes();
- bke::MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
+ const bke::AttributeAccessor src_attributes = src_curves.attributes();
+ bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
auto subdivide_catmull_rom = [&](IndexMask selection) {
- for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) {
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
subdivide_attribute_catmull_rom(src_curves,
dst_curves,
selection,
@@ -402,7 +346,8 @@ Curves *subdivide_curves(const CurveComponent &src_component,
};
auto subdivide_poly = [&](IndexMask selection) {
- for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) {
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
subdivide_attribute_linear(
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
attribute.dst.finish();
@@ -443,13 +388,14 @@ Curves *subdivide_curves(const CurveComponent &src_component,
}
});
- for (auto &attribute : retrieve_point_attributes(src_attributes,
- dst_attributes,
- {"position",
- "handle_type_left",
- "handle_type_right",
- "handle_right",
- "handle_left"})) {
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(src_attributes,
+ dst_attributes,
+ ATTR_DOMAIN_MASK_POINT,
+ {"position",
+ "handle_type_left",
+ "handle_type_right",
+ "handle_right",
+ "handle_left"})) {
subdivide_attribute_linear(
src_curves, dst_curves, selection, point_offsets, attribute.src, attribute.dst.span);
attribute.dst.finish();
@@ -469,14 +415,15 @@ Curves *subdivide_curves(const CurveComponent &src_component,
subdivide_nurbs);
if (!unselected_ranges.is_empty()) {
- for (auto &attribute : retrieve_point_attributes(src_attributes, dst_attributes)) {
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
bke::curves::copy_point_data(
src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span);
attribute.dst.finish();
}
}
- return dst_curves_id;
+ return dst_curves;
}
} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/trim_curves.cc b/source/blender/geometry/intern/trim_curves.cc
new file mode 100644
index 00000000000..cd93f295685
--- /dev/null
+++ b/source/blender/geometry/intern/trim_curves.cc
@@ -0,0 +1,1299 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include "BLI_array_utils.hh"
+#include "BLI_length_parameterize.hh"
+
+#include "BKE_attribute.hh"
+#include "BKE_attribute_math.hh"
+#include "BKE_curves.hh"
+#include "BKE_curves_utils.hh"
+#include "BKE_geometry_set.hh"
+
+#include "GEO_trim_curves.hh"
+
+namespace blender::geometry {
+
+/* -------------------------------------------------------------------- */
+/** \name Curve Enums
+ * \{ */
+
+#define CURVE_TYPE_AS_MASK(curve_type) ((CurveTypeMask)((1 << (int)(curve_type))))
+
+typedef enum CurveTypeMask {
+ CURVE_TYPE_MASK_CATMULL_ROM = (1 << 0),
+ CURVE_TYPE_MASK_POLY = (1 << 1),
+ CURVE_TYPE_MASK_BEZIER = (1 << 2),
+ CURVE_TYPE_MASK_NURBS = (1 << 3),
+ CURVE_TYPE_MASK_ALL = (1 << 4) - 1
+} CurveTypeMask;
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name #IndexRangeCyclic Utilities
+ * \{ */
+
+/**
+ * Create a cyclical iterator for all control points within the interval [start_point, end_point]
+ * including any control point at the start or end point.
+ *
+ * \param start_point: Point on the curve that define the starting point of the interval.
+ * \param end_point: Point on the curve that define the end point of the interval (included).
+ * \param points: #IndexRange for the curve points.
+ */
+static bke::curves::IndexRangeCyclic get_range_between_endpoints(
+ const bke::curves::CurvePoint start_point,
+ const bke::curves::CurvePoint end_point,
+ const IndexRange points)
+{
+ const int64_t start_index = start_point.parameter == 0.0 ? start_point.index :
+ start_point.next_index;
+ int64_t end_index = end_point.parameter == 0.0 ? end_point.index : end_point.next_index;
+ int64_t cycles;
+
+ if (end_point.is_controlpoint()) {
+ ++end_index;
+ if (end_index > points.last()) {
+ end_index = points.one_after_last();
+ }
+ /* end_point < start_point but parameter is irrelevant (end_point is controlpoint), and loop
+ * when equal due to increment. */
+ cycles = end_index <= start_index;
+ }
+ else {
+ cycles = end_point < start_point || end_index < start_index;
+ }
+ return bke::curves::IndexRangeCyclic(start_index, end_index, points, cycles);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Lookup Curve Points
+ * \{ */
+
+/**
+ * Find the point on the curve defined by the distance along the curve. Assumes curve resolution is
+ * constant for all curve segments and evaluated curve points are uniformly spaced between the
+ * segment endpoints in relation to the curve parameter.
+ *
+ * \param lengths: Accumulated length for the evaluated curve.
+ * \param sample_length: Distance along the curve to determine the #CurvePoint for.
+ * \param cyclic: If curve is cyclic.
+ * \param resolution: Curve resolution (number of evaluated points per segment).
+ * \param num_curve_points: Total number of control points in the curve.
+ * \return: Point on the piecewise segment matching the given distance.
+ */
+static bke::curves::CurvePoint lookup_curve_point(const Span<float> lengths,
+ const float sample_length,
+ const bool cyclic,
+ const int resolution,
+ const int num_curve_points)
+{
+ BLI_assert(!cyclic || lengths.size() / resolution >= 2);
+ const int last_index = num_curve_points - 1;
+ if (sample_length <= 0.0f) {
+ return {{0, 1}, 0.0f};
+ }
+ if (sample_length >= lengths.last()) {
+ return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} :
+ bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0};
+ }
+ int eval_index;
+ float eval_factor;
+ length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor);
+
+ const int index = eval_index / resolution;
+ const int next_index = (index == last_index) ? 0 : index + 1;
+ const float parameter = (eval_factor + eval_index) / resolution - index;
+
+ return bke::curves::CurvePoint{{index, next_index}, parameter};
+}
+
+/**
+ * Find the point on the 'evaluated' polygonal curve.
+ */
+static bke::curves::CurvePoint lookup_evaluated_point(const Span<float> lengths,
+ const float sample_length,
+ const bool cyclic,
+ const int evaluated_size)
+{
+ const int last_index = evaluated_size - 1;
+ if (sample_length <= 0.0f) {
+ return {{0, 1}, 0.0f};
+ }
+ if (sample_length >= lengths.last()) {
+ return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} :
+ bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0};
+ }
+
+ int eval_index;
+ float eval_factor;
+ length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor);
+
+ const int next_eval_index = (eval_index == last_index) ? 0 : eval_index + 1;
+ return bke::curves::CurvePoint{{eval_index, next_eval_index}, eval_factor};
+}
+
+/**
+ * Find the point on a Bezier curve using the 'bezier_offsets' cache.
+ */
+static bke::curves::CurvePoint lookup_bezier_point(const Span<int> bezier_offsets,
+ const Span<float> lengths,
+ const float sample_length,
+ const bool cyclic,
+ const int num_curve_points)
+{
+ const int last_index = num_curve_points - 1;
+ if (sample_length <= 0.0f) {
+ return {{0, 1}, 0.0f};
+ }
+ if (sample_length >= lengths.last()) {
+ return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} :
+ bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0};
+ }
+ int eval_index;
+ float eval_factor;
+ length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor);
+
+ /* Find the segment index from the offset mapping. */
+ const int *offset = std::upper_bound(bezier_offsets.begin(), bezier_offsets.end(), eval_index);
+ const int left = offset - bezier_offsets.begin();
+ const int right = left == last_index ? 0 : left + 1;
+
+ const int prev_offset = left == 0 ? 0 : bezier_offsets[(int64_t)left - 1];
+ const float offset_in_segment = eval_factor + eval_index - prev_offset;
+ const int segment_resolution = bezier_offsets[left] - prev_offset;
+ const float parameter = std::clamp(offset_in_segment / segment_resolution, 0.0f, 1.0f);
+
+ return {{left, right}, parameter};
+}
+
+Array<bke::curves::CurvePoint, 12> lookup_curve_points(const bke::CurvesGeometry &curves,
+ const Span<float> lengths,
+ const Span<int64_t> curve_indices,
+ const bool normalized_factors)
+{
+ BLI_assert(lengths.size() == curve_indices.size());
+ BLI_assert(*std::max_element(curve_indices.begin(), curve_indices.end()) < curves.curves_num());
+
+ const VArray<bool> cyclic = curves.cyclic();
+ const VArray<int> resolution = curves.resolution();
+ const VArray<int8_t> curve_types = curves.curve_types();
+
+ /* Compute curve lengths! */
+ curves.ensure_evaluated_lengths();
+ curves.ensure_evaluated_offsets();
+
+ /* Find the curve points referenced by the input! */
+ Array<bke::curves::CurvePoint, 12> lookups(curve_indices.size());
+ threading::parallel_for(curve_indices.index_range(), 128, [&](const IndexRange range) {
+ for (const int64_t lookup_index : range) {
+ const int64_t curve_i = curve_indices[lookup_index];
+
+ const int point_count = curves.points_num_for_curve(curve_i);
+ if (curve_i < 0 || point_count == 1) {
+ lookups[lookup_index] = {{0, 0}, 0.0f};
+ continue;
+ }
+
+ const Span<float> accumulated_lengths = curves.evaluated_lengths_for_curve(curve_i,
+ cyclic[curve_i]);
+ BLI_assert(accumulated_lengths.size() > 0);
+
+ const float sample_length = normalized_factors ?
+ lengths[lookup_index] * accumulated_lengths.last() :
+ lengths[lookup_index];
+
+ const CurveType curve_type = (CurveType)curve_types[curve_i];
+
+ switch (curve_type) {
+ case CURVE_TYPE_BEZIER: {
+ if (bke::curves::bezier::has_vector_handles(
+ point_count,
+ curves.evaluated_points_for_curve(curve_i).size(),
+ cyclic[curve_i],
+ resolution[curve_i])) {
+ const Span<int> bezier_offsets = curves.bezier_evaluated_offsets_for_curve(curve_i);
+ lookups[lookup_index] = lookup_bezier_point(
+ bezier_offsets, accumulated_lengths, sample_length, cyclic[curve_i], point_count);
+ }
+ else {
+ lookups[lookup_index] = lookup_curve_point(accumulated_lengths,
+ sample_length,
+ cyclic[curve_i],
+ resolution[curve_i],
+ point_count);
+ }
+ break;
+ }
+ case CURVE_TYPE_CATMULL_ROM: {
+ lookups[lookup_index] = lookup_curve_point(accumulated_lengths,
+ sample_length,
+ cyclic[curve_i],
+ resolution[curve_i],
+ point_count);
+ break;
+ }
+ case CURVE_TYPE_NURBS:
+ case CURVE_TYPE_POLY:
+ default: {
+ /* Handle general case as an "evaluated" or polygonal curve. */
+ BLI_assert(resolution[curve_i] > 0);
+ lookups[lookup_index] = lookup_evaluated_point(
+ accumulated_lengths,
+ sample_length,
+ cyclic[curve_i],
+ curves.evaluated_points_for_curve(curve_i).size());
+ break;
+ }
+ }
+ }
+ });
+ return lookups;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transfer Curve Domain
+ * \{ */
+
+/**
+ * Determine curve type(s) for the copied curves given the supported set of types and knot modes.
+ * If a curve type is not supported the default type is set.
+ */
+static void determine_copyable_curve_types(const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const IndexMask selection_inverse,
+ const CurveTypeMask supported_curve_type_mask,
+ const int8_t default_curve_type = (int8_t)
+ CURVE_TYPE_POLY)
+{
+ const VArray<int8_t> src_curve_types = src_curves.curve_types();
+ const VArray<int8_t> src_knot_modes = src_curves.nurbs_knots_modes();
+ MutableSpan<int8_t> dst_curve_types = dst_curves.curve_types_for_write();
+
+ threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange selection_range) {
+ for (const int64_t curve_i : selection.slice(selection_range)) {
+ if (supported_curve_type_mask & CURVE_TYPE_AS_MASK(src_curve_types[curve_i])) {
+ dst_curve_types[curve_i] = src_curve_types[curve_i];
+ }
+ else {
+ dst_curve_types[curve_i] = default_curve_type;
+ }
+ }
+ });
+
+ array_utils::copy(src_curve_types, selection_inverse, dst_curve_types);
+}
+
+/**
+ * Determine if a curve is treated as an evaluated curve. Curves which inherently do not support
+ * trimming are discretized (e.g. NURBS).
+ */
+static bool copy_as_evaluated_curve(const int8_t src_type, const int8_t dst_type)
+{
+ return src_type != CURVE_TYPE_POLY && dst_type == CURVE_TYPE_POLY;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Specialized Curve Constructors
+ * \{ */
+
+static void compute_trim_result_offsets(const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ const IndexMask inverse_selection,
+ const Span<bke::curves::CurvePoint> start_points,
+ const Span<bke::curves::CurvePoint> end_points,
+ const VArray<int8_t> dst_curve_types,
+ MutableSpan<int> dst_curve_offsets,
+ Vector<int64_t> &r_curve_indices,
+ Vector<int64_t> &r_point_curve_indices)
+{
+ BLI_assert(r_curve_indices.size() == 0);
+ BLI_assert(r_point_curve_indices.size() == 0);
+ const VArray<bool> cyclic = src_curves.cyclic();
+ const VArray<int8_t> curve_types = src_curves.curve_types();
+ r_curve_indices.reserve(selection.size());
+
+ for (const int64_t curve_i : selection) {
+
+ int64_t src_point_count;
+
+ if (copy_as_evaluated_curve(curve_types[curve_i], dst_curve_types[curve_i])) {
+ src_point_count = src_curves.evaluated_points_for_curve(curve_i).size();
+ }
+ else {
+ src_point_count = (int64_t)src_curves.points_num_for_curve(curve_i);
+ }
+ BLI_assert(src_point_count > 0);
+
+ if (start_points[curve_i] == end_points[curve_i]) {
+ dst_curve_offsets[curve_i] = 1;
+ r_point_curve_indices.append(curve_i);
+ }
+ else {
+ const bke::curves::IndexRangeCyclic point_range = get_range_between_endpoints(
+ start_points[curve_i], end_points[curve_i], {0, src_point_count});
+ const int count = point_range.size() + !start_points[curve_i].is_controlpoint() +
+ !end_points[curve_i].is_controlpoint();
+ dst_curve_offsets[curve_i] = count;
+ r_curve_indices.append(curve_i);
+ }
+ BLI_assert(dst_curve_offsets[curve_i] > 0);
+ }
+ threading::parallel_for(
+ inverse_selection.index_range(), 4096, [&](const IndexRange selection_range) {
+ for (const int64_t curve_i : inverse_selection.slice(selection_range)) {
+ dst_curve_offsets[curve_i] = src_curves.points_num_for_curve(curve_i);
+ }
+ });
+ bke::curves::accumulate_counts_to_offsets(dst_curve_offsets);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Utility Functions
+ * \{ */
+
+static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask selection)
+{
+ if (dst_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
+ MutableSpan<float3> handle_positions_left = dst_curves.handle_positions_left_for_write();
+ MutableSpan<float3> handle_positions_right = dst_curves.handle_positions_right_for_write();
+ MutableSpan<int8_t> handle_types_left = dst_curves.handle_types_left_for_write();
+ MutableSpan<int8_t> handle_types_right = dst_curves.handle_types_right_for_write();
+
+ threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange points = dst_curves.points_for_curve(curve_i);
+ handle_types_right.slice(points).fill((int8_t)BEZIER_HANDLE_FREE);
+ handle_types_left.slice(points).fill((int8_t)BEZIER_HANDLE_FREE);
+ handle_positions_left.slice(points).fill({0.0f, 0.0f, 0.0f});
+ handle_positions_right.slice(points).fill({0.0f, 0.0f, 0.0f});
+ }
+ });
+ }
+}
+static void fill_nurbs_data(bke::CurvesGeometry &dst_curves, const IndexMask selection)
+{
+ if (dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) {
+ bke::curves::fill_points(dst_curves, selection, 0.0f, dst_curves.nurbs_weights_for_write());
+ }
+}
+
+template<typename T>
+static int64_t copy_point_data_between_endpoints(const Span<T> src_data,
+ MutableSpan<T> dst_data,
+ const bke::curves::IndexRangeCyclic src_range,
+ const int64_t src_index,
+ int64_t dst_index)
+{
+ int64_t increment;
+ if (src_range.cycles()) {
+ increment = src_range.size_before_loop();
+ dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_index, increment));
+ dst_index += increment;
+
+ increment = src_range.size_after_loop();
+ dst_data.slice(dst_index, increment)
+ .copy_from(src_data.slice(src_range.curve_range().first(), increment));
+ dst_index += increment;
+ }
+ else {
+ increment = src_range.one_after_last() - src_range.first();
+ dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_index, increment));
+ dst_index += increment;
+ }
+ return dst_index;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sampling Utilities
+ * \{ */
+
+template<typename T>
+static T interpolate_catmull_rom(const Span<T> src_data,
+ const bke::curves::CurvePoint insertion_point,
+ const bool src_cyclic)
+{
+ BLI_assert(insertion_point.index >= 0 && insertion_point.next_index < src_data.size());
+ int i0;
+ if (insertion_point.index == 0) {
+ i0 = src_cyclic ? src_data.size() - 1 : insertion_point.index;
+ }
+ else {
+ i0 = insertion_point.index - 1;
+ }
+ int i3 = insertion_point.next_index + 1;
+ if (i3 == src_data.size()) {
+ i3 = src_cyclic ? 0 : insertion_point.next_index;
+ }
+ return bke::curves::catmull_rom::interpolate<T>(src_data[i0],
+ src_data[insertion_point.index],
+ src_data[insertion_point.next_index],
+ src_data[i3],
+ insertion_point.parameter);
+}
+
+static bke::curves::bezier::Insertion knot_insert_bezier(
+ const Span<float3> positions,
+ const Span<float3> handles_left,
+ const Span<float3> handles_right,
+ const bke::curves::CurvePoint insertion_point)
+{
+ BLI_assert(
+ insertion_point.index + 1 == insertion_point.next_index ||
+ (insertion_point.next_index >= 0 && insertion_point.next_index < insertion_point.index));
+ return bke::curves::bezier::insert(positions[insertion_point.index],
+ handles_right[insertion_point.index],
+ handles_left[insertion_point.next_index],
+ positions[insertion_point.next_index],
+ insertion_point.parameter);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sample Single Point
+ * \{ */
+
+template<typename T>
+static void sample_linear(const Span<T> src_data,
+ MutableSpan<T> dst_data,
+ const IndexRange dst_range,
+ const bke::curves::CurvePoint sample_point)
+{
+ BLI_assert(dst_range.size() == 1);
+ if (sample_point.is_controlpoint()) {
+ /* Resolves cases where the source curve consist of a single control point. */
+ const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index;
+ dst_data[dst_range.first()] = src_data[index];
+ }
+ else {
+ dst_data[dst_range.first()] = attribute_math::mix2(
+ sample_point.parameter, src_data[sample_point.index], src_data[sample_point.next_index]);
+ }
+}
+
+template<typename T>
+static void sample_catmull_rom(const Span<T> src_data,
+ MutableSpan<T> dst_data,
+ const IndexRange dst_range,
+ const bke::curves::CurvePoint sample_point,
+ const bool src_cyclic)
+{
+ BLI_assert(dst_range.size() == 1);
+ if (sample_point.is_controlpoint()) {
+ /* Resolves cases where the source curve consist of a single control point. */
+ const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index;
+ dst_data[dst_range.first()] = src_data[index];
+ }
+ else {
+ dst_data[dst_range.first()] = interpolate_catmull_rom(src_data, sample_point, src_cyclic);
+ }
+}
+
+static void sample_bezier(const Span<float3> src_positions,
+ const Span<float3> src_handles_l,
+ const Span<float3> src_handles_r,
+ const Span<int8_t> src_types_l,
+ const Span<int8_t> src_types_r,
+ MutableSpan<float3> dst_positions,
+ MutableSpan<float3> dst_handles_l,
+ MutableSpan<float3> dst_handles_r,
+ MutableSpan<int8_t> dst_types_l,
+ MutableSpan<int8_t> dst_types_r,
+ const IndexRange dst_range,
+ const bke::curves::CurvePoint sample_point)
+{
+ BLI_assert(dst_range.size() == 1);
+ if (sample_point.is_controlpoint()) {
+ /* Resolves cases where the source curve consist of a single control point. */
+ const int index = sample_point.parameter == 1.0 ? sample_point.next_index : sample_point.index;
+ dst_positions[dst_range.first()] = src_positions[index];
+ dst_handles_l[dst_range.first()] = src_handles_l[index];
+ dst_handles_r[dst_range.first()] = src_handles_r[index];
+ dst_types_l[dst_range.first()] = src_types_l[index];
+ dst_types_r[dst_range.first()] = src_types_r[index];
+ }
+ else {
+ bke::curves::bezier::Insertion insertion_point = knot_insert_bezier(
+ src_positions, src_handles_l, src_handles_r, sample_point);
+ dst_positions[dst_range.first()] = insertion_point.position;
+ dst_handles_l[dst_range.first()] = insertion_point.left_handle;
+ dst_handles_r[dst_range.first()] = insertion_point.right_handle;
+ dst_types_l[dst_range.first()] = BEZIER_HANDLE_FREE;
+ dst_types_r[dst_range.first()] = BEZIER_HANDLE_FREE;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sample Curve Interval (Trim)
+ * \{ */
+
+/**
+ * Sample source curve data in the interval defined by the points [start_point, end_point].
+ * Uses linear interpolation to compute the endpoints.
+ *
+ * \tparam include_start_point If False, the 'start_point' point sample will not be copied
+ * and not accounted for in the destination range.
+ * \param src_data: Source to sample from.
+ * \param dst_data: Destination to write samples to.
+ * \param src_range: Interval within [start_point, end_point] to copy from the source point domain.
+ * \param dst_range: Interval to copy point data to in the destination buffer.
+ * \param start_point: Point on the source curve to start sampling from.
+ * \param end_point: Last point to sample in the source curve.
+ */
+template<typename T, bool include_start_point = true>
+static void sample_interval_linear(const Span<T> src_data,
+ MutableSpan<T> dst_data,
+ const bke::curves::IndexRangeCyclic src_range,
+ const IndexRange dst_range,
+ const bke::curves::CurvePoint start_point,
+ const bke::curves::CurvePoint end_point)
+{
+ int64_t src_index = src_range.first();
+ int64_t dst_index = dst_range.first();
+
+ if (start_point.is_controlpoint()) {
+ /* 'start_point' is included in the copy iteration. */
+ if constexpr (!include_start_point) {
+ /* Skip first. */
+ ++src_index;
+ }
+ }
+ else if constexpr (!include_start_point) {
+ /* Do nothing (excluded). */
+ }
+ else {
+ /* General case, sample 'start_point' */
+ dst_data[dst_index] = attribute_math::mix2(
+ start_point.parameter, src_data[start_point.index], src_data[start_point.next_index]);
+ ++dst_index;
+ }
+
+ dst_index = copy_point_data_between_endpoints(
+ src_data, dst_data, src_range, src_index, dst_index);
+
+ /* Handle last case */
+ if (end_point.is_controlpoint()) {
+ /* 'end_point' is included in the copy iteration. */
+ }
+ else {
+ dst_data[dst_index] = attribute_math::mix2(
+ end_point.parameter, src_data[end_point.index], src_data[end_point.next_index]);
+#ifdef DEBUG
+ ++dst_index;
+#endif
+ }
+ BLI_assert(dst_index == dst_range.one_after_last());
+}
+
+template<typename T, bool include_start_point = true>
+static void sample_interval_catmull_rom(const Span<T> src_data,
+ MutableSpan<T> dst_data,
+ const bke::curves::IndexRangeCyclic src_range,
+ const IndexRange dst_range,
+ const bke::curves::CurvePoint start_point,
+ const bke::curves::CurvePoint end_point,
+ const bool src_cyclic)
+{
+ int64_t src_index = src_range.first();
+ int64_t dst_index = dst_range.first();
+
+ if (start_point.is_controlpoint()) {
+ /* 'start_point' is included in the copy iteration. */
+ if constexpr (!include_start_point) {
+ /* Skip first. */
+ ++src_index;
+ }
+ }
+ else if constexpr (!include_start_point) {
+ /* Do nothing (excluded). */
+ }
+ else {
+ /* General case, sample 'start_point' */
+ dst_data[dst_index] = interpolate_catmull_rom(src_data, start_point, src_cyclic);
+ ++dst_index;
+ }
+
+ dst_index = copy_point_data_between_endpoints(
+ src_data, dst_data, src_range, src_index, dst_index);
+
+ /* Handle last case */
+ if (end_point.is_controlpoint()) {
+ /* 'end_point' is included in the copy iteration. */
+ }
+ else {
+ dst_data[dst_index] = interpolate_catmull_rom(src_data, end_point, src_cyclic);
+#ifdef DEBUG
+ ++dst_index;
+#endif
+ }
+ BLI_assert(dst_index == dst_range.one_after_last());
+}
+
+template<bool include_start_point = true>
+static void sample_interval_bezier(const Span<float3> src_positions,
+ const Span<float3> src_handles_l,
+ const Span<float3> src_handles_r,
+ const Span<int8_t> src_types_l,
+ const Span<int8_t> src_types_r,
+ MutableSpan<float3> dst_positions,
+ MutableSpan<float3> dst_handles_l,
+ MutableSpan<float3> dst_handles_r,
+ MutableSpan<int8_t> dst_types_l,
+ MutableSpan<int8_t> dst_types_r,
+ const bke::curves::IndexRangeCyclic src_range,
+ const IndexRange dst_range,
+ const bke::curves::CurvePoint start_point,
+ const bke::curves::CurvePoint end_point)
+{
+ bke::curves::bezier::Insertion start_point_insert;
+ int64_t src_index = src_range.first();
+ int64_t dst_index = dst_range.first();
+
+ bool start_point_trimmed = false;
+ if (start_point.is_controlpoint()) {
+ /* The 'start_point' control point is included in the copy iteration. */
+ if constexpr (!include_start_point) {
+ ++src_index; /* Skip first! */
+ }
+ }
+ else if constexpr (!include_start_point) {
+ /* Do nothing, 'start_point' is excluded. */
+ }
+ else {
+ /* General case, sample 'start_point'. */
+ start_point_insert = knot_insert_bezier(
+ src_positions, src_handles_l, src_handles_r, start_point);
+ dst_positions[dst_range.first()] = start_point_insert.position;
+ dst_handles_l[dst_range.first()] = start_point_insert.left_handle;
+ dst_handles_r[dst_range.first()] = start_point_insert.right_handle;
+ dst_types_l[dst_range.first()] = src_types_l[start_point.index];
+ dst_types_r[dst_range.first()] = src_types_r[start_point.index];
+
+ start_point_trimmed = true;
+ ++dst_index;
+ }
+
+ /* Copy point data between the 'start_point' and 'end_point'. */
+ int64_t increment = src_range.cycles() ? src_range.size_before_loop() :
+ src_range.one_after_last() - src_range.first();
+
+ const IndexRange dst_range_to_end(dst_index, increment);
+ const IndexRange src_range_to_end(src_index, increment);
+ dst_positions.slice(dst_range_to_end).copy_from(src_positions.slice(src_range_to_end));
+ dst_handles_l.slice(dst_range_to_end).copy_from(src_handles_l.slice(src_range_to_end));
+ dst_handles_r.slice(dst_range_to_end).copy_from(src_handles_r.slice(src_range_to_end));
+ dst_types_l.slice(dst_range_to_end).copy_from(src_types_l.slice(src_range_to_end));
+ dst_types_r.slice(dst_range_to_end).copy_from(src_types_r.slice(src_range_to_end));
+ dst_index += increment;
+
+ increment = src_range.size_after_loop();
+ if (src_range.cycles() && increment > 0) {
+ const IndexRange dst_range_looped(dst_index, increment);
+ const IndexRange src_range_looped(src_range.curve_range().first(), increment);
+ dst_positions.slice(dst_range_looped).copy_from(src_positions.slice(src_range_looped));
+ dst_handles_l.slice(dst_range_looped).copy_from(src_handles_l.slice(src_range_looped));
+ dst_handles_r.slice(dst_range_looped).copy_from(src_handles_r.slice(src_range_looped));
+ dst_types_l.slice(dst_range_looped).copy_from(src_types_l.slice(src_range_looped));
+ dst_types_r.slice(dst_range_looped).copy_from(src_types_r.slice(src_range_looped));
+ dst_index += increment;
+ }
+
+ if (start_point_trimmed) {
+ dst_handles_l[dst_range.first() + 1] = start_point_insert.handle_next;
+ /* No need to set handle type (remains the same)! */
+ }
+
+ /* Handle 'end_point' */
+ bke::curves::bezier::Insertion end_point_insert;
+ if (end_point.is_controlpoint()) {
+ /* Do nothing, the 'end_point' control point is included in the copy iteration. */
+ }
+ else {
+ /* Trimmed in both ends within the same (and only) segment! Ensure both end points is not a
+ * loop. */
+ if (start_point_trimmed && start_point.index == end_point.index &&
+ start_point.parameter <= end_point.parameter) {
+
+ /* Copy following segment control point. */
+ dst_positions[dst_index] = src_positions[end_point.next_index];
+ dst_handles_r[dst_index] = src_handles_r[end_point.next_index];
+
+ /* Compute interpolation in the result curve. */
+ const float parameter = (end_point.parameter - start_point.parameter) /
+ (1.0f - start_point.parameter);
+ end_point_insert = knot_insert_bezier(
+ dst_positions,
+ dst_handles_l,
+ dst_handles_r,
+ {{(int)dst_range.first(), (int)(dst_range.first() + 1)}, parameter});
+ }
+ else {
+ /* General case, compute the insertion point. */
+ end_point_insert = knot_insert_bezier(
+ src_positions, src_handles_l, src_handles_r, end_point);
+ }
+
+ dst_handles_r[dst_index - 1] = end_point_insert.handle_prev;
+ dst_types_r[dst_index - 1] = src_types_l[end_point.index];
+
+ dst_handles_l[dst_index] = end_point_insert.left_handle;
+ dst_handles_r[dst_index] = end_point_insert.right_handle;
+ dst_positions[dst_index] = end_point_insert.position;
+ dst_types_l[dst_index] = src_types_l[end_point.next_index];
+ dst_types_r[dst_index] = src_types_r[end_point.next_index];
+#ifdef DEBUG
+ ++dst_index;
+#endif // DEBUG
+ }
+ BLI_assert(dst_index == dst_range.one_after_last());
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Convert to Point Curves
+ * \{ */
+
+static void convert_point_polygonal_curves(
+ const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> sample_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ const Span<float3> src_positions = src_curves.positions();
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+
+ sample_linear<float3>(
+ src_positions.slice(src_points), dst_positions, dst_points, sample_points[curve_i]);
+
+ for (bke::AttributeTransferData &attribute : transfer_attributes) {
+ attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_linear<T>(attribute.src.template typed<T>().slice(src_points),
+ attribute.dst.span.typed<T>(),
+ dst_curves.points_for_curve(curve_i),
+ sample_points[curve_i]);
+ });
+ }
+ }
+ });
+
+ fill_bezier_data(dst_curves, selection);
+ fill_nurbs_data(dst_curves, selection);
+}
+
+static void convert_point_catmull_curves(
+ const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> sample_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ const Span<float3> src_positions = src_curves.positions();
+ const VArray<bool> src_cyclic = src_curves.cyclic();
+
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+
+ sample_catmull_rom<float3>(src_positions.slice(src_points),
+ dst_positions,
+ dst_points,
+ sample_points[curve_i],
+ src_cyclic[curve_i]);
+ for (bke::AttributeTransferData &attribute : transfer_attributes) {
+ attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points),
+ attribute.dst.span.typed<T>(),
+ dst_points,
+ sample_points[curve_i],
+ src_cyclic[curve_i]);
+ });
+ }
+ }
+ });
+ fill_bezier_data(dst_curves, selection);
+ fill_nurbs_data(dst_curves, selection);
+}
+
+static void convert_point_bezier_curves(
+ const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> sample_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ const Span<float3> src_positions = src_curves.positions();
+ const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()};
+ const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()};
+ const Span<float3> src_handles_l = src_curves.handle_positions_left();
+ const Span<float3> src_handles_r = src_curves.handle_positions_right();
+
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+ MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
+ MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
+ MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
+ MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
+
+ threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+
+ sample_bezier(src_positions.slice(src_points),
+ src_handles_l.slice(src_points),
+ src_handles_r.slice(src_points),
+ src_types_l.slice(src_points),
+ src_types_r.slice(src_points),
+ dst_positions,
+ dst_handles_l,
+ dst_handles_r,
+ dst_types_l,
+ dst_types_r,
+ dst_points,
+ sample_points[curve_i]);
+
+ for (bke::AttributeTransferData &attribute : transfer_attributes) {
+ attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ sample_linear<T>(attribute.src.template typed<T>().slice(src_points),
+ attribute.dst.span.typed<T>(),
+ dst_points,
+ sample_points[curve_i]);
+ });
+ }
+ }
+ });
+ fill_nurbs_data(dst_curves, selection);
+}
+
+static void convert_point_evaluated_curves(
+ const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> evaluated_sample_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ const Span<float3> src_eval_positions = src_curves.evaluated_positions();
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+ const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i);
+
+ sample_linear<float3>(src_eval_positions.slice(src_evaluated_points),
+ dst_positions,
+ dst_points,
+ evaluated_sample_points[curve_i]);
+
+ for (bke::AttributeTransferData &attribute : transfer_attributes) {
+ attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+ GArray evaluated_data(CPPType::get<T>(), src_evaluated_points.size());
+ GMutableSpan evaluated_span = evaluated_data.as_mutable_span();
+ src_curves.interpolate_to_evaluated(
+ curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span);
+ sample_linear<T>(evaluated_span.typed<T>(),
+ attribute.dst.span.typed<T>(),
+ dst_points,
+ evaluated_sample_points[curve_i]);
+ });
+ }
+ }
+ });
+ fill_bezier_data(dst_curves, selection);
+ fill_nurbs_data(dst_curves, selection);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Trim Curves
+ * \{ */
+
+static void trim_attribute_linear(const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> start_points,
+ const Span<bke::curves::CurvePoint> end_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ for (bke::AttributeTransferData &attribute : transfer_attributes) {
+ attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+
+ threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+
+ bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints(
+ start_points[curve_i], end_points[curve_i], {0, src_points.size()});
+ sample_interval_linear<T>(attribute.src.template typed<T>().slice(src_points),
+ attribute.dst.span.typed<T>(),
+ src_sample_range,
+ dst_curves.points_for_curve(curve_i),
+ start_points[curve_i],
+ end_points[curve_i]);
+ }
+ });
+ });
+ }
+}
+
+static void trim_polygonal_curves(const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> start_points,
+ const Span<bke::curves::CurvePoint> end_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ const Span<float3> src_positions = src_curves.positions();
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+
+ bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints(
+ start_points[curve_i], end_points[curve_i], {0, src_points.size()});
+ sample_interval_linear<float3>(src_positions.slice(src_points),
+ dst_positions,
+ src_sample_range,
+ dst_points,
+ start_points[curve_i],
+ end_points[curve_i]);
+ }
+ });
+ fill_bezier_data(dst_curves, selection);
+ fill_nurbs_data(dst_curves, selection);
+ trim_attribute_linear(
+ src_curves, dst_curves, selection, start_points, end_points, transfer_attributes);
+}
+
+static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> start_points,
+ const Span<bke::curves::CurvePoint> end_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ const Span<float3> src_positions = src_curves.positions();
+ const VArray<bool> src_cyclic = src_curves.cyclic();
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+
+ bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints(
+ start_points[curve_i], end_points[curve_i], {0, src_points.size()});
+ sample_interval_catmull_rom<float3>(src_positions.slice(src_points),
+ dst_positions,
+ src_sample_range,
+ dst_points,
+ start_points[curve_i],
+ end_points[curve_i],
+ src_cyclic[curve_i]);
+ }
+ });
+ fill_bezier_data(dst_curves, selection);
+ fill_nurbs_data(dst_curves, selection);
+
+ for (bke::AttributeTransferData &attribute : transfer_attributes) {
+ attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+
+ threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+
+ bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints(
+ start_points[curve_i], end_points[curve_i], {0, src_points.size()});
+ sample_interval_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points),
+ attribute.dst.span.typed<T>(),
+ src_sample_range,
+ dst_points,
+ start_points[curve_i],
+ end_points[curve_i],
+ src_cyclic[curve_i]);
+ }
+ });
+ });
+ }
+}
+
+static void trim_bezier_curves(const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> start_points,
+ const Span<bke::curves::CurvePoint> end_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ const Span<float3> src_positions = src_curves.positions();
+ const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()};
+ const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()};
+ const Span<float3> src_handles_l = src_curves.handle_positions_left();
+ const Span<float3> src_handles_r = src_curves.handle_positions_right();
+
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+ MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write();
+ MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write();
+ MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write();
+ MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write();
+
+ threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange src_points = src_curves.points_for_curve(curve_i);
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+
+ bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints(
+ start_points[curve_i], end_points[curve_i], {0, src_points.size()});
+ sample_interval_bezier(src_positions.slice(src_points),
+ src_handles_l.slice(src_points),
+ src_handles_r.slice(src_points),
+ src_types_l.slice(src_points),
+ src_types_r.slice(src_points),
+ dst_positions,
+ dst_handles_l,
+ dst_handles_r,
+ dst_types_l,
+ dst_types_r,
+ src_sample_range,
+ dst_points,
+ start_points[curve_i],
+ end_points[curve_i]);
+ }
+ });
+ fill_nurbs_data(dst_curves, selection);
+ trim_attribute_linear(
+ src_curves, dst_curves, selection, start_points, end_points, transfer_attributes);
+}
+
+static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves,
+ bke::CurvesGeometry &dst_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> start_points,
+ const Span<bke::curves::CurvePoint> end_points,
+ MutableSpan<bke::AttributeTransferData> transfer_attributes)
+{
+ const Span<float3> src_eval_positions = src_curves.evaluated_positions();
+ MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
+
+ threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ const IndexRange dst_points = dst_curves.points_for_curve(curve_i);
+ const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i);
+
+ bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints(
+ start_points[curve_i], end_points[curve_i], {0, src_evaluated_points.size()});
+ sample_interval_linear<float3>(src_eval_positions.slice(src_evaluated_points),
+ dst_positions,
+ src_sample_range,
+ dst_points,
+ start_points[curve_i],
+ end_points[curve_i]);
+ }
+ });
+ fill_bezier_data(dst_curves, selection);
+ fill_nurbs_data(dst_curves, selection);
+
+ for (bke::AttributeTransferData &attribute : transfer_attributes) {
+ attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+
+ threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) {
+ for (const int64_t curve_i : selection.slice(range)) {
+ /* Interpolate onto the evaluated point domain and sample the evaluated domain. */
+ const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i);
+ GArray evaluated_data(CPPType::get<T>(), src_evaluated_points.size());
+ GMutableSpan evaluated_span = evaluated_data.as_mutable_span();
+ src_curves.interpolate_to_evaluated(
+ curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span);
+ bke::curves::IndexRangeCyclic src_sample_range = get_range_between_endpoints(
+ start_points[curve_i], end_points[curve_i], {0, src_evaluated_points.size()});
+ sample_interval_linear<T>(evaluated_span.typed<T>(),
+ attribute.dst.span.typed<T>(),
+ src_sample_range,
+ dst_curves.points_for_curve(curve_i),
+ start_points[curve_i],
+ end_points[curve_i]);
+ }
+ });
+ });
+ }
+}
+
+bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
+ const IndexMask selection,
+ const Span<bke::curves::CurvePoint> start_points,
+ const Span<bke::curves::CurvePoint> end_points)
+{
+ BLI_assert(selection.size() > 0);
+ BLI_assert(selection.last() <= start_points.size());
+ BLI_assert(start_points.size() == end_points.size());
+
+ src_curves.ensure_evaluated_offsets();
+ Vector<int64_t> inverse_selection_indices;
+ const IndexMask inverse_selection = selection.invert(src_curves.curves_range(),
+ inverse_selection_indices);
+
+ /* Create trim curves. */
+ bke::CurvesGeometry dst_curves(0, src_curves.curves_num());
+ determine_copyable_curve_types(src_curves,
+ dst_curves,
+ selection,
+ inverse_selection,
+ (CurveTypeMask)(CURVE_TYPE_MASK_CATMULL_ROM |
+ CURVE_TYPE_MASK_POLY | CURVE_TYPE_MASK_BEZIER));
+
+ Vector<int64_t> curve_indices;
+ Vector<int64_t> point_curve_indices;
+ compute_trim_result_offsets(src_curves,
+ selection,
+ inverse_selection,
+ start_points,
+ end_points,
+ dst_curves.curve_types(),
+ dst_curves.offsets_for_write(),
+ curve_indices,
+ point_curve_indices);
+ /* Finalize by updating the geometry container. */
+ dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num());
+ dst_curves.update_curve_types();
+
+ /* Populate curve domain. */
+ const bke::AttributeAccessor src_attributes = src_curves.attributes();
+ bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
+ bke::copy_attribute_domain(src_attributes,
+ dst_attributes,
+ selection,
+ ATTR_DOMAIN_CURVE,
+ {"cyclic", "curve_type", "nurbs_order", "knots_mode"});
+
+ /* Fetch custom point domain attributes for transfer (copy). */
+ Vector<bke::AttributeTransferData> transfer_attributes = bke::retrieve_attributes_for_transfer(
+ src_attributes,
+ dst_attributes,
+ ATTR_DOMAIN_MASK_POINT,
+ {"position",
+ "handle_left",
+ "handle_right",
+ "handle_type_left",
+ "handle_type_right",
+ "nurbs_weight"});
+
+ auto trim_catmull = [&](IndexMask selection) {
+ trim_catmull_rom_curves(
+ src_curves, dst_curves, selection, start_points, end_points, transfer_attributes);
+ };
+ auto trim_poly = [&](IndexMask selection) {
+ trim_polygonal_curves(
+ src_curves, dst_curves, selection, start_points, end_points, transfer_attributes);
+ };
+ auto trim_bezier = [&](IndexMask selection) {
+ trim_bezier_curves(
+ src_curves, dst_curves, selection, start_points, end_points, transfer_attributes);
+ };
+ auto trim_evaluated = [&](IndexMask selection) {
+ /* Ensure evaluated positions are available. */
+ src_curves.ensure_evaluated_offsets();
+ src_curves.evaluated_positions();
+ trim_evaluated_curves(
+ src_curves, dst_curves, selection, start_points, end_points, transfer_attributes);
+ };
+
+ auto single_point_catmull = [&](IndexMask selection) {
+ convert_point_catmull_curves(
+ src_curves, dst_curves, selection, start_points, transfer_attributes);
+ };
+ auto single_point_poly = [&](IndexMask selection) {
+ convert_point_polygonal_curves(
+ src_curves, dst_curves, selection, start_points, transfer_attributes);
+ };
+ auto single_point_bezier = [&](IndexMask selection) {
+ convert_point_bezier_curves(
+ src_curves, dst_curves, selection, start_points, transfer_attributes);
+ };
+ auto single_point_evaluated = [&](IndexMask selection) {
+ convert_point_evaluated_curves(
+ src_curves, dst_curves, selection, start_points, transfer_attributes);
+ };
+
+ /* Populate point domain. */
+ bke::curves::foreach_curve_by_type(src_curves.curve_types(),
+ src_curves.curve_type_counts(),
+ curve_indices.as_span(),
+ trim_catmull,
+ trim_poly,
+ trim_bezier,
+ trim_evaluated);
+
+ if (point_curve_indices.size()) {
+ bke::curves::foreach_curve_by_type(src_curves.curve_types(),
+ src_curves.curve_type_counts(),
+ point_curve_indices.as_span(),
+ single_point_catmull,
+ single_point_poly,
+ single_point_bezier,
+ single_point_evaluated);
+ }
+ /* Cleanup/close context */
+ for (bke::AttributeTransferData &attribute : transfer_attributes) {
+ attribute.dst.finish();
+ }
+
+ /* Copy unselected */
+ if (!inverse_selection.is_empty()) {
+ bke::copy_attribute_domain(
+ src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE);
+ /* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */
+ dst_curves.cyclic_for_write().fill_indices(selection, false);
+
+ /* Copy point domain. */
+ for (auto &attribute : bke::retrieve_attributes_for_transfer(
+ src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) {
+ bke::curves::copy_point_data(
+ src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span);
+ attribute.dst.finish();
+ }
+ }
+
+ dst_curves.tag_topology_changed();
+ return dst_curves;
+}
+
+/** \} */
+
+} // namespace blender::geometry
diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.cc
index c75a302f8dc..b15c8b6e319 100644
--- a/source/blender/geometry/intern/uv_parametrizer.c
+++ b/source/blender/geometry/intern/uv_parametrizer.cc
@@ -22,15 +22,15 @@
/* Utils */
#define param_assert(condition) \
- if (!(condition)) { /*printf("Assertion %s:%d\n", __FILE__, __LINE__); abort();*/ \
+ if (!(condition)) { /* `printf("Assertion %s:%d\n", __FILE__, __LINE__); abort();` */ \
} \
(void)0
#define param_warning(message) \
- {/*printf("Warning %s:%d: %s\n", __FILE__, __LINE__, message);*/}(void)0
+ {/* `printf("Warning %s:%d: %s\n", __FILE__, __LINE__, message);` */}(void)0
/* Special Purpose Hash */
-typedef intptr_t PHashKey;
+typedef uintptr_t PHashKey;
typedef struct PHashLink {
struct PHashLink *next;
@@ -45,24 +45,22 @@ typedef struct PHash {
/* Simplices */
-typedef struct PVert {
+struct PVert {
struct PVert *nextlink;
union PVertUnion {
PHashKey key; /* Construct. */
int id; /* ABF/LSCM matrix index. */
- float distortion; /* Area smoothing. */
HeapNode *heaplink; /* Edge collapsing. */
} u;
struct PEdge *edge;
float co[3];
float uv[2];
- uchar flag;
-
-} PVert;
+ uint flag;
+};
-typedef struct PEdge {
+struct PEdge {
struct PEdge *nextlink;
union PEdgeUnion {
@@ -77,11 +75,10 @@ typedef struct PEdge {
struct PEdge *next;
struct PFace *face;
float *orig_uv, old_uv[2];
- ushort flag;
-
-} PEdge;
+ uint flag;
+};
-typedef struct PFace {
+struct PFace {
struct PFace *nextlink;
union PFaceUnion {
@@ -92,8 +89,8 @@ typedef struct PFace {
} u;
struct PEdge *edge;
- uchar flag;
-} PFace;
+ uint flag;
+};
enum PVertFlag {
PVERT_PIN = 1,
@@ -126,7 +123,7 @@ enum PFaceFlag {
/* Chart */
-typedef struct PChart {
+struct PChart {
PVert *verts;
PEdge *edges;
PFace *faces;
@@ -148,16 +145,12 @@ typedef struct PChart {
} lscm;
struct PChartPack {
float rescale, area;
- float size[2] /* , trans[2] */;
+ float size[2];
+ float origin[2];
} pack;
} u;
- uchar flag;
- ParamHandle *handle;
-} PChart;
-
-enum PChartFlag {
- PCHART_HAS_PINS = 1,
+ bool has_pins;
};
enum PHandleState {
@@ -167,7 +160,7 @@ enum PHandleState {
PHANDLE_STATE_STRETCH,
};
-typedef struct ParamHandle {
+struct ParamHandle {
enum PHandleState state;
MemArena *arena;
MemArena *polyfill_arena;
@@ -188,7 +181,7 @@ typedef struct ParamHandle {
RNG *rng;
float blend;
-} ParamHandle;
+};
/* PHash
* - special purpose hash that keeps all its elements in a single linked list.
@@ -224,8 +217,10 @@ static PHash *phash_new(PHashLink **list, int sizehint)
static void phash_delete(PHash *ph)
{
- MEM_freeN(ph->buckets);
- MEM_freeN(ph);
+ if (ph) {
+ MEM_SAFE_FREE(ph->buckets);
+ MEM_freeN(ph);
+ }
}
static int phash_size(PHash *ph)
@@ -239,7 +234,7 @@ static void phash_insert(PHash *ph, PHashLink *link)
uintptr_t hash = PHASH_hash(ph, link->key);
PHashLink *lookup = ph->buckets[hash];
- if (lookup == NULL) {
+ if (lookup == nullptr) {
/* insert in front of the list */
ph->buckets[hash] = link;
link->next = *(ph->list);
@@ -254,13 +249,13 @@ static void phash_insert(PHash *ph, PHashLink *link)
ph->size++;
if (ph->size > (size * 3)) {
- PHashLink *next = NULL, *first = *(ph->list);
+ PHashLink *next = nullptr, *first = *(ph->list);
ph->cursize = PHashSizes[++ph->cursize_id];
MEM_freeN(ph->buckets);
ph->buckets = (PHashLink **)MEM_callocN(ph->cursize * sizeof(*ph->buckets), "PHashBuckets");
ph->size = 0;
- *(ph->list) = NULL;
+ *(ph->list) = nullptr;
for (link = first; link; link = next) {
next = link->next;
@@ -279,7 +274,7 @@ static PHashLink *phash_lookup(PHash *ph, PHashKey key)
return link;
}
if (PHASH_hash(ph, link->key) != hash) {
- return NULL;
+ return nullptr;
}
}
@@ -295,7 +290,7 @@ static PHashLink *phash_next(PHash *ph, PHashKey key, PHashLink *link)
return link;
}
if (PHASH_hash(ph, link->key) != hash) {
- return NULL;
+ return nullptr;
}
}
@@ -312,12 +307,70 @@ static float p_vec2_angle(const float v1[2], const float v2[2], const float v3[2
{
return angle_v2v2v2(v1, v2, v3);
}
+
+/* Angles close to 0 or 180 degrees cause rows filled with zeros in the linear_solver.
+ * The matrix will then be rank deficient and / or have poor conditioning.
+ * => Reduce the maximum angle to 179 degrees, and spread the remainder to the other angles.
+ */
+static void fix_large_angle(const float v_fix[3],
+ const float v1[3],
+ const float v2[3],
+ float *r_fix,
+ float *r_a1,
+ float *r_a2)
+{
+ const float max_angle = (float)M_PI * (179.0f / 180.0f);
+ const float fix_amount = *r_fix - max_angle;
+ if (fix_amount < 0.0f) {
+ return; /* angle is reasonable, i.e. less than 179 degrees. */
+ }
+
+ /* The triangle is probably degenerate, or close to it.
+ * Without loss of generality, transform the triangle such that
+ * v_fix == { 0, s}, *r_fix = 180 degrees
+ * v1 == {-x1, 0}, *r_a1 = 0
+ * v2 == { x2, 0}, *r_a2 = 0
+ *
+ * With `s = 0`, `x1 > 0`, `x2 > 0`
+ *
+ * Now make `s` a small number and do some math:
+ * tan(*r_a1) = s / x1
+ * tan(*r_a2) = s / x2
+ *
+ * Remember that `tan = sin / cos`, `sin(s) ~= s` and `cos(s) = 1`
+ *
+ * Rearrange to obtain:
+ * *r_a1 = fix_amount * x2 / (x1 + x2)
+ * *r_a2 = fix_amount * x1 / (x1 + x2)
+ */
+
+ const float dist_v1 = len_v3v3(v_fix, v1);
+ const float dist_v2 = len_v3v3(v_fix, v2);
+ const float sum = dist_v1 + dist_v2;
+ const float weight = (sum > 1e-20f) ? dist_v2 / sum : 0.5f;
+
+ /* Ensure sum of angles in triangle is unchanged. */
+ *r_fix -= fix_amount;
+ *r_a1 += fix_amount * weight;
+ *r_a2 += fix_amount * (1.0f - weight);
+}
+
static void p_triangle_angles(
const float v1[3], const float v2[3], const float v3[3], float *r_a1, float *r_a2, float *r_a3)
{
*r_a1 = p_vec_angle(v3, v1, v2);
*r_a2 = p_vec_angle(v1, v2, v3);
- *r_a3 = (float)M_PI - *r_a2 - *r_a1;
+ *r_a3 = p_vec_angle(v2, v3, v1);
+
+ /* Fix for degenerate geometry e.g. v1 = sum(v2 + v3). See T100874 */
+ fix_large_angle(v1, v2, v3, r_a1, r_a2, r_a3);
+ fix_large_angle(v2, v3, v1, r_a2, r_a3, r_a1);
+ fix_large_angle(v3, v1, v2, r_a3, r_a1, r_a2);
+
+ /* Workaround for degenerate geometry, e.g. v1 == v2 == v3. */
+ *r_a1 = max_ff(*r_a1, 0.001f);
+ *r_a2 = max_ff(*r_a2, 0.001f);
+ *r_a3 = max_ff(*r_a3, 0.001f);
}
static void p_face_angles(PFace *f, float *r_a1, float *r_a2, float *r_a3)
@@ -461,7 +514,7 @@ static PEdge *p_wheel_edge_next(PEdge *e)
static PEdge *p_wheel_edge_prev(PEdge *e)
{
- return (e->pair) ? e->pair->next : NULL;
+ return (e->pair) ? e->pair->next : nullptr;
}
static PEdge *p_boundary_edge_next(PEdge *e)
@@ -669,9 +722,9 @@ static PVert *p_vert_lookup(ParamHandle *handle, PHashKey key, const float co[3]
return p_vert_add(handle, key, co, e);
}
-static PVert *p_vert_copy(PChart *chart, PVert *v)
+static PVert *p_vert_copy(ParamHandle *handle, PVert *v)
{
- PVert *nv = (PVert *)BLI_memarena_alloc(chart->handle->arena, sizeof(*nv));
+ PVert *nv = (PVert *)BLI_memarena_alloc(handle->arena, sizeof(*nv));
copy_v3_v3(nv->co, v->co);
nv->uv[0] = v->uv[0];
@@ -699,7 +752,7 @@ static PEdge *p_edge_lookup(ParamHandle *handle, const PHashKey *vkeys)
e = (PEdge *)phash_next(handle->hash_edges, key, (PHashLink *)e);
}
- return NULL;
+ return nullptr;
}
static int p_face_exists(ParamHandle *handle, const ParamKey *pvkeys, int i1, int i2, int i3)
@@ -726,20 +779,6 @@ static int p_face_exists(ParamHandle *handle, const ParamKey *pvkeys, int i1, in
return false;
}
-static PChart *p_chart_new(ParamHandle *handle)
-{
- PChart *chart = (PChart *)MEM_callocN(sizeof(*chart), "PChart");
- chart->handle = handle;
-
- return chart;
-}
-
-static void p_chart_delete(PChart *chart)
-{
- /* the actual links are free by memarena */
- MEM_freeN(chart);
-}
-
static bool p_edge_implicit_seam(PEdge *e, PEdge *ep)
{
float *uv1, *uv2, *uvp1, *uvp2;
@@ -788,7 +827,7 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv
key = PHASH_edge(key1, key2);
pe = (PEdge *)phash_lookup(handle->hash_edges, key);
- *r_pair = NULL;
+ *r_pair = nullptr;
while (pe) {
if (pe != e) {
@@ -801,7 +840,7 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv
/* don't connect seams and t-junctions */
if ((pe->flag & PEDGE_SEAM) || *r_pair ||
(topology_from_uvs && p_edge_implicit_seam(e, pe))) {
- *r_pair = NULL;
+ *r_pair = nullptr;
return false;
}
@@ -815,12 +854,12 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv
if (*r_pair && (e->vert == (*r_pair)->vert)) {
if ((*r_pair)->next->pair || (*r_pair)->next->next->pair) {
/* non unfoldable, maybe mobius ring or klein bottle */
- *r_pair = NULL;
+ *r_pair = nullptr;
return false;
}
}
- return (*r_pair != NULL);
+ return (*r_pair != nullptr);
}
static bool p_edge_connect_pair(ParamHandle *handle,
@@ -828,7 +867,7 @@ static bool p_edge_connect_pair(ParamHandle *handle,
bool topology_from_uvs,
PEdge ***stack)
{
- PEdge *pair = NULL;
+ PEdge *pair = nullptr;
if (!e->pair && p_edge_has_pair(handle, e, topology_from_uvs, &pair)) {
if (e->vert == pair->vert) {
@@ -844,13 +883,13 @@ static bool p_edge_connect_pair(ParamHandle *handle,
}
}
- return (e->pair != NULL);
+ return (e->pair != nullptr);
}
static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs)
{
- PEdge **stackbase = MEM_mallocN(sizeof(*stackbase) * phash_size(handle->hash_faces),
- "Pstackbase");
+ PEdge **stackbase = (PEdge **)MEM_mallocN(sizeof(*stackbase) * phash_size(handle->hash_faces),
+ "Pstackbase");
PEdge **stack = stackbase;
PFace *f, *first;
PEdge *e, *e1, *e2;
@@ -897,14 +936,14 @@ static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs)
return ncharts;
}
-static void p_split_vert(PChart *chart, PEdge *e)
+static void p_split_vert(ParamHandle *handle, PChart *chart, PEdge *e)
{
- PEdge *we, *lastwe = NULL;
+ PEdge *we, *lastwe = nullptr;
PVert *v = e->vert;
bool copy = true;
if (e->flag & PEDGE_PIN) {
- chart->flag |= PCHART_HAS_PINS;
+ chart->has_pins = true;
}
if (e->flag & PEDGE_VERTEX_SPLIT) {
@@ -937,7 +976,7 @@ static void p_split_vert(PChart *chart, PEdge *e)
if (copy) {
/* not found, copying */
v->flag |= PVERT_SPLIT;
- v = p_vert_copy(chart, v);
+ v = p_vert_copy(handle, v);
v->flag |= PVERT_SPLIT;
v->nextlink = chart->verts;
@@ -956,20 +995,18 @@ static void p_split_vert(PChart *chart, PEdge *e)
static PChart **p_split_charts(ParamHandle *handle, PChart *chart, int ncharts)
{
- PChart **charts = MEM_mallocN(sizeof(*charts) * ncharts, "PCharts"), *nchart;
- PFace *f, *nextf;
- int i;
+ PChart **charts = (PChart **)MEM_callocN(sizeof(*charts) * ncharts, "PCharts");
- for (i = 0; i < ncharts; i++) {
- charts[i] = p_chart_new(handle);
+ for (int i = 0; i < ncharts; i++) {
+ charts[i] = (PChart *)MEM_callocN(sizeof(*chart), "PChart");
}
- f = chart->faces;
+ PFace *f = chart->faces;
while (f) {
PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
- nextf = f->nextlink;
+ PFace *nextf = f->nextlink;
- nchart = charts[f->u.chart];
+ PChart *nchart = charts[f->u.chart];
f->nextlink = nchart->faces;
nchart->faces = f;
@@ -983,9 +1020,9 @@ static PChart **p_split_charts(ParamHandle *handle, PChart *chart, int ncharts)
nchart->nfaces++;
nchart->nedges += 3;
- p_split_vert(nchart, e1);
- p_split_vert(nchart, e2);
- p_split_vert(nchart, e3);
+ p_split_vert(handle, nchart, e1);
+ p_split_vert(handle, nchart, e2);
+ p_split_vert(handle, nchart, e3);
f = nextf;
}
@@ -1014,9 +1051,9 @@ static PFace *p_face_add(ParamHandle *handle)
e2->next = e3;
e3->next = e1;
- e1->pair = NULL;
- e2->pair = NULL;
- e3->pair = NULL;
+ e1->pair = nullptr;
+ e2->pair = nullptr;
+ e3->pair = nullptr;
e1->flag = 0;
e2->flag = 0;
@@ -1085,16 +1122,16 @@ static PFace *p_face_add_construct(ParamHandle *handle,
return f;
}
-static PFace *p_face_add_fill(PChart *chart, PVert *v1, PVert *v2, PVert *v3)
+static PFace *p_face_add_fill(ParamHandle *handle, PChart *chart, PVert *v1, PVert *v2, PVert *v3)
{
- PFace *f = p_face_add(chart->handle);
+ PFace *f = p_face_add(handle);
PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
e1->vert = v1;
e2->vert = v2;
e3->vert = v3;
- e1->orig_uv = e2->orig_uv = e3->orig_uv = NULL;
+ e1->orig_uv = e2->orig_uv = e3->orig_uv = nullptr;
f->nextlink = chart->faces;
chart->faces = f;
@@ -1146,7 +1183,7 @@ static void p_chart_boundaries(PChart *chart, PEdge **r_outer)
chart->nboundaries = 0;
if (r_outer) {
- *r_outer = NULL;
+ *r_outer = nullptr;
}
for (e = chart->edges; e; e = e->nextlink) {
@@ -1201,7 +1238,7 @@ static float p_edge_boundary_angle(PEdge *e)
return angle;
}
-static void p_chart_fill_boundary(PChart *chart, PEdge *be, int nedges)
+static void p_chart_fill_boundary(ParamHandle *handle, PChart *chart, PEdge *be, int nedges)
{
PEdge *e, *e1, *e2;
@@ -1237,12 +1274,12 @@ static void p_chart_fill_boundary(PChart *chart, PEdge *be, int nedges)
BLI_heap_remove(heap, e1->u.heaplink);
BLI_heap_remove(heap, e2->u.heaplink);
- e->u.heaplink = e1->u.heaplink = e2->u.heaplink = NULL;
+ e->u.heaplink = e1->u.heaplink = e2->u.heaplink = nullptr;
e->flag |= PEDGE_FILLED;
e1->flag |= PEDGE_FILLED;
- f = p_face_add_fill(chart, e->vert, e1->vert, e2->vert);
+ f = p_face_add_fill(handle, chart, e->vert, e1->vert, e2->vert);
f->flag |= PFACE_FILLED;
ne = f->edge->next->next;
@@ -1275,10 +1312,10 @@ static void p_chart_fill_boundary(PChart *chart, PEdge *be, int nedges)
}
}
- BLI_heap_free(heap, NULL);
+ BLI_heap_free(heap, nullptr);
}
-static void p_chart_fill_boundaries(PChart *chart, PEdge *outer)
+static void p_chart_fill_boundaries(ParamHandle *handle, PChart *chart, PEdge *outer)
{
PEdge *e, *be; /* *enext - as yet unused */
int nedges;
@@ -1299,7 +1336,7 @@ static void p_chart_fill_boundaries(PChart *chart, PEdge *outer)
} while (be != e);
if (e != outer) {
- p_chart_fill_boundary(chart, e, nedges);
+ p_chart_fill_boundary(handle, chart, e, nedges);
}
}
}
@@ -1558,7 +1595,7 @@ static void p_vert_harmonic_insert(PVert *v)
e = p_wheel_edge_next(e);
} while (e && (e != v->edge));
- if (e == NULL) {
+ if (e == nullptr) {
npoints++;
}
@@ -1572,7 +1609,7 @@ static void p_vert_harmonic_insert(PVert *v)
points[i][0] = e->next->vert->uv[0];
points[i][1] = e->next->vert->uv[1];
- if (nexte == NULL) {
+ if (nexte == nullptr) {
i++;
points[i][0] = e->next->next->vert->uv[0];
points[i][1] = e->next->next->vert->uv[1];
@@ -1916,8 +1953,8 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair)
int nshapeold = 0, nshapenew = 0;
p_collapsing_verts(edge, pair, &oldv, &keepv);
- oldf1 = (edge) ? edge->face : NULL;
- oldf2 = (pair) ? pair->face : NULL;
+ oldf1 = (edge) ? edge->face : nullptr;
+ oldf2 = (pair) ? pair->face : nullptr;
sub_v3_v3v3(edgevec, keepv->co, oldv->co);
@@ -1938,7 +1975,7 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair)
# if 0
shapecost += dot_v3v3(co1, keepv->co);
- if (p_wheel_edge_next(e) == NULL) {
+ if (p_wheel_edge_next(e) == nullptr) {
shapecost += dot_v3v3(co2, keepv->co);
}
# endif
@@ -1991,14 +2028,14 @@ static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine
{
PEdge *e, *enext, *pair;
- *r_mine = NULL;
+ *r_mine = nullptr;
*r_mincost = 0.0f;
e = vert->edge;
do {
if (p_collapse_allowed(e, e->pair)) {
float cost = p_collapse_cost(e, e->pair);
- if ((*r_mine == NULL) || (cost < *r_mincost)) {
+ if ((*r_mine == nullptr) || (cost < *r_mincost)) {
*r_mincost = cost;
*r_mine = e;
}
@@ -2006,14 +2043,14 @@ static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine
enext = p_wheel_edge_next(e);
- if (enext == NULL) {
+ if (enext == nullptr) {
/* The other boundary edge, where we only have the pair half-edge. */
pair = e->next->next;
- if (p_collapse_allowed(NULL, pair)) {
- float cost = p_collapse_cost(NULL, pair);
+ if (p_collapse_allowed(nullptr, pair)) {
+ float cost = p_collapse_cost(nullptr, pair);
- if ((*r_mine == NULL) || (cost < *r_mincost)) {
+ if ((*r_mine == nullptr) || (cost < *r_mincost)) {
*r_mincost = cost;
*r_mine = pair;
}
@@ -2030,13 +2067,13 @@ static void p_chart_post_collapse_flush(PChart *chart, PEdge *collapsed)
{
/* Move to `collapsed_*`. */
- PVert *v, *nextv = NULL, *verts = chart->verts;
- PEdge *e, *nexte = NULL, *edges = chart->edges, *laste = NULL;
- PFace *f, *nextf = NULL, *faces = chart->faces;
+ PVert *v, *nextv = nullptr, *verts = chart->verts;
+ PEdge *e, *nexte = nullptr, *edges = chart->edges, *laste = nullptr;
+ PFace *f, *nextf = nullptr, *faces = chart->faces;
- chart->verts = chart->collapsed_verts = NULL;
- chart->edges = chart->collapsed_edges = NULL;
- chart->faces = chart->collapsed_faces = NULL;
+ chart->verts = chart->collapsed_verts = nullptr;
+ chart->edges = chart->collapsed_edges = nullptr;
+ chart->faces = chart->collapsed_faces = nullptr;
chart->nverts = chart->nedges = chart->nfaces = 0;
@@ -2100,9 +2137,9 @@ static void p_chart_post_split_flush(PChart *chart)
{
/* Move from `collapsed_*`. */
- PVert *v, *nextv = NULL;
- PEdge *e, *nexte = NULL;
- PFace *f, *nextf = NULL;
+ PVert *v, *nextv = nullptr;
+ PEdge *e, *nexte = nullptr;
+ PFace *f, *nextf = nullptr;
for (v = chart->collapsed_verts; v; v = nextv) {
nextv = v->nextlink;
@@ -2125,9 +2162,9 @@ static void p_chart_post_split_flush(PChart *chart)
chart->nfaces++;
}
- chart->collapsed_verts = NULL;
- chart->collapsed_edges = NULL;
- chart->collapsed_faces = NULL;
+ chart->collapsed_verts = nullptr;
+ chart->collapsed_edges = nullptr;
+ chart->collapsed_faces = nullptr;
}
static void p_chart_simplify_compute(PChart *chart)
@@ -2139,7 +2176,7 @@ static void p_chart_simplify_compute(PChart *chart)
Heap *heap = BLI_heap_new();
PVert *v, **wheelverts;
- PEdge *collapsededges = NULL, *e;
+ PEdge *collapsededges = nullptr, *e;
int nwheelverts, i, ncollapsed = 0;
wheelverts = MEM_mallocN(sizeof(PVert *) * chart->nverts, "PChartWheelVerts");
@@ -2147,7 +2184,7 @@ static void p_chart_simplify_compute(PChart *chart)
/* insert all potential collapses into heap */
for (v = chart->verts; v; v = v->nextlink) {
float cost;
- PEdge *e = NULL;
+ PEdge *e = nullptr;
p_collapse_cost_vertex(v, &cost, &e);
@@ -2155,12 +2192,12 @@ static void p_chart_simplify_compute(PChart *chart)
v->u.heaplink = BLI_heap_insert(heap, cost, e);
}
else {
- v->u.heaplink = NULL;
+ v->u.heaplink = nullptr;
}
}
for (e = chart->edges; e; e = e->nextlink) {
- e->u.nextcollapse = NULL;
+ e->u.nextcollapse = nullptr;
}
/* pop edge collapse out of heap one by one */
@@ -2180,12 +2217,12 @@ static void p_chart_simplify_compute(PChart *chart)
if (edge->vert->u.heaplink != link) {
edge->flag |= (PEDGE_COLLAPSE_EDGE | PEDGE_COLLAPSE_PAIR);
- edge->next->vert->u.heaplink = NULL;
+ edge->next->vert->u.heaplink = nullptr;
SWAP(PEdge *, edge, pair);
}
else {
edge->flag |= PEDGE_COLLAPSE_EDGE;
- edge->vert->u.heaplink = NULL;
+ edge->vert->u.heaplink = nullptr;
}
p_collapsing_verts(edge, pair, &oldv, &keepv);
@@ -2198,7 +2235,7 @@ static void p_chart_simplify_compute(PChart *chart)
wheelverts[nwheelverts++] = wheele->next->vert;
nexte = p_wheel_edge_next(wheele);
- if (nexte == NULL) {
+ if (nexte == nullptr) {
wheelverts[nwheelverts++] = wheele->next->next->vert;
}
@@ -2210,13 +2247,13 @@ static void p_chart_simplify_compute(PChart *chart)
for (i = 0; i < nwheelverts; i++) {
float cost;
- PEdge *collapse = NULL;
+ PEdge *collapse = nullptr;
v = wheelverts[i];
if (v->u.heaplink) {
BLI_heap_remove(heap, v->u.heaplink);
- v->u.heaplink = NULL;
+ v->u.heaplink = nullptr;
}
p_collapse_cost_vertex(v, &cost, &collapse);
@@ -2230,7 +2267,7 @@ static void p_chart_simplify_compute(PChart *chart)
}
MEM_freeN(wheelverts);
- BLI_heap_free(heap, NULL);
+ BLI_heap_free(heap, nullptr);
p_chart_post_collapse_flush(chart, collapsededges);
}
@@ -2281,14 +2318,13 @@ static void p_chart_simplify(PChart *chart)
#define ABF_MAX_ITER 20
-typedef struct PAbfSystem {
+using PAbfSystem = struct PAbfSystem {
int ninterior, nfaces, nangles;
float *alpha, *beta, *sine, *cosine, *weight;
float *bAlpha, *bTriangle, *bInterior;
float *lambdaTriangle, *lambdaPlanar, *lambdaLength;
float (*J2dt)[3], *bstar, *dstar;
- float minangle, maxangle;
-} PAbfSystem;
+};
static void p_abf_setup_system(PAbfSystem *sys)
{
@@ -2308,16 +2344,13 @@ static void p_abf_setup_system(PAbfSystem *sys)
sys->lambdaPlanar = (float *)MEM_callocN(sizeof(float) * sys->ninterior, "ABFlamdaplane");
sys->lambdaLength = (float *)MEM_mallocN(sizeof(float) * sys->ninterior, "ABFlambdalen");
- sys->J2dt = MEM_mallocN(sizeof(float) * sys->nangles * 3, "ABFj2dt");
+ sys->J2dt = static_cast<float(*)[3]>(MEM_mallocN(sizeof(float) * sys->nangles * 3, "ABFj2dt"));
sys->bstar = (float *)MEM_mallocN(sizeof(float) * sys->nfaces, "ABFbstar");
sys->dstar = (float *)MEM_mallocN(sizeof(float) * sys->nfaces, "ABFdstar");
for (i = 0; i < sys->ninterior; i++) {
sys->lambdaLength[i] = 1.0;
}
-
- sys->minangle = 1.0 * M_PI / 180.0;
- sys->maxangle = (float)M_PI - sys->minangle;
}
static void p_abf_free_system(PAbfSystem *sys)
@@ -2728,25 +2761,6 @@ static bool p_chart_abf_solve(PChart *chart)
e3 = e2->next;
p_face_angles(f, &a1, &a2, &a3);
- if (a1 < sys.minangle) {
- a1 = sys.minangle;
- }
- else if (a1 > sys.maxangle) {
- a1 = sys.maxangle;
- }
- if (a2 < sys.minangle) {
- a2 = sys.minangle;
- }
- else if (a2 > sys.maxangle) {
- a2 = sys.maxangle;
- }
- if (a3 < sys.minangle) {
- a3 = sys.minangle;
- }
- else if (a3 > sys.maxangle) {
- a3 = sys.maxangle;
- }
-
sys.alpha[e1->u.id] = sys.beta[e1->u.id] = a1;
sys.alpha[e2->u.id] = sys.beta[e2->u.id] = a2;
sys.alpha[e3->u.id] = sys.beta[e3->u.id] = a3;
@@ -2807,7 +2821,7 @@ static bool p_chart_abf_solve(PChart *chart)
}
}
- chart->u.lscm.abf_alpha = MEM_dupallocN(sys.alpha);
+ chart->u.lscm.abf_alpha = (float *)MEM_dupallocN(sys.alpha);
p_abf_free_system(&sys);
return true;
@@ -2868,8 +2882,8 @@ static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2)
static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVert **pin2)
{
- PEdge *be, *lastbe = NULL, *maxe1 = NULL, *maxe2 = NULL, *be1, *be2;
- PEdge *cure = NULL, *firste1 = NULL, *firste2 = NULL, *nextbe;
+ PEdge *be, *lastbe = nullptr, *maxe1 = nullptr, *maxe2 = nullptr, *be1, *be2;
+ PEdge *cure = nullptr, *firste1 = nullptr, *firste2 = nullptr, *nextbe;
float maxlen = 0.0f, curlen = 0.0f, totlen = 0.0f, firstlen = 0.0f;
float len1, len2;
@@ -2908,7 +2922,7 @@ static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVe
}
curlen = 0.0f;
- cure = NULL;
+ cure = nullptr;
}
lastbe = be;
@@ -2983,8 +2997,8 @@ static void p_chart_extrema_verts(PChart *chart, PVert **pin1, PVert **pin2)
minv[0] = minv[1] = minv[2] = 1e20;
maxv[0] = maxv[1] = maxv[2] = -1e20;
- minvert[0] = minvert[1] = minvert[2] = NULL;
- maxvert[0] = maxvert[1] = maxvert[2] = NULL;
+ minvert[0] = minvert[1] = minvert[2] = nullptr;
+ maxvert[0] = maxvert[1] = maxvert[2] = nullptr;
for (v = chart->verts; v; v = v->nextlink) {
for (i = 0; i < 3; i++) {
@@ -3048,7 +3062,7 @@ static void p_chart_lscm_begin(PChart *chart, bool live, bool abf)
}
if ((live && (!select || !deselect))) {
- chart->u.lscm.context = NULL;
+ chart->u.lscm.context = nullptr;
}
else {
#if 0
@@ -3265,16 +3279,14 @@ static void p_chart_lscm_transform_single_pin(PChart *chart)
static void p_chart_lscm_end(PChart *chart)
{
- if (chart->u.lscm.context) {
- EIG_linear_solver_delete(chart->u.lscm.context);
- }
+ EIG_linear_solver_delete(chart->u.lscm.context);
+ chart->u.lscm.context = nullptr;
MEM_SAFE_FREE(chart->u.lscm.abf_alpha);
- chart->u.lscm.context = NULL;
- chart->u.lscm.pin1 = NULL;
- chart->u.lscm.pin2 = NULL;
- chart->u.lscm.single_pin = NULL;
+ chart->u.lscm.pin1 = nullptr;
+ chart->u.lscm.pin2 = nullptr;
+ chart->u.lscm.single_pin = nullptr;
chart->u.lscm.single_pin_area = 0.0f;
}
@@ -3287,7 +3299,7 @@ static void p_stretch_pin_boundary(PChart *chart)
PVert *v;
for (v = chart->verts; v; v = v->nextlink) {
- if (v->edge->pair == NULL) {
+ if (v->edge->pair == nullptr) {
v->flag |= PVERT_PIN;
}
else {
@@ -3306,8 +3318,10 @@ static float p_face_stretch(PFace *f)
area = p_face_uv_area_signed(f);
- if (area <= 0.0f) { /* flipped face -> infinite stretch */
- return 1e10f;
+ if (area <= 0.0f) {
+ /* When a face is flipped, provide a large penalty.
+ * Add on a slight gradient to unflip the face, see also: T99781. */
+ return 1e8f * (1.0f + p_edge_uv_length(e1) + p_edge_uv_length(e2) + p_edge_uv_length(e3));
}
w = 1.0f / (2.0f * area);
@@ -3563,7 +3577,7 @@ static float p_chart_minimum_area_angle(PChart *chart)
}
/* find left/top/right/bottom points, and compute angle for each point */
- angles = MEM_mallocN(sizeof(float) * npoints, "PMinAreaAngles");
+ angles = (float *)MEM_mallocN(sizeof(float) * npoints, "PMinAreaAngles");
i_min = i_max = 0;
miny = 1e10;
@@ -3687,7 +3701,8 @@ static void p_chart_rotate_minimum_area(PChart *chart)
static void p_chart_rotate_fit_aabb(PChart *chart)
{
- float(*points)[2] = MEM_mallocN(sizeof(*points) * chart->nverts, __func__);
+ float(*points)[2] = static_cast<float(*)[2]>(
+ MEM_mallocN(sizeof(*points) * chart->nverts, __func__));
p_chart_uv_to_array(chart, points);
@@ -3704,10 +3719,10 @@ static void p_chart_rotate_fit_aabb(PChart *chart)
/* Exported */
-ParamHandle *GEO_uv_parametrizer_construct_begin(void)
+ParamHandle *GEO_uv_parametrizer_construct_begin()
{
- ParamHandle *handle = MEM_callocN(sizeof(*handle), "ParamHandle");
- handle->construction_chart = p_chart_new(handle);
+ ParamHandle *handle = new ParamHandle();
+ handle->construction_chart = (PChart *)MEM_callocN(sizeof(PChart), "PChart");
handle->state = PHANDLE_STATE_ALLOCATED;
handle->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "param construct arena");
handle->polyfill_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "param polyfill arena");
@@ -3730,40 +3745,45 @@ void GEO_uv_parametrizer_aspect_ratio(ParamHandle *phandle, float aspx, float as
void GEO_uv_parametrizer_delete(ParamHandle *phandle)
{
- int i;
-
+ if (!phandle) {
+ return;
+ }
param_assert(ELEM(phandle->state, PHANDLE_STATE_ALLOCATED, PHANDLE_STATE_CONSTRUCTED));
- for (i = 0; i < phandle->ncharts; i++) {
- p_chart_delete(phandle->charts[i]);
+ for (int i = 0; i < phandle->ncharts; i++) {
+ MEM_SAFE_FREE(phandle->charts[i]);
}
MEM_SAFE_FREE(phandle->charts);
if (phandle->pin_hash) {
- BLI_ghash_free(phandle->pin_hash, NULL, NULL);
- phandle->pin_hash = NULL;
+ BLI_ghash_free(phandle->pin_hash, nullptr, nullptr);
+ phandle->pin_hash = nullptr;
}
- if (phandle->construction_chart) {
- p_chart_delete(phandle->construction_chart);
+ MEM_SAFE_FREE(phandle->construction_chart);
- phash_delete(phandle->hash_verts);
- phash_delete(phandle->hash_edges);
- phash_delete(phandle->hash_faces);
- }
+ phash_delete(phandle->hash_verts);
+ phash_delete(phandle->hash_edges);
+ phash_delete(phandle->hash_faces);
BLI_memarena_free(phandle->arena);
BLI_memarena_free(phandle->polyfill_arena);
- BLI_heap_free(phandle->polyfill_heap, NULL);
- MEM_freeN(phandle);
+ BLI_heap_free(phandle->polyfill_heap, nullptr);
+
+ if (phandle->rng) {
+ BLI_rng_free(phandle->rng);
+ phandle->rng = nullptr;
+ }
+
+ delete phandle;
}
-typedef struct GeoUVPinIndex {
+using GeoUVPinIndex = struct GeoUVPinIndex {
struct GeoUVPinIndex *next;
float uv[2];
ParamKey reindex;
-} GeoUVPinIndex;
+};
/* Find a (mostly) unique ParamKey given a BMVert index and UV co-ordinates.
* For each unique pinned UVs, return a unique ParamKey, starting with
@@ -3779,7 +3799,8 @@ ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const
return bmvertindex; /* No verts pinned. */
}
- GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, POINTER_FROM_INT(bmvertindex));
+ const GeoUVPinIndex *pinuvlist = (const GeoUVPinIndex *)BLI_ghash_lookup(
+ handle->pin_hash, POINTER_FROM_INT(bmvertindex));
if (!pinuvlist) {
return bmvertindex; /* Vert not pinned. */
}
@@ -3801,8 +3822,8 @@ ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const
static GeoUVPinIndex *new_geo_uv_pinindex(ParamHandle *handle, const float uv[2])
{
- GeoUVPinIndex *pinuv = BLI_memarena_alloc(handle->arena, sizeof(*pinuv));
- pinuv->next = NULL;
+ GeoUVPinIndex *pinuv = (GeoUVPinIndex *)BLI_memarena_alloc(handle->arena, sizeof(*pinuv));
+ pinuv->next = nullptr;
copy_v2_v2(pinuv->uv, uv);
pinuv->reindex = PARAM_KEY_MAX - (handle->unique_pin_count++);
return pinuv;
@@ -3814,7 +3835,8 @@ void GEO_uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const
handle->pin_hash = BLI_ghash_int_new("uv pin reindex");
}
- GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, POINTER_FROM_INT(bmvertindex));
+ GeoUVPinIndex *pinuvlist = (GeoUVPinIndex *)BLI_ghash_lookup(handle->pin_hash,
+ POINTER_FROM_INT(bmvertindex));
if (!pinuvlist) {
BLI_ghash_insert(
handle->pin_hash, POINTER_FROM_INT(bmvertindex), new_geo_uv_pinindex(handle, uv));
@@ -3846,8 +3868,10 @@ static void p_add_ngon(ParamHandle *handle,
MemArena *arena = handle->polyfill_arena;
Heap *heap = handle->polyfill_heap;
uint nfilltri = nverts - 2;
- uint(*tris)[3] = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri);
- float(*projverts)[2] = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts);
+ uint(*tris)[3] = static_cast<uint(*)[3]>(
+ BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri));
+ float(*projverts)[2] = static_cast<float(*)[2]>(
+ BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts));
/* Calc normal, flipped: to get a positive 2d cross product. */
float normal[3];
@@ -3876,7 +3900,7 @@ static void p_add_ngon(ParamHandle *handle,
BLI_polyfill_beautify(projverts, nverts, tris, arena, heap);
/* Add triangles. */
- for (int j = 0; j < nfilltri; j++) {
+ for (uint j = 0; j < nfilltri; j++) {
uint *tri = tris[j];
uint v0 = tri[0];
uint v1 = tri[1];
@@ -3903,7 +3927,7 @@ void GEO_uv_parametrizer_face_add(ParamHandle *phandle,
const bool *pin,
const bool *select)
{
- param_assert(phash_lookup(phandle->hash_faces, key) == NULL);
+ param_assert(phash_lookup(phandle->hash_faces, key) == nullptr);
param_assert(phandle->state == PHANDLE_STATE_ALLOCATED);
param_assert(ELEM(nverts, 3, 4));
@@ -3954,13 +3978,13 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle,
phandle->ncharts = p_connect_pairs(phandle, topology_from_uvs);
phandle->charts = p_split_charts(phandle, chart, phandle->ncharts);
- p_chart_delete(phandle->construction_chart);
- phandle->construction_chart = NULL;
+ MEM_freeN(phandle->construction_chart);
+ phandle->construction_chart = nullptr;
phash_delete(phandle->hash_verts);
phash_delete(phandle->hash_edges);
phash_delete(phandle->hash_faces);
- phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = NULL;
+ phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = nullptr;
for (i = j = 0; i < phandle->ncharts; i++) {
PVert *v;
@@ -3969,8 +3993,8 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle,
p_chart_boundaries(chart, &outer);
if (!topology_from_uvs && chart->nboundaries == 0) {
- p_chart_delete(chart);
- if (count_fail != NULL) {
+ MEM_freeN(chart);
+ if (count_fail != nullptr) {
*count_fail += 1;
}
continue;
@@ -3980,7 +4004,7 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle,
j++;
if (fill && (chart->nboundaries > 1)) {
- p_chart_fill_boundaries(chart, outer);
+ p_chart_fill_boundaries(phandle, chart, outer);
}
for (v = chart->verts; v; v = v->nextlink) {
@@ -4022,7 +4046,7 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, in
if (chart->u.lscm.context) {
const bool result = p_chart_lscm_solve(phandle, chart);
- if (result && !(chart->flag & PCHART_HAS_PINS)) {
+ if (result && !chart->has_pins) {
p_chart_rotate_minimum_area(chart);
}
else if (result && chart->u.lscm.single_pin) {
@@ -4030,17 +4054,17 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, in
p_chart_lscm_transform_single_pin(chart);
}
- if (!result || !(chart->flag & PCHART_HAS_PINS)) {
+ if (!result || !chart->has_pins) {
p_chart_lscm_end(chart);
}
if (result) {
- if (count_changed != NULL) {
+ if (count_changed != nullptr) {
*count_changed += 1;
}
}
else {
- if (count_failed != NULL) {
+ if (count_failed != nullptr) {
*count_failed += 1;
}
}
@@ -4050,11 +4074,9 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, in
void GEO_uv_parametrizer_lscm_end(ParamHandle *phandle)
{
- int i;
+ BLI_assert(phandle->state == PHANDLE_STATE_LSCM);
- param_assert(phandle->state == PHANDLE_STATE_LSCM);
-
- for (i = 0; i < phandle->ncharts; i++) {
+ for (int i = 0; i < phandle->ncharts; i++) {
p_chart_lscm_end(phandle->charts[i]);
#if 0
p_chart_complexify(phandle->charts[i]);
@@ -4116,9 +4138,6 @@ void GEO_uv_parametrizer_stretch_end(ParamHandle *phandle)
{
param_assert(phandle->state == PHANDLE_STATE_STRETCH);
phandle->state = PHANDLE_STATE_CONSTRUCTED;
-
- BLI_rng_free(phandle->rng);
- phandle->rng = NULL;
}
/* don't pack, just rotate (used for better packing) */
@@ -4130,7 +4149,7 @@ static void GEO_uv_parametrizer_pack_rotate(ParamHandle *phandle, bool ignore_pi
for (i = 0; i < phandle->ncharts; i++) {
chart = phandle->charts[i];
- if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) {
+ if (ignore_pinned && chart->has_pins) {
continue;
}
@@ -4166,12 +4185,12 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
}
/* we may not use all these boxes */
- boxarray = MEM_mallocN(handle->ncharts * sizeof(BoxPack), "BoxPack box");
+ boxarray = (BoxPack *)MEM_mallocN(handle->ncharts * sizeof(BoxPack), "BoxPack box");
for (i = 0; i < handle->ncharts; i++) {
chart = handle->charts[i];
- if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) {
+ if (ignore_pinned && chart->has_pins) {
unpacked++;
continue;
}
@@ -4187,7 +4206,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
box->w = chart->u.pack.size[0] + trans[0];
box->h = chart->u.pack.size[1] + trans[1];
- box->index = i; /* warning this index skips PCHART_HAS_PINS boxes */
+ box->index = i; /* Warning this index skips chart->has_pins boxes. */
if (margin > 0.0f) {
area += (double)sqrtf(box->w * box->h);
@@ -4204,7 +4223,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
for (i = 0; i < handle->ncharts; i++) {
chart = handle->charts[i];
- if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) {
+ if (ignore_pinned && chart->has_pins) {
unpacked++;
continue;
}
@@ -4243,7 +4262,10 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle,
}
}
-void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
+void GEO_uv_parametrizer_average(ParamHandle *phandle,
+ bool ignore_pinned,
+ bool scale_uv,
+ bool shear)
{
PChart *chart;
int i;
@@ -4256,17 +4278,93 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
}
for (i = 0; i < phandle->ncharts; i++) {
- PFace *f;
chart = phandle->charts[i];
- if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) {
+ if (ignore_pinned && chart->has_pins) {
continue;
}
+ p_chart_uv_bbox(chart, minv, maxv);
+ mid_v2_v2v2(chart->u.pack.origin, minv, maxv);
+
+ if (scale_uv || shear) {
+ /* It's possible that for some "bad" inputs, the following iteration will converge slowly or
+ * perhaps even diverge. Rather than infinite loop, we only iterate a maximum of `max_iter`
+ * times. (Also useful when making changes to the calculation.) */
+ int max_iter = 10;
+ for (int j = 0; j < max_iter; j++) {
+ /* An island could contain millions of polygons. When summing many small values, we need to
+ * use double precision in the accumulator to maintain accuracy. Note that the individual
+ * calculations only need to be at single precision. */
+ double scale_cou = 0;
+ double scale_cov = 0;
+ double scale_cross = 0;
+ double weight_sum = 0;
+ for (PFace *f = chart->faces; f; f = f->nextlink) {
+ float m[2][2], s[2][2];
+ PVert *va = f->edge->vert;
+ PVert *vb = f->edge->next->vert;
+ PVert *vc = f->edge->next->next->vert;
+ s[0][0] = va->uv[0] - vc->uv[0];
+ s[0][1] = va->uv[1] - vc->uv[1];
+ s[1][0] = vb->uv[0] - vc->uv[0];
+ s[1][1] = vb->uv[1] - vc->uv[1];
+ /* Find the "U" axis and "V" axis in triangle co-ordinates. Normally this would require
+ * SVD, but in 2D we can use a cheaper matrix inversion instead. */
+ if (!invert_m2_m2(m, s)) {
+ continue;
+ }
+ float cou[3], cov[3]; /* i.e. Texture "U" and texture "V" in 3D co-ordinates. */
+ for (int k = 0; k < 3; k++) {
+ cou[k] = m[0][0] * (va->co[k] - vc->co[k]) + m[0][1] * (vb->co[k] - vc->co[k]);
+ cov[k] = m[1][0] * (va->co[k] - vc->co[k]) + m[1][1] * (vb->co[k] - vc->co[k]);
+ }
+ const float weight = p_face_area(f);
+ scale_cou += len_v3(cou) * weight;
+ scale_cov += len_v3(cov) * weight;
+ if (shear) {
+ normalize_v3(cov);
+ normalize_v3(cou);
+
+ /* Why is scale_cross called `cross` when we call `dot`? The next line calculates:
+ * `scale_cross += length(cross(cross(cou, face_normal), cov))`
+ * By construction, both `cou` and `cov` are orthogonal to the face normal.
+ * By definition, the normal vector has unit length. */
+ scale_cross += dot_v3v3(cou, cov) * weight;
+ }
+ weight_sum += weight;
+ }
+ if (scale_cou * scale_cov < 1e-10f) {
+ break;
+ }
+ const float scale_factor_u = scale_uv ? sqrtf(scale_cou / scale_cov) : 1.0f;
+
+ /* Compute correction transform. */
+ float t[2][2];
+ t[0][0] = scale_factor_u;
+ t[1][0] = clamp_f((float)(scale_cross / weight_sum), -0.5f, 0.5f);
+ t[0][1] = 0;
+ t[1][1] = 1.0f / scale_factor_u;
+
+ /* Apply the correction. */
+ p_chart_uv_transform(chart, t);
+
+ /* How far from the identity transform are we? [[1,0],[0,1]] */
+ const float err = fabsf(t[0][0] - 1.0f) + fabsf(t[1][0]) + fabsf(t[0][1]) +
+ fabsf(t[1][1] - 1.0f);
+
+ const float tolerance = 1e-6f; /* Trade accuracy for performance. */
+ if (err < tolerance) {
+ /* Too slow? Use Richardson Extrapolation to accelerate the convergence. */
+ break;
+ }
+ }
+ }
+
chart->u.pack.area = 0.0f; /* 3d area */
chart->u.pack.rescale = 0.0f; /* UV area, abusing rescale for tmp storage, oh well :/ */
- for (f = chart->faces; f; f = f->nextlink) {
+ for (PFace *f = chart->faces; f; f = f->nextlink) {
chart->u.pack.area += p_face_area(f);
chart->u.pack.rescale += fabsf(p_face_uv_area_signed(f));
}
@@ -4285,25 +4383,23 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, bool ignore_pinned)
for (i = 0; i < phandle->ncharts; i++) {
chart = phandle->charts[i];
- if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) {
+ if (ignore_pinned && chart->has_pins) {
continue;
}
if (chart->u.pack.area != 0.0f && chart->u.pack.rescale != 0.0f) {
fac = chart->u.pack.area / chart->u.pack.rescale;
- /* Get the island center */
- p_chart_uv_bbox(chart, minv, maxv);
- trans[0] = (minv[0] + maxv[0]) / -2.0f;
- trans[1] = (minv[1] + maxv[1]) / -2.0f;
-
- /* Move center to 0,0 */
- p_chart_uv_translate(chart, trans);
+ /* Average scale. */
p_chart_uv_scale(chart, sqrtf(fac / tot_fac));
- /* Move to original center */
- trans[0] = -trans[0];
- trans[1] = -trans[1];
+ /* Get the current island center. */
+ p_chart_uv_bbox(chart, minv, maxv);
+
+ /* Move to original center. */
+ mid_v2_v2v2(trans, minv, maxv);
+ negate_v2(trans);
+ add_v2_v2(trans, chart->u.pack.origin);
p_chart_uv_translate(chart, trans);
}
}
diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt
index 947fc32f8c0..5ef9ae1bbc6 100644
--- a/source/blender/gpencil_modifiers/CMakeLists.txt
+++ b/source/blender/gpencil_modifiers/CMakeLists.txt
@@ -94,8 +94,6 @@ endif()
set(LIB
)
-add_definitions(${GL_DEFINITIONS})
-
blender_add_lib(bf_gpencil_modifiers "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
add_dependencies(bf_gpencil_modifiers bf_dna)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
index 092253dde79..d99d9950efb 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
@@ -191,7 +191,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Armature = {
- /* name */ "Armature",
+ /* name */ N_("Armature"),
/* structName */ "ArmatureGpencilModifierData",
/* structSize */ sizeof(ArmatureGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
index 407055c6165..8bb61136cc2 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
@@ -447,7 +447,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Array = {
- /* name */ "Array",
+ /* name */ N_("Array"),
/* structName */ "ArrayGpencilModifierData",
/* structSize */ sizeof(ArrayGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
index cf390e8d3a0..3c971ec6af0 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
@@ -877,7 +877,7 @@ static void updateDepsgraph(GpencilModifierData *md,
/* ******************************************** */
GpencilModifierTypeInfo modifierType_Gpencil_Build = {
- /* name */ "Build",
+ /* name */ N_("Build"),
/* structName */ "BuildGpencilModifierData",
/* structSize */ sizeof(BuildGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
index 8ada66cfd55..7abbd0661cf 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
@@ -12,6 +12,8 @@
#include "BLI_math_color.h"
#include "BLI_math_vector.h"
+#include "BLT_translation.h"
+
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -202,7 +204,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Color = {
- /* name */ "Hue/Saturation",
+ /* name */ N_("Hue/Saturation"),
/* structName */ "ColorGpencilModifierData",
/* structSize */ sizeof(ColorGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
index 41015c429fe..68a4b39a21e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c
@@ -270,7 +270,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk,
}
static void segment_list_item(struct uiList *UNUSED(ui_list),
- struct bContext *UNUSED(C),
+ const struct bContext *UNUSED(C),
struct uiLayout *layout,
struct PointerRNA *UNUSED(idataptr),
struct PointerRNA *itemptr,
@@ -363,7 +363,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Dash = {
- /* name */ "Dot Dash",
+ /* name */ N_("Dot Dash"),
/* structName */ "DashGpencilModifierData",
/* structSize */ sizeof(DashGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
index 0f1652a5620..ec0ed1f6a35 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilenvelope.c
@@ -645,7 +645,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Envelope = {
- /* name */ "Envelope",
+ /* name */ N_("Envelope"),
/* structName */ "EnvelopeGpencilModifierData",
/* structSize */ sizeof(EnvelopeGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
index 652894af017..1a2bfebdc55 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
@@ -387,7 +387,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Hook = {
- /* name */ "Hook",
+ /* name */ N_("Hook"),
/* structName */ "HookGpencilModifierData",
/* structSize */ sizeof(HookGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
index 30f1a004338..7365c59dd5f 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
@@ -247,7 +247,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Lattice = {
- /* name */ "Lattice",
+ /* name */ N_("Lattice"),
/* structName */ "LatticeGpencilModifierData",
/* structSize */ sizeof(LatticeGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
index 5a358f5b3a1..c1a978162b8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
@@ -355,7 +355,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Length = {
- /* name */ "Length",
+ /* name */ N_("Length"),
/* structName */ "LengthGpencilModifierData",
/* structSize */ sizeof(LengthGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index 4f00b997902..018a13a1c48 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -373,19 +373,16 @@ static void edge_types_panel_draw(const bContext *UNUSED(C), Panel *panel)
}
sub = uiLayoutRow(col, false);
- uiItemR(sub, ptr, "use_crease", 0, "", ICON_NONE);
- entry = uiLayoutColumn(sub, false);
- uiItemL(entry, IFACE_("Crease"), ICON_NONE);
- uiLayoutSetActive(entry, RNA_boolean_get(ptr, "use_crease") || is_first);
if (use_cache && !is_first) {
- uiItemL(entry, IFACE_("Crease Angle Cached"), ICON_INFO);
+ uiItemR(sub, ptr, "use_crease", 0, IFACE_("Crease (Angle Cached)"), ICON_NONE);
}
else {
- uiItemR(entry,
+ uiItemR(sub, ptr, "use_crease", 0, "", ICON_NONE);
+ uiItemR(sub,
ptr,
"crease_threshold",
UI_ITEM_R_SLIDER | UI_ITEM_R_FORCE_BLANK_DECORATE,
- IFACE_("Default Angle"),
+ NULL,
ICON_NONE);
}
@@ -400,7 +397,12 @@ static void edge_types_panel_draw(const bContext *UNUSED(C), Panel *panel)
sub = uiLayoutRow(entry, false);
uiItemR(sub, ptr, "use_light_contour", 0, IFACE_("Light Contour"), ICON_NONE);
- uiItemR(entry, ptr, "use_shadow", 0, IFACE_("Cast Shadow"), ICON_NONE);
+ uiItemR(entry,
+ ptr,
+ "use_shadow",
+ 0,
+ CTX_IFACE_(BLT_I18NCONTEXT_ID_GPENCIL, "Cast Shadow"),
+ ICON_NONE);
uiItemL(layout, IFACE_("Options"), ICON_NONE);
@@ -445,10 +447,8 @@ static void options_light_reference_draw(const bContext *UNUSED(C), Panel *panel
uiItemR(remaining, ptr, "shadow_camera_size", 0, NULL, ICON_NONE);
uiLayout *col = uiLayoutColumn(remaining, true);
- uiItemR(col, ptr, "shadow_camera_near", 0, "Near", ICON_NONE);
- uiItemR(col, ptr, "shadow_camera_far", 0, "Far", ICON_NONE);
-
- uiItemR(layout, ptr, "use_shadow_enclosed_shapes", 0, IFACE_("Eclosed Shapes"), ICON_NONE);
+ uiItemR(col, ptr, "shadow_camera_near", 0, IFACE_("Near"), ICON_NONE);
+ uiItemR(col, ptr, "shadow_camera_far", 0, IFACE_("Far"), ICON_NONE);
}
static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
index 1d62e930caa..326e86091c5 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
@@ -23,6 +23,7 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
@@ -100,9 +101,11 @@ static void update_position(Object *ob, MirrorGpencilModifierData *mmd, bGPDstro
}
}
-static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf)
+static void generate_geometry(
+ GpencilModifierData *md, Object *ob, bGPDlayer *gpl, bGPDframe *gpf, const bool update)
{
MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md;
+ bGPdata *gpd = ob->data;
bGPDstroke *gps, *gps_new = NULL;
int tot_strokes;
int i;
@@ -129,6 +132,9 @@ static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gp
mmd->flag & GP_MIRROR_INVERT_MATERIAL)) {
gps_new = BKE_gpencil_stroke_duplicate(gps, true, true);
update_position(ob, mmd, gps_new, xi);
+ if (update) {
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new);
+ }
BLI_addtail(&gpf->strokes, gps_new);
}
}
@@ -147,7 +153,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec
if (gpf == NULL) {
continue;
}
- generate_geometry(md, ob, gpl, gpf);
+ generate_geometry(md, ob, gpl, gpf, false);
}
}
@@ -167,7 +173,7 @@ static void bakeModifier(Main *UNUSED(bmain),
BKE_scene_graph_update_for_newframe(depsgraph);
/* compute mirror effects on this frame */
- generate_geometry(md, ob, gpl, gpf);
+ generate_geometry(md, ob, gpl, gpf, true);
}
}
@@ -237,7 +243,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Mirror = {
- /* name */ "Mirror",
+ /* name */ N_("Mirror"),
/* structName */ "MirrorGpencilModifierData",
/* structSize */ sizeof(MirrorGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
index fd4eb5da835..1cf11a694ac 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
@@ -20,6 +20,8 @@
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
@@ -311,7 +313,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Multiply = {
- /* name */ "MultipleStrokes",
+ /* name */ N_("MultipleStrokes"),
/* structName */ "MultiplyGpencilModifierData",
/* structSize */ sizeof(MultiplyGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
index 253603aea67..4bbcbf775db 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
@@ -339,7 +339,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Noise = {
- /* name */ "Noise",
+ /* name */ N_("Noise"),
/* structName */ "NoiseGpencilModifierData",
/* structSize */ sizeof(NoiseGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
index ba88c83161f..8f0abd0a88e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
@@ -228,7 +228,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Offset = {
- /* name */ "Offset",
+ /* name */ N_("Offset"),
/* structName */ "OffsetGpencilModifierData",
/* structSize */ sizeof(OffsetGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
index 0bcbd71f029..98f26dbd597 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
@@ -279,7 +279,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Opacity = {
- /* name */ "Opacity",
+ /* name */ N_("Opacity"),
/* structName */ "OpacityGpencilModifierData",
/* structSize */ sizeof(OpacityGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
index 017287239ea..74b7efb1d04 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c
@@ -144,7 +144,7 @@ static void bakeModifier(Main *UNUSED(bmain),
MEM_SAFE_FREE(mmd->cache_data);
}
Object *ob_target = DEG_get_evaluated_object(depsgraph, mmd->target);
- Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false);
+ Mesh *target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
mmd->cache_data = MEM_callocN(sizeof(ShrinkwrapTreeData), __func__);
if (BKE_shrinkwrap_init_tree(
mmd->cache_data, target, mmd->shrink_type, mmd->shrink_mode, false)) {
@@ -202,7 +202,6 @@ static void updateDepsgraph(GpencilModifierData *md,
CustomData_MeshMasks mask = {0};
if (BKE_shrinkwrap_needs_normals(mmd->shrink_type, mmd->shrink_mode)) {
- mask.vmask |= CD_MASK_NORMAL;
mask.lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL;
}
@@ -225,7 +224,7 @@ static void updateDepsgraph(GpencilModifierData *md,
ctx->node, &mmd->aux_target->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY);
}
}
- DEG_add_modifier_to_transform_relation(ctx->node, "Shrinkwrap Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Shrinkwrap Modifier");
}
static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData)
@@ -307,7 +306,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Shrinkwrap = {
- /* name */ "Shrinkwrap",
+ /* name */ N_("Shrinkwrap"),
/* structName */ "ShrinkwrapGpencilModifierData",
/* structSize */ sizeof(ShrinkwrapGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
index 07350b73ce3..41de8d74257 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
@@ -11,6 +11,8 @@
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -157,7 +159,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Simplify = {
- /* name */ "Simplify",
+ /* name */ N_("Simplify"),
/* structName */ "SimplifyGpencilModifierData",
/* structSize */ sizeof(SimplifyGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
index 1992ebd1508..977be634490 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
@@ -203,7 +203,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Smooth = {
- /* name */ "Smooth",
+ /* name */ N_("Smooth"),
/* structName */ "SmoothGpencilModifierData",
/* structSize */ sizeof(SmoothGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
index 73f8dada971..b856f7d81dc 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
@@ -124,7 +124,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Subdiv = {
- /* name */ "Subdivide",
+ /* name */ N_("Subdivide"),
/* structName */ "SubdivGpencilModifierData",
/* structSize */ sizeof(SubdivGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
index 6ba7a934bff..16c8365153a 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
@@ -178,7 +178,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Texture = {
- /* name */ "TextureMapping",
+ /* name */ N_("TextureMapping"),
/* structName */ "TextureGpencilModifierData",
/* structSize */ sizeof(TextureGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
index 68fe15df898..81343fb5180 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
@@ -11,6 +11,8 @@
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -204,7 +206,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Thick = {
- /* name */ "Thickness",
+ /* name */ N_("Thickness"),
/* structName */ "ThickGpencilModifierData",
/* structSize */ sizeof(ThickGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
index 414231fcae5..1d45030b68b 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
@@ -230,7 +230,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Time = {
- /* name */ "TimeOffset",
+ /* name */ N_("TimeOffset"),
/* structName */ "TimeGpencilModifierData",
/* structSize */ sizeof(TimeGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
index ad8804782e8..94d8cb98290 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
@@ -12,6 +12,8 @@
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
+#include "BLT_translation.h"
+
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -365,7 +367,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_Tint = {
- /* name */ "Tint",
+ /* name */ N_("Tint"),
/* structName */ "TintGpencilModifierData",
/* structSize */ sizeof(TintGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c
index f40e6ba0728..3ecff4bf447 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c
@@ -11,6 +11,8 @@
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -209,7 +211,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_WeightAngle = {
- /* name */ "Vertex Weight Angle",
+ /* name */ N_("Vertex Weight Angle"),
/* structName */ "WeightAngleGpencilModifierData",
/* structSize */ sizeof(WeightAngleGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
index 1a6cadabf51..f64c83443d8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
@@ -11,6 +11,8 @@
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "DNA_defaults.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
@@ -226,7 +228,7 @@ static void panelRegister(ARegionType *region_type)
}
GpencilModifierTypeInfo modifierType_Gpencil_WeightProximity = {
- /* name */ "Vertex Weight Proximity",
+ /* name */ N_("Vertex Weight Proximity"),
/* structName */ "WeightProxGpencilModifierData",
/* structSize */ sizeof(WeightProxGpencilModifierData),
/* type */ eGpencilModifierTypeType_Gpencil,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
index 5dd833fb12b..22037d10a71 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h
@@ -84,6 +84,7 @@ typedef struct LineartElementLinkNode {
/* For edge element link nodes, used for shadow edge matching. */
int obindex;
+ int global_index_offset;
/** Per object value, always set, if not enabled by #ObjectLineArt, then it's set to global. */
float crease_threshold;
@@ -205,6 +206,10 @@ typedef struct LineartEdgeChain {
uint8_t intersection_mask;
uint32_t shadow_mask_bits;
+ /* We need local index for correct weight transfer, line art index is global, thus
+ * local_index=lineart_index-index_offset. */
+ uint32_t index_offset;
+
struct Object *object_ref;
struct Object *silhouette_backdrop;
} LineartEdgeChain;
@@ -366,8 +371,9 @@ typedef struct LineartData {
/* Keep an copy of these data so when line art is running it's self-contained. */
bool cam_is_persp;
- bool cam_is_persp_secondary; /* "Secondary" ones are from viewing camera (as opposed to shadow
- camera), during shadow calculation. */
+ /* "Secondary" ones are from viewing camera
+ * (as opposed to shadow camera), during shadow calculation. */
+ bool cam_is_persp_secondary;
float cam_obmat[4][4];
float cam_obmat_secondary[4][4];
double camera_pos[3];
@@ -436,15 +442,21 @@ typedef enum eLineartTriangleFlags {
LRT_TRIANGLE_INTERSECTION_ONLY = (1 << 3),
LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
LRT_TRIANGLE_MAT_BACK_FACE_CULLING = (1 << 5),
+ LRT_TRIANGLE_FORCE_INTERSECTION = (1 << 6),
} eLineartTriangleFlags;
#define LRT_SHADOW_MASK_UNDEFINED 0
-#define LRT_SHADOW_MASK_LIT (1 << 0)
+#define LRT_SHADOW_MASK_ILLUMINATED (1 << 0)
#define LRT_SHADOW_MASK_SHADED (1 << 1)
#define LRT_SHADOW_MASK_ENCLOSED_SHAPE (1 << 2)
#define LRT_SHADOW_MASK_INHIBITED (1 << 3)
#define LRT_SHADOW_SILHOUETTE_ERASED_GROUP (1 << 4)
#define LRT_SHADOW_SILHOUETTE_ERASED_OBJECT (1 << 5)
+#define LRT_SHADOW_MASK_ILLUMINATED_SHAPE (1 << 6)
+
+#define LRT_SHADOW_TEST_SHAPE_BITS \
+ (LRT_SHADOW_MASK_ILLUMINATED | LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_INHIBITED | \
+ LRT_SHADOW_MASK_ILLUMINATED_SHAPE)
/**
* Controls how many edges a worker thread is processing at one request.
@@ -477,6 +489,7 @@ typedef struct LineartRenderTaskInfo {
typedef struct LineartObjectInfo {
struct LineartObjectInfo *next;
struct Object *original_ob;
+ struct Object *original_ob_eval; /* For evaluated materials */
struct Mesh *original_me;
double model_view_proj[4][4];
double model_view[4][4];
@@ -858,6 +871,7 @@ void MOD_lineart_chain_find_silhouette_backdrop_objects(LineartData *ld);
int MOD_lineart_chain_count(const LineartEdgeChain *ec);
void MOD_lineart_chain_clear_picked_flag(LineartCache *lc);
+void MOD_lineart_finalize_chains(LineartData *ld);
/**
* This is the entry point of all line art calculations.
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
index 7c8e0c5a6f5..f32141a31eb 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c
@@ -1051,6 +1051,38 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc)
}
}
+LineartElementLinkNode *lineart_find_matching_eln_obj(ListBase *elns, struct Object *obj)
+{
+ LISTBASE_FOREACH (LineartElementLinkNode *, eln, elns) {
+ if (eln->object_ref == obj) {
+ return eln;
+ }
+ }
+ return NULL;
+}
+
+void MOD_lineart_finalize_chains(LineartData *ld)
+{
+ LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) {
+ if (ELEM(ec->type,
+ LRT_EDGE_FLAG_INTERSECTION,
+ LRT_EDGE_FLAG_PROJECTED_SHADOW,
+ LRT_EDGE_FLAG_LIGHT_CONTOUR)) {
+ continue;
+ }
+ LineartElementLinkNode *eln = lineart_find_matching_eln_obj(&ld->geom.vertex_buffer_pointers,
+ ec->object_ref);
+ BLI_assert(eln != NULL);
+ if (LIKELY(eln)) {
+ LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) {
+ if (eci->index > eln->global_index_offset) {
+ eci->index -= eln->global_index_offset;
+ }
+ }
+ }
+ }
+}
+
void MOD_lineart_smooth_chains(LineartData *ld, float tolerance)
{
LISTBASE_FOREACH (LineartEdgeChain *, ec, &ld->chains) {
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 874da3b88c9..9bab1f7a939 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -50,7 +50,7 @@
#include "lineart_intern.h"
typedef struct LineartIsecSingle {
- float v1[3], v2[3];
+ double v1[3], v2[3];
LineartTriangle *tri1, *tri2;
} LineartIsecSingle;
@@ -69,7 +69,7 @@ typedef struct LineartIsecThread {
int max;
int count_test;
- /* For individual thread reference.*/
+ /* For individual thread reference. */
LineartData *ld;
} LineartIsecThread;
@@ -304,13 +304,12 @@ void lineart_edge_cut(LineartData *ld,
/* The enclosed shape flag will override regular lit/shaded
* flags. See LineartEdgeSegment::shadow_mask_bits for details. */
if (shadow_bits == LRT_SHADOW_MASK_ENCLOSED_SHAPE) {
- if (seg->shadow_mask_bits & LRT_SHADOW_MASK_LIT || e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) {
- seg->shadow_mask_bits &= ~LRT_SHADOW_MASK_LIT;
+ if (seg->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED ||
+ e->flags & LRT_EDGE_FLAG_LIGHT_CONTOUR) {
seg->shadow_mask_bits |= LRT_SHADOW_MASK_INHIBITED;
}
else if (seg->shadow_mask_bits & LRT_SHADOW_MASK_SHADED) {
- seg->shadow_mask_bits &= ~LRT_SHADOW_MASK_SHADED;
- seg->shadow_mask_bits |= LRT_SHADOW_MASK_LIT;
+ seg->shadow_mask_bits |= LRT_SHADOW_MASK_ILLUMINATED_SHAPE;
}
}
else {
@@ -1412,7 +1411,7 @@ typedef struct LineartEdgeNeighbor {
} LineartEdgeNeighbor;
typedef struct VertData {
- MVert *mvert;
+ const MVert *mvert;
LineartVert *v_arr;
double (*model_view)[4];
double (*model_view_proj)[4];
@@ -1423,7 +1422,7 @@ static void lineart_mvert_transform_task(void *__restrict userdata,
const TaskParallelTLS *__restrict UNUSED(tls))
{
VertData *vert_task_data = (VertData *)userdata;
- MVert *m_v = &vert_task_data->mvert[i];
+ const MVert *m_v = &vert_task_data->mvert[i];
double co[4];
LineartVert *v = &vert_task_data->v_arr[i];
copy_v3db_v3fl(co, m_v->co);
@@ -1471,8 +1470,9 @@ static LineartTriangle *lineart_triangle_from_index(LineartData *ld,
typedef struct EdgeFeatData {
LineartData *ld;
Mesh *me;
- Object *ob;
+ Object *ob_eval; /* For evaluated materials. */
const MLoopTri *mlooptri;
+ const int *material_indices;
LineartTriangle *tri_array;
LineartVert *v_array;
float crease_threshold;
@@ -1504,7 +1504,8 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
EdgeFeatData *e_feat_data = (EdgeFeatData *)userdata;
EdgeFeatReduceData *reduce_data = (EdgeFeatReduceData *)tls->userdata_chunk;
Mesh *me = e_feat_data->me;
- Object *ob = e_feat_data->ob;
+ const int *material_indices = e_feat_data->material_indices;
+ Object *ob_eval = e_feat_data->ob_eval;
LineartEdgeNeighbor *edge_nabr = e_feat_data->edge_nabr;
const MLoopTri *mlooptri = e_feat_data->mlooptri;
@@ -1637,12 +1638,13 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
}
if (!only_contour) {
+ const MPoly *polys = BKE_mesh_polys(me);
if (ld->conf.use_crease) {
bool do_crease = true;
if (!ld->conf.force_crease && !e_feat_data->use_auto_smooth &&
- (me->mpoly[mlooptri[f1].poly].flag & ME_SMOOTH) &&
- (me->mpoly[mlooptri[f2].poly].flag & ME_SMOOTH)) {
+ (polys[mlooptri[f1].poly].flag & ME_SMOOTH) &&
+ (polys[mlooptri[f2].poly].flag & ME_SMOOTH)) {
do_crease = false;
}
if (do_crease && (dot_v3v3_db(tri1->gn, tri2->gn) < e_feat_data->crease_threshold)) {
@@ -1650,12 +1652,12 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
}
}
- int mat1 = me->mpoly[mlooptri[f1].poly].mat_nr;
- int mat2 = me->mpoly[mlooptri[f2].poly].mat_nr;
+ int mat1 = material_indices ? material_indices[mlooptri[f1].poly] : 0;
+ int mat2 = material_indices ? material_indices[mlooptri[f2].poly] : 0;
if (mat1 != mat2) {
- Material *m1 = BKE_object_material_get(ob, mat1 + 1);
- Material *m2 = BKE_object_material_get(ob, mat2 + 1);
+ Material *m1 = BKE_object_material_get_eval(ob_eval, mat1 + 1);
+ Material *m2 = BKE_object_material_get_eval(ob_eval, mat2 + 1);
if (m1 && m2 &&
((m1->lineart.mat_occlusion == 0 && m2->lineart.mat_occlusion != 0) ||
(m2->lineart.mat_occlusion == 0 && m1->lineart.mat_occlusion != 0))) {
@@ -1674,11 +1676,13 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
}
}
+ const MEdge *edges = BKE_mesh_edges(me);
+
int real_edges[3];
BKE_mesh_looptri_get_real_edges(me, &mlooptri[i / 3], real_edges);
if (real_edges[i % 3] >= 0) {
- MEdge *medge = &me->medge[real_edges[i % 3]];
+ const MEdge *medge = &edges[real_edges[i % 3]];
if (ld->conf.use_crease && ld->conf.sharp_as_crease && (medge->flag & ME_SHARP)) {
edge_flag_result |= LRT_EDGE_FLAG_CREASE;
@@ -1709,16 +1713,16 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata,
typedef struct LooseEdgeData {
int loose_count;
int loose_max;
- MEdge **loose_array;
- Mesh *me;
+ int *loose_array;
+ const MEdge *edges;
} LooseEdgeData;
static void lineart_loose_data_reallocate(LooseEdgeData *loose_data, int count)
{
- MEdge **new_arr = MEM_callocN(sizeof(MEdge *) * count, "loose edge array");
+ int *new_arr = MEM_calloc_arrayN(count, sizeof(int), "loose edge array");
if (loose_data->loose_array) {
- memcpy(new_arr, loose_data->loose_array, sizeof(MEdge *) * loose_data->loose_max);
- MEM_freeN(loose_data->loose_array);
+ memcpy(new_arr, loose_data->loose_array, sizeof(int) * loose_data->loose_max);
+ MEM_SAFE_FREE(loose_data->loose_array);
}
loose_data->loose_max = count;
loose_data->loose_array = new_arr;
@@ -1735,19 +1739,19 @@ static void lineart_join_loose_edge_arr(LooseEdgeData *loose_data, LooseEdgeData
}
memcpy(&loose_data->loose_array[loose_data->loose_count],
to_be_joined->loose_array,
- sizeof(MEdge *) * to_be_joined->loose_count);
+ sizeof(int) * to_be_joined->loose_count);
loose_data->loose_count += to_be_joined->loose_count;
MEM_freeN(to_be_joined->loose_array);
to_be_joined->loose_array = NULL;
}
-static void lineart_add_loose_edge(LooseEdgeData *loose_data, MEdge *e)
+static void lineart_add_loose_edge(LooseEdgeData *loose_data, const int i)
{
if (loose_data->loose_count >= loose_data->loose_max) {
int min_amount = MAX2(100, loose_data->loose_count * 2);
lineart_loose_data_reallocate(loose_data, min_amount);
}
- loose_data->loose_array[loose_data->loose_count] = e;
+ loose_data->loose_array[loose_data->loose_count] = i;
loose_data->loose_count++;
}
@@ -1756,10 +1760,9 @@ static void lineart_identify_loose_edges(void *__restrict UNUSED(userdata),
const TaskParallelTLS *__restrict tls)
{
LooseEdgeData *loose_data = (LooseEdgeData *)tls->userdata_chunk;
- Mesh *me = loose_data->me;
- if (me->medge[i].flag & ME_LOOSEEDGE) {
- lineart_add_loose_edge(loose_data, &me->medge[i]);
+ if (loose_data->edges[i].flag & ME_LOOSEEDGE) {
+ lineart_add_loose_edge(loose_data, i);
}
}
@@ -1797,10 +1800,10 @@ static void lineart_add_edge_to_array_thread(LineartObjectInfo *obi, LineartEdge
}
/* NOTE: For simplicity, this function doesn't actually do anything if you already have data in
- * #pe. */
+ * #pe. */
void lineart_finalize_object_edge_array_reserve(LineartPendingEdges *pe, int count)
{
- if (pe->max || pe->array) {
+ if (pe->max || pe->array || count == 0) {
return;
}
@@ -1842,6 +1845,7 @@ static void lineart_triangle_adjacent_assign(LineartTriangle *tri,
typedef struct TriData {
LineartObjectInfo *ob_info;
const MLoopTri *mlooptri;
+ const int *material_indices;
LineartVert *vert_arr;
LineartTriangle *tri_arr;
int lineart_triangle_size;
@@ -1856,22 +1860,24 @@ static void lineart_load_tri_task(void *__restrict userdata,
Mesh *me = tri_task_data->ob_info->original_me;
LineartObjectInfo *ob_info = tri_task_data->ob_info;
const MLoopTri *mlooptri = &tri_task_data->mlooptri[i];
+ const int *material_indices = tri_task_data->material_indices;
LineartVert *vert_arr = tri_task_data->vert_arr;
LineartTriangle *tri = tri_task_data->tri_arr;
+ const MLoop *loops = BKE_mesh_loops(me);
tri = (LineartTriangle *)(((uchar *)tri) + tri_task_data->lineart_triangle_size * i);
- int v1 = me->mloop[mlooptri->tri[0]].v;
- int v2 = me->mloop[mlooptri->tri[1]].v;
- int v3 = me->mloop[mlooptri->tri[2]].v;
+ int v1 = loops[mlooptri->tri[0]].v;
+ int v2 = loops[mlooptri->tri[1]].v;
+ int v3 = loops[mlooptri->tri[2]].v;
tri->v[0] = &vert_arr[v1];
tri->v[1] = &vert_arr[v2];
tri->v[2] = &vert_arr[v3];
/* Material mask bits and occlusion effectiveness assignment. */
- Material *mat = BKE_object_material_get(ob_info->original_ob,
- me->mpoly[mlooptri->poly].mat_nr + 1);
+ Material *mat = BKE_object_material_get(
+ ob_info->original_ob_eval, material_indices ? material_indices[mlooptri->poly] + 1 : 1);
tri->material_mask_bits |= ((mat && (mat->lineart.flags & LRT_MATERIAL_MASK_ENABLED)) ?
mat->lineart.material_mask_bits :
0);
@@ -1890,7 +1896,8 @@ static void lineart_load_tri_task(void *__restrict userdata,
double gn[3];
float no[3];
- normal_tri_v3(no, me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co);
+ const MVert *verts = BKE_mesh_verts(me);
+ normal_tri_v3(no, verts[v1].co, verts[v2].co, verts[v3].co);
copy_v3db_v3fl(gn, no);
mul_v3_mat3_m4v3_db(tri->gn, ob_info->normal, gn);
normalize_v3_db(tri->gn);
@@ -1898,6 +1905,9 @@ static void lineart_load_tri_task(void *__restrict userdata,
if (ob_info->usage == OBJECT_LRT_INTERSECTION_ONLY) {
tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY;
}
+ else if (ob_info->usage == OBJECT_LRT_FORCE_INTERSECTION) {
+ tri->flags |= LRT_TRIANGLE_FORCE_INTERSECTION;
+ }
else if (ob_info->usage == OBJECT_LRT_NO_INTERSECTION ||
ob_info->usage == OBJECT_LRT_OCCLUSION_ONLY) {
tri->flags |= LRT_TRIANGLE_NO_INTERSECTION;
@@ -1909,8 +1919,8 @@ static void lineart_load_tri_task(void *__restrict userdata,
typedef struct EdgeNeighborData {
LineartEdgeNeighbor *edge_nabr;
LineartAdjacentEdge *adj_e;
- MLoopTri *mlooptri;
- MLoop *mloop;
+ const MLoopTri *mlooptri;
+ const MLoop *mloop;
} EdgeNeighborData;
static void lineart_edge_neighbor_init_task(void *__restrict userdata,
@@ -1919,9 +1929,9 @@ static void lineart_edge_neighbor_init_task(void *__restrict userdata,
{
EdgeNeighborData *en_data = (EdgeNeighborData *)userdata;
LineartAdjacentEdge *adj_e = &en_data->adj_e[i];
- MLoopTri *looptri = &en_data->mlooptri[i / 3];
+ const MLoopTri *looptri = &en_data->mlooptri[i / 3];
LineartEdgeNeighbor *edge_nabr = &en_data->edge_nabr[i];
- MLoop *mloop = en_data->mloop;
+ const MLoop *mloop = en_data->mloop;
adj_e->e = i;
adj_e->v1 = mloop[looptri->tri[i % 3]].v;
@@ -1955,7 +1965,7 @@ static LineartEdgeNeighbor *lineart_build_edge_neighbor(Mesh *me, int total_edge
en_data.adj_e = adj_e;
en_data.edge_nabr = edge_nabr;
en_data.mlooptri = mlooptri;
- en_data.mloop = me->mloop;
+ en_data.mloop = BKE_mesh_loops(me);
BLI_task_parallel_range(0, total_edges, &en_data, lineart_edge_neighbor_init_task, &en_settings);
@@ -1993,6 +2003,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
const MLoopTri *mlooptri = BKE_mesh_runtime_looptri_ensure(me);
const int tot_tri = BKE_mesh_runtime_looptri_len(me);
+ const int *material_indices = (const int *)CustomData_get_layer_named(
+ &me->pdata, CD_PROP_INT32, "material_index");
+
/* Check if we should look for custom data tags like Freestyle edges or faces. */
bool can_find_freestyle_edge = false;
int layer_index = CustomData_get_active_layer_index(&me->edata, CD_FREESTYLE_EDGE);
@@ -2078,7 +2091,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
vert_settings.min_iter_per_thread = 4000;
VertData vert_data;
- vert_data.mvert = me->mvert;
+ vert_data.mvert = BKE_mesh_verts(me);
vert_data.v_arr = la_v_arr;
vert_data.model_view = ob_info->model_view;
vert_data.model_view_proj = ob_info->model_view_proj;
@@ -2096,6 +2109,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
TriData tri_data;
tri_data.ob_info = ob_info;
tri_data.mlooptri = mlooptri;
+ tri_data.material_indices = material_indices;
tri_data.vert_arr = la_v_arr;
tri_data.tri_arr = la_tri_arr;
tri_data.lineart_triangle_size = la_data->sizeof_triangle;
@@ -2121,8 +2135,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
EdgeFeatData edge_feat_data = {0};
edge_feat_data.ld = la_data;
edge_feat_data.me = me;
- edge_feat_data.ob = orig_ob;
+ edge_feat_data.ob_eval = ob_info->original_ob_eval;
edge_feat_data.mlooptri = mlooptri;
+ edge_feat_data.material_indices = material_indices;
edge_feat_data.edge_nabr = lineart_build_edge_neighbor(me, total_edges);
edge_feat_data.tri_array = la_tri_arr;
edge_feat_data.v_array = la_v_arr;
@@ -2146,6 +2161,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
&edge_feat_settings);
LooseEdgeData loose_data = {0};
+
if (la_data->conf.use_loose) {
/* Only identifying floating edges at this point because other edges has been taken care of
* inside #lineart_identify_mlooptri_feature_edges function. */
@@ -2155,7 +2171,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
edge_loose_settings.func_reduce = loose_data_sum_reduce;
edge_loose_settings.userdata_chunk = &loose_data;
edge_loose_settings.userdata_chunk_size = sizeof(LooseEdgeData);
- loose_data.me = me;
+ loose_data.edges = BKE_mesh_edges(me);
BLI_task_parallel_range(
0, me->totedge, &loose_data, lineart_identify_loose_edges, &edge_loose_settings);
}
@@ -2239,7 +2255,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
}
if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
- usage == OBJECT_LRT_NO_INTERSECTION) {
+ usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_FORCE_INTERSECTION) {
lineart_add_edge_to_array_thread(ob_info, la_edge);
}
@@ -2260,14 +2276,15 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
if (loose_data.loose_array) {
for (int i = 0; i < loose_data.loose_count; i++) {
- la_edge->v1 = &la_v_arr[loose_data.loose_array[i]->v1];
- la_edge->v2 = &la_v_arr[loose_data.loose_array[i]->v2];
+ const MEdge *edge = &loose_data.edges[loose_data.loose_array[i]];
+ la_edge->v1 = &la_v_arr[edge->v1];
+ la_edge->v2 = &la_v_arr[edge->v2];
la_edge->flags = LRT_EDGE_FLAG_LOOSE;
la_edge->object_ref = orig_ob;
la_edge->edge_identifier = LRT_EDGE_IDENTIFIER(ob_info, la_edge);
BLI_addtail(&la_edge->segments, la_seg);
if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE ||
- usage == OBJECT_LRT_NO_INTERSECTION) {
+ usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_FORCE_INTERSECTION) {
lineart_add_edge_to_array_thread(ob_info, la_edge);
if (shadow_eln) {
LineartEdge *shadow_e = lineart_find_matching_edge(shadow_eln, la_edge->edge_identifier);
@@ -2279,7 +2296,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info,
la_edge++;
la_seg++;
}
- MEM_freeN(loose_data.loose_array);
+ MEM_SAFE_FREE(loose_data.loose_array);
}
MEM_freeN(edge_feat_data.edge_nabr);
@@ -2368,6 +2385,8 @@ static int lineart_usage_check(Collection *c, Object *ob, bool is_render)
return OBJECT_LRT_INTERSECTION_ONLY;
case COLLECTION_LRT_NO_INTERSECTION:
return OBJECT_LRT_NO_INTERSECTION;
+ case COLLECTION_LRT_FORCE_INTERSECTION:
+ return OBJECT_LRT_FORCE_INTERSECTION;
}
return OBJECT_LRT_INHERIT;
}
@@ -2512,6 +2531,7 @@ static void lineart_object_load_single_instance(LineartData *ld,
obi->original_me = use_mesh;
obi->original_ob = (ref_ob->id.orig_id ? (Object *)ref_ob->id.orig_id : (Object *)ref_ob);
+ obi->original_ob_eval = DEG_get_evaluated_object(depsgraph, obi->original_ob);
lineart_geometry_load_assign_thread(olti, obi, thread_count, use_mesh->totpoly);
}
@@ -2651,6 +2671,7 @@ void lineart_main_load_geometries(Depsgraph *depsgraph,
}
LineartVert *v = (LineartVert *)obi->v_eln->pointer;
int v_count = obi->v_eln->element_count;
+ obi->v_eln->global_index_offset = global_i;
for (int vi = 0; vi < v_count; vi++) {
v[vi].index += global_i;
}
@@ -3286,8 +3307,8 @@ static void lineart_add_isec_thread(LineartIsecThread *th,
th->array = new_array;
}
LineartIsecSingle *isec_single = &th->array[th->current];
- copy_v3fl_v3db(isec_single->v1, v1);
- copy_v3fl_v3db(isec_single->v2, v2);
+ copy_v3_v3_db(isec_single->v1, v1);
+ copy_v3_v3_db(isec_single->v2, v2);
isec_single->tri1 = tri1;
isec_single->tri2 = tri2;
if (tri1->target_reference > tri2->target_reference) {
@@ -3394,10 +3415,11 @@ static void lineart_triangle_intersect_in_bounding_area(LineartTriangle *tri,
}
tt->testing_e[th->thread_id] = (LineartEdge *)tri;
- if ((testing_triangle->flags & LRT_TRIANGLE_NO_INTERSECTION) ||
- ((testing_triangle->flags & LRT_TRIANGLE_INTERSECTION_ONLY) &&
- (tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY))) {
- continue;
+ if (!((testing_triangle->flags | tri->flags) & LRT_TRIANGLE_FORCE_INTERSECTION)) {
+ if ((testing_triangle->flags & LRT_TRIANGLE_NO_INTERSECTION) ||
+ (testing_triangle->flags & tri->flags & LRT_TRIANGLE_INTERSECTION_ONLY)) {
+ continue;
+ }
}
double *RG0 = testing_triangle->v[0]->gloc, *RG1 = testing_triangle->v[1]->gloc,
@@ -3643,7 +3665,8 @@ static LineartData *lineart_create_render_buffer(Scene *scene,
(lmd->light_contour_object != NULL));
ld->conf.shadow_selection = lmd->shadow_selection_override;
- ld->conf.shadow_enclose_shapes = (lmd->calculation_flags & LRT_SHADOW_ENCLOSED_SHAPES) != 0;
+ ld->conf.shadow_enclose_shapes = lmd->shadow_selection_override ==
+ LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES;
ld->conf.shadow_use_silhouette = lmd->shadow_use_silhouette_override != 0;
ld->conf.use_back_face_culling = (lmd->calculation_flags & LRT_USE_BACK_FACE_CULLING) != 0;
@@ -4091,7 +4114,6 @@ static bool lineart_bounding_area_triangle_intersect(LineartData *fb,
* (#LineartBoundingArea) for intersection lines. When splitting the tile into 4 children and
* re-linking triangles into the child tiles, intersections are inhibited so we don't get
* duplicated intersection lines.
- *
*/
static void lineart_bounding_area_link_triangle(LineartData *ld,
LineartBoundingArea *root_ba,
@@ -4519,14 +4541,8 @@ static void lineart_add_triangles_worker(TaskPool *__restrict UNUSED(pool), Line
_dir_control++;
for (co = x1; co <= x2; co++) {
for (r = y1; r <= y2; r++) {
- lineart_bounding_area_link_triangle(ld,
- &ld->qtree.initials[r * ld->qtree.count_x + co],
- tri,
- 0,
- 1,
- 0,
- (!(tri->flags & LRT_TRIANGLE_NO_INTERSECTION)),
- th);
+ lineart_bounding_area_link_triangle(
+ ld, &ld->qtree.initials[r * ld->qtree.count_x + co], tri, 0, 1, 0, 1, th);
}
}
} /* Else throw away. */
@@ -4582,8 +4598,8 @@ static void lineart_create_edges_from_isec_data(LineartIsecData *d)
LineartIsecSingle *is = &th->array[j];
LineartVert *v1 = v;
LineartVert *v2 = v + 1;
- copy_v3db_v3fl(v1->gloc, is->v1);
- copy_v3db_v3fl(v2->gloc, is->v2);
+ copy_v3_v3_db(v1->gloc, is->v1);
+ copy_v3_v3_db(v2->gloc, is->v2);
/* The intersection line has been generated only in geometry space, so we need to transform
* them as well. */
mul_v4_m4v3_db(v1->fbcoord, ld->conf.view_projection, v1->gloc);
@@ -4655,7 +4671,7 @@ void lineart_main_add_triangles(LineartData *ld)
}
/* Initialize per-thread data for thread task scheduling information and storing intersection
- * results. */
+ * results. */
LineartIsecData d = {0};
lineart_init_isec_thread(&d, ld, ld->thread_count);
@@ -5105,6 +5121,8 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph,
/* At last, we need to clear flags so we don't confuse GPencil generation calls. */
MOD_lineart_chain_clear_picked_flag(lc);
+
+ MOD_lineart_finalize_chains(ld);
}
lineart_mem_destroy(&lc->shadow_data_pool);
@@ -5228,12 +5246,21 @@ static void lineart_gpencil_generate(LineartCache *cache,
if (shaodow_selection) {
if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) {
/* TODO(@Yiming): Give a behavior option for how to display undefined shadow info. */
- if ((shaodow_selection == LRT_SHADOW_FILTER_LIT &&
- (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_LIT))) ||
- (shaodow_selection == LRT_SHADOW_FILTER_SHADED &&
+ if ((shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED &&
+ (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)))) {
+ continue;
+ }
+ if ((shaodow_selection == LRT_SHADOW_FILTER_SHADED &&
(!(ec->shadow_mask_bits & LRT_SHADOW_MASK_SHADED)))) {
continue;
}
+ if (shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES) {
+ uint32_t test_bits = ec->shadow_mask_bits & LRT_SHADOW_TEST_SHAPE_BITS;
+ if ((test_bits != LRT_SHADOW_MASK_ILLUMINATED) &&
+ (test_bits != (LRT_SHADOW_MASK_SHADED | LRT_SHADOW_MASK_ILLUMINATED_SHAPE))) {
+ continue;
+ }
+ }
}
}
if (silhouette_mode && (ec->type & (LRT_EDGE_FLAG_CONTOUR))) {
@@ -5294,7 +5321,8 @@ static void lineart_gpencil_generate(LineartCache *cache,
if (eval_ob && eval_ob->type == OB_MESH) {
int dindex = 0;
Mesh *me = BKE_object_get_evaluated_mesh(eval_ob);
- if (me->dvert) {
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(me);
+ if (dvert) {
LISTBASE_FOREACH (bDeformGroup *, db, &me->vertex_group_names) {
if ((!source_vgname) || strstr(db->name, source_vgname) == db->name) {
if (match_output) {
@@ -5309,7 +5337,7 @@ static void lineart_gpencil_generate(LineartCache *cache,
if (vindex >= me->totvert) {
break;
}
- MDeformWeight *mdw = BKE_defvert_ensure_index(&me->dvert[vindex], dindex);
+ MDeformWeight *mdw = BKE_defvert_ensure_index(&dvert[vindex], dindex);
MDeformWeight *gdw = BKE_defvert_ensure_index(&gps->dvert[sindex], gpdg);
float use_weight = mdw->weight;
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
index 3668f1dc6d7..947586aaec4 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_intern.h
@@ -133,6 +133,7 @@ void lineart_main_transform_and_add_shadow(struct LineartData *ld,
struct LineartElementLinkNode *eeln);
LineartElementLinkNode *lineart_find_matching_eln(struct ListBase *shadow_elns, int obindex);
+LineartElementLinkNode *lineart_find_matching_eln_obj(struct ListBase *elns, struct Object *ob);
LineartEdge *lineart_find_matching_edge(struct LineartElementLinkNode *shadow_eln,
uint64_t edge_identifier);
void lineart_register_shadow_cuts(struct LineartData *ld,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c
index ad0137fe0f0..257184bae1e 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c
@@ -109,9 +109,10 @@ void lineart_register_shadow_cuts(LineartData *ld, LineartEdge *e, LineartEdge *
la2 = la2 * e->v2->fbcoord[3] /
(e->v1->fbcoord[3] - la2 * (e->v1->fbcoord[3] - e->v2->fbcoord[3]));
unsigned char shadow_bits = (es->occlusion != 0) ? LRT_SHADOW_MASK_SHADED :
- LRT_SHADOW_MASK_LIT;
+ LRT_SHADOW_MASK_ILLUMINATED;
- if (lineart_contour_viewed_from_dark_side(ld, e) && shadow_bits == LRT_SHADOW_MASK_LIT) {
+ if (lineart_contour_viewed_from_dark_side(ld, e) &&
+ shadow_bits == LRT_SHADOW_MASK_ILLUMINATED) {
shadow_bits = LRT_SHADOW_MASK_SHADED;
}
@@ -481,7 +482,7 @@ static void lineart_shadow_create_shadow_edge_array(LineartData *ld,
* This process is repeated on each existing segments of the shadow edge (#e), which ensures they
* all have been tested for closest segments after cutting. And in the diagram it's clear that the
* left/right side of cuts are likely to be discontinuous, each cut's left side designates the
- * right side of the last segment, and vise versa. */
+ * right side of the last segment, and vice-versa. */
static void lineart_shadow_edge_cut(LineartData *ld,
LineartShadowEdge *e,
double start,
@@ -1302,7 +1303,7 @@ static void lineart_shadow_finalize_shadow_edges_task(
int v2i = (e[i].edge_identifier & LRT_OBINDEX_LOWER);
LineartVert *v = (LineartVert *)eln->pointer;
/* If the global position is close enough, use the original vertex to prevent flickering
- * caused by very slim boundary condition in point_triangle_relation().*/
+ * caused by very slim boundary condition in point_triangle_relation(). */
if (LRT_CLOSE_LOOSER_v3(e[i].v1->gloc, v[v1i].gloc)) {
e[i].v1 = &v[v1i];
}
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt
index 0f539d4b30a..8b38c22ae28 100644
--- a/source/blender/gpu/CMakeLists.txt
+++ b/source/blender/gpu/CMakeLists.txt
@@ -5,7 +5,7 @@
# to more easily highlight code-paths in other libraries that need to be refactored,
# bf_gpu is allowed to have opengl regardless of this option.
-if(NOT WITH_OPENGL AND NOT WITH_METAL_BACKEND)
+if(NOT WITH_OPENGL AND NOT WITH_METAL_BACKEND AND NOT WITH_HEADLESS)
add_definitions(-DWITH_OPENGL)
endif()
@@ -27,6 +27,7 @@ set(INC
# For *_info.hh includes.
../draw/engines/eevee_next
+ ../draw/intern
# For node muting stuff.
../nodes
@@ -34,13 +35,12 @@ set(INC
../../../intern/atomic
../../../intern/clog
../../../intern/ghost
- ../../../intern/glew-mx
../../../intern/guardedalloc
../../../intern/mantaflow/extern
)
set(INC_SYS
- ${GLEW_INCLUDE_PATH}
+ ${Epoxy_INCLUDE_DIRS}
)
set(SRC
@@ -93,12 +93,10 @@ set(SRC
GPU_debug.h
GPU_drawlist.h
GPU_framebuffer.h
- GPU_glew.h
GPU_immediate.h
GPU_immediate_util.h
GPU_index_buffer.h
GPU_init_exit.h
- GPU_legacy_stubs.h
GPU_material.h
GPU_matrix.h
GPU_platform.h
@@ -190,14 +188,20 @@ set(OPENGL_SRC
set(METAL_SRC
metal/mtl_backend.mm
- metal/mtl_context.mm
metal/mtl_command_buffer.mm
+ metal/mtl_context.mm
metal/mtl_debug.mm
metal/mtl_framebuffer.mm
+ metal/mtl_index_buffer.mm
metal/mtl_memory.mm
+ metal/mtl_query.mm
+ metal/mtl_shader.mm
+ metal/mtl_shader_generator.mm
+ metal/mtl_shader_interface.mm
metal/mtl_state.mm
metal/mtl_texture.mm
metal/mtl_texture_util.mm
+ metal/mtl_uniform_buffer.mm
metal/mtl_backend.hh
metal/mtl_capabilities.hh
@@ -205,9 +209,19 @@ set(METAL_SRC
metal/mtl_context.hh
metal/mtl_debug.hh
metal/mtl_framebuffer.hh
+ metal/mtl_index_buffer.hh
metal/mtl_memory.hh
+ metal/mtl_primitive.hh
+ metal/mtl_pso_descriptor_state.hh
+ metal/mtl_query.hh
+ metal/mtl_shader.hh
+ metal/mtl_shader_generator.hh
+ metal/mtl_shader_interface.hh
+ metal/mtl_shader_interface_type.hh
+ metal/mtl_shader_shared.h
metal/mtl_state.hh
metal/mtl_texture.hh
+ metal/mtl_uniform_buffer.hh
)
# Select Backend source based on availability
@@ -220,16 +234,12 @@ if(WITH_METAL_BACKEND)
endif()
set(LIB
- ${BLENDER_GL_LIBRARIES}
+ ${Epoxy_LIBRARIES}
)
-if(NOT WITH_SYSTEM_GLEW)
- list(APPEND LIB
- ${BLENDER_GLEW_LIBRARIES}
- )
-endif()
-
set(MSL_SRC
+ shaders/metal/mtl_shader_defines.msl
+ shaders/metal/mtl_shader_common.msl
metal/kernels/compute_texture_update.msl
metal/kernels/compute_texture_read.msl
@@ -262,11 +272,7 @@ set(GLSL_SRC
shaders/gpu_shader_2D_widget_shadow_frag.glsl
shaders/gpu_shader_2D_nodelink_frag.glsl
shaders/gpu_shader_2D_nodelink_vert.glsl
- shaders/gpu_shader_2D_flat_color_vert.glsl
- shaders/gpu_shader_2D_line_dashed_uniform_color_vert.glsl
shaders/gpu_shader_2D_line_dashed_frag.glsl
- shaders/gpu_shader_2D_smooth_color_vert.glsl
- shaders/gpu_shader_2D_smooth_color_frag.glsl
shaders/gpu_shader_2D_image_vert.glsl
shaders/gpu_shader_2D_image_rect_vert.glsl
shaders/gpu_shader_2D_image_multi_rect_vert.glsl
@@ -274,7 +280,6 @@ set(GLSL_SRC
shaders/gpu_shader_image_desaturate_frag.glsl
shaders/gpu_shader_image_overlays_merge_frag.glsl
shaders/gpu_shader_image_overlays_stereo_merge_frag.glsl
- shaders/gpu_shader_image_modulate_alpha_frag.glsl
shaders/gpu_shader_image_shuffle_color_frag.glsl
shaders/gpu_shader_image_color_frag.glsl
shaders/gpu_shader_image_varying_color_frag.glsl
@@ -319,6 +324,59 @@ set(GLSL_SRC
shaders/common/gpu_shader_common_math_utils.glsl
shaders/common/gpu_shader_common_mix_rgb.glsl
+ shaders/compositor/compositor_alpha_crop.glsl
+ shaders/compositor/compositor_bilateral_blur.glsl
+ shaders/compositor/compositor_blur.glsl
+ shaders/compositor/compositor_bokeh_image.glsl
+ shaders/compositor/compositor_box_mask.glsl
+ shaders/compositor/compositor_convert.glsl
+ shaders/compositor/compositor_despeckle.glsl
+ shaders/compositor/compositor_directional_blur.glsl
+ shaders/compositor/compositor_edge_filter.glsl
+ shaders/compositor/compositor_ellipse_mask.glsl
+ shaders/compositor/compositor_filter.glsl
+ shaders/compositor/compositor_flip.glsl
+ shaders/compositor/compositor_image_crop.glsl
+ shaders/compositor/compositor_morphological_distance.glsl
+ shaders/compositor/compositor_morphological_distance_feather.glsl
+ shaders/compositor/compositor_morphological_distance_threshold.glsl
+ shaders/compositor/compositor_morphological_step.glsl
+ shaders/compositor/compositor_projector_lens_distortion.glsl
+ shaders/compositor/compositor_realize_on_domain.glsl
+ shaders/compositor/compositor_screen_lens_distortion.glsl
+ shaders/compositor/compositor_set_alpha.glsl
+ shaders/compositor/compositor_split_viewer.glsl
+ shaders/compositor/compositor_symmetric_blur.glsl
+ shaders/compositor/compositor_symmetric_separable_blur.glsl
+
+ shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl
+ shaders/compositor/library/gpu_shader_compositor_blur_common.glsl
+ shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl
+ shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_balance.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_correction.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_spill.glsl
+ shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl
+ shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_exposure.glsl
+ shaders/compositor/library/gpu_shader_compositor_gamma.glsl
+ shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl
+ shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl
+ shaders/compositor/library/gpu_shader_compositor_invert.glsl
+ shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl
+ shaders/compositor/library/gpu_shader_compositor_main.glsl
+ shaders/compositor/library/gpu_shader_compositor_map_value.glsl
+ shaders/compositor/library/gpu_shader_compositor_normal.glsl
+ shaders/compositor/library/gpu_shader_compositor_posterize.glsl
+ shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl
+ shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl
+ shaders/compositor/library/gpu_shader_compositor_store_output.glsl
+ shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl
+ shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl
+
shaders/material/gpu_shader_material_add_shader.glsl
shaders/material/gpu_shader_material_ambient_occlusion.glsl
shaders/material/gpu_shader_material_anisotropic.glsl
@@ -355,6 +413,7 @@ set(GLSL_SRC
shaders/material/gpu_shader_material_light_path.glsl
shaders/material/gpu_shader_material_mapping.glsl
shaders/material/gpu_shader_material_map_range.glsl
+ shaders/material/gpu_shader_material_mix_color.glsl
shaders/material/gpu_shader_material_mix_shader.glsl
shaders/material/gpu_shader_material_noise.glsl
shaders/material/gpu_shader_material_normal.glsl
@@ -416,21 +475,44 @@ set(GLSL_SRC
GPU_shader_shared_utils.h
)
-set(GLSL_C)
-foreach(GLSL_FILE ${GLSL_SRC})
- data_to_c_simple(${GLSL_FILE} GLSL_C)
-endforeach()
+set(MTL_BACKEND_GLSL_SRC
+ metal/kernels/compute_texture_update.msl
+ metal/kernels/compute_texture_read.msl
+ metal/kernels/depth_2d_update_float_frag.glsl
+ metal/kernels/depth_2d_update_int24_frag.glsl
+ metal/kernels/depth_2d_update_int32_frag.glsl
+ metal/kernels/depth_2d_update_vert.glsl
+ metal/kernels/gpu_shader_fullscreen_blit_vert.glsl
+ metal/kernels/gpu_shader_fullscreen_blit_frag.glsl
+)
+set(MSL_SRC
+ shaders/metal/mtl_shader_defines.msl
+ shaders/metal/mtl_shader_common.msl
+ metal/mtl_shader_shared.h
+)
if(WITH_METAL_BACKEND)
+ list(APPEND GLSL_SRC ${MTL_BACKEND_GLSL_SRC})
+
set(MSL_C)
foreach(MSL_FILE ${MSL_SRC})
data_to_c_simple(${MSL_FILE} MSL_C)
endforeach()
- list(APPEND GLSL_C ${MSL_C})
endif()
-blender_add_lib(bf_gpu_shaders "${GLSL_C}" "" "" "")
+set(GLSL_C)
+foreach(GLSL_FILE ${GLSL_SRC})
+ data_to_c_simple(${GLSL_FILE} GLSL_C)
+endforeach()
+
+set(SHADER_C)
+list(APPEND SHADER_C ${GLSL_C})
+if(WITH_METAL_BACKEND)
+ list(APPEND SHADER_C ${MSL_C})
+endif()
+
+blender_add_lib(bf_gpu_shaders "${SHADER_C}" "" "" "")
list(APPEND LIB
bf_gpu_shaders
@@ -450,8 +532,12 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR})
set(SRC_SHADER_CREATE_INFOS
../draw/engines/basic/shaders/infos/basic_depth_info.hh
+ ../draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_film_info.hh
+ ../draw/engines/eevee_next/shaders/infos/eevee_hiz_info.hh
+ ../draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh
+ ../draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh
../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh
../draw/engines/gpencil/shaders/infos/gpencil_info.hh
../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh
@@ -464,8 +550,8 @@ set(SRC_SHADER_CREATE_INFOS
../draw/engines/overlay/shaders/infos/overlay_grid_info.hh
../draw/engines/overlay/shaders/infos/overlay_outline_info.hh
../draw/engines/overlay/shaders/infos/overlay_paint_info.hh
- ../draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh
../draw/engines/overlay/shaders/infos/overlay_sculpt_curves_info.hh
+ ../draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh
../draw/engines/overlay/shaders/infos/overlay_volume_info.hh
../draw/engines/overlay/shaders/infos/overlay_wireframe_info.hh
../draw/engines/select/shaders/infos/select_id_info.hh
@@ -480,6 +566,7 @@ set(SRC_SHADER_CREATE_INFOS
../draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh
../draw/engines/workbench/shaders/infos/workbench_volume_info.hh
../draw/engines/image/shaders/infos/engine_image_info.hh
+ ../draw/intern/shaders/draw_debug_info.hh
../draw/intern/shaders/draw_fullscreen_info.hh
../draw/intern/shaders/draw_hair_refine_info.hh
../draw/intern/shaders/draw_object_infos_info.hh
@@ -489,8 +576,6 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/gpu_shader_2D_area_borders_info.hh
shaders/infos/gpu_shader_2D_checker_info.hh
shaders/infos/gpu_shader_2D_diag_stripes_info.hh
- shaders/infos/gpu_shader_2D_flat_color_info.hh
- shaders/infos/gpu_shader_2D_image_color_info.hh
shaders/infos/gpu_shader_2D_image_desaturate_color_info.hh
shaders/infos/gpu_shader_2D_image_info.hh
shaders/infos/gpu_shader_2D_image_multi_rect_color_info.hh
@@ -502,13 +587,10 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh
shaders/infos/gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh
shaders/infos/gpu_shader_2D_point_varying_size_varying_color_info.hh
- shaders/infos/gpu_shader_2D_smooth_color_info.hh
- shaders/infos/gpu_shader_2D_uniform_color_info.hh
shaders/infos/gpu_shader_2D_widget_info.hh
shaders/infos/gpu_shader_3D_depth_only_info.hh
shaders/infos/gpu_shader_3D_flat_color_info.hh
shaders/infos/gpu_shader_3D_image_info.hh
- shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
shaders/infos/gpu_shader_3D_point_info.hh
shaders/infos/gpu_shader_3D_polyline_info.hh
shaders/infos/gpu_shader_3D_smooth_color_info.hh
@@ -520,8 +602,43 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/gpu_shader_simple_lighting_info.hh
shaders/infos/gpu_shader_text_info.hh
shaders/infos/gpu_srgb_to_framebuffer_space_info.hh
+
+ shaders/compositor/infos/compositor_alpha_crop_info.hh
+ shaders/compositor/infos/compositor_bilateral_blur_info.hh
+ shaders/compositor/infos/compositor_blur_info.hh
+ shaders/compositor/infos/compositor_bokeh_image_info.hh
+ shaders/compositor/infos/compositor_box_mask_info.hh
+ shaders/compositor/infos/compositor_convert_info.hh
+ shaders/compositor/infos/compositor_despeckle_info.hh
+ shaders/compositor/infos/compositor_directional_blur_info.hh
+ shaders/compositor/infos/compositor_edge_filter_info.hh
+ shaders/compositor/infos/compositor_ellipse_mask_info.hh
+ shaders/compositor/infos/compositor_filter_info.hh
+ shaders/compositor/infos/compositor_flip_info.hh
+ shaders/compositor/infos/compositor_image_crop_info.hh
+ shaders/compositor/infos/compositor_morphological_distance_feather_info.hh
+ shaders/compositor/infos/compositor_morphological_distance_info.hh
+ shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh
+ shaders/compositor/infos/compositor_morphological_step_info.hh
+ shaders/compositor/infos/compositor_projector_lens_distortion_info.hh
+ shaders/compositor/infos/compositor_realize_on_domain_info.hh
+ shaders/compositor/infos/compositor_screen_lens_distortion_info.hh
+ shaders/compositor/infos/compositor_set_alpha_info.hh
+ shaders/compositor/infos/compositor_split_viewer_info.hh
+ shaders/compositor/infos/compositor_symmetric_blur_info.hh
+ shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh
+)
+
+set(SRC_SHADER_CREATE_INFOS_MTL
+ metal/kernels/depth_2d_update_info.hh
+ metal/kernels/gpu_shader_fullscreen_blit_info.hh
)
+if(WITH_METAL_BACKEND)
+ list(APPEND SRC_SHADER_CREATE_INFOS ${SRC_SHADER_CREATE_INFOS_MTL})
+endif()
+
+
set(SHADER_CREATE_INFOS_CONTENT "")
foreach(DESCRIPTOR_FILE ${SRC_SHADER_CREATE_INFOS})
string(APPEND SHADER_CREATE_INFOS_CONTENT "#include \"${DESCRIPTOR_FILE}\"\n")
@@ -534,8 +651,6 @@ if(WITH_MOD_FLUID)
add_definitions(-DWITH_FLUID)
endif()
-add_definitions(${GL_DEFINITIONS})
-
if(WITH_IMAGE_DDS)
add_definitions(-DWITH_DDS)
endif()
@@ -561,20 +676,17 @@ endif()
-if(WITH_GPU_SHADER_BUILDER)
+if(WITH_GPU_BUILDTIME_SHADER_BUILDER)
# TODO(@fclem) Fix this mess.
if(APPLE)
add_executable(shader_builder
intern/gpu_shader_builder.cc
+ intern/gpu_shader_builder_stubs.cc
${shader_create_info_list_file}
)
setup_platform_linker_flags(shader_builder)
-
- target_link_libraries(shader_builder PUBLIC
- bf_blenkernel
- buildinfoobj
- )
+ target_link_libraries(shader_builder PUBLIC buildinfoobj)
else()
if(WIN32)
# We can re-use the manifest from tests.exe here since it's
@@ -589,12 +701,14 @@ if(WITH_GPU_SHADER_BUILDER)
${MANIFEST}
)
- target_link_libraries(shader_builder PUBLIC
- bf_blenkernel
- ${PLATFORM_LINKLIBS}
- )
endif()
-
+ target_link_libraries(shader_builder PUBLIC
+ bf_gpu
+ bf_intern_clog
+ bf_blenlib
+ bf_intern_ghost
+ ${PLATFORM_LINKLIBS}
+ )
target_include_directories(shader_builder PRIVATE ${INC} ${CMAKE_CURRENT_BINARY_DIR})
set(SRC_BAKED_CREATE_INFOS_FILE ${CMAKE_CURRENT_BINARY_DIR}/shader_baked.hh)
diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h
index 7fad8dd23be..4935ced7f48 100644
--- a/source/blender/gpu/GPU_batch.h
+++ b/source/blender/gpu/GPU_batch.h
@@ -14,6 +14,7 @@
#include "GPU_index_buffer.h"
#include "GPU_shader.h"
+#include "GPU_storage_buffer.h"
#include "GPU_uniform_buffer.h"
#include "GPU_vertex_buffer.h"
@@ -69,6 +70,8 @@ typedef struct GPUBatch {
GPUVertBuf *inst[GPU_BATCH_INST_VBO_MAX_LEN];
/** NULL if element list not needed */
GPUIndexBuf *elem;
+ /** Resource ID attribute workaround. */
+ GPUStorageBuf *resource_id_buf;
/** Bookkeeping. */
eGPUBatchFlag flag;
/** Type of geometry to draw. */
@@ -92,8 +95,10 @@ void GPU_batch_init_ex(GPUBatch *batch,
*/
void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src);
-#define GPU_batch_create(prim, verts, elem) GPU_batch_create_ex(prim, verts, elem, 0)
-#define GPU_batch_init(batch, prim, verts, elem) GPU_batch_init_ex(batch, prim, verts, elem, 0)
+#define GPU_batch_create(prim, verts, elem) \
+ GPU_batch_create_ex(prim, verts, elem, (eGPUBatchFlag)0)
+#define GPU_batch_init(batch, prim, verts, elem) \
+ GPU_batch_init_ex(batch, prim, verts, elem, (eGPUBatchFlag)0)
/**
* Same as discard but does not free. (does not call free callback).
@@ -123,6 +128,11 @@ bool GPU_batch_vertbuf_has(GPUBatch *, GPUVertBuf *);
#define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false)
+/**
+ * Set resource id buffer to bind as instance attribute to workaround the lack of gl_BaseInstance.
+ */
+void GPU_batch_resource_id_buf_set(GPUBatch *batch, GPUStorageBuf *resource_id_buf);
+
void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader);
/**
* Bind program bound to IMM to the batch.
@@ -161,6 +171,13 @@ void GPU_batch_program_set_builtin_with_config(GPUBatch *batch,
#define GPU_batch_texture_bind(batch, name, tex) \
GPU_texture_bind(tex, GPU_shader_get_texture_binding((batch)->shader, name));
+/**
+ * Return indirect draw call parameters for this batch.
+ * NOTE: r_base_index is set to -1 if not using an index buffer.
+ */
+void GPU_batch_draw_parameter_get(
+ GPUBatch *batch, int *r_v_count, int *r_v_first, int *r_base_index, int *r_i_count);
+
void GPU_batch_draw(GPUBatch *batch);
void GPU_batch_draw_range(GPUBatch *batch, int v_first, int v_count);
/**
@@ -171,7 +188,15 @@ void GPU_batch_draw_instanced(GPUBatch *batch, int i_count);
/**
* This does not bind/unbind shader and does not call GPU_matrix_bind().
*/
-void GPU_batch_draw_advanced(GPUBatch *, int v_first, int v_count, int i_first, int i_count);
+void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count);
+
+/**
+ * Issue a draw call using GPU computed arguments. The argument are expected to be valid for the
+ * type of geometry drawn (index or non-indexed).
+ */
+void GPU_batch_draw_indirect(GPUBatch *batch, GPUStorageBuf *indirect_buf, intptr_t offset);
+void GPU_batch_multi_draw_indirect(
+ GPUBatch *batch, GPUStorageBuf *indirect_buf, int count, intptr_t offset, intptr_t stride);
#if 0 /* future plans */
diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h
index 89473ac0fe0..5cdc5f19540 100644
--- a/source/blender/gpu/GPU_buffers.h
+++ b/source/blender/gpu/GPU_buffers.h
@@ -22,6 +22,7 @@ struct CCGKey;
struct DMFlagMat;
struct GSet;
struct TableGSet;
+struct Mesh;
struct MLoop;
struct MLoopCol;
struct MLoopTri;
@@ -46,14 +47,10 @@ typedef struct GPU_PBVH_Buffers GPU_PBVH_Buffers;
*
* Threaded: do not call any functions that use OpenGL calls!
*/
-GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const struct MPoly *mpoly,
- const struct MLoop *mloop,
+GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const struct Mesh *mesh,
const struct MLoopTri *looptri,
- const struct MVert *mvert,
const int *face_indices,
- const int *sculpt_face_sets,
- int face_indices_len,
- const struct Mesh *mesh);
+ int face_indices_len);
/**
* Threaded: do not call any functions that use OpenGL calls!
@@ -91,9 +88,8 @@ enum {
*/
void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
GPU_PBVH_Buffers *buffers,
+ const struct Mesh *mesh,
const struct MVert *mvert,
- const CustomData *vdata,
- const CustomData *ldata,
const float *vmask,
const int *sculpt_face_sets,
const int face_sets_color_seed,
diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h
index 7fe467de402..61c60f336e1 100644
--- a/source/blender/gpu/GPU_capabilities.h
+++ b/source/blender/gpu/GPU_capabilities.h
@@ -16,6 +16,7 @@ extern "C" {
#endif
int GPU_max_texture_size(void);
+int GPU_max_texture_3d_size(void);
int GPU_max_texture_layers(void);
int GPU_max_textures(void);
int GPU_max_textures_vert(void);
@@ -31,6 +32,7 @@ int GPU_max_vertex_attribs(void);
int GPU_max_varying_floats(void);
int GPU_max_shader_storage_buffer_bindings(void);
int GPU_max_compute_shader_storage_blocks(void);
+int GPU_max_samplers(void);
int GPU_extensions_len(void);
const char *GPU_extension_get(int i);
@@ -47,6 +49,7 @@ 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_shader_draw_parameters_support(void);
bool GPU_mem_stats_supported(void);
void GPU_mem_stats_get(int *totalmem, int *freemem);
@@ -56,6 +59,9 @@ void GPU_mem_stats_get(int *totalmem, int *freemem);
*/
bool GPU_stereo_quadbuffer_support(void);
+int GPU_minimum_per_vertex_stride(void);
+bool GPU_transform_feedback_support(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/GPU_compute.h b/source/blender/gpu/GPU_compute.h
index 6dfd6f73ae8..ff94620f186 100644
--- a/source/blender/gpu/GPU_compute.h
+++ b/source/blender/gpu/GPU_compute.h
@@ -20,7 +20,7 @@ void GPU_compute_dispatch(GPUShader *shader,
uint groups_y_len,
uint groups_z_len);
-void GPU_compute_dispatch_indirect(GPUShader *shader, GPUStorageBuf *indirect_buf);
+void GPU_compute_dispatch_indirect(GPUShader *shader, GPUStorageBuf *indirect_buf_);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h
index 1fcd94c48fc..a242bb7cc94 100644
--- a/source/blender/gpu/GPU_context.h
+++ b/source/blender/gpu/GPU_context.h
@@ -17,10 +17,10 @@
extern "C" {
#endif
-void GPU_backend_init(eGPUBackendType backend);
-void GPU_backend_exit(void);
-bool GPU_backend_supported(eGPUBackendType type);
-
+/* GPU back-ends abstract the differences between different APIs. #GPU_context_create
+ * automatically initializes the back-end, and #GPU_context_discard frees it when there
+ * are no more contexts. */
+bool GPU_backend_supported(void);
eGPUBackendType GPU_backend_get_type(void);
/** Opaque type hiding blender::gpu::Context. */
diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h
index 70ec7c19e7c..bdb384c16f1 100644
--- a/source/blender/gpu/GPU_framebuffer.h
+++ b/source/blender/gpu/GPU_framebuffer.h
@@ -189,6 +189,12 @@ void GPU_framebuffer_texture_layer_attach(
void GPU_framebuffer_texture_cubeface_attach(
GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip);
+/**
+ * Default size is used if the framebuffer contains no attachments.
+ * It needs to be re-specified each time an attachment is added.
+ */
+void GPU_framebuffer_default_size(GPUFrameBuffer *gpu_fb, int width, int height);
+
/* Frame-buffer operations. */
/**
diff --git a/source/blender/gpu/GPU_glew.h b/source/blender/gpu/GPU_glew.h
deleted file mode 100644
index 38209a0eb17..00000000000
--- a/source/blender/gpu/GPU_glew.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2012 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- */
-
-#pragma once
-
-#if defined(WITH_OPENGL)
-# include "glew-mx.h"
-# ifndef WITH_LEGACY_OPENGL
-# include "GPU_legacy_stubs.h"
-# endif
-#endif
diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h
index bbb431cbc15..e5fefda527d 100644
--- a/source/blender/gpu/GPU_index_buffer.h
+++ b/source/blender/gpu/GPU_index_buffer.h
@@ -26,14 +26,17 @@ typedef struct GPUIndexBufBuilder {
uint index_len;
uint index_min;
uint index_max;
+ uint restart_index_value;
+ bool uses_restart_indices;
+
GPUPrimType prim_type;
uint32_t *data;
} GPUIndexBufBuilder;
-/* supports all primitive types. */
+/** Supports all primitive types. */
void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uint vertex_len);
-/* supports only GPU_PRIM_POINTS, GPU_PRIM_LINES and GPU_PRIM_TRIS. */
+/** 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);
diff --git a/source/blender/gpu/GPU_legacy_stubs.h b/source/blender/gpu/GPU_legacy_stubs.h
deleted file mode 100644
index 5970738a9b3..00000000000
--- a/source/blender/gpu/GPU_legacy_stubs.h
+++ /dev/null
@@ -1,497 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2017 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- *
- * This is to mark the transition to OpenGL core profile
- * The idea is to allow Blender 2.8 to be built with OpenGL 3.3 even if it means breaking things
- *
- * This file should be removed in the future
- */
-
-#pragma once
-
-#if defined(__GNUC__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wunused-parameter"
-# pragma GCC diagnostic ignored "-Wunused-function"
-#endif
-
-#include <stdlib.h> /* for abort(). */
-
-#include "BLI_utildefines.h"
-
-/**
- * Empty function, use for break-point when a deprecated
- * OpenGL function is called.
- */
-static void gl_deprecated(void)
-{
- BLI_assert(true);
-}
-
-#define _GL_BOOL BLI_INLINE GLboolean
-#define _GL_BOOL_RET \
- { \
- gl_deprecated(); \
- return false; \
- }
-
-#define _GL_ENUM BLI_INLINE GLenum
-#define _GL_ENUM_RET \
- { \
- gl_deprecated(); \
- return 0; \
- }
-
-#define _GL_INT BLI_INLINE GLint
-#define _GL_INT_RET \
- { \
- gl_deprecated(); \
- return 0; \
- }
-
-#define _GL_UINT BLI_INLINE GLuint
-#define _GL_UINT_RET \
- { \
- gl_deprecated(); \
- return 0; \
- }
-
-#define _GL_VOID BLI_INLINE void
-#define _GL_VOID_RET \
- { \
- gl_deprecated(); \
- }
-
-static bool disable_enable_check(GLenum cap)
-{
- const bool is_deprecated = ELEM(cap,
- GL_ALPHA_TEST,
- GL_LINE_STIPPLE,
- GL_POINT_SPRITE,
- GL_TEXTURE_1D,
- GL_TEXTURE_2D,
- GL_TEXTURE_GEN_S,
- GL_TEXTURE_GEN_T,
- -1);
-
- if (is_deprecated) {
- gl_deprecated();
- }
-
- return is_deprecated;
-}
-
-_GL_VOID USE_CAREFULLY_glDisable(GLenum cap)
-{
- if (!disable_enable_check(cap)) {
- glDisable(cap);
- }
-}
-#define glDisable USE_CAREFULLY_glDisable
-
-_GL_VOID USE_CAREFULLY_glEnable(GLenum cap)
-{
- if (!disable_enable_check(cap)) {
- glEnable(cap);
- }
-}
-#define glEnable USE_CAREFULLY_glEnable
-
-/**
- * Hand written cases
- */
-
-_GL_VOID DO_NOT_USE_glClientActiveTexture(GLenum texture) _GL_VOID_RET
-
-/**
- * List automatically generated from `gl-deprecated.h` and `glew.h`
- */
-
-/**
- * ENUM values
- */
-#define DO_NOT_USE_GL_CURRENT_FOG_COORDINATE 0
-#define DO_NOT_USE_GL_FOG_COORDINATE 0
-#define DO_NOT_USE_GL_FOG_COORDINATE_ARRAY 0
-#define DO_NOT_USE_GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0
-#define DO_NOT_USE_GL_FOG_COORDINATE_ARRAY_POINTER 0
-#define DO_NOT_USE_GL_FOG_COORDINATE_ARRAY_STRIDE 0
-#define DO_NOT_USE_GL_FOG_COORDINATE_ARRAY_TYPE 0
-#define DO_NOT_USE_GL_FOG_COORDINATE_SOURCE 0
-#define DO_NOT_USE_GL_POINT_SIZE_GRANULARITY 0
-#define DO_NOT_USE_GL_POINT_SIZE_RANGE 0
-#define DO_NOT_USE_GL_SOURCE0_ALPHA 0
-#define DO_NOT_USE_GL_SOURCE0_RGB 0
-#define DO_NOT_USE_GL_SOURCE1_ALPHA 0
-#define DO_NOT_USE_GL_SOURCE1_RGB 0
-#define DO_NOT_USE_GL_SOURCE2_ALPHA 0
-#define DO_NOT_USE_GL_SOURCE2_RGB 0
-
- /**
- * Functions
- */
- _GL_VOID DO_NOT_USE_glAccum(GLenum op, GLfloat value) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glAlphaFunc(GLenum func, GLclampf ref) _GL_VOID_RET _GL_BOOL
- DO_NOT_USE_glAreTexturesResident(GLsizei n,
- const GLuint *textures,
- GLboolean *residences) _GL_BOOL_RET _GL_VOID
- DO_NOT_USE_glArrayElement(GLint i) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glBegin(GLenum mode) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glBitmap(GLsizei width,
- GLsizei height,
- GLfloat xorig,
- GLfloat yorig,
- GLfloat xmove,
- GLfloat ymove,
- const GLubyte *bitmap) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glCallList(GLuint list) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glCallLists(GLsizei n, GLenum type, const void *lists) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glClearAccum(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
- _GL_VOID_RET _GL_VOID DO_NOT_USE_glClearIndex(GLfloat c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glClipPlane(GLenum plane, const GLdouble *equation) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3b(GLbyte red, GLbyte green, GLbyte blue) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3bv(const GLbyte *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3d(GLdouble red, GLdouble green, GLdouble blue) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3f(GLfloat red, GLfloat green, GLfloat blue) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3i(GLint red, GLint green, GLint blue) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3s(GLshort red, GLshort green, GLshort blue) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3ub(GLubyte red, GLubyte green, GLubyte blue) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3ubv(const GLubyte *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3ui(GLuint red, GLuint green, GLuint blue) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3uiv(const GLuint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3us(GLushort red, GLushort green, GLushort blue) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor3usv(const GLushort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4b(GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4bv(const GLbyte *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4d(GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha)
- _GL_VOID_RET _GL_VOID DO_NOT_USE_glColor4dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)
- _GL_VOID_RET _GL_VOID DO_NOT_USE_glColor4fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4i(GLint red, GLint green, GLint blue, GLint alpha) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4s(GLshort red, GLshort green, GLshort blue, GLshort alpha)
- _GL_VOID_RET _GL_VOID DO_NOT_USE_glColor4sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)
- _GL_VOID_RET _GL_VOID DO_NOT_USE_glColor4ubv(const GLubyte *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4ui(GLuint red, GLuint green, GLuint blue, GLuint alpha)
- _GL_VOID_RET _GL_VOID DO_NOT_USE_glColor4uiv(const GLuint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColor4us(GLushort red, GLushort green, GLushort blue, GLushort alpha)
- _GL_VOID_RET _GL_VOID DO_NOT_USE_glColor4usv(const GLushort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColorMaterial(GLenum face, GLenum mode) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer)
- _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glCopyPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type)
- _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glDeleteLists(GLuint list, GLsizei range) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glDisableClientState(GLenum array) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glDrawPixels(GLsizei width,
- GLsizei height,
- GLenum format,
- GLenum type,
- const void *pixels) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEdgeFlag(GLboolean flag) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEdgeFlagPointer(GLsizei stride, const void *pointer) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEdgeFlagv(const GLboolean *flag) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEnableClientState(GLenum array) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEnd(void) _GL_VOID_RET _GL_VOID DO_NOT_USE_glEndList(void) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalCoord1d(GLdouble u) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalCoord1dv(const GLdouble *u) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalCoord1f(GLfloat u) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalCoord1fv(const GLfloat *u) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalCoord2d(GLdouble u, GLdouble v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalCoord2dv(const GLdouble *u) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalCoord2f(GLfloat u, GLfloat v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalCoord2fv(const GLfloat *u) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalMesh1(GLenum mode, GLint i1, GLint i2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalMesh2(GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2)
- _GL_VOID_RET _GL_VOID DO_NOT_USE_glEvalPoint1(GLint i) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glEvalPoint2(GLint i, GLint j) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glFeedbackBuffer(GLsizei size, GLenum type, GLfloat *buffer) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glFogf(GLenum pname, GLfloat param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glFogfv(GLenum pname, const GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glFogi(GLenum pname, GLint param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glFogiv(GLenum pname, const GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glFrustum(GLdouble left,
- GLdouble right,
- GLdouble bottom,
- GLdouble top,
- GLdouble zNear,
- GLdouble zFar) _GL_VOID_RET _GL_UINT
- DO_NOT_USE_glGenLists(GLsizei range) _GL_UINT_RET _GL_VOID
- DO_NOT_USE_glGetClipPlane(GLenum plane, GLdouble *equation) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetLightfv(GLenum light, GLenum pname, GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetLightiv(GLenum light, GLenum pname, GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetMapdv(GLenum target, GLenum query, GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetMapfv(GLenum target, GLenum query, GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetMapiv(GLenum target, GLenum query, GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetMaterialfv(GLenum face, GLenum pname, GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetMaterialiv(GLenum face, GLenum pname, GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetPixelMapfv(GLenum map, GLfloat *values) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetPixelMapuiv(GLenum map, GLuint *values) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetPixelMapusv(GLenum map, GLushort *values) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetPolygonStipple(GLubyte *mask) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetTexEnvfv(GLenum target, GLenum pname, GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetTexEnviv(GLenum target, GLenum pname, GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetTexGendv(GLenum coord, GLenum pname, GLdouble *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetTexGenfv(GLenum coord, GLenum pname, GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glGetTexGeniv(GLenum coord, GLenum pname, GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexMask(GLuint mask) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexPointer(GLenum type,
- GLsizei stride,
- const void *pointer) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexd(GLdouble c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexdv(const GLdouble *c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexf(GLfloat c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexfv(const GLfloat *c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexi(GLint c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexiv(const GLint *c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexs(GLshort c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexsv(const GLshort *c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexub(GLubyte c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glIndexubv(const GLubyte *c) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glInitNames(void) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glInterleavedArrays(GLenum format,
- GLsizei stride,
- const void *pointer) _GL_VOID_RET _GL_BOOL
- DO_NOT_USE_glIsList(GLuint list) _GL_BOOL_RET _GL_VOID
- DO_NOT_USE_glLightModelf(GLenum pname, GLfloat param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLightModelfv(GLenum pname, const GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLightModeli(GLenum pname, GLint param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLightModeliv(GLenum pname, const GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLightf(GLenum light, GLenum pname, GLfloat param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLightfv(GLenum light, GLenum pname, const GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLighti(GLenum light, GLenum pname, GLint param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLightiv(GLenum light, GLenum pname, const GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLineStipple(GLint factor, GLushort pattern) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glListBase(GLuint base) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLoadIdentity(void) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLoadMatrixd(const GLdouble *m) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLoadMatrixf(const GLfloat *m) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glLoadName(GLuint name) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMap1d(GLenum target,
- GLdouble u1,
- GLdouble u2,
- GLint stride,
- GLint order,
- const GLdouble *points) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMap1f(GLenum target,
- GLfloat u1,
- GLfloat u2,
- GLint stride,
- GLint order,
- const GLfloat *points) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMap2d(GLenum target,
- GLdouble u1,
- GLdouble u2,
- GLint ustride,
- GLint uorder,
- GLdouble v1,
- GLdouble v2,
- GLint vstride,
- GLint vorder,
- const GLdouble *points) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMap2f(GLenum target,
- GLfloat u1,
- GLfloat u2,
- GLint ustride,
- GLint uorder,
- GLfloat v1,
- GLfloat v2,
- GLint vstride,
- GLint vorder,
- const GLfloat *points) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMapGrid1d(GLint un, GLdouble u1, GLdouble u2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMapGrid1f(GLint un, GLfloat u1, GLfloat u2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMapGrid2d(GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2)
- _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMapGrid2f(GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2)
- _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMaterialf(GLenum face, GLenum pname, GLfloat param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMaterialfv(GLenum face, GLenum pname, const GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMateriali(GLenum face, GLenum pname, GLint param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMaterialiv(GLenum face, GLenum pname, const GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMatrixMode(GLenum mode) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMultMatrixd(const GLdouble *m) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glMultMatrixf(const GLfloat *m) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNewList(GLuint list, GLenum mode) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3b(GLbyte nx, GLbyte ny, GLbyte nz) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3bv(const GLbyte *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3d(GLdouble nx, GLdouble ny, GLdouble nz) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3i(GLint nx, GLint ny, GLint nz) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3s(GLshort nx, GLshort ny, GLshort nz) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormal3sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glNormalPointer(GLenum type,
- GLsizei stride,
- const void *pointer) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glOrtho(GLdouble left,
- GLdouble right,
- GLdouble bottom,
- GLdouble top,
- GLdouble zNear,
- GLdouble zFar) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPassThrough(GLfloat token) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPixelMapfv(GLenum map,
- GLsizei mapsize,
- const GLfloat *values) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPixelMapuiv(GLenum map,
- GLsizei mapsize,
- const GLuint *values) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPixelMapusv(GLenum map,
- GLsizei mapsize,
- const GLushort *values) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPixelTransferf(GLenum pname, GLfloat param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPixelTransferi(GLenum pname, GLint param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPixelZoom(GLfloat xfactor, GLfloat yfactor) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPolygonStipple(const GLubyte *mask) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPopAttrib(void) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPopClientAttrib(void) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPopMatrix(void) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPopName(void) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPrioritizeTextures(GLsizei n,
- const GLuint *textures,
- const GLclampf *priorities) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPushAttrib(GLbitfield mask) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPushClientAttrib(GLbitfield mask) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPushMatrix(void) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glPushName(GLuint name) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos2d(GLdouble x, GLdouble y) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos2dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos2f(GLfloat x, GLfloat y) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos2fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos2i(GLint x, GLint y) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos2iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos2s(GLshort x, GLshort y) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos2sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos3d(GLdouble x, GLdouble y, GLdouble z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos3dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos3f(GLfloat x, GLfloat y, GLfloat z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos3fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos3i(GLint x, GLint y, GLint z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos3iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos3s(GLshort x, GLshort y, GLshort z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos3sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos4d(GLdouble x, GLdouble y, GLdouble z, GLdouble w) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos4dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos4fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos4i(GLint x, GLint y, GLint z, GLint w) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos4iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos4s(GLshort x, GLshort y, GLshort z, GLshort w) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRasterPos4sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRectd(GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRectdv(const GLdouble *v1, const GLdouble *v2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRectf(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRectfv(const GLfloat *v1, const GLfloat *v2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRecti(GLint x1, GLint y1, GLint x2, GLint y2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRectiv(const GLint *v1, const GLint *v2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRects(GLshort x1, GLshort y1, GLshort x2, GLshort y2) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRectsv(const GLshort *v1, const GLshort *v2) _GL_VOID_RET _GL_INT
- DO_NOT_USE_glRenderMode(GLenum mode) _GL_INT_RET _GL_VOID
- DO_NOT_USE_glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glScaled(GLdouble x, GLdouble y, GLdouble z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glScalef(GLfloat x, GLfloat y, GLfloat z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glSelectBuffer(GLsizei size, GLuint *buffer) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glShadeModel(GLenum mode) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord1d(GLdouble s) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord1dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord1f(GLfloat s) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord1fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord1i(GLint s) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord1iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord1s(GLshort s) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord1sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord2d(GLdouble s, GLdouble t) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord2dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord2f(GLfloat s, GLfloat t) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord2fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord2i(GLint s, GLint t) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord2iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord2s(GLshort s, GLshort t) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord2sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord3d(GLdouble s, GLdouble t, GLdouble r) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord3dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord3f(GLfloat s, GLfloat t, GLfloat r) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord3fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord3i(GLint s, GLint t, GLint r) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord3iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord3s(GLshort s, GLshort t, GLshort r) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord3sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord4d(GLdouble s, GLdouble t, GLdouble r, GLdouble q) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord4dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord4f(GLfloat s, GLfloat t, GLfloat r, GLfloat q) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord4fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord4i(GLint s, GLint t, GLint r, GLint q) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord4iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord4s(GLshort s, GLshort t, GLshort r, GLshort q) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoord4sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer)
- _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexEnvf(GLenum target, GLenum pname, GLfloat param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexEnvfv(GLenum target, GLenum pname, const GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexEnvi(GLenum target, GLenum pname, GLint param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexEnviv(GLenum target, GLenum pname, const GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexGend(GLenum coord, GLenum pname, GLdouble param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexGendv(GLenum coord, GLenum pname, const GLdouble *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexGenf(GLenum coord, GLenum pname, GLfloat param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexGenfv(GLenum coord, GLenum pname, const GLfloat *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexGeni(GLenum coord, GLenum pname, GLint param) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTexGeniv(GLenum coord, GLenum pname, const GLint *params) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTranslated(GLdouble x, GLdouble y, GLdouble z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glTranslatef(GLfloat x, GLfloat y, GLfloat z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex2d(GLdouble x, GLdouble y) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex2dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex2f(GLfloat x, GLfloat y) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex2fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex2i(GLint x, GLint y) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex2iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex2s(GLshort x, GLshort y) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex2sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex3d(GLdouble x, GLdouble y, GLdouble z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex3dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex3f(GLfloat x, GLfloat y, GLfloat z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex3fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex3i(GLint x, GLint y, GLint z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex3iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex3s(GLshort x, GLshort y, GLshort z) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex3sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex4d(GLdouble x, GLdouble y, GLdouble z, GLdouble w) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex4dv(const GLdouble *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex4f(GLfloat x, GLfloat y, GLfloat z, GLfloat w) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex4fv(const GLfloat *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex4i(GLint x, GLint y, GLint z, GLint w) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex4iv(const GLint *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex4s(GLshort x, GLshort y, GLshort z, GLshort w) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertex4sv(const GLshort *v) _GL_VOID_RET _GL_VOID
- DO_NOT_USE_glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer)
- _GL_VOID_RET
-
-/**
- * End of automatically generated list
- */
-
-#undef _GL_BOOL
-#undef _GL_BOOL_RET
-#undef _GL_ENUM
-#undef _GL_ENUM_RET
-#undef _GL_INT
-#undef _GL_INT_RET
-#undef _GL_UINT
-#undef _GL_UINT_RET
-#undef _GL_VOID
-#undef _GL_VOID_RET
-
-#if defined(__GNUC__)
-# pragma GCC diagnostic pop
-#endif
diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h
index c0633f0323d..922988bf95a 100644
--- a/source/blender/gpu/GPU_material.h
+++ b/source/blender/gpu/GPU_material.h
@@ -77,12 +77,20 @@ typedef enum eGPUMaterialFlag {
GPU_MATFLAG_HOLDOUT = (1 << 6),
GPU_MATFLAG_SHADER_TO_RGBA = (1 << 7),
GPU_MATFLAG_AO = (1 << 8),
+ GPU_MATFLAG_CLEARCOAT = (1 << 9),
GPU_MATFLAG_OBJECT_INFO = (1 << 10),
GPU_MATFLAG_AOV = (1 << 11),
GPU_MATFLAG_BARYCENTRIC = (1 << 20),
+ /* Optimization to only add the branches of the principled shader that are necessary. */
+ GPU_MATFLAG_PRINCIPLED_CLEARCOAT = (1 << 21),
+ GPU_MATFLAG_PRINCIPLED_METALLIC = (1 << 22),
+ GPU_MATFLAG_PRINCIPLED_DIELECTRIC = (1 << 23),
+ GPU_MATFLAG_PRINCIPLED_GLASS = (1 << 24),
+ GPU_MATFLAG_PRINCIPLED_ANY = (1 << 25),
+
/* Tells the render engine the material was just compiled or updated. */
GPU_MATFLAG_UPDATED = (1 << 29),
@@ -121,6 +129,7 @@ typedef struct GPUCodegenOutput {
char *surface;
char *volume;
char *thickness;
+ char *composite;
char *material_functions;
GPUShaderCreateInfo *create_info;
@@ -131,11 +140,19 @@ typedef void (*GPUCodegenCallbackFn)(void *thunk, GPUMaterial *mat, GPUCodegenOu
GPUNodeLink *GPU_constant(const float *num);
GPUNodeLink *GPU_uniform(const float *num);
GPUNodeLink *GPU_attribute(GPUMaterial *mat, eCustomDataType type, const char *name);
+/**
+ * Add a GPU attribute that refers to the default color attribute on a geometry.
+ * The name, type, and domain are unknown and do not depend on the material.
+ */
+GPUNodeLink *GPU_attribute_default_color(GPUMaterial *mat);
GPUNodeLink *GPU_attribute_with_default(GPUMaterial *mat,
eCustomDataType type,
const char *name,
eGPUDefaultValue default_value);
-GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat, const char *name, bool use_dupli);
+GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat,
+ const char *name,
+ bool use_dupli,
+ uint32_t *r_hash);
GPUNodeLink *GPU_image(GPUMaterial *mat,
struct Image *ima,
struct ImageUser *iuser,
@@ -145,6 +162,12 @@ GPUNodeLink *GPU_image_tiled(GPUMaterial *mat,
struct ImageUser *iuser,
eGPUSamplerState sampler_state);
GPUNodeLink *GPU_image_tiled_mapping(GPUMaterial *mat, struct Image *ima, struct ImageUser *iuser);
+GPUNodeLink *GPU_image_sky(GPUMaterial *mat,
+ int width,
+ int height,
+ const float *pixels,
+ float *layer,
+ eGPUSamplerState sampler_state);
GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *row);
/**
@@ -156,15 +179,11 @@ GPUNodeLink *GPU_differentiate_float_function(const char *function_name);
bool GPU_link(GPUMaterial *mat, const char *name, ...);
bool GPU_stack_link(GPUMaterial *mat,
- struct bNode *node,
+ const struct bNode *node,
const char *name,
GPUNodeStack *in,
GPUNodeStack *out,
...);
-GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat,
- struct bNode *node,
- struct GPUNodeStack *stack,
- int index);
void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link);
void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link);
@@ -173,6 +192,8 @@ void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link);
void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash);
+void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link);
+
/**
* Wrap a part of the material graph into a function. You need then need to call the function by
* using something like #GPU_differentiate_float_function.
@@ -213,6 +234,7 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene,
void *thunk);
void GPU_material_compile(GPUMaterial *mat);
+void GPU_material_free_single(GPUMaterial *material);
void GPU_material_free(struct ListBase *gpumaterial);
void GPU_material_acquire(GPUMaterial *mat);
@@ -223,6 +245,7 @@ void GPU_materials_free(struct Main *bmain);
struct Scene *GPU_material_scene(GPUMaterial *material);
struct GPUPass *GPU_material_get_pass(GPUMaterial *material);
struct GPUShader *GPU_material_get_shader(GPUMaterial *material);
+const char *GPU_material_get_name(GPUMaterial *material);
/**
* Return can be NULL if it's a world material.
*/
@@ -266,6 +289,12 @@ typedef struct GPUMaterialAttribute {
eGPUDefaultValue default_value; /* Only for volumes attributes. */
int id;
int users;
+ /**
+ * If true, the corresponding attribute is the specified default color attribute on the mesh,
+ * if it exists. In that case the type and name data can vary per geometry, so it will not be
+ * valid here.
+ */
+ bool is_default_color;
} GPUMaterialAttribute;
typedef struct GPUMaterialTexture {
@@ -274,6 +303,7 @@ typedef struct GPUMaterialTexture {
struct ImageUser iuser;
bool iuser_available;
struct GPUTexture **colorband;
+ struct GPUTexture **sky;
char sampler_name[32]; /* Name of sampler in GLSL. */
char tiled_mapping_name[32]; /* Name of tile mapping sampler in GLSL. */
int users;
@@ -288,6 +318,10 @@ typedef struct GPUUniformAttr {
/* Meaningful part of the attribute set key. */
char name[64]; /* MAX_CUSTOMDATA_LAYER_NAME */
+ /** Escaped name with [""]. */
+ char name_id_prop[64 * 2 + 4];
+ /** Hash of name[64] + use_dupli. */
+ uint32_t hash_code;
bool use_dupli;
/* Helper fields used by code generation. */
@@ -302,12 +336,22 @@ typedef struct GPUUniformAttrList {
unsigned int count, hash_code;
} GPUUniformAttrList;
-GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material);
+const GPUUniformAttrList *GPU_material_uniform_attributes(const GPUMaterial *material);
struct GHash *GPU_uniform_attr_list_hash_new(const char *info);
-void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, GPUUniformAttrList *src);
+void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, const GPUUniformAttrList *src);
void GPU_uniform_attr_list_free(GPUUniformAttrList *set);
+/* A callback passed to GPU_material_from_callbacks to construct the material graph by adding and
+ * linking the necessary GPU material nodes. */
+typedef void (*ConstructGPUMaterialFn)(void *thunk, GPUMaterial *material);
+
+/* Construct a GPU material from a set of callbacks. See the callback types for more information.
+ * The given thunk will be passed as the first parameter of each callback. */
+GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_function_cb,
+ GPUCodegenCallbackFn generate_code_function_cb,
+ void *thunk);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/gpu/GPU_primitive.h b/source/blender/gpu/GPU_primitive.h
index 4860b037bfb..de2feac2607 100644
--- a/source/blender/gpu/GPU_primitive.h
+++ b/source/blender/gpu/GPU_primitive.h
@@ -9,6 +9,7 @@
#pragma once
+#include "BLI_assert.h"
#include "GPU_common.h"
#ifdef __cplusplus
@@ -42,6 +43,79 @@ typedef enum {
GPU_PRIM_CLASS_ANY = GPU_PRIM_CLASS_POINT | GPU_PRIM_CLASS_LINE | GPU_PRIM_CLASS_SURFACE,
} GPUPrimClass;
+inline int gpu_get_prim_count_from_type(uint vertex_len, GPUPrimType prim_type)
+{
+ /* does vertex_len make sense for this primitive type? */
+ if (vertex_len == 0) {
+ return 0;
+ }
+
+ switch (prim_type) {
+ case GPU_PRIM_POINTS:
+ return vertex_len;
+
+ case GPU_PRIM_LINES:
+ BLI_assert(vertex_len % 2 == 0);
+ return vertex_len / 2;
+
+ case GPU_PRIM_LINE_STRIP:
+ return vertex_len - 1;
+
+ case GPU_PRIM_LINE_LOOP:
+ return vertex_len;
+
+ case GPU_PRIM_LINES_ADJ:
+ BLI_assert(vertex_len % 4 == 0);
+ return vertex_len / 4;
+
+ case GPU_PRIM_LINE_STRIP_ADJ:
+ return vertex_len - 2;
+
+ case GPU_PRIM_TRIS:
+ BLI_assert(vertex_len % 3 == 0);
+ return vertex_len / 3;
+
+ case GPU_PRIM_TRI_STRIP:
+ BLI_assert(vertex_len >= 3);
+ return vertex_len - 2;
+
+ case GPU_PRIM_TRI_FAN:
+ BLI_assert(vertex_len >= 3);
+ return vertex_len - 2;
+
+ case GPU_PRIM_TRIS_ADJ:
+ BLI_assert(vertex_len % 6 == 0);
+ return vertex_len / 6;
+
+ default:
+ BLI_assert_unreachable();
+ return 0;
+ }
+}
+
+inline bool is_restart_compatible(GPUPrimType type)
+{
+ switch (type) {
+ case GPU_PRIM_POINTS:
+ case GPU_PRIM_LINES:
+ case GPU_PRIM_TRIS:
+ case GPU_PRIM_LINES_ADJ:
+ case GPU_PRIM_TRIS_ADJ:
+ case GPU_PRIM_NONE:
+ default: {
+ return false;
+ }
+ case GPU_PRIM_LINE_STRIP:
+ case GPU_PRIM_LINE_LOOP:
+ case GPU_PRIM_TRI_STRIP:
+ case GPU_PRIM_TRI_FAN:
+ case GPU_PRIM_LINE_STRIP_ADJ: {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* TODO: Improve error checking by validating that the shader is suited for this primitive type.
* GPUPrimClass GPU_primtype_class(GPUPrimType);
diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h
index 3460d33fe68..c1b3b879c34 100644
--- a/source/blender/gpu/GPU_shader.h
+++ b/source/blender/gpu/GPU_shader.h
@@ -148,11 +148,19 @@ typedef enum {
GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */
} GPUUniformBlockBuiltin;
+typedef enum {
+ GPU_STORAGE_BUFFER_DEBUG_VERTS = 0, /* drw_debug_verts_buf */
+ GPU_STORAGE_BUFFER_DEBUG_PRINT, /* drw_debug_print_buf */
+
+ GPU_NUM_STORAGE_BUFFERS, /* Special value, denotes number of builtin buffer blocks. */
+} GPUStorageBufferBuiltin;
+
void GPU_shader_set_srgb_uniform(GPUShader *shader);
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_builtin_ssbo(GPUShader *shader, int builtin);
/** DEPRECATED: Kept only because of Python GPU API. */
int GPU_shader_get_uniform_block(GPUShader *shader, const char *name);
int GPU_shader_get_ssbo(GPUShader *shader, const char *name);
@@ -177,11 +185,18 @@ void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, fl
void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]);
void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]);
void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]);
+void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2]);
void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]);
+void GPU_shader_uniform_mat3_as_mat4(GPUShader *sh, const char *name, const float data[3][3]);
void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]);
void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]);
+unsigned int GPU_shader_get_attribute_len(const GPUShader *shader);
int GPU_shader_get_attribute(GPUShader *shader, const char *name);
+bool GPU_shader_get_attribute_info(const GPUShader *shader,
+ int attr_location,
+ char r_name[256],
+ int *r_type);
void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear);
@@ -191,30 +206,12 @@ typedef enum eGPUBuiltinShader {
GPU_SHADER_TEXT,
GPU_SHADER_KEYFRAME_SHAPE,
GPU_SHADER_SIMPLE_LIGHTING,
- /* for simple 2D drawing */
- /**
- * Take a single color for all the vertices and a 2D position for each vertex.
- *
- * \param color: uniform vec4
- * \param pos: in vec2
- */
- GPU_SHADER_2D_UNIFORM_COLOR,
- /**
- * Take a 2D position and color for each vertex without color interpolation.
- *
- * \param color: in vec4
- * \param pos: in vec2
- */
- GPU_SHADER_2D_FLAT_COLOR,
/**
* Take a 2D position and color for each vertex with linear interpolation in window space.
*
* \param color: in vec4
* \param pos: in vec2
*/
- GPU_SHADER_2D_SMOOTH_COLOR,
- GPU_SHADER_2D_IMAGE,
- GPU_SHADER_2D_IMAGE_COLOR,
GPU_SHADER_2D_IMAGE_DESATURATE_COLOR,
GPU_SHADER_2D_IMAGE_RECT_COLOR,
GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR,
@@ -290,14 +287,14 @@ typedef enum eGPUBuiltinShader {
*/
GPU_SHADER_3D_IMAGE,
/**
- * Draw texture with alpha. Take a 3D position and a 2D texture coordinate for each vertex.
+ * Take a 3D position and color for each vertex with linear interpolation in window space.
*
- * \param alpha: uniform float
+ * \param color: uniform vec4
* \param image: uniform sampler2D
* \param texCoord: in vec2
* \param pos: in vec3
*/
- GPU_SHADER_3D_IMAGE_MODULATE_ALPHA,
+ GPU_SHADER_3D_IMAGE_COLOR,
/* points */
/**
* Draw round points with a constant size.
@@ -346,7 +343,6 @@ typedef enum eGPUBuiltinShader {
*/
GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR,
/* lines */
- GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR,
GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR,
/* grease pencil drawing */
GPU_SHADER_GPENCIL_STROKE,
diff --git a/source/blender/gpu/GPU_shader_shared_utils.h b/source/blender/gpu/GPU_shader_shared_utils.h
index 88bdad2bf76..96feed9e7d9 100644
--- a/source/blender/gpu/GPU_shader_shared_utils.h
+++ b/source/blender/gpu/GPU_shader_shared_utils.h
@@ -43,20 +43,23 @@
# define sqrtf sqrt
# define expf exp
-# define float2 vec2
-# define float3 vec3
-# define float4 vec4
-# define float4x4 mat4
-# define int2 ivec2
-# define int3 ivec3
-# define int4 ivec4
-# define uint2 uvec2
-# define uint3 uvec3
-# define uint4 uvec4
# define bool1 bool
-# define bool2 bvec2
-# define bool3 bvec3
-# define bool4 bvec4
+/* Type name collision with Metal shading language - These type-names are already defined. */
+# ifndef GPU_METAL
+# define float2 vec2
+# define float3 vec3
+# define float4 vec4
+# define float4x4 mat4
+# define int2 ivec2
+# define int3 ivec3
+# define int4 ivec4
+# define uint2 uvec2
+# define uint3 uvec3
+# define uint4 uvec4
+# define bool2 bvec2
+# define bool3 bvec3
+# define bool4 bvec4
+# endif
#else /* C / C++ */
# pragma once
diff --git a/source/blender/gpu/GPU_storage_buffer.h b/source/blender/gpu/GPU_storage_buffer.h
index ca6a848786b..8837a7c7647 100644
--- a/source/blender/gpu/GPU_storage_buffer.h
+++ b/source/blender/gpu/GPU_storage_buffer.h
@@ -48,6 +48,13 @@ void GPU_storagebuf_clear(GPUStorageBuf *ssbo,
void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo);
/**
+ * Read back content of the buffer to CPU for inspection.
+ * Slow! Only use for inspection / debugging.
+ * NOTE: Not synchronized. Use appropriate barrier before reading.
+ */
+void GPU_storagebuf_read(GPUStorageBuf *ssbo, void *data);
+
+/**
* \brief Copy a part of a vertex buffer to a storage buffer.
*
* \param ssbo: destination storage buffer
diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h
index 5bd20b7be98..8b54f4c9822 100644
--- a/source/blender/gpu/GPU_texture.h
+++ b/source/blender/gpu/GPU_texture.h
@@ -49,7 +49,12 @@ typedef enum eGPUSamplerState {
* #GPU_SAMPLER_MAX is not a valid enum value, but only a limit.
* It also creates a bad mask for the `NOT` operator in #ENUM_OPERATORS.
*/
+#ifdef __cplusplus
+static constexpr eGPUSamplerState GPU_SAMPLER_MAX = eGPUSamplerState(GPU_SAMPLER_ICON + 1);
+#else
static const int GPU_SAMPLER_MAX = (GPU_SAMPLER_ICON + 1);
+#endif
+
ENUM_OPERATORS(eGPUSamplerState, GPU_SAMPLER_ICON)
#ifdef __cplusplus
@@ -193,7 +198,7 @@ unsigned int GPU_texture_memory_usage_get(void);
* \note \a data is expected to be float. If the \a format is not compatible with float data or if
* the data is not in float format, use GPU_texture_update to upload the data with the right data
* format.
- * \a mips is the number of mip level to allocate. It must be >= 1.
+ * \a mip_len is the number of mip level to allocate. It must be >= 1.
*/
GPUTexture *GPU_texture_create_1d(
const char *name, int w, int mip_len, eGPUTextureFormat format, const float *data);
@@ -331,6 +336,7 @@ int GPU_texture_orig_width(const GPUTexture *tex);
int GPU_texture_orig_height(const GPUTexture *tex);
void GPU_texture_orig_size_set(GPUTexture *tex, int w, int h);
eGPUTextureFormat GPU_texture_format(const GPUTexture *tex);
+const char *GPU_texture_format_description(eGPUTextureFormat texture_format);
bool GPU_texture_array(const GPUTexture *tex);
bool GPU_texture_cube(const GPUTexture *tex);
bool GPU_texture_depth(const GPUTexture *tex);
diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h
index 722ef878271..d3c1bd8145d 100644
--- a/source/blender/gpu/GPU_vertex_buffer.h
+++ b/source/blender/gpu/GPU_vertex_buffer.h
@@ -40,12 +40,20 @@ extern "C" {
typedef enum {
/* can be extended to support more types */
- 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. */
+ GPU_USAGE_STREAM = 0,
+ GPU_USAGE_STATIC = 1, /* do not keep data in memory */
+ GPU_USAGE_DYNAMIC = 2,
+ GPU_USAGE_DEVICE_ONLY = 3, /* Do not do host->device data transfers. */
+
+ /** Extended usage flags. */
+ /* Flag for vertex buffers used for textures. Skips additional padding/compaction to ensure
+ * format matches the texture exactly. Can be masked with other properties, and is stripped
+ * during VertBuf::init. */
+ GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY = 1 << 3,
} GPUUsageType;
+ENUM_OPERATORS(GPUUsageType, GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY);
+
/** Opaque type hiding blender::gpu::VertBuf. */
typedef struct GPUVertBuf GPUVertBuf;
diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh
index 6e07e6c3229..d2890efee72 100644
--- a/source/blender/gpu/intern/gpu_backend.hh
+++ b/source/blender/gpu/intern/gpu_backend.hh
@@ -30,6 +30,7 @@ class VertBuf;
class GPUBackend {
public:
virtual ~GPUBackend() = default;
+ virtual void delete_resources() = 0;
static GPUBackend *get();
diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc
index 1b34b6e6c69..c871004deac 100644
--- a/source/blender/gpu/intern/gpu_batch.cc
+++ b/source/blender/gpu/intern/gpu_batch.cc
@@ -200,6 +200,13 @@ bool GPU_batch_vertbuf_has(GPUBatch *batch, GPUVertBuf *verts)
return false;
}
+void GPU_batch_resource_id_buf_set(GPUBatch *batch, GPUStorageBuf *resource_id_buf)
+{
+ BLI_assert(resource_id_buf);
+ batch->flag |= GPU_BATCH_DIRTY;
+ batch->resource_id_buf = resource_id_buf;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -220,6 +227,30 @@ void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader)
/** \name Drawing / Drawcall functions
* \{ */
+void GPU_batch_draw_parameter_get(
+ GPUBatch *gpu_batch, int *r_v_count, int *r_v_first, int *r_base_index, int *r_i_count)
+{
+ Batch *batch = static_cast<Batch *>(gpu_batch);
+
+ if (batch->elem) {
+ *r_v_count = batch->elem_()->index_len_get();
+ *r_v_first = batch->elem_()->index_start_get();
+ *r_base_index = batch->elem_()->index_base_get();
+ }
+ else {
+ *r_v_count = batch->verts_(0)->vertex_len;
+ *r_v_first = 0;
+ *r_base_index = -1;
+ }
+
+ int i_count = (batch->inst[0]) ? batch->inst_(0)->vertex_len : 1;
+ /* Meh. This is to be able to use different numbers of verts in instance VBO's. */
+ if (batch->inst[1] != nullptr) {
+ i_count = min_ii(i_count, batch->inst_(1)->vertex_len);
+ }
+ *r_i_count = i_count;
+}
+
void GPU_batch_draw(GPUBatch *batch)
{
GPU_shader_bind(batch->shader);
@@ -270,6 +301,25 @@ void GPU_batch_draw_advanced(
batch->draw(v_first, v_count, i_first, i_count);
}
+void GPU_batch_draw_indirect(GPUBatch *gpu_batch, GPUStorageBuf *indirect_buf, intptr_t offset)
+{
+ BLI_assert(Context::get()->shader != nullptr);
+ BLI_assert(indirect_buf != nullptr);
+ Batch *batch = static_cast<Batch *>(gpu_batch);
+
+ batch->draw_indirect(indirect_buf, offset);
+}
+
+void GPU_batch_multi_draw_indirect(
+ GPUBatch *gpu_batch, GPUStorageBuf *indirect_buf, int count, intptr_t offset, intptr_t stride)
+{
+ BLI_assert(Context::get()->shader != nullptr);
+ BLI_assert(indirect_buf != nullptr);
+ Batch *batch = static_cast<Batch *>(gpu_batch);
+
+ batch->multi_draw_indirect(indirect_buf, count, offset, stride);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/gpu/intern/gpu_batch_private.hh b/source/blender/gpu/intern/gpu_batch_private.hh
index 23052f601d2..59646925d68 100644
--- a/source/blender/gpu/intern/gpu_batch_private.hh
+++ b/source/blender/gpu/intern/gpu_batch_private.hh
@@ -29,6 +29,11 @@ class Batch : public GPUBatch {
virtual ~Batch() = default;
virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0;
+ virtual void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) = 0;
+ virtual void multi_draw_indirect(GPUStorageBuf *indirect_buf,
+ int count,
+ intptr_t offset,
+ intptr_t stride) = 0;
/* Convenience casts. */
IndexBuf *elem_() const
diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c
index 14bbd82c282..c0527357663 100644
--- a/source/blender/gpu/intern/gpu_buffers.c
+++ b/source/blender/gpu/intern/gpu_buffers.c
@@ -210,20 +210,15 @@ static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers, GPUPrimType prim)
/** \name Mesh PBVH
* \{ */
-static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt,
- const MVert *mvert,
- const MLoop *mloop,
- const int *sculpt_face_sets)
+static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt, const bool *hide_poly)
{
- return (!paint_is_face_hidden(lt, mvert, mloop) && sculpt_face_sets &&
- sculpt_face_sets[lt->poly] > SCULPT_FACE_SET_NONE);
+ return !paint_is_face_hidden(lt, hide_poly);
}
void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
GPU_PBVH_Buffers *buffers,
+ const Mesh *mesh,
const MVert *mvert,
- const CustomData *vdata,
- const CustomData *ldata,
const float *vmask,
const int *sculpt_face_sets,
int face_sets_color_seed,
@@ -234,23 +229,25 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
GPUAttrRef vcol_refs[MAX_GPU_ATTR];
GPUAttrRef cd_uvs[MAX_GPU_ATTR];
- Mesh me_query;
- BKE_id_attribute_copy_domains_temp(ID_ME, vdata, NULL, ldata, NULL, NULL, &me_query.id);
+ const bool *hide_poly = (const bool *)CustomData_get_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, ".hide_poly");
+ const int *material_indices = (const int *)CustomData_get_layer_named(
+ &mesh->pdata, CD_PROP_INT32, "material_index");
- CustomDataLayer *actcol = BKE_id_attributes_active_color_get(&me_query.id);
- eAttrDomain actcol_domain = actcol ? BKE_id_attribute_domain(&me_query.id, actcol) :
+ const CustomDataLayer *actcol = BKE_id_attributes_active_color_get(&mesh->id);
+ eAttrDomain actcol_domain = actcol ? BKE_id_attribute_domain(&mesh->id, actcol) :
ATTR_DOMAIN_AUTO;
- CustomDataLayer *rendercol = BKE_id_attributes_render_color_get(&me_query.id);
+ const CustomDataLayer *rendercol = BKE_id_attributes_render_color_get(&mesh->id);
int totcol;
if (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) {
totcol = gpu_pbvh_make_attr_offs(ATTR_DOMAIN_MASK_COLOR,
CD_MASK_COLOR_ALL,
- vdata,
+ &mesh->vdata,
NULL,
- ldata,
+ &mesh->ldata,
NULL,
vcol_refs,
vbo_id->active_attrs_only,
@@ -267,14 +264,14 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
CD_MASK_MLOOPUV,
NULL,
NULL,
- ldata,
+ &mesh->ldata,
NULL,
cd_uvs,
vbo_id->active_attrs_only,
CD_MLOOPUV,
ATTR_DOMAIN_CORNER,
- get_active_layer(ldata, CD_MLOOPUV),
- get_render_layer(ldata, CD_MLOOPUV));
+ get_active_layer(&mesh->ldata, CD_MLOOPUV),
+ get_render_layer(&mesh->ldata, CD_MLOOPUV));
const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0;
const bool show_face_sets = sculpt_face_sets &&
@@ -308,13 +305,13 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, vbo_id->uv[uv_i], &uv_step);
GPUAttrRef *ref = cd_uvs + uv_i;
- CustomDataLayer *layer = ldata->layers + ref->layer_idx;
+ CustomDataLayer *layer = mesh->ldata.layers + ref->layer_idx;
MLoopUV *muv = layer->data;
for (uint i = 0; i < buffers->face_indices_len; i++) {
const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]];
- if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) {
+ if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
continue;
}
@@ -330,20 +327,20 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
for (int col_i = 0; col_i < totcol; col_i++) {
GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, vbo_id->col[col_i], &col_step);
- MPropCol *pcol = NULL;
- MLoopCol *mcol = NULL;
+ const MPropCol *pcol = NULL;
+ const MLoopCol *mcol = NULL;
GPUAttrRef *ref = vcol_refs + col_i;
- const CustomData *cdata = ref->domain == ATTR_DOMAIN_POINT ? vdata : ldata;
- CustomDataLayer *layer = cdata->layers + ref->layer_idx;
+ const CustomData *cdata = ref->domain == ATTR_DOMAIN_POINT ? &mesh->vdata : &mesh->ldata;
+ const CustomDataLayer *layer = cdata->layers + ref->layer_idx;
bool color_loops = ref->domain == ATTR_DOMAIN_CORNER;
if (layer->type == CD_PROP_COLOR) {
- pcol = (MPropCol *)layer->data;
+ pcol = (const MPropCol *)layer->data;
}
else {
- mcol = (MLoopCol *)layer->data;
+ mcol = (const MLoopCol *)layer->data;
}
for (uint i = 0; i < buffers->face_indices_len; i++) {
@@ -354,7 +351,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
buffers->mloop[lt->tri[2]].v,
};
- if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) {
+ if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
continue;
}
@@ -365,7 +362,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
ushort scol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX};
if (pcol) {
- MPropCol *pcol2 = pcol + (color_loops ? loop_index : vtri[j]);
+ const MPropCol *pcol2 = pcol + (color_loops ? loop_index : vtri[j]);
scol[0] = unit_float_to_ushort_clamp(pcol2->color[0]);
scol[1] = unit_float_to_ushort_clamp(pcol2->color[1]);
@@ -394,7 +391,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
buffers->mloop[lt->tri[2]].v,
};
- if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) {
+ if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
continue;
}
@@ -409,7 +406,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
uchar face_set_color[4] = {UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX};
if (show_face_sets) {
- const int fset = abs(sculpt_face_sets[lt->poly]);
+ const int fset = sculpt_face_sets[lt->poly];
/* Skip for the default color Face Set to render it white. */
if (fset != face_sets_color_default) {
BKE_paint_face_set_overlay_color_get(fset, face_sets_color_seed, face_set_color);
@@ -450,37 +447,38 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id,
/* Get material index from the first face of this buffer. */
const MLoopTri *lt = &buffers->looptri[buffers->face_indices[0]];
- const MPoly *mp = &buffers->mpoly[lt->poly];
- buffers->material_index = mp->mat_nr;
+ buffers->material_index = material_indices ? material_indices[lt->poly] : 0;
buffers->show_overlay = !empty_mask || !default_face_set;
buffers->mvert = mvert;
}
-GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly,
- const MLoop *mloop,
+GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh,
const MLoopTri *looptri,
- const MVert *mvert,
const int *face_indices,
- const int *sculpt_face_sets,
- const int face_indices_len,
- const struct Mesh *mesh)
+ const int face_indices_len)
{
GPU_PBVH_Buffers *buffers;
int i, tottri;
int tot_real_edges = 0;
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
+
buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers");
+ const bool *hide_poly = (bool *)CustomData_get_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, ".hide_poly");
+
/* smooth or flat for all */
- buffers->smooth = mpoly[looptri[face_indices[0]].poly].flag & ME_SMOOTH;
+ buffers->smooth = polys[looptri[face_indices[0]].poly].flag & ME_SMOOTH;
buffers->show_overlay = false;
/* Count the number of visible triangles */
for (i = 0, tottri = 0; i < face_indices_len; i++) {
const MLoopTri *lt = &looptri[face_indices[i]];
- if (gpu_pbvh_is_looptri_visible(lt, mvert, mloop, sculpt_face_sets)) {
+ if (gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
int r_edges[3];
BKE_mesh_looptri_get_real_edges(mesh, lt, r_edges);
for (int j = 0; j < 3; j++) {
@@ -495,8 +493,8 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly,
if (tottri == 0) {
buffers->tot_tri = 0;
- buffers->mpoly = mpoly;
- buffers->mloop = mloop;
+ buffers->mpoly = polys;
+ buffers->mloop = loops;
buffers->looptri = looptri;
buffers->face_indices = face_indices;
buffers->face_indices_len = 0;
@@ -513,7 +511,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly,
const MLoopTri *lt = &looptri[face_indices[i]];
/* Skip hidden faces */
- if (!gpu_pbvh_is_looptri_visible(lt, mvert, mloop, sculpt_face_sets)) {
+ if (!gpu_pbvh_is_looptri_visible(lt, hide_poly)) {
continue;
}
@@ -535,8 +533,8 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly,
buffers->tot_tri = tottri;
- buffers->mpoly = mpoly;
- buffers->mloop = mloop;
+ buffers->mpoly = polys;
+ buffers->mloop = loops;
buffers->looptri = looptri;
buffers->face_indices = face_indices;
@@ -768,7 +766,7 @@ void GPU_pbvh_grid_buffers_update(PBVHGPUFormat *vbo_id,
if (show_face_sets && subdiv_ccg && sculpt_face_sets) {
const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, grid_index);
- const int fset = abs(sculpt_face_sets[face_index]);
+ const int fset = sculpt_face_sets[face_index];
/* Skip for the default color Face Set to render it white. */
if (fset != face_sets_color_default) {
BKE_paint_face_set_overlay_color_get(fset, face_sets_color_seed, face_set_color);
@@ -1230,7 +1228,7 @@ static int gpu_pbvh_make_attr_offs(eAttrDomainMask domain_mask,
continue;
}
- CustomDataLayer *cl = cdata->layers;
+ const CustomDataLayer *cl = cdata->layers;
for (int i = 0; count < MAX_GPU_ATTR && i < cdata->totlayer; i++, cl++) {
if ((CD_TYPE_AS_MASK(cl->type) & type_mask) && !(cl->flag & CD_FLAG_TEMPORARY)) {
@@ -1246,9 +1244,7 @@ static int gpu_pbvh_make_attr_offs(eAttrDomainMask domain_mask,
}
}
- /* ensure render layer is last
- draw cache code seems to need this
- */
+ /* Ensure render layer is last, draw cache code seems to need this. */
for (int i = 0; i < count; i++) {
GPUAttrRef *ref = r_cd_attrs + i;
@@ -1319,12 +1315,12 @@ bool GPU_pbvh_attribute_names_update(PBVHType pbvh_type,
BKE_id_attribute_copy_domains_temp(ID_ME, vdata, NULL, ldata, NULL, NULL, &me_query.id);
- CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&me_query.id);
- CustomDataLayer *render_color_layer = BKE_id_attributes_render_color_get(&me_query.id);
+ const CustomDataLayer *active_color_layer = BKE_id_attributes_active_color_get(&me_query.id);
+ const CustomDataLayer *render_color_layer = BKE_id_attributes_render_color_get(&me_query.id);
eAttrDomain active_color_domain = active_color_layer ?
BKE_id_attribute_domain(&me_query.id,
active_color_layer) :
- ATTR_DOMAIN_NUM;
+ ATTR_DOMAIN_POINT;
GPUAttrRef vcol_layers[MAX_GPU_ATTR];
int totlayer = gpu_pbvh_make_attr_offs(ATTR_DOMAIN_MASK_COLOR,
@@ -1374,7 +1370,7 @@ bool GPU_pbvh_attribute_names_update(PBVHType pbvh_type,
vbo_id->totuv = 0;
if (pbvh_type == PBVH_FACES && ldata && CustomData_has_layer(ldata, CD_MLOOPUV)) {
GPUAttrRef uv_layers[MAX_GPU_ATTR];
- CustomDataLayer *active = NULL, *render = NULL;
+ const CustomDataLayer *active = NULL, *render = NULL;
active = get_active_layer(ldata, CD_MLOOPUV);
render = get_render_layer(ldata, CD_MLOOPUV);
@@ -1400,7 +1396,7 @@ bool GPU_pbvh_attribute_names_update(PBVHType pbvh_type,
vbo_id->uv[i] = GPU_vertformat_attr_add(
&vbo_id->format, "uvs", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- CustomDataLayer *cl = ldata->layers + ref->layer_idx;
+ const CustomDataLayer *cl = ldata->layers + ref->layer_idx;
bool is_active = ref->layer_idx == CustomData_get_active_layer_index(ldata, CD_MLOOPUV);
DRW_cdlayer_attr_aliases_add(&vbo_id->format, "u", ldata, cl, cl == render, is_active);
diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc
index 18748627b83..e584b757a05 100644
--- a/source/blender/gpu/intern/gpu_capabilities.cc
+++ b/source/blender/gpu/intern/gpu_capabilities.cc
@@ -33,6 +33,11 @@ int GPU_max_texture_size()
return GCaps.max_texture_size;
}
+int GPU_max_texture_3d_size(void)
+{
+ return GCaps.max_texture_3d_size;
+}
+
int GPU_texture_size_with_limit(int res)
{
int size = GPU_max_texture_size();
@@ -115,6 +120,11 @@ const char *GPU_extension_get(int i)
return GCaps.extension_get ? GCaps.extension_get(i) : "\0";
}
+int GPU_max_samplers()
+{
+ return GCaps.max_samplers;
+}
+
bool GPU_mip_render_workaround()
{
return GCaps.mip_render_workaround;
@@ -161,6 +171,11 @@ bool GPU_shader_image_load_store_support()
return GCaps.shader_image_load_store_support;
}
+bool GPU_shader_draw_parameters_support()
+{
+ return GCaps.shader_draw_parameters_support;
+}
+
int GPU_max_shader_storage_buffer_bindings()
{
return GCaps.max_shader_storage_buffer_bindings;
@@ -171,6 +186,16 @@ int GPU_max_compute_shader_storage_blocks()
return GCaps.max_compute_shader_storage_blocks;
}
+int GPU_minimum_per_vertex_stride(void)
+{
+ return GCaps.minimum_per_vertex_stride;
+}
+
+bool GPU_transform_feedback_support(void)
+{
+ return GCaps.transform_feedback_support;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh
index a17dbe7f8e6..dadd14791e7 100644
--- a/source/blender/gpu/intern/gpu_capabilities_private.hh
+++ b/source/blender/gpu/intern/gpu_capabilities_private.hh
@@ -44,6 +44,7 @@ struct GPUCapabilities {
bool compute_shader_support = false;
bool shader_storage_buffer_objects_support = false;
bool shader_image_load_store_support = false;
+ bool shader_draw_parameters_support = false;
bool transform_feedback_support = false;
/* OpenGL related workarounds. */
diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc
index 82441c3c89c..2241bcf9f9b 100644
--- a/source/blender/gpu/intern/gpu_codegen.cc
+++ b/source/blender/gpu/intern/gpu_codegen.cc
@@ -11,6 +11,7 @@
#include "DNA_customdata_types.h"
#include "DNA_image_types.h"
+#include "DNA_material_types.h"
#include "BLI_ghash.h"
#include "BLI_hash_mm2a.h"
@@ -20,6 +21,7 @@
#include "PIL_time.h"
+#include "BKE_cryptomatte.hh"
#include "BKE_material.h"
#include "GPU_capabilities.h"
@@ -208,9 +210,10 @@ static std::ostream &operator<<(std::ostream &stream, const GPUConstant *input)
stream << input->type << "(";
for (int i = 0; i < input->type; i++) {
char formated_float[32];
- /* Print with the maximum precision for single precision float using scientific notation.
- * See https://stackoverflow.com/questions/16839658/#answer-21162120 */
- SNPRINTF(formated_float, "%.9g", input->vec[i]);
+ /* Use uint representation to allow exact same bit pattern even if NaN. This is because we can
+ * pass UINTs as floats for constants. */
+ const uint32_t *uint_vec = reinterpret_cast<const uint32_t *>(input->vec);
+ SNPRINTF(formated_float, "uintBitsToFloat(%uu)", uint_vec[i]);
stream << formated_float;
if (i < input->type - 1) {
stream << ", ";
@@ -237,6 +240,7 @@ class GPUCodegen {
uint32_t hash_ = 0;
BLI_HashMurmur2A hm2a_;
ListBase ubo_inputs_ = {nullptr, nullptr};
+ GPUInput *cryptomatte_input_ = nullptr;
public:
GPUCodegen(GPUMaterial *mat_, GPUNodeGraph *graph_) : mat(*mat_), graph(*graph_)
@@ -259,12 +263,15 @@ class GPUCodegen {
MEM_SAFE_FREE(output.volume);
MEM_SAFE_FREE(output.thickness);
MEM_SAFE_FREE(output.displacement);
+ MEM_SAFE_FREE(output.composite);
MEM_SAFE_FREE(output.material_functions);
+ MEM_SAFE_FREE(cryptomatte_input_);
delete create_info;
BLI_freelistN(&ubo_inputs_);
};
void generate_graphs();
+ void generate_cryptomatte();
void generate_uniform_buffer();
void generate_attribs();
void generate_resources();
@@ -280,6 +287,7 @@ class GPUCodegen {
void node_serialize(std::stringstream &eval_ss, const GPUNode *node);
char *graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link);
+ char *graph_serialize(eGPUNodeTag tree_tag);
static char *extract_c_str(std::stringstream &stream)
{
@@ -351,24 +359,47 @@ void GPUCodegen::generate_resources()
{
GPUCodegenCreateInfo &info = *create_info;
+ /* Ref. T98190: Defines are optimizations for old compilers.
+ * Might become unnecessary with EEVEE-Next. */
+ if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_CLEARCOAT)) {
+ info.define("PRINCIPLED_CLEARCOAT");
+ }
+ if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_METALLIC)) {
+ info.define("PRINCIPLED_METALLIC");
+ }
+ if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_DIELECTRIC)) {
+ info.define("PRINCIPLED_DIELECTRIC");
+ }
+ if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_GLASS)) {
+ info.define("PRINCIPLED_GLASS");
+ }
+ if (GPU_material_flag_get(&mat, GPU_MATFLAG_PRINCIPLED_ANY)) {
+ info.define("PRINCIPLED_ANY");
+ }
+
std::stringstream ss;
/* Textures. */
+ int slot = 0;
LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph.textures) {
if (tex->colorband) {
const char *name = info.name_buffer.append_sampler_name(tex->sampler_name);
- info.sampler(0, ImageType::FLOAT_1D_ARRAY, name, Frequency::BATCH);
+ info.sampler(slot++, ImageType::FLOAT_1D_ARRAY, name, Frequency::BATCH);
}
- else if (tex->tiled_mapping_name[0] != '\0') {
+ else if (tex->sky) {
const char *name = info.name_buffer.append_sampler_name(tex->sampler_name);
info.sampler(0, ImageType::FLOAT_2D_ARRAY, name, Frequency::BATCH);
+ }
+ else if (tex->tiled_mapping_name[0] != '\0') {
+ const char *name = info.name_buffer.append_sampler_name(tex->sampler_name);
+ info.sampler(slot++, ImageType::FLOAT_2D_ARRAY, name, Frequency::BATCH);
const char *name_mapping = info.name_buffer.append_sampler_name(tex->tiled_mapping_name);
- info.sampler(0, ImageType::FLOAT_1D_ARRAY, name_mapping, Frequency::BATCH);
+ info.sampler(slot++, ImageType::FLOAT_1D_ARRAY, name_mapping, Frequency::BATCH);
}
else {
const char *name = info.name_buffer.append_sampler_name(tex->sampler_name);
- info.sampler(0, ImageType::FLOAT_2D, name, Frequency::BATCH);
+ info.sampler(slot++, ImageType::FLOAT_2D, name, Frequency::BATCH);
}
}
@@ -377,11 +408,16 @@ void GPUCodegen::generate_resources()
ss << "struct NodeTree {\n";
LISTBASE_FOREACH (LinkData *, link, &ubo_inputs_) {
GPUInput *input = (GPUInput *)(link->data);
- ss << input->type << " u" << input->id << ";\n";
+ if (input->source == GPU_SOURCE_CRYPTOMATTE) {
+ ss << input->type << " crypto_hash;\n";
+ }
+ else {
+ ss << input->type << " u" << input->id << ";\n";
+ }
}
ss << "};\n\n";
- info.uniform_buf(0, "NodeTree", GPU_UBO_BLOCK_NAME, Frequency::BATCH);
+ info.uniform_buf(1, "NodeTree", GPU_UBO_BLOCK_NAME, Frequency::BATCH);
}
if (!BLI_listbase_is_empty(&graph.uniform_attrs.list)) {
@@ -393,7 +429,7 @@ void GPUCodegen::generate_resources()
/* TODO(fclem): Use the macro for length. Currently not working for EEVEE. */
/* DRW_RESOURCE_CHUNK_LEN = 512 */
- info.uniform_buf(0, "UniformAttrs", GPU_ATTRIBUTE_UBO_BLOCK_NAME "[512]", Frequency::BATCH);
+ info.uniform_buf(2, "UniformAttrs", GPU_ATTRIBUTE_UBO_BLOCK_NAME "[512]", Frequency::BATCH);
}
info.typedef_source_generated = ss.str();
@@ -500,6 +536,37 @@ char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link
return eval_c_str;
}
+char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag)
+{
+ std::stringstream eval_ss;
+ LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) {
+ if (node->tag & tree_tag) {
+ node_serialize(eval_ss, node);
+ }
+ }
+ char *eval_c_str = extract_c_str(eval_ss);
+ BLI_hash_mm2a_add(&hm2a_, (uchar *)eval_c_str, eval_ss.str().size());
+ return eval_c_str;
+}
+
+void GPUCodegen::generate_cryptomatte()
+{
+ cryptomatte_input_ = static_cast<GPUInput *>(MEM_callocN(sizeof(GPUInput), __func__));
+ cryptomatte_input_->type = GPU_FLOAT;
+ cryptomatte_input_->source = GPU_SOURCE_CRYPTOMATTE;
+
+ float material_hash = 0.0f;
+ Material *material = GPU_material_get_material(&mat);
+ if (material) {
+ blender::bke::cryptomatte::CryptomatteHash hash(material->id.name,
+ BLI_strnlen(material->id.name, MAX_NAME - 2));
+ material_hash = hash.float_encoded();
+ }
+ cryptomatte_input_->vec[0] = material_hash;
+
+ BLI_addtail(&ubo_inputs_, BLI_genericNodeN(cryptomatte_input_));
+}
+
void GPUCodegen::generate_uniform_buffer()
{
/* Extract uniform inputs. */
@@ -539,6 +606,9 @@ void GPUCodegen::generate_graphs()
output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume);
output.displacement = graph_serialize(GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement);
output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness);
+ if (!BLI_listbase_is_empty(&graph.outlink_compositor)) {
+ output.composite = graph_serialize(GPU_NODE_TAG_COMPOSITOR);
+ }
if (!BLI_listbase_is_empty(&graph.material_functions)) {
std::stringstream eval_ss;
@@ -569,13 +639,15 @@ GPUPass *GPU_generate_pass(GPUMaterial *material,
GPUCodegenCallbackFn finalize_source_cb,
void *thunk)
{
- /* Prune the unused nodes and extract attributes before compiling so the
- * generated VBOs are ready to accept the future shader. */
gpu_node_graph_prune_unused(graph);
+
+ /* Extract attributes before compiling so the generated VBOs are ready to accept the future
+ * shader. */
gpu_node_graph_finalize_uniform_attrs(graph);
GPUCodegen codegen(material, graph);
codegen.generate_graphs();
+ codegen.generate_cryptomatte();
codegen.generate_uniform_buffer();
/* Cache lookup: Reuse shaders already compiled. */
diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc
index 4a0a9ecc7f6..bcc418169b7 100644
--- a/source/blender/gpu/intern/gpu_context.cc
+++ b/source/blender/gpu/intern/gpu_context.cc
@@ -27,6 +27,7 @@
#include "gpu_batch_private.hh"
#include "gpu_context_private.hh"
#include "gpu_matrix_private.h"
+#include "gpu_private.h"
#ifdef WITH_OPENGL_BACKEND
# include "gl_backend.hh"
@@ -43,17 +44,27 @@ using namespace blender::gpu;
static thread_local Context *active_ctx = nullptr;
+static std::mutex backend_users_mutex;
+static int num_backend_users = 0;
+
+static void gpu_backend_create();
+static void gpu_backend_discard();
+
/* -------------------------------------------------------------------- */
/** \name gpu::Context methods
* \{ */
namespace blender::gpu {
+int Context::context_counter = 0;
Context::Context()
{
thread_ = pthread_self();
is_active_ = false;
matrix_state = GPU_matrix_state_create();
+
+ context_id = Context::context_counter;
+ Context::context_counter++;
}
Context::~Context()
@@ -85,9 +96,13 @@ Context *Context::get()
GPUContext *GPU_context_create(void *ghost_window)
{
- if (GPUBackend::get() == nullptr) {
- /* TODO: move where it make sense. */
- GPU_backend_init(GPU_BACKEND_OPENGL);
+ {
+ std::scoped_lock lock(backend_users_mutex);
+ if (num_backend_users == 0) {
+ /* Automatically create backend when first context is created. */
+ gpu_backend_create();
+ }
+ num_backend_users++;
}
Context *ctx = GPUBackend::get()->context_alloc(ghost_window);
@@ -101,6 +116,16 @@ void GPU_context_discard(GPUContext *ctx_)
Context *ctx = unwrap(ctx_);
delete ctx;
active_ctx = nullptr;
+
+ {
+ std::scoped_lock lock(backend_users_mutex);
+ num_backend_users--;
+ BLI_assert(num_backend_users >= 0);
+ if (num_backend_users == 0) {
+ /* Discard backend when last context is discarded. */
+ gpu_backend_discard();
+ }
+ }
}
void GPU_context_active_set(GPUContext *ctx_)
@@ -191,11 +216,12 @@ void GPU_render_step()
/** \name Backend selection
* \{ */
+static const eGPUBackendType g_backend_type = GPU_BACKEND_OPENGL;
static GPUBackend *g_backend = nullptr;
-bool GPU_backend_supported(eGPUBackendType type)
+bool GPU_backend_supported(void)
{
- switch (type) {
+ switch (g_backend_type) {
case GPU_BACKEND_OPENGL:
#ifdef WITH_OPENGL_BACKEND
return true;
@@ -214,12 +240,12 @@ bool GPU_backend_supported(eGPUBackendType type)
}
}
-void GPU_backend_init(eGPUBackendType backend_type)
+static void gpu_backend_create()
{
BLI_assert(g_backend == nullptr);
- BLI_assert(GPU_backend_supported(backend_type));
+ BLI_assert(GPU_backend_supported());
- switch (backend_type) {
+ switch (g_backend_type) {
#ifdef WITH_OPENGL_BACKEND
case GPU_BACKEND_OPENGL:
g_backend = new GLBackend;
@@ -236,10 +262,15 @@ void GPU_backend_init(eGPUBackendType backend_type)
}
}
-void GPU_backend_exit()
+void gpu_backend_delete_resources()
+{
+ BLI_assert(g_backend);
+ g_backend->delete_resources();
+}
+
+void gpu_backend_discard()
{
- /* TODO: assert no resource left. Currently UI textures are still not freed in their context
- * correctly. */
+ /* TODO: assert no resource left. */
delete g_backend;
g_backend = nullptr;
}
diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh
index 9cdf0075632..2217e5262ed 100644
--- a/source/blender/gpu/intern/gpu_context_private.hh
+++ b/source/blender/gpu/intern/gpu_context_private.hh
@@ -28,11 +28,11 @@ namespace blender::gpu {
class Context {
public:
/** State management */
- Shader *shader = NULL;
- FrameBuffer *active_fb = NULL;
- GPUMatrixState *matrix_state = NULL;
- StateManager *state_manager = NULL;
- Immediate *imm = NULL;
+ Shader *shader = nullptr;
+ FrameBuffer *active_fb = nullptr;
+ GPUMatrixState *matrix_state = nullptr;
+ StateManager *state_manager = nullptr;
+ Immediate *imm = nullptr;
/**
* All 4 window frame-buffers.
@@ -41,18 +41,26 @@ class Context {
* Front frame-buffers contains (in principle, but not always) the last frame color.
* Default frame-buffer is back_left.
*/
- FrameBuffer *back_left = NULL;
- FrameBuffer *front_left = NULL;
- FrameBuffer *back_right = NULL;
- FrameBuffer *front_right = NULL;
+ FrameBuffer *back_left = nullptr;
+ FrameBuffer *front_left = nullptr;
+ FrameBuffer *back_right = nullptr;
+ FrameBuffer *front_right = nullptr;
DebugStack debug_stack;
+ /* GPUContext counter used to assign a unique ID to each GPUContext.
+ * NOTE(Metal): This is required by the Metal Backend, as a bug exists in the global OS shader
+ * cache wherein compilation of identical source from two distinct threads can result in an
+ * invalid cache collision, result in a broken shader object. Appending the unique context ID
+ * onto compiled sources ensures the source hashes are different. */
+ static int context_counter;
+ int context_id = 0;
+
protected:
/** Thread on which this context is active. */
pthread_t thread_;
bool is_active_;
- /** Avoid including GHOST headers. Can be NULL for off-screen contexts. */
+ /** Avoid including GHOST headers. Can be nullptr for off-screen contexts. */
void *ghost_window_;
public:
diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc
index 8d93e49d588..4182ce75eac 100644
--- a/source/blender/gpu/intern/gpu_framebuffer.cc
+++ b/source/blender/gpu/intern/gpu_framebuffer.cc
@@ -368,6 +368,11 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *gpu_fb,
}
}
+void GPU_framebuffer_default_size(GPUFrameBuffer *gpu_fb, int width, int height)
+{
+ unwrap(gpu_fb)->size_set(width, height);
+}
+
/* ---------- Viewport & Scissor Region ----------- */
void GPU_framebuffer_viewport_set(GPUFrameBuffer *gpu_fb, int x, int y, int width, int height)
diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh
index 8cecc6b8b15..6e5d9518bfc 100644
--- a/source/blender/gpu/intern/gpu_framebuffer_private.hh
+++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh
@@ -74,9 +74,9 @@ class FrameBuffer {
/** Set of texture attachments to render to. DEPTH and DEPTH_STENCIL are mutually exclusive. */
GPUAttachment attachments_[GPU_FB_MAX_ATTACHMENT];
/** Is true if internal representation need to be updated. */
- bool dirty_attachments_;
+ bool dirty_attachments_ = true;
/** Size of attachment textures. */
- int width_, height_;
+ int width_ = 0, height_ = 0;
/** Debug name. */
char name_[DEBUG_NAME_LEN];
/** Frame-buffer state. */
diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc
index 8f04b1c2dfe..3b4accf9cc5 100644
--- a/source/blender/gpu/intern/gpu_immediate.cc
+++ b/source/blender/gpu/intern/gpu_immediate.cc
@@ -131,15 +131,12 @@ static void wide_line_workaround_start(GPUPrimType prim_type)
case GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR:
polyline_sh = GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR;
break;
- case GPU_SHADER_2D_UNIFORM_COLOR:
case GPU_SHADER_3D_UNIFORM_COLOR:
polyline_sh = GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR;
break;
- case GPU_SHADER_2D_FLAT_COLOR:
case GPU_SHADER_3D_FLAT_COLOR:
polyline_sh = GPU_SHADER_3D_POLYLINE_FLAT_COLOR;
break;
- case GPU_SHADER_2D_SMOOTH_COLOR:
case GPU_SHADER_3D_SMOOTH_COLOR:
polyline_sh = GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR;
break;
diff --git a/source/blender/gpu/intern/gpu_immediate_private.hh b/source/blender/gpu/intern/gpu_immediate_private.hh
index 6c50fa01071..74ebbdc7ae3 100644
--- a/source/blender/gpu/intern/gpu_immediate_private.hh
+++ b/source/blender/gpu/intern/gpu_immediate_private.hh
@@ -19,7 +19,7 @@ namespace blender::gpu {
class Immediate {
public:
/** Pointer to the mapped buffer data for the current vertex. */
- uchar *vertex_data = NULL;
+ uchar *vertex_data = nullptr;
/** Current vertex index. */
uint vertex_idx = 0;
/** Length of the buffer in vertices. */
@@ -32,12 +32,12 @@ class Immediate {
/** Current draw call specification. */
GPUPrimType prim_type = GPU_PRIM_NONE;
GPUVertFormat vertex_format = {};
- GPUShader *shader = NULL;
+ GPUShader *shader = nullptr;
/** Enforce strict vertex count (disabled when using #immBeginAtMost). */
bool strict_vertex_len = true;
/** Batch in construction when using #immBeginBatch. */
- GPUBatch *batch = NULL;
+ GPUBatch *batch = nullptr;
/** Wide Line workaround. */
diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c
index 5233ff2dbf6..743bc058b45 100644
--- a/source/blender/gpu/intern/gpu_immediate_util.c
+++ b/source/blender/gpu/intern/gpu_immediate_util.c
@@ -121,7 +121,7 @@ void immRecti_complete(int x1, int y1, int x2, int y2, const float color[4])
{
GPUVertFormat *format = immVertexFormat();
uint pos = add_attr(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4fv(color);
immRecti(pos, x1, y1, x2, y2);
immUnbindProgram();
@@ -239,9 +239,9 @@ void imm_draw_circle_partial_wire_2d(
}
void imm_draw_circle_partial_wire_3d(
- uint pos, float x, float y, float z, float rad, int nsegments, float start, float sweep)
+ uint pos, float x, float y, float z, float radius, int nsegments, float start, float sweep)
{
- imm_draw_circle_partial_3d(GPU_PRIM_LINE_STRIP, pos, x, y, z, rad, nsegments, start, sweep);
+ imm_draw_circle_partial_3d(GPU_PRIM_LINE_STRIP, pos, x, y, z, radius, nsegments, start, sweep);
}
static void imm_draw_disk_partial(GPUPrimType prim_type,
diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc
index 146461d1dfb..3a66f547403 100644
--- a/source/blender/gpu/intern/gpu_index_buffer.cc
+++ b/source/blender/gpu/intern/gpu_index_buffer.cc
@@ -16,6 +16,8 @@
#include "gpu_index_buffer_private.hh"
+#include "GPU_platform.h"
+
#include <cstring>
#define KEEP_SINGLE_COPY 1
@@ -40,6 +42,28 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder,
builder->index_min = UINT32_MAX;
builder->index_max = 0;
builder->prim_type = prim_type;
+
+#ifdef __APPLE__
+ /* Only encode restart indices for restart-compatible primitive types.
+ * Resolves out-of-bounds read error on macOS. Using 0-index will ensure
+ * degenerative primitives when skipping primitives is required and will
+ * incur no additional performance cost for rendering. */
+ if (GPU_type_matches_ex(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_METAL)) {
+ /* We will still use restart-indices for point primitives and then
+ * patch these during IndexBuf::init, as we cannot benefit from degenerative
+ * primitives to eliminate these. */
+ builder->restart_index_value = (is_restart_compatible(prim_type) ||
+ prim_type == GPU_PRIM_POINTS) ?
+ RESTART_INDEX :
+ 0;
+ }
+ else {
+ builder->restart_index_value = RESTART_INDEX;
+ }
+#else
+ builder->restart_index_value = RESTART_INDEX;
+#endif
+ builder->uses_restart_indices = false;
builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data");
}
@@ -94,7 +118,8 @@ void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *builder)
assert(builder->data != nullptr);
assert(builder->index_len < builder->max_index_len);
#endif
- builder->data[builder->index_len++] = RESTART_INDEX;
+ builder->data[builder->index_len++] = builder->restart_index_value;
+ builder->uses_restart_indices = true;
}
void GPU_indexbuf_add_point_vert(GPUIndexBufBuilder *builder, uint v)
@@ -186,8 +211,9 @@ 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;
+ builder->data[elem++] = builder->restart_index_value;
builder->index_len = MAX2(builder->index_len, elem);
+ builder->uses_restart_indices = true;
}
void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem)
@@ -195,9 +221,10 @@ void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem)
BLI_assert(builder->prim_type == GPU_PRIM_LINES);
BLI_assert((elem + 1) * 2 <= builder->max_index_len);
uint idx = elem * 2;
- builder->data[idx++] = RESTART_INDEX;
- builder->data[idx++] = RESTART_INDEX;
+ builder->data[idx++] = builder->restart_index_value;
+ builder->data[idx++] = builder->restart_index_value;
builder->index_len = MAX2(builder->index_len, idx);
+ builder->uses_restart_indices = true;
}
void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
@@ -205,10 +232,11 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem)
BLI_assert(builder->prim_type == GPU_PRIM_TRIS);
BLI_assert((elem + 1) * 3 <= builder->max_index_len);
uint idx = elem * 3;
- builder->data[idx++] = RESTART_INDEX;
- builder->data[idx++] = RESTART_INDEX;
- builder->data[idx++] = RESTART_INDEX;
+ builder->data[idx++] = builder->restart_index_value;
+ builder->data[idx++] = builder->restart_index_value;
+ builder->data[idx++] = builder->restart_index_value;
builder->index_len = MAX2(builder->index_len, idx);
+ builder->uses_restart_indices = true;
}
/** \} */
@@ -226,7 +254,12 @@ IndexBuf::~IndexBuf()
}
}
-void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint max_index)
+void IndexBuf::init(uint indices_len,
+ uint32_t *indices,
+ uint min_index,
+ uint max_index,
+ GPUPrimType prim_type,
+ bool uses_restart_indices)
{
is_init_ = true;
data_ = indices;
@@ -234,6 +267,21 @@ void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint ma
index_len_ = indices_len;
is_empty_ = min_index > max_index;
+ /* Patch index buffer to remove restart indices from
+ * non-restart-compatible primitive types. Restart indices
+ * are situationally added to selectively hide vertices.
+ * Metal does not support restart-indices for non-restart-compatible
+ * types, as such we should remove these indices.
+ *
+ * We only need to perform this for point primitives, as
+ * line primitives/triangle primitives can use index 0 for all
+ * vertices to create a degenerative primitive, where all
+ * vertices share the same index and skip rendering via HW
+ * culling. */
+ if (prim_type == GPU_PRIM_POINTS && uses_restart_indices) {
+ this->strip_restart_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. */
@@ -243,7 +291,18 @@ void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint ma
if (range <= 0xFFFF) {
index_type_ = GPU_INDEX_U16;
- this->squeeze_indices_short(min_index, max_index);
+ bool do_clamp_indices = false;
+# ifdef __APPLE__
+ /* NOTE: For the Metal Backend, we use degenerative primitives to hide vertices
+ * which are not restart compatible. When this is done, we need to ensure
+ * that compressed index ranges clamp all index values within the valid
+ * range, rather than maximally clamping against the USHORT restart index
+ * value of 0xFFFFu, as this will cause an out-of-bounds read during
+ * vertex assembly. */
+ do_clamp_indices = GPU_type_matches_ex(
+ GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_METAL);
+# endif
+ this->squeeze_indices_short(min_index, max_index, prim_type, do_clamp_indices);
}
#endif
}
@@ -302,7 +361,10 @@ uint IndexBuf::index_range(uint *r_min, uint *r_max)
return max_value - min_value;
}
-void IndexBuf::squeeze_indices_short(uint min_idx, uint max_idx)
+void IndexBuf::squeeze_indices_short(uint min_idx,
+ uint max_idx,
+ GPUPrimType prim_type,
+ bool clamp_indices_in_range)
{
/* data will never be *larger* than builder->data...
* converting in place to avoid extra allocation */
@@ -311,8 +373,22 @@ void IndexBuf::squeeze_indices_short(uint min_idx, uint max_idx)
if (max_idx >= 0xFFFF) {
index_base_ = min_idx;
+ /* NOTE: When using restart_index=0 for degenerative primitives indices,
+ * the compressed index will go below zero and wrap around when min_idx > 0.
+ * In order to ensure the resulting index is still within range, we instead
+ * clamp index to the maximum within the index range.
+ *
+ * `clamp_max_idx` represents the maximum possible index to clamp against. If primitive is
+ * restart-compatible, we can just clamp against the primitive-restart value, otherwise, we
+ * must assign to a valid index within the range.
+ *
+ * NOTE: For OpenGL we skip this by disabling clamping, as we still need to use
+ * restart index values for point primitives to disable rendering. */
+ uint16_t clamp_max_idx = (is_restart_compatible(prim_type) || !clamp_indices_in_range) ?
+ 0xFFFFu :
+ (max_idx - min_idx);
for (uint i = 0; i < index_len_; i++) {
- ushort_idx[i] = (uint16_t)MIN2(0xFFFF, uint_idx[i] - min_idx);
+ ushort_idx[i] = (uint16_t)MIN2(clamp_max_idx, uint_idx[i] - min_idx);
}
}
else {
@@ -363,7 +439,12 @@ 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, builder->index_min, builder->index_max);
+ unwrap(elem)->init(builder->index_len,
+ builder->data,
+ builder->index_min,
+ builder->index_max,
+ builder->prim_type,
+ builder->uses_restart_indices);
builder->data = nullptr;
}
diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh
index 6ce62ae852e..4099d6641a6 100644
--- a/source/blender/gpu/intern/gpu_index_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh
@@ -59,7 +59,12 @@ class IndexBuf {
IndexBuf(){};
virtual ~IndexBuf();
- void init(uint indices_len, uint32_t *indices, uint min_index, uint max_index);
+ void init(uint indices_len,
+ uint32_t *indices,
+ uint min_index,
+ uint max_index,
+ GPUPrimType prim_type,
+ bool uses_restart_indices);
void init_subrange(IndexBuf *elem_src, uint start, uint length);
void init_build_on_device(uint index_len);
@@ -70,6 +75,14 @@ class IndexBuf {
* They can lead to graphical glitches on some systems. (See T96892) */
return is_empty_ ? 0 : index_len_;
}
+ uint32_t index_start_get() const
+ {
+ return index_start_;
+ }
+ uint32_t index_base_get() const
+ {
+ return index_base_;
+ }
/* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */
size_t size_get() const
{
@@ -91,8 +104,12 @@ class IndexBuf {
virtual void update_sub(uint start, uint len, const void *data) = 0;
private:
- inline void squeeze_indices_short(uint min_idx, uint max_idx);
+ inline void squeeze_indices_short(uint min_idx,
+ uint max_idx,
+ GPUPrimType prim_type,
+ bool clamp_indices_in_range);
inline uint index_range(uint *r_min, uint *r_max);
+ virtual void strip_restart_indices() = 0;
};
/* Syntactic sugar. */
diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c
index 062614fb5cb..34b355eefaf 100644
--- a/source/blender/gpu/intern/gpu_init_exit.c
+++ b/source/blender/gpu/intern/gpu_init_exit.c
@@ -55,6 +55,8 @@ void GPU_exit(void)
gpu_shader_dependency_exit();
gpu_shader_create_info_exit();
+ gpu_backend_delete_resources();
+
initialized = false;
}
diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c
index 4d3ea3e0c99..96809db1587 100644
--- a/source/blender/gpu/intern/gpu_material.c
+++ b/source/blender/gpu/intern/gpu_material.c
@@ -41,12 +41,18 @@
/* Structs */
#define MAX_COLOR_BAND 128
+#define MAX_GPU_SKIES 8
typedef struct GPUColorBandBuilder {
float pixels[MAX_COLOR_BAND][CM_TABLE + 1][4];
int current_layer;
} GPUColorBandBuilder;
+typedef struct GPUSkyBuilder {
+ float pixels[MAX_GPU_SKIES][GPU_SKY_WIDTH * GPU_SKY_HEIGHT][4];
+ int current_layer;
+} GPUSkyBuilder;
+
struct GPUMaterial {
/* Contains GPUShader and source code for deferred compilation.
* Can be shared between similar material (i.e: sharing same nodetree topology). */
@@ -73,6 +79,10 @@ struct GPUMaterial {
GPUTexture *coba_tex;
/** Builder for coba_tex. */
GPUColorBandBuilder *coba_builder;
+ /** 2D Texture array containing all sky textures. */
+ GPUTexture *sky_tex;
+ /** Builder for sky_tex. */
+ GPUSkyBuilder *sky_builder;
/* Low level node graph(s). Also contains resources needed by the material. */
GPUNodeGraph graph;
@@ -91,11 +101,42 @@ struct GPUMaterial {
#ifndef NDEBUG
char name[64];
+#else
+ char name[16];
#endif
};
/* Functions */
+GPUTexture **gpu_material_sky_texture_layer_set(
+ GPUMaterial *mat, int width, int height, const float *pixels, float *row)
+{
+ /* In order to put all sky textures into one 2D array texture,
+ * we need them to be the same size. */
+ BLI_assert(width == GPU_SKY_WIDTH);
+ BLI_assert(height == GPU_SKY_HEIGHT);
+ UNUSED_VARS_NDEBUG(width, height);
+
+ if (mat->sky_builder == NULL) {
+ mat->sky_builder = MEM_mallocN(sizeof(GPUSkyBuilder), "GPUSkyBuilder");
+ mat->sky_builder->current_layer = 0;
+ }
+
+ int layer = mat->sky_builder->current_layer;
+ *row = (float)layer;
+
+ if (*row == MAX_GPU_SKIES) {
+ printf("Too many sky textures in shader!\n");
+ }
+ else {
+ float *dst = (float *)mat->sky_builder->pixels[layer];
+ memcpy(dst, pixels, sizeof(float) * GPU_SKY_WIDTH * GPU_SKY_HEIGHT * 4);
+ mat->sky_builder->current_layer += 1;
+ }
+
+ return &mat->sky_tex;
+}
+
GPUTexture **gpu_material_ramp_texture_row_set(GPUMaterial *mat,
int size,
float *pixels,
@@ -141,7 +182,25 @@ static void gpu_material_ramp_texture_build(GPUMaterial *mat)
mat->coba_builder = NULL;
}
-static void gpu_material_free_single(GPUMaterial *material)
+static void gpu_material_sky_texture_build(GPUMaterial *mat)
+{
+ if (mat->sky_builder == NULL) {
+ return;
+ }
+
+ mat->sky_tex = GPU_texture_create_2d_array("mat_sky",
+ GPU_SKY_WIDTH,
+ GPU_SKY_HEIGHT,
+ mat->sky_builder->current_layer,
+ 1,
+ GPU_RGBA32F,
+ (float *)mat->sky_builder->pixels);
+
+ MEM_freeN(mat->sky_builder);
+ mat->sky_builder = NULL;
+}
+
+void GPU_material_free_single(GPUMaterial *material)
{
bool do_free = atomic_sub_and_fetch_uint32(&material->refcount, 1) == 0;
if (!do_free) {
@@ -159,6 +218,9 @@ static void gpu_material_free_single(GPUMaterial *material)
if (material->coba_tex != NULL) {
GPU_texture_free(material->coba_tex);
}
+ if (material->sky_tex != NULL) {
+ GPU_texture_free(material->sky_tex);
+ }
if (material->sss_profile != NULL) {
GPU_uniformbuf_free(material->sss_profile);
}
@@ -173,7 +235,7 @@ void GPU_material_free(ListBase *gpumaterial)
LISTBASE_FOREACH (LinkData *, link, gpumaterial) {
GPUMaterial *material = link->data;
DRW_deferred_shader_remove(material);
- gpu_material_free_single(material);
+ GPU_material_free_single(material);
}
BLI_freelistN(gpumaterial);
}
@@ -193,6 +255,11 @@ GPUShader *GPU_material_get_shader(GPUMaterial *material)
return material->pass ? GPU_pass_shader_get(material->pass) : NULL;
}
+const char *GPU_material_get_name(GPUMaterial *material)
+{
+ return material->name;
+}
+
Material *GPU_material_get_material(GPUMaterial *material)
{
return material->ma;
@@ -205,12 +272,7 @@ GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material)
void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs)
{
-#ifndef NDEBUG
- const char *name = material->name;
-#else
- const char *name = "Material";
-#endif
- material->ubo = GPU_uniformbuf_create_from_list(inputs, name);
+ material->ubo = GPU_uniformbuf_create_from_list(inputs, material->name);
}
ListBase GPU_material_attributes(GPUMaterial *material)
@@ -223,9 +285,9 @@ ListBase GPU_material_textures(GPUMaterial *material)
return material->graph.textures;
}
-GPUUniformAttrList *GPU_material_uniform_attributes(GPUMaterial *material)
+const GPUUniformAttrList *GPU_material_uniform_attributes(const GPUMaterial *material)
{
- GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
+ const GPUUniformAttrList *attrs = &material->graph.uniform_attrs;
return attrs->count > 0 ? attrs : NULL;
}
@@ -538,6 +600,13 @@ void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link,
BLI_addtail(&material->graph.outlink_aovs, aov_link);
}
+void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link)
+{
+ GPUNodeGraphOutputLink *compositor_link = MEM_callocN(sizeof(GPUNodeGraphOutputLink), __func__);
+ compositor_link->outlink = link;
+ BLI_addtail(&material->graph.outlink_compositor, compositor_link);
+}
+
char *GPU_material_split_sub_function(GPUMaterial *material,
eGPUType return_type,
GPUNodeLink **link)
@@ -665,11 +734,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
mat->graph.used_libraries = BLI_gset_new(
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries");
mat->refcount = 1;
-#ifndef NDEBUG
STRNCPY(mat->name, name);
-#else
- UNUSED_VARS(name);
-#endif
if (is_lookdev) {
mat->flag |= GPU_MATFLAG_LOOKDEV_HACK;
}
@@ -679,6 +744,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene,
ntreeGPUMaterialNodes(localtree, mat);
gpu_material_ramp_texture_build(mat);
+ gpu_material_sky_texture_build(mat);
{
/* Create source code and search pass cache for an already compiled version. */
@@ -721,7 +787,7 @@ void GPU_material_acquire(GPUMaterial *mat)
void GPU_material_release(GPUMaterial *mat)
{
- gpu_material_free_single(mat);
+ GPU_material_free_single(mat);
}
void GPU_material_compile(GPUMaterial *mat)
@@ -772,3 +838,42 @@ void GPU_materials_free(Main *bmain)
// BKE_world_defaults_free_gpu();
BKE_material_defaults_free_gpu();
}
+
+GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_function_cb,
+ GPUCodegenCallbackFn generate_code_function_cb,
+ void *thunk)
+{
+ /* Allocate a new material and its material graph, and initialize its reference count. */
+ GPUMaterial *material = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial");
+ material->graph.used_libraries = BLI_gset_new(
+ BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries");
+ material->refcount = 1;
+
+ /* Construct the material graph by adding and linking the necessary GPU material nodes. */
+ construct_function_cb(thunk, material);
+
+ /* Create and initialize the texture storing color bands used by Ramp and Curve nodes. */
+ gpu_material_ramp_texture_build(material);
+
+ /* Lookup an existing pass in the cache or generate a new one. */
+ material->pass = GPU_generate_pass(material, &material->graph, generate_code_function_cb, thunk);
+
+ /* The pass already exists in the pass cache but its shader already failed to compile. */
+ if (material->pass == NULL) {
+ material->status = GPU_MAT_FAILED;
+ gpu_node_graph_free(&material->graph);
+ return material;
+ }
+
+ /* The pass already exists in the pass cache and its shader is already compiled. */
+ GPUShader *shader = GPU_pass_shader_get(material->pass);
+ if (shader != NULL) {
+ material->status = GPU_MAT_SUCCESS;
+ gpu_node_graph_free_nodes(&material->graph);
+ return material;
+ }
+
+ /* The material was created successfully but still needs to be compiled. */
+ material->status = GPU_MAT_CREATED;
+ return material;
+}
diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c
index 1338c5312c2..a305413905b 100644
--- a/source/blender/gpu/intern/gpu_node_graph.c
+++ b/source/blender/gpu/intern/gpu_node_graph.c
@@ -75,9 +75,26 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
if (STR_ELEM(name, "set_value", "set_rgb", "set_rgba") && (input->type == type)) {
input = MEM_dupallocN(outnode->inputs.first);
+
+ switch (input->source) {
+ case GPU_SOURCE_ATTR:
+ input->attr->users++;
+ break;
+ case GPU_SOURCE_UNIFORM_ATTR:
+ input->uniform_attr->users++;
+ break;
+ case GPU_SOURCE_TEX:
+ case GPU_SOURCE_TEX_TILED_MAPPING:
+ input->texture->users++;
+ break;
+ default:
+ break;
+ }
+
if (input->link) {
input->link->users++;
}
+
BLI_addtail(&node->inputs, input);
return;
}
@@ -95,6 +112,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType
break;
case GPU_NODE_LINK_IMAGE:
case GPU_NODE_LINK_IMAGE_TILED:
+ case GPU_NODE_LINK_IMAGE_SKY:
case GPU_NODE_LINK_COLORBAND:
input->source = GPU_SOURCE_TEX;
input->texture = link->texture;
@@ -162,7 +180,7 @@ static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type)
* This is called for the input/output sockets that are not connected.
*/
static GPUNodeLink *gpu_uniformbuffer_link(GPUMaterial *mat,
- bNode *node,
+ const bNode *node,
GPUNodeStack *stack,
const int index,
const eNodeSocketInOut in_out)
@@ -179,39 +197,25 @@ static GPUNodeLink *gpu_uniformbuffer_link(GPUMaterial *mat,
BLI_assert(socket != NULL);
BLI_assert(socket->in_out == in_out);
- if ((socket->flag & SOCK_HIDE_VALUE) == 0) {
- GPUNodeLink *link;
- switch (socket->type) {
- case SOCK_FLOAT: {
- bNodeSocketValueFloat *socket_data = socket->default_value;
- link = GPU_uniform(&socket_data->value);
- break;
- }
- case SOCK_VECTOR: {
- bNodeSocketValueVector *socket_data = socket->default_value;
- link = GPU_uniform(socket_data->value);
- break;
- }
- case SOCK_RGBA: {
- bNodeSocketValueRGBA *socket_data = socket->default_value;
- link = GPU_uniform(socket_data->value);
- break;
- }
- default:
- return NULL;
- break;
- }
+ if (socket->flag & SOCK_HIDE_VALUE) {
+ return NULL;
+ }
- if (in_out == SOCK_IN) {
- GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link);
- }
- return link;
+ if (!ELEM(socket->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA)) {
+ return NULL;
}
- return NULL;
+
+ GPUNodeLink *link = GPU_uniform(stack->vec);
+
+ if (in_out == SOCK_IN) {
+ GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link);
+ }
+
+ return link;
}
static void gpu_node_input_socket(
- GPUMaterial *material, bNode *bnode, GPUNode *node, GPUNodeStack *sock, const int index)
+ GPUMaterial *material, const bNode *bnode, GPUNode *node, GPUNodeStack *sock, const int index)
{
if (sock->link) {
gpu_node_input_link(node, sock->link, sock->type);
@@ -289,7 +293,7 @@ struct GHash *GPU_uniform_attr_list_hash_new(const char *info)
return BLI_ghash_new(uniform_attr_list_hash, uniform_attr_list_cmp, info);
}
-void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, GPUUniformAttrList *src)
+void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, const GPUUniformAttrList *src)
{
dest->count = src->count;
dest->hash_code = src->hash_code;
@@ -317,24 +321,20 @@ void gpu_node_graph_finalize_uniform_attrs(GPUNodeGraph *graph)
LISTBASE_FOREACH (GPUUniformAttr *, attr, &attrs->list) {
attr->id = next_id++;
-
- attrs->hash_code ^= BLI_ghashutil_strhash_p(attr->name);
-
- if (attr->use_dupli) {
- attrs->hash_code ^= BLI_ghashutil_uinthash(attr->id);
- }
+ attrs->hash_code ^= BLI_ghashutil_uinthash(attr->hash_code + (1 << (attr->id + 1)));
}
}
/* Attributes and Textures */
-static char attr_prefix_get(eCustomDataType type)
+static char attr_prefix_get(GPUMaterialAttribute *attr)
{
- switch (type) {
+ if (attr->is_default_color) {
+ return 'c';
+ }
+ switch (attr->type) {
case CD_TANGENT:
return 't';
- case CD_MCOL:
- return 'c';
case CD_AUTO_FROM_NAME:
return 'a';
case CD_HAIRLENGTH:
@@ -353,7 +353,7 @@ static void attr_input_name(GPUMaterialAttribute *attr)
STRNCPY(attr->input_name, "orco");
}
else {
- attr->input_name[0] = attr_prefix_get(attr->type);
+ attr->input_name[0] = attr_prefix_get(attr);
attr->input_name[1] = '\0';
if (attr->name[0] != '\0') {
/* XXX FIXME: see notes in mesh_render_data_create() */
@@ -365,13 +365,15 @@ static void attr_input_name(GPUMaterialAttribute *attr)
/** Add a new varying attribute of given type and name. Returns NULL if out of slots. */
static GPUMaterialAttribute *gpu_node_graph_add_attribute(GPUNodeGraph *graph,
eCustomDataType type,
- const char *name)
+ const char *name,
+ const bool is_default_color)
{
/* Find existing attribute. */
int num_attributes = 0;
GPUMaterialAttribute *attr = graph->attributes.first;
for (; attr; attr = attr->next) {
- if (attr->type == type && STREQ(attr->name, name)) {
+ if (attr->type == type && STREQ(attr->name, name) &&
+ attr->is_default_color == is_default_color) {
break;
}
num_attributes++;
@@ -380,6 +382,7 @@ static GPUMaterialAttribute *gpu_node_graph_add_attribute(GPUNodeGraph *graph,
/* Add new requested attribute if it's within GPU limits. */
if (attr == NULL) {
attr = MEM_callocN(sizeof(*attr), __func__);
+ attr->is_default_color = is_default_color;
attr->type = type;
STRNCPY(attr->name, name);
attr_input_name(attr);
@@ -413,7 +416,13 @@ static GPUUniformAttr *gpu_node_graph_add_uniform_attribute(GPUNodeGraph *graph,
if (attr == NULL && attrs->count < GPU_MAX_UNIFORM_ATTR) {
attr = MEM_callocN(sizeof(*attr), __func__);
STRNCPY(attr->name, name);
+ {
+ char attr_name_esc[sizeof(attr->name) * 2];
+ BLI_str_escape(attr_name_esc, attr->name, sizeof(attr_name_esc));
+ SNPRINTF(attr->name_id_prop, "[\"%s\"]", attr_name_esc);
+ }
attr->use_dupli = use_dupli;
+ attr->hash_code = BLI_ghashutil_strhash_p(attr->name) << 1 | (attr->use_dupli ? 0 : 1);
attr->id = -1;
BLI_addtail(&attrs->list, attr);
attrs->count++;
@@ -430,6 +439,7 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph,
Image *ima,
ImageUser *iuser,
struct GPUTexture **colorband,
+ struct GPUTexture **sky,
GPUNodeLinkType link_type,
eGPUSamplerState sampler_state)
{
@@ -437,7 +447,8 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph,
int num_textures = 0;
GPUMaterialTexture *tex = graph->textures.first;
for (; tex; tex = tex->next) {
- if (tex->ima == ima && tex->colorband == colorband && tex->sampler_state == sampler_state) {
+ if (tex->ima == ima && tex->colorband == colorband && tex->sky == sky &&
+ tex->sampler_state == sampler_state) {
break;
}
num_textures++;
@@ -452,6 +463,7 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph,
tex->iuser_available = true;
}
tex->colorband = colorband;
+ tex->sky = sky;
tex->sampler_state = sampler_state;
BLI_snprintf(tex->sampler_name, sizeof(tex->sampler_name), "samp%d", num_textures);
if (ELEM(link_type, GPU_NODE_LINK_IMAGE_TILED, GPU_NODE_LINK_IMAGE_TILED_MAPPING)) {
@@ -471,7 +483,7 @@ static GPUMaterialTexture *gpu_node_graph_add_texture(GPUNodeGraph *graph,
GPUNodeLink *GPU_attribute(GPUMaterial *mat, const eCustomDataType type, const char *name)
{
GPUNodeGraph *graph = gpu_material_node_graph(mat);
- GPUMaterialAttribute *attr = gpu_node_graph_add_attribute(graph, type, name);
+ GPUMaterialAttribute *attr = gpu_node_graph_add_attribute(graph, type, name, false);
if (type == CD_ORCO) {
/* OPTI: orco might be computed from local positions and needs object infos. */
@@ -490,6 +502,21 @@ GPUNodeLink *GPU_attribute(GPUMaterial *mat, const eCustomDataType type, const c
return link;
}
+GPUNodeLink *GPU_attribute_default_color(GPUMaterial *mat)
+{
+ GPUNodeGraph *graph = gpu_material_node_graph(mat);
+ GPUMaterialAttribute *attr = gpu_node_graph_add_attribute(graph, CD_AUTO_FROM_NAME, "", true);
+ if (attr == NULL) {
+ static const float zero_data[GPU_MAX_CONSTANT_DATA] = {0.0f};
+ return GPU_constant(zero_data);
+ }
+ attr->is_default_color = true;
+ GPUNodeLink *link = gpu_node_link_create();
+ link->link_type = GPU_NODE_LINK_ATTR;
+ link->attr = attr;
+ return link;
+}
+
GPUNodeLink *GPU_attribute_with_default(GPUMaterial *mat,
const eCustomDataType type,
const char *name,
@@ -502,16 +529,21 @@ GPUNodeLink *GPU_attribute_with_default(GPUMaterial *mat,
return link;
}
-GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat, const char *name, bool use_dupli)
+GPUNodeLink *GPU_uniform_attribute(GPUMaterial *mat,
+ const char *name,
+ bool use_dupli,
+ uint32_t *r_hash)
{
GPUNodeGraph *graph = gpu_material_node_graph(mat);
GPUUniformAttr *attr = gpu_node_graph_add_uniform_attribute(graph, name, use_dupli);
/* Dummy fallback if out of slots. */
if (attr == NULL) {
+ *r_hash = 0;
static const float zero_data[GPU_MAX_CONSTANT_DATA] = {0.0f};
return GPU_constant(zero_data);
}
+ *r_hash = attr->hash_code;
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_UNIFORM_ATTR;
@@ -552,7 +584,24 @@ GPUNodeLink *GPU_image(GPUMaterial *mat,
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_IMAGE;
link->texture = gpu_node_graph_add_texture(
- graph, ima, iuser, NULL, link->link_type, sampler_state);
+ graph, ima, iuser, NULL, NULL, link->link_type, sampler_state);
+ return link;
+}
+
+GPUNodeLink *GPU_image_sky(GPUMaterial *mat,
+ int width,
+ int height,
+ const float *pixels,
+ float *layer,
+ eGPUSamplerState sampler_state)
+{
+ struct GPUTexture **sky = gpu_material_sky_texture_layer_set(mat, width, height, pixels, layer);
+
+ GPUNodeGraph *graph = gpu_material_node_graph(mat);
+ GPUNodeLink *link = gpu_node_link_create();
+ link->link_type = GPU_NODE_LINK_IMAGE_SKY;
+ link->texture = gpu_node_graph_add_texture(
+ graph, NULL, NULL, NULL, sky, link->link_type, sampler_state);
return link;
}
@@ -565,7 +614,7 @@ GPUNodeLink *GPU_image_tiled(GPUMaterial *mat,
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_IMAGE_TILED;
link->texture = gpu_node_graph_add_texture(
- graph, ima, iuser, NULL, link->link_type, sampler_state);
+ graph, ima, iuser, NULL, NULL, link->link_type, sampler_state);
return link;
}
@@ -575,7 +624,7 @@ GPUNodeLink *GPU_image_tiled_mapping(GPUMaterial *mat, Image *ima, ImageUser *iu
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_IMAGE_TILED_MAPPING;
link->texture = gpu_node_graph_add_texture(
- graph, ima, iuser, NULL, link->link_type, GPU_SAMPLER_MAX);
+ graph, ima, iuser, NULL, NULL, link->link_type, GPU_SAMPLER_MAX);
return link;
}
@@ -588,7 +637,7 @@ GPUNodeLink *GPU_color_band(GPUMaterial *mat, int size, float *pixels, float *ro
GPUNodeLink *link = gpu_node_link_create();
link->link_type = GPU_NODE_LINK_COLORBAND;
link->texture = gpu_node_graph_add_texture(
- graph, NULL, NULL, colorband, link->link_type, GPU_SAMPLER_MAX);
+ graph, NULL, NULL, colorband, NULL, link->link_type, GPU_SAMPLER_MAX);
return link;
}
@@ -630,7 +679,7 @@ bool GPU_link(GPUMaterial *mat, const char *name, ...)
}
static bool gpu_stack_link_v(GPUMaterial *material,
- bNode *bnode,
+ const bNode *bnode,
const char *name,
GPUNodeStack *in,
GPUNodeStack *out,
@@ -702,7 +751,7 @@ static bool gpu_stack_link_v(GPUMaterial *material,
}
bool GPU_stack_link(GPUMaterial *material,
- bNode *bnode,
+ const bNode *bnode,
const char *name,
GPUNodeStack *in,
GPUNodeStack *out,
@@ -716,14 +765,6 @@ bool GPU_stack_link(GPUMaterial *material,
return valid;
}
-GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat,
- bNode *node,
- GPUNodeStack *stack,
- const int index)
-{
- return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT);
-}
-
/* Node Graph */
static void gpu_inputs_free(ListBase *inputs)
@@ -784,6 +825,7 @@ void gpu_node_graph_free(GPUNodeGraph *graph)
{
BLI_freelistN(&graph->outlink_aovs);
BLI_freelistN(&graph->material_functions);
+ BLI_freelistN(&graph->outlink_compositor);
gpu_node_graph_free_nodes(graph);
BLI_freelistN(&graph->textures);
@@ -836,6 +878,9 @@ void gpu_node_graph_prune_unused(GPUNodeGraph *graph)
LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, funclink, &graph->material_functions) {
gpu_nodes_tag(funclink->outlink, GPU_NODE_TAG_FUNCTION);
}
+ LISTBASE_FOREACH (GPUNodeGraphOutputLink *, compositor_link, &graph->outlink_compositor) {
+ gpu_nodes_tag(compositor_link->outlink, GPU_NODE_TAG_COMPOSITOR);
+ }
for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) {
next = node->next;
diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h
index ae472d5b7aa..085620b30e4 100644
--- a/source/blender/gpu/intern/gpu_node_graph.h
+++ b/source/blender/gpu/intern/gpu_node_graph.h
@@ -35,6 +35,7 @@ typedef enum eGPUDataSource {
GPU_SOURCE_TEX,
GPU_SOURCE_TEX_TILED_MAPPING,
GPU_SOURCE_FUNCTION_CALL,
+ GPU_SOURCE_CRYPTOMATTE,
} eGPUDataSource;
typedef enum {
@@ -46,6 +47,7 @@ typedef enum {
GPU_NODE_LINK_IMAGE,
GPU_NODE_LINK_IMAGE_TILED,
GPU_NODE_LINK_IMAGE_TILED_MAPPING,
+ GPU_NODE_LINK_IMAGE_SKY,
GPU_NODE_LINK_OUTPUT,
GPU_NODE_LINK_UNIFORM,
GPU_NODE_LINK_DIFFERENTIATE_FLOAT_FN,
@@ -59,6 +61,7 @@ typedef enum {
GPU_NODE_TAG_THICKNESS = (1 << 3),
GPU_NODE_TAG_AOV = (1 << 4),
GPU_NODE_TAG_FUNCTION = (1 << 5),
+ GPU_NODE_TAG_COMPOSITOR = (1 << 6),
} eGPUNodeTag;
ENUM_OPERATORS(eGPUNodeTag, GPU_NODE_TAG_FUNCTION)
@@ -158,6 +161,8 @@ typedef struct GPUNodeGraph {
ListBase outlink_aovs;
/* List of GPUNodeGraphFunctionLink */
ListBase material_functions;
+ /* List of GPUNodeGraphOutputLink */
+ ListBase outlink_compositor;
/* Requested attributes and textures. */
ListBase attributes;
@@ -193,6 +198,11 @@ struct GPUTexture **gpu_material_ramp_texture_row_set(struct GPUMaterial *mat,
int size,
float *pixels,
float *row);
+/**
+ * Returns the address of the future pointer to sky_tex
+ */
+struct GPUTexture **gpu_material_sky_texture_layer_set(
+ struct GPUMaterial *mat, int width, int height, const float *pixels, float *layer);
#ifdef __cplusplus
}
diff --git a/source/blender/gpu/intern/gpu_platform.cc b/source/blender/gpu/intern/gpu_platform.cc
index d108dd468a0..f8e2c0fe6fc 100644
--- a/source/blender/gpu/intern/gpu_platform.cc
+++ b/source/blender/gpu/intern/gpu_platform.cc
@@ -79,11 +79,15 @@ void GPUPlatformGlobal::init(eGPUDeviceType gpu_device,
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);
+ const char *vendor = vendor_str ? vendor_str : "UNKNOWN";
+ const char *renderer = renderer_str ? renderer_str : "UNKNOWN";
+ const char *version = version_str ? version_str : "UNKNOWN";
+
+ this->vendor = BLI_strdup(vendor);
+ this->renderer = BLI_strdup(renderer);
+ this->version = BLI_strdup(version);
+ this->support_key = create_key(gpu_support_level, vendor, renderer, version);
+ this->gpu_name = create_gpu_name(vendor, renderer, version);
this->backend = backend;
}
diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h
index a8ee5187d98..0e293302086 100644
--- a/source/blender/gpu/intern/gpu_private.h
+++ b/source/blender/gpu/intern/gpu_private.h
@@ -10,6 +10,10 @@
extern "C" {
#endif
+/* gpu_backend.cc */
+
+void gpu_backend_delete_resources(void);
+
/* gpu_pbvh.c */
void gpu_pbvh_init(void);
diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc
index fe9aacb95f9..4d059ae495e 100644
--- a/source/blender/gpu/intern/gpu_shader.cc
+++ b/source/blender/gpu/intern/gpu_shader.cc
@@ -7,6 +7,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_math_matrix.h"
#include "BLI_string_utils.h"
#include "GPU_capabilities.h"
@@ -94,6 +95,9 @@ static void standard_defines(Vector<const char *> &sources)
case GPU_BACKEND_OPENGL:
sources.append("#define GPU_OPENGL\n");
break;
+ case GPU_BACKEND_METAL:
+ sources.append("#define GPU_METAL\n");
+ break;
default:
BLI_assert(false && "Invalid GPU Backend Type");
break;
@@ -382,6 +386,8 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info)
sources.append(resources.c_str());
sources.append(layout.c_str());
sources.extend(code);
+ sources.extend(info.dependencies_generated);
+ sources.append(info.compute_source_generated.c_str());
shader->compute_shader_from_glsl(sources);
}
@@ -575,6 +581,12 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin)
return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin);
}
+int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin)
+{
+ ShaderInterface *interface = unwrap(shader)->interface;
+ return interface->ssbo_builtin((GPUStorageBufferBuiltin)builtin);
+}
+
int GPU_shader_get_ssbo(GPUShader *shader, const char *name)
{
ShaderInterface *interface = unwrap(shader)->interface;
@@ -603,6 +615,12 @@ int GPU_shader_get_texture_binding(GPUShader *shader, const char *name)
return tex ? tex->binding : -1;
}
+uint GPU_shader_get_attribute_len(const GPUShader *shader)
+{
+ ShaderInterface *interface = unwrap(shader)->interface;
+ return interface->attr_len_;
+}
+
int GPU_shader_get_attribute(GPUShader *shader, const char *name)
{
ShaderInterface *interface = unwrap(shader)->interface;
@@ -610,6 +628,23 @@ int GPU_shader_get_attribute(GPUShader *shader, const char *name)
return attr ? attr->location : -1;
}
+bool GPU_shader_get_attribute_info(const GPUShader *shader,
+ int attr_location,
+ char r_name[256],
+ int *r_type)
+{
+ ShaderInterface *interface = unwrap(shader)->interface;
+
+ const ShaderInput *attr = interface->attr_get(attr_location);
+ if (!attr) {
+ return false;
+ }
+
+ BLI_strncpy(r_name, interface->input_name_get(attr), 256);
+ *r_type = attr->location != -1 ? interface->attr_types_[attr->location] : -1;
+ return true;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -702,12 +737,25 @@ void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]
GPU_shader_uniform_vector(sh, loc, 4, 1, data);
}
+void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2])
+{
+ const int loc = GPU_shader_get_uniform(sh, name);
+ GPU_shader_uniform_vector_int(sh, loc, 2, 1, data);
+}
+
void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4])
{
const int loc = GPU_shader_get_uniform(sh, name);
GPU_shader_uniform_vector(sh, loc, 16, 1, (const float *)data);
}
+void GPU_shader_uniform_mat3_as_mat4(GPUShader *sh, const char *name, const float data[3][3])
+{
+ float matrix[4][4];
+ copy_m4_m3(matrix, data);
+ GPU_shader_uniform_mat4(sh, name, matrix);
+}
+
void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2])
{
const int loc = GPU_shader_get_uniform(sh, name);
diff --git a/source/blender/gpu/intern/gpu_shader_builder.cc b/source/blender/gpu/intern/gpu_shader_builder.cc
index fc99b892554..9b699c60126 100644
--- a/source/blender/gpu/intern/gpu_shader_builder.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder.cc
@@ -51,7 +51,6 @@ void ShaderBuilder::init()
void ShaderBuilder::exit()
{
- GPU_backend_exit();
GPU_exit();
GPU_context_discard(gpu_context_);
diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
index 515f65adb73..db14d7fbeb9 100644
--- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
+++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc
@@ -12,6 +12,7 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
+#include "BKE_attribute.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_material.h"
@@ -101,11 +102,41 @@ void UI_GetThemeColorShadeAlpha4ubv(int UNUSED(colorid),
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Stubs of BKE_attribute.h
+ * \{ */
+
+void BKE_id_attribute_copy_domains_temp(short UNUSED(id_type),
+ const struct CustomData *UNUSED(vdata),
+ const struct CustomData *UNUSED(edata),
+ const struct CustomData *UNUSED(ldata),
+ const struct CustomData *UNUSED(pdata),
+ const struct CustomData *UNUSED(cdata),
+ struct ID *UNUSED(r_id))
+{
+}
+
+struct CustomDataLayer *BKE_id_attributes_active_color_get(const struct ID *UNUSED(id))
+{
+ return nullptr;
+}
+
+struct CustomDataLayer *BKE_id_attributes_render_color_get(const struct ID *UNUSED(id))
+{
+ return nullptr;
+}
+
+eAttrDomain BKE_id_attribute_domain(const struct ID *UNUSED(id),
+ const struct CustomDataLayer *UNUSED(layer))
+{
+ return ATTR_DOMAIN_AUTO;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Stubs of BKE_paint.h
* \{ */
-bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt),
- const struct MVert *UNUSED(mvert),
- const struct MLoop *UNUSED(mloop))
+bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt), const bool *UNUSED(hide_poly))
{
BLI_assert_unreachable();
return false;
@@ -170,6 +201,40 @@ int CustomData_get_offset(const struct CustomData *UNUSED(data), int UNUSED(type
return 0;
}
+int CustomData_get_named_layer_index(const struct CustomData *UNUSED(data),
+ int UNUSED(type),
+ const char *UNUSED(name))
+{
+ return -1;
+}
+
+int CustomData_get_active_layer_index(const struct CustomData *UNUSED(data), int UNUSED(type))
+{
+ return -1;
+}
+
+int CustomData_get_render_layer_index(const struct CustomData *UNUSED(data), int UNUSED(type))
+{
+ return -1;
+}
+
+bool CustomData_has_layer(const struct CustomData *UNUSED(data), int UNUSED(type))
+{
+ return false;
+}
+
+void *CustomData_get_layer_named(const struct CustomData *UNUSED(data),
+ int UNUSED(type),
+ const char *UNUSED(name))
+{
+ return nullptr;
+}
+
+void *CustomData_get_layer(const struct CustomData *UNUSED(data), int UNUSED(type))
+{
+ return nullptr;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -237,5 +302,14 @@ void DRW_deferred_shader_remove(struct GPUMaterial *UNUSED(mat))
BLI_assert_unreachable();
}
+void DRW_cdlayer_attr_aliases_add(struct GPUVertFormat *UNUSED(format),
+ const char *UNUSED(base_name),
+ const struct CustomData *UNUSED(data),
+ const struct CustomDataLayer *UNUSED(cl),
+ bool UNUSED(is_active_render),
+ bool UNUSED(is_active_layer))
+{
+}
+
/** \} */
}
diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c
index c3a1236e814..8a6586e06f6 100644
--- a/source/blender/gpu/intern/gpu_shader_builtin.c
+++ b/source/blender/gpu/intern/gpu_shader_builtin.c
@@ -25,10 +25,7 @@ extern char datatoc_gpu_shader_flat_id_frag_glsl[];
extern char datatoc_gpu_shader_2D_area_borders_vert_glsl[];
extern char datatoc_gpu_shader_2D_area_borders_frag_glsl[];
extern char datatoc_gpu_shader_2D_vert_glsl[];
-extern char datatoc_gpu_shader_2D_flat_color_vert_glsl[];
extern char datatoc_gpu_shader_2D_smooth_color_uniform_alpha_vert_glsl[];
-extern char datatoc_gpu_shader_2D_smooth_color_vert_glsl[];
-extern char datatoc_gpu_shader_2D_smooth_color_frag_glsl[];
extern char datatoc_gpu_shader_2D_image_vert_glsl[];
extern char datatoc_gpu_shader_2D_image_rect_vert_glsl[];
extern char datatoc_gpu_shader_2D_image_multi_rect_vert_glsl[];
@@ -139,10 +136,10 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.name = "GPU_SHADER_3D_IMAGE",
.create_info = "gpu_shader_3D_image",
},
- [GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] =
+ [GPU_SHADER_3D_IMAGE_COLOR] =
{
- .name = "GPU_SHADER_3D_IMAGE_MODULATE_ALPHA",
- .create_info = "gpu_shader_3D_image_modulate_alpha",
+ .name = "GPU_SHADER_3D_IMAGE_COLOR",
+ .create_info = "gpu_shader_3D_image_color",
},
[GPU_SHADER_2D_CHECKER] =
{
@@ -156,21 +153,6 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.create_info = "gpu_shader_2D_diag_stripes",
},
- [GPU_SHADER_2D_UNIFORM_COLOR] =
- {
- .name = "GPU_SHADER_2D_UNIFORM_COLOR",
- .create_info = "gpu_shader_2D_uniform_color",
- },
- [GPU_SHADER_2D_FLAT_COLOR] =
- {
- .name = "GPU_SHADER_2D_FLAT_COLOR",
- .create_info = "gpu_shader_2D_flat_color",
- },
- [GPU_SHADER_2D_SMOOTH_COLOR] =
- {
- .name = "GPU_SHADER_2D_SMOOTH_COLOR",
- .create_info = "gpu_shader_2D_smooth_color",
- },
[GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE] =
{
.name = "GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE",
@@ -181,16 +163,6 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.name = "GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE",
.create_info = "gpu_shader_2D_image_overlays_stereo_merge",
},
- [GPU_SHADER_2D_IMAGE] =
- {
- .name = "GPU_SHADER_2D_IMAGE",
- .create_info = "gpu_shader_2D_image",
- },
- [GPU_SHADER_2D_IMAGE_COLOR] =
- {
- .name = "GPU_SHADER_2D_IMAGE_COLOR",
- .create_info = "gpu_shader_2D_image_color",
- },
[GPU_SHADER_2D_IMAGE_DESATURATE_COLOR] =
{
.name = "GPU_SHADER_2D_IMAGE_DESATURATE_COLOR",
@@ -263,11 +235,6 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = {
.create_info = "gpu_shader_3D_polyline_smooth_color",
},
- [GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR] =
- {
- .name = "GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR",
- .create_info = "gpu_shader_2D_line_dashed_uniform_color",
- },
[GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR] =
{
.name = "GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR",
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc
index 16ce4f7723e..a18fdcd32df 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.cc
+++ b/source/blender/gpu/intern/gpu_shader_create_info.cc
@@ -300,12 +300,25 @@ void gpu_shader_create_info_init()
draw_modelmat = draw_modelmat_legacy;
}
+ /* WORKAROUND: Replace the use of gpu_BaseInstance by an instance attribute. */
+ if (GPU_shader_draw_parameters_support() == false) {
+ draw_resource_id_new = draw_resource_id_fallback;
+ }
+
for (ShaderCreateInfo *info : g_create_infos->values()) {
if (info->do_static_compilation_) {
info->builtins_ |= gpu_shader_dependency_get_builtins(info->vertex_source_);
info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_);
info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_);
info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_);
+
+ /* Automatically amend the create info for ease of use of the debug feature. */
+ if ((info->builtins_ & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) {
+ info->additional_info("draw_debug_draw");
+ }
+ if ((info->builtins_ & BuiltinBits::USE_DEBUG_PRINT) == BuiltinBits::USE_DEBUG_PRINT) {
+ info->additional_info("draw_debug_print");
+ }
}
}
@@ -333,8 +346,11 @@ bool gpu_shader_create_info_compile_all()
int skipped = 0;
int total = 0;
for (ShaderCreateInfo *info : g_create_infos->values()) {
+ info->finalize();
if (info->do_static_compilation_) {
- if (GPU_compute_shader_support() == false && info->compute_source_ != nullptr) {
+ if ((GPU_compute_shader_support() == false && info->compute_source_ != nullptr) ||
+ (GPU_shader_image_load_store_support() == false && info->has_resource_image()) ||
+ (GPU_shader_storage_buffer_objects_support() == false && info->has_resource_storage())) {
skipped++;
continue;
}
diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh
index 4927ef75a75..25a79dd26ac 100644
--- a/source/blender/gpu/intern/gpu_shader_create_info.hh
+++ b/source/blender/gpu/intern/gpu_shader_create_info.hh
@@ -32,6 +32,7 @@ namespace blender::gpu::shader {
#endif
enum class Type {
+ /* Types supported natively across all GPU back-ends. */
FLOAT = 0,
VEC2,
VEC3,
@@ -47,6 +48,21 @@ enum class Type {
IVEC3,
IVEC4,
BOOL,
+ /* Additionally supported types to enable data optimization and native
+ * support in some GPU back-ends.
+ * NOTE: These types must be representable in all APIs. E.g. `VEC3_101010I2` is aliased as vec3
+ * in the GL back-end, as implicit type conversions from packed normal attribute data to vec3 is
+ * supported. UCHAR/CHAR types are natively supported in Metal and can be used to avoid
+ * additional data conversions for `GPU_COMP_U8` vertex attributes. */
+ VEC3_101010I2,
+ UCHAR,
+ UCHAR2,
+ UCHAR3,
+ UCHAR4,
+ CHAR,
+ CHAR2,
+ CHAR3,
+ CHAR4
};
/* All of these functions is a bit out of place */
@@ -86,6 +102,40 @@ static inline std::ostream &operator<<(std::ostream &stream, const Type type)
return stream << "mat3";
case Type::MAT4:
return stream << "mat4";
+ case Type::VEC3_101010I2:
+ return stream << "vec3_1010102_Inorm";
+ case Type::UCHAR:
+ return stream << "uchar";
+ case Type::UCHAR2:
+ return stream << "uchar2";
+ case Type::UCHAR3:
+ return stream << "uchar3";
+ case Type::UCHAR4:
+ return stream << "uchar4";
+ case Type::CHAR:
+ return stream << "char";
+ case Type::CHAR2:
+ return stream << "char2";
+ case Type::CHAR3:
+ return stream << "char3";
+ case Type::CHAR4:
+ return stream << "char4";
+ case Type::INT:
+ return stream << "int";
+ case Type::IVEC2:
+ return stream << "ivec2";
+ case Type::IVEC3:
+ return stream << "ivec3";
+ case Type::IVEC4:
+ return stream << "ivec4";
+ case Type::UINT:
+ return stream << "uint";
+ case Type::UVEC2:
+ return stream << "uvec2";
+ case Type::UVEC3:
+ return stream << "uvec3";
+ case Type::UVEC4:
+ return stream << "uvec4";
default:
BLI_assert(0);
return stream;
@@ -127,8 +177,12 @@ enum class BuiltinBits {
VERTEX_ID = (1 << 14),
WORK_GROUP_ID = (1 << 15),
WORK_GROUP_SIZE = (1 << 16),
+
+ /* Not a builtin but a flag we use to tag shaders that use the debug features. */
+ USE_DEBUG_DRAW = (1 << 29),
+ USE_DEBUG_PRINT = (1 << 30),
};
-ENUM_OPERATORS(BuiltinBits, BuiltinBits::WORK_GROUP_SIZE);
+ENUM_OPERATORS(BuiltinBits, BuiltinBits::USE_DEBUG_PRINT);
/**
* Follow convention described in:
@@ -224,6 +278,8 @@ enum class PrimitiveOut {
POINTS = 0,
LINE_STRIP,
TRIANGLE_STRIP,
+ LINES,
+ TRIANGLES,
};
struct StageInterfaceInfo {
@@ -268,10 +324,10 @@ struct StageInterfaceInfo {
/**
* \brief Describe inputs & outputs, stage interfaces, resources and sources of a shader.
* If all data is correctly provided, this is all that is needed to create and compile
- * a GPUShader.
+ * a #GPUShader.
*
* IMPORTANT: All strings are references only. Make sure all the strings used by a
- * ShaderCreateInfo are not freed until it is consumed or deleted.
+ * #ShaderCreateInfo are not freed until it is consumed or deleted.
*/
struct ShaderCreateInfo {
/** Shader name for debugging. */
@@ -290,7 +346,7 @@ struct ShaderCreateInfo {
DepthWrite depth_write_ = DepthWrite::ANY;
/**
* Maximum length of all the resource names including each null terminator.
- * Only for names used by gpu::ShaderInterface.
+ * Only for names used by #gpu::ShaderInterface.
*/
size_t interface_names_size_ = 0;
/** Manually set builtins. */
@@ -298,6 +354,7 @@ struct ShaderCreateInfo {
/** Manually set generated code. */
std::string vertex_source_generated = "";
std::string fragment_source_generated = "";
+ std::string compute_source_generated = "";
std::string geometry_source_generated = "";
std::string typedef_source_generated = "";
/** Manually set generated dependencies. */
@@ -740,33 +797,16 @@ struct ShaderCreateInfo {
* Used to share parts of the infos that are common to many shaders.
* \{ */
- Self &additional_info(StringRefNull info_name0,
- StringRefNull info_name1 = "",
- StringRefNull info_name2 = "",
- StringRefNull info_name3 = "",
- StringRefNull info_name4 = "",
- StringRefNull info_name5 = "",
- StringRefNull info_name6 = "")
- {
- additional_infos_.append(info_name0);
- if (!info_name1.is_empty()) {
- additional_infos_.append(info_name1);
- }
- if (!info_name2.is_empty()) {
- additional_infos_.append(info_name2);
- }
- if (!info_name3.is_empty()) {
- additional_infos_.append(info_name3);
- }
- if (!info_name4.is_empty()) {
- additional_infos_.append(info_name4);
- }
- if (!info_name5.is_empty()) {
- additional_infos_.append(info_name5);
- }
- if (!info_name6.is_empty()) {
- additional_infos_.append(info_name6);
- }
+ Self &additional_info(StringRefNull info_name)
+ {
+ additional_infos_.append(info_name);
+ return *(Self *)this;
+ }
+
+ template<typename... Args> Self &additional_info(StringRefNull info_name, Args... args)
+ {
+ additional_info(info_name);
+ additional_info(args...);
return *(Self *)this;
}
@@ -818,6 +858,7 @@ struct ShaderCreateInfo {
TEST_EQUAL(*this, b, builtins_);
TEST_EQUAL(*this, b, vertex_source_generated);
TEST_EQUAL(*this, b, fragment_source_generated);
+ TEST_EQUAL(*this, b, compute_source_generated);
TEST_EQUAL(*this, b, typedef_source_generated);
TEST_VECTOR_EQUAL(*this, b, vertex_inputs_);
TEST_EQUAL(*this, b, geometry_layout_);
@@ -872,6 +913,31 @@ struct ShaderCreateInfo {
return stream;
}
+ bool has_resource_type(Resource::BindType bind_type) const
+ {
+ for (auto &res : batch_resources_) {
+ if (res.bind_type == bind_type) {
+ return true;
+ }
+ }
+ for (auto &res : pass_resources_) {
+ if (res.bind_type == bind_type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool has_resource_image() const
+ {
+ return has_resource_type(Resource::BindType::IMAGE);
+ }
+
+ bool has_resource_storage() const
+ {
+ return has_resource_type(Resource::BindType::STORAGE_BUFFER);
+ }
+
/** \} */
#undef TEST_EQUAL
diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc
index d91e15243f3..2c59cb6e501 100644
--- a/source/blender/gpu/intern/gpu_shader_dependency.cc
+++ b/source/blender/gpu/intern/gpu_shader_dependency.cc
@@ -11,6 +11,7 @@
#include <algorithm>
#include <iomanip>
#include <iostream>
+#include <regex>
#include <sstream>
#include "BLI_ghash.h"
@@ -42,7 +43,7 @@ struct GPUSource {
StringRefNull source;
Vector<GPUSource *> dependencies;
bool dependencies_init = false;
- shader::BuiltinBits builtins = (shader::BuiltinBits)0;
+ shader::BuiltinBits builtins = shader::BuiltinBits::NONE;
std::string processed_source;
GPUSource(const char *path,
@@ -54,46 +55,45 @@ struct GPUSource {
/* Scan for builtins. */
/* FIXME: This can trigger false positive caused by disabled #if blocks. */
/* TODO(fclem): Could be made faster by scanning once. */
- if (source.find("gl_FragCoord", 0)) {
+ if (source.find("gl_FragCoord", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::FRAG_COORD;
}
- if (source.find("gl_FrontFacing", 0)) {
+ if (source.find("gl_FrontFacing", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::FRONT_FACING;
}
- if (source.find("gl_GlobalInvocationID", 0)) {
+ if (source.find("gl_GlobalInvocationID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::GLOBAL_INVOCATION_ID;
}
- if (source.find("gl_InstanceID", 0)) {
+ if (source.find("gl_InstanceID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::INSTANCE_ID;
}
- if (source.find("gl_LocalInvocationID", 0)) {
+ if (source.find("gl_LocalInvocationID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::LOCAL_INVOCATION_ID;
}
- if (source.find("gl_LocalInvocationIndex", 0)) {
+ if (source.find("gl_LocalInvocationIndex", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::LOCAL_INVOCATION_INDEX;
}
- if (source.find("gl_NumWorkGroup", 0)) {
+ if (source.find("gl_NumWorkGroup", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::NUM_WORK_GROUP;
}
- if (source.find("gl_PointCoord", 0)) {
+ if (source.find("gl_PointCoord", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::POINT_COORD;
}
- if (source.find("gl_PointSize", 0)) {
+ if (source.find("gl_PointSize", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::POINT_SIZE;
}
- if (source.find("gl_PrimitiveID", 0)) {
+ if (source.find("gl_PrimitiveID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::PRIMITIVE_ID;
}
- if (source.find("gl_VertexID", 0)) {
+ if (source.find("gl_VertexID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::VERTEX_ID;
}
- if (source.find("gl_WorkGroupID", 0)) {
+ if (source.find("gl_WorkGroupID", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::WORK_GROUP_ID;
}
- if (source.find("gl_WorkGroupSize", 0)) {
+ if (source.find("gl_WorkGroupSize", 0) != StringRef::not_found) {
builtins |= shader::BuiltinBits::WORK_GROUP_SIZE;
}
-
/* TODO(fclem): We could do that at compile time. */
/* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */
if (filename.endswith(".h") || filename.endswith(".hh")) {
@@ -101,6 +101,18 @@ struct GPUSource {
quote_preprocess();
}
else {
+ if (source.find("'") != StringRef::not_found) {
+ char_literals_preprocess();
+ }
+ if (source.find("drw_print") != StringRef::not_found) {
+ string_preprocess();
+ }
+ if ((source.find("drw_debug_") != StringRef::not_found) &&
+ /* Avoid these two files where it makes no sense to add the dependency. */
+ (filename != "common_debug_draw_lib.glsl" &&
+ filename != "draw_debug_draw_display_vert.glsl")) {
+ builtins |= shader::BuiltinBits::USE_DEBUG_DRAW;
+ }
check_no_quotes();
}
@@ -522,6 +534,217 @@ struct GPUSource {
}
}
+ void char_literals_preprocess()
+ {
+ const StringRefNull input = source;
+ std::stringstream output;
+ int64_t cursor = -1;
+ int64_t last_pos = 0;
+
+ while (true) {
+ cursor = find_token(input, '\'', cursor + 1);
+ if (cursor == -1) {
+ break;
+ }
+ /* Output anything between 2 print statement. */
+ output << input.substr(last_pos, cursor - last_pos);
+
+ /* Extract string. */
+ int64_t char_start = cursor + 1;
+ int64_t char_end = find_token(input, '\'', char_start);
+ CHECK(char_end, input, cursor, "Malformed char literal. Missing ending `'`.");
+
+ StringRef input_char = input.substr(char_start, char_end - char_start);
+ if (input_char.size() == 0) {
+ CHECK(-1, input, cursor, "Malformed char literal. Empty character constant");
+ }
+
+ uint8_t char_value = input_char[0];
+
+ if (input_char[0] == '\\') {
+ if (input_char[1] == 'n') {
+ char_value = '\n';
+ }
+ else {
+ CHECK(-1, input, cursor, "Unsupported escaped character");
+ }
+ }
+ else {
+ if (input_char.size() > 1) {
+ CHECK(-1, input, cursor, "Malformed char literal. Multi-character character constant");
+ }
+ }
+
+ char hex[8];
+ SNPRINTF(hex, "0x%.2Xu", char_value);
+ output << hex;
+
+ cursor = last_pos = char_end + 1;
+ }
+ /* If nothing has been changed, do not allocate processed_source. */
+ if (last_pos == 0) {
+ return;
+ }
+
+ if (last_pos != 0) {
+ output << input.substr(last_pos);
+ }
+ processed_source = output.str();
+ source = processed_source.c_str();
+ }
+
+ /* Replace print(string) by equivalent drw_print_char4() sequence. */
+ void string_preprocess()
+ {
+ const StringRefNull input = source;
+ std::stringstream output;
+ int64_t cursor = -1;
+ int64_t last_pos = 0;
+
+ while (true) {
+ cursor = find_keyword(input, "drw_print", cursor + 1);
+ if (cursor == -1) {
+ break;
+ }
+
+ bool do_endl = false;
+ StringRef func = input.substr(cursor);
+ if (func.startswith("drw_print(")) {
+ do_endl = true;
+ }
+ else if (func.startswith("drw_print_no_endl(")) {
+ do_endl = false;
+ }
+ else {
+ continue;
+ }
+
+ /* Output anything between 2 print statement. */
+ output << input.substr(last_pos, cursor - last_pos);
+
+ /* Extract string. */
+ int64_t str_start = input.find('(', cursor) + 1;
+ int64_t semicolon = find_token(input, ';', str_start + 1);
+ CHECK(semicolon, input, cursor, "Malformed print(). Missing `;` .");
+ int64_t str_end = rfind_token(input, ')', semicolon);
+ if (str_end < str_start) {
+ CHECK(-1, input, cursor, "Malformed print(). Missing closing `)` .");
+ }
+
+ std::stringstream sub_output;
+ StringRef input_args = input.substr(str_start, str_end - str_start);
+
+ auto print_string = [&](std::string str) -> int {
+ size_t len_before_pad = str.length();
+ /* Pad string to uint size. */
+ while (str.length() % 4 != 0) {
+ str += " ";
+ }
+ /* Keep everything in one line to not mess with the shader logs. */
+ sub_output << "/* " << str << "*/";
+ sub_output << "drw_print_string_start(" << len_before_pad << ");";
+ for (size_t i = 0; i < len_before_pad; i += 4) {
+ uint8_t chars[4] = {*(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0),
+ *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1),
+ *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2),
+ *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3)};
+ if (i + 4 > len_before_pad) {
+ chars[len_before_pad - i] = '\0';
+ }
+ char uint_hex[12];
+ SNPRINTF(uint_hex, "0x%.2X%.2X%.2X%.2Xu", chars[3], chars[2], chars[1], chars[0]);
+ sub_output << "drw_print_char4(" << StringRefNull(uint_hex) << ");";
+ }
+ return 0;
+ };
+
+ std::string func_args = input_args;
+ /* Workaround to support function call inside prints. We replace commas by a non control
+ * character `$` in order to use simpler regex later. */
+ bool string_scope = false;
+ int func_scope = 0;
+ for (char &c : func_args) {
+ if (c == '"') {
+ string_scope = !string_scope;
+ }
+ else if (!string_scope) {
+ if (c == '(') {
+ func_scope++;
+ }
+ else if (c == ')') {
+ func_scope--;
+ }
+ else if (c == ',' && func_scope != 0) {
+ c = '$';
+ }
+ }
+ }
+
+ const bool print_as_variable = (input_args[0] != '"') && find_token(input_args, ',') == -1;
+ if (print_as_variable) {
+ /* Variable or expression debugging. */
+ std::string arg = input_args;
+ /* Pad align most values. */
+ while (arg.length() % 4 != 0) {
+ arg += " ";
+ }
+ print_string(arg);
+ print_string("= ");
+ sub_output << "drw_print_value(" << input_args << ");";
+ }
+ else {
+ const std::regex arg_regex(
+ /* String args. */
+ "[\\s]*\"([^\r\n\t\f\v\"]*)\""
+ /* OR. */
+ "|"
+ /* value args. */
+ "([^,]+)");
+ std::smatch args_match;
+ std::string::const_iterator args_search_start(func_args.cbegin());
+ while (std::regex_search(args_search_start, func_args.cend(), args_match, arg_regex)) {
+ args_search_start = args_match.suffix().first;
+ std::string arg_string = args_match[1].str();
+ std::string arg_val = args_match[2].str();
+
+ if (arg_string.empty()) {
+ for (char &c : arg_val) {
+ if (c == '$') {
+ c = ',';
+ }
+ }
+ sub_output << "drw_print_value(" << arg_val << ");";
+ }
+ else {
+ print_string(arg_string);
+ }
+ }
+ }
+
+ if (do_endl) {
+ sub_output << "drw_print_newline();";
+ }
+
+ output << sub_output.str();
+
+ cursor = last_pos = str_end + 1;
+ }
+ /* If nothing has been changed, do not allocate processed_source. */
+ if (last_pos == 0) {
+ return;
+ }
+
+ if (filename != "common_debug_print_lib.glsl") {
+ builtins |= shader::BuiltinBits::USE_DEBUG_PRINT;
+ }
+
+ if (last_pos != 0) {
+ output << input.substr(last_pos);
+ }
+ processed_source = output.str();
+ source = processed_source.c_str();
+ }
+
#undef find_keyword
#undef rfind_keyword
#undef find_token
@@ -537,6 +760,15 @@ struct GPUSource {
this->dependencies_init = true;
int64_t pos = -1;
+ using namespace shader;
+ /* Auto dependency injection for debug capabilities. */
+ if ((builtins & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) {
+ dependencies.append_non_duplicates(dict.lookup("common_debug_draw_lib.glsl"));
+ }
+ if ((builtins & BuiltinBits::USE_DEBUG_PRINT) == BuiltinBits::USE_DEBUG_PRINT) {
+ dependencies.append_non_duplicates(dict.lookup("common_debug_print_lib.glsl"));
+ }
+
while (true) {
GPUSource *dependency_source = nullptr;
@@ -558,6 +790,7 @@ struct GPUSource {
return 1;
}
}
+
/* Recursive. */
int result = dependency_source->init_dependencies(dict, g_functions);
if (result != 0) {
@@ -583,7 +816,7 @@ struct GPUSource {
shader::BuiltinBits builtins_get() const
{
- shader::BuiltinBits out_builtins = shader::BuiltinBits::NONE;
+ shader::BuiltinBits out_builtins = builtins;
for (auto *dep : dependencies) {
out_builtins |= dep->builtins;
}
@@ -593,7 +826,8 @@ struct GPUSource {
bool is_from_material_library() const
{
return (filename.startswith("gpu_shader_material_") ||
- filename.startswith("gpu_shader_common_")) &&
+ filename.startswith("gpu_shader_common_") ||
+ filename.startswith("gpu_shader_compositor_")) &&
filename.endswith(".glsl");
}
};
diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh
index ac78af38fcc..41e06569bdc 100644
--- a/source/blender/gpu/intern/gpu_shader_interface.hh
+++ b/source/blender/gpu/intern/gpu_shader_interface.hh
@@ -18,6 +18,7 @@
#include "BLI_utildefines.h"
#include "GPU_shader.h"
+#include "GPU_vertex_format.h" /* GPU_VERT_ATTR_MAX_LEN */
#include "gpu_shader_create_info.hh"
namespace blender::gpu {
@@ -39,9 +40,9 @@ class ShaderInterface {
/* TODO(fclem): should be protected. */
public:
/** Flat array. In this order: Attributes, Ubos, Uniforms. */
- ShaderInput *inputs_ = NULL;
+ ShaderInput *inputs_ = nullptr;
/** Buffer containing all inputs names separated by '\0'. */
- char *name_buffer_ = NULL;
+ char *name_buffer_ = nullptr;
/** Input counts inside input array. */
uint attr_len_ = 0;
uint ubo_len_ = 0;
@@ -56,6 +57,14 @@ class ShaderInterface {
/** Location of builtin uniforms. Fast access, no lookup needed. */
int32_t builtins_[GPU_NUM_UNIFORMS];
int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS];
+ int32_t builtin_buffers_[GPU_NUM_STORAGE_BUFFERS];
+
+ /**
+ * Currently only used for `GPU_shader_get_attribute_info`.
+ * This utility is useful for automatic creation of `GPUVertFormat` in Python.
+ * Use `ShaderInput::location` to identify the `Type`.
+ */
+ uint8_t attr_types_[GPU_VERT_ATTR_MAX_LEN];
public:
ShaderInterface();
@@ -68,6 +77,10 @@ class ShaderInterface {
{
return input_lookup(inputs_, attr_len_, name);
}
+ inline const ShaderInput *attr_get(const int binding) const
+ {
+ return input_lookup(inputs_, attr_len_, binding);
+ }
inline const ShaderInput *ubo_get(const char *name) const
{
@@ -116,9 +129,17 @@ class ShaderInterface {
return builtin_blocks_[builtin];
}
+ /* Returns binding position. */
+ inline int32_t ssbo_builtin(const GPUStorageBufferBuiltin builtin) const
+ {
+ BLI_assert(builtin >= 0 && builtin < GPU_NUM_STORAGE_BUFFERS);
+ return builtin_buffers_[builtin];
+ }
+
protected:
static inline const char *builtin_uniform_name(GPUUniformBuiltin u);
static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u);
+ static inline const char *builtin_storage_block_name(GPUStorageBufferBuiltin u);
inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const;
inline void copy_input_name(ShaderInput *input,
@@ -187,7 +208,7 @@ inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u)
return "srgbTarget";
default:
- return NULL;
+ return nullptr;
}
}
@@ -208,7 +229,19 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu
case GPU_UNIFORM_BLOCK_DRW_INFOS:
return "drw_infos";
default:
- return NULL;
+ return nullptr;
+ }
+}
+
+inline const char *ShaderInterface::builtin_storage_block_name(GPUStorageBufferBuiltin u)
+{
+ switch (u) {
+ case GPU_STORAGE_BUFFER_DEBUG_VERTS:
+ return "drw_debug_verts_buf";
+ case GPU_STORAGE_BUFFER_DEBUG_PRINT:
+ return "drw_debug_print_buf";
+ default:
+ return nullptr;
}
}
@@ -258,7 +291,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const
return inputs + i; /* not found */
}
}
- return NULL; /* not found */
+ return nullptr; /* not found */
}
/* This is a bit dangerous since we could have a hash collision.
@@ -268,7 +301,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const
return inputs + i;
}
}
- return NULL; /* not found */
+ return nullptr; /* not found */
}
inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs,
@@ -281,7 +314,7 @@ inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const
return inputs + i;
}
}
- return NULL; /* not found */
+ return nullptr; /* not found */
}
} // namespace blender::gpu
diff --git a/source/blender/gpu/intern/gpu_shader_log.cc b/source/blender/gpu/intern/gpu_shader_log.cc
index 572727fa515..dbc36c5afd0 100644
--- a/source/blender/gpu/intern/gpu_shader_log.cc
+++ b/source/blender/gpu/intern/gpu_shader_log.cc
@@ -228,6 +228,7 @@ void Shader::print_log(Span<const char *> sources,
log_line = line_end + 1;
previous_location = log_item.cursor;
}
+ // printf("%s", sources_combined);
MEM_freeN(sources_combined);
CLG_Severity severity = error ? CLG_SEVERITY_ERROR : CLG_SEVERITY_WARN;
diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh
index 4d318093c98..a822cd8aa38 100644
--- a/source/blender/gpu/intern/gpu_shader_private.hh
+++ b/source/blender/gpu/intern/gpu_shader_private.hh
@@ -55,8 +55,6 @@ class Shader {
virtual void uniform_float(int location, int comp_len, int array_size, const float *data) = 0;
virtual void uniform_int(int location, int comp_len, int array_size, const int *data) = 0;
- virtual void vertformat_from_shader(GPUVertFormat *) const = 0;
-
std::string defines_declare(const shader::ShaderCreateInfo &info) const;
virtual std::string resources_declare(const shader::ShaderCreateInfo &info) const = 0;
virtual std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const = 0;
diff --git a/source/blender/gpu/intern/gpu_storage_buffer.cc b/source/blender/gpu/intern/gpu_storage_buffer.cc
index afa27da9c85..460a643089c 100644
--- a/source/blender/gpu/intern/gpu_storage_buffer.cc
+++ b/source/blender/gpu/intern/gpu_storage_buffer.cc
@@ -109,4 +109,9 @@ void GPU_storagebuf_copy_sub_from_vertbuf(
unwrap(ssbo)->copy_sub(unwrap(src), dst_offset, src_offset, copy_size);
}
+void GPU_storagebuf_read(GPUStorageBuf *ssbo, void *data)
+{
+ unwrap(ssbo)->read(data);
+}
+
/** \} */
diff --git a/source/blender/gpu/intern/gpu_storage_buffer_private.hh b/source/blender/gpu/intern/gpu_storage_buffer_private.hh
index 091e6c2d386..0c96f97ad30 100644
--- a/source/blender/gpu/intern/gpu_storage_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_storage_buffer_private.hh
@@ -29,7 +29,7 @@ class StorageBuf {
/** Data size in bytes. */
size_t size_in_bytes_;
/** Continuous memory block to copy to GPU. This data is owned by the StorageBuf. */
- void *data_ = NULL;
+ void *data_ = nullptr;
/** Debugging name */
char name_[DEBUG_NAME_LEN];
@@ -44,6 +44,7 @@ class StorageBuf {
eGPUDataFormat data_format,
void *data) = 0;
virtual void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) = 0;
+ virtual void read(void *data) = 0;
};
/* Syntactic sugar. */
diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc
index 218d22ddf53..bec8b8a0df3 100644
--- a/source/blender/gpu/intern/gpu_texture.cc
+++ b/source/blender/gpu/intern/gpu_texture.cc
@@ -51,13 +51,13 @@ Texture::~Texture()
#endif
}
-bool Texture::init_1D(int w, int layers, int mips, eGPUTextureFormat format)
+bool Texture::init_1D(int w, int layers, int mip_len, eGPUTextureFormat format)
{
w_ = w;
h_ = layers;
d_ = 0;
- int mips_max = 1 + floorf(log2f(w));
- mipmaps_ = min_ii(mips, mips_max);
+ int mip_len_max = 1 + floorf(log2f(w));
+ mipmaps_ = min_ii(mip_len, mip_len_max);
format_ = format;
format_flag_ = to_format_flag(format);
type_ = (layers > 0) ? GPU_TEXTURE_1D_ARRAY : GPU_TEXTURE_1D;
@@ -67,13 +67,13 @@ bool Texture::init_1D(int w, int layers, int mips, eGPUTextureFormat format)
return this->init_internal();
}
-bool Texture::init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format)
+bool Texture::init_2D(int w, int h, int layers, int mip_len, eGPUTextureFormat format)
{
w_ = w;
h_ = h;
d_ = layers;
- int mips_max = 1 + floorf(log2f(max_ii(w, h)));
- mipmaps_ = min_ii(mips, mips_max);
+ int mip_len_max = 1 + floorf(log2f(max_ii(w, h)));
+ mipmaps_ = min_ii(mip_len, mip_len_max);
format_ = format;
format_flag_ = to_format_flag(format);
type_ = (layers > 0) ? GPU_TEXTURE_2D_ARRAY : GPU_TEXTURE_2D;
@@ -83,13 +83,13 @@ bool Texture::init_2D(int w, int h, int layers, int mips, eGPUTextureFormat form
return this->init_internal();
}
-bool Texture::init_3D(int w, int h, int d, int mips, eGPUTextureFormat format)
+bool Texture::init_3D(int w, int h, int d, int mip_len, eGPUTextureFormat format)
{
w_ = w;
h_ = h;
d_ = d;
- int mips_max = 1 + floorf(log2f(max_iii(w, h, d)));
- mipmaps_ = min_ii(mips, mips_max);
+ int mip_len_max = 1 + floorf(log2f(max_iii(w, h, d)));
+ mipmaps_ = min_ii(mip_len, mip_len_max);
format_ = format;
format_flag_ = to_format_flag(format);
type_ = GPU_TEXTURE_3D;
@@ -99,13 +99,13 @@ bool Texture::init_3D(int w, int h, int d, int mips, eGPUTextureFormat format)
return this->init_internal();
}
-bool Texture::init_cubemap(int w, int layers, int mips, eGPUTextureFormat format)
+bool Texture::init_cubemap(int w, int layers, int mip_len, eGPUTextureFormat format)
{
w_ = w;
h_ = w;
d_ = max_ii(1, layers) * 6;
- int mips_max = 1 + floorf(log2f(w));
- mipmaps_ = min_ii(mips, mips_max);
+ int mip_len_max = 1 + floorf(log2f(w));
+ mipmaps_ = min_ii(mip_len, mip_len_max);
format_ = format;
format_flag_ = to_format_flag(format);
type_ = (layers > 0) ? GPU_TEXTURE_CUBE_ARRAY : GPU_TEXTURE_CUBE;
@@ -237,29 +237,29 @@ static inline GPUTexture *gpu_texture_create(const char *name,
const int h,
const int d,
const eGPUTextureType type,
- int mips,
+ int mip_len,
eGPUTextureFormat tex_format,
eGPUDataFormat data_format,
const void *pixels)
{
- BLI_assert(mips > 0);
+ BLI_assert(mip_len > 0);
Texture *tex = GPUBackend::get()->texture_alloc(name);
bool success = false;
switch (type) {
case GPU_TEXTURE_1D:
case GPU_TEXTURE_1D_ARRAY:
- success = tex->init_1D(w, h, mips, tex_format);
+ success = tex->init_1D(w, h, mip_len, tex_format);
break;
case GPU_TEXTURE_2D:
case GPU_TEXTURE_2D_ARRAY:
- success = tex->init_2D(w, h, d, mips, tex_format);
+ success = tex->init_2D(w, h, d, mip_len, tex_format);
break;
case GPU_TEXTURE_3D:
- success = tex->init_3D(w, h, d, mips, tex_format);
+ success = tex->init_3D(w, h, d, mip_len, tex_format);
break;
case GPU_TEXTURE_CUBE:
case GPU_TEXTURE_CUBE_ARRAY:
- success = tex->init_cubemap(w, d, mips, tex_format);
+ success = tex->init_cubemap(w, d, mip_len, tex_format);
break;
default:
break;
@@ -360,6 +360,13 @@ GPUTexture *GPU_texture_create_compressed_2d(
GPUTexture *GPU_texture_create_from_vertbuf(const char *name, GPUVertBuf *vert)
{
+#ifndef NDEBUG
+ /* Vertex buffers used for texture buffers must be flagged with:
+ * GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY. */
+ BLI_assert_msg(unwrap(vert)->extended_usage_ & GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY,
+ "Vertex Buffers used for textures should have usage flag "
+ "GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY.");
+#endif
eGPUTextureFormat tex_format = to_texture_format(GPU_vertbuf_get_format(vert));
Texture *tex = GPUBackend::get()->texture_alloc(name);
@@ -641,6 +648,112 @@ eGPUTextureFormat GPU_texture_format(const GPUTexture *tex)
return reinterpret_cast<const Texture *>(tex)->format_get();
}
+const char *GPU_texture_format_description(eGPUTextureFormat texture_format)
+{
+ switch (texture_format) {
+ case GPU_RGBA8UI:
+ return "RGBA8UI";
+ case GPU_RGBA8I:
+ return "RGBA8I";
+ case GPU_RGBA8:
+ return "RGBA8";
+ case GPU_RGBA32UI:
+ return "RGBA32UI";
+ case GPU_RGBA32I:
+ return "RGBA32I";
+ case GPU_RGBA32F:
+ return "RGBA32F";
+ case GPU_RGBA16UI:
+ return "RGBA16UI";
+ case GPU_RGBA16I:
+ return "RGBA16I";
+ case GPU_RGBA16F:
+ return "RGBA16F";
+ case GPU_RGBA16:
+ return "RGBA16";
+ case GPU_RG8UI:
+ return "RG8UI";
+ case GPU_RG8I:
+ return "RG8I";
+ case GPU_RG8:
+ return "RG8";
+ case GPU_RG32UI:
+ return "RG32UI";
+ case GPU_RG32I:
+ return "RG32I";
+ case GPU_RG32F:
+ return "RG32F";
+ case GPU_RG16UI:
+ return "RG16UI";
+ case GPU_RG16I:
+ return "RG16I";
+ case GPU_RG16F:
+ return "RG16F";
+ case GPU_RG16:
+ return "RG16";
+ case GPU_R8UI:
+ return "R8UI";
+ case GPU_R8I:
+ return "R8I";
+ case GPU_R8:
+ return "R8";
+ case GPU_R32UI:
+ return "R32UI";
+ case GPU_R32I:
+ return "R32I";
+ case GPU_R32F:
+ return "R32F";
+ case GPU_R16UI:
+ return "R16UI";
+ case GPU_R16I:
+ return "R16I";
+ case GPU_R16F:
+ return "R16F";
+ case GPU_R16:
+ return "R16";
+
+ /* Special formats texture & render-buffer. */
+ case GPU_RGB10_A2:
+ return "RGB10A2";
+ case GPU_R11F_G11F_B10F:
+ return "R11FG11FB10F";
+ case GPU_DEPTH32F_STENCIL8:
+ return "DEPTH32FSTENCIL8";
+ case GPU_DEPTH24_STENCIL8:
+ return "DEPTH24STENCIL8";
+ case GPU_SRGB8_A8:
+ return "SRGB8A8";
+
+ /* Texture only format */
+ case (GPU_RGB16F):
+ return "RGB16F";
+
+ /* Special formats texture only */
+ case GPU_SRGB8_A8_DXT1:
+ return "SRGB8_A8_DXT1";
+ case GPU_SRGB8_A8_DXT3:
+ return "SRGB8_A8_DXT3";
+ case GPU_SRGB8_A8_DXT5:
+ return "SRGB8_A8_DXT5";
+ case GPU_RGBA8_DXT1:
+ return "RGBA8_DXT1";
+ case GPU_RGBA8_DXT3:
+ return "RGBA8_DXT3";
+ case GPU_RGBA8_DXT5:
+ return "RGBA8_DXT5";
+
+ /* Depth Formats */
+ case GPU_DEPTH_COMPONENT32F:
+ return "DEPTH32F";
+ case GPU_DEPTH_COMPONENT24:
+ return "DEPTH24";
+ case GPU_DEPTH_COMPONENT16:
+ return "DEPTH16";
+ }
+ BLI_assert_unreachable();
+ return "";
+}
+
bool GPU_texture_depth(const GPUTexture *tex)
{
return (reinterpret_cast<const Texture *>(tex)->format_flag_get() & GPU_FORMAT_DEPTH) != 0;
diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh
index 00bcc9fac00..8521b0fd77f 100644
--- a/source/blender/gpu/intern/gpu_texture_private.hh
+++ b/source/blender/gpu/intern/gpu_texture_private.hh
@@ -101,10 +101,10 @@ class Texture {
virtual ~Texture();
/* Return true on success. */
- bool init_1D(int w, int layers, int mips, eGPUTextureFormat format);
- bool init_2D(int w, int h, int layers, int mips, eGPUTextureFormat format);
- bool init_3D(int w, int h, int d, int mips, eGPUTextureFormat format);
- bool init_cubemap(int w, int layers, int mips, eGPUTextureFormat format);
+ bool init_1D(int w, int layers, int mip_len, eGPUTextureFormat format);
+ bool init_2D(int w, int h, int layers, int mip_len, eGPUTextureFormat format);
+ bool init_3D(int w, int h, int d, int mip_len, eGPUTextureFormat format);
+ bool init_cubemap(int w, int layers, int mip_len, eGPUTextureFormat format);
bool init_buffer(GPUVertBuf *vbo, eGPUTextureFormat format);
bool init_view(const GPUTexture *src,
eGPUTextureFormat format,
diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh
index 6e3285b6fef..e3d70634ce1 100644
--- a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh
@@ -29,7 +29,7 @@ class UniformBuf {
/** Data size in bytes. */
size_t size_in_bytes_;
/** Continuous memory block to copy to GPU. This data is owned by the UniformBuf. */
- void *data_ = NULL;
+ void *data_ = nullptr;
/** Debugging name */
char name_[DEBUG_NAME_LEN];
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc
index 0dbd565291b..a441cfe2fb8 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer.cc
+++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc
@@ -40,10 +40,21 @@ VertBuf::~VertBuf()
void VertBuf::init(const GPUVertFormat *format, GPUUsageType usage)
{
- usage_ = usage;
+ /* Strip extended usage flags. */
+ usage_ = usage & ~GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY;
+#ifndef NDEBUG
+ /* Store extended usage. */
+ extended_usage_ = usage;
+#endif
flag = GPU_VERTBUF_DATA_DIRTY;
GPU_vertformat_copy(&this->format, format);
- if (!format->packed) {
+ /* Avoid packing vertex formats which are used for texture buffers.
+ * These cases use singular types and do not need packing. They must
+ * also not have increased alignment padding to the minimum per-vertex stride. */
+ if (usage & GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY) {
+ VertexFormat_texture_buffer_pack(&this->format);
+ }
+ if (!this->format.packed) {
VertexFormat_pack(&this->format);
}
flag |= GPU_VERTBUF_INIT;
@@ -62,6 +73,10 @@ VertBuf *VertBuf::duplicate()
*dst = *this;
/* Almost full copy... */
dst->handle_refcount_ = 1;
+ /* Metadata. */
+#ifndef NDEBUG
+ dst->extended_usage_ = extended_usage_;
+#endif
/* Duplicate all needed implementation specifics data. */
this->duplicate_data(dst);
return dst;
@@ -192,6 +207,7 @@ void GPU_vertbuf_data_len_set(GPUVertBuf *verts_, uint v_len)
void GPU_vertbuf_attr_set(GPUVertBuf *verts_, uint a_idx, uint v_idx, const void *data)
{
VertBuf *verts = unwrap(verts_);
+ BLI_assert(verts->get_usage_type() != GPU_USAGE_DEVICE_ONLY);
const GPUVertFormat *format = &verts->format;
const GPUVertAttr *a = &format->attrs[a_idx];
BLI_assert(v_idx < verts->vertex_alloc);
@@ -215,6 +231,7 @@ void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data)
void GPU_vertbuf_vert_set(GPUVertBuf *verts_, uint v_idx, const void *data)
{
VertBuf *verts = unwrap(verts_);
+ BLI_assert(verts->get_usage_type() != GPU_USAGE_DEVICE_ONLY);
const GPUVertFormat *format = &verts->format;
BLI_assert(v_idx < verts->vertex_alloc);
BLI_assert(verts->data != nullptr);
@@ -225,6 +242,7 @@ void GPU_vertbuf_vert_set(GPUVertBuf *verts_, uint v_idx, const void *data)
void GPU_vertbuf_attr_fill_stride(GPUVertBuf *verts_, uint a_idx, uint stride, const void *data)
{
VertBuf *verts = unwrap(verts_);
+ BLI_assert(verts->get_usage_type() != GPU_USAGE_DEVICE_ONLY);
const GPUVertFormat *format = &verts->format;
const GPUVertAttr *a = &format->attrs[a_idx];
BLI_assert(a_idx < format->attr_len);
diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
index 7a0b53cf958..f20f6caf6de 100644
--- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
+++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh
@@ -29,7 +29,12 @@ class VertBuf {
/** Status flag. */
GPUVertBufStatus flag = GPU_VERTBUF_INVALID;
/** NULL indicates data in VRAM (unmapped) */
- uchar *data = NULL;
+ uchar *data = nullptr;
+
+#ifndef NDEBUG
+ /** Usage including extended usage flags. */
+ GPUUsageType extended_usage_ = GPU_USAGE_STATIC;
+#endif
protected:
/** Usage hint for GL optimization. */
@@ -83,6 +88,11 @@ class VertBuf {
}
}
+ GPUUsageType get_usage_type() const
+ {
+ return usage_;
+ }
+
virtual void update_sub(uint start, uint len, const void *data) = 0;
virtual const void *read() const = 0;
virtual void *unmap(const void *mapped_data) const = 0;
diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc
index 59ae862aa51..897e80293bf 100644
--- a/source/blender/gpu/intern/gpu_vertex_format.cc
+++ b/source/blender/gpu/intern/gpu_vertex_format.cc
@@ -8,6 +8,9 @@
*/
#include "GPU_vertex_format.h"
+#include "GPU_capabilities.h"
+
+#include "gpu_shader_create_info.hh"
#include "gpu_shader_private.hh"
#include "gpu_vertex_format_private.h"
@@ -25,6 +28,7 @@
#endif
using namespace blender::gpu;
+using namespace blender::gpu::shader;
void GPU_vertformat_clear(GPUVertFormat *format)
{
@@ -66,7 +70,7 @@ static uint attr_size(const GPUVertAttr *a)
return a->comp_len * comp_size(static_cast<GPUVertCompType>(a->comp_type));
}
-static uint attr_align(const GPUVertAttr *a)
+static uint attr_align(const GPUVertAttr *a, uint minimum_stride)
{
if (a->comp_type == GPU_COMP_I10) {
return 4; /* always packed as 10_10_10_2 */
@@ -76,7 +80,10 @@ static uint attr_align(const GPUVertAttr *a)
return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */
}
- return c; /* most fetches are ok if components are naturally aligned */
+ /* Most fetches are ok if components are naturally aligned.
+ * However, in Metal,the minimum supported per-vertex stride is 4,
+ * so we must query the GPU and pad out the size accordingly. */
+ return max_ii(minimum_stride, c);
}
uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len)
@@ -306,7 +313,7 @@ static void show_pack(uint a_idx, uint size, uint pad)
}
#endif
-void VertexFormat_pack(GPUVertFormat *format)
+static void VertexFormat_pack_impl(GPUVertFormat *format, uint minimum_stride)
{
GPUVertAttr *a0 = &format->attrs[0];
a0->offset = 0;
@@ -318,7 +325,7 @@ void VertexFormat_pack(GPUVertFormat *format)
for (uint a_idx = 1; a_idx < format->attr_len; a_idx++) {
GPUVertAttr *a = &format->attrs[a_idx];
- uint mid_padding = padding(offset, attr_align(a));
+ uint mid_padding = padding(offset, attr_align(a, minimum_stride));
offset += mid_padding;
a->offset = offset;
offset += a->size;
@@ -328,7 +335,7 @@ void VertexFormat_pack(GPUVertFormat *format)
#endif
}
- uint end_padding = padding(offset, attr_align(a0));
+ uint end_padding = padding(offset, attr_align(a0, minimum_stride));
#if PACK_DEBUG
show_pack(0, 0, end_padding);
@@ -338,8 +345,106 @@ void VertexFormat_pack(GPUVertFormat *format)
format->packed = true;
}
+void VertexFormat_pack(GPUVertFormat *format)
+{
+ /* Perform standard vertex packing, ensuring vertex format satisfies
+ * minimum stride requirements for vertex assembly. */
+ VertexFormat_pack_impl(format, GPU_minimum_per_vertex_stride());
+}
+
+void VertexFormat_texture_buffer_pack(GPUVertFormat *format)
+{
+ /* Validates packing for vertex formats used with texture buffers.
+ * In these cases, there must only be a single vertex attribute.
+ * This attribute should be tightly packed without padding, to ensure
+ * it aligns with the backing texture data format, skipping
+ * minimum per-vertex stride, which mandates 4-byte alignment in Metal.
+ * This additional alignment padding caused smaller data types, e.g. U16,
+ * to mis-align. */
+ BLI_assert_msg(format->attr_len == 1,
+ "Texture buffer mode should only use a single vertex attribute.");
+
+ /* Pack vertex format without minimum stride, as this is not required by texture buffers. */
+ VertexFormat_pack_impl(format, 1);
+}
+
+static uint component_size_get(const Type gpu_type)
+{
+ switch (gpu_type) {
+ case Type::VEC2:
+ case Type::IVEC2:
+ case Type::UVEC2:
+ return 2;
+ case Type::VEC3:
+ case Type::IVEC3:
+ case Type::UVEC3:
+ return 3;
+ case Type::VEC4:
+ case Type::IVEC4:
+ case Type::UVEC4:
+ return 4;
+ case Type::MAT3:
+ return 12;
+ case Type::MAT4:
+ return 16;
+ default:
+ return 1;
+ }
+}
+
+static void recommended_fetch_mode_and_comp_type(Type gpu_type,
+ GPUVertCompType *r_comp_type,
+ GPUVertFetchMode *r_fetch_mode)
+{
+ switch (gpu_type) {
+ case Type::FLOAT:
+ case Type::VEC2:
+ case Type::VEC3:
+ case Type::VEC4:
+ case Type::MAT3:
+ case Type::MAT4:
+ *r_comp_type = GPU_COMP_F32;
+ *r_fetch_mode = GPU_FETCH_FLOAT;
+ break;
+ case Type::INT:
+ case Type::IVEC2:
+ case Type::IVEC3:
+ case Type::IVEC4:
+ *r_comp_type = GPU_COMP_I32;
+ *r_fetch_mode = GPU_FETCH_INT;
+ break;
+ case Type::UINT:
+ case Type::UVEC2:
+ case Type::UVEC3:
+ case Type::UVEC4:
+ *r_comp_type = GPU_COMP_U32;
+ *r_fetch_mode = GPU_FETCH_INT;
+ break;
+ default:
+ BLI_assert(0);
+ }
+}
+
void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *gpushader)
{
- const Shader *shader = reinterpret_cast<const Shader *>(gpushader);
- shader->vertformat_from_shader(format);
+ GPU_vertformat_clear(format);
+
+ uint attr_len = GPU_shader_get_attribute_len(gpushader);
+ int location_test = 0, attrs_added = 0;
+ while (attrs_added < attr_len) {
+ char name[256];
+ Type gpu_type;
+ if (!GPU_shader_get_attribute_info(gpushader, location_test++, name, (int *)&gpu_type)) {
+ continue;
+ }
+
+ GPUVertCompType comp_type;
+ GPUVertFetchMode fetch_mode;
+ recommended_fetch_mode_and_comp_type(gpu_type, &comp_type, &fetch_mode);
+
+ int comp_len = component_size_get(gpu_type);
+
+ GPU_vertformat_attr_add(format, name, comp_type, comp_len, fetch_mode);
+ attrs_added++;
+ }
}
diff --git a/source/blender/gpu/intern/gpu_vertex_format_private.h b/source/blender/gpu/intern/gpu_vertex_format_private.h
index 0f8a869f6df..430008b4cb9 100644
--- a/source/blender/gpu/intern/gpu_vertex_format_private.h
+++ b/source/blender/gpu/intern/gpu_vertex_format_private.h
@@ -16,6 +16,7 @@ extern "C" {
struct GPUVertFormat;
void VertexFormat_pack(struct GPUVertFormat *format);
+void VertexFormat_texture_buffer_pack(struct GPUVertFormat *format);
uint padding(uint offset, uint alignment);
uint vertex_buffer_size(const struct GPUVertFormat *format, uint vertex_len);
diff --git a/source/blender/gpu/metal/kernels/compute_texture_read.msl b/source/blender/gpu/metal/kernels/compute_texture_read.msl
index 4bfb48567f9..7b0760d7620 100644
--- a/source/blender/gpu/metal/kernels/compute_texture_read.msl
+++ b/source/blender/gpu/metal/kernels/compute_texture_read.msl
@@ -74,7 +74,7 @@ template<> uchar convert_type<uchar>(float val)
template<> uint convert_type<uint>(float val)
{
- return uint(val * double(0xFFFFFFFFu));
+ return uint(val * float(0xFFFFFFFFu));
}
struct TextureReadParams {
diff --git a/source/blender/gpu/metal/kernels/compute_texture_update.msl b/source/blender/gpu/metal/kernels/compute_texture_update.msl
index 095c495ac54..43c746e0afa 100644
--- a/source/blender/gpu/metal/kernels/compute_texture_update.msl
+++ b/source/blender/gpu/metal/kernels/compute_texture_update.msl
@@ -38,22 +38,6 @@ using namespace metal;
# define POSITION_TYPE uint3
#endif
-float3 mtl_linear_to_srgb_attr(float3 c)
-{
- c = max(c, float3(0.0));
- float3 c1 = c * 12.92;
- float3 c2 = 1.055 * pow(c, float3(1.0 / 2.4)) - 0.055;
- return mix(c1, c2, step(float3(0.0031308), c));
-}
-
-float3 mtl_srgb_to_linear_attr(float3 c)
-{
- c = max(c, float3(0.0));
- float3 c1 = c * (1.0 / 12.92);
- float3 c2 = pow((c + 0.055) * (1.0 / 1.055), float3(2.4));
- return mix(c1, c2, step(float3(0.04045), c));
-}
-
struct TextureUpdateParams {
int mip_index;
int extent[3];
diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl
index 9fd54f3f31f..374aedff90d 100644
--- a/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_float_frag.glsl
@@ -1,9 +1,4 @@
-uniform sampler2D source_data;
-uniform int mip;
-
-in vec2 texCoord_interp;
-
void main()
{
gl_FragDepth = textureLod(source_data, texCoord_interp, mip).r;
diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_info.hh b/source/blender/gpu/metal/kernels/depth_2d_update_info.hh
new file mode 100644
index 00000000000..0a3281a98f2
--- /dev/null
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_info.hh
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_INTERFACE_INFO(depth_2d_update_iface, "").smooth(Type::VEC2, "texCoord_interp");
+
+GPU_SHADER_CREATE_INFO(depth_2d_update_info_base)
+ .vertex_in(0, Type::VEC2, "pos")
+ .vertex_out(depth_2d_update_iface)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .push_constant(Type::VEC2, "extent")
+ .push_constant(Type::VEC2, "offset")
+ .push_constant(Type::VEC2, "size")
+ .push_constant(Type::INT, "mip")
+ .sampler(0, ImageType::FLOAT_2D, "source_data", Frequency::PASS)
+ .vertex_source("depth_2d_update_vert.glsl");
+
+GPU_SHADER_CREATE_INFO(depth_2d_update_float)
+ .fragment_source("depth_2d_update_float_frag.glsl")
+ .additional_info("depth_2d_update_info_base")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(depth_2d_update_int24)
+ .fragment_source("depth_2d_update_int24_frag.glsl")
+ .additional_info("depth_2d_update_info_base")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(depth_2d_update_int32)
+ .fragment_source("depth_2d_update_int32_frag.glsl")
+ .additional_info("depth_2d_update_info_base")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl
index 7483343503f..a4d9e35d491 100644
--- a/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_int24_frag.glsl
@@ -1,8 +1,4 @@
-uniform isampler2D source_data;
-uniform int mip;
-
-in vec2 texCoord_interp;
void main()
{
diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl
index 75d42c57f73..421c25a2e5c 100644
--- a/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_int32_frag.glsl
@@ -1,9 +1,4 @@
-uniform isampler2D source_data;
-uniform int mip;
-
-in vec2 texCoord_interp;
-
void main()
{
uint val = textureLod(source_data, texCoord_interp, mip).r;
diff --git a/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl b/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl
index faae68d2f55..def0c1ae9de 100644
--- a/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl
+++ b/source/blender/gpu/metal/kernels/depth_2d_update_vert.glsl
@@ -1,10 +1,4 @@
-uniform vec2 extent;
-uniform vec2 offset;
-uniform vec2 size;
-out vec2 texCoord_interp;
-in vec2 pos;
-
void main()
{
vec4 rect = vec4(offset.x, offset.y, offset.x + extent.x, offset.y + extent.y);
diff --git a/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_frag.glsl b/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_frag.glsl
index b1353478593..8c81c5c0d83 100644
--- a/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_frag.glsl
+++ b/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_frag.glsl
@@ -1,10 +1,5 @@
-in vec4 uvcoordsvar;
-uniform sampler2D imageTexture;
-uniform int mip;
-out vec4 fragColor;
-
void main()
{
vec4 tex_color = textureLod(imageTexture, uvcoordsvar.xy, mip);
diff --git a/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh b/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh
new file mode 100644
index 00000000000..469e488c176
--- /dev/null
+++ b/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_info.hh
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_INTERFACE_INFO(fullscreen_blit_iface, "").smooth(Type::VEC4, "uvcoordsvar");
+
+GPU_SHADER_CREATE_INFO(fullscreen_blit)
+ .vertex_in(0, Type::VEC2, "pos")
+ .vertex_out(fullscreen_blit_iface)
+ .fragment_out(0, Type::VEC4, "fragColor")
+ .push_constant(Type::VEC2, "fullscreen")
+ .push_constant(Type::VEC2, "size")
+ .push_constant(Type::VEC2, "dst_offset")
+ .push_constant(Type::VEC2, "src_offset")
+ .push_constant(Type::INT, "mip")
+ .sampler(0, ImageType::FLOAT_2D, "imageTexture", Frequency::PASS)
+ .vertex_source("gpu_shader_fullscreen_blit_vert.glsl")
+ .fragment_source("gpu_shader_fullscreen_blit_frag.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_vert.glsl b/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_vert.glsl
index 8e52868f67d..5d5a0e2ab5f 100644
--- a/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_vert.glsl
+++ b/source/blender/gpu/metal/kernels/gpu_shader_fullscreen_blit_vert.glsl
@@ -1,12 +1,4 @@
-out vec4 uvcoordsvar;
-
-in vec2 pos;
-uniform vec2 fullscreen;
-uniform vec2 size;
-uniform vec2 dst_offset;
-uniform vec2 src_offset;
-
void main()
{
/* The position represents a 0-1 square, we first scale it by the size we want to have it on
diff --git a/source/blender/gpu/metal/mtl_backend.hh b/source/blender/gpu/metal/mtl_backend.hh
index 7228a5f7596..214a5d738a9 100644
--- a/source/blender/gpu/metal/mtl_backend.hh
+++ b/source/blender/gpu/metal/mtl_backend.hh
@@ -16,7 +16,6 @@ namespace blender::gpu {
class Batch;
class DrawList;
class FrameBuffer;
-class IndexBuf;
class QueryPool;
class Shader;
class UniformBuf;
@@ -40,6 +39,11 @@ class MTLBackend : public GPUBackend {
MTLBackend::platform_exit();
}
+ void delete_resources() override
+ {
+ /* Delete any resources with context active. */
+ }
+
static bool metal_is_supported();
static MTLBackend *get()
{
diff --git a/source/blender/gpu/metal/mtl_backend.mm b/source/blender/gpu/metal/mtl_backend.mm
index d2524cc16e3..3cd7794f6c9 100644
--- a/source/blender/gpu/metal/mtl_backend.mm
+++ b/source/blender/gpu/metal/mtl_backend.mm
@@ -10,6 +10,10 @@
#include "mtl_backend.hh"
#include "mtl_context.hh"
#include "mtl_framebuffer.hh"
+#include "mtl_index_buffer.hh"
+#include "mtl_query.hh"
+#include "mtl_shader.hh"
+#include "mtl_uniform_buffer.hh"
#include "gpu_capabilities_private.hh"
#include "gpu_platform_private.hh"
@@ -58,20 +62,18 @@ FrameBuffer *MTLBackend::framebuffer_alloc(const char *name)
IndexBuf *MTLBackend::indexbuf_alloc()
{
- /* TODO(Metal): Implement MTLIndexBuf. */
- return nullptr;
+ return new MTLIndexBuf();
};
QueryPool *MTLBackend::querypool_alloc()
{
- /* TODO(Metal): Implement MTLQueryPool. */
- return nullptr;
+ return new MTLQueryPool();
};
Shader *MTLBackend::shader_alloc(const char *name)
{
- /* TODO(Metal): Implement MTLShader. */
- return nullptr;
+ MTLContext *mtl_context = MTLContext::get();
+ return new MTLShader(mtl_context, name);
};
Texture *MTLBackend::texture_alloc(const char *name)
@@ -81,8 +83,7 @@ Texture *MTLBackend::texture_alloc(const char *name)
UniformBuf *MTLBackend::uniformbuf_alloc(int size, const char *name)
{
- /* TODO(Metal): Implement MTLUniformBuf. */
- return nullptr;
+ return new MTLUniformBuf(size, name);
};
StorageBuf *MTLBackend::storagebuf_alloc(int size, GPUUsageType usage, const char *name)
@@ -168,7 +169,7 @@ void MTLBackend::platform_init(MTLContext *ctx)
eGPUSupportLevel support_level = GPU_SUPPORT_LEVEL_SUPPORTED;
BLI_assert(ctx);
- id<MTLDevice> mtl_device = nil; /*ctx->device; TODO(Metal): Implement MTLContext. */
+ id<MTLDevice> mtl_device = ctx->device;
BLI_assert(device);
NSString *gpu_name = [mtl_device name];
@@ -187,7 +188,7 @@ void MTLBackend::platform_init(MTLContext *ctx)
os = GPU_OS_UNIX;
#endif
- BLI_assert(os == GPU_OS_MAC && "Platform must be macOS");
+ BLI_assert_msg(os == GPU_OS_MAC, "Platform must be macOS");
/* Determine Vendor from name. */
if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
@@ -334,7 +335,7 @@ bool MTLBackend::metal_is_supported()
void MTLBackend::capabilities_init(MTLContext *ctx)
{
BLI_assert(ctx);
- id<MTLDevice> device = nil; /*ctx->device TODO(Metal): Implement MTLContext. */
+ id<MTLDevice> device = ctx->device;
BLI_assert(device);
/* Initialize Capabilities. */
@@ -381,6 +382,8 @@ void MTLBackend::capabilities_init(MTLContext *ctx)
GCaps.shader_image_load_store_support = ([device supportsFamily:MTLGPUFamilyApple3] ||
MTLBackend::capabilities.supports_family_mac1 ||
MTLBackend::capabilities.supports_family_mac2);
+ /* TODO(Metal): Add support? */
+ GCaps.shader_draw_parameters_support = false;
GCaps.compute_shader_support = false; /* TODO(Metal): Add compute support. */
GCaps.shader_storage_buffer_objects_support =
false; /* TODO(Metal): implement Storage Buffer support. */
diff --git a/source/blender/gpu/metal/mtl_capabilities.hh b/source/blender/gpu/metal/mtl_capabilities.hh
index d56f796e60f..36536438bf5 100644
--- a/source/blender/gpu/metal/mtl_capabilities.hh
+++ b/source/blender/gpu/metal/mtl_capabilities.hh
@@ -14,6 +14,8 @@ namespace gpu {
#define MTL_MAX_TEXTURE_SLOTS 128
#define MTL_MAX_SAMPLER_SLOTS MTL_MAX_TEXTURE_SLOTS
+/* Max limit without using bind-less for samplers. */
+#define MTL_MAX_DEFAULT_SAMPLERS 16
#define MTL_MAX_UNIFORM_BUFFER_BINDINGS 31
#define MTL_MAX_VERTEX_INPUT_ATTRIBUTES 31
#define MTL_MAX_UNIFORMS_PER_BLOCK 64
diff --git a/source/blender/gpu/metal/mtl_command_buffer.mm b/source/blender/gpu/metal/mtl_command_buffer.mm
index 7b6066b3d86..d2936e8e91f 100644
--- a/source/blender/gpu/metal/mtl_command_buffer.mm
+++ b/source/blender/gpu/metal/mtl_command_buffer.mm
@@ -242,7 +242,7 @@ bool MTLCommandBufferManager::end_active_command_encoder()
active_render_command_encoder_ = nil;
active_command_encoder_type_ = MTL_NO_COMMAND_ENCODER;
- /* Reset associated framebuffer flag. */
+ /* Reset associated frame-buffer flag. */
active_frame_buffer_ = nullptr;
active_pass_descriptor_ = nullptr;
return true;
@@ -286,7 +286,7 @@ bool MTLCommandBufferManager::end_active_command_encoder()
id<MTLRenderCommandEncoder> MTLCommandBufferManager::ensure_begin_render_command_encoder(
MTLFrameBuffer *ctx_framebuffer, bool force_begin, bool *new_pass)
{
- /* Ensure valid framebuffer. */
+ /* Ensure valid frame-buffer. */
BLI_assert(ctx_framebuffer != nullptr);
/* Ensure active command buffer. */
@@ -299,15 +299,21 @@ id<MTLRenderCommandEncoder> MTLCommandBufferManager::ensure_begin_render_command
active_frame_buffer_ != ctx_framebuffer || force_begin) {
this->end_active_command_encoder();
- /* Determine if this is a re-bind of the same framebuffer. */
+ /* Determine if this is a re-bind of the same frame-buffer. */
bool is_rebind = (active_frame_buffer_ == ctx_framebuffer);
- /* Generate RenderPassDescriptor from bound framebuffer. */
+ /* Generate RenderPassDescriptor from bound frame-buffer. */
BLI_assert(ctx_framebuffer);
active_frame_buffer_ = ctx_framebuffer;
active_pass_descriptor_ = active_frame_buffer_->bake_render_pass_descriptor(
is_rebind && (!active_frame_buffer_->get_pending_clear()));
+ /* Determine if there is a visibility buffer assigned to the context. */
+ gpu::MTLBuffer *visibility_buffer = context_.get_visibility_buffer();
+ this->active_pass_descriptor_.visibilityResultBuffer =
+ (visibility_buffer) ? visibility_buffer->get_metal_buffer() : nil;
+ context_.clear_visibility_dirty();
+
/* Ensure we have already cleaned up our previous render command encoder. */
BLI_assert(active_render_command_encoder_ == nil);
@@ -492,7 +498,7 @@ bool MTLCommandBufferManager::insert_memory_barrier(eGPUBarrier barrier_bits,
/* Rendering. */
case MTL_RENDER_COMMAND_ENCODER: {
/* Currently flagging both stages -- can use bits above to filter on stage type --
- * though full barrier is safe for now*/
+ * though full barrier is safe for now. */
MTLRenderStages before_stage_flags = 0;
MTLRenderStages after_stage_flags = 0;
if (before_stages & GPU_BARRIER_STAGE_VERTEX &&
diff --git a/source/blender/gpu/metal/mtl_common.hh b/source/blender/gpu/metal/mtl_common.hh
index 44ba786f90f..b6f9c0050a9 100644
--- a/source/blender/gpu/metal/mtl_common.hh
+++ b/source/blender/gpu/metal/mtl_common.hh
@@ -13,4 +13,6 @@
* Set as number of GPU frames in flight, plus an additional value for extra possible CPU frame. */
#define MTL_NUM_SAFE_FRAMES (MTL_MAX_DRAWABLES + 1)
+/* Display debug information about missing attributes and incorrect vertex formats. */
+#define MTL_DEBUG_SHADER_ATTRIBUTES 0
#endif
diff --git a/source/blender/gpu/metal/mtl_context.hh b/source/blender/gpu/metal/mtl_context.hh
index 4b87b994a3d..577438667d6 100644
--- a/source/blender/gpu/metal/mtl_context.hh
+++ b/source/blender/gpu/metal/mtl_context.hh
@@ -3,6 +3,8 @@
/** \file
* \ingroup gpu
*/
+#pragma once
+
#include "MEM_guardedalloc.h"
#include "gpu_context_private.hh"
@@ -15,6 +17,8 @@
#include "mtl_common.hh"
#include "mtl_framebuffer.hh"
#include "mtl_memory.hh"
+#include "mtl_shader.hh"
+#include "mtl_shader_interface.hh"
#include "mtl_texture.hh"
#include <Cocoa/Cocoa.h>
@@ -30,7 +34,6 @@ namespace blender::gpu {
/* Forward Declarations */
class MTLContext;
class MTLCommandBufferManager;
-class MTLShader;
class MTLUniformBuf;
/* Structs containing information on current binding state for textures and samplers. */
@@ -38,7 +41,7 @@ struct MTLTextureBinding {
bool used;
/* Same value as index in bindings array. */
- uint texture_slot_index;
+ uint slot_index;
gpu::MTLTexture *texture_resource;
};
@@ -54,9 +57,10 @@ struct MTLSamplerBinding {
/* Metal Context Render Pass State -- Used to track active RenderCommandEncoder state based on
* bound MTLFrameBuffer's.Owned by MTLContext. */
-struct MTLRenderPassState {
+class MTLRenderPassState {
friend class MTLContext;
+ public:
MTLRenderPassState(MTLContext &context, MTLCommandBufferManager &command_buffer_manager)
: ctx(context), cmd(command_buffer_manager){};
@@ -171,9 +175,9 @@ struct MTLContextDepthStencilState {
bool has_depth_target;
bool has_stencil_target;
- /* TODO(Metal): Consider optimizing this function using memcmp.
+ /* TODO(Metal): Consider optimizing this function using `memcmp`.
* Un-used, but differing, stencil state leads to over-generation
- * of state objects when doing trivial compare. */
+ * of state objects when doing trivial compare. */
bool operator==(const MTLContextDepthStencilState &other) const
{
bool depth_state_equality = (has_depth_target == other.has_depth_target &&
@@ -244,7 +248,7 @@ struct MTLContextTextureUtils {
/* Depth texture updates are not directly supported with Blit operations, similarly, we cannot
* use a compute shader to write to depth, so we must instead render to a depth target.
* These processes use vertex/fragment shaders to render texture data from an intermediate
- * source, in order to prime the depth buffer*/
+ * source, in order to prime the depth buffer. */
blender::Map<DepthTextureUpdateRoutineSpecialisation, GPUShader *> depth_2d_update_shaders;
GPUShader *fullscreen_blit_shader = nullptr;
@@ -354,7 +358,7 @@ typedef enum MTLPipelineStateDirtyFlag {
MTL_PIPELINE_STATE_NULL_FLAG = 0,
/* Whether we need to call setViewport. */
MTL_PIPELINE_STATE_VIEWPORT_FLAG = (1 << 0),
- /* Whether we need to call setScissor.*/
+ /* Whether we need to call setScissor. */
MTL_PIPELINE_STATE_SCISSOR_FLAG = (1 << 1),
/* Whether we need to update/rebind active depth stencil state. */
MTL_PIPELINE_STATE_DEPTHSTENCIL_FLAG = (1 << 2),
@@ -561,18 +565,23 @@ class MTLCommandBufferManager {
};
/** MTLContext -- Core render loop and state management. **/
-/* NOTE(Metal): Partial MTLContext stub to provide wrapper functionality
- * for work-in-progress MTL* classes. */
+/* NOTE(Metal): Partial #MTLContext stub to provide wrapper functionality
+ * for work-in-progress `MTL*` classes. */
class MTLContext : public Context {
friend class MTLBackend;
private:
+ /* Null buffers for empty/uninitialized bindings.
+ * Null attribute buffer follows default attribute format of OpenGL Back-end. */
+ id<MTLBuffer> null_buffer_; /* All zero's. */
+ id<MTLBuffer> null_attribute_buffer_; /* Value float4(0.0,0.0,0.0,1.0). */
+
/* Compute and specialization caches. */
MTLContextTextureUtils texture_utils_;
/* Texture Samplers. */
- /* Cache of generated MTLSamplerState objects based on permutations of `eGPUSamplerState`. */
+ /* Cache of generated #MTLSamplerState objects based on permutations of `eGPUSamplerState`. */
id<MTLSamplerState> sampler_state_cache_[GPU_SAMPLER_MAX];
id<MTLSamplerState> default_sampler_state_ = nil;
@@ -588,6 +597,10 @@ class MTLContext : public Context {
bool is_inside_frame_ = false;
uint current_frame_index_;
+ /* Visibility buffer for MTLQuery results. */
+ gpu::MTLBuffer *visibility_buffer_ = nullptr;
+ bool visibility_is_dirty_ = false;
+
public:
/* Shaders and Pipeline state. */
MTLContextGlobalShaderPipelineState pipeline_state;
@@ -619,6 +632,11 @@ class MTLContext : public Context {
void memory_statistics_get(int *total_mem, int *free_mem) override;
+ static MTLContext *get()
+ {
+ return static_cast<MTLContext *>(Context::get());
+ }
+
void debug_group_begin(const char *name, int index) override;
void debug_group_end() override;
@@ -660,6 +678,18 @@ class MTLContext : public Context {
void set_scissor(int scissor_x, int scissor_y, int scissor_width, int scissor_height);
void set_scissor_enabled(bool scissor_enabled);
+ /* Visibility buffer control. */
+ void set_visibility_buffer(gpu::MTLBuffer *buffer);
+ gpu::MTLBuffer *get_visibility_buffer() const;
+
+ /* Flag whether the visibility buffer for query results
+ * has changed. This requires a new RenderPass in order
+ * to update. */
+ bool is_visibility_dirty() const;
+
+ /* Reset dirty flag state for visibility buffer. */
+ void clear_visibility_dirty();
+
/* Texture utilities. */
MTLContextTextureUtils &get_texture_utils()
{
@@ -690,6 +720,9 @@ class MTLContext : public Context {
{
return MTLContext::global_memory_manager;
}
+ /* Uniform Buffer Bindings to command encoders. */
+ id<MTLBuffer> get_null_buffer();
+ id<MTLBuffer> get_null_attribute_buffer();
};
} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_context.mm b/source/blender/gpu/metal/mtl_context.mm
index 2ab2e20158a..1302cf0dabd 100644
--- a/source/blender/gpu/metal/mtl_context.mm
+++ b/source/blender/gpu/metal/mtl_context.mm
@@ -5,6 +5,8 @@
*/
#include "mtl_context.hh"
#include "mtl_debug.hh"
+#include "mtl_shader.hh"
+#include "mtl_shader_interface.hh"
#include "mtl_state.hh"
#include "DNA_userdef_types.h"
@@ -29,19 +31,33 @@ MTLContext::MTLContext(void *ghost_window) : memory_manager(*this), main_command
/* Init debug. */
debug::mtl_debug_init();
+ /* Device creation.
+ * TODO(Metal): This is a temporary initialization path to enable testing of features
+ * and shader compilation tests. Future functionality should fetch the existing device
+ * from GHOST_ContextCGL.mm. Plumbing to be updated in future. */
+ this->device = MTLCreateSystemDefaultDevice();
+
/* Initialize command buffer state. */
this->main_command_buffer.prepare();
+ /* Initialize IMM and pipeline state */
+ this->pipeline_state.initialised = false;
+
/* Frame management. */
is_inside_frame_ = false;
current_frame_index_ = 0;
+ /* Prepare null data buffer */
+ null_buffer_ = nil;
+ null_attribute_buffer_ = nil;
+
/* Create FrameBuffer handles. */
MTLFrameBuffer *mtl_front_left = new MTLFrameBuffer(this, "front_left");
MTLFrameBuffer *mtl_back_left = new MTLFrameBuffer(this, "back_left");
this->front_left = mtl_front_left;
this->back_left = mtl_back_left;
this->active_fb = this->back_left;
+
/* Prepare platform and capabilities. (NOTE: With METAL, this needs to be done after CTX
* initialization). */
MTLBackend::platform_init(this);
@@ -93,6 +109,12 @@ MTLContext::~MTLContext()
sampler_state_cache_[i] = nil;
}
}
+ if (null_buffer_) {
+ [null_buffer_ release];
+ }
+ if (null_attribute_buffer_) {
+ [null_attribute_buffer_ release];
+ }
}
void MTLContext::begin_frame()
@@ -177,10 +199,11 @@ id<MTLRenderCommandEncoder> MTLContext::ensure_begin_render_pass()
}
/* Ensure command buffer workload submissions are optimal --
- * Though do not split a batch mid-IMM recording */
+ * Though do not split a batch mid-IMM recording. */
/* TODO(Metal): Add IMM Check once MTLImmediate has been implemented. */
- if (this->main_command_buffer.do_break_submission()/*&&
- !((MTLImmediate *)(this->imm))->imm_is_recording()*/) {
+ if (this->main_command_buffer.do_break_submission()
+ // && !((MTLImmediate *)(this->imm))->imm_is_recording()
+ ) {
this->flush();
}
@@ -188,7 +211,8 @@ id<MTLRenderCommandEncoder> MTLContext::ensure_begin_render_pass()
* framebuffer state has been modified (is_dirty). */
if (!this->main_command_buffer.is_inside_render_pass() ||
this->active_fb != this->main_command_buffer.get_active_framebuffer() ||
- this->main_command_buffer.get_active_framebuffer()->get_dirty()) {
+ this->main_command_buffer.get_active_framebuffer()->get_dirty() ||
+ this->is_visibility_dirty()) {
/* Validate bound framebuffer before beginning render pass. */
if (!static_cast<MTLFrameBuffer *>(this->active_fb)->validate_render_pass()) {
@@ -226,6 +250,50 @@ MTLFrameBuffer *MTLContext::get_default_framebuffer()
return static_cast<MTLFrameBuffer *>(this->back_left);
}
+MTLShader *MTLContext::get_active_shader()
+{
+ return this->pipeline_state.active_shader;
+}
+
+id<MTLBuffer> MTLContext::get_null_buffer()
+{
+ if (null_buffer_ != nil) {
+ return null_buffer_;
+ }
+
+ static const int null_buffer_size = 4096;
+ null_buffer_ = [this->device newBufferWithLength:null_buffer_size
+ options:MTLResourceStorageModeManaged];
+ [null_buffer_ retain];
+ uint32_t *null_data = (uint32_t *)calloc(0, null_buffer_size);
+ memcpy([null_buffer_ contents], null_data, null_buffer_size);
+ [null_buffer_ didModifyRange:NSMakeRange(0, null_buffer_size)];
+ free(null_data);
+
+ BLI_assert(null_buffer_ != nil);
+ return null_buffer_;
+}
+
+id<MTLBuffer> MTLContext::get_null_attribute_buffer()
+{
+ if (null_attribute_buffer_ != nil) {
+ return null_attribute_buffer_;
+ }
+
+ /* Allocate Null buffer if it has not yet been created.
+ * Min buffer size is 256 bytes -- though we only need 64 bytes of data. */
+ static const int null_buffer_size = 256;
+ null_attribute_buffer_ = [this->device newBufferWithLength:null_buffer_size
+ options:MTLResourceStorageModeManaged];
+ BLI_assert(null_attribute_buffer_ != nil);
+ [null_attribute_buffer_ retain];
+ float data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+ memcpy([null_attribute_buffer_ contents], data, sizeof(float) * 4);
+ [null_attribute_buffer_ didModifyRange:NSMakeRange(0, null_buffer_size)];
+
+ return null_attribute_buffer_;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -238,20 +306,20 @@ void MTLContext::pipeline_state_init()
/*** Initialize state only once. ***/
if (!this->pipeline_state.initialised) {
this->pipeline_state.initialised = true;
- this->pipeline_state.active_shader = NULL;
+ this->pipeline_state.active_shader = nullptr;
/* Clear bindings state. */
for (int t = 0; t < GPU_max_textures(); t++) {
this->pipeline_state.texture_bindings[t].used = false;
- this->pipeline_state.texture_bindings[t].texture_slot_index = t;
- this->pipeline_state.texture_bindings[t].texture_resource = NULL;
+ this->pipeline_state.texture_bindings[t].slot_index = -1;
+ this->pipeline_state.texture_bindings[t].texture_resource = nullptr;
}
for (int s = 0; s < MTL_MAX_SAMPLER_SLOTS; s++) {
this->pipeline_state.sampler_bindings[s].used = false;
}
for (int u = 0; u < MTL_MAX_UNIFORM_BUFFER_BINDINGS; u++) {
this->pipeline_state.ubo_bindings[u].bound = false;
- this->pipeline_state.ubo_bindings[u].ubo = NULL;
+ this->pipeline_state.ubo_bindings[u].ubo = nullptr;
}
}
@@ -372,6 +440,45 @@ void MTLContext::set_scissor_enabled(bool scissor_enabled)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Visibility buffer control for MTLQueryPool.
+ * \{ */
+
+void MTLContext::set_visibility_buffer(gpu::MTLBuffer *buffer)
+{
+ /* Flag visibility buffer as dirty if the buffer being used for visibility has changed --
+ * This is required by the render pass, and we will break the pass if the results destination
+ * buffer is modified. */
+ if (buffer) {
+ visibility_is_dirty_ = (buffer != visibility_buffer_) || visibility_is_dirty_;
+ visibility_buffer_ = buffer;
+ visibility_buffer_->debug_ensure_used();
+ }
+ else {
+ /* If buffer is null, reset visibility state, mark dirty to break render pass if results are no
+ * longer needed. */
+ visibility_is_dirty_ = (visibility_buffer_ != nullptr) || visibility_is_dirty_;
+ visibility_buffer_ = nullptr;
+ }
+}
+
+gpu::MTLBuffer *MTLContext::get_visibility_buffer() const
+{
+ return visibility_buffer_;
+}
+
+void MTLContext::clear_visibility_dirty()
+{
+ visibility_is_dirty_ = false;
+}
+
+bool MTLContext::is_visibility_dirty() const
+{
+ return visibility_is_dirty_;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Texture State Management
* \{ */
@@ -447,52 +554,46 @@ id<MTLSamplerState> MTLContext::get_sampler_from_state(MTLSamplerState sampler_s
id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState sampler_state)
{
/* Check if sampler already exists for given state. */
- id<MTLSamplerState> st = sampler_state_cache_[(uint)sampler_state];
- if (st != nil) {
- return st;
- }
- else {
- MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
- descriptor.normalizedCoordinates = true;
-
- MTLSamplerAddressMode clamp_type = (sampler_state.state & GPU_SAMPLER_CLAMP_BORDER) ?
- MTLSamplerAddressModeClampToBorderColor :
- MTLSamplerAddressModeClampToEdge;
- descriptor.rAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_R) ?
- MTLSamplerAddressModeRepeat :
- clamp_type;
- descriptor.sAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_S) ?
- MTLSamplerAddressModeRepeat :
- clamp_type;
- descriptor.tAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_T) ?
- MTLSamplerAddressModeRepeat :
- clamp_type;
- descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
- descriptor.minFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
- MTLSamplerMinMagFilterLinear :
- MTLSamplerMinMagFilterNearest;
- descriptor.magFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
- MTLSamplerMinMagFilterLinear :
- MTLSamplerMinMagFilterNearest;
- descriptor.mipFilter = (sampler_state.state & GPU_SAMPLER_MIPMAP) ?
- MTLSamplerMipFilterLinear :
- MTLSamplerMipFilterNotMipmapped;
- descriptor.lodMinClamp = -1000;
- descriptor.lodMaxClamp = 1000;
- float aniso_filter = max_ff(16, U.anisotropic_filter);
- descriptor.maxAnisotropy = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? aniso_filter : 1;
- descriptor.compareFunction = (sampler_state.state & GPU_SAMPLER_COMPARE) ?
- MTLCompareFunctionLessEqual :
- MTLCompareFunctionAlways;
- descriptor.supportArgumentBuffers = true;
-
- id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor];
- sampler_state_cache_[(uint)sampler_state] = state;
-
- BLI_assert(state != nil);
- [descriptor autorelease];
- return state;
- }
+ MTLSamplerDescriptor *descriptor = [[MTLSamplerDescriptor alloc] init];
+ descriptor.normalizedCoordinates = true;
+
+ MTLSamplerAddressMode clamp_type = (sampler_state.state & GPU_SAMPLER_CLAMP_BORDER) ?
+ MTLSamplerAddressModeClampToBorderColor :
+ MTLSamplerAddressModeClampToEdge;
+ descriptor.rAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_R) ?
+ MTLSamplerAddressModeRepeat :
+ clamp_type;
+ descriptor.sAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_S) ?
+ MTLSamplerAddressModeRepeat :
+ clamp_type;
+ descriptor.tAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_T) ?
+ MTLSamplerAddressModeRepeat :
+ clamp_type;
+ descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
+ descriptor.minFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
+ MTLSamplerMinMagFilterLinear :
+ MTLSamplerMinMagFilterNearest;
+ descriptor.magFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
+ MTLSamplerMinMagFilterLinear :
+ MTLSamplerMinMagFilterNearest;
+ descriptor.mipFilter = (sampler_state.state & GPU_SAMPLER_MIPMAP) ?
+ MTLSamplerMipFilterLinear :
+ MTLSamplerMipFilterNotMipmapped;
+ descriptor.lodMinClamp = -1000;
+ descriptor.lodMaxClamp = 1000;
+ float aniso_filter = max_ff(16, U.anisotropic_filter);
+ descriptor.maxAnisotropy = (sampler_state.state & GPU_SAMPLER_MIPMAP) ? aniso_filter : 1;
+ descriptor.compareFunction = (sampler_state.state & GPU_SAMPLER_COMPARE) ?
+ MTLCompareFunctionLessEqual :
+ MTLCompareFunctionAlways;
+ descriptor.supportArgumentBuffers = true;
+
+ id<MTLSamplerState> state = [this->device newSamplerStateWithDescriptor:descriptor];
+ sampler_state_cache_[(uint)sampler_state] = state;
+
+ BLI_assert(state != nil);
+ [descriptor autorelease];
+ return state;
}
id<MTLSamplerState> MTLContext::get_default_sampler_state()
diff --git a/source/blender/gpu/metal/mtl_framebuffer.hh b/source/blender/gpu/metal/mtl_framebuffer.hh
index d6e9fa76b70..434d1a15b43 100644
--- a/source/blender/gpu/metal/mtl_framebuffer.hh
+++ b/source/blender/gpu/metal/mtl_framebuffer.hh
@@ -40,7 +40,7 @@ struct MTLAttachment {
/**
* Implementation of FrameBuffer object using Metal.
- **/
+ */
class MTLFrameBuffer : public FrameBuffer {
private:
/* Context Handle. */
@@ -54,24 +54,32 @@ class MTLFrameBuffer : public FrameBuffer {
bool use_multilayered_rendering_ = false;
/* State. */
- /* Whether global framebuffer properties have changed and require
- * re-generation of MTLRenderPassDescriptor/RenderCommandEncoders. */
+
+ /**
+ * Whether global frame-buffer properties have changed and require
+ * re-generation of #MTLRenderPassDescriptor / #RenderCommandEncoders.
+ */
bool is_dirty_;
- /* Whether loadstore properties have changed (only affects certain cached configs). */
+ /** Whether `loadstore` properties have changed (only affects certain cached configurations). */
bool is_loadstore_dirty_;
- /* Context that the latest modified state was last applied to.
- * If this does not match current ctx, re-apply state. */
+ /**
+ * Context that the latest modified state was last applied to.
+ * If this does not match current ctx, re-apply state.
+ */
MTLContext *dirty_state_ctx_;
- /* Whether a clear is pending -- Used to toggle between clear and load FB configurations
+ /**
+ * Whether a clear is pending -- Used to toggle between clear and load FB configurations
* (without dirtying the state) - Frame-buffer load config is used if no `GPU_clear_*` command
- * was issued after binding the FrameBuffer. */
+ * was issued after binding the #FrameBuffer.
+ */
bool has_pending_clear_;
- /* Render Pass Descriptors:
- * There are 3 MTLRenderPassDescriptors for different ways in which a frame-buffer
+ /**
+ * Render Pass Descriptors:
+ * There are 3 #MTLRenderPassDescriptors for different ways in which a frame-buffer
* can be configured:
* [0] = CLEAR CONFIG -- Used when a GPU_framebuffer_clear_* command has been issued.
* [1] = LOAD CONFIG -- Used if bound, but no clear is required.
@@ -89,17 +97,17 @@ class MTLFrameBuffer : public FrameBuffer {
MTLRenderPassDescriptor *framebuffer_descriptor_[MTL_FB_CONFIG_MAX];
MTLRenderPassColorAttachmentDescriptor
*colour_attachment_descriptors_[GPU_FB_MAX_COLOR_ATTACHMENT];
- /* Whether MTLRenderPassDescriptor[N] requires updating with latest state. */
+ /** Whether `MTLRenderPassDescriptor[N]` requires updating with latest state. */
bool descriptor_dirty_[MTL_FB_CONFIG_MAX];
- /* Whether SRGB is enabled for this framebuffer configuration. */
+ /** Whether SRGB is enabled for this frame-buffer configuration. */
bool srgb_enabled_;
- /* Whether the primary Frame-buffer attachment is an SRGB target or not. */
+ /** Whether the primary Frame-buffer attachment is an SRGB target or not. */
bool is_srgb_;
public:
/**
* Create a conventional framebuffer to attach texture to.
- **/
+ */
MTLFrameBuffer(MTLContext *ctx, const char *name);
~MTLFrameBuffer();
diff --git a/source/blender/gpu/metal/mtl_framebuffer.mm b/source/blender/gpu/metal/mtl_framebuffer.mm
index 515dd70e5de..975e78fc466 100644
--- a/source/blender/gpu/metal/mtl_framebuffer.mm
+++ b/source/blender/gpu/metal/mtl_framebuffer.mm
@@ -885,12 +885,12 @@ bool MTLFrameBuffer::add_color_attachment(gpu::MTLTexture *texture,
mtl_color_attachments_[slot].depth_plane = 0;
break;
default:
- MTL_LOG_ERROR("MTLFrameBuffer::add_color_attachment Unrecognised texture type %u\n",
+ MTL_LOG_ERROR("MTLFrameBuffer::add_color_attachment Unrecognized texture type %u\n",
texture->type_);
break;
}
- /* Update Framebuffer Resolution. */
+ /* Update Frame-buffer Resolution. */
int width_of_miplayer, height_of_miplayer;
if (miplevel <= 0) {
width_of_miplayer = texture->width_get();
@@ -1007,11 +1007,11 @@ bool MTLFrameBuffer::add_depth_attachment(gpu::MTLTexture *texture, int miplevel
mtl_depth_attachment_.depth_plane = 0;
break;
default:
- BLI_assert(false && "Unrecognised texture type");
+ BLI_assert(false && "Unrecognized texture type");
break;
}
- /* Update Framebuffer Resolution. */
+ /* Update Frame-buffer Resolution. */
int width_of_miplayer, height_of_miplayer;
if (miplevel <= 0) {
width_of_miplayer = texture->width_get();
@@ -1022,7 +1022,7 @@ bool MTLFrameBuffer::add_depth_attachment(gpu::MTLTexture *texture, int miplevel
height_of_miplayer = max_ii(texture->height_get() >> miplevel, 1);
}
- /* Update Framebuffer Resolution. */
+ /* Update Frame-buffer Resolution. */
if (width_ == 0 || height_ == 0) {
this->size_set(width_of_miplayer, height_of_miplayer);
this->scissor_reset();
@@ -1129,11 +1129,11 @@ bool MTLFrameBuffer::add_stencil_attachment(gpu::MTLTexture *texture, int miplev
mtl_stencil_attachment_.depth_plane = 0;
break;
default:
- BLI_assert(false && "Unrecognised texture type");
+ BLI_assert(false && "Unrecognized texture type");
break;
}
- /* Update Framebuffer Resolution. */
+ /* Update Frame-buffer Resolution. */
int width_of_miplayer, height_of_miplayer;
if (miplevel <= 0) {
width_of_miplayer = texture->width_get();
@@ -1144,7 +1144,7 @@ bool MTLFrameBuffer::add_stencil_attachment(gpu::MTLTexture *texture, int miplev
height_of_miplayer = max_ii(texture->height_get() >> miplevel, 1);
}
- /* Update Framebuffer Resolution. */
+ /* Update Frame-buffer Resolution. */
if (width_ == 0 || height_ == 0) {
this->size_set(width_of_miplayer, height_of_miplayer);
this->scissor_reset();
@@ -1376,7 +1376,7 @@ bool MTLFrameBuffer::reset_clear_state()
/** \} */
/* -------------------------------------------------------------------- */
-/** \ Fetch values and Framebuffer status
+/** \ Fetch values and Frame-buffer status
* \{ */
bool MTLFrameBuffer::has_attachment_at_slot(uint slot)
@@ -1506,7 +1506,7 @@ MTLRenderPassDescriptor *MTLFrameBuffer::bake_render_pass_descriptor(bool load_c
BLI_assert(metal_ctx && metal_ctx->get_inside_frame());
UNUSED_VARS_NDEBUG(metal_ctx);
- /* If Framebuffer has been modified, regenerate descriptor. */
+ /* If Frame-buffer has been modified, regenerate descriptor. */
if (is_dirty_) {
/* Clear all configs. */
for (int config = 0; config < 3; config++) {
diff --git a/source/blender/gpu/metal/mtl_index_buffer.hh b/source/blender/gpu/metal/mtl_index_buffer.hh
new file mode 100644
index 00000000000..702aa7f27d6
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_index_buffer.hh
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+#include "gpu_index_buffer_private.hh"
+#include "mtl_context.hh"
+#include <Cocoa/Cocoa.h>
+#include <Metal/Metal.h>
+#include <QuartzCore/QuartzCore.h>
+
+namespace blender::gpu {
+
+class MTLIndexBuf : public IndexBuf {
+ friend class MTLBatch;
+ friend class MTLDrawList;
+
+ private:
+ /* Metal buffer resource. */
+ gpu::MTLBuffer *ibo_ = nullptr;
+ uint64_t alloc_size_ = 0;
+
+#ifndef NDEBUG
+ /* Flags whether point index buffer has been compacted
+ * to remove false restart indices. */
+ bool point_restarts_stripped_ = false;
+#endif
+
+ /* Optimized index buffers.
+ * NOTE(Metal): This optimization encodes a new index buffer following
+ * #TriangleList topology. Parsing of Index buffers is more optimal
+ * when not using restart-compatible primitive topology types. */
+ GPUPrimType optimized_primitive_type_;
+ gpu::MTLBuffer *optimized_ibo_ = nullptr;
+ uint32_t emulated_v_count = 0;
+ void free_optimized_buffer();
+
+ /* Flags whether an index buffer can be optimized.
+ * For index buffers which are partially modified
+ * on the host, or by the GPU, optimization cannot be performed. */
+ bool can_optimize_ = true;
+
+ public:
+ ~MTLIndexBuf();
+
+ void bind_as_ssbo(uint32_t binding) override;
+ const uint32_t *read() const override;
+
+ void upload_data() override;
+ void update_sub(uint32_t start, uint32_t len, const void *data) override;
+
+ /* #get_index_buffer can conditionally return an optimized index buffer of a
+ * differing format, if it is concluded that optimization is preferred
+ * for the given inputs.
+ * Index buffer optimization is used to replace restart-compatible
+ * primitive types with non-restart-compatible ones such as #TriangleList and
+ * #LineList. This improves GPU execution for these types significantly, while
+ * only incurring a small performance penalty.
+ *
+ * This is also used to emulate unsupported topology types
+ * such as triangle fan. */
+ id<MTLBuffer> get_index_buffer(GPUPrimType &in_out_primitive_type, uint &in_out_v_count);
+ void flag_can_optimize(bool can_optimize);
+
+ static MTLIndexType gpu_index_type_to_metal(GPUIndexBufType type)
+ {
+ return (type == GPU_INDEX_U16) ? MTLIndexTypeUInt16 : MTLIndexTypeUInt32;
+ }
+
+ private:
+ void strip_restart_indices() override;
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("MTLIndexBuf")
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_index_buffer.mm b/source/blender/gpu/metal/mtl_index_buffer.mm
new file mode 100644
index 00000000000..9712dce7b40
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_index_buffer.mm
@@ -0,0 +1,517 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "mtl_index_buffer.hh"
+#include "mtl_context.hh"
+#include "mtl_debug.hh"
+
+#include "BLI_span.hh"
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Core MTLIndexBuf implementation.
+ * \{ */
+
+MTLIndexBuf::~MTLIndexBuf()
+{
+ if (ibo_ != nullptr && !this->is_subrange_) {
+ ibo_->free();
+ }
+ this->free_optimized_buffer();
+}
+
+void MTLIndexBuf::free_optimized_buffer()
+{
+ if (optimized_ibo_) {
+ optimized_ibo_->free();
+ optimized_ibo_ = nullptr;
+ }
+}
+
+void MTLIndexBuf::bind_as_ssbo(uint32_t binding)
+{
+ /* Flag buffer as incompatible with optimized/patched buffers as contents
+ * can now have partial modifications from the GPU. */
+ this->flag_can_optimize(false);
+ this->free_optimized_buffer();
+
+ /* Ensure we have a valid IBO. */
+ BLI_assert(this->ibo_);
+
+ /* TODO(Metal): Support index buffer SSBO's. Dependent on compute implementation. */
+ MTL_LOG_WARNING("MTLIndexBuf::bind_as_ssbo not yet implemented!\n");
+}
+
+const uint32_t *MTLIndexBuf::read() const
+{
+ if (ibo_ != nullptr) {
+
+ /* Return host pointer. */
+ void *data = ibo_->get_host_ptr();
+ return static_cast<uint32_t *>(data);
+ }
+ BLI_assert(false && "Index buffer not ready to be read.");
+ return nullptr;
+}
+
+void MTLIndexBuf::upload_data()
+{
+ /* Handle sub-range upload. */
+ if (is_subrange_) {
+ MTLIndexBuf *mtlsrc = static_cast<MTLIndexBuf *>(src_);
+ mtlsrc->upload_data();
+
+#ifndef NDEBUG
+ BLI_assert_msg(!mtlsrc->point_restarts_stripped_,
+ "Cannot use sub-range on stripped point buffer.");
+#endif
+
+ /* If parent sub-range allocation has changed,
+ * update our index buffer. */
+ if (alloc_size_ != mtlsrc->alloc_size_ || ibo_ != mtlsrc->ibo_) {
+
+ /* Update index buffer and allocation from source. */
+ alloc_size_ = mtlsrc->alloc_size_;
+ ibo_ = mtlsrc->ibo_;
+
+ /* Reset any allocated patched or optimized index buffers. */
+ this->free_optimized_buffer();
+ }
+ return;
+ }
+
+ /* If new data ready, and index buffer already exists, release current. */
+ if ((ibo_ != nullptr) && (this->data_ != nullptr)) {
+ MTL_LOG_INFO("Re-creating index buffer with new data. IndexBuf %p\n", this);
+ ibo_->free();
+ ibo_ = nullptr;
+ }
+
+ /* Prepare Buffer and Upload Data. */
+ if (ibo_ == nullptr && data_ != nullptr) {
+ alloc_size_ = this->size_get();
+ if (alloc_size_ == 0) {
+ MTL_LOG_WARNING("[Metal] Warning! Trying to allocate index buffer with size=0 bytes\n");
+ }
+ else {
+ ibo_ = MTLContext::get_global_memory_manager().allocate_with_data(alloc_size_, true, data_);
+ BLI_assert(ibo_);
+ ibo_->set_label(@"Index Buffer");
+ }
+
+ /* No need to keep copy of data_ in system memory. */
+ MEM_SAFE_FREE(data_);
+ }
+}
+
+void MTLIndexBuf::update_sub(uint32_t start, uint32_t len, const void *data)
+{
+ BLI_assert(!is_subrange_);
+
+ /* If host-side data still exists, modify and upload as normal */
+ if (data_ != nullptr) {
+
+ /* Free index buffer if one exists. */
+ if (ibo_ != nullptr && !this->is_subrange_) {
+ ibo_->free();
+ ibo_ = nullptr;
+ }
+
+ BLI_assert(start + len < this->size_get());
+
+ /* Apply start byte offset to data pointer. */
+ void *modified_base_ptr = data_;
+ uint8_t *ptr = static_cast<uint8_t *>(modified_base_ptr);
+ ptr += start;
+ modified_base_ptr = static_cast<void *>(ptr);
+
+ /* Modify host-side data. */
+ memcpy(modified_base_ptr, data, len);
+ return;
+ }
+
+ /* Verify buffer. */
+ BLI_assert(ibo_ != nullptr);
+
+ /* Otherwise, we will inject a data update, using staged data, into the command stream.
+ * Stage update contents in temporary buffer. */
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ BLI_assert(ctx);
+ MTLTemporaryBuffer range = ctx->get_scratchbuffer_manager().scratch_buffer_allocate_range(len);
+ memcpy(range.data, data, len);
+
+ /* Copy updated contents into primary buffer.
+ * These changes need to be uploaded via blit to ensure the data copies happen in-order. */
+ id<MTLBuffer> dest_buffer = ibo_->get_metal_buffer();
+ BLI_assert(dest_buffer != nil);
+
+ id<MTLBlitCommandEncoder> enc = ctx->main_command_buffer.ensure_begin_blit_encoder();
+ [enc copyFromBuffer:range.metal_buffer
+ sourceOffset:(uint32_t)range.buffer_offset
+ toBuffer:dest_buffer
+ destinationOffset:start
+ size:len];
+
+ /* Synchronize changes back to host to ensure CPU-side data is up-to-date for non
+ * Shared buffers. */
+ if (dest_buffer.storageMode == MTLStorageModeManaged) {
+ [enc synchronizeResource:dest_buffer];
+ }
+
+ /* Invalidate patched/optimized buffers. */
+ this->free_optimized_buffer();
+
+ /* Flag buffer as incompatible with optimized/patched buffers as contents
+ * have partial modifications. */
+ this->flag_can_optimize(false);
+
+ BLI_assert(false);
+}
+
+void MTLIndexBuf::flag_can_optimize(bool can_optimize)
+{
+ can_optimize_ = can_optimize;
+}
+
+/** \} */
+
+/** \name Index buffer optimization and topology emulation
+ *
+ * Index buffer optimization and emulation. Optimize index buffers by
+ * eliminating restart-indices.
+ * Emulate unsupported index types e.g. Triangle Fan and Line Loop.
+ * \{ */
+
+/* Returns total vertices in new buffer. */
+template<typename T>
+static uint32_t populate_optimized_tri_strip_buf(Span<T> original_data,
+ MutableSpan<T> output_data,
+ uint32_t input_index_len)
+{
+ /* Generate #TriangleList from #TriangleStrip. */
+ uint32_t current_vert_len = 0;
+ uint32_t current_output_ind = 0;
+ T indices[3];
+
+ for (int c_index = 0; c_index < input_index_len; c_index++) {
+ T current_index = original_data[c_index];
+ if (current_index == T(-1)) {
+ /* Stop current primitive. Move onto next. */
+ current_vert_len = 0;
+ }
+ else {
+ if (current_vert_len < 3) {
+ /* Prepare first triangle.
+ * Cache indices before generating a triangle, in case we have bad primitive-restarts. */
+ indices[current_vert_len] = current_index;
+ }
+
+ /* Emit triangle once we reach 3 input verts in current strip. */
+ if (current_vert_len == 3) {
+ /* First triangle in strip. */
+ output_data[current_output_ind++] = indices[0];
+ output_data[current_output_ind++] = indices[1];
+ output_data[current_output_ind++] = indices[2];
+ }
+ else if (current_vert_len > 3) {
+ /* All other triangles in strip.
+ * These triangles are populated using data from previous 2 vertices
+ * and the latest index. */
+ uint32_t tri_id = current_vert_len - 3;
+ uint32_t base_output_ind = current_output_ind;
+ if ((tri_id % 2) == 0) {
+ output_data[base_output_ind + 0] = output_data[base_output_ind - 2];
+ output_data[base_output_ind + 1] = current_index;
+ output_data[base_output_ind + 2] = output_data[base_output_ind - 1];
+ }
+ else {
+ output_data[base_output_ind + 0] = output_data[base_output_ind - 1];
+ output_data[base_output_ind + 1] = output_data[base_output_ind - 2];
+ output_data[base_output_ind + 2] = current_index;
+ }
+ current_output_ind += 3;
+ }
+
+ /* Increment relative vertex index. */
+ current_vert_len++;
+ }
+ }
+ return current_output_ind;
+}
+
+/* Returns total vertices in new buffer. */
+template<typename T>
+static uint32_t populate_emulated_tri_fan_buf(Span<T> original_data,
+ MutableSpan<T> output_data,
+ uint32_t input_index_len)
+{
+ /* Generate #TriangleList from #TriangleFan. */
+ T base_prim_ind_val = 0;
+ uint32_t current_vert_len = 0;
+ uint32_t current_output_ind = 0;
+ T indices[3];
+
+ for (int c_index = 0; c_index < input_index_len; c_index++) {
+ T current_index = original_data[c_index];
+ if (current_index == T(-1)) {
+ /* Stop current primitive. Move onto next. */
+ current_vert_len = 0;
+ }
+ else {
+ if (current_vert_len < 3) {
+ /* Prepare first triangle.
+ * Cache indices before generating a triangle, in case we have bad primitive-restarts. */
+ indices[current_vert_len] = current_index;
+ }
+
+ /* emit triangle once we reach 3 input verts in current strip. */
+ if (current_vert_len == 3) {
+ /* First triangle in strip. */
+ output_data[current_output_ind++] = indices[0];
+ output_data[current_output_ind++] = indices[1];
+ output_data[current_output_ind++] = indices[2];
+ base_prim_ind_val = indices[0];
+ }
+ else if (current_vert_len > 3) {
+ /* All other triangles in strip.
+ * These triangles are populated using data from previous 2 vertices
+ * and the latest index. */
+ uint32_t base_output_ind = current_output_ind;
+
+ output_data[base_output_ind + 0] = base_prim_ind_val;
+ output_data[base_output_ind + 1] = output_data[base_output_ind - 1];
+ output_data[base_output_ind + 2] = current_index;
+ current_output_ind += 3;
+ }
+
+ /* Increment relative vertex index. */
+ current_vert_len++;
+ }
+ }
+ return current_output_ind;
+}
+
+id<MTLBuffer> MTLIndexBuf::get_index_buffer(GPUPrimType &in_out_primitive_type,
+ uint32_t &in_out_v_count)
+{
+ /* Determine whether to return the original index buffer, or whether we
+ * should emulate an unsupported primitive type, or optimize a restart-
+ * compatible type for faster performance. */
+ bool should_optimize_or_emulate = (in_out_primitive_type == GPU_PRIM_TRI_FAN) ||
+ (in_out_primitive_type == GPU_PRIM_TRI_STRIP);
+ if (!should_optimize_or_emulate || is_subrange_ || !can_optimize_) {
+ /* Ensure we are not optimized. */
+ BLI_assert(this->optimized_ibo_ == nullptr);
+
+ /* Return regular index buffer. */
+ BLI_assert(this->ibo_ && this->ibo_->get_metal_buffer());
+ return this->ibo_->get_metal_buffer();
+ }
+
+ /* Perform optimization on type. */
+ GPUPrimType input_prim_type = in_out_primitive_type;
+ this->upload_data();
+ if (!ibo_ && optimized_ibo_ == nullptr) {
+ /* Cannot optimize buffer if no source IBO exists. */
+ return nil;
+ }
+
+ /* Verify whether existing index buffer is valid. */
+ if (optimized_ibo_ != nullptr && optimized_primitive_type_ != input_prim_type) {
+ BLI_assert_msg(false,
+ "Cannot change the optimized primitive format after generation, as source "
+ "index buffer data is discarded.");
+ return nil;
+ }
+
+ /* Generate optimized index buffer. */
+ if (optimized_ibo_ == nullptr) {
+
+ /* Generate unwrapped index buffer. */
+ switch (input_prim_type) {
+ case GPU_PRIM_TRI_FAN: {
+
+ /* Calculate maximum size. */
+ uint32_t max_possible_verts = (this->index_len_ - 2) * 3;
+ BLI_assert(max_possible_verts > 0);
+
+ /* Allocate new buffer. */
+ optimized_ibo_ = MTLContext::get_global_memory_manager().allocate(
+ max_possible_verts *
+ ((index_type_ == GPU_INDEX_U16) ? sizeof(uint16_t) : sizeof(uint32_t)),
+ true);
+
+ /* Populate new index buffer. */
+ if (index_type_ == GPU_INDEX_U16) {
+ Span<uint16_t> orig_data(static_cast<const uint16_t *>(ibo_->get_host_ptr()),
+ this->index_len_);
+ MutableSpan<uint16_t> output_data(
+ static_cast<uint16_t *>(optimized_ibo_->get_host_ptr()), this->index_len_);
+ emulated_v_count = populate_emulated_tri_fan_buf<uint16_t>(
+ orig_data, output_data, this->index_len_);
+ }
+ else {
+ Span<uint32_t> orig_data(static_cast<const uint32_t *>(ibo_->get_host_ptr()),
+ this->index_len_);
+ MutableSpan<uint32_t> output_data(
+ static_cast<uint32_t *>(optimized_ibo_->get_host_ptr()), this->index_len_);
+ emulated_v_count = populate_emulated_tri_fan_buf<uint32_t>(
+ orig_data, output_data, this->index_len_);
+ }
+
+ BLI_assert(emulated_v_count <= max_possible_verts);
+
+ /* Flush buffer and output. */
+ optimized_ibo_->flush();
+ optimized_primitive_type_ = input_prim_type;
+ in_out_v_count = emulated_v_count;
+ in_out_primitive_type = GPU_PRIM_TRIS;
+ }
+
+ case GPU_PRIM_TRI_STRIP: {
+
+ /* Calculate maximum size. */
+ uint32_t max_possible_verts = (this->index_len_ - 2) * 3;
+ BLI_assert(max_possible_verts > 0);
+
+ /* Allocate new buffer. */
+ optimized_ibo_ = MTLContext::get_global_memory_manager().allocate(
+ max_possible_verts *
+ ((index_type_ == GPU_INDEX_U16) ? sizeof(uint16_t) : sizeof(uint32_t)),
+ true);
+
+ /* Populate new index buffer. */
+ if (index_type_ == GPU_INDEX_U16) {
+ Span<uint16_t> orig_data(static_cast<const uint16_t *>(ibo_->get_host_ptr()),
+ this->index_len_);
+ MutableSpan<uint16_t> output_data(
+ static_cast<uint16_t *>(optimized_ibo_->get_host_ptr()), this->index_len_);
+ emulated_v_count = populate_optimized_tri_strip_buf<uint16_t>(
+ orig_data, output_data, this->index_len_);
+ }
+ else {
+ Span<uint32_t> orig_data(static_cast<const uint32_t *>(ibo_->get_host_ptr()),
+ this->index_len_);
+ MutableSpan<uint32_t> output_data(
+ static_cast<uint32_t *>(optimized_ibo_->get_host_ptr()), this->index_len_);
+ emulated_v_count = populate_optimized_tri_strip_buf<uint32_t>(
+ orig_data, output_data, this->index_len_);
+ }
+
+ BLI_assert(emulated_v_count <= max_possible_verts);
+
+ /* Flush buffer and output. */
+ optimized_ibo_->flush();
+ optimized_primitive_type_ = input_prim_type;
+ in_out_v_count = emulated_v_count;
+ in_out_primitive_type = GPU_PRIM_TRIS;
+ } break;
+
+ case GPU_PRIM_LINE_STRIP: {
+ /* TODO(Metal): Line strip topology types would benefit from optimization to remove
+ * primitive restarts, however, these do not occur frequently, nor with
+ * significant geometry counts. */
+ MTL_LOG_INFO("TODO: Primitive topology: Optimize line strip topology types\n");
+ } break;
+
+ case GPU_PRIM_LINE_LOOP: {
+ /* TODO(Metal): Line Loop primitive type requires use of optimized index buffer for
+ * emulation, if used with indexed rendering. This path is currently not hit as #LineLoop
+ * does not currently appear to be used alongside an index buffer. */
+ MTL_LOG_WARNING(
+ "TODO: Primitive topology: Line Loop Index buffer optimization required for "
+ "emulation.\n");
+ } break;
+
+ case GPU_PRIM_TRIS:
+ case GPU_PRIM_LINES:
+ case GPU_PRIM_POINTS: {
+ /* Should not get here - TRIS/LINES/POINTS do not require emulation or optimization. */
+ BLI_assert_unreachable();
+ return nil;
+ }
+
+ default:
+ /* Should not get here - Invalid primitive type. */
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
+ /* Return optimized buffer. */
+ if (optimized_ibo_ != nullptr) {
+
+ /* Delete original buffer if one still exists, as we do no need it. */
+ if (ibo_ != nullptr) {
+ ibo_->free();
+ ibo_ = nullptr;
+ }
+
+ /* Output params. */
+ in_out_v_count = emulated_v_count;
+ in_out_primitive_type = GPU_PRIM_TRIS;
+ return optimized_ibo_->get_metal_buffer();
+ }
+ return nil;
+}
+
+void MTLIndexBuf::strip_restart_indices()
+{
+ /* We remove point buffer primitive restart indices by swapping restart indices
+ * with the first valid index at the end of the index buffer and reducing the
+ * length. Primitive restarts are invalid in Metal for non-restart-compatible
+ * primitive types. We also cannot just use zero unlike for Lines and Triangles,
+ * as we cannot create de-generative point primitives to hide geometry, as each
+ * point is independent.
+ * Instead, we must remove these hidden indices from the index buffer.
+ * NOTE: This happens prior to index squeezing so operate on 32-bit indices. */
+ MutableSpan<uint32_t> uint_idx(static_cast<uint32_t *>(data_), index_len_);
+ for (uint i = 0; i < index_len_; i++) {
+ if (uint_idx[i] == 0xFFFFFFFFu) {
+
+ /* Find swap index at end of index buffer. */
+ int swap_index = -1;
+ for (uint j = index_len_ - 1; j >= i; j--) {
+ /* If end index is restart, just reduce length. */
+ if (uint_idx[j] == 0xFFFFFFFFu) {
+ index_len_--;
+ continue;
+ }
+ /* Otherwise assign swap index. */
+ swap_index = j;
+ break;
+ }
+
+ /* If swap index is not valid, then there were no valid non-restart indices
+ * to swap with. However, the above loop will have removed these indices by
+ * reducing the length of indices. Debug assertions verify that the restart
+ * index is no longer included. */
+ if (swap_index == -1) {
+ BLI_assert(index_len_ <= i);
+ }
+ else {
+ /* If we have found an index we can swap with, flip the values.
+ * We also reduce the length. As per above loop, swap_index should
+ * now be outside the index length range. */
+ uint32_t swap_index_value = uint_idx[swap_index];
+ uint_idx[i] = swap_index_value;
+ uint_idx[swap_index] = 0xFFFFFFFFu;
+ index_len_--;
+ BLI_assert(index_len_ <= swap_index);
+ }
+ }
+ }
+
+#ifndef NDEBUG
+ /* Flag as having been stripped to ensure invalid usage is tracked. */
+ point_restarts_stripped_ = true;
+#endif
+}
+
+/** \} */
+
+} // blender::gpu
diff --git a/source/blender/gpu/metal/mtl_memory.hh b/source/blender/gpu/metal/mtl_memory.hh
index daa049e78af..df80df6543f 100644
--- a/source/blender/gpu/metal/mtl_memory.hh
+++ b/source/blender/gpu/metal/mtl_memory.hh
@@ -41,7 +41,7 @@
* Each frame, the next scratch buffer is reset, then later flushed upon
* command buffer submission.
*
- * Note: This is allocated per-context due to allocations being tied
+ * NOTE: This is allocated per-context due to allocations being tied
* to workload submissions and context-specific submissions.
*
* Examples of scratch buffer usage are:
@@ -73,13 +73,15 @@
* to ensure they are not prematurely re-used before they have finished being
* used by the GPU.
*
- * Note: The MTLBufferPool is a global construct which can be fetched from anywhere.
+ * NOTE: The MTLBufferPool is a global construct which can be fetched from anywhere.
*
* Usage:
* MTLContext::get_global_memory_manager(); - static routine to fetch global memory manager.
*
- * gpu::MTLBuffer *allocate_buffer(size, is_cpu_visibile, bytes=nullptr)
- * gpu::MTLBuffer *allocate_buffer_aligned(size, alignment, is_cpu_visibile, bytes=nullptr)
+ * gpu::MTLBuffer *allocate(size, is_cpu_visibile)
+ * gpu::MTLBuffer *allocate_aligned(size, alignment, is_cpu_visibile)
+ * gpu::MTLBuffer *allocate_with_data(size, is_cpu_visibile, data_ptr)
+ * gpu::MTLBuffer *allocate_aligned_with_data(size, alignment, is_cpu_visibile, data_ptr)
*/
/* Debug memory statistics: Disabled by Macro rather than guarded for
@@ -271,7 +273,7 @@ struct CompareMTLBuffer {
* when the next MTLSafeFreeList is created, to allow the existing pool to be released once
* the reference count hits zero after submitted command buffers complete.
*
- * Note: the Metal API independently tracks resources used by command buffers for the purpose of
+ * NOTE: the Metal API independently tracks resources used by command buffers for the purpose of
* keeping resources alive while in-use by the driver and CPU, however, this differs from the
* MTLSafeFreeList mechanism in the Metal backend, which exists for the purpose of allowing
* previously allocated MTLBuffer resources to be re-used. This allows us to save on the expensive
@@ -389,11 +391,13 @@ class MTLBufferPool {
void init(id<MTLDevice> device);
~MTLBufferPool();
- gpu::MTLBuffer *allocate_buffer(uint64_t size, bool cpu_visible, const void *bytes = nullptr);
- gpu::MTLBuffer *allocate_buffer_aligned(uint64_t size,
- uint alignment,
- bool cpu_visible,
- const void *bytes = nullptr);
+ gpu::MTLBuffer *allocate(uint64_t size, bool cpu_visible);
+ gpu::MTLBuffer *allocate_aligned(uint64_t size, uint alignment, bool cpu_visible);
+ gpu::MTLBuffer *allocate_with_data(uint64_t size, bool cpu_visible, const void *data = nullptr);
+ gpu::MTLBuffer *allocate_aligned_with_data(uint64_t size,
+ uint alignment,
+ bool cpu_visible,
+ const void *data = nullptr);
bool free_buffer(gpu::MTLBuffer *buffer);
/* Flush MTLSafeFreeList buffers, for completed lists in `completed_safelist_queue_`,
diff --git a/source/blender/gpu/metal/mtl_memory.mm b/source/blender/gpu/metal/mtl_memory.mm
index e5db32ed1b1..788736bdfad 100644
--- a/source/blender/gpu/metal/mtl_memory.mm
+++ b/source/blender/gpu/metal/mtl_memory.mm
@@ -57,17 +57,25 @@ void MTLBufferPool::free()
buffer_pools_.clear();
}
-gpu::MTLBuffer *MTLBufferPool::allocate_buffer(uint64_t size, bool cpu_visible, const void *bytes)
+gpu::MTLBuffer *MTLBufferPool::allocate(uint64_t size, bool cpu_visible)
{
/* Allocate buffer with default HW-compatible alignment of 256 bytes.
* See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */
- return this->allocate_buffer_aligned(size, 256, cpu_visible, bytes);
+ return this->allocate_aligned(size, 256, cpu_visible);
}
-gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size,
- uint alignment,
- bool cpu_visible,
- const void *bytes)
+gpu::MTLBuffer *MTLBufferPool::allocate_with_data(uint64_t size,
+ bool cpu_visible,
+ const void *data)
+{
+ /* Allocate buffer with default HW-compatible alignment of 256 bytes.
+ * See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf for more. */
+ return this->allocate_aligned_with_data(size, 256, cpu_visible, data);
+}
+
+gpu::MTLBuffer *MTLBufferPool::allocate_aligned(uint64_t size,
+ uint32_t alignment,
+ bool cpu_visible)
{
/* Check not required. Main GPU module usage considered thread-safe. */
// BLI_assert(BLI_thread_is_main());
@@ -153,15 +161,6 @@ gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size,
/* Flag buffer as actively in-use. */
new_buffer->flag_in_use(true);
- /* Upload initial data if provided -- Size based on original size param, not aligned size*/
- if (bytes) {
- BLI_assert(!(options & MTLResourceStorageModePrivate));
- BLI_assert(size <= aligned_alloc_size);
- BLI_assert(size <= [new_buffer->get_metal_buffer() length]);
- memcpy(new_buffer->get_host_ptr(), bytes, size);
- new_buffer->flush_range(0, size);
- }
-
#if MTL_DEBUG_MEMORY_STATISTICS == 1
this->per_frame_allocation_count++;
#endif
@@ -169,6 +168,23 @@ gpu::MTLBuffer *MTLBufferPool::allocate_buffer_aligned(uint64_t size,
return new_buffer;
}
+gpu::MTLBuffer *MTLBufferPool::allocate_aligned_with_data(uint64_t size,
+ uint32_t alignment,
+ bool cpu_visible,
+ const void *data)
+{
+ gpu::MTLBuffer *buf = this->allocate_aligned(size, 256, cpu_visible);
+
+ /* Upload initial data. */
+ BLI_assert(data != nullptr);
+ BLI_assert(!(buf->get_resource_options() & MTLResourceStorageModePrivate));
+ BLI_assert(size <= buf->get_size());
+ BLI_assert(size <= [buf->get_metal_buffer() length]);
+ memcpy(buf->get_host_ptr(), data, size);
+ buf->flush_range(0, size);
+ return buf;
+}
+
bool MTLBufferPool::free_buffer(gpu::MTLBuffer *buffer)
{
/* Ensure buffer is flagged as in-use. I.e. has not already been returned to memory pools. */
@@ -356,7 +372,7 @@ void MTLBufferPool::insert_buffer_into_pool(MTLResourceOptions options, gpu::MTL
#if MTL_DEBUG_MEMORY_STATISTICS == 1
/* Debug statistics. */
- allocations_in_pool_ += buffer->size;
+ allocations_in_pool_ += buffer->get_size();
buffers_in_pool_++;
#endif
}
@@ -413,7 +429,7 @@ void MTLSafeFreeList::decrement_reference()
{
lock_.lock();
BLI_assert(in_free_queue_ == false);
- int ref_count = reference_count_--;
+ int ref_count = --reference_count_;
if (ref_count == 0) {
MTLContext::get_global_memory_manager().push_completed_safe_list(this);
@@ -534,9 +550,10 @@ void gpu::MTLBuffer::set_label(NSString *str)
void gpu::MTLBuffer::debug_ensure_used()
{
/* Debug: If buffer is not flagged as in-use, this is a problem. */
- BLI_assert(in_use_ &&
- "Buffer should be marked as 'in-use' if being actively used by an instance. Buffer "
- "has likely already been freed.");
+ BLI_assert_msg(
+ in_use_,
+ "Buffer should be marked as 'in-use' if being actively used by an instance. Buffer "
+ "has likely already been freed.");
}
void gpu::MTLBuffer::flush()
@@ -651,9 +668,9 @@ MTLTemporaryBuffer MTLScratchBufferManager::scratch_buffer_allocate_range_aligne
/* Ensure scratch buffer allocation alignment adheres to offset alignment requirements. */
alignment = max_uu(alignment, 256);
- BLI_assert(current_scratch_buffer_ >= 0 && "Scratch Buffer index not set");
+ BLI_assert_msg(current_scratch_buffer_ >= 0, "Scratch Buffer index not set");
MTLCircularBuffer *current_scratch_buff = this->scratch_buffers_[current_scratch_buffer_];
- BLI_assert(current_scratch_buff != nullptr && "Scratch Buffer does not exist");
+ BLI_assert_msg(current_scratch_buff != nullptr, "Scratch Buffer does not exist");
MTLTemporaryBuffer allocated_range = current_scratch_buff->allocate_range_aligned(alloc_size,
alignment);
BLI_assert(allocated_range.size >= alloc_size && allocated_range.size <= alloc_size + alignment);
diff --git a/source/blender/gpu/metal/mtl_primitive.hh b/source/blender/gpu/metal/mtl_primitive.hh
new file mode 100644
index 00000000000..b32854a04bf
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_primitive.hh
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ *
+ * Encapsulation of Frame-buffer states (attached textures, viewport, scissors).
+ */
+
+#pragma once
+
+#include "BLI_assert.h"
+
+#include "GPU_primitive.h"
+
+#include <Metal/Metal.h>
+
+namespace blender::gpu {
+
+/** Utility functions **/
+static inline MTLPrimitiveTopologyClass mtl_prim_type_to_topology_class(MTLPrimitiveType prim_type)
+{
+ switch (prim_type) {
+ case MTLPrimitiveTypePoint:
+ return MTLPrimitiveTopologyClassPoint;
+ case MTLPrimitiveTypeLine:
+ case MTLPrimitiveTypeLineStrip:
+ return MTLPrimitiveTopologyClassLine;
+ case MTLPrimitiveTypeTriangle:
+ case MTLPrimitiveTypeTriangleStrip:
+ return MTLPrimitiveTopologyClassTriangle;
+ }
+ return MTLPrimitiveTopologyClassUnspecified;
+}
+
+static inline MTLPrimitiveType gpu_prim_type_to_metal(GPUPrimType prim_type)
+{
+ switch (prim_type) {
+ case GPU_PRIM_POINTS:
+ return MTLPrimitiveTypePoint;
+ case GPU_PRIM_LINES:
+ case GPU_PRIM_LINES_ADJ:
+ case GPU_PRIM_LINE_LOOP:
+ return MTLPrimitiveTypeLine;
+ case GPU_PRIM_LINE_STRIP:
+ case GPU_PRIM_LINE_STRIP_ADJ:
+ return MTLPrimitiveTypeLineStrip;
+ case GPU_PRIM_TRIS:
+ case GPU_PRIM_TRI_FAN:
+ case GPU_PRIM_TRIS_ADJ:
+ return MTLPrimitiveTypeTriangle;
+ case GPU_PRIM_TRI_STRIP:
+ return MTLPrimitiveTypeTriangleStrip;
+ case GPU_PRIM_NONE:
+ return MTLPrimitiveTypePoint;
+ };
+}
+
+/* Certain primitive types are not supported in Metal, and require emulation.
+ * `GPU_PRIM_LINE_LOOP` and `GPU_PRIM_TRI_FAN` required index buffer patching.
+ * Adjacency types do not need emulation as the input structure is the same,
+ * and access is controlled from the vertex shader through SSBO vertex fetch.
+ * -- These Adj cases are only used in geometry shaders in OpenGL. */
+static inline bool mtl_needs_topology_emulation(GPUPrimType prim_type)
+{
+
+ BLI_assert(prim_type != GPU_PRIM_NONE);
+ switch (prim_type) {
+ case GPU_PRIM_LINE_LOOP:
+ case GPU_PRIM_TRI_FAN:
+ return true;
+ default:
+ return false;
+ }
+ return false;
+}
+
+static inline bool mtl_vertex_count_fits_primitive_type(uint32_t vertex_count,
+ MTLPrimitiveType prim_type)
+{
+ if (vertex_count == 0) {
+ return false;
+ }
+
+ switch (prim_type) {
+ case MTLPrimitiveTypeLineStrip:
+ return (vertex_count > 1);
+ case MTLPrimitiveTypeLine:
+ return (vertex_count % 2 == 0);
+ case MTLPrimitiveTypePoint:
+ return (vertex_count > 0);
+ case MTLPrimitiveTypeTriangle:
+ return (vertex_count % 3 == 0);
+ case MTLPrimitiveTypeTriangleStrip:
+ return (vertex_count > 2);
+ }
+ BLI_assert(false);
+ return false;
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_pso_descriptor_state.hh b/source/blender/gpu/metal/mtl_pso_descriptor_state.hh
new file mode 100644
index 00000000000..1906350679a
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_pso_descriptor_state.hh
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+#pragma once
+
+#include "GPU_vertex_format.h"
+
+#include <Metal/Metal.h>
+
+namespace blender::gpu {
+
+/** Vertex attribute and buffer descriptor wrappers
+ * for use in PSO construction and caching. */
+struct MTLVertexAttributeDescriptorPSO {
+ MTLVertexFormat format;
+ int offset;
+ int buffer_index;
+ GPUVertFetchMode format_conversion_mode;
+
+ bool operator==(const MTLVertexAttributeDescriptorPSO &other) const
+ {
+ return (format == other.format) && (offset == other.offset) &&
+ (buffer_index == other.buffer_index) &&
+ (format_conversion_mode == other.format_conversion_mode);
+ }
+
+ uint64_t hash() const
+ {
+ return (uint64_t)((uint64_t)this->format ^ (this->offset << 4) ^ (this->buffer_index << 8) ^
+ (this->format_conversion_mode << 12));
+ }
+};
+
+struct MTLVertexBufferLayoutDescriptorPSO {
+ MTLVertexStepFunction step_function;
+ int step_rate;
+ int stride;
+
+ bool operator==(const MTLVertexBufferLayoutDescriptorPSO &other) const
+ {
+ return (step_function == other.step_function) && (step_rate == other.step_rate) &&
+ (stride == other.stride);
+ }
+
+ uint64_t hash() const
+ {
+ return (uint64_t)((uint64_t)this->step_function ^ (this->step_rate << 4) ^
+ (this->stride << 8));
+ }
+};
+
+/* SSBO attribute state caching. */
+struct MTLSSBOAttribute {
+
+ int mtl_attribute_index;
+ int vbo_id;
+ int attribute_offset;
+ int per_vertex_stride;
+ int attribute_format;
+ bool is_instance;
+
+ MTLSSBOAttribute(){};
+ MTLSSBOAttribute(
+ int attribute_ind, int vertexbuffer_ind, int offset, int stride, int format, bool instanced)
+ : mtl_attribute_index(attribute_ind),
+ vbo_id(vertexbuffer_ind),
+ attribute_offset(offset),
+ per_vertex_stride(stride),
+ attribute_format(format),
+ is_instance(instanced)
+ {
+ }
+
+ bool operator==(const MTLSSBOAttribute &other) const
+ {
+ return (memcmp(this, &other, sizeof(MTLSSBOAttribute)) == 0);
+ }
+};
+
+struct MTLVertexDescriptor {
+
+ /* Core Vertex Attributes. */
+ MTLVertexAttributeDescriptorPSO attributes[GPU_VERT_ATTR_MAX_LEN];
+ MTLVertexBufferLayoutDescriptorPSO
+ buffer_layouts[GPU_BATCH_VBO_MAX_LEN + GPU_BATCH_INST_VBO_MAX_LEN];
+ int num_attributes;
+ int num_vert_buffers;
+ MTLPrimitiveTopologyClass prim_topology_class;
+
+ /* WORKAROUND: SSBO Vertex-fetch attributes -- These follow the same structure
+ * but have slightly different binding rules, passed in via uniform
+ * push constant data block. */
+ bool uses_ssbo_vertex_fetch;
+ MTLSSBOAttribute ssbo_attributes[GPU_VERT_ATTR_MAX_LEN];
+ int num_ssbo_attributes;
+
+ bool operator==(const MTLVertexDescriptor &other) const
+ {
+ if ((this->num_attributes != other.num_attributes) ||
+ (this->num_vert_buffers != other.num_vert_buffers)) {
+ return false;
+ }
+ if (this->prim_topology_class != other.prim_topology_class) {
+ return false;
+ };
+
+ for (const int a : IndexRange(this->num_attributes)) {
+ if (!(this->attributes[a] == other.attributes[a])) {
+ return false;
+ }
+ }
+
+ for (const int b : IndexRange(this->num_vert_buffers)) {
+ if (!(this->buffer_layouts[b] == other.buffer_layouts[b])) {
+ return false;
+ }
+ }
+
+ /* NOTE: No need to compare SSBO attributes, as these will match attribute bindings for the
+ * given shader. These are simply extra pre-resolved properties we want to include in the
+ * cache. */
+ return true;
+ }
+
+ uint64_t hash() const
+ {
+ uint64_t hash = (uint64_t)(this->num_attributes ^ this->num_vert_buffers);
+ for (const int a : IndexRange(this->num_attributes)) {
+ hash ^= this->attributes[a].hash() << a;
+ }
+
+ for (const int b : IndexRange(this->num_vert_buffers)) {
+ hash ^= this->buffer_layouts[b].hash() << (b + 10);
+ }
+
+ /* NOTE: SSBO vertex fetch members not hashed as these will match attribute bindings. */
+ return hash;
+ }
+};
+
+/* Metal Render Pipeline State Descriptor -- All unique information which feeds PSO creation. */
+struct MTLRenderPipelineStateDescriptor {
+ /* This state descriptor will contain ALL parameters which generate a unique PSO.
+ * We will then use this state-object to efficiently look-up or create a
+ * new PSO for the current shader.
+ *
+ * Unlike the 'MTLContextGlobalShaderPipelineState', this struct contains a subset of
+ * parameters used to distinguish between unique PSOs. This struct is hash-able and only contains
+ * those parameters which are required by PSO generation. Non-unique state such as bound
+ * resources is not tracked here, as it does not require a unique PSO permutation if changed. */
+
+ /* Input Vertex Descriptor. */
+ MTLVertexDescriptor vertex_descriptor;
+
+ /* Render Target attachment state.
+ * Assign to #MTLPixelFormatInvalid if not used. */
+ int num_color_attachments;
+ MTLPixelFormat color_attachment_format[GPU_FB_MAX_COLOR_ATTACHMENT];
+ MTLPixelFormat depth_attachment_format;
+ MTLPixelFormat stencil_attachment_format;
+
+ /* Render Pipeline State affecting PSO creation. */
+ bool blending_enabled;
+ MTLBlendOperation alpha_blend_op;
+ MTLBlendOperation rgb_blend_op;
+ MTLBlendFactor dest_alpha_blend_factor;
+ MTLBlendFactor dest_rgb_blend_factor;
+ MTLBlendFactor src_alpha_blend_factor;
+ MTLBlendFactor src_rgb_blend_factor;
+
+ /* Global color write mask as this cannot be specified per attachment. */
+ MTLColorWriteMask color_write_mask;
+
+ /* Point size required by point primitives. */
+ float point_size = 0.0f;
+
+ /* Comparison Operator for caching. */
+ bool operator==(const MTLRenderPipelineStateDescriptor &other) const
+ {
+ if (!(vertex_descriptor == other.vertex_descriptor)) {
+ return false;
+ }
+
+ if ((num_color_attachments != other.num_color_attachments) ||
+ (depth_attachment_format != other.depth_attachment_format) ||
+ (stencil_attachment_format != other.stencil_attachment_format) ||
+ (color_write_mask != other.color_write_mask) ||
+ (blending_enabled != other.blending_enabled) || (alpha_blend_op != other.alpha_blend_op) ||
+ (rgb_blend_op != other.rgb_blend_op) ||
+ (dest_alpha_blend_factor != other.dest_alpha_blend_factor) ||
+ (dest_rgb_blend_factor != other.dest_rgb_blend_factor) ||
+ (src_alpha_blend_factor != other.src_alpha_blend_factor) ||
+ (src_rgb_blend_factor != other.src_rgb_blend_factor) ||
+ (vertex_descriptor.prim_topology_class != other.vertex_descriptor.prim_topology_class) ||
+ (point_size != other.point_size)) {
+ return false;
+ }
+
+ /* Attachments can be skipped, so num_color_attachments will not define the range. */
+ for (const int c : IndexRange(GPU_FB_MAX_COLOR_ATTACHMENT)) {
+ if (color_attachment_format[c] != other.color_attachment_format[c]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ uint64_t hash() const
+ {
+ /* NOTE(Metal): Current setup aims to minimize overlap of parameters
+ * which are more likely to be different, to ensure earlier hash
+ * differences without having to fallback to comparisons.
+ * Though this could likely be further improved to remove
+ * has collisions. */
+
+ uint64_t hash = this->vertex_descriptor.hash();
+ hash ^= (uint64_t)this->num_color_attachments << 16; /* up to 6 (3 bits). */
+ hash ^= (uint64_t)this->depth_attachment_format << 18; /* up to 555 (9 bits). */
+ hash ^= (uint64_t)this->stencil_attachment_format << 20; /* up to 555 (9 bits). */
+ hash ^= (uint64_t)(*(
+ (uint64_t *)&this->vertex_descriptor.prim_topology_class)); /* Up to 3 (2 bits). */
+
+ /* Only include elements in Hash if they are needed - avoids variable null assignments
+ * influencing hash. */
+ if (this->num_color_attachments > 0) {
+ hash ^= (uint64_t)this->color_write_mask << 22; /* 4 bit bit-mask. */
+ hash ^= (uint64_t)this->alpha_blend_op << 26; /* Up to 4 (3 bits). */
+ hash ^= (uint64_t)this->rgb_blend_op << 29; /* Up to 4 (3 bits). */
+ hash ^= (uint64_t)this->dest_alpha_blend_factor << 32; /* Up to 18 (5 bits). */
+ hash ^= (uint64_t)this->dest_rgb_blend_factor << 37; /* Up to 18 (5 bits). */
+ hash ^= (uint64_t)this->src_alpha_blend_factor << 42; /* Up to 18 (5 bits). */
+ hash ^= (uint64_t)this->src_rgb_blend_factor << 47; /* Up to 18 (5 bits). */
+ }
+
+ for (const uint c : IndexRange(GPU_FB_MAX_COLOR_ATTACHMENT)) {
+ hash ^= (uint64_t)this->color_attachment_format[c] << (c + 52); // up to 555 (9 bits)
+ }
+
+ hash |= (uint64_t)((this->blending_enabled && (this->num_color_attachments > 0)) ? 1 : 0)
+ << 62;
+ hash ^= (uint64_t)this->point_size;
+
+ return hash;
+ }
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_query.hh b/source/blender/gpu/metal/mtl_query.hh
new file mode 100644
index 00000000000..03436fcd67d
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_query.hh
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "BLI_vector.hh"
+
+#include "gpu_query.hh"
+#include "mtl_context.hh"
+
+namespace blender::gpu {
+
+class MTLQueryPool : public QueryPool {
+ private:
+ /** Number of queries that have been issued since last initialization.
+ * Should be equal to query_ids_.size(). */
+ uint32_t query_issued_;
+ /** Type of this query pool. */
+ GPUQueryType type_;
+ /** Can only be initialized once. */
+ bool initialized_ = false;
+ MTLVisibilityResultMode mtl_type_;
+ Vector<gpu::MTLBuffer *> buffer_;
+
+ void allocate();
+
+ public:
+ MTLQueryPool();
+ ~MTLQueryPool();
+
+ void init(GPUQueryType type) override;
+
+ void begin_query() override;
+ void end_query() override;
+
+ void get_occlusion_result(MutableSpan<uint32_t> r_values) override;
+};
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm
new file mode 100644
index 00000000000..f4bd5754b77
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_query.mm
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "mtl_query.hh"
+
+namespace blender::gpu {
+
+static const size_t VISIBILITY_COUNT_PER_BUFFER = 512;
+/* Defined in the documentation but can't be queried programmatically:
+ * https://developer.apple.com/documentation/metal/mtlvisibilityresultmode/mtlvisibilityresultmodeboolean?language=objc
+ */
+static const size_t VISIBILITY_RESULT_SIZE_IN_BYTES = 8;
+
+MTLQueryPool::MTLQueryPool()
+{
+ allocate();
+}
+MTLQueryPool::~MTLQueryPool()
+{
+ for (gpu::MTLBuffer *buf : buffer_) {
+ BLI_assert(buf);
+ buf->free();
+ }
+}
+
+void MTLQueryPool::allocate()
+{
+ /* Allocate Metal buffer for visibility results. */
+ size_t buffer_size_in_bytes = VISIBILITY_COUNT_PER_BUFFER * VISIBILITY_RESULT_SIZE_IN_BYTES;
+ gpu::MTLBuffer *buffer = MTLContext::get_global_memory_manager().allocate(buffer_size_in_bytes,
+ true);
+ BLI_assert(buffer);
+ buffer_.append(buffer);
+}
+
+static inline MTLVisibilityResultMode to_mtl_type(GPUQueryType type)
+{
+ if (type == GPU_QUERY_OCCLUSION) {
+ return MTLVisibilityResultModeBoolean;
+ }
+ BLI_assert(0);
+ return MTLVisibilityResultModeBoolean;
+}
+
+void MTLQueryPool::init(GPUQueryType type)
+{
+ BLI_assert(initialized_ == false);
+ initialized_ = true;
+ type_ = type;
+ mtl_type_ = to_mtl_type(type);
+ query_issued_ = 0;
+}
+
+void MTLQueryPool::begin_query()
+{
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+
+ /* Ensure our allocated buffer pool has enough space for the current queries. */
+ int query_id = query_issued_;
+ int requested_buffer = query_id / VISIBILITY_COUNT_PER_BUFFER;
+ if (requested_buffer >= buffer_.size()) {
+ allocate();
+ }
+
+ BLI_assert(requested_buffer < buffer_.size());
+ gpu::MTLBuffer *buffer = buffer_[requested_buffer];
+
+ /* Ensure visibility buffer is set on the context. If visibility buffer changes,
+ * we need to begin a new render pass with an updated reference in the
+ * MTLRenderPassDescriptor. */
+ ctx->set_visibility_buffer(buffer);
+
+ ctx->ensure_begin_render_pass();
+ id<MTLRenderCommandEncoder> rec = ctx->main_command_buffer.get_active_render_command_encoder();
+ [rec setVisibilityResultMode:mtl_type_
+ offset:(query_id % VISIBILITY_COUNT_PER_BUFFER) *
+ VISIBILITY_RESULT_SIZE_IN_BYTES];
+ query_issued_ += 1;
+}
+
+void MTLQueryPool::end_query()
+{
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+
+ id<MTLRenderCommandEncoder> rec = ctx->main_command_buffer.get_active_render_command_encoder();
+ [rec setVisibilityResultMode:MTLVisibilityResultModeDisabled offset:0];
+}
+
+void MTLQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values)
+{
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+
+ /* Create a blit encoder to synchronize the query buffer results between
+ * GPU and CPU when not using shared-memory. */
+ if ([ctx->device hasUnifiedMemory] == false) {
+ id<MTLBlitCommandEncoder> blit_encoder = ctx->main_command_buffer.ensure_begin_blit_encoder();
+ BLI_assert(blit_encoder);
+ for (gpu::MTLBuffer *buf : buffer_) {
+ [blit_encoder synchronizeResource:buf->get_metal_buffer()];
+ }
+ BLI_assert(ctx->get_inside_frame());
+ }
+
+ /* Wait for GPU operations to complete and for query buffer contents
+ * to be synchronized back to host memory. */
+ GPU_finish();
+
+ /* Iterate through all possible visibility buffers and copy results into provided
+ * container. */
+ for (const int i : IndexRange(query_issued_)) {
+ int requested_buffer = i / VISIBILITY_COUNT_PER_BUFFER;
+ const uint64_t *queries = static_cast<const uint64_t *>(
+ buffer_[requested_buffer]->get_host_ptr());
+ r_values[i] = static_cast<uint32_t>(queries[i % VISIBILITY_COUNT_PER_BUFFER]);
+ }
+ ctx->set_visibility_buffer(nullptr);
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_shader.hh b/source/blender/gpu/metal/mtl_shader.hh
new file mode 100644
index 00000000000..64d9d1cf849
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_shader.hh
@@ -0,0 +1,1165 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "GPU_batch.h"
+#include "GPU_capabilities.h"
+#include "GPU_shader.h"
+#include "GPU_vertex_format.h"
+
+#include <Metal/Metal.h>
+#include <QuartzCore/QuartzCore.h>
+#include <functional>
+#include <unordered_map>
+
+#include <mutex>
+#include <thread>
+
+#include "mtl_framebuffer.hh"
+#include "mtl_shader_interface.hh"
+#include "mtl_shader_shared.h"
+#include "mtl_state.hh"
+#include "mtl_texture.hh"
+
+#include "gpu_shader_create_info.hh"
+#include "gpu_shader_private.hh"
+
+namespace blender::gpu {
+
+class MTLShaderInterface;
+class MTLContext;
+
+/* Debug control. */
+#define MTL_SHADER_DEBUG_EXPORT_SOURCE 1
+#define MTL_SHADER_TRANSLATION_DEBUG_OUTPUT 0
+
+/* Separate print used only during development and debugging. */
+#if MTL_SHADER_TRANSLATION_DEBUG_OUTPUT
+# define shader_debug_printf printf
+#else
+# define shader_debug_printf(...) /* Null print. */
+#endif
+
+/* Desired reflection data for a buffer binding. */
+struct MTLBufferArgumentData {
+ uint32_t index;
+ uint32_t size;
+ uint32_t alignment;
+ bool active;
+};
+
+/* Metal Render Pipeline State Instance. */
+struct MTLRenderPipelineStateInstance {
+ /* Function instances with specialization.
+ * Required for argument encoder construction. */
+ id<MTLFunction> vert;
+ id<MTLFunction> frag;
+
+ /* PSO handle. */
+ id<MTLRenderPipelineState> pso;
+
+ /** Derived information. */
+ /* Unique index for PSO variant. */
+ uint32_t shader_pso_index;
+ /* Base bind index for binding uniform buffers, offset based on other
+ * bound buffers such as vertex buffers, as the count can vary. */
+ int base_uniform_buffer_index;
+ /* buffer bind slot used for null attributes (-1 if not needed). */
+ int null_attribute_buffer_index;
+ /* buffer bind used for transform feedback output buffer. */
+ int transform_feedback_buffer_index;
+
+ /** Reflection Data.
+ * Currently used to verify whether uniform buffers of incorrect sizes being bound, due to left
+ * over bindings being used for slots that did not need updating for a particular draw. Metal
+ * Back-end over-generates bindings due to detecting their presence, though in many cases, the
+ * bindings in the source are not all used for a given shader.
+ * This information can also be used to eliminate redundant/unused bindings. */
+ bool reflection_data_available;
+ blender::Vector<MTLBufferArgumentData> buffer_bindings_reflection_data_vert;
+ blender::Vector<MTLBufferArgumentData> buffer_bindings_reflection_data_frag;
+};
+
+/* #MTLShaderBuilder source wrapper used during initial compilation. */
+struct MTLShaderBuilder {
+ NSString *msl_source_vert_ = @"";
+ NSString *msl_source_frag_ = @"";
+
+ /* Generated GLSL source used during compilation. */
+ std::string glsl_vertex_source_ = "";
+ std::string glsl_fragment_source_ = "";
+
+ /* Indicates whether source code has been provided via MSL directly. */
+ bool source_from_msl_ = false;
+};
+
+/**
+ * #MTLShader implements shader compilation, Pipeline State Object (PSO)
+ * creation for rendering and uniform data binding.
+ * Shaders can either be created from native MSL, or generated
+ * from a GLSL source shader using #GPUShaderCreateInfo.
+ *
+ * Shader creation process:
+ * - Create #MTLShader:
+ * - Convert GLSL to MSL source if required.
+ * - set MSL source.
+ * - set Vertex/Fragment function names.
+ * - Create and populate #MTLShaderInterface.
+ **/
+class MTLShader : public Shader {
+ friend shader::ShaderCreateInfo;
+ friend shader::StageInterfaceInfo;
+
+ public:
+ /* Cached SSBO vertex fetch attribute uniform locations. */
+ int uni_ssbo_input_prim_type_loc = -1;
+ int uni_ssbo_input_vert_count_loc = -1;
+ int uni_ssbo_uses_indexed_rendering = -1;
+ int uni_ssbo_uses_index_mode_u16 = -1;
+
+ private:
+ /* Context Handle. */
+ MTLContext *context_ = nullptr;
+
+ /** Transform Feedback. */
+ /* Transform feedback mode. */
+ eGPUShaderTFBType transform_feedback_type_ = GPU_SHADER_TFB_NONE;
+ /* Transform feedback outputs written to TFB buffer. */
+ blender::Vector<std::string> tf_output_name_list_;
+ /* Whether transform feedback is currently active. */
+ bool transform_feedback_active_ = false;
+ /* Vertex buffer to write transform feedback data into. */
+ GPUVertBuf *transform_feedback_vertbuf_ = nullptr;
+
+ /** Shader source code. */
+ MTLShaderBuilder *shd_builder_ = nullptr;
+ NSString *vertex_function_name_ = @"";
+ NSString *fragment_function_name_ = @"";
+
+ /** Compiled shader resources. */
+ id<MTLLibrary> shader_library_vert_ = nil;
+ id<MTLLibrary> shader_library_frag_ = nil;
+ bool valid_ = false;
+
+ /** Render pipeline state and PSO caching. */
+ /* Metal API Descriptor used for creation of unique PSOs based on rendering state. */
+ MTLRenderPipelineDescriptor *pso_descriptor_ = nil;
+ /* Metal backend struct containing all high-level pipeline state parameters
+ * which contribute to instantiation of a unique PSO. */
+ MTLRenderPipelineStateDescriptor current_pipeline_state_;
+ /* Cache of compiled PipelineStateObjects. */
+ blender::Map<MTLRenderPipelineStateDescriptor, MTLRenderPipelineStateInstance *> pso_cache_;
+
+ /* True to enable multi-layered rendering support. */
+ bool uses_mtl_array_index_ = false;
+
+ /** SSBO Vertex fetch pragma options. */
+ /* Indicates whether to pass in VertexBuffer's as regular buffer bindings
+ * and perform vertex assembly manually, rather than using Stage-in.
+ * This is used to give a vertex shader full access to all of the
+ * vertex data.
+ * This is primarily used for optimization techniques and
+ * alternative solutions for Geometry-shaders which are unsupported
+ * by Metal. */
+ bool use_ssbo_vertex_fetch_mode_ = false;
+ /* Output primitive type when rendering sing ssbo_vertex_fetch. */
+ MTLPrimitiveType ssbo_vertex_fetch_output_prim_type_;
+
+ /* Output vertices per original vertex shader instance.
+ * This number will be multiplied by the number of input primitives
+ * from the source draw call. */
+ uint32_t ssbo_vertex_fetch_output_num_verts_ = 0;
+
+ bool ssbo_vertex_attribute_bind_active_ = false;
+ int ssbo_vertex_attribute_bind_mask_ = 0;
+ bool ssbo_vbo_slot_used_[MTL_SSBO_VERTEX_FETCH_MAX_VBOS];
+
+ struct ShaderSSBOAttributeBinding {
+ int attribute_index = -1;
+ int uniform_stride;
+ int uniform_offset;
+ int uniform_fetchmode;
+ int uniform_vbo_id;
+ int uniform_attr_type;
+ };
+ ShaderSSBOAttributeBinding cached_ssbo_attribute_bindings_[MTL_MAX_VERTEX_INPUT_ATTRIBUTES] = {};
+
+ /* Metal Shader Uniform data store.
+ * This blocks is used to store current shader push_constant
+ * data before it is submitted to the GPU. This is currently
+ * stored per shader instance, though depending on GPU module
+ * functionality, this could potentially be a global data store.
+ * This data is associated with the PushConstantBlock, which is
+ * always at index zero in the UBO list. */
+ void *push_constant_data_ = nullptr;
+ bool push_constant_modified_ = false;
+
+ public:
+ MTLShader(MTLContext *ctx, const char *name);
+ MTLShader(MTLContext *ctx,
+ MTLShaderInterface *interface,
+ const char *name,
+ NSString *input_vertex_source,
+ NSString *input_fragment_source,
+ NSString *vertex_function_name_,
+ NSString *fragment_function_name_);
+ ~MTLShader();
+
+ /* Assign GLSL source. */
+ 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;
+
+ /* Compile and build - Return true if successful. */
+ bool finalize(const shader::ShaderCreateInfo *info = nullptr) override;
+
+ /* Utility. */
+ bool is_valid()
+ {
+ return valid_;
+ }
+ MTLRenderPipelineStateDescriptor &get_current_pipeline_state()
+ {
+ return current_pipeline_state_;
+ }
+ MTLShaderInterface *get_interface()
+ {
+ return static_cast<MTLShaderInterface *>(this->interface);
+ }
+ void *get_push_constant_data()
+ {
+ return push_constant_data_;
+ }
+
+ /* Shader source generators from create-info.
+ * These aren't all used by Metal, as certain parts of source code generation
+ * for shader entry-points and resource mapping occur during `finalize`. */
+ std::string resources_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string vertex_interface_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string fragment_interface_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string geometry_interface_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const override;
+ std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const override;
+
+ void transform_feedback_names_set(Span<const char *> name_list,
+ const eGPUShaderTFBType geom_type) override;
+ bool transform_feedback_enable(GPUVertBuf *buf) override;
+ void transform_feedback_disable() override;
+
+ void bind() override;
+ void unbind() override;
+
+ void uniform_float(int location, int comp_len, int array_size, const float *data) override;
+ void uniform_int(int location, int comp_len, int array_size, const int *data) override;
+ bool get_push_constant_is_dirty();
+ void push_constant_bindstate_mark_dirty(bool is_dirty);
+
+ void vertformat_from_shader(GPUVertFormat *format) const override;
+
+ /* DEPRECATED: Kept only because of BGL API. (Returning -1 in METAL). */
+ int program_handle_get() const override
+ {
+ return -1;
+ }
+
+ bool get_uses_ssbo_vertex_fetch()
+ {
+ return use_ssbo_vertex_fetch_mode_;
+ }
+ MTLPrimitiveType get_ssbo_vertex_fetch_output_prim_type()
+ {
+ return ssbo_vertex_fetch_output_prim_type_;
+ }
+ uint32_t get_ssbo_vertex_fetch_output_num_verts()
+ {
+ return ssbo_vertex_fetch_output_num_verts_;
+ }
+ static int ssbo_vertex_type_to_attr_type(MTLVertexFormat attribute_type);
+ void prepare_ssbo_vertex_fetch_metadata();
+
+ /* SSBO Vertex Bindings Utility functions. */
+ void ssbo_vertex_fetch_bind_attributes_begin();
+ void ssbo_vertex_fetch_bind_attribute(const MTLSSBOAttribute &ssbo_attr);
+ void ssbo_vertex_fetch_bind_attributes_end(id<MTLRenderCommandEncoder> active_encoder);
+
+ /* Metal shader properties and source mapping. */
+ void set_vertex_function_name(NSString *vetex_function_name);
+ void set_fragment_function_name(NSString *fragment_function_name_);
+ void shader_source_from_msl(NSString *input_vertex_source, NSString *input_fragment_source);
+ void set_interface(MTLShaderInterface *interface);
+ MTLRenderPipelineStateInstance *bake_current_pipeline_state(MTLContext *ctx,
+ MTLPrimitiveTopologyClass prim_type);
+
+ /* Transform Feedback. */
+ GPUVertBuf *get_transform_feedback_active_buffer();
+ bool has_transform_feedback_varying(std::string str);
+
+ private:
+ /* Generate MSL shader from GLSL source. */
+ bool generate_msl_from_glsl(const shader::ShaderCreateInfo *info);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("MTLShader");
+};
+
+/* Vertex format conversion.
+ * Determines whether it is possible to resize a vertex attribute type
+ * during input assembly. A conversion is implied by the difference
+ * between the input vertex descriptor (from MTLBatch/MTLImmediate)
+ * and the type specified in the shader source.
+ *
+ * e.g. vec3 to vec4 expansion, or vec4 to vec2 truncation.
+ * NOTE: Vector expansion will replace empty elements with the values
+ * (0,0,0,1).
+ *
+ * If implicit format resize is not possible, this function
+ * returns false.
+ *
+ * Implicitly supported conversions in Metal are described here:
+ * https://developer.apple.com/documentation/metal/mtlvertexattributedescriptor/1516081-format?language=objc
+ */
+inline bool mtl_vertex_format_resize(MTLVertexFormat mtl_format,
+ uint32_t components,
+ MTLVertexFormat *r_convertedFormat)
+{
+ MTLVertexFormat out_vert_format = MTLVertexFormatInvalid;
+ switch (mtl_format) {
+ /* Char. */
+ case MTLVertexFormatChar:
+ case MTLVertexFormatChar2:
+ case MTLVertexFormatChar3:
+ case MTLVertexFormatChar4:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatChar;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatChar2;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatChar3;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatChar4;
+ break;
+ }
+ break;
+
+ /* Normalized Char. */
+ case MTLVertexFormatCharNormalized:
+ case MTLVertexFormatChar2Normalized:
+ case MTLVertexFormatChar3Normalized:
+ case MTLVertexFormatChar4Normalized:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatCharNormalized;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatChar2Normalized;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatChar3Normalized;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatChar4Normalized;
+ break;
+ }
+ break;
+
+ /* Unsigned Char. */
+ case MTLVertexFormatUChar:
+ case MTLVertexFormatUChar2:
+ case MTLVertexFormatUChar3:
+ case MTLVertexFormatUChar4:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatUChar;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatUChar2;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatUChar3;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatUChar4;
+ break;
+ }
+ break;
+
+ /* Normalized Unsigned char */
+ case MTLVertexFormatUCharNormalized:
+ case MTLVertexFormatUChar2Normalized:
+ case MTLVertexFormatUChar3Normalized:
+ case MTLVertexFormatUChar4Normalized:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatUCharNormalized;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatUChar2Normalized;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatUChar3Normalized;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatUChar4Normalized;
+ break;
+ }
+ break;
+
+ /* Short. */
+ case MTLVertexFormatShort:
+ case MTLVertexFormatShort2:
+ case MTLVertexFormatShort3:
+ case MTLVertexFormatShort4:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatShort;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatShort2;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatShort3;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatShort4;
+ break;
+ }
+ break;
+
+ /* Normalized Short. */
+ case MTLVertexFormatShortNormalized:
+ case MTLVertexFormatShort2Normalized:
+ case MTLVertexFormatShort3Normalized:
+ case MTLVertexFormatShort4Normalized:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatShortNormalized;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatShort2Normalized;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatShort3Normalized;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatShort4Normalized;
+ break;
+ }
+ break;
+
+ /* Unsigned Short. */
+ case MTLVertexFormatUShort:
+ case MTLVertexFormatUShort2:
+ case MTLVertexFormatUShort3:
+ case MTLVertexFormatUShort4:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatUShort;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatUShort2;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatUShort3;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatUShort4;
+ break;
+ }
+ break;
+
+ /* Normalized Unsigned Short. */
+ case MTLVertexFormatUShortNormalized:
+ case MTLVertexFormatUShort2Normalized:
+ case MTLVertexFormatUShort3Normalized:
+ case MTLVertexFormatUShort4Normalized:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatUShortNormalized;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatUShort2Normalized;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatUShort3Normalized;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatUShort4Normalized;
+ break;
+ }
+ break;
+
+ /* Integer. */
+ case MTLVertexFormatInt:
+ case MTLVertexFormatInt2:
+ case MTLVertexFormatInt3:
+ case MTLVertexFormatInt4:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatInt;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatInt2;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatInt3;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatInt4;
+ break;
+ }
+ break;
+
+ /* Unsigned Integer. */
+ case MTLVertexFormatUInt:
+ case MTLVertexFormatUInt2:
+ case MTLVertexFormatUInt3:
+ case MTLVertexFormatUInt4:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatUInt;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatUInt2;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatUInt3;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatUInt4;
+ break;
+ }
+ break;
+
+ /* Half. */
+ case MTLVertexFormatHalf:
+ case MTLVertexFormatHalf2:
+ case MTLVertexFormatHalf3:
+ case MTLVertexFormatHalf4:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatHalf;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatHalf2;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatHalf3;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatHalf4;
+ break;
+ }
+ break;
+
+ /* Float. */
+ case MTLVertexFormatFloat:
+ case MTLVertexFormatFloat2:
+ case MTLVertexFormatFloat3:
+ case MTLVertexFormatFloat4:
+ switch (components) {
+ case 1:
+ out_vert_format = MTLVertexFormatFloat;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatFloat2;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatFloat3;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatFloat4;
+ break;
+ }
+ break;
+
+ /* Other formats */
+ default:
+ out_vert_format = mtl_format;
+ break;
+ }
+ *r_convertedFormat = out_vert_format;
+ return out_vert_format != MTLVertexFormatInvalid;
+}
+
+/**
+ * Returns whether the METAL API can internally convert between the input type of data in the
+ * incoming vertex buffer and the format used by the vertex attribute inside the shader.
+ *
+ * - Returns TRUE if the type can be converted internally, along with returning the appropriate
+ * type to be passed into the #MTLVertexAttributeDescriptorPSO.
+ *
+ * - Returns FALSE if the type cannot be converted internally e.g. casting Int4 to Float4.
+ *
+ * If implicit conversion is not possible, then we can fallback to performing manual attribute
+ * conversion using the special attribute read function specializations in the shader.
+ * These functions selectively convert between types based on the specified vertex
+ * attribute `GPUVertFetchMode fetch_mode` e.g. `GPU_FETCH_INT`.
+ */
+inline bool mtl_convert_vertex_format(MTLVertexFormat shader_attrib_format,
+ GPUVertCompType component_type,
+ uint32_t component_length,
+ GPUVertFetchMode fetch_mode,
+ MTLVertexFormat *r_convertedFormat)
+{
+ bool normalized = (fetch_mode == GPU_FETCH_INT_TO_FLOAT_UNIT);
+ MTLVertexFormat out_vert_format = MTLVertexFormatInvalid;
+
+ switch (component_type) {
+
+ case GPU_COMP_I8:
+ switch (fetch_mode) {
+ case GPU_FETCH_INT:
+ if (shader_attrib_format == MTLVertexFormatChar ||
+ shader_attrib_format == MTLVertexFormatChar2 ||
+ shader_attrib_format == MTLVertexFormatChar3 ||
+ shader_attrib_format == MTLVertexFormatChar4) {
+
+ /* No conversion Needed (as type matches) - Just a vector resize if needed. */
+ bool can_convert = mtl_vertex_format_resize(
+ shader_attrib_format, component_type, &out_vert_format);
+
+ /* Ensure format resize successful. */
+ BLI_assert(can_convert);
+ UNUSED_VARS_NDEBUG(can_convert);
+ }
+ else if (shader_attrib_format == MTLVertexFormatInt4 && component_length == 4) {
+ /* Allow type expansion - Shader expects MTLVertexFormatInt4, we can supply a type
+ * with fewer bytes if component count is the same. Sign must also match original type
+ * -- which is not a problem in this case. */
+ out_vert_format = MTLVertexFormatChar4;
+ }
+ else if (shader_attrib_format == MTLVertexFormatInt3 && component_length == 3) {
+ /* Same as above case for matching length and signage (Len=3)*/
+ out_vert_format = MTLVertexFormatChar3;
+ }
+ else if (shader_attrib_format == MTLVertexFormatInt2 && component_length == 2) {
+ /* Same as above case for matching length and signage (Len=2)*/
+ out_vert_format = MTLVertexFormatChar2;
+ }
+ else if (shader_attrib_format == MTLVertexFormatInt && component_length == 1) {
+ /* Same as above case for matching length and signage (Len=1)*/
+ out_vert_format = MTLVertexFormatChar;
+ }
+ else if (shader_attrib_format == MTLVertexFormatInt && component_length == 4) {
+ /* Special case here, format has been specified as GPU_COMP_U8 with 4 components, which
+ * is equivalent to an Int -- so data will be compatible with the shader interface. */
+ out_vert_format = MTLVertexFormatInt;
+ }
+ else {
+ BLI_assert_msg(false,
+ "Source vertex data format is either Char, Char2, Char3, Char4 but "
+ "format in shader interface is NOT compatible.\n");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+
+ /* Source vertex data is integer type, but shader interface type is floating point.
+ * If the input attribute is specified as normalized, we can convert. */
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ if (normalized) {
+ switch (component_length) {
+ case 1:
+ out_vert_format = MTLVertexFormatCharNormalized;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatChar2Normalized;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatChar3Normalized;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatChar4Normalized;
+ break;
+ default:
+ BLI_assert_msg(false, "invalid vertex format");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ }
+ else {
+ /* Cannot convert. */
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+ }
+ break;
+
+ case GPU_COMP_U8:
+ switch (fetch_mode) {
+ /* Fetching INT: Check backing shader format matches source input. */
+ case GPU_FETCH_INT:
+ if (shader_attrib_format == MTLVertexFormatUChar ||
+ shader_attrib_format == MTLVertexFormatUChar2 ||
+ shader_attrib_format == MTLVertexFormatUChar3 ||
+ shader_attrib_format == MTLVertexFormatUChar4) {
+
+ /* No conversion Needed (as type matches) - Just a vector resize if needed. */
+ bool can_convert = mtl_vertex_format_resize(
+ shader_attrib_format, component_length, &out_vert_format);
+
+ /* Ensure format resize successful. */
+ BLI_assert(can_convert);
+ UNUSED_VARS_NDEBUG(can_convert);
+ /* TODO(Metal): Add other format conversions if needed. Currently no attributes hit
+ * this path. */
+ }
+ else if (shader_attrib_format == MTLVertexFormatUInt4 && component_length == 4) {
+ /* Allow type expansion - Shader expects MTLVertexFormatUInt4, we can supply a type
+ * with fewer bytes if component count is the same. */
+ out_vert_format = MTLVertexFormatUChar4;
+ }
+ else if (shader_attrib_format == MTLVertexFormatUInt3 && component_length == 3) {
+ /* Same as above case for matching length and signage (Len=3)*/
+ out_vert_format = MTLVertexFormatUChar3;
+ }
+ else if (shader_attrib_format == MTLVertexFormatUInt2 && component_length == 2) {
+ /* Same as above case for matching length and signage (Len=2)*/
+ out_vert_format = MTLVertexFormatUChar2;
+ }
+ else if (shader_attrib_format == MTLVertexFormatUInt && component_length == 1) {
+ /* Same as above case for matching length and signage (Len=1)*/
+ out_vert_format = MTLVertexFormatUChar;
+ }
+ else if (shader_attrib_format == MTLVertexFormatInt && component_length == 4) {
+ /* Special case here, format has been specified as GPU_COMP_U8 with 4 components, which
+ * is equivalent to an Int-- so data will be compatible with shader interface. */
+ out_vert_format = MTLVertexFormatInt;
+ }
+ else if (shader_attrib_format == MTLVertexFormatUInt && component_length == 4) {
+ /* Special case here, format has been specified as GPU_COMP_U8 with 4 components, which
+ *is equivalent to a UInt-- so data will be compatible with shader interface. */
+ out_vert_format = MTLVertexFormatUInt;
+ }
+ else {
+ BLI_assert_msg(false,
+ "Source vertex data format is either UChar, UChar2, UChar3, UChar4 but "
+ "format in shader interface is NOT compatible.\n");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+
+ /* Source vertex data is integral type, but shader interface type is floating point.
+ * If the input attribute is specified as normalized, we can convert. */
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ if (normalized) {
+ switch (component_length) {
+ case 1:
+ out_vert_format = MTLVertexFormatUCharNormalized;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatUChar2Normalized;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatUChar3Normalized;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatUChar4Normalized;
+ break;
+ default:
+ BLI_assert_msg(false, "invalid vertex format");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ }
+ else {
+ /* Cannot convert. */
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+ }
+ break;
+
+ case GPU_COMP_I16:
+ switch (fetch_mode) {
+ case GPU_FETCH_INT:
+ if (shader_attrib_format == MTLVertexFormatShort ||
+ shader_attrib_format == MTLVertexFormatShort2 ||
+ shader_attrib_format == MTLVertexFormatShort3 ||
+ shader_attrib_format == MTLVertexFormatShort4) {
+ /* No conversion Needed (as type matches) - Just a vector resize if needed. */
+ bool can_convert = mtl_vertex_format_resize(
+ shader_attrib_format, component_length, &out_vert_format);
+
+ /* Ensure conversion successful. */
+ BLI_assert(can_convert);
+ UNUSED_VARS_NDEBUG(can_convert);
+ }
+ else {
+ BLI_assert_msg(false,
+ "Source vertex data format is either Short, Short2, Short3, Short4 but "
+ "format in shader interface is NOT compatible.\n");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+
+ /* Source vertex data is integral type, but shader interface type is floating point.
+ * If the input attribute is specified as normalized, we can convert. */
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ if (normalized) {
+ switch (component_length) {
+ case 1:
+ out_vert_format = MTLVertexFormatShortNormalized;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatShort2Normalized;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatShort3Normalized;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatShort4Normalized;
+ break;
+ default:
+ BLI_assert_msg(false, "invalid vertex format");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ }
+ else {
+ /* Cannot convert. */
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+ }
+ break;
+
+ case GPU_COMP_U16:
+ switch (fetch_mode) {
+ case GPU_FETCH_INT:
+ if (shader_attrib_format == MTLVertexFormatUShort ||
+ shader_attrib_format == MTLVertexFormatUShort2 ||
+ shader_attrib_format == MTLVertexFormatUShort3 ||
+ shader_attrib_format == MTLVertexFormatUShort4) {
+ /* No conversion Needed (as type matches) - Just a vector resize if needed. */
+ bool can_convert = mtl_vertex_format_resize(
+ shader_attrib_format, component_length, &out_vert_format);
+
+ /* Ensure format resize successful. */
+ BLI_assert(can_convert);
+ UNUSED_VARS_NDEBUG(can_convert);
+ }
+ else {
+ BLI_assert_msg(false,
+ "Source vertex data format is either UShort, UShort2, UShort3, UShort4 "
+ "but format in shader interface is NOT compatible.\n");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+
+ /* Source vertex data is integral type, but shader interface type is floating point.
+ * If the input attribute is specified as normalized, we can convert. */
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ if (normalized) {
+ switch (component_length) {
+ case 1:
+ out_vert_format = MTLVertexFormatUShortNormalized;
+ break;
+ case 2:
+ out_vert_format = MTLVertexFormatUShort2Normalized;
+ break;
+ case 3:
+ out_vert_format = MTLVertexFormatUShort3Normalized;
+ break;
+ case 4:
+ out_vert_format = MTLVertexFormatUShort4Normalized;
+ break;
+ default:
+ BLI_assert_msg(false, "invalid vertex format");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ }
+ else {
+ /* Cannot convert. */
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+ }
+ break;
+
+ case GPU_COMP_I32:
+ switch (fetch_mode) {
+ case GPU_FETCH_INT:
+ if (shader_attrib_format == MTLVertexFormatInt ||
+ shader_attrib_format == MTLVertexFormatInt2 ||
+ shader_attrib_format == MTLVertexFormatInt3 ||
+ shader_attrib_format == MTLVertexFormatInt4) {
+ /* No conversion Needed (as type matches) - Just a vector resize if needed. */
+ bool can_convert = mtl_vertex_format_resize(
+ shader_attrib_format, component_length, &out_vert_format);
+
+ /* Verify conversion successful. */
+ BLI_assert(can_convert);
+ UNUSED_VARS_NDEBUG(can_convert);
+ }
+ else {
+ BLI_assert_msg(false,
+ "Source vertex data format is either Int, Int2, Int3, Int4 but format "
+ "in shader interface is NOT compatible.\n");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ /* Unfortunately we cannot implicitly convert between Int and Float in METAL. */
+ out_vert_format = MTLVertexFormatInvalid;
+ break;
+ }
+ break;
+
+ case GPU_COMP_U32:
+ switch (fetch_mode) {
+ case GPU_FETCH_INT:
+ if (shader_attrib_format == MTLVertexFormatUInt ||
+ shader_attrib_format == MTLVertexFormatUInt2 ||
+ shader_attrib_format == MTLVertexFormatUInt3 ||
+ shader_attrib_format == MTLVertexFormatUInt4) {
+ /* No conversion Needed (as type matches) - Just a vector resize if needed. */
+ bool can_convert = mtl_vertex_format_resize(
+ shader_attrib_format, component_length, &out_vert_format);
+
+ /* Verify conversion successful. */
+ BLI_assert(can_convert);
+ UNUSED_VARS_NDEBUG(can_convert);
+ }
+ else {
+ BLI_assert_msg(false,
+ "Source vertex data format is either UInt, UInt2, UInt3, UInt4 but "
+ "format in shader interface is NOT compatible.\n");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ /* Unfortunately we cannot convert between UInt and Float in METAL */
+ out_vert_format = MTLVertexFormatInvalid;
+ break;
+ }
+ break;
+
+ case GPU_COMP_F32:
+ switch (fetch_mode) {
+
+ /* Source data is float. This will be compatible
+ * if type specified in shader is also float. */
+ case GPU_FETCH_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT:
+ case GPU_FETCH_INT_TO_FLOAT_UNIT:
+ if (shader_attrib_format == MTLVertexFormatFloat ||
+ shader_attrib_format == MTLVertexFormatFloat2 ||
+ shader_attrib_format == MTLVertexFormatFloat3 ||
+ shader_attrib_format == MTLVertexFormatFloat4) {
+ /* No conversion Needed (as type matches) - Just a vector resize, if needed. */
+ bool can_convert = mtl_vertex_format_resize(
+ shader_attrib_format, component_length, &out_vert_format);
+
+ /* Verify conversion successful. */
+ BLI_assert(can_convert);
+ UNUSED_VARS_NDEBUG(can_convert);
+ }
+ else {
+ BLI_assert_msg(false,
+ "Source vertex data format is either Float, Float2, Float3, Float4 but "
+ "format in shader interface is NOT compatible.\n");
+ out_vert_format = MTLVertexFormatInvalid;
+ }
+ break;
+
+ case GPU_FETCH_INT:
+ /* Unfortunately we cannot convert between Float and Int implicitly in METAL. */
+ out_vert_format = MTLVertexFormatInvalid;
+ break;
+ }
+ break;
+
+ case GPU_COMP_I10:
+ out_vert_format = MTLVertexFormatInt1010102Normalized;
+ break;
+ }
+ *r_convertedFormat = out_vert_format;
+ return (out_vert_format != MTLVertexFormatInvalid);
+}
+
+inline uint comp_count_from_vert_format(MTLVertexFormat vert_format)
+{
+ switch (vert_format) {
+ case MTLVertexFormatFloat:
+ case MTLVertexFormatInt:
+ case MTLVertexFormatUInt:
+ case MTLVertexFormatShort:
+ case MTLVertexFormatUChar:
+ case MTLVertexFormatUCharNormalized:
+ return 1;
+ case MTLVertexFormatUChar2:
+ case MTLVertexFormatUInt2:
+ case MTLVertexFormatFloat2:
+ case MTLVertexFormatInt2:
+ case MTLVertexFormatUChar2Normalized:
+ return 2;
+ case MTLVertexFormatUChar3:
+ case MTLVertexFormatUInt3:
+ case MTLVertexFormatFloat3:
+ case MTLVertexFormatInt3:
+ case MTLVertexFormatShort3Normalized:
+ case MTLVertexFormatUChar3Normalized:
+ return 3;
+ case MTLVertexFormatUChar4:
+ case MTLVertexFormatFloat4:
+ case MTLVertexFormatUInt4:
+ case MTLVertexFormatInt4:
+ case MTLVertexFormatUChar4Normalized:
+ case MTLVertexFormatInt1010102Normalized:
+
+ default:
+ BLI_assert_msg(false, "Unrecognized attribute type. Add types to switch as needed.");
+ return 0;
+ }
+}
+
+inline GPUVertFetchMode fetchmode_from_vert_format(MTLVertexFormat vert_format)
+{
+ switch (vert_format) {
+ case MTLVertexFormatFloat:
+ case MTLVertexFormatFloat2:
+ case MTLVertexFormatFloat3:
+ case MTLVertexFormatFloat4:
+ return GPU_FETCH_FLOAT;
+
+ case MTLVertexFormatUChar:
+ case MTLVertexFormatUChar2:
+ case MTLVertexFormatUChar3:
+ case MTLVertexFormatUChar4:
+ case MTLVertexFormatChar:
+ case MTLVertexFormatChar2:
+ case MTLVertexFormatChar3:
+ case MTLVertexFormatChar4:
+ case MTLVertexFormatUShort:
+ case MTLVertexFormatUShort2:
+ case MTLVertexFormatUShort3:
+ case MTLVertexFormatUShort4:
+ case MTLVertexFormatShort:
+ case MTLVertexFormatShort2:
+ case MTLVertexFormatShort3:
+ case MTLVertexFormatShort4:
+ case MTLVertexFormatUInt:
+ case MTLVertexFormatUInt2:
+ case MTLVertexFormatUInt3:
+ case MTLVertexFormatUInt4:
+ case MTLVertexFormatInt:
+ case MTLVertexFormatInt2:
+ case MTLVertexFormatInt3:
+ case MTLVertexFormatInt4:
+ return GPU_FETCH_INT;
+
+ case MTLVertexFormatUCharNormalized:
+ case MTLVertexFormatUChar2Normalized:
+ case MTLVertexFormatUChar3Normalized:
+ case MTLVertexFormatUChar4Normalized:
+ case MTLVertexFormatCharNormalized:
+ case MTLVertexFormatChar2Normalized:
+ case MTLVertexFormatChar3Normalized:
+ case MTLVertexFormatChar4Normalized:
+ case MTLVertexFormatUShortNormalized:
+ case MTLVertexFormatUShort2Normalized:
+ case MTLVertexFormatUShort3Normalized:
+ case MTLVertexFormatUShort4Normalized:
+ case MTLVertexFormatShortNormalized:
+ case MTLVertexFormatShort2Normalized:
+ case MTLVertexFormatShort3Normalized:
+ case MTLVertexFormatShort4Normalized:
+ case MTLVertexFormatInt1010102Normalized:
+ return GPU_FETCH_INT_TO_FLOAT_UNIT;
+
+ default:
+ BLI_assert_msg(false, "Unrecognized attribute type. Add types to switch as needed.");
+ return GPU_FETCH_FLOAT;
+ }
+}
+
+inline GPUVertCompType comp_type_from_vert_format(MTLVertexFormat vert_format)
+{
+ switch (vert_format) {
+ case MTLVertexFormatUChar:
+ case MTLVertexFormatUChar2:
+ case MTLVertexFormatUChar3:
+ case MTLVertexFormatUChar4:
+ case MTLVertexFormatUCharNormalized:
+ case MTLVertexFormatUChar2Normalized:
+ case MTLVertexFormatUChar3Normalized:
+ case MTLVertexFormatUChar4Normalized:
+ return GPU_COMP_U8;
+
+ case MTLVertexFormatChar:
+ case MTLVertexFormatChar2:
+ case MTLVertexFormatChar3:
+ case MTLVertexFormatChar4:
+ case MTLVertexFormatCharNormalized:
+ case MTLVertexFormatChar2Normalized:
+ case MTLVertexFormatChar3Normalized:
+ case MTLVertexFormatChar4Normalized:
+ return GPU_COMP_I8;
+
+ case MTLVertexFormatShort:
+ case MTLVertexFormatShort2:
+ case MTLVertexFormatShort3:
+ case MTLVertexFormatShort4:
+ case MTLVertexFormatShortNormalized:
+ case MTLVertexFormatShort2Normalized:
+ case MTLVertexFormatShort3Normalized:
+ case MTLVertexFormatShort4Normalized:
+ return GPU_COMP_I16;
+
+ case MTLVertexFormatUShort:
+ case MTLVertexFormatUShort2:
+ case MTLVertexFormatUShort3:
+ case MTLVertexFormatUShort4:
+ case MTLVertexFormatUShortNormalized:
+ case MTLVertexFormatUShort2Normalized:
+ case MTLVertexFormatUShort3Normalized:
+ case MTLVertexFormatUShort4Normalized:
+ return GPU_COMP_U16;
+
+ case MTLVertexFormatInt:
+ case MTLVertexFormatInt2:
+ case MTLVertexFormatInt3:
+ case MTLVertexFormatInt4:
+ return GPU_COMP_I32;
+
+ case MTLVertexFormatUInt:
+ case MTLVertexFormatUInt2:
+ case MTLVertexFormatUInt3:
+ case MTLVertexFormatUInt4:
+ return GPU_COMP_U32;
+
+ case MTLVertexFormatFloat:
+ case MTLVertexFormatFloat2:
+ case MTLVertexFormatFloat3:
+ case MTLVertexFormatFloat4:
+ return GPU_COMP_F32;
+
+ case MTLVertexFormatInt1010102Normalized:
+ return GPU_COMP_I10;
+
+ default:
+ BLI_assert_msg(false, "Unrecognized attribute type. Add types to switch as needed.");
+ return GPU_COMP_F32;
+ }
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_shader.mm b/source/blender/gpu/metal/mtl_shader.mm
new file mode 100644
index 00000000000..23097f312f0
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_shader.mm
@@ -0,0 +1,1266 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "BKE_global.h"
+
+#include "BLI_string.h"
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <mutex>
+#include <regex>
+#include <sstream>
+#include <string>
+
+#include <cstring>
+
+#include "GPU_platform.h"
+#include "GPU_vertex_format.h"
+
+#include "mtl_common.hh"
+#include "mtl_context.hh"
+#include "mtl_debug.hh"
+#include "mtl_pso_descriptor_state.hh"
+#include "mtl_shader.hh"
+#include "mtl_shader_generator.hh"
+#include "mtl_shader_interface.hh"
+#include "mtl_texture.hh"
+
+extern char datatoc_mtl_shader_common_msl[];
+
+using namespace blender;
+using namespace blender::gpu;
+using namespace blender::gpu::shader;
+
+namespace blender::gpu {
+
+/* -------------------------------------------------------------------- */
+/** \name Creation / Destruction.
+ * \{ */
+
+/* Create empty shader to be populated later. */
+MTLShader::MTLShader(MTLContext *ctx, const char *name) : Shader(name)
+{
+ context_ = ctx;
+
+ /* Create SHD builder to hold temporary resources until compilation is complete. */
+ shd_builder_ = new MTLShaderBuilder();
+
+#ifndef NDEBUG
+ /* Remove invalid symbols from shader name to ensure debug entry-point function name is valid. */
+ for (uint i : IndexRange(strlen(this->name))) {
+ char c = this->name[i];
+ if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) {
+ }
+ else {
+ this->name[i] = '_';
+ }
+ }
+#endif
+}
+
+/* Create shader from MSL source. */
+MTLShader::MTLShader(MTLContext *ctx,
+ MTLShaderInterface *interface,
+ const char *name,
+ NSString *input_vertex_source,
+ NSString *input_fragment_source,
+ NSString *vert_function_name,
+ NSString *frag_function_name)
+ : MTLShader(ctx, name)
+{
+ BLI_assert([vert_function_name length]);
+ BLI_assert([frag_function_name length]);
+
+ this->set_vertex_function_name(vert_function_name);
+ this->set_fragment_function_name(frag_function_name);
+ this->shader_source_from_msl(input_vertex_source, input_fragment_source);
+ this->set_interface(interface);
+ this->finalize(nullptr);
+}
+
+MTLShader::~MTLShader()
+{
+ if (this->is_valid()) {
+
+ /* Free uniform data block. */
+ if (push_constant_data_ != nullptr) {
+ MEM_freeN(push_constant_data_);
+ push_constant_data_ = nullptr;
+ }
+
+ /* Free Metal resources. */
+ if (shader_library_vert_ != nil) {
+ [shader_library_vert_ release];
+ shader_library_vert_ = nil;
+ }
+ if (shader_library_frag_ != nil) {
+ [shader_library_frag_ release];
+ shader_library_frag_ = nil;
+ }
+
+ if (pso_descriptor_ != nil) {
+ [pso_descriptor_ release];
+ pso_descriptor_ = nil;
+ }
+
+ /* Free Pipeline Cache. */
+ for (const MTLRenderPipelineStateInstance *pso_inst : pso_cache_.values()) {
+ if (pso_inst->vert) {
+ [pso_inst->vert release];
+ }
+ if (pso_inst->frag) {
+ [pso_inst->frag release];
+ }
+ if (pso_inst->pso) {
+ [pso_inst->pso release];
+ }
+ delete pso_inst;
+ }
+ pso_cache_.clear();
+
+ /* NOTE(Metal): #ShaderInterface deletion is handled in the super destructor `~Shader()`. */
+ }
+ valid_ = false;
+
+ if (shd_builder_ != nullptr) {
+ delete shd_builder_;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shader stage creation.
+ * \{ */
+
+void MTLShader::vertex_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ /* Flag source as not being compiled from native MSL. */
+ BLI_assert(shd_builder_ != nullptr);
+ shd_builder_->source_from_msl_ = false;
+
+ /* Remove #version tag entry. */
+ sources[0] = "";
+
+ /* Consolidate GLSL vertex sources. */
+ std::stringstream ss;
+ for (int i = 0; i < sources.size(); i++) {
+ ss << sources[i] << std::endl;
+ }
+ shd_builder_->glsl_vertex_source_ = ss.str();
+}
+
+void MTLShader::geometry_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ MTL_LOG_ERROR("MTLShader::geometry_shader_from_glsl - Geometry shaders unsupported!\n");
+}
+
+void MTLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ /* Flag source as not being compiled from native MSL. */
+ BLI_assert(shd_builder_ != nullptr);
+ shd_builder_->source_from_msl_ = false;
+
+ /* Remove #version tag entry. */
+ sources[0] = "";
+
+ /* Consolidate GLSL fragment sources. */
+ std::stringstream ss;
+ for (int i = 0; i < sources.size(); i++) {
+ ss << sources[i] << std::endl;
+ }
+ shd_builder_->glsl_fragment_source_ = ss.str();
+}
+
+void MTLShader::compute_shader_from_glsl(MutableSpan<const char *> sources)
+{
+ /* Remove #version tag entry. */
+ sources[0] = "";
+
+ /* TODO(Metal): Support compute shaders in Metal. */
+ MTL_LOG_WARNING(
+ "MTLShader::compute_shader_from_glsl - Compute shaders currently unsupported!\n");
+}
+
+bool MTLShader::finalize(const shader::ShaderCreateInfo *info)
+{
+ /* Check if Shader has already been finalized. */
+ if (this->is_valid()) {
+ MTL_LOG_ERROR("Shader (%p) '%s' has already been finalized!\n", this, this->name_get());
+ }
+
+ /* Perform GLSL to MSL source translation. */
+ BLI_assert(shd_builder_ != nullptr);
+ if (!shd_builder_->source_from_msl_) {
+ bool success = generate_msl_from_glsl(info);
+ if (!success) {
+ /* GLSL to MSL translation has failed, or is unsupported for this shader. */
+ valid_ = false;
+ BLI_assert_msg(false, "Shader translation from GLSL to MSL has failed. \n");
+
+ /* Create empty interface to allow shader to be silently used. */
+ MTLShaderInterface *mtl_interface = new MTLShaderInterface(this->name_get());
+ this->set_interface(mtl_interface);
+
+ /* Release temporary compilation resources. */
+ delete shd_builder_;
+ return false;
+ }
+ }
+
+ /* Ensure we have a valid shader interface. */
+ MTLShaderInterface *mtl_interface = this->get_interface();
+ BLI_assert(mtl_interface != nullptr);
+
+ /* Verify Context handle, fetch device and compile shader. */
+ BLI_assert(context_);
+ id<MTLDevice> device = context_->device;
+ BLI_assert(device != nil);
+
+ /* Ensure source and stage entry-point names are set. */
+ BLI_assert([vertex_function_name_ length] > 0);
+ if (transform_feedback_type_ == GPU_SHADER_TFB_NONE) {
+ BLI_assert([fragment_function_name_ length] > 0);
+ }
+ BLI_assert(shd_builder_ != nullptr);
+ BLI_assert([shd_builder_->msl_source_vert_ length] > 0);
+
+ @autoreleasepool {
+ MTLCompileOptions *options = [[[MTLCompileOptions alloc] init] autorelease];
+ options.languageVersion = MTLLanguageVersion2_2;
+ options.fastMathEnabled = YES;
+
+ NSString *source_to_compile = shd_builder_->msl_source_vert_;
+ for (int src_stage = 0; src_stage <= 1; src_stage++) {
+
+ source_to_compile = (src_stage == 0) ? shd_builder_->msl_source_vert_ :
+ shd_builder_->msl_source_frag_;
+
+ /* Transform feedback, skip compilation. */
+ if (src_stage == 1 && (transform_feedback_type_ != GPU_SHADER_TFB_NONE)) {
+ shader_library_frag_ = nil;
+ break;
+ }
+
+ /* Concatenate common source. */
+ NSString *str = [NSString stringWithUTF8String:datatoc_mtl_shader_common_msl];
+ NSString *source_with_header_a = [str stringByAppendingString:source_to_compile];
+
+ /* Inject unique context ID to avoid cross-context shader cache collisions.
+ * Required on macOS 11.0. */
+ NSString *source_with_header = source_with_header_a;
+ if (@available(macos 11.0, *)) {
+ /* Pass-through. Availability syntax requirement, expression cannot be negated. */
+ }
+ else {
+ source_with_header = [source_with_header_a
+ stringByAppendingString:[NSString stringWithFormat:@"\n\n#define MTL_CONTEXT_IND %d\n",
+ context_->context_id]];
+ }
+ [source_with_header retain];
+
+ /* Prepare Shader Library. */
+ NSError *error = nullptr;
+ id<MTLLibrary> library = [device newLibraryWithSource:source_with_header
+ options:options
+ error:&error];
+ if (error) {
+ /* Only exit out if genuine error and not warning. */
+ if ([[error localizedDescription] rangeOfString:@"Compilation succeeded"].location ==
+ NSNotFound) {
+ NSLog(
+ @"Compile Error - Metal Shader Library (Stage: %d), error %@ \n", src_stage, error);
+ BLI_assert(false);
+
+ /* Release temporary compilation resources. */
+ delete shd_builder_;
+ return false;
+ }
+ }
+
+ MTL_LOG_INFO("Successfully compiled Metal Shader Library (Stage: %d) for shader; %s\n",
+ src_stage,
+ name);
+ BLI_assert(library != nil);
+ if (src_stage == 0) {
+ /* Retain generated library and assign debug name. */
+ shader_library_vert_ = library;
+ [shader_library_vert_ retain];
+ shader_library_vert_.label = [NSString stringWithUTF8String:this->name];
+ }
+ else {
+ /* Retain generated library for fragment shader and assign debug name. */
+ shader_library_frag_ = library;
+ [shader_library_frag_ retain];
+ shader_library_frag_.label = [NSString stringWithUTF8String:this->name];
+ }
+
+ [source_with_header autorelease];
+ }
+ pso_descriptor_.label = [NSString stringWithUTF8String:this->name];
+
+ /* Prepare descriptor. */
+ pso_descriptor_ = [[MTLRenderPipelineDescriptor alloc] init];
+ [pso_descriptor_ retain];
+
+ /* Shader has successfully been created. */
+ valid_ = true;
+
+ /* Prepare backing data storage for local uniforms. */
+ const MTLShaderUniformBlock &push_constant_block = mtl_interface->get_push_constant_block();
+ if (push_constant_block.size > 0) {
+ push_constant_data_ = MEM_callocN(push_constant_block.size, __func__);
+ this->push_constant_bindstate_mark_dirty(true);
+ }
+ else {
+ push_constant_data_ = nullptr;
+ }
+ }
+
+ /* Release temporary compilation resources. */
+ delete shd_builder_;
+ return true;
+}
+
+void MTLShader::transform_feedback_names_set(Span<const char *> name_list,
+ const eGPUShaderTFBType geom_type)
+{
+ tf_output_name_list_.clear();
+ for (int i = 0; i < name_list.size(); i++) {
+ tf_output_name_list_.append(std::string(name_list[i]));
+ }
+ transform_feedback_type_ = geom_type;
+}
+
+bool MTLShader::transform_feedback_enable(GPUVertBuf *buf)
+{
+ BLI_assert(transform_feedback_type_ != GPU_SHADER_TFB_NONE);
+ BLI_assert(buf);
+ transform_feedback_active_ = true;
+ transform_feedback_vertbuf_ = buf;
+ /* TODO(Metal): Enable this assertion once #MTLVertBuf lands. */
+ // BLI_assert(static_cast<MTLVertBuf *>(unwrap(transform_feedback_vertbuf_))->get_usage_type() ==
+ // GPU_USAGE_DEVICE_ONLY);
+ return true;
+}
+
+void MTLShader::transform_feedback_disable()
+{
+ transform_feedback_active_ = false;
+ transform_feedback_vertbuf_ = nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shader Binding.
+ * \{ */
+
+void MTLShader::bind()
+{
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ if (interface == nullptr || !this->is_valid()) {
+ MTL_LOG_WARNING(
+ "MTLShader::bind - Shader '%s' has no valid implementation in Metal, draw calls will be "
+ "skipped.\n",
+ this->name_get());
+ }
+ ctx->pipeline_state.active_shader = this;
+}
+
+void MTLShader::unbind()
+{
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ ctx->pipeline_state.active_shader = nullptr;
+}
+
+void MTLShader::uniform_float(int location, int comp_len, int array_size, const float *data)
+{
+ BLI_assert(this);
+ if (!this->is_valid()) {
+ return;
+ }
+ MTLShaderInterface *mtl_interface = get_interface();
+ if (location < 0 || location >= mtl_interface->get_total_uniforms()) {
+ MTL_LOG_WARNING("Uniform location %d is not valid in Shader %s\n", location, this->name_get());
+ return;
+ }
+
+ /* Fetch more information about uniform from interface. */
+ const MTLShaderUniform &uniform = mtl_interface->get_uniform(location);
+
+ /* Prepare to copy data into local shader push constant memory block. */
+ BLI_assert(push_constant_data_ != nullptr);
+ uint8_t *dest_ptr = (uint8_t *)push_constant_data_;
+ dest_ptr += uniform.byte_offset;
+ uint32_t copy_size = sizeof(float) * comp_len * array_size;
+
+ /* Test per-element size. It is valid to copy less array elements than the total, but each
+ * array element needs to match. */
+ uint32_t source_per_element_size = sizeof(float) * comp_len;
+ uint32_t dest_per_element_size = uniform.size_in_bytes / uniform.array_len;
+ BLI_assert_msg(
+ source_per_element_size <= dest_per_element_size,
+ "source Per-array-element size must be smaller than destination storage capacity for "
+ "that data");
+
+ if (source_per_element_size < dest_per_element_size) {
+ switch (uniform.type) {
+
+ /* Special case for handling 'vec3' array upload. */
+ case MTL_DATATYPE_FLOAT3: {
+ int numvecs = uniform.array_len;
+ uint8_t *data_c = (uint8_t *)data;
+
+ /* It is more efficient on the host to only modify data if it has changed.
+ * Data modifications are small, so memory comparison is cheap.
+ * If uniforms have remained unchanged, then we avoid both copying
+ * data into the local uniform struct, and upload of the modified uniform
+ * contents in the command stream. */
+ bool changed = false;
+ for (int i = 0; i < numvecs; i++) {
+ changed = changed || (memcmp((void *)dest_ptr, (void *)data_c, sizeof(float) * 3) != 0);
+ if (changed) {
+ memcpy((void *)dest_ptr, (void *)data_c, sizeof(float) * 3);
+ }
+ data_c += sizeof(float) * 3;
+ dest_ptr += sizeof(float) * 4;
+ }
+ if (changed) {
+ this->push_constant_bindstate_mark_dirty(true);
+ }
+ return;
+ }
+
+ /* Special case for handling 'mat3' upload. */
+ case MTL_DATATYPE_FLOAT3x3: {
+ int numvecs = 3 * uniform.array_len;
+ uint8_t *data_c = (uint8_t *)data;
+
+ /* It is more efficient on the host to only modify data if it has changed.
+ * Data modifications are small, so memory comparison is cheap.
+ * If uniforms have remained unchanged, then we avoid both copying
+ * data into the local uniform struct, and upload of the modified uniform
+ * contents in the command stream. */
+ bool changed = false;
+ for (int i = 0; i < numvecs; i++) {
+ changed = changed || (memcmp((void *)dest_ptr, (void *)data_c, sizeof(float) * 3) != 0);
+ if (changed) {
+ memcpy((void *)dest_ptr, (void *)data_c, sizeof(float) * 3);
+ }
+ data_c += sizeof(float) * 3;
+ dest_ptr += sizeof(float) * 4;
+ }
+ if (changed) {
+ this->push_constant_bindstate_mark_dirty(true);
+ }
+ return;
+ }
+ default:
+ shader_debug_printf("INCOMPATIBLE UNIFORM TYPE: %d\n", uniform.type);
+ break;
+ }
+ }
+
+ /* Debug checks. */
+ BLI_assert_msg(
+ copy_size <= uniform.size_in_bytes,
+ "Size of provided uniform data is greater than size specified in Shader interface\n");
+
+ /* Only flag UBO as modified if data is different -- This can avoid re-binding of unmodified
+ * local uniform data. */
+ bool data_changed = (memcmp((void *)dest_ptr, (void *)data, copy_size) != 0);
+ if (data_changed) {
+ this->push_constant_bindstate_mark_dirty(true);
+ memcpy((void *)dest_ptr, (void *)data, copy_size);
+ }
+}
+
+void MTLShader::uniform_int(int location, int comp_len, int array_size, const int *data)
+{
+ BLI_assert(this);
+ if (!this->is_valid()) {
+ return;
+ }
+
+ /* NOTE(Metal): Invalidation warning for uniform re-mapping of texture slots, unsupported in
+ * Metal, as we cannot point a texture binding at a different slot. */
+ MTLShaderInterface *mtl_interface = this->get_interface();
+ if (location >= mtl_interface->get_total_uniforms() &&
+ location < (mtl_interface->get_total_uniforms() + mtl_interface->get_total_textures())) {
+ MTL_LOG_WARNING(
+ "Texture uniform location re-mapping unsupported in Metal. (Possibly also bad uniform "
+ "location %d)\n",
+ location);
+ return;
+ }
+
+ if (location < 0 || location >= mtl_interface->get_total_uniforms()) {
+ MTL_LOG_WARNING(
+ "Uniform is not valid at location %d - Shader %s\n", location, this->name_get());
+ return;
+ }
+
+ /* Fetch more information about uniform from interface. */
+ const MTLShaderUniform &uniform = mtl_interface->get_uniform(location);
+
+ /* Determine data location in uniform block. */
+ BLI_assert(push_constant_data_ != nullptr);
+ uint8_t *ptr = (uint8_t *)push_constant_data_;
+ ptr += uniform.byte_offset;
+
+ /* Copy data into local block. Only flag UBO as modified if data is different
+ * This can avoid re-binding of unmodified local uniform data, reducing
+ * the total number of copy operations needed and data transfers between
+ * CPU and GPU. */
+ bool data_changed = (memcmp((void *)ptr, (void *)data, sizeof(int) * comp_len * array_size) !=
+ 0);
+ if (data_changed) {
+ this->push_constant_bindstate_mark_dirty(true);
+ memcpy((void *)ptr, (void *)data, sizeof(int) * comp_len * array_size);
+ }
+}
+
+bool MTLShader::get_push_constant_is_dirty()
+{
+ return push_constant_modified_;
+}
+
+void MTLShader::push_constant_bindstate_mark_dirty(bool is_dirty)
+{
+ push_constant_modified_ = is_dirty;
+}
+
+void MTLShader::vertformat_from_shader(GPUVertFormat *format) const
+{
+ GPU_vertformat_clear(format);
+
+ const MTLShaderInterface *mtl_interface = static_cast<const MTLShaderInterface *>(interface);
+ for (const uint attr_id : IndexRange(mtl_interface->get_total_attributes())) {
+ const MTLShaderInputAttribute &attr = mtl_interface->get_attribute(attr_id);
+
+ /* Extract type parameters from Metal type. */
+ GPUVertCompType comp_type = comp_type_from_vert_format(attr.format);
+ uint comp_len = comp_count_from_vert_format(attr.format);
+ GPUVertFetchMode fetch_mode = fetchmode_from_vert_format(attr.format);
+
+ GPU_vertformat_attr_add(format,
+ mtl_interface->get_name_at_offset(attr.name_offset),
+ comp_type,
+ comp_len,
+ fetch_mode);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name METAL Custom Behavior
+ * \{ */
+
+void MTLShader::set_vertex_function_name(NSString *vert_function_name)
+{
+ vertex_function_name_ = vert_function_name;
+}
+
+void MTLShader::set_fragment_function_name(NSString *frag_function_name)
+{
+ fragment_function_name_ = frag_function_name;
+}
+
+void MTLShader::shader_source_from_msl(NSString *input_vertex_source,
+ NSString *input_fragment_source)
+{
+ BLI_assert(shd_builder_ != nullptr);
+ shd_builder_->msl_source_vert_ = input_vertex_source;
+ shd_builder_->msl_source_frag_ = input_fragment_source;
+ shd_builder_->source_from_msl_ = true;
+}
+
+void MTLShader::set_interface(MTLShaderInterface *interface)
+{
+ /* Assign gpu::Shader super-class interface. */
+ Shader::interface = interface;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Bake Pipeline State Objects
+ * \{ */
+
+/**
+ * Bakes or fetches a pipeline state using the current
+ * #MTLRenderPipelineStateDescriptor state.
+ *
+ * This state contains information on shader inputs/outputs, such
+ * as the vertex descriptor, used to control vertex assembly for
+ * current vertex data, and active render target information,
+ * describing the output attachment pixel formats.
+ *
+ * Other rendering parameters such as global point-size, blend state, color mask
+ * etc; are also used. See mtl_shader.h for full #MLRenderPipelineStateDescriptor.
+ */
+MTLRenderPipelineStateInstance *MTLShader::bake_current_pipeline_state(
+ MTLContext *ctx, MTLPrimitiveTopologyClass prim_type)
+{
+ /* NOTE(Metal): PSO cache can be accessed from multiple threads, though these operations should
+ * be thread-safe due to organization of high-level renderer. If there are any issues, then
+ * access can be guarded as appropriate. */
+ BLI_assert(this);
+ MTLShaderInterface *mtl_interface = this->get_interface();
+ BLI_assert(mtl_interface);
+ BLI_assert(this->is_valid());
+
+ /* NOTE(Metal): Vertex input assembly description will have been populated externally
+ * via #MTLBatch or #MTLImmediate during binding or draw. */
+
+ /* Resolve Context Frame-buffer state. */
+ MTLFrameBuffer *framebuffer = ctx->get_current_framebuffer();
+
+ /* Update global pipeline descriptor. */
+ MTLStateManager *state_manager = static_cast<MTLStateManager *>(
+ MTLContext::get()->state_manager);
+ MTLRenderPipelineStateDescriptor &pipeline_descriptor = state_manager->get_pipeline_descriptor();
+
+ pipeline_descriptor.num_color_attachments = 0;
+ for (int attachment = 0; attachment < GPU_FB_MAX_COLOR_ATTACHMENT; attachment++) {
+ MTLAttachment color_attachment = framebuffer->get_color_attachment(attachment);
+
+ if (color_attachment.used) {
+ /* If SRGB is disabled and format is SRGB, use color data directly with no conversions
+ * between linear and SRGB. */
+ MTLPixelFormat mtl_format = gpu_texture_format_to_metal(
+ color_attachment.texture->format_get());
+ if (framebuffer->get_is_srgb() && !framebuffer->get_srgb_enabled()) {
+ mtl_format = MTLPixelFormatRGBA8Unorm;
+ }
+ pipeline_descriptor.color_attachment_format[attachment] = mtl_format;
+ }
+ else {
+ pipeline_descriptor.color_attachment_format[attachment] = MTLPixelFormatInvalid;
+ }
+
+ pipeline_descriptor.num_color_attachments += (color_attachment.used) ? 1 : 0;
+ }
+ MTLAttachment depth_attachment = framebuffer->get_depth_attachment();
+ MTLAttachment stencil_attachment = framebuffer->get_stencil_attachment();
+ pipeline_descriptor.depth_attachment_format = (depth_attachment.used) ?
+ gpu_texture_format_to_metal(
+ depth_attachment.texture->format_get()) :
+ MTLPixelFormatInvalid;
+ pipeline_descriptor.stencil_attachment_format =
+ (stencil_attachment.used) ?
+ gpu_texture_format_to_metal(stencil_attachment.texture->format_get()) :
+ MTLPixelFormatInvalid;
+
+ /* Resolve Context Pipeline State (required by PSO). */
+ pipeline_descriptor.color_write_mask = ctx->pipeline_state.color_write_mask;
+ pipeline_descriptor.blending_enabled = ctx->pipeline_state.blending_enabled;
+ pipeline_descriptor.alpha_blend_op = ctx->pipeline_state.alpha_blend_op;
+ pipeline_descriptor.rgb_blend_op = ctx->pipeline_state.rgb_blend_op;
+ pipeline_descriptor.dest_alpha_blend_factor = ctx->pipeline_state.dest_alpha_blend_factor;
+ pipeline_descriptor.dest_rgb_blend_factor = ctx->pipeline_state.dest_rgb_blend_factor;
+ pipeline_descriptor.src_alpha_blend_factor = ctx->pipeline_state.src_alpha_blend_factor;
+ pipeline_descriptor.src_rgb_blend_factor = ctx->pipeline_state.src_rgb_blend_factor;
+ pipeline_descriptor.point_size = ctx->pipeline_state.point_size;
+
+ /* Primitive Type -- Primitive topology class needs to be specified for layered rendering. */
+ bool requires_specific_topology_class = uses_mtl_array_index_ ||
+ prim_type == MTLPrimitiveTopologyClassPoint;
+ pipeline_descriptor.vertex_descriptor.prim_topology_class =
+ (requires_specific_topology_class) ? prim_type : MTLPrimitiveTopologyClassUnspecified;
+
+ /* Check if current PSO exists in the cache. */
+ MTLRenderPipelineStateInstance **pso_lookup = pso_cache_.lookup_ptr(pipeline_descriptor);
+ MTLRenderPipelineStateInstance *pipeline_state = (pso_lookup) ? *pso_lookup : nullptr;
+ if (pipeline_state != nullptr) {
+ return pipeline_state;
+ }
+
+ shader_debug_printf("Baking new pipeline variant for shader: %s\n", this->name);
+
+ /* Generate new Render Pipeline State Object (PSO). */
+ @autoreleasepool {
+ /* Prepare Render Pipeline Descriptor. */
+
+ /* Setup function specialization constants, used to modify and optimize
+ * generated code based on current render pipeline configuration. */
+ MTLFunctionConstantValues *values = [[MTLFunctionConstantValues new] autorelease];
+
+ /* Prepare Vertex descriptor based on current pipeline vertex binding state. */
+ MTLRenderPipelineStateDescriptor &current_state = pipeline_descriptor;
+ MTLRenderPipelineDescriptor *desc = pso_descriptor_;
+ [desc reset];
+ pso_descriptor_.label = [NSString stringWithUTF8String:this->name];
+
+ /* Offset the bind index for Uniform buffers such that they begin after the VBO
+ * buffer bind slots. `MTL_uniform_buffer_base_index` is passed as a function
+ * specialization constant, customized per unique pipeline state permutation.
+ *
+ * NOTE: For binding point compaction, we could use the number of VBOs present
+ * in the current PSO configuration `current_state.vertex_descriptor.num_vert_buffers`).
+ * However, it is more efficient to simply offset the uniform buffer base index to the
+ * maximal number of VBO bind-points, as then UBO bind-points for similar draw calls
+ * will align and avoid the requirement for additional binding. */
+ int MTL_uniform_buffer_base_index = GPU_BATCH_VBO_MAX_LEN;
+
+ /* Null buffer index is used if an attribute is not found in the
+ * bound VBOs #VertexFormat. */
+ int null_buffer_index = current_state.vertex_descriptor.num_vert_buffers;
+ bool using_null_buffer = false;
+
+ if (this->get_uses_ssbo_vertex_fetch()) {
+ /* If using SSBO Vertex fetch mode, no vertex descriptor is required
+ * as we wont be using stage-in. */
+ desc.vertexDescriptor = nil;
+ desc.inputPrimitiveTopology = MTLPrimitiveTopologyClassUnspecified;
+
+ /* We want to offset the uniform buffer base to allow for sufficient VBO binding slots - We
+ * also require +1 slot for the Index buffer. */
+ MTL_uniform_buffer_base_index = MTL_SSBO_VERTEX_FETCH_IBO_INDEX + 1;
+ }
+ else {
+ for (const uint i : IndexRange(current_state.vertex_descriptor.num_attributes)) {
+
+ /* Metal back-end attribute descriptor state. */
+ MTLVertexAttributeDescriptorPSO &attribute_desc =
+ current_state.vertex_descriptor.attributes[i];
+
+ /* Flag format conversion */
+ /* In some cases, Metal cannot implicitly convert between data types.
+ * In these instances, the fetch mode #GPUVertFetchMode as provided in the vertex format
+ * is passed in, and used to populate function constants named: MTL_AttributeConvert0..15.
+ *
+ * It is then the responsibility of the vertex shader to perform any necessary type
+ * casting.
+ *
+ * See `mtl_shader.hh` for more information. Relevant Metal API documentation:
+ * https://developer.apple.com/documentation/metal/mtlvertexattributedescriptor/1516081-format?language=objc
+ */
+ if (attribute_desc.format == MTLVertexFormatInvalid) {
+ MTL_LOG_WARNING(
+ "MTLShader: baking pipeline state for '%s'- expected input attribute at "
+ "index '%d' but none was specified in the current vertex state\n",
+ mtl_interface->get_name(),
+ i);
+
+ /* Write out null conversion constant if attribute unused. */
+ int MTL_attribute_conversion_mode = 0;
+ [values setConstantValue:&MTL_attribute_conversion_mode
+ type:MTLDataTypeInt
+ withName:[NSString stringWithFormat:@"MTL_AttributeConvert%d", i]];
+ continue;
+ }
+
+ int MTL_attribute_conversion_mode = (int)attribute_desc.format_conversion_mode;
+ [values setConstantValue:&MTL_attribute_conversion_mode
+ type:MTLDataTypeInt
+ withName:[NSString stringWithFormat:@"MTL_AttributeConvert%d", i]];
+ if (MTL_attribute_conversion_mode == GPU_FETCH_INT_TO_FLOAT_UNIT ||
+ MTL_attribute_conversion_mode == GPU_FETCH_INT_TO_FLOAT) {
+ shader_debug_printf(
+ "TODO(Metal): Shader %s needs to support internal format conversion\n",
+ mtl_interface->name);
+ }
+
+ /* Copy metal back-end attribute descriptor state into PSO descriptor.
+ * NOTE: need to copy each element due to direct assignment restrictions.
+ * Also note */
+ MTLVertexAttributeDescriptor *mtl_attribute = desc.vertexDescriptor.attributes[i];
+
+ mtl_attribute.format = attribute_desc.format;
+ mtl_attribute.offset = attribute_desc.offset;
+ mtl_attribute.bufferIndex = attribute_desc.buffer_index;
+ }
+
+ for (const uint i : IndexRange(current_state.vertex_descriptor.num_vert_buffers)) {
+ /* Metal back-end state buffer layout. */
+ const MTLVertexBufferLayoutDescriptorPSO &buf_layout =
+ current_state.vertex_descriptor.buffer_layouts[i];
+ /* Copy metal back-end buffer layout state into PSO descriptor.
+ * NOTE: need to copy each element due to copying from internal
+ * back-end descriptor to Metal API descriptor. */
+ MTLVertexBufferLayoutDescriptor *mtl_buf_layout = desc.vertexDescriptor.layouts[i];
+
+ mtl_buf_layout.stepFunction = buf_layout.step_function;
+ mtl_buf_layout.stepRate = buf_layout.step_rate;
+ mtl_buf_layout.stride = buf_layout.stride;
+ }
+
+ /* Mark empty attribute conversion. */
+ for (int i = current_state.vertex_descriptor.num_attributes; i < GPU_VERT_ATTR_MAX_LEN;
+ i++) {
+ int MTL_attribute_conversion_mode = 0;
+ [values setConstantValue:&MTL_attribute_conversion_mode
+ type:MTLDataTypeInt
+ withName:[NSString stringWithFormat:@"MTL_AttributeConvert%d", i]];
+ }
+
+ /* DEBUG: Missing/empty attributes. */
+ /* Attributes are normally mapped as part of the state setting based on the used
+ * #GPUVertFormat, however, if attributes have not been set, we can sort them out here. */
+ for (const uint i : IndexRange(mtl_interface->get_total_attributes())) {
+ const MTLShaderInputAttribute &attribute = mtl_interface->get_attribute(i);
+ MTLVertexAttributeDescriptor *current_attribute = desc.vertexDescriptor.attributes[i];
+
+ if (current_attribute.format == MTLVertexFormatInvalid) {
+#if MTL_DEBUG_SHADER_ATTRIBUTES == 1
+ MTL_LOG_INFO("-> Filling in unbound attribute '%s' for shader PSO '%s' \n",
+ attribute.name,
+ mtl_interface->name);
+#endif
+ current_attribute.format = attribute.format;
+ current_attribute.offset = 0;
+ current_attribute.bufferIndex = null_buffer_index;
+
+ /* Add Null vert buffer binding for invalid attributes. */
+ if (!using_null_buffer) {
+ MTLVertexBufferLayoutDescriptor *null_buf_layout =
+ desc.vertexDescriptor.layouts[null_buffer_index];
+
+ /* Use constant step function such that null buffer can
+ * contain just a singular dummy attribute. */
+ null_buf_layout.stepFunction = MTLVertexStepFunctionConstant;
+ null_buf_layout.stepRate = 0;
+ null_buf_layout.stride = max_ii(null_buf_layout.stride, attribute.size);
+
+ /* If we are using the maximum number of vertex buffers, or tight binding indices,
+ * MTL_uniform_buffer_base_index needs shifting to the bind slot after the null buffer
+ * index. */
+ if (null_buffer_index >= MTL_uniform_buffer_base_index) {
+ MTL_uniform_buffer_base_index = null_buffer_index + 1;
+ }
+ using_null_buffer = true;
+#if MTL_DEBUG_SHADER_ATTRIBUTES == 1
+ MTL_LOG_INFO("Setting up buffer binding for null attribute with buffer index %d\n",
+ null_buffer_index);
+#endif
+ }
+ }
+ }
+
+ /* Primitive Topology */
+ desc.inputPrimitiveTopology = pipeline_descriptor.vertex_descriptor.prim_topology_class;
+ }
+
+ /* Update constant value for 'MTL_uniform_buffer_base_index' */
+ [values setConstantValue:&MTL_uniform_buffer_base_index
+ type:MTLDataTypeInt
+ withName:@"MTL_uniform_buffer_base_index"];
+
+ /* Transform feedback constant */
+ int MTL_transform_feedback_buffer_index = (this->transform_feedback_type_ !=
+ GPU_SHADER_TFB_NONE) ?
+ MTL_uniform_buffer_base_index +
+ mtl_interface->get_total_uniform_blocks() :
+ -1;
+ if (this->transform_feedback_type_ != GPU_SHADER_TFB_NONE) {
+ [values setConstantValue:&MTL_transform_feedback_buffer_index
+ type:MTLDataTypeInt
+ withName:@"MTL_transform_feedback_buffer_index"];
+ }
+
+ /* gl_PointSize constant */
+ bool null_pointsize = true;
+ float MTL_pointsize = pipeline_descriptor.point_size;
+ if (pipeline_descriptor.vertex_descriptor.prim_topology_class ==
+ MTLPrimitiveTopologyClassPoint) {
+ /* `if pointsize is > 0.0`, PROGRAM_POINT_SIZE is enabled, and `gl_PointSize` shader keyword
+ * overrides the value. Otherwise, if < 0.0, use global constant point size. */
+ if (MTL_pointsize < 0.0) {
+ MTL_pointsize = fabsf(MTL_pointsize);
+ [values setConstantValue:&MTL_pointsize
+ type:MTLDataTypeFloat
+ withName:@"MTL_global_pointsize"];
+ null_pointsize = false;
+ }
+ }
+
+ if (null_pointsize) {
+ MTL_pointsize = 0.0f;
+ [values setConstantValue:&MTL_pointsize
+ type:MTLDataTypeFloat
+ withName:@"MTL_global_pointsize"];
+ }
+
+ /* Compile functions */
+ NSError *error = nullptr;
+ desc.vertexFunction = [shader_library_vert_ newFunctionWithName:vertex_function_name_
+ constantValues:values
+ error:&error];
+ if (error) {
+ NSLog(@"Compile Error - Metal Shader vertex function, error %@", error);
+
+ /* Only exit out if genuine error and not warning */
+ if ([[error localizedDescription] rangeOfString:@"Compilation succeeded"].location ==
+ NSNotFound) {
+ BLI_assert(false);
+ return nullptr;
+ }
+ }
+
+ /* If transform feedback is used, Vertex-only stage */
+ if (transform_feedback_type_ == GPU_SHADER_TFB_NONE) {
+ desc.fragmentFunction = [shader_library_frag_ newFunctionWithName:fragment_function_name_
+ constantValues:values
+ error:&error];
+ if (error) {
+ NSLog(@"Compile Error - Metal Shader fragment function, error %@", error);
+
+ /* Only exit out if genuine error and not warning */
+ if ([[error localizedDescription] rangeOfString:@"Compilation succeeded"].location ==
+ NSNotFound) {
+ BLI_assert(false);
+ return nullptr;
+ }
+ }
+ }
+ else {
+ desc.fragmentFunction = nil;
+ desc.rasterizationEnabled = false;
+ }
+
+ /* Setup pixel format state */
+ for (int color_attachment = 0; color_attachment < GPU_FB_MAX_COLOR_ATTACHMENT;
+ color_attachment++) {
+ /* Fetch color attachment pixel format in back-end pipeline state. */
+ MTLPixelFormat pixel_format = current_state.color_attachment_format[color_attachment];
+ /* Populate MTL API PSO attachment descriptor. */
+ MTLRenderPipelineColorAttachmentDescriptor *col_attachment =
+ desc.colorAttachments[color_attachment];
+
+ col_attachment.pixelFormat = pixel_format;
+ if (pixel_format != MTLPixelFormatInvalid) {
+ bool format_supports_blending = mtl_format_supports_blending(pixel_format);
+
+ col_attachment.writeMask = current_state.color_write_mask;
+ col_attachment.blendingEnabled = current_state.blending_enabled &&
+ format_supports_blending;
+ if (format_supports_blending && current_state.blending_enabled) {
+ col_attachment.alphaBlendOperation = current_state.alpha_blend_op;
+ col_attachment.rgbBlendOperation = current_state.rgb_blend_op;
+ col_attachment.destinationAlphaBlendFactor = current_state.dest_alpha_blend_factor;
+ col_attachment.destinationRGBBlendFactor = current_state.dest_rgb_blend_factor;
+ col_attachment.sourceAlphaBlendFactor = current_state.src_alpha_blend_factor;
+ col_attachment.sourceRGBBlendFactor = current_state.src_rgb_blend_factor;
+ }
+ else {
+ if (current_state.blending_enabled && !format_supports_blending) {
+ shader_debug_printf(
+ "[Warning] Attempting to Bake PSO, but MTLPixelFormat %d does not support "
+ "blending\n",
+ *((int *)&pixel_format));
+ }
+ }
+ }
+ }
+ desc.depthAttachmentPixelFormat = current_state.depth_attachment_format;
+ desc.stencilAttachmentPixelFormat = current_state.stencil_attachment_format;
+
+ /* Compile PSO */
+
+ MTLAutoreleasedRenderPipelineReflection reflection_data;
+ id<MTLRenderPipelineState> pso = [ctx->device
+ newRenderPipelineStateWithDescriptor:desc
+ options:MTLPipelineOptionBufferTypeInfo
+ reflection:&reflection_data
+ error:&error];
+ if (error) {
+ NSLog(@"Failed to create PSO for shader: %s error %@\n", this->name, error);
+ BLI_assert(false);
+ return nullptr;
+ }
+ else if (!pso) {
+ NSLog(@"Failed to create PSO for shader: %s, but no error was provided!\n", this->name);
+ BLI_assert(false);
+ return nullptr;
+ }
+ else {
+ NSLog(@"Successfully compiled PSO for shader: %s (Metal Context: %p)\n", this->name, ctx);
+ }
+
+ /* Prepare pipeline state instance. */
+ MTLRenderPipelineStateInstance *pso_inst = new MTLRenderPipelineStateInstance();
+ pso_inst->vert = desc.vertexFunction;
+ pso_inst->frag = desc.fragmentFunction;
+ pso_inst->pso = pso;
+ pso_inst->base_uniform_buffer_index = MTL_uniform_buffer_base_index;
+ pso_inst->null_attribute_buffer_index = (using_null_buffer) ? null_buffer_index : -1;
+ pso_inst->transform_feedback_buffer_index = MTL_transform_feedback_buffer_index;
+ pso_inst->shader_pso_index = pso_cache_.size();
+
+ pso_inst->reflection_data_available = (reflection_data != nil);
+ if (reflection_data != nil) {
+
+ /* Extract shader reflection data for buffer bindings.
+ * This reflection data is used to contrast the binding information
+ * we know about in the interface against the bindings in the finalized
+ * PSO. This accounts for bindings which have been stripped out during
+ * optimization, and allows us to both avoid over-binding and also
+ * allows us to verify size-correctness for bindings, to ensure
+ * that buffers bound are not smaller than the size of expected data. */
+ NSArray<MTLArgument *> *vert_args = [reflection_data vertexArguments];
+
+ pso_inst->buffer_bindings_reflection_data_vert.clear();
+ int buffer_binding_max_ind = 0;
+
+ for (int i = 0; i < [vert_args count]; i++) {
+ MTLArgument *arg = [vert_args objectAtIndex:i];
+ if ([arg type] == MTLArgumentTypeBuffer) {
+ int buf_index = [arg index] - MTL_uniform_buffer_base_index;
+ if (buf_index >= 0) {
+ buffer_binding_max_ind = max_ii(buffer_binding_max_ind, buf_index);
+ }
+ }
+ }
+ pso_inst->buffer_bindings_reflection_data_vert.resize(buffer_binding_max_ind + 1);
+ for (int i = 0; i < buffer_binding_max_ind + 1; i++) {
+ pso_inst->buffer_bindings_reflection_data_vert[i] = {0, 0, 0, false};
+ }
+
+ for (int i = 0; i < [vert_args count]; i++) {
+ MTLArgument *arg = [vert_args objectAtIndex:i];
+ if ([arg type] == MTLArgumentTypeBuffer) {
+ int buf_index = [arg index] - MTL_uniform_buffer_base_index;
+
+ if (buf_index >= 0) {
+ pso_inst->buffer_bindings_reflection_data_vert[buf_index] = {
+ (uint32_t)([arg index]),
+ (uint32_t)([arg bufferDataSize]),
+ (uint32_t)([arg bufferAlignment]),
+ ([arg isActive] == YES) ? true : false};
+ }
+ }
+ }
+
+ NSArray<MTLArgument *> *frag_args = [reflection_data fragmentArguments];
+
+ pso_inst->buffer_bindings_reflection_data_frag.clear();
+ buffer_binding_max_ind = 0;
+
+ for (int i = 0; i < [frag_args count]; i++) {
+ MTLArgument *arg = [frag_args objectAtIndex:i];
+ if ([arg type] == MTLArgumentTypeBuffer) {
+ int buf_index = [arg index] - MTL_uniform_buffer_base_index;
+ if (buf_index >= 0) {
+ buffer_binding_max_ind = max_ii(buffer_binding_max_ind, buf_index);
+ }
+ }
+ }
+ pso_inst->buffer_bindings_reflection_data_frag.resize(buffer_binding_max_ind + 1);
+ for (int i = 0; i < buffer_binding_max_ind + 1; i++) {
+ pso_inst->buffer_bindings_reflection_data_frag[i] = {0, 0, 0, false};
+ }
+
+ for (int i = 0; i < [frag_args count]; i++) {
+ MTLArgument *arg = [frag_args objectAtIndex:i];
+ if ([arg type] == MTLArgumentTypeBuffer) {
+ int buf_index = [arg index] - MTL_uniform_buffer_base_index;
+ shader_debug_printf(" BUF IND: %d (arg name: %s)\n", buf_index, [[arg name] UTF8String]);
+ if (buf_index >= 0) {
+ pso_inst->buffer_bindings_reflection_data_frag[buf_index] = {
+ (uint32_t)([arg index]),
+ (uint32_t)([arg bufferDataSize]),
+ (uint32_t)([arg bufferAlignment]),
+ ([arg isActive] == YES) ? true : false};
+ }
+ }
+ }
+ }
+
+ [pso_inst->vert retain];
+ [pso_inst->frag retain];
+ [pso_inst->pso retain];
+
+ /* Insert into pso cache. */
+ pso_cache_.add(pipeline_descriptor, pso_inst);
+ shader_debug_printf("PSO CACHE: Stored new variant in PSO cache for shader '%s'\n",
+ this->name);
+ return pso_inst;
+ }
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name SSBO-vertex-fetch-mode attribute control.
+ * \{ */
+
+int MTLShader::ssbo_vertex_type_to_attr_type(MTLVertexFormat attribute_type)
+{
+ switch (attribute_type) {
+ case MTLVertexFormatFloat:
+ return GPU_SHADER_ATTR_TYPE_FLOAT;
+ case MTLVertexFormatInt:
+ return GPU_SHADER_ATTR_TYPE_INT;
+ case MTLVertexFormatUInt:
+ return GPU_SHADER_ATTR_TYPE_UINT;
+ case MTLVertexFormatShort:
+ return GPU_SHADER_ATTR_TYPE_SHORT;
+ case MTLVertexFormatUChar:
+ return GPU_SHADER_ATTR_TYPE_CHAR;
+ case MTLVertexFormatUChar2:
+ return GPU_SHADER_ATTR_TYPE_CHAR2;
+ case MTLVertexFormatUChar3:
+ return GPU_SHADER_ATTR_TYPE_CHAR3;
+ case MTLVertexFormatUChar4:
+ return GPU_SHADER_ATTR_TYPE_CHAR4;
+ case MTLVertexFormatFloat2:
+ return GPU_SHADER_ATTR_TYPE_VEC2;
+ case MTLVertexFormatFloat3:
+ return GPU_SHADER_ATTR_TYPE_VEC3;
+ case MTLVertexFormatFloat4:
+ return GPU_SHADER_ATTR_TYPE_VEC4;
+ case MTLVertexFormatUInt2:
+ return GPU_SHADER_ATTR_TYPE_UVEC2;
+ case MTLVertexFormatUInt3:
+ return GPU_SHADER_ATTR_TYPE_UVEC3;
+ case MTLVertexFormatUInt4:
+ return GPU_SHADER_ATTR_TYPE_UVEC4;
+ case MTLVertexFormatInt2:
+ return GPU_SHADER_ATTR_TYPE_IVEC2;
+ case MTLVertexFormatInt3:
+ return GPU_SHADER_ATTR_TYPE_IVEC3;
+ case MTLVertexFormatInt4:
+ return GPU_SHADER_ATTR_TYPE_IVEC4;
+ case MTLVertexFormatUCharNormalized:
+ return GPU_SHADER_ATTR_TYPE_UCHAR_NORM;
+ case MTLVertexFormatUChar2Normalized:
+ return GPU_SHADER_ATTR_TYPE_UCHAR2_NORM;
+ case MTLVertexFormatUChar3Normalized:
+ return GPU_SHADER_ATTR_TYPE_UCHAR3_NORM;
+ case MTLVertexFormatUChar4Normalized:
+ return GPU_SHADER_ATTR_TYPE_UCHAR4_NORM;
+ case MTLVertexFormatInt1010102Normalized:
+ return GPU_SHADER_ATTR_TYPE_INT1010102_NORM;
+ case MTLVertexFormatShort3Normalized:
+ return GPU_SHADER_ATTR_TYPE_SHORT3_NORM;
+ default:
+ BLI_assert_msg(false,
+ "Not yet supported attribute type for SSBO vertex fetch -- Add entry "
+ "GPU_SHADER_ATTR_TYPE_** to shader defines, and in this table");
+ return -1;
+ }
+ return -1;
+}
+
+void MTLShader::ssbo_vertex_fetch_bind_attributes_begin()
+{
+ MTLShaderInterface *mtl_interface = this->get_interface();
+ ssbo_vertex_attribute_bind_active_ = true;
+ ssbo_vertex_attribute_bind_mask_ = (1 << mtl_interface->get_total_attributes()) - 1;
+
+ /* Reset tracking of actively used VBO bind slots for SSBO vertex fetch mode. */
+ for (int i = 0; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) {
+ ssbo_vbo_slot_used_[i] = false;
+ }
+}
+
+void MTLShader::ssbo_vertex_fetch_bind_attribute(const MTLSSBOAttribute &ssbo_attr)
+{
+ /* Fetch attribute. */
+ MTLShaderInterface *mtl_interface = this->get_interface();
+ BLI_assert(ssbo_attr.mtl_attribute_index >= 0 &&
+ ssbo_attr.mtl_attribute_index < mtl_interface->get_total_attributes());
+
+ /* Update bind-mask to verify this attribute has been used. */
+ BLI_assert((ssbo_vertex_attribute_bind_mask_ & (1 << ssbo_attr.mtl_attribute_index)) ==
+ (1 << ssbo_attr.mtl_attribute_index) &&
+ "Attribute has already been bound");
+ ssbo_vertex_attribute_bind_mask_ &= ~(1 << ssbo_attr.mtl_attribute_index);
+
+ /* Fetch attribute uniform addresses from cache. */
+ ShaderSSBOAttributeBinding &cached_ssbo_attribute =
+ cached_ssbo_attribute_bindings_[ssbo_attr.mtl_attribute_index];
+ BLI_assert(cached_ssbo_attribute.attribute_index >= 0);
+
+ /* Write attribute descriptor properties to shader uniforms. */
+ this->uniform_int(cached_ssbo_attribute.uniform_offset, 1, 1, &ssbo_attr.attribute_offset);
+ this->uniform_int(cached_ssbo_attribute.uniform_stride, 1, 1, &ssbo_attr.per_vertex_stride);
+ int inst_val = (ssbo_attr.is_instance ? 1 : 0);
+ this->uniform_int(cached_ssbo_attribute.uniform_fetchmode, 1, 1, &inst_val);
+ this->uniform_int(cached_ssbo_attribute.uniform_vbo_id, 1, 1, &ssbo_attr.vbo_id);
+ BLI_assert(ssbo_attr.attribute_format >= 0);
+ this->uniform_int(cached_ssbo_attribute.uniform_attr_type, 1, 1, &ssbo_attr.attribute_format);
+ ssbo_vbo_slot_used_[ssbo_attr.vbo_id] = true;
+}
+
+void MTLShader::ssbo_vertex_fetch_bind_attributes_end(id<MTLRenderCommandEncoder> active_encoder)
+{
+ ssbo_vertex_attribute_bind_active_ = false;
+
+ /* If our mask is non-zero, we have unassigned attributes. */
+ if (ssbo_vertex_attribute_bind_mask_ != 0) {
+ MTLShaderInterface *mtl_interface = this->get_interface();
+
+ /* Determine if there is a free slot we can bind the null buffer to -- We should have at
+ * least ONE free slot in this instance. */
+ int null_attr_buffer_slot = -1;
+ for (int i = 0; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) {
+ if (!ssbo_vbo_slot_used_[i]) {
+ null_attr_buffer_slot = i;
+ break;
+ }
+ }
+ BLI_assert_msg(null_attr_buffer_slot >= 0,
+ "No suitable bind location for a NULL buffer was found");
+
+ for (int i = 0; i < mtl_interface->get_total_attributes(); i++) {
+ if (ssbo_vertex_attribute_bind_mask_ & (1 << i)) {
+ const MTLShaderInputAttribute *mtl_shader_attribute = &mtl_interface->get_attribute(i);
+#if MTL_DEBUG_SHADER_ATTRIBUTES == 1
+ MTL_LOG_WARNING(
+ "SSBO Vertex Fetch missing attribute with index: %d. Shader: %s, Attr "
+ "Name: "
+ "%s - Null buffer bound\n",
+ i,
+ this->name_get(),
+ mtl_shader_attribute->name);
+#endif
+ /* Bind Attribute with NULL buffer index and stride zero (for constant access). */
+ MTLSSBOAttribute ssbo_attr(
+ i, null_attr_buffer_slot, 0, 0, GPU_SHADER_ATTR_TYPE_FLOAT, false);
+ ssbo_vertex_fetch_bind_attribute(ssbo_attr);
+ MTL_LOG_WARNING(
+ "Unassigned Shader attribute: %s, Attr Name: %s -- Binding NULL BUFFER to "
+ "slot %d\n",
+ this->name_get(),
+ mtl_interface->get_name_at_offset(mtl_shader_attribute->name_offset),
+ null_attr_buffer_slot);
+ }
+ }
+
+ /* Bind NULL buffer to given VBO slot. */
+ MTLContext *ctx = reinterpret_cast<MTLContext *>(GPU_context_active_get());
+ id<MTLBuffer> null_buf = ctx->get_null_attribute_buffer();
+ BLI_assert(null_buf);
+
+ MTLRenderPassState &rps = ctx->main_command_buffer.get_render_pass_state();
+ rps.bind_vertex_buffer(null_buf, 0, null_attr_buffer_slot);
+ }
+}
+
+GPUVertBuf *MTLShader::get_transform_feedback_active_buffer()
+{
+ if (transform_feedback_type_ == GPU_SHADER_TFB_NONE || !transform_feedback_active_) {
+ return nullptr;
+ }
+ return transform_feedback_vertbuf_;
+}
+
+bool MTLShader::has_transform_feedback_varying(std::string str)
+{
+ if (this->transform_feedback_type_ == GPU_SHADER_TFB_NONE) {
+ return false;
+ }
+
+ return (std::find(tf_output_name_list_.begin(), tf_output_name_list_.end(), str) !=
+ tf_output_name_list_.end());
+}
+
+} // blender::gpu::shdaer
diff --git a/source/blender/gpu/metal/mtl_shader_generator.hh b/source/blender/gpu/metal/mtl_shader_generator.hh
new file mode 100644
index 00000000000..43890ca0170
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_shader_generator.hh
@@ -0,0 +1,727 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+#include "gpu_shader_create_info.hh"
+#include "gpu_shader_private.hh"
+
+/** -- Metal Shader Generator for GLSL -> MSL conversion --
+ *
+ * The Metal shader generator class is used as a conversion utility for generating
+ * a compatible MSL shader from a source GLSL shader. There are several steps
+ * involved in creating a shader, and structural changes which enable the source
+ * to function in the same way.
+ *
+ * 1) Extraction and conversion of shaders input's and output's to their Metal-compatible
+ * version. This is a subtle data transformation from GPUShaderCreateInfo, allowing
+ * for Metal-specific parameters.
+ *
+ * 2) Determine usage of shader features such as GL global variable usage, depth write output,
+ * clip distances, multilayered rendering, barycentric coordinates etc;
+ *
+ * 3) Generate MSL shader.
+ *
+ * 4) Populate #MTLShaderInterface, describing input/output structure, bind-points, buffer size and
+ * alignment, shader feature usage etc; Everything required by the Metal back-end to
+ * successfully enable use of shaders and GPU back-end features.
+ *
+ *
+ *
+ * For each shading stage, we generate an MSL shader following these steps:
+ *
+ * 1) Output custom shader defines describing modes e.g. whether we are using
+ * sampler bindings or argument buffers; at the top of the shader.
+ *
+ * 2) Inject common Metal headers.
+ * - `mtl_shader_defines.msl` is used to map GLSL functions to MSL.
+ * - `mtl_shader_common.msl` is added to ALL MSL shaders to provide
+ * common functionality required by the back-end. This primarily
+ * contains function-constant hooks, used in PSO generation.
+ *
+ * 3) Create a class Scope which wraps the GLSL shader. This is used to
+ * create a global per-thread scope around the shader source, to allow
+ * access to common shader members (GLSL globals, shader inputs/outputs etc)
+ *
+ * 4) Generate shader interface structs and populate local members where required for:
+ * - `VertexInputs`
+ * - `VertexOutputs`
+ * - `Uniforms`
+ * - `Uniform Blocks`
+ * - `textures` ;
+ * etc;
+ *
+ * 5) Inject GLSL source.
+ *
+ * 6) Generate MSL shader entry point function. Every Metal shader must have a
+ * vertex/fragment/kernel entry-point, which contains the function binding table.
+ * This is where bindings are specified and passed into the shader.
+ *
+ * For converted shaders, the MSL entry-point will also instantiate a shader
+ * class per thread, and pass over bound resource references into the class.
+ *
+ * Finally, the shaders "main()" method will be called, and outputs are copied.
+ *
+ * NOTE: For position outputs, the default output position will be converted to
+ * the Metal coordinate space, which involves flipping the Y coordinate and
+ * re-mapping the depth range between 0 and 1, as with Vulkan.
+ *
+ *
+ * The final shader structure looks as follows:
+ *
+ * \code{.cc}
+ * -- Shader defines --
+ * #define USE_ARGUMENT_BUFFER_FOR_SAMPLERS 0
+ * ... etc ...;
+ *
+ * class MetalShaderVertexImp {
+ *
+ * -- Common shader interface structs --
+ * struct VertexIn {
+ * vec4 pos [[attribute(0)]]
+ * }
+ * struct VertexOut {...}
+ * struct PushConstantBlock {...}
+ * struct drw_Globals {...}
+ * ...
+ *
+ * -- GLSL source code --
+ * ...
+ * };
+ *
+ * vertex MetalShaderVertexImp::VertexOut vertex_function_entry(
+ * MetalShaderVertexImp::VertexIn v_in [[stage_in]],
+ * constant PushConstantBlock& globals [[buffer(MTL_uniform_buffer_base_index)]]) {
+ *
+ * MetalShaderVertexImp impl;
+ * -- Copy input members into impl instance --
+ * -- Execute GLSL main function --
+ * impl.main();
+ *
+ * -- Copy outputs and return --
+ * MetalShaderVertexImp::VertexOut out;
+ * out.pos = impl.pos;
+ * -- transform position to Metal coordinate system --
+ * return v_out;
+ * }
+ * \endcode
+ *
+ * -- SSBO-vertex-fetchmode --
+ *
+ * SSBO-vertex-fetchmode is a special option wherein vertex buffers are bound directly
+ * as buffers in the shader, rather than using the VertexDescriptor and [[stage_in]] vertex
+ * assembly.
+ *
+ * The purpose of this mode is to enable random-access reading of all vertex data. This is
+ * particularly useful for efficiently converting geometry shaders to Metal shading language,
+ * as these techniques are not supported natively in Metal.
+ *
+ * Geometry shaders can be re-created by firing off a vertex shader with the desired number of
+ * total output vertices. Each vertex can then read whichever input attributes it needs to
+ * achieve the output result.
+ * This manual reading is also used to provide support for GPU_provoking_vertex, wherein the
+ * output vertex for flat shading needs to change. In these cases, the manual vertex assembly
+ * can flip which vertices are read within the primitive.
+ *
+ * From an efficiency perspective, this is more GPU-friendly than geometry shading, due to improved
+ * parallelism throughout the whole pipe, and for Apple hardware specifically, there is no
+ * significant performance loss from manual vertex assembly vs under-the-hood assembly.
+ *
+ * This mode works by passing the required vertex descriptor information into the shader
+ * as uniform data, describing the type, stride, offset, step-mode and buffer index of each
+ * attribute, such that the shader SSBO-vertex-fetch utility functions know how to extract data.
+ *
+ * This also works with indexed rendering,
+ * by similarly binding the index buffer as a manual buffer.
+ *
+ * When this mode is used, the code generation and shader interface generation varies to
+ * accommodate the required features.
+ *
+ * This mode can be enabled in a shader with:
+ *
+ * `#pragma USE_SSBO_VERTEX_FETCH(TriangleList/LineList, output_vertex_count_per_input_primitive)`
+ *
+ * This mirrors the geometry shader interface `layout(triangle_strip, max_vertices = 3) out;`
+ */
+
+/* SSBO vertex fetch attribute uniform parameter names.
+ * These uniforms are used to pass the information
+ * required to perform manual vertex assembly within
+ * the vertex shader.
+ * Each vertex attribute requires a number of properties
+ * in order to correctly extract data from the bound vertex
+ * buffers. */
+#ifndef NDEBUG
+/* Global. */
+# define UNIFORM_SSBO_USES_INDEXED_RENDERING_STR "uniform_ssbo_uses_indexed_rendering"
+# define UNIFORM_SSBO_INDEX_MODE_U16_STR "uniform_ssbo_index_mode_u16"
+# define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR "uniform_ssbo_input_prim_type"
+# define UNIFORM_SSBO_INPUT_VERT_COUNT_STR "uniform_ssbo_input_vert_count"
+/* Per-attribute. */
+# define UNIFORM_SSBO_OFFSET_STR "uniform_ssbo_offset_"
+# define UNIFORM_SSBO_STRIDE_STR "uniform_ssbo_stride_"
+# define UNIFORM_SSBO_FETCHMODE_STR "uniform_ssbo_fetchmode_"
+# define UNIFORM_SSBO_VBO_ID_STR "uniform_ssbo_vbo_id_"
+# define UNIFORM_SSBO_TYPE_STR "uniform_ssbo_type_"
+#else
+/* Global. */
+# define UNIFORM_SSBO_USES_INDEXED_RENDERING_STR "_ir"
+# define UNIFORM_SSBO_INDEX_MODE_U16_STR "_mu"
+# define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR "_pt"
+# define UNIFORM_SSBO_INPUT_VERT_COUNT_STR "_vc"
+/* Per-attribute. */
+# define UNIFORM_SSBO_OFFSET_STR "_so"
+# define UNIFORM_SSBO_STRIDE_STR "_ss"
+# define UNIFORM_SSBO_FETCHMODE_STR "_sf"
+# define UNIFORM_SSBO_VBO_ID_STR "_sv"
+# define UNIFORM_SSBO_TYPE_STR "_st"
+#endif
+
+namespace blender::gpu {
+
+struct MSLUniform {
+ shader::Type type;
+ std::string name;
+ bool is_array;
+ int array_elems;
+ ShaderStage stage;
+
+ MSLUniform(shader::Type uniform_type,
+ std::string uniform_name,
+ bool is_array_type,
+ uint32_t num_elems = 1)
+ : type(uniform_type), name(uniform_name), is_array(is_array_type), array_elems(num_elems)
+ {
+ }
+
+ bool operator==(const MSLUniform &right) const
+ {
+ return (type == right.type && name == right.name && is_array == right.is_array &&
+ array_elems == right.array_elems);
+ }
+};
+
+struct MSLUniformBlock {
+ std::string type_name;
+ std::string name;
+ ShaderStage stage;
+ bool is_array;
+
+ bool operator==(const MSLUniformBlock &right) const
+ {
+ return (type_name == right.type_name && name == right.name);
+ }
+};
+
+enum MSLTextureSamplerAccess {
+ TEXTURE_ACCESS_NONE = 0,
+ TEXTURE_ACCESS_SAMPLE,
+ TEXTURE_ACCESS_READ,
+ TEXTURE_ACCESS_WRITE,
+ TEXTURE_ACCESS_READWRITE,
+};
+
+struct MSLTextureSampler {
+ ShaderStage stage;
+ shader::ImageType type;
+ std::string name;
+ MSLTextureSamplerAccess access;
+ uint location;
+
+ eGPUTextureType get_texture_binding_type() const;
+
+ void resolve_binding_indices();
+
+ MSLTextureSampler(ShaderStage in_stage,
+ shader::ImageType in_sampler_type,
+ std::string in_sampler_name,
+ MSLTextureSamplerAccess in_access,
+ uint in_location)
+ : stage(in_stage),
+ type(in_sampler_type),
+ name(in_sampler_name),
+ access(in_access),
+ location(in_location)
+ {
+ }
+
+ bool operator==(const MSLTextureSampler &right) const
+ {
+ /* We do not compare stage as we want to avoid duplication of resources used across multiple
+ * stages. */
+ return (type == right.type && name == right.name && access == right.access);
+ }
+
+ std::string get_msl_access_str() const
+ {
+ switch (access) {
+ case TEXTURE_ACCESS_SAMPLE:
+ return "access::sample";
+ case TEXTURE_ACCESS_READ:
+ return "access::read";
+ case TEXTURE_ACCESS_WRITE:
+ return "access::write";
+ case TEXTURE_ACCESS_READWRITE:
+ return "access::read_write";
+ default:
+ BLI_assert(false);
+ return "";
+ }
+ return "";
+ }
+
+ /* Get typestring for wrapped texture class members.
+ * wrapper struct type contains combined texture and sampler, templated
+ * against the texture type.
+ * See `COMBINED_SAMPLER_TYPE` in `mtl_shader_defines.msl`. */
+ std::string get_msl_typestring_wrapper(bool is_addr) const
+ {
+ std::string str;
+ str = this->get_msl_wrapper_type_str() + "<" + this->get_msl_return_type_str() + "," +
+ this->get_msl_access_str() + ">" + ((is_addr) ? "* " : " ") + this->name;
+ return str;
+ }
+
+ /* Get raw texture typestring -- used in entry-point function argument table. */
+ std::string get_msl_typestring(bool is_addr) const
+ {
+ std::string str;
+ str = this->get_msl_texture_type_str() + "<" + this->get_msl_return_type_str() + "," +
+ this->get_msl_access_str() + ">" + ((is_addr) ? "* " : " ") + this->name;
+ return str;
+ }
+
+ std::string get_msl_return_type_str() const;
+ std::string get_msl_texture_type_str() const;
+ std::string get_msl_wrapper_type_str() const;
+};
+
+struct MSLVertexInputAttribute {
+ /* layout_location of -1 means unspecified and will
+ * be populated manually. */
+ int layout_location;
+ shader::Type type;
+ std::string name;
+
+ bool operator==(const MSLVertexInputAttribute &right) const
+ {
+ return (layout_location == right.layout_location && type == right.type && name == right.name);
+ }
+};
+
+struct MSLVertexOutputAttribute {
+ std::string type;
+ std::string name;
+ /* Instance name specified if attributes belong to a struct. */
+ std::string instance_name;
+ /* Interpolation qualifier can be any of smooth (default), flat, no_perspective. */
+ std::string interpolation_qualifier;
+ bool is_array;
+ int array_elems;
+
+ bool operator==(const MSLVertexOutputAttribute &right) const
+ {
+ return (type == right.type && name == right.name &&
+ interpolation_qualifier == right.interpolation_qualifier &&
+ is_array == right.is_array && array_elems == right.array_elems);
+ }
+ std::string get_mtl_interpolation_qualifier() const
+ {
+ if (interpolation_qualifier == "" || interpolation_qualifier == "smooth") {
+ return "";
+ }
+ else if (interpolation_qualifier == "flat") {
+ return " [[flat]]";
+ }
+ else if (interpolation_qualifier == "noperspective") {
+ return " [[center_no_perspective]]";
+ }
+ return "";
+ }
+};
+
+struct MSLFragmentOutputAttribute {
+ /* Explicit output binding location N for [[color(N)]] -1 = unspecified. */
+ int layout_location;
+ /* Output index for dual source blending. -1 = unspecified. */
+ int layout_index;
+ shader::Type type;
+ std::string name;
+
+ bool operator==(const MSLFragmentOutputAttribute &right) const
+ {
+ return (layout_location == right.layout_location && type == right.type && name == right.name &&
+ layout_index == right.layout_index);
+ }
+};
+
+class MSLGeneratorInterface {
+ static char *msl_patch_default;
+
+ public:
+ /** Shader stage input/output binding information.
+ * Derived from shader source reflection or GPUShaderCreateInfo. */
+ blender::Vector<MSLUniformBlock> uniform_blocks;
+ blender::Vector<MSLUniform> uniforms;
+ blender::Vector<MSLTextureSampler> texture_samplers;
+ blender::Vector<MSLVertexInputAttribute> vertex_input_attributes;
+ blender::Vector<MSLVertexOutputAttribute> vertex_output_varyings;
+ /* Should match vertex outputs, but defined separately as
+ * some shader permutations will not utilize all inputs/outputs.
+ * Final shader uses the intersection between the two sets. */
+ blender::Vector<MSLVertexOutputAttribute> fragment_input_varyings;
+ blender::Vector<MSLFragmentOutputAttribute> fragment_outputs;
+ /* Transform feedback interface. */
+ blender::Vector<MSLVertexOutputAttribute> vertex_output_varyings_tf;
+ /* Clip Distances. */
+ blender::Vector<std::string> clip_distances;
+
+ /** GL Global usage. */
+ /* Whether GL position is used, or an alternative vertex output should be the default. */
+ bool uses_gl_Position;
+ /* Whether gl_FragColor is used, or whether an alternative fragment output
+ * should be the default. */
+ bool uses_gl_FragColor;
+ /* Whether gl_PointCoord is used in the fragment shader. If so,
+ * we define float2 gl_PointCoord [[point_coord]]. */
+ bool uses_gl_PointCoord;
+ /* Writes out to gl_PointSize in the vertex shader output. */
+ bool uses_gl_PointSize;
+ bool uses_gl_VertexID;
+ bool uses_gl_InstanceID;
+ bool uses_gl_BaseInstanceARB;
+ bool uses_gl_FrontFacing;
+ /* Sets the output render target array index when using multilayered rendering. */
+ bool uses_gl_FragDepth;
+ bool uses_mtl_array_index_;
+ bool uses_transform_feedback;
+ bool uses_barycentrics;
+
+ /* Parameters. */
+ shader::DepthWrite depth_write;
+
+ /* Shader buffer bind indices for argument buffers. */
+ int sampler_argument_buffer_bind_index[2] = {-1, -1};
+
+ /*** SSBO Vertex fetch mode. ***/
+ /* Indicates whether to pass in Vertex Buffer's as a regular buffers instead of using vertex
+ * assembly in the PSO descriptor. Enabled with special pragma. */
+ bool uses_ssbo_vertex_fetch_mode;
+
+ private:
+ /* Parent shader instance. */
+ MTLShader &parent_shader_;
+
+ /* If prepared from Create info. */
+ const shader::ShaderCreateInfo *create_info_;
+
+ public:
+ MSLGeneratorInterface(MTLShader &shader) : parent_shader_(shader){};
+
+ /** Prepare MSLGeneratorInterface from create-info. **/
+ void prepare_from_createinfo(const shader::ShaderCreateInfo *info);
+
+ /* When SSBO Vertex Fetch mode is used, uniforms are used to pass on the required information
+ * about vertex attribute bindings, in order to perform manual vertex assembly and random-access
+ * vertex lookup throughout the bound VBOs.
+ *
+ * Some parameters are global for the shader, others change with the currently bound
+ * VertexBuffers, and their format, as they do with regular GPUBatch's.
+ *
+ * (Where ##attr is the attributes name)
+ * uniform_ssbo_stride_##attr -- Representing the stride between elements of attribute(attr)
+ * uniform_ssbo_offset_##attr -- Representing the base offset within the vertex
+ * uniform_ssbo_fetchmode_##attr -- Whether using per-vertex fetch or per-instance fetch
+ * (0=vert, 1=inst) uniform_ssbo_vbo_id_##attr -- index of the vertex buffer within which the
+ * data for this attribute is contained uniform_ssbo_type_##attr - The type of data in the
+ * currently bound buffer -- Could be a mismatch with the Officially reported type. */
+ void prepare_ssbo_vertex_fetch_uniforms();
+
+ /* Samplers. */
+ bool use_argument_buffer_for_samplers() const;
+ uint32_t num_samplers_for_stage(ShaderStage stage) const;
+
+ /* Returns the bind index, relative to MTL_uniform_buffer_base_index. */
+ uint32_t get_sampler_argument_buffer_bind_index(ShaderStage stage);
+
+ /* Code generation utility functions. */
+ std::string generate_msl_uniform_structs(ShaderStage shader_stage);
+ std::string generate_msl_vertex_in_struct();
+ std::string generate_msl_vertex_out_struct(ShaderStage shader_stage);
+ std::string generate_msl_vertex_transform_feedback_out_struct(ShaderStage shader_stage);
+ std::string generate_msl_fragment_out_struct();
+ std::string generate_msl_vertex_inputs_string();
+ std::string generate_msl_fragment_inputs_string();
+ std::string generate_msl_vertex_entry_stub();
+ std::string generate_msl_fragment_entry_stub();
+ std::string generate_msl_global_uniform_population(ShaderStage stage);
+ std::string generate_ubo_block_macro_chain(MSLUniformBlock block);
+ std::string generate_msl_uniform_block_population(ShaderStage stage);
+ std::string generate_msl_vertex_attribute_input_population();
+ std::string generate_msl_vertex_output_population();
+ std::string generate_msl_vertex_output_tf_population();
+ std::string generate_msl_fragment_input_population();
+ std::string generate_msl_fragment_output_population();
+ std::string generate_msl_uniform_undefs(ShaderStage stage);
+ std::string generate_ubo_block_undef_chain(ShaderStage stage);
+ std::string generate_msl_texture_vars(ShaderStage shader_stage);
+ void generate_msl_textures_input_string(std::stringstream &out, ShaderStage stage);
+ void generate_msl_uniforms_input_string(std::stringstream &out, ShaderStage stage);
+
+ /* Location is not always specified, so this will resolve outstanding locations. */
+ void resolve_input_attribute_locations();
+ void resolve_fragment_output_locations();
+
+ /* Create shader interface for converted GLSL shader. */
+ MTLShaderInterface *bake_shader_interface(const char *name);
+
+ /* Fetch combined shader source header. */
+ char *msl_patch_default_get();
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("MSLGeneratorInterface");
+};
+
+inline std::string get_stage_class_name(ShaderStage stage)
+{
+ switch (stage) {
+ case ShaderStage::VERTEX:
+ return "MTLShaderVertexImpl";
+ case ShaderStage::FRAGMENT:
+ return "MTLShaderFragmentImpl";
+ default:
+ BLI_assert_unreachable();
+ return "";
+ }
+ return "";
+}
+
+inline bool is_builtin_type(std::string type)
+{
+ /* Add Types as needed. */
+ /* TODO(Metal): Consider replacing this with a switch and constexpr hash and switch.
+ * Though most efficient and maintainable approach to be determined. */
+ static std::map<std::string, eMTLDataType> glsl_builtin_types = {
+ {"float", MTL_DATATYPE_FLOAT},
+ {"vec2", MTL_DATATYPE_FLOAT2},
+ {"vec3", MTL_DATATYPE_FLOAT3},
+ {"vec4", MTL_DATATYPE_FLOAT4},
+ {"int", MTL_DATATYPE_INT},
+ {"ivec2", MTL_DATATYPE_INT2},
+ {"ivec3", MTL_DATATYPE_INT3},
+ {"ivec4", MTL_DATATYPE_INT4},
+ {"uint32_t", MTL_DATATYPE_UINT},
+ {"uvec2", MTL_DATATYPE_UINT2},
+ {"uvec3", MTL_DATATYPE_UINT3},
+ {"uvec4", MTL_DATATYPE_UINT4},
+ {"mat3", MTL_DATATYPE_FLOAT3x3},
+ {"mat4", MTL_DATATYPE_FLOAT4x4},
+ {"bool", MTL_DATATYPE_INT},
+ {"uchar", MTL_DATATYPE_UCHAR},
+ {"uchar2", MTL_DATATYPE_UCHAR2},
+ {"uchar2", MTL_DATATYPE_UCHAR3},
+ {"uchar4", MTL_DATATYPE_UCHAR4},
+ {"vec3_1010102_Unorm", MTL_DATATYPE_UINT1010102_NORM},
+ {"vec3_1010102_Inorm", MTL_DATATYPE_INT1010102_NORM},
+ };
+ return (glsl_builtin_types.find(type) != glsl_builtin_types.end());
+}
+
+inline bool is_matrix_type(const std::string &type)
+{
+ /* Matrix type support. Add types as necessary. */
+ return (type == "mat4");
+}
+
+inline bool is_matrix_type(const shader::Type &type)
+{
+ /* Matrix type support. Add types as necessary. */
+ return (type == shader::Type::MAT4 || type == shader::Type::MAT3);
+}
+
+inline int get_matrix_location_count(const std::string &type)
+{
+ /* Matrix type support. Add types as necessary. */
+ if (type == "mat4") {
+ return 4;
+ }
+ if (type == "mat3") {
+ return 3;
+ }
+ return 1;
+}
+
+inline int get_matrix_location_count(const shader::Type &type)
+{
+ /* Matrix type support. Add types as necessary. */
+ if (type == shader::Type::MAT4) {
+ return 4;
+ }
+ else if (type == shader::Type::MAT3) {
+ return 3;
+ }
+ return 1;
+}
+
+inline std::string get_matrix_subtype(const std::string &type)
+{
+ if (type == "mat4") {
+ return "vec4";
+ }
+ return type;
+}
+
+inline shader::Type get_matrix_subtype(const shader::Type &type)
+{
+ if (type == shader::Type::MAT4) {
+ return shader::Type::VEC4;
+ }
+ if (type == shader::Type::MAT3) {
+ return shader::Type::VEC3;
+ }
+ return type;
+}
+
+inline std::string get_attribute_conversion_function(bool *uses_conversion,
+ const shader::Type &type)
+{
+ /* NOTE(Metal): Add more attribute types as required. */
+ if (type == shader::Type::FLOAT) {
+ *uses_conversion = true;
+ return "internal_vertex_attribute_convert_read_float";
+ }
+ else if (type == shader::Type::VEC2) {
+ *uses_conversion = true;
+ return "internal_vertex_attribute_convert_read_float2";
+ }
+ else if (type == shader::Type::VEC3) {
+ *uses_conversion = true;
+ return "internal_vertex_attribute_convert_read_float3";
+ }
+ else if (type == shader::Type::VEC4) {
+ *uses_conversion = true;
+ return "internal_vertex_attribute_convert_read_float4";
+ }
+ *uses_conversion = false;
+ return "";
+}
+
+inline const char *to_string(const shader::PrimitiveOut &layout)
+{
+ switch (layout) {
+ case shader::PrimitiveOut::POINTS:
+ return "points";
+ case shader::PrimitiveOut::LINE_STRIP:
+ return "line_strip";
+ case shader::PrimitiveOut::TRIANGLE_STRIP:
+ return "triangle_strip";
+ default:
+ BLI_assert(false);
+ return "unknown";
+ }
+}
+
+inline const char *to_string(const shader::PrimitiveIn &layout)
+{
+ switch (layout) {
+ case shader::PrimitiveIn::POINTS:
+ return "points";
+ case shader::PrimitiveIn::LINES:
+ return "lines";
+ case shader::PrimitiveIn::LINES_ADJACENCY:
+ return "lines_adjacency";
+ case shader::PrimitiveIn::TRIANGLES:
+ return "triangles";
+ case shader::PrimitiveIn::TRIANGLES_ADJACENCY:
+ return "triangles_adjacency";
+ default:
+ BLI_assert(false);
+ return "unknown";
+ }
+}
+
+inline const char *to_string(const shader::Interpolation &interp)
+{
+ switch (interp) {
+ case shader::Interpolation::SMOOTH:
+ return "smooth";
+ case shader::Interpolation::FLAT:
+ return "flat";
+ case shader::Interpolation::NO_PERSPECTIVE:
+ return "noperspective";
+ default:
+ BLI_assert(false);
+ return "unkown";
+ }
+}
+
+inline const char *to_string_msl(const shader::Interpolation &interp)
+{
+ switch (interp) {
+ case shader::Interpolation::SMOOTH:
+ return "[[smooth]]";
+ case shader::Interpolation::FLAT:
+ return "[[flat]]";
+ case shader::Interpolation::NO_PERSPECTIVE:
+ return "[[center_no_perspective]]";
+ default:
+ return "";
+ }
+}
+
+inline const char *to_string(const shader::Type &type)
+{
+ switch (type) {
+ case shader::Type::FLOAT:
+ return "float";
+ case shader::Type::VEC2:
+ return "vec2";
+ case shader::Type::VEC3:
+ return "vec3";
+ case shader::Type::VEC3_101010I2:
+ return "vec3_1010102_Inorm";
+ case shader::Type::VEC4:
+ return "vec4";
+ case shader::Type::MAT3:
+ return "mat3";
+ case shader::Type::MAT4:
+ return "mat4";
+ case shader::Type::UINT:
+ return "uint32_t";
+ case shader::Type::UVEC2:
+ return "uvec2";
+ case shader::Type::UVEC3:
+ return "uvec3";
+ case shader::Type::UVEC4:
+ return "uvec4";
+ case shader::Type::INT:
+ return "int";
+ case shader::Type::IVEC2:
+ return "ivec2";
+ case shader::Type::IVEC3:
+ return "ivec3";
+ case shader::Type::IVEC4:
+ return "ivec4";
+ case shader::Type::BOOL:
+ return "bool";
+ case shader::Type::UCHAR:
+ return "uchar";
+ case shader::Type::UCHAR2:
+ return "uchar2";
+ case shader::Type::UCHAR3:
+ return "uchar3";
+ case shader::Type::UCHAR4:
+ return "uchar4";
+ case shader::Type::CHAR:
+ return "char";
+ case shader::Type::CHAR2:
+ return "char2";
+ case shader::Type::CHAR3:
+ return "char3";
+ case shader::Type::CHAR4:
+ return "char4";
+ default:
+ BLI_assert(false);
+ return "unkown";
+ }
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_shader_generator.mm b/source/blender/gpu/metal/mtl_shader_generator.mm
new file mode 100644
index 00000000000..977e97dbd82
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_shader_generator.mm
@@ -0,0 +1,2980 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "BKE_global.h"
+
+#include "BLI_string.h"
+
+#include "BLI_string.h"
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <mutex>
+#include <regex>
+#include <sstream>
+#include <string>
+
+#include <cstring>
+
+#include "GPU_platform.h"
+#include "GPU_vertex_format.h"
+
+#include "gpu_shader_dependency_private.h"
+
+#include "mtl_common.hh"
+#include "mtl_context.hh"
+#include "mtl_debug.hh"
+#include "mtl_shader.hh"
+#include "mtl_shader_generator.hh"
+#include "mtl_shader_interface.hh"
+#include "mtl_texture.hh"
+
+extern char datatoc_mtl_shader_defines_msl[];
+extern char datatoc_mtl_shader_shared_h[];
+
+using namespace blender;
+using namespace blender::gpu;
+using namespace blender::gpu::shader;
+
+namespace blender::gpu {
+
+char *MSLGeneratorInterface::msl_patch_default = nullptr;
+
+/* -------------------------------------------------------------------- */
+/** \name Shader Translation utility functions.
+ * \{ */
+
+static eMTLDataType to_mtl_type(Type type)
+{
+ switch (type) {
+ case Type::FLOAT:
+ return MTL_DATATYPE_FLOAT;
+ case Type::VEC2:
+ return MTL_DATATYPE_FLOAT2;
+ case Type::VEC3:
+ return MTL_DATATYPE_FLOAT3;
+ case Type::VEC4:
+ return MTL_DATATYPE_FLOAT4;
+ case Type::MAT3:
+ return MTL_DATATYPE_FLOAT3x3;
+ case Type::MAT4:
+ return MTL_DATATYPE_FLOAT4x4;
+ case Type::UINT:
+ return MTL_DATATYPE_UINT;
+ case Type::UVEC2:
+ return MTL_DATATYPE_UINT2;
+ case Type::UVEC3:
+ return MTL_DATATYPE_UINT3;
+ case Type::UVEC4:
+ return MTL_DATATYPE_UINT4;
+ case Type::INT:
+ return MTL_DATATYPE_INT;
+ case Type::IVEC2:
+ return MTL_DATATYPE_INT2;
+ case Type::IVEC3:
+ return MTL_DATATYPE_INT3;
+ case Type::IVEC4:
+ return MTL_DATATYPE_INT4;
+ case Type::VEC3_101010I2:
+ return MTL_DATATYPE_INT1010102_NORM;
+ case Type::BOOL:
+ return MTL_DATATYPE_BOOL;
+ case Type::UCHAR:
+ return MTL_DATATYPE_UCHAR;
+ case Type::UCHAR2:
+ return MTL_DATATYPE_UCHAR2;
+ case Type::UCHAR3:
+ return MTL_DATATYPE_UCHAR3;
+ case Type::UCHAR4:
+ return MTL_DATATYPE_UCHAR4;
+ case Type::CHAR:
+ return MTL_DATATYPE_CHAR;
+ case Type::CHAR2:
+ return MTL_DATATYPE_CHAR2;
+ case Type::CHAR3:
+ return MTL_DATATYPE_CHAR3;
+ case Type::CHAR4:
+ return MTL_DATATYPE_CHAR4;
+ default: {
+ BLI_assert_msg(false, "Unexpected data type");
+ }
+ }
+ return MTL_DATATYPE_FLOAT;
+}
+
+static std::regex remove_non_numeric_characters("[^0-9]");
+
+#ifndef NDEBUG
+static void remove_multiline_comments_func(std::string &str)
+{
+ char *current_str_begin = &*str.begin();
+ char *current_str_end = &*str.end();
+
+ bool is_inside_comment = false;
+ for (char *c = current_str_begin; c < current_str_end; c++) {
+ if (is_inside_comment) {
+ if ((*c == '*') && (c < current_str_end - 1) && (*(c + 1) == '/')) {
+ is_inside_comment = false;
+ *c = ' ';
+ *(c + 1) = ' ';
+ }
+ else {
+ *c = ' ';
+ }
+ }
+ else {
+ if ((*c == '/') && (c < current_str_end - 1) && (*(c + 1) == '*')) {
+ is_inside_comment = true;
+ *c = ' ';
+ }
+ }
+ }
+}
+
+static void remove_singleline_comments_func(std::string &str)
+{
+ char *current_str_begin = &*str.begin();
+ char *current_str_end = &*str.end();
+
+ bool is_inside_comment = false;
+ for (char *c = current_str_begin; c < current_str_end; c++) {
+ if (is_inside_comment) {
+ if (*c == '\n') {
+ is_inside_comment = false;
+ }
+ else {
+ *c = ' ';
+ }
+ }
+ else {
+ if ((*c == '/') && (c < current_str_end - 1) && (*(c + 1) == '/')) {
+ is_inside_comment = true;
+ *c = ' ';
+ }
+ }
+ }
+}
+#endif
+
+static bool is_program_word(const char *chr, int *len)
+{
+ int numchars = 0;
+ for (const char *c = chr; *c != '\0'; c++) {
+ char ch = *c;
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
+ (numchars > 0 && ch >= '0' && ch <= '9') || ch == '_') {
+ numchars++;
+ }
+ else {
+ *len = numchars;
+ return (numchars > 0);
+ }
+ }
+ *len = numchars;
+ return true;
+}
+
+/**
+ * Replace function parameter patterns containing:
+ * `out vec3 somevar` with `THD vec3&somevar`.
+ * which enables pass by reference via resolved macro:
+ * `thread vec3& somevar`.
+ */
+static void replace_outvars(std::string &str)
+{
+ char *current_str_begin = &*str.begin();
+ char *current_str_end = &*str.end();
+
+ for (char *c = current_str_begin + 2; c < current_str_end - 6; c++) {
+ char *start = c;
+ if (strncmp(c, "out ", 4) == 0) {
+ if (strncmp(c - 2, "in", 2) == 0) {
+ start = c - 2;
+ }
+
+ /* Check that the following are words. */
+ int len1, len2;
+ char *word_base1 = c + 4;
+ char *word_base2 = word_base1;
+
+ if (is_program_word(word_base1, &len1) && (*(word_base1 + len1) == ' ')) {
+ word_base2 = word_base1 + len1 + 1;
+ if (is_program_word(word_base2, &len2)) {
+ /* Match found. */
+ bool is_array = (*(word_base2 + len2) == '[');
+
+ /* Generate out-variable pattern of form `THD type&var` from original `out vec4 var`. */
+ *start = 'T';
+ *(start + 1) = 'H';
+ *(start + 2) = 'D';
+ for (char *clear = start + 3; clear < c + 4; clear++) {
+ *clear = ' ';
+ }
+ *(word_base2 - 1) = is_array ? '*' : '&';
+ }
+ }
+ }
+ }
+}
+
+static void replace_array_initializers_func(std::string &str)
+{
+ char *current_str_begin = &*str.begin();
+ char *current_str_end = &*str.end();
+
+ for (char *c = current_str_begin; c < current_str_end - 6; c++) {
+ char *base_scan = c;
+ int typelen = 0;
+
+ if (is_program_word(c, &typelen) && *(c + typelen) == '[') {
+
+ char *array_len_start = c + typelen + 1;
+ c = array_len_start;
+ char *closing_square_brace = strchr(c, ']');
+ if (closing_square_brace != nullptr) {
+ c = closing_square_brace;
+ char *first_bracket = c + 1;
+ if (*first_bracket == '(') {
+ c += 1;
+ char *semi_colon = strchr(c, ';');
+ if (semi_colon != nullptr && *(semi_colon - 1) == ')') {
+ char *closing_bracket = semi_colon - 1;
+
+ /* Resolve to MSL-compatible array formatting. */
+ *first_bracket = '{';
+ *closing_bracket = '}';
+ for (char *clear = base_scan; clear <= closing_square_brace; clear++) {
+ *clear = ' ';
+ }
+ }
+ }
+ }
+ else {
+ return;
+ }
+ }
+ }
+}
+
+#ifndef NDEBUG
+
+static bool balanced_braces(char *current_str_begin, char *current_str_end)
+{
+ int nested_bracket_depth = 0;
+ for (char *c = current_str_begin; c < current_str_end; c++) {
+ /* Track whether we are in global scope. */
+ if (*c == '{' || *c == '[' || *c == '(') {
+ nested_bracket_depth++;
+ continue;
+ }
+ if (*c == '}' || *c == ']' || *c == ')') {
+ nested_bracket_depth--;
+ continue;
+ }
+ }
+ return (nested_bracket_depth == 0);
+}
+
+/**
+ * Certain Constants (such as arrays, or pointer types) declared in Global-scope
+ * end up being initialized per shader thread, resulting in high
+ * register pressure within the shader.
+ * Here we flag occurrences of these constants such that
+ * they can be moved to a place where this is not a problem.
+ *
+ * Constants declared within function-scope do not exhibit this problem.
+ */
+static void extract_global_scope_constants(std::string &str, std::stringstream &global_scope_out)
+{
+ char *current_str_begin = &*str.begin();
+ char *current_str_end = &*str.end();
+
+ int nested_bracket_depth = 0;
+ for (char *c = current_str_begin; c < current_str_end - 6; c++) {
+ /* Track whether we are in global scope. */
+ if (*c == '{' || *c == '[' || *c == '(') {
+ nested_bracket_depth++;
+ continue;
+ }
+ if (*c == '}' || *c == ']' || *c == ')') {
+ nested_bracket_depth--;
+ BLI_assert(nested_bracket_depth >= 0);
+ continue;
+ }
+
+ /* Check For global const declarations */
+ if (nested_bracket_depth == 0 && strncmp(c, "const ", 6) == 0 &&
+ strncmp(c, "const constant ", 15) != 0) {
+ char *c_expr_end = strstr(c, ";");
+ if (c_expr_end != nullptr && balanced_braces(c, c_expr_end)) {
+ MTL_LOG_INFO(
+ "[PERFORMANCE WARNING] Global scope constant expression found - These get allocated "
+ "per-thread in METAL - Best to use Macro's or uniforms to avoid overhead: '%.*s'\n",
+ (int)(c_expr_end + 1 - c),
+ c);
+
+ /* Jump ptr forward as we know we remain in global scope. */
+ c = c_expr_end - 1;
+ continue;
+ }
+ }
+ }
+}
+#endif
+
+static bool extract_ssbo_pragma_info(const MTLShader *shader,
+ const MSLGeneratorInterface &,
+ const std::string &in_vertex_src,
+ MTLPrimitiveType &out_prim_tye,
+ uint32_t &out_num_output_verts)
+{
+ /* SSBO Vertex-fetch parameter extraction. */
+ static std::regex use_ssbo_fetch_mode_find(
+ "#pragma "
+ "USE_SSBO_VERTEX_FETCH\\(\\s*(TriangleList|LineList|\\w+)\\s*,\\s*([0-9]+)\\s*\\)");
+
+ /* Perform regex search if pragma string found. */
+ std::smatch vertex_shader_ssbo_flags;
+ bool uses_ssbo_fetch = false;
+ if (in_vertex_src.find("#pragma USE_SSBO_VERTEX_FETCH") != std::string::npos) {
+ uses_ssbo_fetch = std::regex_search(
+ in_vertex_src, vertex_shader_ssbo_flags, use_ssbo_fetch_mode_find);
+ }
+ if (uses_ssbo_fetch) {
+ /* Extract Expected output primitive type:
+ * #pragma USE_SSBO_VERTEX_FETCH(Output Prim Type, num output vertices per input primitive)
+ *
+ * Supported Primitive Types (Others can be added if needed, but List types for efficiency):
+ * - TriangleList
+ * - LineList
+ *
+ * Output vertex count is determined by calculating the number of input primitives, and
+ * multiplying that by the number of output vertices specified. */
+ std::string str_output_primitive_type = vertex_shader_ssbo_flags[1].str();
+ std::string str_output_prim_count_per_vertex = vertex_shader_ssbo_flags[2].str();
+
+ /* Ensure output primitive type is valid. */
+ if (str_output_primitive_type == "TriangleList") {
+ out_prim_tye = MTLPrimitiveTypeTriangle;
+ }
+ else if (str_output_primitive_type == "LineList") {
+ out_prim_tye = MTLPrimitiveTypeLine;
+ }
+ else {
+ MTL_LOG_ERROR("Unsupported output primitive type for SSBO VERTEX FETCH MODE. Shader: %s",
+ shader->name_get());
+ return false;
+ }
+
+ /* Assign output num vertices per primitive. */
+ out_num_output_verts = std::stoi(
+ std::regex_replace(str_output_prim_count_per_vertex, remove_non_numeric_characters, ""));
+ BLI_assert(out_num_output_verts > 0);
+ return true;
+ }
+
+ /* SSBO Vertex fetchmode not used. */
+ return false;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name MTLShader builtin shader generation utilities.
+ * \{ */
+
+static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
+{
+ switch (res.bind_type) {
+ case ShaderCreateInfo::Resource::BindType::SAMPLER:
+ break;
+ case ShaderCreateInfo::Resource::BindType::IMAGE:
+ break;
+ case ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER: {
+ int64_t array_offset = res.uniformbuf.name.find_first_of("[");
+ if (array_offset == -1) {
+ /* Create local class member as constant pointer reference to bound UBO buffer.
+ * Given usage within a shader follows ubo_name.ubo_element syntax, we can
+ * dereference the pointer as the compiler will optimize this data fetch.
+ * To do this, we also give the UBO name a post-fix of `_local` to avoid
+ * macro accessor collisions. */
+ os << "constant " << res.uniformbuf.type_name << " *" << res.uniformbuf.name
+ << "_local;\n";
+ os << "#define " << res.uniformbuf.name << " (*" << res.uniformbuf.name << "_local)\n";
+ }
+ else {
+ /* For arrays, we can directly provide the constant access pointer, as the array
+ * syntax will de-reference this at the correct fetch index. */
+ StringRef name_no_array = StringRef(res.uniformbuf.name.c_str(), array_offset);
+ os << "constant " << res.uniformbuf.type_name << " *" << name_no_array << ";\n";
+ }
+ break;
+ }
+ case ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
+ break;
+ }
+}
+
+std::string MTLShader::resources_declare(const ShaderCreateInfo &info) const
+{
+ /* NOTE(Metal): We only use the upfront preparation functions to populate members which
+ * would exist in the original non-create-info variant.
+ *
+ * This function is only used to generate resource structs.
+ * Global-scope handles for Uniforms, UBOs, textures and samplers
+ * are generated during class-wrapper construction in `generate_msl_from_glsl`. */
+ std::stringstream ss;
+
+ /* Generate resource stubs for UBOs and textures. */
+ ss << "\n/* Pass Resources. */\n";
+ for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
+ print_resource(ss, res);
+ }
+ ss << "\n/* Batch Resources. */\n";
+ for (const ShaderCreateInfo::Resource &res : info.batch_resources_) {
+ print_resource(ss, res);
+ }
+ /* NOTE: Push constant uniform data is generated during `generate_msl_from_glsl`
+ * as the generated output is needed for all paths. This includes generation
+ * of the push constant data structure (struct PushConstantBlock).
+ * As all shader generation paths require creation of this. */
+ return ss.str();
+}
+
+std::string MTLShader::vertex_interface_declare(const shader::ShaderCreateInfo &info) const
+{
+ /* NOTE(Metal): We only use the upfront preparation functions to populate members which
+ * would exist in the original non-create-info variant.
+ *
+ * Here we generate the variables within class wrapper scope to allow reading of
+ * input attributes by the main code. */
+ std::stringstream ss;
+ ss << "\n/* Vertex Inputs. */\n";
+ for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
+ ss << to_string(attr.type) << " " << attr.name << ";\n";
+ }
+ return ss.str();
+}
+
+std::string MTLShader::fragment_interface_declare(const shader::ShaderCreateInfo &info) const
+{
+ /* For shaders generated from MSL, the fragment-output struct is generated as part of the entry
+ * stub during glsl->MSL conversion in `generate_msl_from_glsl`.
+ * Here, we can instead generate the global-scope variables which will be populated during
+ * execution.
+ *
+ * NOTE: The output declaration for location and blend index are generated in the entry-point
+ * struct. This is simply a mirror class member which stores the value during main shader body
+ * execution. */
+ std::stringstream ss;
+ ss << "\n/* Fragment Outputs. */\n";
+ for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) {
+ ss << to_string(output.type) << " " << output.name << ";\n";
+ }
+ ss << "\n";
+
+ return ss.str();
+}
+
+std::string MTLShader::MTLShader::geometry_interface_declare(
+ const shader::ShaderCreateInfo &info) const
+{
+ BLI_assert_msg(false, "Geometry shading unsupported by Metal");
+ return "";
+}
+
+std::string MTLShader::geometry_layout_declare(const shader::ShaderCreateInfo &info) const
+{
+ BLI_assert_msg(false, "Geometry shading unsupported by Metal");
+ return "";
+}
+
+std::string MTLShader::compute_layout_declare(const ShaderCreateInfo &info) const
+{
+ /* TODO(Metal): Metal compute layout pending compute support. */
+ BLI_assert_msg(false, "Compute shaders unsupported by Metal");
+ return "";
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Shader Translation.
+ * \{ */
+
+char *MSLGeneratorInterface::msl_patch_default_get()
+{
+ if (msl_patch_default != nullptr) {
+ return msl_patch_default;
+ }
+
+ std::stringstream ss_patch;
+ ss_patch << datatoc_mtl_shader_shared_h << std::endl;
+ ss_patch << datatoc_mtl_shader_defines_msl << std::endl;
+ size_t len = strlen(ss_patch.str().c_str());
+
+ msl_patch_default = (char *)malloc(len * sizeof(char));
+ strcpy(msl_patch_default, ss_patch.str().c_str());
+ return msl_patch_default;
+}
+
+bool MTLShader::generate_msl_from_glsl(const shader::ShaderCreateInfo *info)
+{
+ /* Verify if create-info is available.
+ * NOTE(Metal): For now, only support creation from CreateInfo.
+ * If needed, we can perform source translation without this using
+ * manual reflection. */
+ bool uses_create_info = info != nullptr;
+ if (!uses_create_info) {
+ MTL_LOG_WARNING("Unable to compile shader %p '%s' as no create-info was provided!\n",
+ this,
+ this->name_get());
+ valid_ = false;
+ return false;
+ }
+
+ /* #MSLGeneratorInterface is a class populated to describe all parameters, resources, bindings
+ * and features used by the source GLSL shader. This information is then used to generate the
+ * appropriate Metal entry points and perform any required source translation. */
+ MSLGeneratorInterface msl_iface(*this);
+ BLI_assert(shd_builder_ != nullptr);
+
+ /* Populate #MSLGeneratorInterface from Create-Info.
+ * NOTE: this is a separate path as #MSLGeneratorInterface can also be manually populated
+ * from parsing, if support for shaders without create-info is required. */
+ msl_iface.prepare_from_createinfo(info);
+
+ /* Verify Source sizes are greater than zero. */
+ BLI_assert(shd_builder_->glsl_vertex_source_.size() > 0);
+ if (!msl_iface.uses_transform_feedback) {
+ BLI_assert(shd_builder_->glsl_fragment_source_.size() > 0);
+ }
+
+ /** Determine use of Transform Feedback. **/
+ msl_iface.uses_transform_feedback = false;
+ if (transform_feedback_type_ != GPU_SHADER_TFB_NONE) {
+ /* Ensure #TransformFeedback is configured correctly. */
+ BLI_assert(tf_output_name_list_.size() > 0);
+ msl_iface.uses_transform_feedback = true;
+ }
+
+ /* Concatenate msl_shader_defines to provide functionality mapping
+ * from GLSL to MSL. Also include additional GPU defines for
+ * optional high-level feature support. */
+ const std::string msl_defines_string =
+ "#define GPU_ARB_texture_cube_map_array 1\n\
+ #define GPU_ARB_shader_draw_parameters 1\n\
+ #define GPU_ARB_texture_gather 1\n";
+
+ shd_builder_->glsl_vertex_source_ = msl_defines_string + shd_builder_->glsl_vertex_source_;
+ if (!msl_iface.uses_transform_feedback) {
+ shd_builder_->glsl_fragment_source_ = msl_defines_string + shd_builder_->glsl_fragment_source_;
+ }
+
+ /* Extract SSBO usage information from shader pragma:
+ *
+ * #pragma USE_SSBO_VERTEX_FETCH(Output Prim Type, num output vertices per input primitive)
+ *
+ * This will determine whether SSBO-vertex-fetch
+ * mode is used for this shader. Returns true if used, and populates output reference
+ * values with the output prim type and output number of vertices. */
+ MTLPrimitiveType vertex_fetch_ssbo_output_prim_type = MTLPrimitiveTypeTriangle;
+ uint32_t vertex_fetch_ssbo_num_output_verts = 0;
+ msl_iface.uses_ssbo_vertex_fetch_mode = extract_ssbo_pragma_info(
+ this,
+ msl_iface,
+ shd_builder_->glsl_vertex_source_,
+ vertex_fetch_ssbo_output_prim_type,
+ vertex_fetch_ssbo_num_output_verts);
+
+ if (msl_iface.uses_ssbo_vertex_fetch_mode) {
+ shader_debug_printf(
+ "[Shader] SSBO VERTEX FETCH Enabled for Shader '%s' With Output primitive type: %s, "
+ "vertex count: %u\n",
+ this->name_get(),
+ output_primitive_type.c_str(),
+ vertex_fetch_ssbo_num_output_verts);
+ }
+
+ /*** Regex Commands ***/
+ /* Source cleanup and syntax replacement. */
+ static std::regex remove_excess_newlines("\\n+");
+ static std::regex replace_mat3("mat3\\s*\\(");
+
+ /* Special condition - mat3 and array constructor replacement.
+ * Also replace excessive new lines to ensure cases are not missed.
+ * NOTE(Metal): May be able to skip excess-newline removal. */
+ shd_builder_->glsl_vertex_source_ = std::regex_replace(
+ shd_builder_->glsl_vertex_source_, remove_excess_newlines, "\n");
+ shd_builder_->glsl_vertex_source_ = std::regex_replace(
+ shd_builder_->glsl_vertex_source_, replace_mat3, "MAT3(");
+ replace_array_initializers_func(shd_builder_->glsl_vertex_source_);
+
+ if (!msl_iface.uses_transform_feedback) {
+ shd_builder_->glsl_fragment_source_ = std::regex_replace(
+ shd_builder_->glsl_fragment_source_, remove_excess_newlines, "\n");
+ shd_builder_->glsl_fragment_source_ = std::regex_replace(
+ shd_builder_->glsl_fragment_source_, replace_mat3, "MAT3(");
+ replace_array_initializers_func(shd_builder_->glsl_fragment_source_);
+ }
+
+ /**** Extract usage of GL globals. ****/
+ /* NOTE(METAL): Currently still performing fallback string scan, as info->builtins_ does
+ * not always contain the usage flag. This can be removed once all appropriate create-info's
+ * have been updated. In some cases, this may incur a false positive if access is guarded
+ * behind a macro. Though in these cases, unused code paths and parameters will be
+ * optimized out by the Metal shader compiler. */
+
+ /** Identify usage of vertex-shader builtins. */
+ msl_iface.uses_gl_VertexID = bool(info->builtins_ & BuiltinBits::VERTEX_ID) ||
+ shd_builder_->glsl_vertex_source_.find("gl_VertexID") !=
+ std::string::npos;
+ msl_iface.uses_gl_InstanceID = bool(info->builtins_ & BuiltinBits::INSTANCE_ID) ||
+ shd_builder_->glsl_vertex_source_.find("gl_InstanceID") !=
+ std::string::npos ||
+ shd_builder_->glsl_vertex_source_.find("gpu_InstanceIndex") !=
+ std::string::npos ||
+ msl_iface.uses_ssbo_vertex_fetch_mode;
+
+ /* instance ID in GL is `[0, instance_count]` in metal it is
+ * `[base_instance, base_instance + instance_count]`,
+ * so we need to offset instance_ID by base instance in Metal --
+ * Thus we expose the `[[base_instance]]` attribute if instance ID is used at all. */
+ msl_iface.uses_gl_BaseInstanceARB = msl_iface.uses_gl_InstanceID ||
+ shd_builder_->glsl_vertex_source_.find(
+ "gl_BaseInstanceARB") != std::string::npos ||
+ shd_builder_->glsl_vertex_source_.find("gpu_BaseInstance") !=
+ std::string::npos;
+ msl_iface.uses_gl_Position = shd_builder_->glsl_vertex_source_.find("gl_Position") !=
+ std::string::npos;
+ msl_iface.uses_gl_PointSize = shd_builder_->glsl_vertex_source_.find("gl_PointSize") !=
+ std::string::npos;
+ msl_iface.uses_mtl_array_index_ = shd_builder_->glsl_vertex_source_.find(
+ "MTLRenderTargetArrayIndex") != std::string::npos;
+
+ /** Identify usage of fragment-shader builtins. */
+ if (!msl_iface.uses_transform_feedback) {
+ std::smatch gl_special_cases;
+ msl_iface.uses_gl_PointCoord = bool(info->builtins_ & BuiltinBits::POINT_COORD) ||
+ shd_builder_->glsl_fragment_source_.find("gl_PointCoord") !=
+ std::string::npos;
+ msl_iface.uses_barycentrics = bool(info->builtins_ & BuiltinBits::BARYCENTRIC_COORD);
+ msl_iface.uses_gl_FrontFacing = bool(info->builtins_ & BuiltinBits::FRONT_FACING) ||
+ shd_builder_->glsl_fragment_source_.find("gl_FrontFacing") !=
+ std::string::npos;
+
+ /* NOTE(Metal): If FragColor is not used, then we treat the first fragment output attachment
+ * as the primary output. */
+ msl_iface.uses_gl_FragColor = shd_builder_->glsl_fragment_source_.find("gl_FragColor") !=
+ std::string::npos;
+
+ /* NOTE(Metal): FragDepth output mode specified in create-info 'DepthWrite depth_write_'.
+ * If parsing without create-info, manual extraction will be required. */
+ msl_iface.uses_gl_FragDepth = shd_builder_->glsl_fragment_source_.find("gl_FragDepth") !=
+ std::string::npos;
+ msl_iface.depth_write = info->depth_write_;
+ }
+
+ /* Generate SSBO vertex fetch mode uniform data hooks. */
+ if (msl_iface.uses_ssbo_vertex_fetch_mode) {
+ msl_iface.prepare_ssbo_vertex_fetch_uniforms();
+ }
+
+ /* Extract gl_ClipDistances. */
+ static std::regex gl_clipdistance_find("gl_ClipDistance\\[([0-9])\\]");
+
+ std::string clip_search_str = shd_builder_->glsl_vertex_source_;
+ std::smatch vertex_clip_distances;
+
+ while (std::regex_search(clip_search_str, vertex_clip_distances, gl_clipdistance_find)) {
+ shader_debug_printf("VERTEX CLIP DISTANCES FOUND: str: %s\n",
+ vertex_clip_distances[1].str().c_str());
+ auto found = std::find(msl_iface.clip_distances.begin(),
+ msl_iface.clip_distances.end(),
+ vertex_clip_distances[1].str());
+ if (found == msl_iface.clip_distances.end()) {
+ msl_iface.clip_distances.append(vertex_clip_distances[1].str());
+ }
+ clip_search_str = vertex_clip_distances.suffix();
+ }
+ shd_builder_->glsl_vertex_source_ = std::regex_replace(
+ shd_builder_->glsl_vertex_source_, gl_clipdistance_find, "gl_ClipDistance_$1");
+
+ /* Replace 'out' attribute on function parameters with pass-by-reference. */
+ replace_outvars(shd_builder_->glsl_vertex_source_);
+ if (!msl_iface.uses_transform_feedback) {
+ replace_outvars(shd_builder_->glsl_fragment_source_);
+ }
+
+ /**** METAL Shader source generation. ****/
+ /* Setup `stringstream` for populating generated MSL shader vertex/frag shaders. */
+ std::stringstream ss_vertex;
+ std::stringstream ss_fragment;
+
+ /*** Generate VERTEX Stage ***/
+ /* Conditional defines. */
+ if (msl_iface.use_argument_buffer_for_samplers()) {
+ ss_vertex << "#define USE_ARGUMENT_BUFFER_FOR_SAMPLERS 1" << std::endl;
+ ss_vertex << "#define ARGUMENT_BUFFER_NUM_SAMPLERS "
+ << msl_iface.num_samplers_for_stage(ShaderStage::VERTEX) << std::endl;
+ }
+ if (msl_iface.uses_ssbo_vertex_fetch_mode) {
+ ss_vertex << "#define MTL_SSBO_VERTEX_FETCH 1" << std::endl;
+ ss_vertex << "#define MTL_SSBO_VERTEX_FETCH_MAX_VBOS " << MTL_SSBO_VERTEX_FETCH_MAX_VBOS
+ << std::endl;
+ ss_vertex << "#define MTL_SSBO_VERTEX_FETCH_IBO_INDEX " << MTL_SSBO_VERTEX_FETCH_IBO_INDEX
+ << std::endl;
+ for (const MSLVertexInputAttribute &attr : msl_iface.vertex_input_attributes) {
+ ss_vertex << "#define SSBO_ATTR_TYPE_" << attr.name << " " << attr.type << std::endl;
+ }
+
+ /* Macro's */
+ ss_vertex << "#define "
+ "UNIFORM_SSBO_USES_INDEXED_RENDERING_STR " UNIFORM_SSBO_USES_INDEXED_RENDERING_STR
+ "\n"
+ "#define UNIFORM_SSBO_INDEX_MODE_U16_STR " UNIFORM_SSBO_INDEX_MODE_U16_STR
+ "\n"
+ "#define UNIFORM_SSBO_INPUT_PRIM_TYPE_STR " UNIFORM_SSBO_INPUT_PRIM_TYPE_STR
+ "\n"
+ "#define UNIFORM_SSBO_INPUT_VERT_COUNT_STR " UNIFORM_SSBO_INPUT_VERT_COUNT_STR
+ "\n"
+ "#define UNIFORM_SSBO_OFFSET_STR " UNIFORM_SSBO_OFFSET_STR
+ "\n"
+ "#define UNIFORM_SSBO_STRIDE_STR " UNIFORM_SSBO_STRIDE_STR
+ "\n"
+ "#define UNIFORM_SSBO_FETCHMODE_STR " UNIFORM_SSBO_FETCHMODE_STR
+ "\n"
+ "#define UNIFORM_SSBO_VBO_ID_STR " UNIFORM_SSBO_VBO_ID_STR
+ "\n"
+ "#define UNIFORM_SSBO_TYPE_STR " UNIFORM_SSBO_TYPE_STR "\n";
+ }
+
+ /* Inject common Metal header. */
+ ss_vertex << msl_iface.msl_patch_default_get() << std::endl << std::endl;
+
+#ifndef NDEBUG
+ /* Performance warning: Extract global-scope expressions.
+ * NOTE: This is dependent on stripping out comments
+ * to remove false positives. */
+ remove_multiline_comments_func(shd_builder_->glsl_vertex_source_);
+ remove_singleline_comments_func(shd_builder_->glsl_vertex_source_);
+ extract_global_scope_constants(shd_builder_->glsl_vertex_source_, ss_vertex);
+#endif
+
+ /* Generate additional shader interface struct members from create-info. */
+ for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
+
+ /* Only generate struct for ones with instance names */
+ if (!iface->instance_name.is_empty()) {
+ ss_vertex << "struct " << iface->name << " {" << std::endl;
+ for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
+ ss_vertex << to_string(inout.type) << " " << inout.name << " "
+ << to_string_msl(inout.interp) << ";" << std::endl;
+ }
+ ss_vertex << "};" << std::endl;
+ }
+ }
+
+ /* Wrap entire GLSL source inside class to create
+ * a scope within the class to enable use of global variables.
+ * e.g. global access to attributes, uniforms, UBOs, textures etc; */
+ ss_vertex << "class " << get_stage_class_name(ShaderStage::VERTEX) << " {" << std::endl;
+ ss_vertex << "public:" << std::endl;
+
+ /* Generate additional shader interface struct members from create-info. */
+ for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
+
+ bool is_inside_struct = false;
+ if (!iface->instance_name.is_empty()) {
+ /* If shader stage interface has an instance name, then it
+ * is using a struct format and as such we only need a local
+ * class member for the struct, not each element. */
+ ss_vertex << iface->name << " " << iface->instance_name << ";" << std::endl;
+ is_inside_struct = true;
+ }
+
+ /* Generate local variables, populate elems for vertex out struct gen. */
+ for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
+
+ /* Only output individual elements if they are not part of an interface struct instance. */
+ if (!is_inside_struct) {
+ ss_vertex << to_string(inout.type) << " " << inout.name << ";" << std::endl;
+ }
+
+ const char *arraystart = strstr(inout.name.c_str(), "[");
+ bool is_array = (arraystart != nullptr);
+ int array_len = (is_array) ? std::stoi(std::regex_replace(
+ arraystart, remove_non_numeric_characters, "")) :
+ 0;
+
+ /* Remove array from string name. */
+ std::string out_name = inout.name.c_str();
+ std::size_t pos = out_name.find('[');
+ if (is_array && pos != std::string::npos) {
+ out_name.resize(pos);
+ }
+
+ /* Add to vertex-output interface. */
+ msl_iface.vertex_output_varyings.append(
+ {to_string(inout.type),
+ out_name.c_str(),
+ ((is_inside_struct) ? iface->instance_name.c_str() : ""),
+ to_string(inout.interp),
+ is_array,
+ array_len});
+
+ /* Add to fragment-input interface. */
+ msl_iface.fragment_input_varyings.append(
+ {to_string(inout.type),
+ out_name.c_str(),
+ ((is_inside_struct) ? iface->instance_name.c_str() : ""),
+ to_string(inout.interp),
+ is_array,
+ array_len});
+ }
+ }
+
+ /** Generate structs from MSL Interface. **/
+ /* Generate VertexIn struct. */
+ if (!msl_iface.uses_ssbo_vertex_fetch_mode) {
+ ss_vertex << msl_iface.generate_msl_vertex_in_struct();
+ }
+ /* Generate Uniform data structs. */
+ ss_vertex << msl_iface.generate_msl_uniform_structs(ShaderStage::VERTEX);
+
+ /* Conditionally use global GL variables. */
+ if (msl_iface.uses_gl_Position) {
+ ss_vertex << "float4 gl_Position;" << std::endl;
+ }
+ if (msl_iface.uses_gl_PointSize) {
+ ss_vertex << "float gl_PointSize = 1.0;" << std::endl;
+ }
+ if (msl_iface.uses_gl_VertexID) {
+ ss_vertex << "int gl_VertexID;" << std::endl;
+ }
+ if (msl_iface.uses_gl_InstanceID) {
+ ss_vertex << "int gl_InstanceID;" << std::endl;
+ }
+ if (msl_iface.uses_gl_BaseInstanceARB) {
+ ss_vertex << "int gl_BaseInstanceARB;" << std::endl;
+ }
+ for (const int cd : IndexRange(msl_iface.clip_distances.size())) {
+ ss_vertex << "float gl_ClipDistance_" << cd << ";" << std::endl;
+ }
+
+ /* Render target array index if using multilayered rendering. */
+ if (msl_iface.uses_mtl_array_index_) {
+ ss_vertex << "int MTLRenderTargetArrayIndex = 0;" << std::endl;
+ }
+
+ /* Global vertex data pointers when using SSBO vertex fetch mode.
+ * Bound vertex buffers passed in via the entry point function
+ * are assigned to these pointers to be globally accessible
+ * from any function within the GLSL source shader. */
+ if (msl_iface.uses_ssbo_vertex_fetch_mode) {
+ ss_vertex << "constant uchar** MTL_VERTEX_DATA;" << std::endl;
+ ss_vertex << "constant ushort* MTL_INDEX_DATA_U16 = nullptr;" << std::endl;
+ ss_vertex << "constant uint32_t* MTL_INDEX_DATA_U32 = nullptr;" << std::endl;
+ }
+
+ /* Add Texture members.
+ * These members pack both a texture and a sampler into a single
+ * struct, as both are needed within texture functions.
+ * e.g. `_mtl_combined_image_sampler_2d<float, access::read>`
+ * The exact typename is generated inside `get_msl_typestring_wrapper()`. */
+ for (const MSLTextureSampler &tex : msl_iface.texture_samplers) {
+ if (bool(tex.stage & ShaderStage::VERTEX)) {
+ ss_vertex << "\tthread " << tex.get_msl_typestring_wrapper(false) << ";" << std::endl;
+ }
+ }
+ ss_vertex << std::endl;
+
+ /* Inject main GLSL source into output stream. */
+ ss_vertex << shd_builder_->glsl_vertex_source_ << std::endl;
+
+ /* Generate VertexOut and TransformFeedbackOutput structs. */
+ ss_vertex << msl_iface.generate_msl_vertex_out_struct(ShaderStage::VERTEX);
+ if (msl_iface.uses_transform_feedback) {
+ ss_vertex << msl_iface.generate_msl_vertex_transform_feedback_out_struct(ShaderStage::VERTEX);
+ }
+
+ /* Class Closing Bracket to end shader global scope. */
+ ss_vertex << "};" << std::endl;
+
+ /* Generate Vertex shader entry-point function containing resource bindings. */
+ ss_vertex << msl_iface.generate_msl_vertex_entry_stub();
+
+ /*** Generate FRAGMENT Stage. ***/
+ if (!msl_iface.uses_transform_feedback) {
+
+ /* Conditional defines. */
+ if (msl_iface.use_argument_buffer_for_samplers()) {
+ ss_fragment << "#define USE_ARGUMENT_BUFFER_FOR_SAMPLERS 1" << std::endl;
+ ss_fragment << "#define ARGUMENT_BUFFER_NUM_SAMPLERS "
+ << msl_iface.num_samplers_for_stage(ShaderStage::FRAGMENT) << std::endl;
+ }
+
+ /* Inject common Metal header. */
+ ss_fragment << msl_iface.msl_patch_default_get() << std::endl << std::endl;
+
+#ifndef NDEBUG
+ /* Performance warning: Identify global-scope expressions.
+ * These cause excessive register pressure due to global arrays being instantiated per-thread.
+ * NOTE: This is dependent on stripping out comments to remove false positives. */
+ remove_multiline_comments_func(shd_builder_->glsl_fragment_source_);
+ remove_singleline_comments_func(shd_builder_->glsl_fragment_source_);
+ extract_global_scope_constants(shd_builder_->glsl_fragment_source_, ss_fragment);
+#endif
+
+ /* Generate additional shader interface struct members from create-info. */
+ for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
+
+ /* Only generate struct for ones with instance names. */
+ if (!iface->instance_name.is_empty()) {
+ ss_fragment << "struct " << iface->name << " {" << std::endl;
+ for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
+ ss_fragment << to_string(inout.type) << " " << inout.name << ""
+ << to_string_msl(inout.interp) << ";" << std::endl;
+ }
+ ss_fragment << "};" << std::endl;
+ }
+ }
+
+ /* Wrap entire GLSL source inside class to create
+ * a scope within the class to enable use of global variables. */
+ ss_fragment << "class " << get_stage_class_name(ShaderStage::FRAGMENT) << " {" << std::endl;
+ ss_fragment << "public:" << std::endl;
+
+ /* In/out interface values */
+ /* Generate additional shader interface struct members from create-info. */
+ for (const StageInterfaceInfo *iface : info->vertex_out_interfaces_) {
+ bool is_inside_struct = false;
+ if (!iface->instance_name.is_empty()) {
+ /* Struct local variable. */
+ ss_fragment << iface->name << " " << iface->instance_name << ";" << std::endl;
+ is_inside_struct = true;
+ }
+
+ /* Generate local variables, populate elems for vertex out struct gen. */
+ for (const StageInterfaceInfo::InOut &inout : iface->inouts) {
+ /* Only output individual elements if they are not part of an interface struct instance.
+ */
+ if (!is_inside_struct) {
+ ss_fragment << to_string(inout.type) << " " << inout.name << ";" << std::endl;
+ }
+ }
+ }
+
+ /* Generate global structs */
+ ss_fragment << msl_iface.generate_msl_vertex_out_struct(ShaderStage::FRAGMENT);
+ ss_fragment << msl_iface.generate_msl_fragment_out_struct();
+ ss_fragment << msl_iface.generate_msl_uniform_structs(ShaderStage::FRAGMENT);
+
+ /** GL globals. */
+ /* gl_FragCoord will always be assigned to the output position from vertex shading. */
+ ss_fragment << "float4 gl_FragCoord;" << std::endl;
+ if (msl_iface.uses_gl_FragColor) {
+ ss_fragment << "float4 gl_FragColor;" << std::endl;
+ }
+ if (msl_iface.uses_gl_FragDepth) {
+ ss_fragment << "float gl_FragDepth;" << std::endl;
+ }
+ if (msl_iface.uses_gl_PointCoord) {
+ ss_fragment << "float2 gl_PointCoord;" << std::endl;
+ }
+ if (msl_iface.uses_gl_FrontFacing) {
+ ss_fragment << "MTLBOOL gl_FrontFacing;" << std::endl;
+ }
+
+ /* Add Texture members. */
+ for (const MSLTextureSampler &tex : msl_iface.texture_samplers) {
+ if (bool(tex.stage & ShaderStage::FRAGMENT)) {
+ ss_fragment << "\tthread " << tex.get_msl_typestring_wrapper(false) << ";" << std::endl;
+ }
+ }
+
+ /* Inject Main GLSL Fragment Source into output stream. */
+ ss_fragment << shd_builder_->glsl_fragment_source_ << std::endl;
+
+ /* Class Closing Bracket to end shader global scope. */
+ ss_fragment << "};" << std::endl;
+
+ /* Generate Fragment entry-point function. */
+ ss_fragment << msl_iface.generate_msl_fragment_entry_stub();
+ }
+
+ /* DEBUG: Export source to file for manual verification. */
+#if MTL_SHADER_DEBUG_EXPORT_SOURCE
+ NSFileManager *sharedFM = [NSFileManager defaultManager];
+ NSURL *app_bundle_url = [[NSBundle mainBundle] bundleURL];
+ NSURL *shader_dir = [[app_bundle_url URLByDeletingLastPathComponent]
+ URLByAppendingPathComponent:@"Shaders/"
+ isDirectory:YES];
+ [sharedFM createDirectoryAtURL:shader_dir
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:nil];
+ const char *path_cstr = [shader_dir fileSystemRepresentation];
+
+ std::ofstream vertex_fs;
+ vertex_fs.open(
+ (std::string(path_cstr) + "/" + std::string(this->name) + "_GeneratedVertexShader.msl")
+ .c_str());
+ vertex_fs << ss_vertex.str();
+ vertex_fs.close();
+
+ if (!msl_iface.uses_transform_feedback) {
+ std::ofstream fragment_fs;
+ fragment_fs.open(
+ (std::string(path_cstr) + "/" + std::string(this->name) + "_GeneratedFragmentShader.msl")
+ .c_str());
+ fragment_fs << ss_fragment.str();
+ fragment_fs.close();
+ }
+
+ shader_debug_printf(
+ "Vertex Shader Saved to: %s\n",
+ (std::string(path_cstr) + std::string(this->name) + "_GeneratedFragmentShader.msl").c_str());
+#endif
+
+ /* Set MSL source NSString's. Required by Metal API. */
+ NSString *msl_final_vert = [NSString stringWithCString:ss_vertex.str().c_str()
+ encoding:[NSString defaultCStringEncoding]];
+ NSString *msl_final_frag = (msl_iface.uses_transform_feedback) ?
+ (@"") :
+ ([NSString stringWithCString:ss_fragment.str().c_str()
+ encoding:[NSString defaultCStringEncoding]]);
+
+ this->shader_source_from_msl(msl_final_vert, msl_final_frag);
+ shader_debug_printf("[METAL] BSL Converted into MSL\n");
+
+#ifndef NDEBUG
+ /* In debug mode, we inject the name of the shader into the entry-point function
+ * name, as these are what show up in the Xcode GPU debugger. */
+ this->set_vertex_function_name(
+ [[NSString stringWithFormat:@"vertex_function_entry_%s", this->name] retain]);
+ this->set_fragment_function_name(
+ [[NSString stringWithFormat:@"fragment_function_entry_%s", this->name] retain]);
+#else
+ this->set_vertex_function_name(@"vertex_function_entry");
+ this->set_fragment_function_name(@"fragment_function_entry");
+#endif
+
+ /* Bake shader interface. */
+ this->set_interface(msl_iface.bake_shader_interface(this->name));
+
+ /* Update other shader properties. */
+ uses_mtl_array_index_ = msl_iface.uses_mtl_array_index_;
+ use_ssbo_vertex_fetch_mode_ = msl_iface.uses_ssbo_vertex_fetch_mode;
+ if (msl_iface.uses_ssbo_vertex_fetch_mode) {
+ ssbo_vertex_fetch_output_prim_type_ = vertex_fetch_ssbo_output_prim_type;
+ ssbo_vertex_fetch_output_num_verts_ = vertex_fetch_ssbo_num_output_verts;
+ this->prepare_ssbo_vertex_fetch_metadata();
+ }
+
+ /* Successfully completed GLSL to MSL translation. */
+ return true;
+}
+
+constexpr size_t const_strlen(const char *str)
+{
+ return (*str == '\0') ? 0 : const_strlen(str + 1) + 1;
+}
+
+void MTLShader::prepare_ssbo_vertex_fetch_metadata()
+{
+ BLI_assert(use_ssbo_vertex_fetch_mode_);
+
+ /* Cache global SSBO-vertex-fetch uniforms locations. */
+ const ShaderInput *inp_prim_type = interface->uniform_get(UNIFORM_SSBO_INPUT_PRIM_TYPE_STR);
+ const ShaderInput *inp_vert_count = interface->uniform_get(UNIFORM_SSBO_INPUT_VERT_COUNT_STR);
+ const ShaderInput *inp_uses_indexed_rendering = interface->uniform_get(
+ UNIFORM_SSBO_USES_INDEXED_RENDERING_STR);
+ const ShaderInput *inp_uses_index_mode_u16 = interface->uniform_get(
+ UNIFORM_SSBO_INDEX_MODE_U16_STR);
+
+ this->uni_ssbo_input_prim_type_loc = (inp_prim_type != nullptr) ? inp_prim_type->location : -1;
+ this->uni_ssbo_input_vert_count_loc = (inp_vert_count != nullptr) ? inp_vert_count->location :
+ -1;
+ this->uni_ssbo_uses_indexed_rendering = (inp_uses_indexed_rendering != nullptr) ?
+ inp_uses_indexed_rendering->location :
+ -1;
+ this->uni_ssbo_uses_index_mode_u16 = (inp_uses_index_mode_u16 != nullptr) ?
+ inp_uses_index_mode_u16->location :
+ -1;
+
+ BLI_assert_msg(this->uni_ssbo_input_prim_type_loc != -1,
+ "uni_ssbo_input_prim_type_loc uniform location invalid!");
+ BLI_assert_msg(this->uni_ssbo_input_vert_count_loc != -1,
+ "uni_ssbo_input_vert_count_loc uniform location invalid!");
+ BLI_assert_msg(this->uni_ssbo_uses_indexed_rendering != -1,
+ "uni_ssbo_uses_indexed_rendering uniform location invalid!");
+ BLI_assert_msg(this->uni_ssbo_uses_index_mode_u16 != -1,
+ "uni_ssbo_uses_index_mode_u16 uniform location invalid!");
+
+ /* Prepare SSBO-vertex-fetch attribute uniform location cache. */
+ MTLShaderInterface *mtl_interface = this->get_interface();
+ for (int i = 0; i < mtl_interface->get_total_attributes(); i++) {
+ const MTLShaderInputAttribute &mtl_shader_attribute = mtl_interface->get_attribute(i);
+ const char *attr_name = mtl_interface->get_name_at_offset(mtl_shader_attribute.name_offset);
+
+ /* SSBO-vertex-fetch Attribute data is passed via uniforms. here we need to extract the uniform
+ * address for each attribute, and we can cache it for later use. */
+ ShaderSSBOAttributeBinding &cached_ssbo_attr = cached_ssbo_attribute_bindings_[i];
+ cached_ssbo_attr.attribute_index = i;
+
+ constexpr int len_UNIFORM_SSBO_STRIDE_STR = const_strlen(UNIFORM_SSBO_STRIDE_STR);
+ constexpr int len_UNIFORM_SSBO_OFFSET_STR = const_strlen(UNIFORM_SSBO_OFFSET_STR);
+ constexpr int len_UNIFORM_SSBO_FETCHMODE_STR = const_strlen(UNIFORM_SSBO_FETCHMODE_STR);
+ constexpr int len_UNIFORM_SSBO_VBO_ID_STR = const_strlen(UNIFORM_SSBO_VBO_ID_STR);
+ constexpr int len_UNIFORM_SSBO_TYPE_STR = const_strlen(UNIFORM_SSBO_TYPE_STR);
+
+ char strattr_buf_stride[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_STRIDE_STR + 1] =
+ UNIFORM_SSBO_STRIDE_STR;
+ char strattr_buf_offset[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_OFFSET_STR + 1] =
+ UNIFORM_SSBO_OFFSET_STR;
+ char strattr_buf_fetchmode[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_FETCHMODE_STR + 1] =
+ UNIFORM_SSBO_FETCHMODE_STR;
+ char strattr_buf_vbo_id[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_VBO_ID_STR + 1] =
+ UNIFORM_SSBO_VBO_ID_STR;
+ char strattr_buf_type[GPU_VERT_ATTR_MAX_LEN + len_UNIFORM_SSBO_TYPE_STR + 1] =
+ UNIFORM_SSBO_TYPE_STR;
+
+ strcpy(&strattr_buf_stride[len_UNIFORM_SSBO_STRIDE_STR], attr_name);
+ strcpy(&strattr_buf_offset[len_UNIFORM_SSBO_OFFSET_STR], attr_name);
+ strcpy(&strattr_buf_fetchmode[len_UNIFORM_SSBO_FETCHMODE_STR], attr_name);
+ strcpy(&strattr_buf_vbo_id[len_UNIFORM_SSBO_VBO_ID_STR], attr_name);
+ strcpy(&strattr_buf_type[len_UNIFORM_SSBO_TYPE_STR], attr_name);
+
+ /* Fetch uniform locations and cache for fast access. */
+ const ShaderInput *inp_unf_stride = mtl_interface->uniform_get(strattr_buf_stride);
+ const ShaderInput *inp_unf_offset = mtl_interface->uniform_get(strattr_buf_offset);
+ const ShaderInput *inp_unf_fetchmode = mtl_interface->uniform_get(strattr_buf_fetchmode);
+ const ShaderInput *inp_unf_vbo_id = mtl_interface->uniform_get(strattr_buf_vbo_id);
+ const ShaderInput *inp_unf_attr_type = mtl_interface->uniform_get(strattr_buf_type);
+
+ BLI_assert(inp_unf_stride != nullptr);
+ BLI_assert(inp_unf_offset != nullptr);
+ BLI_assert(inp_unf_fetchmode != nullptr);
+ BLI_assert(inp_unf_vbo_id != nullptr);
+ BLI_assert(inp_unf_attr_type != nullptr);
+
+ cached_ssbo_attr.uniform_stride = (inp_unf_stride != nullptr) ? inp_unf_stride->location : -1;
+ cached_ssbo_attr.uniform_offset = (inp_unf_offset != nullptr) ? inp_unf_offset->location : -1;
+ cached_ssbo_attr.uniform_fetchmode = (inp_unf_fetchmode != nullptr) ?
+ inp_unf_fetchmode->location :
+ -1;
+ cached_ssbo_attr.uniform_vbo_id = (inp_unf_vbo_id != nullptr) ? inp_unf_vbo_id->location : -1;
+ cached_ssbo_attr.uniform_attr_type = (inp_unf_attr_type != nullptr) ?
+ inp_unf_attr_type->location :
+ -1;
+
+ BLI_assert(cached_ssbo_attr.uniform_offset != -1);
+ BLI_assert(cached_ssbo_attr.uniform_stride != -1);
+ BLI_assert(cached_ssbo_attr.uniform_fetchmode != -1);
+ BLI_assert(cached_ssbo_attr.uniform_vbo_id != -1);
+ BLI_assert(cached_ssbo_attr.uniform_attr_type != -1);
+ }
+}
+
+void MSLGeneratorInterface::prepare_from_createinfo(const shader::ShaderCreateInfo *info)
+{
+ /** Assign info. */
+ create_info_ = info;
+
+ /** Prepare Uniforms. */
+ for (const shader::ShaderCreateInfo::PushConst &push_constant : create_info_->push_constants_) {
+ MSLUniform uniform(push_constant.type,
+ push_constant.name,
+ bool(push_constant.array_size > 1),
+ push_constant.array_size);
+ uniforms.append(uniform);
+ }
+
+ /** Prepare textures and uniform blocks.
+ * Perform across both resource categories and extract both
+ * texture samplers and image types. */
+ for (int i = 0; i < 2; i++) {
+ const Vector<ShaderCreateInfo::Resource> &resources = (i == 0) ? info->pass_resources_ :
+ info->batch_resources_;
+ for (const ShaderCreateInfo::Resource &res : resources) {
+ /* TODO(Metal): Consider adding stage flags to textures in create info. */
+ /* Handle sampler types. */
+ switch (res.bind_type) {
+ case shader::ShaderCreateInfo::Resource::BindType::SAMPLER: {
+
+ /* Samplers to have access::sample by default. */
+ MSLTextureSamplerAccess access = MSLTextureSamplerAccess::TEXTURE_ACCESS_SAMPLE;
+ /* TextureBuffers must have read/write/read-write access pattern. */
+ if (res.sampler.type == ImageType::FLOAT_BUFFER ||
+ res.sampler.type == ImageType::INT_BUFFER ||
+ res.sampler.type == ImageType::UINT_BUFFER) {
+ access = MSLTextureSamplerAccess::TEXTURE_ACCESS_READ;
+ }
+ BLI_assert(res.slot >= 0 && res.slot < MTL_MAX_TEXTURE_SLOTS);
+ MSLTextureSampler msl_tex(
+ ShaderStage::BOTH, res.sampler.type, res.sampler.name, access, res.slot);
+ texture_samplers.append(msl_tex);
+ } break;
+
+ case shader::ShaderCreateInfo::Resource::BindType::IMAGE: {
+ /* Flatten qualifier flags into final access state. */
+ MSLTextureSamplerAccess access;
+ if (bool(res.image.qualifiers & Qualifier::READ_WRITE)) {
+ access = MSLTextureSamplerAccess::TEXTURE_ACCESS_READWRITE;
+ }
+ else if (bool(res.image.qualifiers & Qualifier::WRITE)) {
+ access = MSLTextureSamplerAccess::TEXTURE_ACCESS_WRITE;
+ }
+ else {
+ access = MSLTextureSamplerAccess::TEXTURE_ACCESS_READ;
+ }
+ BLI_assert(res.slot >= 0 && res.slot < MTL_MAX_TEXTURE_SLOTS);
+ MSLTextureSampler msl_tex(
+ ShaderStage::BOTH, res.image.type, res.image.name, access, res.slot);
+ texture_samplers.append(msl_tex);
+ } break;
+
+ case shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER: {
+ MSLUniformBlock ubo;
+ BLI_assert(res.uniformbuf.type_name.size() > 0);
+ BLI_assert(res.uniformbuf.name.size() > 0);
+ int64_t array_offset = res.uniformbuf.name.find_first_of("[");
+
+ ubo.type_name = res.uniformbuf.type_name;
+ ubo.is_array = (array_offset > -1);
+ if (ubo.is_array) {
+ /* If is array UBO, strip out array tag from name. */
+ StringRef name_no_array = StringRef(res.uniformbuf.name.c_str(), array_offset);
+ ubo.name = name_no_array;
+ }
+ else {
+ ubo.name = res.uniformbuf.name;
+ }
+ ubo.stage = ShaderStage::VERTEX | ShaderStage::FRAGMENT;
+ uniform_blocks.append(ubo);
+ } break;
+
+ case shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER: {
+ /* TODO(Metal): Support shader storage buffer in Metal.
+ * Pending compute support. */
+ } break;
+ }
+ }
+ }
+
+ /** Vertex Inputs. */
+ bool all_attr_location_assigned = true;
+ for (const ShaderCreateInfo::VertIn &attr : info->vertex_inputs_) {
+
+ /* Validate input. */
+ BLI_assert(attr.name.size() > 0);
+
+ /* NOTE(Metal): Input attributes may not have a location specified.
+ * unset locations are resolved during: `resolve_input_attribute_locations`. */
+ MSLVertexInputAttribute msl_attr;
+ bool attr_location_assigned = (attr.index >= 0);
+ all_attr_location_assigned = all_attr_location_assigned && attr_location_assigned;
+ msl_attr.layout_location = attr_location_assigned ? attr.index : -1;
+ msl_attr.type = attr.type;
+ msl_attr.name = attr.name;
+ vertex_input_attributes.append(msl_attr);
+ }
+
+ /* Ensure all attributes are assigned a location. */
+ if (!all_attr_location_assigned) {
+ this->resolve_input_attribute_locations();
+ }
+
+ /** Fragment outputs. */
+ for (const shader::ShaderCreateInfo::FragOut &frag_out : create_info_->fragment_outputs_) {
+
+ /* Validate input. */
+ BLI_assert(frag_out.name.size() > 0);
+ BLI_assert(frag_out.index >= 0);
+
+ /* Populate MSLGenerator attribute. */
+ MSLFragmentOutputAttribute mtl_frag_out;
+ mtl_frag_out.layout_location = frag_out.index;
+ mtl_frag_out.layout_index = (frag_out.blend != DualBlend::NONE) ?
+ ((frag_out.blend == DualBlend::SRC_0) ? 0 : 1) :
+ -1;
+ mtl_frag_out.type = frag_out.type;
+ mtl_frag_out.name = frag_out.name;
+
+ fragment_outputs.append(mtl_frag_out);
+ }
+}
+
+bool MSLGeneratorInterface::use_argument_buffer_for_samplers() const
+{
+ /* We can only use argument buffers IF sampler count exceeds static limit of 16,
+ * AND we can support more samplers with an argument buffer. */
+ return texture_samplers.size() >= 16 && GPU_max_samplers() > 16;
+}
+
+uint32_t MSLGeneratorInterface::num_samplers_for_stage(ShaderStage stage) const
+{
+ /* NOTE: Sampler bindings and argument buffer shared across stages,
+ * in case stages share texture/sampler bindings. */
+ return texture_samplers.size();
+}
+
+uint32_t MSLGeneratorInterface::get_sampler_argument_buffer_bind_index(ShaderStage stage)
+{
+ BLI_assert(stage == ShaderStage::VERTEX || stage == ShaderStage::FRAGMENT);
+ if (sampler_argument_buffer_bind_index[get_shader_stage_index(stage)] >= 0) {
+ return sampler_argument_buffer_bind_index[get_shader_stage_index(stage)];
+ }
+ sampler_argument_buffer_bind_index[get_shader_stage_index(stage)] =
+ (this->uniform_blocks.size() + 1);
+ return sampler_argument_buffer_bind_index[get_shader_stage_index(stage)];
+}
+
+void MSLGeneratorInterface::prepare_ssbo_vertex_fetch_uniforms()
+{
+ BLI_assert(this->uses_ssbo_vertex_fetch_mode);
+
+ /* Add Special Uniforms for SSBO vertex fetch mode. */
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_INPUT_PRIM_TYPE_STR, false));
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_INPUT_VERT_COUNT_STR, false));
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_USES_INDEXED_RENDERING_STR, false));
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_INDEX_MODE_U16_STR, false));
+
+ for (const MSLVertexInputAttribute &attr : this->vertex_input_attributes) {
+ const std::string &uname = attr.name;
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_STRIDE_STR + uname, false));
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_OFFSET_STR + uname, false));
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_FETCHMODE_STR + uname, false));
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_VBO_ID_STR + uname, false));
+ this->uniforms.append(MSLUniform(Type::INT, UNIFORM_SSBO_TYPE_STR + uname, false));
+ }
+}
+
+std::string MSLGeneratorInterface::generate_msl_vertex_entry_stub()
+{
+ std::stringstream out;
+ out << std::endl << "/*** AUTO-GENERATED MSL VERETX SHADER STUB. ***/" << std::endl;
+
+ /* Un-define texture defines from main source - avoid conflict with MSL texture. */
+ out << "#undef texture" << std::endl;
+ out << "#undef textureLod" << std::endl;
+
+ /* Disable special case for booleans being treated as ints in GLSL. */
+ out << "#undef bool" << std::endl;
+
+ /* Un-define uniform mappings to avoid name collisions. */
+ out << generate_msl_uniform_undefs(ShaderStage::VERTEX);
+
+ /* Generate function entry point signature w/ resource bindings and inputs. */
+ out << "vertex ";
+ if (this->uses_transform_feedback) {
+ out << "void ";
+ }
+ else {
+ out << get_stage_class_name(ShaderStage::VERTEX) << "::VertexOut ";
+ }
+#ifndef NDEBUG
+ out << "vertex_function_entry_" << parent_shader_.name_get() << "(\n\t";
+#else
+ out << "vertex_function_entry(\n\t";
+#endif
+
+ out << this->generate_msl_vertex_inputs_string();
+ out << ") {" << std::endl << std::endl;
+ out << "\tMTLShaderVertexImpl::VertexOut output;" << std::endl
+ << "\tMTLShaderVertexImpl vertex_shader_instance;" << std::endl;
+
+ /* Copy Vertex Globals. */
+ if (this->uses_gl_VertexID) {
+ out << "vertex_shader_instance.gl_VertexID = gl_VertexID;" << std::endl;
+ }
+ if (this->uses_gl_InstanceID) {
+ out << "vertex_shader_instance.gl_InstanceID = gl_InstanceID-gl_BaseInstanceARB;" << std::endl;
+ }
+ if (this->uses_gl_BaseInstanceARB) {
+ out << "vertex_shader_instance.gl_BaseInstanceARB = gl_BaseInstanceARB;" << std::endl;
+ }
+
+ /* Copy vertex attributes into local variables. */
+ out << this->generate_msl_vertex_attribute_input_population();
+
+ /* Populate Uniforms and uniform blocks. */
+ out << this->generate_msl_texture_vars(ShaderStage::VERTEX);
+ out << this->generate_msl_global_uniform_population(ShaderStage::VERTEX);
+ out << this->generate_msl_uniform_block_population(ShaderStage::VERTEX);
+
+ /* Execute original 'main' function within class scope. */
+ out << "\t/* Execute Vertex main function */\t" << std::endl
+ << "\tvertex_shader_instance.main();" << std::endl
+ << std::endl;
+
+ /* Populate Output values. */
+ out << this->generate_msl_vertex_output_population();
+
+ /* Final point size,
+ * This is only compiled if the `MTL_global_pointsize` is specified
+ * as a function specialization in the PSO. This is restricted to
+ * point primitive types. */
+ out << "if(is_function_constant_defined(MTL_global_pointsize)){ output.pointsize = "
+ "(MTL_global_pointsize > 0.0)?MTL_global_pointsize:output.pointsize; }"
+ << std::endl;
+
+ /* Populate transform feedback buffer. */
+ if (this->uses_transform_feedback) {
+ out << this->generate_msl_vertex_output_tf_population();
+ }
+ else {
+ out << "\treturn output;" << std::endl;
+ }
+ out << "}";
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_fragment_entry_stub()
+{
+ std::stringstream out;
+ out << std::endl << "/*** AUTO-GENERATED MSL FRAGMENT SHADER STUB. ***/" << std::endl;
+
+ /* Undefine texture defines from main source - avoid conflict with MSL texture. */
+ out << "#undef texture" << std::endl;
+ out << "#undef textureLod" << std::endl;
+
+ /* Disable special case for booleans being treated as integers in GLSL. */
+ out << "#undef bool" << std::endl;
+
+ /* Undefine uniform mappings to avoid name collisions. */
+ out << generate_msl_uniform_undefs(ShaderStage::FRAGMENT);
+
+ /* Generate function entry point signature w/ resource bindings and inputs. */
+#ifndef NDEBUG
+ out << "fragment " << get_stage_class_name(ShaderStage::FRAGMENT)
+ << "::FragmentOut fragment_function_entry_" << parent_shader_.name_get() << "(\n\t";
+#else
+ out << "fragment " << get_stage_class_name(ShaderStage::FRAGMENT)
+ << "::FragmentOut fragment_function_entry(\n\t";
+#endif
+ out << this->generate_msl_fragment_inputs_string();
+ out << ") {" << std::endl << std::endl;
+ out << "\tMTLShaderFragmentImpl::FragmentOut output;" << std::endl
+ << "\tMTLShaderFragmentImpl fragment_shader_instance;" << std::endl;
+
+ /* Copy Fragment Globals. */
+ if (this->uses_gl_PointCoord) {
+ out << "fragment_shader_instance.gl_PointCoord = gl_PointCoord;" << std::endl;
+ }
+ if (this->uses_gl_FrontFacing) {
+ out << "fragment_shader_instance.gl_FrontFacing = gl_FrontFacing;" << std::endl;
+ }
+
+ /* Copy vertex attributes into local variable.s */
+ out << this->generate_msl_fragment_input_population();
+
+ /* Barycentrics. */
+ if (this->uses_barycentrics) {
+
+ /* Main barycentrics. */
+ out << "fragment_shader_instance.gpu_BaryCoord = mtl_barycentric_coord.xyz;";
+
+ /* barycentricDist represents the world-space distance from the current world-space position
+ * to the opposite edge of the vertex. */
+ out << "float3 worldPos = fragment_shader_instance.worldPosition.xyz;" << std::endl;
+ out << "float3 wpChange = (length(dfdx(worldPos))+length(dfdy(worldPos)));" << std::endl;
+ out << "float3 bcChange = "
+ "(length(dfdx(mtl_barycentric_coord))+length(dfdy(mtl_barycentric_coord)));"
+ << std::endl;
+ out << "float3 rateOfChange = wpChange/bcChange;" << std::endl;
+
+ /* Distance to edge using inverse barycentric value, as rather than the length of 0.7
+ * contribution, we'd want the distance to the opposite side. */
+ out << "fragment_shader_instance.gpu_BarycentricDist.x = length(rateOfChange * "
+ "(1.0-mtl_barycentric_coord.x));"
+ << std::endl;
+ out << "fragment_shader_instance.gpu_BarycentricDist.y = length(rateOfChange * "
+ "(1.0-mtl_barycentric_coord.y));"
+ << std::endl;
+ out << "fragment_shader_instance.gpu_BarycentricDist.z = length(rateOfChange * "
+ "(1.0-mtl_barycentric_coord.z));"
+ << std::endl;
+ }
+
+ /* Populate Uniforms and uniform blocks. */
+ out << this->generate_msl_texture_vars(ShaderStage::FRAGMENT);
+ out << this->generate_msl_global_uniform_population(ShaderStage::FRAGMENT);
+ out << this->generate_msl_uniform_block_population(ShaderStage::FRAGMENT);
+
+ /* Execute original 'main' function within class scope. */
+ out << "\t/* Execute Fragment main function */\t" << std::endl
+ << "\tfragment_shader_instance.main();" << std::endl
+ << std::endl;
+
+ /* Populate Output values. */
+ out << this->generate_msl_fragment_output_population();
+ out << " return output;" << std::endl << "}";
+
+ return out.str();
+}
+
+void MSLGeneratorInterface::generate_msl_textures_input_string(std::stringstream &out,
+ ShaderStage stage)
+{
+ BLI_assert(stage == ShaderStage::VERTEX || stage == ShaderStage::FRAGMENT);
+ /* Generate texture signatures. */
+ BLI_assert(this->texture_samplers.size() <= GPU_max_textures_vert());
+ for (const MSLTextureSampler &tex : this->texture_samplers) {
+ if (bool(tex.stage & stage)) {
+ out << ",\n\t" << tex.get_msl_typestring(false) << " [[texture(" << tex.location << ")]]";
+ }
+ }
+
+ /* Generate sampler signatures. */
+ /* NOTE: Currently textures and samplers share indices across shading stages, so the limit is
+ * shared.
+ * If we exceed the hardware-supported limit, then follow a bind-less model using argument
+ * buffers. */
+ if (this->use_argument_buffer_for_samplers()) {
+ out << ",\n\tconstant SStruct& samplers [[buffer(MTL_uniform_buffer_base_index+"
+ << (this->get_sampler_argument_buffer_bind_index(stage)) << ")]]";
+ }
+ else {
+ /* Maximum Limit of samplers defined in the function argument table is
+ * `MTL_MAX_DEFAULT_SAMPLERS=16`. */
+ BLI_assert(this->texture_samplers.size() <= MTL_MAX_DEFAULT_SAMPLERS);
+ for (const MSLTextureSampler &tex : this->texture_samplers) {
+ if (bool(tex.stage & stage)) {
+ out << ",\n\tsampler " << tex.name << "_sampler [[sampler(" << tex.location << ")]]";
+ }
+ }
+
+ /* Fallback. */
+ if (this->texture_samplers.size() > 16) {
+ shader_debug_printf(
+ "[Metal] Warning: Shader exceeds limit of %u samplers on current hardware\n",
+ MTL_MAX_DEFAULT_SAMPLERS);
+ }
+ }
+}
+
+void MSLGeneratorInterface::generate_msl_uniforms_input_string(std::stringstream &out,
+ ShaderStage stage)
+{
+ int ubo_index = 0;
+ for (const MSLUniformBlock &ubo : this->uniform_blocks) {
+ if (bool(ubo.stage & stage)) {
+ /* For literal/existing global types, we do not need the class name-space accessor. */
+ out << ",\n\tconstant ";
+ if (!is_builtin_type(ubo.type_name)) {
+ out << get_stage_class_name(stage) << "::";
+ }
+ /* #UniformBuffer bind indices start at `MTL_uniform_buffer_base_index + 1`, as
+ * MTL_uniform_buffer_base_index is reserved for the #PushConstantBlock (push constants).
+ * MTL_uniform_buffer_base_index is an offset depending on the number of unique VBOs
+ * bound for the current PSO specialization. */
+ out << ubo.type_name << "* " << ubo.name << "[[buffer(MTL_uniform_buffer_base_index+"
+ << (ubo_index + 1) << ")]]";
+ }
+ ubo_index++;
+ }
+}
+
+std::string MSLGeneratorInterface::generate_msl_vertex_inputs_string()
+{
+ std::stringstream out;
+
+ if (this->uses_ssbo_vertex_fetch_mode) {
+ /* Vertex Buffers bound as raw buffers. */
+ for (int i = 0; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) {
+ out << "\tconstant uchar* MTL_VERTEX_DATA_" << i << " [[buffer(" << i << ")]],\n";
+ }
+ out << "\tconstant ushort* MTL_INDEX_DATA[[buffer(MTL_SSBO_VERTEX_FETCH_IBO_INDEX)]],";
+ }
+ else {
+ if (this->vertex_input_attributes.size() > 0) {
+ /* Vertex Buffers use input assembly. */
+ out << get_stage_class_name(ShaderStage::VERTEX) << "::VertexIn v_in [[stage_in]],";
+ }
+ }
+ out << "\n\tconstant " << get_stage_class_name(ShaderStage::VERTEX)
+ << "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
+
+ this->generate_msl_uniforms_input_string(out, ShaderStage::VERTEX);
+
+ /* Transform feedback buffer binding. */
+ if (this->uses_transform_feedback) {
+ out << ",\n\tdevice " << get_stage_class_name(ShaderStage::VERTEX)
+ << "::VertexOut_TF* "
+ "transform_feedback_results[[buffer(MTL_transform_feedback_buffer_index)]]";
+ }
+
+ /* Generate texture signatures. */
+ this->generate_msl_textures_input_string(out, ShaderStage::VERTEX);
+
+ /* Entry point parameters for gl Globals. */
+ if (this->uses_gl_VertexID) {
+ out << ",\n\tconst uint32_t gl_VertexID [[vertex_id]]";
+ }
+ if (this->uses_gl_InstanceID) {
+ out << ",\n\tconst uint32_t gl_InstanceID [[instance_id]]";
+ }
+ if (this->uses_gl_BaseInstanceARB) {
+ out << ",\n\tconst uint32_t gl_BaseInstanceARB [[base_instance]]";
+ }
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_fragment_inputs_string()
+{
+ std::stringstream out;
+ out << get_stage_class_name(ShaderStage::FRAGMENT)
+ << "::VertexOut v_in [[stage_in]],\n\tconstant "
+ << get_stage_class_name(ShaderStage::FRAGMENT)
+ << "::PushConstantBlock* uniforms[[buffer(MTL_uniform_buffer_base_index)]]";
+
+ this->generate_msl_uniforms_input_string(out, ShaderStage::FRAGMENT);
+
+ /* Generate texture signatures. */
+ this->generate_msl_textures_input_string(out, ShaderStage::FRAGMENT);
+
+ if (this->uses_gl_PointCoord) {
+ out << ",\n\tconst float2 gl_PointCoord [[point_coord]]";
+ }
+ if (this->uses_gl_FrontFacing) {
+ out << ",\n\tconst MTLBOOL gl_FrontFacing [[front_facing]]";
+ }
+
+ /* Barycentrics. */
+ if (this->uses_barycentrics) {
+ out << ",\n\tconst float3 mtl_barycentric_coord [[barycentric_coord]]";
+ }
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_uniform_structs(ShaderStage shader_stage)
+{
+ BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
+ std::stringstream out;
+
+ /* Common Uniforms. */
+ out << "typedef struct {" << std::endl;
+
+ for (const MSLUniform &uniform : this->uniforms) {
+ if (uniform.is_array) {
+ out << "\t" << to_string(uniform.type) << " " << uniform.name << "[" << uniform.array_elems
+ << "];" << std::endl;
+ }
+ else {
+ out << "\t" << to_string(uniform.type) << " " << uniform.name << ";" << std::endl;
+ }
+ }
+ out << "} PushConstantBlock;\n\n";
+
+ /* Member UBO block reference. */
+ out << std::endl << "const constant PushConstantBlock *global_uniforms;" << std::endl;
+
+ /* Macro define chain.
+ * To access uniforms, we generate a macro such that the uniform name can
+ * be used directly without using the struct's handle. */
+ for (const MSLUniform &uniform : this->uniforms) {
+ out << "#define " << uniform.name << " global_uniforms->" << uniform.name << std::endl;
+ }
+ out << std::endl;
+ return out.str();
+}
+
+/* NOTE: Uniform macro definition vars can conflict with other parameters. */
+std::string MSLGeneratorInterface::generate_msl_uniform_undefs(ShaderStage shader_stage)
+{
+ std::stringstream out;
+
+ /* Macro undef chain. */
+ for (const MSLUniform &uniform : this->uniforms) {
+ out << "#undef " << uniform.name << std::endl;
+ }
+ /* UBO block undef. */
+ for (const MSLUniformBlock &ubo : this->uniform_blocks) {
+ out << "#undef " << ubo.name << std::endl;
+ }
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_vertex_in_struct()
+{
+ std::stringstream out;
+
+ /* Skip struct if no vert attributes. */
+ if (this->vertex_input_attributes.size() == 0) {
+ return "";
+ }
+
+ /* Output */
+ out << "typedef struct {" << std::endl;
+ for (const MSLVertexInputAttribute &in_attr : this->vertex_input_attributes) {
+ /* Matrix and array attributes are not trivially supported and thus
+ * require each element to be passed as an individual attribute.
+ * This requires shader source generation of sequential elements.
+ * The matrix type is then re-packed into a Mat4 inside the entry function.
+ *
+ * e.g.
+ * float4 __internal_modelmatrix_0 [[attribute(0)]];
+ * float4 __internal_modelmatrix_1 [[attribute(1)]];
+ * float4 __internal_modelmatrix_2 [[attribute(2)]];
+ * float4 __internal_modelmatrix_3 [[attribute(3)]];
+ */
+ if (is_matrix_type(in_attr.type) && !this->uses_ssbo_vertex_fetch_mode) {
+ for (int elem = 0; elem < get_matrix_location_count(in_attr.type); elem++) {
+ out << "\t" << get_matrix_subtype(in_attr.type) << " __internal_" << in_attr.name << elem
+ << " [[attribute(" << (in_attr.layout_location + elem) << ")]];" << std::endl;
+ }
+ }
+ else {
+ out << "\t" << in_attr.type << " " << in_attr.name << " [[attribute("
+ << in_attr.layout_location << ")]];" << std::endl;
+ }
+ }
+
+ out << "} VertexIn;" << std::endl << std::endl;
+
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_vertex_out_struct(ShaderStage shader_stage)
+{
+ BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
+ std::stringstream out;
+
+ /* Vertex output struct. */
+ out << "typedef struct {" << std::endl;
+
+ /* If we use GL position, our standard output variable will be mapped to '_default_position_'.
+ * Otherwise, we use the FIRST element in the output array.
+ * If transform feedback is enabled, we do not need to output position, unless it
+ * is explicitly specified as a tf output. */
+ bool first_attr_is_position = false;
+ if (this->uses_gl_Position) {
+ out << "\tfloat4 _default_position_ [[position]];" << std::endl;
+ }
+ else {
+ if (!this->uses_transform_feedback) {
+ /* Use first output element for position. */
+ BLI_assert(this->vertex_output_varyings.size() > 0);
+ BLI_assert(this->vertex_output_varyings[0].type == "vec4");
+ out << "\tfloat4 " << this->vertex_output_varyings[0].name << " [[position]];" << std::endl;
+ first_attr_is_position = true;
+ }
+ }
+
+ /* Generate other vertex output members. */
+ bool skip_first_index = first_attr_is_position;
+ for (const MSLVertexOutputAttribute &v_out : this->vertex_output_varyings) {
+
+ /* Skip first index if used for position. */
+ if (skip_first_index) {
+ skip_first_index = false;
+ continue;
+ }
+
+ if (v_out.is_array) {
+ /* Array types cannot be trivially passed between shading stages.
+ * Instead we pass each component individually. E.g. vec4 pos[2]
+ * will be converted to: `vec4 pos_0; vec4 pos_1;`
+ * The specified interpolation qualifier will be applied per element. */
+ /* TODO(Metal): Support array of matrix in-out types if required
+ * e.g. Mat4 out_matrices[3]. */
+ for (int i = 0; i < v_out.array_elems; i++) {
+ out << "\t" << v_out.type << " " << v_out.instance_name << "_" << v_out.name << i
+ << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
+ }
+ }
+ else {
+ /* Matrix types need to be expressed as their vector sub-components. */
+ if (is_matrix_type(v_out.type)) {
+ BLI_assert(v_out.get_mtl_interpolation_qualifier() == " [[flat]]" &&
+ "Matrix varying types must have [[flat]] interpolation");
+ std::string subtype = get_matrix_subtype(v_out.type);
+ for (int elem = 0; elem < get_matrix_location_count(v_out.type); elem++) {
+ out << "\t" << subtype << v_out.instance_name << " __matrix_" << v_out.name << elem
+ << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
+ }
+ }
+ else {
+ out << "\t" << v_out.type << " " << v_out.instance_name << "_" << v_out.name
+ << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
+ }
+ }
+ }
+
+ /* Add gl_PointSize if written to. */
+ if (shader_stage == ShaderStage::VERTEX) {
+ if (this->uses_gl_PointSize) {
+ /* If `gl_PointSize` is explicitly written to,
+ * we will output the written value directly.
+ * This value can still be overridden by the
+ * global point-size value. */
+ out << "\tfloat pointsize [[point_size]];" << std::endl;
+ }
+ else {
+ /* Otherwise, if point-size is not written to inside the shader,
+ * then its usage is controlled by whether the `MTL_global_pointsize`
+ * function constant has been specified.
+ * This function constant is enabled for all point primitives being rendered. */
+ out << "\tfloat pointsize [[point_size, function_constant(MTL_global_pointsize)]];"
+ << std::endl;
+ }
+ }
+
+ /* Add gl_ClipDistance[n]. */
+ if (shader_stage == ShaderStage::VERTEX) {
+ out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl;
+ if (this->clip_distances.size() > 1) {
+ /* Output array of clip distances if specified. */
+ out << "\tfloat clipdistance [[clip_distance]] [" << this->clip_distances.size() << "];"
+ << std::endl;
+ }
+ else if (this->clip_distances.size() > 0) {
+ out << "\tfloat clipdistance [[clip_distance]];" << std::endl;
+ }
+ out << "#endif" << std::endl;
+ }
+
+ /* Add MTL render target array index for multilayered rendering support. */
+ if (uses_mtl_array_index_) {
+ out << "\tuint MTLRenderTargetArrayIndex [[render_target_array_index]];" << std::endl;
+ }
+
+ out << "} VertexOut;" << std::endl << std::endl;
+
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_vertex_transform_feedback_out_struct(
+ ShaderStage shader_stage)
+{
+ BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
+ std::stringstream out;
+ vertex_output_varyings_tf.clear();
+
+ out << "typedef struct {" << std::endl;
+
+ /* If we use GL position, our standard output variable will be mapped to '_default_position_'.
+ * Otherwise, we use the FIRST element in the output array -- If transform feedback is enabled,
+ * we do not need to output position */
+ bool first_attr_is_position = false;
+ if (this->uses_gl_Position) {
+
+ if (parent_shader_.has_transform_feedback_varying("gl_Position")) {
+ out << "\tfloat4 pos [[position]];" << std::endl;
+ vertex_output_varyings_tf.append({.type = "vec4",
+ .name = "gl_Position",
+ .interpolation_qualifier = "",
+ .is_array = false,
+ .array_elems = 1});
+ }
+ }
+ else {
+ if (!this->uses_transform_feedback) {
+ /* Use first output element for position */
+ BLI_assert(this->vertex_output_varyings.size() > 0);
+ BLI_assert(this->vertex_output_varyings[0].type == "vec4");
+ first_attr_is_position = true;
+ }
+ }
+
+ /* Generate other vertex outputs. */
+ bool skip_first_index = first_attr_is_position;
+ for (const MSLVertexOutputAttribute &v_out : this->vertex_output_varyings) {
+
+ /* Skip first index if used for position. */
+ if (skip_first_index) {
+ skip_first_index = false;
+ continue;
+ }
+
+ if (!parent_shader_.has_transform_feedback_varying(v_out.name)) {
+ continue;
+ }
+ vertex_output_varyings_tf.append(v_out);
+
+ if (v_out.is_array) {
+ /* TODO(Metal): Support array of matrix types if required. */
+ for (int i = 0; i < v_out.array_elems; i++) {
+ out << "\t" << v_out.type << " " << v_out.name << i
+ << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
+ }
+ }
+ else {
+ /* Matrix types need to be expressed as their vector sub-components. */
+ if (is_matrix_type(v_out.type)) {
+ BLI_assert(v_out.get_mtl_interpolation_qualifier() == " [[flat]]" &&
+ "Matrix varying types must have [[flat]] interpolation");
+ std::string subtype = get_matrix_subtype(v_out.type);
+ for (int elem = 0; elem < get_matrix_location_count(v_out.type); elem++) {
+ out << "\t" << subtype << " __matrix_" << v_out.name << elem
+ << v_out.get_mtl_interpolation_qualifier() << ";" << std::endl;
+ }
+ }
+ else {
+ out << "\t" << v_out.type << " " << v_out.name << v_out.get_mtl_interpolation_qualifier()
+ << ";" << std::endl;
+ }
+ }
+ }
+
+ out << "} VertexOut_TF;" << std::endl << std::endl;
+
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_fragment_out_struct()
+{
+ std::stringstream out;
+
+ /* Output. */
+ out << "typedef struct {" << std::endl;
+ for (int f_output = 0; f_output < this->fragment_outputs.size(); f_output++) {
+ out << "\t" << to_string(this->fragment_outputs[f_output].type) << " "
+ << this->fragment_outputs[f_output].name << " [[color("
+ << this->fragment_outputs[f_output].layout_location << ")";
+ if (this->fragment_outputs[f_output].layout_index >= 0) {
+ out << ", index(" << this->fragment_outputs[f_output].layout_index << ")";
+ }
+ out << "]]"
+ << ";" << std::endl;
+ }
+ /* Add gl_FragDepth output if used. */
+ if (this->uses_gl_FragDepth) {
+ std::string out_depth_argument = ((this->depth_write == DepthWrite::GREATER) ?
+ "greater" :
+ ((this->depth_write == DepthWrite::LESS) ? "less" :
+ "any"));
+ out << "\tfloat fragdepth [[depth(" << out_depth_argument << ")]];" << std::endl;
+ }
+
+ out << "} FragmentOut;" << std::endl;
+ out << std::endl;
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_global_uniform_population(ShaderStage stage)
+{
+ /* Populate Global Uniforms. */
+ std::stringstream out;
+
+ /* Copy UBO block ref. */
+ out << "\t/* Copy Uniform block member reference */" << std::endl;
+ out << "\t"
+ << ((stage == ShaderStage::VERTEX) ? "vertex_shader_instance." : "fragment_shader_instance.")
+ << "global_uniforms = uniforms;" << std::endl;
+
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_uniform_block_population(ShaderStage stage)
+{
+ /* Populate Global Uniforms. */
+ std::stringstream out;
+ out << "\t/* Copy UBO block references into local class variables */" << std::endl;
+ for (const MSLUniformBlock &ubo : this->uniform_blocks) {
+
+ /* Only include blocks which are used within this stage. */
+ if (bool(ubo.stage & stage)) {
+ /* Generate UBO reference assignment.
+ * NOTE(Metal): We append `_local` post-fix onto the class member name
+ * for the ubo to avoid name collision with the UBO accessor macro.
+ * We only need to add this post-fix for the non-array access variant,
+ * as the array is indexed directly, rather than requiring a dereference. */
+ out << "\t"
+ << ((stage == ShaderStage::VERTEX) ? "vertex_shader_instance." :
+ "fragment_shader_instance.")
+ << ubo.name;
+ if (!ubo.is_array) {
+ out << "_local";
+ }
+ out << " = " << ubo.name << ";" << std::endl;
+ }
+ }
+ out << std::endl;
+ return out.str();
+}
+
+/* Copy input attributes from stage_in into class local variables. */
+std::string MSLGeneratorInterface::generate_msl_vertex_attribute_input_population()
+{
+
+ /* SSBO Vertex Fetch mode does not require local attribute population,
+ * we only need to pass over the buffer pointer references. */
+ if (this->uses_ssbo_vertex_fetch_mode) {
+ std::stringstream out;
+ out << "const constant uchar* GLOBAL_MTL_VERTEX_DATA[MTL_SSBO_VERTEX_FETCH_MAX_VBOS] = {"
+ << std::endl;
+ for (int i = 0; i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS; i++) {
+ char delimiter = (i < MTL_SSBO_VERTEX_FETCH_MAX_VBOS - 1) ? ',' : ' ';
+ out << "\t\tMTL_VERTEX_DATA_" << i << delimiter << std::endl;
+ }
+ out << "};" << std::endl;
+ out << "\tvertex_shader_instance.MTL_VERTEX_DATA = GLOBAL_MTL_VERTEX_DATA;" << std::endl;
+ out << "\tvertex_shader_instance.MTL_INDEX_DATA_U16 = MTL_INDEX_DATA;" << std::endl;
+ out << "\tvertex_shader_instance.MTL_INDEX_DATA_U32 = reinterpret_cast<constant "
+ "uint32_t*>(MTL_INDEX_DATA);"
+ << std::endl;
+ return out.str();
+ }
+
+ /* Populate local attribute variables. */
+ std::stringstream out;
+ out << "\t/* Copy Vertex Stage-in attributes into local variables */" << std::endl;
+ for (int attribute = 0; attribute < this->vertex_input_attributes.size(); attribute++) {
+
+ if (is_matrix_type(this->vertex_input_attributes[attribute].type)) {
+ /* Reading into an internal matrix from split attributes: Should generate the following:
+ * vertex_shader_instance.mat_attribute_type =
+ * mat4(v_in.__internal_mat_attribute_type0,
+ * v_in.__internal_mat_attribute_type1,
+ * v_in.__internal_mat_attribute_type2,
+ * v_in.__internal_mat_attribute_type3). */
+ out << "\tvertex_shader_instance." << this->vertex_input_attributes[attribute].name << " = "
+ << this->vertex_input_attributes[attribute].type << "(v_in.__internal_"
+ << this->vertex_input_attributes[attribute].name << 0;
+ for (int elem = 1;
+ elem < get_matrix_location_count(this->vertex_input_attributes[attribute].type);
+ elem++) {
+ out << ",\n"
+ << "v_in.__internal_" << this->vertex_input_attributes[attribute].name << elem;
+ }
+ out << ");";
+ }
+ else {
+ /* OpenGL uses the `GPU_FETCH_*` functions which can alter how an attribute value is
+ * interpreted. In Metal, we cannot support all implicit conversions within the vertex
+ * descriptor/vertex stage-in, so we need to perform value transformation on-read.
+ *
+ * This is handled by wrapping attribute reads to local shader registers in a
+ * suitable conversion function `attribute_conversion_func_name`.
+ * This conversion function performs a specific transformation on the source
+ * vertex data, depending on the specified GPU_FETCH_* mode for the current
+ * vertex format.
+ *
+ * The fetch_mode is specified per-attribute using specialization constants
+ * on the PSO, wherein a unique set of constants is passed in per vertex
+ * buffer/format configuration. Efficiently enabling pass-through reads
+ * if no special fetch is required. */
+ bool do_attribute_conversion_on_read = false;
+ std::string attribute_conversion_func_name = get_attribute_conversion_function(
+ &do_attribute_conversion_on_read, this->vertex_input_attributes[attribute].type);
+
+ if (do_attribute_conversion_on_read) {
+ out << "\t" << attribute_conversion_func_name << "(MTL_AttributeConvert" << attribute
+ << ", v_in." << this->vertex_input_attributes[attribute].name
+ << ", vertex_shader_instance." << this->vertex_input_attributes[attribute].name << ");"
+ << std::endl;
+ }
+ else {
+ out << "\tvertex_shader_instance." << this->vertex_input_attributes[attribute].name
+ << " = v_in." << this->vertex_input_attributes[attribute].name << ";" << std::endl;
+ }
+ }
+ }
+ out << std::endl;
+ return out.str();
+}
+
+/* Copy post-main, modified, local class variables into vertex-output struct. */
+std::string MSLGeneratorInterface::generate_msl_vertex_output_population()
+{
+
+ std::stringstream out;
+ out << "\t/* Copy Vertex Outputs into output struct */" << std::endl;
+
+ /* Output gl_Position with conversion to Metal coordinate-space. */
+ if (this->uses_gl_Position) {
+ out << "\toutput._default_position_ = vertex_shader_instance.gl_Position;" << std::endl;
+
+ /* Invert Y and rescale depth range.
+ * This is an alternative method to modifying all projection matrices. */
+ out << "\toutput._default_position_.y = -output._default_position_.y;" << std::endl;
+ out << "\toutput._default_position_.z = "
+ "(output._default_position_.z+output._default_position_.w)/2.0;"
+ << std::endl;
+ }
+
+ /* Output Point-size. */
+ if (this->uses_gl_PointSize) {
+ out << "\toutput.pointsize = vertex_shader_instance.gl_PointSize;" << std::endl;
+ }
+
+ /* Output render target array Index. */
+ if (uses_mtl_array_index_) {
+ out << "\toutput.MTLRenderTargetArrayIndex = "
+ "vertex_shader_instance.MTLRenderTargetArrayIndex;"
+ << std::endl;
+ }
+
+ /* Output clip-distances. */
+ out << "#if defined(USE_CLIP_PLANES) || defined(USE_WORLD_CLIP_PLANES)" << std::endl;
+ if (this->clip_distances.size() > 1) {
+ for (int cd = 0; cd < this->clip_distances.size(); cd++) {
+ out << "\toutput.clipdistance[" << cd << "] = vertex_shader_instance.gl_ClipDistance_" << cd
+ << ";" << std::endl;
+ }
+ }
+ else if (this->clip_distances.size() > 0) {
+ out << "\toutput.clipdistance = vertex_shader_instance.gl_ClipDistance_0;" << std::endl;
+ }
+ out << "#endif" << std::endl;
+
+ /* Populate output vertex variables. */
+ int output_id = 0;
+ for (const MSLVertexOutputAttribute &v_out : this->vertex_output_varyings) {
+ if (v_out.is_array) {
+
+ for (int i = 0; i < v_out.array_elems; i++) {
+ out << "\toutput." << v_out.instance_name << "_" << v_out.name << i
+ << " = vertex_shader_instance.";
+
+ if (v_out.instance_name != "") {
+ out << v_out.instance_name << ".";
+ }
+
+ out << v_out.name << "[" << i << "]"
+ << ";" << std::endl;
+ }
+ }
+ else {
+ /* Matrix types are split into vectors and need to be reconstructed. */
+ if (is_matrix_type(v_out.type)) {
+ for (int elem = 0; elem < get_matrix_location_count(v_out.type); elem++) {
+ out << "\toutput." << v_out.instance_name << "__matrix_" << v_out.name << elem
+ << " = vertex_shader_instance.";
+
+ if (v_out.instance_name != "") {
+ out << v_out.instance_name << ".";
+ }
+
+ out << v_out.name << "[" << elem << "];" << std::endl;
+ }
+ }
+ else {
+ /* If we are not using gl_Position, first vertex output is used for position.
+ * Ensure it is vec4. If transform feedback is enabled, we do not need position. */
+ if (!this->uses_gl_Position && output_id == 0 && !this->uses_transform_feedback) {
+
+ out << "\toutput." << v_out.instance_name << "_" << v_out.name
+ << " = to_vec4(vertex_shader_instance." << v_out.name << ");" << std::endl;
+
+ /* Invert Y */
+ out << "\toutput." << v_out.instance_name << "_" << v_out.name << ".y = -output."
+ << v_out.name << ".y;" << std::endl;
+ }
+ else {
+
+ /* Assign vertex output. */
+ out << "\toutput." << v_out.instance_name << "_" << v_out.name
+ << " = vertex_shader_instance.";
+
+ if (v_out.instance_name != "") {
+ out << v_out.instance_name << ".";
+ }
+
+ out << v_out.name << ";" << std::endl;
+ }
+ }
+ }
+ output_id++;
+ }
+ out << std::endl;
+ return out.str();
+}
+
+/* Copy desired output varyings into transform feedback structure */
+std::string MSLGeneratorInterface::generate_msl_vertex_output_tf_population()
+{
+
+ std::stringstream out;
+ out << "\t/* Copy Vertex TF Outputs into transform feedback buffer */" << std::endl;
+
+ /* Populate output vertex variables */
+ /* TODO(Metal): Currently do not need to support output matrix types etc; but may need to
+ * verify for other configurations if these occur in any cases. */
+ for (int v_output = 0; v_output < this->vertex_output_varyings_tf.size(); v_output++) {
+ out << "transform_feedback_results[gl_VertexID]."
+ << this->vertex_output_varyings_tf[v_output].name << " = vertex_shader_instance."
+ << this->vertex_output_varyings_tf[v_output].name << ";" << std::endl;
+ }
+ out << std::endl;
+ return out.str();
+}
+
+/* Copy fragment stage inputs (Vertex Outputs) into local class variables. */
+std::string MSLGeneratorInterface::generate_msl_fragment_input_population()
+{
+
+ /* Populate local attribute variables. */
+ std::stringstream out;
+ out << "\t/* Copy Fragment input into local variables. */" << std::endl;
+
+ /* Special common case for gl_FragCoord, assigning to input position. */
+ if (this->uses_gl_Position) {
+ out << "\tfragment_shader_instance.gl_FragCoord = v_in._default_position_;" << std::endl;
+ }
+ else {
+ /* When gl_Position is not set, first VertexIn element is used for position. */
+ out << "\tfragment_shader_instance.gl_FragCoord = v_in."
+ << this->vertex_output_varyings[0].name << ";" << std::endl;
+ }
+
+ /* NOTE: We will only assign to the intersection of the vertex output and fragment input.
+ * Fragment input represents varying variables which are declared (but are not necessarily
+ * used). The Vertex out defines the set which is passed into the fragment shader, which
+ * contains out variables declared in the vertex shader, though these are not necessarily
+ * consumed by the fragment shader.
+ *
+ * In the cases where the fragment shader expects a variable, but it does not exist in the
+ * vertex shader, a warning will be provided. */
+ for (int f_input = (this->uses_gl_Position) ? 0 : 1;
+ f_input < this->fragment_input_varyings.size();
+ f_input++) {
+ bool exists_in_vertex_output = false;
+ for (int v_o = 0; v_o < this->vertex_output_varyings.size() && !exists_in_vertex_output;
+ v_o++) {
+ if (this->fragment_input_varyings[f_input].name == this->vertex_output_varyings[v_o].name) {
+ exists_in_vertex_output = true;
+ }
+ }
+ if (!exists_in_vertex_output) {
+ shader_debug_printf(
+ "[Warning] Fragment shader expects varying input '%s', but this is not passed from "
+ "the "
+ "vertex shader\n",
+ this->fragment_input_varyings[f_input].name.c_str());
+ continue;
+ }
+ if (this->fragment_input_varyings[f_input].is_array) {
+ for (int i = 0; i < this->fragment_input_varyings[f_input].array_elems; i++) {
+ out << "\tfragment_shader_instance.";
+
+ if (this->fragment_input_varyings[f_input].instance_name != "") {
+ out << this->fragment_input_varyings[f_input].instance_name << ".";
+ }
+
+ out << this->fragment_input_varyings[f_input].name << "[" << i << "] = v_in."
+ << this->fragment_input_varyings[f_input].instance_name << "_"
+ << this->fragment_input_varyings[f_input].name << i << ";" << std::endl;
+ }
+ }
+ else {
+ /* Matrix types are split into components and need to be regrouped into a matrix. */
+ if (is_matrix_type(this->fragment_input_varyings[f_input].type)) {
+ out << "\tfragment_shader_instance.";
+
+ if (this->fragment_input_varyings[f_input].instance_name != "") {
+ out << this->fragment_input_varyings[f_input].instance_name << ".";
+ }
+
+ out << this->fragment_input_varyings[f_input].name << " = "
+ << this->fragment_input_varyings[f_input].type;
+ int count = get_matrix_location_count(this->fragment_input_varyings[f_input].type);
+ for (int elem = 0; elem < count; elem++) {
+ out << ((elem == 0) ? "(" : "") << "v_in."
+ << this->fragment_input_varyings[f_input].instance_name << "__matrix_"
+ << this->fragment_input_varyings[f_input].name << elem
+ << ((elem < count - 1) ? ",\n" : "");
+ }
+ out << ");" << std::endl;
+ }
+ else {
+ out << "\tfragment_shader_instance.";
+
+ if (this->fragment_input_varyings[f_input].instance_name != "") {
+ out << this->fragment_input_varyings[f_input].instance_name << ".";
+ }
+
+ out << this->fragment_input_varyings[f_input].name << " = v_in."
+ << this->fragment_input_varyings[f_input].instance_name << "_"
+ << this->fragment_input_varyings[f_input].name << ";" << std::endl;
+ }
+ }
+ }
+ out << std::endl;
+ return out.str();
+}
+
+/* Copy post-main, modified, local class variables into fragment-output struct. */
+std::string MSLGeneratorInterface::generate_msl_fragment_output_population()
+{
+
+ /* Populate output fragment variables. */
+ std::stringstream out;
+ out << "\t/* Copy Fragment Outputs into output struct. */" << std::endl;
+
+ /* Output gl_FragDepth. */
+ if (this->uses_gl_FragDepth) {
+ out << "\toutput.fragdepth = fragment_shader_instance.gl_FragDepth;" << std::endl;
+ }
+
+ /* Output attributes. */
+ for (int f_output = 0; f_output < this->fragment_outputs.size(); f_output++) {
+
+ out << "\toutput." << this->fragment_outputs[f_output].name << " = fragment_shader_instance."
+ << this->fragment_outputs[f_output].name << ";" << std::endl;
+ }
+ out << std::endl;
+ return out.str();
+}
+
+std::string MSLGeneratorInterface::generate_msl_texture_vars(ShaderStage shader_stage)
+{
+ BLI_assert(shader_stage == ShaderStage::VERTEX || shader_stage == ShaderStage::FRAGMENT);
+
+ std::stringstream out;
+ out << "\t/* Populate local texture and sampler members */" << std::endl;
+ for (int i = 0; i < this->texture_samplers.size(); i++) {
+ if (bool(this->texture_samplers[i].stage & shader_stage)) {
+
+ /* Assign texture reference. */
+ out << "\t"
+ << ((shader_stage == ShaderStage::VERTEX) ? "vertex_shader_instance." :
+ "fragment_shader_instance.")
+ << this->texture_samplers[i].name << ".texture = &" << this->texture_samplers[i].name
+ << ";" << std::endl;
+
+ /* Assign sampler reference. */
+ if (this->use_argument_buffer_for_samplers()) {
+ out << "\t"
+ << ((shader_stage == ShaderStage::VERTEX) ? "vertex_shader_instance." :
+ "fragment_shader_instance.")
+ << this->texture_samplers[i].name << ".samp = &samplers.sampler_args[" << i << "];"
+ << std::endl;
+ }
+ else {
+ out << "\t"
+ << ((shader_stage == ShaderStage::VERTEX) ? "vertex_shader_instance." :
+ "fragment_shader_instance.")
+ << this->texture_samplers[i].name << ".samp = &" << this->texture_samplers[i].name
+ << "_sampler;" << std::endl;
+ }
+ }
+ }
+ out << std::endl;
+ return out.str();
+}
+
+void MSLGeneratorInterface::resolve_input_attribute_locations()
+{
+ /* Determine used-attribute-location mask. */
+ uint32_t used_locations = 0;
+ for (const MSLVertexInputAttribute &attr : vertex_input_attributes) {
+ if (attr.layout_location >= 0) {
+ /* Matrix and array types span multiple location slots. */
+ uint32_t location_element_count = get_matrix_location_count(attr.type);
+ for (uint32_t i = 1; i <= location_element_count; i++) {
+ /* Ensure our location hasn't already been used. */
+ uint32_t location_mask = (i << attr.layout_location);
+ BLI_assert((used_locations & location_mask) == 0);
+ used_locations = used_locations | location_mask;
+ }
+ }
+ }
+
+ /* Assign unused location slots to other attributes. */
+ for (MSLVertexInputAttribute &attr : vertex_input_attributes) {
+ if (attr.layout_location == -1) {
+ /* Determine number of locations required. */
+ uint32_t required_attr_slot_count = get_matrix_location_count(attr.type);
+
+ /* Determine free location.
+ * Starting from 1 is slightly less efficient, however,
+ * given multi-sized attributes, an earlier slot may remain free.
+ * given GPU_VERT_ATTR_MAX_LEN is small, this wont matter. */
+ for (int loc = 0; loc < GPU_VERT_ATTR_MAX_LEN - (required_attr_slot_count - 1); loc++) {
+
+ uint32_t location_mask = (1 << loc);
+ /* Generate sliding mask using location and required number of slots,
+ * to ensure contiguous slots are free.
+ * slot mask will be a number containing N binary 1's, where N is the
+ * number of attributes needed.
+ * e.g. N=4 -> 1111. */
+ uint32_t location_slot_mask = (1 << required_attr_slot_count) - 1;
+ uint32_t sliding_location_slot_mask = location_slot_mask << location_mask;
+ if ((used_locations & sliding_location_slot_mask) == 0) {
+ /* Assign location and update mask. */
+ attr.layout_location = loc;
+ used_locations = used_locations | location_slot_mask;
+ continue;
+ }
+ }
+
+ /* Error if could not assign attribute. */
+ MTL_LOG_ERROR("Could not assign attribute location to attribute %s for shader %s\n",
+ attr.name.c_str(),
+ this->parent_shader_.name_get());
+ }
+ }
+}
+
+void MSLGeneratorInterface::resolve_fragment_output_locations()
+{
+ int running_location_ind = 0;
+
+ /* This code works under the assumption that either all layout_locations are set,
+ * or none are. */
+ for (int i = 0; i < this->fragment_outputs.size(); i++) {
+ BLI_assert_msg(
+ ((running_location_ind > 0) ? (this->fragment_outputs[i].layout_location == -1) : true),
+ "Error: Mismatched input attributes, some with location specified, some without");
+ if (this->fragment_outputs[i].layout_location == -1) {
+ this->fragment_outputs[i].layout_location = running_location_ind;
+ running_location_ind++;
+ }
+ }
+}
+
+/**
+ * Add string to name buffer. Utility function to be used in bake_shader_interface.
+ * Returns the offset of the inserted name.
+ */
+static uint32_t name_buffer_copystr(char **name_buffer_ptr,
+ const char *str_to_copy,
+ uint32_t &name_buffer_size,
+ uint32_t &name_buffer_offset)
+{
+ /* Verify input is valid. */
+ BLI_assert(str_to_copy != nullptr);
+
+ /* Determine length of new string, and ensure name buffer is large enough. */
+ uint32_t ret_len = strlen(str_to_copy);
+ BLI_assert(ret_len > 0);
+
+ /* If required name buffer size is larger, increase by at least 128 bytes. */
+ if (name_buffer_size + ret_len > name_buffer_size) {
+ name_buffer_size = name_buffer_size + max_ii(128, ret_len);
+ *name_buffer_ptr = (char *)MEM_reallocN(*name_buffer_ptr, name_buffer_size);
+ }
+
+ /* Copy string into name buffer. */
+ uint32_t insert_offset = name_buffer_offset;
+ char *current_offset = (*name_buffer_ptr) + insert_offset;
+ strcpy(current_offset, str_to_copy);
+
+ /* Adjust offset including null terminator. */
+ name_buffer_offset += ret_len + 1;
+
+ /* Return offset into name buffer for inserted string. */
+ return insert_offset;
+}
+
+MTLShaderInterface *MSLGeneratorInterface::bake_shader_interface(const char *name)
+{
+ MTLShaderInterface *interface = new MTLShaderInterface(name);
+ interface->init();
+
+ /* Name buffer. */
+ /* Initialize name buffer. */
+ uint32_t name_buffer_size = 256;
+ uint32_t name_buffer_offset = 0;
+ interface->name_buffer_ = (char *)MEM_mallocN(name_buffer_size, "name_buffer");
+
+ /* Prepare Interface Input Attributes. */
+ int c_offset = 0;
+ for (int attribute = 0; attribute < this->vertex_input_attributes.size(); attribute++) {
+
+ /* We need a special case for handling matrix types, which splits the matrix into its vector
+ * components. */
+ if (is_matrix_type(this->vertex_input_attributes[attribute].type)) {
+
+ eMTLDataType mtl_type = to_mtl_type(
+ get_matrix_subtype(this->vertex_input_attributes[attribute].type));
+ int size = mtl_get_data_type_size(mtl_type);
+ for (int elem = 0;
+ elem < get_matrix_location_count(this->vertex_input_attributes[attribute].type);
+ elem++) {
+ /* First attribute matches the core name -- subsequent attributes tagged with
+ * `__internal_<name><index>`. */
+ std::string _internal_name = (elem == 0) ?
+ this->vertex_input_attributes[attribute].name :
+ "__internal_" +
+ this->vertex_input_attributes[attribute].name +
+ std::to_string(elem);
+
+ /* IF Using SSBO vertex Fetch, we do not need to expose other dummy attributes in the
+ * shader interface, only the first one for the whole matrix, as we can pass whatever data
+ * we want in this mode, and do not need to split attributes. */
+ if (elem == 0 || !this->uses_ssbo_vertex_fetch_mode) {
+ interface->add_input_attribute(
+ name_buffer_copystr(&interface->name_buffer_,
+ _internal_name.c_str(),
+ name_buffer_size,
+ name_buffer_offset),
+ this->vertex_input_attributes[attribute].layout_location + elem,
+ mtl_datatype_to_vertex_type(mtl_type),
+ 0,
+ size,
+ c_offset,
+ (elem == 0) ?
+ get_matrix_location_count(this->vertex_input_attributes[attribute].type) :
+ 0);
+ }
+ c_offset += size;
+ }
+ shader_debug_printf(
+ "[Note] Matrix Type '%s' added to shader interface as vertex attribute. (Elem Count: "
+ "%d)\n",
+ this->vertex_input_attributes[attribute].name.c_str(),
+ get_matrix_location_count(this->vertex_input_attributes[attribute].type));
+ }
+ else {
+
+ /* Normal attribute types. */
+ eMTLDataType mtl_type = to_mtl_type(this->vertex_input_attributes[attribute].type);
+ int size = mtl_get_data_type_size(mtl_type);
+ interface->add_input_attribute(
+ name_buffer_copystr(&interface->name_buffer_,
+ this->vertex_input_attributes[attribute].name.c_str(),
+ name_buffer_size,
+ name_buffer_offset),
+ this->vertex_input_attributes[attribute].layout_location,
+ mtl_datatype_to_vertex_type(mtl_type),
+ 0,
+ size,
+ c_offset);
+ c_offset += size;
+ }
+ }
+
+ /* Prepare Interface Default Uniform Block. */
+ interface->add_push_constant_block(name_buffer_copystr(
+ &interface->name_buffer_, "PushConstantBlock", name_buffer_size, name_buffer_offset));
+
+ for (int uniform = 0; uniform < this->uniforms.size(); uniform++) {
+ interface->add_uniform(
+ name_buffer_copystr(&interface->name_buffer_,
+ this->uniforms[uniform].name.c_str(),
+ name_buffer_size,
+ name_buffer_offset),
+ to_mtl_type(this->uniforms[uniform].type),
+ (this->uniforms[uniform].is_array) ? this->uniforms[uniform].array_elems : 1);
+ }
+
+ /* Prepare Interface Uniform Blocks. */
+ for (int uniform_block = 0; uniform_block < this->uniform_blocks.size(); uniform_block++) {
+ interface->add_uniform_block(
+ name_buffer_copystr(&interface->name_buffer_,
+ this->uniform_blocks[uniform_block].name.c_str(),
+ name_buffer_size,
+ name_buffer_offset),
+ uniform_block,
+ 0,
+ this->uniform_blocks[uniform_block].stage);
+ }
+
+ /* Texture/sampler bindings to interface. */
+ for (const MSLTextureSampler &texture_sampler : this->texture_samplers) {
+ interface->add_texture(name_buffer_copystr(&interface->name_buffer_,
+ texture_sampler.name.c_str(),
+ name_buffer_size,
+ name_buffer_offset),
+ texture_sampler.location,
+ texture_sampler.get_texture_binding_type(),
+ texture_sampler.stage);
+ }
+
+ /* Sampler Parameters. */
+ interface->set_sampler_properties(
+ this->use_argument_buffer_for_samplers(),
+ this->get_sampler_argument_buffer_bind_index(ShaderStage::VERTEX),
+ this->get_sampler_argument_buffer_bind_index(ShaderStage::FRAGMENT));
+
+ /* Map Metal bindings to standardized ShaderInput struct name/binding index. */
+ interface->prepare_common_shader_inputs();
+
+ /* Resize name buffer to save some memory. */
+ if (name_buffer_offset < name_buffer_size) {
+ interface->name_buffer_ = (char *)MEM_reallocN(interface->name_buffer_, name_buffer_offset);
+ }
+
+ return interface;
+}
+
+std::string MSLTextureSampler::get_msl_texture_type_str() const
+{
+ /* Add Types as needed. */
+ switch (this->type) {
+ case ImageType::FLOAT_1D: {
+ return "texture1d";
+ }
+ case ImageType::FLOAT_2D: {
+ return "texture2d";
+ }
+ case ImageType::FLOAT_3D: {
+ return "texture3d";
+ }
+ case ImageType::FLOAT_CUBE: {
+ return "texturecube";
+ }
+ case ImageType::FLOAT_1D_ARRAY: {
+ return "texture1d_array";
+ }
+ case ImageType::FLOAT_2D_ARRAY: {
+ return "texture2d_array";
+ }
+ case ImageType::FLOAT_CUBE_ARRAY: {
+ return "texturecube_array";
+ }
+ case ImageType::FLOAT_BUFFER: {
+ return "texture_buffer";
+ }
+ case ImageType::DEPTH_2D: {
+ return "depth2d";
+ }
+ case ImageType::SHADOW_2D: {
+ return "depth2d";
+ }
+ case ImageType::DEPTH_2D_ARRAY: {
+ return "depth2d_array";
+ }
+ case ImageType::SHADOW_2D_ARRAY: {
+ return "depth2d_array";
+ }
+ case ImageType::DEPTH_CUBE: {
+ return "depthcube";
+ }
+ case ImageType::SHADOW_CUBE: {
+ return "depthcube";
+ }
+ case ImageType::DEPTH_CUBE_ARRAY: {
+ return "depthcube_array";
+ }
+ case ImageType::SHADOW_CUBE_ARRAY: {
+ return "depthcube_array";
+ }
+ case ImageType::INT_1D: {
+ return "texture1d";
+ }
+ case ImageType::INT_2D: {
+ return "texture2d";
+ }
+ case ImageType::INT_3D: {
+ return "texture3d";
+ }
+ case ImageType::INT_CUBE: {
+ return "texturecube";
+ }
+ case ImageType::INT_1D_ARRAY: {
+ return "texture1d_array";
+ }
+ case ImageType::INT_2D_ARRAY: {
+ return "texture2d_array";
+ }
+ case ImageType::INT_CUBE_ARRAY: {
+ return "texturecube_array";
+ }
+ case ImageType::INT_BUFFER: {
+ return "texture_buffer";
+ }
+ case ImageType::UINT_1D: {
+ return "texture1d";
+ }
+ case ImageType::UINT_2D: {
+ return "texture2d";
+ }
+ case ImageType::UINT_3D: {
+ return "texture3d";
+ }
+ case ImageType::UINT_CUBE: {
+ return "texturecube";
+ }
+ case ImageType::UINT_1D_ARRAY: {
+ return "texture1d_array";
+ }
+ case ImageType::UINT_2D_ARRAY: {
+ return "texture2d_array";
+ }
+ case ImageType::UINT_CUBE_ARRAY: {
+ return "texturecube_array";
+ }
+ case ImageType::UINT_BUFFER: {
+ return "texture_buffer";
+ }
+ default: {
+ /* Unrecognized type. */
+ BLI_assert_unreachable();
+ return "ERROR";
+ }
+ };
+}
+
+std::string MSLTextureSampler::get_msl_wrapper_type_str() const
+{
+ /* Add Types as needed. */
+ switch (this->type) {
+ case ImageType::FLOAT_1D: {
+ return "_mtl_combined_image_sampler_1d";
+ }
+ case ImageType::FLOAT_2D: {
+ return "_mtl_combined_image_sampler_2d";
+ }
+ case ImageType::FLOAT_3D: {
+ return "_mtl_combined_image_sampler_3d";
+ }
+ case ImageType::FLOAT_CUBE: {
+ return "_mtl_combined_image_sampler_cube";
+ }
+ case ImageType::FLOAT_1D_ARRAY: {
+ return "_mtl_combined_image_sampler_1d_array";
+ }
+ case ImageType::FLOAT_2D_ARRAY: {
+ return "_mtl_combined_image_sampler_2d_array";
+ }
+ case ImageType::FLOAT_CUBE_ARRAY: {
+ return "_mtl_combined_image_sampler_cube_array";
+ }
+ case ImageType::FLOAT_BUFFER: {
+ return "_mtl_combined_image_sampler_buffer";
+ }
+ case ImageType::DEPTH_2D: {
+ return "_mtl_combined_image_sampler_depth_2d";
+ }
+ case ImageType::SHADOW_2D: {
+ return "_mtl_combined_image_sampler_depth_2d";
+ }
+ case ImageType::DEPTH_2D_ARRAY: {
+ return "_mtl_combined_image_sampler_depth_2d_array";
+ }
+ case ImageType::SHADOW_2D_ARRAY: {
+ return "_mtl_combined_image_sampler_depth_2d_array";
+ }
+ case ImageType::DEPTH_CUBE: {
+ return "_mtl_combined_image_sampler_depth_cube";
+ }
+ case ImageType::SHADOW_CUBE: {
+ return "_mtl_combined_image_sampler_depth_cube";
+ }
+ case ImageType::DEPTH_CUBE_ARRAY: {
+ return "_mtl_combined_image_sampler_depth_cube_array";
+ }
+ case ImageType::SHADOW_CUBE_ARRAY: {
+ return "_mtl_combined_image_sampler_depth_cube_array";
+ }
+ case ImageType::INT_1D: {
+ return "_mtl_combined_image_sampler_1d";
+ }
+ case ImageType::INT_2D: {
+ return "_mtl_combined_image_sampler_2d";
+ }
+ case ImageType::INT_3D: {
+ return "_mtl_combined_image_sampler_3d";
+ }
+ case ImageType::INT_CUBE: {
+ return "_mtl_combined_image_sampler_cube";
+ }
+ case ImageType::INT_1D_ARRAY: {
+ return "_mtl_combined_image_sampler_1d_array";
+ }
+ case ImageType::INT_2D_ARRAY: {
+ return "_mtl_combined_image_sampler_2d_array";
+ }
+ case ImageType::INT_CUBE_ARRAY: {
+ return "_mtl_combined_image_sampler_cube_array";
+ }
+ case ImageType::INT_BUFFER: {
+ return "_mtl_combined_image_sampler_buffer";
+ }
+ case ImageType::UINT_1D: {
+ return "_mtl_combined_image_sampler_1d";
+ }
+ case ImageType::UINT_2D: {
+ return "_mtl_combined_image_sampler_2d";
+ }
+ case ImageType::UINT_3D: {
+ return "_mtl_combined_image_sampler_3d";
+ }
+ case ImageType::UINT_CUBE: {
+ return "_mtl_combined_image_sampler_cube";
+ }
+ case ImageType::UINT_1D_ARRAY: {
+ return "_mtl_combined_image_sampler_1d_array";
+ }
+ case ImageType::UINT_2D_ARRAY: {
+ return "_mtl_combined_image_sampler_2d_array";
+ }
+ case ImageType::UINT_CUBE_ARRAY: {
+ return "_mtl_combined_image_sampler_cube_array";
+ }
+ case ImageType::UINT_BUFFER: {
+ return "_mtl_combined_image_sampler_buffer";
+ }
+ default: {
+ /* Unrecognized type. */
+ BLI_assert_unreachable();
+ return "ERROR";
+ }
+ };
+}
+
+std::string MSLTextureSampler::get_msl_return_type_str() const
+{
+ /* Add Types as needed */
+ switch (this->type) {
+ /* Floating point return. */
+ case ImageType::FLOAT_1D:
+ case ImageType::FLOAT_2D:
+ case ImageType::FLOAT_3D:
+ case ImageType::FLOAT_CUBE:
+ case ImageType::FLOAT_1D_ARRAY:
+ case ImageType::FLOAT_2D_ARRAY:
+ case ImageType::FLOAT_CUBE_ARRAY:
+ case ImageType::FLOAT_BUFFER:
+ case ImageType::DEPTH_2D:
+ case ImageType::SHADOW_2D:
+ case ImageType::DEPTH_2D_ARRAY:
+ case ImageType::SHADOW_2D_ARRAY:
+ case ImageType::DEPTH_CUBE:
+ case ImageType::SHADOW_CUBE:
+ case ImageType::DEPTH_CUBE_ARRAY:
+ case ImageType::SHADOW_CUBE_ARRAY: {
+ return "float";
+ }
+ /* Integer return. */
+ case ImageType::INT_1D:
+ case ImageType::INT_2D:
+ case ImageType::INT_3D:
+ case ImageType::INT_CUBE:
+ case ImageType::INT_1D_ARRAY:
+ case ImageType::INT_2D_ARRAY:
+ case ImageType::INT_CUBE_ARRAY:
+ case ImageType::INT_BUFFER: {
+ return "int";
+ }
+
+ /* Unsigned Integer return. */
+ case ImageType::UINT_1D:
+ case ImageType::UINT_2D:
+ case ImageType::UINT_3D:
+ case ImageType::UINT_CUBE:
+ case ImageType::UINT_1D_ARRAY:
+ case ImageType::UINT_2D_ARRAY:
+ case ImageType::UINT_CUBE_ARRAY:
+ case ImageType::UINT_BUFFER: {
+ return "uint32_t";
+ }
+
+ default: {
+ /* Unrecognized type. */
+ BLI_assert_unreachable();
+ return "ERROR";
+ }
+ };
+}
+
+eGPUTextureType MSLTextureSampler::get_texture_binding_type() const
+{
+ /* Add Types as needed */
+ switch (this->type) {
+ case ImageType::FLOAT_1D: {
+ return GPU_TEXTURE_1D;
+ }
+ case ImageType::FLOAT_2D: {
+ return GPU_TEXTURE_2D;
+ }
+ case ImageType::FLOAT_3D: {
+ return GPU_TEXTURE_3D;
+ }
+ case ImageType::FLOAT_CUBE: {
+ return GPU_TEXTURE_CUBE;
+ }
+ case ImageType::FLOAT_1D_ARRAY: {
+ return GPU_TEXTURE_1D_ARRAY;
+ }
+ case ImageType::FLOAT_2D_ARRAY: {
+ return GPU_TEXTURE_2D_ARRAY;
+ }
+ case ImageType::FLOAT_CUBE_ARRAY: {
+ return GPU_TEXTURE_CUBE_ARRAY;
+ }
+ case ImageType::FLOAT_BUFFER: {
+ return GPU_TEXTURE_BUFFER;
+ }
+ case ImageType::DEPTH_2D: {
+ return GPU_TEXTURE_2D;
+ }
+ case ImageType::SHADOW_2D: {
+ return GPU_TEXTURE_2D;
+ }
+ case ImageType::DEPTH_2D_ARRAY: {
+ return GPU_TEXTURE_2D_ARRAY;
+ }
+ case ImageType::SHADOW_2D_ARRAY: {
+ return GPU_TEXTURE_2D_ARRAY;
+ }
+ case ImageType::DEPTH_CUBE: {
+ return GPU_TEXTURE_CUBE;
+ }
+ case ImageType::SHADOW_CUBE: {
+ return GPU_TEXTURE_CUBE;
+ }
+ case ImageType::DEPTH_CUBE_ARRAY: {
+ return GPU_TEXTURE_CUBE_ARRAY;
+ }
+ case ImageType::SHADOW_CUBE_ARRAY: {
+ return GPU_TEXTURE_CUBE_ARRAY;
+ }
+ case ImageType::INT_1D: {
+ return GPU_TEXTURE_1D;
+ }
+ case ImageType::INT_2D: {
+ return GPU_TEXTURE_2D;
+ }
+ case ImageType::INT_3D: {
+ return GPU_TEXTURE_3D;
+ }
+ case ImageType::INT_CUBE: {
+ return GPU_TEXTURE_CUBE;
+ }
+ case ImageType::INT_1D_ARRAY: {
+ return GPU_TEXTURE_1D_ARRAY;
+ }
+ case ImageType::INT_2D_ARRAY: {
+ return GPU_TEXTURE_2D_ARRAY;
+ }
+ case ImageType::INT_CUBE_ARRAY: {
+ return GPU_TEXTURE_CUBE_ARRAY;
+ }
+ case ImageType::INT_BUFFER: {
+ return GPU_TEXTURE_BUFFER;
+ }
+ case ImageType::UINT_1D: {
+ return GPU_TEXTURE_1D;
+ }
+ case ImageType::UINT_2D: {
+ return GPU_TEXTURE_2D;
+ }
+ case ImageType::UINT_3D: {
+ return GPU_TEXTURE_3D;
+ }
+ case ImageType::UINT_CUBE: {
+ return GPU_TEXTURE_CUBE;
+ }
+ case ImageType::UINT_1D_ARRAY: {
+ return GPU_TEXTURE_1D_ARRAY;
+ }
+ case ImageType::UINT_2D_ARRAY: {
+ return GPU_TEXTURE_2D_ARRAY;
+ }
+ case ImageType::UINT_CUBE_ARRAY: {
+ return GPU_TEXTURE_CUBE_ARRAY;
+ }
+ case ImageType::UINT_BUFFER: {
+ return GPU_TEXTURE_BUFFER;
+ }
+ default: {
+ BLI_assert_unreachable();
+ return GPU_TEXTURE_2D;
+ }
+ };
+}
+
+/** \} */
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_shader_interface.hh b/source/blender/gpu/metal/mtl_shader_interface.hh
new file mode 100644
index 00000000000..0da84cad997
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_shader_interface.hh
@@ -0,0 +1,267 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_vector.hh"
+
+#include "gpu_shader_interface.hh"
+#include "mtl_capabilities.hh"
+#include "mtl_shader_interface_type.hh"
+
+#include "GPU_common.h"
+#include "GPU_common_types.h"
+#include "GPU_texture.h"
+#include "gpu_texture_private.hh"
+#include <Metal/Metal.h>
+#include <functional>
+
+namespace blender::gpu {
+
+/* #MTLShaderInterface describes the layout and properties of a given shader,
+ * including input and output bindings, and any special properties or modes
+ * that the shader may require.
+ *
+ * -- Shader input/output bindings --
+ *
+ * We require custom data-structures for the binding information in Metal.
+ * This is because certain bindings contain and require more information to
+ * be stored than can be tracked solely within the `ShaderInput` struct.
+ * e.g. data sizes and offsets.
+ *
+ * Upon interface completion, `prepare_common_shader_inputs` is used to
+ * populate the global `ShaderInput*` array to enable correct functionality
+ * of shader binding location lookups. These returned locations act as indices
+ * into the arrays stored here in the #MTLShaderInterface, such that extraction
+ * of required information can be performed within the back-end.
+ *
+ * e.g. `int loc = GPU_shader_get_uniform(...)`
+ * `loc` will match the index into the `MTLShaderUniform uniforms_[]` array
+ * to fetch the required Metal specific information.
+ *
+ *
+ *
+ * -- Argument Buffers and Argument Encoders --
+ *
+ * We can use #ArgumentBuffers (AB's) in Metal to extend the resource bind limitations
+ * by providing bind-less support.
+ *
+ * Argument Buffers are used for sampler bindings when the builtin
+ * sampler limit of 16 is exceeded, as in all cases for Blender,
+ * each individual texture is associated with a given sampler, and this
+ * lower limit would otherwise reduce the total availability of textures
+ * used in shaders.
+ *
+ * In future, argument buffers may be extended to support other resource
+ * types, if overall bind limits are ever increased within Blender.
+ *
+ * The #ArgumentEncoder cache used to store the generated #ArgumentEncoders for a given
+ * shader permutation. The #ArgumentEncoder is the resource used to write resource binding
+ * information to a specified buffer, and is unique to the shader's resource interface.
+ */
+
+enum class ShaderStage : uint32_t {
+ VERTEX = 1 << 0,
+ FRAGMENT = 1 << 1,
+ BOTH = (ShaderStage::VERTEX | ShaderStage::FRAGMENT),
+};
+ENUM_OPERATORS(ShaderStage, ShaderStage::BOTH);
+
+inline uint get_shader_stage_index(ShaderStage stage)
+{
+ switch (stage) {
+ case ShaderStage::VERTEX:
+ return 0;
+ case ShaderStage::FRAGMENT:
+ return 1;
+ default:
+ BLI_assert_unreachable();
+ return 0;
+ }
+ return 0;
+}
+
+/* Shader input/output binding information. */
+struct MTLShaderInputAttribute {
+ uint32_t name_offset;
+ MTLVertexFormat format;
+ uint32_t index;
+ uint32_t location;
+ uint32_t size;
+ uint32_t buffer_index;
+ uint32_t offset;
+ /* For attributes of Matrix/array types, we need to insert "fake" attributes for
+ * each element, as matrix types are not natively supported.
+ *
+ * > 1 if matrix/arrays are used, specifying number of elements.
+ * = 1 for non-matrix types
+ * = 0 if used as a dummy slot for "fake" matrix attributes. */
+ uint32_t matrix_element_count;
+};
+
+struct MTLShaderUniformBlock {
+ uint32_t name_offset;
+ uint32_t size = 0;
+ /* Buffer resource bind index in shader `[[buffer(index)]]`. */
+ uint32_t buffer_index;
+
+ /* Tracking for manual uniform addition. */
+ uint32_t current_offset;
+ ShaderStage stage_mask;
+};
+
+struct MTLShaderUniform {
+ uint32_t name_offset;
+ /* Index of `MTLShaderUniformBlock` this uniform belongs to. */
+ uint32_t size_in_bytes;
+ uint32_t byte_offset;
+ eMTLDataType type;
+ uint32_t array_len;
+};
+
+struct MTLShaderTexture {
+ bool used;
+ uint32_t name_offset;
+ /* Texture resource bind slot in shader `[[texture(n)]]`. */
+ int slot_index;
+ eGPUTextureType type;
+ ShaderStage stage_mask;
+};
+
+struct MTLShaderSampler {
+ uint32_t name_offset;
+ /* Sampler resource bind slot in shader `[[sampler(n)]]`. */
+ uint32_t slot_index = 0;
+};
+
+/* Utility Functions. */
+MTLVertexFormat mtl_datatype_to_vertex_type(eMTLDataType type);
+
+/**
+ * Implementation of Shader interface for Metal Back-end.
+ **/
+class MTLShaderInterface : public ShaderInterface {
+
+ private:
+ /* Argument encoders caching.
+ * Static size is based on common input permutation variations. */
+ static const int ARGUMENT_ENCODERS_CACHE_SIZE = 3;
+ struct ArgumentEncoderCacheEntry {
+ id<MTLArgumentEncoder> encoder;
+ int buffer_index;
+ };
+ ArgumentEncoderCacheEntry arg_encoders_[ARGUMENT_ENCODERS_CACHE_SIZE] = {};
+
+ /* Vertex input Attributes. */
+ uint32_t total_attributes_;
+ uint32_t total_vert_stride_;
+ MTLShaderInputAttribute attributes_[MTL_MAX_VERTEX_INPUT_ATTRIBUTES];
+
+ /* Uniforms. */
+ uint32_t total_uniforms_;
+ MTLShaderUniform uniforms_[MTL_MAX_UNIFORMS_PER_BLOCK];
+
+ /* Uniform Blocks. */
+ uint32_t total_uniform_blocks_;
+ MTLShaderUniformBlock ubos_[MTL_MAX_UNIFORM_BUFFER_BINDINGS];
+ MTLShaderUniformBlock push_constant_block_;
+
+ /* Textures. */
+ /* Textures support explicit binding indices, so some texture slots
+ * remain unused. */
+ uint32_t total_textures_;
+ int max_texture_index_;
+ MTLShaderTexture textures_[MTL_MAX_TEXTURE_SLOTS];
+
+ /* Whether argument buffers are used for sampler bindings. */
+ bool sampler_use_argument_buffer_;
+ int sampler_argument_buffer_bind_index_vert_;
+ int sampler_argument_buffer_bind_index_frag_;
+
+ /* Attribute Mask. */
+ uint32_t enabled_attribute_mask_;
+
+ /* Debug. */
+ char name[256];
+
+ public:
+ MTLShaderInterface(const char *name);
+ ~MTLShaderInterface();
+
+ void init();
+ void add_input_attribute(uint32_t name_offset,
+ uint32_t attribute_location,
+ MTLVertexFormat format,
+ uint32_t buffer_index,
+ uint32_t size,
+ uint32_t offset,
+ int matrix_element_count = 1);
+ uint32_t add_uniform_block(uint32_t name_offset,
+ uint32_t buffer_index,
+ uint32_t size,
+ ShaderStage stage_mask = ShaderStage::BOTH);
+ void add_uniform(uint32_t name_offset, eMTLDataType type, int array_len = 1);
+ void add_texture(uint32_t name_offset,
+ uint32_t texture_slot,
+ eGPUTextureType tex_binding_type,
+ ShaderStage stage_mask = ShaderStage::FRAGMENT);
+ void add_push_constant_block(uint32_t name_offset);
+
+ /* Resolve and cache locations of builtin uniforms and uniform blocks. */
+ void map_builtins();
+ void set_sampler_properties(bool use_argument_buffer,
+ uint32_t argument_buffer_bind_index_vert,
+ uint32_t argument_buffer_bind_index_frag);
+
+ /* Prepare #ShaderInput interface for binding resolution. */
+ void prepare_common_shader_inputs();
+
+ /* Fetch Uniforms. */
+ const MTLShaderUniform &get_uniform(uint index) const;
+ uint32_t get_total_uniforms() const;
+
+ /* Fetch Uniform Blocks. */
+ const MTLShaderUniformBlock &get_uniform_block(uint index) const;
+ uint32_t get_total_uniform_blocks() const;
+ bool has_uniform_block(uint32_t block_index) const;
+ uint32_t get_uniform_block_size(uint32_t block_index) const;
+
+ /* Push constant uniform data block should always be available. */
+ const MTLShaderUniformBlock &get_push_constant_block() const;
+
+ /* Fetch textures. */
+ const MTLShaderTexture &get_texture(uint index) const;
+ uint32_t get_total_textures() const;
+ uint32_t get_max_texture_index() const;
+ bool get_use_argument_buffer_for_samplers(int *vertex_arg_buffer_bind_index,
+ int *fragment_arg_buffer_bind_index) const;
+
+ /* Fetch Attributes. */
+ const MTLShaderInputAttribute &get_attribute(uint index) const;
+ uint32_t get_total_attributes() const;
+ uint32_t get_total_vertex_stride() const;
+ uint32_t get_enabled_attribute_mask() const;
+
+ /* Name buffer fetching. */
+ const char *get_name_at_offset(uint32_t offset) const;
+
+ /* Interface name. */
+ const char *get_name() const
+ {
+ return this->name;
+ }
+
+ /* Argument buffer encoder management. */
+ id<MTLArgumentEncoder> find_argument_encoder(int buffer_index) const;
+
+ void insert_argument_encoder(int buffer_index, id encoder);
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("MTLShaderInterface");
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_shader_interface.mm b/source/blender/gpu/metal/mtl_shader_interface.mm
new file mode 100644
index 00000000000..3703d5b5684
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_shader_interface.mm
@@ -0,0 +1,604 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ *
+ * GPU shader interface (C --> GLSL)
+ */
+
+#include "BLI_bitmap.h"
+
+#include "GPU_capabilities.h"
+
+#include "mtl_common.hh"
+#include "mtl_debug.hh"
+#include "mtl_shader_interface.hh"
+#include "mtl_shader_interface_type.hh"
+
+#include "BLI_blenlib.h"
+#include "BLI_math_base.h"
+#include "BLI_utildefines.h"
+#include "MEM_guardedalloc.h"
+
+namespace blender::gpu {
+
+MTLShaderInterface::MTLShaderInterface(const char *name)
+{
+ /* Shared ShaderInputs array is populated later on in `prepare_common_shader_inputs`
+ * after Metal Shader Interface preparation. */
+ inputs_ = nullptr;
+
+ if (name != nullptr) {
+ strcpy(this->name, name);
+ }
+
+ /* Ensure #ShaderInterface parameters are cleared. */
+ this->init();
+}
+
+MTLShaderInterface::~MTLShaderInterface()
+{
+ for (const int i : IndexRange(ARGUMENT_ENCODERS_CACHE_SIZE)) {
+ if (arg_encoders_[i].encoder != nil) {
+ id<MTLArgumentEncoder> enc = arg_encoders_[i].encoder;
+ [enc release];
+ }
+ }
+}
+
+const char *MTLShaderInterface::get_name_at_offset(uint32_t offset) const
+{
+ return name_buffer_ + offset;
+}
+
+void MTLShaderInterface::init()
+{
+ total_attributes_ = 0;
+ total_uniform_blocks_ = 0;
+ total_uniforms_ = 0;
+ total_textures_ = 0;
+ max_texture_index_ = -1;
+ enabled_attribute_mask_ = 0;
+ total_vert_stride_ = 0;
+ sampler_use_argument_buffer_ = false;
+ sampler_argument_buffer_bind_index_vert_ = -1;
+ sampler_argument_buffer_bind_index_frag_ = -1;
+
+ /* NULL initialize uniform location markers for builtins. */
+ for (const int u : IndexRange(GPU_NUM_UNIFORMS)) {
+ builtins_[u] = -1;
+ }
+ for (const int ubo : IndexRange(GPU_NUM_UNIFORM_BLOCKS)) {
+ builtin_blocks_[ubo] = -1;
+ }
+ for (const int tex : IndexRange(MTL_MAX_TEXTURE_SLOTS)) {
+ textures_[tex].used = false;
+ textures_[tex].slot_index = -1;
+ }
+
+ /* Null initialization for argument encoders. */
+ for (const int i : IndexRange(ARGUMENT_ENCODERS_CACHE_SIZE)) {
+ arg_encoders_[i].encoder = nil;
+ arg_encoders_[i].buffer_index = -1;
+ }
+}
+
+void MTLShaderInterface::add_input_attribute(uint32_t name_offset,
+ uint32_t attribute_location,
+ MTLVertexFormat format,
+ uint32_t buffer_index,
+ uint32_t size,
+ uint32_t offset,
+ int matrix_element_count)
+{
+ MTLShaderInputAttribute &input_attr = attributes_[total_attributes_];
+ input_attr.name_offset = name_offset;
+ input_attr.format = format;
+ input_attr.location = attribute_location;
+ input_attr.size = size;
+ input_attr.buffer_index = buffer_index;
+ input_attr.offset = offset;
+ input_attr.matrix_element_count = matrix_element_count;
+ input_attr.index = total_attributes_;
+ total_attributes_++;
+ total_vert_stride_ = max_ii(total_vert_stride_, offset + size);
+ enabled_attribute_mask_ |= (1 << attribute_location);
+}
+
+uint32_t MTLShaderInterface::add_uniform_block(uint32_t name_offset,
+ uint32_t buffer_index,
+ uint32_t size,
+ ShaderStage stage_mask)
+{
+ /* Ensure Size is 16 byte aligned to guarantees alignment rules are satisfied. */
+ if ((size % 16) != 0) {
+ size += 16 - (size % 16);
+ }
+
+ MTLShaderUniformBlock &uni_block = ubos_[total_uniform_blocks_];
+ uni_block.name_offset = name_offset;
+ /* We offset the buffer binding index by one, as the first slot is reserved for push constant
+ * data. */
+ uni_block.buffer_index = buffer_index + 1;
+ uni_block.size = size;
+ uni_block.current_offset = 0;
+ uni_block.stage_mask = ShaderStage::BOTH;
+ return (total_uniform_blocks_++);
+}
+
+void MTLShaderInterface::add_push_constant_block(uint32_t name_offset)
+{
+ push_constant_block_.name_offset = name_offset;
+ /* Push constant data block is always uniform buffer index 0. */
+ push_constant_block_.buffer_index = 0;
+ /* Size starts at zero and grows as uniforms are added. */
+ push_constant_block_.size = 0;
+
+ push_constant_block_.current_offset = 0;
+ push_constant_block_.stage_mask = ShaderStage::BOTH;
+}
+
+void MTLShaderInterface::add_uniform(uint32_t name_offset, eMTLDataType type, int array_len)
+{
+ BLI_assert(array_len > 0);
+ BLI_assert(total_uniforms_ < MTL_MAX_UNIFORMS_PER_BLOCK);
+ if (total_uniforms_ >= MTL_MAX_UNIFORMS_PER_BLOCK) {
+ MTL_LOG_WARNING(
+ "[Warning] Cannot add uniform '%s' to shader interface '%s' as the uniform limit of %d "
+ "has been reached.\n",
+ name,
+ name,
+ MTL_MAX_UNIFORMS_PER_BLOCK);
+ return;
+ }
+ MTLShaderUniform &uniform = uniforms_[total_uniforms_];
+ uniform.name_offset = name_offset;
+
+ /* Determine size and offset alignment -- C++ struct alignment rules: Base address of value must
+ * match alignment of type. GLSL follows minimum type alignment of 4. */
+ int data_type_size = mtl_get_data_type_size(type) * array_len;
+ int data_type_alignment = max_ii(mtl_get_data_type_alignment(type), 4);
+ int current_offset = push_constant_block_.current_offset;
+ if ((current_offset % data_type_alignment) != 0) {
+ current_offset += data_type_alignment - (current_offset % data_type_alignment);
+ }
+
+ uniform.size_in_bytes = data_type_size;
+ uniform.byte_offset = current_offset;
+ uniform.type = type;
+ uniform.array_len = array_len;
+ total_uniforms_++;
+
+ /* Update Push constant block-- update offset, re-size and re-align total memory requirement to
+ * be 16-byte aligned. Following GLSL std140. */
+ push_constant_block_.current_offset = current_offset + data_type_size;
+ if (push_constant_block_.current_offset > push_constant_block_.size) {
+ push_constant_block_.size = push_constant_block_.current_offset;
+ if ((push_constant_block_.size % 16) != 0) {
+ push_constant_block_.size += 16 - (push_constant_block_.size % 16);
+ }
+ }
+
+ /* Validate properties. */
+ BLI_assert(uniform.size_in_bytes > 0);
+ BLI_assert_msg(
+ current_offset + data_type_size <= push_constant_block_.size,
+ "Uniform size and offset sits outside the specified size range for the uniform block");
+}
+
+void MTLShaderInterface::add_texture(uint32_t name_offset,
+ uint32_t texture_slot,
+ eGPUTextureType tex_binding_type,
+ ShaderStage stage_mask)
+{
+ BLI_assert(texture_slot >= 0 && texture_slot < GPU_max_textures());
+ if (texture_slot >= 0 && texture_slot < GPU_max_textures()) {
+
+ MTLShaderTexture &tex = textures_[texture_slot];
+ BLI_assert_msg(tex.used == false, "Texture slot already in-use by another binding");
+ tex.name_offset = name_offset;
+ tex.slot_index = texture_slot;
+ tex.type = tex_binding_type;
+ tex.stage_mask = stage_mask;
+ tex.used = true;
+ total_textures_++;
+ max_texture_index_ = max_ii(max_texture_index_, texture_slot);
+ }
+ else {
+ BLI_assert_msg(false, "Exceeding maximum supported texture count.");
+ MTL_LOG_WARNING(
+ "Could not add additional texture with index %d to shader interface. Maximum "
+ "supported texture count is %d\n",
+ texture_slot,
+ GPU_max_textures());
+ }
+}
+
+void MTLShaderInterface::map_builtins()
+{
+ /* Clear builtin arrays to NULL locations. */
+ for (const int u : IndexRange(GPU_NUM_UNIFORMS)) {
+ builtins_[u] = -1;
+ }
+ for (const int ubo : IndexRange(GPU_NUM_UNIFORM_BLOCKS)) {
+ builtin_blocks_[ubo] = -1;
+ }
+
+ /* Resolve and cache uniform locations for builtin uniforms. */
+ for (const int u : IndexRange(GPU_NUM_UNIFORMS)) {
+ const ShaderInput *uni = this->uniform_get(builtin_uniform_name((GPUUniformBuiltin)u));
+ if (uni != nullptr) {
+ BLI_assert(uni->location >= 0);
+ if (uni->location >= 0) {
+ builtins_[u] = uni->location;
+ MTL_LOG_INFO("Mapped builtin uniform '%s' NB: '%s' to location: %d\n",
+ builtin_uniform_name((GPUUniformBuiltin)u),
+ get_name_at_offset(uni->name_offset),
+ uni->location);
+ }
+ }
+ }
+
+ /* Resolve and cache uniform locations for builtin uniform blocks. */
+ for (const int u : IndexRange(GPU_NUM_UNIFORM_BLOCKS)) {
+ const ShaderInput *uni = this->ubo_get(builtin_uniform_block_name((GPUUniformBlockBuiltin)u));
+
+ if (uni != nullptr) {
+ BLI_assert(uni->location >= 0);
+ if (uni->location >= 0) {
+ builtin_blocks_[u] = uni->binding;
+ MTL_LOG_INFO("Mapped builtin uniform block '%s' to location %d\n",
+ builtin_uniform_block_name((GPUUniformBlockBuiltin)u),
+ uni->location);
+ }
+ }
+ }
+}
+
+/* Populate #ShaderInput struct based on interface. */
+void MTLShaderInterface::prepare_common_shader_inputs()
+{
+ /* `ShaderInput inputs_` maps a uniform name to an external
+ * uniform location, which is used as an array index to look-up
+ * information in the local #MTLShaderInterface input structs.
+ *
+ * #ShaderInput population follows the ordering rules in #gpu_shader_interface. */
+
+ /* Populate #ShaderInterface counts. */
+ attr_len_ = this->get_total_attributes();
+ ubo_len_ = this->get_total_uniform_blocks();
+ uniform_len_ = this->get_total_uniforms() + this->get_total_textures();
+
+ /* TODO(Metal): Support storage buffer bindings. Pending compute shader support. */
+ ssbo_len_ = 0;
+
+ /* Calculate total inputs and allocate #ShaderInput array. */
+ /* NOTE: We use the existing `name_buffer_` allocated for internal input structs. */
+ int input_tot_len = attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_;
+ inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__);
+ ShaderInput *current_input = inputs_;
+
+ /* Attributes. */
+ for (const int attr_index : IndexRange(total_attributes_)) {
+ MTLShaderInputAttribute &shd_attr = attributes_[attr_index];
+ current_input->name_offset = shd_attr.name_offset;
+ current_input->name_hash = BLI_hash_string(this->get_name_at_offset(shd_attr.name_offset));
+ current_input->location = attr_index;
+ current_input->binding = attr_index;
+ current_input++;
+ }
+
+ /* UBOs. */
+ BLI_assert(&inputs_[attr_len_] >= current_input);
+ current_input = &inputs_[attr_len_];
+ for (const int ubo_index : IndexRange(total_uniform_blocks_)) {
+ MTLShaderUniformBlock &shd_ubo = ubos_[ubo_index];
+ current_input->name_offset = shd_ubo.name_offset;
+ current_input->name_hash = BLI_hash_string(this->get_name_at_offset(shd_ubo.name_offset));
+ /* Location refers to the index in the ubos_ array. */
+ current_input->location = ubo_index;
+ /* Final binding location refers to the buffer binding index within the shader (Relative to
+ * MTL_uniform_buffer_base_index). */
+ current_input->binding = shd_ubo.buffer_index;
+ current_input++;
+ }
+
+ /* Uniforms. */
+ BLI_assert(&inputs_[attr_len_ + ubo_len_] >= current_input);
+ current_input = &inputs_[attr_len_ + ubo_len_];
+ for (const int uniform_index : IndexRange(total_uniforms_)) {
+ MTLShaderUniform &shd_uni = uniforms_[uniform_index];
+ current_input->name_offset = shd_uni.name_offset;
+ current_input->name_hash = BLI_hash_string(this->get_name_at_offset(shd_uni.name_offset));
+ current_input->location = uniform_index;
+ current_input->binding = uniform_index;
+ current_input++;
+ }
+
+ /* Textures.
+ * NOTE(Metal): Textures are externally treated as uniforms in #gpu_shader_interface.
+ * Location for textures resolved as `binding` value. This
+ * is the index into the local `MTLShaderTexture textures[]` array.
+ *
+ * In MSL, we cannot trivially remap which texture slot a given texture
+ * handle points to, unlike in GLSL, where a uniform sampler/image can be updated
+ * and queried as both a texture and a uniform. */
+ for (int texture_index = 0; texture_index <= max_texture_index_; texture_index++) {
+ const MTLShaderTexture &shd_tex = textures_[texture_index];
+
+ /* Not all texture entries are used when explicit texture locations are specified. */
+ if (shd_tex.used) {
+ BLI_assert_msg(shd_tex.slot_index == texture_index,
+ "Texture binding slot should match array index for texture.");
+ current_input->name_offset = shd_tex.name_offset;
+ current_input->name_hash = BLI_hash_string(this->get_name_at_offset(shd_tex.name_offset));
+
+ /* Location represents look-up address.
+ * For Metal, this location is a unique value offset by
+ * total_uniforms such that it does not overlap.
+ *
+ * This range offset allows a check in the uniform look-up
+ * to ensure texture handles are not treated as standard uniforms in Metal. */
+ current_input->location = texture_index + total_uniforms_;
+
+ /* Binding represents texture slot `[[texture(n)]]`. */
+ current_input->binding = shd_tex.slot_index;
+ current_input++;
+ }
+ }
+
+ /* SSBO bindings.
+ * TODO(Metal): Support SSBOs. Pending compute support. */
+ BLI_assert(&inputs_[attr_len_ + ubo_len_ + uniform_len_] >= current_input);
+ current_input = &inputs_[attr_len_ + ubo_len_ + uniform_len_];
+
+ /* Map builtin uniform indices to uniform binding locations. */
+ this->map_builtins();
+}
+
+void MTLShaderInterface::set_sampler_properties(bool use_argument_buffer,
+ uint32_t argument_buffer_bind_index_vert,
+ uint32_t argument_buffer_bind_index_frag)
+{
+ sampler_use_argument_buffer_ = use_argument_buffer;
+ sampler_argument_buffer_bind_index_vert_ = argument_buffer_bind_index_vert;
+ sampler_argument_buffer_bind_index_frag_ = argument_buffer_bind_index_frag;
+}
+
+/* Attributes. */
+const MTLShaderInputAttribute &MTLShaderInterface::get_attribute(uint index) const
+{
+ BLI_assert(index < MTL_MAX_VERTEX_INPUT_ATTRIBUTES);
+ BLI_assert(index < get_total_attributes());
+ return attributes_[index];
+}
+
+uint32_t MTLShaderInterface::get_total_attributes() const
+{
+ return total_attributes_;
+}
+
+uint32_t MTLShaderInterface::get_total_vertex_stride() const
+{
+ return total_vert_stride_;
+}
+
+uint32_t MTLShaderInterface::get_enabled_attribute_mask() const
+{
+ return enabled_attribute_mask_;
+}
+
+/* Uniforms. */
+const MTLShaderUniform &MTLShaderInterface::get_uniform(uint index) const
+{
+ BLI_assert(index < MTL_MAX_UNIFORMS_PER_BLOCK);
+ BLI_assert(index < get_total_uniforms());
+ return uniforms_[index];
+}
+
+uint32_t MTLShaderInterface::get_total_uniforms() const
+{
+ return total_uniforms_;
+}
+
+/* Uniform Blocks. */
+const MTLShaderUniformBlock &MTLShaderInterface::get_uniform_block(uint index) const
+{
+ BLI_assert(index < MTL_MAX_UNIFORM_BUFFER_BINDINGS);
+ BLI_assert(index < get_total_uniform_blocks());
+ return ubos_[index];
+}
+
+const MTLShaderUniformBlock &MTLShaderInterface::get_push_constant_block() const
+{
+ return push_constant_block_;
+}
+
+uint32_t MTLShaderInterface::get_total_uniform_blocks() const
+{
+ return total_uniform_blocks_;
+}
+
+bool MTLShaderInterface::has_uniform_block(uint32_t block_index) const
+{
+ return (block_index < total_uniform_blocks_);
+}
+
+uint32_t MTLShaderInterface::get_uniform_block_size(uint32_t block_index) const
+{
+ return (block_index < total_uniform_blocks_) ? ubos_[block_index].size : 0;
+}
+
+/* Textures. */
+const MTLShaderTexture &MTLShaderInterface::get_texture(uint index) const
+{
+ BLI_assert(index < MTL_MAX_TEXTURE_SLOTS);
+ BLI_assert(index <= get_max_texture_index());
+ return textures_[index];
+}
+
+uint32_t MTLShaderInterface::get_total_textures() const
+{
+ return total_textures_;
+}
+
+uint32_t MTLShaderInterface::get_max_texture_index() const
+{
+ return max_texture_index_;
+}
+
+bool MTLShaderInterface::get_use_argument_buffer_for_samplers(
+ int *vertex_arg_buffer_bind_index, int *fragment_arg_buffer_bind_index) const
+{
+ /* Returns argument buffer binding slot for each shader stage.
+ * The exact bind slot may be different, as each stage has different buffer inputs. */
+ *vertex_arg_buffer_bind_index = sampler_argument_buffer_bind_index_vert_;
+ *fragment_arg_buffer_bind_index = sampler_argument_buffer_bind_index_frag_;
+ return sampler_use_argument_buffer_;
+}
+
+id<MTLArgumentEncoder> MTLShaderInterface::find_argument_encoder(int buffer_index) const
+{
+ id encoder = nil;
+ for (const int i : IndexRange(ARGUMENT_ENCODERS_CACHE_SIZE)) {
+ encoder = arg_encoders_[i].buffer_index == buffer_index ? arg_encoders_[i].encoder : encoder;
+ }
+ return encoder;
+}
+
+void MTLShaderInterface::insert_argument_encoder(int buffer_index, id encoder)
+{
+ for (const int i : IndexRange(ARGUMENT_ENCODERS_CACHE_SIZE)) {
+ if (arg_encoders_[i].encoder == nil) {
+ arg_encoders_[i].encoder = encoder;
+ arg_encoders_[i].buffer_index = buffer_index;
+ return;
+ }
+ }
+ MTL_LOG_WARNING("could not insert encoder into cache!");
+}
+
+MTLVertexFormat mtl_datatype_to_vertex_type(eMTLDataType type)
+{
+ switch (type) {
+ case MTL_DATATYPE_CHAR:
+ return MTLVertexFormatChar;
+ case MTL_DATATYPE_UCHAR:
+ return MTLVertexFormatUChar;
+ case MTL_DATATYPE_BOOL:
+ return MTLVertexFormatUChar;
+ case MTL_DATATYPE_CHAR2:
+ return MTLVertexFormatChar2;
+ case MTL_DATATYPE_UCHAR2:
+ return MTLVertexFormatUChar2;
+ case MTL_DATATYPE_BOOL2:
+ return MTLVertexFormatUChar2;
+ case MTL_DATATYPE_SHORT:
+ return MTLVertexFormatShort;
+ case MTL_DATATYPE_USHORT:
+ return MTLVertexFormatUShort;
+ case MTL_DATATYPE_CHAR3:
+ return MTLVertexFormatChar3;
+ case MTL_DATATYPE_UCHAR3:
+ return MTLVertexFormatUChar3;
+ case MTL_DATATYPE_BOOL3:
+ return MTLVertexFormatUChar3;
+ case MTL_DATATYPE_CHAR4:
+ return MTLVertexFormatChar4;
+ case MTL_DATATYPE_UCHAR4:
+ return MTLVertexFormatUChar4;
+ case MTL_DATATYPE_INT:
+ return MTLVertexFormatInt;
+ case MTL_DATATYPE_UINT:
+ return MTLVertexFormatUInt;
+ case MTL_DATATYPE_BOOL4:
+ return MTLVertexFormatUChar4;
+ case MTL_DATATYPE_SHORT2:
+ return MTLVertexFormatShort2;
+ case MTL_DATATYPE_USHORT2:
+ return MTLVertexFormatUShort2;
+ case MTL_DATATYPE_FLOAT:
+ return MTLVertexFormatFloat;
+ case MTL_DATATYPE_HALF2x2:
+ case MTL_DATATYPE_HALF3x2:
+ case MTL_DATATYPE_HALF4x2:
+ BLI_assert_msg(false, "Unsupported raw vertex attribute types in Blender.");
+ return MTLVertexFormatInvalid;
+
+ case MTL_DATATYPE_SHORT3:
+ return MTLVertexFormatShort3;
+ case MTL_DATATYPE_USHORT3:
+ return MTLVertexFormatUShort3;
+ case MTL_DATATYPE_SHORT4:
+ return MTLVertexFormatShort4;
+ case MTL_DATATYPE_USHORT4:
+ return MTLVertexFormatUShort4;
+ case MTL_DATATYPE_INT2:
+ return MTLVertexFormatInt2;
+ case MTL_DATATYPE_UINT2:
+ return MTLVertexFormatUInt2;
+ case MTL_DATATYPE_FLOAT2:
+ return MTLVertexFormatFloat2;
+ case MTL_DATATYPE_LONG:
+ return MTLVertexFormatInt;
+ case MTL_DATATYPE_ULONG:
+ return MTLVertexFormatUInt;
+ case MTL_DATATYPE_HALF2x3:
+ case MTL_DATATYPE_HALF2x4:
+ case MTL_DATATYPE_HALF3x3:
+ case MTL_DATATYPE_HALF3x4:
+ case MTL_DATATYPE_HALF4x3:
+ case MTL_DATATYPE_HALF4x4:
+ case MTL_DATATYPE_FLOAT2x2:
+ case MTL_DATATYPE_FLOAT3x2:
+ case MTL_DATATYPE_FLOAT4x2:
+ BLI_assert_msg(false, "Unsupported raw vertex attribute types in Blender.");
+ return MTLVertexFormatInvalid;
+
+ case MTL_DATATYPE_INT3:
+ return MTLVertexFormatInt3;
+ case MTL_DATATYPE_INT4:
+ return MTLVertexFormatInt4;
+ case MTL_DATATYPE_UINT3:
+ return MTLVertexFormatUInt3;
+ case MTL_DATATYPE_UINT4:
+ return MTLVertexFormatUInt4;
+ case MTL_DATATYPE_FLOAT3:
+ return MTLVertexFormatFloat3;
+ case MTL_DATATYPE_FLOAT4:
+ return MTLVertexFormatFloat4;
+ case MTL_DATATYPE_LONG2:
+ return MTLVertexFormatInt2;
+ case MTL_DATATYPE_ULONG2:
+ return MTLVertexFormatUInt2;
+ case MTL_DATATYPE_FLOAT2x3:
+ case MTL_DATATYPE_FLOAT2x4:
+ case MTL_DATATYPE_FLOAT3x3:
+ case MTL_DATATYPE_FLOAT3x4:
+ case MTL_DATATYPE_FLOAT4x3:
+ case MTL_DATATYPE_FLOAT4x4:
+ BLI_assert_msg(false, "Unsupported raw vertex attribute types in Blender.");
+ return MTLVertexFormatInvalid;
+
+ case MTL_DATATYPE_LONG3:
+ return MTLVertexFormatInt3;
+ case MTL_DATATYPE_LONG4:
+ return MTLVertexFormatInt4;
+ case MTL_DATATYPE_ULONG3:
+ return MTLVertexFormatUInt3;
+ case MTL_DATATYPE_ULONG4:
+ return MTLVertexFormatUInt4;
+
+ /* Special Types */
+ case MTL_DATATYPE_UINT1010102_NORM:
+ return MTLVertexFormatUInt1010102Normalized;
+ case MTL_DATATYPE_INT1010102_NORM:
+ return MTLVertexFormatInt1010102Normalized;
+
+ default:
+ BLI_assert(false);
+ return MTLVertexFormatInvalid;
+ };
+}
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_shader_interface_type.hh b/source/blender/gpu/metal/mtl_shader_interface_type.hh
new file mode 100644
index 00000000000..3c4c87ee25b
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_shader_interface_type.hh
@@ -0,0 +1,251 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+#pragma once
+
+#include "BLI_assert.h"
+
+enum eMTLDataType {
+ MTL_DATATYPE_CHAR,
+ MTL_DATATYPE_CHAR2,
+ MTL_DATATYPE_CHAR3,
+ MTL_DATATYPE_CHAR4,
+
+ MTL_DATATYPE_UCHAR,
+ MTL_DATATYPE_UCHAR2,
+ MTL_DATATYPE_UCHAR3,
+ MTL_DATATYPE_UCHAR4,
+
+ MTL_DATATYPE_BOOL,
+ MTL_DATATYPE_BOOL2,
+ MTL_DATATYPE_BOOL3,
+ MTL_DATATYPE_BOOL4,
+
+ MTL_DATATYPE_SHORT,
+ MTL_DATATYPE_SHORT2,
+ MTL_DATATYPE_SHORT3,
+ MTL_DATATYPE_SHORT4,
+
+ MTL_DATATYPE_USHORT,
+ MTL_DATATYPE_USHORT2,
+ MTL_DATATYPE_USHORT3,
+ MTL_DATATYPE_USHORT4,
+
+ MTL_DATATYPE_INT,
+ MTL_DATATYPE_INT2,
+ MTL_DATATYPE_INT3,
+ MTL_DATATYPE_INT4,
+
+ MTL_DATATYPE_UINT,
+ MTL_DATATYPE_UINT2,
+ MTL_DATATYPE_UINT3,
+ MTL_DATATYPE_UINT4,
+
+ MTL_DATATYPE_FLOAT,
+ MTL_DATATYPE_FLOAT2,
+ MTL_DATATYPE_FLOAT3,
+ MTL_DATATYPE_FLOAT4,
+
+ MTL_DATATYPE_LONG,
+ MTL_DATATYPE_LONG2,
+ MTL_DATATYPE_LONG3,
+ MTL_DATATYPE_LONG4,
+
+ MTL_DATATYPE_ULONG,
+ MTL_DATATYPE_ULONG2,
+ MTL_DATATYPE_ULONG3,
+ MTL_DATATYPE_ULONG4,
+
+ MTL_DATATYPE_HALF2x2,
+ MTL_DATATYPE_HALF2x3,
+ MTL_DATATYPE_HALF2x4,
+ MTL_DATATYPE_HALF3x2,
+ MTL_DATATYPE_HALF3x3,
+ MTL_DATATYPE_HALF3x4,
+ MTL_DATATYPE_HALF4x2,
+ MTL_DATATYPE_HALF4x3,
+ MTL_DATATYPE_HALF4x4,
+
+ MTL_DATATYPE_FLOAT2x2,
+ MTL_DATATYPE_FLOAT2x3,
+ MTL_DATATYPE_FLOAT2x4,
+ MTL_DATATYPE_FLOAT3x2,
+ MTL_DATATYPE_FLOAT3x3,
+ MTL_DATATYPE_FLOAT3x4,
+ MTL_DATATYPE_FLOAT4x2,
+ MTL_DATATYPE_FLOAT4x3,
+ MTL_DATATYPE_FLOAT4x4,
+
+ MTL_DATATYPE_UINT1010102_NORM,
+ MTL_DATATYPE_INT1010102_NORM
+};
+
+inline uint mtl_get_data_type_size(eMTLDataType type)
+{
+ switch (type) {
+ case MTL_DATATYPE_CHAR:
+ case MTL_DATATYPE_UCHAR:
+ case MTL_DATATYPE_BOOL:
+ return 1;
+ case MTL_DATATYPE_CHAR2:
+ case MTL_DATATYPE_UCHAR2:
+ case MTL_DATATYPE_BOOL2:
+ case MTL_DATATYPE_SHORT:
+ case MTL_DATATYPE_USHORT:
+ return 2;
+
+ case MTL_DATATYPE_CHAR3:
+ case MTL_DATATYPE_UCHAR3:
+ case MTL_DATATYPE_BOOL3:
+ return 3;
+ case MTL_DATATYPE_CHAR4:
+ case MTL_DATATYPE_UCHAR4:
+ case MTL_DATATYPE_INT:
+ case MTL_DATATYPE_UINT:
+ case MTL_DATATYPE_BOOL4:
+ case MTL_DATATYPE_SHORT2:
+ case MTL_DATATYPE_USHORT2:
+ case MTL_DATATYPE_FLOAT:
+ case MTL_DATATYPE_UINT1010102_NORM:
+ case MTL_DATATYPE_INT1010102_NORM:
+ return 4;
+
+ case MTL_DATATYPE_SHORT3:
+ case MTL_DATATYPE_USHORT3:
+ case MTL_DATATYPE_SHORT4:
+ case MTL_DATATYPE_USHORT4:
+ case MTL_DATATYPE_INT2:
+ case MTL_DATATYPE_UINT2:
+ case MTL_DATATYPE_FLOAT2:
+ case MTL_DATATYPE_LONG:
+ case MTL_DATATYPE_ULONG:
+ case MTL_DATATYPE_HALF2x2:
+ return 8;
+
+ case MTL_DATATYPE_HALF3x2:
+ return 12;
+
+ case MTL_DATATYPE_INT3:
+ case MTL_DATATYPE_INT4:
+ case MTL_DATATYPE_UINT3:
+ case MTL_DATATYPE_UINT4:
+ case MTL_DATATYPE_FLOAT3:
+ case MTL_DATATYPE_FLOAT4:
+ case MTL_DATATYPE_LONG2:
+ case MTL_DATATYPE_ULONG2:
+ case MTL_DATATYPE_HALF2x3:
+ case MTL_DATATYPE_HALF2x4:
+ case MTL_DATATYPE_HALF4x2:
+ return 16;
+
+ case MTL_DATATYPE_HALF3x3:
+ case MTL_DATATYPE_HALF3x4:
+ case MTL_DATATYPE_FLOAT3x2:
+ return 24;
+
+ case MTL_DATATYPE_LONG3:
+ case MTL_DATATYPE_LONG4:
+ case MTL_DATATYPE_ULONG3:
+ case MTL_DATATYPE_ULONG4:
+ case MTL_DATATYPE_HALF4x3:
+ case MTL_DATATYPE_HALF4x4:
+ case MTL_DATATYPE_FLOAT2x3:
+ case MTL_DATATYPE_FLOAT2x4:
+ case MTL_DATATYPE_FLOAT4x2:
+ return 32;
+
+ case MTL_DATATYPE_FLOAT3x3:
+ case MTL_DATATYPE_FLOAT3x4:
+ return 48;
+
+ case MTL_DATATYPE_FLOAT4x3:
+ case MTL_DATATYPE_FLOAT4x4:
+ return 64;
+ default:
+ BLI_assert(false);
+ return 0;
+ };
+}
+
+inline uint mtl_get_data_type_alignment(eMTLDataType type)
+{
+ switch (type) {
+ case MTL_DATATYPE_CHAR:
+ case MTL_DATATYPE_UCHAR:
+ case MTL_DATATYPE_BOOL:
+ return 1;
+ case MTL_DATATYPE_CHAR2:
+ case MTL_DATATYPE_UCHAR2:
+ case MTL_DATATYPE_BOOL2:
+ case MTL_DATATYPE_SHORT:
+ case MTL_DATATYPE_USHORT:
+ return 2;
+
+ case MTL_DATATYPE_CHAR3:
+ case MTL_DATATYPE_UCHAR3:
+ case MTL_DATATYPE_BOOL3:
+ return 3;
+ case MTL_DATATYPE_CHAR4:
+ case MTL_DATATYPE_UCHAR4:
+ case MTL_DATATYPE_INT:
+ case MTL_DATATYPE_UINT:
+ case MTL_DATATYPE_BOOL4:
+ case MTL_DATATYPE_SHORT2:
+ case MTL_DATATYPE_USHORT2:
+ case MTL_DATATYPE_FLOAT:
+ case MTL_DATATYPE_HALF2x2:
+ case MTL_DATATYPE_HALF3x2:
+ case MTL_DATATYPE_HALF4x2:
+ case MTL_DATATYPE_UINT1010102_NORM:
+ case MTL_DATATYPE_INT1010102_NORM:
+ return 4;
+
+ case MTL_DATATYPE_SHORT3:
+ case MTL_DATATYPE_USHORT3:
+ case MTL_DATATYPE_SHORT4:
+ case MTL_DATATYPE_USHORT4:
+ case MTL_DATATYPE_INT2:
+ case MTL_DATATYPE_UINT2:
+ case MTL_DATATYPE_FLOAT2:
+ case MTL_DATATYPE_LONG:
+ case MTL_DATATYPE_ULONG:
+ case MTL_DATATYPE_HALF2x3:
+ case MTL_DATATYPE_HALF2x4:
+ case MTL_DATATYPE_HALF3x3:
+ case MTL_DATATYPE_HALF3x4:
+ case MTL_DATATYPE_HALF4x3:
+ case MTL_DATATYPE_HALF4x4:
+ case MTL_DATATYPE_FLOAT2x2:
+ case MTL_DATATYPE_FLOAT3x2:
+ case MTL_DATATYPE_FLOAT4x2:
+ return 8;
+
+ case MTL_DATATYPE_INT3:
+ case MTL_DATATYPE_INT4:
+ case MTL_DATATYPE_UINT3:
+ case MTL_DATATYPE_UINT4:
+ case MTL_DATATYPE_FLOAT3:
+ case MTL_DATATYPE_FLOAT4:
+ case MTL_DATATYPE_LONG2:
+ case MTL_DATATYPE_ULONG2:
+ case MTL_DATATYPE_FLOAT2x3:
+ case MTL_DATATYPE_FLOAT2x4:
+ case MTL_DATATYPE_FLOAT3x3:
+ case MTL_DATATYPE_FLOAT3x4:
+ case MTL_DATATYPE_FLOAT4x3:
+ case MTL_DATATYPE_FLOAT4x4:
+ return 16;
+
+ case MTL_DATATYPE_LONG3:
+ case MTL_DATATYPE_LONG4:
+ case MTL_DATATYPE_ULONG3:
+ case MTL_DATATYPE_ULONG4:
+ return 32;
+
+ default:
+ BLI_assert_msg(false, "Unrecognized MTL datatype.");
+ return 0;
+ };
+}
diff --git a/source/blender/gpu/metal/mtl_shader_shared.h b/source/blender/gpu/metal/mtl_shader_shared.h
new file mode 100644
index 00000000000..f6fd9035001
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_shader_shared.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* Global parameters. */
+#define MTL_SSBO_VERTEX_FETCH_MAX_VBOS 6 /* buffer bind 0..5 */
+#define MTL_SSBO_VERTEX_FETCH_IBO_INDEX MTL_SSBO_VERTEX_FETCH_MAX_VBOS
+
+/* Add Types as needed (Also need to be added to mtl_shader.h). */
+#define GPU_SHADER_ATTR_TYPE_FLOAT 0
+#define GPU_SHADER_ATTR_TYPE_INT 1
+#define GPU_SHADER_ATTR_TYPE_SHORT 2
+#define GPU_SHADER_ATTR_TYPE_CHAR 3
+#define GPU_SHADER_ATTR_TYPE_VEC2 4
+#define GPU_SHADER_ATTR_TYPE_VEC3 5
+#define GPU_SHADER_ATTR_TYPE_VEC4 6
+#define GPU_SHADER_ATTR_TYPE_UVEC2 7
+#define GPU_SHADER_ATTR_TYPE_UVEC3 8
+#define GPU_SHADER_ATTR_TYPE_UVEC4 9
+#define GPU_SHADER_ATTR_TYPE_IVEC2 10
+#define GPU_SHADER_ATTR_TYPE_IVEC3 11
+#define GPU_SHADER_ATTR_TYPE_IVEC4 12
+#define GPU_SHADER_ATTR_TYPE_MAT3 13
+#define GPU_SHADER_ATTR_TYPE_MAT4 14
+#define GPU_SHADER_ATTR_TYPE_UCHAR_NORM 15
+#define GPU_SHADER_ATTR_TYPE_UCHAR2_NORM 16
+#define GPU_SHADER_ATTR_TYPE_UCHAR3_NORM 17
+#define GPU_SHADER_ATTR_TYPE_UCHAR4_NORM 18
+#define GPU_SHADER_ATTR_TYPE_INT1010102_NORM 19
+#define GPU_SHADER_ATTR_TYPE_SHORT3_NORM 20
+#define GPU_SHADER_ATTR_TYPE_CHAR2 21
+#define GPU_SHADER_ATTR_TYPE_CHAR3 22
+#define GPU_SHADER_ATTR_TYPE_CHAR4 23
+#define GPU_SHADER_ATTR_TYPE_UINT 24
diff --git a/source/blender/gpu/metal/mtl_state.hh b/source/blender/gpu/metal/mtl_state.hh
index e6472491b35..1af56378c5a 100644
--- a/source/blender/gpu/metal/mtl_state.hh
+++ b/source/blender/gpu/metal/mtl_state.hh
@@ -3,6 +3,7 @@
/** \file
* \ingroup gpu
*/
+#pragma once
#include "MEM_guardedalloc.h"
@@ -11,6 +12,8 @@
#include "GPU_state.h"
#include "gpu_state_private.hh"
+#include "mtl_pso_descriptor_state.hh"
+
namespace blender::gpu {
/* Forward Declarations. */
@@ -21,7 +24,7 @@ class MTLContext;
* Metal Implementation.
**/
class MTLStateManager : public StateManager {
- public:
+
private:
/* Current state of the associated MTLContext.
* Avoids resetting the whole state for every change. */
@@ -29,6 +32,9 @@ class MTLStateManager : public StateManager {
GPUStateMutable current_mutable_;
MTLContext *context_;
+ /* Global pipeline descriptors. */
+ MTLRenderPipelineStateDescriptor pipeline_descriptor_;
+
public:
MTLStateManager(MTLContext *ctx);
@@ -47,6 +53,12 @@ class MTLStateManager : public StateManager {
void texture_unpack_row_length_set(uint len) override;
+ /* Global pipeline descriptors. */
+ MTLRenderPipelineStateDescriptor &get_pipeline_descriptor()
+ {
+ return pipeline_descriptor_;
+ }
+
private:
void set_write_mask(const eGPUWriteMask value);
void set_depth_test(const eGPUDepthTest value);
diff --git a/source/blender/gpu/metal/mtl_state.mm b/source/blender/gpu/metal/mtl_state.mm
index 0f2d4d7dc48..31182cf91d1 100644
--- a/source/blender/gpu/metal/mtl_state.mm
+++ b/source/blender/gpu/metal/mtl_state.mm
@@ -11,6 +11,7 @@
#include "mtl_context.hh"
#include "mtl_framebuffer.hh"
+#include "mtl_shader_interface_type.hh"
#include "mtl_state.hh"
namespace blender::gpu {
@@ -201,7 +202,7 @@ static MTLCompareFunction gpu_stencil_func_to_metal(eGPUStencilTest stencil_func
case GPU_STENCIL_ALWAYS:
return MTLCompareFunctionAlways;
default:
- BLI_assert(false && "Unrecognised eGPUStencilTest function");
+ BLI_assert(false && "Unrecognized eGPUStencilTest function");
break;
}
return MTLCompareFunctionAlways;
diff --git a/source/blender/gpu/metal/mtl_texture.hh b/source/blender/gpu/metal/mtl_texture.hh
index 9387d5af814..766f01d9018 100644
--- a/source/blender/gpu/metal/mtl_texture.hh
+++ b/source/blender/gpu/metal/mtl_texture.hh
@@ -93,7 +93,7 @@ struct TextureReadRoutineSpecialisation {
* 0 = Not a Depth format,
* 1 = FLOAT DEPTH,
* 2 = 24Bit Integer Depth,
- * 4 = 32bit uinteger Depth. */
+ * 4 = 32bit Unsigned-Integer Depth. */
int depth_format_mode;
bool operator==(const TextureReadRoutineSpecialisation &other) const
@@ -247,7 +247,7 @@ class MTLTexture : public Texture {
void mip_range_set(int min, int max) override;
void *read(int mip, eGPUDataFormat type) override;
- /* Remove once no longer required -- will just return 0 for now in MTL path*/
+ /* Remove once no longer required -- will just return 0 for now in MTL path. */
uint gl_bindcode_get() const override;
bool texture_is_baked();
@@ -357,34 +357,34 @@ class MTLTexture : public Texture {
*/
struct TextureUpdateParams {
int mip_index;
- int extent[3]; /* Width, Height, Slice on 2D Array tex*/
- int offset[3]; /* Width, Height, Slice on 2D Array tex*/
- uint unpack_row_length; /* Number of pixels between bytes in input data */
+ int extent[3]; /* Width, Height, Slice on 2D Array tex. */
+ int offset[3]; /* Width, Height, Slice on 2D Array tex. */
+ uint unpack_row_length; /* Number of pixels between bytes in input data. */
};
id<MTLComputePipelineState> texture_update_1d_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation);
+ TextureUpdateRoutineSpecialisation specialization);
id<MTLComputePipelineState> texture_update_1d_array_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation);
+ TextureUpdateRoutineSpecialisation specialization);
id<MTLComputePipelineState> texture_update_2d_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation);
+ TextureUpdateRoutineSpecialisation specialization);
id<MTLComputePipelineState> texture_update_2d_array_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation);
+ TextureUpdateRoutineSpecialisation specialization);
id<MTLComputePipelineState> texture_update_3d_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation);
+ TextureUpdateRoutineSpecialisation specialization);
id<MTLComputePipelineState> mtl_texture_update_impl(
- TextureUpdateRoutineSpecialisation specialisation_params,
+ TextureUpdateRoutineSpecialisation specialization_params,
blender::Map<TextureUpdateRoutineSpecialisation, id<MTLComputePipelineState>>
- &specialisation_cache,
+ &specialization_cache,
eGPUTextureType texture_type);
/* Depth Update Utilities */
/* Depth texture updates are not directly supported with Blit operations, similarly, we cannot
* use a compute shader to write to depth, so we must instead render to a depth target.
* These processes use vertex/fragment shaders to render texture data from an intermediate
- * source, in order to prime the depth buffer*/
- GPUShader *depth_2d_update_sh_get(DepthTextureUpdateRoutineSpecialisation specialisation);
+ * source, in order to prime the depth buffer. */
+ GPUShader *depth_2d_update_sh_get(DepthTextureUpdateRoutineSpecialisation specialization);
void update_sub_depth_2d(
int mip, int offset[3], int extent[3], eGPUDataFormat type, const void *data);
@@ -392,25 +392,25 @@ class MTLTexture : public Texture {
/* Texture Read function utilities -- Follows a similar mechanism to the updating routines */
struct TextureReadParams {
int mip_index;
- int extent[3]; /* Width, Height, Slice on 2D Array tex*/
- int offset[3]; /* Width, Height, Slice on 2D Array tex*/
+ int extent[3]; /* Width, Height, Slice on 2D Array tex. */
+ int offset[3]; /* Width, Height, Slice on 2D Array tex. */
};
id<MTLComputePipelineState> texture_read_1d_get_kernel(
- TextureReadRoutineSpecialisation specialisation);
+ TextureReadRoutineSpecialisation specialization);
id<MTLComputePipelineState> texture_read_1d_array_get_kernel(
- TextureReadRoutineSpecialisation specialisation);
+ TextureReadRoutineSpecialisation specialization);
id<MTLComputePipelineState> texture_read_2d_get_kernel(
- TextureReadRoutineSpecialisation specialisation);
+ TextureReadRoutineSpecialisation specialization);
id<MTLComputePipelineState> texture_read_2d_array_get_kernel(
- TextureReadRoutineSpecialisation specialisation);
+ TextureReadRoutineSpecialisation specialization);
id<MTLComputePipelineState> texture_read_3d_get_kernel(
- TextureReadRoutineSpecialisation specialisation);
+ TextureReadRoutineSpecialisation specialization);
id<MTLComputePipelineState> mtl_texture_read_impl(
- TextureReadRoutineSpecialisation specialisation_params,
+ TextureReadRoutineSpecialisation specialization_params,
blender::Map<TextureReadRoutineSpecialisation, id<MTLComputePipelineState>>
- &specialisation_cache,
+ &specialization_cache,
eGPUTextureType texture_type);
/* fullscreen blit utilities. */
diff --git a/source/blender/gpu/metal/mtl_texture.mm b/source/blender/gpu/metal/mtl_texture.mm
index 0cb38a3a2b7..2b7c2333bff 100644
--- a/source/blender/gpu/metal/mtl_texture.mm
+++ b/source/blender/gpu/metal/mtl_texture.mm
@@ -479,8 +479,8 @@ void gpu::MTLTexture::update_sub(
int expected_dst_bytes_per_pixel = get_mtl_format_bytesize(destination_format);
int destination_num_channels = get_mtl_format_num_components(destination_format);
- /* Prepare specialisation struct (For texture update routine). */
- TextureUpdateRoutineSpecialisation compute_specialisation_kernel = {
+ /* Prepare specialization struct (For texture update routine). */
+ TextureUpdateRoutineSpecialisation compute_specialization_kernel = {
tex_data_format_to_msl_type_str(type), /* INPUT DATA FORMAT */
tex_data_format_to_msl_texture_template_type(type), /* TEXTURE DATA FORMAT */
num_channels,
@@ -620,7 +620,7 @@ void gpu::MTLTexture::update_sub(
/* Use Compute Based update. */
if (type_ == GPU_TEXTURE_1D) {
id<MTLComputePipelineState> pso = texture_update_1d_get_kernel(
- compute_specialisation_kernel);
+ compute_specialization_kernel);
TextureUpdateParams params = {mip,
{extent[0], 1, 1},
{offset[0], 0, 0},
@@ -637,7 +637,7 @@ void gpu::MTLTexture::update_sub(
}
else if (type_ == GPU_TEXTURE_1D_ARRAY) {
id<MTLComputePipelineState> pso = texture_update_1d_array_get_kernel(
- compute_specialisation_kernel);
+ compute_specialization_kernel);
TextureUpdateParams params = {mip,
{extent[0], extent[1], 1},
{offset[0], offset[1], 0},
@@ -694,7 +694,7 @@ void gpu::MTLTexture::update_sub(
/* Use Compute texture update. */
if (type_ == GPU_TEXTURE_2D) {
id<MTLComputePipelineState> pso = texture_update_2d_get_kernel(
- compute_specialisation_kernel);
+ compute_specialization_kernel);
TextureUpdateParams params = {mip,
{extent[0], extent[1], 1},
{offset[0], offset[1], 0},
@@ -712,7 +712,7 @@ void gpu::MTLTexture::update_sub(
}
else if (type_ == GPU_TEXTURE_2D_ARRAY) {
id<MTLComputePipelineState> pso = texture_update_2d_array_get_kernel(
- compute_specialisation_kernel);
+ compute_specialization_kernel);
TextureUpdateParams params = {mip,
{extent[0], extent[1], extent[2]},
{offset[0], offset[1], offset[2]},
@@ -752,7 +752,7 @@ void gpu::MTLTexture::update_sub(
}
else {
id<MTLComputePipelineState> pso = texture_update_3d_get_kernel(
- compute_specialisation_kernel);
+ compute_specialization_kernel);
TextureUpdateParams params = {mip,
{extent[0], extent[1], extent[2]},
{offset[0], offset[1], offset[2]},
@@ -1216,7 +1216,7 @@ void gpu::MTLTexture::read_internal(int mip,
destination_buffer_host_ptr = (void *)((uint8_t *)([destination_buffer contents]) +
destination_offset);
- /* Prepare specialisation struct (For non-trivial texture read routine). */
+ /* Prepare specialization struct (For non-trivial texture read routine). */
int depth_format_mode = 0;
if (is_depth_format) {
depth_format_mode = 1;
@@ -1236,7 +1236,7 @@ void gpu::MTLTexture::read_internal(int mip,
}
}
- TextureReadRoutineSpecialisation compute_specialisation_kernel = {
+ TextureReadRoutineSpecialisation compute_specialization_kernel = {
tex_data_format_to_msl_texture_template_type(data_format), /* TEXTURE DATA TYPE */
tex_data_format_to_msl_type_str(desired_output_format), /* OUTPUT DATA TYPE */
num_channels, /* TEXTURE COMPONENT COUNT */
@@ -1283,7 +1283,7 @@ void gpu::MTLTexture::read_internal(int mip,
id<MTLComputeCommandEncoder> compute_encoder =
ctx->main_command_buffer.ensure_begin_compute_encoder();
id<MTLComputePipelineState> pso = texture_read_2d_get_kernel(
- compute_specialisation_kernel);
+ compute_specialization_kernel);
TextureReadParams params = {
mip,
{width, height, 1},
@@ -1339,7 +1339,7 @@ void gpu::MTLTexture::read_internal(int mip,
id<MTLComputeCommandEncoder> compute_encoder =
ctx->main_command_buffer.ensure_begin_compute_encoder();
id<MTLComputePipelineState> pso = texture_read_2d_array_get_kernel(
- compute_specialisation_kernel);
+ compute_specialization_kernel);
TextureReadParams params = {
mip,
{width, height, depth},
diff --git a/source/blender/gpu/metal/mtl_texture_util.mm b/source/blender/gpu/metal/mtl_texture_util.mm
index e2f0b3c848e..928393fb39e 100644
--- a/source/blender/gpu/metal/mtl_texture_util.mm
+++ b/source/blender/gpu/metal/mtl_texture_util.mm
@@ -124,7 +124,7 @@ MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format)
return MTLPixelFormatDepth16Unorm;
default:
- BLI_assert(!"Unrecognised GPU pixel format!\n");
+ BLI_assert(!"Unrecognized GPU pixel format!\n");
return MTLPixelFormatRGBA8Unorm;
}
}
@@ -183,7 +183,7 @@ int get_mtl_format_bytesize(MTLPixelFormat tex_format)
return 2;
default:
- BLI_assert(!"Unrecognised GPU pixel format!\n");
+ BLI_assert(!"Unrecognized GPU pixel format!\n");
return 1;
}
}
@@ -238,7 +238,7 @@ int get_mtl_format_num_components(MTLPixelFormat tex_format)
return 1;
default:
- BLI_assert(!"Unrecognised GPU pixel format!\n");
+ BLI_assert(!"Unrecognized GPU pixel format!\n");
return 1;
}
}
@@ -305,13 +305,13 @@ bool mtl_format_supports_blending(MTLPixelFormat format)
* \{ */
id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_update_impl(
- TextureUpdateRoutineSpecialisation specialisation_params,
+ TextureUpdateRoutineSpecialisation specialization_params,
blender::Map<TextureUpdateRoutineSpecialisation, id<MTLComputePipelineState>>
- &specialisation_cache,
+ &specialization_cache,
eGPUTextureType texture_type)
{
/* Check whether the Kernel exists. */
- id<MTLComputePipelineState> *result = specialisation_cache.lookup_ptr(specialisation_params);
+ id<MTLComputePipelineState> *result = specialization_cache.lookup_ptr(specialization_params);
if (result != nullptr) {
return *result;
}
@@ -332,18 +332,18 @@ id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_update_impl(
options.languageVersion = MTLLanguageVersion2_2;
options.preprocessorMacros = @{
@"INPUT_DATA_TYPE" :
- [NSString stringWithUTF8String:specialisation_params.input_data_type.c_str()],
+ [NSString stringWithUTF8String:specialization_params.input_data_type.c_str()],
@"OUTPUT_DATA_TYPE" :
- [NSString stringWithUTF8String:specialisation_params.output_data_type.c_str()],
+ [NSString stringWithUTF8String:specialization_params.output_data_type.c_str()],
@"COMPONENT_COUNT_INPUT" :
- [NSNumber numberWithInt:specialisation_params.component_count_input],
+ [NSNumber numberWithInt:specialization_params.component_count_input],
@"COMPONENT_COUNT_OUTPUT" :
- [NSNumber numberWithInt:specialisation_params.component_count_output],
+ [NSNumber numberWithInt:specialization_params.component_count_output],
@"TEX_TYPE" : [NSNumber numberWithInt:((int)(texture_type))]
};
/* Prepare shader library for conversion routine. */
- NSError *error = NULL;
+ NSError *error = nullptr;
id<MTLLibrary> temp_lib = [[ctx->device newLibraryWithSource:tex_update_kernel_src
options:options
error:&error] autorelease];
@@ -370,7 +370,7 @@ id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_update_impl(
/* Store PSO. */
[compute_pso retain];
- specialisation_cache.add_new(specialisation_params, compute_pso);
+ specialization_cache.add_new(specialization_params, compute_pso);
return_pso = compute_pso;
}
@@ -379,53 +379,53 @@ id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_update_impl(
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_update_1d_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation)
+ TextureUpdateRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
- return mtl_texture_update_impl(specialisation,
+ return mtl_texture_update_impl(specialization,
mtl_context->get_texture_utils().texture_1d_update_compute_psos,
GPU_TEXTURE_1D);
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_update_1d_array_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation)
+ TextureUpdateRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
return mtl_texture_update_impl(
- specialisation,
+ specialization,
mtl_context->get_texture_utils().texture_1d_array_update_compute_psos,
GPU_TEXTURE_1D_ARRAY);
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_update_2d_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation)
+ TextureUpdateRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
- return mtl_texture_update_impl(specialisation,
+ return mtl_texture_update_impl(specialization,
mtl_context->get_texture_utils().texture_2d_update_compute_psos,
GPU_TEXTURE_2D);
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_update_2d_array_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation)
+ TextureUpdateRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
return mtl_texture_update_impl(
- specialisation,
+ specialization,
mtl_context->get_texture_utils().texture_2d_array_update_compute_psos,
GPU_TEXTURE_2D_ARRAY);
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_update_3d_get_kernel(
- TextureUpdateRoutineSpecialisation specialisation)
+ TextureUpdateRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
- return mtl_texture_update_impl(specialisation,
+ return mtl_texture_update_impl(specialization,
mtl_context->get_texture_utils().texture_3d_update_compute_psos,
GPU_TEXTURE_3D);
}
@@ -434,7 +434,7 @@ id<MTLComputePipelineState> gpu::MTLTexture::texture_update_3d_get_kernel(
* Currently does not appear to be hit. */
GPUShader *gpu::MTLTexture::depth_2d_update_sh_get(
- DepthTextureUpdateRoutineSpecialisation specialisation)
+ DepthTextureUpdateRoutineSpecialisation specialization)
{
/* Check whether the Kernel exists. */
@@ -442,13 +442,13 @@ GPUShader *gpu::MTLTexture::depth_2d_update_sh_get(
BLI_assert(mtl_context != nullptr);
GPUShader **result = mtl_context->get_texture_utils().depth_2d_update_shaders.lookup_ptr(
- specialisation);
+ specialization);
if (result != nullptr) {
return *result;
}
const char *fragment_source = nullptr;
- switch (specialisation.data_mode) {
+ switch (specialization.data_mode) {
case MTL_DEPTH_UPDATE_MODE_FLOAT:
fragment_source = datatoc_depth_2d_update_float_frag_glsl;
break;
@@ -469,7 +469,7 @@ GPUShader *gpu::MTLTexture::depth_2d_update_sh_get(
nullptr,
nullptr,
"depth_2d_update_sh_get");
- mtl_context->get_texture_utils().depth_2d_update_shaders.add_new(specialisation, shader);
+ mtl_context->get_texture_utils().depth_2d_update_shaders.add_new(specialization, shader);
return shader;
}
@@ -507,18 +507,18 @@ void gpu::MTLTexture::update_sub_depth_2d(
eGPUTextureFormat format = (is_float) ? GPU_R32F : GPU_R32I;
/* Shader key - Add parameters here for different configurations. */
- DepthTextureUpdateRoutineSpecialisation specialisation;
+ DepthTextureUpdateRoutineSpecialisation specialization;
switch (type) {
case GPU_DATA_FLOAT:
- specialisation.data_mode = MTL_DEPTH_UPDATE_MODE_FLOAT;
+ specialization.data_mode = MTL_DEPTH_UPDATE_MODE_FLOAT;
break;
case GPU_DATA_UINT_24_8:
- specialisation.data_mode = MTL_DEPTH_UPDATE_MODE_INT24;
+ specialization.data_mode = MTL_DEPTH_UPDATE_MODE_INT24;
break;
case GPU_DATA_UINT:
- specialisation.data_mode = MTL_DEPTH_UPDATE_MODE_INT32;
+ specialization.data_mode = MTL_DEPTH_UPDATE_MODE_INT32;
break;
default:
@@ -544,7 +544,7 @@ void gpu::MTLTexture::update_sub_depth_2d(
GPU_framebuffer_clear_stencil(depth_fb_temp, 0);
}
- GPUShader *depth_2d_update_sh = depth_2d_update_sh_get(specialisation);
+ GPUShader *depth_2d_update_sh = depth_2d_update_sh_get(specialization);
BLI_assert(depth_2d_update_sh != nullptr);
GPUBatch *quad = GPU_batch_preset_quad();
GPU_batch_set_shader(quad, depth_2d_update_sh);
@@ -591,13 +591,13 @@ void gpu::MTLTexture::update_sub_depth_2d(
* \{ */
id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_read_impl(
- TextureReadRoutineSpecialisation specialisation_params,
+ TextureReadRoutineSpecialisation specialization_params,
blender::Map<TextureReadRoutineSpecialisation, id<MTLComputePipelineState>>
- &specialisation_cache,
+ &specialization_cache,
eGPUTextureType texture_type)
{
/* Check whether the Kernel exists. */
- id<MTLComputePipelineState> *result = specialisation_cache.lookup_ptr(specialisation_params);
+ id<MTLComputePipelineState> *result = specialization_cache.lookup_ptr(specialization_params);
if (result != nullptr) {
return *result;
}
@@ -615,10 +615,10 @@ id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_read_impl(
/* Defensive Debug Checks. */
long long int depth_scale_factor = 1;
- if (specialisation_params.depth_format_mode > 0) {
- BLI_assert(specialisation_params.component_count_input == 1);
- BLI_assert(specialisation_params.component_count_output == 1);
- switch (specialisation_params.depth_format_mode) {
+ if (specialization_params.depth_format_mode > 0) {
+ BLI_assert(specialization_params.component_count_input == 1);
+ BLI_assert(specialization_params.component_count_output == 1);
+ switch (specialization_params.depth_format_mode) {
case 1:
/* FLOAT */
depth_scale_factor = 1;
@@ -632,7 +632,7 @@ id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_read_impl(
depth_scale_factor = 0xFFFFFFFFu;
break;
default:
- BLI_assert_msg(0, "Unrecognised mode");
+ BLI_assert_msg(0, "Unrecognized mode");
break;
}
}
@@ -642,24 +642,24 @@ id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_read_impl(
options.languageVersion = MTLLanguageVersion2_2;
options.preprocessorMacros = @{
@"INPUT_DATA_TYPE" :
- [NSString stringWithUTF8String:specialisation_params.input_data_type.c_str()],
+ [NSString stringWithUTF8String:specialization_params.input_data_type.c_str()],
@"OUTPUT_DATA_TYPE" :
- [NSString stringWithUTF8String:specialisation_params.output_data_type.c_str()],
+ [NSString stringWithUTF8String:specialization_params.output_data_type.c_str()],
@"COMPONENT_COUNT_INPUT" :
- [NSNumber numberWithInt:specialisation_params.component_count_input],
+ [NSNumber numberWithInt:specialization_params.component_count_input],
@"COMPONENT_COUNT_OUTPUT" :
- [NSNumber numberWithInt:specialisation_params.component_count_output],
+ [NSNumber numberWithInt:specialization_params.component_count_output],
@"WRITE_COMPONENT_COUNT" :
- [NSNumber numberWithInt:min_ii(specialisation_params.component_count_input,
- specialisation_params.component_count_output)],
+ [NSNumber numberWithInt:min_ii(specialization_params.component_count_input,
+ specialization_params.component_count_output)],
@"IS_DEPTH_FORMAT" :
- [NSNumber numberWithInt:((specialisation_params.depth_format_mode > 0) ? 1 : 0)],
+ [NSNumber numberWithInt:((specialization_params.depth_format_mode > 0) ? 1 : 0)],
@"DEPTH_SCALE_FACTOR" : [NSNumber numberWithLongLong:depth_scale_factor],
@"TEX_TYPE" : [NSNumber numberWithInt:((int)(texture_type))]
};
/* Prepare shader library for conversion routine. */
- NSError *error = NULL;
+ NSError *error = nullptr;
id<MTLLibrary> temp_lib = [[ctx->device newLibraryWithSource:tex_update_kernel_src
options:options
error:&error] autorelease];
@@ -687,7 +687,7 @@ id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_read_impl(
/* Store PSO. */
[compute_pso retain];
- specialisation_cache.add_new(specialisation_params, compute_pso);
+ specialization_cache.add_new(specialization_params, compute_pso);
return_pso = compute_pso;
}
@@ -696,51 +696,51 @@ id<MTLComputePipelineState> gpu::MTLTexture::mtl_texture_read_impl(
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_read_2d_get_kernel(
- TextureReadRoutineSpecialisation specialisation)
+ TextureReadRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
- return mtl_texture_read_impl(specialisation,
+ return mtl_texture_read_impl(specialization,
mtl_context->get_texture_utils().texture_2d_read_compute_psos,
GPU_TEXTURE_2D);
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_read_2d_array_get_kernel(
- TextureReadRoutineSpecialisation specialisation)
+ TextureReadRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
- return mtl_texture_read_impl(specialisation,
+ return mtl_texture_read_impl(specialization,
mtl_context->get_texture_utils().texture_2d_array_read_compute_psos,
GPU_TEXTURE_2D_ARRAY);
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_read_1d_get_kernel(
- TextureReadRoutineSpecialisation specialisation)
+ TextureReadRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
- return mtl_texture_read_impl(specialisation,
+ return mtl_texture_read_impl(specialization,
mtl_context->get_texture_utils().texture_1d_read_compute_psos,
GPU_TEXTURE_1D);
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_read_1d_array_get_kernel(
- TextureReadRoutineSpecialisation specialisation)
+ TextureReadRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
- return mtl_texture_read_impl(specialisation,
+ return mtl_texture_read_impl(specialization,
mtl_context->get_texture_utils().texture_1d_array_read_compute_psos,
GPU_TEXTURE_1D_ARRAY);
}
id<MTLComputePipelineState> gpu::MTLTexture::texture_read_3d_get_kernel(
- TextureReadRoutineSpecialisation specialisation)
+ TextureReadRoutineSpecialisation specialization)
{
MTLContext *mtl_context = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(mtl_context != nullptr);
- return mtl_texture_read_impl(specialisation,
+ return mtl_texture_read_impl(specialization,
mtl_context->get_texture_utils().texture_3d_read_compute_psos,
GPU_TEXTURE_3D);
}
diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.hh b/source/blender/gpu/metal/mtl_uniform_buffer.hh
new file mode 100644
index 00000000000..789a85f0a92
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_uniform_buffer.hh
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#pragma once
+
+#include "MEM_guardedalloc.h"
+#include "gpu_uniform_buffer_private.hh"
+
+#include "mtl_context.hh"
+
+namespace blender::gpu {
+
+/**
+ * Implementation of Uniform Buffers using Metal.
+ **/
+class MTLUniformBuf : public UniformBuf {
+ private:
+ /* Allocation Handle. */
+ gpu::MTLBuffer *metal_buffer_ = nullptr;
+
+ /* Whether buffer has contents, if false, no GPU buffer will
+ * have yet been allocated. */
+ bool has_data_ = false;
+
+ /* Bind-state tracking. */
+ int bind_slot_ = -1;
+ MTLContext *bound_ctx_ = nullptr;
+
+ public:
+ MTLUniformBuf(size_t size, const char *name);
+ ~MTLUniformBuf();
+
+ void update(const void *data) override;
+ void bind(int slot) override;
+ void unbind() override;
+
+ id<MTLBuffer> get_metal_buffer(int *r_offset);
+ int get_size();
+ const char *get_name()
+ {
+ return name_;
+ }
+
+ MEM_CXX_CLASS_ALLOC_FUNCS("MTLUniformBuf");
+};
+
+} // namespace blender::gpu
diff --git a/source/blender/gpu/metal/mtl_uniform_buffer.mm b/source/blender/gpu/metal/mtl_uniform_buffer.mm
new file mode 100644
index 00000000000..4893014dedf
--- /dev/null
+++ b/source/blender/gpu/metal/mtl_uniform_buffer.mm
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup gpu
+ */
+
+#include "BKE_global.h"
+
+#include "BLI_string.h"
+
+#include "gpu_backend.hh"
+#include "gpu_context_private.hh"
+
+#include "mtl_backend.hh"
+#include "mtl_context.hh"
+#include "mtl_debug.hh"
+#include "mtl_uniform_buffer.hh"
+
+namespace blender::gpu {
+
+MTLUniformBuf::MTLUniformBuf(size_t size, const char *name) : UniformBuf(size, name)
+{
+}
+
+MTLUniformBuf::~MTLUniformBuf()
+{
+ if (metal_buffer_ != nullptr) {
+ metal_buffer_->free();
+ metal_buffer_ = nullptr;
+ }
+ has_data_ = false;
+
+ /* Ensure UBO is not bound to active CTX.
+ * UBO bindings are reset upon Context-switch so we do not need
+ * to check deactivated context's. */
+ MTLContext *ctx = MTLContext::get();
+ if (ctx) {
+ for (int i = 0; i < MTL_MAX_UNIFORM_BUFFER_BINDINGS; i++) {
+ MTLUniformBufferBinding &slot = ctx->pipeline_state.ubo_bindings[i];
+ if (slot.bound && slot.ubo == this) {
+ slot.bound = false;
+ slot.ubo = nullptr;
+ }
+ }
+ }
+}
+
+void MTLUniformBuf::update(const void *data)
+{
+ BLI_assert(this);
+ BLI_assert(size_in_bytes_ > 0);
+
+ /* Free existing allocation.
+ * The previous UBO resource will be tracked by the memory manager,
+ * in case dependent GPU work is still executing. */
+ if (metal_buffer_ != nullptr) {
+ metal_buffer_->free();
+ metal_buffer_ = nullptr;
+ }
+
+ /* Allocate MTL buffer */
+ MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
+ BLI_assert(ctx);
+ BLI_assert(ctx->device);
+ UNUSED_VARS_NDEBUG(ctx);
+
+ if (data != nullptr) {
+ metal_buffer_ = MTLContext::get_global_memory_manager().allocate_with_data(
+ size_in_bytes_, true, data);
+ has_data_ = true;
+
+ metal_buffer_->set_label(@"Uniform Buffer");
+ BLI_assert(metal_buffer_ != nullptr);
+ BLI_assert(metal_buffer_->get_metal_buffer() != nil);
+ }
+ else {
+ /* If data is not yet present, no buffer will be allocated and MTLContext will use an empty
+ * null buffer, containing zeroes, if the UBO is bound. */
+ metal_buffer_ = nullptr;
+ has_data_ = false;
+ }
+}
+
+void MTLUniformBuf::bind(int slot)
+{
+ if (slot < 0) {
+ MTL_LOG_WARNING("Failed to bind UBO %p. uniform location %d invalid.\n", this, slot);
+ return;
+ }
+
+ BLI_assert(slot < MTL_MAX_UNIFORM_BUFFER_BINDINGS);
+
+ /* Bind current UBO to active context. */
+ MTLContext *ctx = MTLContext::get();
+ BLI_assert(ctx);
+
+ MTLUniformBufferBinding &ctx_ubo_bind_slot = ctx->pipeline_state.ubo_bindings[slot];
+ ctx_ubo_bind_slot.ubo = this;
+ ctx_ubo_bind_slot.bound = true;
+
+ bind_slot_ = slot;
+ bound_ctx_ = ctx;
+
+ /* Check if we have any deferred data to upload. */
+ if (data_ != nullptr) {
+ this->update(data_);
+ MEM_SAFE_FREE(data_);
+ }
+
+ /* Ensure there is at least an empty dummy buffer. */
+ if (metal_buffer_ == nullptr) {
+ this->update(nullptr);
+ }
+}
+
+void MTLUniformBuf::unbind()
+{
+ /* Unbind in debug mode to validate missing binds.
+ * Otherwise, only perform a full unbind upon destruction
+ * to ensure no lingering references. */
+#ifndef NDEBUG
+ if (true) {
+#else
+ if (G.debug & G_DEBUG_GPU) {
+#endif
+ if (bound_ctx_ != nullptr && bind_slot_ > -1) {
+ MTLUniformBufferBinding &ctx_ubo_bind_slot =
+ bound_ctx_->pipeline_state.ubo_bindings[bind_slot_];
+ if (ctx_ubo_bind_slot.bound && ctx_ubo_bind_slot.ubo == this) {
+ ctx_ubo_bind_slot.bound = false;
+ ctx_ubo_bind_slot.ubo = nullptr;
+ }
+ }
+ }
+
+ /* Reset bind index. */
+ bind_slot_ = -1;
+ bound_ctx_ = nullptr;
+}
+
+id<MTLBuffer> MTLUniformBuf::get_metal_buffer(int *r_offset)
+{
+ BLI_assert(this);
+ *r_offset = 0;
+ if (metal_buffer_ != nullptr && has_data_) {
+ *r_offset = 0;
+ metal_buffer_->debug_ensure_used();
+ return metal_buffer_->get_metal_buffer();
+ }
+ else {
+ *r_offset = 0;
+ return nil;
+ }
+}
+
+int MTLUniformBuf::get_size()
+{
+ BLI_assert(this);
+ return size_in_bytes_;
+}
+
+} // blender::gpu
diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc
index 4869bff2737..9051003bcd5 100644
--- a/source/blender/gpu/opengl/gl_backend.cc
+++ b/source/blender/gpu/opengl/gl_backend.cc
@@ -51,7 +51,12 @@ void GLBackend::platform_init()
os = GPU_OS_UNIX;
#endif
- if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
+ if (!vendor) {
+ printf("Warning: No OpenGL vendor detected.\n");
+ device = GPU_DEVICE_UNKNOWN;
+ driver = GPU_DRIVER_ANY;
+ }
+ else if (strstr(vendor, "ATI") || strstr(vendor, "AMD")) {
device = GPU_DEVICE_ATI;
driver = GPU_DRIVER_OFFICIAL;
}
@@ -113,7 +118,7 @@ void GLBackend::platform_init()
}
/* Detect support level */
- if (!GLEW_VERSION_3_3) {
+ if (!(epoxy_gl_version() >= 33)) {
support_level = GPU_SUPPORT_LEVEL_UNSUPPORTED;
}
else {
@@ -222,6 +227,7 @@ static void detect_workarounds()
GLContext::unused_fb_slot_workaround = true;
/* Turn off extensions. */
GCaps.shader_image_load_store_support = false;
+ GCaps.shader_draw_parameters_support = false;
GCaps.shader_storage_buffer_objects_support = false;
GLContext::base_instance_support = false;
GLContext::clear_texture_support = false;
@@ -243,14 +249,14 @@ static void detect_workarounds()
return;
}
- /* Limit support for GLEW_ARB_base_instance to OpenGL 4.0 and higher. NVIDIA Quadro FX 4800
- * (TeraScale) report that they support GLEW_ARB_base_instance, but the driver does not support
+ /* Limit support for GL_ARB_base_instance to OpenGL 4.0 and higher. NVIDIA Quadro FX 4800
+ * (TeraScale) report that they support GL_ARB_base_instance, but the driver does not support
* GLEW_ARB_draw_indirect as it has an OpenGL3 context what also matches the minimum needed
* requirements.
*
* We use it as a target for glMapBuffer(Range) what is part of the OpenGL 4 API. So better
* disable it when we don't have an OpenGL4 context (See T77657) */
- if (!GLEW_VERSION_4_0) {
+ if (!(epoxy_gl_version() >= 40)) {
GLContext::base_instance_support = false;
}
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) &&
@@ -266,6 +272,7 @@ static void detect_workarounds()
GLContext::unused_fb_slot_workaround = true;
GCaps.mip_render_workaround = true;
GCaps.shader_image_load_store_support = false;
+ GCaps.shader_draw_parameters_support = false;
GCaps.broken_amd_driver = true;
}
/* Compute shaders have some issues with those versions (see T94936). */
@@ -279,12 +286,14 @@ static void detect_workarounds()
strstr(renderer, "AMD TAHITI"))) {
GLContext::unused_fb_slot_workaround = true;
GCaps.shader_image_load_store_support = false;
+ GCaps.shader_draw_parameters_support = false;
GCaps.broken_amd_driver = true;
}
/* Fix slowdown on this particular driver. (see T77641) */
if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) &&
strstr(version, "Mesa 19.3.4")) {
GCaps.shader_image_load_store_support = false;
+ GCaps.shader_draw_parameters_support = false;
GCaps.broken_amd_driver = true;
}
/* See T82856: AMD drivers since 20.11 running on a polaris architecture doesn't support the
@@ -313,7 +322,8 @@ static void detect_workarounds()
/* Limit this fix to older hardware with GL < 4.5. This means Broadwell GPUs are
* covered since they only support GL 4.4 on windows.
* This fixes some issues with workbench anti-aliasing on Win + Intel GPU. (see T76273) */
- if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) && !GLEW_VERSION_4_5) {
+ if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) &&
+ !(epoxy_gl_version() >= 45)) {
GLContext::copy_image_support = false;
}
/* Special fix for these specific GPUs.
@@ -328,7 +338,7 @@ static void detect_workarounds()
strstr(renderer, "HD Graphics 2500"))) {
GLContext::texture_cube_map_array_support = false;
}
- /* Maybe not all of these drivers have problems with `GLEW_ARB_base_instance`.
+ /* Maybe not all of these drivers have problems with `GL_ARB_base_instance`.
* But it's hard to test each case.
* We get crashes from some crappy Intel drivers don't work well with shaders created in
* different rendering contexts. */
@@ -353,7 +363,8 @@ static void detect_workarounds()
}
/* There is a bug on older Nvidia GPU where GL_ARB_texture_gather
* is reported to be supported but yield a compile error (see T55802). */
- if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) {
+ if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) &&
+ !(epoxy_gl_version() >= 40)) {
GLContext::texture_gather_support = false;
}
@@ -375,6 +386,11 @@ static void detect_workarounds()
}
}
+ /* Disable TF on macOS. */
+ if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) {
+ GCaps.transform_feedback_support = false;
+ }
+
/* Some Intel drivers have issues with using mips as frame-buffer targets if
* GL_TEXTURE_MAX_LEVEL is higher than the target MIP.
* Only check at the end after all other workarounds because this uses the drawing code.
@@ -420,7 +436,6 @@ static void detect_workarounds()
/** Internal capabilities. */
GLint GLContext::max_cubemap_size = 0;
-GLint GLContext::max_texture_3d_size = 0;
GLint GLContext::max_ubo_binds = 0;
GLint GLContext::max_ubo_size = 0;
GLint GLContext::max_ssbo_binds = 0;
@@ -457,7 +472,7 @@ float GLContext::derivative_signs[2] = {1.0f, 1.0f};
void GLBackend::capabilities_init()
{
- BLI_assert(GLEW_VERSION_3_3);
+ BLI_assert(epoxy_gl_version() >= 33);
/* Common Capabilities. */
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &GCaps.max_texture_size);
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &GCaps.max_texture_layers);
@@ -482,9 +497,15 @@ void GLBackend::capabilities_init()
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 && GLEW_VERSION_4_3;
+ GCaps.max_samplers = GCaps.max_textures;
+ GCaps.mem_stats_support = epoxy_has_gl_extension("GL_NVX_gpu_memory_info") ||
+ epoxy_has_gl_extension("GL_ATI_meminfo");
+ GCaps.shader_image_load_store_support = epoxy_has_gl_extension("GL_ARB_shader_image_load_store");
+ GCaps.shader_draw_parameters_support = epoxy_has_gl_extension("GL_ARB_shader_draw_parameters");
+ GCaps.compute_shader_support = epoxy_has_gl_extension("GL_ARB_compute_shader") &&
+ epoxy_gl_version() >= 43;
+ GCaps.max_samplers = GCaps.max_textures;
+
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]);
@@ -496,35 +517,56 @@ void GLBackend::capabilities_init()
&GCaps.max_shader_storage_buffer_bindings);
glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &GCaps.max_compute_shader_storage_blocks);
}
- GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object;
+ GCaps.shader_storage_buffer_objects_support = epoxy_has_gl_extension(
+ "GL_ARB_shader_storage_buffer_object");
+ GCaps.transform_feedback_support = true;
+
/* GL specific capabilities. */
- glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size);
+ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GCaps.max_texture_3d_size);
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GLContext::max_cubemap_size);
glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &GLContext::max_ubo_binds);
glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &GLContext::max_ubo_size);
if (GCaps.shader_storage_buffer_objects_support) {
- glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &GLContext::max_ssbo_binds);
+ GLint max_ssbo_binds;
+ GLContext::max_ssbo_binds = 999999;
+ glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &max_ssbo_binds);
+ GLContext::max_ssbo_binds = min_ii(GLContext::max_ssbo_binds, max_ssbo_binds);
+ glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &max_ssbo_binds);
+ GLContext::max_ssbo_binds = min_ii(GLContext::max_ssbo_binds, max_ssbo_binds);
+ glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &max_ssbo_binds);
+ GLContext::max_ssbo_binds = min_ii(GLContext::max_ssbo_binds, max_ssbo_binds);
+ if (GLContext::max_ssbo_binds < 8) {
+ /* Does not meet our minimum requirements. */
+ GCaps.shader_storage_buffer_objects_support = false;
+ }
glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &GLContext::max_ssbo_size);
}
- GLContext::base_instance_support = GLEW_ARB_base_instance;
- GLContext::clear_texture_support = GLEW_ARB_clear_texture;
- GLContext::copy_image_support = GLEW_ARB_copy_image;
- GLContext::debug_layer_support = GLEW_VERSION_4_3 || GLEW_KHR_debug || GLEW_ARB_debug_output;
- GLContext::direct_state_access_support = GLEW_ARB_direct_state_access;
- GLContext::explicit_location_support = GLEW_VERSION_4_3;
- GLContext::geometry_shader_invocations = GLEW_ARB_gpu_shader5;
- GLContext::fixed_restart_index_support = GLEW_ARB_ES3_compatibility;
- GLContext::layered_rendering_support = GLEW_AMD_vertex_shader_layer;
- GLContext::native_barycentric_support = GLEW_AMD_shader_explicit_vertex_parameter;
- GLContext::multi_bind_support = GLEW_ARB_multi_bind;
- GLContext::multi_draw_indirect_support = GLEW_ARB_multi_draw_indirect;
- GLContext::shader_draw_parameters_support = GLEW_ARB_shader_draw_parameters;
- GLContext::stencil_texturing_support = GLEW_VERSION_4_3;
- GLContext::texture_cube_map_array_support = GLEW_ARB_texture_cube_map_array;
- GLContext::texture_filter_anisotropic_support = GLEW_EXT_texture_filter_anisotropic;
- GLContext::texture_gather_support = GLEW_ARB_texture_gather;
- GLContext::texture_storage_support = GLEW_VERSION_4_3;
- GLContext::vertex_attrib_binding_support = GLEW_ARB_vertex_attrib_binding;
+ GLContext::base_instance_support = epoxy_has_gl_extension("GL_ARB_base_instance");
+ GLContext::clear_texture_support = epoxy_has_gl_extension("GL_ARB_clear_texture");
+ GLContext::copy_image_support = epoxy_has_gl_extension("GL_ARB_copy_image");
+ GLContext::debug_layer_support = epoxy_gl_version() >= 43 ||
+ epoxy_has_gl_extension("GL_KHR_debug") ||
+ epoxy_has_gl_extension("GL_ARB_debug_output");
+ GLContext::direct_state_access_support = epoxy_has_gl_extension("GL_ARB_direct_state_access");
+ GLContext::explicit_location_support = epoxy_gl_version() >= 43;
+ GLContext::geometry_shader_invocations = epoxy_has_gl_extension("GL_ARB_gpu_shader5");
+ GLContext::fixed_restart_index_support = epoxy_has_gl_extension("GL_ARB_ES3_compatibility");
+ GLContext::layered_rendering_support = epoxy_has_gl_extension("GL_AMD_vertex_shader_layer");
+ GLContext::native_barycentric_support = epoxy_has_gl_extension(
+ "GL_AMD_shader_explicit_vertex_parameter");
+ GLContext::multi_bind_support = epoxy_has_gl_extension("GL_ARB_multi_bind");
+ GLContext::multi_draw_indirect_support = epoxy_has_gl_extension("GL_ARB_multi_draw_indirect");
+ GLContext::shader_draw_parameters_support = epoxy_has_gl_extension(
+ "GL_ARB_shader_draw_parameters");
+ GLContext::stencil_texturing_support = epoxy_gl_version() >= 43;
+ GLContext::texture_cube_map_array_support = epoxy_has_gl_extension(
+ "GL_ARB_texture_cube_map_array");
+ GLContext::texture_filter_anisotropic_support = epoxy_has_gl_extension(
+ "GL_EXT_texture_filter_anisotropic");
+ GLContext::texture_gather_support = epoxy_has_gl_extension("GL_ARB_texture_gather");
+ GLContext::texture_storage_support = epoxy_gl_version() >= 43;
+ GLContext::vertex_attrib_binding_support = epoxy_has_gl_extension(
+ "GL_ARB_vertex_attrib_binding");
detect_workarounds();
diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh
index 29249111294..8646d94e2fd 100644
--- a/source/blender/gpu/opengl/gl_backend.hh
+++ b/source/blender/gpu/opengl/gl_backend.hh
@@ -42,11 +42,15 @@ class GLBackend : public GPUBackend {
}
~GLBackend()
{
- GLTexture::samplers_free();
-
GLBackend::platform_exit();
}
+ void delete_resources() override
+ {
+ /* Delete any resources with context active. */
+ GLTexture::samplers_free();
+ }
+
static GLBackend *get()
{
return static_cast<GLBackend *>(GPUBackend::get());
@@ -129,11 +133,11 @@ class GLBackend : public GPUBackend {
dynamic_cast<GLStorageBuf *>(indirect_buf)->bind_as(GL_DISPATCH_INDIRECT_BUFFER);
/* This barrier needs to be here as it only work on the currently bound indirect buffer. */
- glMemoryBarrier(GL_DRAW_INDIRECT_BUFFER);
+ glMemoryBarrier(GL_COMMAND_BARRIER_BIT);
glDispatchComputeIndirect((GLintptr)0);
/* Unbind. */
- glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
+ glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0);
}
/* Render Frame Coordination */
diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc
index fde2a53bb0f..ff8867fe3e6 100644
--- a/source/blender/gpu/opengl/gl_batch.cc
+++ b/source/blender/gpu/opengl/gl_batch.cc
@@ -18,6 +18,7 @@
#include "gl_debug.hh"
#include "gl_index_buffer.hh"
#include "gl_primitive.hh"
+#include "gl_storage_buffer.hh"
#include "gl_vertex_array.hh"
#include "gl_batch.hh"
@@ -326,4 +327,55 @@ void GLBatch::draw(int v_first, int v_count, int i_first, int i_count)
}
}
+void GLBatch::draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset)
+{
+ GL_CHECK_RESOURCES("Batch");
+
+ this->bind(0);
+
+ /* TODO(fclem): Make the barrier and binding optional if consecutive draws are issued. */
+ dynamic_cast<GLStorageBuf *>(unwrap(indirect_buf))->bind_as(GL_DRAW_INDIRECT_BUFFER);
+ /* This barrier needs to be here as it only work on the currently bound indirect buffer. */
+ glMemoryBarrier(GL_COMMAND_BARRIER_BIT);
+
+ GLenum gl_type = to_gl(prim_type);
+ if (elem) {
+ const GLIndexBuf *el = this->elem_();
+ GLenum index_type = to_gl(el->index_type_);
+ glDrawElementsIndirect(gl_type, index_type, (GLvoid *)offset);
+ }
+ else {
+ glDrawArraysIndirect(gl_type, (GLvoid *)offset);
+ }
+ /* Unbind. */
+ glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
+}
+
+void GLBatch::multi_draw_indirect(GPUStorageBuf *indirect_buf,
+ int count,
+ intptr_t offset,
+ intptr_t stride)
+{
+ GL_CHECK_RESOURCES("Batch");
+
+ this->bind(0);
+
+ /* TODO(fclem): Make the barrier and binding optional if consecutive draws are issued. */
+ dynamic_cast<GLStorageBuf *>(unwrap(indirect_buf))->bind_as(GL_DRAW_INDIRECT_BUFFER);
+ /* This barrier needs to be here as it only work on the currently bound indirect buffer. */
+ glMemoryBarrier(GL_COMMAND_BARRIER_BIT);
+
+ GLenum gl_type = to_gl(prim_type);
+ if (elem) {
+ const GLIndexBuf *el = this->elem_();
+ GLenum index_type = to_gl(el->index_type_);
+ glMultiDrawElementsIndirect(gl_type, index_type, (GLvoid *)offset, count, stride);
+ }
+ else {
+ glMultiDrawArraysIndirect(gl_type, (GLvoid *)offset, count, stride);
+ }
+ /* Unbind. */
+ glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
+}
+
/** \} */
diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh
index a25e495b3b1..714aa1220be 100644
--- a/source/blender/gpu/opengl/gl_batch.hh
+++ b/source/blender/gpu/opengl/gl_batch.hh
@@ -17,8 +17,6 @@
#include "gl_index_buffer.hh"
#include "gl_vertex_buffer.hh"
-#include "glew-mx.h"
-
namespace blender {
namespace gpu {
@@ -35,9 +33,9 @@ class GLShaderInterface;
class GLVaoCache {
private:
/** Context for which the vao_cache_ was generated. */
- GLContext *context_ = NULL;
+ GLContext *context_ = nullptr;
/** Last interface this batch was drawn with. */
- GLShaderInterface *interface_ = NULL;
+ GLShaderInterface *interface_ = nullptr;
/** Cached VAO for the last interface. */
GLuint vao_id_ = 0;
/** Used when arb_base_instance is not supported. */
@@ -93,6 +91,11 @@ class GLBatch : public Batch {
public:
void draw(int v_first, int v_count, int i_first, int i_count) override;
+ void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override;
+ void multi_draw_indirect(GPUStorageBuf *indirect_buf,
+ int count,
+ intptr_t offset,
+ intptr_t stride) override;
void bind(int i_first);
/* Convenience getters. */
diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc
index e6af126e9cd..31bd7e0c4dd 100644
--- a/source/blender/gpu/opengl/gl_context.cc
+++ b/source/blender/gpu/opengl/gl_context.cc
@@ -76,7 +76,7 @@ GLContext::GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list
}
}
else {
- /* For off-screen contexts. Default frame-buffer is NULL. */
+ /* For off-screen contexts. Default frame-buffer is null. */
back_left = new GLFrameBuffer("back_left", this, GL_NONE, 0, 0, 0);
}
@@ -304,12 +304,12 @@ void GLContext::vao_cache_unregister(GLVaoCache *cache)
void GLContext::memory_statistics_get(int *r_total_mem, int *r_free_mem)
{
/* TODO(merwin): use Apple's platform API to get this info. */
- if (GLEW_NVX_gpu_memory_info) {
+ if (epoxy_has_gl_extension("GL_NVX_gpu_memory_info")) {
/* Returned value in Kb. */
glGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, r_total_mem);
glGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, r_free_mem);
}
- else if (GLEW_ATI_meminfo) {
+ else if (epoxy_has_gl_extension("GL_ATI_meminfo")) {
int stats[4];
glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, stats);
diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh
index 234bc712513..1d413750fd4 100644
--- a/source/blender/gpu/opengl/gl_context.hh
+++ b/source/blender/gpu/opengl/gl_context.hh
@@ -16,8 +16,6 @@
#include "gl_state.hh"
-#include "glew-mx.h"
-
#include <mutex>
namespace blender {
@@ -42,7 +40,6 @@ class GLContext : public Context {
/** Capabilities. */
static GLint max_cubemap_size;
- static GLint max_texture_3d_size;
static GLint max_ubo_size;
static GLint max_ubo_binds;
static GLint max_ssbo_size;
diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc
index f82138e0d65..4c9f766c93c 100644
--- a/source/blender/gpu/opengl/gl_debug.cc
+++ b/source/blender/gpu/opengl/gl_debug.cc
@@ -19,8 +19,6 @@
#include "CLG_log.h"
-#include "glew-mx.h"
-
#include "gl_context.hh"
#include "gl_uniform_buffer.hh"
@@ -138,8 +136,8 @@ void init_gl_callbacks()
char msg[256] = "";
const char format[] = "Successfully hooked OpenGL debug callback using %s";
- if (GLEW_VERSION_4_3 || GLEW_KHR_debug) {
- SNPRINTF(msg, format, GLEW_VERSION_4_3 ? "OpenGL 4.3" : "KHR_debug extension");
+ if (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug")) {
+ SNPRINTF(msg, format, epoxy_gl_version() >= 43 ? "OpenGL 4.3" : "KHR_debug extension");
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback((GLDEBUGPROC)debug_callback, nullptr);
@@ -151,7 +149,7 @@ void init_gl_callbacks()
-1,
msg);
}
- else if (GLEW_ARB_debug_output) {
+ else if (epoxy_has_gl_extension("GL_ARB_debug_output")) {
SNPRINTF(msg, format, "ARB_debug_output");
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallbackARB((GLDEBUGPROCARB)debug_callback, nullptr);
@@ -189,7 +187,7 @@ void check_gl_error(const char *info)
case err: { \
char msg[256]; \
SNPRINTF(msg, "%s : %s", #err, info); \
- debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); \
+ debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, nullptr); \
break; \
}
@@ -327,7 +325,8 @@ static const char *to_str_suffix(GLenum type)
void object_label(GLenum type, GLuint object, const char *name)
{
- if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ if ((G.debug & G_DEBUG_GPU) &&
+ (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug"))) {
char label[64];
SNPRINTF(label, "%s%s%s", to_str_prefix(type), name, to_str_suffix(type));
/* Small convenience for caller. */
@@ -365,7 +364,8 @@ namespace blender::gpu {
void GLContext::debug_group_begin(const char *name, int index)
{
- if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ if ((G.debug & G_DEBUG_GPU) &&
+ (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug"))) {
/* Add 10 to avoid collision with other indices from other possible callback layers. */
index += 10;
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, index, -1, name);
@@ -374,7 +374,8 @@ void GLContext::debug_group_begin(const char *name, int index)
void GLContext::debug_group_end()
{
- if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) {
+ if ((G.debug & G_DEBUG_GPU) &&
+ (epoxy_gl_version() >= 43 || epoxy_has_gl_extension("GL_KHR_debug"))) {
glPopDebugGroup();
}
}
diff --git a/source/blender/gpu/opengl/gl_debug.hh b/source/blender/gpu/opengl/gl_debug.hh
index e24b6f2bb23..b573196216d 100644
--- a/source/blender/gpu/opengl/gl_debug.hh
+++ b/source/blender/gpu/opengl/gl_debug.hh
@@ -8,8 +8,6 @@
#include "gl_context.hh"
-#include "glew-mx.h"
-
/* Manual line breaks for readability. */
/* clang-format off */
#define _VA_ARG_LIST1(t) t
diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc
index bd9fba4250d..1ab22a16df4 100644
--- a/source/blender/gpu/opengl/gl_framebuffer.cc
+++ b/source/blender/gpu/opengl/gl_framebuffer.cc
@@ -207,6 +207,11 @@ void GLFrameBuffer::update_attachments()
this->size_set(size[0], size[1]);
srgb_ = (GPU_texture_format(attach.tex) == GPU_SRGB8_A8);
}
+ else {
+ /* Empty frame-buffer. */
+ glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, width_);
+ glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, height_);
+ }
dirty_attachments_ = false;
diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh
index 224c7a92c0a..edc05322153 100644
--- a/source/blender/gpu/opengl/gl_framebuffer.hh
+++ b/source/blender/gpu/opengl/gl_framebuffer.hh
@@ -11,8 +11,6 @@
#include "MEM_guardedalloc.h"
-#include "glew-mx.h"
-
#include "gpu_framebuffer_private.hh"
namespace blender::gpu {
@@ -30,11 +28,11 @@ class GLFrameBuffer : public FrameBuffer {
/** OpenGL handle. */
GLuint fbo_id_ = 0;
/** Context the handle is from. Frame-buffers are not shared across contexts. */
- GLContext *context_ = NULL;
+ GLContext *context_ = nullptr;
/** State Manager of the same contexts. */
- GLStateManager *state_manager_ = NULL;
+ GLStateManager *state_manager_ = nullptr;
/** Copy of the GL state. Contains ONLY color attachments enums for slot binding. */
- GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT];
+ GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT] = {0};
/** Internal frame-buffers are immutable. */
bool immutable_;
/** True is the frame-buffer has its first color target using the GPU_SRGB8_A8 format. */
diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh
index eb94dc20e21..5c6ff510cef 100644
--- a/source/blender/gpu/opengl/gl_immediate.hh
+++ b/source/blender/gpu/opengl/gl_immediate.hh
@@ -11,8 +11,6 @@
#include "MEM_guardedalloc.h"
-#include "glew-mx.h"
-
#include "gpu_immediate_private.hh"
namespace blender::gpu {
diff --git a/source/blender/gpu/opengl/gl_index_buffer.hh b/source/blender/gpu/opengl/gl_index_buffer.hh
index 8a10884d48b..974c01d2b65 100644
--- a/source/blender/gpu/opengl/gl_index_buffer.hh
+++ b/source/blender/gpu/opengl/gl_index_buffer.hh
@@ -11,7 +11,7 @@
#include "gpu_index_buffer_private.hh"
-#include "glew-mx.h"
+#include <epoxy/gl.h>
namespace blender::gpu {
@@ -35,9 +35,11 @@ class GLIndexBuf : public IndexBuf {
{
additional_vertex_offset += index_start_;
if (index_type_ == GPU_INDEX_U32) {
- return (GLuint *)0 + additional_vertex_offset;
+ return reinterpret_cast<void *>(static_cast<intptr_t>(additional_vertex_offset) *
+ sizeof(GLuint));
}
- return (GLushort *)0 + additional_vertex_offset;
+ return reinterpret_cast<void *>(static_cast<intptr_t>(additional_vertex_offset) *
+ sizeof(GLushort));
}
GLuint restart_index() const
@@ -51,6 +53,10 @@ class GLIndexBuf : public IndexBuf {
private:
bool is_active() const;
+ void strip_restart_indices() override
+ {
+ /* No-op. */
+ }
MEM_CXX_CLASS_ALLOC_FUNCS("GLIndexBuf")
};
diff --git a/source/blender/gpu/opengl/gl_primitive.hh b/source/blender/gpu/opengl/gl_primitive.hh
index 2a8590e8b3e..c4c7734a2cd 100644
--- a/source/blender/gpu/opengl/gl_primitive.hh
+++ b/source/blender/gpu/opengl/gl_primitive.hh
@@ -13,8 +13,6 @@
#include "GPU_primitive.h"
-#include "glew-mx.h"
-
namespace blender::gpu {
static inline GLenum to_gl(GPUPrimType prim_type)
diff --git a/source/blender/gpu/opengl/gl_query.hh b/source/blender/gpu/opengl/gl_query.hh
index e15a2584e07..a851ab4ecdd 100644
--- a/source/blender/gpu/opengl/gl_query.hh
+++ b/source/blender/gpu/opengl/gl_query.hh
@@ -11,7 +11,7 @@
#include "gpu_query.hh"
-#include "glew-mx.h"
+#include <epoxy/gl.h>
namespace blender::gpu {
diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc
index ccdf10c1ed2..1f2ef36716e 100644
--- a/source/blender/gpu/opengl/gl_shader.cc
+++ b/source/blender/gpu/opengl/gl_shader.cc
@@ -545,7 +545,7 @@ std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) con
if (!GLContext::native_barycentric_support) {
/* Disabled or unsupported. */
}
- else if (GLEW_AMD_shader_explicit_vertex_parameter) {
+ else if (epoxy_has_gl_extension("GL_AMD_shader_explicit_vertex_parameter")) {
/* Need this for stable barycentric. */
ss << "flat out vec4 gpu_pos_flat;\n";
ss << "out vec4 gpu_pos;\n";
@@ -581,7 +581,7 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
ss << "noperspective in vec3 gpu_BaryCoordNoPersp;\n";
ss << "#define gpu_position_at_vertex(v) gpu_pos[v]\n";
}
- else if (GLEW_AMD_shader_explicit_vertex_parameter) {
+ else if (epoxy_has_gl_extension("GL_AMD_shader_explicit_vertex_parameter")) {
std::cout << "native" << std::endl;
/* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry
* shader workaround if this extension/feature is detected. */
@@ -612,7 +612,7 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c
if (info.early_fragment_test_) {
ss << "layout(early_fragment_tests) in;\n";
}
- if (GLEW_ARB_conservative_depth) {
+ if (epoxy_has_gl_extension("GL_ARB_conservative_depth")) {
ss << "layout(" << to_string(info.depth_write_) << ") out float gl_FragDepth;\n";
}
ss << "\n/* Outputs. */\n";
@@ -805,7 +805,7 @@ static char *glsl_patch_default_get()
size_t slen = 0;
/* Version need to go first. */
- if (GLEW_VERSION_4_3) {
+ if (epoxy_gl_version() >= 43) {
STR_CONCAT(patch, slen, "#version 430\n");
}
else {
@@ -816,8 +816,8 @@ static char *glsl_patch_default_get()
* don't use an extension for something already available! */
if (GLContext::texture_gather_support) {
STR_CONCAT(patch, slen, "#extension GL_ARB_texture_gather: enable\n");
- /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the
- * shader so double check the preprocessor define (see T56544). */
+ /* Some drivers don't agree on epoxy_has_gl_extension("GL_ARB_texture_gather") and the actual
+ * support in the shader so double check the preprocessor define (see T56544). */
STR_CONCAT(patch, slen, "#ifdef GL_ARB_texture_gather\n");
STR_CONCAT(patch, slen, "# define GPU_ARB_texture_gather\n");
STR_CONCAT(patch, slen, "#endif\n");
@@ -835,7 +835,7 @@ static char *glsl_patch_default_get()
STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n");
STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n");
}
- if (GLEW_ARB_conservative_depth) {
+ if (epoxy_has_gl_extension("GL_ARB_conservative_depth")) {
STR_CONCAT(patch, slen, "#extension GL_ARB_conservative_depth : enable\n");
}
if (GPU_shader_image_load_store_support()) {
@@ -1136,108 +1136,6 @@ void GLShader::uniform_int(int location, int comp_len, int array_size, const int
/** \name GPUVertFormat from Shader
* \{ */
-static uint calc_component_size(const GLenum gl_type)
-{
- switch (gl_type) {
- case GL_FLOAT_VEC2:
- case GL_INT_VEC2:
- case GL_UNSIGNED_INT_VEC2:
- return 2;
- case GL_FLOAT_VEC3:
- case GL_INT_VEC3:
- case GL_UNSIGNED_INT_VEC3:
- return 3;
- case GL_FLOAT_VEC4:
- case GL_FLOAT_MAT2:
- case GL_INT_VEC4:
- case GL_UNSIGNED_INT_VEC4:
- return 4;
- case GL_FLOAT_MAT3:
- return 9;
- case GL_FLOAT_MAT4:
- return 16;
- case GL_FLOAT_MAT2x3:
- case GL_FLOAT_MAT3x2:
- return 6;
- case GL_FLOAT_MAT2x4:
- case GL_FLOAT_MAT4x2:
- return 8;
- case GL_FLOAT_MAT3x4:
- case GL_FLOAT_MAT4x3:
- return 12;
- default:
- return 1;
- }
-}
-
-static void get_fetch_mode_and_comp_type(int gl_type,
- GPUVertCompType *r_comp_type,
- GPUVertFetchMode *r_fetch_mode)
-{
- switch (gl_type) {
- case GL_FLOAT:
- case GL_FLOAT_VEC2:
- case GL_FLOAT_VEC3:
- case GL_FLOAT_VEC4:
- case GL_FLOAT_MAT2:
- case GL_FLOAT_MAT3:
- case GL_FLOAT_MAT4:
- case GL_FLOAT_MAT2x3:
- case GL_FLOAT_MAT2x4:
- case GL_FLOAT_MAT3x2:
- case GL_FLOAT_MAT3x4:
- case GL_FLOAT_MAT4x2:
- case GL_FLOAT_MAT4x3:
- *r_comp_type = GPU_COMP_F32;
- *r_fetch_mode = GPU_FETCH_FLOAT;
- break;
- case GL_INT:
- case GL_INT_VEC2:
- case GL_INT_VEC3:
- case GL_INT_VEC4:
- *r_comp_type = GPU_COMP_I32;
- *r_fetch_mode = GPU_FETCH_INT;
- break;
- case GL_UNSIGNED_INT:
- case GL_UNSIGNED_INT_VEC2:
- case GL_UNSIGNED_INT_VEC3:
- case GL_UNSIGNED_INT_VEC4:
- *r_comp_type = GPU_COMP_U32;
- *r_fetch_mode = GPU_FETCH_INT;
- break;
- default:
- BLI_assert(0);
- }
-}
-
-void GLShader::vertformat_from_shader(GPUVertFormat *format) const
-{
- GPU_vertformat_clear(format);
-
- GLint attr_len;
- glGetProgramiv(shader_program_, GL_ACTIVE_ATTRIBUTES, &attr_len);
-
- for (int i = 0; i < attr_len; i++) {
- char name[256];
- GLenum gl_type;
- GLint size;
- glGetActiveAttrib(shader_program_, i, sizeof(name), nullptr, &size, &gl_type, name);
-
- /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */
- if (glGetAttribLocation(shader_program_, name) == -1) {
- continue;
- }
-
- GPUVertCompType comp_type;
- GPUVertFetchMode fetch_mode;
- get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode);
-
- int comp_len = calc_component_size(gl_type) * size;
-
- GPU_vertformat_attr_add(format, name, comp_type, comp_len, fetch_mode);
- }
-}
-
int GLShader::program_handle_get() const
{
return (int)this->shader_program_;
diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh
index 9c21d0c6230..bebbb2fa82e 100644
--- a/source/blender/gpu/opengl/gl_shader.hh
+++ b/source/blender/gpu/opengl/gl_shader.hh
@@ -9,7 +9,7 @@
#include "MEM_guardedalloc.h"
-#include "glew-mx.h"
+#include <epoxy/gl.h>
#include "gpu_shader_create_info.hh"
#include "gpu_shader_private.hh"
@@ -67,8 +67,6 @@ class GLShader : public Shader {
void uniform_float(int location, int comp_len, int array_size, const float *data) override;
void uniform_int(int location, int comp_len, int array_size, const int *data) override;
- void vertformat_from_shader(GPUVertFormat *format) const override;
-
/** DEPRECATED: Kept only because of BGL API. */
int program_handle_get() const override;
diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc
index 1b3ab2941a8..b230706b020 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.cc
+++ b/source/blender/gpu/opengl/gl_shader_interface.cc
@@ -16,6 +16,7 @@
#include "GPU_capabilities.h"
+using namespace blender::gpu::shader;
namespace blender::gpu {
/* -------------------------------------------------------------------- */
@@ -151,6 +152,52 @@ static inline int ssbo_binding(int32_t program, uint32_t ssbo_index)
/** \name Creation / Destruction
* \{ */
+static Type gpu_type_from_gl_type(int gl_type)
+{
+ switch (gl_type) {
+ case GL_FLOAT:
+ return Type::FLOAT;
+ case GL_FLOAT_VEC2:
+ return Type::VEC2;
+ case GL_FLOAT_VEC3:
+ return Type::VEC3;
+ case GL_FLOAT_VEC4:
+ return Type::VEC4;
+ case GL_FLOAT_MAT3:
+ return Type::MAT3;
+ case GL_FLOAT_MAT4:
+ return Type::MAT4;
+ case GL_UNSIGNED_INT:
+ return Type::UINT;
+ case GL_UNSIGNED_INT_VEC2:
+ return Type::UVEC2;
+ case GL_UNSIGNED_INT_VEC3:
+ return Type::UVEC3;
+ case GL_UNSIGNED_INT_VEC4:
+ return Type::UVEC4;
+ case GL_INT:
+ return Type::INT;
+ case GL_INT_VEC2:
+ return Type::IVEC2;
+ case GL_INT_VEC3:
+ return Type::IVEC3;
+ case GL_INT_VEC4:
+ return Type::IVEC4;
+ case GL_BOOL:
+ return Type::BOOL;
+ case GL_FLOAT_MAT2:
+ case GL_FLOAT_MAT2x3:
+ case GL_FLOAT_MAT2x4:
+ case GL_FLOAT_MAT3x2:
+ case GL_FLOAT_MAT3x4:
+ case GL_FLOAT_MAT4x2:
+ case GL_FLOAT_MAT4x3:
+ default:
+ BLI_assert(0);
+ }
+ return Type::FLOAT;
+}
+
GLShaderInterface::GLShaderInterface(GLuint program)
{
/* Necessary to make #glUniform works. */
@@ -246,6 +293,9 @@ GLShaderInterface::GLShaderInterface(GLuint program)
name_buffer_offset += set_input_name(input, name, name_len);
enabled_attr_mask_ |= (1 << input->location);
+
+ /* Used in `GPU_shader_get_attribute_info`. */
+ attr_types_[input->location] = (uint8_t)gpu_type_from_gl_type(type);
}
/* Uniform Blocks */
@@ -318,6 +368,13 @@ GLShaderInterface::GLShaderInterface(GLuint program)
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
}
+ /* Builtin Storage Buffers */
+ for (int32_t u_int = 0; u_int < GPU_NUM_STORAGE_BUFFERS; u_int++) {
+ GPUStorageBufferBuiltin u = static_cast<GPUStorageBufferBuiltin>(u_int);
+ const ShaderInput *block = this->ssbo_get(builtin_storage_block_name(u));
+ builtin_buffers_[u] = (block != nullptr) ? block->binding : -1;
+ }
+
MEM_freeN(uniforms_from_blocks);
/* Resize name buffer to save some memory. */
@@ -398,7 +455,11 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
}
if (input->location != -1) {
enabled_attr_mask_ |= (1 << input->location);
+
+ /* Used in `GPU_shader_get_attribute_info`. */
+ attr_types_[input->location] = (uint8_t)attr.type;
}
+
input++;
}
@@ -481,6 +542,13 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI
builtin_blocks_[u] = (block != nullptr) ? block->binding : -1;
}
+ /* Builtin Storage Buffers */
+ for (int32_t u_int = 0; u_int < GPU_NUM_STORAGE_BUFFERS; u_int++) {
+ GPUStorageBufferBuiltin u = static_cast<GPUStorageBufferBuiltin>(u_int);
+ const ShaderInput *block = this->ssbo_get(builtin_storage_block_name(u));
+ builtin_buffers_[u] = (block != nullptr) ? block->binding : -1;
+ }
+
this->sort_inputs();
// this->debug_print();
diff --git a/source/blender/gpu/opengl/gl_shader_interface.hh b/source/blender/gpu/opengl/gl_shader_interface.hh
index e3dce31758b..e31879d4340 100644
--- a/source/blender/gpu/opengl/gl_shader_interface.hh
+++ b/source/blender/gpu/opengl/gl_shader_interface.hh
@@ -16,8 +16,6 @@
#include "BLI_vector.hh"
-#include "glew-mx.h"
-
#include "gpu_shader_create_info.hh"
#include "gpu_shader_interface.hh"
diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc
index 8be4ac29af6..46422124112 100644
--- a/source/blender/gpu/opengl/gl_state.cc
+++ b/source/blender/gpu/opengl/gl_state.cc
@@ -563,14 +563,14 @@ void GLStateManager::image_bind(Texture *tex_, int unit)
}
images_[unit] = tex->tex_id_;
formats_[unit] = to_gl_internal_format(tex->format_);
- tex->is_bound_ = true;
+ tex->is_bound_image_ = true;
dirty_image_binds_ |= 1ULL << unit;
}
void GLStateManager::image_unbind(Texture *tex_)
{
GLTexture *tex = static_cast<GLTexture *>(tex_);
- if (!tex->is_bound_) {
+ if (!tex->is_bound_image_) {
return;
}
@@ -581,7 +581,7 @@ void GLStateManager::image_unbind(Texture *tex_)
dirty_image_binds_ |= 1ULL << i;
}
}
- tex->is_bound_ = false;
+ tex->is_bound_image_ = false;
}
void GLStateManager::image_unbind_all()
diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh
index f29eefbca82..74c68e51755 100644
--- a/source/blender/gpu/opengl/gl_state.hh
+++ b/source/blender/gpu/opengl/gl_state.hh
@@ -13,7 +13,7 @@
#include "gpu_state_private.hh"
-#include "glew-mx.h"
+#include <epoxy/gl.h>
namespace blender {
namespace gpu {
diff --git a/source/blender/gpu/opengl/gl_storage_buffer.cc b/source/blender/gpu/opengl/gl_storage_buffer.cc
index 4592adc3a61..5d876308b3c 100644
--- a/source/blender/gpu/opengl/gl_storage_buffer.cc
+++ b/source/blender/gpu/opengl/gl_storage_buffer.cc
@@ -72,7 +72,7 @@ void GLStorageBuf::bind(int slot)
if (slot >= GLContext::max_ssbo_binds) {
fprintf(
stderr,
- "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.",
+ "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.\n",
name_,
slot,
GLContext::max_ssbo_binds);
@@ -166,6 +166,23 @@ void GLStorageBuf::copy_sub(VertBuf *src_, uint dst_offset, uint src_offset, uin
}
}
+void GLStorageBuf::read(void *data)
+{
+ if (ssbo_id_ == 0) {
+ this->init();
+ }
+
+ if (GLContext::direct_state_access_support) {
+ glGetNamedBufferSubData(ssbo_id_, 0, size_in_bytes_, data);
+ }
+ else {
+ /* This binds the buffer to GL_ARRAY_BUFFER and upload the data if any. */
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_);
+ glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, size_in_bytes_, data);
+ glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
+ }
+}
+
/** \} */
} // namespace blender::gpu
diff --git a/source/blender/gpu/opengl/gl_storage_buffer.hh b/source/blender/gpu/opengl/gl_storage_buffer.hh
index 96052fe0065..680ce911bc7 100644
--- a/source/blender/gpu/opengl/gl_storage_buffer.hh
+++ b/source/blender/gpu/opengl/gl_storage_buffer.hh
@@ -11,8 +11,6 @@
#include "gpu_storage_buffer_private.hh"
-#include "glew-mx.h"
-
namespace blender {
namespace gpu {
@@ -37,6 +35,7 @@ class GLStorageBuf : public StorageBuf {
void unbind() override;
void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) override;
void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override;
+ void read(void *data) override;
/* Special internal function to bind SSBOs to indirect argument targets. */
void bind_as(GLenum target);
diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc
index 055c8d104e2..02fc7dfdb5f 100644
--- a/source/blender/gpu/opengl/gl_texture.cc
+++ b/source/blender/gpu/opengl/gl_texture.cc
@@ -40,6 +40,7 @@ GLTexture::~GLTexture()
if (ctx != nullptr && is_bound_) {
/* This avoid errors when the texture is still inside the bound texture array. */
ctx->state_manager->texture_unbind(this);
+ ctx->state_manager->image_unbind(this);
}
GLContext::tex_free(tex_id_);
}
@@ -310,6 +311,12 @@ void GLTexture::update_sub(
*/
void GLTexture::generate_mipmap()
{
+ /* Allow users to provide mipmaps stored in compressed textures.
+ * Skip generating mipmaps to avoid overriding the existing ones. */
+ if (format_flag_ & GPU_FORMAT_COMPRESSED) {
+ return;
+ }
+
/* Some drivers have bugs when using #glGenerateMipmap with depth textures (see T56789).
* In this case we just create a complete texture with mipmaps manually without
* down-sampling. You must initialize the texture levels using other methods like
@@ -596,7 +603,7 @@ bool GLTexture::proxy_check(int mip)
{
/* Manual validation first, since some implementation have issues with proxy creation. */
int max_size = GPU_max_texture_size();
- int max_3d_size = GLContext::max_texture_3d_size;
+ int max_3d_size = GPU_max_texture_3d_size();
int max_cube_size = GLContext::max_cubemap_size;
int size[3] = {1, 1, 1};
this->mip_size_get(mip, size);
diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh
index e5b879f1f15..b7d72455c25 100644
--- a/source/blender/gpu/opengl/gl_texture.hh
+++ b/source/blender/gpu/opengl/gl_texture.hh
@@ -13,8 +13,6 @@
#include "gpu_texture_private.hh"
-#include "glew-mx.h"
-
struct GPUFrameBuffer;
namespace blender {
@@ -33,10 +31,12 @@ class GLTexture : public Texture {
/** opengl identifier for texture. */
GLuint tex_id_ = 0;
/** Legacy workaround for texture copy. Created when using framebuffer_get(). */
- struct GPUFrameBuffer *framebuffer_ = NULL;
+ struct GPUFrameBuffer *framebuffer_ = nullptr;
/** True if this texture is bound to at least one texture unit. */
/* TODO(fclem): How do we ensure thread safety here? */
bool is_bound_ = false;
+ /** Same as is_bound_ but for image slots. */
+ bool is_bound_image_ = false;
/** True if pixels in the texture have been initialized. */
bool has_pixels_ = false;
diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc
index e58cea9de43..022fbcfdf29 100644
--- a/source/blender/gpu/opengl/gl_uniform_buffer.cc
+++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc
@@ -65,11 +65,12 @@ void GLUniformBuf::update(const void *data)
void GLUniformBuf::bind(int slot)
{
if (slot >= GLContext::max_ubo_binds) {
- fprintf(stderr,
- "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.",
- name_,
- slot,
- GLContext::max_ubo_binds);
+ fprintf(
+ stderr,
+ "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.\n",
+ name_,
+ slot,
+ GLContext::max_ubo_binds);
return;
}
diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.hh b/source/blender/gpu/opengl/gl_uniform_buffer.hh
index 8d945a8e7dc..e602532dc5a 100644
--- a/source/blender/gpu/opengl/gl_uniform_buffer.hh
+++ b/source/blender/gpu/opengl/gl_uniform_buffer.hh
@@ -11,8 +11,6 @@
#include "gpu_uniform_buffer_private.hh"
-#include "glew-mx.h"
-
namespace blender {
namespace gpu {
diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc
index a3299fc3325..6897ac9f4a2 100644
--- a/source/blender/gpu/opengl/gl_vertex_array.cc
+++ b/source/blender/gpu/opengl/gl_vertex_array.cc
@@ -11,6 +11,7 @@
#include "gl_batch.hh"
#include "gl_context.hh"
#include "gl_index_buffer.hh"
+#include "gl_storage_buffer.hh"
#include "gl_vertex_buffer.hh"
#include "gl_vertex_array.hh"
@@ -21,7 +22,7 @@ namespace blender::gpu {
/** \name Vertex Array Bindings
* \{ */
-/* Returns enabled vertex pointers as a bitflag (one bit per attrib). */
+/** Returns enabled vertex pointers as a bit-flag (one bit per attribute). */
static uint16_t vbo_bind(const ShaderInterface *interface,
const GPUVertFormat *format,
uint v_first,
@@ -118,6 +119,18 @@ void GLVertArray::update_bindings(const GLuint vao,
}
}
+ if (batch->resource_id_buf) {
+ const ShaderInput *input = interface->attr_get("drw_ResourceID");
+ if (input) {
+ dynamic_cast<GLStorageBuf *>(unwrap(batch->resource_id_buf))->bind_as(GL_ARRAY_BUFFER);
+ glEnableVertexAttribArray(input->location);
+ glVertexAttribDivisor(input->location, 1);
+ glVertexAttribIPointer(
+ input->location, 1, to_gl(GPU_COMP_I32), sizeof(uint32_t), (GLvoid *)nullptr);
+ attr_mask &= ~(1 << input->location);
+ }
+ }
+
if (attr_mask != 0 && GLContext::vertex_attrib_binding_support) {
for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) {
if (attr_mask & mask) {
diff --git a/source/blender/gpu/opengl/gl_vertex_array.hh b/source/blender/gpu/opengl/gl_vertex_array.hh
index d1d6c5604b5..4f417beed29 100644
--- a/source/blender/gpu/opengl/gl_vertex_array.hh
+++ b/source/blender/gpu/opengl/gl_vertex_array.hh
@@ -7,8 +7,6 @@
#pragma once
-#include "glew-mx.h"
-
#include "GPU_batch.h"
#include "gl_shader_interface.hh"
diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh
index e0a21587b60..deb966961f2 100644
--- a/source/blender/gpu/opengl/gl_vertex_buffer.hh
+++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh
@@ -9,8 +9,6 @@
#include "MEM_guardedalloc.h"
-#include "glew-mx.h"
-
#include "GPU_texture.h"
#include "gpu_vertex_buffer_private.hh"
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
index fe89985ae7f..33108d3a989 100644
--- a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl
@@ -140,6 +140,84 @@ void hsl_to_rgb(vec4 hsl, out vec4 outcol)
outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w);
}
+/* ** YCCA to RGBA ** */
+
+void ycca_to_rgba_itu_601(vec4 ycca, out vec4 color)
+{
+ ycca.xyz *= 255.0;
+ ycca.xyz -= vec3(16.0, 128.0, 128.0);
+ color.rgb = mat3(vec3(1.164), 0.0, -0.392, 2.017, 1.596, -0.813, 0.0) * ycca.xyz;
+ color.rgb /= 255.0;
+ color.a = ycca.a;
+}
+
+void ycca_to_rgba_itu_709(vec4 ycca, out vec4 color)
+{
+ ycca.xyz *= 255.0;
+ ycca.xyz -= vec3(16.0, 128.0, 128.0);
+ color.rgb = mat3(vec3(1.164), 0.0, -0.213, 2.115, 1.793, -0.534, 0.0) * ycca.xyz;
+ color.rgb /= 255.0;
+ color.a = ycca.a;
+}
+
+void ycca_to_rgba_jpeg(vec4 ycca, out vec4 color)
+{
+ ycca.xyz *= 255.0;
+ color.rgb = mat3(vec3(1.0), 0.0, -0.34414, 1.772, 1.402, -0.71414, 0.0) * ycca.xyz;
+ color.rgb += vec3(-179.456, 135.45984, -226.816);
+ color.rgb /= 255.0;
+ color.a = ycca.a;
+}
+
+/* ** RGBA to YCCA ** */
+
+void rgba_to_ycca_itu_601(vec4 rgba, out vec4 ycca)
+{
+ rgba.rgb *= 255.0;
+ ycca.xyz = mat3(0.257, -0.148, 0.439, 0.504, -0.291, -0.368, 0.098, 0.439, -0.071) * rgba.rgb;
+ ycca.xyz += vec3(16.0, 128.0, 128.0);
+ ycca.xyz /= 255.0;
+ ycca.a = rgba.a;
+}
+
+void rgba_to_ycca_itu_709(vec4 rgba, out vec4 ycca)
+{
+ rgba.rgb *= 255.0;
+ ycca.xyz = mat3(0.183, -0.101, 0.439, 0.614, -0.338, -0.399, 0.062, 0.439, -0.040) * rgba.rgb;
+ ycca.xyz += vec3(16.0, 128.0, 128.0);
+ ycca.xyz /= 255.0;
+ ycca.a = rgba.a;
+}
+
+void rgba_to_ycca_jpeg(vec4 rgba, out vec4 ycca)
+{
+ rgba.rgb *= 255.0;
+ ycca.xyz = mat3(0.299, -0.16874, 0.5, 0.587, -0.33126, -0.41869, 0.114, 0.5, -0.08131) *
+ rgba.rgb;
+ ycca.xyz += vec3(0.0, 128.0, 128.0);
+ ycca.xyz /= 255.0;
+ ycca.a = rgba.a;
+}
+
+/* ** YUVA to RGBA ** */
+
+void yuva_to_rgba_itu_709(vec4 yuva, out vec4 color)
+{
+ color.rgb = mat3(vec3(1.0), 0.0, -0.21482, 2.12798, 1.28033, -0.38059, 0.0) * yuva.xyz;
+ color.a = yuva.a;
+}
+
+/* ** RGBA to YUVA ** */
+
+void rgba_to_yuva_itu_709(vec4 rgba, out vec4 yuva)
+{
+ yuva.xyz = mat3(0.2126, -0.09991, 0.615, 0.7152, -0.33609, -0.55861, 0.0722, 0.436, -0.05639) *
+ rgba.rgb;
+ yuva.a = rgba.a;
+}
+
+/* ** Alpha Handling ** */
+
void color_alpha_clear(vec4 color, out vec4 result)
{
result = vec4(color.rgb, 1.0);
@@ -147,15 +225,50 @@ void color_alpha_clear(vec4 color, out vec4 result)
void color_alpha_premultiply(vec4 color, out vec4 result)
{
- result = vec4(color.rgb * color.a, 1.0);
+ result = vec4(color.rgb * color.a, color.a);
}
void color_alpha_unpremultiply(vec4 color, out vec4 result)
{
if (color.a == 0.0 || color.a == 1.0) {
- result = vec4(color.rgb, 1.0);
+ result = color;
}
else {
- result = vec4(color.rgb / color.a, 1.0);
+ result = vec4(color.rgb / color.a, color.a);
+ }
+}
+
+float linear_rgb_to_srgb(float color)
+{
+ if (color < 0.0031308) {
+ return (color < 0.0) ? 0.0 : color * 12.92;
+ }
+
+ return 1.055 * pow(color, 1.0 / 2.4) - 0.055;
+}
+
+vec3 linear_rgb_to_srgb(vec3 color)
+{
+ return vec3(
+ linear_rgb_to_srgb(color.r), linear_rgb_to_srgb(color.g), linear_rgb_to_srgb(color.b));
+}
+
+float srgb_to_linear_rgb(float color)
+{
+ if (color < 0.04045) {
+ return (color < 0.0) ? 0.0 : color * (1.0 / 12.92);
}
+
+ return pow((color + 0.055) * (1.0 / 1.055), 2.4);
+}
+
+vec3 srgb_to_linear_rgb(vec3 color)
+{
+ return vec3(
+ srgb_to_linear_rgb(color.r), srgb_to_linear_rgb(color.g), srgb_to_linear_rgb(color.b));
+}
+
+float get_luminance(vec3 color, vec3 luminance_coefficients)
+{
+ return dot(color, luminance_coefficients);
}
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
index 8948ed77557..e68c173c055 100644
--- a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl
@@ -26,9 +26,29 @@ vec3 extrapolate_if_needed(vec3 parameters, vec3 values, vec3 start_slopes, vec3
return values + parameters * slopes;
}
-/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize
- * parameters accordingly. */
-#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range)
+/* Curve maps are stored in texture samplers that are evaluated in the [0, 1] range, so normalize
+ * the parameters accordingly. Additionally, ensure that the parameters evaluate the sampler at the
+ * center of the pixels, because samplers are evaluated using linear interpolation. */
+float normalize_parameter(float parameter, float minimum, float range_divider)
+{
+ float normalized_parameter = (parameter - minimum) * range_divider;
+
+ /* Curve maps have a fixed width of 257. We offset by the equivalent of half a pixel and scale
+ * down such that the normalized parameter 1.0 corresponds to the center of the last pixel. */
+ float sampler_offset = 0.5 / 257.0;
+ float sampler_scale = 1.0 - (1.0 / 257.0);
+ return normalized_parameter * sampler_scale + sampler_offset;
+}
+
+/* Same as normalize_parameter but vectorized. */
+vec3 normalize_parameters(vec3 parameters, vec3 minimums, vec3 range_dividers)
+{
+ vec3 normalized_parameters = (parameters - minimums) * range_dividers;
+
+ float sampler_offset = 0.5 / 257.0;
+ float sampler_scale = 1.0 - (1.0 / 257.0);
+ return normalized_parameters * sampler_scale + sampler_offset;
+}
void curves_combined_rgb(float factor,
vec4 color,
@@ -46,7 +66,7 @@ void curves_combined_rgb(float factor,
/* First, evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
* UI. */
- vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimums.aaa, range_dividers.aaa);
+ vec3 parameters = normalize_parameters(balanced.rgb, range_minimums.aaa, range_dividers.aaa);
result.r = texture(curve_map, vec2(parameters.x, layer)).a;
result.g = texture(curve_map, vec2(parameters.y, layer)).a;
result.b = texture(curve_map, vec2(parameters.z, layer)).a;
@@ -55,13 +75,14 @@ void curves_combined_rgb(float factor,
result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.aaa, end_slopes.aaa);
/* Then, evaluate each channel on its curve map. */
- parameters = NORMALIZE_PARAMETER(result.rgb, range_minimums.rgb, range_dividers.rgb);
+ parameters = normalize_parameters(result.rgb, range_minimums.rgb, range_dividers.rgb);
result.r = texture(curve_map, vec2(parameters.r, layer)).r;
result.g = texture(curve_map, vec2(parameters.g, layer)).g;
result.b = texture(curve_map, vec2(parameters.b, layer)).b;
/* Then, extrapolate again if needed. */
result.rgb = extrapolate_if_needed(parameters, result.rgb, start_slopes.rgb, end_slopes.rgb);
+
result.a = color.a;
result = mix(color, result, factor);
@@ -83,18 +104,95 @@ void curves_combined_only(float factor,
/* Evaluate alpha curve map at all channels. The alpha curve is the Combined curve in the
* UI. */
- vec3 parameters = NORMALIZE_PARAMETER(balanced.rgb, range_minimum, range_divider);
+ vec3 parameters = normalize_parameters(balanced.rgb, vec3(range_minimum), vec3(range_divider));
result.r = texture(curve_map, vec2(parameters.x, layer)).a;
result.g = texture(curve_map, vec2(parameters.y, layer)).a;
result.b = texture(curve_map, vec2(parameters.z, layer)).a;
/* Then, extrapolate if needed. */
result.rgb = extrapolate_if_needed(parameters, result.rgb, vec3(start_slope), vec3(end_slope));
+
result.a = color.a;
result = mix(color, result, factor);
}
+/* Contrary to standard tone curve implementations, the film-like implementation tries to preserve
+ * the hue of the colors as much as possible. To understand why this might be a problem, consider
+ * the violet color (0.5, 0.0, 1.0). If this color was to be evaluated at a power curve x^4, the
+ * color will be blue (0.0625, 0.0, 1.0). So the color changes and not just its luminosity, which
+ * is what film-like tone curves tries to avoid.
+ *
+ * First, the channels with the lowest and highest values are identified and evaluated at the
+ * curve. Then, the third channel---the median---is computed while maintaining the original hue of
+ * the color. To do that, we look at the equation for deriving the hue from RGB values. Assuming
+ * the maximum, minimum, and median channels are known, and ignoring the 1/3 period offset of the
+ * hue, the equation is:
+ *
+ * hue = (median - min) / (max - min) [1]
+ *
+ * Since we have the new values for the minimum and maximum after evaluating at the curve, we also
+ * have:
+ *
+ * hue = (new_median - new_min) / (new_max - new_min) [2]
+ *
+ * Since we want the hue to be equivalent, by equating [1] and [2] and rearranging:
+ *
+ * (new_median - new_min) / (new_max - new_min) = (median - min) / (max - min)
+ * new_median - new_min = (new_max - new_min) * (median - min) / (max - min)
+ * new_median = new_min + (new_max - new_min) * (median - min) / (max - min)
+ * new_median = new_min + (median - min) * ((new_max - new_min) / (max - min)) [QED]
+ *
+ * Which gives us the median color that preserves the hue. More intuitively, the median is computed
+ * such that the change in the distance from the median to the minimum is proportional to the
+ * change in the distance from the minimum to the maximum. Finally, each of the new minimum,
+ * maximum, and median values are written to the color channel that they were originally extracted
+ * from. */
+void curves_film_like(float factor,
+ vec4 color,
+ vec4 black_level,
+ vec4 white_level,
+ sampler1DArray curve_map,
+ const float layer,
+ float range_minimum,
+ float range_divider,
+ float start_slope,
+ float end_slope,
+ out vec4 result)
+{
+ vec4 balanced = white_balance(color, black_level, white_level);
+
+ /* Find the maximum, minimum, and median of the color channels. */
+ float minimum = min(balanced.r, min(balanced.g, balanced.b));
+ float maximum = max(balanced.r, max(balanced.g, balanced.b));
+ float median = max(min(balanced.r, balanced.g), min(balanced.b, max(balanced.r, balanced.g)));
+
+ /* Evaluate alpha curve map at the maximum and minimum channels. The alpha curve is the Combined
+ * curve in the UI. */
+ float min_parameter = normalize_parameter(minimum, range_minimum, range_divider);
+ float max_parameter = normalize_parameter(maximum, range_minimum, range_divider);
+ float new_min = texture(curve_map, vec2(min_parameter, layer)).a;
+ float new_max = texture(curve_map, vec2(max_parameter, layer)).a;
+
+ /* Then, extrapolate if needed. */
+ new_min = extrapolate_if_needed(min_parameter, new_min, start_slope, end_slope);
+ new_max = extrapolate_if_needed(max_parameter, new_max, start_slope, end_slope);
+
+ /* Compute the new median using the ratio between the new and the original range. */
+ float scaling_ratio = (new_max - new_min) / (maximum - minimum);
+ float new_median = new_min + (median - minimum) * scaling_ratio;
+
+ /* Write each value to its original channel. */
+ bvec3 channel_is_min = equal(balanced.rgb, vec3(minimum));
+ vec3 median_or_min = mix(vec3(new_median), vec3(new_min), channel_is_min);
+ bvec3 channel_is_max = equal(balanced.rgb, vec3(maximum));
+ result.rgb = mix(median_or_min, vec3(new_max), channel_is_max);
+
+ result.a = color.a;
+
+ result = mix(color, result, clamp(factor, 0.0, 1.0));
+}
+
void curves_vector(vec3 vector,
sampler1DArray curve_map,
const float layer,
@@ -105,7 +203,7 @@ void curves_vector(vec3 vector,
out vec3 result)
{
/* Evaluate each component on its curve map. */
- vec3 parameters = NORMALIZE_PARAMETER(vector, range_minimums, range_dividers);
+ vec3 parameters = normalize_parameters(vector, range_minimums, range_dividers);
result.x = texture(curve_map, vec2(parameters.x, layer)).x;
result.y = texture(curve_map, vec2(parameters.y, layer)).y;
result.z = texture(curve_map, vec2(parameters.z, layer)).z;
@@ -139,7 +237,7 @@ void curves_float(float value,
out float result)
{
/* Evaluate the normalized value on the first curve map. */
- float parameter = NORMALIZE_PARAMETER(value, range_minimum, range_divider);
+ float parameter = normalize_parameter(value, range_minimum, range_divider);
result = texture(curve_map, vec2(parameter, layer)).x;
/* Then, extrapolate if needed. */
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
index 124654963fd..1ba22b4c5da 100644
--- a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl
@@ -34,6 +34,17 @@ float compatible_pow(float x, float y)
return pow(x, y);
}
+/* A version of pow that returns a fallback value if the computation is undefined. From the spec:
+ * The result is undefined if x < 0 or if x = 0 and y is less than or equal 0. */
+float fallback_pow(float x, float y, float fallback)
+{
+ if (x < 0.0 || (x == 0.0 && y <= 0.0)) {
+ return fallback;
+ }
+
+ return pow(x, y);
+}
+
float wrap(float a, float b, float c)
{
float range = b - c;
@@ -114,8 +125,24 @@ void vector_copy(vec3 normal, out vec3 outnormal)
outnormal = normal;
}
+vec3 fallback_pow(vec3 a, float b, vec3 fallback)
+{
+ return vec3(fallback_pow(a.x, b, fallback.x),
+ fallback_pow(a.y, b, fallback.y),
+ fallback_pow(a.z, b, fallback.z));
+}
+
/* Matirx Math */
+/* Return a 2D rotation matrix with the angle that the input 2D vector makes with the x axis. */
+mat2 vector_to_rotation_matrix(vec2 vector)
+{
+ vec2 normalized_vector = normalize(vector);
+ float cos_angle = normalized_vector.x;
+ float sin_angle = normalized_vector.y;
+ return mat2(cos_angle, sin_angle, -sin_angle, cos_angle);
+}
+
mat3 euler_to_mat3(vec3 euler)
{
float cx = cos(euler.x);
diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
index f9652f1150b..39f3c722dd2 100644
--- a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
+++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl
@@ -2,28 +2,24 @@
void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col2, fac);
outcol.a = col1.a;
}
void mix_add(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 + col2, fac);
outcol.a = col1.a;
}
void mix_mult(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 * col2, fac);
outcol.a = col1.a;
}
void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1);
@@ -32,7 +28,6 @@ void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@@ -61,14 +56,30 @@ void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_sub(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, col1 - col2, fac);
outcol.a = col1.a;
}
void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
+ float facm = 1.0 - fac;
+
+ outcol = vec4(vec3(0.0), col1.a);
+
+ if (col2.r != 0.0) {
+ outcol.r = facm * col1.r + fac * col1.r / col2.r;
+ }
+ if (col2.g != 0.0) {
+ outcol.g = facm * col1.g + fac * col1.g / col2.g;
+ }
+ if (col2.b != 0.0) {
+ outcol.b = facm * col1.b + fac * col1.b / col2.b;
+ }
+}
+
+/* A variant of mix_div that fallback to the first color upon zero division. */
+void mix_div_fallback(float fac, vec4 col1, vec4 col2, out vec4 outcol)
+{
float facm = 1.0 - fac;
outcol = col1;
@@ -86,28 +97,24 @@ void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = mix(col1, abs(col1 - col2), fac);
outcol.a = col1.a;
}
void mix_dark(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
void mix_light(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac);
outcol.a = col1.a;
}
void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
outcol = col1;
if (outcol.r != 0.0) {
@@ -150,7 +157,6 @@ void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float tmp, facm = 1.0 - fac;
outcol = col1;
@@ -200,7 +206,6 @@ void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@@ -220,7 +225,6 @@ void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@@ -238,7 +242,6 @@ void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
vec4 hsv, hsv2;
@@ -251,7 +254,6 @@ void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
outcol = col1;
@@ -272,22 +274,26 @@ void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol)
void mix_soft(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
float facm = 1.0 - fac;
vec4 one = vec4(1.0);
vec4 scr = one - (one - col2) * (one - col1);
outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr);
+ outcol.a = col1.a;
}
void mix_linear(float fac, vec4 col1, vec4 col2, out vec4 outcol)
{
- fac = clamp(fac, 0.0, 1.0);
-
outcol = col1 + fac * (2.0 * (col2 - vec4(0.5)));
+ outcol.a = col1.a;
}
-void clamp_color(vec3 vec, vec3 min, vec3 max, out vec3 out_vec)
+void clamp_color(vec4 vec, const vec4 min, const vec4 max, out vec4 out_vec)
{
out_vec = clamp(vec, min, max);
}
+
+void multiply_by_alpha(float factor, vec4 color, out float result)
+{
+ result = factor * color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl b/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl
new file mode 100644
index 00000000000..d55c8efd4c6
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl
@@ -0,0 +1,11 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ /* The lower bound is inclusive and upper bound is exclusive. */
+ bool is_inside = all(greaterThanEqual(texel, lower_bound)) && all(lessThan(texel, upper_bound));
+ /* Write the pixel color if it is inside the cropping region, otherwise, write zero. */
+ vec4 color = is_inside ? texture_load(input_tx, texel) : vec4(0.0);
+ imageStore(output_img, texel, color);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_bilateral_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_bilateral_blur.glsl
new file mode 100644
index 00000000000..c7c5ada7a9f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_bilateral_blur.glsl
@@ -0,0 +1,31 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec4 center_determinator = texture_load(determinator_tx, texel);
+
+ /* Go over the pixels in the blur window of the specified radius around the center pixel, and for
+ * pixels whose determinator is close enough to the determinator of the center pixel, accumulate
+ * their color as well as their weights. */
+ float accumulated_weight = 0.0;
+ vec4 accumulated_color = vec4(0.0);
+ for (int y = -radius; y <= radius; y++) {
+ for (int x = -radius; x <= radius; x++) {
+ vec4 determinator = texture_load(determinator_tx, texel + ivec2(x, y));
+ float difference = dot(abs(center_determinator - determinator).rgb, vec3(1.0));
+
+ if (difference < threshold) {
+ accumulated_weight += 1.0;
+ accumulated_color += texture_load(input_tx, texel + ivec2(x, y));
+ }
+ }
+ }
+
+ /* Write the accumulated color divided by the accumulated weight if any pixel in the window was
+ * accumulated, otherwise, write a fallback black color. */
+ vec4 fallback = vec4(vec3(0.0), 1.0);
+ vec4 color = (accumulated_weight != 0.0) ? (accumulated_color / accumulated_weight) : fallback;
+ imageStore(output_img, texel, color);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_blur.glsl
new file mode 100644
index 00000000000..4f981c84f59
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_blur.glsl
@@ -0,0 +1,55 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+vec4 load_input(ivec2 texel)
+{
+ vec4 color;
+ if (extend_bounds) {
+ /* If bounds are extended, then we treat the input as padded by a radius amount of pixels. So
+ * we load the input with an offset by the radius amount and fallback to a transparent color if
+ * it is out of bounds. */
+ color = texture_load(input_tx, texel - radius, vec4(0.0));
+ }
+ else {
+ color = texture_load(input_tx, texel);
+ }
+
+ return color;
+}
+
+/* Given the texel in the range [-radius, radius] in both axis, load the appropriate weight from
+ * the weights texture, where the texel (0, 0) is considered the center of weights texture. */
+vec4 load_weight(ivec2 texel)
+{
+ /* Add the radius to transform the texel into the range [0, radius * 2], then divide by the upper
+ * bound plus one to transform the texel into the normalized range [0, 1] needed to sample the
+ * weights sampler. Finally, also add 0.5 to sample at the center of the pixels. */
+ return texture(weights_tx, (texel + vec2(radius + 0.5)) / (radius * 2 + 1));
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* The mask input is treated as a boolean. If it is zero, then no blurring happens for this
+ * pixel. Otherwise, the pixel is blurred normally and the mask value is irrelevant. */
+ float mask = texture_load(mask_tx, texel).x;
+ if (mask == 0.0) {
+ imageStore(output_img, texel, texture_load(input_tx, texel));
+ return;
+ }
+
+ /* Go over the window of the given radius and accumulate the colors multiplied by their
+ * respective weights as well as the weights themselves. */
+ vec4 accumulated_color = vec4(0.0);
+ vec4 accumulated_weight = vec4(0.0);
+ for (int y = -radius; y <= radius; y++) {
+ for (int x = -radius; x <= radius; x++) {
+ vec4 weight = load_weight(ivec2(x, y));
+ accumulated_color += load_input(texel + ivec2(x, y)) * weight;
+ accumulated_weight += weight;
+ }
+ }
+
+ imageStore(output_img, texel, safe_divide(accumulated_color, accumulated_weight));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_bokeh_image.glsl b/source/blender/gpu/shaders/compositor/compositor_bokeh_image.glsl
new file mode 100644
index 00000000000..6e98aa9fe17
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_bokeh_image.glsl
@@ -0,0 +1,118 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* Get the 2D vertex position of the vertex with the given index in the regular polygon
+ * representing this bokeh. The polygon is rotated by the rotation amount and have a unit
+ * circumradius. The regular polygon is one whose vertices' exterior angles are given by
+ * exterior_angle. See the bokeh function for more information. */
+vec2 get_regular_polygon_vertex_position(int vertex_index)
+{
+ float angle = exterior_angle * vertex_index - rotation;
+ return vec2(cos(angle), sin(angle));
+}
+
+/* Find the closest point to the given point on the given line. This assumes the length of the
+ * given line is not zero. */
+vec2 closest_point_on_line(vec2 point, vec2 line_start, vec2 line_end)
+{
+ vec2 line_vector = line_end - line_start;
+ vec2 point_vector = point - line_start;
+ float line_length_squared = dot(line_vector, line_vector);
+ float parameter = dot(point_vector, line_vector) / line_length_squared;
+ return line_start + line_vector * parameter;
+}
+
+/* Compute the value of the bokeh at the given point. The computed bokeh is essentially a regular
+ * polygon centered in space having the given circumradius. The regular polygon is one whose
+ * vertices' exterior angles are given by "exterior_angle", which relates to the number of vertices
+ * n through the equation "exterior angle = 2 pi / n". The regular polygon may additionally morph
+ * into a shape with the given properties:
+ *
+ * - The regular polygon may have a circular hole in its center whose radius is controlled by the
+ * "catadioptric" value.
+ * - The regular polygon is rotated by the "rotation" value.
+ * - The regular polygon can morph into a circle controlled by the "roundness" value, such that it
+ * becomes a full circle at unit roundness.
+ *
+ * The function returns 0 when the point lies inside the regular polygon and 1 otherwise. However,
+ * at the edges, it returns a narrow band gradient as a form of anti-aliasing. */
+float bokeh(vec2 point, float circumradius)
+{
+ /* Get the index of the vertex of the regular polygon whose polar angle is maximum but less than
+ * the polar angle of the given point, taking rotation into account. This essentially finds the
+ * vertex closest to the given point in the clock-wise direction. */
+ float angle = mod(atan(point.y, point.x) + rotation, M_2PI);
+ int vertex_index = int(angle / exterior_angle);
+
+ /* Compute the shortest distance between the origin and the polygon edge composed from the
+ * previously selected vertex and the one following it. */
+ vec2 first_vertex = get_regular_polygon_vertex_position(vertex_index) * circumradius;
+ vec2 second_vertex = get_regular_polygon_vertex_position(vertex_index + 1) * circumradius;
+ vec2 closest_point = closest_point_on_line(point, first_vertex, second_vertex);
+ float distance_to_edge = length(closest_point);
+
+ /* Mix the distance to the edge with the circumradius, making it tend to the distance to a
+ * circle when roundness tends to 1. */
+ float distance_to_edge_round = mix(distance_to_edge, circumradius, roundness);
+
+ /* The point is outside of the bokeh, so we return 0. */
+ float distance = length(point);
+ if (distance > distance_to_edge_round) {
+ return 0.0;
+ }
+
+ /* The point is inside the catadioptric hole and is not part of the bokeh, so we return 0. */
+ float catadioptric_distance = distance_to_edge_round * catadioptric;
+ if (distance < catadioptric_distance) {
+ return 0.0;
+ }
+
+ /* The point is very close to the edge of the bokeh, so we return the difference between the
+ * distance to the edge and the distance as a form of anti-aliasing. */
+ if (distance_to_edge_round - distance < 1.0) {
+ return distance_to_edge_round - distance;
+ }
+
+ /* The point is very close to the edge of the catadioptric hole, so we return the difference
+ * between the distance to the hole and the distance as a form of anti-aliasing. */
+ if (catadioptric != 0.0 && distance - catadioptric_distance < 1.0) {
+ return distance - catadioptric_distance;
+ }
+
+ /* Otherwise, the point is part of the bokeh and we return 1. */
+ return 1.0;
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Since we need the regular polygon to occupy the entirety of the output image, the circumradius
+ * of the regular polygon is half the width of the output image. */
+ float circumradius = float(imageSize(output_img).x) / 2.0;
+
+ /* Move the texel coordinates such that the regular polygon is centered. */
+ vec2 point = vec2(texel) - circumradius;
+
+ /* Each of the color channels of the output image contains a bokeh with a different circumradius.
+ * The largest one occupies the whole image as stated above, while the other two have circumradii
+ * that are shifted by an amount that is proportional to the "lens_shift" value. The alpha
+ * channel of the output is the average of all three values. */
+ float min_shift = abs(lens_shift * circumradius);
+ float min = mix(bokeh(point, circumradius - min_shift), 0.0, min_shift == circumradius);
+
+ float median_shift = min_shift / 2.0;
+ float median = bokeh(point, circumradius - median_shift);
+
+ float max = bokeh(point, circumradius);
+ vec4 bokeh = vec4(min, median, max, (max + median + min) / 3.0);
+
+ /* If the lens shift is negative, swap the min and max bokeh values, which are stored in the red
+ * and blue channels respectively. Note that we take the absolute value of the lens shift above,
+ * so the sign of the lens shift only controls this swap. */
+ if (lens_shift < 0) {
+ bokeh = bokeh.zyxw;
+ }
+
+ imageStore(output_img, texel, bokeh);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl
new file mode 100644
index 00000000000..fad23f28fde
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl
@@ -0,0 +1,27 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1));
+ uv -= location;
+ uv.y *= float(domain_size.y) / float(domain_size.x);
+ uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv;
+ bool is_inside = all(lessThan(abs(uv), size));
+
+ float base_mask_value = texture_load(base_mask_tx, texel).x;
+ float value = texture_load(mask_value_tx, texel).x;
+
+#if defined(CMP_NODE_MASKTYPE_ADD)
+ float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
+#elif defined(CMP_NODE_MASKTYPE_SUBTRACT)
+ float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value;
+#elif defined(CMP_NODE_MASKTYPE_MULTIPLY)
+ float output_mask_value = is_inside ? base_mask_value * value : 0.0;
+#elif defined(CMP_NODE_MASKTYPE_NOT)
+ float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value;
+#endif
+
+ imageStore(output_mask_img, texel, vec4(output_mask_value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_convert.glsl b/source/blender/gpu/shaders/compositor/compositor_convert.glsl
new file mode 100644
index 00000000000..044fb057ca5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_convert.glsl
@@ -0,0 +1,8 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ vec4 value = texture_load(input_tx, texel);
+ imageStore(output_img, texel, CONVERT_EXPRESSION(value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_despeckle.glsl b/source/blender/gpu/shaders/compositor/compositor_despeckle.glsl
new file mode 100644
index 00000000000..e4743d69d17
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_despeckle.glsl
@@ -0,0 +1,70 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* Returns true if the given color is close enough to the given reference color within the
+ * threshold supplied by the user, and returns false otherwise. */
+bool is_close(vec4 reference_color, vec4 color)
+{
+ return all(lessThan(abs(reference_color - color).rgb, vec3(threshold)));
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* A 3x3 weights kernel whose weights are the inverse of the distance to the center of the
+ * kernel. So the center weight is zero, the corners weights are (1 / sqrt(2)), and the rest
+ * of the weights are 1. The total sum of weights is 4 plus quadruple the corner weight. */
+ float corner_weight = 1.0 / sqrt(2.0);
+ float sum_of_weights = 4.0 + corner_weight * 4.0;
+ mat3 weights = mat3(vec3(corner_weight, 1.0, corner_weight),
+ vec3(1.0, 0.0, 1.0),
+ vec3(corner_weight, 1.0, corner_weight));
+
+ vec4 center_color = texture_load(input_tx, texel);
+
+ /* Go over the pixels in the 3x3 window around the center pixel and compute the total sum of
+ * their colors multiplied by their weights. Additionally, for pixels whose colors are not close
+ * enough to the color of the center pixel, accumulate their color as well as their weights. */
+ vec4 sum_of_colors = vec4(0);
+ float accumulated_weight = 0.0;
+ vec4 accumulated_color = vec4(0);
+ for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < 3; i++) {
+ float weight = weights[j][i];
+ vec4 color = texture_load(input_tx, texel + ivec2(i - 1, j - 1)) * weight;
+ sum_of_colors += color;
+ if (!is_close(center_color, color)) {
+ accumulated_color += color;
+ accumulated_weight += weight;
+ }
+ }
+ }
+
+ /* If the accumulated weight is zero, that means all pixels in the 3x3 window are similar and no
+ * need to despeckle anything, so write the original center color and return. */
+ if (accumulated_weight == 0.0) {
+ imageStore(output_img, texel, center_color);
+ return;
+ }
+
+ /* If the ratio between the accumulated weights and the total sum of weights is not larger than
+ * the user specified neighbor threshold, then the number of pixels in the neighborhood that are
+ * not close enough to the center pixel is low, and no need to despeckle anything, so write the
+ * original center color and return. */
+ if (accumulated_weight / sum_of_weights < neighbor_threshold) {
+ imageStore(output_img, texel, center_color);
+ return;
+ }
+
+ /* If the weighted average color of the neighborhood is close enough to the center pixel, then no
+ * need to despeckle anything, so write the original center color and return. */
+ if (is_close(center_color, sum_of_colors / sum_of_weights)) {
+ imageStore(output_img, texel, center_color);
+ return;
+ }
+
+ /* We need to despeckle, so write the mean accumulated color. */
+ float factor = texture_load(factor_tx, texel).x;
+ vec4 mean_color = accumulated_color / accumulated_weight;
+ imageStore(output_img, texel, mix(center_color, mean_color, factor));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl
new file mode 100644
index 00000000000..1805cb5a7f5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_directional_blur.glsl
@@ -0,0 +1,21 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 input_size = texture_size(input_tx);
+
+ /* Add 0.5 to evaluate the input sampler at the center of the pixel. */
+ vec2 coordinates = vec2(texel) + vec2(0.5);
+
+ /* For each iteration, accumulate the input at the normalize coordinates, hence the divide by
+ * input size, then transform the coordinates for the next iteration. */
+ vec4 accumulated_color = vec4(0.0);
+ for (int i = 0; i < iterations; i++) {
+ accumulated_color += texture(input_tx, coordinates / input_size);
+ coordinates = (mat3(inverse_transformation) * vec3(coordinates, 1.0)).xy;
+ }
+
+ /* Write the accumulated color divided by the number of iterations. */
+ imageStore(output_img, texel, accumulated_color / iterations);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_edge_filter.glsl b/source/blender/gpu/shaders/compositor/compositor_edge_filter.glsl
new file mode 100644
index 00000000000..67e27c22602
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_edge_filter.glsl
@@ -0,0 +1,31 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Compute the dot product between the 3x3 window around the pixel and the edge detection kernel
+ * in the X direction and Y direction. The Y direction kernel is computed by transposing the
+ * given X direction kernel. */
+ vec3 color_x = vec3(0);
+ vec3 color_y = vec3(0);
+ for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < 3; i++) {
+ vec3 color = texture_load(input_tx, texel + ivec2(i - 1, j - 1)).rgb;
+ color_x += color * kernel[j][i];
+ color_y += color * kernel[i][j];
+ }
+ }
+
+ /* Compute the channel-wise magnitude of the 2D vector composed from the X and Y edge detection
+ * filter results. */
+ vec3 magnitude = sqrt(color_x * color_x + color_y * color_y);
+
+ /* Mix the channel-wise magnitude with the original color at the center of the kernel using the
+ * input factor. */
+ vec4 color = texture_load(input_tx, texel);
+ magnitude = mix(color.rgb, magnitude, texture_load(factor_tx, texel).x);
+
+ /* Store the channel-wise magnitude with the original alpha of the input. */
+ imageStore(output_img, texel, vec4(magnitude, color.a));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl
new file mode 100644
index 00000000000..28f725067e0
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl
@@ -0,0 +1,27 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1));
+ uv -= location;
+ uv.y *= float(domain_size.y) / float(domain_size.x);
+ uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv;
+ bool is_inside = length(uv / radius) < 1.0;
+
+ float base_mask_value = texture_load(base_mask_tx, texel).x;
+ float value = texture_load(mask_value_tx, texel).x;
+
+#if defined(CMP_NODE_MASKTYPE_ADD)
+ float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value;
+#elif defined(CMP_NODE_MASKTYPE_SUBTRACT)
+ float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value;
+#elif defined(CMP_NODE_MASKTYPE_MULTIPLY)
+ float output_mask_value = is_inside ? base_mask_value * value : 0.0;
+#elif defined(CMP_NODE_MASKTYPE_NOT)
+ float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value;
+#endif
+
+ imageStore(output_mask_img, texel, vec4(output_mask_value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_filter.glsl b/source/blender/gpu/shaders/compositor/compositor_filter.glsl
new file mode 100644
index 00000000000..e501c563dda
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_filter.glsl
@@ -0,0 +1,20 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Compute the dot product between the 3x3 window around the pixel and the filter kernel. */
+ vec4 color = vec4(0);
+ for (int j = 0; j < 3; j++) {
+ for (int i = 0; i < 3; i++) {
+ color += texture_load(input_tx, texel + ivec2(i - 1, j - 1)) * kernel[j][i];
+ }
+ }
+
+ /* Mix with the original color at the center of the kernel using the input factor. */
+ color = mix(texture_load(input_tx, texel), color, texture_load(factor_tx, texel).x);
+
+ /* Store the color making sure it is not negative. */
+ imageStore(output_img, texel, max(color, 0.0));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_flip.glsl b/source/blender/gpu/shaders/compositor/compositor_flip.glsl
new file mode 100644
index 00000000000..919c454ee63
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_flip.glsl
@@ -0,0 +1,15 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 size = texture_size(input_tx);
+ ivec2 flipped_texel = texel;
+ if (flip_x) {
+ flipped_texel.x = size.x - texel.x - 1;
+ }
+ if (flip_y) {
+ flipped_texel.y = size.y - texel.y - 1;
+ }
+ imageStore(output_img, texel, texture_load(input_tx, flipped_texel));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl b/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl
new file mode 100644
index 00000000000..f20e033dee4
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl
@@ -0,0 +1,7 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ imageStore(output_img, texel, texture_load(input_tx, texel + lower_bound));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_morphological_distance.glsl b/source/blender/gpu/shaders/compositor/compositor_morphological_distance.glsl
new file mode 100644
index 00000000000..09f896b7a9d
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_morphological_distance.glsl
@@ -0,0 +1,24 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Find the minimum/maximum value in the circular window of the given radius around the pixel. By
+ * circular window, we mean that pixels in the window whose distance to the center of window is
+ * larger than the given radius are skipped and not considered. Consequently, the dilation or
+ * erosion that take place produces round results as opposed to squarish ones. This is
+ * essentially a morphological operator with a circular structuring element. The LIMIT value
+ * should be FLT_MAX if OPERATOR is min and FLT_MIN if OPERATOR is max. */
+ float value = LIMIT;
+ for (int y = -radius; y <= radius; y++) {
+ for (int x = -radius; x <= radius; x++) {
+ if (x * x + y * y <= radius * radius) {
+ value = OPERATOR(value, texture_load(input_tx, texel + ivec2(x, y), vec4(LIMIT)).x);
+ }
+ }
+ }
+
+ imageStore(output_img, texel, vec4(value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_morphological_distance_feather.glsl b/source/blender/gpu/shaders/compositor/compositor_morphological_distance_feather.glsl
new file mode 100644
index 00000000000..acdd8a40342
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_morphological_distance_feather.glsl
@@ -0,0 +1,101 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* The Morphological Distance Feather operation is a linear combination between the result of two
+ * operations. The first operation is a Gaussian blur with a radius equivalent to the dilate/erode
+ * distance, which is straightforward and implemented as a separable filter similar to the blur
+ * operation.
+ *
+ * The second operation is an approximation of a morphological inverse distance operation evaluated
+ * at a distance falloff function. The result of a morphological inverse distance operation is a
+ * narrow band distance field that starts at its maximum value at boundaries where a difference in
+ * values took place and linearly deceases until it reaches zero in the span of a number of pixels
+ * equivalent to the erode/dilate distance. Additionally, instead of linearly decreasing, the user
+ * may choose a different falloff which is evaluated at the computed distance. For dilation, the
+ * distance field decreases outwards, and for erosion, the distance field decreased inwards.
+ *
+ * The reason why the result of a Gaussian blur is mixed in with the distance field is because the
+ * distance field is merely approximated and not accurately computed, the defects of which is more
+ * apparent away from boundaries and especially at corners where the distance field should take a
+ * circular shape. That's why the Gaussian blur is mostly mixed only further from boundaries.
+ *
+ * The morphological inverse distance operation is approximated using a separable implementation
+ * and intertwined with the Gaussian blur implementation as follows. A search window of a radius
+ * equivalent to the dilate/erode distance is applied on the image to find either the minimum or
+ * maximum pixel value multiplied by its corresponding falloff value in the window. For dilation,
+ * we try to find the maximum, and for erosion, we try to find the minimum. Additionally, we also
+ * save the falloff value where the minimum or maximum was found. The found value will be that of
+ * the narrow band distance field and the saved falloff value will be used as the mixing factor
+ * with the Gaussian blur.
+ *
+ * To make sense of the aforementioned algorithm, assume we are dilating a binary image by 5 pixels
+ * whose half has a value of 1 and the other half has a value of zero. Consider the following:
+ *
+ * - A pixel of value 1 already has the maximum possible value, so its value will remain unchanged
+ * regardless of its position.
+ * - A pixel of value 0 that is right at the boundary of the 1's region will have a maximum value
+ * of around 0.8 depending on the falloff. That's because the search window intersects the 1's
+ * region, which when multiplied by the falloff gives the first value of the falloff, which is
+ * larger than the initially zero value computed at the center of the search window.
+ * - A pixel of value 0 that is 3 pixels away from the boundary will have a maximum value of around
+ * 0.4 depending on the falloff. That's because the search window intersects the 1's region,
+ * which when multiplied by the falloff gives the third value of the falloff, which is larger
+ * than the initially zero value computed at the center of the search window.
+ * - Finally, a pixel of value 0 that is 6 pixels away from the boundary will have a maximum value
+ * of 0, because the search window doesn't intersects the 1's region and only spans zero values.
+ *
+ * The previous example demonstrates how the distance field naturally arises, and the same goes for
+ * the erode case, except the minimum value is computed instead.
+ */
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* A value for accumulating the blur result. */
+ float accumulated_value = 0.0;
+
+ /* Compute the contribution of the center pixel to the blur result. */
+ float center_value = texture_load(input_tx, texel).x;
+ accumulated_value += center_value * texture_load(weights_tx, 0).x;
+
+ /* Start with the center value as the maximum/minimum distance and reassign to the true maximum
+ * or minimum in the search loop below. Additionally, the center falloff is always 1.0, so start
+ * with that. */
+ float limit_distance = center_value;
+ float limit_distance_falloff = 1.0;
+
+ /* Compute the contributions of the pixels to the right and left, noting that the weights and
+ * falloffs textures only store the weights and falloffs for the positive half, but since the
+ * they are both symmetric, the same weights and falloffs are used for the negative half and we
+ * compute both of their contributions. */
+ for (int i = 1; i < texture_size(weights_tx); i++) {
+ float weight = texture_load(weights_tx, i).x;
+ float falloff = texture_load(falloffs_tx, i).x;
+
+ /* Loop for two iterations, where s takes the value of -1 and 1, which is used as the sign
+ * needed to evaluated the positive and negative sides as explain above. */
+ for (int s = -1; s < 2; s += 2) {
+ /* Compute the contribution of the pixel to the blur result. */
+ float value = texture_load(input_tx, texel + ivec2(s * i, 0)).x;
+ accumulated_value += value * weight;
+
+ /* The distance is computed such that its highest value is the pixel value itself, so
+ * multiply the distance falloff by the pixel value. */
+ float falloff_distance = value * falloff;
+
+ /* Find either the maximum or the minimum for the dilate and erode cases respectively. */
+ if (COMPARE(falloff_distance, limit_distance)) {
+ limit_distance = falloff_distance;
+ limit_distance_falloff = falloff;
+ }
+ }
+ }
+
+ /* Mix between the limit distance and the blurred accumulated value such that the limit distance
+ * is used for pixels closer to the boundary and the blurred value is used for pixels away from
+ * the boundary. */
+ float value = mix(accumulated_value, limit_distance, limit_distance_falloff);
+
+ /* Write the value using the transposed texel. See the execute_distance_feather_horizontal_pass
+ * method for more information on the rational behind this. */
+ imageStore(output_img, texel.yx, vec4(value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_morphological_distance_threshold.glsl b/source/blender/gpu/shaders/compositor/compositor_morphological_distance_threshold.glsl
new file mode 100644
index 00000000000..e6625e7419f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_morphological_distance_threshold.glsl
@@ -0,0 +1,88 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* The Morphological Distance Threshold operation is effectively three consecutive operations
+ * implemented as a single operation. The three operations are as follows:
+ *
+ * .-----------. .--------------. .----------------.
+ * | Threshold |-->| Dilate/Erode |-->| Distance Inset |
+ * '-----------' '--------------' '----------------'
+ *
+ * The threshold operation just converts the input into a binary image, where the pixel is 1 if it
+ * is larger than 0.5 and 0 otherwise. Pixels that are 1 in the output of the threshold operation
+ * are said to be masked. The dilate/erode operation is a dilate or erode morphological operation
+ * with a circular structuring element depending on the sign of the distance, where it is a dilate
+ * operation if the distance is positive and an erode operation otherwise. This is equivalent to
+ * the Morphological Distance operation, see its implementation for more information. Finally, the
+ * distance inset is an operation that converts the binary image into a narrow band distance field.
+ * That is, pixels that are unmasked will remain 0, while pixels that are masked will start from
+ * zero at the boundary of the masked region and linearly increase until reaching 1 in the span of
+ * a number pixels given by the inset value.
+ *
+ * As a performance optimization, the dilate/erode operation is omitted and its effective result is
+ * achieved by slightly adjusting the distance inset operation. The base distance inset operation
+ * works by computing the signed distance from the current center pixel to the nearest pixel with a
+ * different value. Since our image is a binary image, that means that if the pixel is masked, we
+ * compute the signed distance to the nearest unmasked pixel, and if the pixel unmasked, we compute
+ * the signed distance to the nearest masked pixel. The distance is positive if the pixel is masked
+ * and negative otherwise. The distance is then normalized by dividing by the given inset value and
+ * clamped to the [0, 1] range. Since distances larger than the inset value are eventually clamped,
+ * the distance search window is limited to a radius equivalent to the inset value.
+ *
+ * To archive the effective result of the omitted dilate/erode operation, we adjust the distance
+ * inset operation as follows. First, we increase the radius of the distance search window by the
+ * radius of the dilate/erode operation. Then we adjust the resulting narrow band signed distance
+ * field as follows.
+ *
+ * For the erode case, we merely subtract the erode distance, which makes the outermost erode
+ * distance number of pixels zero due to clamping, consequently achieving the result of the erode,
+ * while retaining the needed inset because we increased the distance search window by the same
+ * amount we subtracted.
+ *
+ * Similarly, for the dilate case, we add the dilate distance, which makes the dilate distance
+ * number of pixels just outside of the masked region positive and part of the narrow band distance
+ * field, consequently achieving the result of the dilate, while at the same time, the innermost
+ * dilate distance number of pixels become 1 due to clamping, retaining the needed inset because we
+ * increased the distance search window by the same amount we added.
+ *
+ * Since the erode/dilate distance is already signed appropriately as described before, we just add
+ * it in both cases. */
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Apply a threshold operation on the center pixel, where the threshold is currently hard-coded
+ * at 0.5. The pixels with values larger than the threshold are said to be masked. */
+ bool is_center_masked = texture_load(input_tx, texel).x > 0.5;
+
+ /* Since the distance search window will access pixels outside of the bounds of the image, we use
+ * a texture loader with a fallback value. And since we don't want those values to affect the
+ * result, the fallback value is chosen such that the inner condition fails, which is when the
+ * sampled pixel and the center pixel are the same, so choose a fallback that will be considered
+ * masked if the center pixel is masked and unmasked otherwise. */
+ vec4 fallback = vec4(is_center_masked ? 1.0 : 0.0);
+
+ /* Since the distance search window is limited to the given radius, the maximum possible squared
+ * distance to the center is double the squared radius. */
+ int minimum_squared_distance = radius * radius * 2;
+
+ /* Find the squared distance to the nearest different pixel in the search window of the given
+ * radius. */
+ for (int y = -radius; y <= radius; y++) {
+ for (int x = -radius; x <= radius; x++) {
+ bool is_sample_masked = texture_load(input_tx, texel + ivec2(x, y), fallback).x > 0.5;
+ if (is_center_masked != is_sample_masked) {
+ minimum_squared_distance = min(minimum_squared_distance, x * x + y * y);
+ }
+ }
+ }
+
+ /* Compute the actual distance from the squared distance and assign it an appropriate sign
+ * depending on whether it lies in a masked region or not. */
+ float signed_minimum_distance = sqrt(minimum_squared_distance) * (is_center_masked ? 1.0 : -1.0);
+
+ /* Add the erode/dilate distance and divide by the inset amount as described in the discussion,
+ * then clamp to the [0, 1] range. */
+ float value = clamp((signed_minimum_distance + distance) / inset, 0.0, 1.0);
+
+ imageStore(output_img, texel, vec4(value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_morphological_step.glsl b/source/blender/gpu/shaders/compositor/compositor_morphological_step.glsl
new file mode 100644
index 00000000000..6992bc2afa5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_morphological_step.glsl
@@ -0,0 +1,19 @@
+#pragma BLENDER_REQUIRE(common_math_lib.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Find the minimum/maximum value in the window of the given radius around the pixel. This is
+ * essentially a morphological operator with a square structuring element. The LIMIT value should
+ * be FLT_MAX if OPERATOR is min and FLT_MIN if OPERATOR is max. */
+ float value = LIMIT;
+ for (int i = -radius; i <= radius; i++) {
+ value = OPERATOR(value, texture_load(input_tx, texel + ivec2(i, 0), vec4(LIMIT)).x);
+ }
+
+ /* Write the value using the transposed texel. See the execute_step_horizontal_pass method for
+ * more information on the rational behind this. */
+ imageStore(output_img, texel.yx, vec4(value));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl b/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl
new file mode 100644
index 00000000000..ab44dac93e6
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl
@@ -0,0 +1,16 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Get the normalized coordinates of the pixel centers. */
+ vec2 normalized_texel = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx));
+
+ /* Sample the red and blue channels shifted by the dispersion amount. */
+ const float red = texture(input_tx, normalized_texel + vec2(dispersion, 0.0)).r;
+ const float green = texture_load(input_tx, texel).g;
+ const float blue = texture(input_tx, normalized_texel - vec2(dispersion, 0.0)).b;
+
+ imageStore(output_img, texel, vec4(red, green, blue, 1.0));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl b/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl
new file mode 100644
index 00000000000..b8561e5f059
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl
@@ -0,0 +1,29 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Add 0.5 to evaluate the input sampler at the center of the pixel. */
+ vec2 coordinates = vec2(texel) + vec2(0.5);
+
+ /* Transform the input image by transforming the domain coordinates with the inverse of input
+ * image's transformation. The inverse transformation is an affine matrix and thus the
+ * coordinates should be in homogeneous coordinates. */
+ coordinates = (mat3(inverse_transformation) * vec3(coordinates, 1.0)).xy;
+
+ /* Since an input image with an identity transformation is supposed to be centered in the domain,
+ * we subtract the offset between the lower left corners of the input image and the domain, which
+ * is half the difference between their sizes, because the difference in size is on both sides of
+ * the centered image. Additionally, we floor the offset to retain the 0.5 offset added above in
+ * case the difference in sizes was odd. */
+ ivec2 domain_size = imageSize(domain_img);
+ ivec2 input_size = texture_size(input_tx);
+ vec2 offset = floor((domain_size - input_size) / 2.0);
+
+ /* Subtract the offset and divide by the input image size to get the relevant coordinates into
+ * the sampler's expected [0, 1] range. */
+ vec2 normalized_coordinates = (coordinates - offset) / input_size;
+
+ imageStore(domain_img, texel, texture(input_tx, normalized_coordinates));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl b/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl
new file mode 100644
index 00000000000..dc572ea5aaf
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl
@@ -0,0 +1,151 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+/* A model that approximates lens distortion parameterized by a distortion parameter and dependent
+ * on the squared distance to the center of the image. The distorted pixel is then computed as the
+ * scalar multiplication of the pixel coordinates with the value returned by this model. See the
+ * compute_distorted_uv function for more details. */
+float compute_distortion_scale(float distortion, float distance_squared)
+{
+ return 1.0 / (1.0 + sqrt(max(0.0, 1.0 - distortion * distance_squared)));
+}
+
+/* A vectorized version of compute_distortion_scale that is applied on the chromatic distortion
+ * parameters passed to the shader. */
+vec3 compute_chromatic_distortion_scale(float distance_squared)
+{
+ return 1.0 / (1.0 + sqrt(max(vec3(0.0), 1.0 - chromatic_distortion * distance_squared)));
+}
+
+/* Compute the image coordinates after distortion by the given distortion scale computed by the
+ * compute_distortion_scale function. Note that the function expects centered normalized UV
+ * coordinates but outputs non-centered image coordinates. */
+vec2 compute_distorted_uv(vec2 uv, float scale)
+{
+ return (uv * scale + 0.5) * texture_size(input_tx) - 0.5;
+}
+
+/* Compute the number of integration steps that should be used to approximate the distorted pixel
+ * using a heuristic, see the compute_number_of_steps function for more details. The numbers of
+ * steps is proportional to the number of pixels spanned by the distortion amount. For jitter
+ * distortion, the square root of the distortion amount plus 1 is used with a minimum of 2 steps.
+ * For non-jitter distortion, the distortion amount plus 1 is used as the number of steps */
+int compute_number_of_integration_steps_heuristic(float distortion)
+{
+#if defined(JITTER)
+ return distortion < 4.0 ? 2 : int(sqrt(distortion + 1.0));
+#else
+ return int(distortion + 1.0);
+#endif
+}
+
+/* Compute the number of integration steps that should be used to compute each channel of the
+ * distorted pixel. Each of the channels are distorted by their respective chromatic distortion
+ * amount, then the amount of distortion between each two consecutive channels is computed, this
+ * amount is then used to heuristically infer the number of needed integration steps, see the
+ * integrate_distortion function for more information. */
+ivec3 compute_number_of_integration_steps(vec2 uv, float distance_squared)
+{
+ /* Distort each channel by its respective chromatic distortion amount. */
+ vec3 distortion_scale = compute_chromatic_distortion_scale(distance_squared);
+ vec2 distorted_uv_red = compute_distorted_uv(uv, distortion_scale.r);
+ vec2 distorted_uv_green = compute_distorted_uv(uv, distortion_scale.g);
+ vec2 distorted_uv_blue = compute_distorted_uv(uv, distortion_scale.b);
+
+ /* Infer the number of needed integration steps to compute the distorted red channel starting
+ * from the green channel. */
+ float distortion_red = distance(distorted_uv_red, distorted_uv_green);
+ int steps_red = compute_number_of_integration_steps_heuristic(distortion_red);
+
+ /* Infer the number of needed integration steps to compute the distorted blue channel starting
+ * from the green channel. */
+ float distortion_blue = distance(distorted_uv_green, distorted_uv_blue);
+ int steps_blue = compute_number_of_integration_steps_heuristic(distortion_blue);
+
+ /* The number of integration steps used to compute the green channel is the sum of both the red
+ * and the blue channel steps because it is computed once with each of them. */
+ return ivec3(steps_red, steps_red + steps_blue, steps_blue);
+}
+
+/* Returns a random jitter amount, which is essentially a random value in the [0, 1] range. If
+ * jitter is not enabled, return a constant 0.5 value instead. */
+float get_jitter(int seed)
+{
+#if defined(JITTER)
+ return hash_uint3_to_float(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y, seed);
+#else
+ return 0.5;
+#endif
+}
+
+/* Each color channel may have a different distortion with the guarantee that the red will have the
+ * lowest distortion while the blue will have the highest one. If each channel is distorted
+ * independently, the image will look disintegrated, with each channel seemingly merely shifted.
+ * Consequently, the distorted pixels needs to be computed by integrating along the path of change
+ * of distortion starting from one channel to another. For instance, to compute the distorted red
+ * from the distorted green, we accumulate the color of the distorted pixel starting from the
+ * distortion of the red, taking small steps until we reach the distortion of the green. The pixel
+ * color is weighted such that it is maximum at the start distortion and zero at the end distortion
+ * in an arithmetic progression. The integration steps can be augmented with random values to
+ * simulate lens jitter. Finally, it should be noted that this function integrates both the start
+ * and end channels in reverse directions for more efficient computation. */
+vec3 integrate_distortion(int start, int end, float distance_squared, vec2 uv, int steps)
+{
+ vec3 accumulated_color = vec3(0.0);
+ float distortion_amount = chromatic_distortion[end] - chromatic_distortion[start];
+ for (int i = 0; i < steps; i++) {
+ /* The increment will be in the [0, 1) range across iterations. */
+ float increment = (i + get_jitter(i)) / steps;
+ float distortion = chromatic_distortion[start] + increment * distortion_amount;
+ float distortion_scale = compute_distortion_scale(distortion, distance_squared);
+
+ /* Sample the color at the distorted coordinates and accumulate it weighted by the increment
+ * value for both the start and end channels. */
+ vec2 distorted_uv = compute_distorted_uv(uv, distortion_scale);
+ vec4 color = texture(input_tx, distorted_uv / texture_size(input_tx));
+ accumulated_color[start] += (1.0 - increment) * color[start];
+ accumulated_color[end] += increment * color[end];
+ }
+ return accumulated_color;
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ /* Compute the UV image coordinates in the range [-1, 1] as well as the squared distance to the
+ * center of the image, which is at (0, 0) in the UV coordinates. */
+ vec2 center = texture_size(input_tx) / 2.0;
+ vec2 uv = scale * (texel + 0.5 - center) / center;
+ float distance_squared = dot(uv, uv);
+
+ /* If any of the color channels will get distorted outside of the screen beyond what is possible,
+ * write a zero transparent color and return. */
+ if (any(greaterThan(chromatic_distortion * distance_squared, vec3(1.0)))) {
+ imageStore(output_img, texel, vec4(0.0));
+ return;
+ }
+
+ /* Compute the number of integration steps that should be used to compute each channel of the
+ * distorted pixel. */
+ ivec3 number_of_steps = compute_number_of_integration_steps(uv, distance_squared);
+
+ /* Integrate the distortion of the red and green, then the green and blue channels. That means
+ * the green will be integrated twice, but this is accounted for in the number of steps which the
+ * color will later be divided by. See the compute_number_of_integration_steps function for more
+ * details. */
+ vec3 color = vec3(0.0);
+ color += integrate_distortion(0, 1, distance_squared, uv, number_of_steps.r);
+ color += integrate_distortion(1, 2, distance_squared, uv, number_of_steps.b);
+
+ /* The integration above performed weighted accumulation, and thus the color needs to be divided
+ * by the sum of the weights. Assuming no jitter, the weights are generated as an arithmetic
+ * progression starting from (0.5 / n) to ((n - 0.5) / n) for n terms. The sum of an arithmetic
+ * progression can be computed as (n * (start + end) / 2), which when subsisting the start and
+ * end reduces to (n / 2). So the color should be multiplied by 2 / n. The jitter sequence
+ * approximately sums to the same value because it is a uniform random value whose mean value is
+ * 0.5, so the expression doesn't change regardless of jitter. */
+ color *= 2.0 / vec3(number_of_steps);
+
+ imageStore(output_img, texel, vec4(color, 1.0));
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl
new file mode 100644
index 00000000000..7dd40581790
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl
@@ -0,0 +1,8 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+ vec4 color = vec4(texture_load(image_tx, texel).rgb, texture_load(alpha_tx, texel).x);
+ imageStore(output_img, texel, color);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl
new file mode 100644
index 00000000000..866b9045da2
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl
@@ -0,0 +1,14 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+#if defined(SPLIT_HORIZONTAL)
+ bool condition = (view_size.x * split_ratio) < texel.x;
+#elif defined(SPLIT_VERTICAL)
+ bool condition = (view_size.y * split_ratio) < texel.y;
+#endif
+ vec4 color = condition ? texture_load(first_image_tx, texel) :
+ texture_load(second_image_tx, texel);
+ imageStore(output_img, texel, color);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_symmetric_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_symmetric_blur.glsl
new file mode 100644
index 00000000000..df08991a35c
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_symmetric_blur.glsl
@@ -0,0 +1,77 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_blur_common.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+vec4 load_input(ivec2 texel)
+{
+ vec4 color;
+ if (extend_bounds) {
+ /* If bounds are extended, then we treat the input as padded by a radius amount of pixels. So
+ * we load the input with an offset by the radius amount and fallback to a transparent color if
+ * it is out of bounds. Notice that we subtract 1 because the weights texture have an extra
+ * center weight, see the SymmetricBlurWeights for more information. */
+ ivec2 blur_size = texture_size(weights_tx) - 1;
+ color = texture_load(input_tx, texel - blur_size, vec4(0.0));
+ }
+ else {
+ color = texture_load(input_tx, texel);
+ }
+
+ if (gamma_correct) {
+ color = gamma_correct_blur_input(color);
+ }
+
+ return color;
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec4 accumulated_color = vec4(0.0);
+
+ /* First, compute the contribution of the center pixel. */
+ vec4 center_color = load_input(texel);
+ accumulated_color += center_color * texture_load(weights_tx, ivec2(0)).x;
+
+ ivec2 weights_size = texture_size(weights_tx);
+
+ /* Then, compute the contributions of the pixels along the x axis of the filter, noting that the
+ * weights texture only stores the weights for the positive half, but since the filter is
+ * symmetric, the same weight is used for the negative half and we add both of their
+ * contributions. */
+ for (int x = 1; x < weights_size.x; x++) {
+ float weight = texture_load(weights_tx, ivec2(x, 0)).x;
+ accumulated_color += load_input(texel + ivec2(x, 0)) * weight;
+ accumulated_color += load_input(texel + ivec2(-x, 0)) * weight;
+ }
+
+ /* Then, compute the contributions of the pixels along the y axis of the filter, noting that the
+ * weights texture only stores the weights for the positive half, but since the filter is
+ * symmetric, the same weight is used for the negative half and we add both of their
+ * contributions. */
+ for (int y = 1; y < weights_size.y; y++) {
+ float weight = texture_load(weights_tx, ivec2(0, y)).x;
+ accumulated_color += load_input(texel + ivec2(0, y)) * weight;
+ accumulated_color += load_input(texel + ivec2(0, -y)) * weight;
+ }
+
+ /* Finally, compute the contributions of the pixels in the four quadrants of the filter, noting
+ * that the weights texture only stores the weights for the upper right quadrant, but since the
+ * filter is symmetric, the same weight is used for the rest of the quadrants and we add all four
+ * of their contributions. */
+ for (int y = 1; y < weights_size.y; y++) {
+ for (int x = 1; x < weights_size.x; x++) {
+ float weight = texture_load(weights_tx, ivec2(x, y)).x;
+ accumulated_color += load_input(texel + ivec2(x, y)) * weight;
+ accumulated_color += load_input(texel + ivec2(-x, y)) * weight;
+ accumulated_color += load_input(texel + ivec2(x, -y)) * weight;
+ accumulated_color += load_input(texel + ivec2(-x, -y)) * weight;
+ }
+ }
+
+ if (gamma_correct) {
+ accumulated_color = gamma_uncorrect_blur_output(accumulated_color);
+ }
+
+ imageStore(output_img, texel, accumulated_color);
+}
diff --git a/source/blender/gpu/shaders/compositor/compositor_symmetric_separable_blur.glsl b/source/blender/gpu/shaders/compositor/compositor_symmetric_separable_blur.glsl
new file mode 100644
index 00000000000..ab0c7baa787
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/compositor_symmetric_separable_blur.glsl
@@ -0,0 +1,53 @@
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_blur_common.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
+
+vec4 load_input(ivec2 texel)
+{
+ vec4 color;
+ if (extend_bounds) {
+ /* If bounds are extended, then we treat the input as padded by a radius amount of pixels. So
+ * we load the input with an offset by the radius amount and fallback to a transparent color if
+ * it is out of bounds. Notice that we subtract 1 because the weights texture have an extra
+ * center weight, see the SymmetricSeparableBlurWeights for more information. */
+ int blur_size = texture_size(weights_tx) - 1;
+ color = texture_load(input_tx, texel - ivec2(blur_size, 0), vec4(0.0));
+ }
+ else {
+ color = texture_load(input_tx, texel);
+ }
+
+ if (gamma_correct_input) {
+ color = gamma_correct_blur_input(color);
+ }
+
+ return color;
+}
+
+void main()
+{
+ ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
+
+ vec4 accumulated_color = vec4(0.0);
+
+ /* First, compute the contribution of the center pixel. */
+ vec4 center_color = load_input(texel);
+ accumulated_color += center_color * texture_load(weights_tx, 0).x;
+
+ /* Then, compute the contributions of the pixel to the right and left, noting that the
+ * weights texture only stores the weights for the positive half, but since the filter is
+ * symmetric, the same weight is used for the negative half and we add both of their
+ * contributions. */
+ for (int i = 1; i < texture_size(weights_tx); i++) {
+ float weight = texture_load(weights_tx, i).x;
+ accumulated_color += load_input(texel + ivec2(i, 0)) * weight;
+ accumulated_color += load_input(texel + ivec2(-i, 0)) * weight;
+ }
+
+ if (gamma_uncorrect_output) {
+ accumulated_color = gamma_uncorrect_blur_output(accumulated_color);
+ }
+
+ /* Write the color using the transposed texel. See the execute_separable_blur_horizontal_pass
+ * method for more information on the rational behind this. */
+ imageStore(output_img, texel.yx, accumulated_color);
+}
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh
new file mode 100644
index 00000000000..11f2f329cd8
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_alpha_crop)
+ .local_group_size(16, 16)
+ .push_constant(Type::IVEC2, "lower_bound")
+ .push_constant(Type::IVEC2, "upper_bound")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_alpha_crop.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_bilateral_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_bilateral_blur_info.hh
new file mode 100644
index 00000000000..301cd6acd9e
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_bilateral_blur_info.hh
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_bilateral_blur)
+ .local_group_size(16, 16)
+ .push_constant(Type::INT, "radius")
+ .push_constant(Type::FLOAT, "threshold")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_2D, "determinator_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_bilateral_blur.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh
new file mode 100644
index 00000000000..36b772aa486
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_blur_info.hh
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_blur)
+ .local_group_size(16, 16)
+ .push_constant(Type::INT, "radius")
+ .push_constant(Type::BOOL, "extend_bounds")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_2D, "weights_tx")
+ .sampler(2, ImageType::FLOAT_2D, "mask_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_blur.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_bokeh_image_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_bokeh_image_info.hh
new file mode 100644
index 00000000000..3541de53070
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_bokeh_image_info.hh
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_bokeh_image)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "exterior_angle")
+ .push_constant(Type::FLOAT, "rotation")
+ .push_constant(Type::FLOAT, "roundness")
+ .push_constant(Type::FLOAT, "catadioptric")
+ .push_constant(Type::FLOAT, "lens_shift")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_bokeh_image.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh
new file mode 100644
index 00000000000..ecb253bbab1
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::IVEC2, "domain_size")
+ .push_constant(Type::VEC2, "location")
+ .push_constant(Type::VEC2, "size")
+ .push_constant(Type::FLOAT, "cos_angle")
+ .push_constant(Type::FLOAT, "sin_angle")
+ .sampler(0, ImageType::FLOAT_2D, "base_mask_tx")
+ .sampler(1, ImageType::FLOAT_2D, "mask_value_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img")
+ .compute_source("compositor_box_mask.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_add)
+ .additional_info("compositor_box_mask_shared")
+ .define("CMP_NODE_MASKTYPE_ADD")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_subtract)
+ .additional_info("compositor_box_mask_shared")
+ .define("CMP_NODE_MASKTYPE_SUBTRACT")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_multiply)
+ .additional_info("compositor_box_mask_shared")
+ .define("CMP_NODE_MASKTYPE_MULTIPLY")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_box_mask_not)
+ .additional_info("compositor_box_mask_shared")
+ .define("CMP_NODE_MASKTYPE_NOT")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh
new file mode 100644
index 00000000000..35e60056736
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_convert_shared)
+ .local_group_size(16, 16)
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .typedef_source("gpu_shader_compositor_type_conversion.glsl")
+ .compute_source("compositor_convert.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_convert_float_to_vector)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_float(value.x), 0.0)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_float_to_color)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4_from_float(value.x)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_color_to_float)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(float_from_vec4(value), vec3(0.0))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_color_to_vector)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_vec4(value), 0.0)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_vector_to_float)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(float_from_vec3(value.xyz), vec3(0.0))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_vector_to_color)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4_from_vec3(value.xyz)")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_extract_alpha_from_color)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(value.a, vec3(0.0))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_color_to_half_color)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "value")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_float_to_half_float)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(value.r, vec3(0.0))")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_convert_color_to_opaque)
+ .additional_info("compositor_convert_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .define("CONVERT_EXPRESSION(value)", "vec4(value.rgb, 1.0)")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_despeckle_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_despeckle_info.hh
new file mode 100644
index 00000000000..df86c3a8258
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_despeckle_info.hh
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_despeckle)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "threshold")
+ .push_constant(Type::FLOAT, "neighbor_threshold")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_2D, "factor_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_despeckle.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh
new file mode 100644
index 00000000000..bb9199dcd26
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_directional_blur_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_directional_blur)
+ .local_group_size(16, 16)
+ .push_constant(Type::INT, "iterations")
+ .push_constant(Type::MAT4, "inverse_transformation")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_directional_blur.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_edge_filter_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_edge_filter_info.hh
new file mode 100644
index 00000000000..916ec62bdba
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_edge_filter_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_edge_filter)
+ .local_group_size(16, 16)
+ .push_constant(Type::MAT4, "kernel")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_2D, "factor_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_edge_filter.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh
new file mode 100644
index 00000000000..52db91c94e5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::IVEC2, "domain_size")
+ .push_constant(Type::VEC2, "location")
+ .push_constant(Type::VEC2, "radius")
+ .push_constant(Type::FLOAT, "cos_angle")
+ .push_constant(Type::FLOAT, "sin_angle")
+ .sampler(0, ImageType::FLOAT_2D, "base_mask_tx")
+ .sampler(1, ImageType::FLOAT_2D, "mask_value_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img")
+ .compute_source("compositor_ellipse_mask.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_add)
+ .additional_info("compositor_ellipse_mask_shared")
+ .define("CMP_NODE_MASKTYPE_ADD")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_subtract)
+ .additional_info("compositor_ellipse_mask_shared")
+ .define("CMP_NODE_MASKTYPE_SUBTRACT")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_multiply)
+ .additional_info("compositor_ellipse_mask_shared")
+ .define("CMP_NODE_MASKTYPE_MULTIPLY")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_not)
+ .additional_info("compositor_ellipse_mask_shared")
+ .define("CMP_NODE_MASKTYPE_NOT")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_filter_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_filter_info.hh
new file mode 100644
index 00000000000..9d565cf4b8a
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_filter_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_filter)
+ .local_group_size(16, 16)
+ .push_constant(Type::MAT4, "kernel")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_2D, "factor_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_filter.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh
new file mode 100644
index 00000000000..db831518cb7
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_flip)
+ .local_group_size(16, 16)
+ .push_constant(Type::BOOL, "flip_x")
+ .push_constant(Type::BOOL, "flip_y")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_flip.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh
new file mode 100644
index 00000000000..e7736744c40
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_image_crop)
+ .local_group_size(16, 16)
+ .push_constant(Type::IVEC2, "lower_bound")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_image_crop.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_feather_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_feather_info.hh
new file mode 100644
index 00000000000..9f17f60129d
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_feather_info.hh
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_distance_feather_shared)
+ .local_group_size(16, 16)
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_1D, "weights_tx")
+ .sampler(2, ImageType::FLOAT_1D, "falloffs_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_morphological_distance_feather.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_distance_feather_dilate)
+ .additional_info("compositor_morphological_distance_feather_shared")
+ .define("COMPARE(x, y)", "x > y")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_distance_feather_erode)
+ .additional_info("compositor_morphological_distance_feather_shared")
+ .define("COMPARE(x, y)", "x < y")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_info.hh
new file mode 100644
index 00000000000..fc960e119e5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_info.hh
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_distance_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::INT, "radius")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_morphological_distance.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_distance_dilate)
+ .additional_info("compositor_morphological_distance_shared")
+ .define("OPERATOR(a, b)", "max(a, b)")
+ .define("LIMIT", "FLT_MIN")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_distance_erode)
+ .additional_info("compositor_morphological_distance_shared")
+ .define("OPERATOR(a, b)", "min(a, b)")
+ .define("LIMIT", "FLT_MAX")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh
new file mode 100644
index 00000000000..b1d64f61b80
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_morphological_distance_threshold_info.hh
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_distance_threshold)
+ .local_group_size(16, 16)
+ .push_constant(Type::INT, "radius")
+ .push_constant(Type::INT, "distance")
+ .push_constant(Type::FLOAT, "inset")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_morphological_distance_threshold.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_morphological_step_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_morphological_step_info.hh
new file mode 100644
index 00000000000..e97ffd9feea
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_morphological_step_info.hh
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_step_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::INT, "radius")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_morphological_step.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_step_dilate)
+ .additional_info("compositor_morphological_step_shared")
+ .define("OPERATOR(a, b)", "max(a, b)")
+ .define("LIMIT", "FLT_MIN")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_morphological_step_erode)
+ .additional_info("compositor_morphological_step_shared")
+ .define("OPERATOR(a, b)", "min(a, b)")
+ .define("LIMIT", "FLT_MAX")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh
new file mode 100644
index 00000000000..98fe1731703
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_projector_lens_distortion)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "dispersion")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_projector_lens_distortion.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh
new file mode 100644
index 00000000000..4528649ae98
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::MAT4, "inverse_transformation")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .compute_source("compositor_realize_on_domain.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_color)
+ .additional_info("compositor_realize_on_domain_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_vector)
+ .additional_info("compositor_realize_on_domain_shared")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_float)
+ .additional_info("compositor_realize_on_domain_shared")
+ .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh
new file mode 100644
index 00000000000..c42f2b328d4
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::VEC3, "chromatic_distortion")
+ .push_constant(Type::FLOAT, "scale")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_screen_lens_distortion.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion)
+ .additional_info("compositor_screen_lens_distortion_shared")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion_jitter)
+ .additional_info("compositor_screen_lens_distortion_shared")
+ .define("JITTER")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh
new file mode 100644
index 00000000000..ca28194e921
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_set_alpha)
+ .local_group_size(16, 16)
+ .sampler(0, ImageType::FLOAT_2D, "image_tx")
+ .sampler(1, ImageType::FLOAT_2D, "alpha_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_set_alpha.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh
new file mode 100644
index 00000000000..d5793b0ce59
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_split_viewer_shared)
+ .local_group_size(16, 16)
+ .push_constant(Type::FLOAT, "split_ratio")
+ .push_constant(Type::IVEC2, "view_size")
+ .sampler(0, ImageType::FLOAT_2D, "first_image_tx")
+ .sampler(1, ImageType::FLOAT_2D, "second_image_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_split_viewer.glsl");
+
+GPU_SHADER_CREATE_INFO(compositor_split_viewer_horizontal)
+ .additional_info("compositor_split_viewer_shared")
+ .define("SPLIT_HORIZONTAL")
+ .do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(compositor_split_viewer_vertical)
+ .additional_info("compositor_split_viewer_shared")
+ .define("SPLIT_VERTICAL")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_blur_info.hh
new file mode 100644
index 00000000000..8ba2b4e04ef
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_blur_info.hh
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_symmetric_blur)
+ .local_group_size(16, 16)
+ .push_constant(Type::BOOL, "extend_bounds")
+ .push_constant(Type::BOOL, "gamma_correct")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_2D, "weights_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_symmetric_blur.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh
new file mode 100644
index 00000000000..57247dba4b8
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/infos/compositor_symmetric_separable_blur_info.hh
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "gpu_shader_create_info.hh"
+
+GPU_SHADER_CREATE_INFO(compositor_symmetric_separable_blur)
+ .local_group_size(16, 16)
+ .push_constant(Type::BOOL, "extend_bounds")
+ .push_constant(Type::BOOL, "gamma_correct_input")
+ .push_constant(Type::BOOL, "gamma_uncorrect_output")
+ .sampler(0, ImageType::FLOAT_2D, "input_tx")
+ .sampler(1, ImageType::FLOAT_1D, "weights_tx")
+ .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
+ .compute_source("compositor_symmetric_separable_blur.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl
new file mode 100644
index 00000000000..8e3e033147f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl
@@ -0,0 +1,48 @@
+void node_composite_alpha_over_mixed(
+ float factor, vec4 color, vec4 over_color, float premultiply_factor, out vec4 result)
+{
+ if (over_color.a <= 0.0) {
+ result = color;
+ }
+ else if (factor == 1.0 && over_color.a >= 1.0) {
+ result = over_color;
+ }
+ else {
+ float add_factor = 1.0 - premultiply_factor + over_color.a * premultiply_factor;
+ float premultiplier = factor * add_factor;
+ float multiplier = 1.0 - factor * over_color.a;
+
+ result = multiplier * color + vec2(premultiplier, factor).xxxy * over_color;
+ }
+}
+
+void node_composite_alpha_over_key(float factor, vec4 color, vec4 over_color, out vec4 result)
+{
+ if (over_color.a <= 0.0) {
+ result = color;
+ }
+ else if (factor == 1.0 && over_color.a >= 1.0) {
+ result = over_color;
+ }
+ else {
+ result = mix(color, vec4(over_color.rgb, 1.0), factor * over_color.a);
+ }
+}
+
+void node_composite_alpha_over_premultiply(float factor,
+ vec4 color,
+ vec4 over_color,
+ out vec4 result)
+{
+ if (over_color.a < 0.0) {
+ result = color;
+ }
+ else if (factor == 1.0 && over_color.a >= 1.0) {
+ result = over_color;
+ }
+ else {
+ float multiplier = 1.0 - factor * over_color.a;
+
+ result = multiplier * color + factor * over_color;
+ }
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_blur_common.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_blur_common.glsl
new file mode 100644
index 00000000000..e404c03bbb0
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_blur_common.glsl
@@ -0,0 +1,32 @@
+/* Preprocess the input of the blur filter by squaring it in its alpha straight form, assuming the
+ * given color is alpha premultiplied. */
+vec4 gamma_correct_blur_input(vec4 color)
+{
+ /* Unpremultiply alpha. */
+ color.rgb /= color.a > 0.0 ? color.a : 1.0;
+
+ /* Square color channel if it is positive, otherwise zero it. */
+ color.rgb *= mix(color.rgb, vec3(0.0), lessThan(color.rgb, vec3(0.0)));
+
+ /* Premultiply alpha to undo previous alpha unpremultiplication. */
+ color.rgb *= color.a > 0.0 ? color.a : 1.0;
+
+ return color;
+}
+
+/* Postprocess the output of the blur filter by taking its square root it in its alpha straight
+ * form, assuming the given color is alpha premultiplied. This essential undoes the processing done
+ * by the gamma_correct_blur_input function. */
+vec4 gamma_uncorrect_blur_output(vec4 color)
+{
+ /* Unpremultiply alpha. */
+ color.rgb /= color.a > 0.0 ? color.a : 1.0;
+
+ /* Take the square root of the color channel if it is positive, otherwise zero it. */
+ color.rgb = mix(sqrt(color.rgb), vec3(0.0), lessThan(color.rgb, vec3(0.0)));
+
+ /* Premultiply alpha to undo previous alpha unpremultiplication. */
+ color.rgb *= color.a > 0.0 ? color.a : 1.0;
+
+ return color;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl
new file mode 100644
index 00000000000..ce71b4fd8a4
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl
@@ -0,0 +1,38 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+/* The algorithm is by Werner D. Streidt
+ * (http://visca.com/ffactory/archives/5-99/msg00021.html)
+ * Extracted of OpenCV demhist.c
+ */
+
+#define FLT_EPSILON 1.192092896e-07F
+
+void node_composite_bright_contrast(
+ vec4 color, float brightness, float contrast, const float use_premultiply, out vec4 result)
+{
+ brightness /= 100.0;
+ float delta = contrast / 200.0;
+
+ float multiplier, offset;
+ if (contrast > 0.0) {
+ multiplier = 1.0 - delta * 2.0;
+ multiplier = 1.0 / max(multiplier, FLT_EPSILON);
+ offset = multiplier * (brightness - delta);
+ }
+ else {
+ delta *= -1.0;
+ multiplier = max(1.0 - delta * 2.0, 0.0);
+ offset = multiplier * brightness + delta;
+ }
+
+ if (use_premultiply != 0.0) {
+ color_alpha_unpremultiply(color, color);
+ }
+
+ result.rgb = color.rgb * multiplier + offset;
+ result.a = color.a;
+
+ if (use_premultiply != 0.0) {
+ color_alpha_premultiply(result, result);
+ }
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl
new file mode 100644
index 00000000000..f2dcc9543f2
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl
@@ -0,0 +1,52 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+#define CMP_NODE_CHANNEL_MATTE_CS_RGB 1.0
+#define CMP_NODE_CHANNEL_MATTE_CS_HSV 2.0
+#define CMP_NODE_CHANNEL_MATTE_CS_YUV 3.0
+#define CMP_NODE_CHANNEL_MATTE_CS_YCC 4.0
+
+void node_composite_channel_matte(vec4 color,
+ const float color_space,
+ const float matte_channel,
+ const vec2 limit_channels,
+ float max_limit,
+ float min_limit,
+ out vec4 result,
+ out float matte)
+{
+ vec4 channels;
+ if (color_space == CMP_NODE_CHANNEL_MATTE_CS_HSV) {
+ rgb_to_hsv(color, channels);
+ }
+ else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YUV) {
+ rgba_to_yuva_itu_709(color, channels);
+ }
+ else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YCC) {
+ rgba_to_ycca_itu_709(color, channels);
+ }
+ else {
+ channels = color;
+ }
+
+ float matte_value = channels[int(matte_channel)];
+ float limit_value = max(channels[int(limit_channels.x)], channels[int(limit_channels.y)]);
+
+ float alpha = 1.0 - (matte_value - limit_value);
+ if (alpha > max_limit) {
+ alpha = color.a;
+ }
+ else if (alpha < min_limit) {
+ alpha = 0.0;
+ }
+ else {
+ alpha = (alpha - min_limit) / (max_limit - min_limit);
+ }
+
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
+
+#undef CMP_NODE_CHANNEL_MATTE_CS_RGB
+#undef CMP_NODE_CHANNEL_MATTE_CS_HSV
+#undef CMP_NODE_CHANNEL_MATTE_CS_YUV
+#undef CMP_NODE_CHANNEL_MATTE_CS_YCC
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl
new file mode 100644
index 00000000000..5d6bea0c9db
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl
@@ -0,0 +1,43 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+/* Algorithm from the book Video Demystified. Chapter 7. Chroma Keying. */
+void node_composite_chroma_matte(vec4 color,
+ vec4 key,
+ float acceptance,
+ float cutoff,
+ float falloff,
+ out vec4 result,
+ out float matte)
+{
+ vec4 color_ycca;
+ rgba_to_ycca_itu_709(color, color_ycca);
+ vec4 key_ycca;
+ rgba_to_ycca_itu_709(key, key_ycca);
+
+ /* Normalize the CrCb components into the [-1, 1] range. */
+ vec2 color_cc = color_ycca.yz * 2.0 - 1.0;
+ vec2 key_cc = key_ycca.yz * 2.0 - 1.0;
+
+ /* Rotate the color onto the space of the key such that x axis of the color space passes through
+ * the key color. */
+ color_cc = vector_to_rotation_matrix(key_cc * vec2(1.0, -1.0)) * color_cc;
+
+ /* Compute foreground key. If positive, the value is in the [0, 1] range. */
+ float foreground_key = color_cc.x - (abs(color_cc.y) / acceptance);
+
+ /* Negative foreground key values retain the original alpha. Positive values are scaled by the
+ * falloff, while colors that make an angle less than the cutoff angle get a zero alpha. */
+ float alpha = color.a;
+ if (foreground_key > 0.0) {
+ alpha = 1.0 - (foreground_key / falloff);
+
+ if (abs(atan(color_cc.y, color_cc.x)) < (cutoff / 2.0)) {
+ alpha = 0.0;
+ }
+ }
+
+ /* Compute output. */
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl
new file mode 100644
index 00000000000..bffb94cdedb
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl
@@ -0,0 +1,34 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_color_balance_lgg(
+ float factor, vec4 color, vec3 lift, vec3 gamma, vec3 gain, out vec4 result)
+{
+ lift = 2.0 - lift;
+ vec3 srgb_color = linear_rgb_to_srgb(color.rgb);
+ vec3 lift_balanced = ((srgb_color - 1.0) * lift) + 1.0;
+
+ vec3 gain_balanced = lift_balanced * gain;
+ gain_balanced = max(gain_balanced, vec3(0.0));
+
+ vec3 linear_color = srgb_to_linear_rgb(gain_balanced);
+ gamma = mix(gamma, vec3(1e-6), equal(gamma, vec3(0.0)));
+ vec3 gamma_balanced = pow(linear_color, 1.0 / gamma);
+
+ result.rgb = mix(color.rgb, gamma_balanced, min(factor, 1.0));
+ result.a = color.a;
+}
+
+void node_composite_color_balance_asc_cdl(float factor,
+ vec4 color,
+ vec3 offset,
+ vec3 power,
+ vec3 slope,
+ float offset_basis,
+ out vec4 result)
+{
+ offset += offset_basis;
+ vec3 balanced = color.rgb * slope + offset;
+ balanced = pow(max(balanced, vec3(0.0)), power);
+ result.rgb = mix(color.rgb, balanced, min(factor, 1.0));
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl
new file mode 100644
index 00000000000..9b4858f03be
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl
@@ -0,0 +1,87 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_color_correction(vec4 color,
+ float mask,
+ const vec3 enabled_channels,
+ float start_midtones,
+ float end_midtones,
+ float master_saturation,
+ float master_contrast,
+ float master_gamma,
+ float master_gain,
+ float master_lift,
+ float shadows_saturation,
+ float shadows_contrast,
+ float shadows_gamma,
+ float shadows_gain,
+ float shadows_lift,
+ float midtones_saturation,
+ float midtones_contrast,
+ float midtones_gamma,
+ float midtones_gain,
+ float midtones_lift,
+ float highlights_saturation,
+ float highlights_contrast,
+ float highlights_gamma,
+ float highlights_gain,
+ float highlights_lift,
+ const vec3 luminance_coefficients,
+ out vec4 result)
+{
+ const float margin = 0.10;
+ const float margin_divider = 0.5 / margin;
+ float level = (color.r + color.g + color.b) / 3.0;
+ float level_shadows = 0.0;
+ float level_midtones = 0.0;
+ float level_highlights = 0.0;
+ if (level < (start_midtones - margin)) {
+ level_shadows = 1.0;
+ }
+ else if (level < (start_midtones + margin)) {
+ level_midtones = ((level - start_midtones) * margin_divider) + 0.5;
+ level_shadows = 1.0 - level_midtones;
+ }
+ else if (level < (end_midtones - margin)) {
+ level_midtones = 1.0;
+ }
+ else if (level < (end_midtones + margin)) {
+ level_highlights = ((level - end_midtones) * margin_divider) + 0.5;
+ level_midtones = 1.0 - level_highlights;
+ }
+ else {
+ level_highlights = 1.0;
+ }
+
+ float contrast = level_shadows * shadows_contrast;
+ contrast += level_midtones * midtones_contrast;
+ contrast += level_highlights * highlights_contrast;
+ contrast *= master_contrast;
+ float saturation = level_shadows * shadows_saturation;
+ saturation += level_midtones * midtones_saturation;
+ saturation += level_highlights * highlights_saturation;
+ saturation *= master_saturation;
+ float gamma = level_shadows * shadows_gamma;
+ gamma += level_midtones * midtones_gamma;
+ gamma += level_highlights * highlights_gamma;
+ gamma *= master_gamma;
+ float gain = level_shadows * shadows_gain;
+ gain += level_midtones * midtones_gain;
+ gain += level_highlights * highlights_gain;
+ gain *= master_gain;
+ float lift = level_shadows * shadows_lift;
+ lift += level_midtones * midtones_lift;
+ lift += level_highlights * highlights_lift;
+ lift += master_lift;
+
+ float inverse_gamma = 1.0 / gamma;
+ float luma = get_luminance(color.rgb, luminance_coefficients);
+
+ vec3 corrected = luma + saturation * (color.rgb - luma);
+ corrected = 0.5 + (corrected - 0.5) * contrast;
+ corrected = fallback_pow(corrected * gain + lift, inverse_gamma, corrected);
+ corrected = mix(color.rgb, corrected, min(mask, 1.0));
+
+ result.rgb = mix(corrected, color.rgb, equal(enabled_channels, vec3(0.0)));
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl
new file mode 100644
index 00000000000..038471bc1bc
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl
@@ -0,0 +1,27 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_color_matte(vec4 color,
+ vec4 key,
+ float hue_epsilon,
+ float saturation_epsilon,
+ float value_epsilon,
+ out vec4 result,
+ out float matte)
+
+{
+ vec4 color_hsva;
+ rgb_to_hsv(color, color_hsva);
+ vec4 key_hsva;
+ rgb_to_hsv(key, key_hsva);
+
+ bool is_within_saturation = distance(color_hsva.y, key_hsva.y) < saturation_epsilon;
+ bool is_within_value = distance(color_hsva.z, key_hsva.z) < value_epsilon;
+ bool is_within_hue = distance(color_hsva.x, key_hsva.x) < hue_epsilon;
+ /* Hue wraps around, so check the distance around the boundary. */
+ float min_hue = min(color_hsva.x, key_hsva.x);
+ float max_hue = max(color_hsva.x, key_hsva.x);
+ is_within_hue = is_within_hue || ((min_hue + (1.0 - max_hue)) < hue_epsilon);
+
+ matte = (is_within_hue && is_within_saturation && is_within_value) ? 0.0 : color.a;
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl
new file mode 100644
index 00000000000..0adad53ad80
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl
@@ -0,0 +1,13 @@
+void node_composite_color_spill(vec4 color,
+ float factor,
+ const float spill_channel,
+ vec3 spill_scale,
+ const vec2 limit_channels,
+ float limit_scale,
+ out vec4 result)
+{
+ float average_limit = (color[int(limit_channels.x)] + color[int(limit_channels.y)]) / 2.0;
+ float map = factor * color[int(spill_channel)] - limit_scale * average_limit;
+ result.rgb = map > 0.0 ? color.rgb + spill_scale * map : color.rgb;
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl
new file mode 100644
index 00000000000..bcdd625bd4f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl
@@ -0,0 +1,6 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void color_to_luminance(vec4 color, const vec3 luminance_coefficients, out float result)
+{
+ result = get_luminance(color.rgb, luminance_coefficients);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl
new file mode 100644
index 00000000000..d769cadce3c
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl
@@ -0,0 +1,10 @@
+void node_composite_difference_matte(
+ vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
+{
+ vec4 difference = abs(color - key);
+ float average_difference = (difference.r + difference.g + difference.b) / 3.0;
+ bool is_opaque = average_difference > tolerance + falloff;
+ float alpha = is_opaque ? color.a : (max(0.0, average_difference - tolerance) / falloff);
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl
new file mode 100644
index 00000000000..9beed66826c
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl
@@ -0,0 +1,26 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_distance_matte_rgba(
+ vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
+{
+ float difference = distance(color.rgb, key.rgb);
+ bool is_opaque = difference > tolerance + falloff;
+ float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff;
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
+
+void node_composite_distance_matte_ycca(
+ vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte)
+{
+ vec4 color_ycca;
+ rgba_to_ycca_itu_709(color, color_ycca);
+ vec4 key_ycca;
+ rgba_to_ycca_itu_709(key, key_ycca);
+
+ float difference = distance(color_ycca.yz, key_ycca.yz);
+ bool is_opaque = difference > tolerance + falloff;
+ float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff;
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl
new file mode 100644
index 00000000000..f246635a91e
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl
@@ -0,0 +1,6 @@
+void node_composite_exposure(vec4 color, float exposure, out vec4 result)
+{
+ float multiplier = exp2(exposure);
+ result.rgb = color.rgb * multiplier;
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl
new file mode 100644
index 00000000000..53070d4b0e2
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl
@@ -0,0 +1,7 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl)
+
+void node_composite_gamma(vec4 color, float gamma, out vec4 result)
+{
+ result.rgb = fallback_pow(color.rgb, gamma, color.rgb);
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl
new file mode 100644
index 00000000000..99eb125cdf2
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl
@@ -0,0 +1,39 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+/* Curve maps are stored in sampler objects that are evaluated in the [0, 1] range, so normalize
+ * parameters accordingly. */
+#define NORMALIZE_PARAMETER(parameter, minimum, range) ((parameter - minimum) * range)
+
+void node_composite_hue_correct(float factor,
+ vec4 color,
+ sampler1DArray curve_map,
+ const float layer,
+ vec3 minimums,
+ vec3 range_dividers,
+ out vec4 result)
+{
+ vec4 hsv;
+ rgb_to_hsv(color, hsv);
+
+ /* First, adjust the hue channel on its own, since corrections in the saturation and value
+ * channels depends on the new value of the hue, not its original value. A curve map value of 0.5
+ * means no change in hue, so adjust the value to get an identity at 0.5. Since the identity of
+ * addition is 0, we subtract 0.5 (0.5 - 0.5 = 0). */
+ const float hue_parameter = NORMALIZE_PARAMETER(hsv.x, minimums.x, range_dividers.x);
+ hsv.x += texture(curve_map, vec2(hue_parameter, layer)).x - 0.5;
+
+ /* Second, adjust the saturation and value based on the new value of the hue. A curve map value
+ * of 0.5 means no change in hue, so adjust the value to get an identity at 0.5. Since the
+ * identity of duplication is 1, we multiply by 2 (0.5 * 2 = 1). */
+ vec2 parameters = NORMALIZE_PARAMETER(hsv.x, minimums.yz, range_dividers.yz);
+ hsv.y *= texture(curve_map, vec2(parameters.x, layer)).y * 2.0;
+ hsv.z *= texture(curve_map, vec2(parameters.y, layer)).z * 2.0;
+
+ /* Sanitize the new hue and saturation values. */
+ hsv.x = fract(hsv.x);
+ hsv.y = clamp(hsv.y, 0.0, 1.0);
+
+ hsv_to_rgb(hsv, result);
+
+ result = mix(color, result, factor);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl
new file mode 100644
index 00000000000..dd5eb33d318
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl
@@ -0,0 +1,16 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_hue_saturation_value(
+ vec4 color, float hue, float saturation, float value, float factor, out vec4 result)
+{
+ vec4 hsv;
+ rgb_to_hsv(color, hsv);
+
+ hsv.x = fract(hsv.x + hue + 0.5);
+ hsv.y = clamp(hsv.y * saturation, 0.0, 1.0);
+ hsv.z = hsv.z * value;
+
+ hsv_to_rgb(hsv, result);
+
+ result = mix(color, result, factor);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl
new file mode 100644
index 00000000000..59be746da7f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl
@@ -0,0 +1,13 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_invert(float fac, vec4 color, float do_rgb, float do_alpha, out vec4 result)
+{
+ result = color;
+ if (do_rgb != 0.0) {
+ result.rgb = 1.0 - result.rgb;
+ }
+ if (do_alpha != 0.0) {
+ result.a = 1.0 - result.a;
+ }
+ result = mix(color, result, fac);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl
new file mode 100644
index 00000000000..3647ac583fe
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl
@@ -0,0 +1,14 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_composite_luminance_matte(vec4 color,
+ float high,
+ float low,
+ const vec3 luminance_coefficients,
+ out vec4 result,
+ out float matte)
+{
+ float luminance = get_luminance(color.rgb, luminance_coefficients);
+ float alpha = clamp(0.0, 1.0, (luminance - low) / (high - low));
+ matte = min(alpha, color.a);
+ result = color * matte;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl
new file mode 100644
index 00000000000..27624223dbc
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl
@@ -0,0 +1,7 @@
+/* The compute shader that will be dispatched by the compositor ShaderOperation. It just calls the
+ * evaluate function that will be dynamically generated and appended to this shader in the
+ * ShaderOperation::generate_code method. */
+void main()
+{
+ evaluate();
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl
new file mode 100644
index 00000000000..20874b4ef44
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl
@@ -0,0 +1,56 @@
+/* An arbitrary value determined by Blender. */
+#define BLENDER_ZMAX 10000.0
+
+void node_composite_map_range(float value,
+ float from_min,
+ float from_max,
+ float to_min,
+ float to_max,
+ const float should_clamp,
+ out float result)
+{
+ if (abs(from_max - from_min) < 1e-6) {
+ result = 0.0;
+ }
+ else {
+ if (value >= -BLENDER_ZMAX && value <= BLENDER_ZMAX) {
+ result = (value - from_min) / (from_max - from_min);
+ result = to_min + result * (to_max - to_min);
+ }
+ else if (value > BLENDER_ZMAX) {
+ result = to_max;
+ }
+ else {
+ result = to_min;
+ }
+
+ if (should_clamp != 0.0) {
+ if (to_max > to_min) {
+ result = clamp(result, to_min, to_max);
+ }
+ else {
+ result = clamp(result, to_max, to_min);
+ }
+ }
+ }
+}
+
+void node_composite_map_value(float value,
+ float offset,
+ float size,
+ const float use_min,
+ float min,
+ const float use_max,
+ float max,
+ out float result)
+{
+ result = (value + offset) * size;
+
+ if (use_min != 0.0 && result < min) {
+ result = min;
+ }
+
+ if (use_max != 0.0 && result > max) {
+ result = max;
+ }
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl
new file mode 100644
index 00000000000..a2e3b6c4aaa
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl
@@ -0,0 +1,9 @@
+void node_composite_normal(vec3 input_vector,
+ vec3 input_normal,
+ out vec3 result_normal,
+ out float result_dot)
+{
+ vec3 normal = normalize(input_normal);
+ result_normal = normal;
+ result_dot = -dot(input_vector, normal);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl
new file mode 100644
index 00000000000..ee8ae234abe
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl
@@ -0,0 +1,6 @@
+void node_composite_posterize(vec4 color, float steps, out vec4 result)
+{
+ steps = clamp(steps, 2.0, 1024.0);
+ result = floor(color * steps) / steps;
+ result.a = color.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl
new file mode 100644
index 00000000000..d72d2260394
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl
@@ -0,0 +1,132 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+/* ** Combine/Separate XYZ ** */
+
+void node_composite_combine_xyz(float x, float y, float z, out vec3 vector)
+{
+ vector = vec3(x, y, z);
+}
+
+void node_composite_separate_xyz(vec3 vector, out float x, out float y, out float z)
+{
+ x = vector.x;
+ y = vector.y;
+ z = vector.z;
+}
+
+/* ** Combine/Separate RGBA ** */
+
+void node_composite_combine_rgba(float r, float g, float b, float a, out vec4 color)
+{
+ color = vec4(r, g, b, a);
+}
+
+void node_composite_separate_rgba(vec4 color, out float r, out float g, out float b, out float a)
+{
+ r = color.r;
+ g = color.g;
+ b = color.b;
+ a = color.a;
+}
+
+/* ** Combine/Separate HSVA ** */
+
+void node_composite_combine_hsva(float h, float s, float v, float a, out vec4 color)
+{
+ hsv_to_rgb(vec4(h, s, v, a), color);
+}
+
+void node_composite_separate_hsva(vec4 color, out float h, out float s, out float v, out float a)
+{
+ vec4 hsva;
+ rgb_to_hsv(color, hsva);
+ h = hsva.x;
+ s = hsva.y;
+ v = hsva.z;
+ a = hsva.a;
+}
+
+/* ** Combine/Separate HSLA ** */
+
+void node_composite_combine_hsla(float h, float s, float l, float a, out vec4 color)
+{
+ hsl_to_rgb(vec4(h, s, l, a), color);
+}
+
+void node_composite_separate_hsla(vec4 color, out float h, out float s, out float l, out float a)
+{
+ vec4 hsla;
+ rgb_to_hsl(color, hsla);
+ h = hsla.x;
+ s = hsla.y;
+ l = hsla.z;
+ a = hsla.a;
+}
+
+/* ** Combine/Separate YCCA ** */
+
+void node_composite_combine_ycca_itu_601(float y, float cb, float cr, float a, out vec4 color)
+{
+ ycca_to_rgba_itu_601(vec4(y, cb, cr, a), color);
+}
+
+void node_composite_combine_ycca_itu_709(float y, float cb, float cr, float a, out vec4 color)
+{
+ ycca_to_rgba_itu_709(vec4(y, cb, cr, a), color);
+}
+
+void node_composite_combine_ycca_jpeg(float y, float cb, float cr, float a, out vec4 color)
+{
+ ycca_to_rgba_jpeg(vec4(y, cb, cr, a), color);
+}
+
+void node_composite_separate_ycca_itu_601(
+ vec4 color, out float y, out float cb, out float cr, out float a)
+{
+ vec4 ycca;
+ rgba_to_ycca_itu_601(color, ycca);
+ y = ycca.x;
+ cb = ycca.y;
+ cr = ycca.z;
+ a = ycca.a;
+}
+
+void node_composite_separate_ycca_itu_709(
+ vec4 color, out float y, out float cb, out float cr, out float a)
+{
+ vec4 ycca;
+ rgba_to_ycca_itu_709(color, ycca);
+ y = ycca.x;
+ cb = ycca.y;
+ cr = ycca.z;
+ a = ycca.a;
+}
+
+void node_composite_separate_ycca_jpeg(
+ vec4 color, out float y, out float cb, out float cr, out float a)
+{
+ vec4 ycca;
+ rgba_to_ycca_jpeg(color, ycca);
+ y = ycca.x;
+ cb = ycca.y;
+ cr = ycca.z;
+ a = ycca.a;
+}
+
+/* ** Combine/Separate YUVA ** */
+
+void node_composite_combine_yuva_itu_709(float y, float u, float v, float a, out vec4 color)
+{
+ yuva_to_rgba_itu_709(vec4(y, u, v, a), color);
+}
+
+void node_composite_separate_yuva_itu_709(
+ vec4 color, out float y, out float u, out float v, out float a)
+{
+ vec4 yuva;
+ rgba_to_yuva_itu_709(color, yuva);
+ y = yuva.x;
+ u = yuva.y;
+ v = yuva.z;
+ a = yuva.a;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl
new file mode 100644
index 00000000000..95380d1ed0f
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl
@@ -0,0 +1,9 @@
+void node_composite_set_alpha_apply(vec4 color, float alpha, out vec4 result)
+{
+ result = color * alpha;
+}
+
+void node_composite_set_alpha_replace(vec4 color, float alpha, out vec4 result)
+{
+ result = vec4(color.rgb, alpha);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl
new file mode 100644
index 00000000000..7fba26907b5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl
@@ -0,0 +1,26 @@
+/* The following functions are called to store the given value in the output identified by the
+ * given ID. The ID is an unsigned integer that is encoded in a float, so floatBitsToUint is called
+ * to get the actual identifier. The functions have an output value as their last argument that is
+ * used to establish an output link that is then used to track the nodes that contribute to the
+ * output of the compositor node tree.
+ *
+ * The store_[float|vector|color] functions are dynamically generated in
+ * ShaderOperation::generate_code_for_outputs. */
+
+void node_compositor_store_output_float(const float id, float value, out float out_value)
+{
+ store_float(floatBitsToUint(id), value);
+ out_value = value;
+}
+
+void node_compositor_store_output_vector(const float id, vec3 vector, out vec3 out_vector)
+{
+ store_vector(floatBitsToUint(id), vector);
+ out_vector = vector;
+}
+
+void node_compositor_store_output_color(const float id, vec4 color, out vec4 out_color)
+{
+ store_color(floatBitsToUint(id), color);
+ out_color = color;
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl
new file mode 100644
index 00000000000..128fc6aeaf5
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl
@@ -0,0 +1,35 @@
+/* A shorthand for 1D textureSize with a zero LOD. */
+int texture_size(sampler1D sampler)
+{
+ return textureSize(sampler, 0);
+}
+
+/* A shorthand for 1D texelFetch with zero LOD and bounded access clamped to border. */
+vec4 texture_load(sampler1D sampler, int x)
+{
+ const int texture_bound = texture_size(sampler) - 1;
+ return texelFetch(sampler, clamp(x, 0, texture_bound), 0);
+}
+
+/* A shorthand for 2D textureSize with a zero LOD. */
+ivec2 texture_size(sampler2D sampler)
+{
+ return textureSize(sampler, 0);
+}
+
+/* A shorthand for 2D texelFetch with zero LOD and bounded access clamped to border. */
+vec4 texture_load(sampler2D sampler, ivec2 texel)
+{
+ const ivec2 texture_bounds = texture_size(sampler) - ivec2(1);
+ return texelFetch(sampler, clamp(texel, ivec2(0), texture_bounds), 0);
+}
+
+/* A shorthand for 2D texelFetch with zero LOD and a fallback value for out-of-bound access. */
+vec4 texture_load(sampler2D sampler, ivec2 texel, vec4 fallback)
+{
+ const ivec2 texture_bounds = texture_size(sampler) - ivec2(1);
+ if (any(lessThan(texel, ivec2(0))) || any(greaterThan(texel, texture_bounds))) {
+ return fallback;
+ }
+ return texelFetch(sampler, texel, 0);
+}
diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl
new file mode 100644
index 00000000000..75c76fd7341
--- /dev/null
+++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl
@@ -0,0 +1,29 @@
+float float_from_vec4(vec4 vector)
+{
+ return dot(vector.rgb, vec3(1.0)) / 3.0;
+}
+
+float float_from_vec3(vec3 vector)
+{
+ return dot(vector, vec3(1.0)) / 3.0;
+}
+
+vec3 vec3_from_vec4(vec4 vector)
+{
+ return vector.rgb;
+}
+
+vec3 vec3_from_float(float value)
+{
+ return vec3(value);
+}
+
+vec4 vec4_from_vec3(vec3 vector)
+{
+ return vec4(vector, 1.0);
+}
+
+vec4 vec4_from_float(float value)
+{
+ return vec4(vec3(value), 1.0);
+}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_flat_color_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_flat_color_vert.glsl
deleted file mode 100644
index cf948bb2533..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_2D_flat_color_vert.glsl
+++ /dev/null
@@ -1,6 +0,0 @@
-
-void main()
-{
- gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
- finalColor = color;
-}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_vert.glsl
index 0b5e3759dfb..8191fb6a8d6 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_image_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_image_vert.glsl
@@ -10,6 +10,5 @@ out vec2 texCoord_interp;
void main()
{
gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f);
- gl_Position.z = 1.0;
texCoord_interp = texCoord;
}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_line_dashed_uniform_color_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_line_dashed_uniform_color_vert.glsl
deleted file mode 100644
index 7878dc18362..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_2D_line_dashed_uniform_color_vert.glsl
+++ /dev/null
@@ -1,13 +0,0 @@
-
-/*
- * Vertex Shader for dashed lines with 2D coordinates,
- * with uniform multi-colors or uniform single-color, and unary thickness.
- *
- * Dashed is performed in screen space.
- */
-
-void main()
-{
- gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
- stipple_start = stipple_pos = viewport_size * 0.5 * (gl_Position.xy / gl_Position.w);
-}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl
index ecda17a7495..433aad85cf6 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl
@@ -26,5 +26,5 @@ void main()
fragColor.a *= alpha;
}
- fragColor.a *= smoothstep(1.0, 0.1, abs(colorGradient));
+ fragColor.a *= smoothstep(lineThickness, lineThickness - 0.6, abs(colorGradient));
}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
index 779bcc59487..794af5b69a5 100644
--- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl
@@ -12,10 +12,8 @@
void main(void)
{
- /* Define where along the noodle the gradient will starts and ends.
- * Use 0.25 instead of 0.35-0.65, because of a visual shift issue. */
- const float start_gradient_threshold = 0.25;
- const float end_gradient_threshold = 0.55;
+ const float start_gradient_threshold = 0.35;
+ const float end_gradient_threshold = 0.65;
#ifdef USE_INSTANCE
# define colStart (colid_doarrow[0] < 3 ? start_color : node_link_data.colors[colid_doarrow[0]])
@@ -40,6 +38,31 @@ void main(void)
vec4 colEnd = node_link_data.colors[2];
#endif
+ float line_thickness = thickness;
+
+ if (gl_VertexID < MID_VERTEX) {
+ /* Outline pass. */
+ finalColor = colShadow;
+ }
+ else {
+ /* Second pass. */
+ if (uv.x < start_gradient_threshold) {
+ finalColor = colStart;
+ }
+ else if (uv.x > end_gradient_threshold) {
+ finalColor = colEnd;
+ }
+ else {
+ float mixFactor = (uv.x - start_gradient_threshold) /
+ (end_gradient_threshold - start_gradient_threshold);
+ finalColor = mix(colStart, colEnd, mixFactor);
+ }
+ line_thickness *= 0.65f;
+ if (doMuted) {
+ finalColor[3] = 0.65;
+ }
+ }
+
/* Parameters for the dashed line. */
isMainLine = expand.y != 1.0 ? 0 : 1;
dashFactor = dash_factor;
@@ -76,35 +99,14 @@ void main(void)
exp_axis = ModelViewProjectionMatrix[0].xy * exp_axis.xx +
ModelViewProjectionMatrix[1].xy * exp_axis.yy;
- float expand_dist = (uv.y * 2.0 - 1.0);
+ float expand_dist = line_thickness * (uv.y * 2.0 - 1.0);
colorGradient = expand_dist;
-
- if (gl_VertexID < MID_VERTEX) {
- /* Shadow pass */
- finalColor = colShadow;
- }
- else {
- /* Second pass */
- if (uv.x < start_gradient_threshold) {
- finalColor = colStart;
- }
- else if (uv.x > end_gradient_threshold) {
- finalColor = colEnd;
- }
- else {
- /* Add 0.1 to avoid a visual shift issue. */
- finalColor = mix(colStart, colEnd, uv.x + 0.1);
- }
- expand_dist *= 0.5;
- if (doMuted) {
- finalColor[3] = 0.65;
- }
- }
+ lineThickness = line_thickness;
finalColor[3] *= dim_factor;
/* Expand into a line */
- gl_Position.xy += exp_axis * node_link_data.expandSize * expand_dist * thickness;
+ gl_Position.xy += exp_axis * node_link_data.expandSize * expand_dist;
/* If the link is not muted or is not a reroute arrow the points are squashed to the center of
* the line. Magic numbers are defined in drawnode.c */
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl
deleted file mode 100644
index 8690ba0767a..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_frag.glsl
+++ /dev/null
@@ -1,7 +0,0 @@
-#pragma BLENDER_REQUIRE(gpu_shader_colorspace_lib.glsl)
-
-void main()
-{
- fragColor = finalColor;
- fragColor = blender_srgb_to_framebuffer_space(fragColor);
-}
diff --git a/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_vert.glsl
deleted file mode 100644
index cf948bb2533..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_2D_smooth_color_vert.glsl
+++ /dev/null
@@ -1,6 +0,0 @@
-
-void main()
-{
- gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
- finalColor = color;
-}
diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
index 6091a5c834a..94707de71ed 100644
--- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
@@ -187,8 +187,10 @@ struct ClosureTransparency {
struct GlobalData {
/** World position. */
vec3 P;
- /** Surface Normal. */
+ /** Surface Normal. Normalized, overridden by bump displacement. */
vec3 N;
+ /** Raw interpolated normal (non-normalized) data. */
+ vec3 Ni;
/** Geometric Normal. */
vec3 Ng;
/** Curve Tangent Space. */
diff --git a/source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl b/source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl
deleted file mode 100644
index 26f96a5da32..00000000000
--- a/source/blender/gpu/shaders/gpu_shader_image_modulate_alpha_frag.glsl
+++ /dev/null
@@ -1,6 +0,0 @@
-
-void main()
-{
- fragColor = texture(image, texCoord_interp);
- fragColor.a *= alpha;
-}
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh
deleted file mode 100644
index 24a06a37a44..00000000000
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_flat_color_info.hh
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2022 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- */
-
-#include "gpu_shader_create_info.hh"
-
-#include "gpu_interface_info.hh"
-
-GPU_SHADER_CREATE_INFO(gpu_shader_2D_flat_color)
- .vertex_in(0, Type::VEC2, "pos")
- .vertex_in(1, Type::VEC4, "color")
- .vertex_out(flat_color_iface)
- .fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
- .vertex_source("gpu_shader_2D_flat_color_vert.glsl")
- .fragment_source("gpu_shader_flat_color_frag.glsl")
- .additional_info("gpu_srgb_to_framebuffer_space")
- .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh
deleted file mode 100644
index 021bd9ebb95..00000000000
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_color_info.hh
+++ /dev/null
@@ -1,14 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2022 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- */
-
-#include "gpu_shader_create_info.hh"
-
-GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_color)
- .additional_info("gpu_shader_2D_image_common")
- .push_constant(Type::VEC4, "color")
- .fragment_source("gpu_shader_image_color_frag.glsl")
- .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh
index 06aad15c18a..a92dca0ce90 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_image_info.hh
@@ -16,8 +16,3 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_image_common)
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.sampler(0, ImageType::FLOAT_2D, "image")
.vertex_source("gpu_shader_2D_image_vert.glsl");
-
-GPU_SHADER_CREATE_INFO(gpu_shader_2D_image)
- .additional_info("gpu_shader_2D_image_common")
- .fragment_source("gpu_shader_image_frag.glsl")
- .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh
index 6a419242d21..c7a6635fef7 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_2D_nodelink_info.hh
@@ -12,6 +12,7 @@ GPU_SHADER_INTERFACE_INFO(nodelink_iface, "")
.smooth(Type::FLOAT, "colorGradient")
.smooth(Type::FLOAT, "lineU")
.flat(Type::FLOAT, "lineLength")
+ .flat(Type::FLOAT, "lineThickness")
.flat(Type::FLOAT, "dashFactor")
.flat(Type::FLOAT, "dashAlpha")
.flat(Type::INT, "isMainLine");
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh
deleted file mode 100644
index d6edeef0dfb..00000000000
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_smooth_color_info.hh
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2022 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- */
-
-#include "gpu_interface_info.hh"
-#include "gpu_shader_create_info.hh"
-
-GPU_SHADER_CREATE_INFO(gpu_shader_2D_smooth_color)
- .vertex_in(0, Type::VEC2, "pos")
- .vertex_in(1, Type::VEC4, "color")
- .vertex_out(smooth_color_iface)
- .fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
- .vertex_source("gpu_shader_2D_smooth_color_vert.glsl")
- .fragment_source("gpu_shader_2D_smooth_color_frag.glsl")
- .additional_info("gpu_srgb_to_framebuffer_space")
- .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh
deleted file mode 100644
index 56ccc3f105c..00000000000
--- a/source/blender/gpu/shaders/infos/gpu_shader_2D_uniform_color_info.hh
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2022 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- */
-
-#include "gpu_shader_create_info.hh"
-
-GPU_SHADER_CREATE_INFO(gpu_shader_2D_uniform_color)
- .vertex_in(0, Type::VEC2, "pos")
- .fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(Type::VEC4, "color")
- .vertex_source("gpu_shader_2D_vert.glsl")
- .fragment_source("gpu_shader_uniform_color_frag.glsl")
- .additional_info("gpu_srgb_to_framebuffer_space")
- .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh
index 94cf58933af..8abd140397f 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_info.hh
@@ -8,13 +8,22 @@
#include "gpu_interface_info.hh"
#include "gpu_shader_create_info.hh"
-GPU_SHADER_CREATE_INFO(gpu_shader_3D_image)
+GPU_SHADER_CREATE_INFO(gpu_shader_3D_image_common)
.vertex_in(0, Type::VEC3, "pos")
.vertex_in(1, Type::VEC2, "texCoord")
.vertex_out(smooth_tex_coord_interp_iface)
.fragment_out(0, Type::VEC4, "fragColor")
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.sampler(0, ImageType::FLOAT_2D, "image")
- .vertex_source("gpu_shader_3D_image_vert.glsl")
+ .vertex_source("gpu_shader_3D_image_vert.glsl");
+
+GPU_SHADER_CREATE_INFO(gpu_shader_3D_image)
+ .additional_info("gpu_shader_3D_image_common")
.fragment_source("gpu_shader_image_frag.glsl")
.do_static_compilation(true);
+
+GPU_SHADER_CREATE_INFO(gpu_shader_3D_image_color)
+ .additional_info("gpu_shader_3D_image_common")
+ .push_constant(Type::VEC4, "color")
+ .fragment_source("gpu_shader_image_color_frag.glsl")
+ .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
deleted file mode 100644
index 35ddaa5c71c..00000000000
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_image_modulate_alpha_info.hh
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2022 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- */
-
-#include "gpu_interface_info.hh"
-#include "gpu_shader_create_info.hh"
-
-GPU_SHADER_CREATE_INFO(gpu_shader_3D_image_modulate_alpha)
- .vertex_in(0, Type::VEC3, "pos")
- .vertex_in(1, Type::VEC2, "texCoord")
- .vertex_out(smooth_tex_coord_interp_iface)
- .fragment_out(0, Type::VEC4, "fragColor")
- .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
- .push_constant(Type::FLOAT, "alpha")
- .sampler(0, ImageType::FLOAT_2D, "image", Frequency::PASS)
- .vertex_source("gpu_shader_3D_image_vert.glsl")
- .fragment_source("gpu_shader_image_modulate_alpha_frag.glsl")
- .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh
deleted file mode 100644
index 2987077ffba..00000000000
--- a/source/blender/gpu/shaders/infos/gpu_shader_3D_line_dashed_uniform_color_info.hh
+++ /dev/null
@@ -1,18 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later
- * Copyright 2022 Blender Foundation. All rights reserved. */
-
-/** \file
- * \ingroup gpu
- */
-
-#include "gpu_interface_info.hh"
-#include "gpu_shader_create_info.hh"
-
-/* TODO(jbakker): Skipped as data doesn't fit as push constant. */
-GPU_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color)
- .vertex_in(0, Type::VEC3, "pos")
- .vertex_out(flat_color_iface)
- .push_constant(Type::MAT4, "ModelViewProjectionMatrix")
- .vertex_source("gpu_shader_3D_line_dashed_uniform_color_vert.glsl")
- .fragment_source("gpu_shader_2D_line_dashed_frag.glsl")
- .do_static_compilation(true);
diff --git a/source/blender/gpu/shaders/infos/gpu_shader_line_dashed_uniform_color_info.hh b/source/blender/gpu/shaders/infos/gpu_shader_line_dashed_uniform_color_info.hh
index 57cb02c8484..a2ac08c689b 100644
--- a/source/blender/gpu/shaders/infos/gpu_shader_line_dashed_uniform_color_info.hh
+++ b/source/blender/gpu/shaders/infos/gpu_shader_line_dashed_uniform_color_info.hh
@@ -13,7 +13,8 @@ GPU_SHADER_INTERFACE_INFO(gpu_shader_line_dashed_interface, "")
.no_perspective(Type::VEC2, "stipple_start") /* In screen space */
.flat(Type::VEC2, "stipple_pos"); /* In screen space */
-GPU_SHADER_CREATE_INFO(gpu_shader_line_dashed)
+GPU_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color)
+ .vertex_in(0, Type::VEC3, "pos")
.vertex_out(flat_color_iface)
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")
.push_constant(Type::VEC2, "viewport_size")
@@ -25,18 +26,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_line_dashed)
.push_constant(Type::VEC4, "color2")
.vertex_out(gpu_shader_line_dashed_interface)
.fragment_out(0, Type::VEC4, "fragColor")
- .fragment_source("gpu_shader_2D_line_dashed_frag.glsl");
-
-GPU_SHADER_CREATE_INFO(gpu_shader_2D_line_dashed_uniform_color)
- .vertex_in(0, Type::VEC2, "pos")
- .vertex_source("gpu_shader_2D_line_dashed_uniform_color_vert.glsl")
- .additional_info("gpu_shader_line_dashed")
- .do_static_compilation(true);
-
-GPU_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color)
- .vertex_in(0, Type::VEC3, "pos")
.vertex_source("gpu_shader_3D_line_dashed_uniform_color_vert.glsl")
- .additional_info("gpu_shader_line_dashed")
+ .fragment_source("gpu_shader_2D_line_dashed_frag.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(gpu_shader_3D_line_dashed_uniform_color_clipped)
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl
index 2ae53b35b3f..bacf089deb1 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_attribute.glsl
@@ -12,6 +12,23 @@ void node_attribute_temperature(vec4 attr, out vec4 out_attr)
out_attr.w = 1.0;
}
+void node_attribute_density(vec4 attr, out float out_attr)
+{
+ out_attr = attr.x;
+}
+
+void node_attribute_flame(vec4 attr, out float out_attr)
+{
+ out_attr = attr.x;
+}
+
+void node_attribute_uniform(vec4 attr, const float attr_hash, out vec4 out_attr)
+{
+ /* Temporary solution to support both old UBO attribs and new SSBO loading.
+ * Old UBO load is already done through `attr` and will just be passed through. */
+ out_attr = attr_load_uniform(attr, floatBitsToUint(attr_hash));
+}
+
void node_attribute(
vec4 attr, out vec4 outcol, out vec3 outvec, out float outf, out float outalpha)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
index cdcdbe50917..52b4edf665f 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_displacement.glsl
@@ -1,6 +1,6 @@
void node_displacement_object(float height, float midlevel, float scale, vec3 N, out vec3 result)
{
- N = transform_direction(ModelMatrix, N);
+ N = transform_direction(ModelMatrixInverse, N);
result = (height - midlevel) * scale * normalize(N);
/* Apply object scale and orientation. */
result = transform_direction(ModelMatrix, result);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl
new file mode 100644
index 00000000000..933a8de9cb7
--- /dev/null
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_mix_color.glsl
@@ -0,0 +1,537 @@
+#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl)
+
+void node_mix_blend(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+ outcol = mix(col1, col2, fac);
+ outcol.a = col1.a;
+}
+
+void node_mix_add(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ outcol = mix(col1, col1 + col2, fac);
+ outcol.a = col1.a;
+}
+
+void node_mix_mult(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ outcol = mix(col1, col1 * col2, fac);
+ outcol.a = col1.a;
+}
+
+void node_mix_screen(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float facm = 1.0 - fac;
+
+ outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1);
+ outcol.a = col1.a;
+}
+
+void node_mix_overlay(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float facm = 1.0 - fac;
+
+ outcol = col1;
+
+ if (outcol.r < 0.5) {
+ outcol.r *= facm + 2.0 * fac * col2.r;
+ }
+ else {
+ outcol.r = 1.0 - (facm + 2.0 * fac * (1.0 - col2.r)) * (1.0 - outcol.r);
+ }
+
+ if (outcol.g < 0.5) {
+ outcol.g *= facm + 2.0 * fac * col2.g;
+ }
+ else {
+ outcol.g = 1.0 - (facm + 2.0 * fac * (1.0 - col2.g)) * (1.0 - outcol.g);
+ }
+
+ if (outcol.b < 0.5) {
+ outcol.b *= facm + 2.0 * fac * col2.b;
+ }
+ else {
+ outcol.b = 1.0 - (facm + 2.0 * fac * (1.0 - col2.b)) * (1.0 - outcol.b);
+ }
+}
+
+void node_mix_sub(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ outcol = mix(col1, col1 - col2, fac);
+ outcol.a = col1.a;
+}
+
+/* A variant of mix_div that fallback to the first color upon zero division. */
+void node_mix_div_fallback(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float facm = 1.0 - fac;
+
+ outcol = col1;
+
+ if (col2.r != 0.0) {
+ outcol.r = facm * outcol.r + fac * outcol.r / col2.r;
+ }
+ if (col2.g != 0.0) {
+ outcol.g = facm * outcol.g + fac * outcol.g / col2.g;
+ }
+ if (col2.b != 0.0) {
+ outcol.b = facm * outcol.b + fac * outcol.b / col2.b;
+ }
+}
+
+void node_mix_diff(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ outcol = mix(col1, abs(col1 - col2), fac);
+ outcol.a = col1.a;
+}
+
+void node_mix_dark(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac);
+ outcol.a = col1.a;
+}
+
+void node_mix_light(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+ outcol.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac);
+ outcol.a = col1.a;
+}
+
+void node_mix_dodge(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+ outcol = col1;
+
+ if (outcol.r != 0.0) {
+ float tmp = 1.0 - fac * col2.r;
+ if (tmp <= 0.0) {
+ outcol.r = 1.0;
+ }
+ else if ((tmp = outcol.r / tmp) > 1.0) {
+ outcol.r = 1.0;
+ }
+ else {
+ outcol.r = tmp;
+ }
+ }
+ if (outcol.g != 0.0) {
+ float tmp = 1.0 - fac * col2.g;
+ if (tmp <= 0.0) {
+ outcol.g = 1.0;
+ }
+ else if ((tmp = outcol.g / tmp) > 1.0) {
+ outcol.g = 1.0;
+ }
+ else {
+ outcol.g = tmp;
+ }
+ }
+ if (outcol.b != 0.0) {
+ float tmp = 1.0 - fac * col2.b;
+ if (tmp <= 0.0) {
+ outcol.b = 1.0;
+ }
+ else if ((tmp = outcol.b / tmp) > 1.0) {
+ outcol.b = 1.0;
+ }
+ else {
+ outcol.b = tmp;
+ }
+ }
+}
+
+void node_mix_burn(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float tmp, facm = 1.0 - fac;
+
+ outcol = col1;
+
+ tmp = facm + fac * col2.r;
+ if (tmp <= 0.0) {
+ outcol.r = 0.0;
+ }
+ else if ((tmp = (1.0 - (1.0 - outcol.r) / tmp)) < 0.0) {
+ outcol.r = 0.0;
+ }
+ else if (tmp > 1.0) {
+ outcol.r = 1.0;
+ }
+ else {
+ outcol.r = tmp;
+ }
+
+ tmp = facm + fac * col2.g;
+ if (tmp <= 0.0) {
+ outcol.g = 0.0;
+ }
+ else if ((tmp = (1.0 - (1.0 - outcol.g) / tmp)) < 0.0) {
+ outcol.g = 0.0;
+ }
+ else if (tmp > 1.0) {
+ outcol.g = 1.0;
+ }
+ else {
+ outcol.g = tmp;
+ }
+
+ tmp = facm + fac * col2.b;
+ if (tmp <= 0.0) {
+ outcol.b = 0.0;
+ }
+ else if ((tmp = (1.0 - (1.0 - outcol.b) / tmp)) < 0.0) {
+ outcol.b = 0.0;
+ }
+ else if (tmp > 1.0) {
+ outcol.b = 1.0;
+ }
+ else {
+ outcol.b = tmp;
+ }
+}
+
+void node_mix_hue(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float facm = 1.0 - fac;
+
+ outcol = col1;
+
+ vec4 hsv, hsv2, tmp;
+ rgb_to_hsv(col2, hsv2);
+
+ if (hsv2.y != 0.0) {
+ rgb_to_hsv(outcol, hsv);
+ hsv.x = hsv2.x;
+ hsv_to_rgb(hsv, tmp);
+
+ outcol = mix(outcol, tmp, fac);
+ outcol.a = col1.a;
+ }
+}
+
+void node_mix_sat(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float facm = 1.0 - fac;
+
+ outcol = col1;
+
+ vec4 hsv, hsv2;
+ rgb_to_hsv(outcol, hsv);
+
+ if (hsv.y != 0.0) {
+ rgb_to_hsv(col2, hsv2);
+
+ hsv.y = facm * hsv.y + fac * hsv2.y;
+ hsv_to_rgb(hsv, outcol);
+ }
+}
+
+void node_mix_val(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float facm = 1.0 - fac;
+
+ vec4 hsv, hsv2;
+ rgb_to_hsv(col1, hsv);
+ rgb_to_hsv(col2, hsv2);
+
+ hsv.z = facm * hsv.z + fac * hsv2.z;
+ hsv_to_rgb(hsv, outcol);
+}
+
+void node_mix_color(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float facm = 1.0 - fac;
+
+ outcol = col1;
+
+ vec4 hsv, hsv2, tmp;
+ rgb_to_hsv(col2, hsv2);
+
+ if (hsv2.y != 0.0) {
+ rgb_to_hsv(outcol, hsv);
+ hsv.x = hsv2.x;
+ hsv.y = hsv2.y;
+ hsv_to_rgb(hsv, tmp);
+
+ outcol = mix(outcol, tmp, fac);
+ outcol.a = col1.a;
+ }
+}
+
+void node_mix_soft(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ float facm = 1.0 - fac;
+
+ vec4 one = vec4(1.0);
+ vec4 scr = one - (one - col2) * (one - col1);
+ outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr);
+}
+
+void node_mix_linear(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ outcol = col1 + fac * (2.0 * (col2 - vec4(0.5)));
+}
+
+void node_mix_float(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ outfloat = mix(f1, f2, fac);
+}
+
+void node_mix_vector(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+
+ outvec = mix(v1, v2, fac);
+}
+
+void node_mix_vector_non_uniform(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+ outvec = mix(v1, v2, facvec);
+}
+
+void node_mix_rgba(float fac,
+ vec3 facvec,
+ float f1,
+ float f2,
+ vec3 v1,
+ vec3 v2,
+ vec4 col1,
+ vec4 col2,
+ out float outfloat,
+ out vec3 outvec,
+ out vec4 outcol)
+{
+ outcol = mix(col1, col2, fac);
+}
+
+void node_mix_clamp_vector(vec3 vec, vec3 min, vec3 max, out vec3 outvec)
+{
+ outvec = clamp(vec, min, max);
+}
+
+void node_mix_clamp_value(float value, float min, float max, out float outfloat)
+{
+ outfloat = clamp(value, min, max);
+}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
index 881e38ea11a..480334f9bbd 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_noise.glsl
@@ -13,7 +13,6 @@
* + + |
* @ + + + + @ @------> x
* v0 v1
- *
*/
float bi_mix(float v0, float v1, float v2, float v3, float x, float y)
{
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
index a54dc59ddfe..3fc4992f7c4 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_normal_map.glsl
@@ -3,13 +3,13 @@
void node_normal_map(vec4 tangent, vec3 texnormal, out vec3 outnormal)
{
if (all(equal(tangent, vec4(0.0, 0.0, 0.0, 1.0)))) {
- outnormal = g_data.N;
+ outnormal = g_data.Ni;
return;
}
tangent *= (FrontFacing ? 1.0 : -1.0);
- vec3 B = tangent.w * cross(g_data.N, tangent.xyz) * sign(ObjectInfo.w);
+ vec3 B = tangent.w * cross(g_data.Ni, tangent.xyz) * sign(ObjectInfo.w);
- outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.N;
+ outnormal = texnormal.x * tangent.xyz + texnormal.y * B + texnormal.z * g_data.Ni;
outnormal = normalize(outnormal);
}
#endif
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
index 2e695fa3e14..0d8f2272c10 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl
@@ -149,25 +149,37 @@ void node_bsdf_principled(vec4 base_color,
max(roughness, transmission_roughness);
refraction_data.ior = ior;
+ /* Ref. T98190: Defines are optimizations for old compilers.
+ * Might become unecessary with EEVEE-Next. */
if (do_diffuse == 0.0 && do_refraction == 0.0 && do_clearcoat != 0.0) {
+#ifdef PRINCIPLED_CLEARCOAT
/* Metallic & Clearcoat case. */
result = closure_eval(reflection_data, clearcoat_data);
+#endif
}
else if (do_diffuse == 0.0 && do_refraction == 0.0 && do_clearcoat == 0.0) {
+#ifdef PRINCIPLED_METALLIC
/* Metallic case. */
result = closure_eval(reflection_data);
+#endif
}
else if (do_diffuse != 0.0 && do_refraction == 0.0 && do_clearcoat == 0.0) {
+#ifdef PRINCIPLED_DIELECTRIC
/* Dielectric case. */
result = closure_eval(diffuse_data, reflection_data);
+#endif
}
else if (do_diffuse == 0.0 && do_refraction != 0.0 && do_clearcoat == 0.0) {
+#ifdef PRINCIPLED_GLASS
/* Glass case. */
result = closure_eval(reflection_data, refraction_data);
+#endif
}
else {
+#ifdef PRINCIPLED_ANY
/* Un-optimized case. */
result = closure_eval(diffuse_data, reflection_data, clearcoat_data, refraction_data);
+#endif
}
Closure emission_cl = closure_eval(emission_data);
Closure transparency_cl = closure_eval(transparency_data);
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
index 961fe23e67e..7171c5f2b36 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl
@@ -153,13 +153,12 @@ void node_tex_musgrave_hybrid_multi_fractal_1d(vec3 co,
float lacunarity = max(lac, 1e-5);
float pwHL = pow(lacunarity, -H);
- float pwr = pwHL;
- float value = snoise(p) + offset;
- float weight = gain * value;
- p *= lacunarity;
+ float pwr = 1.0;
+ float value = 0.0;
+ float weight = 1.0;
- for (int i = 1; (weight > 0.001f) && (i < int(octaves)); i++) {
+ for (int i = 0; (weight > 0.001f) && (i < int(octaves)); i++) {
if (weight > 1.0) {
weight = 1.0;
}
@@ -172,8 +171,12 @@ void node_tex_musgrave_hybrid_multi_fractal_1d(vec3 co,
}
float rmd = octaves - floor(octaves);
- if (rmd != 0.0) {
- value += rmd * ((snoise(p) + offset) * pwr);
+ if ((rmd != 0.0) && (weight > 0.001f)) {
+ if (weight > 1.0) {
+ weight = 1.0;
+ }
+ float signal = (snoise(p) + offset) * pwr;
+ value += rmd * weight * signal;
}
fac = value;
@@ -375,13 +378,12 @@ void node_tex_musgrave_hybrid_multi_fractal_2d(vec3 co,
float lacunarity = max(lac, 1e-5);
float pwHL = pow(lacunarity, -H);
- float pwr = pwHL;
- float value = snoise(p) + offset;
- float weight = gain * value;
- p *= lacunarity;
+ float pwr = 1.0;
+ float value = 0.0;
+ float weight = 1.0;
- for (int i = 1; (weight > 0.001f) && (i < int(octaves)); i++) {
+ for (int i = 0; (weight > 0.001f) && (i < int(octaves)); i++) {
if (weight > 1.0) {
weight = 1.0;
}
@@ -394,8 +396,12 @@ void node_tex_musgrave_hybrid_multi_fractal_2d(vec3 co,
}
float rmd = octaves - floor(octaves);
- if (rmd != 0.0) {
- value += rmd * ((snoise(p) + offset) * pwr);
+ if ((rmd != 0.0) && (weight > 0.001f)) {
+ if (weight > 1.0) {
+ weight = 1.0;
+ }
+ float signal = (snoise(p) + offset) * pwr;
+ value += rmd * weight * signal;
}
fac = value;
@@ -597,13 +603,12 @@ void node_tex_musgrave_hybrid_multi_fractal_3d(vec3 co,
float lacunarity = max(lac, 1e-5);
float pwHL = pow(lacunarity, -H);
- float pwr = pwHL;
- float value = snoise(p) + offset;
- float weight = gain * value;
- p *= lacunarity;
+ float pwr = 1.0;
+ float value = 0.0;
+ float weight = 1.0;
- for (int i = 1; (weight > 0.001f) && (i < int(octaves)); i++) {
+ for (int i = 0; (weight > 0.001f) && (i < int(octaves)); i++) {
if (weight > 1.0) {
weight = 1.0;
}
@@ -616,8 +621,12 @@ void node_tex_musgrave_hybrid_multi_fractal_3d(vec3 co,
}
float rmd = octaves - floor(octaves);
- if (rmd != 0.0) {
- value += rmd * ((snoise(p) + offset) * pwr);
+ if ((rmd != 0.0) && (weight > 0.001f)) {
+ if (weight > 1.0) {
+ weight = 1.0;
+ }
+ float signal = (snoise(p) + offset) * pwr;
+ value += rmd * weight * signal;
}
fac = value;
@@ -819,13 +828,12 @@ void node_tex_musgrave_hybrid_multi_fractal_4d(vec3 co,
float lacunarity = max(lac, 1e-5);
float pwHL = pow(lacunarity, -H);
- float pwr = pwHL;
- float value = snoise(p) + offset;
- float weight = gain * value;
- p *= lacunarity;
+ float pwr = 1.0;
+ float value = 0.0;
+ float weight = 1.0;
- for (int i = 1; (weight > 0.001f) && (i < int(octaves)); i++) {
+ for (int i = 0; (weight > 0.001f) && (i < int(octaves)); i++) {
if (weight > 1.0) {
weight = 1.0;
}
@@ -838,8 +846,12 @@ void node_tex_musgrave_hybrid_multi_fractal_4d(vec3 co,
}
float rmd = octaves - floor(octaves);
- if (rmd != 0.0) {
- value += rmd * ((snoise(p) + offset) * pwr);
+ if ((rmd != 0.0) && (weight > 0.001f)) {
+ if (weight > 1.0) {
+ weight = 1.0;
+ }
+ float signal = (snoise(p) + offset) * pwr;
+ value += rmd * weight * signal;
}
fac = value;
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl
index b6aad5904ff..c4b47bc1756 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_sky.glsl
@@ -144,7 +144,47 @@ void node_tex_sky_hosekwilkie(vec3 co,
color = vec4(dot(xyz_to_r, xyz), dot(xyz_to_g, xyz), dot(xyz_to_b, xyz), 1);
}
-void node_tex_sky_nishita(vec3 co, out vec4 color)
+void node_tex_sky_nishita(vec3 co,
+ float sun_rotation,
+ vec3 xyz_to_r,
+ vec3 xyz_to_g,
+ vec3 xyz_to_b,
+ sampler2DArray ima,
+ float layer,
+ out vec4 color)
{
- color = vec4(1.0);
+ vec3 spherical = sky_spherical_coordinates(co);
+
+ vec3 xyz;
+ if (co.z < -0.4) {
+ /* too far below the horizon, just return black */
+ color = vec4(0, 0, 0, 1);
+ }
+ else {
+ /* evaluate longitudinal position on the map */
+ float x = (spherical.y + M_PI + sun_rotation) / M_2PI;
+ if (x > 1.0) {
+ x -= 1.0;
+ }
+
+ float fade;
+ float y;
+ if (co.z < 0.0) {
+ /* we're below the horizon, so extend the map by blending from values at the horizon
+ * to zero according to a cubic falloff */
+ fade = 1.0 + co.z * 2.5;
+ fade = fade * fade * fade;
+ y = 0.0;
+ }
+ else {
+ /* we're above the horizon, so compute the lateral position by inverting the remapped
+ * coordinates that are preserve to have more detail near the horizon. */
+ fade = 1.0;
+ y = sqrt((M_PI_2 - spherical.x) / M_PI_2);
+ }
+
+ /* look up color in the precomputed map and convert to RGB */
+ xyz = fade * texture(ima, vec3(x, y, layer)).rgb;
+ color = vec4(dot(xyz_to_r, xyz), dot(xyz_to_g, xyz), dot(xyz_to_b, xyz), 1);
+ }
}
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
index 0fb8ef15f5f..aac3d98b43b 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_voronoi.glsl
@@ -15,7 +15,6 @@
*
* With optimization to change -2..2 scan window to -1..1 for better performance,
* as explained in https://www.shadertoy.com/view/llG3zy.
- *
*/
/* **** 1D Voronoi **** */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
index 204f134dfa6..c849553ae4c 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl
@@ -1,4 +1,9 @@
+void node_tex_coord_position(out vec3 out_pos)
+{
+ out_pos = g_data.P;
+}
+
void node_tex_coord(mat4 obmatinv,
vec3 attr_orco,
vec4 attr_uv,
diff --git a/source/blender/gpu/shaders/metal/mtl_shader_common.msl b/source/blender/gpu/shaders/metal/mtl_shader_common.msl
new file mode 100644
index 00000000000..c504cdbacb1
--- /dev/null
+++ b/source/blender/gpu/shaders/metal/mtl_shader_common.msl
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/* Common Metal header to be included in all compiled Metal shaders.
+ * Both native MSL shaders and GLSL shaders. */
+
+using namespace metal;
+
+/* Should match GPUVertFetchMode. */
+typedef enum {
+ GPU_FETCH_FLOAT = 0,
+ GPU_FETCH_INT,
+ GPU_FETCH_INT_TO_FLOAT_UNIT,
+ GPU_FETCH_INT_TO_FLOAT,
+} GPUVertFetchMode;
+
+/* Consant to flag base binding index of uniform buffers. */
+constant int MTL_uniform_buffer_base_index [[function_constant(0)]];
+
+/* Default Point Size.
+ * Unused if function constant not set. */
+constant float MTL_global_pointsize [[function_constant(1)]];
+
+/* Attribute conversions flags (Up to 16 attributes supported in Blender). */
+constant int MTL_AttributeConvert0 [[function_constant(2)]];
+constant int MTL_AttributeConvert1 [[function_constant(3)]];
+constant int MTL_AttributeConvert2 [[function_constant(4)]];
+constant int MTL_AttributeConvert3 [[function_constant(5)]];
+constant int MTL_AttributeConvert4 [[function_constant(6)]];
+constant int MTL_AttributeConvert5 [[function_constant(7)]];
+constant int MTL_AttributeConvert6 [[function_constant(8)]];
+constant int MTL_AttributeConvert7 [[function_constant(9)]];
+constant int MTL_AttributeConvert8 [[function_constant(10)]];
+constant int MTL_AttributeConvert9 [[function_constant(11)]];
+constant int MTL_AttributeConvert10 [[function_constant(12)]];
+constant int MTL_AttributeConvert11 [[function_constant(13)]];
+constant int MTL_AttributeConvert12 [[function_constant(14)]];
+constant int MTL_AttributeConvert13 [[function_constant(15)]];
+constant int MTL_AttributeConvert14 [[function_constant(16)]];
+constant int MTL_AttributeConvert15 [[function_constant(17)]];
+
+/* Consant to flag binding index of transform feedback buffer.
+ * Unused if function constant not set. */
+constant int MTL_transform_feedback_buffer_index [[function_constant(18)]];
+
+/** Internal attribute conversion functionality. */
+/* Following descriptions in mtl_shader.hh, Metal only supports some implicit
+ * attribute type conversions. These conversions occur when there is a difference
+ * between the type specified in the vertex descriptor (In the input vertex buffers),
+ * and the attribute type in the shader's VertexIn struct (ShaderInterface).
+ *
+ * The supported implicit conversions are described here:
+ * https://developer.apple.com/documentation/metal/mtlvertexattributedescriptor/1516081-format?language=objc
+ *
+ * For unsupported conversions, the mtl_shader_generator will create an attribute reading function
+ * which performs this conversion manually upon read, depending on the requested fetchmode.
+ *
+ * These conversions use the function constants above, so any branching is optimized out during
+ * backend shader compilation (PSO creation).
+ *
+ * NOTE: Not all possibilities have been covered here, any additional conversion routines should
+ * be added as needed, and mtl_shader_generator should also be updated with any newly required
+ * read functions.
+ *
+ * These paths are only needed for cases where implicit conversion will not happen, in which
+ * case the value will be read as the type in the shader.
+ */
+#define internal_vertex_attribute_convert_read_float(ATTR, v_in, v_out) \
+ if (ATTR == GPU_FETCH_INT_TO_FLOAT) { \
+ v_out = float(as_type<int>(v_in)); \
+ } \
+ else if (ATTR == GPU_FETCH_INT_TO_FLOAT_UNIT) { \
+ v_out = float(as_type<int>(v_in)) / float(__INT_MAX__); \
+ } \
+ else { \
+ v_out = v_in; \
+ }
+
+#define internal_vertex_attribute_convert_read_float2(ATTR, v_in, v_out) \
+ if (ATTR == GPU_FETCH_INT_TO_FLOAT) { \
+ v_out = float2(as_type<int2>(v_in)); \
+ } \
+ else if (ATTR == GPU_FETCH_INT_TO_FLOAT_UNIT) { \
+ v_out = float2(as_type<int2>(v_in)) / float2(__INT_MAX__); \
+ } \
+ else { \
+ v_out = v_in; \
+ }
+
+#define internal_vertex_attribute_convert_read_float3(ATTR, v_in, v_out) \
+ if (ATTR == GPU_FETCH_INT_TO_FLOAT) { \
+ v_out = float3(as_type<int3>(v_in)); \
+ } \
+ else if (ATTR == GPU_FETCH_INT_TO_FLOAT_UNIT) { \
+ v_out = float3(as_type<int3>(v_in)) / float3(__INT_MAX__); \
+ } \
+ else { \
+ v_out = v_in; \
+ }
+
+#define internal_vertex_attribute_convert_read_float4(ATTR, v_in, v_out) \
+ if (ATTR == GPU_FETCH_INT_TO_FLOAT) { \
+ v_out = float4(as_type<int4>(v_in)); \
+ } \
+ else if (ATTR == GPU_FETCH_INT_TO_FLOAT_UNIT) { \
+ v_out = float4(as_type<int4>(v_in)) / float4(__INT_MAX__); \
+ } \
+ else { \
+ v_out = v_in; \
+ }
diff --git a/source/blender/gpu/shaders/metal/mtl_shader_defines.msl b/source/blender/gpu/shaders/metal/mtl_shader_defines.msl
new file mode 100644
index 00000000000..3b32783620d
--- /dev/null
+++ b/source/blender/gpu/shaders/metal/mtl_shader_defines.msl
@@ -0,0 +1,1065 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** Special header for mapping commonly defined tokens to API-specific variations.
+ * Where possible, this will adhere closely to base GLSL, where semantics are the same.
+ * However, host code shader code may need modifying to support types where necessary variations
+ * exist between APIs but are not expressed through the source. (e.g. distinctio between depth2d
+ * and texture2d types in metal).
+ */
+
+/* Base instance with offsets. */
+#define gpu_BaseInstance gl_BaseInstanceARB
+#define gpu_InstanceIndex (gl_InstanceID + gpu_BaseInstance)
+
+/* derivative signs. */
+#define DFDX_SIGN 1.0
+#define DFDY_SIGN 1.0
+
+/* Type definitions. */
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define mat2 float2x2
+#define mat2x2 float2x2
+#define mat3 float3x3
+#define mat4 float4x4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+/* MTLBOOL is used for native boolean's generated by the Metal backend, to avoid type-emulation
+ * for GLSL bools, which are treated as integers. */
+#define MTLBOOL bool
+#define bool int
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define vec3_1010102_Unorm uint
+#define vec3_1010102_Inorm int
+
+/* Strip GLSL Decorators. */
+#define in
+#define flat
+#define smooth
+#define noperspective
+#define layout(std140) struct
+#define uniform
+
+/* Used to replace 'out' in function parameters with threadlocal reference
+ * shortened to avoid expanding the glsl source string. */
+#define THD thread
+
+/* Generate wrapper structs for combined texture and sampler type. */
+#ifdef USE_ARGUMENT_BUFFER_FOR_SAMPLERS
+# define COMBINED_SAMPLER_TYPE(STRUCT_NAME, TEX_TYPE) \
+ template<typename T, access A = access::sample> struct STRUCT_NAME { \
+ thread TEX_TYPE<T, A> *texture; \
+ constant sampler *samp; \
+ }
+#else
+# define COMBINED_SAMPLER_TYPE(STRUCT_NAME, TEX_TYPE) \
+ template<typename T, access A = access::sample> struct STRUCT_NAME { \
+ thread TEX_TYPE<T, A> *texture; \
+ thread sampler *samp; \
+ }
+#endif
+
+/* Add any types as needed. */
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_1d, texture1d);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_1d_array, texture1d_array);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_2d, texture2d);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_depth_2d, depth2d);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_2d_array, texture2d_array);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_depth_2d_array, depth2d_array);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_3d, texture3d);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_buffer, texture_buffer);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_cube, texturecube);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_cube_array, texturecube_array);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_depth_cube, texturecube_array);
+COMBINED_SAMPLER_TYPE(_mtl_combined_image_sampler_depth_cube_array, texturecube_array);
+
+/* Sampler struct for argument buffer. */
+#ifdef USE_ARGUMENT_BUFFER_FOR_SAMPLERS
+struct SStruct {
+ array<sampler, ARGUMENT_BUFFER_NUM_SAMPLERS> sampler_args [[id(0)]];
+};
+#endif
+
+/* Samplers as function parameters. */
+#define sampler1D thread _mtl_combined_image_sampler_1d<float>
+#define sampler1DArray thread _mtl_combined_image_sampler_1d_array<float>
+#define sampler2D thread _mtl_combined_image_sampler_2d<float>
+#define depth2D thread _mtl_combined_image_sampler_depth_2d<float>
+#define sampler2DArray thread _mtl_combined_image_sampler_2d_array<float>
+#define sampler2DArrayShadow thread _mtl_combined_image_sampler_depth_2d_array<float>
+#define depth2DArrayShadow thread _mtl_combined_image_sampler_depth_2d_array<float>
+#define sampler3D thread _mtl_combined_image_sampler_3d<float>
+#define samplerBuffer thread _mtl_combined_image_sampler_buffer<float, access::read>
+#define samplerCube thread _mtl_combined_image_sampler_cube<float>
+#define samplerCubeArray thread _mtl_combined_image_sampler_cube_array<float>
+
+#define usampler1D thread _mtl_combined_image_sampler_1d<uint>
+#define usampler1DArray thread _mtl_combined_image_sampler_1d_array<uint>
+#define usampler2D thread _mtl_combined_image_sampler_2d<uint>
+#define udepth2D thread _mtl_combined_image_sampler_depth_2d<uint>
+#define usampler2DArray thread _mtl_combined_image_sampler_2d_array<uint>
+#define usampler2DArrayShadow thread _mtl_combined_image_sampler_depth_2d_array<uint>
+#define udepth2DArrayShadow thread _mtl_combined_image_sampler_depth_2d_array<uint>
+#define usampler3D thread _mtl_combined_image_sampler_3d<uint>
+#define usamplerBuffer thread _mtl_combined_image_sampler_buffer<uint, access::read>
+#define usamplerCube thread _mtl_combined_image_sampler_cube<uint>
+#define usamplerCubeArray thread _mtl_combined_image_sampler_cube_array<uint>
+
+#define isampler1D thread _mtl_combined_image_sampler_1d<int>
+#define isampler1DArray thread _mtl_combined_image_sampler_1d_array<int>
+#define isampler2D thread _mtl_combined_image_sampler_2d<int>
+#define idepth2D thread _mtl_combined_image_sampler_depth_2d<int>
+#define isampler2DArray thread _mtl_combined_image_sampler_2d_array<int>
+#define isampler2DArrayShadow thread _mtl_combined_image_sampler_depth_2d_array<int>
+#define idepth2DArrayShadow thread _mtl_combined_image_sampler_depth_2d_array<int>
+#define isampler3D thread _mtl_combined_image_sampler_3d<int>
+#define isamplerBuffer thread _mtl_combined_image_sampler_buffer<int, access::read>
+#define isamplerCube thread _mtl_combined_image_sampler_cube<int>
+#define isamplerCubeArray thread _mtl_combined_image_sampler_cube_array<int>
+
+/* Vector accessor aliases. */
+#define st xy
+
+/* Texture functions. */
+#define texelFetch _texelFetch_internal
+#define texelFetchOffset(__tex, __texel, __lod, __offset) \
+ _texelFetch_internal(__tex, __texel, __lod, __offset)
+#define texture2(__tex, __uv) _texture_internal_samp(__tex, __uv)
+#define texture3(__tex, __uv, _bias) _texture_internal_bias(__tex, __uv, bias(float(_bias)))
+#define textureLod(__tex, __uv, __lod) _texture_internal_level(__tex, __uv, level(float(__lod)))
+#define textureLodOffset(__tex, __uv, __lod, __offset) \
+ _texture_internal_level(__tex, __uv, level(float(__lod)), __offset)
+#define textureGather2(__tex, __uv) _texture_gather_internal(__tex, __uv, 0)
+#define textureGather3(__tex, __uv, __comp) _texture_gather_internal(__tex, __uv, __comp)
+#define textureGatherOffset(__tex, __offset, __uv, __comp) \
+ _texture_gather_internal(__tex, __uv, __comp, __offset)
+
+#define TEXURE_MACRO(_1, _2, _3, TEXNAME, ...) TEXNAME
+#define texture(...) TEXURE_MACRO(__VA_ARGS__, texture3, texture2)(__VA_ARGS__)
+#define textureGather(...) TEXURE_MACRO(__VA_ARGS__, textureGather3, textureGather2)(__VA_ARGS__)
+
+/* Texture-write functions. */
+#define imageStore(_tex, _coord, _value) _texture_write_internal(_tex, _coord, _value)
+
+/* Singular return values from texture functions of type DEPTH are often indexed with either .r or
+ * .x. This is a lightweight wrapper type for handling this syntax. */
+union _msl_return_float {
+ float r;
+ float x;
+ inline operator float() const
+ {
+ return r;
+ }
+};
+
+/* Add custom texture sampling/reading routines for each type to account for special return cases,
+ * e.g. returning a float with an r parameter Note: Cannot use template specialization for input
+ * type, as return types are specific to the signature of 'tex'. */
+/* Texture Read. */
+template<typename S, typename T, access A>
+inline vec<S, 4> _texelFetch_internal(thread _mtl_combined_image_sampler_1d<S, A> tex, T texel)
+{
+ float w = tex.texture->get_width();
+ if (texel >= 0 && texel < w) {
+ return tex.texture->read(uint(texel));
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename S, typename T>
+inline vec<S, 4> _texelFetch_internal(
+ const thread _mtl_combined_image_sampler_buffer<S, access::read> tex, T texel)
+{
+ float w = tex.texture->get_width();
+ if (texel >= 0 && texel < w) {
+ return tex.texture->read(uint(texel));
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename S, typename T, access A>
+inline vec<S, 4> _texelFetch_internal(thread _mtl_combined_image_sampler_1d<S, A> tex,
+ T texel,
+ uint lod,
+ T offset = 0)
+{
+ float w = tex.texture->get_width();
+ if ((texel + offset) >= 0 && (texel + offset) < w) {
+ /* LODs not supported for 1d textures. This must be zero. */
+ return tex.texture->read(uint(texel + offset), 0);
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename S, typename T, access A>
+inline vec<S, 4> _texelFetch_internal(thread _mtl_combined_image_sampler_1d<S, A> tex,
+ vec<T, 1> texel,
+ uint lod,
+ vec<T, 1> offset = 0)
+{
+ float w = tex.texture->get_width();
+ if ((texel + offset) >= 0 && (texel + offset) < w) {
+ /* LODs not supported for 1d textures. This must be zero. */
+ return tex.texture->read(uint(texel + offset), 0);
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename S, typename T, int n, access A>
+inline vec<S, 4> _texelFetch_internal(thread _mtl_combined_image_sampler_1d<S, A> tex,
+ vec<T, n> texel,
+ uint lod,
+ vec<T, n> offset = vec<T, n>(0))
+{
+ float w = tex.texture->get_width();
+ if ((texel.x + offset.x) >= 0 && (texel.x + offset.x) < w) {
+ /* LODs not supported for 1d textures. This must be zero. */
+ return tex.texture->read(uint(texel.x + offset.x), 0);
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename S, typename T, access A>
+inline vec<S, 4> _texelFetch_internal(thread _mtl_combined_image_sampler_1d_array<S, A> tex,
+ vec<T, 2> texel,
+ uint lod,
+ vec<T, 2> offset = vec<T, 2>(0, 0))
+{
+
+ float w = tex.texture->get_width();
+ float h = tex.texture->get_array_size();
+ if ((texel.x + offset.x) >= 0 && (texel.x + offset.x) < w && (texel.y + offset.y) >= 0 &&
+ (texel.y + offset.y) < h) {
+ /* LODs not supported for 1d textures. This must be zero. */
+ return tex.texture->read(uint(texel.x + offset.x), uint(texel.y + offset.y), 0);
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename S, typename T, access A>
+inline vec<S, 4> _texelFetch_internal(thread _mtl_combined_image_sampler_2d<S, A> tex,
+ vec<T, 2> texel,
+ uint lod,
+ vec<T, 2> offset = vec<T, 2>(0))
+{
+
+ float w = tex.texture->get_width() >> lod;
+ float h = tex.texture->get_height() >> lod;
+ if ((texel.x + offset.x) >= 0 && (texel.x + offset.x) < w && (texel.y + offset.y) >= 0 &&
+ (texel.y + offset.y) < h) {
+ return tex.texture->read(uint2(texel + offset), lod);
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename S, typename T, access A>
+inline vec<S, 4> _texelFetch_internal(thread _mtl_combined_image_sampler_2d_array<S, A> tex,
+ vec<T, 3> texel,
+ uint lod,
+ vec<T, 3> offset = vec<T, 3>(0))
+{
+ float w = tex.texture->get_width() >> lod;
+ float h = tex.texture->get_height() >> lod;
+ float d = tex.texture->get_array_size();
+ if ((texel.x + offset.x) >= 0 && (texel.x + offset.x) < w && (texel.y + offset.y) >= 0 &&
+ (texel.y + offset.y) < h && (texel.z + offset.z) >= 0 && (texel.z + offset.z) < d) {
+ return tex.texture->read(uint2(texel.xy + offset.xy), uint(texel.z + offset.z), lod);
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename S, typename T, access A>
+inline vec<S, 4> _texelFetch_internal(thread _mtl_combined_image_sampler_3d<S, A> tex,
+ vec<T, 3> texel,
+ uint lod,
+ vec<T, 3> offset = vec<T, 3>(0))
+{
+
+ float w = tex.texture->get_width() >> lod;
+ float h = tex.texture->get_height() >> lod;
+ float d = tex.texture->get_depth() >> lod;
+ if ((texel.x + offset.x) >= 0 && (texel.x + offset.x) < w && (texel.y + offset.y) >= 0 &&
+ (texel.y + offset.y) < h && (texel.z + offset.z) >= 0 && (texel.z + offset.z) < d) {
+ return tex.texture->read(uint3(texel + offset), lod);
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+template<typename T, access A>
+inline _msl_return_float _texelFetch_internal(
+ thread _mtl_combined_image_sampler_depth_2d<float, A> tex,
+ vec<T, 2> texel,
+ uint lod,
+ vec<T, 2> offset = vec<T, 2>(0))
+{
+
+ float w = tex.texture->get_width() >> lod;
+ float h = tex.texture->get_height() >> lod;
+ if ((texel.x + offset.x) >= 0 && (texel.x + offset.x) < w && (texel.y + offset.y) >= 0 &&
+ (texel.y + offset.y) < h) {
+ _msl_return_float fl = {tex.texture->read(uint2(texel + offset), lod)};
+ return fl;
+ }
+ else {
+ _msl_return_float fl = {0};
+ return fl;
+ }
+}
+
+template<typename S, typename T, access A>
+inline vec<S, 4> _texture_internal_samp(thread _mtl_combined_image_sampler_2d_array<S, A> tex,
+ vec<T, 3> texel,
+ uint lod,
+ vec<T, 3> offset = vec<T, 3>(0))
+{
+
+ float w = tex.texture->get_width() >> lod;
+ float h = tex.texture->get_height() >> lod;
+ float d = tex.texture->get_array_size();
+ if ((texel.x + offset.x) >= 0 && (texel.x + offset.x) < w && (texel.y + offset.y) >= 0 &&
+ (texel.y + offset.y) < h && (texel.z + offset.z) >= 0 && (texel.z + offset.z) < d) {
+ return tex.texture->read(uint2(texel.xy + offset.xy), uint(texel.z + offset.z), lod);
+ }
+ else {
+ return vec<S, 4>(0);
+ }
+}
+
+/* Sample. */
+template<typename T>
+inline vec<T, 4> _texture_internal_samp(
+ thread _mtl_combined_image_sampler_1d<T, access::sample> tex, float u)
+{
+ return tex.texture->sample(*tex.samp, u);
+}
+
+inline float4 _texture_internal_samp(
+ thread _mtl_combined_image_sampler_1d_array<float, access::sample> tex, float2 ua)
+{
+ return tex.texture->sample(*tex.samp, ua.x, uint(ua.y));
+}
+
+inline int4 _texture_internal_samp(thread _mtl_combined_image_sampler_2d<int, access::sample> tex,
+ float2 uv)
+{
+ return tex.texture->sample(*tex.samp, uv);
+}
+
+inline uint4 _texture_internal_samp(
+ thread _mtl_combined_image_sampler_2d<uint, access::sample> tex, float2 uv)
+{
+ return tex.texture->sample(*tex.samp, uv);
+}
+
+inline float4 _texture_internal_samp(
+ thread _mtl_combined_image_sampler_2d<float, access::sample> tex, float2 uv)
+{
+ return tex.texture->sample(*tex.samp, uv);
+}
+
+inline _msl_return_float _texture_internal_samp(
+ thread _mtl_combined_image_sampler_depth_2d<float, access::sample> tex, float2 uv)
+{
+ _msl_return_float fl = {tex.texture->sample(*tex.samp, uv)};
+ return fl;
+}
+
+template<typename T>
+inline vec<T, 4> _texture_internal_samp(
+ thread _mtl_combined_image_sampler_3d<T, access::sample> tex, float3 uvw)
+{
+ return tex.texture->sample(*tex.samp, uvw);
+}
+
+template<typename T>
+inline vec<T, 4> _texture_internal_samp(
+ thread _mtl_combined_image_sampler_2d_array<T, access::sample> tex, float3 uva)
+{
+ return tex.texture->sample(*tex.samp, uva.xy, uint(uva.z));
+}
+
+inline _msl_return_float _texture_internal_samp(
+ thread _mtl_combined_image_sampler_depth_2d_array<float, access::sample> tex, float3 uva)
+{
+ _msl_return_float fl = {tex.texture->sample(*tex.samp, uva.xy, uint(uva.z))};
+ return fl;
+}
+
+inline _msl_return_float _texture_internal_samp(
+ thread _mtl_combined_image_sampler_depth_2d_array<float, access::sample> tex, float4 uvac)
+{
+ _msl_return_float fl = {
+ tex.texture->sample_compare(*tex.samp, uvac.xy, uint(uvac.z), uvac.w, level(0))};
+ return fl;
+}
+
+template<typename T>
+inline vec<T, 4> _texture_internal_samp(
+ thread _mtl_combined_image_sampler_cube<T, access::sample> tex, float3 uvs)
+{
+ return tex.texture->sample(*tex.samp, uvs.xyz);
+}
+
+template<typename T>
+inline vec<T, 4> _texture_internal_samp(
+ thread _mtl_combined_image_sampler_cube_array<T, access::sample> tex, float4 coord_a)
+{
+ return tex.texture->sample(*tex.samp, coord_a.xyz, uint(coord_a.w));
+}
+
+/* Sample Level. */
+template<typename T>
+inline vec<T, 4> _texture_internal_level(
+ thread _mtl_combined_image_sampler_1d<T, access::sample> tex,
+ float u,
+ level options,
+ int offset = 0)
+{
+ /* LODs not supported for 1d textures. This must be zero. */
+ return tex.texture->sample(*tex.samp, u);
+}
+
+inline float4 _texture_internal_level(
+ thread _mtl_combined_image_sampler_1d_array<float, access::sample> tex,
+ float2 ua,
+ level options,
+ int offset = 0)
+{
+ /* LODs not supported for 1d textures. This must be zero. */
+ return tex.texture->sample(*tex.samp, ua.x, uint(ua.y));
+}
+
+inline int4 _texture_internal_level(thread _mtl_combined_image_sampler_2d<int, access::sample> tex,
+ float2 uv,
+ level options,
+ int2 offset = int2(0))
+{
+ return tex.texture->sample(*tex.samp, uv, options, offset);
+}
+
+inline uint4 _texture_internal_level(
+ thread _mtl_combined_image_sampler_2d<uint, access::sample> tex,
+ float2 uv,
+ level options,
+ int2 offset = int2(0))
+{
+ return tex.texture->sample(*tex.samp, uv, options, offset);
+}
+
+inline float4 _texture_internal_level(
+ thread _mtl_combined_image_sampler_2d<float, access::sample> tex,
+ float2 uv,
+ level options,
+ int2 offset = int2(0))
+{
+ return tex.texture->sample(*tex.samp, uv, options, offset);
+}
+
+inline _msl_return_float _texture_internal_level(
+ thread _mtl_combined_image_sampler_depth_2d<float, access::sample> tex,
+ float2 uv,
+ level options,
+ int2 offset = int2(0))
+{
+ _msl_return_float fl = {tex.texture->sample(*tex.samp, uv, options, offset)};
+ return fl;
+}
+
+template<typename T>
+inline vec<T, 4> _texture_internal_level(
+ thread _mtl_combined_image_sampler_3d<T, access::sample> tex,
+ float3 uvw,
+ level options = level(0),
+ int3 offset = int3(0))
+{
+ return tex.texture->sample(*tex.samp, uvw, options, offset);
+}
+
+template<typename T>
+inline vec<T, 4> _texture_internal_level(
+ thread _mtl_combined_image_sampler_2d_array<T, access::sample> tex,
+ float3 uva,
+ level options = level(0),
+ int2 offset = int2(0))
+{
+ return tex.texture->sample(*tex.samp, uva.xy, uint(uva.z), options, offset);
+}
+
+inline _msl_return_float _texture_internal_level(
+ thread _mtl_combined_image_sampler_depth_2d_array<float, access::sample> tex,
+ float3 uva,
+ level options = level(0),
+ int2 offset = int2(0))
+{
+ _msl_return_float fl = {tex.texture->sample(*tex.samp, uva.xy, uint(uva.z), options, offset)};
+ return fl;
+}
+
+inline _msl_return_float _texture_internal_level(
+ thread _mtl_combined_image_sampler_depth_2d_array<float, access::sample> tex,
+ float4 uvac,
+ level options = level(0),
+ int2 offset = int2(0))
+{
+ _msl_return_float fl = {
+ tex.texture->sample_compare(*tex.samp, uvac.xy, uint(uvac.z), uvac.w, level(0), offset)};
+ return fl;
+}
+
+template<typename T>
+inline vec<T, 4> _texture_internal_level(
+ thread _mtl_combined_image_sampler_cube<T, access::sample> tex,
+ float3 uvs,
+ level options = level(0),
+ int2 offset = int2(0))
+{
+ return tex.texture->sample(*tex.samp, uvs.xyz, options);
+}
+
+template<typename T>
+inline vec<T, 4> _texture_internal_level(
+ thread _mtl_combined_image_sampler_cube_array<T, access::sample> tex,
+ float4 coord_a,
+ level options = level(0),
+ int3 offset = int3(0))
+{
+ return tex.texture->sample(*tex.samp, coord_a.xyz, uint(coord_a.w), options);
+}
+
+/* Sample Bias. */
+template<typename T>
+inline vec<T, 4> _texture_internal_bias(
+ thread _mtl_combined_image_sampler_1d<T, access::sample> tex,
+ float u,
+ bias options = bias(0.0),
+ int offset = 0)
+{
+ return tex.texture->sample(*tex.samp, u);
+}
+
+inline float4 _texture_internal_bias(
+ thread _mtl_combined_image_sampler_2d<float, access::sample> tex,
+ float2 uv,
+ bias options = bias(0.0),
+ int2 offset = int2(0))
+{
+ return tex.texture->sample(*tex.samp, uv, options, offset);
+}
+
+inline _msl_return_float _texture_internal_bias(
+ thread _mtl_combined_image_sampler_depth_2d<float, access::sample> tex,
+ float2 uv,
+ bias options = bias(0),
+ int2 offset = int2(0))
+{
+ _msl_return_float fl = {tex.texture->sample(*tex.samp, uv, options, offset)};
+ return fl;
+}
+
+/* Texture Gather. */
+component int_to_component(const int comp)
+{
+ switch (comp) {
+ default:
+ case 0:
+ return component::x;
+ case 1:
+ return component::y;
+ case 2:
+ return component::z;
+ case 3:
+ return component::w;
+ }
+ return component::x;
+}
+
+inline float4 _texture_gather_internal(
+ thread _mtl_combined_image_sampler_depth_2d<float, access::sample> tex,
+ float2 uv,
+ const int comp = 0,
+ int2 offset = int2(0))
+{
+ return tex.texture->gather(*tex.samp, uv, offset);
+}
+
+template<typename T>
+inline vec<T, 4> _texture_gather_internal(
+ thread _mtl_combined_image_sampler_2d<T, access::sample> tex,
+ float2 uv,
+ const int comp = 0,
+ int2 offset = int2(0))
+{
+ return tex.texture->gather(*tex.samp, uv, offset);
+}
+
+template<typename T>
+inline vec<T, 4> _texture_gather_internal(
+ thread _mtl_combined_image_sampler_2d_array<T, access::sample> tex,
+ float2 uv,
+ const int comp = 0,
+ int2 offset = int2(0))
+{
+ return tex.texture->gather(*tex.samp, uv, offset);
+}
+
+/* Texture write support. */
+template<typename S, typename T, access A>
+inline void _texture_write_internal(thread _mtl_combined_image_sampler_2d<S, A> tex,
+ T _coord,
+ vec<S, 4> value)
+{
+ float w = tex.texture->get_width();
+ float h = tex.texture->get_height();
+ if (_coord.x >= 0 && _coord.x < w && _coord.y >= 0 && _coord.y < h) {
+ tex.texture->write(value, uint2(_coord.xy));
+ }
+}
+
+template<typename S, typename T, access A>
+inline void _texture_write_internal(thread _mtl_combined_image_sampler_3d<S, A> tex,
+ T _coord,
+ vec<S, 4> value)
+{
+ float w = tex.texture->get_width();
+ float h = tex.texture->get_height();
+ float d = tex.texture->get_depth();
+ if (_coord.x >= 0 && _coord.x < w && _coord.y >= 0 && _coord.y < h && _coord.z >= 0 &&
+ _coord.z < d) {
+ tex.texture->write(value, uint3(_coord.xyz));
+ }
+}
+
+/* SSBO Vertex Fetch Mode. */
+#ifdef MTL_SSBO_VERTEX_FETCH
+/* Enabled when geometry is passed via raw buffer bindings, rather than using
+ * vertex assembly in the vertex-descriptor.
+ *
+ * To describe the layout of input attribute data, we will generate uniforms (defaulting to 0)
+ * with the names per unique input attribute with name `attr`:
+ *
+ * - uniform_ssbo_stride_##attr -- Representing the stride between element.
+ * - uniform_ssbo_offset_##attr -- Representing the base offset within the vertex.
+ * - uniform_ssbo_fetchmode_##attr - Whether using per-vertex (=0) or per-instance fetch (=1).
+ * - uniform_ssbo_vbo_id_##attr - buffer binding index for VBO with data for this attribute.
+ * - uniform_ssbo_type_##attr - The type of data in the currently bound buffer.
+ *
+ * If the uniform_ssbo_type_* does not match with the desired type, then it is the responsibility
+ * of the shader to perform the conversion. Types should always be read as the raw attribute type,
+ * and then converted. e.g. If the uniform_ssbo_type_* is `int`, but we want to read it to be
+ * normalized to a float.
+ * The implementation should query the attribute type using vertex_fetch_get_attr_type(attr_name):
+ *
+ * float fweight = 0.0;
+ * if(vertex_fetch_get_attr_type(in_weight) == GPU_SHADER_ATTR_TYPE_INT) {
+ * int iweight = vertex_fetch_attribute(gl_VertexID, in_weight, int);
+ * fweight = (float)iweight/(float)INT32_MAX;
+ * } else {
+ * fweight = = vertex_fetch_attribute(gl_VertexID, in_weight, float);
+ * }
+ *
+ * Note: These uniforms are generated as part of the same data block used for regular uniforms
+ * and attribute data is written prior to each draw call, depending on the configuration of
+ * the vertex descriptor for an MTLBatch or MTLImmedaite call. */
+# define PPCAT_NX(A, B) A##B
+# define PPCAT(A, B) PPCAT_NX(A, B)
+
+# define RESOLVE_VERTEX(v_id) \
+ ((UNIFORM_SSBO_USES_INDEXED_RENDERING_STR > 0) ? \
+ ((UNIFORM_SSBO_INDEX_MODE_U16_STR > 0) ? MTL_INDEX_DATA_U16[v_id] : \
+ MTL_INDEX_DATA_U32[v_id]) : \
+ v_id)
+# define ATTR_TYPE(attr) PPCAT(SSBO_ATTR_TYPE_, attr)
+# define vertex_fetch_attribute_raw(n, attr, type) \
+ (reinterpret_cast<constant type *>( \
+ &MTL_VERTEX_DATA[PPCAT(UNIFORM_SSBO_VBO_ID_STR, attr)] \
+ [(PPCAT(UNIFORM_SSBO_STRIDE_STR, attr) * \
+ ((PPCAT(UNIFORM_SSBO_FETCHMODE_STR, attr)) ? gl_InstanceID : n)) + \
+ PPCAT(UNIFORM_SSBO_OFFSET_STR, attr)]))[0]
+# define vertex_fetch_attribute(n, attr, type) \
+ vertex_fetch_attribute_raw(RESOLVE_VERTEX(n), attr, type)
+# define vertex_id_from_index_id(n) RESOLVE_VERTEX(n)
+# define vertex_fetch_get_input_prim_type() UNIFORM_SSBO_INPUT_PRIM_TYPE_STR
+# define vertex_fetch_get_input_vert_count() UNIFORM_SSBO_INPUT_VERT_COUNT_STR
+# define vertex_fetch_get_attr_type(attr) PPCAT(UNIFORM_SSBO_TYPE_STR, attr)
+
+/* Must mirror GPU_primitive.h. */
+# define GPU_PRIM_POINTS 0
+# define GPU_PRIM_LINES 1
+# define GPU_PRIM_TRIS 2
+# define GPU_PRIM_LINE_STRIP 3
+# define GPU_PRIM_LINE_LOOP 4
+# define GPU_PRIM_TRI_STRIP 5
+# define GPU_PRIM_TRI_FAN 6
+# define GPU_PRIM_LINES_ADJ 7
+# define GPU_PRIM_TRIS_ADJ 8
+# define GPU_PRIM_LINE_STRIP_ADJ 9
+#endif
+
+/* Common Functions. */
+#define dFdx(x) dfdx(x)
+#define dFdy(x) dfdy(x)
+#define mod(x, y) _mtlmod(x, y)
+#define discard discard_fragment()
+#define inversesqrt rsqrt
+
+inline float radians(float deg)
+{
+ /* Constant factor: M_PI_F/180.0. */
+ return deg * 0.01745329251f;
+}
+
+inline float degrees(float rad)
+{
+ /* Constant factor: 180.0/M_PI_F. */
+ return rad * 57.2957795131;
+}
+
+#define select(A, B, C) mix(A, B, C)
+
+/* Type conversions and type truncations. */
+inline float4 to_float4(float3 val)
+{
+ return float4(val, 1.0);
+}
+
+/* Type conversions and type truncations (Utility Functions). */
+inline float3x3 mat4_to_mat3(float4x4 matrix)
+{
+ return float3x3(matrix[0].xyz, matrix[1].xyz, matrix[2].xyz);
+}
+
+inline int floatBitsToInt(float f)
+{
+ return as_type<int>(f);
+}
+
+inline int2 floatBitsToInt(float2 f)
+{
+ return as_type<int2>(f);
+}
+
+inline int3 floatBitsToInt(float3 f)
+{
+ return as_type<int3>(f);
+}
+
+inline int4 floatBitsToInt(float4 f)
+{
+ return as_type<int4>(f);
+}
+
+inline uint floatBitsToUint(float f)
+{
+ return as_type<uint>(f);
+}
+
+inline uint2 floatBitsToUint(float2 f)
+{
+ return as_type<uint2>(f);
+}
+
+inline uint3 floatBitsToUint(float3 f)
+{
+ return as_type<uint3>(f);
+}
+
+inline uint4 floatBitsToUint(float4 f)
+{
+ return as_type<uint4>(f);
+}
+
+inline float intBitsToFloat(int f)
+{
+ return as_type<float>(f);
+}
+
+inline float2 intBitsToFloat(int2 f)
+{
+ return as_type<float2>(f);
+}
+
+inline float3 intBitsToFloat(int3 f)
+{
+ return as_type<float3>(f);
+}
+
+inline float4 intBitsToFloat(int4 f)
+{
+ return as_type<float4>(f);
+}
+
+/* Texture size functions. Add texture types as needed. */
+template<typename T, access A>
+int textureSize(thread _mtl_combined_image_sampler_1d<T, A> image, uint lod)
+{
+ return int(image.texture->get_width());
+}
+
+template<typename T, access A>
+int2 textureSize(thread _mtl_combined_image_sampler_1d_array<T, A> image, uint lod)
+{
+ return int2(image.texture->get_width(), image.texture->get_array_size());
+}
+
+template<typename T, access A>
+int2 textureSize(thread _mtl_combined_image_sampler_2d<T, A> image, uint lod)
+{
+ return int2(image.texture->get_width(lod), image.texture->get_height(lod));
+}
+
+template<typename T, access A>
+int2 textureSize(thread _mtl_combined_image_sampler_depth_2d<T, A> image, uint lod)
+{
+ return int2(image.texture->get_width(lod), image.texture->get_height(lod));
+}
+
+template<typename T, access A>
+int3 textureSize(thread _mtl_combined_image_sampler_2d_array<T, A> image, uint lod)
+{
+ return int3(image.texture->get_width(lod),
+ image.texture->get_height(lod),
+ image.texture->get_array_size());
+}
+
+template<typename T, access A>
+int3 textureSize(thread _mtl_combined_image_sampler_depth_2d_array<T, A> image, uint lod)
+{
+ return int3(image.texture->get_width(lod),
+ image.texture->get_height(lod),
+ image.texture->get_array_size());
+}
+
+template<typename T, access A>
+int2 textureSize(thread _mtl_combined_image_sampler_cube<T, A> image, uint lod)
+{
+ return int2(image.texture->get_width(lod), image.texture->get_height(lod));
+}
+
+template<typename T, access A>
+int3 textureSize(thread _mtl_combined_image_sampler_3d<T, A> image, uint lod)
+{
+ return int3(image.texture->get_width(lod),
+ image.texture->get_height(lod),
+ image.texture->get_depth(lod));
+}
+
+/* Equality and comparison functions. */
+#define lessThan(a, b) ((a) < (b))
+#define lessThanEqual(a, b) ((a) <= (b))
+#define greaterThan(a, b) ((a) > (b))
+#define greaterThanEqual(a, b) ((a) >= (b))
+#define equal(a, b) ((a) == (b))
+#define notEqual(a, b) ((a) != (b))
+
+template<typename T, int n> bool all(vec<T, n> x)
+{
+ bool _all = true;
+ for (int i = 0; i < n; i++) {
+ _all = _all && (x[i] > 0);
+ }
+ return _all;
+}
+
+template<typename T, int n> bool any(vec<T, n> x)
+{
+ bool _any = false;
+ for (int i = 0; i < n; i++) {
+ _any = _any || (x[i] > 0);
+ }
+ return _any;
+}
+
+/* Modulo functionality. */
+int _mtlmod(int a, int b)
+{
+ return a - b * (a / b);
+}
+
+template<typename T, int n> vec<T, n> _mtlmod(vec<T, n> x, vec<T, n> y)
+{
+ return x - y * floor(x / y);
+}
+
+template<typename T, int n, typename U> vec<T, n> _mtlmod(vec<T, n> x, U y)
+{
+ return x - vec<T, n>(y) * floor(x / vec<T, n>(y));
+}
+
+template<typename T, typename U, int n> vec<U, n> _mtlmod(T x, vec<U, n> y)
+{
+ return vec<U, n>(x) - y * floor(vec<U, n>(x) / y);
+}
+
+/* Mathematical functions. */
+template<typename T> T atan(T y, T x)
+{
+ return atan2(y, x);
+}
+
+/* Matrix Inverse. */
+float4x4 inverse(float4x4 a)
+{
+ float b00 = a[0][0] * a[1][1] - a[0][1] * a[1][0];
+ float b01 = a[0][0] * a[1][2] - a[0][2] * a[1][0];
+ float b02 = a[0][0] * a[1][3] - a[0][3] * a[1][0];
+ float b03 = a[0][1] * a[1][2] - a[0][2] * a[1][1];
+ float b04 = a[0][1] * a[1][3] - a[0][3] * a[1][1];
+ float b05 = a[0][2] * a[1][3] - a[0][3] * a[1][2];
+ float b06 = a[2][0] * a[3][1] - a[2][1] * a[3][0];
+ float b07 = a[2][0] * a[3][2] - a[2][2] * a[3][0];
+ float b08 = a[2][0] * a[3][3] - a[2][3] * a[3][0];
+ float b09 = a[2][1] * a[3][2] - a[2][2] * a[3][1];
+ float b10 = a[2][1] * a[3][3] - a[2][3] * a[3][1];
+ float b11 = a[2][2] * a[3][3] - a[2][3] * a[3][2];
+
+ float invdet = 1.0 / (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06);
+
+ return float4x4(a[1][1] * b11 - a[1][2] * b10 + a[1][3] * b09,
+ a[0][2] * b10 - a[0][1] * b11 - a[0][3] * b09,
+ a[3][1] * b05 - a[3][2] * b04 + a[3][3] * b03,
+ a[2][2] * b04 - a[2][1] * b05 - a[2][3] * b03,
+ a[1][2] * b08 - a[1][0] * b11 - a[1][3] * b07,
+ a[0][0] * b11 - a[0][2] * b08 + a[0][3] * b07,
+ a[3][2] * b02 - a[3][0] * b05 - a[3][3] * b01,
+ a[2][0] * b05 - a[2][2] * b02 + a[2][3] * b01,
+ a[1][0] * b10 - a[1][1] * b08 + a[1][3] * b06,
+ a[0][1] * b08 - a[0][0] * b10 - a[0][3] * b06,
+ a[3][0] * b04 - a[3][1] * b02 + a[3][3] * b00,
+ a[2][1] * b02 - a[2][0] * b04 - a[2][3] * b00,
+ a[1][1] * b07 - a[1][0] * b09 - a[1][2] * b06,
+ a[0][0] * b09 - a[0][1] * b07 + a[0][2] * b06,
+ a[3][1] * b01 - a[3][0] * b03 - a[3][2] * b00,
+ a[2][0] * b03 - a[2][1] * b01 + a[2][2] * b00) *
+ invdet;
+}
+
+float3x3 inverse(float3x3 m)
+{
+
+ float invdet = 1.0 / (m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) -
+ m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]) +
+ m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]));
+
+ float3x3 inverse(0);
+ inverse[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]);
+ inverse[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]);
+ inverse[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]);
+ inverse[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]);
+ inverse[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]);
+ inverse[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]);
+ inverse[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]);
+ inverse[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]);
+ inverse[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]);
+ inverse = inverse * invdet;
+
+ return inverse;
+}
+
+/* Additional overloads for builtin functions. */
+float distance(float x, float y)
+{
+ return abs(y - x);
+}
+
+/* Overload for mix(A, B, float ratio). */
+template<typename T, int Size> vec<T, Size> mix(vec<T, Size> a, vec<T, Size> b, float val)
+{
+ return mix(a, b, vec<T, Size>(val));
+}
+
+/* Overload for mix(A, B, bvec<N>). */
+template<typename T, int Size>
+vec<T, Size> mix(vec<T, Size> a, vec<T, Size> b, vec<int, Size> mask)
+{
+ vec<T, Size> result;
+ for (int i = 0; i < Size; i++) {
+ result[i] = mask[i] ? b[i] : a[i];
+ }
+ return result;
+}
+
+/* Using vec<bool, S> does not appear to work, splitting cases. */
+/* Overload for mix(A, B, bvec<N>). */
+template<typename T> vec<T, 4> mix(vec<T, 4> a, vec<T, 4> b, bvec4 mask)
+{
+ vec<T, 4> result;
+ for (int i = 0; i < 4; i++) {
+ result[i] = mask[i] ? b[i] : a[i];
+ }
+ return result;
+}
+
+/* Overload for mix(A, B, bvec<N>). */
+template<typename T> vec<T, 3> mix(vec<T, 3> a, vec<T, 3> b, bvec3 mask)
+{
+ vec<T, 3> result;
+ for (int i = 0; i < 3; i++) {
+ result[i] = mask[i] ? b[i] : a[i];
+ }
+ return result;
+}
+
+/* Overload for mix(A, B, bvec<N>). */
+template<typename T> vec<T, 2> mix(vec<T, 2> a, vec<T, 2> b, bvec2 mask)
+{
+ vec<T, 2> result;
+ for (int i = 0; i < 2; i++) {
+ result[i] = mask[i] ? b[i] : a[i];
+ }
+ return result;
+}
+
+/* Overload for mix(A, B, bvec<N>). */
+template<typename T> T mix(T a, T b, MTLBOOL mask)
+{
+ return (mask) ? b : a;
+}
+
+template<typename T, unsigned int Size> bool is_zero(vec<T, Size> a)
+{
+ for (int i = 0; i < Size; i++) {
+ if (a[i] != T(0)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Matrix conversion fallback. */
+mat3 MAT3(vec3 a, vec3 b, vec3 c)
+{
+ return mat3(a, b, c);
+}
+mat3 MAT3(float f)
+{
+ return mat3(f);
+}
+mat3 MAT3(mat4 m)
+{
+ return mat4_to_mat3(m);
+} \ No newline at end of file
diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc
index 5dc70a8bf0f..567f1370f00 100644
--- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc
+++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc
@@ -32,12 +32,8 @@ static void test_shader_builtin()
test_compile_builtin_shader(GPU_SHADER_TEXT, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_KEYFRAME_SHAPE, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_SIMPLE_LIGHTING, GPU_SHADER_CFG_DEFAULT);
- test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT);
- test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT);
- test_compile_builtin_shader(GPU_SHADER_2D_SMOOTH_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_3D_IMAGE, GPU_SHADER_CFG_DEFAULT);
- test_compile_builtin_shader(GPU_SHADER_2D_IMAGE, GPU_SHADER_CFG_DEFAULT);
- test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT);
+ test_compile_builtin_shader(GPU_SHADER_3D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_DESATURATE_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR, GPU_SHADER_CFG_DEFAULT);
@@ -60,7 +56,6 @@ static void test_shader_builtin()
GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR,
GPU_SHADER_CFG_DEFAULT);
- test_compile_builtin_shader(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_GPENCIL_STROKE, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_AREA_BORDERS, GPU_SHADER_CFG_DEFAULT);
test_compile_builtin_shader(GPU_SHADER_2D_WIDGET_BASE, GPU_SHADER_CFG_DEFAULT);
diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc
index adb9b059fc6..35ffc647c97 100644
--- a/source/blender/gpu/tests/gpu_shader_test.cc
+++ b/source/blender/gpu/tests/gpu_shader_test.cc
@@ -14,8 +14,6 @@
#include "gpu_testing.hh"
-#include "GPU_glew.h"
-
namespace blender::gpu::tests {
static void test_gpu_shader_compute_2d()
@@ -48,7 +46,7 @@ void main() {
/* 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);
+ "gpu_shader_compute_2d", SIZE, SIZE, 1, GPU_RGBA32F, nullptr);
EXPECT_NE(texture, nullptr);
GPU_shader_bind(shader);
@@ -109,7 +107,7 @@ void main() {
/* Construct Texture. */
GPUTexture *texture = GPU_texture_create_1d(
- "gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, nullptr);
+ "gpu_shader_compute_1d", SIZE, 1, GPU_RGBA32F, nullptr);
EXPECT_NE(texture, nullptr);
GPU_shader_bind(shader);
diff --git a/source/blender/gpu/tests/gpu_testing.cc b/source/blender/gpu/tests/gpu_testing.cc
index 4e93e062b50..224a9afcf59 100644
--- a/source/blender/gpu/tests/gpu_testing.cc
+++ b/source/blender/gpu/tests/gpu_testing.cc
@@ -26,7 +26,6 @@ void GPUTest::SetUp()
void GPUTest::TearDown()
{
GPU_exit();
- GPU_backend_exit();
GPU_context_discard(context);
GHOST_DisposeOpenGLContext(ghost_system, ghost_context);
GHOST_DisposeSystem(ghost_system);
diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h
index 0818dd653a1..2fb1d814c83 100644
--- a/source/blender/imbuf/IMB_colormanagement.h
+++ b/source/blender/imbuf/IMB_colormanagement.h
@@ -56,6 +56,8 @@ bool IMB_colormanagement_space_name_is_data(const char *name);
bool IMB_colormanagement_space_name_is_scene_linear(const char *name);
bool IMB_colormanagement_space_name_is_srgb(const char *name);
+BLI_INLINE void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3]);
+
/**
* Convert a float RGB triplet to the correct luminance weighted average.
*
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 20c414bb1ad..7e652e31506 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -41,6 +41,7 @@
/* for bool */
#include "../blenlib/BLI_sys_types.h"
+#include "../gpu/GPU_texture.h"
#ifdef __cplusplus
extern "C" {
@@ -49,7 +50,6 @@ extern "C" {
#define IM_MAX_SPACE 64
/**
- *
* \attention defined in ???
*/
struct ImBuf;
@@ -57,7 +57,6 @@ struct rctf;
struct rcti;
/**
- *
* \attention defined in ???
*/
struct anim;
@@ -66,27 +65,18 @@ struct ColorManagedDisplay;
struct GSet;
/**
- *
* \attention defined in DNA_scene_types.h
*/
struct ImageFormatData;
struct Stereo3dFormat;
/**
- *
- * \attention defined in GPU_texture.h
- */
-struct GPUTexture;
-
-/**
- *
* \attention Defined in allocimbuf.c
*/
void IMB_init(void);
void IMB_exit(void);
/**
- *
* \attention Defined in readimage.c
*/
struct ImBuf *IMB_ibImageFromMemory(const unsigned char *mem,
@@ -96,19 +86,16 @@ struct ImBuf *IMB_ibImageFromMemory(const unsigned char *mem,
const char *descr);
/**
- *
* \attention Defined in readimage.c
*/
struct ImBuf *IMB_testiffname(const char *filepath, int flags);
/**
- *
* \attention Defined in readimage.c
*/
struct ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE]);
/**
- *
* \attention Defined in readimage.c
*/
struct ImBuf *IMB_thumb_load_image(const char *filepath,
@@ -116,13 +103,11 @@ struct ImBuf *IMB_thumb_load_image(const char *filepath,
char colorspace[IM_MAX_SPACE]);
/**
- *
* \attention Defined in allocimbuf.c
*/
void IMB_freeImBuf(struct ImBuf *ibuf);
/**
- *
* \attention Defined in allocimbuf.c
*/
struct ImBuf *IMB_allocImBuf(unsigned int x,
@@ -159,7 +144,6 @@ struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect,
unsigned int channels);
/**
- *
* Increase reference count to imbuf
* (to delete an imbuf you have to call freeImBuf as many times as it
* is referenced)
@@ -171,13 +155,11 @@ void IMB_refImBuf(struct ImBuf *ibuf);
struct ImBuf *IMB_makeSingleUser(struct ImBuf *ibuf);
/**
- *
* \attention Defined in allocimbuf.c
*/
struct ImBuf *IMB_dupImBuf(const struct ImBuf *ibuf1);
/**
- *
* \attention Defined in allocimbuf.c
*/
bool addzbufImBuf(struct ImBuf *ibuf);
@@ -202,7 +184,6 @@ size_t IMB_get_size_in_memory(struct ImBuf *ibuf);
size_t IMB_get_rect_len(const struct ImBuf *ibuf);
/**
- *
* \attention Defined in rectop.c
*/
@@ -309,7 +290,6 @@ void IMB_rectblend_threaded(struct ImBuf *dbuf,
bool accumulate);
/**
- *
* \attention Defined in indexer.c
*/
@@ -404,7 +384,6 @@ double IMD_anim_get_offset(struct anim *anim);
bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bool no_av_base);
/**
- *
* \attention Defined in anim_movie.c
*/
struct anim *IMB_open_anim(const char *name,
@@ -417,7 +396,6 @@ void IMB_close_anim_proxies(struct anim *anim);
bool IMB_anim_can_produce_frames(const struct anim *anim);
/**
- *
* \attention Defined in anim_movie.c
*/
@@ -427,7 +405,6 @@ int IMB_anim_get_image_height(struct anim *anim);
bool IMB_get_gop_decode_time(struct anim *anim);
/**
- *
* \attention Defined in anim_movie.c
*/
@@ -437,20 +414,17 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
IMB_Proxy_Size preview_size /* = 0 = IMB_PROXY_NONE */);
/**
- *
* \attention Defined in anim_movie.c
* fetches a define preview-frame, usually half way into the movie.
*/
struct ImBuf *IMB_anim_previewframe(struct anim *anim);
/**
- *
* \attention Defined in anim_movie.c
*/
void IMB_free_anim(struct anim *anim);
/**
- *
* \attention Defined in filter.c
*/
@@ -479,7 +453,6 @@ void IMB_remakemipmap(struct ImBuf *ibuf, int use_filter);
struct ImBuf *IMB_getmipmap(struct ImBuf *ibuf, int level);
/**
- *
* \attention Defined in cache.c
*/
@@ -491,19 +464,16 @@ unsigned int *IMB_gettile(struct ImBuf *ibuf, int tx, int ty, int thread);
void IMB_tiles_to_rect(struct ImBuf *ibuf);
/**
- *
* \attention Defined in filter.c
*/
void IMB_filtery(struct ImBuf *ibuf);
/**
- *
* \attention Defined in scaling.c
*/
struct ImBuf *IMB_onehalf(struct ImBuf *ibuf1);
/**
- *
* \attention Defined in scaling.c
*
* Return true if \a ibuf is modified.
@@ -511,7 +481,6 @@ struct ImBuf *IMB_onehalf(struct ImBuf *ibuf1);
bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy);
/**
- *
* \attention Defined in scaling.c
*/
/**
@@ -520,19 +489,16 @@ bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy);
bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy);
/**
- *
* \attention Defined in scaling.c
*/
void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int newy);
/**
- *
* \attention Defined in writeimage.c
*/
bool IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags);
/**
- *
* \attention Defined in util.c
*/
bool IMB_ispic(const char *filepath);
@@ -541,13 +507,11 @@ int IMB_ispic_type_from_memory(const unsigned char *buf, size_t buf_size);
int IMB_ispic_type(const char *filepath);
/**
- *
* \attention Defined in util.c
*/
bool IMB_isanim(const char *filepath);
/**
- *
* \attention Defined in util.c
*/
int imb_get_anim_type(const char *filepath);
@@ -672,7 +636,6 @@ void IMB_buffer_float_premultiply(float *buf, int width, int height);
void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf);
/**
- *
* \attention defined in imageprocess.c
*/
@@ -723,50 +686,42 @@ void IMB_sampleImageAtLocation(
struct ImBuf *ibuf, float x, float y, bool make_linear_rgb, float color[4]);
/**
- *
* \attention defined in readimage.c
*/
struct ImBuf *IMB_loadifffile(
int file, const char *filepath, int flags, char colorspace[IM_MAX_SPACE], const char *descr);
/**
- *
* \attention defined in scaling.c
*/
struct ImBuf *IMB_half_x(struct ImBuf *ibuf1);
/**
- *
* \attention defined in scaling.c
*/
struct ImBuf *IMB_double_fast_x(struct ImBuf *ibuf1);
/**
- *
* \attention defined in scaling.c
*/
struct ImBuf *IMB_double_x(struct ImBuf *ibuf1);
/**
- *
* \attention defined in scaling.c
*/
struct ImBuf *IMB_half_y(struct ImBuf *ibuf1);
/**
- *
* \attention defined in scaling.c
*/
struct ImBuf *IMB_double_fast_y(struct ImBuf *ibuf1);
/**
- *
* \attention defined in scaling.c
*/
struct ImBuf *IMB_double_y(struct ImBuf *ibuf1);
/**
- *
* \attention Defined in rotate.c
*/
void IMB_flipx(struct ImBuf *ibuf);
@@ -778,14 +733,12 @@ void IMB_premultiply_alpha(struct ImBuf *ibuf);
void IMB_unpremultiply_alpha(struct ImBuf *ibuf);
/**
- *
* \attention Defined in allocimbuf.c
*/
void IMB_freezbufImBuf(struct ImBuf *ibuf);
void IMB_freezbuffloatImBuf(struct ImBuf *ibuf);
/**
- *
* \attention Defined in rectop.c
*/
/**
@@ -856,7 +809,7 @@ bool imb_addrectImBuf(struct ImBuf *ibuf);
*/
void imb_freerectImBuf(struct ImBuf *ibuf);
-bool imb_addrectfloatImBuf(struct ImBuf *ibuf);
+bool imb_addrectfloatImBuf(struct ImBuf *ibuf, const unsigned int channels);
/**
* Any free `ibuf->rect` frees mipmaps to be sure, creation is in render on first request.
*/
@@ -930,25 +883,35 @@ void IMB_ffmpeg_init(void);
const char *IMB_ffmpeg_last_error(void);
/**
- *
* \attention defined in util_gpu.c
*/
-struct GPUTexture *IMB_create_gpu_texture(const char *name,
- struct ImBuf *ibuf,
- bool use_high_bitdepth,
- bool use_premult);
+GPUTexture *IMB_create_gpu_texture(const char *name,
+ struct ImBuf *ibuf,
+ bool use_high_bitdepth,
+ bool use_premult);
+
+eGPUTextureFormat IMB_gpu_get_texture_format(const struct ImBuf *ibuf,
+ bool high_bitdepth,
+ bool use_grayscale);
+
/**
* The `ibuf` is only here to detect the storage type. The produced texture will have undefined
* content. It will need to be populated by using #IMB_update_gpu_texture_sub().
*/
-struct GPUTexture *IMB_touch_gpu_texture(
- const char *name, struct ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth);
+GPUTexture *IMB_touch_gpu_texture(const char *name,
+ struct ImBuf *ibuf,
+ int w,
+ int h,
+ int layers,
+ bool use_high_bitdepth,
+ bool use_grayscale);
+
/**
* Will update a #GPUTexture using the content of the #ImBuf. Only one layer will be updated.
* Will resize the ibuf if needed.
* Z is the layer to update. Unused if the texture is 2D.
*/
-void IMB_update_gpu_texture_sub(struct GPUTexture *tex,
+void IMB_update_gpu_texture_sub(GPUTexture *tex,
struct ImBuf *ibuf,
int x,
int y,
@@ -956,6 +919,7 @@ void IMB_update_gpu_texture_sub(struct GPUTexture *tex,
int w,
int h,
bool use_high_bitdepth,
+ bool use_grayscale,
bool use_premult);
/**
diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h
index 1b32bef0a98..81e9420c8ba 100644
--- a/source/blender/imbuf/IMB_imbuf_types.h
+++ b/source/blender/imbuf/IMB_imbuf_types.h
@@ -125,7 +125,7 @@ enum eImbFileType {
typedef struct ImbFormatOptions {
short flag;
- /** quality serves dual purpose as quality number for jpeg or compression amount for png */
+ /** Quality serves dual purpose as quality number for JPEG or compression amount for PNG. */
char quality;
} ImbFormatOptions;
@@ -166,8 +166,6 @@ typedef enum eImBufFlags {
* \{ */
typedef struct ImBuf {
- struct ImBuf *next, *prev; /** < allow lists of #ImBufs, for caches or flip-books. */
-
/* dimensions */
/** Width and Height of our image buffer.
* Should be 'unsigned int' since most formats use this.
@@ -251,11 +249,11 @@ typedef struct ImBuf {
int refcounter;
/* some parameters to pass along for packing images */
- /** Compressed image only used with png and exr currently */
+ /** Compressed image only used with PNG and EXR currently. */
unsigned char *encodedbuffer;
- /** Size of data written to encodedbuffer */
+ /** Size of data written to `encodedbuffer`. */
unsigned int encodedsize;
- /** Size of encodedbuffer */
+ /** Size of `encodedbuffer` */
unsigned int encodedbuffersize;
/* color management */
diff --git a/source/blender/imbuf/intern/IMB_filetype.h b/source/blender/imbuf/intern/IMB_filetype.h
index 9a0a6998fab..bd17316d173 100644
--- a/source/blender/imbuf/intern/IMB_filetype.h
+++ b/source/blender/imbuf/intern/IMB_filetype.h
@@ -264,6 +264,12 @@ struct ImBuf *imb_loadwebp(const unsigned char *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE]);
+struct ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath,
+ const int flags,
+ const size_t max_thumb_size,
+ char colorspace[],
+ size_t *r_width,
+ size_t *r_height);
bool imb_savewebp(struct ImBuf *ibuf, const char *name, int flags);
/** \} */
diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c
index 8b9ad94de0c..42b587c3c81 100644
--- a/source/blender/imbuf/intern/allocimbuf.c
+++ b/source/blender/imbuf/intern/allocimbuf.c
@@ -258,7 +258,7 @@ bool addzbufImBuf(ImBuf *ibuf)
IMB_freezbufImBuf(ibuf);
- if ((ibuf->zbuf = imb_alloc_pixels(ibuf->x, ibuf->y, 1, sizeof(unsigned int), __func__))) {
+ if ((ibuf->zbuf = imb_alloc_pixels(ibuf->x, ibuf->y, 1, sizeof(uint), __func__))) {
ibuf->mall |= IB_zbuf;
ibuf->flags |= IB_zbuf;
return true;
@@ -309,7 +309,7 @@ bool imb_addencodedbufferImBuf(ImBuf *ibuf)
bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf)
{
- unsigned int newsize, encodedsize;
+ uint newsize, encodedsize;
void *newbuffer;
if (ibuf == NULL) {
@@ -351,8 +351,7 @@ bool imb_enlargeencodedbufferImBuf(ImBuf *ibuf)
return true;
}
-void *imb_alloc_pixels(
- unsigned int x, unsigned int y, unsigned int channels, size_t typesize, const char *name)
+void *imb_alloc_pixels(uint x, uint y, uint channels, size_t typesize, const char *name)
{
/* Protect against buffer overflow vulnerabilities from files specifying
* a width and height that overflow and alloc too little memory. */
@@ -364,7 +363,7 @@ void *imb_alloc_pixels(
return MEM_callocN(size, name);
}
-bool imb_addrectfloatImBuf(ImBuf *ibuf)
+bool imb_addrectfloatImBuf(ImBuf *ibuf, const uint channels)
{
if (ibuf == NULL) {
return false;
@@ -374,8 +373,8 @@ bool imb_addrectfloatImBuf(ImBuf *ibuf)
imb_freerectfloatImBuf(ibuf); /* frees mipmap too, hrm */
}
- ibuf->channels = 4;
- if ((ibuf->rect_float = imb_alloc_pixels(ibuf->x, ibuf->y, 4, sizeof(float), __func__))) {
+ ibuf->channels = channels;
+ if ((ibuf->rect_float = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(float), __func__))) {
ibuf->mall |= IB_rectfloat;
ibuf->flags |= IB_rectfloat;
return true;
@@ -399,7 +398,7 @@ bool imb_addrectImBuf(ImBuf *ibuf)
}
ibuf->rect = NULL;
- if ((ibuf->rect = imb_alloc_pixels(ibuf->x, ibuf->y, 4, sizeof(unsigned char), __func__))) {
+ if ((ibuf->rect = imb_alloc_pixels(ibuf->x, ibuf->y, 4, sizeof(uchar), __func__))) {
ibuf->mall |= IB_rect;
ibuf->flags |= IB_rect;
if (ibuf->planes > 32) {
@@ -412,8 +411,7 @@ bool imb_addrectImBuf(ImBuf *ibuf)
return false;
}
-struct ImBuf *IMB_allocFromBufferOwn(
- unsigned int *rect, float *rectf, unsigned int w, unsigned int h, unsigned int channels)
+struct ImBuf *IMB_allocFromBufferOwn(uint *rect, float *rectf, uint w, uint h, uint channels)
{
ImBuf *ibuf = NULL;
@@ -444,11 +442,8 @@ struct ImBuf *IMB_allocFromBufferOwn(
return ibuf;
}
-struct ImBuf *IMB_allocFromBuffer(const unsigned int *rect,
- const float *rectf,
- unsigned int w,
- unsigned int h,
- unsigned int channels)
+struct ImBuf *IMB_allocFromBuffer(
+ const uint *rect, const float *rectf, uint w, uint h, uint channels)
{
ImBuf *ibuf = NULL;
@@ -488,8 +483,7 @@ bool imb_addtilesImBuf(ImBuf *ibuf)
}
if (!ibuf->tiles) {
- if ((ibuf->tiles = MEM_callocN(sizeof(unsigned int *) * ibuf->xtiles * ibuf->ytiles,
- "imb_tiles"))) {
+ if ((ibuf->tiles = MEM_callocN(sizeof(uint *) * ibuf->xtiles * ibuf->ytiles, "imb_tiles"))) {
ibuf->mall |= IB_tiles;
}
}
@@ -497,7 +491,7 @@ bool imb_addtilesImBuf(ImBuf *ibuf)
return (ibuf->tiles != NULL);
}
-ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int flags)
+ImBuf *IMB_allocImBuf(uint x, uint y, uchar planes, uint flags)
{
ImBuf *ibuf;
@@ -513,8 +507,7 @@ ImBuf *IMB_allocImBuf(unsigned int x, unsigned int y, uchar planes, unsigned int
return ibuf;
}
-bool IMB_initImBuf(
- struct ImBuf *ibuf, unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
+bool IMB_initImBuf(struct ImBuf *ibuf, uint x, uint y, uchar planes, uint flags)
{
memset(ibuf, 0, sizeof(ImBuf));
@@ -536,7 +529,7 @@ bool IMB_initImBuf(
}
if (flags & IB_rectfloat) {
- if (imb_addrectfloatImBuf(ibuf) == false) {
+ if (imb_addrectfloatImBuf(ibuf, ibuf->channels) == false) {
return false;
}
}
@@ -678,7 +671,7 @@ size_t IMB_get_size_in_memory(ImBuf *ibuf)
}
if (ibuf->tiles) {
- size += sizeof(unsigned int) * ibuf->ytiles * ibuf->xtiles;
+ size += sizeof(uint) * ibuf->ytiles * ibuf->xtiles;
}
return size;
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 52ed68a1ff3..4e6a52f8464 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -97,9 +97,9 @@ static void free_anim_movie(struct anim *UNUSED(anim))
# define PATHSEPARATOR '/'
#endif
-static int an_stringdec(const char *string, char *head, char *tail, unsigned short *numlen)
+static int an_stringdec(const char *string, char *head, char *tail, ushort *numlen)
{
- unsigned short len, nume, nums = 0;
+ ushort len, nume, nums = 0;
short i;
bool found = false;
@@ -139,8 +139,7 @@ static int an_stringdec(const char *string, char *head, char *tail, unsigned sho
return true;
}
-static void an_stringenc(
- char *string, const char *head, const char *tail, unsigned short numlen, int pic)
+static void an_stringenc(char *string, const char *head, const char *tail, ushort numlen, int pic)
{
BLI_path_sequence_encode(string, head, tail, numlen, pic);
}
@@ -454,7 +453,7 @@ static ImBuf *avi_fetchibuf(struct anim *anim, int position)
lpbi = AVIStreamGetFrame(anim->pgf, position + AVIStreamStart(anim->pavi[anim->firstvideo]));
if (lpbi) {
ibuf = IMB_ibImageFromMemory(
- (const unsigned char *)lpbi, 100, IB_rect, anim->colorspace, "<avi_fetchibuf>");
+ (const uchar *)lpbi, 100, IB_rect, anim->colorspace, "<avi_fetchibuf>");
/* Oh brother... */
}
}
@@ -1568,7 +1567,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
{
struct ImBuf *ibuf = NULL;
char head[256], tail[256];
- unsigned short digits;
+ ushort digits;
int pic;
int filter_y;
if (anim == NULL) {
diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c
index 967cbd04813..af9b62f1a74 100644
--- a/source/blender/imbuf/intern/bmp.c
+++ b/source/blender/imbuf/intern/bmp.c
@@ -178,7 +178,6 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[
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;
int bitoffs = 8;
int bitmask = startmask;
int nbytes = 0;
@@ -189,7 +188,7 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[
for (size_t j = x; j > 0; j--) {
bitoffs -= depth;
bitmask >>= depth;
- index = (bmp[0] & bitmask) >> bitoffs;
+ const int index = (bmp[0] & bitmask) >> bitoffs;
pcol = palette[index];
/* intentionally BGR -> RGB */
rect[0] = pcol[2];
diff --git a/source/blender/imbuf/intern/cache.c b/source/blender/imbuf/intern/cache.c
index 51f7dbdf41a..98d33ac061f 100644
--- a/source/blender/imbuf/intern/cache.c
+++ b/source/blender/imbuf/intern/cache.c
@@ -81,11 +81,11 @@ static ImGlobalTileCache GLOBAL_CACHE;
/** \name Hash Functions
* \{ */
-static unsigned int imb_global_tile_hash(const void *gtile_p)
+static uint imb_global_tile_hash(const void *gtile_p)
{
const ImGlobalTile *gtile = gtile_p;
- return ((unsigned int)(intptr_t)gtile->ibuf) * 769 + gtile->tx * 53 + gtile->ty * 97;
+ return ((uint)(intptr_t)gtile->ibuf) * 769 + gtile->tx * 53 + gtile->ty * 97;
}
static bool imb_global_tile_cmp(const void *a_p, const void *b_p)
@@ -96,11 +96,11 @@ static bool imb_global_tile_cmp(const void *a_p, const void *b_p)
return ((a->ibuf != b->ibuf) || (a->tx != b->tx) || (a->ty != b->ty));
}
-static unsigned int imb_thread_tile_hash(const void *ttile_p)
+static uint imb_thread_tile_hash(const void *ttile_p)
{
const ImThreadTile *ttile = ttile_p;
- return ((unsigned int)(intptr_t)ttile->ibuf) * 769 + ttile->tx * 53 + ttile->ty * 97;
+ return ((uint)(intptr_t)ttile->ibuf) * 769 + ttile->tx * 53 + ttile->ty * 97;
}
static bool imb_thread_tile_cmp(const void *a_p, const void *b_p)
@@ -121,9 +121,9 @@ static void imb_global_cache_tile_load(ImGlobalTile *gtile)
{
ImBuf *ibuf = gtile->ibuf;
int toffs = ibuf->xtiles * gtile->ty + gtile->tx;
- unsigned int *rect;
+ uint *rect;
- rect = MEM_callocN(sizeof(unsigned int) * ibuf->tilex * ibuf->tiley, "imb_tile");
+ rect = MEM_callocN(sizeof(uint) * ibuf->tilex * ibuf->tiley, "imb_tile");
imb_loadtile(ibuf, gtile->tx, gtile->ty, rect);
ibuf->tiles[toffs] = rect;
}
@@ -136,7 +136,7 @@ static void imb_global_cache_tile_unload(ImGlobalTile *gtile)
MEM_freeN(ibuf->tiles[toffs]);
ibuf->tiles[toffs] = NULL;
- GLOBAL_CACHE.totmem -= sizeof(unsigned int) * ibuf->tilex * ibuf->tiley;
+ GLOBAL_CACHE.totmem -= sizeof(uint) * ibuf->tilex * ibuf->tiley;
}
void imb_tile_cache_tile_free(ImBuf *ibuf, int tx, int ty)
@@ -167,7 +167,7 @@ void imb_tile_cache_tile_free(ImBuf *ibuf, int tx, int ty)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Init/Exit
+/** \name Initialize/Exit
* \{ */
static void imb_thread_cache_init(ImThreadTileCache *cache)
@@ -343,7 +343,7 @@ static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf,
BLI_addhead(&GLOBAL_CACHE.tiles, gtile);
/* mark as being loaded and unlock to allow other threads to load too */
- GLOBAL_CACHE.totmem += sizeof(unsigned int) * ibuf->tilex * ibuf->tiley;
+ GLOBAL_CACHE.totmem += sizeof(uint) * ibuf->tilex * ibuf->tiley;
BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
@@ -363,10 +363,7 @@ static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf,
/** \name Per-Thread Cache
* \{ */
-static unsigned int *imb_thread_cache_get_tile(ImThreadTileCache *cache,
- ImBuf *ibuf,
- int tx,
- int ty)
+static uint *imb_thread_cache_get_tile(ImThreadTileCache *cache, ImBuf *ibuf, int tx, int ty)
{
ImThreadTile *ttile, lookuptile;
ImGlobalTile *gtile, *replacetile;
@@ -418,7 +415,7 @@ static unsigned int *imb_thread_cache_get_tile(ImThreadTileCache *cache,
return ibuf->tiles[toffs];
}
-unsigned int *IMB_gettile(ImBuf *ibuf, int tx, int ty, int thread)
+uint *IMB_gettile(ImBuf *ibuf, int tx, int ty, int thread)
{
return imb_thread_cache_get_tile(&GLOBAL_CACHE.thread_cache[thread + 1], ibuf, tx, ty);
}
@@ -427,7 +424,7 @@ void IMB_tiles_to_rect(ImBuf *ibuf)
{
ImBuf *mipbuf;
ImGlobalTile *gtile;
- unsigned int *to, *from;
+ uint *to, *from;
int a, tx, ty, y, w, h;
for (a = 0; a < ibuf->miptot; a++) {
@@ -435,8 +432,7 @@ void IMB_tiles_to_rect(ImBuf *ibuf)
/* don't call imb_addrectImBuf, it frees all mipmaps */
if (!mipbuf->rect) {
- if ((mipbuf->rect = MEM_callocN(ibuf->x * ibuf->y * sizeof(unsigned int),
- "imb_addrectImBuf"))) {
+ if ((mipbuf->rect = MEM_callocN(ibuf->x * ibuf->y * sizeof(uint), "imb_addrectImBuf"))) {
mipbuf->mall |= IB_rect;
mipbuf->flags |= IB_rect;
}
@@ -460,7 +456,7 @@ void IMB_tiles_to_rect(ImBuf *ibuf)
h = (ty == mipbuf->ytiles - 1) ? mipbuf->y - ty * mipbuf->tiley : mipbuf->tiley;
for (y = 0; y < h; y++) {
- memcpy(to, from, sizeof(unsigned int) * w);
+ memcpy(to, from, sizeof(uint) * w);
from += mipbuf->tilex;
to += mipbuf->x;
}
diff --git a/source/blender/imbuf/intern/cineon/cineon_dpx.c b/source/blender/imbuf/intern/cineon/cineon_dpx.c
index 1a99d2a34d9..3bff8184b19 100644
--- a/source/blender/imbuf/intern/cineon/cineon_dpx.c
+++ b/source/blender/imbuf/intern/cineon/cineon_dpx.c
@@ -21,11 +21,8 @@
#include "MEM_guardedalloc.h"
-static struct ImBuf *imb_load_dpx_cineon(const unsigned char *mem,
- size_t size,
- int use_cineon,
- int flags,
- char colorspace[IM_MAX_SPACE])
+static struct ImBuf *imb_load_dpx_cineon(
+ const uchar *mem, size_t size, int use_cineon, int flags, char colorspace[IM_MAX_SPACE])
{
ImBuf *ibuf;
LogImageFile *image;
@@ -74,7 +71,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filepath, int use_cineon
LogImageFile *logImage;
float *fbuf;
float *fbuf_ptr;
- unsigned char *rect_ptr;
+ uchar *rect_ptr;
int x, y, depth, bitspersample, rvalue;
if (flags & IB_mem) {
@@ -153,7 +150,7 @@ static int imb_save_dpx_cineon(ImBuf *ibuf, const char *filepath, int use_cineon
for (y = 0; y < ibuf->y; y++) {
for (x = 0; x < ibuf->x; x++) {
fbuf_ptr = fbuf + 4 * ((ibuf->y - y - 1) * ibuf->x + x);
- rect_ptr = (unsigned char *)ibuf->rect + 4 * (y * ibuf->x + x);
+ rect_ptr = (uchar *)ibuf->rect + 4 * (y * ibuf->x + x);
fbuf_ptr[0] = (float)rect_ptr[0] / 255.0f;
fbuf_ptr[1] = (float)rect_ptr[1] / 255.0f;
fbuf_ptr[2] = (float)rect_ptr[2] / 255.0f;
@@ -173,15 +170,12 @@ bool imb_save_cineon(struct ImBuf *buf, const char *filepath, int flags)
return imb_save_dpx_cineon(buf, filepath, 1, flags);
}
-bool imb_is_a_cineon(const unsigned char *buf, size_t size)
+bool imb_is_a_cineon(const uchar *buf, size_t size)
{
return logImageIsCineon(buf, size);
}
-ImBuf *imb_load_cineon(const unsigned char *mem,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+ImBuf *imb_load_cineon(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
if (!imb_is_a_cineon(mem, size)) {
return NULL;
@@ -194,15 +188,12 @@ bool imb_save_dpx(struct ImBuf *buf, const char *filepath, int flags)
return imb_save_dpx_cineon(buf, filepath, 0, flags);
}
-bool imb_is_a_dpx(const unsigned char *buf, size_t size)
+bool imb_is_a_dpx(const uchar *buf, size_t size)
{
return logImageIsDpx(buf, size);
}
-ImBuf *imb_load_dpx(const unsigned char *mem,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+ImBuf *imb_load_dpx(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
if (!imb_is_a_dpx(mem, size)) {
return NULL;
diff --git a/source/blender/imbuf/intern/cineon/cineonlib.c b/source/blender/imbuf/intern/cineon/cineonlib.c
index 8312476bda0..6417d92644f 100644
--- a/source/blender/imbuf/intern/cineon/cineonlib.c
+++ b/source/blender/imbuf/intern/cineon/cineonlib.c
@@ -18,6 +18,7 @@
#include <time.h>
#include "BLI_fileops.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
@@ -56,9 +57,8 @@ static void fillCineonMainHeader(LogImageFile *cineon,
cineon->height *
getRowLength(cineon->width, cineon->element[0]),
cineon->isMSB);
- strcpy(header->fileHeader.version, "v4.5");
- strncpy(header->fileHeader.file_name, filepath, 99);
- header->fileHeader.file_name[99] = 0;
+ STRNCPY(header->fileHeader.version, "v4.5");
+ STRNCPY(header->fileHeader.file_name, filepath);
fileClock = time(NULL);
fileTime = localtime(&fileClock);
strftime(header->fileHeader.creation_date, 12, "%Y:%m:%d", fileTime);
@@ -93,8 +93,7 @@ static void fillCineonMainHeader(LogImageFile *cineon,
header->imageHeader.green_primary_y = swap_float(0.0f, cineon->isMSB);
header->imageHeader.blue_primary_x = swap_float(0.0f, cineon->isMSB);
header->imageHeader.blue_primary_y = swap_float(0.0f, cineon->isMSB);
- strncpy(header->imageHeader.label, creator, 199);
- header->imageHeader.label[199] = 0;
+ STRNCPY(header->imageHeader.label, creator);
header->imageHeader.interleave = 0;
header->imageHeader.data_sign = 0;
header->imageHeader.sense = 0;
@@ -122,13 +121,13 @@ static void fillCineonMainHeader(LogImageFile *cineon,
/* we leave it blank */
}
-LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize)
+LogImageFile *cineonOpen(const uchar *byteStuff, int fromMemory, size_t bufferSize)
{
CineonMainHeader header;
LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
const char *filepath = (const char *)byteStuff;
int i;
- unsigned int dataOffset;
+ uint dataOffset;
if (cineon == NULL) {
if (verbose) {
@@ -159,8 +158,8 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t
cineon->memBufferSize = 0;
}
else {
- cineon->memBuffer = (unsigned char *)byteStuff;
- cineon->memCursor = (unsigned char *)byteStuff;
+ cineon->memBuffer = (uchar *)byteStuff;
+ cineon->memCursor = (uchar *)byteStuff;
cineon->memBufferSize = bufferSize;
}
@@ -188,7 +187,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t
else {
if (verbose) {
printf("Cineon: Bad magic number %lu in \"%s\".\n",
- (unsigned long)header.fileHeader.magic_num,
+ (ulong)header.fileHeader.magic_num,
byteStuff);
}
logImageClose(cineon);
@@ -297,7 +296,7 @@ LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t
}
if (cineon->element[i].refHighData == CINEON_UNDEFINED_U32) {
- cineon->element[i].refHighData = (unsigned int)cineon->element[i].maxValue;
+ cineon->element[i].refHighData = (uint)cineon->element[i].maxValue;
}
if (cineon->element[i].refLowQuantity == CINEON_UNDEFINED_R32 ||
@@ -354,7 +353,7 @@ LogImageFile *cineonCreate(
{
CineonMainHeader header;
const char *shortFilename = NULL;
- /* unsigned char pad[6044]; */
+ /* uchar pad[6044]; */
LogImageFile *cineon = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
if (cineon == NULL) {
diff --git a/source/blender/imbuf/intern/cineon/cineonlib.h b/source/blender/imbuf/intern/cineon/cineonlib.h
index 13d40461728..ac0cc15590d 100644
--- a/source/blender/imbuf/intern/cineon/cineonlib.h
+++ b/source/blender/imbuf/intern/cineon/cineonlib.h
@@ -38,10 +38,10 @@ typedef struct {
} CineonFileHeader;
typedef struct {
- unsigned char descriptor1;
- unsigned char descriptor2;
- unsigned char bits_per_sample;
- unsigned char filler;
+ uchar descriptor1;
+ uchar descriptor2;
+ uchar bits_per_sample;
+ uchar filler;
unsigned int pixels_per_line;
unsigned int lines_per_image;
unsigned int ref_low_data;
@@ -51,8 +51,8 @@ typedef struct {
} CineonElementHeader;
typedef struct {
- unsigned char orientation;
- unsigned char elements_per_image;
+ uchar orientation;
+ uchar elements_per_image;
unsigned short filler;
CineonElementHeader element[8];
float white_point_x;
@@ -65,10 +65,10 @@ typedef struct {
float blue_primary_y;
char label[200];
char reserved[28];
- unsigned char interleave;
- unsigned char packing;
- unsigned char data_sign;
- unsigned char sense;
+ uchar interleave;
+ uchar packing;
+ uchar data_sign;
+ uchar sense;
unsigned int line_padding;
unsigned int element_padding;
char reserved2[20];
@@ -90,10 +90,10 @@ typedef struct {
} CineonOriginationHeader;
typedef struct {
- unsigned char film_code;
- unsigned char film_type;
- unsigned char edge_code_perforation_offset;
- unsigned char filler;
+ uchar film_code;
+ uchar film_type;
+ uchar edge_code_perforation_offset;
+ uchar filler;
unsigned int prefix;
unsigned int count;
char format[32];
@@ -112,7 +112,7 @@ typedef struct {
} CineonMainHeader;
void cineonSetVerbose(int);
-LogImageFile *cineonOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize);
+LogImageFile *cineonOpen(const uchar *byteStuff, int fromMemory, size_t bufferSize);
LogImageFile *cineonCreate(
const char *filepath, int width, int height, int bitsPerSample, const char *creator);
diff --git a/source/blender/imbuf/intern/cineon/dpxlib.c b/source/blender/imbuf/intern/cineon/dpxlib.c
index 28c19116361..be8fab26301 100644
--- a/source/blender/imbuf/intern/cineon/dpxlib.c
+++ b/source/blender/imbuf/intern/cineon/dpxlib.c
@@ -4,7 +4,7 @@
/** \file
* \ingroup imbcineon
*
- * Dpx image file format library routines.
+ * DPX image file format library routines.
*/
#include "dpxlib.h"
@@ -18,6 +18,7 @@
#include <time.h>
#include "BLI_fileops.h"
+#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "MEM_guardedalloc.h"
@@ -60,14 +61,12 @@ static void fillDpxMainHeader(LogImageFile *dpx,
header->fileHeader.ind_hdr_size = swap_uint(sizeof(DpxFilmHeader) + sizeof(DpxTelevisionHeader),
dpx->isMSB);
header->fileHeader.user_data_size = DPX_UNDEFINED_U32;
- strncpy(header->fileHeader.file_name, filename, 99);
- header->fileHeader.file_name[99] = 0;
+ STRNCPY(header->fileHeader.file_name, filename);
fileClock = time(NULL);
fileTime = localtime(&fileClock);
strftime(header->fileHeader.creation_date, 24, "%Y:%m:%d:%H:%M:%S%Z", fileTime);
header->fileHeader.creation_date[23] = 0;
- strncpy(header->fileHeader.creator, creator, 99);
- header->fileHeader.creator[99] = 0;
+ STRNCPY(header->fileHeader.creator, creator);
header->fileHeader.project[0] = 0;
header->fileHeader.copyright[0] = 0;
header->fileHeader.key = 0xFFFFFFFF;
@@ -120,7 +119,7 @@ static void fillDpxMainHeader(LogImageFile *dpx,
header->televisionHeader.integration_times = swap_float(DPX_UNDEFINED_R32, dpx->isMSB);
}
-LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t bufferSize)
+LogImageFile *dpxOpen(const uchar *byteStuff, int fromMemory, size_t bufferSize)
{
DpxMainHeader header;
LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
@@ -156,8 +155,8 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf
dpx->memBufferSize = 0;
}
else {
- dpx->memBuffer = (unsigned char *)byteStuff;
- dpx->memCursor = (unsigned char *)byteStuff;
+ dpx->memBuffer = (uchar *)byteStuff;
+ dpx->memCursor = (uchar *)byteStuff;
dpx->memBufferSize = bufferSize;
}
@@ -321,7 +320,7 @@ LogImageFile *dpxOpen(const unsigned char *byteStuff, int fromMemory, size_t buf
}
if (dpx->element[i].refHighData == DPX_UNDEFINED_U32) {
- dpx->element[i].refHighData = (unsigned int)dpx->element[i].maxValue;
+ dpx->element[i].refHighData = (uint)dpx->element[i].maxValue;
}
if (IS_DPX_UNDEFINED_R32(dpx->element[i].refLowQuantity)) {
@@ -419,7 +418,7 @@ LogImageFile *dpxCreate(const char *filepath,
{
DpxMainHeader header;
const char *shortFilename = NULL;
- unsigned char pad[6044];
+ uchar pad[6044];
LogImageFile *dpx = (LogImageFile *)MEM_mallocN(sizeof(LogImageFile), __func__);
if (dpx == NULL) {
diff --git a/source/blender/imbuf/intern/cineon/logImageCore.c b/source/blender/imbuf/intern/cineon/logImageCore.c
index e693aa6f891..8188d0d04b9 100644
--- a/source/blender/imbuf/intern/cineon/logImageCore.c
+++ b/source/blender/imbuf/intern/cineon/logImageCore.c
@@ -81,29 +81,29 @@ void logImageSetVerbose(int verbosity)
* IO stuff
*/
-int logImageIsDpx(const void *buffer, const unsigned int size)
+int logImageIsDpx(const void *buffer, const uint size)
{
- unsigned int magicNum;
+ uint magicNum;
if (size < sizeof(magicNum)) {
return 0;
}
- magicNum = *(unsigned int *)buffer;
+ magicNum = *(uint *)buffer;
return (magicNum == DPX_FILE_MAGIC || magicNum == swap_uint(DPX_FILE_MAGIC, 1));
}
-int logImageIsCineon(const void *buffer, const unsigned int size)
+int logImageIsCineon(const void *buffer, const uint size)
{
- unsigned int magicNum;
+ uint magicNum;
if (size < sizeof(magicNum)) {
return 0;
}
- magicNum = *(unsigned int *)buffer;
+ magicNum = *(uint *)buffer;
return (magicNum == CINEON_FILE_MAGIC || magicNum == swap_uint(CINEON_FILE_MAGIC, 1));
}
LogImageFile *logImageOpenFromFile(const char *filepath, int cineon)
{
- unsigned int magicNum;
+ uint magicNum;
FILE *f = BLI_fopen(filepath, "rb");
(void)cineon;
@@ -120,16 +120,16 @@ LogImageFile *logImageOpenFromFile(const char *filepath, int cineon)
fclose(f);
if (logImageIsDpx(&magicNum, sizeof(magicNum))) {
- return dpxOpen((const unsigned char *)filepath, 0, 0);
+ return dpxOpen((const uchar *)filepath, 0, 0);
}
if (logImageIsCineon(&magicNum, sizeof(magicNum))) {
- return cineonOpen((const unsigned char *)filepath, 0, 0);
+ return cineonOpen((const uchar *)filepath, 0, 0);
}
return NULL;
}
-LogImageFile *logImageOpenFromMemory(const unsigned char *buffer, unsigned int size)
+LogImageFile *logImageOpenFromMemory(const uchar *buffer, uint size)
{
if (logImageIsDpx(buffer, size)) {
return dpxOpen(buffer, 1, size);
@@ -276,9 +276,9 @@ int logImageSetDataRGBA(LogImageFile *logImage, float *data, int dataIsLinearRGB
static int logImageSetData8(LogImageFile *logImage, LogImageElement logElement, float *data)
{
size_t rowLength = getRowLength(logImage->width, logElement);
- unsigned char *row;
+ uchar *row;
- row = (unsigned char *)MEM_mallocN(rowLength, __func__);
+ row = (uchar *)MEM_mallocN(rowLength, __func__);
if (row == NULL) {
if (verbose) {
printf("DPX/Cineon: Cannot allocate row.\n");
@@ -289,7 +289,7 @@ static int logImageSetData8(LogImageFile *logImage, LogImageElement logElement,
for (size_t y = 0; y < logImage->height; y++) {
for (size_t x = 0; x < logImage->width * logImage->depth; x++) {
- row[x] = (unsigned char)float_uint(data[y * logImage->width * logImage->depth + x], 255);
+ row[x] = (uchar)float_uint(data[y * logImage->width * logImage->depth + x], 255);
}
if (logimage_fwrite(row, rowLength, 1, logImage) == 0) {
@@ -307,10 +307,10 @@ static int logImageSetData8(LogImageFile *logImage, LogImageElement logElement,
static int logImageSetData10(LogImageFile *logImage, LogImageElement logElement, float *data)
{
size_t rowLength = getRowLength(logImage->width, logElement);
- unsigned int pixel, index;
- unsigned int *row;
+ uint pixel, index;
+ uint *row;
- row = (unsigned int *)MEM_mallocN(rowLength, __func__);
+ row = (uint *)MEM_mallocN(rowLength, __func__);
if (row == NULL) {
if (verbose) {
printf("DPX/Cineon: Cannot allocate row.\n");
@@ -324,8 +324,7 @@ static int logImageSetData10(LogImageFile *logImage, LogImageElement logElement,
pixel = 0;
for (size_t x = 0; x < logImage->width * logImage->depth; x++) {
- pixel |= (unsigned int)float_uint(data[y * logImage->width * logImage->depth + x], 1023)
- << offset;
+ pixel |= (uint)float_uint(data[y * logImage->width * logImage->depth + x], 1023) << offset;
offset -= 10;
if (offset < 0) {
row[index] = swap_uint(pixel, logImage->isMSB);
@@ -353,9 +352,9 @@ static int logImageSetData10(LogImageFile *logImage, LogImageElement logElement,
static int logImageSetData12(LogImageFile *logImage, LogImageElement logElement, float *data)
{
size_t rowLength = getRowLength(logImage->width, logElement);
- unsigned short *row;
+ ushort *row;
- row = (unsigned short *)MEM_mallocN(rowLength, __func__);
+ row = (ushort *)MEM_mallocN(rowLength, __func__);
if (row == NULL) {
if (verbose) {
printf("DPX/Cineon: Cannot allocate row.\n");
@@ -366,7 +365,7 @@ static int logImageSetData12(LogImageFile *logImage, LogImageElement logElement,
for (size_t y = 0; y < logImage->height; y++) {
for (size_t x = 0; x < logImage->width * logImage->depth; x++) {
row[x] = swap_ushort(
- ((unsigned short)float_uint(data[y * logImage->width * logImage->depth + x], 4095)) << 4,
+ ((ushort)float_uint(data[y * logImage->width * logImage->depth + x], 4095)) << 4,
logImage->isMSB);
}
@@ -385,9 +384,9 @@ static int logImageSetData12(LogImageFile *logImage, LogImageElement logElement,
static int logImageSetData16(LogImageFile *logImage, LogImageElement logElement, float *data)
{
size_t rowLength = getRowLength(logImage->width, logElement);
- unsigned short *row;
+ ushort *row;
- row = (unsigned short *)MEM_mallocN(rowLength, __func__);
+ row = (ushort *)MEM_mallocN(rowLength, __func__);
if (row == NULL) {
if (verbose) {
printf("DPX/Cineon: Cannot allocate row.\n");
@@ -398,7 +397,7 @@ static int logImageSetData16(LogImageFile *logImage, LogImageElement logElement,
for (size_t y = 0; y < logImage->height; y++) {
for (size_t x = 0; x < logImage->width * logImage->depth; x++) {
row[x] = swap_ushort(
- (unsigned short)float_uint(data[y * logImage->width * logImage->depth + x], 65535),
+ (ushort)float_uint(data[y * logImage->width * logImage->depth + x], 65535),
logImage->isMSB);
}
@@ -425,11 +424,11 @@ int logImageGetDataRGBA(LogImageFile *logImage, float *data, int dataIsLinearRGB
float *elementData[8];
float *elementData_ptr[8];
float *mergedData;
- unsigned int sampleIndex;
+ uint sampleIndex;
LogImageElement mergedElement;
/* Determine the depth of the picture and if there's a separate alpha element.
- * If the element is supported, load it into an unsigned ints array. */
+ * If the element is supported, load it into an `uint` array. */
memset(&elementData, 0, 8 * sizeof(float *));
hasAlpha = 0;
@@ -695,7 +694,7 @@ static int logImageElementGetData(LogImageFile *logImage, LogImageElement logEle
static int logImageElementGetData1(LogImageFile *logImage, LogImageElement logElement, float *data)
{
- unsigned int pixel;
+ uint pixel;
/* seek at the right place */
if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) {
@@ -727,7 +726,7 @@ static int logImageElementGetData1(LogImageFile *logImage, LogImageElement logEl
static int logImageElementGetData8(LogImageFile *logImage, LogImageElement logElement, float *data)
{
size_t rowLength = getRowLength(logImage->width, logElement);
- unsigned char pixel;
+ uchar pixel;
/* extract required pixels */
for (size_t y = 0; y < logImage->height; y++) {
@@ -756,7 +755,7 @@ static int logImageElementGetData10(LogImageFile *logImage,
LogImageElement logElement,
float *data)
{
- unsigned int pixel;
+ uint pixel;
/* seek to data */
if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) {
@@ -829,15 +828,14 @@ static int logImageElementGetData10Packed(LogImageFile *logImage,
float *data)
{
size_t rowLength = getRowLength(logImage->width, logElement);
- unsigned int pixel, oldPixel;
+ uint pixel, oldPixel;
/* converting bytes to pixels */
for (size_t y = 0; y < logImage->height; y++) {
/* seek to data */
if (logimage_fseek(logImage, y * rowLength + logElement.dataOffset, SEEK_SET) != 0) {
if (verbose) {
- printf("DPX/Cineon: Couldn't seek at %u\n",
- (unsigned int)(y * rowLength + logElement.dataOffset));
+ printf("DPX/Cineon: Couldn't seek at %u\n", (uint)(y * rowLength + logElement.dataOffset));
}
return 1;
}
@@ -884,9 +882,9 @@ static int logImageElementGetData12(LogImageFile *logImage,
LogImageElement logElement,
float *data)
{
- unsigned int sampleIndex;
- unsigned int numSamples = logImage->width * logImage->height * logElement.depth;
- unsigned short pixel;
+ uint sampleIndex;
+ uint numSamples = logImage->width * logImage->height * logElement.depth;
+ ushort pixel;
/* seek to data */
if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) {
@@ -923,15 +921,14 @@ static int logImageElementGetData12Packed(LogImageFile *logImage,
float *data)
{
size_t rowLength = getRowLength(logImage->width, logElement);
- unsigned int pixel, oldPixel;
+ uint pixel, oldPixel;
/* converting bytes to pixels */
for (size_t y = 0; y < logImage->height; y++) {
/* seek to data */
if (logimage_fseek(logImage, y * rowLength + logElement.dataOffset, SEEK_SET) != 0) {
if (verbose) {
- printf("DPX/Cineon: Couldn't seek at %u\n",
- (unsigned int)(y * rowLength + logElement.dataOffset));
+ printf("DPX/Cineon: Couldn't seek at %u\n", (uint)(y * rowLength + logElement.dataOffset));
}
return 1;
}
@@ -978,9 +975,9 @@ static int logImageElementGetData16(LogImageFile *logImage,
LogImageElement logElement,
float *data)
{
- unsigned int numSamples = logImage->width * logImage->height * logElement.depth;
- unsigned int sampleIndex;
- unsigned short pixel;
+ uint numSamples = logImage->width * logImage->height * logElement.depth;
+ uint sampleIndex;
+ ushort pixel;
/* seek to data */
if (logimage_fseek(logImage, logElement.dataOffset, SEEK_SET) != 0) {
@@ -1076,8 +1073,8 @@ static float *getLinToLogLut(LogImageFile *logImage, LogImageElement logElement)
{
float *lut;
float gain, negativeFilmGamma, offset, step;
- unsigned int lutsize = (unsigned int)(logElement.maxValue + 1);
- unsigned int i;
+ uint lutsize = (uint)(logElement.maxValue + 1);
+ uint i;
lut = MEM_mallocN(sizeof(float) * lutsize, "getLinToLogLut");
@@ -1104,8 +1101,8 @@ static float *getLogToLinLut(LogImageFile *logImage, LogImageElement logElement)
float *lut;
float breakPoint, gain, kneeGain, kneeOffset, negativeFilmGamma, offset, step, softClip;
/* float filmGamma; unused */
- unsigned int lutsize = (unsigned int)(logElement.maxValue + 1);
- unsigned int i;
+ uint lutsize = (uint)(logElement.maxValue + 1);
+ uint i;
lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut");
@@ -1154,8 +1151,8 @@ static float *getLogToLinLut(LogImageFile *logImage, LogImageElement logElement)
static float *getLinToSrgbLut(LogImageElement logElement)
{
float col, *lut;
- unsigned int lutsize = (unsigned int)(logElement.maxValue + 1);
- unsigned int i;
+ uint lutsize = (uint)(logElement.maxValue + 1);
+ uint i;
lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut");
@@ -1175,8 +1172,8 @@ static float *getLinToSrgbLut(LogImageElement logElement)
static float *getSrgbToLinLut(LogImageElement logElement)
{
float col, *lut;
- unsigned int lutsize = (unsigned int)(logElement.maxValue + 1);
- unsigned int i;
+ uint lutsize = (uint)(logElement.maxValue + 1);
+ uint i;
lut = MEM_mallocN(sizeof(float) * lutsize, "getLogToLinLut");
@@ -1199,7 +1196,7 @@ static int convertRGBA_RGB(float *src,
LogImageElement logElement,
int elementIsSource)
{
- unsigned int i;
+ uint i;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1254,7 +1251,7 @@ static int convertRGB_RGBA(float *src,
LogImageElement logElement,
int elementIsSource)
{
- unsigned int i;
+ uint i;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1309,7 +1306,7 @@ static int convertRGBA_RGBA(float *src,
LogImageElement logElement,
int elementIsSource)
{
- unsigned int i;
+ uint i;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1354,7 +1351,7 @@ static int convertABGR_RGBA(float *src,
LogImageElement logElement,
int elementIsSource)
{
- unsigned int i;
+ uint i;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1407,7 +1404,7 @@ static int convertCbYCr_RGBA(float *src,
LogImageFile *logImage,
LogImageElement logElement)
{
- unsigned int i;
+ uint i;
float conversionMatrix[9], refLowData, y, cb, cr;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1439,7 +1436,7 @@ static int convertCbYCrA_RGBA(float *src,
LogImageFile *logImage,
LogImageElement logElement)
{
- unsigned int i;
+ uint i;
float conversionMatrix[9], refLowData, y, cb, cr, a;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1472,7 +1469,7 @@ static int convertCbYCrY_RGBA(float *src,
LogImageFile *logImage,
LogImageElement logElement)
{
- unsigned int i;
+ uint i;
float conversionMatrix[9], refLowData, y1, y2, cb, cr;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1524,7 +1521,7 @@ static int convertCbYACrYA_RGBA(float *src,
LogImageFile *logImage,
LogImageElement logElement)
{
- unsigned int i;
+ uint i;
float conversionMatrix[9], refLowData, y1, y2, cb, cr, a1, a2;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1578,7 +1575,7 @@ static int convertLuminance_RGBA(float *src,
LogImageFile *logImage,
LogImageElement logElement)
{
- unsigned int i;
+ uint i;
float conversionMatrix[9], value, refLowData;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1604,7 +1601,7 @@ static int convertYA_RGBA(float *src,
LogImageFile *logImage,
LogImageElement logElement)
{
- unsigned int i;
+ uint i;
float conversionMatrix[9], value, refLowData;
float *src_ptr = src;
float *dst_ptr = dst;
@@ -1629,7 +1626,7 @@ static int convertLogElementToRGBA(
float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int dstIsLinearRGB)
{
int rvalue;
- unsigned int i;
+ uint i;
float *src_ptr;
float *dst_ptr;
@@ -1698,7 +1695,7 @@ static int convertLogElementToRGBA(
static int convertRGBAToLogElement(
float *src, float *dst, LogImageFile *logImage, LogImageElement logElement, int srcIsLinearRGB)
{
- unsigned int i;
+ uint i;
int rvalue;
float *srgbSrc;
float *srgbSrc_ptr;
diff --git a/source/blender/imbuf/intern/cineon/logmemfile.c b/source/blender/imbuf/intern/cineon/logmemfile.c
index f5bd87f96d1..6c24d67b33f 100644
--- a/source/blender/imbuf/intern/cineon/logmemfile.c
+++ b/source/blender/imbuf/intern/cineon/logmemfile.c
@@ -44,7 +44,7 @@ int logimage_fseek(LogImageFile *logFile, intptr_t offset, int origin)
return 0;
}
-int logimage_fwrite(void *buffer, size_t size, unsigned int count, LogImageFile *logFile)
+int logimage_fwrite(void *buffer, size_t size, uint count, LogImageFile *logFile)
{
if (logFile->file) {
return fwrite(buffer, size, count, logFile->file);
@@ -54,13 +54,13 @@ int logimage_fwrite(void *buffer, size_t size, unsigned int count, LogImageFile
return count;
}
-int logimage_fread(void *buffer, size_t size, unsigned int count, LogImageFile *logFile)
+int logimage_fread(void *buffer, size_t size, uint count, LogImageFile *logFile)
{
if (logFile->file) {
return fread(buffer, size, count, logFile->file);
}
/* we're reading from memory */
- unsigned char *buf = (unsigned char *)buffer;
+ uchar *buf = (uchar *)buffer;
uintptr_t pos = (uintptr_t)logFile->memCursor - (uintptr_t)logFile->memBuffer;
size_t total_size = size * count;
if (pos + total_size > logFile->memBufferSize) {
@@ -77,38 +77,38 @@ int logimage_fread(void *buffer, size_t size, unsigned int count, LogImageFile *
return count;
}
-int logimage_read_uchar(unsigned char *x, LogImageFile *logFile)
+int logimage_read_uchar(uchar *x, LogImageFile *logFile)
{
uintptr_t pos = (uintptr_t)logFile->memCursor - (uintptr_t)logFile->memBuffer;
- if (pos + sizeof(unsigned char) > logFile->memBufferSize) {
+ if (pos + sizeof(uchar) > logFile->memBufferSize) {
return 1;
}
- *x = *(unsigned char *)logFile->memCursor;
- logFile->memCursor += sizeof(unsigned char);
+ *x = *(uchar *)logFile->memCursor;
+ logFile->memCursor += sizeof(uchar);
return 0;
}
-int logimage_read_ushort(unsigned short *x, LogImageFile *logFile)
+int logimage_read_ushort(ushort *x, LogImageFile *logFile)
{
uintptr_t pos = (uintptr_t)logFile->memCursor - (uintptr_t)logFile->memBuffer;
- if (pos + sizeof(unsigned short) > logFile->memBufferSize) {
+ if (pos + sizeof(ushort) > logFile->memBufferSize) {
return 1;
}
- *x = *(unsigned short *)logFile->memCursor;
- logFile->memCursor += sizeof(unsigned short);
+ *x = *(ushort *)logFile->memCursor;
+ logFile->memCursor += sizeof(ushort);
return 0;
}
-int logimage_read_uint(unsigned int *x, LogImageFile *logFile)
+int logimage_read_uint(uint *x, LogImageFile *logFile)
{
uintptr_t pos = (uintptr_t)logFile->memCursor - (uintptr_t)logFile->memBuffer;
- if (pos + sizeof(unsigned int) > logFile->memBufferSize) {
+ if (pos + sizeof(uint) > logFile->memBufferSize) {
return 1;
}
- *x = *(unsigned int *)logFile->memCursor;
- logFile->memCursor += sizeof(unsigned int);
+ *x = *(uint *)logFile->memCursor;
+ logFile->memCursor += sizeof(uint);
return 0;
}
diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c
index b95ec1cd92b..ea5f4ec275d 100644
--- a/source/blender/imbuf/intern/colormanagement.c
+++ b/source/blender/imbuf/intern/colormanagement.c
@@ -235,11 +235,11 @@ static ColormanageCacheData *colormanage_cachedata_get(const ImBuf *ibuf)
return ibuf->colormanage_cache->data;
}
-static unsigned int colormanage_hashhash(const void *key_v)
+static uint colormanage_hashhash(const void *key_v)
{
const ColormanageCacheKey *key = key_v;
- unsigned int rval = (key->display << 16) | (key->view % 0xffff);
+ uint rval = (key->display << 16) | (key->view % 0xffff);
return rval;
}
@@ -336,11 +336,10 @@ static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf,
return cache_ibuf;
}
-static unsigned char *colormanage_cache_get(
- ImBuf *ibuf,
- const ColormanageCacheViewSettings *view_settings,
- const ColormanageCacheDisplaySettings *display_settings,
- void **cache_handle)
+static uchar *colormanage_cache_get(ImBuf *ibuf,
+ const ColormanageCacheViewSettings *view_settings,
+ const ColormanageCacheDisplaySettings *display_settings,
+ void **cache_handle)
{
ColormanageCacheKey key;
ImBuf *cache_ibuf;
@@ -383,7 +382,7 @@ static unsigned char *colormanage_cache_get(
return NULL;
}
- return (unsigned char *)cache_ibuf->rect;
+ return (uchar *)cache_ibuf->rect;
}
return NULL;
@@ -392,7 +391,7 @@ static unsigned char *colormanage_cache_get(
static void colormanage_cache_put(ImBuf *ibuf,
const ColormanageCacheViewSettings *view_settings,
const ColormanageCacheDisplaySettings *display_settings,
- unsigned char *display_buffer,
+ uchar *display_buffer,
void **cache_handle)
{
ColormanageCacheKey key;
@@ -410,7 +409,7 @@ static void colormanage_cache_put(ImBuf *ibuf,
/* buffer itself */
cache_ibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, 0);
- cache_ibuf->rect = (unsigned int *)display_buffer;
+ cache_ibuf->rect = (uint *)display_buffer;
cache_ibuf->mall |= IB_rect;
cache_ibuf->flags |= IB_rect;
@@ -1441,10 +1440,10 @@ typedef struct DisplayBufferThread {
ColormanageProcessor *cm_processor;
const float *buffer;
- unsigned char *byte_buffer;
+ uchar *byte_buffer;
float *display_buffer;
- unsigned char *display_buffer_byte;
+ uchar *display_buffer_byte;
int width;
int start_line;
@@ -1463,10 +1462,10 @@ typedef struct DisplayBufferInitData {
ImBuf *ibuf;
ColormanageProcessor *cm_processor;
const float *buffer;
- unsigned char *byte_buffer;
+ uchar *byte_buffer;
float *display_buffer;
- unsigned char *display_buffer_byte;
+ uchar *display_buffer_byte;
int width;
@@ -1539,13 +1538,13 @@ static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle,
bool predivide = handle->predivide;
if (!handle->buffer) {
- unsigned char *byte_buffer = handle->byte_buffer;
+ uchar *byte_buffer = handle->byte_buffer;
const char *from_colorspace = handle->byte_colorspace;
const char *to_colorspace = global_role_scene_linear;
float *fp;
- unsigned char *cp;
+ uchar *cp;
const size_t i_last = ((size_t)width) * height;
size_t i;
@@ -1608,7 +1607,7 @@ static void *do_display_buffer_apply_thread(void *handle_v)
DisplayBufferThread *handle = (DisplayBufferThread *)handle_v;
ColormanageProcessor *cm_processor = handle->cm_processor;
float *display_buffer = handle->display_buffer;
- unsigned char *display_buffer_byte = handle->display_buffer_byte;
+ uchar *display_buffer_byte = handle->display_buffer_byte;
int channels = handle->channels;
int width = handle->width;
int height = handle->tot_line;
@@ -1698,9 +1697,9 @@ static void *do_display_buffer_apply_thread(void *handle_v)
static void display_buffer_apply_threaded(ImBuf *ibuf,
const float *buffer,
- unsigned char *byte_buffer,
+ uchar *byte_buffer,
float *display_buffer,
- unsigned char *display_buffer_byte,
+ uchar *display_buffer_byte,
ColormanageProcessor *cm_processor)
{
DisplayBufferInitData init_data;
@@ -1761,7 +1760,7 @@ static bool is_ibuf_rect_in_display_space(ImBuf *ibuf,
static void colormanage_display_buffer_process_ex(
ImBuf *ibuf,
float *display_buffer,
- unsigned char *display_buffer_byte,
+ uchar *display_buffer_byte,
const ColorManagedViewSettings *view_settings,
const ColorManagedDisplaySettings *display_settings)
{
@@ -1783,7 +1782,7 @@ static void colormanage_display_buffer_process_ex(
display_buffer_apply_threaded(ibuf,
ibuf->rect_float,
- (unsigned char *)ibuf->rect,
+ (uchar *)ibuf->rect,
display_buffer,
display_buffer_byte,
cm_processor);
@@ -1794,7 +1793,7 @@ static void colormanage_display_buffer_process_ex(
}
static void colormanage_display_buffer_process(ImBuf *ibuf,
- unsigned char *display_buffer,
+ uchar *display_buffer,
const ColorManagedViewSettings *view_settings,
const ColorManagedDisplaySettings *display_settings)
{
@@ -1810,7 +1809,7 @@ static void colormanage_display_buffer_process(ImBuf *ibuf,
typedef struct ProcessorTransformThread {
ColormanageProcessor *cm_processor;
- unsigned char *byte_buffer;
+ uchar *byte_buffer;
float *float_buffer;
int width;
int start_line;
@@ -1822,7 +1821,7 @@ typedef struct ProcessorTransformThread {
typedef struct ProcessorTransformInit {
ColormanageProcessor *cm_processor;
- unsigned char *byte_buffer;
+ uchar *byte_buffer;
float *float_buffer;
int width;
int height;
@@ -1871,7 +1870,7 @@ static void processor_transform_init_handle(void *handle_v,
static void *do_processor_transform_thread(void *handle_v)
{
ProcessorTransformThread *handle = (ProcessorTransformThread *)handle_v;
- unsigned char *byte_buffer = handle->byte_buffer;
+ uchar *byte_buffer = handle->byte_buffer;
float *float_buffer = handle->float_buffer;
const int channels = handle->channels;
const int width = handle->width;
@@ -1907,7 +1906,7 @@ static void *do_processor_transform_thread(void *handle_v)
return NULL;
}
-static void processor_transform_apply_threaded(unsigned char *byte_buffer,
+static void processor_transform_apply_threaded(uchar *byte_buffer,
float *float_buffer,
const int width,
const int height,
@@ -1942,7 +1941,7 @@ static void processor_transform_apply_threaded(unsigned char *byte_buffer,
/* Convert the whole buffer from specified by name color space to another -
* internal implementation. */
-static void colormanagement_transform_ex(unsigned char *byte_buffer,
+static void colormanagement_transform_ex(uchar *byte_buffer,
float *float_buffer,
int width,
int height,
@@ -2008,7 +2007,7 @@ void IMB_colormanagement_transform_threaded(float *buffer,
NULL, buffer, width, height, channels, from_colorspace, to_colorspace, predivide, true);
}
-void IMB_colormanagement_transform_byte(unsigned char *buffer,
+void IMB_colormanagement_transform_byte(uchar *buffer,
int width,
int height,
int channels,
@@ -2018,7 +2017,7 @@ void IMB_colormanagement_transform_byte(unsigned char *buffer,
colormanagement_transform_ex(
buffer, NULL, width, height, channels, from_colorspace, to_colorspace, false, false);
}
-void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer,
+void IMB_colormanagement_transform_byte_threaded(uchar *buffer,
int width,
int height,
int channels,
@@ -2030,7 +2029,7 @@ void IMB_colormanagement_transform_byte_threaded(unsigned char *buffer,
}
void IMB_colormanagement_transform_from_byte(float *float_buffer,
- unsigned char *byte_buffer,
+ uchar *byte_buffer,
int width,
int height,
int channels,
@@ -2050,7 +2049,7 @@ void IMB_colormanagement_transform_from_byte(float *float_buffer,
float_buffer, width, height, channels, from_colorspace, to_colorspace, true);
}
void IMB_colormanagement_transform_from_byte_threaded(float *float_buffer,
- unsigned char *byte_buffer,
+ uchar *byte_buffer,
int width,
int height,
int channels,
@@ -2205,7 +2204,7 @@ void IMB_colormanagement_colorspace_to_scene_linear(float *buffer,
}
}
-void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
+void IMB_colormanagement_imbuf_to_byte_texture(uchar *out_buffer,
const int offset_x,
const int offset_y,
const int width,
@@ -2213,20 +2212,21 @@ void IMB_colormanagement_imbuf_to_byte_texture(unsigned char *out_buffer,
const struct ImBuf *ibuf,
const bool store_premultiplied)
{
- /* Byte buffer storage, only for sRGB and data texture since other
+ /* Byte buffer storage, only for sRGB, scene linear and data texture since other
* color space conversions can't be done on the GPU. */
BLI_assert(ibuf->rect && ibuf->rect_float == NULL);
BLI_assert(IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) ||
+ IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace) ||
IMB_colormanagement_space_is_data(ibuf->rect_colorspace));
- const unsigned char *in_buffer = (unsigned char *)ibuf->rect;
+ const uchar *in_buffer = (uchar *)ibuf->rect;
const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
for (int y = 0; y < height; y++) {
const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
const size_t out_offset = y * width;
- const unsigned char *in = in_buffer + in_offset * 4;
- unsigned char *out = out_buffer + out_offset * 4;
+ const uchar *in = in_buffer + in_offset * 4;
+ uchar *out = out_buffer + out_offset * 4;
if (use_premultiply) {
/* Premultiply only. */
@@ -2304,7 +2304,7 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer,
}
else {
/* Byte source buffer. */
- const unsigned char *in_buffer = (unsigned char *)ibuf->rect;
+ const uchar *in_buffer = (uchar *)ibuf->rect;
const bool use_premultiply = IMB_alpha_affects_rgb(ibuf) && store_premultiplied;
/* TODO(brecht): make this multi-threaded, or at least process in batches. */
@@ -2316,7 +2316,7 @@ void IMB_colormanagement_imbuf_to_float_texture(float *out_buffer,
for (int y = 0; y < height; y++) {
const size_t in_offset = (offset_y + y) * ibuf->x + offset_x;
const size_t out_offset = y * width;
- const unsigned char *in = in_buffer + in_offset * 4;
+ const uchar *in = in_buffer + in_offset * 4;
float *out = out_buffer + out_offset * 4;
/* Convert to scene linear, to sRGB and premultiply. */
@@ -2457,7 +2457,7 @@ static void colormanagement_imbuf_make_display_space(
}
colormanage_display_buffer_process_ex(
- ibuf, ibuf->rect_float, (unsigned char *)ibuf->rect, view_settings, display_settings);
+ ibuf, ibuf->rect_float, (uchar *)ibuf->rect, view_settings, display_settings);
}
void IMB_colormanagement_imbuf_make_display_space(
@@ -2544,10 +2544,8 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf,
}
if (colormanaged_ibuf->rect) {
- IMB_alpha_under_color_byte((unsigned char *)colormanaged_ibuf->rect,
- colormanaged_ibuf->x,
- colormanaged_ibuf->y,
- color);
+ IMB_alpha_under_color_byte(
+ (uchar *)colormanaged_ibuf->rect, colormanaged_ibuf->x, colormanaged_ibuf->y, color);
}
}
@@ -2602,7 +2600,7 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf,
if (colormanaged_ibuf->rect) {
/* Byte to byte. */
- IMB_colormanagement_transform_byte_threaded((unsigned char *)colormanaged_ibuf->rect,
+ IMB_colormanagement_transform_byte_threaded((uchar *)colormanaged_ibuf->rect,
colormanaged_ibuf->x,
colormanaged_ibuf->y,
colormanaged_ibuf->channels,
@@ -2649,12 +2647,12 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf,
/** \name Public Display Buffers Interfaces
* \{ */
-unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf,
- const ColorManagedViewSettings *view_settings,
- const ColorManagedDisplaySettings *display_settings,
- void **cache_handle)
+uchar *IMB_display_buffer_acquire(ImBuf *ibuf,
+ const ColorManagedViewSettings *view_settings,
+ const ColorManagedDisplaySettings *display_settings,
+ void **cache_handle)
{
- unsigned char *display_buffer;
+ uchar *display_buffer;
size_t buffer_size;
ColormanageCacheViewSettings cache_view_settings;
ColormanageCacheDisplaySettings cache_display_settings;
@@ -2682,7 +2680,7 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf,
*/
if (ibuf->rect_float == NULL && ibuf->rect_colorspace && ibuf->channels == 4) {
if (is_ibuf_rect_in_display_space(ibuf, applied_view_settings, display_settings)) {
- return (unsigned char *)ibuf->rect;
+ return (uchar *)ibuf->rect;
}
}
@@ -2693,7 +2691,7 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf,
if ((ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) == 0) {
IMB_partial_display_buffer_update_threaded(ibuf,
ibuf->rect_float,
- (unsigned char *)ibuf->rect,
+ (uchar *)ibuf->rect,
ibuf->x,
0,
0,
@@ -2712,14 +2710,14 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf,
/* ensure color management bit fields exists */
if (!ibuf->display_buffer_flags) {
- ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display,
+ ibuf->display_buffer_flags = MEM_callocN(sizeof(uint) * global_tot_display,
"imbuf display_buffer_flags");
}
else if (ibuf->userflags & IB_DISPLAY_BUFFER_INVALID) {
/* all display buffers were marked as invalid from other areas,
* now propagate this flag to internal color management routines
*/
- memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
+ memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(uint));
ibuf->userflags &= ~IB_DISPLAY_BUFFER_INVALID;
}
@@ -2746,7 +2744,7 @@ unsigned char *IMB_display_buffer_acquire(ImBuf *ibuf,
return display_buffer;
}
-unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
+uchar *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, void **cache_handle)
{
ColorManagedViewSettings *view_settings;
ColorManagedDisplaySettings *display_settings;
@@ -2756,7 +2754,7 @@ unsigned char *IMB_display_buffer_acquire_ctx(const bContext *C, ImBuf *ibuf, vo
return IMB_display_buffer_acquire(ibuf, view_settings, display_settings, cache_handle);
}
-void IMB_display_buffer_transform_apply(unsigned char *display_buffer,
+void IMB_display_buffer_transform_apply(uchar *display_buffer,
float *linear_buffer,
int width,
int height,
@@ -3395,9 +3393,9 @@ void IMB_colormanagement_colorspace_items_add(EnumPropertyItem **items, int *tot
*/
static void partial_buffer_update_rect(ImBuf *ibuf,
- unsigned char *display_buffer,
+ uchar *display_buffer,
const float *linear_buffer,
- const unsigned char *byte_buffer,
+ const uchar *byte_buffer,
int display_stride,
int linear_stride,
int linear_offset_x,
@@ -3546,9 +3544,9 @@ static void partial_buffer_update_rect(ImBuf *ibuf,
typedef struct PartialThreadData {
ImBuf *ibuf;
- unsigned char *display_buffer;
+ uchar *display_buffer;
const float *linear_buffer;
- const unsigned char *byte_buffer;
+ const uchar *byte_buffer;
int display_stride;
int linear_stride;
int linear_offset_x, linear_offset_y;
@@ -3579,7 +3577,7 @@ static void partial_buffer_update_rect_thread_do(void *data_v, int scanline)
static void imb_partial_display_buffer_update_ex(
ImBuf *ibuf,
const float *linear_buffer,
- const unsigned char *byte_buffer,
+ const uchar *byte_buffer,
int stride,
int offset_x,
int offset_y,
@@ -3594,7 +3592,7 @@ static void imb_partial_display_buffer_update_ex(
ColormanageCacheViewSettings cache_view_settings;
ColormanageCacheDisplaySettings cache_display_settings;
void *cache_handle = NULL;
- unsigned char *display_buffer = NULL;
+ uchar *display_buffer = NULL;
int buffer_width = ibuf->x;
if (ibuf->display_buffer_flags) {
@@ -3620,7 +3618,7 @@ static void imb_partial_display_buffer_update_ex(
buffer_width = ibuf->x;
/* Mark all other buffers as invalid. */
- memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(unsigned int));
+ memset(ibuf->display_buffer_flags, 0, global_tot_display * sizeof(uint));
ibuf->display_buffer_flags[display_index] |= view_flag;
BLI_thread_unlock(LOCK_COLORMANAGE);
@@ -3688,7 +3686,7 @@ static void imb_partial_display_buffer_update_ex(
void IMB_partial_display_buffer_update(ImBuf *ibuf,
const float *linear_buffer,
- const unsigned char *byte_buffer,
+ const uchar *byte_buffer,
int stride,
int offset_x,
int offset_y,
@@ -3717,7 +3715,7 @@ void IMB_partial_display_buffer_update(ImBuf *ibuf,
void IMB_partial_display_buffer_update_threaded(
struct ImBuf *ibuf,
const float *linear_buffer,
- const unsigned char *byte_buffer,
+ const uchar *byte_buffer,
int stride,
int offset_x,
int offset_y,
@@ -3924,7 +3922,7 @@ void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor,
}
void IMB_colormanagement_processor_apply_byte(
- ColormanageProcessor *cm_processor, unsigned char *buffer, int width, int height, int channels)
+ ColormanageProcessor *cm_processor, uchar *buffer, int width, int height, int channels)
{
/* TODO(sergey): Would be nice to support arbitrary channels configurations,
* but for now it's not so important.
diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c
index 668307ec802..df513a7330c 100644
--- a/source/blender/imbuf/intern/colormanagement_inline.c
+++ b/source/blender/imbuf/intern/colormanagement_inline.c
@@ -11,12 +11,17 @@
#include "BLI_math_vector.h"
#include "IMB_colormanagement_intern.h"
+void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3])
+{
+ copy_v3_v3(r_rgb, imbuf_luma_coefficients);
+}
+
float IMB_colormanagement_get_luminance(const float rgb[3])
{
return dot_v3v3(imbuf_luma_coefficients, rgb);
}
-unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3])
+uchar IMB_colormanagement_get_luminance_byte(const uchar rgb[3])
{
float rgbf[3];
float val;
diff --git a/source/blender/imbuf/intern/dds/BlockDXT.cpp b/source/blender/imbuf/intern/dds/BlockDXT.cpp
index 4048a78e5cf..2d198135a66 100644
--- a/source/blender/imbuf/intern/dds/BlockDXT.cpp
+++ b/source/blender/imbuf/intern/dds/BlockDXT.cpp
@@ -34,6 +34,8 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE. */
+#include <BLI_sys_types.h> /* For `uint`. */
+
#include <BlockDXT.h>
#include <ColorBlock.h>
#include <Common.h>
@@ -576,7 +578,7 @@ void mem_read(Stream &mem, BlockDXT1 &block)
void mem_read(Stream &mem, AlphaBlockDXT3 &block)
{
- for (unsigned short &alpha : block.row) {
+ for (ushort &alpha : block.row) {
mem_read(mem, alpha);
}
}
diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
index ce5dd4927be..39432827467 100644
--- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
+++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp
@@ -867,7 +867,7 @@ uint DDSHeader::d3d9Format() const
return findD3D9Format(pf.bitcount, pf.rmask, pf.gmask, pf.bmask, pf.amask);
}
-DirectDrawSurface::DirectDrawSurface(unsigned char *mem, uint size) : stream(mem, size), header()
+DirectDrawSurface::DirectDrawSurface(uchar *mem, uint size) : stream(mem, size), header()
{
mem_read(stream, header);
@@ -942,7 +942,7 @@ bool DirectDrawSurface::isSupported() const
if (isTextureCube() &&
(header.caps.caps2 & DDSCAPS2_CUBEMAP_ALL_FACES) != DDSCAPS2_CUBEMAP_ALL_FACES) {
- /* Cubemaps must contain all faces. */
+ /* Cube-maps must contain all faces. */
return false;
}
@@ -1112,7 +1112,7 @@ void *DirectDrawSurface::readData(uint &rsize)
uint size = stream.size - header_size;
rsize = size;
- unsigned char *data = (unsigned char *)malloc(sizeof(*data) * size);
+ uchar *data = (uchar *)malloc(sizeof(*data) * size);
stream.seek(header_size);
mem_read(stream, data, size);
@@ -1158,7 +1158,7 @@ void DirectDrawSurface::readLinearImage(Image *img)
for (uint y = 0; y < h; y++) {
for (uint x = 0; x < w; x++) {
uint c = 0;
- mem_read(stream, (unsigned char *)(&c), byteCount);
+ mem_read(stream, (uchar *)(&c), byteCount);
Color32 pixel(0, 0, 0, 0xFF);
pixel.r = PixelFormat::convert((c & header.pf.rmask) >> rshift, rsize, 8);
diff --git a/source/blender/imbuf/intern/dds/FlipDXT.cpp b/source/blender/imbuf/intern/dds/FlipDXT.cpp
index fc978bff788..3d2b7e51a46 100644
--- a/source/blender/imbuf/intern/dds/FlipDXT.cpp
+++ b/source/blender/imbuf/intern/dds/FlipDXT.cpp
@@ -104,19 +104,19 @@ static void FlipDXT5BlockFull(uint8_t *block)
* bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * (bits_3 +
* 256 * (bits_4 + 256 * bits_5))))
*
- * bits is a 48-bit unsigned integer, from which a three-bit control code
+ * bits is a 48-bit unsigned-integer, from which a three-bit control code
* is extracted for a texel at location (x,y) in the block using:
*
* code(x,y) = bits[3*(4*y+x)+1..3*(4*y+x)+0]
*
* where bit 47 is the most significant and bit 0 is the least
* significant bit. */
- unsigned int line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]);
- unsigned int line_2_3 = block[5] + 256 * (block[6] + 256 * block[7]);
+ uint line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]);
+ uint line_2_3 = block[5] + 256 * (block[6] + 256 * block[7]);
/* swap lines 0 and 1 in line_0_1. */
- unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12);
+ uint line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12);
/* swap lines 2 and 3 in line_2_3. */
- unsigned int line_3_2 = ((line_2_3 & 0x000fff) << 12) | ((line_2_3 & 0xfff000) >> 12);
+ uint line_3_2 = ((line_2_3 & 0x000fff) << 12) | ((line_2_3 & 0xfff000) >> 12);
block[2] = line_3_2 & 0xff;
block[3] = (line_3_2 & 0xff00) >> 8;
@@ -133,21 +133,21 @@ static void FlipDXT5BlockFull(uint8_t *block)
static void FlipDXT5BlockHalf(uint8_t *block)
{
/* See layout above. */
- unsigned int line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]);
- unsigned int line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12);
+ uint line_0_1 = block[2] + 256 * (block[3] + 256 * block[4]);
+ uint line_1_0 = ((line_0_1 & 0x000fff) << 12) | ((line_0_1 & 0xfff000) >> 12);
block[2] = line_1_0 & 0xff;
block[3] = (line_1_0 & 0xff00) >> 8;
block[4] = (line_1_0 & 0xff0000) >> 16;
FlipDXT1BlockHalf(block + 8);
}
-int FlipDXTCImage(unsigned int width,
- unsigned int height,
- unsigned int levels,
+int FlipDXTCImage(uint width,
+ uint height,
+ uint levels,
int fourcc,
uint8_t *data,
int data_size,
- unsigned int *r_num_valid_levels)
+ uint *r_num_valid_levels)
{
*r_num_valid_levels = 0;
@@ -162,7 +162,7 @@ int FlipDXTCImage(unsigned int width,
FlipBlockFunction full_block_function;
FlipBlockFunction half_block_function;
- unsigned int block_bytes = 0;
+ uint block_bytes = 0;
switch (fourcc) {
case FOURCC_DXT1:
@@ -186,15 +186,15 @@ int FlipDXTCImage(unsigned int width,
*r_num_valid_levels = levels;
- unsigned int mip_width = width;
- unsigned int mip_height = height;
+ uint mip_width = width;
+ uint mip_height = height;
const uint8_t *data_end = data + data_size;
- for (unsigned int i = 0; i < levels; i++) {
- unsigned int blocks_per_row = (mip_width + 3) / 4;
- unsigned int blocks_per_col = (mip_height + 3) / 4;
- unsigned int blocks = blocks_per_row * blocks_per_col;
+ for (uint i = 0; i < levels; i++) {
+ uint blocks_per_row = (mip_width + 3) / 4;
+ uint blocks_per_col = (mip_height + 3) / 4;
+ uint blocks = blocks_per_row * blocks_per_col;
if (data + block_bytes * blocks > data_end) {
/* Stop flipping when running out of data to be modified, avoiding possible buffer overrun
@@ -209,23 +209,23 @@ int FlipDXTCImage(unsigned int width,
}
if (mip_height == 2) {
/* flip the first 2 lines in each block. */
- for (unsigned int i = 0; i < blocks_per_row; i++) {
+ for (uint i = 0; i < blocks_per_row; i++) {
half_block_function(data + i * block_bytes);
}
}
else {
/* flip each block. */
- for (unsigned int i = 0; i < blocks; i++) {
+ for (uint i = 0; i < blocks; i++) {
full_block_function(data + i * block_bytes);
}
/* Swap each block line in the first half of the image with the
* corresponding one in the second half.
* note that this is a no-op if mip_height is 4. */
- unsigned int row_bytes = block_bytes * blocks_per_row;
+ uint row_bytes = block_bytes * blocks_per_row;
uint8_t *temp_line = new uint8_t[row_bytes];
- for (unsigned int y = 0; y < blocks_per_col / 2; y++) {
+ for (uint y = 0; y < blocks_per_col / 2; y++) {
uint8_t *line1 = data + y * row_bytes;
uint8_t *line2 = data + (blocks_per_col - y - 1) * row_bytes;
diff --git a/source/blender/imbuf/intern/dds/Stream.cpp b/source/blender/imbuf/intern/dds/Stream.cpp
index 566891dac8b..44b7e6d8f42 100644
--- a/source/blender/imbuf/intern/dds/Stream.cpp
+++ b/source/blender/imbuf/intern/dds/Stream.cpp
@@ -4,6 +4,8 @@
* \ingroup imbdds
*/
+#include "BLI_sys_types.h" /* For `uint`. */
+
#include <Stream.h>
#include <cstdio> /* printf */
@@ -12,7 +14,7 @@
static const char *msg_error_seek = "DDS: trying to seek beyond end of stream (corrupt file?)";
static const char *msg_error_read = "DDS: trying to read beyond end of stream (corrupt file?)";
-inline bool is_read_within_bounds(const Stream &mem, unsigned int count)
+inline bool is_read_within_bounds(const Stream &mem, uint count)
{
if (mem.pos >= mem.size) {
/* No more data remained in the memory buffer. */
@@ -27,7 +29,7 @@ inline bool is_read_within_bounds(const Stream &mem, unsigned int count)
return true;
}
-unsigned int Stream::seek(unsigned int p)
+uint Stream::seek(uint p)
{
if (p > size) {
set_failed(msg_error_seek);
@@ -39,7 +41,7 @@ unsigned int Stream::seek(unsigned int p)
return pos;
}
-unsigned int mem_read(Stream &mem, unsigned long long &i)
+uint mem_read(Stream &mem, unsigned long long &i)
{
if (!is_read_within_bounds(mem, 8)) {
mem.set_failed(msg_error_seek);
@@ -50,7 +52,7 @@ unsigned int mem_read(Stream &mem, unsigned long long &i)
return 8;
}
-unsigned int mem_read(Stream &mem, unsigned int &i)
+uint mem_read(Stream &mem, uint &i)
{
if (!is_read_within_bounds(mem, 4)) {
mem.set_failed(msg_error_read);
@@ -61,7 +63,7 @@ unsigned int mem_read(Stream &mem, unsigned int &i)
return 4;
}
-unsigned int mem_read(Stream &mem, unsigned short &i)
+uint mem_read(Stream &mem, ushort &i)
{
if (!is_read_within_bounds(mem, 2)) {
mem.set_failed(msg_error_read);
@@ -72,7 +74,7 @@ unsigned int mem_read(Stream &mem, unsigned short &i)
return 2;
}
-unsigned int mem_read(Stream &mem, unsigned char &i)
+uint mem_read(Stream &mem, uchar &i)
{
if (!is_read_within_bounds(mem, 1)) {
mem.set_failed(msg_error_read);
@@ -83,7 +85,7 @@ unsigned int mem_read(Stream &mem, unsigned char &i)
return 1;
}
-unsigned int mem_read(Stream &mem, unsigned char *i, unsigned int count)
+uint mem_read(Stream &mem, uchar *i, uint count)
{
if (!is_read_within_bounds(mem, count)) {
mem.set_failed(msg_error_read);
diff --git a/source/blender/imbuf/intern/dds/dds_api.cpp b/source/blender/imbuf/intern/dds/dds_api.cpp
index e9a13573116..213e10cf744 100644
--- a/source/blender/imbuf/intern/dds/dds_api.cpp
+++ b/source/blender/imbuf/intern/dds/dds_api.cpp
@@ -58,7 +58,7 @@ bool imb_save_dds(struct ImBuf *ibuf, const char *name, int /*flags*/)
return true;
}
-bool imb_is_a_dds(const unsigned char *mem, const size_t size)
+bool imb_is_a_dds(const uchar *mem, const size_t size)
{
if (size < 8) {
return false;
@@ -75,19 +75,16 @@ bool imb_is_a_dds(const unsigned char *mem, const size_t size)
return true;
}
-struct ImBuf *imb_load_dds(const unsigned char *mem,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+struct ImBuf *imb_load_dds(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
struct ImBuf *ibuf = nullptr;
- DirectDrawSurface dds((unsigned char *)mem, size); /* reads header */
- unsigned char bits_per_pixel;
- unsigned int *rect;
+ DirectDrawSurface dds((uchar *)mem, size); /* reads header */
+ uchar bits_per_pixel;
+ uint *rect;
Image img;
- unsigned int numpixels = 0;
+ uint numpixels = 0;
int col;
- unsigned char *cp = (unsigned char *)&col;
+ uchar *cp = (uchar *)&col;
Color32 pixel;
Color32 *pixels = nullptr;
@@ -128,7 +125,7 @@ struct ImBuf *imb_load_dds(const unsigned char *mem,
bits_per_pixel = 24;
if (img.format() == Image::Format_ARGB) {
/* check that there is effectively an alpha channel */
- for (unsigned int i = 0; i < numpixels; i++) {
+ for (uint i = 0; i < numpixels; i++) {
pixel = pixels[i];
if (pixel.a != 255) {
bits_per_pixel = 32;
@@ -156,7 +153,7 @@ struct ImBuf *imb_load_dds(const unsigned char *mem,
rect = ibuf->rect;
cp[3] = 0xff; /* default alpha if alpha channel is not present */
- for (unsigned int i = 0; i < numpixels; i++) {
+ for (uint i = 0; i < numpixels; i++) {
pixel = pixels[i];
cp[0] = pixel.r; /* set R component of col */
cp[1] = pixel.g; /* set G component of col */
@@ -168,7 +165,7 @@ struct ImBuf *imb_load_dds(const unsigned char *mem,
}
if (ibuf->dds_data.fourcc != FOURCC_DDS) {
- ibuf->dds_data.data = (unsigned char *)dds.readData(ibuf->dds_data.size);
+ ibuf->dds_data.data = (uchar *)dds.readData(ibuf->dds_data.size);
/* flip compressed texture */
if (ibuf->dds_data.data) {
diff --git a/source/blender/imbuf/intern/divers.c b/source/blender/imbuf/intern/divers.c
index 588c92d748d..61ef9c111d7 100644
--- a/source/blender/imbuf/intern/divers.c
+++ b/source/blender/imbuf/intern/divers.c
@@ -48,7 +48,7 @@ static void clear_dither_context(DitherContext *di)
/** \name Generic Buffer Conversion
* \{ */
-MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4])
+MINLINE void ushort_to_byte_v4(uchar b[4], const ushort us[4])
{
b[0] = unit_ushort_to_uchar(us[0]);
b[1] = unit_ushort_to_uchar(us[1]);
@@ -56,13 +56,13 @@ MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4])
b[3] = unit_ushort_to_uchar(us[3]);
}
-MINLINE unsigned char ftochar(float value)
+MINLINE uchar ftochar(float value)
{
return unit_float_to_uchar_clamp(value);
}
MINLINE void ushort_to_byte_dither_v4(
- uchar b[4], const unsigned short us[4], DitherContext *di, float s, float t)
+ uchar b[4], const ushort us[4], DitherContext *di, float s, float t)
{
#define USHORTTOFLOAT(val) ((float)val / 65535.0f)
float dither_value = dither_random_value(s, t) * 0.0033f * di->dither;
@@ -192,7 +192,7 @@ void IMB_buffer_byte_from_float(uchar *rect_to,
}
else if (profile_to == IB_PROFILE_SRGB) {
/* convert from linear to sRGB */
- unsigned short us[4];
+ ushort us[4];
float straight[4];
if (dither && predivide) {
@@ -695,9 +695,6 @@ void IMB_buffer_byte_from_byte(uchar *rect_to,
void IMB_rect_from_float(ImBuf *ibuf)
{
- float *buffer;
- const char *from_colorspace;
-
/* verify we have a float buffer */
if (ibuf->rect_float == NULL) {
return;
@@ -710,24 +707,21 @@ void IMB_rect_from_float(ImBuf *ibuf)
}
}
- if (ibuf->float_colorspace == NULL) {
- from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
- }
- else {
- from_colorspace = ibuf->float_colorspace->name;
- }
+ const char *from_colorspace = (ibuf->float_colorspace == NULL) ?
+ IMB_colormanagement_role_colorspace_name_get(
+ COLOR_ROLE_SCENE_LINEAR) :
+ ibuf->float_colorspace->name;
+ const char *to_colorspace = (ibuf->rect_colorspace == NULL) ?
+ IMB_colormanagement_role_colorspace_name_get(
+ COLOR_ROLE_DEFAULT_BYTE) :
+ ibuf->rect_colorspace->name;
- buffer = MEM_dupallocN(ibuf->rect_float);
+ float *buffer = MEM_dupallocN(ibuf->rect_float);
/* first make float buffer in byte space */
const bool predivide = IMB_alpha_affects_rgb(ibuf);
- IMB_colormanagement_transform(buffer,
- ibuf->x,
- ibuf->y,
- ibuf->channels,
- from_colorspace,
- ibuf->rect_colorspace->name,
- predivide);
+ IMB_colormanagement_transform(
+ buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, to_colorspace, predivide);
/* convert from float's premul alpha to byte's straight alpha */
if (IMB_alpha_affects_rgb(ibuf)) {
@@ -735,7 +729,7 @@ void IMB_rect_from_float(ImBuf *ibuf)
}
/* convert float to byte */
- IMB_buffer_byte_from_float((unsigned char *)ibuf->rect,
+ IMB_buffer_byte_from_float((uchar *)ibuf->rect,
buffer,
ibuf->channels,
ibuf->dither,
@@ -774,7 +768,7 @@ void IMB_float_from_rect_ex(struct ImBuf *dst,
float *rect_float = dst->rect_float;
rect_float += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4;
- unsigned char *rect = (unsigned char *)src->rect;
+ uchar *rect = (uchar *)src->rect;
rect += (region_to_update->xmin + region_to_update->ymin * dst->x) * 4;
const int region_width = BLI_rcti_size_x(region_to_update);
const int region_height = BLI_rcti_size_y(region_to_update);
@@ -895,7 +889,7 @@ void IMB_buffer_float_premultiply(float *buf, int width, int height)
void IMB_saturation(ImBuf *ibuf, float sat)
{
size_t i;
- unsigned char *rct = (unsigned char *)ibuf->rect;
+ uchar *rct = (uchar *)ibuf->rect;
float *rct_fl = ibuf->rect_float;
float hsv[3];
diff --git a/source/blender/imbuf/intern/filetype.c b/source/blender/imbuf/intern/filetype.c
index 92fa980cd7f..e1d2bea4ae9 100644
--- a/source/blender/imbuf/intern/filetype.c
+++ b/source/blender/imbuf/intern/filetype.c
@@ -217,7 +217,7 @@ const ImFileType IMB_FILE_TYPES[] = {
.is_a = imb_is_a_webp,
.load = imb_loadwebp,
.load_filepath = NULL,
- .load_filepath_thumbnail = NULL,
+ .load_filepath_thumbnail = imb_load_filepath_thumbnail_webp,
.save = imb_savewebp,
.load_tile = NULL,
.flag = 0,
diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c
index 91c69d3abc8..67de467bd93 100644
--- a/source/blender/imbuf/intern/filter.c
+++ b/source/blender/imbuf/intern/filter.c
@@ -18,9 +18,9 @@
#include "imbuf.h"
-static void filtrow(unsigned char *point, int x)
+static void filtrow(uchar *point, int x)
{
- unsigned int c1, c2, c3, error;
+ uint c1, c2, c3, error;
if (x > 1) {
c1 = c2 = *point;
@@ -56,10 +56,10 @@ static void filtrowf(float *point, int x)
}
}
-static void filtcolum(unsigned char *point, int y, int skip)
+static void filtcolum(uchar *point, int y, int skip)
{
- unsigned int c1, c2, c3, error;
- unsigned char *point2;
+ uint c1, c2, c3, error;
+ uchar *point2;
if (y > 1) {
c1 = c2 = *point;
@@ -101,11 +101,11 @@ static void filtcolumf(float *point, int y, int skip)
void IMB_filtery(struct ImBuf *ibuf)
{
- unsigned char *point;
+ uchar *point;
float *pointf;
int x, y, skip;
- point = (unsigned char *)ibuf->rect;
+ point = (uchar *)ibuf->rect;
pointf = ibuf->rect_float;
x = ibuf->x;
@@ -142,11 +142,11 @@ void IMB_filtery(struct ImBuf *ibuf)
void imb_filterx(struct ImBuf *ibuf)
{
- unsigned char *point;
+ uchar *point;
float *pointf;
int x, y, skip;
- point = (unsigned char *)ibuf->rect;
+ point = (uchar *)ibuf->rect;
pointf = ibuf->rect_float;
x = ibuf->x;
@@ -395,7 +395,7 @@ static int check_pixel_assigned(
res = mask[index] != 0 ? 1 : 0;
}
else if ((is_float && ((const float *)buffer)[alpha_index] != 0.0f) ||
- (!is_float && ((const unsigned char *)buffer)[alpha_index] != 0)) {
+ (!is_float && ((const uchar *)buffer)[alpha_index] != 0)) {
res = 1;
}
}
@@ -408,7 +408,7 @@ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter)
const int width = ibuf->x;
const int height = ibuf->y;
const int depth = 4; /* always 4 channels */
- const int chsize = ibuf->rect_float ? sizeof(float) : sizeof(unsigned char);
+ const int chsize = ibuf->rect_float ? sizeof(float) : sizeof(uchar);
const size_t bsize = ((size_t)width) * height * depth * chsize;
const bool is_float = (ibuf->rect_float != NULL);
void *dstbuf = (void *)MEM_dupallocN(ibuf->rect_float ? (void *)ibuf->rect_float :
@@ -478,7 +478,7 @@ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter)
}
else {
for (c = 0; c < depth; c++) {
- tmp[c] = (float)((const unsigned char *)srcbuf)[depth * tmpindex + c];
+ tmp[c] = (float)((const uchar *)srcbuf)[depth * tmpindex + c];
}
}
@@ -505,8 +505,10 @@ void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter)
}
else {
for (c = 0; c < depth; c++) {
- ((unsigned char *)dstbuf)[depth * index + c] =
- acc[c] > 255 ? 255 : (acc[c] < 0 ? 0 : (unsigned char)roundf(acc[c]));
+ ((uchar *)dstbuf)[depth * index + c] = acc[c] > 255 ?
+ 255 :
+ (acc[c] < 0 ? 0 :
+ (uchar)roundf(acc[c]));
}
}
@@ -613,7 +615,7 @@ ImBuf *IMB_getmipmap(ImBuf *ibuf, int level)
return (level == 0) ? ibuf : ibuf->mipmap[level - 1];
}
-void IMB_premultiply_rect(unsigned int *rect, char planes, int w, int h)
+void IMB_premultiply_rect(uint *rect, char planes, int w, int h)
{
char *cp;
int x, y, val;
@@ -674,7 +676,7 @@ void IMB_premultiply_alpha(ImBuf *ibuf)
}
}
-void IMB_unpremultiply_rect(unsigned int *rect, char planes, int w, int h)
+void IMB_unpremultiply_rect(uint *rect, char planes, int w, int h)
{
char *cp;
int x, y;
diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c
index 13bf3697946..f4775fa5fe8 100644
--- a/source/blender/imbuf/intern/imageprocess.c
+++ b/source/blender/imbuf/intern/imageprocess.c
@@ -26,7 +26,7 @@
void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf)
{
size_t size;
- unsigned char rt, *cp = (unsigned char *)ibuf->rect;
+ uchar rt, *cp = (uchar *)ibuf->rect;
float rtf, *cpf = ibuf->rect_float;
if (ibuf->rect) {
@@ -58,14 +58,13 @@ void IMB_convert_rgba_to_abgr(struct ImBuf *ibuf)
}
}
-static void pixel_from_buffer(
- const struct ImBuf *ibuf, unsigned char **outI, float **outF, int x, int y)
+static void pixel_from_buffer(const struct ImBuf *ibuf, uchar **outI, float **outF, int x, int y)
{
size_t offset = ((size_t)ibuf->x) * y * 4 + 4 * x;
if (ibuf->rect) {
- *outI = (unsigned char *)ibuf->rect + offset;
+ *outI = (uchar *)ibuf->rect + offset;
}
if (ibuf->rect_float) {
@@ -78,19 +77,19 @@ static void pixel_from_buffer(
* \{ */
void bicubic_interpolation_color(
- const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+ const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
if (outF) {
BLI_bicubic_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v);
}
else {
- BLI_bicubic_interpolation_char((unsigned char *)in->rect, outI, in->x, in->y, 4, u, v);
+ BLI_bicubic_interpolation_char((uchar *)in->rect, outI, in->x, in->y, 4, u, v);
}
}
void bicubic_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout)
{
- unsigned char *outI = NULL;
+ uchar *outI = NULL;
float *outF = NULL;
if (in == NULL || (in->rect == NULL && in->rect_float == NULL)) {
@@ -110,7 +109,7 @@ void bicubic_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xo
* \{ */
void bilinear_interpolation_color_fl(
- const struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v)
+ const struct ImBuf *in, uchar UNUSED(outI[4]), float outF[4], float u, float v)
{
BLI_assert(outF);
BLI_assert(in->rect_float);
@@ -118,21 +117,21 @@ void bilinear_interpolation_color_fl(
}
void bilinear_interpolation_color_char(
- const struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v)
+ const struct ImBuf *in, uchar 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);
+ BLI_bilinear_interpolation_char((uchar *)in->rect, outI, in->x, in->y, 4, u, v);
}
void bilinear_interpolation_color(
- const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+ const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
if (outF) {
BLI_bilinear_interpolation_fl(in->rect_float, outF, in->x, in->y, 4, u, v);
}
else {
- BLI_bilinear_interpolation_char((unsigned char *)in->rect, outI, in->x, in->y, 4, u, v);
+ BLI_bilinear_interpolation_char((uchar *)in->rect, outI, in->x, in->y, 4, u, v);
}
}
@@ -140,10 +139,10 @@ void bilinear_interpolation_color(
/* BILINEAR INTERPOLATION */
void bilinear_interpolation_color_wrap(
- const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+ const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
float *row1, *row2, *row3, *row4, a, b;
- unsigned char *row1I, *row2I, *row3I, *row4I;
+ uchar *row1I, *row2I, *row3I, *row4I;
float a_b, ma_b, a_mb, ma_mb;
int y1, y2, x1, x2;
@@ -198,10 +197,10 @@ void bilinear_interpolation_color_wrap(
}
if (outI) {
/* sample including outside of edges of image */
- row1I = (unsigned char *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x1;
- row2I = (unsigned char *)in->rect + ((size_t)in->x) * y2 * 4 + 4 * x1;
- row3I = (unsigned char *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x2;
- row4I = (unsigned char *)in->rect + ((size_t)in->x) * y2 * 4 + 4 * x2;
+ row1I = (uchar *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x1;
+ row2I = (uchar *)in->rect + ((size_t)in->x) * y2 * 4 + 4 * x1;
+ row3I = (uchar *)in->rect + ((size_t)in->x) * y1 * 4 + 4 * x2;
+ row4I = (uchar *)in->rect + ((size_t)in->x) * y2 * 4 + 4 * x2;
/* Tested with white images and this should not wrap back to zero. */
outI[0] = roundf(ma_mb * row1I[0] + a_mb * row3I[0] + ma_b * row2I[0] + a_b * row4I[0]);
@@ -213,14 +212,14 @@ void bilinear_interpolation_color_wrap(
void bilinear_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout)
{
- unsigned char *outI = NULL;
+ uchar *outI = NULL;
float *outF = NULL;
if (in == NULL || (in->rect == NULL && in->rect_float == NULL)) {
return;
}
- /* gcc warns these could be uninitialized, but its ok. */
+ /* GCC warns these could be uninitialized, but its ok. */
pixel_from_buffer(out, &outI, &outF, xout, yout);
bilinear_interpolation_color(in, outI, outF, u, v);
@@ -233,7 +232,7 @@ void bilinear_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int x
* \{ */
void nearest_interpolation_color_char(
- const struct ImBuf *in, unsigned char outI[4], float UNUSED(outF[4]), float u, float v)
+ const struct ImBuf *in, uchar outI[4], float UNUSED(outF[4]), float u, float v)
{
BLI_assert(outI);
BLI_assert(in->rect);
@@ -248,7 +247,7 @@ void nearest_interpolation_color_char(
}
const size_t offset = ((size_t)in->x * y1 + x1) * 4;
- const unsigned char *dataI = (unsigned char *)in->rect + offset;
+ const uchar *dataI = (uchar *)in->rect + offset;
outI[0] = dataI[0];
outI[1] = dataI[1];
outI[2] = dataI[2];
@@ -256,7 +255,7 @@ void nearest_interpolation_color_char(
}
void nearest_interpolation_color_fl(
- const struct ImBuf *in, unsigned char UNUSED(outI[4]), float outF[4], float u, float v)
+ const struct ImBuf *in, uchar UNUSED(outI[4]), float outF[4], float u, float v)
{
BLI_assert(outF);
BLI_assert(in->rect_float);
@@ -276,7 +275,7 @@ void nearest_interpolation_color_fl(
}
void nearest_interpolation_color(
- const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+ const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
if (outF) {
nearest_interpolation_color_fl(in, outI, outF, u, v);
@@ -287,10 +286,10 @@ void nearest_interpolation_color(
}
void nearest_interpolation_color_wrap(
- const struct ImBuf *in, unsigned char outI[4], float outF[4], float u, float v)
+ const struct ImBuf *in, uchar outI[4], float outF[4], float u, float v)
{
const float *dataF;
- unsigned char *dataI;
+ uchar *dataI;
int y, x;
/* ImBuf in must have a valid rect or rect_float, assume this is already checked */
@@ -309,7 +308,7 @@ void nearest_interpolation_color_wrap(
y += in->y;
}
- dataI = (unsigned char *)in->rect + ((size_t)in->x) * y * 4 + 4 * x;
+ dataI = (uchar *)in->rect + ((size_t)in->x) * y * 4 + 4 * x;
if (outI) {
outI[0] = dataI[0];
outI[1] = dataI[1];
@@ -327,7 +326,7 @@ void nearest_interpolation_color_wrap(
void nearest_interpolation(const ImBuf *in, ImBuf *out, float u, float v, int xout, int yout)
{
- unsigned char *outI = NULL;
+ uchar *outI = NULL;
float *outF = NULL;
if (in == NULL || (in->rect == NULL && in->rect_float == NULL)) {
@@ -446,10 +445,10 @@ void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[
}
}
-void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, const float backcol[3])
+void IMB_alpha_under_color_byte(uchar *rect, int x, int y, const float backcol[3])
{
size_t a = ((size_t)x) * y;
- unsigned char *cp = rect;
+ uchar *cp = rect;
while (a--) {
if (cp[3] == 255) {
@@ -487,7 +486,7 @@ void IMB_sampleImageAtLocation(ImBuf *ibuf, float x, float y, bool make_linear_r
nearest_interpolation_color(ibuf, NULL, color, x, y);
}
else {
- unsigned char byte_color[4];
+ uchar byte_color[4];
nearest_interpolation_color(ibuf, byte_color, NULL, x, y);
rgba_uchar_to_float(color, byte_color);
if (make_linear_rgb) {
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index cbc5d984755..a4f1f5e813a 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -1012,7 +1012,7 @@ static int index_rebuild_ffmpeg(FFmpegIndexBuilderContext *context,
stream_size = avio_size(context->iFormatCtx->pb);
- context->frame_rate = av_q2d(context->iStream->r_frame_rate);
+ 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) {
@@ -1098,6 +1098,7 @@ static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *contex
while (av_read_frame(context->iFormatCtx, packet) >= 0) {
if (packet->stream_index != context->videoStream) {
+ av_packet_unref(packet);
continue;
}
@@ -1121,6 +1122,7 @@ static int indexer_performance_get_decode_rate(FFmpegIndexBuilderContext *contex
if (end > start + time_period) {
break;
}
+ av_packet_unref(packet);
}
av_packet_free(&packet);
@@ -1145,6 +1147,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte
while (av_read_frame(context->iFormatCtx, packet) >= 0) {
if (packet->stream_index != context->videoStream) {
+ av_packet_unref(packet);
continue;
}
packet_index++;
@@ -1158,6 +1161,7 @@ static int indexer_performance_get_max_gop_size(FFmpegIndexBuilderContext *conte
if (packet_index > packets_max) {
break;
}
+ av_packet_unref(packet);
}
av_packet_free(&packet);
diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c
index a8150fd1648..29dfcc1b8f8 100644
--- a/source/blender/imbuf/intern/iris.c
+++ b/source/blender/imbuf/intern/iris.c
@@ -88,7 +88,7 @@ typedef struct MFileOffset {
#define DIRTY_FLAG_EOF (1 << 0)
#define DIRTY_FLAG_ENCODING (1 << 1)
-/* funcs */
+/* Functions. */
static void readheader(MFileOffset *inf, IMAGE *image);
static int writeheader(FILE *outf, IMAGE *image);
diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c
index a14c94d5d62..f57d4382672 100644
--- a/source/blender/imbuf/intern/jp2.c
+++ b/source/blender/imbuf/intern/jp2.c
@@ -39,7 +39,7 @@ typedef struct img_folder {
float *rates;
} img_fol_t;
-static bool check_jp2(const unsigned char *mem, const size_t size) /* J2K_CFMT */
+static bool check_jp2(const uchar *mem, const size_t size) /* J2K_CFMT */
{
if (size < sizeof(JP2_HEAD)) {
return false;
@@ -47,7 +47,7 @@ static bool check_jp2(const unsigned char *mem, const size_t size) /* J2K_CFMT *
return memcmp(JP2_HEAD, mem, sizeof(JP2_HEAD)) ? 0 : 1;
}
-static bool check_j2k(const unsigned char *mem, const size_t size) /* J2K_CFMT */
+static bool check_j2k(const uchar *mem, const size_t size) /* J2K_CFMT */
{
if (size < sizeof(J2K_HEAD)) {
return false;
@@ -55,8 +55,7 @@ static bool check_j2k(const unsigned char *mem, const size_t size) /* J2K_CFMT *
return memcmp(J2K_HEAD, mem, sizeof(J2K_HEAD)) ? 0 : 1;
}
-static OPJ_CODEC_FORMAT format_from_header(const unsigned char mem[JP2_FILEHEADER_SIZE],
- const size_t size)
+static OPJ_CODEC_FORMAT format_from_header(const uchar mem[JP2_FILEHEADER_SIZE], const size_t size)
{
if (check_jp2(mem, size)) {
return OPJ_CODEC_JP2;
@@ -68,7 +67,7 @@ static OPJ_CODEC_FORMAT format_from_header(const unsigned char mem[JP2_FILEHEADE
return OPJ_CODEC_UNKNOWN;
}
-bool imb_is_a_jp2(const unsigned char *buf, size_t size)
+bool imb_is_a_jp2(const uchar *buf, size_t size)
{
return (check_jp2(buf, size) || check_j2k(buf, size));
}
@@ -102,11 +101,11 @@ static void info_callback(const char *msg, void *client_data)
#endif
#define PIXEL_LOOPER_BEGIN(_rect) \
- for (y = h - 1; y != (unsigned int)(-1); y--) { \
+ for (y = h - 1; y != (uint)(-1); y--) { \
for (i = y * w, i_next = (y + 1) * w; i < i_next; i++, _rect += 4) {
#define PIXEL_LOOPER_BEGIN_CHANNELS(_rect, _channels) \
- for (y = h - 1; y != (unsigned int)(-1); y--) { \
+ for (y = h - 1; y != (uint)(-1); y--) { \
for (i = y * w, i_next = (y + 1) * w; i < i_next; i++, _rect += _channels) {
#define PIXEL_LOOPER_END \
@@ -119,8 +118,8 @@ static void info_callback(const char *msg, void *client_data)
* \{ */
struct BufInfo {
- const unsigned char *buf;
- const unsigned char *cur;
+ const uchar *buf;
+ const uchar *cur;
OPJ_OFF_T len;
};
@@ -300,10 +299,7 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream,
int flags,
char colorspace[IM_MAX_SPACE]);
-ImBuf *imb_load_jp2(const unsigned char *mem,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+ImBuf *imb_load_jp2(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
const OPJ_CODEC_FORMAT format = (size > JP2_FILEHEADER_SIZE) ? format_from_header(mem, size) :
OPJ_CODEC_UNKNOWN;
@@ -322,7 +318,7 @@ ImBuf *imb_load_jp2(const unsigned char *mem,
ImBuf *imb_load_jp2_filepath(const char *filepath, int flags, char colorspace[IM_MAX_SPACE])
{
FILE *p_file = NULL;
- unsigned char mem[JP2_FILEHEADER_SIZE];
+ uchar mem[JP2_FILEHEADER_SIZE];
opj_stream_t *stream = opj_stream_create_from_file(
filepath, OPJ_J2K_STREAM_CHUNK_SIZE, true, &p_file);
if (stream) {
@@ -358,8 +354,8 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream,
long signed_offsets[4] = {0, 0, 0, 0};
int float_divs[4] = {1, 1, 1, 1};
- unsigned int i, i_next, w, h, planes;
- unsigned int y;
+ uint i, i_next, w, h, planes;
+ uint y;
int *r, *g, *b, *a; /* matching 'opj_image_comp.data' type */
opj_dparameters_t parameters; /* decompression parameters */
@@ -509,7 +505,7 @@ static ImBuf *imb_load_jp2_stream(opj_stream_t *stream,
}
}
else {
- unsigned char *rect_uchar = (unsigned char *)ibuf->rect;
+ uchar *rect_uchar = (uchar *)ibuf->rect;
if (image->numcomps < 3) {
r = image->comps[0].data;
@@ -599,11 +595,11 @@ static opj_image_t *rawtoimage(const char *filename,
(_val) <= 0.0f ? 0 : ((_val) >= 1.0f ? 65535 : (int)(65535.0f * (_val)))
#else
-BLI_INLINE int UPSAMPLE_8_TO_12(const unsigned char _val)
+BLI_INLINE int UPSAMPLE_8_TO_12(const uchar _val)
{
return (_val << 4) | (_val & ((1 << 4) - 1));
}
-BLI_INLINE int UPSAMPLE_8_TO_16(const unsigned char _val)
+BLI_INLINE int UPSAMPLE_8_TO_16(const uchar _val)
{
return (_val << 8) + _val;
}
@@ -811,14 +807,14 @@ static float channel_colormanage_noop(float value)
static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters)
{
- unsigned char *rect_uchar;
+ uchar *rect_uchar;
float *rect_float, from_straight[4];
- unsigned int subsampling_dx = parameters->subsampling_dx;
- unsigned int subsampling_dy = parameters->subsampling_dy;
+ uint subsampling_dx = parameters->subsampling_dx;
+ uint subsampling_dy = parameters->subsampling_dy;
- unsigned int i, i_next, numcomps, w, h, prec;
- unsigned int y;
+ uint i, i_next, numcomps, w, h, prec;
+ uint y;
int *r, *g, *b, *a; /* matching 'opj_image_comp.data' type */
OPJ_COLOR_SPACE color_space;
opj_image_cmptparm_t cmptparm[4]; /* maximum of 4 components */
@@ -910,7 +906,7 @@ static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters)
image->y1 = image->y0 + (h - 1) * subsampling_dy + 1 + image->y0;
/* set image data */
- rect_uchar = (unsigned char *)ibuf->rect;
+ rect_uchar = (uchar *)ibuf->rect;
rect_float = ibuf->rect_float;
/* set the destination channels */
diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c
index 06f9202a1c6..e03765fea92 100644
--- a/source/blender/imbuf/intern/jpeg.c
+++ b/source/blender/imbuf/intern/jpeg.c
@@ -37,7 +37,7 @@ static void init_source(j_decompress_ptr cinfo);
static boolean fill_input_buffer(j_decompress_ptr cinfo);
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes);
static void term_source(j_decompress_ptr cinfo);
-static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size);
+static void memory_source(j_decompress_ptr cinfo, const uchar *buffer, size_t size);
static boolean handle_app1(j_decompress_ptr cinfo);
static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo,
int flags,
@@ -48,7 +48,7 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo,
static const uchar jpeg_default_quality = 75;
static uchar ibuf_quality;
-bool imb_is_a_jpeg(const unsigned char *mem, const size_t size)
+bool imb_is_a_jpeg(const uchar *mem, const size_t size)
{
const char magic[2] = {0xFF, 0xD8};
if (size < sizeof(magic)) {
@@ -89,7 +89,7 @@ static void jpeg_error(j_common_ptr cinfo)
#if 0
typedef struct {
- unsigned char *buffer;
+ uchar *buffer;
int filled;
} buffer_struct;
#endif
@@ -97,7 +97,7 @@ typedef struct {
typedef struct {
struct jpeg_source_mgr pub; /* public fields */
- const unsigned char *buffer;
+ const uchar *buffer;
int size;
JOCTET terminal[2];
} my_source_mgr;
@@ -144,7 +144,7 @@ static void term_source(j_decompress_ptr cinfo)
(void)cinfo; /* unused */
}
-static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, size_t size)
+static void memory_source(j_decompress_ptr cinfo, const uchar *buffer, size_t size)
{
my_src_ptr src;
@@ -205,11 +205,11 @@ static void memory_source(j_decompress_ptr cinfo, const unsigned char *buffer, s
MAKESTMT(MAKE_BYTE_AVAIL(cinfo, action); bytes_in_buffer--; V = GETJOCTET(*next_input_byte++);)
/* As above, but read two bytes interpreted as an unsigned 16-bit integer.
- * V should be declared unsigned int or perhaps INT32.
+ * V should be declared `uint` or perhaps INT32.
*/
#define INPUT_2BYTES(cinfo, V, action) \
MAKESTMT(MAKE_BYTE_AVAIL(cinfo, action); bytes_in_buffer--; \
- V = ((unsigned int)GETJOCTET(*next_input_byte++)) << 8; \
+ V = ((uint)GETJOCTET(*next_input_byte++)) << 8; \
MAKE_BYTE_AVAIL(cinfo, action); \
bytes_in_buffer--; \
V += GETJOCTET(*next_input_byte++);)
@@ -445,10 +445,7 @@ static ImBuf *ibJpegImageFromCinfo(struct jpeg_decompress_struct *cinfo,
return ibuf;
}
-ImBuf *imb_load_jpeg(const unsigned char *buffer,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+ImBuf *imb_load_jpeg(const uchar *buffer, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
struct jpeg_decompress_struct _cinfo, *cinfo = &_cinfo;
struct my_error_mgr jerr;
@@ -521,7 +518,7 @@ struct ImBuf *imb_thumbnail_jpeg(const char *filepath,
if ((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI) &&
(fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_APP1)) {
/* This is a JPEG in EXIF format (SOI + APP1), not JFIF (SOI + APP0). */
- unsigned int i = JPEG_APP1_MAX;
+ uint i = JPEG_APP1_MAX;
/* All EXIF data is within this 64K header segment. Skip ahead until next SOI for thumbnail. */
while (!((fgetc(infile) == JPEG_MARKER_MSB) && (fgetc(infile) == JPEG_MARKER_SOI)) &&
!feof(infile) && i--) {
diff --git a/source/blender/imbuf/intern/moviecache.cc b/source/blender/imbuf/intern/moviecache.cc
index 91a7dfdfae2..54d95578120 100644
--- a/source/blender/imbuf/intern/moviecache.cc
+++ b/source/blender/imbuf/intern/moviecache.cc
@@ -81,7 +81,7 @@ struct MovieCacheItem {
bool added_empty;
};
-static unsigned int moviecache_hashhash(const void *keyv)
+static uint moviecache_hashhash(const void *keyv)
{
const MovieCacheKey *key = (const MovieCacheKey *)keyv;
diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.cpp b/source/blender/imbuf/intern/oiio/openimageio_api.cpp
index e887424d7b2..5c7b7d9fae4 100644
--- a/source/blender/imbuf/intern/oiio/openimageio_api.cpp
+++ b/source/blender/imbuf/intern/oiio/openimageio_api.cpp
@@ -32,7 +32,7 @@ OIIO_NAMESPACE_USING
using std::string;
using std::unique_ptr;
-using uchar = unsigned char;
+using uchar = uchar;
template<class T, class Q>
static void fill_all_channels(T *pixels, int width, int height, int components, Q alpha)
@@ -147,9 +147,9 @@ static ImBuf *imb_oiio_load_image_float(
extern "C" {
-bool imb_is_a_photoshop(const unsigned char *mem, size_t size)
+bool imb_is_a_photoshop(const uchar *mem, size_t size)
{
- const unsigned char magic[4] = {'8', 'B', 'P', 'S'};
+ const uchar magic[4] = {'8', 'B', 'P', 'S'};
if (size < sizeof(magic)) {
return false;
}
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index 0414fa1268d..da4aaf8522e 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -122,8 +122,7 @@ static void imb_exr_type_by_channels(ChannelList &channels,
class IMemStream : public Imf::IStream {
public:
- IMemStream(unsigned char *exrbuf, size_t exrsize)
- : IStream("<memory>"), _exrpos(0), _exrsize(exrsize)
+ IMemStream(uchar *exrbuf, size_t exrsize) : IStream("<memory>"), _exrpos(0), _exrsize(exrsize)
{
_exrbuf = exrbuf;
}
@@ -156,7 +155,7 @@ class IMemStream : public Imf::IStream {
private:
exr_file_offset_t _exrpos;
exr_file_offset_t _exrsize;
- unsigned char *_exrbuf;
+ uchar *_exrbuf;
};
/* Memory-Mapped Input Stream */
@@ -178,7 +177,7 @@ class IMMapStream : public Imf::IStream {
throw IEX_NAMESPACE::InputExc("BLI_mmap_open failed");
}
close(file);
- _exrbuf = (unsigned char *)BLI_mmap_get_pointer(_mmap_file);
+ _exrbuf = (uchar *)BLI_mmap_get_pointer(_mmap_file);
}
~IMMapStream() override
@@ -216,7 +215,7 @@ class IMMapStream : public Imf::IStream {
BLI_mmap_file *_mmap_file;
exr_file_offset_t _exrpos;
exr_file_offset_t _exrsize;
- unsigned char *_exrbuf;
+ uchar *_exrbuf;
};
/* File Input Stream */
@@ -395,7 +394,7 @@ static half float_to_half_safe(const float value)
extern "C" {
-bool imb_is_a_openexr(const unsigned char *mem, const size_t size)
+bool imb_is_a_openexr(const uchar *mem, const size_t size)
{
/* No define is exposed for this size. */
if (size < 4) {
@@ -547,10 +546,10 @@ static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags
}
}
else {
- unsigned char *from;
+ uchar *from;
for (int i = ibuf->y - 1; i >= 0; i--) {
- from = (unsigned char *)ibuf->rect + 4 * i * width;
+ from = (uchar *)ibuf->rect + 4 * i * width;
for (int j = ibuf->x; j > 0; j--) {
to->r = srgb_to_linearrgb((float)from[0] / 255.0f);
@@ -709,7 +708,8 @@ struct ExrHandle {
ListBase channels; /* flattened out, ExrChannel */
ListBase layers; /* hierarchical, pointing in end to ExrChannel */
- int num_half_channels; /* used during filr save, allows faster temporary buffers allocation */
+ /** Used during file save, allows faster temporary buffers allocation. */
+ int num_half_channels;
};
/* flattened out channel */
@@ -1463,7 +1463,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa
const char *end = name + strlen(name);
const char *token;
- /* some multilayers have the combined buffer with names A B G R saved */
+ /* Some multi-layers have the combined buffer with names A B G R saved. */
if (name[1] == 0) {
echan->chan_id = BLI_toupper_ascii(name[0]);
layname[0] = '\0';
@@ -1498,7 +1498,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa
bool ok = false;
if (len == 2) {
- /* some multilayers are using two-letter channels name,
+ /* Some multi-layers are using two-letter channels name,
* like, MX or NZ, which is basically has structure of
* <pass_prefix><component>
*
@@ -1670,29 +1670,29 @@ static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data)
if (ELEM(pass->totchan, 3, 4)) {
if (pass->chan[0]->chan_id == 'B' || pass->chan[1]->chan_id == 'B' ||
pass->chan[2]->chan_id == 'B') {
- lookup[(unsigned int)'R'] = 0;
- lookup[(unsigned int)'G'] = 1;
- lookup[(unsigned int)'B'] = 2;
- lookup[(unsigned int)'A'] = 3;
+ lookup[(uint)'R'] = 0;
+ lookup[(uint)'G'] = 1;
+ lookup[(uint)'B'] = 2;
+ lookup[(uint)'A'] = 3;
}
else if (pass->chan[0]->chan_id == 'Y' || pass->chan[1]->chan_id == 'Y' ||
pass->chan[2]->chan_id == 'Y') {
- lookup[(unsigned int)'X'] = 0;
- lookup[(unsigned int)'Y'] = 1;
- lookup[(unsigned int)'Z'] = 2;
- lookup[(unsigned int)'W'] = 3;
+ lookup[(uint)'X'] = 0;
+ lookup[(uint)'Y'] = 1;
+ lookup[(uint)'Z'] = 2;
+ lookup[(uint)'W'] = 3;
}
else {
- lookup[(unsigned int)'U'] = 0;
- lookup[(unsigned int)'V'] = 1;
- lookup[(unsigned int)'A'] = 2;
+ lookup[(uint)'U'] = 0;
+ lookup[(uint)'V'] = 1;
+ lookup[(uint)'A'] = 2;
}
for (int a = 0; a < pass->totchan; a++) {
echan = pass->chan[a];
- echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id];
+ echan->rect = pass->rect + lookup[(uint)echan->chan_id];
echan->xstride = pass->totchan;
echan->ystride = data->width * pass->totchan;
- pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id;
+ pass->chan_id[(uint)lookup[(uint)echan->chan_id]] = echan->chan_id;
}
}
else { /* unknown */
@@ -1969,7 +1969,7 @@ bool IMB_exr_has_multilayer(void *handle)
return imb_exr_is_multi(*data->ifile);
}
-struct ImBuf *imb_load_openexr(const unsigned char *mem,
+struct ImBuf *imb_load_openexr(const uchar *mem,
size_t size,
int flags,
char colorspace[IM_MAX_SPACE])
@@ -1987,7 +1987,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
try {
bool is_multi;
- membuf = new IMemStream((unsigned char *)mem, size);
+ membuf = new IMemStream((uchar *)mem, size);
file = new MultiPartInputFile(*membuf);
Box2i dw = file->header(0).dataWindow();
@@ -2008,7 +2008,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
printf("Error: can't process EXR multilayer file\n");
}
else {
- const int is_alpha = exr_has_alpha(*file);
+ const bool is_alpha = exr_has_alpha(*file);
ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);
ibuf->flags |= exr_is_half_float(*file) ? IB_halffloat : 0;
@@ -2058,7 +2058,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem,
size_t xstride = sizeof(float[4]);
size_t ystride = -xstride * width;
- imb_addrectfloatImBuf(ibuf);
+ imb_addrectfloatImBuf(ibuf, 4);
/* Inverse correct first pixel for data-window
* coordinates (- dw.min.y because of y flip). */
@@ -2209,7 +2209,7 @@ struct ImBuf *imb_load_filepath_thumbnail_openexr(const char *filepath,
if (file->header().hasPreviewImage()) {
const Imf::PreviewImage &preview = file->header().previewImage();
ImBuf *ibuf = IMB_allocFromBuffer(
- (unsigned int *)preview.pixels(), nullptr, preview.width(), preview.height(), 4);
+ (uint *)preview.pixels(), nullptr, preview.width(), preview.height(), 4);
delete file;
delete stream;
IMB_flipy(ibuf);
diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c
index 4d6dfac0ba0..df6959ca90b 100644
--- a/source/blender/imbuf/intern/png.c
+++ b/source/blender/imbuf/intern/png.c
@@ -31,21 +31,21 @@
#include "IMB_colormanagement_intern.h"
typedef struct PNGReadStruct {
- const unsigned char *data;
- unsigned int size;
- unsigned int seek;
+ const uchar *data;
+ uint size;
+ uint seek;
} PNGReadStruct;
static void ReadData(png_structp png_ptr, png_bytep data, png_size_t length);
static void WriteData(png_structp png_ptr, png_bytep data, png_size_t length);
static void Flush(png_structp png_ptr);
-BLI_INLINE unsigned short UPSAMPLE_8_TO_16(const unsigned char _val)
+BLI_INLINE ushort UPSAMPLE_8_TO_16(const uchar _val)
{
return (_val << 8) + _val;
}
-bool imb_is_a_png(const unsigned char *mem, size_t size)
+bool imb_is_a_png(const uchar *mem, size_t size)
{
const int num_to_check = 8;
if (size < num_to_check) {
@@ -102,7 +102,7 @@ static float channel_colormanage_noop(float value)
}
/* wrap to avoid macro calling functions multiple times */
-BLI_INLINE unsigned short ftoshort(float val)
+BLI_INLINE ushort ftoshort(float val)
{
return unit_float_to_ushort_clamp(val);
}
@@ -112,9 +112,9 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags)
png_structp png_ptr;
png_infop info_ptr;
- unsigned char *pixels = NULL;
- unsigned char *from, *to;
- unsigned short *pixels16 = NULL, *to16;
+ uchar *pixels = NULL;
+ uchar *from, *to;
+ ushort *pixels16 = NULL, *to16;
float *from_float, from_straight[4];
png_bytepp row_pointers = NULL;
int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY;
@@ -169,10 +169,10 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags)
/* copy image data */
num_bytes = ((size_t)ibuf->x) * ibuf->y * bytesperpixel;
if (is_16bit) {
- pixels16 = MEM_mallocN(num_bytes * sizeof(unsigned short), "png 16bit pixels");
+ pixels16 = MEM_mallocN(num_bytes * sizeof(ushort), "png 16bit pixels");
}
else {
- pixels = MEM_mallocN(num_bytes * sizeof(unsigned char), "png 8bit pixels");
+ pixels = MEM_mallocN(num_bytes * sizeof(uchar), "png 8bit pixels");
}
if (pixels == NULL && pixels16 == NULL) {
printf(
@@ -210,7 +210,7 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags)
return 0;
}
- from = (unsigned char *)ibuf->rect;
+ from = (uchar *)ibuf->rect;
to = pixels;
from_float = ibuf->rect_float;
to16 = pixels16;
@@ -453,8 +453,8 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags)
if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) {
png_set_pHYs(png_ptr,
info_ptr,
- (unsigned int)(ibuf->ppm[0] + 0.5),
- (unsigned int)(ibuf->ppm[1] + 0.5),
+ (uint)(ibuf->ppm[0] + 0.5),
+ (uint)(ibuf->ppm[1] + 0.5),
PNG_RESOLUTION_METER);
}
@@ -468,15 +468,15 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags)
/* set the individual row-pointers to point at the correct offsets */
if (is_16bit) {
for (i = 0; i < ibuf->y; i++) {
- row_pointers[ibuf->y - 1 - i] = (png_bytep)((unsigned short *)pixels16 +
+ row_pointers[ibuf->y - 1 - i] = (png_bytep)((ushort *)pixels16 +
(((size_t)i) * ibuf->x) * bytesperpixel);
}
}
else {
for (i = 0; i < ibuf->y; i++) {
- row_pointers[ibuf->y - 1 - i] = (png_bytep)((unsigned char *)pixels +
- (((size_t)i) * ibuf->x) * bytesperpixel *
- sizeof(unsigned char));
+ row_pointers[ibuf->y - 1 - i] = (png_bytep)((uchar *)pixels + (((size_t)i) * ibuf->x) *
+ bytesperpixel *
+ sizeof(uchar));
}
}
@@ -521,22 +521,22 @@ static void imb_png_error(png_structp UNUSED(png_ptr), png_const_charp message)
fprintf(stderr, "libpng error: %s\n", message);
}
-ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
+ImBuf *imb_loadpng(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
struct ImBuf *ibuf = NULL;
png_structp png_ptr;
png_infop info_ptr;
- unsigned char *pixels = NULL;
- unsigned short *pixels16 = NULL;
+ uchar *pixels = NULL;
+ ushort *pixels16 = NULL;
png_bytepp row_pointers = NULL;
png_uint_32 width, height;
int bit_depth, color_type;
PNGReadStruct ps;
- unsigned char *from, *to;
- unsigned short *from16;
+ uchar *from, *to;
+ ushort *from16;
float *to_float;
- unsigned int channels;
+ uint channels;
if (imb_is_a_png(mem, size) == 0) {
return NULL;
@@ -646,7 +646,7 @@ ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colors
if (ibuf && ((flags & IB_test) == 0)) {
if (bit_depth == 16) {
- imb_addrectfloatImBuf(ibuf);
+ imb_addrectfloatImBuf(ibuf, 4);
png_set_swap(png_ptr);
pixels16 = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(png_uint_16), "pixels");
@@ -718,7 +718,7 @@ ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colors
else {
imb_addrectImBuf(ibuf);
- pixels = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(unsigned char), "pixels");
+ pixels = imb_alloc_pixels(ibuf->x, ibuf->y, channels, sizeof(uchar), "pixels");
if (pixels == NULL || ibuf->rect == NULL) {
printf("Cannot allocate pixels array\n");
longjmp(png_jmpbuf(png_ptr), 1);
@@ -733,16 +733,16 @@ ImBuf *imb_loadpng(const unsigned char *mem, size_t size, int flags, char colors
/* set the individual row-pointers to point at the correct offsets */
for (int i = 0; i < ibuf->y; i++) {
- row_pointers[ibuf->y - 1 - i] = (png_bytep)((unsigned char *)pixels +
- (((size_t)i) * ibuf->x) * channels *
- sizeof(unsigned char));
+ row_pointers[ibuf->y - 1 - i] = (png_bytep)((uchar *)pixels + (((size_t)i) * ibuf->x) *
+ channels *
+ sizeof(uchar));
}
png_read_image(png_ptr, row_pointers);
/* copy image data */
- to = (unsigned char *)ibuf->rect;
+ to = (uchar *)ibuf->rect;
from = pixels;
switch (channels) {
diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c
index aa07edf5c3a..00ef12a54f8 100644
--- a/source/blender/imbuf/intern/radiance_hdr.c
+++ b/source/blender/imbuf/intern/radiance_hdr.c
@@ -33,7 +33,7 @@
#define BLU 2
#define EXP 3
#define COLXS 128
-typedef unsigned char RGBE[4];
+typedef uchar RGBE[4];
typedef float fCOLOR[3];
/* copy source -> dest */
@@ -41,10 +41,7 @@ typedef float fCOLOR[3];
(c2[RED] = c1[RED], c2[GRN] = c1[GRN], c2[BLU] = c1[BLU], c2[EXP] = c1[EXP])
/* read routines */
-static const unsigned char *oldreadcolrs(RGBE *scan,
- const unsigned char *mem,
- int xmax,
- const unsigned char *mem_eof)
+static const uchar *oldreadcolrs(RGBE *scan, const uchar *mem, int xmax, const uchar *mem_eof)
{
size_t i, rshift = 0, len = xmax;
while (len > 0) {
@@ -72,10 +69,7 @@ static const unsigned char *oldreadcolrs(RGBE *scan,
return mem;
}
-static const unsigned char *freadcolrs(RGBE *scan,
- const unsigned char *mem,
- int xmax,
- const unsigned char *mem_eof)
+static const uchar *freadcolrs(RGBE *scan, const uchar *mem, int xmax, const uchar *mem_eof)
{
if (UNLIKELY(mem_eof - mem < 4)) {
return NULL;
@@ -118,7 +112,7 @@ static const unsigned char *freadcolrs(RGBE *scan,
}
val = *mem++;
while (code--) {
- scan[j++][i] = (unsigned char)val;
+ scan[j++][i] = (uchar)val;
}
}
else {
@@ -167,16 +161,16 @@ static void FLOAT2RGBE(const fCOLOR fcol, RGBE rgbe)
}
else {
d = (float)frexp(d, &e) * 256.0f / d;
- rgbe[RED] = (unsigned char)(fcol[RED] * d);
- rgbe[GRN] = (unsigned char)(fcol[GRN] * d);
- rgbe[BLU] = (unsigned char)(fcol[BLU] * d);
- rgbe[EXP] = (unsigned char)(e + COLXS);
+ rgbe[RED] = (uchar)(fcol[RED] * d);
+ rgbe[GRN] = (uchar)(fcol[GRN] * d);
+ rgbe[BLU] = (uchar)(fcol[BLU] * d);
+ rgbe[EXP] = (uchar)(e + COLXS);
}
}
/* ImBuf read */
-bool imb_is_a_hdr(const unsigned char *buf, const size_t size)
+bool imb_is_a_hdr(const uchar *buf, const size_t size)
{
/* NOTE: `#?RADIANCE` is used by other programs such as `ImageMagik`,
* Although there are some files in the wild that only use `#?` (from looking online).
@@ -187,17 +181,14 @@ bool imb_is_a_hdr(const unsigned char *buf, const size_t size)
*
* See: http://paulbourke.net/dataformats/pic/
*/
- const unsigned char magic[2] = {'#', '?'};
+ const uchar magic[2] = {'#', '?'};
if (size < sizeof(magic)) {
return false;
}
return memcmp(buf, magic, sizeof(magic)) == 0;
}
-struct ImBuf *imb_loadhdr(const unsigned char *mem,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+struct ImBuf *imb_loadhdr(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
struct ImBuf *ibuf;
RGBE *sline;
@@ -205,7 +196,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem,
float *rect_float;
int found = 0;
int width = 0, height = 0;
- const unsigned char *ptr, *mem_eof = mem + size;
+ const uchar *ptr, *mem_eof = mem + size;
char oriY[3], oriX[3];
if (!imb_is_a_hdr(mem, size)) {
@@ -246,7 +237,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem,
* since the format uses RLE compression. Can cause excessive memory allocation to occur. */
/* find end of this line, data right behind it */
- ptr = (const unsigned char *)strchr((const char *)&mem[x], '\n');
+ ptr = (const uchar *)strchr((const char *)&mem[x], '\n');
if (ptr == NULL || ptr >= mem_eof) {
return NULL;
}
@@ -306,7 +297,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem,
/* ImBuf write */
static int fwritecolrs(
- FILE *file, int width, int channels, const unsigned char *ibufscan, const float *fpscan)
+ FILE *file, int width, int channels, const uchar *ibufscan, const float *fpscan)
{
int beg, c2, count = 0;
fCOLOR fcol;
@@ -343,8 +334,8 @@ static int fwritecolrs(
/* put magic header */
putc(2, file);
putc(2, file);
- putc((unsigned char)(width >> 8), file);
- putc((unsigned char)(width & 255), file);
+ putc((uchar)(width >> 8), file);
+ putc((uchar)(width & 255), file);
/* put components separately */
for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < width; j += count) { /* find next run */
@@ -362,8 +353,8 @@ static int fwritecolrs(
c2 = j + 1;
while (rgbe_scan[c2++][i] == rgbe_scan[j][i]) {
if (c2 == beg) { /* short run */
- putc((unsigned char)(128 + beg - j), file);
- putc((unsigned char)(rgbe_scan[j][i]), file);
+ putc((uchar)(128 + beg - j), file);
+ putc((uchar)(rgbe_scan[j][i]), file);
j = beg;
break;
}
@@ -373,13 +364,13 @@ static int fwritecolrs(
if ((c2 = beg - j) > 128) {
c2 = 128;
}
- putc((unsigned char)(c2), file);
+ putc((uchar)(c2), file);
while (c2--) {
putc(rgbe_scan[j++][i], file);
}
}
if (count >= MINRUN) { /* write out run */
- putc((unsigned char)(128 + count), file);
+ putc((uchar)(128 + count), file);
putc(rgbe_scan[beg][i], file);
}
else {
@@ -411,7 +402,7 @@ bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags)
FILE *file = BLI_fopen(filepath, "wb");
float *fp = NULL;
size_t width = ibuf->x, height = ibuf->y;
- unsigned char *cp = NULL;
+ uchar *cp = NULL;
(void)flags; /* unused */
@@ -422,7 +413,7 @@ bool imb_savehdr(struct ImBuf *ibuf, const char *filepath, int flags)
writeHeader(file, width, height);
if (ibuf->rect) {
- cp = (unsigned char *)ibuf->rect + ibuf->channels * (height - 1) * width;
+ cp = (uchar *)ibuf->rect + ibuf->channels * (height - 1) * width;
}
if (ibuf->rect_float) {
fp = ibuf->rect_float + ibuf->channels * (height - 1) * width;
diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c
index 4b433836767..a9b79ad6d19 100644
--- a/source/blender/imbuf/intern/readimage.c
+++ b/source/blender/imbuf/intern/readimage.c
@@ -81,11 +81,8 @@ static void imb_handle_alpha(ImBuf *ibuf,
colormanage_imbuf_make_linear(ibuf, effective_colorspace);
}
-ImBuf *IMB_ibImageFromMemory(const unsigned char *mem,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE],
- const char *descr)
+ImBuf *IMB_ibImageFromMemory(
+ const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE], const char *descr)
{
ImBuf *ibuf;
const ImFileType *type;
@@ -157,7 +154,7 @@ ImBuf *IMB_loadifffile(
int file, const char *filepath, int flags, char colorspace[IM_MAX_SPACE], const char *descr)
{
ImBuf *ibuf;
- unsigned char *mem;
+ uchar *mem;
size_t size;
if (file == -1) {
@@ -209,7 +206,7 @@ static void imb_cache_filename(char *filepath, const char *name, int flags)
ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_SPACE])
{
ImBuf *ibuf;
- int file, a;
+ int file;
char filepath_tx[IMB_FILENAME_SIZE];
BLI_assert(!BLI_path_is_rel(filepath));
@@ -226,7 +223,7 @@ ImBuf *IMB_loadiffname(const char *filepath, int flags, char colorspace[IM_MAX_S
if (ibuf) {
BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name));
BLI_strncpy(ibuf->cachename, filepath_tx, sizeof(ibuf->cachename));
- for (a = 1; a < ibuf->miptot; a++) {
+ for (int a = 1; a < ibuf->miptot; a++) {
BLI_strncpy(ibuf->mipmap[a - 1]->cachename, filepath_tx, sizeof(ibuf->cachename));
}
}
@@ -319,9 +316,9 @@ ImBuf *IMB_testiffname(const char *filepath, int flags)
return ibuf;
}
-static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int *rect)
+static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, uint *rect)
{
- unsigned char *mem;
+ uchar *mem;
size_t size;
if (file == -1) {
@@ -352,7 +349,7 @@ static void imb_loadtilefile(ImBuf *ibuf, int file, int tx, int ty, unsigned int
imb_mmap_unlock();
}
-void imb_loadtile(ImBuf *ibuf, int tx, int ty, unsigned int *rect)
+void imb_loadtile(ImBuf *ibuf, int tx, int ty, uint *rect)
{
int file;
diff --git a/source/blender/imbuf/intern/rectop.c b/source/blender/imbuf/intern/rectop.c
index 2f864534d61..4159aa851c4 100644
--- a/source/blender/imbuf/intern/rectop.c
+++ b/source/blender/imbuf/intern/rectop.c
@@ -21,9 +21,9 @@
#include "MEM_guardedalloc.h"
-void IMB_blend_color_byte(unsigned char dst[4],
- const unsigned char src1[4],
- const unsigned char src2[4],
+void IMB_blend_color_byte(uchar dst[4],
+ const uchar src1[4],
+ const uchar src2[4],
IMB_BlendMode mode)
{
switch (mode) {
@@ -487,17 +487,15 @@ void IMB_rectcpy(ImBuf *dbuf,
false);
}
-typedef void (*IMB_blend_func)(unsigned char *dst,
- const unsigned char *src1,
- const unsigned char *src2);
+typedef void (*IMB_blend_func)(uchar *dst, const uchar *src1, const uchar *src2);
typedef void (*IMB_blend_func_float)(float *dst, const float *src1, const float *src2);
void IMB_rectblend(ImBuf *dbuf,
const ImBuf *obuf,
const ImBuf *sbuf,
- unsigned short *dmask,
- const unsigned short *curvemask,
- const unsigned short *texmask,
+ ushort *dmask,
+ const ushort *curvemask,
+ const ushort *texmask,
float mask_max,
int destx,
int desty,
@@ -510,11 +508,11 @@ void IMB_rectblend(ImBuf *dbuf,
IMB_BlendMode mode,
bool accumulate)
{
- unsigned int *drect = NULL, *orect = NULL, *srect = NULL, *dr, * or, *sr;
+ uint *drect = NULL, *orect = NULL, *srect = NULL, *dr, * or, *sr;
float *drectf = NULL, *orectf = NULL, *srectf = NULL, *drf, *orf, *srf;
- const unsigned short *cmaskrect = curvemask, *cmr;
- unsigned short *dmaskrect = dmask, *dmr;
- const unsigned short *texmaskrect = texmask, *tmr;
+ const ushort *cmaskrect = curvemask, *cmr;
+ ushort *dmaskrect = dmask, *dmr;
+ const ushort *texmaskrect = texmask, *tmr;
int srcskip, destskip, origskip, x;
IMB_blend_func func = NULL;
IMB_blend_func_float func_float = NULL;
@@ -766,7 +764,7 @@ void IMB_rectblend(ImBuf *dbuf,
if (dmaskrect) {
dmr = dmaskrect;
for (x = width; x > 0; x--, dr++, or ++, sr++, dmr++, cmr++) {
- unsigned char *src = (unsigned char *)sr;
+ uchar *src = (uchar *)sr;
float mask_lim = mask_max * (*cmr);
if (texmaskrect) {
@@ -786,7 +784,7 @@ void IMB_rectblend(ImBuf *dbuf,
mask = min_ff(mask, 65535.0);
if (mask > *dmr) {
- unsigned char mask_src[4];
+ uchar mask_src[4];
*dmr = mask;
@@ -797,11 +795,11 @@ void IMB_rectblend(ImBuf *dbuf,
if (mode == IMB_BLEND_INTERPOLATE) {
mask_src[3] = src[3];
blend_color_interpolate_byte(
- (unsigned char *)dr, (unsigned char *) or, mask_src, mask / 65535.0f);
+ (uchar *)dr, (uchar *) or, mask_src, mask / 65535.0f);
}
else {
mask_src[3] = divide_round_i(src[3] * mask, 65535);
- func((unsigned char *)dr, (unsigned char *) or, mask_src);
+ func((uchar *)dr, (uchar *) or, mask_src);
}
}
}
@@ -811,7 +809,7 @@ void IMB_rectblend(ImBuf *dbuf,
/* no destination mask buffer, do regular blend with masktexture if present */
else {
for (x = width; x > 0; x--, dr++, or ++, sr++, cmr++) {
- unsigned char *src = (unsigned char *)sr;
+ uchar *src = (uchar *)sr;
float mask = (float)mask_max * ((float)(*cmr));
if (texmaskrect) {
@@ -821,7 +819,7 @@ void IMB_rectblend(ImBuf *dbuf,
mask = min_ff(mask, 65535.0);
if (src[3] && (mask > 0.0f)) {
- unsigned char mask_src[4];
+ uchar mask_src[4];
mask_src[0] = src[0];
mask_src[1] = src[1];
@@ -830,11 +828,11 @@ void IMB_rectblend(ImBuf *dbuf,
if (mode == IMB_BLEND_INTERPOLATE) {
mask_src[3] = src[3];
blend_color_interpolate_byte(
- (unsigned char *)dr, (unsigned char *) or, mask_src, mask / 65535.0f);
+ (uchar *)dr, (uchar *) or, mask_src, mask / 65535.0f);
}
else {
mask_src[3] = divide_round_i(src[3] * mask, 65535);
- func((unsigned char *)dr, (unsigned char *) or, mask_src);
+ func((uchar *)dr, (uchar *) or, mask_src);
}
}
}
@@ -848,8 +846,8 @@ void IMB_rectblend(ImBuf *dbuf,
else {
/* regular blending */
for (x = width; x > 0; x--, dr++, or ++, sr++) {
- if (((unsigned char *)sr)[3]) {
- func((unsigned char *)dr, (unsigned char *) or, (unsigned char *)sr);
+ if (((uchar *)sr)[3]) {
+ func((uchar *)dr, (uchar *) or, (uchar *)sr);
}
}
}
@@ -956,8 +954,8 @@ void IMB_rectblend(ImBuf *dbuf,
typedef struct RectBlendThreadData {
ImBuf *dbuf;
const ImBuf *obuf, *sbuf;
- unsigned short *dmask;
- const unsigned short *curvemask, *texmask;
+ ushort *dmask;
+ const ushort *curvemask, *texmask;
float mask_max;
int destx, desty, origx, origy;
int srcx, srcy, width;
@@ -991,9 +989,9 @@ static void rectblend_thread_do(void *data_v, int scanline)
void IMB_rectblend_threaded(ImBuf *dbuf,
const ImBuf *obuf,
const ImBuf *sbuf,
- unsigned short *dmask,
- const unsigned short *curvemask,
- const unsigned short *texmask,
+ ushort *dmask,
+ const ushort *curvemask,
+ const ushort *texmask,
float mask_max,
int destx,
int desty,
@@ -1052,7 +1050,7 @@ void IMB_rectfill(ImBuf *drect, const float col[4])
int num;
if (drect->rect) {
- unsigned int *rrect = drect->rect;
+ uint *rrect = drect->rect;
char ccol[4];
ccol[0] = (int)(col[0] * 255);
@@ -1062,7 +1060,7 @@ void IMB_rectfill(ImBuf *drect, const float col[4])
num = drect->x * drect->y;
for (; num > 0; num--) {
- *rrect++ = *((unsigned int *)ccol);
+ *rrect++ = *((uint *)ccol);
}
}
@@ -1106,15 +1104,15 @@ void IMB_rectfill_area_replace(
return;
}
- unsigned char col_char[4] = {col[0] * 255, col[1] * 255, col[2] * 255, col[3] * 255};
+ uchar col_char[4] = {col[0] * 255, col[1] * 255, col[2] * 255, col[3] * 255};
for (int y = y1; y < y2; y++) {
for (int x = x1; x < x2; x++) {
size_t offset = ((size_t)ibuf->x) * y * 4 + 4 * x;
if (ibuf->rect) {
- unsigned char *rrect = (unsigned char *)ibuf->rect + offset;
- memcpy(rrect, &col_char, sizeof(unsigned char) * 4);
+ uchar *rrect = (uchar *)ibuf->rect + offset;
+ memcpy(rrect, &col_char, sizeof(uchar) * 4);
}
if (ibuf->rect_float) {
@@ -1125,7 +1123,7 @@ void IMB_rectfill_area_replace(
}
}
-void buf_rectfill_area(unsigned char *rect,
+void buf_rectfill_area(uchar *rect,
float *rectf,
int width,
int height,
@@ -1165,8 +1163,8 @@ void buf_rectfill_area(unsigned char *rect,
aich = ai / 255.0f;
if (rect) {
- unsigned char *pixel;
- unsigned char chr = 0, chg = 0, chb = 0;
+ uchar *pixel;
+ uchar chr = 0, chg = 0, chb = 0;
float fr = 0, fg = 0, fb = 0;
const int alphaint = unit_float_to_uchar_clamp(a);
@@ -1247,16 +1245,8 @@ void IMB_rectfill_area(ImBuf *ibuf,
if (!ibuf) {
return;
}
- buf_rectfill_area((unsigned char *)ibuf->rect,
- ibuf->rect_float,
- ibuf->x,
- ibuf->y,
- col,
- display,
- x1,
- y1,
- x2,
- y2);
+ buf_rectfill_area(
+ (uchar *)ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, col, display, x1, y1, x2, y2);
}
void IMB_rectfill_alpha(ImBuf *ibuf, const float value)
@@ -1271,8 +1261,8 @@ void IMB_rectfill_alpha(ImBuf *ibuf, const float value)
}
if (ibuf->rect) {
- const unsigned char cvalue = value * 255;
- unsigned char *cbuf = ((unsigned char *)ibuf->rect) + 3;
+ const uchar cvalue = value * 255;
+ uchar *cbuf = ((uchar *)ibuf->rect) + 3;
for (i = ibuf->x * ibuf->y; i > 0; i--, cbuf += 4) {
*cbuf = cvalue;
}
diff --git a/source/blender/imbuf/intern/rotate.c b/source/blender/imbuf/intern/rotate.c
index ac07ce85526..7081bf2ad26 100644
--- a/source/blender/imbuf/intern/rotate.c
+++ b/source/blender/imbuf/intern/rotate.c
@@ -22,7 +22,7 @@ void IMB_flipy(struct ImBuf *ibuf)
}
if (ibuf->rect) {
- unsigned int *top, *bottom, *line;
+ uint *top, *bottom, *line;
x_size = ibuf->x;
y_size = ibuf->y;
@@ -88,7 +88,7 @@ void IMB_flipx(struct ImBuf *ibuf)
for (yi = y - 1; yi >= 0; yi--) {
const size_t x_offset = (size_t)x * yi;
for (xr = x - 1, xl = 0; xr >= xl; xr--, xl++) {
- SWAP(unsigned int, ibuf->rect[x_offset + xr], ibuf->rect[x_offset + xl]);
+ SWAP(uint, ibuf->rect[x_offset + xr], ibuf->rect[x_offset + xl]);
}
}
}
diff --git a/source/blender/imbuf/intern/scaling.c b/source/blender/imbuf/intern/scaling.c
index f4abc668402..acffc4e7f8f 100644
--- a/source/blender/imbuf/intern/scaling.c
+++ b/source/blender/imbuf/intern/scaling.c
@@ -324,10 +324,9 @@ struct ImBuf *IMB_double_y(struct ImBuf *ibuf1)
/* pretty much specific functions which converts uchar <-> ushort but assumes
* ushort range of 255*255 which is more convenient here
*/
-MINLINE void straight_uchar_to_premul_ushort(unsigned short result[4],
- const unsigned char color[4])
+MINLINE void straight_uchar_to_premul_ushort(ushort result[4], const uchar color[4])
{
- unsigned short alpha = color[3];
+ ushort alpha = color[3];
result[0] = color[0] * alpha;
result[1] = color[1] * alpha;
@@ -335,7 +334,7 @@ MINLINE void straight_uchar_to_premul_ushort(unsigned short result[4],
result[3] = alpha * 256;
}
-MINLINE void premul_ushort_to_straight_uchar(unsigned char *result, const unsigned short color[4])
+MINLINE void premul_ushort_to_straight_uchar(uchar *result, const ushort color[4])
{
if (color[3] <= 255) {
result[0] = unit_ushort_to_uchar(color[0]);
@@ -344,7 +343,7 @@ MINLINE void premul_ushort_to_straight_uchar(unsigned char *result, const unsign
result[3] = unit_ushort_to_uchar(color[3]);
}
else {
- unsigned short alpha = color[3] / 256;
+ ushort alpha = color[3] / 256;
result[0] = unit_ushort_to_uchar((ushort)(color[0] / alpha * 256));
result[1] = unit_ushort_to_uchar((ushort)(color[1] / alpha * 256));
@@ -373,25 +372,25 @@ void imb_onehalf_no_alloc(struct ImBuf *ibuf2, struct ImBuf *ibuf1)
}
if (do_rect) {
- unsigned char *cp1, *cp2, *dest;
+ uchar *cp1, *cp2, *dest;
- cp1 = (unsigned char *)ibuf1->rect;
- dest = (unsigned char *)ibuf2->rect;
+ cp1 = (uchar *)ibuf1->rect;
+ dest = (uchar *)ibuf2->rect;
for (y = ibuf2->y; y > 0; y--) {
cp2 = cp1 + (ibuf1->x << 2);
for (x = ibuf2->x; x > 0; x--) {
- unsigned short p1i[8], p2i[8], desti[4];
+ ushort p1i[8], p2i[8], desti[4];
straight_uchar_to_premul_ushort(p1i, cp1);
straight_uchar_to_premul_ushort(p2i, cp2);
straight_uchar_to_premul_ushort(p1i + 4, cp1 + 4);
straight_uchar_to_premul_ushort(p2i + 4, cp2 + 4);
- desti[0] = ((unsigned int)p1i[0] + p2i[0] + p1i[4] + p2i[4]) >> 2;
- desti[1] = ((unsigned int)p1i[1] + p2i[1] + p1i[5] + p2i[5]) >> 2;
- desti[2] = ((unsigned int)p1i[2] + p2i[2] + p1i[6] + p2i[6]) >> 2;
- desti[3] = ((unsigned int)p1i[3] + p2i[3] + p1i[7] + p2i[7]) >> 2;
+ desti[0] = ((uint)p1i[0] + p2i[0] + p1i[4] + p2i[4]) >> 2;
+ desti[1] = ((uint)p1i[1] + p2i[1] + p1i[5] + p2i[5]) >> 2;
+ desti[2] = ((uint)p1i[2] + p2i[2] + p1i[6] + p2i[6]) >> 2;
+ desti[3] = ((uint)p1i[3] + p2i[3] + p1i[7] + p2i[7]) >> 2;
premul_ushort_to_straight_uchar(dest, desti);
@@ -460,12 +459,8 @@ ImBuf *IMB_onehalf(struct ImBuf *ibuf1)
/* q_scale_linear_interpolation helper functions */
-static void enlarge_picture_byte(unsigned char *src,
- unsigned char *dst,
- int src_width,
- int src_height,
- int dst_width,
- int dst_height)
+static void enlarge_picture_byte(
+ uchar *src, uchar *dst, int src_width, int src_height, int dst_width, int dst_height)
{
double ratiox = (double)(dst_width - 1.0) / (double)(src_width - 1.001);
double ratioy = (double)(dst_height - 1.0) / (double)(src_height - 1.001);
@@ -477,8 +472,8 @@ static void enlarge_picture_byte(unsigned char *src,
y_src = 0;
for (y_dst = 0; y_dst < dst_height; y_dst++) {
- unsigned char *line1 = src + (y_src >> 16) * 4 * src_width;
- unsigned char *line2 = line1 + 4 * src_width;
+ uchar *line1 = src + (y_src >> 16) * 4 * src_width;
+ uchar *line2 = line1 + 4 * src_width;
uintptr_t weight1y = 65536 - (y_src & 0xffff);
uintptr_t weight2y = 65536 - weight1y;
@@ -491,7 +486,7 @@ static void enlarge_picture_byte(unsigned char *src,
uintptr_t weight1x = 65536 - (x_src & 0xffff);
uintptr_t weight2x = 65536 - weight1x;
- unsigned long x = (x_src >> 16) * 4;
+ ulong x = (x_src >> 16) * 4;
*dst++ = ((((line1[x] * weight1y) >> 16) * weight1x) >> 16) +
((((line2[x] * weight2y) >> 16) * weight1x) >> 16) +
@@ -528,19 +523,15 @@ struct scale_outpix_byte {
uintptr_t weight;
};
-static void shrink_picture_byte(unsigned char *src,
- unsigned char *dst,
- int src_width,
- int src_height,
- int dst_width,
- int dst_height)
+static void shrink_picture_byte(
+ uchar *src, uchar *dst, int src_width, int src_height, int dst_width, int dst_height)
{
double ratiox = (double)(dst_width) / (double)(src_width);
double ratioy = (double)(dst_height) / (double)(src_height);
uintptr_t x_src, dx_dst, x_dst;
uintptr_t y_src, dy_dst, y_dst;
intptr_t y_counter;
- unsigned char *dst_begin = dst;
+ uchar *dst_begin = dst;
struct scale_outpix_byte *dst_line1 = NULL;
struct scale_outpix_byte *dst_line2 = NULL;
@@ -556,7 +547,7 @@ static void shrink_picture_byte(unsigned char *src,
y_dst = 0;
y_counter = 65536;
for (y_src = 0; y_src < src_height; y_src++) {
- unsigned char *line = src + y_src * 4 * src_width;
+ uchar *line = src + y_src * 4 * src_width;
uintptr_t weight1y = 65535 - (y_dst & 0xffff);
uintptr_t weight2y = 65535 - weight1y;
x_dst = 0;
@@ -643,12 +634,8 @@ static void shrink_picture_byte(unsigned char *src,
MEM_freeN(dst_line2);
}
-static void q_scale_byte(unsigned char *in,
- unsigned char *out,
- int in_width,
- int in_height,
- int dst_width,
- int dst_height)
+static void q_scale_byte(
+ uchar *in, uchar *out, int in_width, int in_height, int dst_width, int dst_height)
{
if (dst_width > in_width && dst_height > in_height) {
enlarge_picture_byte(in, out, in_width, in_height, dst_width, dst_height);
@@ -841,7 +828,7 @@ static void q_scale_float(
}
/**
- * q_scale_linear_interpolation (derived from ppmqscale, http://libdv.sf.net)
+ * q_scale_linear_interpolation (derived from `ppmqscale`, http://libdv.sf.net)
*
* q stands for quick _and_ quality :)
*
@@ -868,12 +855,12 @@ static bool q_scale_linear_interpolation(struct ImBuf *ibuf, int newx, int newy)
}
if (ibuf->rect) {
- unsigned char *newrect = MEM_mallocN(sizeof(int) * newx * newy, "q_scale rect");
- q_scale_byte((unsigned char *)ibuf->rect, newrect, ibuf->x, ibuf->y, newx, newy);
+ uchar *newrect = MEM_mallocN(sizeof(int) * newx * newy, "q_scale rect");
+ q_scale_byte((uchar *)ibuf->rect, newrect, ibuf->x, ibuf->y, newx, newy);
imb_freerectImBuf(ibuf);
ibuf->mall |= IB_rect;
- ibuf->rect = (unsigned int *)newrect;
+ ibuf->rect = (uint *)newrect;
}
if (ibuf->rect_float) {
float *newrect = MEM_mallocN(sizeof(float[4]) * newx * newy, "q_scale rectfloat");
@@ -1014,7 +1001,7 @@ static ImBuf *scaledownx(struct ImBuf *ibuf, int newx)
BLI_assert((uchar *)rect - ((uchar *)ibuf->rect) == rect_size); /* see bug T26502. */
imb_freerectImBuf(ibuf);
ibuf->mall |= IB_rect;
- ibuf->rect = (unsigned int *)_newrect;
+ ibuf->rect = (uint *)_newrect;
}
if (do_float) {
// printf("%ld %ld\n", rectf - ibuf->rect_float, rect_size);
@@ -1156,7 +1143,7 @@ static ImBuf *scaledowny(struct ImBuf *ibuf, int newy)
BLI_assert((uchar *)rect - ((uchar *)ibuf->rect) == rect_size); /* see bug T26502. */
imb_freerectImBuf(ibuf);
ibuf->mall |= IB_rect;
- ibuf->rect = (unsigned int *)_newrect;
+ ibuf->rect = (uint *)_newrect;
}
if (do_float) {
// printf("%ld %ld\n", rectf - ibuf->rect_float, rect_size);
@@ -1361,7 +1348,7 @@ static ImBuf *scaleupx(struct ImBuf *ibuf, int newx)
if (do_rect) {
imb_freerectImBuf(ibuf);
ibuf->mall |= IB_rect;
- ibuf->rect = (unsigned int *)_newrect;
+ ibuf->rect = (uint *)_newrect;
}
if (do_float) {
imb_freerectfloatImBuf(ibuf);
@@ -1564,7 +1551,7 @@ static ImBuf *scaleupy(struct ImBuf *ibuf, int newy)
if (do_rect) {
imb_freerectImBuf(ibuf);
ibuf->mall |= IB_rect;
- ibuf->rect = (unsigned int *)_newrect;
+ ibuf->rect = (uint *)_newrect;
}
if (do_float) {
imb_freerectfloatImBuf(ibuf);
@@ -1641,7 +1628,7 @@ static void scalefast_Z_ImBuf(ImBuf *ibuf, int newx, int newy)
}
}
-bool IMB_scaleImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy)
+bool IMB_scaleImBuf(struct ImBuf *ibuf, uint newx, uint newy)
{
BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
@@ -1686,11 +1673,11 @@ struct imbufRGBA {
float r, g, b, a;
};
-bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy)
+bool IMB_scalefastImBuf(struct ImBuf *ibuf, uint newx, uint newy)
{
BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
- unsigned int *rect, *_newrect, *newrect;
+ uint *rect, *_newrect, *newrect;
struct imbufRGBA *rectf, *_newrectf, *newrectf;
int x, y;
bool do_float = false, do_rect = false;
@@ -1789,23 +1776,23 @@ bool IMB_scalefastImBuf(struct ImBuf *ibuf, unsigned int newx, unsigned int newy
typedef struct ScaleTreadInitData {
ImBuf *ibuf;
- unsigned int newx;
- unsigned int newy;
+ uint newx;
+ uint newy;
- unsigned char *byte_buffer;
+ uchar *byte_buffer;
float *float_buffer;
} ScaleTreadInitData;
typedef struct ScaleThreadData {
ImBuf *ibuf;
- unsigned int newx;
- unsigned int newy;
+ uint newx;
+ uint newy;
int start_line;
int tot_line;
- unsigned char *byte_buffer;
+ uchar *byte_buffer;
float *float_buffer;
} ScaleThreadData;
@@ -1844,9 +1831,8 @@ static void *do_scale_thread(void *data_v)
int offset = y * data->newx + x;
if (data->byte_buffer) {
- unsigned char *pixel = data->byte_buffer + 4 * offset;
- BLI_bilinear_interpolation_char(
- (unsigned char *)ibuf->rect, pixel, ibuf->x, ibuf->y, 4, u, v);
+ uchar *pixel = data->byte_buffer + 4 * offset;
+ BLI_bilinear_interpolation_char((uchar *)ibuf->rect, pixel, ibuf->x, ibuf->y, 4, u, v);
}
if (data->float_buffer) {
@@ -1860,7 +1846,7 @@ static void *do_scale_thread(void *data_v)
return NULL;
}
-void IMB_scaleImBuf_threaded(ImBuf *ibuf, unsigned int newx, unsigned int newy)
+void IMB_scaleImBuf_threaded(ImBuf *ibuf, uint newx, uint newy)
{
BLI_assert_msg(newx > 0 && newy > 0, "Images must be at least 1 on both dimensions!");
@@ -1893,7 +1879,7 @@ void IMB_scaleImBuf_threaded(ImBuf *ibuf, unsigned int newx, unsigned int newy)
if (ibuf->rect) {
imb_freerectImBuf(ibuf);
ibuf->mall |= IB_rect;
- ibuf->rect = (unsigned int *)init_data.byte_buffer;
+ ibuf->rect = (uint *)init_data.byte_buffer;
}
if (ibuf->rect_float) {
diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c
index 2a0baaf6172..eb2701b5b9c 100644
--- a/source/blender/imbuf/intern/stereoimbuf.c
+++ b/source/blender/imbuf/intern/stereoimbuf.c
@@ -650,8 +650,8 @@ static void imb_stereo3d_squeeze_rect(
IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height);
ibuf = IMB_allocImBuf(width, height, channels, IB_rect);
- IMB_buffer_byte_from_byte((unsigned char *)ibuf->rect,
- (unsigned char *)rect,
+ IMB_buffer_byte_from_byte((uchar *)ibuf->rect,
+ (uchar *)rect,
IB_PROFILE_SRGB,
IB_PROFILE_SRGB,
false,
@@ -661,7 +661,7 @@ static void imb_stereo3d_squeeze_rect(
width);
IMB_scaleImBuf_threaded(ibuf, x, y);
- memcpy(rect, ibuf->rect, x * y * sizeof(unsigned int));
+ memcpy(rect, ibuf->rect, x * y * sizeof(uint));
IMB_freeImBuf(ibuf);
}
@@ -761,11 +761,14 @@ ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, Im
IMB_stereo3d_write_dimensions(
im_format->stereo3d_format.display_mode, false, ibuf_left->x, ibuf_left->y, &width, &height);
- ibuf_stereo = IMB_allocImBuf(
- width, height, ibuf_left->planes, (is_float ? IB_rectfloat : IB_rect));
+ ibuf_stereo = IMB_allocImBuf(width, height, ibuf_left->planes, 0);
- ibuf_stereo->rect_colorspace = ibuf_left->rect_colorspace;
- ibuf_stereo->float_colorspace = ibuf_left->float_colorspace;
+ if (is_float) {
+ imb_addrectfloatImBuf(ibuf_stereo, ibuf_left->channels);
+ }
+ else {
+ imb_addrectImBuf(ibuf_stereo);
+ }
ibuf_stereo->flags = ibuf_left->flags;
@@ -773,7 +776,7 @@ ImBuf *IMB_stereo3d_ImBuf(const ImageFormatData *im_format, ImBuf *ibuf_left, Im
is_float,
ibuf_left->x,
ibuf_left->y,
- 4,
+ ibuf_left->channels,
(int *)ibuf_left->rect,
(int *)ibuf_right->rect,
(int *)ibuf_stereo->rect,
@@ -1286,10 +1289,17 @@ void IMB_ImBufFromStereo3d(const Stereo3dFormat *s3d,
&width,
&height);
- ibuf_left = IMB_allocImBuf(
- width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect));
- ibuf_right = IMB_allocImBuf(
- width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect));
+ ibuf_left = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, 0);
+ ibuf_right = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, 0);
+
+ if (is_float) {
+ imb_addrectfloatImBuf(ibuf_left, ibuf_stereo3d->channels);
+ imb_addrectfloatImBuf(ibuf_right, ibuf_stereo3d->channels);
+ }
+ else {
+ imb_addrectImBuf(ibuf_left);
+ imb_addrectImBuf(ibuf_right);
+ }
ibuf_left->flags = ibuf_stereo3d->flags;
ibuf_right->flags = ibuf_stereo3d->flags;
@@ -1307,7 +1317,7 @@ void IMB_ImBufFromStereo3d(const Stereo3dFormat *s3d,
is_float,
ibuf_left->x,
ibuf_left->y,
- 4,
+ ibuf_left->channels,
(int *)ibuf_left->rect,
(int *)ibuf_right->rect,
(int *)ibuf_stereo3d->rect,
diff --git a/source/blender/imbuf/intern/targa.c b/source/blender/imbuf/intern/targa.c
index 7cf90cd12e2..ed6e6e9866d 100644
--- a/source/blender/imbuf/intern/targa.c
+++ b/source/blender/imbuf/intern/targa.c
@@ -30,18 +30,18 @@
/***/
typedef struct TARGA {
- unsigned char numid;
- unsigned char maptyp;
- unsigned char imgtyp;
+ uchar numid;
+ uchar maptyp;
+ uchar imgtyp;
short maporig;
short mapsize;
- unsigned char mapbits;
+ uchar mapbits;
short xorig;
short yorig;
short xsize;
short ysize;
- unsigned char pixsize;
- unsigned char imgdes;
+ uchar pixsize;
+ uchar imgdes;
} TARGA;
/**
@@ -54,7 +54,7 @@ typedef struct TARGA {
/***/
-static int tga_out1(unsigned int data, FILE *file)
+static int tga_out1(uint data, FILE *file)
{
uchar *p;
@@ -65,7 +65,7 @@ static int tga_out1(unsigned int data, FILE *file)
return ~EOF;
}
-static int tga_out2(unsigned int data, FILE *file)
+static int tga_out2(uint data, FILE *file)
{
uchar *p;
@@ -79,7 +79,7 @@ static int tga_out2(unsigned int data, FILE *file)
return ~EOF;
}
-static int tga_out3(unsigned int data, FILE *file)
+static int tga_out3(uint data, FILE *file)
{
uchar *p;
@@ -96,7 +96,7 @@ static int tga_out3(unsigned int data, FILE *file)
return ~EOF;
}
-static int tga_out4(unsigned int data, FILE *file)
+static int tga_out4(uint data, FILE *file)
{
uchar *p;
@@ -117,11 +117,11 @@ static int tga_out4(unsigned int data, FILE *file)
return ~EOF;
}
-static bool makebody_tga(ImBuf *ibuf, FILE *file, int (*out)(unsigned int, FILE *))
+static bool makebody_tga(ImBuf *ibuf, FILE *file, int (*out)(uint, FILE *))
{
int last, this;
int copy, bytes;
- unsigned int *rect, *rectstart, *temp;
+ uint *rect, *rectstart, *temp;
int y;
for (y = 0; y < ibuf->y; y++) {
@@ -345,7 +345,7 @@ bool imb_savetarga(struct ImBuf *ibuf, const char *filepath, int UNUSED(flags))
return ok;
}
-static bool checktarga(TARGA *tga, const unsigned char *mem, const size_t size)
+static bool checktarga(TARGA *tga, const uchar *mem, const size_t size)
{
if (size < TARGA_HEADER_SIZE) {
return false;
@@ -397,14 +397,14 @@ static bool checktarga(TARGA *tga, const unsigned char *mem, const size_t size)
return true;
}
-bool imb_is_a_targa(const unsigned char *buf, size_t size)
+bool imb_is_a_targa(const uchar *buf, size_t size)
{
TARGA tga;
return checktarga(&tga, buf, size);
}
-static void complete_partial_load(struct ImBuf *ibuf, unsigned int *rect)
+static void complete_partial_load(struct ImBuf *ibuf, uint *rect)
{
int size = (ibuf->x * ibuf->y) - (rect - ibuf->rect);
if (size) {
@@ -420,11 +420,11 @@ static void complete_partial_load(struct ImBuf *ibuf, unsigned int *rect)
}
}
-static void decodetarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem_size, int psize)
+static void decodetarga(struct ImBuf *ibuf, const uchar *mem, size_t mem_size, int psize)
{
- const unsigned char *mem_end = mem + mem_size;
+ const uchar *mem_end = mem + mem_size;
int count, col, size;
- unsigned int *rect;
+ uint *rect;
uchar *cp = (uchar *)&col;
if (ibuf == NULL) {
@@ -545,11 +545,11 @@ partial_load:
complete_partial_load(ibuf, rect);
}
-static void ldtarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem_size, int psize)
+static void ldtarga(struct ImBuf *ibuf, const uchar *mem, size_t mem_size, int psize)
{
- const unsigned char *mem_end = mem + mem_size;
+ const uchar *mem_end = mem + mem_size;
int col, size;
- unsigned int *rect;
+ uint *rect;
uchar *cp = (uchar *)&col;
if (ibuf == NULL) {
@@ -609,15 +609,12 @@ partial_load:
complete_partial_load(ibuf, rect);
}
-ImBuf *imb_loadtarga(const unsigned char *mem,
- size_t mem_size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+ImBuf *imb_loadtarga(const uchar *mem, size_t mem_size, int flags, char colorspace[IM_MAX_SPACE])
{
TARGA tga;
struct ImBuf *ibuf;
int count, size;
- unsigned int *rect, *cmap = NULL /*, mincol = 0*/, cmap_max = 0;
+ uint *rect, *cmap = NULL /*, mincol = 0*/, cmap_max = 0;
int32_t cp_data;
uchar *cp = (uchar *)&cp_data;
@@ -650,7 +647,7 @@ ImBuf *imb_loadtarga(const unsigned char *mem,
/* Load color map. */
// mincol = tga.maporig; /* UNUSED */
cmap_max = tga.mapsize;
- cmap = MEM_callocN(sizeof(unsigned int) * cmap_max, "targa cmap");
+ cmap = MEM_callocN(sizeof(uint) * cmap_max, "targa cmap");
for (count = 0; count < cmap_max; count++) {
switch (tga.mapbits >> 3) {
@@ -753,7 +750,7 @@ ImBuf *imb_loadtarga(const unsigned char *mem,
}
if (tga.pixsize == 16) {
- unsigned int col;
+ uint col;
rect = ibuf->rect;
for (size = ibuf->x * ibuf->y; size > 0; size--, rect++) {
col = *rect;
@@ -773,10 +770,10 @@ ImBuf *imb_loadtarga(const unsigned char *mem,
if (ELEM(tga.imgtyp, 3, 11)) {
uchar *crect;
- unsigned int *lrect, col;
+ uint *lrect, col;
crect = (uchar *)ibuf->rect;
- lrect = (unsigned int *)ibuf->rect;
+ lrect = (uint *)ibuf->rect;
for (size = ibuf->x * ibuf->y; size > 0; size--) {
col = *lrect++;
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index 6f39009d38d..d535bd00501 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -136,7 +136,7 @@ typedef enum {
/* Don't lose comment alignment. */
/* clang-format off */
-static const unsigned char acceptable[96] = {
+static const uchar acceptable[96] = {
/* A table of the ASCII chars from space (32) to DEL (127) */
/* ! " # $ % & ' ( ) * + , - . / */
0x00,0x3F,0x20,0x20,0x28,0x00,0x2C,0x3F,0x3F,0x3F,0x3F,0x2A,0x28,0x3F,0x3F,0x1C,
@@ -176,7 +176,7 @@ static void escape_uri_string(const char *string,
escaped_string_size -= 1;
for (q = escaped_string, p = string; (*p != '\0') && escaped_string_size; p++) {
- c = (unsigned char)*p;
+ c = (uchar)*p;
if (!ACCEPTABLE(c)) {
if (escaped_string_size < 3) {
@@ -227,7 +227,7 @@ static bool uri_from_filename(const char *path, char *uri)
return 0;
}
/* on windows, using always uppercase drive/volume letter in uri */
- vol[0] = (unsigned char)toupper(path[0]);
+ vol[0] = (uchar)toupper(path[0]);
vol[1] = ':';
vol[2] = '\0';
strcat(orig_uri, vol);
@@ -256,7 +256,7 @@ static bool thumbpathname_from_uri(
if (r_name) {
char hexdigest[33];
- unsigned char digest[16];
+ uchar digest[16];
BLI_hash_md5_buffer(uri, strlen(uri), digest);
hexdigest[0] = '\0';
BLI_snprintf(r_name, name_len, "%s.png", BLI_hash_md5_to_hexdigest(digest, hexdigest));
diff --git a/source/blender/imbuf/intern/thumbs_font.c b/source/blender/imbuf/intern/thumbs_font.c
index c0a33f608a5..65848bfb55e 100644
--- a/source/blender/imbuf/intern/thumbs_font.c
+++ b/source/blender/imbuf/intern/thumbs_font.c
@@ -41,7 +41,7 @@ void IMB_thumb_ensure_translations(void)
}
}
-struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned int y)
+struct ImBuf *IMB_thumb_load_font(const char *filepath, uint x, uint y)
{
const int font_size = y / 4;
@@ -66,7 +66,7 @@ struct ImBuf *IMB_thumb_load_font(const char *filepath, unsigned int x, unsigned
ARRAY_SIZE(thumb_str),
font_color,
font_size,
- (unsigned char *)ibuf->rect,
+ (uchar *)ibuf->rect,
ibuf->x,
ibuf->y,
ibuf->channels);
@@ -83,7 +83,7 @@ bool IMB_thumb_load_font_get_hash(char *r_hash)
int draw_str_lines = ARRAY_SIZE(thumb_str);
int i;
- unsigned char digest[16];
+ uchar digest[16];
len += BLI_strncpy_rlen(str + len, THUMB_DEFAULT_HASH, sizeof(buf) - len);
diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c
index 2f13ef409e3..f4829386aac 100644
--- a/source/blender/imbuf/intern/tiff.c
+++ b/source/blender/imbuf/intern/tiff.c
@@ -60,7 +60,7 @@ static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size);
/** Structure for in-memory TIFF file. */
typedef struct ImbTIFFMemFile {
/** Location of first byte of TIFF file. */
- const unsigned char *mem;
+ const uchar *mem;
/** Current offset within the file. */
toff_t offset;
/** Size of the TIFF file. */
@@ -262,7 +262,7 @@ static toff_t imb_tiff_SizeProc(thandle_t handle)
return (toff_t)(mfile->size);
}
-static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const unsigned char *mem, size_t size)
+static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const uchar *mem, size_t size)
{
/* open the TIFF client layer interface to the in-memory file */
memFile->mem = mem;
@@ -303,7 +303,7 @@ static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const unsigned char *
* hence my manual comparison. - Jonathan Merritt (lancelet) 4th Sept 2005.
*/
#define IMB_TIFF_NCB 4 /* number of comparison bytes used */
-bool imb_is_a_tiff(const unsigned char *buf, size_t size)
+bool imb_is_a_tiff(const uchar *buf, size_t size)
{
const char big_endian[IMB_TIFF_NCB] = {0x4d, 0x4d, 0x00, 0x2a};
const char lil_endian[IMB_TIFF_NCB] = {0x49, 0x49, 0x2a, 0x00};
@@ -315,10 +315,7 @@ bool imb_is_a_tiff(const unsigned char *buf, size_t size)
(memcmp(lil_endian, buf, IMB_TIFF_NCB) == 0));
}
-static void scanline_contig_16bit(float *rectf,
- const unsigned short *sbuf,
- int scanline_w,
- int spp)
+static void scanline_contig_16bit(float *rectf, const ushort *sbuf, int scanline_w, int spp)
{
int i;
for (i = 0; i < scanline_w; i++) {
@@ -340,10 +337,7 @@ static void scanline_contig_32bit(float *rectf, const float *fbuf, int scanline_
}
}
-static void scanline_separate_16bit(float *rectf,
- const unsigned short *sbuf,
- int scanline_w,
- int chan)
+static void scanline_separate_16bit(float *rectf, const ushort *sbuf, int scanline_w, int chan)
{
int i;
for (i = 0; i < scanline_w; i++) {
@@ -392,7 +386,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image)
size_t scanline;
int ib_flag = 0, row, chan;
float *fbuf = NULL;
- unsigned short *sbuf = NULL;
+ ushort *sbuf = NULL;
TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample);
TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp); /* number of 'channels' */
@@ -410,7 +404,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image)
* So let's keep this thing here for until proper solution is found (sergey)
*/
- unsigned short extraSampleTypes[1];
+ ushort extraSampleTypes[1];
extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA;
TIFFSetField(image, TIFFTAG_EXTRASAMPLES, 1, extraSampleTypes);
}
@@ -428,7 +422,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image)
}
else if (bitspersample == 16) {
ib_flag = IB_rectfloat;
- sbuf = (unsigned short *)_TIFFmalloc(scanline);
+ sbuf = (ushort *)_TIFFmalloc(scanline);
if (!sbuf) {
goto cleanup;
}
@@ -460,7 +454,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image)
scanline_contig_16bit(tmpibuf->rect_float + ib_offset, sbuf, ibuf->x, spp);
}
}
- /* separate channels: RRRGGGBBB */
+ /* Separate channels: RRRGGGBBB. */
}
else if (config == PLANARCONFIG_SEPARATE) {
@@ -539,20 +533,15 @@ void imb_inittiff(void)
}
}
-ImBuf *imb_loadtiff(const unsigned char *mem,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+ImBuf *imb_loadtiff(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
TIFF *image = NULL;
ImBuf *ibuf = NULL, *hbuf;
ImbTIFFMemFile memFile;
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 (imb_is_a_tiff(mem, size) == 0) {
@@ -574,7 +563,7 @@ ImBuf *imb_loadtiff(const unsigned char *mem,
TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height);
TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp);
- ib_depth = (spp == 3) ? 24 : 32;
+ ib_depth = spp * 8;
ibuf = IMB_allocImBuf(width, height, ib_depth, 0);
if (ibuf) {
@@ -591,9 +580,8 @@ ImBuf *imb_loadtiff(const unsigned char *mem,
/* get alpha mode from file header */
if (flags & IB_alphamode_detect) {
if (spp == 4) {
- unsigned short extra, *extraSampleTypes;
-
- found = TIFFGetField(image, TIFFTAG_EXTRASAMPLES, &extra, &extraSampleTypes);
+ ushort extra, *extraSampleTypes;
+ const int found = TIFFGetField(image, TIFFTAG_EXTRASAMPLES, &extra, &extraSampleTypes);
if (found && (extraSampleTypes[0] == EXTRASAMPLE_ASSOCALPHA)) {
ibuf->flags |= IB_alphamode_premul;
@@ -617,7 +605,7 @@ ImBuf *imb_loadtiff(const unsigned char *mem,
int numlevel = TIFFNumberOfDirectories(image);
/* create empty mipmap levels in advance */
- for (level = 0; level < numlevel; level++) {
+ for (int level = 0; level < numlevel; level++) {
if (!TIFFSetDirectory(image, level)) {
break;
}
@@ -664,8 +652,7 @@ ImBuf *imb_loadtiff(const unsigned char *mem,
return ibuf;
}
-void imb_loadtiletiff(
- ImBuf *ibuf, const unsigned char *mem, size_t size, int tx, int ty, unsigned int *rect)
+void imb_loadtiletiff(ImBuf *ibuf, const uchar *mem, size_t size, int tx, int ty, uint *rect)
{
TIFF *image = NULL;
uint32_t width, height;
@@ -726,9 +713,9 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
TIFF *image = NULL;
uint16_t samplesperpixel, bitspersample;
size_t npixels;
- unsigned char *pixels = NULL;
- unsigned char *from = NULL, *to = NULL;
- unsigned short *pixels16 = NULL, *to16 = NULL;
+ uchar *pixels = NULL;
+ uchar *from = NULL, *to = NULL;
+ ushort *pixels16 = NULL, *to16 = NULL;
float *fromf = NULL;
float xres, yres;
int x, y, from_i, to_i, i;
@@ -789,10 +776,10 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
/* allocate array for pixel data */
npixels = ibuf->x * ibuf->y;
if (bitspersample == 16) {
- pixels16 = (unsigned short *)_TIFFmalloc(npixels * samplesperpixel * sizeof(unsigned short));
+ pixels16 = (ushort *)_TIFFmalloc(npixels * samplesperpixel * sizeof(ushort));
}
else {
- pixels = (unsigned char *)_TIFFmalloc(npixels * samplesperpixel * sizeof(unsigned char));
+ pixels = (uchar *)_TIFFmalloc(npixels * samplesperpixel * sizeof(uchar));
}
if (pixels == NULL && pixels16 == NULL) {
@@ -807,7 +794,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
to16 = pixels16;
}
else {
- from = (unsigned char *)ibuf->rect;
+ from = (uchar *)ibuf->rect;
to = pixels;
}
@@ -816,7 +803,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
if (samplesperpixel == 4) {
- unsigned short extraSampleTypes[1];
+ ushort extraSampleTypes[1];
if (bitspersample == 16) {
extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA;
@@ -911,7 +898,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
if (TIFFWriteEncodedStrip(image,
0,
- (bitspersample == 16) ? (unsigned char *)pixels16 : pixels,
+ (bitspersample == 16) ? (uchar *)pixels16 : pixels,
(size_t)ibuf->x * ibuf->y * samplesperpixel * bitspersample / 8) ==
-1) {
fprintf(stderr, "imb_savetiff: Could not write encoded TIFF.\n");
diff --git a/source/blender/imbuf/intern/transform.cc b/source/blender/imbuf/intern/transform.cc
index 1499c1071e3..276d31c0557 100644
--- a/source/blender/imbuf/intern/transform.cc
+++ b/source/blender/imbuf/intern/transform.cc
@@ -147,7 +147,7 @@ class NoDiscard : public BaseDiscard {
template<
/**
* \brief Kind of buffer.
- * Possible options: float, unsigned char.
+ * Possible options: float, uchar.
*/
typename StorageType = float,
@@ -170,10 +170,9 @@ class PixelPointer {
if constexpr (std::is_same_v<StorageType, float>) {
pointer = image_buffer->rect_float + offset;
}
- else if constexpr (std::is_same_v<StorageType, unsigned char>) {
- pointer = const_cast<unsigned char *>(
- static_cast<const unsigned char *>(static_cast<const void *>(image_buffer->rect)) +
- offset);
+ else if constexpr (std::is_same_v<StorageType, uchar>) {
+ pointer = const_cast<uchar *>(
+ static_cast<const uchar *>(static_cast<const void *>(image_buffer->rect)) + offset);
}
else {
pointer = nullptr;
@@ -259,13 +258,12 @@ class WrapRepeatUV : public BaseUVWrapping {
* \brief Read a sample from an image buffer.
*
* A sampler can read from an image buffer.
- *
*/
template<
/** \brief Interpolation mode to use when sampling. */
eIMBInterpolationFilterMode Filter,
- /** \brief storage type of a single pixel channel (unsigned char or float). */
+ /** \brief storage type of a single pixel channel (uchar or float). */
typename StorageType,
/**
* \brief number of channels if the image to read.
@@ -295,14 +293,14 @@ class Sampler {
const float wrapped_v = uv_wrapper.modify_v(source, v);
bilinear_interpolation_color_fl(source, nullptr, r_sample.data(), wrapped_u, wrapped_v);
}
- else if constexpr (Filter == IMB_FILTER_NEAREST &&
- std::is_same_v<StorageType, unsigned char> && NumChannels == 4) {
+ else if constexpr (Filter == IMB_FILTER_NEAREST && std::is_same_v<StorageType, uchar> &&
+ NumChannels == 4) {
const float wrapped_u = uv_wrapper.modify_u(source, u);
const float wrapped_v = uv_wrapper.modify_v(source, v);
nearest_interpolation_color_char(source, r_sample.data(), nullptr, wrapped_u, wrapped_v);
}
- else if constexpr (Filter == IMB_FILTER_BILINEAR &&
- std::is_same_v<StorageType, unsigned char> && NumChannels == 4) {
+ else if constexpr (Filter == IMB_FILTER_BILINEAR && std::is_same_v<StorageType, uchar> &&
+ NumChannels == 4) {
const float wrapped_u = uv_wrapper.modify_u(source, u);
const float wrapped_v = uv_wrapper.modify_v(source, v);
bilinear_interpolation_color_char(source, r_sample.data(), nullptr, wrapped_u, wrapped_v);
@@ -375,7 +373,7 @@ class Sampler {
*
* Template class to convert and store a sample in a PixelPointer.
* It supports:
- * - 4 channel unsigned char -> 4 channel unsigned char.
+ * - 4 channel uchar -> 4 channel uchar.
* - 4 channel float -> 4 channel float.
* - 3 channel float -> 4 channel float.
* - 2 channel float -> 4 channel float.
@@ -393,7 +391,7 @@ class ChannelConverter {
*/
void convert_and_store(const SampleType &sample, PixelType &pixel_pointer)
{
- if constexpr (std::is_same_v<StorageType, unsigned char>) {
+ if constexpr (std::is_same_v<StorageType, uchar>) {
BLI_STATIC_ASSERT(SourceNumChannels == 4, "Unsigned chars always have 4 channels.");
BLI_STATIC_ASSERT(DestinationNumChannels == 4, "Unsigned chars always have 4 channels.");
@@ -551,8 +549,8 @@ static void transform_threaded(TransformUserData *user_data, const eIMBTransform
scanline_func = get_scanline_function<Filter>(user_data, mode);
}
else if (user_data->dst->rect && user_data->src->rect) {
- /* Number of channels is always 4 when using unsigned char buffers (sRGB + straight alpha). */
- scanline_func = get_scanline_function<Filter, unsigned char, 4, 4>(mode);
+ /* Number of channels is always 4 when using uchar buffers (sRGB + straight alpha). */
+ scanline_func = get_scanline_function<Filter, uchar, 4, 4>(mode);
}
if (scanline_func != nullptr) {
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index ffa989a29b4..2870ff56c0a 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -106,8 +106,7 @@ const char *imb_ext_audio[] = {
/* Increased from 32 to 64 because of the bitmaps header size. */
#define HEADER_SIZE 64
-static ssize_t imb_ispic_read_header_from_filepath(const char *filepath,
- unsigned char buf[HEADER_SIZE])
+static ssize_t imb_ispic_read_header_from_filepath(const char *filepath, uchar buf[HEADER_SIZE])
{
BLI_stat_t st;
int fp;
@@ -135,7 +134,7 @@ static ssize_t imb_ispic_read_header_from_filepath(const char *filepath,
return size;
}
-int IMB_ispic_type_from_memory(const unsigned char *buf, const size_t buf_size)
+int IMB_ispic_type_from_memory(const uchar *buf, const size_t buf_size)
{
for (const ImFileType *type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
if (type->is_a != NULL) {
@@ -150,7 +149,7 @@ int IMB_ispic_type_from_memory(const unsigned char *buf, const size_t buf_size)
int IMB_ispic_type(const char *filepath)
{
- unsigned char buf[HEADER_SIZE];
+ uchar buf[HEADER_SIZE];
const ssize_t buf_size = imb_ispic_read_header_from_filepath(filepath, buf);
if (buf_size <= 0) {
return IMB_FTYPE_NONE;
@@ -160,7 +159,7 @@ int IMB_ispic_type(const char *filepath)
bool IMB_ispic_type_matches(const char *filepath, int filetype)
{
- unsigned char buf[HEADER_SIZE];
+ uchar buf[HEADER_SIZE];
const ssize_t buf_size = imb_ispic_read_header_from_filepath(filepath, buf);
if (buf_size <= 0) {
return false;
@@ -251,7 +250,7 @@ const char *IMB_ffmpeg_last_error(void)
static int isffmpeg(const char *filepath)
{
AVFormatContext *pFormatCtx = NULL;
- unsigned int i;
+ uint i;
int videoStream;
const AVCodec *pCodec;
diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c
index 5feb0ceb515..6f1275e1812 100644
--- a/source/blender/imbuf/intern/util_gpu.c
+++ b/source/blender/imbuf/intern/util_gpu.c
@@ -14,6 +14,7 @@
#include "BKE_global.h"
#include "GPU_capabilities.h"
+#include "GPU_state.h"
#include "GPU_texture.h"
#include "IMB_colormanagement.h"
@@ -22,39 +23,62 @@
/* gpu ibuf utils */
+static bool imb_is_grayscale_texture_format_compatible(const ImBuf *ibuf)
+{
+ if (ibuf->planes > 8) {
+ return false;
+ }
+ /* Only imbufs with colorspace that do not modify the chrominance of the texture data relative
+ * to the scene color space can be uploaded as single channel textures. */
+ if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace) ||
+ IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) ||
+ IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) {
+ return true;
+ };
+ return false;
+}
+
static void imb_gpu_get_format(const ImBuf *ibuf,
bool high_bitdepth,
+ bool use_grayscale,
eGPUDataFormat *r_data_format,
eGPUTextureFormat *r_texture_format)
{
const bool float_rect = (ibuf->rect_float != NULL);
+ const bool is_grayscale = use_grayscale && imb_is_grayscale_texture_format_compatible(ibuf);
if (float_rect) {
/* Float. */
const bool use_high_bitdepth = (!(ibuf->flags & IB_halffloat) && high_bitdepth);
*r_data_format = GPU_DATA_FLOAT;
- *r_texture_format = use_high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F;
+ *r_texture_format = is_grayscale ? (use_high_bitdepth ? GPU_R32F : GPU_R16F) :
+ (use_high_bitdepth ? GPU_RGBA32F : GPU_RGBA16F);
}
else {
if (IMB_colormanagement_space_is_data(ibuf->rect_colorspace) ||
IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) {
/* Non-color data or scene linear, just store buffer as is. */
*r_data_format = GPU_DATA_UBYTE;
- *r_texture_format = GPU_RGBA8;
+ *r_texture_format = (is_grayscale) ? GPU_R8 : GPU_RGBA8;
}
else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace)) {
/* sRGB, store as byte texture that the GPU can decode directly. */
- *r_data_format = GPU_DATA_UBYTE;
- *r_texture_format = GPU_SRGB8_A8;
+ *r_data_format = (is_grayscale) ? GPU_DATA_FLOAT : GPU_DATA_UBYTE;
+ *r_texture_format = (is_grayscale) ? GPU_R16F : GPU_SRGB8_A8;
}
else {
/* Other colorspace, store as half float texture to avoid precision loss. */
*r_data_format = GPU_DATA_FLOAT;
- *r_texture_format = GPU_RGBA16F;
+ *r_texture_format = (is_grayscale) ? GPU_R16F : GPU_RGBA16F;
}
}
}
+static const char *imb_gpu_get_swizzle(const ImBuf *ibuf)
+{
+ return imb_is_grayscale_texture_format_compatible(ibuf) ? "rrra" : "rgba";
+}
+
/* Return false if no suitable format was found. */
#ifdef WITH_DDS
static bool IMB_gpu_get_compressed_format(const ImBuf *ibuf, eGPUTextureFormat *r_texture_format)
@@ -90,7 +114,8 @@ static void *imb_gpu_get_data(const ImBuf *ibuf,
const bool store_premultiplied,
bool *r_freedata)
{
- const bool is_float_rect = (ibuf->rect_float != NULL);
+ bool is_float_rect = (ibuf->rect_float != NULL);
+ const bool is_grayscale = imb_is_grayscale_texture_format_compatible(ibuf);
void *data_rect = (is_float_rect) ? (void *)ibuf->rect_float : (void *)ibuf->rect;
bool freedata = false;
@@ -121,7 +146,8 @@ static void *imb_gpu_get_data(const ImBuf *ibuf,
else if (IMB_colormanagement_space_is_srgb(ibuf->rect_colorspace) ||
IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)) {
/* sRGB or scene linear, store as byte texture that the GPU can decode directly. */
- data_rect = MEM_mallocN(sizeof(uchar[4]) * ibuf->x * ibuf->y, __func__);
+ data_rect = MEM_mallocN(
+ (is_grayscale ? sizeof(float[4]) : sizeof(uchar[4])) * ibuf->x * ibuf->y, __func__);
*r_freedata = freedata = true;
if (data_rect == NULL) {
@@ -133,8 +159,16 @@ static void *imb_gpu_get_data(const ImBuf *ibuf,
* this allows us to use sRGB texture formats and preserves color values in
* zero alpha areas, and appears generally closer to what game engines that we
* want to be compatible with do. */
- IMB_colormanagement_imbuf_to_byte_texture(
- (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
+ if (is_grayscale) {
+ /* Convert to byte buffer to then pack as half floats reducing the buffer size by half. */
+ IMB_colormanagement_imbuf_to_float_texture(
+ (float *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
+ is_float_rect = true;
+ }
+ else {
+ IMB_colormanagement_imbuf_to_byte_texture(
+ (uchar *)data_rect, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied);
+ }
}
else {
/* Other colorspace, store as float texture to avoid precision loss. */
@@ -167,21 +201,52 @@ static void *imb_gpu_get_data(const ImBuf *ibuf,
}
data_rect = (is_float_rect) ? (void *)scale_ibuf->rect_float : (void *)scale_ibuf->rect;
- *r_freedata = true;
+ *r_freedata = freedata = true;
/* Steal the rescaled buffer to avoid double free. */
scale_ibuf->rect_float = NULL;
scale_ibuf->rect = NULL;
IMB_freeImBuf(scale_ibuf);
}
+
+ /* Pack first channel data manually at the start of the buffer. */
+ if (is_grayscale) {
+ void *src_rect = data_rect;
+
+ if (freedata == false) {
+ data_rect = MEM_mallocN((is_float_rect ? sizeof(float) : sizeof(uchar)) * ibuf->x * ibuf->y,
+ __func__);
+ *r_freedata = freedata = true;
+ }
+
+ if (data_rect == NULL) {
+ return NULL;
+ }
+
+ if (is_float_rect) {
+ for (uint64_t i = 0; i < ibuf->x * ibuf->y; i++) {
+ ((float *)data_rect)[i] = ((float *)src_rect)[i * 4];
+ }
+ }
+ else {
+ for (uint64_t i = 0; i < ibuf->x * ibuf->y; i++) {
+ ((uchar *)data_rect)[i] = ((uchar *)src_rect)[i * 4];
+ }
+ }
+ }
return data_rect;
}
-GPUTexture *IMB_touch_gpu_texture(
- const char *name, ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth)
+GPUTexture *IMB_touch_gpu_texture(const char *name,
+ ImBuf *ibuf,
+ int w,
+ int h,
+ int layers,
+ bool use_high_bitdepth,
+ bool use_grayscale)
{
eGPUDataFormat data_format;
eGPUTextureFormat tex_format;
- imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
+ imb_gpu_get_format(ibuf, use_high_bitdepth, use_grayscale, &data_format, &tex_format);
GPUTexture *tex;
if (layers > 0) {
@@ -191,6 +256,7 @@ GPUTexture *IMB_touch_gpu_texture(
tex = GPU_texture_create_2d(name, w, h, 9999, tex_format, NULL);
}
+ GPU_texture_swizzle_set(tex, imb_gpu_get_swizzle(ibuf));
GPU_texture_anisotropic_filter(tex, true);
return tex;
}
@@ -203,6 +269,7 @@ void IMB_update_gpu_texture_sub(GPUTexture *tex,
int w,
int h,
bool use_high_bitdepth,
+ bool use_grayscale,
bool use_premult)
{
const bool do_rescale = (ibuf->x != w || ibuf->y != h);
@@ -210,7 +277,7 @@ void IMB_update_gpu_texture_sub(GPUTexture *tex,
eGPUDataFormat data_format;
eGPUTextureFormat tex_format;
- imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
+ imb_gpu_get_format(ibuf, use_high_bitdepth, use_grayscale, &data_format, &tex_format);
bool freebuf = false;
@@ -266,7 +333,7 @@ GPUTexture *IMB_create_gpu_texture(const char *name,
eGPUDataFormat data_format;
eGPUTextureFormat tex_format;
- imb_gpu_get_format(ibuf, use_high_bitdepth, &data_format, &tex_format);
+ imb_gpu_get_format(ibuf, use_high_bitdepth, true, &data_format, &tex_format);
bool freebuf = false;
@@ -282,6 +349,7 @@ GPUTexture *IMB_create_gpu_texture(const char *name,
void *data = imb_gpu_get_data(ibuf, do_rescale, size, use_premult, &freebuf);
GPU_texture_update(tex, data_format, data);
+ GPU_texture_swizzle_set(tex, imb_gpu_get_swizzle(ibuf));
GPU_texture_anisotropic_filter(tex, true);
if (freebuf) {
@@ -290,3 +358,15 @@ GPUTexture *IMB_create_gpu_texture(const char *name,
return tex;
}
+
+eGPUTextureFormat IMB_gpu_get_texture_format(const ImBuf *ibuf,
+ bool high_bitdepth,
+ bool use_grayscale)
+{
+ eGPUTextureFormat gpu_texture_format;
+ eGPUDataFormat gpu_data_format;
+
+ imb_gpu_get_format(ibuf, high_bitdepth, use_grayscale, &gpu_data_format, &gpu_texture_format);
+
+ return gpu_texture_format;
+}
diff --git a/source/blender/imbuf/intern/webp.c b/source/blender/imbuf/intern/webp.c
index 19fe2373ea0..27c26fb19c1 100644
--- a/source/blender/imbuf/intern/webp.c
+++ b/source/blender/imbuf/intern/webp.c
@@ -4,14 +4,23 @@
* \ingroup imbuf
*/
+#ifdef _WIN32
+# include <io.h>
+#else
+# include <unistd.h>
+#endif
+
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <webp/decode.h>
#include <webp/encode.h>
#include "BLI_fileops.h"
+#include "BLI_mmap.h"
#include "BLI_utildefines.h"
+#include "IMB_allocimbuf.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
#include "IMB_filetype.h"
@@ -20,7 +29,7 @@
#include "MEM_guardedalloc.h"
-bool imb_is_a_webp(const unsigned char *buf, size_t size)
+bool imb_is_a_webp(const uchar *buf, size_t size)
{
if (WebPGetInfo(buf, size, NULL, NULL)) {
return true;
@@ -28,10 +37,7 @@ bool imb_is_a_webp(const unsigned char *buf, size_t size)
return false;
}
-ImBuf *imb_loadwebp(const unsigned char *mem,
- size_t size,
- int flags,
- char colorspace[IM_MAX_SPACE])
+ImBuf *imb_loadwebp(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
if (!imb_is_a_webp(mem, size)) {
return NULL;
@@ -57,7 +63,7 @@ ImBuf *imb_loadwebp(const unsigned char *mem,
ibuf->ftype = IMB_FTYPE_WEBP;
imb_addrectImBuf(ibuf);
/* Flip the image during decoding to match Blender. */
- unsigned char *last_row = (unsigned char *)(ibuf->rect + (ibuf->y - 1) * ibuf->x);
+ uchar *last_row = (uchar *)(ibuf->rect + (ibuf->y - 1) * ibuf->x);
if (WebPDecodeRGBAInto(mem, size, last_row, (size_t)(ibuf->x) * ibuf->y * 4, -4 * ibuf->x) ==
NULL) {
fprintf(stderr, "WebP: Failed to decode image\n");
@@ -67,10 +73,93 @@ ImBuf *imb_loadwebp(const unsigned char *mem,
return ibuf;
}
+struct ImBuf *imb_load_filepath_thumbnail_webp(const char *filepath,
+ const int UNUSED(flags),
+ const size_t max_thumb_size,
+ char colorspace[],
+ size_t *r_width,
+ size_t *r_height)
+{
+ const int file = BLI_open(filepath, O_BINARY | O_RDONLY, 0);
+ if (file == -1) {
+ return NULL;
+ }
+
+ const size_t data_size = BLI_file_descriptor_size(file);
+
+ imb_mmap_lock();
+ BLI_mmap_file *mmap_file = BLI_mmap_open(file);
+ imb_mmap_unlock();
+ close(file);
+ if (mmap_file == NULL) {
+ return NULL;
+ }
+
+ const uchar *data = BLI_mmap_get_pointer(mmap_file);
+
+ WebPDecoderConfig config;
+ if (!data || !WebPInitDecoderConfig(&config) ||
+ WebPGetFeatures(data, data_size, &config.input) != VP8_STATUS_OK) {
+ fprintf(stderr, "WebP: Invalid file\n");
+ imb_mmap_lock();
+ BLI_mmap_free(mmap_file);
+ imb_mmap_unlock();
+ return NULL;
+ }
+
+ /* Return full size of the image. */
+ *r_width = (size_t)config.input.width;
+ *r_height = (size_t)config.input.height;
+
+ const float scale = (float)max_thumb_size / MAX2(config.input.width, config.input.height);
+ const int dest_w = (int)(config.input.width * scale);
+ const int dest_h = (int)(config.input.height * scale);
+
+ colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
+ struct ImBuf *ibuf = IMB_allocImBuf(dest_w, dest_h, 32, IB_rect);
+ if (ibuf == NULL) {
+ fprintf(stderr, "WebP: Failed to allocate image memory\n");
+ imb_mmap_lock();
+ BLI_mmap_free(mmap_file);
+ imb_mmap_unlock();
+ return NULL;
+ }
+
+ config.options.no_fancy_upsampling = 1;
+ config.options.use_scaling = 1;
+ config.options.scaled_width = dest_w;
+ config.options.scaled_height = dest_h;
+ config.options.bypass_filtering = 1;
+ config.options.use_threads = 0;
+ config.options.flip = 1;
+ config.output.is_external_memory = 1;
+ config.output.colorspace = MODE_RGBA;
+ config.output.u.RGBA.rgba = (uint8_t *)ibuf->rect;
+ config.output.u.RGBA.stride = 4 * ibuf->x;
+ config.output.u.RGBA.size = (size_t)(config.output.u.RGBA.stride * ibuf->y);
+
+ if (WebPDecode(data, data_size, &config) != VP8_STATUS_OK) {
+ fprintf(stderr, "WebP: Failed to decode image\n");
+ imb_mmap_lock();
+ BLI_mmap_free(mmap_file);
+ imb_mmap_unlock();
+ return NULL;
+ }
+
+ /* Free the output buffer. */
+ WebPFreeDecBuffer(&config.output);
+
+ imb_mmap_lock();
+ BLI_mmap_free(mmap_file);
+ imb_mmap_unlock();
+
+ return ibuf;
+}
+
bool imb_savewebp(struct ImBuf *ibuf, const char *name, int UNUSED(flags))
{
const int bytesperpixel = (ibuf->planes + 7) >> 3;
- unsigned char *encoded_data, *last_row;
+ uchar *encoded_data, *last_row;
size_t encoded_data_size;
if (bytesperpixel == 3) {
@@ -84,7 +173,7 @@ bool imb_savewebp(struct ImBuf *ibuf, const char *name, int UNUSED(flags))
rgb_rect[i * 3 + 2] = rgba_rect[i * 4 + 2];
}
- last_row = (unsigned char *)(rgb_rect + (ibuf->y - 1) * ibuf->x * 3);
+ last_row = (uchar *)(rgb_rect + (ibuf->y - 1) * ibuf->x * 3);
if (ibuf->foptions.quality == 100.0f) {
encoded_data_size = WebPEncodeLosslessRGB(
@@ -97,7 +186,7 @@ bool imb_savewebp(struct ImBuf *ibuf, const char *name, int UNUSED(flags))
MEM_freeN(rgb_rect);
}
else if (bytesperpixel == 4) {
- last_row = (unsigned char *)(ibuf->rect + (ibuf->y - 1) * ibuf->x);
+ last_row = (uchar *)(ibuf->rect + (ibuf->y - 1) * ibuf->x);
if (ibuf->foptions.quality == 100.0f) {
encoded_data_size = WebPEncodeLosslessRGBA(
diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h
index ded3258ff18..05025861857 100644
--- a/source/blender/io/alembic/ABC_alembic.h
+++ b/source/blender/io/alembic/ABC_alembic.h
@@ -60,6 +60,28 @@ struct AlembicExportParams {
float global_scale;
};
+struct AlembicImportParams {
+ /* Multiplier for the cached data scale. Mostly useful if the data is stored in a different unit
+ * as what Blender expects (e.g. centimeters instead of meters). */
+ float global_scale;
+
+ /* Number of consecutive files to expect if the cached animation is split in a sequence. */
+ int sequence_len;
+ /* Start frame of the sequence, offset from 0. */
+ int sequence_offset;
+ /* True if the cache is split in multiple files. */
+ bool is_sequence;
+
+ /* True if the importer should set the current scene's start and end frame based on the start and
+ * end frames of the cached animation. */
+ bool set_frame_range;
+ /* True if imported meshes should be validated. Error messages are sent to the console. */
+ bool validate_meshes;
+ /* True if a cache reader should be added regardless of whether there is animated data in the
+ * cached file. */
+ bool always_add_cache_reader;
+};
+
/* The ABC_export and ABC_import functions both take a as_background_job
* parameter, and return a boolean.
*
@@ -78,13 +100,7 @@ bool ABC_export(struct Scene *scene,
bool ABC_import(struct bContext *C,
const char *filepath,
- float scale,
- bool is_sequence,
- bool set_frame_range,
- int sequence_len,
- int offset,
- bool validate_meshes,
- bool always_add_cache_reader,
+ const struct AlembicImportParams *params,
bool as_background_job);
struct CacheArchiveHandle *ABC_create_handle(struct Main *bmain,
diff --git a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
index 514ce389e36..712b04f3992 100644
--- a/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
+++ b/source/blender/io/alembic/exporter/abc_subdiv_disabler.cc
@@ -14,6 +14,7 @@
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
+#include "BKE_layer.h"
#include "BKE_modifier.h"
namespace blender::io::alembic {
@@ -34,7 +35,8 @@ void SubdivModifierDisabler::disable_modifiers()
Scene *scene = DEG_get_input_scene(depsgraph_);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph_);
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *object = base->object;
if (object->type != OB_MESH) {
diff --git a/source/blender/io/alembic/exporter/abc_writer_hair.cc b/source/blender/io/alembic/exporter/abc_writer_hair.cc
index 99c609b0235..4f09aee3ea9 100644
--- a/source/blender/io/alembic/exporter/abc_writer_hair.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_hair.cc
@@ -120,9 +120,9 @@ void ABCHairWriter::write_hair_sample(const HierarchyContext &context,
float inv_mat[4][4];
invert_m4_m4_safe(inv_mat, context.object->obmat);
- MTFace *mtface = mesh->mtface;
- MFace *mface = mesh->mface;
- MVert *mverts = mesh->mvert;
+ MTFace *mtface = (MTFace *)CustomData_get_layer(&mesh->fdata, CD_MTFACE);
+ MFace *mface = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ const MVert *mverts = BKE_mesh_verts(mesh);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
if ((!mtface || !mface) && !uv_warning_shown_) {
@@ -243,8 +243,9 @@ void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
float inv_mat[4][4];
invert_m4_m4_safe(inv_mat, context.object->obmat);
- MTFace *mtface = mesh->mtface;
- MVert *mverts = mesh->mvert;
+ MFace *mface = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
+ MTFace *mtface = (MTFace *)CustomData_get_layer(&mesh->fdata, CD_MTFACE);
+ const MVert *mverts = BKE_mesh_verts(mesh);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
ParticleSystem *psys = context.particle_system;
@@ -269,7 +270,7 @@ void ABCHairWriter::write_hair_child_sample(const HierarchyContext &context,
continue;
}
- MFace *face = &mesh->mface[num];
+ MFace *face = &mface[num];
MTFace *tface = mtface + num;
float r_uv[2], tmpnor[3], mapfw[4], vec[3];
diff --git a/source/blender/io/alembic/exporter/abc_writer_mesh.cc b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
index 07b185ffd64..7d38cd1ec88 100644
--- a/source/blender/io/alembic/exporter/abc_writer_mesh.cc
+++ b/source/blender/io/alembic/exporter/abc_writer_mesh.cc
@@ -12,6 +12,7 @@
#include "BLI_math_vector.h"
#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_material.h"
@@ -175,8 +176,8 @@ 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.mpoly = mesh->polys_for_write().data();
+ m_custom_data_config.mloop = mesh->loops_for_write().data();
m_custom_data_config.totpoly = mesh->totpoly;
m_custom_data_config.totloop = mesh->totloop;
m_custom_data_config.totvert = mesh->totvert;
@@ -366,7 +367,7 @@ bool ABCGenericMeshWriter::get_velocities(struct Mesh *mesh, std::vector<Imath::
{
/* Export velocity attribute output by fluid sim, sequence cache modifier
* and geometry nodes. */
- CustomDataLayer *velocity_layer = BKE_id_attribute_find(
+ const CustomDataLayer *velocity_layer = BKE_id_attribute_find(
&mesh->id, "velocity", CD_PROP_FLOAT3, ATTR_DOMAIN_POINT);
if (velocity_layer == nullptr) {
@@ -390,12 +391,12 @@ void ABCGenericMeshWriter::get_geo_groups(Object *object,
struct Mesh *mesh,
std::map<std::string, std::vector<int32_t>> &geo_groups)
{
- const int num_poly = mesh->totpoly;
- MPoly *polygons = mesh->mpoly;
+ const bke::AttributeAccessor attributes = mesh->attributes();
+ const VArraySpan<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
- for (int i = 0; i < num_poly; i++) {
- MPoly &current_poly = polygons[i];
- short mnr = current_poly.mat_nr;
+ for (const int i : material_indices.index_range()) {
+ short mnr = material_indices[i];
Material *mat = BKE_object_material_get(object, mnr + 1);
@@ -435,8 +436,7 @@ static void get_vertices(struct Mesh *mesh, std::vector<Imath::V3f> &points)
points.clear();
points.resize(mesh->totvert);
- MVert *verts = mesh->mvert;
-
+ const Span<MVert> verts = mesh->verts();
for (int i = 0, e = mesh->totvert; i < e; i++) {
copy_yup_from_zup(points[i].getValue(), verts[i].co);
}
@@ -447,25 +447,23 @@ static void get_topology(struct Mesh *mesh,
std::vector<int32_t> &loop_counts,
bool &r_has_flat_shaded_poly)
{
- const int num_poly = mesh->totpoly;
- const int num_loops = mesh->totloop;
- MLoop *mloop = mesh->mloop;
- MPoly *mpoly = mesh->mpoly;
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
r_has_flat_shaded_poly = false;
poly_verts.clear();
loop_counts.clear();
- poly_verts.reserve(num_loops);
- loop_counts.reserve(num_poly);
+ poly_verts.reserve(loops.size());
+ loop_counts.reserve(polys.size());
/* NOTE: data needs to be written in the reverse order. */
- for (int i = 0; i < num_poly; i++) {
- MPoly &poly = mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
loop_counts.push_back(poly.totloop);
r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0;
- MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1);
+ const MLoop *loop = &loops[poly.loopstart + (poly.totloop - 1)];
for (int j = 0; j < poly.totloop; j++, loop--) {
poly_verts.push_back(loop->v);
@@ -484,14 +482,14 @@ static void get_edge_creases(struct Mesh *mesh,
lengths.clear();
sharpnesses.clear();
- MEdge *edge = mesh->medge;
+ const Span<MEdge> edges = mesh->edges();
- for (int i = 0, e = mesh->totedge; i < e; i++) {
- const float sharpness = static_cast<float>(edge[i].crease) * factor;
+ for (const int i : edges.index_range()) {
+ const float sharpness = static_cast<float>(edges[i].crease) * factor;
if (sharpness != 0.0f) {
- indices.push_back(edge[i].v1);
- indices.push_back(edge[i].v2);
+ indices.push_back(edges[i].v1);
+ indices.push_back(edges[i].v2);
sharpnesses.push_back(sharpness);
}
}
@@ -543,8 +541,10 @@ static void get_loop_normals(struct Mesh *mesh,
/* NOTE: data needs to be written in the reverse order. */
int abc_index = 0;
- MPoly *mp = mesh->mpoly;
- for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) {
+ const Span<MPoly> polys = mesh->polys();
+
+ for (const int i : polys.index_range()) {
+ const MPoly *mp = &polys[i];
for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) {
int blender_index = mp->loopstart + j;
copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]);
diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc
index 2820a128072..5494bfaa6e8 100644
--- a/source/blender/io/alembic/intern/abc_customdata.cc
+++ b/source/blender/io/alembic/intern/abc_customdata.cc
@@ -57,7 +57,7 @@ static void get_uvs(const CDStreamConfig &config,
}
const int num_poly = config.totpoly;
- MPoly *polygons = config.mpoly;
+ MPoly *mpoly = config.mpoly;
MLoop *mloop = config.mloop;
if (!config.pack_uvs) {
@@ -67,7 +67,7 @@ static void get_uvs(const CDStreamConfig &config,
/* Iterate in reverse order to match exported polygons. */
for (int i = 0; i < num_poly; i++) {
- MPoly &current_poly = polygons[i];
+ MPoly &current_poly = mpoly[i];
const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop;
for (int j = 0; j < current_poly.totloop; j++, count++) {
@@ -85,7 +85,7 @@ static void get_uvs(const CDStreamConfig &config,
int idx_count = 0;
for (int i = 0; i < num_poly; i++) {
- MPoly &current_poly = polygons[i];
+ MPoly &current_poly = mpoly[i];
MLoop *looppoly = mloop + current_poly.loopstart + current_poly.totloop;
const MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop;
@@ -540,7 +540,7 @@ void read_generated_coordinates(const ICompoundProperty &prop,
cd_data = CustomData_get_layer(&mesh->vdata, CD_ORCO);
}
else {
- cd_data = CustomData_add_layer(&mesh->vdata, CD_ORCO, CD_CALLOC, nullptr, totvert);
+ cd_data = CustomData_add_layer(&mesh->vdata, CD_ORCO, CD_CONSTRUCT, nullptr, totvert);
}
float(*orcodata)[3] = static_cast<float(*)[3]>(cd_data);
@@ -564,7 +564,6 @@ void read_custom_data(const std::string &iobject_full_name,
}
int num_uvs = 0;
- int num_colors = 0;
const size_t num_props = prop.getNumProperties();
@@ -583,10 +582,6 @@ void read_custom_data(const std::string &iobject_full_name,
/* Read vertex colors according to convention. */
if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) {
- if (++num_colors > MAX_MCOL) {
- continue;
- }
-
read_custom_data_mcols(iobject_full_name, prop, prop_header, config, iss);
continue;
}
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc
index df3559c108c..c07aaa37988 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.cc
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc
@@ -20,11 +20,13 @@
#include "DNA_object_types.h"
#include "BLI_compiler_compat.h"
+#include "BLI_edgehash.h"
#include "BLI_index_range.hh"
#include "BLI_listbase.h"
#include "BLI_math_geom.h"
-#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
@@ -92,6 +94,7 @@ static void assign_materials(Main *bmain,
mat_iter = matname_to_material.find(mat_name);
if (mat_iter == matname_to_material.end()) {
assigned_mat = BKE_material_add(bmain, mat_name.c_str());
+ id_us_min(&assigned_mat->id);
matname_to_material[mat_name] = assigned_mat;
}
else {
@@ -100,6 +103,9 @@ static void assign_materials(Main *bmain,
BKE_object_material_assign_single_obdata(bmain, ob, assigned_mat, mat_index);
}
+ if (ob->totcol > 0) {
+ ob->actcol = 1;
+ }
}
} /* namespace utils */
@@ -129,8 +135,6 @@ static void read_mverts_interp(MVert *mverts,
interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), static_cast<float>(weight));
copy_zup_from_yup(mvert.co, tmp);
-
- mvert.bweight = 0;
}
}
@@ -151,13 +155,12 @@ static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data)
void read_mverts(Mesh &mesh, const P3fArraySamplePtr positions, const N3fArraySamplePtr normals)
{
+ MutableSpan<MVert> verts = mesh.verts_for_write();
for (int i = 0; i < positions->size(); i++) {
- MVert &mvert = mesh.mvert[i];
+ MVert &mvert = verts[i];
Imath::V3f pos_in = (*positions)[i];
copy_zup_from_yup(mvert.co, pos_in.getValue());
-
- mvert.bweight = 0;
}
if (normals) {
float(*vert_normals)[3] = BKE_mesh_vertex_normals_for_write(&mesh);
@@ -268,7 +271,7 @@ static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr
float(*lnors)[3] = static_cast<float(*)[3]>(
MEM_malloc_arrayN(loop_count, sizeof(float[3]), "ABC::FaceNormals"));
- MPoly *mpoly = mesh->mpoly;
+ MPoly *mpoly = mesh->polys_for_write().data();
const N3fArraySample &loop_normals = *loop_normals_ptr;
int abc_index = 0;
for (int i = 0, e = mesh->totpoly; i < e; i++, mpoly++) {
@@ -303,7 +306,7 @@ static void process_vertex_normals(CDStreamConfig &config,
}
config.mesh->flag |= ME_AUTOSMOOTH;
- BKE_mesh_set_custom_normals_from_vertices(config.mesh, vnors);
+ BKE_mesh_set_custom_normals_from_verts(config.mesh, vnors);
MEM_freeN(vnors);
}
@@ -390,7 +393,7 @@ static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type)
/* Create a new layer. */
int numloops = mesh->totloop;
cd_ptr = CustomData_add_layer_named(
- &mesh->ldata, cd_data_type, CD_DEFAULT, nullptr, numloops, name);
+ &mesh->ldata, cd_data_type, CD_SET_DEFAULT, nullptr, numloops, name);
return cd_ptr;
}
@@ -513,13 +516,10 @@ static void read_mesh_sample(const std::string &iobject_full_name,
CDStreamConfig get_config(Mesh *mesh, const bool use_vertex_interpolation)
{
CDStreamConfig config;
-
- BLI_assert(mesh->mvert || mesh->totvert == 0);
-
config.mesh = mesh;
- config.mvert = mesh->mvert;
- config.mloop = mesh->mloop;
- config.mpoly = mesh->mpoly;
+ config.mvert = mesh->verts_for_write().data();
+ config.mloop = mesh->loops_for_write().data();
+ config.mpoly = mesh->polys_for_write().data();
config.totvert = mesh->totvert;
config.totloop = mesh->totloop;
config.totpoly = mesh->totpoly;
@@ -615,11 +615,7 @@ void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr);
if (read_mesh != mesh) {
- /* 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. */
- uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
- mesh->flag |= autosmooth;
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object);
}
if (m_settings->validate_meshes) {
@@ -765,7 +761,11 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh,
size_t num_polys = new_mesh->totpoly;
if (num_polys > 0) {
std::map<std::string, int> mat_map;
- assign_facesets_to_mpoly(sample_sel, new_mesh->mpoly, num_polys, mat_map);
+ bke::MutableAttributeAccessor attributes = new_mesh->attributes_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE);
+ assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map);
+ material_indices.finish();
}
return new_mesh;
@@ -774,10 +774,9 @@ Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh,
return existing_mesh;
}
-void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel,
- MPoly *mpoly,
- int totpoly,
- std::map<std::string, int> &r_mat_map)
+void AbcMeshReader::assign_facesets_to_material_indices(const ISampleSelector &sample_sel,
+ MutableSpan<int> material_indices,
+ std::map<std::string, int> &r_mat_map)
{
std::vector<std::string> face_sets;
m_schema.getFaceSetNames(face_sets);
@@ -810,13 +809,12 @@ void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel,
for (size_t l = 0; l < num_group_faces; l++) {
size_t pos = (*group_faces)[l];
- if (pos >= totpoly) {
+ if (pos >= material_indices.size()) {
std::cerr << "Faceset overflow on " << faceset.getName() << '\n';
break;
}
- MPoly &poly = mpoly[pos];
- poly.mat_nr = assigned_mat - 1;
+ material_indices[pos] = assigned_mat - 1;
}
}
}
@@ -824,25 +822,16 @@ void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel,
void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSelector &sample_sel)
{
std::map<std::string, int> mat_map;
- assign_facesets_to_mpoly(sample_sel, mesh->mpoly, mesh->totpoly, mat_map);
+ bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE);
+ assign_facesets_to_material_indices(sample_sel, material_indices.span, mat_map);
+ material_indices.finish();
utils::assign_materials(bmain, m_object, mat_map);
}
/* ************************************************************************** */
-BLI_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2)
-{
- for (int i = 0, e = totedge; i < e; i++) {
- MEdge &edge = edges[i];
-
- if (edge.v1 == v1 && edge.v2 == v2) {
- return &edge;
- }
- }
-
- return nullptr;
-}
-
static void read_subd_sample(const std::string &iobject_full_name,
ImportSettings *settings,
const ISubDSchema &schema,
@@ -902,7 +891,7 @@ static void read_vertex_creases(Mesh *mesh,
}
float *vertex_crease_data = (float *)CustomData_add_layer(
- &mesh->vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh->totvert);
+ &mesh->vdata, CD_CREASE, CD_SET_DEFAULT, nullptr, mesh->totvert);
const int totvert = mesh->totvert;
for (int i = 0, v = indices->size(); i < v; ++i) {
@@ -926,8 +915,13 @@ static void read_edge_creases(Mesh *mesh,
return;
}
- MEdge *edges = mesh->medge;
- int totedge = mesh->totedge;
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ EdgeHash *edge_hash = BLI_edgehash_new_ex(__func__, edges.size());
+
+ for (const int i : edges.index_range()) {
+ MEdge *edge = &edges[i];
+ BLI_edgehash_insert(edge_hash, edge->v1, edge->v2, edge);
+ }
for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) {
int v1 = (*indices)[i];
@@ -939,9 +933,9 @@ static void read_edge_creases(Mesh *mesh,
std::swap(v1, v2);
}
- MEdge *edge = find_edge(edges, totedge, v1, v2);
+ MEdge *edge = static_cast<MEdge *>(BLI_edgehash_lookup(edge_hash, v1, v2));
if (edge == nullptr) {
- edge = find_edge(edges, totedge, v2, v1);
+ edge = static_cast<MEdge *>(BLI_edgehash_lookup(edge_hash, v2, v1));
}
if (edge) {
@@ -949,6 +943,8 @@ static void read_edge_creases(Mesh *mesh,
}
}
+ BLI_edgehash_free(edge_hash, nullptr);
+
mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE;
}
@@ -999,7 +995,7 @@ void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelec
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, "", 0.0f, nullptr);
if (read_mesh != mesh) {
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_EVERYTHING, true);
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object);
}
ISubDSchema::Sample sample;
diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h
index f97525297b7..151f4d82226 100644
--- a/source/blender/io/alembic/intern/abc_reader_mesh.h
+++ b/source/blender/io/alembic/intern/abc_reader_mesh.h
@@ -5,6 +5,8 @@
* \ingroup balembic
*/
+#include "BLI_span.hh"
+
#include "abc_customdata.h"
#include "abc_reader_object.h"
@@ -38,10 +40,9 @@ class AbcMeshReader final : public AbcObjectReader {
Mesh *mesh,
const Alembic::AbcGeom::ISampleSelector &sample_sel);
- void assign_facesets_to_mpoly(const Alembic::Abc::ISampleSelector &sample_sel,
- MPoly *mpoly,
- int totpoly,
- std::map<std::string, int> &r_mat_map);
+ void assign_facesets_to_material_indices(const Alembic::Abc::ISampleSelector &sample_sel,
+ MutableSpan<int> material_indices,
+ std::map<std::string, int> &r_mat_map);
};
class AbcSubDReader final : public AbcObjectReader {
diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc
index a698eeca8f1..db056c0eef6 100644
--- a/source/blender/io/alembic/intern/abc_reader_object.cc
+++ b/source/blender/io/alembic/intern/abc_reader_object.cc
@@ -218,12 +218,12 @@ Alembic::AbcGeom::IXform AbcObjectReader::xform()
void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */,
const chrono_t time,
const float scale,
- bool &is_constant)
+ bool &r_is_constant)
{
IXform ixform = xform();
if (!ixform) {
unit_m4(r_mat);
- is_constant = true;
+ r_is_constant = true;
return;
}
@@ -254,7 +254,7 @@ void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */,
mul_m4_m4m4(r_mat, scale_mat, r_mat);
}
- is_constant = schema.isConstant();
+ r_is_constant = schema.isConstant();
}
void AbcObjectReader::addCacheModifier()
diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc
index ff189bc92dc..54ae71ad7a6 100644
--- a/source/blender/io/alembic/intern/abc_reader_points.cc
+++ b/source/blender/io/alembic/intern/abc_reader_points.cc
@@ -69,7 +69,7 @@ void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSel
Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, "", 0.0f, 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);
}
if (m_settings->validate_meshes) {
diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc
index 27df23b38c6..11c26fd2f72 100644
--- a/source/blender/io/alembic/intern/alembic_capi.cc
+++ b/source/blender/io/alembic/intern/alembic_capi.cc
@@ -601,9 +601,10 @@ static void import_endjob(void *user_data)
else {
Base *base;
LayerCollection *lc;
+ const Scene *scene = data->scene;
ViewLayer *view_layer = data->view_layer;
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
lc = BKE_layer_collection_get_active(view_layer);
@@ -616,6 +617,7 @@ static void import_endjob(void *user_data)
/* Sync the collection, and do view layer operations. */
BKE_layer_collection_resync_allow();
BKE_main_collection_sync(data->bmain);
+ BKE_view_layer_synced_ensure(scene, view_layer);
for (AbcObjectReader *reader : data->readers) {
Object *ob = reader->object();
base = BKE_view_layer_base_find(view_layer, ob);
@@ -672,13 +674,7 @@ static void import_freejob(void *user_data)
bool ABC_import(bContext *C,
const char *filepath,
- float scale,
- bool is_sequence,
- bool set_frame_range,
- int sequence_len,
- int offset,
- bool validate_meshes,
- bool always_add_cache_reader,
+ const AlembicImportParams *params,
bool as_background_job)
{
/* Using new here since MEM_* functions do not call constructor to properly initialize data. */
@@ -691,13 +687,13 @@ bool ABC_import(bContext *C,
job->import_ok = false;
BLI_strncpy(job->filename, filepath, 1024);
- job->settings.scale = scale;
- job->settings.is_sequence = is_sequence;
- job->settings.set_frame_range = set_frame_range;
- job->settings.sequence_len = sequence_len;
- job->settings.sequence_offset = offset;
- job->settings.validate_meshes = validate_meshes;
- job->settings.always_add_cache_reader = always_add_cache_reader;
+ job->settings.scale = params->global_scale;
+ job->settings.is_sequence = params->is_sequence;
+ job->settings.set_frame_range = params->set_frame_range;
+ job->settings.sequence_len = params->sequence_len;
+ job->settings.sequence_offset = params->sequence_offset;
+ job->settings.validate_meshes = params->validate_meshes;
+ job->settings.always_add_cache_reader = params->always_add_cache_reader;
job->error_code = ABC_NO_ERROR;
job->was_cancelled = false;
job->archive = nullptr;
diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp
index 923a392dbde..cc91c3eeac9 100644
--- a/source/blender/io/collada/AnimationImporter.cpp
+++ b/source/blender/io/collada/AnimationImporter.cpp
@@ -64,7 +64,7 @@ void AnimationImporter::add_bezt(FCurve *fcu,
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS);
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve)
@@ -132,7 +132,7 @@ void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve)
insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS);
}
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
fcurves.push_back(fcu);
unused_curves.push_back(fcu);
diff --git a/source/blender/io/collada/ArmatureExporter.cpp b/source/blender/io/collada/ArmatureExporter.cpp
index 87dd2fbd816..3cc98917116 100644
--- a/source/blender/io/collada/ArmatureExporter.cpp
+++ b/source/blender/io/collada/ArmatureExporter.cpp
@@ -76,7 +76,7 @@ bool ArmatureExporter::add_instance_controller(Object *ob)
ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
Mesh *me = (Mesh *)ob->data;
- if (!me->dvert) {
+ if (BKE_mesh_deform_verts(me) == nullptr) {
return false;
}
diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp
index fbb2ba499a5..fe90dc5d5fa 100644
--- a/source/blender/io/collada/BCAnimationCurve.cpp
+++ b/source/blender/io/collada/BCAnimationCurve.cpp
@@ -1,6 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2008 Blender Foundation. All rights reserved. */
+#include "RNA_path.h"
+
#include "BCAnimationCurve.h"
BCAnimationCurve::BCAnimationCurve()
@@ -96,7 +98,7 @@ void BCAnimationCurve::create_bezt(float frame, float output)
bez.f1 = bez.f2 = bez.f3 = SELECT;
bez.h1 = bez.h2 = HD_AUTO;
insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS);
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
}
BCAnimationCurve::~BCAnimationCurve()
diff --git a/source/blender/io/collada/BlenderContext.cpp b/source/blender/io/collada/BlenderContext.cpp
index 5f54f38a0ab..807488233ce 100644
--- a/source/blender/io/collada/BlenderContext.cpp
+++ b/source/blender/io/collada/BlenderContext.cpp
@@ -9,21 +9,25 @@
#include "BlenderContext.h"
#include "ExportSettings.h"
+#include "BKE_layer.h"
#include "BKE_scene.h"
-bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer)
+#include "BLI_listbase.h"
+
+bool bc_is_base_node(LinkNode *export_set, Object *ob, const Scene *scene, ViewLayer *view_layer)
{
- Object *root = bc_get_highest_exported_ancestor_or_self(export_set, ob, view_layer);
+ Object *root = bc_get_highest_exported_ancestor_or_self(export_set, ob, scene, view_layer);
return (root == ob);
}
Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set,
Object *ob,
+ const Scene *scene,
ViewLayer *view_layer)
{
Object *ancestor = ob;
while (ob->parent) {
- if (bc_is_in_Export_set(export_set, ob->parent, view_layer)) {
+ if (bc_is_in_Export_set(export_set, ob->parent, scene, view_layer)) {
ancestor = ob->parent;
}
ob = ob->parent;
@@ -31,10 +35,13 @@ Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set,
return ancestor;
}
-void bc_get_children(std::vector<Object *> &child_set, Object *ob, ViewLayer *view_layer)
+void bc_get_children(std::vector<Object *> &child_set,
+ Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer)
{
- Base *base;
- for (base = (Base *)view_layer->object_bases.first; base; base = base->next) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *cob = base->object;
if (cob->parent == ob) {
switch (ob->type) {
@@ -51,7 +58,10 @@ void bc_get_children(std::vector<Object *> &child_set, Object *ob, ViewLayer *vi
}
}
-bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer)
+bool bc_is_in_Export_set(LinkNode *export_set,
+ Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer)
{
bool to_export = (BLI_linklist_index(export_set, ob) != -1);
@@ -60,9 +70,9 @@ bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer
* export list, but it contains children to export. */
std::vector<Object *> children;
- bc_get_children(children, ob, view_layer);
+ bc_get_children(children, ob, scene, view_layer);
for (Object *child : children) {
- if (bc_is_in_Export_set(export_set, child, view_layer)) {
+ if (bc_is_in_Export_set(export_set, child, scene, view_layer)) {
to_export = true;
break;
}
diff --git a/source/blender/io/collada/BlenderContext.h b/source/blender/io/collada/BlenderContext.h
index 6fdb043b3dc..8a782b74e52 100644
--- a/source/blender/io/collada/BlenderContext.h
+++ b/source/blender/io/collada/BlenderContext.h
@@ -22,8 +22,11 @@ extern "C" {
static const BC_global_forward_axis BC_DEFAULT_FORWARD = BC_GLOBAL_FORWARD_Y;
static const BC_global_up_axis BC_DEFAULT_UP = BC_GLOBAL_UP_Z;
-bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer);
-bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer);
+bool bc_is_in_Export_set(LinkNode *export_set,
+ Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer);
+bool bc_is_base_node(LinkNode *export_set, Object *ob, const Scene *scene, ViewLayer *view_layer);
/**
* Returns the highest selected ancestor
* returns NULL if no ancestor is selected
@@ -32,6 +35,7 @@ bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer);
*/
Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set,
Object *ob,
+ const Scene *scene,
ViewLayer *view_layer);
int bc_is_marked(Object *ob);
void bc_remove_mark(Object *ob);
diff --git a/source/blender/io/collada/ControllerExporter.cpp b/source/blender/io/collada/ControllerExporter.cpp
index 38ad0e42d0f..6bf8d904a41 100644
--- a/source/blender/io/collada/ControllerExporter.cpp
+++ b/source/blender/io/collada/ControllerExporter.cpp
@@ -63,7 +63,7 @@ bool ControllerExporter::add_instance_controller(Object *ob)
ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
Mesh *me = (Mesh *)ob->data;
- if (!me->dvert) {
+ if (BKE_mesh_deform_verts(me) == nullptr) {
return false;
}
@@ -160,7 +160,7 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm)
bool use_instantiation = this->export_settings.get_use_object_instantiation();
Mesh *me;
- if (((Mesh *)ob->data)->dvert == nullptr) {
+ if (BKE_mesh_deform_verts((Mesh *)ob->data) == nullptr) {
return;
}
@@ -203,9 +203,10 @@ void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm)
}
}
+ const MDeformVert *dvert = BKE_mesh_deform_verts(me);
int oob_counter = 0;
for (i = 0; i < me->totvert; i++) {
- MDeformVert *vert = &me->dvert[i];
+ const MDeformVert *vert = &dvert[i];
std::map<int, float> jw;
/* We're normalizing the weights later */
diff --git a/source/blender/io/collada/DocumentImporter.cpp b/source/blender/io/collada/DocumentImporter.cpp
index 2ce97bc8b5d..00c69a5fb8a 100644
--- a/source/blender/io/collada/DocumentImporter.cpp
+++ b/source/blender/io/collada/DocumentImporter.cpp
@@ -90,8 +90,12 @@ DocumentImporter::DocumentImporter(bContext *C, const ImportSettings *import_set
CTX_data_scene(C),
view_layer,
import_settings),
- mesh_importer(
- &unit_converter, &armature_importer, CTX_data_main(C), CTX_data_scene(C), view_layer),
+ mesh_importer(&unit_converter,
+ import_settings->custom_normals,
+ &armature_importer,
+ CTX_data_main(C),
+ CTX_data_scene(C),
+ view_layer),
anim_importer(C, &unit_converter, &armature_importer, CTX_data_scene(C))
{
}
@@ -116,7 +120,7 @@ bool DocumentImporter::import()
loader.registerExtraDataCallbackHandler(ehandler);
/* deselect all to select new objects */
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(CTX_data_scene(mContext), view_layer);
std::string mFilename = std::string(this->import_settings->filepath);
const std::string encodedFilename = bc_url_encode(mFilename);
@@ -743,6 +747,7 @@ bool DocumentImporter::writeMaterial(const COLLADAFW::Material *cmat)
const std::string &str_mat_id = cmat->getName().empty() ? cmat->getOriginalId() :
cmat->getName();
Material *ma = BKE_material_add(bmain, (char *)str_mat_id.c_str());
+ id_us_min(&ma->id);
this->uid_effect_map[cmat->getInstantiatedEffect()] = ma;
this->uid_material_map[cmat->getUniqueId()] = ma;
diff --git a/source/blender/io/collada/EffectExporter.cpp b/source/blender/io/collada/EffectExporter.cpp
index 71a54e3a7c9..40ce20617fc 100644
--- a/source/blender/io/collada/EffectExporter.cpp
+++ b/source/blender/io/collada/EffectExporter.cpp
@@ -46,6 +46,7 @@ EffectsExporter::EffectsExporter(COLLADASW::StreamWriter *sw,
bool EffectsExporter::hasEffects(Scene *sce)
{
+ bool result = false;
FOREACH_SCENE_OBJECT_BEGIN (sce, ob) {
int a;
for (a = 0; a < ob->totcol; a++) {
@@ -56,11 +57,12 @@ bool EffectsExporter::hasEffects(Scene *sce)
continue;
}
- return true;
+ result = true;
+ break;
}
}
FOREACH_SCENE_OBJECT_END;
- return false;
+ return result;
}
void EffectsExporter::exportEffects(bContext *C, Scene *sce)
diff --git a/source/blender/io/collada/ExportSettings.h b/source/blender/io/collada/ExportSettings.h
index b3f192fdac6..e4c1b41fdde 100644
--- a/source/blender/io/collada/ExportSettings.h
+++ b/source/blender/io/collada/ExportSettings.h
@@ -81,7 +81,10 @@ typedef struct ExportSettings {
#ifdef __cplusplus
}
-void bc_get_children(std::vector<Object *> &child_set, Object *ob, ViewLayer *view_layer);
+void bc_get_children(std::vector<Object *> &child_set,
+ Object *ob,
+ const Scene *scene,
+ ViewLayer *view_layer);
class BCExportSettings {
@@ -271,7 +274,7 @@ class BCExportSettings {
bool is_export_root(Object *ob)
{
- return bc_is_base_node(get_export_set(), ob, get_view_layer());
+ return bc_is_base_node(get_export_set(), ob, get_scene(), get_view_layer());
}
};
diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp
index 7e2a24aeb41..e60900ccdb6 100644
--- a/source/blender/io/collada/GeometryExporter.cpp
+++ b/source/blender/io/collada/GeometryExporter.cpp
@@ -17,6 +17,7 @@
#include "BLI_utildefines.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
@@ -26,6 +27,8 @@
#include "collada_internal.h"
#include "collada_utils.h"
+using blender::Span;
+
void GeometryExporter::exportGeom()
{
Scene *sce = blender_context.get_scene();
@@ -116,11 +119,12 @@ void GeometryExporter::operator()(Object *ob)
if (this->export_settings.get_include_shapekeys()) {
Key *key = BKE_key_from_object(ob);
if (key) {
+ blender::MutableSpan<MVert> verts = me->verts_for_write();
KeyBlock *kb = (KeyBlock *)key->block.first;
/* skip the basis */
kb = kb->next;
for (; kb; kb = kb->next) {
- BKE_keyblock_convert_to_mesh(kb, me->mvert, me->totvert);
+ BKE_keyblock_convert_to_mesh(kb, verts.data(), me->totvert);
export_key_mesh(ob, me, kb);
}
}
@@ -196,8 +200,7 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb)
void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, std::string &geom_id)
{
-
- MEdge *medges = me->medge;
+ const Span<MEdge> edges = me->edges();
int totedges = me->totedge;
int edges_in_linelist = 0;
std::vector<unsigned int> edge_list;
@@ -206,7 +209,7 @@ void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, std::string &ge
/* Find all loose edges in Mesh
* and save vertex indices in edge_list */
for (index = 0; index < totedges; index++) {
- MEdge *edge = &medges[index];
+ const MEdge *edge = &edges[index];
if (edge->flag & ME_LOOSEEDGE) {
edges_in_linelist += 1;
@@ -284,16 +287,17 @@ static bool collect_vertex_counts_per_poly(Mesh *me,
int material_index,
std::vector<unsigned long> &vcount_list)
{
- MPoly *mpolys = me->mpoly;
- int totpolys = me->totpoly;
+ const Span<MPoly> polys = me->polys();
+ const blender::bke::AttributeAccessor attributes = me->attributes();
+ const blender::VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
bool is_triangulated = true;
- int i;
- /* Expecting that p->mat_nr is always 0 if the mesh has no materials assigned */
- for (i = 0; i < totpolys; i++) {
- MPoly *p = &mpolys[i];
- if (p->mat_nr == material_index) {
- int vertex_count = p->totloop;
+ /* Expecting that the material index is always 0 if the mesh has no materials assigned */
+ for (const int i : polys.index_range()) {
+ if (material_indices[i] == material_index) {
+ const MPoly &poly = polys[i];
+ const int vertex_count = poly.totloop;
vcount_list.push_back(vertex_count);
if (vertex_count != 3) {
is_triangulated = false;
@@ -318,10 +322,8 @@ void GeometryExporter::create_mesh_primitive_list(short material_index,
std::string &geom_id,
std::vector<BCPolygonNormalsIndices> &norind)
{
-
- MPoly *mpolys = me->mpoly;
- MLoop *mloops = me->mloop;
- int totpolys = me->totpoly;
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
std::vector<unsigned long> vcount_list;
@@ -397,14 +399,18 @@ void GeometryExporter::create_mesh_primitive_list(short material_index,
/* performs the actual writing */
prepareToAppendValues(is_triangulated, *primitive_list, vcount_list);
+ const blender::bke::AttributeAccessor attributes = me->attributes();
+ const blender::VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+
/* <p> */
int texindex = 0;
- for (int i = 0; i < totpolys; i++) {
- MPoly *p = &mpolys[i];
+ for (const int i : polys.index_range()) {
+ const MPoly *p = &polys[i];
int loop_count = p->totloop;
- if (p->mat_nr == material_index) {
- MLoop *l = &mloops[p->loopstart];
+ if (material_indices[i] == material_index) {
+ const MLoop *l = &loops[p->loopstart];
BCPolygonNormalsIndices normal_indices = norind[i];
for (int j = 0; j < loop_count; j++) {
@@ -428,18 +434,13 @@ void GeometryExporter::create_mesh_primitive_list(short material_index,
void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me)
{
-#if 0
- int totverts = dm->getNumVerts(dm);
- MVert *verts = dm->getVertArray(dm);
-#endif
- int totverts = me->totvert;
- MVert *verts = me->mvert;
+ const Span<MVert> verts = me->verts();
COLLADASW::FloatSourceF source(mSW);
source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION));
source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION) +
ARRAY_ID_SUFFIX);
- source.setAccessorCount(totverts);
+ source.setAccessorCount(verts.size());
source.setAccessorStride(3);
COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
@@ -450,8 +451,7 @@ void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me)
* count = ""> */
source.prepareToAppendValues();
/* appends data to <float_array> */
- int i = 0;
- for (i = 0; i < totverts; i++) {
+ for (const int i : verts.index_range()) {
Vector co;
if (export_settings.get_apply_global_orientation()) {
bc_add_global_transform(co, verts[i].co, export_settings.get_global_transform());
@@ -500,11 +500,11 @@ void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me)
source.prepareToAppendValues();
- MPoly *mpoly;
- int i;
- for (i = 0, mpoly = me->mpoly; i < me->totpoly; i++, mpoly++) {
- const MLoopCol *mlc = mloopcol + mpoly->loopstart;
- for (int j = 0; j < mpoly->totloop; j++, mlc++) {
+ const Span<MPoly> polys = me->polys();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ const MLoopCol *mlc = mloopcol + poly.loopstart;
+ for (int j = 0; j < poly.totloop; j++, mlc++) {
source.appendValues(mlc->r / 255.0f, mlc->g / 255.0f, mlc->b / 255.0f, mlc->a / 255.0f);
}
}
@@ -529,10 +529,8 @@ std::string GeometryExporter::makeTexcoordSourceId(std::string &geom_id,
void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me)
{
-
- int totpoly = me->totpoly;
int totuv = me->totloop;
- MPoly *mpolys = me->mpoly;
+ const Span<MPoly> polys = me->polys();
int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV);
@@ -558,8 +556,8 @@ void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me)
source.prepareToAppendValues();
- for (int index = 0; index < totpoly; index++) {
- MPoly *mpoly = mpolys + index;
+ for (const int i : polys.index_range()) {
+ const MPoly *mpoly = &polys[i];
MLoopUV *mloop = mloops + mpoly->loopstart;
for (int j = 0; j < mpoly->totloop; j++) {
source.appendValues(mloop[j].uv[0], mloop[j].uv[1]);
@@ -617,9 +615,10 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals,
std::map<Normal, unsigned int> shared_normal_indices;
int last_normal_index = -1;
- MVert *verts = me->mvert;
+ const Span<MVert> verts = me->verts();
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me);
- MLoop *mloops = me->mloop;
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
const float(*lnors)[3] = nullptr;
bool use_custom_normals = false;
@@ -629,15 +628,15 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals,
use_custom_normals = true;
}
- for (int poly_index = 0; poly_index < me->totpoly; poly_index++) {
- MPoly *mpoly = &me->mpoly[poly_index];
+ for (const int poly_index : polys.index_range()) {
+ const MPoly *mpoly = &polys[poly_index];
bool use_vertex_normals = use_custom_normals || mpoly->flag & ME_SMOOTH;
if (!use_vertex_normals) {
/* For flat faces use face normal as vertex normal: */
float vector[3];
- BKE_mesh_calc_poly_normal(mpoly, mloops + mpoly->loopstart, verts, vector);
+ BKE_mesh_calc_poly_normal(mpoly, &loops[mpoly->loopstart], verts.data(), vector);
Normal n = {vector[0], vector[1], vector[2]};
normals.push_back(n);
@@ -654,7 +653,7 @@ void GeometryExporter::create_normals(std::vector<Normal> &normals,
normalize_v3_v3(normalized, lnors[loop_idx]);
}
else {
- copy_v3_v3(normalized, vert_normals[mloops[loop_index].v]);
+ copy_v3_v3(normalized, vert_normals[loops[loop_index].v]);
normalize_v3(normalized);
}
Normal n = {normalized[0], normalized[1], normalized[2]};
diff --git a/source/blender/io/collada/ImportSettings.h b/source/blender/io/collada/ImportSettings.h
index c92cf580112..2772314900c 100644
--- a/source/blender/io/collada/ImportSettings.h
+++ b/source/blender/io/collada/ImportSettings.h
@@ -8,6 +8,7 @@
typedef struct ImportSettings {
bool import_units;
+ bool custom_normals;
bool find_chains;
bool auto_connect;
bool fix_orientation;
diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp
index b5d89d8d1cf..997da31b939 100644
--- a/source/blender/io/collada/Materials.cpp
+++ b/source/blender/io/collada/Materials.cpp
@@ -86,7 +86,7 @@ bNodeTree *MaterialNode::prepare_material_nodetree()
return nullptr;
}
- material->nodetree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree");
+ ntreeAddTreeEmbedded(nullptr, &material->id, "Shader Nodetree", "ShaderNodeTree");
material->use_nodes = true;
ntree = material->nodetree;
return ntree;
diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp
index fa0348fbcf2..719ac752413 100644
--- a/source/blender/io/collada/MeshImporter.cpp
+++ b/source/blender/io/collada/MeshImporter.cpp
@@ -33,6 +33,8 @@
#include "MeshImporter.h"
#include "collada_utils.h"
+using blender::MutableSpan;
+
/* get node name, or fall back to original id if not present (name is optional) */
template<class T> static std::string bc_get_dae_name(T *node)
{
@@ -189,9 +191,14 @@ void VCOLDataWrapper::get_vcol(int v_index, MLoopCol *mloopcol)
}
}
-MeshImporter::MeshImporter(
- UnitConverter *unitconv, ArmatureImporter *arm, Main *bmain, Scene *sce, ViewLayer *view_layer)
+MeshImporter::MeshImporter(UnitConverter *unitconv,
+ bool use_custom_normals,
+ ArmatureImporter *arm,
+ Main *bmain,
+ Scene *sce,
+ ViewLayer *view_layer)
: unitconverter(unitconv),
+ use_custom_normals(use_custom_normals),
m_bmain(bmain),
scene(sce),
view_layer(view_layer),
@@ -341,13 +348,10 @@ void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *me)
}
me->totvert = pos.getFloatValues()->getCount() / stride;
- me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, me->totvert);
-
- MVert *mvert;
- int i;
-
- for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
- get_vector(mvert->co, pos, i, stride);
+ CustomData_add_layer(&me->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, me->totvert);
+ MutableSpan<MVert> verts = me->verts_for_write();
+ for (const int i : verts.index_range()) {
+ get_vector(verts[i].co, pos, i, stride);
}
}
@@ -448,10 +452,8 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me)
if (total_poly_count > 0) {
me->totpoly = total_poly_count;
me->totloop = total_loop_count;
- me->mpoly = (MPoly *)CustomData_add_layer(
- &me->pdata, CD_MPOLY, CD_CALLOC, nullptr, me->totpoly);
- me->mloop = (MLoop *)CustomData_add_layer(
- &me->ldata, CD_MLOOP, CD_CALLOC, nullptr, me->totloop);
+ CustomData_add_layer(&me->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, me->totpoly);
+ CustomData_add_layer(&me->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, me->totloop);
unsigned int totuvset = collada_mesh->getUVCoords().getInputInfosArray().getCount();
for (int i = 0; i < totuvset; i++) {
@@ -468,10 +470,10 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me)
COLLADAFW::String &uvname = info->mName;
/* Allocate space for UV_data */
CustomData_add_layer_named(
- &me->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, me->totloop, uvname.c_str());
+ &me->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, me->totloop, uvname.c_str());
}
/* activate the first uv map */
- me->mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, 0);
+ CustomData_set_layer_active(&me->ldata, CD_MLOOPUV, 0);
}
int totcolset = collada_mesh->getColors().getInputInfosArray().getCount();
@@ -481,9 +483,9 @@ void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me)
collada_mesh->getColors().getInputInfosArray()[i];
COLLADAFW::String colname = extract_vcolname(info->mName);
CustomData_add_layer_named(
- &me->ldata, CD_PROP_BYTE_COLOR, CD_DEFAULT, nullptr, me->totloop, colname.c_str());
+ &me->ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, nullptr, me->totloop, colname.c_str());
}
- me->mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_PROP_BYTE_COLOR, 0);
+ CustomData_set_layer_active(&me->ldata, CD_PROP_BYTE_COLOR, 0);
}
}
}
@@ -546,19 +548,20 @@ void MeshImporter::mesh_add_edges(Mesh *mesh, int len)
totedge = mesh->totedge + len;
/* Update custom-data. */
- CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_DEFAULT, totedge);
+ CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_SET_DEFAULT, totedge);
CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge);
if (!CustomData_has_layer(&edata, CD_MEDGE)) {
- CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, nullptr, totedge);
+ CustomData_add_layer(&edata, CD_MEDGE, CD_SET_DEFAULT, nullptr, totedge);
}
CustomData_free(&mesh->edata, mesh->totedge);
mesh->edata = edata;
- BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */
+
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
/* set default flags */
- medge = &mesh->medge[mesh->totedge];
+ medge = &edges[mesh->totedge];
for (int i = 0; i < len; i++, medge++) {
medge->flag = ME_EDGEDRAW | ME_EDGERENDER | SELECT;
}
@@ -575,7 +578,8 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
/* unsigned int total_edge_count = loose_edge_count + face_edge_count; */ /* UNUSED */
mesh_add_edges(me, loose_edge_count);
- MEdge *med = me->medge + face_edge_count;
+ MutableSpan<MEdge> edges = me->edges_for_write();
+ MEdge *med = edges.data() + face_edge_count;
COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives();
@@ -588,7 +592,6 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
unsigned int *indices = mp->getPositionIndices().getData();
for (int j = 0; j < edge_count; j++, med++) {
- med->bweight = 0;
med->crease = 0;
med->flag |= ME_LOOSEEDGE;
med->v1 = indices[2 * j];
@@ -599,7 +602,9 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me)
}
}
-void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
+void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh,
+ Mesh *me,
+ blender::Vector<blender::float3> &loop_normals)
{
unsigned int i;
@@ -608,12 +613,17 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
UVDataWrapper uvs(collada_mesh->getUVCoords());
VCOLDataWrapper vcol(collada_mesh->getColors());
- MPoly *mpoly = me->mpoly;
- MLoop *mloop = me->mloop;
+ MutableSpan<MPoly> polys = me->polys_for_write();
+ MutableSpan<MLoop> loops = me->loops_for_write();
+ MPoly *mpoly = polys.data();
+ MLoop *mloop = loops.data();
int loop_index = 0;
MaterialIdPrimitiveArrayMap mat_prim_map;
+ int *material_indices = (int *)CustomData_add_layer_named(
+ &me->pdata, CD_PROP_INT32, CD_SET_DEFAULT, nullptr, me->totpoly, "material_index");
+
COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives();
COLLADAFW::MeshVertexData &nor = collada_mesh->getNormals();
@@ -632,12 +642,13 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
int collada_meshtype = mp->getPrimitiveType();
/* since we cannot set mpoly->mat_nr here, we store a portion of me->mpoly in Primitive */
- Primitive prim = {mpoly, 0};
+ Primitive prim = {mpoly, material_indices, 0};
/* If MeshPrimitive is TRIANGLE_FANS we split it into triangles
* The first triangle-fan vertex will be the first vertex in every triangle
* XXX The proper function of TRIANGLE_FANS is not tested!!!
- * XXX In particular the handling of the normal_indices looks very wrong to me */
+ * XXX In particular the handling of the normal_indices is very wrong */
+ /* TODO: UV, vertex color and custom normal support */
if (collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) {
unsigned int grouped_vertex_count = mp->getGroupedVertexElementsCount();
for (unsigned int group_index = 0; group_index < grouped_vertex_count; group_index++) {
@@ -662,6 +673,9 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
}
mpoly++;
+ if (material_indices) {
+ material_indices++;
+ }
mloop += 3;
loop_index += 3;
prim.totpoly++;
@@ -721,9 +735,22 @@ void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me)
}
if (mp_has_normals) {
+ /* If it turns out that we have complete custom normals for each MPoly
+ * and we want to use custom normals, this will be overridden. */
if (!is_flat_face(normal_indices, nor, vcount)) {
mpoly->flag |= ME_SMOOTH;
}
+
+ if (use_custom_normals) {
+ /* Store the custom normals for later application. */
+ float vert_normal[3];
+ unsigned int *cur_normal = normal_indices;
+ for (int k = 0; k < vcount; k++, cur_normal++) {
+ get_vector(vert_normal, nor, *cur_normal, 3);
+ normalize_v3(vert_normal);
+ loop_normals.append(vert_normal);
+ }
+ }
}
if (mp->hasColorIndices()) {
@@ -868,6 +895,16 @@ std::string *MeshImporter::get_geometry_name(const std::string &mesh_name)
return nullptr;
}
+static bool bc_has_out_of_bound_indices(Mesh *me)
+{
+ for (const MLoop &loop : me->loops()) {
+ if (loop.v >= me->totvert) {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* this function checks if both objects have the same
* materials assigned to Object (in the same order)
@@ -897,11 +934,9 @@ static bool bc_has_same_material_configuration(Object *ob1, Object *ob2)
}
/**
- *
* Caution here: This code assumes that all materials are assigned to Object
* and no material is assigned to Data.
* That is true right after the objects have been imported.
- *
*/
static void bc_copy_materials_to_data(Object *ob, Mesh *me)
{
@@ -912,9 +947,7 @@ static void bc_copy_materials_to_data(Object *ob, Mesh *me)
}
/**
- *
- * Remove all references to materials from the object
- *
+ * Remove all references to materials from the object.
*/
static void bc_remove_materials_from_object(Object *ob, Mesh *me)
{
@@ -1010,10 +1043,9 @@ void MeshImporter::assign_material_to_geom(
for (it = prims.begin(); it != prims.end(); it++) {
Primitive &prim = *it;
- MPoly *mpoly = prim.mpoly;
- for (int i = 0; i < prim.totpoly; i++, mpoly++) {
- mpoly->mat_nr = mat_index;
+ for (int i = 0; i < prim.totpoly; i++) {
+ prim.material_indices[i] = mat_index;
}
}
}
@@ -1119,8 +1151,37 @@ bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom)
this->mesh_geom_map[std::string(me->id.name)] = str_geom_id;
read_vertices(mesh, me);
- read_polys(mesh, me);
+
+ blender::Vector<blender::float3> loop_normals;
+ read_polys(mesh, me, loop_normals);
+
BKE_mesh_calc_edges(me, false, false);
+
+ /* We must apply custom normals after edges have been calculated, because
+ * BKE_mesh_set_custom_normals()'s internals expect me->medge to be populated
+ * and for the MLoops to have correct edge indices. */
+ if (use_custom_normals && !loop_normals.is_empty()) {
+ /* BKE_mesh_set_custom_normals()'s internals also expect that each MLoop
+ * has a valid vertex index, which may not be the case due to the existing
+ * logic in read_polys(). This check isn't necessary in the no-custom-normals
+ * case because the invalid MLoops get stripped in a later step. */
+ if (bc_has_out_of_bound_indices(me)) {
+ fprintf(stderr, "Can't apply custom normals, encountered invalid loop vert indices!\n");
+ }
+ /* There may be a mismatch in lengths if one or more of the MeshPrimitives in
+ * the Geometry had missing or otherwise invalid normals. */
+ else if (me->totloop != loop_normals.size()) {
+ fprintf(stderr,
+ "Can't apply custom normals, me->totloop != loop_normals.size() (%d != %d)\n",
+ me->totloop,
+ (int)loop_normals.size());
+ }
+ else {
+ BKE_mesh_set_custom_normals(me, reinterpret_cast<float(*)[3]>(loop_normals.data()));
+ me->flag |= ME_AUTOSMOOTH;
+ }
+ }
+
/* read_lines() must be called after the face edges have been generated.
* Otherwise the loose edges will be silently deleted again. */
read_lines(mesh, me);
diff --git a/source/blender/io/collada/MeshImporter.h b/source/blender/io/collada/MeshImporter.h
index 416b5728b66..a59b24d4f24 100644
--- a/source/blender/io/collada/MeshImporter.h
+++ b/source/blender/io/collada/MeshImporter.h
@@ -24,6 +24,7 @@
#include "collada_utils.h"
#include "BLI_edgehash.h"
+#include "BLI_math_vec_types.hh"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
@@ -63,6 +64,7 @@ class VCOLDataWrapper {
class MeshImporter : public MeshImporterBase {
private:
UnitConverter *unitconverter;
+ bool use_custom_normals;
Main *m_bmain;
Scene *scene;
@@ -80,6 +82,7 @@ class MeshImporter : public MeshImporterBase {
* (<triangles>, <polylist>, etc.) */
struct Primitive {
MPoly *mpoly;
+ int *material_indices;
unsigned int totpoly;
};
typedef std::map<COLLADAFW::MaterialId, std::vector<Primitive>> MaterialIdPrimitiveArrayMap;
@@ -155,7 +158,7 @@ class MeshImporter : public MeshImporterBase {
*
* TODO: import uv set names.
*/
- void read_polys(COLLADAFW::Mesh *mesh, Mesh *me);
+ void read_polys(COLLADAFW::Mesh *mesh, Mesh *me, blender::Vector<blender::float3> &loop_normals);
/**
* Read all loose edges.
* IMPORTANT: This function assumes that all edges from existing
@@ -178,6 +181,7 @@ class MeshImporter : public MeshImporterBase {
public:
MeshImporter(UnitConverter *unitconv,
+ bool use_custom_normals,
ArmatureImporter *arm,
Main *bmain,
Scene *sce,
@@ -203,7 +207,6 @@ class MeshImporter : public MeshImporterBase {
* if the check is positive:
* Add the materials of the first user to the geometry
* adjust all other users accordingly.
- *
*/
void optimize_material_assignements();
diff --git a/source/blender/io/collada/SceneExporter.cpp b/source/blender/io/collada/SceneExporter.cpp
index 1b1da110573..b98ff27c89e 100644
--- a/source/blender/io/collada/SceneExporter.cpp
+++ b/source/blender/io/collada/SceneExporter.cpp
@@ -82,11 +82,13 @@ void SceneExporter::writeNodeList(std::vector<Object *> &child_objects, Object *
void SceneExporter::writeNode(Object *ob)
{
+ const Scene *scene = blender_context.get_scene();
ViewLayer *view_layer = blender_context.get_view_layer();
std::vector<Object *> child_objects;
- bc_get_children(child_objects, ob, view_layer);
- bool can_export = bc_is_in_Export_set(this->export_settings.get_export_set(), ob, view_layer);
+ bc_get_children(child_objects, ob, scene, view_layer);
+ bool can_export = bc_is_in_Export_set(
+ this->export_settings.get_export_set(), ob, scene, view_layer);
/* Add associated armature first if available */
bool armature_exported = false;
@@ -94,7 +96,7 @@ void SceneExporter::writeNode(Object *ob)
if (ob_arm != nullptr) {
armature_exported = bc_is_in_Export_set(
- this->export_settings.get_export_set(), ob_arm, view_layer);
+ this->export_settings.get_export_set(), ob_arm, scene, view_layer);
if (armature_exported && bc_is_marked(ob_arm)) {
writeNode(ob_arm);
bc_remove_mark(ob_arm);
diff --git a/source/blender/io/collada/collada.cpp b/source/blender/io/collada/collada.cpp
index d559c0b4962..c33363ef205 100644
--- a/source/blender/io/collada/collada.cpp
+++ b/source/blender/io/collada/collada.cpp
@@ -29,6 +29,7 @@ static void print_import_header(ImportSettings &import_settings)
fprintf(stderr, "+-- Collada Import parameters------\n");
fprintf(stderr, "| input file : %s\n", import_settings.filepath);
fprintf(stderr, "| use units : %s\n", (import_settings.import_units) ? "yes" : "no");
+ fprintf(stderr, "| custom normals : %s\n", (import_settings.custom_normals) ? "yes" : "no");
fprintf(stderr, "| autoconnect : %s\n", (import_settings.auto_connect) ? "yes" : "no");
fprintf(stderr, "+-- Armature Import parameters ----\n");
fprintf(stderr, "| find bone chains: %s\n", (import_settings.find_chains) ? "yes" : "no");
@@ -57,6 +58,7 @@ int collada_import(bContext *C, ImportSettings *import_settings)
int collada_export(bContext *C, ExportSettings *export_settings)
{
BlenderContext blender_context(C);
+ const Scene *scene = blender_context.get_scene();
ViewLayer *view_layer = blender_context.get_view_layer();
int includeFilter = OB_REL_NONE;
@@ -72,7 +74,7 @@ int collada_export(bContext *C, ExportSettings *export_settings)
*/
eObjectSet objectSet = (export_settings->selected) ? OB_SET_SELECTED : OB_SET_ALL;
export_settings->export_set = BKE_object_relational_superset(
- view_layer, objectSet, (eObRelationTypes)includeFilter);
+ scene, view_layer, objectSet, (eObRelationTypes)includeFilter);
int export_count = BLI_linklist_count(export_settings->export_set);
diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp
index 75842734b08..03bc14c6d13 100644
--- a/source/blender/io/collada/collada_utils.cpp
+++ b/source/blender/io/collada/collada_utils.cpp
@@ -197,6 +197,7 @@ Object *bc_add_object(Main *bmain, Scene *scene, ViewLayer *view_layer, int type
LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer);
BKE_collection_object_add(bmain, layer_collection->collection, ob);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
/* TODO: is setting active needed? */
BKE_view_layer_base_select_and_set_active(view_layer, base);
@@ -1108,7 +1109,7 @@ static std::string bc_get_uvlayer_name(Mesh *me, int layer)
static bNodeTree *prepare_material_nodetree(Material *ma)
{
if (ma->nodetree == nullptr) {
- ma->nodetree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree");
+ ntreeAddTreeEmbedded(nullptr, &ma->id, "Shader Nodetree", "ShaderNodeTree");
ma->use_nodes = true;
}
return ma->nodetree;
diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt
index a6818c0bf5d..ee5c6a0a47f 100644
--- a/source/blender/io/common/CMakeLists.txt
+++ b/source/blender/io/common/CMakeLists.txt
@@ -19,15 +19,15 @@ set(SRC
intern/dupli_parent_finder.cc
intern/dupli_persistent_id.cc
intern/object_identifier.cc
- intern/path_util.cc
intern/orientation.c
+ intern/path_util.cc
IO_abstract_hierarchy_iterator.h
IO_dupli_persistent_id.hh
+ IO_orientation.h
IO_path_util.hh
IO_path_util_types.h
IO_types.h
- IO_orientation.h
intern/dupli_parent_finder.hh
)
diff --git a/source/blender/io/common/IO_abstract_hierarchy_iterator.h b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
index a67cfe6a9d6..966eb640264 100644
--- a/source/blender/io/common/IO_abstract_hierarchy_iterator.h
+++ b/source/blender/io/common/IO_abstract_hierarchy_iterator.h
@@ -228,7 +228,7 @@ class AbstractHierarchyIterator {
* writer is created it will also write the current iteration, to ensure the hierarchy is
* complete. The `export_subset` option is only in effect when the writer already existed from a
* previous iteration. */
- void set_export_subset(ExportSubset export_subset_);
+ void set_export_subset(ExportSubset export_subset);
/* Convert the given name to something that is valid for the exported file format.
* This base implementation is a no-op; override in a concrete subclass. */
@@ -267,7 +267,7 @@ class AbstractHierarchyIterator {
/* These three functions create writers and call their write() method. */
void make_writers(const HierarchyContext *parent_context);
void make_writer_object_data(const HierarchyContext *context);
- void make_writers_particle_systems(const HierarchyContext *context);
+ void make_writers_particle_systems(const HierarchyContext *transform_context);
/* Return the appropriate HierarchyContext for the data of the object represented by
* object_context. */
@@ -332,7 +332,7 @@ class AbstractHierarchyIterator {
virtual void release_writer(AbstractHierarchyWriter *writer) = 0;
AbstractHierarchyWriter *get_writer(const std::string &export_path) const;
- ExportChildren &graph_children(const HierarchyContext *parent_context);
+ ExportChildren &graph_children(const HierarchyContext *context);
};
} // namespace blender::io
diff --git a/source/blender/io/gpencil/gpencil_io.h b/source/blender/io/gpencil/gpencil_io.h
index 215891e3e48..eb811fa2de8 100644
--- a/source/blender/io/gpencil/gpencil_io.h
+++ b/source/blender/io/gpencil/gpencil_io.h
@@ -35,6 +35,8 @@ typedef struct GpencilIOParams {
/** Stroke sampling factor. */
float stroke_sample;
int32_t resolution;
+ /** Filename to be used in new objects. */
+ char filename[128];
} GpencilIOParams;
/* GpencilIOParams->flag. */
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.cc b/source/blender/io/gpencil/intern/gpencil_io_base.cc
index 05f1158c57d..bcf9a36c200 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.cc
@@ -20,6 +20,7 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_geom.h"
+#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_scene.h"
@@ -91,8 +92,7 @@ void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *ipara
/* Camera rectangle. */
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;
+ BKE_render_resolution(&scene->r, false, &render_x_, &render_y_);
ED_view3d_calc_camera_border(CTX_data_scene(params_.C),
depsgraph_,
@@ -128,13 +128,15 @@ void GpencilIO::prepare_camera_params(Scene *scene, const GpencilIOParams *ipara
void GpencilIO::create_object_list()
{
+ Scene *scene = CTX_data_scene(params_.C);
ViewLayer *view_layer = CTX_data_view_layer(params_.C);
float3 camera_z_axis;
copy_v3_v3(camera_z_axis, rv3d_->viewinv[2]);
ob_list_.clear();
- LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) {
Object *object = base->object;
if (object->type != OB_GPENCIL) {
@@ -258,7 +260,7 @@ float GpencilIO::stroke_point_radius_get(bGPDlayer *gpl, bGPDstroke *gps)
/* Radius. */
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
- rv3d_, gpd_, gpl, gps, 3, diff_mat_.values);
+ rv3d_->viewmat, gpd_, gpl, gps, 3, diff_mat_.values, 0.0f);
pt = &gps_perimeter->points[0];
const float2 screen_ex = gpencil_3D_point_to_2D(&pt->x);
diff --git a/source/blender/io/gpencil/intern/gpencil_io_base.hh b/source/blender/io/gpencil/intern/gpencil_io_base.hh
index a89b723ed6c..f712ed839d9 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_base.hh
+++ b/source/blender/io/gpencil/intern/gpencil_io_base.hh
@@ -58,8 +58,8 @@ class GpencilIO {
struct Scene *scene_;
struct RegionView3D *rv3d_;
- int16_t winx_, winy_;
- int16_t render_x_, render_y_;
+ int winx_, winy_;
+ int render_x_, render_y_;
float camera_ratio_;
rctf camera_rect_;
@@ -70,7 +70,7 @@ class GpencilIO {
float stroke_color_[4], fill_color_[4];
/* Geometry functions. */
- /** Convert to screenspace. */
+ /** Convert to screen-space. */
bool gpencil_3D_point_to_screen_space(const float3 co, float2 &r_co);
/** Convert to render space. */
float2 gpencil_3D_point_to_render_space(const float3 co);
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 700d91791a8..463032ebb9d 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_pdf.cc
@@ -192,7 +192,7 @@ void GpencilExporterPDF::export_gpencil_layers()
}
else {
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
- rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values);
+ rv3d_->viewmat, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f);
/* Sample stroke. */
if (params_.stroke_sample > 0.0f) {
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 2601ad05ea7..58f12e9b8b1 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_export_svg.cc
@@ -217,7 +217,7 @@ void GpencilExporterSVG::export_gpencil_layers()
}
else {
bGPDstroke *gps_perimeter = BKE_gpencil_stroke_perimeter_from_view(
- rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values);
+ rv3d_->viewmat, gpd_, gpl, gps_duplicate, 3, diff_mat_.values, 0.0f);
/* Sample stroke. */
if (params_.stroke_sample > 0.0f) {
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 9b00fbaa027..6d4439243fd 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc
@@ -14,6 +14,7 @@
#include "BKE_material.h"
#include "ED_gpencil.h"
+#include "ED_object.h"
#include "gpencil_io_import_base.hh"
@@ -27,10 +28,22 @@ GpencilImporter::GpencilImporter(const GpencilIOParams *iparams) : GpencilIO(ipa
Object *GpencilImporter::create_object()
{
- const float *cur = scene_->cursor.location;
+ const float *cur_loc = scene_->cursor.location;
+ const float rot[3] = {0.0f};
ushort local_view_bits = (params_.v3d && params_.v3d->localvd) ? params_.v3d->local_view_uuid :
(ushort)0;
- Object *ob_gpencil = ED_gpencil_add_object(params_.C, cur, local_view_bits);
+
+ Object *ob_gpencil = ED_object_add_type(params_.C,
+ OB_GPENCIL,
+ (params_.filename[0] != '\0') ? params_.filename :
+ nullptr,
+ cur_loc,
+ rot,
+ false,
+ local_view_bits);
+
+ /* Set object defaults. */
+ ED_gpencil_add_defaults(params_.C, ob_gpencil);
return ob_gpencil;
}
diff --git a/source/blender/io/stl/CMakeLists.txt b/source/blender/io/stl/CMakeLists.txt
index e0c48bbbf7e..3a21da5c579 100644
--- a/source/blender/io/stl/CMakeLists.txt
+++ b/source/blender/io/stl/CMakeLists.txt
@@ -24,16 +24,16 @@ set(INC_SYS
set(SRC
IO_stl.cc
- importer/stl_import_mesh.cc
+ importer/stl_import.cc
importer/stl_import_ascii_reader.cc
importer/stl_import_binary_reader.cc
- importer/stl_import.cc
+ importer/stl_import_mesh.cc
IO_stl.h
- importer/stl_import_mesh.hh
+ importer/stl_import.hh
importer/stl_import_ascii_reader.hh
importer/stl_import_binary_reader.hh
- importer/stl_import.hh
+ importer/stl_import_mesh.hh
)
set(LIB
diff --git a/source/blender/io/stl/importer/stl_import.cc b/source/blender/io/stl/importer/stl_import.cc
index 097d14b038c..e5fde6658ab 100644
--- a/source/blender/io/stl/importer/stl_import.cc
+++ b/source/blender/io/stl/importer/stl_import.cc
@@ -99,11 +99,12 @@ void importer_main(Main *bmain,
BKE_mesh_validate(mesh, verbose_validate, false);
}
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name);
BKE_mesh_assign_object(bmain, obj, mesh);
BKE_collection_object_add(bmain, lc->collection, obj);
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, obj);
BKE_view_layer_base_select_and_set_active(view_layer, base);
diff --git a/source/blender/io/stl/importer/stl_import_mesh.cc b/source/blender/io/stl/importer/stl_import_mesh.cc
index b9ed441f0d9..de993cd2f27 100644
--- a/source/blender/io/stl/importer/stl_import_mesh.cc
+++ b/source/blender/io/stl/importer/stl_import_mesh.cc
@@ -76,27 +76,26 @@ Mesh *STLMeshHelper::to_mesh(Main *bmain, char *mesh_name)
id_us_min(&mesh->id);
mesh->totvert = verts_.size();
- mesh->mvert = static_cast<MVert *>(
- CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, nullptr, mesh->totvert));
+ CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_SET_DEFAULT, nullptr, mesh->totvert);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
for (int i = 0; i < mesh->totvert; i++) {
- copy_v3_v3(mesh->mvert[i].co, verts_[i]);
+ copy_v3_v3(verts[i].co, verts_[i]);
}
mesh->totpoly = tris_.size();
mesh->totloop = tris_.size() * 3;
- mesh->mpoly = static_cast<MPoly *>(
- CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, nullptr, mesh->totpoly));
- mesh->mloop = static_cast<MLoop *>(
- CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, nullptr, mesh->totloop));
-
+ CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_SET_DEFAULT, nullptr, mesh->totpoly);
+ CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_SET_DEFAULT, nullptr, mesh->totloop);
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
threading::parallel_for(tris_.index_range(), 2048, [&](IndexRange tris_range) {
for (const int i : tris_range) {
- mesh->mpoly[i].loopstart = 3 * i;
- mesh->mpoly[i].totloop = 3;
+ polys[i].loopstart = 3 * i;
+ polys[i].totloop = 3;
- mesh->mloop[3 * i].v = tris_[i].v1;
- mesh->mloop[3 * i + 1].v = tris_[i].v2;
- mesh->mloop[3 * i + 2].v = tris_[i].v3;
+ loops[3 * i].v = tris_[i].v1;
+ loops[3 * i + 1].v = tris_[i].v2;
+ loops[3 * i + 2].v = tris_[i].v3;
}
});
diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc
index 13ae6f4d4c0..54ad7ef5410 100644
--- a/source/blender/io/usd/intern/usd_capi_import.cc
+++ b/source/blender/io/usd/intern/usd_capi_import.cc
@@ -218,6 +218,7 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo
data->scene->r.efra = stage->GetEndTimeCode();
}
+ *data->do_update = true;
*data->progress = 0.15f;
USDStageReader *archive = new USDStageReader(stage, data->params, data->settings);
@@ -226,13 +227,32 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo
archive->collect_readers(data->bmain);
+ *data->do_update = true;
*data->progress = 0.2f;
const float size = static_cast<float>(archive->readers().size());
size_t i = 0;
- /* Setup parenthood */
+ /* Sort readers by name: when creating a lot of objects in Blender,
+ * it is much faster if the order is sorted by name. */
+ archive->sort_readers();
+ *data->do_update = true;
+ *data->progress = 0.25f;
+
+ /* Create blender objects. */
+ for (USDPrimReader *reader : archive->readers()) {
+ if (!reader) {
+ continue;
+ }
+ reader->create_object(data->bmain, 0.0);
+ if ((++i & 1023) == 0) {
+ *data->do_update = true;
+ *data->progress = 0.25f + 0.25f * (i / size);
+ }
+ }
+ /* Setup parenthood and read actual object data. */
+ i = 0;
for (USDPrimReader *reader : archive->readers()) {
if (!reader) {
@@ -252,7 +272,7 @@ static void import_startjob(void *customdata, short *stop, short *do_update, flo
ob->parent = parent->object();
}
- *data->progress = 0.2f + 0.8f * (++i / size);
+ *data->progress = 0.5f + 0.5f * (++i / size);
*data->do_update = true;
if (G.is_break) {
@@ -290,9 +310,10 @@ static void import_endjob(void *customdata)
else if (data->archive) {
Base *base;
LayerCollection *lc;
+ const Scene *scene = data->scene;
ViewLayer *view_layer = data->view_layer;
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
lc = BKE_layer_collection_get_active(view_layer);
@@ -312,6 +333,7 @@ static void import_endjob(void *customdata)
/* Sync the collection, and do view layer operations. */
BKE_layer_collection_resync_allow();
BKE_main_collection_sync(data->bmain);
+ BKE_view_layer_synced_ensure(scene, view_layer);
for (USDPrimReader *reader : data->archive->readers()) {
if (!reader) {
continue;
diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc
index 8feceee55ed..3546beb022c 100644
--- a/source/blender/io/usd/intern/usd_reader_material.cc
+++ b/source/blender/io/usd/intern/usd_reader_material.cc
@@ -4,13 +4,17 @@
#include "usd_reader_material.h"
#include "BKE_image.h"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_node.h"
#include "BKE_node_tree_update.h"
+#include "BLI_fileops.h"
#include "BLI_math_vector.h"
+#include "BLI_path_util.h"
#include "BLI_string.h"
+#include "BLI_vector.hh"
#include "DNA_material_types.h"
@@ -94,6 +98,60 @@ static void link_nodes(
nodeAddLink(ntree, source, source_socket, dest, dest_socket);
}
+/* Returns a layer handle retrieved from the given attribute's property specs.
+ * Note that the returned handle may be invalid if no layer could be found. */
+static pxr::SdfLayerHandle get_layer_handle(const pxr::UsdAttribute &attribute)
+{
+ for (auto PropertySpec : attribute.GetPropertyStack(pxr::UsdTimeCode::EarliestTime())) {
+ if (PropertySpec->HasDefaultValue() ||
+ PropertySpec->GetLayer()->GetNumTimeSamplesForPath(PropertySpec->GetPath()) > 0) {
+ return PropertySpec->GetLayer();
+ }
+ }
+
+ return pxr::SdfLayerHandle();
+}
+
+static bool is_udim_path(const std::string &path)
+{
+ return path.find("<UDIM>") != std::string::npos;
+}
+
+/* For the given UDIM path (assumed to contain the UDIM token), returns an array
+ * containing valid tile indices. */
+static blender::Vector<int> get_udim_tiles(const std::string &file_path)
+{
+ char base_udim_path[FILE_MAX];
+ BLI_strncpy(base_udim_path, file_path.c_str(), sizeof(base_udim_path));
+
+ blender::Vector<int> udim_tiles;
+
+ /* Extract the tile numbers from all files on disk. */
+ ListBase tiles = {nullptr, nullptr};
+ int tile_start, tile_range;
+ bool result = BKE_image_get_tile_info(base_udim_path, &tiles, &tile_start, &tile_range);
+ if (result) {
+ LISTBASE_FOREACH (LinkData *, tile, &tiles) {
+ int tile_number = POINTER_AS_INT(tile->data);
+ udim_tiles.append(tile_number);
+ }
+ }
+
+ BLI_freelistN(&tiles);
+
+ return udim_tiles;
+}
+
+/* Add tiles with the given indices to the given image. */
+static void add_udim_tiles(Image *image, const blender::Vector<int> &indices)
+{
+ image->source = IMA_SRC_TILED;
+
+ for (int tile_number : indices) {
+ BKE_image_add_tile(image, tile_number, nullptr);
+ }
+}
+
/* Returns true if the given shader may have opacity < 1.0, based
* on heuristics. */
static bool needs_blend(const pxr::UsdShadeShader &usd_shader)
@@ -266,6 +324,7 @@ Material *USDMaterialReader::add_material(const pxr::UsdShadeMaterial &usd_mater
/* Create the material. */
Material *mtl = BKE_material_add(bmain_, mtl_name.c_str());
+ id_us_min(&mtl->id);
/* Get the UsdPreviewSurface shader source for the material,
* if there is one. */
@@ -294,8 +353,7 @@ void USDMaterialReader::import_usd_preview(Material *mtl,
* and output shaders. */
/* Add the node tree. */
- bNodeTree *ntree = ntreeAddTree(nullptr, "Shader Nodetree", "ShaderNodeTree");
- mtl->nodetree = ntree;
+ bNodeTree *ntree = ntreeAddTreeEmbedded(nullptr, &mtl->id, "Shader Nodetree", "ShaderNodeTree");
mtl->use_nodes = true;
/* Create the Principled BSDF shader node. */
@@ -601,11 +659,31 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
const pxr::SdfAssetPath &asset_path = file_val.Get<pxr::SdfAssetPath>();
std::string file_path = asset_path.GetResolvedPath();
if (file_path.empty()) {
+ /* No resolved path, so use the asset path (usually
+ * necessary for UDIM paths). */
+ file_path = asset_path.GetAssetPath();
+
+ /* Texture paths are frequently relative to the USD, so get
+ * the absolute path. */
+ if (pxr::SdfLayerHandle layer_handle = get_layer_handle(file_input.GetAttr())) {
+ file_path = layer_handle->ComputeAbsolutePath(file_path);
+ }
+ }
+
+ if (file_path.empty()) {
std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path
<< "' for Texture Image node." << std::endl;
return;
}
+ /* If this is a UDIM texture, this will store the
+ * UDIM tile indices. */
+ blender::Vector<int> udim_tiles;
+
+ if (is_udim_path(file_path)) {
+ udim_tiles = get_udim_tiles(file_path);
+ }
+
const char *im_file = file_path.c_str();
Image *image = BKE_image_load_exists(bmain_, im_file);
if (!image) {
@@ -614,6 +692,10 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader,
return;
}
+ if (udim_tiles.size() > 0) {
+ add_udim_tiles(image, udim_tiles);
+ }
+
tex_image->id = &image->id;
/* Set texture color space.
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc
index 45657525527..7cb4c65f166 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.cc
+++ b/source/blender/io/usd/intern/usd_reader_mesh.cc
@@ -6,6 +6,7 @@
#include "usd_reader_mesh.h"
#include "usd_reader_material.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -64,12 +65,26 @@ static void build_mat_map(const Main *bmain, std::map<std::string, Material *> *
static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim)
{
- return pxr::UsdShadeMaterialBindingAPI(prim).ComputeBoundMaterial();
+ pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim);
+
+ /* Compute generically bound ('allPurpose') materials. */
+ pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial();
+
+ /* If no generic material could be resolved, also check for 'preview' and
+ * 'full' purpose materials as fallbacks. */
+ if (!mtl) {
+ mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->preview);
+ }
+
+ if (!mtl) {
+ mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->full);
+ }
+
+ return mtl;
}
-/* Returns an existing Blender material that corresponds to the USD
- * material with with the given path. Returns null if no such material
- * exists. */
+/* Returns an existing Blender material that corresponds to the USD material with the given path.
+ * Returns null if no such material exists. */
static Material *find_existing_material(
const pxr::SdfPath &usd_mat_path,
const USDImportParams &params,
@@ -88,7 +103,7 @@ static Material *find_existing_material(
return mat_iter->second;
}
/* We can't find the Blender material which was previously created for this USD
- * material, which should never happen. */
+ * material, which should never happen. */
BLI_assert_unreachable();
}
}
@@ -168,6 +183,9 @@ static void assign_materials(Main *bmain,
std::cout << "WARNING: Couldn't assign material " << it->first << std::endl;
}
}
+ if (ob->totcol > 0) {
+ ob->actcol = 1;
+ }
}
} // namespace utils
@@ -193,7 +211,8 @@ static void *add_customdata_cb(Mesh *mesh, const char *name, const int data_type
/* Create a new layer. */
numloops = mesh->totloop;
- cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, nullptr, numloops, name);
+ cd_ptr = CustomData_add_layer_named(
+ loopdata, cd_data_type, CD_SET_DEFAULT, nullptr, numloops, name);
return cd_ptr;
}
@@ -229,11 +248,7 @@ void USDMeshReader::read_object_data(Main *bmain, const double motionSampleTime)
is_initial_load_ = false;
if (read_mesh != mesh) {
- /* 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. */
- uint16_t autosmooth = (read_mesh->flag & ME_AUTOSMOOTH);
- BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_, &CD_MASK_MESH, true);
- mesh->flag |= autosmooth;
+ BKE_mesh_nomain_to_mesh(read_mesh, mesh, object_);
}
readFaceSetsSample(bmain, mesh, motionSampleTime);
@@ -293,18 +308,17 @@ bool USDMeshReader::topology_changed(const Mesh *existing_mesh, const double mot
void USDMeshReader::read_mpolys(Mesh *mesh)
{
- MPoly *mpolys = mesh->mpoly;
- MLoop *mloops = mesh->mloop;
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
int loop_index = 0;
for (int i = 0; i < face_counts_.size(); i++) {
const int face_size = face_counts_[i];
- MPoly &poly = mpolys[i];
+ MPoly &poly = polys[i];
poly.loopstart = loop_index;
poly.totloop = face_size;
- poly.mat_nr = 0;
/* Polygons are always assumed to be smooth-shaded. If the mesh should be flat-shaded,
* this is encoded in custom loop normals. */
@@ -313,12 +327,12 @@ void USDMeshReader::read_mpolys(Mesh *mesh)
if (is_left_handed_) {
int loop_end_index = loop_index + (face_size - 1);
for (int f = 0; f < face_size; ++f, ++loop_index) {
- mloops[loop_index].v = face_indices_[loop_end_index - f];
+ loops[loop_index].v = face_indices_[loop_end_index - f];
}
}
else {
for (int f = 0; f < face_size; ++f, ++loop_index) {
- mloops[loop_index].v = face_indices_[loop_index];
+ loops[loop_index].v = face_indices_[loop_index];
}
}
}
@@ -382,6 +396,7 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo
}
}
+ const Span<MLoop> loops = mesh->loops();
for (int i = 0; i < face_counts_.size(); i++) {
const int face_size = face_counts_[i];
@@ -417,7 +432,7 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo
/* For Vertex interpolation, use the vertex index. */
int usd_uv_index = sample.interpolation == pxr::UsdGeomTokens->vertex ?
- mesh->mloop[loop_index].v :
+ loops[loop_index].v :
loop_index;
if (usd_uv_index >= sample.uvs.size()) {
@@ -497,24 +512,23 @@ void USDMeshReader::read_colors(Mesh *mesh, const double motionSampleTime)
MLoopCol *colors = static_cast<MLoopCol *>(cd_ptr);
- mesh->mloopcol = colors;
-
- MPoly *poly = mesh->mpoly;
-
- for (int i = 0, e = mesh->totpoly; i < e; ++i, ++poly) {
- for (int j = 0; j < poly->totloop; ++j) {
- int loop_index = poly->loopstart + j;
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ for (int j = 0; j < poly.totloop; ++j) {
+ int loop_index = poly.loopstart + j;
/* Default for constant varying interpolation. */
int usd_index = 0;
if (interp == pxr::UsdGeomTokens->vertex) {
- usd_index = mesh->mloop[loop_index].v;
+ usd_index = loops[loop_index].v;
}
else if (interp == pxr::UsdGeomTokens->faceVarying) {
- usd_index = poly->loopstart;
+ usd_index = poly.loopstart;
if (is_left_handed_) {
- usd_index += poly->totloop - 1 - j;
+ usd_index += poly.totloop - 1 - j;
}
else {
usd_index += j;
@@ -562,7 +576,7 @@ void USDMeshReader::read_vertex_creases(Mesh *mesh, const double motionSampleTim
}
float *creases = static_cast<float *>(
- CustomData_add_layer(&mesh->vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh->totvert));
+ CustomData_add_layer(&mesh->vdata, CD_CREASE, CD_SET_DEFAULT, nullptr, mesh->totvert));
for (size_t i = 0; i < corner_indices.size(); i++) {
creases[corner_indices[i]] = corner_sharpnesses[i];
@@ -612,15 +626,15 @@ void USDMeshReader::process_normals_face_varying(Mesh *mesh)
float(*lnors)[3] = static_cast<float(*)[3]>(
MEM_malloc_arrayN(loop_count, sizeof(float[3]), "USD::FaceNormals"));
- MPoly *mpoly = mesh->mpoly;
-
- for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) {
- for (int j = 0; j < mpoly->totloop; j++) {
- int blender_index = mpoly->loopstart + j;
+ const Span<MPoly> polys = mesh->polys();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ for (int j = 0; j < poly.totloop; j++) {
+ int blender_index = poly.loopstart + j;
- int usd_index = mpoly->loopstart;
+ int usd_index = poly.loopstart;
if (is_left_handed_) {
- usd_index += mpoly->totloop - 1 - j;
+ usd_index += poly.totloop - 1 - j;
}
else {
usd_index += j;
@@ -653,12 +667,11 @@ void USDMeshReader::process_normals_uniform(Mesh *mesh)
float(*lnors)[3] = static_cast<float(*)[3]>(
MEM_malloc_arrayN(mesh->totloop, sizeof(float[3]), "USD::FaceNormals"));
- MPoly *mpoly = mesh->mpoly;
-
- for (int i = 0, e = mesh->totpoly; i < e; ++i, ++mpoly) {
-
- for (int j = 0; j < mpoly->totloop; j++) {
- int loop_index = mpoly->loopstart + j;
+ const Span<MPoly> polys = mesh->polys();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ for (int j = 0; j < poly.totloop; j++) {
+ int loop_index = poly.loopstart + j;
lnors[loop_index][0] = normals_[i][0];
lnors[loop_index][1] = normals_[i][1];
lnors[loop_index][2] = normals_[i][2];
@@ -681,8 +694,9 @@ void USDMeshReader::read_mesh_sample(ImportSettings *settings,
* in code that expect this data to be there. */
if (new_mesh || (settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) {
+ MutableSpan<MVert> verts = mesh->verts_for_write();
for (int i = 0; i < positions_.size(); i++) {
- MVert &mvert = mesh->mvert[i];
+ MVert &mvert = verts[i];
mvert.co[0] = positions_[i][0];
mvert.co[1] = positions_[i][1];
mvert.co[2] = positions_[i][2];
@@ -720,10 +734,9 @@ void USDMeshReader::read_mesh_sample(ImportSettings *settings,
}
}
-void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime,
- MPoly *mpoly,
- const int /* totpoly */,
- std::map<pxr::SdfPath, int> *r_mat_map)
+void USDMeshReader::assign_facesets_to_material_indices(double motionSampleTime,
+ MutableSpan<int> material_indices,
+ std::map<pxr::SdfPath, int> *r_mat_map)
{
if (r_mat_map == nullptr) {
return;
@@ -763,9 +776,8 @@ void USDMeshReader::assign_facesets_to_mpoly(double motionSampleTime,
pxr::VtIntArray indices;
indicesAttribute.Get(&indices, motionSampleTime);
- for (int i = 0; i < indices.size(); i++) {
- MPoly &poly = mpoly[indices[i]];
- poly.mat_nr = mat_idx;
+ for (const int i : indices) {
+ material_indices[i] = mat_idx;
}
}
}
@@ -790,7 +802,12 @@ void USDMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const double mot
}
std::map<pxr::SdfPath, int> mat_map;
- assign_facesets_to_mpoly(motionSampleTime, mesh->mpoly, mesh->totpoly, &mat_map);
+
+ bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE);
+ this->assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map);
+ material_indices.finish();
/* Build material name map if it's not built yet. */
if (this->settings_->mat_name_to_mat.empty()) {
utils::build_mat_map(bmain, &this->settings_->mat_name_to_mat);
@@ -882,8 +899,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
existing_mesh, positions_.size(), 0, 0, face_indices_.size(), face_counts_.size());
for (pxr::TfToken token : uv_tokens) {
- void *cd_ptr = add_customdata_cb(active_mesh, token.GetText(), CD_MLOOPUV);
- active_mesh->mloopuv = static_cast<MLoopUV *>(cd_ptr);
+ add_customdata_cb(active_mesh, token.GetText(), CD_MLOOPUV);
}
}
@@ -893,10 +909,14 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh,
/* Here we assume that the number of materials doesn't change, i.e. that
* the material slots that were created when the object was loaded from
* USD are still valid now. */
- size_t num_polys = active_mesh->totpoly;
- if (num_polys > 0 && import_params_.import_materials) {
+ MutableSpan<MPoly> polys = active_mesh->polys_for_write();
+ if (!polys.is_empty() && import_params_.import_materials) {
std::map<pxr::SdfPath, int> mat_map;
- assign_facesets_to_mpoly(motionSampleTime, active_mesh->mpoly, num_polys, &mat_map);
+ bke::MutableAttributeAccessor attributes = active_mesh->attributes_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ attributes.lookup_or_add_for_write_only_span<int>("material_index", ATTR_DOMAIN_FACE);
+ assign_facesets_to_material_indices(motionSampleTime, material_indices.span, &mat_map);
+ material_indices.finish();
}
}
diff --git a/source/blender/io/usd/intern/usd_reader_mesh.h b/source/blender/io/usd/intern/usd_reader_mesh.h
index 5e33ce8b5e8..181fd5ebf79 100644
--- a/source/blender/io/usd/intern/usd_reader_mesh.h
+++ b/source/blender/io/usd/intern/usd_reader_mesh.h
@@ -3,13 +3,13 @@
* Modifications Copyright 2021 Tangent Animation and. NVIDIA Corporation. All rights reserved. */
#pragma once
+#include "BLI_span.hh"
+
#include "usd.h"
#include "usd_reader_geom.h"
#include "pxr/usd/usdGeom/mesh.h"
-struct MPoly;
-
namespace blender::io::usd {
class USDMeshReader : public USDGeomReader {
@@ -61,10 +61,9 @@ class USDMeshReader : public USDGeomReader {
/** Set USD uniform (per-face) normals as Blender loop normals. */
void process_normals_uniform(Mesh *mesh);
void readFaceSetsSample(Main *bmain, Mesh *mesh, double motionSampleTime);
- void assign_facesets_to_mpoly(double motionSampleTime,
- struct MPoly *mpoly,
- int totpoly,
- std::map<pxr::SdfPath, int> *r_mat_map);
+ void assign_facesets_to_material_indices(double motionSampleTime,
+ MutableSpan<int> material_indices,
+ std::map<pxr::SdfPath, int> *r_mat_map);
void read_mpolys(Mesh *mesh);
void read_uvs(Mesh *mesh, double motionSampleTime, bool load_uvs = false);
diff --git a/source/blender/io/usd/intern/usd_reader_stage.cc b/source/blender/io/usd/intern/usd_reader_stage.cc
index 583c58a1356..df75be849e2 100644
--- a/source/blender/io/usd/intern/usd_reader_stage.cc
+++ b/source/blender/io/usd/intern/usd_reader_stage.cc
@@ -28,6 +28,9 @@
#include <iostream>
+#include "BLI_sort.hh"
+#include "BLI_string.h"
+
namespace blender::io::usd {
USDStageReader::USDStageReader(pxr::UsdStageRefPtr stage,
@@ -252,8 +255,6 @@ USDPrimReader *USDStageReader::collect_readers(Main *bmain, const pxr::UsdPrim &
return nullptr;
}
- reader->create_object(bmain, 0.0);
-
readers_.push_back(reader);
reader->incref();
@@ -310,4 +311,14 @@ void USDStageReader::clear_readers()
readers_.clear();
}
+void USDStageReader::sort_readers()
+{
+ blender::parallel_sort(
+ readers_.begin(), readers_.end(), [](const USDPrimReader *a, const USDPrimReader *b) {
+ const char *na = a ? a->name().c_str() : "";
+ const char *nb = b ? b->name().c_str() : "";
+ return BLI_strcasecmp(na, nb) < 0;
+ });
+}
+
} // Namespace blender::io::usd
diff --git a/source/blender/io/usd/intern/usd_reader_stage.h b/source/blender/io/usd/intern/usd_reader_stage.h
index 0ed964c7679..5f4a343f874 100644
--- a/source/blender/io/usd/intern/usd_reader_stage.h
+++ b/source/blender/io/usd/intern/usd_reader_stage.h
@@ -63,6 +63,8 @@ class USDStageReader {
return readers_;
};
+ void sort_readers();
+
private:
USDPrimReader *collect_readers(Main *bmain, const pxr::UsdPrim &prim);
diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc
index b76f74cfd3d..a39f74c6420 100644
--- a/source/blender/io/usd/intern/usd_writer_mesh.cc
+++ b/source/blender/io/usd/intern/usd_writer_mesh.cc
@@ -11,6 +11,7 @@
#include "BLI_math_vector.h"
#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_lib_id.h"
#include "BKE_material.h"
@@ -245,8 +246,8 @@ static void get_vertices(const Mesh *mesh, USDMeshData &usd_mesh_data)
{
usd_mesh_data.points.reserve(mesh->totvert);
- const MVert *verts = mesh->mvert;
- for (int i = 0; i < mesh->totvert; ++i) {
+ const Span<MVert> verts = mesh->verts();
+ for (const int i : verts.index_range()) {
usd_mesh_data.points.push_back(pxr::GfVec3f(verts[i].co));
}
}
@@ -255,22 +256,27 @@ static void get_loops_polys(const Mesh *mesh, USDMeshData &usd_mesh_data)
{
/* Only construct face groups (a.k.a. geometry subsets) when we need them for material
* assignments. */
- bool construct_face_groups = mesh->totcol > 1;
+ const bke::AttributeAccessor attributes = mesh->attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ if (!material_indices.is_single() && mesh->totcol > 1) {
+ const VArraySpan<int> indices_span(material_indices);
+ for (const int i : indices_span.index_range()) {
+ usd_mesh_data.face_groups[indices_span[i]].push_back(i);
+ }
+ }
usd_mesh_data.face_vertex_counts.reserve(mesh->totpoly);
usd_mesh_data.face_indices.reserve(mesh->totloop);
- MLoop *mloop = mesh->mloop;
- MPoly *mpoly = mesh->mpoly;
- for (int i = 0; i < mesh->totpoly; ++i, ++mpoly) {
- MLoop *loop = mloop + mpoly->loopstart;
- usd_mesh_data.face_vertex_counts.push_back(mpoly->totloop);
- for (int j = 0; j < mpoly->totloop; ++j, ++loop) {
- usd_mesh_data.face_indices.push_back(loop->v);
- }
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
- if (construct_face_groups) {
- usd_mesh_data.face_groups[mpoly->mat_nr].push_back(i);
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ usd_mesh_data.face_vertex_counts.push_back(poly.totloop);
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ usd_mesh_data.face_indices.push_back(loop.v);
}
}
}
@@ -279,22 +285,23 @@ static void get_edge_creases(const Mesh *mesh, USDMeshData &usd_mesh_data)
{
const float factor = 1.0f / 255.0f;
- MEdge *edge = mesh->medge;
+ const Span<MEdge> edges = mesh->edges();
float sharpness;
- for (int edge_idx = 0, totedge = mesh->totedge; edge_idx < totedge; ++edge_idx, ++edge) {
- if (edge->crease == 0) {
+ for (const int i : edges.index_range()) {
+ const MEdge &edge = edges[i];
+ if (edge.crease == 0) {
continue;
}
- if (edge->crease == 255) {
+ if (edge.crease == 255) {
sharpness = pxr::UsdGeomMesh::SHARPNESS_INFINITE;
}
else {
- sharpness = static_cast<float>(edge->crease) * factor;
+ sharpness = static_cast<float>(edge.crease) * factor;
}
- usd_mesh_data.crease_vertex_indices.push_back(edge->v1);
- usd_mesh_data.crease_vertex_indices.push_back(edge->v2);
+ usd_mesh_data.crease_vertex_indices.push_back(edge.v1);
+ usd_mesh_data.crease_vertex_indices.push_back(edge.v2);
usd_mesh_data.crease_lengths.push_back(2);
usd_mesh_data.crease_sharpnesses.push_back(sharpness);
}
@@ -392,6 +399,8 @@ void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_
{
pxr::UsdTimeCode timecode = get_export_time_code();
const float(*lnors)[3] = static_cast<float(*)[3]>(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
pxr::VtVec3fArray loop_normals;
loop_normals.reserve(mesh->totloop);
@@ -406,21 +415,20 @@ void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_
/* Compute the loop normals based on the 'smooth' flag. */
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
const float(*face_normals)[3] = BKE_mesh_poly_normals_ensure(mesh);
- MPoly *mpoly = mesh->mpoly;
- for (int poly_idx = 0, totpoly = mesh->totpoly; poly_idx < totpoly; ++poly_idx, ++mpoly) {
- MLoop *mloop = mesh->mloop + mpoly->loopstart;
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
- if ((mpoly->flag & ME_SMOOTH) == 0) {
+ if ((poly.flag & ME_SMOOTH) == 0) {
/* Flat shaded, use common normal for all verts. */
- pxr::GfVec3f pxr_normal(face_normals[poly_idx]);
- for (int loop_idx = 0; loop_idx < mpoly->totloop; ++loop_idx) {
+ pxr::GfVec3f pxr_normal(face_normals[i]);
+ for (int loop_idx = 0; loop_idx < poly.totloop; ++loop_idx) {
loop_normals.push_back(pxr_normal);
}
}
else {
/* Smooth shaded, use individual vert normals. */
- for (int loop_idx = 0; loop_idx < mpoly->totloop; ++loop_idx, ++mloop) {
- loop_normals.push_back(pxr::GfVec3f(vert_normals[mloop->v]));
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ loop_normals.push_back(pxr::GfVec3f(vert_normals[loop.v]));
}
}
}
diff --git a/source/blender/io/usd/intern/usd_writer_volume.cc b/source/blender/io/usd/intern/usd_writer_volume.cc
index 6300e5c657c..12db6d73901 100644
--- a/source/blender/io/usd/intern/usd_writer_volume.cc
+++ b/source/blender/io/usd/intern/usd_writer_volume.cc
@@ -152,7 +152,7 @@ std::optional<std::string> USDVolumeWriter::construct_vdb_file_path(const Volume
strcat(vdb_file_name, ".vdb");
char vdb_file_path[FILE_MAX];
- BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, NULL);
+ BLI_path_join(vdb_file_path, sizeof(vdb_file_path), vdb_directory_path, vdb_file_name, nullptr);
return vdb_file_path;
}
diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h
index a07315d8b4e..3494d8ffdc3 100644
--- a/source/blender/io/usd/usd.h
+++ b/source/blender/io/usd/usd.h
@@ -52,7 +52,7 @@ struct USDImportParams {
bool import_materials;
bool import_meshes;
bool import_volumes;
- char *prim_path_mask;
+ char prim_path_mask[1024];
bool import_subdiv;
bool import_instance_proxies;
bool create_collection;
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
index fb0b4a1aca9..2fd2973ee73 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc
@@ -4,6 +4,7 @@
* \ingroup obj
*/
+#include "BLI_path_util.h"
#include "BLI_timeit.hh"
#include "IO_wavefront_obj.h"
@@ -11,14 +12,26 @@
#include "obj_exporter.hh"
#include "obj_importer.hh"
+using namespace blender::timeit;
+
+static void report_duration(const char *job, const TimePoint &start_time, const char *path)
+{
+ Nanoseconds duration = Clock::now() - start_time;
+ std::cout << "OBJ " << job << " of '" << BLI_path_basename(path) << "' took ";
+ print_duration(duration);
+ std::cout << '\n';
+}
+
void OBJ_export(bContext *C, const OBJExportParams *export_params)
{
- SCOPED_TIMER("OBJ export");
+ TimePoint start_time = Clock::now();
blender::io::obj::exporter_main(C, *export_params);
+ report_duration("export", start_time, export_params->filepath);
}
void OBJ_import(bContext *C, const OBJImportParams *import_params)
{
- SCOPED_TIMER(__func__);
+ TimePoint start_time = Clock::now();
blender::io::obj::importer_main(C, *import_params);
+ report_duration("import", start_time, import_params->filepath);
}
diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
index b4a00deb99c..0a92bbca477 100644
--- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h
+++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h
@@ -16,8 +16,6 @@
extern "C" {
#endif
-static const int TOTAL_AXES = 3;
-
struct OBJExportParams {
/** Full path to the destination .OBJ file. */
char filepath[FILE_MAX];
@@ -50,18 +48,15 @@ struct OBJExportParams {
bool export_triangulated_mesh;
bool export_curves_as_nurbs;
ePathReferenceMode path_mode;
+ bool export_pbr_extensions;
/* Grouping options. */
bool export_object_groups;
bool export_material_groups;
bool export_vertex_groups;
- /**
- * Calculate smooth groups from sharp edges.
- */
+ /* Calculate smooth groups from sharp edges. */
bool export_smooth_groups;
- /**
- * Create bitflags instead of the default "0"/"1" group IDs.
- */
+ /* Create bitflags instead of the default "0"/"1" group IDs. */
bool smooth_groups_bitflags;
};
@@ -74,6 +69,8 @@ struct OBJImportParams {
eIOAxis up_axis;
bool import_vertex_groups;
bool validate_meshes;
+ bool relative_paths;
+ bool clear_selection;
};
/**
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
index 731587bfcea..f2547e6fc14 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc
@@ -44,7 +44,7 @@ static const char *DEFORM_GROUP_DISABLED = "off";
* So an empty material name is written. */
static const char *MATERIAL_GROUP_DISABLED = "";
-void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vert_uv_normal_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -57,12 +57,12 @@ void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
const int uv_offset = offsets.uv_vertex_offset + 1;
const int normal_offset = offsets.normal_offset + 1;
const int n = vert_indices.size();
- fh.write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write_obj_poly_begin();
if (!flip) {
for (int j = 0; j < n; ++j) {
- fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(vert_indices[j] + vertex_offset,
- uv_indices[j] + uv_offset,
- normal_indices[j] + normal_offset);
+ fh.write_obj_poly_v_uv_normal(vert_indices[j] + vertex_offset,
+ uv_indices[j] + uv_offset,
+ normal_indices[j] + normal_offset);
}
}
else {
@@ -71,15 +71,15 @@ void OBJWriter::write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
* then go backwards. Same logic in other write_*_indices functions below. */
for (int k = 0; k < n; ++k) {
int j = k == 0 ? 0 : n - k;
- fh.write<eOBJSyntaxElement::vertex_uv_normal_indices>(vert_indices[j] + vertex_offset,
- uv_indices[j] + uv_offset,
- normal_indices[j] + normal_offset);
+ fh.write_obj_poly_v_uv_normal(vert_indices[j] + vertex_offset,
+ uv_indices[j] + uv_offset,
+ normal_indices[j] + normal_offset);
}
}
- fh.write<eOBJSyntaxElement::poly_element_end>();
+ fh.write_obj_poly_end();
}
-void OBJWriter::write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vert_normal_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> /*uv_indices*/,
@@ -90,24 +90,24 @@ void OBJWriter::write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
const int vertex_offset = offsets.vertex_offset + 1;
const int normal_offset = offsets.normal_offset + 1;
const int n = vert_indices.size();
- fh.write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write_obj_poly_begin();
if (!flip) {
for (int j = 0; j < n; ++j) {
- fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + vertex_offset,
- normal_indices[j] + normal_offset);
+ fh.write_obj_poly_v_normal(vert_indices[j] + vertex_offset,
+ normal_indices[j] + normal_offset);
}
}
else {
for (int k = 0; k < n; ++k) {
int j = k == 0 ? 0 : n - k;
- fh.write<eOBJSyntaxElement::vertex_normal_indices>(vert_indices[j] + vertex_offset,
- normal_indices[j] + normal_offset);
+ fh.write_obj_poly_v_normal(vert_indices[j] + vertex_offset,
+ normal_indices[j] + normal_offset);
}
}
- fh.write<eOBJSyntaxElement::poly_element_end>();
+ fh.write_obj_poly_end();
}
-void OBJWriter::write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vert_uv_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -118,24 +118,22 @@ void OBJWriter::write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
const int vertex_offset = offsets.vertex_offset + 1;
const int uv_offset = offsets.uv_vertex_offset + 1;
const int n = vert_indices.size();
- fh.write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write_obj_poly_begin();
if (!flip) {
for (int j = 0; j < n; ++j) {
- fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + vertex_offset,
- uv_indices[j] + uv_offset);
+ fh.write_obj_poly_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
}
}
else {
for (int k = 0; k < n; ++k) {
int j = k == 0 ? 0 : n - k;
- fh.write<eOBJSyntaxElement::vertex_uv_indices>(vert_indices[j] + vertex_offset,
- uv_indices[j] + uv_offset);
+ fh.write_obj_poly_v_uv(vert_indices[j] + vertex_offset, uv_indices[j] + uv_offset);
}
}
- fh.write<eOBJSyntaxElement::poly_element_end>();
+ fh.write_obj_poly_end();
}
-void OBJWriter::write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vert_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> /*uv_indices*/,
@@ -144,27 +142,27 @@ void OBJWriter::write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
{
const int vertex_offset = offsets.vertex_offset + 1;
const int n = vert_indices.size();
- fh.write<eOBJSyntaxElement::poly_element_begin>();
+ fh.write_obj_poly_begin();
if (!flip) {
for (int j = 0; j < n; ++j) {
- fh.write<eOBJSyntaxElement::vertex_indices>(vert_indices[j] + vertex_offset);
+ fh.write_obj_poly_v(vert_indices[j] + vertex_offset);
}
}
else {
for (int k = 0; k < n; ++k) {
int j = k == 0 ? 0 : n - k;
- fh.write<eOBJSyntaxElement::vertex_indices>(vert_indices[j] + vertex_offset);
+ fh.write_obj_poly_v(vert_indices[j] + vertex_offset);
}
}
- fh.write<eOBJSyntaxElement::poly_element_end>();
+ fh.write_obj_poly_end();
}
void OBJWriter::write_header() const
{
using namespace std::string_literals;
- FormatHandler<eFileType::OBJ> fh;
- fh.write<eOBJSyntaxElement::string>("# Blender "s + BKE_blender_version_string() + "\n");
- fh.write<eOBJSyntaxElement::string>("# www.blender.org\n");
+ FormatHandler fh;
+ fh.write_string("# Blender "s + BKE_blender_version_string());
+ fh.write_string("# www.blender.org");
fh.write_to_file(outfile_);
}
@@ -174,22 +172,27 @@ void OBJWriter::write_mtllib_name(const StringRefNull mtl_filepath) const
char mtl_file_name[FILE_MAXFILE];
char mtl_dir_name[FILE_MAXDIR];
BLI_split_dirfile(mtl_filepath.data(), mtl_dir_name, mtl_file_name, FILE_MAXDIR, FILE_MAXFILE);
- FormatHandler<eFileType::OBJ> fh;
- fh.write<eOBJSyntaxElement::mtllib>(mtl_file_name);
+ FormatHandler fh;
+ fh.write_obj_mtllib(mtl_file_name);
fh.write_to_file(outfile_);
}
-void OBJWriter::write_object_name(FormatHandler<eFileType::OBJ> &fh,
- const OBJMesh &obj_mesh_data) const
+static void spaces_to_underscores(std::string &r_name)
{
- const char *object_name = obj_mesh_data.get_object_name();
+ std::replace(r_name.begin(), r_name.end(), ' ', '_');
+}
+
+void OBJWriter::write_object_name(FormatHandler &fh, const OBJMesh &obj_mesh_data) const
+{
+ std::string object_name = obj_mesh_data.get_object_name();
+ spaces_to_underscores(object_name);
if (export_params_.export_object_groups) {
- const std::string object_name = obj_mesh_data.get_object_name();
- const char *mesh_name = obj_mesh_data.get_object_mesh_name();
- fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mesh_name);
+ std::string mesh_name = obj_mesh_data.get_object_mesh_name();
+ spaces_to_underscores(mesh_name);
+ fh.write_obj_group(object_name + "_" + mesh_name);
return;
}
- fh.write<eOBJSyntaxElement::object_name>(object_name);
+ fh.write_obj_object(object_name);
}
/* Split up large meshes into multi-threaded jobs; each job processes
@@ -207,9 +210,7 @@ static int calc_chunk_count(int count)
* will be written into the final /fh/ buffer at the end.
*/
template<typename Function>
-void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh,
- int tot_count,
- const Function &function)
+void obj_parallel_chunked_output(FormatHandler &fh, int tot_count, const Function &function)
{
if (tot_count <= 0) {
return;
@@ -225,7 +226,7 @@ void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh,
return;
}
/* Give each chunk its own temporary output buffer, and process them in parallel. */
- std::vector<FormatHandler<eFileType::OBJ>> buffers(chunk_count);
+ std::vector<FormatHandler> buffers(chunk_count);
blender::threading::parallel_for(IndexRange(chunk_count), 1, [&](IndexRange range) {
for (const int r : range) {
int i_start = r * chunk_size;
@@ -242,58 +243,57 @@ void obj_parallel_chunked_output(FormatHandler<eFileType::OBJ> &fh,
}
}
-void OBJWriter::write_vertex_coords(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_vertex_coords(FormatHandler &fh,
const OBJMesh &obj_mesh_data,
bool write_colors) const
{
const int tot_count = obj_mesh_data.tot_vertices();
Mesh *mesh = obj_mesh_data.get_mesh();
- CustomDataLayer *colors_layer = nullptr;
+ const CustomDataLayer *colors_layer = nullptr;
if (write_colors) {
colors_layer = BKE_id_attributes_active_color_get(&mesh->id);
}
if (write_colors && (colors_layer != nullptr)) {
- const bke::AttributeAccessor attributes = bke::mesh_attributes(*mesh);
+ const bke::AttributeAccessor attributes = mesh->attributes();
const VArray<ColorGeometry4f> attribute = attributes.lookup_or_default<ColorGeometry4f>(
colors_layer->name, ATTR_DOMAIN_POINT, {0.0f, 0.0f, 0.0f, 0.0f});
BLI_assert(tot_count == attribute.size());
- obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
ColorGeometry4f linear = attribute.get(i);
float srgb[3];
linearrgb_to_srgb_v3_v3(srgb, linear);
- buf.write<eOBJSyntaxElement::vertex_coords_color>(
- vertex[0], vertex[1], vertex[2], srgb[0], srgb[1], srgb[2]);
+ buf.write_obj_vertex_color(vertex[0], vertex[1], vertex[2], srgb[0], srgb[1], srgb[2]);
});
}
else {
- obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
float3 vertex = obj_mesh_data.calc_vertex_coords(i, export_params_.scaling_factor);
- buf.write<eOBJSyntaxElement::vertex_coords>(vertex[0], vertex[1], vertex[2]);
+ buf.write_obj_vertex(vertex[0], vertex[1], vertex[2]);
});
}
}
-void OBJWriter::write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &r_obj_mesh_data) const
+void OBJWriter::write_uv_coords(FormatHandler &fh, OBJMesh &r_obj_mesh_data) const
{
const Vector<float2> &uv_coords = r_obj_mesh_data.get_uv_coords();
const int tot_count = uv_coords.size();
- obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
const float2 &uv_vertex = uv_coords[i];
- buf.write<eOBJSyntaxElement::uv_vertex_coords>(uv_vertex[0], uv_vertex[1]);
+ buf.write_obj_uv(uv_vertex[0], uv_vertex[1]);
});
}
-void OBJWriter::write_poly_normals(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data)
+void OBJWriter::write_poly_normals(FormatHandler &fh, OBJMesh &obj_mesh_data)
{
/* Poly normals should be calculated earlier via store_normal_coords_and_indices. */
const Vector<float3> &normal_coords = obj_mesh_data.get_normal_coords();
const int tot_count = normal_coords.size();
- obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler<eFileType::OBJ> &buf, int i) {
+ obj_parallel_chunked_output(fh, tot_count, [&](FormatHandler &buf, int i) {
const float3 &normal = normal_coords[i];
- buf.write<eOBJSyntaxElement::normal>(normal[0], normal[1], normal[2]);
+ buf.write_obj_normal(normal[0], normal[1], normal[2]);
});
}
@@ -328,7 +328,7 @@ static int get_smooth_group(const OBJMesh &mesh, const OBJExportParams &params,
return group;
}
-void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_poly_elements(FormatHandler &fh,
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data,
std::function<const char *(int)> matname_fn)
@@ -340,7 +340,7 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
const int tot_deform_groups = obj_mesh_data.tot_deform_groups();
threading::EnumerableThreadSpecific<Vector<float>> group_weights;
- obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler<eFileType::OBJ> &buf, int idx) {
+ obj_parallel_chunked_output(fh, tot_polygons, [&](FormatHandler &buf, int idx) {
/* Polygon order for writing into the file is not necessarily the same
* as order in the mesh; it will be sorted by material indices. Remap current
* and previous indices here according to the order. */
@@ -356,7 +356,7 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
const int prev_group = get_smooth_group(obj_mesh_data, export_params_, prev_i);
const int group = get_smooth_group(obj_mesh_data, export_params_, i);
if (group != prev_group) {
- buf.write<eOBJSyntaxElement::smooth_group>(group);
+ buf.write_obj_smooth(group);
}
}
@@ -369,19 +369,22 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
prev_i, local_weights);
const int16_t group = obj_mesh_data.get_poly_deform_group_index(i, local_weights);
if (group != prev_group) {
- buf.write<eOBJSyntaxElement::object_group>(
- group == NOT_FOUND ? DEFORM_GROUP_DISABLED :
- obj_mesh_data.get_poly_deform_group_name(group));
+ buf.write_obj_group(group == NOT_FOUND ? DEFORM_GROUP_DISABLED :
+ obj_mesh_data.get_poly_deform_group_name(group));
}
}
+ const bke::AttributeAccessor attributes = obj_mesh_data.get_mesh()->attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+
/* Write material name and material group if different from previous. */
if (export_params_.export_materials && obj_mesh_data.tot_materials() > 0) {
- const int16_t prev_mat = idx == 0 ? NEGATIVE_INIT : obj_mesh_data.ith_poly_matnr(prev_i);
- const int16_t mat = obj_mesh_data.ith_poly_matnr(i);
+ const int16_t prev_mat = idx == 0 ? NEGATIVE_INIT : std::max(0, material_indices[prev_i]);
+ const int16_t mat = std::max(0, material_indices[i]);
if (mat != prev_mat) {
if (mat == NOT_FOUND) {
- buf.write<eOBJSyntaxElement::poly_usemtl>(MATERIAL_GROUP_DISABLED);
+ buf.write_obj_usemtl(MATERIAL_GROUP_DISABLED);
}
else {
const char *mat_name = matname_fn(mat);
@@ -389,10 +392,11 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
mat_name = MATERIAL_GROUP_DISABLED;
}
if (export_params_.export_material_groups) {
- const std::string object_name = obj_mesh_data.get_object_name();
- fh.write<eOBJSyntaxElement::object_group>(object_name + "_" + mat_name);
+ std::string object_name = obj_mesh_data.get_object_name();
+ spaces_to_underscores(object_name);
+ fh.write_obj_group(object_name + "_" + mat_name);
}
- buf.write<eOBJSyntaxElement::poly_usemtl>(mat_name);
+ buf.write_obj_usemtl(mat_name);
}
}
}
@@ -407,7 +411,7 @@ void OBJWriter::write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
});
}
-void OBJWriter::write_edges_indices(FormatHandler<eFileType::OBJ> &fh,
+void OBJWriter::write_edges_indices(FormatHandler &fh,
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data) const
{
@@ -419,13 +423,12 @@ void OBJWriter::write_edges_indices(FormatHandler<eFileType::OBJ> &fh,
if (!vertex_indices) {
continue;
}
- fh.write<eOBJSyntaxElement::edge>((*vertex_indices)[0] + offsets.vertex_offset + 1,
- (*vertex_indices)[1] + offsets.vertex_offset + 1);
+ fh.write_obj_edge((*vertex_indices)[0] + offsets.vertex_offset + 1,
+ (*vertex_indices)[1] + offsets.vertex_offset + 1);
}
}
-void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
- const OBJCurve &obj_nurbs_data) const
+void OBJWriter::write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_data) const
{
const int total_splines = obj_nurbs_data.total_splines();
for (int spline_idx = 0; spline_idx < total_splines; spline_idx++) {
@@ -433,15 +436,14 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
for (int vertex_idx = 0; vertex_idx < total_vertices; vertex_idx++) {
const float3 vertex_coords = obj_nurbs_data.vertex_coordinates(
spline_idx, vertex_idx, export_params_.scaling_factor);
- fh.write<eOBJSyntaxElement::vertex_coords>(
- vertex_coords[0], vertex_coords[1], vertex_coords[2]);
+ fh.write_obj_vertex(vertex_coords[0], vertex_coords[1], vertex_coords[2]);
}
const char *nurbs_name = obj_nurbs_data.get_curve_name();
const int nurbs_degree = obj_nurbs_data.get_nurbs_degree(spline_idx);
- fh.write<eOBJSyntaxElement::object_group>(nurbs_name);
- fh.write<eOBJSyntaxElement::cstype>();
- fh.write<eOBJSyntaxElement::nurbs_degree>(nurbs_degree);
+ fh.write_obj_group(nurbs_name);
+ fh.write_obj_cstype();
+ fh.write_obj_nurbs_degree(nurbs_degree);
/**
* The numbers written here are indices into the vertex coordinates written
* earlier, relative to the line that is going to be written.
@@ -450,13 +452,13 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
* 0.0 1.0 -1 -2 -3 -4 -1 -2 -3 for a cyclic curve with 4 vertices.
*/
const int total_control_points = obj_nurbs_data.total_spline_control_points(spline_idx);
- fh.write<eOBJSyntaxElement::curve_element_begin>();
+ fh.write_obj_curve_begin();
for (int i = 0; i < total_control_points; i++) {
/* "+1" to keep indices one-based, even if they're negative: i.e., -1 refers to the
* last vertex coordinate, -2 second last. */
- fh.write<eOBJSyntaxElement::vertex_indices>(-((i % total_vertices) + 1));
+ fh.write_obj_poly_v(-((i % total_vertices) + 1));
}
- fh.write<eOBJSyntaxElement::curve_element_end>();
+ fh.write_obj_curve_end();
/**
* In `parm u 0 0.1 ..` line:, (total control points + 2) equidistant numbers in the
@@ -467,7 +469,7 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
const short flagsu = obj_nurbs_data.get_nurbs_flagu(spline_idx);
const bool cyclic = flagsu & CU_NURB_CYCLIC;
const bool endpoint = !cyclic && (flagsu & CU_NURB_ENDPOINT);
- fh.write<eOBJSyntaxElement::nurbs_parameter_begin>();
+ fh.write_obj_nurbs_parm_begin();
for (int i = 1; i <= total_control_points + 2; i++) {
float parm = 1.0f * i / (total_control_points + 2 + 1);
if (endpoint) {
@@ -478,11 +480,10 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
parm = 1;
}
}
- fh.write<eOBJSyntaxElement::nurbs_parameters>(parm);
+ fh.write_obj_nurbs_parm(parm);
}
- fh.write<eOBJSyntaxElement::nurbs_parameter_end>();
-
- fh.write<eOBJSyntaxElement::nurbs_group_end>();
+ fh.write_obj_nurbs_parm_end();
+ fh.write_obj_nurbs_group_end();
}
}
@@ -490,6 +491,21 @@ void OBJWriter::write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh,
/** \name .MTL writers.
* \{ */
+static const char *tex_map_type_to_string[] = {
+ "map_Kd",
+ "map_Pm",
+ "map_Ks",
+ "map_Ns",
+ "map_Pr",
+ "map_Ps",
+ "map_refl",
+ "map_Ke",
+ "map_d",
+ "map_Bump",
+};
+BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_string) == (int)MTLTexMapType::Count,
+ "array size mismatch");
+
/**
* Convert #float3 to string of space-separated numbers, with no leading or trailing space.
* Only to be used in NON-performance-critical code.
@@ -530,9 +546,9 @@ void MTLWriter::write_header(const char *blen_filepath)
const char *blen_basename = (blen_filepath && blen_filepath[0] != '\0') ?
BLI_path_basename(blen_filepath) :
"None";
- fmt_handler_.write<eMTLSyntaxElement::string>("# Blender "s + BKE_blender_version_string() +
- " MTL File: '" + blen_basename + "'\n");
- fmt_handler_.write<eMTLSyntaxElement::string>("# www.blender.org\n");
+ fmt_handler_.write_string("# Blender "s + BKE_blender_version_string() + " MTL File: '" +
+ blen_basename + "'");
+ fmt_handler_.write_string("# www.blender.org");
}
StringRefNull MTLWriter::mtl_file_path() const
@@ -540,66 +556,109 @@ StringRefNull MTLWriter::mtl_file_path() const
return mtl_filepath_;
}
-void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl_material)
+void MTLWriter::write_bsdf_properties(const MTLMaterial &mtl, bool write_pbr)
{
- fmt_handler_.write<eMTLSyntaxElement::Ns>(mtl_material.Ns);
- fmt_handler_.write<eMTLSyntaxElement::Ka>(
- mtl_material.Ka.x, mtl_material.Ka.y, mtl_material.Ka.z);
- fmt_handler_.write<eMTLSyntaxElement::Kd>(
- mtl_material.Kd.x, mtl_material.Kd.y, mtl_material.Kd.z);
- fmt_handler_.write<eMTLSyntaxElement::Ks>(
- mtl_material.Ks.x, mtl_material.Ks.y, mtl_material.Ks.z);
- fmt_handler_.write<eMTLSyntaxElement::Ke>(
- mtl_material.Ke.x, mtl_material.Ke.y, mtl_material.Ke.z);
- fmt_handler_.write<eMTLSyntaxElement::Ni>(mtl_material.Ni);
- fmt_handler_.write<eMTLSyntaxElement::d>(mtl_material.d);
- fmt_handler_.write<eMTLSyntaxElement::illum>(mtl_material.illum);
+ /* For various material properties, we only capture information
+ * coming from the texture, or the default value of the socket.
+ * When the texture is present, do not emit the default value. */
+
+ /* Do not write Ns & Ka when writing in PBR mode. */
+ if (!write_pbr) {
+ if (!mtl.tex_map_of_type(MTLTexMapType::SpecularExponent).is_valid()) {
+ fmt_handler_.write_mtl_float("Ns", mtl.spec_exponent);
+ }
+ fmt_handler_.write_mtl_float3(
+ "Ka", mtl.ambient_color.x, mtl.ambient_color.y, mtl.ambient_color.z);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Color).is_valid()) {
+ fmt_handler_.write_mtl_float3("Kd", mtl.color.x, mtl.color.y, mtl.color.z);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Specular).is_valid()) {
+ fmt_handler_.write_mtl_float3("Ks", mtl.spec_color.x, mtl.spec_color.y, mtl.spec_color.z);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Emission).is_valid()) {
+ fmt_handler_.write_mtl_float3(
+ "Ke", mtl.emission_color.x, mtl.emission_color.y, mtl.emission_color.z);
+ }
+ fmt_handler_.write_mtl_float("Ni", mtl.ior);
+ if (!mtl.tex_map_of_type(MTLTexMapType::Alpha).is_valid()) {
+ fmt_handler_.write_mtl_float("d", mtl.alpha);
+ }
+ fmt_handler_.write_mtl_illum(mtl.illum_mode);
+
+ if (write_pbr) {
+ if (!mtl.tex_map_of_type(MTLTexMapType::Roughness).is_valid() && mtl.roughness >= 0.0f) {
+ fmt_handler_.write_mtl_float("Pr", mtl.roughness);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Metallic).is_valid() && mtl.metallic >= 0.0f) {
+ fmt_handler_.write_mtl_float("Pm", mtl.metallic);
+ }
+ if (!mtl.tex_map_of_type(MTLTexMapType::Sheen).is_valid() && mtl.sheen >= 0.0f) {
+ fmt_handler_.write_mtl_float("Ps", mtl.sheen);
+ }
+ if (mtl.cc_thickness >= 0.0f) {
+ fmt_handler_.write_mtl_float("Pc", mtl.cc_thickness);
+ }
+ if (mtl.cc_roughness >= 0.0f) {
+ fmt_handler_.write_mtl_float("Pcr", mtl.cc_roughness);
+ }
+ if (mtl.aniso >= 0.0f) {
+ fmt_handler_.write_mtl_float("aniso", mtl.aniso);
+ }
+ if (mtl.aniso_rot >= 0.0f) {
+ fmt_handler_.write_mtl_float("anisor", mtl.aniso_rot);
+ }
+ if (mtl.transmit_color.x > 0.0f || mtl.transmit_color.y > 0.0f ||
+ mtl.transmit_color.z > 0.0f) {
+ fmt_handler_.write_mtl_float3(
+ "Tf", mtl.transmit_color.x, mtl.transmit_color.y, mtl.transmit_color.z);
+ }
+ }
}
-void MTLWriter::write_texture_map(
- const MTLMaterial &mtl_material,
- const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map,
- const char *blen_filedir,
- const char *dest_dir,
- ePathReferenceMode path_mode,
- Set<std::pair<std::string, std::string>> &copy_set)
+void MTLWriter::write_texture_map(const MTLMaterial &mtl_material,
+ MTLTexMapType texture_key,
+ const MTLTexMap &texture_map,
+ const char *blen_filedir,
+ const char *dest_dir,
+ ePathReferenceMode path_mode,
+ Set<std::pair<std::string, std::string>> &copy_set)
{
std::string options;
/* Option strings should have their own leading spaces. */
- if (texture_map.value.translation != float3{0.0f, 0.0f, 0.0f}) {
- options.append(" -o ").append(float3_to_string(texture_map.value.translation));
+ if (texture_map.translation != float3{0.0f, 0.0f, 0.0f}) {
+ options.append(" -o ").append(float3_to_string(texture_map.translation));
}
- if (texture_map.value.scale != float3{1.0f, 1.0f, 1.0f}) {
- options.append(" -s ").append(float3_to_string(texture_map.value.scale));
+ if (texture_map.scale != float3{1.0f, 1.0f, 1.0f}) {
+ options.append(" -s ").append(float3_to_string(texture_map.scale));
}
- if (texture_map.key == eMTLSyntaxElement::map_Bump && mtl_material.map_Bump_strength > 0.0001f) {
- options.append(" -bm ").append(std::to_string(mtl_material.map_Bump_strength));
+ if (texture_key == MTLTexMapType::Normal && mtl_material.normal_strength > 0.0001f) {
+ options.append(" -bm ").append(std::to_string(mtl_material.normal_strength));
}
-#define SYNTAX_DISPATCH(eMTLSyntaxElement) \
- if (texture_map.key == eMTLSyntaxElement) { \
- std::string path = path_reference( \
- texture_map.value.image_path.c_str(), blen_filedir, dest_dir, path_mode, &copy_set); \
- /* Always emit forward slashes for cross-platform compatibility. */ \
- std::replace(path.begin(), path.end(), '\\', '/'); \
- fmt_handler_.write<eMTLSyntaxElement>(options, path.c_str()); \
- return; \
- }
+ std::string path = path_reference(
+ texture_map.image_path.c_str(), blen_filedir, dest_dir, path_mode, &copy_set);
+ /* Always emit forward slashes for cross-platform compatibility. */
+ std::replace(path.begin(), path.end(), '\\', '/');
+
+ fmt_handler_.write_mtl_map(tex_map_type_to_string[(int)texture_key], options, path);
+}
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Kd);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ks);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ns);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_d);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_refl);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Ke);
- SYNTAX_DISPATCH(eMTLSyntaxElement::map_Bump);
+static bool is_pbr_map(MTLTexMapType type)
+{
+ return type == MTLTexMapType::Metallic || type == MTLTexMapType::Roughness ||
+ type == MTLTexMapType::Sheen;
+}
- BLI_assert(!"This map type was not written to the file.");
+static bool is_non_pbr_map(MTLTexMapType type)
+{
+ return type == MTLTexMapType::SpecularExponent || type == MTLTexMapType::Reflection;
}
void MTLWriter::write_materials(const char *blen_filepath,
ePathReferenceMode path_mode,
- const char *dest_dir)
+ const char *dest_dir,
+ bool write_pbr)
{
if (mtlmaterials_.size() == 0) {
return;
@@ -615,14 +674,22 @@ void MTLWriter::write_materials(const char *blen_filepath,
[](const MTLMaterial &a, const MTLMaterial &b) { return a.name < b.name; });
Set<std::pair<std::string, std::string>> copy_set;
for (const MTLMaterial &mtlmat : mtlmaterials_) {
- fmt_handler_.write<eMTLSyntaxElement::string>("\n");
- fmt_handler_.write<eMTLSyntaxElement::newmtl>(mtlmat.name);
- write_bsdf_properties(mtlmat);
- for (const auto &tex : mtlmat.texture_maps.items()) {
- if (tex.value.image_path.empty()) {
+ fmt_handler_.write_string("");
+ fmt_handler_.write_mtl_newmtl(mtlmat.name);
+ write_bsdf_properties(mtlmat, write_pbr);
+ for (int key = 0; key < (int)MTLTexMapType::Count; key++) {
+ const MTLTexMap &tex = mtlmat.texture_maps[key];
+ if (!tex.is_valid()) {
+ continue;
+ }
+ if (!write_pbr && is_pbr_map((MTLTexMapType)key)) {
+ continue;
+ }
+ if (write_pbr && is_non_pbr_map((MTLTexMapType)key)) {
continue;
}
- write_texture_map(mtlmat, tex, blen_filedir, dest_dir, path_mode, copy_set);
+ write_texture_map(
+ mtlmat, (MTLTexMapType)key, tex, blen_filedir, dest_dir, path_mode, copy_set);
}
}
path_reference_copy(copy_set);
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
index 97c23484426..eda4576297b 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.hh
@@ -66,7 +66,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write object's name or group.
*/
- void write_object_name(FormatHandler<eFileType::OBJ> &fh, const OBJMesh &obj_mesh_data) const;
+ void write_object_name(FormatHandler &fh, const OBJMesh &obj_mesh_data) const;
/**
* Write file name of Material Library in .OBJ file.
*/
@@ -74,19 +74,19 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write vertex coordinates for all vertices as "v x y z" or "v x y z r g b".
*/
- void write_vertex_coords(FormatHandler<eFileType::OBJ> &fh,
+ void write_vertex_coords(FormatHandler &fh,
const OBJMesh &obj_mesh_data,
bool write_colors) const;
/**
* Write UV vertex coordinates for all vertices as `vt u v`.
* \note UV indices are stored here, but written with polygons later.
*/
- void write_uv_coords(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data) const;
+ void write_uv_coords(FormatHandler &fh, OBJMesh &obj_mesh_data) const;
/**
* Write loop normals for smooth-shaded polygons, and polygon normals otherwise, as "vn x y z".
* \note Normal indices ares stored here, but written with polygons later.
*/
- void write_poly_normals(FormatHandler<eFileType::OBJ> &fh, OBJMesh &obj_mesh_data);
+ void write_poly_normals(FormatHandler &fh, OBJMesh &obj_mesh_data);
/**
* Write polygon elements with at least vertex indices, and conditionally with UV vertex
* indices and polygon normal indices. Also write groups: smooth, vertex, material.
@@ -94,23 +94,23 @@ class OBJWriter : NonMovable, NonCopyable {
* name used in the .obj file.
* \note UV indices were stored while writing UV vertices.
*/
- void write_poly_elements(FormatHandler<eFileType::OBJ> &fh,
+ void write_poly_elements(FormatHandler &fh,
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data,
std::function<const char *(int)> matname_fn);
/**
* Write loose edges of a mesh as "l v1 v2".
*/
- void write_edges_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_edges_indices(FormatHandler &fh,
const IndexOffsets &offsets,
const OBJMesh &obj_mesh_data) const;
/**
* Write a NURBS curve to the .OBJ file in parameter form.
*/
- void write_nurbs_curve(FormatHandler<eFileType::OBJ> &fh, const OBJCurve &obj_nurbs_data) const;
+ void write_nurbs_curve(FormatHandler &fh, const OBJCurve &obj_nurbs_data) const;
private:
- using func_vert_uv_normal_indices = void (OBJWriter::*)(FormatHandler<eFileType::OBJ> &fh,
+ using func_vert_uv_normal_indices = void (OBJWriter::*)(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -124,7 +124,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1/vt1/vn1 v2/vt2/vn2 ...".
*/
- void write_vert_uv_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_vert_uv_normal_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -133,7 +133,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1//vn1 v2//vn2 ...".
*/
- void write_vert_normal_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_vert_normal_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> /*uv_indices*/,
@@ -142,7 +142,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1/vt1 v2/vt2 ...".
*/
- void write_vert_uv_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_vert_uv_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> uv_indices,
@@ -151,7 +151,7 @@ class OBJWriter : NonMovable, NonCopyable {
/**
* Write one line of polygon indices as "f v1 v2 ...".
*/
- void write_vert_indices(FormatHandler<eFileType::OBJ> &fh,
+ void write_vert_indices(FormatHandler &fh,
const IndexOffsets &offsets,
Span<int> vert_indices,
Span<int> /*uv_indices*/,
@@ -164,7 +164,7 @@ class OBJWriter : NonMovable, NonCopyable {
*/
class MTLWriter : NonMovable, NonCopyable {
private:
- FormatHandler<eFileType::MTL> fmt_handler_;
+ FormatHandler fmt_handler_;
FILE *outfile_;
std::string mtl_filepath_;
Vector<MTLMaterial> mtlmaterials_;
@@ -186,7 +186,8 @@ class MTLWriter : NonMovable, NonCopyable {
*/
void write_materials(const char *blen_filepath,
ePathReferenceMode path_mode,
- const char *dest_dir);
+ const char *dest_dir,
+ bool write_pbr);
StringRefNull mtl_file_path() const;
/**
* Add the materials of the given object to #MTLWriter, de-duplicating
@@ -203,12 +204,13 @@ class MTLWriter : NonMovable, NonCopyable {
/**
* Write properties sourced from p-BSDF node or #Object.Material.
*/
- void write_bsdf_properties(const MTLMaterial &mtl_material);
+ void write_bsdf_properties(const MTLMaterial &mtl_material, bool write_pbr);
/**
* Write a texture map in the form "map_XX -s 1. 1. 1. -o 0. 0. 0. [-bm 1.] path/to/image".
*/
void write_texture_map(const MTLMaterial &mtl_material,
- const Map<const eMTLSyntaxElement, tex_map_XX>::Item &texture_map,
+ MTLTexMapType texture_key,
+ const MTLTexMap &texture_map,
const char *blen_filedir,
const char *dest_dir,
ePathReferenceMode mode,
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
index 5413c9969e3..59ee7bd32c0 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_io.hh
@@ -23,268 +23,24 @@
namespace blender::io::obj {
-enum class eFileType {
- OBJ,
- MTL,
-};
-
-enum class eOBJSyntaxElement {
- vertex_coords,
- vertex_coords_color,
- uv_vertex_coords,
- normal,
- poly_element_begin,
- vertex_uv_normal_indices,
- vertex_normal_indices,
- vertex_uv_indices,
- vertex_indices,
- poly_element_end,
- poly_usemtl,
- edge,
- cstype,
- nurbs_degree,
- curve_element_begin,
- curve_element_end,
- nurbs_parameter_begin,
- nurbs_parameters,
- nurbs_parameter_end,
- nurbs_group_end,
- new_line,
- mtllib,
- smooth_group,
- object_group,
- object_name,
- /* Use rarely. New line is NOT included for string. */
- string,
-};
-
-enum class eMTLSyntaxElement {
- newmtl,
- Ni,
- d,
- Ns,
- illum,
- Ka,
- Kd,
- Ks,
- Ke,
- map_Kd,
- map_Ks,
- map_Ns,
- map_d,
- map_refl,
- map_Ke,
- map_Bump,
- /* Use rarely. New line is NOT included for string. */
- string,
-};
-
-template<eFileType filetype> struct FileTypeTraits;
-
-/* Used to prevent mixing of say OBJ file format with MTL syntax elements. */
-template<> struct FileTypeTraits<eFileType::OBJ> {
- using SyntaxType = eOBJSyntaxElement;
-};
-
-template<> struct FileTypeTraits<eFileType::MTL> {
- using SyntaxType = eMTLSyntaxElement;
-};
-
-struct FormattingSyntax {
- /* Formatting syntax with the file format key like `newmtl %s\n`. */
- const char *fmt = nullptr;
- /* Number of arguments needed by the syntax. */
- const int total_args = 0;
- /* Whether types of the given arguments are accepted by the syntax above. Fail to compile by
- * default.
- */
- const bool are_types_valid = false;
-};
-
-/**
- * Type dependent but always false. Use to add a `constexpr` conditional compile-time error.
- */
-template<typename T> struct always_false : std::false_type {
-};
-
-template<typename... T>
-constexpr bool is_type_float = (... && std::is_floating_point_v<std::decay_t<T>>);
-
-template<typename... T>
-constexpr bool is_type_integral = (... && std::is_integral_v<std::decay_t<T>>);
-
-template<typename... T>
-constexpr bool is_type_string_related = (... && std::is_constructible_v<std::string, T>);
-
-/* GCC (at least 9.3) while compiling the obj_exporter_tests.cc with optimizations on,
- * results in "obj_export_io.hh:205:18: warning: ‘%s’ directive output truncated writing 34 bytes
- * into a region of size 6" and similar warnings. Yes the output is truncated, and that is covered
- * as an edge case by tests on purpose. */
-#if defined(__GNUC__) && !defined(__clang__)
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wformat-truncation"
-#endif
-template<typename... T>
-constexpr FormattingSyntax syntax_elem_to_formatting(const eOBJSyntaxElement key)
-{
- switch (key) {
- case eOBJSyntaxElement::vertex_coords: {
- return {"v {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- case eOBJSyntaxElement::vertex_coords_color: {
- return {"v {:.6f} {:.6f} {:.6f} {:.4f} {:.4f} {:.4f}\n", 6, is_type_float<T...>};
- }
- case eOBJSyntaxElement::uv_vertex_coords: {
- return {"vt {:.6f} {:.6f}\n", 2, is_type_float<T...>};
- }
- case eOBJSyntaxElement::normal: {
- return {"vn {:.4f} {:.4f} {:.4f}\n", 3, is_type_float<T...>};
- }
- case eOBJSyntaxElement::poly_element_begin: {
- return {"f", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::vertex_uv_normal_indices: {
- return {" {}/{}/{}", 3, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::vertex_normal_indices: {
- return {" {}//{}", 2, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::vertex_uv_indices: {
- return {" {}/{}", 2, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::vertex_indices: {
- return {" {}", 1, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::poly_usemtl: {
- return {"usemtl {}\n", 1, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::edge: {
- return {"l {} {}\n", 2, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::cstype: {
- return {"cstype bspline\n", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::nurbs_degree: {
- return {"deg {}\n", 1, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::curve_element_begin: {
- return {"curv 0.0 1.0", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::nurbs_parameter_begin: {
- return {"parm u 0.0", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::nurbs_parameters: {
- return {" {:.6f}", 1, is_type_float<T...>};
- }
- case eOBJSyntaxElement::nurbs_parameter_end: {
- return {" 1.0\n", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::nurbs_group_end: {
- return {"end\n", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::poly_element_end: {
- ATTR_FALLTHROUGH;
- }
- case eOBJSyntaxElement::curve_element_end: {
- ATTR_FALLTHROUGH;
- }
- case eOBJSyntaxElement::new_line: {
- return {"\n", 0, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::mtllib: {
- return {"mtllib {}\n", 1, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::smooth_group: {
- return {"s {}\n", 1, is_type_integral<T...>};
- }
- case eOBJSyntaxElement::object_group: {
- return {"g {}\n", 1, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::object_name: {
- return {"o {}\n", 1, is_type_string_related<T...>};
- }
- case eOBJSyntaxElement::string: {
- return {"{}", 1, is_type_string_related<T...>};
- }
- }
-}
-
-template<typename... T>
-constexpr FormattingSyntax syntax_elem_to_formatting(const eMTLSyntaxElement key)
-{
- switch (key) {
- case eMTLSyntaxElement::newmtl: {
- return {"newmtl {}\n", 1, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::Ni: {
- return {"Ni {:.6f}\n", 1, is_type_float<T...>};
- }
- case eMTLSyntaxElement::d: {
- return {"d {:.6f}\n", 1, is_type_float<T...>};
- }
- case eMTLSyntaxElement::Ns: {
- return {"Ns {:.6f}\n", 1, is_type_float<T...>};
- }
- case eMTLSyntaxElement::illum: {
- return {"illum {}\n", 1, is_type_integral<T...>};
- }
- case eMTLSyntaxElement::Ka: {
- return {"Ka {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- case eMTLSyntaxElement::Kd: {
- return {"Kd {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- case eMTLSyntaxElement::Ks: {
- return {"Ks {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- case eMTLSyntaxElement::Ke: {
- return {"Ke {:.6f} {:.6f} {:.6f}\n", 3, is_type_float<T...>};
- }
- /* NOTE: first texture map related argument, if present, will have its own leading space. */
- case eMTLSyntaxElement::map_Kd: {
- return {"map_Kd{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_Ks: {
- return {"map_Ks{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_Ns: {
- return {"map_Ns{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_d: {
- return {"map_d{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_refl: {
- return {"map_refl{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_Ke: {
- return {"map_Ke{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::map_Bump: {
- return {"map_Bump{} {}\n", 2, is_type_string_related<T...>};
- }
- case eMTLSyntaxElement::string: {
- return {"{}", 1, is_type_string_related<T...>};
- }
- }
-}
-#if defined(__GNUC__) && !defined(__clang__)
-# pragma GCC diagnostic pop
-#endif
-
/**
- * File format and syntax agnostic file buffer writer.
+ * File buffer writer.
* All writes are done into an internal chunked memory buffer
* (list of default 64 kilobyte blocks).
* Call write_fo_file once in a while to write the memory buffer(s)
* into the given file.
*/
-template<eFileType filetype, size_t buffer_chunk_size = 64 * 1024>
class FormatHandler : NonCopyable, NonMovable {
private:
typedef std::vector<char> VectorChar;
std::vector<VectorChar> blocks_;
+ size_t buffer_chunk_size_;
public:
+ FormatHandler(size_t buffer_chunk_size = 64 * 1024) : buffer_chunk_size_(buffer_chunk_size)
+ {
+ }
+
/* Write contents to the buffer(s) into a file, and clear the buffers. */
void write_to_file(FILE *f)
{
@@ -305,7 +61,7 @@ class FormatHandler : NonCopyable, NonMovable {
return blocks_.size();
}
- void append_from(FormatHandler<filetype, buffer_chunk_size> &v)
+ void append_from(FormatHandler &v)
{
blocks_.insert(blocks_.end(),
std::make_move_iterator(v.blocks_.begin()),
@@ -313,24 +69,132 @@ class FormatHandler : NonCopyable, NonMovable {
v.blocks_.clear();
}
- /**
- * Example invocation: `writer->write<eMTLSyntaxElement::newmtl>("foo")`.
- *
- * \param key: Must match what the instance's filetype expects; i.e., `eMTLSyntaxElement` for
- * `eFileType::MTL`.
- */
- template<typename FileTypeTraits<filetype>::SyntaxType key, typename... T>
- constexpr void write(T &&...args)
- {
- /* Get format syntax, number of arguments expected and whether types of given arguments are
- * valid.
- */
- constexpr FormattingSyntax fmt_nargs_valid = syntax_elem_to_formatting<T...>(key);
- BLI_STATIC_ASSERT(fmt_nargs_valid.are_types_valid &&
- (sizeof...(T) == fmt_nargs_valid.total_args),
- "Types of all arguments and the number of arguments should match what the "
- "formatting specifies.");
- write_impl(fmt_nargs_valid.fmt, std::forward<T>(args)...);
+ void write_obj_vertex(float x, float y, float z)
+ {
+ write_impl("v {:.6f} {:.6f} {:.6f}\n", x, y, z);
+ }
+ void write_obj_vertex_color(float x, float y, float z, float r, float g, float b)
+ {
+ write_impl("v {:.6f} {:.6f} {:.6f} {:.4f} {:.4f} {:.4f}\n", x, y, z, r, g, b);
+ }
+ void write_obj_uv(float x, float y)
+ {
+ write_impl("vt {:.6f} {:.6f}\n", x, y);
+ }
+ void write_obj_normal(float x, float y, float z)
+ {
+ write_impl("vn {:.4f} {:.4f} {:.4f}\n", x, y, z);
+ }
+ void write_obj_poly_begin()
+ {
+ write_impl("f");
+ }
+ void write_obj_poly_end()
+ {
+ write_obj_newline();
+ }
+ void write_obj_poly_v_uv_normal(int v, int uv, int n)
+ {
+ write_impl(" {}/{}/{}", v, uv, n);
+ }
+ void write_obj_poly_v_normal(int v, int n)
+ {
+ write_impl(" {}//{}", v, n);
+ }
+ void write_obj_poly_v_uv(int v, int uv)
+ {
+ write_impl(" {}/{}", v, uv);
+ }
+ void write_obj_poly_v(int v)
+ {
+ write_impl(" {}", v);
+ }
+ void write_obj_usemtl(StringRef s)
+ {
+ write_impl("usemtl {}\n", s);
+ }
+ void write_obj_mtllib(StringRef s)
+ {
+ write_impl("mtllib {}\n", s);
+ }
+ void write_obj_smooth(int s)
+ {
+ write_impl("s {}\n", s);
+ }
+ void write_obj_group(StringRef s)
+ {
+ write_impl("g {}\n", s);
+ }
+ void write_obj_object(StringRef s)
+ {
+ write_impl("o {}\n", s);
+ }
+ void write_obj_edge(int a, int b)
+ {
+ write_impl("l {} {}\n", a, b);
+ }
+ void write_obj_cstype()
+ {
+ write_impl("cstype bspline\n");
+ }
+ void write_obj_nurbs_degree(int deg)
+ {
+ write_impl("deg {}\n", deg);
+ }
+ void write_obj_curve_begin()
+ {
+ write_impl("curv 0.0 1.0");
+ }
+ void write_obj_curve_end()
+ {
+ write_obj_newline();
+ }
+ void write_obj_nurbs_parm_begin()
+ {
+ write_impl("parm u 0.0");
+ }
+ void write_obj_nurbs_parm(float v)
+ {
+ write_impl(" {:.6f}", v);
+ }
+ void write_obj_nurbs_parm_end()
+ {
+ write_impl(" 1.0\n");
+ }
+ void write_obj_nurbs_group_end()
+ {
+ write_impl("end\n");
+ }
+ void write_obj_newline()
+ {
+ write_impl("\n");
+ }
+
+ void write_mtl_newmtl(StringRef s)
+ {
+ write_impl("newmtl {}\n", s);
+ }
+ void write_mtl_float(const char *type, float v)
+ {
+ write_impl("{} {:.6f}\n", type, v);
+ }
+ void write_mtl_float3(const char *type, float r, float g, float b)
+ {
+ write_impl("{} {:.6f} {:.6f} {:.6f}\n", type, r, g, b);
+ }
+ void write_mtl_illum(int mode)
+ {
+ write_impl("illum {}\n", mode);
+ }
+ /* NOTE: options, if present, will have its own leading space. */
+ void write_mtl_map(const char *type, StringRef options, StringRef value)
+ {
+ write_impl("{}{} {}\n", type, options, value);
+ }
+
+ void write_string(StringRef s)
+ {
+ write_impl("{}\n", s);
}
private:
@@ -340,7 +204,7 @@ class FormatHandler : NonCopyable, NonMovable {
{
if (blocks_.empty() || (blocks_.back().capacity() - blocks_.back().size() < at_least)) {
VectorChar &b = blocks_.emplace_back(VectorChar());
- b.reserve(std::max(at_least, buffer_chunk_size));
+ b.reserve(std::max(at_least, buffer_chunk_size_));
}
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
index e2ecda32717..10880b016fb 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc
@@ -6,6 +6,7 @@
/* Silence warnings from copying deprecated fields. Needed for an Object copy constructor use. */
#define DNA_DEPRECATED_ALLOW
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_lib_id.h"
@@ -47,7 +48,7 @@ OBJMesh::OBJMesh(Depsgraph *depsgraph, const OBJExportParams &export_params, Obj
/* Since a new mesh been allocated, it needs to be freed in the destructor. */
mesh_eval_needs_free_ = true;
}
- if (export_params.export_triangulated_mesh && ELEM(export_object_eval_.type, OB_MESH, OB_SURF)) {
+ if (export_params.export_triangulated_mesh && export_object_eval_.type == OB_MESH) {
std::tie(export_mesh_eval_, mesh_eval_needs_free_) = triangulate_mesh_eval();
}
set_world_axes_transform(export_params.forward_axis, export_params.up_axis);
@@ -133,7 +134,7 @@ void OBJMesh::set_world_axes_transform(const eIOAxis forward, const eIOAxis up)
copy_m3_m4(normal_matrix, world_and_axes_transform_);
invert_m3_m3(world_and_axes_normal_transform_, normal_matrix);
transpose_m3(world_and_axes_normal_transform_);
- mirrored_transform_ = determinant_m3_array(world_and_axes_normal_transform_) < 0;
+ mirrored_transform_ = is_negative_m3(world_and_axes_normal_transform_);
}
int OBJMesh::tot_vertices() const
@@ -187,28 +188,38 @@ void OBJMesh::ensure_mesh_edges() const
void OBJMesh::calc_smooth_groups(const bool use_bitflags)
{
- poly_smooth_groups_ = BKE_mesh_calc_smoothgroups(export_mesh_eval_->medge,
- export_mesh_eval_->totedge,
- export_mesh_eval_->mpoly,
- export_mesh_eval_->totpoly,
- export_mesh_eval_->mloop,
- export_mesh_eval_->totloop,
+ const Span<MEdge> edges = export_mesh_eval_->edges();
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
+ poly_smooth_groups_ = BKE_mesh_calc_smoothgroups(edges.data(),
+ edges.size(),
+ polys.data(),
+ polys.size(),
+ loops.data(),
+ loops.size(),
&tot_smooth_groups_,
use_bitflags);
}
void OBJMesh::calc_poly_order()
{
- const int tot_polys = tot_polygons();
- poly_order_.resize(tot_polys);
- for (int i = 0; i < tot_polys; ++i) {
+ const bke::AttributeAccessor attributes = export_mesh_eval_->attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ if (material_indices.is_single() && material_indices.get_internal_single() == 0) {
+ return;
+ }
+ const VArraySpan<int> material_indices_span(material_indices);
+
+ poly_order_.resize(material_indices_span.size());
+ for (const int i : material_indices_span.index_range()) {
poly_order_[i] = i;
}
- const MPoly *mpolys = export_mesh_eval_->mpoly;
+
/* Sort polygons by their material index. */
blender::parallel_sort(poly_order_.begin(), poly_order_.end(), [&](int a, int b) {
- int mat_a = mpolys[a].mat_nr;
- int mat_b = mpolys[b].mat_nr;
+ int mat_a = material_indices_span[a];
+ int mat_b = material_indices_span[b];
if (mat_a != mat_b) {
return mat_a < mat_b;
}
@@ -219,26 +230,20 @@ void OBJMesh::calc_poly_order()
const Material *OBJMesh::get_object_material(const int16_t mat_nr) const
{
/**
- * The const_cast is safe here because BKE_object_material_get won't change the object
+ * The const_cast is safe here because #BKE_object_material_get_eval won't change the object
* but it is a big can of worms to fix the declaration of that function right now.
*
* The call uses "+ 1" as material getter needs one-based indices.
*/
Object *obj = const_cast<Object *>(&export_object_eval_);
- const Material *r_mat = BKE_object_material_get(obj, mat_nr + 1);
+ const Material *r_mat = BKE_object_material_get_eval(obj, mat_nr + 1);
return r_mat;
}
bool OBJMesh::is_ith_poly_smooth(const int poly_index) const
{
- return export_mesh_eval_->mpoly[poly_index].flag & ME_SMOOTH;
-}
-
-int16_t OBJMesh::ith_poly_matnr(const int poly_index) const
-{
- BLI_assert(poly_index < export_mesh_eval_->totpoly);
- const int16_t r_mat_nr = export_mesh_eval_->mpoly[poly_index].mat_nr;
- return r_mat_nr >= 0 ? r_mat_nr : NOT_FOUND;
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ return polys[poly_index].flag & ME_SMOOTH;
}
const char *OBJMesh::get_object_name() const
@@ -263,7 +268,8 @@ const char *OBJMesh::get_object_material_name(const int16_t mat_nr) const
float3 OBJMesh::calc_vertex_coords(const int vert_index, const float scaling_factor) const
{
float3 r_coords;
- copy_v3_v3(r_coords, export_mesh_eval_->mvert[vert_index].co);
+ const Span<MVert> verts = export_mesh_eval_->verts();
+ copy_v3_v3(r_coords, verts[vert_index].co);
mul_m4_v3(world_and_axes_transform_, r_coords);
mul_v3_fl(r_coords, scaling_factor);
return r_coords;
@@ -271,8 +277,10 @@ float3 OBJMesh::calc_vertex_coords(const int vert_index, const float scaling_fac
Vector<int> OBJMesh::calc_poly_vertex_indices(const int poly_index) const
{
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
- const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
+ const MPoly &mpoly = polys[poly_index];
+ const MLoop *mloop = &loops[mpoly.loopstart];
const int totloop = mpoly.totloop;
Vector<int> r_poly_vertex_indices(totloop);
for (int loop_index = 0; loop_index < totloop; loop_index++) {
@@ -283,9 +291,8 @@ Vector<int> OBJMesh::calc_poly_vertex_indices(const int poly_index) const
void OBJMesh::store_uv_coords_and_indices()
{
- const MPoly *mpoly = export_mesh_eval_->mpoly;
- const MLoop *mloop = export_mesh_eval_->mloop;
- const int totpoly = export_mesh_eval_->totpoly;
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
const int totvert = export_mesh_eval_->totvert;
const MLoopUV *mloopuv = static_cast<const MLoopUV *>(
CustomData_get_layer(&export_mesh_eval_->ldata, CD_MLOOPUV));
@@ -296,9 +303,9 @@ void OBJMesh::store_uv_coords_and_indices()
const float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT};
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
- mpoly, mloop, mloopuv, totpoly, totvert, limit, false, false);
+ polys.data(), nullptr, loops.data(), mloopuv, polys.size(), totvert, limit, false, false);
- uv_indices_.resize(totpoly);
+ uv_indices_.resize(polys.size());
/* At least total vertices of a mesh will be present in its texture map. So
* reserve minimum space early. */
uv_coords_.reserve(totvert);
@@ -310,16 +317,16 @@ void OBJMesh::store_uv_coords_and_indices()
if (uv_vert->separate) {
tot_uv_vertices_ += 1;
}
- const int vertices_in_poly = mpoly[uv_vert->poly_index].totloop;
+ const int verts_in_poly = polys[uv_vert->poly_index].totloop;
/* Store UV vertex coordinates. */
uv_coords_.resize(tot_uv_vertices_);
- const int loopstart = mpoly[uv_vert->poly_index].loopstart;
+ const int loopstart = polys[uv_vert->poly_index].loopstart;
Span<float> vert_uv_coords(mloopuv[loopstart + uv_vert->loop_of_poly_index].uv, 2);
uv_coords_[tot_uv_vertices_ - 1] = float2(vert_uv_coords[0], vert_uv_coords[1]);
/* Store UV vertex indices. */
- uv_indices_[uv_vert->poly_index].resize(vertices_in_poly);
+ uv_indices_[uv_vert->poly_index].resize(verts_in_poly);
/* Keep indices zero-based and let the writer handle the "+ 1" as per OBJ spec. */
uv_indices_[uv_vert->poly_index][uv_vert->loop_of_poly_index] = tot_uv_vertices_ - 1;
}
@@ -340,10 +347,11 @@ Span<int> OBJMesh::calc_poly_uv_indices(const int poly_index) const
float3 OBJMesh::calc_poly_normal(const int poly_index) const
{
float3 r_poly_normal;
- const MPoly &poly = export_mesh_eval_->mpoly[poly_index];
- const MLoop &mloop = export_mesh_eval_->mloop[poly.loopstart];
- const MVert &mvert = *(export_mesh_eval_->mvert);
- BKE_mesh_calc_poly_normal(&poly, &mloop, &mvert, r_poly_normal);
+ const Span<MVert> verts = export_mesh_eval_->verts();
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
+ const MPoly &poly = polys[poly_index];
+ BKE_mesh_calc_poly_normal(&poly, &loops[poly.loopstart], verts.data(), r_poly_normal);
mul_m3_v3(world_and_axes_normal_transform_, r_poly_normal);
normalize_v3(r_poly_normal);
return r_poly_normal;
@@ -367,6 +375,8 @@ static float3 round_float3_to_n_digits(const float3 &v, int round_digits)
void OBJMesh::store_normal_coords_and_indices()
{
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+
/* We'll round normal components to 4 digits.
* This will cover up some minor differences
* between floating point calculations on different platforms.
@@ -382,7 +392,7 @@ void OBJMesh::store_normal_coords_and_indices()
const float(*lnors)[3] = static_cast<const float(*)[3]>(
CustomData_get_layer(&export_mesh_eval_->ldata, CD_NORMAL));
for (int poly_index = 0; poly_index < export_mesh_eval_->totpoly; ++poly_index) {
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
+ const MPoly &mpoly = polys[poly_index];
bool need_per_loop_normals = lnors != nullptr || (mpoly.flag & ME_SMOOTH);
if (need_per_loop_normals) {
for (int loop_of_poly = 0; loop_of_poly < mpoly.totloop; ++loop_of_poly) {
@@ -426,7 +436,8 @@ Vector<int> OBJMesh::calc_poly_normal_indices(const int poly_index) const
if (loop_to_normal_index_.is_empty()) {
return {};
}
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const MPoly &mpoly = polys[poly_index];
const int totloop = mpoly.totloop;
Vector<int> r_poly_normal_indices(totloop);
for (int poly_loop_index = 0; poly_loop_index < totloop; poly_loop_index++) {
@@ -449,23 +460,23 @@ int16_t OBJMesh::get_poly_deform_group_index(const int poly_index,
{
BLI_assert(poly_index < export_mesh_eval_->totpoly);
BLI_assert(group_weights.size() == BKE_object_defgroup_count(&export_object_eval_));
-
- const MDeformVert *dvert_layer = static_cast<const MDeformVert *>(
- CustomData_get_layer(&export_mesh_eval_->vdata, CD_MDEFORMVERT));
- if (!dvert_layer) {
+ const Span<MPoly> polys = export_mesh_eval_->polys();
+ const Span<MLoop> loops = export_mesh_eval_->loops();
+ const Span<MDeformVert> dverts = export_mesh_eval_->deform_verts();
+ if (dverts.is_empty()) {
return NOT_FOUND;
}
group_weights.fill(0);
bool found_any_group = false;
- const MPoly &mpoly = export_mesh_eval_->mpoly[poly_index];
- const MLoop *mloop = &export_mesh_eval_->mloop[mpoly.loopstart];
+ const MPoly &mpoly = polys[poly_index];
+ const MLoop *mloop = &loops[mpoly.loopstart];
for (int loop_i = 0; loop_i < mpoly.totloop; ++loop_i, ++mloop) {
- const MDeformVert &dvert = dvert_layer[mloop->v];
- for (int weight_i = 0; weight_i < dvert.totweight; ++weight_i) {
- const auto group = dvert.dw[weight_i].def_nr;
+ const MDeformVert &dv = dverts[mloop->v];
+ for (int weight_i = 0; weight_i < dv.totweight; ++weight_i) {
+ const auto group = dv.dw[weight_i].def_nr;
if (group < group_weights.size()) {
- group_weights[group] += dvert.dw[weight_i].weight;
+ group_weights[group] += dv.dw[weight_i].weight;
found_any_group = true;
}
}
@@ -489,7 +500,8 @@ const char *OBJMesh::get_poly_deform_group_name(const int16_t def_group_index) c
std::optional<std::array<int, 2>> OBJMesh::calc_loose_edge_vert_indices(const int edge_index) const
{
- const MEdge &edge = export_mesh_eval_->medge[edge_index];
+ const Span<MEdge> edges = export_mesh_eval_->edges();
+ const MEdge &edge = edges[edge_index];
if (edge.flag & ME_LOOSEEDGE) {
return std::array<int, 2>{static_cast<int>(edge.v1), static_cast<int>(edge.v2)};
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
index ee2e6227700..db29f5651ed 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh
@@ -130,11 +130,6 @@ class OBJMesh : NonCopyable {
* Return mat_nr-th material of the object. The given index should be zero-based.
*/
const Material *get_object_material(int16_t mat_nr) const;
- /**
- * Returns a zero-based index of a polygon's material indexing into
- * the Object's material slots.
- */
- int16_t ith_poly_matnr(int poly_index) const;
void ensure_mesh_normals() const;
void ensure_mesh_edges() const;
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
index 4ed148ec64e..f8c7da75a70 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.cc
@@ -6,6 +6,7 @@
#include "BKE_image.h"
#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
#include "BLI_map.hh"
#include "BLI_math_vector.h"
@@ -15,13 +16,26 @@
#include "DNA_material_types.h"
#include "DNA_node_types.h"
-#include "NOD_node_tree_ref.hh"
-
#include "obj_export_mesh.hh"
#include "obj_export_mtl.hh"
namespace blender::io::obj {
+const char *tex_map_type_to_socket_id[] = {
+ "Base Color",
+ "Metallic",
+ "Specular",
+ "Roughness", /* Map specular exponent to roughness. */
+ "Roughness",
+ "Sheen",
+ "Metallic", /* Map reflection to metallic. */
+ "Emission",
+ "Alpha",
+ "Normal",
+};
+BLI_STATIC_ASSERT(ARRAY_SIZE(tex_map_type_to_socket_id) == (int)MTLTexMapType::Count,
+ "array size mismatch");
+
/**
* Copy a float property of the given type from the bNode to given buffer.
*/
@@ -72,25 +86,25 @@ static void copy_property_from_node(const eNodeSocketDatatype property_type,
* Collect all the source sockets linked to the destination socket in a destination node.
*/
static void linked_sockets_to_dest_id(const bNode *dest_node,
- const nodes::NodeTreeRef &node_tree,
- StringRefNull dest_socket_id,
- Vector<const nodes::OutputSocketRef *> &r_linked_sockets)
+ const bNodeTree &node_tree,
+ const char *dest_socket_id,
+ Vector<const bNodeSocket *> &r_linked_sockets)
{
r_linked_sockets.clear();
if (!dest_node) {
return;
}
- Span<const nodes::NodeRef *> object_dest_nodes = node_tree.nodes_by_type(dest_node->idname);
- Span<const nodes::InputSocketRef *> dest_inputs = object_dest_nodes.first()->inputs();
- const nodes::InputSocketRef *dest_socket = nullptr;
- for (const nodes::InputSocketRef *curr_socket : dest_inputs) {
- if (STREQ(curr_socket->bsocket()->identifier, dest_socket_id.c_str())) {
+ Span<const bNode *> object_dest_nodes = node_tree.nodes_by_type(dest_node->idname);
+ Span<const bNodeSocket *> dest_inputs = object_dest_nodes.first()->input_sockets();
+ const bNodeSocket *dest_socket = nullptr;
+ for (const bNodeSocket *curr_socket : dest_inputs) {
+ if (STREQ(curr_socket->identifier, dest_socket_id)) {
dest_socket = curr_socket;
break;
}
}
if (dest_socket) {
- Span<const nodes::OutputSocketRef *> linked_sockets = dest_socket->directly_linked_sockets();
+ Span<const bNodeSocket *> linked_sockets = dest_socket->directly_linked_sockets();
r_linked_sockets.resize(linked_sockets.size());
r_linked_sockets = linked_sockets;
}
@@ -99,40 +113,52 @@ static void linked_sockets_to_dest_id(const bNode *dest_node,
/**
* From a list of sockets, get the parent node which is of the given node type.
*/
-static const bNode *get_node_of_type(Span<const nodes::OutputSocketRef *> sockets_list,
- const int node_type)
+static const bNode *get_node_of_type(Span<const bNodeSocket *> sockets_list, const int node_type)
{
- for (const nodes::SocketRef *socket : sockets_list) {
- const bNode *parent_node = socket->bnode();
- if (parent_node->typeinfo->type == node_type) {
- return parent_node;
+ for (const bNodeSocket *socket : sockets_list) {
+ const bNode &parent_node = socket->owner_node();
+ if (parent_node.typeinfo->type == node_type) {
+ return &parent_node;
}
}
return nullptr;
}
-/**
+/*
* From a texture image shader node, get the image's filepath.
* If packed image is found, only the file "name" is returned.
*/
-static const char *get_image_filepath(const bNode *tex_node)
+static std::string get_image_filepath(const bNode *tex_node)
{
if (!tex_node) {
- return nullptr;
+ return "";
}
Image *tex_image = reinterpret_cast<Image *>(tex_node->id);
if (!tex_image || !BKE_image_has_filepath(tex_image)) {
- return nullptr;
+ return "";
}
- const char *path = tex_image->filepath;
+
if (BKE_image_has_packedfile(tex_image)) {
/* Put image in the same directory as the .MTL file. */
- path = BLI_path_slash_rfind(path) + 1;
+ const char *filename = BLI_path_slash_rfind(tex_image->filepath) + 1;
fprintf(stderr,
"Packed image found:'%s'. Unpack and place the image in the same "
"directory as the .MTL file.\n",
- path);
+ filename);
+ return filename;
}
+
+ char path[FILE_MAX];
+ BLI_strncpy(path, tex_image->filepath, FILE_MAX);
+
+ if (tex_image->source == IMA_SRC_SEQUENCE) {
+ char head[FILE_MAX], tail[FILE_MAX];
+ unsigned short numlen;
+ int framenr = static_cast<NodeTexImage *>(tex_node->storage)->iuser.framenr;
+ BLI_path_sequence_decode(path, head, tail, &numlen);
+ BLI_path_sequence_encode(path, head, tail, numlen, framenr);
+ }
+
return path;
}
@@ -141,16 +167,16 @@ static const char *get_image_filepath(const bNode *tex_node)
* We only want one that feeds directly into a Material Output node
* (that is the behavior of the legacy Python exporter).
*/
-static const nodes::NodeRef *find_bsdf_node(const nodes::NodeTreeRef *nodetree)
+static const bNode *find_bsdf_node(const bNodeTree *nodetree)
{
if (!nodetree) {
return nullptr;
}
- for (const nodes::NodeRef *node : nodetree->nodes_by_type("ShaderNodeOutputMaterial")) {
- const nodes::InputSocketRef *node_input_socket0 = node->inputs()[0];
- for (const nodes::OutputSocketRef *out_sock : node_input_socket0->directly_linked_sockets()) {
- const nodes::NodeRef &in_node = out_sock->node();
- if (in_node.typeinfo()->type == SH_NODE_BSDF_PRINCIPLED) {
+ for (const bNode *node : nodetree->nodes_by_type("ShaderNodeOutputMaterial")) {
+ const bNodeSocket &node_input_socket0 = node->input_socket(0);
+ for (const bNodeSocket *out_sock : node_input_socket0.directly_linked_sockets()) {
+ const bNode &in_node = out_sock->owner_node();
+ if (in_node.typeinfo->type == SH_NODE_BSDF_PRINCIPLED) {
return &in_node;
}
}
@@ -161,58 +187,68 @@ static const nodes::NodeRef *find_bsdf_node(const nodes::NodeTreeRef *nodetree)
/**
* Store properties found either in bNode or material into r_mtl_mat.
*/
-static void store_bsdf_properties(const nodes::NodeRef *bsdf_node,
+static void store_bsdf_properties(const bNode *bsdf_node,
const Material *material,
MTLMaterial &r_mtl_mat)
{
- const bNode *bnode = nullptr;
- if (bsdf_node) {
- bnode = bsdf_node->bnode();
- }
-
- /* If p-BSDF is not present, fallback to #Object.Material. */
float roughness = material->roughness;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Roughness", {&roughness, 1});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Roughness", {&roughness, 1});
}
/* Empirical approximation. Importer should use the inverse of this method. */
float spec_exponent = (1.0f - roughness);
spec_exponent *= spec_exponent * 1000.0f;
float specular = material->spec;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Specular", {&specular, 1});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Specular", {&specular, 1});
}
float metallic = material->metallic;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Metallic", {&metallic, 1});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Metallic", {&metallic, 1});
}
float refraction_index = 1.0f;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "IOR", {&refraction_index, 1});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "IOR", {&refraction_index, 1});
}
- float dissolved = material->a;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Alpha", {&dissolved, 1});
+ float alpha = material->a;
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Alpha", {&alpha, 1});
}
- const bool transparent = dissolved != 1.0f;
+ const bool transparent = alpha != 1.0f;
float3 diffuse_col = {material->r, material->g, material->b};
- if (bnode) {
- copy_property_from_node(SOCK_RGBA, bnode, "Base Color", {diffuse_col, 3});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_RGBA, bsdf_node, "Base Color", {diffuse_col, 3});
}
float3 emission_col{0.0f};
float emission_strength = 0.0f;
- if (bnode) {
- copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1});
- copy_property_from_node(SOCK_RGBA, bnode, "Emission", {emission_col, 3});
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Emission Strength", {&emission_strength, 1});
+ copy_property_from_node(SOCK_RGBA, bsdf_node, "Emission", {emission_col, 3});
}
mul_v3_fl(emission_col, emission_strength);
+ float sheen = -1.0f;
+ float clearcoat = -1.0f;
+ float clearcoat_roughness = -1.0f;
+ float aniso = -1.0f;
+ float aniso_rot = -1.0f;
+ float transmission = -1.0f;
+ if (bsdf_node) {
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Sheen", {&sheen, 1});
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Clearcoat", {&clearcoat, 1});
+ copy_property_from_node(
+ SOCK_FLOAT, bsdf_node, "Clearcoat Roughness", {&clearcoat_roughness, 1});
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic", {&aniso, 1});
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Anisotropic Rotation", {&aniso_rot, 1});
+ copy_property_from_node(SOCK_FLOAT, bsdf_node, "Transmission", {&transmission, 1});
+ }
+
/* See https://wikipedia.org/wiki/Wavefront_.obj_file for all possible values of `illum`. */
/* Highlight on. */
int illum = 2;
@@ -235,26 +271,34 @@ static void store_bsdf_properties(const nodes::NodeRef *bsdf_node,
/* Transparency: Glass on, Reflection: Ray trace off */
illum = 9;
}
- r_mtl_mat.Ns = spec_exponent;
+ r_mtl_mat.spec_exponent = spec_exponent;
if (metallic != 0.0f) {
- r_mtl_mat.Ka = {metallic, metallic, metallic};
+ r_mtl_mat.ambient_color = {metallic, metallic, metallic};
}
else {
- r_mtl_mat.Ka = {1.0f, 1.0f, 1.0f};
+ r_mtl_mat.ambient_color = {1.0f, 1.0f, 1.0f};
}
- r_mtl_mat.Kd = diffuse_col;
- r_mtl_mat.Ks = {specular, specular, specular};
- r_mtl_mat.Ke = emission_col;
- r_mtl_mat.Ni = refraction_index;
- r_mtl_mat.d = dissolved;
- r_mtl_mat.illum = illum;
+ r_mtl_mat.color = diffuse_col;
+ r_mtl_mat.spec_color = {specular, specular, specular};
+ r_mtl_mat.emission_color = emission_col;
+ r_mtl_mat.ior = refraction_index;
+ r_mtl_mat.alpha = alpha;
+ r_mtl_mat.illum_mode = illum;
+ r_mtl_mat.roughness = roughness;
+ r_mtl_mat.metallic = metallic;
+ r_mtl_mat.sheen = sheen;
+ r_mtl_mat.cc_thickness = clearcoat;
+ r_mtl_mat.cc_roughness = clearcoat_roughness;
+ r_mtl_mat.aniso = aniso;
+ r_mtl_mat.aniso_rot = aniso_rot;
+ r_mtl_mat.transmit_color = {transmission, transmission, transmission};
}
/**
* Store image texture options and file-paths in `r_mtl_mat`.
*/
-static void store_image_textures(const nodes::NodeRef *bsdf_node,
- const nodes::NodeTreeRef *node_tree,
+static void store_image_textures(const bNode *bsdf_node,
+ const bNodeTree *node_tree,
const Material *material,
MTLMaterial &r_mtl_mat)
{
@@ -262,21 +306,20 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
/* No nodetree, no images, or no Principled BSDF node. */
return;
}
- const bNode *bnode = bsdf_node->bnode();
/* Normal Map Texture has two extra tasks of:
* - finding a Normal Map node before finding a texture node.
* - finding "Strength" property of the node for `-bm` option.
*/
- for (Map<const eMTLSyntaxElement, tex_map_XX>::MutableItem texture_map :
- r_mtl_mat.texture_maps.items()) {
- Vector<const nodes::OutputSocketRef *> linked_sockets;
+ for (int key = 0; key < (int)MTLTexMapType::Count; ++key) {
+ MTLTexMap &value = r_mtl_mat.texture_maps[key];
+ Vector<const bNodeSocket *> linked_sockets;
const bNode *normal_map_node{nullptr};
- if (texture_map.key == eMTLSyntaxElement::map_Bump) {
+ if (key == (int)MTLTexMapType::Normal) {
/* Find sockets linked to destination "Normal" socket in P-BSDF node. */
- linked_sockets_to_dest_id(bnode, *node_tree, "Normal", linked_sockets);
+ linked_sockets_to_dest_id(bsdf_node, *node_tree, "Normal", linked_sockets);
/* Among the linked sockets, find Normal Map shader node. */
normal_map_node = get_node_of_type(linked_sockets, SH_NODE_NORMAL_MAP);
@@ -285,16 +328,17 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
}
else {
/* Skip emission map if emission strength is zero. */
- if (texture_map.key == eMTLSyntaxElement::map_Ke) {
+ if (key == (int)MTLTexMapType::Emission) {
float emission_strength = 0.0f;
- copy_property_from_node(SOCK_FLOAT, bnode, "Emission Strength", {&emission_strength, 1});
+ copy_property_from_node(
+ SOCK_FLOAT, bsdf_node, "Emission Strength", {&emission_strength, 1});
if (emission_strength == 0.0f) {
continue;
}
}
/* Find sockets linked to the destination socket of interest, in P-BSDF node. */
linked_sockets_to_dest_id(
- bnode, *node_tree, texture_map.value.dest_socket_id, linked_sockets);
+ bsdf_node, *node_tree, tex_map_type_to_socket_id[key], linked_sockets);
}
/* Among the linked sockets, find Image Texture shader node. */
@@ -302,8 +346,8 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
if (!tex_node) {
continue;
}
- const char *tex_image_filepath = get_image_filepath(tex_node);
- if (!tex_image_filepath) {
+ const std::string tex_image_filepath = get_image_filepath(tex_node);
+ if (tex_image_filepath.empty()) {
continue;
}
@@ -313,14 +357,14 @@ static void store_image_textures(const nodes::NodeRef *bsdf_node,
if (normal_map_node) {
copy_property_from_node(
- SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.map_Bump_strength, 1});
+ SOCK_FLOAT, normal_map_node, "Strength", {&r_mtl_mat.normal_strength, 1});
}
/* Texture transform options. Only translation (origin offset, "-o") and scale
* ("-o") are supported. */
- copy_property_from_node(SOCK_VECTOR, mapping, "Location", {texture_map.value.translation, 3});
- copy_property_from_node(SOCK_VECTOR, mapping, "Scale", {texture_map.value.scale, 3});
+ copy_property_from_node(SOCK_VECTOR, mapping, "Location", {value.translation, 3});
+ copy_property_from_node(SOCK_VECTOR, mapping, "Scale", {value.scale, 3});
- texture_map.value.image_path = tex_image_filepath;
+ value.image_path = tex_image_filepath;
}
}
@@ -330,14 +374,14 @@ MTLMaterial mtlmaterial_for_material(const Material *material)
MTLMaterial mtlmat;
mtlmat.name = std::string(material->id.name + 2);
std::replace(mtlmat.name.begin(), mtlmat.name.end(), ' ', '_');
- const nodes::NodeTreeRef *nodetree = nullptr;
- if (material->nodetree) {
- nodetree = new nodes::NodeTreeRef(material->nodetree);
+ const bNodeTree *nodetree = material->nodetree;
+ if (nodetree != nullptr) {
+ nodetree->ensure_topology_cache();
}
- const nodes::NodeRef *bsdf_node = find_bsdf_node(nodetree);
+
+ const bNode *bsdf_node = find_bsdf_node(nodetree);
store_bsdf_properties(bsdf_node, material, mtlmat);
store_image_textures(bsdf_node, nodetree, material, mtlmat);
- delete nodetree;
return mtlmat;
}
diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
index 80e3127f69f..9c1bc2f0f8f 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
+++ b/source/blender/io/wavefront_obj/exporter/obj_export_mtl.hh
@@ -6,38 +6,38 @@
#pragma once
-#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_vector.hh"
#include "DNA_node_types.h"
-#include "obj_export_io.hh"
-
-namespace blender {
-template<> struct DefaultHash<io::obj::eMTLSyntaxElement> {
- uint64_t operator()(const io::obj::eMTLSyntaxElement value) const
- {
- return static_cast<uint64_t>(value);
- }
-};
-
-} // namespace blender
+struct Material;
namespace blender::io::obj {
-class OBJMesh;
-/**
- * Generic container for texture node properties.
- */
-struct tex_map_XX {
- tex_map_XX(StringRef to_socket_id) : dest_socket_id(to_socket_id){};
+enum class MTLTexMapType {
+ Color = 0,
+ Metallic,
+ Specular,
+ SpecularExponent,
+ Roughness,
+ Sheen,
+ Reflection,
+ Emission,
+ Alpha,
+ Normal,
+ Count
+};
+extern const char *tex_map_type_to_socket_id[];
+
+struct MTLTexMap {
+ bool is_valid() const
+ {
+ return !image_path.empty();
+ }
- /** Target socket which this texture node connects to. */
- const std::string dest_socket_id;
+ /* Target socket which this texture node connects to. */
float3 translation{0.0f};
float3 scale{1.0f};
- /* Only Flat and Smooth projections are supported. */
+ /* Only Flat and Sphere projections are supported. */
int projection_type = SHD_PROJ_FLAT;
std::string image_path;
std::string mtl_dir_path;
@@ -47,43 +47,38 @@ struct tex_map_XX {
* Container suited for storing Material data for/from a .MTL file.
*/
struct MTLMaterial {
- MTLMaterial()
+ const MTLTexMap &tex_map_of_type(MTLTexMapType key) const
{
- texture_maps.add(eMTLSyntaxElement::map_Kd, tex_map_XX("Base Color"));
- texture_maps.add(eMTLSyntaxElement::map_Ks, tex_map_XX("Specular"));
- texture_maps.add(eMTLSyntaxElement::map_Ns, tex_map_XX("Roughness"));
- texture_maps.add(eMTLSyntaxElement::map_d, tex_map_XX("Alpha"));
- texture_maps.add(eMTLSyntaxElement::map_refl, tex_map_XX("Metallic"));
- texture_maps.add(eMTLSyntaxElement::map_Ke, tex_map_XX("Emission"));
- texture_maps.add(eMTLSyntaxElement::map_Bump, tex_map_XX("Normal"));
+ return texture_maps[(int)key];
}
-
- /**
- * Caller must ensure that the given lookup key exists in the Map.
- * \return Texture map corresponding to the given ID.
- */
- tex_map_XX &tex_map_of_type(const eMTLSyntaxElement key)
+ MTLTexMap &tex_map_of_type(MTLTexMapType key)
{
- {
- BLI_assert(texture_maps.contains_as(key));
- return texture_maps.lookup_as(key);
- }
+ return texture_maps[(int)key];
}
std::string name;
/* Always check for negative values while importing or exporting. Use defaults if
* any value is negative. */
- float Ns{-1.0f};
- float3 Ka{-1.0f};
- float3 Kd{-1.0f};
- float3 Ks{-1.0f};
- float3 Ke{-1.0f};
- float Ni{-1.0f};
- float d{-1.0f};
- int illum{-1};
- Map<const eMTLSyntaxElement, tex_map_XX> texture_maps;
- /** Only used for Normal Map node: "map_Bump". */
- float map_Bump_strength{-1.0f};
+ float spec_exponent{-1.0f}; /* `Ns` */
+ float3 ambient_color{-1.0f}; /* `Ka` */
+ float3 color{-1.0f}; /* `Kd` */
+ float3 spec_color{-1.0f}; /* `Ks` */
+ float3 emission_color{-1.0f}; /* `Ke` */
+ float ior{-1.0f}; /* `Ni` */
+ float alpha{-1.0f}; /* `d` */
+ float3 transmit_color{-1.0f}; /* `Kt` / `Tf` */
+ float roughness{-1.0f}; /* `Pr` */
+ float metallic{-1.0f}; /* `Pm` */
+ float sheen{-1.0f}; /* `Ps` */
+ float cc_thickness{-1.0f}; /* `Pc` */
+ float cc_roughness{-1.0f}; /* `Pcr` */
+ float aniso{-1.0f}; /* `aniso` */
+ float aniso_rot{-1.0f}; /* `anisor` */
+
+ int illum_mode{-1};
+ MTLTexMap texture_maps[(int)MTLTexMapType::Count];
+ /* Only used for Normal Map node: `map_Bump`. */
+ float normal_strength{-1.0f};
};
MTLMaterial mtlmaterial_for_material(const Material *material);
diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
index 77d4f6268bc..a51c017f81d 100644
--- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
+++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc
@@ -143,7 +143,7 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me
* we have to have the output text buffer for each object,
* and write them all into the file at the end. */
size_t count = exportable_as_mesh.size();
- std::vector<FormatHandler<eFileType::OBJ>> buffers(count);
+ std::vector<FormatHandler> buffers(count);
/* Serial: gather material indices, ensure normals & edges. */
Vector<Vector<int>> mtlindices;
@@ -242,7 +242,7 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> exportable_as_me
static void write_nurbs_curve_objects(const Vector<std::unique_ptr<OBJCurve>> &exportable_as_nurbs,
const OBJWriter &obj_writer)
{
- FormatHandler<eFileType::OBJ> fh;
+ FormatHandler fh;
/* #OBJCurve doesn't have any dynamically allocated memory, so it's fine
* to wait for #blender::Vector to clean the objects up. */
for (const std::unique_ptr<OBJCurve> &obj_curve : exportable_as_nurbs) {
@@ -268,7 +268,7 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
std::unique_ptr<MTLWriter> mtl_writer = nullptr;
if (export_params.export_materials) {
try {
- mtl_writer = std::make_unique<MTLWriter>(export_params.filepath);
+ mtl_writer = std::make_unique<MTLWriter>(filepath);
}
catch (const std::system_error &ex) {
print_exception_error(ex);
@@ -293,7 +293,10 @@ void export_frame(Depsgraph *depsgraph, const OBJExportParams &export_params, co
}
BLI_path_slash_native(dest_dir);
BLI_path_normalize(nullptr, dest_dir);
- mtl_writer->write_materials(export_params.blen_filepath, export_params.path_mode, dest_dir);
+ mtl_writer->write_materials(export_params.blen_filepath,
+ export_params.path_mode,
+ dest_dir,
+ export_params.export_pbr_extensions);
}
write_nurbs_curve_objects(std::move(exportable_as_nurbs), *frame_writer);
}
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
index c2aa96713b1..f92f9894f75 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_file_reader.cc
@@ -10,9 +10,11 @@
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
+#include "obj_export_mtl.hh"
#include "obj_import_file_reader.hh"
#include "obj_import_string_utils.hh"
+#include <algorithm>
#include <charconv>
namespace blender::io::obj {
@@ -22,32 +24,24 @@ using std::string;
/**
* Based on the properties of the given Geometry instance, create a new Geometry instance
* or return the previous one.
- *
- * Also update index offsets which should always happen if a new Geometry instance is created.
*/
static Geometry *create_geometry(Geometry *const prev_geometry,
const eGeometryType new_type,
StringRef name,
- const GlobalVertices &global_vertices,
- Vector<std::unique_ptr<Geometry>> &r_all_geometries,
- VertexIndexOffset &r_offset)
+ Vector<std::unique_ptr<Geometry>> &r_all_geometries)
{
auto new_geometry = [&]() {
r_all_geometries.append(std::make_unique<Geometry>());
Geometry *g = r_all_geometries.last().get();
g->geom_type_ = new_type;
g->geometry_name_ = name.is_empty() ? "New object" : name;
- g->vertex_start_ = global_vertices.vertices.size();
- g->vertex_color_start_ = global_vertices.vertex_colors.size();
- r_offset.set_index_offset(g->vertex_start_);
return g;
};
if (prev_geometry && prev_geometry->geom_type_ == GEOM_MESH) {
/* After the creation of a Geometry instance, at least one element has been found in the OBJ
- * file that indicates that it is a mesh (basically anything but the vertex positions). */
- if (!prev_geometry->face_elements_.is_empty() || prev_geometry->has_vertex_normals_ ||
- !prev_geometry->edges_.is_empty()) {
+ * file that indicates that it is a mesh (faces or edges). */
+ if (!prev_geometry->face_elements_.is_empty() || !prev_geometry->edges_.is_empty()) {
return new_geometry();
}
if (new_type == GEOM_MESH) {
@@ -70,15 +64,11 @@ static Geometry *create_geometry(Geometry *const prev_geometry,
return new_geometry();
}
-static void geom_add_vertex(Geometry *geom,
- const char *p,
- const char *end,
- GlobalVertices &r_global_vertices)
+static void geom_add_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices)
{
float3 vert;
p = parse_floats(p, end, 0.0f, vert, 3);
r_global_vertices.vertices.append(vert);
- geom->vertex_count_++;
/* OBJ extension: `xyzrgb` vertex colors, when the vertex position
* is followed by 3 more RGB color components. See
* http://paulbourke.net/dataformats/obj/colour.html */
@@ -88,16 +78,22 @@ static void geom_add_vertex(Geometry *geom,
if (srgb.x >= 0 && srgb.y >= 0 && srgb.z >= 0) {
float3 linear;
srgb_to_linearrgb_v3_v3(linear, srgb);
- r_global_vertices.vertex_colors.append(linear);
- geom->vertex_color_count_++;
+
+ auto &blocks = r_global_vertices.vertex_colors;
+ /* If we don't have vertex colors yet, or the previous vertex
+ * was without color, we need to start a new vertex colors block. */
+ if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() !=
+ r_global_vertices.vertices.size() - 1)) {
+ GlobalVertices::VertexColorsBlock block;
+ block.start_vertex_index = r_global_vertices.vertices.size() - 1;
+ blocks.append(block);
+ }
+ blocks.last().colors.append(linear);
}
}
}
-static void geom_add_mrgb_colors(Geometry *geom,
- const char *p,
- const char *end,
- GlobalVertices &r_global_vertices)
+static void geom_add_mrgb_colors(const char *p, const char *end, GlobalVertices &r_global_vertices)
{
/* MRGB color extension, in the form of
* "#MRGB MMRRGGBBMMRRGGBB ..."
@@ -117,14 +113,26 @@ static void geom_add_mrgb_colors(Geometry *geom,
srgb[3] = 0xFF;
float linear[4];
srgb_to_linearrgb_uchar4(linear, srgb);
- r_global_vertices.vertex_colors.append({linear[0], linear[1], linear[2]});
- geom->vertex_color_count_++;
+
+ auto &blocks = r_global_vertices.vertex_colors;
+ /* If we don't have vertex colors yet, or the previous vertex
+ * was without color, we need to start a new vertex colors block. */
+ if (blocks.is_empty() || (blocks.last().start_vertex_index + blocks.last().colors.size() !=
+ r_global_vertices.vertices.size())) {
+ GlobalVertices::VertexColorsBlock block;
+ block.start_vertex_index = r_global_vertices.vertices.size();
+ blocks.append(block);
+ }
+ blocks.last().colors.append({linear[0], linear[1], linear[2]});
+ /* MRGB colors are specified after vertex positions; each new color
+ * "pushes" the vertex colors block further back into which vertices it is for. */
+ blocks.last().start_vertex_index--;
+
p += mrgb_length;
}
}
-static void geom_add_vertex_normal(Geometry *geom,
- const char *p,
+static void geom_add_vertex_normal(const char *p,
const char *end,
GlobalVertices &r_global_vertices)
{
@@ -135,7 +143,6 @@ static void geom_add_vertex_normal(Geometry *geom,
* normalized. */
normalize_v3(normal);
r_global_vertices.vertex_normals.append(normal);
- geom->has_vertex_normals_ = true;
}
static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r_global_vertices)
@@ -148,24 +155,24 @@ static void geom_add_uv_vertex(const char *p, const char *end, GlobalVertices &r
static void geom_add_edge(Geometry *geom,
const char *p,
const char *end,
- const VertexIndexOffset &offsets,
GlobalVertices &r_global_vertices)
{
int edge_v1, edge_v2;
p = parse_int(p, end, -1, edge_v1);
p = parse_int(p, end, -1, edge_v2);
/* Always keep stored indices non-negative and zero-based. */
- edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1;
- edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -offsets.get_index_offset() - 1;
+ edge_v1 += edge_v1 < 0 ? r_global_vertices.vertices.size() : -1;
+ edge_v2 += edge_v2 < 0 ? r_global_vertices.vertices.size() : -1;
BLI_assert(edge_v1 >= 0 && edge_v2 >= 0);
geom->edges_.append({static_cast<uint>(edge_v1), static_cast<uint>(edge_v2)});
+ geom->track_vertex_index(edge_v1);
+ geom->track_vertex_index(edge_v2);
}
static void geom_add_polygon(Geometry *geom,
const char *p,
const char *end,
const GlobalVertices &global_vertices,
- const VertexIndexOffset &offsets,
const int material_index,
const int group_index,
const bool shaded_smooth)
@@ -204,8 +211,7 @@ static void geom_add_polygon(Geometry *geom,
}
}
/* Always keep stored indices non-negative and zero-based. */
- corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() :
- -offsets.get_index_offset() - 1;
+ corner.vert_index += corner.vert_index < 0 ? global_vertices.vertices.size() : -1;
if (corner.vert_index < 0 || corner.vert_index >= global_vertices.vertices.size()) {
fprintf(stderr,
"Invalid vertex index %i (valid range [0, %zu)), ignoring face\n",
@@ -213,6 +219,9 @@ static void geom_add_polygon(Geometry *geom,
(size_t)global_vertices.vertices.size());
face_valid = false;
}
+ else {
+ geom->track_vertex_index(corner.vert_index);
+ }
if (got_uv) {
corner.uv_vert_index += corner.uv_vert_index < 0 ? global_vertices.uv_vertices.size() : -1;
if (corner.uv_vert_index < 0 || corner.uv_vert_index >= global_vertices.uv_vertices.size()) {
@@ -226,7 +235,7 @@ static void geom_add_polygon(Geometry *geom,
/* Ignore corner normal index, if the geometry does not have any normals.
* Some obj files out there do have face definitions that refer to normal indices,
* without any normals being present (T98782). */
- if (got_normal && geom->has_vertex_normals_) {
+ if (got_normal && !global_vertices.vertex_normals.is_empty()) {
corner.vertex_normal_index += corner.vertex_normal_index < 0 ?
global_vertices.vertex_normals.size() :
-1;
@@ -260,9 +269,7 @@ static void geom_add_polygon(Geometry *geom,
static Geometry *geom_set_curve_type(Geometry *geom,
const char *p,
const char *end,
- const GlobalVertices &global_vertices,
const StringRef group_name,
- VertexIndexOffset &r_offsets,
Vector<std::unique_ptr<Geometry>> &r_all_geometries)
{
p = drop_whitespace(p, end);
@@ -270,8 +277,7 @@ static Geometry *geom_set_curve_type(Geometry *geom,
std::cerr << "Curve type not supported: '" << std::string(p, end) << "'" << std::endl;
return geom;
}
- geom = create_geometry(
- geom, GEOM_CURVE, group_name, global_vertices, r_all_geometries, r_offsets);
+ geom = create_geometry(geom, GEOM_CURVE, group_name, r_all_geometries);
geom->nurbs_element_.group_ = group_name;
return geom;
}
@@ -390,6 +396,22 @@ static bool parse_keyword(const char *&p, const char *end, StringRef keyword)
return true;
}
+/* Special case: if there were no faces/edges in any geometries,
+ * treat all the vertices as a point cloud. */
+static void use_all_vertices_if_no_faces(Geometry *geom,
+ const Vector<std::unique_ptr<Geometry>> &all_geometries,
+ const GlobalVertices &global_vertices)
+{
+ if (!global_vertices.vertices.is_empty() && geom && geom->geom_type_ == GEOM_MESH) {
+ if (std::all_of(
+ all_geometries.begin(), all_geometries.end(), [](const std::unique_ptr<Geometry> &g) {
+ return g->get_vertex_count() == 0;
+ })) {
+ geom->track_all_vertices(global_vertices.vertices.size());
+ }
+ }
+}
+
void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
GlobalVertices &r_global_vertices)
{
@@ -402,9 +424,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
BLI_strncpy(ob_name, BLI_path_basename(import_params_.filepath), FILE_MAXFILE);
BLI_path_extension_replace(ob_name, FILE_MAXFILE, "");
- VertexIndexOffset offsets;
- Geometry *curr_geom = create_geometry(
- nullptr, GEOM_MESH, ob_name, r_global_vertices, r_all_geometries, offsets);
+ Geometry *curr_geom = create_geometry(nullptr, GEOM_MESH, ob_name, r_all_geometries);
/* State variables: once set, they remain the same for the remaining
* elements in the object. */
@@ -427,6 +447,11 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
break; /* No more data to read. */
}
+ /* Take care of line continuations now (turn them into spaces);
+ * the rest of the parsing code does not need to worry about them anymore. */
+ fixup_line_continuations(buffer.data() + buffer_offset,
+ buffer.data() + buffer_offset + bytes_read);
+
/* Ensure buffer ends in a newline. */
if (bytes_read < read_buffer_size_) {
if (bytes_read == 0 || buffer[buffer_offset + bytes_read - 1] != '\n') {
@@ -445,9 +470,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
while (last_nl > 0) {
--last_nl;
if (buffer[last_nl] == '\n') {
- if (last_nl < 1 || buffer[last_nl - 1] != '\\') {
- break;
- }
+ break;
}
}
if (buffer[last_nl] != '\n') {
@@ -474,10 +497,10 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
/* Most common things that start with 'v': vertices, normals, UVs. */
if (*p == 'v') {
if (parse_keyword(p, end, "v")) {
- geom_add_vertex(curr_geom, p, end, r_global_vertices);
+ geom_add_vertex(p, end, r_global_vertices);
}
else if (parse_keyword(p, end, "vn")) {
- geom_add_vertex_normal(curr_geom, p, end, r_global_vertices);
+ geom_add_vertex_normal(p, end, r_global_vertices);
}
else if (parse_keyword(p, end, "vt")) {
geom_add_uv_vertex(p, end, r_global_vertices);
@@ -489,26 +512,21 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
p,
end,
r_global_vertices,
- offsets,
state_material_index,
- state_group_index, /* TODO was wrongly material name! */
+ state_group_index,
state_shaded_smooth);
}
/* Faces. */
else if (parse_keyword(p, end, "l")) {
- geom_add_edge(curr_geom, p, end, offsets, r_global_vertices);
+ geom_add_edge(curr_geom, p, end, r_global_vertices);
}
/* Objects. */
else if (parse_keyword(p, end, "o")) {
state_shaded_smooth = false;
state_group_name = "";
state_material_name = "";
- curr_geom = create_geometry(curr_geom,
- GEOM_MESH,
- StringRef(p, end).trim(),
- r_global_vertices,
- r_all_geometries,
- offsets);
+ curr_geom = create_geometry(
+ curr_geom, GEOM_MESH, StringRef(p, end).trim(), r_all_geometries);
}
/* Groups. */
else if (parse_keyword(p, end, "g")) {
@@ -537,7 +555,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
add_mtl_library(StringRef(p, end).trim());
}
else if (parse_keyword(p, end, "#MRGB")) {
- geom_add_mrgb_colors(curr_geom, p, end, r_global_vertices);
+ geom_add_mrgb_colors(p, end, r_global_vertices);
}
/* Comments. */
else if (*p == '#') {
@@ -545,8 +563,7 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
}
/* Curve related things. */
else if (parse_keyword(p, end, "cstype")) {
- curr_geom = geom_set_curve_type(
- curr_geom, p, end, r_global_vertices, state_group_name, offsets, r_all_geometries);
+ curr_geom = geom_set_curve_type(curr_geom, p, end, state_group_name, r_all_geometries);
}
else if (parse_keyword(p, end, "deg")) {
geom_set_curve_degree(curr_geom, p, end);
@@ -572,39 +589,44 @@ void OBJParser::parse(Vector<std::unique_ptr<Geometry>> &r_all_geometries,
buffer_offset = left_size;
}
+ use_all_vertices_if_no_faces(curr_geom, r_all_geometries, r_global_vertices);
add_default_mtl_library();
}
-static eMTLSyntaxElement mtl_line_start_to_enum(const char *&p, const char *end)
+static MTLTexMapType mtl_line_start_to_texture_type(const char *&p, const char *end)
{
if (parse_keyword(p, end, "map_Kd")) {
- return eMTLSyntaxElement::map_Kd;
+ return MTLTexMapType::Color;
}
if (parse_keyword(p, end, "map_Ks")) {
- return eMTLSyntaxElement::map_Ks;
+ return MTLTexMapType::Specular;
}
if (parse_keyword(p, end, "map_Ns")) {
- return eMTLSyntaxElement::map_Ns;
+ return MTLTexMapType::SpecularExponent;
}
if (parse_keyword(p, end, "map_d")) {
- return eMTLSyntaxElement::map_d;
+ return MTLTexMapType::Alpha;
}
- if (parse_keyword(p, end, "refl")) {
- return eMTLSyntaxElement::map_refl;
- }
- if (parse_keyword(p, end, "map_refl")) {
- return eMTLSyntaxElement::map_refl;
+ if (parse_keyword(p, end, "refl") || parse_keyword(p, end, "map_refl")) {
+ return MTLTexMapType::Reflection;
}
if (parse_keyword(p, end, "map_Ke")) {
- return eMTLSyntaxElement::map_Ke;
+ return MTLTexMapType::Emission;
+ }
+ if (parse_keyword(p, end, "bump") || parse_keyword(p, end, "map_Bump") ||
+ parse_keyword(p, end, "map_bump")) {
+ return MTLTexMapType::Normal;
}
- if (parse_keyword(p, end, "bump")) {
- return eMTLSyntaxElement::map_Bump;
+ if (parse_keyword(p, end, "map_Pr")) {
+ return MTLTexMapType::Roughness;
}
- if (parse_keyword(p, end, "map_Bump") || parse_keyword(p, end, "map_bump")) {
- return eMTLSyntaxElement::map_Bump;
+ if (parse_keyword(p, end, "map_Pm")) {
+ return MTLTexMapType::Metallic;
}
- return eMTLSyntaxElement::string;
+ if (parse_keyword(p, end, "map_Ps")) {
+ return MTLTexMapType::Sheen;
+ }
+ return MTLTexMapType::Count;
}
static const std::pair<StringRef, int> unsupported_texture_options[] = {
@@ -622,7 +644,7 @@ static const std::pair<StringRef, int> unsupported_texture_options[] = {
static bool parse_texture_option(const char *&p,
const char *end,
MTLMaterial *material,
- tex_map_XX &tex_map)
+ MTLTexMap &tex_map)
{
p = drop_whitespace(p, end);
if (parse_keyword(p, end, "-o")) {
@@ -634,7 +656,7 @@ static bool parse_texture_option(const char *&p,
return true;
}
if (parse_keyword(p, end, "-bm")) {
- p = parse_float(p, end, 1.0f, material->map_Bump_strength, true, true);
+ p = parse_float(p, end, 1.0f, material->normal_strength, true, true);
return true;
}
if (parse_keyword(p, end, "-type")) {
@@ -676,13 +698,13 @@ static void parse_texture_map(const char *p,
if (!is_map && !is_refl && !is_bump) {
return;
}
- eMTLSyntaxElement key = mtl_line_start_to_enum(p, end);
- if (key == eMTLSyntaxElement::string || !material->texture_maps.contains(key)) {
+ MTLTexMapType key = mtl_line_start_to_texture_type(p, end);
+ if (key == MTLTexMapType::Count) {
/* No supported texture map found. */
std::cerr << "OBJ import: MTL texture map type not supported: '" << line << "'" << std::endl;
return;
}
- tex_map_XX &tex_map = material->texture_maps.lookup(key);
+ MTLTexMap &tex_map = material->tex_map_of_type(key);
tex_map.mtl_dir_path = mtl_dir_path;
/* Parse texture map options. */
@@ -731,7 +753,7 @@ MTLParser::MTLParser(StringRefNull mtl_library, StringRefNull obj_filepath)
{
char obj_file_dir[FILE_MAXDIR];
BLI_split_dir_part(obj_filepath.data(), obj_file_dir, FILE_MAXDIR);
- BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), NULL);
+ BLI_path_join(mtl_file_path_, FILE_MAX, obj_file_dir, mtl_library.data(), nullptr);
BLI_split_dir_part(mtl_file_path_, mtl_dir_path_, FILE_MAXDIR);
}
@@ -767,31 +789,55 @@ void MTLParser::parse_and_store(Map<string, std::unique_ptr<MTLMaterial>> &r_mat
}
else if (material != nullptr) {
if (parse_keyword(p, end, "Ns")) {
- parse_float(p, end, 324.0f, material->Ns);
+ parse_float(p, end, 324.0f, material->spec_exponent);
}
else if (parse_keyword(p, end, "Ka")) {
- parse_floats(p, end, 0.0f, material->Ka, 3);
+ parse_floats(p, end, 0.0f, material->ambient_color, 3);
}
else if (parse_keyword(p, end, "Kd")) {
- parse_floats(p, end, 0.8f, material->Kd, 3);
+ parse_floats(p, end, 0.8f, material->color, 3);
}
else if (parse_keyword(p, end, "Ks")) {
- parse_floats(p, end, 0.5f, material->Ks, 3);
+ parse_floats(p, end, 0.5f, material->spec_color, 3);
}
else if (parse_keyword(p, end, "Ke")) {
- parse_floats(p, end, 0.0f, material->Ke, 3);
+ parse_floats(p, end, 0.0f, material->emission_color, 3);
}
else if (parse_keyword(p, end, "Ni")) {
- parse_float(p, end, 1.45f, material->Ni);
+ parse_float(p, end, 1.45f, material->ior);
}
else if (parse_keyword(p, end, "d")) {
- parse_float(p, end, 1.0f, material->d);
+ parse_float(p, end, 1.0f, material->alpha);
}
else if (parse_keyword(p, end, "illum")) {
/* Some files incorrectly use a float (T60135). */
float val;
parse_float(p, end, 1.0f, val);
- material->illum = val;
+ material->illum_mode = val;
+ }
+ else if (parse_keyword(p, end, "Pr")) {
+ parse_float(p, end, 0.5f, material->roughness);
+ }
+ else if (parse_keyword(p, end, "Pm")) {
+ parse_float(p, end, 0.0f, material->metallic);
+ }
+ else if (parse_keyword(p, end, "Ps")) {
+ parse_float(p, end, 0.0f, material->sheen);
+ }
+ else if (parse_keyword(p, end, "Pc")) {
+ parse_float(p, end, 0.0f, material->cc_thickness);
+ }
+ else if (parse_keyword(p, end, "Pcr")) {
+ parse_float(p, end, 0.0f, material->cc_roughness);
+ }
+ else if (parse_keyword(p, end, "aniso")) {
+ parse_float(p, end, 0.0f, material->aniso);
+ }
+ else if (parse_keyword(p, end, "anisor")) {
+ parse_float(p, end, 0.0f, material->aniso_rot);
+ }
+ else if (parse_keyword(p, end, "Kt") || parse_keyword(p, end, "Tf")) {
+ parse_floats(p, end, 0.0f, material->transmit_color, 3);
}
else {
parse_texture_map(p, end, material, mtl_dir_path_);
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
index 8cef0700b3b..ef05534928a 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc
@@ -8,7 +8,7 @@
#include "DNA_mesh_types.h"
#include "DNA_scene_types.h"
-#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_material.h"
@@ -22,6 +22,7 @@
#include "IO_wavefront_obj.h"
#include "importer_mesh_utils.hh"
+#include "obj_export_mtl.hh"
#include "obj_import_mesh.hh"
namespace blender::io::obj {
@@ -31,13 +32,17 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
Map<std::string, Material *> &created_materials,
const OBJImportParams &import_params)
{
+ const int64_t tot_verts_object{mesh_geometry_.get_vertex_count()};
+ if (tot_verts_object <= 0) {
+ /* Empty mesh */
+ return nullptr;
+ }
std::string ob_name{mesh_geometry_.geometry_name_};
if (ob_name.empty()) {
ob_name = "Untitled";
}
fixup_invalid_faces();
- const int64_t tot_verts_object{mesh_geometry_.vertex_count_};
/* Total explicitly imported edges, not the ones belonging the polygons to be created. */
const int64_t tot_edges{mesh_geometry_.edges_.size()};
const int64_t tot_face_elems{mesh_geometry_.face_elements_.size()};
@@ -53,7 +58,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
create_uv_verts(mesh);
create_normals(mesh);
create_colors(mesh);
- create_materials(bmain, materials, created_materials, obj);
+ create_materials(bmain, materials, created_materials, obj, import_params.relative_paths);
if (import_params.validate_meshes || mesh_geometry_.has_invalid_polys_) {
bool verbose_validate = false;
@@ -64,11 +69,7 @@ Object *MeshFromGeometry::create_mesh(Main *bmain,
}
transform_object(obj, import_params);
- /* FIXME: after 2.80; `mesh->flag` isn't copied by #BKE_mesh_nomain_to_mesh() */
- const uint16_t autosmooth = (mesh->flag & ME_AUTOSMOOTH);
- Mesh *dst = static_cast<Mesh *>(obj->data);
- BKE_mesh_nomain_to_mesh(mesh, dst, obj, &CD_MASK_EVERYTHING, true);
- dst->flag |= autosmooth;
+ BKE_mesh_nomain_to_mesh(mesh, static_cast<Mesh *>(obj->data), obj);
/* NOTE: vertex groups have to be created after final mesh is assigned to the object. */
create_vertex_groups(obj);
@@ -153,29 +154,39 @@ void MeshFromGeometry::fixup_invalid_faces()
void MeshFromGeometry::create_vertices(Mesh *mesh)
{
- const int tot_verts_object{mesh_geometry_.vertex_count_};
- for (int i = 0; i < tot_verts_object; ++i) {
- int vi = mesh_geometry_.vertex_start_ + i;
- if (vi < global_vertices_.vertices.size()) {
- copy_v3_v3(mesh->mvert[i].co, global_vertices_.vertices[vi]);
- }
- else {
- std::cerr << "Vertex index:" << vi
- << " larger than total vertices:" << global_vertices_.vertices.size() << " ."
- << std::endl;
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ /* Go through all the global vertex indices from min to max,
+ * checking which ones are actually and building a global->local
+ * index mapping. Write out the used vertex positions into the Mesh
+ * data. */
+ mesh_geometry_.global_to_local_vertices_.clear();
+ mesh_geometry_.global_to_local_vertices_.reserve(mesh_geometry_.vertices_.size());
+ for (int vi = mesh_geometry_.vertex_index_min_; vi <= mesh_geometry_.vertex_index_max_; ++vi) {
+ BLI_assert(vi >= 0 && vi < global_vertices_.vertices.size());
+ if (!mesh_geometry_.vertices_.contains(vi)) {
+ continue;
}
+ int local_vi = (int)mesh_geometry_.global_to_local_vertices_.size();
+ BLI_assert(local_vi >= 0 && local_vi < mesh->totvert);
+ copy_v3_v3(verts[local_vi].co, global_vertices_.vertices[vi]);
+ mesh_geometry_.global_to_local_vertices_.add_new(vi, local_vi);
}
}
void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups)
{
- mesh->dvert = nullptr;
- const int64_t total_verts = mesh_geometry_.vertex_count_;
+ MutableSpan<MDeformVert> dverts;
+ const int64_t total_verts = mesh_geometry_.get_vertex_count();
if (use_vertex_groups && total_verts && mesh_geometry_.has_vertex_groups_) {
- mesh->dvert = static_cast<MDeformVert *>(
- CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, nullptr, total_verts));
+ dverts = mesh->deform_verts_for_write();
}
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
+ bke::SpanAttributeWriter<int> material_indices =
+ mesh->attributes_for_write().lookup_or_add_for_write_only_span<int>("material_index",
+ ATTR_DOMAIN_FACE);
+
const int64_t tot_face_elems{mesh->totpoly};
int tot_loop_idx = 0;
@@ -187,40 +198,42 @@ void MeshFromGeometry::create_polys_loops(Mesh *mesh, bool use_vertex_groups)
continue;
}
- MPoly &mpoly = mesh->mpoly[poly_idx];
+ MPoly &mpoly = polys[poly_idx];
mpoly.totloop = curr_face.corner_count_;
mpoly.loopstart = tot_loop_idx;
if (curr_face.shaded_smooth) {
mpoly.flag |= ME_SMOOTH;
}
- mpoly.mat_nr = curr_face.material_index;
+ material_indices.span[poly_idx] = curr_face.material_index;
/* Importing obj files without any materials would result in negative indices, which is not
* supported. */
- if (mpoly.mat_nr < 0) {
- mpoly.mat_nr = 0;
+ if (material_indices.span[poly_idx] < 0) {
+ material_indices.span[poly_idx] = 0;
}
for (int idx = 0; idx < curr_face.corner_count_; ++idx) {
const PolyCorner &curr_corner = mesh_geometry_.face_corners_[curr_face.start_index_ + idx];
- MLoop &mloop = mesh->mloop[tot_loop_idx];
+ MLoop &mloop = loops[tot_loop_idx];
tot_loop_idx++;
- mloop.v = curr_corner.vert_index;
+ mloop.v = mesh_geometry_.global_to_local_vertices_.lookup_default(curr_corner.vert_index, 0);
/* Setup vertex group data, if needed. */
- if (!mesh->dvert) {
+ if (dverts.is_empty()) {
continue;
}
const int group_index = curr_face.vertex_group_index;
- MDeformWeight *dw = BKE_defvert_ensure_index(mesh->dvert + mloop.v, group_index);
+ MDeformWeight *dw = BKE_defvert_ensure_index(&dverts[mloop.v], group_index);
dw->weight = 1.0f;
}
}
+
+ material_indices.finish();
}
void MeshFromGeometry::create_vertex_groups(Object *obj)
{
Mesh *mesh = static_cast<Mesh *>(obj->data);
- if (mesh->dvert == nullptr) {
+ if (mesh->deform_verts().is_empty()) {
return;
}
for (const std::string &name : mesh_geometry_.group_order_) {
@@ -230,15 +243,17 @@ void MeshFromGeometry::create_vertex_groups(Object *obj)
void MeshFromGeometry::create_edges(Mesh *mesh)
{
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+
const int64_t tot_edges{mesh_geometry_.edges_.size()};
- const int64_t total_verts{mesh_geometry_.vertex_count_};
+ const int64_t total_verts{mesh_geometry_.get_vertex_count()};
UNUSED_VARS_NDEBUG(total_verts);
for (int i = 0; i < tot_edges; ++i) {
const MEdge &src_edge = mesh_geometry_.edges_[i];
- MEdge &dst_edge = mesh->medge[i];
- BLI_assert(src_edge.v1 < total_verts && src_edge.v2 < total_verts);
- dst_edge.v1 = src_edge.v1;
- dst_edge.v2 = src_edge.v2;
+ MEdge &dst_edge = edges[i];
+ dst_edge.v1 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v1, 0);
+ dst_edge.v2 = mesh_geometry_.global_to_local_vertices_.lookup_default(src_edge.v2, 0);
+ BLI_assert(dst_edge.v1 < total_verts && dst_edge.v2 < total_verts);
dst_edge.flag = ME_LOOSEEDGE;
}
@@ -254,7 +269,7 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh)
return;
}
MLoopUV *mluv_dst = static_cast<MLoopUV *>(CustomData_add_layer(
- &mesh->ldata, CD_MLOOPUV, CD_DEFAULT, nullptr, mesh_geometry_.total_loops_));
+ &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, nullptr, mesh_geometry_.total_loops_));
int tot_loop_idx = 0;
for (const PolyElem &curr_face : mesh_geometry_.face_elements_) {
@@ -273,7 +288,8 @@ void MeshFromGeometry::create_uv_verts(Mesh *mesh)
static Material *get_or_create_material(Main *bmain,
const std::string &name,
Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
- Map<std::string, Material *> &created_materials)
+ Map<std::string, Material *> &created_materials,
+ bool relative_paths)
{
/* Have we created this material already? */
Material **found_mat = created_materials.lookup_ptr(name);
@@ -287,9 +303,10 @@ static Material *get_or_create_material(Main *bmain,
const MTLMaterial &mtl = *materials.lookup_or_add(name, std::make_unique<MTLMaterial>());
Material *mat = BKE_material_add(bmain, name.c_str());
- ShaderNodetreeWrap mat_wrap{bmain, mtl, mat};
+ id_us_min(&mat->id);
+
mat->use_nodes = true;
- mat->nodetree = mat_wrap.get_nodetree();
+ mat->nodetree = create_mtl_node_tree(bmain, mtl, mat, relative_paths);
BKE_ntree_update_main_tree(bmain, mat->nodetree, nullptr);
created_materials.add_new(name, mat);
@@ -299,23 +316,30 @@ static Material *get_or_create_material(Main *bmain,
void MeshFromGeometry::create_materials(Main *bmain,
Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
Map<std::string, Material *> &created_materials,
- Object *obj)
+ Object *obj,
+ bool relative_paths)
{
for (const std::string &name : mesh_geometry_.material_order_) {
- Material *mat = get_or_create_material(bmain, name, materials, created_materials);
+ Material *mat = get_or_create_material(
+ bmain, name, materials, created_materials, relative_paths);
if (mat == nullptr) {
continue;
}
BKE_object_material_assign_single_obdata(bmain, obj, mat, obj->totcol + 1);
}
+ if (obj->totcol > 0) {
+ obj->actcol = 1;
+ }
}
void MeshFromGeometry::create_normals(Mesh *mesh)
{
- /* NOTE: Needs more clarity about what is expected in the viewport if the function works. */
-
/* No normal data: nothing to do. */
- if (global_vertices_.vertex_normals.is_empty() || !mesh_geometry_.has_vertex_normals_) {
+ if (global_vertices_.vertex_normals.is_empty()) {
+ return;
+ }
+ /* Custom normals can only be stored on face corners. */
+ if (mesh_geometry_.total_loops_ == 0) {
return;
}
@@ -341,23 +365,26 @@ void MeshFromGeometry::create_normals(Mesh *mesh)
void MeshFromGeometry::create_colors(Mesh *mesh)
{
- /* Nothing to do if we don't have vertex colors. */
- if (mesh_geometry_.vertex_color_count_ < 1) {
- return;
- }
- if (mesh_geometry_.vertex_color_count_ != mesh_geometry_.vertex_count_) {
- std::cerr << "Mismatching number of vertices (" << mesh_geometry_.vertex_count_
- << ") and colors (" << mesh_geometry_.vertex_color_count_ << ") on object '"
- << mesh_geometry_.geometry_name_ << "', ignoring colors." << std::endl;
+ /* Nothing to do if we don't have vertex colors at all. */
+ if (global_vertices_.vertex_colors.is_empty()) {
return;
}
- CustomDataLayer *color_layer = BKE_id_attribute_new(
- &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr);
- float4 *colors = (float4 *)color_layer->data;
- for (int i = 0; i < mesh_geometry_.vertex_color_count_; ++i) {
- float3 c = global_vertices_.vertex_colors[mesh_geometry_.vertex_color_start_ + i];
- colors[i] = float4(c.x, c.y, c.z, 1.0f);
+ /* Find which vertex color block is for this mesh (if any). */
+ for (const auto &block : global_vertices_.vertex_colors) {
+ if (mesh_geometry_.vertex_index_min_ >= block.start_vertex_index &&
+ mesh_geometry_.vertex_index_max_ < block.start_vertex_index + block.colors.size()) {
+ /* This block is suitable, use colors from it. */
+ CustomDataLayer *color_layer = BKE_id_attribute_new(
+ &mesh->id, "Color", CD_PROP_COLOR, ATTR_DOMAIN_POINT, nullptr);
+ float4 *colors = (float4 *)color_layer->data;
+ int offset = mesh_geometry_.vertex_index_min_ - block.start_vertex_index;
+ for (int i = 0, n = mesh_geometry_.get_vertex_count(); i != n; ++i) {
+ float3 c = block.colors[offset + i];
+ colors[i] = float4(c.x, c.y, c.z, 1.0f);
+ }
+ return;
+ }
}
}
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
index 591a7b81e63..cdc88528f1e 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.hh
@@ -62,7 +62,8 @@ class MeshFromGeometry : NonMovable, NonCopyable {
void create_materials(Main *bmain,
Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
Map<std::string, Material *> &created_materials,
- Object *obj);
+ Object *obj,
+ bool relative_paths);
void create_normals(Mesh *mesh);
void create_colors(Mesh *mesh);
void create_vertex_groups(Object *obj);
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
index 60e419728f3..c471b2002de 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.cc
@@ -5,18 +5,19 @@
*/
#include "BKE_image.h"
+#include "BKE_main.h"
#include "BKE_node.h"
#include "BLI_map.hh"
#include "BLI_math_vector.h"
+#include "BLI_path_util.h"
#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "NOD_shader.h"
-/* TODO: move eMTLSyntaxElement out of following file into a more neutral place */
-#include "obj_export_io.hh"
+#include "obj_export_mtl.hh"
#include "obj_import_mtl.hh"
#include "obj_import_string_utils.hh"
@@ -27,12 +28,12 @@ namespace blender::io::obj {
* Only float value(s) can be set using this method.
*/
static void set_property_of_socket(eNodeSocketDatatype property_type,
- StringRef socket_id,
+ const char *socket_id,
Span<float> value,
bNode *r_node)
{
BLI_assert(r_node);
- bNodeSocket *socket{nodeFindSocket(r_node, SOCK_IN, socket_id.data())};
+ bNodeSocket *socket{nodeFindSocket(r_node, SOCK_IN, socket_id)};
BLI_assert(socket && socket->type == property_type);
switch (property_type) {
case SOCK_FLOAT: {
@@ -60,136 +61,124 @@ static void set_property_of_socket(eNodeSocketDatatype property_type,
}
}
-static bool load_texture_image_at_path(Main *bmain,
- const tex_map_XX &tex_map,
- bNode *r_node,
- const std::string &path)
+static Image *load_image_at_path(Main *bmain, const std::string &path, bool relative_paths)
{
- Image *tex_image = BKE_image_load(bmain, path.c_str());
- if (!tex_image) {
+ Image *image = BKE_image_load_exists(bmain, path.c_str());
+ if (!image) {
fprintf(stderr, "Cannot load image file: '%s'\n", path.c_str());
- return false;
+ return nullptr;
}
fprintf(stderr, "Loaded image from: '%s'\n", path.c_str());
- r_node->id = reinterpret_cast<ID *>(tex_image);
- NodeTexImage *image = static_cast<NodeTexImage *>(r_node->storage);
- image->projection = tex_map.projection_type;
- return true;
+ if (relative_paths) {
+ BLI_path_rel(image->filepath, BKE_main_blendfile_path(bmain));
+ }
+ return image;
}
-/**
- * Load image for Image Texture node and set the node properties.
- * Return success if Image can be loaded successfully.
- */
-static bool load_texture_image(Main *bmain, const tex_map_XX &tex_map, bNode *r_node)
+static Image *create_placeholder_image(Main *bmain, const std::string &path)
{
- BLI_assert(r_node && r_node->type == SH_NODE_TEX_IMAGE);
+ const float color[4] = {0, 0, 0, 1};
+ Image *image = BKE_image_add_generated(bmain,
+ 32,
+ 32,
+ BLI_path_basename(path.c_str()),
+ 24,
+ false,
+ IMA_GENTYPE_BLANK,
+ color,
+ false,
+ false,
+ false);
+ STRNCPY(image->filepath, path.c_str());
+ image->source = IMA_SRC_FILE;
+ return image;
+}
+
+static Image *load_texture_image(Main *bmain, const MTLTexMap &tex_map, bool relative_paths)
+{
+ Image *image = nullptr;
/* First try treating texture path as relative. */
std::string tex_path{tex_map.mtl_dir_path + tex_map.image_path};
- if (load_texture_image_at_path(bmain, tex_map, r_node, tex_path)) {
- return true;
+ image = load_image_at_path(bmain, tex_path, relative_paths);
+ if (image != nullptr) {
+ return image;
}
/* Then try using it directly as absolute path. */
std::string raw_path{tex_map.image_path};
- if (load_texture_image_at_path(bmain, tex_map, r_node, raw_path)) {
- return true;
+ image = load_image_at_path(bmain, raw_path, relative_paths);
+ if (image != nullptr) {
+ return image;
}
/* Try removing quotes. */
std::string no_quote_path{tex_path};
auto end_pos = std::remove(no_quote_path.begin(), no_quote_path.end(), '"');
no_quote_path.erase(end_pos, no_quote_path.end());
- if (no_quote_path != tex_path &&
- load_texture_image_at_path(bmain, tex_map, r_node, no_quote_path)) {
- return true;
+ if (no_quote_path != tex_path) {
+ image = load_image_at_path(bmain, no_quote_path, relative_paths);
+ if (image != nullptr) {
+ return image;
+ }
}
/* Try replacing underscores with spaces. */
std::string no_underscore_path{no_quote_path};
std::replace(no_underscore_path.begin(), no_underscore_path.end(), '_', ' ');
- if (no_underscore_path != no_quote_path && no_underscore_path != tex_path &&
- load_texture_image_at_path(bmain, tex_map, r_node, no_underscore_path)) {
- return true;
+ if (no_underscore_path != no_quote_path && no_underscore_path != tex_path) {
+ image = load_image_at_path(bmain, no_underscore_path, relative_paths);
+ if (image != nullptr) {
+ return image;
+ }
+ }
+ /* Try taking just the basename from input path. */
+ std::string base_path{tex_map.mtl_dir_path + BLI_path_basename(tex_map.image_path.c_str())};
+ if (base_path != tex_path) {
+ image = load_image_at_path(bmain, base_path, relative_paths);
+ if (image != nullptr) {
+ return image;
+ }
}
- return false;
-}
-
-ShaderNodetreeWrap::ShaderNodetreeWrap(Main *bmain, const MTLMaterial &mtl_mat, Material *mat)
- : mtl_mat_(mtl_mat)
-{
- nodetree_.reset(ntreeAddTree(nullptr, "Shader Nodetree", ntreeType_Shader->idname));
- bsdf_ = add_node_to_tree(SH_NODE_BSDF_PRINCIPLED);
- shader_output_ = add_node_to_tree(SH_NODE_OUTPUT_MATERIAL);
-
- set_bsdf_socket_values(mat);
- add_image_textures(bmain, mat);
- link_sockets(bsdf_, "BSDF", shader_output_, "Surface", 4);
-
- nodeSetActive(nodetree_.get(), shader_output_);
+ image = create_placeholder_image(bmain, tex_path);
+ return image;
}
-/**
- * Assert if caller hasn't acquired nodetree.
- */
-ShaderNodetreeWrap::~ShaderNodetreeWrap()
-{
- if (nodetree_) {
- /* nodetree's ownership must be acquired by the caller. */
- nodetree_.reset();
- BLI_assert(0);
- }
-}
+/* Nodes are arranged in columns by type, with manually placed x coordinates
+ * based on node widths. */
+const float node_locx_texcoord = -880.0f;
+const float node_locx_mapping = -680.0f;
+const float node_locx_image = -480.0f;
+const float node_locx_normalmap = -200.0f;
+const float node_locx_bsdf = 0.0f;
+const float node_locx_output = 280.0f;
-bNodeTree *ShaderNodetreeWrap::get_nodetree()
-{
- /* If this function has been reached, we know that nodes and the nodetree
- * can be added to the scene safely. */
- return nodetree_.release();
-}
+/* Nodes are arranged in rows; one row for each image being used. */
+const float node_locy_top = 300.0f;
+const float node_locy_step = 300.0f;
-bNode *ShaderNodetreeWrap::add_node_to_tree(const int node_type)
+/* Add a node of the given type at the given location. */
+static bNode *add_node(bNodeTree *ntree, int type, float x, float y)
{
- return nodeAddStaticNode(nullptr, nodetree_.get(), node_type);
+ bNode *node = nodeAddStaticNode(nullptr, ntree, type);
+ node->locx = x;
+ node->locy = y;
+ return node;
}
-std::pair<float, float> ShaderNodetreeWrap::set_node_locations(const int pos_x)
+static void link_sockets(bNodeTree *ntree,
+ bNode *from_node,
+ const char *from_node_id,
+ bNode *to_node,
+ const char *to_node_id)
{
- int pos_y = 0;
- bool found = false;
- while (true) {
- for (Span<int> location : node_locations) {
- if (location[0] == pos_x && location[1] == pos_y) {
- pos_y += 1;
- found = true;
- }
- else {
- found = false;
- }
- }
- if (!found) {
- node_locations.append({pos_x, pos_y});
- return {pos_x * node_size_, pos_y * node_size_ * 2.0 / 3.0};
- }
- }
-}
-
-void ShaderNodetreeWrap::link_sockets(bNode *from_node,
- StringRef from_node_id,
- bNode *to_node,
- StringRef to_node_id,
- const int from_node_pos_x)
-{
- std::tie(from_node->locx, from_node->locy) = set_node_locations(from_node_pos_x);
- std::tie(to_node->locx, to_node->locy) = set_node_locations(from_node_pos_x + 1);
- bNodeSocket *from_sock{nodeFindSocket(from_node, SOCK_OUT, from_node_id.data())};
- bNodeSocket *to_sock{nodeFindSocket(to_node, SOCK_IN, to_node_id.data())};
+ bNodeSocket *from_sock{nodeFindSocket(from_node, SOCK_OUT, from_node_id)};
+ bNodeSocket *to_sock{nodeFindSocket(to_node, SOCK_IN, to_node_id)};
BLI_assert(from_sock && to_sock);
- nodeAddLink(nodetree_.get(), from_node, from_sock, to_node, to_sock);
+ nodeAddLink(ntree, from_node, from_sock, to_node, to_sock);
}
-void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
+static void set_bsdf_socket_values(bNode *bsdf, Material *mat, const MTLMaterial &mtl_mat)
{
- const int illum = mtl_mat_.illum;
+ const int illum = mtl_mat.illum_mode;
bool do_highlight = false;
bool do_tranparency = false;
bool do_reflection = false;
@@ -255,21 +244,23 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
/* Approximations for trying to map obj/mtl material model into
* Principled BSDF: */
/* Specular: average of Ks components. */
- float specular = (mtl_mat_.Ks[0] + mtl_mat_.Ks[1] + mtl_mat_.Ks[2]) / 3;
+ float specular = (mtl_mat.spec_color[0] + mtl_mat.spec_color[1] + mtl_mat.spec_color[2]) / 3;
if (specular < 0.0f) {
specular = do_highlight ? 1.0f : 0.0f;
}
/* Roughness: map 0..1000 range to 1..0 and apply non-linearity. */
float roughness;
- if (mtl_mat_.Ns < 0.0f) {
+ if (mtl_mat.spec_exponent < 0.0f) {
roughness = do_highlight ? 0.0f : 1.0f;
}
else {
- float clamped_ns = std::max(0.0f, std::min(1000.0f, mtl_mat_.Ns));
+ float clamped_ns = std::max(0.0f, std::min(1000.0f, mtl_mat.spec_exponent));
roughness = 1.0f - sqrt(clamped_ns / 1000.0f);
}
- /* Metallic: average of Ka components. */
- float metallic = (mtl_mat_.Ka[0] + mtl_mat_.Ka[1] + mtl_mat_.Ka[2]) / 3;
+ /* Metallic: average of `Ka` components. */
+ float metallic = (mtl_mat.ambient_color[0] + mtl_mat.ambient_color[1] +
+ mtl_mat.ambient_color[2]) /
+ 3;
if (do_reflection) {
if (metallic < 0.0f) {
metallic = 1.0f;
@@ -279,7 +270,7 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
metallic = 0.0f;
}
- float ior = mtl_mat_.Ni;
+ float ior = mtl_mat.ior;
if (ior < 0) {
if (do_tranparency) {
ior = 1.0f;
@@ -288,89 +279,153 @@ void ShaderNodetreeWrap::set_bsdf_socket_values(Material *mat)
ior = 1.5f;
}
}
- float alpha = mtl_mat_.d;
+ float alpha = mtl_mat.alpha;
if (do_tranparency && alpha < 0) {
alpha = 1.0f;
}
- float3 base_color = {mtl_mat_.Kd[0], mtl_mat_.Kd[1], mtl_mat_.Kd[2]};
+ /* PBR values, when present, override the ones calculated above. */
+ if (mtl_mat.roughness >= 0) {
+ roughness = mtl_mat.roughness;
+ }
+ if (mtl_mat.metallic >= 0) {
+ metallic = mtl_mat.metallic;
+ }
+
+ float3 base_color = mtl_mat.color;
if (base_color.x >= 0 && base_color.y >= 0 && base_color.z >= 0) {
- set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf_);
+ set_property_of_socket(SOCK_RGBA, "Base Color", {base_color, 3}, bsdf);
/* Viewport shading uses legacy r,g,b base color. */
mat->r = base_color.x;
mat->g = base_color.y;
mat->b = base_color.z;
}
- float3 emission_color = {mtl_mat_.Ke[0], mtl_mat_.Ke[1], mtl_mat_.Ke[2]};
+ float3 emission_color = mtl_mat.emission_color;
if (emission_color.x >= 0 && emission_color.y >= 0 && emission_color.z >= 0) {
- set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf_);
+ set_property_of_socket(SOCK_RGBA, "Emission", {emission_color, 3}, bsdf);
}
- if (mtl_mat_.texture_maps.contains_as(eMTLSyntaxElement::map_Ke)) {
- set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf_);
+ if (mtl_mat.tex_map_of_type(MTLTexMapType::Emission).is_valid()) {
+ set_property_of_socket(SOCK_FLOAT, "Emission Strength", {1.0f}, bsdf);
}
- set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf_);
- set_property_of_socket(SOCK_FLOAT, "Roughness", {roughness}, bsdf_);
+ set_property_of_socket(SOCK_FLOAT, "Specular", {specular}, bsdf);
+ set_property_of_socket(SOCK_FLOAT, "Roughness", {roughness}, bsdf);
mat->roughness = roughness;
- set_property_of_socket(SOCK_FLOAT, "Metallic", {metallic}, bsdf_);
+ set_property_of_socket(SOCK_FLOAT, "Metallic", {metallic}, bsdf);
mat->metallic = metallic;
if (ior != -1) {
- set_property_of_socket(SOCK_FLOAT, "IOR", {ior}, bsdf_);
+ set_property_of_socket(SOCK_FLOAT, "IOR", {ior}, bsdf);
}
if (alpha != -1) {
- set_property_of_socket(SOCK_FLOAT, "Alpha", {alpha}, bsdf_);
+ set_property_of_socket(SOCK_FLOAT, "Alpha", {alpha}, bsdf);
}
if (do_tranparency || (alpha >= 0.0f && alpha < 1.0f)) {
mat->blend_method = MA_BM_BLEND;
}
+
+ if (mtl_mat.sheen >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Sheen", {mtl_mat.sheen}, bsdf);
+ }
+ if (mtl_mat.cc_thickness >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Clearcoat", {mtl_mat.cc_thickness}, bsdf);
+ }
+ if (mtl_mat.cc_roughness >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Clearcoat Roughness", {mtl_mat.cc_roughness}, bsdf);
+ }
+ if (mtl_mat.aniso >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Anisotropic", {mtl_mat.aniso}, bsdf);
+ }
+ if (mtl_mat.aniso_rot >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Anisotropic Rotation", {mtl_mat.aniso_rot}, bsdf);
+ }
+
+ /* Transmission: average of transmission color. */
+ float transmission = (mtl_mat.transmit_color[0] + mtl_mat.transmit_color[1] +
+ mtl_mat.transmit_color[2]) /
+ 3;
+ if (transmission >= 0) {
+ set_property_of_socket(SOCK_FLOAT, "Transmission", {transmission}, bsdf);
+ }
}
-void ShaderNodetreeWrap::add_image_textures(Main *bmain, Material *mat)
+static void add_image_textures(Main *bmain,
+ bNodeTree *ntree,
+ bNode *bsdf,
+ Material *mat,
+ const MTLMaterial &mtl_mat,
+ bool relative_paths)
{
- for (const Map<const eMTLSyntaxElement, tex_map_XX>::Item texture_map :
- mtl_mat_.texture_maps.items()) {
- if (texture_map.value.image_path.empty()) {
+ float node_locy = node_locy_top;
+ for (int key = 0; key < (int)MTLTexMapType::Count; ++key) {
+ const MTLTexMap &value = mtl_mat.texture_maps[key];
+ if (!value.is_valid()) {
/* No Image texture node of this map type can be added to this material. */
continue;
}
- bNode *image_texture = add_node_to_tree(SH_NODE_TEX_IMAGE);
- if (!load_texture_image(bmain, texture_map.value, image_texture)) {
- /* Image could not be added, so don't add or link further nodes. */
+ Image *image = load_texture_image(bmain, value, relative_paths);
+ if (image == nullptr) {
continue;
}
+ bNode *image_node = add_node(ntree, SH_NODE_TEX_IMAGE, node_locx_image, node_locy);
+ BLI_assert(image_node);
+ image_node->id = &image->id;
+ static_cast<NodeTexImage *>(image_node->storage)->projection = value.projection_type;
+
/* Add normal map node if needed. */
bNode *normal_map = nullptr;
- if (texture_map.key == eMTLSyntaxElement::map_Bump) {
- normal_map = add_node_to_tree(SH_NODE_NORMAL_MAP);
- const float bump = std::max(0.0f, mtl_mat_.map_Bump_strength);
+ if (key == (int)MTLTexMapType::Normal) {
+ normal_map = add_node(ntree, SH_NODE_NORMAL_MAP, node_locx_normalmap, node_locy);
+ const float bump = std::max(0.0f, mtl_mat.normal_strength);
set_property_of_socket(SOCK_FLOAT, "Strength", {bump}, normal_map);
}
/* Add UV mapping & coordinate nodes only if needed. */
- if (texture_map.value.translation != float3(0, 0, 0) ||
- texture_map.value.scale != float3(1, 1, 1)) {
- bNode *mapping = add_node_to_tree(SH_NODE_MAPPING);
- bNode *texture_coordinate = add_node_to_tree(SH_NODE_TEX_COORD);
- set_property_of_socket(SOCK_VECTOR, "Location", {texture_map.value.translation, 3}, mapping);
- set_property_of_socket(SOCK_VECTOR, "Scale", {texture_map.value.scale, 3}, mapping);
-
- link_sockets(texture_coordinate, "UV", mapping, "Vector", 0);
- link_sockets(mapping, "Vector", image_texture, "Vector", 1);
+ if (value.translation != float3(0, 0, 0) || value.scale != float3(1, 1, 1)) {
+ bNode *texcoord = add_node(ntree, SH_NODE_TEX_COORD, node_locx_texcoord, node_locy);
+ bNode *mapping = add_node(ntree, SH_NODE_MAPPING, node_locx_mapping, node_locy);
+ set_property_of_socket(SOCK_VECTOR, "Location", {value.translation, 3}, mapping);
+ set_property_of_socket(SOCK_VECTOR, "Scale", {value.scale, 3}, mapping);
+
+ link_sockets(ntree, texcoord, "UV", mapping, "Vector");
+ link_sockets(ntree, mapping, "Vector", image_node, "Vector");
}
if (normal_map) {
- link_sockets(image_texture, "Color", normal_map, "Color", 2);
- link_sockets(normal_map, "Normal", bsdf_, "Normal", 3);
+ link_sockets(ntree, image_node, "Color", normal_map, "Color");
+ link_sockets(ntree, normal_map, "Normal", bsdf, "Normal");
}
- else if (texture_map.key == eMTLSyntaxElement::map_d) {
- link_sockets(image_texture, "Alpha", bsdf_, texture_map.value.dest_socket_id, 2);
+ else if (key == (int)MTLTexMapType::Alpha) {
+ link_sockets(ntree, image_node, "Alpha", bsdf, tex_map_type_to_socket_id[key]);
mat->blend_method = MA_BM_BLEND;
}
else {
- link_sockets(image_texture, "Color", bsdf_, texture_map.value.dest_socket_id, 2);
+ link_sockets(ntree, image_node, "Color", bsdf, tex_map_type_to_socket_id[key]);
}
+
+ /* Next layout row: goes downwards on the screen. */
+ node_locy -= node_locy_step;
}
}
+
+bNodeTree *create_mtl_node_tree(Main *bmain,
+ const MTLMaterial &mtl,
+ Material *mat,
+ bool relative_paths)
+{
+ bNodeTree *ntree = ntreeAddTreeEmbedded(
+ nullptr, &mat->id, "Shader Nodetree", ntreeType_Shader->idname);
+
+ bNode *bsdf = add_node(ntree, SH_NODE_BSDF_PRINCIPLED, node_locx_bsdf, node_locy_top);
+ bNode *output = add_node(ntree, SH_NODE_OUTPUT_MATERIAL, node_locx_output, node_locy_top);
+
+ set_bsdf_socket_values(bsdf, mat, mtl);
+ add_image_textures(bmain, ntree, bsdf, mat, mtl, relative_paths);
+ link_sockets(ntree, bsdf, "BSDF", output, "Surface");
+ nodeSetActive(ntree, output);
+
+ return ntree;
+}
+
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh b/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
index 12bea1cdc5a..a3ba803e921 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_mtl.hh
@@ -1,93 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-/** \file
- * \ingroup obj
- */
-
#pragma once
-#include <array>
-
-#include "BLI_map.hh"
-#include "BLI_math_vec_types.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_vector.hh"
-
#include "DNA_node_types.h"
-#include "MEM_guardedalloc.h"
-
-#include "obj_export_mtl.hh"
+struct Main;
+struct Material;
namespace blender::io::obj {
-struct UniqueNodetreeDeleter {
- void operator()(bNodeTree *node)
- {
- MEM_freeN(node);
- }
-};
-
-using unique_nodetree_ptr = std::unique_ptr<bNodeTree, UniqueNodetreeDeleter>;
-
-class ShaderNodetreeWrap {
- private:
- /* Node arrangement:
- * Texture Coordinates -> Mapping -> Image Texture -> (optional) Normal Map -> p-BSDF -> Material
- * Output. */
- unique_nodetree_ptr nodetree_;
- bNode *bsdf_;
- bNode *shader_output_;
- const MTLMaterial &mtl_mat_;
-
- /* List of all locations occupied by nodes. */
- Vector<std::array<int, 2>> node_locations;
- const float node_size_{300.f};
-
- public:
- /**
- * Initializes a nodetree with a p-BSDF node's BSDF socket connected to shader output node's
- * surface socket.
- */
- ShaderNodetreeWrap(Main *bmain, const MTLMaterial &mtl_mat, Material *mat);
- ~ShaderNodetreeWrap();
-
- /**
- * Release nodetree for materials to own it. nodetree has its unique deleter
- * if destructor is not reached for some reason.
- */
- bNodeTree *get_nodetree();
+struct MTLMaterial;
- private:
- /**
- * Add a new static node to the tree.
- * No two nodes are linked here.
- */
- bNode *add_node_to_tree(const int node_type);
- /**
- * Return x-y coordinates for a node where y is determined by other nodes present in
- * the same vertical column.
- */
- std::pair<float, float> set_node_locations(const int pos_x);
- /**
- * Link two nodes by the sockets of given IDs.
- * Also releases the ownership of the "from" node for nodetree to free it.
- * \param from_node_pos_x: 0 to 4 value as per nodetree arrangement.
- */
- void link_sockets(bNode *from_node,
- StringRef from_node_id,
- bNode *to_node,
- StringRef to_node_id,
- const int from_node_pos_x);
- /**
- * Set values of sockets in p-BSDF node of the nodetree.
- */
- void set_bsdf_socket_values(Material *mat);
- /**
- * Create image texture, vector and normal mapping nodes from MTL materials and link the
- * nodes to p-BSDF node.
- */
- void add_image_textures(Main *bmain, Material *mat);
-};
+bNodeTree *create_mtl_node_tree(Main *bmain,
+ const MTLMaterial &mtl_mat,
+ Material *mat,
+ bool relative_paths);
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
index 3d6733d661e..04d9a665588 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_objects.hh
@@ -9,9 +9,10 @@
#include "BKE_lib_id.h"
#include "BLI_map.hh"
+#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
+#include "BLI_set.hh"
#include "BLI_vector.hh"
-#include "BLI_vector_set.hh"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -19,35 +20,24 @@
namespace blender::io::obj {
/**
- * List of all vertex and UV vertex coordinates in an OBJ file accessible to any
- * Geometry instance at any time.
+ * All vertex positions, normals, UVs, colors in the OBJ file.
*/
struct GlobalVertices {
Vector<float3> vertices;
Vector<float2> uv_vertices;
Vector<float3> vertex_normals;
- Vector<float3> vertex_colors;
-};
-/**
- * Keeps track of the vertices that belong to other Geometries.
- * Needed only for MLoop.v and MEdge.v1 which needs vertex indices ranging from (0 to total
- * vertices in the mesh) as opposed to the other OBJ indices ranging from (0 to total vertices
- * in the global list).
- */
-struct VertexIndexOffset {
- private:
- int offset_ = 0;
-
- public:
- void set_index_offset(const int64_t total_vertices)
- {
- offset_ = total_vertices;
- }
- int64_t get_index_offset() const
- {
- return offset_;
- }
+ /**
+ * Vertex colors might not be present in the file at all, or only
+ * provided for some meshes. Store them in chunks as they are
+ * spelled out in the file, e.g. if there are 10 vertices in sequence, all
+ * with `xyzrgb` colors, they will be one block.
+ */
+ struct VertexColorsBlock {
+ Vector<float3> colors;
+ int start_vertex_index;
+ };
+ Vector<VertexColorsBlock> vertex_colors;
};
/**
@@ -101,21 +91,42 @@ struct Geometry {
Map<std::string, int> material_indices_;
Vector<std::string> material_order_;
- int vertex_start_ = 0;
- int vertex_count_ = 0;
- int vertex_color_start_ = 0;
- int vertex_color_count_ = 0;
- /** Edges written in the file in addition to (or even without polygon) elements. */
+ int vertex_index_min_ = INT_MAX;
+ int vertex_index_max_ = -1;
+ /* Global vertex indices used by this geometry. */
+ Set<int> vertices_;
+ /* Mapping from global vertex index to geometry-local vertex index. */
+ Map<int, int> global_to_local_vertices_;
+ /* Loose edges in the file. */
Vector<MEdge> edges_;
Vector<PolyCorner> face_corners_;
Vector<PolyElem> face_elements_;
bool has_invalid_polys_ = false;
- bool has_vertex_normals_ = false;
bool has_vertex_groups_ = false;
NurbsElement nurbs_element_;
int total_loops_ = 0;
+
+ int get_vertex_count() const
+ {
+ return (int)vertices_.size();
+ }
+ void track_vertex_index(int index)
+ {
+ vertices_.add(index);
+ math::min_inplace(vertex_index_min_, index);
+ math::max_inplace(vertex_index_max_, index);
+ }
+ void track_all_vertices(int count)
+ {
+ vertices_.reserve(count);
+ for (int i = 0; i < count; ++i) {
+ vertices_.add(i);
+ }
+ vertex_index_min_ = 0;
+ vertex_index_max_ = count - 1;
+ }
};
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc
index bc9006e1051..7e282b164b0 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc
@@ -18,14 +18,12 @@ StringRef read_next_line(StringRef &buffer)
const char *start = buffer.begin();
const char *end = buffer.end();
size_t len = 0;
- char prev = 0;
const char *ptr = start;
while (ptr < end) {
char c = *ptr++;
- if (c == '\n' && prev != '\\') {
+ if (c == '\n') {
break;
}
- prev = c;
++len;
}
@@ -35,7 +33,29 @@ StringRef read_next_line(StringRef &buffer)
static bool is_whitespace(char c)
{
- return c <= ' ' || c == '\\';
+ return c <= ' ';
+}
+
+void fixup_line_continuations(char *p, char *end)
+{
+ while (true) {
+ /* Find next backslash, if any. */
+ char *backslash = std::find(p, end, '\\');
+ if (backslash == end) {
+ break;
+ }
+ /* Skip over possible whitespace right after it. */
+ p = backslash + 1;
+ while (p < end && is_whitespace(*p) && *p != '\n') {
+ ++p;
+ }
+ /* If then we have a newline, turn both backslash
+ * and the newline into regular spaces. */
+ if (p < end && *p == '\n') {
+ *backslash = ' ';
+ *p = ' ';
+ }
+ }
}
const char *drop_whitespace(const char *p, const char *end)
diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh
index f6dd1a6b675..e42f5080d25 100644
--- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh
+++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.hh
@@ -6,9 +6,6 @@
/*
* Various text parsing utilities used by OBJ importer.
- * The utilities are not directly usable by other formats, since
- * they treat backslash (\) as a whitespace character (OBJ format
- * allows backslashes to function as a line-continuation character).
*
* Many of these functions take two pointers (p, end) indicating
* which part of a string to operate on, and return a possibly
@@ -27,21 +24,22 @@ namespace blender::io::obj {
* The returned line will not have '\n' characters at the end;
* the `buffer` is modified to contain remaining text without
* the input line.
- *
- * Note that backslash (\) character is treated as a line
- * continuation.
*/
StringRef read_next_line(StringRef &buffer);
/**
+ * Fix up OBJ line continuations by replacing backslash (\) and the
+ * following newline with spaces.
+ */
+void fixup_line_continuations(char *p, char *end);
+
+/**
* Drop leading white-space from a string part.
- * Note that backslash character is considered white-space.
*/
const char *drop_whitespace(const char *p, const char *end);
/**
* Drop leading non-white-space from a string part.
- * Note that backslash character is considered white-space.
*/
const char *drop_non_whitespace(const char *p, const char *end);
diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc
index f2051d195c8..ccbcce64d65 100644
--- a/source/blender/io/wavefront_obj/importer/obj_importer.cc
+++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc
@@ -9,6 +9,7 @@
#include "BLI_map.hh"
#include "BLI_math_vec_types.hh"
#include "BLI_set.hh"
+#include "BLI_sort.hh"
#include "BLI_string_ref.hh"
#include "BKE_layer.h"
@@ -18,6 +19,7 @@
#include "DNA_collection_types.h"
+#include "obj_export_mtl.hh"
#include "obj_import_file_reader.hh"
#include "obj_import_mesh.hh"
#include "obj_import_nurbs.hh"
@@ -38,12 +40,20 @@ static void geometry_to_blender_objects(Main *bmain,
Map<std::string, std::unique_ptr<MTLMaterial>> &materials,
Map<std::string, Material *> &created_materials)
{
- BKE_view_layer_base_deselect_all(view_layer);
LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
/* Don't do collection syncs for each object, will do once after the loop. */
BKE_layer_collection_resync_forbid();
+ /* Sort objects by name: creating many objects is much faster if the creation
+ * order is sorted by name. */
+ blender::parallel_sort(
+ all_geometries.begin(), all_geometries.end(), [](const auto &a, const auto &b) {
+ const char *na = a ? a->geometry_name_.c_str() : "";
+ const char *nb = b ? b->geometry_name_.c_str() : "";
+ return BLI_strcasecmp(na, nb) < 0;
+ });
+
/* Create all the objects. */
Vector<Object *> objects;
objects.reserve(all_geometries.size());
@@ -66,6 +76,7 @@ static void geometry_to_blender_objects(Main *bmain,
/* Sync the collection after all objects are created. */
BKE_layer_collection_resync_allow();
BKE_main_collection_sync(bmain);
+ BKE_view_layer_synced_ensure(scene, view_layer);
/* After collection sync, select objects in the view layer and do DEG updates. */
for (Object *obj : objects) {
@@ -112,6 +123,9 @@ void importer_main(Main *bmain,
mtl_parser.parse_and_store(materials);
}
+ if (import_params.clear_selection) {
+ BKE_view_layer_base_deselect_all(scene, view_layer);
+ }
geometry_to_blender_objects(bmain,
scene,
view_layer,
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
index 6aec848573f..dcba78ac99e 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.cc
@@ -60,7 +60,7 @@ TEST_F(obj_exporter_test, filter_objects_curves_as_mesh)
return;
}
auto [objmeshes, objcurves]{filter_supported_objects(depsgraph, _export.params)};
- EXPECT_EQ(objmeshes.size(), 20);
+ EXPECT_EQ(objmeshes.size(), 21);
EXPECT_EQ(objcurves.size(), 0);
}
@@ -185,17 +185,17 @@ TEST(obj_exporter_writer, mtllib)
TEST(obj_exporter_writer, format_handler_buffer_chunking)
{
/* Use a tiny buffer chunk size, so that the test below ends up creating several blocks. */
- FormatHandler<eFileType::OBJ, 16> h;
- h.write<eOBJSyntaxElement::object_name>("abc");
- h.write<eOBJSyntaxElement::object_name>("abcd");
- h.write<eOBJSyntaxElement::object_name>("abcde");
- h.write<eOBJSyntaxElement::object_name>("abcdef");
- h.write<eOBJSyntaxElement::object_name>("012345678901234567890123456789abcd");
- h.write<eOBJSyntaxElement::object_name>("123");
- h.write<eOBJSyntaxElement::curve_element_begin>();
- h.write<eOBJSyntaxElement::new_line>();
- h.write<eOBJSyntaxElement::nurbs_parameter_begin>();
- h.write<eOBJSyntaxElement::new_line>();
+ FormatHandler h(16);
+ h.write_obj_object("abc");
+ h.write_obj_object("abcd");
+ h.write_obj_object("abcde");
+ h.write_obj_object("abcdef");
+ h.write_obj_object("012345678901234567890123456789abcd");
+ h.write_obj_object("123");
+ h.write_obj_curve_begin();
+ h.write_obj_newline();
+ h.write_obj_nurbs_parm_begin();
+ h.write_obj_newline();
size_t got_blocks = h.get_block_count();
ASSERT_EQ(got_blocks, 7);
@@ -527,4 +527,27 @@ TEST_F(obj_exporter_regression_test, all_objects_mat_groups)
_export.params);
}
+TEST_F(obj_exporter_regression_test, materials_without_pbr)
+{
+ OBJExportParamsDefault _export;
+ _export.params.export_normals = false;
+ _export.params.path_mode = PATH_REFERENCE_RELATIVE;
+ compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend",
+ "io_tests/obj/materials_without_pbr.obj",
+ "io_tests/obj/materials_without_pbr.mtl",
+ _export.params);
+}
+
+TEST_F(obj_exporter_regression_test, materials_pbr)
+{
+ OBJExportParamsDefault _export;
+ _export.params.export_normals = false;
+ _export.params.path_mode = PATH_REFERENCE_RELATIVE;
+ _export.params.export_pbr_extensions = true;
+ compare_obj_export_to_golden("io_tests/blend_geometry/materials_pbr.blend",
+ "io_tests/obj/materials_pbr.obj",
+ "io_tests/obj/materials_pbr.mtl",
+ _export.params);
+}
+
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
index 7d3b41ed527..006d86312b6 100644
--- a/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
+++ b/source/blender/io/wavefront_obj/tests/obj_exporter_tests.hh
@@ -31,6 +31,7 @@ struct OBJExportParamsDefault {
params.path_mode = PATH_REFERENCE_AUTO;
params.export_triangulated_mesh = false;
params.export_curves_as_nurbs = false;
+ params.export_pbr_extensions = false;
params.export_object_groups = false;
params.export_material_groups = false;
diff --git a/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc
index e9747b437cc..dc1cfd2b449 100644
--- a/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_import_string_utils_tests.cc
@@ -10,17 +10,34 @@ namespace blender::io::obj {
TEST(obj_import_string_utils, read_next_line)
{
- std::string str = "abc\n \n\nline with \\\ncontinuation\nCRLF ending:\r\na";
+ std::string str = "abc\n \n\nline with \t spaces\nCRLF ending:\r\na";
StringRef s = str;
EXPECT_STRREF_EQ("abc", read_next_line(s));
EXPECT_STRREF_EQ(" ", read_next_line(s));
EXPECT_STRREF_EQ("", read_next_line(s));
- EXPECT_STRREF_EQ("line with \\\ncontinuation", read_next_line(s));
+ EXPECT_STRREF_EQ("line with \t spaces", read_next_line(s));
EXPECT_STRREF_EQ("CRLF ending:\r", read_next_line(s));
EXPECT_STRREF_EQ("a", read_next_line(s));
EXPECT_TRUE(s.is_empty());
}
+TEST(obj_import_string_utils, fixup_line_continuations)
+{
+ const char *str =
+ "backslash \\\n eol\n"
+ "backslash spaces \\ \n eol\n"
+ "without eol \\ is \\\\ \\ left intact\n"
+ "\\";
+ const char *exp =
+ "backslash eol\n"
+ "backslash spaces eol\n"
+ "without eol \\ is \\\\ \\ left intact\n"
+ "\\";
+ std::string buf(str);
+ fixup_line_continuations(buf.data(), buf.data() + buf.size());
+ EXPECT_STRREF_EQ(exp, buf);
+}
+
static StringRef drop_whitespace(StringRef s)
{
return StringRef(drop_whitespace(s.begin(), s.end()), s.end());
@@ -54,7 +71,7 @@ TEST(obj_import_string_utils, drop_whitespace)
/* No leading whitespace */
EXPECT_STRREF_EQ("c", drop_whitespace("c"));
/* Case with backslash, should be treated as whitespace */
- EXPECT_STRREF_EQ("d", drop_whitespace(" \\ d"));
+ EXPECT_STRREF_EQ("d", drop_whitespace(" \t d"));
}
TEST(obj_import_string_utils, parse_int_valid)
diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
index eeb81f5e23e..99e12aed99c 100644
--- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc
@@ -8,11 +8,12 @@
#include "BKE_curve.h"
#include "BKE_customdata.h"
#include "BKE_main.h"
+#include "BKE_mesh.h"
#include "BKE_object.h"
#include "BKE_scene.h"
#include "BLI_listbase.h"
-#include "BLI_math_base.h"
+#include "BLI_math_base.hh"
#include "BLI_math_vec_types.hh"
#include "BLO_readfile.h"
@@ -47,7 +48,8 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
void import_and_check(const char *path,
const Expectation *expect,
size_t expect_count,
- int expect_mat_count)
+ int expect_mat_count,
+ int expect_image_count = 0)
{
if (!blendfile_load("io_tests/blend_geometry/all_quads.blend")) {
ADD_FAILURE();
@@ -60,6 +62,8 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
params.up_axis = IO_AXIS_Y;
params.validate_meshes = true;
params.import_vertex_groups = false;
+ params.relative_paths = true;
+ params.clear_selection = true;
std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path;
strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1);
@@ -92,8 +96,9 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
EXPECT_EQ(mesh->totedge, exp.mesh_totedge_or_curve_endp);
EXPECT_EQ(mesh->totpoly, exp.mesh_totpoly_or_curve_order);
EXPECT_EQ(mesh->totloop, exp.mesh_totloop_or_curve_cyclic);
- EXPECT_V3_NEAR(mesh->mvert[0].co, exp.vert_first, 0.0001f);
- EXPECT_V3_NEAR(mesh->mvert[mesh->totvert - 1].co, exp.vert_last, 0.0001f);
+ const Span<MVert> verts = mesh->verts();
+ EXPECT_V3_NEAR(verts.first().co, exp.vert_first, 0.0001f);
+ EXPECT_V3_NEAR(verts.last().co, exp.vert_last, 0.0001f);
const float3 *lnors = (const float3 *)(CustomData_get_layer(&mesh->ldata, CD_NORMAL));
float3 normal_first = lnors != nullptr ? lnors[0] : float3(0, 0, 0);
EXPECT_V3_NEAR(normal_first, exp.normal_first, 0.0001f);
@@ -132,12 +137,12 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
DEG_OBJECT_ITER_END;
EXPECT_EQ(object_index, expect_count);
- /* Count number of materials. */
- int mat_count = 0;
- LISTBASE_FOREACH (ID *, id, &bfile->main->materials) {
- ++mat_count;
- }
+ /* Check number of materials & textures. */
+ const int mat_count = BLI_listbase_count(&bfile->main->materials);
EXPECT_EQ(mat_count, expect_mat_count);
+
+ const int ima_count = BLI_listbase_count(&bfile->main->images);
+ EXPECT_EQ(ima_count, expect_image_count);
}
};
@@ -158,6 +163,36 @@ TEST_F(obj_importer_test, import_cube)
import_and_check("cube.obj", expect, std::size(expect), 1);
}
+TEST_F(obj_importer_test, import_cube_o_after_verts)
+{
+ Expectation expect[] = {
+ {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ {
+ "OBActualCube",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(-1, -1, 1),
+ float3(1, -1, -1),
+ float3(0, 0, 1),
+ },
+ {
+ "OBSparseTri",
+ OB_MESH,
+ 3,
+ 3,
+ 1,
+ 3,
+ float3(1, -1, 1),
+ float3(-2, -2, 2),
+ float3(-0.2357f, 0.9428f, 0.2357f),
+ },
+ };
+ import_and_check("cube_o_after_verts.obj", expect, std::size(expect), 2);
+}
+
TEST_F(obj_importer_test, import_suzanne_all_data)
{
Expectation expect[] = {
@@ -196,7 +231,9 @@ TEST_F(obj_importer_test, import_nurbs_curves)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ {"OBCurveDeg3", OB_CURVES_LEGACY, 4, 0, 3, 0, float3(10, -2, 0), float3(6, -2, 0)},
{"OBnurbs_curves", OB_CURVES_LEGACY, 4, 0, 4, 0, float3(2, -2, 0), float3(-2, -2, 0)},
+ {"OBNurbsCurveCyclic", OB_CURVES_LEGACY, 7, 0, 4, 1, float3(-2, -2, 0), float3(-6, 2, 0)},
{"OBNurbsCurveDiffWeights",
OB_CURVES_LEGACY,
4,
@@ -205,7 +242,6 @@ TEST_F(obj_importer_test, import_nurbs_curves)
0,
float3(6, -2, 0),
float3(2, -2, 0)},
- {"OBNurbsCurveCyclic", OB_CURVES_LEGACY, 7, 0, 4, 1, float3(-2, -2, 0), float3(-6, 2, 0)},
{"OBNurbsCurveEndpoint",
OB_CURVES_LEGACY,
4,
@@ -214,7 +250,6 @@ TEST_F(obj_importer_test, import_nurbs_curves)
0,
float3(-6, -2, 0),
float3(-10, -2, 0)},
- {"OBCurveDeg3", OB_CURVES_LEGACY, 4, 0, 3, 0, float3(10, -2, 0), float3(6, -2, 0)},
};
import_and_check("nurbs_curves.obj", expect, std::size(expect), 0);
}
@@ -239,7 +274,8 @@ TEST_F(obj_importer_test, import_nurbs_manual)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBCurve_Uniform_Parm", OB_CURVES_LEGACY, 5, 0, 4, 0, float3(-2, 0, 2), float3(-2, 0, 2)},
+ {"OBCurve_Cyclic", OB_CURVES_LEGACY, 7, 0, 4, 1, float3(-2, 0, 2), float3(2, 0, -2)},
+ {"OBCurve_Endpoints", OB_CURVES_LEGACY, 5, 1, 4, 0, float3(-2, 0, 2), float3(-2, 0, 2)},
{"OBCurve_NonUniform_Parm",
OB_CURVES_LEGACY,
5,
@@ -248,8 +284,7 @@ TEST_F(obj_importer_test, import_nurbs_manual)
0,
float3(-2, 0, 2),
float3(-2, 0, 2)},
- {"OBCurve_Endpoints", OB_CURVES_LEGACY, 5, 1, 4, 0, float3(-2, 0, 2), float3(-2, 0, 2)},
- {"OBCurve_Cyclic", OB_CURVES_LEGACY, 7, 0, 4, 1, float3(-2, 0, 2), float3(2, 0, -2)},
+ {"OBCurve_Uniform_Parm", OB_CURVES_LEGACY, 5, 0, 4, 0, float3(-2, 0, 2), float3(-2, 0, 2)},
};
import_and_check("nurbs_manual.obj", expect, std::size(expect), 0);
}
@@ -258,7 +293,7 @@ TEST_F(obj_importer_test, import_nurbs_mesh)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBTorus Knot",
+ {"OBTorus_Knot",
OB_MESH,
108,
108,
@@ -276,30 +311,69 @@ TEST_F(obj_importer_test, import_materials)
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
{"OBmaterials", OB_MESH, 8, 12, 6, 24, float3(-1, -1, 1), float3(1, -1, -1)},
};
- import_and_check("materials.obj", expect, std::size(expect), 4);
+ import_and_check("materials.obj", expect, std::size(expect), 4, 8);
}
-TEST_F(obj_importer_test, import_faces_invalid_or_with_holes)
+TEST_F(obj_importer_test, import_cubes_with_textures_rel)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBFaceWithHole_BecomesTwoFacesFormingAHole",
+ {"OBCube4Tex",
OB_MESH,
8,
- 10,
- 2,
12,
- float3(-2, 0, -2),
- float3(1, 0, -1)},
- {"OBFaceQuadDupSomeVerts_BecomesOneQuadUsing4Verts",
+ 6,
+ 24,
+ float3(1, 1, -1),
+ float3(-1, -1, 1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ {"OBCubeTexMul",
OB_MESH,
8,
- 4,
+ 12,
+ 6,
+ 24,
+ float3(4, -2, -1),
+ float3(2, -4, 1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ {"OBCubeTiledTex",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(4, 1, -1),
+ float3(2, -1, 1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ {"OBCubeTiledTexFromAnotherFolder",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(7, 1, -1),
+ float3(5, -1, 1),
+ float3(0, 1, 0),
+ float2(0.9935f, 0.0020f)},
+ };
+ import_and_check("cubes_with_textures_rel.obj", expect, std::size(expect), 4, 4);
+}
+
+TEST_F(obj_importer_test, import_faces_invalid_or_with_holes)
+{
+ Expectation expect[] = {
+ {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ {"OBFaceAllVerts_BecomesOneOverlappingFaceUsingAllVerts",
+ OB_MESH,
+ 8,
+ 8,
1,
- 4,
- float3(3, 0, -2),
- float3(6, 0, -1)},
- {"OBFaceTriDupVert_Becomes1Tri", OB_MESH, 8, 3, 1, 3, float3(-2, 0, 3), float3(1, 0, 4)},
+ 8,
+ float3(8, 0, -2),
+ float3(11, 0, -1)},
{"OBFaceAllVertsDup_BecomesOneOverlappingFaceUsingAllVerts",
OB_MESH,
8,
@@ -308,15 +382,24 @@ TEST_F(obj_importer_test, import_faces_invalid_or_with_holes)
8,
float3(3, 0, 3),
float3(6, 0, 4)},
- {"OBFaceAllVerts_BecomesOneOverlappingFaceUsingAllVerts",
+ {"OBFaceJustTwoVerts_IsSkipped", OB_MESH, 2, 0, 0, 0, float3(8, 0, 3), float3(8, 0, 7)},
+ {"OBFaceQuadDupSomeVerts_BecomesOneQuadUsing4Verts",
OB_MESH,
- 8,
- 8,
+ 4,
+ 4,
1,
+ 4,
+ float3(3, 0, -2),
+ float3(7, 0, -2)},
+ {"OBFaceTriDupVert_Becomes1Tri", OB_MESH, 3, 3, 1, 3, float3(-2, 0, 3), float3(2, 0, 7)},
+ {"OBFaceWithHole_BecomesTwoFacesFormingAHole",
+ OB_MESH,
8,
- float3(8, 0, -2),
- float3(11, 0, -1)},
- {"OBFaceJustTwoVerts_IsSkipped", OB_MESH, 8, 0, 0, 0, float3(8, 0, 3), float3(11, 0, 4)},
+ 10,
+ 2,
+ 12,
+ float3(-2, 0, -2),
+ float3(1, 0, -1)},
};
import_and_check("faces_invalid_or_with_holes.obj", expect, std::size(expect), 0);
}
@@ -327,12 +410,12 @@ TEST_F(obj_importer_test, import_invalid_indices)
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
{"OBQuad",
OB_MESH,
- 4,
+ 3,
3,
1,
3,
float3(-2, 0, -2),
- float3(2, 0, -2),
+ float3(2, 0, 2),
float3(0, 1, 0),
float2(0.5f, 0.25f)},
};
@@ -345,12 +428,12 @@ TEST_F(obj_importer_test, import_invalid_syntax)
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
{"OBObjectWithAReallyLongNameToCheckHowImportHandlesNamesThatAreLon",
OB_MESH,
- 10, /* NOTE: right now parses some invalid obj syntax as valid vertices. */
+ 3,
3,
1,
3,
float3(1, 2, 3),
- float3(10, 11, 12),
+ float3(7, 8, 9),
float3(0, 1, 0),
float2(0.5f, 0.25f)},
};
@@ -362,6 +445,63 @@ TEST_F(obj_importer_test, import_all_objects)
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
/* .obj file has empty EmptyText and EmptyMesh objects; these are ignored and skipped */
+ {"OBBezierCurve", OB_MESH, 13, 12, 0, 0, float3(-1, -2, 0), float3(1, -2, 0)},
+ {"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, -1), float3(-1, 1, 1), float3(0, 0, 1)},
+ {"OBMaterialCube",
+ OB_MESH,
+ 8,
+ 13,
+ 7,
+ 26,
+ float3(28, 1, -1),
+ float3(26, 1, 1),
+ float3(-1, 0, 0)},
+ {"OBNurbsCircle",
+ OB_MESH,
+ 96,
+ 96,
+ 0,
+ 0,
+ float3(3.292893f, -2.707107f, 0),
+ float3(3.369084f, -2.77607f, 0)},
+ {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(2, -3, 0), float3(3, -2, 0)},
+ {"OBParticleCube",
+ OB_MESH,
+ 8,
+ 13,
+ 7,
+ 26,
+ float3(22, 1, -1),
+ float3(20, 1, 1),
+ float3(0, 0, 1)},
+ {"OBShapeKeyCube",
+ OB_MESH,
+ 8,
+ 13,
+ 7,
+ 26,
+ float3(19, 1, -1),
+ float3(17, 1, 1),
+ float3(-0.4082f, -0.4082f, 0.8165f)},
+ {"OBSmoothCube",
+ OB_MESH,
+ 8,
+ 13,
+ 7,
+ 26,
+ float3(4, 1, -1),
+ float3(2, 1, 1),
+ float3(0.5774f, 0.5773f, 0.5774f)},
+ {"OBSurface",
+ OB_MESH,
+ 256,
+ 480,
+ 224,
+ 896,
+ float3(7.292893f, -2.707107f, -1),
+ float3(7.525872f, -2.883338f, 1),
+ float3(-0.7071f, -0.7071f, 0),
+ float2(0, 0.142857f)},
{"OBSurfPatch",
OB_MESH,
256,
@@ -382,24 +522,16 @@ TEST_F(obj_importer_test, import_all_objects)
float3(11, -2, 1),
float3(-0.0541f, -0.0541f, -0.9971f),
float2(0, 1)},
- {"OBSmoothCube",
- OB_MESH,
- 8,
- 13,
- 7,
- 26,
- float3(4, 1, -1),
- float3(2, 1, 1),
- float3(0.5774f, 0.5773f, 0.5774f)},
- {"OBMaterialCube",
+ {"OBSurfTorus.001",
OB_MESH,
- 8,
- 13,
- 7,
- 26,
- float3(28, 1, -1),
- float3(26, 1, 1),
- float3(-1, 0, 0)},
+ 1024,
+ 2048,
+ 1024,
+ 4096,
+ float3(5.34467f, -2.65533f, -0.176777f),
+ float3(5.232792f, -2.411795f, -0.220835f),
+ float3(-0.5042f, -0.5042f, -0.7011f),
+ float2(0, 1)},
{"OBTaperCube",
OB_MESH,
106,
@@ -409,24 +541,26 @@ TEST_F(obj_importer_test, import_all_objects)
float3(24.444445f, 0.502543f, -0.753814f),
float3(23.790743f, 0.460522f, -0.766546f),
float3(-0.0546f, 0.1716f, 0.9837f)},
- {"OBParticleCube",
+ {"OBText",
OB_MESH,
- 8,
- 13,
- 7,
- 26,
- float3(22, 1, -1),
- float3(20, 1, 1),
- float3(0, 0, 1)},
- {"OBShapeKeyCube",
+ 177,
+ 345,
+ 171,
+ 513,
+ float3(1.75f, -9.458f, 0),
+ float3(0.587f, -9.406f, 0),
+ float3(0, 0, 1),
+ float2(0.017544f, 0)},
+ {"OBUVCube",
OB_MESH,
8,
13,
7,
26,
- float3(19, 1, -1),
- float3(17, 1, 1),
- float3(-0.4082f, -0.4082f, 0.8165f)},
+ float3(7, 1, -1),
+ float3(5, 1, 1),
+ float3(0, 0, 1),
+ float2(0.654526f, 0.579873f)},
{"OBUVImageCube",
OB_MESH,
8,
@@ -437,15 +571,6 @@ TEST_F(obj_importer_test, import_all_objects)
float3(8, 1, 1),
float3(0, 0, 1),
float2(0.654526f, 0.579873f)},
- {"OBVGroupCube",
- OB_MESH,
- 8,
- 13,
- 7,
- 26,
- float3(16, 1, -1),
- float3(14, 1, 1),
- float3(0, 0, 1)},
{"OBVColCube",
OB_MESH,
8,
@@ -457,57 +582,15 @@ TEST_F(obj_importer_test, import_all_objects)
float3(0, 0, 1),
float2(0, 0),
float4(0.0f, 0.002125f, 1.0f, 1.0f)},
- {"OBUVCube",
+ {"OBVGroupCube",
OB_MESH,
8,
13,
7,
26,
- float3(7, 1, -1),
- float3(5, 1, 1),
- float3(0, 0, 1),
- float2(0.654526f, 0.579873f)},
- {"OBNurbsCircle.001", OB_MESH, 4, 4, 0, 0, float3(2, -3, 0), float3(3, -2, 0)},
- {"OBSurface",
- OB_MESH,
- 256,
- 480,
- 224,
- 896,
- float3(7.292893f, -2.707107f, -1),
- float3(7.525872f, -2.883338f, 1),
- float3(-0.7071f, -0.7071f, 0),
- float2(0, 0.142857f)},
- {"OBText",
- OB_MESH,
- 177,
- 345,
- 171,
- 513,
- float3(1.75f, -9.458f, 0),
- float3(0.587f, -9.406f, 0),
- float3(0, 0, 1),
- float2(0.017544f, 0)},
- {"OBSurfTorus.001",
- OB_MESH,
- 1024,
- 2048,
- 1024,
- 4096,
- float3(5.34467f, -2.65533f, -0.176777f),
- float3(5.232792f, -2.411795f, -0.220835f),
- float3(-0.5042f, -0.5042f, -0.7011f),
- float2(0, 1)},
- {"OBNurbsCircle",
- OB_MESH,
- 96,
- 96,
- 0,
- 0,
- float3(3.292893f, -2.707107f, 0),
- float3(3.369084f, -2.77607f, 0)},
- {"OBBezierCurve", OB_MESH, 13, 12, 0, 0, float3(-1, -2, 0), float3(1, -2, 0)},
- {"OBBlankCube", OB_MESH, 8, 13, 7, 26, float3(1, 1, -1), float3(-1, 1, 1), float3(0, 0, 1)},
+ float3(16, 1, -1),
+ float3(14, 1, 1),
+ float3(0, 0, 1)},
};
import_and_check("all_objects.obj", expect, std::size(expect), 7);
}
@@ -516,28 +599,6 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
{
Expectation expect[] = {
{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBCubeVertexByte",
- OB_MESH,
- 8,
- 12,
- 6,
- 24,
- float3(1.0f, 1.0f, -1.0f),
- float3(-1.0f, -1.0f, 1.0f),
- float3(0, 0, 0),
- float2(0, 0),
- float4(0.846873f, 0.027321f, 0.982123f, 1.0f)},
- {"OBCubeVertexFloat",
- OB_MESH,
- 8,
- 12,
- 6,
- 24,
- float3(3.392028f, 1.0f, -1.0f),
- float3(1.392028f, -1.0f, 1.0f),
- float3(0, 0, 0),
- float2(0, 0),
- float4(49.99467f, 0.027321f, 0.982123f, 1.0f)},
{"OBCubeCornerByte",
OB_MESH,
8,
@@ -579,36 +640,91 @@ TEST_F(obj_importer_test, import_cubes_vertex_colors)
24,
float3(-4.550208f, -1.0f, -1.918042f),
float3(-2.550208f, 1.0f, -3.918042f)},
+ {"OBCubeVertexByte",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(1.0f, 1.0f, -1.0f),
+ float3(-1.0f, -1.0f, 1.0f),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(0.846873f, 0.027321f, 0.982123f, 1.0f)},
+ {"OBCubeVertexFloat",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(3.392028f, 1.0f, -1.0f),
+ float3(1.392028f, -1.0f, 1.0f),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(49.99467f, 0.027321f, 0.982123f, 1.0f)},
};
import_and_check("cubes_vertex_colors.obj", expect, std::size(expect), 0);
}
TEST_F(obj_importer_test, import_cubes_vertex_colors_mrgb)
{
- Expectation expect[] = {{"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
- {"OBCubeXYZRGB",
- OB_MESH,
- 8,
- 12,
- 6,
- 24,
- float3(1, 1, -1),
- float3(-1, -1, 1),
- float3(0, 0, 0),
- float2(0, 0),
- float4(0.6038f, 0.3185f, 0.1329f, 1.0f)},
- {"OBCubeMRGB",
- OB_MESH,
- 8,
- 12,
- 6,
- 24,
- float3(4, 1, -1),
- float3(2, -1, 1),
- float3(0, 0, 0),
- float2(0, 0),
- float4(0.8714f, 0.6308f, 0.5271f, 1.0f)}};
+ Expectation expect[] = {
+ {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ {"OBCubeMRGB",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(4, 1, -1),
+ float3(2, -1, 1),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(0.8714f, 0.6308f, 0.5271f, 1.0f)},
+ {"OBCubeXYZRGB",
+ OB_MESH,
+ 8,
+ 12,
+ 6,
+ 24,
+ float3(1, 1, -1),
+ float3(-1, -1, 1),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(0.6038f, 0.3185f, 0.1329f, 1.0f)},
+ {"OBTriMRGB",
+ OB_MESH,
+ 3,
+ 3,
+ 1,
+ 3,
+ float3(12, 1, -1),
+ float3(10, 0, -1),
+ float3(0, 0, 0),
+ float2(0, 0),
+ float4(1.0f, 0.0f, 0.0f, 1.0f)},
+ {
+ "OBTriNoColors",
+ OB_MESH,
+ 3,
+ 3,
+ 1,
+ 3,
+ float3(8, 1, -1),
+ float3(6, 0, -1),
+ },
+ };
import_and_check("cubes_vertex_colors_mrgb.obj", expect, std::size(expect), 0);
}
+TEST_F(obj_importer_test, import_vertices)
+{
+ Expectation expect[] = {
+ {"OBCube", OB_MESH, 8, 12, 6, 24, float3(1, 1, -1), float3(-1, 1, 1)},
+ /* Loose vertices without faces or edges. */
+ {"OBCube.001", OB_MESH, 8, 0, 0, 0, float3(1, 1, -1), float3(-1, 1, 1)},
+ };
+ import_and_check("vertices.obj", expect, std::size(expect), 0);
+}
+
} // namespace blender::io::obj
diff --git a/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc
index 08050ac34c9..e473d629673 100644
--- a/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc
+++ b/source/blender/io/wavefront_obj/tests/obj_mtl_parser_tests.cc
@@ -6,6 +6,7 @@
#include "testing/testing.h"
+#include "obj_export_mtl.hh"
#include "obj_import_file_reader.hh"
namespace blender::io::obj {
@@ -49,18 +50,26 @@ class obj_mtl_parser_test : public testing::Test {
}
const MTLMaterial &got = *materials.lookup(exp.name);
const float tol = 0.0001f;
- EXPECT_V3_NEAR(exp.Ka, got.Ka, tol);
- EXPECT_V3_NEAR(exp.Kd, got.Kd, tol);
- EXPECT_V3_NEAR(exp.Ks, got.Ks, tol);
- EXPECT_V3_NEAR(exp.Ke, got.Ke, tol);
- EXPECT_NEAR(exp.Ns, got.Ns, tol);
- EXPECT_NEAR(exp.Ni, got.Ni, tol);
- EXPECT_NEAR(exp.d, got.d, tol);
- EXPECT_NEAR(exp.map_Bump_strength, got.map_Bump_strength, tol);
- EXPECT_EQ(exp.illum, got.illum);
- for (const auto &it : exp.texture_maps.items()) {
- const tex_map_XX &exp_tex = it.value;
- const tex_map_XX &got_tex = got.texture_maps.lookup(it.key);
+ EXPECT_V3_NEAR(exp.ambient_color, got.ambient_color, tol);
+ EXPECT_V3_NEAR(exp.color, got.color, tol);
+ EXPECT_V3_NEAR(exp.spec_color, got.spec_color, tol);
+ EXPECT_V3_NEAR(exp.emission_color, got.emission_color, tol);
+ EXPECT_V3_NEAR(exp.transmit_color, got.transmit_color, tol);
+ EXPECT_NEAR(exp.spec_exponent, got.spec_exponent, tol);
+ EXPECT_NEAR(exp.ior, got.ior, tol);
+ EXPECT_NEAR(exp.alpha, got.alpha, tol);
+ EXPECT_NEAR(exp.normal_strength, got.normal_strength, tol);
+ EXPECT_EQ(exp.illum_mode, got.illum_mode);
+ EXPECT_NEAR(exp.roughness, got.roughness, tol);
+ EXPECT_NEAR(exp.metallic, got.metallic, tol);
+ EXPECT_NEAR(exp.sheen, got.sheen, tol);
+ EXPECT_NEAR(exp.cc_thickness, got.cc_thickness, tol);
+ EXPECT_NEAR(exp.cc_roughness, got.cc_roughness, tol);
+ EXPECT_NEAR(exp.aniso, got.aniso, tol);
+ EXPECT_NEAR(exp.aniso_rot, got.aniso_rot, tol);
+ for (int key = 0; key < (int)MTLTexMapType::Count; key++) {
+ const MTLTexMap &exp_tex = exp.texture_maps[key];
+ const MTLTexMap &got_tex = got.texture_maps[key];
EXPECT_STREQ(exp_tex.image_path.c_str(), got_tex.image_path.c_str());
EXPECT_V3_NEAR(exp_tex.translation, got_tex.translation, tol);
EXPECT_V3_NEAR(exp_tex.scale, got_tex.scale, tol);
@@ -101,20 +110,20 @@ TEST_F(obj_mtl_parser_test, string_newlines_whitespace)
"map_Ks sometex_s_spaces_after_name.png \t \r\n";
MTLMaterial mat[6];
mat[0].name = "simple";
- mat[0].Ka = {0.1f, 0.2f, 0.3f};
- mat[0].illum = 4;
+ mat[0].ambient_color = {0.1f, 0.2f, 0.3f};
+ mat[0].illum_mode = 4;
mat[1].name = "tab_indentation";
- mat[1].Kd = {0.2f, 0.3f, 0.4f};
+ mat[1].color = {0.2f, 0.3f, 0.4f};
mat[2].name = "space_after_name";
- mat[2].Ks = {0.4f, 0.5f, 0.6f};
+ mat[2].spec_color = {0.4f, 0.5f, 0.6f};
mat[3].name = "space_before_name";
mat[4].name = "indented_values";
- mat[4].Ka = {0.5f, 0.6f, 0.7f};
- mat[4].Kd = {0.6f, 0.7f, 0.8f};
+ mat[4].ambient_color = {0.5f, 0.6f, 0.7f};
+ mat[4].color = {0.6f, 0.7f, 0.8f};
mat[5].name = "crlf_ending";
- mat[5].Ns = 5.0f;
- mat[5].tex_map_of_type(eMTLSyntaxElement::map_Kd).image_path = "sometex_d.png";
- mat[5].tex_map_of_type(eMTLSyntaxElement::map_Ks).image_path = "sometex_s_spaces_after_name.png";
+ mat[5].spec_exponent = 5.0f;
+ mat[5].tex_map_of_type(MTLTexMapType::Color).image_path = "sometex_d.png";
+ mat[5].tex_map_of_type(MTLTexMapType::Specular).image_path = "sometex_s_spaces_after_name.png";
check_string(text, mat, ARRAY_SIZE(mat));
}
@@ -122,8 +131,8 @@ TEST_F(obj_mtl_parser_test, cube)
{
MTLMaterial mat;
mat.name = "red";
- mat.Ka = {0.2f, 0.2f, 0.2f};
- mat.Kd = {1, 0, 0};
+ mat.ambient_color = {0.2f, 0.2f, 0.2f};
+ mat.color = {1, 0, 0};
check("cube.mtl", &mat, 1);
}
@@ -131,28 +140,28 @@ TEST_F(obj_mtl_parser_test, all_objects)
{
MTLMaterial mat[7];
for (auto &m : mat) {
- m.Ka = {1, 1, 1};
- m.Ks = {0.5f, 0.5f, 0.5f};
- m.Ke = {0, 0, 0};
- m.Ns = 250;
- m.Ni = 1;
- m.d = 1;
- m.illum = 2;
+ m.ambient_color = {1, 1, 1};
+ m.spec_color = {0.5f, 0.5f, 0.5f};
+ m.emission_color = {0, 0, 0};
+ m.spec_exponent = 250;
+ m.ior = 1;
+ m.alpha = 1;
+ m.illum_mode = 2;
}
mat[0].name = "Blue";
- mat[0].Kd = {0, 0, 1};
+ mat[0].color = {0, 0, 1};
mat[1].name = "BlueDark";
- mat[1].Kd = {0, 0, 0.5f};
+ mat[1].color = {0, 0, 0.5f};
mat[2].name = "Green";
- mat[2].Kd = {0, 1, 0};
+ mat[2].color = {0, 1, 0};
mat[3].name = "GreenDark";
- mat[3].Kd = {0, 0.5f, 0};
+ mat[3].color = {0, 0.5f, 0};
mat[4].name = "Material";
- mat[4].Kd = {0.8f, 0.8f, 0.8f};
+ mat[4].color = {0.8f, 0.8f, 0.8f};
mat[5].name = "Red";
- mat[5].Kd = {1, 0, 0};
+ mat[5].color = {1, 0, 0};
mat[6].name = "RedDark";
- mat[6].Kd = {0.5f, 0, 0};
+ mat[6].color = {0.5f, 0, 0};
check("all_objects.mtl", mat, ARRAY_SIZE(mat));
}
@@ -160,92 +169,101 @@ TEST_F(obj_mtl_parser_test, materials)
{
MTLMaterial mat[6];
mat[0].name = "no_textures_red";
- mat[0].Ka = {0.3f, 0.3f, 0.3f};
- mat[0].Kd = {0.8f, 0.3f, 0.1f};
- mat[0].Ns = 5.624998f;
+ mat[0].ambient_color = {0.3f, 0.3f, 0.3f};
+ mat[0].color = {0.8f, 0.3f, 0.1f};
+ mat[0].spec_exponent = 5.624998f;
mat[1].name = "four_maps";
- mat[1].Ka = {1, 1, 1};
- mat[1].Kd = {0.8f, 0.8f, 0.8f};
- mat[1].Ks = {0.5f, 0.5f, 0.5f};
- mat[1].Ke = {0, 0, 0};
- mat[1].Ns = 1000;
- mat[1].Ni = 1.45f;
- mat[1].d = 1;
- mat[1].illum = 2;
- mat[1].map_Bump_strength = 1;
+ mat[1].ambient_color = {1, 1, 1};
+ mat[1].color = {0.8f, 0.8f, 0.8f};
+ mat[1].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[1].emission_color = {0, 0, 0};
+ mat[1].spec_exponent = 1000;
+ mat[1].ior = 1.45f;
+ mat[1].alpha = 1;
+ mat[1].illum_mode = 2;
+ mat[1].normal_strength = 1;
{
- tex_map_XX &kd = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ MTLTexMap &kd = mat[1].tex_map_of_type(MTLTexMapType::Color);
kd.image_path = "texture.png";
- tex_map_XX &ns = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ MTLTexMap &ns = mat[1].tex_map_of_type(MTLTexMapType::SpecularExponent);
ns.image_path = "sometexture_Roughness.png";
- tex_map_XX &refl = mat[1].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ MTLTexMap &refl = mat[1].tex_map_of_type(MTLTexMapType::Reflection);
refl.image_path = "sometexture_Metallic.png";
- tex_map_XX &bump = mat[1].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ MTLTexMap &bump = mat[1].tex_map_of_type(MTLTexMapType::Normal);
bump.image_path = "sometexture_Normal.png";
}
mat[2].name = "Clay";
- mat[2].Ka = {1, 1, 1};
- mat[2].Kd = {0.8f, 0.682657f, 0.536371f};
- mat[2].Ks = {0.5f, 0.5f, 0.5f};
- mat[2].Ke = {0, 0, 0};
- mat[2].Ns = 440.924042f;
- mat[2].Ni = 1.45f;
- mat[2].d = 1;
- mat[2].illum = 2;
+ mat[2].ambient_color = {1, 1, 1};
+ mat[2].color = {0.8f, 0.682657f, 0.536371f};
+ mat[2].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[2].emission_color = {0, 0, 0};
+ mat[2].spec_exponent = 440.924042f;
+ mat[2].ior = 1.45f;
+ mat[2].alpha = 1;
+ mat[2].illum_mode = 2;
mat[3].name = "Hat";
- mat[3].Ka = {1, 1, 1};
- mat[3].Kd = {0.8f, 0.8f, 0.8f};
- mat[3].Ks = {0.5f, 0.5f, 0.5f};
- mat[3].Ns = 800;
- mat[3].map_Bump_strength = 0.5f;
+ mat[3].ambient_color = {1, 1, 1};
+ mat[3].color = {0.8f, 0.8f, 0.8f};
+ mat[3].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[3].spec_exponent = 800;
+ mat[3].normal_strength = 0.5f;
{
- tex_map_XX &kd = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ MTLTexMap &kd = mat[3].tex_map_of_type(MTLTexMapType::Color);
kd.image_path = "someHatTexture_BaseColor.jpg";
- tex_map_XX &ns = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ MTLTexMap &ns = mat[3].tex_map_of_type(MTLTexMapType::SpecularExponent);
ns.image_path = "someHatTexture_Roughness.jpg";
- tex_map_XX &refl = mat[3].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ MTLTexMap &refl = mat[3].tex_map_of_type(MTLTexMapType::Reflection);
refl.image_path = "someHatTexture_Metalness.jpg";
- tex_map_XX &bump = mat[3].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ MTLTexMap &bump = mat[3].tex_map_of_type(MTLTexMapType::Normal);
bump.image_path = "someHatTexture_Normal.jpg";
}
mat[4].name = "Parser_Test";
- mat[4].Ka = {0.1f, 0.2f, 0.3f};
- mat[4].Kd = {0.4f, 0.5f, 0.6f};
- mat[4].Ks = {0.7f, 0.8f, 0.9f};
- mat[4].illum = 6;
- mat[4].Ns = 15.5;
- mat[4].Ni = 1.5;
- mat[4].d = 0.5;
- mat[4].map_Bump_strength = 0.1f;
+ mat[4].ambient_color = {0.1f, 0.2f, 0.3f};
+ mat[4].color = {0.4f, 0.5f, 0.6f};
+ mat[4].spec_color = {0.7f, 0.8f, 0.9f};
+ mat[4].illum_mode = 6;
+ mat[4].spec_exponent = 15.5;
+ mat[4].ior = 1.5;
+ mat[4].alpha = 0.5;
+ mat[4].normal_strength = 0.1f;
+ mat[4].transmit_color = {0.1f, 0.3f, 0.5f};
+ mat[4].normal_strength = 0.1f;
+ mat[4].roughness = 0.2f;
+ mat[4].metallic = 0.3f;
+ mat[4].sheen = 0.4f;
+ mat[4].cc_thickness = 0.5f;
+ mat[4].cc_roughness = 0.6f;
+ mat[4].aniso = 0.7f;
+ mat[4].aniso_rot = 0.8f;
{
- tex_map_XX &kd = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ MTLTexMap &kd = mat[4].tex_map_of_type(MTLTexMapType::Color);
kd.image_path = "sometex_d.png";
- tex_map_XX &ns = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ MTLTexMap &ns = mat[4].tex_map_of_type(MTLTexMapType::SpecularExponent);
ns.image_path = "sometex_ns.psd";
- tex_map_XX &refl = mat[4].tex_map_of_type(eMTLSyntaxElement::map_refl);
+ MTLTexMap &refl = mat[4].tex_map_of_type(MTLTexMapType::Reflection);
refl.image_path = "clouds.tiff";
refl.scale = {1.5f, 2.5f, 3.5f};
refl.translation = {4.5f, 5.5f, 6.5f};
refl.projection_type = SHD_PROJ_SPHERE;
- tex_map_XX &bump = mat[4].tex_map_of_type(eMTLSyntaxElement::map_Bump);
+ MTLTexMap &bump = mat[4].tex_map_of_type(MTLTexMapType::Normal);
bump.image_path = "somebump.tga";
bump.scale = {3, 4, 5};
}
mat[5].name = "Parser_ScaleOffset_Test";
{
- tex_map_XX &kd = mat[5].tex_map_of_type(eMTLSyntaxElement::map_Kd);
+ MTLTexMap &kd = mat[5].tex_map_of_type(MTLTexMapType::Color);
kd.translation = {2.5f, 0.0f, 0.0f};
kd.image_path = "OffsetOneValue.png";
- tex_map_XX &ks = mat[5].tex_map_of_type(eMTLSyntaxElement::map_Ks);
+ MTLTexMap &ks = mat[5].tex_map_of_type(MTLTexMapType::Specular);
ks.scale = {1.5f, 2.5f, 1.0f};
ks.translation = {3.5f, 4.5f, 0.0f};
ks.image_path = "ScaleOffsetBothTwovalues.png";
- tex_map_XX &ns = mat[5].tex_map_of_type(eMTLSyntaxElement::map_Ns);
+ MTLTexMap &ns = mat[5].tex_map_of_type(MTLTexMapType::SpecularExponent);
ns.scale = {0.5f, 1.0f, 1.0f};
ns.image_path = "1.Value.png";
}
@@ -253,4 +271,75 @@ TEST_F(obj_mtl_parser_test, materials)
check("materials.mtl", mat, ARRAY_SIZE(mat));
}
+TEST_F(obj_mtl_parser_test, materials_without_pbr)
+{
+ MTLMaterial mat[2];
+ mat[0].name = "Mat1";
+ mat[0].spec_exponent = 360.0f;
+ mat[0].ambient_color = {0.9f, 0.9f, 0.9f};
+ mat[0].color = {0.8f, 0.276449f, 0.101911f};
+ mat[0].spec_color = {0.25f, 0.25f, 0.25f};
+ mat[0].emission_color = {0, 0, 0};
+ mat[0].ior = 1.45f;
+ mat[0].alpha = 1;
+ mat[0].illum_mode = 3;
+
+ mat[1].name = "Mat2";
+ mat[1].ambient_color = {1, 1, 1};
+ mat[1].color = {0.8f, 0.8f, 0.8f};
+ mat[1].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[1].ior = 1.45f;
+ mat[1].alpha = 1;
+ mat[1].illum_mode = 2;
+ {
+ MTLTexMap &ns = mat[1].tex_map_of_type(MTLTexMapType::SpecularExponent);
+ ns.image_path = "../blend_geometry/texture_roughness.png";
+ MTLTexMap &ke = mat[1].tex_map_of_type(MTLTexMapType::Emission);
+ ke.image_path = "../blend_geometry/texture_illum.png";
+ }
+
+ check("materials_without_pbr.mtl", mat, ARRAY_SIZE(mat));
+}
+
+TEST_F(obj_mtl_parser_test, materials_pbr)
+{
+ MTLMaterial mat[2];
+ mat[0].name = "Mat1";
+ mat[0].color = {0.8f, 0.276449f, 0.101911f};
+ mat[0].spec_color = {0.25f, 0.25f, 0.25f};
+ mat[0].emission_color = {0, 0, 0};
+ mat[0].ior = 1.45f;
+ mat[0].alpha = 1;
+ mat[0].illum_mode = 3;
+ mat[0].roughness = 0.4f;
+ mat[0].metallic = 0.9f;
+ mat[0].sheen = 0.3f;
+ mat[0].cc_thickness = 0.393182f;
+ mat[0].cc_roughness = 0.05f;
+ mat[0].aniso = 0.2f;
+ mat[0].aniso_rot = 0.0f;
+
+ mat[1].name = "Mat2";
+ mat[1].color = {0.8f, 0.8f, 0.8f};
+ mat[1].spec_color = {0.5f, 0.5f, 0.5f};
+ mat[1].ior = 1.45f;
+ mat[1].alpha = 1;
+ mat[1].illum_mode = 2;
+ mat[1].metallic = 0.0f;
+ mat[1].cc_thickness = 0.3f;
+ mat[1].cc_roughness = 0.4f;
+ mat[1].aniso = 0.8f;
+ mat[1].aniso_rot = 0.7f;
+ {
+ MTLTexMap &pr = mat[1].tex_map_of_type(MTLTexMapType::Roughness);
+ pr.image_path = "../blend_geometry/texture_roughness.png";
+ MTLTexMap &ps = mat[1].tex_map_of_type(MTLTexMapType::Sheen);
+ ps.image_path = "../blend_geometry/texture_checker.png";
+ MTLTexMap &ke = mat[1].tex_map_of_type(MTLTexMapType::Emission);
+ ke.image_path = "../blend_geometry/texture_illum.png";
+ }
+
+ check("materials_pbr.mtl", mat, ARRAY_SIZE(mat));
+}
+
} // namespace blender::io::obj
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 451c921c4ef..d64b3d361cf 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -22,6 +22,7 @@ struct GPUTexture;
struct ID;
struct Library;
struct PackedFile;
+struct UniqueName_Map;
/* Runtime display data */
struct DrawData;
@@ -36,7 +37,7 @@ typedef struct DrawData {
/* Only nested data, NOT the engine data itself. */
DrawDataFreeCb free;
/* Accumulated recalc flags, which corresponds to ID->recalc flags. */
- int recalc;
+ unsigned int recalc;
} DrawData;
typedef struct DrawDataList {
@@ -255,6 +256,7 @@ typedef struct IDOverrideLibraryProperty {
/**
* List of overriding operations (IDOverrideLibraryPropertyOperation) applied to this property.
+ * Recreated as part of the diffing, so do not store any of these elsewhere.
*/
ListBase operations;
@@ -386,7 +388,7 @@ typedef struct ID {
int tag;
int us;
int icon_id;
- int recalc;
+ unsigned int recalc;
/**
* Used by undo code. recalc_after_undo_push contains the changes between the
* last undo push and the current state. This is accumulated as IDs are tagged
@@ -396,8 +398,8 @@ typedef struct ID {
* recalc_after_undo_push at the time of the undo push. This means it can be
* used to find the changes between undo states.
*/
- int recalc_up_to_undo_push;
- int recalc_after_undo_push;
+ unsigned int recalc_up_to_undo_push;
+ unsigned int recalc_after_undo_push;
/**
* A session-wide unique identifier for a given ID, that remain the same across potential
@@ -444,6 +446,11 @@ typedef struct ID {
struct ID_Runtime runtime;
} ID;
+typedef struct Library_Runtime {
+ /* Used for efficient calculations of unique names. */
+ struct UniqueName_Map *name_map;
+} Library_Runtime;
+
/**
* For each library file used, a Library struct is added to Main
* WARNING: readfile.c, expand_doit() reads this struct without DNA check!
@@ -476,6 +483,8 @@ typedef struct Library {
int temp_index;
/** See BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION, needed for do_versions. */
short versionfile, subversionfile;
+
+ struct Library_Runtime runtime;
} Library;
/** #Library.tag */
@@ -646,62 +655,123 @@ enum {
/**
* id->tag (runtime-only).
*
- * Those flags belong to three different categories,
- * which have different expected handling in code:
+ * Those flags belong to three different categories, which have different expected handling in
+ * code:
*
- * - RESET_BEFORE_USE: piece of code that wants to use such flag
- * has to ensure they are properly 'reset' first.
+ * - RESET_BEFORE_USE: piece of code that wants to use such flag has to ensure they are properly
+ * 'reset' first.
* - RESET_AFTER_USE: piece of code that wants to use such flag has to ensure they are properly
- * 'reset' after usage
- * (though 'lifetime' of those flags is a bit fuzzy, e.g. _RECALC ones are reset on depsgraph
- * evaluation...).
- * - RESET_NEVER: those flags are 'status' one, and never actually need any reset
- * (except on initialization during .blend file reading).
+ * 'reset' after usage (though 'lifetime' of those flags is a bit fuzzy, e.g. _RECALC ones are
+ * reset on depsgraph evaluation...).
+ * - RESET_NEVER: these flags are 'status' ones, and never actually need any reset (except on
+ * initialization during .blend file reading).
*/
enum {
- /* RESET_NEVER Datablock is from current .blend file. */
+ /**
+ * ID is from current .blend file.
+ *
+ * RESET_NEVER
+ */
LIB_TAG_LOCAL = 0,
- /* RESET_NEVER Datablock is from a library,
- * but is used (linked) directly by current .blend file. */
+ /**
+ * ID is from a library, but is used (linked) directly by current .blend file.
+ *
+ * RESET_NEVER
+ */
LIB_TAG_EXTERN = 1 << 0,
- /* RESET_NEVER Datablock is from a library,
- * and is only used (linked) indirectly through other libraries. */
+ /**
+ * ID is from a library, and is only used (linked) indirectly through other libraries.
+ *
+ * RESET_NEVER
+ */
LIB_TAG_INDIRECT = 1 << 1,
- /* RESET_AFTER_USE Flag used internally in readfile.c,
- * to mark IDs needing to be expanded (only done once). */
+ /**
+ * Tag used internally in readfile.c, to mark IDs needing to be expanded (only done once).
+ *
+ * RESET_AFTER_USE
+ */
LIB_TAG_NEED_EXPAND = 1 << 3,
- /* RESET_AFTER_USE Flag used internally in readfile.c to mark ID
- * placeholders for linked data-blocks needing to be read. */
+ /**
+ * Tag used internally in readfile.c, to mark ID placeholders for linked data-blocks needing to
+ * be read.
+ *
+ * RESET_AFTER_USE
+ */
LIB_TAG_ID_LINK_PLACEHOLDER = 1 << 4,
- /* RESET_AFTER_USE */
+ /**
+ * Tag used internally in readfile.c, to mark IDs needing to be 'lib-linked', i.e. to get their
+ * pointers to other data-blocks updated from the 'UID' values stored in .blend files to the new,
+ * actual pointers.
+ *
+ * RESET_AFTER_USE
+ */
LIB_TAG_NEED_LINK = 1 << 5,
- /* RESET_NEVER tag data-block as a place-holder
- * (because the real one could not be linked from its library e.g.). */
+ /**
+ * ID is a place-holder, an 'empty shell' (because the real one could not be linked from its
+ * library e.g.).
+ *
+ * RESET_NEVER
+ */
LIB_TAG_MISSING = 1 << 6,
- /* RESET_NEVER tag data-block as being up-to-date regarding its reference. */
+ /**
+ * ID is up-to-date regarding its reference (only for library overrides).
+ *
+ * RESET_NEVER
+ */
LIB_TAG_OVERRIDE_LIBRARY_REFOK = 1 << 9,
- /* RESET_NEVER tag data-block as needing an auto-override execution, if enabled. */
+ /**
+ * ID needs an auto-diffing execution, if enabled (only for library overrides).
+ *
+ * RESET_NEVER
+ */
LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH = 1 << 17,
- /* tag data-block as having an extra user. */
+ /**
+ * ID has an extra virtual user (aka 'ensured real', as set by e.g. some editors, not to be
+ * confused with the `LIB_FAKEUSER` flag).
+ *
+ * RESET_NEVER
+ *
+ * \note This tag does not necessarily mean the actual user count of the ID is increased, this is
+ * defined by #LIB_TAG_EXTRAUSER_SET.
+ */
LIB_TAG_EXTRAUSER = 1 << 2,
- /* tag data-block as having actually increased user-count for the extra virtual user. */
+ /**
+ * ID actually has increased user-count for the extra virtual user.
+ *
+ * RESET_NEVER
+ */
LIB_TAG_EXTRAUSER_SET = 1 << 7,
- /* RESET_AFTER_USE tag newly duplicated/copied IDs (see #ID_NEW_SET macro above).
- * Also used internally in readfile.c to mark data-blocks needing do_versions. */
+ /**
+ * ID is newly duplicated/copied (see #ID_NEW_SET macro above).
+ *
+ * RESET_AFTER_USE
+ *
+ * \note Also used internally in readfile.c to mark data-blocks needing do_versions.
+ */
LIB_TAG_NEW = 1 << 8,
- /* RESET_BEFORE_USE free test flag.
- * TODO: make it a RESET_AFTER_USE too. */
+ /**
+ * Free to use tag, often used in BKE code to mark IDs to be processed.
+ *
+ * RESET_BEFORE_USE
+ *
+ * \todo Make it a RESET_AFTER_USE too.
+ */
LIB_TAG_DOIT = 1 << 10,
- /* RESET_AFTER_USE tag existing data before linking so we know what is new. */
+ /**
+ * ID is already existing. Set before linking, to distinguish between existing data-blocks and
+ * newly linked ones.
+ *
+ * RESET_AFTER_USE
+ */
LIB_TAG_PRE_EXISTING = 1 << 11,
/**
- * The data-block is a copy-on-write/localized version.
+ * ID is a copy-on-write/localized version.
*
* RESET_NEVER
*
@@ -711,8 +781,8 @@ enum {
*/
LIB_TAG_COPIED_ON_WRITE = 1 << 12,
/**
- * The data-block is not the original COW ID created by the depsgraph, but has be re-allocated
- * during the evaluation process of another ID.
+ * ID is not the original COW ID created by the depsgraph, but has been re-allocated during the
+ * evaluation process of another ID.
*
* RESET_NEVER
*
@@ -722,34 +792,58 @@ enum {
LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT = 1 << 13,
/**
- * The data-block is fully outside of any ID management area, and should be considered as a
- * purely independent data.
+ * ID is fully outside of any ID management area, and should be considered as a purely
+ * independent data.
*
* RESET_NEVER
*
- * NOTE: Only used by node-groups currently.
+ * \note Only used by node-trees currently.
*/
LIB_TAG_LOCALIZED = 1 << 14,
- /* RESET_NEVER tag data-block for freeing etc. behavior
- * (usually set when copying real one into temp/runtime one). */
- LIB_TAG_NO_MAIN = 1 << 15, /* Datablock is not listed in Main database. */
- LIB_TAG_NO_USER_REFCOUNT = 1 << 16, /* Datablock does not refcount usages of other IDs. */
- /* Datablock was not allocated by standard system (BKE_libblock_alloc), do not free its memory
- * (usual type-specific freeing is called though). */
+ /** General ID management info, for freeing or copying behavior e.g. */
+ /**
+ * ID is not listed/stored in Main database.
+ *
+ * RESET_NEVER
+ */
+ LIB_TAG_NO_MAIN = 1 << 15,
+ /**
+ * Datablock does not refcount usages of other IDs.
+ *
+ * RESET_NEVER
+ */
+ LIB_TAG_NO_USER_REFCOUNT = 1 << 16,
+ /**
+ * ID was not allocated by standard system (BKE_libblock_alloc), do not free its memory
+ * (usual type-specific freeing is called though).
+ *
+ * RESET_NEVER
+ */
LIB_TAG_NOT_ALLOCATED = 1 << 18,
- /* RESET_AFTER_USE Used by undo system to tag unchanged IDs re-used from old Main (instead of
- * read from memfile). */
+ /**
+ * ID is being re-used from the old Main (instead of read from memfile), during memfile undo
+ * processing.
+ *
+ * RESET_AFTER_USE
+ */
LIB_TAG_UNDO_OLD_ID_REUSED = 1 << 19,
- /* This ID is part of a temporary #Main which is expected to be freed in a short time-frame.
+ /**
+ * ID is part of a temporary #Main which is expected to be freed in a short time-frame.
+ *
+ * RESET_NEVER
+ *
* Don't allow assigning this to non-temporary members (since it's likely to cause errors).
- * When set #ID.session_uuid isn't initialized, since the data isn't part of the session. */
+ * When set #ID.session_uuid isn't initialized, since the data isn't part of the session.
+ */
LIB_TAG_TEMP_MAIN = 1 << 20,
/**
- * The data-block is a library override that needs re-sync to its linked reference.
+ * ID is a library override that needs re-sync to its linked reference.
+ *
+ * RESET_NEVER
*/
LIB_TAG_LIB_OVERRIDE_NEED_RESYNC = 1 << 21,
};
@@ -862,6 +956,17 @@ typedef enum IDRecalcFlag {
/* The node tree has changed in a way that affects its output nodes. */
ID_RECALC_NTREE_OUTPUT = (1 << 25),
+ /* Provisioned flags.
+ *
+ * Not for actual use. The idea of them is to have all bits of the `IDRecalcFlag` defined to a
+ * known value, silencing sanitizer warnings when checking bits of the ID_RECALC_ALL. */
+ ID_RECALC_PROVISION_26 = (1 << 26),
+ ID_RECALC_PROVISION_27 = (1 << 27),
+ ID_RECALC_PROVISION_28 = (1 << 28),
+ ID_RECALC_PROVISION_29 = (1 << 29),
+ ID_RECALC_PROVISION_30 = (1 << 30),
+ ID_RECALC_PROVISION_31 = (1u << 31),
+
/***************************************************************************
* Pseudonyms, to have more semantic meaning in the actual code without
* using too much low-level and implementation specific tags. */
@@ -880,7 +985,8 @@ typedef enum IDRecalcFlag {
* Do NOT use those for tagging. */
/* Identifies that SOMETHING has been changed in this ID. */
- ID_RECALC_ALL = ~(0),
+ ID_RECALC_ALL = (0xffffffff),
+
/* Identifies that something in particle system did change. */
ID_RECALC_PSYS_ALL = (ID_RECALC_PSYS_REDO | ID_RECALC_PSYS_RESET | ID_RECALC_PSYS_CHILD |
ID_RECALC_PSYS_PHYS),
@@ -923,6 +1029,10 @@ typedef enum IDRecalcFlag {
#define FILTER_ID_PT (1ULL << 33)
#define FILTER_ID_VO (1ULL << 34)
#define FILTER_ID_SIM (1ULL << 35)
+#define FILTER_ID_KE (1ULL << 36)
+#define FILTER_ID_SCR (1ULL << 37)
+#define FILTER_ID_WM (1ULL << 38)
+#define FILTER_ID_LI (1ULL << 39)
#define FILTER_ID_ALL \
(FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU_LEGACY | \
@@ -930,7 +1040,8 @@ typedef enum IDRecalcFlag {
FILTER_ID_MA | FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | \
FILTER_ID_OB | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | \
FILTER_ID_SO | FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | \
- FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM)
+ FILTER_ID_WS | FILTER_ID_LP | FILTER_ID_CV | FILTER_ID_PT | FILTER_ID_VO | FILTER_ID_SIM | \
+ FILTER_ID_KE | FILTER_ID_SCR | FILTER_ID_WM | FILTER_ID_LI)
/**
* This enum defines the index assigned to each type of IDs in the array returned by
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index e2b58cefef6..0ab14988e40 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -1160,6 +1160,9 @@ typedef struct IdAdtTemplate {
AnimData *adt;
} IdAdtTemplate;
+/* From: `DNA_object_types.h`, see it's doc-string there. */
+#define SELECT 1
+
/* ************************************************ */
#ifdef __cplusplus
diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h
index d49d0906aa7..29795519719 100644
--- a/source/blender/makesdna/DNA_asset_types.h
+++ b/source/blender/makesdna/DNA_asset_types.h
@@ -96,7 +96,7 @@ typedef enum eAssetLibraryType {
} eAssetLibraryType;
/**
- * Information to identify a asset library. May be either one of the predefined types (current
+ * Information to identify an asset library. May be either one of the predefined types (current
* 'Main', builtin library, project library), or a custom type as defined in the Preferences.
*
* If the type is set to #ASSET_LIBRARY_CUSTOM, `custom_library_index` must be set to identify the
diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h
index adda23c26f2..97abf0fec5e 100644
--- a/source/blender/makesdna/DNA_brush_enums.h
+++ b/source/blender/makesdna/DNA_brush_enums.h
@@ -87,6 +87,8 @@ typedef enum eGPDbrush_Flag {
GP_BRUSH_OCCLUDE_ERASER = (1 << 15),
/* Post process trim stroke */
GP_BRUSH_TRIM_STROKE = (1 << 16),
+ /* Post process convert to outline stroke */
+ GP_BRUSH_OUTLINE_STROKE = (1 << 17),
} eGPDbrush_Flag;
typedef enum eGPDbrush_Flag2 {
@@ -116,13 +118,19 @@ typedef enum eGPDbrush_Flag2 {
GP_BRUSH_USE_UV_RAND_PRESS = (1 << 11),
} eGPDbrush_Flag2;
-/* BrushGpencilSettings->gp_fill_draw_mode */
+/* BrushGpencilSettings->fill_draw_mode */
typedef enum eGP_FillDrawModes {
GP_FILL_DMODE_BOTH = 0,
GP_FILL_DMODE_STROKE = 1,
GP_FILL_DMODE_CONTROL = 2,
} eGP_FillDrawModes;
+/* BrushGpencilSettings->fill_extend_mode */
+typedef enum eGP_FillExtendModes {
+ GP_FILL_EMODE_RADIUS = 0,
+ GP_FILL_EMODE_EXTEND = 1,
+} eGP_FillExtendModes;
+
/* BrushGpencilSettings->fill_layer_mode */
typedef enum eGP_FillLayerModes {
GP_FILL_GPLMODE_VISIBLE = 0,
diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h
index b24bb786593..761a36e8d1f 100644
--- a/source/blender/makesdna/DNA_brush_types.h
+++ b/source/blender/makesdna/DNA_brush_types.h
@@ -58,11 +58,10 @@ typedef struct BrushGpencilSettings {
/** Factor for transparency. */
float fill_threshold;
- /** Number of pixel to consider the leak is too small (x 2). */
- short fill_leak;
+ char _pad2[2];
/* Type of caps: eGPDstroke_Caps. */
int8_t caps_type;
- char _pad;
+ char _pad[5];
int flag2;
@@ -70,6 +69,8 @@ typedef struct BrushGpencilSettings {
int fill_simplylvl;
/** Type of control lines drawing mode. */
int fill_draw_mode;
+ /** Type of gap filling extension to use. */
+ int fill_extend_mode;
/** Icon identifier. */
int icon_id;
@@ -132,9 +133,15 @@ typedef struct BrushGpencilSettings {
struct CurveMapping *curve_rand_saturation;
struct CurveMapping *curve_rand_value;
+ /** Factor for external line thickness conversion to outline. */
+ float outline_fac;
+ char _pad1[4];
+
/* optional link of material to replace default in context */
/** Material. */
struct Material *material;
+ /** Material Alternative for secondary operations. */
+ struct Material *material_alt;
} BrushGpencilSettings;
typedef struct BrushCurvesSculptSettings {
diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h
index e0aec298cd0..10a6c936be1 100644
--- a/source/blender/makesdna/DNA_camera_types.h
+++ b/source/blender/makesdna/DNA_camera_types.h
@@ -54,6 +54,7 @@ typedef struct CameraBGImage {
typedef struct CameraDOFSettings {
/** Focal distance for depth of field. */
struct Object *focus_object;
+ char focus_subtarget[64];
float focus_distance;
float aperture_fstop;
float aperture_rotation;
diff --git a/source/blender/makesdna/DNA_collection_types.h b/source/blender/makesdna/DNA_collection_types.h
index 26011c990d4..75bfc2575a5 100644
--- a/source/blender/makesdna/DNA_collection_types.h
+++ b/source/blender/makesdna/DNA_collection_types.h
@@ -36,6 +36,7 @@ enum eCollectionLineArt_Usage {
COLLECTION_LRT_EXCLUDE = (1 << 1),
COLLECTION_LRT_INTERSECTION_ONLY = (1 << 2),
COLLECTION_LRT_NO_INTERSECTION = (1 << 3),
+ COLLECTION_LRT_FORCE_INTERSECTION = (1 << 4),
};
enum eCollectionLineArt_Flags {
@@ -46,6 +47,9 @@ enum eCollectionLineArt_Flags {
typedef struct Collection {
ID id;
+ /** The ID owning this node tree, in case it is an embedded one. */
+ ID *owner_id;
+
/** CollectionObject. */
ListBase gobject;
/** CollectionChild. */
diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h
index 623d1dd6440..4eb64290032 100644
--- a/source/blender/makesdna/DNA_color_types.h
+++ b/source/blender/makesdna/DNA_color_types.h
@@ -21,6 +21,9 @@ extern "C" {
#define CM_TOT 4
+#define GPU_SKY_WIDTH 512
+#define GPU_SKY_HEIGHT 128
+
typedef struct CurveMapPoint {
float x, y;
/** Shorty for result lookup. */
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 8e0ce68f71a..04887f1223c 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -181,7 +181,7 @@ typedef struct bKinematicConstraint {
float orientweight;
/** CopyPose: for target-less IK. */
float grabtarget[3];
- /** Subtype of IK constraint: eConstraint_IK_Type. */
+ /** Sub-type of IK constraint: #eConstraint_IK_Type. */
short type;
/** Distance: how to limit in relation to clamping sphere: LIMITDIST_... */
short mode;
diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h
index 6e18d442ee2..5862c3a6707 100644
--- a/source/blender/makesdna/DNA_curve_types.h
+++ b/source/blender/makesdna/DNA_curve_types.h
@@ -439,9 +439,9 @@ enum {
/* *************** BEZTRIPLE **************** */
-/* BezTriple.f1,2,3 */
+/** #BezTriple.f1, #BezTriple.f2, #BezTriple.f3. */
typedef enum eBezTriple_Flag {
- /* SELECT */
+ /* `SELECT = (1 << 0)` */
BEZT_FLAG_TEMP_TAG = (1 << 1), /* always clear. */
/* Can be used to ignore keyframe points for certain operations. */
BEZT_FLAG_IGNORE_TAG = (1 << 2),
diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h
index ed909c283c4..6c38d316508 100644
--- a/source/blender/makesdna/DNA_curves_types.h
+++ b/source/blender/makesdna/DNA_curves_types.h
@@ -30,6 +30,7 @@ typedef enum CurveType {
CURVE_TYPE_BEZIER = 2,
CURVE_TYPE_NURBS = 3,
} CurveType;
+/* The number of supported curve types. */
#define CURVE_TYPES_NUM 4
typedef enum HandleType {
@@ -67,23 +68,6 @@ typedef enum NormalMode {
*/
typedef struct CurvesGeometry {
/**
- * A runtime pointer to the "position" attribute data.
- * \note This data is owned by #point_data.
- */
- float (*position)[3];
- /**
- * A runtime pointer to the "radius" attribute data.
- * \note This data is owned by #point_data.
- */
- float *radius;
-
- /**
- * The type of each curve. #CurveType.
- * \note This data is owned by #curve_data.
- */
- int8_t *curve_type;
-
- /**
* The start index of each curve in the point data. The size of each curve can be calculated by
* subtracting the offset from the next offset. That is valid even for the last curve because
* this array is allocated with a length one larger than the number of curves. This is allowed
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index bc29de66cf9..f51b1c790b0 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -256,7 +256,6 @@ enum {
/* Limits */
#define MAX_MTFACE 8
-#define MAX_MCOL 8
#define DYNTOPO_NODE_NONE -1
diff --git a/source/blender/makesdna/DNA_fileglobal_types.h b/source/blender/makesdna/DNA_fileglobal_types.h
index 0fcf232cdaa..58b8504430a 100644
--- a/source/blender/makesdna/DNA_fileglobal_types.h
+++ b/source/blender/makesdna/DNA_fileglobal_types.h
@@ -29,9 +29,9 @@ typedef struct FileGlobal {
int fileflags;
int globalf;
- /** Commit timestamp from buildinfo. */
+ /** Commit timestamp from `buildinfo`. */
uint64_t build_commit_timestamp;
- /** Hash from buildinfo. */
+ /** Hash from `buildinfo`. */
char build_hash[16];
/** File path where this was saved, for recover (1024 = FILE_MAX). */
char filepath[1024];
diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h
index 2bb95caddfb..ca1eac0bde8 100644
--- a/source/blender/makesdna/DNA_gpencil_modifier_types.h
+++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h
@@ -999,9 +999,14 @@ typedef enum eLineartGpencilModifierSource {
} eLineartGpencilModifierSource;
typedef enum eLineartGpencilModifierShadowFilter {
+ /* These options need to be ordered in this way because those latter options requires line art to
+ * run a few extra stages. Having those values set up this way will allow
+ * #BKE_gpencil_get_lineart_modifier_limits() to find out maximum stages needed in multiple
+ * cached line art modifiers. */
LRT_SHADOW_FILTER_NONE = 0,
- LRT_SHADOW_FILTER_LIT = 1,
+ LRT_SHADOW_FILTER_ILLUMINATED = 1,
LRT_SHADOW_FILTER_SHADED = 2,
+ LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES = 3,
} eLineartGpencilModifierShadowFilter;
typedef enum eLineartGpencilModifierSilhouetteFilter {
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index a83262d7639..26dbb544f52 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -347,6 +347,10 @@ typedef enum eGPDstroke_Flag {
/* Flag to indicated that the editcurve has been changed and the stroke needs to be updated with
* the curve data */
GP_STROKE_NEEDS_CURVE_UPDATE = (1 << 9),
+ /* Flag to indicate that a stroke is used only for help, and will not affect rendering or fill */
+ GP_STROKE_HELP = (1 << 10),
+ /* Flag to indicate that a extend stroke collide (fill tool) */
+ GP_STROKE_COLLIDE = (1 << 11),
/* only for use with stroke-buffer (while drawing arrows) */
GP_STROKE_USE_ARROW_START = (1 << 12),
/* only for use with stroke-buffer (while drawing arrows) */
@@ -579,7 +583,7 @@ 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 */
+ /* Disable masks in view-layer render */
GP_LAYER_DISABLE_MASKS_IN_VIEWLAYER = (1 << 15),
} eGPDlayer_Flag;
diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h
index 6e4e515a0fe..f35c77f663b 100644
--- a/source/blender/makesdna/DNA_image_types.h
+++ b/source/blender/makesdna/DNA_image_types.h
@@ -92,8 +92,14 @@ typedef struct ImageTile {
struct ImageTile_Runtime runtime;
- char _pad[4];
int tile_number;
+
+ /* for generated images */
+ int gen_x, gen_y;
+ char gen_type, gen_flag;
+ short gen_depth;
+ float gen_color[4];
+
char label[64];
} ImageTile;
@@ -167,10 +173,10 @@ typedef struct Image {
int lastused;
/* for generated images */
- int gen_x, gen_y;
- char gen_type, gen_flag;
- short gen_depth;
- float gen_color[4];
+ int gen_x DNA_DEPRECATED, gen_y DNA_DEPRECATED;
+ char gen_type DNA_DEPRECATED, gen_flag DNA_DEPRECATED;
+ short gen_depth DNA_DEPRECATED;
+ float gen_color[4] DNA_DEPRECATED;
/* display aspect - for UV editing images resized for faster openGL display */
float aspx, aspy;
@@ -262,7 +268,8 @@ enum {
/** #Image.gen_flag */
enum {
- IMA_GEN_FLOAT = 1,
+ IMA_GEN_FLOAT = (1 << 0),
+ IMA_GEN_TILE = (1 << 1),
};
/** #Image.alpha_mode */
diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h
index 0af50b2bd4f..e011e1c491e 100644
--- a/source/blender/makesdna/DNA_layer_types.h
+++ b/source/blender/makesdna/DNA_layer_types.h
@@ -34,10 +34,18 @@ typedef enum eViewLayerEEVEEPassType {
EEVEE_RENDER_PASS_AO = (1 << 13),
EEVEE_RENDER_PASS_BLOOM = (1 << 14),
EEVEE_RENDER_PASS_AOV = (1 << 15),
+ /*
+ * TODO(@jbakker): Clean up conflicting bits after EEVEE has been removed.
+ * #EEVEE_RENDER_PASS_CRYPTOMATTE is for EEVEE, `EEVEE_RENDER_PASS_CRYTPOMATTE_*` are for
+ * EEVEE-Next.
+ */
EEVEE_RENDER_PASS_CRYPTOMATTE = (1 << 16),
- EEVEE_RENDER_PASS_VECTOR = (1 << 17),
+ EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT = (1 << 16),
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET = (1 << 17),
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL = (1 << 18),
+ EEVEE_RENDER_PASS_VECTOR = (1 << 19),
} eViewLayerEEVEEPassType;
-#define EEVEE_RENDER_PASS_MAX_BIT 17
+#define EEVEE_RENDER_PASS_MAX_BIT 20
/* #ViewLayerAOV.type */
typedef enum eViewLayerAOVType {
@@ -198,16 +206,44 @@ enum {
BASE_HIDDEN = (1 << 8), /* Object is hidden for editing. */
/* Runtime evaluated flags. */
- BASE_VISIBLE_DEPSGRAPH = (1 << 1), /* Object is enabled and visible for the depsgraph. */
- BASE_SELECTABLE = (1 << 2), /* Object can be selected. */
- BASE_FROM_DUPLI = (1 << 3), /* Object comes from duplicator. */
- BASE_VISIBLE_VIEWLAYER = (1 << 4), /* Object is enabled and visible for the viewlayer. */
- BASE_FROM_SET = (1 << 5), /* Object comes from set. */
- BASE_ENABLED_VIEWPORT = (1 << 6), /* Object is enabled in viewport. */
- BASE_ENABLED_RENDER = (1 << 7), /* Object is enabled in final render */
+
+ /* Object is enabled and potentially visible in a viewport. Layer collection
+ * visibility, local collection visibility, and local view are not part of this
+ * and may cause the object to be hidden depending on the 3D viewport settings.
+ *
+ * Objects with this flag will be considered visible by the viewport depsgraph
+ * and be evaluated as a result.
+ *
+ * This implies BASE_ENABLED_VIEWPORT. */
+ BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT = (1 << 1),
+
+ /* Object can be selected. */
+ BASE_SELECTABLE = (1 << 2),
+
+ /* Object comes from a duplicator. */
+ BASE_FROM_DUPLI = (1 << 3),
+
+ /* Object is enabled and visible in a viewport with default viewport settings,
+ * (so without any local view or local collection visibility overrides). Used
+ * when editors other than the 3D viewport need to know if an object is visible. */
+ BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT = (1 << 4),
+
+ /* Object comes from a scene set. */
+ BASE_FROM_SET = (1 << 5),
+
+ /* Object is enabled for viewport or final render respectively. Only enabled
+ * objects can be pulled into the depsgraph for evaluation, either through being
+ * directly visible, as a dependency of another object, or as part of colliders
+ * and effectors for physics. */
+ BASE_ENABLED_VIEWPORT = (1 << 6),
+ BASE_ENABLED_RENDER = (1 << 7),
+
/* BASE_DEPRECATED = (1 << 9), */
- BASE_HOLDOUT = (1 << 10), /* Object masked out from render */
- BASE_INDIRECT_ONLY = (1 << 11), /* Object only contributes indirectly to render */
+
+ /* Object masked out from render */
+ BASE_HOLDOUT = (1 << 10),
+ /* Object only contributes indirectly to render */
+ BASE_INDIRECT_ONLY = (1 << 11),
};
/* LayerCollection->flag */
@@ -237,6 +273,7 @@ enum {
VIEW_LAYER_RENDER = (1 << 0),
/* VIEW_LAYER_DEPRECATED = (1 << 1), */
VIEW_LAYER_FREESTYLE = (1 << 2),
+ VIEW_LAYER_OUT_OF_SYNC = (1 << 3),
};
/****************************** Deprecated ******************************/
diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h
index d6afaf33052..5b5bc4c7392 100644
--- a/source/blender/makesdna/DNA_lightprobe_types.h
+++ b/source/blender/makesdna/DNA_lightprobe_types.h
@@ -64,7 +64,6 @@ typedef struct LightProbe {
/* Runtime display data */
float distfalloff, distgridinf;
- char _pad[8];
} LightProbe;
/* Probe->type */
diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h
index 1ff656f85ed..05380325852 100644
--- a/source/blender/makesdna/DNA_lineart_types.h
+++ b/source/blender/makesdna/DNA_lineart_types.h
@@ -38,7 +38,6 @@ typedef enum eLineartMainFlags {
LRT_USE_BACK_FACE_CULLING = (1 << 19),
LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20),
LRT_CHAIN_PRESERVE_DETAILS = (1 << 22),
- LRT_SHADOW_ENCLOSED_SHAPES = (1 << 23),
LRT_SHADOW_USE_SILHOUETTE = (1 << 24),
} eLineartMainFlags;
diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h
index 2eca84959b8..cbefbf021a9 100644
--- a/source/blender/makesdna/DNA_mesh_types.h
+++ b/source/blender/makesdna/DNA_mesh_types.h
@@ -10,8 +10,21 @@
#include "DNA_ID.h"
#include "DNA_customdata_types.h"
#include "DNA_defs.h"
+#include "DNA_meshdata_types.h"
#include "DNA_session_uuid_types.h"
+/** Workaround to forward-declare C++ type in C header. */
+#ifdef __cplusplus
+namespace blender {
+template<typename T> class Span;
+template<typename T> class MutableSpan;
+namespace bke {
+class AttributeAccessor;
+class MutableAttributeAccessor;
+} // namespace bke
+} // namespace blender
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -23,11 +36,8 @@ struct Key;
struct MCol;
struct MEdge;
struct MFace;
-struct MLoop;
struct MLoopCol;
struct MLoopTri;
-struct MLoopUV;
-struct MPoly;
struct MVert;
struct Material;
struct Mesh;
@@ -112,7 +122,7 @@ typedef struct Mesh_Runtime {
* (most #eModifierTypeType_NonGeometrical modifiers). Otherwise the edit-mesh
* data will be used for drawing, missing changes from modifiers. See T79517.
*/
- char is_original;
+ char is_original_bmesh;
/** #eMeshWrapperType and others. */
char wrapper_type;
@@ -161,34 +171,11 @@ typedef struct Mesh {
/**
* An array of materials, with length #totcol. These can be overridden by material slots
- * on #Object. Indices in #MPoly.mat_nr control which material is used for every face.
+ * on #Object. Indices in the "material_index" attribute control which material is used for every
+ * face.
*/
struct Material **mat;
- /**
- * Array of vertices. Edges and faces are defined by indices into this array.
- * \note This pointer is for convenient access to the #CD_MVERT layer in #vdata.
- */
- struct MVert *mvert;
- /**
- * Array of edges, containing vertex indices. For simple triangle or quad meshes, edges could be
- * calculated from the #MPoly and #MLoop arrays, however, edges need to be stored explicitly to
- * edge domain attributes and to support loose edges that aren't connected to faces.
- * \note This pointer is for convenient access to the #CD_MEDGE layer in #edata.
- */
- struct MEdge *medge;
- /**
- * Face topology storage of the size and offset of each face's section of the #mloop face corner
- * array. Also stores various flags and the `material_index` attribute.
- * \note This pointer is for convenient access to the #CD_MPOLY layer in #pdata.
- */
- struct MPoly *mpoly;
- /**
- * The vertex and edge index at each face corner.
- * \note This pointer is for convenient access to the #CD_MLOOP layer in #ldata.
- */
- struct MLoop *mloop;
-
/** The number of vertices (#MVert) in the mesh, and the size of #vdata. */
int totvert;
/** The number of edges (#MEdge) in the mesh, and the size of #edata. */
@@ -200,8 +187,6 @@ typedef struct Mesh {
CustomData vdata, edata, pdata, ldata;
- /** "Vertex group" vertices. */
- struct MDeformVert *dvert;
/**
* List of vertex group (#bDeformGroup) names and flags only. Actual weights are stored in dvert.
* \note This pointer is for convenient access to the #CD_MDEFORMVERT layer in #vdata.
@@ -217,18 +202,6 @@ typedef struct Mesh {
int attributes_active_index;
/**
- * 2D vector data used for UVs. "UV" data can also be stored as generic attributes in #ldata.
- * \note This pointer is for convenient access to the #CD_MLOOPUV layer in #ldata.
- */
- struct MLoopUV *mloopuv;
- /**
- * The active vertex corner color layer, if it exists. Also called "Vertex Color" in Blender's
- * UI, even though it is stored per face corner.
- * \note This pointer is for convenient access to the #CD_PROP_BYTE_COLOR layer in #ldata.
- */
- struct MLoopCol *mloopcol;
-
- /**
* Runtime storage of the edit mode mesh. If it exists, it generally has the most up-to-date
* information about the mesh.
* \note When the object is available, the preferred access method is #BKE_editmesh_from_object.
@@ -303,29 +276,32 @@ typedef struct Mesh {
char subdivr DNA_DEPRECATED;
char subsurftype DNA_DEPRECATED;
- /**
- * Deprecated. Store of runtime data for tessellation face UVs and texture.
- *
- * \note This would be marked deprecated, however the particles still use this at run-time
- * for placing particles on the mesh (something which should be eventually upgraded).
- */
- struct MTFace *mtface;
+ /** Deprecated pointer to mesh polygons, kept for forward compatibility. */
+ struct MPoly *mpoly DNA_DEPRECATED;
+ /** Deprecated pointer to face corners, kept for forward compatibility. */
+ struct MLoop *mloop DNA_DEPRECATED;
+
+ /** Deprecated array of mesh vertices, kept for reading old files, now stored in #CustomData. */
+ struct MVert *mvert DNA_DEPRECATED;
+ /** Deprecated array of mesh edges, kept for reading old files, now stored in #CustomData. */
+ struct MEdge *medge DNA_DEPRECATED;
+ /** Deprecated "Vertex group" data. Kept for reading old files, now stored in #CustomData. */
+ struct MDeformVert *dvert DNA_DEPRECATED;
+ /** Deprecated runtime data for tessellation face UVs and texture, kept for reading old files. */
+ struct MTFace *mtface DNA_DEPRECATED;
/** Deprecated, use mtface. */
struct TFace *tface DNA_DEPRECATED;
-
- /* Deprecated. Array of colors for the tessellated faces, must be number of tessellated
- * faces * 4 in length. This is stored in #fdata, and deprecated. */
- struct MCol *mcol;
+ /** Deprecated array of colors for the tessellated faces, kept for reading old files. */
+ struct MCol *mcol DNA_DEPRECATED;
+ /** Deprecated face storage (quads & triangles only). Kept for reading old files. */
+ struct MFace *mface DNA_DEPRECATED;
/**
- * Deprecated face storage (quads & triangles only);
- * faces are now pointed to by #Mesh.mpoly and #Mesh.mloop.
+ * Deprecated storage of old faces (only triangles or quads).
*
* \note This would be marked deprecated, however the particles still use this at run-time
* for placing particles on the mesh (something which should be eventually upgraded).
*/
- struct MFace *mface;
- /* Deprecated storage of old faces (only triangles or quads). */
CustomData fdata;
/* Deprecated size of #fdata. */
int totface;
@@ -344,6 +320,48 @@ typedef struct Mesh {
void *_pad2;
Mesh_Runtime runtime;
+#ifdef __cplusplus
+ /**
+ * Array of vertex positions (and various other data). Edges and faces are defined by indices
+ * into this array.
+ */
+ blender::Span<MVert> verts() const;
+ /** Write access to vertex data. */
+ blender::MutableSpan<MVert> verts_for_write();
+ /**
+ * Array of edges, containing vertex indices. For simple triangle or quad meshes, edges could be
+ * calculated from the #MPoly and #MLoop arrays, however, edges need to be stored explicitly to
+ * edge domain attributes and to support loose edges that aren't connected to faces.
+ */
+ blender::Span<MEdge> edges() const;
+ /** Write access to edge data. */
+ blender::MutableSpan<MEdge> edges_for_write();
+ /**
+ * Face topology storage of the size and offset of each face's section of the face corners.
+ */
+ blender::Span<MPoly> polys() const;
+ /** Write access to polygon data. */
+ blender::MutableSpan<MPoly> polys_for_write();
+ /**
+ * Mesh face corners that "loop" around each face, storing the vertex index and the index of the
+ * subsequent edge.
+ */
+ blender::Span<MLoop> loops() const;
+ /** Write access to loop data. */
+ blender::MutableSpan<MLoop> loops_for_write();
+
+ blender::bke::AttributeAccessor attributes() const;
+ blender::bke::MutableAttributeAccessor attributes_for_write();
+
+ /**
+ * Vertex group data, encoded as an array of indices and weights for every vertex.
+ * \warning: May be empty.
+ */
+ blender::Span<MDeformVert> deform_verts() const;
+ /** Write access to vertex group data. */
+ blender::MutableSpan<MDeformVert> deform_verts_for_write();
+
+#endif
} Mesh;
/* deprecated by MTFace, only here for file reading */
@@ -422,8 +440,10 @@ enum {
/** #Mesh.cd_flag */
enum {
+#ifdef DNA_DEPRECATED_ALLOW
ME_CDFLAG_VERT_BWEIGHT = 1 << 0,
ME_CDFLAG_EDGE_BWEIGHT = 1 << 1,
+#endif
ME_CDFLAG_EDGE_CREASE = 1 << 2,
ME_CDFLAG_VERT_CREASE = 1 << 3,
};
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index 2a4234bde6a..77cb27083ab 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -25,15 +25,23 @@ extern "C" {
*/
typedef struct MVert {
float co[3];
- char flag, bweight;
+ char flag;
+ /**
+ * Deprecated bevel weight storage, now located in #CD_BWEIGHT, except for file read and write.
+ */
+ char bweight_legacy;
char _pad[2];
} MVert;
/** #MVert.flag */
+
+#ifdef DNA_DEPRECATED_ALLOW
enum {
/* SELECT = (1 << 0), */
+ /** Deprecated hide status. Now stored in ".hide_vert" attribute. */
ME_HIDE = (1 << 4),
};
+#endif
/**
* Mesh Edges.
@@ -43,7 +51,11 @@ enum {
typedef struct MEdge {
/** Un-ordered vertex indices (cannot match). */
unsigned int v1, v2;
- char crease, bweight;
+ char crease;
+ /**
+ * Deprecated bevel weight storage, now located in #CD_BWEIGHT, except for file read and write.
+ */
+ char bweight_legacy;
short flag;
} MEdge;
@@ -52,10 +64,10 @@ enum {
/* SELECT = (1 << 0), */
ME_EDGEDRAW = (1 << 1),
ME_SEAM = (1 << 2),
+ /** Deprecated hide status. Now stored in ".hide_edge" attribute. */
/* ME_HIDE = (1 << 4), */
ME_EDGERENDER = (1 << 5),
ME_LOOSEEDGE = (1 << 7),
- ME_EDGE_TMP_TAG = (1 << 8),
ME_SHARP = (1 << 9), /* only reason this flag remains a 'short' */
};
@@ -70,7 +82,8 @@ typedef struct MPoly {
int loopstart;
/** Keep signed since we need to subtract when getting the previous loop. */
int totloop;
- short mat_nr;
+ /** Deprecated material index. Now stored in the "material_index" attribute, but kept for IO. */
+ short mat_nr_legacy;
char flag, _pad;
} MPoly;
@@ -78,6 +91,7 @@ typedef struct MPoly {
enum {
ME_SMOOTH = (1 << 0),
ME_FACE_SEL = (1 << 1),
+ /** Deprecated hide status. Now stored in ".hide_poly" attribute. */
/* ME_HIDE = (1 << 4), */
};
@@ -151,8 +165,8 @@ enum {
*
* Usage examples:
* \code{.c}
- * // access original material.
- * short mat_nr = mpoly[lt->poly].mat_nr;
+ * // access polygon attribute value.
+ * T value = polygon_attribute[lt->poly];
*
* // access vertex locations.
* float *vtri_co[3] = {
@@ -352,7 +366,7 @@ typedef struct MDisps {
/**
* Used for hiding parts of a multires mesh.
- * Essentially the multires equivalent of #MVert.flag's ME_HIDE bit.
+ * Essentially the multires equivalent of the mesh ".hide_vert" boolean layer.
*
* \note This is a bitmap, keep in sync with type used in BLI_bitmap.h
*/
diff --git a/source/blender/makesdna/DNA_meta_types.h b/source/blender/makesdna/DNA_meta_types.h
index 519dfb7e9b3..d0c09a0d6ab 100644
--- a/source/blender/makesdna/DNA_meta_types.h
+++ b/source/blender/makesdna/DNA_meta_types.h
@@ -92,8 +92,6 @@ typedef struct MetaBall {
/* used in editmode */
// ListBase edit_elems;
MetaElem *lastelem;
-
- void *batch_cache;
} MetaBall;
/* **************** METABALL ********************* */
diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h
index f148116eba8..7625f04fefa 100644
--- a/source/blender/makesdna/DNA_modifier_types.h
+++ b/source/blender/makesdna/DNA_modifier_types.h
@@ -1856,9 +1856,13 @@ enum {
};
typedef struct CorrectiveSmoothDeltaCache {
- /* delta's between the original positions and the smoothed positions */
+ /**
+ * Delta's between the original positions and the smoothed positions,
+ * calculated loop-tangent and which is accumulated into the vertex it uses.
+ * (run-time only).
+ */
float (*deltas)[3];
- unsigned int totverts;
+ unsigned int deltas_num;
/* Value of settings when creating the cache.
* These are used to check if the cache should be recomputed. */
@@ -2217,7 +2221,7 @@ typedef struct SurfaceDeformModifierData {
SDefVert *verts;
void *_pad1;
float falloff;
- /* Number of of vertices on the deformed mesh upon the bind process. */
+ /* Number of vertices on the deformed mesh upon the bind process. */
unsigned int mesh_verts_num;
/* Number of vertices in the `verts` array of this modifier. */
unsigned int bind_verts_num;
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 76d8207eead..06e14c3134b 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -12,8 +12,33 @@
#include "DNA_scene_types.h" /* for #ImageFormatData */
#include "DNA_vec_types.h" /* for #rctf */
+/** Workaround to forward-declare C++ type in C header. */
#ifdef __cplusplus
-extern "C" {
+namespace blender {
+template<typename T> class Span;
+class StringRef;
+class StringRefNull;
+} // namespace blender
+namespace blender::nodes {
+class NodeDeclaration;
+class SocketDeclaration;
+} // namespace blender::nodes
+namespace blender::bke {
+class bNodeTreeRuntime;
+class bNodeRuntime;
+class bNodeSocketRuntime;
+} // namespace blender::bke
+using NodeDeclarationHandle = blender::nodes::NodeDeclaration;
+using SocketDeclarationHandle = blender::nodes::SocketDeclaration;
+using bNodeTreeRuntimeHandle = blender::bke::bNodeTreeRuntime;
+using bNodeRuntimeHandle = blender::bke::bNodeRuntime;
+using bNodeSocketRuntimeHandle = blender::bke::bNodeSocketRuntime;
+#else
+typedef struct NodeDeclarationHandle NodeDeclarationHandle;
+typedef struct SocketDeclarationHandle SocketDeclarationHandle;
+typedef struct bNodeTreeRuntimeHandle bNodeTreeRuntimeHandle;
+typedef struct bNodeRuntimeHandle bNodeRuntimeHandle;
+typedef struct bNodeSocketRuntimeHandle bNodeSocketRuntimeHandle;
#endif
struct AnimData;
@@ -30,6 +55,7 @@ struct bNodeLink;
struct bNodePreview;
struct bNodeTreeExec;
struct bNodeType;
+struct bNode;
struct uiBlock;
#define NODE_MAXSTR 64
@@ -65,30 +91,6 @@ typedef struct bNodeStack {
#define NS_CR_FIT 4
#define NS_CR_STRETCH 5
-/** Workaround to forward-declare C++ type in C header. */
-#ifdef __cplusplus
-namespace blender::nodes {
-class NodeDeclaration;
-class SocketDeclaration;
-} // namespace blender::nodes
-namespace blender::bke {
-class bNodeTreeRuntime;
-class bNodeRuntime;
-class bNodeSocketRuntime;
-} // namespace blender::bke
-using NodeDeclarationHandle = blender::nodes::NodeDeclaration;
-using SocketDeclarationHandle = blender::nodes::SocketDeclaration;
-using bNodeTreeRuntimeHandle = blender::bke::bNodeTreeRuntime;
-using bNodeRuntimeHandle = blender::bke::bNodeRuntime;
-using bNodeSocketRuntimeHandle = blender::bke::bNodeSocketRuntime;
-#else
-typedef struct NodeDeclarationHandle NodeDeclarationHandle;
-typedef struct SocketDeclarationHandle SocketDeclarationHandle;
-typedef struct bNodeTreeRuntimeHandle bNodeTreeRuntimeHandle;
-typedef struct bNodeRuntimeHandle bNodeRuntimeHandle;
-typedef struct bNodeSocketRuntimeHandle bNodeSocketRuntimeHandle;
-#endif
-
typedef struct bNodeSocket {
struct bNodeSocket *next, *prev;
@@ -181,6 +183,50 @@ typedef struct bNodeSocket {
bNodeStack ns DNA_DEPRECATED;
bNodeSocketRuntimeHandle *runtime;
+
+#ifdef __cplusplus
+ bool is_available() const;
+ bool is_multi_input() const;
+ bool is_input() const;
+ bool is_output() const;
+
+ /** Utility to access the value of the socket. */
+ template<typename T> const T *default_value_typed() const;
+
+ /* The following methods are only available when #bNodeTree.ensure_topology_cache has been
+ * called. */
+
+ /** Zero based index for every input and output socket. */
+ int index() const;
+ /** Socket index in the entire node tree. Inputs and outputs share the same index space. */
+ int index_in_tree() const;
+ /** Node this socket belongs to. */
+ bNode &owner_node();
+ const bNode &owner_node() const;
+ /** Node tree this socket belongs to. */
+ const bNodeTree &owner_tree() const;
+
+ /** Links which are incident to this socket. */
+ blender::Span<bNodeLink *> directly_linked_links();
+ blender::Span<const bNodeLink *> directly_linked_links() const;
+ /** Sockets which are connected to this socket with a link. */
+ blender::Span<bNodeSocket *> directly_linked_sockets();
+ blender::Span<const bNodeSocket *> directly_linked_sockets() const;
+ bool is_directly_linked() const;
+ /**
+ * Sockets which are connected to this socket when reroutes and muted nodes are taken into
+ * account.
+ */
+ blender::Span<const bNodeSocket *> logically_linked_sockets() const;
+ bool is_logically_linked() const;
+
+ /**
+ * For output sockets, this is the corresponding input socket the value of which should be
+ * forwarded when the node is muted.
+ */
+ const bNodeSocket *internal_link_input() const;
+
+#endif
} bNodeSocket;
/** #bNodeSocket.type & #bNodeSocketType.type */
@@ -333,6 +379,40 @@ typedef struct bNode {
char iter_flag;
bNodeRuntimeHandle *runtime;
+
+#ifdef __cplusplus
+ blender::StringRefNull label_or_name() const;
+ bool is_muted() const;
+ bool is_reroute() const;
+ bool is_frame() const;
+ bool is_group() const;
+ bool is_group_input() const;
+ bool is_group_output() const;
+ const blender::nodes::NodeDeclaration *declaration() const;
+
+ /* The following methods are only available when #bNodeTree.ensure_topology_cache has been
+ * called. */
+
+ /** A span containing all input sockets of the node (including unavailable sockets). */
+ blender::Span<bNodeSocket *> input_sockets();
+ blender::Span<const bNodeSocket *> input_sockets() const;
+ /** A span containing all output sockets of the node (including unavailable sockets). */
+ blender::Span<bNodeSocket *> output_sockets();
+ blender::Span<const bNodeSocket *> output_sockets() const;
+ /** Utility to get an input socket by its index. */
+ bNodeSocket &input_socket(int index);
+ const bNodeSocket &input_socket(int index) const;
+ /** Utility to get an output socket by its index. */
+ bNodeSocket &output_socket(int index);
+ const bNodeSocket &output_socket(int index) const;
+ /** A span containing all internal links when the node is muted. */
+ blender::Span<const bNodeLink *> internal_links_span() const;
+ /** Lookup socket of this node by its identifier. */
+ const bNodeSocket &input_by_identifier(blender::StringRef identifier) const;
+ const bNodeSocket &output_by_identifier(blender::StringRef identifier) const;
+ /** Node tree this node belongs to. */
+ const bNodeTree &owner_tree() const;
+#endif
} bNode;
/* node->flag */
@@ -422,6 +502,12 @@ typedef struct bNodeLink {
int flag;
int multi_input_socket_index;
+
+#ifdef __cplusplus
+ bool is_muted() const;
+ bool is_available() const;
+#endif
+
} bNodeLink;
/* link->flag */
@@ -453,6 +539,9 @@ typedef struct bNodeTree {
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
+ /** The ID owning this node tree, in case it is an embedded one. */
+ ID *owner_id;
+
/** Runtime type information. */
struct bNodeTreeType *typeinfo;
/** Runtime type identifier. */
@@ -535,6 +624,53 @@ typedef struct bNodeTree {
struct PreviewImage *preview;
bNodeTreeRuntimeHandle *runtime;
+
+#ifdef __cplusplus
+ /**
+ * Update a run-time cache for the node tree based on it's current state. This makes many methods
+ * available which allow efficient lookup for topology information (like neighboring sockets).
+ */
+ void ensure_topology_cache() const;
+
+ /* The following methods are only available when #bNodeTree.ensure_topology_cache has been
+ * called. */
+
+ /** A span containing all nodes in the node tree. */
+ blender::Span<bNode *> all_nodes();
+ blender::Span<const bNode *> all_nodes() const;
+ /** A span containing all group nodes in the node tree. */
+ blender::Span<bNode *> group_nodes();
+ blender::Span<const bNode *> group_nodes() const;
+ /** A span containing all input sockets in the node tree. */
+ blender::Span<bNodeSocket *> all_input_sockets();
+ blender::Span<const bNodeSocket *> all_input_sockets() const;
+ /** A span containing all output sockets in the node tree. */
+ blender::Span<bNodeSocket *> all_output_sockets();
+ blender::Span<const bNodeSocket *> all_output_sockets() const;
+ /** A span containing all sockets in the node tree. */
+ blender::Span<bNodeSocket *> all_sockets();
+ blender::Span<const bNodeSocket *> all_sockets() const;
+ /** Efficient lookup of all nodes with a specific type. */
+ blender::Span<bNode *> nodes_by_type(blender::StringRefNull type_idname);
+ blender::Span<const bNode *> nodes_by_type(blender::StringRefNull type_idname) const;
+ /**
+ * Cached toposort of all nodes. If there are cycles, the returned array is not actually a
+ * toposort. However, if a connected component does not contain a cycle, this component is sorted
+ * correctly. Use #has_available_link_cycle to check for cycles.
+ */
+ blender::Span<const bNode *> toposort_left_to_right() const;
+ blender::Span<const bNode *> toposort_right_to_left() const;
+ /** True when there are any cycles in the node tree. */
+ bool has_available_link_cycle() const;
+ /**
+ * True when there are nodes or sockets in the node tree that don't use a known type. This can
+ * happen when nodes don't exist in the current Blender version that existed in the version where
+ * this node tree was saved.
+ */
+ bool has_undefined_nodes_or_sockets() const;
+ /** Get the active group output node. */
+ const bNode *group_output_node() const;
+#endif
} bNodeTree;
/** #NodeTree.type, index */
@@ -632,19 +768,19 @@ typedef struct bNodeSocketValueMaterial {
/* Data structs, for `node->storage`. */
-enum {
+typedef enum CMPNodeMaskType {
CMP_NODE_MASKTYPE_ADD = 0,
CMP_NODE_MASKTYPE_SUBTRACT = 1,
CMP_NODE_MASKTYPE_MULTIPLY = 2,
CMP_NODE_MASKTYPE_NOT = 3,
-};
+} CMPNodeMaskType;
-enum {
- CMP_NODE_DILATEERODE_STEP = 0,
- CMP_NODE_DILATEERODE_DISTANCE_THRESH = 1,
- CMP_NODE_DILATEERODE_DISTANCE = 2,
- CMP_NODE_DILATEERODE_DISTANCE_FEATHER = 3,
-};
+typedef enum CMPNodeDilateErodeMethod {
+ CMP_NODE_DILATE_ERODE_STEP = 0,
+ CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD = 1,
+ CMP_NODE_DILATE_ERODE_DISTANCE = 2,
+ CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER = 3,
+} CMPNodeDilateErodeMethod;
enum {
CMP_NODE_INPAINT_SIMPLE = 0,
@@ -857,7 +993,7 @@ typedef struct NodeGlare {
char _pad1[4];
} NodeGlare;
-/** Tonemap node. */
+/** Tone-map node. */
typedef struct NodeTonemap {
float key, offset, gamma;
float f, m, a, c;
@@ -1460,6 +1596,11 @@ typedef struct NodeGeometryUVUnwrap {
uint8_t method;
} NodeGeometryUVUnwrap;
+typedef struct NodeGeometryDistributePointsInVolume {
+ /* GeometryNodePointDistributeVolumeMode. */
+ uint8_t mode;
+} NodeGeometryDistributePointsInVolume;
+
typedef struct NodeFunctionCompare {
/* NodeCompareOperation */
int8_t operation;
@@ -1475,6 +1616,17 @@ typedef struct NodeCombSepColor {
int8_t mode;
} NodeCombSepColor;
+typedef struct NodeShaderMix {
+ /* eNodeSocketDatatype */
+ int8_t data_type;
+ /* NodeShaderMixMode */
+ int8_t factor_mode;
+ int8_t clamp_factor;
+ int8_t clamp_result;
+ int8_t blend_type;
+ char _pad[3];
+} NodeShaderMix;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1762,6 +1914,11 @@ typedef enum NodeBooleanMathOperation {
NODE_BOOLEAN_MATH_NIMPLY = 8,
} NodeBooleanMathOperation;
+typedef enum NodeShaderMixMode {
+ NODE_MIX_MODE_UNIFORM = 0,
+ NODE_MIX_MODE_NON_UNIFORM = 1,
+} NodeShaderMixMode;
+
typedef enum NodeCompareMode {
NODE_COMPARE_MODE_ELEMENT = 0,
NODE_COMPARE_MODE_LENGTH = 1,
@@ -1838,6 +1995,76 @@ enum {
/* viewer and composite output. */
#define CMP_NODE_OUTPUT_IGNORE_ALPHA 1
+/** Split Viewer Node. Stored in `custom2`. */
+typedef enum CMPNodeSplitViewerAxis {
+ CMP_NODE_SPLIT_VIEWER_HORIZONTAL = 0,
+ CMP_NODE_SPLIT_VIEWER_VERTICAL = 1,
+} CMPNodeSplitViewerAxis;
+
+/** Color Balance Node. Stored in `custom1`. */
+typedef enum CMPNodeColorBalanceMethod {
+ CMP_NODE_COLOR_BALANCE_LGG = 0,
+ CMP_NODE_COLOR_BALANCE_ASC_CDL = 1,
+} CMPNodeColorBalanceMethod;
+
+/** Alpha Convert Node. Stored in `custom1`. */
+typedef enum CMPNodeAlphaConvertMode {
+ CMP_NODE_ALPHA_CONVERT_PREMULTIPLY = 0,
+ CMP_NODE_ALPHA_CONVERT_UNPREMULTIPLY = 1,
+} CMPNodeAlphaConvertMode;
+
+/** Distance Matte Node. Stored in #NodeChroma.channel. */
+typedef enum CMPNodeDistanceMatteColorSpace {
+ CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_YCCA = 0,
+ CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA = 1,
+} CMPNodeDistanceMatteColorSpace;
+
+/** Color Spill Node. Stored in `custom2`. */
+typedef enum CMPNodeColorSpillLimitAlgorithm {
+ CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE = 0,
+ CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE = 1,
+} CMPNodeColorSpillLimitAlgorithm;
+
+/** Channel Matte Node. Stored in #NodeChroma.algorithm. */
+typedef enum CMPNodeChannelMatteLimitAlgorithm {
+ CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE = 0,
+ CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX = 1,
+} CMPNodeChannelMatteLimitAlgorithm;
+
+/* Flip Node. Stored in custom1. */
+typedef enum CMPNodeFlipMode {
+ CMP_NODE_FLIP_X = 0,
+ CMP_NODE_FLIP_Y = 1,
+ CMP_NODE_FLIP_X_Y = 2,
+} CMPNodeFlipMode;
+
+/* Scale Node. Stored in custom1. */
+typedef enum CMPNodeScaleMethod {
+ CMP_NODE_SCALE_RELATIVE = 0,
+ CMP_NODE_SCALE_ABSOLUTE = 1,
+ CMP_NODE_SCALE_RENDER_PERCENT = 2,
+ CMP_NODE_SCALE_RENDER_SIZE = 3,
+} CMPNodeScaleMethod;
+
+/* Scale Node. Stored in custom2. */
+typedef enum CMPNodeScaleRenderSizeMethod {
+ CMP_NODE_SCALE_RENDER_SIZE_STRETCH = 0,
+ CMP_NODE_SCALE_RENDER_SIZE_FIT = 1,
+ CMP_NODE_SCALE_RENDER_SIZE_CROP = 2,
+} CMPNodeScaleRenderSizeMethod;
+
+/* Filter Node. Stored in custom1. */
+typedef enum CMPNodeFilterMethod {
+ CMP_NODE_FILTER_SOFT = 0,
+ CMP_NODE_FILTER_SHARP_BOX = 1,
+ CMP_NODE_FILTER_LAPLACE = 2,
+ CMP_NODE_FILTER_SOBEL = 3,
+ CMP_NODE_FILTER_PREWITT = 4,
+ CMP_NODE_FILTER_KIRSCH = 5,
+ CMP_NODE_FILTER_SHADOW = 6,
+ CMP_NODE_FILTER_SHARP_DIAMOND = 7,
+} CMPNodeFilterMethod;
+
/* Plane track deform node. */
enum {
@@ -1955,6 +2182,11 @@ typedef enum GeometryNodeTriangulateQuads {
GEO_NODE_TRIANGULATE_QUAD_LONGEDGE = 4,
} GeometryNodeTriangulateQuads;
+typedef enum GeometryNodeDistributePointsInVolumeMode {
+ GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM = 0,
+ GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID = 1,
+} GeometryNodeDistributePointsInVolumeMode;
+
typedef enum GeometryNodeDistributePointsOnFacesMode {
GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM = 0,
GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1,
@@ -2139,7 +2371,3 @@ typedef enum NodeCombSepColorMode {
NODE_COMBSEP_COLOR_HSV = 1,
NODE_COMBSEP_COLOR_HSL = 2,
} NodeCombSepColorMode;
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index ac9e61e03e8..8296855ec29 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -231,6 +231,7 @@ enum eObjectLineArt_Usage {
OBJECT_LRT_EXCLUDE = (1 << 2),
OBJECT_LRT_INTERSECTION_ONLY = (1 << 3),
OBJECT_LRT_NO_INTERSECTION = (1 << 4),
+ OBJECT_LRT_FORCE_INTERSECTION = (1 << 5),
};
enum eObjectLineArt_Flags {
@@ -477,7 +478,13 @@ typedef struct ObHook {
/* **************** OBJECT ********************* */
-/* used many places, should be specialized. */
+/**
+ * This is used as a flag for many kinds of data that use selections, examples include:
+ * - #BezTriple.f1, #BezTriple.f2, #BezTriple.f3
+ * - #bNote.flag
+ * - #MovieTrackingTrack.flag
+ * And more, ideally this would have a generic location.
+ */
#define SELECT 1
/** #Object.type */
diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h
index 4a2e3e1ab72..489fb6917e8 100644
--- a/source/blender/makesdna/DNA_outliner_types.h
+++ b/source/blender/makesdna/DNA_outliner_types.h
@@ -113,7 +113,9 @@ typedef enum eTreeStoreElemType {
TSE_GPENCIL_EFFECT_BASE = 42,
TSE_GPENCIL_EFFECT = 43,
TSE_LIBRARY_OVERRIDE_BASE = 44,
- TSE_LIBRARY_OVERRIDE = 45,
+ TSE_LIBRARY_OVERRIDE = 45, /* No ID */
+ TSE_LIBRARY_OVERRIDE_OPERATION = 46, /* No ID */
+ TSE_GENERIC_LABEL = 47, /* No ID */
} eTreeStoreElemType;
/** Check whether given #TreeStoreElem should have a real ID in #TreeStoreElem.id member. */
@@ -129,7 +131,8 @@ typedef enum eTreeStoreElemType {
TSE_RNA_PROPERTY, \
TSE_RNA_ARRAY_ELEM, \
TSE_ID_BASE, \
- TSE_GP_LAYER))
+ TSE_GP_LAYER, \
+ TSE_GENERIC_LABEL))
#ifdef __cplusplus
}
diff --git a/source/blender/makesdna/DNA_particle_types.h b/source/blender/makesdna/DNA_particle_types.h
index 268e1412eef..f152a5bcc61 100644
--- a/source/blender/makesdna/DNA_particle_types.h
+++ b/source/blender/makesdna/DNA_particle_types.h
@@ -159,7 +159,7 @@ typedef struct SPHFluidSettings {
char _pad[6];
} SPHFluidSettings;
-/* fluid->flag */
+/** #SPHFluidSettings.flag */
#define SPH_VISCOELASTIC_SPRINGS 1
#define SPH_CURRENT_REST_LENGTH 2
#define SPH_FAC_REPULSION 4
@@ -168,7 +168,7 @@ typedef struct SPHFluidSettings {
#define SPH_FAC_VISCOSITY 32
#define SPH_FAC_REST_LENGTH 64
-/* fluid->solver (numerical ID field, not bitfield) */
+/** #SPHFluidSettings.solver (numerical ID field, not bit-field). */
#define SPH_SOLVER_DDR 0
#define SPH_SOLVER_CLASSICAL 1
diff --git a/source/blender/makesdna/DNA_pointcloud_types.h b/source/blender/makesdna/DNA_pointcloud_types.h
index 1cb490190c3..34c5d153165 100644
--- a/source/blender/makesdna/DNA_pointcloud_types.h
+++ b/source/blender/makesdna/DNA_pointcloud_types.h
@@ -10,6 +10,13 @@
#include "DNA_customdata_types.h"
#ifdef __cplusplus
+namespace blender::bke {
+class AttributeAccessor;
+class MutableAttributeAccessor;
+} // namespace blender::bke
+#endif
+
+#ifdef __cplusplus
extern "C" {
#endif
@@ -18,13 +25,9 @@ typedef struct PointCloud {
struct AnimData *adt; /* animation data (must be immediately after id) */
int flag;
- int _pad1[1];
/* Geometry */
- float (*co)[3];
- float *radius;
int totpoint;
- int _pad2[1];
/* Custom Data */
struct CustomData pdata;
@@ -36,6 +39,11 @@ typedef struct PointCloud {
short totcol;
short _pad3[3];
+#ifdef __cplusplus
+ blender::bke::AttributeAccessor attributes() const;
+ blender::bke::MutableAttributeAccessor attributes_for_write();
+#endif
+
/* Draw Cache */
void *batch_cache;
} PointCloud;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index f8fcd78d63b..23b76e855b2 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -9,7 +9,7 @@
#include "DNA_defs.h"
-/* XXX(campbell): temp feature. */
+/* XXX(@campbellbarton): temp feature. */
#define DURIAN_CAMERA_SWITCH
/* check for cyclic set-scene,
@@ -304,6 +304,10 @@ typedef enum eScenePassType {
#define RE_PASSNAME_BLOOM "BloomCol"
#define RE_PASSNAME_VOLUME_LIGHT "VolumeDir"
+#define RE_PASSNAME_CRYPTOMATTE_OBJECT "CryptoObject"
+#define RE_PASSNAME_CRYPTOMATTE_ASSET "CryptoAsset"
+#define RE_PASSNAME_CRYPTOMATTE_MATERIAL "CryptoMaterial"
+
/** View - MultiView. */
typedef struct SceneRenderView {
struct SceneRenderView *next, *prev;
@@ -397,12 +401,12 @@ typedef struct ImageFormatData {
/** R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA. */
char planes;
- /** Generic options for all image types, alpha zbuffer. */
+ /** Generic options for all image types, alpha Z-buffer. */
char flag;
- /** (0 - 100), eg: jpeg quality. */
+ /** (0 - 100), eg: JPEG quality. */
char quality;
- /** (0 - 100), eg: png compression. */
+ /** (0 - 100), eg: PNG compression. */
char compress;
/* --- format specific --- */
@@ -820,6 +824,7 @@ typedef struct RenderProfile {
/** #ToolSettings.uv_relax_method */
#define UV_SCULPT_TOOL_RELAX_LAPLACIAN 1
#define UV_SCULPT_TOOL_RELAX_HC 2
+#define UV_SCULPT_TOOL_RELAX_COTAN 3
/* Stereo Flags */
#define STEREO_RIGHT_NAME "right"
@@ -1495,12 +1500,12 @@ typedef struct ToolSettings {
/* Transform */
char transform_pivot_point;
char transform_flag;
- /** Snap elements (per spacetype), #eSnapMode. */
+ /** Snap elements (per space-type), #eSnapMode. */
char _pad1[1];
short snap_mode;
char snap_node_mode;
char snap_uv_mode;
- /** Generic flags (per spacetype), #eSnapFlag. */
+ /** Generic flags (per space-type), #eSnapFlag. */
short snap_flag;
short snap_flag_node;
short snap_flag_seq;
@@ -2037,18 +2042,10 @@ extern const char *RE_engine_id_CYCLES;
(BASE_EDITABLE(v3d, base) && (((base)->flag & BASE_SELECTED) != 0))
/* deprecate this! */
-#define FIRSTBASE(_view_layer) ((_view_layer)->object_bases.first)
-#define LASTBASE(_view_layer) ((_view_layer)->object_bases.last)
-#define BASACT(_view_layer) ((_view_layer)->basact)
-#define OBACT(_view_layer) (BASACT(_view_layer) ? BASACT(_view_layer)->object : NULL)
-
-#define OBEDIT_FROM_WORKSPACE(workspace, _view_layer) \
- (((workspace)->object_mode & OD_MODE_EDIT) ? OBACT(_view_layer) : NULL)
#define OBEDIT_FROM_OBACT(ob) ((ob) ? (((ob)->mode & OB_MODE_EDIT) ? ob : NULL) : NULL)
#define OBPOSE_FROM_OBACT(ob) ((ob) ? (((ob)->mode & OB_MODE_POSE) ? ob : NULL) : NULL)
#define OBWEIGHTPAINT_FROM_OBACT(ob) \
((ob) ? (((ob)->mode & OB_MODE_WEIGHT_PAINT) ? ob : NULL) : NULL)
-#define OBEDIT_FROM_VIEW_LAYER(view_layer) OBEDIT_FROM_OBACT(OBACT(view_layer))
#define V3D_CAMERA_LOCAL(v3d) ((!(v3d)->scenelock && (v3d)->camera) ? (v3d)->camera : NULL)
#define V3D_CAMERA_SCENE(scene, v3d) \
@@ -2209,7 +2206,7 @@ enum {
OB_DRAW_GROUPUSER_ALL = 2,
};
-/* object_vgroup.c */
+/* object_vgroup.cc */
/** #ToolSettings.vgroupsubset */
typedef enum eVGroupSelect {
diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h
index 8560f8a454e..4d4bd9ef775 100644
--- a/source/blender/makesdna/DNA_screen_types.h
+++ b/source/blender/makesdna/DNA_screen_types.h
@@ -305,12 +305,6 @@ typedef struct uiList { /* some list UI data need to be saved in file */
int filter_flag;
int filter_sort_flag;
- /** Operator executed when activating an item. */
- const char *custom_activate_opname;
- /** Operator executed when dragging an item (item gets activated too, without running
- * custom_activate_opname above). */
- const char *custom_drag_opname;
-
/* Custom sub-classes properties. */
IDProperty *properties;
@@ -443,7 +437,7 @@ typedef struct ARegion_Runtime {
* Lazy initialize, zero'd when unset, relative to #ARegion.winrct x/y min. */
rcti visible_rect;
- /* The offset needed to not overlap with window scrollbars. Only used by HUD regions for now. */
+ /* The offset needed to not overlap with window scroll-bars. Only used by HUD regions for now. */
int offset_x, offset_y;
/* Maps uiBlock->name to uiBlock for faster lookups. */
@@ -483,7 +477,7 @@ typedef struct ARegion {
short do_draw_paintcursor;
/** Private, set for indicate drawing overlapped. */
short overlap;
- /** Temporary copy of flag settings for clean fullscreen. */
+ /** Temporary copy of flag settings for clean full-screen. */
short flagfullscreen;
/** Callbacks for this region type. */
@@ -545,7 +539,7 @@ enum {
};
#define AREAGRID 4
-#define AREAMINX 32
+#define AREAMINX 29
#define HEADER_PADDING_Y 6
#define HEADERY (20 + HEADER_PADDING_Y)
@@ -634,7 +628,7 @@ enum {
/* Bitflags affecting behavior of any kind of sorting. */
/** Special flag to indicate that order is locked (not user-changeable). */
UILST_FLT_SORT_LOCK = 1u << 30,
- /** Special value, bitflag used to reverse order! */
+ /** Special value, bit-flag used to reverse order! */
UILST_FLT_SORT_REVERSE = 1u << 31,
};
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index a46d737ba9d..c0f92010c22 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -136,7 +136,7 @@ typedef struct SequenceRuntime {
*/
typedef struct Sequence {
struct Sequence *next, *prev;
- /** Tmp var for copying, and tagging for linked selection. */
+ /** Temp var for copying, and tagging for linked selection. */
void *tmp;
/** Needed (to be like ipo), else it will raise libdata warnings, this should never be used. */
void *lib;
@@ -522,8 +522,6 @@ typedef struct SequencerScopes {
#define MAXSEQ 128
-#define SELECT 1
-
/** #Editor.overlay_frame_flag */
#define SEQ_EDIT_OVERLAY_FRAME_SHOW 1
#define SEQ_EDIT_OVERLAY_FRAME_ABS 2
@@ -549,9 +547,12 @@ typedef struct SequencerScopes {
#define SEQ_NAME_MAXSTR 64
+/* From: `DNA_object_types.h`, see it's doc-string there. */
+#define SELECT 1
+
/** #Sequence.flag */
enum {
- /* SELECT */
+ /* `SELECT = (1 << 0)` */
SEQ_LEFTSEL = (1 << 1),
SEQ_RIGHTSEL = (1 << 2),
SEQ_OVERLAP = (1 << 3),
diff --git a/source/blender/makesdna/DNA_sound_types.h b/source/blender/makesdna/DNA_sound_types.h
index ba926f0f4fa..65d62b68561 100644
--- a/source/blender/makesdna/DNA_sound_types.h
+++ b/source/blender/makesdna/DNA_sound_types.h
@@ -78,7 +78,7 @@ typedef struct bSound {
/* XXX unused currently (SOUND_TYPE_LIMITER) */
/* float start, end; */
- /* Description of Audio channels, as of eSoundChannels*/
+ /* Description of Audio channels, as of #eSoundChannels. */
int audio_channels;
int samplerate;
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 6bc68baa640..5a819ead4ec 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -8,6 +8,8 @@
#pragma once
+#include "BLI_utildefines.h"
+
#include "DNA_asset_types.h"
#include "DNA_color_types.h" /* for Histogram */
#include "DNA_defs.h"
@@ -48,14 +50,19 @@ struct wmTimer;
/** Defined in `buttons_intern.h`. */
typedef struct SpaceProperties_Runtime SpaceProperties_Runtime;
-/** Defined in `node_intern.hh`. */
#ifdef __cplusplus
namespace blender::ed::space_node {
struct SpaceNode_Runtime;
} // namespace blender::ed::space_node
using SpaceNode_Runtime = blender::ed::space_node::SpaceNode_Runtime;
+
+namespace blender::ed::outliner {
+struct SpaceOutliner_Runtime;
+} // namespace blender::ed::outliner
+using SpaceOutliner_Runtime = blender::ed::outliner::SpaceOutliner_Runtime;
#else
typedef struct SpaceNode_Runtime SpaceNode_Runtime;
+typedef struct SpaceOutliner_Runtime SpaceOutliner_Runtime;
#endif
/** Defined in `file_intern.h`. */
@@ -250,9 +257,6 @@ typedef enum eSpaceButtons_OutlinerSync {
/** \name Outliner
* \{ */
-/** Defined in `outliner_intern.hh`. */
-typedef struct SpaceOutliner_Runtime SpaceOutliner_Runtime;
-
/** Outliner */
typedef struct SpaceOutliner {
SpaceLink *next, *prev;
@@ -276,9 +280,7 @@ typedef struct SpaceOutliner {
*/
struct BLI_mempool *treestore;
- /* search stuff */
char search_string[64];
- struct TreeStoreElem search_tse;
short flag;
short outlinevis;
@@ -405,8 +407,8 @@ typedef enum eSpaceOutliner_StoreFlag {
/* cleanup tree */
SO_TREESTORE_CLEANUP = (1 << 0),
SO_TREESTORE_UNUSED_1 = (1 << 1), /* cleared */
- /* rebuild the tree, similar to cleanup,
- * but defer a call to BKE_outliner_treehash_rebuild_from_treestore instead */
+ /** Rebuild the tree, similar to cleanup, but defer a call to
+ * bke::outliner::treehash::rebuild_from_treestore instead. */
SO_TREESTORE_REBUILD = (1 << 2),
} eSpaceOutliner_StoreFlag;
@@ -1028,6 +1030,7 @@ typedef enum eFileSel_Params_Flag {
/** Enables filtering by asset catalog. */
FILE_FILTER_ASSET_CATALOG = (1 << 15),
} eFileSel_Params_Flag;
+ENUM_OPERATORS(eFileSel_Params_Flag, FILE_FILTER_ASSET_CATALOG);
typedef enum eFileSel_Params_AssetCatalogVisibility {
FILE_SHOW_ASSETS_ALL_CATALOGS,
@@ -1083,6 +1086,7 @@ typedef enum eFileSel_File_Types {
FILE_TYPE_DIR = (1 << 30),
FILE_TYPE_BLENDERLIB = (1u << 31),
} eFileSel_File_Types;
+ENUM_OPERATORS(eFileSel_File_Types, FILE_TYPE_BLENDERLIB);
/** Selection Flags in filesel: struct direntry, unsigned char selflag. */
typedef enum eDirEntry_SelectFlag {
@@ -1215,7 +1219,7 @@ typedef struct SpaceImage {
char pin;
- char pixel_snap_mode;
+ char pixel_round_mode;
char lock;
/** UV draw type. */
@@ -1233,11 +1237,10 @@ typedef struct SpaceImage {
int tile_grid_shape[2];
/**
- * UV editor custom-grid. Value of `N` will produce `NxN` grid.
+ * UV editor custom-grid. Value of `{M,N}` will produce `MxN` grid.
* Use when #SI_CUSTOM_GRID is set.
*/
- int custom_grid_subdiv;
- char _pad3[4];
+ int custom_grid_subdiv[2];
MaskSpaceInfo mask_info;
SpaceImageOverlay overlay;
@@ -1257,12 +1260,12 @@ typedef enum eSpaceImage_UVDT_Stretch {
SI_UVDT_STRETCH_AREA = 1,
} eSpaceImage_UVDT_Stretch;
-/** #SpaceImage.pixel_snap_mode */
-typedef enum eSpaceImage_PixelSnapMode {
- SI_PIXEL_SNAP_DISABLED = 0,
- SI_PIXEL_SNAP_CENTER = 1,
- SI_PIXEL_SNAP_CORNER = 2,
-} eSpaceImage_Snap_Mode;
+/** #SpaceImage.pixel_round_mode */
+typedef enum eSpaceImage_PixelRoundMode {
+ SI_PIXEL_ROUND_DISABLED = 0,
+ SI_PIXEL_ROUND_CENTER = 1,
+ SI_PIXEL_ROUND_CORNER = 2,
+} eSpaceImage_Round_Mode;
/** #SpaceImage.mode */
typedef enum eSpaceImage_Mode {
@@ -1316,6 +1319,8 @@ typedef enum eSpaceImage_Flag {
SI_SHOW_R = (1 << 27),
SI_SHOW_G = (1 << 28),
SI_SHOW_B = (1 << 29),
+
+ SI_GRID_OVER_IMAGE = (1 << 30),
} eSpaceImage_Flag;
typedef enum eSpaceImageOverlay_Flag {
@@ -1633,7 +1638,7 @@ enum {
typedef struct ConsoleLine {
struct ConsoleLine *next, *prev;
- /* keep these 3 vars so as to share free, realloc funcs */
+ /* Keep these 3 vars so as to share free, realloc functions. */
/** Allocated length. */
int len_alloc;
/** Real len - strlen(). */
diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h
index e32d9dbe300..c7d49db130e 100644
--- a/source/blender/makesdna/DNA_texture_types.h
+++ b/source/blender/makesdna/DNA_texture_types.h
@@ -115,7 +115,7 @@ typedef struct PointDensity {
/** for 'Object' or 'Particle system' type - source object */
struct Object *object;
- /** `index + 1` in ob.particlesystem, non-ID pointer not allowed */
+ /** `index + 1` in ob.particle-system, non-ID pointer not allowed. */
int psys;
/** cache points in world-space, object space, ... ? */
short psys_cache_space;
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 2ceef4f623e..ed4bea97aa0 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -581,7 +581,6 @@ typedef struct bUserAssetLibrary {
typedef struct SolidLight {
int flag;
float smooth;
- char _pad0[8];
float col[4], spec[4], vec[4];
} SolidLight;
@@ -637,10 +636,10 @@ typedef struct UserDef_Experimental {
/* Debug options, always available. */
char use_undo_legacy;
char no_override_auto_resync;
- char use_override_new_fully_editable;
char use_cycles_debug;
char show_asset_debug_info;
char no_asset_indexing;
+ char use_viewport_debug;
char SANITIZE_AFTER_HERE;
/* The following options are automatically sanitized (set to 0)
* when the release cycle is not alpha. */
@@ -653,6 +652,8 @@ typedef struct UserDef_Experimental {
char enable_eevee_next;
char use_sculpt_texture_paint;
char use_draw_manager_acquire_lock;
+ char use_realtime_compositor;
+ char _pad[7];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
@@ -913,8 +914,7 @@ typedef struct UserDef {
/** Pie menu distance from center before a direction is set. */
short pie_menu_threshold;
- short opensubdiv_compute_type;
- short _pad6;
+ short _pad6[2];
char factor_display_type;
@@ -1161,7 +1161,7 @@ typedef enum eUserpref_StatusBar_Flag {
* #UserDef.autokey_mode
*/
typedef enum eAutokey_Mode {
- /* AUTOKEY_ON is a bitflag */
+ /* AUTOKEY_ON is a bit-flag. */
AUTOKEY_ON = 1,
/**
diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h
index c8498f096ed..7e53c1e4d37 100644
--- a/source/blender/makesdna/DNA_view2d_types.h
+++ b/source/blender/makesdna/DNA_view2d_types.h
@@ -19,9 +19,9 @@ extern "C" {
typedef struct View2D {
/** Tot - area that data can be drawn in; cur - region of tot that is visible in viewport. */
rctf tot, cur;
- /** Vert - vertical scrollbar region; hor - horizontal scrollbar region. */
+ /** Vert - vertical scroll-bar region; hor - horizontal scroll-bar region. */
rcti vert, hor;
- /** Mask - region (in screenspace) within which 'cur' can be viewed. */
+ /** Mask - region (in screen-space) within which 'cur' can be viewed. */
rcti mask;
/** Min/max sizes of 'cur' rect (only when keepzoom not set). */
@@ -29,7 +29,7 @@ typedef struct View2D {
/** Allowable zoom factor range (only when (keepzoom & V2D_LIMITZOOM)) is set. */
float minzoom, maxzoom;
- /** Scroll - scrollbars to display (bitflag). */
+ /** Scroll - scroll-bars to display (bit-flag). */
short scroll;
/** Scroll_ui - temp settings used for UI drawing of scrollers. */
short scroll_ui;
@@ -56,7 +56,7 @@ typedef struct View2D {
short around;
/* Usually set externally (as in, not in view2d files). */
- /** Alpha of vertical and horizontal scrollbars (range is [0, 255]). */
+ /** Alpha of vertical and horizontal scroll-bars (range is [0, 255]). */
char alpha_vert, alpha_hor;
char _pad[6];
@@ -124,11 +124,11 @@ enum {
/** Scroller flags for View2D (#View2D.scroll). */
enum {
- /* left scrollbar */
+ /* Left scroll-bar. */
V2D_SCROLL_LEFT = (1 << 0),
V2D_SCROLL_RIGHT = (1 << 1),
V2D_SCROLL_VERTICAL = (V2D_SCROLL_LEFT | V2D_SCROLL_RIGHT),
- /* horizontal scrollbar */
+ /* Horizontal scroll-bar. */
V2D_SCROLL_TOP = (1 << 2),
V2D_SCROLL_BOTTOM = (1 << 3),
/* UNUSED = (1 << 4), */
@@ -137,11 +137,11 @@ enum {
V2D_SCROLL_VERTICAL_HANDLES = (1 << 5),
/* display horizontal scale handles */
V2D_SCROLL_HORIZONTAL_HANDLES = (1 << 6),
- /* induce hiding of scrollbars - set by region drawing in response to size of region */
+ /* Induce hiding of scroll-bar - set by region drawing in response to size of region. */
V2D_SCROLL_VERTICAL_HIDE = (1 << 7),
V2D_SCROLL_HORIZONTAL_HIDE = (1 << 8),
- /* scrollbar extends beyond its available window -
- * set when calculating scrollbars for drawing */
+ /* Scroll-bar extends beyond its available window -
+ * set when calculating scroll-bar for drawing */
V2D_SCROLL_VERTICAL_FULLR = (1 << 9),
V2D_SCROLL_HORIZONTAL_FULLR = (1 << 10),
};
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index 8554d070dc3..2b422f9aebf 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -296,7 +296,9 @@ typedef struct View3D {
char _pad6[2];
int layact DNA_DEPRECATED;
unsigned short local_collections_uuid;
- short _pad7[3];
+ short _pad7[2];
+
+ short debug_flag;
/** Optional bool for 3d cursor to define center. */
short ob_center_cursor;
@@ -389,7 +391,7 @@ enum {
#define RV3D_PAINTING (1 << 5)
/*#define RV3D_IS_GAME_ENGINE (1 << 5) */ /* UNUSED */
/**
- * Disable zbuffer offset, skip calls to #ED_view3d_polygon_offset.
+ * Disable Z-buffer offset, skip calls to #ED_view3d_polygon_offset.
* Use when precise surface depth is needed and picking bias isn't, see T45434).
*/
#define RV3D_ZOFFSET_DISABLED 64
@@ -486,6 +488,12 @@ enum {
V3D_SHADING_SCENE_LIGHTS_RENDER = (1 << 12),
V3D_SHADING_SCENE_WORLD_RENDER = (1 << 13),
V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION = (1 << 14),
+ V3D_SHADING_COMPOSITOR = (1 << 15),
+};
+
+/** #View3D.debug_flag */
+enum {
+ V3D_DEBUG_FREEZE_CULLING = (1 << 0),
};
#define V3D_USES_SCENE_LIGHTS(v3d) \
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index a3d9b5fc7b6..d57cca0b055 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -70,6 +70,8 @@ enum ReportListFlags {
RPT_STORE = (1 << 1),
RPT_FREE = (1 << 2),
RPT_OP_HOLD = (1 << 3), /* don't move them into the operator global list (caller will use) */
+ /** Don't print (the owner of the #ReportList will handle printing to the `stdout`). */
+ RPT_PRINT_HANDLED_BY_OWNER = (1 << 4),
};
/* These two Lines with # tell makesdna this struct can be excluded. */
@@ -149,8 +151,18 @@ typedef struct wmWindowManager {
/** Operator registry. */
ListBase operators;
- /** Refresh/redraw #wmNotifier structs. */
+ /**
+ * Refresh/redraw #wmNotifier structs.
+ * \note Once in the queue, notifiers should be considered read-only.
+ * With the exception of clearing notifiers for data which has been removed,
+ * see: #NOTE_CATEGORY_TAG_CLEARED.
+ */
ListBase notifier_queue;
+ /**
+ * For duplicate detection.
+ * \note keep in sync with `notifier_queue` adding/removing elements must also update this set.
+ */
+ struct GSet *notifier_queue_set;
/** Information and error reports. */
struct ReportList reports;
@@ -295,7 +307,22 @@ typedef struct wmWindow {
*/
short pie_event_type_last;
- /** Storage for event system. */
+ /**
+ * Storage for event system.
+ *
+ * For the most part this is storage for `wmEvent.xy` & `wmEvent.modifiers`.
+ * newly added key/button events copy the cursor location and modifier state stored here.
+ *
+ * It's also convenient at times to be able to pass this as if it's a regular event.
+ *
+ * - This is not simply the current event being handled.
+ * The type and value is always set to the last press/release events
+ * otherwise cursor motion would always clear these values.
+ *
+ * - The value of `eventstate->modifiers` is set from the last pressed/released modifier key.
+ * This has the down side that the modifier value will be incorrect if users hold both
+ * left/right modifiers then release one. See note in #wm_event_add_ghostevent for details.
+ */
struct wmEvent *eventstate;
/** Keep the last handled event in `event_queue` here (owned and must be freed). */
struct wmEvent *event_last_handled;
@@ -407,13 +434,13 @@ enum {
KMI_USER_MODIFIED = (1 << 2),
KMI_UPDATE = (1 << 3),
/**
- * When set, ignore events with #wmEvent.is_repeat enabled.
+ * When set, ignore events with `wmEvent.flag & WM_EVENT_IS_REPEAT` enabled.
*
* \note this flag isn't cleared when editing/loading the key-map items,
* so it may be set in cases which don't make sense (modifier-keys or mouse-motion for example).
*
* Knowing if an event may repeat is something set at the operating-systems event handling level
- * so rely on #wmEvent.is_repeat being false non keyboard events instead of checking if this
+ * so rely on #WM_EVENT_IS_REPEAT being false non keyboard events instead of checking if this
* flag makes sense.
*
* Only used when: `ISKEYBOARD(kmi->type) || (kmi->type == KM_TEXTINPUT)`
diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h
index a72689badb1..1a6e19c31ad 100644
--- a/source/blender/makesdna/DNA_workspace_types.h
+++ b/source/blender/makesdna/DNA_workspace_types.h
@@ -59,7 +59,7 @@ typedef struct bToolRef {
/** Use to avoid initializing the same tool multiple times. */
short tag;
- /** #bToolKey (spacetype, mode), used in 'WM_api.h' */
+ /** #bToolKey (space-type, mode), used in 'WM_api.h' */
short space_type;
/**
* Value depends on the 'space_type', object mode for 3D view, image editor has own mode too.
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index c26696b4572..0d04d7df067 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -3,16 +3,29 @@
# message(STATUS "Configuring makesdna")
-add_definitions(-DWITH_DNA_GHASH)
-
-blender_include_dirs(
- ../../../../intern/atomic
- ../../../../intern/guardedalloc
+set(INC
+ ..
../../blenlib
../../imbuf
- ..
+ ../../../../intern/atomic
+ ../../../../intern/guardedalloc
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+set(INC_SYS
+
)
+set(LIB
+)
+
+add_definitions(-DWITH_DNA_GHASH)
+
+# Needed for `mallocn.c`.
+if(HAVE_MALLOC_STATS_H)
+ add_definitions(-DHAVE_MALLOC_STATS_H)
+endif()
+
set(dna_header_include_file "${CMAKE_CURRENT_BINARY_DIR}/dna_includes_all.h")
set(dna_header_string_file "${CMAKE_CURRENT_BINARY_DIR}/dna_includes_as_strings.h")
@@ -52,6 +65,8 @@ add_cc_flags_custom_test(makesdna)
add_executable(makesdna ${SRC} ${SRC_DNA_INC})
setup_platform_linker_flags(makesdna)
+blender_target_include_dirs(makesdna ${INC})
+blender_target_include_dirs_sys(makesdna ${INC_SYS})
if(WIN32 AND NOT UNIX)
target_link_libraries(makesdna ${PTHREADS_LIBRARIES})
@@ -75,14 +90,6 @@ add_custom_command(
# -----------------------------------------------------------------------------
# Build bf_dna library
-set(INC
- ${CMAKE_CURRENT_BINARY_DIR}
-)
-
-set(INC_SYS
-
-)
-
set(SRC
dna_defaults.c
dna_genfile.c
@@ -96,9 +103,6 @@ set(SRC
dna_utils.h
)
-set(LIB
-)
-
set_source_files_properties(
${CMAKE_CURRENT_BINARY_DIR}/dna.c
${CMAKE_CURRENT_BINARY_DIR}/dna_type_offsets.h
@@ -112,7 +116,10 @@ blender_add_lib(bf_dna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# -----------------------------------------------------------------------------
# Build bf_dna_blenlib library
set(INC
-
+ ../../blenlib
+ ../../makesdna
+ ../../../../intern/atomic
+ ../../../../intern/guardedalloc
)
set(INC_SYS
diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c
index f43cf02c0b8..1b424756b0a 100644
--- a/source/blender/makesdna/intern/dna_genfile.c
+++ b/source/blender/makesdna/intern/dna_genfile.c
@@ -491,15 +491,15 @@ static bool init_structDNA(SDNA *sdna, bool do_endian_swap, const char **r_error
return false;
}
- /* finally pointer_size: use struct ListBase to test it, never change the size of it! */
+ /* finally pointer_size: use struct #ListBase to test it, never change the size of it! */
SDNA_Struct *struct_info = sdna->structs[nr];
- /* weird; i have no memory of that... I think I used sizeof(void *) before... (ton) */
+ /* Weird; I have no memory of that... I think I used `sizeof(void *)` before... (ton). */
sdna->pointer_size = sdna->types_size[struct_info->type] / 2;
if (struct_info->members_len != 2 || (!ELEM(sdna->pointer_size, 4, 8))) {
- *r_error_message = "ListBase struct error! Needs it to calculate pointerize.";
- /* well, at least sizeof(ListBase) is error proof! (ton) */
+ *r_error_message = "ListBase struct error! Needs it to calculate pointer-size.";
+ /* Well, at least `sizeof(ListBase)` is error proof! (ton). */
return false;
}
}
@@ -676,9 +676,8 @@ const char *DNA_struct_get_compareflags(const SDNA *oldsdna, const SDNA *newsdna
BLI_assert(compare_flags[a] != SDNA_CMP_UNKNOWN);
}
- /* first struct in util.h is struct Link, this is skipped in compare_flags (als # 0).
- * was a bug, and this way dirty patched! Solve this later....
- */
+ /* First struct in `util.h` is struct Link, this is skipped in compare_flags (als # 0).
+ * was a bug, and this way dirty patched! Solve this later. */
compare_flags[0] = SDNA_CMP_EQUAL;
/* This code can be enabled to see which structs have changed. */
@@ -852,7 +851,7 @@ static void cast_pointer_64_to_32(const int array_len,
}
/**
- * Equality test on name and oname excluding any array-size suffix.
+ * Equality test on name and `oname` excluding any array-size suffix.
*/
static bool elem_streq(const char *name, const char *oname)
{
@@ -879,7 +878,7 @@ static bool elem_streq(const char *name, const char *oname)
*
* \param type: Current field type name.
* \param name: Current field name.
- * \param old: Pointer to struct information in sdna.
+ * \param old: Pointer to struct information in `sdna`.
* \return true when existing, false otherwise..
*/
static bool elem_exists_impl(
@@ -1042,7 +1041,7 @@ void DNA_struct_switch_endian(const SDNA *sdna, int struct_nr, char *data)
}
case SDNA_TYPE_INT:
case SDNA_TYPE_FLOAT: {
- /* NOTE: intentionally ignore long/ulong, because these could be 4 or 8 bytes.
+ /* NOTE: intentionally ignore `long/ulong`, because these could be 4 or 8 bytes.
* Fortunately, we only use these types for runtime variables and only once for a
* struct type that is no longer used. */
BLI_endian_switch_int32_array((int32_t *)member_data, member_array_length);
@@ -1061,7 +1060,7 @@ void DNA_struct_switch_endian(const SDNA *sdna, int struct_nr, char *data)
break;
}
case STRUCT_MEMBER_CATEGORY_POINTER: {
- /* See readfile.c (#bh4_from_bh8 swap endian argument),
+ /* See `readfile.c` (#bh4_from_bh8 swap endian argument),
* this is only done when reducing the size of a pointer from 4 to 8. */
if (sizeof(void *) < 8) {
if (sdna->pointer_size == 8) {
@@ -1304,7 +1303,7 @@ static void init_reconstruct_step_for_member(const SDNA *oldsdna,
enum eSDNA_StructCompare compare_flag = compare_flags[old_struct_nr];
BLI_assert(compare_flag != SDNA_CMP_REMOVED);
if (compare_flag == SDNA_CMP_EQUAL) {
- /* The old and new members are identical, just do a memcpy. */
+ /* The old and new members are identical, just do a #memcpy. */
r_step->type = RECONSTRUCT_STEP_MEMCPY;
r_step->data.memcpy.new_offset = new_member_offset;
r_step->data.memcpy.old_offset = old_member_offset;
@@ -1333,7 +1332,7 @@ static void init_reconstruct_step_for_member(const SDNA *oldsdna,
}
case STRUCT_MEMBER_CATEGORY_PRIMITIVE: {
if (STREQ(new_type_name, old_type_name)) {
- /* Primitives with the same name cannot be different, so just do a memcpy. */
+ /* Primitives with the same name cannot be different, so just do a #memcpy. */
r_step->type = RECONSTRUCT_STEP_MEMCPY;
r_step->data.memcpy.new_offset = new_member_offset;
r_step->data.memcpy.old_offset = old_member_offset;
@@ -1352,7 +1351,7 @@ static void init_reconstruct_step_for_member(const SDNA *oldsdna,
}
case STRUCT_MEMBER_CATEGORY_POINTER: {
if (newsdna->pointer_size == oldsdna->pointer_size) {
- /* The pointer size is the same, so just do a memcpy. */
+ /* The pointer size is the same, so just do a #memcpy. */
r_step->type = RECONSTRUCT_STEP_MEMCPY;
r_step->data.memcpy.new_offset = new_member_offset;
r_step->data.memcpy.old_offset = old_member_offset;
@@ -1386,7 +1385,7 @@ static void print_reconstruct_step(ReconstructStep *step, const SDNA *oldsdna, c
{
switch (step->type) {
case RECONSTRUCT_STEP_INIT_ZERO: {
- printf("init zero");
+ printf("initialize zero");
break;
}
case RECONSTRUCT_STEP_MEMCPY: {
diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h
index f25ff5fbbb8..facf6199dce 100644
--- a/source/blender/makesdna/intern/dna_rename_defs.h
+++ b/source/blender/makesdna/intern/dna_rename_defs.h
@@ -97,6 +97,9 @@ DNA_STRUCT_RENAME_ELEM(Object, dupfacesca, instance_faces_scale)
DNA_STRUCT_RENAME_ELEM(Object, restrictflag, visibility_flag)
DNA_STRUCT_RENAME_ELEM(Object, size, scale)
DNA_STRUCT_RENAME_ELEM(Object_Runtime, crazyspace_num_verts, crazyspace_verts_num)
+DNA_STRUCT_RENAME_ELEM(MEdge, bweight, bweight_legacy)
+DNA_STRUCT_RENAME_ELEM(MPoly, mat_nr, mat_nr_legacy)
+DNA_STRUCT_RENAME_ELEM(MVert, bweight, bweight_legacy)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, child_nbr, child_percent)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection)
DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object)
@@ -106,6 +109,7 @@ DNA_STRUCT_RENAME_ELEM(RenderData, bake_filter, bake_margin)
DNA_STRUCT_RENAME_ELEM(RigidBodyWorld, steps_per_second, substeps_per_frame)
DNA_STRUCT_RENAME_ELEM(SDefBind, numverts, verts_num)
DNA_STRUCT_RENAME_ELEM(SDefVert, numbinds, binds_num)
+DNA_STRUCT_RENAME_ELEM(SpaceImage, pixel_snap_mode, pixel_round_mode)
DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type)
DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, num_mesh_verts, mesh_verts_num)
DNA_STRUCT_RENAME_ELEM(SurfaceDeformModifierData, numpoly, target_polys_num)
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index 806513009be..7b893078b22 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -393,10 +393,10 @@ static int add_name(const char *str)
if (!isfuncptr) {
/* multidimensional array pointer case */
if (str[j] == 0) {
- DEBUG_PRINTF(3, "offsetting for multidim array pointer\n");
+ DEBUG_PRINTF(3, "offsetting for multi-dimensional array pointer\n");
}
else {
- printf("Error during tokening multidim array pointer\n");
+ printf("Error during tokenizing multi-dimensional array pointer\n");
}
}
else if (str[j] == 0) {
@@ -506,6 +506,54 @@ static short *add_struct(int namecode)
return sp;
}
+/* Copied from `BLI_str_startswith` string.c
+ * to avoid complicating the compilation process of makesdna. */
+static bool str_startswith(const char *__restrict str, const char *__restrict start)
+{
+ for (; *str && *start; str++, start++) {
+ if (*str != *start) {
+ return false;
+ }
+ }
+
+ return (*start == '\0');
+}
+
+/**
+ * Check if `str` is a preprocessor string that starts with `start`.
+ * The `start` doesn't need the `#` prefix.
+ * `ifdef VALUE` will match `#ifdef VALUE` as well as `# ifdef VALUE`.
+ */
+static bool match_preproc_prefix(const char *__restrict str, const char *__restrict start)
+{
+ if (*str != '#') {
+ return false;
+ }
+ str++;
+ while (*str == ' ') {
+ str++;
+ }
+ return str_startswith(str, start);
+}
+
+/**
+ * \return The point in `str` that starts with `start` or NULL when not found.
+ *
+ */
+static char *match_preproc_strstr(char *__restrict str, const char *__restrict start)
+{
+ while ((str = strchr(str, '#'))) {
+ str++;
+ while (*str == ' ') {
+ str++;
+ }
+ if (str_startswith(str, start)) {
+ return str;
+ }
+ }
+ return NULL;
+}
+
static int preprocess_include(char *maindata, const int maindata_len)
{
/* NOTE: len + 1, last character is a dummy to prevent
@@ -533,6 +581,10 @@ static int preprocess_include(char *maindata, const int maindata_len)
cp++;
}
+ /* No need for leading '#' character. */
+ const char *cpp_block_start = "ifdef __cplusplus";
+ const char *cpp_block_end = "endif";
+
/* data from temp copy to maindata, remove comments and double spaces */
cp = temp;
char *md = maindata;
@@ -577,6 +629,18 @@ static int preprocess_include(char *maindata, const int maindata_len)
skip_until_closing_brace = false;
}
}
+ else if (match_preproc_prefix(cp, cpp_block_start)) {
+ char *end_ptr = match_preproc_strstr(cp, cpp_block_end);
+
+ if (end_ptr == NULL) {
+ fprintf(stderr, "Error: '%s' block must end with '%s'\n", cpp_block_start, cpp_block_end);
+ }
+ else {
+ const int skip_offset = end_ptr - cp + strlen(cpp_block_end);
+ a -= skip_offset;
+ cp += skip_offset;
+ }
+ }
else {
md[0] = cp[0];
md++;
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 005228bea72..a7eacb03a63 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -159,10 +159,10 @@ PropertyScaleType RNA_property_ui_scale(PropertyRNA *prop);
int RNA_property_flag(PropertyRNA *prop);
int RNA_property_override_flag(PropertyRNA *prop);
/**
- * Get the tags set for \a prop as int bitfield.
+ * Get the tags set for \a prop as int bit-field.
* \note Doesn't perform any validity check on the set bits. #RNA_def_property_tags does this
* in debug builds (to avoid performance issues in non-debug builds), which should be
- * the only way to set tags. Hence, at this point we assume the tag bitfield to be valid.
+ * the only way to set tags. Hence, at this point we assume the tag bit-field to be valid.
*/
int RNA_property_tags(PropertyRNA *prop);
bool RNA_property_builtin(PropertyRNA *prop);
@@ -430,6 +430,10 @@ int RNA_property_collection_lookup_string(PointerRNA *ptr,
PointerRNA *r_ptr);
int RNA_property_collection_lookup_string_index(
PointerRNA *ptr, PropertyRNA *prop, const char *key, PointerRNA *r_ptr, int *r_index);
+
+bool RNA_property_collection_lookup_int_has_fn(PropertyRNA *prop);
+bool RNA_property_collection_lookup_string_has_fn(PropertyRNA *prop);
+
/**
* Zero return is an assignment error.
*/
@@ -475,247 +479,6 @@ bool RNA_property_copy(
bool RNA_property_reset(PointerRNA *ptr, PropertyRNA *prop, int index);
bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop);
-/* Path
- *
- * Experimental method to refer to structs and properties with a string,
- * using a syntax like: scenes[0].objects["Cube"].data.verts[7].co
- *
- * This provides a way to refer to RNA data while being detached from any
- * particular pointers, which is useful in a number of applications, like
- * UI code or Actions, though efficiency is a concern. */
-
-char *RNA_path_append(
- const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey);
-#if 0 /* UNUSED. */
-char *RNA_path_back(const char *path);
-#endif
-
-/**
- * Search for the start of the 'rna array index' part of the given `rna_path`.
- *
- * Given the root RNA pointer and resolved RNA property, and the RNA path, return the first
- * character in `rna_path` that is part of the array index for the given property. Return NULL if
- * none can be found, e.g. because the property is not an RNA array.
- *
- * \param array_prop: if not NULL, the #PropertyRNA assumed to be the last one from the RNA path.
- * Only used to ensure it is a valid array property.
- */
-const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop);
-
-/* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */
-
-/**
- * Resolve the given RNA Path to find the pointer and/or property
- * indicated by fully resolving the path.
- *
- * \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers,
- * e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr,
- * and a NULL \a r_prop...
- *
- * \note Assumes all pointers provided are valid
- * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
- */
-bool RNA_path_resolve(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop);
-
-/**
- * Resolve the given RNA Path to find the pointer and/or property + array index
- * indicated by fully resolving the path.
- *
- * \note Assumes all pointers provided are valid.
- * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
- */
-bool RNA_path_resolve_full(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index);
-/**
- * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data.
- *
- * \note While it's correct to ignore the value of #PointerRNA.data
- * most callers need to know if the resulting pointer was found and not null.
- */
-bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index);
-
-/* RNA_path_resolve_property() variants ensure that pointer + property both exist. */
-
-/**
- * Resolve the given RNA Path to find both the pointer AND property
- * indicated by fully resolving the path.
- *
- * This is a convenience method to avoid logic errors and ugly syntax.
- * \note Assumes all pointers provided are valid
- * \return True only if both a valid pointer and property are found after resolving the path
- */
-bool RNA_path_resolve_property(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop);
-
-/**
- * Resolve the given RNA Path to find the pointer AND property (as well as the array index)
- * indicated by fully resolving the path.
- *
- * This is a convenience method to avoid logic errors and ugly syntax.
- * \note Assumes all pointers provided are valid
- * \return True only if both a valid pointer and property are found after resolving the path
- */
-bool RNA_path_resolve_property_full(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index);
-
-/* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist,
- * and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */
-
-/**
- * Resolve the given RNA Path to find both the pointer AND property
- * indicated by fully resolving the path, and get the value of the Pointer property
- * (or item of the collection).
- *
- * This is a convenience method to avoid logic errors and ugly syntax,
- * it combines both \a RNA_path_resolve and #RNA_path_resolve_property in a single call.
- * \note Assumes all pointers provided are valid.
- * \param r_item_ptr: The final Pointer or Collection item value.
- * You must check for its validity before use!
- * \return True only if both a valid pointer and property are found after resolving the path
- */
-bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- PointerRNA *r_item_ptr);
-
-/**
- * Resolve the given RNA Path to find both the pointer AND property (as well as the array index)
- * indicated by fully resolving the path,
- * and get the value of the Pointer property (or item of the collection).
- *
- * This is a convenience method to avoid logic errors and ugly syntax,
- * it combines both \a RNA_path_resolve_full and
- * \a RNA_path_resolve_property_full in a single call.
- * \note Assumes all pointers provided are valid.
- * \param r_item_ptr: The final Pointer or Collection item value.
- * You must check for its validity before use!
- * \return True only if both a valid pointer and property are found after resolving the path
- */
-bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index,
- PointerRNA *r_item_ptr);
-
-typedef struct PropertyElemRNA PropertyElemRNA;
-struct PropertyElemRNA {
- PropertyElemRNA *next, *prev;
- PointerRNA ptr;
- PropertyRNA *prop;
- int index;
-};
-/**
- * Resolve the given RNA Path into a linked list of #PropertyElemRNA's.
- *
- * To be used when complex operations over path are needed, like e.g. get relative paths,
- * to avoid too much string operations.
- *
- * \return True if there was no error while resolving the path
- * \note Assumes all pointers provided are valid
- */
-bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBase *r_elements);
-
-/**
- * Find the path from the structure referenced by the pointer to the runtime RNA-defined
- * #IDProperty object.
- *
- * \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties).
- *
- * \param ptr: Reference to the object owning the custom property storage.
- * \param needle: Custom property object to find.
- * \return Relative path or NULL.
- */
-char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle);
-
-/**
- * Find the actual ID pointer and path from it to the given ID.
- *
- * \param id: ID reference to search the global owner for.
- * \param[out] r_path: Path from the real ID to the initial ID.
- * \return The ID pointer, or NULL in case of failure.
- */
-struct ID *RNA_find_real_ID_and_path(struct Main *bmain, struct ID *id, const char **r_path);
-
-char *RNA_path_from_ID_to_struct(const PointerRNA *ptr);
-
-char *RNA_path_from_real_ID_to_struct(struct Main *bmain,
- const PointerRNA *ptr,
- struct ID **r_real);
-
-char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop);
-/**
- * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc.
- * \param index: The *flattened* index to use when \a `index_dim > 0`,
- * this is expanded when used with multi-dimensional arrays.
- */
-char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index);
-
-char *RNA_path_from_real_ID_to_property_index(struct Main *bmain,
- const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index,
- struct ID **r_real_id);
-
-/**
- * \return the path to given ptr/prop from the closest ancestor of given type,
- * if any (else return NULL).
- */
-char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr,
- PropertyRNA *prop,
- const struct StructRNA *type);
-
-/**
- * Get the ID as a python representation, eg:
- * bpy.data.foo["bar"]
- */
-char *RNA_path_full_ID_py(struct Main *bmain, struct ID *id);
-/**
- * Get the ID.struct as a python representation, eg:
- * bpy.data.foo["bar"].some_struct
- */
-char *RNA_path_full_struct_py(struct Main *bmain, const PointerRNA *ptr);
-/**
- * Get the ID.struct.property as a python representation, eg:
- * bpy.data.foo["bar"].some_struct.some_prop[10]
- */
-char *RNA_path_full_property_py_ex(
- struct Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback);
-char *RNA_path_full_property_py(struct Main *bmain,
- const PointerRNA *ptr,
- PropertyRNA *prop,
- int index);
-/**
- * Get the struct.property as a python representation, eg:
- * some_struct.some_prop[10]
- */
-char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index);
-/**
- * Get the struct.property as a python representation, eg:
- * some_prop[10]
- */
-char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index);
-
/* Quick name based property access
*
* These are just an easier way to access property values without having to
diff --git a/source/blender/makesrna/RNA_path.h b/source/blender/makesrna/RNA_path.h
new file mode 100644
index 00000000000..5e29905cc4f
--- /dev/null
+++ b/source/blender/makesrna/RNA_path.h
@@ -0,0 +1,258 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/** \file
+ * \ingroup RNA
+ *
+ * RNA paths are a way to refer to pointers and properties with a string,
+ * using a syntax like: scenes[0].objects["Cube"].data.verts[7].co
+ *
+ * This provides a way to refer to RNA data while being detached from any
+ * particular pointers, which is useful in a number of applications, like
+ * UI code or Actions, though efficiency is a concern.
+ */
+
+#include "RNA_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ListBase;
+struct IDProperty;
+
+char *RNA_path_append(
+ const char *path, const PointerRNA *ptr, PropertyRNA *prop, int intkey, const char *strkey);
+#if 0 /* UNUSED. */
+char *RNA_path_back(const char *path);
+#endif
+
+/**
+ * Search for the start of the 'rna array index' part of the given `rna_path`.
+ *
+ * Given the root RNA pointer and resolved RNA property, and the RNA path, return the first
+ * character in `rna_path` that is part of the array index for the given property. Return NULL if
+ * none can be found, e.g. because the property is not an RNA array.
+ *
+ * \param array_prop: if not NULL, the #PropertyRNA assumed to be the last one from the RNA path.
+ * Only used to ensure it is a valid array property.
+ */
+const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop);
+
+/* RNA_path_resolve() variants only ensure that a valid pointer (and optionally property) exist. */
+
+/**
+ * Resolve the given RNA Path to find the pointer and/or property
+ * indicated by fully resolving the path.
+ *
+ * \warning Unlike \a RNA_path_resolve_property(), that one *will* try to follow RNAPointers,
+ * e.g. the path 'parent' applied to a RNAObject \a ptr will return the object.parent in \a r_ptr,
+ * and a NULL \a r_prop...
+ *
+ * \note Assumes all pointers provided are valid
+ * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
+ */
+bool RNA_path_resolve(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop);
+
+/**
+ * Resolve the given RNA Path to find the pointer and/or property + array index
+ * indicated by fully resolving the path.
+ *
+ * \note Assumes all pointers provided are valid.
+ * \return True if path can be resolved to a valid "pointer + property" OR "pointer only"
+ */
+bool RNA_path_resolve_full(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index);
+/**
+ * A version of #RNA_path_resolve_full doesn't check the value of #PointerRNA.data.
+ *
+ * \note While it's correct to ignore the value of #PointerRNA.data
+ * most callers need to know if the resulting pointer was found and not null.
+ */
+bool RNA_path_resolve_full_maybe_null(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index);
+
+/* RNA_path_resolve_property() variants ensure that pointer + property both exist. */
+
+/**
+ * Resolve the given RNA Path to find both the pointer AND property
+ * indicated by fully resolving the path.
+ *
+ * This is a convenience method to avoid logic errors and ugly syntax.
+ * \note Assumes all pointers provided are valid
+ * \return True only if both a valid pointer and property are found after resolving the path
+ */
+bool RNA_path_resolve_property(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop);
+
+/**
+ * Resolve the given RNA Path to find the pointer AND property (as well as the array index)
+ * indicated by fully resolving the path.
+ *
+ * This is a convenience method to avoid logic errors and ugly syntax.
+ * \note Assumes all pointers provided are valid
+ * \return True only if both a valid pointer and property are found after resolving the path
+ */
+bool RNA_path_resolve_property_full(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index);
+
+/* RNA_path_resolve_property_and_item_pointer() variants ensure that pointer + property both exist,
+ * and resolve last Pointer value if possible (Pointer prop or item of a Collection prop). */
+
+/**
+ * Resolve the given RNA Path to find both the pointer AND property
+ * indicated by fully resolving the path, and get the value of the Pointer property
+ * (or item of the collection).
+ *
+ * This is a convenience method to avoid logic errors and ugly syntax,
+ * it combines both \a RNA_path_resolve and #RNA_path_resolve_property in a single call.
+ * \note Assumes all pointers provided are valid.
+ * \param r_item_ptr: The final Pointer or Collection item value.
+ * You must check for its validity before use!
+ * \return True only if both a valid pointer and property are found after resolving the path
+ */
+bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ PointerRNA *r_item_ptr);
+
+/**
+ * Resolve the given RNA Path to find both the pointer AND property (as well as the array index)
+ * indicated by fully resolving the path,
+ * and get the value of the Pointer property (or item of the collection).
+ *
+ * This is a convenience method to avoid logic errors and ugly syntax,
+ * it combines both \a RNA_path_resolve_full and
+ * \a RNA_path_resolve_property_full in a single call.
+ * \note Assumes all pointers provided are valid.
+ * \param r_item_ptr: The final Pointer or Collection item value.
+ * You must check for its validity before use!
+ * \return True only if both a valid pointer and property are found after resolving the path
+ */
+bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index,
+ PointerRNA *r_item_ptr);
+
+typedef struct PropertyElemRNA PropertyElemRNA;
+struct PropertyElemRNA {
+ PropertyElemRNA *next, *prev;
+ PointerRNA ptr;
+ PropertyRNA *prop;
+ int index;
+};
+/**
+ * Resolve the given RNA Path into a linked list of #PropertyElemRNA's.
+ *
+ * To be used when complex operations over path are needed, like e.g. get relative paths,
+ * to avoid too much string operations.
+ *
+ * \return True if there was no error while resolving the path
+ * \note Assumes all pointers provided are valid
+ */
+bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, struct ListBase *r_elements);
+
+/**
+ * Find the path from the structure referenced by the pointer to the runtime RNA-defined
+ * #IDProperty object.
+ *
+ * \note Does *not* handle pure user-defined IDProperties (a.k.a. custom properties).
+ *
+ * \param ptr: Reference to the object owning the custom property storage.
+ * \param needle: Custom property object to find.
+ * \return Relative path or NULL.
+ */
+char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, struct IDProperty *needle);
+
+/**
+ * Find the actual ID pointer and path from it to the given ID.
+ *
+ * \param id: ID reference to search the global owner for.
+ * \param[out] r_path: Path from the real ID to the initial ID.
+ * \return The ID pointer, or NULL in case of failure.
+ */
+struct ID *RNA_find_real_ID_and_path(struct ID *id, const char **r_path);
+
+char *RNA_path_from_ID_to_struct(const PointerRNA *ptr);
+
+char *RNA_path_from_real_ID_to_struct(struct Main *bmain,
+ const PointerRNA *ptr,
+ struct ID **r_real);
+
+char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop);
+/**
+ * \param index_dim: The dimension to show, 0 disables. 1 for 1d array, 2 for 2d. etc.
+ * \param index: The *flattened* index to use when \a `index_dim > 0`,
+ * this is expanded when used with multi-dimensional arrays.
+ */
+char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index);
+
+char *RNA_path_from_real_ID_to_property_index(struct Main *bmain,
+ const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index,
+ struct ID **r_real_id);
+
+/**
+ * \return the path to given ptr/prop from the closest ancestor of given type,
+ * if any (else return NULL).
+ */
+char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ const struct StructRNA *type);
+
+/**
+ * Get the ID as a python representation, eg:
+ * bpy.data.foo["bar"]
+ */
+char *RNA_path_full_ID_py(struct ID *id);
+/**
+ * Get the ID.struct as a python representation, eg:
+ * bpy.data.foo["bar"].some_struct
+ */
+char *RNA_path_full_struct_py(const PointerRNA *ptr);
+/**
+ * Get the ID.struct.property as a python representation, eg:
+ * bpy.data.foo["bar"].some_struct.some_prop[10]
+ */
+char *RNA_path_full_property_py_ex(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index,
+ bool use_fallback);
+char *RNA_path_full_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index);
+/**
+ * Get the struct.property as a python representation, eg:
+ * some_struct.some_prop[10]
+ */
+char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index);
+/**
+ * Get the struct.property as a python representation, eg:
+ * some_prop[10]
+ */
+char *RNA_path_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index 5346228050a..b9556a411cf 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -120,7 +120,7 @@ typedef enum PropertyScaleType {
/**
* \note Also update enums in bpy_props.c and rna_rna.c when adding items here.
- * Watch it: these values are written to files as part of node socket button subtypes!
+ * Watch it: these values are written to files as part of node socket button sub-types!
*/
typedef enum PropertySubType {
PROP_NONE = 0,
@@ -179,7 +179,7 @@ typedef enum PropertySubType {
/* Make sure enums are updated with these */
/* HIGHEST FLAG IN USE: 1 << 31
- * FREE FLAGS: 2, 9, 11, 13, 14, 15. */
+ * FREE FLAGS: 9, 11, 13, 14, 15. */
typedef enum PropertyFlag {
/**
* Editable means the property is editable in the user
@@ -299,6 +299,12 @@ typedef enum PropertyFlag {
* properties which denotes whether modifier panel is collapsed or not.
*/
PROP_NO_DEG_UPDATE = (1 << 30),
+
+ /**
+ * Filepaths that refer to output get a special treatment such
+ * as having the +/- operators available in the file browser.
+ **/
+ PROP_PATH_OUTPUT = (1 << 2),
} PropertyFlag;
/**
@@ -355,7 +361,7 @@ typedef enum ParameterFlag {
/**
* This allows for non-breaking API updates,
* when adding non-critical new parameter to a callback function.
- * This way, old py code defining funcs without that parameter would still work.
+ * This way, old Python code defining functions without that parameter would still work.
* WARNING: any parameter after the first PYFUNC_OPTIONAL one will be considered as optional!
* \note only for input parameters!
*/
@@ -719,7 +725,7 @@ typedef enum StructFlag {
STRUCT_CONTAINS_DATABLOCK_IDPROPERTIES = (1 << 8),
/** Added to type-map #BlenderRNA.structs_map */
STRUCT_PUBLIC_NAMESPACE = (1 << 9),
- /** All subtypes are added too. */
+ /** All sub-types are added too. */
STRUCT_PUBLIC_NAMESPACE_INHERIT = (1 << 10),
/**
* When the #PointerRNA.owner_id is NULL, this signifies the property should be accessed
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index af8767a1220..2b06daf34e3 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -6,6 +6,11 @@ if(CMAKE_COMPILER_IS_GNUCC)
string(APPEND CMAKE_C_FLAGS " -Werror=implicit-function-declaration")
endif()
+# Needed for `mallocn.c`.
+if(HAVE_MALLOC_STATS_H)
+ add_definitions(-DHAVE_MALLOC_STATS_H)
+endif()
+
# files rna_access.c rna_define.c makesrna.c intentionally excluded.
set(DEFSRC
rna_ID.c
@@ -26,6 +31,7 @@ set(DEFSRC
rna_context.c
rna_curve.c
rna_curveprofile.c
+ rna_curves.c
rna_depsgraph.c
rna_dynamicpaint.c
rna_fcurve.c
@@ -84,9 +90,7 @@ set(DEFSRC
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
- add_definitions(-DWITH_NEW_CURVES_TYPE)
list(APPEND DEFSRC
- rna_curves.c
rna_simulation.c
)
endif()
@@ -160,6 +164,7 @@ set(SRC_RNA_INC
../RNA_documentation.h
../RNA_enum_items.h
../RNA_enum_types.h
+ ../RNA_path.h
../RNA_types.h
)
@@ -180,10 +185,33 @@ set(SRC
)
set(INC
- ../../../../intern/clog
-
- # Needed for defaults forward declarations.
+ .
+ ..
+ ../../blenfont
+ ../../blenkernel
+ ../../blenlib
../../blenloader
+ ../../blentranslation
+ ../../bmesh
+ ../../depsgraph
+ ../../draw
+ ../../gpu
+ ../../ikplugin
+ ../../imbuf
+ ../../makesdna
+ ../../modifiers
+ ../../nodes/
+ ../../sequencer
+ ../../simulation
+ ../../windowmanager
+ ../../editors/include
+ ../../render
+ ../../../../intern/clog
+ ../../../../intern/cycles/blender
+ ../../../../intern/atomic
+ ../../../../intern/guardedalloc
+ ../../../../intern/memutil
+ ../../../../intern/mantaflow/extern
${CMAKE_BINARY_DIR}/source/blender/makesdna/intern
@@ -364,39 +392,12 @@ if(WITH_GMP)
endif()
# Build makesrna executable
-blender_include_dirs(
- .
- ..
- ../../blenfont
- ../../blenkernel
- ../../blenlib
- ../../blentranslation
- ../../bmesh
- ../../depsgraph
- ../../draw
- ../../gpu
- ../../ikplugin
- ../../imbuf
- ../../makesdna
- ../../modifiers
- ../../nodes/
- ../../sequencer
- ../../simulation
- ../../windowmanager
- ../../editors/include
- ../../render
- ../../../../intern/cycles/blender
- ../../../../intern/atomic
- ../../../../intern/glew-mx
- ../../../../intern/guardedalloc
- ../../../../intern/memutil
- ../../../../intern/mantaflow/extern
-)
-
add_cc_flags_custom_test(makesrna)
add_executable(makesrna ${SRC} ${SRC_RNA_INC} ${SRC_DNA_INC})
setup_platform_linker_flags(makesrna)
+blender_target_include_dirs(makesrna ${INC})
+blender_target_include_dirs_sys(makesrna ${INC_SYS})
target_link_libraries(makesrna bf_dna)
target_link_libraries(makesrna bf_dna_blenlib)
@@ -417,6 +418,7 @@ add_custom_command(
set(SRC
rna_access.c
rna_access_compare_override.c
+ rna_path.cc
${GENSRC}
${SRC_RNA_INC}
@@ -449,8 +451,6 @@ set(LIB
bf_editor_undo
)
-add_definitions(${GL_DEFINITIONS})
-
blender_add_lib(bf_rna "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# Needed so we can use dna_type_offsets.h for defaults initialization.
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index b5354514205..a7b8488c371 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -826,7 +826,23 @@ static char *rna_def_property_get_func(
fprintf(f, "{\n");
if (manualfunc) {
- fprintf(f, " %s(ptr, values);\n", manualfunc);
+ /* Assign `fn` to ensure function signatures match. */
+ if (prop->type == PROP_BOOLEAN) {
+ fprintf(f, " PropBooleanArrayGetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, values);\n");
+ }
+ else if (prop->type == PROP_INT) {
+ fprintf(f, " PropIntArrayGetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, values);\n");
+ }
+ else if (prop->type == PROP_FLOAT) {
+ fprintf(f, " PropFloatArrayGetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, values);\n");
+ }
+ else {
+ BLI_assert_unreachable(); /* Valid but should be handled by type checks. */
+ fprintf(f, " %s(ptr, values);\n", manualfunc);
+ }
}
else {
rna_print_data_get(f, dp);
@@ -902,7 +918,27 @@ static char *rna_def_property_get_func(
fprintf(f, "{\n");
if (manualfunc) {
- fprintf(f, " return %s(ptr);\n", manualfunc);
+ /* Assign `fn` to ensure function signatures match. */
+ if (prop->type == PROP_BOOLEAN) {
+ fprintf(f, " PropBooleanGetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " return fn(ptr);\n");
+ }
+ else if (prop->type == PROP_INT) {
+ fprintf(f, " PropIntGetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " return fn(ptr);\n");
+ }
+ else if (prop->type == PROP_FLOAT) {
+ fprintf(f, " PropFloatGetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " return fn(ptr);\n");
+ }
+ else if (prop->type == PROP_ENUM) {
+ fprintf(f, " PropEnumGetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " return fn(ptr);\n");
+ }
+ else {
+ BLI_assert_unreachable(); /* Valid but should be handled by type checks. */
+ fprintf(f, " return %s(ptr);\n", manualfunc);
+ }
}
else {
rna_print_data_get(f, dp);
@@ -1117,8 +1153,10 @@ static char *rna_def_property_set_func(
fprintf(
f, " if (data->%s != NULL) { MEM_freeN(data->%s); }\n", dp->dnaname, dp->dnaname);
fprintf(f, " const int length = strlen(value);\n");
- fprintf(f, " data->%s = MEM_mallocN(length + 1, __func__);\n", dp->dnaname);
- fprintf(f, " %s(data->%s, value, length + 1);\n", string_copy_func, dp->dnaname);
+ fprintf(f, " if (length > 0) {\n");
+ fprintf(f, " data->%s = MEM_mallocN(length + 1, __func__);\n", dp->dnaname);
+ fprintf(f, " %s(data->%s, value, length + 1);\n", string_copy_func, dp->dnaname);
+ fprintf(f, " } else { data->%s = NULL; }\n", dp->dnaname);
}
else {
/* Handle char array properties. */
@@ -1195,7 +1233,23 @@ static char *rna_def_property_set_func(
fprintf(f, "{\n");
if (manualfunc) {
- fprintf(f, " %s(ptr, values);\n", manualfunc);
+ /* Assign `fn` to ensure function signatures match. */
+ if (prop->type == PROP_BOOLEAN) {
+ fprintf(f, " PropBooleanArraySetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, values);\n");
+ }
+ else if (prop->type == PROP_INT) {
+ fprintf(f, " PropIntArraySetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, values);\n");
+ }
+ else if (prop->type == PROP_FLOAT) {
+ fprintf(f, " PropFloatArraySetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, values);\n");
+ }
+ else {
+ BLI_assert_unreachable(); /* Valid but should be handled by type checks. */
+ fprintf(f, " %s(ptr, values);\n", manualfunc);
+ }
}
else {
rna_print_data_get(f, dp);
@@ -1287,7 +1341,27 @@ static char *rna_def_property_set_func(
fprintf(f, "{\n");
if (manualfunc) {
- fprintf(f, " %s(ptr, value);\n", manualfunc);
+ /* Assign `fn` to ensure function signatures match. */
+ if (prop->type == PROP_BOOLEAN) {
+ fprintf(f, " PropBooleanSetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, value);\n");
+ }
+ else if (prop->type == PROP_INT) {
+ fprintf(f, " PropIntSetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, value);\n");
+ }
+ else if (prop->type == PROP_FLOAT) {
+ fprintf(f, " PropFloatSetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, value);\n");
+ }
+ else if (prop->type == PROP_ENUM) {
+ fprintf(f, " PropEnumSetFunc fn = %s;\n", manualfunc);
+ fprintf(f, " fn(ptr, value);\n");
+ }
+ else {
+ BLI_assert_unreachable(); /* Valid but should be handled by type checks. */
+ fprintf(f, " %s(ptr, value);\n", manualfunc);
+ }
}
else {
rna_print_data_get(f, dp);
@@ -4433,9 +4507,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint},
{"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve},
{"rna_gpencil.c", NULL, RNA_def_gpencil},
-#ifdef WITH_NEW_CURVES_TYPE
{"rna_curves.c", NULL, RNA_def_curves},
-#endif
{"rna_image.c", "rna_image_api.c", RNA_def_image},
{"rna_key.c", NULL, RNA_def_key},
{"rna_light.c", NULL, RNA_def_light},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 8d2ee0ab534..d31a312816a 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -16,6 +16,7 @@
#include "BKE_icons.h"
#include "BKE_lib_id.h"
+#include "BKE_main_namemap.h"
#include "BKE_object.h"
#include "RNA_access.h"
@@ -233,6 +234,10 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = {
# include "WM_api.h"
+# ifdef WITH_PYTHON
+# include "BPY_extern.h"
+# endif
+
void rna_ID_override_library_property_operation_refname_get(PointerRNA *ptr, char *value)
{
IDOverrideLibraryPropertyOperation *opop = ptr->data;
@@ -273,6 +278,7 @@ int rna_ID_name_length(PointerRNA *ptr)
void rna_ID_name_set(PointerRNA *ptr, const char *value)
{
ID *id = (ID *)ptr->data;
+ BKE_main_namemap_remove_name(G_MAIN, id, id->name + 2);
BLI_strncpy_utf8(id->name + 2, value, sizeof(id->name) - 2);
BLI_assert(BKE_id_is_in_global_main(id));
BLI_libblock_ensure_unique_name(G_MAIN, id->name);
@@ -316,7 +322,7 @@ int rna_ID_name_full_length(PointerRNA *ptr)
return strlen(name);
}
-static int rna_ID_is_evaluated_get(PointerRNA *ptr)
+static bool rna_ID_is_evaluated_get(PointerRNA *ptr)
{
ID *id = (ID *)ptr->data;
@@ -375,11 +381,9 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_FreestyleLineStyle) {
return ID_LS;
}
-# ifdef WITH_NEW_CURVES_TYPE
if (base_type == &RNA_Curves) {
return ID_CV;
}
-# endif
if (base_type == &RNA_Lattice) {
return ID_LT;
}
@@ -483,11 +487,7 @@ StructRNA *ID_code_to_RNA_type(short idcode)
case ID_GR:
return &RNA_Collection;
case ID_CV:
-# ifdef WITH_NEW_CURVES_TYPE
return &RNA_Curves;
-# else
- return &RNA_ID;
-# endif
case ID_IM:
return &RNA_Image;
case ID_KE:
@@ -654,7 +654,7 @@ static ID *rna_ID_evaluated_get(ID *id, struct Depsgraph *depsgraph)
static ID *rna_ID_copy(ID *id, Main *bmain)
{
- ID *newid = BKE_id_copy(bmain, id);
+ ID *newid = BKE_id_copy_for_use_in_bmain(bmain, id);
if (newid != NULL) {
id_us_min(newid);
@@ -699,7 +699,16 @@ static ID *rna_ID_override_create(ID *id, Main *bmain, bool remap_local_usages)
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, true);
}
- ID *local_id = BKE_lib_override_library_create_from_id(bmain, id, remap_local_usages);
+ ID *local_id = NULL;
+# ifdef WITH_PYTHON
+ BPy_BEGIN_ALLOW_THREADS;
+# endif
+
+ local_id = BKE_lib_override_library_create_from_id(bmain, id, remap_local_usages);
+
+# ifdef WITH_PYTHON
+ BPy_END_ALLOW_THREADS;
+# endif
if (remap_local_usages) {
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
@@ -721,9 +730,18 @@ static ID *rna_ID_override_hierarchy_create(
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
ID *id_root_override = NULL;
+
+# ifdef WITH_PYTHON
+ BPy_BEGIN_ALLOW_THREADS;
+# endif
+
BKE_lib_override_library_create(
bmain, scene, view_layer, NULL, id, id, id_instance_hint, &id_root_override, false);
+# ifdef WITH_PYTHON
+ BPy_END_ALLOW_THREADS;
+# endif
+
WM_main_add_notifier(NC_ID | NA_ADDED, NULL);
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
@@ -2049,7 +2067,10 @@ static void rna_def_ID(BlenderRNA *brna)
func = RNA_def_function(srna, "copy", "rna_ID_copy");
RNA_def_function_ui_description(
- func, "Create a copy of this data-block (not supported for all data-blocks)");
+ func,
+ "Create a copy of this data-block (not supported for all data-blocks). "
+ "The result is added to the Blend-File Data (Main database), with all references to other "
+ "data-blocks ensured to be from within the same Blend-File Data");
RNA_def_function_flag(func, FUNC_USE_MAIN);
parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID");
RNA_def_function_return(func, parm);
@@ -2159,7 +2180,7 @@ static void rna_def_ID(BlenderRNA *brna)
func = RNA_def_function(srna, "animation_data_clear", "rna_ID_animation_data_free");
RNA_def_function_flag(func, FUNC_USE_MAIN);
- RNA_def_function_ui_description(func, "Clear animation on this this ID");
+ RNA_def_function_ui_description(func, "Clear animation on this ID");
func = RNA_def_function(srna, "update_tag", "rna_ID_update_tag");
RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index cf4182243b9..e5932f33604 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -46,6 +46,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "WM_api.h"
#include "WM_message.h"
@@ -738,7 +739,7 @@ PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
}
/* Find the property which uses the given nested struct */
-static PropertyRNA *RNA_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
+PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
{
PropertyRNA *prop = NULL;
@@ -1134,7 +1135,7 @@ char RNA_property_array_item_char(PropertyRNA *prop, int index)
int RNA_property_array_item_index(PropertyRNA *prop, char name)
{
- /* Don't use custom property subtypes in RNA path lookup. */
+ /* Don't use custom property sub-types in RNA path lookup. */
PropertySubType subtype = rna_ensure_property(prop)->subtype;
/* get index based on string name/alias */
@@ -1422,7 +1423,7 @@ StructRNA *RNA_property_pointer_type(PointerRNA *ptr, PropertyRNA *prop)
return cprop->item_type;
}
}
- /* ignore other types, RNA_struct_find_nested calls with unchecked props */
+ /* ignore other types, rna_struct_find_nested calls with unchecked props */
return &RNA_UnknownType;
}
@@ -2072,7 +2073,7 @@ static void rna_property_update(
}
#if 1
- /* TODO(campbell): Should eventually be replaced entirely by message bus (below)
+ /* TODO(@campbellbarton): Should eventually be replaced entirely by message bus (below)
* for now keep since COW, bugs are hard to track when we have other missing updates. */
if (prop->noteflag) {
WM_main_add_notifier(prop->noteflag, ptr->owner_id);
@@ -4079,6 +4080,20 @@ int RNA_property_collection_lookup_index(PointerRNA *ptr,
return -1;
}
+bool RNA_property_collection_lookup_int_has_fn(PropertyRNA *prop)
+{
+ BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
+ CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(prop);
+ return cprop->lookupint != NULL;
+}
+
+bool RNA_property_collection_lookup_string_has_fn(PropertyRNA *prop)
+{
+ BLI_assert(RNA_property_type(prop) == PROP_COLLECTION);
+ CollectionPropertyRNA *cprop = (CollectionPropertyRNA *)rna_ensure_property(prop);
+ return cprop->lookupstring != NULL;
+}
+
int RNA_property_collection_lookup_int(PointerRNA *ptr,
PropertyRNA *prop,
int key,
@@ -4821,1337 +4836,6 @@ PointerRNA rna_array_lookup_int(
return rna_pointer_inherit_refine(ptr, type, ((char *)data) + index * itemsize);
}
-/* RNA Path - Experiment */
-
-/**
- * Extract the first token from `path`.
- *
- * \param path: Extract the token from path, step the pointer to the beginning of the next token
- * \return The nil terminated token.
- */
-static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen)
-{
- int len = 0;
-
- /* Get data until `.` or `[`. */
- const char *p = *path;
- while (*p && !ELEM(*p, '.', '[')) {
- len++;
- p++;
- }
-
- /* Empty, return. */
- if (UNLIKELY(len == 0)) {
- return NULL;
- }
-
- /* Try to use fixed buffer if possible. */
- char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__);
- memcpy(buf, *path, sizeof(char) * len);
- buf[len] = '\0';
-
- if (*p == '.') {
- p++;
- }
- *path = p;
-
- return buf;
-}
-
-/**
- * Extract the first token in brackets from `path` (with quoted text support).
- *
- * - `[0]` -> `0`
- * - `["Some\"Quote"]` -> `Some"Quote`
- *
- * \param path: Extract the token from path, step the pointer to the beginning of the next token
- * (past quoted text and brackets).
- * \return The nil terminated token.
- */
-static char *rna_path_token_in_brackets(const char **path,
- char *fixedbuf,
- int fixedlen,
- bool *r_quoted)
-{
- int len = 0;
- bool quoted = false;
-
- BLI_assert(r_quoted != NULL);
-
- /* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */
- if (UNLIKELY(**path != '[')) {
- return NULL;
- }
-
- (*path)++;
- const char *p = *path;
-
- /* 2 kinds of look-ups now, quoted or unquoted. */
- if (*p == '"') {
- /* Find the matching quote. */
- (*path)++;
- p = *path;
- const char *p_end = BLI_str_escape_find_quote(p);
- if (p_end == NULL) {
- /* No Matching quote. */
- return NULL;
- }
- /* Exclude the last quote from the length. */
- len += (p_end - p);
-
- /* Skip the last quoted char to get the `]`. */
- p_end += 1;
- p = p_end;
- quoted = true;
- }
- else {
- /* Find the matching bracket. */
- while (*p && (*p != ']')) {
- len++;
- p++;
- }
- }
-
- if (UNLIKELY(*p != ']')) {
- return NULL;
- }
-
- /* Empty, return. */
- if (UNLIKELY(len == 0)) {
- return NULL;
- }
-
- /* Try to use fixed buffer if possible. */
- char *buf = (len + 1 < fixedlen) ? fixedbuf : MEM_mallocN(sizeof(char) * (len + 1), __func__);
-
- /* Copy string, taking into account escaped ']' */
- if (quoted) {
- BLI_str_unescape(buf, *path, len);
- /* +1 to step over the last quote. */
- BLI_assert((*path)[len] == '"');
- p = (*path) + len + 1;
- }
- else {
- memcpy(buf, *path, sizeof(char) * len);
- buf[len] = '\0';
- }
- /* Set path to start of next token. */
- if (*p == ']') {
- p++;
- }
- if (*p == '.') {
- p++;
- }
- *path = p;
-
- *r_quoted = quoted;
-
- return buf;
-}
-
-/**
- * \return true when the key in the path is correctly parsed and found in the collection
- * or when the path is empty.
- */
-static bool rna_path_parse_collection_key(const char **path,
- PointerRNA *ptr,
- PropertyRNA *prop,
- PointerRNA *r_nextptr)
-{
- char fixedbuf[256];
- int intkey;
-
- *r_nextptr = *ptr;
-
- /* end of path, ok */
- if (!(**path)) {
- return true;
- }
-
- bool found = false;
- if (**path == '[') {
- bool quoted;
- char *token;
-
- /* resolve the lookup with [] brackets */
- token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), &quoted);
-
- if (!token) {
- return false;
- }
-
- /* check for "" to see if it is a string */
- if (quoted) {
- if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) {
- found = true;
- }
- else {
- r_nextptr->data = NULL;
- }
- }
- else {
- /* otherwise do int lookup */
- intkey = atoi(token);
- if (intkey == 0 && (token[0] != '0' || token[1] != '\0')) {
- return false; /* we can be sure the fixedbuf was used in this case */
- }
- if (RNA_property_collection_lookup_int(ptr, prop, intkey, r_nextptr)) {
- found = true;
- }
- else {
- r_nextptr->data = NULL;
- }
- }
-
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
- }
- else {
- if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) {
- found = true;
- }
- else {
- /* ensure we quit on invalid values */
- r_nextptr->data = NULL;
- }
- }
-
- return found;
-}
-
-static bool rna_path_parse_array_index(const char **path,
- PointerRNA *ptr,
- PropertyRNA *prop,
- int *r_index)
-{
- char fixedbuf[256];
- int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0};
- int len[RNA_MAX_ARRAY_DIMENSION];
- const int dim = RNA_property_array_dimension(ptr, prop, len);
- int i;
-
- *r_index = -1;
-
- /* end of path, ok */
- if (!(**path)) {
- return true;
- }
-
- for (i = 0; i < dim; i++) {
- int temp_index = -1;
- char *token;
-
- /* multi index resolve */
- if (**path == '[') {
- bool quoted;
- token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), &quoted);
-
- if (token == NULL) {
- /* invalid syntax blah[] */
- return false;
- }
- /* check for "" to see if it is a string */
- if (quoted) {
- temp_index = RNA_property_array_item_index(prop, *token);
- }
- else {
- /* otherwise do int lookup */
- temp_index = atoi(token);
-
- if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
-
- return false;
- }
- }
- }
- else if (dim == 1) {
- /* location.x || scale.X, single dimension arrays only */
- token = rna_path_token(path, fixedbuf, sizeof(fixedbuf));
- if (token == NULL) {
- /* invalid syntax blah. */
- return false;
- }
- temp_index = RNA_property_array_item_index(prop, *token);
- }
- else {
- /* just to avoid uninitialized pointer use */
- token = fixedbuf;
- }
-
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
-
- /* out of range */
- if (temp_index < 0 || temp_index >= len[i]) {
- return false;
- }
-
- index_arr[i] = temp_index;
- /* end multi index resolve */
- }
-
- /* arrays always contain numbers so further values are not valid */
- if (**path) {
- return false;
- }
-
- /* flatten index over all dimensions */
- {
- int totdim = 1;
- int flat_index = 0;
-
- for (i = dim - 1; i >= 0; i--) {
- flat_index += index_arr[i] * totdim;
- totdim *= len[i];
- }
-
- *r_index = flat_index;
- }
- return true;
-}
-
-/**
- * Generic rna path parser.
- *
- * \note All parameters besides \a ptr and \a path are optional.
- *
- * \param ptr: The root of given RNA path.
- * \param path: The RNA path.
- * \param r_ptr: The final RNA data holding the last property in \a path.
- * \param r_prop: The final property of \a r_ptr, from \a path.
- * \param r_index: The final index in the \a r_prop, if defined by \a path.
- * \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the
- * pointer, or of the collection item.
- * Mutually exclusive with \a eval_pointer option.
- * \param r_elements: A list of \a PropertyElemRNA items(pairs of \a PointerRNA, \a PropertyRNA
- * that represent the whole given \a path).
- * \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a
- * Collection property, \a r_ptr will be set to the value of that property,
- * and \a r_prop will be NULL.
- * Mutually exclusive with \a r_item_ptr.
- *
- * \return \a true on success, \a false if the path is somehow invalid.
- */
-static bool rna_path_parse(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index,
- PointerRNA *r_item_ptr,
- ListBase *r_elements,
- const bool eval_pointer)
-{
- BLI_assert(r_item_ptr == NULL || !eval_pointer);
- PropertyRNA *prop;
- PointerRNA curptr, nextptr;
- PropertyElemRNA *prop_elem = NULL;
- int index = -1;
- char fixedbuf[256];
- int type;
- const bool do_item_ptr = r_item_ptr != NULL && !eval_pointer;
-
- if (do_item_ptr) {
- RNA_POINTER_INVALIDATE(&nextptr);
- }
-
- prop = NULL;
- curptr = *ptr;
-
- if (path == NULL || *path == '\0') {
- return false;
- }
-
- while (*path) {
- if (do_item_ptr) {
- RNA_POINTER_INVALIDATE(&nextptr);
- }
-
- const bool use_id_prop = (*path == '[');
- /* custom property lookup ?
- * C.object["someprop"]
- */
-
- if (!curptr.data) {
- return false;
- }
-
- /* look up property name in current struct */
- bool quoted = false;
- char *token = use_id_prop ?
- rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), &quoted) :
- rna_path_token(&path, fixedbuf, sizeof(fixedbuf));
- if (!token) {
- return false;
- }
-
- prop = NULL;
- if (use_id_prop) { /* look up property name in current struct */
- IDProperty *group = RNA_struct_idprops(&curptr, 0);
- if (group && quoted) {
- prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token);
- }
- }
- else {
- prop = RNA_struct_find_property(&curptr, token);
- }
-
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
-
- if (!prop) {
- return false;
- }
-
- if (r_elements) {
- prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
- prop_elem->ptr = curptr;
- prop_elem->prop = prop;
- prop_elem->index = -1; /* index will be added later, if needed. */
- BLI_addtail(r_elements, prop_elem);
- }
-
- type = RNA_property_type(prop);
-
- /* now look up the value of this property if it is a pointer or
- * collection, otherwise return the property rna so that the
- * caller can read the value of the property itself */
- switch (type) {
- case PROP_POINTER: {
- /* resolve pointer if further path elements follow
- * or explicitly requested
- */
- if (do_item_ptr || eval_pointer || *path != '\0') {
- nextptr = RNA_property_pointer_get(&curptr, prop);
- }
-
- if (eval_pointer || *path != '\0') {
- curptr = nextptr;
- prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
- index = -1;
- }
- break;
- }
- case PROP_COLLECTION: {
- /* Resolve pointer if further path elements follow.
- * Note that if path is empty, rna_path_parse_collection_key will do nothing anyway,
- * so do_item_ptr is of no use in that case.
- */
- if (*path) {
- if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) {
- return false;
- }
-
- if (eval_pointer || *path != '\0') {
- curptr = nextptr;
- prop = NULL; /* now we have a PointerRNA, the prop is our parent so forget it */
- index = -1;
- }
- }
- break;
- }
- default:
- if (r_index || prop_elem) {
- if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) {
- return false;
- }
-
- if (prop_elem) {
- prop_elem->index = index;
- }
- }
- break;
- }
- }
-
- if (r_ptr) {
- *r_ptr = curptr;
- }
- if (r_prop) {
- *r_prop = prop;
- }
- if (r_index) {
- *r_index = index;
- }
- if (r_item_ptr && do_item_ptr) {
- *r_item_ptr = nextptr;
- }
-
- if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop ||
- prop_elem->index != index)) {
- prop_elem = MEM_mallocN(sizeof(PropertyElemRNA), __func__);
- prop_elem->ptr = curptr;
- prop_elem->prop = prop;
- prop_elem->index = index;
- BLI_addtail(r_elements, prop_elem);
- }
-
- return true;
-}
-
-bool RNA_path_resolve(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, true)) {
- return false;
- }
-
- return r_ptr->data != NULL;
-}
-
-bool RNA_path_resolve_full(
- const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true)) {
- return false;
- }
-
- return r_ptr->data != NULL;
-}
-
-bool RNA_path_resolve_full_maybe_null(
- const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
-{
- return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, true);
-}
-
-bool RNA_path_resolve_property(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, NULL, NULL, false)) {
- return false;
- }
-
- return r_ptr->data != NULL && *r_prop != NULL;
-}
-
-bool RNA_path_resolve_property_full(
- const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, NULL, NULL, false)) {
- return false;
- }
-
- return r_ptr->data != NULL && *r_prop != NULL;
-}
-
-bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- PointerRNA *r_item_ptr)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, NULL, r_item_ptr, NULL, false)) {
- return false;
- }
-
- return r_ptr->data != NULL && *r_prop != NULL;
-}
-
-bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr,
- const char *path,
- PointerRNA *r_ptr,
- PropertyRNA **r_prop,
- int *r_index,
- PointerRNA *r_item_ptr)
-{
- if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, r_item_ptr, NULL, false)) {
- return false;
- }
-
- return r_ptr->data != NULL && *r_prop != NULL;
-}
-bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
-{
- return rna_path_parse(ptr, path, NULL, NULL, NULL, NULL, r_elements, false);
-}
-
-char *RNA_path_append(const char *path,
- const PointerRNA *UNUSED(ptr),
- PropertyRNA *prop,
- int intkey,
- const char *strkey)
-{
- DynStr *dynstr;
- char *result;
-
- dynstr = BLI_dynstr_new();
-
- /* add .identifier */
- if (path) {
- BLI_dynstr_append(dynstr, path);
- if (*path) {
- BLI_dynstr_append(dynstr, ".");
- }
- }
-
- BLI_dynstr_append(dynstr, RNA_property_identifier(prop));
-
- if (RNA_property_type(prop) == PROP_COLLECTION) {
- /* add ["strkey"] or [intkey] */
- BLI_dynstr_append(dynstr, "[");
-
- if (strkey) {
- const int strkey_esc_max_size = (strlen(strkey) * 2) + 1;
- char *strkey_esc = BLI_array_alloca(strkey_esc, strkey_esc_max_size);
- BLI_str_escape(strkey_esc, strkey, strkey_esc_max_size);
- BLI_dynstr_append(dynstr, "\"");
- BLI_dynstr_append(dynstr, strkey_esc);
- BLI_dynstr_append(dynstr, "\"");
- }
- else {
- char appendstr[128];
- BLI_snprintf(appendstr, sizeof(appendstr), "%d", intkey);
- BLI_dynstr_append(dynstr, appendstr);
- }
-
- BLI_dynstr_append(dynstr, "]");
- }
-
- result = BLI_dynstr_get_cstring(dynstr);
- BLI_dynstr_free(dynstr);
-
- return result;
-}
-
-/* Having both path append & back seems like it could be useful,
- * this function isn't used at the moment. */
-static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path)
-{
- char fixedbuf[256];
- const char *previous, *current;
- char *result;
- int i;
-
- if (!path) {
- return NULL;
- }
-
- previous = NULL;
- current = path;
-
- /* parse token by token until the end, then we back up to the previous
- * position and strip of the next token to get the path one step back */
- while (*current) {
- char *token;
-
- token = rna_path_token(&current, fixedbuf, sizeof(fixedbuf));
-
- if (!token) {
- return NULL;
- }
- if (token != fixedbuf) {
- MEM_freeN(token);
- }
-
- /* in case of collection we also need to strip off [] */
- bool quoted;
- token = rna_path_token_in_brackets(&current, fixedbuf, sizeof(fixedbuf), &quoted);
- if (token && token != fixedbuf) {
- MEM_freeN(token);
- }
-
- if (!*current) {
- break;
- }
-
- previous = current;
- }
-
- if (!previous) {
- return NULL;
- }
-
- /* copy and strip off last token */
- i = previous - path;
- result = BLI_strdup(path);
-
- if (i > 0 && result[i - 1] == '.') {
- i--;
- }
- result[i] = 0;
-
- return result;
-}
-
-const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop)
-{
- if (array_prop != NULL) {
- if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
- BLI_assert(array_prop->arraydimension == 0);
- return NULL;
- }
- if (array_prop->arraydimension == 0) {
- return NULL;
- }
- }
-
- /* Valid 'array part' of a rna path can only have '[', ']' and digit characters.
- * It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */
- off_t rna_path_len = (off_t)strlen(rna_path);
- if (rna_path[rna_path_len] != ']') {
- return NULL;
- }
- const char *last_valid_index_token_start = NULL;
- for (rna_path_len--; rna_path_len >= 0; rna_path_len--) {
- switch (rna_path[rna_path_len]) {
- case '[':
- if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') {
- return &rna_path[rna_path_len];
- }
- last_valid_index_token_start = &rna_path[rna_path_len];
- rna_path_len--;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- break;
- default:
- return last_valid_index_token_start;
- }
- }
- return last_valid_index_token_start;
-}
-
-/* generic path search func
- * if its needed this could also reference the IDProperty direct */
-typedef struct IDP_Chain {
- struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */
-
- const char *name;
- int index;
-
-} IDP_Chain;
-
-static char *rna_idp_path_create(IDP_Chain *child_link)
-{
- DynStr *dynstr = BLI_dynstr_new();
- char *path;
- bool is_first = true;
-
- int tot = 0;
- IDP_Chain *link = child_link;
-
- /* reverse the list */
- IDP_Chain *link_prev;
- link_prev = NULL;
- while (link) {
- IDP_Chain *link_next = link->up;
- link->up = link_prev;
- link_prev = link;
- link = link_next;
- tot++;
- }
-
- for (link = link_prev; link; link = link->up) {
- /* pass */
- if (link->index >= 0) {
- BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index);
- }
- else {
- BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name);
- }
-
- is_first = false;
- }
-
- path = BLI_dynstr_get_cstring(dynstr);
- BLI_dynstr_free(dynstr);
-
- if (*path == '\0') {
- MEM_freeN(path);
- path = NULL;
- }
-
- return path;
-}
-
-static char *rna_idp_path(PointerRNA *ptr,
- IDProperty *haystack,
- IDProperty *needle,
- IDP_Chain *parent_link)
-{
- char *path = NULL;
- IDP_Chain link;
-
- IDProperty *iter;
- int i;
-
- BLI_assert(haystack->type == IDP_GROUP);
-
- link.up = parent_link;
- /* Always set both name and index, else a stale value might get used. */
- link.name = NULL;
- link.index = -1;
-
- for (i = 0, iter = haystack->data.group.first; iter; iter = iter->next, i++) {
- if (needle == iter) { /* found! */
- link.name = iter->name;
- link.index = -1;
- path = rna_idp_path_create(&link);
- break;
- }
-
- /* Early out in case the IDProperty type cannot contain RNA properties. */
- if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) {
- continue;
- }
-
- /* Ensure this is RNA. */
- /* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name
- * collides with an actual fully static RNA property of the same struct (which would then not
- * be flagged with `PROP_IDPROPERTY`).
- *
- * That case must be ignored here, we only want to deal with runtime RNA properties stored in
- * IDProps.
- *
- * See T84091. */
- PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
- if (prop == NULL || (prop->flag & PROP_IDPROPERTY) == 0) {
- continue;
- }
-
- if (iter->type == IDP_GROUP) {
- if (prop->type == PROP_POINTER) {
- PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
- if (RNA_pointer_is_null(&child_ptr)) {
- /* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL
- * value is perfectly valid. Just means it won't match the searched needle. */
- continue;
- }
-
- link.name = iter->name;
- link.index = -1;
- if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
- break;
- }
- }
- }
- else if (iter->type == IDP_IDPARRAY) {
- if (prop->type == PROP_COLLECTION) {
- IDProperty *array = IDP_IDPArray(iter);
- if (needle >= array && needle < (iter->len + array)) { /* found! */
- link.name = iter->name;
- link.index = (int)(needle - array);
- path = rna_idp_path_create(&link);
- break;
- }
- int j;
- link.name = iter->name;
- for (j = 0; j < iter->len; j++, array++) {
- PointerRNA child_ptr;
- if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) {
- if (RNA_pointer_is_null(&child_ptr)) {
- /* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case
- * a NULL value is perfectly valid. Just means it won't match the searched needle. */
- continue;
- }
- link.index = j;
- if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
- break;
- }
- }
- }
- if (path) {
- break;
- }
- }
- }
- }
-
- return path;
-}
-
-char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
-{
- IDProperty *haystack = RNA_struct_idprops(ptr, false);
-
- if (haystack) { /* can fail when called on bones */
- return rna_idp_path(ptr, haystack, needle, NULL);
- }
- return NULL;
-}
-
-static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr)
-{
- PointerRNA id_ptr;
-
- BLI_assert(ptr->owner_id != NULL);
-
- /* TODO: Support Bones/PoseBones. no pointers stored to the bones from here, only the ID.
- * See example in T25746.
- * Unless this is added only way to find this is to also search
- * all bones and pose bones of an armature or object.
- */
- RNA_id_pointer_create(ptr->owner_id, &id_ptr);
-
- return RNA_path_from_struct_to_idproperty(&id_ptr, ptr->data);
-}
-
-ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path)
-{
- if (r_path) {
- *r_path = "";
- }
-
- if ((id == NULL) || (id->flag & LIB_EMBEDDED_DATA) == 0) {
- return id;
- }
-
- const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
- if (r_path) {
- switch (GS(id->name)) {
- case ID_NT:
- *r_path = "node_tree";
- break;
- case ID_GR:
- *r_path = "collection";
- break;
- default:
- BLI_assert_msg(0, "Missing handling of embedded id type.");
- }
- }
-
- if (id_type->owner_get == NULL) {
- BLI_assert_msg(0, "Missing handling of embedded id type.");
- return id;
- }
- return id_type->owner_get(bmain, id);
-}
-
-static char *rna_prepend_real_ID_path(Main *bmain, ID *id, char *path, ID **r_real_id)
-{
- if (r_real_id != NULL) {
- *r_real_id = NULL;
- }
-
- const char *prefix;
- ID *real_id = RNA_find_real_ID_and_path(bmain, id, &prefix);
-
- if (r_real_id != NULL) {
- *r_real_id = real_id;
- }
-
- if (path != NULL) {
- char *new_path = NULL;
-
- if (real_id) {
- if (prefix[0]) {
- new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path);
- }
- else {
- return path;
- }
- }
-
- MEM_freeN(path);
- return new_path;
- }
- return prefix[0] != '\0' ? BLI_strdup(prefix) : NULL;
-}
-
-char *RNA_path_from_ID_to_struct(const PointerRNA *ptr)
-{
- char *ptrpath = NULL;
-
- if (!ptr->owner_id || !ptr->data) {
- return NULL;
- }
-
- if (!RNA_struct_is_ID(ptr->type)) {
- if (ptr->type->path) {
- /* if type has a path to some ID, use it */
- ptrpath = ptr->type->path((PointerRNA *)ptr);
- }
- else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
- PointerRNA parentptr;
- PropertyRNA *userprop;
-
- /* find the property in the struct we're nested in that references this struct, and
- * use its identifier as the first part of the path used...
- */
- RNA_id_pointer_create(ptr->owner_id, &parentptr);
- userprop = RNA_struct_find_nested(&parentptr, ptr->type);
-
- if (userprop) {
- ptrpath = BLI_strdup(RNA_property_identifier(userprop));
- }
- else {
- return NULL; /* can't do anything about this case yet... */
- }
- }
- else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
- /* special case, easier to deal with here than in ptr->type->path() */
- return rna_path_from_ID_to_idpgroup(ptr);
- }
- else {
- return NULL;
- }
- }
-
- return ptrpath;
-}
-
-char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real)
-{
- char *path = RNA_path_from_ID_to_struct(ptr);
-
- /* NULL path is valid in that case, when given struct is an ID one... */
- return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real);
-}
-
-static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY_LENGTH],
- const int totdims,
- const int index_dim,
- int index,
- int r_index_multi[RNA_MAX_ARRAY_LENGTH])
-{
- int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1];
- int i = totdims - 1;
- dimsize_step[i + 1] = 1;
- dimsize_step[i] = dimsize[i];
- while (--i != -1) {
- dimsize_step[i] = dimsize[i] * dimsize_step[i + 1];
- }
- while (++i != index_dim) {
- int index_round = index / dimsize_step[i + 1];
- r_index_multi[i] = index_round;
- index -= (index_round * dimsize_step[i + 1]);
- }
- BLI_assert(index == 0);
-}
-
-static void rna_path_array_multi_string_from_flat_index(const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index,
- char *index_str,
- int index_str_len)
-{
- int dimsize[RNA_MAX_ARRAY_LENGTH];
- int totdims = RNA_property_array_dimension(ptr, prop, dimsize);
- int index_multi[RNA_MAX_ARRAY_LENGTH];
-
- rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi);
-
- for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) {
- offset += BLI_snprintf_rlen(
- &index_str[offset], index_str_len - offset, "[%d]", index_multi[i]);
- }
-}
-
-char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index)
-{
- const bool is_rna = (prop->magic == RNA_MAGIC);
- const char *propname;
- char *ptrpath, *path;
-
- if (!ptr->owner_id || !ptr->data) {
- return NULL;
- }
-
- /* path from ID to the struct holding this property */
- ptrpath = RNA_path_from_ID_to_struct(ptr);
-
- propname = RNA_property_identifier(prop);
-
- /* support indexing w/ multi-dimensional arrays */
- char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1];
- if (index_dim == 0) {
- index_str[0] = '\0';
- }
- else {
- rna_path_array_multi_string_from_flat_index(
- ptr, prop, index_dim, index, index_str, sizeof(index_str));
- }
-
- if (ptrpath) {
- if (is_rna) {
- path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str);
- }
- else {
- char propname_esc[MAX_IDPROP_NAME * 2];
- BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
- path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str);
- }
- MEM_freeN(ptrpath);
- }
- else if (RNA_struct_is_ID(ptr->type)) {
- if (is_rna) {
- path = BLI_sprintfN("%s%s", propname, index_str);
- }
- else {
- char propname_esc[MAX_IDPROP_NAME * 2];
- BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
- path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str);
- }
- }
- else {
- path = NULL;
- }
-
- return path;
-}
-
-char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
-{
- return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1);
-}
-
-char *RNA_path_from_real_ID_to_property_index(Main *bmain,
- const PointerRNA *ptr,
- PropertyRNA *prop,
- int index_dim,
- int index,
- ID **r_real_id)
-{
- char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index);
-
- /* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part
- * of the path either. */
- return path != NULL ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) : NULL;
-}
-
-char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr,
- PropertyRNA *prop,
- const StructRNA *type)
-{
- /* Try to recursively find an "type"'d ancestor,
- * to handle situations where path from ID is not enough. */
- PointerRNA idptr;
- ListBase path_elems = {NULL};
- char *path = NULL;
- char *full_path = RNA_path_from_ID_to_property(ptr, prop);
-
- if (full_path == NULL) {
- return NULL;
- }
-
- RNA_id_pointer_create(ptr->owner_id, &idptr);
-
- if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) {
- PropertyElemRNA *prop_elem;
-
- for (prop_elem = path_elems.last; prop_elem; prop_elem = prop_elem->prev) {
- if (RNA_struct_is_a(prop_elem->ptr.type, type)) {
- char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr);
- if (ref_path) {
- path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */
- MEM_freeN(ref_path);
- }
- break;
- }
- }
-
- BLI_freelistN(&path_elems);
- }
-
- MEM_freeN(full_path);
- return path;
-}
-
-char *RNA_path_full_ID_py(Main *bmain, ID *id)
-{
- const char *path;
- ID *id_real = RNA_find_real_ID_and_path(bmain, id, &path);
-
- if (id_real) {
- id = id_real;
- }
- else {
- path = "";
- }
-
- char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4];
- if (ID_IS_LINKED(id)) {
- 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%s",
- BKE_idtype_idcode_to_name_plural(GS(id->name)),
- id_esc,
- lib_filepath_esc,
- path[0] ? "." : "",
- path);
-}
-
-char *RNA_path_full_struct_py(Main *bmain, const PointerRNA *ptr)
-{
- char *id_path;
- char *data_path;
-
- char *ret;
-
- if (!ptr->owner_id) {
- return NULL;
- }
-
- /* never fails */
- id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
-
- data_path = RNA_path_from_ID_to_struct(ptr);
-
- /* XXX data_path may be NULL (see T36788),
- * do we want to get the 'bpy.data.foo["bar"].(null)' stuff? */
- ret = BLI_sprintfN("%s.%s", id_path, data_path);
-
- if (data_path) {
- MEM_freeN(data_path);
- }
- MEM_freeN(id_path);
-
- return ret;
-}
-
-char *RNA_path_full_property_py_ex(
- Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index, bool use_fallback)
-{
- char *id_path;
- const char *data_delim;
- const char *data_path;
- bool data_path_free;
-
- char *ret;
-
- if (!ptr->owner_id) {
- return NULL;
- }
-
- /* never fails */
- id_path = RNA_path_full_ID_py(bmain, ptr->owner_id);
-
- data_path = RNA_path_from_ID_to_property(ptr, prop);
- if (data_path) {
- data_delim = (data_path[0] == '[') ? "" : ".";
- data_path_free = true;
- }
- else {
- if (use_fallback) {
- /* Fuzzy fallback. Be explicit in our ignorance. */
- data_path = RNA_property_identifier(prop);
- data_delim = " ... ";
- }
- else {
- data_delim = ".";
- }
- data_path_free = false;
- }
-
- if ((index == -1) || (RNA_property_array_check(prop) == false)) {
- ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path);
- }
- else {
- ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index);
- }
- MEM_freeN(id_path);
- if (data_path_free) {
- MEM_freeN((void *)data_path);
- }
-
- return ret;
-}
-
-char *RNA_path_full_property_py(Main *bmain, const PointerRNA *ptr, PropertyRNA *prop, int index)
-{
- return RNA_path_full_property_py_ex(bmain, ptr, prop, index, false);
-}
-
-char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
-{
- char *data_path;
-
- char *ret;
-
- if (!ptr->owner_id) {
- return NULL;
- }
-
- data_path = RNA_path_from_ID_to_property(ptr, prop);
-
- if (data_path == NULL) {
- /* This may not be an ID at all, check for simple when pointer owns property.
- * TODO: more complex nested case. */
- if (!RNA_struct_is_ID(ptr->type)) {
- const char *prop_identifier = RNA_property_identifier(prop);
- if (RNA_struct_find_property(ptr, prop_identifier) == prop) {
- data_path = BLI_strdup(prop_identifier);
- }
- }
- }
-
- if ((index == -1) || (RNA_property_array_check(prop) == false)) {
- ret = BLI_strdup(data_path);
- }
- else {
- ret = BLI_sprintfN("%s[%d]", data_path, index);
- }
-
- if (data_path) {
- MEM_freeN(data_path);
- }
-
- return ret;
-}
-
-char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
-{
- const bool is_rna = (prop->magic == RNA_MAGIC);
- const char *propname = RNA_property_identifier(prop);
- char *ret;
-
- if ((index == -1) || (RNA_property_array_check(prop) == false)) {
- if (is_rna) {
- ret = BLI_strdup(propname);
- }
- else {
- char propname_esc[MAX_IDPROP_NAME * 2];
- BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
- ret = BLI_sprintfN("[\"%s\"]", propname_esc);
- }
- }
- else {
- if (is_rna) {
- ret = BLI_sprintfN("%s[%d]", propname, index);
- }
- else {
- char propname_esc[MAX_IDPROP_NAME * 2];
- BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
- ret = BLI_sprintfN("[\"%s\"][%d]", propname_esc, index);
- }
- }
-
- return ret;
-}
-
/* Quick name based property access */
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
@@ -6662,15 +5346,15 @@ char *RNA_pointer_as_string_id(bContext *C, PointerRNA *ptr)
return cstring;
}
-static char *rna_pointer_as_string__bldata(Main *bmain, PointerRNA *ptr)
+static char *rna_pointer_as_string__bldata(PointerRNA *ptr)
{
if (ptr->type == NULL || ptr->owner_id == NULL) {
return BLI_strdup("None");
}
if (RNA_struct_is_ID(ptr->type)) {
- return RNA_path_full_ID_py(bmain, ptr->owner_id);
+ return RNA_path_full_ID_py(ptr->owner_id);
}
- return RNA_path_full_struct_py(bmain, ptr);
+ return RNA_path_full_struct_py(ptr);
}
char *RNA_pointer_as_string(bContext *C,
@@ -6685,7 +5369,7 @@ char *RNA_pointer_as_string(bContext *C,
if ((prop = rna_idproperty_check(&prop_ptr, ptr)) && prop->type != IDP_ID) {
return RNA_pointer_as_string_id(C, ptr_prop);
}
- return rna_pointer_as_string__bldata(CTX_data_main(C), ptr_prop);
+ return rna_pointer_as_string__bldata(ptr_prop);
}
char *RNA_pointer_as_string_keywords_ex(bContext *C,
diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c
index 17c00923efa..808578b4746 100644
--- a/source/blender/makesrna/intern/rna_access_compare_override.c
+++ b/source/blender/makesrna/intern/rna_access_compare_override.c
@@ -38,6 +38,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "rna_access_internal.h"
@@ -53,7 +54,7 @@ static CLG_LogRef LOG = {"rna.access_compare_override"};
* #RNA_find_real_ID_and_path, since in overrides we also consider shape keys as embedded data, not
* only root node trees and master collections.
*/
-static ID *rna_property_override_property_real_id_owner(Main *bmain,
+static ID *rna_property_override_property_real_id_owner(Main *UNUSED(bmain),
PointerRNA *ptr,
PropertyRNA *prop,
char **r_rna_path)
@@ -85,7 +86,7 @@ static ID *rna_property_override_property_real_id_owner(Main *bmain,
case ID_GR:
case ID_NT:
/* Master collections, Root node trees. */
- owner_id = RNA_find_real_ID_and_path(bmain, id, &rna_path_prefix);
+ owner_id = RNA_find_real_ID_and_path(id, &rna_path_prefix);
break;
default:
BLI_assert_unreachable();
diff --git a/source/blender/makesrna/intern/rna_access_internal.h b/source/blender/makesrna/intern/rna_access_internal.h
index a7dd35af670..384ff417fc9 100644
--- a/source/blender/makesrna/intern/rna_access_internal.h
+++ b/source/blender/makesrna/intern/rna_access_internal.h
@@ -10,6 +10,10 @@
#include "rna_internal_types.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct IDProperty;
struct PropertyRNAOrID;
@@ -26,3 +30,9 @@ void rna_property_rna_or_id_get(PropertyRNA *prop,
void rna_idproperty_touch(struct IDProperty *idprop);
struct IDProperty *rna_idproperty_find(PointerRNA *ptr, const char *name);
+
+PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index ac90ec69784..14f4a82c62b 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -177,7 +177,7 @@ static void rna_Action_fcurve_clear(bAction *act)
static TimeMarker *rna_Action_pose_markers_new(bAction *act, const char name[])
{
TimeMarker *marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
- marker->flag = 1;
+ marker->flag = SELECT;
marker->frame = 1;
BLI_strncpy_utf8(marker->name, name, sizeof(marker->name));
BLI_addtail(&act->markers, marker);
@@ -696,6 +696,11 @@ static void rna_def_action_group(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Lock", "Action group is locked");
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+ prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", AGRP_MUTED);
+ RNA_def_property_ui_text(prop, "Mute", "Action group is muted");
+ RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+
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, "flag", AGRP_EXPANDED);
diff --git a/source/blender/makesrna/intern/rna_armature.c b/source/blender/makesrna/intern/rna_armature.c
index a4094630266..f83ec0dc09b 100644
--- a/source/blender/makesrna/intern/rna_armature.c
+++ b/source/blender/makesrna/intern/rna_armature.c
@@ -8,6 +8,8 @@
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "RNA_access.h"
#include "RNA_define.h"
@@ -725,6 +727,7 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb
RNA_def_property_ui_range(prop, -5.0f, 5.0f, 1, 3);
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "Ease In", "Length of first Bezier Handle (for B-Bones only)");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ARMATURE);
RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone);
prop = RNA_def_property(srna, "bbone_easeout", PROP_FLOAT, PROP_NONE);
@@ -732,6 +735,7 @@ void rna_def_bone_curved_common(StructRNA *srna, bool is_posebone, bool is_editb
RNA_def_property_ui_range(prop, -5.0f, 5.0f, 1, 3);
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "Ease Out", "Length of second Bezier Handle (for B-Bones only)");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ARMATURE);
RNA_DEF_CURVEBONE_UPDATE(prop, is_posebone, is_editbone);
if (is_posebone == false) {
diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c
index 5e17f22ecf5..03bb1a44526 100644
--- a/source/blender/makesrna/intern/rna_attribute.c
+++ b/source/blender/makesrna/intern/rna_attribute.c
@@ -21,6 +21,8 @@
#include "BKE_attribute.h"
#include "BKE_customdata.h"
+#include "BLT_translation.h"
+
#include "WM_types.h"
const EnumPropertyItem rna_enum_attribute_type_items[] = {
@@ -194,7 +196,7 @@ const EnumPropertyItem *rna_enum_attribute_domain_itemf(ID *id,
int totitem = 0, a;
static EnumPropertyItem mesh_vertex_domain_item = {
- ATTR_DOMAIN_POINT, "POINT", 0, "Vertex", "Attribute per point/vertex"};
+ ATTR_DOMAIN_POINT, "POINT", 0, N_("Vertex"), N_("Attribute per point/vertex")};
for (a = 0; rna_enum_attribute_domain_items[a].identifier; a++) {
domain_item = &rna_enum_attribute_domain_items[a];
@@ -321,6 +323,36 @@ static void rna_ByteColorAttributeValue_color_set(PointerRNA *ptr, const float *
linearrgb_to_srgb_uchar4(&mlcol->r, values);
}
+static void rna_ByteColorAttributeValue_color_srgb_get(PointerRNA *ptr, float *values)
+{
+ MLoopCol *col = (MLoopCol *)ptr->data;
+ values[0] = col->r / 255.0f;
+ values[1] = col->g / 255.0f;
+ values[2] = col->b / 255.0f;
+ values[3] = col->a / 255.0f;
+}
+
+static void rna_ByteColorAttributeValue_color_srgb_set(PointerRNA *ptr, const float *values)
+{
+ MLoopCol *col = (MLoopCol *)ptr->data;
+ col->r = round_fl_to_uchar_clamp(values[0] * 255.0f);
+ col->g = round_fl_to_uchar_clamp(values[1] * 255.0f);
+ col->b = round_fl_to_uchar_clamp(values[2] * 255.0f);
+ col->a = round_fl_to_uchar_clamp(values[3] * 255.0f);
+}
+
+static void rna_FloatColorAttributeValue_color_srgb_get(PointerRNA *ptr, float *values)
+{
+ MPropCol *col = (MPropCol *)ptr->data;
+ linearrgb_to_srgb_v4(values, col->color);
+}
+
+static void rna_FloatColorAttributeValue_color_srgb_set(PointerRNA *ptr, const float *values)
+{
+ MPropCol *col = (MPropCol *)ptr->data;
+ srgb_to_linearrgb_v4(col->color, values);
+}
+
/* Int8 Attribute. */
static int rna_ByteIntAttributeValue_get(PointerRNA *ptr)
@@ -715,6 +747,16 @@ static void rna_def_attribute_float_color(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "color");
RNA_def_property_array(prop, 4);
RNA_def_property_update(prop, 0, "rna_Attribute_update_data");
+
+ prop = RNA_def_property(srna, "color_srgb", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_ui_text(prop, "Color", "RGBA color in sRGB color space");
+ RNA_def_property_float_sdna(prop, NULL, "color");
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_float_funcs(prop,
+ "rna_FloatColorAttributeValue_color_srgb_get",
+ "rna_FloatColorAttributeValue_color_srgb_set",
+ NULL);
+ RNA_def_property_update(prop, 0, "rna_Attribute_update_data");
}
static void rna_def_attribute_byte_color(BlenderRNA *brna)
@@ -756,6 +798,16 @@ static void rna_def_attribute_byte_color(BlenderRNA *brna)
NULL);
RNA_def_property_ui_text(prop, "Color", "RGBA color in scene linear color space");
RNA_def_property_update(prop, 0, "rna_Attribute_update_data");
+
+ prop = RNA_def_property(srna, "color_srgb", PROP_FLOAT, PROP_COLOR);
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_float_funcs(prop,
+ "rna_ByteColorAttributeValue_color_srgb_get",
+ "rna_ByteColorAttributeValue_color_srgb_set",
+ NULL);
+ RNA_def_property_ui_text(prop, "Color", "RGBA color in sRGB color space");
+ RNA_def_property_update(prop, 0, "rna_Attribute_update_data");
}
static void rna_def_attribute_int(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index e0d55050c63..bd0e14e4b59 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -14,8 +14,12 @@
#include "DNA_texture_types.h"
#include "DNA_workspace_types.h"
+#include "BKE_layer.h"
+
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -282,6 +286,11 @@ static EnumPropertyItem rna_enum_gpencil_fill_draw_modes_items[] = {
{GP_FILL_DMODE_CONTROL, "CONTROL", 0, "Edit Lines", "Use edit lines as fill boundary limits"},
{0, NULL, 0, NULL, NULL}};
+static EnumPropertyItem rna_enum_gpencil_fill_extend_modes_items[] = {
+ {GP_FILL_EMODE_RADIUS, "RADIUS", 0, "Radius", "Connect endpoints that are close together"},
+ {GP_FILL_EMODE_EXTEND, "EXTEND", 0, "Extend", "Extend strokes in straight lines"},
+ {0, NULL, 0, NULL, NULL}};
+
static EnumPropertyItem rna_enum_gpencil_fill_layers_modes_items[] = {
{GP_FILL_GPLMODE_VISIBLE, "VISIBLE", 0, "Visible", "Visible layers"},
{GP_FILL_GPLMODE_ACTIVE, "ACTIVE", 0, "Active", "Only active layer"},
@@ -765,11 +774,11 @@ static void rna_Brush_set_size(PointerRNA *ptr, int value)
brush->size = value;
}
-static void rna_Brush_use_gradient_set(PointerRNA *ptr, bool value)
+static void rna_Brush_use_gradient_set(PointerRNA *ptr, int value)
{
Brush *br = (Brush *)ptr->data;
- if (value) {
+ if (value & BRUSH_USE_GRADIENT) {
br->flag |= BRUSH_USE_GRADIENT;
}
else {
@@ -968,8 +977,10 @@ static void rna_BrushGpencilSettings_default_eraser_update(Main *bmain,
static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, PointerRNA *ptr)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Brush *brush = (Brush *)ptr->owner_id;
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
@@ -1324,6 +1335,7 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "draw_jitter");
RNA_def_property_range(prop, 0.0f, 1.0f);
RNA_def_property_ui_text(prop, "Jitter", "Jitter factor for new strokes");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_BRUSH);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
@@ -1479,14 +1491,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
- /* fill leak size */
- prop = RNA_def_property(srna, "fill_leak", PROP_INT, PROP_PIXEL);
- RNA_def_property_int_sdna(prop, NULL, "fill_leak");
- RNA_def_property_range(prop, 0, 100);
- RNA_def_property_ui_text(prop, "Leak Size", "Size in pixels to consider the leak closed");
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
-
/* fill factor size */
prop = RNA_def_property(srna, "fill_factor", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "fill_factor");
@@ -1638,7 +1642,14 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, 10.0f);
RNA_def_property_float_default(prop, 0.0f);
RNA_def_property_ui_text(
- prop, "Stroke Extension", "Strokes end extension for closing gaps, use zero to disable");
+ prop, "Closure Size", "Strokes end extension for closing gaps, use zero to disable");
+ RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+
+ prop = RNA_def_property(srna, "fill_extend_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "fill_extend_mode");
+ RNA_def_property_enum_items(prop, rna_enum_gpencil_fill_extend_modes_items);
+ RNA_def_property_ui_text(
+ prop, "Closure Mode", "Types of stroke extensions used for closing gaps");
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
/* Number of pixels to dilate fill area. Negative values contract the filled area. */
@@ -1651,6 +1662,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, NULL);
+ /* Factor to determine outline external perimeter thickness. */
+ prop = RNA_def_property(srna, "outline_thickness_factor", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "outline_fac");
+ RNA_def_property_range(prop, 0.0f, 1.0f);
+ RNA_def_property_float_default(prop, 0.0f);
+ RNA_def_property_ui_text(
+ prop, "Thickness", "Thickness of the outline stroke relative to current brush thickness");
+ RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
+
/* Flags */
prop = RNA_def_property(srna, "use_pressure", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_USE_PRESSURE);
@@ -1820,6 +1840,12 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Trim Stroke Ends", "Trim intersecting stroke ends");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ prop = RNA_def_property(srna, "use_settings_outline", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_OUTLINE_STROKE);
+ RNA_def_property_boolean_default(prop, false);
+ RNA_def_property_ui_text(prop, "Outline", "Convert stroke to perimeter");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
prop = RNA_def_property(srna, "direction", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "sculpt_flag");
RNA_def_property_enum_items(prop, prop_direction_items);
@@ -1883,6 +1909,15 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_Brush_material_update");
+ /* Secondary Material */
+ prop = RNA_def_property(srna, "material_alt", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_BrushGpencilSettings_material_poll");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK | PROP_CONTEXT_UPDATE);
+ RNA_def_property_ui_text(prop, "Material", "Material used for secondary uses for this brush");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_Brush_material_update");
+
prop = RNA_def_property(srna, "show_fill_boundary", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_FILL_SHOW_HELPLINES);
RNA_def_property_boolean_default(prop, true);
@@ -2591,6 +2626,7 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, 1000.0f);
RNA_def_property_ui_range(prop, 0.0f, 2.0f, 0.1, 4);
RNA_def_property_ui_text(prop, "Jitter", "Jitter the position of the brush while painting");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_BRUSH);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "jitter_absolute", PROP_INT, PROP_PIXEL);
@@ -2598,6 +2634,7 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_range(prop, 0, 1000000);
RNA_def_property_ui_text(
prop, "Jitter", "Jitter the position of the brush in pixels while painting");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_BRUSH);
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "spacing", PROP_INT, PROP_PERCENTAGE);
diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c
index 9628c6b2d65..0807dbe61eb 100644
--- a/source/blender/makesrna/intern/rna_camera.c
+++ b/source/blender/makesrna/intern/rna_camera.c
@@ -10,6 +10,8 @@
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "RNA_access.h"
#include "RNA_define.h"
@@ -369,6 +371,7 @@ static void rna_def_camera_background_image(BlenderRNA *brna)
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
RNA_def_property_enum_items(prop, bgpic_display_depth_items);
RNA_def_property_ui_text(prop, "Depth", "Display under or over everything");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_CAMERA);
RNA_def_property_update(prop, NC_CAMERA | ND_DRAW_RENDER_VIEWPORT, NULL);
/* expose 2 flags as a enum of 3 items */
@@ -519,6 +522,12 @@ static void rna_def_camera_dof_settings_data(BlenderRNA *brna)
prop, "Focus Object", "Use this object to define the depth of field focal point");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_dependency_update");
+ prop = RNA_def_property(srna, "focus_subtarget", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "focus_subtarget");
+ RNA_def_property_ui_text(
+ prop, "Focus Bone", "Use this armature bone to define the depth of field focal point");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_dependency_update");
+
prop = RNA_def_property(srna, "focus_distance", PROP_FLOAT, PROP_DISTANCE);
// RNA_def_property_pointer_sdna(prop, NULL, "focus_distance");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
@@ -689,14 +698,12 @@ void RNA_def_camera(BlenderRNA *brna)
prop = RNA_def_property(srna, "shift_x", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "shiftx");
- RNA_def_property_range(prop, -10.0f, 10.0f);
RNA_def_property_ui_range(prop, -2.0, 2.0, 1, 3);
RNA_def_property_ui_text(prop, "Shift X", "Camera horizontal shift");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
prop = RNA_def_property(srna, "shift_y", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "shifty");
- RNA_def_property_range(prop, -10.0f, 10.0f);
RNA_def_property_ui_range(prop, -2.0, 2.0, 1, 3);
RNA_def_property_ui_text(prop, "Shift Y", "Camera vertical shift");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Camera_update");
diff --git a/source/blender/makesrna/intern/rna_collection.c b/source/blender/makesrna/intern/rna_collection.c
index 8cd83ddcf12..833495dce7a 100644
--- a/source/blender/makesrna/intern/rna_collection.c
+++ b/source/blender/makesrna/intern/rna_collection.c
@@ -39,6 +39,7 @@ const EnumPropertyItem rna_enum_collection_color_items[] = {
# include "DEG_depsgraph.h"
# include "DEG_depsgraph_build.h"
+# include "DEG_depsgraph_query.h"
# include "BKE_collection.h"
# include "BKE_global.h"
@@ -79,26 +80,45 @@ static PointerRNA rna_Collection_objects_get(CollectionPropertyIterator *iter)
return rna_pointer_inherit_refine(&iter->parent, &RNA_Object, cob->ob);
}
-static void rna_Collection_objects_link(Collection *collection,
- Main *bmain,
- ReportList *reports,
- Object *object)
+static bool rna_collection_objects_edit_check(Collection *collection,
+ ReportList *reports,
+ Object *object)
{
+ if (!DEG_is_original_id(&collection->id)) {
+ BKE_reportf(
+ reports, RPT_ERROR, "Collection '%s' is not an original ID", collection->id.name + 2);
+ return false;
+ }
+ if (!DEG_is_original_id(&object->id)) {
+ BKE_reportf(reports, RPT_ERROR, "Collection '%s' is not an original ID", object->id.name + 2);
+ return false;
+ }
/* Currently this should not be allowed (might be supported in the future though...). */
if (ID_IS_OVERRIDE_LIBRARY(&collection->id)) {
BKE_reportf(reports,
RPT_ERROR,
- "Could not link the object '%s' because the collection '%s' is overridden",
+ "Could not (un)link the object '%s' because the collection '%s' is overridden",
object->id.name + 2,
collection->id.name + 2);
- return;
+ return false;
}
if (ID_IS_LINKED(&collection->id)) {
BKE_reportf(reports,
RPT_ERROR,
- "Could not link the object '%s' because the collection '%s' is linked",
+ "Could not (un)link the object '%s' because the collection '%s' is linked",
object->id.name + 2,
collection->id.name + 2);
+ return false;
+ }
+ return true;
+}
+
+static void rna_Collection_objects_link(Collection *collection,
+ Main *bmain,
+ ReportList *reports,
+ Object *object)
+{
+ if (!rna_collection_objects_edit_check(collection, reports, object)) {
return;
}
if (!BKE_collection_object_add(bmain, collection, object)) {
@@ -120,6 +140,9 @@ static void rna_Collection_objects_unlink(Collection *collection,
ReportList *reports,
Object *object)
{
+ if (!rna_collection_objects_edit_check(collection, reports, object)) {
+ return;
+ }
if (!BKE_collection_object_remove(bmain, collection, object, false)) {
BKE_reportf(reports,
RPT_ERROR,
@@ -204,11 +227,47 @@ static PointerRNA rna_Collection_children_get(CollectionPropertyIterator *iter)
return rna_pointer_inherit_refine(&iter->parent, &RNA_Collection, child->collection);
}
+static bool rna_collection_children_edit_check(Collection *collection,
+ ReportList *reports,
+ Collection *child)
+{
+ if (!DEG_is_original_id(&collection->id)) {
+ BKE_reportf(
+ reports, RPT_ERROR, "Collection '%s' is not an original ID", collection->id.name + 2);
+ return false;
+ }
+ if (!DEG_is_original_id(&child->id)) {
+ BKE_reportf(reports, RPT_ERROR, "Collection '%s' is not an original ID", child->id.name + 2);
+ return false;
+ }
+ /* Currently this should not be allowed (might be supported in the future though...). */
+ if (ID_IS_OVERRIDE_LIBRARY(&collection->id)) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Could not (un)link the collection '%s' because the collection '%s' is overridden",
+ child->id.name + 2,
+ collection->id.name + 2);
+ return false;
+ }
+ if (ID_IS_LINKED(&collection->id)) {
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "Could not (un)link the collection '%s' because the collection '%s' is linked",
+ child->id.name + 2,
+ collection->id.name + 2);
+ return false;
+ }
+ return true;
+}
+
static void rna_Collection_children_link(Collection *collection,
Main *bmain,
ReportList *reports,
Collection *child)
{
+ if (!rna_collection_children_edit_check(collection, reports, child)) {
+ return;
+ }
if (!BKE_collection_child_add(bmain, collection, child)) {
BKE_reportf(reports,
RPT_ERROR,
@@ -228,6 +287,9 @@ static void rna_Collection_children_unlink(Collection *collection,
ReportList *reports,
Collection *child)
{
+ if (!rna_collection_children_edit_check(collection, reports, child)) {
+ return;
+ }
if (!BKE_collection_child_remove(bmain, collection, child)) {
BKE_reportf(reports,
RPT_ERROR,
@@ -359,6 +421,14 @@ static void rna_Collection_color_tag_update(Main *UNUSED(bmain),
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, scene);
}
+static void rna_Collection_instance_offset_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ Collection *collection = (Collection *)ptr->data;
+ DEG_id_tag_update(&collection->id, ID_RECALC_GEOMETRY);
+}
+
#else
/* collection.objects */
@@ -433,7 +503,7 @@ void RNA_def_collections(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Instance Offset", "Offset from the origin to use when instancing");
RNA_def_property_ui_range(prop, -10000.0, 10000.0, 10, RNA_TRANSLATION_PREC_DEFAULT);
- RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Collection_instance_offset_update");
prop = RNA_def_property(srna, "objects", PROP_COLLECTION, PROP_NONE);
RNA_def_property_struct_type(prop, "Object");
@@ -529,6 +599,11 @@ void RNA_def_collections(BlenderRNA *brna)
0,
"No Intersection",
"Include this collection but do not generate intersection lines"},
+ {COLLECTION_LRT_FORCE_INTERSECTION,
+ "FORCE_INTERSECTION",
+ 0,
+ "Force Intersection",
+ "Generate intersection lines even with objects that disabled intersection"},
{0, NULL, 0, NULL, NULL}};
prop = RNA_def_property(srna, "lineart_usage", PROP_ENUM, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index 92cdcc6d781..b68d87587e7 100644
--- a/source/blender/makesrna/intern/rna_color.c
+++ b/source/blender/makesrna/intern/rna_color.c
@@ -23,6 +23,7 @@
#ifdef RNA_RUNTIME
# include "RNA_access.h"
+# include "RNA_path.h"
# include "DNA_image_types.h"
# include "DNA_material_types.h"
@@ -681,7 +682,6 @@ static void rna_ColorManagement_update(Main *UNUSED(bmain), Scene *UNUSED(scene)
}
if (GS(id->name) == ID_SCE) {
- DEG_id_tag_update(id, 0);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
}
}
@@ -1270,7 +1270,7 @@ static void rna_def_colormanage(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Use Curves", "Use RGB curved for pre-display transformation");
RNA_def_property_update(prop, NC_WINDOW, "rna_ColorManagement_update");
- /* ** Colorspace ** */
+ /* ** Color-space ** */
srna = RNA_def_struct(brna, "ColorManagedInputColorspaceSettings", NULL);
RNA_def_struct_path_func(srna, "rna_ColorManagedInputColorspaceSettings_path");
RNA_def_struct_ui_text(
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 1420ef36493..719c7441174 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -603,7 +603,7 @@ static const EnumPropertyItem *rna_Constraint_target_space_itemf(bContext *UNUSE
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
- if (ct->tar && ct->tar->type == OB_ARMATURE) {
+ if (ct->tar && ct->tar->type == OB_ARMATURE && !(ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE)) {
break;
}
}
@@ -3450,6 +3450,7 @@ void RNA_def_constraint(BlenderRNA *brna)
RNA_def_struct_refine_func(srna, "rna_ConstraintType_refine");
RNA_def_struct_path_func(srna, "rna_Constraint_path");
RNA_def_struct_sdna(srna, "bConstraint");
+ RNA_def_struct_ui_icon(srna, ICON_CONSTRAINT);
/* strings */
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c
index fff3f479a3f..3a90d631c63 100644
--- a/source/blender/makesrna/intern/rna_curve.c
+++ b/source/blender/makesrna/intern/rna_curve.c
@@ -67,7 +67,8 @@ const EnumPropertyItem rna_enum_keyframe_handle_type_items[] = {
*/
const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[] = {
/* Interpolation. */
- RNA_ENUM_ITEM_HEADING(N_("Interpolation"), "Standard transitions between keyframes"),
+ RNA_ENUM_ITEM_HEADING(CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Interpolation"),
+ N_("Standard transitions between keyframes")),
{BEZT_IPO_CONST,
"CONSTANT",
ICON_IPO_CONSTANT,
@@ -85,9 +86,9 @@ const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[] = {
"Smooth interpolation between A and B, with some control over curve shape"},
/* Easing. */
- RNA_ENUM_ITEM_HEADING(N_("Easing (by strength)"),
- "Predefined inertial transitions, useful for motion graphics "
- "(from least to most \"dramatic\")"),
+ RNA_ENUM_ITEM_HEADING(CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Easing (by strength)"),
+ N_("Predefined inertial transitions, useful for motion graphics "
+ "(from least to most \"dramatic\")")),
{BEZT_IPO_SINE,
"SINE",
ICON_IPO_SINE,
@@ -104,7 +105,8 @@ const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[] = {
"Circular",
"Circular easing (strongest and most dynamic)"},
- RNA_ENUM_ITEM_HEADING(N_("Dynamic Effects"), "Simple physics-inspired easing effects"),
+ RNA_ENUM_ITEM_HEADING(CTX_N_(BLT_I18NCONTEXT_ID_ACTION, "Dynamic Effects"),
+ N_("Simple physics-inspired easing effects")),
{BEZT_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"},
{BEZT_IPO_BOUNCE,
"BOUNCE",
@@ -437,7 +439,7 @@ static void rna_Curve_bevelObject_set(PointerRNA *ptr,
if (ob) {
/* If bevel object has got the save curve, as object, for which it's set as bevobj,
- * there could be infinity loop in #DispList calculation. */
+ * there could be an infinite loop in curve evaluation. */
if (ob->type == OB_CURVES_LEGACY && ob->data != cu) {
cu->bevobj = ob;
id_lib_extern((ID *)ob);
@@ -512,7 +514,7 @@ static void rna_Curve_taperObject_set(PointerRNA *ptr,
if (ob) {
/* If taper object has got the save curve, as object, for which it's set as bevobj,
- * there could be infinity loop in #DispList calculation. */
+ * there could be an infinite loop in curve evaluation. */
if (ob->type == OB_CURVES_LEGACY && ob->data != cu) {
cu->taperobj = ob;
id_lib_extern((ID *)ob);
diff --git a/source/blender/makesrna/intern/rna_curveprofile.c b/source/blender/makesrna/intern/rna_curveprofile.c
index 8aa358e074d..d425afdd537 100644
--- a/source/blender/makesrna/intern/rna_curveprofile.c
+++ b/source/blender/makesrna/intern/rna_curveprofile.c
@@ -13,6 +13,8 @@
#include "RNA_define.h"
#include "rna_internal.h"
+#include "BLT_translation.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -220,6 +222,7 @@ static void rna_def_curveprofile(BlenderRNA *brna)
prop = RNA_def_property(srna, "preset", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "preset");
RNA_def_property_enum_items(prop, rna_enum_curveprofile_preset_items);
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MESH);
RNA_def_property_ui_text(prop, "Preset", "");
prop = RNA_def_property(srna, "use_clip", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c
index bc3e5203ed0..3d29add3427 100644
--- a/source/blender/makesrna/intern/rna_curves.c
+++ b/source/blender/makesrna/intern/rna_curves.c
@@ -60,11 +60,53 @@ static void rna_Curves_curve_offset_data_begin(CollectionPropertyIterator *iter,
NULL);
}
+static float (*get_curves_positions(Curves *curves))[3]
+{
+ return (float(*)[3])CustomData_get_layer_named(
+ &curves->geometry.point_data, CD_PROP_FLOAT3, "position");
+}
+
+static const float (*get_curves_positions_const(const Curves *curves))[3]
+{
+ return (const float(*)[3])CustomData_get_layer_named(
+ &curves->geometry.point_data, CD_PROP_FLOAT3, "position");
+}
+
static int rna_CurvePoint_index_get_const(const PointerRNA *ptr)
{
const Curves *curves = rna_curves(ptr);
const float(*co)[3] = ptr->data;
- return (int)(co - curves->geometry.position);
+ const float(*positions)[3] = get_curves_positions_const(curves);
+ return (int)(co - positions);
+}
+
+static int rna_Curves_position_data_length(PointerRNA *ptr)
+{
+ const Curves *curves = rna_curves(ptr);
+ return curves->geometry.point_num;
+}
+
+int rna_Curves_position_data_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ Curves *curves = rna_curves(ptr);
+ if (index < 0 || index >= curves->geometry.point_num) {
+ return false;
+ }
+ r_ptr->owner_id = &curves->id;
+ r_ptr->type = &RNA_FloatVectorAttributeValue;
+ r_ptr->data = &get_curves_positions(curves)[index];
+ return true;
+}
+
+static void rna_Curves_position_data_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Curves *curves = rna_curves(ptr);
+ rna_iterator_array_begin(iter,
+ get_curves_positions(curves),
+ sizeof(float[3]),
+ curves->geometry.point_num,
+ false,
+ NULL);
}
static int rna_CurvePoint_index_get(PointerRNA *ptr)
@@ -85,21 +127,23 @@ static void rna_CurvePoint_location_set(PointerRNA *ptr, const float value[3])
static float rna_CurvePoint_radius_get(PointerRNA *ptr)
{
const Curves *curves = rna_curves(ptr);
- if (curves->geometry.radius == NULL) {
+ const float *radii = (const float *)CustomData_get_layer_named(
+ &curves->geometry.point_data, CD_PROP_FLOAT, "radius");
+ if (radii == NULL) {
return 0.0f;
}
- const float(*co)[3] = ptr->data;
- return curves->geometry.radius[co - curves->geometry.position];
+ return radii[rna_CurvePoint_index_get_const(ptr)];
}
static void rna_CurvePoint_radius_set(PointerRNA *ptr, float value)
{
const Curves *curves = rna_curves(ptr);
- if (curves->geometry.radius == NULL) {
+ float *radii = (float *)CustomData_get_layer_named(
+ &curves->geometry.point_data, CD_PROP_FLOAT, "radius");
+ if (radii == NULL) {
return;
}
- const float(*co)[3] = ptr->data;
- curves->geometry.radius[co - curves->geometry.position] = value;
+ radii[rna_CurvePoint_index_get_const(ptr)] = value;
}
static char *rna_CurvePoint_path(const PointerRNA *ptr)
@@ -107,6 +151,18 @@ static char *rna_CurvePoint_path(const PointerRNA *ptr)
return BLI_sprintfN("points[%d]", rna_CurvePoint_index_get_const(ptr));
}
+int rna_Curves_points_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ Curves *curves = rna_curves(ptr);
+ if (index < 0 || index >= curves->geometry.point_num) {
+ return false;
+ }
+ r_ptr->owner_id = &curves->id;
+ r_ptr->type = &RNA_CurvePoint;
+ r_ptr->data = &get_curves_positions(curves)[index];
+ return true;
+}
+
static int rna_CurveSlice_index_get_const(const PointerRNA *ptr)
{
Curves *curves = rna_curves(ptr);
@@ -123,16 +179,6 @@ static char *rna_CurveSlice_path(const PointerRNA *ptr)
return BLI_sprintfN("curves[%d]", rna_CurveSlice_index_get_const(ptr));
}
-static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
-{
- Curves *curves = rna_curves(ptr);
- const int *offset_ptr = (int *)ptr->data;
- const int offset = *offset_ptr;
- const int size = *(offset_ptr + 1) - offset;
- float(*co)[3] = curves->geometry.position + *offset_ptr;
- rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL);
-}
-
static int rna_CurveSlice_first_point_index_get(PointerRNA *ptr)
{
const int *offset_ptr = (int *)ptr->data;
@@ -146,6 +192,17 @@ static int rna_CurveSlice_points_length_get(PointerRNA *ptr)
return *(offset_ptr + 1) - offset;
}
+static void rna_CurveSlice_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Curves *curves = rna_curves(ptr);
+ const int offset = rna_CurveSlice_first_point_index_get(ptr);
+ const int size = rna_CurveSlice_points_length_get(ptr);
+ float(*positions)[3] = (float(*)[3])CustomData_get_layer_named(
+ &curves->geometry.point_data, CD_PROP_FLOAT3, "position");
+ float(*co)[3] = positions + offset;
+ rna_iterator_array_begin(iter, co, sizeof(float[3]), size, 0, NULL);
+}
+
static void rna_Curves_update_data(struct Main *UNUSED(bmain),
struct Scene *UNUSED(scene),
PointerRNA *ptr)
@@ -175,7 +232,7 @@ static void rna_def_curves_point(BlenderRNA *brna)
PropertyRNA *prop;
srna = RNA_def_struct(brna, "CurvePoint", NULL);
- RNA_def_struct_ui_text(srna, "Curve Point", "Curve curve control point");
+ RNA_def_struct_ui_text(srna, "Curve Point", "Curve control point");
RNA_def_struct_path_func(srna, "rna_CurvePoint_path");
prop = RNA_def_property(srna, "position", PROP_FLOAT, PROP_TRANSLATION);
@@ -252,20 +309,32 @@ static void rna_def_curves(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "CurveSlice");
RNA_def_property_ui_text(prop, "Curves", "All curves in the data-block");
- /* TODO: better solution for (*co)[3] parsing issue. */
-
- RNA_define_verify_sdna(0);
prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num");
RNA_def_property_struct_type(prop, "CurvePoint");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Curves_position_data_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_Curves_position_data_length",
+ "rna_Curves_points_lookup_int",
+ NULL,
+ NULL);
RNA_def_property_ui_text(prop, "Points", "Control points of all curves");
- RNA_define_verify_sdna(1);
/* Direct access to built-in attributes. */
RNA_define_verify_sdna(0);
prop = RNA_def_property(srna, "position_data", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "geometry.position", "geometry.point_num");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Curves_position_data_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_Curves_position_data_length",
+ "rna_Curves_position_data_lookup_int",
+ NULL,
+ NULL);
RNA_def_property_struct_type(prop, "FloatVectorAttributeValue");
RNA_def_property_update(prop, 0, "rna_Curves_update_data");
RNA_define_verify_sdna(1);
diff --git a/source/blender/makesrna/intern/rna_depsgraph.c b/source/blender/makesrna/intern/rna_depsgraph.c
index 6196f8d1ca0..d277f2f7bd7 100644
--- a/source/blender/makesrna/intern/rna_depsgraph.c
+++ b/source/blender/makesrna/intern/rna_depsgraph.c
@@ -43,23 +43,41 @@
/* **************** Object Instance **************** */
+typedef struct RNA_DepsgraphIterator {
+ BLI_Iterator iter;
+# ifdef WITH_PYTHON
+ /**
+ * Store the Python instance so the #BPy_StructRNA can be set as invalid iteration is completed.
+ * Otherwise accessing from Python (console auto-complete for e.g.) crashes, see: T100286. */
+ void *py_instance;
+# endif
+} RNA_DepsgraphIterator;
+
+# ifdef WITH_PYTHON
+void **rna_DepsgraphIterator_instance(PointerRNA *ptr)
+{
+ RNA_DepsgraphIterator *di = ptr->data;
+ return &di->py_instance;
+}
+# endif
+
static PointerRNA rna_DepsgraphObjectInstance_object_get(PointerRNA *ptr)
{
- BLI_Iterator *iterator = ptr->data;
- return rna_pointer_inherit_refine(ptr, &RNA_Object, iterator->current);
+ RNA_DepsgraphIterator *di = ptr->data;
+ return rna_pointer_inherit_refine(ptr, &RNA_Object, di->iter.current);
}
-static int rna_DepsgraphObjectInstance_is_instance_get(PointerRNA *ptr)
+static bool rna_DepsgraphObjectInstance_is_instance_get(PointerRNA *ptr)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
return (deg_iter->dupli_object_current != NULL);
}
static PointerRNA rna_DepsgraphObjectInstance_instance_object_get(PointerRNA *ptr)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
Object *instance_object = NULL;
if (deg_iter->dupli_object_current != NULL) {
instance_object = deg_iter->dupli_object_current->ob;
@@ -69,24 +87,24 @@ static PointerRNA rna_DepsgraphObjectInstance_instance_object_get(PointerRNA *pt
static bool rna_DepsgraphObjectInstance_show_self_get(PointerRNA *ptr)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
- int ob_visibility = BKE_object_visibility(iterator->current, deg_iter->eval_mode);
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
+ int ob_visibility = BKE_object_visibility(di->iter.current, deg_iter->eval_mode);
return (ob_visibility & OB_VISIBLE_SELF) != 0;
}
static bool rna_DepsgraphObjectInstance_show_particles_get(PointerRNA *ptr)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
- int ob_visibility = BKE_object_visibility(iterator->current, deg_iter->eval_mode);
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
+ int ob_visibility = BKE_object_visibility(di->iter.current, deg_iter->eval_mode);
return (ob_visibility & OB_VISIBLE_PARTICLES) != 0;
}
static PointerRNA rna_DepsgraphObjectInstance_parent_get(PointerRNA *ptr)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
Object *dupli_parent = NULL;
if (deg_iter->dupli_object_current != NULL) {
dupli_parent = deg_iter->dupli_parent;
@@ -96,8 +114,8 @@ static PointerRNA rna_DepsgraphObjectInstance_parent_get(PointerRNA *ptr)
static PointerRNA rna_DepsgraphObjectInstance_particle_system_get(PointerRNA *ptr)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
struct ParticleSystem *particle_system = NULL;
if (deg_iter->dupli_object_current != NULL) {
particle_system = deg_iter->dupli_object_current->particle_system;
@@ -107,8 +125,8 @@ static PointerRNA rna_DepsgraphObjectInstance_particle_system_get(PointerRNA *pt
static void rna_DepsgraphObjectInstance_persistent_id_get(PointerRNA *ptr, int *persistent_id)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
if (deg_iter->dupli_object_current != NULL) {
memcpy(persistent_id,
deg_iter->dupli_object_current->persistent_id,
@@ -119,12 +137,12 @@ static void rna_DepsgraphObjectInstance_persistent_id_get(PointerRNA *ptr, int *
}
}
-static unsigned int rna_DepsgraphObjectInstance_random_id_get(PointerRNA *ptr)
+static int rna_DepsgraphObjectInstance_random_id_get(PointerRNA *ptr)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
if (deg_iter->dupli_object_current != NULL) {
- return deg_iter->dupli_object_current->random_id;
+ return (int)deg_iter->dupli_object_current->random_id;
}
else {
return 0;
@@ -133,23 +151,23 @@ static unsigned int rna_DepsgraphObjectInstance_random_id_get(PointerRNA *ptr)
static void rna_DepsgraphObjectInstance_matrix_world_get(PointerRNA *ptr, float *mat)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
if (deg_iter->dupli_object_current != NULL) {
copy_m4_m4((float(*)[4])mat, deg_iter->dupli_object_current->mat);
}
else {
/* We can return actual object's matrix here, no reason to return identity matrix
* when this is not actually an instance... */
- Object *ob = (Object *)iterator->current;
+ Object *ob = (Object *)di->iter.current;
copy_m4_m4((float(*)[4])mat, ob->obmat);
}
}
static void rna_DepsgraphObjectInstance_orco_get(PointerRNA *ptr, float *orco)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
if (deg_iter->dupli_object_current != NULL) {
copy_v3_v3(orco, deg_iter->dupli_object_current->orco);
}
@@ -160,8 +178,8 @@ static void rna_DepsgraphObjectInstance_orco_get(PointerRNA *ptr, float *orco)
static void rna_DepsgraphObjectInstance_uv_get(PointerRNA *ptr, float *uv)
{
- BLI_Iterator *iterator = ptr->data;
- DEGObjectIterData *deg_iter = (DEGObjectIterData *)iterator->data;
+ RNA_DepsgraphIterator *di = ptr->data;
+ DEGObjectIterData *deg_iter = (DEGObjectIterData *)di->iter.data;
if (deg_iter->dupli_object_current != NULL) {
copy_v2_v2(uv, deg_iter->dupli_object_current->uv);
}
@@ -321,7 +339,7 @@ static PointerRNA rna_Depsgraph_objects_get(CollectionPropertyIterator *iter)
* so that previous one remains valid memory for python to access to. Yuck.
*/
typedef struct RNA_Depsgraph_Instances_Iterator {
- BLI_Iterator iterators[2];
+ RNA_DepsgraphIterator iterators[2];
DEGObjectIterData deg_data[2];
DupliObject dupli_object_current[2];
int counter;
@@ -337,9 +355,9 @@ static void rna_Depsgraph_object_instances_begin(CollectionPropertyIterator *ite
data->flag = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET |
DEG_ITER_OBJECT_FLAG_VISIBLE | DEG_ITER_OBJECT_FLAG_DUPLI;
- di_it->iterators[0].valid = true;
- DEG_iterator_objects_begin(&di_it->iterators[0], data);
- iter->valid = di_it->iterators[0].valid;
+ di_it->iterators[0].iter.valid = true;
+ DEG_iterator_objects_begin(&di_it->iterators[0].iter, data);
+ iter->valid = di_it->iterators[0].iter.valid;
}
static void rna_Depsgraph_object_instances_next(CollectionPropertyIterator *iter)
@@ -348,12 +366,12 @@ static void rna_Depsgraph_object_instances_next(CollectionPropertyIterator *iter
iter->internal.custom;
/* We need to copy current iterator status to next one being worked on. */
- di_it->iterators[(di_it->counter + 1) % 2] = di_it->iterators[di_it->counter % 2];
+ di_it->iterators[(di_it->counter + 1) % 2].iter = di_it->iterators[di_it->counter % 2].iter;
di_it->deg_data[(di_it->counter + 1) % 2] = di_it->deg_data[di_it->counter % 2];
di_it->counter++;
- di_it->iterators[di_it->counter % 2].data = &di_it->deg_data[di_it->counter % 2];
- DEG_iterator_objects_next(&di_it->iterators[di_it->counter % 2]);
+ di_it->iterators[di_it->counter % 2].iter.data = &di_it->deg_data[di_it->counter % 2];
+ DEG_iterator_objects_next(&di_it->iterators[di_it->counter % 2].iter);
/* Dupli_object_current is also temp memory generated during the iterations,
* it may be freed when last item has been iterated,
* so we have same issue as with the iterator itself:
@@ -365,15 +383,24 @@ static void rna_Depsgraph_object_instances_next(CollectionPropertyIterator *iter
di_it->deg_data[di_it->counter % 2].dupli_object_current =
&di_it->dupli_object_current[di_it->counter % 2];
}
- iter->valid = di_it->iterators[di_it->counter % 2].valid;
+ iter->valid = di_it->iterators[di_it->counter % 2].iter.valid;
}
static void rna_Depsgraph_object_instances_end(CollectionPropertyIterator *iter)
{
RNA_Depsgraph_Instances_Iterator *di_it = (RNA_Depsgraph_Instances_Iterator *)
iter->internal.custom;
- DEG_iterator_objects_end(&di_it->iterators[0]);
- DEG_iterator_objects_end(&di_it->iterators[1]);
+ for (int i = 0; i < ARRAY_SIZE(di_it->iterators); i++) {
+ RNA_DepsgraphIterator *di = &di_it->iterators[i];
+ DEG_iterator_objects_end(&di->iter);
+
+# ifdef WITH_PYTHON
+ if (di->py_instance) {
+ BPY_DECREF_RNA_INVALIDATE(di->py_instance);
+ }
+# endif
+ }
+
MEM_freeN(di_it);
}
@@ -381,8 +408,8 @@ static PointerRNA rna_Depsgraph_object_instances_get(CollectionPropertyIterator
{
RNA_Depsgraph_Instances_Iterator *di_it = (RNA_Depsgraph_Instances_Iterator *)
iter->internal.custom;
- BLI_Iterator *iterator = &di_it->iterators[di_it->counter % 2];
- return rna_pointer_inherit_refine(&iter->parent, &RNA_DepsgraphObjectInstance, iterator);
+ RNA_DepsgraphIterator *di = &di_it->iterators[di_it->counter % 2];
+ return rna_pointer_inherit_refine(&iter->parent, &RNA_DepsgraphObjectInstance, di);
}
/* Iteration over evaluated IDs */
@@ -498,6 +525,10 @@ static void rna_def_depsgraph_instance(BlenderRNA *brna)
"Extended information about dependency graph object iterator "
"(Warning: All data here is 'evaluated' one, not original .blend IDs)");
+# ifdef WITH_PYTHON
+ RNA_def_struct_register_funcs(srna, NULL, NULL, "rna_DepsgraphIterator_instance");
+# endif
+
prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Object");
RNA_def_property_ui_text(prop, "Object", "Evaluated object the iterator points to");
@@ -709,14 +740,13 @@ static void rna_def_depsgraph(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "Scene");
RNA_def_property_pointer_funcs(prop, "rna_Depsgraph_scene_eval_get", NULL, NULL, NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- RNA_def_property_ui_text(prop, "Scene", "Original scene dependency graph is built for");
+ RNA_def_property_ui_text(prop, "Scene", "Scene at its evaluated state");
prop = RNA_def_property(srna, "view_layer_eval", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "ViewLayer");
RNA_def_property_pointer_funcs(prop, "rna_Depsgraph_view_layer_eval_get", NULL, NULL, NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- RNA_def_property_ui_text(
- prop, "View Layer", "Original view layer dependency graph is built for");
+ RNA_def_property_ui_text(prop, "View Layer", "View layer at its evaluated state");
/* Iterators. */
diff --git a/source/blender/makesrna/intern/rna_fcurve.c b/source/blender/makesrna/intern/rna_fcurve.c
index ac8aebd2fdd..727d329781d 100644
--- a/source/blender/makesrna/intern/rna_fcurve.c
+++ b/source/blender/makesrna/intern/rna_fcurve.c
@@ -612,7 +612,7 @@ static void rna_tag_animation_update(Main *bmain, ID *id)
static void rna_FCurve_update_data_ex(ID *id, FCurve *fcu, Main *bmain)
{
sort_time_fcurve(fcu);
- calchandles_fcurve(fcu);
+ BKE_fcurve_handles_recalc(fcu);
rna_tag_animation_update(bmain, id);
}
@@ -752,7 +752,7 @@ static void rna_FModifier_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *
FModifier *fcm = (FModifier *)ptr->data;
if (fcm->curve && fcm->type == FMODIFIER_TYPE_CYCLES) {
- calchandles_fcurve(fcm->curve);
+ BKE_fcurve_handles_recalc(fcm->curve);
}
rna_tag_animation_update(bmain, id);
@@ -1021,9 +1021,20 @@ static void rna_FKeyframe_points_remove(
return;
}
- delete_fcurve_key(fcu, index, !do_fast);
+ BKE_fcurve_delete_key(fcu, index);
RNA_POINTER_INVALIDATE(bezt_ptr);
+ if (!do_fast) {
+ BKE_fcurve_handles_recalc(fcu);
+ }
+
+ rna_tag_animation_update(bmain, id);
+}
+
+static void rna_FKeyframe_points_clear(ID *id, FCurve *fcu, Main *bmain)
+{
+ BKE_fcurve_delete_keys_all(fcu);
+
rna_tag_animation_update(bmain, id);
}
@@ -2306,6 +2317,10 @@ static void rna_def_fcurve_keyframe_points(BlenderRNA *brna, PropertyRNA *cprop)
/* optional */
RNA_def_boolean(
func, "fast", 0, "Fast", "Fast keyframe removal to avoid recalculating the curve each time");
+
+ func = RNA_def_function(srna, "clear", "rna_FKeyframe_points_clear");
+ RNA_def_function_ui_description(func, "Remove all keyframes from an F-Curve");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
}
static void rna_def_fcurve(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_fluid.c b/source/blender/makesrna/intern/rna_fluid.c
index 3b22ae9d40f..384ce8f04fb 100644
--- a/source/blender/makesrna/intern/rna_fluid.c
+++ b/source/blender/makesrna/intern/rna_fluid.c
@@ -218,16 +218,22 @@ static void rna_Fluid_parts_create(Main *bmain,
# else
Object *ob = (Object *)ptr->owner_id;
BKE_fluid_particle_system_create(bmain, ob, pset_name, parts_name, psys_name, psys_type);
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ DEG_relations_tag_update(bmain);
# endif
}
-static void rna_Fluid_parts_delete(PointerRNA *ptr, int ptype)
+static void rna_Fluid_parts_delete(Main *bmain, PointerRNA *ptr, int ptype)
{
# ifndef WITH_FLUID
- UNUSED_VARS(ptr, ptype);
+ UNUSED_VARS(bmain, ptr, ptype);
# else
Object *ob = (Object *)ptr->owner_id;
BKE_fluid_particle_system_destroy(ob, ptype);
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ DEG_relations_tag_update(bmain);
# endif
}
@@ -254,7 +260,7 @@ static void rna_Fluid_flip_parts_update(Main *bmain, Scene *scene, PointerRNA *p
/* Only create a particle system in liquid domain mode.
* Remove any remaining data from a liquid sim when switching to gas. */
if (fmd->domain->type != FLUID_DOMAIN_TYPE_LIQUID) {
- rna_Fluid_parts_delete(ptr, PART_FLUID_FLIP);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FLIP);
fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP;
rna_Fluid_domain_data_reset(bmain, scene, ptr);
return;
@@ -266,7 +272,7 @@ static void rna_Fluid_flip_parts_update(Main *bmain, Scene *scene, PointerRNA *p
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FLIP;
}
else {
- rna_Fluid_parts_delete(ptr, PART_FLUID_FLIP);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FLIP);
fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FLIP;
}
rna_Fluid_update(bmain, scene, ptr);
@@ -285,7 +291,7 @@ static void rna_Fluid_spray_parts_update(Main *bmain, Scene *UNUSED(scene), Poin
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
}
else {
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAY);
fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_SPRAY;
}
}
@@ -307,7 +313,7 @@ static void rna_Fluid_bubble_parts_update(Main *bmain, Scene *UNUSED(scene), Poi
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
}
else {
- rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_BUBBLE);
fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_BUBBLE;
}
}
@@ -325,7 +331,7 @@ static void rna_Fluid_foam_parts_update(Main *bmain, Scene *UNUSED(scene), Point
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
}
else {
- rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FOAM);
fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_FOAM;
}
}
@@ -347,7 +353,7 @@ static void rna_Fluid_tracer_parts_update(Main *bmain, Scene *UNUSED(scene), Poi
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_TRACER;
}
else {
- rna_Fluid_parts_delete(ptr, PART_FLUID_TRACER);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_TRACER);
fmd->domain->particle_type &= ~FLUID_DOMAIN_PARTICLE_TRACER;
}
}
@@ -359,10 +365,10 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
fmd = (FluidModifierData *)BKE_modifiers_findby_type(ob, eModifierType_Fluid);
if (fmd->domain->sndparticle_combined_export == SNDPARTICLE_COMBINED_EXPORT_OFF) {
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAMBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYFOAM);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FOAMBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYFOAMBUBBLE);
bool exists_spray = rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAY);
bool exists_foam = rna_Fluid_parts_exists(ptr, PART_FLUID_FOAM);
@@ -392,11 +398,11 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY);
- rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAMBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAY);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FOAM);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FOAMBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYFOAMBUBBLE);
/* Re-add spray if enabled and no particle system exists for it anymore. */
bool exists_bubble = rna_Fluid_parts_exists(ptr, PART_FLUID_BUBBLE);
@@ -418,11 +424,11 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_SPRAY;
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY);
- rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM);
- rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAMBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAY);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_BUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYFOAM);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FOAMBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYFOAMBUBBLE);
/* Re-add foam if enabled and no particle system exists for it anymore. */
bool exists_foam = rna_Fluid_parts_exists(ptr, PART_FLUID_FOAM);
@@ -444,11 +450,11 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
- rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM);
- rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAMBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FOAM);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_BUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYFOAM);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYFOAMBUBBLE);
/* Re-add foam if enabled and no particle system exists for it anymore. */
bool exists_spray = rna_Fluid_parts_exists(ptr, PART_FLUID_SPRAY);
@@ -472,12 +478,12 @@ static void rna_Fluid_combined_export_update(Main *bmain, Scene *scene, PointerR
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_FOAM;
fmd->domain->particle_type |= FLUID_DOMAIN_PARTICLE_BUBBLE;
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAY);
- rna_Fluid_parts_delete(ptr, PART_FLUID_FOAM);
- rna_Fluid_parts_delete(ptr, PART_FLUID_BUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYFOAM);
- rna_Fluid_parts_delete(ptr, PART_FLUID_SPRAYBUBBLE);
- rna_Fluid_parts_delete(ptr, PART_FLUID_FOAMBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAY);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FOAM);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_BUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYFOAM);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_SPRAYBUBBLE);
+ rna_Fluid_parts_delete(bmain, ptr, PART_FLUID_FOAMBUBBLE);
}
}
else {
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 6854ce37c94..cf0ff546d41 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -1807,6 +1807,7 @@ static void rna_def_gpencil_frame(BlenderRNA *brna)
/* XXX NOTE: this cannot occur on the same frame as another sketch. */
RNA_def_property_range(prop, -MAXFRAME, MAXFRAME);
RNA_def_property_ui_text(prop, "Frame Number", "The frame on which this sketch appears");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
prop = RNA_def_property(srna, "keyframe_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "key_type");
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index dccf7d7a7a9..2dfd9d46665 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -774,7 +774,7 @@ static bool dash_segment_name_exists_fn(void *arg, const char *name)
{
const DashGpencilModifierData *dmd = (const DashGpencilModifierData *)arg;
for (int i = 0; i < dmd->segments_len; i++) {
- if (STREQ(dmd->segments[i].name, name)) {
+ if (STREQ(dmd->segments[i].name, name) && dmd->segments[i].name != name) {
return true;
}
}
@@ -3197,9 +3197,27 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
};
static const EnumPropertyItem modifier_lineart_shadow_region_filtering[] = {
- {LRT_SHADOW_FILTER_NONE, "NONE", 0, "None", ""},
- {LRT_SHADOW_FILTER_LIT, "LIT", 0, "Lit", ""},
- {LRT_SHADOW_FILTER_SHADED, "SHADED", 0, "Shaded", ""},
+ {LRT_SHADOW_FILTER_NONE,
+ "NONE",
+ 0,
+ "None",
+ "Not filtering any lines based on illumination region"},
+ {LRT_SHADOW_FILTER_ILLUMINATED,
+ "ILLUMINATED",
+ 0,
+ "Illuminated",
+ "Only selecting lines from illuminated regions"},
+ {LRT_SHADOW_FILTER_SHADED,
+ "SHADED",
+ 0,
+ "Shaded",
+ "Only selecting lines from shaded regions"},
+ {LRT_SHADOW_FILTER_ILLUMINATED_ENCLOSED_SHAPES,
+ "ILLUMINATED_ENCLOSED",
+ 0,
+ "Illuminated (Enclosed Shapes)",
+ "Selecting lines from lit regions, and make the combination of contour, light contour and "
+ "shadow lines into enclosed shapes"},
{0, NULL, 0, NULL, NULL},
};
@@ -3452,7 +3470,7 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_shadow", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "edge_types", LRT_EDGE_FLAG_PROJECTED_SHADOW);
RNA_def_property_ui_text(
- prop, "Use Shadow", "Project contour lines using a light shource object");
+ prop, "Use Shadow", "Project contour lines using a light source object");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "shadow_region_filtering", PROP_ENUM, PROP_NONE);
@@ -3464,13 +3482,6 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna)
"affect cast shadow and light contour since they are at the border");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
- prop = RNA_def_property(srna, "use_shadow_enclosed_shapes", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_SHADOW_ENCLOSED_SHAPES);
- RNA_def_property_ui_text(prop,
- "Shadow Enclosed Shapes",
- "Reproject visible lines again to get enclosed shadow shapes");
- RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
-
prop = RNA_def_property(srna, "silhouette_filtering", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "silhouette_selection");
RNA_def_property_enum_items(prop, modifier_lineart_silhouette_filtering);
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index 39f5b6e0e9f..b7ab7689dd7 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -52,6 +52,7 @@ static const EnumPropertyItem image_source_items[] = {
#ifdef RNA_RUNTIME
# include "BLI_math_base.h"
+# include "BLI_math_vector.h"
# include "BKE_global.h"
@@ -85,6 +86,10 @@ static void rna_Image_source_set(PointerRNA *ptr, int value)
ima->source = value;
BLI_assert(BKE_id_is_in_global_main(&ima->id));
BKE_image_signal(G_MAIN, ima, NULL, IMA_SIGNAL_SRC_CHANGE);
+ if (ima->source == IMA_SRC_TILED) {
+ BKE_image_signal(G_MAIN, ima, NULL, IMA_SIGNAL_RELOAD);
+ }
+
DEG_id_tag_update(&ima->id, 0);
DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS);
DEG_relations_tag_update(G_MAIN);
@@ -100,6 +105,83 @@ static void rna_Image_reload_update(Main *bmain, Scene *UNUSED(scene), PointerRN
DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS);
}
+static int rna_Image_generated_type_get(PointerRNA *ptr)
+{
+ Image *ima = (Image *)ptr->data;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ return base_tile->gen_type;
+}
+
+static void rna_Image_generated_type_set(PointerRNA *ptr, int value)
+{
+ Image *ima = (Image *)ptr->data;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ base_tile->gen_type = value;
+}
+
+static int rna_Image_generated_width_get(PointerRNA *ptr)
+{
+ Image *ima = (Image *)ptr->data;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ return base_tile->gen_x;
+}
+
+static void rna_Image_generated_width_set(PointerRNA *ptr, int value)
+{
+ Image *ima = (Image *)ptr->data;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ base_tile->gen_x = CLAMPIS(value, 1, 65536);
+}
+
+static int rna_Image_generated_height_get(PointerRNA *ptr)
+{
+ Image *ima = (Image *)ptr->data;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ return base_tile->gen_y;
+}
+
+static void rna_Image_generated_height_set(PointerRNA *ptr, int value)
+{
+ Image *ima = (Image *)ptr->data;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ base_tile->gen_y = CLAMPIS(value, 1, 65536);
+}
+
+static bool rna_Image_generated_float_get(PointerRNA *ptr)
+{
+ Image *ima = (Image *)ptr->data;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ return (base_tile->gen_flag & IMA_GEN_FLOAT) != 0;
+}
+
+static void rna_Image_generated_float_set(PointerRNA *ptr, bool value)
+{
+ Image *ima = (Image *)ptr->data;
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ if (value) {
+ base_tile->gen_flag |= IMA_GEN_FLOAT;
+ }
+ else {
+ base_tile->gen_flag &= ~IMA_GEN_FLOAT;
+ }
+}
+
+void rna_Image_generated_color_get(PointerRNA *ptr, float values[4])
+{
+ Image *ima = (Image *)(ptr->data);
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ copy_v4_v4(values, base_tile->gen_color);
+}
+
+void rna_Image_generated_color_set(PointerRNA *ptr, const float values[4])
+{
+ Image *ima = (Image *)(ptr->data);
+ ImageTile *base_tile = BKE_image_get_tile(ima, 0);
+ for (unsigned int i = 0; i < 4; i++) {
+ base_tile->gen_color[i] = CLAMPIS(values[i], 0.0f, FLT_MAX);
+ }
+}
+
static void rna_Image_generated_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Image *ima = (Image *)ptr->owner_id;
@@ -335,6 +417,20 @@ static void rna_UDIMTile_tile_number_set(PointerRNA *ptr, int value)
}
}
+static void rna_UDIMTile_generated_update(Main *UNUSED(bmain),
+ Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ Image *ima = (Image *)ptr->owner_id;
+ ImageTile *tile = (ImageTile *)ptr->data;
+
+ /* If the tile is still marked as generated, then update the tile as requested. */
+ if ((tile->gen_flag & IMA_GEN_TILE) != 0) {
+ BKE_image_fill_tile(ima, tile);
+ BKE_image_partial_update_mark_full_update(ima);
+ }
+}
+
static int rna_Image_active_tile_index_get(PointerRNA *ptr)
{
Image *image = (Image *)ptr->data;
@@ -475,18 +571,19 @@ static int rna_Image_frame_duration_get(PointerRNA *ptr)
Image *ima = (Image *)ptr->owner_id;
int duration = 1;
+ if (!BKE_image_has_anim(ima)) {
+ /* Ensure image has been loaded into memory and frame duration is known. */
+ void *lock;
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ BKE_image_release_ibuf(ima, ibuf, lock);
+ }
+
if (BKE_image_has_anim(ima)) {
struct anim *anim = ((ImageAnim *)ima->anims.first)->anim;
if (anim) {
duration = IMB_anim_get_duration(anim, IMB_TC_RECORD_RUN);
}
}
- else {
- /* acquire ensures ima->anim is set, if possible! */
- void *lock;
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
- BKE_image_release_ibuf(ima, ibuf, lock);
- }
return duration;
}
@@ -895,6 +992,43 @@ static void rna_def_udim_tile(BlenderRNA *brna)
RNA_def_property_int_funcs(prop, "rna_UDIMTile_channels_get", NULL, NULL);
RNA_def_property_ui_text(prop, "Channels", "Number of channels in the tile pixels buffer");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ /* Generated tile information. */
+ prop = RNA_def_property(srna, "generated_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "gen_type");
+ RNA_def_property_enum_items(prop, rna_enum_image_generated_type_items);
+ RNA_def_property_ui_text(prop, "Generated Type", "Generated image type");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
+ prop = RNA_def_property(srna, "generated_width", PROP_INT, PROP_PIXEL);
+ RNA_def_property_int_sdna(prop, NULL, "gen_x");
+ RNA_def_property_flag(prop, PROP_PROPORTIONAL);
+ RNA_def_property_range(prop, 1, 65536);
+ RNA_def_property_ui_text(prop, "Generated Width", "Generated image width");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
+ prop = RNA_def_property(srna, "generated_height", PROP_INT, PROP_PIXEL);
+ RNA_def_property_int_sdna(prop, NULL, "gen_y");
+ RNA_def_property_flag(prop, PROP_PROPORTIONAL);
+ RNA_def_property_range(prop, 1, 65536);
+ RNA_def_property_ui_text(prop, "Generated Height", "Generated image height");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
+ prop = RNA_def_property(srna, "use_generated_float", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gen_flag", IMA_GEN_FLOAT);
+ RNA_def_property_ui_text(prop, "Float Buffer", "Generate floating-point buffer");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
+ prop = RNA_def_property(srna, "generated_color", PROP_FLOAT, PROP_COLOR_GAMMA);
+ RNA_def_property_float_sdna(prop, NULL, "gen_color");
+ RNA_def_property_array(prop, 4);
+ RNA_def_property_ui_text(prop, "Color", "Fill color for the generated image");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
}
static void rna_def_udim_tiles(BlenderRNA *brna, PropertyRNA *cprop)
@@ -1078,6 +1212,8 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "gen_type");
RNA_def_property_enum_items(prop, rna_enum_image_generated_type_items);
RNA_def_property_ui_text(prop, "Generated Type", "Generated image type");
+ RNA_def_property_enum_funcs(
+ prop, "rna_Image_generated_type_get", "rna_Image_generated_type_set", NULL);
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -1086,6 +1222,8 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_range(prop, 1, 65536);
RNA_def_property_ui_text(prop, "Generated Width", "Generated image width");
+ RNA_def_property_int_funcs(
+ prop, "rna_Image_generated_width_get", "rna_Image_generated_width_set", NULL);
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -1094,12 +1232,16 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_PROPORTIONAL);
RNA_def_property_range(prop, 1, 65536);
RNA_def_property_ui_text(prop, "Generated Height", "Generated image height");
+ RNA_def_property_int_funcs(
+ prop, "rna_Image_generated_height_get", "rna_Image_generated_height_set", NULL);
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "use_generated_float", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "gen_flag", IMA_GEN_FLOAT);
RNA_def_property_ui_text(prop, "Float Buffer", "Generate floating-point buffer");
+ RNA_def_property_boolean_funcs(
+ prop, "rna_Image_generated_float_get", "rna_Image_generated_float_set");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -1107,6 +1249,8 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_float_sdna(prop, NULL, "gen_color");
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Color", "Fill color for the generated image");
+ RNA_def_property_float_funcs(
+ prop, "rna_Image_generated_color_get", "rna_Image_generated_color_set", NULL);
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 6ca8e668fa0..3d5c1810558 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -14,6 +14,10 @@
#include "UI_resources.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#define RNA_MAGIC ((int)~0)
struct AssetLibraryReference;
@@ -351,7 +355,7 @@ void rna_FreestyleSettings_module_remove(struct ID *id,
void rna_Scene_use_view_map_cache_update(struct Main *bmain,
struct Scene *scene,
struct PointerRNA *ptr);
-void rna_Scene_glsl_update(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr);
+void rna_Scene_render_update(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr);
void rna_Scene_freestyle_update(struct Main *bmain, struct Scene *scene, struct PointerRNA *ptr);
void rna_ViewLayer_name_set(struct PointerRNA *ptr, const char *value);
void rna_ViewLayer_material_override_update(struct Main *bmain,
@@ -504,9 +508,7 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop);
-#ifdef WITH_NEW_CURVES_TYPE
void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop);
-#endif
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
#ifdef WITH_SIMULATION_DATABLOCK
@@ -693,3 +695,7 @@ void rna_RenderPass_rect_set(PointerRNA *ptr, const float *values);
: -FLT_MAX, double \
: -DBL_MAX)
#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c
index 6ab9d3a46ad..ae0366bebad 100644
--- a/source/blender/makesrna/intern/rna_layer.c
+++ b/source/blender/makesrna/intern/rna_layer.c
@@ -50,8 +50,10 @@
static PointerRNA rna_ViewLayer_active_layer_collection_get(PointerRNA *ptr)
{
+ const Scene *scene = (const Scene *)ptr->owner_id;
ViewLayer *view_layer = (ViewLayer *)ptr->data;
- LayerCollection *lc = view_layer->active_collection;
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ LayerCollection *lc = BKE_view_layer_active_collection_get(view_layer);
return rna_pointer_inherit_refine(ptr, &RNA_LayerCollection, lc);
}
@@ -59,8 +61,10 @@ static void rna_ViewLayer_active_layer_collection_set(PointerRNA *ptr,
PointerRNA value,
struct ReportList *UNUSED(reports))
{
+ const Scene *scene = (const Scene *)ptr->owner_id;
ViewLayer *view_layer = (ViewLayer *)ptr->data;
LayerCollection *lc = (LayerCollection *)value.data;
+ BKE_view_layer_synced_ensure(scene, view_layer);
const int index = BKE_layer_collection_findindex(view_layer, lc);
if (index != -1) {
BKE_layer_collection_activate(view_layer, lc);
@@ -69,18 +73,22 @@ static void rna_ViewLayer_active_layer_collection_set(PointerRNA *ptr,
static PointerRNA rna_LayerObjects_active_object_get(PointerRNA *ptr)
{
+ const Scene *scene = (Scene *)ptr->owner_id;
ViewLayer *view_layer = (ViewLayer *)ptr->data;
+ BKE_view_layer_synced_ensure(scene, view_layer);
return rna_pointer_inherit_refine(
- ptr, &RNA_Object, view_layer->basact ? view_layer->basact->object : NULL);
+ ptr, &RNA_Object, BKE_view_layer_active_object_get(view_layer));
}
static void rna_LayerObjects_active_object_set(PointerRNA *ptr,
PointerRNA value,
struct ReportList *reports)
{
+ const Scene *scene = (Scene *)ptr->owner_id;
ViewLayer *view_layer = (ViewLayer *)ptr->data;
if (value.data) {
Object *ob = value.data;
+ BKE_view_layer_synced_ensure(scene, view_layer);
Base *basact_test = BKE_view_layer_base_find(view_layer, ob);
if (basact_test != NULL) {
view_layer->basact = basact_test;
@@ -197,7 +205,7 @@ static void rna_LayerObjects_selected_begin(CollectionPropertyIterator *iter, Po
{
ViewLayer *view_layer = (ViewLayer *)ptr->data;
rna_iterator_listbase_begin(
- iter, &view_layer->object_bases, rna_ViewLayer_objects_selected_skip);
+ iter, BKE_view_layer_object_bases_get(view_layer), rna_ViewLayer_objects_selected_skip);
}
static void rna_ViewLayer_update_tagged(ID *id_ptr,
@@ -245,7 +253,7 @@ static void rna_ObjectBase_hide_viewport_update(bContext *C, PointerRNA *UNUSED(
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
}
@@ -309,13 +317,13 @@ static void rna_LayerCollection_exclude_update(Main *bmain, Scene *UNUSED(scene)
const bool exclude = (lc->flag & LAYER_COLLECTION_EXCLUDE) != 0;
BKE_layer_collection_set_flag(lc, LAYER_COLLECTION_EXCLUDE, exclude);
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
if (!exclude) {
/* We need to update animation of objects added back to the scene through enabling this view
* layer. */
- FOREACH_OBJECT_BEGIN (view_layer, ob) {
+ FOREACH_OBJECT_BEGIN (scene, view_layer, ob) {
DEG_id_tag_update(&ob->id, ID_RECALC_ANIMATION);
}
FOREACH_OBJECT_END;
@@ -334,7 +342,7 @@ static void rna_LayerCollection_update(Main *UNUSED(bmain), Scene *UNUSED(scene)
LayerCollection *lc = (LayerCollection *)ptr->data;
ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, lc);
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
@@ -347,9 +355,18 @@ static bool rna_LayerCollection_has_objects(LayerCollection *lc)
return (lc->runtime_flag & LAYER_COLLECTION_HAS_OBJECTS) != 0;
}
-static bool rna_LayerCollection_has_selected_objects(LayerCollection *lc, ViewLayer *view_layer)
+static bool rna_LayerCollection_has_selected_objects(LayerCollection *lc,
+ Main *bmain,
+ ViewLayer *view_layer)
{
- return BKE_layer_collection_has_selected_objects(view_layer, lc);
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ LISTBASE_FOREACH (ViewLayer *, scene_view_layer, &scene->view_layers) {
+ if (scene_view_layer == view_layer) {
+ return BKE_layer_collection_has_selected_objects(scene, view_layer, lc);
+ }
+ }
+ }
+ return false;
}
#else
@@ -442,6 +459,7 @@ static void rna_def_layer_collection(BlenderRNA *brna)
func = RNA_def_function(
srna, "has_selected_objects", "rna_LayerCollection_has_selected_objects");
+ RNA_def_function_flag(func, FUNC_USE_MAIN);
RNA_def_function_ui_description(func, "");
prop = RNA_def_pointer(
func, "view_layer", "ViewLayer", "", "View layer the layer collection belongs to");
diff --git a/source/blender/makesrna/intern/rna_light.c b/source/blender/makesrna/intern/rna_light.c
index a9e33b8cea6..9cc4b52e637 100644
--- a/source/blender/makesrna/intern/rna_light.c
+++ b/source/blender/makesrna/intern/rna_light.c
@@ -212,6 +212,7 @@ static void rna_def_light_energy(StructRNA *srna, const short light_type)
"Power",
"The energy this light would emit over its entire area "
"if it wasn't limited by the spot angle");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_LIGHT);
RNA_def_property_update(prop, 0, "rna_Light_draw_update");
break;
}
@@ -224,6 +225,7 @@ static void rna_def_light_energy(StructRNA *srna, const short light_type)
prop,
"Power",
"Light energy emitted over the entire area of the light in all directions");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_LIGHT);
RNA_def_property_update(prop, 0, "rna_Light_draw_update");
break;
}
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index 7200bcaa2a6..e0128595b7f 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -96,9 +96,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections)
RNA_MAIN_LISTBASE_FUNCS_DEF(curves)
RNA_MAIN_LISTBASE_FUNCS_DEF(fonts)
RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils)
-# ifdef WITH_NEW_CURVES_TYPE
RNA_MAIN_LISTBASE_FUNCS_DEF(hair_curves)
-# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(images)
RNA_MAIN_LISTBASE_FUNCS_DEF(lattices)
RNA_MAIN_LISTBASE_FUNCS_DEF(libraries)
@@ -171,7 +169,6 @@ void RNA_def_main(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
- CollectionDefFunc *func;
/* plural must match idtypes in readblenentry.c */
MainCollectionDef lists[] = {
@@ -375,7 +372,6 @@ void RNA_def_main(BlenderRNA *brna)
"Light Probes",
"Light Probe data-blocks",
RNA_def_main_lightprobes},
-# ifdef WITH_NEW_CURVES_TYPE
/**
* \note The name `hair_curves` is chosen to be different than `curves`,
* but they are generic curve data-blocks, not just for hair.
@@ -386,7 +382,6 @@ void RNA_def_main(BlenderRNA *brna)
"Hair Curves",
"Hair curve data-blocks",
RNA_def_main_hair_curves},
-# endif
{"pointclouds",
"PointCloud",
"rna_Main_pointclouds_begin",
@@ -471,7 +466,7 @@ void RNA_def_main(BlenderRNA *brna)
RNA_def_property_ui_text(prop, lists[i].name, lists[i].description);
/* collection functions */
- func = lists[i].func;
+ CollectionDefFunc *func = lists[i].func;
if (func) {
func(brna, prop);
}
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 6c621604e40..1f21fa3fab9 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -749,7 +749,6 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name)
return gpd;
}
-# ifdef WITH_NEW_CURVES_TYPE
static Curves *rna_Main_hair_curves_new(Main *bmain, const char *name)
{
char safe_name[MAX_ID_NAME - 2];
@@ -762,7 +761,6 @@ static Curves *rna_Main_hair_curves_new(Main *bmain, const char *name)
return curves;
}
-# endif
static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
{
@@ -847,9 +845,7 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF)
RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC)
RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS)
RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP)
-# ifdef WITH_NEW_CURVES_TYPE
RNA_MAIN_ID_TAG_FUNCS_DEF(hair_curves, hair_curves, ID_CV)
-# endif
RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT)
RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO)
# ifdef WITH_SIMULATION_DATABLOCK
@@ -2255,7 +2251,6 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
-# ifdef WITH_NEW_CURVES_TYPE
void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
@@ -2299,7 +2294,6 @@ void RNA_def_main_hair_curves(BlenderRNA *brna, PropertyRNA *cprop)
parm = RNA_def_boolean(func, "value", 0, "Value", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
-# endif
void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop)
{
diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c
index 4a9bc608598..252d2e657b5 100644
--- a/source/blender/makesrna/intern/rna_material.c
+++ b/source/blender/makesrna/intern/rna_material.c
@@ -367,13 +367,13 @@ static char *rna_GpencilColorData_path(const PointerRNA *UNUSED(ptr))
return BLI_strdup("grease_pencil");
}
-static int rna_GpencilColorData_is_stroke_visible_get(PointerRNA *ptr)
+static bool rna_GpencilColorData_is_stroke_visible_get(PointerRNA *ptr)
{
MaterialGPencilStyle *pcolor = ptr->data;
return (pcolor->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH);
}
-static int rna_GpencilColorData_is_fill_visible_get(PointerRNA *ptr)
+static bool rna_GpencilColorData_is_fill_visible_get(PointerRNA *ptr)
{
MaterialGPencilStyle *pcolor = (MaterialGPencilStyle *)ptr->data;
return ((pcolor->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (pcolor->fill_style > 0));
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 65468977ccb..222e4ac0371 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -108,13 +108,11 @@ static CustomData *rna_mesh_vdata(const PointerRNA *ptr)
Mesh *me = rna_mesh(ptr);
return rna_mesh_vdata_helper(me);
}
-# if 0
static CustomData *rna_mesh_edata(PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
return rna_mesh_edata_helper(me);
}
-# endif
static CustomData *rna_mesh_pdata(const PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
@@ -231,6 +229,16 @@ static bool rna_Mesh_has_custom_normals_get(PointerRNA *ptr)
return BKE_mesh_has_custom_loop_normals(me);
}
+static bool rna_Mesh_has_edge_bevel_weight_get(PointerRNA *ptr)
+{
+ return CustomData_has_layer(rna_mesh_edata(ptr), CD_BWEIGHT);
+}
+
+static bool rna_Mesh_has_vertex_bevel_weight_get(PointerRNA *ptr)
+{
+ return CustomData_has_layer(rna_mesh_vdata(ptr), CD_BWEIGHT);
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -344,40 +352,122 @@ static void rna_Mesh_update_positions_tag(Main *bmain, Scene *scene, PointerRNA
/** \name Property get/set Callbacks
* \{ */
+static int rna_MeshVertex_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MVert *vert = (MVert *)ptr->data;
+ const int index = (int)(vert - BKE_mesh_verts(mesh));
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->totvert);
+ return index;
+}
+
+static int rna_MeshEdge_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MEdge *edge = (MEdge *)ptr->data;
+ const int index = (int)(edge - BKE_mesh_edges(mesh));
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->totedge);
+ return index;
+}
+
+static int rna_MeshPolygon_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MPoly *mpoly = (MPoly *)ptr->data;
+ const int index = (int)(mpoly - BKE_mesh_polys(mesh));
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->totpoly);
+ return index;
+}
+
+static int rna_MeshLoop_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MLoop *mloop = (MLoop *)ptr->data;
+ const int index = (int)(mloop - BKE_mesh_loops(mesh));
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->totloop);
+ return index;
+}
+
+static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const MLoopTri *ltri = (MLoopTri *)ptr->data;
+ const int index = (int)(ltri - mesh->runtime.looptris.array);
+ BLI_assert(index >= 0);
+ BLI_assert(index < mesh->runtime.looptris.len);
+ return index;
+}
+
static void rna_MeshVertex_normal_get(PointerRNA *ptr, float *value)
{
Mesh *mesh = rna_mesh(ptr);
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh);
+ const int index = rna_MeshVertex_index_get(ptr);
+ copy_v3_v3(value, vert_normals[index]);
+}
- const int index = (MVert *)ptr->data - mesh->mvert;
- BLI_assert(index >= 0);
- BLI_assert(index < mesh->totvert);
+static bool rna_MeshVertex_hide_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const bool *hide_vert = (const bool *)CustomData_get_layer_named(
+ &mesh->vdata, CD_PROP_BOOL, ".hide_vert");
+ const int index = rna_MeshVertex_index_get(ptr);
+ return hide_vert == NULL ? false : hide_vert[index];
+}
- copy_v3_v3(value, vert_normals[index]);
+static void rna_MeshVertex_hide_set(PointerRNA *ptr, bool value)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ bool *hide_vert = (bool *)CustomData_duplicate_referenced_layer_named(
+ &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert);
+ if (!hide_vert) {
+ if (!value) {
+ /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */
+ return;
+ }
+ hide_vert = (bool *)CustomData_add_layer_named(
+ &mesh->vdata, CD_PROP_BOOL, CD_SET_DEFAULT, NULL, mesh->totvert, ".hide_vert");
+ }
+ const int index = rna_MeshVertex_index_get(ptr);
+ hide_vert[index] = value;
}
static float rna_MeshVertex_bevel_weight_get(PointerRNA *ptr)
{
- MVert *mvert = (MVert *)ptr->data;
- return mvert->bweight / 255.0f;
+ const Mesh *mesh = rna_mesh(ptr);
+ const int index = rna_MeshVertex_index_get(ptr);
+ const float *values = (const float *)CustomData_get_layer(&mesh->vdata, CD_BWEIGHT);
+ return values == NULL ? 0.0f : values[index];
}
static void rna_MeshVertex_bevel_weight_set(PointerRNA *ptr, float value)
{
- MVert *mvert = (MVert *)ptr->data;
- mvert->bweight = round_fl_to_uchar_clamp(value * 255.0f);
+ Mesh *mesh = rna_mesh(ptr);
+ const int index = rna_MeshVertex_index_get(ptr);
+ float *values = (float *)CustomData_add_layer(
+ &mesh->vdata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, mesh->totvert);
+ values[index] = clamp_f(value, 0.0f, 1.0f);
}
static float rna_MEdge_bevel_weight_get(PointerRNA *ptr)
{
- MEdge *medge = (MEdge *)ptr->data;
- return medge->bweight / 255.0f;
+ const Mesh *mesh = rna_mesh(ptr);
+ const int index = rna_MeshEdge_index_get(ptr);
+ const float *values = (const float *)CustomData_get_layer(&mesh->edata, CD_BWEIGHT);
+ return values == NULL ? 0.0f : values[index];
}
static void rna_MEdge_bevel_weight_set(PointerRNA *ptr, float value)
{
- MEdge *medge = (MEdge *)ptr->data;
- medge->bweight = round_fl_to_uchar_clamp(value * 255.0f);
+ Mesh *mesh = rna_mesh(ptr);
+ const int index = rna_MeshEdge_index_get(ptr);
+ float *values = (float *)CustomData_add_layer(
+ &mesh->edata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, mesh->totedge);
+ values[index] = clamp_f(value, 0.0f, 1.0f);
}
static float rna_MEdge_crease_get(PointerRNA *ptr)
@@ -395,8 +485,8 @@ static void rna_MEdge_crease_set(PointerRNA *ptr, float value)
static void rna_MeshLoop_normal_get(PointerRNA *ptr, float *values)
{
Mesh *me = rna_mesh(ptr);
- MLoop *ml = (MLoop *)ptr->data;
- const float(*vec)[3] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_NORMAL);
+ const int index = rna_MeshLoop_index_get(ptr);
+ const float(*vec)[3] = CustomData_get(&me->ldata, index, CD_NORMAL);
if (!vec) {
zero_v3(values);
@@ -409,8 +499,8 @@ static void rna_MeshLoop_normal_get(PointerRNA *ptr, float *values)
static void rna_MeshLoop_normal_set(PointerRNA *ptr, const float *values)
{
Mesh *me = rna_mesh(ptr);
- MLoop *ml = (MLoop *)ptr->data;
- float(*vec)[3] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_NORMAL);
+ const int index = rna_MeshLoop_index_get(ptr);
+ float(*vec)[3] = CustomData_get(&me->ldata, index, CD_NORMAL);
if (vec) {
normalize_v3_v3(*vec, values);
@@ -420,8 +510,8 @@ static void rna_MeshLoop_normal_set(PointerRNA *ptr, const float *values)
static void rna_MeshLoop_tangent_get(PointerRNA *ptr, float *values)
{
Mesh *me = rna_mesh(ptr);
- MLoop *ml = (MLoop *)ptr->data;
- const float(*vec)[4] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_MLOOPTANGENT);
+ const int index = rna_MeshLoop_index_get(ptr);
+ const float(*vec)[4] = CustomData_get(&me->ldata, index, CD_MLOOPTANGENT);
if (!vec) {
zero_v3(values);
@@ -434,8 +524,8 @@ static void rna_MeshLoop_tangent_get(PointerRNA *ptr, float *values)
static float rna_MeshLoop_bitangent_sign_get(PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
- MLoop *ml = (MLoop *)ptr->data;
- const float(*vec)[4] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_MLOOPTANGENT);
+ const int index = rna_MeshLoop_index_get(ptr);
+ const float(*vec)[4] = CustomData_get(&me->ldata, index, CD_MLOOPTANGENT);
return (vec) ? (*vec)[3] : 0.0f;
}
@@ -443,9 +533,9 @@ static float rna_MeshLoop_bitangent_sign_get(PointerRNA *ptr)
static void rna_MeshLoop_bitangent_get(PointerRNA *ptr, float *values)
{
Mesh *me = rna_mesh(ptr);
- MLoop *ml = (MLoop *)ptr->data;
- const float(*nor)[3] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_NORMAL);
- const float(*vec)[4] = CustomData_get(&me->ldata, (int)(ml - me->mloop), CD_MLOOPTANGENT);
+ const int index = rna_MeshLoop_index_get(ptr);
+ const float(*nor)[3] = CustomData_get(&me->ldata, index, CD_NORMAL);
+ const float(*vec)[4] = CustomData_get(&me->ldata, index, CD_MLOOPTANGENT);
if (nor && vec) {
cross_v3_v3v3(values, (const float *)nor, (const float *)vec);
@@ -460,31 +550,76 @@ static void rna_MeshPolygon_normal_get(PointerRNA *ptr, float *values)
{
Mesh *me = rna_mesh(ptr);
MPoly *mp = (MPoly *)ptr->data;
+ const MVert *verts = BKE_mesh_verts(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+ BKE_mesh_calc_poly_normal(mp, loops + mp->loopstart, verts, values);
+}
+
+static bool rna_MeshPolygon_hide_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const bool *hide_poly = (const bool *)CustomData_get_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, ".hide_poly");
+ const int index = rna_MeshPolygon_index_get(ptr);
+ return hide_poly == NULL ? false : hide_poly[index];
+}
+
+static void rna_MeshPolygon_hide_set(PointerRNA *ptr, bool value)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ bool *hide_poly = (bool *)CustomData_duplicate_referenced_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly);
+ if (!hide_poly) {
+ if (!value) {
+ /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */
+ return;
+ }
+ hide_poly = (bool *)CustomData_add_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, CD_SET_DEFAULT, NULL, mesh->totpoly, ".hide_poly");
+ }
+ const int index = rna_MeshPolygon_index_get(ptr);
+ hide_poly[index] = value;
+}
- BKE_mesh_calc_poly_normal(mp, me->mloop + mp->loopstart, me->mvert, values);
+static int rna_MeshPolygon_material_index_get(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ const int *material_indices = BKE_mesh_material_indices(mesh);
+ const int index = rna_MeshPolygon_index_get(ptr);
+ return material_indices == NULL ? 0 : material_indices[index];
+}
+
+static void rna_MeshPolygon_material_index_set(PointerRNA *ptr, int value)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ int *material_indices = BKE_mesh_material_indices_for_write(mesh);
+ const int index = rna_MeshPolygon_index_get(ptr);
+ material_indices[index] = value;
}
static void rna_MeshPolygon_center_get(PointerRNA *ptr, float *values)
{
Mesh *me = rna_mesh(ptr);
MPoly *mp = (MPoly *)ptr->data;
-
- BKE_mesh_calc_poly_center(mp, me->mloop + mp->loopstart, me->mvert, values);
+ const MVert *verts = BKE_mesh_verts(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+ BKE_mesh_calc_poly_center(mp, loops + mp->loopstart, verts, values);
}
static float rna_MeshPolygon_area_get(PointerRNA *ptr)
{
Mesh *me = (Mesh *)ptr->owner_id;
MPoly *mp = (MPoly *)ptr->data;
-
- return BKE_mesh_calc_poly_area(mp, me->mloop + mp->loopstart, me->mvert);
+ const MVert *verts = BKE_mesh_verts(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+ return BKE_mesh_calc_poly_area(mp, loops + mp->loopstart, verts);
}
static void rna_MeshPolygon_flip(ID *id, MPoly *mp)
{
Mesh *me = (Mesh *)id;
-
- BKE_mesh_polygon_flip(mp, me->mloop, &me->ldata);
+ MLoop *loops = BKE_mesh_loops_for_write(me);
+ BKE_mesh_polygon_flip(mp, loops, &me->ldata);
BKE_mesh_tessface_clear(me);
BKE_mesh_runtime_clear_geometry(me);
BKE_mesh_normals_tag_dirty(me);
@@ -493,21 +628,24 @@ static void rna_MeshPolygon_flip(ID *id, MPoly *mp)
static void rna_MeshLoopTriangle_verts_get(PointerRNA *ptr, int *values)
{
Mesh *me = rna_mesh(ptr);
+ const MLoop *loops = BKE_mesh_loops(me);
MLoopTri *lt = (MLoopTri *)ptr->data;
- values[0] = me->mloop[lt->tri[0]].v;
- values[1] = me->mloop[lt->tri[1]].v;
- values[2] = me->mloop[lt->tri[2]].v;
+ values[0] = loops[lt->tri[0]].v;
+ values[1] = loops[lt->tri[1]].v;
+ values[2] = loops[lt->tri[2]].v;
}
static void rna_MeshLoopTriangle_normal_get(PointerRNA *ptr, float *values)
{
Mesh *me = rna_mesh(ptr);
MLoopTri *lt = (MLoopTri *)ptr->data;
- unsigned int v1 = me->mloop[lt->tri[0]].v;
- unsigned int v2 = me->mloop[lt->tri[1]].v;
- unsigned int v3 = me->mloop[lt->tri[2]].v;
+ const MVert *verts = BKE_mesh_verts(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+ unsigned int v1 = loops[lt->tri[0]].v;
+ unsigned int v2 = loops[lt->tri[1]].v;
+ unsigned int v3 = loops[lt->tri[2]].v;
- normal_tri_v3(values, me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co);
+ normal_tri_v3(values, verts[v1].co, verts[v2].co, verts[v3].co);
}
static void rna_MeshLoopTriangle_split_normals_get(PointerRNA *ptr, float *values)
@@ -532,11 +670,12 @@ static float rna_MeshLoopTriangle_area_get(PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
MLoopTri *lt = (MLoopTri *)ptr->data;
- unsigned int v1 = me->mloop[lt->tri[0]].v;
- unsigned int v2 = me->mloop[lt->tri[1]].v;
- unsigned int v3 = me->mloop[lt->tri[2]].v;
-
- return area_tri_v3(me->mvert[v1].co, me->mvert[v2].co, me->mvert[v3].co);
+ const MVert *verts = BKE_mesh_verts(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+ unsigned int v1 = loops[lt->tri[0]].v;
+ unsigned int v2 = loops[lt->tri[1]].v;
+ unsigned int v3 = loops[lt->tri[2]].v;
+ return area_tri_v3(verts[v1].co, verts[v2].co, verts[v3].co);
}
static void rna_MeshLoopColor_color_get(PointerRNA *ptr, float *values)
@@ -586,10 +725,10 @@ static void rna_Mesh_texspace_loc_get(PointerRNA *ptr, float values[3])
static void rna_MeshVertex_groups_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
Mesh *me = rna_mesh(ptr);
-
- if (me->dvert) {
- MVert *mvert = (MVert *)ptr->data;
- MDeformVert *dvert = me->dvert + (mvert - me->mvert);
+ MDeformVert *dverts = (MDeformVert *)BKE_mesh_deform_verts(me);
+ if (dverts) {
+ const int index = rna_MeshVertex_index_get(ptr);
+ MDeformVert *dvert = &dverts[index];
rna_iterator_array_begin(
iter, (void *)dvert->dw, sizeof(MDeformWeight), dvert->totweight, 0, NULL);
@@ -606,11 +745,12 @@ static void rna_MeshVertex_undeformed_co_get(PointerRNA *ptr, float values[3])
const float(*orco)[3] = CustomData_get_layer(&me->vdata, CD_ORCO);
if (orco) {
+ const int index = rna_MeshVertex_index_get(ptr);
/* orco is normalized to 0..1, we do inverse to match mvert->co */
float loc[3], size[3];
BKE_mesh_texspace_get(me->texcomesh ? me->texcomesh : me, loc, size);
- madd_v3_v3v3v3(values, loc, orco[(mvert - me->mvert)], size);
+ madd_v3_v3v3v3(values, loc, orco[index], size);
}
else {
copy_v3_v3(values, mvert->co);
@@ -653,7 +793,7 @@ static void rna_CustomDataLayer_active_set(
CustomData_set_layer_active(data, type, n);
}
- BKE_mesh_update_customdata_pointers(me, true);
+ BKE_mesh_tessface_clear(me);
}
static void rna_CustomDataLayer_clone_set(PointerRNA *ptr, CustomData *data, int value, int type)
@@ -670,9 +810,8 @@ static void rna_CustomDataLayer_clone_set(PointerRNA *ptr, CustomData *data, int
static bool rna_MEdge_freestyle_edge_mark_get(PointerRNA *ptr)
{
const Mesh *me = rna_mesh(ptr);
- const MEdge *medge = (MEdge *)ptr->data;
- const FreestyleEdge *fed = CustomData_get(
- &me->edata, (int)(medge - me->medge), CD_FREESTYLE_EDGE);
+ const int index = rna_MeshEdge_index_get(ptr);
+ const FreestyleEdge *fed = CustomData_get(&me->edata, index, CD_FREESTYLE_EDGE);
return fed && (fed->flag & FREESTYLE_EDGE_MARK) != 0;
}
@@ -680,11 +819,11 @@ static bool rna_MEdge_freestyle_edge_mark_get(PointerRNA *ptr)
static void rna_MEdge_freestyle_edge_mark_set(PointerRNA *ptr, bool value)
{
Mesh *me = rna_mesh(ptr);
- MEdge *medge = (MEdge *)ptr->data;
- FreestyleEdge *fed = CustomData_get(&me->edata, (int)(medge - me->medge), CD_FREESTYLE_EDGE);
+ const int index = rna_MeshEdge_index_get(ptr);
+ FreestyleEdge *fed = CustomData_get(&me->edata, index, CD_FREESTYLE_EDGE);
if (!fed) {
- fed = CustomData_add_layer(&me->edata, CD_FREESTYLE_EDGE, CD_CALLOC, NULL, me->totedge);
+ fed = CustomData_add_layer(&me->edata, CD_FREESTYLE_EDGE, CD_SET_DEFAULT, NULL, me->totedge);
}
if (value) {
fed->flag |= FREESTYLE_EDGE_MARK;
@@ -697,21 +836,20 @@ static void rna_MEdge_freestyle_edge_mark_set(PointerRNA *ptr, bool value)
static bool rna_MPoly_freestyle_face_mark_get(PointerRNA *ptr)
{
const Mesh *me = rna_mesh(ptr);
- const MPoly *mpoly = (MPoly *)ptr->data;
- const FreestyleFace *ffa = CustomData_get(
- &me->pdata, (int)(mpoly - me->mpoly), CD_FREESTYLE_FACE);
+ const int index = rna_MeshPolygon_index_get(ptr);
+ const FreestyleFace *ffa = CustomData_get(&me->pdata, index, CD_FREESTYLE_FACE);
return ffa && (ffa->flag & FREESTYLE_FACE_MARK) != 0;
}
-static void rna_MPoly_freestyle_face_mark_set(PointerRNA *ptr, int value)
+static void rna_MPoly_freestyle_face_mark_set(PointerRNA *ptr, bool value)
{
Mesh *me = rna_mesh(ptr);
- MPoly *mpoly = (MPoly *)ptr->data;
- FreestyleFace *ffa = CustomData_get(&me->pdata, (int)(mpoly - me->mpoly), CD_FREESTYLE_FACE);
+ const int index = rna_MeshPolygon_index_get(ptr);
+ FreestyleFace *ffa = CustomData_get(&me->pdata, index, CD_FREESTYLE_FACE);
if (!ffa) {
- ffa = CustomData_add_layer(&me->pdata, CD_FREESTYLE_FACE, CD_CALLOC, NULL, me->totpoly);
+ ffa = CustomData_add_layer(&me->pdata, CD_FREESTYLE_FACE, CD_SET_DEFAULT, NULL, me->totpoly);
}
if (value) {
ffa->flag |= FREESTYLE_FACE_MARK;
@@ -1145,7 +1283,8 @@ static void rna_MeshPoly_vertices_get(PointerRNA *ptr, int *values)
{
Mesh *me = rna_mesh(ptr);
MPoly *mp = (MPoly *)ptr->data;
- MLoop *ml = &me->mloop[mp->loopstart];
+ const MLoop *loops = BKE_mesh_loops(me);
+ const MLoop *ml = &loops[mp->loopstart];
unsigned int i;
for (i = mp->totloop; i > 0; i--, values++, ml++) {
*values = ml->v;
@@ -1155,8 +1294,10 @@ static void rna_MeshPoly_vertices_get(PointerRNA *ptr, int *values)
static void rna_MeshPoly_vertices_set(PointerRNA *ptr, const int *values)
{
Mesh *me = rna_mesh(ptr);
- MPoly *mp = (MPoly *)ptr->data;
- MLoop *ml = &me->mloop[mp->loopstart];
+ const MPoly *mp = (const MPoly *)ptr->data;
+ MLoop *loops = BKE_mesh_loops_for_write(me);
+
+ MLoop *ml = &loops[mp->loopstart];
unsigned int i;
for (i = mp->totloop; i > 0; i--, values++, ml++) {
ml->v = *values;
@@ -1174,53 +1315,46 @@ static void rna_MeshPoly_material_index_range(
}
# endif
-static int rna_MeshVertex_index_get(PointerRNA *ptr)
-{
- Mesh *me = rna_mesh(ptr);
- MVert *vert = (MVert *)ptr->data;
- return (int)(vert - me->mvert);
-}
-
-static int rna_MeshEdge_index_get(PointerRNA *ptr)
+static bool rna_MeshEdge_hide_get(PointerRNA *ptr)
{
- Mesh *me = rna_mesh(ptr);
- MEdge *edge = (MEdge *)ptr->data;
- return (int)(edge - me->medge);
+ const Mesh *mesh = rna_mesh(ptr);
+ const bool *hide_edge = (const bool *)CustomData_get_layer_named(
+ &mesh->edata, CD_PROP_BOOL, ".hide_edge");
+ const int index = rna_MeshEdge_index_get(ptr);
+ return hide_edge == NULL ? false : hide_edge[index];
}
-static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr)
+static void rna_MeshEdge_hide_set(PointerRNA *ptr, bool value)
{
- Mesh *me = rna_mesh(ptr);
- MLoopTri *ltri = (MLoopTri *)ptr->data;
- return (int)(ltri - me->runtime.looptris.array);
+ Mesh *mesh = rna_mesh(ptr);
+ bool *hide_edge = (bool *)CustomData_duplicate_referenced_layer_named(
+ &mesh->edata, CD_PROP_BOOL, ".hide_edge", mesh->totedge);
+ if (!hide_edge) {
+ if (!value) {
+ /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */
+ return;
+ }
+ hide_edge = (bool *)CustomData_add_layer_named(
+ &mesh->edata, CD_PROP_BOOL, CD_SET_DEFAULT, NULL, mesh->totedge, ".hide_edge");
+ }
+ const int index = rna_MeshEdge_index_get(ptr);
+ hide_edge[index] = value;
}
static int rna_MeshLoopTriangle_material_index_get(PointerRNA *ptr)
{
- Mesh *me = rna_mesh(ptr);
- MLoopTri *ltri = (MLoopTri *)ptr->data;
- return me->mpoly[ltri->poly].mat_nr;
+ const Mesh *me = rna_mesh(ptr);
+ const int *material_indices = BKE_mesh_material_indices(me);
+ const MLoopTri *ltri = (MLoopTri *)ptr->data;
+ return material_indices == NULL ? 0 : material_indices[ltri->poly];
}
static bool rna_MeshLoopTriangle_use_smooth_get(PointerRNA *ptr)
{
- Mesh *me = rna_mesh(ptr);
- MLoopTri *ltri = (MLoopTri *)ptr->data;
- return me->mpoly[ltri->poly].flag & ME_SMOOTH;
-}
-
-static int rna_MeshPolygon_index_get(PointerRNA *ptr)
-{
- Mesh *me = rna_mesh(ptr);
- MPoly *mpoly = (MPoly *)ptr->data;
- return (int)(mpoly - me->mpoly);
-}
-
-static int rna_MeshLoop_index_get(PointerRNA *ptr)
-{
- Mesh *me = rna_mesh(ptr);
- MLoop *mloop = (MLoop *)ptr->data;
- return (int)(mloop - me->mloop);
+ const Mesh *me = rna_mesh(ptr);
+ const MLoopTri *ltri = (MLoopTri *)ptr->data;
+ const MPoly *polys = BKE_mesh_polys(me);
+ return polys[ltri->poly].flag & ME_SMOOTH;
}
/* path construction */
@@ -1229,10 +1363,10 @@ static char *rna_VertexGroupElement_path(const PointerRNA *ptr)
{
const Mesh *me = rna_mesh(ptr); /* XXX not always! */
const MDeformWeight *dw = (MDeformWeight *)ptr->data;
- const MDeformVert *dvert;
+ const MDeformVert *dvert = BKE_mesh_deform_verts(me);
int a, b;
- for (a = 0, dvert = me->dvert; a < me->totvert; a++, dvert++) {
+ for (a = 0; a < me->totvert; a++, dvert++) {
for (b = 0; b < dvert->totweight; b++) {
if (dw == &dvert->dw[b]) {
return BLI_sprintfN("vertices[%d].groups[%d]", a, b);
@@ -1245,7 +1379,7 @@ static char *rna_VertexGroupElement_path(const PointerRNA *ptr)
static char *rna_MeshPolygon_path(const PointerRNA *ptr)
{
- return BLI_sprintfN("polygons[%d]", (int)((MPoly *)ptr->data - rna_mesh(ptr)->mpoly));
+ return BLI_sprintfN("polygons[%d]", rna_MeshPolygon_index_get((PointerRNA *)ptr));
}
static char *rna_MeshLoopTriangle_path(const PointerRNA *ptr)
@@ -1256,17 +1390,17 @@ static char *rna_MeshLoopTriangle_path(const PointerRNA *ptr)
static char *rna_MeshEdge_path(const PointerRNA *ptr)
{
- return BLI_sprintfN("edges[%d]", (int)((MEdge *)ptr->data - rna_mesh(ptr)->medge));
+ return BLI_sprintfN("edges[%d]", rna_MeshEdge_index_get((PointerRNA *)ptr));
}
static char *rna_MeshLoop_path(const PointerRNA *ptr)
{
- return BLI_sprintfN("loops[%d]", (int)((MLoop *)ptr->data - rna_mesh(ptr)->mloop));
+ return BLI_sprintfN("loops[%d]", rna_MeshLoop_index_get((PointerRNA *)ptr));
}
static char *rna_MeshVertex_path(const PointerRNA *ptr)
{
- return BLI_sprintfN("vertices[%d]", (int)((MVert *)ptr->data - rna_mesh(ptr)->mvert));
+ return BLI_sprintfN("vertices[%d]", rna_MeshVertex_index_get((PointerRNA *)ptr));
}
static char *rna_VertCustomData_data_path(const PointerRNA *ptr, const char *collection, int type)
@@ -1332,6 +1466,98 @@ static char *rna_LoopCustomData_data_path(const PointerRNA *ptr, const char *col
return NULL;
}
+static void rna_Mesh_vertices_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ rna_iterator_array_begin(
+ iter, BKE_mesh_verts_for_write(mesh), sizeof(MVert), mesh->totvert, false, NULL);
+}
+static int rna_Mesh_vertices_length(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ return mesh->totvert;
+}
+int rna_Mesh_vertices_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ if (index < 0 || index >= mesh->totvert) {
+ return false;
+ }
+ r_ptr->owner_id = &mesh->id;
+ r_ptr->type = &RNA_MeshVertex;
+ r_ptr->data = &BKE_mesh_verts_for_write(mesh)[index];
+ return true;
+}
+
+static void rna_Mesh_edges_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ rna_iterator_array_begin(
+ iter, BKE_mesh_edges_for_write(mesh), sizeof(MEdge), mesh->totedge, false, NULL);
+}
+static int rna_Mesh_edges_length(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ return mesh->totedge;
+}
+int rna_Mesh_edges_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ if (index < 0 || index >= mesh->totedge) {
+ return false;
+ }
+ r_ptr->owner_id = &mesh->id;
+ r_ptr->type = &RNA_MeshEdge;
+ r_ptr->data = &BKE_mesh_edges_for_write(mesh)[index];
+ return true;
+}
+
+static void rna_Mesh_polygons_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ rna_iterator_array_begin(
+ iter, BKE_mesh_polys_for_write(mesh), sizeof(MPoly), mesh->totpoly, false, NULL);
+}
+static int rna_Mesh_polygons_length(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ return mesh->totpoly;
+}
+int rna_Mesh_polygons_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ if (index < 0 || index >= mesh->totpoly) {
+ return false;
+ }
+ r_ptr->owner_id = &mesh->id;
+ r_ptr->type = &RNA_MeshPolygon;
+ r_ptr->data = &BKE_mesh_polys_for_write(mesh)[index];
+ return true;
+}
+
+static void rna_Mesh_loops_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ rna_iterator_array_begin(
+ iter, BKE_mesh_loops_for_write(mesh), sizeof(MLoop), mesh->totloop, false, NULL);
+}
+static int rna_Mesh_loops_length(PointerRNA *ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ return mesh->totloop;
+}
+int rna_Mesh_loops_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ Mesh *mesh = rna_mesh(ptr);
+ if (index < 0 || index >= mesh->totloop) {
+ return false;
+ }
+ r_ptr->owner_id = &mesh->id;
+ r_ptr->type = &RNA_MeshLoop;
+ r_ptr->data = &BKE_mesh_loops_for_write(mesh)[index];
+ return true;
+}
+
static void rna_Mesh_vertex_normals_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
const Mesh *mesh = rna_mesh(ptr);
@@ -1345,6 +1571,19 @@ static int rna_Mesh_vertex_normals_length(PointerRNA *ptr)
return mesh->totvert;
}
+int rna_Mesh_vertex_normals_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ if (index < 0 || index >= mesh->totvert) {
+ return false;
+ }
+ /* Casting away const is okay because this RNA type doesn't allow changing the value. */
+ r_ptr->owner_id = (ID *)&mesh->id;
+ r_ptr->type = &RNA_MeshNormalValue;
+ r_ptr->data = (float *)BKE_mesh_vertex_normals_ensure(mesh)[index];
+ return true;
+}
+
static void rna_Mesh_poly_normals_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
const Mesh *mesh = rna_mesh(ptr);
@@ -1358,6 +1597,19 @@ static int rna_Mesh_poly_normals_length(PointerRNA *ptr)
return mesh->totpoly;
}
+int rna_Mesh_poly_normals_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ const Mesh *mesh = rna_mesh(ptr);
+ if (index < 0 || index >= mesh->totpoly) {
+ return false;
+ }
+ /* Casting away const is okay because this RNA type doesn't allow changing the value. */
+ r_ptr->owner_id = (ID *)&mesh->id;
+ r_ptr->type = &RNA_MeshNormalValue;
+ r_ptr->data = (float *)BKE_mesh_poly_normals_ensure(mesh)[index];
+ return true;
+}
+
static char *rna_MeshUVLoop_path(const PointerRNA *ptr)
{
return rna_LoopCustomData_data_path(ptr, "uv_layers", CD_MLOOPUV);
@@ -1608,9 +1860,7 @@ static void rna_Mesh_vertex_color_remove(struct Mesh *me,
ReportList *reports,
CustomDataLayer *layer)
{
- if (ED_mesh_color_remove_named(me, layer->name) == false) {
- BKE_reportf(reports, RPT_ERROR, "Vertex color '%s' not found", layer->name);
- }
+ BKE_id_attribute_remove(&me->id, layer->name, reports);
}
static PointerRNA rna_Mesh_sculpt_vertex_color_new(struct Mesh *me,
@@ -1621,7 +1871,7 @@ static PointerRNA rna_Mesh_sculpt_vertex_color_new(struct Mesh *me,
PointerRNA ptr;
CustomData *vdata;
CustomDataLayer *cdl = NULL;
- int index = ED_mesh_sculpt_color_add(me, name, false, do_init, reports);
+ int index = ED_mesh_sculpt_color_add(me, name, do_init, reports);
if (index != -1) {
vdata = rna_mesh_vdata_helper(me);
@@ -1636,9 +1886,7 @@ static void rna_Mesh_sculpt_vertex_color_remove(struct Mesh *me,
ReportList *reports,
CustomDataLayer *layer)
{
- if (ED_mesh_sculpt_color_remove_named(me, layer->name) == false) {
- BKE_reportf(reports, RPT_ERROR, "Sculpt vertex color '%s' not found", layer->name);
- }
+ BKE_id_attribute_remove(&me->id, layer->name, reports);
}
# define DEFINE_CUSTOMDATA_PROPERTY_API( \
@@ -1650,7 +1898,8 @@ static void rna_Mesh_sculpt_vertex_color_remove(struct Mesh *me,
CustomDataLayer *cdl = NULL; \
int index; \
\
- CustomData_add_layer_named(&me->cdata, cd_prop_type, CD_DEFAULT, NULL, me->countvar, name); \
+ CustomData_add_layer_named( \
+ &me->cdata, cd_prop_type, CD_SET_DEFAULT, NULL, me->countvar, name); \
index = CustomData_get_named_layer_index(&me->cdata, cd_prop_type, name); \
\
cdl = (index == -1) ? NULL : &(me->cdata.layers[index]); \
@@ -1834,8 +2083,8 @@ static void rna_def_mvert(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE);
RNA_def_property_ui_text(prop, "Hide", "");
+ RNA_def_property_boolean_funcs(prop, "rna_MeshVertex_hide_get", "rna_MeshVertex_hide_set");
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "bevel_weight", PROP_FLOAT, PROP_NONE);
@@ -1910,8 +2159,8 @@ static void rna_def_medge(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE);
RNA_def_property_ui_text(prop, "Hide", "");
+ RNA_def_property_boolean_funcs(prop, "rna_MeshEdge_hide_get", "rna_MeshEdge_hide_set");
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "use_seam", PROP_BOOLEAN, PROP_NONE);
@@ -2110,7 +2359,8 @@ static void rna_def_mpolygon(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Loop Total", "Number of loops used by this polygon");
prop = RNA_def_property(srna, "material_index", PROP_INT, PROP_UNSIGNED);
- RNA_def_property_int_sdna(prop, NULL, "mat_nr");
+ RNA_def_property_int_funcs(
+ prop, "rna_MeshPolygon_material_index_get", "rna_MeshPolygon_material_index_set", false);
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");
@@ -2123,8 +2373,8 @@ static void rna_def_mpolygon(BlenderRNA *brna)
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE);
RNA_def_property_ui_text(prop, "Hide", "");
+ RNA_def_property_boolean_funcs(prop, "rna_MeshPolygon_hide_get", "rna_MeshPolygon_hide_set");
RNA_def_property_update(prop, 0, "rna_Mesh_update_select");
prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE);
@@ -2217,6 +2467,7 @@ static void rna_def_mloopuv(BlenderRNA *brna)
srna = RNA_def_struct(brna, "MeshUVLoop", NULL);
RNA_def_struct_sdna(srna, "MLoopUV");
+ RNA_def_struct_ui_text(srna, "Mesh UV Layer", "Layer of UV coordinates in a Mesh data-block");
RNA_def_struct_path_func(srna, "rna_MeshUVLoop_path");
prop = RNA_def_property(srna, "uv", PROP_FLOAT, PROP_XYZ);
@@ -3163,28 +3414,60 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_MESH_DATA);
prop = RNA_def_property(srna, "vertices", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "mvert", "totvert");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Mesh_vertices_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_Mesh_vertices_length",
+ "rna_Mesh_vertices_lookup_int",
+ NULL,
+ NULL);
RNA_def_property_struct_type(prop, "MeshVertex");
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Vertices", "Vertices of the mesh");
rna_def_mesh_vertices(brna, prop);
prop = RNA_def_property(srna, "edges", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "medge", "totedge");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Mesh_edges_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_Mesh_edges_length",
+ "rna_Mesh_edges_lookup_int",
+ NULL,
+ NULL);
RNA_def_property_struct_type(prop, "MeshEdge");
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Edges", "Edges of the mesh");
rna_def_mesh_edges(brna, prop);
prop = RNA_def_property(srna, "loops", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "mloop", "totloop");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Mesh_loops_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_Mesh_loops_length",
+ "rna_Mesh_loops_lookup_int",
+ NULL,
+ NULL);
RNA_def_property_struct_type(prop, "MeshLoop");
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Loops", "Loops of the mesh (polygon corners)");
rna_def_mesh_loops(brna, prop);
prop = RNA_def_property(srna, "polygons", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "mpoly", "totpoly");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Mesh_polygons_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_Mesh_polygons_length",
+ "rna_Mesh_polygons_lookup_int",
+ NULL,
+ NULL);
RNA_def_property_struct_type(prop, "MeshPolygon");
RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE);
RNA_def_property_ui_text(prop, "Polygons", "Polygons of the mesh");
@@ -3205,7 +3488,7 @@ static void rna_def_mesh(BlenderRNA *brna)
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_Mesh_vertex_normals_length",
- NULL,
+ "rna_Mesh_vertex_normals_lookup_int",
NULL,
NULL);
@@ -3222,7 +3505,7 @@ static void rna_def_mesh(BlenderRNA *brna)
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_Mesh_poly_normals_length",
- NULL,
+ "rna_Mesh_poly_normals_lookup_int",
NULL,
NULL);
@@ -3615,6 +3898,18 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_boolean_funcs(prop, "rna_Mesh_has_custom_normals_get", NULL);
RNA_define_verify_sdna(true);
+ prop = RNA_def_property(srna, "has_bevel_weight_edge", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "Has Edge Bevel Weight", "True if the mesh has an edge bevel weight layer");
+ RNA_def_property_boolean_funcs(prop, "rna_Mesh_has_edge_bevel_weight_get", NULL);
+
+ prop = RNA_def_property(srna, "has_bevel_weight_vertex", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(
+ prop, "Has Vertex Bevel Weight", "True if the mesh has an vertex bevel weight layer");
+ RNA_def_property_boolean_funcs(prop, "rna_Mesh_has_vertex_bevel_weight_get", NULL);
+
prop = RNA_def_property(srna, "texco_mesh", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "texcomesh");
RNA_def_property_flag(prop, PROP_EDITABLE);
@@ -3668,13 +3963,6 @@ static void rna_def_mesh(BlenderRNA *brna)
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_Mesh_update_vertmask");
/* customdata flags */
- prop = RNA_def_property(srna, "use_customdata_vertex_bevel", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "cd_flag", ME_CDFLAG_VERT_BWEIGHT);
- RNA_def_property_ui_text(prop, "Store Vertex Bevel Weight", "");
-
- prop = RNA_def_property(srna, "use_customdata_edge_bevel", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "cd_flag", ME_CDFLAG_EDGE_BWEIGHT);
- RNA_def_property_ui_text(prop, "Store Edge Bevel Weight", "");
prop = RNA_def_property(srna, "use_customdata_vertex_crease", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "cd_flag", ME_CDFLAG_VERT_CREASE);
diff --git a/source/blender/makesrna/intern/rna_mesh_api.c b/source/blender/makesrna/intern/rna_mesh_api.c
index 8447074a3ef..6b1df3fc4d4 100644
--- a/source/blender/makesrna/intern/rna_mesh_api.c
+++ b/source/blender/makesrna/intern/rna_mesh_api.c
@@ -22,6 +22,7 @@
# include "DNA_mesh_types.h"
+# include "BKE_anim_data.h"
# include "BKE_mesh.h"
# include "BKE_mesh_mapping.h"
# include "BKE_mesh_runtime.h"
@@ -44,7 +45,7 @@ static const char *rna_Mesh_unit_test_compare(struct Mesh *mesh,
static void rna_Mesh_create_normals_split(Mesh *mesh)
{
if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) {
- CustomData_add_layer(&mesh->ldata, CD_NORMAL, CD_CALLOC, NULL, mesh->totloop);
+ CustomData_add_layer(&mesh->ldata, CD_NORMAL, CD_SET_DEFAULT, NULL, mesh->totloop);
CustomData_set_layer_flag(&mesh->ldata, CD_NORMAL, CD_FLAG_TEMPORARY);
}
}
@@ -64,7 +65,7 @@ static void rna_Mesh_calc_tangents(Mesh *mesh, ReportList *reports, const char *
}
else {
r_looptangents = CustomData_add_layer(
- &mesh->ldata, CD_MLOOPTANGENT, CD_CALLOC, NULL, mesh->totloop);
+ &mesh->ldata, CD_MLOOPTANGENT, CD_SET_DEFAULT, NULL, mesh->totloop);
CustomData_set_layer_flag(&mesh->ldata, CD_MLOOPTANGENT, CD_FLAG_TEMPORARY);
}
@@ -90,11 +91,11 @@ static void rna_Mesh_calc_smooth_groups(
Mesh *mesh, bool use_bitflags, int *r_poly_group_len, int **r_poly_group, int *r_group_total)
{
*r_poly_group_len = mesh->totpoly;
- *r_poly_group = BKE_mesh_calc_smoothgroups(mesh->medge,
+ *r_poly_group = BKE_mesh_calc_smoothgroups(BKE_mesh_edges(mesh),
mesh->totedge,
- mesh->mpoly,
+ BKE_mesh_polys(mesh),
mesh->totpoly,
- mesh->mloop,
+ BKE_mesh_loops(mesh),
mesh->totloop,
r_group_total,
use_bitflags);
@@ -102,10 +103,10 @@ static void rna_Mesh_calc_smooth_groups(
static void rna_Mesh_normals_split_custom_do(Mesh *mesh,
float (*custom_loopnors)[3],
- const bool use_vertices)
+ const bool use_verts)
{
- if (use_vertices) {
- BKE_mesh_set_custom_normals_from_vertices(mesh, custom_loopnors);
+ if (use_verts) {
+ BKE_mesh_set_custom_normals_from_verts(mesh, custom_loopnors);
}
else {
BKE_mesh_set_custom_normals(mesh, custom_loopnors);
@@ -165,7 +166,8 @@ static void rna_Mesh_transform(Mesh *mesh, float mat[16], bool shape_keys)
static void rna_Mesh_flip_normals(Mesh *mesh)
{
- BKE_mesh_polygons_flip(mesh->mpoly, mesh->mloop, &mesh->ldata, mesh->totpoly);
+ BKE_mesh_polys_flip(
+ BKE_mesh_polys(mesh), BKE_mesh_loops_for_write(mesh), &mesh->ldata, mesh->totpoly);
BKE_mesh_tessface_clear(mesh);
BKE_mesh_normals_tag_dirty(mesh);
BKE_mesh_runtime_clear_geometry(mesh);
@@ -191,6 +193,7 @@ static void rna_Mesh_count_selected_items(Mesh *mesh, int r_count[3])
static void rna_Mesh_clear_geometry(Mesh *mesh)
{
BKE_mesh_clear_geometry(mesh);
+ BKE_animdata_free(&mesh->id, false);
DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY_ALL_MODES);
WM_main_add_notifier(NC_GEOM | ND_DATA, mesh);
diff --git a/source/blender/makesrna/intern/rna_mesh_utils.h b/source/blender/makesrna/intern/rna_mesh_utils.h
index 0b1a8ddcb4d..495c58d7b30 100644
--- a/source/blender/makesrna/intern/rna_mesh_utils.h
+++ b/source/blender/makesrna/intern/rna_mesh_utils.h
@@ -81,7 +81,7 @@
layer++, a++) { \
if (value.data == layer) { \
CustomData_set_layer_##active_type(data, layer_type, a); \
- BKE_mesh_update_customdata_pointers(me, true); \
+ BKE_mesh_tessface_clear(me); \
return; \
} \
} \
@@ -105,6 +105,6 @@
CustomData *data = rna_mesh_##customdata_type(ptr); \
if (data) { \
CustomData_set_layer_##active_type(data, layer_type, value); \
- BKE_mesh_update_customdata_pointers(me, true); \
+ BKE_mesh_tessface_clear(me); \
} \
}
diff --git a/source/blender/makesrna/intern/rna_meta_api.c b/source/blender/makesrna/intern/rna_meta_api.c
index 6595c811abc..1f8748143e3 100644
--- a/source/blender/makesrna/intern/rna_meta_api.c
+++ b/source/blender/makesrna/intern/rna_meta_api.c
@@ -28,7 +28,7 @@ static void rna_Meta_transform(struct MetaBall *mb, float mat[16])
static void rna_Mball_update_gpu_tag(MetaBall *mb)
{
- BKE_mball_batch_cache_dirty_tag(mb, BKE_MBALL_BATCH_DIRTY_ALL);
+ DEG_id_tag_update(&mb->id, ID_RECALC_SHADING);
}
#else
diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c
index 4810784b3f7..c5da15003e1 100644
--- a/source/blender/makesrna/intern/rna_modifier.c
+++ b/source/blender/makesrna/intern/rna_modifier.c
@@ -725,6 +725,11 @@ static void rna_Modifier_name_set(PointerRNA *ptr, const char *value)
BKE_animdata_fix_paths_rename_all(NULL, "modifiers", oldname, md->name);
}
+static void rna_Modifier_name_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UNUSED(ptr))
+{
+ DEG_relations_tag_update(bmain);
+}
+
static char *rna_Modifier_path(const PointerRNA *ptr)
{
const ModifierData *md = ptr->data;
@@ -7243,12 +7248,13 @@ void RNA_def_modifier(BlenderRNA *brna)
RNA_def_struct_refine_func(srna, "rna_Modifier_refine");
RNA_def_struct_path_func(srna, "rna_Modifier_path");
RNA_def_struct_sdna(srna, "ModifierData");
+ RNA_def_struct_ui_icon(srna, ICON_MODIFIER);
/* strings */
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Modifier_name_set");
RNA_def_property_ui_text(prop, "Name", "Modifier name");
- RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, NULL);
+ RNA_def_property_update(prop, NC_OBJECT | ND_MODIFIER | NA_RENAME, "rna_Modifier_name_update");
RNA_def_struct_name_property(srna, prop);
/* enums */
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index 386ef3f74a3..892d523bdcf 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -1267,6 +1267,20 @@ static void rna_NodeTree_active_node_set(PointerRNA *ptr,
if (node && BLI_findindex(&ntree->nodes, node) != -1) {
nodeSetActive(ntree, node);
+
+ /* Handle NODE_DO_OUTPUT as well. */
+ if (node->typeinfo->nclass == NODE_CLASS_OUTPUT && node->type != CMP_NODE_OUTPUT_FILE) {
+ /* If this node becomes the active output, the others of the same type can't be the active
+ * output anymore. */
+ LISTBASE_FOREACH (bNode *, other_node, &ntree->nodes) {
+ if (other_node->type == node->type) {
+ other_node->flag &= ~NODE_DO_OUTPUT;
+ }
+ }
+ node->flag |= NODE_DO_OUTPUT;
+ ntreeSetOutput(ntree);
+ BKE_ntree_update_tag_active_output_changed(ntree);
+ }
}
else {
nodeClearActive(ntree);
@@ -1878,7 +1892,7 @@ static void rna_Node_unregister(Main *UNUSED(bmain), StructRNA *type)
}
/* Generic internal registration function.
- * Can be used to implement callbacks for registerable RNA node subtypes.
+ * Can be used to implement callbacks for registerable RNA node sub-types.
*/
static bNodeType *rna_Node_register_base(Main *bmain,
ReportList *reports,
@@ -3571,7 +3585,7 @@ static void rna_difference_matte_t2_set(PointerRNA *ptr, float value)
chroma->t2 = value;
}
-/* Button Set Funcs for Matte Nodes */
+/* Button Set Functions for Matte Nodes */
static void rna_Matte_t1_set(PointerRNA *ptr, float value)
{
bNode *node = (bNode *)ptr->data;
@@ -4840,6 +4854,7 @@ static void def_math(StructRNA *srna)
RNA_def_property_enum_sdna(prop, NULL, "custom1");
RNA_def_property_enum_items(prop, rna_enum_node_math_items);
RNA_def_property_ui_text(prop, "Operation", "");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_NODETREE);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update");
prop = RNA_def_property(srna, "use_clamp", PROP_BOOLEAN, PROP_NONE);
@@ -4912,6 +4927,54 @@ static void def_compare(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_sh_mix(StructRNA *srna)
+{
+ static const EnumPropertyItem rna_enum_mix_data_type_items[] = {
+ {SOCK_FLOAT, "FLOAT", 0, "Float", ""},
+ {SOCK_VECTOR, "VECTOR", 0, "Vector", ""},
+ {SOCK_RGBA, "RGBA", 0, "Color", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem rna_enum_mix_mode_items[] = {
+ {NODE_MIX_MODE_UNIFORM, "UNIFORM", 0, "Uniform", "Use a single factor for all components"},
+ {NODE_MIX_MODE_NON_UNIFORM, "NON_UNIFORM", 0, "Non-Uniform", "Per component factor"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeShaderMix", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_mix_data_type_items);
+ RNA_def_property_enum_default(prop, SOCK_FLOAT);
+ 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, "factor_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, rna_enum_mix_mode_items);
+ RNA_def_property_enum_default(prop, NODE_MIX_MODE_UNIFORM);
+ RNA_def_property_ui_text(prop, "Factor Mode", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "blend_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "blend_type");
+ RNA_def_property_enum_items(prop, rna_enum_ramp_blend_items);
+ RNA_def_property_ui_text(prop, "Blending Mode", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "clamp_factor", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "clamp_factor", 1);
+ RNA_def_property_ui_text(prop, "Clamp Factor", "Clamp the factor to [0,1] range");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "clamp_result", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "clamp_result", 1);
+ RNA_def_property_ui_text(prop, "Clamp Result", "Clamp the result to [0,1] range");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_float_to_int(StructRNA *srna)
{
PropertyRNA *prop;
@@ -5282,7 +5345,6 @@ static void def_sh_tex_sky(StructRNA *srna)
prop = RNA_def_property(srna, "sun_elevation", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_ui_text(prop, "Sun Elevation", "Sun angle from horizon");
- RNA_def_property_range(prop, -M_PI_2, M_PI_2);
RNA_def_property_float_default(prop, M_PI_2);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
@@ -6478,7 +6540,7 @@ static void def_sh_script(StructRNA *srna)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "add_socket", "rna_ShaderNodeScript_add_socket");
- RNA_def_function_ui_description(func, "Add a socket socket");
+ RNA_def_function_ui_description(func, "Add a socket");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
parm = RNA_def_string(func, "name", NULL, 0, "Name", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
@@ -6489,7 +6551,7 @@ static void def_sh_script(StructRNA *srna)
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove_socket", "rna_ShaderNodeScript_remove_socket");
- RNA_def_function_ui_description(func, "Remove a socket socket");
+ RNA_def_function_ui_description(func, "Remove a socket");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
parm = RNA_def_pointer(func, "sock", "NodeSocket", "Socket", "");
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
@@ -6738,7 +6800,7 @@ static void def_cmp_set_alpha(StructRNA *srna)
"REPLACE_ALPHA",
0,
"Replace Alpha",
- "Replace the input image's alpha channels by the alpha input value"},
+ "Replace the input image's alpha channel by the alpha input value"},
{0, NULL, 0, NULL, NULL},
};
@@ -6998,6 +7060,7 @@ static void def_cmp_output_file(BlenderRNA *brna, StructRNA *srna)
prop = RNA_def_property(srna, "base_path", PROP_STRING, PROP_FILEPATH);
RNA_def_property_string_sdna(prop, NULL, "base_path");
RNA_def_property_ui_text(prop, "Base Path", "Base output path for the image");
+ RNA_def_property_flag(prop, PROP_PATH_OUTPUT);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "active_input_index", PROP_INT, PROP_NONE);
@@ -7046,10 +7109,10 @@ static void def_cmp_dilate_erode(StructRNA *srna)
PropertyRNA *prop;
static const EnumPropertyItem mode_items[] = {
- {CMP_NODE_DILATEERODE_STEP, "STEP", 0, "Step", ""},
- {CMP_NODE_DILATEERODE_DISTANCE_THRESH, "THRESHOLD", 0, "Threshold", ""},
- {CMP_NODE_DILATEERODE_DISTANCE, "DISTANCE", 0, "Distance", ""},
- {CMP_NODE_DILATEERODE_DISTANCE_FEATHER, "FEATHER", 0, "Feather", ""},
+ {CMP_NODE_DILATE_ERODE_STEP, "STEP", 0, "Step", ""},
+ {CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD, "THRESHOLD", 0, "Threshold", ""},
+ {CMP_NODE_DILATE_ERODE_DISTANCE, "DISTANCE", 0, "Distance", ""},
+ {CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER, "FEATHER", 0, "Feather", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -7066,7 +7129,7 @@ static void def_cmp_dilate_erode(StructRNA *srna)
RNA_def_property_ui_text(prop, "Distance", "Distance to grow/shrink (number of iterations)");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
- /* CMP_NODE_DILATEERODE_DISTANCE_THRESH only */
+ /* CMP_NODE_DILATE_ERODE_DISTANCE_THRESH only */
prop = RNA_def_property(srna, "edge", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "custom3");
RNA_def_property_range(prop, -100, 100);
@@ -7075,7 +7138,7 @@ static void def_cmp_dilate_erode(StructRNA *srna)
RNA_def_struct_sdna_from(srna, "NodeDilateErode", "storage");
- /* CMP_NODE_DILATEERODE_DISTANCE_FEATHER only */
+ /* CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER only */
prop = RNA_def_property(srna, "falloff", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "falloff");
RNA_def_property_enum_items(prop, rna_enum_proportional_falloff_curve_only_items);
@@ -7128,18 +7191,18 @@ static void def_cmp_scale(StructRNA *srna)
PropertyRNA *prop;
static const EnumPropertyItem space_items[] = {
- {CMP_SCALE_RELATIVE, "RELATIVE", 0, "Relative", ""},
- {CMP_SCALE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", ""},
- {CMP_SCALE_SCENEPERCENT, "SCENE_SIZE", 0, "Scene Size", ""},
- {CMP_SCALE_RENDERPERCENT, "RENDER_SIZE", 0, "Render Size", ""},
+ {CMP_NODE_SCALE_RELATIVE, "RELATIVE", 0, "Relative", ""},
+ {CMP_NODE_SCALE_ABSOLUTE, "ABSOLUTE", 0, "Absolute", ""},
+ {CMP_NODE_SCALE_RENDER_PERCENT, "SCENE_SIZE", 0, "Scene Size", ""},
+ {CMP_NODE_SCALE_RENDER_SIZE, "RENDER_SIZE", 0, "Render Size", ""},
{0, NULL, 0, NULL, NULL},
};
/* matching bgpic_camera_frame_items[] */
static const EnumPropertyItem space_frame_items[] = {
- {0, "STRETCH", 0, "Stretch", ""},
- {CMP_SCALE_RENDERSIZE_FRAME_ASPECT, "FIT", 0, "Fit", ""},
- {CMP_SCALE_RENDERSIZE_FRAME_ASPECT | CMP_SCALE_RENDERSIZE_FRAME_CROP, "CROP", 0, "Crop", ""},
+ {CMP_NODE_SCALE_RENDER_SIZE_STRETCH, "STRETCH", 0, "Stretch", ""},
+ {CMP_NODE_SCALE_RENDER_SIZE_FIT, "FIT", 0, "Fit", ""},
+ {CMP_NODE_SCALE_RENDER_SIZE_CROP, "CROP", 0, "Crop", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -8051,6 +8114,7 @@ static void def_cmp_lensdist(StructRNA *srna)
prop = RNA_def_property(srna, "use_jitter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "jit", 1);
RNA_def_property_ui_text(prop, "Jitter", "Enable/disable jittering (faster, but also noisier)");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_NODETREE);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
prop = RNA_def_property(srna, "use_fit", PROP_BOOLEAN, PROP_NONE);
@@ -9551,6 +9615,31 @@ static void def_geo_extrude_mesh(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_distribute_points_in_volume(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem mode_items[] = {
+ {GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM,
+ "DENSITY_RANDOM",
+ 0,
+ "Random",
+ "Distribute points randomly inside of the volume"},
+ {GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID,
+ "DENSITY_GRID",
+ 0,
+ "Grid",
+ "Distribute the points in a grid pattern inside of the volume"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryDistributePointsInVolume", "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, "Distribution Method", "Method to use for scattering points");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_distribute_points_on_faces(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10755,7 +10844,7 @@ static void def_geo_field_at_index(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update");
}
-static void def_geo_field_on_domain(StructRNA *srna)
+static void def_geo_interpolate_domain(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10949,6 +11038,11 @@ 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_unavailable", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SOCK_UNAVAIL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Unavailable", "True if the socket is unavailable");
+
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);
@@ -11533,6 +11627,8 @@ static void rna_def_node_socket_object(BlenderRNA *brna,
RNA_def_property_struct_type(prop, "Object");
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");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void rna_def_node_socket_image(BlenderRNA *brna,
@@ -11569,6 +11665,8 @@ static void rna_def_node_socket_image(BlenderRNA *brna,
RNA_def_property_struct_type(prop, "Image");
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");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void rna_def_node_socket_geometry(BlenderRNA *brna,
@@ -11620,6 +11718,8 @@ static void rna_def_node_socket_collection(BlenderRNA *brna,
RNA_def_property_struct_type(prop, "Collection");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void rna_def_node_socket_texture(BlenderRNA *brna,
@@ -11656,6 +11756,8 @@ static void rna_def_node_socket_texture(BlenderRNA *brna,
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");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void rna_def_node_socket_material(BlenderRNA *brna,
@@ -11696,6 +11798,8 @@ static void rna_def_node_socket_material(BlenderRNA *brna,
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");
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void rna_def_node_socket_standard_types(BlenderRNA *brna)
@@ -12390,7 +12494,7 @@ static void rna_def_nodetree_nodes_api(BlenderRNA *brna, PropertyRNA *cprop)
prop, "rna_NodeTree_active_node_get", "rna_NodeTree_active_node_set", NULL, NULL);
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_UNLINK);
RNA_def_property_ui_text(prop, "Active Node", "Active node in this tree");
- RNA_def_property_update(prop, NC_SCENE | ND_OB_ACTIVE, NULL);
+ RNA_def_property_update(prop, NC_SCENE | ND_OB_ACTIVE, "rna_NodeTree_update");
}
static void rna_def_nodetree_link_api(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index a68ef361f04..46a89d3a6e0 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -381,7 +381,7 @@ static void rna_MaterialIndex_update(Main *UNUSED(bmain), Scene *UNUSED(scene),
{
Object *ob = (Object *)ptr->owner_id;
if (ob && ob->type == OB_GPENCIL) {
- /* notifying material property in topbar */
+ /* Notifying material property in top-bar. */
WM_main_add_notifier(NC_SPACE | ND_SPACE_VIEW3D, NULL);
}
}
@@ -497,12 +497,6 @@ static void rna_Object_dependency_update(Main *bmain, Scene *UNUSED(scene), Poin
void rna_Object_data_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
- Object *object = (Object *)ptr->data;
-
- if (object->mode == OB_MODE_SCULPT) {
- BKE_sculpt_ensure_orig_mesh_data(scene, object);
- }
-
rna_Object_internal_update_data_dependency(bmain, scene, ptr);
}
@@ -607,11 +601,7 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr)
case OB_GPENCIL:
return &RNA_GreasePencil;
case OB_CURVES:
-# ifdef WITH_NEW_CURVES_TYPE
return &RNA_Curves;
-# else
- return &RNA_ID;
-# endif
case OB_POINTCLOUD:
return &RNA_PointCloud;
case OB_VOLUME:
@@ -691,6 +681,11 @@ static void rna_Object_parent_type_set(PointerRNA *ptr, int value)
{
Object *ob = (Object *)ptr->data;
+ /* Skip if type did not change (otherwise we loose parent inverse in ED_object_parent). */
+ if (ob->partype == value) {
+ return;
+ }
+
ED_object_parent(ob, ob->parent, value, ob->parsubstr);
}
@@ -2228,7 +2223,7 @@ bool rna_GPencil_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value)
return ((Object *)value.owner_id)->type == OB_GPENCIL;
}
-int rna_Object_use_dynamic_topology_sculpting_get(PointerRNA *ptr)
+bool rna_Object_use_dynamic_topology_sculpting_get(PointerRNA *ptr)
{
SculptSession *ss = ((Object *)ptr->owner_id)->sculpt;
return (ss && ss->bm);
@@ -2927,6 +2922,11 @@ static void rna_def_object_lineart(BlenderRNA *brna)
0,
"No Intersection",
"Include this object but do not generate intersection lines"},
+ {OBJECT_LRT_FORCE_INTERSECTION,
+ "FORCE_INTERSECTION",
+ 0,
+ "Force Intersection",
+ "Generate intersection lines even with objects that disabled intersection"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index 6967f78026a..c99c5fb723d 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -77,13 +77,36 @@ static const EnumPropertyItem space_items[] = {
# include "MEM_guardedalloc.h"
-static void rna_Object_select_set(
- Object *ob, bContext *C, ReportList *reports, bool select, ViewLayer *view_layer)
+static Base *find_view_layer_base_with_synced_ensure(
+ Object *ob, bContext *C, PointerRNA *view_layer_ptr, Scene **r_scene, ViewLayer **r_view_layer)
{
- if (view_layer == NULL) {
+ Scene *scene;
+ ViewLayer *view_layer;
+ if (view_layer_ptr->data) {
+ scene = (Scene *)view_layer_ptr->owner_id;
+ view_layer = view_layer_ptr->data;
+ }
+ else {
+ scene = CTX_data_scene(C);
view_layer = CTX_data_view_layer(C);
}
- Base *base = BKE_view_layer_base_find(view_layer, ob);
+ if (r_scene != NULL) {
+ *r_scene = scene;
+ }
+ if (r_view_layer != NULL) {
+ *r_view_layer = view_layer;
+ }
+
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ return BKE_view_layer_base_find(view_layer, ob);
+}
+
+static void rna_Object_select_set(
+ Object *ob, bContext *C, ReportList *reports, bool select, PointerRNA *view_layer_ptr)
+{
+ Scene *scene;
+ ViewLayer *view_layer;
+ Base *base = find_view_layer_base_with_synced_ensure(ob, C, view_layer_ptr, &scene, &view_layer);
if (!base) {
if (select) {
@@ -98,19 +121,14 @@ static void rna_Object_select_set(
ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT);
- Scene *scene = CTX_data_scene(C);
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, scene);
ED_outliner_select_sync_from_object_tag(C);
}
-static bool rna_Object_select_get(Object *ob, bContext *C, ViewLayer *view_layer)
+static bool rna_Object_select_get(Object *ob, bContext *C, PointerRNA *view_layer_ptr)
{
- if (view_layer == NULL) {
- view_layer = CTX_data_view_layer(C);
- }
- Base *base = BKE_view_layer_base_find(view_layer, ob);
-
+ Base *base = find_view_layer_base_with_synced_ensure(ob, C, view_layer_ptr, NULL, NULL);
if (!base) {
return false;
}
@@ -119,13 +137,11 @@ static bool rna_Object_select_get(Object *ob, bContext *C, ViewLayer *view_layer
}
static void rna_Object_hide_set(
- Object *ob, bContext *C, ReportList *reports, bool hide, ViewLayer *view_layer)
+ Object *ob, bContext *C, ReportList *reports, bool hide, PointerRNA *view_layer_ptr)
{
- if (view_layer == NULL) {
- view_layer = CTX_data_view_layer(C);
- }
- Base *base = BKE_view_layer_base_find(view_layer, ob);
-
+ Scene *scene;
+ ViewLayer *view_layer;
+ Base *base = find_view_layer_base_with_synced_ensure(ob, C, view_layer_ptr, &scene, &view_layer);
if (!base) {
if (hide) {
BKE_reportf(reports,
@@ -144,19 +160,14 @@ static void rna_Object_hide_set(
base->flag &= ~BASE_HIDDEN;
}
- Scene *scene = CTX_data_scene(C);
- BKE_layer_collection_sync(scene, view_layer);
+ BKE_view_layer_need_resync_tag(view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
}
-static bool rna_Object_hide_get(Object *ob, bContext *C, ViewLayer *view_layer)
+static bool rna_Object_hide_get(Object *ob, bContext *C, PointerRNA *view_layer_ptr)
{
- if (view_layer == NULL) {
- view_layer = CTX_data_view_layer(C);
- }
- Base *base = BKE_view_layer_base_find(view_layer, ob);
-
+ Base *base = find_view_layer_base_with_synced_ensure(ob, C, view_layer_ptr, NULL, NULL);
if (!base) {
return false;
}
@@ -164,15 +175,15 @@ static bool rna_Object_hide_get(Object *ob, bContext *C, ViewLayer *view_layer)
return ((base->flag & BASE_HIDDEN) != 0);
}
-static bool rna_Object_visible_get(Object *ob, bContext *C, ViewLayer *view_layer, View3D *v3d)
+static bool rna_Object_visible_get(Object *ob,
+ bContext *C,
+ PointerRNA *view_layer_ptr,
+ View3D *v3d)
{
- if (view_layer == NULL) {
- view_layer = CTX_data_view_layer(C);
- }
+ Base *base = find_view_layer_base_with_synced_ensure(ob, C, view_layer_ptr, NULL, NULL);
if (v3d == NULL) {
v3d = CTX_wm_view3d(C);
}
- Base *base = BKE_view_layer_base_find(view_layer, ob);
if (!base) {
return false;
@@ -181,13 +192,9 @@ static bool rna_Object_visible_get(Object *ob, bContext *C, ViewLayer *view_laye
return BASE_VISIBLE(v3d, base);
}
-static bool rna_Object_holdout_get(Object *ob, bContext *C, ViewLayer *view_layer)
+static bool rna_Object_holdout_get(Object *ob, bContext *C, PointerRNA *view_layer_ptr)
{
- if (view_layer == NULL) {
- view_layer = CTX_data_view_layer(C);
- }
- Base *base = BKE_view_layer_base_find(view_layer, ob);
-
+ Base *base = find_view_layer_base_with_synced_ensure(ob, C, view_layer_ptr, NULL, NULL);
if (!base) {
return false;
}
@@ -195,17 +202,9 @@ static bool rna_Object_holdout_get(Object *ob, bContext *C, ViewLayer *view_laye
return ((base->flag & BASE_HOLDOUT) != 0);
}
-static bool rna_Object_indirect_only_get(Object *ob, bContext *C, ViewLayer *view_layer)
+static bool rna_Object_indirect_only_get(Object *ob, bContext *C, PointerRNA *view_layer_ptr)
{
- if (view_layer == NULL) {
- view_layer = CTX_data_view_layer(C);
- }
- Base *base = BKE_view_layer_base_find(view_layer, ob);
-
- if (!base) {
- return false;
- }
-
+ Base *base = find_view_layer_base_with_synced_ensure(ob, C, view_layer_ptr, NULL, NULL);
return ((base->flag & BASE_INDIRECT_ONLY) != 0);
}
@@ -227,6 +226,7 @@ static Base *rna_Object_local_view_property_helper(bScreen *screen,
view_layer = WM_window_get_active_view_layer(win);
}
+ BKE_view_layer_synced_ensure(win ? WM_window_get_active_scene(win) : NULL, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base == NULL) {
BKE_reportf(
@@ -831,6 +831,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(
func, "view_layer", "ViewLayer", "", "Use this instead of the active view layer");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
parm = RNA_def_boolean(func, "result", 0, "", "Object selected");
RNA_def_function_return(func, parm);
@@ -842,6 +843,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_pointer(
func, "view_layer", "ViewLayer", "", "Use this instead of the active view layer");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
func = RNA_def_function(srna, "hide_get", "rna_Object_hide_get");
RNA_def_function_ui_description(
@@ -850,6 +852,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(
func, "view_layer", "ViewLayer", "", "Use this instead of the active view layer");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
parm = RNA_def_boolean(func, "result", 0, "", "Object hidden");
RNA_def_function_return(func, parm);
@@ -861,6 +864,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_pointer(
func, "view_layer", "ViewLayer", "", "Use this instead of the active view layer");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
func = RNA_def_function(srna, "visible_get", "rna_Object_visible_get");
RNA_def_function_ui_description(func,
@@ -869,6 +873,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(
func, "view_layer", "ViewLayer", "", "Use this instead of the active view layer");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
parm = RNA_def_pointer(
func, "viewport", "SpaceView3D", "", "Use this instead of the active 3D viewport");
parm = RNA_def_boolean(func, "result", 0, "", "Object visible");
@@ -879,6 +884,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(
func, "view_layer", "ViewLayer", "", "Use this instead of the active view layer");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
parm = RNA_def_boolean(func, "result", 0, "", "Object holdout");
RNA_def_function_return(func, parm);
@@ -889,6 +895,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
parm = RNA_def_pointer(
func, "view_layer", "ViewLayer", "", "Use this instead of the active view layer");
+ RNA_def_parameter_flags(parm, 0, PARM_RNAPTR);
parm = RNA_def_boolean(func, "result", 0, "", "Object indirect only");
RNA_def_function_return(func, parm);
diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c
index 2ed539aa511..40000a49f03 100644
--- a/source/blender/makesrna/intern/rna_object_force.c
+++ b/source/blender/makesrna/intern/rna_object_force.c
@@ -1276,7 +1276,7 @@ static void rna_def_effector_weight(BlenderRNA *brna)
prop = RNA_def_property(srna, "collection", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Collection");
RNA_def_property_pointer_sdna(prop, NULL, "group");
- RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
RNA_def_property_ui_text(prop, "Effector Collection", "Limit effectors to this collection");
RNA_def_property_update(prop, 0, "rna_EffectorWeight_dependency_update");
diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c
index 3fc98d769b6..40e7f6e65c2 100644
--- a/source/blender/makesrna/intern/rna_particle.c
+++ b/source/blender/makesrna/intern/rna_particle.c
@@ -212,10 +212,10 @@ static void rna_ParticleHairKey_location_object_get(PointerRNA *ptr, float *valu
if (pa) {
Mesh *hair_mesh = (psmd->psys->flag & PSYS_HAIR_DYNAMICS) ? psmd->psys->hair_out_mesh : NULL;
-
+ const MVert *verts = BKE_mesh_verts(hair_mesh);
if (hair_mesh) {
- MVert *mvert = &hair_mesh->mvert[pa->hair_index + (hkey - pa->hair)];
- copy_v3_v3(values, mvert->co);
+ const MVert *mv = &verts[pa->hair_index + (hkey - pa->hair)];
+ copy_v3_v3(values, mv->co);
}
else {
float hairmat[4][4];
@@ -279,9 +279,9 @@ static void hair_key_location_object_set(HairKey *hair_key,
if (hair_key_index == -1) {
return;
}
-
- MVert *mvert = &hair_mesh->mvert[particle->hair_index + (hair_key_index)];
- copy_v3_v3(mvert->co, src_co);
+ MVert *verts = BKE_mesh_verts_for_write(hair_mesh);
+ MVert *mv = &verts[particle->hair_index + (hair_key_index)];
+ copy_v3_v3(mv->co, src_co);
return;
}
@@ -324,8 +324,9 @@ static void rna_ParticleHairKey_co_object(HairKey *hairkey,
NULL;
if (particle) {
if (hair_mesh) {
- MVert *mvert = &hair_mesh->mvert[particle->hair_index + (hairkey - particle->hair)];
- copy_v3_v3(n_co, mvert->co);
+ const MVert *verts = BKE_mesh_verts(hair_mesh);
+ const MVert *mv = &verts[particle->hair_index + (hairkey - particle->hair)];
+ copy_v3_v3(n_co, mv->co);
}
else {
float hairmat[4][4];
@@ -402,8 +403,8 @@ static void rna_Particle_uv_on_emitter(ParticleData *particle,
MFace *mface;
MTFace *mtface;
- mface = modifier->mesh_final->mface;
- mtface = modifier->mesh_final->mtface;
+ mface = CustomData_get_layer(&modifier->mesh_final->fdata, CD_MFACE);
+ mtface = CustomData_get_layer(&modifier->mesh_final->fdata, CD_MTFACE);
if (mface && mtface) {
mtface += num;
@@ -567,7 +568,7 @@ static int rna_ParticleSystem_tessfaceidx_on_emitter(ParticleSystem *particlesys
}
else if (part->from == PART_FROM_VERT) {
if (num != DMCACHE_NOTFOUND && num < totvert) {
- MFace *mface = modifier->mesh_final->mface;
+ MFace *mface = CustomData_get_layer(&modifier->mesh_final->fdata, CD_MFACE);
*r_fuv = &particle->fuv;
@@ -610,7 +611,7 @@ static int rna_ParticleSystem_tessfaceidx_on_emitter(ParticleSystem *particlesys
}
else if (part->from == PART_FROM_VERT) {
if (num != DMCACHE_NOTFOUND && num < totvert) {
- MFace *mface = modifier->mesh_final->mface;
+ MFace *mface = CustomData_get_layer(&modifier->mesh_final->fdata, CD_MFACE);
*r_fuv = &parent->fuv;
@@ -660,7 +661,8 @@ static void rna_ParticleSystem_uv_on_emitter(ParticleSystem *particlesystem,
zero_v2(r_uv);
}
else {
- MFace *mface = &modifier->mesh_final->mface[num];
+ MFace *mfaces = CustomData_get_layer(&modifier->mesh_final->fdata, CD_MFACE);
+ MFace *mface = &mfaces[num];
const MTFace *mtface = (const MTFace *)CustomData_get_layer_n(
&modifier->mesh_final->fdata, CD_MTFACE, uv_no);
@@ -694,7 +696,8 @@ static void rna_ParticleSystem_mcol_on_emitter(ParticleSystem *particlesystem,
zero_v3(r_mcol);
}
else {
- MFace *mface = &modifier->mesh_final->mface[num];
+ MFace *mfaces = CustomData_get_layer(&modifier->mesh_final->fdata, CD_MFACE);
+ MFace *mface = &mfaces[num];
const MCol *mc = (const MCol *)CustomData_get_layer_n(
&modifier->mesh_final->fdata, CD_MCOL, vcol_no);
MCol mcol;
@@ -1044,7 +1047,7 @@ static float rna_PartSetting_linelenhead_get(struct PointerRNA *ptr)
return settings->draw_line[1];
}
-static int rna_PartSettings_is_fluid_get(PointerRNA *ptr)
+static bool rna_PartSettings_is_fluid_get(PointerRNA *ptr)
{
ParticleSettings *part = ptr->data;
return (ELEM(part->type,
@@ -2686,6 +2689,7 @@ static void rna_def_particle_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_parent_particles", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "draw", PART_DRAW_PARENT);
RNA_def_property_ui_text(prop, "Parents", "Render parent particles");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_PARTICLESETTINGS);
RNA_def_property_update(prop, 0, "rna_Particle_redo");
prop = RNA_def_property(srna, "show_number", PROP_BOOLEAN, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc
new file mode 100644
index 00000000000..96f46f5dbe6
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_path.cc
@@ -0,0 +1,1364 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/** \file
+ * \ingroup RNA
+ */
+
+#include <cstdlib>
+#include <stdlib.h>
+#include <string.h>
+
+#include "BLI_alloca.h"
+#include "BLI_dynstr.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_idprop.h"
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+
+#include "DNA_ID.h" /* For ID properties. */
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_path.h"
+#include "RNA_prototypes.h"
+
+#include "rna_access_internal.h"
+#include "rna_internal.h"
+
+/**
+ * Extract the first token from `path`.
+ *
+ * \param path: Extract the token from path, step the pointer to the beginning of the next token
+ * \return The nil terminated token.
+ */
+static char *rna_path_token(const char **path, char *fixedbuf, int fixedlen)
+{
+ int len = 0;
+
+ /* Get data until `.` or `[`. */
+ const char *p = *path;
+ while (*p && !ELEM(*p, '.', '[')) {
+ len++;
+ p++;
+ }
+
+ /* Empty, return. */
+ if (UNLIKELY(len == 0)) {
+ return nullptr;
+ }
+
+ /* Try to use fixed buffer if possible. */
+ char *buf = (len + 1 < fixedlen) ? fixedbuf :
+ (char *)MEM_mallocN(sizeof(char) * (len + 1), __func__);
+ memcpy(buf, *path, sizeof(char) * len);
+ buf[len] = '\0';
+
+ if (*p == '.') {
+ p++;
+ }
+ *path = p;
+
+ return buf;
+}
+
+/**
+ * Extract the first token in brackets from `path` (with quoted text support).
+ *
+ * - `[0]` -> `0`
+ * - `["Some\"Quote"]` -> `Some"Quote`
+ *
+ * \param path: Extract the token from path, step the pointer to the beginning of the next token
+ * (past quoted text and brackets).
+ * \return The nil terminated token.
+ */
+static char *rna_path_token_in_brackets(const char **path,
+ char *fixedbuf,
+ int fixedlen,
+ bool *r_quoted)
+{
+ int len = 0;
+ bool quoted = false;
+
+ BLI_assert(r_quoted != nullptr);
+
+ /* Get data between `[]`, check escaping quotes and back-slashes with #BLI_str_unescape. */
+ if (UNLIKELY(**path != '[')) {
+ return nullptr;
+ }
+
+ (*path)++;
+ const char *p = *path;
+
+ /* 2 kinds of look-ups now, quoted or unquoted. */
+ if (*p == '"') {
+ /* Find the matching quote. */
+ (*path)++;
+ p = *path;
+ const char *p_end = BLI_str_escape_find_quote(p);
+ if (p_end == nullptr) {
+ /* No Matching quote. */
+ return nullptr;
+ }
+ /* Exclude the last quote from the length. */
+ len += (p_end - p);
+
+ /* Skip the last quoted char to get the `]`. */
+ p_end += 1;
+ p = p_end;
+ quoted = true;
+ }
+ else {
+ /* Find the matching bracket. */
+ while (*p && (*p != ']')) {
+ len++;
+ p++;
+ }
+ }
+
+ if (UNLIKELY(*p != ']')) {
+ return nullptr;
+ }
+
+ /* Empty, return. */
+ if (UNLIKELY(len == 0)) {
+ return nullptr;
+ }
+
+ /* Try to use fixed buffer if possible. */
+ char *buf = (len + 1 < fixedlen) ? fixedbuf :
+ (char *)MEM_mallocN(sizeof(char) * (len + 1), __func__);
+
+ /* Copy string, taking into account escaped ']' */
+ if (quoted) {
+ BLI_str_unescape(buf, *path, len);
+ /* +1 to step over the last quote. */
+ BLI_assert((*path)[len] == '"');
+ p = (*path) + len + 1;
+ }
+ else {
+ memcpy(buf, *path, sizeof(char) * len);
+ buf[len] = '\0';
+ }
+ /* Set path to start of next token. */
+ if (*p == ']') {
+ p++;
+ }
+ if (*p == '.') {
+ p++;
+ }
+ *path = p;
+
+ *r_quoted = quoted;
+
+ return buf;
+}
+
+/**
+ * \return true when the key in the path is correctly parsed and found in the collection
+ * or when the path is empty.
+ */
+static bool rna_path_parse_collection_key(const char **path,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ PointerRNA *r_nextptr)
+{
+ char fixedbuf[256];
+ int intkey;
+
+ *r_nextptr = *ptr;
+
+ /* end of path, ok */
+ if (!(**path)) {
+ return true;
+ }
+
+ bool found = false;
+ if (**path == '[') {
+ bool quoted;
+ char *token;
+
+ /* resolve the lookup with [] brackets */
+ token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), &quoted);
+
+ if (!token) {
+ return false;
+ }
+
+ /* check for "" to see if it is a string */
+ if (quoted) {
+ if (RNA_property_collection_lookup_string(ptr, prop, token, r_nextptr)) {
+ found = true;
+ }
+ else {
+ r_nextptr->data = nullptr;
+ }
+ }
+ else {
+ /* otherwise do int lookup */
+ intkey = atoi(token);
+ if (intkey == 0 && (token[0] != '0' || token[1] != '\0')) {
+ return false; /* we can be sure the fixedbuf was used in this case */
+ }
+ if (RNA_property_collection_lookup_int(ptr, prop, intkey, r_nextptr)) {
+ found = true;
+ }
+ else {
+ r_nextptr->data = nullptr;
+ }
+ }
+
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+ }
+ else {
+ if (RNA_property_collection_type_get(ptr, prop, r_nextptr)) {
+ found = true;
+ }
+ else {
+ /* ensure we quit on invalid values */
+ r_nextptr->data = nullptr;
+ }
+ }
+
+ return found;
+}
+
+static bool rna_path_parse_array_index(const char **path,
+ PointerRNA *ptr,
+ PropertyRNA *prop,
+ int *r_index)
+{
+ char fixedbuf[256];
+ int index_arr[RNA_MAX_ARRAY_DIMENSION] = {0};
+ int len[RNA_MAX_ARRAY_DIMENSION];
+ const int dim = RNA_property_array_dimension(ptr, prop, len);
+ int i;
+
+ *r_index = -1;
+
+ /* end of path, ok */
+ if (!(**path)) {
+ return true;
+ }
+
+ for (i = 0; i < dim; i++) {
+ int temp_index = -1;
+ char *token;
+
+ /* multi index resolve */
+ if (**path == '[') {
+ bool quoted;
+ token = rna_path_token_in_brackets(path, fixedbuf, sizeof(fixedbuf), &quoted);
+
+ if (token == nullptr) {
+ /* invalid syntax blah[] */
+ return false;
+ }
+ /* check for "" to see if it is a string */
+ if (quoted) {
+ temp_index = RNA_property_array_item_index(prop, *token);
+ }
+ else {
+ /* otherwise do int lookup */
+ temp_index = atoi(token);
+
+ if (temp_index == 0 && (token[0] != '0' || token[1] != '\0')) {
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ return false;
+ }
+ }
+ }
+ else if (dim == 1) {
+ /* location.x || scale.X, single dimension arrays only */
+ token = rna_path_token(path, fixedbuf, sizeof(fixedbuf));
+ if (token == nullptr) {
+ /* invalid syntax blah. */
+ return false;
+ }
+ temp_index = RNA_property_array_item_index(prop, *token);
+ }
+ else {
+ /* just to avoid uninitialized pointer use */
+ token = fixedbuf;
+ }
+
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ /* out of range */
+ if (temp_index < 0 || temp_index >= len[i]) {
+ return false;
+ }
+
+ index_arr[i] = temp_index;
+ /* end multi index resolve */
+ }
+
+ /* arrays always contain numbers so further values are not valid */
+ if (**path) {
+ return false;
+ }
+
+ /* flatten index over all dimensions */
+ {
+ int totdim = 1;
+ int flat_index = 0;
+
+ for (i = dim - 1; i >= 0; i--) {
+ flat_index += index_arr[i] * totdim;
+ totdim *= len[i];
+ }
+
+ *r_index = flat_index;
+ }
+ return true;
+}
+
+/**
+ * Generic rna path parser.
+ *
+ * \note All parameters besides \a ptr and \a path are optional.
+ *
+ * \param ptr: The root of given RNA path.
+ * \param path: The RNA path.
+ * \param r_ptr: The final RNA data holding the last property in \a path.
+ * \param r_prop: The final property of \a r_ptr, from \a path.
+ * \param r_index: The final index in the \a r_prop, if defined by \a path.
+ * \param r_item_ptr: Only valid for Pointer and Collection, return the actual value of the
+ * pointer, or of the collection item.
+ * Mutually exclusive with \a eval_pointer option.
+ * \param r_elements: A list of \a PropertyElemRNA items(pairs of \a PointerRNA, \a PropertyRNA
+ * that represent the whole given \a path).
+ * \param eval_pointer: If \a true, and \a path leads to a Pointer property, or an item in a
+ * Collection property, \a r_ptr will be set to the value of that property,
+ * and \a r_prop will be NULL.
+ * Mutually exclusive with \a r_item_ptr.
+ *
+ * \return \a true on success, \a false if the path is somehow invalid.
+ */
+static bool rna_path_parse(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index,
+ PointerRNA *r_item_ptr,
+ ListBase *r_elements,
+ const bool eval_pointer)
+{
+ BLI_assert(r_item_ptr == nullptr || !eval_pointer);
+ PropertyRNA *prop;
+ PointerRNA curptr, nextptr;
+ PropertyElemRNA *prop_elem = nullptr;
+ int index = -1;
+ char fixedbuf[256];
+ int type;
+ const bool do_item_ptr = r_item_ptr != nullptr && !eval_pointer;
+
+ if (do_item_ptr) {
+ RNA_POINTER_INVALIDATE(&nextptr);
+ }
+
+ prop = nullptr;
+ curptr = *ptr;
+
+ if (path == nullptr || *path == '\0') {
+ return false;
+ }
+
+ while (*path) {
+ if (do_item_ptr) {
+ RNA_POINTER_INVALIDATE(&nextptr);
+ }
+
+ const bool use_id_prop = (*path == '[');
+ /* Custom property lookup: e.g. `C.object["someprop"]`. */
+
+ if (!curptr.data) {
+ return false;
+ }
+
+ /* look up property name in current struct */
+ bool quoted = false;
+ char *token = use_id_prop ?
+ rna_path_token_in_brackets(&path, fixedbuf, sizeof(fixedbuf), &quoted) :
+ rna_path_token(&path, fixedbuf, sizeof(fixedbuf));
+ if (!token) {
+ return false;
+ }
+
+ prop = nullptr;
+ if (use_id_prop) { /* look up property name in current struct */
+ IDProperty *group = RNA_struct_idprops(&curptr, 0);
+ if (group && quoted) {
+ prop = (PropertyRNA *)IDP_GetPropertyFromGroup(group, token);
+ }
+ }
+ else {
+ prop = RNA_struct_find_property(&curptr, token);
+ }
+
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ if (!prop) {
+ return false;
+ }
+
+ if (r_elements) {
+ prop_elem = MEM_cnew<PropertyElemRNA>(__func__);
+ prop_elem->ptr = curptr;
+ prop_elem->prop = prop;
+ prop_elem->index = -1; /* index will be added later, if needed. */
+ BLI_addtail(r_elements, prop_elem);
+ }
+
+ type = RNA_property_type(prop);
+
+ /* now look up the value of this property if it is a pointer or
+ * collection, otherwise return the property rna so that the
+ * caller can read the value of the property itself */
+ switch (type) {
+ case PROP_POINTER: {
+ /* resolve pointer if further path elements follow
+ * or explicitly requested
+ */
+ if (do_item_ptr || eval_pointer || *path != '\0') {
+ nextptr = RNA_property_pointer_get(&curptr, prop);
+ }
+
+ if (eval_pointer || *path != '\0') {
+ curptr = nextptr;
+ prop = nullptr; /* now we have a PointerRNA, the prop is our parent so forget it */
+ index = -1;
+ }
+ break;
+ }
+ case PROP_COLLECTION: {
+ /* Resolve pointer if further path elements follow.
+ * Note that if path is empty, rna_path_parse_collection_key will do nothing anyway,
+ * so do_item_ptr is of no use in that case.
+ */
+ if (*path) {
+ if (!rna_path_parse_collection_key(&path, &curptr, prop, &nextptr)) {
+ return false;
+ }
+
+ if (eval_pointer || *path != '\0') {
+ curptr = nextptr;
+ prop = nullptr; /* now we have a PointerRNA, the prop is our parent so forget it */
+ index = -1;
+ }
+ }
+ break;
+ }
+ default:
+ if (r_index || prop_elem) {
+ if (!rna_path_parse_array_index(&path, &curptr, prop, &index)) {
+ return false;
+ }
+
+ if (prop_elem) {
+ prop_elem->index = index;
+ }
+ }
+ break;
+ }
+ }
+
+ if (r_ptr) {
+ *r_ptr = curptr;
+ }
+ if (r_prop) {
+ *r_prop = prop;
+ }
+ if (r_index) {
+ *r_index = index;
+ }
+ if (r_item_ptr && do_item_ptr) {
+ *r_item_ptr = nextptr;
+ }
+
+ if (prop_elem && (prop_elem->ptr.data != curptr.data || prop_elem->prop != prop ||
+ prop_elem->index != index)) {
+ prop_elem = MEM_cnew<PropertyElemRNA>(__func__);
+ prop_elem->ptr = curptr;
+ prop_elem->prop = prop;
+ prop_elem->index = index;
+ BLI_addtail(r_elements, prop_elem);
+ }
+
+ return true;
+}
+
+bool RNA_path_resolve(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, nullptr, nullptr, nullptr, true)) {
+ return false;
+ }
+
+ return r_ptr->data != nullptr;
+}
+
+bool RNA_path_resolve_full(
+ const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, nullptr, nullptr, true)) {
+ return false;
+ }
+
+ return r_ptr->data != nullptr;
+}
+
+bool RNA_path_resolve_full_maybe_null(
+ const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
+{
+ return rna_path_parse(ptr, path, r_ptr, r_prop, r_index, nullptr, nullptr, true);
+}
+
+bool RNA_path_resolve_property(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, nullptr, nullptr, nullptr, false)) {
+ return false;
+ }
+
+ return r_ptr->data != nullptr && *r_prop != nullptr;
+}
+
+bool RNA_path_resolve_property_full(
+ const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, nullptr, nullptr, false)) {
+ return false;
+ }
+
+ return r_ptr->data != nullptr && *r_prop != nullptr;
+}
+
+bool RNA_path_resolve_property_and_item_pointer(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ PointerRNA *r_item_ptr)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, nullptr, r_item_ptr, nullptr, false)) {
+ return false;
+ }
+
+ return r_ptr->data != nullptr && *r_prop != nullptr;
+}
+
+bool RNA_path_resolve_property_and_item_pointer_full(const PointerRNA *ptr,
+ const char *path,
+ PointerRNA *r_ptr,
+ PropertyRNA **r_prop,
+ int *r_index,
+ PointerRNA *r_item_ptr)
+{
+ if (!rna_path_parse(ptr, path, r_ptr, r_prop, r_index, r_item_ptr, nullptr, false)) {
+ return false;
+ }
+
+ return r_ptr->data != nullptr && *r_prop != nullptr;
+}
+bool RNA_path_resolve_elements(PointerRNA *ptr, const char *path, ListBase *r_elements)
+{
+ return rna_path_parse(ptr, path, nullptr, nullptr, nullptr, nullptr, r_elements, false);
+}
+
+char *RNA_path_append(const char *path,
+ const PointerRNA *UNUSED(ptr),
+ PropertyRNA *prop,
+ int intkey,
+ const char *strkey)
+{
+ DynStr *dynstr;
+ char *result;
+
+ dynstr = BLI_dynstr_new();
+
+ /* add .identifier */
+ if (path) {
+ BLI_dynstr_append(dynstr, path);
+ if (*path) {
+ BLI_dynstr_append(dynstr, ".");
+ }
+ }
+
+ BLI_dynstr_append(dynstr, RNA_property_identifier(prop));
+
+ const bool has_key = (intkey > -1) || (strkey != nullptr);
+ if (has_key && (RNA_property_type(prop) == PROP_COLLECTION)) {
+ /* add ["strkey"] or [intkey] */
+ BLI_dynstr_append(dynstr, "[");
+
+ if (strkey) {
+ const int strkey_esc_max_size = (strlen(strkey) * 2) + 1;
+ char *strkey_esc = static_cast<char *>(BLI_array_alloca(strkey_esc, strkey_esc_max_size));
+ BLI_str_escape(strkey_esc, strkey, strkey_esc_max_size);
+ BLI_dynstr_append(dynstr, "\"");
+ BLI_dynstr_append(dynstr, strkey_esc);
+ BLI_dynstr_append(dynstr, "\"");
+ }
+ else {
+ char appendstr[128];
+ BLI_snprintf(appendstr, sizeof(appendstr), "%d", intkey);
+ BLI_dynstr_append(dynstr, appendstr);
+ }
+
+ BLI_dynstr_append(dynstr, "]");
+ }
+
+ result = BLI_dynstr_get_cstring(dynstr);
+ BLI_dynstr_free(dynstr);
+
+ return result;
+}
+
+/* Having both path append & back seems like it could be useful,
+ * this function isn't used at the moment. */
+static UNUSED_FUNCTION_WITH_RETURN_TYPE(char *, RNA_path_back)(const char *path)
+{
+ char fixedbuf[256];
+ const char *previous, *current;
+ char *result;
+ int i;
+
+ if (!path) {
+ return nullptr;
+ }
+
+ previous = nullptr;
+ current = path;
+
+ /* parse token by token until the end, then we back up to the previous
+ * position and strip of the next token to get the path one step back */
+ while (*current) {
+ char *token;
+
+ token = rna_path_token(&current, fixedbuf, sizeof(fixedbuf));
+
+ if (!token) {
+ return nullptr;
+ }
+ if (token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ /* in case of collection we also need to strip off [] */
+ bool quoted;
+ token = rna_path_token_in_brackets(&current, fixedbuf, sizeof(fixedbuf), &quoted);
+ if (token && token != fixedbuf) {
+ MEM_freeN(token);
+ }
+
+ if (!*current) {
+ break;
+ }
+
+ previous = current;
+ }
+
+ if (!previous) {
+ return nullptr;
+ }
+
+ /* copy and strip off last token */
+ i = previous - path;
+ result = BLI_strdup(path);
+
+ if (i > 0 && result[i - 1] == '.') {
+ i--;
+ }
+ result[i] = 0;
+
+ return result;
+}
+
+const char *RNA_path_array_index_token_find(const char *rna_path, const PropertyRNA *array_prop)
+{
+ if (array_prop != nullptr) {
+ if (!ELEM(array_prop->type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) {
+ BLI_assert(array_prop->arraydimension == 0);
+ return nullptr;
+ }
+ if (array_prop->arraydimension == 0) {
+ return nullptr;
+ }
+ }
+
+ /* Valid 'array part' of a rna path can only have '[', ']' and digit characters.
+ * It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */
+ if (UNLIKELY(rna_path[0] == '\0')) {
+ return nullptr;
+ }
+ size_t rna_path_len = (size_t)strlen(rna_path) - 1;
+ if (rna_path[rna_path_len] != ']') {
+ return nullptr;
+ }
+
+ const char *last_valid_index_token_start = nullptr;
+ while (rna_path_len--) {
+ switch (rna_path[rna_path_len]) {
+ case '[':
+ if (rna_path_len <= 0 || rna_path[rna_path_len - 1] != ']') {
+ return &rna_path[rna_path_len];
+ }
+ last_valid_index_token_start = &rna_path[rna_path_len];
+ rna_path_len--;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ break;
+ default:
+ return last_valid_index_token_start;
+ }
+ }
+ return last_valid_index_token_start;
+}
+
+/* generic path search func
+ * if its needed this could also reference the IDProperty direct */
+typedef struct IDP_Chain {
+ struct IDP_Chain *up; /* parent member, reverse and set to child for path conversion. */
+
+ const char *name;
+ int index;
+
+} IDP_Chain;
+
+static char *rna_idp_path_create(IDP_Chain *child_link)
+{
+ DynStr *dynstr = BLI_dynstr_new();
+ char *path;
+ bool is_first = true;
+
+ int tot = 0;
+ IDP_Chain *link = child_link;
+
+ /* reverse the list */
+ IDP_Chain *link_prev;
+ link_prev = nullptr;
+ while (link) {
+ IDP_Chain *link_next = link->up;
+ link->up = link_prev;
+ link_prev = link;
+ link = link_next;
+ tot++;
+ }
+
+ for (link = link_prev; link; link = link->up) {
+ /* pass */
+ if (link->index >= 0) {
+ BLI_dynstr_appendf(dynstr, is_first ? "%s[%d]" : ".%s[%d]", link->name, link->index);
+ }
+ else {
+ BLI_dynstr_appendf(dynstr, is_first ? "%s" : ".%s", link->name);
+ }
+
+ is_first = false;
+ }
+
+ path = BLI_dynstr_get_cstring(dynstr);
+ BLI_dynstr_free(dynstr);
+
+ if (*path == '\0') {
+ MEM_freeN(path);
+ path = nullptr;
+ }
+
+ return path;
+}
+
+static char *rna_idp_path(PointerRNA *ptr,
+ IDProperty *haystack,
+ IDProperty *needle,
+ IDP_Chain *parent_link)
+{
+ char *path = nullptr;
+ IDP_Chain link;
+
+ IDProperty *iter;
+ int i;
+
+ BLI_assert(haystack->type == IDP_GROUP);
+
+ link.up = parent_link;
+ /* Always set both name and index, else a stale value might get used. */
+ link.name = nullptr;
+ link.index = -1;
+
+ for (i = 0, iter = static_cast<IDProperty *>(haystack->data.group.first); iter;
+ iter = iter->next, i++) {
+ if (needle == iter) { /* found! */
+ link.name = iter->name;
+ link.index = -1;
+ path = rna_idp_path_create(&link);
+ break;
+ }
+
+ /* Early out in case the IDProperty type cannot contain RNA properties. */
+ if (!ELEM(iter->type, IDP_GROUP, IDP_IDPARRAY)) {
+ continue;
+ }
+
+ /* Ensure this is RNA. */
+ /* NOTE: `iter` might be a fully user-defined IDProperty (a.k.a. custom data), which name
+ * collides with an actual fully static RNA property of the same struct (which would then not
+ * be flagged with `PROP_IDPROPERTY`).
+ *
+ * That case must be ignored here, we only want to deal with runtime RNA properties stored in
+ * IDProps.
+ *
+ * See T84091. */
+ PropertyRNA *prop = RNA_struct_find_property(ptr, iter->name);
+ if (prop == nullptr || (prop->flag & PROP_IDPROPERTY) == 0) {
+ continue;
+ }
+
+ if (iter->type == IDP_GROUP) {
+ if (prop->type == PROP_POINTER) {
+ PointerRNA child_ptr = RNA_property_pointer_get(ptr, prop);
+ if (RNA_pointer_is_null(&child_ptr)) {
+ /* Pointer ID prop might be a 'leaf' in the IDProp group hierarchy, in which case a NULL
+ * value is perfectly valid. Just means it won't match the searched needle. */
+ continue;
+ }
+
+ link.name = iter->name;
+ link.index = -1;
+ if ((path = rna_idp_path(&child_ptr, iter, needle, &link))) {
+ break;
+ }
+ }
+ }
+ else if (iter->type == IDP_IDPARRAY) {
+ if (prop->type == PROP_COLLECTION) {
+ IDProperty *array = IDP_IDPArray(iter);
+ if (needle >= array && needle < (iter->len + array)) { /* found! */
+ link.name = iter->name;
+ link.index = (int)(needle - array);
+ path = rna_idp_path_create(&link);
+ break;
+ }
+ int j;
+ link.name = iter->name;
+ for (j = 0; j < iter->len; j++, array++) {
+ PointerRNA child_ptr;
+ if (RNA_property_collection_lookup_int(ptr, prop, j, &child_ptr)) {
+ if (RNA_pointer_is_null(&child_ptr)) {
+ /* Array item ID prop might be a 'leaf' in the IDProp group hierarchy, in which case
+ * a NULL value is perfectly valid. Just means it won't match the searched needle. */
+ continue;
+ }
+ link.index = j;
+ if ((path = rna_idp_path(&child_ptr, array, needle, &link))) {
+ break;
+ }
+ }
+ }
+ if (path) {
+ break;
+ }
+ }
+ }
+ }
+
+ return path;
+}
+
+char *RNA_path_from_struct_to_idproperty(PointerRNA *ptr, IDProperty *needle)
+{
+ IDProperty *haystack = RNA_struct_idprops(ptr, false);
+
+ if (haystack) { /* can fail when called on bones */
+ return rna_idp_path(ptr, haystack, needle, nullptr);
+ }
+ return nullptr;
+}
+
+static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr)
+{
+ PointerRNA id_ptr;
+
+ BLI_assert(ptr->owner_id != nullptr);
+
+ /* TODO: Support Bones/PoseBones. no pointers stored to the bones from here, only the ID.
+ * See example in T25746.
+ * Unless this is added only way to find this is to also search
+ * all bones and pose bones of an armature or object.
+ */
+ RNA_id_pointer_create(ptr->owner_id, &id_ptr);
+
+ return RNA_path_from_struct_to_idproperty(&id_ptr, static_cast<IDProperty *>(ptr->data));
+}
+
+ID *RNA_find_real_ID_and_path(ID *id, const char **r_path)
+{
+ if (r_path) {
+ *r_path = "";
+ }
+
+ if ((id == nullptr) || (id->flag & LIB_EMBEDDED_DATA) == 0) {
+ return id;
+ }
+
+ if (r_path) {
+ switch (GS(id->name)) {
+ case ID_NT:
+ *r_path = "node_tree";
+ break;
+ case ID_GR:
+ *r_path = "collection";
+ break;
+ default:
+ BLI_assert_msg(0, "Missing handling of embedded id type.");
+ }
+ }
+
+ ID *owner_id = BKE_id_owner_get(id);
+ BLI_assert_msg(owner_id != nullptr, "Missing handling of embedded id type.");
+ return (owner_id != nullptr) ? owner_id : id;
+}
+
+static char *rna_prepend_real_ID_path(Main * /*bmain*/, ID *id, char *path, ID **r_real_id)
+{
+ if (r_real_id != nullptr) {
+ *r_real_id = nullptr;
+ }
+
+ const char *prefix;
+ ID *real_id = RNA_find_real_ID_and_path(id, &prefix);
+
+ if (r_real_id != nullptr) {
+ *r_real_id = real_id;
+ }
+
+ if (path != nullptr) {
+ char *new_path = nullptr;
+
+ if (real_id) {
+ if (prefix[0]) {
+ new_path = BLI_sprintfN("%s%s%s", prefix, path[0] == '[' ? "" : ".", path);
+ }
+ else {
+ return path;
+ }
+ }
+
+ MEM_freeN(path);
+ return new_path;
+ }
+ return prefix[0] != '\0' ? BLI_strdup(prefix) : nullptr;
+}
+
+char *RNA_path_from_ID_to_struct(const PointerRNA *ptr)
+{
+ char *ptrpath = nullptr;
+
+ if (!ptr->owner_id || !ptr->data) {
+ return nullptr;
+ }
+
+ if (!RNA_struct_is_ID(ptr->type)) {
+ if (ptr->type->path) {
+ /* if type has a path to some ID, use it */
+ ptrpath = ptr->type->path((PointerRNA *)ptr);
+ }
+ else if (ptr->type->nested && RNA_struct_is_ID(ptr->type->nested)) {
+ PointerRNA parentptr;
+ PropertyRNA *userprop;
+
+ /* find the property in the struct we're nested in that references this struct, and
+ * use its identifier as the first part of the path used...
+ */
+ RNA_id_pointer_create(ptr->owner_id, &parentptr);
+ userprop = rna_struct_find_nested(&parentptr, ptr->type);
+
+ if (userprop) {
+ ptrpath = BLI_strdup(RNA_property_identifier(userprop));
+ }
+ else {
+ return nullptr; /* can't do anything about this case yet... */
+ }
+ }
+ else if (RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
+ /* special case, easier to deal with here than in ptr->type->path() */
+ return rna_path_from_ID_to_idpgroup(ptr);
+ }
+ else {
+ return nullptr;
+ }
+ }
+
+ return ptrpath;
+}
+
+char *RNA_path_from_real_ID_to_struct(Main *bmain, const PointerRNA *ptr, struct ID **r_real)
+{
+ char *path = RNA_path_from_ID_to_struct(ptr);
+
+ /* NULL path is valid in that case, when given struct is an ID one... */
+ return rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real);
+}
+
+static void rna_path_array_multi_from_flat_index(const int dimsize[RNA_MAX_ARRAY_LENGTH],
+ const int totdims,
+ const int index_dim,
+ int index,
+ int r_index_multi[RNA_MAX_ARRAY_LENGTH])
+{
+ int dimsize_step[RNA_MAX_ARRAY_LENGTH + 1];
+ int i = totdims - 1;
+ dimsize_step[i + 1] = 1;
+ dimsize_step[i] = dimsize[i];
+ while (--i != -1) {
+ dimsize_step[i] = dimsize[i] * dimsize_step[i + 1];
+ }
+ while (++i != index_dim) {
+ int index_round = index / dimsize_step[i + 1];
+ r_index_multi[i] = index_round;
+ index -= (index_round * dimsize_step[i + 1]);
+ }
+ BLI_assert(index == 0);
+}
+
+static void rna_path_array_multi_string_from_flat_index(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index,
+ char *index_str,
+ int index_str_len)
+{
+ int dimsize[RNA_MAX_ARRAY_LENGTH];
+ int totdims = RNA_property_array_dimension(ptr, prop, dimsize);
+ int index_multi[RNA_MAX_ARRAY_LENGTH];
+
+ rna_path_array_multi_from_flat_index(dimsize, totdims, index_dim, index, index_multi);
+
+ for (int i = 0, offset = 0; (i < index_dim) && (offset < index_str_len); i++) {
+ offset += BLI_snprintf_rlen(
+ &index_str[offset], index_str_len - offset, "[%d]", index_multi[i]);
+ }
+}
+
+char *RNA_path_from_ID_to_property_index(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index)
+{
+ const bool is_rna = (prop->magic == RNA_MAGIC);
+ const char *propname;
+ char *ptrpath, *path;
+
+ if (!ptr->owner_id || !ptr->data) {
+ return nullptr;
+ }
+
+ /* path from ID to the struct holding this property */
+ ptrpath = RNA_path_from_ID_to_struct(ptr);
+
+ propname = RNA_property_identifier(prop);
+
+ /* support indexing w/ multi-dimensional arrays */
+ char index_str[RNA_MAX_ARRAY_LENGTH * 12 + 1];
+ if (index_dim == 0) {
+ index_str[0] = '\0';
+ }
+ else {
+ rna_path_array_multi_string_from_flat_index(
+ ptr, prop, index_dim, index, index_str, sizeof(index_str));
+ }
+
+ if (ptrpath) {
+ if (is_rna) {
+ path = BLI_sprintfN("%s.%s%s", ptrpath, propname, index_str);
+ }
+ else {
+ char propname_esc[MAX_IDPROP_NAME * 2];
+ BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
+ path = BLI_sprintfN("%s[\"%s\"]%s", ptrpath, propname_esc, index_str);
+ }
+ MEM_freeN(ptrpath);
+ }
+ else if (RNA_struct_is_ID(ptr->type)) {
+ if (is_rna) {
+ path = BLI_sprintfN("%s%s", propname, index_str);
+ }
+ else {
+ char propname_esc[MAX_IDPROP_NAME * 2];
+ BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
+ path = BLI_sprintfN("[\"%s\"]%s", propname_esc, index_str);
+ }
+ }
+ else {
+ path = nullptr;
+ }
+
+ return path;
+}
+
+char *RNA_path_from_ID_to_property(const PointerRNA *ptr, PropertyRNA *prop)
+{
+ return RNA_path_from_ID_to_property_index(ptr, prop, 0, -1);
+}
+
+char *RNA_path_from_real_ID_to_property_index(Main *bmain,
+ const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index_dim,
+ int index,
+ ID **r_real_id)
+{
+ char *path = RNA_path_from_ID_to_property_index(ptr, prop, index_dim, index);
+
+ /* NULL path is always an error here, in that case do not return the 'fake ID from real ID' part
+ * of the path either. */
+ return path != nullptr ? rna_prepend_real_ID_path(bmain, ptr->owner_id, path, r_real_id) :
+ nullptr;
+}
+
+char *RNA_path_resolve_from_type_to_property(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ const StructRNA *type)
+{
+ /* Try to recursively find an "type"'d ancestor,
+ * to handle situations where path from ID is not enough. */
+ PointerRNA idptr;
+ ListBase path_elems = {nullptr};
+ char *path = nullptr;
+ char *full_path = RNA_path_from_ID_to_property(ptr, prop);
+
+ if (full_path == nullptr) {
+ return nullptr;
+ }
+
+ RNA_id_pointer_create(ptr->owner_id, &idptr);
+
+ if (RNA_path_resolve_elements(&idptr, full_path, &path_elems)) {
+ LISTBASE_FOREACH_BACKWARD (PropertyElemRNA *, prop_elem, &path_elems) {
+ if (RNA_struct_is_a(prop_elem->ptr.type, type)) {
+ char *ref_path = RNA_path_from_ID_to_struct(&prop_elem->ptr);
+ if (ref_path) {
+ path = BLI_strdup(full_path + strlen(ref_path) + 1); /* +1 for the linking '.' */
+ MEM_freeN(ref_path);
+ }
+ break;
+ }
+ }
+
+ BLI_freelistN(&path_elems);
+ }
+
+ MEM_freeN(full_path);
+ return path;
+}
+
+char *RNA_path_full_ID_py(ID *id)
+{
+ const char *path;
+ ID *id_real = RNA_find_real_ID_and_path(id, &path);
+
+ if (id_real) {
+ id = id_real;
+ }
+ else {
+ path = "";
+ }
+
+ char lib_filepath_esc[(sizeof(id->lib->filepath) * 2) + 4];
+ if (ID_IS_LINKED(id)) {
+ 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%s",
+ BKE_idtype_idcode_to_name_plural(GS(id->name)),
+ id_esc,
+ lib_filepath_esc,
+ path[0] ? "." : "",
+ path);
+}
+
+char *RNA_path_full_struct_py(const PointerRNA *ptr)
+{
+ char *id_path;
+ char *data_path;
+
+ char *ret;
+
+ if (!ptr->owner_id) {
+ return nullptr;
+ }
+
+ /* never fails */
+ id_path = RNA_path_full_ID_py(ptr->owner_id);
+
+ data_path = RNA_path_from_ID_to_struct(ptr);
+
+ /* XXX data_path may be NULL (see T36788),
+ * do we want to get the 'bpy.data.foo["bar"].(null)' stuff? */
+ ret = BLI_sprintfN("%s.%s", id_path, data_path);
+
+ if (data_path) {
+ MEM_freeN(data_path);
+ }
+ MEM_freeN(id_path);
+
+ return ret;
+}
+
+char *RNA_path_full_property_py_ex(const PointerRNA *ptr,
+ PropertyRNA *prop,
+ int index,
+ bool use_fallback)
+{
+ char *id_path;
+ const char *data_delim;
+ const char *data_path;
+ bool data_path_free;
+
+ char *ret;
+
+ if (!ptr->owner_id) {
+ return nullptr;
+ }
+
+ /* never fails */
+ id_path = RNA_path_full_ID_py(ptr->owner_id);
+
+ data_path = RNA_path_from_ID_to_property(ptr, prop);
+ if (data_path) {
+ data_delim = (data_path[0] == '[') ? "" : ".";
+ data_path_free = true;
+ }
+ else {
+ if (use_fallback) {
+ /* Fuzzy fallback. Be explicit in our ignorance. */
+ data_path = RNA_property_identifier(prop);
+ data_delim = " ... ";
+ }
+ else {
+ data_delim = ".";
+ }
+ data_path_free = false;
+ }
+
+ if ((index == -1) || (RNA_property_array_check(prop) == false)) {
+ ret = BLI_sprintfN("%s%s%s", id_path, data_delim, data_path);
+ }
+ else {
+ ret = BLI_sprintfN("%s%s%s[%d]", id_path, data_delim, data_path, index);
+ }
+ MEM_freeN(id_path);
+ if (data_path_free) {
+ MEM_freeN((void *)data_path);
+ }
+
+ return ret;
+}
+
+char *RNA_path_full_property_py(const PointerRNA *ptr, PropertyRNA *prop, int index)
+{
+ return RNA_path_full_property_py_ex(ptr, prop, index, false);
+}
+
+char *RNA_path_struct_property_py(PointerRNA *ptr, PropertyRNA *prop, int index)
+{
+ char *data_path;
+
+ char *ret;
+
+ if (!ptr->owner_id) {
+ return nullptr;
+ }
+
+ data_path = RNA_path_from_ID_to_property(ptr, prop);
+
+ if (data_path == nullptr) {
+ /* This may not be an ID at all, check for simple when pointer owns property.
+ * TODO: more complex nested case. */
+ if (!RNA_struct_is_ID(ptr->type)) {
+ const char *prop_identifier = RNA_property_identifier(prop);
+ if (RNA_struct_find_property(ptr, prop_identifier) == prop) {
+ data_path = BLI_strdup(prop_identifier);
+ }
+ }
+ }
+
+ if ((index == -1) || (RNA_property_array_check(prop) == false)) {
+ ret = BLI_strdup(data_path);
+ }
+ else {
+ ret = BLI_sprintfN("%s[%d]", data_path, index);
+ }
+
+ if (data_path) {
+ MEM_freeN(data_path);
+ }
+
+ return ret;
+}
+
+char *RNA_path_property_py(const PointerRNA *UNUSED(ptr), PropertyRNA *prop, int index)
+{
+ const bool is_rna = (prop->magic == RNA_MAGIC);
+ const char *propname = RNA_property_identifier(prop);
+ char *ret;
+
+ if ((index == -1) || (RNA_property_array_check(prop) == false)) {
+ if (is_rna) {
+ ret = BLI_strdup(propname);
+ }
+ else {
+ char propname_esc[MAX_IDPROP_NAME * 2];
+ BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
+ ret = BLI_sprintfN("[\"%s\"]", propname_esc);
+ }
+ }
+ else {
+ if (is_rna) {
+ ret = BLI_sprintfN("%s[%d]", propname, index);
+ }
+ else {
+ char propname_esc[MAX_IDPROP_NAME * 2];
+ BLI_str_escape(propname_esc, propname, sizeof(propname_esc));
+ ret = BLI_sprintfN("[\"%s\"][%d]", propname_esc, index);
+ }
+ }
+
+ return ret;
+}
diff --git a/source/blender/makesrna/intern/rna_pointcloud.c b/source/blender/makesrna/intern/rna_pointcloud.c
index 4c5dcd5a587..904d011fa04 100644
--- a/source/blender/makesrna/intern/rna_pointcloud.c
+++ b/source/blender/makesrna/intern/rna_pointcloud.c
@@ -20,6 +20,7 @@
# include "BLI_math_vector.h"
+# include "BKE_customdata.h"
# include "BKE_pointcloud.h"
# include "DEG_depsgraph.h"
@@ -32,11 +33,23 @@ static PointCloud *rna_pointcloud(const PointerRNA *ptr)
return (PointCloud *)ptr->owner_id;
}
+static float (*get_pointcloud_positions(PointCloud *pointcloud))[3]
+{
+ return (float(*)[3])CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT3, "position");
+}
+
+static const float (*get_pointcloud_positions_const(const PointCloud *pointcloud))[3]
+{
+ return (const float(*)[3])CustomData_get_layer_named(
+ &pointcloud->pdata, CD_PROP_FLOAT3, "position");
+}
+
static int rna_Point_index_get_const(const PointerRNA *ptr)
{
const PointCloud *pointcloud = rna_pointcloud(ptr);
const float(*co)[3] = ptr->data;
- return (int)(co - pointcloud->co);
+ const float(*positions)[3] = get_pointcloud_positions_const(pointcloud);
+ return (int)(co - positions);
}
static int rna_Point_index_get(PointerRNA *ptr)
@@ -44,6 +57,35 @@ static int rna_Point_index_get(PointerRNA *ptr)
return rna_Point_index_get_const(ptr);
}
+static int rna_PointCloud_points_length(PointerRNA *ptr)
+{
+ const PointCloud *pointcloud = rna_pointcloud(ptr);
+ return pointcloud->totpoint;
+}
+
+static void rna_PointCloud_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ PointCloud *pointcloud = rna_pointcloud(ptr);
+ rna_iterator_array_begin(iter,
+ get_pointcloud_positions(pointcloud),
+ sizeof(float[3]),
+ pointcloud->totpoint,
+ false,
+ NULL);
+}
+
+int rna_PointCloud_points_lookup_int(PointerRNA *ptr, int index, PointerRNA *r_ptr)
+{
+ PointCloud *pointcloud = rna_pointcloud(ptr);
+ if (index < 0 || index >= pointcloud->totpoint) {
+ return false;
+ }
+ r_ptr->owner_id = &pointcloud->id;
+ r_ptr->type = &RNA_Point;
+ r_ptr->data = &get_pointcloud_positions(pointcloud)[index];
+ return true;
+}
+
static void rna_Point_location_get(PointerRNA *ptr, float value[3])
{
copy_v3_v3(value, (const float *)ptr->data);
@@ -57,21 +99,22 @@ static void rna_Point_location_set(PointerRNA *ptr, const float value[3])
static float rna_Point_radius_get(PointerRNA *ptr)
{
const PointCloud *pointcloud = rna_pointcloud(ptr);
- if (pointcloud->radius == NULL) {
+ const float *radii = (const float *)CustomData_get_layer_named(
+ &pointcloud->pdata, CD_PROP_FLOAT, "radius");
+ if (radii == NULL) {
return 0.0f;
}
- const float(*co)[3] = ptr->data;
- return pointcloud->radius[co - pointcloud->co];
+ return radii[rna_Point_index_get_const(ptr)];
}
static void rna_Point_radius_set(PointerRNA *ptr, float value)
{
- const PointCloud *pointcloud = rna_pointcloud(ptr);
- if (pointcloud->radius == NULL) {
+ PointCloud *pointcloud = rna_pointcloud(ptr);
+ float *radii = (float *)CustomData_get_layer_named(&pointcloud->pdata, CD_PROP_FLOAT, "radius");
+ if (radii == NULL) {
return;
}
- const float(*co)[3] = ptr->data;
- pointcloud->radius[co - pointcloud->co] = value;
+ radii[rna_Point_index_get_const(ptr)] = value;
}
static char *rna_Point_path(const PointerRNA *ptr)
@@ -130,13 +173,18 @@ static void rna_def_pointcloud(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_POINTCLOUD_DATA);
/* geometry */
- /* TODO: better solution for (*co)[3] parsing issue. */
- RNA_define_verify_sdna(0);
prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
- RNA_def_property_collection_sdna(prop, NULL, "co", "totpoint");
RNA_def_property_struct_type(prop, "Point");
+ RNA_def_property_collection_funcs(prop,
+ "rna_PointCloud_points_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_PointCloud_points_length",
+ "rna_PointCloud_points_lookup_int",
+ NULL,
+ NULL);
RNA_def_property_ui_text(prop, "Points", "");
- RNA_define_verify_sdna(1);
/* materials */
prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c
index 30df8e20e8d..182f3ff6afa 100644
--- a/source/blender/makesrna/intern/rna_pose.c
+++ b/source/blender/makesrna/intern/rna_pose.c
@@ -127,7 +127,7 @@ static char *rna_PoseBone_path(const PointerRNA *ptr)
static bool rna_bone_group_poll(Object *ob, ReportList *reports)
{
if (ID_IS_OVERRIDE_LIBRARY(ob)) {
- BKE_report(reports, RPT_ERROR, "Cannot edit bone groups for proxies or library overrides");
+ BKE_report(reports, RPT_ERROR, "Cannot edit bone groups for library overrides");
return false;
}
@@ -600,7 +600,7 @@ static void rna_PoseChannel_constraints_remove(
ED_object_constraint_update(bmain, ob);
- /* XXX(Campbell): is this really needed? */
+ /* XXX(@campbellbarton): is this really needed? */
BKE_constraints_active_set(&pchan->constraints, NULL);
WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, id);
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 11a7be69f68..6a66445ee4c 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -118,7 +118,7 @@ static int engine_get_preview_pixel_size(RenderEngine *UNUSED(engine), Scene *sc
static void engine_bind_display_space_shader(RenderEngine *UNUSED(engine), Scene *UNUSED(scene))
{
- GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE);
+ GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_3D_IMAGE);
GPU_shader_bind(shader);
int img_loc = GPU_shader_get_uniform(shader, "image");
diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c
index 0c1fd8cab3c..7a499d10d3a 100644
--- a/source/blender/makesrna/intern/rna_rigidbody.c
+++ b/source/blender/makesrna/intern/rna_rigidbody.c
@@ -865,7 +865,7 @@ static void rna_def_rigidbody_world(BlenderRNA *brna)
prop = RNA_def_property(srna, "collection", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Collection");
RNA_def_property_pointer_sdna(prop, NULL, "group");
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK | PROP_ID_REFCOUNT);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Collection", "Collection containing objects participating in this simulation");
@@ -873,7 +873,7 @@ static void rna_def_rigidbody_world(BlenderRNA *brna)
prop = RNA_def_property(srna, "constraints", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "Collection");
- RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK | PROP_ID_REFCOUNT);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(
prop, "Constraints", "Collection containing rigid body constraint objects");
diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c
index 16a4dfe71cf..8d5e82cbfa8 100644
--- a/source/blender/makesrna/intern/rna_rna.c
+++ b/source/blender/makesrna/intern/rna_rna.c
@@ -160,6 +160,7 @@ const EnumPropertyItem rna_enum_property_flag_items[] = {
0,
"Update on every keystroke in textedit 'mode'",
""},
+ {PROP_PATH_OUTPUT, "OUTPUT_PATH", 0, "Output Path", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -729,6 +730,12 @@ static bool rna_Property_is_library_editable_flag_get(PointerRNA *ptr)
return (prop->flag & PROP_LIB_EXCEPTION) != 0;
}
+static bool rna_Property_is_path_output_flag_get(PointerRNA *ptr)
+{
+ PropertyRNA *prop = (PropertyRNA *)ptr->data;
+ return (prop->flag & PROP_PATH_OUTPUT) != 0;
+}
+
static int rna_Property_tags_get(PointerRNA *ptr)
{
return RNA_property_tags(ptr->data);
@@ -1145,7 +1152,7 @@ static bool rna_Function_no_self_get(PointerRNA *ptr)
return !(func->flag & FUNC_NO_SELF);
}
-static int rna_Function_use_self_type_get(PointerRNA *ptr)
+static bool rna_Function_use_self_type_get(PointerRNA *ptr)
{
FunctionRNA *func = (FunctionRNA *)ptr->data;
return 0 != (func->flag & FUNC_USE_SELF_TYPE);
@@ -1577,7 +1584,7 @@ static int rna_property_override_diff_propptr(Main *bmain,
RNA_property_##_typename##_set((_ptr), (_prop), (_value)))
/**
- * /return `0` is matching, `-1` if `prop_a < prop_b`, `1` if `prop_a > prop_b`. Note that for
+ * \return `0` is matching, `-1` if `prop_a < prop_b`, `1` if `prop_a > prop_b`. Note that for
* unquantifiable properties (e.g. pointers or collections), return value should be interpreted as
* a boolean (false == matching, true == not matching).
*/
@@ -3023,6 +3030,12 @@ static void rna_def_property(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Library Editable", "Property is editable from linked instances (changes not saved)");
+ prop = RNA_def_property(srna, "is_path_output", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_Property_is_path_output_flag_get", NULL);
+ RNA_def_property_ui_text(
+ prop, "Path Output", "Property is a filename, filepath or directory output");
+
prop = RNA_def_property(srna, "tags", PROP_ENUM, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_enum_items(prop, dummy_prop_tags);
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index daf4c99845d..a1612159806 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -88,6 +88,11 @@ static const EnumPropertyItem uv_sculpt_relaxation_items[] = {
"Laplacian",
"Use Laplacian method for relaxation"},
{UV_SCULPT_TOOL_RELAX_HC, "HC", 0, "HC", "Use HC method for relaxation"},
+ {UV_SCULPT_TOOL_RELAX_COTAN,
+ "COTAN",
+ 0,
+ "Geometry",
+ "Use Geometry (cotangent) relaxation, making UV's follow the underlying 3D geometry"},
{0, NULL, 0, NULL, NULL},
};
#endif
@@ -701,8 +706,10 @@ static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UN
static void rna_Gpencil_extend_selection(bContext *C, PointerRNA *UNUSED(ptr))
{
/* Extend selection to all points in all selected strokes. */
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if ((ob) && (ob->type == OB_GPENCIL)) {
bGPdata *gpd = (bGPdata *)ob->data;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
@@ -848,12 +855,12 @@ void rna_Scene_set_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
Scene *scene = (Scene *)ptr->owner_id;
DEG_relations_tag_update(bmain);
- DEG_id_tag_update_ex(bmain, &scene->id, 0);
+ DEG_id_tag_update_ex(bmain, &scene->id, ID_RECALC_BASE_FLAGS);
if (scene->set != NULL) {
/* Objects which are pulled into main scene's depsgraph needs to have
* their base flags updated.
*/
- DEG_id_tag_update_ex(bmain, &scene->set->id, 0);
+ DEG_id_tag_update_ex(bmain, &scene->set->id, ID_RECALC_BASE_FLAGS);
}
}
@@ -1295,7 +1302,7 @@ static const EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf(bContext
ID *id = ptr->owner_id;
const bool is_render = (id && GS(id->name) == ID_SCE);
- /* NOTE(campbell): we need to act differently for render
+ /* NOTE(@campbellbarton): we need to act differently for render
* where 'BW' will force grayscale even if the output format writes
* as RGBA, this is age old blender convention and not sure how useful
* it really is but keep it for now. */
@@ -1477,8 +1484,7 @@ static void rna_ImageFormatSettings_color_management_set(PointerRNA *ptr, int va
ID *owner_id = ptr->owner_id;
if (owner_id && GS(owner_id->name) == ID_NT) {
/* For compositing nodes, find the corresponding scene. */
- const IDTypeInfo *type_info = BKE_idtype_get_info_from_id(owner_id);
- owner_id = type_info->owner_get(G_MAIN, owner_id);
+ owner_id = BKE_id_owner_get(owner_id);
}
if (owner_id && GS(owner_id->name) == ID_SCE) {
BKE_image_format_color_management_copy_from_scene(imf, (Scene *)owner_id);
@@ -1675,18 +1681,18 @@ static bool rna_RenderSettings_use_spherical_stereo_get(PointerRNA *ptr)
return BKE_scene_use_spherical_stereo(scene);
}
-void rna_Scene_glsl_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+void rna_Scene_render_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
}
static void rna_Scene_world_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
Scene *screen = (Scene *)ptr->owner_id;
- rna_Scene_glsl_update(bmain, scene, ptr);
+ rna_Scene_render_update(bmain, scene, ptr);
WM_main_add_notifier(NC_WORLD | ND_WORLD, &screen->id);
DEG_relations_tag_update(bmain);
}
@@ -1702,21 +1708,21 @@ static void rna_Scene_mesh_quality_update(Main *bmain, Scene *UNUSED(scene), Poi
}
FOREACH_SCENE_OBJECT_END;
- rna_Scene_glsl_update(bmain, scene, ptr);
+ rna_Scene_render_update(bmain, scene, ptr);
}
void rna_Scene_freestyle_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
}
void rna_Scene_use_freestyle_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
if (scene->nodetree) {
ntreeCompositUpdateRLayers(scene->nodetree);
@@ -1756,7 +1762,7 @@ static void rna_SceneRenderView_name_set(PointerRNA *ptr, const char *value)
void rna_ViewLayer_material_override_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->owner_id;
- rna_Scene_glsl_update(bmain, scene, ptr);
+ rna_Scene_render_update(bmain, scene, ptr);
DEG_relations_tag_update(bmain);
}
@@ -1793,7 +1799,7 @@ void rna_ViewLayer_pass_update(Main *bmain, Scene *activescene, PointerRNA *ptr)
}
}
- rna_Scene_glsl_update(bmain, activescene, ptr);
+ rna_Scene_render_update(bmain, activescene, ptr);
}
static char *rna_ViewLayerEEVEE_path(const PointerRNA *ptr)
@@ -1858,13 +1864,17 @@ static void rna_Scene_editmesh_select_mode_set(PointerRNA *ptr, const bool *valu
/* Update select mode in all the workspaces in mesh edit mode. */
wmWindowManager *wm = G_MAIN->wm.first;
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
-
- if (view_layer && view_layer->basact) {
- Mesh *me = BKE_mesh_from_object(view_layer->basact->object);
- if (me && me->edit_mesh && me->edit_mesh->selectmode != flag) {
- me->edit_mesh->selectmode = flag;
- EDBM_selectmode_set(me->edit_mesh);
+ if (view_layer) {
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *object = BKE_view_layer_active_object_get(view_layer);
+ if (object) {
+ Mesh *me = BKE_mesh_from_object(object);
+ if (me && me->edit_mesh && me->edit_mesh->selectmode != flag) {
+ me->edit_mesh->selectmode = flag;
+ EDBM_selectmode_set(me->edit_mesh);
+ }
}
}
}
@@ -1873,11 +1883,14 @@ static void rna_Scene_editmesh_select_mode_set(PointerRNA *ptr, const bool *valu
static void rna_Scene_editmesh_select_mode_update(bContext *C, PointerRNA *UNUSED(ptr))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Mesh *me = NULL;
- if (view_layer->basact) {
- me = BKE_mesh_from_object(view_layer->basact->object);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *object = BKE_view_layer_active_object_get(view_layer);
+ if (object) {
+ me = BKE_mesh_from_object(object);
if (me && me->edit_mesh == NULL) {
me = NULL;
}
@@ -1950,7 +1963,7 @@ static void rna_Scene_use_simplify_update(Main *bmain, Scene *UNUSED(scene), Poi
WM_main_add_notifier(NC_GEOM | ND_DATA, NULL);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, NULL);
- DEG_id_tag_update(&sce->id, 0);
+ DEG_id_tag_update(&sce->id, ID_RECALC_COPY_ON_WRITE);
}
static void rna_Scene_simplify_update(Main *bmain, Scene *scene, PointerRNA *ptr)
@@ -2216,11 +2229,14 @@ static char *rna_SequencerToolSettings_path(const PointerRNA *UNUSED(ptr))
/* generic function to recalc geometry */
static void rna_EditMesh_update(bContext *C, PointerRNA *UNUSED(ptr))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Mesh *me = NULL;
- if (view_layer->basact) {
- me = BKE_mesh_from_object(view_layer->basact->object);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *object = BKE_view_layer_active_object_get(view_layer);
+ if (object) {
+ me = BKE_mesh_from_object(object);
if (me && me->edit_mesh == NULL) {
me = NULL;
}
@@ -2244,8 +2260,10 @@ static char *rna_MeshStatVis_path(const PointerRNA *UNUSED(ptr))
* given its own notifier. */
static void rna_Scene_update_active_object_data(bContext *C, PointerRNA *UNUSED(ptr))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
@@ -2303,7 +2321,7 @@ FreestyleLineSet *rna_FreestyleSettings_lineset_add(ID *id,
Scene *scene = (Scene *)id;
FreestyleLineSet *lineset = BKE_freestyle_lineset_add(bmain, (FreestyleConfig *)config, name);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL);
return lineset;
@@ -2324,7 +2342,7 @@ void rna_FreestyleSettings_lineset_remove(ID *id,
RNA_POINTER_INVALIDATE(lineset_ptr);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL);
}
@@ -2361,7 +2379,7 @@ FreestyleModuleConfig *rna_FreestyleSettings_module_add(ID *id, FreestyleSetting
Scene *scene = (Scene *)id;
FreestyleModuleConfig *module = BKE_freestyle_module_add((FreestyleConfig *)config);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL);
return module;
@@ -2390,7 +2408,7 @@ void rna_FreestyleSettings_module_remove(ID *id,
RNA_POINTER_INVALIDATE(module_ptr);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL);
}
@@ -2421,7 +2439,7 @@ static ViewLayer *rna_ViewLayer_new(ID *id, Scene *UNUSED(sce), Main *bmain, con
Scene *scene = (Scene *)id;
ViewLayer *view_layer = BKE_view_layer_add(scene, name, NULL, VIEWLAYER_ADD_NEW);
- DEG_id_tag_update(&scene->id, 0);
+ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(bmain);
WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL);
@@ -2501,7 +2519,7 @@ static void rna_ViewLayerLightgroup_name_set(PointerRNA *ptr, const char *value)
Scene *scene = (Scene *)ptr->owner_id;
ViewLayer *view_layer = BKE_view_layer_find_with_lightgroup(scene, lightgroup);
- BKE_view_layer_rename_lightgroup(view_layer, lightgroup, value);
+ BKE_view_layer_rename_lightgroup(scene, view_layer, lightgroup, value);
}
/* Fake value, used internally (not saved to DNA). */
@@ -2728,7 +2746,11 @@ static void rna_FFmpegSettings_codec_update(Main *UNUSED(bmain),
PointerRNA *ptr)
{
FFMpegCodecData *codec_data = (FFMpegCodecData *)ptr->data;
- if (!ELEM(codec_data->codec, AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_VP9)) {
+ if (!ELEM(codec_data->codec,
+ AV_CODEC_ID_H264,
+ AV_CODEC_ID_MPEG4,
+ AV_CODEC_ID_VP9,
+ AV_CODEC_ID_DNXHD)) {
/* Constant Rate Factor (CRF) setting is only available for H264,
* MPEG4 and WEBM/VP9 codecs. So changing encoder quality mode to
* CBR as CRF is not supported.
@@ -3134,6 +3156,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "workspace_tool_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "workspace_tool_type");
RNA_def_property_enum_items(prop, workspace_tool_items);
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_EDITOR_VIEW3D);
RNA_def_property_ui_text(prop, "Drag", "Action when dragging in the viewport");
/* Transform */
@@ -4457,7 +4480,7 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce
RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_SKY);
RNA_def_property_ui_text(prop, "Sky", "Render Sky in this Layer");
if (scene) {
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_render_update");
}
else {
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -4467,7 +4490,7 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce
RNA_def_property_boolean_sdna(prop, NULL, "layflag", SCE_LAY_AO);
RNA_def_property_ui_text(prop, "Ambient Occlusion", "Render Ambient Occlusion in this Layer");
if (scene) {
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_render_update");
}
else {
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -5807,19 +5830,19 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "cineon_black", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "cineon_black");
RNA_def_property_range(prop, 0, 1024);
- RNA_def_property_ui_text(prop, "B", "Log conversion reference blackpoint");
+ RNA_def_property_ui_text(prop, "Black", "Log conversion reference blackpoint");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "cineon_white", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "cineon_white");
RNA_def_property_range(prop, 0, 1024);
- RNA_def_property_ui_text(prop, "W", "Log conversion reference whitepoint");
+ RNA_def_property_ui_text(prop, "White", "Log conversion reference whitepoint");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
prop = RNA_def_property(srna, "cineon_gamma", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "cineon_gamma");
RNA_def_property_range(prop, 0.0f, 10.0f);
- RNA_def_property_ui_text(prop, "G", "Log conversion gamma");
+ RNA_def_property_ui_text(prop, "Gamma", "Log conversion gamma");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
/* multiview */
@@ -6344,7 +6367,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop,
"Transparent",
"World background is transparent, for compositing the render over another background");
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_render_update");
prop = RNA_def_property(srna, "use_freestyle", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
@@ -6375,14 +6398,14 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "mode", R_MBLUR);
RNA_def_property_ui_text(prop, "Motion Blur", "Use multi-sampled 3D scene motion blur");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_render_update");
prop = RNA_def_property(srna, "motion_blur_shutter", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "blurfac");
RNA_def_property_range(prop, 0.0f, FLT_MAX);
RNA_def_property_ui_range(prop, 0.01f, 1.0f, 1, 2);
RNA_def_property_ui_text(prop, "Shutter", "Time taken in frames between shutter open and close");
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_render_update");
prop = RNA_def_property(srna, "motion_blur_shutter_curve", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "mblur_shutter_curve");
@@ -6394,13 +6417,13 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop = RNA_def_property(srna, "hair_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, hair_shape_type_items);
RNA_def_property_ui_text(prop, "Curves Shape Type", "Curves shape type");
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_render_update");
prop = RNA_def_property(srna, "hair_subdiv", PROP_INT, PROP_NONE);
RNA_def_property_range(prop, 0, 3);
RNA_def_property_ui_text(
prop, "Additional Subdivision", "Additional subdivision along the curves");
- RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_glsl_update");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_Scene_render_update");
/* Performance */
prop = RNA_def_property(srna, "use_high_quality_normals", PROP_BOOLEAN, PROP_NONE);
@@ -6530,6 +6553,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
"Output Path",
"Directory/name to save animations, # characters defines the position "
"and length of frame numbers");
+ RNA_def_property_flag(prop, PROP_PATH_OUTPUT);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
/* Render result EXR cache. */
diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c
index 3de9e632ff6..a4298c8c3aa 100644
--- a/source/blender/makesrna/intern/rna_sculpt_paint.c
+++ b/source/blender/makesrna/intern/rna_sculpt_paint.c
@@ -22,6 +22,7 @@
#include "DNA_space_types.h"
#include "BKE_brush.h"
+#include "BKE_layer.h"
#include "BKE_material.h"
#include "BKE_paint.h"
@@ -164,7 +165,8 @@ static void rna_ParticleEdit_redo(bContext *C, PointerRNA *UNUSED(ptr))
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
PTCacheEdit *edit = PE_get_current(depsgraph, scene, ob);
if (!edit) {
@@ -184,7 +186,8 @@ static void rna_ParticleEdit_update(bContext *C, PointerRNA *UNUSED(ptr))
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
@@ -214,8 +217,10 @@ static const EnumPropertyItem *rna_ParticleEdit_tool_itemf(bContext *C,
PropertyRNA *UNUSED(prop),
bool *UNUSED(r_free))
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
# if 0
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Scene *scene = CTX_data_scene(C);
@@ -373,7 +378,8 @@ static void rna_Sculpt_update(bContext *C, PointerRNA *UNUSED(ptr))
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
@@ -388,12 +394,13 @@ static void rna_Sculpt_update(bContext *C, PointerRNA *UNUSED(ptr))
static void rna_Sculpt_ShowMask_update(bContext *C, PointerRNA *UNUSED(ptr))
{
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *object = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *object = BKE_view_layer_active_object_get(view_layer);
if (object == NULL || object->sculpt == NULL) {
return;
}
- Scene *scene = CTX_data_scene(C);
Sculpt *sd = scene->toolsettings->sculpt;
object->sculpt->show_mask = ((sd->flags & SCULPT_HIDE_MASK) == 0);
if (object->sculpt->pbvh != NULL) {
@@ -487,7 +494,8 @@ static void rna_ImaPaint_mode_update(bContext *C, PointerRNA *UNUSED(ptr))
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob && ob->type == OB_MESH) {
/* of course we need to invalidate here */
@@ -504,7 +512,8 @@ static void rna_ImaPaint_stencil_update(bContext *C, PointerRNA *UNUSED(ptr))
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob && ob->type == OB_MESH) {
ED_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
@@ -523,7 +532,8 @@ static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA *UNUSED(ptr))
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
bScreen *screen;
Image *ima = scene->toolsettings->imapaint.canvas;
@@ -1040,7 +1050,7 @@ static void rna_def_paint_mode(BlenderRNA *brna)
RNA_def_property_pointer_funcs(
prop, NULL, NULL, NULL, "rna_PaintModeSettings_canvas_image_poll");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE);
- RNA_def_property_ui_text(prop, "Texture", "Image used as as painting target");
+ RNA_def_property_ui_text(prop, "Texture", "Image used as painting target");
}
static void rna_def_image_paint(BlenderRNA *brna)
@@ -1303,6 +1313,7 @@ static void rna_def_particle_edit(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", PE_INTERPOLATE_ADDED);
RNA_def_property_ui_text(
prop, "Interpolate", "Interpolate new particles from the existing ones");
+ RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "default_key_count", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "totaddkey");
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index 04037a64426..ea508bf117c 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -324,7 +324,7 @@ static int rna_Sequence_frame_final_end_get(PointerRNA *ptr)
return SEQ_time_right_handle_frame_get(scene, (Sequence *)ptr->data);
}
-static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, float value)
+static void rna_Sequence_start_frame_final_set(PointerRNA *ptr, int value)
{
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
@@ -346,7 +346,7 @@ static void rna_Sequence_end_frame_final_set(PointerRNA *ptr, int value)
SEQ_relations_invalidate_cache_composite(scene, seq);
}
-static void rna_Sequence_start_frame_set(PointerRNA *ptr, int value)
+static void rna_Sequence_start_frame_set(PointerRNA *ptr, float value)
{
Sequence *seq = (Sequence *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
@@ -1754,12 +1754,12 @@ static void rna_def_color_balance(BlenderRNA *brna)
prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN);
- RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`");
+ RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
prop = RNA_def_property(srna, "invert_slope", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_SLOPE);
- RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color`");
+ RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update");
prop = RNA_def_property(srna, "invert_offset", PROP_BOOLEAN, PROP_NONE);
@@ -2247,6 +2247,7 @@ static void rna_def_editor(BlenderRNA *brna)
prop = RNA_def_property(srna, "proxy_storage", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, editing_storage_items);
RNA_def_property_ui_text(prop, "Proxy Storage", "How to store proxies for this project");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SEQUENCE);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache");
prop = RNA_def_property(srna, "proxy_dir", PROP_STRING, PROP_DIRPATH);
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index aab6174cab2..ae241c11522 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -63,7 +63,7 @@ static void rna_Sequence_swap_internal(ID *id,
const char *error_msg;
Scene *scene = (Scene *)id;
- if (SEQ_edit_sequence_swap(scene, seq_self, seq_other, &error_msg) == 0) {
+ if (SEQ_edit_sequence_swap(scene, seq_self, seq_other, &error_msg) == false) {
BKE_report(reports, RPT_ERROR, error_msg);
}
}
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 969d1f2075e..9bbe37b5187 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -88,7 +88,7 @@ const EnumPropertyItem rna_enum_space_type_items[] = {
{SPACE_EMPTY, "EMPTY", ICON_NONE, "Empty", ""},
/* General. */
- RNA_ENUM_ITEM_HEADING("General", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("General"), NULL),
{SPACE_VIEW3D,
"VIEW_3D",
ICON_VIEW3D,
@@ -108,7 +108,7 @@ const EnumPropertyItem rna_enum_space_type_items[] = {
{SPACE_CLIP, "CLIP_EDITOR", ICON_TRACKER, "Movie Clip Editor", "Motion tracking tools"},
/* Animation. */
- RNA_ENUM_ITEM_HEADING("Animation", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Animation"), NULL),
#if 0
{SPACE_ACTION,
"TIMELINE",
@@ -125,7 +125,7 @@ const EnumPropertyItem rna_enum_space_type_items[] = {
{SPACE_NLA, "NLA_EDITOR", ICON_NLA, "Nonlinear Animation", "Combine and layer Actions"},
/* Scripting. */
- RNA_ENUM_ITEM_HEADING("Scripting", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Scripting"), NULL),
{SPACE_TEXT,
"TEXT_EDITOR",
ICON_TEXT,
@@ -153,7 +153,7 @@ const EnumPropertyItem rna_enum_space_type_items[] = {
"screen for general status information"},
/* Data. */
- RNA_ENUM_ITEM_HEADING("Data", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Data"), NULL),
{SPACE_OUTLINER,
"OUTLINER",
ICON_OUTLINER,
@@ -435,28 +435,31 @@ static const EnumPropertyItem rna_enum_studio_light_items[] = {
};
static const EnumPropertyItem rna_enum_view3dshading_render_pass_type_items[] = {
- RNA_ENUM_ITEM_HEADING("General", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("General"), NULL),
{EEVEE_RENDER_PASS_COMBINED, "COMBINED", 0, "Combined", ""},
{EEVEE_RENDER_PASS_EMIT, "EMISSION", 0, "Emission", ""},
{EEVEE_RENDER_PASS_ENVIRONMENT, "ENVIRONMENT", 0, "Environment", ""},
{EEVEE_RENDER_PASS_AO, "AO", 0, "Ambient Occlusion", ""},
{EEVEE_RENDER_PASS_SHADOW, "SHADOW", 0, "Shadow", ""},
- RNA_ENUM_ITEM_HEADING("Light", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Light"), NULL),
{EEVEE_RENDER_PASS_DIFFUSE_LIGHT, "DIFFUSE_LIGHT", 0, "Diffuse Light", ""},
{EEVEE_RENDER_PASS_DIFFUSE_COLOR, "DIFFUSE_COLOR", 0, "Diffuse Color", ""},
{EEVEE_RENDER_PASS_SPECULAR_LIGHT, "SPECULAR_LIGHT", 0, "Specular Light", ""},
{EEVEE_RENDER_PASS_SPECULAR_COLOR, "SPECULAR_COLOR", 0, "Specular Color", ""},
{EEVEE_RENDER_PASS_VOLUME_LIGHT, "VOLUME_LIGHT", 0, "Volume Light", ""},
- RNA_ENUM_ITEM_HEADING("Effects", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Effects"), NULL),
{EEVEE_RENDER_PASS_BLOOM, "BLOOM", 0, "Bloom", ""},
- RNA_ENUM_ITEM_HEADING("Data", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Data"), NULL),
{EEVEE_RENDER_PASS_NORMAL, "NORMAL", 0, "Normal", ""},
{EEVEE_RENDER_PASS_MIST, "MIST", 0, "Mist", ""},
+ {EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT, "CryptoObject", 0, "CryptoObject", ""},
+ {EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET, "CryptoAsset", 0, "CryptoAsset", ""},
+ {EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL, "CryptoMaterial", 0, "CryptoMaterial", ""},
- RNA_ENUM_ITEM_HEADING("Shader AOV", NULL),
+ RNA_ENUM_ITEM_HEADING(N_("Shader AOV"), NULL),
{EEVEE_RENDER_PASS_AOV, "AOV", 0, "AOV", ""},
{0, NULL, 0, NULL, NULL},
@@ -1100,7 +1103,7 @@ static bool rna_RegionView3D_is_orthographic_side_view_get(PointerRNA *ptr)
return RV3D_VIEW_IS_AXIS(rv3d->view);
}
-static void rna_RegionView3D_is_orthographic_side_view_set(PointerRNA *ptr, int value)
+static void rna_RegionView3D_is_orthographic_side_view_set(PointerRNA *ptr, bool value)
{
RegionView3D *rv3d = (RegionView3D *)(ptr->data);
const bool was_axis_view = RV3D_VIEW_IS_AXIS(rv3d->view);
@@ -1423,6 +1426,7 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C,
const bool bloom_enabled = scene->eevee.flag & SCE_EEVEE_BLOOM_ENABLED;
const bool aov_available = BKE_view_layer_has_valid_aov(view_layer);
+ const bool eevee_next_active = STREQ(scene->r.engine, "BLENDER_EEVEE_NEXT");
int totitem = 0;
EnumPropertyItem *result = NULL;
@@ -1443,6 +1447,12 @@ static const EnumPropertyItem *rna_3DViewShading_render_pass_itemf(bContext *C,
aov_template.value++;
}
}
+ else if (ELEM(item->value,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_OBJECT,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_ASSET,
+ EEVEE_RENDER_PASS_CRYPTOMATTE_MATERIAL) &&
+ !eevee_next_active) {
+ }
else if (!((!bloom_enabled &&
(item->value == EEVEE_RENDER_PASS_BLOOM || STREQ(item->name, "Effects"))) ||
(!aov_available && STREQ(item->name, "Shader AOV")))) {
@@ -1519,7 +1529,7 @@ static void rna_SpaceView3D_use_local_collections_update(bContext *C, PointerRNA
View3D *v3d = (View3D *)ptr->data;
if (ED_view3d_local_collections_set(bmain, v3d)) {
- BKE_layer_collection_local_sync(view_layer, v3d);
+ BKE_layer_collection_local_sync(scene, view_layer, v3d);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
}
}
@@ -1609,7 +1619,7 @@ static void rna_SpaceImageEditor_mode_update(Main *bmain, Scene *scene, PointerR
}
}
-static void rna_SpaceImageEditor_show_stereo_set(PointerRNA *ptr, int value)
+static void rna_SpaceImageEditor_show_stereo_set(PointerRNA *ptr, bool value)
{
SpaceImage *sima = (SpaceImage *)(ptr->data);
@@ -1663,8 +1673,10 @@ static bool rna_SpaceImageEditor_show_uvedit_get(PointerRNA *ptr)
Object *obedit = NULL;
wmWindow *win = ED_screen_window_find(screen, G_MAIN->wm.first);
if (win != NULL) {
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ obedit = BKE_view_layer_edit_object_get(view_layer);
}
return ED_space_image_show_uvedit(sima, obedit);
}
@@ -1676,8 +1688,10 @@ static bool rna_SpaceImageEditor_show_maskedit_get(PointerRNA *ptr)
Object *obedit = NULL;
wmWindow *win = ED_screen_window_find(screen, G_MAIN->wm.first);
if (win != NULL) {
+ Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- obedit = OBEDIT_FROM_VIEW_LAYER(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ obedit = BKE_view_layer_edit_object_get(view_layer);
}
return ED_space_image_check_show_maskedit(sima, obedit);
}
@@ -1872,6 +1886,15 @@ static void rna_SpaceUVEditor_tile_grid_shape_set(PointerRNA *ptr, const int *va
}
}
+static void rna_SpaceUVEditor_custom_grid_subdiv_set(PointerRNA *ptr, const int *values)
+{
+ SpaceImage *data = (SpaceImage *)(ptr->data);
+
+ for (int i = 0; i < 2; i++) {
+ data->custom_grid_subdiv[i] = CLAMPIS(values[i], 1, 5000);
+ }
+}
+
/* Space Text Editor */
static void rna_SpaceTextEditor_word_wrap_set(PointerRNA *ptr, bool value)
@@ -2174,10 +2197,12 @@ static void rna_SpaceDopeSheetEditor_action_set(PointerRNA *ptr,
static void rna_SpaceDopeSheetEditor_action_update(bContext *C, PointerRNA *ptr)
{
SpaceAction *saction = (SpaceAction *)(ptr->data);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Main *bmain = CTX_data_main(C);
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact == NULL) {
return;
}
@@ -2249,8 +2274,10 @@ static void rna_SpaceDopeSheetEditor_mode_update(bContext *C, PointerRNA *ptr)
{
SpaceAction *saction = (SpaceAction *)(ptr->data);
ScrArea *area = CTX_wm_area(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
/* special exceptions for ShapeKey Editor mode */
if (saction->mode == SACTCONT_SHAPEKEY) {
@@ -3513,10 +3540,10 @@ static void rna_def_space_image_uv(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
- static const EnumPropertyItem pixel_snap_mode_items[] = {
- {SI_PIXEL_SNAP_DISABLED, "DISABLED", 0, "Disabled", "Don't snap to pixels"},
- {SI_PIXEL_SNAP_CORNER, "CORNER", 0, "Corner", "Snap to pixel corners"},
- {SI_PIXEL_SNAP_CENTER, "CENTER", 0, "Center", "Snap to pixel centers"},
+ static const EnumPropertyItem pixel_round_mode_items[] = {
+ {SI_PIXEL_ROUND_DISABLED, "DISABLED", 0, "Disabled", "Don't round to pixels"},
+ {SI_PIXEL_ROUND_CORNER, "CORNER", 0, "Corner", "Round to pixel corners"},
+ {SI_PIXEL_ROUND_CENTER, "CENTER", 0, "Center", "Round to pixel centers"},
{0, NULL, 0, NULL, NULL},
};
@@ -3546,6 +3573,7 @@ static void rna_def_space_image_uv(BlenderRNA *brna)
RNA_def_property_enum_sdna(prop, NULL, "dt_uvstretch");
RNA_def_property_enum_items(prop, dt_uvstretch_items);
RNA_def_property_ui_text(prop, "Display Stretch Type", "Type of stretch to display");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_MESH);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
prop = RNA_def_property(srna, "show_modified_edges", PROP_BOOLEAN, PROP_NONE);
@@ -3586,15 +3614,24 @@ static void rna_def_space_image_uv(BlenderRNA *brna)
prop, "Tile Grid Shape", "How many tiles will be shown in the background");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+ prop = RNA_def_property(srna, "show_grid_over_image", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_GRID_OVER_IMAGE);
+ RNA_def_property_boolean_default(prop, true);
+ RNA_def_property_ui_text(prop, "Grid Over Image", "Show the grid over the image");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+
prop = RNA_def_property(srna, "use_custom_grid", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_CUSTOM_GRID);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(prop, "Custom Grid", "Use a grid with a user-defined number of steps");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
- prop = RNA_def_property(srna, "custom_grid_subdivisions", PROP_INT, PROP_NONE);
+ prop = RNA_def_property(srna, "custom_grid_subdivisions", PROP_INT, PROP_XYZ);
RNA_def_property_int_sdna(prop, NULL, "custom_grid_subdiv");
+ RNA_def_property_array(prop, 2);
+ RNA_def_property_int_default(prop, 10);
RNA_def_property_range(prop, 1, 5000);
+ RNA_def_property_int_funcs(prop, NULL, "rna_SpaceUVEditor_custom_grid_subdiv_set", NULL);
RNA_def_property_ui_text(
prop, "Dynamic Grid Size", "Number of grid units in UV space that make one UV Unit");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
@@ -3605,11 +3642,9 @@ static void rna_def_space_image_uv(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "UV Opacity", "Opacity of UV overlays");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
- /* TODO: move edge and face drawing options here from `G.f`. */
-
- prop = RNA_def_property(srna, "pixel_snap_mode", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_items(prop, pixel_snap_mode_items);
- RNA_def_property_ui_text(prop, "Snap to Pixels", "Snap UVs to pixels while editing");
+ prop = RNA_def_property(srna, "pixel_round_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, pixel_round_mode_items);
+ RNA_def_property_ui_text(prop, "Round to Pixels", "Round UVs to pixels while editing");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
prop = RNA_def_property(srna, "lock_bounds", PROP_BOOLEAN, PROP_NONE);
@@ -3979,6 +4014,7 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna)
prop = RNA_def_property(srna, "cavity_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, cavity_type_items);
RNA_def_property_ui_text(prop, "Cavity Type", "Way to display the cavity shading");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_EDITOR_VIEW3D);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL);
prop = RNA_def_property(srna, "curvature_ridge_factor", PROP_FLOAT, PROP_FACTOR);
@@ -4199,6 +4235,14 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Shader AOV Name", "Name of the active Shader AOV");
RNA_def_property_flag(prop, PROP_HIDDEN);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "use_compositor", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_COMPOSITOR);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_boolean_default(prop, false);
+ RNA_def_property_ui_text(
+ prop, "Compositor", "Preview the compositor output inside the viewport");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL);
}
static void rna_def_space_view3d_overlay(BlenderRNA *brna)
@@ -4554,7 +4598,7 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
RNA_def_property_enum_items(prop, rna_enum_curve_display_handle_items);
RNA_def_property_ui_text(
prop, "Display Handles", "Limit the display of curve handles in edit mode");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, "rna_GPencil_update");
prop = RNA_def_property(srna, "show_curve_normals", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "overlay.edit_flag", V3D_OVERLAY_EDIT_CU_NORMALS);
@@ -4727,6 +4771,13 @@ static void rna_def_space_view3d_overlay(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0f, 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");
+
+ /* Developer Debug overlay */
+
+ prop = RNA_def_property(srna, "use_debug_freeze_view_culling", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "debug_flag", V3D_DEBUG_FREEZE_CULLING);
+ RNA_def_property_ui_text(prop, "Freeze Culling", "Freeze view culling bounds");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
}
static void rna_def_space_view3d(BlenderRNA *brna)
@@ -5901,7 +5952,7 @@ static void rna_def_space_text(BlenderRNA *brna)
prop = RNA_def_property(srna, "font_size", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "lheight");
- RNA_def_property_range(prop, 8, 32);
+ RNA_def_property_range(prop, 1, 256); /* Large range since Hi-DPI scales down size. */
RNA_def_property_ui_text(prop, "Font Size", "Font size to use for displaying the text");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_TEXT, NULL);
@@ -5982,7 +6033,7 @@ static void rna_def_space_dopesheet(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SpaceAction");
RNA_def_struct_ui_text(srna, "Space Dope Sheet Editor", "Dope Sheet space data");
- rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI));
+ rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_HUD));
/* data */
prop = RNA_def_property(srna, "action", PROP_POINTER, PROP_NONE);
@@ -6295,7 +6346,7 @@ static void rna_def_space_nla(BlenderRNA *brna)
RNA_def_struct_sdna(srna, "SpaceNla");
RNA_def_struct_ui_text(srna, "Space Nla Editor", "NLA editor space data");
- rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI));
+ rna_def_space_generic_show_region_toggles(srna, (1 << RGN_TYPE_UI) | (1 << RGN_TYPE_HUD));
/* display */
prop = RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE);
@@ -6395,7 +6446,7 @@ static void rna_def_space_console(BlenderRNA *brna)
/* display */
prop = RNA_def_property(srna, "font_size", PROP_INT, PROP_NONE); /* copied from text editor */
RNA_def_property_int_sdna(prop, NULL, "lheight");
- RNA_def_property_range(prop, 8, 32);
+ RNA_def_property_range(prop, 1, 256); /* Large range since Hi-DPI scales down size. */
RNA_def_property_ui_text(prop, "Font Size", "Font size to use for displaying the text");
RNA_def_property_update(prop, 0, "rna_SpaceConsole_rect_update");
@@ -6585,7 +6636,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
static const EnumPropertyItem display_size_items[] = {
{64, "TINY", 0, "Tiny", ""},
{96, "SMALL", 0, "Small", ""},
- {128, "NORMAL", 0, "Regular", ""},
+ {128, "NORMAL", 0, "Medium", ""},
{192, "LARGE", 0, "Large", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -6599,7 +6650,7 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Title", "Title for the file browser");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
- /* Use BYTESTRING rather than DIRPATH as subtype so UI code doesn't add OT_directory_browse
+ /* Use BYTESTRING rather than DIRPATH as sub-type so UI code doesn't add OT_directory_browse
* button when displaying this prop in the file browser (it would just open a file browser). That
* should be the only effective difference between the two. */
prop = RNA_def_property(srna, "directory", PROP_STRING, PROP_BYTESTRING);
diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c
index 3b28dc70e9e..26849bfa622 100644
--- a/source/blender/makesrna/intern/rna_texture.c
+++ b/source/blender/makesrna/intern/rna_texture.c
@@ -1235,7 +1235,7 @@ static void rna_def_texture_image(BlenderRNA *brna)
# if 0
/* XXX: did this as an array, but needs better descriptions than "1 2 3 4"
- * perhaps a new subtype could be added?
+ * perhaps a new sub-type could be added?
* --I actually used single values for this, maybe change later with a RNA_Rect thing? */
prop = RNA_def_property(srna, "crop_rectangle", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "cropxmin");
diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c
index dabb89bcd5e..9de32c24702 100644
--- a/source/blender/makesrna/intern/rna_ui.c
+++ b/source/blender/makesrna/intern/rna_ui.c
@@ -440,7 +440,7 @@ static PointerRNA rna_Panel_custom_data_get(PointerRNA *ptr)
}
/* UIList */
-static unsigned int rna_UIList_filter_const_FILTER_ITEM_get(PointerRNA *UNUSED(ptr))
+static int rna_UIList_filter_const_FILTER_ITEM_get(PointerRNA *UNUSED(ptr))
{
return UILST_FLT_ITEM;
}
@@ -473,7 +473,7 @@ static int rna_UIList_list_id_length(PointerRNA *ptr)
}
static void uilist_draw_item(uiList *ui_list,
- bContext *C,
+ const bContext *C,
uiLayout *layout,
PointerRNA *dataptr,
PointerRNA *itemptr,
@@ -507,7 +507,7 @@ static void uilist_draw_item(uiList *ui_list,
RNA_parameter_list_free(&list);
}
-static void uilist_draw_filter(uiList *ui_list, bContext *C, uiLayout *layout)
+static void uilist_draw_filter(uiList *ui_list, const bContext *C, uiLayout *layout)
{
extern FunctionRNA rna_UIList_draw_filter_func;
@@ -527,7 +527,7 @@ static void uilist_draw_filter(uiList *ui_list, bContext *C, uiLayout *layout)
}
static void uilist_filter_items(uiList *ui_list,
- bContext *C,
+ const bContext *C,
PointerRNA *dataptr,
const char *propname)
{
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index 1b416e4b6e5..fc68e8421d7 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -1808,8 +1808,7 @@ void RNA_api_ui_layout(StructRNA *srna)
func = RNA_def_function(
srna, "template_colormanaged_view_settings", "uiTemplateColormanagedViewSettings");
- RNA_def_function_ui_description(
- func, "Item. A widget to control color managed view settings settings.");
+ RNA_def_function_ui_description(func, "Item. A widget to control color managed view settings.");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
api_ui_item_rna_common(func);
# if 0
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 25eacdaab30..b2f37ee2ae1 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -38,23 +38,6 @@
#include "BLT_lang.h"
-#ifdef WITH_OPENSUBDIV
-static const EnumPropertyItem opensubdiv_compute_type_items[] = {
- {USER_OPENSUBDIV_COMPUTE_NONE, "NONE", 0, "None", ""},
- {USER_OPENSUBDIV_COMPUTE_CPU, "CPU", 0, "CPU", ""},
- {USER_OPENSUBDIV_COMPUTE_OPENMP, "OPENMP", 0, "OpenMP", ""},
- {USER_OPENSUBDIV_COMPUTE_OPENCL, "OPENCL", 0, "OpenCL", ""},
- {USER_OPENSUBDIV_COMPUTE_CUDA, "CUDA", 0, "CUDA", ""},
- {USER_OPENSUBDIV_COMPUTE_GLSL_TRANSFORM_FEEDBACK,
- "GLSL_TRANSFORM_FEEDBACK",
- 0,
- "GLSL Transform Feedback",
- ""},
- {USER_OPENSUBDIV_COMPUTE_GLSL_COMPUTE, "GLSL_COMPUTE", 0, "GLSL Compute", ""},
- {0, NULL, 0, NULL, NULL},
-};
-#endif
-
const EnumPropertyItem rna_enum_preference_section_items[] = {
{USER_SECTION_INTERFACE, "INTERFACE", 0, "Interface", ""},
{USER_SECTION_THEME, "THEMES", 0, "Themes", ""},
@@ -189,10 +172,6 @@ static const EnumPropertyItem rna_enum_userdef_viewport_aa_items[] = {
# include "UI_interface.h"
-# ifdef WITH_OPENSUBDIV
-# include "opensubdiv_capi.h"
-# endif
-
# ifdef WITH_SDL_DYNLOAD
# include "sdlew.h"
# endif
@@ -728,55 +707,6 @@ static PointerRNA rna_Theme_space_list_generic_get(PointerRNA *ptr)
return rna_pointer_inherit_refine(ptr, &RNA_ThemeSpaceListGeneric, ptr->data);
}
-# ifdef WITH_OPENSUBDIV
-static const EnumPropertyItem *rna_userdef_opensubdiv_compute_type_itemf(bContext *UNUSED(C),
- PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
- bool *r_free)
-{
- EnumPropertyItem *item = NULL;
- int totitem = 0;
- int evaluators = openSubdiv_getAvailableEvaluators();
-
- RNA_enum_items_add_value(
- &item, &totitem, opensubdiv_compute_type_items, USER_OPENSUBDIV_COMPUTE_NONE);
-
-# define APPEND_COMPUTE(compute) \
- if (evaluators & OPENSUBDIV_EVALUATOR_##compute) { \
- RNA_enum_items_add_value( \
- &item, &totitem, opensubdiv_compute_type_items, USER_OPENSUBDIV_COMPUTE_##compute); \
- } \
- ((void)0)
-
- APPEND_COMPUTE(CPU);
- APPEND_COMPUTE(OPENMP);
- APPEND_COMPUTE(OPENCL);
- APPEND_COMPUTE(CUDA);
- APPEND_COMPUTE(GLSL_TRANSFORM_FEEDBACK);
- APPEND_COMPUTE(GLSL_COMPUTE);
-
-# undef APPEND_COMPUTE
-
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
-
- return item;
-}
-
-static void rna_userdef_opensubdiv_update(Main *bmain,
- Scene *UNUSED(scene),
- PointerRNA *UNUSED(ptr))
-{
- Object *object;
-
- for (object = bmain->objects.first; object; object = object->id.next) {
- DEG_id_tag_update(&object->id, ID_RECALC_TRANSFORM);
- }
- USERDEF_TAG_DIRTY;
-}
-
-# endif
-
static const EnumPropertyItem *rna_userdef_audio_device_itemf(bContext *UNUSED(C),
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
@@ -1755,7 +1685,7 @@ static void rna_def_userdef_theme_space_common(StructRNA *srna)
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
/* buttons */
- /* if (! ELEM(spacetype, SPACE_PROPERTIES, SPACE_OUTLINER)) { */
+ // if (!ELEM(spacetype, SPACE_PROPERTIES, SPACE_OUTLINER)) {
prop = RNA_def_property(srna, "button", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Region Background", "");
@@ -1807,7 +1737,7 @@ static void rna_def_userdef_theme_space_common(StructRNA *srna)
RNA_def_property_ui_text(prop, "Tab Outline", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
- /* } */
+ // }
}
static void rna_def_userdef_theme_space_gradient(BlenderRNA *brna)
@@ -1968,6 +1898,7 @@ static void rna_def_userdef_theme_spaces_edge(StructRNA *srna)
prop = RNA_def_property(srna, "edge_crease", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 3);
RNA_def_property_ui_text(prop, "Edge Crease", "");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_WINDOWMANAGER);
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
prop = RNA_def_property(srna, "edge_bevel", PROP_FLOAT, PROP_COLOR_GAMMA);
@@ -2849,7 +2780,7 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna)
prop = RNA_def_property(srna, "wire", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_float_sdna(prop, NULL, "wire");
- RNA_def_property_array(prop, 3);
+ RNA_def_property_array(prop, 4);
RNA_def_property_ui_text(prop, "Wires", "");
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
@@ -5635,8 +5566,8 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"VBO Time Out",
- "Time since last access of a GL Vertex buffer object in seconds after which it is freed "
- "(set to 0 to keep vbo allocated)");
+ "Time since last access of a GL vertex buffer object in seconds after which it is freed "
+ "(set to 0 to keep VBO allocated)");
prop = RNA_def_property(srna, "vbo_collection_rate", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "vbocollectrate");
@@ -5644,7 +5575,7 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_ui_text(
prop,
"VBO Collection Rate",
- "Number of seconds between each run of the GL Vertex buffer object garbage collector");
+ "Number of seconds between each run of the GL vertex buffer object garbage collector");
/* Select */
@@ -5699,17 +5630,6 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Audio Channels", "Audio channel count");
RNA_def_property_update(prop, 0, "rna_UserDef_audio_update");
-# ifdef WITH_OPENSUBDIV
- prop = RNA_def_property(srna, "opensubdiv_compute_type", PROP_ENUM, PROP_NONE);
- RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
- RNA_def_property_enum_sdna(prop, NULL, "opensubdiv_compute_type");
- RNA_def_property_enum_items(prop, opensubdiv_compute_type_items);
- RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_userdef_opensubdiv_compute_type_itemf");
- RNA_def_property_ui_text(
- prop, "OpenSubdiv Compute Type", "Type of computer back-end used with OpenSubdiv");
- RNA_def_property_update(prop, NC_SPACE | ND_SPACE_PROPERTIES, "rna_userdef_opensubdiv_update");
-# endif
-
# ifdef WITH_CYCLES
prop = RNA_def_property(srna, "legacy_compute_device_type", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "compute_device_type");
@@ -6382,15 +6302,6 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"Enable library overrides automatic resync detection and process on file load. Disable when "
"dealing with older .blend files that need manual Resync (Enforce) handling");
- prop = RNA_def_property(srna, "use_override_new_fully_editable", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "use_override_new_fully_editable", 1);
- RNA_def_property_ui_text(
- prop,
- "Override New Fully Editable",
- "Make all override of a hierarchy fully user-editable by default when creating a new "
- "override (if that option is disabled, most overrides created as part of a hierarchy will "
- "not be editable by the user by default)");
-
prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1);
RNA_def_property_ui_text(
@@ -6419,6 +6330,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Sculpt Mode Tilt Support", "Support for pen tablet tilt events in Sculpt Mode");
+ prop = RNA_def_property(srna, "use_realtime_compositor", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_realtime_compositor", 1);
+ RNA_def_property_ui_text(prop, "Realtime Compositor", "Enable the new realtime compositor");
+
prop = RNA_def_property(srna, "use_sculpt_texture_paint", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_texture_paint", 1);
RNA_def_property_ui_text(prop, "Sculpt Texture Paint", "Use texture painting in Sculpt Mode");
@@ -6457,6 +6372,14 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
prop = RNA_def_property(srna, "enable_eevee_next", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "enable_eevee_next", 1);
RNA_def_property_ui_text(prop, "EEVEE Next", "Enable the new EEVEE codebase, requires restart");
+
+ prop = RNA_def_property(srna, "use_viewport_debug", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "use_viewport_debug", 1);
+ RNA_def_property_ui_text(prop,
+ "Viewport Debug",
+ "Enable viewport debugging options for developers in the overlays "
+ "pop-over");
+ RNA_def_property_update(prop, 0, "rna_userdef_ui_update");
}
static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 7893a2f4bc0..7fd590005fd 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -607,14 +607,14 @@ static PointerRNA rna_OperatorMacro_properties_get(PointerRNA *ptr)
static void rna_Event_ascii_get(PointerRNA *ptr, char *value)
{
const wmEvent *event = ptr->data;
- value[0] = event->ascii;
+ value[0] = WM_event_utf8_to_ascii(event);
value[1] = '\0';
}
static int rna_Event_ascii_length(PointerRNA *ptr)
{
const wmEvent *event = ptr->data;
- return (event->ascii) ? 1 : 0;
+ return WM_event_utf8_to_ascii(event) ? 1 : 0;
}
static void rna_Event_unicode_get(PointerRNA *ptr, char *value)
@@ -864,6 +864,21 @@ static void rna_Window_view_layer_set(PointerRNA *ptr,
WM_window_set_active_view_layer(win, view_layer);
}
+static void rna_KeyMap_modal_event_values_items_begin(CollectionPropertyIterator *iter,
+ PointerRNA *ptr)
+{
+ wmKeyMap *km = ptr->data;
+
+ const EnumPropertyItem *items = rna_enum_keymap_propvalue_items;
+ if ((km->flag & KEYMAP_MODAL) != 0 && km->modal_items != NULL) {
+ items = km->modal_items;
+ }
+
+ const int totitem = RNA_enum_items_count(items);
+
+ rna_iterator_array_begin(iter, (void *)items, sizeof(EnumPropertyItem), totitem, false, NULL);
+}
+
static PointerRNA rna_KeyMapItem_properties_get(PointerRNA *ptr)
{
wmKeyMapItem *kmi = ptr->data;
@@ -2342,6 +2357,7 @@ static void rna_def_window(BlenderRNA *brna)
"rna_Window_screen_set",
NULL,
"rna_Window_screen_assign_poll");
+ RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_SCREEN);
RNA_def_property_flag(prop, PROP_NEVER_NULL | PROP_EDITABLE | PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, 0, "rna_workspace_screen_update");
@@ -2617,6 +2633,23 @@ static void rna_def_keyconfig(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Children Expanded", "Children expanded in the user interface");
RNA_def_property_ui_icon(prop, ICON_DISCLOSURE_TRI_RIGHT, 1);
+ prop = RNA_def_property(srna, "modal_event_values", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_struct_type(prop, "EnumPropertyItem");
+ RNA_def_property_collection_funcs(prop,
+ "rna_KeyMap_modal_event_values_items_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ RNA_def_property_ui_text(prop,
+ "Modal Events",
+ "Give access to the possible event values of this modal keymap's items "
+ "(#KeyMapItem.propvalue), for API introspection");
+
RNA_api_keymap(srna);
/* KeyMapItem */
diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c
index b9f36d35ee8..b82458c4442 100644
--- a/source/blender/makesrna/intern/rna_wm_api.c
+++ b/source/blender/makesrna/intern/rna_wm_api.c
@@ -625,7 +625,7 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win,
return NULL;
}
}
- if (ELEM(type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(type)) {
if (value != KM_NOTHING) {
BKE_report(reports, RPT_ERROR, "Value: must be 'NOTHING' for motion");
return NULL;
@@ -639,16 +639,12 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win,
}
/* TODO: validate NDOF. */
- char ascii = 0;
if (unicode != NULL) {
int len = BLI_str_utf8_size(unicode);
if (len == -1 || unicode[len] != '\0') {
BKE_report(reports, RPT_ERROR, "Only a single character supported");
return NULL;
}
- if (len == 1 && isascii(unicode[0])) {
- ascii = unicode[0];
- }
}
wmEvent e = *win->eventstate;
@@ -672,10 +668,8 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win,
e.modifier |= KM_OSKEY;
}
- e.ascii = '\0';
e.utf8_buf[0] = '\0';
if (unicode != NULL) {
- e.ascii = ascii;
STRNCPY(e.utf8_buf, unicode);
}
diff --git a/source/blender/makesrna/intern/rna_wm_gizmo.c b/source/blender/makesrna/intern/rna_wm_gizmo.c
index 6c3d96726bb..a4630415ccd 100644
--- a/source/blender/makesrna/intern/rna_wm_gizmo.c
+++ b/source/blender/makesrna/intern/rna_wm_gizmo.c
@@ -353,7 +353,7 @@ static PointerRNA rna_Gizmo_properties_get(PointerRNA *ptr)
}
# define RNA_GIZMO_FLAG_RO_DEF(func_id, member_id, flag_value) \
- static int rna_Gizmo_##func_id##_get(PointerRNA *ptr) \
+ static bool rna_Gizmo_##func_id##_get(PointerRNA *ptr) \
{ \
wmGizmo *gz = ptr->data; \
return (gz->member_id & flag_value) != 0; \
@@ -1398,6 +1398,11 @@ static void rna_def_gizmogroup(BlenderRNA *brna)
0,
"Tool Init",
"Postpone running until tool operator run (when used with a tool)"},
+ {WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP,
+ "TOOL_FALLBACK_KEYMAP",
+ 0,
+ "Use fallback tools keymap",
+ "Add fallback tools keymap to this gizmo type"},
{WM_GIZMOGROUPTYPE_VR_REDRAWS,
"VR_REDRAWS",
0,
diff --git a/source/blender/makesrna/intern/rna_wm_gizmo_api.c b/source/blender/makesrna/intern/rna_wm_gizmo_api.c
index 419dfa68305..760121d2279 100644
--- a/source/blender/makesrna/intern/rna_wm_gizmo_api.c
+++ b/source/blender/makesrna/intern/rna_wm_gizmo_api.c
@@ -211,7 +211,15 @@ void RNA_api_gizmo(StructRNA *srna)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
RNA_def_property_ui_text(parm, "", "The matrix to transform");
- RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX);
+ RNA_def_int(func,
+ "select_id",
+ -1,
+ -1,
+ INT_MAX,
+ "ID to use when gizmo is selectable. Use -1 when not selecting",
+ "",
+ -1,
+ INT_MAX);
/* draw_preset_box */
func = RNA_def_function(srna, "draw_preset_arrow", "rna_gizmo_draw_preset_arrow");
@@ -221,7 +229,15 @@ void RNA_api_gizmo(StructRNA *srna)
RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
RNA_def_property_ui_text(parm, "", "The matrix to transform");
RNA_def_enum(func, "axis", rna_enum_object_axis_items, 2, "", "Arrow Orientation");
- RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX);
+ RNA_def_int(func,
+ "select_id",
+ -1,
+ -1,
+ INT_MAX,
+ "ID to use when gizmo is selectable. Use -1 when not selecting",
+ "",
+ -1,
+ INT_MAX);
func = RNA_def_function(srna, "draw_preset_circle", "rna_gizmo_draw_preset_circle");
RNA_def_function_ui_description(func, "Draw a box");
@@ -230,7 +246,15 @@ void RNA_api_gizmo(StructRNA *srna)
RNA_def_property_multi_array(parm, 2, rna_matrix_dimsize_4x4);
RNA_def_property_ui_text(parm, "", "The matrix to transform");
RNA_def_enum(func, "axis", rna_enum_object_axis_items, 2, "", "Arrow Orientation");
- RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX);
+ RNA_def_int(func,
+ "select_id",
+ -1,
+ -1,
+ INT_MAX,
+ "ID to use when gizmo is selectable. Use -1 when not selecting",
+ "",
+ -1,
+ INT_MAX);
/* -------------------------------------------------------------------- */
/* Other Shapes */
@@ -243,7 +267,15 @@ void RNA_api_gizmo(StructRNA *srna)
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_int(func, "face_map", 0, 0, INT_MAX, "Face map index", "", 0, INT_MAX);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
- RNA_def_int(func, "select_id", -1, -1, INT_MAX, "Zero when not selecting", "", -1, INT_MAX);
+ RNA_def_int(func,
+ "select_id",
+ -1,
+ -1,
+ INT_MAX,
+ "ID to use when gizmo is selectable. Use -1 when not selecting",
+ "",
+ -1,
+ INT_MAX);
/* -------------------------------------------------------------------- */
/* Property API */
diff --git a/source/blender/makesrna/intern/rna_workspace.c b/source/blender/makesrna/intern/rna_workspace.c
index a0d89b8b15a..2294c2c2b2d 100644
--- a/source/blender/makesrna/intern/rna_workspace.c
+++ b/source/blender/makesrna/intern/rna_workspace.c
@@ -193,7 +193,7 @@ static int rna_WorkSpaceTool_index_get(PointerRNA *ptr)
return (tref->runtime) ? tref->runtime->index : 0;
}
-static int rna_WorkSpaceTool_has_datablock_get(PointerRNA *ptr)
+static bool rna_WorkSpaceTool_has_datablock_get(PointerRNA *ptr)
{
bToolRef *tref = ptr->data;
return (tref->runtime) ? (tref->runtime->data_block[0] != '\0') : false;
diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt
index 1aac3c2191d..8bace2e048c 100644
--- a/source/blender/modifiers/CMakeLists.txt
+++ b/source/blender/modifiers/CMakeLists.txt
@@ -65,7 +65,6 @@ 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
@@ -105,7 +104,6 @@ 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
@@ -217,7 +215,6 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
- add_definitions(-DWITH_NEW_CURVES_TYPE)
endif()
# So we can have special tricks in modifier system.
diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c
index f6e8a26f0e1..8e627f9964d 100644
--- a/source/blender/modifiers/intern/MOD_armature.c
+++ b/source/blender/modifiers/intern/MOD_armature.c
@@ -68,9 +68,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
tamd->vert_coords_prev = NULL;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks)
{
/* ask for vertexgroups */
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
@@ -125,7 +123,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
DEG_add_object_relation(ctx->node, amd->object, DEG_OB_COMP_TRANSFORM, "Armature Modifier");
}
- DEG_add_modifier_to_transform_relation(ctx->node, "Armature Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Armature Modifier");
}
static void deformVerts(ModifierData *md,
@@ -211,8 +209,7 @@ static void deformMatrices(ModifierData *md,
int verts_num)
{
ArmatureModifierData *amd = (ArmatureModifierData *)md;
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
BKE_armature_deform_coords_with_mesh(amd->object,
ctx->object,
@@ -266,7 +263,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
}
ModifierTypeInfo modifierType_Armature = {
- /* name */ "Armature",
+ /* name */ N_("Armature"),
/* structName */ "ArmatureModifierData",
/* structSize */ sizeof(ArmatureModifierData),
/* srna */ &RNA_ArmatureModifier,
diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c
index 77915702ee4..7feff30968f 100644
--- a/source/blender/modifiers/intern/MOD_array.c
+++ b/source/blender/modifiers/intern/MOD_array.c
@@ -93,7 +93,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
if (need_transform_dependency) {
- DEG_add_modifier_to_transform_relation(ctx->node, "Array Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Array Modifier");
}
}
@@ -279,18 +279,22 @@ static void mesh_merge_transform(Mesh *result,
MEdge *me;
MLoop *ml;
MPoly *mp;
+ MVert *result_verts = BKE_mesh_verts_for_write(result);
+ MEdge *result_edges = BKE_mesh_edges_for_write(result);
+ MPoly *result_polys = BKE_mesh_polys_for_write(result);
+ MLoop *result_loops = BKE_mesh_loops_for_write(result);
CustomData_copy_data(&cap_mesh->vdata, &result->vdata, 0, cap_verts_index, cap_nverts);
CustomData_copy_data(&cap_mesh->edata, &result->edata, 0, cap_edges_index, cap_nedges);
CustomData_copy_data(&cap_mesh->ldata, &result->ldata, 0, cap_loops_index, cap_nloops);
CustomData_copy_data(&cap_mesh->pdata, &result->pdata, 0, cap_polys_index, cap_npolys);
- mv = result->mvert + cap_verts_index;
+ mv = result_verts + cap_verts_index;
for (i = 0; i < cap_nverts; i++, mv++) {
mul_m4_v3(cap_offset, mv->co);
/* Reset MVert flags for caps */
- mv->flag = mv->bweight = 0;
+ mv->flag = 0;
}
/* We have to correct normals too, if we do not tag them as dirty later! */
@@ -303,26 +307,26 @@ static void mesh_merge_transform(Mesh *result,
}
/* remap the vertex groups if necessary */
- if (result->dvert != NULL) {
- BKE_object_defgroup_index_map_apply(
- &result->dvert[cap_verts_index], cap_nverts, remap, remap_len);
+ if (BKE_mesh_deform_verts(result) != NULL) {
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(result);
+ BKE_object_defgroup_index_map_apply(&dvert[cap_verts_index], cap_nverts, remap, remap_len);
}
/* adjust cap edge vertex indices */
- me = result->medge + cap_edges_index;
+ me = result_edges + cap_edges_index;
for (i = 0; i < cap_nedges; i++, me++) {
me->v1 += cap_verts_index;
me->v2 += cap_verts_index;
}
/* adjust cap poly loopstart indices */
- mp = result->mpoly + cap_polys_index;
+ mp = result_polys + cap_polys_index;
for (i = 0; i < cap_npolys; i++, mp++) {
mp->loopstart += cap_loops_index;
}
/* adjust cap loop vertex and edge indices */
- ml = result->mloop + cap_loops_index;
+ ml = result_loops + cap_loops_index;
for (i = 0; i < cap_nloops; i++, ml++) {
ml->v += cap_verts_index;
ml->e += cap_edges_index;
@@ -352,11 +356,8 @@ static void mesh_merge_transform(Mesh *result,
static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
const ModifierEvalContext *ctx,
- Mesh *mesh)
+ const Mesh *mesh)
{
- const MVert *src_mvert;
- MVert *result_dm_verts;
-
MEdge *me;
MLoop *ml;
MPoly *mp;
@@ -402,7 +403,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
start_cap_ob, ctx->object, &vgroup_start_cap_remap_len);
}
- start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob, false);
+ start_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(start_cap_ob);
if (start_cap_mesh) {
start_cap_nverts = start_cap_mesh->totvert;
start_cap_nedges = start_cap_mesh->totedge;
@@ -417,7 +418,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
end_cap_ob, ctx->object, &vgroup_end_cap_remap_len);
}
- end_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(end_cap_ob, false);
+ end_cap_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(end_cap_ob);
if (end_cap_mesh) {
end_cap_nverts = end_cap_mesh->totvert;
end_cap_nedges = end_cap_mesh->totedge;
@@ -429,7 +430,10 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
/* Build up offset array, accumulating all settings options. */
unit_m4(offset);
- src_mvert = mesh->mvert;
+ const MVert *src_verts = BKE_mesh_verts(mesh);
+ const MEdge *src_edges = BKE_mesh_edges(mesh);
+ const MPoly *src_polys = BKE_mesh_polys(mesh);
+ const MLoop *src_loops = BKE_mesh_loops(mesh);
if (amd->offset_type & MOD_ARR_OFF_CONST) {
add_v3_v3(offset[3], amd->offset);
@@ -440,7 +444,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
const MVert *src_mv;
INIT_MINMAX(min, max);
- for (src_mv = src_mvert, j = chunk_nverts; j--; src_mv++) {
+ for (src_mv = src_verts, j = chunk_nverts; j--; src_mv++) {
minmax_v3v3_v3(min, max, src_mv->co);
}
@@ -478,7 +482,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
}
/* About 67 million vertices max seems a decent limit for now. */
- const size_t max_vertices_num = 1 << 26;
+ const size_t max_verts_num = 1 << 26;
/* calculate the maximum number of copies which will fit within the
* prescribed length */
@@ -496,7 +500,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
* vertices.
*/
if (((size_t)count * (size_t)chunk_nverts + (size_t)start_cap_nverts +
- (size_t)end_cap_nverts) > max_vertices_num) {
+ (size_t)end_cap_nverts) > max_verts_num) {
count = 1;
offset_is_too_small = true;
}
@@ -518,7 +522,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
* vertices.
*/
else if (((size_t)count * (size_t)chunk_nverts + (size_t)start_cap_nverts +
- (size_t)end_cap_nverts) > max_vertices_num) {
+ (size_t)end_cap_nverts) > max_verts_num) {
count = 1;
BKE_modifier_set_error(ctx->object,
&amd->modifier,
@@ -539,7 +543,10 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
/* Initialize a result dm */
result = BKE_mesh_new_nomain_from_template(
mesh, result_nverts, result_nedges, 0, result_nloops, result_npolys);
- result_dm_verts = result->mvert;
+ MVert *result_verts = BKE_mesh_verts_for_write(result);
+ MEdge *result_edges = BKE_mesh_edges_for_write(result);
+ MPoly *result_polys = BKE_mesh_polys_for_write(result);
+ MLoop *result_loops = BKE_mesh_loops_for_write(result);
if (use_merge) {
/* Will need full_doubles_map for handling merge */
@@ -553,17 +560,17 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
CustomData_copy_data(&mesh->ldata, &result->ldata, 0, 0, chunk_nloops);
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, chunk_npolys);
- /* Subsurf for eg won't have mesh data in the custom data arrays.
- * now add mvert/medge/mpoly layers. */
+ /* Subdivision-surface for eg won't have mesh data in the custom-data arrays.
+ * Now add #MVert/#MEdge/#MPoly layers. */
if (!CustomData_has_layer(&mesh->vdata, CD_MVERT)) {
- memcpy(result->mvert, mesh->mvert, sizeof(*result->mvert) * mesh->totvert);
+ memcpy(result_verts, src_verts, sizeof(MVert) * mesh->totvert);
}
if (!CustomData_has_layer(&mesh->edata, CD_MEDGE)) {
- memcpy(result->medge, mesh->medge, sizeof(*result->medge) * mesh->totedge);
+ memcpy(result_edges, src_edges, sizeof(MEdge) * mesh->totedge);
}
if (!CustomData_has_layer(&mesh->pdata, CD_MPOLY)) {
- memcpy(result->mloop, mesh->mloop, sizeof(*result->mloop) * mesh->totloop);
- memcpy(result->mpoly, mesh->mpoly, sizeof(*result->mpoly) * mesh->totpoly);
+ memcpy(result_loops, src_loops, sizeof(MLoop) * mesh->totloop);
+ memcpy(result_polys, src_polys, sizeof(MPoly) * mesh->totpoly);
}
/* Remember first chunk, in case of cap merge */
@@ -594,7 +601,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
/* apply offset to all new verts */
for (i = 0; i < chunk_nverts; i++) {
const int i_dst = vert_offset + i;
- mul_m4_v3(current_offset, result_dm_verts[i_dst].co);
+ mul_m4_v3(current_offset, result_verts[i_dst].co);
/* We have to correct normals too, if we do not tag them as dirty! */
if (!use_recalc_normals) {
@@ -605,19 +612,19 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
}
/* adjust edge vertex indices */
- me = result->medge + c * chunk_nedges;
+ me = result_edges + c * chunk_nedges;
for (i = 0; i < chunk_nedges; i++, me++) {
me->v1 += c * chunk_nverts;
me->v2 += c * chunk_nverts;
}
- mp = result->mpoly + c * chunk_npolys;
+ mp = result_polys + c * chunk_npolys;
for (i = 0; i < chunk_npolys; i++, mp++) {
mp->loopstart += c * chunk_nloops;
}
/* adjust loop vertex and edge indices */
- ml = result->mloop + c * chunk_nloops;
+ ml = result_loops + c * chunk_nloops;
for (i = 0; i < chunk_nloops; i++, ml++) {
ml->v += c * chunk_nverts;
ml->e += c * chunk_nedges;
@@ -638,8 +645,8 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
while (target != -1 && !ELEM(full_doubles_map[target], -1, target)) {
/* If target is already mapped, we only follow that mapping if final target remains
* close enough from current vert (otherwise no mapping at all). */
- if (compare_len_v3v3(result_dm_verts[this_chunk_index].co,
- result_dm_verts[full_doubles_map[target]].co,
+ if (compare_len_v3v3(result_verts[this_chunk_index].co,
+ result_verts[full_doubles_map[target]].co,
amd->merge_dist)) {
target = full_doubles_map[target];
}
@@ -653,7 +660,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
}
else {
dm_mvert_map_doubles(full_doubles_map,
- result_dm_verts,
+ result_verts,
(c - 1) * chunk_nverts,
chunk_nverts,
c * chunk_nverts,
@@ -691,7 +698,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
if (use_merge && (amd->flags & MOD_ARR_MERGEFINAL) && (count > 1)) {
/* Merge first and last copies */
dm_mvert_map_doubles(full_doubles_map,
- result_dm_verts,
+ result_verts,
last_chunk_start,
last_chunk_nverts,
first_chunk_start,
@@ -721,7 +728,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
/* Identify doubles with first chunk */
if (use_merge) {
dm_mvert_map_doubles(full_doubles_map,
- result_dm_verts,
+ result_verts,
first_chunk_start,
first_chunk_nverts,
start_cap_start,
@@ -751,7 +758,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
/* Identify doubles with last chunk */
if (use_merge) {
dm_mvert_map_doubles(full_doubles_map,
- result_dm_verts,
+ result_verts,
last_chunk_start,
last_chunk_nverts,
end_cap_start,
@@ -1002,7 +1009,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Array = {
- /* name */ "Array",
+ /* name */ N_("Array"),
/* structName */ "ArrayModifierData",
/* structSize */ sizeof(ArrayModifierData),
/* srna */ &RNA_ArrayModifier,
diff --git a/source/blender/modifiers/intern/MOD_bevel.c b/source/blender/modifiers/intern/MOD_bevel.c
index c634873cfe4..48d355d8ca7 100644
--- a/source/blender/modifiers/intern/MOD_bevel.c
+++ b/source/blender/modifiers/intern/MOD_bevel.c
@@ -64,9 +64,7 @@ static void copyData(const ModifierData *md_src, ModifierData *md_dst, const int
bmd_dst->custom_profile = BKE_curveprofile_copy(bmd_src->custom_profile);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
BevelModifierData *bmd = (BevelModifierData *)md;
@@ -74,6 +72,10 @@ static void requiredDataMask(Object *UNUSED(ob),
if (bmd->defgrp_name[0] != '\0') {
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
}
+ if (bmd->lim_flags & MOD_BEVEL_WEIGHT) {
+ r_cddata_masks->vmask |= CD_MASK_BWEIGHT;
+ r_cddata_masks->emask |= CD_MASK_BWEIGHT;
+ }
}
/*
@@ -88,7 +90,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BMVert *v;
float weight, weight2;
int vgroup = -1;
- MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
BevelModifierData *bmd = (BevelModifierData *)md;
const float threshold = cosf(bmd->bevel_angle + 0.000000175f);
const bool do_clamp = !(bmd->flags & MOD_BEVEL_OVERLAP_OK);
@@ -417,7 +419,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_Bevel = {
- /* name */ "Bevel",
+ /* name */ N_("Bevel"),
/* structName */ "BevelModifierData",
/* structSize */ sizeof(BevelModifierData),
/* srna */ &RNA_BevelModifier,
diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc
index 09676d0e9ee..7316baebfb6 100644
--- a/source/blender/modifiers/intern/MOD_boolean.cc
+++ b/source/blender/modifiers/intern/MOD_boolean.cc
@@ -117,7 +117,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
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");
+ DEG_add_depends_on_transform_relation(ctx->node, "Boolean Modifier");
}
static Mesh *get_quick_mesh(
@@ -144,11 +144,9 @@ static Mesh *get_quick_mesh(
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);
+ MutableSpan<MVert> verts = result->verts_for_write();
+ for (const int i : verts.index_range()) {
+ mul_m4_v3(omat, verts[i].co);
}
BKE_mesh_tag_coords_changed(result);
@@ -429,7 +427,7 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
}
if (bmd->flag & eBooleanModifierFlag_Object) {
- Mesh *mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object, false);
+ Mesh *mesh_operand = BKE_modifier_get_evaluated_mesh_from_evaluated_object(bmd->object);
if (!mesh_operand) {
return mesh;
}
@@ -444,7 +442,7 @@ static Mesh *exact_boolean_mesh(BooleanModifierData *bmd,
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);
+ Mesh *collection_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob);
if (!collection_mesh) {
continue;
}
@@ -505,8 +503,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
Object *operand_ob = bmd->object;
- Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob,
- false);
+ Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob);
if (mesh_operand_ob) {
/* XXX This is utterly non-optimal, we may go from a bmesh to a mesh back to a bmesh!
@@ -540,8 +537,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
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);
+ Mesh *mesh_operand_ob = BKE_modifier_get_evaluated_mesh_from_evaluated_object(operand_ob);
if (mesh_operand_ob == nullptr) {
continue;
@@ -574,9 +570,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return result;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks)
{
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
r_cddata_masks->emask |= CD_MASK_MEDGE;
@@ -639,7 +633,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Boolean = {
- /* name */ "Boolean",
+ /* name */ N_("Boolean"),
/* structName */ "BooleanModifierData",
/* structSize */ sizeof(BooleanModifierData),
/* srna */ &RNA_BooleanModifier,
diff --git a/source/blender/modifiers/intern/MOD_build.c b/source/blender/modifiers/intern/MOD_build.c
index 687ff04cedf..78724d6a2a1 100644
--- a/source/blender/modifiers/intern/MOD_build.c
+++ b/source/blender/modifiers/intern/MOD_build.c
@@ -13,6 +13,8 @@
#include "BLI_math_vector.h"
#include "BLI_rand.h"
+#include "BLT_translation.h"
+
#include "DNA_defaults.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -60,7 +62,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
int *vertMap, *edgeMap, *faceMap;
float frac;
MPoly *mpoly_dst;
- MLoop *ml_dst, *ml_src /*, *mloop_dst */;
+ MLoop *ml_dst;
+ const MLoop *ml_src;
GHashIterator gh_iter;
/* maps vert indices in old mesh to indices in new mesh */
GHash *vertHash = BLI_ghash_int_new("build ve apply gh");
@@ -72,10 +75,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
const int vert_src_num = mesh->totvert;
const int edge_src_num = mesh->totedge;
const int poly_src_num = mesh->totpoly;
- MPoly *mpoly_src = mesh->mpoly;
- MLoop *mloop_src = mesh->mloop;
- MEdge *medge_src = mesh->medge;
- MVert *mvert_src = mesh->mvert;
+ const MVert *mvert_src = BKE_mesh_verts(mesh);
+ const MEdge *medge_src = BKE_mesh_edges(mesh);
+ const MPoly *mpoly_src = BKE_mesh_polys(mesh);
+ const MLoop *mloop_src = BKE_mesh_loops(mesh);
vertMap = MEM_malloc_arrayN(vert_src_num, sizeof(*vertMap), "build modifier vertMap");
edgeMap = MEM_malloc_arrayN(edge_src_num, sizeof(*edgeMap), "build modifier edgeMap");
@@ -97,8 +100,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
/* if there's at least one face, build based on faces */
if (faces_dst_num) {
- MPoly *mpoly, *mp;
- MLoop *ml, *mloop;
+ const MPoly *mpoly, *mp;
+ const MLoop *ml, *mloop;
uintptr_t hash_num, hash_num_alt;
if (bmd->flag & MOD_BUILD_FLAG_RANDOMIZE) {
@@ -133,7 +136,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
hash_num = 0;
hash_num_alt = 0;
for (i = 0; i < edge_src_num; i++, hash_num_alt++) {
- MEdge *me = medge_src + i;
+ const MEdge *me = medge_src + i;
if (BLI_ghash_haskey(vertHash, POINTER_FROM_INT(me->v1)) &&
BLI_ghash_haskey(vertHash, POINTER_FROM_INT(me->v2))) {
@@ -145,7 +148,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
BLI_assert(hash_num == BLI_ghash_len(edgeHash));
}
else if (edges_dst_num) {
- MEdge *medge, *me;
+ const MEdge *medge, *me;
uintptr_t hash_num;
if (bmd->flag & MOD_BUILD_FLAG_RANDOMIZE) {
@@ -199,6 +202,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
/* now we know the number of verts, edges and faces, we can create the mesh. */
result = BKE_mesh_new_nomain_from_template(
mesh, BLI_ghash_len(vertHash), BLI_ghash_len(edgeHash), 0, loops_dst_num, faces_dst_num);
+ MVert *result_verts = BKE_mesh_verts_for_write(result);
+ MEdge *result_edges = BKE_mesh_edges_for_write(result);
+ MPoly *result_polys = BKE_mesh_polys_for_write(result);
+ MLoop *result_loops = BKE_mesh_loops_for_write(result);
/* copy the vertices across */
GHASH_ITER (gh_iter, vertHash) {
@@ -208,7 +215,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
int newIndex = POINTER_AS_INT(BLI_ghashIterator_getValue(&gh_iter));
source = mvert_src[oldIndex];
- dest = &result->mvert[newIndex];
+ dest = &result_verts[newIndex];
CustomData_copy_data(&mesh->vdata, &result->vdata, oldIndex, newIndex, 1);
*dest = source;
@@ -221,7 +228,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
int oldIndex = POINTER_AS_INT(BLI_ghash_lookup(edgeHash, POINTER_FROM_INT(i)));
source = medge_src[oldIndex];
- dest = &result->medge[i];
+ dest = &result_edges[i];
source.v1 = POINTER_AS_INT(BLI_ghash_lookup(vertHash, POINTER_FROM_INT(source.v1)));
source.v2 = POINTER_AS_INT(BLI_ghash_lookup(vertHash, POINTER_FROM_INT(source.v2)));
@@ -230,13 +237,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, struct
*dest = source;
}
- mpoly_dst = result->mpoly;
- ml_dst = result->mloop;
+ mpoly_dst = result_polys;
+ ml_dst = result_loops;
/* copy the faces across, remapping indices */
k = 0;
for (i = 0; i < faces_dst_num; i++) {
- MPoly *source;
+ const MPoly *source;
MPoly *dest;
source = mpoly_src + faceMap[i];
@@ -311,7 +318,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Build = {
- /* name */ "Build",
+ /* name */ N_("Build"),
/* structName */ "BuildModifierData",
/* structSize */ sizeof(BuildModifierData),
/* srna */ &RNA_BuildModifier,
diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c
index 3916551bc90..1a4942fcaf1 100644
--- a/source/blender/modifiers/intern/MOD_cast.c
+++ b/source/blender/modifiers/intern/MOD_cast.c
@@ -63,9 +63,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
return false;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
CastModifierData *cmd = (CastModifierData *)md;
@@ -87,7 +85,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
CastModifierData *cmd = (CastModifierData *)md;
if (cmd->object != NULL) {
DEG_add_object_relation(ctx->node, cmd->object, DEG_OB_COMP_TRANSFORM, "Cast Modifier");
- DEG_add_modifier_to_transform_relation(ctx->node, "Cast Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Cast Modifier");
}
}
@@ -98,7 +96,7 @@ static void sphere_do(CastModifierData *cmd,
float (*vertexCos)[3],
int verts_num)
{
- MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
const bool invert_vgroup = (cmd->flag & MOD_CAST_INVERT_VGROUP) != 0;
Object *ctrl_ob = NULL;
@@ -239,7 +237,7 @@ static void cuboid_do(CastModifierData *cmd,
float (*vertexCos)[3],
int verts_num)
{
- MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
int defgrp_index;
const bool invert_vgroup = (cmd->flag & MOD_CAST_INVERT_VGROUP) != 0;
@@ -467,7 +465,7 @@ static void deformVerts(ModifierData *md,
if (ctx->object->type == OB_MESH && cmd->defgrp_name[0] != '\0') {
/* mesh_src is only needed for vgroups. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
if (cmd->type == MOD_CAST_TYPE_CUBOID) {
@@ -493,15 +491,14 @@ static void deformVertsEM(ModifierData *md,
Mesh *mesh_src = NULL;
if (cmd->defgrp_name[0] != '\0') {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) {
BLI_assert(mesh->totvert == verts_num);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -559,7 +556,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Cast = {
- /* name */ "Cast",
+ /* name */ N_("Cast"),
/* structName */ "CastModifierData",
/* structSize */ sizeof(CastModifierData),
/* srna */ &RNA_CastModifier,
diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c
index 185b05b4cf9..dcb4803b08a 100644
--- a/source/blender/modifiers/intern/MOD_cloth.c
+++ b/source/blender/modifiers/intern/MOD_cloth.c
@@ -94,7 +94,7 @@ static void deformVerts(ModifierData *md,
}
if (mesh == NULL) {
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false);
}
else {
/* Not possible to use get_mesh() in this case as we'll modify its vertices
@@ -115,7 +115,7 @@ static void deformVerts(ModifierData *md,
float(*layerorco)[3];
if (!(layerorco = CustomData_get_layer(&mesh_src->vdata, CD_CLOTH_ORCO))) {
layerorco = CustomData_add_layer(
- &mesh_src->vdata, CD_CLOTH_ORCO, CD_CALLOC, NULL, mesh_src->totvert);
+ &mesh_src->vdata, CD_CLOTH_ORCO, CD_SET_DEFAULT, NULL, mesh_src->totvert);
}
memcpy(layerorco, kb->data, sizeof(float[3]) * verts_num);
@@ -144,12 +144,10 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
DEG_add_forcefield_relations(
ctx->node, ctx->object, clmd->sim_parms->effector_weights, true, 0, "Cloth Field");
}
- DEG_add_modifier_to_transform_relation(ctx->node, "Cloth Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Cloth Modifier");
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
ClothModifierData *clmd = (ClothModifierData *)md;
@@ -254,7 +252,7 @@ static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *u
}
if (clmd->sim_parms && clmd->sim_parms->effector_weights) {
- walk(userData, ob, (ID **)&clmd->sim_parms->effector_weights->group, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&clmd->sim_parms->effector_weights->group, IDWALK_CB_USER);
}
}
@@ -275,7 +273,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Cloth = {
- /* name */ "Cloth",
+ /* name */ N_("Cloth"),
/* structName */ "ClothModifierData",
/* structSize */ sizeof(ClothModifierData),
/* srna */ &RNA_ClothModifier,
diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c
index f9cd3d5937d..e38bf96500e 100644
--- a/source/blender/modifiers/intern/MOD_collision.c
+++ b/source/blender/modifiers/intern/MOD_collision.c
@@ -107,7 +107,7 @@ static void deformVerts(ModifierData *md,
}
if (mesh == NULL) {
- mesh_src = MOD_deform_mesh_eval_get(ob, NULL, NULL, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ob, NULL, NULL, NULL, verts_num, false);
}
else {
/* Not possible to use get_mesh() in this case as we'll modify its vertices
@@ -145,7 +145,7 @@ static void deformVerts(ModifierData *md,
if (collmd->time_xnew == -1000) { /* first time */
- collmd->x = MEM_dupallocN(mesh_src->mvert); /* frame start position */
+ collmd->x = MEM_dupallocN(BKE_mesh_verts(mesh_src)); /* frame start position */
for (uint i = 0; i < mvert_num; i++) {
/* we save global positions */
@@ -160,7 +160,7 @@ static void deformVerts(ModifierData *md,
collmd->mvert_num = mvert_num;
{
- const MLoop *mloop = mesh_src->mloop;
+ const MLoop *mloop = BKE_mesh_loops(mesh_src);
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh_src);
collmd->tri_num = BKE_mesh_runtime_looptri_len(mesh_src);
MVertTri *tri = MEM_mallocN(sizeof(*tri) * collmd->tri_num, __func__);
@@ -182,7 +182,7 @@ static void deformVerts(ModifierData *md,
collmd->xnew = tempVert;
collmd->time_x = collmd->time_xnew;
- memcpy(collmd->xnew, mesh_src->mvert, mvert_num * sizeof(MVert));
+ memcpy(collmd->xnew, BKE_mesh_verts(mesh_src), mvert_num * sizeof(MVert));
bool is_static = true;
@@ -236,7 +236,7 @@ static void deformVerts(ModifierData *md,
static void updateDepsgraph(ModifierData *UNUSED(md), const ModifierUpdateDepsgraphContext *ctx)
{
- DEG_add_modifier_to_transform_relation(ctx->node, "Collision Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Collision Modifier");
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -284,7 +284,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
}
ModifierTypeInfo modifierType_Collision = {
- /* name */ "Collision",
+ /* name */ N_("Collision"),
/* structName */ "CollisionModifierData",
/* structSize */ sizeof(CollisionModifierData),
/* srna */ &RNA_CollisionModifier,
diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c
index 3698f4403a1..5a9f7c657eb 100644
--- a/source/blender/modifiers/intern/MOD_correctivesmooth.c
+++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c
@@ -49,10 +49,10 @@
#include "PIL_time.h"
#ifdef DEBUG_TIME
# include "PIL_time_utildefines.h"
+
#endif
-/* minor optimization, calculate this inline */
-#define USE_TANGENT_CALC_INLINE
+#include "BLI_strict_flags.h"
static void initData(ModifierData *md)
{
@@ -65,8 +65,6 @@ static void initData(ModifierData *md)
csmd->delta_cache.deltas = NULL;
}
-#include "BLI_strict_flags.h"
-
static void copyData(const ModifierData *md, ModifierData *target, const int flag)
{
const CorrectiveSmoothModifierData *csmd = (const CorrectiveSmoothModifierData *)md;
@@ -79,7 +77,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
}
tcsmd->delta_cache.deltas = NULL;
- tcsmd->delta_cache.totverts = 0;
+ tcsmd->delta_cache.deltas_num = 0;
}
static void freeBind(CorrectiveSmoothModifierData *csmd)
@@ -96,9 +94,7 @@ static void freeData(ModifierData *md)
freeBind(csmd);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)md;
@@ -109,7 +105,7 @@ static void requiredDataMask(Object *UNUSED(ob),
}
/* check individual weights for changes and cache values */
-static void mesh_get_weights(MDeformVert *dvert,
+static void mesh_get_weights(const MDeformVert *dvert,
const int defgrp_index,
const uint verts_num,
const bool use_invert_vgroup,
@@ -131,28 +127,27 @@ static void mesh_get_weights(MDeformVert *dvert,
static void mesh_get_boundaries(Mesh *mesh, float *smooth_weights)
{
- const MPoly *mpoly = mesh->mpoly;
- const MLoop *mloop = mesh->mloop;
- const MEdge *medge = mesh->medge;
- uint mpoly_num, medge_num, i;
- ushort *boundaries;
+ const MEdge *medge = BKE_mesh_edges(mesh);
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
+ const MLoop *mloop = BKE_mesh_loops(mesh);
- mpoly_num = (uint)mesh->totpoly;
- medge_num = (uint)mesh->totedge;
+ const uint mpoly_num = (uint)mesh->totpoly;
+ const uint medge_num = (uint)mesh->totedge;
- boundaries = MEM_calloc_arrayN(medge_num, sizeof(*boundaries), __func__);
+ /* Flag boundary edges so only boundaries are set to 1. */
+ uint8_t *boundaries = MEM_calloc_arrayN(medge_num, sizeof(*boundaries), __func__);
- /* count the number of adjacent faces */
- for (i = 0; i < mpoly_num; i++) {
+ for (uint i = 0; i < mpoly_num; i++) {
const MPoly *p = &mpoly[i];
const int totloop = p->totloop;
int j;
for (j = 0; j < totloop; j++) {
- boundaries[mloop[p->loopstart + j].e]++;
+ uint8_t *e_value = &boundaries[mloop[p->loopstart + j].e];
+ *e_value |= (uint8_t)((*e_value) + 1);
}
}
- for (i = 0; i < medge_num; i++) {
+ for (uint i = 0; i < medge_num; i++) {
if (boundaries[i] == 1) {
smooth_weights[medge[i].v1] = 0.0f;
smooth_weights[medge[i].v2] = 0.0f;
@@ -178,7 +173,7 @@ static void smooth_iter__simple(CorrectiveSmoothModifierData *csmd,
uint i;
const uint edges_num = (uint)mesh->totedge;
- const MEdge *edges = mesh->medge;
+ const MEdge *edges = BKE_mesh_edges(mesh);
float *vertex_edge_count_div;
struct SmoothingData_Simple {
@@ -255,7 +250,7 @@ static void smooth_iter__length_weight(CorrectiveSmoothModifierData *csmd,
/* NOTE: the way this smoothing method works, its approx half as strong as the simple-smooth,
* and 2.0 rarely spikes, double the value for consistent behavior. */
const float lambda = csmd->lambda * 2.0f;
- const MEdge *edges = mesh->medge;
+ const MEdge *edges = BKE_mesh_edges(mesh);
float *vertex_edge_count;
uint i;
@@ -358,7 +353,7 @@ static void smooth_iter(CorrectiveSmoothModifierData *csmd,
static void smooth_verts(CorrectiveSmoothModifierData *csmd,
Mesh *mesh,
- MDeformVert *dvert,
+ const MDeformVert *dvert,
const int defgrp_index,
float (*vertexCos)[3],
uint verts_num)
@@ -393,69 +388,61 @@ static void smooth_verts(CorrectiveSmoothModifierData *csmd,
}
/**
- * finalize after accumulation.
+ * Calculate an orthogonal 3x3 matrix from 2 edge vectors.
+ * \return false if this loop should be ignored (have zero influence).
*/
-static void calc_tangent_ortho(float ts[3][3])
+static bool calc_tangent_loop(const float v_dir_prev[3],
+ const float v_dir_next[3],
+ float r_tspace[3][3])
{
- float v_tan_a[3], v_tan_b[3];
- float t_vec_a[3], t_vec_b[3];
-
- normalize_v3(ts[2]);
-
- copy_v3_v3(v_tan_a, ts[0]);
- copy_v3_v3(v_tan_b, ts[1]);
-
- cross_v3_v3v3(ts[1], ts[2], v_tan_a);
- mul_v3_fl(ts[1], dot_v3v3(ts[1], v_tan_b) < 0.0f ? -1.0f : 1.0f);
-
- /* Orthogonalize tangent. */
- mul_v3_v3fl(t_vec_a, ts[2], dot_v3v3(ts[2], v_tan_a));
- sub_v3_v3v3(ts[0], v_tan_a, t_vec_a);
-
- /* Orthogonalize bi-tangent. */
- mul_v3_v3fl(t_vec_a, ts[2], dot_v3v3(ts[2], ts[1]));
- mul_v3_v3fl(t_vec_b, ts[0], dot_v3v3(ts[0], ts[1]) / dot_v3v3(v_tan_a, v_tan_a));
- sub_v3_v3(ts[1], t_vec_a);
- sub_v3_v3(ts[1], t_vec_b);
-
- normalize_v3(ts[0]);
- normalize_v3(ts[1]);
+ if (UNLIKELY(compare_v3v3(v_dir_prev, v_dir_next, FLT_EPSILON * 10.0f))) {
+ /* As there are no weights, the value doesn't matter just initialize it. */
+ unit_m3(r_tspace);
+ return false;
+ }
+
+ copy_v3_v3(r_tspace[0], v_dir_prev);
+ copy_v3_v3(r_tspace[1], v_dir_next);
+
+ cross_v3_v3v3(r_tspace[2], v_dir_prev, v_dir_next);
+ normalize_v3(r_tspace[2]);
+
+ /* Make orthogonal using `r_tspace[2]` as a basis.
+ *
+ * NOTE: while it seems more logical to use `v_dir_prev` & `v_dir_next` as separate X/Y axis
+ * (instead of combining them as is done here). It's not necessary as the directions of the
+ * axis aren't important as long as the difference between tangent matrices is equivalent.
+ * Some computations can be skipped by combining the two directions,
+ * using the cross product for the 3rd axes. */
+ add_v3_v3(r_tspace[0], r_tspace[1]);
+ normalize_v3(r_tspace[0]);
+ cross_v3_v3v3(r_tspace[1], r_tspace[2], r_tspace[0]);
+
+ return true;
}
/**
- * accumulate edge-vectors from all polys.
+ * \param r_tangent_spaces: Loop aligned array of tangents.
+ * \param r_tangent_weights: Loop aligned array of weights (may be NULL).
+ * \param r_tangent_weights_per_vertex: Vertex aligned array, accumulating weights for each loop
+ * (may be NULL).
*/
-static void calc_tangent_loop_accum(const float v_dir_prev[3],
- const float v_dir_next[3],
- float r_tspace[3][3])
-{
- add_v3_v3v3(r_tspace[1], v_dir_prev, v_dir_next);
-
- if (compare_v3v3(v_dir_prev, v_dir_next, FLT_EPSILON * 10.0f) == false) {
- const float weight = fabsf(acosf(dot_v3v3(v_dir_next, v_dir_prev)));
- float nor[3];
-
- cross_v3_v3v3(nor, v_dir_prev, v_dir_next);
- normalize_v3(nor);
-
- cross_v3_v3v3(r_tspace[0], r_tspace[1], nor);
-
- mul_v3_fl(nor, weight);
- /* accumulate weighted normals */
- add_v3_v3(r_tspace[2], nor);
- }
-}
-
-static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tangent_spaces)[3][3])
+static void calc_tangent_spaces(const Mesh *mesh,
+ const float (*vertexCos)[3],
+ float (*r_tangent_spaces)[3][3],
+ float *r_tangent_weights,
+ float *r_tangent_weights_per_vertex)
{
const uint mpoly_num = (uint)mesh->totpoly;
-#ifndef USE_TANGENT_CALC_INLINE
- const uint mvert_num = (uint)dm->getNumVerts(dm);
-#endif
- const MPoly *mpoly = mesh->mpoly;
- const MLoop *mloop = mesh->mloop;
+ const uint mvert_num = (uint)mesh->totvert;
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
+ const MLoop *mloop = BKE_mesh_loops(mesh);
uint i;
+ if (r_tangent_weights_per_vertex != NULL) {
+ copy_vn_fl(r_tangent_weights_per_vertex, (int)mvert_num, 0.0f);
+ }
+
for (i = 0; i < mpoly_num; i++) {
const MPoly *mp = &mpoly[i];
const MLoop *l_next = &mloop[mp->loopstart];
@@ -471,7 +458,8 @@ static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tan
normalize_v3(v_dir_prev);
for (; l_next != l_term; l_prev = l_curr, l_curr = l_next, l_next++) {
- float(*ts)[3] = r_tangent_spaces[l_curr->v];
+ uint l_index = (uint)(l_curr - mloop);
+ float(*ts)[3] = r_tangent_spaces[l_index];
/* re-use the previous value */
#if 0
@@ -481,19 +469,22 @@ static void calc_tangent_spaces(Mesh *mesh, float (*vertexCos)[3], float (*r_tan
sub_v3_v3v3(v_dir_next, vertexCos[l_curr->v], vertexCos[l_next->v]);
normalize_v3(v_dir_next);
- calc_tangent_loop_accum(v_dir_prev, v_dir_next, ts);
+ if (calc_tangent_loop(v_dir_prev, v_dir_next, ts)) {
+ if (r_tangent_weights != NULL) {
+ const float weight = fabsf(acosf(dot_v3v3(v_dir_next, v_dir_prev)));
+ r_tangent_weights[l_index] = weight;
+ r_tangent_weights_per_vertex[l_curr->v] += weight;
+ }
+ }
+ else {
+ if (r_tangent_weights != NULL) {
+ r_tangent_weights[l_index] = 0;
+ }
+ }
copy_v3_v3(v_dir_prev, v_dir_next);
}
}
-
- /* do inline */
-#ifndef USE_TANGENT_CALC_INLINE
- for (i = 0; i < mvert_num; i++) {
- float(*ts)[3] = r_tangent_spaces[i];
- calc_tangent_ortho(ts);
- }
-#endif
}
static void store_cache_settings(CorrectiveSmoothModifierData *csmd)
@@ -519,43 +510,47 @@ static bool cache_settings_equal(CorrectiveSmoothModifierData *csmd)
*/
static void calc_deltas(CorrectiveSmoothModifierData *csmd,
Mesh *mesh,
- MDeformVert *dvert,
+ const MDeformVert *dvert,
const int defgrp_index,
const float (*rest_coords)[3],
uint verts_num)
{
+ const MLoop *mloop = BKE_mesh_loops(mesh);
+ const uint loops_num = (uint)mesh->totloop;
+
float(*smooth_vertex_coords)[3] = MEM_dupallocN(rest_coords);
float(*tangent_spaces)[3][3];
- uint i;
- tangent_spaces = MEM_calloc_arrayN(verts_num, sizeof(float[3][3]), __func__);
+ uint l_index;
- if (csmd->delta_cache.totverts != verts_num) {
+ tangent_spaces = MEM_malloc_arrayN(loops_num, sizeof(float[3][3]), __func__);
+
+ if (csmd->delta_cache.deltas_num != loops_num) {
MEM_SAFE_FREE(csmd->delta_cache.deltas);
}
/* allocate deltas if they have not yet been allocated, otherwise we will just write over them */
if (!csmd->delta_cache.deltas) {
- csmd->delta_cache.totverts = verts_num;
- csmd->delta_cache.deltas = MEM_malloc_arrayN(verts_num, sizeof(float[3]), __func__);
+ csmd->delta_cache.deltas_num = loops_num;
+ csmd->delta_cache.deltas = MEM_malloc_arrayN(loops_num, sizeof(float[3]), __func__);
}
smooth_verts(csmd, mesh, dvert, defgrp_index, smooth_vertex_coords, verts_num);
- calc_tangent_spaces(mesh, smooth_vertex_coords, tangent_spaces);
+ calc_tangent_spaces(mesh, smooth_vertex_coords, tangent_spaces, NULL, NULL);
- for (i = 0; i < verts_num; i++) {
- float imat[3][3], delta[3];
+ copy_vn_fl(&csmd->delta_cache.deltas[0][0], (int)loops_num * 3, 0.0f);
-#ifdef USE_TANGENT_CALC_INLINE
- calc_tangent_ortho(tangent_spaces[i]);
-#endif
+ for (l_index = 0; l_index < loops_num; l_index++) {
+ const int v_index = (int)mloop[l_index].v;
+ float delta[3];
+ sub_v3_v3v3(delta, rest_coords[v_index], smooth_vertex_coords[v_index]);
- sub_v3_v3v3(delta, rest_coords[i], smooth_vertex_coords[i]);
- if (UNLIKELY(!invert_m3_m3(imat, tangent_spaces[i]))) {
- transpose_m3_m3(imat, tangent_spaces[i]);
+ float imat[3][3];
+ if (UNLIKELY(!invert_m3_m3(imat, tangent_spaces[l_index]))) {
+ transpose_m3_m3(imat, tangent_spaces[l_index]);
}
- mul_v3_m3v3(csmd->delta_cache.deltas[i], imat, delta);
+ mul_v3_m3v3(csmd->delta_cache.deltas[l_index], imat, delta);
}
MEM_freeN(tangent_spaces);
@@ -578,8 +573,11 @@ static void correctivesmooth_modifier_do(ModifierData *md,
((csmd->rest_source == MOD_CORRECTIVESMOOTH_RESTSOURCE_ORCO) &&
(((ID *)ob->data)->recalc & ID_RECALC_ALL));
+ const MLoop *mloop = BKE_mesh_loops(mesh);
+ const uint loops_num = (uint)mesh->totloop;
+
bool use_only_smooth = (csmd->flag & MOD_CORRECTIVESMOOTH_ONLY_SMOOTH) != 0;
- MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
int defgrp_index;
MOD_get_vgroup(ob, mesh, csmd->defgrp_name, &dvert, &defgrp_index);
@@ -640,7 +638,7 @@ static void correctivesmooth_modifier_do(ModifierData *md,
}
/* check to see if our deltas are still valid */
- if (!csmd->delta_cache.deltas || (csmd->delta_cache.totverts != verts_num) ||
+ if (!csmd->delta_cache.deltas || (csmd->delta_cache.deltas_num != loops_num) ||
force_delta_cache_update) {
const float(*rest_coords)[3];
bool is_rest_coords_alloc = false;
@@ -688,27 +686,38 @@ static void correctivesmooth_modifier_do(ModifierData *md,
smooth_verts(csmd, mesh, dvert, defgrp_index, vertexCos, verts_num);
{
- uint i;
+ uint l_index;
float(*tangent_spaces)[3][3];
+ float *tangent_weights;
+
+ float *tangent_weights_per_vertex;
const float scale = csmd->scale;
- /* calloc, since values are accumulated */
- tangent_spaces = MEM_calloc_arrayN(verts_num, sizeof(float[3][3]), __func__);
- calc_tangent_spaces(mesh, vertexCos, tangent_spaces);
+ tangent_spaces = MEM_malloc_arrayN(loops_num, sizeof(float[3][3]), __func__);
+ tangent_weights = MEM_malloc_arrayN(loops_num, sizeof(float), __func__);
+ tangent_weights_per_vertex = MEM_malloc_arrayN(verts_num, sizeof(float), __func__);
- for (i = 0; i < verts_num; i++) {
- float delta[3];
+ calc_tangent_spaces(
+ mesh, vertexCos, tangent_spaces, tangent_weights, tangent_weights_per_vertex);
-#ifdef USE_TANGENT_CALC_INLINE
- calc_tangent_ortho(tangent_spaces[i]);
-#endif
+ for (l_index = 0; l_index < loops_num; l_index++) {
+ const uint v_index = mloop[l_index].v;
+ const float weight = tangent_weights[l_index] / tangent_weights_per_vertex[v_index];
+ if (UNLIKELY(!(weight > 0.0f))) {
+ /* Catches zero & divide by zero. */
+ continue;
+ }
- mul_v3_m3v3(delta, tangent_spaces[i], csmd->delta_cache.deltas[i]);
- madd_v3_v3fl(vertexCos[i], delta, scale);
+ float delta[3];
+ mul_v3_m3v3(delta, tangent_spaces[l_index], csmd->delta_cache.deltas[l_index]);
+ mul_v3_fl(delta, weight);
+ madd_v3_v3fl(vertexCos[v_index], delta, scale);
}
MEM_freeN(tangent_spaces);
+ MEM_freeN(tangent_weights);
+ MEM_freeN(tangent_weights_per_vertex);
}
#ifdef DEBUG_TIME
@@ -720,7 +729,7 @@ static void correctivesmooth_modifier_do(ModifierData *md,
/* when the modifier fails to execute */
error:
MEM_SAFE_FREE(csmd->delta_cache.deltas);
- csmd->delta_cache.totverts = 0;
+ csmd->delta_cache.deltas_num = 0;
}
static void deformVerts(ModifierData *md,
@@ -729,8 +738,7 @@ static void deformVerts(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
correctivesmooth_modifier_do(
md, ctx->depsgraph, ctx->object, mesh_src, vertexCos, (uint)verts_num, NULL);
@@ -747,10 +755,9 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -831,11 +838,11 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
/* runtime only */
csmd->delta_cache.deltas = NULL;
- csmd->delta_cache.totverts = 0;
+ csmd->delta_cache.deltas_num = 0;
}
ModifierTypeInfo modifierType_CorrectiveSmooth = {
- /* name */ "CorrectiveSmooth",
+ /* name */ N_("CorrectiveSmooth"),
/* structName */ "CorrectiveSmoothModifierData",
/* structSize */ sizeof(CorrectiveSmoothModifierData),
/* srna */ &RNA_CorrectiveSmoothModifier,
diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c
index a82b999f4dc..16c97d486da 100644
--- a/source/blender/modifiers/intern/MOD_curve.c
+++ b/source/blender/modifiers/intern/MOD_curve.c
@@ -51,9 +51,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(cmd, DNA_struct_default_get(CurveModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
CurveModifierData *cmd = (CurveModifierData *)md;
@@ -97,7 +95,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
DEG_add_special_eval_flag(ctx->node, &cmd->object->id, DAG_EVAL_NEED_CURVE_PATH);
}
- DEG_add_modifier_to_transform_relation(ctx->node, "Curve Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Curve Modifier");
}
static void deformVerts(ModifierData *md,
@@ -111,10 +109,10 @@ static void deformVerts(ModifierData *md,
if (ctx->object->type == OB_MESH && cmd->name[0] != '\0') {
/* mesh_src is only needed for vgroups. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
- struct MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
int defgrp_index = -1;
MOD_get_vgroup(ctx->object, mesh_src, cmd->name, &dvert, &defgrp_index);
@@ -203,7 +201,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Curve = {
- /* name */ "Curve",
+ /* name */ N_("Curve"),
/* structName */ "CurveModifierData",
/* structSize */ sizeof(CurveModifierData),
/* srna */ &RNA_CurveModifier,
diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c
index a3b088799cc..613d3d5196f 100644
--- a/source/blender/modifiers/intern/MOD_datatransfer.c
+++ b/source/blender/modifiers/intern/MOD_datatransfer.c
@@ -22,6 +22,7 @@
#include "BKE_data_transfer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_remap.h"
#include "BKE_modifier.h"
@@ -72,9 +73,7 @@ static void initData(ModifierData *md)
dtmd->flags = MOD_DATATRANSFER_OBSRC_TRANSFORM;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
DataTransferModifierData *dtmd = (DataTransferModifierData *)md;
@@ -129,7 +128,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
if (dtmd->flags & MOD_DATATRANSFER_OBSRC_TRANSFORM) {
DEG_add_object_relation(
ctx->node, dtmd->ob_source, DEG_OB_COMP_TRANSFORM, "DataTransfer Modifier");
- DEG_add_modifier_to_transform_relation(ctx->node, "DataTransfer Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "DataTransfer Modifier");
}
}
}
@@ -178,7 +177,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BLI_SPACE_TRANSFORM_SETUP(space_transform, ctx->object, ob_source);
}
- if (((result == me) || (me->mvert == result->mvert) || (me->medge == result->medge)) &&
+ const MVert *me_verts = BKE_mesh_verts(me);
+ const MEdge *me_edges = BKE_mesh_edges(me);
+ const MVert *result_verts = BKE_mesh_verts(result);
+ const MEdge *result_edges = BKE_mesh_edges(result);
+
+ if (((result == me) || (me_verts == result_verts) || (me_edges == result_edges)) &&
(dtmd->data_types & DT_TYPES_AFFECT_MESH)) {
/* We need to duplicate data here, otherwise setting custom normals, edges' sharpness, etc.,
* could modify org mesh, see T43671. */
@@ -211,7 +215,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
dtmd->defgrp_name,
invert_vgroup,
&reports)) {
- result->runtime.is_original = false;
+ result->runtime.is_original_bmesh = false;
}
if (BKE_reports_contain(&reports, RPT_ERROR)) {
@@ -474,7 +478,7 @@ static void panelRegister(ARegionType *region_type)
#undef DT_TYPES_AFFECT_MESH
ModifierTypeInfo modifierType_DataTransfer = {
- /* name */ "DataTransfer",
+ /* name */ N_("DataTransfer"),
/* structName */ "DataTransferModifierData",
/* structSize */ sizeof(DataTransferModifierData),
/* srna */ &RNA_DataTransferModifier,
diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c
index 70f028d6907..059ded4f873 100644
--- a/source/blender/modifiers/intern/MOD_decimate.c
+++ b/source/blender/modifiers/intern/MOD_decimate.c
@@ -54,9 +54,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(dmd, DNA_struct_default_get(DecimateModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
DecimateModifierData *dmd = (DecimateModifierData *)md;
@@ -135,7 +133,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
if (dmd->mode == MOD_DECIM_MODE_COLLAPSE) {
if (dmd->defgrp_name[0] && (dmd->defgrp_factor > 0.0f)) {
- MDeformVert *dvert;
+ const MDeformVert *dvert;
int defgrp_index;
MOD_get_vgroup(ctx->object, mesh, dmd->defgrp_name, &dvert, &defgrp_index);
@@ -201,10 +199,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
updateFaceCount(ctx, dmd, bm->totface);
- result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
/* make sure we never alloc'd these */
BLI_assert(bm->vtoolflagpool == NULL && bm->etoolflagpool == NULL && bm->ftoolflagpool == NULL);
- BLI_assert(bm->vtable == NULL && bm->etable == NULL && bm->ftable == NULL);
+
+ result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh);
BM_mesh_free(bm);
@@ -271,7 +269,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Decimate = {
- /* name */ "Decimate",
+ /* name */ N_("Decimate"),
/* structName */ "DecimateModifierData",
/* structSize */ sizeof(DecimateModifierData),
/* srna */ &RNA_DecimateModifier,
diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c
index 1e2224e3a65..ad5be57ad68 100644
--- a/source/blender/modifiers/intern/MOD_displace.c
+++ b/source/blender/modifiers/intern/MOD_displace.c
@@ -59,9 +59,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(dmd, DNA_struct_default_get(DisplaceModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
DisplaceModifierData *dmd = (DisplaceModifierData *)md;
@@ -142,7 +140,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
if (need_transform_relation) {
- DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Displace Modifier");
}
}
@@ -150,7 +148,7 @@ typedef struct DisplaceUserdata {
/*const*/ DisplaceModifierData *dmd;
struct Scene *scene;
struct ImagePool *pool;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
float weight;
int defgrp_index;
int direction;
@@ -170,7 +168,7 @@ static void displaceModifier_do_task(void *__restrict userdata,
{
DisplaceUserdata *data = (DisplaceUserdata *)userdata;
DisplaceModifierData *dmd = data->dmd;
- MDeformVert *dvert = data->dvert;
+ const MDeformVert *dvert = data->dvert;
const bool invert_vgroup = (dmd->flag & MOD_DISP_INVERT_VGROUP) != 0;
float weight = data->weight;
int defgrp_index = data->defgrp_index;
@@ -270,7 +268,7 @@ static void displaceModifier_do(DisplaceModifierData *dmd,
{
Object *ob = ctx->object;
MVert *mvert;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
int direction = dmd->direction;
int defgrp_index;
float(*tex_co)[3];
@@ -286,7 +284,7 @@ static void displaceModifier_do(DisplaceModifierData *dmd,
return;
}
- mvert = mesh->mvert;
+ mvert = BKE_mesh_verts_for_write(mesh);
MOD_get_vgroup(ob, mesh, dmd->defgrp_name, &dvert, &defgrp_index);
if (defgrp_index >= 0 && dvert == NULL) {
@@ -316,7 +314,7 @@ static void displaceModifier_do(DisplaceModifierData *dmd,
float(*clnors)[3] = CustomData_get_layer(ldata, CD_NORMAL);
vert_clnors = MEM_malloc_arrayN(verts_num, sizeof(*vert_clnors), __func__);
BKE_mesh_normals_loop_to_vertex(
- verts_num, mesh->mloop, mesh->totloop, (const float(*)[3])clnors, vert_clnors);
+ verts_num, BKE_mesh_loops(mesh), mesh->totloop, (const float(*)[3])clnors, vert_clnors);
}
else {
direction = MOD_DISP_DIR_NOR;
@@ -372,8 +370,7 @@ static void deformVerts(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
displaceModifier_do((DisplaceModifierData *)md, ctx, mesh_src, vertexCos, verts_num);
@@ -389,10 +386,9 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -473,7 +469,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Displace = {
- /* name */ "Displace",
+ /* name */ N_("Displace"),
/* structName */ "DisplaceModifierData",
/* structSize */ sizeof(DisplaceModifierData),
/* srna */ &RNA_DisplaceModifier,
diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c
index 6b0578c77f1..c19c231d44c 100644
--- a/source/blender/modifiers/intern/MOD_dynamicpaint.c
+++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c
@@ -74,9 +74,7 @@ static void freeData(ModifierData *md)
dynamicPaint_Modifier_free(pmd);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md;
@@ -158,7 +156,7 @@ static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *u
walk(userData, ob, (ID **)&surface->brush_group, IDWALK_CB_NOP);
walk(userData, ob, (ID **)&surface->init_texture, IDWALK_CB_USER);
if (surface->effector_weights) {
- walk(userData, ob, (ID **)&surface->effector_weights->group, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&surface->effector_weights->group, IDWALK_CB_USER);
}
}
}
@@ -189,7 +187,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_DynamicPaint = {
- /* name */ "Dynamic Paint",
+ /* name */ N_("Dynamic Paint"),
/* structName */ "DynamicPaintModifierData",
/* structSize */ sizeof(DynamicPaintModifierData),
/* srna */ &RNA_DynamicPaintModifier,
diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c
index 49ddcb9a61d..b381ff32aa2 100644
--- a/source/blender/modifiers/intern/MOD_edgesplit.c
+++ b/source/blender/modifiers/intern/MOD_edgesplit.c
@@ -154,7 +154,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_EdgeSplit = {
- /* name */ "EdgeSplit",
+ /* name */ N_("EdgeSplit"),
/* structName */ "EdgeSplitModifierData",
/* structSize */ sizeof(EdgeSplitModifierData),
/* srna */ &RNA_EdgeSplitModifier,
diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c
index f88e930e127..b5f884b1843 100644
--- a/source/blender/modifiers/intern/MOD_explode.c
+++ b/source/blender/modifiers/intern/MOD_explode.c
@@ -76,9 +76,7 @@ static bool dependsOnTime(struct Scene *UNUSED(scene), ModifierData *UNUSED(md))
{
return true;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
ExplodeModifierData *emd = (ExplodeModifierData *)md;
@@ -100,8 +98,8 @@ static void createFacepa(ExplodeModifierData *emd, ParticleSystemModifierData *p
int i, p, v1, v2, v3, v4 = 0;
const bool invert_vgroup = (emd->flag & eExplodeFlag_INVERT_VGROUP) != 0;
- mvert = mesh->mvert;
- mface = mesh->mface;
+ mvert = BKE_mesh_verts_for_write(mesh);
+ mface = (MFace *)CustomData_get_layer(&mesh->fdata, CD_MFACE);
totvert = mesh->totvert;
totface = mesh->totface;
totpart = psmd->psys->totpart;
@@ -216,7 +214,8 @@ static const short add_faces[24] = {
static MFace *get_dface(Mesh *mesh, Mesh *split, int cur, int i, MFace *mf)
{
- MFace *df = &split->mface[cur];
+ MFace *mfaces = CustomData_get_layer(&split->fdata, CD_MFACE);
+ MFace *df = &mfaces[cur];
CustomData_copy_data(&mesh->fdata, &split->fdata, i, cur, 1);
*df = *mf;
return df;
@@ -639,7 +638,7 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh)
{
Mesh *split_m;
MFace *mf = NULL, *df1 = NULL;
- MFace *mface = mesh->mface;
+ MFace *mface = CustomData_get_layer(&mesh->fdata, CD_MFACE);
MVert *dupve, *mv;
EdgeHash *edgehash;
EdgeHashIterator *ehi;
@@ -729,12 +728,15 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh)
layers_num = CustomData_number_of_layers(&split_m->fdata, CD_MTFACE);
+ const MVert *mesh_verts = BKE_mesh_verts(mesh);
+ MVert *split_m_verts = BKE_mesh_verts_for_write(split_m);
+
/* copy new faces & verts (is it really this painful with custom data??) */
for (i = 0; i < totvert; i++) {
MVert source;
MVert *dest;
- source = mesh->mvert[i];
- dest = &split_m->mvert[i];
+ source = mesh_verts[i];
+ dest = &split_m_verts[i];
CustomData_copy_data(&mesh->vdata, &split_m->vdata, i, i, 1);
*dest = source;
@@ -742,7 +744,7 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh)
/* override original facepa (original pointer is saved in caller function) */
- /* TODO(campbell): `(totfsplit * 2)` over allocation is used since the quads are
+ /* TODO(@campbellbarton): `(totfsplit * 2)` over allocation is used since the quads are
* later interpreted as tri's, for this to work right I think we probably
* have to stop using tessface. */
@@ -755,14 +757,14 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh)
for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
BLI_edgehashIterator_getKey(ehi, &ed_v1, &ed_v2);
esplit = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
- mv = &split_m->mvert[ed_v2];
- dupve = &split_m->mvert[esplit];
+ mv = &split_m_verts[ed_v2];
+ dupve = &split_m_verts[esplit];
CustomData_copy_data(&split_m->vdata, &split_m->vdata, ed_v2, esplit, 1);
*dupve = *mv;
- mv = &split_m->mvert[ed_v1];
+ mv = &split_m_verts[ed_v1];
mid_v3_v3v3(dupve->co, dupve->co, mv->co);
}
@@ -772,7 +774,7 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh)
curdupface = 0; //=totface;
// curdupin=totesplit;
for (i = 0, fs = facesplit; i < totface; i++, fs++) {
- mf = &mesh->mface[i];
+ mf = &mface[i];
switch (*fs) {
case 3:
@@ -876,8 +878,9 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh)
curdupface += add_faces[*fs] + 1;
}
+ MFace *split_mface = CustomData_get_layer(&split_m->fdata, CD_MFACE);
for (i = 0; i < curdupface; i++) {
- mf = &split_m->mface[i];
+ mf = &split_mface[i];
BKE_mesh_mface_index_validate(mf, &split_m->fdata, i, ((mf->flag & ME_FACE_SEL) ? 4 : 3));
}
@@ -915,7 +918,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd,
totface = mesh->totface;
totvert = mesh->totvert;
- mface = mesh->mface;
+ mface = CustomData_get_layer(&mesh->fdata, CD_MFACE);
totpart = psmd->psys->totpart;
sim.depsgraph = ctx->depsgraph;
@@ -984,6 +987,9 @@ static Mesh *explodeMesh(ExplodeModifierData *emd,
psmd->psys->lattice_deform_data = psys_create_lattice_deform_data(&sim);
+ const MVert *mesh_verts = BKE_mesh_verts(mesh);
+ MVert *explode_verts = BKE_mesh_verts_for_write(explode);
+
/* duplicate & displace vertices */
ehi = BLI_edgehashIterator_new(vertpahash);
for (; !BLI_edgehashIterator_isDone(ehi); BLI_edgehashIterator_step(ehi)) {
@@ -995,8 +1001,8 @@ static Mesh *explodeMesh(ExplodeModifierData *emd,
ed_v2 -= totvert;
v = POINTER_AS_INT(BLI_edgehashIterator_getValue(ehi));
- source = mesh->mvert[ed_v1];
- dest = &explode->mvert[v];
+ source = mesh_verts[ed_v1];
+ dest = &explode_verts[v];
CustomData_copy_data(&mesh->vdata, &explode->vdata, ed_v1, v, 1);
@@ -1011,7 +1017,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd,
state.time = ctime;
psys_get_particle_state(&sim, ed_v2, &state, 1);
- vertco = explode->mvert[v].co;
+ vertco = explode_verts[v].co;
mul_m4_v3(ctx->object->obmat, vertco);
sub_v3_v3(vertco, birth.co);
@@ -1035,6 +1041,7 @@ static Mesh *explodeMesh(ExplodeModifierData *emd,
BLI_edgehashIterator_free(ehi);
/* Map new vertices to faces. */
+ MFace *explode_mface = CustomData_get_layer(&explode->fdata, CD_MFACE);
for (i = 0, u = 0; i < totface; i++) {
MFace source;
int orig_v4;
@@ -1056,8 +1063,8 @@ static Mesh *explodeMesh(ExplodeModifierData *emd,
pa = NULL;
}
- source = mesh->mface[i];
- mf = &explode->mface[u];
+ source = mface[i];
+ mf = &explode_mface[u];
orig_v4 = source.v4;
@@ -1224,7 +1231,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
}
ModifierTypeInfo modifierType_Explode = {
- /* name */ "Explode",
+ /* name */ N_("Explode"),
/* structName */ "ExplodeModifierData",
/* structSize */ sizeof(ExplodeModifierData),
/* srna */ &RNA_ExplodeModifier,
diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c
index 562f0df510d..0a2b01fe101 100644
--- a/source/blender/modifiers/intern/MOD_fluid.c
+++ b/source/blender/modifiers/intern/MOD_fluid.c
@@ -78,9 +78,7 @@ static void freeData(ModifierData *md)
#endif /* WITH_FLUID */
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
FluidModifierData *fmd = (FluidModifierData *)md;
@@ -215,7 +213,7 @@ static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *u
}
if (fmd->domain->effector_weights) {
- walk(userData, ob, (ID **)&fmd->domain->effector_weights->group, IDWALK_CB_NOP);
+ walk(userData, ob, (ID **)&fmd->domain->effector_weights->group, IDWALK_CB_USER);
}
}
@@ -241,7 +239,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Fluid = {
- /* name */ "Fluid",
+ /* name */ N_("Fluid"),
/* structName */ "FluidModifierData",
/* structSize */ sizeof(FluidModifierData),
/* srna */ &RNA_FluidModifier,
diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c
index 3649ece12e1..11bc9ee9574 100644
--- a/source/blender/modifiers/intern/MOD_hook.c
+++ b/source/blender/modifiers/intern/MOD_hook.c
@@ -68,9 +68,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
thmd->indexar = MEM_dupallocN(hmd->indexar);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
HookModifierData *hmd = (HookModifierData *)md;
@@ -122,7 +120,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
DEG_add_object_relation(ctx->node, hmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier");
}
/* We need own transformation as well. */
- DEG_add_modifier_to_transform_relation(ctx->node, "Hook Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Hook Modifier");
}
struct HookData_cb {
@@ -281,7 +279,7 @@ static void deformVerts_do(HookModifierData *hmd,
bPoseChannel *pchan = BKE_pose_channel_find_name(ob_target->pose, hmd->subtarget);
float dmat[4][4];
int i, *index_pt;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
struct HookData_cb hd;
const bool invert_vgroup = (hmd->flag & MOD_HOOK_INVERT_VGROUP) != 0;
@@ -430,8 +428,7 @@ static void deformVerts(struct ModifierData *md,
int verts_num)
{
HookModifierData *hmd = (HookModifierData *)md;
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
deformVerts_do(hmd, ctx, ctx->object, mesh_src, NULL, vertexCos, verts_num);
@@ -545,7 +542,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_Hook = {
- /* name */ "Hook",
+ /* name */ N_("Hook"),
/* structName */ "HookModifierData",
/* structSize */ sizeof(HookModifierData),
/* srna */ &RNA_HookModifier,
diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c
index a22f4b35e0d..b2b97bc0d08 100644
--- a/source/blender/modifiers/intern/MOD_laplaciandeform.c
+++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c
@@ -512,7 +512,7 @@ static void laplacianDeformPreview(LaplacianSystem *sys, float (*vertexCos)[3])
static bool isValidVertexGroup(LaplacianDeformModifierData *lmd, Object *ob, Mesh *mesh)
{
int defgrp_index;
- MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
MOD_get_vgroup(ob, mesh, lmd->anchor_grp_name, &dvert, &defgrp_index);
@@ -526,8 +526,8 @@ static void initSystem(
int defgrp_index;
int anchors_num;
float wpaint;
- MDeformVert *dvert = NULL;
- MDeformVert *dv = NULL;
+ const MDeformVert *dvert = NULL;
+ const MDeformVert *dv = NULL;
LaplacianSystem *sys;
const bool invert_vgroup = (lmd->flag & MOD_LAPLACIANDEFORM_INVERT_VGROUP) != 0;
@@ -570,14 +570,14 @@ static void initSystem(
createFaceRingMap(mesh->totvert,
BKE_mesh_runtime_looptri_ensure(mesh),
BKE_mesh_runtime_looptri_len(mesh),
- mesh->mloop,
+ BKE_mesh_loops(mesh),
&sys->ringf_map,
&sys->ringf_indices);
createVertRingMap(
- mesh->totvert, mesh->medge, mesh->totedge, &sys->ringv_map, &sys->ringv_indices);
+ mesh->totvert, BKE_mesh_edges(mesh), mesh->totedge, &sys->ringv_map, &sys->ringv_indices);
mlooptri = BKE_mesh_runtime_looptri_ensure(mesh);
- mloop = mesh->mloop;
+ mloop = BKE_mesh_loops(mesh);
for (i = 0; i < sys->tris_num; i++) {
sys->tris[i][0] = mloop[mlooptri[i].tri[0]].v;
@@ -596,8 +596,8 @@ static int isSystemDifferent(LaplacianDeformModifierData *lmd,
int defgrp_index;
int anchors_num = 0;
float wpaint;
- MDeformVert *dvert = NULL;
- MDeformVert *dv = NULL;
+ const MDeformVert *dvert = NULL;
+ const MDeformVert *dv = NULL;
LaplacianSystem *sys = (LaplacianSystem *)lmd->cache_system;
const bool invert_vgroup = (lmd->flag & MOD_LAPLACIANDEFORM_INVERT_VGROUP) != 0;
@@ -747,9 +747,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
return 1;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
LaplacianDeformModifierData *lmd = (LaplacianDeformModifierData *)md;
@@ -764,8 +762,7 @@ static void deformVerts(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
LaplacianDeformModifier_do(
(LaplacianDeformModifierData *)md, ctx->object, mesh_src, vertexCos, verts_num);
@@ -782,10 +779,9 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -875,7 +871,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_LaplacianDeform = {
- /* name */ "LaplacianDeform",
+ /* name */ N_("LaplacianDeform"),
/* structName */ "LaplacianDeformModifierData",
/* structSize */ sizeof(LaplacianDeformModifierData),
/* srna */ &RNA_LaplacianDeformModifier,
diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
index 95283b1cd20..1534708ac72 100644
--- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c
+++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c
@@ -66,8 +66,6 @@ struct BLaplacianSystem {
};
typedef struct BLaplacianSystem LaplacianSystem;
-static void required_data_mask(Object *ob, ModifierData *md, CustomData_MeshMasks *r_cddata_masks);
-static bool is_disabled(const struct Scene *scene, ModifierData *md, bool useRenderParams);
static float compute_volume(const float center[3],
float (*vertexCos)[3],
const MPoly *mpoly,
@@ -376,8 +374,8 @@ static void laplaciansmoothModifier_do(
LaplacianSmoothModifierData *smd, Object *ob, Mesh *mesh, float (*vertexCos)[3], int verts_num)
{
LaplacianSystem *sys;
- MDeformVert *dvert = NULL;
- MDeformVert *dv = NULL;
+ const MDeformVert *dvert = NULL;
+ const MDeformVert *dv = NULL;
float w, wpaint;
int i, iter;
int defgrp_index;
@@ -388,9 +386,9 @@ static void laplaciansmoothModifier_do(
return;
}
- sys->mpoly = mesh->mpoly;
- sys->mloop = mesh->mloop;
- sys->medges = mesh->medge;
+ sys->mpoly = BKE_mesh_polys(mesh);
+ sys->mloop = BKE_mesh_loops(mesh);
+ sys->medges = BKE_mesh_edges(mesh);
sys->vertexCos = vertexCos;
sys->min_area = 0.00001f;
MOD_get_vgroup(ob, mesh, smd->defgrp_name, &dvert, &defgrp_index);
@@ -511,9 +509,7 @@ static bool is_disabled(const struct Scene *UNUSED(scene),
return 0;
}
-static void required_data_mask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void required_data_mask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
LaplacianSmoothModifierData *smd = (LaplacianSmoothModifierData *)md;
@@ -535,7 +531,7 @@ static void deformVerts(ModifierData *md,
return;
}
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
laplaciansmoothModifier_do(
(LaplacianSmoothModifierData *)md, ctx->object, mesh_src, vertexCos, verts_num);
@@ -558,9 +554,9 @@ static void deformVertsEM(ModifierData *md,
return;
}
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -608,7 +604,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_LaplacianSmooth = {
- /* name */ "LaplacianSmooth",
+ /* name */ N_("LaplacianSmooth"),
/* structName */ "LaplacianSmoothModifierData",
/* structSize */ sizeof(LaplacianSmoothModifierData),
/* srna */ &RNA_LaplacianSmoothModifier,
diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c
index 832372304a0..ede2d7b581e 100644
--- a/source/blender/modifiers/intern/MOD_lattice.c
+++ b/source/blender/modifiers/intern/MOD_lattice.c
@@ -47,9 +47,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(lmd, DNA_struct_default_get(LatticeModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
LatticeModifierData *lmd = (LatticeModifierData *)md;
@@ -87,7 +85,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Lattice Modifier");
DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Lattice Modifier");
}
- DEG_add_modifier_to_transform_relation(ctx->node, "Lattice Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Lattice Modifier");
}
static void deformVerts(ModifierData *md,
@@ -98,7 +96,7 @@ static void deformVerts(ModifierData *md,
{
LatticeModifierData *lmd = (LatticeModifierData *)md;
struct Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ ctx->object, NULL, mesh, NULL, verts_num, false);
MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */
@@ -160,7 +158,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Lattice = {
- /* name */ "Lattice",
+ /* name */ N_("Lattice"),
/* structName */ "LatticeModifierData",
/* structSize */ sizeof(LatticeModifierData),
/* srna */ &RNA_LatticeModifier,
diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc
index 0813901fc49..6209985ecff 100644
--- a/source/blender/modifiers/intern/MOD_mask.cc
+++ b/source/blender/modifiers/intern/MOD_mask.cc
@@ -64,9 +64,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(mmd, DNA_struct_default_get(MaskModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks)
{
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
}
@@ -86,7 +84,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
/* TODO(sergey): Is it a proper relation here? */
DEG_add_object_relation(ctx->node, mmd->ob_arm, DEG_OB_COMP_TRANSFORM, "Mask Modifier");
arm->flag |= ARM_HAS_VIZ_DEPS;
- DEG_add_modifier_to_transform_relation(ctx->node, "Mask Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Mask Modifier");
}
}
@@ -143,9 +141,9 @@ static void invert_boolean_array(MutableSpan<bool> array)
}
}
-static void compute_masked_vertices(Span<bool> vertex_mask,
- MutableSpan<int> r_vertex_map,
- uint *r_verts_masked_num)
+static void compute_masked_verts(Span<bool> vertex_mask,
+ MutableSpan<int> r_vertex_map,
+ uint *r_verts_masked_num)
{
BLI_assert(vertex_mask.size() == r_vertex_map.size());
@@ -169,10 +167,11 @@ static void computed_masked_edges(const Mesh *mesh,
uint *r_edges_masked_num)
{
BLI_assert(mesh->totedge == r_edge_map.size());
+ const Span<MEdge> edges = mesh->edges();
uint edges_masked_num = 0;
for (int i : IndexRange(mesh->totedge)) {
- const MEdge &edge = mesh->medge[i];
+ const MEdge &edge = edges[i];
/* only add if both verts will be in new mesh */
if (vertex_mask[edge.v1] && vertex_mask[edge.v2]) {
@@ -194,11 +193,12 @@ static void computed_masked_edges_smooth(const Mesh *mesh,
uint *r_verts_add_num)
{
BLI_assert(mesh->totedge == r_edge_map.size());
+ const Span<MEdge> edges = mesh->edges();
uint edges_masked_num = 0;
uint verts_add_num = 0;
for (int i : IndexRange(mesh->totedge)) {
- const MEdge &edge = mesh->medge[i];
+ const MEdge &edge = edges[i];
/* only add if both verts will be in new mesh */
bool v1 = vertex_mask[edge.v1];
@@ -221,24 +221,26 @@ static void computed_masked_edges_smooth(const Mesh *mesh,
*r_verts_add_num = verts_add_num;
}
-static void computed_masked_polygons(const Mesh *mesh,
- Span<bool> vertex_mask,
- Vector<int> &r_masked_poly_indices,
- Vector<int> &r_loop_starts,
- uint *r_polys_masked_num,
- uint *r_loops_masked_num)
+static void computed_masked_polys(const Mesh *mesh,
+ Span<bool> vertex_mask,
+ Vector<int> &r_masked_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_polys_masked_num,
+ uint *r_loops_masked_num)
{
BLI_assert(mesh->totvert == vertex_mask.size());
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
r_masked_poly_indices.reserve(mesh->totpoly);
r_loop_starts.reserve(mesh->totpoly);
uint loops_masked_num = 0;
for (int i : IndexRange(mesh->totpoly)) {
- const MPoly &poly_src = mesh->mpoly[i];
+ const MPoly &poly_src = polys[i];
bool all_verts_in_mask = true;
- Span<MLoop> loops_src(&mesh->mloop[poly_src.loopstart], poly_src.totloop);
+ Span<MLoop> loops_src = loops.slice(poly_src.loopstart, poly_src.totloop);
for (const MLoop &loop : loops_src) {
if (!vertex_mask[loop.v]) {
all_verts_in_mask = false;
@@ -257,15 +259,15 @@ static void computed_masked_polygons(const Mesh *mesh,
*r_loops_masked_num = loops_masked_num;
}
-static void compute_interpolated_polygons(const Mesh *mesh,
- Span<bool> vertex_mask,
- uint verts_add_num,
- uint loops_masked_num,
- Vector<int> &r_masked_poly_indices,
- Vector<int> &r_loop_starts,
- uint *r_edges_add_num,
- uint *r_polys_add_num,
- uint *r_loops_add_num)
+static void compute_interpolated_polys(const Mesh *mesh,
+ Span<bool> vertex_mask,
+ uint verts_add_num,
+ uint loops_masked_num,
+ Vector<int> &r_masked_poly_indices,
+ Vector<int> &r_loop_starts,
+ uint *r_edges_add_num,
+ uint *r_polys_add_num,
+ uint *r_loops_add_num)
{
BLI_assert(mesh->totvert == vertex_mask.size());
@@ -273,17 +275,19 @@ static void compute_interpolated_polygons(const Mesh *mesh,
/* NOTE: this reserve can only lift the capacity if there are ngons, which get split. */
r_masked_poly_indices.reserve(r_masked_poly_indices.size() + verts_add_num);
r_loop_starts.reserve(r_loop_starts.size() + verts_add_num);
+ const Span<MPoly> polys = mesh->polys();
+ const Span<MLoop> loops = mesh->loops();
uint edges_add_num = 0;
uint polys_add_num = 0;
uint loops_add_num = 0;
for (int i : IndexRange(mesh->totpoly)) {
- const MPoly &poly_src = mesh->mpoly[i];
+ const MPoly &poly_src = polys[i];
int in_count = 0;
int start = -1;
int dst_totloop = -1;
- Span<MLoop> loops_src(&mesh->mloop[poly_src.loopstart], poly_src.totloop);
+ const Span<MLoop> loops_src = loops.slice(poly_src.loopstart, poly_src.totloop);
for (const int j : loops_src.index_range()) {
const MLoop &loop = loops_src[j];
if (vertex_mask[loop.v]) {
@@ -327,19 +331,22 @@ static void compute_interpolated_polygons(const Mesh *mesh,
*r_loops_add_num = loops_add_num;
}
-static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map)
+static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
+ const Span<MVert> src_verts = src_mesh.verts();
+ MutableSpan<MVert> dst_verts = dst_mesh.verts_for_write();
+
for (const int i_src : vertex_map.index_range()) {
const int i_dst = vertex_map[i_src];
if (i_dst == -1) {
continue;
}
- const MVert &v_src = src_mesh.mvert[i_src];
- MVert &v_dst = dst_mesh.mvert[i_dst];
+ const MVert &v_src = src_verts[i_src];
+ MVert &v_dst = dst_verts[i_dst];
v_dst = v_src;
CustomData_copy_data(&src_mesh.vdata, &dst_mesh.vdata, i_src, i_dst, 1);
@@ -369,6 +376,10 @@ static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh,
{
BLI_assert(src_mesh.totvert == vertex_mask.size());
BLI_assert(src_mesh.totedge == r_edge_map.size());
+ const Span<MVert> src_verts = src_mesh.verts();
+ const Span<MEdge> src_edges = src_mesh.edges();
+ MutableSpan<MVert> dst_verts = dst_mesh.verts_for_write();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
uint vert_index = dst_mesh.totvert - verts_add_num;
uint edge_index = edges_masked_num - verts_add_num;
@@ -378,8 +389,8 @@ static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh,
if (i_dst == -2) {
i_dst = edge_index;
}
- const MEdge &e_src = src_mesh.medge[i_src];
- MEdge &e_dst = dst_mesh.medge[i_dst];
+ const MEdge &e_src = src_edges[i_src];
+ MEdge &e_dst = dst_edges[i_dst];
CustomData_copy_data(&src_mesh.edata, &dst_mesh.edata, i_src, i_dst, 1);
e_dst = e_src;
@@ -389,9 +400,9 @@ static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh,
if (r_edge_map[i_src] == -2) {
const int i_dst = edge_index++;
r_edge_map[i_src] = i_dst;
- const MEdge &e_src = src_mesh.medge[i_src];
+ const MEdge &e_src = src_edges[i_src];
/* Cut destination edge and make v1 the new vertex. */
- MEdge &e_dst = dst_mesh.medge[i_dst];
+ MEdge &e_dst = dst_edges[i_dst];
if (!vertex_mask[e_src.v1]) {
e_dst.v1 = vert_index;
}
@@ -407,9 +418,9 @@ static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh,
float weights[2] = {1.0f - fac, fac};
CustomData_interp(
&src_mesh.vdata, &dst_mesh.vdata, (int *)&e_src.v1, weights, nullptr, 2, vert_index);
- MVert &v = dst_mesh.mvert[vert_index];
- MVert &v1 = src_mesh.mvert[e_src.v1];
- MVert &v2 = src_mesh.mvert[e_src.v2];
+ MVert &v = dst_verts[vert_index];
+ const MVert &v1 = src_verts[e_src.v1];
+ const MVert &v2 = src_verts[e_src.v2];
interp_v3_v3v3(v.co, v1.co, v2.co, fac);
vert_index++;
@@ -424,6 +435,9 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
Span<int> vertex_map,
Span<int> edge_map)
{
+ const Span<MEdge> src_edges = src_mesh.edges();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
+
BLI_assert(src_mesh.totvert == vertex_map.size());
BLI_assert(src_mesh.totedge == edge_map.size());
for (const int i_src : IndexRange(src_mesh.totedge)) {
@@ -432,8 +446,8 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
continue;
}
- const MEdge &e_src = src_mesh.medge[i_src];
- MEdge &e_dst = dst_mesh.medge[i_dst];
+ const MEdge &e_src = src_edges[i_src];
+ MEdge &e_dst = dst_edges[i_dst];
CustomData_copy_data(&src_mesh.edata, &dst_mesh.edata, i_src, i_dst, 1);
e_dst = e_src;
@@ -450,19 +464,24 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> new_loop_starts,
int polys_masked_num)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
for (const int i_dst : IndexRange(polys_masked_num)) {
const int i_src = masked_poly_indices[i_dst];
- const MPoly &mp_src = src_mesh.mpoly[i_src];
- MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[i_dst];
const int i_ml_src = mp_src.loopstart;
const int i_ml_dst = new_loop_starts[i_dst];
CustomData_copy_data(&src_mesh.pdata, &dst_mesh.pdata, i_src, i_dst, 1);
CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src, i_ml_dst, mp_src.totloop);
- const MLoop *ml_src = src_mesh.mloop + i_ml_src;
- MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+ const MLoop *ml_src = src_loops.data() + i_ml_src;
+ MLoop *ml_dst = dst_loops.data() + i_ml_dst;
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -486,6 +505,12 @@ static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh,
int polys_masked_num,
int edges_add_num)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
int edge_index = dst_mesh.totedge - edges_add_num;
int sub_poly_index = 0;
int last_i_src = -1;
@@ -500,8 +525,8 @@ static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh,
last_i_src = i_src;
}
- const MPoly &mp_src = src_mesh.mpoly[i_src];
- MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[i_dst];
const int i_ml_src = mp_src.loopstart;
int i_ml_dst = new_loop_starts[i_dst];
const int mp_totloop = (i_dst + 1 < new_loop_starts.size() ? new_loop_starts[i_dst + 1] :
@@ -517,7 +542,7 @@ static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh,
/* Ring search starting at a vertex which is not included in the mask. */
int start = -sub_poly_index - 1;
bool skip = false;
- Span<MLoop> loops_src(&src_mesh.mloop[i_ml_src], mp_src.totloop);
+ Span<MLoop> loops_src(&src_loops[i_ml_src], mp_src.totloop);
for (const int j : loops_src.index_range()) {
if (!vertex_mask[loops_src[j].v]) {
if (start == -1) {
@@ -552,13 +577,13 @@ static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh,
int indices[2] = {i_ml_src + last_index, i_ml_src + index};
CustomData_interp(
&src_mesh.ldata, &dst_mesh.ldata, indices, weights, nullptr, 2, i_ml_dst);
- MLoop &cut_dst_loop = dst_mesh.mloop[i_ml_dst];
+ MLoop &cut_dst_loop = dst_loops[i_ml_dst];
cut_dst_loop.e = edge_map[last_loop->e];
- cut_dst_loop.v = dst_mesh.medge[cut_dst_loop.e].v1;
+ cut_dst_loop.v = dst_edges[cut_dst_loop.e].v1;
i_ml_dst++;
CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src + index, i_ml_dst, 1);
- MLoop &next_dst_loop = dst_mesh.mloop[i_ml_dst];
+ MLoop &next_dst_loop = dst_loops[i_ml_dst];
next_dst_loop.v = vertex_map[loop.v];
next_dst_loop.e = edge_map[loop.e];
i_ml_dst++;
@@ -572,14 +597,14 @@ static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh,
int indices[2] = {i_ml_src + last_index, i_ml_src + index};
CustomData_interp(
&src_mesh.ldata, &dst_mesh.ldata, indices, weights, nullptr, 2, i_ml_dst);
- MLoop &cut_dst_loop = dst_mesh.mloop[i_ml_dst];
+ MLoop &cut_dst_loop = dst_loops[i_ml_dst];
cut_dst_loop.e = edge_index;
- cut_dst_loop.v = dst_mesh.medge[edge_map[last_loop->e]].v1;
+ cut_dst_loop.v = dst_edges[edge_map[last_loop->e]].v1;
i_ml_dst++;
/* Create closing edge. */
- MEdge &cut_edge = dst_mesh.medge[edge_index];
- cut_edge.v1 = dst_mesh.mloop[mp_dst.loopstart].v;
+ MEdge &cut_edge = dst_edges[edge_index];
+ cut_edge.v1 = dst_loops[mp_dst.loopstart].v;
cut_edge.v2 = cut_dst_loop.v;
BLI_assert(cut_edge.v1 != cut_edge.v2);
cut_edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -592,7 +617,7 @@ static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh,
BLI_assert(i_ml_dst != mp_dst.loopstart);
/* Extend active poly. */
CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src + index, i_ml_dst, 1);
- MLoop &dst_loop = dst_mesh.mloop[i_ml_dst];
+ MLoop &dst_loop = dst_loops[i_ml_dst];
dst_loop.v = vertex_map[loop.v];
dst_loop.e = edge_map[loop.e];
i_ml_dst++;
@@ -619,9 +644,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
(mmd->flag & MOD_MASK_SMOOTH);
/* Return empty or input mesh when there are no vertex groups. */
- const MDeformVert *dvert = (const MDeformVert *)CustomData_get_layer(&mesh->vdata,
- CD_MDEFORMVERT);
- if (dvert == nullptr) {
+ const Span<MDeformVert> dverts = mesh->deform_verts();
+ if (dverts.is_empty()) {
return invert_mask ? mesh : BKE_mesh_new_nomain_from_template(mesh, 0, 0, 0, 0, 0);
}
@@ -643,7 +667,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
}
vertex_mask = Array<bool>(mesh->totvert);
- compute_vertex_mask__armature_mode(dvert, mesh, armature_ob, mmd->threshold, vertex_mask);
+ compute_vertex_mask__armature_mode(
+ dverts.data(), mesh, armature_ob, mmd->threshold, vertex_mask);
}
else {
BLI_assert(mmd->mode == MOD_MASK_MODE_VGROUP);
@@ -655,7 +680,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
}
vertex_mask = Array<bool>(mesh->totvert);
- compute_vertex_mask__vertex_group_mode(dvert, defgrp_index, mmd->threshold, vertex_mask);
+ compute_vertex_mask__vertex_group_mode(
+ dverts.data(), defgrp_index, mmd->threshold, vertex_mask);
}
if (invert_mask) {
@@ -664,7 +690,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
Array<int> vertex_map(mesh->totvert);
uint verts_masked_num;
- compute_masked_vertices(vertex_mask, vertex_map, &verts_masked_num);
+ compute_masked_verts(vertex_mask, vertex_map, &verts_masked_num);
Array<int> edge_map(mesh->totedge);
uint edges_masked_num;
@@ -681,26 +707,26 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
Vector<int> new_loop_starts;
uint polys_masked_num;
uint loops_masked_num;
- computed_masked_polygons(mesh,
- vertex_mask,
- masked_poly_indices,
- new_loop_starts,
- &polys_masked_num,
- &loops_masked_num);
+ computed_masked_polys(mesh,
+ vertex_mask,
+ masked_poly_indices,
+ new_loop_starts,
+ &polys_masked_num,
+ &loops_masked_num);
uint edges_add_num = 0;
uint polys_add_num = 0;
uint loops_add_num = 0;
if (use_interpolation) {
- compute_interpolated_polygons(mesh,
- vertex_mask,
- verts_add_num,
- loops_masked_num,
- masked_poly_indices,
- new_loop_starts,
- &edges_add_num,
- &polys_add_num,
- &loops_add_num);
+ compute_interpolated_polys(mesh,
+ vertex_mask,
+ verts_add_num,
+ loops_masked_num,
+ masked_poly_indices,
+ new_loop_starts,
+ &edges_add_num,
+ &polys_add_num,
+ &loops_add_num);
}
Mesh *result = BKE_mesh_new_nomain_from_template(mesh,
@@ -710,13 +736,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
loops_masked_num + loops_add_num,
polys_masked_num + polys_add_num);
- copy_masked_vertices_to_new_mesh(*mesh, *result, vertex_map);
+ copy_masked_verts_to_new_mesh(*mesh, *result, vertex_map);
if (use_interpolation) {
add_interp_verts_copy_edges_to_new_mesh(*mesh,
*result,
vertex_mask,
vertex_map,
- dvert,
+ dverts.data(),
defgrp_index,
mmd->threshold,
edges_masked_num,
@@ -739,7 +765,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
vertex_mask,
vertex_map,
edge_map,
- dvert,
+ dverts.data(),
defgrp_index,
mmd->threshold,
masked_poly_indices,
@@ -804,7 +830,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Mask = {
- /* name */ "Mask",
+ /* name */ N_("Mask"),
/* structName */ "MaskModifierData",
/* structSize */ sizeof(MaskModifierData),
/* srna */ &RNA_MaskModifier,
diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
index 01c1875a760..0471beadcc1 100644
--- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
+++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc
@@ -7,6 +7,7 @@
#include <vector>
#include "BKE_geometry_set.hh"
+#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_wrapper.h"
@@ -14,6 +15,8 @@
#include "BKE_object.h"
#include "BKE_volume.h"
+#include "BLT_translation.h"
+
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -57,7 +60,7 @@ static void initData(ModifierData *md)
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md);
- DEG_add_modifier_to_transform_relation(ctx->node, "Mesh to Volume Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Mesh to Volume Modifier");
if (mvmd->object) {
DEG_add_object_relation(
ctx->node, mvmd->object, DEG_OB_COMP_GEOMETRY, "Mesh to Volume Modifier");
@@ -125,7 +128,7 @@ static Volume *mesh_to_volume(ModifierData *md,
if (object_to_convert == nullptr) {
return input_volume;
}
- Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_to_convert, false);
+ Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_to_convert);
if (mesh == nullptr) {
return input_volume;
}
@@ -161,7 +164,13 @@ static Volume *mesh_to_volume(ModifierData *md,
mesh_to_own_object_space_transform);
/* Create a new volume. */
- Volume *volume = BKE_volume_new_for_eval(input_volume);
+ Volume *volume;
+ if (input_volume == nullptr) {
+ volume = static_cast<Volume *>(BKE_id_new_nomain(ID_VO, "Volume"));
+ }
+ else {
+ volume = BKE_volume_new_for_eval(input_volume);
+ }
/* Convert mesh to grid and add to volume. */
geometry::volume_grid_add_from_mesh(volume,
@@ -195,7 +204,7 @@ static void modifyGeometrySet(ModifierData *md,
}
ModifierTypeInfo modifierType_MeshToVolume = {
- /* name */ "Mesh to Volume",
+ /* name */ N_("Mesh to Volume"),
/* structName */ "MeshToVolumeModifierData",
/* structSize */ sizeof(MeshToVolumeModifierData),
/* srna */ &RNA_MeshToVolumeModifier,
diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c
index 6f065797b43..822da40edb7 100644
--- a/source/blender/modifiers/intern/MOD_meshcache.c
+++ b/source/blender/modifiers/intern/MOD_meshcache.c
@@ -79,7 +79,7 @@ static void meshcache_do(MeshCacheModifierData *mcmd,
{
const bool use_factor = mcmd->factor < 1.0f;
int influence_group_index;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
MOD_get_vgroup(ob, mesh, mcmd->defgrp_name, &dvert, &influence_group_index);
float(*vertexCos_Store)[3] = (use_factor || influence_group_index != -1 ||
@@ -182,16 +182,16 @@ static void meshcache_do(MeshCacheModifierData *mcmd,
float(*vertexCos_Source)[3] = MEM_malloc_arrayN(
verts_num, sizeof(*vertexCos_Source), __func__);
float(*vertexCos_New)[3] = MEM_malloc_arrayN(verts_num, sizeof(*vertexCos_New), __func__);
- MVert *mv = me->mvert;
+ const MVert *mv = BKE_mesh_verts(me);
for (i = 0; i < verts_num; i++, mv++) {
copy_v3_v3(vertexCos_Source[i], mv->co);
}
BKE_mesh_calc_relative_deform(
- me->mpoly,
+ BKE_mesh_polys(me),
me->totpoly,
- me->mloop,
+ BKE_mesh_loops(me),
me->totvert,
(const float(*)[3])vertexCos_Source, /* From the original Mesh. */
@@ -257,7 +257,7 @@ static void meshcache_do(MeshCacheModifierData *mcmd,
const float global_offset = (mcmd->flag & MOD_MESHCACHE_INVERT_VERTEX_GROUP) ?
mcmd->factor :
0.0f;
- if (mesh->dvert != NULL) {
+ if (BKE_mesh_deform_verts(mesh) != NULL) {
for (int i = 0; i < verts_num; i++) {
/* For each vertex, compute its blending factor between the mesh cache (for `fac = 0`)
* and the former position of the vertex (for `fac = 1`). */
@@ -297,7 +297,7 @@ static void deformVerts(ModifierData *md,
if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') {
/* `mesh_src` is only needed for vertex groups. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, verts_num);
@@ -320,8 +320,7 @@ static void deformVertsEM(ModifierData *md,
if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') {
/* `mesh_src` is only needed for vertex groups. */
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
@@ -416,7 +415,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_MeshCache = {
- /* name */ "MeshCache",
+ /* name */ N_("MeshCache"),
/* structName */ "MeshCacheModifierData",
/* structSize */ sizeof(MeshCacheModifierData),
/* srna */ &RNA_MeshCacheModifier,
diff --git a/source/blender/modifiers/intern/MOD_meshcache_util.h b/source/blender/modifiers/intern/MOD_meshcache_util.h
index 276bdf72bc3..2726f2d7efb 100644
--- a/source/blender/modifiers/intern/MOD_meshcache_util.h
+++ b/source/blender/modifiers/intern/MOD_meshcache_util.h
@@ -8,12 +8,8 @@
/* MOD_meshcache_mdd.c */
-bool MOD_meshcache_read_mdd_index(FILE *fp,
- float (*vertexCos)[3],
- int vertex_tot,
- int index,
- float factor,
- const char **err_str);
+bool MOD_meshcache_read_mdd_index(
+ FILE *fp, float (*vertexCos)[3], int verts_tot, int index, float factor, const char **err_str);
bool MOD_meshcache_read_mdd_frame(FILE *fp,
float (*vertexCos)[3],
int verts_tot,
diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c
index 5dac31ac1df..d7ed346a61f 100644
--- a/source/blender/modifiers/intern/MOD_meshdeform.c
+++ b/source/blender/modifiers/intern/MOD_meshdeform.c
@@ -119,9 +119,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
}
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
MeshDeformModifierData *mmd = (MeshDeformModifierData *)md;
@@ -160,7 +158,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_GEOMETRY, "Mesh Deform Modifier");
}
/* We need own transformation as well. */
- DEG_add_modifier_to_transform_relation(ctx->node, "Mesh Deform Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Mesh Deform Modifier");
}
static float meshdeform_dynamic_bind(MeshDeformModifierData *mmd, float (*dco)[3], float vec[3])
@@ -330,7 +328,7 @@ static void meshdeformModifier_do(ModifierData *md,
Object *ob = ctx->object;
Mesh *cagemesh;
- MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
float imat[4][4], cagemat[4][4], iobmat[4][4], icagemat[3][3], cmat[4][4];
float(*dco)[3] = NULL, (*bindcagecos)[3];
int a, cage_verts_num, defgrp_index;
@@ -353,7 +351,7 @@ static void meshdeformModifier_do(ModifierData *md,
* We'll support this case once granular dependency graph is landed.
*/
Object *ob_target = mmd->object;
- cagemesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false);
+ cagemesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
if (cagemesh == NULL) {
BKE_modifier_set_error(ctx->object, md, "Cannot get mesh from cage object");
return;
@@ -444,8 +442,7 @@ static void deformVerts(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */
@@ -463,10 +460,9 @@ static void deformVertsEM(ModifierData *md,
float (*vertexCos)[3],
int verts_num)
{
- Mesh *mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -650,7 +646,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_MeshDeform = {
- /* name */ "MeshDeform",
+ /* name */ N_("MeshDeform"),
/* structName */ "MeshDeformModifierData",
/* structSize */ sizeof(MeshDeformModifierData),
/* srna */ &RNA_MeshDeformModifier,
diff --git a/source/blender/modifiers/intern/MOD_meshsequencecache.cc b/source/blender/modifiers/intern/MOD_meshsequencecache.cc
index 273050eafd8..f30e6a95787 100644
--- a/source/blender/modifiers/intern/MOD_meshsequencecache.cc
+++ b/source/blender/modifiers/intern/MOD_meshsequencecache.cc
@@ -60,6 +60,8 @@
# include "usd.h"
#endif
+using blender::Span;
+
static void initData(ModifierData *md)
{
MeshSeqCacheModifierData *mcmd = reinterpret_cast<MeshSeqCacheModifierData *>(md);
@@ -176,13 +178,18 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
if (me != nullptr) {
- MVert *mvert = mesh->mvert;
- MEdge *medge = mesh->medge;
- MPoly *mpoly = mesh->mpoly;
+ const Span<MVert> mesh_verts = mesh->verts();
+ const Span<MEdge> mesh_edges = mesh->edges();
+ const Span<MPoly> mesh_polys = mesh->polys();
+ const Span<MVert> me_verts = me->verts();
+ const Span<MEdge> me_edges = me->edges();
+ const Span<MPoly> me_polys = me->polys();
/* TODO(sybren+bastien): possibly check relevant custom data layers (UV/color depending on
- * flags) and duplicate those too. */
- if ((me->mvert == mvert) || (me->medge == medge) || (me->mpoly == mpoly)) {
+ * flags) and duplicate those too.
+ * XXX(Hans): This probably isn't true anymore with various CoW improvements, etc. */
+ if ((me_verts.data() == mesh_verts.data()) || (me_edges.data() == mesh_edges.data()) ||
+ (me_polys.data() == mesh_polys.data())) {
/* We need to duplicate data here, otherwise we'll modify org mesh, see T51701. */
mesh = reinterpret_cast<Mesh *>(
BKE_id_copy_ex(nullptr,
@@ -392,7 +399,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
}
ModifierTypeInfo modifierType_MeshSequenceCache = {
- /* name */ "MeshSequenceCache",
+ /* name */ N_("MeshSequenceCache"),
/* structName */ "MeshSeqCacheModifierData",
/* structSize */ sizeof(MeshSeqCacheModifierData),
/* srna */ &RNA_MeshSequenceCacheModifier,
diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c
index b6ba8c9e0f9..f1a36c04453 100644
--- a/source/blender/modifiers/intern/MOD_mirror.c
+++ b/source/blender/modifiers/intern/MOD_mirror.c
@@ -62,7 +62,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
MirrorModifierData *mmd = (MirrorModifierData *)md;
if (mmd->mirror_ob != NULL) {
DEG_add_object_relation(ctx->node, mmd->mirror_ob, DEG_OB_COMP_TRANSFORM, "Mirror Modifier");
- DEG_add_modifier_to_transform_relation(ctx->node, "Mirror Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Mirror Modifier");
}
}
@@ -204,7 +204,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Mirror = {
- /* name */ "Mirror",
+ /* name */ N_("Mirror"),
/* structName */ "MirrorModifierData",
/* structSize */ sizeof(MirrorModifierData),
/* srna */ &RNA_MirrorModifier,
diff --git a/source/blender/modifiers/intern/MOD_multires.c b/source/blender/modifiers/intern/MOD_multires.c
index a4c5ddac5c9..87108836a90 100644
--- a/source/blender/modifiers/intern/MOD_multires.c
+++ b/source/blender/modifiers/intern/MOD_multires.c
@@ -62,9 +62,7 @@ static void initData(ModifierData *md)
md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT | UI_SUBPANEL_DATA_EXPAND_1;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
MultiresModifierData *mmd = (MultiresModifierData *)md;
if (mmd->flags & eMultiresModifierFlag_UseCustomNormals) {
@@ -487,7 +485,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Multires = {
- /* name */ "Multires",
+ /* name */ N_("Multires"),
/* structName */ "MultiresModifierData",
/* structSize */ sizeof(MultiresModifierData),
/* srna */ &RNA_MultiresModifier,
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 885d2f901ec..2910f44e37a 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -36,11 +36,12 @@
#include "DNA_windowmanager_types.h"
#include "BKE_attribute_math.hh"
+#include "BKE_compute_contexts.hh"
#include "BKE_customdata.h"
#include "BKE_geometry_fields.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_global.h"
-#include "BKE_idprop.h"
+#include "BKE_idprop.hh"
#include "BKE_lib_id.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
@@ -73,7 +74,6 @@
#include "MOD_modifiertypes.h"
#include "MOD_nodes.h"
-#include "MOD_nodes_evaluator.hh"
#include "MOD_ui_common.h"
#include "ED_object.h"
@@ -81,15 +81,18 @@
#include "ED_spreadsheet.h"
#include "ED_undo.h"
-#include "NOD_derived_node_tree.hh"
#include "NOD_geometry.h"
-#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_node_declaration.hh"
#include "FN_field.hh"
#include "FN_field_cpp_type.hh"
+#include "FN_lazy_function_execute.hh"
+#include "FN_lazy_function_graph_executor.hh"
#include "FN_multi_function.hh"
+namespace lf = blender::fn::lazy_function;
+
using blender::Array;
using blender::ColorGeometry4f;
using blender::CPPType;
@@ -106,22 +109,31 @@ using blender::MultiValueMap;
using blender::MutableSpan;
using blender::Set;
using blender::Span;
+using blender::Stack;
using blender::StringRef;
using blender::StringRefNull;
using blender::Vector;
using blender::bke::AttributeMetaData;
+using blender::bke::AttributeValidator;
using blender::fn::Field;
+using blender::fn::FieldOperation;
using blender::fn::GField;
using blender::fn::ValueOrField;
using blender::fn::ValueOrFieldCPPType;
using blender::nodes::FieldInferencingInterface;
using blender::nodes::GeoNodeExecParams;
using blender::nodes::InputSocketFieldType;
+using blender::nodes::geo_eval_log::GeoModifierLog;
using blender::threading::EnumerableThreadSpecific;
using namespace blender::fn::multi_function_types;
-using namespace blender::nodes::derived_node_tree_types;
-using geo_log::eNamedAttrUsage;
-using geo_log::GeometryAttributeInfo;
+using blender::nodes::geo_eval_log::GeometryAttributeInfo;
+using blender::nodes::geo_eval_log::GeometryInfoLog;
+using blender::nodes::geo_eval_log::GeoNodeLog;
+using blender::nodes::geo_eval_log::GeoTreeLog;
+using blender::nodes::geo_eval_log::NamedAttributeUsage;
+using blender::nodes::geo_eval_log::NodeWarning;
+using blender::nodes::geo_eval_log::NodeWarningType;
+using blender::nodes::geo_eval_log::ValueLog;
static void initData(ModifierData *md)
{
@@ -200,7 +212,7 @@ static bool node_needs_own_transform_relation(const bNode &node)
static void process_nodes_for_depsgraph(const bNodeTree &tree,
Set<ID *> &ids,
- bool &needs_own_transform_relation)
+ bool &r_needs_own_transform_relation)
{
Set<const bNodeTree *> handled_groups;
@@ -211,10 +223,10 @@ static void process_nodes_for_depsgraph(const bNodeTree &tree,
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
const bNodeTree *group = (bNodeTree *)node->id;
if (group != nullptr && handled_groups.add(group)) {
- process_nodes_for_depsgraph(*group, ids, needs_own_transform_relation);
+ process_nodes_for_depsgraph(*group, ids, r_needs_own_transform_relation);
}
}
- needs_own_transform_relation |= node_needs_own_transform_relation(*node);
+ r_needs_own_transform_relation |= node_needs_own_transform_relation(*node);
}
}
@@ -297,6 +309,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
case ID_IM:
case ID_TE: {
DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier");
+ break;
}
default: {
/* Purposefully don't add relations for materials. While there are material sockets,
@@ -307,7 +320,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
if (needs_own_transform_relation) {
- DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Nodes Modifier");
}
}
@@ -416,15 +429,16 @@ static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int soc
return field_interface.inputs[socket_index] != InputSocketFieldType::None;
}
-static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
+static std::unique_ptr<IDProperty, blender::bke::idprop::IDPropertyDeleter>
+id_property_create_from_socket(const bNodeSocket &socket)
{
+ using namespace blender;
switch (socket.type) {
case SOCK_FLOAT: {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.f = value->value;
- IDProperty *property = IDP_New(IDP_FLOAT, &idprop, socket.identifier);
- IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property);
+ const bNodeSocketValueFloat *value = static_cast<const bNodeSocketValueFloat *>(
+ socket.default_value);
+ auto property = bke::idprop::create(socket.identifier, value->value);
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
ui_data->base.rna_subtype = value->subtype;
ui_data->min = ui_data->soft_min = (double)value->min;
ui_data->max = ui_data->soft_max = (double)value->max;
@@ -432,11 +446,10 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
return property;
}
case SOCK_INT: {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value;
- IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier);
- IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property);
+ const bNodeSocketValueInt *value = static_cast<const bNodeSocketValueInt *>(
+ socket.default_value);
+ auto property = bke::idprop::create(socket.identifier, value->value);
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get());
ui_data->base.rna_subtype = value->subtype;
ui_data->min = ui_data->soft_min = value->min;
ui_data->max = ui_data->soft_max = value->max;
@@ -444,13 +457,11 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
return property;
}
case SOCK_VECTOR: {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.array.len = 3;
- idprop.array.type = IDP_FLOAT;
- IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier);
- copy_v3_v3((float *)IDP_Array(property), value->value);
- IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property);
+ const bNodeSocketValueVector *value = static_cast<const bNodeSocketValueVector *>(
+ socket.default_value);
+ auto property = bke::idprop::create(
+ socket.identifier, Span<float>{value->value[0], value->value[1], value->value[2]});
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
ui_data->base.rna_subtype = value->subtype;
ui_data->min = ui_data->soft_min = (double)value->min;
ui_data->max = ui_data->soft_max = (double)value->max;
@@ -462,13 +473,12 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
return property;
}
case SOCK_RGBA: {
- bNodeSocketValueRGBA *value = (bNodeSocketValueRGBA *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.array.len = 4;
- idprop.array.type = IDP_FLOAT;
- IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier);
- copy_v4_v4((float *)IDP_Array(property), value->value);
- IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property);
+ const bNodeSocketValueRGBA *value = static_cast<const bNodeSocketValueRGBA *>(
+ socket.default_value);
+ auto property = bke::idprop::create(
+ socket.identifier,
+ Span<float>{value->value[0], value->value[1], value->value[2], value->value[3]});
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get());
ui_data->base.rna_subtype = PROP_COLOR;
ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__);
ui_data->default_array_len = 4;
@@ -482,53 +492,48 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
return property;
}
case SOCK_BOOLEAN: {
- bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value != 0;
- IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier);
- IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property);
+ const bNodeSocketValueBoolean *value = static_cast<const bNodeSocketValueBoolean *>(
+ socket.default_value);
+ auto property = bke::idprop::create(socket.identifier, int(value->value));
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get());
ui_data->min = ui_data->soft_min = 0;
ui_data->max = ui_data->soft_max = 1;
ui_data->default_value = value->value != 0;
return property;
}
case SOCK_STRING: {
- bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
- IDProperty *property = IDP_NewString(
- value->value, socket.identifier, BLI_strnlen(value->value, sizeof(value->value)) + 1);
- IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(property);
+ const bNodeSocketValueString *value = static_cast<const bNodeSocketValueString *>(
+ socket.default_value);
+ auto property = bke::idprop::create(socket.identifier, value->value);
+ IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(
+ property.get());
ui_data->default_value = BLI_strdup(value->value);
return property;
}
case SOCK_OBJECT: {
- bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, socket.identifier);
+ const bNodeSocketValueObject *value = static_cast<const bNodeSocketValueObject *>(
+ socket.default_value);
+ return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_COLLECTION: {
- bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, socket.identifier);
+ const bNodeSocketValueCollection *value = static_cast<const bNodeSocketValueCollection *>(
+ socket.default_value);
+ return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_TEXTURE: {
- bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, socket.identifier);
+ const bNodeSocketValueTexture *value = static_cast<const bNodeSocketValueTexture *>(
+ socket.default_value);
+ return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_IMAGE: {
- bNodeSocketValueImage *value = (bNodeSocketValueImage *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, socket.identifier);
+ const bNodeSocketValueImage *value = static_cast<const bNodeSocketValueImage *>(
+ socket.default_value);
+ return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_MATERIAL: {
- bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, socket.identifier);
+ const bNodeSocketValueMaterial *value = static_cast<const bNodeSocketValueMaterial *>(
+ socket.default_value);
+ return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
}
return nullptr;
@@ -658,7 +663,7 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
int socket_index;
LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) {
- IDProperty *new_prop = id_property_create_from_socket(*socket);
+ IDProperty *new_prop = id_property_create_from_socket(*socket).release();
if (new_prop == nullptr) {
/* Out of the set of supported input sockets, only
* geometry sockets aren't added to the modifier. */
@@ -763,36 +768,37 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
}
static void initialize_group_input(NodesModifierData &nmd,
- const OutputSocketRef &socket,
+ const bNodeSocket &interface_socket,
+ const int input_index,
void *r_value)
{
- const bNodeSocketType &socket_type = *socket.typeinfo();
- const bNodeSocket &bsocket = *socket.bsocket();
- const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>(bsocket.type);
+ const bNodeSocketType &socket_type = *interface_socket.typeinfo;
+ const eNodeSocketDatatype socket_data_type = static_cast<eNodeSocketDatatype>(
+ interface_socket.type);
if (nmd.settings.properties == nullptr) {
- socket_type.get_geometry_nodes_cpp_value(bsocket, r_value);
+ socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value);
return;
}
const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties,
- socket.identifier().c_str());
+ interface_socket.identifier);
if (property == nullptr) {
- socket_type.get_geometry_nodes_cpp_value(bsocket, r_value);
+ socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value);
return;
}
- if (!id_property_type_matches_socket(bsocket, *property)) {
- socket_type.get_geometry_nodes_cpp_value(bsocket, r_value);
+ if (!id_property_type_matches_socket(interface_socket, *property)) {
+ socket_type.get_geometry_nodes_cpp_value(interface_socket, r_value);
return;
}
- if (!input_has_attribute_toggle(*nmd.node_group, socket.index())) {
+ if (!input_has_attribute_toggle(*nmd.node_group, input_index)) {
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
return;
}
const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup(
- nmd.settings.properties, (socket.identifier() + use_attribute_suffix).c_str());
+ nmd.settings.properties, (interface_socket.identifier + use_attribute_suffix).c_str());
const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup(
- nmd.settings.properties, (socket.identifier() + attribute_name_suffix).c_str());
+ nmd.settings.properties, (interface_socket.identifier + attribute_name_suffix).c_str());
if (property_use_attribute == nullptr || property_attribute_name == nullptr) {
init_socket_cpp_value_from_property(*property, socket_data_type, r_value);
return;
@@ -838,13 +844,25 @@ static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
return spreadsheets;
}
-static void find_sockets_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet,
- NodesModifierData *nmd,
- const ModifierEvalContext *ctx,
- const DerivedNodeTree &tree,
- Set<DSocket> &r_sockets_to_preview)
+static const lf::FunctionNode &find_viewer_lf_node(const bNode &viewer_bnode)
{
- Vector<SpreadsheetContext *> context_path = sspreadsheet->context_path;
+ return *blender::nodes::ensure_geometry_nodes_lazy_function_graph(viewer_bnode.owner_tree())
+ ->mapping.viewer_node_map.lookup(&viewer_bnode);
+}
+static const lf::FunctionNode &find_group_lf_node(const bNode &group_bnode)
+{
+ return *blender::nodes::ensure_geometry_nodes_lazy_function_graph(group_bnode.owner_tree())
+ ->mapping.group_node_map.lookup(&group_bnode);
+}
+
+static void find_side_effect_nodes_for_spreadsheet(
+ const SpaceSpreadsheet &sspreadsheet,
+ const NodesModifierData &nmd,
+ const ModifierEvalContext &ctx,
+ const bNodeTree &root_tree,
+ MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
+{
+ Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path;
if (context_path.size() < 3) {
return;
}
@@ -855,11 +873,11 @@ static void find_sockets_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadshe
return;
}
SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0];
- if (object_context->object != DEG_get_original_object(ctx->object)) {
+ 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) {
+ if (StringRef(modifier_context->modifier_name) != nmd.modifier.name) {
return;
}
for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) {
@@ -868,61 +886,77 @@ static void find_sockets_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadshe
}
}
- Span<SpreadsheetContextNode *> nested_group_contexts =
+ blender::ComputeContextBuilder compute_context_builder;
+ compute_context_builder.push<blender::bke::ModifierComputeContext>(nmd.modifier.name);
+
+ const 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 SpreadsheetContextNode *last_context = (SpreadsheetContextNode *)context_path.last();
- const DTreeContext *context = &tree.root_context();
+ Stack<const bNode *> group_node_stack;
+ const bNodeTree *group = &root_tree;
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;
+ const bNode *found_node = nullptr;
+ for (const bNode *node : group->group_nodes()) {
+ if (STREQ(node->name, node_context->node_name)) {
+ found_node = node;
break;
}
}
if (found_node == nullptr) {
return;
}
- context = context->child_context(*found_node);
- if (context == nullptr) {
+ if (found_node->id == nullptr) {
return;
}
+ group_node_stack.push(found_node);
+ group = reinterpret_cast<const bNodeTree *>(found_node->id);
+ compute_context_builder.push<blender::bke::NodeGroupComputeContext>(node_context->node_name);
}
- const NodeTreeRef &tree_ref = context->tree();
- for (const NodeRef *node_ref : tree_ref.nodes_by_type("GeometryNodeViewer")) {
- if (node_ref->name() == last_context->node_name) {
- const DNode viewer_node{context, node_ref};
- for (const InputSocketRef *input_socket : node_ref->inputs()) {
- if (input_socket->is_available() && input_socket->is_logically_linked()) {
- r_sockets_to_preview.add(DSocket{context, input_socket});
- }
- }
+ const bNode *found_viewer_node = nullptr;
+ for (const bNode *viewer_node : group->nodes_by_type("GeometryNodeViewer")) {
+ if (STREQ(viewer_node->name, last_context->node_name)) {
+ found_viewer_node = viewer_node;
+ break;
}
}
+ if (found_viewer_node == nullptr) {
+ return;
+ }
+
+ /* Not only mark the viewer node as having side effects, but also all group nodes it is contained
+ * in. */
+ r_side_effect_nodes.add(compute_context_builder.hash(),
+ &find_viewer_lf_node(*found_viewer_node));
+ compute_context_builder.pop();
+ while (!compute_context_builder.is_empty()) {
+ r_side_effect_nodes.add(compute_context_builder.hash(),
+ &find_group_lf_node(*group_node_stack.pop()));
+ compute_context_builder.pop();
+ }
}
-static void find_sockets_to_preview(NodesModifierData *nmd,
- const ModifierEvalContext *ctx,
- const DerivedNodeTree &tree,
- Set<DSocket> &r_sockets_to_preview)
+static void find_side_effect_nodes(
+ const NodesModifierData &nmd,
+ const ModifierEvalContext &ctx,
+ const bNodeTree &tree,
+ MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> &r_side_effect_nodes)
{
- Main *bmain = DEG_get_bmain(ctx->depsgraph);
+ 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) {
- find_sockets_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree, r_sockets_to_preview);
+ find_side_effect_nodes_for_spreadsheet(*sspreadsheet, nmd, ctx, tree, r_side_effect_nodes);
}
}
static void clear_runtime_data(NodesModifierData *nmd)
{
if (nmd->runtime_eval_log != nullptr) {
- delete (geo_log::ModifierLog *)nmd->runtime_eval_log;
+ delete static_cast<GeoModifierLog *>(nmd->runtime_eval_log);
nmd->runtime_eval_log = nullptr;
}
}
@@ -944,15 +978,15 @@ struct OutputAttributeToStore {
* can be evaluated together.
*/
static MultiValueMap<eAttrDomain, OutputAttributeInfo> find_output_attributes_to_store(
- const NodesModifierData &nmd, const NodeRef &output_node, Span<GMutablePointer> output_values)
+ const NodesModifierData &nmd, const bNode &output_node, Span<GMutablePointer> output_values)
{
MultiValueMap<eAttrDomain, OutputAttributeInfo> outputs_by_domain;
- for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) {
- if (!socket_type_has_attribute_toggle(*socket->bsocket())) {
+ for (const bNodeSocket *socket : output_node.input_sockets().drop_front(1).drop_back(1)) {
+ if (!socket_type_has_attribute_toggle(*socket)) {
continue;
}
- const std::string prop_name = socket->identifier() + attribute_name_suffix;
+ const std::string prop_name = socket->identifier + attribute_name_suffix;
const IDProperty *prop = IDP_GetPropertyFromGroup(nmd.settings.properties, prop_name.c_str());
if (prop == nullptr) {
continue;
@@ -972,7 +1006,7 @@ static MultiValueMap<eAttrDomain, OutputAttributeInfo> find_output_attributes_to
const GField field = cpp_type->as_field(value.get());
const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink(
- &nmd.node_group->outputs, socket->index());
+ &nmd.node_group->outputs, index);
const eAttrDomain domain = (eAttrDomain)interface_socket->attribute_domain;
OutputAttributeInfo output_info;
output_info.field = std::move(field);
@@ -1010,17 +1044,19 @@ static Vector<OutputAttributeToStore> compute_attributes_to_store(
continue;
}
const int domain_size = attributes.domain_size(domain);
- blender::bke::GeometryComponentFieldContext field_context{component, domain};
+ blender::bke::GeometryFieldContext field_context{component, domain};
blender::fn::FieldEvaluator field_evaluator{field_context, domain_size};
for (const OutputAttributeInfo &output_info : outputs_info) {
const CPPType &type = output_info.field.cpp_type();
+ const AttributeValidator validator = attributes.lookup_validator(output_info.name);
OutputAttributeToStore store{
component_type,
domain,
output_info.name,
GMutableSpan{
type, MEM_malloc_arrayN(domain_size, type.size(), __func__), domain_size}};
- field_evaluator.add_with_destination(output_info.field, store.data);
+ GField field = validator.validate_field_if_necessary(output_info.field);
+ field_evaluator.add_with_destination(std::move(field), store.data);
attributes_to_store.append(store);
}
field_evaluator.evaluate();
@@ -1052,7 +1088,7 @@ static void store_computed_output_attributes(
if (attributes.add(store.name,
store.domain,
blender::bke::cpp_type_to_custom_data_type(store.data.type()),
- blender::bke::AttributeInitMove(store.data.data()))) {
+ blender::bke::AttributeInitMoveArray(store.data.data()))) {
continue;
}
@@ -1071,7 +1107,7 @@ static void store_computed_output_attributes(
static void store_output_attributes(GeometrySet &geometry,
const NodesModifierData &nmd,
- const NodeRef &output_node,
+ const bNode &output_node,
Span<GMutablePointer> output_values)
{
/* All new attribute values have to be computed before the geometry is actually changed. This is
@@ -1086,91 +1122,103 @@ static void store_output_attributes(GeometrySet &geometry,
/**
* Evaluate a node group to compute the output geometry.
*/
-static GeometrySet compute_geometry(const DerivedNodeTree &tree,
- Span<const NodeRef *> group_input_nodes,
- const NodeRef &output_node,
- GeometrySet input_geometry_set,
- NodesModifierData *nmd,
- const ModifierEvalContext *ctx)
+static GeometrySet compute_geometry(
+ const bNodeTree &btree,
+ const blender::nodes::GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
+ const bNode &output_node,
+ GeometrySet input_geometry_set,
+ NodesModifierData *nmd,
+ const ModifierEvalContext *ctx)
{
- blender::ResourceScope scope;
- blender::LinearAllocator<> &allocator = scope.linear_allocator();
- blender::nodes::NodeMultiFunctions mf_by_node{tree};
+ const blender::nodes::GeometryNodeLazyFunctionGraphMapping &mapping = lf_graph_info.mapping;
- Map<DOutputSocket, GMutablePointer> group_inputs;
+ Span<const lf::OutputSocket *> graph_inputs = mapping.group_input_sockets;
+ Vector<const lf::InputSocket *> graph_outputs;
+ for (const bNodeSocket *bsocket : output_node.input_sockets().drop_back(1)) {
+ const lf::InputSocket &socket = mapping.dummy_socket_map.lookup(bsocket)->as_input();
+ graph_outputs.append(&socket);
+ }
- const DTreeContext *root_context = &tree.root_context();
- 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;
- }
+ Array<GMutablePointer> param_inputs(graph_inputs.size());
+ Array<GMutablePointer> param_outputs(graph_outputs.size());
+ Array<std::optional<lf::ValueUsage>> param_input_usages(graph_inputs.size());
+ Array<lf::ValueUsage> param_output_usages(graph_outputs.size(), lf::ValueUsage::Used);
+ Array<bool> param_set_outputs(graph_outputs.size(), false);
- Span<const OutputSocketRef *> remaining_input_sockets = group_input_sockets;
+ blender::nodes::GeometryNodesLazyFunctionLogger lf_logger(lf_graph_info);
+ blender::nodes::GeometryNodesLazyFunctionSideEffectProvider lf_side_effect_provider;
- /* If the group expects a geometry as first input, use the geometry that has been passed to
- * modifier. */
- const OutputSocketRef *first_input_socket = group_input_sockets[0];
- if (first_input_socket->bsocket()->type == SOCK_GEOMETRY) {
- GeometrySet *geometry_set_in =
- 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);
- }
+ lf::GraphExecutor graph_executor{
+ lf_graph_info.graph, graph_inputs, graph_outputs, &lf_logger, &lf_side_effect_provider};
- /* Initialize remaining group inputs. */
- for (const OutputSocketRef *socket : remaining_input_sockets) {
- const CPPType &cpp_type = *socket->typeinfo()->geometry_nodes_cpp_type;
- void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
- initialize_group_input(*nmd, *socket, value_in);
- group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
+ blender::nodes::GeoNodesModifierData geo_nodes_modifier_data;
+ geo_nodes_modifier_data.depsgraph = ctx->depsgraph;
+ geo_nodes_modifier_data.self_object = ctx->object;
+ auto eval_log = std::make_unique<GeoModifierLog>();
+ if (logging_enabled(ctx)) {
+ geo_nodes_modifier_data.eval_log = eval_log.get();
+ }
+ MultiValueMap<blender::ComputeContextHash, const lf::FunctionNode *> r_side_effect_nodes;
+ find_side_effect_nodes(*nmd, *ctx, btree, r_side_effect_nodes);
+ geo_nodes_modifier_data.side_effect_nodes = &r_side_effect_nodes;
+ blender::nodes::GeoNodesLFUserData user_data;
+ user_data.modifier_data = &geo_nodes_modifier_data;
+ blender::bke::ModifierComputeContext modifier_compute_context{nullptr, nmd->modifier.name};
+ user_data.compute_context = &modifier_compute_context;
+
+ blender::LinearAllocator<> allocator;
+ Vector<GMutablePointer> inputs_to_destruct;
+
+ int input_index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, interface_socket, &btree.inputs, input_index) {
+ if (interface_socket->type == SOCK_GEOMETRY && input_index == 0) {
+ param_inputs[input_index] = &input_geometry_set;
+ continue;
}
- }
- Vector<DInputSocket> group_outputs;
- for (const InputSocketRef *socket_ref : output_node.inputs().drop_back(1)) {
- group_outputs.append({root_context, socket_ref});
+ const CPPType *type = interface_socket->typeinfo->geometry_nodes_cpp_type;
+ BLI_assert(type != nullptr);
+ void *value = allocator.allocate(type->size(), type->alignment());
+ initialize_group_input(*nmd, *interface_socket, input_index, value);
+ param_inputs[input_index] = {type, value};
+ inputs_to_destruct.append({type, value});
}
- std::optional<geo_log::GeoLogger> geo_logger;
-
- blender::modifiers::geometry_nodes::GeometryNodesEvaluationParams eval_params;
-
- if (logging_enabled(ctx)) {
- Set<DSocket> preview_sockets;
- find_sockets_to_preview(nmd, ctx, tree, preview_sockets);
- eval_params.force_compute_sockets.extend(preview_sockets.begin(), preview_sockets.end());
- geo_logger.emplace(std::move(preview_sockets));
-
- geo_logger->log_input_geometry(input_geometry_set);
+ for (const int i : graph_outputs.index_range()) {
+ const lf::InputSocket &socket = *graph_outputs[i];
+ const CPPType &type = socket.type();
+ void *buffer = allocator.allocate(type.size(), type.alignment());
+ param_outputs[i] = {type, buffer};
}
- /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */
- input_geometry_set.clear();
+ lf::Context lf_context;
+ lf_context.storage = graph_executor.init_storage(allocator);
+ lf_context.user_data = &user_data;
+ lf::BasicParams lf_params{graph_executor,
+ param_inputs,
+ param_outputs,
+ param_input_usages,
+ param_output_usages,
+ param_set_outputs};
+ graph_executor.execute(lf_params, lf_context);
+ graph_executor.destruct_storage(lf_context.storage);
- 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.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr;
- blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params);
+ for (GMutablePointer &ptr : inputs_to_destruct) {
+ ptr.destruct();
+ }
- GeometrySet output_geometry_set = std::move(*eval_params.r_output_values[0].get<GeometrySet>());
+ GeometrySet output_geometry_set = std::move(*static_cast<GeometrySet *>(param_outputs[0].get()));
+ store_output_attributes(output_geometry_set, *nmd, output_node, param_outputs);
- if (geo_logger.has_value()) {
- geo_logger->log_output_geometry(output_geometry_set);
- NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(ctx->object,
- &nmd->modifier);
- clear_runtime_data(nmd_orig);
- nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger);
+ for (GMutablePointer &ptr : param_outputs) {
+ ptr.destruct();
}
- store_output_attributes(output_geometry_set, *nmd, output_node, eval_params.r_output_values);
-
- for (GMutablePointer value : eval_params.r_output_values) {
- value.destruct();
+ if (logging_enabled(ctx)) {
+ NodesModifierData *nmd_orig = reinterpret_cast<NodesModifierData *>(
+ BKE_modifier_get_original(ctx->object, &nmd->modifier));
+ delete static_cast<GeoModifierLog *>(nmd_orig->runtime_eval_log);
+ nmd_orig->runtime_eval_log = eval_log.release();
}
return output_geometry_set;
@@ -1231,37 +1279,35 @@ static void modifyGeometry(ModifierData *md,
return;
}
+ const bNodeTree &tree = *nmd->node_group;
+ tree.ensure_topology_cache();
check_property_socket_sync(ctx->object, md);
- NodeTreeRefMap tree_refs;
- DerivedNodeTree tree{*nmd->node_group, tree_refs};
-
- if (tree.has_link_cycles()) {
- BKE_modifier_set_error(ctx->object, md, "Node group has cycles");
+ const bNode *output_node = tree.group_output_node();
+ if (output_node == nullptr) {
+ BKE_modifier_set_error(ctx->object, md, "Node group must have a group output node");
geometry_set.clear();
return;
}
- const NodeTreeRef &root_tree_ref = tree.root_context().tree();
- 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 (output_nodes.size() != 1) {
- BKE_modifier_set_error(ctx->object, md, "Node group must have a single output node");
+ Span<const bNodeSocket *> group_outputs = output_node->input_sockets().drop_back(1);
+ if (group_outputs.is_empty()) {
+ BKE_modifier_set_error(ctx->object, md, "Node group must have an output socket");
geometry_set.clear();
return;
}
- const NodeRef &output_node = *output_nodes[0];
- Span<const InputSocketRef *> group_outputs = output_node.inputs().drop_back(1);
- if (group_outputs.is_empty()) {
- BKE_modifier_set_error(ctx->object, md, "Node group must have an output socket");
+ const bNodeSocket *first_output_socket = group_outputs[0];
+ if (!STREQ(first_output_socket->idname, "NodeSocketGeometry")) {
+ BKE_modifier_set_error(ctx->object, md, "Node group's first output must be a geometry");
geometry_set.clear();
return;
}
- const InputSocketRef *first_output_socket = group_outputs[0];
- if (first_output_socket->idname() != "NodeSocketGeometry") {
- BKE_modifier_set_error(ctx->object, md, "Node group's first output must be a geometry");
+ const blender::nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
+ blender::nodes::ensure_geometry_nodes_lazy_function_graph(tree);
+ if (lf_graph_info == nullptr) {
+ BKE_modifier_set_error(ctx->object, md, "Cannot evaluate node group");
geometry_set.clear();
return;
}
@@ -1277,7 +1323,7 @@ static void modifyGeometry(ModifierData *md,
}
geometry_set = compute_geometry(
- tree, input_nodes, output_node, std::move(geometry_set), nmd, ctx);
+ tree, *lf_graph_info, *output_node, std::move(geometry_set), nmd, ctx);
if (geometry_set.has_mesh()) {
/* Add #CD_ORIGINDEX layers if they don't exist already. This is required because the
@@ -1285,13 +1331,13 @@ static void modifyGeometry(ModifierData *md,
* assumed that the output mesh does not have a mapping to the original mesh. */
Mesh &mesh = *geometry_set.get_mesh_for_write();
if (use_orig_index_verts) {
- CustomData_add_layer(&mesh.vdata, CD_ORIGINDEX, CD_DEFAULT, nullptr, mesh.totvert);
+ CustomData_add_layer(&mesh.vdata, CD_ORIGINDEX, CD_SET_DEFAULT, nullptr, mesh.totvert);
}
if (use_orig_index_edges) {
- CustomData_add_layer(&mesh.edata, CD_ORIGINDEX, CD_DEFAULT, nullptr, mesh.totedge);
+ CustomData_add_layer(&mesh.edata, CD_ORIGINDEX, CD_SET_DEFAULT, nullptr, mesh.totedge);
}
if (use_orig_index_polys) {
- CustomData_add_layer(&mesh.pdata, CD_ORIGINDEX, CD_DEFAULT, nullptr, mesh.totpoly);
+ CustomData_add_layer(&mesh.pdata, CD_ORIGINDEX, CD_SET_DEFAULT, nullptr, mesh.totpoly);
}
}
}
@@ -1349,6 +1395,16 @@ static NodesModifierData *get_modifier_data(Main &bmain,
return reinterpret_cast<NodesModifierData *>(md);
}
+static GeoTreeLog *get_root_tree_log(const NodesModifierData &nmd)
+{
+ if (nmd.runtime_eval_log == nullptr) {
+ return nullptr;
+ }
+ GeoModifierLog &modifier_log = *static_cast<GeoModifierLog *>(nmd.runtime_eval_log);
+ blender::bke::ModifierComputeContext compute_context{nullptr, nmd.modifier.name};
+ return &modifier_log.get_tree_log(compute_context.hash());
+}
+
static void attribute_search_update_fn(
const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first)
{
@@ -1357,27 +1413,52 @@ static void attribute_search_update_fn(
if (nmd == nullptr) {
return;
}
- const geo_log::ModifierLog *modifier_log = static_cast<const geo_log::ModifierLog *>(
- nmd->runtime_eval_log);
- if (modifier_log == nullptr) {
+ if (nmd->node_group == nullptr) {
return;
}
- const geo_log::GeometryValueLog *geometry_log = data.is_output ?
- modifier_log->output_geometry_log() :
- modifier_log->input_geometry_log();
- if (geometry_log == nullptr) {
+ GeoTreeLog *tree_log = get_root_tree_log(*nmd);
+ if (tree_log == nullptr) {
return;
}
+ tree_log->ensure_existing_attributes();
+ nmd->node_group->ensure_topology_cache();
- Span<GeometryAttributeInfo> infos = geometry_log->attributes();
-
- /* The shared attribute search code expects a span of pointers, so convert to that. */
- Array<const GeometryAttributeInfo *> info_ptrs(infos.size());
- for (const int i : infos.index_range()) {
- info_ptrs[i] = &infos[i];
+ Vector<const bNodeSocket *> sockets_to_check;
+ if (data.is_output) {
+ for (const bNode *node : nmd->node_group->nodes_by_type("NodeGroupOutput")) {
+ for (const bNodeSocket *socket : node->input_sockets()) {
+ if (socket->type == SOCK_GEOMETRY) {
+ sockets_to_check.append(socket);
+ }
+ }
+ }
+ }
+ else {
+ for (const bNode *node : nmd->node_group->nodes_by_type("NodeGroupInput")) {
+ for (const bNodeSocket *socket : node->output_sockets()) {
+ if (socket->type == SOCK_GEOMETRY) {
+ sockets_to_check.append(socket);
+ }
+ }
+ }
+ }
+ Set<StringRef> names;
+ Vector<const GeometryAttributeInfo *> attributes;
+ for (const bNodeSocket *socket : sockets_to_check) {
+ const ValueLog *value_log = tree_log->find_socket_value_log(*socket);
+ if (value_log == nullptr) {
+ continue;
+ }
+ if (const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(value_log)) {
+ for (const GeometryAttributeInfo &attribute : geo_log->attributes) {
+ if (names.add(attribute.name)) {
+ attributes.append(&attribute);
+ }
+ }
+ }
}
blender::ui::attribute_search_add_items(
- str, data.is_output, info_ptrs.as_span(), items, is_first);
+ str, data.is_output, attributes.as_span(), items, is_first);
}
static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
@@ -1408,8 +1489,7 @@ static void add_attribute_search_button(const bContext &C,
const bNodeSocket &socket,
const bool is_output)
{
- const geo_log::ModifierLog *log = static_cast<geo_log::ModifierLog *>(nmd.runtime_eval_log);
- if (log == nullptr) {
+ if (nmd.runtime_eval_log == nullptr) {
uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, "", ICON_NONE);
return;
}
@@ -1634,15 +1714,14 @@ static void panel_draw(const bContext *C, Panel *panel)
}
/* Draw node warnings. */
- if (nmd->runtime_eval_log != nullptr) {
- const geo_log::ModifierLog &log = *static_cast<geo_log::ModifierLog *>(nmd->runtime_eval_log);
- log.foreach_node_log([&](const geo_log::NodeLog &node_log) {
- for (const geo_log::NodeWarning &warning : node_log.warnings()) {
- if (warning.type != geo_log::NodeWarningType::Info) {
- uiItemL(layout, warning.message.c_str(), ICON_ERROR);
- }
+ GeoTreeLog *tree_log = get_root_tree_log(*nmd);
+ if (tree_log != nullptr) {
+ tree_log->ensure_node_warnings();
+ for (const NodeWarning &warning : tree_log->all_warnings) {
+ if (warning.type != NodeWarningType::Info) {
+ uiItemL(layout, warning.message.c_str(), ICON_ERROR);
}
- });
+ }
}
modifier_panel_end(layout, ptr);
@@ -1679,17 +1758,14 @@ static void internal_dependencies_panel_draw(const bContext *UNUSED(C), Panel *p
PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr);
NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr->data);
- if (nmd->runtime_eval_log == nullptr) {
+ GeoTreeLog *tree_log = get_root_tree_log(*nmd);
+ if (tree_log == nullptr) {
return;
}
- const geo_log::ModifierLog &log = *static_cast<geo_log::ModifierLog *>(nmd->runtime_eval_log);
- Map<std::string, eNamedAttrUsage> usage_by_attribute;
- log.foreach_node_log([&](const geo_log::NodeLog &node_log) {
- for (const geo_log::UsedNamedAttribute &used_attribute : node_log.used_named_attributes()) {
- usage_by_attribute.lookup_or_add_as(used_attribute.name,
- used_attribute.usage) |= used_attribute.usage;
- }
- });
+
+ tree_log->ensure_used_named_attributes();
+ const Map<StringRefNull, NamedAttributeUsage> &usage_by_attribute =
+ tree_log->used_named_attributes;
if (usage_by_attribute.is_empty()) {
uiItemL(layout, IFACE_("No named attributes used"), ICON_INFO);
@@ -1698,7 +1774,7 @@ static void internal_dependencies_panel_draw(const bContext *UNUSED(C), Panel *p
struct NameWithUsage {
StringRefNull name;
- eNamedAttrUsage usage;
+ NamedAttributeUsage usage;
};
Vector<NameWithUsage> sorted_used_attribute;
@@ -1713,20 +1789,20 @@ static void internal_dependencies_panel_draw(const bContext *UNUSED(C), Panel *p
for (const NameWithUsage &attribute : sorted_used_attribute) {
const StringRefNull attribute_name = attribute.name;
- const eNamedAttrUsage usage = attribute.usage;
+ const NamedAttributeUsage usage = attribute.usage;
/* #uiLayoutRowWithHeading doesn't seem to work in this case. */
uiLayout *split = uiLayoutSplit(layout, 0.4f, false);
std::stringstream ss;
Vector<std::string> usages;
- if ((usage & eNamedAttrUsage::Read) != eNamedAttrUsage::None) {
+ if ((usage & NamedAttributeUsage::Read) != NamedAttributeUsage::None) {
usages.append(TIP_("Read"));
}
- if ((usage & eNamedAttrUsage::Write) != eNamedAttrUsage::None) {
+ if ((usage & NamedAttributeUsage::Write) != NamedAttributeUsage::None) {
usages.append(TIP_("Write"));
}
- if ((usage & eNamedAttrUsage::Remove) != eNamedAttrUsage::None) {
+ if ((usage & NamedAttributeUsage::Remove) != NamedAttributeUsage::None) {
usages.append(TIP_("Remove"));
}
for (const int i : usages.index_range()) {
@@ -1814,9 +1890,7 @@ static void freeData(ModifierData *md)
clear_runtime_data(nmd);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks)
{
/* We don't know what the node tree will need. If there are vertex groups, it is likely that the
* node tree wants to access them. */
@@ -1825,7 +1899,7 @@ static void requiredDataMask(Object *UNUSED(ob),
}
ModifierTypeInfo modifierType_Nodes = {
- /* name */ "GeometryNodes",
+ /* name */ N_("GeometryNodes"),
/* structName */ "NodesModifierData",
/* structSize */ sizeof(NodesModifierData),
/* srna */ &RNA_NodesModifier,
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
deleted file mode 100644
index e43d2b4ad85..00000000000
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ /dev/null
@@ -1,1928 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "MOD_nodes_evaluator.hh"
-
-#include "BKE_type_conversions.hh"
-
-#include "NOD_geometry_exec.hh"
-#include "NOD_socket_declarations.hh"
-
-#include "DEG_depsgraph_query.h"
-
-#include "FN_field.hh"
-#include "FN_field_cpp_type.hh"
-#include "FN_multi_function.hh"
-
-#include "BLT_translation.h"
-
-#include "BLI_enumerable_thread_specific.hh"
-#include "BLI_generic_value_map.hh"
-#include "BLI_stack.hh"
-#include "BLI_task.h"
-#include "BLI_task.hh"
-#include "BLI_vector_set.hh"
-
-#include <chrono>
-
-namespace blender::modifiers::geometry_nodes {
-
-using fn::Field;
-using fn::GField;
-using fn::ValueOrField;
-using fn::ValueOrFieldCPPType;
-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 MultiInputValue {
- /**
- * Ordered sockets connected to this multi-input.
- */
- Vector<DSocket> origins;
- /**
- * A value for every origin socket. The order is determined by #origins.
- * Note, the same origin can occur multiple times. However, it is guaranteed that values coming
- * from the same origin have the same value (the pointer is different, but they point to values
- * that would compare equal).
- */
- Vector<void *> values;
- /**
- * Number of non-null values.
- */
- int provided_value_count = 0;
-
- bool all_values_available() const
- {
- return this->missing_values() == 0;
- }
-
- int missing_values() const
- {
- return this->values.size() - this->provided_value_count;
- }
-
- void add_value(const DSocket origin, void *value)
- {
- const int index = this->find_available_index(origin);
- this->values[index] = value;
- this->provided_value_count++;
- }
-
- private:
- int find_available_index(DSocket origin) const
- {
- for (const int i : origins.index_range()) {
- if (values[i] != nullptr) {
- continue;
- }
- if (origins[i] != origin) {
- continue;
- }
- return i;
- }
- BLI_assert_unreachable();
- return -1;
- }
-};
-
-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;
-
- /**
- * True when this input has to be computed for logging/debugging purposes, regardless of whether
- * it is needed for some output.
- */
- bool force_compute = 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;
-
- /**
- * Most nodes have inputs that are always required. Those have special handling to avoid an extra
- * call to the node execution function.
- */
- bool non_lazy_inputs_handled = 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 wraps a node whose state is locked. 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 {
- 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(const DNode node, NodeState &node_state) : node(node), node_state(node_state)
- {
- }
-};
-
-static const CPPType *get_socket_cpp_type(const SocketRef &socket)
-{
- const bNodeSocketType *typeinfo = socket.typeinfo();
- if (typeinfo->geometry_nodes_cpp_type == nullptr) {
- return nullptr;
- }
- const CPPType *type = typeinfo->geometry_nodes_cpp_type;
- if (type == nullptr) {
- return nullptr;
- }
- /* The evaluator only supports types that have special member functions. */
- if (!type->has_special_member_functions()) {
- return nullptr;
- }
- return type;
-}
-
-static const CPPType *get_socket_cpp_type(const DSocket socket)
-{
- return get_socket_cpp_type(*socket.socket_ref());
-}
-
-/**
- * \note This is not supposed to be a long term solution. Eventually we want that nodes can
- * specify more complex defaults (other than just single values) in their socket declarations.
- */
-static bool get_implicit_socket_input(const SocketRef &socket, void *r_value)
-{
- const NodeRef &node = socket.node();
- const nodes::NodeDeclaration *node_declaration = node.declaration();
- if (node_declaration == nullptr) {
- return false;
- }
- const nodes::SocketDeclaration &socket_declaration = *node_declaration->inputs()[socket.index()];
- if (socket_declaration.input_field_type() == nodes::InputSocketFieldType::Implicit) {
- const bNode &bnode = *socket.bnode();
- if (socket.typeinfo()->type == SOCK_VECTOR) {
- if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) {
- StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode ==
- GEO_NODE_CURVE_HANDLE_LEFT ?
- "handle_left" :
- "handle_right";
- new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side));
- return true;
- }
- if (bnode.type == GEO_NODE_EXTRUDE_MESH) {
- new (r_value)
- ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>()));
- return true;
- }
- new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position"));
- return true;
- }
- if (socket.typeinfo()->type == SOCK_INT) {
- if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) {
- new (r_value)
- ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>()));
- return true;
- }
- new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>()));
- return true;
- }
- }
- return false;
-}
-
-static void get_socket_value(const SocketRef &socket, void *r_value)
-{
- if (get_implicit_socket_input(socket, r_value)) {
- return;
- }
-
- const bNodeSocketType *typeinfo = socket.typeinfo();
- typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value);
-}
-
-static bool node_supports_laziness(const DNode node)
-{
- return node->typeinfo()->geometry_node_execute_supports_laziness;
-}
-
-struct NodeTaskRunState {
- /** The node that should be run on the same thread after the current node finished. */
- DNode next_node_to_run;
-};
-
-/** Implements the callbacks that might be called when a node is executed. */
-class NodeParamsProvider : public nodes::GeoNodeExecParamsProvider {
- private:
- GeometryNodesEvaluator &evaluator_;
- NodeState &node_state_;
- NodeTaskRunState *run_state_;
-
- public:
- NodeParamsProvider(GeometryNodesEvaluator &evaluator,
- DNode dnode,
- NodeState &node_state,
- NodeTaskRunState *run_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;
-
- void set_default_remaining_outputs() 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.
- */
- threading::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::bke::DataTypeConversions &conversions_;
-
- friend NodeParamsProvider;
-
- public:
- GeometryNodesEvaluator(GeometryNodesEvaluationParams &params)
- : outer_allocator_(params.allocator),
- params_(params),
- conversions_(blender::bke::get_implicit_type_conversions())
- {
- }
-
- void execute()
- {
- task_pool_ = BLI_task_pool_create(this, TASK_PRIORITY_HIGH);
-
- 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());
- }
- for (const DSocket &socket : params_.force_compute_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. */
- threading::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);
- }
- });
-
- /* Mark input sockets that have to be computed. */
- for (const DSocket &socket : params_.force_compute_sockets) {
- NodeState &node_state = *node_states_.lookup_key_as(socket.node()).state;
- if (socket->is_input()) {
- node_state.inputs[socket->index()].force_compute = true;
- }
- }
- }
-
- 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();
- MultiInputValue &multi_value = *input_state.value.multi;
- /* Count how many values should be added until the socket is complete. */
- socket.foreach_origin_socket([&](DSocket origin) { multi_value.origins.append(origin); });
- /* If no links are connected, we do read the value from socket itself. */
- if (multi_value.origins.is_empty()) {
- multi_value.origins.append(socket);
- }
- multi_value.values.resize(multi_value.origins.size(), nullptr);
- }
- 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 DOutputSocket::TargetSocketPathInfo &UNUSED(path_info)) {
- 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. It might become required again in
- * `schedule_initial_nodes`. */
- output_state.output_usage = ValueUsage::Unused;
- }
- }
- }
-
- void destruct_node_states()
- {
- threading::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 (void *value : multi_value.values) {
- if (value != nullptr) {
- input_state.type->destruct(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;
-
- const DNode node = socket.node();
- if (!node_states_.contains_as(node)) {
- /* The socket is not connected to any output. */
- this->log_socket_value({socket}, value);
- value.destruct();
- continue;
- }
- this->forward_output(socket, value, nullptr);
- }
- }
-
- void schedule_initial_nodes()
- {
- for (const DInputSocket &socket : params_.output_sockets) {
- const DNode node = socket.node();
- NodeState &node_state = this->get_node_state(node);
- this->with_locked_node(node, node_state, nullptr, [&](LockedNode &locked_node) {
- /* Setting an input as required will schedule any linked node. */
- this->set_input_required(locked_node, socket);
- });
- }
- for (const DSocket socket : params_.force_compute_sockets) {
- const DNode node = socket.node();
- NodeState &node_state = this->get_node_state(node);
- this->with_locked_node(node, node_state, nullptr, [&](LockedNode &locked_node) {
- if (socket->is_input()) {
- this->set_input_required(locked_node, DInputSocket(socket));
- }
- else {
- OutputState &output_state = node_state.outputs[socket->index()];
- output_state.output_usage = ValueUsage::Required;
- this->schedule_node(locked_node);
- }
- });
- }
- }
-
- 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 *root_node_with_state = (const NodeWithState *)task_data;
-
- /* First, the node provided by the task pool is executed. During the execution other nodes
- * might be scheduled. One of those nodes is not added to the task pool but is executed in the
- * loop below directly. This has two main benefits:
- * - Fewer round trips through the task pool which add threading overhead.
- * - Helps with cpu cache efficiency, because a thread is more likely to process data that it
- * has processed shortly before.
- */
- DNode next_node_to_run = root_node_with_state->node;
- while (next_node_to_run) {
- NodeTaskRunState run_state;
- evaluator.node_task_run(next_node_to_run, &run_state);
- next_node_to_run = run_state.next_node_to_run;
- }
- }
-
- void node_task_run(const DNode node, NodeTaskRunState *run_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;
- }
-
- NodeState &node_state = *node_states_.lookup_key_as(node).state;
-
- const bool do_execute_node = this->node_task_preprocessing(node, node_state, run_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, run_state);
- }
-
- this->node_task_postprocessing(node, node_state, do_execute_node, run_state);
- }
-
- bool node_task_preprocessing(const DNode node,
- NodeState &node_state,
- NodeTaskRunState *run_state)
- {
- bool do_execute_node = false;
- this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) {
- 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;
- }
- /* Prepare outputs and check if actually any new outputs have to be computed. */
- if (!this->prepare_node_outputs_for_execution(locked_node)) {
- return;
- }
- /* Initialize inputs 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_inputs_handled) {
- this->require_non_lazy_inputs(locked_node);
- node_state.non_lazy_inputs_handled = true;
- }
- /* Prepare inputs and check if all required inputs are provided. */
- if (!this->prepare_node_inputs_for_execution(locked_node)) {
- return;
- }
- do_execute_node = true;
- });
- return do_execute_node;
- }
-
- /* A node is finished when it has computed all outputs that may be used have been computed and
- * when no input is still forced to be computed. */
- 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. */
- for (OutputState &output_state : locked_node.node_state.outputs) {
- if (output_state.has_been_computed) {
- continue;
- }
- if (output_state.output_usage != ValueUsage::Unused) {
- return false;
- }
- }
-
- /* Check if there is an input that still has to be computed. */
- for (InputState &input_state : locked_node.node_state.inputs) {
- if (input_state.force_compute) {
- if (!input_state.was_ready_for_execution) {
- return false;
- }
- }
- }
-
- /* 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 true;
- }
-
- 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 require_non_lazy_inputs(LockedNode &locked_node)
- {
- this->foreach_non_lazy_input(locked_node, [&](const DInputSocket socket) {
- this->set_input_required(locked_node, socket);
- });
- }
-
- void foreach_non_lazy_input(LockedNode &locked_node, FunctionRef<void(DInputSocket socket)> fn)
- {
- if (node_supports_laziness(locked_node.node)) {
- /* In the future only some of the inputs may support laziness. */
- return;
- }
- /* Nodes that don't support laziness require all inputs. */
- 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;
- }
- fn(locked_node.node.input(i));
- }
- }
-
- /**
- * 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.all_values_available()) {
- input_state.was_ready_for_execution = true;
- }
- 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;
- }
- 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, NodeTaskRunState *run_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, run_state);
- return;
- }
-
- /* Use the multi-function implementation if it exists. */
- const nodes::NodeMultiFunctions::Item &fn_item = params_.mf_by_node->try_get(node);
- if (fn_item.fn != nullptr) {
- this->execute_multi_function_node(node, fn_item, node_state, run_state);
- return;
- }
-
- this->execute_unknown_node(node, node_state, run_state);
- }
-
- void execute_geometry_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_state)
- {
- using Clock = std::chrono::steady_clock;
- const bNode &bnode = *node->bnode();
-
- NodeParamsProvider params_provider{*this, node, node_state, run_state};
- GeoNodeExecParams params{params_provider};
- Clock::time_point begin = Clock::now();
- bnode.typeinfo->geometry_node_execute(params);
- Clock::time_point end = Clock::now();
- const std::chrono::microseconds duration =
- std::chrono::duration_cast<std::chrono::microseconds>(end - begin);
- if (params_.geo_logger != nullptr) {
- params_.geo_logger->local().log_execution_time(node, duration);
- }
- }
-
- void execute_multi_function_node(const DNode node,
- const nodes::NodeMultiFunctions::Item &fn_item,
- NodeState &node_state,
- NodeTaskRunState *run_state)
- {
- LinearAllocator<> &allocator = local_allocators_.local();
-
- bool any_input_is_field = false;
- Vector<const void *, 16> input_values;
- Vector<const ValueOrFieldCPPType *, 16> input_types;
- 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);
- const ValueOrFieldCPPType &field_cpp_type = static_cast<const ValueOrFieldCPPType &>(
- *input_state.type);
- input_values.append(single_value.value);
- input_types.append(&field_cpp_type);
- if (field_cpp_type.is_field(single_value.value)) {
- any_input_is_field = true;
- }
- }
-
- if (any_input_is_field) {
- this->execute_multi_function_node__field(
- node, fn_item, node_state, allocator, input_values, input_types, run_state);
- }
- else {
- this->execute_multi_function_node__value(
- node, *fn_item.fn, node_state, allocator, input_values, input_types, run_state);
- }
- }
-
- void execute_multi_function_node__field(const DNode node,
- const nodes::NodeMultiFunctions::Item &fn_item,
- NodeState &node_state,
- LinearAllocator<> &allocator,
- Span<const void *> input_values,
- Span<const ValueOrFieldCPPType *> input_types,
- NodeTaskRunState *run_state)
- {
- Vector<GField> input_fields;
- for (const int i : input_values.index_range()) {
- const void *input_value_or_field = input_values[i];
- const ValueOrFieldCPPType &field_cpp_type = *input_types[i];
- input_fields.append(field_cpp_type.as_field(input_value_or_field));
- }
-
- std::shared_ptr<fn::FieldOperation> operation;
- if (fn_item.owned_fn) {
- operation = std::make_shared<fn::FieldOperation>(fn_item.owned_fn, std::move(input_fields));
- }
- else {
- operation = std::make_shared<fn::FieldOperation>(*fn_item.fn, std::move(input_fields));
- }
-
- 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};
- const ValueOrFieldCPPType *cpp_type = static_cast<const ValueOrFieldCPPType *>(
- get_socket_cpp_type(socket_ref));
- GField new_field{operation, output_index};
- void *buffer = allocator.allocate(cpp_type->size(), cpp_type->alignment());
- cpp_type->construct_from_field(buffer, std::move(new_field));
- this->forward_output(socket, {cpp_type, buffer}, run_state);
- output_state.has_been_computed = true;
- output_index++;
- }
- }
-
- void execute_multi_function_node__value(const DNode node,
- const MultiFunction &fn,
- NodeState &node_state,
- LinearAllocator<> &allocator,
- Span<const void *> input_values,
- Span<const ValueOrFieldCPPType *> input_types,
- NodeTaskRunState *run_state)
- {
- MFParamsBuilder params{fn, 1};
- for (const int i : input_values.index_range()) {
- const void *input_value_or_field = input_values[i];
- const ValueOrFieldCPPType &field_cpp_type = *input_types[i];
- const CPPType &base_type = field_cpp_type.base_type();
- const void *input_value = field_cpp_type.get_value_ptr(input_value_or_field);
- params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, input_value));
- }
-
- Vector<GMutablePointer, 16> output_buffers;
- for (const int i : node->outputs().index_range()) {
- const DOutputSocket socket = node.output(i);
- if (!socket->is_available()) {
- output_buffers.append({});
- continue;
- }
- const ValueOrFieldCPPType *value_or_field_type = static_cast<const ValueOrFieldCPPType *>(
- get_socket_cpp_type(socket));
- const CPPType &base_type = value_or_field_type->base_type();
- void *value_or_field_buffer = allocator.allocate(value_or_field_type->size(),
- value_or_field_type->alignment());
- value_or_field_type->default_construct(value_or_field_buffer);
- void *value_buffer = value_or_field_type->get_value_ptr(value_or_field_buffer);
- base_type.destruct(value_buffer);
- params.add_uninitialized_single_output(GMutableSpan{base_type, value_buffer, 1});
- output_buffers.append({value_or_field_type, value_or_field_buffer});
- }
-
- MFContextBuilder context;
- fn.call(IndexRange(1), params, context);
-
- for (const int i : output_buffers.index_range()) {
- GMutablePointer buffer = output_buffers[i];
- if (buffer.get() == nullptr) {
- continue;
- }
- const DOutputSocket socket = node.output(i);
- this->forward_output(socket, buffer, run_state);
-
- OutputState &output_state = node_state.outputs[i];
- output_state.has_been_computed = true;
- }
- }
-
- void execute_unknown_node(const DNode node, NodeState &node_state, NodeTaskRunState *run_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());
- this->construct_default_value(*type, buffer);
- this->forward_output({node.context(), socket}, {*type, buffer}, run_state);
- }
- }
-
- void node_task_postprocessing(const DNode node,
- NodeState &node_state,
- bool was_executed,
- NodeTaskRunState *run_state)
- {
- this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) {
- 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);
- }
- if (was_executed) {
- 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_construct(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.
- * \return True when the node will be triggered by another node again when the value is computed.
- */
- bool 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.was_ready_for_execution) {
- return false;
- }
-
- if (input_state.usage == ValueUsage::Required) {
- /* If the input was not ready for execution but is required, the node will be triggered again
- * once the input has been computed. */
- return true;
- }
- input_state.usage = ValueUsage::Required;
-
- /* 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.missing_values();
- }
- else {
- SingleInputValue &single_value = *input_state.value.single;
- if (single_value.value == nullptr) {
- missing_values = 1;
- }
- }
- if (missing_values == 0) {
- return false;
- }
- /* 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;
- return false;
- }
- bool requested_from_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;
- }
- else {
- /* The value has not been computed yet, so when it will be forwarded by another node, this
- * node will be triggered. */
- requested_from_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 (requested_from_other_node) {
- return true;
- }
- return false;
- }
-
- 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, its 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, NodeTaskRunState *run_state)
- {
- const DNode node = socket.node();
- NodeState &node_state = this->get_node_state(node);
- OutputState &output_state = node_state.outputs[socket->index()];
-
- this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) {
- 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, NodeTaskRunState *run_state)
- {
- const DNode node = socket.node();
- NodeState &node_state = this->get_node_state(node);
- OutputState &output_state = node_state.outputs[socket->index()];
-
- this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) {
- output_state.potential_users -= 1;
- if (output_state.potential_users == 0) {
- /* The socket might be required even though the output is not used by other sockets. That
- * can happen when the socket is forced to be computed. */
- if (output_state.output_usage != ValueUsage::Required) {
- /* 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.
- * Takes ownership of the value and destructs if it is unused.
- */
- void forward_output(const DOutputSocket from_socket,
- GMutablePointer value_to_forward,
- NodeTaskRunState *run_state)
- {
- BLI_assert(value_to_forward.get() != nullptr);
-
- LinearAllocator<> &allocator = local_allocators_.local();
-
- Vector<DSocket> log_original_value_sockets;
- Vector<DInputSocket> forward_original_value_sockets;
- log_original_value_sockets.append(from_socket);
-
- from_socket.foreach_target_socket(
- [&](const DInputSocket to_socket, const DOutputSocket::TargetSocketPathInfo &path_info) {
- if (!this->should_forward_to_socket(to_socket)) {
- return;
- }
- BLI_assert(to_socket == path_info.sockets.last());
- GMutablePointer current_value = value_to_forward;
- for (const DSocket &next_socket : path_info.sockets) {
- const DNode next_node = next_socket.node();
- const bool is_last_socket = to_socket == next_socket;
- const bool do_conversion_if_necessary = is_last_socket ||
- next_node->is_group_output_node() ||
- (next_node->is_group_node() &&
- !next_node->is_muted());
- if (do_conversion_if_necessary) {
- const CPPType &next_type = *get_socket_cpp_type(next_socket);
- if (*current_value.type() != next_type) {
- void *buffer = allocator.allocate(next_type.size(), next_type.alignment());
- this->convert_value(*current_value.type(), next_type, current_value.get(), buffer);
- if (current_value.get() != value_to_forward.get()) {
- current_value.destruct();
- }
- current_value = {next_type, buffer};
- }
- }
- if (current_value.get() == value_to_forward.get()) {
- /* Log the original value at the current socket. */
- log_original_value_sockets.append(next_socket);
- }
- else {
- /* Multi-input sockets are logged when all values are available. */
- if (!(next_socket->is_input() && next_socket->as_input().is_multi_input_socket())) {
- /* Log the converted value at the socket. */
- this->log_socket_value({next_socket}, current_value);
- }
- }
- }
- if (current_value.get() == value_to_forward.get()) {
- /* The value has not been converted, so forward the original value. */
- forward_original_value_sockets.append(to_socket);
- }
- else {
- /* The value has been converted. */
- this->add_value_to_input_socket(to_socket, from_socket, current_value, run_state);
- }
- });
- this->log_socket_value(log_original_value_sockets, value_to_forward);
- this->forward_to_sockets_with_same_type(
- allocator, forward_original_value_sockets, value_to_forward, from_socket, run_state);
- }
-
- 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_sockets_with_same_type(LinearAllocator<> &allocator,
- Span<DInputSocket> to_sockets,
- GMutablePointer value_to_forward,
- const DOutputSocket from_socket,
- NodeTaskRunState *run_state)
- {
- 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, run_state);
- }
- 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_construct(value_to_forward.get(), buffer);
- this->add_value_to_input_socket(to_socket, from_socket, {type, buffer}, run_state);
- }
- /* 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, run_state);
- }
- }
-
- void add_value_to_input_socket(const DInputSocket socket,
- const DOutputSocket origin,
- GMutablePointer value,
- NodeTaskRunState *run_state)
- {
- 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()];
-
- this->with_locked_node(node, node_state, run_state, [&](LockedNode &locked_node) {
- if (socket->is_multi_input_socket()) {
- /* Add a new value to the multi-input. */
- MultiInputValue &multi_value = *input_state.value.multi;
- multi_value.add_value(origin, value.get());
-
- if (multi_value.all_values_available()) {
- this->log_socket_value({socket}, input_state, multi_value.values);
- }
- }
- 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);
- }
- }
- });
- }
-
- /**
- * Loads the value of a socket that is not computed by another node. Note that the socket may
- * still be linked to e.g. a Group Input node, but the socket on the outside is not connected to
- * anything.
- *
- * \param input_socket: The socket of the node that wants to use the value.
- * \param origin_socket: The socket that we want to load the value from.
- */
- 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.add_value(origin_socket, value.get());
- if (multi_value.all_values_available()) {
- this->log_socket_value({input_socket}, input_state, multi_value.values);
- }
- }
- else {
- SingleInputValue &single_value = *input_state.value.single;
- single_value.value = value.get();
- Vector<DSocket> sockets_to_log_to = {input_socket};
- if (origin_socket != input_socket) {
- /* This might log the socket value for the #origin_socket more than once, but this is
- * handled by the logging system gracefully. */
- sockets_to_log_to.append(origin_socket);
- }
- /* TODO: Log to the intermediate sockets between the group input and where the value is
- * actually used as well. */
- this->log_socket_value(sockets_to_log_to, value);
- }
- }
-
- 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 (void *&value : multi_value.values) {
- if (value != nullptr) {
- input_state.type->destruct(value);
- value = nullptr;
- }
- }
- multi_value.provided_value_count = 0;
- }
- 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();
-
- const CPPType &type = *get_socket_cpp_type(socket);
- void *buffer = allocator.allocate(type.size(), type.alignment());
- get_socket_value(*socket.socket_ref(), buffer);
-
- if (type == required_type) {
- return {type, buffer};
- }
- void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment());
- this->convert_value(type, required_type, buffer, converted_buffer);
- type.destruct(buffer);
- return {required_type, converted_buffer};
- }
-
- void convert_value(const CPPType &from_type,
- const CPPType &to_type,
- const void *from_value,
- void *to_value)
- {
- if (from_type == to_type) {
- from_type.copy_construct(from_value, to_value);
- return;
- }
- const ValueOrFieldCPPType *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(
- &from_type);
- const ValueOrFieldCPPType *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type);
-
- if (from_field_type != nullptr && to_field_type != nullptr) {
- const CPPType &from_base_type = from_field_type->base_type();
- const CPPType &to_base_type = to_field_type->base_type();
- if (conversions_.is_convertible(from_base_type, to_base_type)) {
- if (from_field_type->is_field(from_value)) {
- const GField &from_field = *from_field_type->get_field_ptr(from_value);
- to_field_type->construct_from_field(to_value,
- conversions_.try_convert(from_field, to_base_type));
- }
- else {
- to_field_type->default_construct(to_value);
- const void *from_value_ptr = from_field_type->get_value_ptr(from_value);
- void *to_value_ptr = to_field_type->get_value_ptr(to_value);
- conversions_.get_conversion_functions(from_base_type, to_base_type)
- ->convert_single_to_initialized(from_value_ptr, to_value_ptr);
- }
- return;
- }
- }
- if (conversions_.is_convertible(from_type, to_type)) {
- /* Do the conversion if possible. */
- conversions_.convert_to_uninitialized(from_type, to_type, from_value, to_value);
- }
- else {
- /* Cannot convert, use default value instead. */
- this->construct_default_value(to_type, to_value);
- }
- }
-
- void construct_default_value(const CPPType &type, void *r_value)
- {
- type.value_initialize(r_value);
- }
-
- NodeState &get_node_state(const DNode node)
- {
- return *node_states_.lookup_key_as(node).state;
- }
-
- void log_socket_value(DSocket socket, InputState &input_state, Span<void *> values)
- {
- if (params_.geo_logger == nullptr) {
- return;
- }
-
- Vector<GPointer, 16> value_pointers;
- value_pointers.reserve(values.size());
- const CPPType &type = *input_state.type;
- for (const void *value : values) {
- value_pointers.append({type, value});
- }
- params_.geo_logger->local().log_multi_value_socket(socket, value_pointers);
- }
-
- void log_socket_value(Span<DSocket> sockets, GPointer value)
- {
- if (params_.geo_logger == nullptr) {
- return;
- }
- params_.geo_logger->local().log_value_for_sockets(sockets, value);
- }
-
- void log_debug_message(DNode node, std::string message)
- {
- if (params_.geo_logger == nullptr) {
- return;
- }
- params_.geo_logger->local().log_debug_message(node, std::move(message));
- }
-
- /* In most cases when `NodeState` is accessed, the node has to be locked first to avoid race
- * conditions. */
- template<typename Function>
- void with_locked_node(const DNode node,
- NodeState &node_state,
- NodeTaskRunState *run_state,
- const Function &function)
- {
- LockedNode locked_node{node, node_state};
-
- node_state.mutex.lock();
- /* Isolate this thread because we don't want it to start executing another node. This other
- * node might want to lock the same mutex leading to a deadlock. */
- threading::isolate_task([&] { function(locked_node); });
- node_state.mutex.unlock();
-
- /* Then send notifications to the other nodes after the node state is unlocked. This avoids
- * locking two nodes at the same time on this thread and helps to prevent deadlocks. */
- for (const DOutputSocket &socket : locked_node.delayed_required_outputs) {
- this->send_output_required_notification(socket, run_state);
- }
- for (const DOutputSocket &socket : locked_node.delayed_unused_outputs) {
- this->send_output_unused_notification(socket, run_state);
- }
- for (const DNode &node_to_schedule : locked_node.delayed_scheduled_nodes) {
- if (run_state != nullptr && !run_state->next_node_to_run) {
- /* Execute the node on the same thread after the current node finished. */
- /* Currently, this assumes that it is always best to run the first node that is scheduled
- * on the same thread. That is usually correct, because the geometry socket which carries
- * the most data usually comes first in nodes. */
- run_state->next_node_to_run = node_to_schedule;
- }
- else {
- /* Push the node to the task pool so that another thread can start working on it. */
- this->add_node_to_task_pool(node_to_schedule);
- }
- }
- }
-};
-
-NodeParamsProvider::NodeParamsProvider(GeometryNodesEvaluator &evaluator,
- DNode dnode,
- NodeState &node_state,
- NodeTaskRunState *run_state)
- : evaluator_(evaluator), node_state_(node_state), run_state_(run_state)
-{
- this->dnode = dnode;
- this->self_object = evaluator.params_.self_object;
- this->modifier = &evaluator.params_.modifier_->modifier;
- this->depsgraph = evaluator.params_.depsgraph;
- this->logger = evaluator.params_.geo_logger;
-}
-
-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.all_values_available();
- }
- 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;
- for (void *&value : multi_value.values) {
- BLI_assert(value != nullptr);
- ret_values.append({*input_state.type, value});
- value = nullptr;
- }
- 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);
-
- OutputState &output_state = node_state_.outputs[socket->index()];
- BLI_assert(!output_state.has_been_computed);
- evaluator_.forward_output(socket, value, run_state_);
- 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;
- }
- evaluator_.with_locked_node(this->dnode, node_state_, run_state_, [&](LockedNode &locked_node) {
- if (!evaluator_.set_input_required(locked_node, socket)) {
- /* Schedule the currently executed node again because the value is available now but was not
- * ready for the current execution. */
- evaluator_.schedule_node(locked_node);
- }
- });
- return true;
-}
-
-void NodeParamsProvider::set_input_unused(StringRef identifier)
-{
- const DInputSocket socket = this->dnode.input_by_identifier(identifier);
- BLI_assert(socket);
-
- evaluator_.with_locked_node(this->dnode, node_state_, run_state_, [&](LockedNode &locked_node) {
- 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 NodeParamsProvider::set_default_remaining_outputs()
-{
- LinearAllocator<> &allocator = evaluator_.local_allocators_.local();
-
- for (const int i : this->dnode->outputs().index_range()) {
- OutputState &output_state = node_state_.outputs[i];
- if (output_state.has_been_computed) {
- continue;
- }
- if (output_state.output_usage_for_execution == ValueUsage::Unused) {
- continue;
- }
-
- const DOutputSocket socket = this->dnode.output(i);
- const CPPType *type = get_socket_cpp_type(socket);
- BLI_assert(type != nullptr);
- void *buffer = allocator.allocate(type->size(), type->alignment());
- type->value_initialize(buffer);
- evaluator_.forward_output(socket, {type, buffer}, run_state_);
- output_state.has_been_computed = true;
- }
-}
-
-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
deleted file mode 100644
index cbcbcab5679..00000000000
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.hh
+++ /dev/null
@@ -1,44 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-#include "BLI_generic_pointer.hh"
-#include "BLI_map.hh"
-
-#include "NOD_derived_node_tree.hh"
-#include "NOD_geometry_nodes_eval_log.hh"
-#include "NOD_multi_function.hh"
-
-#include "DNA_modifier_types.h"
-
-#include "FN_multi_function.hh"
-
-namespace geo_log = blender::nodes::geometry_nodes_eval_log;
-
-namespace blender::modifiers::geometry_nodes {
-
-using namespace nodes::derived_node_tree_types;
-
-struct GeometryNodesEvaluationParams {
- blender::LinearAllocator<> allocator;
-
- Map<DOutputSocket, GMutablePointer> input_values;
- Vector<DInputSocket> output_sockets;
- /* These sockets will be computed but are not part of the output. Their value can be retrieved in
- * `log_socket_value_fn`. These sockets are not part of `output_sockets` because then the
- * evaluator would have to keep the socket values in memory until the end, which might not be
- * necessary in all cases. Sometimes `log_socket_value_fn` might just want to look at the value
- * and then it can be freed. */
- Vector<DSocket> force_compute_sockets;
- nodes::NodeMultiFunctions *mf_by_node;
- const NodesModifierData *modifier_;
- Depsgraph *depsgraph;
- Object *self_object;
- geo_log::GeoLogger *geo_logger;
-
- Vector<GMutablePointer> r_output_values;
-};
-
-void evaluate_geometry_nodes(GeometryNodesEvaluationParams &params);
-
-} // namespace blender::modifiers::geometry_nodes
diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c
index c215ac601a1..caada225e75 100644
--- a/source/blender/modifiers/intern/MOD_normal_edit.c
+++ b/source/blender/modifiers/intern/MOD_normal_edit.c
@@ -53,7 +53,7 @@ static void generate_vert_coordinates(Mesh *mesh,
INIT_MINMAX(min_co, max_co);
- MVert *mv = mesh->mvert;
+ const MVert *mv = BKE_mesh_verts(mesh);
for (int i = 0; i < mesh->totvert; i++, mv++) {
copy_v3_v3(r_cos[i], mv->co);
if (r_size != NULL && ob_center == NULL) {
@@ -117,13 +117,13 @@ static void generate_vert_coordinates(Mesh *mesh,
/* Note this modifies nos_new in-place. */
static void mix_normals(const float mix_factor,
- MDeformVert *dvert,
+ const MDeformVert *dvert,
const int defgrp_index,
const bool use_invert_vgroup,
const float mix_limit,
const short mix_mode,
const int verts_num,
- MLoop *mloop,
+ const MLoop *mloop,
float (*nos_old)[3],
float (*nos_new)[3],
const int loops_num)
@@ -175,11 +175,11 @@ static void mix_normals(const float mix_factor,
static bool polygons_check_flip(MLoop *mloop,
float (*nos)[3],
CustomData *ldata,
- MPoly *mpoly,
+ const MPoly *mpoly,
float (*polynors)[3],
const int polys_num)
{
- MPoly *mp;
+ const MPoly *mp;
MDisps *mdisp = CustomData_get_layer(ldata, CD_MDISPS);
int i;
bool flipped = false;
@@ -218,16 +218,16 @@ static void normalEditModifier_do_radial(NormalEditModifierData *enmd,
const short mix_mode,
const float mix_factor,
const float mix_limit,
- MDeformVert *dvert,
+ const MDeformVert *dvert,
const int defgrp_index,
const bool use_invert_vgroup,
- MVert *mvert,
+ const MVert *mvert,
const int verts_num,
MEdge *medge,
const int edges_num,
MLoop *mloop,
const int loops_num,
- MPoly *mpoly,
+ const MPoly *mpoly,
const int polys_num)
{
Object *ob_target = enmd->target;
@@ -279,7 +279,7 @@ static void normalEditModifier_do_radial(NormalEditModifierData *enmd,
const float m2 = (b * b) / (a * a);
const float n2 = (c * c) / (a * a);
- MLoop *ml;
+ const MLoop *ml;
float(*no)[3];
/* We reuse cos to now store the ellipsoid-normal of the verts! */
@@ -355,16 +355,16 @@ static void normalEditModifier_do_directional(NormalEditModifierData *enmd,
const short mix_mode,
const float mix_factor,
const float mix_limit,
- MDeformVert *dvert,
+ const MDeformVert *dvert,
const int defgrp_index,
const bool use_invert_vgroup,
- MVert *mvert,
+ const MVert *mvert,
const int verts_num,
MEdge *medge,
const int edges_num,
MLoop *mloop,
const int loops_num,
- MPoly *mpoly,
+ const MPoly *mpoly,
const int polys_num)
{
Object *ob_target = enmd->target;
@@ -399,7 +399,7 @@ static void normalEditModifier_do_directional(NormalEditModifierData *enmd,
generate_vert_coordinates(mesh, ob, ob_target, NULL, verts_num, cos, NULL);
BLI_bitmap *done_verts = BLI_BITMAP_NEW((size_t)verts_num, __func__);
- MLoop *ml;
+ const MLoop *ml;
float(*no)[3];
/* We reuse cos to now store the 'to target' normal of the verts! */
@@ -509,7 +509,7 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
}
Mesh *result;
- if (mesh->medge == ((Mesh *)ob->data)->medge) {
+ if (BKE_mesh_edges(mesh) == BKE_mesh_edges(((Mesh *)ob->data))) {
/* We need to duplicate data here, otherwise setting custom normals
* (which may also affect sharp edges) could
* modify original mesh, see T43671. */
@@ -523,13 +523,13 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
const int edges_num = result->totedge;
const int loops_num = result->totloop;
const int polys_num = result->totpoly;
- MVert *mvert = result->mvert;
- MEdge *medge = result->medge;
- MLoop *mloop = result->mloop;
- MPoly *mpoly = result->mpoly;
+ const MVert *verts = BKE_mesh_verts(result);
+ MEdge *edges = BKE_mesh_edges_for_write(result);
+ const MPoly *polys = BKE_mesh_polys(result);
+ MLoop *loops = BKE_mesh_loops_for_write(result);
int defgrp_index;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
float(*loopnors)[3] = NULL;
@@ -543,15 +543,15 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
clnors = CustomData_duplicate_referenced_layer(ldata, CD_CUSTOMLOOPNORMAL, loops_num);
loopnors = MEM_malloc_arrayN((size_t)loops_num, sizeof(*loopnors), __func__);
- BKE_mesh_normals_loop_split(mvert,
+ BKE_mesh_normals_loop_split(verts,
vert_normals,
verts_num,
- medge,
+ edges,
edges_num,
- mloop,
+ loops,
loopnors,
loops_num,
- mpoly,
+ polys,
poly_normals,
polys_num,
true,
@@ -562,7 +562,7 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
}
if (clnors == NULL) {
- clnors = CustomData_add_layer(ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, loops_num);
+ clnors = CustomData_add_layer(ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, NULL, loops_num);
}
MOD_get_vgroup(ob, result, enmd->defgrp_name, &dvert, &defgrp_index);
@@ -581,13 +581,13 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
dvert,
defgrp_index,
use_invert_vgroup,
- mvert,
+ verts,
verts_num,
- medge,
+ edges,
edges_num,
- mloop,
+ loops,
loops_num,
- mpoly,
+ polys,
polys_num);
}
else if (enmd->mode == MOD_NORMALEDIT_MODE_DIRECTIONAL) {
@@ -604,19 +604,19 @@ static Mesh *normalEditModifier_do(NormalEditModifierData *enmd,
dvert,
defgrp_index,
use_invert_vgroup,
- mvert,
+ verts,
verts_num,
- medge,
+ edges,
edges_num,
- mloop,
+ loops,
loops_num,
- mpoly,
+ polys,
polys_num);
}
MEM_SAFE_FREE(loopnors);
- result->runtime.is_original = false;
+ result->runtime.is_original_bmesh = false;
return result;
}
@@ -630,9 +630,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(enmd, DNA_struct_default_get(NormalEditModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
NormalEditModifierData *enmd = (NormalEditModifierData *)md;
@@ -670,7 +668,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
NormalEditModifierData *enmd = (NormalEditModifierData *)md;
if (enmd->target) {
DEG_add_object_relation(ctx->node, enmd->target, DEG_OB_COMP_TRANSFORM, "NormalEdit Modifier");
- DEG_add_modifier_to_transform_relation(ctx->node, "NormalEdit Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "NormalEdit Modifier");
}
}
@@ -756,7 +754,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_NormalEdit = {
- /* name */ "NormalEdit",
+ /* name */ N_("NormalEdit"),
/* structName */ "NormalEditModifierData",
/* structSize */ sizeof(NormalEditModifierData),
/* srna */ &RNA_NormalEditModifier,
diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c
index 75708851030..bee1bd7795a 100644
--- a/source/blender/modifiers/intern/MOD_ocean.c
+++ b/source/blender/modifiers/intern/MOD_ocean.c
@@ -130,9 +130,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
}
#ifdef WITH_OCEANSIM
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
OceanModifierData *omd = (OceanModifierData *)md;
@@ -141,8 +139,7 @@ static void requiredDataMask(Object *UNUSED(ob),
}
}
#else /* WITH_OCEANSIM */
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
+static void requiredDataMask(ModifierData *UNUSED(md),
CustomData_MeshMasks *UNUSED(r_cddata_masks))
{
}
@@ -169,9 +166,9 @@ typedef struct GenerateOceanGeometryData {
float ix, iy;
} GenerateOceanGeometryData;
-static void generate_ocean_geometry_vertices(void *__restrict userdata,
- const int y,
- const TaskParallelTLS *__restrict UNUSED(tls))
+static void generate_ocean_geometry_verts(void *__restrict userdata,
+ const int y,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
GenerateOceanGeometryData *gogd = userdata;
int x;
@@ -185,9 +182,9 @@ static void generate_ocean_geometry_vertices(void *__restrict userdata,
}
}
-static void generate_ocean_geometry_polygons(void *__restrict userdata,
- const int y,
- const TaskParallelTLS *__restrict UNUSED(tls))
+static void generate_ocean_geometry_polys(void *__restrict userdata,
+ const int y,
+ const TaskParallelTLS *__restrict UNUSED(tls))
{
GenerateOceanGeometryData *gogd = userdata;
int x;
@@ -273,26 +270,26 @@ static Mesh *generate_ocean_geometry(OceanModifierData *omd, Mesh *mesh_orig, co
result = BKE_mesh_new_nomain(verts_num, 0, 0, polys_num * 4, polys_num);
BKE_mesh_copy_parameters_for_eval(result, mesh_orig);
- gogd.mverts = result->mvert;
- gogd.mpolys = result->mpoly;
- gogd.mloops = result->mloop;
+ gogd.mverts = BKE_mesh_verts_for_write(result);
+ gogd.mpolys = BKE_mesh_polys_for_write(result);
+ gogd.mloops = BKE_mesh_loops_for_write(result);
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = use_threading;
/* create vertices */
- BLI_task_parallel_range(0, gogd.res_y + 1, &gogd, generate_ocean_geometry_vertices, &settings);
+ BLI_task_parallel_range(0, gogd.res_y + 1, &gogd, generate_ocean_geometry_verts, &settings);
/* create faces */
- BLI_task_parallel_range(0, gogd.res_y, &gogd, generate_ocean_geometry_polygons, &settings);
+ BLI_task_parallel_range(0, gogd.res_y, &gogd, generate_ocean_geometry_polys, &settings);
BKE_mesh_calc_edges(result, false, false);
/* add uvs */
if (CustomData_number_of_layers(&result->ldata, CD_MLOOPUV) < MAX_MTFACE) {
gogd.mloopuvs = CustomData_add_layer(
- &result->ldata, CD_MLOOPUV, CD_CALLOC, NULL, polys_num * 4);
+ &result->ldata, CD_MLOOPUV, CD_SET_DEFAULT, NULL, polys_num * 4);
if (gogd.mloopuvs) { /* unlikely to fail */
gogd.ix = 1.0 / gogd.rx;
@@ -322,8 +319,6 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes
const int resolution = (ctx->flag & MOD_APPLY_RENDER) ? omd->resolution :
omd->viewport_resolution;
- MVert *mverts;
-
int cfra_for_cache;
int i, j;
@@ -368,73 +363,75 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes
CLAMP(cfra_for_cache, omd->bakestart, omd->bakeend);
cfra_for_cache -= omd->bakestart; /* shift to 0 based */
- mverts = result->mvert;
+ MVert *verts = BKE_mesh_verts_for_write(result);
+ MPoly *polys = BKE_mesh_polys_for_write(result);
/* add vcols before displacement - allows lookup based on position */
if (omd->flag & MOD_OCEAN_GENERATE_FOAM) {
- if (CustomData_number_of_layers(&result->ldata, CD_PROP_BYTE_COLOR) < MAX_MCOL) {
- const int polys_num = result->totpoly;
- const int loops_num = result->totloop;
- MLoop *mloops = result->mloop;
- MLoopCol *mloopcols = CustomData_add_layer_named(
- &result->ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, NULL, loops_num, omd->foamlayername);
-
- MLoopCol *mloopcols_spray = NULL;
- if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
- mloopcols_spray = CustomData_add_layer_named(
- &result->ldata, CD_PROP_BYTE_COLOR, CD_CALLOC, NULL, loops_num, omd->spraylayername);
- }
+ const int polys_num = result->totpoly;
+ const int loops_num = result->totloop;
+ MLoop *mloops = BKE_mesh_loops_for_write(result);
+ MLoopCol *mloopcols = CustomData_add_layer_named(
+ &result->ldata, CD_PROP_BYTE_COLOR, CD_SET_DEFAULT, NULL, loops_num, omd->foamlayername);
+
+ MLoopCol *mloopcols_spray = NULL;
+ if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
+ mloopcols_spray = CustomData_add_layer_named(&result->ldata,
+ CD_PROP_BYTE_COLOR,
+ CD_SET_DEFAULT,
+ NULL,
+ loops_num,
+ omd->spraylayername);
+ }
- if (mloopcols) { /* unlikely to fail */
- MPoly *mpolys = result->mpoly;
- MPoly *mp;
+ if (mloopcols) { /* unlikely to fail */
+ MPoly *mp;
- for (i = 0, mp = mpolys; i < polys_num; i++, mp++) {
- MLoop *ml = &mloops[mp->loopstart];
- MLoopCol *mlcol = &mloopcols[mp->loopstart];
+ for (i = 0, mp = polys; i < polys_num; i++, mp++) {
+ MLoop *ml = &mloops[mp->loopstart];
+ MLoopCol *mlcol = &mloopcols[mp->loopstart];
- MLoopCol *mlcolspray = NULL;
- if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
- mlcolspray = &mloopcols_spray[mp->loopstart];
+ MLoopCol *mlcolspray = NULL;
+ if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
+ mlcolspray = &mloopcols_spray[mp->loopstart];
+ }
+
+ for (j = mp->totloop; j--; ml++, mlcol++) {
+ const float *vco = verts[ml->v].co;
+ const float u = OCEAN_CO(size_co_inv, vco[0]);
+ const float v = OCEAN_CO(size_co_inv, vco[1]);
+ float foam;
+
+ if (omd->oceancache && omd->cached == true) {
+ BKE_ocean_cache_eval_uv(omd->oceancache, &ocr, cfra_for_cache, u, v);
+ foam = ocr.foam;
+ CLAMP(foam, 0.0f, 1.0f);
+ }
+ else {
+ BKE_ocean_eval_uv(omd->ocean, &ocr, u, v);
+ foam = BKE_ocean_jminus_to_foam(ocr.Jminus, omd->foam_coverage);
}
- for (j = mp->totloop; j--; ml++, mlcol++) {
- const float *vco = mverts[ml->v].co;
- const float u = OCEAN_CO(size_co_inv, vco[0]);
- const float v = OCEAN_CO(size_co_inv, vco[1]);
- float foam;
+ mlcol->r = mlcol->g = mlcol->b = (char)(foam * 255);
+ /* This needs to be set (render engine uses) */
+ mlcol->a = 255;
- if (omd->oceancache && omd->cached == true) {
- BKE_ocean_cache_eval_uv(omd->oceancache, &ocr, cfra_for_cache, u, v);
- foam = ocr.foam;
- CLAMP(foam, 0.0f, 1.0f);
+ if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
+ if (omd->flag & MOD_OCEAN_INVERT_SPRAY) {
+ mlcolspray->r = ocr.Eminus[0] * 255;
}
else {
- BKE_ocean_eval_uv(omd->ocean, &ocr, u, v);
- foam = BKE_ocean_jminus_to_foam(ocr.Jminus, omd->foam_coverage);
+ mlcolspray->r = ocr.Eplus[0] * 255;
}
-
- mlcol->r = mlcol->g = mlcol->b = (char)(foam * 255);
- /* This needs to be set (render engine uses) */
- mlcol->a = 255;
-
- if (omd->flag & MOD_OCEAN_GENERATE_SPRAY) {
- if (omd->flag & MOD_OCEAN_INVERT_SPRAY) {
- mlcolspray->r = ocr.Eminus[0] * 255;
- }
- else {
- mlcolspray->r = ocr.Eplus[0] * 255;
- }
- mlcolspray->g = 0;
- if (omd->flag & MOD_OCEAN_INVERT_SPRAY) {
- mlcolspray->b = ocr.Eminus[2] * 255;
- }
- else {
- mlcolspray->b = ocr.Eplus[2] * 255;
- }
- mlcolspray->a = 255;
+ mlcolspray->g = 0;
+ if (omd->flag & MOD_OCEAN_INVERT_SPRAY) {
+ mlcolspray->b = ocr.Eminus[2] * 255;
+ }
+ else {
+ mlcolspray->b = ocr.Eplus[2] * 255;
}
+ mlcolspray->a = 255;
}
}
}
@@ -449,7 +446,7 @@ static Mesh *doOcean(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mes
const int verts_num = result->totvert;
for (i = 0; i < verts_num; i++) {
- float *vco = mverts[i].co;
+ float *vco = verts[i].co;
const float u = OCEAN_CO(size_co_inv, vco[0]);
const float v = OCEAN_CO(size_co_inv, vco[1]);
@@ -595,7 +592,7 @@ static void spray_panel_draw_header(const bContext *UNUSED(C), Panel *panel)
row = uiLayoutRow(layout, false);
uiLayoutSetActive(row, use_foam);
- uiItemR(row, ptr, "use_spray", 0, IFACE_("Spray"), ICON_NONE);
+ uiItemR(row, ptr, "use_spray", 0, CTX_IFACE_(BLT_I18NCONTEXT_ID_MESH, "Spray"), ICON_NONE);
}
static void spray_panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -701,7 +698,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
}
ModifierTypeInfo modifierType_Ocean = {
- /* name */ "Ocean",
+ /* name */ N_("Ocean"),
/* structName */ "OceanModifierData",
/* structSize */ sizeof(OceanModifierData),
/* srna */ &RNA_OceanModifier,
diff --git a/source/blender/modifiers/intern/MOD_particleinstance.c b/source/blender/modifiers/intern/MOD_particleinstance.c
index 2a8d2454670..aab48101351 100644
--- a/source/blender/modifiers/intern/MOD_particleinstance.c
+++ b/source/blender/modifiers/intern/MOD_particleinstance.c
@@ -52,9 +52,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(pimd, DNA_struct_default_get(ParticleInstanceModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
ParticleInstanceModifierData *pimd = (ParticleInstanceModifierData *)md;
@@ -200,9 +198,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
ParticleSimulationData sim;
ParticleSystem *psys = NULL;
ParticleData *pa = NULL;
- MPoly *mpoly, *orig_mpoly;
- MLoop *mloop, *orig_mloop;
- MVert *mvert, *orig_mvert;
int totvert, totpoly, totloop, totedge;
int maxvert, maxpoly, maxloop, maxedge, part_end = 0, part_start;
int k, p, p_skip;
@@ -320,12 +315,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
result = BKE_mesh_new_nomain_from_template(mesh, maxvert, maxedge, 0, maxloop, maxpoly);
- mvert = result->mvert;
- orig_mvert = mesh->mvert;
- mpoly = result->mpoly;
- orig_mpoly = mesh->mpoly;
- mloop = result->mloop;
- orig_mloop = mesh->mloop;
+ const MVert *orig_mvert = BKE_mesh_verts(mesh);
+ const MPoly *orig_mpoly = BKE_mesh_polys(mesh);
+ const MLoop *orig_mloop = BKE_mesh_loops(mesh);
+ MVert *mvert = BKE_mesh_verts_for_write(result);
+ MEdge *edges = BKE_mesh_edges_for_write(result);
+ MPoly *mpoly = BKE_mesh_polys_for_write(result);
+ MLoop *mloop = BKE_mesh_loops_for_write(result);
MLoopCol *mloopcols_index = CustomData_get_layer_named(
&result->ldata, CD_PROP_BYTE_COLOR, pimd->index_layer_name);
@@ -353,7 +349,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* set vertices coordinates */
for (k = 0; k < totvert; k++) {
ParticleKey state;
- MVert *inMV;
+ const MVert *inMV;
int vindex = p_skip * totvert + k;
MVert *mv = mvert + vindex;
@@ -477,7 +473,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* Create edges and adjust edge vertex indices. */
CustomData_copy_data(&mesh->edata, &result->edata, 0, p_skip * totedge, totedge);
- MEdge *me = &result->medge[p_skip * totedge];
+ MEdge *me = &edges[p_skip * totedge];
for (k = 0; k < totedge; k++, me++) {
me->v1 += p_skip * totvert;
me->v2 += p_skip * totvert;
@@ -486,7 +482,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* create polys and loops */
for (k = 0; k < totpoly; k++) {
- MPoly *inMP = orig_mpoly + k;
+ const MPoly *inMP = orig_mpoly + k;
MPoly *mp = mpoly + p_skip * totpoly + k;
CustomData_copy_data(&mesh->pdata, &result->pdata, k, p_skip * totpoly + k, 1);
@@ -494,7 +490,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
mp->loopstart += p_skip * totloop;
{
- MLoop *inML = orig_mloop + inMP->loopstart;
+ const MLoop *inML = orig_mloop + inMP->loopstart;
MLoop *ml = mloop + mp->loopstart;
int j = mp->totloop;
@@ -553,7 +549,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
"particle_system",
&particle_obj_ptr,
"particle_systems",
- "Particle System",
+ IFACE_("Particle System"),
ICON_NONE);
}
else {
@@ -644,7 +640,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_ParticleInstance = {
- /* name */ "ParticleInstance",
+ /* name */ N_("ParticleInstance"),
/* structName */ "ParticleInstanceModifierData",
/* structSize */ sizeof(ParticleInstanceModifierData),
/* srna */ &RNA_ParticleInstanceModifier,
diff --git a/source/blender/modifiers/intern/MOD_particlesystem.cc b/source/blender/modifiers/intern/MOD_particlesystem.cc
index ccbc8f1d835..7d3d384d3a8 100644
--- a/source/blender/modifiers/intern/MOD_particlesystem.cc
+++ b/source/blender/modifiers/intern/MOD_particlesystem.cc
@@ -87,9 +87,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
tpsmd->totdmvert = tpsmd->totdmedge = tpsmd->totdmface = 0;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
@@ -119,8 +117,7 @@ static void deformVerts(ModifierData *md,
}
if (mesh_src == nullptr) {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, nullptr, nullptr, vertexCos, verts_num, true);
if (mesh_src == nullptr) {
return;
}
@@ -301,7 +298,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_ParticleSystem = {
- /* name */ "ParticleSystem",
+ /* name */ N_("ParticleSystem"),
/* structName */ "ParticleSystemModifierData",
/* structSize */ sizeof(ParticleSystemModifierData),
/* srna */ &RNA_ParticleSystemModifier,
diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c
index 288faee247f..4241ca5a591 100644
--- a/source/blender/modifiers/intern/MOD_remesh.c
+++ b/source/blender/modifiers/intern/MOD_remesh.c
@@ -60,11 +60,11 @@ static void init_dualcon_mesh(DualConInput *input, Mesh *mesh)
{
memset(input, 0, sizeof(DualConInput));
- input->co = (void *)mesh->mvert;
+ input->co = (void *)BKE_mesh_verts(mesh);
input->co_stride = sizeof(MVert);
input->totco = mesh->totvert;
- input->mloop = (void *)mesh->mloop;
+ input->mloop = (void *)BKE_mesh_loops(mesh);
input->loop_stride = sizeof(MLoop);
BKE_mesh_runtime_looptri_ensure(mesh);
@@ -80,6 +80,9 @@ static void init_dualcon_mesh(DualConInput *input, Mesh *mesh)
* keep track of the current elements */
typedef struct {
Mesh *mesh;
+ MVert *verts;
+ MPoly *polys;
+ MLoop *loops;
int curvert, curface;
} DualConOutput;
@@ -93,17 +96,20 @@ static void *dualcon_alloc_output(int totvert, int totquad)
}
output->mesh = BKE_mesh_new_nomain(totvert, 0, 0, 4 * totquad, totquad);
+ output->verts = BKE_mesh_verts_for_write(output->mesh);
+ output->polys = BKE_mesh_polys_for_write(output->mesh);
+ output->loops = BKE_mesh_loops_for_write(output->mesh);
+
return output;
}
static void dualcon_add_vert(void *output_v, const float co[3])
{
DualConOutput *output = output_v;
- Mesh *mesh = output->mesh;
- BLI_assert(output->curvert < mesh->totvert);
+ BLI_assert(output->curvert < output->mesh->totvert);
- copy_v3_v3(mesh->mvert[output->curvert].co, co);
+ copy_v3_v3(output->verts[output->curvert].co, co);
output->curvert++;
}
@@ -111,14 +117,13 @@ static void dualcon_add_quad(void *output_v, const int vert_indices[4])
{
DualConOutput *output = output_v;
Mesh *mesh = output->mesh;
- MLoop *mloop;
- MPoly *cur_poly;
int i;
BLI_assert(output->curface < mesh->totpoly);
+ UNUSED_VARS_NDEBUG(mesh);
- mloop = mesh->mloop;
- cur_poly = &mesh->mpoly[output->curface];
+ MLoop *mloop = output->loops;
+ MPoly *cur_poly = &output->polys[output->curface];
cur_poly->loopstart = output->curface * 4;
cur_poly->totloop = 4;
@@ -195,7 +200,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx)
}
if (rmd->flag & MOD_REMESH_SMOOTH_SHADING) {
- MPoly *mpoly = result->mpoly;
+ MPoly *mpoly = BKE_mesh_polys_for_write(result);
int i, totpoly = result->totpoly;
/* Apply smooth shading to output faces */
@@ -268,7 +273,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Remesh = {
- /* name */ "Remesh",
+ /* name */ N_("Remesh"),
/* structName */ "RemeshModifierData",
/* structSize */ sizeof(RemeshModifierData),
/* srna */ &RNA_RemeshModifier,
diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c
index 0e22f59c2fb..6a46edef9a2 100644
--- a/source/blender/modifiers/intern/MOD_screw.c
+++ b/source/blender/modifiers/intern/MOD_screw.c
@@ -41,6 +41,8 @@
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
+#include "BLI_strict_flags.h"
+
static void initData(ModifierData *md)
{
ScrewModifierData *ltmd = (ScrewModifierData *)md;
@@ -50,15 +52,18 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(ltmd, DNA_struct_default_get(ScrewModifierData), modifier);
}
-#include "BLI_strict_flags.h"
-
-/* used for gathering edge connectivity */
+/** Used for gathering edge connectivity. */
typedef struct ScrewVertConnect {
- float dist; /* distance from the center axis */
- float co[3]; /* location relative to the transformed axis */
- float no[3]; /* calc normal of the vertex */
- uint v[2]; /* 2 verts on either side of this one */
- MEdge *e[2]; /* edges on either side, a bit of a waste since each edge ref's 2 edges */
+ /** Distance from the center axis. */
+ float dist_sq;
+ /** Location relative to the transformed axis. */
+ float co[3];
+ /** Calc normal of the vertex. */
+ float no[3];
+ /** 2 verts on either side of this one. */
+ uint v[2];
+ /** Edges on either side, a bit of a waste since each edge ref's 2 edges. */
+ MEdge *e[2];
char flag;
} ScrewVertConnect;
@@ -177,7 +182,7 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result,
static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *meshData)
{
- Mesh *mesh = meshData;
+ const Mesh *mesh = meshData;
Mesh *result;
ScrewModifierData *ltmd = (ScrewModifierData *)md;
const bool use_render_params = (ctx->flag & MOD_APPLY_RENDER) != 0;
@@ -236,11 +241,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
uint edge_offset;
- MPoly *mpoly_orig, *mpoly_new, *mp_new;
- MLoop *mloop_orig, *mloop_new, *ml_new;
- MEdge *medge_orig, *med_orig, *med_new, *med_new_firstloop, *medge_new;
- MVert *mvert_new, *mvert_orig, *mv_orig, *mv_new, *mv_new_base;
-
+ MPoly *mp_new;
+ MLoop *ml_new;
+ MEdge *med_new, *med_new_firstloop;
+ MVert *mv_new, *mv_new_base;
+ const MVert *mv_orig;
Object *ob_axis = ltmd->ob_axis;
ScrewVertConnect *vc, *vc_tmp, *vert_connect = NULL;
@@ -270,18 +275,18 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
axis_vec[ltmd->axis] = 1.0f;
if (ob_axis != NULL) {
- /* calc the matrix relative to the axis object */
+ /* Calculate the matrix relative to the axis object. */
invert_m4_m4(mtx_tmp_a, ctx->object->obmat);
copy_m4_m4(mtx_tx_inv, ob_axis->obmat);
mul_m4_m4m4(mtx_tx, mtx_tmp_a, mtx_tx_inv);
- /* calc the axis vec */
+ /* Calculate the axis vector. */
mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */
normalize_v3(axis_vec);
/* screw */
if (ltmd->flag & MOD_SCREW_OBJECT_OFFSET) {
- /* find the offset along this axis relative to this objects matrix */
+ /* Find the offset along this axis relative to this objects matrix. */
float totlen = len_v3(mtx_tx[3]);
if (totlen != 0.0f) {
@@ -330,7 +335,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
else {
axis_char = (char)(axis_char + ltmd->axis); /* 'X' + axis */
- /* useful to be able to use the axis vec in some cases still */
+ /* Useful to be able to use the axis vector in some cases still. */
zero_v3(axis_vec);
axis_vec[ltmd->axis] = 1.0f;
}
@@ -383,17 +388,18 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
result = BKE_mesh_new_nomain_from_template(
mesh, (int)maxVerts, (int)maxEdges, 0, (int)maxPolys * 4, (int)maxPolys);
- /* copy verts from mesh */
- mvert_orig = mesh->mvert;
- medge_orig = mesh->medge;
+ const MVert *mvert_orig = BKE_mesh_verts(mesh);
+ const MEdge *medge_orig = BKE_mesh_edges(mesh);
+ const MPoly *mpoly_orig = BKE_mesh_polys(mesh);
+ const MLoop *mloop_orig = BKE_mesh_loops(mesh);
- mvert_new = result->mvert;
- mpoly_new = result->mpoly;
- mloop_new = result->mloop;
- medge_new = result->medge;
+ MVert *mvert_new = BKE_mesh_verts_for_write(result);
+ MEdge *medge_new = BKE_mesh_edges_for_write(result);
+ MPoly *mpoly_new = BKE_mesh_polys_for_write(result);
+ MLoop *mloop_new = BKE_mesh_loops_for_write(result);
if (!CustomData_has_layer(&result->pdata, CD_ORIGINDEX)) {
- CustomData_add_layer(&result->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, (int)maxPolys);
+ CustomData_add_layer(&result->pdata, CD_ORIGINDEX, CD_SET_DEFAULT, NULL, (int)maxPolys);
}
int *origindex = CustomData_get_layer(&result->pdata, CD_ORIGINDEX);
@@ -433,7 +439,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BLI_bitmap *vert_tag = BLI_BITMAP_NEW(totvert, __func__);
/* Copy the first set of edges */
- med_orig = medge_orig;
+ const MEdge *med_orig = medge_orig;
med_new = medge_new;
for (i = 0; i < totedge; i++, med_orig++, med_new++) {
med_new->v1 = med_orig->v1;
@@ -441,17 +447,15 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
med_new->crease = med_orig->crease;
med_new->flag = med_orig->flag & ~ME_LOOSEEDGE;
- /* Tag mvert as not loose. */
+ /* Tag #MVert as not loose. */
BLI_BITMAP_ENABLE(vert_tag, med_orig->v1);
BLI_BITMAP_ENABLE(vert_tag, med_orig->v2);
}
/* build polygon -> edge map */
if (totpoly) {
- MPoly *mp_orig;
+ const MPoly *mp_orig;
- mpoly_orig = mesh->mpoly;
- mloop_orig = mesh->mloop;
edge_poly_map = MEM_malloc_arrayN(totedge, sizeof(*edge_poly_map), __func__);
memset(edge_poly_map, 0xff, sizeof(*edge_poly_map) * totedge);
@@ -462,7 +466,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
uint loopstart = (uint)mp_orig->loopstart;
uint loopend = loopstart + (uint)mp_orig->totloop;
- MLoop *ml_orig = &mloop_orig[loopstart];
+ const MLoop *ml_orig = &mloop_orig[loopstart];
uint k;
for (k = loopstart; k < loopend; k++, ml_orig++) {
edge_poly_map[ml_orig->e] = i;
@@ -481,8 +485,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
if (ltmd->flag & MOD_SCREW_NORMAL_CALC) {
- /*
- * Normal Calculation (for face flipping)
+ /* Normal Calculation (for face flipping)
* Sort edge verts for correct face flipping
* NOT REALLY NEEDED but face flipping is nice. */
@@ -490,19 +493,19 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
*
* Since we are only ordering the edges here it can avoid mallocing the
* extra space by abusing the vert array before its filled with new verts.
- * The new array for vert_connect must be at least sizeof(ScrewVertConnect) * totvert
- * and the size of our resulting meshes array is sizeof(MVert) * totvert * 3
- * so its safe to use the second 2 thirds of MVert the array for vert_connect,
- * just make sure ScrewVertConnect struct is no more than twice as big as MVert,
+ * The new array for vert_connect must be at least `sizeof(ScrewVertConnect) * totvert`
+ * and the size of our resulting meshes array is `sizeof(MVert) * totvert * 3`
+ * so its safe to use the second 2 thirds of #MVert the array for vert_connect,
+ * just make sure #ScrewVertConnect struct is no more than twice as big as #MVert,
* at the moment there is no chance of that being a problem,
- * unless MVert becomes half its current size.
+ * unless #MVert becomes half its current size.
*
* once the edges are ordered, vert_connect is not needed and it can be used for verts
*
- * This makes the modifier faster with one less alloc.
+ * This makes the modifier faster with one less allocate.
*/
- vert_connect = MEM_malloc_arrayN(totvert, sizeof(ScrewVertConnect), "ScrewVertConnect");
+ vert_connect = MEM_malloc_arrayN(totvert, sizeof(ScrewVertConnect), __func__);
/* skip the first slice of verts. */
// vert_connect = (ScrewVertConnect *) &medge_new[totvert];
vc = vert_connect;
@@ -512,7 +515,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
if (!totedge) {
for (i = 0; i < totvert; i++, mv_orig++, mv_new++) {
copy_v3_v3(mv_new->co, mv_orig->co);
- normalize_v3_v3(vc->no, mv_new->co); /* no edges- this is really a dummy normal */
+ /* No edges: this is really a dummy normal. */
+ normalize_v3_v3(vc->no, mv_new->co);
}
}
else {
@@ -533,11 +537,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
vc->v[0] = vc->v[1] = SV_UNUSED;
mul_m4_v3(mtx_tx, vc->co);
- /* length in 2d, don't sqrt because this is only for comparison */
- vc->dist = vc->co[other_axis_1] * vc->co[other_axis_1] +
- vc->co[other_axis_2] * vc->co[other_axis_2];
+ /* Length in 2D, don't `sqrt` because this is only for comparison. */
+ vc->dist_sq = vc->co[other_axis_1] * vc->co[other_axis_1] +
+ vc->co[other_axis_2] * vc->co[other_axis_2];
- // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist);
+ // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist_sq);
}
}
else {
@@ -550,11 +554,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
vc->e[0] = vc->e[1] = NULL;
vc->v[0] = vc->v[1] = SV_UNUSED;
- /* length in 2d, don't sqrt because this is only for comparison */
- vc->dist = vc->co[other_axis_1] * vc->co[other_axis_1] +
- vc->co[other_axis_2] * vc->co[other_axis_2];
+ /* Length in 2D, don't sqrt because this is only for comparison. */
+ vc->dist_sq = vc->co[other_axis_1] * vc->co[other_axis_1] +
+ vc->co[other_axis_2] * vc->co[other_axis_2];
- // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist);
+ // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist_sq);
}
}
@@ -622,9 +626,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
lt_iter.v_poin->flag = 1;
vc_tot_linked++;
- // printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist);
- if (fl <= lt_iter.v_poin->dist) {
- fl = lt_iter.v_poin->dist;
+ // printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist_sq);
+ if (fl <= lt_iter.v_poin->dist_sq) {
+ fl = lt_iter.v_poin->dist_sq;
v_best = lt_iter.v;
// printf("\t\t\tVERT BEST: %i\n", v_best);
}
@@ -942,6 +946,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* more of an offset in this case */
edge_offset = totedge + (totvert * (step_tot - (close ? 0 : 1)));
+ const int *src_material_index = BKE_mesh_material_indices(mesh);
+ int *dst_material_index = BKE_mesh_material_indices_for_write(result);
+
for (i = 0; i < totedge; i++, med_new_firstloop++) {
const uint step_last = step_tot - (close ? 1 : 2);
const uint mpoly_index_orig = totpoly ? edge_poly_map[i] : UINT_MAX;
@@ -954,14 +961,14 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
};
const bool has_mloop_orig = mloop_index_orig[0] != UINT_MAX;
- short mat_nr;
+ int mat_nr;
/* for each edge, make a cylinder of quads */
i1 = med_new_firstloop->v1;
i2 = med_new_firstloop->v2;
if (has_mpoly_orig) {
- mat_nr = mpoly_orig[mpoly_index_orig].mat_nr;
+ mat_nr = src_material_index == NULL ? 0 : src_material_index[mpoly_index_orig];
}
else {
mat_nr = 0;
@@ -987,8 +994,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
else {
origindex[mpoly_index] = ORIGINDEX_NONE;
+ dst_material_index[mpoly_index] = mat_nr;
mp_new->flag = mpoly_flag;
- mp_new->mat_nr = mat_nr;
}
mp_new->loopstart = mpoly_index * 4;
mp_new->totloop = 4;
@@ -1148,7 +1155,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
ScrewModifierData *ltmd = (ScrewModifierData *)md;
if (ltmd->ob_axis != NULL) {
DEG_add_object_relation(ctx->node, ltmd->ob_axis, DEG_OB_COMP_TRANSFORM, "Screw Modifier");
- DEG_add_modifier_to_transform_relation(ctx->node, "Screw Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Screw Modifier");
}
}
@@ -1235,7 +1242,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Screw = {
- /* name */ "Screw",
+ /* name */ N_("Screw"),
/* structName */ "ScrewModifierData",
/* structSize */ sizeof(ScrewModifierData),
/* srna */ &RNA_ScrewModifier,
diff --git a/source/blender/modifiers/intern/MOD_shapekey.c b/source/blender/modifiers/intern/MOD_shapekey.c
index 6953e89dfc2..3649a108ed4 100644
--- a/source/blender/modifiers/intern/MOD_shapekey.c
+++ b/source/blender/modifiers/intern/MOD_shapekey.c
@@ -9,6 +9,8 @@
#include "BLI_math.h"
+#include "BLT_translation.h"
+
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
@@ -34,7 +36,7 @@ static void deformVerts(ModifierData *UNUSED(md),
if (key && key->block.first) {
int deformedVerts_tot;
BKE_key_evaluate_object_ex(
- ctx->object, &deformedVerts_tot, (float *)vertexCos, sizeof(*vertexCos) * verts_num);
+ ctx->object, &deformedVerts_tot, (float *)vertexCos, sizeof(*vertexCos) * verts_num, NULL);
}
}
@@ -47,11 +49,11 @@ static void deformMatrices(ModifierData *md,
{
Key *key = BKE_key_from_object(ctx->object);
KeyBlock *kb = BKE_keyblock_from_object(ctx->object);
- float scale[3][3];
(void)vertexCos; /* unused */
if (kb && kb->totelem == verts_num && kb != key->refkey) {
+ float scale[3][3];
int a;
if (ctx->object->shapeflag & OB_SHAPE_LOCK) {
@@ -93,22 +95,21 @@ static void deformMatricesEM(ModifierData *UNUSED(md),
{
Key *key = BKE_key_from_object(ctx->object);
KeyBlock *kb = BKE_keyblock_from_object(ctx->object);
- float scale[3][3];
(void)vertexCos; /* unused */
if (kb && kb->totelem == verts_num && kb != key->refkey) {
- int a;
+ float scale[3][3];
scale_m3_fl(scale, kb->curval);
- for (a = 0; a < verts_num; a++) {
+ for (int a = 0; a < verts_num; a++) {
copy_m3_m3(defMats[a], scale);
}
}
}
ModifierTypeInfo modifierType_ShapeKey = {
- /* name */ "ShapeKey",
+ /* name */ N_("ShapeKey"),
/* structName */ "ShapeKeyModifierData",
/* structSize */ sizeof(ShapeKeyModifierData),
/* srna */ &RNA_Modifier,
diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c
index 488df3d6f4c..df8b9d53a2f 100644
--- a/source/blender/modifiers/intern/MOD_shrinkwrap.c
+++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c
@@ -48,9 +48,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(ShrinkwrapModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
ShrinkwrapModifierData *smd = (ShrinkwrapModifierData *)md;
@@ -108,10 +106,10 @@ static void deformVerts(ModifierData *md,
(swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) {
/* mesh_src is needed for vgroups, but also used as ShrinkwrapCalcData.vert when projecting.
* Avoid time-consuming mesh conversion for curves when not projecting. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
- struct MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
int defgrp_index = -1;
MOD_get_vgroup(ctx->object, mesh_src, swmd->vgroup_name, &dvert, &defgrp_index);
@@ -135,16 +133,15 @@ static void deformVertsEM(ModifierData *md,
Mesh *mesh_src = NULL;
if ((swmd->vgroup_name[0] != '\0') || (swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
- struct MDeformVert *dvert = NULL;
+ const MDeformVert *dvert = NULL;
int defgrp_index = -1;
if (swmd->vgroup_name[0] != '\0') {
MOD_get_vgroup(ctx->object, mesh_src, swmd->vgroup_name, &dvert, &defgrp_index);
@@ -186,7 +183,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
DEG_add_special_eval_flag(ctx->node, &smd->auxTarget->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY);
}
}
- DEG_add_modifier_to_transform_relation(ctx->node, "Shrinkwrap Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Shrinkwrap Modifier");
}
static bool dependsOnNormals(ModifierData *md)
@@ -260,7 +257,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Shrinkwrap = {
- /* name */ "Shrinkwrap",
+ /* name */ N_("Shrinkwrap"),
/* structName */ "ShrinkwrapModifierData",
/* structSize */ sizeof(ShrinkwrapModifierData),
/* srna */ &RNA_ShrinkwrapModifier,
diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c
index e3c7f1c423b..0de89850bc9 100644
--- a/source/blender/modifiers/intern/MOD_simpledeform.c
+++ b/source/blender/modifiers/intern/MOD_simpledeform.c
@@ -294,7 +294,7 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd,
float smd_limit[2], smd_factor;
SpaceTransform *transf = NULL, tmp_transf;
int vgroup;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
/* This is historically the lock axis, _not_ the deform axis as the name would imply */
const int deform_axis = smd->deform_axis;
@@ -414,9 +414,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SimpleDeformModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
SimpleDeformModifierData *smd = (SimpleDeformModifierData *)md;
@@ -438,7 +436,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
if (smd->origin != NULL) {
DEG_add_object_relation(
ctx->node, smd->origin, DEG_OB_COMP_TRANSFORM, "SimpleDeform Modifier");
- DEG_add_modifier_to_transform_relation(ctx->node, "SimpleDeform Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "SimpleDeform Modifier");
}
}
@@ -453,7 +451,7 @@ static void deformVerts(ModifierData *md,
if (ctx->object->type == OB_MESH && sdmd->vgroup_name[0] != '\0') {
/* mesh_src is only needed for vgroups. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
SimpleDeformModifier_do(sdmd, ctx, ctx->object, mesh_src, vertexCos, verts_num);
@@ -475,11 +473,10 @@ static void deformVertsEM(ModifierData *md,
if (ctx->object->type == OB_MESH && sdmd->vgroup_name[0] != '\0') {
/* mesh_src is only needed for vgroups. */
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -564,7 +561,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_SimpleDeform = {
- /* name */ "SimpleDeform",
+ /* name */ N_("SimpleDeform"),
/* structName */ "SimpleDeformModifierData",
/* structSize */ sizeof(SimpleDeformModifierData),
/* srna */ &RNA_SimpleDeformModifier,
diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c
index 5f238209015..b01bcec0bc1 100644
--- a/source/blender/modifiers/intern/MOD_skin.c
+++ b/source/blender/modifiers/intern/MOD_skin.c
@@ -889,9 +889,9 @@ static Mesh *subdivide_base(const Mesh *orig)
float radrat;
const MVertSkin *orignode = CustomData_get_layer(&orig->vdata, CD_MVERT_SKIN);
- const MVert *origvert = orig->mvert;
- const MEdge *origedge = orig->medge;
- const MDeformVert *origdvert = orig->dvert;
+ const MVert *origvert = BKE_mesh_verts(orig);
+ const MEdge *origedge = BKE_mesh_edges(orig);
+ const MDeformVert *origdvert = BKE_mesh_deform_verts(orig);
int orig_vert_num = orig->totvert;
int orig_edge_num = orig->totedge;
@@ -916,10 +916,13 @@ static Mesh *subdivide_base(const Mesh *orig)
Mesh *result = BKE_mesh_new_nomain_from_template(
orig, orig_vert_num + subd_num, orig_edge_num + subd_num, 0, 0, 0);
- MVert *outvert = result->mvert;
- MEdge *outedge = result->medge;
+ MVert *outvert = BKE_mesh_verts_for_write(result);
+ MEdge *outedge = BKE_mesh_edges_for_write(result);
MVertSkin *outnode = CustomData_get_layer(&result->vdata, CD_MVERT_SKIN);
- MDeformVert *outdvert = result->dvert;
+ MDeformVert *outdvert = NULL;
+ if (origdvert) {
+ outdvert = BKE_mesh_deform_verts_for_write(result);
+ }
/* Copy original vertex data */
CustomData_copy_data(&orig->vdata, &result->vdata, 0, 0, orig_vert_num);
@@ -1731,7 +1734,7 @@ static void skin_smooth_hulls(BMesh *bm,
}
}
- /* Add temporary shapekey layer to store original coordinates */
+ /* Add temporary shape-key layer to store original coordinates. */
BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY);
skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1;
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
@@ -1888,7 +1891,7 @@ static void skin_set_orig_indices(Mesh *mesh)
int *orig, totpoly;
totpoly = mesh->totpoly;
- orig = CustomData_add_layer(&mesh->pdata, CD_ORIGINDEX, CD_CALLOC, NULL, totpoly);
+ orig = CustomData_add_layer(&mesh->pdata, CD_ORIGINDEX, CD_CONSTRUCT, NULL, totpoly);
copy_vn_i(orig, totpoly, ORIGINDEX_NONE);
}
@@ -1907,17 +1910,17 @@ static Mesh *base_skin(Mesh *origmesh, SkinModifierData *smd, eSkinErrorFlag *r_
SkinNode *skin_nodes;
MeshElemMap *emap;
int *emapmem;
- MVert *mvert;
- MEdge *medge;
- MDeformVert *dvert;
+ const MVert *mvert;
+ const MEdge *medge;
+ const MDeformVert *dvert;
int verts_num, edges_num;
bool has_valid_root = false;
nodes = CustomData_get_layer(&origmesh->vdata, CD_MVERT_SKIN);
- mvert = origmesh->mvert;
- dvert = origmesh->dvert;
- medge = origmesh->medge;
+ mvert = BKE_mesh_verts(origmesh);
+ dvert = BKE_mesh_deform_verts(origmesh);
+ medge = BKE_mesh_edges(origmesh);
verts_num = origmesh->totvert;
edges_num = origmesh->totedge;
@@ -2004,9 +2007,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return result;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks)
{
r_cddata_masks->vmask |= CD_MASK_MVERT_SKIN | CD_MASK_MDEFORMVERT;
}
@@ -2069,7 +2070,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Skin = {
- /* name */ "Skin",
+ /* name */ N_("Skin"),
/* structName */ "SkinModifierData",
/* structSize */ sizeof(SkinModifierData),
/* srna */ &RNA_SkinModifier,
diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c
index 5439083d9a7..72fc945675c 100644
--- a/source/blender/modifiers/intern/MOD_smooth.c
+++ b/source/blender/modifiers/intern/MOD_smooth.c
@@ -63,9 +63,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene),
return false;
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
SmoothModifierData *smd = (SmoothModifierData *)md;
@@ -99,10 +97,10 @@ static void smoothModifier_do(
const float fac_orig = 1.0f - fac_new;
const bool invert_vgroup = (smd->flag & MOD_SMOOTH_INVERT_VGROUP) != 0;
- MEdge *medges = mesh->medge;
+ const MEdge *medges = BKE_mesh_edges(mesh);
const int edges_num = mesh->totedge;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
int defgrp_index;
MOD_get_vgroup(ob, mesh, smd->defgrp_name, &dvert, &defgrp_index);
@@ -128,7 +126,7 @@ static void smoothModifier_do(
const short flag = smd->flag;
if (dvert) {
- MDeformVert *dv = dvert;
+ const MDeformVert *dv = dvert;
for (int i = 0; i < verts_num; i++, dv++) {
float *vco_orig = vertexCos[i];
if (accumulated_vecs_count[i] > 0) {
@@ -190,7 +188,7 @@ static void deformVerts(ModifierData *md,
Mesh *mesh_src = NULL;
/* mesh_src is needed for vgroups, and taking edges into account. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
smoothModifier_do(smd, ctx->object, mesh_src, vertexCos, verts_num);
@@ -210,9 +208,9 @@ static void deformVertsEM(ModifierData *md,
Mesh *mesh_src = NULL;
/* mesh_src is needed for vgroups, and taking edges into account. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
- /* TODO(campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
BKE_mesh_wrapper_ensure_mdata(mesh_src);
smoothModifier_do(smd, ctx->object, mesh_src, vertexCos, verts_num);
@@ -253,7 +251,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Smooth = {
- /* name */ "Smooth",
+ /* name */ N_("Smooth"),
/* structName */ "SmoothModifierData",
/* structSize */ sizeof(SmoothModifierData),
/* srna */ &RNA_SmoothModifier,
diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c
index d8379cc870a..ecff6d80893 100644
--- a/source/blender/modifiers/intern/MOD_softbody.c
+++ b/source/blender/modifiers/intern/MOD_softbody.c
@@ -66,7 +66,7 @@ static void updateDepsgraph(ModifierData *UNUSED(md), const ModifierUpdateDepsgr
ctx->node, ctx->object, ctx->object->soft->effector_weights, true, 0, "Softbody Field");
}
/* We need own transformation as well. */
- DEG_add_modifier_to_transform_relation(ctx->node, "SoftBody Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "SoftBody Modifier");
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -86,7 +86,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Softbody = {
- /* name */ "Softbody",
+ /* name */ N_("Softbody"),
/* structName */ "SoftbodyModifierData",
/* structSize */ sizeof(SoftbodyModifierData),
/* srna */ &RNA_SoftBodyModifier,
diff --git a/source/blender/modifiers/intern/MOD_solidify.c b/source/blender/modifiers/intern/MOD_solidify.c
index 1f0aee7d689..1c7369d9158 100644
--- a/source/blender/modifiers/intern/MOD_solidify.c
+++ b/source/blender/modifiers/intern/MOD_solidify.c
@@ -53,9 +53,7 @@ static void initData(ModifierData *md)
# pragma GCC diagnostic error "-Wsign-conversion"
#endif
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
SolidifyModifierData *smd = (SolidifyModifierData *)md;
@@ -241,7 +239,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Solidify = {
- /* name */ "Solidify",
+ /* name */ N_("Solidify"),
/* structName */ "SolidifyModifierData",
/* structSize */ sizeof(SolidifyModifierData),
/* srna */ &RNA_SolidifyModifier,
diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c
index 80af23054e4..1456254c31f 100644
--- a/source/blender/modifiers/intern/MOD_solidify_extrude.c
+++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c
@@ -53,29 +53,24 @@ BLI_INLINE bool edgeref_is_init(const EdgeFaceRef *edge_ref)
* \param poly_nors: Precalculated face normals.
* \param r_vert_nors: Return vert normals.
*/
-static void mesh_calc_hq_normal(Mesh *mesh, const float (*poly_nors)[3], float (*r_vert_nors)[3])
+static void mesh_calc_hq_normal(Mesh *mesh,
+ const float (*poly_nors)[3],
+ float (*r_vert_nors)[3],
+#ifdef USE_NONMANIFOLD_WORKAROUND
+ BLI_bitmap *edge_tmp_tag
+#endif
+)
{
- int i, verts_num, edges_num, polys_num;
- MPoly *mpoly, *mp;
- MLoop *mloop, *ml;
- MEdge *medge, *ed;
+ int i;
- verts_num = mesh->totvert;
- edges_num = mesh->totedge;
- polys_num = mesh->totpoly;
- mpoly = mesh->mpoly;
- medge = mesh->medge;
- mloop = mesh->mloop;
+ const int verts_num = mesh->totvert;
+ const int edges_num = mesh->totedge;
+ const int polys_num = mesh->totpoly;
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
+ const MLoop *mloop = BKE_mesh_loops(mesh);
+ const MEdge *medge = BKE_mesh_edges(mesh);
- /* we don't want to overwrite any referenced layers */
-
- /* Doesn't work here! */
-#if 0
- mv = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, verts_num);
- cddm->mvert = mv;
-#endif
-
- mp = mpoly;
+ const MPoly *mp = mpoly;
{
EdgeFaceRef *edge_ref_array = MEM_calloc_arrayN(
@@ -87,7 +82,7 @@ static void mesh_calc_hq_normal(Mesh *mesh, const float (*poly_nors)[3], float (
for (i = 0; i < polys_num; i++, mp++) {
int j;
- ml = mloop + mp->loopstart;
+ const MLoop *ml = mloop + mp->loopstart;
for (j = 0; j < mp->totloop; j++, ml++) {
/* --- add edge ref to face --- */
@@ -103,13 +98,14 @@ static void mesh_calc_hq_normal(Mesh *mesh, const float (*poly_nors)[3], float (
/* 3+ faces using an edge, we can't handle this usefully */
edge_ref->p1 = edge_ref->p2 = -1;
#ifdef USE_NONMANIFOLD_WORKAROUND
- medge[ml->e].flag |= ME_EDGE_TMP_TAG;
+ BLI_BITMAP_ENABLE(edge_tmp_tag, ml->e);
#endif
}
/* --- done --- */
}
}
+ const MEdge *ed;
for (i = 0, ed = medge, edge_ref = edge_ref_array; i < edges_num; i++, ed++, edge_ref++) {
/* Get the edge vert indices, and edge value (the face indices that use it) */
@@ -160,10 +156,6 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
Mesh *result;
const SolidifyModifierData *smd = (SolidifyModifierData *)md;
- MVert *mv, *mvert, *orig_mvert;
- MEdge *ed, *medge, *orig_medge;
- MLoop *ml, *mloop, *orig_mloop;
- MPoly *mp, *mpoly, *orig_mpoly;
const uint verts_num = (uint)mesh->totvert;
const uint edges_num = (uint)mesh->totedge;
const uint polys_num = (uint)mesh->totpoly;
@@ -210,7 +202,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
const bool do_shell = !(do_rim && (smd->flag & MOD_SOLIDIFY_NOSHELL) != 0);
/* weights */
- MDeformVert *dvert;
+ const MDeformVert *dvert;
const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0;
int defgrp_index;
const int shell_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->shell_defgrp_name);
@@ -223,10 +215,10 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
MOD_get_vgroup(ctx->object, mesh, smd->defgrp_name, &dvert, &defgrp_index);
- orig_mvert = mesh->mvert;
- orig_medge = mesh->medge;
- orig_mloop = mesh->mloop;
- orig_mpoly = mesh->mpoly;
+ const MVert *orig_mvert = BKE_mesh_verts(mesh);
+ const MEdge *orig_medge = BKE_mesh_edges(mesh);
+ const MPoly *orig_mpoly = BKE_mesh_polys(mesh);
+ const MLoop *orig_mloop = BKE_mesh_loops(mesh);
if (need_poly_normals) {
/* calculate only face normals */
@@ -256,16 +248,17 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
copy_vn_i(edge_users, edges_num, INVALID_UNUSED);
#endif
+ const MEdge *ed;
for (eidx = 0, ed = orig_medge; eidx < edges_num; eidx++, ed++) {
edge_users[eidx] = INVALID_UNUSED;
}
+ const MPoly *mp;
for (i = 0, mp = orig_mpoly; i < polys_num; i++, mp++) {
- MLoop *ml_prev;
int j;
- ml = orig_mloop + mp->loopstart;
- ml_prev = ml + (mp->totloop - 1);
+ const MLoop *ml = orig_mloop + mp->loopstart;
+ const MLoop *ml_prev = ml + (mp->totloop - 1);
for (j = 0; j < mp->totloop; j++, ml++) {
/* add edge user */
@@ -319,9 +312,20 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
BLI_assert(newEdges == 0);
}
+#ifdef USE_NONMANIFOLD_WORKAROUND
+ BLI_bitmap *edge_tmp_tag = BLI_BITMAP_NEW(mesh->totedge, __func__);
+#endif
+
if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) {
vert_nors = MEM_calloc_arrayN(verts_num, sizeof(float[3]), "mod_solid_vno_hq");
- mesh_calc_hq_normal(mesh, poly_nors, vert_nors);
+ mesh_calc_hq_normal(mesh,
+ poly_nors,
+ vert_nors
+#ifdef USE_NONMANIFOLD_WORKAROUND
+ ,
+ edge_tmp_tag
+#endif
+ );
}
result = BKE_mesh_new_nomain_from_template(mesh,
@@ -331,15 +335,10 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
(int)((loops_num * stride) + newLoops),
(int)((polys_num * stride) + newPolys));
- mpoly = result->mpoly;
- mloop = result->mloop;
- medge = result->medge;
- mvert = result->mvert;
-
- if (do_bevel_convex) {
- /* Make sure bweight is enabled. */
- result->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
- }
+ MVert *mvert = BKE_mesh_verts_for_write(result);
+ MEdge *medge = BKE_mesh_edges_for_write(result);
+ MPoly *mpoly = BKE_mesh_polys_for_write(result);
+ MLoop *mloop = BKE_mesh_loops_for_write(result);
if (do_shell) {
CustomData_copy_data(&mesh->vdata, &result->vdata, 0, 0, (int)verts_num);
@@ -388,6 +387,12 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
CustomData_copy_data(&mesh->pdata, &result->pdata, 0, 0, (int)polys_num);
}
+ float *result_edge_bweight = NULL;
+ if (do_bevel_convex) {
+ result_edge_bweight = CustomData_add_layer(
+ &result->edata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, result->totedge);
+ }
+
/* initializes: (i_end, do_shell_align, mv). */
#define INIT_VERT_ARRAY_OFFSETS(test) \
if (((ofs_new >= ofs_orig) == do_flip) == test) { \
@@ -408,12 +413,14 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
} \
(void)0
+ int *dst_material_index = BKE_mesh_material_indices_for_write(result);
+
/* flip normals */
if (do_shell) {
uint i;
- mp = mpoly + polys_num;
+ MPoly *mp = mpoly + polys_num;
for (i = 0; i < mesh->totpoly; i++, mp++) {
const int loop_end = mp->totloop - 1;
MLoop *ml2;
@@ -445,8 +452,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
#endif
if (mat_ofs) {
- mp->mat_nr += mat_ofs;
- CLAMP(mp->mat_nr, 0, mat_nr_max);
+ dst_material_index[mp - mpoly] += mat_ofs;
+ CLAMP(dst_material_index[mp - mpoly], 0, mat_nr_max);
}
e = ml2[0].e;
@@ -463,6 +470,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
}
}
+ MEdge *ed;
for (i = 0, ed = medge + edges_num; i < edges_num; i++, ed++) {
ed->v1 += verts_num;
ed->v2 += verts_num;
@@ -511,15 +519,15 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
edge_user_pairs[eidx][0] = INVALID_UNUSED;
edge_user_pairs[eidx][1] = INVALID_UNUSED;
}
- mp = orig_mpoly;
+ const MPoly *mp = orig_mpoly;
for (uint i = 0; i < polys_num; i++, mp++) {
- ml = orig_mloop + mp->loopstart;
- MLoop *ml_prev = ml + (mp->totloop - 1);
+ const MLoop *ml = orig_mloop + mp->loopstart;
+ const MLoop *ml_prev = ml + (mp->totloop - 1);
for (uint j = 0; j < mp->totloop; j++, ml++) {
/* add edge user */
eidx = ml_prev->e;
- ed = orig_medge + eidx;
+ const MEdge *ed = orig_medge + eidx;
BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2));
char flip = (char)((ml_prev->v > ml->v) == (ed->v1 < ed->v2));
if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) {
@@ -532,7 +540,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
ml_prev = ml;
}
}
- ed = orig_medge;
+ const MEdge *ed = orig_medge;
float e[3];
for (uint i = 0; i < edges_num; i++, ed++) {
if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) &&
@@ -563,12 +571,13 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
ofs_new_vgroup = ofs_new;
+ MVert *mv;
INIT_VERT_ARRAY_OFFSETS(false);
for (i_orig = 0; i_orig < i_end; i_orig++, mv++) {
const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig];
if (dvert) {
- MDeformVert *dv = &dvert[i];
+ const MDeformVert *dv = &dvert[i];
if (defgrp_invert) {
ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
}
@@ -614,12 +623,13 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
ofs_new_vgroup = ofs_orig;
/* as above but swapped */
+ MVert *mv;
INIT_VERT_ARRAY_OFFSETS(true);
for (i_orig = 0; i_orig < i_end; i_orig++, mv++) {
const uint i = do_shell_align ? i_orig : new_vert_arr[i_orig];
if (dvert) {
- MDeformVert *dv = &dvert[i];
+ const MDeformVert *dv = &dvert[i];
if (defgrp_invert) {
ofs_new_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
}
@@ -662,20 +672,18 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
for (uint i = 0; i < edges_num; i++) {
if (edge_users[i] == INVALID_PAIR) {
float angle = edge_angs[i];
- medge[i].bweight = (char)clamp_i(
- (int)medge[i].bweight + (int)((angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
- clamp_f(bevel_convex, -1.0f, 0.0f)) *
- 255),
- 0,
- 255);
+ result_edge_bweight[i] = clamp_f(result_edge_bweight[i] +
+ (angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
+ clamp_f(bevel_convex, -1.0f, 0.0f)),
+ 0.0f,
+ 1.0f);
if (do_shell) {
- medge[i + edges_num].bweight = (char)clamp_i(
- (int)medge[i + edges_num].bweight +
- (int)((angle > M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
- clamp_f(bevel_convex, -1.0f, 0.0f)) *
- 255),
+ result_edge_bweight[i + edges_num] = clamp_f(
+ result_edge_bweight[i + edges_num] + (angle > M_PI ?
+ clamp_f(bevel_convex, 0.0f, 1.0f) :
+ clamp_f(bevel_convex, -1.0f, 0.0f)),
0,
- 255);
+ 1.0f);
}
}
}
@@ -705,11 +713,13 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
if (vert_nors == NULL) {
vert_nors = MEM_malloc_arrayN(verts_num, sizeof(float[3]), "mod_solid_vno");
+ const MVert *mv;
for (i = 0, mv = mvert; i < verts_num; i++, mv++) {
copy_v3_v3(vert_nors[i], mesh_vert_normals[i]);
}
}
+ const MPoly *mp;
for (i = 0, mp = mpoly; i < polys_num; i++, mp++) {
/* #BKE_mesh_calc_poly_angles logic is inlined here */
float nor_prev[3];
@@ -718,7 +728,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
int i_curr = mp->totloop - 1;
int i_next = 0;
- ml = &mloop[mp->loopstart];
+ const MLoop *ml = &mloop[mp->loopstart];
sub_v3_v3v3(nor_prev, mvert[ml[i_curr - 1].v].co, mvert[ml[i_curr].v].co);
normalize_v3(nor_prev);
@@ -740,8 +750,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
#ifdef USE_NONMANIFOLD_WORKAROUND
/* skip 3+ face user edges */
if ((check_non_manifold == false) ||
- LIKELY(((orig_medge[ml[i_curr].e].flag & ME_EDGE_TMP_TAG) == 0) &&
- ((orig_medge[ml[i_next].e].flag & ME_EDGE_TMP_TAG) == 0))) {
+ LIKELY(!BLI_BITMAP_TEST(edge_tmp_tag, ml[i_curr].e) &&
+ !BLI_BITMAP_TEST(edge_tmp_tag, ml[i_next].e))) {
vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], poly_nors[i]) *
angle;
}
@@ -762,7 +772,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
/* vertex group support */
if (dvert) {
- MDeformVert *dv = dvert;
+ const MDeformVert *dv = dvert;
float scalar;
if (defgrp_invert) {
@@ -805,13 +815,13 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
edge_user_pairs[eidx][1] = INVALID_UNUSED;
}
for (i = 0, mp = orig_mpoly; i < polys_num; i++, mp++) {
- ml = orig_mloop + mp->loopstart;
- MLoop *ml_prev = ml + (mp->totloop - 1);
+ const MLoop *ml = orig_mloop + mp->loopstart;
+ const MLoop *ml_prev = ml + (mp->totloop - 1);
for (int j = 0; j < mp->totloop; j++, ml++) {
/* add edge user */
eidx = ml_prev->e;
- ed = orig_medge + eidx;
+ const MEdge *ed = orig_medge + eidx;
BLI_assert(ELEM(ml_prev->v, ed->v1, ed->v2) && ELEM(ml->v, ed->v1, ed->v2));
char flip = (char)((ml_prev->v > ml->v) == (ed->v1 < ed->v2));
if (edge_user_pairs[eidx][flip] == INVALID_UNUSED) {
@@ -824,7 +834,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
ml_prev = ml;
}
}
- ed = orig_medge;
+ const MEdge *ed = orig_medge;
float e[3];
for (i = 0; i < edges_num; i++, ed++) {
if (!ELEM(edge_user_pairs[i][0], INVALID_UNUSED, INVALID_PAIR) &&
@@ -889,20 +899,17 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
for (i = 0; i < edges_num; i++) {
if (edge_users[i] == INVALID_PAIR) {
float angle = edge_angs[i];
- medge[i].bweight = (char)clamp_i(
- (int)medge[i].bweight + (int)((angle < M_PI ? clamp_f(bevel_convex, 0, 1) :
- clamp_f(bevel_convex, -1, 0)) *
- 255),
- 0,
- 255);
+ result_edge_bweight[i] = clamp_f(result_edge_bweight[i] +
+ (angle < M_PI ? clamp_f(bevel_convex, 0.0f, 1.0f) :
+ clamp_f(bevel_convex, -1.0f, 0.0f)),
+ 0.0f,
+ 1.0f);
if (do_shell) {
- medge[i + edges_num].bweight = (char)clamp_i(
- (int)medge[i + edges_num].bweight +
- (int)((angle > M_PI ? clamp_f(bevel_convex, 0, 1) :
- clamp_f(bevel_convex, -1, 0)) *
- 255),
- 0,
- 255);
+ result_edge_bweight[i + edges_num] = clamp_f(
+ result_edge_bweight[i + edges_num] +
+ (angle > M_PI ? clamp_f(bevel_convex, 0, 1) : clamp_f(bevel_convex, -1, 0)),
+ 0.0f,
+ 1.0f);
}
}
}
@@ -919,6 +926,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
uint i_orig, i_end;
bool do_shell_align;
+ MVert *mv;
INIT_VERT_ARRAY_OFFSETS(false);
for (i_orig = 0; i_orig < i_end; i_orig++, mv++) {
@@ -935,6 +943,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
bool do_shell_align;
/* same as above but swapped, intentional use of 'ofs_new' */
+ MVert *mv;
INIT_VERT_ARRAY_OFFSETS(true);
for (i_orig = 0; i_orig < i_end; i_orig++, mv++) {
@@ -949,6 +958,10 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
MEM_freeN(vert_angles);
}
+#ifdef USE_NONMANIFOLD_WORKAROUND
+ MEM_SAFE_FREE(edge_tmp_tag);
+#endif
+
if (vert_nors) {
MEM_freeN(vert_nors);
}
@@ -960,7 +973,6 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
else if (do_shell) {
uint i;
/* flip vertex normals for copied verts */
- mv = mvert + verts_num;
for (i = 0; i < verts_num; i++) {
negate_v3((float *)mesh_vert_normals[i]);
}
@@ -968,22 +980,15 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
/* Add vertex weights for rim and shell vgroups. */
if (shell_defgrp_index != -1 || rim_defgrp_index != -1) {
- dvert = CustomData_duplicate_referenced_layer(&result->vdata, CD_MDEFORMVERT, result->totvert);
- /* If no vertices were ever added to an object's vgroup, dvert might be NULL. */
- if (dvert == NULL) {
- /* Add a valid data layer! */
- dvert = CustomData_add_layer(
- &result->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, result->totvert);
- }
+ MDeformVert *dst_dvert = BKE_mesh_deform_verts_for_write(result);
+
/* Ultimate security check. */
- if (dvert != NULL) {
- result->dvert = dvert;
+ if (dst_dvert != NULL) {
if (rim_defgrp_index != -1) {
for (uint i = 0; i < rimVerts; i++) {
- BKE_defvert_ensure_index(&result->dvert[new_vert_arr[i]], rim_defgrp_index)->weight =
- 1.0f;
- BKE_defvert_ensure_index(&result->dvert[(do_shell ? new_vert_arr[i] : i) + verts_num],
+ BKE_defvert_ensure_index(&dst_dvert[new_vert_arr[i]], rim_defgrp_index)->weight = 1.0f;
+ BKE_defvert_ensure_index(&dst_dvert[(do_shell ? new_vert_arr[i] : i) + verts_num],
rim_defgrp_index)
->weight = 1.0f;
}
@@ -991,7 +996,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
if (shell_defgrp_index != -1) {
for (uint i = verts_num; i < result->totvert; i++) {
- BKE_defvert_ensure_index(&result->dvert[i], shell_defgrp_index)->weight = 1.0f;
+ BKE_defvert_ensure_index(&dst_dvert[i], shell_defgrp_index)->weight = 1.0f;
}
}
}
@@ -999,9 +1004,9 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
if (do_rim) {
uint i;
- /* NOTE(campbell): Unfortunately re-calculate the normals for the new edge faces is necessary.
- * This could be done in many ways, but probably the quickest way
- * is to calculate the average normals for side faces only.
+ /* NOTE(@campbellbarton): Unfortunately re-calculate the normals for the new edge
+ * faces is necessary. This could be done in many ways, but probably the quickest
+ * way is to calculate the average normals for side faces only.
* Then blend them with the normals of the edge verts.
*
* At the moment its easiest to allocate an entire array for every vertex,
@@ -1034,7 +1039,7 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
/* add faces & edges */
origindex_edge = CustomData_get_layer(&result->edata, CD_ORIGINDEX);
orig_ed = (origindex_edge) ? &origindex_edge[(edges_num * stride) + newEdges] : NULL;
- ed = &medge[(edges_num * stride) + newEdges]; /* start after copied edges */
+ MEdge *ed = &medge[(edges_num * stride) + newEdges]; /* start after copied edges */
for (i = 0; i < rimVerts; i++, ed++) {
ed->v1 = new_vert_arr[i];
ed->v2 = (do_shell ? new_vert_arr[i] : i) + verts_num;
@@ -1051,8 +1056,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
}
/* faces */
- mp = mpoly + (polys_num * stride);
- ml = mloop + (loops_num * stride);
+ MPoly *mp = mpoly + (polys_num * stride);
+ MLoop *ml = mloop + (loops_num * stride);
j = 0;
for (i = 0; i < newPolys; i++, mp++) {
uint eidx = new_edge_arr[i];
@@ -1130,8 +1135,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex
/* use the next material index if option enabled */
if (mat_ofs_rim) {
- mp->mat_nr += mat_ofs_rim;
- CLAMP(mp->mat_nr, 0, mat_nr_max);
+ dst_material_index[mp - mpoly] += mat_ofs_rim;
+ CLAMP(dst_material_index[mp - mpoly], 0, mat_nr_max);
}
if (crease_outer) {
/* crease += crease_outer; without wrapping */
diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
index 8a5b600974c..d3aff5c58c5 100644
--- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
+++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c
@@ -76,7 +76,7 @@ static float clamp_nonzero(const float value, const float epsilon)
/* Data structures for manifold solidify. */
typedef struct NewFaceRef {
- MPoly *face;
+ const MPoly *face;
uint index;
bool reversed;
struct NewEdgeRef **link_edges;
@@ -137,10 +137,6 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
Mesh *result;
const SolidifyModifierData *smd = (SolidifyModifierData *)md;
- MVert *mv, *mvert, *orig_mvert;
- MEdge *ed, *medge, *orig_medge;
- MLoop *ml, *mloop, *orig_mloop;
- MPoly *mp, *mpoly, *orig_mpoly;
const uint verts_num = (uint)mesh->totvert;
const uint edges_num = (uint)mesh->totedge;
const uint polys_num = (uint)mesh->totpoly;
@@ -178,7 +174,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
const float bevel_convex = smd->bevel_convex;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
const bool defgrp_invert = (smd->flag & MOD_SOLIDIFY_VGROUP_INV) != 0;
int defgrp_index;
const int shell_defgrp_index = BKE_id_defgroup_name_index(&mesh->id, smd->shell_defgrp_name);
@@ -188,10 +184,14 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
const bool do_flat_faces = dvert && (smd->flag & MOD_SOLIDIFY_NONMANIFOLD_FLAT_FACES);
- orig_mvert = mesh->mvert;
- orig_medge = mesh->medge;
- orig_mloop = mesh->mloop;
- orig_mpoly = mesh->mpoly;
+ const MVert *orig_mvert = BKE_mesh_verts(mesh);
+ const MEdge *orig_medge = BKE_mesh_edges(mesh);
+ const MPoly *orig_mpoly = BKE_mesh_polys(mesh);
+ const MLoop *orig_mloop = BKE_mesh_loops(mesh);
+
+ /* These might be null. */
+ const float *orig_vert_bweight = CustomData_get_layer(&mesh->vdata, CD_BWEIGHT);
+ const float *orig_edge_bweight = CustomData_get_layer(&mesh->edata, CD_BWEIGHT);
uint new_verts_num = 0;
uint new_edges_num = 0;
@@ -213,11 +213,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
uint largest_ngon = 3;
/* Calculate face to #NewFaceRef map. */
{
- mp = orig_mpoly;
+ const MPoly *mp = orig_mpoly;
for (uint i = 0; i < polys_num; i++, mp++) {
/* Make normals for faces without area (should really be avoided though). */
if (len_squared_v3(poly_nors[i]) < 0.5f) {
- MEdge *e = orig_medge + orig_mloop[mp->loopstart].e;
+ const MEdge *e = orig_medge + orig_mloop[mp->loopstart].e;
float edgedir[3];
sub_v3_v3v3(edgedir, orig_mvert[e->v2].co, orig_mvert[e->v1].co);
if (fabsf(edgedir[2]) < fabsf(edgedir[1])) {
@@ -254,9 +254,9 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
edges_num, sizeof(*edge_adj_faces_len), "edge_adj_faces_len in solidify");
/* Count for each edge how many faces it has adjacent. */
{
- mp = orig_mpoly;
+ const MPoly *mp = orig_mpoly;
for (uint i = 0; i < polys_num; i++, mp++) {
- ml = orig_mloop + mp->loopstart;
+ const MLoop *ml = orig_mloop + mp->loopstart;
for (uint j = 0; j < mp->totloop; j++, ml++) {
edge_adj_faces_len[ml->e]++;
}
@@ -304,9 +304,9 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
/* Create link_faces for edges. */
{
- mp = orig_mpoly;
+ const MPoly *mp = orig_mpoly;
for (uint i = 0; i < polys_num; i++, mp++) {
- ml = orig_mloop + mp->loopstart;
+ const MLoop *ml = orig_mloop + mp->loopstart;
for (uint j = 0; j < mp->totloop; j++, ml++) {
const uint edge = ml->e;
const bool reversed = orig_medge[edge].v2 != ml->v;
@@ -353,7 +353,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
uint *combined_verts = MEM_calloc_arrayN(
verts_num, sizeof(*combined_verts), "combined_verts in solidify");
- ed = orig_medge;
+ const MEdge *ed = orig_medge;
for (uint i = 0; i < edges_num; i++, ed++) {
if (edge_adj_faces_len[i] > 0) {
uint v1 = vm[ed->v1];
@@ -377,7 +377,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
if (k != i && edge_adj_faces_len[k] > 0 &&
(ELEM(vm[orig_medge[k].v1], v1, v2) != ELEM(vm[orig_medge[k].v2], v1, v2))) {
for (uint j = 0; j < edge_adj_faces[k]->faces_len && can_merge; j++) {
- mp = orig_mpoly + edge_adj_faces[k]->faces[j];
+ const MPoly *mp = orig_mpoly + edge_adj_faces[k]->faces[j];
uint changes = 0;
int cur = mp->totloop - 1;
for (int next = 0; next < mp->totloop && changes <= 2; next++) {
@@ -473,7 +473,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
/* Create vert_adj_edges for verts. */
{
- ed = orig_medge;
+ const MEdge *ed = orig_medge;
for (uint i = 0; i < edges_num; i++, ed++) {
if (edge_adj_faces_len[i] > 0) {
const uint vs[2] = {vm[ed->v1], vm[ed->v2]};
@@ -592,7 +592,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
/* Filter duplicate polys. */
{
- ed = orig_medge;
+ const MEdge *ed = orig_medge;
/* Iterate over edges and only check the faces around an edge for duplicates
* (performance optimization). */
for (uint i = 0; i < edges_num; i++, ed++) {
@@ -615,7 +615,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
/* Find first face first loop vert in second face loops. */
const int k_loopstart = orig_mpoly[adj_faces->faces[k]].loopstart;
int l;
- ml = orig_mloop + k_loopstart;
+ const MLoop *ml = orig_mloop + k_loopstart;
for (l = 0; l < totloop && vm[ml->v] != j_first_v; l++, ml++) {
/* Pass. */
}
@@ -703,7 +703,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
/* Create #NewEdgeRef array. */
{
- ed = orig_medge;
+ const MEdge *ed = orig_medge;
for (uint i = 0; i < edges_num; i++, ed++) {
const uint v1 = vm[ed->v1];
const uint v2 = vm[ed->v2];
@@ -848,7 +848,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
new_edges[j] = edge_data;
for (uint k = 0; k < 2; k++) {
if (faces[k] != NULL) {
- ml = orig_mloop + faces[k]->face->loopstart;
+ const MLoop *ml = orig_mloop + faces[k]->face->loopstart;
for (int l = 0; l < faces[k]->face->totloop; l++, ml++) {
if (edge_adj_faces[ml->e] == edge_adj_faces[i]) {
if (ml->e != i && orig_edge_data_arr[ml->e] == NULL) {
@@ -1377,13 +1377,13 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
if (do_flat_faces) {
face_weight = MEM_malloc_arrayN(polys_num, sizeof(*face_weight), "face_weight in solidify");
- mp = orig_mpoly;
+ const MPoly *mp = orig_mpoly;
for (uint i = 0; i < polys_num; i++, mp++) {
float scalar_vgroup = 1.0f;
int loopend = mp->loopstart + mp->totloop;
- ml = orig_mloop + mp->loopstart;
+ const MLoop *ml = orig_mloop + mp->loopstart;
for (int j = mp->loopstart; j < loopend; j++, ml++) {
- MDeformVert *dv = &dvert[ml->v];
+ const MDeformVert *dv = &dvert[ml->v];
if (defgrp_invert) {
scalar_vgroup = min_ff(1.0f - BKE_defvert_find_weight(dv, defgrp_index),
scalar_vgroup);
@@ -1397,7 +1397,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
}
- mv = orig_mvert;
+ const MVert *mv = orig_mvert;
gs_ptr = orig_vert_groups_arr;
for (uint i = 0; i < verts_num; i++, mv++, gs_ptr++) {
if (*gs_ptr) {
@@ -1655,9 +1655,9 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
if (smd->nonmanifold_offset_mode ==
MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_EVEN) {
- MLoop *ml_next = orig_mloop + face->face->loopstart;
- ml = ml_next + (face->face->totloop - 1);
- MLoop *ml_prev = ml - 1;
+ const MLoop *ml_next = orig_mloop + face->face->loopstart;
+ const MLoop *ml = ml_next + (face->face->totloop - 1);
+ const MLoop *ml_prev = ml - 1;
for (int m = 0; m < face->face->totloop && vm[ml->v] != i;
m++, ml_next++) {
ml_prev = ml;
@@ -1766,7 +1766,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
float tmp[3];
int k;
for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) {
- MEdge *e = orig_medge + (*edge_ptr)->old_edge;
+ const MEdge *e = orig_medge + (*edge_ptr)->old_edge;
sub_v3_v3v3(tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]);
add_v3_v3(move_nor, tmp);
}
@@ -1781,8 +1781,8 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
if (!disable_boundary_fix) {
/* Constraint normal, nor * constr_nor == 0 after this fix. */
float constr_nor[3];
- MEdge *e0_edge = orig_medge + g->edges[0]->old_edge;
- MEdge *e1_edge = orig_medge + g->edges[g->edges_len - 1]->old_edge;
+ const MEdge *e0_edge = orig_medge + g->edges[0]->old_edge;
+ const MEdge *e1_edge = orig_medge + g->edges[g->edges_len - 1]->old_edge;
float e0[3];
float e1[3];
sub_v3_v3v3(e0,
@@ -1831,7 +1831,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
float scalar_vgroup = 1;
/* Use vertex group. */
if (dvert && !do_flat_faces) {
- MDeformVert *dv = &dvert[i];
+ const MDeformVert *dv = &dvert[i];
if (defgrp_invert) {
scalar_vgroup = 1.0f - BKE_defvert_find_weight(dv, defgrp_index);
}
@@ -1961,29 +1961,24 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
(int)(new_loops_num),
(int)(new_polys_num));
- mpoly = result->mpoly;
- mloop = result->mloop;
- medge = result->medge;
- mvert = result->mvert;
+ MVert *mvert = BKE_mesh_verts_for_write(result);
+ MEdge *medge = BKE_mesh_edges_for_write(result);
+ MPoly *mpoly = BKE_mesh_polys_for_write(result);
+ MLoop *mloop = BKE_mesh_loops_for_write(result);
int *origindex_edge = CustomData_get_layer(&result->edata, CD_ORIGINDEX);
int *origindex_poly = CustomData_get_layer(&result->pdata, CD_ORIGINDEX);
- if (bevel_convex != 0.0f || (result->cd_flag & ME_CDFLAG_VERT_BWEIGHT) != 0) {
- /* make sure bweight is enabled */
- result->cd_flag |= ME_CDFLAG_EDGE_BWEIGHT;
+ float *result_edge_bweight = CustomData_get_layer(&result->edata, CD_BWEIGHT);
+ if (bevel_convex != 0.0f || orig_vert_bweight != NULL) {
+ result_edge_bweight = CustomData_add_layer(
+ &result->edata, CD_BWEIGHT, CD_SET_DEFAULT, NULL, result->totedge);
}
/* Checks that result has dvert data. */
+ MDeformVert *dst_dvert = NULL;
if (shell_defgrp_index != -1 || rim_defgrp_index != -1) {
- dvert = CustomData_duplicate_referenced_layer(&result->vdata, CD_MDEFORMVERT, result->totvert);
- /* If no vertices were ever added to an object's vgroup, dvert might be NULL. */
- if (dvert == NULL) {
- /* Add a valid data layer! */
- dvert = CustomData_add_layer(
- &result->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, result->totvert);
- }
- result->dvert = dvert;
+ dst_dvert = BKE_mesh_deform_verts_for_write(result);
}
/* Get vertex crease layer and ensure edge creases are active if vertex creases are found, since
@@ -2048,17 +2043,18 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
medge[insert].v2 = v2;
medge[insert].flag = orig_medge[(*l)->old_edge].flag | ME_EDGEDRAW | ME_EDGERENDER;
medge[insert].crease = orig_medge[(*l)->old_edge].crease;
- medge[insert].bweight = orig_medge[(*l)->old_edge].bweight;
+ if (result_edge_bweight) {
+ result_edge_bweight[insert] = orig_edge_bweight[(*l)->old_edge];
+ }
if (bevel_convex != 0.0f && (*l)->faces[1] != NULL) {
- medge[insert].bweight = (char)clamp_i(
- (int)medge[insert].bweight + (int)(((*l)->angle > M_PI + FLT_EPSILON ?
- clamp_f(bevel_convex, 0.0f, 1.0f) :
- ((*l)->angle < M_PI - FLT_EPSILON ?
- clamp_f(bevel_convex, -1.0f, 0.0f) :
- 0)) *
- 255),
- 0,
- 255);
+ result_edge_bweight[insert] = clamp_f(
+ result_edge_bweight[insert] +
+ ((*l)->angle > M_PI + FLT_EPSILON ?
+ clamp_f(bevel_convex, 0.0f, 1.0f) :
+ ((*l)->angle < M_PI - FLT_EPSILON ? clamp_f(bevel_convex, -1.0f, 0.0f) :
+ 0)),
+ 0.0f,
+ 1.0f);
}
(*l)->new_edge = insert;
}
@@ -2108,10 +2104,13 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
#endif
+ const int *src_material_index = BKE_mesh_material_indices(mesh);
+ int *dst_material_index = BKE_mesh_material_indices_for_write(result);
+
/* Make boundary edges/faces. */
{
gs_ptr = orig_vert_groups_arr;
- mv = orig_mvert;
+ const MVert *mv = orig_mvert;
for (uint i = 0; i < verts_num; i++, gs_ptr++, mv++) {
EdgeGroup *gs = *gs_ptr;
if (gs) {
@@ -2120,13 +2119,14 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
EdgeGroup *last_g = NULL;
EdgeGroup *first_g = NULL;
char mv_crease = vertex_crease ? (char)(vertex_crease[i] * 255.0f) : 0;
+ float mv_bweight = orig_vert_bweight ? orig_vert_bweight[i] : 0.0f;
/* Data calculation cache. */
char max_crease;
char last_max_crease = 0;
char first_max_crease = 0;
- char max_bweight;
- char last_max_bweight = 0;
- char first_max_bweight = 0;
+ float max_bweight;
+ float last_max_bweight = 0.0f;
+ float first_max_bweight = 0.0f;
short flag;
short last_flag = 0;
short first_flag = 0;
@@ -2144,25 +2144,29 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
else {
for (uint k = 1; k < g->edges_len - 1; k++) {
- ed = orig_medge + g->edges[k]->old_edge;
+ const MEdge *ed = orig_medge + g->edges[k]->old_edge;
if (ed->crease > max_crease) {
max_crease = ed->crease;
}
if (g->edges[k]->new_edge != MOD_SOLIDIFY_EMPTY_TAG) {
- char bweight = medge[g->edges[k]->new_edge].bweight;
- if (bweight > max_bweight) {
- max_bweight = bweight;
+ if (result_edge_bweight) {
+ float bweight = result_edge_bweight[g->edges[k]->new_edge];
+ if (bweight > max_bweight) {
+ max_bweight = bweight;
+ }
}
}
flag |= ed->flag;
}
}
- const char bweight_open_edge = min_cc(
- orig_medge[g->edges[0]->old_edge].bweight,
- orig_medge[g->edges[g->edges_len - 1]->old_edge].bweight);
+ const float bweight_open_edge =
+ orig_edge_bweight ?
+ min_ff(orig_edge_bweight[g->edges[0]->old_edge],
+ orig_edge_bweight[g->edges[g->edges_len - 1]->old_edge]) :
+ 0.0f;
if (bweight_open_edge > 0) {
- max_bweight = min_cc(bweight_open_edge, max_bweight);
+ max_bweight = min_ff(bweight_open_edge, max_bweight);
}
else {
if (bevel_convex < 0.0f) {
@@ -2190,8 +2194,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
medge[edge_index].flag = ME_EDGEDRAW | ME_EDGERENDER |
((last_flag | flag) & (ME_SEAM | ME_SHARP));
medge[edge_index].crease = max_cc(mv_crease, min_cc(last_max_crease, max_crease));
- medge[edge_index++].bweight = max_cc(mv->bweight,
- min_cc(last_max_bweight, max_bweight));
+ if (result_edge_bweight) {
+ result_edge_bweight[edge_index] = max_ff(mv_bweight,
+ min_ff(last_max_bweight, max_bweight));
+ }
+ edge_index++;
}
last_g = g;
last_max_crease = max_crease;
@@ -2219,12 +2226,15 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
((last_flag | first_flag) & (ME_SEAM | ME_SHARP));
medge[edge_index].crease = max_cc(mv_crease,
min_cc(last_max_crease, first_max_crease));
- medge[edge_index++].bweight = max_cc(mv->bweight,
- min_cc(last_max_bweight, first_max_bweight));
+ if (result_edge_bweight) {
+ result_edge_bweight[edge_index] = max_ff(
+ mv_bweight, min_ff(last_max_bweight, first_max_bweight));
+ }
+ edge_index++;
/* Loop data. */
int *loops = MEM_malloc_arrayN(j, sizeof(*loops), "loops in solidify");
- /* The #mat_nr is from consensus. */
+ /* The result material index is from consensus. */
short most_mat_nr = 0;
uint most_mat_nr_face = 0;
uint most_mat_nr_count = 0;
@@ -2235,16 +2245,20 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
for (EdgeGroup *g3 = g2; g3->valid && k < j; g3++) {
if ((do_rim && !g3->is_orig_closed) || (do_shell && g3->split)) {
/* Check both far ends in terms of faces of an edge group. */
- if (g3->edges[0]->faces[0]->face->mat_nr == l) {
+ if ((src_material_index ? src_material_index[g3->edges[0]->faces[0]->index] :
+ 0) == l) {
face = g3->edges[0]->faces[0]->index;
count++;
}
NewEdgeRef *le = g3->edges[g3->edges_len - 1];
- if (le->faces[1] && le->faces[1]->face->mat_nr == l) {
+ if (le->faces[1] &&
+ (src_material_index ? src_material_index[le->faces[1]->index] : 0) == l) {
face = le->faces[1]->index;
count++;
}
- else if (!le->faces[1] && le->faces[0]->face->mat_nr == l) {
+ else if (!le->faces[1] &&
+ (src_material_index ? src_material_index[le->faces[0]->index] : 0) ==
+ l) {
face = le->faces[0]->index;
count++;
}
@@ -2264,16 +2278,16 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
mpoly[poly_index].loopstart = (int)loop_index;
mpoly[poly_index].totloop = (int)j;
- mpoly[poly_index].mat_nr = most_mat_nr +
- (g->is_orig_closed || !do_rim ? 0 : mat_ofs_rim);
- CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max);
+ dst_material_index[poly_index] = most_mat_nr +
+ (g->is_orig_closed || !do_rim ? 0 : mat_ofs_rim);
+ CLAMP(dst_material_index[poly_index], 0, mat_nr_max);
mpoly[poly_index].flag = orig_mpoly[most_mat_nr_face].flag;
poly_index++;
for (uint k = 0; g2->valid && k < j; g2++) {
if ((do_rim && !g2->is_orig_closed) || (do_shell && g2->split)) {
- MPoly *face = g2->edges[0]->faces[0]->face;
- ml = orig_mloop + face->loopstart;
+ const MPoly *face = g2->edges[0]->faces[0]->face;
+ const MLoop *ml = orig_mloop + face->loopstart;
for (int l = 0; l < face->totloop; l++, ml++) {
if (vm[ml->v] == i) {
loops[k] = face->loopstart + l;
@@ -2334,19 +2348,21 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
continue;
}
- MPoly *face = (*new_edges)->faces[0]->face;
+ const uint orig_face_index = (*new_edges)->faces[0]->index;
+ const MPoly *face = (*new_edges)->faces[0]->face;
CustomData_copy_data(
&mesh->pdata, &result->pdata, (int)(*new_edges)->faces[0]->index, (int)poly_index, 1);
mpoly[poly_index].loopstart = (int)loop_index;
mpoly[poly_index].totloop = 4 - (int)(v1_singularity || v2_singularity);
- mpoly[poly_index].mat_nr = face->mat_nr + mat_ofs_rim;
- CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max);
+ dst_material_index[poly_index] =
+ (src_material_index ? src_material_index[orig_face_index] : 0) + mat_ofs_rim;
+ CLAMP(dst_material_index[poly_index], 0, mat_nr_max);
mpoly[poly_index].flag = face->flag;
poly_index++;
int loop1 = -1;
int loop2 = -1;
- ml = orig_mloop + face->loopstart;
+ const MLoop *ml = orig_mloop + face->loopstart;
const uint old_v1 = vm[orig_medge[edge1->old_edge].v1];
const uint old_v2 = vm[orig_medge[edge1->old_edge].v2];
for (uint j = 0; j < face->totloop; j++, ml++) {
@@ -2362,7 +2378,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
uint open_face_edge_index;
if (!do_flip) {
if (rim_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v1], rim_defgrp_index)
+ BKE_defvert_ensure_index(&dst_dvert[medge[edge1->new_edge].v1], rim_defgrp_index)
->weight = 1.0f;
}
CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1);
@@ -2372,7 +2388,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
if (!v2_singularity) {
open_face_edge_index = edge1->link_edge_groups[1]->open_face_edge;
if (rim_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v2], rim_defgrp_index)
+ BKE_defvert_ensure_index(&dst_dvert[medge[edge1->new_edge].v2], rim_defgrp_index)
->weight = 1.0f;
}
CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1);
@@ -2387,7 +2403,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
if (rim_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v2], rim_defgrp_index)
+ BKE_defvert_ensure_index(&dst_dvert[medge[edge2->new_edge].v2], rim_defgrp_index)
->weight = 1.0f;
}
CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1);
@@ -2397,7 +2413,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
if (!v1_singularity) {
open_face_edge_index = edge2->link_edge_groups[0]->open_face_edge;
if (rim_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v1], rim_defgrp_index)
+ BKE_defvert_ensure_index(&dst_dvert[medge[edge2->new_edge].v1], rim_defgrp_index)
->weight = 1.0f;
}
CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1);
@@ -2415,7 +2431,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
if (!v1_singularity) {
open_face_edge_index = edge1->link_edge_groups[0]->open_face_edge;
if (rim_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v1], rim_defgrp_index)
+ BKE_defvert_ensure_index(&dst_dvert[medge[edge1->new_edge].v1], rim_defgrp_index)
->weight = 1.0f;
}
CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1);
@@ -2430,7 +2446,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
if (rim_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v1], rim_defgrp_index)
+ BKE_defvert_ensure_index(&dst_dvert[medge[edge2->new_edge].v1], rim_defgrp_index)
->weight = 1.0f;
}
CustomData_copy_data(&mesh->ldata, &result->ldata, loop1, (int)loop_index, 1);
@@ -2440,7 +2456,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
if (!v2_singularity) {
open_face_edge_index = edge2->link_edge_groups[1]->open_face_edge;
if (rim_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[medge[edge2->new_edge].v2], rim_defgrp_index)
+ BKE_defvert_ensure_index(&dst_dvert[medge[edge2->new_edge].v2], rim_defgrp_index)
->weight = 1.0f;
}
CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1);
@@ -2455,7 +2471,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
}
if (rim_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[medge[edge1->new_edge].v2], rim_defgrp_index)
+ BKE_defvert_ensure_index(&dst_dvert[medge[edge1->new_edge].v2], rim_defgrp_index)
->weight = 1.0f;
}
CustomData_copy_data(&mesh->ldata, &result->ldata, loop2, (int)loop_index, 1);
@@ -2530,14 +2546,16 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
CustomData_copy_data(&mesh->pdata, &result->pdata, (int)(i / 2), (int)poly_index, 1);
mpoly[poly_index].loopstart = (int)loop_index;
mpoly[poly_index].totloop = (int)k;
- mpoly[poly_index].mat_nr = fr->face->mat_nr + (fr->reversed != do_flip ? mat_ofs : 0);
- CLAMP(mpoly[poly_index].mat_nr, 0, mat_nr_max);
+ dst_material_index[poly_index] = (src_material_index ? src_material_index[fr->index] :
+ 0) +
+ (fr->reversed != do_flip ? mat_ofs : 0);
+ CLAMP(dst_material_index[poly_index], 0, mat_nr_max);
mpoly[poly_index].flag = fr->face->flag;
if (fr->reversed != do_flip) {
for (int l = (int)k - 1; l >= 0; l--) {
if (shell_defgrp_index != -1) {
- BKE_defvert_ensure_index(&result->dvert[face_verts[l]], shell_defgrp_index)
- ->weight = 1.0f;
+ BKE_defvert_ensure_index(&dst_dvert[face_verts[l]], shell_defgrp_index)->weight =
+ 1.0f;
}
CustomData_copy_data(
&mesh->ldata, &result->ldata, (int)face_loops[l], (int)loop_index, 1);
diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c
index 23f447f2469..95bbc5ea8cc 100644
--- a/source/blender/modifiers/intern/MOD_subsurf.c
+++ b/source/blender/modifiers/intern/MOD_subsurf.c
@@ -61,9 +61,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SubsurfModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
SubsurfModifierData *smd = (SubsurfModifierData *)md;
if (smd->flags & eSubsurfModifierFlag_UseCustomNormals) {
@@ -472,7 +470,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
}
ModifierTypeInfo modifierType_Subsurf = {
- /* name */ "Subdivision",
+ /* name */ N_("Subdivision"),
/* structName */ "SubsurfModifierData",
/* structSize */ sizeof(SubsurfModifierData),
/* srna */ &RNA_SubsurfModifier,
diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c
index af1de77d8c9..c5e117635b5 100644
--- a/source/blender/modifiers/intern/MOD_surface.c
+++ b/source/blender/modifiers/intern/MOD_surface.c
@@ -114,7 +114,7 @@ static void deformVerts(ModifierData *md,
surmd->mesh = (Mesh *)BKE_id_copy_ex(NULL, (ID *)mesh, NULL, LIB_ID_COPY_LOCALIZE);
}
else {
- surmd->mesh = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false, false);
+ surmd->mesh = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false);
}
if (!ctx->object->pd) {
@@ -125,7 +125,6 @@ static void deformVerts(ModifierData *md,
if (surmd->mesh) {
uint mesh_verts_num = 0, i = 0;
int init = 0;
- float *vec;
MVert *x, *v;
BKE_mesh_vert_coords_apply(surmd->mesh, vertexCos);
@@ -152,8 +151,9 @@ static void deformVerts(ModifierData *md,
}
/* convert to global coordinates and calculate velocity */
+ MVert *verts = BKE_mesh_verts_for_write(surmd->mesh);
for (i = 0, x = surmd->x, v = surmd->v; i < mesh_verts_num; i++, x++, v++) {
- vec = surmd->mesh->mvert[i].co;
+ float *vec = verts[i].co;
mul_m4_v3(ctx->object->obmat, vec);
if (init) {
@@ -211,7 +211,7 @@ static void blendRead(BlendDataReader *UNUSED(reader), ModifierData *md)
}
ModifierTypeInfo modifierType_Surface = {
- /* name */ "Surface",
+ /* name */ N_("Surface"),
/* structName */ "SurfaceModifierData",
/* structSize */ sizeof(SurfaceModifierData),
/* srna */ &RNA_SurfaceModifier,
diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c
index 20cc9b2392f..d9c0e0cc502 100644
--- a/source/blender/modifiers/intern/MOD_surfacedeform.c
+++ b/source/blender/modifiers/intern/MOD_surfacedeform.c
@@ -192,9 +192,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(smd, DNA_struct_default_get(SurfaceDeformModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
SurfaceDeformModifierData *smd = (SurfaceDeformModifierData *)md;
@@ -215,8 +213,7 @@ static void freeData(ModifierData *md)
MEM_SAFE_FREE(smd->verts[i].binds[j].vert_inds);
MEM_SAFE_FREE(smd->verts[i].binds[j].vert_weights);
}
-
- MEM_SAFE_FREE(smd->verts[i].binds);
+ MEM_freeN(smd->verts[i].binds);
}
}
@@ -1172,10 +1169,10 @@ static bool surfacedeformBind(Object *ob,
Mesh *mesh)
{
BVHTreeFromMesh treeData = {NULL};
- const MVert *mvert = target->mvert;
- const MPoly *mpoly = target->mpoly;
- const MEdge *medge = target->medge;
- const MLoop *mloop = target->mloop;
+ const MVert *mvert = BKE_mesh_verts(target);
+ const MPoly *mpoly = BKE_mesh_polys(target);
+ const MEdge *medge = BKE_mesh_edges(target);
+ const MLoop *mloop = BKE_mesh_loops(target);
uint tedges_num = target->totedge;
int adj_result;
SDefAdjacencyArray *vert_edges;
@@ -1237,7 +1234,7 @@ static bool surfacedeformBind(Object *ob,
smd_orig->target_polys_num = target_polys_num;
int defgrp_index;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
MOD_get_vgroup(ob, mesh, smd_orig->defgrp_name, &dvert, &defgrp_index);
const bool invert_vgroup = (smd_orig->flags & MOD_SDEF_INVERT_VGROUP) != 0;
const bool sparse_bind = (smd_orig->flags & MOD_SDEF_SPARSE_BIND) != 0;
@@ -1449,7 +1446,7 @@ static void surfacedeformModifier_do(ModifierData *md,
}
Object *ob_target = smd->target;
- target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target, false);
+ target = BKE_modifier_get_evaluated_mesh_from_evaluated_object(ob_target);
if (!target) {
BKE_modifier_set_error(ob, md, "No valid target mesh");
return;
@@ -1521,11 +1518,11 @@ static void surfacedeformModifier_do(ModifierData *md,
* added after the original ones. This covers typical case when target was at the subdivision
* level 0 and then subdivision was increased (i.e. for the render purposes). */
- BKE_modifier_set_error(ob,
- md,
- "Target vertices changed from %u to %u, continuing anyway",
- smd->target_verts_num,
- target_verts_num);
+ BKE_modifier_set_warning(ob,
+ md,
+ "Target vertices changed from %u to %u, continuing anyway",
+ smd->target_verts_num,
+ target_verts_num);
/* In theory we only need the `smd->verts_num` vertices in the `targetCos` for evaluation, but
* it is not currently possible to request a subset of coordinates: the API expects that the
@@ -1539,7 +1536,7 @@ static void surfacedeformModifier_do(ModifierData *md,
}
int defgrp_index;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
MOD_get_vgroup(ob, mesh, smd->defgrp_name, &dvert, &defgrp_index);
const bool invert_vgroup = (smd->flags & MOD_SDEF_INVERT_VGROUP) != 0;
@@ -1578,7 +1575,7 @@ static void deformVerts(ModifierData *md,
if (smd->defgrp_name[0] != '\0') {
/* Only need to use mesh_src when a vgroup is used. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
surfacedeformModifier_do(md, ctx, vertexCos, verts_num, ctx->object, mesh_src);
@@ -1600,7 +1597,12 @@ static void deformVertsEM(ModifierData *md,
if (smd->defgrp_name[0] != '\0') {
/* Only need to use mesh_src when a vgroup is used. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false);
+ }
+
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
+ if (mesh_src != NULL) {
+ BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
surfacedeformModifier_do(md, ctx, vertexCos, verts_num, ctx->object, mesh_src);
@@ -1741,7 +1743,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_SurfaceDeform = {
- /* name */ "SurfaceDeform",
+ /* name */ N_("SurfaceDeform"),
/* structName */ "SurfaceDeformModifierData",
/* structSize */ sizeof(SurfaceDeformModifierData),
/* srna */ &RNA_SurfaceDeformModifier,
diff --git a/source/blender/modifiers/intern/MOD_triangulate.c b/source/blender/modifiers/intern/MOD_triangulate.c
index f1e8ef5bf38..e8280bc9c97 100644
--- a/source/blender/modifiers/intern/MOD_triangulate.c
+++ b/source/blender/modifiers/intern/MOD_triangulate.c
@@ -82,7 +82,7 @@ static Mesh *triangulate_mesh(Mesh *mesh,
}
edges_num = result->totedge;
- me = result->medge;
+ me = BKE_mesh_edges_for_write(result);
/* force drawing of all edges (seems to be omitted in CDDM_from_bmesh) */
for (i = 0; i < edges_num; i++, me++) {
@@ -139,7 +139,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Triangulate = {
- /* name */ "Triangulate",
+ /* name */ N_("Triangulate"),
/* structName */ "TriangulateModifierData",
/* structSize */ sizeof(TriangulateModifierData),
/* srna */ &RNA_TriangulateModifier,
diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c
index 575182a846b..bc1a04d65ce 100644
--- a/source/blender/modifiers/intern/MOD_util.c
+++ b/source/blender/modifiers/intern/MOD_util.c
@@ -94,9 +94,9 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd,
/* UVs need special handling, since they come from faces */
if (texmapping == MOD_DISP_MAP_UV) {
if (CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) {
- MPoly *mpoly = mesh->mpoly;
- MPoly *mp;
- MLoop *mloop = mesh->mloop;
+ const MPoly *mpoly = BKE_mesh_polys(mesh);
+ const MPoly *mp;
+ const MLoop *mloop = BKE_mesh_loops(mesh);
BLI_bitmap *done = BLI_BITMAP_NEW(verts_num, __func__);
const int polys_num = mesh->totpoly;
char uvname[MAX_CUSTOMDATA_LAYER_NAME];
@@ -130,7 +130,7 @@ void MOD_get_texture_coords(MappingInfoModifierData *dmd,
texmapping = MOD_DISP_MAP_LOCAL;
}
- MVert *mv = mesh->mvert;
+ const MVert *mv = BKE_mesh_verts(mesh);
for (i = 0; i < verts_num; i++, mv++, r_texco++) {
switch (texmapping) {
case MOD_DISP_MAP_LOCAL:
@@ -169,7 +169,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob,
Mesh *mesh,
const float (*vertexCos)[3],
const int verts_num,
- const bool use_normals,
const bool use_orco)
{
if (mesh != NULL) {
@@ -217,14 +216,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob,
}
}
- /* TODO: Remove this "use_normals" argument, since the caller should retrieve normals afterwards
- * if necessary. */
- if (use_normals) {
- if (LIKELY(mesh)) {
- BKE_mesh_vertex_normals_ensure(mesh);
- }
- }
-
if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) {
BLI_assert(mesh->totvert == verts_num);
}
@@ -233,12 +224,12 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob,
}
void MOD_get_vgroup(
- Object *ob, struct Mesh *mesh, const char *name, MDeformVert **dvert, int *defgrp_index)
+ Object *ob, struct Mesh *mesh, const char *name, const MDeformVert **dvert, int *defgrp_index)
{
if (mesh) {
*defgrp_index = BKE_id_defgroup_name_index(&mesh->id, name);
if (*defgrp_index != -1) {
- *dvert = mesh->dvert;
+ *dvert = BKE_mesh_deform_verts(mesh);
}
else {
*dvert = NULL;
diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h
index b3b75898557..5f9bd97744b 100644
--- a/source/blender/modifiers/intern/MOD_util.h
+++ b/source/blender/modifiers/intern/MOD_util.h
@@ -42,13 +42,12 @@ struct Mesh *MOD_deform_mesh_eval_get(struct Object *ob,
struct Mesh *mesh,
const float (*vertexCos)[3],
int verts_num,
- bool use_normals,
bool use_orco);
void MOD_get_vgroup(struct Object *ob,
struct Mesh *mesh,
const char *name,
- struct MDeformVert **dvert,
+ const struct MDeformVert **dvert,
int *defgrp_index);
void MOD_depsgraph_update_object_bone_relation(struct DepsNodeHandle *node,
diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c
index d4d7ecef283..64e025ea56e 100644
--- a/source/blender/modifiers/intern/MOD_uvproject.c
+++ b/source/blender/modifiers/intern/MOD_uvproject.c
@@ -52,9 +52,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(umd, DNA_struct_default_get(UVProjectModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *UNUSED(md),
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *UNUSED(md), CustomData_MeshMasks *r_cddata_masks)
{
/* ask for UV coordinates */
r_cddata_masks->lmask |= CD_MASK_MLOOPUV;
@@ -80,7 +78,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
}
if (do_add_own_transform) {
- DEG_add_modifier_to_transform_relation(ctx->node, "UV Project Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "UV Project Modifier");
}
}
@@ -99,8 +97,7 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
float(*coords)[3], (*co)[3];
MLoopUV *mloop_uv;
int i, verts_num, polys_num, loops_num;
- MPoly *mpoly, *mp;
- MLoop *mloop;
+ const MPoly *mp;
Projector projectors[MOD_UVPROJECT_MAXPROJECTORS];
int projectors_num = 0;
char uvname[MAX_CUSTOMDATA_LAYER_NAME];
@@ -124,7 +121,7 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
* (e.g. if a preceding modifier could not preserve it). */
if (!CustomData_has_layer(&mesh->ldata, CD_MLOOPUV)) {
CustomData_add_layer_named(
- &mesh->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, mesh->totloop, umd->uvlayer_name);
+ &mesh->ldata, CD_MLOOPUV, CD_SET_DEFAULT, NULL, mesh->totloop, umd->uvlayer_name);
}
/* make sure we're using an existing layer */
@@ -205,17 +202,17 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
}
}
- mpoly = mesh->mpoly;
- mloop = mesh->mloop;
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
/* apply coords as UVs */
- for (i = 0, mp = mpoly; i < polys_num; i++, mp++) {
+ for (i = 0, mp = polys; i < polys_num; i++, mp++) {
if (projectors_num == 1) {
if (projectors[0].uci) {
uint fidx = mp->totloop - 1;
do {
uint lidx = mp->loopstart + fidx;
- uint vidx = mloop[lidx].v;
+ uint vidx = loops[lidx].v;
BLI_uvproject_from_camera(mloop_uv[lidx].uv, coords[vidx], projectors[0].uci);
} while (fidx--);
}
@@ -224,7 +221,7 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
uint fidx = mp->totloop - 1;
do {
uint lidx = mp->loopstart + fidx;
- uint vidx = mloop[lidx].v;
+ uint vidx = loops[lidx].v;
copy_v2_v2(mloop_uv[lidx].uv, coords[vidx]);
} while (fidx--);
}
@@ -238,7 +235,7 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
/* get the untransformed face normal */
BKE_mesh_calc_poly_normal_coords(
- mp, mloop + mp->loopstart, (const float(*)[3])coords, face_no);
+ mp, loops + mp->loopstart, (const float(*)[3])coords, face_no);
/* find the projector which the face points at most directly
* (projector normal with largest dot product is best)
@@ -258,7 +255,7 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
uint fidx = mp->totloop - 1;
do {
uint lidx = mp->loopstart + fidx;
- uint vidx = mloop[lidx].v;
+ uint vidx = loops[lidx].v;
BLI_uvproject_from_camera(mloop_uv[lidx].uv, coords[vidx], best_projector->uci);
} while (fidx--);
}
@@ -266,7 +263,7 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
uint fidx = mp->totloop - 1;
do {
uint lidx = mp->loopstart + fidx;
- uint vidx = mloop[lidx].v;
+ uint vidx = loops[lidx].v;
mul_v2_project_m4_v3(mloop_uv[lidx].uv, best_projector->projmat, coords[vidx]);
} while (fidx--);
}
@@ -284,7 +281,7 @@ static Mesh *uvprojectModifier_do(UVProjectModifierData *umd,
}
}
- mesh->runtime.is_original = false;
+ mesh->runtime.is_original_bmesh = false;
return mesh;
}
@@ -349,7 +346,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_UVProject = {
- /* name */ "UVProject",
+ /* name */ N_("UVProject"),
/* structName */ "UVProjectModifierData",
/* structSize */ sizeof(UVProjectModifierData),
/* srna */ &RNA_UVProjectModifier,
diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c
index a15efdaa381..b72a15ef1ec 100644
--- a/source/blender/modifiers/intern/MOD_uvwarp.c
+++ b/source/blender/modifiers/intern/MOD_uvwarp.c
@@ -23,6 +23,7 @@
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
@@ -57,9 +58,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(umd, DNA_struct_default_get(UVWarpModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
UVWarpModifierData *umd = (UVWarpModifierData *)md;
@@ -81,11 +80,11 @@ static void matrix_from_obj_pchan(float mat[4][4], Object *ob, const char *bonen
}
typedef struct UVWarpData {
- MPoly *mpoly;
- MLoop *mloop;
+ const MPoly *mpoly;
+ const MLoop *mloop;
MLoopUV *mloopuv;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
int defgrp_index;
float (*warp_mat)[4];
@@ -131,10 +130,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
{
UVWarpModifierData *umd = (UVWarpModifierData *)md;
int polys_num, loops_num;
- MPoly *mpoly;
- MLoop *mloop;
MLoopUV *mloopuv;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
int defgrp_index;
char uvname[MAX_CUSTOMDATA_LAYER_NAME];
float warp_mat[4][4];
@@ -196,19 +193,19 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
/* make sure we're using an existing layer */
CustomData_validate_layer_name(&mesh->ldata, CD_MLOOPUV, umd->uvlayer_name, uvname);
+ const MPoly *polys = BKE_mesh_polys(mesh);
+ const MLoop *loops = BKE_mesh_loops(mesh);
polys_num = mesh->totpoly;
loops_num = mesh->totloop;
- mpoly = mesh->mpoly;
- mloop = mesh->mloop;
/* make sure we are not modifying the original UV map */
mloopuv = CustomData_duplicate_referenced_layer_named(
&mesh->ldata, CD_MLOOPUV, uvname, loops_num);
MOD_get_vgroup(ctx->object, mesh, umd->vgroup_name, &dvert, &defgrp_index);
UVWarpData data = {
- .mpoly = mpoly,
- .mloop = mloop,
+ .mpoly = polys,
+ .mloop = loops,
.mloopuv = mloopuv,
.dvert = dvert,
.defgrp_index = defgrp_index,
@@ -220,7 +217,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
settings.use_threading = (polys_num > 1000);
BLI_task_parallel_range(0, polys_num, &data, uv_warp_compute, &settings);
- mesh->runtime.is_original = false;
+ mesh->runtime.is_original_bmesh = false;
return mesh;
}
@@ -242,7 +239,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
MOD_depsgraph_update_object_bone_relation(
ctx->node, umd->object_dst, umd->bone_dst, "UVWarp Modifier");
- DEG_add_modifier_to_transform_relation(ctx->node, "UVWarp Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "UVWarp Modifier");
}
static void panel_draw(const bContext *UNUSED(C), Panel *panel)
@@ -308,7 +305,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_UVWarp = {
- /* name */ "UVWarp",
+ /* name */ N_("UVWarp"),
/* structName */ "UVWarpModifierData",
/* structSize */ sizeof(UVWarpModifierData),
/* srna */ &RNA_UVWarpModifier,
diff --git a/source/blender/modifiers/intern/MOD_volume_displace.cc b/source/blender/modifiers/intern/MOD_volume_displace.cc
index 059cfdbdd4e..d9b94d79348 100644
--- a/source/blender/modifiers/intern/MOD_volume_displace.cc
+++ b/source/blender/modifiers/intern/MOD_volume_displace.cc
@@ -12,6 +12,8 @@
#include "BKE_texture.h"
#include "BKE_volume.h"
+#include "BLT_translation.h"
+
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
@@ -307,7 +309,7 @@ static void modifyGeometrySet(ModifierData *md,
}
ModifierTypeInfo modifierType_VolumeDisplace = {
- /* name */ "Volume Displace",
+ /* name */ N_("Volume Displace"),
/* structName */ "VolumeDisplaceModifierData",
/* structSize */ sizeof(VolumeDisplaceModifierData),
/* srna */ &RNA_VolumeDisplaceModifier,
diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
index d33687e4d92..0065012db97 100644
--- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
+++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc
@@ -12,6 +12,8 @@
#include "BKE_volume.h"
#include "BKE_volume_to_mesh.hh"
+#include "BLT_translation.h"
+
#include "MOD_modifiertypes.h"
#include "MOD_ui_common.h"
@@ -49,7 +51,7 @@ static void initData(ModifierData *md)
VolumeToMeshModifierData *vmmd = reinterpret_cast<VolumeToMeshModifierData *>(md);
vmmd->object = nullptr;
vmmd->threshold = 0.1f;
- strncpy(vmmd->grid_name, "density", MAX_NAME);
+ STRNCPY(vmmd->grid_name, "density");
vmmd->adaptivity = 0.0f;
vmmd->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID;
vmmd->voxel_amount = 32;
@@ -60,7 +62,7 @@ static void initData(ModifierData *md)
static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx)
{
VolumeToMeshModifierData *vmmd = reinterpret_cast<VolumeToMeshModifierData *>(md);
- DEG_add_modifier_to_transform_relation(ctx->node, "Volume to Mesh Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Volume to Mesh Modifier");
if (vmmd->object) {
DEG_add_object_relation(
ctx->node, vmmd->object, DEG_OB_COMP_GEOMETRY, "Volume to Mesh Modifier");
@@ -193,7 +195,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
ModifierTypeInfo modifierType_VolumeToMesh = {
- /* name */ "Volume to Mesh",
+ /* name */ N_("Volume to Mesh"),
/* structName */ "VolumeToMeshModifierData",
/* structSize */ sizeof(VolumeToMeshModifierData),
/* srna */ &RNA_VolumeToMeshModifier,
diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c
index 9693cf0c0f2..3fafbd97fee 100644
--- a/source/blender/modifiers/intern/MOD_warp.c
+++ b/source/blender/modifiers/intern/MOD_warp.c
@@ -70,9 +70,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
twmd->curfalloff = BKE_curvemapping_copy(wmd->curfalloff);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WarpModifierData *wmd = (WarpModifierData *)md;
@@ -171,7 +169,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
if (need_transform_relation) {
- DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Warp Modifier");
}
}
@@ -196,7 +194,7 @@ static void warpModifier_do(WarpModifierData *wmd,
float fac = 1.0f, weight;
int i;
int defgrp_index;
- MDeformVert *dvert, *dv = NULL;
+ const MDeformVert *dvert, *dv = NULL;
const bool invert_vgroup = (wmd->flag & MOD_WARP_INVERT_VGROUP) != 0;
float(*tex_co)[3] = NULL;
@@ -348,7 +346,7 @@ static void deformVerts(ModifierData *md,
if (wmd->defgrp_name[0] != '\0' || wmd->texture != NULL) {
/* mesh_src is only needed for vgroups and textures. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
warpModifier_do(wmd, ctx, mesh_src, vertexCos, verts_num);
@@ -370,10 +368,10 @@ static void deformVertsEM(ModifierData *md,
if (wmd->defgrp_name[0] != '\0' || wmd->texture != NULL) {
/* mesh_src is only needed for vgroups and textures. */
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -510,7 +508,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_Warp = {
- /* name */ "Warp",
+ /* name */ N_("Warp"),
/* structName */ "WarpModifierData",
/* structSize */ sizeof(WarpModifierData),
/* srna */ &RNA_WarpModifier,
diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c
index 4073f028db5..b49a47b0fb4 100644
--- a/source/blender/modifiers/intern/MOD_wave.c
+++ b/source/blender/modifiers/intern/MOD_wave.c
@@ -98,13 +98,11 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
if (need_transform_relation) {
- DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "Wave Modifier");
}
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WaveModifierData *wmd = (WaveModifierData *)md;
@@ -134,8 +132,7 @@ static void waveModifier_do(WaveModifierData *md,
int verts_num)
{
WaveModifierData *wmd = (WaveModifierData *)md;
- MVert *mvert = NULL;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
int defgrp_index;
float ctime = DEG_get_ctime(ctx->depsgraph);
float minfac = (float)(1.0 / exp(wmd->width * wmd->narrow * wmd->width * wmd->narrow));
@@ -148,7 +145,6 @@ static void waveModifier_do(WaveModifierData *md,
const float(*vert_normals)[3] = NULL;
if ((wmd->flag & MOD_WAVE_NORM) && (mesh != NULL)) {
- mvert = mesh->mvert;
vert_normals = BKE_mesh_vertex_normals_ensure(mesh);
}
@@ -269,7 +265,7 @@ static void waveModifier_do(WaveModifierData *md,
/* Apply weight & falloff. */
amplit *= def_weight * falloff_fac;
- if (mvert) {
+ if (vert_normals) {
/* move along normals */
if (wmd->flag & MOD_WAVE_NORM_X) {
co[0] += (lifefac * amplit) * vert_normals[i][0];
@@ -302,11 +298,10 @@ static void deformVerts(ModifierData *md,
Mesh *mesh_src = NULL;
if (wmd->flag & MOD_WAVE_NORM) {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, NULL, mesh, vertexCos, verts_num, true, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, vertexCos, verts_num, false);
}
else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') {
- mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false);
}
waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num);
@@ -327,15 +322,13 @@ static void deformVertsEM(ModifierData *md,
Mesh *mesh_src = NULL;
if (wmd->flag & MOD_WAVE_NORM) {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, vertexCos, verts_num, true, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, vertexCos, verts_num, false);
}
else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') {
- mesh_src = MOD_deform_mesh_eval_get(
- ctx->object, editData, mesh, NULL, verts_num, false, false);
+ mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false);
}
- /* TODO(Campbell): use edit-mode data only (remove this line). */
+ /* TODO(@campbellbarton): use edit-mode data only (remove this line). */
if (mesh_src != NULL) {
BKE_mesh_wrapper_ensure_mdata(mesh_src);
}
@@ -343,6 +336,12 @@ static void deformVertsEM(ModifierData *md,
waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num);
if (!ELEM(mesh_src, NULL, mesh)) {
+ /* Important not to free `vertexCos` owned by the caller. */
+ EditMeshData *edit_data = mesh_src->runtime.edit_data;
+ if (edit_data->vertexCos == vertexCos) {
+ edit_data->vertexCos = NULL;
+ }
+
BKE_id_free(NULL, mesh_src);
}
}
@@ -372,7 +371,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(sub, ptr, "use_normal_z", UI_ITEM_R_TOGGLE, "Z", ICON_NONE);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "falloff_radius", 0, "Falloff", ICON_NONE);
+ uiItemR(col, ptr, "falloff_radius", 0, IFACE_("Falloff"), ICON_NONE);
uiItemR(col, ptr, "height", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
uiItemR(col, ptr, "width", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
uiItemR(col, ptr, "narrowness", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
@@ -394,7 +393,7 @@ static void position_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiItemR(layout, ptr, "start_position_object", 0, IFACE_("Object"), ICON_NONE);
col = uiLayoutColumn(layout, true);
- uiItemR(col, ptr, "start_position_x", 0, "Start Position X", ICON_NONE);
+ uiItemR(col, ptr, "start_position_x", 0, IFACE_("Start Position X"), ICON_NONE);
uiItemR(col, ptr, "start_position_y", 0, "Y", ICON_NONE);
}
@@ -408,9 +407,9 @@ static void time_panel_draw(const bContext *UNUSED(C), Panel *panel)
uiLayoutSetPropSep(layout, true);
col = uiLayoutColumn(layout, false);
- uiItemR(col, ptr, "time_offset", 0, "Offset", ICON_NONE);
- uiItemR(col, ptr, "lifetime", 0, "Life", ICON_NONE);
- uiItemR(col, ptr, "damping_time", 0, "Damping", ICON_NONE);
+ uiItemR(col, ptr, "time_offset", 0, IFACE_("Offset"), ICON_NONE);
+ uiItemR(col, ptr, "lifetime", 0, IFACE_("Life"), ICON_NONE);
+ uiItemR(col, ptr, "damping_time", 0, IFACE_("Damping"), ICON_NONE);
uiItemR(col, ptr, "speed", UI_ITEM_R_SLIDER, NULL, ICON_NONE);
}
@@ -462,7 +461,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Wave = {
- /* name */ "Wave",
+ /* name */ N_("Wave"),
/* structName */ "WaveModifierData",
/* structSize */ sizeof(WaveModifierData),
/* srna */ &RNA_WaveModifier,
diff --git a/source/blender/modifiers/intern/MOD_weighted_normal.c b/source/blender/modifiers/intern/MOD_weighted_normal.c
index d436acb8ad5..69210c85862 100644
--- a/source/blender/modifiers/intern/MOD_weighted_normal.c
+++ b/source/blender/modifiers/intern/MOD_weighted_normal.c
@@ -71,20 +71,20 @@ typedef struct WeightedNormalData {
const int loops_num;
const int polys_num;
- MVert *mvert;
+ const MVert *mvert;
const float (*vert_normals)[3];
MEdge *medge;
- MLoop *mloop;
+ const MLoop *mloop;
short (*clnors)[2];
const bool has_clnors; /* True if clnors already existed, false if we had to create them. */
const float split_angle;
- MPoly *mpoly;
+ const MPoly *mpoly;
const float (*polynors)[3];
const int *poly_strength;
- MDeformVert *dvert;
+ const MDeformVert *dvert;
const int defgrp_index;
const bool use_invert_vgroup;
@@ -133,7 +133,7 @@ static void aggregate_item_normal(WeightedNormalModifierData *wnmd,
{
const float(*polynors)[3] = wn_data->polynors;
- MDeformVert *dvert = wn_data->dvert;
+ const MDeformVert *dvert = wn_data->dvert;
const int defgrp_index = wn_data->defgrp_index;
const bool use_invert_vgroup = wn_data->use_invert_vgroup;
@@ -186,18 +186,18 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
const int loops_num = wn_data->loops_num;
const int polys_num = wn_data->polys_num;
- MVert *mvert = wn_data->mvert;
+ const MVert *mvert = wn_data->mvert;
MEdge *medge = wn_data->medge;
- MLoop *mloop = wn_data->mloop;
+ const MLoop *mloop = wn_data->mloop;
short(*clnors)[2] = wn_data->clnors;
int *loop_to_poly = wn_data->loop_to_poly;
- MPoly *mpoly = wn_data->mpoly;
+ const MPoly *mpoly = wn_data->mpoly;
const float(*polynors)[3] = wn_data->polynors;
const int *poly_strength = wn_data->poly_strength;
- MDeformVert *dvert = wn_data->dvert;
+ const MDeformVert *dvert = wn_data->dvert;
const short mode = wn_data->mode;
ModePair *mode_pair = wn_data->mode_pair;
@@ -243,7 +243,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
/* In this first loop, we assign each WeightedNormalDataAggregateItem
* to its smooth fan of loops (aka lnor space). */
- MPoly *mp;
+ const MPoly *mp;
int mp_index;
int item_index;
for (mp = mpoly, mp_index = 0, item_index = 0; mp_index < polys_num; mp++, mp_index++) {
@@ -362,7 +362,7 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
clnors);
}
else {
- /* TODO: Ideally, we could add an option to BKE_mesh_normals_loop_custom_[from_vertices_]set()
+ /* TODO: Ideally, we could add an option to `BKE_mesh_normals_loop_custom_[from_verts_]set()`
* to keep current clnors instead of resetting them to default auto-computed ones,
* when given new custom normal is zero-vec.
* But this is not exactly trivial change, better to keep this optimization for later...
@@ -379,18 +379,18 @@ static void apply_weights_vertex_normal(WeightedNormalModifierData *wnmd,
copy_v3_v3(vert_normals[mv_index], items_data[mv_index].normal);
}
- BKE_mesh_normals_loop_custom_from_vertices_set(mvert,
- wn_data->vert_normals,
- vert_normals,
- verts_num,
- medge,
- edges_num,
- mloop,
- loops_num,
- mpoly,
- polynors,
- polys_num,
- clnors);
+ BKE_mesh_normals_loop_custom_from_verts_set(mvert,
+ wn_data->vert_normals,
+ vert_normals,
+ verts_num,
+ medge,
+ edges_num,
+ mloop,
+ loops_num,
+ mpoly,
+ polynors,
+ polys_num,
+ clnors);
MEM_freeN(vert_normals);
}
@@ -446,11 +446,11 @@ static void wn_face_area(WeightedNormalModifierData *wnmd, WeightedNormalData *w
{
const int polys_num = wn_data->polys_num;
- MVert *mvert = wn_data->mvert;
- MLoop *mloop = wn_data->mloop;
- MPoly *mpoly = wn_data->mpoly;
+ const MVert *mvert = wn_data->mvert;
+ const MLoop *mloop = wn_data->mloop;
+ const MPoly *mpoly = wn_data->mpoly;
- MPoly *mp;
+ const MPoly *mp;
int mp_index;
ModePair *face_area = MEM_malloc_arrayN((size_t)polys_num, sizeof(*face_area), __func__);
@@ -472,11 +472,11 @@ static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData
const int loops_num = wn_data->loops_num;
const int polys_num = wn_data->polys_num;
- MVert *mvert = wn_data->mvert;
- MLoop *mloop = wn_data->mloop;
- MPoly *mpoly = wn_data->mpoly;
+ const MVert *mvert = wn_data->mvert;
+ const MLoop *mloop = wn_data->mloop;
+ const MPoly *mpoly = wn_data->mpoly;
- MPoly *mp;
+ const MPoly *mp;
int mp_index;
int *loop_to_poly = MEM_malloc_arrayN((size_t)loops_num, sizeof(*loop_to_poly), __func__);
@@ -484,7 +484,7 @@ static void wn_corner_angle(WeightedNormalModifierData *wnmd, WeightedNormalData
ModePair *corner_angle = MEM_malloc_arrayN((size_t)loops_num, sizeof(*corner_angle), __func__);
for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++) {
- MLoop *ml_start = &mloop[mp->loopstart];
+ const MLoop *ml_start = &mloop[mp->loopstart];
float *index_angle = MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__);
BKE_mesh_calc_poly_angles(mp, ml_start, mvert, index_angle);
@@ -513,11 +513,11 @@ static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalD
const int loops_num = wn_data->loops_num;
const int polys_num = wn_data->polys_num;
- MVert *mvert = wn_data->mvert;
- MLoop *mloop = wn_data->mloop;
- MPoly *mpoly = wn_data->mpoly;
+ const MVert *mvert = wn_data->mvert;
+ const MLoop *mloop = wn_data->mloop;
+ const MPoly *mpoly = wn_data->mpoly;
- MPoly *mp;
+ const MPoly *mp;
int mp_index;
int *loop_to_poly = MEM_malloc_arrayN((size_t)loops_num, sizeof(*loop_to_poly), __func__);
@@ -525,7 +525,7 @@ static void wn_face_with_angle(WeightedNormalModifierData *wnmd, WeightedNormalD
ModePair *combined = MEM_malloc_arrayN((size_t)loops_num, sizeof(*combined), __func__);
for (mp_index = 0, mp = mpoly; mp_index < polys_num; mp_index++, mp++) {
- MLoop *ml_start = &mloop[mp->loopstart];
+ const MLoop *ml_start = &mloop[mp->loopstart];
float face_area = BKE_mesh_calc_poly_area(mp, ml_start, mvert);
float *index_angle = MEM_malloc_arrayN((size_t)mp->totloop, sizeof(*index_angle), __func__);
@@ -579,11 +579,10 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
const int edges_num = result->totedge;
const int loops_num = result->totloop;
const int polys_num = result->totpoly;
-
- MEdge *medge = result->medge;
- MPoly *mpoly = result->mpoly;
- MVert *mvert = result->mvert;
- MLoop *mloop = result->mloop;
+ const MVert *mvert = BKE_mesh_verts(result);
+ MEdge *medge = BKE_mesh_edges_for_write(result);
+ const MPoly *mpoly = BKE_mesh_polys(result);
+ const MLoop *mloop = BKE_mesh_loops(result);
/* Right now:
* If weight = 50 then all faces are given equal weight.
@@ -609,10 +608,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
* it helps when generating clnor spaces and default normals. */
const bool has_clnors = clnors != NULL;
if (!clnors) {
- clnors = CustomData_add_layer(&result->ldata, CD_CUSTOMLOOPNORMAL, CD_CALLOC, NULL, loops_num);
+ clnors = CustomData_add_layer(
+ &result->ldata, CD_CUSTOMLOOPNORMAL, CD_SET_DEFAULT, NULL, loops_num);
}
- MDeformVert *dvert;
+ const MDeformVert *dvert;
int defgrp_index;
MOD_get_vgroup(ctx->object, mesh, wnmd->defgrp_name, &dvert, &defgrp_index);
@@ -660,7 +660,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
MEM_SAFE_FREE(wn_data.mode_pair);
MEM_SAFE_FREE(wn_data.items_data);
- result->runtime.is_original = false;
+ result->runtime.is_original_bmesh = false;
return result;
}
@@ -674,9 +674,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(wnmd, DNA_struct_default_get(WeightedNormalModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WeightedNormalModifierData *wnmd = (WeightedNormalModifierData *)md;
@@ -726,7 +724,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_WeightedNormal = {
- /* name */ "WeightedNormal",
+ /* name */ N_("WeightedNormal"),
/* structName */ "WeightedNormalModifierData",
/* structSize */ sizeof(WeightedNormalModifierData),
/* srna */ &RNA_WeightedNormalModifier,
diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c
index e1b43157adb..fa147201dc3 100644
--- a/source/blender/modifiers/intern/MOD_weightvgedit.c
+++ b/source/blender/modifiers/intern/MOD_weightvgedit.c
@@ -27,6 +27,7 @@
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "BKE_texture.h" /* Texture masking. */
@@ -80,9 +81,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
twmd->cmap_curve = BKE_curvemapping_copy(wmd->cmap_curve);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md;
@@ -139,7 +138,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
if (need_transform_relation) {
- DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "WeightVGEdit Modifier");
}
}
@@ -158,7 +157,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
WeightVGEditModifierData *wmd = (WeightVGEditModifierData *)md;
- MDeformVert *dvert = NULL;
MDeformWeight **dw = NULL;
float *org_w; /* Array original weights. */
float *new_w; /* Array new weights. */
@@ -198,18 +196,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
}
- if (has_mdef) {
- dvert = CustomData_duplicate_referenced_layer(&mesh->vdata, CD_MDEFORMVERT, verts_num);
- }
- else {
- /* Add a valid data layer! */
- dvert = CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, verts_num);
- }
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(mesh);
+
/* Ultimate security check. */
if (!dvert) {
return mesh;
}
- mesh->dvert = dvert;
/* Get org weights, assuming 0.0 for vertices not in given vgroup. */
org_w = MEM_malloc_arrayN(verts_num, sizeof(float), "WeightVGEdit Modifier, org_w");
@@ -287,7 +279,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
MEM_freeN(new_w);
MEM_freeN(dw);
- mesh->runtime.is_original = false;
+ mesh->runtime.is_original_bmesh = false;
/* Return the vgroup-modified mesh. */
return mesh;
@@ -316,7 +308,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
sub = uiLayoutRow(sub, true);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_add"));
uiLayoutSetPropSep(sub, false);
- uiItemR(sub, ptr, "add_threshold", UI_ITEM_R_SLIDER, "Threshold", ICON_NONE);
+ uiItemR(sub, ptr, "add_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE);
uiItemDecoratorR(row, ptr, "add_threshold", 0);
col = uiLayoutColumnWithHeading(layout, false, IFACE_("Group Remove"));
@@ -327,7 +319,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
sub = uiLayoutRow(sub, true);
uiLayoutSetActive(sub, RNA_boolean_get(ptr, "use_remove"));
uiLayoutSetPropSep(sub, false);
- uiItemR(sub, ptr, "remove_threshold", UI_ITEM_R_SLIDER, "Threshold", ICON_NONE);
+ uiItemR(sub, ptr, "remove_threshold", UI_ITEM_R_SLIDER, IFACE_("Threshold"), ICON_NONE);
uiItemDecoratorR(row, ptr, "remove_threshold", 0);
uiItemR(layout, ptr, "normalize", 0, NULL, ICON_NONE);
@@ -397,7 +389,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_WeightVGEdit = {
- /* name */ "VertexWeightEdit",
+ /* name */ N_("VertexWeightEdit"),
/* structName */ "WeightVGEditModifierData",
/* structSize */ sizeof(WeightVGEditModifierData),
/* srna */ &RNA_VertexWeightEditModifier,
diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c
index b827d41e80a..957ea3b6c8f 100644
--- a/source/blender/modifiers/intern/MOD_weightvgmix.c
+++ b/source/blender/modifiers/intern/MOD_weightvgmix.c
@@ -23,6 +23,7 @@
#include "BKE_customdata.h"
#include "BKE_deform.h"
#include "BKE_lib_query.h"
+#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
#include "BKE_texture.h" /* Texture masking. */
@@ -128,9 +129,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeightVGMixModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
@@ -187,7 +186,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
if (need_transform_relation) {
- DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "WeightVGMix Modifier");
}
}
@@ -206,7 +205,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
WeightVGMixModifierData *wmd = (WeightVGMixModifierData *)md;
- MDeformVert *dvert = NULL;
MDeformWeight **dw1, **tdw1, **dw2, **tdw2;
float *org_w;
float *new_w;
@@ -263,18 +261,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
}
}
- if (has_mdef) {
- dvert = CustomData_duplicate_referenced_layer(&mesh->vdata, CD_MDEFORMVERT, verts_num);
- }
- else {
- /* Add a valid data layer! */
- dvert = CustomData_add_layer(&mesh->vdata, CD_MDEFORMVERT, CD_CALLOC, NULL, verts_num);
- }
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(mesh);
+
/* Ultimate security check. */
if (!dvert) {
return mesh;
}
- mesh->dvert = dvert;
/* Find out which vertices to work on. */
tidx = MEM_malloc_arrayN(verts_num, sizeof(int), "WeightVGMix Modifier, tidx");
@@ -444,7 +436,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
MEM_freeN(dw2);
MEM_SAFE_FREE(indices);
- mesh->runtime.is_original = false;
+ mesh->runtime.is_original_bmesh = false;
/* Return the vgroup-modified mesh. */
return mesh;
@@ -496,7 +488,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_WeightVGMix = {
- /* name */ "VertexWeightMix",
+ /* name */ N_("VertexWeightMix"),
/* structName */ "WeightVGMixModifierData",
/* structSize */ sizeof(WeightVGMixModifierData),
/* srna */ &RNA_VertexWeightMixModifier,
diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c
index 1bea5b93c97..ead12d9f08d 100644
--- a/source/blender/modifiers/intern/MOD_weightvgproximity.c
+++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c
@@ -330,9 +330,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla
twmd->cmap_curve = BKE_curvemapping_copy(wmd->cmap_curve);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md;
@@ -401,7 +399,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
}
if (need_transform_relation) {
- DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGProximity Modifier");
+ DEG_add_depends_on_transform_relation(ctx->node, "WeightVGProximity Modifier");
}
}
@@ -423,7 +421,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
BLI_assert(mesh != NULL);
WeightVGProximityModifierData *wmd = (WeightVGProximityModifierData *)md;
- MDeformVert *dvert = NULL;
MDeformWeight **dw, **tdw;
float(*v_cos)[3] = NULL; /* The vertices coordinates. */
Object *ob = ctx->object;
@@ -475,12 +472,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
return mesh;
}
- dvert = CustomData_duplicate_referenced_layer(&mesh->vdata, CD_MDEFORMVERT, verts_num);
+ MDeformVert *dvert = BKE_mesh_deform_verts_for_write(mesh);
/* Ultimate security check. */
if (!dvert) {
return mesh;
}
- mesh->dvert = dvert;
/* Find out which vertices to work on (all vertices in vgroup), and get their relevant weight. */
tidx = MEM_malloc_arrayN(verts_num, sizeof(int), "WeightVGProximity Modifier, tidx");
@@ -544,7 +540,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
const bool use_trgt_faces = (wmd->proximity_flags & MOD_WVG_PROXIMITY_GEOM_FACES) != 0;
if (use_trgt_verts || use_trgt_edges || use_trgt_faces) {
- Mesh *target_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(obr, false);
+ Mesh *target_mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(obr);
/* We must check that we do have a valid target_mesh! */
if (target_mesh != NULL) {
@@ -640,7 +636,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *
TIMEIT_END(perf);
#endif
- mesh->runtime.is_original = false;
+ mesh->runtime.is_original_bmesh = false;
/* Return the vgroup-modified mesh. */
return mesh;
@@ -737,7 +733,7 @@ static void blendRead(BlendDataReader *reader, ModifierData *md)
}
ModifierTypeInfo modifierType_WeightVGProximity = {
- /* name */ "VertexWeightProximity",
+ /* name */ N_("VertexWeightProximity"),
/* structName */ "WeightVGProximityModifierData",
/* structSize */ sizeof(WeightVGProximityModifierData),
/* srna */ &RNA_VertexWeightProximityModifier,
diff --git a/source/blender/modifiers/intern/MOD_weld.cc b/source/blender/modifiers/intern/MOD_weld.cc
index 93d4b56176c..ef6d561fa00 100644
--- a/source/blender/modifiers/intern/MOD_weld.cc
+++ b/source/blender/modifiers/intern/MOD_weld.cc
@@ -147,9 +147,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WeldModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WeldModifierData *wmd = (WeldModifierData *)md;
@@ -185,7 +183,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Weld = {
- /* name */ "Weld",
+ /* name */ N_("Weld"),
/* structName */ "WeldModifierData",
/* structSize */ sizeof(WeldModifierData),
/* srna */ &RNA_WeldModifier,
diff --git a/source/blender/modifiers/intern/MOD_wireframe.c b/source/blender/modifiers/intern/MOD_wireframe.c
index b657ea87244..85696bed162 100644
--- a/source/blender/modifiers/intern/MOD_wireframe.c
+++ b/source/blender/modifiers/intern/MOD_wireframe.c
@@ -41,9 +41,7 @@ static void initData(ModifierData *md)
MEMCPY_STRUCT_AFTER(wmd, DNA_struct_default_get(WireframeModifierData), modifier);
}
-static void requiredDataMask(Object *UNUSED(ob),
- ModifierData *md,
- CustomData_MeshMasks *r_cddata_masks)
+static void requiredDataMask(ModifierData *md, CustomData_MeshMasks *r_cddata_masks)
{
WireframeModifierData *wmd = (WireframeModifierData *)md;
@@ -165,7 +163,7 @@ static void panelRegister(ARegionType *region_type)
}
ModifierTypeInfo modifierType_Wireframe = {
- /* name */ "Wireframe",
+ /* name */ N_("Wireframe"),
/* structName */ "WireframeModifierData",
/* structSize */ sizeof(WireframeModifierData),
/* srna */ &RNA_WireframeModifier,
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 386e5fe14c9..e9c0f2be368 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -29,7 +29,6 @@ set(INC
../makesrna
../render
../windowmanager
- ../../../intern/glew-mx
../../../intern/guardedalloc
# dna_type_offsets.h
@@ -41,7 +40,8 @@ set(INC
set(SRC
intern/derived_node_tree.cc
- intern/geometry_nodes_eval_log.cc
+ intern/geometry_nodes_lazy_function.cc
+ intern/geometry_nodes_log.cc
intern/math_functions.cc
intern/node_common.cc
intern/node_declaration.cc
@@ -50,7 +50,6 @@ set(SRC
intern/node_multi_function.cc
intern/node_socket.cc
intern/node_socket_declarations.cc
- intern/node_tree_ref.cc
intern/node_util.c
intern/socket_search_link.cc
@@ -60,11 +59,10 @@ set(SRC
NOD_function.h
NOD_geometry.h
NOD_geometry_exec.hh
- NOD_geometry_nodes_eval_log.hh
+ NOD_geometry_nodes_lazy_function.hh
NOD_math_functions.hh
NOD_multi_function.hh
NOD_node_declaration.hh
- NOD_node_tree_ref.hh
NOD_shader.h
NOD_socket.h
NOD_socket_declarations.hh
@@ -104,20 +102,6 @@ if(WITH_BULLET)
add_definitions(-DWITH_BULLET)
endif()
-if(WITH_PYTHON)
- list(APPEND INC
- ../python
- )
- list(APPEND INC_SYS
- ${PYTHON_INCLUDE_DIRS}
- )
- list(APPEND LIB
- ${PYTHON_LINKFLAGS}
- ${PYTHON_LIBRARIES}
- )
- add_definitions(-DWITH_PYTHON)
-endif()
-
if(WITH_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index 5d782674f16..58126c5722d 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -169,7 +169,7 @@ void ntreeCompositClearTags(struct bNodeTree *ntree);
struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree,
struct bNode *node,
const char *name,
- struct ImageFormatData *im_format);
+ const struct ImageFormatData *im_format);
int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node);
void ntreeCompositOutputFileSetPath(struct bNode *node,
diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh
index b0799d90dcd..b3775e729da 100644
--- a/source/blender/nodes/NOD_derived_node_tree.hh
+++ b/source/blender/nodes/NOD_derived_node_tree.hh
@@ -5,16 +5,17 @@
/** \file
* \ingroup nodes
*
- * DerivedNodeTree builds on top of NodeTreeRef and makes working with (nested) node groups more
- * convenient and safe. It does so by pairing nodes and sockets with a context. The context
- * contains information about the current "instance" of the node or socket. A node might be
- * "instanced" multiple times when it is in a node group that is used multiple times.
+ * DerivedNodeTree makes working with (nested) node groups more convenient and safe. It does so by
+ * pairing nodes and sockets with a context. The context contains information about the current
+ * "instance" of the node or socket. A node might be "instanced" multiple times when it is in a
+ * node group that is used multiple times.
*/
#include "BLI_function_ref.hh"
+#include "BLI_linear_allocator.hh"
#include "BLI_vector_set.hh"
-#include "NOD_node_tree_ref.hh"
+#include "BKE_node_runtime.hh"
namespace blender::nodes {
@@ -40,20 +41,20 @@ class DTreeContext {
DTreeContext *parent_context_;
/* Null when this context is for the root node group. Otherwise it points to the group node in
* the parent node group that contains this context. */
- const NodeRef *parent_node_;
+ const bNode *parent_node_;
/* The current node tree. */
- const NodeTreeRef *tree_;
+ const bNodeTree *btree_;
/* All the children contexts of this context. */
- Map<const NodeRef *, DTreeContext *> children_;
+ Map<const bNode *, DTreeContext *> children_;
DerivedNodeTree *derived_tree_;
friend DerivedNodeTree;
public:
- const NodeTreeRef &tree() const;
+ const bNodeTree &btree() const;
const DTreeContext *parent_context() const;
- const NodeRef *parent_node() const;
- const DTreeContext *child_context(const NodeRef &node) const;
+ const bNode *parent_node() const;
+ const DTreeContext *child_context(const bNode &node) const;
const DerivedNodeTree &derived_tree() const;
bool is_root() const;
};
@@ -65,15 +66,16 @@ class DTreeContext {
class DNode {
private:
const DTreeContext *context_ = nullptr;
- const NodeRef *node_ref_ = nullptr;
+ const bNode *bnode_ = nullptr;
public:
DNode() = default;
- DNode(const DTreeContext *context, const NodeRef *node);
+ DNode(const DTreeContext *context, const bNode *node);
const DTreeContext *context() const;
- const NodeRef *node_ref() const;
- const NodeRef *operator->() const;
+ const bNode *bnode() const;
+ const bNode *operator->() const;
+ const bNode &operator*() const;
friend bool operator==(const DNode &a, const DNode &b);
friend bool operator!=(const DNode &a, const DNode &b);
@@ -98,17 +100,18 @@ class DNode {
class DSocket {
protected:
const DTreeContext *context_ = nullptr;
- const SocketRef *socket_ref_ = nullptr;
+ const bNodeSocket *bsocket_ = nullptr;
public:
DSocket() = default;
- DSocket(const DTreeContext *context, const SocketRef *socket);
+ DSocket(const DTreeContext *context, const bNodeSocket *socket);
DSocket(const DInputSocket &input_socket);
DSocket(const DOutputSocket &output_socket);
const DTreeContext *context() const;
- const SocketRef *socket_ref() const;
- const SocketRef *operator->() const;
+ const bNodeSocket *bsocket() const;
+ const bNodeSocket *operator->() const;
+ const bNodeSocket &operator*() const;
friend bool operator==(const DSocket &a, const DSocket &b);
friend bool operator!=(const DSocket &a, const DSocket &b);
@@ -123,12 +126,9 @@ class DSocket {
class DInputSocket : public DSocket {
public:
DInputSocket() = default;
- DInputSocket(const DTreeContext *context, const InputSocketRef *socket);
+ DInputSocket(const DTreeContext *context, const bNodeSocket *socket);
explicit DInputSocket(const DSocket &base_socket);
- const InputSocketRef *socket_ref() const;
- const InputSocketRef *operator->() const;
-
DOutputSocket get_corresponding_group_node_output() const;
Vector<DOutputSocket, 4> get_corresponding_group_input_sockets() const;
@@ -144,12 +144,9 @@ class DInputSocket : public DSocket {
class DOutputSocket : public DSocket {
public:
DOutputSocket() = default;
- DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket);
+ DOutputSocket(const DTreeContext *context, const bNodeSocket *socket);
explicit DOutputSocket(const DSocket &base_socket);
- const OutputSocketRef *socket_ref() const;
- const OutputSocketRef *operator->() const;
-
DInputSocket get_corresponding_group_node_input() const;
DInputSocket get_active_corresponding_group_output_socket() const;
@@ -177,7 +174,7 @@ class DerivedNodeTree {
private:
LinearAllocator<> allocator_;
DTreeContext *root_context_;
- VectorSet<const NodeTreeRef *> used_node_tree_refs_;
+ VectorSet<const bNodeTree *> used_btrees_;
public:
/**
@@ -186,11 +183,11 @@ class DerivedNodeTree {
* has to make sure that the node tree refs added to #node_tree_refs live at least as long as the
* derived node tree.
*/
- DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs);
+ DerivedNodeTree(const bNodeTree &btree);
~DerivedNodeTree();
const DTreeContext &root_context() const;
- Span<const NodeTreeRef *> used_node_tree_refs() const;
+ Span<const bNodeTree *> used_btrees() const;
/**
* \return True when there is a link cycle. Unavailable sockets are ignored.
@@ -205,9 +202,8 @@ class DerivedNodeTree {
private:
DTreeContext &construct_context_recursively(DTreeContext *parent_context,
- const NodeRef *parent_node,
- bNodeTree &btree,
- NodeTreeRefMap &node_tree_refs);
+ const bNode *parent_node,
+ const bNodeTree &btree);
void destruct_context_recursively(DTreeContext *context);
void foreach_node_in_context_recursive(const DTreeContext &context,
@@ -215,7 +211,6 @@ class DerivedNodeTree {
};
namespace derived_node_tree_types {
-using namespace node_tree_ref_types;
using nodes::DerivedNodeTree;
using nodes::DInputSocket;
using nodes::DNode;
@@ -228,9 +223,9 @@ using nodes::DTreeContext;
/** \name #DTreeContext Inline Methods
* \{ */
-inline const NodeTreeRef &DTreeContext::tree() const
+inline const bNodeTree &DTreeContext::btree() const
{
- return *tree_;
+ return *btree_;
}
inline const DTreeContext *DTreeContext::parent_context() const
@@ -238,12 +233,12 @@ inline const DTreeContext *DTreeContext::parent_context() const
return parent_context_;
}
-inline const NodeRef *DTreeContext::parent_node() const
+inline const bNode *DTreeContext::parent_node() const
{
return parent_node_;
}
-inline const DTreeContext *DTreeContext::child_context(const NodeRef &node) const
+inline const DTreeContext *DTreeContext::child_context(const bNode &node) const
{
return children_.lookup_default(&node, nullptr);
}
@@ -264,10 +259,10 @@ inline bool DTreeContext::is_root() const
/** \name #DNode Inline Methods
* \{ */
-inline DNode::DNode(const DTreeContext *context, const NodeRef *node_ref)
- : context_(context), node_ref_(node_ref)
+inline DNode::DNode(const DTreeContext *context, const bNode *bnode)
+ : context_(context), bnode_(bnode)
{
- BLI_assert(node_ref == nullptr || &node_ref->tree() == &context->tree());
+ BLI_assert(bnode == nullptr || bnode->runtime->owner_tree == &context->btree());
}
inline const DTreeContext *DNode::context() const
@@ -275,14 +270,14 @@ inline const DTreeContext *DNode::context() const
return context_;
}
-inline const NodeRef *DNode::node_ref() const
+inline const bNode *DNode::bnode() const
{
- return node_ref_;
+ return bnode_;
}
inline bool operator==(const DNode &a, const DNode &b)
{
- return a.context_ == b.context_ && a.node_ref_ == b.node_ref_;
+ return a.context_ == b.context_ && a.bnode_ == b.bnode_;
}
inline bool operator!=(const DNode &a, const DNode &b)
@@ -292,37 +287,43 @@ inline bool operator!=(const DNode &a, const DNode &b)
inline DNode::operator bool() const
{
- return node_ref_ != nullptr;
+ return bnode_ != nullptr;
+}
+
+inline const bNode *DNode::operator->() const
+{
+ return bnode_;
}
-inline const NodeRef *DNode::operator->() const
+inline const bNode &DNode::operator*() const
{
- return node_ref_;
+ BLI_assert(bnode_ != nullptr);
+ return *bnode_;
}
inline uint64_t DNode::hash() const
{
- return get_default_hash_2(context_, node_ref_);
+ return get_default_hash_2(context_, bnode_);
}
inline DInputSocket DNode::input(int index) const
{
- return {context_, &node_ref_->input(index)};
+ return {context_, &bnode_->input_socket(index)};
}
inline DOutputSocket DNode::output(int index) const
{
- return {context_, &node_ref_->output(index)};
+ return {context_, &bnode_->output_socket(index)};
}
inline DInputSocket DNode::input_by_identifier(StringRef identifier) const
{
- return {context_, &node_ref_->input_by_identifier(identifier)};
+ return {context_, &bnode_->input_by_identifier(identifier)};
}
inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
{
- return {context_, &node_ref_->output_by_identifier(identifier)};
+ return {context_, &bnode_->output_by_identifier(identifier)};
}
/** \} */
@@ -331,19 +332,20 @@ inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const
/** \name #DSocket Inline Methods
* \{ */
-inline DSocket::DSocket(const DTreeContext *context, const SocketRef *socket_ref)
- : context_(context), socket_ref_(socket_ref)
+inline DSocket::DSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : context_(context), bsocket_(bsocket)
{
- BLI_assert(socket_ref == nullptr || &socket_ref->tree() == &context->tree());
+ BLI_assert(bsocket == nullptr ||
+ bsocket->runtime->owner_node->runtime->owner_tree == &context->btree());
}
inline DSocket::DSocket(const DInputSocket &input_socket)
- : DSocket(input_socket.context_, input_socket.socket_ref_)
+ : DSocket(input_socket.context_, input_socket.bsocket_)
{
}
inline DSocket::DSocket(const DOutputSocket &output_socket)
- : DSocket(output_socket.context_, output_socket.socket_ref_)
+ : DSocket(output_socket.context_, output_socket.bsocket_)
{
}
@@ -352,14 +354,14 @@ inline const DTreeContext *DSocket::context() const
return context_;
}
-inline const SocketRef *DSocket::socket_ref() const
+inline const bNodeSocket *DSocket::bsocket() const
{
- return socket_ref_;
+ return bsocket_;
}
inline bool operator==(const DSocket &a, const DSocket &b)
{
- return a.context_ == b.context_ && a.socket_ref_ == b.socket_ref_;
+ return a.context_ == b.context_ && a.bsocket_ == b.bsocket_;
}
inline bool operator!=(const DSocket &a, const DSocket &b)
@@ -369,23 +371,29 @@ inline bool operator!=(const DSocket &a, const DSocket &b)
inline DSocket::operator bool() const
{
- return socket_ref_ != nullptr;
+ return bsocket_ != nullptr;
}
-inline const SocketRef *DSocket::operator->() const
+inline const bNodeSocket *DSocket::operator->() const
{
- return socket_ref_;
+ return bsocket_;
+}
+
+inline const bNodeSocket &DSocket::operator*() const
+{
+ BLI_assert(bsocket_ != nullptr);
+ return *bsocket_;
}
inline uint64_t DSocket::hash() const
{
- return get_default_hash_2(context_, socket_ref_);
+ return get_default_hash_2(context_, bsocket_);
}
inline DNode DSocket::node() const
{
- BLI_assert(socket_ref_ != nullptr);
- return {context_, &socket_ref_->node()};
+ BLI_assert(bsocket_ != nullptr);
+ return {context_, bsocket_->runtime->owner_node};
}
/** \} */
@@ -394,8 +402,8 @@ inline DNode DSocket::node() const
/** \name #DInputSocket Inline Methods
* \{ */
-inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocketRef *socket_ref)
- : DSocket(context, socket_ref)
+inline DInputSocket::DInputSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : DSocket(context, bsocket)
{
}
@@ -404,24 +412,14 @@ inline DInputSocket::DInputSocket(const DSocket &base_socket) : DSocket(base_soc
BLI_assert(base_socket->is_input());
}
-inline const InputSocketRef *DInputSocket::socket_ref() const
-{
- return (const InputSocketRef *)socket_ref_;
-}
-
-inline const InputSocketRef *DInputSocket::operator->() const
-{
- return (const InputSocketRef *)socket_ref_;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
/** \name #DOutputSocket Inline Methods
* \{ */
-inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket_ref)
- : DSocket(context, socket_ref)
+inline DOutputSocket::DOutputSocket(const DTreeContext *context, const bNodeSocket *bsocket)
+ : DSocket(context, bsocket)
{
}
@@ -430,16 +428,6 @@ inline DOutputSocket::DOutputSocket(const DSocket &base_socket) : DSocket(base_s
BLI_assert(base_socket->is_output());
}
-inline const OutputSocketRef *DOutputSocket::socket_ref() const
-{
- return (const OutputSocketRef *)socket_ref_;
-}
-
-inline const OutputSocketRef *DOutputSocket::operator->() const
-{
- return (const OutputSocketRef *)socket_ref_;
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -451,9 +439,9 @@ inline const DTreeContext &DerivedNodeTree::root_context() const
return *root_context_;
}
-inline Span<const NodeTreeRef *> DerivedNodeTree::used_node_tree_refs() const
+inline Span<const bNodeTree *> DerivedNodeTree::used_btrees() const
{
- return used_node_tree_refs_;
+ return used_btrees_;
}
/** \} */
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 86c276fbd6f..a5e8ac2b093 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -49,17 +49,18 @@ void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_deform_curves_on_surface(void);
void register_node_type_geo_delete_geometry(void);
-void register_node_type_geo_duplicate_elements(void);
+void register_node_type_geo_distribute_points_in_volume(void);
void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_dual_mesh(void);
+void register_node_type_geo_duplicate_elements(void);
+void register_node_type_geo_edge_paths_to_curves(void);
+void register_node_type_geo_edge_paths_to_selection(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_extrude_mesh(void);
void register_node_type_geo_field_at_index(void);
-void register_node_type_geo_field_on_domain(void);
void register_node_type_geo_flip_faces(void);
void register_node_type_geo_geometry_to_instance(void);
void register_node_type_geo_image_texture(void);
-void register_node_type_geo_input_named_attribute(void);
void register_node_type_geo_input_curve_handles(void);
void register_node_type_geo_input_curve_tilt(void);
void register_node_type_geo_input_id(void);
@@ -76,22 +77,26 @@ void register_node_type_geo_input_mesh_face_is_planar(void);
void register_node_type_geo_input_mesh_face_neighbors(void);
void register_node_type_geo_input_mesh_island(void);
void register_node_type_geo_input_mesh_vertex_neighbors(void);
+void register_node_type_geo_input_named_attribute(void);
void register_node_type_geo_input_normal(void);
void register_node_type_geo_input_position(void);
void register_node_type_geo_input_radius(void);
void register_node_type_geo_input_scene_time(void);
void register_node_type_geo_input_shade_smooth(void);
+void register_node_type_geo_input_shortest_edge_paths(void);
void register_node_type_geo_input_spline_cyclic(void);
void register_node_type_geo_input_spline_length(void);
void register_node_type_geo_input_spline_resolution(void);
void register_node_type_geo_input_tangent(void);
void register_node_type_geo_instance_on_points(void);
void register_node_type_geo_instances_to_points(void);
+void register_node_type_geo_interpolate_domain(void);
void register_node_type_geo_is_viewport(void);
void register_node_type_geo_join_geometry(void);
void register_node_type_geo_material_replace(void);
void register_node_type_geo_material_selection(void);
void register_node_type_geo_merge_by_distance(void);
+void register_node_type_geo_mesh_face_set_boundaries(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);
@@ -105,9 +110,9 @@ void register_node_type_geo_mesh_to_curve(void);
void register_node_type_geo_mesh_to_points(void);
void register_node_type_geo_mesh_to_volume(void);
void register_node_type_geo_object_info(void);
-void register_node_type_geo_points(void);
void register_node_type_geo_points_to_vertices(void);
void register_node_type_geo_points_to_volume(void);
+void register_node_type_geo_points(void);
void register_node_type_geo_proximity(void);
void register_node_type_geo_raycast(void);
void register_node_type_geo_realize_instances(void);
@@ -138,11 +143,11 @@ void register_node_type_geo_transfer_attribute(void);
void register_node_type_geo_transform(void);
void register_node_type_geo_translate_instances(void);
void register_node_type_geo_triangulate(void);
+void register_node_type_geo_uv_pack_islands(void);
+void register_node_type_geo_uv_unwrap(void);
void register_node_type_geo_viewer(void);
void register_node_type_geo_volume_cube(void);
void register_node_type_geo_volume_to_mesh(void);
-void register_node_type_geo_uv_pack_islands(void);
-void register_node_type_geo_uv_unwrap(void);
#ifdef __cplusplus
}
diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh
index 0d5ba6cf5db..73e82f741ab 100644
--- a/source/blender/nodes/NOD_geometry_exec.hh
+++ b/source/blender/nodes/NOD_geometry_exec.hh
@@ -3,6 +3,7 @@
#pragma once
#include "FN_field.hh"
+#include "FN_lazy_function.hh"
#include "FN_multi_function_builder.hh"
#include "BKE_geometry_fields.hh"
@@ -11,9 +12,8 @@
#include "DNA_node_types.h"
#include "NOD_derived_node_tree.hh"
-#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_geometry_nodes_lazy_function.hh"
-struct Depsgraph;
struct ModifierData;
namespace blender::nodes {
@@ -28,8 +28,6 @@ using bke::AttributeReader;
using bke::AttributeWriter;
using bke::GAttributeReader;
using bke::GAttributeWriter;
-using bke::GeometryComponentFieldContext;
-using bke::GeometryFieldInput;
using bke::GSpanAttributeWriter;
using bke::MutableAttributeAccessor;
using bke::SpanAttributeWriter;
@@ -42,75 +40,18 @@ using fn::FieldInput;
using fn::FieldOperation;
using fn::GField;
using fn::ValueOrField;
-using geometry_nodes_eval_log::eNamedAttrUsage;
-using geometry_nodes_eval_log::NodeWarningType;
-
-/**
- * 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;
- geometry_nodes_eval_log::GeoLogger *logger = 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;
-
- virtual void set_default_remaining_outputs() = 0;
-};
+using geo_eval_log::NamedAttributeUsage;
+using geo_eval_log::NodeWarningType;
class GeoNodeExecParams {
private:
- GeoNodeExecParamsProvider *provider_;
+ const bNode &node_;
+ lf::Params &params_;
+ const lf::Context &lf_context_;
public:
- GeoNodeExecParams(GeoNodeExecParamsProvider &provider) : provider_(&provider)
+ GeoNodeExecParams(const bNode &node, lf::Params &params, const lf::Context &lf_context)
+ : node_(node), params_(params), lf_context_(lf_context)
{
}
@@ -121,20 +62,6 @@ class GeoNodeExecParams {
/**
* Get the input value for the input socket with the given identifier.
*
- * The node calling becomes responsible for destructing the value before it is done
- * executing. This method can only be called once for each identifier.
- */
- GMutablePointer extract_input(StringRef identifier)
- {
-#ifdef DEBUG
- this->check_input_access(identifier);
-#endif
- return provider_->extract_input(identifier);
- }
-
- /**
- * Get the input value for the input socket with the given identifier.
- *
* This method can only be called once for each identifier.
*/
template<typename T> T extract_input(StringRef identifier)
@@ -153,8 +80,8 @@ class GeoNodeExecParams {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
#endif
- GMutablePointer gvalue = this->extract_input(identifier);
- T value = gvalue.relocate_out<T>();
+ const int index = this->get_input_index(identifier);
+ T value = params_.extract_input<T>(index);
if constexpr (std::is_same_v<T, GeometrySet>) {
this->check_input_geometry_set(identifier, value);
}
@@ -163,27 +90,7 @@ class GeoNodeExecParams {
}
void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const;
-
- /**
- * Get input as vector for multi input socket with the given identifier.
- *
- * This method can only be called once for each identifier.
- */
- template<typename T> Vector<T> extract_multi_input(StringRef identifier)
- {
- Vector<GMutablePointer> gvalues = provider_->extract_multi_input(identifier);
- Vector<T> values;
- for (GMutablePointer gvalue : gvalues) {
- if constexpr (is_field_base_type_v<T>) {
- const ValueOrField<T> value_or_field = gvalue.relocate_out<ValueOrField<T>>();
- values.append(value_or_field.as_value());
- }
- else {
- values.append(gvalue.relocate_out<T>());
- }
- }
- return values;
- }
+ void check_output_geometry_set(const GeometrySet &geometry_set) const;
/**
* Get the input value for the input socket with the given identifier.
@@ -203,9 +110,8 @@ class GeoNodeExecParams {
#ifdef DEBUG
this->check_input_access(identifier, &CPPType::get<T>());
#endif
- GPointer gvalue = provider_->get_input(identifier);
- BLI_assert(gvalue.is_type<T>());
- const T &value = *(const T *)gvalue.get();
+ const int index = this->get_input_index(identifier);
+ const T &value = params_.get_input<T>(index);
if constexpr (std::is_same_v<T, GeometrySet>) {
this->check_input_geometry_set(identifier, value);
}
@@ -227,22 +133,37 @@ class GeoNodeExecParams {
this->set_output(identifier, ValueOrField<BaseType>(std::forward<T>(value)));
}
else {
- const CPPType &type = CPPType::get<StoredT>();
#ifdef DEBUG
+ const CPPType &type = CPPType::get<StoredT>();
this->check_output_access(identifier, type);
#endif
- GMutablePointer gvalue = provider_->alloc_output_value(type);
- new (gvalue.get()) StoredT(std::forward<T>(value));
- provider_->set_output(identifier, gvalue);
+ if constexpr (std::is_same_v<StoredT, GeometrySet>) {
+ this->check_output_geometry_set(value);
+ }
+ const int index = this->get_output_index(identifier);
+ params_.set_output(index, std::forward<T>(value));
}
}
+ geo_eval_log::GeoTreeLogger *get_local_tree_logger() const
+ {
+ GeoNodesLFUserData *user_data = this->user_data();
+ BLI_assert(user_data != nullptr);
+ const ComputeContext *compute_context = user_data->compute_context;
+ BLI_assert(compute_context != nullptr);
+ if (user_data->modifier_data->eval_log == nullptr) {
+ return nullptr;
+ }
+ return &user_data->modifier_data->eval_log->get_local_tree_logger(*compute_context);
+ }
+
/**
* Tell the evaluator that a specific input won't be used anymore.
*/
void set_input_unused(StringRef identifier)
{
- provider_->set_input_unused(identifier);
+ const int index = this->get_input_index(identifier);
+ params_.set_input_unused(index);
}
/**
@@ -252,7 +173,8 @@ class GeoNodeExecParams {
*/
bool output_is_required(StringRef identifier) const
{
- return provider_->output_is_required(identifier);
+ const int index = this->get_output_index(identifier);
+ return params_.get_output_usage(index) != lf::ValueUsage::Unused;
}
/**
@@ -263,7 +185,8 @@ class GeoNodeExecParams {
*/
bool lazy_require_input(StringRef identifier)
{
- return provider_->lazy_require_input(identifier);
+ const int index = this->get_input_index(identifier);
+ return params_.try_get_input_data_ptr_or_request(index) == nullptr;
}
/**
@@ -273,7 +196,8 @@ class GeoNodeExecParams {
*/
bool lazy_output_is_required(StringRef identifier)
{
- return provider_->lazy_output_is_required(identifier);
+ const int index = this->get_output_index(identifier);
+ return params_.get_output_usage(index) == lf::ValueUsage::Used;
}
/**
@@ -281,30 +205,45 @@ class GeoNodeExecParams {
*/
const bNode &node() const
{
- return *provider_->dnode->bnode();
+ return node_;
}
const Object *self_object() const
{
- return provider_->self_object;
+ if (const auto *data = this->user_data()) {
+ if (data->modifier_data) {
+ return data->modifier_data->self_object;
+ }
+ }
+ return nullptr;
}
Depsgraph *depsgraph() const
{
- return provider_->depsgraph;
+ if (const auto *data = this->user_data()) {
+ if (data->modifier_data) {
+ return data->modifier_data->depsgraph;
+ }
+ }
+ return nullptr;
+ }
+
+ GeoNodesLFUserData *user_data() const
+ {
+ return dynamic_cast<GeoNodesLFUserData *>(lf_context_.user_data);
}
/**
* Add an error message displayed at the top of the node when displaying the node tree,
* and potentially elsewhere in Blender.
*/
- void error_message_add(const NodeWarningType type, std::string message) const;
+ void error_message_add(const NodeWarningType type, StringRef message) const;
std::string attribute_producer_name() const;
void set_default_remaining_outputs();
- void used_named_attribute(std::string attribute_name, eNamedAttrUsage usage);
+ void used_named_attribute(StringRef attribute_name, NamedAttributeUsage usage);
private:
/* Utilities for detecting common errors at when using this class. */
@@ -313,6 +252,38 @@ class GeoNodeExecParams {
/* Find the active socket with the input name (not the identifier). */
const bNodeSocket *find_available_socket(const StringRef name) const;
+
+ int get_input_index(const StringRef identifier) const
+ {
+ int counter = 0;
+ for (const bNodeSocket *socket : node_.input_sockets()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ if (socket->identifier == identifier) {
+ return counter;
+ }
+ counter++;
+ }
+ BLI_assert_unreachable();
+ return -1;
+ }
+
+ int get_output_index(const StringRef identifier) const
+ {
+ int counter = 0;
+ for (const bNodeSocket *socket : node_.output_sockets()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ if (socket->identifier == identifier) {
+ return counter;
+ }
+ counter++;
+ }
+ BLI_assert_unreachable();
+ return -1;
+ }
};
} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
deleted file mode 100644
index 05c97c3903d..00000000000
--- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh
+++ /dev/null
@@ -1,406 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-/**
- * Many geometry nodes related UI features need access to data produced during evaluation. Not only
- * is the final output required but also the intermediate results. Those features include
- * attribute search, node warnings, socket inspection and the viewer node.
- *
- * This file provides the framework for logging data during evaluation and accessing the data after
- * evaluation.
- *
- * During logging every thread gets its own local logger to avoid too much locking (logging
- * generally happens for every socket). After geometry nodes evaluation is done, the thread-local
- * logging information is combined and post-processed to make it easier for the UI to lookup.
- * necessary information.
- */
-
-#include "BLI_enumerable_thread_specific.hh"
-#include "BLI_function_ref.hh"
-#include "BLI_generic_pointer.hh"
-#include "BLI_linear_allocator.hh"
-#include "BLI_map.hh"
-
-#include "BKE_geometry_set.hh"
-
-#include "NOD_derived_node_tree.hh"
-
-#include "FN_field.hh"
-
-#include <chrono>
-
-struct SpaceNode;
-struct SpaceSpreadsheet;
-
-namespace blender::nodes::geometry_nodes_eval_log {
-
-/** Contains information about a value that has been computed during geometry nodes evaluation. */
-class ValueLog {
- public:
- virtual ~ValueLog() = default;
-};
-
-/** Contains an owned copy of a value of a generic type. */
-class GenericValueLog : public ValueLog {
- private:
- GMutablePointer data_;
-
- public:
- GenericValueLog(GMutablePointer data) : data_(data)
- {
- }
-
- ~GenericValueLog()
- {
- data_.destruct();
- }
-
- GPointer value() const
- {
- return data_;
- }
-};
-
-class GFieldValueLog : public ValueLog {
- private:
- fn::GField field_;
- const CPPType &type_;
- Vector<std::string> input_tooltips_;
-
- public:
- GFieldValueLog(fn::GField field, bool log_full_field);
-
- const fn::GField &field() const
- {
- return field_;
- }
-
- Span<std::string> input_tooltips() const
- {
- return input_tooltips_;
- }
-
- const CPPType &type() const
- {
- return type_;
- }
-};
-
-struct GeometryAttributeInfo {
- std::string name;
- /** Can be empty when #name does not actually exist on a geometry yet. */
- std::optional<eAttrDomain> domain;
- std::optional<eCustomDataType> data_type;
-};
-
-/** Contains information about a geometry set. In most cases this does not store the entire
- * geometry set as this would require too much memory. */
-class GeometryValueLog : public ValueLog {
- private:
- Vector<GeometryAttributeInfo> attributes_;
- Vector<GeometryComponentType> component_types_;
- std::unique_ptr<GeometrySet> full_geometry_;
-
- public:
- struct MeshInfo {
- int verts_num, edges_num, faces_num;
- };
- struct CurveInfo {
- int splines_num;
- };
- struct PointCloudInfo {
- int points_num;
- };
- struct InstancesInfo {
- int instances_num;
- };
-
- std::optional<MeshInfo> mesh_info;
- std::optional<CurveInfo> curve_info;
- std::optional<PointCloudInfo> pointcloud_info;
- std::optional<InstancesInfo> instances_info;
-
- GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false);
-
- Span<GeometryAttributeInfo> attributes() const
- {
- return attributes_;
- }
-
- Span<GeometryComponentType> component_types() const
- {
- return component_types_;
- }
-
- const GeometrySet *full_geometry() const
- {
- return full_geometry_.get();
- }
-};
-
-enum class NodeWarningType {
- Error,
- Warning,
- Info,
-};
-
-struct NodeWarning {
- NodeWarningType type;
- std::string message;
-};
-
-struct NodeWithWarning {
- DNode node;
- NodeWarning warning;
-};
-
-struct NodeWithExecutionTime {
- DNode node;
- std::chrono::microseconds exec_time;
-};
-
-struct NodeWithDebugMessage {
- DNode node;
- std::string message;
-};
-
-/** The same value can be referenced by multiple sockets when they are linked. */
-struct ValueOfSockets {
- Span<DSocket> sockets;
- destruct_ptr<ValueLog> value;
-};
-
-enum class eNamedAttrUsage {
- None = 0,
- Read = 1 << 0,
- Write = 1 << 1,
- Remove = 1 << 2,
-};
-ENUM_OPERATORS(eNamedAttrUsage, eNamedAttrUsage::Remove);
-
-struct UsedNamedAttribute {
- std::string name;
- eNamedAttrUsage usage;
-};
-
-struct NodeWithUsedNamedAttribute {
- DNode node;
- UsedNamedAttribute attribute;
-};
-
-class GeoLogger;
-class ModifierLog;
-
-/** Every thread has its own local logger to avoid having to communicate between threads during
- * evaluation. After evaluation the individual logs are combined. */
-class LocalGeoLogger {
- private:
- /* Back pointer to the owner of this local logger. */
- GeoLogger *main_logger_;
- /* Allocator for the many small allocations during logging. This is in a `unique_ptr` so that
- * ownership can be transferred later on. */
- std::unique_ptr<LinearAllocator<>> allocator_;
- Vector<ValueOfSockets> values_;
- Vector<NodeWithWarning> node_warnings_;
- Vector<NodeWithExecutionTime> node_exec_times_;
- Vector<NodeWithDebugMessage> node_debug_messages_;
- Vector<NodeWithUsedNamedAttribute> used_named_attributes_;
-
- friend ModifierLog;
-
- public:
- LocalGeoLogger(GeoLogger &main_logger) : main_logger_(&main_logger)
- {
- this->allocator_ = std::make_unique<LinearAllocator<>>();
- }
-
- void log_value_for_sockets(Span<DSocket> sockets, GPointer value);
- void log_multi_value_socket(DSocket socket, Span<GPointer> values);
- void log_node_warning(DNode node, NodeWarningType type, std::string message);
- void log_execution_time(DNode node, std::chrono::microseconds exec_time);
- void log_used_named_attribute(DNode node, std::string attribute_name, eNamedAttrUsage usage);
- /**
- * Log a message that will be displayed in the node editor next to the node.
- * This should only be used for debugging purposes and not to display information to users.
- */
- void log_debug_message(DNode node, std::string message);
-};
-
-/** The root logger class. */
-class GeoLogger {
- private:
- /**
- * Log the entire value for these sockets, because they may be inspected afterwards.
- * We don't log everything, because that would take up too much memory and cause significant
- * slowdowns.
- */
- Set<DSocket> log_full_sockets_;
- threading::EnumerableThreadSpecific<LocalGeoLogger> threadlocals_;
-
- /* These are only optional since they don't have a default constructor. */
- std::unique_ptr<GeometryValueLog> input_geometry_log_;
- std::unique_ptr<GeometryValueLog> output_geometry_log_;
-
- friend LocalGeoLogger;
- friend ModifierLog;
-
- public:
- GeoLogger(Set<DSocket> log_full_sockets)
- : log_full_sockets_(std::move(log_full_sockets)),
- threadlocals_([this]() { return LocalGeoLogger(*this); })
- {
- }
-
- void log_input_geometry(const GeometrySet &geometry)
- {
- input_geometry_log_ = std::make_unique<GeometryValueLog>(geometry);
- }
-
- void log_output_geometry(const GeometrySet &geometry)
- {
- output_geometry_log_ = std::make_unique<GeometryValueLog>(geometry);
- }
-
- LocalGeoLogger &local()
- {
- return threadlocals_.local();
- }
-
- auto begin()
- {
- return threadlocals_.begin();
- }
-
- auto end()
- {
- return threadlocals_.end();
- }
-};
-
-/** Contains information that has been logged for one specific socket. */
-class SocketLog {
- private:
- ValueLog *value_ = nullptr;
-
- friend ModifierLog;
-
- public:
- const ValueLog *value() const
- {
- return value_;
- }
-};
-
-/** Contains information that has been logged for one specific node. */
-class NodeLog {
- private:
- Vector<SocketLog> input_logs_;
- Vector<SocketLog> output_logs_;
- Vector<NodeWarning, 0> warnings_;
- Vector<std::string, 0> debug_messages_;
- Vector<UsedNamedAttribute, 0> used_named_attributes_;
- std::chrono::microseconds exec_time_;
-
- friend ModifierLog;
-
- public:
- const SocketLog *lookup_socket_log(eNodeSocketInOut in_out, int index) const;
- const SocketLog *lookup_socket_log(const bNode &node, const bNodeSocket &socket) const;
- void execution_time(std::chrono::microseconds exec_time);
-
- Span<SocketLog> input_logs() const
- {
- return input_logs_;
- }
-
- Span<SocketLog> output_logs() const
- {
- return output_logs_;
- }
-
- Span<NodeWarning> warnings() const
- {
- return warnings_;
- }
-
- Span<std::string> debug_messages() const
- {
- return debug_messages_;
- }
-
- Span<UsedNamedAttribute> used_named_attributes() const
- {
- return used_named_attributes_;
- }
-
- std::chrono::microseconds execution_time() const
- {
- return exec_time_;
- }
-
- Vector<const GeometryAttributeInfo *> lookup_available_attributes() const;
-};
-
-/** Contains information that has been logged for one specific tree. */
-class TreeLog {
- private:
- Map<std::string, destruct_ptr<NodeLog>> node_logs_;
- Map<std::string, destruct_ptr<TreeLog>> child_logs_;
-
- friend ModifierLog;
-
- public:
- const NodeLog *lookup_node_log(StringRef node_name) const;
- const NodeLog *lookup_node_log(const bNode &node) const;
- const TreeLog *lookup_child_log(StringRef node_name) const;
- void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const;
-};
-
-/** Contains information about an entire geometry nodes evaluation. */
-class ModifierLog {
- private:
- LinearAllocator<> allocator_;
- /* Allocators of the individual loggers. */
- Vector<std::unique_ptr<LinearAllocator<>>> logger_allocators_;
- destruct_ptr<TreeLog> root_tree_logs_;
- Vector<destruct_ptr<ValueLog>> logged_values_;
-
- std::unique_ptr<GeometryValueLog> input_geometry_log_;
- std::unique_ptr<GeometryValueLog> output_geometry_log_;
-
- public:
- ModifierLog(GeoLogger &logger);
-
- const TreeLog &root_tree() const
- {
- return *root_tree_logs_;
- }
-
- /* Utilities to find logged information for a specific context. */
- static const ModifierLog *find_root_by_node_editor_context(const SpaceNode &snode);
- static const TreeLog *find_tree_by_node_editor_context(const SpaceNode &snode);
- static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode,
- const bNode &node);
- static const NodeLog *find_node_by_node_editor_context(const SpaceNode &snode,
- const StringRef node_name);
- static const SocketLog *find_socket_by_node_editor_context(const SpaceNode &snode,
- const bNode &node,
- const bNodeSocket &socket);
- static const NodeLog *find_node_by_spreadsheet_editor_context(
- const SpaceSpreadsheet &sspreadsheet);
- void foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const;
-
- const GeometryValueLog *input_geometry_log() const;
- const GeometryValueLog *output_geometry_log() const;
-
- private:
- using LogByTreeContext = Map<const DTreeContext *, TreeLog *>;
-
- TreeLog &lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context,
- const DTreeContext &tree_context);
- NodeLog &lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node);
- SocketLog &lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context, DSocket socket);
-};
-
-} // namespace blender::nodes::geometry_nodes_eval_log
diff --git a/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh
new file mode 100644
index 00000000000..240a0115f68
--- /dev/null
+++ b/source/blender/nodes/NOD_geometry_nodes_lazy_function.hh
@@ -0,0 +1,181 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/**
+ * For evaluation, geometry node groups are converted to a lazy-function graph. The generated graph
+ * is cached per node group, so it only has to be generated once after a change.
+ *
+ * Node groups are *not* inlined into the lazy-function graph. This could be added in the future as
+ * it might improve performance in some cases, but generally does not seem necessary. Inlining node
+ * groups also has disadvantages like making per-node-group caches less useful, resulting in more
+ * overhead.
+ *
+ * Instead, group nodes are just like all other nodes in the lazy-function graph. What makes them
+ * special is that they reference the lazy-function graph of the group they reference.
+ *
+ * During lazy-function graph generation, a mapping between the #bNodeTree and
+ * #lazy_function::Graph is build that can be used when evaluating the graph (e.g. for logging).
+ */
+
+#include "FN_lazy_function_graph.hh"
+#include "FN_lazy_function_graph_executor.hh"
+
+#include "NOD_geometry_nodes_log.hh"
+#include "NOD_multi_function.hh"
+
+#include "BLI_compute_context.hh"
+
+struct Object;
+struct Depsgraph;
+
+namespace blender::nodes {
+
+namespace lf = fn::lazy_function;
+using lf::LazyFunction;
+
+/**
+ * Data that is passed into geometry nodes evaluation from the modifier.
+ */
+struct GeoNodesModifierData {
+ /** Object that is currently evaluated. */
+ const Object *self_object = nullptr;
+ /** Depsgraph that is evaluating the modifier. */
+ Depsgraph *depsgraph = nullptr;
+ /** Optional logger. */
+ geo_eval_log::GeoModifierLog *eval_log = nullptr;
+ /**
+ * Some nodes should be executed even when their output is not used (e.g. active viewer nodes and
+ * the node groups they are contained in).
+ */
+ const MultiValueMap<ComputeContextHash, const lf::FunctionNode *> *side_effect_nodes;
+};
+
+/**
+ * Custom user data that is passed to every geometry nodes related lazy-function evaluation.
+ */
+struct GeoNodesLFUserData : public lf::UserData {
+ /**
+ * Data from the modifier that is being evaluated.
+ */
+ GeoNodesModifierData *modifier_data = nullptr;
+ /**
+ * Current compute context. This is different depending in the (nested) node group that is being
+ * evaluated.
+ */
+ const ComputeContext *compute_context = nullptr;
+};
+
+/**
+ * Contains the mapping between the #bNodeTree and the corresponding lazy-function graph.
+ * This is *not* a one-to-one mapping.
+ */
+struct GeometryNodeLazyFunctionGraphMapping {
+ /**
+ * Contains mapping of sockets for special nodes like group input and group output.
+ */
+ Map<const bNodeSocket *, lf::Socket *> dummy_socket_map;
+ /**
+ * The inputs sockets in the graph. Multiple group input nodes are combined into one in the
+ * lazy-function graph.
+ */
+ Vector<lf::OutputSocket *> group_input_sockets;
+ /**
+ * A mapping used for logging intermediate values.
+ */
+ MultiValueMap<const lf::Socket *, const bNodeSocket *> bsockets_by_lf_socket_map;
+ /**
+ * Mappings for some special node types. Generally, this mapping does not exist for all node
+ * types, so better have more specialized mappings for now.
+ */
+ Map<const bNode *, const lf::FunctionNode *> group_node_map;
+ Map<const bNode *, const lf::FunctionNode *> viewer_node_map;
+};
+
+/**
+ * Data that is cached for every #bNodeTree.
+ */
+struct GeometryNodesLazyFunctionGraphInfo {
+ /**
+ * Allocator used for many things contained in this struct.
+ */
+ LinearAllocator<> allocator;
+ /**
+ * Many nodes are implemented as multi-functions. So this contains a mapping from nodes to their
+ * corresponding multi-functions.
+ */
+ std::unique_ptr<NodeMultiFunctions> node_multi_functions;
+ /**
+ * Many lazy-functions are build for the lazy-function graph. Since the graph does not own them,
+ * we have to keep track of them separately.
+ */
+ Vector<std::unique_ptr<LazyFunction>> functions;
+ /**
+ * Many sockets have default values. Since those are not owned by the lazy-function graph, we
+ * have to keep track of them separately. This only owns the values, the memory is owned by the
+ * allocator above.
+ */
+ Vector<GMutablePointer> values_to_destruct;
+ /**
+ * The actual lazy-function graph.
+ */
+ lf::Graph graph;
+ /**
+ * Mappings between the lazy-function graph and the #bNodeTree.
+ */
+ GeometryNodeLazyFunctionGraphMapping mapping;
+ /**
+ * Approximate number of nodes in the graph if all sub-graphs were inlined.
+ * This can be used as a simple heuristic for the complexity of the node group.
+ */
+ int num_inline_nodes_approximate = 0;
+
+ GeometryNodesLazyFunctionGraphInfo();
+ ~GeometryNodesLazyFunctionGraphInfo();
+};
+
+/**
+ * Logs intermediate values from the lazy-function graph evaluation into #GeoModifierLog based on
+ * the mapping between the lazy-function graph and the corresponding #bNodeTree.
+ */
+class GeometryNodesLazyFunctionLogger : public fn::lazy_function::GraphExecutor::Logger {
+ private:
+ const GeometryNodesLazyFunctionGraphInfo &lf_graph_info_;
+
+ public:
+ GeometryNodesLazyFunctionLogger(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info);
+ void log_socket_value(const fn::lazy_function::Socket &lf_socket,
+ GPointer value,
+ const fn::lazy_function::Context &context) const override;
+ void dump_when_outputs_are_missing(const lf::FunctionNode &node,
+ Span<const lf::OutputSocket *> missing_sockets,
+ const lf::Context &context) const override;
+ void dump_when_input_is_set_twice(const lf::InputSocket &target_socket,
+ const lf::OutputSocket &from_socket,
+ const lf::Context &context) const override;
+ void log_before_node_execute(const lf::FunctionNode &node,
+ const lf::Params &params,
+ const lf::Context &context) const override;
+};
+
+/**
+ * Tells the lazy-function graph evaluator which nodes have side effects based on the current
+ * context. For example, the same viewer node can have side effects in one context, but not in
+ * another (depending on e.g. which tree path is currently viewed in the node editor).
+ */
+class GeometryNodesLazyFunctionSideEffectProvider
+ : public fn::lazy_function::GraphExecutor::SideEffectProvider {
+ public:
+ Vector<const lf::FunctionNode *> get_nodes_with_side_effects(
+ const lf::Context &context) const override;
+};
+
+/**
+ * Main function that converts a #bNodeTree into a lazy-function graph. If the graph has been
+ * generated already, nothing is done. Under some circumstances a valid graph cannot be created. In
+ * those cases null is returned.
+ */
+const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph(
+ const bNodeTree &btree);
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_geometry_nodes_log.hh b/source/blender/nodes/NOD_geometry_nodes_log.hh
new file mode 100644
index 00000000000..cf59c99bc79
--- /dev/null
+++ b/source/blender/nodes/NOD_geometry_nodes_log.hh
@@ -0,0 +1,340 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#pragma once
+
+/**
+ * Many geometry nodes related UI features need access to data produced during evaluation. Not only
+ * is the final output required but also the intermediate results. Those features include attribute
+ * search, node warnings, socket inspection and the viewer node.
+ *
+ * This file provides the system for logging data during evaluation and accessing the data after
+ * evaluation. Geometry nodes is executed by a modifier, therefore the "root" of logging is
+ * #GeoModifierLog which will contain all data generated in a modifier.
+ *
+ * The system makes a distinction between "loggers" and the "log":
+ * - Logger (#GeoTreeLogger): Is used during geometry nodes evaluation. Each thread logs data
+ * independently to avoid communication between threads. Logging should generally be fast.
+ * Generally, the logged data is just dumped into simple containers. Any processing of the data
+ * happens later if necessary. This is important for performance, because in practice, most of
+ * the logged data is never used again. So any processing of the data is likely to be a waste of
+ * resources.
+ * - Log (#GeoTreeLog, #GeoNodeLog): Those are used when accessing logged data in UI code. They
+ * contain and cache preprocessed data produced during logging. The log combines data from all
+ * thread-local loggers to provide simple access. Importantly, the (preprocessed) log is only
+ * created when it is actually used by UI code.
+ */
+
+#include <chrono>
+
+#include "BLI_compute_context.hh"
+#include "BLI_enumerable_thread_specific.hh"
+#include "BLI_generic_pointer.hh"
+#include "BLI_multi_value_map.hh"
+
+#include "BKE_attribute.h"
+#include "BKE_geometry_set.hh"
+
+#include "FN_field.hh"
+
+#include "DNA_node_types.h"
+
+struct SpaceNode;
+struct SpaceSpreadsheet;
+struct NodesModifierData;
+
+namespace blender::nodes::geo_eval_log {
+
+using fn::GField;
+
+enum class NodeWarningType {
+ Error,
+ Warning,
+ Info,
+};
+
+struct NodeWarning {
+ NodeWarningType type;
+ std::string message;
+};
+
+enum class NamedAttributeUsage {
+ None = 0,
+ Read = 1 << 0,
+ Write = 1 << 1,
+ Remove = 1 << 2,
+};
+ENUM_OPERATORS(NamedAttributeUsage, NamedAttributeUsage::Remove);
+
+/**
+ * Values of different types are logged differently. This is necessary because some types are so
+ * simple that we can log them entirely (e.g. `int`), while we don't want to log all intermediate
+ * geometries in their entirety.
+ *
+ * #ValueLog is a base class for the different ways we log values.
+ */
+class ValueLog {
+ public:
+ virtual ~ValueLog() = default;
+};
+
+/**
+ * Simplest logger. It just stores a copy of the entire value. This is used for most simple types
+ * like `int`.
+ */
+class GenericValueLog : public ValueLog {
+ public:
+ /**
+ * This is owning the value, but not the memory.
+ */
+ GMutablePointer value;
+
+ GenericValueLog(const GMutablePointer value) : value(value)
+ {
+ }
+
+ ~GenericValueLog();
+};
+
+/**
+ * Fields are not logged entirely, because they might contain arbitrarily large data (e.g.
+ * geometries that are sampled). Instead, only the data needed for UI features is logged.
+ */
+class FieldInfoLog : public ValueLog {
+ public:
+ const CPPType &type;
+ Vector<std::string> input_tooltips;
+
+ FieldInfoLog(const GField &field);
+};
+
+struct GeometryAttributeInfo {
+ std::string name;
+ /** Can be empty when #name does not actually exist on a geometry yet. */
+ std::optional<eAttrDomain> domain;
+ std::optional<eCustomDataType> data_type;
+};
+
+/**
+ * Geometries are not logged entirely, because that would result in a lot of time and memory
+ * overhead. Instead, only the data needed for UI features is logged.
+ */
+class GeometryInfoLog : public ValueLog {
+ public:
+ Vector<GeometryAttributeInfo> attributes;
+ Vector<GeometryComponentType> component_types;
+
+ struct MeshInfo {
+ int verts_num, edges_num, faces_num;
+ };
+ struct CurveInfo {
+ int splines_num;
+ };
+ struct PointCloudInfo {
+ int points_num;
+ };
+ struct InstancesInfo {
+ int instances_num;
+ };
+ struct EditDataInfo {
+ bool has_deformed_positions;
+ bool has_deform_matrices;
+ };
+
+ std::optional<MeshInfo> mesh_info;
+ std::optional<CurveInfo> curve_info;
+ std::optional<PointCloudInfo> pointcloud_info;
+ std::optional<InstancesInfo> instances_info;
+ std::optional<EditDataInfo> edit_data_info;
+
+ GeometryInfoLog(const GeometrySet &geometry_set);
+};
+
+/**
+ * Data logged by a viewer node when it is executed. In this case, we do want to log the entire
+ * geometry.
+ */
+class ViewerNodeLog {
+ public:
+ GeometrySet geometry;
+ GField field;
+};
+
+using Clock = std::chrono::steady_clock;
+using TimePoint = Clock::time_point;
+
+/**
+ * Logs all data for a specific geometry node tree in a specific context. When the same node group
+ * is used in multiple times each instantiation will have a separate logger.
+ */
+class GeoTreeLogger {
+ public:
+ std::optional<ComputeContextHash> parent_hash;
+ std::optional<StringRefNull> group_node_name;
+ Vector<ComputeContextHash> children_hashes;
+
+ LinearAllocator<> *allocator = nullptr;
+
+ struct WarningWithNode {
+ StringRefNull node_name;
+ NodeWarning warning;
+ };
+ struct SocketValueLog {
+ StringRefNull node_name;
+ StringRefNull socket_identifier;
+ destruct_ptr<ValueLog> value;
+ };
+ struct NodeExecutionTime {
+ StringRefNull node_name;
+ TimePoint start;
+ TimePoint end;
+ };
+ struct ViewerNodeLogWithNode {
+ StringRefNull node_name;
+ destruct_ptr<ViewerNodeLog> viewer_log;
+ };
+ struct AttributeUsageWithNode {
+ StringRefNull node_name;
+ StringRefNull attribute_name;
+ NamedAttributeUsage usage;
+ };
+ struct DebugMessage {
+ StringRefNull node_name;
+ StringRefNull message;
+ };
+
+ Vector<WarningWithNode> node_warnings;
+ Vector<SocketValueLog> input_socket_values;
+ Vector<SocketValueLog> output_socket_values;
+ Vector<NodeExecutionTime> node_execution_times;
+ Vector<ViewerNodeLogWithNode, 0> viewer_node_logs;
+ Vector<AttributeUsageWithNode, 0> used_named_attributes;
+ Vector<DebugMessage, 0> debug_messages;
+
+ GeoTreeLogger();
+ ~GeoTreeLogger();
+
+ void log_value(const bNode &node, const bNodeSocket &socket, GPointer value);
+ void log_viewer_node(const bNode &viewer_node, const GeometrySet &geometry, const GField &field);
+};
+
+/**
+ * Contains data that has been logged for a specific node in a context. So when the node is in a
+ * node group that is used multiple times, there will be a different #GeoNodeLog for every
+ * instance.
+ *
+ * By default, not all of the info below is valid. A #GeoTreeLog::ensure_* method has to be called
+ * first.
+ */
+class GeoNodeLog {
+ public:
+ /** Warnings generated for that node. */
+ Vector<NodeWarning> warnings;
+ /**
+ * Time spend in that node. For node groups this is the sum of the run times of the nodes
+ * inside.
+ */
+ std::chrono::nanoseconds run_time{0};
+ /** Maps from socket identifiers to their values. */
+ Map<StringRefNull, ValueLog *> input_values_;
+ Map<StringRefNull, ValueLog *> output_values_;
+ /** Maps from attribute name to their usage flags. */
+ Map<StringRefNull, NamedAttributeUsage> used_named_attributes;
+ /** Messages that are used for debugging purposes during development. */
+ Vector<StringRefNull> debug_messages;
+
+ GeoNodeLog();
+ ~GeoNodeLog();
+};
+
+class GeoModifierLog;
+
+/**
+ * Contains data that has been logged for a specific node group in a context. If the same node
+ * group is used multiple times, there will be a different #GeoTreeLog for every instance.
+ *
+ * This contains lazily evaluated data. Call the corresponding `ensure_*` methods before accessing
+ * data.
+ */
+class GeoTreeLog {
+ private:
+ GeoModifierLog *modifier_log_;
+ Vector<GeoTreeLogger *> tree_loggers_;
+ VectorSet<ComputeContextHash> children_hashes_;
+ bool reduced_node_warnings_ = false;
+ bool reduced_node_run_times_ = false;
+ bool reduced_socket_values_ = false;
+ bool reduced_viewer_node_logs_ = false;
+ bool reduced_existing_attributes_ = false;
+ bool reduced_used_named_attributes_ = false;
+ bool reduced_debug_messages_ = false;
+
+ public:
+ Map<StringRefNull, GeoNodeLog> nodes;
+ Map<StringRefNull, ViewerNodeLog *, 0> viewer_node_logs;
+ Vector<NodeWarning> all_warnings;
+ std::chrono::nanoseconds run_time_sum{0};
+ Vector<const GeometryAttributeInfo *> existing_attributes;
+ Map<StringRefNull, NamedAttributeUsage> used_named_attributes;
+
+ GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers);
+ ~GeoTreeLog();
+
+ void ensure_node_warnings();
+ void ensure_node_run_time();
+ void ensure_socket_values();
+ void ensure_viewer_node_logs();
+ void ensure_existing_attributes();
+ void ensure_used_named_attributes();
+ void ensure_debug_messages();
+
+ ValueLog *find_socket_value_log(const bNodeSocket &query_socket);
+};
+
+/**
+ * There is one #GeoModifierLog for every modifier that evaluates geometry nodes. It contains all
+ * the loggers that are used during evaluation as well as the preprocessed logs that are used by UI
+ * code.
+ */
+class GeoModifierLog {
+ private:
+ /** Data that is stored for each thread. */
+ struct LocalData {
+ /** Each thread has its own allocator. */
+ LinearAllocator<> allocator;
+ /**
+ * Store a separate #GeoTreeLogger for each instance of the corresponding node group (e.g.
+ * when the same node group is used multiple times).
+ */
+ Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> tree_logger_by_context;
+ };
+
+ /** Container for all thread-local data. */
+ threading::EnumerableThreadSpecific<LocalData> data_per_thread_;
+ /**
+ * A #GeoTreeLog for every compute context. Those are created lazily when requested by UI code.
+ */
+ Map<ComputeContextHash, std::unique_ptr<GeoTreeLog>> tree_logs_;
+
+ public:
+ GeoModifierLog();
+ ~GeoModifierLog();
+
+ /**
+ * Get a thread-local logger for the current node tree.
+ */
+ GeoTreeLogger &get_local_tree_logger(const ComputeContext &compute_context);
+
+ /**
+ * Get a log a specific node tree instance.
+ */
+ GeoTreeLog &get_tree_log(const ComputeContextHash &compute_context_hash);
+
+ /**
+ * Utility accessor to logged data.
+ */
+ static GeoTreeLog *get_tree_log_for_node_editor(const SpaceNode &snode);
+ static const ViewerNodeLog *find_viewer_node_log_for_spreadsheet(
+ const SpaceSpreadsheet &sspreadsheet);
+};
+
+} // namespace blender::nodes::geo_eval_log
diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh
index b6d51578b1c..676bf03927e 100644
--- a/source/blender/nodes/NOD_multi_function.hh
+++ b/source/blender/nodes/NOD_multi_function.hh
@@ -6,8 +6,6 @@
#include "DNA_node_types.h"
-#include "NOD_derived_node_tree.hh"
-
namespace blender::nodes {
using namespace fn::multi_function_types;
@@ -19,15 +17,15 @@ class NodeMultiFunctions;
*/
class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
private:
- bNode &node_;
- bNodeTree &tree_;
+ const bNode &node_;
+ const bNodeTree &tree_;
std::shared_ptr<MultiFunction> owned_built_fn_;
const MultiFunction *built_fn_ = nullptr;
friend NodeMultiFunctions;
public:
- NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree);
+ NodeMultiFunctionBuilder(const bNode &node, const bNodeTree &tree);
/**
* Assign a multi-function for the current node. The input and output parameters of the function
@@ -42,8 +40,8 @@ class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
*/
template<typename T, typename... Args> void construct_and_set_matching_fn(Args &&...args);
- bNode &node();
- bNodeTree &tree();
+ const bNode &node();
+ const bNodeTree &tree();
};
/**
@@ -60,26 +58,26 @@ class NodeMultiFunctions {
Map<const bNode *, Item> map_;
public:
- NodeMultiFunctions(const DerivedNodeTree &tree);
+ NodeMultiFunctions(const bNodeTree &tree);
- const Item &try_get(const DNode &node) const;
+ const Item &try_get(const bNode &node) const;
};
/* -------------------------------------------------------------------- */
/** \name #NodeMultiFunctionBuilder Inline Methods
* \{ */
-inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree)
+inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(const bNode &node, const bNodeTree &tree)
: node_(node), tree_(tree)
{
}
-inline bNode &NodeMultiFunctionBuilder::node()
+inline const bNode &NodeMultiFunctionBuilder::node()
{
return node_;
}
-inline bNodeTree &NodeMultiFunctionBuilder::tree()
+inline const bNodeTree &NodeMultiFunctionBuilder::tree()
{
return tree_;
}
@@ -107,10 +105,10 @@ inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...ar
/** \name #NodeMultiFunctions Inline Methods
* \{ */
-inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const
+inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const bNode &node) const
{
static Item empty_item;
- const Item *item = map_.lookup_ptr(node->bnode());
+ const Item *item = map_.lookup_ptr(&node);
if (item == nullptr) {
return empty_item;
}
diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh
index 4e78f6c1142..42755b2e8dd 100644
--- a/source/blender/nodes/NOD_node_declaration.hh
+++ b/source/blender/nodes/NOD_node_declaration.hh
@@ -88,6 +88,18 @@ class SocketDeclaration {
InputSocketFieldType input_field_type_ = InputSocketFieldType::None;
OutputFieldDependency output_field_dependency_;
+ /** The priority of the input for determining the domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ int compositor_domain_priority_ = 0;
+
+ /** This input shouldn't be realized on the operation domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ bool compositor_skip_realization_ = false;
+
+ /** This input expects a single value and can't operate on non-single values. See
+ * realtime_compositor::InputDescriptor for more information. */
+ bool compositor_expects_single_value_ = false;
+
/** Utility method to make the socket available if there is a straightforward way to do so. */
std::function<void(bNode &)> make_available_fn_;
@@ -124,6 +136,10 @@ class SocketDeclaration {
InputSocketFieldType input_field_type() const;
const OutputFieldDependency &output_field_dependency() const;
+ int compositor_domain_priority() const;
+ bool compositor_skip_realization() const;
+ bool compositor_expects_single_value() const;
+
protected:
void set_common_flags(bNodeSocket &socket) const;
bool matches_common_data(const bNodeSocket &socket) const;
@@ -238,6 +254,30 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
return *(Self *)this;
}
+ /** The priority of the input for determining the domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ Self &compositor_domain_priority(int priority)
+ {
+ decl_->compositor_domain_priority_ = priority;
+ return *(Self *)this;
+ }
+
+ /** This input shouldn't be realized on the operation domain of the node. See
+ * realtime_compositor::InputDescriptor for more information. */
+ Self &compositor_skip_realization(bool value = true)
+ {
+ decl_->compositor_skip_realization_ = value;
+ return *(Self *)this;
+ }
+
+ /** This input expects a single value and can't operate on non-single values. See
+ * realtime_compositor::InputDescriptor for more information. */
+ Self &compositor_expects_single_value(bool value = true)
+ {
+ decl_->compositor_expects_single_value_ = value;
+ return *(Self *)this;
+ }
+
/**
* Pass a function that sets properties on the node required to make the corresponding socket
* available, if it is not available on the default state of the node. The function is allowed to
@@ -428,6 +468,21 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency()
return output_field_dependency_;
}
+inline int SocketDeclaration::compositor_domain_priority() const
+{
+ return compositor_domain_priority_;
+}
+
+inline bool SocketDeclaration::compositor_skip_realization() const
+{
+ return compositor_skip_realization_;
+}
+
+inline bool SocketDeclaration::compositor_expects_single_value() const
+{
+ return compositor_expects_single_value_;
+}
+
inline void SocketDeclaration::make_available(bNode &node) const
{
if (make_available_fn_) {
diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh
deleted file mode 100644
index 257aa5f4110..00000000000
--- a/source/blender/nodes/NOD_node_tree_ref.hh
+++ /dev/null
@@ -1,760 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#pragma once
-
-/** \file
- * \ingroup nodes
- *
- * NodeTreeRef makes querying information about a bNodeTree more efficient. It is an immutable data
- * structure. It should not be used after anymore, after the underlying node tree changed.
- *
- * The following queries are supported efficiently:
- * - socket -> index of socket
- * - socket -> directly linked sockets
- * - socket -> directly linked links
- * - socket -> linked sockets when skipping reroutes
- * - socket -> node
- * - socket/node -> rna pointer
- * - node -> inputs/outputs
- * - node -> tree
- * - tree -> all nodes
- * - tree -> all (input/output) sockets
- * - idname -> nodes
- *
- * Every socket has an id. The id-space is shared between input and output sockets.
- * When storing data per socket, it is often better to use the id as index into an array, instead
- * of a hash table.
- *
- * Every node has an id as well. The same rule regarding hash tables applies.
- *
- * There is an utility to export this data structure as graph in dot format.
- */
-
-#include "BLI_array.hh"
-#include "BLI_function_ref.hh"
-#include "BLI_linear_allocator.hh"
-#include "BLI_map.hh"
-#include "BLI_multi_value_map.hh"
-#include "BLI_string_ref.hh"
-#include "BLI_timeit.hh"
-#include "BLI_utility_mixins.hh"
-#include "BLI_vector.hh"
-
-#include "BKE_node.h"
-#include "BKE_node_runtime.hh"
-
-#include "DNA_node_types.h"
-
-#include "RNA_access.h"
-
-namespace blender::nodes {
-
-class SocketRef;
-class InputSocketRef;
-class OutputSocketRef;
-class NodeRef;
-class NodeTreeRef;
-class LinkRef;
-class InternalLinkRef;
-
-using SocketIndexByIdentifierMap = Map<std::string, int>;
-
-class SocketRef : NonCopyable, NonMovable {
- protected:
- NodeRef *node_;
- bNodeSocket *bsocket_;
- bool is_input_;
- int id_;
- int index_;
- Vector<LinkRef *> directly_linked_links_;
-
- /* These sockets are linked directly, i.e. with a single link in between. */
- MutableSpan<const SocketRef *> directly_linked_sockets_;
- /* These sockets are linked when reroutes, muted links and muted nodes have been taken into
- * account. */
- 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;
-
- bool is_directly_linked() const;
- bool is_logically_linked() const;
-
- const NodeRef &node() const;
- const NodeTreeRef &tree() const;
-
- int id() const;
- int index() const;
-
- bool is_input() const;
- bool is_output() const;
-
- const SocketRef &as_base() const;
- const InputSocketRef &as_input() const;
- const OutputSocketRef &as_output() const;
-
- PointerRNA rna() const;
-
- StringRefNull idname() const;
- StringRefNull name() const;
- StringRefNull identifier() const;
- bNodeSocketType *typeinfo() const;
-
- bNodeSocket *bsocket() const;
- bNode *bnode() const;
- bNodeTree *btree() const;
-
- bool is_available() const;
- bool is_undefined() const;
-
- void *default_value() const;
- template<typename T> T *default_value() const;
-};
-
-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 *> &seen_sockets_stack) 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 *> &seen_sockets_stack) const;
-};
-
-class NodeRef : NonCopyable, NonMovable {
- private:
- NodeTreeRef *tree_;
- bNode *bnode_;
- int id_;
- Vector<InputSocketRef *> inputs_;
- Vector<OutputSocketRef *> outputs_;
- Vector<InternalLinkRef *> internal_links_;
- SocketIndexByIdentifierMap *input_index_by_identifier_;
- SocketIndexByIdentifierMap *output_index_by_identifier_;
-
- friend NodeTreeRef;
-
- public:
- const NodeTreeRef &tree() const;
-
- Span<const InputSocketRef *> inputs() const;
- Span<const OutputSocketRef *> outputs() const;
- Span<const InternalLinkRef *> internal_links() const;
- Span<const SocketRef *> sockets(eNodeSocketInOut in_out) const;
-
- 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;
-
- bool any_input_is_directly_linked() const;
- bool any_output_is_directly_linked() const;
- bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const;
-
- bNode *bnode() const;
- bNodeTree *btree() const;
-
- PointerRNA rna() const;
- StringRefNull idname() const;
- StringRefNull name() const;
- StringRefNull label() const;
- StringRefNull label_or_name() const;
- bNodeType *typeinfo() const;
- const NodeDeclaration *declaration() const;
-
- int id() const;
-
- bool is_reroute_node() const;
- bool is_group_node() const;
- bool is_group_input_node() const;
- 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;
-};
-
-class LinkRef : NonCopyable, NonMovable {
- private:
- OutputSocketRef *from_;
- InputSocketRef *to_;
- bNodeLink *blink_;
-
- friend NodeTreeRef;
-
- public:
- const OutputSocketRef &from() const;
- const InputSocketRef &to() const;
-
- bNodeLink *blink() const;
-
- bool is_muted() const;
-};
-
-class InternalLinkRef : NonCopyable, NonMovable {
- private:
- InputSocketRef *from_;
- OutputSocketRef *to_;
- bNodeLink *blink_;
-
- friend NodeTreeRef;
-
- public:
- const InputSocketRef &from() const;
- const OutputSocketRef &to() const;
-
- bNodeLink *blink() const;
-};
-
-class NodeTreeRef : NonCopyable, NonMovable {
- private:
- LinearAllocator<> allocator_;
- bNodeTree *btree_;
- Vector<NodeRef *> nodes_by_id_;
- Vector<SocketRef *> sockets_by_id_;
- Vector<InputSocketRef *> input_sockets_;
- Vector<OutputSocketRef *> output_sockets_;
- Vector<LinkRef *> links_;
- MultiValueMap<const bNodeType *, NodeRef *> nodes_by_type_;
- Vector<std::unique_ptr<SocketIndexByIdentifierMap>> owned_identifier_maps_;
- const NodeRef *group_output_node_ = nullptr;
-
- public:
- NodeTreeRef(bNodeTree *btree);
- ~NodeTreeRef();
-
- Span<const NodeRef *> nodes() const;
- Span<const NodeRef *> nodes_by_type(StringRefNull idname) const;
- Span<const NodeRef *> nodes_by_type(const bNodeType *nodetype) const;
-
- Span<const SocketRef *> sockets() const;
- Span<const InputSocketRef *> input_sockets() const;
- Span<const OutputSocketRef *> output_sockets() const;
-
- Span<const LinkRef *> links() const;
-
- const NodeRef *find_node(const bNode &bnode) const;
-
- /**
- * This is the active group output node if there are multiple.
- */
- const NodeRef *group_output_node() const;
-
- /**
- * \return True when there is a link cycle. Unavailable sockets are ignored.
- */
- bool has_link_cycles() const;
- bool has_undefined_nodes_or_sockets() const;
-
- enum class ToposortDirection {
- LeftToRight,
- RightToLeft,
- };
-
- struct ToposortResult {
- Vector<const NodeRef *> sorted_nodes;
- /**
- * There can't be a correct topological sort of the nodes when there is a cycle. The nodes will
- * still be sorted to some degree. The caller has to decide whether it can handle non-perfect
- * sorts or not.
- */
- bool has_cycle = false;
- };
-
- /**
- * Sort nodes topologically from left to right or right to left.
- * In the future the result if this could be cached on #NodeTreeRef.
- */
- ToposortResult toposort(ToposortDirection direction) const;
-
- bNodeTree *btree() const;
- StringRefNull name() const;
-
- std::string to_dot() const;
-
- private:
- /* Utility functions used during construction. */
- InputSocketRef &find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket);
- OutputSocketRef &find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket);
-
- void create_linked_socket_caches();
- void create_socket_identifier_maps();
-};
-
-using NodeTreeRefMap = Map<bNodeTree *, std::unique_ptr<const NodeTreeRef>>;
-
-const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree);
-
-namespace node_tree_ref_types {
-using nodes::InputSocketRef;
-using nodes::NodeRef;
-using nodes::NodeTreeRef;
-using nodes::NodeTreeRefMap;
-using nodes::OutputSocketRef;
-using nodes::SocketRef;
-} // namespace node_tree_ref_types
-
-/* -------------------------------------------------------------------- */
-/** \name #SocketRef Inline Methods
- * \{ */
-
-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_;
-}
-
-inline Span<const LinkRef *> SocketRef::directly_linked_links() const
-{
- return directly_linked_links_;
-}
-
-inline bool SocketRef::is_directly_linked() const
-{
- return directly_linked_sockets_.size() > 0;
-}
-
-inline bool SocketRef::is_logically_linked() const
-{
- return logically_linked_sockets_.size() > 0;
-}
-
-inline const NodeRef &SocketRef::node() const
-{
- return *node_;
-}
-
-inline const NodeTreeRef &SocketRef::tree() const
-{
- return node_->tree();
-}
-
-inline int SocketRef::id() const
-{
- return id_;
-}
-
-inline int SocketRef::index() const
-{
- return index_;
-}
-
-inline bool SocketRef::is_input() const
-{
- return is_input_;
-}
-
-inline bool SocketRef::is_output() const
-{
- return !is_input_;
-}
-
-inline const SocketRef &SocketRef::as_base() const
-{
- return *this;
-}
-
-inline const InputSocketRef &SocketRef::as_input() const
-{
- BLI_assert(this->is_input());
- return static_cast<const InputSocketRef &>(*this);
-}
-
-inline const OutputSocketRef &SocketRef::as_output() const
-{
- BLI_assert(this->is_output());
- return static_cast<const OutputSocketRef &>(*this);
-}
-
-inline StringRefNull SocketRef::idname() const
-{
- return bsocket_->idname;
-}
-
-inline StringRefNull SocketRef::name() const
-{
- return bsocket_->name;
-}
-
-inline StringRefNull SocketRef::identifier() const
-{
- return bsocket_->identifier;
-}
-
-inline bNodeSocketType *SocketRef::typeinfo() const
-{
- return bsocket_->typeinfo;
-}
-
-inline bNodeSocket *SocketRef::bsocket() const
-{
- return bsocket_;
-}
-
-inline bNode *SocketRef::bnode() const
-{
- return node_->bnode();
-}
-
-inline bNodeTree *SocketRef::btree() const
-{
- return node_->btree();
-}
-
-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;
-}
-
-template<typename T> inline T *SocketRef::default_value() const
-{
- return (T *)bsocket_->default_value;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #InputSocketRef Inline Methods
- * \{ */
-
-inline Span<const OutputSocketRef *> InputSocketRef::logically_linked_sockets() const
-{
- return logically_linked_sockets_.as_span().cast<const OutputSocketRef *>();
-}
-
-inline Span<const OutputSocketRef *> InputSocketRef::directly_linked_sockets() const
-{
- return directly_linked_sockets_.cast<const OutputSocketRef *>();
-}
-
-inline bool InputSocketRef::is_multi_input_socket() const
-{
- return bsocket_->flag & SOCK_MULTI_INPUT;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #OutputSocketRef Inline Methods
- * \{ */
-
-inline Span<const InputSocketRef *> OutputSocketRef::logically_linked_sockets() const
-{
- return logically_linked_sockets_.as_span().cast<const InputSocketRef *>();
-}
-
-inline Span<const InputSocketRef *> OutputSocketRef::directly_linked_sockets() const
-{
- return directly_linked_sockets_.cast<const InputSocketRef *>();
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #NodeRef Inline Methods
- * \{ */
-
-inline const NodeTreeRef &NodeRef::tree() const
-{
- return *tree_;
-}
-
-inline Span<const InputSocketRef *> NodeRef::inputs() const
-{
- return inputs_;
-}
-
-inline Span<const OutputSocketRef *> NodeRef::outputs() const
-{
- return outputs_;
-}
-
-inline Span<const SocketRef *> NodeRef::sockets(const eNodeSocketInOut in_out) const
-{
- return in_out == SOCK_IN ? inputs_.as_span().cast<const SocketRef *>() :
- outputs_.as_span().cast<const SocketRef *>();
-}
-
-inline Span<const InternalLinkRef *> NodeRef::internal_links() const
-{
- return internal_links_;
-}
-
-inline const InputSocketRef &NodeRef::input(int index) const
-{
- return *inputs_[index];
-}
-
-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_;
-}
-
-inline bNodeTree *NodeRef::btree() const
-{
- return tree_->btree();
-}
-
-inline StringRefNull NodeRef::idname() const
-{
- return bnode_->idname;
-}
-
-inline StringRefNull NodeRef::name() const
-{
- return bnode_->name;
-}
-
-inline StringRefNull NodeRef::label() const
-{
- return bnode_->label;
-}
-
-inline StringRefNull NodeRef::label_or_name() const
-{
- const StringRefNull label = this->label();
- if (!label.is_empty()) {
- return label;
- }
- return this->name();
-}
-
-inline bNodeType *NodeRef::typeinfo() const
-{
- return bnode_->typeinfo;
-}
-
-/* Returns a pointer because not all nodes have declarations currently. */
-inline const NodeDeclaration *NodeRef::declaration() const
-{
- nodeDeclarationEnsure(this->tree().btree(), bnode_);
- return bnode_->runtime->declaration;
-}
-
-inline int NodeRef::id() const
-{
- return id_;
-}
-
-inline bool NodeRef::is_reroute_node() const
-{
- return bnode_->type == NODE_REROUTE;
-}
-
-inline bool NodeRef::is_group_node() const
-{
- return bnode_->type == NODE_GROUP || bnode_->type == NODE_CUSTOM_GROUP;
-}
-
-inline bool NodeRef::is_group_input_node() const
-{
- return bnode_->type == NODE_GROUP_INPUT;
-}
-
-inline bool NodeRef::is_group_output_node() const
-{
- return bnode_->type == NODE_GROUP_OUTPUT;
-}
-
-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;
-}
-
-inline void *NodeRef::storage() const
-{
- return bnode_->storage;
-}
-
-template<typename T> inline T *NodeRef::storage() const
-{
- return (T *)bnode_->storage;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #LinkRef Inline Methods
- * \{ */
-
-inline const OutputSocketRef &LinkRef::from() const
-{
- return *from_;
-}
-
-inline const InputSocketRef &LinkRef::to() const
-{
- return *to_;
-}
-
-inline bNodeLink *LinkRef::blink() const
-{
- return blink_;
-}
-
-inline bool LinkRef::is_muted() const
-{
- return blink_->flag & NODE_LINK_MUTED;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #InternalLinkRef Inline Methods
- * \{ */
-
-inline const InputSocketRef &InternalLinkRef::from() const
-{
- return *from_;
-}
-
-inline const OutputSocketRef &InternalLinkRef::to() const
-{
- return *to_;
-}
-
-inline bNodeLink *InternalLinkRef::blink() const
-{
- return blink_;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name #NodeTreeRef Inline Methods
- * \{ */
-
-inline Span<const NodeRef *> NodeTreeRef::nodes() const
-{
- return nodes_by_id_;
-}
-
-inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(StringRefNull idname) const
-{
- const bNodeType *nodetype = nodeTypeFind(idname.c_str());
- return this->nodes_by_type(nodetype);
-}
-
-inline Span<const NodeRef *> NodeTreeRef::nodes_by_type(const bNodeType *nodetype) const
-{
- return nodes_by_type_.lookup(nodetype);
-}
-
-inline Span<const SocketRef *> NodeTreeRef::sockets() const
-{
- return sockets_by_id_;
-}
-
-inline Span<const InputSocketRef *> NodeTreeRef::input_sockets() const
-{
- return input_sockets_;
-}
-
-inline Span<const OutputSocketRef *> NodeTreeRef::output_sockets() const
-{
- return output_sockets_;
-}
-
-inline Span<const LinkRef *> NodeTreeRef::links() const
-{
- return links_;
-}
-
-inline const NodeRef *NodeTreeRef::group_output_node() const
-{
- return group_output_node_;
-}
-
-inline bNodeTree *NodeTreeRef::btree() const
-{
- return btree_;
-}
-
-inline StringRefNull NodeTreeRef::name() const
-{
- return btree_->id.name + 2;
-}
-
-/** \} */
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h
index 1d1310360b8..8fe77bffaad 100644
--- a/source/blender/nodes/NOD_shader.h
+++ b/source/blender/nodes/NOD_shader.h
@@ -26,6 +26,7 @@ void register_node_type_sh_camera(void);
void register_node_type_sh_value(void);
void register_node_type_sh_rgb(void);
void register_node_type_sh_mix_rgb(void);
+void register_node_type_sh_mix(void);
void register_node_type_sh_valtorgb(void);
void register_node_type_sh_rgbtobw(void);
void register_node_type_sh_shadertorgb(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 7c7f114bb78..10eb1721133 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -25,7 +25,7 @@ DefNode(Node, NODE_REROUTE, 0, "REROUT
DefNode(ShaderNode, SH_NODE_RGB, 0, "RGB", RGB, "RGB", "A color picker")
DefNode(ShaderNode, SH_NODE_VALUE, 0, "VALUE", Value, "Value", "Used to Input numerical values to other nodes in the tree")
-DefNode(ShaderNode, SH_NODE_MIX_RGB, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors")
+DefNode(ShaderNode, SH_NODE_MIX_RGB_LEGACY, def_mix_rgb, "MIX_RGB", MixRGB, "MixRGB", "Mix two input colors")
DefNode(ShaderNode, SH_NODE_VALTORGB, def_colorramp, "VALTORGB", ValToRGB, "ColorRamp", "Map values to colors with the use of a gradient")
DefNode(ShaderNode, SH_NODE_RGBTOBW, 0, "RGBTOBW", RGBToBW, "RGB to BW", "Convert a color's luminance to a grayscale value")
DefNode(ShaderNode, SH_NODE_SHADERTORGB, 0, "SHADERTORGB", ShaderToRGB, "Shader to RGB", "Convert rendering effect (such as light and shadow) to color. Typically used for non-photorealistic rendering, to apply additional effects on the output of BSDFs.\nNote: only supported for Eevee")
@@ -64,7 +64,7 @@ DefNode(ShaderNode, SH_NODE_BSDF_DIFFUSE, 0, "BSD
DefNode(ShaderNode, SH_NODE_BSDF_PRINCIPLED, def_principled, "BSDF_PRINCIPLED", BsdfPrincipled, "Principled BSDF", "Physically-based, easy-to-use shader for rendering surface materials, based on the Disney principled model also known as the \"PBR\" shader")
DefNode(ShaderNode, SH_NODE_BSDF_GLOSSY, def_glossy, "BSDF_GLOSSY", BsdfGlossy, "Glossy BSDF", "Reflection with microfacet distribution, used for materials such as metal or mirrors")
DefNode(ShaderNode, SH_NODE_BSDF_GLASS, def_glass, "BSDF_GLASS", BsdfGlass, "Glass BSDF", "Glass-like shader mixing refraction and reflection at grazing angles")
-DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution,. Typically used for materials that transmit light")
+DefNode(ShaderNode, SH_NODE_BSDF_REFRACTION, def_refraction, "BSDF_REFRACTION", BsdfRefraction, "Refraction BSDF", "Glossy refraction with sharp or microfacet distribution, typically used for materials that transmit light")
DefNode(ShaderNode, SH_NODE_BSDF_TRANSLUCENT, 0, "BSDF_TRANSLUCENT", BsdfTranslucent, "Translucent BSDF", "Lambertian diffuse transmission")
DefNode(ShaderNode, SH_NODE_BSDF_TRANSPARENT, 0, "BSDF_TRANSPARENT", BsdfTransparent, "Transparent BSDF", "Transparency without refraction, passing straight through the surface as if there were no geometry")
DefNode(ShaderNode, SH_NODE_BSDF_VELVET, 0, "BSDF_VELVET", BsdfVelvet, "Velvet BSDF", "Reflection for materials such as cloth.\nTypically mixed with other shaders (such as a Diffuse Shader) and is not particularly useful on its own")
@@ -122,6 +122,7 @@ DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUT
DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "Map an input float to a curve and outputs a float value")
DefNode(ShaderNode, SH_NODE_COMBINE_COLOR, def_sh_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "Create a color from individual components using multiple models")
DefNode(ShaderNode, SH_NODE_SEPARATE_COLOR, def_sh_combsep_color, "SEPARATE_COLOR", SeparateColor, "Separate Color", "Split a color into its individual components using multiple models")
+DefNode(ShaderNode, SH_NODE_MIX, def_sh_mix, "MIX", Mix, "Mix", "Mix values by a factor")
DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" )
DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" )
@@ -260,8 +261,8 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO
DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" )
DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" )
-DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "")
-DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
+DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler to Vector", "")
+DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_COMBINE_COLOR, def_fn_combsep_color, "COMBINE_COLOR", CombineColor, "Combine Color", "")
DefNode(FunctionNode, FN_NODE_COMPARE, def_compare, "COMPARE", Compare, "Compare", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
@@ -279,132 +280,137 @@ DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Sli
DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "")
DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "")
-DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "")
-DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "")
-DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "")
-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_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_ARC, def_geo_curve_primitive_arc, "CURVE_PRIMITIVE_ARC", CurveArc, "Arc", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLE_TYPE, def_geo_curve_set_handle_type, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_PARAMETER, 0, "SPLINE_PARAMETER", SplineParameter, "Spline Parameter", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
-DefNode(GeometryNode, GEO_NODE_DEFORM_CURVES_ON_SURFACE, 0, "DEFORM_CURVES_ON_SURFACE", DeformCurvesOnSurface, "Deform Curves on Surface", "")
-DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "")
-DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "")
-DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
-DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "")
-DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "")
-DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "")
-DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "")
-DefNode(GeometryNode, GEO_NODE_FIELD_ON_DOMAIN, def_geo_field_on_domain, "FIELD_ON_DOMAIN", FieldOnDomain, "Field on Domain", "")
-DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "")
-DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "")
-DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "")
-DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "")
-DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_ROTATION, 0, "INPUT_INSTANCE_ROTATION", InputInstanceRotation, "Instance Rotation", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_SCALE, 0, "INPUT_INSTANCE_SCALE", InputInstanceScale, "Instance Scale", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", InputMeshEdgeAngle, "Edge Angle", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS", InputMeshEdgeNeighbors, "Edge Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR", InputMeshFaceIsPlanar, "Face is Planar", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS", InputMeshFaceNeighbors, "Face Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SCENE_TIME, 0, "INPUT_SCENE_TIME", InputSceneTime, "Scene Time", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC", InputSplineCyclic, "Is Spline Cyclic", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "")
-DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "")
-DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "")
-DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "")
-DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "")
-DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "")
-DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "")
-DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance, "MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "")
-DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "")
-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_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "")
-DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "")
-DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh To Volume", "")
-DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "")
-DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "")
-DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "")
-DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "")
-DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "")
-DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "")
-DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "")
-DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "")
-DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "")
-DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "")
-DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "")
-DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "")
-DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "")
-DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
-DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "")
-DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "")
-DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "")
-DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "")
-DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "")
-DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "")
-DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
-DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "")
-DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "")
-DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "")
-DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "")
-DefNode(GeometryNode, GEO_NODE_STORE_NAMED_ATTRIBUTE, def_geo_store_named_attribute, "STORE_NAMED_ATTRIBUTE", StoreNamedAttribute, "Store Named Attribute", "")
-DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "")
-DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "")
-DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
-DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "")
-DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
-DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "")
-DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "")
-DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "")
-DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "")
-DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "")
-DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "")
-DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "")
+DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "Add the values of an evaluated field together and output the running total for each element")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "Retrieve the number of elements in a geometry for each attribute domain")
+DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC",AttributeStatistic, "Attribute Statistic","Calculate statistics about a data set from a field evaluated on a geometry")
+DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "Calculate the limits of a geometry's positions and generate a box mesh with those dimensions")
+DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture,"CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "Store the result of a field on a geometry and output the data as a node socket. Allows remembering or interpolating data as the geometry changes, such as positions before deformation")
+DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "Retrieve geometry from a collection")
+DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "Create a mesh that encloses all points in the input geometry with the smallest number of points")
+DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "Provide a selection for an arbitrary number of endpoints in each spline")
+DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "Provide a selection based on the handle types of Bézier control points")
+DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "Retrieve the length of all splines added together")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_ARC, def_geo_curve_primitive_arc, "CURVE_PRIMITIVE_ARC",CurveArc, "Arc", "Generate a poly spline arc")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "Generate a 2D Bézier spline from the given control points and handles")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE,def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "Generate a poly spline circle")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "Generate a poly spline line with two points")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMITIVE_QUADRATIC_BEZIER", CurveQuadraticBezier, "Quadratic Bezier", "Generate a poly spline in a parabola shape with control points positions")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "Generate a polygon with four points")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL,0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "Generate a poly spline in a spiral shape")
+DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "Generate a poly spline in a star pattern by connecting alternating points of two circles")
+DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLE_TYPE, def_geo_curve_set_handle_type, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "Set the handle type for the control points of a Bézier curve")
+DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_PARAMETER,0, "SPLINE_PARAMETER", SplineParameter, "Spline Parameter", "Retrieve how far along each spline a control point is")
+DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type,"CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "Change the type of curves")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "Convert curves into a mesh, optionally with a custom profile shape defined by curves")
+DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "Generate a point cloud by sampling positions along curves")
+DefNode(GeometryNode, GEO_NODE_DEFORM_CURVES_ON_SURFACE, 0, "DEFORM_CURVES_ON_SURFACE", DeformCurvesOnSurface, "Deform Curves on Surface", "Translate and rotate curves based on changes between the object's original and evaluated surface mesh")
+DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "Remove selected elements of a geometry")
+DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME, def_geo_distribute_points_in_volume, "DISTRIBUTE_POINTS_IN_VOLUME", DistributePointsInVolume, "Distribute Points In Volume", "Generate points inside a volume")
+DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "Generate points spread out on the surface of a mesh")
+DefNode(GeometryNode, GEO_NODE_DUAL_MESH, 0, "DUAL_MESH", DualMesh, "Dual Mesh", "Convert Faces into vertices and vertices into faces")
+DefNode(GeometryNode, GEO_NODE_DUPLICATE_ELEMENTS, def_geo_duplicate_elements, "DUPLICATE_ELEMENTS", DuplicateElements, "Duplicate Elements", "Generate an arbitrary number copies of each selected input element")
+DefNode(GeometryNode, GEO_NODE_EDGE_PATHS_TO_CURVES, 0, "EDGE_PATHS_TO_CURVES", EdgePathsToCurves, "Edge Paths to Curves", "")
+DefNode(GeometryNode, GEO_NODE_EDGE_PATHS_TO_SELECTION, 0, "EDGE_PATHS_TO_SELECTION", EdgePathsToSelection, "Edge Paths to Selection", "")
+DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "Generate new vertices, edges, or faces from selected elements and move them based on an offset while keeping them connected by their boundary")
+DefNode(GeometryNode, GEO_NODE_FIELD_AT_INDEX, def_geo_field_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Field at Index", "Retrieve data of other elements in the context's geometry")
+DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "Generate a mesh on the XY plane with faces on the inside of input curves")
+DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "Round corners by generating circular arcs on each control point")
+DefNode(GeometryNode, GEO_NODE_FLIP_FACES, 0, "FLIP_FACES", FlipFaces, "Flip Faces", "Reverse the order of the vertices and edges of selected faces, flipping their normal direction")
+DefNode(GeometryNode, GEO_NODE_GEOMETRY_TO_INSTANCE, 0, "GEOMETRY_TO_INSTANCE", GeometryToInstance, "Geometry to Instance", "Convert each input geometry into an instance, which can be much faster than the Join Geometry node when the inputs are large")
+DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture")
+DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES",InputCurveHandlePositions,"Curve Handle Positions", "Retrieve the position of each Bézier control point's handles")
+DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "Retrieve the angle at each control point used to twist the curve's normal around its tangent")
+DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "Retrieve a stable random identifier value from the \"id\" attribute on the point domain, or the index if the attribute does not exist")
+DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "Retrieve an integer value indicating the position of each element in the list, starting at zero")
+DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_ROTATION, 0, "INPUT_INSTANCE_ROTATION", InputInstanceRotation, "Instance Rotation", "Retrieve the rotation of each instance in the geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_INSTANCE_SCALE, 0, "INPUT_INSTANCE_SCALE", InputInstanceScale, "Instance Scale", "Retrieve the scale of each instance in the geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "Retrieve the index of the material used for each element in the geometry's list of materials")
+DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "Output a single material")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_ANGLE, 0, "MESH_EDGE_ANGLE", InputMeshEdgeAngle, "Edge Angle", "Calculate the surface area of each face in a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_NEIGHBORS, 0, "MESH_EDGE_NEIGHBORS",InputMeshEdgeNeighbors, "Edge Neighbors", "Retrieve the number of faces that use each edge as one of their sides")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_EDGE_VERTICES, 0, "MESH_EDGE_VERTICES", InputMeshEdgeVertices, "Edge Vertices", "Retrieve topology information relating to each edge of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_AREA, 0, "MESH_FACE_AREA", InputMeshFaceArea, "Face Area", "Calculate the surface area of a mesh's faces")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_IS_PLANAR, 0, "MESH_FACE_IS_PLANAR",InputMeshFaceIsPlanar, "Is Face Planar", "Retrieve whether all triangles in a face are on the same plane, i.e. whether have the same normal")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_FACE_NEIGHBORS, 0, "MESH_FACE_NEIGHBORS",InputMeshFaceNeighbors, "Face Neighbors", "Retrieve topology information relating to each face of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_ISLAND, 0, "MESH_ISLAND", InputMeshIsland, "Mesh Island", "Retrieve information about separate connected regions in a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_MESH_VERTEX_NEIGHBORS, 0, "MESH_VERTEX_NEIGHBORS", InputMeshVertexNeighbors, "Vertex Neighbors", "Retrieve topology information relating to each vertex of a mesh")
+DefNode(GeometryNode, GEO_NODE_INPUT_NAMED_ATTRIBUTE, def_geo_input_named_attribute, "INPUT_ATTRIBUTE", InputNamedAttribute, "Named Attribute", "Retrieve the data of a specified attribute")
+DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "Retrieve a unit length vector indicating the direction pointing away from the geometry at each element")
+DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "Retrieve a vector indicating the location of each element")
+DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "Retrieve the radius at each point on curve or point cloud geometry")
+DefNode(GeometryNode, GEO_NODE_INPUT_SCENE_TIME, 0, "INPUT_SCENE_TIME", InputSceneTime, "Scene Time", "Retrieve the current time in the scene's animation in units of seconds or frames")
+DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "Retrieve whether each face is marked for smooth shading")
+DefNode(GeometryNode, GEO_NODE_INPUT_SHORTEST_EDGE_PATHS, 0, "SHORTEST_EDGE_PATHS", InputShortestEdgePaths, "Shortest Edge Paths", "")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC",InputSplineCyclic, "Is Spline Cyclic", "Retrieve whether each spline endpoint connects to the beginning")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "Retrieve the total length of each spline, as a distance or as a number of points")
+DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "Retrieve the number of evaluated points that will be generated for every control point on curves")
+DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "Retrieve the direction of curves at each control point")
+DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "Generate a reference to geometry at each of the input points, without duplicating its underlying data")
+DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS",InstancesToPoints, "Instances to Points","Generate points at the origins of instances.\nNote: Nested instances are not affected by this node")
+DefNode(GeometryNode, GEO_NODE_INTERPOLATE_DOMAIN, def_geo_interpolate_domain, "FIELD_ON_DOMAIN", FieldOnDomain, "Interpolate Domain", "Retrieve values from a field on a different domain besides the domain from the context")
+DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "Retrieve whether the nodes are being evaluated for the viewport rather than the final render")
+DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "Merge separately generated geometries into a single one")
+DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "Provide a selection of faces that use the specified material")
+DefNode(GeometryNode, GEO_NODE_MERGE_BY_DISTANCE, def_geo_merge_by_distance,"MERGE_BY_DISTANCE", MergeByDistance, "Merge by Distance", "Merge vertices or points within a given distance")
+DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "Cut, subtract, or join multiple mesh inputs")
+DefNode(GeometryNode, GEO_NODE_MESH_FACE_SET_BOUNDARIES, 0, "MESH_FACE_SET_BOUNDARIES", MeshFaceSetBoundaries, "Face Set Boundaries", "Find edges on the boundaries between face sets")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "Generate a circular ring of edges")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE",MeshCone, "Cone", "Generate a cone mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE",MeshCube, "Cube", "Generate a cuboid mesh with variable side lengths and subdivisions")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CYLINDER, def_geo_mesh_cylinder, "MESH_PRIMITIVE_CYLINDER", MeshCylinder, "Cylinder", "Generate a cylinder mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID",MeshGrid, "Grid", "Generate a planar mesh on the XY plane")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "Generate a spherical mesh that consists of equally sized triangles")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE",MeshLine, "Mesh Line", "Generate vertices in a line and connect them with edges")
+DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "Generate a spherical mesh with quads, except for triangles at the top and bottom")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "Generate a curve from a mesh")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "Generate a point cloud from a mesh's vertices")
+DefNode(GeometryNode, GEO_NODE_MESH_TO_VOLUME, def_geo_mesh_to_volume, "MESH_TO_VOLUME", MeshToVolume, "Mesh to Volume", "Create a fog volume with the shape of the input mesh's surface")
+DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "Retrieve information from an object")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "Generate a mesh vertex for each point cloud point")
+DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "Generate a fog volume sphere around every point")
+DefNode(GeometryNode, GEO_NODE_POINTS, 0, "POINTS", Points, "Points", "Generate a point cloud with positions and radii defined by fields")
+DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "Compute the closest location on the target geometry")
+DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "Cast rays from the context geometry onto a target geometry, and retrieve information from each hit point")
+DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, def_geo_realize_instances,"REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "Change the direction of the curve by swapping each spline's start and end data")
+DefNode(GeometryNode, GEO_NODE_REMOVE_ATTRIBUTE, 0, "REMOVE_ATTRIBUTE", RemoveAttribute, "Remove Named Attribute", "Delete an attribute with a specified name from a geometry. Typically used to optimize performance")
+DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "Swap one material with another")
+DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "Generate a poly spline for each input spline")
+DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "Swap the start and end of splines")
+DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "Rotate geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "Retrieve data from a point on a curve at a certain distance from its start")
+DefNode(GeometryNode, GEO_NODE_SCALE_ELEMENTS, def_geo_scale_elements, "SCALE_ELEMENTS", ScaleElements, "Scale Elements", "Scale groups of connected edges and faces")
+DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "Scale geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS",SeparateComponents, "Separate Components","Split a geometry into a separate output for each type of data in the geometry")
+DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry,"SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "Split a geometry into two geometry outputs based on a selection")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "Set the positions for the handles of Bézier curves")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "Set the radius of the curve at each control point")
+DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "Set the tilt angle at each curve control point")
+DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "Set the id attribute on the input geometry, mainly used internally for randomizing")
+DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "Set the material index for each selected geometry element")
+DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "Assign a material to geometry elements")
+DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "Set the display size of point cloud points")
+DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "Set the location of each point")
+DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "Control the smoothness of mesh normals around each face by changing the \"shade smooth\" attribute")
+DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "Control whether each spline loops back on itself by changing the \"cyclic\" attribute")
+DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "Control how many evaluated points should be generated on every curve segment")
+DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "Duplicate mesh edges and break connections with the surrounding faces")
+DefNode(GeometryNode, GEO_NODE_STORE_NAMED_ATTRIBUTE, def_geo_store_named_attribute, "STORE_NAMED_ATTRIBUTE", StoreNamedAttribute, "Store Named Attribute", "Store the result of a field on a geometry as an attribute with the specified name")
+DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "Combine any number of input strings")
+DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "Generate a paragraph of text with a specific font, using a curve instance to store each character")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "Dividing each curve segment into a specified number of pieces")
+DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "Divide mesh faces into smaller ones without changing the shape or volume, using linear interpolation to place the new vertices")
+DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE",SubdivisionSurface, "Subdivision Surface","Divide mesh faces to form a smooth surface, using the Catmull-Clark subdivision method")
+DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "Switch between two inputs")
+DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "Retrieve values from a source geometry and provides them as a field by interpolating them with the context geometry")
+DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "Translate, rotate or scale the geometry")
+DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",TranslateInstances, "Translate Instances","Move top-level geometry instances in local or global space")
+DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces")
+DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "Shorten curves by removing portions at the start or end")
+DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "Scale islands of a UV map and move them so they fill the UV space as much as possible")
+DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map based on seam edges")
+DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "Display the input data in the Spreadsheet Editor")
+DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "Generate a dense volume with a field that controls the density at each grid voxel based on its position")
+DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "Generate a mesh on the \"surface\" of a volume")
/* undefine macros */
#undef DefNode
diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt
index c0100d77889..2537e8e93cc 100644
--- a/source/blender/nodes/composite/CMakeLists.txt
+++ b/source/blender/nodes/composite/CMakeLists.txt
@@ -10,11 +10,14 @@ set(INC
../../blenlib
../../blentranslation
../../depsgraph
+ ../../functions
+ ../../gpu
../../imbuf
../../makesdna
../../makesrna
../../render
../../windowmanager
+ ../../compositor/realtime_compositor
../../../../intern/guardedalloc
# dna_type_offsets.h
@@ -120,15 +123,19 @@ set(SRC
node_composite_util.hh
)
+set(LIB
+ bf_realtime_compositor
+)
+
if(WITH_IMAGE_OPENEXR)
add_definitions(-DWITH_OPENEXR)
endif()
-if(WITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
list(APPEND INC
../../compositor
)
- add_definitions(-DWITH_COMPOSITOR)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_OPENIMAGEDENOISE)
diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc
index 32b5d98a556..9792c55b590 100644
--- a/source/blender/nodes/composite/node_composite_tree.cc
+++ b/source/blender/nodes/composite/node_composite_tree.cc
@@ -32,7 +32,7 @@
#include "NOD_composite.h"
#include "node_composite_util.hh"
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
# include "COM_compositor.h"
#endif
@@ -183,6 +183,7 @@ void register_node_tree_type_cmp()
tt->type = NTREE_COMPOSIT;
strcpy(tt->idname, "CompositorNodeTree");
+ strcpy(tt->group_idname, "CompositorNodeGroup");
strcpy(tt->ui_name, N_("Compositor"));
tt->ui_icon = ICON_NODE_COMPOSITING;
strcpy(tt->ui_description, N_("Compositing nodes"));
@@ -209,7 +210,7 @@ void ntreeCompositExecTree(Scene *scene,
int do_preview,
const char *view_name)
{
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
COM_execute(rd, scene, ntree, rendering, view_name);
#else
UNUSED_VARS(scene, ntree, rd, rendering, view_name);
diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
index d392b810bc1..12f81da3d1c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc
@@ -8,17 +8,32 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** ALPHAOVER ******************** */
namespace blender::nodes::node_composite_alpha_over_cc {
+NODE_STORAGE_FUNCS(NodeTwoFloats)
+
static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image"), "Image_001")
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -36,6 +51,52 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "premul", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AlphaOverShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float premultiply_factor = get_premultiply_factor();
+ if (premultiply_factor != 0.0f) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_alpha_over_mixed",
+ inputs,
+ outputs,
+ GPU_uniform(&premultiply_factor));
+ return;
+ }
+
+ if (get_use_premultiply()) {
+ GPU_stack_link(material, &bnode(), "node_composite_alpha_over_key", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "node_composite_alpha_over_premultiply", inputs, outputs);
+ }
+
+ bool get_use_premultiply()
+ {
+ return bnode().custom1;
+ }
+
+ float get_premultiply_factor()
+ {
+ return node_storage(bnode()).x;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new AlphaOverShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_alpha_over_cc
void register_node_type_cmp_alphaover()
@@ -50,6 +111,7 @@ void register_node_type_cmp_alphaover()
node_type_init(&ntype, file_ns::node_alphaover_init);
node_type_storage(
&ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
index f45b678fc50..55fe3366526 100644
--- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Anti-Aliasing (SMAA 1x) ******************** */
@@ -42,6 +44,23 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C
uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AntiAliasingOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new AntiAliasingOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_antialiasing_cc
void register_node_type_cmp_antialiasing()
@@ -58,6 +77,7 @@ void register_node_type_cmp_antialiasing()
node_type_init(&ntype, file_ns::node_composit_init_antialiasing);
node_type_storage(
&ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
index ad4a1f701d6..ac9a6c89aa4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc
@@ -5,19 +5,32 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** BILATERALBLUR ******************** */
namespace blender::nodes::node_composite_bilateralblur_cc {
+NODE_STORAGE_FUNCS(NodeBilateralBlurData)
+
static void cmp_node_bilateralblur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Determinator")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Determinator"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -42,6 +55,61 @@ static void node_composit_buts_bilateralblur(uiLayout *layout,
uiItemR(col, ptr, "sigma_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BilateralBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &input_image = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input_image.is_single_value()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_bilateral_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1i(shader, "radius", get_blur_radius());
+ GPU_shader_uniform_1f(shader, "threshold", get_threshold());
+
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &determinator_image = get_input("Determinator");
+ determinator_image.bind_as_texture(shader, "determinator_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ determinator_image.unbind_as_texture();
+ }
+
+ int get_blur_radius()
+ {
+ return math::ceil(node_storage(bnode()).iter + node_storage(bnode()).sigma_space);
+ }
+
+ float get_threshold()
+ {
+ return node_storage(bnode()).sigma_color;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BilateralBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bilateralblur_cc
void register_node_type_cmp_bilateralblur()
@@ -56,6 +124,7 @@ void register_node_type_cmp_bilateralblur()
node_type_init(&ntype, file_ns::node_composit_init_bilateralblur);
node_type_storage(
&ntype, "NodeBilateralBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc
index 7beffe15c8e..630f18361e3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc
@@ -5,17 +5,36 @@
* \ingroup cmpnodes
*/
+#include <cstdint>
+
+#include "BLI_array.hh"
+#include "BLI_assert.h"
+#include "BLI_index_range.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RE_pipeline.h"
+
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** BLUR ******************** */
namespace blender::nodes::node_composite_blur_cc {
+NODE_STORAGE_FUNCS(NodeBlurData)
+
static void cmp_node_blur_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
@@ -71,6 +90,405 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(col, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+/* A helper class that computes and caches a 1D GPU texture containing the weights of the separable
+ * filter of the given type and radius. The filter is assumed to be symmetric, because the filter
+ * functions are all even functions. Consequently, only the positive half of the filter is computed
+ * and the shader takes that into consideration. */
+class SymmetricSeparableBlurWeights {
+ private:
+ float radius_ = 1.0f;
+ int type_ = R_FILTER_GAUSS;
+ GPUTexture *texture_ = nullptr;
+
+ public:
+ ~SymmetricSeparableBlurWeights()
+ {
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+ }
+
+ /* Check if a texture containing the weights was already computed for the given filter type and
+ * radius. If such texture exists, do nothing, otherwise, free the already computed texture and
+ * recompute it with the given filter type and radius. */
+ void update(float radius, int type)
+ {
+ if (texture_ && type == type_ && radius == radius_) {
+ return;
+ }
+
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+
+ /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only
+ * compute half of it and no doubling happens. We add 1 to make sure the filter size is always
+ * odd and there is a center weight. */
+ const int size = math::ceil(radius) + 1;
+ Array<float> weights(size);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(type, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Second, compute the other weights in the positive direction, making sure to add double the
+ * weight to the sum of weights because the filter is symmetric and we only loop over half of
+ * it. Skip the center weight already computed by dropping the front index. */
+ const float scale = radius > 0.0f ? 1.0f / radius : 0.0f;
+ for (const int i : weights.index_range().drop_front(1)) {
+ const float weight = RE_filter_value(type, i * scale);
+ weights[i] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int i : weights.index_range()) {
+ weights[i] /= sum;
+ }
+
+ texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data());
+
+ type_ = type;
+ radius_ = radius;
+ }
+
+ void bind_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(texture_, texture_image_unit);
+ }
+
+ void unbind_as_texture()
+ {
+ GPU_texture_unbind(texture_);
+ }
+};
+
+/* A helper class that computes and caches a 2D GPU texture containing the weights of the filter of
+ * the given type and radius. The filter is assumed to be symmetric, because the filter functions
+ * are evaluated on the normalized distance to the center. Consequently, only the upper right
+ * quadrant are computed and the shader takes that into consideration. */
+class SymmetricBlurWeights {
+ private:
+ int type_ = R_FILTER_GAUSS;
+ float2 radius_ = float2(1.0f);
+ GPUTexture *texture_ = nullptr;
+
+ public:
+ ~SymmetricBlurWeights()
+ {
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+ }
+
+ /* Check if a texture containing the weights was already computed for the given filter type and
+ * radius. If such texture exists, do nothing, otherwise, free the already computed texture and
+ * recompute it with the given filter type and radius. */
+ void update(float2 radius, int type)
+ {
+ if (texture_ && type == type_ && radius == radius_) {
+ return;
+ }
+
+ if (texture_) {
+ GPU_texture_free(texture_);
+ }
+
+ /* The full size of filter is double the radius plus 1, but since the filter is symmetric, we
+ * only compute a single quadrant of it and so no doubling happens. We add 1 to make sure the
+ * filter size is always odd and there is a center weight. */
+ const float2 scale = math::safe_divide(float2(1.0f), radius);
+ const int2 size = int2(math::ceil(radius)) + int2(1);
+ Array<float> weights(size.x * size.y);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(type, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Then, compute the weights along the positive x axis, making sure to add double the weight to
+ * the sum of weights because the filter is symmetric and we only loop over the positive half
+ * of the x axis. Skip the center weight already computed by dropping the front index. */
+ for (const int x : IndexRange(size.x).drop_front(1)) {
+ const float weight = RE_filter_value(type, x * scale.x);
+ weights[x] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Then, compute the weights along the positive y axis, making sure to add double the weight to
+ * the sum of weights because the filter is symmetric and we only loop over the positive half
+ * of the y axis. Skip the center weight already computed by dropping the front index. */
+ for (const int y : IndexRange(size.y).drop_front(1)) {
+ const float weight = RE_filter_value(type, y * scale.y);
+ weights[size.x * y] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Then, compute the other weights in the upper right quadrant, making sure to add quadruple
+ * the weight to the sum of weights because the filter is symmetric and we only loop over one
+ * quadrant of it. Skip the weights along the y and x axis already computed by dropping the
+ * front index. */
+ for (const int y : IndexRange(size.y).drop_front(1)) {
+ for (const int x : IndexRange(size.x).drop_front(1)) {
+ const float weight = RE_filter_value(type, math::length(float2(x, y) * scale));
+ weights[size.x * y + x] = weight;
+ sum += weight * 4.0f;
+ }
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int y : IndexRange(size.y)) {
+ for (const int x : IndexRange(size.x)) {
+ weights[size.x * y + x] /= sum;
+ }
+ }
+
+ texture_ = GPU_texture_create_2d("Weights", size.x, size.y, 1, GPU_R16F, weights.data());
+
+ type_ = type;
+ radius_ = radius;
+ }
+
+ void bind_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(texture_, texture_image_unit);
+ }
+
+ void unbind_as_texture()
+ {
+ GPU_texture_unbind(texture_);
+ }
+};
+
+class BlurOperation : public NodeOperation {
+ private:
+ /* Cached symmetric blur weights. */
+ SymmetricBlurWeights blur_weights_;
+ /* Cached symmetric blur weights for the separable horizontal pass. */
+ SymmetricSeparableBlurWeights blur_horizontal_weights_;
+ /* Cached symmetric blur weights for the separable vertical pass. */
+ SymmetricSeparableBlurWeights blur_vertical_weights_;
+
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (use_separable_filter()) {
+ GPUTexture *horizontal_pass_result = execute_separable_blur_horizontal_pass();
+ execute_separable_blur_vertical_pass(horizontal_pass_result);
+ }
+ else {
+ execute_blur();
+ }
+ }
+
+ void execute_blur()
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct", node_storage(bnode()).gamma);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ blur_weights_.update(compute_blur_radius(), node_storage(bnode()).filtertype);
+ blur_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(math::ceil(compute_blur_radius())) * 2;
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ blur_weights_.unbind_as_texture();
+ }
+
+ GPUTexture *execute_separable_blur_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct_input", node_storage(bnode()).gamma);
+ GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", false);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ blur_horizontal_weights_.update(compute_blur_radius().x, node_storage(bnode()).filtertype);
+ blur_horizontal_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ domain.size.x += static_cast<int>(math::ceil(compute_blur_radius().x)) * 2;
+ }
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will blur the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal blur shader, but since its input is
+ * transposed, it will effectively do a vertical blur and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each blur
+ * pass. */
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_image.unbind_as_texture();
+ blur_horizontal_weights_.unbind_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_separable_blur_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get("compositor_symmetric_separable_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+ GPU_shader_uniform_1b(shader, "gamma_correct_input", false);
+ GPU_shader_uniform_1b(shader, "gamma_uncorrect_output", node_storage(bnode()).gamma);
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ blur_vertical_weights_.update(compute_blur_radius().y, node_storage(bnode()).filtertype);
+ blur_vertical_weights_.bind_as_texture(shader, "weights_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(math::ceil(compute_blur_radius())) * 2;
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ blur_vertical_weights_.unbind_as_texture();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ float2 compute_blur_radius()
+ {
+ const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 1.0f);
+
+ if (!node_storage(bnode()).relative) {
+ return float2(node_storage(bnode()).sizex, node_storage(bnode()).sizey) * size;
+ }
+
+ int2 image_size = get_input("Image").domain().size;
+ switch (node_storage(bnode()).aspect) {
+ case CMP_NODE_BLUR_ASPECT_Y:
+ image_size.y = image_size.x;
+ break;
+ case CMP_NODE_BLUR_ASPECT_X:
+ image_size.x = image_size.y;
+ break;
+ default:
+ BLI_assert(node_storage(bnode()).aspect == CMP_NODE_BLUR_ASPECT_NONE);
+ break;
+ }
+
+ return float2(image_size) * get_size_factor() * size;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ /* Zero blur radius. The operation does nothing and the input can be passed through. */
+ if (compute_blur_radius() == float2(0.0)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /* The blur node can operate with different filter types, evaluated on the normalized distance to
+ * the center of the filter. Some of those filters are separable and can be computed as such. If
+ * the bokeh member is disabled in the node, then the filter is always computed as separable even
+ * if it is not in fact separable, in which case, the used filter is a cheaper approximation to
+ * the actual filter. If the bokeh member is enabled, then the filter is computed as separable if
+ * it is in fact separable and as a normal 2D filter otherwise. */
+ bool use_separable_filter()
+ {
+ if (!node_storage(bnode()).bokeh) {
+ return true;
+ }
+
+ /* Both Box and Gaussian filters are separable. The rest is not. */
+ switch (node_storage(bnode()).filtertype) {
+ case R_FILTER_BOX:
+ case R_FILTER_GAUSS:
+ case R_FILTER_FAST_GAUSS:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ float2 get_size_factor()
+ {
+ return float2(node_storage(bnode()).percentx, node_storage(bnode()).percenty) / 100.0f;
+ }
+
+ bool get_extend_bounds()
+ {
+ return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_blur_cc
void register_node_type_cmp_blur()
@@ -86,6 +504,7 @@ void register_node_type_cmp_blur()
node_type_init(&ntype, file_ns::node_composit_init_blur);
node_type_storage(
&ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
index a936bafe671..9c0617ee8c3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc
@@ -5,9 +5,17 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** BLUR ******************** */
@@ -16,10 +24,22 @@ namespace blender::nodes::node_composite_bokehblur_cc {
static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Color>(N_("Bokeh")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).max(10.0f);
- b.add_input<decl::Float>(N_("Bounding box")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Bokeh"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_skip_realization();
+ b.add_input<decl::Float>(N_("Size"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(10.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Bounding box"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(2);
b.add_output<decl::Color>(N_("Image"));
}
@@ -37,6 +57,98 @@ static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BokehBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_blur");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1i(shader, "radius", compute_blur_radius());
+ GPU_shader_uniform_1b(shader, "extend_bounds", get_extend_bounds());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &input_weights = get_input("Bokeh");
+ input_weights.bind_as_texture(shader, "weights_tx");
+
+ const Result &input_mask = get_input("Bounding box");
+ input_mask.bind_as_texture(shader, "mask_tx");
+
+ Domain domain = compute_domain();
+ if (get_extend_bounds()) {
+ /* Add a radius amount of pixels in both sides of the image, hence the multiply by 2. */
+ domain.size += int2(compute_blur_radius() * 2);
+ }
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ input_weights.unbind_as_texture();
+ input_mask.unbind_as_texture();
+ }
+
+ int compute_blur_radius()
+ {
+ const int2 image_size = get_input("Image").domain().size;
+ const int max_size = math::max(image_size.x, image_size.y);
+
+ /* The [0, 10] range of the size is arbitrary and is merely in place to avoid very long
+ * computations of the bokeh blur. */
+ const float size = math::clamp(get_input("Size").get_float_value_default(1.0f), 0.0f, 10.0f);
+
+ /* The 100 divisor is arbitrary and was chosen using visual judgment. */
+ return size * (max_size / 100.0f);
+ }
+
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ if (compute_blur_radius() == 0) {
+ return true;
+ }
+
+ /* This input is, in fact, a boolean mask. If it is zero, no blurring will take place.
+ * Otherwise, the blurring will take place ignoring the value of the input entirely. */
+ const Result &bounding_box = get_input("Bounding box");
+ if (bounding_box.is_single_value() && bounding_box.get_float_value() == 0.0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ bool get_extend_bounds()
+ {
+ return bnode().custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BokehBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bokehblur_cc
void register_node_type_cmp_bokehblur()
@@ -49,6 +161,7 @@ void register_node_type_cmp_bokehblur()
ntype.declare = file_ns::cmp_node_bokehblur_declare;
ntype.draw_buttons = file_ns::node_composit_buts_bokehblur;
node_type_init(&ntype, file_ns::node_composit_init_bokehblur);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
index 8330c56736a..81cc8990d35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc
@@ -5,15 +5,25 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Bokeh image Tools ******************** */
namespace blender::nodes::node_composite_bokehimage_cc {
+NODE_STORAGE_FUNCS(NodeBokehImage)
+
static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b)
{
b.add_output<decl::Color>(N_("Image"));
@@ -45,6 +55,61 @@ static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "shift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BokehImageOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get("compositor_bokeh_image");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "exterior_angle", get_exterior_angle());
+ GPU_shader_uniform_1f(shader, "rotation", get_rotation());
+ GPU_shader_uniform_1f(shader, "roundness", node_storage(bnode()).rounding);
+ GPU_shader_uniform_1f(shader, "catadioptric", node_storage(bnode()).catadioptric);
+ GPU_shader_uniform_1f(shader, "lens_shift", node_storage(bnode()).lensshift);
+
+ Result &output = get_result("Image");
+ const Domain domain = compute_domain();
+ output.allocate_texture(domain);
+ output.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ output.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ return Domain(int2(512));
+ }
+
+ /* The exterior angle is the angle between each two consecutive vertices of the regular polygon
+ * from its center. */
+ float get_exterior_angle()
+ {
+ return (M_PI * 2.0f) / node_storage(bnode()).flaps;
+ }
+
+ float get_rotation()
+ {
+ /* Offset the rotation such that the second vertex of the regular polygon lies on the positive
+ * y axis, which is 90 degrees minus the angle that it makes with the positive x axis assuming
+ * the first vertex lies on the positive x axis. */
+ const float offset = M_PI_2 - get_exterior_angle();
+ return node_storage(bnode()).angle - offset;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BokehImageOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_bokehimage_cc
void register_node_type_cmp_bokehimage()
@@ -60,6 +125,7 @@ void register_node_type_cmp_bokehimage()
node_type_init(&ntype, file_ns::node_composit_init_bokehimage);
node_type_storage(
&ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
index f39b69c63f2..3cf0932e1b3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc
@@ -5,15 +5,26 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
namespace blender::nodes::node_composite_boxmask_cc {
+NODE_STORAGE_FUNCS(NodeBoxMask)
+
static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -48,6 +59,93 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BoxMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ const Domain domain = compute_domain();
+
+ GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
+
+ GPU_shader_uniform_2fv(shader, "location", get_location());
+ GPU_shader_uniform_2fv(shader, "size", get_size() / 2.0f);
+ GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
+ GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "base_mask_tx");
+
+ const Result &value = get_input("Value");
+ value.bind_as_texture(shader, "mask_value_tx");
+
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_mask_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_mask.unbind_as_texture();
+ value.unbind_as_texture();
+ output_mask.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ if (get_input("Mask").is_single_value()) {
+ return Domain(context().get_output_size());
+ }
+ return get_input("Mask").domain();
+ }
+
+ CMPNodeMaskType get_mask_type()
+ {
+ return (CMPNodeMaskType)bnode().custom1;
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_mask_type()) {
+ default:
+ case CMP_NODE_MASKTYPE_ADD:
+ return "compositor_box_mask_add";
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ return "compositor_box_mask_subtract";
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ return "compositor_box_mask_multiply";
+ case CMP_NODE_MASKTYPE_NOT:
+ return "compositor_box_mask_not";
+ }
+ }
+
+ float2 get_location()
+ {
+ return float2(node_storage(bnode()).x, node_storage(bnode()).y);
+ }
+
+ float2 get_size()
+ {
+ return float2(node_storage(bnode()).width, node_storage(bnode()).height);
+ }
+
+ float get_angle()
+ {
+ return node_storage(bnode()).rotation;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new BoxMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_boxmask_cc
void register_node_type_cmp_boxmask()
@@ -61,6 +159,7 @@ void register_node_type_cmp_boxmask()
ntype.draw_buttons = file_ns::node_composit_buts_boxmask;
node_type_init(&ntype, file_ns::node_composit_init_boxmask);
node_type_storage(&ntype, "NodeBoxMask", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
index 65ed2885d9b..fa22f551de6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Bright and Contrast ******************** */
@@ -16,9 +20,11 @@ namespace blender::nodes::node_composite_brightness_cc {
static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f);
- b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f).compositor_domain_priority(2);
b.add_output<decl::Color>(N_("Image"));
}
@@ -34,6 +40,38 @@ static void node_composit_buts_brightcontrast(uiLayout *layout,
uiItemR(layout, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class BrightContrastShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float use_premultiply = get_use_premultiply();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_bright_contrast",
+ inputs,
+ outputs,
+ GPU_constant(&use_premultiply));
+ }
+
+ bool get_use_premultiply()
+ {
+ return bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new BrightContrastShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_brightness_cc
void register_node_type_cmp_brightcontrast()
@@ -46,6 +84,7 @@ void register_node_type_cmp_brightcontrast()
ntype.declare = file_ns::cmp_node_brightcontrast_declare;
ntype.draw_buttons = file_ns::node_composit_buts_brightcontrast;
node_type_init(&ntype, file_ns::node_composit_init_brightcontrast);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
index 627f07fdfce..3b825017da8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc
@@ -10,15 +10,23 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Channel Matte Node ********************************* */
namespace blender::nodes::node_composite_channel_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -79,6 +87,91 @@ static void node_composit_buts_channel_matte(uiLayout *layout,
col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ChannelMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float color_space = get_color_space();
+ const float matte_channel = get_matte_channel();
+ float limit_channels[2];
+ get_limit_channels(limit_channels);
+ const float max_limit = get_max_limit();
+ const float min_limit = get_min_limit();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_channel_matte",
+ inputs,
+ outputs,
+ GPU_constant(&color_space),
+ GPU_constant(&matte_channel),
+ GPU_constant(limit_channels),
+ GPU_uniform(&max_limit),
+ GPU_uniform(&min_limit));
+ }
+
+ /* 1 -> CMP_NODE_CHANNEL_MATTE_CS_RGB
+ * 2 -> CMP_NODE_CHANNEL_MATTE_CS_HSV
+ * 3 -> CMP_NODE_CHANNEL_MATTE_CS_YUV
+ * 4 -> CMP_NODE_CHANNEL_MATTE_CS_YCC */
+ int get_color_space()
+ {
+ return bnode().custom1;
+ }
+
+ /* Get the index of the channel used to generate the matte. */
+ int get_matte_channel()
+ {
+ return bnode().custom2 - 1;
+ }
+
+ /* Get the index of the channel used to compute the limit value. */
+ int get_limit_channel()
+ {
+ return node_storage(bnode()).channel - 1;
+ }
+
+ /* Get the indices of the channels used to compute the limit value. We always assume the limit
+ * algorithm is Max, if it is a single limit channel, store it in both limit channels, because
+ * the maximum of two identical values is the same value. */
+ void get_limit_channels(float limit_channels[2])
+ {
+ if (node_storage(bnode()).algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) {
+ /* If the algorithm is Max, store the indices of the other two channels other than the matte
+ * channel. */
+ limit_channels[0] = (get_matte_channel() + 1) % 3;
+ limit_channels[1] = (get_matte_channel() + 2) % 3;
+ }
+ else {
+ /* If the algorithm is Single, store the index of the limit channel in both channels. */
+ limit_channels[0] = get_limit_channel();
+ limit_channels[1] = get_limit_channel();
+ }
+ }
+
+ float get_max_limit()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_min_limit()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ChannelMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_channel_matte_cc
void register_node_type_cmp_channel_matte()
@@ -93,6 +186,7 @@ void register_node_type_cmp_channel_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_channel_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
index 69319c6825d..e5ce87169d4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc
@@ -5,21 +5,33 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
#include "BLI_math_rotation.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Chroma Key ********************************************************** */
namespace blender::nodes::node_composite_chroma_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -51,6 +63,52 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C
// uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ChromaMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float acceptance = get_acceptance();
+ const float cutoff = get_cutoff();
+ const float falloff = get_falloff();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_chroma_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&acceptance),
+ GPU_uniform(&cutoff),
+ GPU_uniform(&falloff));
+ }
+
+ float get_acceptance()
+ {
+ return std::tan(node_storage(bnode()).t1) / 2.0f;
+ }
+
+ float get_cutoff()
+ {
+ return node_storage(bnode()).t2;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).fstrength;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ChromaMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_chroma_matte_cc
void register_node_type_cmp_chroma_matte()
@@ -65,6 +123,7 @@ void register_node_type_cmp_chroma_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_chroma_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
index 474fb1b72f2..08329601f14 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc
@@ -8,16 +8,26 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Matte ********************************************************** */
namespace blender::nodes::node_composite_color_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -50,6 +60,53 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C)
col, ptr, "color_value", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ColorMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float hue_epsilon = get_hue_epsilon();
+ const float saturation_epsilon = get_saturation_epsilon();
+ const float value_epsilon = get_value_epsilon();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&hue_epsilon),
+ GPU_uniform(&saturation_epsilon),
+ GPU_uniform(&value_epsilon));
+ }
+
+ float get_hue_epsilon()
+ {
+ /* Divide by 2 because the hue wraps around. */
+ return node_storage(bnode()).t1 / 2.0f;
+ }
+
+ float get_saturation_epsilon()
+ {
+ return node_storage(bnode()).t2;
+ }
+
+ float get_value_epsilon()
+ {
+ return node_storage(bnode()).t3;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_color_matte_cc
void register_node_type_cmp_color_matte()
@@ -64,6 +121,7 @@ void register_node_type_cmp_color_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_color_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
index 9ad5dfbaeb2..29401d7b20f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc
@@ -10,16 +10,29 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Spill Suppression ********************************* */
namespace blender::nodes::node_composite_color_spill_cc {
+NODE_STORAGE_FUNCS(NodeColorspill)
+
static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -27,8 +40,8 @@ static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node
{
NodeColorspill *ncs = MEM_cnew<NodeColorspill>(__func__);
node->storage = ncs;
+ node->custom2 = CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE;
node->custom1 = 2; /* green channel */
- node->custom2 = 0; /* simple limit algorithm */
ncs->limchan = 0; /* limit by red */
ncs->limscale = 1.0f; /* limit scaling factor */
ncs->unspill = 0; /* do not use unspill */
@@ -80,6 +93,98 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C)
}
}
+using namespace blender::realtime_compositor;
+
+class ColorSpillShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float spill_channel = get_spill_channel();
+ float spill_scale[3];
+ get_spill_scale(spill_scale);
+ float limit_channels[2];
+ get_limit_channels(limit_channels);
+ const float limit_scale = get_limit_scale();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_spill",
+ inputs,
+ outputs,
+ GPU_constant(&spill_channel),
+ GPU_uniform(spill_scale),
+ GPU_constant(limit_channels),
+ GPU_uniform(&limit_scale));
+ }
+
+ /* Get the index of the channel used for spilling. */
+ int get_spill_channel()
+ {
+ return bnode().custom1 - 1;
+ }
+
+ CMPNodeColorSpillLimitAlgorithm get_limit_algorithm()
+ {
+ return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2;
+ }
+
+ void get_spill_scale(float spill_scale[3])
+ {
+ const NodeColorspill &node_color_spill = node_storage(bnode());
+ if (node_color_spill.unspill) {
+ spill_scale[0] = node_color_spill.uspillr;
+ spill_scale[1] = node_color_spill.uspillg;
+ spill_scale[2] = node_color_spill.uspillb;
+ spill_scale[get_spill_channel()] *= -1.0f;
+ }
+ else {
+ spill_scale[0] = 0.0f;
+ spill_scale[1] = 0.0f;
+ spill_scale[2] = 0.0f;
+ spill_scale[get_spill_channel()] = -1.0f;
+ }
+ }
+
+ /* Get the index of the channel used for limiting. */
+ int get_limit_channel()
+ {
+ return node_storage(bnode()).limchan;
+ }
+
+ /* Get the indices of the channels used to compute the limit value. We always assume the limit
+ * algorithm is Average, if it is a single limit channel, store it in both limit channels,
+ * because the average of two identical values is the same value. */
+ void get_limit_channels(float limit_channels[2])
+ {
+ if (get_limit_algorithm() == CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE) {
+ /* If the algorithm is Average, store the indices of the other two channels other than the
+ * spill channel. */
+ limit_channels[0] = (get_spill_channel() + 1) % 3;
+ limit_channels[1] = (get_spill_channel() + 2) % 3;
+ }
+ else {
+ /* If the algorithm is Single, store the index of the limit channel in both channels. */
+ limit_channels[0] = get_limit_channel();
+ limit_channels[1] = get_limit_channel();
+ }
+ }
+
+ float get_limit_scale()
+ {
+ return node_storage(bnode()).limscale;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorSpillShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_color_spill_cc
void register_node_type_cmp_color_spill()
@@ -94,6 +199,7 @@ void register_node_type_cmp_color_spill()
node_type_init(&ntype, file_ns::node_composit_init_color_spill);
node_type_storage(
&ntype, "NodeColorspill", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
index dd081c8fc12..e05fbf00a25 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc
@@ -10,6 +10,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Balance ********************************* */
@@ -44,10 +48,19 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node)
namespace blender::nodes::node_composite_colorbalance_cc {
+NODE_STORAGE_FUNCS(NodeColorBalance)
+
static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -71,7 +84,7 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C
uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
- if (RNA_enum_get(ptr, "correction_method") == 0) {
+ if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) {
split = uiLayoutSplit(layout, 0.0f, false);
col = uiLayoutColumn(split, false);
@@ -116,7 +129,7 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout,
{
uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
- if (RNA_enum_get(ptr, "correction_method") == 0) {
+ if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) {
uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true);
uiItemR(layout, ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
@@ -139,6 +152,53 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout,
}
}
+using namespace blender::realtime_compositor;
+
+class ColorBalanceShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const NodeColorBalance &node_color_balance = node_storage(bnode());
+
+ if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_balance_lgg",
+ inputs,
+ outputs,
+ GPU_uniform(node_color_balance.lift),
+ GPU_uniform(node_color_balance.gamma),
+ GPU_uniform(node_color_balance.gain));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_balance_asc_cdl",
+ inputs,
+ outputs,
+ GPU_uniform(node_color_balance.offset),
+ GPU_uniform(node_color_balance.power),
+ GPU_uniform(node_color_balance.slope),
+ GPU_uniform(&node_color_balance.offset_basis));
+ }
+
+ CMPNodeColorBalanceMethod get_color_balance_method()
+ {
+ return (CMPNodeColorBalanceMethod)bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorBalanceShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_colorbalance_cc
void register_node_type_cmp_colorbalance()
@@ -155,6 +215,7 @@ void register_node_type_cmp_colorbalance()
node_type_init(&ntype, file_ns::node_composit_init_colorbalance);
node_type_storage(
&ntype, "NodeColorBalance", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
index 39ecd277cec..92b10fc1877 100644
--- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc
@@ -5,19 +5,33 @@
* \ingroup cmpnodes
*/
+#include "IMB_colormanagement.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Color Correction ********************************* */
namespace blender::nodes::node_composite_colorcorrection_cc {
+NODE_STORAGE_FUNCS(NodeColorCorrection)
+
static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Mask"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -266,6 +280,68 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout,
uiItemR(row, ptr, "midtones_end", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ColorCorrectionShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ float enabled_channels[3];
+ get_enabled_channels(enabled_channels);
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ const NodeColorCorrection &node_color_correction = node_storage(bnode());
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_color_correction",
+ inputs,
+ outputs,
+ GPU_constant(enabled_channels),
+ GPU_uniform(&node_color_correction.startmidtones),
+ GPU_uniform(&node_color_correction.endmidtones),
+ GPU_uniform(&node_color_correction.master.saturation),
+ GPU_uniform(&node_color_correction.master.contrast),
+ GPU_uniform(&node_color_correction.master.gamma),
+ GPU_uniform(&node_color_correction.master.gain),
+ GPU_uniform(&node_color_correction.master.lift),
+ GPU_uniform(&node_color_correction.shadows.saturation),
+ GPU_uniform(&node_color_correction.shadows.contrast),
+ GPU_uniform(&node_color_correction.shadows.gamma),
+ GPU_uniform(&node_color_correction.shadows.gain),
+ GPU_uniform(&node_color_correction.shadows.lift),
+ GPU_uniform(&node_color_correction.midtones.saturation),
+ GPU_uniform(&node_color_correction.midtones.contrast),
+ GPU_uniform(&node_color_correction.midtones.gamma),
+ GPU_uniform(&node_color_correction.midtones.gain),
+ GPU_uniform(&node_color_correction.midtones.lift),
+ GPU_uniform(&node_color_correction.highlights.saturation),
+ GPU_uniform(&node_color_correction.highlights.contrast),
+ GPU_uniform(&node_color_correction.highlights.gamma),
+ GPU_uniform(&node_color_correction.highlights.gain),
+ GPU_uniform(&node_color_correction.highlights.lift),
+ GPU_constant(luminance_coefficients));
+ }
+
+ void get_enabled_channels(float enabled_channels[3])
+ {
+ for (int i = 0; i < 3; i++) {
+ enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f;
+ }
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorCorrectionShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_colorcorrection_cc
void register_node_type_cmp_colorcorrection()
@@ -282,6 +358,7 @@ void register_node_type_cmp_colorcorrection()
node_type_init(&ntype, file_ns::node_composit_init_colorcorrection);
node_type_storage(
&ntype, "NodeColorCorrection", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc
index d35ce7dc11a..68061bb434d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_composite.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** COMPOSITE ******************** */
@@ -26,6 +35,125 @@ static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class CompositeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ if (image.is_single_value() && alpha.is_single_value()) {
+ execute_clear();
+ }
+ else if (ignore_alpha()) {
+ execute_ignore_alpha();
+ }
+ else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
+ execute_copy();
+ }
+ else {
+ execute_set_alpha();
+ }
+ }
+
+ /* Executes when all inputs are single values, in which case, the output texture can just be
+ * cleared to the appropriate color. */
+ void execute_clear()
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ float4 color = image.get_color_value();
+ if (ignore_alpha()) {
+ color.w = 1.0f;
+ }
+ else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
+ color.w = alpha.get_float_value();
+ }
+
+ GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
+ }
+
+ /* Executes when the alpha channel of the image is ignored. */
+ void execute_ignore_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "input_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* Executes when the image texture is written with no adjustments and can thus be copied directly
+ * to the output texture. */
+ void execute_copy()
+ {
+ const Result &image = get_input("Image");
+
+ /* Make sure any prior writes to the texture are reflected before copying it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ GPU_texture_copy(context().get_output_texture(), image.texture());
+ }
+
+ /* Executes when the alpha channel of the image is set as the value of the input alpha. */
+ void execute_set_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_set_alpha");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "image_tx");
+
+ const Result &alpha = get_input("Alpha");
+ alpha.bind_as_texture(shader, "alpha_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ alpha.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
+ * alpha channel of the image is retained, but only if the alpha input is not linked. If the
+ * alpha input is linked, it the value of that input will be used as the alpha of the image. */
+ bool ignore_alpha()
+ {
+ return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CompositeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_composite_cc
void register_node_type_cmp_composite()
@@ -37,6 +165,7 @@ void register_node_type_cmp_composite()
cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT);
ntype.declare = file_ns::cmp_node_composite_declare;
ntype.draw_buttons = file_ns::node_composit_buts_composite;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.flag |= NODE_PREVIEW;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
index 303248c3852..e36da39cca1 100644
--- a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc
@@ -14,6 +14,8 @@
#include "IMB_colormanagement.h"
+#include "COM_node_operation.hh"
+
namespace blender::nodes::node_composite_convert_color_space_cc {
static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b)
@@ -47,6 +49,23 @@ static void node_composit_buts_convert_colorspace(uiLayout *layout,
uiItemR(layout, ptr, "to_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ConvertColorSpaceOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ConvertColorSpaceOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_convert_color_space_cc
void register_node_type_cmp_convert_color_space(void)
@@ -62,6 +81,7 @@ void register_node_type_cmp_convert_color_space(void)
node_type_init(&ntype, file_ns::node_composit_init_convert_colorspace);
node_type_storage(
&ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
index 07da0da0be1..9679701a7cf 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_cornerpin_cc {
@@ -32,6 +34,24 @@ static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Plane"));
}
+using namespace blender::realtime_compositor;
+
+class CornerPinOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Plane").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CornerPinOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_cornerpin_cc
void register_node_type_cmp_cornerpin()
@@ -42,6 +62,7 @@ void register_node_type_cmp_cornerpin()
cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_cornerpin_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc
index 823e1052dd0..13d02a707be 100644
--- a/source/blender/nodes/composite/nodes/node_composite_crop.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc
@@ -5,20 +5,35 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Crop ******************** */
namespace blender::nodes::node_composite_crop_cc {
+NODE_STORAGE_FUNCS(NodeTwoXYs)
+
static void cmp_node_crop_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -54,6 +69,156 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point
}
}
+using namespace blender::realtime_compositor;
+
+class CropOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ /* The operation does nothing, so just pass the input through. */
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (get_is_image_crop()) {
+ execute_image_crop();
+ }
+ else {
+ execute_alpha_crop();
+ }
+ }
+
+ /* Crop by replacing areas outside of the cropping bounds with zero alpha. The output have the
+ * same domain as the input image. */
+ void execute_alpha_crop()
+ {
+ GPUShader *shader = shader_manager().get("compositor_alpha_crop");
+ GPU_shader_bind(shader);
+
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+ GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
+ GPU_shader_uniform_2iv(shader, "upper_bound", upper_bound);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ /* Crop the image into a new size that matches the cropping bounds. */
+ void execute_image_crop()
+ {
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+
+ /* The image is cropped into nothing, so just return a single zero value. */
+ if (lower_bound.x == upper_bound.x || lower_bound.y == upper_bound.y) {
+ Result &result = get_result("Image");
+ result.allocate_invalid();
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_image_crop");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const int2 size = upper_bound - lower_bound;
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(Domain(size, compute_domain().transformation));
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ /* If true, the image should actually be cropped into a new size. Otherwise, if false, the region
+ * outside of the cropping bounds will be set to a zero alpha value. */
+ bool get_is_image_crop()
+ {
+ return bnode().custom1;
+ }
+
+ bool get_is_relative()
+ {
+ return bnode().custom2;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be cropped and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ int2 lower_bound, upper_bound;
+ compute_cropping_bounds(lower_bound, upper_bound);
+ const int2 input_size = input.domain().size;
+ /* The cropping bounds cover the whole image, so no cropping happens. */
+ if (lower_bound == int2(0) && upper_bound == input_size) {
+ return true;
+ }
+
+ return false;
+ }
+
+ void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound)
+ {
+ const NodeTwoXYs &node_two_xys = node_storage(bnode());
+ const int2 input_size = get_input("Image").domain().size;
+
+ if (get_is_relative()) {
+ /* The cropping bounds are relative to the image size. The factors are in the [0, 1] range,
+ * so it is guaranteed that they won't go over the input image size. */
+ lower_bound.x = input_size.x * node_two_xys.fac_x1;
+ lower_bound.y = input_size.y * node_two_xys.fac_y2;
+ upper_bound.x = input_size.x * node_two_xys.fac_x2;
+ upper_bound.y = input_size.y * node_two_xys.fac_y1;
+ }
+ else {
+ /* Make sure the bounds don't go over the input image size. */
+ lower_bound.x = min_ii(node_two_xys.x1, input_size.x);
+ lower_bound.y = min_ii(node_two_xys.y2, input_size.y);
+ upper_bound.x = min_ii(node_two_xys.x2, input_size.x);
+ upper_bound.y = min_ii(node_two_xys.y1, input_size.y);
+ }
+
+ /* Make sure upper bound is actually higher than the lower bound. */
+ lower_bound.x = min_ii(lower_bound.x, upper_bound.x);
+ lower_bound.y = min_ii(lower_bound.y, upper_bound.y);
+ upper_bound.x = max_ii(lower_bound.x, upper_bound.x);
+ upper_bound.y = max_ii(lower_bound.y, upper_bound.y);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CropOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_crop_cc
void register_node_type_cmp_crop()
@@ -67,6 +232,7 @@ void register_node_type_cmp_crop()
ntype.draw_buttons = file_ns::node_composit_buts_crop;
node_type_init(&ntype, file_ns::node_composit_init_crop);
node_type_storage(&ntype, "NodeTwoXYs", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
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 5462441660c..7e5544381a4 100644
--- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc
@@ -26,6 +26,8 @@
#include "RE_pipeline.h"
+#include "COM_node_operation.hh"
+
#include <optional>
/* -------------------------------------------------------------------- */
@@ -105,7 +107,6 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no
return session;
}
-extern "C" {
static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encoded_hash)
{
LISTBASE_FOREACH (CryptomatteEntry *, entry, &n.entries) {
@@ -299,6 +300,25 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype),
return false;
}
+using namespace blender::realtime_compositor;
+
+class CryptoMatteOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Pick").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CryptoMatteOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_cryptomatte_cc
void register_node_type_cmp_cryptomatte()
@@ -316,6 +336,8 @@ void register_node_type_cmp_cryptomatte()
ntype.poll = file_ns::node_poll_cryptomatte;
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
@@ -350,7 +372,7 @@ int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node)
return 1;
}
-namespace blender::nodes::node_composite_cryptomatte_cc {
+namespace blender::nodes::node_composite_legacy_cryptomatte_cc {
static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
{
@@ -365,22 +387,43 @@ static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node)
ntreeCompositCryptomatteAddSocket(ntree, node);
}
-} // namespace blender::nodes::node_composite_cryptomatte_cc
+using namespace blender::realtime_compositor;
+
+class CryptoMatteOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Pick").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new CryptoMatteOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_legacy_cryptomatte_cc
void register_node_type_cmp_cryptomatte_legacy()
{
- namespace legacy_file_ns = blender::nodes::node_composite_cryptomatte_cc;
+ namespace legacy_file_ns = blender::nodes::node_composite_legacy_cryptomatte_cc;
namespace file_ns = blender::nodes::node_composite_cryptomatte_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE_LEGACY, "Cryptomatte", NODE_CLASS_MATTE);
node_type_socket_templates(&ntype, nullptr, file_ns::cmp_node_cryptomatte_out);
- node_type_init(&ntype, file_ns::node_init_cryptomatte_legacy);
+ node_type_init(&ntype, legacy_file_ns::node_init_cryptomatte_legacy);
node_type_storage(
&ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
/** \} */
-}
diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc
index 802664d7934..bf45e219730 100644
--- a/source/blender/nodes/composite/nodes/node_composite_curves.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc
@@ -5,16 +5,23 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+
#include "BKE_colortools.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_node_operation.hh"
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** CURVE Time ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_time_curves_cc {
static void cmp_node_time_declare(NodeDeclarationBuilder &b)
{
@@ -29,11 +36,65 @@ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node
node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class TimeCurveOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("Fac");
+ result.allocate_single_value();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+ BKE_curvemapping_init(curve_mapping);
+ const float time = BKE_curvemapping_evaluateF(curve_mapping, 0, compute_normalized_time());
+ result.set_float_value(clamp_f(time, 0.0f, 1.0f));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+
+ int get_start_time()
+ {
+ return bnode().custom1;
+ }
+
+ int get_end_time()
+ {
+ return bnode().custom2;
+ }
+
+ float compute_normalized_time()
+ {
+ const int frame_number = context().get_frame_number();
+ if (frame_number < get_start_time()) {
+ return 0.0f;
+ }
+ if (frame_number > get_end_time()) {
+ return 1.0f;
+ }
+ if (get_start_time() == get_end_time()) {
+ return 0.0f;
+ }
+ return static_cast<float>(frame_number - get_start_time()) /
+ static_cast<float>(get_end_time() - get_start_time());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TimeCurveOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_time_curves_cc
void register_node_type_cmp_curve_time()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_time_curves_cc;
static bNodeType ntype;
@@ -42,17 +103,22 @@ void register_node_type_cmp_curve_time()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curves_time);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
/* **************** CURVE VEC ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_vector_curves_cc {
static void cmp_node_curve_vec_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Vector>(N_("Vector")).default_value({0.0f, 0.0f, 0.0f}).min(-1.0f).max(1.0f);
+ b.add_input<decl::Vector>(N_("Vector"))
+ .default_value({0.0f, 0.0f, 0.0f})
+ .min(-1.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
b.add_output<decl::Vector>(N_("Vector"));
}
@@ -66,11 +132,63 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA
uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class VectorCurvesShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_vector",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new VectorCurvesShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_vector_curves_cc
void register_node_type_cmp_curve_vec()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_vector_curves_cc;
static bNodeType ntype;
@@ -80,19 +198,26 @@ void register_node_type_cmp_curve_vec()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curve_vec);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** CURVE RGB ******************** */
-namespace blender::nodes::node_composite_curves_cc {
+namespace blender::nodes::node_composite_rgb_curves_cc {
static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype(
- PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(-1.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Color>(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f});
b.add_input<decl::Color>(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
b.add_output<decl::Color>(N_("Image"));
@@ -103,11 +228,105 @@ static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f);
}
-} // namespace blender::nodes::node_composite_curves_cc
+using namespace blender::realtime_compositor;
+
+class RGBCurvesShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float start_slopes[CM_TOT];
+ float end_slopes[CM_TOT];
+ BKE_curvemapping_compute_slopes(curve_mapping, start_slopes, end_slopes);
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ if (curve_mapping->tone == CURVE_TONE_FILMLIKE) {
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_film_like",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
+ return;
+ }
+
+ const float min = 0.0f;
+ const float max = 1.0f;
+ GPU_link(material,
+ "clamp_value",
+ get_input_link("Fac"),
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &get_input("Fac").link);
+
+ /* If the RGB curves do nothing, use a function that skips RGB computations. */
+ if (BKE_curvemapping_is_map_identity(curve_mapping, 0) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 1) &&
+ BKE_curvemapping_is_map_identity(curve_mapping, 2)) {
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_combined_only",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(&range_minimums[3]),
+ GPU_uniform(&range_dividers[3]),
+ GPU_uniform(&start_slopes[3]),
+ GPU_uniform(&end_slopes[3]));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "curves_combined_rgb",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers),
+ GPU_uniform(start_slopes),
+ GPU_uniform(end_slopes));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new RGBCurvesShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_rgb_curves_cc
void register_node_type_cmp_curve_rgb()
{
- namespace file_ns = blender::nodes::node_composite_curves_cc;
+ namespace file_ns = blender::nodes::node_composite_rgb_curves_cc;
static bNodeType ntype;
@@ -116,6 +335,7 @@ void register_node_type_cmp_curve_rgb()
node_type_size(&ntype, 200, 140, 320);
node_type_init(&ntype, file_ns::node_composit_init_curve_rgb);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.cc b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
index 83dd397ff1f..94b4908a1bd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_defocus.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc
@@ -12,6 +12,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* ************ Defocus Node ****************** */
@@ -81,6 +83,23 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA
uiItemR(sub, ptr, "z_scale", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DefocusOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DefocusOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_defocus_cc
void register_node_type_cmp_defocus()
@@ -94,6 +113,7 @@ void register_node_type_cmp_defocus()
ntype.draw_buttons = file_ns::node_composit_buts_defocus;
node_type_init(&ntype, file_ns::node_composit_init_defocus);
node_type_storage(&ntype, "NodeDefocus", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.cc b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
index 051a2580ef9..0452e7cd943 100644
--- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_denoise_cc {
@@ -52,6 +54,23 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_hdr", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DenoiseOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DenoiseOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_denoise_cc
void register_node_type_cmp_denoise()
@@ -65,6 +84,7 @@ void register_node_type_cmp_denoise()
ntype.draw_buttons = file_ns::node_composit_buts_denoise;
node_type_init(&ntype, file_ns::node_composit_init_denonise);
node_type_storage(&ntype, "NodeDenoise", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
index 66a18cfa369..aa6725b8750 100644
--- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc
@@ -8,6 +8,11 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** FILTER ******************** */
@@ -16,8 +21,15 @@ namespace blender::nodes::node_composite_despeckle_cc {
static void cmp_node_despeckle_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -36,6 +48,61 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "threshold_neighbor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DespeckleOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &input_image = get_input("Image");
+ /* Single value inputs can't be despeckled and are returned as is. */
+ if (input_image.is_single_value()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_despeckle");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "threshold", get_threshold());
+ GPU_shader_uniform_1f(shader, "neighbor_threshold", get_neighbor_threshold());
+
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &factor_image = get_input("Fac");
+ factor_image.bind_as_texture(shader, "factor_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ factor_image.unbind_as_texture();
+ }
+
+ float get_threshold()
+ {
+ return bnode().custom3;
+ }
+
+ float get_neighbor_threshold()
+ {
+ return bnode().custom4;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DespeckleOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_despeckle_cc
void register_node_type_cmp_despeckle()
@@ -49,6 +116,7 @@ void register_node_type_cmp_despeckle()
ntype.draw_buttons = file_ns::node_composit_buts_despeckle;
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_despeckle);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
index 20dd61a9725..8912d00a9be 100644
--- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc
@@ -8,18 +8,28 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* channel Difference Matte ********************************* */
namespace blender::nodes::node_composite_diff_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image 1"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image 2"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
- b.add_output<decl::Color>(N_("Matte"));
+ b.add_output<decl::Float>(N_("Matte"));
}
static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node)
@@ -40,6 +50,45 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DifferenceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float tolerance = get_tolerance();
+ const float falloff = get_falloff();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_difference_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ }
+
+ float get_tolerance()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new DifferenceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_diff_matte_cc
void register_node_type_cmp_diff_matte()
@@ -54,6 +103,7 @@ void register_node_type_cmp_diff_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_diff_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
index 9bdb9ae0837..551dfacb276 100644
--- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc
@@ -5,17 +5,36 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_array.hh"
+#include "BLI_assert.h"
+#include "BLI_math_base.hh"
+
+#include "DNA_scene_types.h"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RE_pipeline.h"
+
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Dilate/Erode ******************** */
namespace blender::nodes::node_composite_dilate_cc {
+NODE_STORAGE_FUNCS(NodeDilateErode)
+
static void cmp_node_dilate_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -34,15 +53,477 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
switch (RNA_enum_get(ptr, "mode")) {
- case CMP_NODE_DILATEERODE_DISTANCE_THRESH:
+ case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD:
uiItemR(layout, ptr, "edge", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
break;
- case CMP_NODE_DILATEERODE_DISTANCE_FEATHER:
+ case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER:
uiItemR(layout, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
break;
}
}
+using namespace blender::realtime_compositor;
+
+/* Computes a falloff that is equal to 1 at an input of zero and decrease to zero at an input of 1,
+ * with the rate of decrease depending on the falloff type. */
+static float compute_distance_falloff(float x, int falloff_type)
+{
+ x = 1.0f - x;
+
+ switch (falloff_type) {
+ case PROP_SMOOTH:
+ return 3.0f * x * x - 2.0f * x * x * x;
+ case PROP_SPHERE:
+ return std::sqrt(2.0f * x - x * x);
+ case PROP_ROOT:
+ return std::sqrt(x);
+ case PROP_SHARP:
+ return x * x;
+ case PROP_INVSQUARE:
+ return x * (2.0f - x);
+ case PROP_LIN:
+ return x;
+ default:
+ BLI_assert_unreachable();
+ return x;
+ }
+}
+
+/* A helper class that computes and caches 1D GPU textures containing the weights of the separable
+ * Gaussian filter of the given radius as well as an inverse distance falloff of the given type and
+ * radius. The weights and falloffs are symmetric, because the Gaussian and falloff functions are
+ * all even functions. Consequently, only the positive half of the filter is computed and the
+ * shader takes that into consideration. */
+class SymmetricSeparableMorphologicalDistanceFeatherWeights {
+ private:
+ int radius_ = 1;
+ int falloff_type_ = PROP_SMOOTH;
+ GPUTexture *weights_texture_ = nullptr;
+ GPUTexture *distance_falloffs_texture_ = nullptr;
+
+ public:
+ ~SymmetricSeparableMorphologicalDistanceFeatherWeights()
+ {
+ if (weights_texture_) {
+ GPU_texture_free(weights_texture_);
+ }
+
+ if (distance_falloffs_texture_) {
+ GPU_texture_free(distance_falloffs_texture_);
+ }
+ }
+
+ /* Check if textures containing the weights and distance falloffs were already computed for the
+ * given distance falloff type and radius. If such textures exists, do nothing, otherwise, free
+ * the already computed textures and recompute it with the given distance falloff type and
+ * radius. */
+ void update(int radius, int falloff_type)
+ {
+ if (weights_texture_ && distance_falloffs_texture_ && falloff_type == falloff_type_ &&
+ radius == radius_) {
+ return;
+ }
+
+ radius_ = radius;
+ falloff_type_ = falloff_type;
+
+ compute_weights();
+ compute_distance_falloffs();
+ }
+
+ void compute_weights()
+ {
+ if (weights_texture_) {
+ GPU_texture_free(weights_texture_);
+ }
+
+ /* The size of filter is double the radius plus 1, but since the filter is symmetric, we only
+ * compute half of it and no doubling happens. We add 1 to make sure the filter size is always
+ * odd and there is a center weight. */
+ const int size = radius_ + 1;
+ Array<float> weights(size);
+
+ float sum = 0.0f;
+
+ /* First, compute the center weight. */
+ const float center_weight = RE_filter_value(R_FILTER_GAUSS, 0.0f);
+ weights[0] = center_weight;
+ sum += center_weight;
+
+ /* Second, compute the other weights in the positive direction, making sure to add double the
+ * weight to the sum of weights because the filter is symmetric and we only loop over half of
+ * it. Skip the center weight already computed by dropping the front index. */
+ const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f;
+ for (const int i : weights.index_range().drop_front(1)) {
+ const float weight = RE_filter_value(R_FILTER_GAUSS, i * scale);
+ weights[i] = weight;
+ sum += weight * 2.0f;
+ }
+
+ /* Finally, normalize the weights. */
+ for (const int i : weights.index_range()) {
+ weights[i] /= sum;
+ }
+
+ weights_texture_ = GPU_texture_create_1d("Weights", size, 1, GPU_R16F, weights.data());
+ }
+
+ void compute_distance_falloffs()
+ {
+ if (distance_falloffs_texture_) {
+ GPU_texture_free(distance_falloffs_texture_);
+ }
+
+ /* The size of the distance falloffs is double the radius plus 1, but since the falloffs are
+ * symmetric, we only compute half of them and no doubling happens. We add 1 to make sure the
+ * falloffs size is always odd and there is a center falloff. */
+ const int size = radius_ + 1;
+ Array<float> falloffs(size);
+
+ /* Compute the distance falloffs in the positive direction only, because the falloffs are
+ * symmetric. */
+ const float scale = radius_ > 0.0f ? 1.0f / radius_ : 0.0f;
+ for (const int i : falloffs.index_range()) {
+ falloffs[i] = compute_distance_falloff(i * scale, falloff_type_);
+ }
+
+ distance_falloffs_texture_ = GPU_texture_create_1d(
+ "Distance Factors", size, 1, GPU_R16F, falloffs.data());
+ }
+
+ void bind_weights_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(weights_texture_, texture_image_unit);
+ }
+
+ void unbind_weights_as_texture()
+ {
+ GPU_texture_unbind(weights_texture_);
+ }
+
+ void bind_distance_falloffs_as_texture(GPUShader *shader, const char *texture_name)
+ {
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name);
+ GPU_texture_bind(distance_falloffs_texture_, texture_image_unit);
+ }
+
+ void unbind_distance_falloffs_as_texture()
+ {
+ GPU_texture_unbind(distance_falloffs_texture_);
+ }
+};
+
+class DilateErodeOperation : public NodeOperation {
+ private:
+ /* Cached symmetric blur weights and distance falloffs for the distance feature method. */
+ SymmetricSeparableMorphologicalDistanceFeatherWeights distance_feather_weights_;
+
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Mask").pass_through(get_result("Mask"));
+ return;
+ }
+
+ switch (get_method()) {
+ case CMP_NODE_DILATE_ERODE_STEP:
+ execute_step();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE:
+ execute_distance();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD:
+ execute_distance_threshold();
+ return;
+ case CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER:
+ execute_distance_feather();
+ return;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
+ }
+
+ /* ----------------------------
+ * Step Morphological Operator.
+ * ---------------------------- */
+
+ void execute_step()
+ {
+ GPUTexture *horizontal_pass_result = execute_step_horizontal_pass();
+ execute_step_vertical_pass(horizontal_pass_result);
+ }
+
+ GPUTexture *execute_step_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_step_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will process the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal pass shader, but since its input is
+ * transposed, it will effectively do a vertical pass and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each of
+ * the passes. */
+ const Domain domain = compute_domain();
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_mask.unbind_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_step_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_step_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ const char *get_morphological_step_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_step_dilate";
+ }
+ return "compositor_morphological_step_erode";
+ }
+
+ /* --------------------------------
+ * Distance Morphological Operator.
+ * -------------------------------- */
+
+ void execute_distance()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_shader_name());
+ GPU_shader_bind(shader);
+
+ /* Pass the absolute value of the distance. We have specialized shaders for each sign. */
+ GPU_shader_uniform_1i(shader, "radius", math::abs(get_distance()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ input_mask.unbind_as_texture();
+ }
+
+ const char *get_morphological_distance_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_distance_dilate";
+ }
+ return "compositor_morphological_distance_erode";
+ }
+
+ /* ------------------------------------------
+ * Distance Threshold Morphological Operator.
+ * ------------------------------------------ */
+
+ void execute_distance_threshold()
+ {
+ GPUShader *shader = shader_manager().get("compositor_morphological_distance_threshold");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1f(shader, "inset", get_inset());
+ GPU_shader_uniform_1i(shader, "radius", get_morphological_distance_threshold_radius());
+ GPU_shader_uniform_1i(shader, "distance", get_distance());
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_mask.unbind_as_image();
+ input_mask.unbind_as_texture();
+ }
+
+ /* See the discussion in the implementation for more information. */
+ int get_morphological_distance_threshold_radius()
+ {
+ return static_cast<int>(math::ceil(get_inset())) + math::abs(get_distance());
+ }
+
+ /* ----------------------------------------
+ * Distance Feather Morphological Operator.
+ * ---------------------------------------- */
+
+ void execute_distance_feather()
+ {
+ GPUTexture *horizontal_pass_result = execute_distance_feather_horizontal_pass();
+ execute_distance_feather_vertical_pass(horizontal_pass_result);
+ }
+
+ GPUTexture *execute_distance_feather_horizontal_pass()
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name());
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Mask");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff);
+ distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx");
+ distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
+
+ /* We allocate an output image of a transposed size, that is, with a height equivalent to the
+ * width of the input and vice versa. This is done as a performance optimization. The shader
+ * will process the image horizontally and write it to the intermediate output transposed. Then
+ * the vertical pass will execute the same horizontal pass shader, but since its input is
+ * transposed, it will effectively do a vertical pass and write to the output transposed,
+ * effectively undoing the transposition in the horizontal pass. This is done to improve
+ * spatial cache locality in the shader and to avoid having two separate shaders for each of
+ * the passes. */
+ const Domain domain = compute_domain();
+ const int2 transposed_domain = int2(domain.size.y, domain.size.x);
+
+ GPUTexture *horizontal_pass_result = texture_pool().acquire_color(transposed_domain);
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(horizontal_pass_result, image_unit);
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ input_image.unbind_as_texture();
+ distance_feather_weights_.unbind_weights_as_texture();
+ distance_feather_weights_.unbind_distance_falloffs_as_texture();
+ GPU_texture_image_unbind(horizontal_pass_result);
+
+ return horizontal_pass_result;
+ }
+
+ void execute_distance_feather_vertical_pass(GPUTexture *horizontal_pass_result)
+ {
+ GPUShader *shader = shader_manager().get(get_morphological_distance_feather_shader_name());
+ GPU_shader_bind(shader);
+
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
+ const int texture_image_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(horizontal_pass_result, texture_image_unit);
+
+ distance_feather_weights_.update(math::abs(get_distance()), node_storage(bnode()).falloff);
+ distance_feather_weights_.bind_weights_as_texture(shader, "weights_tx");
+ distance_feather_weights_.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Mask");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ /* Notice that the domain is transposed, see the note on the horizontal pass method for more
+ * information on the reasoning behind this. */
+ compute_dispatch_threads_at_least(shader, int2(domain.size.y, domain.size.x));
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ distance_feather_weights_.unbind_weights_as_texture();
+ distance_feather_weights_.unbind_distance_falloffs_as_texture();
+ GPU_texture_unbind(horizontal_pass_result);
+ }
+
+ const char *get_morphological_distance_feather_shader_name()
+ {
+ if (get_distance() > 0) {
+ return "compositor_morphological_distance_feather_dilate";
+ }
+ return "compositor_morphological_distance_feather_erode";
+ }
+
+ /* ---------------
+ * Common Methods.
+ * --------------- */
+
+ bool is_identity()
+ {
+ const Result &input = get_input("Mask");
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ if (get_method() == CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD && get_inset() != 0.0f) {
+ return false;
+ }
+
+ if (get_distance() == 0) {
+ return true;
+ }
+
+ return false;
+ }
+
+ int get_distance()
+ {
+ return bnode().custom2;
+ }
+
+ float get_inset()
+ {
+ return bnode().custom3;
+ }
+
+ CMPNodeDilateErodeMethod get_method()
+ {
+ return (CMPNodeDilateErodeMethod)bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DilateErodeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_dilate_cc
void register_node_type_cmp_dilateerode()
@@ -57,6 +538,7 @@ void register_node_type_cmp_dilateerode()
node_type_init(&ntype, file_ns::node_composit_init_dilateerode);
node_type_storage(
&ntype, "NodeDilateErode", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
index 3d82ab04fc9..6e6bec70283 100644
--- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc
@@ -5,16 +5,30 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_directionalblur_cc {
+NODE_STORAGE_FUNCS(NodeDBlurData)
+
static void cmp_node_directional_blur_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -51,6 +65,129 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin
uiItemR(layout, ptr, "zoom", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DirectionalBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_directional_blur");
+ GPU_shader_bind(shader);
+
+ /* The number of iterations does not cover the original image, that is, the image with no
+ * transformation. So add an extra iteration for the original image and put that into
+ * consideration in the shader. */
+ GPU_shader_uniform_1i(shader, "iterations", get_iterations() + 1);
+ GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", get_transformation().ptr());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ GPU_shader_unbind();
+ output_image.unbind_as_image();
+ input_image.unbind_as_texture();
+ }
+
+ /* Get the amount of translation that will be applied on each iteration. The translation is in
+ * the negative x direction rotated in the clock-wise direction, hence the negative sign for the
+ * rotation and translation vector. */
+ float2 get_translation()
+ {
+ const float diagonal_length = math::length(float2(get_input("Image").domain().size));
+ const float translation_amount = diagonal_length * node_storage(bnode()).distance;
+ const float3x3 rotation = float3x3::from_rotation(-node_storage(bnode()).angle);
+ return rotation * float2(-translation_amount / get_iterations(), 0.0f);
+ }
+
+ /* Get the amount of rotation that will be applied on each iteration. */
+ float get_rotation()
+ {
+ return node_storage(bnode()).spin / get_iterations();
+ }
+
+ /* Get the amount of scale that will be applied on each iteration. The scale is identity when the
+ * user supplies 0, so we add 1. */
+ float2 get_scale()
+ {
+ return float2(1.0f + node_storage(bnode()).zoom / get_iterations());
+ }
+
+ float2 get_origin()
+ {
+ const float2 center = float2(node_storage(bnode()).center_x, node_storage(bnode()).center_y);
+ return float2(get_input("Image").domain().size) * center;
+ }
+
+ float3x3 get_transformation()
+ {
+ /* Construct the transformation that will be applied on each iteration. */
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ get_translation(), get_rotation(), get_scale());
+ /* Change the origin of the transformation to the user-specified origin. */
+ const float3x3 origin_transformation = float3x3::from_origin_transformation(transformation,
+ get_origin());
+ /* The shader will transform the coordinates, not the image itself, so take the inverse. */
+ return origin_transformation.inverted();
+ }
+
+ /* The actual number of iterations is 2 to the power of the user supplied iterations. The power
+ * is implemented using a bit shift. But also make sure it doesn't exceed the upper limit which
+ * is the number of diagonal pixels. */
+ int get_iterations()
+ {
+ const int iterations = 2 << (node_storage(bnode()).iter - 1);
+ const int upper_limit = math::ceil(math::length(float2(get_input("Image").domain().size)));
+ return math::min(iterations, upper_limit);
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ const Result &input = get_input("Image");
+ /* Single value inputs can't be blurred and are returned as is. */
+ if (input.is_single_value()) {
+ return true;
+ }
+
+ /* If any of the following options are non-zero, then the operation is not an identity. */
+ if (node_storage(bnode()).distance != 0.0f) {
+ return false;
+ }
+
+ if (node_storage(bnode()).spin != 0.0f) {
+ return false;
+ }
+
+ if (node_storage(bnode()).zoom != 0.0f) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DirectionalBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_directionalblur_cc
void register_node_type_cmp_dblur()
@@ -65,6 +202,7 @@ void register_node_type_cmp_dblur()
node_type_init(&ntype, file_ns::node_composit_init_dblur);
node_type_storage(
&ntype, "NodeDBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc
index 0b0d42cbb08..1049f2fa4a9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_displace.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Displace ******************** */
@@ -24,6 +26,23 @@ static void cmp_node_displace_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class DisplaceOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DisplaceOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_displace_cc
void register_node_type_cmp_displace()
@@ -34,6 +53,7 @@ void register_node_type_cmp_displace()
cmp_node_type_base(&ntype, CMP_NODE_DISPLACE, "Displace", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_displace_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
index a8646d8498e..6a786571f43 100644
--- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc
@@ -8,16 +8,26 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* channel Distance Matte ********************************* */
namespace blender::nodes::node_composite_distance_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Key Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -26,7 +36,7 @@ static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *n
{
NodeChroma *c = MEM_cnew<NodeChroma>(__func__);
node->storage = c;
- c->channel = 1;
+ c->channel = CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA;
c->t1 = 0.1f;
c->t2 = 0.1f;
}
@@ -48,6 +58,61 @@ static void node_composit_buts_distance_matte(uiLayout *layout,
uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DistanceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float tolerance = get_tolerance();
+ const float falloff = get_falloff();
+
+ if (get_color_space() == CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA) {
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_distance_matte_rgba",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ return;
+ }
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_distance_matte_ycca",
+ inputs,
+ outputs,
+ GPU_uniform(&tolerance),
+ GPU_uniform(&falloff));
+ }
+
+ CMPNodeDistanceMatteColorSpace get_color_space()
+ {
+ return (CMPNodeDistanceMatteColorSpace)node_storage(bnode()).channel;
+ }
+
+ float get_tolerance()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_falloff()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new DistanceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_distance_matte_cc
void register_node_type_cmp_distance_matte()
@@ -62,6 +127,7 @@ void register_node_type_cmp_distance_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_distance_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
index 9dc2b223618..fec7879ed78 100644
--- a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Double Edge Mask ******************** */
@@ -35,6 +37,23 @@ static void node_composit_buts_double_edge_mask(uiLayout *layout,
uiItemR(col, ptr, "edge_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class DoubleEdgeMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Inner Mask").pass_through(get_result("Mask"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new DoubleEdgeMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_double_edge_mask_cc
void register_node_type_cmp_doubleedgemask()
@@ -46,6 +65,7 @@ void register_node_type_cmp_doubleedgemask()
cmp_node_type_base(&ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_MATTE);
ntype.declare = file_ns::cmp_node_double_edge_mask_declare;
ntype.draw_buttons = file_ns::node_composit_buts_double_edge_mask;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
index 4da6a0a442e..7c031b354e5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc
@@ -5,15 +5,26 @@
* \ingroup cmpnodes
*/
+#include <cmath>
+
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
namespace blender::nodes::node_composite_ellipsemask_cc {
+NODE_STORAGE_FUNCS(NodeEllipseMask)
+
static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
@@ -46,6 +57,93 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class EllipseMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ const Domain domain = compute_domain();
+
+ GPU_shader_uniform_2iv(shader, "domain_size", domain.size);
+
+ GPU_shader_uniform_2fv(shader, "location", get_location());
+ GPU_shader_uniform_2fv(shader, "radius", get_size() / 2.0f);
+ GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle()));
+ GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle()));
+
+ const Result &input_mask = get_input("Mask");
+ input_mask.bind_as_texture(shader, "base_mask_tx");
+
+ const Result &value = get_input("Value");
+ value.bind_as_texture(shader, "mask_value_tx");
+
+ Result &output_mask = get_result("Mask");
+ output_mask.allocate_texture(domain);
+ output_mask.bind_as_image(shader, "output_mask_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_mask.unbind_as_texture();
+ value.unbind_as_texture();
+ output_mask.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ Domain compute_domain() override
+ {
+ if (get_input("Mask").is_single_value()) {
+ return Domain(context().get_output_size());
+ }
+ return get_input("Mask").domain();
+ }
+
+ CMPNodeMaskType get_mask_type()
+ {
+ return (CMPNodeMaskType)bnode().custom1;
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_mask_type()) {
+ default:
+ case CMP_NODE_MASKTYPE_ADD:
+ return "compositor_ellipse_mask_add";
+ case CMP_NODE_MASKTYPE_SUBTRACT:
+ return "compositor_ellipse_mask_subtract";
+ case CMP_NODE_MASKTYPE_MULTIPLY:
+ return "compositor_ellipse_mask_multiply";
+ case CMP_NODE_MASKTYPE_NOT:
+ return "compositor_ellipse_mask_not";
+ }
+ }
+
+ float2 get_location()
+ {
+ return float2(node_storage(bnode()).x, node_storage(bnode()).y);
+ }
+
+ float2 get_size()
+ {
+ return float2(node_storage(bnode()).width, node_storage(bnode()).height);
+ }
+
+ float get_angle()
+ {
+ return node_storage(bnode()).rotation;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new EllipseMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_ellipsemask_cc
void register_node_type_cmp_ellipsemask()
@@ -61,6 +159,7 @@ void register_node_type_cmp_ellipsemask()
node_type_init(&ntype, file_ns::node_composit_init_ellipsemask);
node_type_storage(
&ntype, "NodeEllipseMask", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
index 881cfc11058..19b93680ff6 100644
--- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Exposure ******************** */
@@ -13,11 +17,33 @@ namespace blender::nodes::node_composite_exposure_cc {
static void cmp_node_exposure_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f).compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class ExposureShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_exposure", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ExposureShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_exposure_cc
void register_node_type_cmp_exposure()
@@ -28,6 +54,7 @@ void register_node_type_cmp_exposure()
cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_exposure_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc
index c343c21feb2..bd7b443e17e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_filter.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc
@@ -5,9 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** FILTER ******************** */
@@ -16,8 +21,15 @@ namespace blender::nodes::node_composite_filter_cc {
static void cmp_node_filter_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -26,6 +38,119 @@ static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class FilterOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = shader_manager().get(get_shader_name());
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_mat3_as_mat4(shader, "kernel", get_filter_kernel().ptr());
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ const Result &factor = get_input("Fac");
+ factor.bind_as_texture(shader, "factor_tx");
+
+ const Domain domain = compute_domain();
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ factor.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ CMPNodeFilterMethod get_filter_method()
+ {
+ return (CMPNodeFilterMethod)bnode().custom1;
+ }
+
+ float3x3 get_filter_kernel()
+ {
+ /* Initialize the kernels as arrays of rows with the top row first. Edge detection kernels
+ * return the kernel in the X direction, while the kernel in the Y direction will be computed
+ * inside the shader by transposing the kernel in the X direction. */
+ switch (get_filter_method()) {
+ case CMP_NODE_FILTER_SOFT: {
+ const float kernel[3][3] = {{1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f},
+ {2.0f / 16.0f, 4.0f / 16.0f, 2.0f / 16.0f},
+ {1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHARP_BOX: {
+ const float kernel[3][3] = {
+ {-1.0f, -1.0f, -1.0f}, {-1.0f, 9.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_LAPLACE: {
+ const float kernel[3][3] = {{-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f},
+ {-1.0f / 8.0f, 1.0f, -1.0f / 8.0f},
+ {-1.0f / 8.0f, -1.0f / 8.0f, -1.0f / 8.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SOBEL: {
+ const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {2.0f, 0.0f, -2.0f}, {1.0f, 0.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_PREWITT: {
+ const float kernel[3][3] = {{1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}, {1.0f, 0.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_KIRSCH: {
+ const float kernel[3][3] = {
+ {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}, {5.0f, -3.0f, -2.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHADOW: {
+ const float kernel[3][3] = {{1.0f, 2.0f, 1.0f}, {0.0f, 1.0f, 0.0f}, {-1.0f, -2.0f, -1.0f}};
+ return float3x3(kernel);
+ }
+ case CMP_NODE_FILTER_SHARP_DIAMOND: {
+ const float kernel[3][3] = {
+ {0.0f, -1.0f, 0.0f}, {-1.0f, 5.0f, -1.0f}, {0.0f, -1.0f, 0.0f}};
+ return float3x3(kernel);
+ }
+ default: {
+ const float kernel[3][3] = {{0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}};
+ return float3x3(kernel);
+ }
+ }
+ }
+
+ const char *get_shader_name()
+ {
+ switch (get_filter_method()) {
+ case CMP_NODE_FILTER_LAPLACE:
+ case CMP_NODE_FILTER_SOBEL:
+ case CMP_NODE_FILTER_PREWITT:
+ case CMP_NODE_FILTER_KIRSCH:
+ return "compositor_edge_filter";
+ case CMP_NODE_FILTER_SOFT:
+ case CMP_NODE_FILTER_SHARP_BOX:
+ case CMP_NODE_FILTER_SHADOW:
+ case CMP_NODE_FILTER_SHARP_DIAMOND:
+ default:
+ return "compositor_filter";
+ }
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new FilterOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_filter_cc
void register_node_type_cmp_filter()
@@ -39,6 +164,7 @@ void register_node_type_cmp_filter()
ntype.draw_buttons = file_ns::node_composit_buts_filter;
ntype.labelfunc = node_filter_label;
ntype.flag |= NODE_PREVIEW;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.cc b/source/blender/nodes/composite/nodes/node_composite_flip.cc
index 37b9a2d020d..aaa2b565ed2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_flip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc
@@ -5,9 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_utildefines.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** Flip ******************** */
@@ -16,7 +25,9 @@ namespace blender::nodes::node_composite_flip_cc {
static void cmp_node_flip_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -25,6 +36,56 @@ static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), Point
uiItemR(layout, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class FlipOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+
+ /* Can't flip a single value, pass it through to the output. */
+ if (input.is_single_value()) {
+ input.pass_through(result);
+ return;
+ }
+
+ GPUShader *shader = shader_manager().get("compositor_flip");
+ GPU_shader_bind(shader);
+
+ GPU_shader_uniform_1b(
+ shader, "flip_x", ELEM(get_flip_mode(), CMP_NODE_FLIP_X, CMP_NODE_FLIP_X_Y));
+ GPU_shader_uniform_1b(
+ shader, "flip_y", ELEM(get_flip_mode(), CMP_NODE_FLIP_Y, CMP_NODE_FLIP_X_Y));
+
+ input.bind_as_texture(shader, "input_tx");
+
+ const Domain domain = compute_domain();
+
+ result.allocate_texture(domain);
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input.unbind_as_texture();
+ result.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ CMPNodeFlipMode get_flip_mode()
+ {
+ return (CMPNodeFlipMode)bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new FlipOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_flip_cc
void register_node_type_cmp_flip()
@@ -36,6 +97,7 @@ void register_node_type_cmp_flip()
cmp_node_type_base(&ntype, CMP_NODE_FLIP, "Flip", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_flip_declare;
ntype.draw_buttons = file_ns::node_composit_buts_flip;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
index b4b8502e915..660d8068231 100644
--- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Gamma Tools ******************** */
@@ -13,15 +17,38 @@ namespace blender::nodes::node_composite_gamma_cc {
static void cmp_node_gamma_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Gamma"))
.default_value(1.0f)
.min(0.001f)
.max(10.0f)
- .subtype(PROP_UNSIGNED);
+ .subtype(PROP_UNSIGNED)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class GammaShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_gamma", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new GammaShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_gamma_cc
void register_node_type_cmp_gamma()
@@ -32,6 +59,7 @@ void register_node_type_cmp_gamma()
cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_gamma_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.cc b/source/blender/nodes/composite/nodes/node_composite_glare.cc
index 7f21d30cfa6..33577d5caf8 100644
--- a/source/blender/nodes/composite/nodes/node_composite_glare.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_glare_cc {
@@ -75,6 +77,23 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin
}
}
+using namespace blender::realtime_compositor;
+
+class GlareOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new GlareOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_glare_cc
void register_node_type_cmp_glare()
@@ -88,6 +107,7 @@ void register_node_type_cmp_glare()
ntype.draw_buttons = file_ns::node_composit_buts_glare;
node_type_init(&ntype, file_ns::node_composit_init_glare);
node_type_storage(&ntype, "NodeGlare", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
index 08a048829df..091864a06f7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Hue Saturation ******************** */
@@ -13,22 +17,56 @@ namespace blender::nodes::node_composite_hue_sat_val_cc {
static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Hue"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Saturation"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
b.add_input<decl::Float>(N_("Value"))
.default_value(1.0f)
.min(0.0f)
.max(2.0f)
- .subtype(PROP_FACTOR);
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(3);
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(4);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class HueSaturationValueShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_hue_saturation_value", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new HueSaturationValueShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_hue_sat_val_cc
void register_node_type_cmp_hue_sat()
@@ -39,6 +77,7 @@ void register_node_type_cmp_hue_sat()
cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_huesatval_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
index d252d96f8c3..6333860a19b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "BKE_colortools.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
#include "BKE_colortools.h"
@@ -13,8 +19,15 @@ namespace blender::nodes::node_composite_huecorrect_cc {
static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -35,6 +48,53 @@ static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node)
cumapping->cur = 1;
}
+using namespace blender::realtime_compositor;
+
+class HueCorrectShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ CurveMapping *curve_mapping = const_cast<CurveMapping *>(get_curve_mapping());
+
+ BKE_curvemapping_init(curve_mapping);
+ float *band_values;
+ int band_size;
+ BKE_curvemapping_table_RGBA(curve_mapping, &band_values, &band_size);
+ float band_layer;
+ GPUNodeLink *band_texture = GPU_color_band(material, band_size, band_values, &band_layer);
+
+ float range_minimums[CM_TOT];
+ BKE_curvemapping_get_range_minimums(curve_mapping, range_minimums);
+ float range_dividers[CM_TOT];
+ BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_hue_correct",
+ inputs,
+ outputs,
+ band_texture,
+ GPU_constant(&band_layer),
+ GPU_uniform(range_minimums),
+ GPU_uniform(range_dividers));
+ }
+
+ const CurveMapping *get_curve_mapping()
+ {
+ return static_cast<const CurveMapping *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new HueCorrectShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_huecorrect_cc
void register_node_type_cmp_huecorrect()
@@ -48,6 +108,7 @@ void register_node_type_cmp_huecorrect()
node_type_size(&ntype, 320, 140, 500);
node_type_init(&ntype, file_ns::node_composit_init_huecorrect);
node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
index 25ab9aa63fc..ac8456cb931 100644
--- a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** ID Mask ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_antialiasing", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class IDMaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("ID value").pass_through(get_result("Alpha"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new IDMaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_id_mask_cc
void register_node_type_cmp_idmask()
@@ -37,6 +56,7 @@ void register_node_type_cmp_idmask()
cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_idmask_declare;
ntype.draw_buttons = file_ns::node_composit_buts_id_mask;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc
index d75aa575395..4d1eff0b940 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_image.cc
@@ -8,6 +8,7 @@
#include "node_composite_util.hh"
#include "BLI_linklist.h"
+#include "BLI_math_vec_types.hh"
#include "BLI_utildefines.h"
#include "BKE_context.h"
@@ -17,6 +18,8 @@
#include "BKE_main.h"
#include "BKE_scene.h"
+#include "DEG_depsgraph_query.h"
+
#include "DNA_scene_types.h"
#include "RE_engine.h"
@@ -27,6 +30,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
/* **************** IMAGE (and RenderResult, multilayer image) ******************** */
static bNodeSocketTemplate cmp_node_rlayers_out[] = {
@@ -433,6 +442,215 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree),
}
}
+using namespace blender::realtime_compositor;
+
+class ImageOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (!is_valid()) {
+ allocate_invalid();
+ return;
+ }
+
+ update_image_frame_number();
+
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ compute_output(output->identifier);
+ }
+ }
+
+ /* Returns true if the node results can be computed, otherwise, returns false. */
+ bool is_valid()
+ {
+ Image *image = get_image();
+ ImageUser *image_user = get_image_user();
+ if (!image || !image_user) {
+ return false;
+ }
+
+ if (BKE_image_is_multilayer(image)) {
+ if (!image->rr) {
+ return false;
+ }
+
+ RenderLayer *render_layer = get_render_layer();
+ if (!render_layer) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */
+ void allocate_invalid()
+ {
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ if (!should_compute_output(output->identifier)) {
+ continue;
+ }
+
+ Result &result = get_result(output->identifier);
+ result.allocate_invalid();
+ }
+ }
+
+ /* Compute the effective frame number of the image if it was animated and invalidate the cached
+ * GPU texture if the computed frame number is different. */
+ void update_image_frame_number()
+ {
+ BKE_image_user_frame_calc(get_image(), get_image_user(), context().get_frame_number());
+ }
+
+ void compute_output(StringRef identifier)
+ {
+ if (!should_compute_output(identifier)) {
+ return;
+ }
+
+ ImageUser image_user = compute_image_user_for_output(identifier);
+ GPUTexture *image_texture = BKE_image_get_gpu_texture(get_image(), &image_user, nullptr);
+
+ const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture));
+ Result &result = get_result(identifier);
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get(get_shader_name(identifier));
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(image_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(image_texture);
+ result.unbind_as_image();
+ }
+
+ /* Get a copy of the image user that is appropriate to retrieve the image buffer for the output
+ * with the given identifier. This essentially sets the appropriate pass and view indices that
+ * corresponds to the output. */
+ ImageUser compute_image_user_for_output(StringRef identifier)
+ {
+ ImageUser image_user = *get_image_user();
+
+ /* Set the needed view. */
+ image_user.view = get_view_index();
+
+ /* Set the needed pass. */
+ if (BKE_image_is_multilayer(get_image())) {
+ image_user.pass = get_pass_index(get_pass_name(identifier));
+ BKE_image_multilayer_index(get_image()->rr, &image_user);
+ }
+ else {
+ BKE_image_multiview_index(get_image(), &image_user);
+ }
+
+ return image_user;
+ }
+
+ /* Get the shader that should be used to compute the output with the given identifier. The
+ * shaders just copy the retrieved image textures into the results except for the alpha output,
+ * which extracts the alpha and writes it to the result instead. Note that a call to a host
+ * texture copy doesn't work because results are stored in a different half float formats. */
+ const char *get_shader_name(StringRef identifier)
+ {
+ if (identifier == "Alpha") {
+ return "compositor_extract_alpha_from_color";
+ }
+ else if (get_result(identifier).type() == ResultType::Color) {
+ return "compositor_convert_color_to_half_color";
+ }
+ else {
+ return "compositor_convert_float_to_half_float";
+ }
+ }
+
+ Image *get_image()
+ {
+ return (Image *)bnode().id;
+ }
+
+ ImageUser *get_image_user()
+ {
+ return static_cast<ImageUser *>(bnode().storage);
+ }
+
+ /* Get the render layer selected in the node assuming the image is a multilayer image. */
+ RenderLayer *get_render_layer()
+ {
+ const ListBase *layers = &get_image()->rr->layers;
+ return static_cast<RenderLayer *>(BLI_findlink(layers, get_image_user()->layer));
+ }
+
+ /* Get the name of the pass corresponding to the output with the given identifier assuming the
+ * image is a multilayer image. */
+ const char *get_pass_name(StringRef identifier)
+ {
+ DOutputSocket output = node().output_by_identifier(identifier);
+ return static_cast<NodeImageLayer *>(output->storage)->pass_name;
+ }
+
+ /* Get the index of the pass with the given name in the selected render layer's passes list
+ * assuming the image is a multilayer image. */
+ int get_pass_index(const char *name)
+ {
+ return BLI_findstringindex(&get_render_layer()->passes, name, offsetof(RenderPass, name));
+ }
+
+ /* Get the index of the view selected in the node. If the image is not a multi-view image or only
+ * has a single view, then zero is returned. Otherwise, if the image is a multi-view image, the
+ * index of the selected view is returned. However, note that the value of the view member of the
+ * image user is not the actual index of the view. More specifically, the index 0 is reserved to
+ * denote the special mode of operation "All", which dynamically selects the view whose name
+ * matches the view currently being rendered. It follows that the views are then indexed starting
+ * from 1. So for non zero view values, the actual index of the view is the value of the view
+ * member of the image user minus 1. */
+ int get_view_index()
+ {
+ /* The image is not a multi-view image, so just return zero. */
+ if (!BKE_image_is_multiview(get_image())) {
+ return 0;
+ }
+
+ const ListBase *views = &get_image()->rr->views;
+ /* There is only one view and its index is 0. */
+ if (BLI_listbase_count_at_most(views, 2) < 2) {
+ return 0;
+ }
+
+ const int view = get_image_user()->view;
+ /* The view is not zero, which means it is manually specified and the actual index is then the
+ * view value minus 1. */
+ if (view != 0) {
+ return view - 1;
+ }
+
+ /* Otherwise, the view value is zero, denoting the special mode of operation "All", which finds
+ * the index of the view whose name matches the view currently being rendered. */
+ const char *view_name = context().get_view_name().data();
+ const int matched_view = BLI_findstringindex(views, view_name, offsetof(RenderView, name));
+
+ /* No view matches the view currently being rendered, so fallback to the first view. */
+ if (matched_view == -1) {
+ return 0;
+ }
+
+ return matched_view;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ImageOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_image_cc
void register_node_type_cmp_image()
@@ -446,6 +664,7 @@ void register_node_type_cmp_image()
node_type_storage(
&ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image);
node_type_update(&ntype, file_ns::cmp_node_image_update);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.labelfunc = node_image_label;
ntype.flag |= NODE_PREVIEW;
@@ -469,7 +688,7 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index)
return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name;
}
-namespace blender::nodes::node_composite_image_cc {
+namespace blender::nodes::node_composite_render_layer_cc {
static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr)
{
@@ -595,11 +814,60 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer
RNA_string_set(&op_ptr, "scene", scene_name);
}
-} // namespace blender::nodes::node_composite_image_cc
+using namespace blender::realtime_compositor;
+
+class RenderLayerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const int view_layer = bnode().custom1;
+ GPUTexture *pass_texture = context().get_input_texture(view_layer, SCE_PASS_COMBINED);
+ const int2 size = int2(GPU_texture_width(pass_texture), GPU_texture_height(pass_texture));
+
+ /* Compute image output. */
+ Result &image_result = get_result("Image");
+ image_result.allocate_texture(Domain(size));
+ GPU_texture_copy(image_result.texture(), pass_texture);
+
+ /* Compute alpha output. */
+ Result &alpha_result = get_result("Alpha");
+ alpha_result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(pass_texture, input_unit);
+
+ alpha_result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(pass_texture);
+ alpha_result.unbind_as_image();
+
+ /* Other output passes are not supported for now, so allocate them as invalid. */
+ for (const bNodeSocket *output : this->node()->output_sockets()) {
+ if (!STREQ(output->identifier, "Image") && !STREQ(output->identifier, "Alpha")) {
+ get_result(output->identifier).allocate_invalid();
+ }
+ }
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RenderLayerOperation(context, node);
+}
+
+} // namespace blender::nodes::node_composite_render_layer_cc
void register_node_type_cmp_rlayers()
{
- namespace file_ns = blender::nodes::node_composite_image_cc;
+ namespace file_ns = blender::nodes::node_composite_render_layer_cc;
static bNodeType ntype;
@@ -608,6 +876,7 @@ void register_node_type_cmp_rlayers()
ntype.draw_buttons = file_ns::node_composit_buts_viewlayers;
ntype.initfunc_api = file_ns::node_composit_init_rlayers;
ntype.poll = file_ns::node_composit_poll_rlayers;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.flag |= NODE_PREVIEW;
node_type_storage(
&ntype, nullptr, file_ns::node_composit_free_rlayers, file_ns::node_composit_copy_rlayers);
diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
index 2958d1b2869..f6e46bef299 100644
--- a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Inpaint/ ******************** */
@@ -25,6 +27,23 @@ static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class InpaintOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new InpaintOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_inpaint_cc
void register_node_type_cmp_inpaint()
@@ -36,6 +55,7 @@ void register_node_type_cmp_inpaint()
cmp_node_type_base(&ntype, CMP_NODE_INPAINT, "Inpaint", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_inpaint_declare;
ntype.draw_buttons = file_ns::node_composit_buts_inpaint;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc
index 6dff043537a..4bfcc7b6b9c 100644
--- a/source/blender/nodes/composite/nodes/node_composite_invert.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** INVERT ******************** */
@@ -16,8 +20,15 @@ namespace blender::nodes::node_composite_invert_cc {
static void cmp_node_invert_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Color>(N_("Color"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Color"));
}
@@ -35,6 +46,45 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(col, ptr, "invert_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class InvertShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float do_rgb = get_do_rgb();
+ const float do_alpha = get_do_alpha();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_invert",
+ inputs,
+ outputs,
+ GPU_constant(&do_rgb),
+ GPU_constant(&do_alpha));
+ }
+
+ bool get_do_rgb()
+ {
+ return bnode().custom1 & CMP_CHAN_RGB;
+ }
+
+ bool get_do_alpha()
+ {
+ return bnode().custom1 & CMP_CHAN_A;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new InvertShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_invert_cc
void register_node_type_cmp_invert()
@@ -47,6 +97,7 @@ void register_node_type_cmp_invert()
ntype.declare = file_ns::cmp_node_invert_declare;
ntype.draw_buttons = file_ns::node_composit_buts_invert;
node_type_init(&ntype, file_ns::node_composit_init_invert);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.cc b/source/blender/nodes/composite/nodes/node_composite_keying.cc
index fbfdf2ad3c6..8b584e216cd 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keying.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc
@@ -14,6 +14,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Keying ******************** */
@@ -63,6 +65,25 @@ static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "blur_post", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class KeyingOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Matte").allocate_invalid();
+ get_result("Edges").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new KeyingOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_keying_cc
void register_node_type_cmp_keying()
@@ -77,6 +98,7 @@ void register_node_type_cmp_keying()
node_type_init(&ntype, file_ns::node_composit_init_keying);
node_type_storage(
&ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
index e835ee9e721..9eec705b6ca 100644
--- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc
@@ -21,6 +21,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Keying Screen ******************** */
@@ -78,6 +80,23 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point
}
}
+using namespace blender::realtime_compositor;
+
+class KeyingScreenOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Screen").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new KeyingScreenOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_keyingscreen_cc
void register_node_type_cmp_keyingscreen()
@@ -92,6 +111,7 @@ void register_node_type_cmp_keyingscreen()
ntype.initfunc_api = file_ns::node_composit_init_keyingscreen;
node_type_storage(
&ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
index 593b7cc9b71..260fccf66d0 100644
--- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc
@@ -5,20 +5,50 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_base.h"
+#include "BLI_math_vec_types.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
+/* Distortion can't be exactly -1.0 as it will cause infinite pincushion distortion. */
+#define MINIMUM_DISTORTION -0.999f
+/* Arbitrary scaling factor for the dispersion input in projector distortion mode. */
+#define PROJECTOR_DISPERSION_SCALE 5.0f
+/* Arbitrary scaling factor for the dispersion input in screen distortion mode. */
+#define SCREEN_DISPERSION_SCALE 4.0f
+/* Arbitrary scaling factor for the distortion input. */
+#define DISTORTION_SCALE 4.0f
+
namespace blender::nodes::node_composite_lensdist_cc {
+NODE_STORAGE_FUNCS(NodeLensDist)
+
static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Distort")).default_value(0.0f).min(-0.999f).max(1.0f);
- b.add_input<decl::Float>(N_("Dispersion")).default_value(0.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Distort"))
+ .default_value(0.0f)
+ .min(MINIMUM_DISTORTION)
+ .max(1.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Dispersion"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -42,6 +72,173 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "use_fit", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LensDistortionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ if (is_identity()) {
+ get_input("Image").pass_through(get_result("Image"));
+ return;
+ }
+
+ if (get_is_projector()) {
+ execute_projector_distortion();
+ }
+ else {
+ execute_screen_distortion();
+ }
+ }
+
+ void execute_projector_distortion()
+ {
+ GPUShader *shader = shader_manager().get("compositor_projector_lens_distortion");
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+
+ const float dispersion = (get_dispersion() * PROJECTOR_DISPERSION_SCALE) / domain.size.x;
+ GPU_shader_uniform_1f(shader, "dispersion", dispersion);
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ void execute_screen_distortion()
+ {
+ GPUShader *shader = shader_manager().get(get_screen_distortion_shader());
+ GPU_shader_bind(shader);
+
+ const Result &input_image = get_input("Image");
+ input_image.bind_as_texture(shader, "input_tx");
+
+ GPU_texture_filter_mode(input_image.texture(), true);
+ GPU_texture_wrap_mode(input_image.texture(), false, false);
+
+ const Domain domain = compute_domain();
+
+ const float3 chromatic_distortion = compute_chromatic_distortion();
+ GPU_shader_uniform_3fv(shader, "chromatic_distortion", chromatic_distortion);
+
+ GPU_shader_uniform_1f(shader, "scale", compute_scale());
+
+ Result &output_image = get_result("Image");
+ output_image.allocate_texture(domain);
+ output_image.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, domain.size);
+
+ input_image.unbind_as_texture();
+ output_image.unbind_as_image();
+ GPU_shader_unbind();
+ }
+
+ const char *get_screen_distortion_shader()
+ {
+ if (get_is_jitter()) {
+ return "compositor_screen_lens_distortion_jitter";
+ }
+ return "compositor_screen_lens_distortion";
+ }
+
+ float get_distortion()
+ {
+ const Result &input = get_input("Distort");
+ return clamp_f(input.get_float_value_default(0.0f), MINIMUM_DISTORTION, 1.0f);
+ }
+
+ float get_dispersion()
+ {
+ const Result &input = get_input("Dispersion");
+ return clamp_f(input.get_float_value_default(0.0f), 0.0f, 1.0f);
+ }
+
+ /* Get the distortion amount for each channel. The green channel has a distortion amount that
+ * matches that specified in the node inputs, while the red and blue channels have higher and
+ * lower distortion amounts respectively based on the dispersion value. */
+ float3 compute_chromatic_distortion()
+ {
+ const float green_distortion = get_distortion();
+ const float dispersion = get_dispersion() / SCREEN_DISPERSION_SCALE;
+ const float red_distortion = clamp_f(green_distortion + dispersion, MINIMUM_DISTORTION, 1.0f);
+ const float blue_distortion = clamp_f(green_distortion - dispersion, MINIMUM_DISTORTION, 1.0f);
+ return float3(red_distortion, green_distortion, blue_distortion) * DISTORTION_SCALE;
+ }
+
+ /* The distortion model will distort the image in such a way that the result will no longer
+ * fit the domain of the original image, so we scale the image to account for that. If get_is_fit
+ * is false, then the scaling factor will be such that the furthest pixels horizontally and
+ * vertically are at the boundary of the image. Otherwise, if get_is_fit is true, the scaling
+ * factor will be such that the furthest pixels diagonally are at the corner of the image. */
+ float compute_scale()
+ {
+ const float3 distortion = compute_chromatic_distortion() / DISTORTION_SCALE;
+ const float maximum_distortion = max_fff(distortion[0], distortion[1], distortion[2]);
+
+ if (get_is_fit() && (maximum_distortion > 0.0f)) {
+ return 1.0f / (1.0f + 2.0f * maximum_distortion);
+ }
+ return 1.0f / (1.0f + maximum_distortion);
+ }
+
+ bool get_is_projector()
+ {
+ return node_storage(bnode()).proj;
+ }
+
+ bool get_is_jitter()
+ {
+ return node_storage(bnode()).jit;
+ }
+
+ bool get_is_fit()
+ {
+ return node_storage(bnode()).fit;
+ }
+
+ /* Returns true if the operation does nothing and the input can be passed through. */
+ bool is_identity()
+ {
+ /* The input is a single value and the operation does nothing. */
+ if (get_input("Image").is_single_value()) {
+ return true;
+ }
+
+ /* Projector have zero dispersion and does nothing. */
+ if (get_is_projector() && get_dispersion() == 0.0f) {
+ return true;
+ }
+
+ /* Both distortion and dispersion are zero and the operation does nothing. */
+ if (get_distortion() == 0.0f && get_dispersion() == 0.0f) {
+ return true;
+ }
+
+ return false;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new LensDistortionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_lensdist_cc
void register_node_type_cmp_lensdist()
@@ -56,6 +253,7 @@ void register_node_type_cmp_lensdist()
node_type_init(&ntype, file_ns::node_composit_init_lensdist);
node_type_storage(
&ntype, "NodeLensDist", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc
index a30567672f0..2f1ebeb79b5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_levels.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** LEVELS ******************** */
@@ -31,6 +33,24 @@ static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C)
uiItemR(layout, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LevelsOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Mean").allocate_invalid();
+ get_result("Std Dev").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new LevelsOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_levels_cc
void register_node_type_cmp_view_levels()
@@ -44,6 +64,7 @@ void register_node_type_cmp_view_levels()
ntype.draw_buttons = file_ns::node_composit_buts_view_levels;
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_view_levels);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
index 94697a2aafd..59ae62ec411 100644
--- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc
@@ -5,18 +5,28 @@
* \ingroup cmpnodes
*/
+#include "IMB_colormanagement.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* ******************* Luma Matte Node ********************************* */
namespace blender::nodes::node_composite_luma_matte_cc {
+NODE_STORAGE_FUNCS(NodeChroma)
+
static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
b.add_output<decl::Float>(N_("Matte"));
}
@@ -40,6 +50,48 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C),
col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class LuminanceMatteShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float high = get_high();
+ const float low = get_low();
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_luminance_matte",
+ inputs,
+ outputs,
+ GPU_uniform(&high),
+ GPU_uniform(&low),
+ GPU_constant(luminance_coefficients));
+ }
+
+ float get_high()
+ {
+ return node_storage(bnode()).t1;
+ }
+
+ float get_low()
+ {
+ return node_storage(bnode()).t2;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new LuminanceMatteShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_luma_matte_cc
void register_node_type_cmp_luma_matte()
@@ -54,6 +106,7 @@ void register_node_type_cmp_luma_matte()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_luma_matte);
node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_range.cc b/source/blender/nodes/composite/nodes/node_composite_map_range.cc
index e52c6d096b9..e72869efa93 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_range.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_range.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Map Range ******************** */
@@ -16,11 +20,31 @@ namespace blender::nodes::node_composite_map_range_cc {
static void cmp_node_map_range_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("From Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("From Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("To Min")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("To Max")).default_value(1.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("From Min"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("From Max"))
+ .default_value(1.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("To Min"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(3);
+ b.add_input<decl::Float>(N_("To Max"))
+ .default_value(1.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(4);
b.add_output<decl::Float>(N_("Value"));
}
@@ -32,6 +56,38 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C),
uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapRangeShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const float should_clamp = get_should_clamp();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_map_range",
+ inputs,
+ outputs,
+ GPU_constant(&should_clamp));
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MapRangeShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_map_range_cc
void register_node_type_cmp_map_range()
@@ -43,6 +99,7 @@ void register_node_type_cmp_map_range()
cmp_node_type_base(&ntype, CMP_NODE_MAP_RANGE, "Map Range", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_map_range_declare;
ntype.draw_buttons = file_ns::node_composit_buts_map_range;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
index 31961f07ea4..4f660d62c3b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Map UV ******************** */
@@ -26,6 +28,23 @@ static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapUVOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MapUVOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_map_uv_cc
void register_node_type_cmp_mapuv()
@@ -37,6 +56,7 @@ void register_node_type_cmp_mapuv()
cmp_node_type_base(&ntype, CMP_NODE_MAP_UV, "Map UV", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_map_uv_declare;
ntype.draw_buttons = file_ns::node_composit_buts_map_uv;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
index bb42628ed3d..e30de39605d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc
@@ -12,15 +12,25 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** MAP VALUE ******************** */
namespace blender::nodes::node_composite_map_value_cc {
+NODE_STORAGE_FUNCS(TexMapping)
+
static void cmp_node_map_value_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Value"));
}
@@ -50,6 +60,51 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C),
uiItemR(sub, ptr, "max", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MapValueShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ const TexMapping &texture_mapping = node_storage(bnode());
+
+ const float use_min = get_use_min();
+ const float use_max = get_use_max();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_map_value",
+ inputs,
+ outputs,
+ GPU_uniform(texture_mapping.loc),
+ GPU_uniform(texture_mapping.size),
+ GPU_constant(&use_min),
+ GPU_uniform(texture_mapping.min),
+ GPU_constant(&use_max),
+ GPU_uniform(texture_mapping.max));
+ }
+
+ bool get_use_min()
+ {
+ return node_storage(bnode()).flag & TEXMAP_CLIP_MIN;
+ }
+
+ bool get_use_max()
+ {
+ return node_storage(bnode()).flag & TEXMAP_CLIP_MAX;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MapValueShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_map_value_cc
void register_node_type_cmp_map_value()
@@ -63,6 +118,7 @@ void register_node_type_cmp_map_value()
ntype.draw_buttons = file_ns::node_composit_buts_map_value;
node_type_init(&ntype, file_ns::node_composit_init_map_value);
node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc
index 5b8fac5d1c0..2372dbae3f2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mask.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Mask ******************** */
@@ -74,6 +76,23 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p
}
}
+using namespace blender::realtime_compositor;
+
+class MaskOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Mask").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MaskOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_mask_cc
void register_node_type_cmp_mask()
@@ -87,6 +106,7 @@ void register_node_type_cmp_mask()
ntype.draw_buttons = file_ns::node_composit_buts_mask;
node_type_init(&ntype, file_ns::node_composit_init_mask);
ntype.labelfunc = file_ns::node_mask_label;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
node_type_storage(&ntype, "NodeMask", node_free_standard_storage, node_copy_standard_storage);
diff --git a/source/blender/nodes/composite/nodes/node_composite_math.cc b/source/blender/nodes/composite/nodes/node_composite_math.cc
index 7b2eadef2cb..4baf057913e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_math.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_math.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
+#include "NOD_math_functions.hh"
+
#include "node_composite_util.hh"
/* **************** SCALAR MATH ******************** */
@@ -13,18 +19,72 @@ namespace blender::nodes::node_composite_math_cc {
static void cmp_node_math_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Float>(N_("Value"))
+ .default_value(0.5f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Value"), "Value_001")
.default_value(0.5f)
.min(-10000.0f)
- .max(10000.0f);
+ .max(10000.0f)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Value"), "Value_002")
.default_value(0.5f)
.min(-10000.0f)
- .max(10000.0f);
+ .max(10000.0f)
+ .compositor_domain_priority(2);
b.add_output<decl::Float>(N_("Value"));
}
+using namespace blender::realtime_compositor;
+
+class MathShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+
+ if (!get_should_clamp()) {
+ return;
+ }
+
+ const float min = 0.0f;
+ const float max = 1.0f;
+ GPU_link(material,
+ "clamp_value",
+ get_output("Value").link,
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &get_output("Value").link);
+ }
+
+ NodeMathOperation get_operation()
+ {
+ return (NodeMathOperation)bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ return get_float_math_operation_info(get_operation())->shader_name.c_str();
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom2 & SHD_MATH_CLAMP;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MathShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_math_cc
void register_node_type_cmp_math()
@@ -37,6 +97,7 @@ void register_node_type_cmp_math()
ntype.declare = file_ns::cmp_node_math_declare;
ntype.labelfunc = node_math_label;
node_type_update(&ntype, node_math_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
index fc11aa188b0..a1fbbfe7d40 100644
--- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc
@@ -5,6 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "DNA_material_types.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** MIX RGB ******************** */
@@ -13,12 +21,122 @@ namespace blender::nodes::node_composite_mixrgb_cc {
static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Color>(N_("Image"), "Image_001")
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class MixRGBShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_use_alpha()) {
+ GPU_link(material,
+ "multiply_by_alpha",
+ get_input_link("Fac"),
+ get_input_link("Image_001"),
+ &get_input("Fac").link);
+ }
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+
+ if (!get_should_clamp()) {
+ return;
+ }
+
+ const float min[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ const float max[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ GPU_link(material,
+ "clamp_color",
+ get_output("Image").link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &get_output("Image").link);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_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";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+
+ bool get_use_alpha()
+ {
+ return bnode().custom2 & SHD_MIXRGB_USE_ALPHA;
+ }
+
+ bool get_should_clamp()
+ {
+ return bnode().custom2 & SHD_MIXRGB_CLAMP;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new MixRGBShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_mixrgb_cc
void register_node_type_cmp_mix_rgb()
@@ -31,6 +149,7 @@ void register_node_type_cmp_mix_rgb()
ntype.flag |= NODE_PREVIEW;
ntype.declare = file_ns::cmp_node_mixrgb_declare;
ntype.labelfunc = node_blend_label;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
index a4d5f294fe0..b9d9620a214 100644
--- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc
@@ -5,8 +5,13 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "BKE_movieclip.h"
+#include "BKE_tracking.h"
+
#include "DNA_defaults.h"
#include "RNA_access.h"
@@ -14,6 +19,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_movieclip_cc {
@@ -79,6 +90,184 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point
uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings");
}
+using namespace blender::realtime_compositor;
+
+class MovieClipOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUTexture *movie_clip_texture = get_movie_clip_texture();
+
+ compute_image(movie_clip_texture);
+ compute_alpha(movie_clip_texture);
+ compute_stabilization_data(movie_clip_texture);
+
+ free_movie_clip_texture();
+ }
+
+ void compute_image(GPUTexture *movie_clip_texture)
+ {
+ if (!should_compute_output("Image")) {
+ return;
+ }
+
+ Result &result = get_result("Image");
+
+ /* The movie clip texture is invalid or missing, set an appropriate fallback value. */
+ if (!movie_clip_texture) {
+ result.allocate_invalid();
+ return;
+ }
+
+ const int2 size = int2(GPU_texture_width(movie_clip_texture),
+ GPU_texture_height(movie_clip_texture));
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_half_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(movie_clip_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(movie_clip_texture);
+ result.unbind_as_image();
+ }
+
+ void compute_alpha(GPUTexture *movie_clip_texture)
+ {
+ if (!should_compute_output("Alpha")) {
+ return;
+ }
+
+ Result &result = get_result("Alpha");
+
+ /* The movie clip texture is invalid or missing, set an appropriate fallback value. */
+ if (!movie_clip_texture) {
+ result.allocate_single_value();
+ result.set_float_value(1.0f);
+ return;
+ }
+
+ const int2 size = int2(GPU_texture_width(movie_clip_texture),
+ GPU_texture_height(movie_clip_texture));
+ result.allocate_texture(Domain(size));
+
+ GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color");
+ GPU_shader_bind(shader);
+
+ const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx");
+ GPU_texture_bind(movie_clip_texture, input_unit);
+
+ result.bind_as_image(shader, "output_img");
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ GPU_shader_unbind();
+ GPU_texture_unbind(movie_clip_texture);
+ result.unbind_as_image();
+ }
+
+ void compute_stabilization_data(GPUTexture *movie_clip_texture)
+ {
+ /* The movie clip texture is invalid or missing, set appropriate fallback values. */
+ if (!movie_clip_texture) {
+ if (should_compute_output("Offset X")) {
+ Result &result = get_result("Offset X");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ if (should_compute_output("Offset Y")) {
+ Result &result = get_result("Offset Y");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ if (should_compute_output("Scale")) {
+ Result &result = get_result("Scale");
+ result.allocate_single_value();
+ result.set_float_value(1.0f);
+ }
+ if (should_compute_output("Angle")) {
+ Result &result = get_result("Angle");
+ result.allocate_single_value();
+ result.set_float_value(0.0f);
+ }
+ return;
+ }
+
+ MovieClip *movie_clip = get_movie_clip();
+ const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip,
+ context().get_frame_number());
+ const int width = GPU_texture_width(movie_clip_texture);
+ const int height = GPU_texture_height(movie_clip_texture);
+
+ /* If the movie clip has no stabilization data, it will initialize the given values with
+ * fallback values regardless, so no need to handle that case. */
+ float2 offset;
+ float scale, angle;
+ BKE_tracking_stabilization_data_get(
+ movie_clip, frame_number, width, height, offset, &scale, &angle);
+
+ if (should_compute_output("Offset X")) {
+ Result &result = get_result("Offset X");
+ result.allocate_single_value();
+ result.set_float_value(offset.x);
+ }
+ if (should_compute_output("Offset Y")) {
+ Result &result = get_result("Offset Y");
+ result.allocate_single_value();
+ result.set_float_value(offset.y);
+ }
+ if (should_compute_output("Scale")) {
+ Result &result = get_result("Scale");
+ result.allocate_single_value();
+ result.set_float_value(scale);
+ }
+ if (should_compute_output("Angle")) {
+ Result &result = get_result("Angle");
+ result.allocate_single_value();
+ result.set_float_value(angle);
+ }
+ }
+
+ GPUTexture *get_movie_clip_texture()
+ {
+ MovieClip *movie_clip = get_movie_clip();
+ MovieClipUser *movie_clip_user = get_movie_clip_user();
+ BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number());
+ return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user);
+ }
+
+ void free_movie_clip_texture()
+ {
+ MovieClip *movie_clip = get_movie_clip();
+ if (movie_clip) {
+ BKE_movieclip_free_gputexture(movie_clip);
+ }
+ }
+
+ MovieClip *get_movie_clip()
+ {
+ return (MovieClip *)bnode().id;
+ }
+
+ MovieClipUser *get_movie_clip_user()
+ {
+ return static_cast<MovieClipUser *>(bnode().storage);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MovieClipOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_movieclip_cc
void register_node_type_cmp_movieclip()
@@ -91,6 +280,7 @@ void register_node_type_cmp_movieclip()
ntype.declare = file_ns::cmp_node_movieclip_declare;
ntype.draw_buttons = file_ns::node_composit_buts_movieclip;
ntype.draw_buttons_ex = file_ns::node_composit_buts_movieclip_ex;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.initfunc_api = file_ns::init;
ntype.flag |= NODE_PREVIEW;
node_type_storage(
diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
index 4d52a767b8a..88638586594 100644
--- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc
@@ -12,6 +12,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Translate ******************** */
@@ -81,6 +83,23 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po
uiItemR(layout, ptr, "distortion_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class MovieDistortionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new MovieDistortionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_moviedistortion_cc
void register_node_type_cmp_moviedistortion()
@@ -95,6 +114,7 @@ void register_node_type_cmp_moviedistortion()
ntype.labelfunc = file_ns::label;
ntype.initfunc_api = file_ns::init;
node_type_storage(&ntype, nullptr, file_ns::storage_free, file_ns::storage_copy);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc
index b4dd0bbacd0..a1a6303e21b 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normal.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** NORMAL ******************** */
@@ -17,7 +21,8 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b)
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
.max(1.0f)
- .subtype(PROP_DIRECTION);
+ .subtype(PROP_DIRECTION)
+ .compositor_domain_priority(0);
b.add_output<decl::Vector>(N_("Normal"))
.default_value({0.0f, 0.0f, 1.0f})
.min(-1.0f)
@@ -26,6 +31,40 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Dot"));
}
+using namespace blender::realtime_compositor;
+
+class NormalShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material,
+ &bnode(),
+ "node_composite_normal",
+ inputs,
+ outputs,
+ GPU_uniform(get_vector_value()));
+ }
+
+ /* The vector value is stored in the default value of the output socket. */
+ const float *get_vector_value()
+ {
+ return node()
+ .output_by_identifier("Normal")
+ ->default_value_typed<bNodeSocketValueVector>()
+ ->value;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new NormalShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_normal_cc
void register_node_type_cmp_normal()
@@ -36,6 +75,7 @@ void register_node_type_cmp_normal()
cmp_node_type_base(&ntype, CMP_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_normal_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.cc b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
index 49318279bdb..21765825468 100644
--- a/source/blender/nodes/composite/nodes/node_composite_normalize.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** NORMALIZE single channel, useful for Z buffer ******************** */
@@ -17,6 +19,23 @@ static void cmp_node_normalize_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Value"));
}
+using namespace blender::realtime_compositor;
+
+class NormalizeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Value").pass_through(get_result("Value"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new NormalizeOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_normalize_cc
void register_node_type_cmp_normalize()
@@ -27,6 +46,7 @@ void register_node_type_cmp_normalize()
cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR);
ntype.declare = file_ns::cmp_node_normalize_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
index f1621f83ac3..5ed383977a5 100644
--- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc
@@ -24,6 +24,8 @@
#include "IMB_openexr.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** OUTPUT FILE ******************** */
@@ -114,7 +116,7 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list,
bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree,
bNode *node,
const char *name,
- ImageFormatData *im_format)
+ const ImageFormatData *im_format)
{
NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage;
bNodeSocket *sock = nodeAddStaticSocket(
@@ -439,6 +441,22 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
}
}
+using namespace blender::realtime_compositor;
+
+class OutputFileOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new OutputFileOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_output_file_cc
void register_node_type_cmp_output_file()
@@ -455,6 +473,7 @@ void register_node_type_cmp_output_file()
node_type_storage(
&ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file);
node_type_update(&ntype, file_ns::update_output_file);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
index 529aa0f84de..c4e42f8247d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc
@@ -5,6 +5,11 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+#include "BLI_math_vector.hh"
+
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Pixelate ******************** */
@@ -17,6 +22,49 @@ static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Color"));
}
+using namespace blender::realtime_compositor;
+
+class PixelateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ /* It might seems strange that the input is passed through without any processing, but note
+ * that the actual processing happens inside the domain realization input processor of the
+ * input. Indeed, the pixelate node merely realizes its input on a smaller-sized domain that
+ * matches its apparent size, that is, its size after the domain transformation. The pixelate
+ * node has no effect if the input is scaled-up. See the compute_domain method for more
+ * information. */
+ get_input("Color").pass_through(get_result("Color"));
+ }
+
+ /* Compute a smaller-sized domain that matches the apparent size of the input while having a unit
+ * scale transformation, see the execute method for more information. */
+ Domain compute_domain() override
+ {
+ Domain domain = get_input("Color").domain();
+
+ /* Get the scaling component of the domain transformation, but make sure it doesn't exceed 1,
+ * because pixelation should only happen if the input is scaled down. */
+ const float2 scale = math::min(float2(1.0f), domain.transformation.scale_2d());
+
+ /* Multiply the size of the domain by its scale to match its apparent size, but make sure it is
+ * at least 1 pixel in both axis. */
+ domain.size = math::max(int2(float2(domain.size) * scale), int2(1));
+
+ /* Reset the scale of the transformation by transforming it with the inverse of the scale. */
+ domain.transformation *= float3x3::from_scale(math::safe_divide(float2(1.0f), scale));
+
+ return domain;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new PixelateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_pixelate_cc
void register_node_type_cmp_pixelate()
@@ -27,6 +75,7 @@ void register_node_type_cmp_pixelate()
cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER);
ntype.declare = file_ns::cmp_node_pixelate_declare;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
index 6557478fc4b..68dc020a02e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc
@@ -18,6 +18,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_planetrackdeform_cc {
@@ -107,6 +109,24 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P
}
}
+using namespace blender::realtime_compositor;
+
+class PlaneTrackDeformOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Plane").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new PlaneTrackDeformOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_planetrackdeform_cc
void register_node_type_cmp_planetrackdeform()
@@ -121,6 +141,7 @@ void register_node_type_cmp_planetrackdeform()
ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodePlaneTrackDeformData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.cc b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
index c97035d55ea..1268219e7e2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_posterize.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc
@@ -5,6 +5,10 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Posterize ******************** */
@@ -13,11 +17,37 @@ namespace blender::nodes::node_composite_posterize_cc {
static void cmp_node_posterize_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Steps")).default_value(8.0f).min(2.0f).max(1024.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Steps"))
+ .default_value(8.0f)
+ .min(2.0f)
+ .max(1024.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
+using namespace blender::realtime_compositor;
+
+class PosterizeShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_posterize", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new PosterizeShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_posterize_cc
void register_node_type_cmp_posterize()
@@ -28,6 +58,7 @@ void register_node_type_cmp_posterize()
cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_posterize_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
index 000cc9df90a..c814ea5f738 100644
--- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc
@@ -8,6 +8,10 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** Premul and Key Alpha Convert ******************** */
@@ -16,7 +20,9 @@ namespace blender::nodes::node_composite_premulkey_cc {
static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Color>(N_("Image"));
}
@@ -25,6 +31,36 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "mapping", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class AlphaConvertShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (get_mode() == 0) {
+ GPU_stack_link(material, &bnode(), "color_alpha_premultiply", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "color_alpha_unpremultiply", inputs, outputs);
+ }
+
+ CMPNodeAlphaConvertMode get_mode()
+ {
+ return (CMPNodeAlphaConvertMode)bnode().custom1;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new AlphaConvertShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_premulkey_cc
void register_node_type_cmp_premulkey()
@@ -36,6 +72,7 @@ void register_node_type_cmp_premulkey()
cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_premulkey_declare;
ntype.draw_buttons = file_ns::node_composit_buts_premulkey;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
index 5bc4c67dd8e..f107961f301 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc
@@ -5,6 +5,12 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
+#include "DNA_node_types.h"
+
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** RGB ******************** */
@@ -16,6 +22,29 @@ static void cmp_node_rgb_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f});
}
+using namespace blender::realtime_compositor;
+
+class RGBOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("RGBA");
+ result.allocate_single_value();
+
+ const bNodeSocket *socket = static_cast<const bNodeSocket *>(bnode().outputs.first);
+ float4 color = float4(static_cast<const bNodeSocketValueRGBA *>(socket->default_value)->value);
+
+ result.set_color_value(color);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RGBOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_rgb_cc
void register_node_type_cmp_rgb()
@@ -27,6 +56,7 @@ void register_node_type_cmp_rgb()
cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_rgb_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.cc b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
index a083bc1837b..35caa3cd242 100644
--- a/source/blender/nodes/composite/nodes/node_composite_rotate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc
@@ -5,9 +5,14 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Rotate ******************** */
@@ -16,12 +21,15 @@ namespace blender::nodes::node_composite_rotate_cc {
static void cmp_node_rotate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Degr"))
.default_value(0.0f)
.min(-10000.0f)
.max(10000.0f)
- .subtype(PROP_ANGLE);
+ .subtype(PROP_ANGLE)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -35,6 +43,47 @@ static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class RotateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float rotation = get_input("Degr").get_float_value_default(0.0f);
+
+ const float3x3 transformation = float3x3::from_rotation(rotation);
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = get_interpolation();
+ }
+
+ Interpolation get_interpolation()
+ {
+ switch (bnode().custom1) {
+ case 0:
+ return Interpolation::Nearest;
+ case 1:
+ return Interpolation::Bilinear;
+ case 2:
+ return Interpolation::Bicubic;
+ }
+
+ BLI_assert_unreachable();
+ return Interpolation::Nearest;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new RotateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_rotate_cc
void register_node_type_cmp_rotate()
@@ -47,6 +96,7 @@ void register_node_type_cmp_rotate()
ntype.declare = file_ns::cmp_node_rotate_declare;
ntype.draw_buttons = file_ns::node_composit_buts_rotate;
node_type_init(&ntype, file_ns::node_composit_init_rotate);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc
index b2b42a3613c..eb2d7162c69 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scale.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc
@@ -5,11 +5,18 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+#include "BLI_math_base.hh"
+#include "BLI_math_vec_types.hh"
+
#include "RNA_access.h"
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Scale ******************** */
@@ -18,16 +25,26 @@ namespace blender::nodes::node_composite_scale_cc {
static void cmp_node_scale_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
- b.add_input<decl::Float>(N_("Y")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
static void node_composite_update_scale(bNodeTree *ntree, bNode *node)
{
bNodeSocket *sock;
- bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE);
+ bool use_xy_scale = ELEM(node->custom1, CMP_NODE_SCALE_RELATIVE, CMP_NODE_SCALE_ABSOLUTE);
/* Only show X/Y scale factor inputs for modes using them! */
for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) {
@@ -41,7 +58,7 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
{
uiItemR(layout, ptr, "space", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
- if (RNA_enum_get(ptr, "space") == CMP_SCALE_RENDERPERCENT) {
+ if (RNA_enum_get(ptr, "space") == CMP_NODE_SCALE_RENDER_SIZE) {
uiLayout *row;
uiItemR(layout,
ptr,
@@ -55,6 +72,145 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin
}
}
+using namespace blender::realtime_compositor;
+
+class ScaleOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ get_translation(), 0.0f, get_scale());
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = Interpolation::Bilinear;
+ }
+
+ float2 get_scale()
+ {
+ switch (get_scale_method()) {
+ case CMP_NODE_SCALE_RELATIVE:
+ return get_scale_relative();
+ case CMP_NODE_SCALE_ABSOLUTE:
+ return get_scale_absolute();
+ case CMP_NODE_SCALE_RENDER_PERCENT:
+ return get_scale_render_percent();
+ case CMP_NODE_SCALE_RENDER_SIZE:
+ return get_scale_render_size();
+ default:
+ BLI_assert_unreachable();
+ return float2(1.0f);
+ }
+ }
+
+ /* Scale by the input factors. */
+ float2 get_scale_relative()
+ {
+ return float2(get_input("X").get_float_value_default(1.0f),
+ get_input("Y").get_float_value_default(1.0f));
+ }
+
+ /* Scale such that the new size matches the input absolute size. */
+ float2 get_scale_absolute()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 absolute_size = float2(get_input("X").get_float_value_default(1.0f),
+ get_input("Y").get_float_value_default(1.0f));
+ return absolute_size / input_size;
+ }
+
+ /* Scale by the render resolution percentage. */
+ float2 get_scale_render_percent()
+ {
+ return float2(context().get_scene()->r.size / 100.0f);
+ }
+
+ float2 get_scale_render_size()
+ {
+ switch (get_scale_render_size_method()) {
+ case CMP_NODE_SCALE_RENDER_SIZE_STRETCH:
+ return get_scale_render_size_stretch();
+ case CMP_NODE_SCALE_RENDER_SIZE_FIT:
+ return get_scale_render_size_fit();
+ case CMP_NODE_SCALE_RENDER_SIZE_CROP:
+ return get_scale_render_size_crop();
+ default:
+ BLI_assert_unreachable();
+ return float2(1.0f);
+ }
+ }
+
+ /* Scale such that the new size matches the render size. Since the input is freely scaled, it is
+ * potentially stretched, hence the name. */
+ float2 get_scale_render_size_stretch()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ return render_size / input_size;
+ }
+
+ /* Scale such that the dimension with the smaller scaling factor matches that of the render size
+ * while maintaining the input's aspect ratio. Since the other dimension is guaranteed not to
+ * exceed the render size region due to its larger scaling factor, the image is said to be fit
+ * inside that region, hence the name. */
+ float2 get_scale_render_size_fit()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ const float2 scale = render_size / input_size;
+ return float2(math::min(scale.x, scale.y));
+ }
+
+ /* Scale such that the dimension with the larger scaling factor matches that of the render size
+ * while maintaining the input's aspect ratio. Since the other dimension is guaranteed to exceed
+ * the render size region due to its lower scaling factor, the image will be cropped inside that
+ * region, hence the name. */
+ float2 get_scale_render_size_crop()
+ {
+ const float2 input_size = float2(get_input("Image").domain().size);
+ const float2 render_size = float2(context().get_output_size());
+ const float2 scale = render_size / input_size;
+ return float2(math::max(scale.x, scale.y));
+ }
+
+ float2 get_translation()
+ {
+ /* Only the render size option supports offset translation. */
+ if (get_scale_method() != CMP_NODE_SCALE_RENDER_SIZE) {
+ return float2(0.0f);
+ }
+
+ /* Translate by the offset factor relative to the new size. */
+ const float2 input_size = float2(get_input("Image").domain().size);
+ return get_offset() * input_size * get_scale();
+ }
+
+ CMPNodeScaleMethod get_scale_method()
+ {
+ return (CMPNodeScaleMethod)bnode().custom1;
+ }
+
+ CMPNodeScaleRenderSizeMethod get_scale_render_size_method()
+ {
+ return (CMPNodeScaleRenderSizeMethod)bnode().custom2;
+ }
+
+ float2 get_offset()
+ {
+ return float2(bnode().custom3, bnode().custom4);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ScaleOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_scale_cc
void register_node_type_cmp_scale()
@@ -67,6 +223,7 @@ void register_node_type_cmp_scale()
ntype.declare = file_ns::cmp_node_scale_declare;
ntype.draw_buttons = file_ns::node_composit_buts_scale;
node_type_update(&ntype, file_ns::node_composite_update_scale);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
index 20bafb0d3d4..1f5317378bb 100644
--- a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc
@@ -3,6 +3,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes {
@@ -13,6 +15,38 @@ static void cmp_node_scene_time_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Frame"));
}
+using namespace blender::realtime_compositor;
+
+class SceneTimeOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ execute_seconds();
+ execute_frame();
+ }
+
+ void execute_seconds()
+ {
+ Result &result = get_result("Seconds");
+ result.allocate_single_value();
+ result.set_float_value(context().get_time());
+ }
+
+ void execute_frame()
+ {
+ Result &result = get_result("Frame");
+ result.allocate_single_value();
+ result.set_float_value(static_cast<float>(context().get_frame_number()));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SceneTimeOperation(context, node);
+}
+
} // namespace blender::nodes
void register_node_type_cmp_scene_time()
@@ -21,5 +55,7 @@ void register_node_type_cmp_scene_time()
cmp_node_type_base(&ntype, CMP_NODE_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT);
ntype.declare = blender::nodes::cmp_node_scene_time_declare;
+ ntype.get_compositor_operation = blender::nodes::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
index b253656a628..f6792d7ce61 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc
@@ -1,5 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_assert.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node)
@@ -56,9 +62,13 @@ static void node_cmp_combsep_color_label(const ListBase *sockets, CMPNodeCombSep
namespace blender::nodes::node_composite_separate_color_cc {
+NODE_STORAGE_FUNCS(NodeCMPCombSepColor)
+
static void cmp_node_separate_color_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Red"));
b.add_output<decl::Float>(N_("Green"));
b.add_output<decl::Float>(N_("Blue"));
@@ -71,6 +81,52 @@ static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node
node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode);
}
+using namespace blender::realtime_compositor;
+
+class SeparateColorShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (node_storage(bnode()).mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ return "node_composite_separate_rgba";
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ return "node_composite_separate_hsva";
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ return "node_composite_separate_hsla";
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ return "node_composite_separate_yuva_itu_709";
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ switch (node_storage(bnode()).ycc_mode) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_separate_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_separate_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_separate_ycca_jpeg";
+ }
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateColorShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_separate_color_cc
void register_node_type_cmp_separate_color()
@@ -85,6 +141,7 @@ void register_node_type_cmp_separate_color()
node_type_storage(
&ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, file_ns::cmp_node_separate_color_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
@@ -93,24 +150,34 @@ void register_node_type_cmp_separate_color()
namespace blender::nodes::node_composite_combine_color_cc {
+NODE_STORAGE_FUNCS(NodeCMPCombSepColor)
+
static void cmp_node_combine_color_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Red")).default_value(0.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
+ b.add_input<decl::Float>(N_("Red"))
+ .default_value(0.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Green"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
b.add_input<decl::Float>(N_("Blue"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(2);
b.add_input<decl::Float>(N_("Alpha"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
- .subtype(PROP_FACTOR);
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
@@ -120,6 +187,52 @@ static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node)
node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode);
}
+using namespace blender::realtime_compositor;
+
+class CombineColorShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (node_storage(bnode()).mode) {
+ case CMP_NODE_COMBSEP_COLOR_RGB:
+ return "node_composite_combine_rgba";
+ case CMP_NODE_COMBSEP_COLOR_HSV:
+ return "node_composite_combine_hsva";
+ case CMP_NODE_COMBSEP_COLOR_HSL:
+ return "node_composite_combine_hsla";
+ case CMP_NODE_COMBSEP_COLOR_YUV:
+ return "node_composite_combine_yuva_itu_709";
+ case CMP_NODE_COMBSEP_COLOR_YCC:
+ switch (node_storage(bnode()).ycc_mode) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_combine_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_combine_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_combine_ycca_jpeg";
+ }
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineColorShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_combine_color_cc
void register_node_type_cmp_combine_color()
@@ -134,6 +247,7 @@ void register_node_type_cmp_combine_color()
node_type_storage(
&ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage);
node_type_update(&ntype, file_ns::cmp_node_combine_color_update);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
index a0d2485ea5a..b655c0db106 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc
@@ -5,57 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE HSVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_hsva_cc {
+namespace blender::nodes::node_composite_separate_hsva_cc {
static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("H"));
b.add_output<decl::Float>(N_("S"));
b.add_output<decl::Float>(N_("V"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_hsva_cc
+using namespace blender::realtime_compositor;
+
+class SeparateHSVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_hsva", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateHSVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_hsva_cc
void register_node_type_cmp_sephsva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_hsva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sephsva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
+
nodeRegisterType(&ntype);
}
/* **************** COMBINE HSVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_hsva_cc {
+namespace blender::nodes::node_composite_combine_hsva_cc {
static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_hsva_cc
+using namespace blender::realtime_compositor;
+
+class CombineHSVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_hsva", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineHSVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_hsva_cc
void register_node_type_cmp_combhsva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_hsva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combhsva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
index ae46681b0f4..1f4c9fd153f 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc
@@ -5,57 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE RGBA ******************** */
-namespace blender::nodes::node_composite_sepcomb_rgba_cc {
+
+namespace blender::nodes::node_composite_separate_rgba_cc {
static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("R"));
b.add_output<decl::Float>(N_("G"));
b.add_output<decl::Float>(N_("B"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_rgba_cc
+using namespace blender::realtime_compositor;
+
+class SeparateRGBAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_rgba", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateRGBAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_rgba_cc
void register_node_type_cmp_seprgba()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_rgba_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_seprgba_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE RGBA ******************** */
-namespace blender::nodes::node_composite_sepcomb_rgba_cc {
+namespace blender::nodes::node_composite_combine_rgba_cc {
static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_rgba_cc
+using namespace blender::realtime_compositor;
+
+class CombineRGBAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_rgba", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineRGBAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_rgba_cc
void register_node_type_cmp_combrgba()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_rgba_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combrgba_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
index 4979c376cab..e288e698808 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc
@@ -5,10 +5,15 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE XYZ ******************** */
-namespace blender::nodes {
+
+namespace blender::nodes::node_composite_separate_xyz_cc {
static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
{
@@ -18,21 +23,44 @@ static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>("Z");
}
-} // namespace blender::nodes
+using namespace blender::realtime_compositor;
+
+class SeparateXYZShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_xyz", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateXYZShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_xyz_cc
void register_node_type_cmp_separate_xyz()
{
+ namespace file_ns = blender::nodes::node_composite_separate_xyz_cc;
+
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_XYZ, "Separate XYZ", NODE_CLASS_CONVERTER);
- ntype.declare = blender::nodes::cmp_node_separate_xyz_declare;
+ ntype.declare = file_ns::cmp_node_separate_xyz_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE XYZ ******************** */
-namespace blender::nodes {
+namespace blender::nodes::node_composite_combine_xyz_cc {
static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
{
@@ -42,14 +70,37 @@ static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>("Vector");
}
-} // namespace blender::nodes
+using namespace blender::realtime_compositor;
+
+class CombineXYZShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_xyz", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineXYZShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_xyz_cc
void register_node_type_cmp_combine_xyz()
{
+ namespace file_ns = blender::nodes::node_composite_combine_xyz_cc;
+
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBINE_XYZ, "Combine XYZ", NODE_CLASS_CONVERTER);
- ntype.declare = blender::nodes::cmp_node_combine_xyz_declare;
+ ntype.declare = file_ns::cmp_node_combine_xyz_declare;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
index a3c40b61e64..bebe6abe115 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc
@@ -5,15 +5,23 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE YCCA ******************** */
-namespace blender::nodes::node_composite_sepcomb_ycca_cc {
+namespace blender::nodes::node_composite_separate_ycca_cc {
static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Y"));
b.add_output<decl::Float>(N_("Cb"));
b.add_output<decl::Float>(N_("Cr"));
@@ -25,31 +33,85 @@ static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *nod
node->custom1 = 1; /* BLI_YCC_ITU_BT709 */
}
-} // namespace blender::nodes::node_composite_sepcomb_ycca_cc
+using namespace blender::realtime_compositor;
+
+class SeparateYCCAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_separate_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_separate_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_separate_ycca_jpeg";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateYCCAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_ycca_cc
void register_node_type_cmp_sepycca()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_ycca_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA_LEGACY, "Separate YCbCrA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YCCA ******************** */
-namespace blender::nodes::node_composite_sepcomb_ycca_cc {
+namespace blender::nodes::node_composite_combine_ycca_cc {
static void cmp_node_combycca_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Cb"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("Cr"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
@@ -58,17 +120,59 @@ static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *no
node->custom1 = 1; /* BLI_YCC_ITU_BT709 */
}
-} // namespace blender::nodes::node_composite_sepcomb_ycca_cc
+using namespace blender::realtime_compositor;
+
+class CombineYCCAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs);
+ }
+
+ int get_mode()
+ {
+ return bnode().custom1;
+ }
+
+ const char *get_shader_function_name()
+ {
+ switch (get_mode()) {
+ case BLI_YCC_ITU_BT601:
+ return "node_composite_combine_ycca_itu_601";
+ case BLI_YCC_ITU_BT709:
+ return "node_composite_combine_ycca_itu_709";
+ case BLI_YCC_JFIF_0_255:
+ return "node_composite_combine_ycca_jpeg";
+ }
+
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineYCCAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_ycca_cc
void register_node_type_cmp_combycca()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_ycca_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA_LEGACY, "Combine YCbCrA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combycca_declare;
node_type_init(&ntype, file_ns::node_composit_init_mode_combycca);
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
index 7fdece5904d..1f0eb04cfc3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc
@@ -5,58 +5,112 @@
* \ingroup cmpnodes
*/
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SEPARATE YUVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_yuva_cc {
+namespace blender::nodes::node_composite_separate_yuva_cc {
static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Y"));
b.add_output<decl::Float>(N_("U"));
b.add_output<decl::Float>(N_("V"));
b.add_output<decl::Float>(N_("A"));
}
-} // namespace blender::nodes::node_composite_sepcomb_yuva_cc
+using namespace blender::realtime_compositor;
+
+class SeparateYUVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_separate_yuva_itu_709", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SeparateYUVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_separate_yuva_cc
void register_node_type_cmp_sepyuva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc;
+ namespace file_ns = blender::nodes::node_composite_separate_yuva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_sepyuva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** COMBINE YUVA ******************** */
-namespace blender::nodes::node_composite_sepcomb_yuva_cc {
+namespace blender::nodes::node_composite_combine_yuva_cc {
static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f);
- b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f).compositor_domain_priority(1);
+ b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2);
+ b.add_input<decl::Float>(N_("A"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(3);
b.add_output<decl::Color>(N_("Image"));
}
-} // namespace blender::nodes::node_composite_sepcomb_yuva_cc
+using namespace blender::realtime_compositor;
+
+class CombineYUVAShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ GPU_stack_link(material, &bnode(), "node_composite_combine_yuva_itu_709", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new CombineYUVAShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_combine_yuva_cc
void register_node_type_cmp_combyuva()
{
- namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc;
+ namespace file_ns = blender::nodes::node_composite_combine_yuva_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_combyuva_declare;
+ ntype.gather_link_search_ops = nullptr;
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
index 8aeaafbbf67..df3aca2c665 100644
--- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc
@@ -8,16 +8,28 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
/* **************** SET ALPHA ******************** */
namespace blender::nodes::node_composite_setalpha_cc {
+NODE_STORAGE_FUNCS(NodeSetAlpha)
+
static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("Alpha"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .compositor_domain_priority(1);
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +45,31 @@ static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SetAlphaShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ if (node_storage(bnode()).mode == CMP_NODE_SETALPHA_MODE_APPLY) {
+ GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs);
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new SetAlphaShaderNode(node);
+}
+
} // namespace blender::nodes::node_composite_setalpha_cc
void register_node_type_cmp_setalpha()
@@ -47,6 +84,7 @@ void register_node_type_cmp_setalpha()
node_type_init(&ntype, file_ns::node_composit_init_setalpha);
node_type_storage(
&ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
index ab325c4559f..085de69e63e 100644
--- a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc
@@ -11,6 +11,12 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** SPLIT VIEWER ******************** */
@@ -43,6 +49,70 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C)
uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ViewerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ GPUShader *shader = get_split_viewer_shader();
+ GPU_shader_bind(shader);
+
+ const int2 size = compute_domain().size;
+
+ GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio());
+ GPU_shader_uniform_2iv(shader, "view_size", size);
+
+ const Result &first_image = get_input("Image");
+ first_image.bind_as_texture(shader, "first_image_tx");
+ const Result &second_image = get_input("Image_001");
+ second_image.bind_as_texture(shader, "second_image_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, size);
+
+ first_image.unbind_as_texture();
+ second_image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+
+ GPUShader *get_split_viewer_shader()
+ {
+ if (get_split_axis() == CMP_NODE_SPLIT_VIEWER_HORIZONTAL) {
+ return shader_manager().get("compositor_split_viewer_horizontal");
+ }
+
+ return shader_manager().get("compositor_split_viewer_vertical");
+ }
+
+ CMPNodeSplitViewerAxis get_split_axis()
+ {
+ return (CMPNodeSplitViewerAxis)bnode().custom2;
+ }
+
+ float get_split_ratio()
+ {
+ return bnode().custom1 / 100.0f;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ViewerOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_split_viewer_cc
void register_node_type_cmp_splitviewer()
@@ -57,6 +127,7 @@ void register_node_type_cmp_splitviewer()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_splitviewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
index 63d00a0864b..75a96a05863 100644
--- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc
@@ -11,6 +11,8 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Stabilize 2D ******************** */
@@ -58,6 +60,23 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe
uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class Stabilize2DOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new Stabilize2DOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_stabilize2d_cc
void register_node_type_cmp_stabilize2d()
@@ -70,6 +89,7 @@ void register_node_type_cmp_stabilize2d()
ntype.declare = file_ns::cmp_node_stabilize2d_declare;
ntype.draw_buttons = file_ns::node_composit_buts_stabilize2d;
ntype.initfunc_api = file_ns::init;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
index 766f26745ef..4b9264d7e35 100644
--- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_sunbeams_cc {
@@ -38,6 +40,23 @@ static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), P
ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SunBeamsOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SunBeamsOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_sunbeams_cc
void register_node_type_cmp_sunbeams()
@@ -52,6 +71,7 @@ void register_node_type_cmp_sunbeams()
node_type_init(&ntype, file_ns::init);
node_type_storage(
&ntype, "NodeSunBeams", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.cc b/source/blender/nodes/composite/nodes/node_composite_switch.cc
index bda490572e9..767802cc442 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switch.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Switch ******************** */
@@ -26,6 +28,30 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "check", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class SwitchOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input(get_condition() ? "On" : "Off");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+ }
+
+ bool get_condition()
+ {
+ return bnode().custom1;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SwitchOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_switch_cc
void register_node_type_cmp_switch()
@@ -38,5 +64,7 @@ void register_node_type_cmp_switch()
ntype.declare = file_ns::cmp_node_switch_declare;
ntype.draw_buttons = file_ns::node_composit_buts_switch;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
+
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.cc b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
index 2cf3da03a05..e74c3b6007a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_switchview.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc
@@ -11,6 +11,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** SWITCH VIEW ******************** */
@@ -140,6 +142,25 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout,
nullptr);
}
+using namespace blender::realtime_compositor;
+
+class SwitchViewOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input(context().get_view_name());
+ Result &result = get_result("Image");
+ input.pass_through(result);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new SwitchViewOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_switchview_cc
void register_node_type_cmp_switch_view()
@@ -153,6 +174,7 @@ void register_node_type_cmp_switch_view()
ntype.draw_buttons_ex = file_ns::node_composit_buts_switch_view_ex;
ntype.initfunc_api = file_ns::init_switch_view;
node_type_update(&ntype, file_ns::cmp_node_switch_view_update);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc
index 7571e97a2cd..5a628aae7a7 100644
--- a/source/blender/nodes/composite/nodes/node_composite_texture.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** TEXTURE ******************** */
@@ -23,6 +25,24 @@ static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Color>(N_("Color"));
}
+using namespace blender::realtime_compositor;
+
+class TextureOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("Value").allocate_invalid();
+ get_result("Color").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TextureOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_texture_cc
void register_node_type_cmp_texture()
@@ -34,6 +54,7 @@ void register_node_type_cmp_texture()
cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_texture_declare;
ntype.flag |= NODE_PREVIEW;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
index cdfe97b038d..4cc3d4f32a3 100644
--- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc
@@ -10,6 +10,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_tonemap_cc {
@@ -58,6 +60,23 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po
}
}
+using namespace blender::realtime_compositor;
+
+class ToneMapOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ToneMapOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_tonemap_cc
void register_node_type_cmp_tonemap()
@@ -71,6 +90,7 @@ void register_node_type_cmp_tonemap()
ntype.draw_buttons = file_ns::node_composit_buts_tonemap;
node_type_init(&ntype, file_ns::node_composit_init_tonemap);
node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
index 0e99ff59327..0e9bd800f44 100644
--- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc
@@ -18,6 +18,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
namespace blender::nodes::node_composite_trackpos_cc {
@@ -102,6 +104,25 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN
}
}
+using namespace blender::realtime_compositor;
+
+class TrackPositionOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_result("X").allocate_invalid();
+ get_result("Y").allocate_invalid();
+ get_result("Speed").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TrackPositionOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_trackpos_cc
void register_node_type_cmp_trackpos()
@@ -116,6 +137,7 @@ void register_node_type_cmp_trackpos()
ntype.initfunc_api = file_ns::init;
node_type_storage(
&ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.cc b/source/blender/nodes/composite/nodes/node_composite_transform.cc
index fe72f5e33ca..7c5866d2d06 100644
--- a/source/blender/nodes/composite/nodes/node_composite_transform.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc
@@ -5,9 +5,15 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+#include "BLI_float3x3.hh"
+#include "BLI_math_vector.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Transform ******************** */
@@ -16,15 +22,30 @@ namespace blender::nodes::node_composite_transform_cc {
static void cmp_node_transform_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
b.add_input<decl::Float>(N_("Angle"))
.default_value(0.0f)
.min(-10000.0f)
.max(10000.0f)
- .subtype(PROP_ANGLE);
- b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX);
+ .subtype(PROP_ANGLE)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Scale"))
+ .default_value(1.0f)
+ .min(0.0001f)
+ .max(CMP_SCALE_MAX)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -33,6 +54,51 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class TransformOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ const float2 translation = float2(get_input("X").get_float_value_default(0.0f),
+ get_input("Y").get_float_value_default(0.0f));
+ const float rotation = get_input("Angle").get_float_value_default(0.0f);
+ const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f));
+
+ const float3x3 transformation = float3x3::from_translation_rotation_scale(
+ translation, rotation, scale);
+
+ result.transform(transformation);
+ result.get_realization_options().interpolation = get_interpolation();
+ }
+
+ Interpolation get_interpolation()
+ {
+ switch (bnode().custom1) {
+ case 0:
+ return Interpolation::Nearest;
+ case 1:
+ return Interpolation::Bilinear;
+ case 2:
+ return Interpolation::Bicubic;
+ }
+
+ BLI_assert_unreachable();
+ return Interpolation::Nearest;
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TransformOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_transform_cc
void register_node_type_cmp_transform()
@@ -44,6 +110,7 @@ void register_node_type_cmp_transform()
cmp_node_type_base(&ntype, CMP_NODE_TRANSFORM, "Transform", NODE_CLASS_DISTORT);
ntype.declare = file_ns::cmp_node_transform_declare;
ntype.draw_buttons = file_ns::node_composit_buts_transform;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc
index bbdc8ca4d31..e0f87ff604a 100644
--- a/source/blender/nodes/composite/nodes/node_composite_translate.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc
@@ -5,20 +5,37 @@
* \ingroup cmpnodes
*/
+#include "BLI_float3x3.hh"
+#include "BLI_math_vec_types.hh"
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Translate ******************** */
namespace blender::nodes::node_composite_translate_cc {
+NODE_STORAGE_FUNCS(NodeTranslateData)
+
static void cmp_node_translate_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f});
- b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f);
- b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f);
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({1.0f, 1.0f, 1.0f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_input<decl::Float>(N_("X"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
+ b.add_input<decl::Float>(N_("Y"))
+ .default_value(0.0f)
+ .min(-10000.0f)
+ .max(10000.0f)
+ .compositor_expects_single_value();
b.add_output<decl::Color>(N_("Image"));
}
@@ -34,6 +51,54 @@ static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C),
uiItemR(layout, ptr, "wrap_axis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class TranslateOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &input = get_input("Image");
+ Result &result = get_result("Image");
+ input.pass_through(result);
+
+ float x = get_input("X").get_float_value_default(0.0f);
+ float y = get_input("Y").get_float_value_default(0.0f);
+ if (get_use_relative()) {
+ x *= input.domain().size.x;
+ y *= input.domain().size.y;
+ }
+
+ const float2 translation = float2(x, y);
+ const float3x3 transformation = float3x3::from_translation(translation);
+
+ result.transform(transformation);
+ result.get_realization_options().repeat_x = get_repeat_x();
+ result.get_realization_options().repeat_y = get_repeat_y();
+ }
+
+ bool get_use_relative()
+ {
+ return node_storage(bnode()).relative;
+ }
+
+ bool get_repeat_x()
+ {
+ return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY);
+ }
+
+ bool get_repeat_y()
+ {
+ return ELEM(node_storage(bnode()).wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new TranslateOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_translate_cc
void register_node_type_cmp_translate()
@@ -48,6 +113,7 @@ void register_node_type_cmp_translate()
node_type_init(&ntype, file_ns::node_composit_init_translate);
node_type_storage(
&ntype, "NodeTranslateData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
index 0dfdeda24e6..03a7bc61924 100644
--- a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc
@@ -5,18 +5,33 @@
* \ingroup cmpnodes
*/
+#include "BLI_assert.h"
+
+#include "IMB_colormanagement.h"
+
+#include "BKE_colorband.h"
+
+#include "GPU_material.h"
+
+#include "COM_shader_node.hh"
+
#include "node_composite_util.hh"
#include "BKE_colorband.h"
/* **************** VALTORGB ******************** */
-namespace blender::nodes::node_composite_val_to_rgb_cc {
+namespace blender::nodes::node_composite_color_ramp_cc {
static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR);
- b.add_output<decl::Color>(N_("Image"));
+ b.add_input<decl::Float>(N_("Fac"))
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR)
+ .compositor_domain_priority(1);
+ b.add_output<decl::Color>(N_("Image")).compositor_domain_priority(0);
b.add_output<decl::Float>(N_("Alpha"));
}
@@ -25,11 +40,94 @@ static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node)
node->storage = BKE_colorband_add(true);
}
-} // namespace blender::nodes::node_composite_val_to_rgb_cc
+using namespace blender::realtime_compositor;
+
+class ColorRampShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ struct ColorBand *color_band = get_color_band();
+
+ /* Common / easy case optimization. */
+ if ((color_band->tot <= 2) && (color_band->color_mode == COLBAND_BLEND_RGB)) {
+ float mul_bias[2];
+ switch (color_band->ipotype) {
+ case COLBAND_INTERP_LINEAR:
+ mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos);
+ mul_bias[1] = -mul_bias[0] * color_band->data[0].pos;
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_linear",
+ inputs,
+ outputs,
+ GPU_uniform(mul_bias),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ case COLBAND_INTERP_CONSTANT:
+ mul_bias[1] = max_ff(color_band->data[0].pos, color_band->data[1].pos);
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_constant",
+ inputs,
+ outputs,
+ GPU_uniform(&mul_bias[1]),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ case COLBAND_INTERP_EASE:
+ mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos);
+ mul_bias[1] = -mul_bias[0] * color_band->data[0].pos;
+ GPU_stack_link(material,
+ &bnode(),
+ "valtorgb_opti_ease",
+ inputs,
+ outputs,
+ GPU_uniform(mul_bias),
+ GPU_uniform(&color_band->data[0].r),
+ GPU_uniform(&color_band->data[1].r));
+ return;
+ default:
+ BLI_assert_unreachable();
+ return;
+ }
+ }
+
+ float *array, layer;
+ int size;
+ BKE_colorband_evaluate_table_rgba(color_band, &array, &size);
+ GPUNodeLink *tex = GPU_color_band(material, size, array, &layer);
+
+ if (color_band->ipotype == COLBAND_INTERP_CONSTANT) {
+ GPU_stack_link(
+ material, &bnode(), "valtorgb_nearest", inputs, outputs, tex, GPU_constant(&layer));
+ return;
+ }
+
+ GPU_stack_link(material, &bnode(), "valtorgb", inputs, outputs, tex, GPU_constant(&layer));
+ }
+
+ struct ColorBand *get_color_band()
+ {
+ return static_cast<struct ColorBand *>(bnode().storage);
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new ColorRampShaderNode(node);
+}
+
+} // namespace blender::nodes::node_composite_color_ramp_cc
void register_node_type_cmp_valtorgb()
{
- namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc;
+ namespace file_ns = blender::nodes::node_composite_color_ramp_cc;
static bNodeType ntype;
@@ -38,31 +136,63 @@ void register_node_type_cmp_valtorgb()
node_type_size(&ntype, 240, 200, 320);
node_type_init(&ntype, file_ns::node_composit_init_valtorgb);
node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
/* **************** RGBTOBW ******************** */
-namespace blender::nodes::node_composite_val_to_rgb_cc {
+namespace blender::nodes::node_composite_rgb_to_bw_cc {
static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f});
- b.add_output<decl::Color>(N_("Val"));
+ b.add_input<decl::Color>(N_("Image"))
+ .default_value({0.8f, 0.8f, 0.8f, 1.0f})
+ .compositor_domain_priority(0);
+ b.add_output<decl::Float>(N_("Val"));
+}
+
+using namespace blender::realtime_compositor;
+
+class RGBToBWShaderNode : public ShaderNode {
+ public:
+ using ShaderNode::ShaderNode;
+
+ void compile(GPUMaterial *material) override
+ {
+ GPUNodeStack *inputs = get_inputs_array();
+ GPUNodeStack *outputs = get_outputs_array();
+
+ float luminance_coefficients[3];
+ IMB_colormanagement_get_luminance_coefficients(luminance_coefficients);
+
+ GPU_stack_link(material,
+ &bnode(),
+ "color_to_luminance",
+ inputs,
+ outputs,
+ GPU_constant(luminance_coefficients));
+ }
+};
+
+static ShaderNode *get_compositor_shader_node(DNode node)
+{
+ return new RGBToBWShaderNode(node);
}
-} // namespace blender::nodes::node_composite_val_to_rgb_cc
+} // namespace blender::nodes::node_composite_rgb_to_bw_cc
void register_node_type_cmp_rgbtobw()
{
- namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc;
+ namespace file_ns = blender::nodes::node_composite_rgb_to_bw_cc;
static bNodeType ntype;
cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::cmp_node_rgbtobw_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc
index a3269d3d1c2..a96e1db14ad 100644
--- a/source/blender/nodes/composite/nodes/node_composite_value.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_value.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** VALUE ******************** */
@@ -16,6 +18,29 @@ static void cmp_node_value_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Float>(N_("Value")).default_value(0.5f);
}
+using namespace blender::realtime_compositor;
+
+class ValueOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ Result &result = get_result("Value");
+ result.allocate_single_value();
+
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first);
+ float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value;
+
+ result.set_float_value(value);
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ValueOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_value_cc
void register_node_type_cmp_value()
@@ -27,6 +52,7 @@ void register_node_type_cmp_value()
cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT);
ntype.declare = file_ns::cmp_node_value_declare;
node_type_size_preset(&ntype, NODE_SIZE_SMALL);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
index 741f2e0e816..515478da75d 100644
--- a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** VECTOR BLUR ******************** */
@@ -51,6 +53,23 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po
uiItemR(layout, ptr, "use_curved", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class VectorBlurOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new VectorBlurOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_vec_blur_cc
void register_node_type_cmp_vecblur()
@@ -65,6 +84,7 @@ void register_node_type_cmp_vecblur()
node_type_init(&ntype, file_ns::node_composit_init_vecblur);
node_type_storage(
&ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
index 05f395183b5..4e82b31ca47 100644
--- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc
@@ -5,6 +5,8 @@
* \ingroup cmpnodes
*/
+#include "BLI_math_vec_types.hh"
+
#include "BKE_global.h"
#include "BKE_image.h"
@@ -13,6 +15,13 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+#include "GPU_texture.h"
+
+#include "COM_node_operation.hh"
+#include "COM_utilities.hh"
+
#include "node_composite_util.hh"
/* **************** VIEWER ******************** */
@@ -55,6 +64,125 @@ static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C),
}
}
+using namespace blender::realtime_compositor;
+
+class ViewerOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ if (image.is_single_value() && alpha.is_single_value()) {
+ execute_clear();
+ }
+ else if (ignore_alpha()) {
+ execute_ignore_alpha();
+ }
+ else if (!node().input_by_identifier("Alpha")->is_logically_linked()) {
+ execute_copy();
+ }
+ else {
+ execute_set_alpha();
+ }
+ }
+
+ /* Executes when all inputs are single values, in which case, the output texture can just be
+ * cleared to the appropriate color. */
+ void execute_clear()
+ {
+ const Result &image = get_input("Image");
+ const Result &alpha = get_input("Alpha");
+
+ float4 color = image.get_color_value();
+ if (ignore_alpha()) {
+ color.w = 1.0f;
+ }
+ else if (node().input_by_identifier("Alpha")->is_logically_linked()) {
+ color.w = alpha.get_float_value();
+ }
+
+ GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color);
+ }
+
+ /* Executes when the alpha channel of the image is ignored. */
+ void execute_ignore_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "input_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* Executes when the image texture is written with no adjustments and can thus be copied directly
+ * to the output texture. */
+ void execute_copy()
+ {
+ const Result &image = get_input("Image");
+
+ /* Make sure any prior writes to the texture are reflected before copying it. */
+ GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE);
+
+ GPU_texture_copy(context().get_output_texture(), image.texture());
+ }
+
+ /* Executes when the alpha channel of the image is set as the value of the input alpha. */
+ void execute_set_alpha()
+ {
+ GPUShader *shader = shader_manager().get("compositor_set_alpha");
+ GPU_shader_bind(shader);
+
+ const Result &image = get_input("Image");
+ image.bind_as_texture(shader, "image_tx");
+
+ const Result &alpha = get_input("Alpha");
+ alpha.bind_as_texture(shader, "alpha_tx");
+
+ GPUTexture *output_texture = context().get_output_texture();
+ const int image_unit = GPU_shader_get_texture_binding(shader, "output_img");
+ GPU_texture_image_bind(output_texture, image_unit);
+
+ compute_dispatch_threads_at_least(shader, compute_domain().size);
+
+ image.unbind_as_texture();
+ alpha.unbind_as_texture();
+ GPU_texture_image_unbind(output_texture);
+ GPU_shader_unbind();
+ }
+
+ /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the
+ * alpha channel of the image is retained, but only if the alpha input is not linked. If the
+ * alpha input is linked, it the value of that input will be used as the alpha of the image. */
+ bool ignore_alpha()
+ {
+ return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA;
+ }
+
+ /* The operation domain have the same dimensions of the output without any transformations. */
+ Domain compute_domain() override
+ {
+ return Domain(context().get_output_size());
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ViewerOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_viewer_cc
void register_node_type_cmp_viewer()
@@ -70,6 +198,7 @@ void register_node_type_cmp_viewer()
ntype.flag |= NODE_PREVIEW;
node_type_init(&ntype, file_ns::node_composit_init_viewer);
node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.no_muting = true;
diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
index be90aeb7acc..e5f460099e9 100644
--- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
+++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc
@@ -8,6 +8,8 @@
#include "UI_interface.h"
#include "UI_resources.h"
+#include "COM_node_operation.hh"
+
#include "node_composite_util.hh"
/* **************** Z COMBINE ******************** */
@@ -33,6 +35,24 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(col, ptr, "use_antialias_z", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE);
}
+using namespace blender::realtime_compositor;
+
+class ZCombineOperation : public NodeOperation {
+ public:
+ using NodeOperation::NodeOperation;
+
+ void execute() override
+ {
+ get_input("Image").pass_through(get_result("Image"));
+ get_result("Z").allocate_invalid();
+ }
+};
+
+static NodeOperation *get_compositor_operation(Context &context, DNode node)
+{
+ return new ZCombineOperation(context, node);
+}
+
} // namespace blender::nodes::node_composite_zcombine_cc
void register_node_type_cmp_zcombine()
@@ -44,6 +64,7 @@ void register_node_type_cmp_zcombine()
cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::cmp_node_zcombine_declare;
ntype.draw_buttons = file_ns::node_composit_buts_zcombine;
+ ntype.get_compositor_operation = file_ns::get_compositor_operation;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/node_function_util.hh b/source/blender/nodes/function/node_function_util.hh
index fd0b6c31b1d..059b2f9bc17 100644
--- a/source/blender/nodes/function/node_function_util.hh
+++ b/source/blender/nodes/function/node_function_util.hh
@@ -23,4 +23,6 @@
#include "FN_multi_function_builder.hh"
+#include "RNA_access.h"
+
void fn_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass);
diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
index 2cb455832e5..e5c89567d44 100644
--- a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc
@@ -150,7 +150,7 @@ class MF_AlignEulerToVector : public fn::MultiFunction {
static fn::MFSignature create_signature()
{
- fn::MFSignatureBuilder signature{"Align Euler To Vector"};
+ fn::MFSignatureBuilder signature{"Align Euler to Vector"};
signature.single_input<float3>("Rotation");
signature.single_input<float>("Factor");
signature.single_input<float3>("Vector");
@@ -190,7 +190,7 @@ class MF_AlignEulerToVector : public fn::MultiFunction {
static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
builder.construct_and_set_matching_fn<MF_AlignEulerToVector>(node.custom1, node.custom2);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
index b6d7e6c9a5f..5fc28509a49 100644
--- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
+++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc
@@ -68,7 +68,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle();
static fn::CustomMF_SI_SI_SO<bool, bool, bool> and_fn{
diff --git a/source/blender/nodes/function/nodes/node_fn_combine_color.cc b/source/blender/nodes/function/nodes/node_fn_combine_color.cc
index c5fd3ce38a1..450cd166e78 100644
--- a/source/blender/nodes/function/nodes/node_fn_combine_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_combine_color.cc
@@ -49,7 +49,7 @@ static void fn_node_combine_color_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
const NodeCombSepColor &storage = node_storage(bnode);
diff --git a/source/blender/nodes/function/nodes/node_fn_compare.cc b/source/blender/nodes/function/nodes/node_fn_compare.cc
index e3f13dc7d6b..122d1a3c93e 100644
--- a/source/blender/nodes/function/nodes/node_fn_compare.cc
+++ b/source/blender/nodes/function/nodes/node_fn_compare.cc
@@ -167,7 +167,7 @@ static float component_average(float3 a)
return (a.x + a.y + a.z) / 3.0f;
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
const NodeFunctionCompare *data = (NodeFunctionCompare *)node.storage;
diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
index 9c9d8620a7e..aad2f532d20 100644
--- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc
@@ -39,7 +39,7 @@ static void node_float_to_int_label(const bNodeTree *UNUSED(ntree),
BLI_strncpy(label, IFACE_(name), maxlen);
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static auto exec_preset = fn::CustomMF_presets::AllSpanOrSingle();
static fn::CustomMF_SI_SO<float, int> round_fn{
diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc
index 5ced719627f..717f4d1ac6b 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_bool.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc
@@ -22,7 +22,7 @@ static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), Poi
static void fn_node_input_bool_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputBool *node_storage = static_cast<NodeInputBool *>(bnode.storage);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<bool>>(node_storage->boolean);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc
index 46787f7575d..cdad1542c66 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_color.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc
@@ -23,7 +23,7 @@ static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), Po
static void fn_node_input_color_build_multi_function(
blender::nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputColor *node_storage = static_cast<NodeInputColor *>(bnode.storage);
blender::ColorGeometry4f color = (ColorGeometry4f)node_storage->color;
builder.construct_and_set_matching_fn<blender::fn::CustomMF_Constant<ColorGeometry4f>>(color);
diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc
index 1e5dcd5ae7a..16506b5f9b8 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_int.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc
@@ -22,7 +22,7 @@ static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), Poin
static void fn_node_input_int_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputInt *node_storage = static_cast<NodeInputInt *>(bnode.storage);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<int>>(node_storage->integer);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc
index 124a8572f78..129d19f4f04 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_string.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc
@@ -20,7 +20,7 @@ static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), P
static void fn_node_input_string_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputString *node_storage = static_cast<NodeInputString *>(bnode.storage);
std::string string = std::string((node_storage->string) ? node_storage->string : "");
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<std::string>>(std::move(string));
diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
index 898c19e92f0..de894a4038d 100644
--- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc
+++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc
@@ -22,7 +22,7 @@ static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), P
static void fn_node_input_vector_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
NodeInputVector *node_storage = static_cast<NodeInputVector *>(bnode.storage);
float3 vector(node_storage->vector);
builder.construct_and_set_matching_fn<fn::CustomMF_Constant<float3>>(vector);
diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
index a4fc1a6bfd1..299c0f7a932 100644
--- a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
+++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc
@@ -52,7 +52,7 @@ static void fn_node_rotate_euler_layout(uiLayout *layout, bContext *UNUSED(C), P
uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE);
}
-static const fn::MultiFunction *get_multi_function(bNode &bnode)
+static const fn::MultiFunction *get_multi_function(const bNode &bnode)
{
static fn::CustomMF_SI_SI_SO<float3, float3, float3> obj_euler_rot{
"Rotate Euler by Euler/Object", [](const float3 &input, const float3 &rotation) {
diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt
index d87f0312958..ce285488109 100644
--- a/source/blender/nodes/geometry/CMakeLists.txt
+++ b/source/blender/nodes/geometry/CMakeLists.txt
@@ -59,13 +59,15 @@ set(SRC
nodes/node_geo_curve_trim.cc
nodes/node_geo_deform_curves_on_surface.cc
nodes/node_geo_delete_geometry.cc
+ nodes/node_geo_distribute_points_in_volume.cc
nodes/node_geo_distribute_points_on_faces.cc
nodes/node_geo_dual_mesh.cc
nodes/node_geo_duplicate_elements.cc
+ nodes/node_geo_edge_paths_to_curves.cc
+ nodes/node_geo_edge_paths_to_selection.cc
nodes/node_geo_edge_split.cc
nodes/node_geo_extrude_mesh.cc
nodes/node_geo_field_at_index.cc
- nodes/node_geo_field_on_domain.cc
nodes/node_geo_flip_faces.cc
nodes/node_geo_geometry_to_instance.cc
nodes/node_geo_image_texture.cc
@@ -91,17 +93,20 @@ set(SRC
nodes/node_geo_input_radius.cc
nodes/node_geo_input_scene_time.cc
nodes/node_geo_input_shade_smooth.cc
+ nodes/node_geo_input_shortest_edge_paths.cc
nodes/node_geo_input_spline_cyclic.cc
nodes/node_geo_input_spline_length.cc
nodes/node_geo_input_spline_resolution.cc
nodes/node_geo_input_tangent.cc
nodes/node_geo_instance_on_points.cc
nodes/node_geo_instances_to_points.cc
+ nodes/node_geo_interpolate_domain.cc
nodes/node_geo_is_viewport.cc
nodes/node_geo_join_geometry.cc
nodes/node_geo_material_replace.cc
nodes/node_geo_material_selection.cc
nodes/node_geo_merge_by_distance.cc
+ nodes/node_geo_mesh_face_set_boundaries.cc
nodes/node_geo_mesh_primitive_circle.cc
nodes/node_geo_mesh_primitive_cone.cc
nodes/node_geo_mesh_primitive_cube.cc
@@ -184,20 +189,6 @@ if(WITH_BULLET)
add_definitions(-DWITH_BULLET)
endif()
-if(WITH_PYTHON)
- list(APPEND INC
- ../../python
- )
- list(APPEND INC_SYS
- ${PYTHON_INCLUDE_DIRS}
- )
- list(APPEND LIB
- ${PYTHON_LINKFLAGS}
- ${PYTHON_LIBRARIES}
- )
- add_definitions(-DWITH_PYTHON)
-endif()
-
if(WITH_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
diff --git a/source/blender/nodes/geometry/node_geometry_exec.cc b/source/blender/nodes/geometry/node_geometry_exec.cc
index 58ded7aadd2..ef4daf94bbe 100644
--- a/source/blender/nodes/geometry/node_geometry_exec.cc
+++ b/source/blender/nodes/geometry/node_geometry_exec.cc
@@ -4,3 +4,4 @@
#include "NOD_geometry_exec.hh"
BLI_CPP_TYPE_MAKE(GeometrySet, GeometrySet, CPPTypeFlags::Printable);
+BLI_CPP_TYPE_MAKE(GeometrySetVector, blender::Vector<GeometrySet>, CPPTypeFlags::None);
diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc
index 38e914b9a9f..1a12d2c49e6 100644
--- a/source/blender/nodes/geometry/node_geometry_tree.cc
+++ b/source/blender/nodes/geometry/node_geometry_tree.cc
@@ -7,6 +7,7 @@
#include "NOD_geometry.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_node.h"
#include "BKE_object.h"
@@ -31,8 +32,10 @@ static void geometry_node_tree_get_from_context(const bContext *C,
ID **r_id,
ID **r_from)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob == nullptr) {
return;
@@ -109,6 +112,7 @@ void register_node_tree_type_geo()
MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type"));
tt->type = NTREE_GEOMETRY;
strcpy(tt->idname, "GeometryNodeTree");
+ strcpy(tt->group_idname, "GeometryNodeGroup");
strcpy(tt->ui_name, N_("Geometry Node Editor"));
tt->ui_icon = ICON_GEOMETRY_NODES;
strcpy(tt->ui_description, N_("Geometry nodes"));
diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh
index efb7efaf1cc..4db4d8bb097 100644
--- a/source/blender/nodes/geometry/node_geometry_util.hh
+++ b/source/blender/nodes/geometry/node_geometry_util.hh
@@ -20,6 +20,8 @@
#include "NOD_socket_declarations.hh"
#include "NOD_socket_declarations_geometry.hh"
+#include "RNA_access.h"
+
#include "node_util.h"
void geo_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass);
@@ -34,7 +36,8 @@ void transform_mesh(Mesh &mesh,
const float3 rotation,
const float3 scale);
-void transform_geometry_set(GeometrySet &geometry,
+void transform_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float4x4 &transform,
const Depsgraph &depsgraph);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
index 58fbfb5a000..13a9cdc8600 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_accumulate_field.cc
@@ -192,7 +192,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-template<typename T> class AccumulateFieldInput final : public GeometryFieldInput {
+template<typename T> class AccumulateFieldInput final : public bke::GeometryFieldInput {
private:
Field<T> input_;
Field<int> group_index_;
@@ -204,7 +204,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
Field<T> input,
Field<int> group_index,
AccumulationMode accumulation_mode)
- : GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
+ : bke::GeometryFieldInput(CPPType::get<T>(), "Accumulation"),
input_(input),
group_index_(group_index),
source_domain_(source_domain),
@@ -212,18 +212,18 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask UNUSED(mask)) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask /*mask*/) const final
{
- const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_size = component.attribute_domain_size(field_context.domain());
+ const AttributeAccessor attributes = *context.attributes();
+ const int domain_size = attributes.domain_size(source_domain_);
if (domain_size == 0) {
return {};
}
- const AttributeAccessor attributes = *component.attributes();
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ const bke::GeometryFieldContext source_context{
+ context.geometry(), context.type(), source_domain_};
+ fn::FieldEvaluator evaluator{source_context, domain_size};
evaluator.add(input_);
evaluator.add(group_index_);
evaluator.evaluate();
@@ -266,7 +266,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
}
return attributes.adapt_domain<T>(
- VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
+ VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain());
}
uint64_t hash() const override
@@ -287,7 +287,7 @@ template<typename T> class AccumulateFieldInput final : public GeometryFieldInpu
}
};
-template<typename T> class TotalFieldInput final : public GeometryFieldInput {
+template<typename T> class TotalFieldInput final : public bke::GeometryFieldInput {
private:
Field<T> input_;
Field<int> group_index_;
@@ -295,25 +295,25 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
public:
TotalFieldInput(const eAttrDomain source_domain, Field<T> input, Field<int> group_index)
- : GeometryFieldInput(CPPType::get<T>(), "Total Value"),
+ : bke::GeometryFieldInput(CPPType::get<T>(), "Total Value"),
input_(input),
group_index_(group_index),
source_domain_(source_domain)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask UNUSED(mask)) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ IndexMask /*mask*/) const final
{
- const GeometryComponentFieldContext field_context{component, source_domain_};
- const int domain_size = component.attribute_domain_size(field_context.domain());
+ const AttributeAccessor attributes = *context.attributes();
+ const int domain_size = attributes.domain_size(source_domain_);
if (domain_size == 0) {
return {};
}
- const AttributeAccessor attributes = *component.attributes();
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ const bke::GeometryFieldContext source_context{
+ context.geometry(), context.type(), source_domain_};
+ fn::FieldEvaluator evaluator{source_context, domain_size};
evaluator.add(input_);
evaluator.add(group_index_);
evaluator.evaluate();
@@ -339,7 +339,7 @@ template<typename T> class TotalFieldInput final : public GeometryFieldInput {
}
return attributes.adapt_domain<T>(
- VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, domain);
+ VArray<T>::ForContainer(std::move(accumulations_out)), source_domain_, context.domain());
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
index 9f317075bb5..8c11288efdd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc
@@ -115,7 +115,7 @@ static void try_capture_field_on_geometry(GeometryComponent &component,
if (domain_size == 0) {
return;
}
- GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
MutableAttributeAccessor attributes = *component.attributes_for_write();
const IndexMask mask{IndexMask(domain_size)};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
index 7e4904a7a6a..f6ea6073459 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_domain_size.cc
@@ -65,13 +65,12 @@ static void node_update(bNodeTree *ntree, bNode *node)
static void node_geo_exec(GeoNodeExecParams params)
{
- GeometryComponentType component = (GeometryComponentType)params.node().custom1;
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+ const GeometryComponentType component = (GeometryComponentType)params.node().custom1;
+ const GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
switch (component) {
case GEO_COMPONENT_TYPE_MESH: {
- if (geometry_set.has_mesh()) {
- const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
const AttributeAccessor attributes = *component->attributes();
params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
params.set_output("Edge Count", attributes.domain_size(ATTR_DOMAIN_EDGE));
@@ -84,8 +83,8 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_COMPONENT_TYPE_CURVE: {
- if (geometry_set.has_curves()) {
- const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
+ if (const CurveComponent *component =
+ geometry_set.get_component_for_read<CurveComponent>()) {
const AttributeAccessor attributes = *component->attributes();
params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
params.set_output("Spline Count", attributes.domain_size(ATTR_DOMAIN_CURVE));
@@ -96,10 +95,10 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_COMPONENT_TYPE_POINT_CLOUD: {
- if (geometry_set.has_pointcloud()) {
- const PointCloudComponent *component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- params.set_output("Point Count", component->attributes()->domain_size(ATTR_DOMAIN_POINT));
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Point Count", attributes.domain_size(ATTR_DOMAIN_POINT));
}
else {
params.set_default_remaining_outputs();
@@ -107,11 +106,10 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
case GEO_COMPONENT_TYPE_INSTANCES: {
- if (geometry_set.has_instances()) {
- const InstancesComponent *component =
- geometry_set.get_component_for_read<InstancesComponent>();
- params.set_output("Instance Count",
- component->attributes()->domain_size(ATTR_DOMAIN_INSTANCE));
+ if (const InstancesComponent *component =
+ geometry_set.get_component_for_read<InstancesComponent>()) {
+ const AttributeAccessor attributes = *component->attributes();
+ params.set_output("Instance Count", attributes.domain_size(ATTR_DOMAIN_INSTANCE));
}
else {
params.set_default_remaining_outputs();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
index 34e28e50c81..af0007c2fa4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc
@@ -200,7 +200,7 @@ static void node_geo_exec(GeoNodeExecParams params)
continue;
}
if (attributes->domain_supported(domain)) {
- GeometryComponentFieldContext field_context{*component, domain};
+ bke::GeometryFieldContext field_context{*component, domain};
const int domain_num = attributes->domain_size(domain);
fn::FieldEvaluator data_evaluator{field_context, domain_num};
@@ -282,7 +282,7 @@ static void node_geo_exec(GeoNodeExecParams params)
continue;
}
if (attributes->domain_supported(domain)) {
- GeometryComponentFieldContext field_context{*component, domain};
+ bke::GeometryFieldContext field_context{*component, domain};
const int domain_num = attributes->domain_size(domain);
fn::FieldEvaluator data_evaluator{field_context, domain_num};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
index 69938f3e447..c8c58945bce 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc
@@ -93,7 +93,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* 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>("Mesh 2");
+ Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Mesh 2");
for (const GeometrySet &geometry_set : geometry_sets) {
bke::geometry_set_gather_instances(geometry_set, set_groups);
}
@@ -154,7 +154,7 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Store intersecting edges in attribute. */
if (attribute_outputs.intersecting_edges_id) {
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*result);
+ MutableAttributeAccessor attributes = result->attributes_for_write();
SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
attribute_outputs.intersecting_edges_id.get(), ATTR_DOMAIN_EDGE);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
index 00b10cc8a2f..75c198f7bdd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc
@@ -50,7 +50,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (sub_min == float3(FLT_MAX)) {
- sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ sub_geometry.remove_geometry_during_modify();
}
else {
const float3 scale = sub_max - sub_min;
@@ -58,7 +58,7 @@ static void node_geo_exec(GeoNodeExecParams params)
Mesh *mesh = geometry::create_cuboid_mesh(scale, 2, 2, 2, "uv_map");
transform_mesh(*mesh, center, float3(0), float3(1));
sub_geometry.replace_mesh(mesh);
- sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ sub_geometry.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
index 489b618b8be..c8b692651e9 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc
@@ -46,6 +46,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
/* Copy vertices. */
+ MutableSpan<MVert> dst_verts = result->verts_for_write();
for (const int i : IndexRange(verts_num)) {
float co[3];
int original_index;
@@ -59,7 +60,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
# endif
/* Copy the position of the original point. */
- copy_v3_v3(result->mvert[i].co, co);
+ copy_v3_v3(dst_verts[i].co, co);
}
else {
BLI_assert_msg(0, "Unexpected new vertex in hull output");
@@ -73,6 +74,8 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
* to a loop and edges need to be created from that. */
Array<MLoop> mloop_src(loops_num);
uint edge_index = 0;
+ MutableSpan<MEdge> edges = result->edges_for_write();
+
for (const int i : IndexRange(loops_num)) {
int v_from;
int v_to;
@@ -81,7 +84,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
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];
+ MEdge &edge = edges[edge_index];
edge.v1 = v_from;
edge.v2 = v_to;
edge.flag = ME_EDGEDRAW | ME_EDGERENDER;
@@ -95,7 +98,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
}
if (edges_num == 1) {
/* In this case there are no loops. */
- MEdge &edge = result->medge[0];
+ MEdge &edge = edges[0];
edge.v1 = 0;
edge.v2 = 1;
edge.flag |= ME_EDGEDRAW | ME_EDGERENDER | ME_LOOSEEDGE;
@@ -106,7 +109,10 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
/* Copy faces. */
Array<int> loops;
int j = 0;
- MLoop *loop = result->mloop;
+ MutableSpan<MPoly> polys = result->polys_for_write();
+ MutableSpan<MLoop> mesh_loops = result->loops_for_write();
+ MLoop *loop = mesh_loops.data();
+
for (const int i : IndexRange(faces_num)) {
const int len = plConvexHullGetFaceSize(hull, i);
@@ -116,7 +122,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> coords)
loops.reinitialize(len);
plConvexHullGetFaceLoops(hull, i, loops.data());
- MPoly &face = result->mpoly[i];
+ MPoly &face = polys[i];
face.loopstart = j;
face.totloop = len;
for (const int k : IndexRange(len)) {
@@ -140,28 +146,35 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
Span<float3> positions_span;
- if (geometry_set.has_mesh()) {
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
count++;
- const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- const Mesh *mesh = component->get_for_read();
- total_num += mesh->totvert;
+ if (const VArray<float3> positions = component->attributes()->lookup<float3>(
+ "position", ATTR_DOMAIN_POINT)) {
+ if (positions.is_span()) {
+ span_count++;
+ positions_span = positions.get_internal_span();
+ }
+ total_num += positions.size();
+ }
}
- if (geometry_set.has_pointcloud()) {
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
count++;
- span_count++;
- const PointCloudComponent *component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- const PointCloud *pointcloud = component->get_for_read();
- positions_span = {reinterpret_cast<const float3 *>(pointcloud->co), pointcloud->totpoint};
- total_num += pointcloud->totpoint;
+ if (const VArray<float3> positions = component->attributes()->lookup<float3>(
+ "position", ATTR_DOMAIN_POINT)) {
+ if (positions.is_span()) {
+ span_count++;
+ positions_span = positions.get_internal_span();
+ }
+ total_num += positions.size();
+ }
}
- if (geometry_set.has_curves()) {
+ if (const Curves *curves_id = geometry_set.get_curves_for_read()) {
count++;
span_count++;
- const Curves &curves_id = *geometry_set.get_curves_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
positions_span = curves.evaluated_positions();
total_num += positions_span.size();
}
@@ -179,26 +192,25 @@ static Mesh *compute_hull(const GeometrySet &geometry_set)
Array<float3> positions(total_num);
int offset = 0;
- if (geometry_set.has_mesh()) {
- const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>();
- const VArray<float3> varray = component->attributes()->lookup<float3>("position",
- ATTR_DOMAIN_POINT);
- varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
- offset += varray.size();
+ if (const MeshComponent *component = geometry_set.get_component_for_read<MeshComponent>()) {
+ if (const VArray<float3> varray = component->attributes()->lookup<float3>("position",
+ ATTR_DOMAIN_POINT)) {
+ 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>();
- const VArray<float3> varray = component->attributes()->lookup<float3>("position",
- ATTR_DOMAIN_POINT);
- varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
- offset += varray.size();
+ if (const PointCloudComponent *component =
+ geometry_set.get_component_for_read<PointCloudComponent>()) {
+ if (const VArray<float3> varray = component->attributes()->lookup<float3>("position",
+ ATTR_DOMAIN_POINT)) {
+ varray.materialize(positions.as_mutable_span().slice(offset, varray.size()));
+ offset += varray.size();
+ }
}
- if (geometry_set.has_curves()) {
- const Curves &curves_id = *geometry_set.get_curves_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (const Curves *curves_id = geometry_set.get_curves_for_read()) {
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
Span<float3> array = curves.evaluated_positions();
positions.as_mutable_span().slice(offset, array.size()).copy_from(array);
offset += array.size();
@@ -218,7 +230,7 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
Mesh *mesh = compute_hull(geometry_set);
geometry_set.replace_mesh(mesh);
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
params.set_output("Convex Hull", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
index db3f108aad5..28d979facac 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc
@@ -27,39 +27,31 @@ static void node_declare(NodeDeclarationBuilder &b)
N_("The selection from the start and end of the splines based on the input sizes"));
}
-class EndpointFieldInput final : public GeometryFieldInput {
+class EndpointFieldInput final : public bke::CurvesFieldInput {
Field<int> start_size_;
Field<int> end_size_;
public:
EndpointFieldInput(Field<int> start_size, Field<int> end_size)
- : GeometryFieldInput(CPPType::get<bool>(), "Endpoint Selection node"),
+ : bke::CurvesFieldInput(CPPType::get<bool>(), "Endpoint Selection node"),
start_size_(start_size),
end_size_(end_size)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) {
- return nullptr;
+ if (domain != ATTR_DOMAIN_POINT) {
+ return {};
}
-
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (!curve_component.has_curves()) {
- return nullptr;
- }
-
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
if (curves.points_num() == 0) {
- return nullptr;
+ return {};
}
- GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext size_context{curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{size_context, curves.curves_num()};
evaluator.add(start_size_);
evaluator.add(end_size_);
@@ -72,12 +64,12 @@ class EndpointFieldInput final : public GeometryFieldInput {
devirtualize_varray2(start_size, end_size, [&](const auto &start_size, const auto &end_size) {
threading::parallel_for(curves.curves_range(), 1024, [&](IndexRange curves_range) {
for (const int i : curves_range) {
- const IndexRange range = curves.points_for_curve(i);
+ const IndexRange points = curves.points_for_curve(i);
const int start = std::max(start_size[i], 0);
const int end = std::max(end_size[i], 0);
- selection_span.slice(range.take_front(start)).fill(true);
- selection_span.slice(range.take_back(end)).fill(true);
+ selection_span.slice(points.take_front(start)).fill(true);
+ selection_span.slice(points.take_back(end)).fill(true);
}
});
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
index c9a8dba55b2..c675ee472cd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc
@@ -79,10 +79,10 @@ static Mesh *cdt_to_mesh(const meshintersect::CDT_result<double> &result)
}
Mesh *mesh = BKE_mesh_new_nomain(vert_len, edge_len, 0, loop_len, poly_len);
- 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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
for (const int i : IndexRange(result.vert.size())) {
copy_v3_v3(verts[i].co, float3((float)result.vert[i].x, (float)result.vert[i].y, 0.0f));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
index e200350dc18..4586bb24464 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc
@@ -1,16 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_task.hh"
-
#include "UI_interface.h"
#include "UI_resources.h"
-#include "DNA_node_types.h"
+#include "GEO_fillet_curves.hh"
#include "node_geometry_util.hh"
-#include "BKE_spline.hh"
-
namespace blender::nodes::node_geo_curve_fillet_cc {
NODE_STORAGE_FUNCS(NodeGeometryCurveFillet)
@@ -44,571 +40,18 @@ static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
static void node_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeGeometryCurveFillet *data = MEM_cnew<NodeGeometryCurveFillet>(__func__);
-
data->mode = GEO_NODE_CURVE_FILLET_BEZIER;
node->storage = data;
}
-struct FilletParam {
- GeometryNodeCurveFilletMode mode;
-
- /* Number of points to be added. */
- VArray<int> counts;
-
- /* Radii for fillet arc at all vertices. */
- VArray<float> radii;
-
- /* Whether or not fillets are allowed to overlap. */
- bool limit_radius;
-};
-
-/* A data structure used to store fillet data about all vertices to be filleted. */
-struct FilletData {
- Span<float3> positions;
- Array<float3> directions, axes;
- Array<float> radii, angles;
- Array<int> counts;
-};
-
static void node_update(bNodeTree *ntree, bNode *node)
{
const NodeGeometryCurveFillet &storage = node_storage(*node);
const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)storage.mode;
-
bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next;
-
nodeSetSocketAvailability(ntree, poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY);
}
-/* Function to get the center of a fillet. */
-static float3 get_center(const float3 vec_pos2prev,
- const float3 pos,
- const float3 axis,
- const float angle)
-{
- float3 vec_pos2center;
- rotate_normalized_v3_v3v3fl(vec_pos2center, vec_pos2prev, axis, M_PI_2 - angle / 2.0f);
- vec_pos2center *= 1.0f / sinf(angle / 2.0f);
-
- return vec_pos2center + pos;
-}
-
-/* Function to get the center of the fillet using fillet data */
-static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const int index)
-{
- const float angle = fd.angles[index];
- const float3 axis = fd.axes[index];
- const float3 pos = fd.positions[index];
-
- return get_center(vec_pos2prev, pos, axis, angle);
-}
-
-/* Calculate the direction vectors from each vertex to their previous vertex. */
-static Array<float3> calculate_directions(const Span<float3> positions)
-{
- const int num = positions.size();
- Array<float3> directions(num);
-
- for (const int i : IndexRange(num - 1)) {
- directions[i] = math::normalize(positions[i + 1] - positions[i]);
- }
- directions[num - 1] = math::normalize(positions[0] - positions[num - 1]);
-
- return directions;
-}
-
-/* Calculate the axes around which the fillet is built. */
-static Array<float3> calculate_axes(const Span<float3> directions)
-{
- const int num = directions.size();
- Array<float3> axes(num);
-
- axes[0] = math::normalize(math::cross(-directions[num - 1], directions[0]));
- for (const int i : IndexRange(1, num - 1)) {
- axes[i] = math::normalize(math::cross(-directions[i - 1], directions[i]));
- }
-
- return axes;
-}
-
-/* Calculate the angle of the arc formed by the fillet. */
-static Array<float> calculate_angles(const Span<float3> directions)
-{
- const int num = directions.size();
- Array<float> angles(num);
-
- angles[0] = M_PI - angle_v3v3(-directions[num - 1], directions[0]);
- for (const int i : IndexRange(1, num - 1)) {
- angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]);
- }
-
- return angles;
-}
-
-/* Calculate the segment count in each filleted arc. */
-static Array<int> calculate_counts(const FilletParam &fillet_param,
- const int num,
- const int spline_offset,
- const bool cyclic)
-{
- Array<int> counts(num, 1);
- if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
- for (const int i : IndexRange(num)) {
- counts[i] = fillet_param.counts[spline_offset + i];
- }
- }
- if (!cyclic) {
- counts[0] = counts[num - 1] = 0;
- }
-
- return counts;
-}
-
-/* Calculate the radii for the vertices to be filleted. */
-static Array<float> calculate_radii(const FilletParam &fillet_param,
- const int num,
- const int spline_offset)
-{
- Array<float> radii(num, 0.0f);
- if (fillet_param.limit_radius) {
- for (const int i : IndexRange(num)) {
- radii[i] = std::max(fillet_param.radii[spline_offset + i], 0.0f);
- }
- }
- else {
- for (const int i : IndexRange(num)) {
- radii[i] = fillet_param.radii[spline_offset + i];
- }
- }
-
- return radii;
-}
-
-/* Calculate the number of vertices added per vertex on the source spline. */
-static int calculate_point_counts(MutableSpan<int> point_counts,
- const Span<float> radii,
- const Span<int> counts)
-{
- int added_count = 0;
- for (const int i : IndexRange(point_counts.size())) {
- /* Calculate number of points to be added for the vertex. */
- if (radii[i] != 0.0f) {
- added_count += counts[i];
- point_counts[i] = counts[i] + 1;
- }
- }
-
- return added_count;
-}
-
-static FilletData calculate_fillet_data(const Spline &spline,
- const FilletParam &fillet_param,
- int &added_count,
- MutableSpan<int> point_counts,
- const int spline_offset)
-{
- const int num = spline.size();
-
- FilletData fd;
- fd.directions = calculate_directions(spline.positions());
- fd.positions = spline.positions();
- fd.axes = calculate_axes(fd.directions);
- fd.angles = calculate_angles(fd.directions);
- fd.counts = calculate_counts(fillet_param, num, spline_offset, spline.is_cyclic());
- fd.radii = calculate_radii(fillet_param, num, spline_offset);
-
- added_count = calculate_point_counts(point_counts, fd.radii, fd.counts);
-
- return fd;
-}
-
-/* Limit the radius based on angle and radii to prevent overlapping. */
-static void limit_radii(FilletData &fd, const bool cyclic)
-{
- MutableSpan<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> positions(fd.positions);
-
- const int num = radii.size();
- const int fillet_count = cyclic ? num : num - 2;
- const int start = cyclic ? 0 : 1;
- Array<float> max_radii(num, FLT_MAX);
-
- if (cyclic) {
- /* Calculate lengths between adjacent control points. */
- const float len_prev = math::distance(positions[0], positions[num - 1]);
- const float len_next = math::distance(positions[0], positions[1]);
-
- /* Calculate tangent lengths of fillets in control points. */
- const float tan_len = radii[0] * tan(angles[0] / 2.0f);
- const float tan_len_prev = radii[num - 1] * tan(angles[num - 1] / 2.0f);
- const float tan_len_next = radii[1] * tan(angles[1] / 2.0f);
-
- float factor_prev = 1.0f, factor_next = 1.0f;
- if (tan_len + tan_len_prev > len_prev) {
- factor_prev = len_prev / (tan_len + tan_len_prev);
- }
- if (tan_len + tan_len_next > len_next) {
- factor_next = len_next / (tan_len + tan_len_next);
- }
-
- /* Scale max radii by calculated factors. */
- max_radii[0] = radii[0] * std::min(factor_next, factor_prev);
- max_radii[1] = radii[1] * factor_next;
- max_radii[num - 1] = radii[num - 1] * factor_prev;
- }
-
- /* Initialize max_radii to largest possible radii. */
- float prev_dist = math::distance(positions[1], positions[0]);
- for (const int i : IndexRange(1, num - 2)) {
- const float temp_dist = math::distance(positions[i], positions[i + 1]);
- max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f);
- prev_dist = temp_dist;
- }
-
- /* Max radii calculations for each index. */
- for (const int i : IndexRange(start, fillet_count - 1)) {
- const float len_next = math::distance(positions[i], positions[i + 1]);
- const float tan_len = radii[i] * tan(angles[i] / 2.0f);
- const float tan_len_next = radii[i + 1] * tan(angles[i + 1] / 2.0f);
-
- /* Scale down radii if too large for segment. */
- float factor = 1.0f;
- if (tan_len + tan_len_next > len_next) {
- factor = len_next / (tan_len + tan_len_next);
- }
- max_radii[i] = std::min(max_radii[i], radii[i] * factor);
- max_radii[i + 1] = std::min(max_radii[i + 1], radii[i + 1] * factor);
- }
-
- /* Assign the max_radii to the fillet data's radii. */
- for (const int i : IndexRange(num)) {
- radii[i] = std::min(radii[i], max_radii[i]);
- }
-}
-
-/*
- * Create a mapping from each vertex in the destination spline to that of the source spline.
- * Used for copying the data from the source spline.
- */
-static Array<int> create_dst_to_src_map(const Span<int> point_counts, const int total_points)
-{
- Array<int> map(total_points);
- MutableSpan<int> map_span{map};
- int index = 0;
-
- for (const int i : point_counts.index_range()) {
- map_span.slice(index, point_counts[i]).fill(i);
- index += point_counts[i];
- }
-
- BLI_assert(index == total_points);
-
- return map;
-}
-
-template<typename T>
-static void copy_attribute_by_mapping(const Span<T> src,
- MutableSpan<T> dst,
- const Span<int> mapping)
-{
- for (const int i : dst.index_range()) {
- dst[i] = src[mapping[i]];
- }
-}
-
-/* Copy radii and tilts from source spline to destination. Positions are handled later in update
- * positions methods. */
-static void copy_common_attributes_by_mapping(const Spline &src,
- Spline &dst,
- const Span<int> mapping)
-{
- copy_attribute_by_mapping(src.radii(), dst.radii(), mapping);
- copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping);
-
- src.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- std::optional<GSpan> src_attribute = src.attributes.get_for_read(attribute_id);
- if (dst.attributes.create(attribute_id, meta_data.data_type)) {
- std::optional<GMutableSpan> dst_attribute = dst.attributes.get_for_write(attribute_id);
- if (dst_attribute) {
- attribute_math::convert_to_static_type(dst_attribute->type(), [&](auto dummy) {
- using T = decltype(dummy);
- copy_attribute_by_mapping(
- src_attribute->typed<T>(), dst_attribute->typed<T>(), mapping);
- });
- return true;
- }
- }
- BLI_assert_unreachable();
- return false;
- },
- ATTR_DOMAIN_POINT);
-}
-
-/* Update the vertex positions and handle positions of a Bezier spline based on fillet data. */
-static void update_bezier_positions(const FilletData &fd,
- BezierSpline &dst_spline,
- const BezierSpline &src_spline,
- const Span<int> point_counts)
-{
- Span<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> axes(fd.axes);
- Span<float3> positions(fd.positions);
- Span<float3> directions(fd.directions);
-
- const int num = radii.size();
-
- int i_dst = 0;
- for (const int i_src : IndexRange(num)) {
- const int count = point_counts[i_src];
-
- /* Skip if the point count for the vertex is 1. */
- if (count == 1) {
- dst_spline.positions()[i_dst] = src_spline.positions()[i_src];
- dst_spline.handle_types_left()[i_dst] = src_spline.handle_types_left()[i_src];
- dst_spline.handle_types_right()[i_dst] = src_spline.handle_types_right()[i_src];
- dst_spline.handle_positions_left()[i_dst] = src_spline.handle_positions_left()[i_src];
- dst_spline.handle_positions_right()[i_dst] = src_spline.handle_positions_right()[i_src];
- i_dst++;
- continue;
- }
-
- /* Calculate the angle to be formed between any 2 adjacent vertices within the fillet. */
- const float segment_angle = angles[i_src] / (count - 1);
- /* Calculate the handle length for each added vertex. Equation: L = 4R/3 * tan(A/4) */
- const float handle_length = 4.0f * radii[i_src] / 3.0f * tan(segment_angle / 4.0f);
- /* Calculate the distance by which each vertex should be displaced from their initial position.
- */
- const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f);
-
- /* Position the end points of the arc and their handles. */
- const int end_i = i_dst + count - 1;
- const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1];
- const float3 next_dir = directions[i_src];
- dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir;
- dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir;
- dst_spline.handle_positions_right()[i_dst] = dst_spline.positions()[i_dst] -
- handle_length * prev_dir;
- dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] -
- handle_length * next_dir;
- dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] =
- BEZIER_HANDLE_ALIGN;
- dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] =
- BEZIER_HANDLE_VECTOR;
- dst_spline.mark_cache_invalid();
-
- /* Calculate the center of the radius to be formed. */
- const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src);
- /* Calculate the vector of the radius formed by the first vertex. */
- float3 radius_vec = dst_spline.positions()[i_dst] - center;
- float radius;
- radius_vec = math::normalize_and_get_length(radius_vec, radius);
-
- dst_spline.handle_types_right().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
- dst_spline.handle_types_left().slice(1, count - 2).fill(BEZIER_HANDLE_ALIGN);
-
- /* For each of the vertices in between the end points. */
- for (const int j : IndexRange(1, count - 2)) {
- int index = i_dst + j;
- /* Rotate the radius by the segment angle and determine its tangent (used for getting handle
- * directions). */
- float3 new_radius_vec, tangent_vec;
- rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle);
- rotate_normalized_v3_v3v3fl(tangent_vec, new_radius_vec, axes[i_src], M_PI_2);
- radius_vec = new_radius_vec;
- tangent_vec *= handle_length;
-
- /* Adjust the positions of the respective vertex and its handles. */
- dst_spline.positions()[index] = center + new_radius_vec * radius;
- dst_spline.handle_positions_left()[index] = dst_spline.positions()[index] + tangent_vec;
- dst_spline.handle_positions_right()[index] = dst_spline.positions()[index] - tangent_vec;
- }
-
- i_dst += count;
- }
-}
-
-/* Update the vertex positions of a Poly spline based on fillet data. */
-static void update_poly_positions(const FilletData &fd,
- Spline &dst_spline,
- const Spline &src_spline,
- const Span<int> point_counts)
-{
- Span<float> radii(fd.radii);
- Span<float> angles(fd.angles);
- Span<float3> axes(fd.axes);
- Span<float3> positions(fd.positions);
- Span<float3> directions(fd.directions);
-
- const int num = radii.size();
-
- int i_dst = 0;
- for (const int i_src : IndexRange(num)) {
- const int count = point_counts[i_src];
-
- /* Skip if the point count for the vertex is 1. */
- if (count == 1) {
- dst_spline.positions()[i_dst] = src_spline.positions()[i_src];
- i_dst++;
- continue;
- }
-
- const float segment_angle = angles[i_src] / (count - 1);
- const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f);
-
- /* Position the end points of the arc. */
- const int end_i = i_dst + count - 1;
- const float3 prev_dir = i_src == 0 ? -directions[num - 1] : -directions[i_src - 1];
- const float3 next_dir = directions[i_src];
- dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir;
- dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir;
-
- /* Calculate the center of the radius to be formed. */
- const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src);
- /* Calculate the vector of the radius formed by the first vertex. */
- float3 radius_vec = dst_spline.positions()[i_dst] - center;
-
- for (const int j : IndexRange(1, count - 2)) {
- /* Rotate the radius by the segment angle */
- float3 new_radius_vec;
- rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle);
- radius_vec = new_radius_vec;
-
- dst_spline.positions()[i_dst + j] = center + new_radius_vec;
- }
-
- i_dst += count;
- }
-}
-
-static SplinePtr fillet_spline(const Spline &spline,
- const FilletParam &fillet_param,
- const int spline_offset)
-{
- const int num = spline.size();
- const bool cyclic = spline.is_cyclic();
-
- if (num < 3) {
- return spline.copy();
- }
-
- /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */
- Array<int> point_counts(num, 1);
-
- int added_count = 0;
- /* Update point_counts array and added_count. */
- FilletData fd = calculate_fillet_data(
- spline, fillet_param, added_count, point_counts, spline_offset);
- if (fillet_param.limit_radius) {
- limit_radii(fd, cyclic);
- }
-
- const int total_points = added_count + num;
- const Array<int> dst_to_src = create_dst_to_src_map(point_counts, total_points);
- SplinePtr dst_spline_ptr = spline.copy_only_settings();
- (*dst_spline_ptr).resize(total_points);
- copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src);
-
- switch (spline.type()) {
- case CURVE_TYPE_BEZIER: {
- const BezierSpline &src_spline = static_cast<const BezierSpline &>(spline);
- BezierSpline &dst_spline = static_cast<BezierSpline &>(*dst_spline_ptr);
- if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) {
- dst_spline.handle_types_left().fill(BEZIER_HANDLE_VECTOR);
- dst_spline.handle_types_right().fill(BEZIER_HANDLE_VECTOR);
- update_poly_positions(fd, dst_spline, src_spline, point_counts);
- }
- else {
- update_bezier_positions(fd, dst_spline, src_spline, point_counts);
- }
- break;
- }
- case CURVE_TYPE_POLY: {
- update_poly_positions(fd, *dst_spline_ptr, spline, point_counts);
- break;
- }
- case CURVE_TYPE_NURBS: {
- const NURBSpline &src_spline = static_cast<const NURBSpline &>(spline);
- NURBSpline &dst_spline = static_cast<NURBSpline &>(*dst_spline_ptr);
- copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src);
- update_poly_positions(fd, dst_spline, src_spline, point_counts);
- break;
- }
- case CURVE_TYPE_CATMULL_ROM: {
- BLI_assert_unreachable();
- break;
- }
- }
-
- return dst_spline_ptr;
-}
-
-static std::unique_ptr<CurveEval> fillet_curve(const CurveEval &input_curve,
- const FilletParam &fillet_param)
-{
- Span<SplinePtr> input_splines = input_curve.splines();
-
- std::unique_ptr<CurveEval> output_curve = std::make_unique<CurveEval>();
- const int splines_num = input_splines.size();
- output_curve->resize(splines_num);
- MutableSpan<SplinePtr> output_splines = output_curve->splines();
- Array<int> spline_offsets = input_curve.control_point_offsets();
-
- threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- output_splines[i] = fillet_spline(*input_splines[i], fillet_param, spline_offsets[i]);
- }
- });
- output_curve->attributes = input_curve.attributes;
-
- return output_curve;
-}
-
-static void calculate_curve_fillet(GeometrySet &geometry_set,
- const GeometryNodeCurveFilletMode mode,
- const Field<float> &radius_field,
- const std::optional<Field<int>> &count_field,
- const bool limit_radius)
-{
- if (!geometry_set.has_curves()) {
- return;
- }
-
- FilletParam fillet_param;
- fillet_param.mode = mode;
-
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- fn::FieldEvaluator field_evaluator{field_context, domain_size};
-
- field_evaluator.add(radius_field);
-
- if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- field_evaluator.add(*count_field);
- }
-
- field_evaluator.evaluate();
-
- fillet_param.radii = field_evaluator.get_evaluated<float>(0);
- if (fillet_param.radii.is_single() && fillet_param.radii.get_internal_single() < 0.0f) {
- return;
- }
-
- if (mode == GEO_NODE_CURVE_FILLET_POLY) {
- fillet_param.counts = field_evaluator.get_evaluated<int>(1);
- }
-
- fillet_param.limit_radius = limit_radius;
-
- const std::unique_ptr<CurveEval> input_curve = curves_to_curve_eval(*component.get_for_read());
- std::unique_ptr<CurveEval> output_curve = fillet_curve(*input_curve, fillet_param);
-
- geometry_set.replace_curves(curve_eval_to_curves(*output_curve));
-}
-
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
@@ -625,7 +68,41 @@ static void node_geo_exec(GeoNodeExecParams params)
}
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius);
+ if (!geometry_set.has_curves()) {
+ return;
+ }
+
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ bke::CurvesFieldContext context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, curves.points_num()};
+ evaluator.add(radius_field);
+
+ switch (mode) {
+ case GEO_NODE_CURVE_FILLET_BEZIER: {
+ evaluator.evaluate();
+ bke::CurvesGeometry dst_curves = geometry::fillet_curves_bezier(
+ curves, curves.curves_range(), evaluator.get_evaluated<float>(0), limit_radius);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
+ break;
+ }
+ case GEO_NODE_CURVE_FILLET_POLY: {
+ evaluator.add(*count_field);
+ evaluator.evaluate();
+ bke::CurvesGeometry dst_curves = geometry::fillet_curves_poly(
+ curves,
+ curves.curves_range(),
+ evaluator.get_evaluated<float>(0),
+ evaluator.get_evaluated<int>(1),
+ limit_radius);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
+ break;
+ }
+ }
});
params.set_output("Curve", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
index 5ef20f03f28..b34b22e995d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc
@@ -70,35 +70,28 @@ static void select_by_handle_type(const bke::CurvesGeometry &curves,
}
}
-class HandleTypeFieldInput final : public GeometryFieldInput {
+class HandleTypeFieldInput final : public bke::CurvesFieldInput {
HandleType type_;
GeometryNodeCurveHandleMode mode_;
public:
HandleTypeFieldInput(HandleType type, GeometryNodeCurveHandleMode mode)
- : GeometryFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
+ : bke::CurvesFieldInput(CPPType::get<bool>(), "Handle Type Selection node"),
type_(type),
mode_(mode)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) {
+ if (domain != ATTR_DOMAIN_POINT) {
return {};
}
-
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- const Curves *curves_id = curve_component.get_for_read();
- if (curves_id == nullptr) {
- return {};
- }
-
Array<bool> selection(mask.min_array_size());
- select_by_handle_type(bke::CurvesGeometry::wrap(curves_id->geometry), type_, mode_, selection);
+ select_by_handle_type(curves, type_, mode_, selection);
return VArray<bool>::ForContainer(std::move(selection));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
index 78a132064ed..41eafe2a741 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc
@@ -60,14 +60,20 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
+
switch (mode) {
case GEO_NODE_CURVE_RESAMPLE_COUNT: {
Field<int> count = params.extract_input<Field<int>>("Count");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_count(*component, selection, count));
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_count(
+ src_curves, selection, count);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
@@ -75,20 +81,27 @@ static void node_geo_exec(GeoNodeExecParams params)
case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
Field<float> length = params.extract_input<Field<float>>("Length");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_length(*component, selection, length));
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_length(
+ src_curves, selection, length);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
}
case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
- if (const CurveComponent *component = geometry.get_component_for_read<CurveComponent>()) {
- if (!component->is_empty()) {
- geometry.replace_curves(geometry::resample_to_evaluated(*component, selection));
- }
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(src_curves, selection);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(*src_curves_id, *dst_curves_id);
+ geometry.replace_curves(dst_curves_id);
}
});
break;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
index de29735bd2d..0169ead5bd2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc
@@ -23,14 +23,12 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_curves()) {
return;
}
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
- selection_evaluator.add(selection_field);
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator selection_evaluator{field_context, src_curves.curves_num()};
+ selection_evaluator.add(params.get_input<Field<bool>>("Selection"));
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
if (selection.is_empty()) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
index 01cf1d8db52..e80b600a740 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc
@@ -1,8 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BLI_task.hh"
+#include "BLI_devirtualize_parameters.hh"
+#include "BLI_length_parameterize.hh"
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -58,34 +59,103 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH);
}
-template<typename T> static T sample_with_lookup(const Spline::LookupResult lookup, Span<T> data)
+static void sample_indices_and_lengths(const Span<float> accumulated_lengths,
+ const Span<float> sample_lengths,
+ const IndexMask mask,
+ MutableSpan<int> r_segment_indices,
+ MutableSpan<float> r_length_in_segment)
{
- return attribute_math::mix2(
- lookup.factor, data[lookup.evaluated_index], data[lookup.next_evaluated_index]);
+ const float total_length = accumulated_lengths.last();
+ length_parameterize::SampleSegmentHint hint;
+
+ mask.to_best_mask_type([&](const auto mask) {
+ for (const int64_t i : mask) {
+ int segment_i;
+ float factor_in_segment;
+ length_parameterize::sample_at_length(accumulated_lengths,
+ std::clamp(sample_lengths[i], 0.0f, total_length),
+ segment_i,
+ factor_in_segment,
+ &hint);
+ const float segment_start = segment_i == 0 ? 0.0f : accumulated_lengths[segment_i - 1];
+ const float segment_end = accumulated_lengths[segment_i];
+ const float segment_length = segment_end - segment_start;
+
+ r_segment_indices[i] = segment_i;
+ r_length_in_segment[i] = factor_in_segment * segment_length;
+ }
+ });
+}
+
+static void sample_indices_and_factors_to_compressed(const Span<float> accumulated_lengths,
+ const Span<float> sample_lengths,
+ const IndexMask mask,
+ MutableSpan<int> r_segment_indices,
+ MutableSpan<float> r_factor_in_segment)
+{
+ const float total_length = accumulated_lengths.last();
+ length_parameterize::SampleSegmentHint hint;
+
+ mask.to_best_mask_type([&](const auto mask) {
+ for (const int64_t i : IndexRange(mask.size())) {
+ const float length = sample_lengths[mask[i]];
+ length_parameterize::sample_at_length(accumulated_lengths,
+ std::clamp(length, 0.0f, total_length),
+ r_segment_indices[i],
+ r_factor_in_segment[i],
+ &hint);
+ }
+ });
}
+/**
+ * Given an array of accumulated lengths, find the segment indices that
+ * sample lengths lie on, and how far along the segment they are.
+ */
+class SampleFloatSegmentsFunction : public fn::MultiFunction {
+ private:
+ Array<float> accumulated_lengths_;
+
+ public:
+ SampleFloatSegmentsFunction(Array<float> accumulated_lengths)
+ : accumulated_lengths_(std::move(accumulated_lengths))
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Sample Curve Index"};
+ signature.single_input<float>("Length");
+
+ signature.single_output<int>("Curve Index");
+ signature.single_output<float>("Length in Curve");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArraySpan<float> lengths = params.readonly_single_input<float>(0, "Length");
+ MutableSpan<int> indices = params.uninitialized_single_output<int>(1, "Curve Index");
+ MutableSpan<float> lengths_in_segments = params.uninitialized_single_output<float>(
+ 2, "Length in Curve");
+
+ sample_indices_and_lengths(accumulated_lengths_, lengths, mask, indices, lengths_in_segments);
+ }
+};
+
class SampleCurveFunction : public fn::MultiFunction {
private:
/**
- * The function holds a geometry set instead of a curve or a curve component in order to
- * maintain a reference to the geometry while the field tree is being built, so that the
- * curve is not freed before the function can execute.
+ * The function holds a geometry set instead of curves or a curve component reference in order
+ * to maintain a ownership of the geometry while the field tree is being built and used, so
+ * that the curve is not freed before the function can execute.
*/
GeometrySet geometry_set_;
- /**
- * To support factor inputs, the node adds another field operation before this one to multiply by
- * the curve's total length. Since that must calculate the spline lengths anyway, store them to
- * reuse the calculation.
- */
- Array<float> spline_lengths_;
- /** The last member of #spline_lengths_, extracted for convenience. */
- const float total_length_;
public:
- SampleCurveFunction(GeometrySet geometry_set, Array<float> spline_lengths)
- : geometry_set_(std::move(geometry_set)),
- spline_lengths_(std::move(spline_lengths)),
- total_length_(spline_lengths_.last())
+ SampleCurveFunction(GeometrySet geometry_set) : geometry_set_(std::move(geometry_set))
{
static fn::MFSignature signature = create_signature();
this->set_signature(&signature);
@@ -93,7 +163,8 @@ class SampleCurveFunction : public fn::MultiFunction {
static fn::MFSignature create_signature()
{
- blender::fn::MFSignatureBuilder signature{"Curve Sample"};
+ blender::fn::MFSignatureBuilder signature{"Sample Curve"};
+ signature.single_input<int>("Curve Index");
signature.single_input<float>("Length");
signature.single_output<float3>("Position");
signature.single_output<float3>("Tangent");
@@ -104,11 +175,11 @@ class SampleCurveFunction : public fn::MultiFunction {
void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
{
MutableSpan<float3> sampled_positions = params.uninitialized_single_output_if_required<float3>(
- 1, "Position");
+ 2, "Position");
MutableSpan<float3> sampled_tangents = params.uninitialized_single_output_if_required<float3>(
- 2, "Tangent");
+ 3, "Tangent");
MutableSpan<float3> sampled_normals = params.uninitialized_single_output_if_required<float3>(
- 3, "Normal");
+ 4, "Normal");
auto return_default = [&]() {
if (!sampled_positions.is_empty()) {
@@ -126,61 +197,78 @@ class SampleCurveFunction : public fn::MultiFunction {
return return_default();
}
- const CurveComponent *curve_component = geometry_set_.get_component_for_read<CurveComponent>();
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *curve_component->get_for_read());
- Span<SplinePtr> splines = curve->splines();
- if (splines.is_empty()) {
+ const Curves &curves_id = *geometry_set_.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (curves.points_num() == 0) {
return return_default();
}
-
- const VArray<float> &lengths_varray = params.readonly_single_input<float>(0, "Length");
- const VArraySpan lengths{lengths_varray};
-#ifdef DEBUG
- for (const float length : lengths) {
- /* Lengths must be in range of the curve's total length. This is ensured in
- * #get_length_input_field by adding another multi-function before this one
- * to clamp the lengths. */
- BLI_assert(length >= 0.0f && length <= total_length_);
- }
-#endif
-
- Array<int> spline_indices(mask.min_array_size());
- for (const int i : mask) {
- const float *offset = std::lower_bound(
- spline_lengths_.begin(), spline_lengths_.end(), lengths[i]);
- const int index = offset - spline_lengths_.data() - 1;
- spline_indices[i] = std::max(index, 0);
+ Span<float3> evaluated_positions = curves.evaluated_positions();
+ Span<float3> evaluated_tangents;
+ Span<float3> evaluated_normals;
+ if (!sampled_tangents.is_empty()) {
+ evaluated_tangents = curves.evaluated_tangents();
}
-
- /* Storing lookups in an array is unnecessary but will simplify custom attribute transfer. */
- Array<Spline::LookupResult> lookups(mask.min_array_size());
- for (const int i : mask) {
- const float length_in_spline = lengths[i] - spline_lengths_[spline_indices[i]];
- lookups[i] = splines[spline_indices[i]]->lookup_evaluated_length(length_in_spline);
+ if (!sampled_normals.is_empty()) {
+ evaluated_normals = curves.evaluated_normals();
}
- if (!sampled_positions.is_empty()) {
- for (const int i : mask) {
- const Spline::LookupResult &lookup = lookups[i];
- const Span<float3> evaluated_positions = splines[spline_indices[i]]->evaluated_positions();
- sampled_positions[i] = sample_with_lookup(lookup, evaluated_positions);
+ const VArray<int> curve_indices = params.readonly_single_input<int>(0, "Curve Index");
+ const VArraySpan<float> lengths = params.readonly_single_input<float>(1, "Length");
+ const VArray<bool> cyclic = curves.cyclic();
+
+ Array<int> indices;
+ Array<float> factors;
+
+ auto sample_curve = [&](const int curve_i, const IndexMask mask) {
+ /* Store the sampled indices and factors in arrays the size of the mask.
+ * Then, during interpolation, move the results back to the masked indices. */
+ indices.reinitialize(mask.size());
+ factors.reinitialize(mask.size());
+ sample_indices_and_factors_to_compressed(
+ curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]),
+ lengths,
+ mask,
+ indices,
+ factors);
+
+ const IndexRange evaluated_points = curves.evaluated_points_for_curve(curve_i);
+ if (!sampled_positions.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_positions.slice(evaluated_points),
+ indices,
+ factors,
+ mask,
+ sampled_positions);
}
- }
-
- if (!sampled_tangents.is_empty()) {
- for (const int i : mask) {
- const Spline::LookupResult &lookup = lookups[i];
- const Span<float3> evaluated_tangents = splines[spline_indices[i]]->evaluated_tangents();
- sampled_tangents[i] = math::normalize(sample_with_lookup(lookup, evaluated_tangents));
+ if (!sampled_tangents.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_tangents.slice(evaluated_points), indices, factors, mask, sampled_tangents);
+ for (const int64_t i : mask) {
+ sampled_tangents[i] = math::normalize(sampled_tangents[i]);
+ }
}
- }
+ if (!sampled_normals.is_empty()) {
+ length_parameterize::interpolate_to_masked<float3>(
+ evaluated_normals.slice(evaluated_points), indices, factors, mask, sampled_normals);
+ for (const int64_t i : mask) {
+ sampled_normals[i] = math::normalize(sampled_normals[i]);
+ }
+ }
+ };
- if (!sampled_normals.is_empty()) {
- for (const int i : mask) {
- const Spline::LookupResult &lookup = lookups[i];
- const Span<float3> evaluated_normals = splines[spline_indices[i]]->evaluated_normals();
- sampled_normals[i] = math::normalize(sample_with_lookup(lookup, evaluated_normals));
+ if (curve_indices.is_single()) {
+ sample_curve(curve_indices.get_internal_single(), mask);
+ }
+ else {
+ MultiValueMap<int, int64_t> indices_per_curve;
+ devirtualize_varray(curve_indices, [&](const auto curve_indices) {
+ for (const int64_t i : mask) {
+ indices_per_curve.add(curve_indices[i], i);
+ }
+ });
+
+ for (const int curve_i : indices_per_curve.keys()) {
+ sample_curve(curve_i, IndexMask(indices_per_curve.lookup(curve_i)));
}
}
}
@@ -188,82 +276,82 @@ class SampleCurveFunction : public fn::MultiFunction {
/**
* Pre-process the lengths or factors used for the sampling, turning factors into lengths, and
- * clamping between zero and the total length of the curve. Do this as a separate operation in the
+ * clamping between zero and the total length of the curves. Do this as a separate operation in the
* field tree to make the sampling simpler, and to let the evaluator optimize better.
*
* \todo Use a mutable single input instead when they are supported.
*/
-static Field<float> get_length_input_field(const GeoNodeExecParams &params,
- const float curve_total_length)
+static Field<float> get_length_input_field(GeoNodeExecParams params,
+ const GeometryNodeCurveSampleMode mode,
+ const float curves_total_length)
{
- const NodeGeometryCurveSample &storage = node_storage(params.node());
- const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
-
if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
- /* Just make sure the length is in bounds of the curve. */
- Field<float> length_field = params.get_input<Field<float>>("Length");
- auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>(
- __func__,
- [curve_total_length](float length) {
- return std::clamp(length, 0.0f, curve_total_length);
- },
- fn::CustomMF_presets::AllSpanOrSingle());
- auto clamp_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(clamp_fn), {std::move(length_field)}));
-
- return Field<float>(std::move(clamp_op), 0);
+ return params.extract_input<Field<float>>("Length");
}
- /* Convert the factor to a length and clamp it to the bounds of the curve. */
+ /* Convert the factor to a length. */
Field<float> factor_field = params.get_input<Field<float>>("Factor");
auto clamp_fn = std::make_unique<fn::CustomMF_SI_SO<float, float>>(
__func__,
- [curve_total_length](float factor) {
- const float length = factor * curve_total_length;
- return std::clamp(length, 0.0f, curve_total_length);
- },
+ [curves_total_length](float factor) { return factor * curves_total_length; },
fn::CustomMF_presets::AllSpanOrSingle());
- auto process_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(clamp_fn), {std::move(factor_field)}));
- return Field<float>(std::move(process_op), 0);
+ return Field<float>(FieldOperation::Create(std::move(clamp_fn), {std::move(factor_field)}), 0);
}
-static void node_geo_exec(GeoNodeExecParams params)
+static Array<float> curve_accumulated_lengths(const bke::CurvesGeometry &curves)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
-
- const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>();
- if (component == nullptr) {
- params.set_default_remaining_outputs();
- return;
+ curves.ensure_evaluated_lengths();
+
+ Array<float> curve_lengths(curves.curves_num());
+ const VArray<bool> cyclic = curves.cyclic();
+ float length = 0.0f;
+ for (const int i : curves.curves_range()) {
+ length += curves.evaluated_length_total_for_curve(i, cyclic[i]);
+ curve_lengths[i] = length;
}
+ return curve_lengths;
+}
- if (!component->has_curves()) {
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ if (!geometry_set.has_curves()) {
params.set_default_remaining_outputs();
return;
}
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*component->get_for_read());
-
- if (curve->splines().is_empty()) {
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
+ if (curves.points_num() == 0) {
params.set_default_remaining_outputs();
return;
}
- Array<float> spline_lengths = curve->accumulated_spline_lengths();
- const float total_length = spline_lengths.last();
+ Array<float> curve_lengths = curve_accumulated_lengths(curves);
+ const float total_length = curve_lengths.last();
if (total_length == 0.0f) {
params.set_default_remaining_outputs();
return;
}
- Field<float> length_field = get_length_input_field(params, total_length);
+ const NodeGeometryCurveSample &storage = node_storage(params.node());
+ const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
+ Field<float> length_field = get_length_input_field(params, mode, total_length);
+
+ auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set));
- auto sample_fn = std::make_unique<SampleCurveFunction>(std::move(geometry_set),
- std::move(spline_lengths));
- auto sample_op = std::make_shared<FieldOperation>(
- FieldOperation(std::move(sample_fn), {length_field}));
+ std::shared_ptr<FieldOperation> sample_op;
+ if (curves.curves_num() == 1) {
+ sample_op = FieldOperation::Create(std::move(sample_fn),
+ {fn::make_constant_field<int>(0), std::move(length_field)});
+ }
+ else {
+ auto index_fn = std::make_unique<SampleFloatSegmentsFunction>(std::move(curve_lengths));
+ auto index_op = FieldOperation::Create(std::move(index_fn), {std::move(length_field)});
+ sample_op = FieldOperation::Create(std::move(sample_fn),
+ {Field<int>(index_op, 0), Field<float>(index_op, 1)});
+ }
params.set_output("Position", Field<float3>(sample_op, 0));
params.set_output("Tangent", Field<float3>(sample_op, 1));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
index f7ba724c377..8bb24821064 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handle_type.cc
@@ -51,15 +51,12 @@ static HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type)
return BEZIER_HANDLE_AUTO;
}
-static void set_type_in_component(CurveComponent &component,
- const GeometryNodeCurveHandleMode mode,
- const HandleType new_handle_type,
- const Field<bool> &selection_field)
+static void set_handle_type(bke::CurvesGeometry &curves,
+ const GeometryNodeCurveHandleMode mode,
+ const HandleType new_handle_type,
+ const Field<bool> &selection_field)
{
- Curves &curves_id = *component.get_for_write();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -93,21 +90,17 @@ static void node_geo_exec(GeoNodeExecParams params)
std::atomic<bool> has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- return;
- }
- has_curves = true;
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- const AttributeAccessor attributes = *component.attributes();
- if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) {
- return;
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ has_curves = true;
+ const AttributeAccessor attributes = curves.attributes();
+ if (!attributes.contains("handle_type_left") || !attributes.contains("handle_type_right")) {
+ return;
+ }
+ has_bezier = true;
+
+ set_handle_type(curves, mode, new_handle_type, selection_field);
}
- has_bezier = true;
-
- set_type_in_component(geometry_set.get_component_for_write<CurveComponent>(),
- mode,
- new_handle_type,
- selection_field);
});
if (has_curves && !has_bezier) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
index b98541e3446..b5d8d1f020a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_parameter.cc
@@ -143,8 +143,8 @@ static VArray<float> construct_curve_parameter_varray(const bke::CurvesGeometry
Array<float> lengths = accumulated_lengths_curve_domain(curves);
const int last_index = curves.curves_num() - 1;
- const int total_length = lengths.last() + curves.evaluated_length_total_for_curve(
- last_index, cyclic[last_index]);
+ const float total_length = lengths.last() + curves.evaluated_length_total_for_curve(
+ last_index, cyclic[last_index]);
if (total_length > 0.0f) {
const float factor = 1.0f / total_length;
for (float &value : lengths) {
@@ -203,26 +203,18 @@ static VArray<int> construct_index_on_spline_varray(const bke::CurvesGeometry &c
return {};
}
-class CurveParameterFieldInput final : public GeometryFieldInput {
+class CurveParameterFieldInput final : public bke::CurvesFieldInput {
public:
- CurveParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Parameter node")
+ CurveParameterFieldInput() : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Parameter node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_curve_parameter_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_curve_parameter_varray(curves, mask, domain);
}
uint64_t hash() const override
@@ -237,26 +229,19 @@ class CurveParameterFieldInput final : public GeometryFieldInput {
}
};
-class CurveLengthParameterFieldInput final : public GeometryFieldInput {
+class CurveLengthParameterFieldInput final : public bke::CurvesFieldInput {
public:
- CurveLengthParameterFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Curve Length node")
+ CurveLengthParameterFieldInput()
+ : bke::CurvesFieldInput(CPPType::get<float>(), "Curve Length node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_curve_length_parameter_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_curve_length_parameter_varray(curves, mask, domain);
}
uint64_t hash() const override
@@ -271,26 +256,18 @@ class CurveLengthParameterFieldInput final : public GeometryFieldInput {
}
};
-class IndexOnSplineFieldInput final : public GeometryFieldInput {
+class IndexOnSplineFieldInput final : public bke::CurvesFieldInput {
public:
- IndexOnSplineFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Index")
+ IndexOnSplineFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Index")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask mask) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- if (curve_component.has_curves()) {
- const Curves &curves_id = *curve_component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- return construct_index_on_spline_varray(curves, mask, domain);
- }
- }
- return {};
+ return construct_index_on_spline_varray(curves, mask, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
index 183c98e9c9f..4d7e5f13969 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc
@@ -45,14 +45,13 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_curves()) {
return;
}
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &src_curves_id = *src_component.get_for_read();
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
if (src_curves.is_single_type(dst_type)) {
return;
}
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -61,15 +60,18 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- if (geometry::try_curves_conversion_in_place(selection, dst_type, [&]() -> Curves & {
- return *geometry_set.get_curves_for_write();
- })) {
+ if (geometry::try_curves_conversion_in_place(
+ selection, dst_type, [&]() -> bke::CurvesGeometry & {
+ return bke::CurvesGeometry::wrap(geometry_set.get_curves_for_write()->geometry);
+ })) {
return;
}
- Curves *dst_curves = geometry::convert_curves(src_component, src_curves, selection, dst_type);
+ bke::CurvesGeometry dst_curves = geometry::convert_curves(src_curves, selection, dst_type);
- geometry_set.replace_curves(dst_curves);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
});
params.set_output("Curve", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
index 864e6289135..919d0056bca 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc
@@ -34,12 +34,11 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(
- component.get_for_read()->geometry);
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- fn::FieldEvaluator evaluator{field_context, curves.points_num()};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, src_curves.points_num()};
evaluator.add(cuts_field);
evaluator.evaluate();
const VArray<int> cuts = evaluator.get_evaluated<int>(0);
@@ -47,9 +46,12 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- Curves *result = geometry::subdivide_curves(component, curves, curves.curves_range(), cuts);
+ bke::CurvesGeometry dst_curves = geometry::subdivide_curves(
+ src_curves, src_curves.curves_range(), cuts);
- geometry_set.replace_curves(result);
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
});
params.set_output("Curve", geometry_set);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
index 903a5e7c1d7..bc319ce905a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc
@@ -28,9 +28,10 @@ static void geometry_set_curve_to_mesh(GeometrySet &geometry_set,
const bool fill_caps)
{
const Curves &curves = *geometry_set.get_curves_for_read();
-
const Curves *profile_curves = profile_set.get_curves_for_read();
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
+
if (profile_curves == nullptr) {
Mesh *mesh = bke::curve_to_wire_mesh(bke::CurvesGeometry::wrap(curves.geometry));
geometry_set.replace_mesh(mesh);
@@ -55,7 +56,7 @@ static void node_geo_exec(GeoNodeExecParams params)
has_curves = true;
geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps);
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
params.set_output("Mesh", std::move(curve_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
index 6c4fb2e0855..799a65ec3d1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc
@@ -4,8 +4,11 @@
#include "BLI_task.hh"
#include "BLI_timeit.hh"
+#include "DNA_pointcloud_types.h"
+
#include "BKE_pointcloud.h"
-#include "BKE_spline.hh"
+
+#include "GEO_resample_curves.hh"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -62,9 +65,9 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH);
}
-static void curve_create_default_rotation_attribute(Span<float3> tangents,
- Span<float3> normals,
- MutableSpan<float3> rotations)
+static void fill_rotation_attribute(const Span<float3> tangents,
+ const Span<float3> normals,
+ MutableSpan<float3> rotations)
{
threading::parallel_for(IndexRange(rotations.size()), 512, [&](IndexRange range) {
for (const int i : range) {
@@ -74,308 +77,128 @@ static void curve_create_default_rotation_attribute(Span<float3> tangents,
});
}
-static Array<int> calculate_spline_point_offsets(GeoNodeExecParams &params,
- const GeometryNodeCurveResampleMode mode,
- const CurveEval &curve,
- const Span<SplinePtr> splines)
+static PointCloud *pointcloud_from_curves(bke::CurvesGeometry curves,
+ const AttributeIDRef &tangent_id,
+ const AttributeIDRef &normal_id,
+ const AttributeIDRef &rotation_id)
{
- const int size = curve.splines().size();
- switch (mode) {
- case GEO_NODE_CURVE_RESAMPLE_COUNT: {
- const int count = params.get_input<int>("Count");
- if (count < 1) {
- return {0};
- }
- Array<int> offsets(size + 1);
- int offset = 0;
- for (const int i : IndexRange(size)) {
- offsets[i] = offset;
- if (splines[i]->evaluated_points_num() > 0) {
- offset += count;
- }
- }
- offsets.last() = offset;
- return offsets;
- }
- case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
- /* Don't allow asymptotic count increase for low resolution values. */
- const float resolution = std::max(params.get_input<float>("Length"), 0.0001f);
- Array<int> offsets(size + 1);
- int offset = 0;
- for (const int i : IndexRange(size)) {
- offsets[i] = offset;
- if (splines[i]->evaluated_points_num() > 0) {
- offset += splines[i]->length() / resolution + 1;
- }
- }
- offsets.last() = offset;
- return offsets;
- }
- case GEO_NODE_CURVE_RESAMPLE_EVALUATED: {
- return curve.evaluated_point_offsets();
- }
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(0);
+ pointcloud->totpoint = curves.points_num();
+
+ if (rotation_id) {
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
+ const VArraySpan<float3> tangents = attributes.lookup<float3>(tangent_id, ATTR_DOMAIN_POINT);
+ const VArraySpan<float3> normals = attributes.lookup<float3>(normal_id, ATTR_DOMAIN_POINT);
+ SpanAttributeWriter<float3> rotations = attributes.lookup_or_add_for_write_only_span<float3>(
+ rotation_id, ATTR_DOMAIN_POINT);
+ fill_rotation_attribute(tangents, normals, rotations.span);
+ rotations.finish();
}
- BLI_assert_unreachable();
- return {0};
-}
-/**
- * \note Relies on the fact that all attributes on point clouds are stored contiguously.
- */
-static GMutableSpan ensure_point_attribute(PointCloudComponent &points,
- const AttributeIDRef &attribute_id,
- const eCustomDataType data_type)
-{
- return points.attributes_for_write()
- ->lookup_or_add_for_write(attribute_id, ATTR_DOMAIN_POINT, data_type)
- .varray.get_internal_span();
-}
+ /* Move the curve point custom data to the pointcloud, to avoid any copying. */
+ CustomData_free(&pointcloud->pdata, pointcloud->totpoint);
+ pointcloud->pdata = curves.point_data;
+ CustomData_reset(&curves.point_data);
-template<typename T>
-static MutableSpan<T> ensure_point_attribute(PointCloudComponent &points,
- const AttributeIDRef &attribute_id)
-{
- return points.attributes_for_write()
- ->lookup_or_add_for_write<T>(attribute_id, ATTR_DOMAIN_POINT)
- .varray.get_internal_span();
+ return pointcloud;
}
-namespace {
-struct AnonymousAttributeIDs {
- StrongAnonymousAttributeID tangent_id;
- StrongAnonymousAttributeID normal_id;
- StrongAnonymousAttributeID rotation_id;
-};
-
-struct ResultAttributes {
- MutableSpan<float3> positions;
- MutableSpan<float> radii;
-
- Map<AttributeIDRef, GMutableSpan> point_attributes;
-
- MutableSpan<float3> tangents;
- MutableSpan<float3> normals;
- MutableSpan<float3> rotations;
-};
-} // namespace
-
-static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points,
- const CurveEval &curve,
- const AnonymousAttributeIDs &attributes)
+static void node_geo_exec(GeoNodeExecParams params)
{
- ResultAttributes outputs;
+ const NodeGeometryCurveToPoints &storage = node_storage(params.node());
+ const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode;
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
- outputs.positions = ensure_point_attribute<float3>(points, "position");
- outputs.radii = ensure_point_attribute<float>(points, "radius");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
- if (attributes.tangent_id) {
- outputs.tangents = ensure_point_attribute<float3>(points, attributes.tangent_id.get());
+ StrongAnonymousAttributeID tangent_anonymous_id;
+ StrongAnonymousAttributeID normal_anonymous_id;
+ StrongAnonymousAttributeID rotation_anonymous_id;
+ const bool rotation_required = params.output_is_required("Rotation");
+ if (params.output_is_required("Tangent") || rotation_required) {
+ tangent_anonymous_id = StrongAnonymousAttributeID("Tangent");
}
- if (attributes.normal_id) {
- outputs.normals = ensure_point_attribute<float3>(points, attributes.normal_id.get());
+ if (params.output_is_required("Normal") || rotation_required) {
+ normal_anonymous_id = StrongAnonymousAttributeID("Normal");
}
- if (attributes.rotation_id) {
- outputs.rotations = ensure_point_attribute<float3>(points, attributes.rotation_id.get());
+ if (rotation_required) {
+ rotation_anonymous_id = StrongAnonymousAttributeID("Rotation");
}
- /* Because of the invariants of the curve component, we use the attributes of the first spline
- * as a representative for the attribute meta data all splines. Attributes from the spline domain
- * are handled separately. */
- curve.splines().first()->attributes.foreach_attribute(
- [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
- if (id.should_be_kept()) {
- outputs.point_attributes.add_new(
- id, ensure_point_attribute(points, id, meta_data.data_type));
- }
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- return outputs;
-}
-
-/**
- * TODO: For non-poly splines, this has double copies that could be avoided as part
- * of a general look at optimizing uses of #Spline::interpolate_to_evaluated.
- */
-static void copy_evaluated_point_attributes(const Span<SplinePtr> splines,
- const Span<int> offsets,
- ResultAttributes &data)
-{
- threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
- for (const int i : range) {
- const Spline &spline = *splines[i];
- const int offset = offsets[i];
- const int size = offsets[i + 1] - offsets[i];
-
- data.positions.slice(offset, size).copy_from(spline.evaluated_positions());
- spline.interpolate_to_evaluated(spline.radii()).materialize(data.radii.slice(offset, size));
-
- for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
- const AttributeIDRef attribute_id = item.key;
- const GMutableSpan dst = item.value;
-
- BLI_assert(spline.attributes.get_for_read(attribute_id));
- GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
-
- spline.interpolate_to_evaluated(spline_span).materialize(dst.slice(offset, size).data());
- }
-
- if (!data.tangents.is_empty()) {
- data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents());
- }
- if (!data.normals.is_empty()) {
- data.normals.slice(offset, size).copy_from(spline.evaluated_normals());
- }
- }
- });
-}
-
-static void copy_uniform_sample_point_attributes(const Span<SplinePtr> splines,
- const Span<int> offsets,
- ResultAttributes &data)
-{
- threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) {
- for (const int i : range) {
- const Spline &spline = *splines[i];
- const int offset = offsets[i];
- const int num = offsets[i + 1] - offsets[i];
- if (num == 0) {
- continue;
- }
-
- const Array<float> uniform_samples = spline.sample_uniform_index_factors(num);
-
- spline.sample_with_index_factors<float3>(
- spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, num));
- spline.sample_with_index_factors<float>(spline.interpolate_to_evaluated(spline.radii()),
- uniform_samples,
- data.radii.slice(offset, num));
-
- for (const Map<AttributeIDRef, GMutableSpan>::Item item : data.point_attributes.items()) {
- const AttributeIDRef attribute_id = item.key;
- const GMutableSpan dst = item.value;
-
- BLI_assert(spline.attributes.get_for_read(attribute_id));
- GSpan spline_span = *spline.attributes.get_for_read(attribute_id);
+ geometry::ResampleCurvesOutputAttributeIDs resample_attributes;
+ resample_attributes.tangent_id = tangent_anonymous_id.get();
+ resample_attributes.normal_id = normal_anonymous_id.get();
- spline.sample_with_index_factors(
- spline.interpolate_to_evaluated(spline_span), uniform_samples, dst.slice(offset, num));
- }
-
- if (!data.tangents.is_empty()) {
- Span<float3> src_tangents = spline.evaluated_tangents();
- MutableSpan<float3> sampled_tangents = data.tangents.slice(offset, num);
- spline.sample_with_index_factors<float3>(src_tangents, uniform_samples, sampled_tangents);
- for (float3 &vector : sampled_tangents) {
- vector = math::normalize(vector);
- }
- }
-
- if (!data.normals.is_empty()) {
- Span<float3> src_normals = spline.evaluated_normals();
- MutableSpan<float3> sampled_normals = data.normals.slice(offset, num);
- spline.sample_with_index_factors<float3>(src_normals, uniform_samples, sampled_normals);
- for (float3 &vector : sampled_normals) {
- vector = math::normalize(vector);
+ switch (mode) {
+ case GEO_NODE_CURVE_RESAMPLE_COUNT: {
+ Field<int> count = params.extract_input<Field<int>>("Count");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_count(
+ src_curves, fn::make_constant_field<bool>(true), count, resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
}
- }
+ });
+ break;
}
- });
-}
-
-static void copy_spline_domain_attributes(const CurveEval &curve,
- const Span<int> offsets,
- PointCloudComponent &points)
-{
- curve.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id);
- const CPPType &type = curve_attribute.type();
- const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type);
-
- for (const int i : curve.splines().index_range()) {
- const int offset = offsets[i];
- const int num = offsets[i + 1] - offsets[i];
- type.fill_assign_n(curve_attribute[i], dst[offset], num);
+ case GEO_NODE_CURVE_RESAMPLE_LENGTH: {
+ Field<float> length = params.extract_input<Field<float>>("Length");
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_length(
+ src_curves, fn::make_constant_field<bool>(true), length, resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
}
-
- return true;
- },
- ATTR_DOMAIN_CURVE);
-}
-
-static void node_geo_exec(GeoNodeExecParams params)
-{
- const NodeGeometryCurveToPoints &storage = node_storage(params.node());
- const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)storage.mode;
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
-
- AnonymousAttributeIDs attribute_outputs;
- attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent");
- attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal");
- attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
-
- geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
- return;
+ });
+ break;
}
- const std::unique_ptr<CurveEval> curve = curves_to_curve_eval(
- *geometry_set.get_curves_for_read());
- const Span<SplinePtr> splines = curve->splines();
- curve->assert_valid_point_attributes();
-
- const Array<int> offsets = calculate_spline_point_offsets(params, mode, *curve, splines);
- const int total_num = offsets.last();
- if (total_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
- return;
- }
-
- geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_num));
- PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
- ResultAttributes point_attributes = create_attributes_for_transfer(
- points, *curve, attribute_outputs);
-
- switch (mode) {
- case GEO_NODE_CURVE_RESAMPLE_COUNT:
- case GEO_NODE_CURVE_RESAMPLE_LENGTH:
- copy_uniform_sample_point_attributes(splines, offsets, point_attributes);
- break;
- case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
- copy_evaluated_point_attributes(splines, offsets, point_attributes);
- break;
- }
-
- copy_spline_domain_attributes(*curve, offsets, points);
-
- if (!point_attributes.rotations.is_empty()) {
- curve_create_default_rotation_attribute(
- point_attributes.tangents, point_attributes.normals, point_attributes.rotations);
- }
-
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES, GEO_COMPONENT_TYPE_POINT_CLOUD});
- });
+ case GEO_NODE_CURVE_RESAMPLE_EVALUATED:
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry) {
+ if (const Curves *src_curves_id = geometry.get_curves_for_read()) {
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(
+ src_curves_id->geometry);
+ bke::CurvesGeometry dst_curves = geometry::resample_to_evaluated(
+ src_curves, fn::make_constant_field<bool>(true), resample_attributes);
+ PointCloud *pointcloud = pointcloud_from_curves(std::move(dst_curves),
+ resample_attributes.tangent_id,
+ resample_attributes.normal_id,
+ rotation_anonymous_id.get());
+ geometry.remove_geometry_during_modify();
+ geometry.replace_pointcloud(pointcloud);
+ }
+ });
+ break;
+ }
params.set_output("Points", std::move(geometry_set));
- if (attribute_outputs.tangent_id) {
- params.set_output(
- "Tangent",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.tangent_id),
- params.attribute_producer_name()));
+ if (tangent_anonymous_id) {
+ params.set_output("Tangent",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(tangent_anonymous_id), params.attribute_producer_name()));
}
- if (attribute_outputs.normal_id) {
- params.set_output(
- "Normal",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id),
- params.attribute_producer_name()));
+ if (normal_anonymous_id) {
+ params.set_output("Normal",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(normal_anonymous_id), params.attribute_producer_name()));
}
- if (attribute_outputs.rotation_id) {
- params.set_output(
- "Rotation",
- AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id),
- params.attribute_producer_name()));
+ if (rotation_anonymous_id) {
+ params.set_output("Rotation",
+ AnonymousAttributeFieldInput::Create<float3>(
+ std::move(rotation_anonymous_id), params.attribute_producer_name()));
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
index 00a79168087..1576b573058 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
-#include "BKE_spline.hh"
+#include "BKE_curves.hh"
#include "BLI_task.hh"
#include "UI_interface.h"
@@ -8,12 +8,12 @@
#include "NOD_socket_search_link.hh"
+#include "GEO_trim_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_curve_trim_cc {
-using blender::attribute_math::mix2;
-
NODE_STORAGE_FUNCS(NodeGeometryCurveTrim)
static void node_declare(NodeDeclarationBuilder &b)
@@ -107,394 +107,6 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
}
}
-struct TrimLocation {
- /* Control point index at the start side of the trim location. */
- int left_index;
- /* Control point index at the end of the trim location's segment. */
- int right_index;
- /* The factor between the left and right indices. */
- float factor;
-};
-
-template<typename T>
-static void shift_slice_to_start(MutableSpan<T> data, const int start_index, const int num)
-{
- BLI_assert(start_index + num - 1 <= data.size());
- memmove(data.data(), &data[start_index], sizeof(T) * num);
-}
-
-/* Shift slice to start of span and modifies start and end data. */
-template<typename T>
-static void linear_trim_data(const TrimLocation &start,
- const TrimLocation &end,
- MutableSpan<T> data)
-{
- const int num = end.right_index - start.left_index + 1;
-
- if (start.left_index > 0) {
- shift_slice_to_start<T>(data, start.left_index, num);
- }
-
- const T start_data = mix2<T>(start.factor, data.first(), data[1]);
- const T end_data = mix2<T>(end.factor, data[num - 2], data[num - 1]);
-
- data.first() = start_data;
- data[num - 1] = end_data;
-}
-
-/**
- * Identical operation as #linear_trim_data, but copy data to a new #MutableSpan rather than
- * modifying the original data.
- */
-template<typename T>
-static void linear_trim_to_output_data(const TrimLocation &start,
- const TrimLocation &end,
- Span<T> src,
- MutableSpan<T> dst)
-{
- const int num = end.right_index - start.left_index + 1;
-
- const T start_data = mix2<T>(start.factor, src[start.left_index], src[start.right_index]);
- const T end_data = mix2<T>(end.factor, src[end.left_index], src[end.right_index]);
-
- dst.copy_from(src.slice(start.left_index, num));
- dst.first() = start_data;
- dst.last() = end_data;
-}
-
-/* Look up the control points to the left and right of factor, and get the factor between them. */
-static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup,
- const BezierSpline &spline)
-{
- Span<int> offsets = spline.control_point_offsets();
-
- const int *offset = std::lower_bound(offsets.begin(), offsets.end(), lookup.evaluated_index);
- const int index = offset - offsets.begin();
-
- const int left = offsets[index] > lookup.evaluated_index ? index - 1 : index;
- const int right = left == (spline.size() - 1) ? 0 : left + 1;
-
- const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left];
- const int segment_eval_num = offsets[left + 1] - offsets[left];
- const float factor = std::clamp(offset_in_segment / segment_eval_num, 0.0f, 1.0f);
-
- return {left, right, factor};
-}
-
-static void trim_poly_spline(Spline &spline,
- const Spline::LookupResult &start_lookup,
- const Spline::LookupResult &end_lookup)
-{
- /* Poly splines have a 1 to 1 mapping between control points and evaluated points. */
- const TrimLocation start = {
- start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor};
- const TrimLocation end = {
- end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor};
-
- const int num = end.right_index - start.left_index + 1;
-
- linear_trim_data<float3>(start, end, spline.positions());
- linear_trim_data<float>(start, end, spline.radii());
- linear_trim_data<float>(start, end, spline.tilts());
-
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
- std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id);
- BLI_assert(src);
- attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
- using T = decltype(dummy);
- linear_trim_data<T>(start, end, src->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- spline.resize(num);
-}
-
-/**
- * Trim NURB splines by converting to a poly spline.
- */
-static PolySpline trim_nurbs_spline(const Spline &spline,
- const Spline::LookupResult &start_lookup,
- const Spline::LookupResult &end_lookup)
-{
- /* Since this outputs a poly spline, the evaluated indices are the control point indices. */
- const TrimLocation start = {
- start_lookup.evaluated_index, start_lookup.next_evaluated_index, start_lookup.factor};
- const TrimLocation end = {
- end_lookup.evaluated_index, end_lookup.next_evaluated_index, end_lookup.factor};
-
- const int num = end.right_index - start.left_index + 1;
-
- /* Create poly spline and copy trimmed data to it. */
- PolySpline new_spline;
- new_spline.resize(num);
-
- /* Copy generic attribute data. */
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id);
- BLI_assert(src);
- if (!new_spline.attributes.create(attribute_id, meta_data.data_type)) {
- BLI_assert_unreachable();
- return false;
- }
- std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id);
- BLI_assert(dst);
-
- attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
- using T = decltype(dummy);
- VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>());
- linear_trim_to_output_data<T>(
- start, end, eval_data.get_internal_span(), dst->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- linear_trim_to_output_data<float3>(
- start, end, spline.evaluated_positions(), new_spline.positions());
-
- VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii());
- linear_trim_to_output_data<float>(
- start, end, evaluated_radii.get_internal_span(), new_spline.radii());
-
- VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts());
- linear_trim_to_output_data<float>(
- start, end, evaluated_tilts.get_internal_span(), new_spline.tilts());
-
- return new_spline;
-}
-
-/**
- * Trim Bezier splines by adjusting the first and last handles
- * and control points to maintain the original shape.
- */
-static void trim_bezier_spline(Spline &spline,
- const Spline::LookupResult &start_lookup,
- const Spline::LookupResult &end_lookup)
-{
- BezierSpline &bezier_spline = static_cast<BezierSpline &>(spline);
-
- const TrimLocation start = lookup_control_point_position(start_lookup, bezier_spline);
- TrimLocation end = lookup_control_point_position(end_lookup, bezier_spline);
-
- const Span<int> control_offsets = bezier_spline.control_point_offsets();
-
- /* The number of control points in the resulting spline. */
- const int num = end.right_index - start.left_index + 1;
-
- /* Trim the spline attributes. Done before end.factor recalculation as it needs
- * the original end.factor value. */
- linear_trim_data<float>(start, end, bezier_spline.radii());
- linear_trim_data<float>(start, end, bezier_spline.tilts());
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
- std::optional<GMutableSpan> src = spline.attributes.get_for_write(attribute_id);
- BLI_assert(src);
- attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
- using T = decltype(dummy);
- linear_trim_data<T>(start, end, src->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- /* Recalculate end.factor if the `num` is two, because the adjustment in the
- * position of the control point of the spline to the left of the new end point will change the
- * factor between them. */
- if (num == 2) {
- if (start_lookup.factor == 1.0f) {
- end.factor = 0.0f;
- }
- else {
- end.factor = (end_lookup.evaluated_index + end_lookup.factor -
- (start_lookup.evaluated_index + start_lookup.factor)) /
- (control_offsets[end.right_index] -
- (start_lookup.evaluated_index + start_lookup.factor));
- end.factor = std::clamp(end.factor, 0.0f, 1.0f);
- }
- }
-
- BezierSpline::InsertResult start_point = bezier_spline.calculate_segment_insertion(
- start.left_index, start.right_index, start.factor);
-
- /* Update the start control point parameters so they are used calculating the new end point. */
- bezier_spline.positions()[start.left_index] = start_point.position;
- bezier_spline.handle_positions_right()[start.left_index] = start_point.right_handle;
- bezier_spline.handle_positions_left()[start.right_index] = start_point.handle_next;
-
- const BezierSpline::InsertResult end_point = bezier_spline.calculate_segment_insertion(
- end.left_index, end.right_index, end.factor);
-
- /* If `num` is two, then the start point right handle needs to change to reflect the end point
- * previous handle update. */
- if (num == 2) {
- start_point.right_handle = end_point.handle_prev;
- }
-
- /* Shift control point position data to start at beginning of array. */
- if (start.left_index > 0) {
- shift_slice_to_start(bezier_spline.positions(), start.left_index, num);
- shift_slice_to_start(bezier_spline.handle_positions_left(), start.left_index, num);
- shift_slice_to_start(bezier_spline.handle_positions_right(), start.left_index, num);
- }
-
- bezier_spline.positions().first() = start_point.position;
- bezier_spline.positions()[num - 1] = end_point.position;
-
- bezier_spline.handle_positions_left().first() = start_point.left_handle;
- bezier_spline.handle_positions_left()[num - 1] = end_point.left_handle;
-
- bezier_spline.handle_positions_right().first() = start_point.right_handle;
- bezier_spline.handle_positions_right()[num - 1] = end_point.right_handle;
-
- /* If there is at least one control point between the endpoints, update the control
- * point handle to the right of the start point and to the left of the end point. */
- if (num > 2) {
- bezier_spline.handle_positions_left()[start.right_index - start.left_index] =
- start_point.handle_next;
- bezier_spline.handle_positions_right()[end.left_index - start.left_index] =
- end_point.handle_prev;
- }
-
- bezier_spline.resize(num);
-}
-
-static void trim_spline(SplinePtr &spline,
- const Spline::LookupResult start,
- const Spline::LookupResult end)
-{
- switch (spline->type()) {
- case CURVE_TYPE_BEZIER:
- trim_bezier_spline(*spline, start, end);
- break;
- case CURVE_TYPE_POLY:
- trim_poly_spline(*spline, start, end);
- break;
- case CURVE_TYPE_NURBS:
- spline = std::make_unique<PolySpline>(trim_nurbs_spline(*spline, start, end));
- break;
- case CURVE_TYPE_CATMULL_ROM:
- BLI_assert_unreachable();
- spline = {};
- }
- spline->mark_cache_invalid();
-}
-
-template<typename T>
-static void to_single_point_data(const TrimLocation &trim, MutableSpan<T> data)
-{
- data.first() = mix2<T>(trim.factor, data[trim.left_index], data[trim.right_index]);
-}
-template<typename T>
-static void to_single_point_data(const TrimLocation &trim, Span<T> src, MutableSpan<T> dst)
-{
- dst.first() = mix2<T>(trim.factor, src[trim.left_index], src[trim.right_index]);
-}
-
-static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &lookup)
-{
- BezierSpline &bezier = static_cast<BezierSpline &>(spline);
-
- const TrimLocation trim = lookup_control_point_position(lookup, bezier);
-
- const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion(
- trim.left_index, trim.right_index, trim.factor);
- bezier.positions().first() = new_point.position;
- bezier.handle_types_left().first() = BEZIER_HANDLE_FREE;
- bezier.handle_types_right().first() = BEZIER_HANDLE_FREE;
- bezier.handle_positions_left().first() = new_point.left_handle;
- bezier.handle_positions_right().first() = new_point.right_handle;
-
- to_single_point_data<float>(trim, bezier.radii());
- to_single_point_data<float>(trim, bezier.tilts());
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
- std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id);
- attribute_math::convert_to_static_type(data->type(), [&](auto dummy) {
- using T = decltype(dummy);
- to_single_point_data<T>(trim, data->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
- spline.resize(1);
-}
-
-static void to_single_point_poly(Spline &spline, const Spline::LookupResult &lookup)
-{
- const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor};
-
- to_single_point_data<float3>(trim, spline.positions());
- to_single_point_data<float>(trim, spline.radii());
- to_single_point_data<float>(trim, spline.tilts());
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) {
- std::optional<GMutableSpan> data = spline.attributes.get_for_write(attribute_id);
- attribute_math::convert_to_static_type(data->type(), [&](auto dummy) {
- using T = decltype(dummy);
- to_single_point_data<T>(trim, data->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
- spline.resize(1);
-}
-
-static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::LookupResult &lookup)
-{
- /* Since this outputs a poly spline, the evaluated indices are the control point indices. */
- const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor};
-
- /* Create poly spline and copy trimmed data to it. */
- PolySpline new_spline;
- new_spline.resize(1);
-
- spline.attributes.foreach_attribute(
- [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
- new_spline.attributes.create(attribute_id, meta_data.data_type);
- std::optional<GSpan> src = spline.attributes.get_for_read(attribute_id);
- std::optional<GMutableSpan> dst = new_spline.attributes.get_for_write(attribute_id);
- attribute_math::convert_to_static_type(src->type(), [&](auto dummy) {
- using T = decltype(dummy);
- VArray<T> eval_data = spline.interpolate_to_evaluated<T>(src->typed<T>());
- to_single_point_data<T>(trim, eval_data.get_internal_span(), dst->typed<T>());
- });
- return true;
- },
- ATTR_DOMAIN_POINT);
-
- to_single_point_data<float3>(trim, spline.evaluated_positions(), new_spline.positions());
-
- VArray<float> evaluated_radii = spline.interpolate_to_evaluated(spline.radii());
- to_single_point_data<float>(trim, evaluated_radii.get_internal_span(), new_spline.radii());
-
- VArray<float> evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts());
- to_single_point_data<float>(trim, evaluated_tilts.get_internal_span(), new_spline.tilts());
-
- return new_spline;
-}
-
-static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup)
-{
- switch (spline->type()) {
- case CURVE_TYPE_BEZIER:
- to_single_point_bezier(*spline, lookup);
- break;
- case CURVE_TYPE_POLY:
- to_single_point_poly(*spline, lookup);
- break;
- case CURVE_TYPE_NURBS:
- spline = std::make_unique<PolySpline>(to_single_point_nurbs(*spline, lookup));
- break;
- case CURVE_TYPE_CATMULL_ROM:
- BLI_assert_unreachable();
- spline = {};
- }
-}
-
static void geometry_set_curve_trim(GeometrySet &geometry_set,
const GeometryNodeCurveSampleMode mode,
Field<float> &start_field,
@@ -503,70 +115,52 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set,
if (!geometry_set.has_curves()) {
return;
}
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
+ if (src_curves.curves_num() == 0) {
+ return;
+ }
- CurveComponent &component = geometry_set.get_component_for_write<CurveComponent>();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
-
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, src_curves.curves_num()};
evaluator.add(start_field);
evaluator.add(end_field);
evaluator.evaluate();
const VArray<float> starts = evaluator.get_evaluated<float>(0);
const VArray<float> ends = evaluator.get_evaluated<float>(1);
- std::unique_ptr<CurveEval> curve = curves_to_curve_eval(*geometry_set.get_curves_for_read());
- MutableSpan<SplinePtr> splines = curve->splines();
-
- threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
- for (const int i : range) {
- SplinePtr &spline = splines[i];
-
- /* Currently trimming cyclic splines is not supported. It could be in the future though. */
- if (spline->is_cyclic()) {
- continue;
- }
-
- if (spline->evaluated_edges_num() == 0) {
- continue;
- }
-
- const float length = spline->length();
- if (length == 0.0f) {
- continue;
- }
-
- const float start = starts[i];
- const float end = ends[i];
-
- /* When the start and end samples are reversed, instead of implicitly reversing the spline
- * or switching the parameters, create a single point spline with the end sample point. */
- if (end <= start) {
- if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
- to_single_point_spline(spline,
- spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)));
- }
- else {
- to_single_point_spline(spline,
- spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)));
- }
- continue;
- }
-
- if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) {
- trim_spline(spline,
- spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)),
- spline->lookup_evaluated_length(std::clamp(end, 0.0f, length)));
- }
- else {
- trim_spline(spline,
- spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)),
- spline->lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)));
- }
+ const VArray<bool> cyclic = src_curves.cyclic();
+
+ /* If node length input is on form [0, 1] instead of [0, length]*/
+ const bool normalized_length_lookup = mode == GEO_NODE_CURVE_SAMPLE_FACTOR;
+
+ /* Stack start + end field. */
+ Vector<float> length_factors(src_curves.curves_num() * 2);
+ Vector<int64_t> lookup_indices(src_curves.curves_num() * 2);
+ threading::parallel_for(src_curves.curves_range(), 512, [&](IndexRange curve_range) {
+ for (const int64_t curve_i : curve_range) {
+ const bool negative_trim = !cyclic[curve_i] && starts[curve_i] > ends[curve_i];
+ length_factors[curve_i] = starts[curve_i];
+ length_factors[curve_i + src_curves.curves_num()] = negative_trim ? starts[curve_i] :
+ ends[curve_i];
+ lookup_indices[curve_i] = curve_i;
+ lookup_indices[curve_i + src_curves.curves_num()] = curve_i;
}
});
- geometry_set.replace_curves(curve_eval_to_curves(*curve));
+ /* Create curve trim lookup table. */
+ Array<bke::curves::CurvePoint, 12> point_lookups = geometry::lookup_curve_points(
+ src_curves, length_factors, lookup_indices, normalized_length_lookup);
+
+ bke::CurvesGeometry dst_curves = geometry::trim_curves(
+ src_curves,
+ src_curves.curves_range().as_span(),
+ point_lookups.as_span().slice(0, src_curves.curves_num()),
+ point_lookups.as_span().slice(src_curves.curves_num(), src_curves.curves_num()));
+
+ Curves *dst_curves_id = bke::curves_new_nomain(std::move(dst_curves));
+ bke::curves_copy_parameters(src_curves_id, *dst_curves_id);
+ geometry_set.replace_curves(dst_curves_id);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -575,6 +169,7 @@ static void node_geo_exec(GeoNodeExecParams params)
const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)storage.mode;
GeometrySet geometry_set = params.extract_input<GeometrySet>("Curve");
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) {
Field<float> start_field = params.extract_input<Field<float>>("Start");
diff --git a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
index ab7ddfa71f1..3f6155460ed 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_deform_curves_on_surface.cc
@@ -41,7 +41,7 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curves"));
}
-static void deform_curves(CurvesGeometry &curves,
+static void deform_curves(const CurvesGeometry &curves,
const Mesh &surface_mesh_old,
const Mesh &surface_mesh_new,
const Span<float2> curve_attachment_uvs,
@@ -51,6 +51,8 @@ static void deform_curves(CurvesGeometry &curves,
const Span<float3> corner_normals_new,
const Span<float3> rest_positions,
const float4x4 &surface_to_curves,
+ MutableSpan<float3> r_positions,
+ MutableSpan<float3x3> r_rotations,
std::atomic<int> &r_invalid_uv_count)
{
/* Find attachment points on old and new mesh. */
@@ -58,13 +60,18 @@ static void deform_curves(CurvesGeometry &curves,
Array<ReverseUVSampler::Result> surface_samples_old(curves_num);
Array<ReverseUVSampler::Result> surface_samples_new(curves_num);
threading::parallel_invoke(
+ 1024 < curves_num,
[&]() { reverse_uv_sampler_old.sample_many(curve_attachment_uvs, surface_samples_old); },
[&]() { reverse_uv_sampler_new.sample_many(curve_attachment_uvs, surface_samples_new); });
- MutableSpan<float3> positions = curves.positions_for_write();
-
const float4x4 curves_to_surface = surface_to_curves.inverted();
+ const Span<MVert> surface_verts_old = surface_mesh_old.verts();
+ const Span<MLoop> surface_loops_old = surface_mesh_old.loops();
+
+ const Span<MVert> surface_verts_new = surface_mesh_new.verts();
+ const Span<MLoop> surface_loops_new = surface_mesh_new.loops();
+
threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange range) {
for (const int curve_i : range) {
const ReverseUVSampler::Result &surface_sample_old = surface_samples_old[curve_i];
@@ -91,13 +98,13 @@ static void deform_curves(CurvesGeometry &curves,
const int corner_1_new = looptri_new.tri[1];
const int corner_2_new = looptri_new.tri[2];
- const int vert_0_old = surface_mesh_old.mloop[corner_0_old].v;
- const int vert_1_old = surface_mesh_old.mloop[corner_1_old].v;
- const int vert_2_old = surface_mesh_old.mloop[corner_2_old].v;
+ const int vert_0_old = surface_loops_old[corner_0_old].v;
+ const int vert_1_old = surface_loops_old[corner_1_old].v;
+ const int vert_2_old = surface_loops_old[corner_2_old].v;
- const int vert_0_new = surface_mesh_new.mloop[corner_0_new].v;
- const int vert_1_new = surface_mesh_new.mloop[corner_1_new].v;
- const int vert_2_new = surface_mesh_new.mloop[corner_2_new].v;
+ const int vert_0_new = surface_loops_new[corner_0_new].v;
+ const int vert_1_new = surface_loops_new[corner_1_new].v;
+ const int vert_2_new = surface_loops_new[corner_2_new].v;
const float3 &normal_0_old = corner_normals_old[corner_0_old];
const float3 &normal_1_old = corner_normals_old[corner_1_old];
@@ -111,14 +118,14 @@ static void deform_curves(CurvesGeometry &curves,
const float3 normal_new = math::normalize(
mix3(bary_weights_new, normal_0_new, normal_1_new, normal_2_new));
- const float3 &pos_0_old = surface_mesh_old.mvert[vert_0_old].co;
- const float3 &pos_1_old = surface_mesh_old.mvert[vert_1_old].co;
- const float3 &pos_2_old = surface_mesh_old.mvert[vert_2_old].co;
+ const float3 &pos_0_old = surface_verts_old[vert_0_old].co;
+ const float3 &pos_1_old = surface_verts_old[vert_1_old].co;
+ const float3 &pos_2_old = surface_verts_old[vert_2_old].co;
const float3 pos_old = mix3(bary_weights_old, pos_0_old, pos_1_old, pos_2_old);
- const float3 &pos_0_new = surface_mesh_new.mvert[vert_0_new].co;
- const float3 &pos_1_new = surface_mesh_new.mvert[vert_1_new].co;
- const float3 &pos_2_new = surface_mesh_new.mvert[vert_2_new].co;
+ const float3 &pos_0_new = surface_verts_new[vert_0_new].co;
+ const float3 &pos_1_new = surface_verts_new[vert_1_new].co;
+ const float3 &pos_2_new = surface_verts_new[vert_2_new].co;
const float3 pos_new = mix3(bary_weights_new, pos_0_new, pos_1_new, pos_2_new);
/* The translation is just the difference between the old and new position on the surface. */
@@ -136,7 +143,7 @@ static void deform_curves(CurvesGeometry &curves,
*
* TODO: Figure out if this can be smoothly interpolated across the surface as well.
* Currently, this is a source of discontinuity in the deformation, because the vector
- * changes intantly from one triangle to the next. */
+ * changes instantly from one triangle to the next. */
const float3 tangent_reference_dir_old = rest_pos_1 - rest_pos_0;
const float3 tangent_reference_dir_new = pos_1_new - pos_0_new;
@@ -190,9 +197,15 @@ static void deform_curves(CurvesGeometry &curves,
/* Actually transform all points. */
const IndexRange points = curves.points_for_curve(curve_i);
for (const int point_i : points) {
- const float3 old_point_pos = positions[point_i];
+ const float3 old_point_pos = r_positions[point_i];
const float3 new_point_pos = curve_transform * old_point_pos;
- positions[point_i] = new_point_pos;
+ r_positions[point_i] = new_point_pos;
+ }
+
+ if (!r_rotations.is_empty()) {
+ for (const int point_i : points) {
+ r_rotations[point_i] = rotation * r_rotations[point_i];
+ }
}
}
});
@@ -215,9 +228,15 @@ static void node_geo_exec(GeoNodeExecParams params)
const Object *self_ob_eval = params.self_object();
if (self_ob_eval == nullptr || self_ob_eval->type != OB_CURVES) {
pass_through_input();
+ params.error_message_add(NodeWarningType::Error, TIP_("Node only works for curves objects"));
return;
}
const Curves *self_curves_eval = static_cast<const Curves *>(self_ob_eval->data);
+ if (self_curves_eval->surface_uv_map == nullptr || self_curves_eval->surface_uv_map[0] == '\0') {
+ pass_through_input();
+ params.error_message_add(NodeWarningType::Error, TIP_("Surface UV map not defined"));
+ return;
+ }
/* Take surface information from self-object. */
Object *surface_ob_eval = self_curves_eval->surface;
const StringRefNull uv_map_name = self_curves_eval->surface_uv_map;
@@ -229,44 +248,37 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (surface_ob_eval == nullptr || surface_ob_eval->type != OB_MESH) {
pass_through_input();
- params.error_message_add(NodeWarningType::Error, "Curves not attached to a surface.");
+ params.error_message_add(NodeWarningType::Error, TIP_("Curves not attached to a surface"));
return;
}
Object *surface_ob_orig = DEG_get_original_object(surface_ob_eval);
Mesh &surface_object_data = *static_cast<Mesh *>(surface_ob_orig->data);
if (BMEditMesh *em = surface_object_data.edit_mesh) {
- surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, NULL, &surface_object_data);
+ surface_mesh_orig = BKE_mesh_from_bmesh_for_eval_nomain(em->bm, nullptr, &surface_object_data);
free_suface_mesh_orig = true;
}
else {
surface_mesh_orig = &surface_object_data;
}
- Mesh *surface_mesh_eval = BKE_modifier_get_evaluated_mesh_from_evaluated_object(surface_ob_eval,
- false);
+ Mesh *surface_mesh_eval = BKE_modifier_get_evaluated_mesh_from_evaluated_object(surface_ob_eval);
if (surface_mesh_eval == nullptr) {
pass_through_input();
- params.error_message_add(NodeWarningType::Error, "Surface has no mesh.");
+ params.error_message_add(NodeWarningType::Error, TIP_("Surface has no mesh"));
return;
}
BKE_mesh_wrapper_ensure_mdata(surface_mesh_eval);
- const AttributeAccessor mesh_attributes_eval = bke::mesh_attributes(*surface_mesh_eval);
- const AttributeAccessor mesh_attributes_orig = bke::mesh_attributes(*surface_mesh_orig);
+ const AttributeAccessor mesh_attributes_eval = surface_mesh_eval->attributes();
+ const AttributeAccessor mesh_attributes_orig = surface_mesh_orig->attributes();
Curves &curves_id = *curves_geometry.get_curves_for_write();
CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry);
- if (uv_map_name.is_empty()) {
- pass_through_input();
- const char *message = TIP_("Surface UV map not defined.");
- params.error_message_add(NodeWarningType::Error, message);
- return;
- }
if (!mesh_attributes_eval.contains(uv_map_name)) {
pass_through_input();
- char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: %s."),
+ char *message = BLI_sprintfN(TIP_("Evaluated surface missing UV map: \"%s\""),
uv_map_name.c_str());
params.error_message_add(NodeWarningType::Error, message);
MEM_freeN(message);
@@ -274,7 +286,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
if (!mesh_attributes_orig.contains(uv_map_name)) {
pass_through_input();
- char *message = BLI_sprintfN(TIP_("Original surface missing UV map: %s."),
+ char *message = BLI_sprintfN(TIP_("Original surface missing UV map: \"%s\""),
uv_map_name.c_str());
params.error_message_add(NodeWarningType::Error, message);
MEM_freeN(message);
@@ -283,13 +295,13 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!mesh_attributes_eval.contains(rest_position_name)) {
pass_through_input();
params.error_message_add(NodeWarningType::Error,
- TIP_("Evaluated surface missing attribute: rest_position."));
+ TIP_("Evaluated surface missing attribute: \"rest_position\""));
return;
}
- if (curves.surface_uv_coords().is_empty()) {
+ if (curves.surface_uv_coords().is_empty() && curves.curves_num() > 0) {
pass_through_input();
params.error_message_add(NodeWarningType::Error,
- TIP_("Curves are not attached to any UV map."));
+ TIP_("Curves are not attached to any UV map"));
return;
}
const VArraySpan<float2> uv_map_orig = mesh_attributes_orig.lookup<float2>(uv_map_name,
@@ -322,22 +334,72 @@ static void node_geo_exec(GeoNodeExecParams params)
const bke::CurvesSurfaceTransforms transforms{*self_ob_eval, surface_ob_eval};
- deform_curves(curves,
- *surface_mesh_orig,
- *surface_mesh_eval,
- surface_uv_coords,
- reverse_uv_sampler_orig,
- reverse_uv_sampler_eval,
- corner_normals_orig,
- corner_normals_eval,
- rest_positions,
- transforms.surface_to_curves,
- invalid_uv_count);
+ bke::CurvesEditHints *edit_hints = curves_geometry.get_curve_edit_hints_for_write();
+ MutableSpan<float3> edit_hint_positions;
+ MutableSpan<float3x3> edit_hint_rotations;
+ if (edit_hints != nullptr) {
+ if (edit_hints->positions.has_value()) {
+ edit_hint_positions = *edit_hints->positions;
+ }
+ if (!edit_hints->deform_mats.has_value()) {
+ edit_hints->deform_mats.emplace(edit_hints->curves_id_orig.geometry.point_num,
+ float3x3::identity());
+ edit_hints->deform_mats->fill(float3x3::identity());
+ }
+ edit_hint_rotations = *edit_hints->deform_mats;
+ }
+
+ if (edit_hint_positions.is_empty()) {
+ deform_curves(curves,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ curves.positions_for_write(),
+ edit_hint_rotations,
+ invalid_uv_count);
+ }
+ else {
+ /* First deform the actual curves in the input geometry. */
+ deform_curves(curves,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ curves.positions_for_write(),
+ {},
+ invalid_uv_count);
+ /* Then also deform edit curve information for use in sculpt mode. */
+ const CurvesGeometry &curves_orig = CurvesGeometry::wrap(edit_hints->curves_id_orig.geometry);
+ deform_curves(curves_orig,
+ *surface_mesh_orig,
+ *surface_mesh_eval,
+ surface_uv_coords,
+ reverse_uv_sampler_orig,
+ reverse_uv_sampler_eval,
+ corner_normals_orig,
+ corner_normals_eval,
+ rest_positions,
+ transforms.surface_to_curves,
+ edit_hint_positions,
+ edit_hint_rotations,
+ invalid_uv_count);
+ }
curves.tag_positions_changed();
if (invalid_uv_count) {
- char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves."),
+ char *message = BLI_sprintfN(TIP_("Invalid surface UVs on %d curves"),
invalid_uv_count.load());
params.error_message_add(NodeWarningType::Warning, message);
MEM_freeN(message);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
index 408596b65aa..851ca622d6b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc
@@ -7,6 +7,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
#include "BKE_curves.hh"
@@ -43,13 +44,13 @@ static void copy_data_based_on_map(Span<T> src, MutableSpan<T> dst, Span<int> in
* Copies the attributes with a domain in `domains` to `result_component`.
*/
static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &result_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const Span<eAttrDomain> domains)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader attribute = in_component.attributes()->lookup(attribute_id);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -60,9 +61,8 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- GSpanAttributeWriter result_attribute =
- result_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- attribute_id, attribute.domain, data_type);
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ attribute_id, attribute.domain, data_type);
if (!result_attribute) {
continue;
@@ -83,14 +83,14 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
* the mask to `result_component`.
*/
static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &result_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const eAttrDomain domain,
const IndexMask mask)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader attribute = in_component.attributes()->lookup(attribute_id);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -101,9 +101,8 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- GSpanAttributeWriter result_attribute =
- result_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- attribute_id, attribute.domain, data_type);
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ attribute_id, attribute.domain, data_type);
if (!result_attribute) {
continue;
@@ -120,14 +119,14 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
}
static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &result_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const eAttrDomain domain,
const Span<int> index_map)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader attribute = in_component.attributes()->lookup(attribute_id);
+ GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
continue;
}
@@ -138,9 +137,8 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
}
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray.type());
- GSpanAttributeWriter result_attribute =
- result_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- attribute_id, attribute.domain, data_type);
+ GSpanAttributeWriter result_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ attribute_id, attribute.domain, data_type);
if (!result_attribute) {
continue;
@@ -157,16 +155,17 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
}
static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind> &attributes,
- const GeometryComponent &in_component,
- GeometryComponent &out_component,
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes,
const int selected_loops_num,
const Span<int> selected_poly_indices,
const Mesh &mesh_in)
{
+ const Span<MPoly> polys = mesh_in.polys();
Vector<int64_t> indices;
indices.reserve(selected_loops_num);
for (const int src_poly_index : selected_poly_indices) {
- const MPoly &src_poly = mesh_in.mpoly[src_poly_index];
+ const MPoly &src_poly = polys[src_poly_index];
const int src_loop_start = src_poly.loopstart;
const int tot_loop = src_poly.totloop;
for (const int i : IndexRange(tot_loop)) {
@@ -174,42 +173,38 @@ static void copy_face_corner_attributes(const Map<AttributeIDRef, AttributeKind>
}
}
copy_attributes_based_on_mask(
- attributes, in_component, out_component, ATTR_DOMAIN_CORNER, IndexMask(indices));
+ attributes, src_attributes, dst_attributes, ATTR_DOMAIN_CORNER, IndexMask(indices));
}
-static void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh,
- Mesh &dst_mesh,
- Span<int> vertex_map)
+static void copy_masked_verts_to_new_mesh(const Mesh &src_mesh,
+ Mesh &dst_mesh,
+ Span<int> vertex_map)
{
BLI_assert(src_mesh.totvert == vertex_map.size());
+ const Span<MVert> src_verts = src_mesh.verts();
+ MutableSpan<MVert> dst_verts = dst_mesh.verts_for_write();
+
for (const int i_src : vertex_map.index_range()) {
const int i_dst = vertex_map[i_src];
if (i_dst == -1) {
continue;
}
-
- const MVert &v_src = src_mesh.mvert[i_src];
- MVert &v_dst = dst_mesh.mvert[i_dst];
-
- v_dst = v_src;
+ dst_verts[i_dst] = src_verts[i_src];
}
}
static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> edge_map)
{
BLI_assert(src_mesh.totedge == edge_map.size());
+ const Span<MEdge> src_edges = src_mesh.edges();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
+
for (const int i_src : IndexRange(src_mesh.totedge)) {
const int i_dst = edge_map[i_src];
if (ELEM(i_dst, -1, -2)) {
continue;
}
-
- const MEdge &e_src = src_mesh.medge[i_src];
- MEdge &e_dst = dst_mesh.medge[i_dst];
-
- e_dst = e_src;
- e_dst.v1 = e_src.v1;
- e_dst.v2 = e_src.v2;
+ dst_edges[i_dst] = src_edges[i_src];
}
}
@@ -220,14 +215,16 @@ static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh,
{
BLI_assert(src_mesh.totvert == vertex_map.size());
BLI_assert(src_mesh.totedge == edge_map.size());
+ const Span<MEdge> src_edges = src_mesh.edges();
+ MutableSpan<MEdge> dst_edges = dst_mesh.edges_for_write();
+
for (const int i_src : IndexRange(src_mesh.totedge)) {
const int i_dst = edge_map[i_src];
if (i_dst == -1) {
continue;
}
-
- const MEdge &e_src = src_mesh.medge[i_src];
- MEdge &e_dst = dst_mesh.medge[i_dst];
+ const MEdge &e_src = src_edges[i_src];
+ MEdge &e_dst = dst_edges[i_dst];
e_dst = e_src;
e_dst.v1 = vertex_map[e_src.v1];
@@ -242,16 +239,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
for (const int i_dst : masked_poly_indices.index_range()) {
const int i_src = masked_poly_indices[i_dst];
- const MPoly &mp_src = src_mesh.mpoly[i_src];
- MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[i_dst];
const int i_ml_src = mp_src.loopstart;
const int i_ml_dst = new_loop_starts[i_dst];
- const MLoop *ml_src = src_mesh.mloop + i_ml_src;
- MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -268,16 +270,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
for (const int i_dst : masked_poly_indices.index_range()) {
const int i_src = masked_poly_indices[i_dst];
- const MPoly &mp_src = src_mesh.mpoly[i_src];
- MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[i_dst];
const int i_ml_src = mp_src.loopstart;
const int i_ml_dst = new_loop_starts[i_dst];
- const MLoop *ml_src = src_mesh.mloop + i_ml_src;
- MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -295,16 +302,21 @@ static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh,
Span<int> masked_poly_indices,
Span<int> new_loop_starts)
{
+ const Span<MPoly> src_polys = src_mesh.polys();
+ const Span<MLoop> src_loops = src_mesh.loops();
+ MutableSpan<MPoly> dst_polys = dst_mesh.polys_for_write();
+ MutableSpan<MLoop> dst_loops = dst_mesh.loops_for_write();
+
for (const int i_dst : masked_poly_indices.index_range()) {
const int i_src = masked_poly_indices[i_dst];
- const MPoly &mp_src = src_mesh.mpoly[i_src];
- MPoly &mp_dst = dst_mesh.mpoly[i_dst];
+ const MPoly &mp_src = src_polys[i_src];
+ MPoly &mp_dst = dst_polys[i_dst];
const int i_ml_src = mp_src.loopstart;
const int i_ml_dst = new_loop_starts[i_dst];
- const MLoop *ml_src = src_mesh.mloop + i_ml_src;
- MLoop *ml_dst = dst_mesh.mloop + i_ml_dst;
+ const MLoop *ml_src = &src_loops[i_ml_src];
+ MLoop *ml_dst = &dst_loops[i_ml_dst];
mp_dst = mp_src;
mp_dst.loopstart = i_ml_dst;
@@ -319,18 +331,19 @@ static void delete_curves_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const eAttrDomain selection_domain)
{
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- GeometryComponentFieldContext field_context{src_component, selection_domain};
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
+ const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
- const int domain_num = src_component.attribute_domain_size(selection_domain);
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const int domain_size = src_curves.attributes().domain_size(selection_domain);
+ bke::CurvesFieldContext field_context{src_curves, selection_domain};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
if (selection.is_empty()) {
return;
}
- if (selection.size() == domain_num) {
+ if (selection.size() == domain_size) {
geometry_set.remove<CurveComponent>();
return;
}
@@ -350,11 +363,10 @@ static void delete_curves_selection(GeometrySet &geometry_set,
static void separate_point_cloud_selection(GeometrySet &geometry_set,
const Field<bool> &selection_field)
{
- const PointCloudComponent &src_points =
- *geometry_set.get_component_for_read<PointCloudComponent>();
- GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
+ const PointCloud &src_pointcloud = *geometry_set.get_pointcloud_for_read();
- fn::FieldEvaluator evaluator{field_context, src_points.attribute_domain_size(ATTR_DOMAIN_POINT)};
+ bke::PointCloudFieldContext field_context{src_pointcloud};
+ fn::FieldEvaluator evaluator{field_context, src_pointcloud.totpoint};
evaluator.set_selection(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
@@ -365,14 +377,15 @@ static void separate_point_cloud_selection(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
- PointCloudComponent dst_points;
- dst_points.replace(pointcloud, GeometryOwnershipType::Editable);
-
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
{GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
- copy_attributes_based_on_mask(attributes, src_points, dst_points, ATTR_DOMAIN_POINT, selection);
+ copy_attributes_based_on_mask(attributes,
+ src_pointcloud.attributes(),
+ pointcloud->attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection);
geometry_set.replace_pointcloud(pointcloud);
}
@@ -380,7 +393,7 @@ static void delete_selected_instances(GeometrySet &geometry_set,
const Field<bool> &selection_field)
{
InstancesComponent &instances = geometry_set.get_component_for_write<InstancesComponent>();
- GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
+ bke::GeometryFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
fn::FieldEvaluator evaluator{field_context, instances.instances_num()};
evaluator.set_selection(selection_field);
@@ -394,9 +407,9 @@ static void delete_selected_instances(GeometrySet &geometry_set,
instances.remove_instances(selection);
}
-static void compute_selected_vertices_from_vertex_selection(const Span<bool> vertex_selection,
- MutableSpan<int> r_vertex_map,
- int *r_selected_vertices_num)
+static void compute_selected_verts_from_vertex_selection(const Span<bool> vertex_selection,
+ MutableSpan<int> r_vertex_map,
+ int *r_selected_verts_num)
{
BLI_assert(vertex_selection.size() == r_vertex_map.size());
@@ -411,7 +424,7 @@ static void compute_selected_vertices_from_vertex_selection(const Span<bool> ver
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
}
static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
@@ -420,10 +433,11 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MEdge> edges = mesh.edges();
int selected_edges_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
/* Only add the edge if both vertices will be in the new mesh. */
if (vertex_selection[edge.v1] && vertex_selection[edge.v2]) {
@@ -438,25 +452,27 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh,
*r_selected_edges_num = selected_edges_num;
}
-static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
- const Span<bool> vertex_selection,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_vertex_selection(const Mesh &mesh,
+ const Span<bool> vertex_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
BLI_assert(mesh.totvert == vertex_selection.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[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) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
if (!vertex_selection[loop.v]) {
all_verts_in_selection = false;
break;
@@ -478,20 +494,20 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh,
* 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 Span<bool> edge_selection,
- MutableSpan<int> r_vertex_map,
- MutableSpan<int> r_edge_map,
- int *r_selected_vertices_num,
- int *r_selected_edges_num)
+static void compute_selected_verts_and_edges_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ MutableSpan<int> r_vertex_map,
+ MutableSpan<int> r_edge_map,
+ int *r_selected_verts_num,
+ int *r_selected_edges_num)
{
BLI_assert(mesh.totedge == edge_selection.size());
+ const Span<MEdge> edges = mesh.edges();
int selected_edges_num = 0;
int selected_verts_num = 0;
for (const int i : IndexRange(mesh.totedge)) {
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
if (edge_selection[i]) {
r_edge_map[i] = selected_edges_num;
selected_edges_num++;
@@ -509,7 +525,7 @@ static void compute_selected_vertices_and_edges_from_edge_selection(
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
*r_selected_edges_num = selected_edges_num;
}
@@ -541,23 +557,26 @@ static void compute_selected_edges_from_edge_selection(const Mesh &mesh,
* 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 Span<bool> edge_selection,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_edge_selection(const Mesh &mesh,
+ const Span<bool> edge_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[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) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
if (!edge_selection[loop.e]) {
all_edges_in_selection = false;
break;
@@ -592,12 +611,12 @@ static void compute_selected_mesh_data_from_vertex_selection_edge_face(
compute_selected_edges_from_vertex_selection(
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_vertex_selection(mesh,
- vertex_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh,
+ vertex_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
@@ -610,23 +629,23 @@ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
- compute_selected_vertices_from_vertex_selection(
- vertex_selection, r_vertex_map, r_selected_vertices_num);
+ compute_selected_verts_from_vertex_selection(
+ vertex_selection, r_vertex_map, r_selected_verts_num);
compute_selected_edges_from_vertex_selection(
mesh, vertex_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_vertex_selection(mesh,
- vertex_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh,
+ vertex_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
@@ -645,17 +664,17 @@ static void compute_selected_mesh_data_from_edge_selection_edge_face(
{
compute_selected_edges_from_edge_selection(
mesh, edge_selection, r_edge_map, r_selected_edges_num);
- compute_selected_polygons_from_edge_selection(mesh,
- edge_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_polys_from_edge_selection(mesh,
+ edge_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
* 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.
+ * that edge are kept as well. The polys are kept if all edges are in the selection.
*/
static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
const Span<bool> edge_selection,
@@ -663,44 +682,41 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
r_vertex_map.fill(-1);
- compute_selected_vertices_and_edges_from_edge_selection(mesh,
- edge_selection,
- r_vertex_map,
- r_edge_map,
- r_selected_vertices_num,
- r_selected_edges_num);
- compute_selected_polygons_from_edge_selection(mesh,
- edge_selection,
- r_selected_poly_indices,
- r_loop_starts,
- r_selected_polys_num,
- r_selected_loops_num);
+ compute_selected_verts_and_edges_from_edge_selection(
+ mesh, edge_selection, r_vertex_map, r_edge_map, r_selected_verts_num, r_selected_edges_num);
+ compute_selected_polys_from_edge_selection(mesh,
+ edge_selection,
+ r_selected_poly_indices,
+ r_loop_starts,
+ r_selected_polys_num,
+ r_selected_loops_num);
}
/**
* Checks for every polygon if it is in `poly_selection`.
*/
-static void compute_selected_polygons_from_poly_selection(const Mesh &mesh,
- const Span<bool> poly_selection,
- Vector<int> &r_selected_poly_indices,
- Vector<int> &r_loop_starts,
- int *r_selected_polys_num,
- int *r_selected_loops_num)
+static void compute_selected_polys_from_poly_selection(const Mesh &mesh,
+ const Span<bool> poly_selection,
+ Vector<int> &r_selected_poly_indices,
+ Vector<int> &r_loop_starts,
+ int *r_selected_polys_num,
+ int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
+ const Span<MPoly> polys = mesh.polys();
r_selected_poly_indices.reserve(mesh.totpoly);
r_loop_starts.reserve(mesh.totloop);
int selected_loops_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -727,6 +743,9 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
{
BLI_assert(mesh.totpoly == poly_selection.size());
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_edge_map.fill(-1);
r_selected_poly_indices.reserve(mesh.totpoly);
@@ -734,8 +753,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
int selected_loops_num = 0;
int selected_edges_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -743,8 +762,8 @@ static void compute_selected_mesh_data_from_poly_selection_edge_face(
selected_loops_num += 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) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
/* Check first if it has not yet been added. */
if (r_edge_map[loop.e] == -1) {
r_edge_map[loop.e] = selected_edges_num;
@@ -768,13 +787,16 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
MutableSpan<int> r_edge_map,
Vector<int> &r_selected_poly_indices,
Vector<int> &r_loop_starts,
- int *r_selected_vertices_num,
+ int *r_selected_verts_num,
int *r_selected_edges_num,
int *r_selected_polys_num,
int *r_selected_loops_num)
{
BLI_assert(mesh.totpoly == poly_selection.size());
BLI_assert(mesh.totedge == r_edge_map.size());
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_vertex_map.fill(-1);
r_edge_map.fill(-1);
@@ -784,8 +806,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
int selected_loops_num = 0;
int selected_verts_num = 0;
int selected_edges_num = 0;
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly_src = mesh.mpoly[i];
+ for (const int i : polys.index_range()) {
+ const MPoly &poly_src = polys[i];
/* We keep this one. */
if (poly_selection[i]) {
r_selected_poly_indices.append_unchecked(i);
@@ -793,8 +815,8 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
selected_loops_num += 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) {
+ const Span<MLoop> poly_loops = loops.slice(poly_src.loopstart, poly_src.totloop);
+ for (const MLoop &loop : poly_loops) {
/* Check first if it has not yet been added. */
if (r_vertex_map[loop.v] == -1) {
r_vertex_map[loop.v] = selected_verts_num;
@@ -807,7 +829,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
}
}
}
- *r_selected_vertices_num = selected_verts_num;
+ *r_selected_verts_num = selected_verts_num;
*r_selected_edges_num = selected_edges_num;
*r_selected_polys_num = r_selected_poly_indices.size();
*r_selected_loops_num = selected_loops_num;
@@ -817,7 +839,7 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh,
* Keep the parts of the mesh that are in the selection.
*/
static void do_mesh_separation(GeometrySet &geometry_set,
- const MeshComponent &in_component,
+ const Mesh &mesh_in,
const Span<bool> selection,
const eAttrDomain domain,
const GeometryNodeDeleteGeometryMode mode)
@@ -828,9 +850,7 @@ static void do_mesh_separation(GeometrySet &geometry_set,
int selected_polys_num = 0;
int selected_loops_num = 0;
- const Mesh &mesh_in = *in_component.get_for_read();
Mesh *mesh_out;
- MeshComponent out_component;
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -892,27 +912,32 @@ static void do_mesh_separation(GeometrySet &geometry_set,
0,
selected_loops_num,
selected_polys_num);
- out_component.replace(mesh_out, GeometryOwnershipType::Editable);
/* Copy the selected parts of the mesh over to the new mesh. */
- copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map);
+ copy_masked_verts_to_new_mesh(mesh_in, *mesh_out, vertex_map);
copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map);
copy_masked_polys_to_new_mesh(
mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
- copy_attributes_based_on_map(
- attributes, in_component, out_component, ATTR_DOMAIN_POINT, vertex_map);
- copy_attributes_based_on_map(
- attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map);
+ copy_attributes_based_on_map(attributes,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ vertex_map);
+ copy_attributes_based_on_map(attributes,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
+ ATTR_DOMAIN_EDGE,
+ edge_map);
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -964,26 +989,29 @@ static void do_mesh_separation(GeometrySet &geometry_set,
0,
selected_loops_num,
selected_polys_num);
- out_component.replace(mesh_out, GeometryOwnershipType::Editable);
/* Copy the selected parts of the mesh over to the new mesh. */
- memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert));
+ mesh_out->verts_for_write().copy_from(mesh_in.verts());
copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map);
copy_masked_polys_to_new_mesh(
mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
- copy_attributes(attributes, in_component, out_component, {ATTR_DOMAIN_POINT});
- copy_attributes_based_on_map(
- attributes, in_component, out_component, ATTR_DOMAIN_EDGE, edge_map);
+ copy_attributes(
+ attributes, mesh_in.attributes(), mesh_out->attributes_for_write(), {ATTR_DOMAIN_POINT});
+ copy_attributes_based_on_map(attributes,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
+ ATTR_DOMAIN_EDGE,
+ edge_map);
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -993,28 +1021,28 @@ static void do_mesh_separation(GeometrySet &geometry_set,
/* Fill all the maps based on the selection. */
switch (domain) {
case ATTR_DOMAIN_POINT:
- compute_selected_polygons_from_vertex_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_vertex_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
case ATTR_DOMAIN_EDGE:
- compute_selected_polygons_from_edge_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_edge_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
case ATTR_DOMAIN_FACE:
- compute_selected_polygons_from_poly_selection(mesh_in,
- selection,
- selected_poly_indices,
- new_loop_starts,
- &selected_polys_num,
- &selected_loops_num);
+ compute_selected_polys_from_poly_selection(mesh_in,
+ selection,
+ selected_poly_indices,
+ new_loop_starts,
+ &selected_polys_num,
+ &selected_loops_num);
break;
default:
BLI_assert_unreachable();
@@ -1022,24 +1050,25 @@ static void do_mesh_separation(GeometrySet &geometry_set,
}
mesh_out = BKE_mesh_new_nomain_from_template(
&mesh_in, mesh_in.totvert, mesh_in.totedge, 0, selected_loops_num, selected_polys_num);
- out_component.replace(mesh_out, GeometryOwnershipType::Editable);
/* Copy the selected parts of the mesh over to the new mesh. */
- memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert));
- memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge));
+ mesh_out->verts_for_write().copy_from(mesh_in.verts());
+ mesh_out->edges_for_write().copy_from(mesh_in.edges());
copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts);
/* Copy attributes. */
- copy_attributes(
- attributes, in_component, out_component, {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE});
+ copy_attributes(attributes,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
+ {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE});
copy_attributes_based_on_mask(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
ATTR_DOMAIN_FACE,
IndexMask(Vector<int64_t>(selected_poly_indices.as_span())));
copy_face_corner_attributes(attributes,
- in_component,
- out_component,
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write(),
selected_loops_num,
selected_poly_indices,
mesh_in);
@@ -1056,22 +1085,20 @@ static void separate_mesh_selection(GeometrySet &geometry_set,
const eAttrDomain selection_domain,
const GeometryNodeDeleteGeometryMode mode)
{
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext field_context{src_component, selection_domain};
-
- fn::FieldEvaluator evaluator{field_context,
- src_component.attribute_domain_size(selection_domain)};
+ const Mesh &src_mesh = *geometry_set.get_mesh_for_read();
+ bke::MeshFieldContext field_context{src_mesh, selection_domain};
+ fn::FieldEvaluator evaluator{field_context, src_mesh.attributes().domain_size(selection_domain)};
evaluator.add(selection_field);
evaluator.evaluate();
const VArray<bool> selection = evaluator.get_evaluated<bool>(0);
/* Check if there is anything to delete. */
- if (selection.is_single() && selection.get_internal_single()) {
+ if (selection.is_empty() || (selection.is_single() && selection.get_internal_single())) {
return;
}
const VArraySpan<bool> selection_span{selection};
- do_mesh_separation(geometry_set, src_component, selection_span, selection_domain, mode);
+ do_mesh_separation(geometry_set, src_mesh, selection_span, selection_domain, mode);
}
} // namespace blender::nodes::node_geo_delete_geometry_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc
new file mode 100644
index 00000000000..a0bd28218cc
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_in_volume.cc
@@ -0,0 +1,285 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifdef WITH_OPENVDB
+# include <openvdb/openvdb.h>
+# include <openvdb/tools/Interpolation.h>
+# include <openvdb/tools/PointScatter.h>
+#endif
+
+#include "DNA_node_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_pointcloud.h"
+#include "BKE_volume.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+NODE_STORAGE_FUNCS(NodeGeometryDistributePointsInVolume)
+
+static void geo_node_distribute_points_in_volume_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME);
+ b.add_input<decl::Float>(N_("Density"))
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(100000.0f)
+ .subtype(PROP_NONE)
+ .description(N_("Number of points to sample per unit volume"));
+ b.add_input<decl::Int>(N_("Seed"))
+ .min(-10000)
+ .max(10000)
+ .description(N_("Seed used by the random number generator to generate random points"));
+ b.add_input<decl::Vector>(N_("Spacing"))
+ .default_value({0.3, 0.3, 0.3})
+ .min(0.0001f)
+ .subtype(PROP_XYZ)
+ .description(N_("Spacing between grid points"));
+ b.add_input<decl::Float>(N_("Threshold"))
+ .default_value(0.1f)
+ .min(0.0f)
+ .max(FLT_MAX)
+ .description(N_("Minimum density of a volume cell to contain a grid point"));
+ b.add_output<decl::Geometry>(N_("Points"));
+}
+
+static void geo_node_distribute_points_in_volume_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "mode", 0, "", ICON_NONE);
+}
+
+static void node_distribute_points_in_volume_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryDistributePointsInVolume *data = MEM_cnew<NodeGeometryDistributePointsInVolume>(
+ __func__);
+ data->mode = GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM;
+ node->storage = data;
+}
+
+static void node_distribute_points_in_volume_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeGeometryDistributePointsInVolume &storage = node_storage(*node);
+ GeometryNodeDistributePointsInVolumeMode mode =
+ static_cast<GeometryNodeDistributePointsInVolumeMode>(storage.mode);
+
+ bNodeSocket *sock_density = ((bNodeSocket *)(node->inputs.first))->next;
+ bNodeSocket *sock_seed = sock_density->next;
+ bNodeSocket *sock_spacing = sock_seed->next;
+ bNodeSocket *sock_threshold = sock_spacing->next;
+
+ nodeSetSocketAvailability(
+ ntree, sock_density, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM);
+ nodeSetSocketAvailability(
+ ntree, sock_seed, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM);
+ nodeSetSocketAvailability(
+ ntree, sock_spacing, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID);
+ nodeSetSocketAvailability(
+ ntree, sock_threshold, mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID);
+}
+
+#ifdef WITH_OPENVDB
+/* Implements the interface required by #openvdb::tools::NonUniformPointScatter. */
+class PositionsVDBWrapper {
+ private:
+ float3 offset_fix_;
+ Vector<float3> &vector_;
+
+ public:
+ PositionsVDBWrapper(Vector<float3> &vector, const float3 offset_fix)
+ : offset_fix_(offset_fix), vector_(vector)
+ {
+ }
+ PositionsVDBWrapper(const PositionsVDBWrapper &wrapper) = default;
+
+ void add(const openvdb::Vec3R &pos)
+ {
+ vector_.append((float3((float)pos[0], (float)pos[1], (float)pos[2]) + offset_fix_));
+ }
+};
+
+/* Use #std::mt19937 as a random number generator,
+ * it has a very long period and thus there should be no visible patterns in the generated points.
+ */
+using RNGType = std::mt19937;
+/* Non-uniform scatter allows the amount of points to be scaled with the volume's density. */
+using NonUniformPointScatterVDB =
+ openvdb::tools::NonUniformPointScatter<PositionsVDBWrapper, RNGType>;
+
+static void point_scatter_density_random(const openvdb::FloatGrid &grid,
+ const float density,
+ const int seed,
+ Vector<float3> &r_positions)
+{
+ /* Offset points by half a voxel so that grid points are aligned with world grid points. */
+ const float3 offset_fix = {0.5f * (float)grid.voxelSize().x(),
+ 0.5f * (float)grid.voxelSize().y(),
+ 0.5f * (float)grid.voxelSize().z()};
+ /* Setup and call into OpenVDB's point scatter API. */
+ PositionsVDBWrapper vdb_position_wrapper = PositionsVDBWrapper(r_positions, offset_fix);
+ RNGType random_generator(seed);
+ NonUniformPointScatterVDB point_scatter(vdb_position_wrapper, density, random_generator);
+ point_scatter(grid);
+}
+
+static void point_scatter_density_grid(const openvdb::FloatGrid &grid,
+ const float3 spacing,
+ const float threshold,
+ Vector<float3> &r_positions)
+{
+ const openvdb::Vec3d half_voxel(0.5, 0.5, 0.5);
+ const openvdb::Vec3d voxel_spacing((double)spacing.x / grid.voxelSize().x(),
+ (double)spacing.y / grid.voxelSize().y(),
+ (double)spacing.z / grid.voxelSize().z());
+
+ /* Abort if spacing is zero. */
+ const double min_spacing = std::min(voxel_spacing.x(),
+ std::min(voxel_spacing.y(), voxel_spacing.z()));
+ if (std::abs(min_spacing) < 0.0001) {
+ return;
+ }
+
+ /* Iterate through tiles and voxels on the grid. */
+ for (openvdb::FloatGrid::ValueOnCIter cell = grid.cbeginValueOn(); cell; ++cell) {
+ /* Check if the cell's value meets the minimum threshold. */
+ if (cell.getValue() < threshold) {
+ continue;
+ }
+ /* Compute the bounding box of each tile/voxel. */
+ const openvdb::CoordBBox bbox = cell.getBoundingBox();
+ const openvdb::Vec3d box_min = bbox.min().asVec3d() - half_voxel;
+ const openvdb::Vec3d box_max = bbox.max().asVec3d() + half_voxel;
+
+ /* Pick a starting point rounded up to the nearest possible point. */
+ double abs_spacing_x = std::abs(voxel_spacing.x());
+ double abs_spacing_y = std::abs(voxel_spacing.y());
+ double abs_spacing_z = std::abs(voxel_spacing.z());
+ const openvdb::Vec3d start(ceil(box_min.x() / abs_spacing_x) * abs_spacing_x,
+ ceil(box_min.y() / abs_spacing_y) * abs_spacing_y,
+ ceil(box_min.z() / abs_spacing_z) * abs_spacing_z);
+
+ /* Iterate through all possible points in box. */
+ for (double x = start.x(); x < box_max.x(); x += abs_spacing_x) {
+ for (double y = start.y(); y < box_max.y(); y += abs_spacing_y) {
+ for (double z = start.z(); z < box_max.z(); z += abs_spacing_z) {
+ /* Transform with grid matrix and add point. */
+ const openvdb::Vec3d idx_pos(x, y, z);
+ const openvdb::Vec3d local_pos = grid.indexToWorld(idx_pos + half_voxel);
+ r_positions.append({(float)local_pos.x(), (float)local_pos.y(), (float)local_pos.z()});
+ }
+ }
+ }
+ }
+}
+
+#endif /* WITH_OPENVDB */
+
+static void geo_node_distribute_points_in_volume_exec(GeoNodeExecParams params)
+{
+#ifdef WITH_OPENVDB
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
+
+ const NodeGeometryDistributePointsInVolume &storage = node_storage(params.node());
+ const GeometryNodeDistributePointsInVolumeMode mode =
+ static_cast<GeometryNodeDistributePointsInVolumeMode>(storage.mode);
+
+ float density;
+ int seed;
+ float3 spacing{0, 0, 0};
+ float threshold;
+ if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM) {
+ density = params.extract_input<float>("Density");
+ seed = params.extract_input<int>("Seed");
+ }
+ else if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID) {
+ spacing = params.extract_input<float3>("Spacing");
+ threshold = params.extract_input<float>("Threshold");
+ }
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ if (!geometry_set.has_volume()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+ const VolumeComponent *component = geometry_set.get_component_for_read<VolumeComponent>();
+ const Volume *volume = component->get_for_read();
+
+ Vector<float3> positions;
+
+ for (const int i : IndexRange(BKE_volume_num_grids(volume))) {
+ const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i);
+ if (volume_grid == nullptr) {
+ continue;
+ }
+
+ openvdb::GridBase::ConstPtr base_grid = BKE_volume_grid_openvdb_for_read(volume,
+ volume_grid);
+ if (!base_grid) {
+ continue;
+ }
+
+ if (!base_grid->isType<openvdb::FloatGrid>()) {
+ continue;
+ }
+
+ const openvdb::FloatGrid::ConstPtr grid = openvdb::gridConstPtrCast<openvdb::FloatGrid>(
+ base_grid);
+
+ if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_RANDOM) {
+ point_scatter_density_random(*grid, density, seed, positions);
+ }
+ else if (mode == GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME_DENSITY_GRID) {
+ point_scatter_density_grid(*grid, spacing, threshold, positions);
+ }
+ }
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ bke::SpanAttributeWriter<float> point_radii =
+ point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
+
+ point_positions.span.copy_from(positions);
+ point_radii.span.fill(0.05f);
+ point_positions.finish();
+ point_radii.finish();
+
+ geometry_set.replace_pointcloud(pointcloud);
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
+ });
+
+ params.set_output("Points", std::move(geometry_set));
+
+#else
+ params.set_default_remaining_outputs();
+ params.error_message_add(NodeWarningType::Error,
+ TIP_("Disabled, Blender was compiled without OpenVDB"));
+#endif
+}
+} // namespace blender::nodes
+
+void register_node_type_geo_distribute_points_in_volume()
+{
+ static bNodeType ntype;
+ geo_node_type_base(&ntype,
+ GEO_NODE_DISTRIBUTE_POINTS_IN_VOLUME,
+ "Distribute Points in Volume",
+ NODE_CLASS_GEOMETRY);
+ node_type_storage(&ntype,
+ "NodeGeometryDistributePointsInVolume",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ node_type_init(&ntype, blender::nodes::node_distribute_points_in_volume_init);
+ node_type_update(&ntype, blender::nodes::node_distribute_points_in_volume_update);
+ node_type_size(&ntype, 170, 100, 320);
+ ntype.declare = blender::nodes::geo_node_distribute_points_in_volume_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_distribute_points_in_volume_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_distribute_points_in_volume_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
index faf5b7f65fa..cdcb16985ac 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -105,6 +105,8 @@ static void sample_mesh_surface(const Mesh &mesh,
Vector<float3> &r_bary_coords,
Vector<int> &r_looptri_indices)
{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -113,12 +115,12 @@ static void sample_mesh_surface(const Mesh &mesh,
const int v0_loop = looptri.tri[0];
const int v1_loop = looptri.tri[1];
const int v2_loop = looptri.tri[2];
- const int v0_index = mesh.mloop[v0_loop].v;
- const int v1_index = mesh.mloop[v1_loop].v;
- const int v2_index = mesh.mloop[v2_loop].v;
- const float3 v0_pos = float3(mesh.mvert[v0_index].co);
- const float3 v1_pos = float3(mesh.mvert[v1_index].co);
- const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+ const int v0_index = loops[v0_loop].v;
+ const int v1_index = loops[v1_loop].v;
+ const int v2_index = loops[v2_loop].v;
+ const float3 v0_pos = verts[v0_index].co;
+ const float3 v1_pos = verts[v1_index].co;
+ const float3 v2_pos = verts[v2_index].co;
float looptri_density_factor = 1.0f;
if (!density_factors.is_empty()) {
@@ -220,11 +222,11 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
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 probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
- v2_density_factor * bary_coord.z;
+ const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
+ v2_density_factor * bary_coord.z;
const float hash = noise::hash_float_to_float(bary_coord);
- if (hash > probablity) {
+ if (hash > probability) {
elimination_mask[i] = true;
}
}
@@ -283,15 +285,14 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
}
BLI_NOINLINE static void propagate_existing_attributes(
- const MeshComponent &mesh_component,
+ const Mesh &mesh,
const Map<AttributeIDRef, AttributeKind> &attributes,
- GeometryComponent &point_component,
+ PointCloud &points,
const Span<float3> bary_coords,
const Span<int> looptri_indices)
{
- const Mesh &mesh = *mesh_component.get_for_read();
- const AttributeAccessor mesh_attributes = *mesh_component.attributes();
- MutableAttributeAccessor point_attributes = *point_component.attributes_for_write();
+ const AttributeAccessor mesh_attributes = mesh.attributes();
+ MutableAttributeAccessor point_attributes = points.attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
@@ -326,30 +327,31 @@ struct AttributeOutputs {
};
} // namespace
-BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component,
- PointCloudComponent &point_component,
+BLI_NOINLINE static void compute_attribute_outputs(const Mesh &mesh,
+ PointCloud &points,
const Span<float3> bary_coords,
const Span<int> looptri_indices,
const AttributeOutputs &attribute_outputs)
{
- MutableAttributeAccessor pointcloud_attributes = *point_component.attributes_for_write();
+ MutableAttributeAccessor point_attributes = points.attributes_for_write();
- SpanAttributeWriter<int> ids = pointcloud_attributes.lookup_or_add_for_write_only_span<int>(
+ SpanAttributeWriter<int> ids = point_attributes.lookup_or_add_for_write_only_span<int>(
"id", ATTR_DOMAIN_POINT);
SpanAttributeWriter<float3> normals;
SpanAttributeWriter<float3> rotations;
if (attribute_outputs.normal_id) {
- normals = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>(
+ normals = point_attributes.lookup_or_add_for_write_only_span<float3>(
attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT);
}
if (attribute_outputs.rotation_id) {
- rotations = pointcloud_attributes.lookup_or_add_for_write_only_span<float3>(
+ rotations = point_attributes.lookup_or_add_for_write_only_span<float3>(
attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT);
}
- const Mesh &mesh = *mesh_component.get_for_read();
+ const Span<MVert> verts = mesh.verts();
+ const Span<MLoop> loops = mesh.loops();
const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
BKE_mesh_runtime_looptri_len(&mesh)};
@@ -358,12 +360,12 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
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 float3 v0_pos = float3(mesh.mvert[v0_index].co);
- const float3 v1_pos = float3(mesh.mvert[v1_index].co);
- const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+ const int v0_index = loops[looptri.tri[0]].v;
+ const int v1_index = loops[looptri.tri[1]].v;
+ const int v2_index = loops[looptri.tri[2]].v;
+ const float3 v0_pos = verts[v0_index].co;
+ const float3 v1_pos = verts[v1_index].co;
+ const float3 v2_pos = verts[v2_index].co;
ids.span[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
@@ -380,25 +382,19 @@ BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_com
}
ids.finish();
-
- if (normals) {
- normals.finish();
- }
- if (rotations) {
- rotations.finish();
- }
+ normals.finish();
+ rotations.finish();
}
-static Array<float> calc_full_density_factors_with_selection(const MeshComponent &component,
+static Array<float> calc_full_density_factors_with_selection(const Mesh &mesh,
const Field<float> &density_field,
const Field<bool> &selection_field)
{
- const eAttrDomain attribute_domain = ATTR_DOMAIN_CORNER;
- GeometryComponentFieldContext field_context{component, attribute_domain};
- const int domain_size = component.attribute_domain_size(attribute_domain);
-
+ const eAttrDomain domain = ATTR_DOMAIN_CORNER;
+ const int domain_size = mesh.attributes().domain_size(domain);
Array<float> densities(domain_size, 0.0f);
+ bke::MeshFieldContext field_context{mesh, domain};
fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(density_field, densities.as_mutable_span());
@@ -406,7 +402,7 @@ static Array<float> calc_full_density_factors_with_selection(const MeshComponent
return densities;
}
-static void distribute_points_random(const MeshComponent &component,
+static void distribute_points_random(const Mesh &mesh,
const Field<float> &density_field,
const Field<bool> &selection_field,
const int seed,
@@ -415,12 +411,11 @@ static void distribute_points_random(const MeshComponent &component,
Vector<int> &looptri_indices)
{
const Array<float> densities = calc_full_density_factors_with_selection(
- component, density_field, selection_field);
- const Mesh &mesh = *component.get_for_read();
+ mesh, density_field, selection_field);
sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices);
}
-static void distribute_points_poisson_disk(const MeshComponent &mesh_component,
+static void distribute_points_poisson_disk(const Mesh &mesh,
const float minimum_distance,
const float max_density,
const Field<float> &density_factor_field,
@@ -430,14 +425,13 @@ static void distribute_points_poisson_disk(const MeshComponent &mesh_component,
Vector<float3> &bary_coords,
Vector<int> &looptri_indices)
{
- const Mesh &mesh = *mesh_component.get_for_read();
sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices);
Array<bool> elimination_mask(positions.size(), false);
update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask);
const Array<float> density_factors = calc_full_density_factors_with_selection(
- mesh_component, density_factor_field, selection_field);
+ mesh, density_factor_field, selection_field);
update_elimination_mask_based_on_density_factors(
mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span());
@@ -457,7 +451,7 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
return;
}
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
Vector<float3> positions;
Vector<float3> bary_coords;
@@ -466,20 +460,15 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
switch (method) {
case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: {
const Field<float> density_field = params.get_input<Field<float>>("Density");
- distribute_points_random(mesh_component,
- density_field,
- selection_field,
- seed,
- positions,
- bary_coords,
- looptri_indices);
+ distribute_points_random(
+ mesh, density_field, selection_field, seed, positions, bary_coords, looptri_indices);
break;
}
case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: {
const float minimum_distance = params.get_input<float>("Distance Min");
const float density_max = params.get_input<float>("Density Max");
const Field<float> density_factors_field = params.get_input<Field<float>>("Density Factor");
- distribute_points_poisson_disk(mesh_component,
+ distribute_points_poisson_disk(mesh,
minimum_distance,
density_max,
density_factors_field,
@@ -497,12 +486,17 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
}
PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size());
- memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size());
- uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
- geometry_set.replace_pointcloud(pointcloud);
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ bke::SpanAttributeWriter<float> point_radii =
+ point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
+ point_positions.span.copy_from(positions);
+ point_radii.span.fill(0.05f);
+ point_positions.finish();
+ point_radii.finish();
- PointCloudComponent &point_component =
- geometry_set.get_component_for_write<PointCloudComponent>();
+ geometry_set.replace_pointcloud(pointcloud);
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -511,11 +505,9 @@ static void point_distribution_calculate(GeometrySet &geometry_set,
/* Position is set separately. */
attributes.remove("position");
- propagate_existing_attributes(
- mesh_component, attributes, point_component, bary_coords, looptri_indices);
+ propagate_existing_attributes(mesh, attributes, *pointcloud, bary_coords, looptri_indices);
- compute_attribute_outputs(
- mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs);
+ compute_attribute_outputs(mesh, *pointcloud, bary_coords, looptri_indices, attribute_outputs);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -536,12 +528,14 @@ static void node_geo_exec(GeoNodeExecParams params)
attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation");
}
+ lazy_threading::send_hint();
+
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
point_distribution_calculate(
geometry_set, selection_field, method, seed, attribute_outputs, params);
/* Keep instances because the original geometry set may contain instances that are processed as
* well. */
- geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
});
params.set_output("Points", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
index bf5479d552e..84e63845b84 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_dual_mesh.cc
@@ -143,12 +143,9 @@ static void transfer_attributes(
const Span<int> new_to_old_edges_map,
const Span<int> new_to_old_face_corners_map,
const Span<std::pair<int, int>> boundary_vertex_to_relevant_face_map,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const AttributeAccessor src_attributes,
+ MutableAttributeAccessor dst_attributes)
{
- const AttributeAccessor src_attributes = *src_component.attributes();
- MutableAttributeAccessor dst_attributes = *dst_component.attributes_for_write();
-
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
@@ -212,13 +209,18 @@ static void calc_boundaries(const Mesh &mesh,
{
BLI_assert(r_vertex_types.size() == mesh.totvert);
BLI_assert(r_edge_types.size() == mesh.totedge);
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
r_vertex_types.fill(VertexType::Loose);
r_edge_types.fill(EdgeType::Loose);
/* Add up the number of polys connected to each edge. */
for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[i];
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const MPoly &poly = polys[i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
r_edge_types[loop.e] = get_edge_type_with_added_neighbor(r_edge_types[loop.e]);
}
}
@@ -229,7 +231,7 @@ static void calc_boundaries(const Mesh &mesh,
if (edge_type == EdgeType::Loose) {
continue;
}
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
if (edge_type == EdgeType::Boundary) {
r_vertex_types[edge.v1] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v1]);
r_vertex_types[edge.v2] = get_vertex_type_with_added_neighbor(r_vertex_types[edge.v2]);
@@ -244,7 +246,7 @@ static void calc_boundaries(const Mesh &mesh,
for (const int i : IndexRange(mesh.totedge)) {
const EdgeType edge_type = r_edge_types[i];
if (edge_type == EdgeType::Normal) {
- const MEdge &edge = mesh.medge[i];
+ const MEdge &edge = edges[i];
if (r_vertex_types[edge.v1] == VertexType::Loose) {
r_vertex_types[edge.v1] = VertexType::Normal;
}
@@ -261,9 +263,12 @@ static void calc_boundaries(const Mesh &mesh,
static void create_vertex_poly_map(const Mesh &mesh,
MutableSpan<Vector<int>> r_vertex_poly_indices)
{
- for (const int i : IndexRange(mesh.totpoly)) {
- const MPoly &poly = mesh.mpoly[i];
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
r_vertex_poly_indices[loop.v].append(i);
}
}
@@ -324,26 +329,28 @@ static void create_vertex_poly_map(const Mesh &mesh,
* - Finally if we are in the normal case we also need to add the last "shared edge" to close the
* loop.
*/
-static bool sort_vertex_polys(const Mesh &mesh,
+static bool sort_vertex_polys(const Span<MEdge> edges,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
const int vertex_index,
const bool boundary_vertex,
const Span<EdgeType> edge_types,
- MutableSpan<int> connected_polygons,
+ MutableSpan<int> connected_polys,
MutableSpan<int> r_shared_edges,
MutableSpan<int> r_sorted_corners)
{
- if (connected_polygons.size() <= 2 && (!boundary_vertex || connected_polygons.size() == 0)) {
+ if (connected_polys.size() <= 2 && (!boundary_vertex || connected_polys.size() == 0)) {
return true;
}
/* For each polygon store the two corners whose edge contains the vertex. */
- Array<std::pair<int, int>> poly_vertex_corners(connected_polygons.size());
- for (const int i : connected_polygons.index_range()) {
- const MPoly &poly = mesh.mpoly[connected_polygons[i]];
+ Array<std::pair<int, int>> poly_vertex_corners(connected_polys.size());
+ for (const int i : connected_polys.index_range()) {
+ const MPoly &poly = polys[connected_polys[i]];
bool first_edge_done = false;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
- const MLoop &loop = mesh.mloop[loop_index];
- if (mesh.medge[loop.e].v1 == vertex_index || mesh.medge[loop.e].v2 == vertex_index) {
+ const MLoop &loop = loops[loop_index];
+ if (edges[loop.e].v1 == vertex_index || edges[loop.e].v2 == vertex_index) {
if (!first_edge_done) {
poly_vertex_corners[i].first = loop_index;
first_edge_done = true;
@@ -362,20 +369,20 @@ static bool sort_vertex_polys(const Mesh &mesh,
* the loop to determine the 'average' orientation. */
if (boundary_vertex) {
/* Our first polygon needs to be one which has a boundary edge. */
- for (const int i : connected_polygons.index_range()) {
- const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first];
- const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second];
+ for (const int i : connected_polys.index_range()) {
+ const MLoop &first_loop = loops[poly_vertex_corners[i].first];
+ const MLoop &second_loop = loops[poly_vertex_corners[i].second];
if (edge_types[first_loop.e] == EdgeType::Boundary && first_loop.v == vertex_index) {
shared_edge_i = second_loop.e;
r_sorted_corners[0] = poly_vertex_corners[i].first;
- std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
if (edge_types[second_loop.e] == EdgeType::Boundary && second_loop.v == vertex_index) {
shared_edge_i = first_loop.e;
r_sorted_corners[0] = poly_vertex_corners[i].second;
- std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
@@ -383,20 +390,20 @@ static bool sort_vertex_polys(const Mesh &mesh,
if (shared_edge_i == -1) {
/* The rotation is inconsistent between the two polygons on the boundary. Just choose one
* of the polygon's orientation. */
- for (const int i : connected_polygons.index_range()) {
- const MLoop &first_loop = mesh.mloop[poly_vertex_corners[i].first];
- const MLoop &second_loop = mesh.mloop[poly_vertex_corners[i].second];
+ for (const int i : connected_polys.index_range()) {
+ const MLoop &first_loop = loops[poly_vertex_corners[i].first];
+ const MLoop &second_loop = loops[poly_vertex_corners[i].second];
if (edge_types[first_loop.e] == EdgeType::Boundary) {
shared_edge_i = second_loop.e;
r_sorted_corners[0] = poly_vertex_corners[i].first;
- std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
if (edge_types[second_loop.e] == EdgeType::Boundary) {
shared_edge_i = first_loop.e;
r_sorted_corners[0] = poly_vertex_corners[i].second;
- std::swap(connected_polygons[i], connected_polygons[0]);
+ std::swap(connected_polys[i], connected_polys[0]);
std::swap(poly_vertex_corners[i], poly_vertex_corners[0]);
break;
}
@@ -405,8 +412,8 @@ static bool sort_vertex_polys(const Mesh &mesh,
}
else {
/* Any polygon can be the first. Just need to check the orientation. */
- const MLoop &first_loop = mesh.mloop[poly_vertex_corners[0].first];
- const MLoop &second_loop = mesh.mloop[poly_vertex_corners[0].second];
+ const MLoop &first_loop = loops[poly_vertex_corners[0].first];
+ const MLoop &second_loop = loops[poly_vertex_corners[0].second];
if (first_loop.v == vertex_index) {
shared_edge_i = second_loop.e;
r_sorted_corners[0] = poly_vertex_corners[0].first;
@@ -418,14 +425,14 @@ static bool sort_vertex_polys(const Mesh &mesh,
}
BLI_assert(shared_edge_i != -1);
- for (const int i : IndexRange(connected_polygons.size() - 1)) {
+ for (const int i : IndexRange(connected_polys.size() - 1)) {
r_shared_edges[i] = shared_edge_i;
/* Look at the other polys to see if it has this shared edge. */
int j = i + 1;
- for (; j < connected_polygons.size(); ++j) {
- const MLoop &first_loop = mesh.mloop[poly_vertex_corners[j].first];
- const MLoop &second_loop = mesh.mloop[poly_vertex_corners[j].second];
+ for (; j < connected_polys.size(); ++j) {
+ const MLoop &first_loop = loops[poly_vertex_corners[j].first];
+ const MLoop &second_loop = loops[poly_vertex_corners[j].second];
if (first_loop.e == shared_edge_i) {
r_sorted_corners[i + 1] = poly_vertex_corners[j].first;
shared_edge_i = second_loop.e;
@@ -437,13 +444,13 @@ static bool sort_vertex_polys(const Mesh &mesh,
break;
}
}
- if (j == connected_polygons.size()) {
+ if (j == connected_polys.size()) {
/* The vertex is not manifold because the polygons around the vertex don't form a loop, and
* hence can't be sorted. */
return false;
}
- std::swap(connected_polygons[i + 1], connected_polygons[j]);
+ std::swap(connected_polys[i + 1], connected_polys[j]);
std::swap(poly_vertex_corners[i + 1], poly_vertex_corners[j]);
}
@@ -458,14 +465,16 @@ static bool sort_vertex_polys(const Mesh &mesh,
* Get the edge on the poly that contains the given vertex and is a boundary edge.
*/
static void boundary_edge_on_poly(const MPoly &poly,
- const Mesh &mesh,
+ const Span<MEdge> edges,
+ const Span<MLoop> loops,
const int vertex_index,
const Span<EdgeType> edge_types,
int &r_edge)
{
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
if (edge_types[loop.e] == EdgeType::Boundary) {
- const MEdge &edge = mesh.medge[loop.e];
+ const MEdge &edge = edges[loop.e];
if (edge.v1 == vertex_index || edge.v2 == vertex_index) {
r_edge = loop.e;
return;
@@ -479,7 +488,8 @@ static void boundary_edge_on_poly(const MPoly &poly,
* orientation of the poly is taken into account.
*/
static void boundary_edges_on_poly(const MPoly &poly,
- const Mesh &mesh,
+ const Span<MEdge> edges,
+ const Span<MLoop> loops,
const int vertex_index,
const Span<EdgeType> edge_types,
int &r_edge1,
@@ -489,9 +499,10 @@ static void boundary_edges_on_poly(const MPoly &poly,
/* This is set to true if the order in which we encounter the two edges is inconsistent with the
* orientation of the polygon. */
bool needs_swap = false;
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
if (edge_types[loop.e] == EdgeType::Boundary) {
- const MEdge &edge = mesh.medge[loop.e];
+ const MEdge &edge = edges[loop.e];
if (edge.v1 == vertex_index || edge.v2 == vertex_index) {
if (edge1_done) {
if (needs_swap) {
@@ -513,7 +524,7 @@ static void boundary_edges_on_poly(const MPoly &poly,
}
}
-static void add_edge(const Mesh &mesh,
+static void add_edge(const Span<MEdge> src_edges,
const int old_edge_i,
const int v1,
const int v2,
@@ -521,7 +532,7 @@ static void add_edge(const Mesh &mesh,
Vector<MEdge> &new_edges,
Vector<int> &loop_edges)
{
- MEdge new_edge = MEdge(mesh.medge[old_edge_i]);
+ MEdge new_edge = src_edges[old_edge_i];
new_edge.v1 = v1;
new_edge.v2 = v2;
const int new_edge_i = new_edges.size();
@@ -552,14 +563,17 @@ static bool vertex_needs_dissolving(const int vertex,
* edges in the input mesh which contain such a vertex are marked as 'done' to prevent duplicate
* edges being created. (See T94144)
*/
-static void dissolve_redundant_verts(const Mesh &mesh,
+static void dissolve_redundant_verts(const Span<MEdge> edges,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
const Span<Vector<int>> vertex_poly_indices,
MutableSpan<VertexType> vertex_types,
MutableSpan<int> old_to_new_edges_map,
Vector<MEdge> &new_edges,
Vector<int> &new_to_old_edges_map)
{
- for (const int vert_i : IndexRange(mesh.totvert)) {
+ const int vertex_num = vertex_types.size();
+ for (const int vert_i : IndexRange(vertex_num)) {
if (vertex_poly_indices[vert_i].size() != 2 || vertex_types[vert_i] != VertexType::Normal) {
continue;
}
@@ -567,9 +581,10 @@ static void dissolve_redundant_verts(const Mesh &mesh,
const int second_poly_index = vertex_poly_indices[vert_i][1];
const int new_edge_index = new_edges.size();
bool edge_created = false;
- const MPoly &poly = mesh.mpoly[first_poly_index];
- for (const MLoop &loop : Span<MLoop>(&mesh.mloop[poly.loopstart], poly.totloop)) {
- const MEdge &edge = mesh.medge[loop.e];
+ const MPoly &poly = polys[first_poly_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ for (const MLoop &loop : poly_loops) {
+ const MEdge &edge = edges[loop.e];
const int v1 = edge.v1;
const int v2 = edge.v2;
bool mark_edge = false;
@@ -620,6 +635,10 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
const bool keep_boundaries)
{
const Mesh &mesh_in = *in_component.get_for_read();
+ const Span<MVert> src_verts = mesh_in.verts();
+ const Span<MEdge> src_edges = mesh_in.edges();
+ const Span<MPoly> src_polys = mesh_in.polys();
+ const Span<MLoop> src_loops = mesh_in.loops();
Map<AttributeIDRef, AttributeKind> attributes;
geometry_set.gather_attributes_for_propagation(
@@ -647,14 +666,28 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
bool vertex_ok = true;
if (vertex_types[i] == VertexType::Normal) {
Array<int> shared_edges(loop_indices.size());
- vertex_ok = sort_vertex_polys(
- mesh_in, i, false, edge_types, loop_indices, shared_edges, sorted_corners);
+ vertex_ok = sort_vertex_polys(src_edges,
+ src_polys,
+ src_loops,
+ i,
+ false,
+ edge_types,
+ loop_indices,
+ shared_edges,
+ sorted_corners);
vertex_shared_edges[i] = std::move(shared_edges);
}
else {
Array<int> shared_edges(loop_indices.size() - 1);
- vertex_ok = sort_vertex_polys(
- mesh_in, i, true, edge_types, loop_indices, shared_edges, sorted_corners);
+ vertex_ok = sort_vertex_polys(src_edges,
+ src_polys,
+ src_loops,
+ i,
+ true,
+ edge_types,
+ loop_indices,
+ shared_edges,
+ sorted_corners);
vertex_shared_edges[i] = std::move(shared_edges);
}
if (!vertex_ok) {
@@ -669,9 +702,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
Vector<float3> vertex_positions(mesh_in.totpoly);
for (const int i : IndexRange(mesh_in.totpoly)) {
- const MPoly poly = mesh_in.mpoly[i];
+ const MPoly &poly = src_polys[i];
BKE_mesh_calc_poly_center(
- &poly, &mesh_in.mloop[poly.loopstart], mesh_in.mvert, vertex_positions[i]);
+ &poly, &src_loops[poly.loopstart], src_verts.data(), vertex_positions[i]);
}
Array<int> boundary_edge_midpoint_index;
@@ -682,8 +715,8 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
for (const int i : IndexRange(mesh_in.totedge)) {
if (edge_types[i] == EdgeType::Boundary) {
float3 mid;
- const MEdge &edge = mesh_in.medge[i];
- mid_v3_v3v3(mid, mesh_in.mvert[edge.v1].co, mesh_in.mvert[edge.v2].co);
+ const MEdge &edge = src_edges[i];
+ mid_v3_v3v3(mid, src_verts[edge.v1].co, src_verts[edge.v2].co);
boundary_edge_midpoint_index[i] = vertex_positions.size();
vertex_positions.append(mid);
}
@@ -709,7 +742,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
/* This is necessary to prevent duplicate edges from being created, but will likely not do
* anything for most meshes. */
- dissolve_redundant_verts(mesh_in,
+ dissolve_redundant_verts(src_edges,
+ src_polys,
+ src_loops,
vertex_poly_indices,
vertex_types,
old_to_new_edges_map,
@@ -737,7 +772,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
const int old_edge_i = shared_edges[i];
if (old_to_new_edges_map[old_edge_i] == -1) {
/* This edge has not been created yet. */
- MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]);
+ MEdge new_edge = src_edges[old_edge_i];
new_edge.v1 = loop_indices[i];
new_edge.v2 = loop_indices[(i + 1) % loop_indices.size()];
new_to_old_edges_map.append(old_edge_i);
@@ -779,7 +814,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
const int old_edge_i = shared_edges[i];
if (old_to_new_edges_map[old_edge_i] == -1) {
/* This edge has not been created yet. */
- MEdge new_edge = MEdge(mesh_in.medge[old_edge_i]);
+ MEdge new_edge = src_edges[old_edge_i];
new_edge.v1 = loop_indices[i];
new_edge.v2 = loop_indices[i + 1];
new_to_old_edges_map.append(old_edge_i);
@@ -798,13 +833,15 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
int edge2;
if (loop_indices.size() >= 2) {
/* The first boundary edge is at the end of the chain of polygons. */
- boundary_edge_on_poly(mesh_in.mpoly[loop_indices.last()], mesh_in, i, edge_types, edge1);
- boundary_edge_on_poly(mesh_in.mpoly[loop_indices.first()], mesh_in, i, edge_types, edge2);
+ boundary_edge_on_poly(
+ src_polys[loop_indices.last()], src_edges, src_loops, i, edge_types, edge1);
+ boundary_edge_on_poly(
+ src_polys[loop_indices.first()], src_edges, src_loops, i, edge_types, edge2);
}
else {
/* If there is only one polygon both edges are in that polygon. */
boundary_edges_on_poly(
- mesh_in.mpoly[loop_indices[0]], mesh_in, i, edge_types, edge1, edge2);
+ src_polys[loop_indices[0]], src_edges, src_loops, i, edge_types, edge1, edge2);
}
const int last_face_center = loop_indices.last();
@@ -812,7 +849,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
new_to_old_face_corners_map.append(sorted_corners.last());
const int first_midpoint = loop_indices.last();
if (old_to_new_edges_map[edge1] == -1) {
- add_edge(mesh_in,
+ add_edge(src_edges,
edge1,
last_face_center,
first_midpoint,
@@ -830,9 +867,9 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
new_to_old_face_corners_map.append(sorted_corners.first());
boundary_vertex_to_relevant_face_map.append(
std::pair(loop_indices.last(), last_face_center));
- vertex_positions.append(mesh_in.mvert[i].co);
+ vertex_positions.append(src_verts[i].co);
const int boundary_vertex = loop_indices.last();
- add_edge(mesh_in,
+ add_edge(src_edges,
edge1,
first_midpoint,
boundary_vertex,
@@ -843,7 +880,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
loop_indices.append(boundary_edge_midpoint_index[edge2]);
new_to_old_face_corners_map.append(sorted_corners.first());
const int second_midpoint = loop_indices.last();
- add_edge(mesh_in,
+ add_edge(src_edges,
edge2,
boundary_vertex,
second_midpoint,
@@ -853,7 +890,7 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
if (old_to_new_edges_map[edge2] == -1) {
const int first_face_center = loop_indices.first();
- add_edge(mesh_in,
+ add_edge(src_edges,
edge2,
second_midpoint,
first_face_center,
@@ -875,31 +912,34 @@ static void calc_dual_mesh(GeometrySet &geometry_set,
}
Mesh *mesh_out = BKE_mesh_new_nomain(
vertex_positions.size(), new_edges.size(), 0, loops.size(), loop_lengths.size());
- MeshComponent out_component;
- out_component.replace(mesh_out, GeometryOwnershipType::Editable);
transfer_attributes(attributes,
vertex_types,
keep_boundaries,
new_to_old_edges_map,
new_to_old_face_corners_map,
boundary_vertex_to_relevant_face_map,
- in_component,
- out_component);
+ mesh_in.attributes(),
+ mesh_out->attributes_for_write());
+
+ MutableSpan<MVert> dst_verts = mesh_out->verts_for_write();
+ MutableSpan<MEdge> dst_edges = mesh_out->edges_for_write();
+ MutableSpan<MPoly> dst_polys = mesh_out->polys_for_write();
+ MutableSpan<MLoop> dst_loops = mesh_out->loops_for_write();
int loop_start = 0;
for (const int i : IndexRange(mesh_out->totpoly)) {
- mesh_out->mpoly[i].loopstart = loop_start;
- mesh_out->mpoly[i].totloop = loop_lengths[i];
+ dst_polys[i].loopstart = loop_start;
+ dst_polys[i].totloop = loop_lengths[i];
loop_start += loop_lengths[i];
}
for (const int i : IndexRange(mesh_out->totloop)) {
- mesh_out->mloop[i].v = loops[i];
- mesh_out->mloop[i].e = loop_edges[i];
+ dst_loops[i].v = loops[i];
+ dst_loops[i].e = loop_edges[i];
}
for (const int i : IndexRange(mesh_out->totvert)) {
- copy_v3_v3(mesh_out->mvert[i].co, vertex_positions[i]);
+ copy_v3_v3(dst_verts[i].co, vertex_positions[i]);
}
- memcpy(mesh_out->medge, new_edges.data(), sizeof(MEdge) * new_edges.size());
+ dst_edges.copy_from(new_edges);
geometry_set.replace_mesh(mesh_out);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
index 41136060ab7..d2a3c339301 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc
@@ -141,15 +141,14 @@ static void threaded_id_offset_copy(const Span<int> offsets,
}
/** Create the copy indices for the duplication domain. */
-static void create_duplicate_index_attribute(GeometryComponent &component,
+static void create_duplicate_index_attribute(bke::MutableAttributeAccessor attributes,
const eAttrDomain output_domain,
const IndexMask selection,
const IndexAttributes &attribute_outputs,
const Span<int> offsets)
{
- SpanAttributeWriter<int> duplicate_indices =
- component.attributes_for_write()->lookup_or_add_for_write_only_span<int>(
- attribute_outputs.duplicate_index.get(), output_domain);
+ SpanAttributeWriter<int> duplicate_indices = attributes.lookup_or_add_for_write_only_span<int>(
+ attribute_outputs.duplicate_index.get(), output_domain);
for (const int i : IndexRange(selection.size())) {
const IndexRange range = range_for_offsets_index(offsets, i);
MutableSpan<int> indices = duplicate_indices.span.slice(range);
@@ -165,16 +164,15 @@ static void create_duplicate_index_attribute(GeometryComponent &component,
* and the duplicate number. This function is used for the point domain elements.
*/
static void copy_stable_id_point(const Span<int> offsets,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- GAttributeReader src_attribute = src_component.attributes()->lookup("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
@@ -190,24 +188,23 @@ static void copy_attributes_without_id(GeometrySet &geometry_set,
const eAttrDomain domain,
const Span<int> offsets,
const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
const Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, component_type);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute || src_attribute.domain != domain) {
continue;
}
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- attribute_id, out_domain, data_type);
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
@@ -232,19 +229,17 @@ static void copy_attributes_without_id(GeometrySet &geometry_set,
* copied with an offset fill, otherwise a mapping is used.
*/
static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
- const CurveComponent &src_component,
const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<int> curve_offsets,
- bke::CurvesGeometry &dst_curves,
- CurveComponent &dst_component)
+ bke::CurvesGeometry &dst_curves)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_CURVE);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
+ GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -253,7 +248,7 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
+ dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
@@ -296,16 +291,14 @@ static void copy_curve_attributes_without_id(const GeometrySet &geometry_set,
static void copy_stable_id_curves(const bke::CurvesGeometry &src_curves,
const IndexMask selection,
const Span<int> curve_offsets,
- const CurveComponent &src_component,
- bke::CurvesGeometry &dst_curves,
- CurveComponent &dst_component)
+ bke::CurvesGeometry &dst_curves)
{
- GAttributeReader src_attribute = src_component.attributes()->lookup("id");
+ GAttributeReader src_attribute = src_curves.attributes().lookup("id");
if (!src_attribute) {
return;
}
GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
+ dst_curves.attributes_for_write().lookup_or_add_for_write_only_span(
"id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
@@ -335,16 +328,16 @@ static void duplicate_curves(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_curves()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE});
+ GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(geometry_set);
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &curves_id = *src_component.get_for_read();
+ const Curves &curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_CURVE};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -369,6 +362,7 @@ static void duplicate_curves(GeometrySet &geometry_set,
point_offsets.last() = dst_points_num;
Curves *new_curves_id = bke::curves_new_nomain(dst_points_num, dst_curves_num);
+ bke::curves_copy_parameters(curves_id, *new_curves_id);
bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
MutableSpan<int> all_dst_offsets = new_curves.offsets_for_write();
@@ -386,18 +380,16 @@ static void duplicate_curves(GeometrySet &geometry_set,
});
all_dst_offsets.last() = dst_points_num;
- CurveComponent dst_component;
- dst_component.replace(new_curves_id, GeometryOwnershipType::Editable);
+ copy_curve_attributes_without_id(geometry_set, curves, selection, curve_offsets, new_curves);
- copy_curve_attributes_without_id(
- geometry_set, src_component, curves, selection, curve_offsets, new_curves, dst_component);
-
- copy_stable_id_curves(
- curves, selection, curve_offsets, src_component, new_curves, dst_component);
+ copy_stable_id_curves(curves, selection, curve_offsets, new_curves);
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_CURVE, selection, attribute_outputs, curve_offsets);
+ create_duplicate_index_attribute(new_curves.attributes_for_write(),
+ ATTR_DOMAIN_CURVE,
+ selection,
+ attribute_outputs,
+ curve_offsets);
}
geometry_set.replace_curves(new_curves_id);
@@ -419,15 +411,15 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
const Span<int> loop_mapping,
const Span<int> offsets,
const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_MESH);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -435,9 +427,8 @@ static void copy_face_attributes_without_id(GeometrySet &geometry_set,
eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- attribute_id, out_domain, data_type);
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
@@ -479,16 +470,15 @@ static void copy_stable_id_faces(const Mesh &mesh,
const IndexMask selection,
const Span<int> poly_offsets,
const Span<int> vert_mapping,
- const MeshComponent &src_component,
- MeshComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- GAttributeReader src_attribute = src_component.attributes()->lookup("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
@@ -496,7 +486,7 @@ static void copy_stable_id_faces(const Mesh &mesh,
VArraySpan<int> src{src_attribute.varray.typed<int>()};
MutableSpan<int> dst = dst_attribute.span.typed<int>();
- Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
+ const Span<MPoly> polys = mesh.polys();
int loop_index = 0;
for (const int i_poly : selection.index_range()) {
const IndexRange range = range_for_offsets_index(poly_offsets, i_poly);
@@ -526,19 +516,18 @@ static void duplicate_faces(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_mesh()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *src_component.get_for_read();
- Span<MVert> verts(mesh.mvert, mesh.totvert);
- Span<MEdge> edges(mesh.medge, mesh.totedge);
- Span<MPoly> polys(mesh.mpoly, mesh.totpoly);
- Span<MLoop> loops(mesh.mloop, mesh.totloop);
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator evaluator(field_context, polys.size());
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -558,10 +547,10 @@ static void duplicate_faces(GeometrySet &geometry_set,
offsets[selection.size()] = total_polys;
Mesh *new_mesh = BKE_mesh_new_nomain(total_loops, total_loops, 0, total_loops, total_polys);
- MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
- MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
- MutableSpan<MLoop> new_loops(new_mesh->mloop, new_mesh->totloop);
- MutableSpan<MPoly> new_poly(new_mesh->mpoly, new_mesh->totpoly);
+ MutableSpan<MVert> new_verts = new_mesh->verts_for_write();
+ MutableSpan<MEdge> new_edges = new_mesh->edges_for_write();
+ MutableSpan<MPoly> new_polys = new_mesh->polys_for_write();
+ MutableSpan<MLoop> new_loops = new_mesh->loops_for_write();
Array<int> vert_mapping(new_verts.size());
Array<int> edge_mapping(new_edges.size());
@@ -574,8 +563,8 @@ static void duplicate_faces(GeometrySet &geometry_set,
const MPoly &source = polys[selection[i_selection]];
for ([[maybe_unused]] const int i_duplicate : IndexRange(poly_range.size())) {
- new_poly[poly_index] = source;
- new_poly[poly_index].loopstart = loop_index;
+ new_polys[poly_index] = source;
+ new_polys[poly_index].loopstart = loop_index;
for (const int i_loops : IndexRange(source.totloop)) {
const MLoop &current_loop = loops[source.loopstart + i_loops];
loop_mapping[loop_index] = source.loopstart + i_loops;
@@ -588,7 +577,7 @@ static void duplicate_faces(GeometrySet &geometry_set,
new_edges[loop_index].v2 = loop_index + 1;
}
else {
- new_edges[loop_index].v2 = new_poly[poly_index].loopstart;
+ new_edges[loop_index].v2 = new_polys[poly_index].loopstart;
}
new_loops[loop_index].v = loop_index;
new_loops[loop_index].e = loop_index;
@@ -598,23 +587,21 @@ static void duplicate_faces(GeometrySet &geometry_set,
}
}
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
-
copy_face_attributes_without_id(geometry_set,
edge_mapping,
vert_mapping,
loop_mapping,
offsets,
selection,
- src_component,
- dst_component);
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_faces(mesh, selection, offsets, vert_mapping, src_component, dst_component);
+ copy_stable_id_faces(
+ mesh, selection, offsets, vert_mapping, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets);
+ new_mesh->attributes_for_write(), ATTR_DOMAIN_FACE, selection, attribute_outputs, offsets);
}
geometry_set.replace_mesh(new_mesh);
@@ -634,15 +621,15 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
const Span<int> point_mapping,
const Span<int> offsets,
const IndexMask selection,
- const GeometryComponent &src_component,
- GeometryComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_MESH);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
+ GAttributeReader src_attribute = src_attributes.lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -650,9 +637,8 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
const eAttrDomain out_domain = src_attribute.domain;
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
- GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- attribute_id, out_domain, data_type);
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ attribute_id, out_domain, data_type);
if (!dst_attribute) {
continue;
}
@@ -683,21 +669,20 @@ static void copy_edge_attributes_without_id(GeometrySet &geometry_set,
static void copy_stable_id_edges(const Mesh &mesh,
const IndexMask selection,
const Span<int> edge_offsets,
- const MeshComponent &src_component,
- MeshComponent &dst_component)
+ const bke::AttributeAccessor src_attributes,
+ bke::MutableAttributeAccessor dst_attributes)
{
- GAttributeReader src_attribute = src_component.attributes()->lookup("id");
+ GAttributeReader src_attribute = src_attributes.lookup("id");
if (!src_attribute) {
return;
}
- GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
+ GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span(
+ "id", ATTR_DOMAIN_POINT, CD_PROP_INT32);
if (!dst_attribute) {
return;
}
- Span<MEdge> edges(mesh.medge, mesh.totedge);
+ const Span<MEdge> edges = mesh.edges();
VArraySpan<int> src{src_attribute.varray.typed<int>()};
MutableSpan<int> dst = dst_attribute.span.typed<int>();
@@ -727,15 +712,13 @@ static void duplicate_edges(GeometrySet &geometry_set,
const IndexAttributes &attribute_outputs)
{
if (!geometry_set.has_mesh()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
};
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
- const Mesh &mesh = *src_component.get_for_read();
- Span<MVert> verts(mesh.mvert, mesh.totvert);
- Span<MEdge> edges(mesh.medge, mesh.totedge);
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ const Span<MEdge> edges = mesh.edges();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator evaluator{field_context, edges.size()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -746,8 +729,7 @@ static void duplicate_edges(GeometrySet &geometry_set,
Array<int> edge_offsets = accumulate_counts_to_offsets(selection, counts);
Mesh *new_mesh = BKE_mesh_new_nomain(edge_offsets.last() * 2, edge_offsets.last(), 0, 0, 0);
- MutableSpan<MVert> new_verts(new_mesh->mvert, new_mesh->totvert);
- MutableSpan<MEdge> new_edges(new_mesh->medge, new_mesh->totedge);
+ MutableSpan<MEdge> new_edges = new_mesh->edges_for_write();
Array<int> vert_orig_indices(edge_offsets.last() * 2);
threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) {
@@ -776,17 +758,22 @@ static void duplicate_edges(GeometrySet &geometry_set,
}
});
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
-
- copy_edge_attributes_without_id(
- geometry_set, vert_orig_indices, edge_offsets, selection, src_component, dst_component);
+ copy_edge_attributes_without_id(geometry_set,
+ vert_orig_indices,
+ edge_offsets,
+ selection,
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_edges(mesh, selection, edge_offsets, src_component, dst_component);
+ copy_stable_id_edges(
+ mesh, selection, edge_offsets, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_EDGE, selection, attribute_outputs, edge_offsets);
+ create_duplicate_index_attribute(new_mesh->attributes_for_write(),
+ ATTR_DOMAIN_EDGE,
+ selection,
+ attribute_outputs,
+ edge_offsets);
}
geometry_set.replace_mesh(new_mesh);
@@ -803,14 +790,13 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const CurveComponent &src_component = *geometry_set.get_component_for_read<CurveComponent>();
- const Curves &src_curves_id = *src_component.get_for_read();
+ const Curves &src_curves_id = *geometry_set.get_curves_for_read();
const bke::CurvesGeometry &src_curves = bke::CurvesGeometry::wrap(src_curves_id.geometry);
if (src_curves.points_num() == 0) {
return;
}
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{src_curves, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{field_context, src_curves.points_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -830,6 +816,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
});
Curves *new_curves_id = bke::curves_new_nomain(dst_num, dst_num);
+ bke::curves_copy_parameters(src_curves_id, *new_curves_id);
bke::CurvesGeometry &new_curves = bke::CurvesGeometry::wrap(new_curves_id->geometry);
MutableSpan<int> new_curve_offsets = new_curves.offsets_for_write();
for (const int i : new_curves.curves_range()) {
@@ -837,15 +824,12 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
}
new_curve_offsets.last() = dst_num;
- CurveComponent dst_component;
- dst_component.replace(new_curves_id, GeometryOwnershipType::Editable);
-
Map<AttributeIDRef, AttributeKind> attributes = gather_attributes_without_id(
geometry_set, GEO_COMPONENT_TYPE_CURVE);
for (const Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
- GAttributeReader src_attribute = src_component.attributes()->lookup(attribute_id);
+ GAttributeReader src_attribute = src_curves.attributes().lookup(attribute_id);
if (!src_attribute) {
continue;
}
@@ -854,7 +838,7 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(
src_attribute.varray.type());
GSpanAttributeWriter dst_attribute =
- dst_component.attributes_for_write()->lookup_or_add_for_write_only_span(
+ new_curves.attributes_for_write().lookup_or_add_for_write_only_span(
attribute_id, domain, data_type);
if (!dst_attribute) {
continue;
@@ -885,11 +869,14 @@ static void duplicate_points_curve(GeometrySet &geometry_set,
dst_attribute.finish();
}
- copy_stable_id_point(offsets, src_component, dst_component);
+ copy_stable_id_point(offsets, src_curves.attributes(), new_curves.attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets.as_span());
+ create_duplicate_index_attribute(new_curves.attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets.as_span());
}
geometry_set.replace_curves(new_curves_id);
@@ -906,11 +893,10 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const MeshComponent &src_component = *geometry_set.get_component_for_read<MeshComponent>();
const Mesh &mesh = *geometry_set.get_mesh_for_read();
- Span<MVert> src_verts(mesh.mvert, mesh.totvert);
+ const Span<MVert> src_verts = mesh.verts();
- GeometryComponentFieldContext field_context{src_component, ATTR_DOMAIN_POINT};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{field_context, src_verts.size()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -921,25 +907,26 @@ static void duplicate_points_mesh(GeometrySet &geometry_set,
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
Mesh *new_mesh = BKE_mesh_new_nomain(offsets.last(), 0, 0, 0, 0);
- MutableSpan<MVert> dst_verts(new_mesh->mvert, new_mesh->totvert);
+ MutableSpan<MVert> dst_verts = new_mesh->verts_for_write();
threaded_slice_fill(offsets.as_span(), selection, src_verts, dst_verts);
- MeshComponent dst_component;
- dst_component.replace(new_mesh, GeometryOwnershipType::Editable);
copy_attributes_without_id(geometry_set,
GEO_COMPONENT_TYPE_MESH,
ATTR_DOMAIN_POINT,
offsets,
selection,
- src_component,
- dst_component);
+ mesh.attributes(),
+ new_mesh->attributes_for_write());
- copy_stable_id_point(offsets, src_component, dst_component);
+ copy_stable_id_point(offsets, mesh.attributes(), new_mesh->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets.as_span());
+ create_duplicate_index_attribute(new_mesh->attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets.as_span());
}
geometry_set.replace_mesh(new_mesh);
@@ -956,12 +943,10 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
const Field<bool> &selection_field,
const IndexAttributes &attribute_outputs)
{
- const PointCloudComponent &src_points =
- *geometry_set.get_component_for_read<PointCloudComponent>();
- const int point_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
+ const PointCloud &src_points = *geometry_set.get_pointcloud_for_read();
- GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{field_context, point_num};
+ bke::PointCloudFieldContext field_context{src_points};
+ FieldEvaluator evaluator{field_context, src_points.totpoint};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
evaluator.evaluate();
@@ -971,22 +956,23 @@ static void duplicate_points_pointcloud(GeometrySet &geometry_set,
Array<int> offsets = accumulate_counts_to_offsets(selection, counts);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(offsets.last());
- PointCloudComponent dst_component;
- dst_component.replace(pointcloud, GeometryOwnershipType::Editable);
copy_attributes_without_id(geometry_set,
GEO_COMPONENT_TYPE_POINT_CLOUD,
ATTR_DOMAIN_POINT,
offsets,
selection,
- src_points,
- dst_component);
+ src_points.attributes(),
+ pointcloud->attributes_for_write());
- copy_stable_id_point(offsets, src_points, dst_component);
+ copy_stable_id_point(offsets, src_points.attributes(), pointcloud->attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_component, ATTR_DOMAIN_POINT, selection, attribute_outputs, offsets);
+ create_duplicate_index_attribute(pointcloud->attributes_for_write(),
+ ATTR_DOMAIN_POINT,
+ selection,
+ attribute_outputs,
+ offsets);
}
geometry_set.replace_pointcloud(pointcloud);
}
@@ -1026,7 +1012,7 @@ static void duplicate_points(GeometrySet &geometry_set,
}
}
component_types.append(GEO_COMPONENT_TYPE_INSTANCES);
- geometry_set.keep_only(component_types);
+ geometry_set.keep_only_during_modify(component_types);
}
/** \} */
@@ -1048,7 +1034,7 @@ static void duplicate_instances(GeometrySet &geometry_set,
const InstancesComponent &src_instances =
*geometry_set.get_component_for_read<InstancesComponent>();
- GeometryComponentFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
+ bke::GeometryFieldContext field_context{src_instances, ATTR_DOMAIN_INSTANCE};
FieldEvaluator evaluator{field_context, src_instances.instances_num()};
evaluator.add(count_field);
evaluator.set_selection(selection_field);
@@ -1083,12 +1069,15 @@ static void duplicate_instances(GeometrySet &geometry_set,
ATTR_DOMAIN_INSTANCE,
offsets,
selection,
- src_instances,
- dst_instances);
+ *src_instances.attributes(),
+ *dst_instances.attributes_for_write());
if (attribute_outputs.duplicate_index) {
- create_duplicate_index_attribute(
- dst_instances, ATTR_DOMAIN_INSTANCE, selection, attribute_outputs, offsets);
+ create_duplicate_index_attribute(*dst_instances.attributes_for_write(),
+ ATTR_DOMAIN_INSTANCE,
+ selection,
+ attribute_outputs,
+ offsets);
}
geometry_set = std::move(dst_geometry);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc
new file mode 100644
index 00000000000..ba09acf0bf0
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_curves.cc
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_curves.hh"
+
+#include "DNA_mesh_types.h"
+
+#include "GEO_mesh_to_curve.hh"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_edge_paths_to_curves_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH);
+ b.add_input<decl::Bool>(N_("Start Vertices")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Next Vertex Index")).default_value(-1).hide_value().supports_field();
+ b.add_output<decl::Geometry>(N_("Curves"));
+}
+
+static Curves *edge_paths_to_curves_convert(const Mesh &mesh,
+ const IndexMask start_verts_mask,
+ const Span<int> next_indices)
+{
+ Vector<int> vert_indices;
+ Vector<int> curve_offsets;
+ Array<bool> visited(mesh.totvert, false);
+ for (const int first_vert : start_verts_mask) {
+ const int second_vert = next_indices[first_vert];
+ if (first_vert == second_vert) {
+ continue;
+ }
+ if (second_vert < 0 || second_vert >= mesh.totvert) {
+ continue;
+ }
+
+ curve_offsets.append(vert_indices.size());
+
+ /* Iterate through path defined by #next_indices. */
+ int current_vert = first_vert;
+ while (!visited[current_vert]) {
+ visited[current_vert] = true;
+ vert_indices.append(current_vert);
+ const int next_vert = next_indices[current_vert];
+ if (next_vert < 0 || next_vert >= mesh.totvert) {
+ break;
+ }
+ current_vert = next_vert;
+ }
+
+ /* Reset visited status. */
+ const int points_in_curve_num = vert_indices.size() - curve_offsets.last();
+ for (const int vert_in_curve : vert_indices.as_span().take_back(points_in_curve_num)) {
+ visited[vert_in_curve] = false;
+ }
+ }
+
+ if (vert_indices.is_empty()) {
+ return nullptr;
+ }
+ Curves *curves_id = bke::curves_new_nomain(
+ geometry::create_curve_from_vert_indices(mesh, vert_indices, curve_offsets, IndexRange(0)));
+ return curves_id;
+}
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
+
+ geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, mesh->totvert};
+ evaluator.add(params.get_input<Field<int>>("Next Vertex Index"));
+ evaluator.add(params.get_input<Field<bool>>("Start Vertices"));
+ evaluator.evaluate();
+ const VArraySpan<int> next_vert = evaluator.get_evaluated<int>(0);
+ IndexMask start_verts = evaluator.get_evaluated_as_mask(1);
+
+ if (start_verts.is_empty()) {
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ return;
+ }
+
+ geometry_set.replace_curves(edge_paths_to_curves_convert(*mesh, start_verts, next_vert));
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ });
+
+ params.set_output("Curves", std::move(geometry_set));
+}
+
+} // namespace blender::nodes::node_geo_edge_paths_to_curves_cc
+
+void register_node_type_geo_edge_paths_to_curves()
+{
+ namespace file_ns = blender::nodes::node_geo_edge_paths_to_curves_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_EDGE_PATHS_TO_CURVES, "Edge Paths to Curves", NODE_CLASS_GEOMETRY);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc
new file mode 100644
index 00000000000..9ef9ee8ad6e
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_paths_to_selection.cc
@@ -0,0 +1,134 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "BKE_attribute_math.hh"
+#include "BKE_mesh.h"
+
+#include "BLI_map.hh"
+#include "BLI_set.hh"
+#include "BLI_task.hh"
+
+#include "node_geometry_util.hh"
+
+#include <set>
+
+namespace blender::nodes::node_geo_edge_paths_to_selection_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Bool>(N_("Start Vertices")).default_value(true).hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Next Vertex Index")).default_value(-1).hide_value().supports_field();
+ b.add_output<decl::Bool>(N_("Selection")).field_source();
+}
+
+static void edge_paths_to_selection(const Mesh &src_mesh,
+ const IndexMask start_selection,
+ const Span<int> next_indices,
+ MutableSpan<bool> r_selection)
+{
+ const Span<MEdge> edges = src_mesh.edges();
+
+ Array<bool> selection(src_mesh.totvert, false);
+
+ for (const int start_vert : start_selection) {
+ selection[start_vert] = true;
+ }
+
+ for (const int start_i : start_selection) {
+ int iter = start_i;
+ while (iter != next_indices[iter] && !selection[next_indices[iter]]) {
+ if (next_indices[iter] < 0 || next_indices[iter] >= src_mesh.totvert) {
+ break;
+ }
+ selection[next_indices[iter]] = true;
+ iter = next_indices[iter];
+ }
+ }
+
+ for (const int i : edges.index_range()) {
+ const MEdge &edge = edges[i];
+ if ((selection[edge.v1] && selection[edge.v2]) &&
+ (edge.v1 == next_indices[edge.v2] || edge.v2 == next_indices[edge.v1])) {
+ r_selection[i] = true;
+ }
+ }
+}
+
+class PathToEdgeSelectionFieldInput final : public bke::MeshFieldInput {
+ private:
+ Field<bool> start_vertices_;
+ Field<int> next_vertex_;
+
+ public:
+ PathToEdgeSelectionFieldInput(Field<bool> start_verts, Field<int> next_vertex)
+ : bke::MeshFieldInput(CPPType::get<bool>(), "Edge Selection"),
+ start_vertices_(start_verts),
+ next_vertex_(next_vertex)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ const IndexMask /*mask*/) const final
+ {
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{context, mesh.totvert};
+ evaluator.add(next_vertex_);
+ evaluator.add(start_vertices_);
+ evaluator.evaluate();
+ const VArraySpan<int> next_vert = evaluator.get_evaluated<int>(0);
+ const IndexMask start_verts = evaluator.get_evaluated_as_mask(1);
+
+ if (start_verts.is_empty()) {
+ return {};
+ }
+
+ Array<bool> selection(mesh.totedge, false);
+ MutableSpan<bool> selection_span = selection.as_mutable_span();
+
+ edge_paths_to_selection(mesh, start_verts, next_vert, selection_span);
+
+ return mesh.attributes().adapt_domain<bool>(
+ VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_EDGE, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(start_vertices_, next_vertex_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const PathToEdgeSelectionFieldInput *other_field =
+ dynamic_cast<const PathToEdgeSelectionFieldInput *>(&other)) {
+ return other_field->start_vertices_ == start_vertices_ &&
+ other_field->next_vertex_ == next_vertex_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<bool> start_verts = params.extract_input<Field<bool>>("Start Vertices");
+ Field<int> next_vertex = params.extract_input<Field<int>>("Next Vertex Index");
+ Field<bool> selection_field{
+ std::make_shared<PathToEdgeSelectionFieldInput>(start_verts, next_vertex)};
+ params.set_output("Selection", std::move(selection_field));
+}
+
+} // namespace blender::nodes::node_geo_edge_paths_to_selection_cc
+
+void register_node_type_geo_edge_paths_to_selection()
+{
+ namespace file_ns = blender::nodes::node_geo_edge_paths_to_selection_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_EDGE_PATHS_TO_SELECTION, "Edge Paths to Selection", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ node_type_size(&ntype, 150, 100, 300);
+ ntype.geometry_node_execute = file_ns::node_geo_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 84acab47661..0b4d5bd53f3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
@@ -51,19 +53,18 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_mesh()) {
- return;
- }
+ if (const Mesh *mesh = geometry_set.get_mesh_for_write()) {
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
- const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- fn::FieldEvaluator selection_evaluator{field_context, domain_size};
- selection_evaluator.add(selection_field);
- selection_evaluator.evaluate();
- const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
+ bke::MeshFieldContext field_context{*mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator selection_evaluator{field_context, mesh->totedge};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
- geometry_set.replace_mesh(mesh_edge_split(*mesh_component.get_for_read(), selection));
+ Mesh *result = mesh_edge_split(*mesh, selection);
+
+ geometry_set.replace_mesh(result);
+ }
});
params.set_output("Mesh", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
index baa9952b950..dc5b778b4bd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc
@@ -61,15 +61,15 @@ struct AttributeOutputs {
StrongAnonymousAttributeID side_id;
};
-static void save_selection_as_attribute(MeshComponent &component,
+static void save_selection_as_attribute(Mesh &mesh,
const AnonymousAttributeID *id,
const eAttrDomain domain,
const IndexMask selection)
{
- BLI_assert(!component.attributes()->contains(id));
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ BLI_assert(!attributes.contains(id));
- SpanAttributeWriter<bool> attribute =
- component.attributes_for_write()->lookup_or_add_for_write_span<bool>(id, domain);
+ SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_span<bool>(id, domain);
/* Rely on the new attribute being zeroed by default. */
BLI_assert(!attribute.span.as_span().contains(true));
@@ -83,31 +83,6 @@ static void save_selection_as_attribute(MeshComponent &component,
attribute.finish();
}
-static MutableSpan<MVert> mesh_verts(Mesh &mesh)
-{
- return {mesh.mvert, mesh.totvert};
-}
-static MutableSpan<MEdge> mesh_edges(Mesh &mesh)
-{
- return {mesh.medge, mesh.totedge};
-}
-static Span<MPoly> mesh_polys(const Mesh &mesh)
-{
- return {mesh.mpoly, mesh.totpoly};
-}
-static MutableSpan<MPoly> mesh_polys(Mesh &mesh)
-{
- return {mesh.mpoly, mesh.totpoly};
-}
-static Span<MLoop> mesh_loops(const Mesh &mesh)
-{
- return {mesh.mloop, mesh.totloop};
-}
-static MutableSpan<MLoop> mesh_loops(Mesh &mesh)
-{
- return {mesh.mloop, mesh.totloop};
-}
-
/**
* \note Some areas in this file rely on the new sections of attributes from #CustomData_realloc
* to be zeroed.
@@ -119,30 +94,25 @@ static void expand_mesh(Mesh &mesh,
const int loop_expand)
{
if (vert_expand != 0) {
- CustomData_duplicate_referenced_layers(&mesh.vdata, mesh.totvert);
+ const int old_verts_num = mesh.totvert;
mesh.totvert += vert_expand;
- CustomData_realloc(&mesh.vdata, mesh.totvert);
- }
- else {
- /* Even when the number of vertices is not changed, the mesh can still be deformed. */
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert);
+ CustomData_realloc(&mesh.vdata, old_verts_num, mesh.totvert);
}
if (edge_expand != 0) {
- CustomData_duplicate_referenced_layers(&mesh.edata, mesh.totedge);
+ const int old_edges_num = mesh.totedge;
mesh.totedge += edge_expand;
- CustomData_realloc(&mesh.edata, mesh.totedge);
+ CustomData_realloc(&mesh.edata, old_edges_num, mesh.totedge);
}
if (poly_expand != 0) {
- CustomData_duplicate_referenced_layers(&mesh.pdata, mesh.totpoly);
+ const int old_polys_num = mesh.totpoly;
mesh.totpoly += poly_expand;
- CustomData_realloc(&mesh.pdata, mesh.totpoly);
+ CustomData_realloc(&mesh.pdata, old_polys_num, mesh.totpoly);
}
if (loop_expand != 0) {
- CustomData_duplicate_referenced_layers(&mesh.ldata, mesh.totloop);
+ const int old_loops_num = mesh.totloop;
mesh.totloop += loop_expand;
- CustomData_realloc(&mesh.ldata, mesh.totloop);
+ CustomData_realloc(&mesh.ldata, old_loops_num, mesh.totloop);
}
- BKE_mesh_update_customdata_pointers(&mesh, false);
}
static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain)
@@ -162,13 +132,15 @@ static CustomData &get_customdata(Mesh &mesh, const eAttrDomain domain)
}
}
+/**
+ * \note The result may be an empty span.
+ */
static MutableSpan<int> get_orig_index_layer(Mesh &mesh, const eAttrDomain domain)
{
- MeshComponent component;
- component.replace(&mesh, GeometryOwnershipType::ReadOnly);
+ const bke::AttributeAccessor attributes = mesh.attributes();
CustomData &custom_data = get_customdata(mesh, domain);
if (int *orig_indices = static_cast<int *>(CustomData_get_layer(&custom_data, CD_ORIGINDEX))) {
- return {orig_indices, component.attribute_domain_size(domain)};
+ return {orig_indices, attributes.domain_size(domain)};
}
return {};
}
@@ -178,6 +150,7 @@ static MEdge new_edge(const int v1, const int v2)
MEdge edge;
edge.v1 = v1;
edge.v2 = v2;
+ edge.crease = 0;
edge.flag = (ME_EDGEDRAW | ME_EDGERENDER);
return edge;
}
@@ -187,6 +160,7 @@ static MEdge new_loose_edge(const int v1, const int v2)
MEdge edge;
edge.v1 = v1;
edge.v2 = v2;
+ edge.crease = 0;
edge.flag = ME_LOOSEEDGE;
return edge;
}
@@ -226,7 +200,7 @@ template<typename T, typename GetMixIndicesFn>
void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn)
{
threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) {
- attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)};
+ attribute_math::DefaultPropagationMixer<T> mixer{dst.slice(range)};
for (const int i_dst : IndexRange(range.size())) {
for (const int i_src : get_mix_indices_fn(range[i_dst])) {
mixer.mix_in(i_dst, src[i_src]);
@@ -248,16 +222,15 @@ static Array<Vector<int>> create_vert_to_edge_map(const int vert_size,
return vert_to_edge_map;
}
-static void extrude_mesh_vertices(MeshComponent &component,
+static void extrude_mesh_vertices(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
const AttributeOutputs &attribute_outputs)
{
- Mesh &mesh = *component.get_for_write();
const int orig_vert_size = mesh.totvert;
const int orig_edge_size = mesh.totedge;
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_POINT};
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add(offset_field);
evaluator.set_selection(selection_field);
@@ -266,21 +239,21 @@ static void extrude_mesh_vertices(MeshComponent &component,
const VArray<float3> offsets = evaluator.get_evaluated<float3>(0);
/* This allows parallelizing attribute mixing for new edges. */
- Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh_edges(mesh));
+ Array<Vector<int>> vert_to_edge_map = create_vert_to_edge_map(orig_vert_size, mesh.edges());
expand_mesh(mesh, selection.size(), selection.size(), 0, 0);
const IndexRange new_vert_range{orig_vert_size, selection.size()};
const IndexRange new_edge_range{orig_edge_size, selection.size()};
- MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
- MutableSpan<MEdge> new_edges = mesh_edges(mesh).slice(new_edge_range);
+ MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
+ MutableSpan<MEdge> new_edges = mesh.edges_for_write().slice(new_edge_range);
for (const int i_selection : selection.index_range()) {
new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]);
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
if (!ELEM(meta_data.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE)) {
@@ -318,6 +291,7 @@ static void extrude_mesh_vertices(MeshComponent &component,
for (const int i : range) {
const float3 offset = offsets[selection[i]];
add_v3_v3(new_verts[i].co, offset);
+ new_verts[i].flag = 0;
}
});
});
@@ -325,13 +299,16 @@ static void extrude_mesh_vertices(MeshComponent &component,
MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT);
vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE);
+ MutableSpan<int> new_edge_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_EDGE);
+ new_edge_orig_indices.slice(new_edge_range).fill(ORIGINDEX_NONE);
+
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range);
+ mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_POINT, new_vert_range);
}
if (attribute_outputs.side_id) {
save_selection_as_attribute(
- component, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_EDGE, new_edge_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -339,8 +316,8 @@ static void extrude_mesh_vertices(MeshComponent &component,
static Array<Vector<int, 2>> mesh_calculate_polys_of_edge(const Mesh &mesh)
{
- Span<MPoly> polys = mesh_polys(mesh);
- Span<MLoop> loops = mesh_loops(mesh);
+ Span<MPoly> polys = mesh.polys();
+ Span<MLoop> loops = mesh.loops();
Array<Vector<int, 2>> polys_of_edge(mesh.totedge);
for (const int i_poly : polys.index_range()) {
@@ -398,29 +375,29 @@ template<typename T>
static VectorSet<int> vert_indices_from_edges(const Mesh &mesh, const Span<T> edge_indices)
{
static_assert(is_same_any_v<T, int, int64_t>);
+ const Span<MEdge> edges = mesh.edges();
VectorSet<int> vert_indices;
vert_indices.reserve(edge_indices.size());
for (const T i_edge : edge_indices) {
- const MEdge &edge = mesh.medge[i_edge];
+ const MEdge &edge = edges[i_edge];
vert_indices.add(edge.v1);
vert_indices.add(edge.v2);
}
return vert_indices;
}
-static void extrude_mesh_edges(MeshComponent &component,
+static void extrude_mesh_edges(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
const AttributeOutputs &attribute_outputs)
{
- Mesh &mesh = *component.get_for_write();
const int orig_vert_size = mesh.totvert;
- Span<MEdge> orig_edges = mesh_edges(mesh);
- Span<MPoly> orig_polys = mesh_polys(mesh);
+ Span<MEdge> orig_edges = mesh.edges();
+ Span<MPoly> orig_polys = mesh.polys();
const int orig_loop_size = mesh.totloop;
- GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
edge_evaluator.set_selection(selection_field);
edge_evaluator.add(offset_field);
@@ -438,7 +415,7 @@ static void extrude_mesh_edges(MeshComponent &component,
Array<float3> vert_offsets;
if (!edge_offsets.is_single()) {
vert_offsets.reinitialize(orig_vert_size);
- attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
+ attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets);
for (const int i_edge : edge_selection) {
const MEdge &edge = orig_edges[i_edge];
const float3 offset = edge_offsets[i_edge];
@@ -466,12 +443,12 @@ static void extrude_mesh_edges(MeshComponent &component,
new_poly_range.size(),
new_loop_range.size());
- MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
- MutableSpan<MEdge> connect_edges = mesh_edges(mesh).slice(connect_edge_range);
- MutableSpan<MEdge> duplicate_edges = mesh_edges(mesh).slice(duplicate_edge_range);
- MutableSpan<MPoly> polys = mesh_polys(mesh);
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
+ MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
+ MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range);
+ MutableSpan<MPoly> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(new_poly_range);
- MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
MutableSpan<MLoop> new_loops = loops.slice(new_loop_range);
for (const int i : connect_edges.index_range()) {
@@ -479,7 +456,7 @@ static void extrude_mesh_edges(MeshComponent &component,
}
for (const int i : duplicate_edges.index_range()) {
- const MEdge &orig_edge = mesh.medge[edge_selection[i]];
+ const MEdge &orig_edge = edges[edge_selection[i]];
const int i_new_vert_1 = new_vert_indices.index_of(orig_edge.v1);
const int i_new_vert_2 = new_vert_indices.index_of(orig_edge.v2);
duplicate_edges[i] = new_edge(new_vert_range[i_new_vert_1], new_vert_range[i_new_vert_2]);
@@ -526,7 +503,7 @@ static void extrude_mesh_edges(MeshComponent &component,
const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
new_vert_range.size(), duplicate_edges, orig_vert_size);
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
@@ -584,7 +561,7 @@ static void extrude_mesh_edges(MeshComponent &component,
/* Both corners on each vertical edge of the side polygon get the same value,
* so there are only two unique values to mix. */
Array<T> side_poly_corner_data(2);
- attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data};
+ attribute_math::DefaultPropagationMixer<T> mixer{side_poly_corner_data};
const MEdge &duplicate_edge = duplicate_edges[i_edge_selection];
const int new_vert_1 = duplicate_edge.v1;
@@ -634,11 +611,13 @@ static void extrude_mesh_edges(MeshComponent &component,
return true;
});
+ MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
if (edge_offsets.is_single()) {
const float3 offset = edge_offsets.get_internal_single();
threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
add_v3_v3(new_verts[i].co, offset);
+ new_verts[i].flag = 0;
}
});
}
@@ -646,6 +625,7 @@ static void extrude_mesh_edges(MeshComponent &component,
threading::parallel_for(new_verts.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
add_v3_v3(new_verts[i].co, vert_offsets[new_vert_indices[i]]);
+ new_verts[i].flag = 0;
}
});
}
@@ -657,13 +637,16 @@ static void extrude_mesh_edges(MeshComponent &component,
edge_orig_indices.slice(connect_edge_range).fill(ORIGINDEX_NONE);
edge_orig_indices.slice(duplicate_edge_range).fill(ORIGINDEX_NONE);
+ MutableSpan<int> poly_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_FACE);
+ poly_orig_indices.slice(new_poly_range).fill(ORIGINDEX_NONE);
+
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range);
+ mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_EDGE, duplicate_edge_range);
}
if (attribute_outputs.side_id) {
save_selection_as_attribute(
- component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, new_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -673,18 +656,17 @@ static void extrude_mesh_edges(MeshComponent &component,
* Edges connected to one selected face are on the boundary of a region and will be duplicated into
* a "side face". Edges inside a region will be duplicated to leave any original faces unchanged.
*/
-static void extrude_mesh_face_regions(MeshComponent &component,
+static void extrude_mesh_face_regions(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
const AttributeOutputs &attribute_outputs)
{
- Mesh &mesh = *component.get_for_write();
const int orig_vert_size = mesh.totvert;
- Span<MEdge> orig_edges = mesh_edges(mesh);
- Span<MPoly> orig_polys = mesh_polys(mesh);
- Span<MLoop> orig_loops = mesh_loops(mesh);
+ Span<MEdge> orig_edges = mesh.edges();
+ Span<MPoly> orig_polys = mesh.polys();
+ Span<MLoop> orig_loops = mesh.loops();
- GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext poly_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
poly_evaluator.set_selection(selection_field);
poly_evaluator.add(offset_field);
@@ -706,7 +688,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
Array<float3> vert_offsets;
if (!poly_offsets.is_single()) {
vert_offsets.reinitialize(orig_vert_size);
- attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets);
+ attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets);
for (const int i_poly : poly_selection) {
const MPoly &poly = orig_polys[i_poly];
const float3 offset = poly_offsets[i_poly];
@@ -785,7 +767,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
/* The vertices attached to duplicate inner edges also have to be duplicated. */
for (const int i_edge : new_inner_edge_indices) {
- const MEdge &edge = mesh.medge[i_edge];
+ const MEdge &edge = orig_edges[i_edge];
new_vert_indices.add(edge.v1);
new_vert_indices.add(edge.v2);
}
@@ -809,13 +791,13 @@ static void extrude_mesh_face_regions(MeshComponent &component,
side_poly_range.size(),
side_loop_range.size());
- MutableSpan<MEdge> edges = mesh_edges(mesh);
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
MutableSpan<MEdge> boundary_edges = edges.slice(boundary_edge_range);
MutableSpan<MEdge> new_inner_edges = edges.slice(new_inner_edge_range);
- MutableSpan<MPoly> polys = mesh_polys(mesh);
+ MutableSpan<MPoly> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
- MutableSpan<MLoop> loops = mesh_loops(mesh);
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
MutableSpan<MLoop> new_loops = loops.slice(side_loop_range);
/* Initialize the edges that form the sides of the extrusion. */
@@ -906,7 +888,7 @@ static void extrude_mesh_face_regions(MeshComponent &component,
const Array<Vector<int>> new_vert_to_duplicate_edge_map = create_vert_to_edge_map(
new_vert_range.size(), boundary_edges, orig_vert_size);
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
@@ -1004,13 +986,14 @@ static void extrude_mesh_face_regions(MeshComponent &component,
/* Translate vertices based on the offset. If the vertex is used by a selected edge, it will
* have been duplicated and only the new vertex should use the offset. Otherwise the vertex might
* still need an offset, but it was reused on the inside of a region of extruded faces. */
+ MutableSpan<MVert> verts = mesh.verts_for_write();
if (poly_offsets.is_single()) {
const float3 offset = poly_offsets.get_internal_single();
threading::parallel_for(
IndexRange(all_selected_verts.size()), 1024, [&](const IndexRange range) {
for (const int i_orig : all_selected_verts.as_span().slice(range)) {
const int i_new = new_vert_indices.index_of_try(i_orig);
- MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
+ MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]];
add_v3_v3(vert.co, offset);
}
});
@@ -1021,12 +1004,16 @@ static void extrude_mesh_face_regions(MeshComponent &component,
for (const int i_orig : all_selected_verts.as_span().slice(range)) {
const int i_new = new_vert_indices.index_of_try(i_orig);
const float3 offset = vert_offsets[i_orig];
- MVert &vert = mesh_verts(mesh)[(i_new == -1) ? i_orig : new_vert_range[i_new]];
+ MVert &vert = verts[(i_new == -1) ? i_orig : new_vert_range[i_new]];
add_v3_v3(vert.co, offset);
}
});
}
+ for (MVert &vert : verts.slice(new_vert_range)) {
+ vert.flag = 0;
+ }
+
MutableSpan<int> vert_orig_indices = get_orig_index_layer(mesh, ATTR_DOMAIN_POINT);
vert_orig_indices.slice(new_vert_range).fill(ORIGINDEX_NONE);
@@ -1040,11 +1027,11 @@ static void extrude_mesh_face_regions(MeshComponent &component,
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
}
if (attribute_outputs.side_id) {
save_selection_as_attribute(
- component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -1058,21 +1045,20 @@ static IndexRange selected_corner_range(Span<int> offsets, const int index)
return IndexRange(offset, next_offset - offset);
}
-static void extrude_individual_mesh_faces(MeshComponent &component,
+static void extrude_individual_mesh_faces(Mesh &mesh,
const Field<bool> &selection_field,
const Field<float3> &offset_field,
const AttributeOutputs &attribute_outputs)
{
- Mesh &mesh = *component.get_for_write();
const int orig_vert_size = mesh.totvert;
const int orig_edge_size = mesh.totedge;
- Span<MPoly> orig_polys = mesh_polys(mesh);
- Span<MLoop> orig_loops = mesh_loops(mesh);
+ Span<MPoly> orig_polys = mesh.polys();
+ Span<MLoop> orig_loops = mesh.loops();
/* Use a mesh for the result of the evaluation because the mesh is reallocated before
* the vertices are moved, and the evaluated result might reference an attribute. */
Array<float3> poly_offset(orig_polys.size());
- GeometryComponentFieldContext poly_context{component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext poly_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator poly_evaluator{poly_context, mesh.totpoly};
poly_evaluator.set_selection(selection_field);
poly_evaluator.add_with_destination(offset_field, poly_offset.as_mutable_span());
@@ -1106,13 +1092,13 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
side_poly_range.size(),
side_loop_range.size());
- MutableSpan<MVert> new_verts = mesh_verts(mesh).slice(new_vert_range);
- MutableSpan<MEdge> edges{mesh.medge, mesh.totedge};
+ MutableSpan<MVert> new_verts = mesh.verts_for_write().slice(new_vert_range);
+ MutableSpan<MEdge> edges = mesh.edges_for_write();
MutableSpan<MEdge> connect_edges = edges.slice(connect_edge_range);
MutableSpan<MEdge> duplicate_edges = edges.slice(duplicate_edge_range);
- MutableSpan<MPoly> polys{mesh.mpoly, mesh.totpoly};
+ MutableSpan<MPoly> polys = mesh.polys_for_write();
MutableSpan<MPoly> new_polys = polys.slice(side_poly_range);
- MutableSpan<MLoop> loops{mesh.mloop, mesh.totloop};
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
/* For every selected polygon, build the faces that form the sides of the extrusion. Filling some
* of this data like the new edges or polygons could be easily split into separate loops, which
@@ -1160,7 +1146,7 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
}
});
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData meta_data) {
GSpanAttributeWriter attribute = attributes.lookup_or_add_for_write_span(
@@ -1285,6 +1271,7 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
const IndexRange poly_corner_range = selected_corner_range(index_offsets, i_selection);
for (MVert &vert : new_verts.slice(poly_corner_range)) {
add_v3_v3(vert.co, poly_offset[poly_selection[i_selection]]);
+ vert.flag = 0;
}
}
});
@@ -1319,11 +1306,11 @@ static void extrude_individual_mesh_faces(MeshComponent &component,
if (attribute_outputs.top_id) {
save_selection_as_attribute(
- component, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
+ mesh, attribute_outputs.top_id.get(), ATTR_DOMAIN_FACE, poly_selection);
}
if (attribute_outputs.side_id) {
save_selection_as_attribute(
- component, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
+ mesh, attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE, side_poly_range);
}
BKE_mesh_runtime_clear_cache(&mesh);
@@ -1360,27 +1347,26 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<bool>("Individual");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_mesh()) {
- MeshComponent &component = geometry_set.get_component_for_write<MeshComponent>();
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
switch (mode) {
case GEO_NODE_EXTRUDE_MESH_VERTICES:
- extrude_mesh_vertices(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_vertices(*mesh, selection, final_offset, attribute_outputs);
break;
case GEO_NODE_EXTRUDE_MESH_EDGES:
- extrude_mesh_edges(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_edges(*mesh, selection, final_offset, attribute_outputs);
break;
case GEO_NODE_EXTRUDE_MESH_FACES: {
if (extrude_individual) {
- extrude_individual_mesh_faces(component, selection, final_offset, attribute_outputs);
+ extrude_individual_mesh_faces(*mesh, selection, final_offset, attribute_outputs);
}
else {
- extrude_mesh_face_regions(component, selection, final_offset, attribute_outputs);
+ extrude_mesh_face_regions(*mesh, selection, final_offset, attribute_outputs);
}
break;
}
}
- BLI_assert(BKE_mesh_is_valid(component.get_for_write()));
+ BLI_assert(BKE_mesh_is_valid(mesh));
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
index 64861e529bc..c8df5785fed 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_field_at_index.cc
@@ -9,17 +9,19 @@
#include "BLI_task.hh"
+#include "NOD_socket_search_link.hh"
+
namespace blender::nodes::node_geo_field_at_index_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Int>(N_("Index")).min(0).supports_field();
- b.add_input<decl::Float>(N_("Value"), "Value_Float").supports_field();
- b.add_input<decl::Int>(N_("Value"), "Value_Int").supports_field();
- b.add_input<decl::Vector>(N_("Value"), "Value_Vector").supports_field();
- b.add_input<decl::Color>(N_("Value"), "Value_Color").supports_field();
- b.add_input<decl::Bool>(N_("Value"), "Value_Bool").supports_field();
+ b.add_input<decl::Float>(N_("Value"), "Value_Float").hide_value().supports_field();
+ b.add_input<decl::Int>(N_("Value"), "Value_Int").hide_value().supports_field();
+ b.add_input<decl::Vector>(N_("Value"), "Value_Vector").hide_value().supports_field();
+ b.add_input<decl::Color>(N_("Value"), "Value_Color").hide_value().supports_field();
+ b.add_input<decl::Bool>(N_("Value"), "Value_Bool").hide_value().supports_field();
b.add_output<decl::Float>(N_("Value"), "Value_Float").field_source();
b.add_output<decl::Int>(N_("Value"), "Value_Int").field_source();
@@ -70,7 +72,24 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL);
}
-class FieldAtIndex final : public GeometryFieldInput {
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
+ search_link_ops_for_declarations(params, declaration.inputs().take_front(1));
+
+ const bNodeType &node_type = params.node_type();
+ const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
+ (eNodeSocketDatatype)params.other_socket().type);
+ if (type && *type != CD_PROP_STRING) {
+ params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node(node_type);
+ node.custom2 = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
+}
+
+class FieldAtIndex final : public bke::GeometryFieldInput {
private:
Field<int> index_field_;
GField value_field_;
@@ -78,26 +97,25 @@ class FieldAtIndex final : public GeometryFieldInput {
public:
FieldAtIndex(Field<int> index_field, GField value_field, eAttrDomain value_field_domain)
- : GeometryFieldInput(value_field.cpp_type(), "Field at Index"),
+ : bke::GeometryFieldInput(value_field.cpp_type(), "Field at Index"),
index_field_(std::move(index_field)),
value_field_(std::move(value_field)),
value_field_domain_(value_field_domain)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask mask) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask mask) const final
{
- const GeometryComponentFieldContext value_field_context{component, value_field_domain_};
+ const bke::GeometryFieldContext value_field_context{
+ context.geometry(), context.type(), value_field_domain_};
FieldEvaluator value_evaluator{value_field_context,
- component.attribute_domain_size(value_field_domain_)};
+ context.attributes()->domain_size(value_field_domain_)};
value_evaluator.add(value_field_);
value_evaluator.evaluate();
const GVArray &values = value_evaluator.get_evaluated(0);
- const GeometryComponentFieldContext index_field_context{component, domain};
- FieldEvaluator index_evaluator{index_field_context, &mask};
+ FieldEvaluator index_evaluator{context, &mask};
index_evaluator.add(index_field_);
index_evaluator.evaluate();
const VArray<int> indices = index_evaluator.get_evaluated<int>(0);
@@ -175,5 +193,6 @@ void register_node_type_geo_field_at_index()
ntype.draw_buttons = file_ns::node_layout;
ntype.initfunc = file_ns::node_init;
ntype.updatefunc = file_ns::node_update;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
index 15b2822805a..613425716d4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_flip_faces.cc
@@ -19,24 +19,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Mesh"));
}
-static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selection_field)
+static void mesh_flip_faces(Mesh &mesh, const Field<bool> &selection_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- if (domain_size == 0) {
+ if (mesh.totpoly == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{field_context, mesh.totpoly};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
- Mesh *mesh = component.get_for_write();
-
- mesh->mloop = (MLoop *)CustomData_duplicate_referenced_layer(
- &mesh->ldata, CD_MLOOP, mesh->totloop);
- Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
+ const Span<MPoly> polys = mesh.polys();
+ MutableSpan<MLoop> loops = mesh.loops_for_write();
for (const int i : selection.index_range()) {
const MPoly &poly = polys[selection[i]];
@@ -49,7 +44,7 @@ static void mesh_flip_faces(MeshComponent &component, const Field<bool> &selecti
}
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
attributes.for_all(
[&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
if (meta_data.domain == ATTR_DOMAIN_CORNER) {
@@ -76,11 +71,9 @@ static void node_geo_exec(GeoNodeExecParams params)
const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_mesh()) {
- return;
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
+ mesh_flip_faces(*mesh, selection_field);
}
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
- mesh_flip_faces(mesh_component, selection_field);
});
params.set_output("Mesh", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc
index 1f84f8f288d..8e64209a418 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_geometry_to_instance.cc
@@ -12,7 +12,7 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
- Vector<GeometrySet> geometries = params.extract_multi_input<GeometrySet>("Geometry");
+ Vector<GeometrySet> geometries = params.extract_input<Vector<GeometrySet>>("Geometry");
GeometrySet instances_geometry;
InstancesComponent &instances_component =
instances_geometry.get_component_for_write<InstancesComponent>();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
index bc1b9e940a1..bff2e7831c6 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_input_curve_handles_cc {
@@ -15,31 +17,27 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Right")).field_source();
}
-class HandlePositionFieldInput final : public GeometryFieldInput {
+class HandlePositionFieldInput final : public bke::CurvesFieldInput {
Field<bool> relative_;
bool left_;
public:
HandlePositionFieldInput(Field<bool> relative, bool left)
- : GeometryFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left)
+ : bke::CurvesFieldInput(CPPType::get<float3>(), "Handle"), relative_(relative), left_(left)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
- IndexMask mask) const final
+ const IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_CURVE) {
- return {};
- }
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
fn::FieldEvaluator evaluator(field_context, &mask);
evaluator.add(relative_);
evaluator.evaluate();
const VArray<bool> relative = evaluator.get_evaluated<bool>(0);
- const AttributeAccessor attributes = *component.attributes();
+ const AttributeAccessor attributes = curves.attributes();
VArray<float3> positions = attributes.lookup_or_default<float3>(
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
@@ -69,7 +67,7 @@ class HandlePositionFieldInput final : public GeometryFieldInput {
output[i] = handles[i];
}
}
- return component.attributes()->adapt_domain<float3>(
+ return attributes.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
index 4c7a148a797..8c5a92904ab 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_rotation.cc
@@ -9,28 +9,20 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Rotation")).field_source();
}
-class VectorFieldInput final : public GeometryFieldInput {
+class InstanceRotationFieldInput final : public bke::InstancesFieldInput {
public:
- VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Rotation")
+ InstanceRotationFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Rotation")
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain UNUSED(domain),
+ GVArray get_varray_for_context(const InstancesComponent &instances,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) {
- return {};
- }
-
- const InstancesComponent &instance_component = static_cast<const InstancesComponent &>(
- component);
-
auto rotation_fn = [&](const int i) -> float3 {
- return instance_component.instance_transforms()[i].to_euler();
+ return instances.instance_transforms()[i].to_euler();
};
- return VArray<float3>::ForFunc(instance_component.instances_num(), rotation_fn);
+ return VArray<float3>::ForFunc(instances.instances_num(), rotation_fn);
}
uint64_t hash() const override
@@ -40,13 +32,13 @@ class VectorFieldInput final : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const VectorFieldInput *>(&other) != nullptr;
+ return dynamic_cast<const InstanceRotationFieldInput *>(&other) != nullptr;
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float3> rotation{std::make_shared<VectorFieldInput>()};
+ Field<float3> rotation{std::make_shared<InstanceRotationFieldInput>()};
params.set_output("Rotation", std::move(rotation));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
index b3a362fbf3e..b79e73915b7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_instance_scale.cc
@@ -9,28 +9,20 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("Scale")).field_source();
}
-class VectorFieldInput final : public GeometryFieldInput {
+class InstanceScaleFieldInput final : public bke::InstancesFieldInput {
public:
- VectorFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Scale")
+ InstanceScaleFieldInput() : bke::InstancesFieldInput(CPPType::get<float3>(), "Scale")
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain UNUSED(domain),
+ GVArray get_varray_for_context(const InstancesComponent &instances,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_INSTANCES) {
- return {};
- }
-
- const InstancesComponent &instance_component = static_cast<const InstancesComponent &>(
- component);
-
auto scale_fn = [&](const int i) -> float3 {
- return instance_component.instance_transforms()[i].scale();
+ return instances.instance_transforms()[i].scale();
};
- return VArray<float3>::ForFunc(instance_component.instances_num(), scale_fn);
+ return VArray<float3>::ForFunc(instances.instances_num(), scale_fn);
}
uint64_t hash() const override
@@ -40,13 +32,13 @@ class VectorFieldInput final : public GeometryFieldInput {
bool is_equal_to(const fn::FieldNode &other) const override
{
- return dynamic_cast<const VectorFieldInput *>(&other) != nullptr;
+ return dynamic_cast<const InstanceScaleFieldInput *>(&other) != nullptr;
}
};
static void node_geo_exec(GeoNodeExecParams params)
{
- Field<float3> scale{std::make_shared<VectorFieldInput>()};
+ Field<float3> scale{std::make_shared<InstanceScaleFieldInput>()};
params.set_output("Scale", std::move(scale));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
index b009aaa5291..f2e7379b3a2 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_angle.cc
@@ -53,46 +53,36 @@ static Array<EdgeMapEntry> create_edge_map(const Span<MPoly> polys,
return edge_map;
}
-class AngleFieldInput final : public GeometryFieldInput {
+class AngleFieldInput final : public bke::MeshFieldInput {
public:
- AngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
+ AngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Unsigned Angle Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
-
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge);
- Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
- Span<MLoop> loops{mesh->mloop, mesh->totloop};
- Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
-
- auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
+ auto angle_fn = [edge_map = std::move(edge_map), verts, polys, loops](const int i) -> float {
if (edge_map[i].face_count != 2) {
return 0.0f;
}
const MPoly &mpoly_1 = polys[edge_map[i].face_index_1];
const MPoly &mpoly_2 = polys[edge_map[i].face_index_2];
float3 normal_1, normal_2;
- BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, normal_1);
- BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, normal_2);
+ BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), normal_1);
+ BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), normal_2);
return angle_normalized_v3v3(normal_1, normal_2);
};
- VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
- return component.attributes()->adapt_domain<float>(
- std::move(angles), ATTR_DOMAIN_EDGE, domain);
+ VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn);
+ return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
@@ -107,32 +97,25 @@ class AngleFieldInput final : public GeometryFieldInput {
}
};
-class SignedAngleFieldInput final : public GeometryFieldInput {
+class SignedAngleFieldInput final : public bke::MeshFieldInput {
public:
- SignedAngleFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Signed Angle Field")
+ SignedAngleFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Signed Angle Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
-
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- Span<MPoly> polys{mesh->mpoly, mesh->totpoly};
- Span<MLoop> loops{mesh->mloop, mesh->totloop};
- Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh->totedge);
-
- auto angle_fn = [edge_map, polys, loops, mesh](const int i) -> float {
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ Array<EdgeMapEntry> edge_map = create_edge_map(polys, loops, mesh.totedge);
+
+ auto angle_fn =
+ [edge_map = std::move(edge_map), verts, edges, polys, loops](const int i) -> float {
if (edge_map[i].face_count != 2) {
return 0.0f;
}
@@ -141,18 +124,18 @@ class SignedAngleFieldInput final : public GeometryFieldInput {
/* Find the normals of the 2 polys. */
float3 poly_1_normal, poly_2_normal;
- BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], mesh->mvert, poly_1_normal);
- BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_2_normal);
+ BKE_mesh_calc_poly_normal(&mpoly_1, &loops[mpoly_1.loopstart], verts.data(), poly_1_normal);
+ BKE_mesh_calc_poly_normal(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), poly_2_normal);
/* Find the centerpoint of the axis edge */
- const float3 edge_centerpoint = (float3(mesh->mvert[mesh->medge[i].v1].co) +
- float3(mesh->mvert[mesh->medge[i].v2].co)) *
+ const float3 edge_centerpoint = (float3(verts[edges[i].v1].co) +
+ float3(verts[edges[i].v2].co)) *
0.5f;
/* Get the centerpoint of poly 2 and subtract the edge centerpoint to get a tangent
* normal for poly 2. */
float3 poly_center_2;
- BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], mesh->mvert, poly_center_2);
+ BKE_mesh_calc_poly_center(&mpoly_2, &loops[mpoly_2.loopstart], verts.data(), poly_center_2);
const float3 poly_2_tangent = math::normalize(poly_center_2 - edge_centerpoint);
const float concavity = math::dot(poly_1_normal, poly_2_tangent);
@@ -165,9 +148,8 @@ class SignedAngleFieldInput final : public GeometryFieldInput {
return -angle;
};
- VArray<float> angles = VArray<float>::ForFunc(mesh->totedge, angle_fn);
- return component.attributes()->adapt_domain<float>(
- std::move(angles), ATTR_DOMAIN_EDGE, domain);
+ VArray<float> angles = VArray<float>::ForFunc(mesh.totedge, angle_fn);
+ return mesh.attributes().adapt_domain<float>(std::move(angles), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
index 50d6998bb27..bfe8753c039 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_neighbors.cc
@@ -16,34 +16,26 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The number of faces that use each edge as one of their sides"));
}
-class EdgeNeighborCountFieldInput final : public GeometryFieldInput {
+class EdgeNeighborCountFieldInput final : public bke::MeshFieldInput {
public:
EdgeNeighborCountFieldInput()
- : GeometryFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field")
+ : bke::MeshFieldInput(CPPType::get<int>(), "Edge Neighbor Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- Array<int> face_count(mesh->totedge, 0);
- for (const int i : IndexRange(mesh->totloop)) {
- face_count[mesh->mloop[i].e]++;
- }
-
- return mesh_component.attributes()->adapt_domain<int>(
- VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
+ const Span<MLoop> loops = mesh.loops();
+ Array<int> face_count(mesh.totedge, 0);
+ for (const MLoop &loop : loops) {
+ face_count[loop.e]++;
}
- return {};
+
+ return mesh.attributes().adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(face_count)), ATTR_DOMAIN_EDGE, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
index 83e511f45c2..c8ceae239a4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_edge_vertices.cc
@@ -27,45 +27,37 @@ static void node_declare(NodeDeclarationBuilder &b)
enum VertexNumber { VERTEX_ONE, VERTEX_TWO };
-static VArray<int> construct_edge_vertices_gvarray(const MeshComponent &component,
- const VertexNumber vertex,
- const eAttrDomain domain)
+static VArray<int> construct_edge_verts_gvarray(const Mesh &mesh,
+ const VertexNumber vertex,
+ const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MEdge> edges = mesh.edges();
if (domain == ATTR_DOMAIN_EDGE) {
if (vertex == VERTEX_ONE) {
- return VArray<int>::ForFunc(mesh->totedge,
- [mesh](const int i) -> int { return mesh->medge[i].v1; });
+ return VArray<int>::ForFunc(edges.size(),
+ [edges](const int i) -> int { return edges[i].v1; });
}
- return VArray<int>::ForFunc(mesh->totedge,
- [mesh](const int i) -> int { return mesh->medge[i].v2; });
+ return VArray<int>::ForFunc(edges.size(), [edges](const int i) -> int { return edges[i].v2; });
}
return {};
}
-class EdgeVerticesFieldInput final : public GeometryFieldInput {
+class EdgeVerticesFieldInput final : public bke::MeshFieldInput {
private:
VertexNumber vertex_;
public:
EdgeVerticesFieldInput(VertexNumber vertex)
- : GeometryFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex)
+ : bke::MeshFieldInput(CPPType::get<int>(), "Edge Vertices Field"), vertex_(vertex)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_edge_vertices_gvarray(mesh_component, vertex_, domain);
- }
- return {};
+ return construct_edge_verts_gvarray(mesh, vertex_, domain);
}
uint64_t hash() const override
@@ -83,51 +75,43 @@ class EdgeVerticesFieldInput final : public GeometryFieldInput {
}
};
-static VArray<float3> construct_edge_positions_gvarray(const MeshComponent &component,
+static VArray<float3> construct_edge_positions_gvarray(const Mesh &mesh,
const VertexNumber vertex,
const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
if (vertex == VERTEX_ONE) {
- return component.attributes()->adapt_domain<float3>(
- VArray<float3>::ForFunc(
- mesh->totedge,
- [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v1].co); }),
+ return mesh.attributes().adapt_domain<float3>(
+ VArray<float3>::ForFunc(edges.size(),
+ [verts, edges](const int i) { return verts[edges[i].v1].co; }),
ATTR_DOMAIN_EDGE,
domain);
}
- return component.attributes()->adapt_domain<float3>(
- VArray<float3>::ForFunc(
- mesh->totedge,
- [mesh](const int i) { return float3(mesh->mvert[mesh->medge[i].v2].co); }),
+ return mesh.attributes().adapt_domain<float3>(
+ VArray<float3>::ForFunc(edges.size(),
+ [verts, edges](const int i) { return verts[edges[i].v2].co; }),
ATTR_DOMAIN_EDGE,
domain);
}
-class EdgePositionFieldInput final : public GeometryFieldInput {
+class EdgePositionFieldInput final : public bke::MeshFieldInput {
private:
VertexNumber vertex_;
public:
EdgePositionFieldInput(VertexNumber vertex)
- : GeometryFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex)
+ : bke::MeshFieldInput(CPPType::get<float3>(), "Edge Position Field"), vertex_(vertex)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_edge_positions_gvarray(mesh_component, vertex_, domain);
- }
- return {};
+ return construct_edge_positions_gvarray(mesh, vertex_, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
index 4d21bf9443a..be921c1f1c5 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_area.cc
@@ -16,39 +16,33 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The surface area of each of the mesh's faces"));
}
-static VArray<float> construct_face_area_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<float> construct_face_area_varray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- auto area_fn = [mesh](const int i) -> float {
- const MPoly *mp = &mesh->mpoly[i];
- return BKE_mesh_calc_poly_area(mp, &mesh->mloop[mp->loopstart], mesh->mvert);
+ auto area_fn = [verts, polys, loops](const int i) -> float {
+ const MPoly &poly = polys[i];
+ return BKE_mesh_calc_poly_area(&poly, &loops[poly.loopstart], verts.data());
};
- return component.attributes()->adapt_domain<float>(
- VArray<float>::ForFunc(mesh->totpoly, area_fn), ATTR_DOMAIN_FACE, domain);
+ return mesh.attributes().adapt_domain<float>(
+ VArray<float>::ForFunc(polys.size(), area_fn), ATTR_DOMAIN_FACE, domain);
}
-class FaceAreaFieldInput final : public GeometryFieldInput {
+class FaceAreaFieldInput final : public bke::MeshFieldInput {
public:
- FaceAreaFieldInput() : GeometryFieldInput(CPPType::get<float>(), "Face Area Field")
+ FaceAreaFieldInput() : bke::MeshFieldInput(CPPType::get<float>(), "Face Area Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_face_area_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_face_area_varray(mesh, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
index 6b04ff08d9e..72c45de7b0f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_is_planar.cc
@@ -22,53 +22,45 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Bool>("Planar").field_source();
}
-class PlanarFieldInput final : public GeometryFieldInput {
+class PlanarFieldInput final : public bke::MeshFieldInput {
private:
Field<float> threshold_;
public:
PlanarFieldInput(Field<float> threshold)
- : GeometryFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold)
+ : bke::MeshFieldInput(CPPType::get<bool>(), "Planar"), threshold_(threshold)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
- [[maybe_unused]] IndexMask mask) const final
+ IndexMask /*mask*/) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
-
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_FACE};
- fn::FieldEvaluator evaluator{context, mesh->totpoly};
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ const Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(&mesh), mesh.totpoly};
+
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{context, polys.size()};
evaluator.add(threshold_);
evaluator.evaluate();
const VArray<float> thresholds = evaluator.get_evaluated<float>(0);
- Span<float3> poly_normals{(float3 *)BKE_mesh_poly_normals_ensure(mesh), mesh->totpoly};
-
- auto planar_fn = [mesh, thresholds, poly_normals](const int i_poly) -> bool {
- if (mesh->mpoly[i_poly].totloop <= 3) {
+ auto planar_fn = [verts, polys, loops, thresholds, poly_normals](const int i) -> bool {
+ const MPoly &poly = polys[i];
+ if (poly.totloop <= 3) {
return true;
}
- const int loopstart = mesh->mpoly[i_poly].loopstart;
- const int loops = mesh->mpoly[i_poly].totloop;
- Span<MLoop> poly_loops(&mesh->mloop[loopstart], loops);
- float3 reference_normal = poly_normals[i_poly];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
+ const float3 &reference_normal = poly_normals[i];
float min = FLT_MAX;
float max = -FLT_MAX;
for (const int i_loop : poly_loops.index_range()) {
- const float3 vert = mesh->mvert[poly_loops[i_loop].v].co;
+ const float3 vert = verts[poly_loops[i_loop].v].co;
float dot = math::dot(reference_normal, vert);
if (dot > max) {
max = dot;
@@ -77,11 +69,11 @@ class PlanarFieldInput final : public GeometryFieldInput {
min = dot;
}
}
- return max - min < thresholds[i_poly] / 2.0f;
+ return max - min < thresholds[i] / 2.0f;
};
- return component.attributes()->adapt_domain<bool>(
- VArray<bool>::ForFunc(mesh->totpoly, planar_fn), ATTR_DOMAIN_FACE, domain);
+ return mesh.attributes().adapt_domain<bool>(
+ VArray<bool>::ForFunc(polys.size(), planar_fn), ATTR_DOMAIN_FACE, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
index a225ce61b14..9e85eae3a31 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_face_neighbors.cc
@@ -19,48 +19,41 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("Number of faces which share an edge with the face"));
}
-static VArray<int> construct_neighbor_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_neighbor_count_varray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- Array<int> edge_count(mesh->totedge, 0);
- for (const int i : IndexRange(mesh->totloop)) {
- edge_count[mesh->mloop[i].e]++;
+ Array<int> edge_count(mesh.totedge, 0);
+ for (const MLoop &loop : loops) {
+ edge_count[loop.e]++;
}
- Array<int> poly_count(mesh->totpoly, 0);
- for (const int poly_num : IndexRange(mesh->totpoly)) {
- MPoly &poly = mesh->mpoly[poly_num];
- for (const int loop_num : IndexRange(poly.loopstart, poly.totloop)) {
- poly_count[poly_num] += edge_count[mesh->mloop[loop_num].e] - 1;
+ Array<int> poly_count(polys.size(), 0);
+ for (const int poly_index : polys.index_range()) {
+ const MPoly &poly = polys[poly_index];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ poly_count[poly_index] += edge_count[loop.e] - 1;
}
}
- return component.attributes()->adapt_domain<int>(
+ return mesh.attributes().adapt_domain<int>(
VArray<int>::ForContainer(std::move(poly_count)), ATTR_DOMAIN_FACE, domain);
}
-class FaceNeighborCountFieldInput final : public GeometryFieldInput {
+class FaceNeighborCountFieldInput final : public bke::MeshFieldInput {
public:
FaceNeighborCountFieldInput()
- : GeometryFieldInput(CPPType::get<int>(), "Face Neighbor Count Field")
+ : bke::MeshFieldInput(CPPType::get<int>(), "Face Neighbor Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_neighbor_count_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_neighbor_count_varray(mesh, domain);
}
uint64_t hash() const override
@@ -75,37 +68,28 @@ class FaceNeighborCountFieldInput final : public GeometryFieldInput {
}
};
-static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_vertex_count_varray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
- return component.attributes()->adapt_domain<int>(
- VArray<int>::ForFunc(mesh->totpoly,
- [mesh](const int i) -> float { return mesh->mpoly[i].totloop; }),
+ const Span<MPoly> polys = mesh.polys();
+ return mesh.attributes().adapt_domain<int>(
+ VArray<int>::ForFunc(polys.size(),
+ [polys](const int i) -> float { return polys[i].totloop; }),
ATTR_DOMAIN_FACE,
domain);
}
-class FaceVertexCountFieldInput final : public GeometryFieldInput {
+class FaceVertexCountFieldInput final : public bke::MeshFieldInput {
public:
- FaceVertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field")
+ FaceVertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_vertex_count_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_vertex_count_varray(mesh, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
index 2c7eef5665f..9d7735e707d 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_island.cc
@@ -22,39 +22,32 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("The total number of mesh islands"));
}
-class IslandFieldInput final : public GeometryFieldInput {
+class IslandFieldInput final : public bke::MeshFieldInput {
public:
- IslandFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Index")
+ IslandFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Index")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MEdge> edges = mesh.edges();
- DisjointSet islands(mesh->totvert);
- for (const int i : IndexRange(mesh->totedge)) {
- islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ DisjointSet islands(mesh.totvert);
+ for (const int i : edges.index_range()) {
+ islands.join(edges[i].v1, edges[i].v2);
}
- Array<int> output(mesh->totvert);
+ Array<int> output(mesh.totvert);
VectorSet<int> ordered_roots;
- for (const int i : IndexRange(mesh->totvert)) {
+ for (const int i : IndexRange(mesh.totvert)) {
const int64_t root = islands.find_root(i);
output[i] = ordered_roots.index_of_or_add(root);
}
- return mesh_component.attributes()->adapt_domain<int>(
+ return mesh.attributes().adapt_domain<int>(
VArray<int>::ForContainer(std::move(output)), ATTR_DOMAIN_POINT, domain);
}
@@ -70,39 +63,31 @@ class IslandFieldInput final : public GeometryFieldInput {
}
};
-class IslandCountFieldInput final : public GeometryFieldInput {
+class IslandCountFieldInput final : public bke::MeshFieldInput {
public:
- IslandCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Island Count")
+ IslandCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Island Count")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
- return {};
- }
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MEdge> edges = mesh.edges();
- DisjointSet islands(mesh->totvert);
- for (const int i : IndexRange(mesh->totedge)) {
- islands.join(mesh->medge[i].v1, mesh->medge[i].v2);
+ DisjointSet islands(mesh.totvert);
+ for (const int i : edges.index_range()) {
+ islands.join(edges[i].v1, edges[i].v2);
}
Set<int> island_list;
- for (const int i_vert : IndexRange(mesh->totvert)) {
+ for (const int i_vert : IndexRange(mesh.totvert)) {
const int64_t root = islands.find_root(i_vert);
island_list.add(root);
}
- return VArray<int>::ForSingle(island_list.size(),
- mesh_component.attribute_domain_size(domain));
+ return VArray<int>::ForSingle(island_list.size(), mesh.attributes().domain_size(domain));
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
index 62b3f9d0e92..ab44a6c8515 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_mesh_vertex_neighbors.cc
@@ -20,41 +20,32 @@ static void node_declare(NodeDeclarationBuilder &b)
.description(N_("Number of faces that contain the vertex"));
}
-static VArray<int> construct_vertex_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_vertex_count_gvarray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
+ const Span<MEdge> edges = mesh.edges();
if (domain == ATTR_DOMAIN_POINT) {
- Array<int> vertices(mesh->totvert, 0);
- for (const int i : IndexRange(mesh->totedge)) {
- vertices[mesh->medge[i].v1]++;
- vertices[mesh->medge[i].v2]++;
+ Array<int> counts(mesh.totvert, 0);
+ for (const int i : edges.index_range()) {
+ counts[edges[i].v1]++;
+ counts[edges[i].v2]++;
}
- return VArray<int>::ForContainer(std::move(vertices));
+ return VArray<int>::ForContainer(std::move(counts));
}
return {};
}
-class VertexCountFieldInput final : public GeometryFieldInput {
+class VertexCountFieldInput final : public bke::MeshFieldInput {
public:
- VertexCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Count Field")
+ VertexCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_vertex_count_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_vertex_count_gvarray(mesh, domain);
}
uint64_t hash() const override
@@ -69,18 +60,13 @@ class VertexCountFieldInput final : public GeometryFieldInput {
}
};
-static VArray<int> construct_face_count_gvarray(const MeshComponent &component,
- const eAttrDomain domain)
+static VArray<int> construct_face_count_gvarray(const Mesh &mesh, const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
-
+ const Span<MLoop> loops = mesh.loops();
if (domain == ATTR_DOMAIN_POINT) {
- Array<int> vertices(mesh->totvert, 0);
- for (const int i : IndexRange(mesh->totloop)) {
- int vertex = mesh->mloop[i].v;
+ Array<int> vertices(mesh.totvert, 0);
+ for (const int i : loops.index_range()) {
+ int vertex = loops[i].v;
vertices[vertex]++;
}
return VArray<int>::ForContainer(std::move(vertices));
@@ -88,22 +74,18 @@ static VArray<int> construct_face_count_gvarray(const MeshComponent &component,
return {};
}
-class VertexFaceCountFieldInput final : public GeometryFieldInput {
+class VertexFaceCountFieldInput final : public bke::MeshFieldInput {
public:
- VertexFaceCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Vertex Face Count Field")
+ VertexFaceCountFieldInput() : bke::MeshFieldInput(CPPType::get<int>(), "Vertex Face Count Field")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_face_count_gvarray(mesh_component, domain);
- }
- return {};
+ return construct_face_count_gvarray(mesh, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
index 122c7b352c7..da09d3650e3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_named_attribute.cc
@@ -88,7 +88,7 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- params.used_named_attribute(name, eNamedAttrUsage::Read);
+ params.used_named_attribute(name, NamedAttributeUsage::Read);
switch (data_type) {
case CD_PROP_FLOAT:
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc
new file mode 100644
index 00000000000..a54daabde3b
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_shortest_edge_paths.cc
@@ -0,0 +1,236 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <queue>
+
+#include "BLI_map.hh"
+#include "BLI_math_vec_types.hh"
+#include "BLI_set.hh"
+#include "BLI_task.hh"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_input_shortest_edge_paths_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Bool>(N_("End Vertex")).default_value(false).hide_value().supports_field();
+ b.add_input<decl::Float>(N_("Edge Cost")).default_value(1.0f).hide_value().supports_field();
+ b.add_output<decl::Int>(N_("Next Vertex Index")).field_source();
+ b.add_output<decl::Float>(N_("Total Cost")).field_source();
+}
+
+typedef std::pair<float, int> VertPriority;
+
+struct EdgeVertMap {
+ Array<Vector<int>> edges_by_vertex_map;
+
+ EdgeVertMap(const Mesh &mesh)
+ {
+ const Span<MEdge> edges = mesh.edges();
+ edges_by_vertex_map.reinitialize(mesh.totvert);
+ for (const int edge_i : edges.index_range()) {
+ const MEdge &edge = edges[edge_i];
+ edges_by_vertex_map[edge.v1].append(edge_i);
+ edges_by_vertex_map[edge.v2].append(edge_i);
+ }
+ }
+};
+
+static void shortest_paths(const Mesh &mesh,
+ EdgeVertMap &maps,
+ const IndexMask end_selection,
+ const VArray<float> &input_cost,
+ MutableSpan<int> r_next_index,
+ MutableSpan<float> r_cost)
+{
+ const Span<MEdge> edges = mesh.edges();
+ Array<bool> visited(mesh.totvert, false);
+
+ std::priority_queue<VertPriority, std::vector<VertPriority>, std::greater<VertPriority>> queue;
+
+ for (const int start_vert_i : end_selection) {
+ r_cost[start_vert_i] = 0.0f;
+ queue.emplace(0.0f, start_vert_i);
+ }
+
+ while (!queue.empty()) {
+ const float cost_i = queue.top().first;
+ const int vert_i = queue.top().second;
+ queue.pop();
+ if (visited[vert_i]) {
+ continue;
+ }
+ visited[vert_i] = true;
+ const Span<int> incident_edge_indices = maps.edges_by_vertex_map[vert_i];
+ for (const int edge_i : incident_edge_indices) {
+ const MEdge &edge = edges[edge_i];
+ const int neighbor_vert_i = edge.v1 + edge.v2 - vert_i;
+ if (visited[neighbor_vert_i]) {
+ continue;
+ }
+ const float edge_cost = std::max(0.0f, input_cost[edge_i]);
+ const float new_neighbour_cost = cost_i + edge_cost;
+ if (new_neighbour_cost < r_cost[neighbor_vert_i]) {
+ r_cost[neighbor_vert_i] = new_neighbour_cost;
+ r_next_index[neighbor_vert_i] = vert_i;
+ queue.emplace(new_neighbour_cost, neighbor_vert_i);
+ }
+ }
+ }
+}
+
+class ShortestEdgePathsNextVertFieldInput final : public bke::MeshFieldInput {
+ private:
+ Field<bool> end_selection_;
+ Field<float> cost_;
+
+ public:
+ ShortestEdgePathsNextVertFieldInput(Field<bool> end_selection, Field<float> cost)
+ : bke::MeshFieldInput(CPPType::get<int>(), "Shortest Edge Paths Next Vertex Field"),
+ end_selection_(end_selection),
+ cost_(cost)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ const IndexMask /*mask*/) const final
+ {
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
+ edge_evaluator.add(cost_);
+ edge_evaluator.evaluate();
+ const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0);
+
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator point_evaluator{point_context, mesh.totvert};
+ point_evaluator.add(end_selection_);
+ point_evaluator.evaluate();
+ const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0);
+
+ Array<int> next_index(mesh.totvert, -1);
+ Array<float> cost(mesh.totvert, FLT_MAX);
+
+ if (!end_selection.is_empty()) {
+ EdgeVertMap maps(mesh);
+ shortest_paths(mesh, maps, end_selection, input_cost, next_index, cost);
+ }
+ threading::parallel_for(next_index.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ if (next_index[i] == -1) {
+ next_index[i] = i;
+ }
+ }
+ });
+ return mesh.attributes().adapt_domain<int>(
+ VArray<int>::ForContainer(std::move(next_index)), ATTR_DOMAIN_POINT, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ /* Some random constant hash. */
+ return 8466507837;
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const ShortestEdgePathsNextVertFieldInput *other_field =
+ dynamic_cast<const ShortestEdgePathsNextVertFieldInput *>(&other)) {
+ return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_;
+ }
+ return false;
+ }
+};
+
+class ShortestEdgePathsCostFieldInput final : public bke::MeshFieldInput {
+ private:
+ Field<bool> end_selection_;
+ Field<float> cost_;
+
+ public:
+ ShortestEdgePathsCostFieldInput(Field<bool> end_selection, Field<float> cost)
+ : bke::MeshFieldInput(CPPType::get<float>(), "Shortest Edge Paths Cost Field"),
+ end_selection_(end_selection),
+ cost_(cost)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ const IndexMask /*mask*/) const final
+ {
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator edge_evaluator{edge_context, mesh.totedge};
+ edge_evaluator.add(cost_);
+ edge_evaluator.evaluate();
+ const VArray<float> input_cost = edge_evaluator.get_evaluated<float>(0);
+
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator point_evaluator{point_context, mesh.totvert};
+ point_evaluator.add(end_selection_);
+ point_evaluator.evaluate();
+ const IndexMask end_selection = point_evaluator.get_evaluated_as_mask(0);
+
+ Array<int> next_index(mesh.totvert, -1);
+ Array<float> cost(mesh.totvert, FLT_MAX);
+
+ if (!end_selection.is_empty()) {
+ EdgeVertMap maps(mesh);
+ shortest_paths(mesh, maps, end_selection, input_cost, next_index, cost);
+ }
+ threading::parallel_for(cost.index_range(), 1024, [&](const IndexRange range) {
+ for (const int i : range) {
+ if (cost[i] == FLT_MAX) {
+ cost[i] = 0;
+ }
+ }
+ });
+ return mesh.attributes().adapt_domain<float>(
+ VArray<float>::ForContainer(std::move(cost)), ATTR_DOMAIN_POINT, domain);
+ }
+
+ uint64_t hash() const override
+ {
+ return get_default_hash_2(end_selection_, cost_);
+ }
+
+ bool is_equal_to(const fn::FieldNode &other) const override
+ {
+ if (const ShortestEdgePathsCostFieldInput *other_field =
+ dynamic_cast<const ShortestEdgePathsCostFieldInput *>(&other)) {
+ return other_field->end_selection_ == end_selection_ && other_field->cost_ == cost_;
+ }
+ return false;
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ Field<bool> end_selection = params.extract_input<Field<bool>>("End Vertex");
+ Field<float> cost = params.extract_input<Field<float>>("Edge Cost");
+
+ Field<int> next_vert_field{
+ std::make_shared<ShortestEdgePathsNextVertFieldInput>(end_selection, cost)};
+ Field<float> cost_field{std::make_shared<ShortestEdgePathsCostFieldInput>(end_selection, cost)};
+ params.set_output("Next Vertex Index", std::move(next_vert_field));
+ params.set_output("Total Cost", std::move(cost_field));
+}
+
+} // namespace blender::nodes::node_geo_input_shortest_edge_paths_cc
+
+void register_node_type_geo_input_shortest_edge_paths()
+{
+ namespace file_ns = blender::nodes::node_geo_input_shortest_edge_paths_cc;
+
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_INPUT_SHORTEST_EDGE_PATHS, "Shortest Edge Paths", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
index 267ba44cc00..07dc158ff48 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc
@@ -16,15 +16,9 @@ static void node_declare(NodeDeclarationBuilder &b)
* Spline Count
*/
-static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &component,
+static VArray<int> construct_curve_point_count_gvarray(const bke::CurvesGeometry &curves,
const eAttrDomain domain)
{
- if (!component.has_curves()) {
- return {};
- }
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
auto count_fn = [curves](int64_t i) { return curves.points_for_curve(i).size(); };
if (domain == ATTR_DOMAIN_CURVE) {
@@ -32,29 +26,24 @@ static VArray<int> construct_curve_point_count_gvarray(const CurveComponent &com
}
if (domain == ATTR_DOMAIN_POINT) {
VArray<int> count = VArray<int>::ForFunc(curves.curves_num(), count_fn);
- return component.attributes()->adapt_domain<int>(
- std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
+ return curves.adapt_domain<int>(std::move(count), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT);
}
return {};
}
-class SplineCountFieldInput final : public GeometryFieldInput {
+class SplineCountFieldInput final : public bke::CurvesFieldInput {
public:
- SplineCountFieldInput() : GeometryFieldInput(CPPType::get<int>(), "Spline Point Count")
+ SplineCountFieldInput() : bke::CurvesFieldInput(CPPType::get<int>(), "Spline Point Count")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_point_count_gvarray(curve_component, domain);
- }
- return {};
+ return construct_curve_point_count_gvarray(curves, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
index a2aab5464aa..ea3d060f03c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc
@@ -63,19 +63,12 @@ static Array<float3> curve_tangent_point_domain(const bke::CurvesGeometry &curve
return results;
}
-static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &component,
+static VArray<float3> construct_curve_tangent_gvarray(const bke::CurvesGeometry &curves,
const eAttrDomain domain)
{
- if (!component.has_curves()) {
- return {};
- }
-
- const Curves &curves_id = *component.get_for_read();
- const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
const VArray<int8_t> types = curves.curve_types();
if (curves.is_single_type(CURVE_TYPE_POLY)) {
- return component.attributes()->adapt_domain<float3>(
+ return curves.adapt_domain<float3>(
VArray<float3>::ForSpan(curves.evaluated_tangents()), ATTR_DOMAIN_POINT, domain);
}
@@ -86,29 +79,25 @@ static VArray<float3> construct_curve_tangent_gvarray(const CurveComponent &comp
}
if (domain == ATTR_DOMAIN_CURVE) {
- return component.attributes()->adapt_domain<float3>(
+ return curves.adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(tangents)), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE);
}
return nullptr;
}
-class TangentFieldInput final : public GeometryFieldInput {
+class TangentFieldInput final : public bke::CurvesFieldInput {
public:
- TangentFieldInput() : GeometryFieldInput(CPPType::get<float3>(), "Tangent node")
+ TangentFieldInput() : bke::CurvesFieldInput(CPPType::get<float3>(), "Tangent node")
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const bke::CurvesGeometry &curves,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_CURVE) {
- const CurveComponent &curve_component = static_cast<const CurveComponent &>(component);
- return construct_curve_tangent_gvarray(curve_component, domain);
- }
- return {};
+ return construct_curve_tangent_gvarray(curves, domain);
}
uint64_t hash() const override
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
index 3ce26a086e2..d54d082311f 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc
@@ -57,7 +57,7 @@ static void add_instances_from_component(
VArray<float3> rotations;
VArray<float3> scales;
- GeometryComponentFieldContext field_context{src_component, domain};
+ bke::GeometryFieldContext field_context{src_component, domain};
const Field<bool> selection_field = params.get_input<Field<bool>>("Selection");
fn::FieldEvaluator evaluator{field_context, domain_num};
evaluator.set_selection(selection_field);
@@ -70,6 +70,9 @@ static void add_instances_from_component(
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_selection_as_mask();
+ if (selection.is_empty()) {
+ return;
+ }
/* The initial size of the component might be non-zero when this function is called for multiple
* component types. */
@@ -213,7 +216,7 @@ static void node_geo_exec(GeoNodeExecParams params)
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
});
/* Unused references may have been added above. Remove those now so that other nodes don't
diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
index edbe6e1593f..ec2f1b00e6c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc
@@ -22,16 +22,6 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Points"));
}
-template<typename T>
-static void copy_attribute_to_points(const VArray<T> &src,
- const IndexMask mask,
- MutableSpan<T> dst)
-{
- for (const int i : mask.index_range()) {
- dst[i] = src[mask[i]];
- }
-}
-
static void convert_instances_to_points(GeometrySet &geometry_set,
Field<float3> position_field,
Field<float> radius_field,
@@ -39,10 +29,8 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
{
const InstancesComponent &instances = *geometry_set.get_component_for_read<InstancesComponent>();
- GeometryComponentFieldContext field_context{instances, ATTR_DOMAIN_INSTANCE};
- const int domain_size = instances.instances_num();
-
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ const bke::InstancesFieldContext context{instances};
+ fn::FieldEvaluator evaluator{context, instances.instances_num()};
evaluator.set_selection(std::move(selection_field));
evaluator.add(std::move(position_field));
evaluator.add(std::move(radius_field));
@@ -51,16 +39,23 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
if (selection.is_empty()) {
return;
}
+ const VArray<float3> &positions = evaluator.get_evaluated<float3>(0);
+ const VArray<float> radii = evaluator.get_evaluated<float>(1);
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
geometry_set.replace_pointcloud(pointcloud);
- PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
+ bke::MutableAttributeAccessor point_attributes = pointcloud->attributes_for_write();
- const VArray<float3> &positions = evaluator.get_evaluated<float3>(0);
- copy_attribute_to_points(positions, selection, {(float3 *)pointcloud->co, pointcloud->totpoint});
- const VArray<float> radii = evaluator.get_evaluated<float>(1);
- copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint});
+ bke::SpanAttributeWriter<float3> point_positions =
+ point_attributes.lookup_or_add_for_write_only_span<float3>("position", ATTR_DOMAIN_POINT);
+ bke::SpanAttributeWriter<float> point_radii =
+ point_attributes.lookup_or_add_for_write_only_span<float>("radius", ATTR_DOMAIN_POINT);
+
+ positions.materialize_compressed_to_uninitialized(selection, point_positions.span);
+ radii.materialize_compressed_to_uninitialized(selection, point_radii.span);
+ point_positions.finish();
+ point_radii.finish();
Map<AttributeIDRef, AttributeKind> attributes_to_propagate;
geometry_set.gather_attributes_for_propagation({GEO_COMPONENT_TYPE_INSTANCES},
@@ -78,14 +73,11 @@ static void convert_instances_to_points(GeometrySet &geometry_set,
const GVArray src = instances.attributes()->lookup_or_default(
attribute_id, ATTR_DOMAIN_INSTANCE, attribute_kind.data_type);
BLI_assert(src);
- GSpanAttributeWriter dst = points.attributes_for_write()->lookup_or_add_for_write_only_span(
+ GSpanAttributeWriter dst = point_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, attribute_kind.data_type);
BLI_assert(dst);
- attribute_math::convert_to_static_type(attribute_kind.data_type, [&](auto dummy) {
- using T = decltype(dummy);
- copy_attribute_to_points(src.typed<T>(), selection, dst.span.typed<T>());
- });
+ src.materialize_compressed_to_uninitialized(selection, dst.span.data());
dst.finish();
}
}
@@ -99,7 +91,7 @@ static void node_geo_exec(GeoNodeExecParams params)
params.extract_input<Field<float3>>("Position"),
params.extract_input<Field<float>>("Radius"),
params.extract_input<Field<bool>>("Selection"));
- geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD});
+ geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_EDIT});
params.set_output("Points", std::move(geometry_set));
}
else {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc
index 5939ed5334d..8e38ef14aba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_field_on_domain.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_interpolate_domain.cc
@@ -9,7 +9,9 @@
#include "BLI_task.hh"
-namespace blender::nodes::node_geo_field_on_domain_cc {
+#include "NOD_socket_search_link.hh"
+
+namespace blender::nodes::node_geo_interpolate_domain_cc {
static void node_declare(NodeDeclarationBuilder &b)
{
@@ -67,30 +69,47 @@ static void node_update(bNodeTree *ntree, bNode *node)
nodeSetSocketAvailability(ntree, sock_out_bool, data_type == CD_PROP_BOOL);
}
-class FieldOnDomain final : public GeometryFieldInput {
+static void node_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const bNodeType &node_type = params.node_type();
+ const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
+ (eNodeSocketDatatype)params.other_socket().type);
+ if (type && *type != CD_PROP_STRING) {
+ params.add_item(IFACE_("Value"), [node_type, type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node(node_type);
+ node.custom2 = *type;
+ params.update_and_connect_available_socket(node, "Value");
+ });
+ }
+}
+
+class InterpolateDomain final : public bke::GeometryFieldInput {
private:
GField src_field_;
eAttrDomain src_domain_;
public:
- FieldOnDomain(GField field, eAttrDomain domain)
- : GeometryFieldInput(field.cpp_type(), "Field on Domain"),
+ InterpolateDomain(GField field, eAttrDomain domain)
+ : bke::GeometryFieldInput(field.cpp_type(), "Interpolate Domain"),
src_field_(std::move(field)),
src_domain_(domain)
{
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask /* mask */) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ IndexMask /*mask*/) const final
{
- const GeometryComponentFieldContext context{component, src_domain_};
- FieldEvaluator value_evaluator{context, component.attribute_domain_size(src_domain_)};
- value_evaluator.add(src_field_);
+ const bke::AttributeAccessor attributes = *context.attributes();
+
+ const bke::GeometryFieldContext other_domain_context{
+ context.geometry(), context.type(), src_domain_};
+ const int64_t src_domain_size = attributes.domain_size(src_domain_);
+ GArray values(src_field_.cpp_type(), src_domain_size);
+ FieldEvaluator value_evaluator{other_domain_context, src_domain_size};
+ value_evaluator.add_with_destination(src_field_, values.as_mutable_span());
value_evaluator.evaluate();
- const GVArray &values = value_evaluator.get_evaluated(0);
-
- return component.attributes()->adapt_domain(values, src_domain_, domain);
+ return attributes.adapt_domain(
+ GVArray::ForGArray(std::move(values)), src_domain_, context.domain());
}
};
@@ -123,24 +142,26 @@ static void node_geo_exec(GeoNodeExecParams params)
using T = decltype(dummy);
static const std::string identifier = "Value_" + identifier_suffix(data_type);
Field<T> src_field = params.extract_input<Field<T>>(identifier);
- Field<T> dst_field{std::make_shared<FieldOnDomain>(std::move(src_field), domain)};
+ Field<T> dst_field{std::make_shared<InterpolateDomain>(std::move(src_field), domain)};
params.set_output(identifier, std::move(dst_field));
});
}
-} // namespace blender::nodes::node_geo_field_on_domain_cc
+} // namespace blender::nodes::node_geo_interpolate_domain_cc
-void register_node_type_geo_field_on_domain()
+void register_node_type_geo_interpolate_domain()
{
- namespace file_ns = blender::nodes::node_geo_field_on_domain_cc;
+ namespace file_ns = blender::nodes::node_geo_interpolate_domain_cc;
static bNodeType ntype;
- geo_node_type_base(&ntype, GEO_NODE_FIELD_ON_DOMAIN, "Field on Domain", NODE_CLASS_CONVERTER);
+ geo_node_type_base(
+ &ntype, GEO_NODE_INTERPOLATE_DOMAIN, "Interpolate Domain", NODE_CLASS_CONVERTER);
ntype.geometry_node_execute = file_ns::node_geo_exec;
ntype.declare = file_ns::node_declare;
ntype.draw_buttons = file_ns::node_layout;
ntype.initfunc = file_ns::node_init;
ntype.updatefunc = file_ns::node_update;
+ ntype.gather_link_search_ops = file_ns::node_gather_link_searches;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
index 083a505539a..9fdf7fe7d31 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc
@@ -177,7 +177,7 @@ static void join_component_type(Span<GeometrySet> src_geometry_sets, GeometrySet
static void node_geo_exec(GeoNodeExecParams params)
{
- Vector<GeometrySet> geometry_sets = params.extract_multi_input<GeometrySet>("Geometry");
+ Vector<GeometrySet> geometry_sets = params.extract_input<Vector<GeometrySet>>("Geometry");
GeometrySet geometry_set_result;
join_component_type<MeshComponent>(geometry_sets, geometry_set_result);
@@ -185,6 +185,7 @@ static void node_geo_exec(GeoNodeExecParams params)
join_component_type<InstancesComponent>(geometry_sets, geometry_set_result);
join_component_type<VolumeComponent>(geometry_sets, geometry_set_result);
join_component_type<CurveComponent>(geometry_sets, geometry_set_result);
+ join_component_type<GeometryComponentEditData>(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_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
index ca613ae009b..628688f3b47 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc
@@ -23,46 +23,56 @@ static void node_declare(NodeDeclarationBuilder &b)
static void select_mesh_by_material(const Mesh &mesh,
const Material *material,
const IndexMask mask,
- const MutableSpan<bool> r_selection)
+ MutableSpan<bool> r_selection)
{
BLI_assert(mesh.totpoly >= r_selection.size());
- Vector<int> material_indices;
+ Vector<int> slots;
for (const int i : IndexRange(mesh.totcol)) {
if (mesh.mat[i] == material) {
- material_indices.append(i);
+ slots.append(i);
}
}
+ const AttributeAccessor attributes = mesh.attributes();
+ const VArray<int> material_indices = attributes.lookup_or_default<int>(
+ "material_index", ATTR_DOMAIN_FACE, 0);
+ if (material != nullptr && material_indices.is_single() &&
+ material_indices.get_internal_single() == 0) {
+ r_selection.fill_indices(mask, false);
+ return;
+ }
+
+ const VArraySpan<int> material_indices_span(material_indices);
+
threading::parallel_for(mask.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
const int face_index = mask[i];
- r_selection[i] = material_indices.contains(mesh.mpoly[face_index].mat_nr);
+ r_selection[i] = slots.contains(material_indices_span[face_index]);
}
});
}
-class MaterialSelectionFieldInput final : public GeometryFieldInput {
+class MaterialSelectionFieldInput final : public bke::GeometryFieldInput {
Material *material_;
public:
MaterialSelectionFieldInput(Material *material)
- : GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"), material_(material)
+ : bke::GeometryFieldInput(CPPType::get<bool>(), "Material Selection node"),
+ material_(material)
{
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
- const eAttrDomain domain,
- IndexMask mask) const final
+ GVArray get_varray_for_context(const bke::GeometryFieldContext &context,
+ const IndexMask mask) const final
{
- if (component.type() != GEO_COMPONENT_TYPE_MESH) {
+ if (context.type() != GEO_COMPONENT_TYPE_MESH) {
return {};
}
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- const Mesh *mesh = mesh_component.get_for_read();
+ const Mesh *mesh = context.mesh();
if (mesh == nullptr) {
return {};
}
-
+ const eAttrDomain domain = context.domain();
if (domain == ATTR_DOMAIN_FACE) {
Array<bool> selection(mask.min_array_size());
select_mesh_by_material(*mesh, material_, mask, selection);
@@ -71,7 +81,7 @@ class MaterialSelectionFieldInput final : public GeometryFieldInput {
Array<bool> selection(mesh->totpoly);
select_mesh_by_material(*mesh, material_, IndexMask(mesh->totpoly), selection);
- return mesh_component.attributes()->adapt_domain<bool>(
+ return mesh->attributes().adapt_domain<bool>(
VArray<bool>::ForContainer(std::move(selection)), ATTR_DOMAIN_FACE, domain);
return nullptr;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
index 4bc84a7a08a..f64f997810e 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_merge_by_distance.cc
@@ -1,5 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+#include "DNA_pointcloud_types.h"
+
#include "GEO_mesh_merge_by_distance.hh"
#include "GEO_point_merge_by_distance.hh"
@@ -35,13 +38,12 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_points,
+static PointCloud *pointcloud_merge_by_distance(const PointCloud &src_points,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = src_points.attribute_domain_size(ATTR_DOMAIN_POINT);
- GeometryComponentFieldContext context{src_points, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ bke::PointCloudFieldContext context{src_points};
+ FieldEvaluator evaluator{context, src_points.totpoint};
evaluator.add(selection_field);
evaluator.evaluate();
@@ -53,28 +55,25 @@ static PointCloud *pointcloud_merge_by_distance(const PointCloudComponent &src_p
return geometry::point_merge_by_distance(src_points, merge_distance, selection);
}
-static std::optional<Mesh *> mesh_merge_by_distance_connected(const MeshComponent &mesh_component,
+static std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- Array<bool> selection(src_num);
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ Array<bool> selection(mesh.totvert);
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add_with_destination(selection_field, selection.as_mutable_span());
evaluator.evaluate();
- const Mesh &mesh = *mesh_component.get_for_read();
return geometry::mesh_merge_by_distance_connected(mesh, selection, merge_distance, false);
}
-static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mesh_component,
+static std::optional<Mesh *> mesh_merge_by_distance_all(const Mesh &mesh,
const float merge_distance,
const Field<bool> &selection_field)
{
- const int src_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- GeometryComponentFieldContext context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator evaluator{context, src_num};
+ bke::MeshFieldContext context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator evaluator{context, mesh.totvert};
evaluator.add(selection_field);
evaluator.evaluate();
@@ -83,7 +82,6 @@ static std::optional<Mesh *> mesh_merge_by_distance_all(const MeshComponent &mes
return std::nullopt;
}
- const Mesh &mesh = *mesh_component.get_for_read();
return geometry::mesh_merge_by_distance_all(mesh, selection, merge_distance);
}
@@ -98,22 +96,20 @@ static void node_geo_exec(GeoNodeExecParams params)
const float merge_distance = params.extract_input<float>("Distance");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_pointcloud()) {
- PointCloud *result = pointcloud_merge_by_distance(
- *geometry_set.get_component_for_read<PointCloudComponent>(), merge_distance, selection);
+ if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) {
+ PointCloud *result = pointcloud_merge_by_distance(*pointcloud, merge_distance, selection);
if (result) {
geometry_set.replace_pointcloud(result);
}
}
- if (geometry_set.has_mesh()) {
- const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
+ if (const Mesh *mesh = geometry_set.get_mesh_for_read()) {
std::optional<Mesh *> result;
switch (mode) {
case GEO_NODE_MERGE_BY_DISTANCE_MODE_ALL:
- result = mesh_merge_by_distance_all(component, merge_distance, selection);
+ result = mesh_merge_by_distance_all(*mesh, merge_distance, selection);
break;
case GEO_NODE_MERGE_BY_DISTANCE_MODE_CONNECTED:
- result = mesh_merge_by_distance_connected(component, merge_distance, selection);
+ result = mesh_merge_by_distance_connected(*mesh, merge_distance, selection);
break;
default:
BLI_assert_unreachable();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc
new file mode 100644
index 00000000000..88cccfb2f94
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_face_set_boundaries.cc
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+
+#include "BKE_mesh.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes::node_geo_mesh_face_set_boundaries_cc {
+
+static void node_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Int>(N_("Face Set"))
+ .default_value(0)
+ .hide_value()
+ .supports_field()
+ .description(N_("An identifier for the group of each face. All contiguous faces with the "
+ "same value are in the same region"));
+ b.add_output<decl::Bool>(N_("Boundary Edges"))
+ .field_source()
+ .description(N_("The edges that lie on the boundaries between the different face sets"));
+}
+
+class BoundaryFieldInput final : public bke::MeshFieldInput {
+ private:
+ const Field<int> face_set;
+
+ public:
+ BoundaryFieldInput(const Field<int> face_set)
+ : bke::MeshFieldInput(CPPType::get<bool>(), "Boundary Field"), face_set(face_set)
+ {
+ category_ = Category::Generated;
+ }
+
+ GVArray get_varray_for_context(const Mesh &mesh,
+ const eAttrDomain domain,
+ const IndexMask /*mask*/) const final
+ {
+ const bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE};
+ FieldEvaluator face_evaluator{face_context, mesh.totpoly};
+ face_evaluator.add(face_set);
+ face_evaluator.evaluate();
+ const VArray<int> face_set = face_evaluator.get_evaluated<int>(0);
+
+ Array<bool> boundary(mesh.totedge, false);
+ Array<bool> edge_visited(mesh.totedge, false);
+ Array<int> edge_face_set(mesh.totedge, 0);
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+ for (const int i : polys.index_range()) {
+ const MPoly &poly = polys[i];
+ for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) {
+ const int edge = loop.e;
+ if (edge_visited[edge]) {
+ if (edge_face_set[edge] != face_set[i]) {
+ /* This edge is connected to two faces on different face sets. */
+ boundary[edge] = true;
+ }
+ }
+ edge_visited[edge] = true;
+ edge_face_set[edge] = face_set[i];
+ }
+ }
+ return mesh.attributes().adapt_domain<bool>(
+ VArray<bool>::ForContainer(std::move(boundary)), ATTR_DOMAIN_EDGE, domain);
+ }
+};
+
+static void node_geo_exec(GeoNodeExecParams params)
+{
+ const Field<int> face_set_field = params.extract_input<Field<int>>("Face Set");
+ Field<bool> face_set_boundaries{std::make_shared<BoundaryFieldInput>(face_set_field)};
+ params.set_output("Boundary Edges", std::move(face_set_boundaries));
+}
+
+} // namespace blender::nodes::node_geo_mesh_face_set_boundaries_cc
+
+void register_node_type_geo_mesh_face_set_boundaries()
+{
+ namespace file_ns = blender::nodes::node_geo_mesh_face_set_boundaries_cc;
+
+ static bNodeType ntype;
+ geo_node_type_base(
+ &ntype, GEO_NODE_MESH_FACE_SET_BOUNDARIES, "Face Set Boundaries", NODE_CLASS_INPUT);
+ ntype.declare = file_ns::node_declare;
+ ntype.geometry_node_execute = file_ns::node_geo_exec;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc
index 9e85547315c..801b3c78060 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
@@ -109,10 +109,10 @@ static Mesh *create_circle_mesh(const float radius,
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};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
/* Assign vertex coordinates. */
const float angle_delta = 2.0f * (M_PI / static_cast<float>(verts_num));
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 cb79ef93de9..1f9ad9f6ea2 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
@@ -255,7 +255,7 @@ int ConeConfig::calculate_total_corners()
return corner_total;
}
-static void calculate_cone_vertices(const MutableSpan<MVert> &verts, const ConeConfig &config)
+static void calculate_cone_verts(const MutableSpan<MVert> &verts, const ConeConfig &config)
{
Array<float2> circle(config.circle_segments);
const float angle_delta = 2.0f * (M_PI / static_cast<float>(config.circle_segments));
@@ -480,12 +480,12 @@ static void calculate_selection_outputs(Mesh *mesh,
const ConeConfig &config,
ConeAttributeOutputs &attribute_outputs)
{
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
/* Populate "Top" selection output. */
if (attribute_outputs.top_id) {
const bool face = !config.top_is_point && config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
- SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
attribute_outputs.top_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
if (config.top_is_point) {
@@ -501,7 +501,7 @@ static void calculate_selection_outputs(Mesh *mesh,
if (attribute_outputs.bottom_id) {
const bool face = !config.bottom_is_point &&
config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE;
- SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
attribute_outputs.bottom_id.get(), face ? ATTR_DOMAIN_FACE : ATTR_DOMAIN_POINT);
if (config.bottom_is_point) {
@@ -518,7 +518,7 @@ static void calculate_selection_outputs(Mesh *mesh,
/* Populate "Side" selection output. */
if (attribute_outputs.side_id) {
- SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_only_span<bool>(
+ SpanAttributeWriter<bool> selection = attributes.lookup_or_add_for_write_span<bool>(
attribute_outputs.side_id.get(), ATTR_DOMAIN_FACE);
selection.span.slice(config.side_faces_start, config.side_faces_len).fill(true);
@@ -536,7 +536,7 @@ static void calculate_selection_outputs(Mesh *mesh,
*/
static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config)
{
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
"uv_map", ATTR_DOMAIN_CORNER);
@@ -657,7 +657,7 @@ static Mesh *create_vertex_mesh()
{
/* Returns a mesh with a single vertex at the origin. */
Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0);
- copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f);
+ copy_v3_fl3(mesh->verts_for_write().first().co, 0.0f, 0.0f, 0.0f);
return mesh;
}
@@ -689,12 +689,12 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top,
config.tot_verts, config.tot_edges, 0, config.tot_corners, config.tot_faces);
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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
- calculate_cone_vertices(verts, config);
+ calculate_cone_verts(verts, config);
calculate_cone_edges(edges, config);
calculate_cone_faces(loops, polys, config);
calculate_cone_uvs(mesh, config);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
index 9baf0b3171e..6f0b8283b72 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc
@@ -18,7 +18,7 @@ namespace blender::nodes {
static void calculate_uvs(
Mesh *mesh, Span<MVert> verts, Span<MLoop> loops, const float size_x, const float size_y)
{
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
"uv_map", ATTR_DOMAIN_CORNER);
@@ -49,10 +49,10 @@ Mesh *create_grid_mesh(const int verts_x,
0,
edges_x * edges_y * 4,
edges_x * edges_y);
- 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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
{
const float dx = edges_x == 0 ? 0.0f : size_x / edges_x;
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 2af86b4b1d2..4fd6399f4eb 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
@@ -179,10 +179,11 @@ Mesh *create_line_mesh(const float3 start, const float3 delta, const int count)
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};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
threading::parallel_invoke(
+ 1024 < count,
[&]() {
threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) {
for (const int i : range) {
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 f78752387c6..d39e72b7f0a 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
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_task.hh"
+
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
@@ -61,15 +63,26 @@ static int sphere_face_total(const int segments, const int rings)
* Also calculate vertex normals here, since the calculation is trivial, and it allows avoiding the
* calculation later, if it's necessary. The vertex normals are just the normalized positions.
*/
-static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
- MutableSpan<float3> vert_normals,
- const float radius,
- const int segments,
- const int rings)
+BLI_NOINLINE static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
+ MutableSpan<float3> vert_normals,
+ const float radius,
+ const int segments,
+ const int rings)
{
const float delta_theta = M_PI / rings;
const float delta_phi = (2.0f * M_PI) / segments;
+ Array<float, 64> segment_cosines(segments + 1);
+ for (const int segment : IndexRange(1, segments)) {
+ const float phi = segment * delta_phi;
+ segment_cosines[segment] = std::cos(phi);
+ }
+ Array<float, 64> segment_sines(segments + 1);
+ for (const int segment : IndexRange(1, segments)) {
+ const float phi = segment * delta_phi;
+ segment_sines[segment] = std::sin(phi);
+ }
+
copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
vert_normals.first() = float3(0.0f, 0.0f, 1.0f);
@@ -79,9 +92,8 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
const float sin_theta = std::sin(theta);
const float z = std::cos(theta);
for (const int segment : IndexRange(1, segments)) {
- const float phi = segment * delta_phi;
- const float x = sin_theta * std::cos(phi);
- const float y = sin_theta * std::sin(phi);
+ const float x = sin_theta * segment_cosines[segment];
+ const float y = sin_theta * segment_sines[segment];
copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
vert_normals[vert_index] = float3(x, y, z);
vert_index++;
@@ -92,9 +104,9 @@ static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
vert_normals.last() = float3(0.0f, 0.0f, -1.0f);
}
-static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
- const int segments,
- const int rings)
+BLI_NOINLINE static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
+ const int segments,
+ const int rings)
{
int edge_index = 0;
@@ -142,20 +154,46 @@ static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
}
}
-static void calculate_sphere_faces(MutableSpan<MLoop> loops,
- MutableSpan<MPoly> polys,
- const int segments,
- const int rings)
+BLI_NOINLINE static void calculate_sphere_faces(MutableSpan<MPoly> polys, const int segments)
{
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++];
+ for (MPoly &poly : polys.take_front(segments)) {
+ poly.loopstart = loop_index;
+ poly.totloop = 3;
+ loop_index += 3;
+ }
+
+ /* Add the middle quads. */
+ for (MPoly &poly : polys.drop_front(segments).drop_back(segments)) {
+ poly.loopstart = loop_index;
+ poly.totloop = 4;
+ loop_index += 4;
+ }
+
+ /* Add the triangles connected to the bottom vertex. */
+ for (MPoly &poly : polys.take_back(segments)) {
poly.loopstart = loop_index;
poly.totloop = 3;
+ loop_index += 3;
+ }
+}
+
+BLI_NOINLINE static void calculate_sphere_corners(MutableSpan<MLoop> loops,
+ const int segments,
+ const int rings)
+{
+ int loop_index = 0;
+ auto segment_next_or_first = [&](const int segment) {
+ return segment == segments - 1 ? 0 : segment + 1;
+ };
+
+ /* Add the triangles connected to the top vertex. */
+ const int first_vert_ring_index_start = 1;
+ for (const int segment : IndexRange(segments)) {
+ const int segment_next = segment_next_or_first(segment);
+
MLoop &loop_a = loops[loop_index++];
loop_a.v = 0;
loop_a.e = segment;
@@ -163,8 +201,8 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
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;
+ loop_c.v = first_vert_ring_index_start + segment_next;
+ loop_c.e = segment_next;
}
int ring_vert_index_start = 1;
@@ -175,9 +213,7 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
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;
+ const int segment_next = segment_next_or_first(segment);
MLoop &loop_a = loops[loop_index++];
loop_a.v = ring_vert_index_start + segment;
@@ -186,10 +222,10 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
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;
+ loop_c.v = next_ring_vert_index_start + segment_next;
+ loop_c.e = ring_vertical_edge_index_start + segment_next;
MLoop &loop_d = loops[loop_index++];
- loop_d.v = ring_vert_index_start + (segment + 1) % segments;
+ loop_d.v = ring_vert_index_start + segment_next;
loop_d.e = ring_edge_index_start + segment;
}
ring_vert_index_start += segments;
@@ -202,15 +238,13 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
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;
+ const int segment_next = segment_next_or_first(segment);
MLoop &loop_a = loops[loop_index++];
loop_a.v = last_vert_index;
- loop_a.e = bottom_edge_fan_start + (segment + 1) % segments;
+ loop_a.e = bottom_edge_fan_start + segment_next;
MLoop &loop_b = loops[loop_index++];
- loop_b.v = last_vert_ring_start + (segment + 1) % segments;
+ loop_b.v = last_vert_ring_start + segment_next;
loop_b.e = last_edge_ring_start + segment;
MLoop &loop_c = loops[loop_index++];
loop_c.v = last_vert_ring_start + segment;
@@ -218,9 +252,9 @@ static void calculate_sphere_faces(MutableSpan<MLoop> loops,
}
}
-static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
+BLI_NOINLINE static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
{
- MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*mesh);
+ MutableAttributeAccessor attributes = mesh->attributes_for_write();
SpanAttributeWriter<float2> uv_attribute = attributes.lookup_or_add_for_write_only_span<float2>(
"uv_map", ATTR_DOMAIN_CORNER);
@@ -229,29 +263,31 @@ static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float r
int loop_index = 0;
const float dy = 1.0f / rings;
+ const float segments_inv = 1.0f / segments;
+
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);
+ uvs[loop_index++] = float2((segment + 0.5f) * segments_inv, 0.0f);
+ uvs[loop_index++] = float2(segment * segments_inv, dy);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, 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);
+ uvs[loop_index++] = float2(segment * segments_inv, ring / rings);
+ uvs[loop_index++] = float2(segment * segments_inv, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, (ring + 1.0f) / rings);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, 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);
+ uvs[loop_index++] = float2((segment + 0.5f) * segments_inv, 1.0f);
+ uvs[loop_index++] = float2((segment + 1.0f) * segments_inv, 1.0f - dy);
+ uvs[loop_index++] = float2(segment * segments_inv, 1.0f - dy);
}
uv_attribute.finish();
@@ -265,20 +301,22 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const
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};
-
- MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert};
- calculate_sphere_vertex_data(verts, vert_normals, radius, segments, rings);
- BKE_mesh_vertex_normals_clear_dirty(mesh);
-
- calculate_sphere_edge_indices(edges, segments, rings);
-
- calculate_sphere_faces(loops, polys, segments, rings);
-
- calculate_sphere_uvs(mesh, segments, rings);
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MEdge> edges = mesh->edges_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
+
+ threading::parallel_invoke(
+ 1024 < segments * rings,
+ [&]() {
+ MutableSpan vert_normals{(float3 *)BKE_mesh_vertex_normals_for_write(mesh), mesh->totvert};
+ calculate_sphere_vertex_data(verts, vert_normals, radius, segments, rings);
+ BKE_mesh_vertex_normals_clear_dirty(mesh);
+ },
+ [&]() { calculate_sphere_edge_indices(edges, segments, rings); },
+ [&]() { calculate_sphere_faces(polys, segments); },
+ [&]() { calculate_sphere_corners(loops, segments, rings); },
+ [&]() { calculate_sphere_uvs(mesh, segments, rings); });
return 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
index f6ee3d00dee..4d08fa40a29 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "GEO_mesh_to_curve.hh"
#include "node_geometry_util.hh"
@@ -18,24 +20,25 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet geometry_set = params.extract_input<GeometrySet>("Mesh");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_mesh()) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
+ geometry_set.remove_geometry_during_modify();
return;
}
- const MeshComponent &component = *geometry_set.get_component_for_read<MeshComponent>();
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE};
- fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)};
+ bke::MeshFieldContext context{*mesh, ATTR_DOMAIN_EDGE};
+ fn::FieldEvaluator evaluator{context, mesh->totedge};
evaluator.add(params.get_input<Field<bool>>("Selection"));
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
if (selection.size() == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.remove_geometry_during_modify();
return;
}
- geometry_set.replace_curves(geometry::mesh_to_curve_convert(component, selection));
- geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES});
+ bke::CurvesGeometry curves = geometry::mesh_to_curve_convert(*mesh, selection);
+ geometry_set.replace_curves(bke::curves_new_nomain(std::move(curves)));
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_CURVE});
});
params.set_output("Curve", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
index 8e621d3ed97..ce06ccbda75 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc
@@ -1,7 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BLI_array_utils.hh"
#include "BLI_task.hh"
+#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "BKE_attribute_math.hh"
@@ -43,35 +45,24 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static void materialize_compressed_to_uninitialized_threaded(const GVArray &src,
- const IndexMask mask,
- GMutableSpan dst)
-{
- BLI_assert(src.type() == dst.type());
- BLI_assert(mask.size() == dst.size());
- threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) {
- src.materialize_compressed_to_uninitialized(mask.slice(range), dst.slice(range).data());
- });
-}
-
static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
Field<float3> &position_field,
Field<float> &radius_field,
Field<bool> &selection_field,
const eAttrDomain domain)
{
- const MeshComponent *mesh_component = geometry_set.get_component_for_read<MeshComponent>();
- if (mesh_component == nullptr) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ const Mesh *mesh = geometry_set.get_mesh_for_read();
+ if (mesh == nullptr) {
+ geometry_set.remove_geometry_during_modify();
return;
}
- GeometryComponentFieldContext field_context{*mesh_component, domain};
- const int domain_num = mesh_component->attribute_domain_size(domain);
- if (domain_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ const int domain_size = mesh->attributes().domain_size(domain);
+ if (domain_size == 0) {
+ geometry_set.remove_geometry_during_modify();
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ bke::MeshFieldContext field_context{*mesh, domain};
+ fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
/* Evaluating directly into the point cloud doesn't work because we are not using the full
* "min_array_size" array but compressing the selected elements into the final array with no
@@ -83,19 +74,16 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size());
geometry_set.replace_pointcloud(pointcloud);
- MutableAttributeAccessor pointcloud_attributes = bke::pointcloud_attributes_for_write(
- *pointcloud);
+ MutableAttributeAccessor dst_attributes = pointcloud->attributes_for_write();
- GSpanAttributeWriter position = pointcloud_attributes.lookup_or_add_for_write_only_span(
+ GSpanAttributeWriter position = dst_attributes.lookup_or_add_for_write_only_span(
"position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3);
- materialize_compressed_to_uninitialized_threaded(
- evaluator.get_evaluated(0), selection, position.span);
+ array_utils::gather(evaluator.get_evaluated(0), selection, position.span);
position.finish();
- GSpanAttributeWriter radius = pointcloud_attributes.lookup_or_add_for_write_only_span(
+ GSpanAttributeWriter radius = dst_attributes.lookup_or_add_for_write_only_span(
"radius", ATTR_DOMAIN_POINT, CD_PROP_FLOAT);
- materialize_compressed_to_uninitialized_threaded(
- evaluator.get_evaluated(1), selection, radius.span);
+ array_utils::gather(evaluator.get_evaluated(1), selection, radius.span);
radius.finish();
Map<AttributeIDRef, AttributeKind> attributes;
@@ -103,19 +91,21 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
{GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes);
attributes.remove("position");
+ const AttributeAccessor src_attributes = mesh->attributes();
+
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
- GVArray src = mesh_component->attributes()->lookup_or_default(attribute_id, domain, data_type);
- GSpanAttributeWriter dst = pointcloud_attributes.lookup_or_add_for_write_only_span(
+ GVArray src = src_attributes.lookup_or_default(attribute_id, domain, data_type);
+ GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
- materialize_compressed_to_uninitialized_threaded(src, selection, dst.span);
+ array_utils::gather(src, selection, dst.span);
dst.finish();
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_POINT_CLOUD});
}
static void node_geo_exec(GeoNodeExecParams params)
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
index 5890e070b2f..f6fa5c99013 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_volume.cc
@@ -149,19 +149,18 @@ static void node_geo_exec(GeoNodeExecParams params)
{
#ifdef WITH_OPENVDB
GeometrySet geometry_set(params.extract_input<GeometrySet>("Mesh"));
-
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_mesh()) {
Volume *volume = create_volume_from_mesh(*geometry_set.get_mesh_for_read(), params);
geometry_set.replace_volume(volume);
- geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME});
}
});
params.set_output("Volume", std::move(geometry_set));
#else
+ params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
- params.set_default_remaining_outputs();
return;
#endif
}
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 0b2159364f1..691988249df 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc
@@ -80,7 +80,7 @@ static void node_geo_exec(GeoNodeExecParams params)
else {
geometry_set = bke::object_get_evaluated_geometry_set(*object);
if (transform_space_relative) {
- transform_geometry_set(geometry_set, transform, *params.depsgraph());
+ transform_geometry_set(params, geometry_set, transform, *params.depsgraph());
}
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points.cc b/source/blender/nodes/geometry/nodes/node_geo_points.cc
index dd32e6714f4..4a294076834 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points.cc
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_pointcloud.h"
+#include "DNA_pointcloud_types.h"
#include "BLI_task.hh"
@@ -69,10 +70,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float3> position_field = params.extract_input<Field<float3>>("Position");
Field<float> radius_field = params.extract_input<Field<float>>("Radius");
- PointCloud *new_point_cloud = BKE_pointcloud_new_nomain(count);
- GeometrySet geometry_set = GeometrySet::create_with_pointcloud(new_point_cloud);
- PointCloudComponent &points = geometry_set.get_component_for_write<PointCloudComponent>();
- MutableAttributeAccessor attributes = *points.attributes_for_write();
+ PointCloud *points = BKE_pointcloud_new_nomain(count);
+ MutableAttributeAccessor attributes = points->attributes_for_write();
AttributeWriter<float3> output_position = attributes.lookup_or_add_for_write<float3>(
"position", ATTR_DOMAIN_POINT);
AttributeWriter<float> output_radii = attributes.lookup_or_add_for_write<float>(
@@ -86,7 +85,7 @@ static void node_geo_exec(GeoNodeExecParams params)
output_position.finish();
output_radii.finish();
- params.set_output("Geometry", std::move(geometry_set));
+ params.set_output("Geometry", GeometrySet::create_with_pointcloud(points));
}
} // namespace blender::nodes::node_geo_points_cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
index 9cc64d4bc44..4ac3bf712f7 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc
@@ -2,6 +2,8 @@
#include "BLI_task.hh"
+#include "DNA_pointcloud_types.h"
+
#include "BKE_attribute_math.hh"
#include "BKE_mesh.h"
@@ -18,33 +20,22 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Mesh"));
}
-template<typename T>
-static void copy_attribute_to_vertices(const Span<T> src, const IndexMask mask, MutableSpan<T> dst)
-{
- for (const int i : mask.index_range()) {
- dst[i] = src[mask[i]];
- }
-}
-
/* One improvement would be to move the attribute arrays directly to the mesh when possible. */
static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
Field<bool> &selection_field)
{
- const PointCloudComponent *point_component =
- geometry_set.get_component_for_read<PointCloudComponent>();
- if (point_component == nullptr) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ const PointCloud *points = geometry_set.get_pointcloud_for_read();
+ if (points == nullptr) {
+ geometry_set.remove_geometry_during_modify();
return;
}
-
- GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT};
- const int domain_num = point_component->attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_num == 0) {
- geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES});
+ if (points->totpoint == 0) {
+ geometry_set.remove_geometry_during_modify();
return;
}
- fn::FieldEvaluator selection_evaluator{field_context, domain_num};
+ bke::PointCloudFieldContext field_context{*points};
+ fn::FieldEvaluator selection_evaluator{field_context, points->totpoint};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0);
@@ -55,28 +46,23 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set,
Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0);
geometry_set.replace_mesh(mesh);
- MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
+
+ const AttributeAccessor src_attributes = points->attributes();
+ MutableAttributeAccessor dst_attributes = mesh->attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
- GVArray src = point_component->attributes()->lookup_or_default(
+ GVArray src = src_attributes.lookup_or_default(attribute_id, ATTR_DOMAIN_POINT, data_type);
+ GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(
attribute_id, ATTR_DOMAIN_POINT, data_type);
- GSpanAttributeWriter dst =
- mesh_component.attributes_for_write()->lookup_or_add_for_write_only_span(
- attribute_id, ATTR_DOMAIN_POINT, data_type);
if (dst && src) {
- attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
- using T = decltype(dummy);
- VArray<T> src_typed = src.typed<T>();
- VArraySpan<T> src_typed_span{src_typed};
- copy_attribute_to_vertices(src_typed_span, selection, dst.span.typed<T>());
- });
+ src.materialize_compressed_to_uninitialized(selection, dst.span.data());
dst.finish();
}
}
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
}
static void node_geo_exec(GeoNodeExecParams params)
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 28a01a5cbce..0990eebb903 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
@@ -170,7 +170,7 @@ static void gather_point_data_from_component(GeoNodeExecParams &params,
"position", ATTR_DOMAIN_POINT, {0, 0, 0});
Field<float> radius_field = params.get_input<Field<float>>("Radius");
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
+ bke::GeometryFieldContext field_context{component, ATTR_DOMAIN_POINT};
const int domain_num = component.attribute_domain_size(ATTR_DOMAIN_POINT);
r_positions.resize(r_positions.size() + domain_num);
@@ -224,24 +224,23 @@ static void initialize_volume_component_from_points(GeoNodeExecParams &params,
new_grid->transform().postScale(voxel_size);
BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid));
- r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES});
+ r_geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_VOLUME});
r_geometry_set.replace_volume(volume);
}
#endif
static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
-
#ifdef WITH_OPENVDB
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Points");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
initialize_volume_component_from_points(params, geometry_set);
});
params.set_output("Volume", std::move(geometry_set));
#else
+ params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
- params.set_default_remaining_outputs();
#endif
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
index f81748da587..f657b128c51 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc
@@ -208,7 +208,7 @@ class RaycastFunction : public fn::MultiFunction {
GeometryNodeRaycastMapMode mapping_;
/** The field for data evaluated on the target geometry. */
- std::optional<GeometryComponentFieldContext> target_context_;
+ std::optional<bke::MeshFieldContext> target_context_;
std::unique_ptr<FieldEvaluator> target_evaluator_;
const GVArray *target_data_ = nullptr;
@@ -310,9 +310,9 @@ class RaycastFunction : public fn::MultiFunction {
if (!src_field) {
return;
}
- const MeshComponent &mesh_component = *target_.get_component_for_read<MeshComponent>();
- target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
- const int domain_size = mesh_component.attribute_domain_size(domain_);
+ const Mesh &mesh = *target_.get_mesh_for_read();
+ target_context_.emplace(bke::MeshFieldContext{mesh, domain_});
+ const int domain_size = mesh.attributes().domain_size(domain_);
target_evaluator_ = std::make_unique<FieldEvaluator>(*target_context_, domain_size);
target_evaluator_->add(std::move(src_field));
target_evaluator_->evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
index ee279ba58f9..1b398f63691 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_remove_attribute.cc
@@ -55,7 +55,7 @@ static void node_geo_exec(GeoNodeExecParams params)
});
if (attribute_exists && !cannot_delete) {
- params.used_named_attribute(name, eNamedAttrUsage::Remove);
+ params.used_named_attribute(name, NamedAttributeUsage::Remove);
}
if (!attribute_exists) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
index d414bb1fa1d..4ed94e67e74 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc
@@ -18,10 +18,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void rotate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
{
- GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
- const int domain_num = instances_component.instances_num();
-
- fn::FieldEvaluator evaluator{field_context, domain_num};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
evaluator.add(params.extract_input<Field<float3>>("Rotation"));
evaluator.add(params.extract_input<Field<float3>>("Pivot Point"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
index d674f611c9f..2ebbf88b8ad 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_elements.cc
@@ -147,14 +147,22 @@ static float4x4 create_single_axis_transform(const float3 &center,
return transform;
}
-using GetVertexIndicesFn =
- FunctionRef<void(const Mesh &mesh, int element_index, VectorSet<int> &r_vertex_indices)>;
+using GetVertexIndicesFn = FunctionRef<void(Span<MEdge> edges,
+ Span<MPoly> polys,
+ Span<MLoop> loops,
+ int element_index,
+ VectorSet<int> &r_vertex_indices)>;
static void scale_vertex_islands_uniformly(Mesh &mesh,
const Span<ElementIsland> islands,
const UniformScaleParams &params,
const GetVertexIndicesFn get_vertex_indices)
{
+ MutableSpan<MVert> verts = mesh.verts_for_write();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
for (const int island_index : range) {
const ElementIsland &island = islands[island_index];
@@ -164,7 +172,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh,
VectorSet<int> vertex_indices;
for (const int poly_index : island.element_indices) {
- get_vertex_indices(mesh, poly_index, vertex_indices);
+ get_vertex_indices(edges, polys, loops, poly_index, vertex_indices);
center += params.centers[poly_index];
scale += params.scales[poly_index];
}
@@ -175,7 +183,7 @@ static void scale_vertex_islands_uniformly(Mesh &mesh,
center *= f;
for (const int vert_index : vertex_indices) {
- MVert &vert = mesh.mvert[vert_index];
+ MVert &vert = verts[vert_index];
const float3 old_position = vert.co;
const float3 new_position = transform_with_uniform_scale(old_position, center, scale);
copy_v3_v3(vert.co, new_position);
@@ -191,6 +199,11 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
const AxisScaleParams &params,
const GetVertexIndicesFn get_vertex_indices)
{
+ MutableSpan<MVert> verts = mesh.verts_for_write();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
threading::parallel_for(islands.index_range(), 256, [&](const IndexRange range) {
for (const int island_index : range) {
const ElementIsland &island = islands[island_index];
@@ -201,7 +214,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
VectorSet<int> vertex_indices;
for (const int poly_index : island.element_indices) {
- get_vertex_indices(mesh, poly_index, vertex_indices);
+ get_vertex_indices(edges, polys, loops, poly_index, vertex_indices);
center += params.centers[poly_index];
scale += params.scales[poly_index];
axis += params.axis_vectors[poly_index];
@@ -219,7 +232,7 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
const float4x4 transform = create_single_axis_transform(center, axis, scale);
for (const int vert_index : vertex_indices) {
- MVert &vert = mesh.mvert[vert_index];
+ MVert &vert = verts[vert_index];
const float3 old_position = vert.co;
const float3 new_position = transform * old_position;
copy_v3_v3(vert.co, new_position);
@@ -232,11 +245,14 @@ static void scale_vertex_islands_on_axis(Mesh &mesh,
static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexMask face_selection)
{
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
/* Use the disjoint set data structure to determine which vertices have to be scaled together. */
DisjointSet disjoint_set(mesh.totvert);
for (const int poly_index : face_selection) {
- const MPoly &poly = mesh.mpoly[poly_index];
- const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const MPoly &poly = polys[poly_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
for (const int loop_index : IndexRange(poly.totloop - 1)) {
const int v1 = poly_loops[loop_index].v;
const int v2 = poly_loops[loop_index + 1].v;
@@ -252,8 +268,8 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
/* Gather all of the face indices in each island into separate vectors. */
for (const int poly_index : face_selection) {
- const MPoly &poly = mesh.mpoly[poly_index];
- const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const MPoly &poly = polys[poly_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
const int island_id = disjoint_set.find_root(poly_loops[0].v);
const int island_index = island_ids.index_of_or_add(island_id);
if (island_index == islands.size()) {
@@ -266,10 +282,14 @@ static Vector<ElementIsland> prepare_face_islands(const Mesh &mesh, const IndexM
return islands;
}
-static void get_face_vertices(const Mesh &mesh, int face_index, VectorSet<int> &r_vertex_indices)
+static void get_face_verts(const Span<MEdge> /*edges*/,
+ const Span<MPoly> polys,
+ const Span<MLoop> loops,
+ int face_index,
+ VectorSet<int> &r_vertex_indices)
{
- const MPoly &poly = mesh.mpoly[face_index];
- const Span<MLoop> poly_loops{mesh.mloop + poly.loopstart, poly.totloop};
+ const MPoly &poly = polys[face_index];
+ const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop);
for (const MLoop &loop : poly_loops) {
r_vertex_indices.add(loop.v);
}
@@ -288,18 +308,14 @@ static AxisScaleParams evaluate_axis_scale_fields(FieldEvaluator &evaluator,
return out;
}
-static void scale_faces_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+static void scale_faces_on_axis(Mesh &mesh, const AxisScaleFields &fields)
{
- Mesh &mesh = *mesh_component.get_for_write();
- mesh.mvert = static_cast<MVert *>(
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
-
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator evaluator{field_context, mesh.totpoly};
AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
- scale_vertex_islands_on_axis(mesh, island, params, get_face_vertices);
+ scale_vertex_islands_on_axis(mesh, island, params, get_face_verts);
}
static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluator,
@@ -314,26 +330,24 @@ static UniformScaleParams evaluate_uniform_scale_fields(FieldEvaluator &evaluato
return out;
}
-static void scale_faces_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+static void scale_faces_uniformly(Mesh &mesh, const UniformScaleFields &fields)
{
- Mesh &mesh = *mesh_component.get_for_write();
- mesh.mvert = static_cast<MVert *>(
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
-
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
FieldEvaluator evaluator{field_context, mesh.totpoly};
UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
Vector<ElementIsland> island = prepare_face_islands(mesh, params.selection);
- scale_vertex_islands_uniformly(mesh, island, params, get_face_vertices);
+ scale_vertex_islands_uniformly(mesh, island, params, get_face_verts);
}
static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexMask edge_selection)
{
+ const Span<MEdge> edges = mesh.edges();
+
/* Use the disjoint set data structure to determine which vertices have to be scaled together. */
DisjointSet disjoint_set(mesh.totvert);
for (const int edge_index : edge_selection) {
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[edge_index];
disjoint_set.join(edge.v1, edge.v2);
}
@@ -344,7 +358,7 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
/* Gather all of the edge indices in each island into separate vectors. */
for (const int edge_index : edge_selection) {
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[edge_index];
const int island_id = disjoint_set.find_root(edge.v1);
const int island_index = island_ids.index_of_or_add(island_id);
if (island_index == islands.size()) {
@@ -357,39 +371,35 @@ static Vector<ElementIsland> prepare_edge_islands(const Mesh &mesh, const IndexM
return islands;
}
-static void get_edge_vertices(const Mesh &mesh, int edge_index, VectorSet<int> &r_vertex_indices)
+static void get_edge_verts(const Span<MEdge> edges,
+ const Span<MPoly> /*polys*/,
+ const Span<MLoop> /*loops*/,
+ int edge_index,
+ VectorSet<int> &r_vertex_indices)
{
- const MEdge &edge = mesh.medge[edge_index];
+ const MEdge &edge = edges[edge_index];
r_vertex_indices.add(edge.v1);
r_vertex_indices.add(edge.v2);
}
-static void scale_edges_uniformly(MeshComponent &mesh_component, const UniformScaleFields &fields)
+static void scale_edges_uniformly(Mesh &mesh, const UniformScaleFields &fields)
{
- Mesh &mesh = *mesh_component.get_for_write();
- mesh.mvert = static_cast<MVert *>(
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
-
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator evaluator{field_context, mesh.totedge};
UniformScaleParams params = evaluate_uniform_scale_fields(evaluator, fields);
Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
- scale_vertex_islands_uniformly(mesh, island, params, get_edge_vertices);
+ scale_vertex_islands_uniformly(mesh, island, params, get_edge_verts);
}
-static void scale_edges_on_axis(MeshComponent &mesh_component, const AxisScaleFields &fields)
+static void scale_edges_on_axis(Mesh &mesh, const AxisScaleFields &fields)
{
- Mesh &mesh = *mesh_component.get_for_write();
- mesh.mvert = static_cast<MVert *>(
- CustomData_duplicate_referenced_layer(&mesh.vdata, CD_MVERT, mesh.totvert));
-
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_EDGE};
FieldEvaluator evaluator{field_context, mesh.totedge};
AxisScaleParams params = evaluate_axis_scale_fields(evaluator, fields);
Vector<ElementIsland> island = prepare_edge_islands(mesh, params.selection);
- scale_vertex_islands_on_axis(mesh, island, params, get_edge_vertices);
+ scale_vertex_islands_on_axis(mesh, island, params, get_edge_verts);
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -410,42 +420,38 @@ static void node_geo_exec(GeoNodeExecParams params)
}
geometry.modify_geometry_sets([&](GeometrySet &geometry) {
- if (!geometry.has_mesh()) {
- return;
- }
- MeshComponent &mesh_component = geometry.get_component_for_write<MeshComponent>();
- switch (domain) {
- case ATTR_DOMAIN_FACE: {
- switch (scale_mode) {
- case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
- scale_faces_uniformly(mesh_component, {selection_field, scale_field, center_field});
- break;
- }
- case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
- scale_faces_on_axis(mesh_component,
- {selection_field, scale_field, center_field, axis_field});
- break;
+ if (Mesh *mesh = geometry.get_mesh_for_write()) {
+ switch (domain) {
+ case ATTR_DOMAIN_FACE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_faces_uniformly(*mesh, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_faces_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
}
+ break;
}
- break;
- }
- case ATTR_DOMAIN_EDGE: {
- switch (scale_mode) {
- case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
- scale_edges_uniformly(mesh_component, {selection_field, scale_field, center_field});
- break;
- }
- case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
- scale_edges_on_axis(mesh_component,
- {selection_field, scale_field, center_field, axis_field});
- break;
+ case ATTR_DOMAIN_EDGE: {
+ switch (scale_mode) {
+ case GEO_NODE_SCALE_ELEMENTS_UNIFORM: {
+ scale_edges_uniformly(*mesh, {selection_field, scale_field, center_field});
+ break;
+ }
+ case GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS: {
+ scale_edges_on_axis(*mesh, {selection_field, scale_field, center_field, axis_field});
+ break;
+ }
}
+ break;
}
- break;
+ default:
+ BLI_assert_unreachable();
+ break;
}
- default:
- BLI_assert_unreachable();
- break;
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
index 7156feb37d7..21fe724e194 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc
@@ -21,9 +21,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void scale_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
{
- GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
-
- fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
evaluator.add(params.extract_input<Field<float3>>("Scale"));
evaluator.add(params.extract_input<Field<float3>>("Center"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
index fc3cb7006bb..e529ddddabe 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc
@@ -68,19 +68,18 @@ static void update_handle_types_for_movement(int8_t &type, int8_t &other)
}
}
-static void set_position_in_component(CurveComponent &component,
+static void set_position_in_component(bke::CurvesGeometry &curves,
const GeometryNodeCurveHandleMode mode,
const Field<bool> &selection_field,
const Field<float3> &position_field,
const Field<float3> &offset_field)
{
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ if (curves.points_num() == 0) {
return;
}
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.add(position_field);
evaluator.add(offset_field);
@@ -89,9 +88,6 @@ static void set_position_in_component(CurveComponent &component,
const VArray<float3> new_positions = evaluator.get_evaluated<float3>(0);
const VArray<float3> new_offsets = evaluator.get_evaluated<float3>(1);
- Curves &curves_id = *component.get_for_write();
- bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry);
-
Span<float3> positions = curves.positions();
const bool use_left = mode == GEO_NODE_CURVE_HANDLE_LEFT;
@@ -141,22 +137,17 @@ static void node_geo_exec(GeoNodeExecParams params)
std::atomic<bool> has_bezier = false;
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (!geometry_set.has_curves()) {
- return;
- }
- has_curves = true;
- const CurveComponent &component = *geometry_set.get_component_for_read<CurveComponent>();
- const AttributeAccessor attributes = *component.attributes();
- if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) {
- return;
- }
- has_bezier = true;
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry);
+ has_curves = true;
+ const AttributeAccessor attributes = curves.attributes();
+ if (!attributes.contains("handle_left") || !attributes.contains("handle_right")) {
+ return;
+ }
+ has_bezier = true;
- set_position_in_component(geometry_set.get_component_for_write<CurveComponent>(),
- mode,
- selection_field,
- position_field,
- offset_field);
+ set_position_in_component(curves, mode, selection_field, position_field, offset_field);
+ }
});
if (has_curves && !has_bezier) {
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
index e4fae95b5a5..0d361090068 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_radius_cc {
@@ -16,21 +18,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curve"));
}
-static void set_radius_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<float> &radius_field)
+static void set_radius(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<float> &radius_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ if (curves.points_num() == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
-
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(radius_field, radii.varray);
evaluator.evaluate();
@@ -45,9 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> radii_field = params.extract_input<Field<float>>("Radius");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_radius_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, radii_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_radius(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, radii_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
index 2211ac62727..8c1fb883f46 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_curve_tilt_cc {
@@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Curve"));
}
-static void set_tilt_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<float> &tilt_field)
+static void set_tilt(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<float> &tilt_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ if (curves.points_num() == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
-
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
AttributeWriter<float> tilts = attributes.lookup_or_add_for_write<float>("tilt",
ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_POINT};
+ fn::FieldEvaluator evaluator{field_context, curves.points_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(tilt_field, tilts.varray);
evaluator.evaluate();
@@ -42,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> tilt_field = params.extract_input<Field<float>>("Tilt");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_tilt_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, tilt_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_tilt(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, tilt_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
index fbb2ecbb799..5864401223b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc
@@ -24,7 +24,7 @@ static void set_id_in_component(GeometryComponent &component,
return;
}
MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
index 507c6e81b1f..8d00d82664b 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc
@@ -12,6 +12,7 @@
#include "DNA_volume_types.h"
#include "BKE_material.h"
+#include "BKE_mesh.h"
namespace blender::nodes::node_geo_set_material_cc {
@@ -49,11 +50,11 @@ static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Mate
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 : selection) {
- MPoly &poly = mesh.mpoly[i];
- poly.mat_nr = new_material_index;
- }
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ SpanAttributeWriter<int> material_indices = attributes.lookup_or_add_for_write_span<int>(
+ "material_index", ATTR_DOMAIN_FACE);
+ material_indices.span.fill_indices(selection, new_material_index);
+ material_indices.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -72,8 +73,8 @@ static void node_geo_exec(GeoNodeExecParams params)
if (geometry_set.has_mesh()) {
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
Mesh &mesh = *mesh_component.get_for_write();
- GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
fn::FieldEvaluator selection_evaluator{field_context, mesh.totpoly};
selection_evaluator.add(selection_field);
selection_evaluator.evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
index 0dc89bb7ef4..bb9ac9b5d4c 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc
@@ -14,21 +14,23 @@ static void node_declare(NodeDeclarationBuilder &b)
static void set_material_index_in_component(GeometryComponent &component,
const Field<bool> &selection_field,
- const Field<int> &index_field)
+ const Field<int> &index_field,
+ const eAttrDomain domain)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ const int domain_size = component.attribute_domain_size(domain);
if (domain_size == 0) {
return;
}
MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
+ bke::GeometryFieldContext field_context{component, domain};
- AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index",
- ATTR_DOMAIN_FACE);
+ const bke::AttributeValidator validator = attributes.lookup_validator("material_index");
+ AttributeWriter<int> indices = attributes.lookup_or_add_for_write<int>("material_index", domain);
fn::FieldEvaluator evaluator{field_context, domain_size};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(index_field, indices.varray);
+ evaluator.add_with_destination(validator.validate_field_if_necessary(index_field),
+ indices.varray);
evaluator.evaluate();
indices.finish();
}
@@ -41,8 +43,10 @@ static void node_geo_exec(GeoNodeExecParams params)
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
if (geometry_set.has_mesh()) {
- set_material_index_in_component(
- geometry_set.get_component_for_write<MeshComponent>(), selection_field, index_field);
+ set_material_index_in_component(geometry_set.get_component_for_write<MeshComponent>(),
+ selection_field,
+ index_field,
+ ATTR_DOMAIN_FACE);
}
});
params.set_output("Geometry", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
index da7977a4fb4..28d07b31218 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_pointcloud_types.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_point_radius_cc {
@@ -16,21 +18,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Points"));
}
-static void set_radius_in_component(GeometryComponent &component,
+static void set_radius_in_component(PointCloud &pointcloud,
const Field<bool> &selection_field,
const Field<float> &radius_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT);
- if (domain_size == 0) {
+ if (pointcloud.totpoint == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT};
-
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
AttributeWriter<float> radii = attributes.lookup_or_add_for_write<float>("radius",
ATTR_DOMAIN_POINT);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::PointCloudFieldContext field_context{pointcloud};
+ fn::FieldEvaluator evaluator{field_context, pointcloud.totpoint};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(radius_field, radii.varray);
evaluator.evaluate();
@@ -45,10 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<float> radii_field = params.extract_input<Field<float>>("Radius");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_pointcloud()) {
- set_radius_in_component(geometry_set.get_component_for_write<PointCloudComponent>(),
- selection_field,
- radii_field);
+ if (PointCloud *pointcloud = geometry_set.get_pointcloud_for_write()) {
+ set_radius_in_component(*pointcloud, selection_field, radii_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index 880252de4fa..613f140ff0a 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -8,6 +8,7 @@
#include "DNA_meshdata_types.h"
#include "BKE_curves.hh"
+#include "BKE_mesh.h"
#include "node_geometry_util.hh"
@@ -35,14 +36,14 @@ static void set_computed_position_and_offset(GeometryComponent &component,
switch (component.type()) {
case GEO_COMPONENT_TYPE_MESH: {
Mesh *mesh = static_cast<MeshComponent &>(component).get_for_write();
- MutableSpan<MVert> mverts{mesh->mvert, mesh->totvert};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
if (in_positions.is_same(positions.varray)) {
devirtualize_varray(in_offsets, [&](const auto in_offsets) {
threading::parallel_for(
selection.index_range(), grain_size, [&](const IndexRange range) {
for (const int i : selection.slice(range)) {
const float3 offset = in_offsets[i];
- add_v3_v3(mverts[i].co, offset);
+ add_v3_v3(verts[i].co, offset);
}
});
});
@@ -54,7 +55,7 @@ static void set_computed_position_and_offset(GeometryComponent &component,
selection.index_range(), grain_size, [&](const IndexRange range) {
for (const int i : selection.slice(range)) {
const float3 new_position = in_positions[i] + in_offsets[i];
- copy_v3_v3(mverts[i].co, new_position);
+ copy_v3_v3(verts[i].co, new_position);
}
});
});
@@ -136,7 +137,7 @@ static void set_position_in_component(GeometryComponent &component,
{
eAttrDomain domain = component.type() == GEO_COMPONENT_TYPE_INSTANCES ? ATTR_DOMAIN_INSTANCE :
ATTR_DOMAIN_POINT;
- GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
const int domain_size = component.attribute_domain_size(domain);
if (domain_size == 0) {
return;
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
index e0cf0f98d58..0df51e49827 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "DNA_mesh_types.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_shade_smooth_cc {
@@ -12,27 +14,25 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void set_smooth_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<bool> &shade_field)
+static void set_smooth(Mesh &mesh,
+ const Field<bool> &selection_field,
+ const Field<bool> &shade_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- if (domain_size == 0) {
+ if (mesh.totpoly == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE};
- AttributeWriter<bool> shades = attributes.lookup_or_add_for_write<bool>("shade_smooth",
+ MutableAttributeAccessor attributes = mesh.attributes_for_write();
+ AttributeWriter<bool> smooth = attributes.lookup_or_add_for_write<bool>("shade_smooth",
ATTR_DOMAIN_FACE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::MeshFieldContext field_context{mesh, ATTR_DOMAIN_FACE};
+ fn::FieldEvaluator evaluator{field_context, mesh.totpoly};
evaluator.set_selection(selection_field);
- evaluator.add_with_destination(shade_field, shades.varray);
+ evaluator.add_with_destination(shade_field, smooth.varray);
evaluator.evaluate();
- shades.finish();
+ smooth.finish();
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -42,9 +42,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> shade_field = params.extract_input<Field<bool>>("Shade Smooth");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_mesh()) {
- set_smooth_in_component(
- geometry_set.get_component_for_write<MeshComponent>(), selection_field, shade_field);
+ if (Mesh *mesh = geometry_set.get_mesh_for_write()) {
+ set_smooth(*mesh, selection_field, shade_field);
}
});
params.set_output("Geometry", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
index a35d8d66558..d8faa154477 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_spline_cyclic_cc {
@@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void set_cyclic_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<bool> &cyclic_field)
+static void set_cyclic(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<bool> &cyclic_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- if (domain_size == 0) {
+ if (curves.curves_num() == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
-
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
AttributeWriter<bool> cyclics = attributes.lookup_or_add_for_write<bool>("cyclic",
ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(cyclic_field, cyclics.varray);
evaluator.evaluate();
@@ -42,9 +41,8 @@ static void node_geo_exec(GeoNodeExecParams params)
Field<bool> cyclic_field = params.extract_input<Field<bool>>("Cyclic");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- if (geometry_set.has_curves()) {
- set_cyclic_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, cyclic_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_cyclic(bke::CurvesGeometry::wrap(curves_id->geometry), selection_field, cyclic_field);
}
});
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
index fcebc1116d7..d46ceac92ba 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc
@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include "BKE_curves.hh"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_set_spline_resolution_cc {
@@ -12,22 +14,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>(N_("Geometry"));
}
-static void set_resolution_in_component(GeometryComponent &component,
- const Field<bool> &selection_field,
- const Field<int> &resolution_field)
+static void set_resolution(bke::CurvesGeometry &curves,
+ const Field<bool> &selection_field,
+ const Field<int> &resolution_field)
{
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- if (domain_size == 0) {
+ if (curves.curves_num() == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
-
- GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE};
-
+ MutableAttributeAccessor attributes = curves.attributes_for_write();
AttributeWriter<int> resolutions = attributes.lookup_or_add_for_write<int>("resolution",
ATTR_DOMAIN_CURVE);
- fn::FieldEvaluator evaluator{field_context, domain_size};
+ bke::CurvesFieldContext field_context{curves, ATTR_DOMAIN_CURVE};
+ fn::FieldEvaluator evaluator{field_context, curves.curves_num()};
evaluator.set_selection(selection_field);
evaluator.add_with_destination(resolution_field, resolutions.varray);
evaluator.evaluate();
@@ -38,12 +37,13 @@ static void set_resolution_in_component(GeometryComponent &component,
static void node_geo_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
- Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
- Field<int> resolution_field = params.extract_input<Field<int>>("Resolution");
+ Field<bool> selection = params.extract_input<Field<bool>>("Selection");
+ Field<int> resolution = params.extract_input<Field<int>>("Resolution");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
- set_resolution_in_component(
- geometry_set.get_component_for_write<CurveComponent>(), selection_field, resolution_field);
+ if (Curves *curves_id = geometry_set.get_curves_for_write()) {
+ set_resolution(bke::CurvesGeometry::wrap(curves_id->geometry), selection, resolution);
+ }
});
params.set_output("Geometry", std::move(geometry_set));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
index 69a4fad10e2..ad31c8a2191 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_store_named_attribute.cc
@@ -1,8 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
+#include <atomic>
+
#include "UI_interface.h"
#include "UI_resources.h"
+#include "RNA_enum_types.h"
+
#include "NOD_socket_search_link.hh"
#include "BKE_type_conversions.hh"
@@ -68,7 +72,7 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
const NodeDeclaration &declaration = *params.node_type().fixed_declaration;
search_link_ops_for_declarations(params, declaration.inputs().take_front(2));
- if (params.in_out() == SOCK_OUT) {
+ if (params.in_out() == SOCK_IN) {
const std::optional<eCustomDataType> type = node_data_type_to_custom_data_type(
static_cast<eNodeSocketDatatype>(params.other_socket().type));
if (type && *type != CD_PROP_STRING) {
@@ -85,48 +89,51 @@ static void node_gather_link_searches(GatherLinkSearchOpParams &params)
static void try_capture_field_on_geometry(GeometryComponent &component,
const StringRef name,
const eAttrDomain domain,
- const GField &field)
+ const GField &field,
+ std::atomic<bool> &r_failure)
{
- const int domain_size = component.attribute_domain_size(domain);
+ MutableAttributeAccessor attributes = *component.attributes_for_write();
+ const int domain_size = attributes.domain_size(domain);
if (domain_size == 0) {
return;
}
- MutableAttributeAccessor attributes = *component.attributes_for_write();
- GeometryComponentFieldContext field_context{component, domain};
+ bke::GeometryFieldContext field_context{component, domain};
const IndexMask mask{IndexMask(domain_size)};
const CPPType &type = field.cpp_type();
const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type);
+ const bke::AttributeValidator validator = attributes.lookup_validator(name);
/* Could avoid allocating a new buffer if:
- * - We are writing to an attribute that exists already.
+ * - We are writing to an attribute that exists already with the correct domain and type.
* - The field does not depend on that attribute (we can't easily check for that yet). */
void *buffer = MEM_mallocN(type.size() * domain_size, __func__);
fn::FieldEvaluator evaluator{field_context, &mask};
- evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_size});
+ evaluator.add_with_destination(validator.validate_field_if_necessary(field),
+ GMutableSpan{type, buffer, domain_size});
evaluator.evaluate();
- attributes.remove(name);
- if (attributes.contains(name)) {
- GAttributeWriter write_attribute = attributes.lookup_for_write(name);
- if (write_attribute && write_attribute.domain == domain &&
- write_attribute.varray.type() == type) {
- write_attribute.varray.set_all(buffer);
- write_attribute.finish();
- }
- else {
- /* Cannot change type of built-in attribute. */
- }
- type.destruct_n(buffer, domain_size);
- MEM_freeN(buffer);
- }
- else {
- if (!attributes.add(name, domain, data_type, bke::AttributeInitMove{buffer})) {
+ if (GAttributeWriter attribute = attributes.lookup_for_write(name)) {
+ if (attribute.domain == domain && attribute.varray.type() == type) {
+ attribute.varray.set_all(buffer);
+ attribute.finish();
+ type.destruct_n(buffer, domain_size);
MEM_freeN(buffer);
+ return;
}
}
+ attributes.remove(name);
+ if (attributes.add(name, domain, data_type, bke::AttributeInitMoveArray{buffer})) {
+ return;
+ }
+
+ /* If the name corresponds to a builtin attribute, removing the attribute might fail if
+ * it's required, and adding the attribute might fail if the domain or type is incorrect. */
+ type.destruct_n(buffer, domain_size);
+ MEM_freeN(buffer);
+ r_failure = true;
}
static void node_geo_exec(GeoNodeExecParams params)
@@ -144,7 +151,7 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- params.used_named_attribute(name, eNamedAttrUsage::Write);
+ params.used_named_attribute(name, NamedAttributeUsage::Write);
const NodeGeometryStoreNamedAttribute &storage = node_storage(params.node());
const eCustomDataType data_type = static_cast<eCustomDataType>(storage.data_type);
@@ -177,12 +184,14 @@ static void node_geo_exec(GeoNodeExecParams params)
break;
}
+ std::atomic<bool> failure = false;
+
/* Run on the instances component separately to only affect the top level of instances. */
if (domain == ATTR_DOMAIN_INSTANCE) {
if (geometry_set.has_instances()) {
GeometryComponent &component = geometry_set.get_component_for_write(
GEO_COMPONENT_TYPE_INSTANCES);
- try_capture_field_on_geometry(component, name, domain, field);
+ try_capture_field_on_geometry(component, name, domain, field, failure);
}
}
else {
@@ -191,12 +200,26 @@ static void node_geo_exec(GeoNodeExecParams params)
{GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) {
if (geometry_set.has(type)) {
GeometryComponent &component = geometry_set.get_component_for_write(type);
- try_capture_field_on_geometry(component, name, domain, field);
+ try_capture_field_on_geometry(component, name, domain, field, failure);
}
}
});
}
+ if (failure) {
+ const char *domain_name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &domain_name);
+ const char *type_name = nullptr;
+ RNA_enum_name_from_value(rna_enum_attribute_type_items, data_type, &type_name);
+ char *message = BLI_sprintfN(
+ TIP_("Failed to write to attribute \"%s\" with domain \"%s\" and type \"%s\""),
+ name.c_str(),
+ TIP_(domain_name),
+ TIP_(type_name));
+ params.error_message_add(NodeWarningType::Warning, message);
+ MEM_freeN(message);
+ }
+
params.set_output("Geometry", std::move(geometry_set));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
index bb33430a02f..09c01b8c627 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc
@@ -13,12 +13,13 @@ static void node_declare(NodeDeclarationBuilder &b)
static void node_geo_exec(GeoNodeExecParams params)
{
- Vector<std::string> strings = params.extract_multi_input<std::string>("Strings");
+ Vector<fn::ValueOrField<std::string>> strings =
+ params.extract_input<Vector<fn::ValueOrField<std::string>>>("Strings");
const std::string delim = params.extract_input<std::string>("Delimiter");
std::string output;
for (const int i : strings.index_range()) {
- output += strings[i];
+ output += strings[i].as_value();
if (i < (strings.size() - 1)) {
output += delim;
}
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 eda6a51d412..60c8a89a6bf 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc
@@ -73,7 +73,7 @@ static void write_vertex_creases(Mesh &mesh, const VArray<float> &crease_varray)
}
else {
crease = static_cast<float *>(
- CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_DEFAULT, nullptr, mesh.totvert));
+ CustomData_add_layer(&mesh.vdata, CD_CREASE, CD_CONSTRUCT, nullptr, mesh.totvert));
}
materialize_and_clamp_creases(crease_varray, {crease, mesh.totvert});
}
@@ -119,21 +119,19 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
- const MeshComponent &mesh_component = *geometry_set.get_component_for_read<MeshComponent>();
- const int verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- const int edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- if (verts_num == 0 || edges_num == 0) {
+ const Mesh &mesh = *geometry_set.get_mesh_for_read();
+ if (mesh.totvert == 0 || mesh.totedge == 0) {
return;
}
- GeometryComponentFieldContext point_context{mesh_component, ATTR_DOMAIN_POINT};
- FieldEvaluator point_evaluator(point_context, verts_num);
+ bke::MeshFieldContext point_context{mesh, ATTR_DOMAIN_POINT};
+ FieldEvaluator point_evaluator(point_context, mesh.totvert);
point_evaluator.add(vertex_crease_field);
point_evaluator.evaluate();
const VArray<float> vertex_creases = point_evaluator.get_evaluated<float>(0);
- GeometryComponentFieldContext edge_context{mesh_component, ATTR_DOMAIN_EDGE};
- FieldEvaluator edge_evaluator(edge_context, edges_num);
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ FieldEvaluator edge_evaluator(edge_context, mesh.totedge);
edge_evaluator.add(edge_crease_field);
edge_evaluator.evaluate();
const VArray<float> edge_creases = edge_evaluator.get_evaluated<float>(0);
@@ -162,17 +160,15 @@ static void node_geo_exec(GeoNodeExecParams params)
subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth(
uv_smooth);
- const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
-
/* Apply subdivision to mesh. */
- Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh_in);
+ Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, &mesh);
/* In case of bad topology, skip to input mesh. */
if (subdiv == nullptr) {
return;
}
- Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh_in);
+ Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, &mesh);
geometry_set.replace_mesh(mesh_out);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
index cd75822f665..afc492c40e4 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc
@@ -10,6 +10,7 @@
#include "BKE_attribute_math.hh"
#include "BKE_bvhutils.h"
+#include "BKE_mesh.h"
#include "BKE_mesh_runtime.h"
#include "BKE_mesh_sample.hh"
@@ -234,12 +235,12 @@ static void get_closest_mesh_looptris(const Mesh &mesh,
free_bvhtree_from_mesh(&tree_data);
}
-static void get_closest_mesh_polygons(const Mesh &mesh,
- const VArray<float3> &positions,
- const IndexMask mask,
- const MutableSpan<int> r_poly_indices,
- const MutableSpan<float> r_distances_sq,
- const MutableSpan<float3> r_positions)
+static void get_closest_mesh_polys(const Mesh &mesh,
+ const VArray<float3> &positions,
+ const IndexMask mask,
+ const MutableSpan<int> r_poly_indices,
+ const MutableSpan<float> r_distances_sq,
+ const MutableSpan<float3> r_positions)
{
BLI_assert(mesh.totpoly > 0);
@@ -263,23 +264,27 @@ static void get_closest_mesh_corners(const Mesh &mesh,
const MutableSpan<float> r_distances_sq,
const MutableSpan<float3> r_positions)
{
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
+
BLI_assert(mesh.totloop > 0);
Array<int> poly_indices(positions.size());
- get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {});
+ get_closest_mesh_polys(mesh, positions, mask, poly_indices, {}, {});
for (const int i : mask) {
const float3 position = positions[i];
const int poly_index = poly_indices[i];
- const MPoly &poly = mesh.mpoly[poly_index];
+ const MPoly &poly = polys[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 MLoop &loop = loops[loop_index];
const int vertex_index = loop.v;
- const MVert &mvert = mesh.mvert[vertex_index];
+ const MVert &mvert = verts[vertex_index];
const float distance_sq = math::distance_squared(position, float3(mvert.co));
if (distance_sq < min_distance_sq) {
min_distance_sq = distance_sq;
@@ -387,7 +392,7 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
fn::MFSignature signature_;
- std::optional<GeometryComponentFieldContext> source_context_;
+ std::optional<bke::MeshFieldContext> source_context_;
std::unique_ptr<FieldEvaluator> source_evaluator_;
const GVArray *source_data_;
@@ -431,10 +436,10 @@ class NearestInterpolatedTransferFunction : public fn::MultiFunction {
private:
void evaluate_source_field()
{
- const MeshComponent &mesh_component = *source_.get_component_for_read<MeshComponent>();
- source_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_});
- const int domain_num = mesh_component.attribute_domain_size(domain_);
- source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_num);
+ const Mesh &mesh = *source_.get_mesh_for_read();
+ source_context_.emplace(bke::MeshFieldContext{mesh, domain_});
+ const int domain_size = mesh.attributes().domain_size(domain_);
+ source_evaluator_ = std::make_unique<FieldEvaluator>(*source_context_, domain_size);
source_evaluator_->add(src_field_);
source_evaluator_->evaluate();
source_data_ = &source_evaluator_->get_evaluated(0);
@@ -457,11 +462,11 @@ class NearestTransferFunction : public fn::MultiFunction {
bool use_points_;
/* Store data from the source as a virtual array, since we may only access a few indices. */
- std::optional<GeometryComponentFieldContext> mesh_context_;
+ std::optional<bke::MeshFieldContext> mesh_context_;
std::unique_ptr<FieldEvaluator> mesh_evaluator_;
const GVArray *mesh_data_;
- std::optional<GeometryComponentFieldContext> point_context_;
+ std::optional<bke::PointCloudFieldContext> point_context_;
std::unique_ptr<FieldEvaluator> point_evaluator_;
const GVArray *point_data_;
@@ -535,7 +540,7 @@ class NearestTransferFunction : public fn::MultiFunction {
break;
}
case ATTR_DOMAIN_FACE: {
- get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {});
+ get_closest_mesh_polys(*mesh, positions, mask, mesh_indices, mesh_distances, {});
break;
}
case ATTR_DOMAIN_CORNER: {
@@ -577,20 +582,19 @@ class NearestTransferFunction : public fn::MultiFunction {
void evaluate_source_field()
{
if (use_mesh_) {
- const MeshComponent &mesh = *source_.get_component_for_read<MeshComponent>();
- const int domain_num = mesh.attribute_domain_size(domain_);
- mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_));
- mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_num);
+ const Mesh &mesh = *source_.get_mesh_for_read();
+ const int domain_size = mesh.attributes().domain_size(domain_);
+ mesh_context_.emplace(bke::MeshFieldContext(mesh, domain_));
+ mesh_evaluator_ = std::make_unique<FieldEvaluator>(*mesh_context_, domain_size);
mesh_evaluator_->add(src_field_);
mesh_evaluator_->evaluate();
mesh_data_ = &mesh_evaluator_->get_evaluated(0);
}
if (use_points_) {
- const PointCloudComponent &points = *source_.get_component_for_read<PointCloudComponent>();
- const int domain_num = points.attribute_domain_size(domain_);
- point_context_.emplace(GeometryComponentFieldContext(points, domain_));
- point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, domain_num);
+ const PointCloud &points = *source_.get_pointcloud_for_read();
+ point_context_.emplace(bke::PointCloudFieldContext(points));
+ point_evaluator_ = std::make_unique<FieldEvaluator>(*point_context_, points.totpoint);
point_evaluator_->add(src_field_);
point_evaluator_->evaluate();
point_data_ = &point_evaluator_->get_evaluated(0);
@@ -628,7 +632,7 @@ class IndexTransferFunction : public fn::MultiFunction {
fn::MFSignature signature_;
- std::optional<GeometryComponentFieldContext> geometry_context_;
+ std::optional<bke::GeometryFieldContext> geometry_context_;
std::unique_ptr<FieldEvaluator> evaluator_;
const GVArray *src_data_ = nullptr;
@@ -659,7 +663,7 @@ class IndexTransferFunction : public fn::MultiFunction {
return;
}
const int domain_num = component->attribute_domain_size(domain_);
- geometry_context_.emplace(GeometryComponentFieldContext(*component, domain_));
+ geometry_context_.emplace(bke::GeometryFieldContext(*component, domain_));
evaluator_ = std::make_unique<FieldEvaluator>(*geometry_context_, domain_num);
evaluator_->add(src_field_);
evaluator_->evaluate();
diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
index e95db205920..4130cad3bda 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc
@@ -47,21 +47,24 @@ static void transform_mesh(Mesh &mesh, const float4x4 &transform)
static void translate_pointcloud(PointCloud &pointcloud, const float3 translation)
{
- CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint);
- BKE_pointcloud_update_customdata_pointers(&pointcloud);
- for (const int i : IndexRange(pointcloud.totpoint)) {
- add_v3_v3(pointcloud.co[i], translation);
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
+ SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
+ "position", ATTR_DOMAIN_POINT);
+ for (const int i : position.span.index_range()) {
+ position.span[i] += translation;
}
+ position.finish();
}
static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform)
{
- CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint);
- BKE_pointcloud_update_customdata_pointers(&pointcloud);
- for (const int i : IndexRange(pointcloud.totpoint)) {
- float3 &co = *(float3 *)pointcloud.co[i];
- co = transform * co;
+ MutableAttributeAccessor attributes = pointcloud.attributes_for_write();
+ SpanAttributeWriter position = attributes.lookup_or_add_for_write_span<float3>(
+ "position", ATTR_DOMAIN_POINT);
+ for (const int i : position.span.index_range()) {
+ position.span[i] = transform * position.span[i];
}
+ position.finish();
}
static void translate_instances(InstancesComponent &instances, const float3 translation)
@@ -80,47 +83,93 @@ static void transform_instances(InstancesComponent &instances, const float4x4 &t
}
}
-static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph)
+static void transform_volume(GeoNodeExecParams &params,
+ Volume &volume,
+ const float4x4 &transform,
+ const Depsgraph &depsgraph)
{
#ifdef WITH_OPENVDB
- /* Scaling an axis to zero is not supported for volumes. */
- const float3 translation = transform.translation();
- const float3 rotation = transform.to_euler();
- const float3 scale = transform.scale();
- const float3 limited_scale = {
- (scale.x == 0.0f) ? FLT_EPSILON : scale.x,
- (scale.y == 0.0f) ? FLT_EPSILON : scale.y,
- (scale.z == 0.0f) ? FLT_EPSILON : scale.z,
- };
- const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale(
- translation, rotation, limited_scale);
-
const Main *bmain = DEG_get_bmain(&depsgraph);
BKE_volume_load(&volume, bmain);
openvdb::Mat4s vdb_matrix;
- memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4]));
+ memcpy(vdb_matrix.asPointer(), &transform, sizeof(float[4][4]));
openvdb::Mat4d vdb_matrix_d{vdb_matrix};
+ bool found_too_small_scale = false;
const int grids_num = BKE_volume_num_grids(&volume);
for (const int i : IndexRange(grids_num)) {
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();
- grid_transform.postMult(vdb_matrix_d);
+ float4x4 grid_matrix;
+ BKE_volume_grid_transform_matrix(volume_grid, grid_matrix.values);
+ mul_m4_m4_pre(grid_matrix.values, transform.values);
+ const float determinant = determinant_m4(grid_matrix.values);
+ if (!BKE_volume_grid_determinant_valid(determinant)) {
+ found_too_small_scale = true;
+ /* Clear the tree because it is too small. */
+ BKE_volume_grid_clear_tree(volume, *volume_grid);
+ if (determinant == 0) {
+ /* Reset rotation and scale. */
+ copy_v3_fl3(grid_matrix.values[0], 1, 0, 0);
+ copy_v3_fl3(grid_matrix.values[1], 0, 1, 0);
+ copy_v3_fl3(grid_matrix.values[2], 0, 0, 1);
+ }
+ else {
+ /* Keep rotation but reset scale. */
+ normalize_v3(grid_matrix.values[0]);
+ normalize_v3(grid_matrix.values[1]);
+ normalize_v3(grid_matrix.values[2]);
+ }
+ }
+ BKE_volume_grid_transform_matrix_set(volume_grid, grid_matrix.values);
+ }
+ if (found_too_small_scale) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("Volume scale is lower than permitted by OpenVDB"));
}
#else
- UNUSED_VARS(volume, transform, depsgraph);
+ UNUSED_VARS(params, volume, transform, depsgraph);
#endif
}
-static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph)
+static void translate_volume(GeoNodeExecParams &params,
+ Volume &volume,
+ const float3 translation,
+ const Depsgraph &depsgraph)
+{
+ transform_volume(params, volume, float4x4::from_location(translation), depsgraph);
+}
+
+static void transform_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float4x4 &transform)
+{
+ if (edit_hints.positions.has_value()) {
+ for (float3 &pos : *edit_hints.positions) {
+ pos = transform * pos;
+ }
+ }
+ float3x3 deform_mat;
+ copy_m3_m4(deform_mat.values, transform.values);
+ if (edit_hints.deform_mats.has_value()) {
+ for (float3x3 &mat : *edit_hints.deform_mats) {
+ mat = deform_mat * mat;
+ }
+ }
+ else {
+ edit_hints.deform_mats.emplace(edit_hints.curves_id_orig.geometry.point_num, deform_mat);
+ }
+}
+
+static void translate_curve_edit_hints(bke::CurvesEditHints &edit_hints, const float3 &translation)
{
- transform_volume(volume, float4x4::from_location(translation), depsgraph);
+ if (edit_hints.positions.has_value()) {
+ for (float3 &pos : *edit_hints.positions) {
+ pos += translation;
+ }
+ }
}
-static void translate_geometry_set(GeometrySet &geometry,
+static void translate_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float3 translation,
const Depsgraph &depsgraph)
{
@@ -134,14 +183,18 @@ static void translate_geometry_set(GeometrySet &geometry,
translate_pointcloud(*pointcloud, translation);
}
if (Volume *volume = geometry.get_volume_for_write()) {
- translate_volume(*volume, translation, depsgraph);
+ translate_volume(params, *volume, translation, depsgraph);
}
if (geometry.has_instances()) {
translate_instances(geometry.get_component_for_write<InstancesComponent>(), translation);
}
+ if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
+ translate_curve_edit_hints(*curve_edit_hints, translation);
+ }
}
-void transform_geometry_set(GeometrySet &geometry,
+void transform_geometry_set(GeoNodeExecParams &params,
+ GeometrySet &geometry,
const float4x4 &transform,
const Depsgraph &depsgraph)
{
@@ -155,11 +208,14 @@ void transform_geometry_set(GeometrySet &geometry,
transform_pointcloud(*pointcloud, transform);
}
if (Volume *volume = geometry.get_volume_for_write()) {
- transform_volume(*volume, transform, depsgraph);
+ transform_volume(params, *volume, transform, depsgraph);
}
if (geometry.has_instances()) {
transform_instances(geometry.get_component_for_write<InstancesComponent>(), transform);
}
+ if (bke::CurvesEditHints *curve_edit_hints = geometry.get_curve_edit_hints_for_write()) {
+ transform_curve_edit_hints(*curve_edit_hints, transform);
+ }
}
void transform_mesh(Mesh &mesh,
@@ -193,10 +249,11 @@ static void node_geo_exec(GeoNodeExecParams params)
/* Use only translation if rotation and scale don't apply. */
if (use_translate(rotation, scale)) {
- translate_geometry_set(geometry_set, translation, *params.depsgraph());
+ translate_geometry_set(params, geometry_set, translation, *params.depsgraph());
}
else {
- transform_geometry_set(geometry_set,
+ transform_geometry_set(params,
+ geometry_set,
float4x4::from_loc_eul_scale(translation, rotation, scale),
*params.depsgraph());
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
index ae538072e65..3e9fe99adb0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc
@@ -17,9 +17,8 @@ static void node_declare(NodeDeclarationBuilder &b)
static void translate_instances(GeoNodeExecParams &params, InstancesComponent &instances_component)
{
- GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_INSTANCE};
-
- fn::FieldEvaluator evaluator{field_context, instances_component.instances_num()};
+ const bke::InstancesFieldContext context{instances_component};
+ fn::FieldEvaluator evaluator{context, instances_component.instances_num()};
evaluator.set_selection(params.extract_input<Field<bool>>("Selection"));
evaluator.add(params.extract_input<Field<float3>>("Translation"));
evaluator.add(params.extract_input<Field<bool>>("Local Space"));
diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
index 992470e8279..57487059437 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc
@@ -47,9 +47,6 @@ static Mesh *triangulate_mesh_selection(const Mesh &mesh,
BMeshFromMeshParams from_mesh_params{};
from_mesh_params.calc_face_normal = true;
from_mesh_params.calc_vert_normal = true;
- from_mesh_params.add_key_index = true;
- from_mesh_params.use_shapekey = true;
- from_mesh_params.active_shapekey = 1;
from_mesh_params.cd_mask_extra = cd_mask_extra;
BMesh *bm = BKE_mesh_to_bmesh_ex(&mesh, &create_params, &from_mesh_params);
@@ -80,12 +77,10 @@ static void node_geo_exec(GeoNodeExecParams params)
if (!geometry_set.has_mesh()) {
return;
}
- GeometryComponent &component = geometry_set.get_component_for_write<MeshComponent>();
const Mesh &mesh_in = *geometry_set.get_mesh_for_read();
- const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- GeometryComponentFieldContext context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator evaluator{context, domain_size};
+ bke::MeshFieldContext context{mesh_in, ATTR_DOMAIN_FACE};
+ FieldEvaluator evaluator{context, mesh_in.totpoly};
evaluator.add(selection_field);
evaluator.evaluate();
const IndexMask selection = evaluator.get_evaluated_as_mask(0);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
index 17413e64f7d..ccb489f6e29 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_uv_pack_islands.cc
@@ -5,6 +5,8 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_mesh.h"
+
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_uv_pack_islands_cc {
@@ -28,21 +30,19 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Vector>(N_("UV")).field_source();
}
-static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
+static VArray<float3> construct_uv_gvarray(const Mesh &mesh,
const Field<bool> selection_field,
const Field<float3> uv_field,
const bool rotate,
const float margin,
const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator face_evaluator{face_context, face_num};
+ bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE};
+ FieldEvaluator face_evaluator{face_context, polys.size()};
face_evaluator.add(selection_field);
face_evaluator.evaluate();
const IndexMask selection = face_evaluator.get_evaluated_as_mask(0);
@@ -50,25 +50,24 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
return {};
}
- const int corner_num = component.attribute_domain_size(ATTR_DOMAIN_CORNER);
- GeometryComponentFieldContext corner_context{component, ATTR_DOMAIN_CORNER};
- FieldEvaluator evaluator{corner_context, corner_num};
- Array<float3> uv(corner_num);
+ bke::MeshFieldContext corner_context{mesh, ATTR_DOMAIN_CORNER};
+ FieldEvaluator evaluator{corner_context, mesh.totloop};
+ Array<float3> uv(mesh.totloop);
evaluator.add_with_destination(uv_field, uv.as_mutable_span());
evaluator.evaluate();
ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
for (const int mp_index : selection) {
- const MPoly &mp = mesh->mpoly[mp_index];
+ const MPoly &mp = polys[mp_index];
Array<ParamKey, 16> mp_vkeys(mp.totloop);
Array<bool, 16> mp_pin(mp.totloop);
Array<bool, 16> mp_select(mp.totloop);
Array<const float *, 16> mp_co(mp.totloop);
Array<float *, 16> mp_uv(mp.totloop);
for (const int i : IndexRange(mp.totloop)) {
- const MLoop &ml = mesh->mloop[mp.loopstart + i];
+ const MLoop &ml = loops[mp.loopstart + i];
mp_vkeys[i] = ml.v;
- mp_co[i] = mesh->mvert[ml.v].co;
+ mp_co[i] = verts[ml.v].co;
mp_uv[i] = uv[mp.loopstart + i];
mp_pin[i] = false;
mp_select[i] = false;
@@ -88,11 +87,11 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
- return component.attributes()->adapt_domain<float3>(
+ return mesh.attributes().adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain);
}
-class PackIslandsFieldInput final : public GeometryFieldInput {
+class PackIslandsFieldInput final : public bke::MeshFieldInput {
private:
const Field<bool> selection_field;
const Field<float3> uv_field;
@@ -104,7 +103,7 @@ class PackIslandsFieldInput final : public GeometryFieldInput {
const Field<float3> uv_field,
const bool rotate,
const float margin)
- : GeometryFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"),
+ : bke::MeshFieldInput(CPPType::get<float3>(), "Pack UV Islands Field"),
selection_field(selection_field),
uv_field(uv_field),
rotate(rotate),
@@ -113,16 +112,11 @@ class PackIslandsFieldInput final : public GeometryFieldInput {
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_uv_gvarray(
- mesh_component, selection_field, uv_field, rotate, margin, domain);
- }
- return {};
+ return construct_uv_gvarray(mesh, selection_field, uv_field, rotate, margin, domain);
}
};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
index 2ec14ad2d29..801bc3f4642 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_uv_unwrap.cc
@@ -5,6 +5,8 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
+#include "BKE_mesh.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -52,7 +54,7 @@ static void node_init(bNodeTree *UNUSED(tree), bNode *node)
node->storage = data;
}
-static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
+static VArray<float3> construct_uv_gvarray(const Mesh &mesh,
const Field<bool> selection_field,
const Field<bool> seam_field,
const bool fill_holes,
@@ -60,14 +62,13 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
const GeometryNodeUVUnwrapMethod method,
const eAttrDomain domain)
{
- const Mesh *mesh = component.get_for_read();
- if (mesh == nullptr) {
- return {};
- }
+ const Span<MVert> verts = mesh.verts();
+ const Span<MEdge> edges = mesh.edges();
+ const Span<MPoly> polys = mesh.polys();
+ const Span<MLoop> loops = mesh.loops();
- const int face_num = component.attribute_domain_size(ATTR_DOMAIN_FACE);
- GeometryComponentFieldContext face_context{component, ATTR_DOMAIN_FACE};
- FieldEvaluator face_evaluator{face_context, face_num};
+ bke::MeshFieldContext face_context{mesh, ATTR_DOMAIN_FACE};
+ FieldEvaluator face_evaluator{face_context, polys.size()};
face_evaluator.add(selection_field);
face_evaluator.evaluate();
const IndexMask selection = face_evaluator.get_evaluated_as_mask(0);
@@ -75,27 +76,26 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
return {};
}
- const int edge_num = component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- GeometryComponentFieldContext edge_context{component, ATTR_DOMAIN_EDGE};
- FieldEvaluator edge_evaluator{edge_context, edge_num};
+ bke::MeshFieldContext edge_context{mesh, ATTR_DOMAIN_EDGE};
+ FieldEvaluator edge_evaluator{edge_context, edges.size()};
edge_evaluator.add(seam_field);
edge_evaluator.evaluate();
const IndexMask seam = edge_evaluator.get_evaluated_as_mask(0);
- Array<float3> uv(mesh->totloop, float3(0));
+ Array<float3> uv(loops.size(), float3(0));
ParamHandle *handle = GEO_uv_parametrizer_construct_begin();
for (const int mp_index : selection) {
- const MPoly &mp = mesh->mpoly[mp_index];
+ const MPoly &mp = polys[mp_index];
Array<ParamKey, 16> mp_vkeys(mp.totloop);
Array<bool, 16> mp_pin(mp.totloop);
Array<bool, 16> mp_select(mp.totloop);
Array<const float *, 16> mp_co(mp.totloop);
Array<float *, 16> mp_uv(mp.totloop);
for (const int i : IndexRange(mp.totloop)) {
- const MLoop &ml = mesh->mloop[mp.loopstart + i];
+ const MLoop &ml = loops[mp.loopstart + i];
mp_vkeys[i] = ml.v;
- mp_co[i] = mesh->mvert[ml.v].co;
+ mp_co[i] = verts[ml.v].co;
mp_uv[i] = uv[mp.loopstart + i];
mp_pin[i] = false;
mp_select[i] = false;
@@ -110,7 +110,7 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
mp_select.data());
}
for (const int i : seam) {
- const MEdge &edge = mesh->medge[i];
+ const MEdge &edge = edges[i];
ParamKey vkeys[2]{edge.v1, edge.v2};
GEO_uv_parametrizer_edge_set_seam(handle, vkeys);
}
@@ -121,16 +121,16 @@ static VArray<float3> construct_uv_gvarray(const MeshComponent &component,
GEO_uv_parametrizer_lscm_begin(handle, false, method == GEO_NODE_UV_UNWRAP_METHOD_ANGLE_BASED);
GEO_uv_parametrizer_lscm_solve(handle, nullptr, nullptr);
GEO_uv_parametrizer_lscm_end(handle);
- GEO_uv_parametrizer_average(handle, true);
+ GEO_uv_parametrizer_average(handle, true, false, false);
GEO_uv_parametrizer_pack(handle, margin, true, true);
GEO_uv_parametrizer_flush(handle);
GEO_uv_parametrizer_delete(handle);
- return component.attributes()->adapt_domain<float3>(
+ return mesh.attributes().adapt_domain<float3>(
VArray<float3>::ForContainer(std::move(uv)), ATTR_DOMAIN_CORNER, domain);
}
-class UnwrapFieldInput final : public GeometryFieldInput {
+class UnwrapFieldInput final : public bke::MeshFieldInput {
private:
const Field<bool> selection;
const Field<bool> seam;
@@ -144,7 +144,7 @@ class UnwrapFieldInput final : public GeometryFieldInput {
const bool fill_holes,
const float margin,
const GeometryNodeUVUnwrapMethod method)
- : GeometryFieldInput(CPPType::get<float3>(), "UV Unwrap Field"),
+ : bke::MeshFieldInput(CPPType::get<float3>(), "UV Unwrap Field"),
selection(selection),
seam(seam),
fill_holes(fill_holes),
@@ -154,16 +154,11 @@ class UnwrapFieldInput final : public GeometryFieldInput {
category_ = Category::Generated;
}
- GVArray get_varray_for_context(const GeometryComponent &component,
+ GVArray get_varray_for_context(const Mesh &mesh,
const eAttrDomain domain,
IndexMask UNUSED(mask)) const final
{
- if (component.type() == GEO_COMPONENT_TYPE_MESH) {
- const MeshComponent &mesh_component = static_cast<const MeshComponent &>(component);
- return construct_uv_gvarray(
- mesh_component, selection, seam, fill_holes, margin, method, domain);
- }
- return {};
+ return construct_uv_gvarray(mesh, selection, seam, fill_holes, margin, method, domain);
}
};
diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc
index d7e9e38ea0d..c102b91acb1 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_volume_cube.cc
@@ -113,9 +113,9 @@ class Grid3DFieldContext : public FieldContext {
}
};
-#ifdef WITH_OPENVDB
static void node_geo_exec(GeoNodeExecParams params)
{
+#ifdef WITH_OPENVDB
const float3 bounds_min = params.extract_input<float3>("Min");
const float3 bounds_max = params.extract_input<float3>("Max");
@@ -137,6 +137,14 @@ static void node_geo_exec(GeoNodeExecParams params)
return;
}
+ const double3 scale_fac = double3(bounds_max - bounds_min) / double3(resolution - 1);
+ if (!BKE_volume_grid_determinant_valid(scale_fac.x * scale_fac.y * scale_fac.z)) {
+ params.error_message_add(NodeWarningType::Warning,
+ TIP_("Volume scale is lower than permitted by OpenVDB"));
+ params.set_default_remaining_outputs();
+ return;
+ }
+
Field<float> input_field = params.extract_input<Field<float>>("Density");
/* Evaluate input field on a 3D grid. */
@@ -157,8 +165,7 @@ static void node_geo_exec(GeoNodeExecParams params)
openvdb::tools::copyFromDense(dense_grid, *grid, 0.0f);
grid->transform().preTranslate(openvdb::math::Vec3<float>(-0.5f));
- const float3 scale_fac = (bounds_max - bounds_min) / float3(resolution - 1);
- grid->transform().postScale(openvdb::math::Vec3<float>(scale_fac.x, scale_fac.y, scale_fac.z));
+ grid->transform().postScale(openvdb::math::Vec3<double>(scale_fac.x, scale_fac.y, scale_fac.z));
grid->transform().postTranslate(
openvdb::math::Vec3<float>(bounds_min.x, bounds_min.y, bounds_min.z));
@@ -170,16 +177,12 @@ static void node_geo_exec(GeoNodeExecParams params)
GeometrySet r_geometry_set;
r_geometry_set.replace_volume(volume);
params.set_output("Volume", r_geometry_set);
-}
-
#else
-static void node_geo_exec(GeoNodeExecParams params)
-{
+ params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
- params.set_default_remaining_outputs();
+#endif
}
-#endif /* WITH_OPENVDB */
} // namespace blender::nodes::node_geo_volume_cube_cc
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 e1d1c67b8c8..763e207b388 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
@@ -123,9 +123,9 @@ static Mesh *create_mesh_from_volume_grids(Span<openvdb::GridBase::ConstPtr> gri
Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset);
BKE_id_material_eval_ensure_default_slot(&mesh->id);
- MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
- MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
- MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
+ MutableSpan<MVert> verts = mesh->verts_for_write();
+ MutableSpan<MPoly> polys = mesh->polys_for_write();
+ MutableSpan<MLoop> loops = mesh->loops_for_write();
for (const int i : grids.index_range()) {
const bke::OpenVDBMeshData &data = mesh_data[i];
@@ -187,20 +187,19 @@ static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParam
static void node_geo_exec(GeoNodeExecParams params)
{
- GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
-
#ifdef WITH_OPENVDB
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Volume");
geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) {
Mesh *mesh = create_mesh_from_volume(geometry_set, params);
geometry_set.replace_mesh(mesh);
- geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES});
+ geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH});
});
+ params.set_output("Mesh", std::move(geometry_set));
#else
+ params.set_default_remaining_outputs();
params.error_message_add(NodeWarningType::Error,
TIP_("Disabled, Blender was compiled without OpenVDB"));
#endif
-
- params.set_output("Mesh", std::move(geometry_set));
}
} // namespace blender::nodes::node_geo_volume_to_mesh_cc
diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc
index e589da09b16..2ea80008af8 100644
--- a/source/blender/nodes/intern/derived_node_tree.cc
+++ b/source/blender/nodes/intern/derived_node_tree.cc
@@ -2,38 +2,38 @@
#include "NOD_derived_node_tree.hh"
+#include "BKE_node.h"
+
#include "BLI_dot_export.hh"
namespace blender::nodes {
-DerivedNodeTree::DerivedNodeTree(bNodeTree &btree, NodeTreeRefMap &node_tree_refs)
+DerivedNodeTree::DerivedNodeTree(const bNodeTree &btree)
{
/* Construct all possible contexts immediately. This is significantly cheaper than inlining all
* node groups. If it still becomes a performance issue in the future, contexts could be
* constructed lazily when they are needed. */
- root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree, node_tree_refs);
+ root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree);
}
DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context,
- const NodeRef *parent_node,
- bNodeTree &btree,
- NodeTreeRefMap &node_tree_refs)
+ const bNode *parent_node,
+ const bNodeTree &btree)
{
+ btree.ensure_topology_cache();
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_);
+ context.btree_ = &btree;
+ used_btrees_.add(context.btree_);
- for (const NodeRef *node : context.tree_->nodes()) {
- if (node->is_group_node()) {
- bNode *bnode = node->bnode();
+ for (const bNode *bnode : context.btree_->all_nodes()) {
+ if (bnode->is_group()) {
bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id);
if (child_btree != nullptr) {
- DTreeContext &child = this->construct_context_recursively(
- &context, node, *child_btree, node_tree_refs);
- context.children_.add_new(node, &child);
+ DTreeContext &child = this->construct_context_recursively(&context, bnode, *child_btree);
+ context.children_.add_new(bnode, &child);
}
}
}
@@ -57,8 +57,8 @@ void DerivedNodeTree::destruct_context_recursively(DTreeContext *context)
bool DerivedNodeTree::has_link_cycles() const
{
- for (const NodeTreeRef *tree_ref : used_node_tree_refs_) {
- if (tree_ref->has_link_cycles()) {
+ for (const bNodeTree *btree : used_btrees_) {
+ if (btree->has_available_link_cycle()) {
return true;
}
}
@@ -67,8 +67,8 @@ bool DerivedNodeTree::has_link_cycles() const
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()) {
+ for (const bNodeTree *btree : used_btrees_) {
+ if (btree->has_undefined_nodes_or_sockets()) {
return true;
}
}
@@ -83,8 +83,8 @@ void DerivedNodeTree::foreach_node(FunctionRef<void(DNode)> callback) const
void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &context,
FunctionRef<void(DNode)> callback) const
{
- for (const NodeRef *node_ref : context.tree_->nodes()) {
- callback(DNode(&context, node_ref));
+ for (const bNode *bnode : context.btree_->all_nodes()) {
+ callback(DNode(&context, bnode));
}
for (const DTreeContext *child_context : context.children_.values()) {
this->foreach_node_in_context_recursive(*child_context, callback);
@@ -94,32 +94,32 @@ void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &cont
DOutputSocket DInputSocket::get_corresponding_group_node_output() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_output_node());
- BLI_assert(socket_ref_->index() < socket_ref_->node().inputs().size() - 1);
+ BLI_assert(bsocket_->owner_node().is_group_output());
+ BLI_assert(bsocket_->index() < bsocket_->owner_node().input_sockets().size() - 1);
const DTreeContext *parent_context = context_->parent_context();
- const NodeRef *parent_node = context_->parent_node();
+ const bNode *parent_node = context_->parent_node();
BLI_assert(parent_context != nullptr);
BLI_assert(parent_node != nullptr);
- const int socket_index = socket_ref_->index();
- return {parent_context, &parent_node->output(socket_index)};
+ const int socket_index = bsocket_->index();
+ return {parent_context, &parent_node->output_socket(socket_index)};
}
Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_node());
+ BLI_assert(bsocket_->owner_node().is_group());
- const DTreeContext *child_context = context_->child_context(socket_ref_->node());
+ const DTreeContext *child_context = context_->child_context(bsocket_->owner_node());
BLI_assert(child_context != nullptr);
- const NodeTreeRef &child_tree = child_context->tree();
- Span<const NodeRef *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
- const int socket_index = socket_ref_->index();
+ const bNodeTree &child_tree = child_context->btree();
+ Span<const bNode *> group_input_nodes = child_tree.nodes_by_type("NodeGroupInput");
+ const int socket_index = bsocket_->index();
Vector<DOutputSocket> sockets;
- for (const NodeRef *group_input_node : group_input_nodes) {
- sockets.append(DOutputSocket(child_context, &group_input_node->output(socket_index)));
+ for (const bNode *group_input_node : group_input_nodes) {
+ sockets.append(DOutputSocket(child_context, &group_input_node->output_socket(socket_index)));
}
return sockets;
}
@@ -127,36 +127,36 @@ Vector<DOutputSocket> DInputSocket::get_corresponding_group_input_sockets() cons
DInputSocket DOutputSocket::get_corresponding_group_node_input() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_input_node());
- BLI_assert(socket_ref_->index() < socket_ref_->node().outputs().size() - 1);
+ BLI_assert(bsocket_->owner_node().is_group_input());
+ BLI_assert(bsocket_->index() < bsocket_->owner_node().output_sockets().size() - 1);
const DTreeContext *parent_context = context_->parent_context();
- const NodeRef *parent_node = context_->parent_node();
+ const bNode *parent_node = context_->parent_node();
BLI_assert(parent_context != nullptr);
BLI_assert(parent_node != nullptr);
- const int socket_index = socket_ref_->index();
- return {parent_context, &parent_node->input(socket_index)};
+ const int socket_index = bsocket_->index();
+ return {parent_context, &parent_node->input_socket(socket_index)};
}
DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() const
{
BLI_assert(*this);
- BLI_assert(socket_ref_->node().is_group_node());
+ BLI_assert(bsocket_->owner_node().is_group());
- const DTreeContext *child_context = context_->child_context(socket_ref_->node());
+ const DTreeContext *child_context = context_->child_context(bsocket_->owner_node());
if (child_context == nullptr) {
/* Can happen when the group node references a non-existent group (e.g. when the group is
* linked but the original file is not found). */
return {};
}
- const NodeTreeRef &child_tree = child_context->tree();
- Span<const NodeRef *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
- const int socket_index = socket_ref_->index();
- for (const NodeRef *group_output_node : group_output_nodes) {
- if (group_output_node->bnode()->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
- return {child_context, &group_output_node->input(socket_index)};
+ const bNodeTree &child_tree = child_context->btree();
+ Span<const bNode *> group_output_nodes = child_tree.nodes_by_type("NodeGroupOutput");
+ const int socket_index = bsocket_->index();
+ for (const bNode *group_output_node : group_output_nodes) {
+ if (group_output_node->flag & NODE_DO_OUTPUT || group_output_nodes.size() == 1) {
+ return {child_context, &group_output_node->input_socket(socket_index)};
}
}
return {};
@@ -165,11 +165,11 @@ DInputSocket DOutputSocket::get_active_corresponding_group_output_socket() 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()) {
- const NodeRef &linked_node = linked_socket->node();
+ for (const bNodeSocket *linked_socket : bsocket_->logically_linked_sockets()) {
+ const bNode &linked_node = linked_socket->owner_node();
DOutputSocket linked_dsocket{context_, linked_socket};
- if (linked_node.is_group_input_node()) {
+ if (linked_node.is_group_input()) {
if (context_->is_root()) {
/* This is a group input in the root node group. */
origin_fn(linked_dsocket);
@@ -187,7 +187,7 @@ void DInputSocket::foreach_origin_socket(FunctionRef<void(DSocket)> origin_fn) c
}
}
}
- else if (linked_node.is_group_node()) {
+ else if (linked_node.is_group()) {
DInputSocket socket_in_group = linked_dsocket.get_active_corresponding_group_output_socket();
if (socket_in_group) {
if (socket_in_group->is_logically_linked()) {
@@ -217,16 +217,16 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn) const
void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
TargetSocketPathInfo &path_info) const
{
- for (const LinkRef *link : socket_ref_->as_output().directly_linked_links()) {
+ for (const bNodeLink *link : bsocket_->directly_linked_links()) {
if (link->is_muted()) {
continue;
}
- const DInputSocket &linked_socket{context_, &link->to()};
+ const DInputSocket &linked_socket{context_, link->tosock};
if (!linked_socket->is_available()) {
continue;
}
const DNode linked_node = linked_socket.node();
- if (linked_node->is_reroute_node()) {
+ if (linked_node->is_reroute()) {
const DInputSocket reroute_input = linked_socket;
const DOutputSocket reroute_output = linked_node.output(0);
path_info.sockets.append(reroute_input);
@@ -236,18 +236,18 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
else if (linked_node->is_muted()) {
- for (const InternalLinkRef *internal_link : linked_node->internal_links()) {
- if (&internal_link->from() != linked_socket.socket_ref()) {
+ for (const bNodeLink *internal_link : linked_node->internal_links_span()) {
+ if (internal_link->fromsock != linked_socket.bsocket()) {
continue;
}
/* The internal link only forwards the first incoming link. */
- if (linked_socket->is_multi_input_socket()) {
+ if (linked_socket->is_multi_input()) {
if (linked_socket->directly_linked_links()[0] != link) {
continue;
}
}
const DInputSocket mute_input = linked_socket;
- const DOutputSocket mute_output{context_, &internal_link->to()};
+ const DOutputSocket mute_output{context_, internal_link->tosock};
path_info.sockets.append(mute_input);
path_info.sockets.append(mute_output);
mute_output.foreach_target_socket(target_fn, path_info);
@@ -255,8 +255,8 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
}
- else if (linked_node->is_group_output_node()) {
- if (linked_node.node_ref() != context_->tree().group_output_node()) {
+ else if (linked_node->is_group_output()) {
+ if (linked_node.bnode() != context_->btree().group_output_node()) {
continue;
}
if (context_->is_root()) {
@@ -276,7 +276,7 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
path_info.sockets.pop_last();
}
}
- else if (linked_node->is_group_node()) {
+ else if (linked_node->is_group()) {
/* Follow the links within the nested node group. */
path_info.sockets.append(linked_socket);
const Vector<DOutputSocket> sockets_in_group =
@@ -310,7 +310,8 @@ static dot::Cluster *get_dot_cluster_for_context(
}
dot::Cluster *parent_cluster = get_dot_cluster_for_context(
digraph, parent_context, dot_clusters);
- std::string cluster_name = context->tree().name() + " / " + context->parent_node()->name();
+ std::string cluster_name = StringRef(context->btree().id.name + 2) + " / " +
+ context->parent_node()->name;
dot::Cluster &cluster = digraph.new_cluster(cluster_name);
cluster.set_parent_cluster(parent_cluster);
return &cluster;
@@ -328,11 +329,11 @@ std::string DerivedNodeTree::to_dot() const
this->foreach_node([&](DNode node) {
/* Ignore nodes that should not show up in the final output. */
- if (node->is_muted() || node->is_group_node() || node->is_reroute_node() || node->is_frame()) {
+ if (node->is_muted() || node->is_group() || node->is_reroute() || node->is_frame()) {
return;
}
if (!node.context()->is_root()) {
- if (node->is_group_input_node() || node->is_group_output_node()) {
+ if (node->is_group_input() || node->is_group_output()) {
return;
}
}
@@ -345,22 +346,22 @@ std::string DerivedNodeTree::to_dot() const
Vector<std::string> input_names;
Vector<std::string> output_names;
- for (const InputSocketRef *socket : node->inputs()) {
+ for (const bNodeSocket *socket : node->input_sockets()) {
if (socket->is_available()) {
- input_names.append(socket->name());
+ input_names.append(socket->name);
}
}
- for (const OutputSocketRef *socket : node->outputs()) {
+ for (const bNodeSocket *socket : node->output_sockets()) {
if (socket->is_available()) {
- output_names.append(socket->name());
+ output_names.append(socket->name);
}
}
dot::NodeWithSocketsRef dot_node_with_sockets = dot::NodeWithSocketsRef(
- dot_node, node->name(), input_names, output_names);
+ dot_node, node->name, input_names, output_names);
int input_index = 0;
- for (const InputSocketRef *socket : node->inputs()) {
+ for (const bNodeSocket *socket : node->input_sockets()) {
if (socket->is_available()) {
dot_input_sockets.add_new(DInputSocket{node.context(), socket},
dot_node_with_sockets.input(input_index));
@@ -368,7 +369,7 @@ std::string DerivedNodeTree::to_dot() const
}
}
int output_index = 0;
- for (const OutputSocketRef *socket : node->outputs()) {
+ for (const bNodeSocket *socket : node->output_sockets()) {
if (socket->is_available()) {
dot_output_sockets.add_new(DOutputSocket{node.context(), socket},
dot_node_with_sockets.output(output_index));
@@ -392,7 +393,7 @@ std::string DerivedNodeTree::to_dot() const
}
}
dot::Node &dot_node = *dot_floating_inputs.lookup_or_add_cb(from_socket, [&]() {
- dot::Node &dot_node = digraph.new_node(from_socket->name());
+ dot::Node &dot_node = digraph.new_node(from_socket->name);
dot_node.set_background_color("white");
dot_node.set_shape(dot::Attr_shape::Ellipse);
dot_node.set_parent_cluster(
diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc
deleted file mode 100644
index cf7cbbdc4bf..00000000000
--- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc
+++ /dev/null
@@ -1,508 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include "NOD_geometry_nodes_eval_log.hh"
-
-#include "BKE_geometry_set_instances.hh"
-
-#include "DNA_modifier_types.h"
-#include "DNA_space_types.h"
-
-#include "FN_field_cpp_type.hh"
-
-#include "BLT_translation.h"
-
-#include <chrono>
-
-namespace blender::nodes::geometry_nodes_eval_log {
-
-using fn::FieldCPPType;
-using fn::FieldInput;
-using fn::GField;
-using fn::ValueOrFieldCPPType;
-
-ModifierLog::ModifierLog(GeoLogger &logger)
- : input_geometry_log_(std::move(logger.input_geometry_log_)),
- output_geometry_log_(std::move(logger.output_geometry_log_))
-{
- root_tree_logs_ = allocator_.construct<TreeLog>();
-
- LogByTreeContext log_by_tree_context;
-
- /* Combine all the local loggers that have been used by separate threads. */
- for (LocalGeoLogger &local_logger : logger) {
- /* Take ownership of the allocator. */
- logger_allocators_.append(std::move(local_logger.allocator_));
-
- for (ValueOfSockets &value_of_sockets : local_logger.values_) {
- ValueLog *value_log = value_of_sockets.value.get();
-
- /* Take centralized ownership of the logged value. It might be referenced by multiple
- * sockets. */
- logged_values_.append(std::move(value_of_sockets.value));
-
- for (const DSocket &socket : value_of_sockets.sockets) {
- SocketLog &socket_log = this->lookup_or_add_socket_log(log_by_tree_context, socket);
- socket_log.value_ = value_log;
- }
- }
-
- for (NodeWithWarning &node_with_warning : local_logger.node_warnings_) {
- NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
- node_with_warning.node);
- node_log.warnings_.append(node_with_warning.warning);
- }
-
- for (NodeWithExecutionTime &node_with_exec_time : local_logger.node_exec_times_) {
- NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
- node_with_exec_time.node);
- node_log.exec_time_ = node_with_exec_time.exec_time;
- }
-
- for (NodeWithDebugMessage &debug_message : local_logger.node_debug_messages_) {
- NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, debug_message.node);
- node_log.debug_messages_.append(debug_message.message);
- }
-
- for (NodeWithUsedNamedAttribute &node_with_attribute_name :
- local_logger.used_named_attributes_) {
- NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context,
- node_with_attribute_name.node);
- node_log.used_named_attributes_.append(std::move(node_with_attribute_name.attribute));
- }
- }
-}
-
-TreeLog &ModifierLog::lookup_or_add_tree_log(LogByTreeContext &log_by_tree_context,
- const DTreeContext &tree_context)
-{
- TreeLog *tree_log = log_by_tree_context.lookup_default(&tree_context, nullptr);
- if (tree_log != nullptr) {
- return *tree_log;
- }
-
- const DTreeContext *parent_context = tree_context.parent_context();
- if (parent_context == nullptr) {
- return *root_tree_logs_.get();
- }
- TreeLog &parent_log = this->lookup_or_add_tree_log(log_by_tree_context, *parent_context);
- destruct_ptr<TreeLog> owned_tree_log = allocator_.construct<TreeLog>();
- tree_log = owned_tree_log.get();
- log_by_tree_context.add_new(&tree_context, tree_log);
- parent_log.child_logs_.add_new(tree_context.parent_node()->name(), std::move(owned_tree_log));
- return *tree_log;
-}
-
-NodeLog &ModifierLog::lookup_or_add_node_log(LogByTreeContext &log_by_tree_context, DNode node)
-{
- TreeLog &tree_log = this->lookup_or_add_tree_log(log_by_tree_context, *node.context());
- NodeLog &node_log = *tree_log.node_logs_.lookup_or_add_cb(node->name(), [&]() {
- destruct_ptr<NodeLog> node_log = allocator_.construct<NodeLog>();
- node_log->input_logs_.resize(node->inputs().size());
- node_log->output_logs_.resize(node->outputs().size());
- return node_log;
- });
- return node_log;
-}
-
-SocketLog &ModifierLog::lookup_or_add_socket_log(LogByTreeContext &log_by_tree_context,
- DSocket socket)
-{
- NodeLog &node_log = this->lookup_or_add_node_log(log_by_tree_context, socket.node());
- MutableSpan<SocketLog> socket_logs = socket->is_input() ? node_log.input_logs_ :
- node_log.output_logs_;
- SocketLog &socket_log = socket_logs[socket->index()];
- return socket_log;
-}
-
-void ModifierLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
-{
- if (root_tree_logs_) {
- root_tree_logs_->foreach_node_log(fn);
- }
-}
-
-const GeometryValueLog *ModifierLog::input_geometry_log() const
-{
- return input_geometry_log_.get();
-}
-const GeometryValueLog *ModifierLog::output_geometry_log() const
-{
- return output_geometry_log_.get();
-}
-
-const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const
-{
- const destruct_ptr<NodeLog> *node_log = node_logs_.lookup_ptr_as(node_name);
- if (node_log == nullptr) {
- return nullptr;
- }
- return node_log->get();
-}
-
-const NodeLog *TreeLog::lookup_node_log(const bNode &node) const
-{
- return this->lookup_node_log(node.name);
-}
-
-const TreeLog *TreeLog::lookup_child_log(StringRef node_name) const
-{
- const destruct_ptr<TreeLog> *tree_log = child_logs_.lookup_ptr_as(node_name);
- if (tree_log == nullptr) {
- return nullptr;
- }
- return tree_log->get();
-}
-
-void TreeLog::foreach_node_log(FunctionRef<void(const NodeLog &)> fn) const
-{
- for (auto node_log : node_logs_.items()) {
- fn(*node_log.value);
- }
-
- for (auto child : child_logs_.items()) {
- child.value->foreach_node_log(fn);
- }
-}
-
-const SocketLog *NodeLog::lookup_socket_log(eNodeSocketInOut in_out, int index) const
-{
- BLI_assert(index >= 0);
- Span<SocketLog> socket_logs = (in_out == SOCK_IN) ? input_logs_ : output_logs_;
- if (index >= socket_logs.size()) {
- return nullptr;
- }
- return &socket_logs[index];
-}
-
-const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket &socket) const
-{
- ListBase sockets = socket.in_out == SOCK_IN ? node.inputs : node.outputs;
- int index = BLI_findindex(&sockets, &socket);
- return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index);
-}
-
-GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type())
-{
- const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs();
-
- /* Put the deduplicated field inputs into a vector so that they can be sorted below. */
- Vector<std::reference_wrapper<const FieldInput>> field_inputs;
- if (field_input_nodes) {
- field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(),
- field_input_nodes->deduplicated_nodes.end());
- }
-
- std::sort(
- field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) {
- const int index_a = (int)a.category();
- const int index_b = (int)b.category();
- if (index_a == index_b) {
- return a.socket_inspection_name().size() < b.socket_inspection_name().size();
- }
- return index_a < index_b;
- });
-
- for (const FieldInput &field_input : field_inputs) {
- input_tooltips_.append(field_input.socket_inspection_name());
- }
-
- if (log_full_field) {
- field_ = std::move(field);
- }
-}
-
-GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry)
-{
- static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE,
- GEO_COMPONENT_TYPE_INSTANCES,
- GEO_COMPONENT_TYPE_MESH,
- GEO_COMPONENT_TYPE_POINT_CLOUD,
- GEO_COMPONENT_TYPE_VOLUME};
-
- /* Keep track handled attribute names to make sure that we do not return the same name twice.
- * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge
- * attributes with the same name but different domains or data types on separate components. */
- Set<StringRef> names;
-
- geometry_set.attribute_foreach(
- all_component_types,
- true,
- [&](const bke::AttributeIDRef &attribute_id,
- const bke::AttributeMetaData &meta_data,
- const GeometryComponent &UNUSED(component)) {
- if (attribute_id.is_named() && names.add(attribute_id.name())) {
- this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
- }
- });
-
- for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
- component_types_.append(component->type());
- switch (component->type()) {
- case GEO_COMPONENT_TYPE_MESH: {
- const MeshComponent &mesh_component = *(const MeshComponent *)component;
- MeshInfo &info = this->mesh_info.emplace();
- info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
- info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE);
- break;
- }
- case GEO_COMPONENT_TYPE_CURVE: {
- const CurveComponent &curve_component = *(const CurveComponent *)component;
- CurveInfo &info = this->curve_info.emplace();
- info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
- break;
- }
- case GEO_COMPONENT_TYPE_POINT_CLOUD: {
- const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component;
- PointCloudInfo &info = this->pointcloud_info.emplace();
- info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT);
- break;
- }
- case GEO_COMPONENT_TYPE_INSTANCES: {
- const InstancesComponent &instances_component = *(const InstancesComponent *)component;
- InstancesInfo &info = this->instances_info.emplace();
- info.instances_num = instances_component.instances_num();
- break;
- }
- case GEO_COMPONENT_TYPE_VOLUME: {
- break;
- }
- }
- }
- if (log_full_geometry) {
- full_geometry_ = std::make_unique<GeometrySet>(geometry_set);
- full_geometry_->ensure_owns_direct_data();
- }
-}
-
-Vector<const GeometryAttributeInfo *> NodeLog::lookup_available_attributes() const
-{
- Vector<const GeometryAttributeInfo *> attributes;
- Set<StringRef> names;
- for (const SocketLog &socket_log : input_logs_) {
- const ValueLog *value_log = socket_log.value();
- if (const GeometryValueLog *geo_value_log = dynamic_cast<const GeometryValueLog *>(
- value_log)) {
- for (const GeometryAttributeInfo &attribute : geo_value_log->attributes()) {
- if (names.add(attribute.name)) {
- attributes.append(&attribute);
- }
- }
- }
- }
- return attributes;
-}
-
-const ModifierLog *ModifierLog::find_root_by_node_editor_context(const SpaceNode &snode)
-{
- if (snode.id == nullptr) {
- return nullptr;
- }
- if (GS(snode.id->name) != ID_OB) {
- return nullptr;
- }
- Object *object = (Object *)snode.id;
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- if (md->type == eModifierType_Nodes) {
- NodesModifierData *nmd = (NodesModifierData *)md;
- if (nmd->node_group == snode.nodetree) {
- return (ModifierLog *)nmd->runtime_eval_log;
- }
- }
- }
- return nullptr;
-}
-
-const TreeLog *ModifierLog::find_tree_by_node_editor_context(const SpaceNode &snode)
-{
- const ModifierLog *eval_log = ModifierLog::find_root_by_node_editor_context(snode);
- if (eval_log == nullptr) {
- return nullptr;
- }
- Vector<bNodeTreePath *> tree_path_vec = snode.treepath;
- if (tree_path_vec.is_empty()) {
- return nullptr;
- }
- TreeLog *current = eval_log->root_tree_logs_.get();
- for (bNodeTreePath *path : tree_path_vec.as_span().drop_front(1)) {
- destruct_ptr<TreeLog> *tree_log = current->child_logs_.lookup_ptr_as(path->node_name);
- if (tree_log == nullptr) {
- return nullptr;
- }
- current = tree_log->get();
- }
- return current;
-}
-
-const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode,
- const bNode &node)
-{
- const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode);
- if (tree_log == nullptr) {
- return nullptr;
- }
- return tree_log->lookup_node_log(node);
-}
-
-const NodeLog *ModifierLog::find_node_by_node_editor_context(const SpaceNode &snode,
- const StringRef node_name)
-{
- const TreeLog *tree_log = ModifierLog::find_tree_by_node_editor_context(snode);
- if (tree_log == nullptr) {
- return nullptr;
- }
- return tree_log->lookup_node_log(node_name);
-}
-
-const SocketLog *ModifierLog::find_socket_by_node_editor_context(const SpaceNode &snode,
- const bNode &node,
- const bNodeSocket &socket)
-{
- const NodeLog *node_log = ModifierLog::find_node_by_node_editor_context(snode, node);
- if (node_log == nullptr) {
- return nullptr;
- }
- return node_log->lookup_socket_log(node, socket);
-}
-
-const NodeLog *ModifierLog::find_node_by_spreadsheet_editor_context(
- const SpaceSpreadsheet &sspreadsheet)
-{
- Vector<SpreadsheetContext *> context_path = sspreadsheet.context_path;
- if (context_path.size() <= 2) {
- return nullptr;
- }
- if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
- return nullptr;
- }
- if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
- return nullptr;
- }
- for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) {
- if (context->type != SPREADSHEET_CONTEXT_NODE) {
- return nullptr;
- }
- }
- Span<SpreadsheetContextNode *> node_contexts =
- context_path.as_span().drop_front(2).cast<SpreadsheetContextNode *>();
-
- Object *object = ((SpreadsheetContextObject *)context_path[0])->object;
- StringRefNull modifier_name = ((SpreadsheetContextModifier *)context_path[1])->modifier_name;
- if (object == nullptr) {
- return nullptr;
- }
-
- const ModifierLog *eval_log = nullptr;
- LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) {
- if (md->type == eModifierType_Nodes) {
- if (md->name == modifier_name) {
- NodesModifierData *nmd = (NodesModifierData *)md;
- eval_log = (const ModifierLog *)nmd->runtime_eval_log;
- break;
- }
- }
- }
- if (eval_log == nullptr) {
- return nullptr;
- }
-
- const TreeLog *tree_log = &eval_log->root_tree();
- for (SpreadsheetContextNode *context : node_contexts.drop_back(1)) {
- tree_log = tree_log->lookup_child_log(context->node_name);
- if (tree_log == nullptr) {
- return nullptr;
- }
- }
- const NodeLog *node_log = tree_log->lookup_node_log(node_contexts.last()->node_name);
- return node_log;
-}
-
-void LocalGeoLogger::log_value_for_sockets(Span<DSocket> sockets, GPointer value)
-{
- const CPPType &type = *value.type();
- Span<DSocket> copied_sockets = allocator_->construct_array_copy(sockets);
- if (type.is<GeometrySet>()) {
- bool log_full_geometry = false;
- for (const DSocket &socket : sockets) {
- if (main_logger_->log_full_sockets_.contains(socket)) {
- log_full_geometry = true;
- break;
- }
- }
-
- const GeometrySet &geometry_set = *value.get<GeometrySet>();
- destruct_ptr<GeometryValueLog> value_log = allocator_->construct<GeometryValueLog>(
- geometry_set, log_full_geometry);
- values_.append({copied_sockets, std::move(value_log)});
- }
- else if (const ValueOrFieldCPPType *value_or_field_type =
- dynamic_cast<const ValueOrFieldCPPType *>(&type)) {
- const void *value_or_field = value.get();
- if (value_or_field_type->is_field(value_or_field)) {
- GField field = *value_or_field_type->get_field_ptr(value_or_field);
- bool log_full_field = false;
- if (!field.node().depends_on_input()) {
- /* Always log constant fields so that their value can be shown in socket inspection.
- * In the future we can also evaluate the field here and only store the value. */
- log_full_field = true;
- }
- if (!log_full_field) {
- for (const DSocket &socket : sockets) {
- if (main_logger_->log_full_sockets_.contains(socket)) {
- log_full_field = true;
- break;
- }
- }
- }
- destruct_ptr<GFieldValueLog> value_log = allocator_->construct<GFieldValueLog>(
- std::move(field), log_full_field);
- values_.append({copied_sockets, std::move(value_log)});
- }
- else {
- const CPPType &base_type = value_or_field_type->base_type();
- const void *value = value_or_field_type->get_value_ptr(value_or_field);
- void *buffer = allocator_->allocate(base_type.size(), base_type.alignment());
- base_type.copy_construct(value, buffer);
- destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>(
- GMutablePointer{base_type, buffer});
- values_.append({copied_sockets, std::move(value_log)});
- }
- }
- else {
- void *buffer = allocator_->allocate(type.size(), type.alignment());
- type.copy_construct(value.get(), buffer);
- destruct_ptr<GenericValueLog> value_log = allocator_->construct<GenericValueLog>(
- GMutablePointer{type, buffer});
- values_.append({copied_sockets, std::move(value_log)});
- }
-}
-
-void LocalGeoLogger::log_multi_value_socket(DSocket socket, Span<GPointer> values)
-{
- /* Doesn't have to be logged currently. */
- UNUSED_VARS(socket, values);
-}
-
-void LocalGeoLogger::log_node_warning(DNode node, NodeWarningType type, std::string message)
-{
- node_warnings_.append({node, {type, std::move(message)}});
-}
-
-void LocalGeoLogger::log_execution_time(DNode node, std::chrono::microseconds exec_time)
-{
- node_exec_times_.append({node, exec_time});
-}
-
-void LocalGeoLogger::log_used_named_attribute(DNode node,
- std::string attribute_name,
- eNamedAttrUsage usage)
-{
- used_named_attributes_.append({node, {std::move(attribute_name), usage}});
-}
-
-void LocalGeoLogger::log_debug_message(DNode node, std::string message)
-{
- node_debug_messages_.append({node, std::move(message)});
-}
-
-} // namespace blender::nodes::geometry_nodes_eval_log
diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
new file mode 100644
index 00000000000..4ffeb815e34
--- /dev/null
+++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc
@@ -0,0 +1,1423 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/**
+ * This file mainly converts a #bNodeTree into a lazy-function graph. This generally works by
+ * creating a lazy-function for every node, which is then put into the lazy-function graph. Then
+ * the nodes in the new graph are linked based on links in the original #bNodeTree. Some additional
+ * nodes are inserted for things like type conversions and multi-input sockets.
+ *
+ * Currently, lazy-functions are even created for nodes that don't strictly require it, like
+ * reroutes or muted nodes. In the future we could avoid that at the cost of additional code
+ * complexity. So far, this does not seem to be a performance issue.
+ */
+
+#include "NOD_geometry_exec.hh"
+#include "NOD_geometry_nodes_lazy_function.hh"
+#include "NOD_multi_function.hh"
+#include "NOD_node_declaration.hh"
+
+#include "BLI_lazy_threading.hh"
+#include "BLI_map.hh"
+
+#include "DNA_ID.h"
+
+#include "BKE_compute_contexts.hh"
+#include "BKE_geometry_set.hh"
+#include "BKE_type_conversions.hh"
+
+#include "FN_field_cpp_type.hh"
+#include "FN_lazy_function_graph_executor.hh"
+
+namespace blender::nodes {
+
+using fn::ValueOrField;
+using fn::ValueOrFieldCPPType;
+using namespace fn::multi_function_types;
+
+static const CPPType *get_socket_cpp_type(const bNodeSocketType &typeinfo)
+{
+ const CPPType *type = typeinfo.geometry_nodes_cpp_type;
+ if (type == nullptr) {
+ return nullptr;
+ }
+ BLI_assert(type->has_special_member_functions());
+ return type;
+}
+
+static const CPPType *get_socket_cpp_type(const bNodeSocket &socket)
+{
+ return get_socket_cpp_type(*socket.typeinfo);
+}
+
+static const CPPType *get_vector_type(const CPPType &type)
+{
+ /* This could be generalized in the future. For now we only support a small set of vectors. */
+ if (type.is<GeometrySet>()) {
+ return &CPPType::get<Vector<GeometrySet>>();
+ }
+ if (type.is<ValueOrField<std::string>>()) {
+ return &CPPType::get<Vector<ValueOrField<std::string>>>();
+ }
+ return nullptr;
+}
+
+/**
+ * Checks which sockets of the node are available and creates corresponding inputs/outputs on the
+ * lazy-function.
+ */
+static void lazy_function_interface_from_node(const bNode &node,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs,
+ Vector<lf::Input> &r_inputs,
+ Vector<lf::Output> &r_outputs)
+{
+ const bool is_muted = node.is_muted();
+ const bool supports_laziness = node.typeinfo->geometry_node_execute_supports_laziness ||
+ node.is_group();
+ const lf::ValueUsage input_usage = supports_laziness ? lf::ValueUsage::Maybe :
+ lf::ValueUsage::Used;
+ for (const bNodeSocket *socket : node.input_sockets()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(*socket);
+ if (type == nullptr) {
+ continue;
+ }
+ if (socket->is_multi_input() && !is_muted) {
+ type = get_vector_type(*type);
+ }
+ r_inputs.append({socket->identifier, *type, input_usage});
+ r_used_inputs.append(socket);
+ }
+ for (const bNodeSocket *socket : node.output_sockets()) {
+ if (!socket->is_available()) {
+ continue;
+ }
+ const CPPType *type = get_socket_cpp_type(*socket);
+ if (type == nullptr) {
+ continue;
+ }
+ r_outputs.append({socket->identifier, *type});
+ r_used_outputs.append(socket);
+ }
+}
+
+/**
+ * Used for most normal geometry nodes like Subdivision Surface and Set Position.
+ */
+class LazyFunctionForGeometryNode : public LazyFunction {
+ private:
+ const bNode &node_;
+
+ public:
+ LazyFunctionForGeometryNode(const bNode &node,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs)
+ : node_(node)
+ {
+ BLI_assert(node.typeinfo->geometry_node_execute != nullptr);
+ debug_name_ = node.name;
+ lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_);
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &context) const override
+ {
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+
+ GeoNodeExecParams geo_params{node_, params, context};
+
+ geo_eval_log::TimePoint start_time = geo_eval_log::Clock::now();
+ node_.typeinfo->geometry_node_execute(geo_params);
+ geo_eval_log::TimePoint end_time = geo_eval_log::Clock::now();
+
+ if (geo_eval_log::GeoModifierLog *modifier_log = user_data->modifier_data->eval_log) {
+ geo_eval_log::GeoTreeLogger &tree_logger = modifier_log->get_local_tree_logger(
+ *user_data->compute_context);
+ tree_logger.node_execution_times.append(
+ {tree_logger.allocator->copy_string(node_.name), start_time, end_time});
+ }
+ }
+};
+
+/**
+ * Used to gather all inputs of a multi-input socket. A separate node is necessary, because
+ * multi-inputs are not supported in lazy-function graphs.
+ */
+class LazyFunctionForMultiInput : public LazyFunction {
+ private:
+ const CPPType *base_type_;
+
+ public:
+ LazyFunctionForMultiInput(const bNodeSocket &socket)
+ {
+ debug_name_ = "Multi Input";
+ base_type_ = get_socket_cpp_type(socket);
+ BLI_assert(base_type_ != nullptr);
+ BLI_assert(socket.is_multi_input());
+ for (const bNodeLink *link : socket.directly_linked_links()) {
+ if (!link->is_muted()) {
+ inputs_.append({"Input", *base_type_});
+ }
+ }
+ const CPPType *vector_type = get_vector_type(*base_type_);
+ BLI_assert(vector_type != nullptr);
+ outputs_.append({"Output", *vector_type});
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ /* Currently we only have multi-inputs for geometry and string sockets. This could be
+ * generalized in the future. */
+ base_type_->to_static_type_tag<GeometrySet, ValueOrField<std::string>>([&](auto type_tag) {
+ using T = typename decltype(type_tag)::type;
+ if constexpr (std::is_void_v<T>) {
+ /* This type is not support in this node for now. */
+ BLI_assert_unreachable();
+ }
+ else {
+ void *output_ptr = params.get_output_data_ptr(0);
+ Vector<T> &values = *new (output_ptr) Vector<T>();
+ for (const int i : inputs_.index_range()) {
+ values.append(params.extract_input<T>(i));
+ }
+ params.output_set(0);
+ }
+ });
+ }
+};
+
+/**
+ * Simple lazy-function that just forwards the input.
+ */
+class LazyFunctionForRerouteNode : public LazyFunction {
+ public:
+ LazyFunctionForRerouteNode(const CPPType &type)
+ {
+ debug_name_ = "Reroute";
+ inputs_.append({"Input", type});
+ outputs_.append({"Output", type});
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ void *input_value = params.try_get_input_data_ptr(0);
+ void *output_value = params.get_output_data_ptr(0);
+ BLI_assert(input_value != nullptr);
+ BLI_assert(output_value != nullptr);
+ const CPPType &type = *inputs_[0].type;
+ type.move_construct(input_value, output_value);
+ params.output_set(0);
+ }
+};
+
+/**
+ * Lazy functions for nodes whose type cannot be found. An undefined function just outputs default
+ * values. It's useful to have so other parts of the conversion don't have to care about undefined
+ * nodes.
+ */
+class LazyFunctionForUndefinedNode : public LazyFunction {
+ public:
+ LazyFunctionForUndefinedNode(const bNode &node, Vector<const bNodeSocket *> &r_used_outputs)
+ {
+ debug_name_ = "Undefined";
+ Vector<const bNodeSocket *> dummy_used_inputs;
+ Vector<lf::Input> dummy_inputs;
+ lazy_function_interface_from_node(
+ node, dummy_used_inputs, r_used_outputs, dummy_inputs, outputs_);
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ params.set_default_remaining_outputs();
+ }
+};
+
+/**
+ * Executes a multi-function. If all inputs are single values, the results will also be single
+ * values. If any input is a field, the outputs will also be fields.
+ */
+static void execute_multi_function_on_value_or_field(
+ const MultiFunction &fn,
+ const std::shared_ptr<MultiFunction> &owned_fn,
+ const Span<const ValueOrFieldCPPType *> input_types,
+ const Span<const ValueOrFieldCPPType *> output_types,
+ const Span<const void *> input_values,
+ const Span<void *> output_values)
+{
+ BLI_assert(fn.param_amount() == input_types.size() + output_types.size());
+ BLI_assert(input_types.size() == input_values.size());
+ BLI_assert(output_types.size() == output_values.size());
+
+ /* Check if any input is a field. */
+ bool any_input_is_field = false;
+ for (const int i : input_types.index_range()) {
+ const ValueOrFieldCPPType &type = *input_types[i];
+ const void *value_or_field = input_values[i];
+ if (type.is_field(value_or_field)) {
+ any_input_is_field = true;
+ break;
+ }
+ }
+
+ if (any_input_is_field) {
+ /* Convert all inputs into fields, so that they can be used as input in the new field. */
+ Vector<GField> input_fields;
+ for (const int i : input_types.index_range()) {
+ const ValueOrFieldCPPType &type = *input_types[i];
+ const void *value_or_field = input_values[i];
+ input_fields.append(type.as_field(value_or_field));
+ }
+
+ /* Construct the new field node. */
+ std::shared_ptr<fn::FieldOperation> operation;
+ if (owned_fn) {
+ operation = std::make_shared<fn::FieldOperation>(owned_fn, std::move(input_fields));
+ }
+ else {
+ operation = std::make_shared<fn::FieldOperation>(fn, std::move(input_fields));
+ }
+
+ /* Store the new fields in the output. */
+ for (const int i : output_types.index_range()) {
+ const ValueOrFieldCPPType &type = *output_types[i];
+ void *value_or_field = output_values[i];
+ type.construct_from_field(value_or_field, GField{operation, i});
+ }
+ }
+ else {
+ /* In this case, the multi-function is evaluated directly. */
+ MFParamsBuilder params{fn, 1};
+ MFContextBuilder context;
+
+ for (const int i : input_types.index_range()) {
+ const ValueOrFieldCPPType &type = *input_types[i];
+ const CPPType &base_type = type.base_type();
+ const void *value_or_field = input_values[i];
+ const void *value = type.get_value_ptr(value_or_field);
+ params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, value));
+ }
+ for (const int i : output_types.index_range()) {
+ const ValueOrFieldCPPType &type = *output_types[i];
+ const CPPType &base_type = type.base_type();
+ void *value_or_field = output_values[i];
+ type.default_construct(value_or_field);
+ void *value = type.get_value_ptr(value_or_field);
+ base_type.destruct(value);
+ params.add_uninitialized_single_output(GMutableSpan{base_type, value, 1});
+ }
+ fn.call(IndexRange(1), params, context);
+ }
+}
+
+/**
+ * Behavior of muted nodes:
+ * - Some inputs are forwarded to outputs without changes.
+ * - Some inputs are converted to a different type which becomes the output.
+ * - Some outputs are value initialized because they don't have a corresponding input.
+ */
+class LazyFunctionForMutedNode : public LazyFunction {
+ private:
+ Array<int> input_by_output_index_;
+
+ public:
+ LazyFunctionForMutedNode(const bNode &node,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs)
+ {
+ debug_name_ = "Muted";
+ lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_);
+ for (lf::Input &fn_input : inputs_) {
+ fn_input.usage = lf::ValueUsage::Maybe;
+ }
+
+ for (lf::Input &fn_input : inputs_) {
+ fn_input.usage = lf::ValueUsage::Unused;
+ }
+
+ input_by_output_index_.reinitialize(outputs_.size());
+ input_by_output_index_.fill(-1);
+ for (const bNodeLink *internal_link : node.internal_links_span()) {
+ const int input_i = r_used_inputs.first_index_of_try(internal_link->fromsock);
+ const int output_i = r_used_outputs.first_index_of_try(internal_link->tosock);
+ if (ELEM(-1, input_i, output_i)) {
+ continue;
+ }
+ input_by_output_index_[output_i] = input_i;
+ inputs_[input_i].usage = lf::ValueUsage::Maybe;
+ }
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ for (const int output_i : outputs_.index_range()) {
+ if (params.output_was_set(output_i)) {
+ continue;
+ }
+ const CPPType &output_type = *outputs_[output_i].type;
+ void *output_value = params.get_output_data_ptr(output_i);
+ const int input_i = input_by_output_index_[output_i];
+ if (input_i == -1) {
+ /* The output does not have a corresponding input. */
+ output_type.value_initialize(output_value);
+ params.output_set(output_i);
+ continue;
+ }
+ const void *input_value = params.try_get_input_data_ptr_or_request(input_i);
+ if (input_value == nullptr) {
+ continue;
+ }
+ const CPPType &input_type = *inputs_[input_i].type;
+ if (input_type == output_type) {
+ /* Forward the value as is. */
+ input_type.copy_construct(input_value, output_value);
+ params.output_set(output_i);
+ continue;
+ }
+ /* Perform a type conversion and then format the value. */
+ const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions();
+ const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&input_type);
+ const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&output_type);
+ if (from_field_type != nullptr && to_field_type != nullptr) {
+ const CPPType &from_base_type = from_field_type->base_type();
+ const CPPType &to_base_type = to_field_type->base_type();
+ if (conversions.is_convertible(from_base_type, to_base_type)) {
+ const MultiFunction &multi_fn = *conversions.get_conversion_multi_function(
+ MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type));
+ execute_multi_function_on_value_or_field(
+ multi_fn, {}, {from_field_type}, {to_field_type}, {input_value}, {output_value});
+ }
+ params.output_set(output_i);
+ continue;
+ }
+ /* Use a value initialization if the conversion does not work. */
+ output_type.value_initialize(output_value);
+ params.output_set(output_i);
+ }
+ }
+};
+
+/**
+ * Type conversions are generally implemented as multi-functions. This node checks if the input is
+ * a field or single value and outputs a field or single value respectively.
+ */
+class LazyFunctionForMultiFunctionConversion : public LazyFunction {
+ private:
+ const MultiFunction &fn_;
+ const ValueOrFieldCPPType &from_type_;
+ const ValueOrFieldCPPType &to_type_;
+ const Vector<const bNodeSocket *> target_sockets_;
+
+ public:
+ LazyFunctionForMultiFunctionConversion(const MultiFunction &fn,
+ const ValueOrFieldCPPType &from,
+ const ValueOrFieldCPPType &to,
+ Vector<const bNodeSocket *> &&target_sockets)
+ : fn_(fn), from_type_(from), to_type_(to), target_sockets_(std::move(target_sockets))
+ {
+ debug_name_ = "Convert";
+ inputs_.append({"From", from});
+ outputs_.append({"To", to});
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ const void *from_value = params.try_get_input_data_ptr(0);
+ void *to_value = params.get_output_data_ptr(0);
+ BLI_assert(from_value != nullptr);
+ BLI_assert(to_value != nullptr);
+
+ execute_multi_function_on_value_or_field(
+ fn_, {}, {&from_type_}, {&to_type_}, {from_value}, {to_value});
+
+ params.output_set(0);
+ }
+};
+
+/**
+ * This lazy-function wraps nodes that are implemented as multi-function (mostly math nodes).
+ */
+class LazyFunctionForMultiFunctionNode : public LazyFunction {
+ private:
+ const NodeMultiFunctions::Item fn_item_;
+ Vector<const ValueOrFieldCPPType *> input_types_;
+ Vector<const ValueOrFieldCPPType *> output_types_;
+
+ public:
+ LazyFunctionForMultiFunctionNode(const bNode &node,
+ NodeMultiFunctions::Item fn_item,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs)
+ : fn_item_(std::move(fn_item))
+ {
+ BLI_assert(fn_item_.fn != nullptr);
+ debug_name_ = node.name;
+ lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_);
+ for (const lf::Input &fn_input : inputs_) {
+ input_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_input.type));
+ }
+ for (const lf::Output &fn_output : outputs_) {
+ output_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_output.type));
+ }
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ Vector<const void *> input_values(inputs_.size());
+ Vector<void *> output_values(outputs_.size());
+ for (const int i : inputs_.index_range()) {
+ input_values[i] = params.try_get_input_data_ptr(i);
+ }
+ for (const int i : outputs_.index_range()) {
+ output_values[i] = params.get_output_data_ptr(i);
+ }
+ execute_multi_function_on_value_or_field(
+ *fn_item_.fn, fn_item_.owned_fn, input_types_, output_types_, input_values, output_values);
+ for (const int i : outputs_.index_range()) {
+ params.output_set(i);
+ }
+ }
+};
+
+/**
+ * Some sockets have non-trivial implicit inputs (e.g. the Position input of the Set Position
+ * node). Those are implemented as a separate node that outputs the value.
+ */
+class LazyFunctionForImplicitInput : public LazyFunction {
+ private:
+ /**
+ * The function that generates the implicit input. The passed in memory is uninitialized.
+ */
+ std::function<void(void *)> init_fn_;
+
+ public:
+ LazyFunctionForImplicitInput(const CPPType &type, std::function<void(void *)> init_fn)
+ : init_fn_(std::move(init_fn))
+ {
+ debug_name_ = "Input";
+ outputs_.append({"Output", type});
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &UNUSED(context)) const override
+ {
+ void *value = params.get_output_data_ptr(0);
+ init_fn_(value);
+ params.output_set(0);
+ }
+};
+
+/**
+ * The viewer node does not have outputs. Instead it is executed because the executor knows that it
+ * has side effects. The side effect is that the inputs to the viewer are logged.
+ */
+class LazyFunctionForViewerNode : public LazyFunction {
+ private:
+ const bNode &bnode_;
+ /** The field is only logged when it is linked. */
+ bool use_field_input_ = true;
+
+ public:
+ LazyFunctionForViewerNode(const bNode &bnode, Vector<const bNodeSocket *> &r_used_inputs)
+ : bnode_(bnode)
+ {
+ debug_name_ = "Viewer";
+ Vector<const bNodeSocket *> dummy_used_outputs;
+ lazy_function_interface_from_node(bnode, r_used_inputs, dummy_used_outputs, inputs_, outputs_);
+ if (!r_used_inputs[1]->is_directly_linked()) {
+ use_field_input_ = false;
+ r_used_inputs.pop_last();
+ inputs_.pop_last();
+ }
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &context) const override
+ {
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+
+ GeometrySet geometry = params.extract_input<GeometrySet>(0);
+
+ GField field;
+ if (use_field_input_) {
+ const void *value_or_field = params.try_get_input_data_ptr(1);
+ BLI_assert(value_or_field != nullptr);
+ const ValueOrFieldCPPType &value_or_field_type = static_cast<const ValueOrFieldCPPType &>(
+ *inputs_[1].type);
+ field = value_or_field_type.as_field(value_or_field);
+ }
+
+ geo_eval_log::GeoTreeLogger &tree_logger =
+ user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context);
+ tree_logger.log_viewer_node(bnode_, geometry, field);
+ }
+};
+
+/**
+ * This lazy-function wraps a group node. Internally it just executes the lazy-function graph of
+ * the referenced group.
+ */
+class LazyFunctionForGroupNode : public LazyFunction {
+ private:
+ const bNode &group_node_;
+ bool has_many_nodes_ = false;
+ std::optional<GeometryNodesLazyFunctionLogger> lf_logger_;
+ std::optional<GeometryNodesLazyFunctionSideEffectProvider> lf_side_effect_provider_;
+ std::optional<lf::GraphExecutor> graph_executor_;
+
+ public:
+ LazyFunctionForGroupNode(const bNode &group_node,
+ const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
+ Vector<const bNodeSocket *> &r_used_inputs,
+ Vector<const bNodeSocket *> &r_used_outputs)
+ : group_node_(group_node)
+ {
+ debug_name_ = group_node.name;
+ lazy_function_interface_from_node(
+ group_node, r_used_inputs, r_used_outputs, inputs_, outputs_);
+
+ bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(group_node_.id);
+ BLI_assert(group_btree != nullptr);
+
+ has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000;
+
+ Vector<const lf::OutputSocket *> graph_inputs;
+ for (const lf::OutputSocket *socket : lf_graph_info.mapping.group_input_sockets) {
+ if (socket != nullptr) {
+ graph_inputs.append(socket);
+ }
+ }
+ Vector<const lf::InputSocket *> graph_outputs;
+ if (const bNode *group_output_bnode = group_btree->group_output_node()) {
+ for (const bNodeSocket *bsocket : group_output_bnode->input_sockets().drop_back(1)) {
+ const lf::Socket *socket = lf_graph_info.mapping.dummy_socket_map.lookup_default(bsocket,
+ nullptr);
+ if (socket != nullptr) {
+ graph_outputs.append(&socket->as_input());
+ }
+ }
+ }
+
+ lf_logger_.emplace(lf_graph_info);
+ lf_side_effect_provider_.emplace();
+ graph_executor_.emplace(lf_graph_info.graph,
+ std::move(graph_inputs),
+ std::move(graph_outputs),
+ &*lf_logger_,
+ &*lf_side_effect_provider_);
+ }
+
+ void execute_impl(lf::Params &params, const lf::Context &context) const override
+ {
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+
+ if (has_many_nodes_) {
+ /* If the called node group has many nodes, it's likely that executing it takes a while even
+ * if every individual node is very small. */
+ lazy_threading::send_hint();
+ }
+
+ /* The compute context changes when entering a node group. */
+ bke::NodeGroupComputeContext compute_context{user_data->compute_context, group_node_.name};
+ GeoNodesLFUserData group_user_data = *user_data;
+ group_user_data.compute_context = &compute_context;
+
+ lf::Context group_context = context;
+ group_context.user_data = &group_user_data;
+
+ graph_executor_->execute(params, group_context);
+ }
+
+ void *init_storage(LinearAllocator<> &allocator) const override
+ {
+ return graph_executor_->init_storage(allocator);
+ }
+
+ void destruct_storage(void *storage) const override
+ {
+ graph_executor_->destruct_storage(storage);
+ }
+};
+
+static GMutablePointer get_socket_default_value(LinearAllocator<> &allocator,
+ const bNodeSocket &bsocket)
+{
+ const bNodeSocketType &typeinfo = *bsocket.typeinfo;
+ const CPPType *type = get_socket_cpp_type(typeinfo);
+ if (type == nullptr) {
+ return {};
+ }
+ void *buffer = allocator.allocate(type->size(), type->alignment());
+ typeinfo.get_geometry_nodes_cpp_value(bsocket, buffer);
+ return {type, buffer};
+}
+
+/**
+ * Utility class to build a lazy-function graph based on a geometry nodes tree.
+ * This is mainly a separate class because it makes it easier to have variables that can be
+ * accessed by many functions.
+ */
+struct GeometryNodesLazyFunctionGraphBuilder {
+ private:
+ const bNodeTree &btree_;
+ GeometryNodesLazyFunctionGraphInfo *lf_graph_info_;
+ lf::Graph *lf_graph_;
+ GeometryNodeLazyFunctionGraphMapping *mapping_;
+ MultiValueMap<const bNodeSocket *, lf::InputSocket *> input_socket_map_;
+ Map<const bNodeSocket *, lf::OutputSocket *> output_socket_map_;
+ Map<const bNodeSocket *, lf::Node *> multi_input_socket_nodes_;
+ const bke::DataTypeConversions *conversions_;
+
+ /**
+ * All group input nodes are combined into one dummy node in the lazy-function graph.
+ * If some input has an invalid type, it is ignored in the new graph. In this case null and -1 is
+ * used in the vectors below.
+ */
+ Vector<const CPPType *> group_input_types_;
+ Vector<int> group_input_indices_;
+ lf::DummyNode *group_input_lf_node_;
+
+ /**
+ * The output types or null if an output is invalid. Each group output node gets a separate
+ * corresponding dummy node in the new graph.
+ */
+ Vector<const CPPType *> group_output_types_;
+ Vector<int> group_output_indices_;
+
+ public:
+ GeometryNodesLazyFunctionGraphBuilder(const bNodeTree &btree,
+ GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
+ : btree_(btree), lf_graph_info_(&lf_graph_info)
+ {
+ }
+
+ void build()
+ {
+ btree_.ensure_topology_cache();
+
+ lf_graph_ = &lf_graph_info_->graph;
+ mapping_ = &lf_graph_info_->mapping;
+ conversions_ = &bke::get_implicit_type_conversions();
+
+ this->prepare_node_multi_functions();
+ this->prepare_group_inputs();
+ this->prepare_group_outputs();
+ this->build_group_input_node();
+ this->handle_nodes();
+ this->handle_links();
+ this->add_default_inputs();
+
+ lf_graph_->update_node_indices();
+ lf_graph_info_->num_inline_nodes_approximate += lf_graph_->nodes().size();
+ }
+
+ private:
+ void prepare_node_multi_functions()
+ {
+ lf_graph_info_->node_multi_functions = std::make_unique<NodeMultiFunctions>(btree_);
+ }
+
+ void prepare_group_inputs()
+ {
+ LISTBASE_FOREACH (const bNodeSocket *, interface_bsocket, &btree_.inputs) {
+ const CPPType *type = get_socket_cpp_type(*interface_bsocket->typeinfo);
+ if (type != nullptr) {
+ const int index = group_input_types_.append_and_get_index(type);
+ group_input_indices_.append(index);
+ }
+ else {
+ group_input_indices_.append(-1);
+ }
+ }
+ }
+
+ void prepare_group_outputs()
+ {
+ LISTBASE_FOREACH (const bNodeSocket *, interface_bsocket, &btree_.outputs) {
+ const CPPType *type = get_socket_cpp_type(*interface_bsocket->typeinfo);
+ if (type != nullptr) {
+ const int index = group_output_types_.append_and_get_index(type);
+ group_output_indices_.append(index);
+ }
+ else {
+ group_output_indices_.append(-1);
+ }
+ }
+ }
+
+ void build_group_input_node()
+ {
+ /* Create a dummy node for the group inputs. */
+ group_input_lf_node_ = &lf_graph_->add_dummy({}, group_input_types_);
+ for (const int group_input_index : group_input_indices_) {
+ if (group_input_index == -1) {
+ mapping_->group_input_sockets.append(nullptr);
+ }
+ else {
+ mapping_->group_input_sockets.append(&group_input_lf_node_->output(group_input_index));
+ }
+ }
+ }
+
+ void handle_nodes()
+ {
+ /* Insert all nodes into the lazy function graph. */
+ for (const bNode *bnode : btree_.all_nodes()) {
+ const bNodeType *node_type = bnode->typeinfo;
+ if (node_type == nullptr) {
+ continue;
+ }
+ if (bnode->is_muted()) {
+ this->handle_muted_node(*bnode);
+ continue;
+ }
+ switch (node_type->type) {
+ case NODE_FRAME: {
+ /* Ignored. */
+ break;
+ }
+ case NODE_REROUTE: {
+ this->handle_reroute_node(*bnode);
+ break;
+ }
+ case NODE_GROUP_INPUT: {
+ this->handle_group_input_node(*bnode);
+ break;
+ }
+ case NODE_GROUP_OUTPUT: {
+ this->handle_group_output_node(*bnode);
+ break;
+ }
+ case NODE_CUSTOM_GROUP:
+ case NODE_GROUP: {
+ this->handle_group_node(*bnode);
+ break;
+ }
+ case GEO_NODE_VIEWER: {
+ this->handle_viewer_node(*bnode);
+ break;
+ }
+ default: {
+ if (node_type->geometry_node_execute) {
+ this->handle_geometry_node(*bnode);
+ break;
+ }
+ const NodeMultiFunctions::Item &fn_item = lf_graph_info_->node_multi_functions->try_get(
+ *bnode);
+ if (fn_item.fn != nullptr) {
+ this->handle_multi_function_node(*bnode, fn_item);
+ break;
+ }
+ if (node_type == &NodeTypeUndefined) {
+ this->handle_undefined_node(*bnode);
+ break;
+ }
+ /* Nodes that don't match any of the criteria above are just ignored. */
+ break;
+ }
+ }
+ }
+ }
+
+ void handle_muted_node(const bNode &bnode)
+ {
+ Vector<const bNodeSocket *> used_inputs;
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForMutedNode>(
+ bnode, used_inputs, used_outputs);
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ lf::InputSocket &lf_socket = lf_node.input(i);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add_new(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_reroute_node(const bNode &bnode)
+ {
+ const bNodeSocket &input_bsocket = bnode.input_socket(0);
+ const bNodeSocket &output_bsocket = bnode.output_socket(0);
+ const CPPType *type = get_socket_cpp_type(input_bsocket);
+ if (type == nullptr) {
+ return;
+ }
+
+ auto lazy_function = std::make_unique<LazyFunctionForRerouteNode>(*type);
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ lf::InputSocket &lf_input = lf_node.input(0);
+ lf::OutputSocket &lf_output = lf_node.output(0);
+ input_socket_map_.add(&input_bsocket, &lf_input);
+ output_socket_map_.add_new(&output_bsocket, &lf_output);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_input, &input_bsocket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_output, &output_bsocket);
+ }
+
+ void handle_group_input_node(const bNode &bnode)
+ {
+ for (const int btree_index : group_input_indices_.index_range()) {
+ const int lf_index = group_input_indices_[btree_index];
+ if (lf_index == -1) {
+ continue;
+ }
+ const bNodeSocket &bsocket = bnode.output_socket(btree_index);
+ lf::OutputSocket &lf_socket = group_input_lf_node_->output(lf_index);
+ output_socket_map_.add_new(&bsocket, &lf_socket);
+ mapping_->dummy_socket_map.add_new(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_group_output_node(const bNode &bnode)
+ {
+ lf::DummyNode &group_output_lf_node = lf_graph_->add_dummy(group_output_types_, {});
+ for (const int btree_index : group_output_indices_.index_range()) {
+ const int lf_index = group_output_indices_[btree_index];
+ if (lf_index == -1) {
+ continue;
+ }
+ const bNodeSocket &bsocket = bnode.input_socket(btree_index);
+ lf::InputSocket &lf_socket = group_output_lf_node.input(lf_index);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->dummy_socket_map.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_group_node(const bNode &bnode)
+ {
+ const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id);
+ if (group_btree == nullptr) {
+ return;
+ }
+ const GeometryNodesLazyFunctionGraphInfo *group_lf_graph_info =
+ ensure_geometry_nodes_lazy_function_graph(*group_btree);
+ if (group_lf_graph_info == nullptr) {
+ return;
+ }
+
+ Vector<const bNodeSocket *> used_inputs;
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForGroupNode>(
+ bnode, *group_lf_graph_info, used_inputs, used_outputs);
+ lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ BLI_assert(!bsocket.is_multi_input());
+ lf::InputSocket &lf_socket = lf_node.input(i);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add_new(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ mapping_->group_node_map.add(&bnode, &lf_node);
+ lf_graph_info_->num_inline_nodes_approximate +=
+ group_lf_graph_info->num_inline_nodes_approximate;
+ }
+
+ void handle_geometry_node(const bNode &bnode)
+ {
+ Vector<const bNodeSocket *> used_inputs;
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForGeometryNode>(
+ bnode, used_inputs, used_outputs);
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ lf::InputSocket &lf_socket = lf_node.input(i);
+
+ if (bsocket.is_multi_input()) {
+ auto multi_input_lazy_function = std::make_unique<LazyFunctionForMultiInput>(bsocket);
+ lf::Node &lf_multi_input_node = lf_graph_->add_function(*multi_input_lazy_function);
+ lf_graph_info_->functions.append(std::move(multi_input_lazy_function));
+ lf_graph_->add_link(lf_multi_input_node.output(0), lf_socket);
+ multi_input_socket_nodes_.add_new(&bsocket, &lf_multi_input_node);
+ for (lf::InputSocket *lf_multi_input_socket : lf_multi_input_node.inputs()) {
+ mapping_->bsockets_by_lf_socket_map.add(lf_multi_input_socket, &bsocket);
+ }
+ }
+ else {
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add_new(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_multi_function_node(const bNode &bnode, const NodeMultiFunctions::Item &fn_item)
+ {
+ Vector<const bNodeSocket *> used_inputs;
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForMultiFunctionNode>(
+ bnode, fn_item, used_inputs, used_outputs);
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ BLI_assert(!bsocket.is_multi_input());
+ lf::InputSocket &lf_socket = lf_node.input(i);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_viewer_node(const bNode &bnode)
+ {
+ Vector<const bNodeSocket *> used_inputs;
+ auto lazy_function = std::make_unique<LazyFunctionForViewerNode>(bnode, used_inputs);
+ lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ for (const int i : used_inputs.index_range()) {
+ const bNodeSocket &bsocket = *used_inputs[i];
+ lf::InputSocket &lf_socket = lf_node.input(i);
+ input_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+
+ mapping_->viewer_node_map.add(&bnode, &lf_node);
+ }
+
+ void handle_undefined_node(const bNode &bnode)
+ {
+ Vector<const bNodeSocket *> used_outputs;
+ auto lazy_function = std::make_unique<LazyFunctionForUndefinedNode>(bnode, used_outputs);
+ lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+
+ for (const int i : used_outputs.index_range()) {
+ const bNodeSocket &bsocket = *used_outputs[i];
+ lf::OutputSocket &lf_socket = lf_node.output(i);
+ output_socket_map_.add(&bsocket, &lf_socket);
+ mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
+ }
+ }
+
+ void handle_links()
+ {
+ for (const auto item : output_socket_map_.items()) {
+ this->insert_links_from_socket(*item.key, *item.value);
+ }
+ }
+
+ void insert_links_from_socket(const bNodeSocket &from_bsocket, lf::OutputSocket &from_lf_socket)
+ {
+ const Span<const bNodeLink *> links_from_bsocket = from_bsocket.directly_linked_links();
+
+ struct TypeWithLinks {
+ const CPPType *type;
+ Vector<const bNodeLink *> links;
+ };
+
+ /* Group available target sockets by type so that they can be handled together. */
+ Vector<TypeWithLinks> types_with_links;
+ for (const bNodeLink *link : links_from_bsocket) {
+ if (link->is_muted()) {
+ continue;
+ }
+ if (!link->is_available()) {
+ continue;
+ }
+ const bNodeSocket &to_bsocket = *link->tosock;
+ const CPPType *to_type = get_socket_cpp_type(to_bsocket);
+ if (to_type == nullptr) {
+ continue;
+ }
+ bool inserted = false;
+ for (TypeWithLinks &types_with_links : types_with_links) {
+ if (types_with_links.type == to_type) {
+ types_with_links.links.append(link);
+ inserted = true;
+ break;
+ }
+ }
+ if (inserted) {
+ continue;
+ }
+ types_with_links.append({to_type, {link}});
+ }
+
+ for (const TypeWithLinks &type_with_links : types_with_links) {
+ const CPPType &to_type = *type_with_links.type;
+ const Span<const bNodeLink *> links = type_with_links.links;
+
+ Vector<const bNodeSocket *> target_bsockets;
+ for (const bNodeLink *link : links) {
+ target_bsockets.append(link->tosock);
+ }
+
+ lf::OutputSocket *converted_from_lf_socket = this->insert_type_conversion_if_necessary(
+ from_lf_socket, to_type, std::move(target_bsockets));
+
+ auto make_input_link_or_set_default = [&](lf::InputSocket &to_lf_socket) {
+ if (converted_from_lf_socket == nullptr) {
+ const void *default_value = to_type.default_value();
+ to_lf_socket.set_default_value(default_value);
+ }
+ else {
+ lf_graph_->add_link(*converted_from_lf_socket, to_lf_socket);
+ }
+ };
+
+ for (const bNodeLink *link : links) {
+ const bNodeSocket &to_bsocket = *link->tosock;
+ if (to_bsocket.is_multi_input()) {
+ /* TODO: Cache this index on the link. */
+ int link_index = 0;
+ for (const bNodeLink *multi_input_link : to_bsocket.directly_linked_links()) {
+ if (multi_input_link == link) {
+ break;
+ }
+ if (!multi_input_link->is_muted()) {
+ link_index++;
+ }
+ }
+ if (to_bsocket.owner_node().is_muted()) {
+ if (link_index == 0) {
+ for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) {
+ make_input_link_or_set_default(*to_lf_socket);
+ }
+ }
+ }
+ else {
+ lf::Node *multi_input_lf_node = multi_input_socket_nodes_.lookup_default(&to_bsocket,
+ nullptr);
+ if (multi_input_lf_node == nullptr) {
+ continue;
+ }
+ make_input_link_or_set_default(multi_input_lf_node->input(link_index));
+ }
+ }
+ else {
+ for (lf::InputSocket *to_lf_socket : input_socket_map_.lookup(&to_bsocket)) {
+ make_input_link_or_set_default(*to_lf_socket);
+ }
+ }
+ }
+ }
+ }
+
+ lf::OutputSocket *insert_type_conversion_if_necessary(
+ lf::OutputSocket &from_socket,
+ const CPPType &to_type,
+ Vector<const bNodeSocket *> &&target_sockets)
+ {
+ const CPPType &from_type = from_socket.type();
+ if (from_type == to_type) {
+ return &from_socket;
+ }
+ const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&from_type);
+ const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type);
+ if (from_field_type != nullptr && to_field_type != nullptr) {
+ const CPPType &from_base_type = from_field_type->base_type();
+ const CPPType &to_base_type = to_field_type->base_type();
+ if (conversions_->is_convertible(from_base_type, to_base_type)) {
+ const MultiFunction &multi_fn = *conversions_->get_conversion_multi_function(
+ MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type));
+ auto fn = std::make_unique<LazyFunctionForMultiFunctionConversion>(
+ multi_fn, *from_field_type, *to_field_type, std::move(target_sockets));
+ lf::Node &conversion_node = lf_graph_->add_function(*fn);
+ lf_graph_info_->functions.append(std::move(fn));
+ lf_graph_->add_link(from_socket, conversion_node.input(0));
+ return &conversion_node.output(0);
+ }
+ }
+ return nullptr;
+ }
+
+ void add_default_inputs()
+ {
+ for (auto item : input_socket_map_.items()) {
+ const bNodeSocket &bsocket = *item.key;
+ const Span<lf::InputSocket *> lf_sockets = item.value;
+ for (lf::InputSocket *lf_socket : lf_sockets) {
+ if (lf_socket->origin() != nullptr) {
+ /* Is linked already. */
+ continue;
+ }
+ this->add_default_input(bsocket, *lf_socket);
+ }
+ }
+ }
+
+ void add_default_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket)
+ {
+ if (this->try_add_implicit_input(input_bsocket, input_lf_socket)) {
+ return;
+ }
+ GMutablePointer value = get_socket_default_value(lf_graph_info_->allocator, input_bsocket);
+ if (value.get() == nullptr) {
+ /* Not possible to add a default value. */
+ return;
+ }
+ input_lf_socket.set_default_value(value.get());
+ if (!value.type()->is_trivially_destructible()) {
+ lf_graph_info_->values_to_destruct.append(value);
+ }
+ }
+
+ bool try_add_implicit_input(const bNodeSocket &input_bsocket, lf::InputSocket &input_lf_socket)
+ {
+ const bNode &bnode = input_bsocket.owner_node();
+ const NodeDeclaration *node_declaration = bnode.declaration();
+ if (node_declaration == nullptr) {
+ return false;
+ }
+ const SocketDeclaration &socket_declaration =
+ *node_declaration->inputs()[input_bsocket.index()];
+ if (socket_declaration.input_field_type() != InputSocketFieldType::Implicit) {
+ return false;
+ }
+ const CPPType &type = input_lf_socket.type();
+ std::function<void(void *)> init_fn = this->get_implicit_input_init_function(bnode,
+ input_bsocket);
+ if (!init_fn) {
+ return false;
+ }
+
+ auto lazy_function = std::make_unique<LazyFunctionForImplicitInput>(type, std::move(init_fn));
+ lf::Node &lf_node = lf_graph_->add_function(*lazy_function);
+ lf_graph_info_->functions.append(std::move(lazy_function));
+ lf_graph_->add_link(lf_node.output(0), input_lf_socket);
+ return true;
+ }
+
+ std::function<void(void *)> get_implicit_input_init_function(const bNode &bnode,
+ const bNodeSocket &bsocket)
+ {
+ const bNodeSocketType &socket_type = *bsocket.typeinfo;
+ if (socket_type.type == SOCK_VECTOR) {
+ if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) {
+ StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode ==
+ GEO_NODE_CURVE_HANDLE_LEFT ?
+ "handle_left" :
+ "handle_right";
+ return [side](void *r_value) {
+ new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>(side));
+ };
+ }
+ else if (bnode.type == GEO_NODE_EXTRUDE_MESH) {
+ return [](void *r_value) {
+ new (r_value)
+ ValueOrField<float3>(Field<float3>(std::make_shared<bke::NormalFieldInput>()));
+ };
+ }
+ else {
+ return [](void *r_value) {
+ new (r_value) ValueOrField<float3>(bke::AttributeFieldInput::Create<float3>("position"));
+ };
+ }
+ }
+ else if (socket_type.type == SOCK_INT) {
+ if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) {
+ return [](void *r_value) {
+ new (r_value)
+ ValueOrField<int>(Field<int>(std::make_shared<bke::IDAttributeFieldInput>()));
+ };
+ }
+ else {
+ return [](void *r_value) {
+ new (r_value) ValueOrField<int>(Field<int>(std::make_shared<fn::IndexFieldInput>()));
+ };
+ }
+ }
+ return {};
+ }
+};
+
+const GeometryNodesLazyFunctionGraphInfo *ensure_geometry_nodes_lazy_function_graph(
+ const bNodeTree &btree)
+{
+ btree.ensure_topology_cache();
+ if (btree.has_available_link_cycle()) {
+ return nullptr;
+ }
+
+ std::unique_ptr<GeometryNodesLazyFunctionGraphInfo> &lf_graph_info_ptr =
+ btree.runtime->geometry_nodes_lazy_function_graph_info;
+
+ if (lf_graph_info_ptr) {
+ return lf_graph_info_ptr.get();
+ }
+ std::lock_guard lock{btree.runtime->geometry_nodes_lazy_function_graph_info_mutex};
+ if (lf_graph_info_ptr) {
+ return lf_graph_info_ptr.get();
+ }
+
+ auto lf_graph_info = std::make_unique<GeometryNodesLazyFunctionGraphInfo>();
+ GeometryNodesLazyFunctionGraphBuilder builder{btree, *lf_graph_info};
+ builder.build();
+
+ lf_graph_info_ptr = std::move(lf_graph_info);
+ return lf_graph_info_ptr.get();
+}
+
+GeometryNodesLazyFunctionLogger::GeometryNodesLazyFunctionLogger(
+ const GeometryNodesLazyFunctionGraphInfo &lf_graph_info)
+ : lf_graph_info_(lf_graph_info)
+{
+}
+
+void GeometryNodesLazyFunctionLogger::log_socket_value(
+ const fn::lazy_function::Socket &lf_socket,
+ const GPointer value,
+ const fn::lazy_function::Context &context) const
+{
+ const Span<const bNodeSocket *> bsockets =
+ lf_graph_info_.mapping.bsockets_by_lf_socket_map.lookup(&lf_socket);
+ if (bsockets.is_empty()) {
+ return;
+ }
+
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ if (user_data->modifier_data->eval_log == nullptr) {
+ return;
+ }
+ geo_eval_log::GeoTreeLogger &tree_logger =
+ user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context);
+ for (const bNodeSocket *bsocket : bsockets) {
+ /* Avoid logging to some sockets when the same value will also be logged to a linked socket.
+ * This reduces the number of logged values without losing information. */
+ if (bsocket->is_input() && bsocket->is_directly_linked()) {
+ continue;
+ }
+ const bNode &bnode = bsocket->owner_node();
+ if (bnode.is_reroute()) {
+ continue;
+ }
+ tree_logger.log_value(bsocket->owner_node(), *bsocket, value);
+ }
+}
+
+static std::mutex dump_error_context_mutex;
+
+void GeometryNodesLazyFunctionLogger::dump_when_outputs_are_missing(
+ const lf::FunctionNode &node,
+ Span<const lf::OutputSocket *> missing_sockets,
+ const lf::Context &context) const
+{
+ std::lock_guard lock{dump_error_context_mutex};
+
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ user_data->compute_context->print_stack(std::cout, node.name());
+ std::cout << "Missing outputs:\n";
+ for (const lf::OutputSocket *socket : missing_sockets) {
+ std::cout << " " << socket->name() << "\n";
+ }
+}
+
+void GeometryNodesLazyFunctionLogger::dump_when_input_is_set_twice(
+ const lf::InputSocket &target_socket,
+ const lf::OutputSocket &from_socket,
+ const lf::Context &context) const
+{
+ std::lock_guard lock{dump_error_context_mutex};
+
+ std::stringstream ss;
+ ss << from_socket.node().name() << ":" << from_socket.name() << " -> "
+ << target_socket.node().name() << ":" << target_socket.name();
+
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ user_data->compute_context->print_stack(std::cout, ss.str());
+}
+
+Vector<const lf::FunctionNode *> GeometryNodesLazyFunctionSideEffectProvider::
+ get_nodes_with_side_effects(const lf::Context &context) const
+{
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ const ComputeContextHash &context_hash = user_data->compute_context->hash();
+ const GeoNodesModifierData &modifier_data = *user_data->modifier_data;
+ return modifier_data.side_effect_nodes->lookup(context_hash);
+}
+
+GeometryNodesLazyFunctionGraphInfo::GeometryNodesLazyFunctionGraphInfo() = default;
+GeometryNodesLazyFunctionGraphInfo::~GeometryNodesLazyFunctionGraphInfo()
+{
+ for (GMutablePointer &p : this->values_to_destruct) {
+ p.destruct();
+ }
+}
+
+[[maybe_unused]] static void add_thread_id_debug_message(
+ const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
+ const lf::FunctionNode &node,
+ const lf::Context &context)
+{
+ static std::atomic<int> thread_id_source = 0;
+ static thread_local const int thread_id = thread_id_source.fetch_add(1);
+ static thread_local const std::string thread_id_str = "Thread: " + std::to_string(thread_id);
+
+ GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
+ BLI_assert(user_data != nullptr);
+ if (user_data->modifier_data->eval_log == nullptr) {
+ return;
+ }
+ geo_eval_log::GeoTreeLogger &tree_logger =
+ user_data->modifier_data->eval_log->get_local_tree_logger(*user_data->compute_context);
+
+ /* Find corresponding node based on the socket mapping. */
+ auto check_sockets = [&](const Span<const lf::Socket *> lf_sockets) {
+ for (const lf::Socket *lf_socket : lf_sockets) {
+ const Span<const bNodeSocket *> bsockets =
+ lf_graph_info.mapping.bsockets_by_lf_socket_map.lookup(lf_socket);
+ if (!bsockets.is_empty()) {
+ const bNodeSocket &bsocket = *bsockets[0];
+ const bNode &bnode = bsocket.owner_node();
+ tree_logger.debug_messages.append(
+ {tree_logger.allocator->copy_string(bnode.name), thread_id_str});
+ return true;
+ }
+ }
+ return false;
+ };
+
+ if (check_sockets(node.inputs().cast<const lf::Socket *>())) {
+ return;
+ }
+ check_sockets(node.outputs().cast<const lf::Socket *>());
+}
+
+void GeometryNodesLazyFunctionLogger::log_before_node_execute(const lf::FunctionNode &node,
+ const lf::Params &UNUSED(params),
+ const lf::Context &context) const
+{
+ /* Enable this to see the threads that invoked a node. */
+ if constexpr (false) {
+ add_thread_id_debug_message(lf_graph_info_, node, context);
+ }
+}
+
+} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc
new file mode 100644
index 00000000000..110573c9119
--- /dev/null
+++ b/source/blender/nodes/intern/geometry_nodes_log.cc
@@ -0,0 +1,610 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include "NOD_geometry_nodes_lazy_function.hh"
+#include "NOD_geometry_nodes_log.hh"
+
+#include "BKE_compute_contexts.hh"
+#include "BKE_curves.hh"
+#include "BKE_node_runtime.hh"
+
+#include "FN_field_cpp_type.hh"
+
+#include "DNA_modifier_types.h"
+#include "DNA_space_types.h"
+
+namespace blender::nodes::geo_eval_log {
+
+using fn::FieldInput;
+using fn::FieldInputs;
+
+GenericValueLog::~GenericValueLog()
+{
+ this->value.destruct();
+}
+
+FieldInfoLog::FieldInfoLog(const GField &field) : type(field.cpp_type())
+{
+ const std::shared_ptr<const fn::FieldInputs> &field_input_nodes = field.node().field_inputs();
+
+ /* Put the deduplicated field inputs into a vector so that they can be sorted below. */
+ Vector<std::reference_wrapper<const FieldInput>> field_inputs;
+ if (field_input_nodes) {
+ field_inputs.extend(field_input_nodes->deduplicated_nodes.begin(),
+ field_input_nodes->deduplicated_nodes.end());
+ }
+
+ std::sort(
+ field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) {
+ const int index_a = (int)a.category();
+ const int index_b = (int)b.category();
+ if (index_a == index_b) {
+ return a.socket_inspection_name().size() < b.socket_inspection_name().size();
+ }
+ return index_a < index_b;
+ });
+
+ for (const FieldInput &field_input : field_inputs) {
+ this->input_tooltips.append(field_input.socket_inspection_name());
+ }
+}
+
+GeometryInfoLog::GeometryInfoLog(const GeometrySet &geometry_set)
+{
+ static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE,
+ GEO_COMPONENT_TYPE_INSTANCES,
+ GEO_COMPONENT_TYPE_MESH,
+ GEO_COMPONENT_TYPE_POINT_CLOUD,
+ GEO_COMPONENT_TYPE_VOLUME};
+
+ /* Keep track handled attribute names to make sure that we do not return the same name twice.
+ * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge
+ * attributes with the same name but different domains or data types on separate components. */
+ Set<StringRef> names;
+
+ geometry_set.attribute_foreach(
+ all_component_types,
+ true,
+ [&](const bke::AttributeIDRef &attribute_id,
+ const bke::AttributeMetaData &meta_data,
+ const GeometryComponent &UNUSED(component)) {
+ if (attribute_id.is_named() && names.add(attribute_id.name())) {
+ this->attributes.append({attribute_id.name(), meta_data.domain, meta_data.data_type});
+ }
+ });
+
+ for (const GeometryComponent *component : geometry_set.get_components_for_read()) {
+ this->component_types.append(component->type());
+ switch (component->type()) {
+ case GEO_COMPONENT_TYPE_MESH: {
+ const MeshComponent &mesh_component = *(const MeshComponent *)component;
+ MeshInfo &info = this->mesh_info.emplace();
+ info.verts_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ info.edges_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE);
+ info.faces_num = mesh_component.attribute_domain_size(ATTR_DOMAIN_FACE);
+ break;
+ }
+ case GEO_COMPONENT_TYPE_CURVE: {
+ const CurveComponent &curve_component = *(const CurveComponent *)component;
+ CurveInfo &info = this->curve_info.emplace();
+ info.splines_num = curve_component.attribute_domain_size(ATTR_DOMAIN_CURVE);
+ break;
+ }
+ case GEO_COMPONENT_TYPE_POINT_CLOUD: {
+ const PointCloudComponent &pointcloud_component = *(const PointCloudComponent *)component;
+ PointCloudInfo &info = this->pointcloud_info.emplace();
+ info.points_num = pointcloud_component.attribute_domain_size(ATTR_DOMAIN_POINT);
+ break;
+ }
+ case GEO_COMPONENT_TYPE_INSTANCES: {
+ const InstancesComponent &instances_component = *(const InstancesComponent *)component;
+ InstancesInfo &info = this->instances_info.emplace();
+ info.instances_num = instances_component.instances_num();
+ break;
+ }
+ case GEO_COMPONENT_TYPE_EDIT: {
+ const GeometryComponentEditData &edit_component = *(
+ const GeometryComponentEditData *)component;
+ if (const bke::CurvesEditHints *curve_edit_hints =
+ edit_component.curves_edit_hints_.get()) {
+ EditDataInfo &info = this->edit_data_info.emplace();
+ info.has_deform_matrices = curve_edit_hints->deform_mats.has_value();
+ info.has_deformed_positions = curve_edit_hints->positions.has_value();
+ }
+ break;
+ }
+ case GEO_COMPONENT_TYPE_VOLUME: {
+ break;
+ }
+ }
+ }
+}
+
+/* Avoid generating these in every translation unit. */
+GeoModifierLog::GeoModifierLog() = default;
+GeoModifierLog::~GeoModifierLog() = default;
+
+GeoTreeLogger::GeoTreeLogger() = default;
+GeoTreeLogger::~GeoTreeLogger() = default;
+
+GeoNodeLog::GeoNodeLog() = default;
+GeoNodeLog::~GeoNodeLog() = default;
+
+GeoTreeLog::GeoTreeLog(GeoModifierLog *modifier_log, Vector<GeoTreeLogger *> tree_loggers)
+ : modifier_log_(modifier_log), tree_loggers_(std::move(tree_loggers))
+{
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const ComputeContextHash &hash : tree_logger->children_hashes) {
+ children_hashes_.add(hash);
+ }
+ }
+}
+
+GeoTreeLog::~GeoTreeLog() = default;
+
+void GeoTreeLogger::log_value(const bNode &node, const bNodeSocket &socket, const GPointer value)
+{
+ const CPPType &type = *value.type();
+
+ auto store_logged_value = [&](destruct_ptr<ValueLog> value_log) {
+ auto &socket_values = socket.in_out == SOCK_IN ? this->input_socket_values :
+ this->output_socket_values;
+ socket_values.append({this->allocator->copy_string(node.name),
+ this->allocator->copy_string(socket.identifier),
+ std::move(value_log)});
+ };
+
+ auto log_generic_value = [&](const CPPType &type, const void *value) {
+ void *buffer = this->allocator->allocate(type.size(), type.alignment());
+ type.copy_construct(value, buffer);
+ store_logged_value(this->allocator->construct<GenericValueLog>(GMutablePointer{type, buffer}));
+ };
+
+ if (type.is<GeometrySet>()) {
+ const GeometrySet &geometry = *value.get<GeometrySet>();
+ store_logged_value(this->allocator->construct<GeometryInfoLog>(geometry));
+ }
+ else if (const auto *value_or_field_type = dynamic_cast<const fn::ValueOrFieldCPPType *>(
+ &type)) {
+ const void *value_or_field = value.get();
+ const CPPType &base_type = value_or_field_type->base_type();
+ if (value_or_field_type->is_field(value_or_field)) {
+ const GField *field = value_or_field_type->get_field_ptr(value_or_field);
+ if (field->node().depends_on_input()) {
+ store_logged_value(this->allocator->construct<FieldInfoLog>(*field));
+ }
+ else {
+ BUFFER_FOR_CPP_TYPE_VALUE(base_type, value);
+ fn::evaluate_constant_field(*field, value);
+ log_generic_value(base_type, value);
+ }
+ }
+ else {
+ const void *value = value_or_field_type->get_value_ptr(value_or_field);
+ log_generic_value(base_type, value);
+ }
+ }
+ else {
+ log_generic_value(type, value.get());
+ }
+}
+
+void GeoTreeLogger::log_viewer_node(const bNode &viewer_node,
+ const GeometrySet &geometry,
+ const GField &field)
+{
+ destruct_ptr<ViewerNodeLog> log = this->allocator->construct<ViewerNodeLog>();
+ log->geometry = geometry;
+ log->field = field;
+ log->geometry.ensure_owns_direct_data();
+ this->viewer_node_logs.append({this->allocator->copy_string(viewer_node.name), std::move(log)});
+}
+
+void GeoTreeLog::ensure_node_warnings()
+{
+ if (reduced_node_warnings_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::WarningWithNode &warnings : tree_logger->node_warnings) {
+ this->nodes.lookup_or_add_default(warnings.node_name).warnings.append(warnings.warning);
+ this->all_warnings.append(warnings.warning);
+ }
+ }
+ for (const ComputeContextHash &child_hash : children_hashes_) {
+ GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash);
+ child_log.ensure_node_warnings();
+ const std::optional<std::string> &group_node_name =
+ child_log.tree_loggers_[0]->group_node_name;
+ if (group_node_name.has_value()) {
+ this->nodes.lookup_or_add_default(*group_node_name).warnings.extend(child_log.all_warnings);
+ }
+ this->all_warnings.extend(child_log.all_warnings);
+ }
+ reduced_node_warnings_ = true;
+}
+
+void GeoTreeLog::ensure_node_run_time()
+{
+ if (reduced_node_run_times_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::NodeExecutionTime &timings : tree_logger->node_execution_times) {
+ const std::chrono::nanoseconds duration = timings.end - timings.start;
+ this->nodes.lookup_or_add_default_as(timings.node_name).run_time += duration;
+ this->run_time_sum += duration;
+ }
+ }
+ for (const ComputeContextHash &child_hash : children_hashes_) {
+ GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash);
+ child_log.ensure_node_run_time();
+ const std::optional<std::string> &group_node_name =
+ child_log.tree_loggers_[0]->group_node_name;
+ if (group_node_name.has_value()) {
+ this->nodes.lookup_or_add_default(*group_node_name).run_time += child_log.run_time_sum;
+ }
+ this->run_time_sum += child_log.run_time_sum;
+ }
+ reduced_node_run_times_ = true;
+}
+
+void GeoTreeLog::ensure_socket_values()
+{
+ if (reduced_socket_values_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->input_socket_values) {
+ this->nodes.lookup_or_add_as(value_log_data.node_name)
+ .input_values_.add(value_log_data.socket_identifier, value_log_data.value.get());
+ }
+ for (const GeoTreeLogger::SocketValueLog &value_log_data : tree_logger->output_socket_values) {
+ this->nodes.lookup_or_add_as(value_log_data.node_name)
+ .output_values_.add(value_log_data.socket_identifier, value_log_data.value.get());
+ }
+ }
+ reduced_socket_values_ = true;
+}
+
+void GeoTreeLog::ensure_viewer_node_logs()
+{
+ if (reduced_viewer_node_logs_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::ViewerNodeLogWithNode &viewer_log : tree_logger->viewer_node_logs) {
+ this->viewer_node_logs.add(viewer_log.node_name, viewer_log.viewer_log.get());
+ }
+ }
+ reduced_viewer_node_logs_ = true;
+}
+
+void GeoTreeLog::ensure_existing_attributes()
+{
+ if (reduced_existing_attributes_) {
+ return;
+ }
+ this->ensure_socket_values();
+
+ Set<StringRef> names;
+
+ auto handle_value_log = [&](const ValueLog &value_log) {
+ const GeometryInfoLog *geo_log = dynamic_cast<const GeometryInfoLog *>(&value_log);
+ if (geo_log == nullptr) {
+ return;
+ }
+ for (const GeometryAttributeInfo &attribute : geo_log->attributes) {
+ if (names.add(attribute.name)) {
+ this->existing_attributes.append(&attribute);
+ }
+ }
+ };
+
+ for (const GeoNodeLog &node_log : this->nodes.values()) {
+ for (const ValueLog *value_log : node_log.input_values_.values()) {
+ handle_value_log(*value_log);
+ }
+ for (const ValueLog *value_log : node_log.output_values_.values()) {
+ handle_value_log(*value_log);
+ }
+ }
+ reduced_existing_attributes_ = true;
+}
+
+void GeoTreeLog::ensure_used_named_attributes()
+{
+ if (reduced_used_named_attributes_) {
+ return;
+ }
+
+ auto add_attribute = [&](const StringRefNull node_name,
+ const StringRefNull attribute_name,
+ const NamedAttributeUsage &usage) {
+ this->nodes.lookup_or_add_default(node_name).used_named_attributes.lookup_or_add(
+ attribute_name, usage) |= usage;
+ this->used_named_attributes.lookup_or_add_as(attribute_name, usage) |= usage;
+ };
+
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::AttributeUsageWithNode &item : tree_logger->used_named_attributes) {
+ add_attribute(item.node_name, item.attribute_name, item.usage);
+ }
+ }
+ for (const ComputeContextHash &child_hash : children_hashes_) {
+ GeoTreeLog &child_log = modifier_log_->get_tree_log(child_hash);
+ child_log.ensure_used_named_attributes();
+ if (const std::optional<std::string> &group_node_name =
+ child_log.tree_loggers_[0]->group_node_name) {
+ for (const auto &item : child_log.used_named_attributes.items()) {
+ add_attribute(*group_node_name, item.key, item.value);
+ }
+ }
+ }
+ reduced_used_named_attributes_ = true;
+}
+
+void GeoTreeLog::ensure_debug_messages()
+{
+ if (reduced_debug_messages_) {
+ return;
+ }
+ for (GeoTreeLogger *tree_logger : tree_loggers_) {
+ for (const GeoTreeLogger::DebugMessage &debug_message : tree_logger->debug_messages) {
+ this->nodes.lookup_or_add_as(debug_message.node_name)
+ .debug_messages.append(debug_message.message);
+ }
+ }
+ reduced_debug_messages_ = true;
+}
+
+ValueLog *GeoTreeLog::find_socket_value_log(const bNodeSocket &query_socket)
+{
+ /**
+ * Geometry nodes does not log values for every socket. That would produce a lot of redundant
+ * data,because often many linked sockets have the same value. To find the logged value for a
+ * socket one might have to look at linked sockets as well.
+ */
+
+ BLI_assert(reduced_socket_values_);
+ if (query_socket.is_multi_input()) {
+ /* Not supported currently. */
+ return nullptr;
+ }
+
+ Set<const bNodeSocket *> added_sockets;
+ Stack<const bNodeSocket *> sockets_to_check;
+ sockets_to_check.push(&query_socket);
+ added_sockets.add(&query_socket);
+
+ while (!sockets_to_check.is_empty()) {
+ const bNodeSocket &socket = *sockets_to_check.pop();
+ const bNode &node = socket.owner_node();
+ if (GeoNodeLog *node_log = this->nodes.lookup_ptr(node.name)) {
+ ValueLog *value_log = socket.is_input() ?
+ node_log->input_values_.lookup_default(socket.identifier,
+ nullptr) :
+ node_log->output_values_.lookup_default(socket.identifier,
+ nullptr);
+ if (value_log != nullptr) {
+ return value_log;
+ }
+ }
+
+ if (socket.is_input()) {
+ const Span<const bNodeLink *> links = socket.directly_linked_links();
+ for (const bNodeLink *link : links) {
+ const bNodeSocket &from_socket = *link->fromsock;
+ if (added_sockets.add(&from_socket)) {
+ sockets_to_check.push(&from_socket);
+ }
+ }
+ }
+ else {
+ if (node.is_reroute()) {
+ const bNodeSocket &input_socket = node.input_socket(0);
+ if (added_sockets.add(&input_socket)) {
+ sockets_to_check.push(&input_socket);
+ }
+ const Span<const bNodeLink *> links = input_socket.directly_linked_links();
+ for (const bNodeLink *link : links) {
+ const bNodeSocket &from_socket = *link->fromsock;
+ if (added_sockets.add(&from_socket)) {
+ sockets_to_check.push(&from_socket);
+ }
+ }
+ }
+ else if (node.is_muted()) {
+ if (const bNodeSocket *input_socket = socket.internal_link_input()) {
+ if (added_sockets.add(input_socket)) {
+ sockets_to_check.push(input_socket);
+ }
+ const Span<const bNodeLink *> links = input_socket->directly_linked_links();
+ for (const bNodeLink *link : links) {
+ const bNodeSocket &from_socket = *link->fromsock;
+ if (added_sockets.add(&from_socket)) {
+ sockets_to_check.push(&from_socket);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+GeoTreeLogger &GeoModifierLog::get_local_tree_logger(const ComputeContext &compute_context)
+{
+ LocalData &local_data = data_per_thread_.local();
+ Map<ComputeContextHash, destruct_ptr<GeoTreeLogger>> &local_tree_loggers =
+ local_data.tree_logger_by_context;
+ destruct_ptr<GeoTreeLogger> &tree_logger_ptr = local_tree_loggers.lookup_or_add_default(
+ compute_context.hash());
+ if (tree_logger_ptr) {
+ return *tree_logger_ptr;
+ }
+ tree_logger_ptr = local_data.allocator.construct<GeoTreeLogger>();
+ GeoTreeLogger &tree_logger = *tree_logger_ptr;
+ tree_logger.allocator = &local_data.allocator;
+ const ComputeContext *parent_compute_context = compute_context.parent();
+ if (parent_compute_context != nullptr) {
+ tree_logger.parent_hash = parent_compute_context->hash();
+ GeoTreeLogger &parent_logger = this->get_local_tree_logger(*parent_compute_context);
+ parent_logger.children_hashes.append(compute_context.hash());
+ }
+ if (const bke::NodeGroupComputeContext *node_group_compute_context =
+ dynamic_cast<const bke::NodeGroupComputeContext *>(&compute_context)) {
+ tree_logger.group_node_name.emplace(node_group_compute_context->node_name());
+ }
+ return tree_logger;
+}
+
+GeoTreeLog &GeoModifierLog::get_tree_log(const ComputeContextHash &compute_context_hash)
+{
+ GeoTreeLog &reduced_tree_log = *tree_logs_.lookup_or_add_cb(compute_context_hash, [&]() {
+ Vector<GeoTreeLogger *> tree_logs;
+ for (LocalData &local_data : data_per_thread_) {
+ destruct_ptr<GeoTreeLogger> *tree_log = local_data.tree_logger_by_context.lookup_ptr(
+ compute_context_hash);
+ if (tree_log != nullptr) {
+ tree_logs.append(tree_log->get());
+ }
+ }
+ return std::make_unique<GeoTreeLog>(this, std::move(tree_logs));
+ });
+ return reduced_tree_log;
+}
+
+struct ObjectAndModifier {
+ const Object *object;
+ const NodesModifierData *nmd;
+};
+
+static std::optional<ObjectAndModifier> get_modifier_for_node_editor(const SpaceNode &snode)
+{
+ if (snode.id == nullptr) {
+ return std::nullopt;
+ }
+ if (GS(snode.id->name) != ID_OB) {
+ return std::nullopt;
+ }
+ const Object *object = reinterpret_cast<Object *>(snode.id);
+ const NodesModifierData *used_modifier = nullptr;
+ if (snode.flag & SNODE_PIN) {
+ LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
+ /* Would be good to store the name of the pinned modifier in the node editor. */
+ if (nmd->node_group == snode.nodetree) {
+ used_modifier = nmd;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ LISTBASE_FOREACH (const ModifierData *, md, &object->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ const NodesModifierData *nmd = reinterpret_cast<const NodesModifierData *>(md);
+ if (nmd->node_group == snode.nodetree) {
+ if (md->flag & eModifierFlag_Active) {
+ used_modifier = nmd;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (used_modifier == nullptr) {
+ return std::nullopt;
+ }
+ return ObjectAndModifier{object, used_modifier};
+}
+
+GeoTreeLog *GeoModifierLog::get_tree_log_for_node_editor(const SpaceNode &snode)
+{
+ std::optional<ObjectAndModifier> object_and_modifier = get_modifier_for_node_editor(snode);
+ if (!object_and_modifier) {
+ return nullptr;
+ }
+ GeoModifierLog *modifier_log = static_cast<GeoModifierLog *>(
+ object_and_modifier->nmd->runtime_eval_log);
+ if (modifier_log == nullptr) {
+ return nullptr;
+ }
+ Vector<const bNodeTreePath *> tree_path = snode.treepath;
+ if (tree_path.is_empty()) {
+ return nullptr;
+ }
+ ComputeContextBuilder compute_context_builder;
+ compute_context_builder.push<bke::ModifierComputeContext>(
+ object_and_modifier->nmd->modifier.name);
+ for (const bNodeTreePath *path_item : tree_path.as_span().drop_front(1)) {
+ compute_context_builder.push<bke::NodeGroupComputeContext>(path_item->node_name);
+ }
+ return &modifier_log->get_tree_log(compute_context_builder.hash());
+}
+
+const ViewerNodeLog *GeoModifierLog::find_viewer_node_log_for_spreadsheet(
+ const SpaceSpreadsheet &sspreadsheet)
+{
+ Vector<const SpreadsheetContext *> context_path = sspreadsheet.context_path;
+ if (context_path.size() < 3) {
+ return nullptr;
+ }
+ if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) {
+ return nullptr;
+ }
+ if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) {
+ return nullptr;
+ }
+ const SpreadsheetContextObject *object_context =
+ reinterpret_cast<const SpreadsheetContextObject *>(context_path[0]);
+ const SpreadsheetContextModifier *modifier_context =
+ reinterpret_cast<const SpreadsheetContextModifier *>(context_path[1]);
+ if (object_context->object == nullptr) {
+ return nullptr;
+ }
+ NodesModifierData *nmd = nullptr;
+ LISTBASE_FOREACH (ModifierData *, md, &object_context->object->modifiers) {
+ if (STREQ(md->name, modifier_context->modifier_name)) {
+ if (md->type == eModifierType_Nodes) {
+ nmd = reinterpret_cast<NodesModifierData *>(md);
+ }
+ }
+ }
+ if (nmd == nullptr) {
+ return nullptr;
+ }
+ if (nmd->runtime_eval_log == nullptr) {
+ return nullptr;
+ }
+ nodes::geo_eval_log::GeoModifierLog *modifier_log =
+ static_cast<nodes::geo_eval_log::GeoModifierLog *>(nmd->runtime_eval_log);
+
+ ComputeContextBuilder compute_context_builder;
+ compute_context_builder.push<bke::ModifierComputeContext>(modifier_context->modifier_name);
+ for (const SpreadsheetContext *context : context_path.as_span().drop_front(2).drop_back(1)) {
+ if (context->type != SPREADSHEET_CONTEXT_NODE) {
+ return nullptr;
+ }
+ const SpreadsheetContextNode &node_context = *reinterpret_cast<const SpreadsheetContextNode *>(
+ context);
+ compute_context_builder.push<bke::NodeGroupComputeContext>(node_context.node_name);
+ }
+ const ComputeContextHash context_hash = compute_context_builder.hash();
+ nodes::geo_eval_log::GeoTreeLog &tree_log = modifier_log->get_tree_log(context_hash);
+ tree_log.ensure_viewer_node_logs();
+
+ const SpreadsheetContext *last_context = context_path.last();
+ if (last_context->type != SPREADSHEET_CONTEXT_NODE) {
+ return nullptr;
+ }
+ const SpreadsheetContextNode &last_node_context =
+ *reinterpret_cast<const SpreadsheetContextNode *>(last_context);
+ const ViewerNodeLog *viewer_log = tree_log.viewer_node_logs.lookup_default(
+ last_node_context.node_name, nullptr);
+ return viewer_log;
+}
+
+} // namespace blender::nodes::geo_eval_log
diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc
index b7c5f9570e4..1f085375329 100644
--- a/source/blender/nodes/intern/node_common.cc
+++ b/source/blender/nodes/intern/node_common.cc
@@ -37,6 +37,7 @@ using blender::MultiValueMap;
using blender::Set;
using blender::Stack;
using blender::StringRef;
+using blender::Vector;
/* -------------------------------------------------------------------- */
/** \name Node Group
@@ -81,7 +82,9 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **dis
return false;
}
-bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint)
+bool nodeGroupPoll(const bNodeTree *nodetree,
+ const bNodeTree *grouptree,
+ const char **r_disabled_hint)
{
bool valid = true;
@@ -93,13 +96,16 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis
}
if (nodetree == grouptree) {
- *r_disabled_hint = TIP_("Nesting a node group inside of itself is not allowed");
+ if (r_disabled_hint) {
+ *r_disabled_hint = TIP_("Nesting a node group inside of itself is not allowed");
+ }
return false;
}
- LISTBASE_FOREACH (bNode *, node, &grouptree->nodes) {
+ LISTBASE_FOREACH (const bNode *, node, &grouptree->nodes) {
if (node->typeinfo->poll_instance &&
- !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) {
+ !node->typeinfo->poll_instance(
+ const_cast<bNode *>(node), const_cast<bNodeTree *>(nodetree), r_disabled_hint)) {
valid = false;
break;
}
@@ -160,6 +166,7 @@ static void group_verify_socket_list(bNodeTree &node_tree,
const bool ensure_extend_socket_exists)
{
ListBase old_sockets = verify_lb;
+ Vector<bNodeSocket *> ordered_old_sockets = old_sockets;
BLI_listbase_clear(&verify_lb);
LISTBASE_FOREACH (const bNodeSocket *, interface_socket, &interface_sockets) {
@@ -193,6 +200,19 @@ static void group_verify_socket_list(bNodeTree &node_tree,
LISTBASE_FOREACH_MUTABLE (bNodeSocket *, unused_socket, &old_sockets) {
nodeRemoveSocket(&node_tree, &node, unused_socket);
}
+
+ {
+ /* Check if new sockets match the old sockets. */
+ int index;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, new_socket, &verify_lb, index) {
+ if (index < ordered_old_sockets.size()) {
+ if (ordered_old_sockets[index] != new_socket) {
+ BKE_ntree_update_tag_interface(&node_tree);
+ break;
+ }
+ }
+ }
+ }
}
void node_group_update(struct bNodeTree *ntree, struct bNode *node)
diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc
index 56e9c9f0496..1de92fa8409 100644
--- a/source/blender/nodes/intern/node_geometry_exec.cc
+++ b/source/blender/nodes/intern/node_geometry_exec.cc
@@ -4,40 +4,38 @@
#include "DEG_depsgraph_query.h"
+#include "BKE_curves.hh"
#include "BKE_type_conversions.hh"
#include "NOD_geometry_exec.hh"
#include "node_geometry_util.hh"
-using blender::nodes::geometry_nodes_eval_log::LocalGeoLogger;
-
namespace blender::nodes {
-void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::string message) const
+void GeoNodeExecParams::error_message_add(const NodeWarningType type,
+ const StringRef message) const
{
- if (provider_->logger == nullptr) {
- return;
+ if (geo_eval_log::GeoTreeLogger *tree_logger = this->get_local_tree_logger()) {
+ tree_logger->node_warnings.append({tree_logger->allocator->copy_string(node_.name),
+ {type, tree_logger->allocator->copy_string(message)}});
}
- LocalGeoLogger &local_logger = provider_->logger->local();
- local_logger.log_node_warning(provider_->dnode, type, std::move(message));
}
-void GeoNodeExecParams::used_named_attribute(std::string attribute_name,
- const eNamedAttrUsage usage)
+void GeoNodeExecParams::used_named_attribute(const StringRef attribute_name,
+ const NamedAttributeUsage usage)
{
- if (provider_->logger == nullptr) {
- return;
+ if (geo_eval_log::GeoTreeLogger *tree_logger = this->get_local_tree_logger()) {
+ tree_logger->used_named_attributes.append({tree_logger->allocator->copy_string(node_.name),
+ tree_logger->allocator->copy_string(attribute_name),
+ usage});
}
- LocalGeoLogger &local_logger = provider_->logger->local();
- local_logger.log_used_named_attribute(provider_->dnode, std::move(attribute_name), usage);
}
void GeoNodeExecParams::check_input_geometry_set(StringRef identifier,
const GeometrySet &geometry_set) const
{
- const SocketDeclaration &decl =
- *provider_->dnode->input_by_identifier(identifier).bsocket()->runtime->declaration;
+ const SocketDeclaration &decl = *node_.input_by_identifier(identifier).runtime->declaration;
const decl::Geometry *geo_decl = dynamic_cast<const decl::Geometry *>(&decl);
if (geo_decl == nullptr) {
return;
@@ -94,16 +92,32 @@ void GeoNodeExecParams::check_input_geometry_set(StringRef identifier,
message += TIP_("Curve");
break;
}
+ case GEO_COMPONENT_TYPE_EDIT: {
+ continue;
+ }
}
this->error_message_add(NodeWarningType::Info, std::move(message));
}
}
+void GeoNodeExecParams::check_output_geometry_set(const GeometrySet &geometry_set) const
+{
+ UNUSED_VARS_NDEBUG(geometry_set);
+#ifdef DEBUG
+ if (const bke::CurvesEditHints *curve_edit_hints =
+ geometry_set.get_curve_edit_hints_for_read()) {
+ /* If this is not valid, it's likely that the number of stored deformed points does not match
+ * the number of points in the original data. */
+ BLI_assert(curve_edit_hints->is_valid());
+ }
+#endif
+}
+
const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const
{
- for (const InputSocketRef *socket : provider_->dnode->inputs()) {
- if (socket->is_available() && socket->name() == name) {
- return socket->bsocket();
+ for (const bNodeSocket *socket : node_.input_sockets()) {
+ if (socket->is_available() && socket->name == name) {
+ return socket;
}
}
@@ -112,21 +126,21 @@ const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name
std::string GeoNodeExecParams::attribute_producer_name() const
{
- return provider_->dnode->label_or_name() + TIP_(" node");
+ return node_.label_or_name() + TIP_(" node");
}
void GeoNodeExecParams::set_default_remaining_outputs()
{
- provider_->set_default_remaining_outputs();
+ params_.set_default_remaining_outputs();
}
void GeoNodeExecParams::check_input_access(StringRef identifier,
const CPPType *requested_type) const
{
- bNodeSocket *found_socket = nullptr;
- for (const InputSocketRef *socket : provider_->dnode->inputs()) {
- if (socket->identifier() == identifier) {
- found_socket = socket->bsocket();
+ const bNodeSocket *found_socket = nullptr;
+ for (const bNodeSocket *socket : node_.input_sockets()) {
+ if (socket->identifier == identifier) {
+ found_socket = socket;
break;
}
}
@@ -134,9 +148,9 @@ void GeoNodeExecParams::check_input_access(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 : provider_->dnode->inputs()) {
+ for (const bNodeSocket *socket : node_.input_sockets()) {
if (socket->is_available()) {
- std::cout << "'" << socket->identifier() << "', ";
+ std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
@@ -147,13 +161,7 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
<< "' is disabled.\n";
BLI_assert_unreachable();
}
- 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_unreachable();
- }
- else if (requested_type != nullptr) {
+ else if (requested_type != nullptr && (found_socket->flag & SOCK_MULTI_INPUT) == 0) {
const CPPType &expected_type = *found_socket->typeinfo->geometry_nodes_cpp_type;
if (*requested_type != expected_type) {
std::cout << "The requested type '" << requested_type->name() << "' is incorrect. Expected '"
@@ -165,10 +173,10 @@ void GeoNodeExecParams::check_input_access(StringRef identifier,
void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType &value_type) const
{
- bNodeSocket *found_socket = nullptr;
- for (const OutputSocketRef *socket : provider_->dnode->outputs()) {
- if (socket->identifier() == identifier) {
- found_socket = socket->bsocket();
+ const bNodeSocket *found_socket = nullptr;
+ for (const bNodeSocket *socket : node_.output_sockets()) {
+ if (socket->identifier == identifier) {
+ found_socket = socket;
break;
}
}
@@ -176,9 +184,9 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType
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 : provider_->dnode->outputs()) {
+ for (const bNodeSocket *socket : node_.output_sockets()) {
if (socket->is_available()) {
- std::cout << "'" << socket->identifier() << "', ";
+ std::cout << "'" << socket->identifier << "', ";
}
}
std::cout << "\n";
@@ -189,7 +197,7 @@ void GeoNodeExecParams::check_output_access(StringRef identifier, const CPPType
<< "' is disabled.\n";
BLI_assert_unreachable();
}
- else if (!provider_->can_set_output(identifier)) {
+ else if (params_.output_was_set(this->get_output_index(identifier))) {
std::cout << "The identifier '" << identifier << "' has been set already.\n";
BLI_assert_unreachable();
}
diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc
index 13bfdfbfac1..d731fe8f877 100644
--- a/source/blender/nodes/intern/node_multi_function.cc
+++ b/source/blender/nodes/intern/node_multi_function.cc
@@ -2,22 +2,22 @@
#include "NOD_multi_function.hh"
+#include "BKE_node.h"
+#include "BKE_node_runtime.hh"
+
namespace blender::nodes {
-NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree)
+NodeMultiFunctions::NodeMultiFunctions(const bNodeTree &tree)
{
- for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) {
- bNodeTree *btree = tree_ref->btree();
- for (const NodeRef *node : tree_ref->nodes()) {
- bNode *bnode = node->bnode();
- if (bnode->typeinfo->build_multi_function == nullptr) {
- continue;
- }
- NodeMultiFunctionBuilder builder{*bnode, *btree};
- bnode->typeinfo->build_multi_function(builder);
- if (builder.built_fn_ != nullptr) {
- map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)});
- }
+ tree.ensure_topology_cache();
+ for (const bNode *bnode : tree.all_nodes()) {
+ if (bnode->typeinfo->build_multi_function == nullptr) {
+ continue;
+ }
+ NodeMultiFunctionBuilder builder{*bnode, tree};
+ bnode->typeinfo->build_multi_function(builder);
+ if (builder.built_fn_ != nullptr) {
+ map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)});
}
}
}
diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc
deleted file mode 100644
index 64a8690a869..00000000000
--- a/source/blender/nodes/intern/node_tree_ref.cc
+++ /dev/null
@@ -1,678 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-
-#include <mutex>
-
-#include "NOD_node_tree_ref.hh"
-
-#include "BLI_dot_export.hh"
-#include "BLI_stack.hh"
-
-#include "RNA_prototypes.h"
-
-namespace blender::nodes {
-
-NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
-{
- Map<bNode *, NodeRef *> node_mapping;
-
- LISTBASE_FOREACH (bNode *, bnode, &btree->nodes) {
- NodeRef &node = *allocator_.construct<NodeRef>().release();
-
- node.tree_ = this;
- node.bnode_ = bnode;
- node.id_ = nodes_by_id_.append_and_get_index(&node);
-
- LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->inputs) {
- InputSocketRef &socket = *allocator_.construct<InputSocketRef>().release();
- socket.node_ = &node;
- socket.index_ = node.inputs_.append_and_get_index(&socket);
- socket.is_input_ = true;
- socket.bsocket_ = bsocket;
- socket.id_ = sockets_by_id_.append_and_get_index(&socket);
- }
-
- LISTBASE_FOREACH (bNodeSocket *, bsocket, &bnode->outputs) {
- OutputSocketRef &socket = *allocator_.construct<OutputSocketRef>().release();
- socket.node_ = &node;
- socket.index_ = node.outputs_.append_and_get_index(&socket);
- socket.is_input_ = false;
- socket.bsocket_ = bsocket;
- socket.id_ = sockets_by_id_.append_and_get_index(&socket);
- }
-
- LISTBASE_FOREACH (bNodeLink *, blink, &bnode->internal_links) {
- InternalLinkRef &internal_link = *allocator_.construct<InternalLinkRef>().release();
- internal_link.blink_ = blink;
- for (InputSocketRef *socket_ref : node.inputs_) {
- if (socket_ref->bsocket_ == blink->fromsock) {
- internal_link.from_ = socket_ref;
- break;
- }
- }
- for (OutputSocketRef *socket_ref : node.outputs_) {
- if (socket_ref->bsocket_ == blink->tosock) {
- internal_link.to_ = socket_ref;
- break;
- }
- }
- BLI_assert(internal_link.from_ != nullptr);
- BLI_assert(internal_link.to_ != nullptr);
- node.internal_links_.append(&internal_link);
- }
-
- input_sockets_.extend(node.inputs_.as_span());
- output_sockets_.extend(node.outputs_.as_span());
-
- node_mapping.add_new(bnode, &node);
- }
-
- LISTBASE_FOREACH (bNodeLink *, blink, &btree->links) {
- OutputSocketRef &from_socket = this->find_output_socket(
- node_mapping, blink->fromnode, blink->fromsock);
- InputSocketRef &to_socket = this->find_input_socket(
- node_mapping, blink->tonode, blink->tosock);
-
- LinkRef &link = *allocator_.construct<LinkRef>().release();
- link.from_ = &from_socket;
- link.to_ = &to_socket;
- link.blink_ = blink;
-
- links_.append(&link);
-
- from_socket.directly_linked_links_.append(&link);
- to_socket.directly_linked_links_.append(&link);
- }
-
- for (InputSocketRef *input_socket : input_sockets_) {
- if (input_socket->is_multi_input_socket()) {
- std::sort(input_socket->directly_linked_links_.begin(),
- input_socket->directly_linked_links_.end(),
- [&](const LinkRef *a, const LinkRef *b) -> bool {
- int index_a = a->blink()->multi_input_socket_index;
- int index_b = b->blink()->multi_input_socket_index;
- return index_a > index_b;
- });
- }
- }
-
- this->create_socket_identifier_maps();
- this->create_linked_socket_caches();
-
- for (NodeRef *node : nodes_by_id_) {
- const bNodeType *nodetype = node->bnode_->typeinfo;
- nodes_by_type_.add(nodetype, node);
- }
-
- const Span<const NodeRef *> group_output_nodes = this->nodes_by_type("NodeGroupOutput");
- if (group_output_nodes.is_empty()) {
- group_output_node_ = nullptr;
- }
- else if (group_output_nodes.size() == 1) {
- group_output_node_ = group_output_nodes.first();
- }
- else {
- for (const NodeRef *group_output : group_output_nodes) {
- if (group_output->bnode_->flag & NODE_DO_OUTPUT) {
- group_output_node_ = group_output;
- break;
- }
- }
- }
-}
-
-NodeTreeRef::~NodeTreeRef()
-{
- /* The destructor has to be called manually, because these types are allocated in a linear
- * allocator. */
- for (NodeRef *node : nodes_by_id_) {
- node->~NodeRef();
- }
- for (InputSocketRef *socket : input_sockets_) {
- socket->~InputSocketRef();
- }
- for (OutputSocketRef *socket : output_sockets_) {
- socket->~OutputSocketRef();
- }
- for (LinkRef *link : links_) {
- link->~LinkRef();
- }
-}
-
-InputSocketRef &NodeTreeRef::find_input_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket)
-{
- NodeRef *node = node_mapping.lookup(bnode);
- for (InputSocketRef *socket : node->inputs_) {
- if (socket->bsocket_ == bsocket) {
- return *socket;
- }
- }
- BLI_assert_unreachable();
- return *node->inputs_[0];
-}
-
-OutputSocketRef &NodeTreeRef::find_output_socket(Map<bNode *, NodeRef *> &node_mapping,
- bNode *bnode,
- bNodeSocket *bsocket)
-{
- NodeRef *node = node_mapping.lookup(bnode);
- for (OutputSocketRef *socket : node->outputs_) {
- if (socket->bsocket_ == bsocket) {
- return *socket;
- }
- }
- BLI_assert_unreachable();
- return *node->outputs_[0];
-}
-
-void NodeTreeRef::create_linked_socket_caches()
-{
- for (InputSocketRef *socket : input_sockets_) {
- /* Find directly linked socket based on incident links. */
- Vector<const SocketRef *> directly_linked_sockets;
- for (LinkRef *link : socket->directly_linked_links_) {
- directly_linked_sockets.append(link->from_);
- }
- socket->directly_linked_sockets_ = allocator_.construct_array_copy(
- directly_linked_sockets.as_span());
-
- /* Find logically linked sockets. */
- Vector<const SocketRef *> logically_linked_sockets;
- Vector<const SocketRef *> logically_linked_skipped_sockets;
- Vector<const InputSocketRef *> seen_sockets_stack;
- socket->foreach_logical_origin(
- [&](const OutputSocketRef &origin) { logically_linked_sockets.append(&origin); },
- [&](const SocketRef &socket) { logically_linked_skipped_sockets.append(&socket); },
- false,
- seen_sockets_stack);
- if (logically_linked_sockets == directly_linked_sockets) {
- socket->logically_linked_sockets_ = socket->directly_linked_sockets_;
- }
- else {
- 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<const SocketRef *> directly_linked_sockets;
- for (LinkRef *link : socket->directly_linked_links_) {
- directly_linked_sockets.append(link->to_);
- }
- socket->directly_linked_sockets_ = allocator_.construct_array_copy(
- directly_linked_sockets.as_span());
-
- /* Find logically linked sockets. */
- 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_;
- }
- else {
- 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 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 *> &seen_sockets_stack) const
-{
- /* Protect against loops. */
- if (seen_sockets_stack.contains(this)) {
- return;
- }
- seen_sockets_stack.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 (const LinkRef *link : links_to_check) {
- if (link->is_muted()) {
- continue;
- }
- 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_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, seen_sockets_stack);
- }
- 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, seen_sockets_stack);
- }
- }
- }
- else {
- origin_fn(origin);
- }
- }
-
- seen_sockets_stack.pop_last();
-}
-
-void OutputSocketRef::foreach_logical_target(
- FunctionRef<void(const InputSocketRef &)> target_fn,
- FunctionRef<void(const SocketRef &)> skipped_fn,
- Vector<const OutputSocketRef *> &seen_sockets_stack) const
-{
- /* Protect against loops. */
- if (seen_sockets_stack.contains(this)) {
- return;
- }
- seen_sockets_stack.append(this);
-
- for (const LinkRef *link : this->directly_linked_links()) {
- if (link->is_muted()) {
- continue;
- }
- 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_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, seen_sockets_stack);
- }
- 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, seen_sockets_stack);
- }
- }
- }
- else {
- target_fn(target);
- }
- }
-
- seen_sockets_stack.pop_last();
-}
-
-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));
- }
- }
-}
-
-static bool has_link_cycles_recursive(const NodeRef &node,
- MutableSpan<bool> visited,
- MutableSpan<bool> is_in_stack)
-{
- const int node_id = node.id();
- if (is_in_stack[node_id]) {
- return true;
- }
- if (visited[node_id]) {
- return false;
- }
-
- visited[node_id] = true;
- is_in_stack[node_id] = true;
-
- for (const OutputSocketRef *from_socket : node.outputs()) {
- if (!from_socket->is_available()) {
- continue;
- }
- for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
- if (!to_socket->is_available()) {
- continue;
- }
- const NodeRef &to_node = to_socket->node();
- if (has_link_cycles_recursive(to_node, visited, is_in_stack)) {
- return true;
- }
- }
- }
-
- is_in_stack[node_id] = false;
- return false;
-}
-
-bool NodeTreeRef::has_link_cycles() const
-{
- const int node_amount = nodes_by_id_.size();
- Array<bool> visited(node_amount, false);
- Array<bool> is_in_stack(node_amount, false);
-
- for (const NodeRef *node : nodes_by_id_) {
- if (has_link_cycles_recursive(*node, visited, is_in_stack)) {
- return true;
- }
- }
- 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 false;
-}
-
-bool NodeRef::any_input_is_directly_linked() const
-{
- for (const SocketRef *socket : inputs_) {
- if (!socket->directly_linked_sockets().is_empty()) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeRef::any_output_is_directly_linked() const
-{
- for (const SocketRef *socket : outputs_) {
- if (!socket->directly_linked_sockets().is_empty()) {
- return true;
- }
- }
- return false;
-}
-
-bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const
-{
- if (in_out == SOCK_IN) {
- return this->any_input_is_directly_linked();
- }
- return this->any_output_is_directly_linked();
-}
-
-struct ToposortNodeState {
- bool is_done = false;
- bool is_in_stack = false;
-};
-
-static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction,
- const NodeRef &start_node,
- MutableSpan<ToposortNodeState> node_states,
- NodeTreeRef::ToposortResult &result)
-{
- struct Item {
- const NodeRef *node;
- /* Index of the next socket that is checked in the depth-first search. */
- int socket_index = 0;
- /* Link index in the next socket that is checked in the depth-first search. */
- int link_index = 0;
- };
-
- /* Do a depth-first search to sort nodes topologically. */
- Stack<Item, 64> nodes_to_check;
- nodes_to_check.push({&start_node});
- while (!nodes_to_check.is_empty()) {
- Item &item = nodes_to_check.peek();
- const NodeRef &node = *item.node;
- const Span<const SocketRef *> sockets = node.sockets(
- direction == NodeTreeRef::ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT);
-
- while (true) {
- if (item.socket_index == sockets.size()) {
- /* All sockets have already been visited. */
- break;
- }
- const SocketRef &socket = *sockets[item.socket_index];
- const Span<const SocketRef *> linked_sockets = socket.directly_linked_sockets();
- if (item.link_index == linked_sockets.size()) {
- /* All links connected to this socket have already been visited. */
- item.socket_index++;
- item.link_index = 0;
- continue;
- }
- const SocketRef &linked_socket = *linked_sockets[item.link_index];
- const NodeRef &linked_node = linked_socket.node();
- ToposortNodeState &linked_node_state = node_states[linked_node.id()];
- if (linked_node_state.is_done) {
- /* The linked node has already been visited. */
- item.link_index++;
- continue;
- }
- if (linked_node_state.is_in_stack) {
- result.has_cycle = true;
- }
- else {
- nodes_to_check.push({&linked_node});
- linked_node_state.is_in_stack = true;
- }
- break;
- }
-
- /* If no other element has been pushed, the current node can be pushed to the sorted list. */
- if (&item == &nodes_to_check.peek()) {
- ToposortNodeState &node_state = node_states[node.id()];
- node_state.is_done = true;
- node_state.is_in_stack = false;
- result.sorted_nodes.append(&node);
- nodes_to_check.pop();
- }
- }
-}
-
-NodeTreeRef::ToposortResult NodeTreeRef::toposort(const ToposortDirection direction) const
-{
- ToposortResult result;
- result.sorted_nodes.reserve(nodes_by_id_.size());
-
- Array<ToposortNodeState> node_states(nodes_by_id_.size());
-
- for (const NodeRef *node : nodes_by_id_) {
- if (node_states[node->id()].is_done) {
- /* Ignore nodes that are done already. */
- continue;
- }
- if (node->any_socket_is_directly_linked(
- direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) {
- /* Ignore non-start nodes. */
- continue;
- }
-
- toposort_from_start_node(direction, *node, node_states, result);
- }
-
- /* Check if the loop above forgot some nodes because there is a cycle. */
- if (result.sorted_nodes.size() < nodes_by_id_.size()) {
- result.has_cycle = true;
- for (const NodeRef *node : nodes_by_id_) {
- if (node_states[node->id()].is_done) {
- /* Ignore nodes that are done already. */
- continue;
- }
- /* Start toposort at this node which is somewhere in the middle of a loop. */
- toposort_from_start_node(direction, *node, node_states, result);
- }
- }
-
- BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size());
- return result;
-}
-
-const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const
-{
- for (const NodeRef *node : this->nodes_by_type(bnode.typeinfo)) {
- if (node->bnode_ == &bnode) {
- return node;
- }
- }
- return nullptr;
-}
-
-std::string NodeTreeRef::to_dot() const
-{
- dot::DirectedGraph digraph;
- digraph.set_rankdir(dot::Attr_rankdir::LeftToRight);
-
- Map<const NodeRef *, dot::NodeWithSocketsRef> dot_nodes;
-
- for (const NodeRef *node : nodes_by_id_) {
- dot::Node &dot_node = digraph.new_node("");
- dot_node.set_background_color("white");
-
- Vector<std::string> input_names;
- Vector<std::string> output_names;
- for (const InputSocketRef *socket : node->inputs()) {
- input_names.append(socket->name());
- }
- for (const OutputSocketRef *socket : node->outputs()) {
- output_names.append(socket->name());
- }
-
- dot_nodes.add_new(node,
- dot::NodeWithSocketsRef(dot_node, node->name(), input_names, output_names));
- }
-
- for (const OutputSocketRef *from_socket : output_sockets_) {
- for (const InputSocketRef *to_socket : from_socket->directly_linked_sockets()) {
- dot::NodeWithSocketsRef &from_dot_node = dot_nodes.lookup(&from_socket->node());
- dot::NodeWithSocketsRef &to_dot_node = dot_nodes.lookup(&to_socket->node());
-
- digraph.new_edge(from_dot_node.output(from_socket->index()),
- to_dot_node.input(to_socket->index()));
- }
- }
-
- return digraph.to_dot_string();
-}
-
-const NodeTreeRef &get_tree_ref_from_map(NodeTreeRefMap &node_tree_refs, bNodeTree &btree)
-{
- return *node_tree_refs.lookup_or_add_cb(&btree,
- [&]() { return std::make_unique<NodeTreeRef>(&btree); });
-}
-
-PointerRNA NodeRef::rna() const
-{
- PointerRNA rna;
- RNA_pointer_create(&tree_->btree()->id, &RNA_Node, bnode_, &rna);
- return rna;
-}
-
-PointerRNA SocketRef::rna() const
-{
- PointerRNA rna;
- RNA_pointer_create(&this->tree().btree()->id, &RNA_NodeSocket, bsocket_, &rna);
- return rna;
-}
-
-} // namespace blender::nodes
diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c
index e8be093c606..ddab455509d 100644
--- a/source/blender/nodes/intern/node_util.c
+++ b/source/blender/nodes/intern/node_util.c
@@ -200,7 +200,7 @@ void node_math_label(const bNodeTree *UNUSED(ntree), const bNode *node, char *la
if (!enum_label) {
name = "Unknown";
}
- BLI_strncpy(label, IFACE_(name), maxlen);
+ BLI_strncpy(label, CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, name), maxlen);
}
void node_vector_math_label(const bNodeTree *UNUSED(ntree),
diff --git a/source/blender/nodes/shader/CMakeLists.txt b/source/blender/nodes/shader/CMakeLists.txt
index 3e90f185168..e6f90449843 100644
--- a/source/blender/nodes/shader/CMakeLists.txt
+++ b/source/blender/nodes/shader/CMakeLists.txt
@@ -67,6 +67,7 @@ set(SRC
nodes/node_shader_map_range.cc
nodes/node_shader_mapping.cc
nodes/node_shader_math.cc
+ nodes/node_shader_mix.cc
nodes/node_shader_mix_rgb.cc
nodes/node_shader_mix_shader.cc
nodes/node_shader_normal.cc
@@ -131,22 +132,24 @@ set(LIB
bf_nodes
)
-if(WITH_PYTHON)
- list(APPEND INC
- ../../python
- )
+if(WITH_FREESTYLE)
+ add_definitions(-DWITH_FREESTYLE)
+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
- ${PYTHON_INCLUDE_DIRS}
+ ${TBB_INCLUDE_DIRS}
)
+
list(APPEND LIB
- ${PYTHON_LINKFLAGS}
- ${PYTHON_LIBRARIES}
+ ${TBB_LIBRARIES}
)
- add_definitions(-DWITH_PYTHON)
-endif()
-
-if(WITH_FREESTYLE)
- add_definitions(-DWITH_FREESTYLE)
endif()
blender_add_lib(bf_nodes_shader "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc
index 43dbf5060bd..a6d2e954a0c 100644
--- a/source/blender/nodes/shader/node_shader_tree.cc
+++ b/source/blender/nodes/shader/node_shader_tree.cc
@@ -26,6 +26,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_linestyle.h"
#include "BKE_node.h"
@@ -71,7 +72,8 @@ static void shader_get_from_context(const bContext *C,
SpaceNode *snode = CTX_wm_space_node(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
if (snode->shaderfrom == SNODE_SHADER_OBJECT) {
if (ob) {
@@ -166,6 +168,7 @@ void register_node_tree_type_sh()
tt->type = NTREE_SHADER;
strcpy(tt->idname, "ShaderNodeTree");
+ strcpy(tt->group_idname, "ShaderNodeGroup");
strcpy(tt->ui_name, N_("Shader Editor"));
tt->ui_icon = ICON_NODE_MATERIAL;
strcpy(tt->ui_description, N_("Shader nodes"));
@@ -616,7 +619,7 @@ static bool ntree_shader_implicit_closure_cast(bNodeTree *ntree)
bool modified = false;
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
if ((link->fromsock->type != SOCK_SHADER) && (link->tosock->type == SOCK_SHADER)) {
- bNode *emission_node = nodeAddStaticNode(NULL, ntree, SH_NODE_EMISSION);
+ bNode *emission_node = nodeAddStaticNode(nullptr, ntree, SH_NODE_EMISSION);
bNodeSocket *in_sock = ntree_shader_node_find_input(emission_node, "Color");
bNodeSocket *out_sock = ntree_shader_node_find_output(emission_node, "Emission");
nodeAddLink(ntree, link->fromnode, link->fromsock, emission_node, in_sock);
@@ -644,7 +647,7 @@ static void ntree_weight_tree_merge_weight(bNodeTree *ntree,
bNode **tonode,
bNodeSocket **tosock)
{
- bNode *addnode = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ bNode *addnode = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
addnode->custom1 = NODE_MATH_ADD;
addnode->tmp_flag = -2; /* Copy */
bNodeSocket *addsock_out = ntree_shader_node_output_get(addnode, 0);
@@ -682,19 +685,19 @@ static bool ntree_weight_tree_tag_nodes(bNode *fromnode, bNode *tonode, void *us
* with their respective weights. */
static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node)
{
- bNodeLink *displace_link = NULL;
+ bNodeLink *displace_link = nullptr;
bNodeSocket *displace_output = ntree_shader_node_find_input(output_node, "Displacement");
if (displace_output && displace_output->link) {
/* Remove any displacement link to avoid tagging it later on. */
displace_link = displace_output->link;
- displace_output->link = NULL;
+ displace_output->link = nullptr;
}
- bNodeLink *thickness_link = NULL;
+ bNodeLink *thickness_link = nullptr;
bNodeSocket *thickness_output = ntree_shader_node_find_input(output_node, "Thickness");
if (thickness_output && thickness_output->link) {
/* Remove any thickness link to avoid tagging it later on. */
thickness_link = thickness_output->link;
- thickness_output->link = NULL;
+ thickness_output->link = nullptr;
}
/* Init tmp flag. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@@ -716,7 +719,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
case SH_NODE_OUTPUT_WORLD:
case SH_NODE_OUTPUT_MATERIAL: {
/* Start the tree with full weight. */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_VALUE);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_VALUE);
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_output_get(nodes_copy[id], 0)->default_value)
->value = 1.0f;
@@ -725,7 +728,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
case SH_NODE_ADD_SHADER: {
/* Simple passthrough node. Each original inputs will get the same weight. */
/* TODO(fclem): Better use some kind of reroute node? */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
@@ -738,17 +741,17 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
bNodeSocket *fromsock, *tosock;
int id_start = id;
/* output = (factor * input_weight) */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_MULTIPLY;
nodes_copy[id]->tmp_flag = -2; /* Copy */
id++;
/* output = ((1.0 - factor) * input_weight) <=> (input_weight - factor * input_weight) */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_SUBTRACT;
nodes_copy[id]->tmp_flag = -2; /* Copy */
id++;
/* Node sanitizes the input mix factor by clamping it. */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->custom2 = SHD_MATH_CLAMP;
nodes_copy[id]->tmp_flag = -2; /* Copy */
@@ -764,7 +767,7 @@ static void ntree_shader_weight_tree_invert(bNodeTree *ntree, bNode *output_node
id++;
/* Reroute the weight input to the 3 processing nodes. Simplify linking later-on. */
/* TODO(fclem): Better use some kind of reroute node? */
- nodes_copy[id] = nodeAddStaticNode(NULL, ntree, SH_NODE_MATH);
+ nodes_copy[id] = nodeAddStaticNode(nullptr, ntree, SH_NODE_MATH);
nodes_copy[id]->custom1 = NODE_MATH_ADD;
nodes_copy[id]->tmp_flag = -2; /* Copy */
((bNodeSocketValueFloat *)ntree_shader_node_input_get(nodes_copy[id], 0)->default_value)
@@ -1039,7 +1042,7 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree, GPUMaterial *mat)
/* Tree is valid if it contains no undefined implicit socket type cast. */
bool valid_tree = ntree_shader_implicit_closure_cast(localtree);
- if (valid_tree && output != NULL) {
+ if (valid_tree && output != nullptr) {
ntree_shader_pruned_unused(localtree, output);
ntree_shader_shader_to_rgba_branch(localtree, output);
ntree_shader_weight_tree_invert(localtree, output);
diff --git a/source/blender/nodes/shader/node_shader_util.hh b/source/blender/nodes/shader/node_shader_util.hh
index d5f54d9cac9..38220634695 100644
--- a/source/blender/nodes/shader/node_shader_util.hh
+++ b/source/blender/nodes/shader/node_shader_util.hh
@@ -59,6 +59,8 @@
#include "RE_pipeline.h"
#include "RE_texture.h"
+#include "RNA_access.h"
+
bool sh_node_poll_default(struct bNodeType *ntype,
struct bNodeTree *ntree,
const char **r_disabled_hint);
diff --git a/source/blender/nodes/shader/nodes/node_shader_attribute.cc b/source/blender/nodes/shader/nodes/node_shader_attribute.cc
index d01271c15d3..65d053e6379 100644
--- a/source/blender/nodes/shader/nodes/node_shader_attribute.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_attribute.cc
@@ -36,6 +36,7 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
{
NodeShaderAttribute *attr = static_cast<NodeShaderAttribute *>(node->storage);
bool is_varying = attr->type == SHD_ATTRIBUTE_GEOMETRY;
+ float attr_hash = 0.0f;
GPUNodeLink *cd_attr;
@@ -43,7 +44,12 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
cd_attr = GPU_attribute(mat, CD_AUTO_FROM_NAME, attr->name);
}
else {
- cd_attr = GPU_uniform_attribute(mat, attr->name, attr->type == SHD_ATTRIBUTE_INSTANCER);
+ cd_attr = GPU_uniform_attribute(mat,
+ attr->name,
+ attr->type == SHD_ATTRIBUTE_INSTANCER,
+ reinterpret_cast<uint32_t *>(&attr_hash));
+
+ GPU_link(mat, "node_attribute_uniform", cd_attr, GPU_constant(&attr_hash), &cd_attr);
}
if (STREQ(attr->name, "color")) {
@@ -55,9 +61,11 @@ static int node_shader_gpu_attribute(GPUMaterial *mat,
GPU_stack_link(mat, node, "node_attribute", in, out, cd_attr);
- int i;
- LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
- node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
+ if (is_varying) {
+ int i;
+ LISTBASE_FOREACH_INDEX (bNodeSocket *, sock, &node->outputs, i) {
+ node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
+ }
}
return 1;
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
index 6495dcfffba..a0579372a15 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_hair_principled.cc
@@ -42,8 +42,7 @@ static void node_declare(NodeDeclarationBuilder &b)
.min(0.0f)
.max(1.0f)
.subtype(PROP_FACTOR);
- b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f).subtype(
- PROP_FACTOR);
+ b.add_input<decl::Float>(N_("IOR")).default_value(1.55f).min(0.0f).max(1000.0f);
b.add_input<decl::Float>(N_("Offset"))
.default_value(2.0f * ((float)M_PI) / 180.0f)
.min(-M_PI_2)
diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
index a63c7aede04..7b72d4b9be4 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc
@@ -167,6 +167,27 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat,
if (use_transparency) {
flag |= GPU_MATFLAG_TRANSPARENT;
}
+ if (use_clear) {
+ flag |= GPU_MATFLAG_CLEARCOAT;
+ }
+
+ /* Ref. T98190: Defines are optimizations for old compilers.
+ * Might become unnecessary with EEVEE-Next. */
+ if (use_diffuse == false && use_refract == false && use_clear == true) {
+ flag |= GPU_MATFLAG_PRINCIPLED_CLEARCOAT;
+ }
+ else if (use_diffuse == false && use_refract == false && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_METALLIC;
+ }
+ else if (use_diffuse == true && use_refract == false && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_DIELECTRIC;
+ }
+ else if (use_diffuse == false && use_refract == true && use_clear == false) {
+ flag |= GPU_MATFLAG_PRINCIPLED_GLASS;
+ }
+ else {
+ flag |= GPU_MATFLAG_PRINCIPLED_ANY;
+ }
if (use_subsurf) {
bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->original->inputs, 2);
diff --git a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
index 3723480ffa3..d73ffd89288 100644
--- a/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_color_ramp.cc
@@ -125,7 +125,7 @@ class ColorBandFunction : public fn::MultiFunction {
static void sh_node_valtorgb_build_multi_function(nodes::NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
const ColorBand *color_band = (const ColorBand *)bnode.storage;
builder.construct_and_set_matching_fn<ColorBandFunction>(*color_band);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc
index eb47059063d..4725aef5991 100644
--- a/source/blender/nodes/shader/nodes/node_shader_curves.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc
@@ -95,7 +95,7 @@ class CurveVecFunction : public fn::MultiFunction {
static void sh_node_curve_vec_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveVecFunction>(*cumap);
@@ -237,7 +237,7 @@ class CurveRGBFunction : public fn::MultiFunction {
static void sh_node_curve_rgb_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveRGBFunction>(*cumap);
@@ -356,7 +356,7 @@ class CurveFloatFunction : public fn::MultiFunction {
static void sh_node_curve_float_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &bnode = builder.node();
+ const bNode &bnode = builder.node();
CurveMapping *cumap = (CurveMapping *)bnode.storage;
BKE_curvemapping_init(cumap);
builder.construct_and_set_matching_fn<CurveFloatFunction>(*cumap);
diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.cc b/source/blender/nodes/shader/nodes/node_shader_geometry.cc
index 47df932f9d4..d23561de7ff 100644
--- a/source/blender/nodes/shader/nodes/node_shader_geometry.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc
@@ -29,10 +29,9 @@ static int node_shader_gpu_geometry(GPUMaterial *mat,
if (out[5].hasoutput) {
GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC);
}
- /* Opti: don't request orco if not needed. */
+ /* Optimization: don't request orco if not needed. */
const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) :
- GPU_attribute(mat, CD_ORCO, "");
+ GPUNodeLink *orco_link = out[2].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(val);
const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link);
diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
index 11d23e47735..f46556291ce 100644
--- a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc
@@ -23,8 +23,8 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat,
{
/* Length: don't request length if not needed. */
static const float zero = 0;
- GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) :
- GPU_attribute(mat, CD_HAIRLENGTH, "");
+ GPUNodeLink *length_link = out[2].hasoutput ? GPU_attribute(mat, CD_HAIRLENGTH, "") :
+ GPU_constant(&zero);
return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc
index 8a2b18d7d76..bd83f8dac37 100644
--- a/source/blender/nodes/shader/nodes/node_shader_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_math.cc
@@ -61,8 +61,9 @@ static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams &params)
ELEM(item->value, NODE_MATH_COMPARE, NODE_MATH_GREATER_THAN, NODE_MATH_LESS_THAN)) ?
-1 :
weight;
- params.add_item(
- IFACE_(item->name), SocketSearchOp{"Value", (NodeMathOperation)item->value}, gn_weight);
+ params.add_item(CTX_IFACE_(BLT_I18NCONTEXT_ID_NODETREE, item->name),
+ SocketSearchOp{"Value", (NodeMathOperation)item->value},
+ gn_weight);
}
}
}
@@ -101,7 +102,7 @@ static int gpu_shader_math(GPUMaterial *mat,
return 0;
}
-static const fn::MultiFunction *get_base_multi_function(bNode &node)
+static const fn::MultiFunction *get_base_multi_function(const bNode &node)
{
const int mode = node.custom1;
const fn::MultiFunction *base_fn = nullptr;
diff --git a/source/blender/nodes/shader/nodes/node_shader_mix.cc b/source/blender/nodes/shader/nodes/node_shader_mix.cc
new file mode 100644
index 00000000000..f785e32832e
--- /dev/null
+++ b/source/blender/nodes/shader/nodes/node_shader_mix.cc
@@ -0,0 +1,443 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2005 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup shdnodes
+ */
+
+#include <algorithm>
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_shader_util.hh"
+
+#include "NOD_socket_search_link.hh"
+#include "RNA_enum_types.h"
+
+namespace blender::nodes::node_sh_mix_cc {
+
+NODE_STORAGE_FUNCS(NodeShaderMix)
+
+static void sh_node_mix_declare(NodeDeclarationBuilder &b)
+{
+ b.is_function_node();
+ b.add_input<decl::Float>(N_("Factor"), "Factor_Float")
+ .default_value(0.5f)
+ .min(0.0f)
+ .max(1.0f)
+ .subtype(PROP_FACTOR);
+ b.add_input<decl::Vector>(N_("Factor"), "Factor_Vector")
+ .default_value(float3(0.5f))
+ .subtype(PROP_FACTOR);
+
+ b.add_input<decl::Float>(N_("A"), "A_Float")
+ .min(-10000.0f)
+ .max(10000.0f)
+ .is_default_link_socket();
+ b.add_input<decl::Float>(N_("B"), "B_Float").min(-10000.0f).max(10000.0f);
+
+ b.add_input<decl::Vector>(N_("A"), "A_Vector").is_default_link_socket();
+ b.add_input<decl::Vector>(N_("B"), "B_Vector");
+
+ b.add_input<decl::Color>(N_("A"), "A_Color")
+ .default_value({0.5f, 0.5f, 0.5f, 1.0f})
+ .is_default_link_socket();
+ b.add_input<decl::Color>(N_("B"), "B_Color").default_value({0.5f, 0.5f, 0.5f, 1.0f});
+
+ b.add_output<decl::Float>(N_("Result"), "Result_Float");
+ b.add_output<decl::Vector>(N_("Result"), "Result_Vector");
+ b.add_output<decl::Color>(N_("Result"), "Result_Color");
+};
+
+static void sh_node_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ const NodeShaderMix &data = node_storage(*static_cast<const bNode *>(ptr->data));
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+ if (data.data_type == SOCK_VECTOR) {
+ uiItemR(layout, ptr, "factor_mode", 0, "", ICON_NONE);
+ }
+ if (data.data_type == SOCK_RGBA) {
+ uiItemR(layout, ptr, "blend_type", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "clamp_result", 0, nullptr, ICON_NONE);
+ uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
+ }
+ else {
+ uiItemR(layout, ptr, "clamp_factor", 0, nullptr, ICON_NONE);
+ }
+}
+
+static void sh_node_mix_label(const bNodeTree *UNUSED(ntree),
+ const bNode *node,
+ char *label,
+ int maxlen)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ if (storage.data_type == SOCK_RGBA) {
+ const char *name;
+ bool enum_label = RNA_enum_name(rna_enum_ramp_blend_items, storage.blend_type, &name);
+ if (!enum_label) {
+ name = "Unknown";
+ }
+ BLI_strncpy(label, IFACE_(name), maxlen);
+ }
+}
+
+static int sh_node_mix_ui_class(const bNode *node)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type);
+
+ switch (data_type) {
+ case SOCK_VECTOR:
+ return NODE_CLASS_OP_VECTOR;
+ case SOCK_RGBA:
+ return NODE_CLASS_OP_COLOR;
+ default:
+ return NODE_CLASS_CONVERTER;
+ }
+}
+
+static void sh_node_mix_update(bNodeTree *ntree, bNode *node)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const eNodeSocketDatatype data_type = static_cast<eNodeSocketDatatype>(storage.data_type);
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ nodeSetSocketAvailability(ntree, socket, socket->type == data_type);
+ }
+
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
+ nodeSetSocketAvailability(ntree, socket, socket->type == data_type);
+ }
+
+ bool use_vector_factor = data_type == SOCK_VECTOR &&
+ storage.factor_mode != NODE_MIX_MODE_UNIFORM;
+
+ bNodeSocket *sock_factor = (bNodeSocket *)BLI_findlink(&node->inputs, 0);
+ nodeSetSocketAvailability(ntree, sock_factor, !use_vector_factor);
+
+ bNodeSocket *sock_factor_vec = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ nodeSetSocketAvailability(ntree, sock_factor_vec, use_vector_factor);
+}
+
+static void node_mix_gather_link_searches(GatherLinkSearchOpParams &params)
+{
+ const eNodeSocketDatatype sock_type = static_cast<eNodeSocketDatatype>(
+ params.other_socket().type);
+
+ if (ELEM(sock_type, SOCK_BOOLEAN, SOCK_FLOAT, SOCK_RGBA, SOCK_VECTOR, SOCK_INT)) {
+ const eNodeSocketDatatype type = ELEM(sock_type, SOCK_BOOLEAN, SOCK_INT) ? SOCK_FLOAT :
+ sock_type;
+
+ if (params.in_out() == SOCK_OUT) {
+ params.add_item(IFACE_("Result"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "Result");
+ });
+ }
+ else {
+ if (ELEM(sock_type, SOCK_VECTOR, SOCK_RGBA)) {
+ params.add_item(IFACE_("Factor (Non-Uniform)"), [](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = SOCK_VECTOR;
+ node_storage(node).factor_mode = NODE_MIX_MODE_NON_UNIFORM;
+ params.update_and_connect_available_socket(node, "Factor");
+ });
+ }
+ params.add_item(IFACE_("Factor"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "Factor");
+ });
+ params.add_item(IFACE_("A"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "A");
+ });
+ params.add_item(IFACE_("B"), [type](LinkSearchOpParams &params) {
+ bNode &node = params.add_node("ShaderNodeMix");
+ node_storage(node).data_type = type;
+ params.update_and_connect_available_socket(node, "B");
+ });
+ }
+ }
+}
+
+static void node_mix_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeShaderMix *data = MEM_cnew<NodeShaderMix>(__func__);
+ data->data_type = SOCK_FLOAT;
+ data->factor_mode = NODE_MIX_MODE_UNIFORM;
+ data->clamp_factor = 1;
+ data->clamp_result = 0;
+ data->blend_type = MA_RAMP_BLEND;
+ node->storage = data;
+}
+
+static const char *gpu_shader_get_name(eNodeSocketDatatype data_type,
+ const bool non_uniform,
+ const int blend_type)
+{
+ switch (data_type) {
+ case SOCK_FLOAT:
+ return "node_mix_float";
+ case SOCK_VECTOR:
+ return (non_uniform) ? "node_mix_vector_non_uniform" : "node_mix_vector";
+ case SOCK_RGBA:
+ switch (blend_type) {
+ case MA_RAMP_BLEND:
+ return "node_mix_blend";
+ case MA_RAMP_ADD:
+ return "node_mix_add";
+ case MA_RAMP_MULT:
+ return "node_mix_mult";
+ case MA_RAMP_SUB:
+ return "node_mix_sub";
+ case MA_RAMP_SCREEN:
+ return "node_mix_screen";
+ case MA_RAMP_DIV:
+ return "node_mix_div_fallback";
+ case MA_RAMP_DIFF:
+ return "node_mix_diff";
+ case MA_RAMP_DARK:
+ return "node_mix_dark";
+ case MA_RAMP_LIGHT:
+ return "node_mix_light";
+ case MA_RAMP_OVERLAY:
+ return "node_mix_overlay";
+ case MA_RAMP_DODGE:
+ return "node_mix_dodge";
+ case MA_RAMP_BURN:
+ return "node_mix_burn";
+ case MA_RAMP_HUE:
+ return "node_mix_hue";
+ case MA_RAMP_SAT:
+ return "node_mix_sat";
+ case MA_RAMP_VAL:
+ return "node_mix_val";
+ case MA_RAMP_COLOR:
+ return "node_mix_color";
+ case MA_RAMP_SOFT:
+ return "node_mix_soft";
+ case MA_RAMP_LINEAR:
+ return "node_mix_linear";
+ default:
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+ default:
+ BLI_assert_unreachable();
+ return nullptr;
+ }
+}
+
+static int gpu_shader_mix(GPUMaterial *mat,
+ bNode *node,
+ bNodeExecData *UNUSED(execdata),
+ GPUNodeStack *in,
+ GPUNodeStack *out)
+{
+ const NodeShaderMix &storage = node_storage(*node);
+ const bool is_non_uniform = storage.factor_mode == NODE_MIX_MODE_NON_UNIFORM;
+ const bool is_color_mode = storage.data_type == SOCK_RGBA;
+ const bool is_vector_mode = storage.data_type == SOCK_VECTOR;
+ const int blend_type = storage.blend_type;
+ const char *name = gpu_shader_get_name(
+ (eNodeSocketDatatype)storage.data_type, is_non_uniform, blend_type);
+
+ if (name == nullptr) {
+ return 0;
+ }
+
+ if (storage.clamp_factor) {
+ if (is_non_uniform && is_vector_mode) {
+ const float min[3] = {0.0f, 0.0f, 0.0f};
+ const float max[3] = {1.0f, 1.0f, 1.0f};
+ const GPUNodeLink *factor_link = in[1].link ? in[1].link : GPU_uniform(in[1].vec);
+ GPU_link(mat,
+ "node_mix_clamp_vector",
+ factor_link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &in[1].link);
+ }
+ else {
+ const float min = 0.0f;
+ const float max = 1.0f;
+ const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
+ GPU_link(mat,
+ "node_mix_clamp_value",
+ factor_link,
+ GPU_constant(&min),
+ GPU_constant(&max),
+ &in[0].link);
+ }
+ }
+
+ int ret = GPU_stack_link(mat, node, name, in, out);
+
+ if (ret && is_color_mode && storage.clamp_result) {
+ const float min[3] = {0.0f, 0.0f, 0.0f};
+ const float max[3] = {1.0f, 1.0f, 1.0f};
+ GPU_link(mat,
+ "node_mix_clamp_vector",
+ out[2].link,
+ GPU_constant(min),
+ GPU_constant(max),
+ &out[2].link);
+ }
+ return ret;
+}
+
+class MixColorFunction : public fn::MultiFunction {
+ private:
+ const bool clamp_factor_;
+ const bool clamp_result_;
+ const int blend_type_;
+
+ public:
+ MixColorFunction(const bool clamp_factor, const bool clamp_result, const int blend_type)
+ : clamp_factor_(clamp_factor), clamp_result_(clamp_result), blend_type_(blend_type)
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"MixColor"};
+ signature.single_input<float>("Factor");
+ signature.single_input<ColorGeometry4f>("A");
+ signature.single_input<ColorGeometry4f>("B");
+ signature.single_output<ColorGeometry4f>("Result");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float> &fac = params.readonly_single_input<float>(0, "Factor");
+ const VArray<ColorGeometry4f> &col1 = params.readonly_single_input<ColorGeometry4f>(1, "A");
+ const VArray<ColorGeometry4f> &col2 = params.readonly_single_input<ColorGeometry4f>(2, "B");
+ MutableSpan<ColorGeometry4f> results = params.uninitialized_single_output<ColorGeometry4f>(
+ 3, "Result");
+
+ if (clamp_factor_) {
+ for (int64_t i : mask) {
+ results[i] = col1[i];
+ ramp_blend(blend_type_, results[i], std::clamp(fac[i], 0.0f, 1.0f), col2[i]);
+ }
+ }
+ else {
+ for (int64_t i : mask) {
+ results[i] = col1[i];
+ ramp_blend(blend_type_, results[i], fac[i], col2[i]);
+ }
+ }
+
+ if (clamp_result_) {
+ for (int64_t i : mask) {
+ clamp_v3(results[i], 0.0f, 1.0f);
+ }
+ }
+ }
+};
+
+static const fn::MultiFunction *get_multi_function(const bNode &node)
+{
+ const NodeShaderMix *data = (NodeShaderMix *)node.storage;
+ bool uniform_factor = data->factor_mode == NODE_MIX_MODE_UNIFORM;
+ const bool clamp_factor = data->clamp_factor;
+ switch (data->data_type) {
+ case SOCK_FLOAT: {
+ if (clamp_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ "Clamp Mix Float", [](float t, const float a, const float b) {
+ return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float, float, float, float> fn{
+ "Mix Float", [](const float t, const float a, const float b) {
+ return math::interpolate(a, b, t);
+ }};
+ return &fn;
+ }
+ }
+ case SOCK_VECTOR: {
+ if (clamp_factor) {
+ if (uniform_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
+ "Clamp Mix Vector", [](const float t, const float3 a, const float3 b) {
+ return math::interpolate(a, b, std::clamp(t, 0.0f, 1.0f));
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ "Clamp Mix Vector Non Uniform", [](float3 t, const float3 a, const float3 b) {
+ t = math::clamp(t, 0.0f, 1.0f);
+ return a * (float3(1.0f) - t) + b * t;
+ }};
+ return &fn;
+ }
+ }
+ else {
+ if (uniform_factor) {
+ static fn::CustomMF_SI_SI_SI_SO<float, float3, float3, float3> fn{
+ "Mix Vector", [](const float t, const float3 a, const float3 b) {
+ return math::interpolate(a, b, t);
+ }};
+ return &fn;
+ }
+ else {
+ static fn::CustomMF_SI_SI_SI_SO<float3, float3, float3, float3> fn{
+ "Mix Vector Non Uniform", [](const float3 t, const float3 a, const float3 b) {
+ return a * (float3(1.0f) - t) + b * t;
+ }};
+ return &fn;
+ }
+ }
+ }
+ }
+ BLI_assert_unreachable();
+ return nullptr;
+}
+
+static void sh_node_mix_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ const NodeShaderMix &storage = node_storage(builder.node());
+
+ if (storage.data_type == SOCK_RGBA) {
+ builder.construct_and_set_matching_fn<MixColorFunction>(
+ storage.clamp_factor, storage.clamp_result, storage.blend_type);
+ }
+ else {
+ const fn::MultiFunction *fn = get_multi_function(builder.node());
+ builder.set_matching_fn(fn);
+ }
+}
+
+} // namespace blender::nodes::node_sh_mix_cc
+
+void register_node_type_sh_mix()
+{
+ namespace file_ns = blender::nodes::node_sh_mix_cc;
+
+ static bNodeType ntype;
+ sh_fn_node_type_base(&ntype, SH_NODE_MIX, "Mix", NODE_CLASS_CONVERTER);
+ ntype.declare = file_ns::sh_node_mix_declare;
+ ntype.ui_class = file_ns::sh_node_mix_ui_class;
+ node_type_gpu(&ntype, file_ns::gpu_shader_mix);
+ node_type_update(&ntype, file_ns::sh_node_mix_update);
+ node_type_init(&ntype, file_ns::node_mix_init);
+ node_type_storage(
+ &ntype, "NodeShaderMix", node_free_standard_storage, node_copy_standard_storage);
+ ntype.build_multi_function = file_ns::sh_node_mix_build_multi_function;
+ ntype.draw_buttons = file_ns::sh_node_mix_layout;
+ ntype.labelfunc = file_ns::sh_node_mix_label;
+ ntype.gather_link_search_ops = file_ns::node_mix_gather_link_searches;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
index 12707623049..46ac8f05803 100644
--- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc
@@ -32,7 +32,7 @@ static const char *gpu_shader_get_name(int mode)
case MA_RAMP_SCREEN:
return "mix_screen";
case MA_RAMP_DIV:
- return "mix_div";
+ return "mix_div_fallback";
case MA_RAMP_DIFF:
return "mix_diff";
case MA_RAMP_DARK:
@@ -70,18 +70,23 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat,
{
const char *name = gpu_shader_get_name(node->custom1);
- 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};
- GPU_link(
- mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
- }
- return ret;
+ if (name == nullptr) {
+ return 0;
}
- return 0;
+ const float min = 0.0f;
+ const float max = 1.0f;
+ const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec);
+ GPU_link(mat, "clamp_value", factor_link, GPU_constant(&min), GPU_constant(&max), &in[0].link);
+
+ 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};
+ GPU_link(mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link);
+ }
+ return ret;
}
class MixRGBFunction : public fn::MultiFunction {
@@ -131,7 +136,7 @@ class MixRGBFunction : public fn::MultiFunction {
static void sh_node_mix_rgb_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
bool clamp = node.custom2 & SHD_MIXRGB_CLAMP;
int mix_type = node.custom1;
builder.construct_and_set_matching_fn<MixRGBFunction>(clamp, mix_type);
@@ -145,11 +150,11 @@ void register_node_type_sh_mix_rgb()
static bNodeType ntype;
- sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR);
+ sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB_LEGACY, "Mix", NODE_CLASS_OP_COLOR);
ntype.declare = file_ns::sh_node_mix_rgb_declare;
ntype.labelfunc = node_blend_label;
node_type_gpu(&ntype, file_ns::gpu_shader_mix_rgb);
ntype.build_multi_function = file_ns::sh_node_mix_rgb_build_multi_function;
-
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_rgb.cc
index 38acfab322f..3d28f5278a2 100644
--- a/source/blender/nodes/shader/nodes/node_shader_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_rgb.cc
@@ -17,11 +17,12 @@ static void node_declare(NodeDeclarationBuilder &b)
static int gpu_shader_rgb(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
+ GPUNodeStack * /*in*/,
GPUNodeStack *out)
{
- GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0);
- return GPU_stack_link(mat, node, "set_rgba", in, out, link);
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first);
+ float *value = static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value;
+ return GPU_link(mat, "set_rgba", GPU_uniform(value), &out->link);
}
} // namespace blender::nodes::node_shader_rgb_cc
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
index 6dfabe48292..1c02c5ba074 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_hsv.cc
@@ -39,6 +39,7 @@ void register_node_type_sh_sephsv()
sh_node_type_base(&ntype, SH_NODE_SEPHSV_LEGACY, "Separate HSV", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::node_declare_sephsv;
node_type_gpu(&ntype, file_ns::gpu_shader_sephsv);
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
@@ -75,6 +76,7 @@ void register_node_type_sh_combhsv()
sh_node_type_base(&ntype, SH_NODE_COMBHSV_LEGACY, "Combine HSV", NODE_CLASS_CONVERTER);
ntype.declare = file_ns::node_declare_combhsv;
node_type_gpu(&ntype, file_ns::gpu_shader_combhsv);
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
index 28b55047633..3fe76b05b5d 100644
--- a/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_sepcomb_rgb.cc
@@ -80,6 +80,7 @@ void register_node_type_sh_seprgb()
ntype.declare = file_ns::sh_node_seprgb_declare;
node_type_gpu(&ntype, file_ns::gpu_shader_seprgb);
ntype.build_multi_function = file_ns::sh_node_seprgb_build_multi_function;
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
@@ -123,6 +124,7 @@ void register_node_type_sh_combrgb()
ntype.declare = file_ns::sh_node_combrgb_declare;
node_type_gpu(&ntype, file_ns::gpu_shader_combrgb);
ntype.build_multi_function = file_ns::sh_node_combrgb_build_multi_function;
+ ntype.gather_link_search_ops = nullptr;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
index cad9e1b33f2..a1c51a440d0 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc
@@ -261,7 +261,7 @@ class BrickFunction : public fn::MultiFunction {
static void sh_node_brick_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexBrick *tex = (NodeTexBrick *)node.storage;
builder.construct_and_set_matching_fn<BrickFunction>(
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
index fb5971021fc..9727a6089a6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc
@@ -38,12 +38,12 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat,
/* Use special matrix to let the shader branch to using the render object's matrix. */
float dummy_matrix[4][4];
dummy_matrix[3][3] = 0.0f;
- GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) :
- GPU_uniform(&dummy_matrix[0][0]);
+ GPUNodeLink *inv_obmat = (ob != nullptr) ? GPU_uniform(&ob->imat[0][0]) :
+ GPU_uniform(&dummy_matrix[0][0]);
- /* Opti: don't request orco if not needed. */
+ /* Optimization: don't request orco if not needed. */
float4 zero(0.0f);
- GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(zero) : GPU_attribute(mat, CD_ORCO, "");
+ GPUNodeLink *orco = out[0].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(zero);
GPUNodeLink *mtface = GPU_attribute(mat, CD_AUTO_FROM_NAME, "");
GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
index df5fbac3ab5..2739a75ed21 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.cc
@@ -50,7 +50,11 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
return GPU_stack_link(mat, node, "node_tex_environment_empty", in, out);
}
- node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
+ if (!in[0].link) {
+ GPU_link(mat, "node_tex_coord_position", &in[0].link);
+ node_shader_gpu_bump_tex_coord(mat, node, &in[0].link);
+ }
+
node_shader_gpu_tex_mapping(mat, node, in, out);
/* Compute texture coordinate. */
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
index 8478cbd406b..37c72ec1f17 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc
@@ -139,7 +139,7 @@ class GradientFunction : public fn::MultiFunction {
static void sh_node_gradient_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexGradient *tex = (NodeTexGradient *)node.storage;
builder.construct_and_set_matching_fn<GradientFunction>(tex->gradient_type);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
index 95c4a8b8e46..205d3b89016 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc
@@ -161,7 +161,7 @@ class MagicFunction : public fn::MultiFunction {
static void sh_node_magic_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexMagic *tex = (NodeTexMagic *)node.storage;
builder.construct_and_set_matching_fn<MagicFunction>(tex->depth);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
index c13ce3c3df3..a2241c2327f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc
@@ -516,7 +516,7 @@ class MusgraveFunction : public fn::MultiFunction {
static void sh_node_musgrave_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage;
builder.construct_and_set_matching_fn<MusgraveFunction>(tex->dimensions, tex->musgrave_type);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc
index f5a4d087dbd..97b7f2616ae 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_sky.cc
@@ -4,6 +4,8 @@
#include "node_shader_util.hh"
#include "sky_model.h"
+#include "BLI_task.hh"
+
#include "BKE_context.h"
#include "BKE_scene.h"
@@ -36,7 +38,7 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *C, PointerRNA *
if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) {
Scene *scene = CTX_data_scene(C);
if (BKE_scene_uses_blender_eevee(scene)) {
- uiItemL(layout, TIP_("Nishita not available in Eevee"), ICON_ERROR);
+ uiItemL(layout, TIP_("Sun disc not available in Eevee"), ICON_ERROR);
}
uiItemR(layout, ptr, "sun_disc", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, 0);
@@ -179,7 +181,7 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat,
GPU_uniform(xyz_to_rgb.g),
GPU_uniform(xyz_to_rgb.b));
}
- if (tex->sky_model == 1) {
+ else if (tex->sky_model == 1) {
/* Hosek / Wilkie */
sun_angles[0] = fmin(M_PI_2, sun_angles[0]); /* clamp to horizon */
SKY_ArHosekSkyModelState *sky_state = SKY_arhosek_xyz_skymodelstate_alloc_init(
@@ -219,8 +221,52 @@ static int node_shader_gpu_tex_sky(GPUMaterial *mat,
GPU_uniform(xyz_to_rgb.g),
GPU_uniform(xyz_to_rgb.b));
}
+ else {
+ /* Nishita */
+
+ Array<float> pixels(4 * GPU_SKY_WIDTH * GPU_SKY_HEIGHT);
+
+ threading::parallel_for(IndexRange(GPU_SKY_HEIGHT), 2, [&](IndexRange range) {
+ SKY_nishita_skymodel_precompute_texture(pixels.data(),
+ 4,
+ range.first(),
+ range.one_after_last(),
+ GPU_SKY_WIDTH,
+ GPU_SKY_HEIGHT,
+ tex->sun_elevation,
+ tex->altitude,
+ tex->air_density,
+ tex->dust_density,
+ tex->ozone_density);
+ });
+
+ float sun_rotation = fmodf(tex->sun_rotation, 2.0f * M_PI);
+ if (sun_rotation < 0.0f) {
+ sun_rotation += 2.0f * M_PI;
+ }
+ sun_rotation = 2.0f * M_PI - sun_rotation;
+
+ XYZ_to_RGB xyz_to_rgb;
+ get_XYZ_to_RGB_for_gpu(&xyz_to_rgb);
- return GPU_stack_link(mat, node, "node_tex_sky_nishita", in, out);
+ eGPUSamplerState sampler = GPU_SAMPLER_REPEAT | GPU_SAMPLER_FILTER;
+ /* To fix pole issue we clamp the v coordinate. */
+ sampler &= ~GPU_SAMPLER_REPEAT_T;
+ float layer;
+ GPUNodeLink *sky_texture = GPU_image_sky(
+ mat, GPU_SKY_WIDTH, GPU_SKY_HEIGHT, pixels.data(), &layer, sampler);
+ return GPU_stack_link(mat,
+ node,
+ "node_tex_sky_nishita",
+ in,
+ out,
+ GPU_constant(&sun_rotation),
+ GPU_uniform(xyz_to_rgb.r),
+ GPU_uniform(xyz_to_rgb.g),
+ GPU_uniform(xyz_to_rgb.b),
+ sky_texture,
+ GPU_constant(&layer));
+ }
}
static void node_shader_update_sky(bNodeTree *ntree, bNode *node)
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
index ad24224dc7f..8475101dbaf 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc
@@ -206,7 +206,7 @@ class WaveFunction : public fn::MultiFunction {
static void sh_node_wave_tex_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
NodeTexWave *tex = (NodeTexWave *)node.storage;
builder.construct_and_set_matching_fn<WaveFunction>(
tex->wave_type, tex->bands_direction, tex->rings_direction, tex->wave_profile);
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
index 6d4c491046b..64075a903ab 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc
@@ -176,7 +176,7 @@ class WhiteNoiseFunction : public fn::MultiFunction {
static void sh_node_noise_build_multi_function(NodeMultiFunctionBuilder &builder)
{
- bNode &node = builder.node();
+ const bNode &node = builder.node();
builder.construct_and_set_matching_fn<WhiteNoiseFunction>((int)node.custom1);
}
diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc
index 362cdf58052..14dbb3b25eb 100644
--- a/source/blender/nodes/shader/nodes/node_shader_value.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_value.cc
@@ -17,11 +17,12 @@ static void sh_node_value_declare(NodeDeclarationBuilder &b)
static int gpu_shader_value(GPUMaterial *mat,
bNode *node,
bNodeExecData *UNUSED(execdata),
- GPUNodeStack *in,
+ GPUNodeStack * /*in*/,
GPUNodeStack *out)
{
- GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0);
- return GPU_stack_link(mat, node, "set_value", in, out, link);
+ const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first);
+ float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value;
+ return GPU_link(mat, "set_value", GPU_uniform(&value), &out->link);
}
static void sh_node_value_build_multi_function(NodeMultiFunctionBuilder &builder)
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 21f5c44c640..d01e03f9d71 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc
@@ -225,7 +225,7 @@ static void node_shader_update_vector_math(bNodeTree *ntree, bNode *node)
}
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
NodeVectorMathOperation operation = NodeVectorMathOperation(node.custom1);
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
index b35f686e331..a036fc52d84 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -96,7 +96,7 @@ static float3 sh_node_vector_rotate_euler(const float3 &vector,
return result + center;
}
-static const fn::MultiFunction *get_multi_function(bNode &node)
+static const fn::MultiFunction *get_multi_function(const bNode &node)
{
bool invert = node.custom2;
const int mode = node.custom1;
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
index de588f9005f..159bd238212 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_transform.cc
@@ -83,7 +83,7 @@ static const char *get_gpufn_name_from_to(short from, short to, bool is_directio
}
break;
}
- return NULL;
+ return nullptr;
}
static int gpu_shader_vect_transform(GPUMaterial *mat,
diff --git a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc
index 830f02d8df1..c636cc976e6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vertex_color.cc
@@ -42,9 +42,8 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat,
GPUNodeStack *out)
{
NodeShaderVertexColor *vertexColor = (NodeShaderVertexColor *)node->storage;
- /* NOTE: using CD_AUTO_FROM_NAME instead of CD_MCOL or CD_PROP_COLOR for named attributes
- * as geometry nodes may overwrite data which will also change the eCustomDataType.
- * This will also make EEVEE and Cycles
+ /* NOTE: Using #CD_AUTO_FROM_NAME is necessary because there are multiple color attribute types,
+ * and the type may change during evaluation anyway. This will also make EEVEE and Cycles
* consistent. See T93179. */
GPUNodeLink *vertexColorLink;
@@ -53,7 +52,7 @@ static int node_shader_gpu_vertex_color(GPUMaterial *mat,
vertexColorLink = GPU_attribute(mat, CD_AUTO_FROM_NAME, vertexColor->layer_name);
}
else { /* Fall back on active render color attribute. */
- vertexColorLink = GPU_attribute(mat, CD_MCOL, vertexColor->layer_name);
+ vertexColorLink = GPU_attribute_default_color(mat);
}
return GPU_stack_link(mat, node, "node_vertex_color", in, out, vertexColorLink);
diff --git a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
index a202312f8d8..1f31e9c605f 100644
--- a/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_volume_info.cc
@@ -25,9 +25,11 @@ static int node_shader_gpu_volume_info(GPUMaterial *mat,
}
if (out[1].hasoutput) {
out[1].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "density");
+ GPU_link(mat, "node_attribute_density", out[1].link, &out[1].link);
}
if (out[2].hasoutput) {
out[2].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "flame");
+ GPU_link(mat, "node_attribute_flame", out[2].link, &out[2].link);
}
if (out[3].hasoutput) {
out[3].link = GPU_attribute(mat, CD_AUTO_FROM_NAME, "temperature");
diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt
index 5bed54ebfd7..77db71d4b1a 100644
--- a/source/blender/nodes/texture/CMakeLists.txt
+++ b/source/blender/nodes/texture/CMakeLists.txt
@@ -15,6 +15,7 @@ set(INC
../../render
../../windowmanager
../../../../intern/guardedalloc
+ ../../bmesh
# RNA_prototypes.h
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
@@ -56,21 +57,6 @@ set(LIB
bf_nodes
)
-if(WITH_PYTHON)
- list(APPEND INC
- ../../python
- )
- list(APPEND INC_SYS
- ${PYTHON_INCLUDE_DIRS}
- )
- list(APPEND LIB
- ${PYTHON_LINKFLAGS}
- ${PYTHON_LIBRARIES}
- )
- add_definitions(-DWITH_PYTHON)
-endif()
-
-
blender_add_lib(bf_nodes_texture "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# RNA_prototypes.h
diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c
index 03dc61af9a2..81d0b0fbc84 100644
--- a/source/blender/nodes/texture/node_texture_tree.c
+++ b/source/blender/nodes/texture/node_texture_tree.c
@@ -18,6 +18,7 @@
#include "BLT_translation.h"
#include "BKE_context.h"
+#include "BKE_layer.h"
#include "BKE_linestyle.h"
#include "BKE_node.h"
#include "BKE_paint.h"
@@ -46,7 +47,8 @@ static void texture_get_from_context(const bContext *C,
SpaceNode *snode = CTX_wm_space_node(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
- Object *ob = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *ob = BKE_view_layer_active_object_get(view_layer);
Tex *tx = NULL;
if (snode->texfrom == SNODE_TEX_BRUSH) {
@@ -140,6 +142,7 @@ void register_node_tree_type_tex(void)
tt->type = NTREE_TEXTURE;
strcpy(tt->idname, "TextureNodeTree");
+ strcpy(tt->group_idname, "TextureNodeGroup");
strcpy(tt->ui_name, N_("Texture Node Editor"));
tt->ui_icon = ICON_NODE_TEXTURE; /* Defined in `drawnode.c`. */
strcpy(tt->ui_description, N_("Texture nodes"));
@@ -248,7 +251,7 @@ bNodeTreeExec *ntreeTexBeginExecTree(bNodeTree *ntree)
exec = ntreeTexBeginExecTree_internal(&context, ntree, NODE_INSTANCE_KEY_BASE);
- /* XXX this should not be necessary, but is still used for cmp/sha/tex nodes,
+ /* XXX this should not be necessary, but is still used for compositor/shading/texture nodes,
* which only store the ntree pointer. Should be fixed at some point!
*/
ntree->execdata = exec;
diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h
index 8075e4ecd22..aecefa97423 100644
--- a/source/blender/python/BPY_extern.h
+++ b/source/blender/python/BPY_extern.h
@@ -34,7 +34,7 @@ void BPY_pyconstraint_exec(struct bPythonConstraint *con,
// void BPY_pyconstraint_settings(void *arg1, void *arg2);
void BPY_pyconstraint_target(struct bPythonConstraint *con, struct bConstraintTarget *ct);
void BPY_pyconstraint_update(struct Object *owner, struct bConstraint *con);
-int BPY_is_pyconstraint(struct Text *text);
+bool BPY_is_pyconstraint(struct Text *text);
// void BPY_free_pyconstraint_links(struct Text *text);
/* global interpreter lock */
diff --git a/source/blender/python/CMakeLists.txt b/source/blender/python/CMakeLists.txt
index 9f6ee359d67..f82499ba1d4 100644
--- a/source/blender/python/CMakeLists.txt
+++ b/source/blender/python/CMakeLists.txt
@@ -1,5 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-or-later
+if(WITH_PYTHON_MODULE)
+ add_definitions(-DPy_ENABLE_SHARED)
+endif()
+
add_subdirectory(intern)
add_subdirectory(generic)
add_subdirectory(gpu)
diff --git a/source/blender/python/bmesh/bmesh_py_api.c b/source/blender/python/bmesh/bmesh_py_api.c
index 3ddab4bebd9..2e6d1698da9 100644
--- a/source/blender/python/bmesh/bmesh_py_api.c
+++ b/source/blender/python/bmesh/bmesh_py_api.c
@@ -160,7 +160,7 @@ static struct PyModuleDef BPy_BM_module_def = {
BPy_BM_doc, /* m_doc */
0, /* m_size */
BPy_BM_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/bmesh/bmesh_py_geometry.c b/source/blender/python/bmesh/bmesh_py_geometry.c
index 5625812fbda..f2af8599807 100644
--- a/source/blender/python/bmesh/bmesh_py_geometry.c
+++ b/source/blender/python/bmesh/bmesh_py_geometry.c
@@ -66,7 +66,7 @@ static struct PyModuleDef BPy_BM_geometry_module_def = {
BPy_BM_utils_doc, /* m_doc */
0, /* m_size */
BPy_BM_geometry_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/bmesh/bmesh_py_ops.c b/source/blender/python/bmesh/bmesh_py_ops.c
index 8b343b00342..37e2b009f55 100644
--- a/source/blender/python/bmesh/bmesh_py_ops.c
+++ b/source/blender/python/bmesh/bmesh_py_ops.c
@@ -267,7 +267,7 @@ static struct PyModuleDef BPy_BM_ops_module_def = {
BPy_BM_ops_doc, /* m_doc */
0, /* m_size */
BPy_BM_ops_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c
index 972a782d650..364adb5458b 100644
--- a/source/blender/python/bmesh/bmesh_py_types.c
+++ b/source/blender/python/bmesh/bmesh_py_types.c
@@ -3740,7 +3740,7 @@ static struct PyModuleDef BPy_BM_types_module_def = {
NULL, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
@@ -4273,10 +4273,10 @@ char *BPy_BMElem_StringFromHType(const char htype)
/* -------------------------------------------------------------------- */
/* keep at bottom */
-/* this function is called on free, it should stay quite fast */
+/* This function is called on free, it should stay quite fast */
static void bm_dealloc_editmode_warn(BPy_BMesh *self)
{
if (self->flag & BPY_BMFLAG_IS_WRAPPED) {
- /* currently nop - this works without warnings now */
+ /* Currently NOP - this works without warnings now. */
}
}
diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
index d4455fd3668..9f200734786 100644
--- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
@@ -298,7 +298,7 @@ static int mathutils_bmloopcol_set(BaseMathObject *bmo, int UNUSED(subtype))
static int mathutils_bmloopcol_get_index(BaseMathObject *bmo, int subtype, int UNUSED(index))
{
- /* lazy, avoid repeteing the case statement */
+ /* Lazy, avoid repeating the case statement. */
if (mathutils_bmloopcol_get(bmo, subtype) == -1) {
return -1;
}
@@ -309,7 +309,7 @@ static int mathutils_bmloopcol_set_index(BaseMathObject *bmo, int subtype, int i
{
const float f = bmo->data[index];
- /* lazy, avoid repeteing the case statement */
+ /* Lazy, avoid repeating the case statement. */
if (mathutils_bmloopcol_get(bmo, subtype) == -1) {
return -1;
}
diff --git a/source/blender/python/bmesh/bmesh_py_utils.c b/source/blender/python/bmesh/bmesh_py_utils.c
index 1bee1342a07..6630eb4924e 100644
--- a/source/blender/python/bmesh/bmesh_py_utils.c
+++ b/source/blender/python/bmesh/bmesh_py_utils.c
@@ -822,7 +822,7 @@ static struct PyModuleDef BPy_BM_utils_module_def = {
BPy_BM_utils_doc, /* m_doc */
0, /* m_size */
BPy_BM_utils_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt
index 69bcfdfae4e..27b7ad28943 100644
--- a/source/blender/python/generic/CMakeLists.txt
+++ b/source/blender/python/generic/CMakeLists.txt
@@ -7,12 +7,11 @@ set(INC
../../gpu
../../makesdna
../../makesrna
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
)
set(INC_SYS
- ${GLEW_INCLUDE_PATH}
+ ${Epoxy_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
)
@@ -41,11 +40,9 @@ set(SRC
)
set(LIB
- ${GLEW_LIBRARY}
+ ${Epoxy_LIBRARIES}
${PYTHON_LINKFLAGS}
${PYTHON_LIBRARIES}
)
-add_definitions(${GL_DEFINITIONS})
-
blender_add_lib(bf_python_ext "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c
index 92ed0c51b1f..36ab1e86d92 100644
--- a/source/blender/python/generic/bgl.c
+++ b/source/blender/python/generic/bgl.c
@@ -19,7 +19,7 @@
#include "../generic/py_capi_utils.h"
-#include "glew-mx.h"
+#include <epoxy/gl.h>
#include "bgl.h"
@@ -1082,18 +1082,34 @@ static PyObject *Buffer_repr(Buffer *self)
/** \name OpenGL API Wrapping
* \{ */
-#define BGL_Wrap(funcname, ret, arg_list) \
- static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \
- { \
- arg_def arg_list; \
- ret_def_##ret; \
- if (!PyArg_ParseTuple(args, arg_str arg_list, arg_ref arg_list)) { \
+#ifdef WITH_OPENGL
+# define BGL_Wrap(funcname, ret, arg_list) \
+ static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \
+ { \
+ arg_def arg_list; \
+ ret_def_##ret; \
+ if (!PyArg_ParseTuple(args, arg_str arg_list, arg_ref arg_list)) { \
+ return NULL; \
+ } \
+ GPU_bgl_start(); \
+ ret_set_##ret gl##funcname(arg_var arg_list); \
+ ret_ret_##ret; \
+ }
+#else
+
+static void bgl_no_opengl_error(void)
+{
+ PyErr_SetString(PyExc_RuntimeError, "Built without OpenGL support");
+}
+
+# define BGL_Wrap(funcname, ret, arg_list) \
+ static PyObject *Method_##funcname(PyObject *UNUSED(self), PyObject *args) \
+ { \
+ (void)args; \
+ bgl_no_opengl_error(); \
return NULL; \
- } \
- GPU_bgl_start(); \
- ret_set_##ret gl##funcname(arg_var arg_list); \
- ret_ret_##ret; \
- }
+ }
+#endif
/* GL_VERSION_1_0 */
BGL_Wrap(BlendFunc, void, (GLenum, GLenum));
@@ -1381,7 +1397,7 @@ static struct PyModuleDef BGL_module_def = {
NULL, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
@@ -1421,12 +1437,22 @@ static void py_module_dict_add_method(PyObject *submodule,
#ifdef __GNUC__
# pragma GCC diagnostic ignored "-Waddress"
#endif
-#define PY_MOD_ADD_METHOD(func) \
- { \
- static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \
- py_module_dict_add_method(submodule, dict, &method_def, (gl##func != NULL)); \
- } \
- ((void)0)
+
+#ifdef WITH_OPENGL
+# define PY_MOD_ADD_METHOD(func) \
+ { \
+ static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \
+ py_module_dict_add_method(submodule, dict, &method_def, (gl##func != NULL)); \
+ } \
+ ((void)0)
+#else
+# define PY_MOD_ADD_METHOD(func) \
+ { \
+ static PyMethodDef method_def = {"gl" #func, Method_##func, METH_VARARGS}; \
+ py_module_dict_add_method(submodule, dict, &method_def, false); \
+ } \
+ ((void)0)
+#endif
static void init_bgl_version_1_0_methods(PyObject *submodule, PyObject *dict)
{
@@ -2620,9 +2646,13 @@ static PyObject *Method_ShaderSource(PyObject *UNUSED(self), PyObject *args)
return NULL;
}
+#ifdef WITH_OPENGL
glShaderSource(shader, 1, (const char **)&source, NULL);
-
Py_RETURN_NONE;
+#else
+ bgl_no_opengl_error();
+ return NULL;
+#endif
}
/** \} */
diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c
index 0f0a2bf0ed1..19958a99df9 100644
--- a/source/blender/python/generic/bl_math_py_api.c
+++ b/source/blender/python/generic/bl_math_py_api.c
@@ -133,7 +133,7 @@ static struct PyModuleDef M_bl_math_module_def = {
M_bl_math_doc, /* m_doc */
0, /* m_size */
M_bl_math_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/generic/blf_py_api.c b/source/blender/python/generic/blf_py_api.c
index 060b7758ea9..11b71256327 100644
--- a/source/blender/python/generic/blf_py_api.c
+++ b/source/blender/python/generic/blf_py_api.c
@@ -465,7 +465,7 @@ static struct PyModuleDef BLF_module_def = {
BLF_doc, /* m_doc */
0, /* m_size */
BLF_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 3f880da2f56..333ab9487d1 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -2119,7 +2119,7 @@ static struct PyModuleDef IDProp_types_module_def = {
NULL, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
@@ -2168,7 +2168,7 @@ static struct PyModuleDef IDProp_module_def = {
IDProp_module_doc, /* m_doc */
0, /* m_size */
IDProp_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c
index e6d90c46866..056af83cc49 100644
--- a/source/blender/python/generic/imbuf_py_api.c
+++ b/source/blender/python/generic/imbuf_py_api.c
@@ -570,7 +570,7 @@ static struct PyModuleDef IMB_module_def = {
IMB_doc, /* m_doc */
0, /* m_size */
IMB_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
@@ -614,7 +614,7 @@ static struct PyModuleDef IMB_types_module_def = {
IMB_types_doc, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 8230801e184..681c5404ed9 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -28,8 +28,8 @@
# include "BLI_string.h"
-/* Only for BLI_strncpy_wchar_from_utf8,
- * should replace with py funcs but too late in release now. */
+/* Only for #BLI_strncpy_wchar_from_utf8,
+ * should replace with Python functions but too late in release now. */
# include "BLI_string_utf8.h"
#endif
@@ -823,6 +823,36 @@ void PyC_Err_PrintWithFunc(PyObject *py_func)
/** \name Exception Buffer Access
* \{ */
+static void pyc_exception_buffer_handle_system_exit(PyObject *error_type,
+ PyObject *error_value,
+ PyObject *error_traceback)
+{
+ if (!PyErr_GivenExceptionMatches(error_type, PyExc_SystemExit)) {
+ return;
+ }
+ /* Inspecting, follow Python's logic in #_Py_HandleSystemExit & treat as a regular exception. */
+ if (_Py_GetConfig()->inspect) {
+ return;
+ }
+
+ /* NOTE(@campbellbarton): A `SystemExit` exception will exit immediately (unless inspecting).
+ * So print the error and exit now. This is necessary as the call to #PyErr_Print exits,
+ * the temporary `sys.stderr` assignment causes the output to be suppressed, failing silently.
+ * Instead, restore the error and print it. If Python changes it's behavior and doesn't exit in
+ * the future - continue to create the exception buffer, see: T99966.
+ *
+ * Arguably accessing a `SystemExit` exception as a buffer should be supported without exiting.
+ * (by temporarily enabling inspection for example) however - it's not obvious exactly when this
+ * should be enabled and complicates the Python API by introducing different kinds of execution.
+ * Since the rule of thumb is for Blender's embedded Python to match stand-alone Python,
+ * favor exiting when a `SystemExit` is raised.
+ * Especially since this exception more likely to be used for background/batch-processing
+ * utilities where exiting immediately makes sense, the possibility of this being called
+ * indirectly from python-drivers or modal-operators is less of a concern. */
+ PyErr_Restore(error_type, error_value, error_traceback);
+ PyErr_Print();
+}
+
/* returns the exception string as a new PyUnicode object, depends on external traceback module */
# if 0
@@ -873,6 +903,8 @@ PyObject *PyC_ExceptionBuffer(void)
PyErr_Fetch(&error_type, &error_value, &error_traceback);
+ pyc_exception_buffer_handle_system_exit(error_type, error_value, error_traceback);
+
/* import io
* string_io = io.StringIO()
*/
@@ -896,7 +928,7 @@ PyObject *PyC_ExceptionBuffer(void)
PySys_SetObject("stderr", string_io);
PyErr_Restore(error_type, error_value, error_traceback);
- /* Printing clears (call #PyErr_Clear as well to ensure it's cleared). */
+ /* Printing clears (call #PyErr_Clear as well to ensure it's cleared). */
Py_XINCREF(error_type);
Py_XINCREF(error_value);
Py_XINCREF(error_traceback);
@@ -944,6 +976,11 @@ PyObject *PyC_ExceptionBuffer_Simple(void)
PyErr_Fetch(&error_type, &error_value, &error_traceback);
+ /* Since #PyErr_Print is not called it's not essential that `SystemExit` exceptions are handled.
+ * Do this to match the behavior of #PyC_ExceptionBuffer since requesting a brief exception
+ * shouldn't result in completely different behavior. */
+ pyc_exception_buffer_handle_system_exit(error_type, error_value, error_traceback);
+
if (PyErr_GivenExceptionMatches(error_type, PyExc_SyntaxError)) {
/* Special exception for syntax errors,
* in these cases the full error is verbose and not very useful,
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index ecb6db2b82c..91ebef8d0b0 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -215,7 +215,6 @@ int PyC_CheckArgs_DeepCopy(PyObject *args);
/* Integer parsing (with overflow checks), -1 on error. */
/**
- *
* Comparison with #PyObject_IsTrue
* ================================
*
diff --git a/source/blender/python/gpu/CMakeLists.txt b/source/blender/python/gpu/CMakeLists.txt
index 8ccb29beb13..e9db5c8716b 100644
--- a/source/blender/python/gpu/CMakeLists.txt
+++ b/source/blender/python/gpu/CMakeLists.txt
@@ -8,12 +8,11 @@ set(INC
../../gpu
../../imbuf
../../makesdna
- ../../../../intern/glew-mx
../../../../intern/guardedalloc
)
set(INC_SYS
- ${GLEW_INCLUDE_PATH}
+ ${Epoxy_INCLUDE_DIRS}
${PYTHON_INCLUDE_DIRS}
)
@@ -59,10 +58,9 @@ set(SRC
)
set(LIB
+ ${Epoxy_LIBRARIES}
${PYTHON_LINKFLAGS}
${PYTHON_LIBRARIES}
)
-add_definitions(${GL_DEFINITIONS})
-
blender_add_lib(bf_python_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/python/gpu/gpu_py_batch.c b/source/blender/python/gpu/gpu_py_batch.c
index 533e5154d83..879e1c0ce8b 100644
--- a/source/blender/python/gpu/gpu_py_batch.c
+++ b/source/blender/python/gpu/gpu_py_batch.c
@@ -127,7 +127,7 @@ PyDoc_STRVAR(pygpu_batch_vertbuf_add_doc,
" vertex buffer for vertex positions and vertex normals.\n"
" Current a batch can have at most " STRINGIFY(GPU_BATCH_VBO_MAX_LEN) " vertex buffers.\n"
"\n"
-" :param buf: The vertex buffer that will be added to the batch.\n"
+" :arg buf: The vertex buffer that will be added to the batch.\n"
" :type buf: :class:`gpu.types.GPUVertBuf`\n"
);
static PyObject *pygpu_batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_buf)
@@ -171,7 +171,7 @@ PyDoc_STRVAR(
" This function does not need to be called when you always\n"
" set the shader when calling :meth:`gpu.types.GPUBatch.draw`.\n"
"\n"
- " :param program: The program/shader the batch will use in future draw calls.\n"
+ " :arg program: The program/shader the batch will use in future draw calls.\n"
" :type program: :class:`gpu.types.GPUShader`\n");
static PyObject *pygpu_batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader)
{
@@ -209,7 +209,7 @@ PyDoc_STRVAR(pygpu_batch_draw_doc,
"\n"
" Run the drawing program with the parameters assigned to the batch.\n"
"\n"
- " :param program: Program that performs the drawing operations.\n"
+ " :arg program: Program that performs the drawing operations.\n"
" If ``None`` is passed, the last program set to this batch will run.\n"
" :type program: :class:`gpu.types.GPUShader`\n");
static PyObject *pygpu_batch_draw(BPyGPUBatch *self, PyObject *args)
diff --git a/source/blender/python/gpu/gpu_py_buffer.c b/source/blender/python/gpu/gpu_py_buffer.c
index 020535d002a..9a415b7f2c4 100644
--- a/source/blender/python/gpu/gpu_py_buffer.c
+++ b/source/blender/python/gpu/gpu_py_buffer.c
@@ -100,7 +100,7 @@ static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
}
else {
PyErr_Format(PyExc_TypeError,
- "invalid second argument argument expected a sequence "
+ "invalid second argument expected a sequence "
"or an int, not a %.200s",
Py_TYPE(shape_obj)->tp_name);
}
@@ -655,7 +655,7 @@ PyDoc_STRVAR(
"\n"
" :arg format: Format type to interpret the buffer.\n"
" Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
- " :type type: str\n"
+ " :type format: str\n"
" :arg dimensions: Array describing the dimensions.\n"
" :type dimensions: int\n"
" :arg data: Optional data array.\n"
diff --git a/source/blender/python/gpu/gpu_py_element.c b/source/blender/python/gpu/gpu_py_element.c
index d52a97c0c84..46f1d4d49eb 100644
--- a/source/blender/python/gpu/gpu_py_element.c
+++ b/source/blender/python/gpu/gpu_py_element.c
@@ -180,7 +180,7 @@ PyDoc_STRVAR(pygpu_IndexBuf__tp_doc,
" :arg type: The primitive type this index buffer is composed of.\n"
" Possible values are `POINTS`, `LINES`, `TRIS` and `LINE_STRIP_ADJ`.\n"
" :type type: str\n"
- " :param seq: Indices this index buffer will contain.\n"
+ " :arg seq: Indices this index buffer will contain.\n"
" Whether a 1D or 2D sequence is required depends on the type.\n"
" Optionally the sequence can support the buffer protocol.\n"
" :type seq: 1D or 2D sequence\n");
diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c
index 33d9ff0b041..2de8c680b23 100644
--- a/source/blender/python/gpu/gpu_py_framebuffer.c
+++ b/source/blender/python/gpu/gpu_py_framebuffer.c
@@ -421,9 +421,10 @@ PyDoc_STRVAR(pygpu_framebuffer_viewport_set_doc,
" Set the viewport for this framebuffer object.\n"
" Note: The viewport state is not saved upon framebuffer rebind.\n"
"\n"
- " :param x, y: lower left corner of the viewport_set rectangle, in pixels.\n"
- " :param xsize, ysize: width and height of the viewport_set.\n"
- " :type x, y, xsize, ysize: int\n");
+ " :arg x, y: lower left corner of the viewport_set rectangle, in pixels.\n"
+ " :type x, y: int\n"
+ " :arg xsize, ysize: width and height of the viewport_set.\n"
+ " :type xsize, ysize: int\n");
static PyObject *pygpu_framebuffer_viewport_set(BPyGPUFrameBuffer *self,
PyObject *args,
void *UNUSED(type))
@@ -462,16 +463,16 @@ PyDoc_STRVAR(
"\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"
+ " :arg x, y: Lower left corner of a rectangular block of pixels.\n"
+ " :arg xsize, ysize: Dimensions of the pixel rectangle.\n"
" :type x, y, xsize, ysize: int\n"
- " :param channels: Number of components to read.\n"
+ " :arg channels: Number of components to read.\n"
" :type channels: int\n"
- " :param slot: The framebuffer slot to read data from.\n"
+ " :arg 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"
+ " :arg 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"
+ " :type format: 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"
@@ -569,9 +570,10 @@ PyDoc_STRVAR(pygpu_framebuffer_read_depth_doc,
"\n"
" Read a pixel depth block from the frame buffer.\n"
"\n"
- " :param x, y: Lower left corner of a rectangular block of pixels.\n"
- " :param xsize, ysize: Dimensions of the pixel rectangle.\n"
- " :type x, y, xsize, ysize: int\n"
+ " :arg x, y: Lower left corner of a rectangular block of pixels.\n"
+ " :type x, y: int\n"
+ " :arg xsize, ysize: Dimensions of the pixel rectangle.\n"
+ " :type xsize, ysize: int\n"
" :arg data: Optional Buffer object to fill with the pixels values.\n"
" :type data: :class:`gpu.types.Buffer`\n"
" :return: The Buffer with the read pixels.\n"
@@ -686,7 +688,7 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = {
PyDoc_STRVAR(pygpu_framebuffer__tp_doc,
".. class:: GPUFrameBuffer(depth_slot=None, color_slots=None)\n"
"\n"
- " This object gives access to framebuffer functionallities.\n"
+ " This object gives access to framebuffer functionalities.\n"
" When a 'layer' is specified in a argument, a single layer of a 3D or array "
"texture is attached to the frame-buffer.\n"
" For cube map textures, layer is translated into a cube map face.\n"
diff --git a/source/blender/python/gpu/gpu_py_matrix.c b/source/blender/python/gpu/gpu_py_matrix.c
index a47e3dc8a5f..18925a101da 100644
--- a/source/blender/python/gpu/gpu_py_matrix.c
+++ b/source/blender/python/gpu/gpu_py_matrix.c
@@ -266,7 +266,7 @@ PyDoc_STRVAR(pygpu_matrix_multiply_matrix_doc,
"\n"
" Multiply the current stack matrix.\n"
"\n"
- " :param matrix: A 4x4 matrix.\n"
+ " :arg matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_multiply_matrix(PyObject *UNUSED(self), PyObject *value)
{
@@ -283,7 +283,7 @@ PyDoc_STRVAR(pygpu_matrix_scale_doc,
"\n"
" Scale the current stack matrix.\n"
"\n"
- " :param scale: Scale the current stack matrix.\n"
+ " :arg scale: Scale the current stack matrix.\n"
" :type scale: sequence of 2 or 3 floats\n");
static PyObject *pygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value)
{
@@ -305,7 +305,7 @@ static PyObject *pygpu_matrix_scale(PyObject *UNUSED(self), PyObject *value)
PyDoc_STRVAR(pygpu_matrix_scale_uniform_doc,
".. function:: scale_uniform(scale)\n"
"\n"
- " :param scale: Scale the current stack matrix.\n"
+ " :arg scale: Scale the current stack matrix.\n"
" :type scale: float\n");
static PyObject *pygpu_matrix_scale_uniform(PyObject *UNUSED(self), PyObject *value)
{
@@ -323,7 +323,7 @@ PyDoc_STRVAR(pygpu_matrix_translate_doc,
"\n"
" Scale the current stack matrix.\n"
"\n"
- " :param offset: Translate the current stack matrix.\n"
+ " :arg offset: Translate the current stack matrix.\n"
" :type offset: sequence of 2 or 3 floats\n");
static PyObject *pygpu_matrix_translate(PyObject *UNUSED(self), PyObject *value)
{
@@ -373,7 +373,7 @@ PyDoc_STRVAR(pygpu_matrix_load_matrix_doc,
"\n"
" Load a matrix into the stack.\n"
"\n"
- " :param matrix: A 4x4 matrix.\n"
+ " :arg matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_load_matrix(PyObject *UNUSED(self), PyObject *value)
{
@@ -390,7 +390,7 @@ PyDoc_STRVAR(pygpu_matrix_load_projection_matrix_doc,
"\n"
" Load a projection matrix into the stack.\n"
"\n"
- " :param matrix: A 4x4 matrix.\n"
+ " :arg matrix: A 4x4 matrix.\n"
" :type matrix: :class:`mathutils.Matrix`\n");
static PyObject *pygpu_matrix_load_projection_matrix(PyObject *UNUSED(self), PyObject *value)
{
diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c
index 656024ae22c..b877e3ceb98 100644
--- a/source/blender/python/gpu/gpu_py_platform.c
+++ b/source/blender/python/gpu/gpu_py_platform.c
@@ -55,6 +55,34 @@ static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self))
return PyUnicode_FromString(GPU_platform_version());
}
+PyDoc_STRVAR(
+ pygpu_platform_device_type_get_doc,
+ ".. function:: device_type_get()\n"
+ "\n"
+ " Get GPU device type.\n"
+ "\n"
+ " :return: Device type ('APPLE', 'NVIDIA', 'AMD', 'INTEL', 'SOFTWARE', 'UNKNOWN').\n"
+ " :rtype: str\n");
+static PyObject *pygpu_platform_device_type_get(PyObject *UNUSED(self))
+{
+ if (GPU_type_matches(GPU_DEVICE_APPLE, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ return PyUnicode_FromString("APPLE");
+ }
+ if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ return PyUnicode_FromString("NVIDIA");
+ }
+ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ return PyUnicode_FromString("AMD");
+ }
+ if (GPU_type_matches(GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ return PyUnicode_FromString("INTEL");
+ }
+ if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ return PyUnicode_FromString("SOFTWARE");
+ }
+ return PyUnicode_FromString("UNKNOWN");
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -74,6 +102,10 @@ static struct PyMethodDef pygpu_platform__tp_methods[] = {
(PyCFunction)pygpu_platform_version_get,
METH_NOARGS,
pygpu_platform_version_get_doc},
+ {"device_type_get",
+ (PyCFunction)pygpu_platform_device_type_get,
+ METH_NOARGS,
+ pygpu_platform_device_type_get_doc},
{NULL, NULL, 0, NULL},
};
diff --git a/source/blender/python/gpu/gpu_py_select.c b/source/blender/python/gpu/gpu_py_select.c
index 8869ea38e32..6d11e94433e 100644
--- a/source/blender/python/gpu/gpu_py_select.c
+++ b/source/blender/python/gpu/gpu_py_select.c
@@ -31,7 +31,7 @@ PyDoc_STRVAR(pygpu_select_load_id_doc,
"\n"
" Set the selection ID.\n"
"\n"
- " :param id: Number (32-bit uint).\n"
+ " :arg id: Number (32-bit uint).\n"
" :type select: int\n");
static PyObject *pygpu_select_load_id(PyObject *UNUSED(self), PyObject *value)
{
diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c
index 101b9f8e4c6..a57b00e671e 100644
--- a/source/blender/python/gpu/gpu_py_shader.c
+++ b/source/blender/python/gpu/gpu_py_shader.c
@@ -16,6 +16,7 @@
#include "GPU_uniform_buffer.h"
#include "../generic/py_capi_utils.h"
+#include "../generic/python_utildefines.h"
#include "../mathutils/mathutils.h"
#include "gpu_py.h"
@@ -30,52 +31,40 @@
* \{ */
#define PYDOC_BUILTIN_SHADER_DESCRIPTION \
- "``2D_FLAT_COLOR``\n" \
+ "``FLAT_COLOR``\n" \
" :Attributes: vec3 pos, vec4 color\n" \
" :Uniforms: none\n" \
- "``2D_IMAGE``\n" \
+ "``IMAGE``\n" \
" :Attributes: vec3 pos, vec2 texCoord\n" \
" :Uniforms: sampler2D image\n" \
- "``2D_SMOOTH_COLOR``\n" \
- " :Attributes: vec3 pos, vec4 color\n" \
- " :Uniforms: none\n" \
- "``2D_UNIFORM_COLOR``\n" \
- " :Attributes: vec3 pos\n" \
- " :Uniforms: vec4 color\n" \
- "``3D_FLAT_COLOR``\n" \
- " :Attributes: vec3 pos, vec4 color\n" \
- " :Uniforms: none\n" \
- "``3D_IMAGE``\n" \
+ "``IMAGE_COLOR``\n" \
" :Attributes: vec3 pos, vec2 texCoord\n" \
- " :Uniforms: sampler2D image\n" \
- "``3D_SMOOTH_COLOR``\n" \
+ " :Uniforms: sampler2D image, vec4 color\n" \
+ "``SMOOTH_COLOR``\n" \
" :Attributes: vec3 pos, vec4 color\n" \
" :Uniforms: none\n" \
- "``3D_UNIFORM_COLOR``\n" \
+ "``UNIFORM_COLOR``\n" \
" :Attributes: vec3 pos\n" \
" :Uniforms: vec4 color\n" \
- "``3D_POLYLINE_FLAT_COLOR``\n" \
+ "``POLYLINE_FLAT_COLOR``\n" \
" :Attributes: vec3 pos, vec4 color\n" \
" :Uniforms: vec2 viewportSize, float lineWidth\n" \
- "``3D_POLYLINE_SMOOTH_COLOR``\n" \
+ "``POLYLINE_SMOOTH_COLOR``\n" \
" :Attributes: vec3 pos, vec4 color\n" \
" :Uniforms: vec2 viewportSize, float lineWidth\n" \
- "``3D_POLYLINE_UNIFORM_COLOR``\n" \
+ "``POLYLINE_UNIFORM_COLOR``\n" \
" :Attributes: vec3 pos\n" \
" :Uniforms: vec2 viewportSize, float lineWidth\n"
static const struct PyC_StringEnumItems pygpu_shader_builtin_items[] = {
- {GPU_SHADER_2D_FLAT_COLOR, "2D_FLAT_COLOR"},
- {GPU_SHADER_2D_IMAGE, "2D_IMAGE"},
- {GPU_SHADER_2D_SMOOTH_COLOR, "2D_SMOOTH_COLOR"},
- {GPU_SHADER_2D_UNIFORM_COLOR, "2D_UNIFORM_COLOR"},
- {GPU_SHADER_3D_FLAT_COLOR, "3D_FLAT_COLOR"},
- {GPU_SHADER_3D_IMAGE, "3D_IMAGE"},
- {GPU_SHADER_3D_SMOOTH_COLOR, "3D_SMOOTH_COLOR"},
- {GPU_SHADER_3D_UNIFORM_COLOR, "3D_UNIFORM_COLOR"},
- {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "3D_POLYLINE_FLAT_COLOR"},
- {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "3D_POLYLINE_SMOOTH_COLOR"},
- {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "3D_POLYLINE_UNIFORM_COLOR"},
+ {GPU_SHADER_3D_FLAT_COLOR, "FLAT_COLOR"},
+ {GPU_SHADER_3D_IMAGE, "IMAGE"},
+ {GPU_SHADER_3D_IMAGE_COLOR, "IMAGE_COLOR"},
+ {GPU_SHADER_3D_SMOOTH_COLOR, "SMOOTH_COLOR"},
+ {GPU_SHADER_3D_UNIFORM_COLOR, "UNIFORM_COLOR"},
+ {GPU_SHADER_3D_POLYLINE_FLAT_COLOR, "POLYLINE_FLAT_COLOR"},
+ {GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, "POLYLINE_SMOOTH_COLOR"},
+ {GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, "POLYLINE_UNIFORM_COLOR"},
{0, NULL},
};
@@ -174,7 +163,7 @@ PyDoc_STRVAR(pygpu_shader_uniform_from_name_doc,
"\n"
" Get uniform location by name.\n"
"\n"
- " :param name: Name of the uniform variable whose location is to be queried.\n"
+ " :arg name: Name of the uniform variable whose location is to be queried.\n"
" :type name: str\n"
" :return: Location of the uniform variable.\n"
" :rtype: int\n");
@@ -195,16 +184,15 @@ static PyObject *pygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *ar
return PyLong_FromLong(uniform);
}
-PyDoc_STRVAR(
- pygpu_shader_uniform_block_from_name_doc,
- ".. method:: uniform_block_from_name(name)\n"
- "\n"
- " Get uniform block location by name.\n"
- "\n"
- " :param name: Name of the uniform block variable whose location is to be queried.\n"
- " :type name: str\n"
- " :return: The location of the uniform block variable.\n"
- " :rtype: int\n");
+PyDoc_STRVAR(pygpu_shader_uniform_block_from_name_doc,
+ ".. method:: uniform_block_from_name(name)\n"
+ "\n"
+ " Get uniform block location by name.\n"
+ "\n"
+ " :arg name: Name of the uniform block variable whose location is to be queried.\n"
+ " :type name: str\n"
+ " :return: The location of the uniform block variable.\n"
+ " :rtype: int\n");
static PyObject *pygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObject *arg)
{
const char *name = PyUnicode_AsUTF8(arg);
@@ -256,11 +244,11 @@ PyDoc_STRVAR(pygpu_shader_uniform_vector_float_doc,
"\n"
" Set the buffer to fill the uniform.\n"
"\n"
- " :param location: Location of the uniform variable to be modified.\n"
+ " :arg location: Location of the uniform variable to be modified.\n"
" :type location: int\n"
- " :param buffer: The data that should be set. Can support the buffer protocol.\n"
+ " :arg buffer: The data that should be set. Can support the buffer protocol.\n"
" :type buffer: sequence of floats\n"
- " :param length: Size of the uniform data type:\n\n"
+ " :arg length: Size of the uniform data type:\n\n"
" - 1: float\n"
" - 2: vec2 or float[2]\n"
" - 3: vec3 or float[3]\n"
@@ -268,7 +256,7 @@ PyDoc_STRVAR(pygpu_shader_uniform_vector_float_doc,
" - 9: mat3\n"
" - 16: mat4\n"
" :type length: int\n"
- " :param count: Specifies the number of elements, vector or matrices that are to "
+ " :arg count: Specifies the number of elements, vector or matrices that are to "
"be modified.\n"
" :type count: int\n");
static PyObject *pygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject *args)
@@ -282,6 +270,7 @@ static PyObject *pygpu_shader_uniform_vector_float(BPyGPUShader *self, PyObject
return NULL;
}
+ GPU_shader_bind(self->shader);
GPU_shader_uniform_vector(self->shader, location, length, count, pybuffer.buf);
PyBuffer_Release(&pybuffer);
@@ -304,6 +293,7 @@ static PyObject *pygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *a
return NULL;
}
+ GPU_shader_bind(self->shader);
GPU_shader_uniform_vector_int(self->shader, location, length, count, pybuffer.buf);
PyBuffer_Release(&pybuffer);
@@ -312,14 +302,14 @@ static PyObject *pygpu_shader_uniform_vector_int(BPyGPUShader *self, PyObject *a
}
PyDoc_STRVAR(pygpu_shader_uniform_bool_doc,
- ".. method:: uniform_bool(name, seq)\n"
+ ".. method:: uniform_bool(name, value)\n"
"\n"
" Specify the value of a uniform variable for the current program object.\n"
"\n"
- " :param name: Name of the uniform variable whose value is to be changed.\n"
+ " :arg name: Name of the uniform variable whose value is to be changed.\n"
" :type name: str\n"
- " :param seq: Value that will be used to update the specified uniform variable.\n"
- " :type seq: sequence of bools\n");
+ " :arg value: Value that will be used to update the specified uniform variable.\n"
+ " :type value: bool or sequence of bools\n");
static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
{
const char *error_prefix = "GPUShader.uniform_bool";
@@ -335,15 +325,14 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
int values[4];
int length;
- int ret;
- {
+ int ret = -1;
+ if (PySequence_Check(params.seq)) {
PyObject *seq_fast = PySequence_Fast(params.seq, error_prefix);
if (seq_fast == NULL) {
PyErr_Format(PyExc_TypeError,
"%s: expected a sequence, got %s",
error_prefix,
Py_TYPE(params.seq)->tp_name);
- ret = -1;
}
else {
length = PySequence_Fast_GET_SIZE(seq_fast);
@@ -352,7 +341,6 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
"%s: invalid sequence length. expected 1..4, got %d",
error_prefix,
length);
- ret = -1;
}
else {
ret = PyC_AsArray_FAST(
@@ -361,6 +349,15 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
Py_DECREF(seq_fast);
}
}
+ else if (((values[0] = (int)PyLong_AsLong(params.seq)) != -1) && ELEM(values[0], 0, 1)) {
+ length = 1;
+ ret = 0;
+ }
+ else {
+ PyErr_Format(
+ PyExc_ValueError, "expected a bool or sequence, got %s", Py_TYPE(params.seq)->tp_name);
+ }
+
if (ret == -1) {
return NULL;
}
@@ -371,6 +368,7 @@ static PyObject *pygpu_shader_uniform_bool(BPyGPUShader *self, PyObject *args)
return NULL;
}
+ GPU_shader_bind(self->shader);
GPU_shader_uniform_vector_int(self->shader, location, length, 1, values);
Py_RETURN_NONE;
@@ -381,9 +379,9 @@ PyDoc_STRVAR(pygpu_shader_uniform_float_doc,
"\n"
" Specify the value of a uniform variable for the current program object.\n"
"\n"
- " :param name: Name of the uniform variable whose value is to be changed.\n"
+ " :arg name: Name of the uniform variable whose value is to be changed.\n"
" :type name: str\n"
- " :param value: Value that will be used to update the specified uniform variable.\n"
+ " :arg value: Value that will be used to update the specified uniform variable.\n"
" :type value: single number or sequence of numbers\n");
static PyObject *pygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args)
{
@@ -440,6 +438,7 @@ static PyObject *pygpu_shader_uniform_float(BPyGPUShader *self, PyObject *args)
return NULL;
}
+ GPU_shader_bind(self->shader);
GPU_shader_uniform_vector(self->shader, location, length, 1, values);
Py_RETURN_NONE;
@@ -450,9 +449,9 @@ PyDoc_STRVAR(pygpu_shader_uniform_int_doc,
"\n"
" Specify the value of a uniform variable for the current program object.\n"
"\n"
- " :param name: name of the uniform variable whose value is to be changed.\n"
+ " :arg name: name of the uniform variable whose value is to be changed.\n"
" :type name: str\n"
- " :param seq: Value that will be used to update the specified uniform variable.\n"
+ " :arg seq: Value that will be used to update the specified uniform variable.\n"
" :type seq: sequence of numbers\n");
static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
{
@@ -511,6 +510,7 @@ static PyObject *pygpu_shader_uniform_int(BPyGPUShader *self, PyObject *args)
return NULL;
}
+ GPU_shader_bind(self->shader);
GPU_shader_uniform_vector_int(self->shader, location, length, 1, values);
Py_RETURN_NONE;
@@ -521,9 +521,9 @@ PyDoc_STRVAR(pygpu_shader_uniform_sampler_doc,
"\n"
" Specify the value of a texture uniform variable for the current GPUShader.\n"
"\n"
- " :param name: name of the uniform variable whose texture is to be specified.\n"
+ " :arg name: name of the uniform variable whose texture is to be specified.\n"
" :type name: str\n"
- " :param texture: Texture to attach.\n"
+ " :arg texture: Texture to attach.\n"
" :type texture: :class:`gpu.types.GPUTexture`\n");
static PyObject *pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args)
{
@@ -534,6 +534,7 @@ static PyObject *pygpu_shader_uniform_sampler(BPyGPUShader *self, PyObject *args
return NULL;
}
+ GPU_shader_bind(self->shader);
int slot = GPU_shader_get_texture_binding(self->shader, name);
GPU_texture_bind(py_texture->tex, slot);
GPU_shader_uniform_1i(self->shader, name, slot);
@@ -547,9 +548,9 @@ PyDoc_STRVAR(
"\n"
" Specify the value of an uniform buffer object variable for the current GPUShader.\n"
"\n"
- " :param name: name of the uniform variable whose UBO is to be specified.\n"
+ " :arg name: name of the uniform variable whose UBO is to be specified.\n"
" :type name: str\n"
- " :param ubo: Uniform Buffer to attach.\n"
+ " :arg ubo: Uniform Buffer to attach.\n"
" :type texture: :class:`gpu.types.GPUUniformBuf`\n");
static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args)
{
@@ -568,21 +569,21 @@ static PyObject *pygpu_shader_uniform_block(BPyGPUShader *self, PyObject *args)
return NULL;
}
+ GPU_shader_bind(self->shader);
GPU_uniformbuf_bind(py_ubo->ubo, binding);
Py_RETURN_NONE;
}
-PyDoc_STRVAR(
- pygpu_shader_attr_from_name_doc,
- ".. method:: attr_from_name(name)\n"
- "\n"
- " Get attribute location by name.\n"
- "\n"
- " :param name: The name of the attribute variable whose location is to be queried.\n"
- " :type name: str\n"
- " :return: The location of an attribute variable.\n"
- " :rtype: int\n");
+PyDoc_STRVAR(pygpu_shader_attr_from_name_doc,
+ ".. method:: attr_from_name(name)\n"
+ "\n"
+ " Get attribute location by name.\n"
+ "\n"
+ " :arg name: The name of the attribute variable whose location is to be queried.\n"
+ " :type name: str\n"
+ " :return: The location of an attribute variable.\n"
+ " :rtype: int\n");
static PyObject *pygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
{
const char *name = PyUnicode_AsUTF8(arg);
@@ -600,20 +601,58 @@ static PyObject *pygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg)
return PyLong_FromLong(attr);
}
-PyDoc_STRVAR(pygpu_shader_calc_format_doc,
- ".. method:: calc_format()\n"
+PyDoc_STRVAR(pygpu_shader_format_calc_doc,
+ ".. method:: format_calc()\n"
"\n"
" Build a new format based on the attributes of the shader.\n"
"\n"
" :return: vertex attribute format for the shader\n"
" :rtype: :class:`gpu.types.GPUVertFormat`\n");
-static PyObject *pygpu_shader_calc_format(BPyGPUShader *self, PyObject *UNUSED(arg))
+static PyObject *pygpu_shader_format_calc(BPyGPUShader *self, PyObject *UNUSED(arg))
{
BPyGPUVertFormat *ret = (BPyGPUVertFormat *)BPyGPUVertFormat_CreatePyObject(NULL);
GPU_vertformat_from_shader(&ret->fmt, self->shader);
return (PyObject *)ret;
}
+PyDoc_STRVAR(
+ pygpu_shader_attrs_info_get_doc,
+ ".. method:: attrs_info_get()\n"
+ "\n"
+ " Information about the attributes used in the Shader.\n"
+ "\n"
+ " :return: tuples containing information about the attributes in order (name, type)\n"
+ " :rtype: tuple\n");
+static PyObject *pygpu_shader_attrs_info_get(BPyGPUShader *self, PyObject *UNUSED(arg))
+{
+ uint attr_len = GPU_shader_get_attribute_len(self->shader);
+ int location_test = 0, attrs_added = 0;
+ ;
+ PyObject *ret = PyTuple_New(attr_len);
+ while (attrs_added < attr_len) {
+ char name[256];
+ int type;
+ if (!GPU_shader_get_attribute_info(self->shader, location_test++, name, &type)) {
+ continue;
+ }
+ PyObject *py_type;
+ if (type != -1) {
+ py_type = PyUnicode_InternFromString(
+ PyC_StringEnum_FindIDFromValue(pygpu_attrtype_items, type));
+ }
+ else {
+ py_type = Py_None;
+ Py_INCREF(py_type);
+ }
+
+ PyObject *attr_info = PyTuple_New(2);
+ PyTuple_SET_ITEMS(attr_info, PyUnicode_FromString(name), py_type);
+ PyTuple_SetItem(ret, attrs_added, attr_info);
+ attrs_added++;
+ }
+ return ret;
+}
+
static struct PyMethodDef pygpu_shader__tp_methods[] = {
{"bind", (PyCFunction)pygpu_shader_bind, METH_NOARGS, pygpu_shader_bind_doc},
{"uniform_from_name",
@@ -657,9 +696,13 @@ static struct PyMethodDef pygpu_shader__tp_methods[] = {
METH_O,
pygpu_shader_attr_from_name_doc},
{"format_calc",
- (PyCFunction)pygpu_shader_calc_format,
+ (PyCFunction)pygpu_shader_format_calc,
METH_NOARGS,
- pygpu_shader_calc_format_doc},
+ pygpu_shader_format_calc_doc},
+ {"attrs_info_get",
+ (PyCFunction)pygpu_shader_attrs_info_get,
+ METH_NOARGS,
+ pygpu_shader_attrs_info_get_doc},
{NULL, NULL, 0, NULL},
};
@@ -712,17 +755,17 @@ PyDoc_STRVAR(
" ``fragOutput = blender_srgb_to_framebuffer_space(fragOutput)``\n"
" to transform the output sRGB colors to the frame-buffer color-space.\n"
"\n"
- " :param vertexcode: Vertex shader code.\n"
+ " :arg vertexcode: Vertex shader code.\n"
" :type vertexcode: str\n"
- " :param fragcode: Fragment shader code.\n"
+ " :arg fragcode: Fragment shader code.\n"
" :type value: str\n"
- " :param geocode: Geometry shader code.\n"
+ " :arg geocode: Geometry shader code.\n"
" :type value: str\n"
- " :param libcode: Code with functions and presets to be shared between shaders.\n"
+ " :arg libcode: Code with functions and presets to be shared between shaders.\n"
" :type value: str\n"
- " :param defines: Preprocessor directives.\n"
+ " :arg defines: Preprocessor directives.\n"
" :type value: str\n"
- " :param name: Name of shader code, for debugging purposes.\n"
+ " :arg name: Name of shader code, for debugging purposes.\n"
" :type value: str\n");
PyTypeObject BPyGPUShader_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUShader",
@@ -741,6 +784,24 @@ PyTypeObject BPyGPUShader_Type = {
/** \name gpu.shader Module API
* \{ */
+static int pyc_parse_buitinshader_w_backward_compatibility(PyObject *o, void *p)
+{
+ struct PyC_StringEnum *e = p;
+ const char *value = PyUnicode_AsUTF8(o);
+ if (value && ELEM(value[0], u'2', u'3')) {
+ /* Deprecated enums that start with "3D_" or "2D_". */
+ value += 3;
+ for (int i = 0; e->items[i].id; i++) {
+ if (STREQ(e->items[i].id, value)) {
+ e->value_found = e->items[i].value;
+ return 1;
+ }
+ }
+ }
+
+ return PyC_ParseStringEnum(o, p);
+}
+
PyDoc_STRVAR(pygpu_shader_unbind_doc,
".. function:: unbind()\n"
"\n"
@@ -763,9 +824,9 @@ PyDoc_STRVAR(
"``CLIPPED`` value to the config parameter. Note that in this case you also need to "
"manually set the value of ``mat4 ModelMatrix``.\n"
"\n"
- " :param shader_name: One of the builtin shader names.\n"
+ " :arg shader_name: One of the builtin shader names.\n"
" :type shader_name: str\n"
- " :param config: One of these types of shader configuration:\n"
+ " :arg config: One of these types of shader configuration:\n"
"\n"
" - ``DEFAULT``\n"
" - ``CLIPPED``\n"
@@ -791,7 +852,7 @@ static PyObject *pygpu_shader_from_builtin(PyObject *UNUSED(self), PyObject *arg
if (!_PyArg_ParseTupleAndKeywordsFast(args,
kwds,
&_parser,
- PyC_ParseStringEnum,
+ pyc_parse_buitinshader_w_backward_compatibility,
&pygpu_bultinshader,
PyC_ParseStringEnum,
&pygpu_config)) {
@@ -809,7 +870,7 @@ PyDoc_STRVAR(pygpu_shader_create_from_info_doc,
"\n"
" Create shader from a GPUShaderCreateInfo.\n"
"\n"
- " :param shader_info: GPUShaderCreateInfo\n"
+ " :arg shader_info: GPUShaderCreateInfo\n"
" :type shader_info: :class:`bpy.types.GPUShaderCreateInfo`\n"
" :return: Shader object corresponding to the given name.\n"
" :rtype: :class:`bpy.types.GPUShader`\n");
diff --git a/source/blender/python/gpu/gpu_py_shader.h b/source/blender/python/gpu/gpu_py_shader.h
index b5944c4b3a0..ba40636981f 100644
--- a/source/blender/python/gpu/gpu_py_shader.h
+++ b/source/blender/python/gpu/gpu_py_shader.h
@@ -6,6 +6,10 @@
#pragma once
+#ifndef __cplusplus
+# include "../generic/py_capi_utils.h"
+#endif
+
/* Make sure that there is always a reference count for PyObjects of type String as the strings are
* passed by reference in the #GPUStageInterfaceInfo and #GPUShaderCreateInfo APIs. */
#define USE_GPU_PY_REFERENCES
@@ -31,6 +35,7 @@ extern "C" {
/* gpu_py_shader_create_info.cc */
+extern const struct PyC_StringEnumItems pygpu_attrtype_items[];
extern PyTypeObject BPyGPUShaderCreateInfo_Type;
extern PyTypeObject BPyGPUStageInterfaceInfo_Type;
diff --git a/source/blender/python/gpu/gpu_py_shader_create_info.cc b/source/blender/python/gpu/gpu_py_shader_create_info.cc
index e00d01174f4..f35c5a01145 100644
--- a/source/blender/python/gpu/gpu_py_shader_create_info.cc
+++ b/source/blender/python/gpu/gpu_py_shader_create_info.cc
@@ -58,7 +58,7 @@ static const struct PyC_FlagSet pygpu_qualifiers[] = {
" - ``IVEC3``\n" \
" - ``IVEC4``\n" \
" - ``BOOL``\n"
-static const struct PyC_StringEnumItems pygpu_attrtype_items[] = {
+const struct PyC_StringEnumItems pygpu_attrtype_items[] = {
{(int)Type::FLOAT, "FLOAT"},
{(int)Type::VEC2, "VEC2"},
{(int)Type::VEC3, "VEC3"},
@@ -189,11 +189,11 @@ PyDoc_STRVAR(pygpu_interface_info_smooth_doc,
"\n"
" Add an attribute with qualifier of type `smooth` to the interface block.\n"
"\n"
- " :param type: One of these types:\n"
+ " :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
- " :param name: name of the attribute.\n"
+ " :arg name: name of the attribute.\n"
" :type name: str\n");
static PyObject *pygpu_interface_info_smooth(BPyGPUStageInterfaceInfo *self, PyObject *args)
{
@@ -213,11 +213,11 @@ PyDoc_STRVAR(pygpu_interface_info_flat_doc,
"\n"
" Add an attribute with qualifier of type `flat` to the interface block.\n"
"\n"
- " :param type: One of these types:\n"
+ " :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
- " :param name: name of the attribute.\n"
+ " :arg name: name of the attribute.\n"
" :type name: str\n");
static PyObject *pygpu_interface_info_flat(BPyGPUStageInterfaceInfo *self, PyObject *args)
{
@@ -238,11 +238,11 @@ PyDoc_STRVAR(
"\n"
" Add an attribute with qualifier of type `no_perspective` to the interface block.\n"
"\n"
- " :param type: One of these types:\n"
+ " :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
- " :param name: name of the attribute.\n"
+ " :arg name: name of the attribute.\n"
" :type name: str\n");
static PyObject *pygpu_interface_info_no_perspective(BPyGPUStageInterfaceInfo *self,
PyObject *args)
@@ -370,7 +370,7 @@ PyDoc_STRVAR(pygpu_interface_info__tp_doc,
"\n"
" List of varyings between shader stages.\n\n"
"\n"
- " :param name: Name of the interface block.\n"
+ " :arg name: Name of the interface block.\n"
" :type value: str\n");
constexpr PyTypeObject pygpu_interface_info_type()
{
@@ -403,13 +403,13 @@ PyDoc_STRVAR(pygpu_shader_info_vertex_in_doc,
"\n"
" Add a vertex shader input attribute.\n"
"\n"
- " :param slot: The attribute index.\n"
+ " :arg slot: The attribute index.\n"
" :type slot: int\n"
- " :param type: One of these types:\n"
+ " :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
- " :param name: name of the attribute.\n"
+ " :arg name: name of the attribute.\n"
" :type name: str\n");
static PyObject *pygpu_shader_info_vertex_in(BPyGPUShaderCreateInfo *self, PyObject *args)
{
@@ -436,7 +436,7 @@ PyDoc_STRVAR(pygpu_shader_info_vertex_out_doc,
"\n"
" Add a vertex shader output interface block.\n"
"\n"
- " :param interface: Object describing the block.\n"
+ " :arg interface: Object describing the block.\n"
" :type interface: :class:`gpu.types.GPUStageInterfaceInfo`\n");
static PyObject *pygpu_shader_info_vertex_out(BPyGPUShaderCreateInfo *self,
BPyGPUStageInterfaceInfo *o)
@@ -462,15 +462,15 @@ PyDoc_STRVAR(pygpu_shader_info_fragment_out_doc,
"\n"
" Specify a fragment output corresponding to a framebuffer target slot.\n"
"\n"
- " :param slot: The attribute index.\n"
+ " :arg slot: The attribute index.\n"
" :type slot: int\n"
- " :param type: One of these types:\n"
+ " :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
- " :param name: Name of the attribute.\n"
+ " :arg name: Name of the attribute.\n"
" :type name: str\n"
- " :param blend: Dual Source Blending Index. It can be 'NONE', 'SRC_0' or 'SRC_1'.\n"
+ " :arg blend: Dual Source Blending Index. It can be 'NONE', 'SRC_0' or 'SRC_1'.\n"
" :type blend: str\n");
static PyObject *pygpu_shader_info_fragment_out(BPyGPUShaderCreateInfo *self,
PyObject *args,
@@ -521,12 +521,12 @@ PyDoc_STRVAR(
"\n"
" Specify a uniform variable whose type can be one of those declared in `typedef_source`.\n"
"\n"
- " :param slot: The uniform variable index.\n"
+ " :arg slot: The uniform variable index.\n"
" :type slot: int\n"
- " :param type_name: Name of the data type. It can be a struct type defined in the source "
+ " :arg type_name: Name of the data type. It can be a struct type defined in the source "
"passed through the :meth:`gpu.types.GPUShaderCreateInfo.typedef_source`.\n"
" :type type_name: str\n"
- " :param name: The uniform variable name.\n"
+ " :arg name: The uniform variable name.\n"
" :type name: str\n");
static PyObject *pygpu_shader_info_uniform_buf(BPyGPUShaderCreateInfo *self, PyObject *args)
{
@@ -556,19 +556,19 @@ PyDoc_STRVAR(
"\n"
" Specify an image resource used for arbitrary load and store operations.\n"
"\n"
- " :param slot: The image resource index.\n"
+ " :arg slot: The image resource index.\n"
" :type slot: int\n"
- " :param format: The GPUTexture format that is passed to the shader. Possible values are:\n"
+ " :arg format: The GPUTexture format that is passed to the shader. Possible values are:\n"
"" PYDOC_TEX_FORMAT_ITEMS
" :type format: str\n"
- " :param type: The data type describing how the image is to be read in the shader. "
+ " :arg type: The data type describing how the image is to be read in the shader. "
"Possible values are:\n"
"\n" PYDOC_IMAGE_TYPES
"\n"
" :type type: str\n"
- " :param name: The image resource name.\n"
+ " :arg name: The image resource name.\n"
" :type name: str\n"
- " :param qualifiers: Set containing values that describe how the image resource is to be "
+ " :arg qualifiers: Set containing values that describe how the image resource is to be "
"read or written. Possible values are:\n"
"" PYDOC_QUALIFIERS
""
@@ -636,14 +636,14 @@ PyDoc_STRVAR(
"\n"
" Specify an image texture sampler.\n"
"\n"
- " :param slot: The image texture sampler index.\n"
+ " :arg slot: The image texture sampler index.\n"
" :type slot: int\n"
- " :param type: The data type describing the format of each sampler unit. Possible values "
+ " :arg type: The data type describing the format of each sampler unit. Possible values "
"are:\n"
"\n" PYDOC_IMAGE_TYPES
"\n"
" :type type: str\n"
- " :param name: The image texture sampler name.\n"
+ " :arg name: The image texture sampler name.\n"
" :type name: str\n");
static PyObject *pygpu_shader_info_sampler(BPyGPUShaderCreateInfo *self, PyObject *args)
{
@@ -673,6 +673,9 @@ static int constant_type_size(Type type)
case Type::FLOAT:
case Type::INT:
case Type::UINT:
+ case Type::UCHAR4:
+ case Type::CHAR4:
+ case blender::gpu::shader::Type::VEC3_101010I2:
return 4;
break;
case Type::VEC2:
@@ -695,6 +698,18 @@ static int constant_type_size(Type type)
case Type::MAT4:
return 64;
break;
+ case blender::gpu::shader::Type::UCHAR:
+ case blender::gpu::shader::Type::CHAR:
+ return 1;
+ break;
+ case blender::gpu::shader::Type::UCHAR2:
+ case blender::gpu::shader::Type::CHAR2:
+ return 2;
+ break;
+ case blender::gpu::shader::Type::UCHAR3:
+ case blender::gpu::shader::Type::CHAR3:
+ return 3;
+ break;
}
BLI_assert(false);
return -1;
@@ -733,13 +748,13 @@ PyDoc_STRVAR(pygpu_shader_info_push_constant_doc,
"\n"
" Specify a global access constant.\n"
"\n"
- " :param type: One of these types:\n"
+ " :arg type: One of these types:\n"
"\n" PYDOC_TYPE_LIST
"\n"
" :type type: str\n"
- " :param name: Name of the constant.\n"
+ " :arg name: Name of the constant.\n"
" :type name: str\n"
- " :param size: If not zero, indicates that the constant is an array with the "
+ " :arg size: If not zero, indicates that the constant is an array with the "
"specified size.\n"
" :type size: uint\n");
static PyObject *pygpu_shader_info_push_constant(BPyGPUShaderCreateInfo *self,
@@ -797,7 +812,7 @@ PyDoc_STRVAR(
"\n"
" \"void main {gl_Position = vec4(pos, 1.0);}\"\n"
"\n"
- " :param source: The vertex shader source code.\n"
+ " :arg source: The vertex shader source code.\n"
" :type source: str\n"
"\n"
" .. seealso:: `GLSL Cross Compilation "
@@ -838,7 +853,7 @@ PyDoc_STRVAR(
"\n"
" \"void main {fragColor = vec4(0.0, 0.0, 0.0, 1.0);}\"\n"
"\n"
- " :param source: The fragment shader source code.\n"
+ " :arg source: The fragment shader source code.\n"
" :type source: str\n"
"\n"
" .. seealso:: `GLSL Cross Compilation "
@@ -879,7 +894,7 @@ PyDoc_STRVAR(pygpu_shader_info_typedef_source_doc,
"\n"
" \"struct MyType {int foo; float bar;};\"\n"
"\n"
- " :param source: The source code defining types.\n"
+ " :arg source: The source code defining types.\n"
" :type source: str\n");
static PyObject *pygpu_shader_info_typedef_source(BPyGPUShaderCreateInfo *self, PyObject *o)
{
@@ -918,9 +933,9 @@ PyDoc_STRVAR(pygpu_shader_info_define_doc,
"\n"
" #define name value\n"
"\n"
- " :param name: Token name.\n"
+ " :arg name: Token name.\n"
" :type name: str\n"
- " :param value: Text that replaces token occurrences.\n"
+ " :arg value: Text that replaces token occurrences.\n"
" :type value: str\n");
static PyObject *pygpu_shader_info_define(BPyGPUShaderCreateInfo *self, PyObject *args)
{
diff --git a/source/blender/python/gpu/gpu_py_state.c b/source/blender/python/gpu/gpu_py_state.c
index fb69bb316c4..35ecc2aff40 100644
--- a/source/blender/python/gpu/gpu_py_state.c
+++ b/source/blender/python/gpu/gpu_py_state.c
@@ -72,7 +72,7 @@ PyDoc_STRVAR(
"\n"
" Defines the fixed pipeline blending equation.\n"
"\n"
- " :param mode: The type of blend mode.\n"
+ " :arg mode: The type of blend mode.\n"
" * ``NONE`` No blending.\n"
" * ``ALPHA`` The original color channels are interpolated according to the alpha "
"value.\n"
@@ -114,7 +114,7 @@ PyDoc_STRVAR(pygpu_state_clip_distances_set_doc,
"\n"
" Sets the number of `gl_ClipDistance` planes used for clip geometry.\n"
"\n"
- " :param distances_enabled: Number of clip distances enabled.\n"
+ " :arg distances_enabled: Number of clip distances enabled.\n"
" :type distances_enabled: int\n");
static PyObject *pygpu_state_clip_distances_set(PyObject *UNUSED(self), PyObject *value)
{
@@ -136,7 +136,7 @@ PyDoc_STRVAR(pygpu_state_depth_test_set_doc,
"\n"
" Defines the depth_test equation.\n"
"\n"
- " :param mode: The depth test equation name.\n"
+ " :arg mode: The depth test equation name.\n"
" Possible values are `NONE`, `ALWAYS`, `LESS`, `LESS_EQUAL`, `EQUAL`, "
"`GREATER` and `GREATER_EQUAL`.\n"
" :type mode: str\n");
@@ -166,7 +166,7 @@ PyDoc_STRVAR(pygpu_state_depth_mask_set_doc,
"\n"
" Write to depth component.\n"
"\n"
- " :param value: True for writing to the depth component.\n"
+ " :arg value: True for writing to the depth component.\n"
" :type near: bool\n");
static PyObject *pygpu_state_depth_mask_set(PyObject *UNUSED(self), PyObject *value)
{
@@ -193,9 +193,10 @@ PyDoc_STRVAR(pygpu_state_viewport_set_doc,
" Specifies the viewport of the active framebuffer.\n"
" Note: The viewport state is not saved upon framebuffer rebind.\n"
"\n"
- " :param x, y: lower left corner of the viewport_set rectangle, in pixels.\n"
- " :param width, height: width and height of the viewport_set.\n"
- " :type x, y, xsize, ysize: int\n");
+ " :arg x, y: lower left corner of the viewport_set rectangle, in pixels.\n"
+ " :type x, y: int\n"
+ " :arg xsize, ysize: width and height of the viewport_set.\n"
+ " :type xsize, ysize: int\n");
static PyObject *pygpu_state_viewport_set(PyObject *UNUSED(self), PyObject *args)
{
int x, y, xsize, ysize;
@@ -230,7 +231,7 @@ PyDoc_STRVAR(pygpu_state_line_width_set_doc,
"\n"
" Specify the width of rasterized lines.\n"
"\n"
- " :param size: New width.\n"
+ " :arg size: New width.\n"
" :type mode: float\n");
static PyObject *pygpu_state_line_width_set(PyObject *UNUSED(self), PyObject *value)
{
@@ -258,7 +259,7 @@ PyDoc_STRVAR(pygpu_state_point_size_set_doc,
"\n"
" Specify the diameter of rasterized points.\n"
"\n"
- " :param size: New diameter.\n"
+ " :arg size: New diameter.\n"
" :type mode: float\n");
static PyObject *pygpu_state_point_size_set(PyObject *UNUSED(self), PyObject *value)
{
@@ -276,7 +277,7 @@ PyDoc_STRVAR(pygpu_state_color_mask_set_doc,
"\n"
" Enable or disable writing of frame buffer color components.\n"
"\n"
- " :param r, g, b, a: components red, green, blue, and alpha.\n"
+ " :arg r, g, b, a: components red, green, blue, and alpha.\n"
" :type r, g, b, a: bool\n");
static PyObject *pygpu_state_color_mask_set(PyObject *UNUSED(self), PyObject *args)
{
@@ -294,7 +295,7 @@ PyDoc_STRVAR(pygpu_state_face_culling_set_doc,
"\n"
" Specify whether none, front-facing or back-facing facets can be culled.\n"
"\n"
- " :param mode: `NONE`, `FRONT` or `BACK`.\n"
+ " :arg mode: `NONE`, `FRONT` or `BACK`.\n"
" :type mode: str\n");
static PyObject *pygpu_state_face_culling_set(PyObject *UNUSED(self), PyObject *value)
{
@@ -312,7 +313,7 @@ PyDoc_STRVAR(pygpu_state_front_facing_set_doc,
"\n"
" Specifies the orientation of front-facing polygons.\n"
"\n"
- " :param invert: True for clockwise polygons as front-facing.\n"
+ " :arg invert: True for clockwise polygons as front-facing.\n"
" :type mode: bool\n");
static PyObject *pygpu_state_front_facing_set(PyObject *UNUSED(self), PyObject *value)
{
@@ -331,7 +332,7 @@ PyDoc_STRVAR(pygpu_state_program_point_size_set_doc,
" If enabled, the derived point size is taken from the (potentially clipped) "
"shader builtin gl_PointSize.\n"
"\n"
- " :param enable: True for shader builtin gl_PointSize.\n"
+ " :arg enable: True for shader builtin gl_PointSize.\n"
" :type enable: bool\n");
static PyObject *pygpu_state_program_point_size_set(PyObject *UNUSED(self), PyObject *value)
{
diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c
index 7a23f7ac91f..ae004341304 100644
--- a/source/blender/python/gpu/gpu_py_texture.c
+++ b/source/blender/python/gpu/gpu_py_texture.c
@@ -280,9 +280,9 @@ PyDoc_STRVAR(
"\n"
" Fill texture with specific value.\n"
"\n"
- " :param format: The format that describes the content of a single item.\n"
+ " :arg format: The format that describes the content of a single item.\n"
" Possible values are `FLOAT`, `INT`, `UINT`, `UBYTE`, `UINT_24_8` and `10_11_11_REV`.\n"
- " :type type: str\n"
+ " :type format: str\n"
" :arg value: sequence each representing the value to fill.\n"
" :type value: sequence of 1, 2, 3 or 4 values\n");
static PyObject *pygpu_texture_clear(BPyGPUTexture *self, PyObject *args, PyObject *kwds)
diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c
index a295bedeae2..fd36c0a2d71 100644
--- a/source/blender/python/gpu/gpu_py_vertex_buffer.c
+++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c
@@ -261,9 +261,9 @@ PyDoc_STRVAR(pygpu_vertbuf_attr_fill_doc,
"\n"
" Insert data into the buffer for a single attribute.\n"
"\n"
- " :param id: Either the name or the id of the attribute.\n"
+ " :arg id: Either the name or the id of the attribute.\n"
" :type id: int or str\n"
- " :param data: Sequence of data that should be stored in the buffer\n"
+ " :arg data: Sequence of data that should be stored in the buffer\n"
" :type data: sequence of floats, ints, vectors or matrices\n");
static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds)
{
@@ -292,7 +292,7 @@ static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, Py
const char *name = PyUnicode_AsUTF8(identifier);
id = GPU_vertformat_attr_id_get(format, name);
if (id == -1) {
- PyErr_SetString(PyExc_ValueError, "Unknown attribute name");
+ PyErr_Format(PyExc_ValueError, "Unknown attribute '%s'", name);
return NULL;
}
}
@@ -323,14 +323,14 @@ static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self)
}
PyDoc_STRVAR(pygpu_vertbuf__tp_doc,
- ".. class:: GPUVertBuf(len, format)\n"
+ ".. class:: GPUVertBuf(format, len)\n"
"\n"
" Contains a VBO.\n"
"\n"
- " :param len: Amount of vertices that will fit into this buffer.\n"
- " :type type: `int`\n"
- " :param format: Vertex format.\n"
- " :type buf: :class:`gpu.types.GPUVertFormat`\n");
+ " :arg format: Vertex format.\n"
+ " :type format: :class:`gpu.types.GPUVertFormat`\n"
+ " :arg len: Amount of vertices that will fit into this buffer.\n"
+ " :type len: int\n");
PyTypeObject BPyGPUVertBuf_Type = {
PyVarObject_HEAD_INIT(NULL, 0).tp_name = "GPUVertBuf",
.tp_basicsize = sizeof(BPyGPUVertBuf),
diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c
index 40a0e5d1e9f..ac0ec6bdc01 100644
--- a/source/blender/python/gpu/gpu_py_vertex_format.c
+++ b/source/blender/python/gpu/gpu_py_vertex_format.c
@@ -63,15 +63,15 @@ PyDoc_STRVAR(
"\n"
" Add a new attribute to the format.\n"
"\n"
- " :param id: Name the attribute. Often `position`, `normal`, ...\n"
+ " :arg id: Name the attribute. Often `position`, `normal`, ...\n"
" :type id: str\n"
- " :param comp_type: The data type that will be used store the value in memory.\n"
+ " :arg comp_type: The data type that will be used store the value in memory.\n"
" Possible values are `I8`, `U8`, `I16`, `U16`, `I32`, `U32`, `F32` and `I10`.\n"
" :type comp_type: str\n"
- " :param len: How many individual values the attribute consists of\n"
+ " :arg len: How many individual values the attribute consists of\n"
" (e.g. 2 for uv coordinates).\n"
" :type len: int\n"
- " :param fetch_mode: How values from memory will be converted when used in the shader.\n"
+ " :arg fetch_mode: How values from memory will be converted when used in the shader.\n"
" This is mainly useful for memory optimizations when you want to store values with\n"
" reduced precision. E.g. you can store a float in only 1 byte but it will be\n"
" converted to a normal 4 byte float when used.\n"
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt
index 71138134370..9d2516969cf 100644
--- a/source/blender/python/intern/CMakeLists.txt
+++ b/source/blender/python/intern/CMakeLists.txt
@@ -174,8 +174,8 @@ if(WITH_CODEC_SNDFILE)
add_definitions(-DWITH_SNDFILE)
endif()
-if(WITH_COMPOSITOR)
- add_definitions(-DWITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_CYCLES)
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index 2e97ae0fc1d..3b3ffb321c3 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -32,6 +32,7 @@
#include "bpy.h"
#include "bpy_app.h"
#include "bpy_capi_utils.h"
+#include "bpy_driver.h"
#include "bpy_library.h"
#include "bpy_operator.h"
#include "bpy_props.h"
@@ -326,6 +327,49 @@ static PyObject *bpy_resource_path(PyObject *UNUSED(self), PyObject *args, PyObj
return PyC_UnicodeFromByte(path ? path : "");
}
+/* This is only exposed for tests, see: `tests/python/bl_pyapi_bpy_driver_secure_eval.py`. */
+PyDoc_STRVAR(bpy_driver_secure_code_test_doc,
+ ".. function:: _driver_secure_code_test(code)\n"
+ "\n"
+ " Test if the script should be considered trusted.\n"
+ "\n"
+ " :arg code: The code to test.\n"
+ " :type code: code\n"
+ " :arg namespace: The namespace of values which are allowed.\n"
+ " :type namespace: dict\n"
+ " :arg verbose: Print the reason for considering insecure to the ``stderr``.\n"
+ " :type verbose: bool\n"
+ " :return: True when the script is considered trusted.\n"
+ " :rtype: bool\n");
+static PyObject *bpy_driver_secure_code_test(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
+{
+ PyObject *py_code;
+ PyObject *py_namespace = NULL;
+ const bool verbose = false;
+ static const char *_keywords[] = {"code", "namespace", "verbose", NULL};
+ static _PyArg_Parser _parser = {
+ "O!" /* `expression` */
+ "|$" /* Optional keyword only arguments. */
+ "O!" /* `namespace` */
+ "O&" /* `verbose` */
+ ":driver_secure_code_test",
+ _keywords,
+ 0,
+ };
+ if (!_PyArg_ParseTupleAndKeywordsFast(args,
+ kw,
+ &_parser,
+ &PyCode_Type,
+ &py_code,
+ &PyDict_Type,
+ &py_namespace,
+ PyC_ParseBool,
+ &verbose)) {
+ return NULL;
+ }
+ return PyBool_FromLong(BPY_driver_secure_bytecode_test(py_code, py_namespace, verbose));
+}
+
PyDoc_STRVAR(bpy_escape_identifier_doc,
".. function:: escape_identifier(string)\n"
"\n"
@@ -492,65 +536,37 @@ static PyObject *bpy_rna_enum_items_static(PyObject *UNUSED(self))
return result;
}
-static PyMethodDef meth_bpy_script_paths = {
- "script_paths",
- (PyCFunction)bpy_script_paths,
- METH_NOARGS,
- bpy_script_paths_doc,
-};
-static PyMethodDef meth_bpy_blend_paths = {
- "blend_paths",
- (PyCFunction)bpy_blend_paths,
- METH_VARARGS | METH_KEYWORDS,
- bpy_blend_paths_doc,
-};
-static PyMethodDef meth_bpy_flip_name = {
- "flip_name",
- (PyCFunction)bpy_flip_name,
- METH_VARARGS | METH_KEYWORDS,
- bpy_flip_name_doc,
-};
-static PyMethodDef meth_bpy_user_resource = {
- "user_resource",
- (PyCFunction)bpy_user_resource,
- METH_VARARGS | METH_KEYWORDS,
- NULL,
-};
-static PyMethodDef meth_bpy_system_resource = {
- "system_resource",
- (PyCFunction)bpy_system_resource,
- METH_VARARGS | METH_KEYWORDS,
- bpy_system_resource_doc,
-};
-static PyMethodDef meth_bpy_resource_path = {
- "resource_path",
- (PyCFunction)bpy_resource_path,
- METH_VARARGS | METH_KEYWORDS,
- bpy_resource_path_doc,
-};
-static PyMethodDef meth_bpy_escape_identifier = {
- "escape_identifier",
- (PyCFunction)bpy_escape_identifier,
- METH_O,
- bpy_escape_identifier_doc,
-};
-static PyMethodDef meth_bpy_unescape_identifier = {
- "unescape_identifier",
- (PyCFunction)bpy_unescape_identifier,
- METH_O,
- bpy_unescape_identifier_doc,
-};
-static PyMethodDef meth_bpy_context_members = {
- "context_members",
- (PyCFunction)bpy_context_members,
- METH_NOARGS,
- bpy_context_members_doc,
-};
-static PyMethodDef meth_bpy_rna_enum_items_static = {
- "rna_enum_items_static",
- (PyCFunction)bpy_rna_enum_items_static,
- METH_NOARGS,
- bpy_rna_enum_items_static_doc,
+static PyMethodDef bpy_methods[] = {
+ {"script_paths", (PyCFunction)bpy_script_paths, METH_NOARGS, bpy_script_paths_doc},
+ {"blend_paths",
+ (PyCFunction)bpy_blend_paths,
+ METH_VARARGS | METH_KEYWORDS,
+ bpy_blend_paths_doc},
+ {"flip_name", (PyCFunction)bpy_flip_name, METH_VARARGS | METH_KEYWORDS, bpy_flip_name_doc},
+ {"user_resource", (PyCFunction)bpy_user_resource, METH_VARARGS | METH_KEYWORDS, NULL},
+ {"system_resource",
+ (PyCFunction)bpy_system_resource,
+ METH_VARARGS | METH_KEYWORDS,
+ bpy_system_resource_doc},
+ {"resource_path",
+ (PyCFunction)bpy_resource_path,
+ METH_VARARGS | METH_KEYWORDS,
+ bpy_resource_path_doc},
+ {"_driver_secure_code_test",
+ (PyCFunction)bpy_driver_secure_code_test,
+ METH_VARARGS | METH_KEYWORDS,
+ bpy_driver_secure_code_test_doc},
+ {"escape_identifier", (PyCFunction)bpy_escape_identifier, METH_O, bpy_escape_identifier_doc},
+ {"unescape_identifier",
+ (PyCFunction)bpy_unescape_identifier,
+ METH_O,
+ bpy_unescape_identifier_doc},
+ {"context_members", (PyCFunction)bpy_context_members, METH_NOARGS, bpy_context_members_doc},
+ {"rna_enum_items_static",
+ (PyCFunction)bpy_rna_enum_items_static,
+ METH_NOARGS,
+ bpy_rna_enum_items_static_doc},
+ {NULL, NULL, 0, NULL},
};
static PyObject *bpy_import_test(const char *modname)
@@ -632,37 +648,14 @@ void BPy_init_modules(struct bContext *C)
/* Register methods and property get/set for RNA types. */
BPY_rna_types_extend_capi();
- /* utility func's that have nowhere else to go */
- PyModule_AddObject(mod,
- meth_bpy_script_paths.ml_name,
- (PyObject *)PyCFunction_New(&meth_bpy_script_paths, NULL));
- PyModule_AddObject(
- mod, meth_bpy_blend_paths.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_blend_paths, NULL));
- PyModule_AddObject(mod,
- meth_bpy_user_resource.ml_name,
- (PyObject *)PyCFunction_New(&meth_bpy_user_resource, NULL));
- PyModule_AddObject(mod,
- meth_bpy_system_resource.ml_name,
- (PyObject *)PyCFunction_New(&meth_bpy_system_resource, NULL));
- PyModule_AddObject(mod,
- meth_bpy_resource_path.ml_name,
- (PyObject *)PyCFunction_New(&meth_bpy_resource_path, NULL));
- PyModule_AddObject(mod,
- meth_bpy_escape_identifier.ml_name,
- (PyObject *)PyCFunction_New(&meth_bpy_escape_identifier, NULL));
- PyModule_AddObject(mod,
- meth_bpy_unescape_identifier.ml_name,
- (PyObject *)PyCFunction_New(&meth_bpy_unescape_identifier, NULL));
- PyModule_AddObject(
- mod, meth_bpy_flip_name.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_flip_name, NULL));
- PyModule_AddObject(mod,
- meth_bpy_context_members.ml_name,
- (PyObject *)PyCFunction_New(&meth_bpy_context_members, NULL));
- PyModule_AddObject(mod,
- meth_bpy_rna_enum_items_static.ml_name,
- (PyObject *)PyCFunction_New(&meth_bpy_rna_enum_items_static, NULL));
+ for (int i = 0; bpy_methods[i].ml_name; i++) {
+ PyMethodDef *m = &bpy_methods[i];
+ /* Currently there is no need to support these. */
+ BLI_assert((m->ml_flags & (METH_CLASS | METH_STATIC)) == 0);
+ PyModule_AddObject(mod, m->ml_name, (PyObject *)PyCFunction_New(m, NULL));
+ }
- /* register funcs (bpy_rna.c) */
+ /* Register functions (`bpy_rna.c`). */
PyModule_AddObject(mod,
meth_bpy_register_class.ml_name,
(PyObject *)PyCFunction_New(&meth_bpy_register_class, NULL));
diff --git a/source/blender/python/intern/bpy.h b/source/blender/python/intern/bpy.h
index 313385b7f5d..f62542bb342 100644
--- a/source/blender/python/intern/bpy.h
+++ b/source/blender/python/intern/bpy.h
@@ -12,7 +12,7 @@ extern "C" {
struct bContext;
-/** Creates the bpy module and adds it to `sys.modules` for importing. */
+/** Creates the `bpy` module and adds it to `sys.modules` for importing. */
void BPy_init_modules(struct bContext *C);
extern PyObject *bpy_package_py;
diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c
index 939473ceaa0..70cf231bc26 100644
--- a/source/blender/python/intern/bpy_app.c
+++ b/source/blender/python/intern/bpy_app.c
@@ -79,8 +79,6 @@ static PyStructSequence_Field app_info_fields[] = {
{"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"},
- {"binary_path",
- "The location of Blender's executable, useful for utilities that open new instances"},
{"background",
"Boolean, True when blender is running without a user interface (started with -b)"},
{"factory_startup", "Boolean, True when blender is running with --factory-startup)"},
@@ -151,7 +149,6 @@ static PyObject *make_app_info(void)
SetStrItem(STRINGIFY(BLENDER_VERSION_CYCLE));
SetStrItem("");
- SetStrItem(BKE_appdir_program_path());
SetObjItem(PyBool_FromLong(G.background));
SetObjItem(PyBool_FromLong(G.factory_startup));
@@ -345,6 +342,33 @@ static PyObject *bpy_app_autoexec_fail_message_get(PyObject *UNUSED(self), void
return PyC_UnicodeFromByte(G.autoexec_fail);
}
+PyDoc_STRVAR(bpy_app_binary_path_doc,
+ "The location of Blender's executable, useful for utilities that open new instances. "
+ "Read-only unless Blender is built as a Python module - in this case the value is "
+ "an empty string which script authors may point to a Blender binary.");
+static PyObject *bpy_app_binary_path_get(PyObject *UNUSED(self), void *UNUSED(closure))
+{
+ return PyC_UnicodeFromByte(BKE_appdir_program_path());
+}
+
+static int bpy_app_binary_path_set(PyObject *UNUSED(self), PyObject *value, void *UNUSED(closure))
+{
+#ifndef WITH_PYTHON_MODULE
+ PyErr_SetString(PyExc_AttributeError,
+ "bpy.app.binary_path is only writable when built as a Python module");
+ return -1;
+#endif
+ PyObject *value_coerce = NULL;
+ const char *filepath = PyC_UnicodeAsByte(value, &value_coerce);
+ if (filepath == NULL) {
+ PyErr_Format(PyExc_ValueError, "expected a string or bytes, got %s", Py_TYPE(value)->tp_name);
+ return -1;
+ }
+ BKE_appdir_program_path_init(filepath);
+ Py_XDECREF(value_coerce);
+ return 0;
+}
+
static PyGetSetDef bpy_app_getsets[] = {
{"debug", bpy_app_debug_get, bpy_app_debug_set, bpy_app_debug_doc, (void *)G_DEBUG},
{"debug_ffmpeg",
@@ -450,7 +474,14 @@ static PyGetSetDef bpy_app_getsets[] = {
(void *)G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET},
{"autoexec_fail_message", bpy_app_autoexec_fail_message_get, NULL, NULL, NULL},
- /* End-of-list marker. */
+ /* Support script authors setting the Blender binary path to use, otherwise this value
+ * is not known when built as a Python module. */
+ {"binary_path",
+ bpy_app_binary_path_get,
+ bpy_app_binary_path_set,
+ bpy_app_binary_path_doc,
+ NULL},
+
{NULL, NULL, NULL, NULL, NULL},
};
@@ -528,7 +559,7 @@ PyObject *BPY_app_struct(void)
BlenderAppType.tp_hash = (hashfunc)
_Py_HashPointer; /* without this we can't do set(sys.modules) T29635. */
- /* kindof a hack ontop of PyStructSequence */
+ /* Kind of a hack on top of #PyStructSequence. */
py_struct_seq_getset_init();
py_struct_seq_method_init();
diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c
index fe5111c37f2..a744f3fd4fa 100644
--- a/source/blender/python/intern/bpy_app_build_options.c
+++ b/source/blender/python/intern/bpy_app_build_options.c
@@ -18,7 +18,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = {
{"codec_avi", NULL},
{"codec_ffmpeg", NULL},
{"codec_sndfile", NULL},
- {"compositor", NULL},
+ {"compositor_cpu", NULL},
{"cycles", NULL},
{"cycles_osl", NULL},
{"freestyle", NULL},
@@ -104,7 +104,7 @@ static PyObject *make_builtopts_info(void)
SetObjIncref(Py_False);
#endif
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
SetObjIncref(Py_True);
#else
SetObjIncref(Py_False);
diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c
index 3f884338bbb..918d96d9f44 100644
--- a/source/blender/python/intern/bpy_app_icons.c
+++ b/source/blender/python/intern/bpy_app_icons.c
@@ -166,7 +166,7 @@ static struct PyModuleDef M_AppIcons_module_def = {
NULL, /* m_doc */
0, /* m_size */
M_AppIcons_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/intern/bpy_app_timers.c b/source/blender/python/intern/bpy_app_timers.c
index 5a42ecfdbc8..4adc200357b 100644
--- a/source/blender/python/intern/bpy_app_timers.c
+++ b/source/blender/python/intern/bpy_app_timers.c
@@ -168,7 +168,7 @@ static struct PyModuleDef M_AppTimers_module_def = {
NULL, /* m_doc */
0, /* m_size */
M_AppTimers_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c
index cc64af2a489..04aa203d198 100644
--- a/source/blender/python/intern/bpy_driver.c
+++ b/source/blender/python/intern/bpy_driver.c
@@ -318,6 +318,7 @@ static const bool secure_opcodes[255] = {
OK_OP(UNARY_INVERT),
OK_OP(BINARY_SUBSCR),
OK_OP(GET_LEN),
+ OK_OP(LIST_TO_TUPLE),
OK_OP(RETURN_VALUE),
OK_OP(SWAP),
OK_OP(BUILD_TUPLE),
@@ -332,6 +333,7 @@ static const bool secure_opcodes[255] = {
OK_OP(POP_JUMP_FORWARD_IF_TRUE),
OK_OP(LOAD_GLOBAL),
OK_OP(IS_OP),
+ OK_OP(CONTAINS_OP),
OK_OP(BINARY_OP),
OK_OP(LOAD_FAST),
OK_OP(STORE_FAST),
@@ -342,6 +344,14 @@ static const bool secure_opcodes[255] = {
OK_OP(LOAD_DEREF),
OK_OP(STORE_DEREF),
OK_OP(RESUME),
+ OK_OP(LIST_EXTEND),
+ OK_OP(SET_UPDATE),
+/* NOTE(@campbellbarton): Don't enable dict manipulation, unless we can prove there is not way it
+ * can be used to manipulate the name-space (potentially allowing malicious code). */
+# if 0
+ OK_OP(DICT_MERGE),
+ OK_OP(DICT_UPDATE),
+# endif
OK_OP(POP_JUMP_BACKWARD_IF_NOT_NONE),
OK_OP(POP_JUMP_BACKWARD_IF_NONE),
OK_OP(POP_JUMP_BACKWARD_IF_FALSE),
@@ -395,6 +405,7 @@ static const bool secure_opcodes[255] = {
OK_OP(INPLACE_AND),
OK_OP(INPLACE_XOR),
OK_OP(INPLACE_OR),
+ OK_OP(LIST_TO_TUPLE),
OK_OP(RETURN_VALUE),
OK_OP(ROT_N),
OK_OP(BUILD_TUPLE),
@@ -410,12 +421,21 @@ static const bool secure_opcodes[255] = {
OK_OP(POP_JUMP_IF_TRUE),
OK_OP(LOAD_GLOBAL),
OK_OP(IS_OP),
+ OK_OP(CONTAINS_OP),
OK_OP(LOAD_FAST),
OK_OP(STORE_FAST),
OK_OP(DELETE_FAST),
OK_OP(BUILD_SLICE),
OK_OP(LOAD_DEREF),
OK_OP(STORE_DEREF),
+ OK_OP(LIST_EXTEND),
+ OK_OP(SET_UPDATE),
+/* NOTE(@campbellbarton): Don't enable dict manipulation, unless we can prove there is not way it
+ * can be used to manipulate the name-space (potentially allowing malicious code). */
+# if 0
+ OK_OP(DICT_MERGE),
+ OK_OP(DICT_UPDATE),
+# endif
/* Special cases. */
OK_OP(LOAD_CONST), /* Ok because constants are accepted. */
@@ -429,9 +449,10 @@ static const bool secure_opcodes[255] = {
# undef OK_OP
-static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code,
- PyObject *dict_arr[],
- const char *error_prefix)
+bool BPY_driver_secure_bytecode_test_ex(PyObject *expr_code,
+ PyObject *namespace_array[],
+ const bool verbose,
+ const char *error_prefix)
{
PyCodeObject *py_code = (PyCodeObject *)expr_code;
@@ -439,21 +460,23 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code,
{
for (int i = 0; i < PyTuple_GET_SIZE(py_code->co_names); i++) {
PyObject *name = PyTuple_GET_ITEM(py_code->co_names, i);
-
+ const char *name_str = PyUnicode_AsUTF8(name);
bool contains_name = false;
- for (int j = 0; dict_arr[j]; j++) {
- if (PyDict_Contains(dict_arr[j], name)) {
+ for (int j = 0; namespace_array[j]; j++) {
+ if (PyDict_Contains(namespace_array[j], name)) {
contains_name = true;
break;
}
}
- if (contains_name == false) {
- fprintf(stderr,
- "\t%s: restricted access disallows name '%s', "
- "enable auto-execution to support\n",
- error_prefix,
- PyUnicode_AsUTF8(name));
+ if ((contains_name == false) || (name_str[0] == '_')) {
+ if (verbose) {
+ fprintf(stderr,
+ "\t%s: restricted access disallows name '%s', "
+ "enable auto-execution to support\n",
+ error_prefix,
+ name_str);
+ }
return false;
}
}
@@ -485,11 +508,13 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code,
for (Py_ssize_t i = 0; i < code_len; i++) {
const int opcode = _Py_OPCODE(codestr[i]);
if (secure_opcodes[opcode] == false) {
- fprintf(stderr,
- "\t%s: restricted access disallows opcode '%d', "
- "enable auto-execution to support\n",
- error_prefix,
- opcode);
+ if (verbose) {
+ fprintf(stderr,
+ "\t%s: restricted access disallows opcode '%d', "
+ "enable auto-execution to support\n",
+ error_prefix,
+ opcode);
+ }
ok = false;
break;
}
@@ -506,6 +531,26 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code,
return true;
}
+bool BPY_driver_secure_bytecode_test(PyObject *expr_code, PyObject *namespace, const bool verbose)
+{
+
+ if (!bpy_pydriver_Dict) {
+ if (bpy_pydriver_create_dict() != 0) {
+ fprintf(stderr, "%s: couldn't create Python dictionary\n", __func__);
+ return false;
+ }
+ }
+ return BPY_driver_secure_bytecode_test_ex(expr_code,
+ (PyObject *[]){
+ bpy_pydriver_Dict,
+ bpy_pydriver_Dict__whitelist,
+ namespace,
+ NULL,
+ },
+ verbose,
+ __func__);
+}
+
#endif /* USE_BYTECODE_WHITELIST */
float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
ChannelDriver *driver,
@@ -697,14 +742,17 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
#ifdef USE_BYTECODE_WHITELIST
if (is_recompile && expr_code) {
if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC)) {
- if (!bpy_driver_secure_bytecode_validate(expr_code,
- (PyObject *[]){
- bpy_pydriver_Dict,
- bpy_pydriver_Dict__whitelist,
- driver_vars,
- NULL,
- },
- __func__)) {
+ if (!BPY_driver_secure_bytecode_test_ex(
+ expr_code,
+ (PyObject *[]){
+ bpy_pydriver_Dict,
+ bpy_pydriver_Dict__whitelist,
+ driver_vars,
+ NULL,
+ },
+ /* Always be verbose since this can give hints to why evaluation fails. */
+ true,
+ __func__)) {
if (!(G.f & G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)) {
G.f |= G_FLAG_SCRIPT_AUTOEXEC_FAIL;
BLI_snprintf(G.autoexec_fail, sizeof(G.autoexec_fail), "Driver '%s'", expr);
diff --git a/source/blender/python/intern/bpy_driver.h b/source/blender/python/intern/bpy_driver.h
index 301c6b3662e..f0d9717dbbd 100644
--- a/source/blender/python/intern/bpy_driver.h
+++ b/source/blender/python/intern/bpy_driver.h
@@ -10,6 +10,8 @@
extern "C" {
#endif
+#include <stdbool.h>
+
/**
* For faster execution we keep a special dictionary for py-drivers, with
* the needed modules and aliases.
@@ -21,6 +23,14 @@ int bpy_pydriver_create_dict(void);
*/
extern PyObject *bpy_pydriver_Dict;
+extern bool BPY_driver_secure_bytecode_test_ex(PyObject *expr_code,
+ PyObject *namespace_array[],
+ const bool verbose,
+ const char *error_prefix);
+extern bool BPY_driver_secure_bytecode_test(PyObject *expr_code,
+ PyObject *namespace,
+ const bool verbose);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/python/intern/bpy_gizmo_wrap.c b/source/blender/python/intern/bpy_gizmo_wrap.c
index ad235e691c4..99720e7a1ba 100644
--- a/source/blender/python/intern/bpy_gizmo_wrap.c
+++ b/source/blender/python/intern/bpy_gizmo_wrap.c
@@ -150,7 +150,7 @@ void BPY_RNA_gizmo_wrapper(wmGizmoType *gzt, void *userdata)
/* don't do translations here yet */
#if 0
- /* Use i18n context from rna_ext.srna if possible (py gizmogroups). */
+ /* Use i18n context from rna_ext.srna if possible (py gizmo-groups). */
if (gt->rna_ext.srna) {
RNA_def_struct_translation_context(gt->srna, RNA_struct_translation_context(gt->rna_ext.srna));
}
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 939fa475344..3a095f4b9f3 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -11,6 +11,10 @@
#include <Python.h>
#include <frameobject.h>
+#ifdef WITH_PYTHON_MODULE
+# include "pylifecycle.h" /* For `Py_Version`. */
+#endif
+
#include "MEM_guardedalloc.h"
#include "CLG_log.h"
@@ -585,6 +589,11 @@ void BPY_python_use_system_env(void)
void BPY_python_backtrace(FILE *fp)
{
fputs("\n# Python backtrace\n", fp);
+
+ /* Can happen in rare cases. */
+ if (!_PyThreadState_UncheckedGet()) {
+ return;
+ }
PyFrameObject *frame;
if (!(frame = PyEval_GetFrame())) {
return;
@@ -763,7 +772,7 @@ static struct PyModuleDef bpy_proxy_def = {
NULL, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
bpy_module_free, /* m_free */
@@ -802,6 +811,50 @@ static void bpy_module_delay_init(PyObject *bpy_proxy)
PyDict_Update(PyModule_GetDict(bpy_proxy), PyModule_GetDict(bpy_package_py));
}
+/**
+ * Raise an error and return false if the Python version used to compile Blender
+ * isn't compatible with the interpreter loading the `bpy` module.
+ */
+static bool bpy_module_ensure_compatible_version(void)
+{
+ /* First check the Python version used matches the major version that Blender was built with.
+ * While this isn't essential, the error message in this case may be cryptic and misleading.
+ * NOTE: using `Py_LIMITED_API` would remove the need for this, in practice it's
+ * unlikely Blender will ever used the limited API though. */
+# if PY_VERSION_HEX >= 0x030b0000 /* Python 3.11 & newer. */
+ const uint version_runtime = Py_Version;
+# else
+ uint version_runtime;
+ {
+ uint version_runtime_major = 0, version_runtime_minor = 0;
+ const char *version_str = Py_GetVersion();
+ if (sscanf(version_str, "%u.%u.", &version_runtime_major, &version_runtime_minor) != 2) {
+ /* Should never happen, raise an error to ensure this check never fails silently. */
+ PyErr_Format(PyExc_ImportError, "Failed to extract the version from \"%s\"", version_str);
+ return false;
+ }
+ version_runtime = (version_runtime_major << 24) | (version_runtime_minor << 16);
+ }
+# endif
+
+ uint version_compile_major = PY_VERSION_HEX >> 24;
+ uint version_compile_minor = ((PY_VERSION_HEX & 0x00ff0000) >> 16);
+ uint version_runtime_major = version_runtime >> 24;
+ uint version_runtime_minor = ((version_runtime & 0x00ff0000) >> 16);
+ if ((version_compile_major != version_runtime_major) ||
+ (version_compile_minor != version_runtime_minor)) {
+ PyErr_Format(PyExc_ImportError,
+ "The version of \"bpy\" was compiled with: "
+ "(%u.%u) is incompatible with: (%u.%u) used by the interpreter!",
+ version_compile_major,
+ version_compile_minor,
+ version_runtime_major,
+ version_runtime_minor);
+ return false;
+ }
+ return true;
+}
+
static void dealloc_obj_dealloc(PyObject *self);
static PyTypeObject dealloc_obj_Type;
@@ -819,6 +872,10 @@ PyMODINIT_FUNC PyInit_bpy(void);
PyMODINIT_FUNC PyInit_bpy(void)
{
+ if (!bpy_module_ensure_compatible_version()) {
+ return NULL; /* The error has been set. */
+ }
+
PyObject *bpy_proxy = PyModule_Create(&bpy_proxy_def);
/* Problem:
diff --git a/source/blender/python/intern/bpy_interface_atexit.c b/source/blender/python/intern/bpy_interface_atexit.c
index 1ba3ab6a40b..cba9bd59abf 100644
--- a/source/blender/python/intern/bpy_interface_atexit.c
+++ b/source/blender/python/intern/bpy_interface_atexit.c
@@ -32,7 +32,7 @@ static PyObject *func_bpy_atregister = NULL; /* borrowed reference, `atexit` hol
static void atexit_func_call(const char *func_name, PyObject *atexit_func_arg)
{
- /* NOTE(campbell): no error checking, if any of these fail we'll get a crash
+ /* NOTE(@campbellbarton): no error checking, if any of these fail we'll get a crash
* this is intended, but if its problematic it could be changed. */
PyObject *atexit_mod = PyImport_ImportModuleLevel("atexit", NULL, NULL, NULL, 0);
diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c
index 9754599eeed..f29a8d34359 100644
--- a/source/blender/python/intern/bpy_library_load.c
+++ b/source/blender/python/intern/bpy_library_load.c
@@ -108,7 +108,7 @@ static PyTypeObject bpy_lib_Type = {
NULL, /* ternaryfunc tp_call; */
NULL, /* reprfunc tp_str; */
- /* will only use these if this is a subtype of a py class */
+ /* Will only use these if this is a sub-type of a Python class. */
PyObject_GenericGetAttr, /* getattrofunc tp_getattro; */
NULL, /* setattrofunc tp_setattro; */
diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c
index 95879b02295..2db8c08cfd4 100644
--- a/source/blender/python/intern/bpy_operator.c
+++ b/source/blender/python/intern/bpy_operator.c
@@ -289,7 +289,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args)
reports = MEM_mallocN(sizeof(ReportList), "wmOperatorReportList");
/* Own so these don't move into global reports. */
- BKE_reports_init(reports, RPT_STORE | RPT_OP_HOLD);
+ BKE_reports_init(reports, RPT_STORE | RPT_OP_HOLD | RPT_PRINT_HANDLED_BY_OWNER);
#ifdef BPY_RELEASE_GIL
/* release GIL, since a thread could be started from an operator
diff --git a/source/blender/python/intern/bpy_path.c b/source/blender/python/intern/bpy_path.c
index fbb47817389..f3a1a7cb1df 100644
--- a/source/blender/python/intern/bpy_path.c
+++ b/source/blender/python/intern/bpy_path.c
@@ -26,7 +26,7 @@ static struct PyModuleDef _bpy_path_module_def = {
NULL, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 14be9ceda94..07ce44f5f3b 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -15,6 +15,7 @@
#include <float.h> /* FLT_MIN/MAX */
#include <stddef.h>
+#include "RNA_path.h"
#include "RNA_types.h"
#include "BLI_bitmap.h"
@@ -779,7 +780,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop)
return ret;
}
-/* NOTE(campbell): Regarding comparison `__cmp__`:
+/* NOTE(@campbellbarton): Regarding comparison `__cmp__`:
* checking the 'ptr->data' matches works in almost all cases,
* however there are a few RNA properties that are fake sub-structs and
* share the pointer with the parent, in those cases this happens 'a.b == a'
@@ -1396,7 +1397,7 @@ PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
buf = RNA_property_string_get_alloc(ptr, prop, buf_fixed, sizeof(buf_fixed), &buf_len);
#ifdef USE_STRING_COERCE
- /* Only file paths get special treatment, they may contain non utf-8 chars. */
+ /* Only file paths get special treatment, they may contain non UTF-8 chars. */
if (subtype == PROP_BYTESTRING) {
ret = PyBytes_FromStringAndSize(buf, buf_len);
}
@@ -2212,6 +2213,26 @@ static int pyrna_prop_collection_bool(BPy_PropertyRNA *self)
} \
(void)0
+/**
+ * \param result: The result of calling a subscription operation on a collection (never NULL).
+ */
+static int pyrna_prop_collection_subscript_is_valid_or_error(const PyObject *value)
+{
+ if (value != Py_None) {
+ BLI_assert(BPy_StructRNA_Check(value));
+ const BPy_StructRNA *value_pyrna = (const BPy_StructRNA *)value;
+ if (UNLIKELY(value_pyrna->ptr.type == NULL)) {
+ /* It's important to use a `TypeError` as that is what's returned when `__getitem__` is
+ * called on an object that doesn't support item access. */
+ PyErr_Format(PyExc_TypeError,
+ "'%.200s' object is not subscriptable (only iteration is supported)",
+ Py_TYPE(value)->tp_name);
+ return -1;
+ }
+ }
+ return 0;
+}
+
/* Internal use only. */
static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_ssize_t keynum)
{
@@ -2222,8 +2243,35 @@ static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_s
PYRNA_PROP_COLLECTION_ABS_INDEX(NULL);
- if (RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) {
- return pyrna_struct_CreatePyObject(&newptr);
+ if (RNA_property_collection_lookup_int_has_fn(self->prop)) {
+ if (RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) {
+ return pyrna_struct_CreatePyObject(&newptr);
+ }
+ }
+ else {
+ /* No callback defined, just iterate and find the nth item. */
+ const int key = (int)keynum_abs;
+ PyObject *result = NULL;
+ bool found = false;
+ CollectionPropertyIterator iter;
+ RNA_property_collection_begin(&self->ptr, self->prop, &iter);
+ for (int i = 0; iter.valid; RNA_property_collection_next(&iter), i++) {
+ if (i == key) {
+ result = pyrna_struct_CreatePyObject(&iter.ptr);
+ found = true;
+ break;
+ }
+ }
+ /* It's important to end the iterator after `result` has been created
+ * so iterators may optionally invalidate items that were iterated over, see: T100286. */
+ RNA_property_collection_end(&iter);
+ if (found) {
+ if (result && (pyrna_prop_collection_subscript_is_valid_or_error(result) == -1)) {
+ Py_DECREF(result);
+ result = NULL; /* The exception has been set. */
+ }
+ return result;
+ }
}
const int len = RNA_property_collection_length(&self->ptr, self->prop);
@@ -2305,8 +2353,45 @@ static PyObject *pyrna_prop_collection_subscript_str(BPy_PropertyRNA *self, cons
PYRNA_PROP_CHECK_OBJ(self);
- if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr)) {
- return pyrna_struct_CreatePyObject(&newptr);
+ if (RNA_property_collection_lookup_string_has_fn(self->prop)) {
+ if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr)) {
+ return pyrna_struct_CreatePyObject(&newptr);
+ }
+ }
+ else {
+ /* No callback defined, just iterate and find the nth item. */
+ const int keylen = strlen(keyname);
+ char name[256];
+ int namelen;
+ PyObject *result = NULL;
+ bool found = false;
+ CollectionPropertyIterator iter;
+ RNA_property_collection_begin(&self->ptr, self->prop, &iter);
+ for (int i = 0; iter.valid; RNA_property_collection_next(&iter), i++) {
+ PropertyRNA *nameprop = RNA_struct_name_property(iter.ptr.type);
+ char *nameptr = RNA_property_string_get_alloc(
+ &iter.ptr, nameprop, name, sizeof(name), &namelen);
+ if ((keylen == namelen) && STREQ(nameptr, keyname)) {
+ found = true;
+ }
+ if ((char *)&name != nameptr) {
+ MEM_freeN(nameptr);
+ }
+ if (found) {
+ result = pyrna_struct_CreatePyObject(&iter.ptr);
+ break;
+ }
+ }
+ /* It's important to end the iterator after `result` has been created
+ * so iterators may optionally invalidate items that were iterated over, see: T100286. */
+ RNA_property_collection_end(&iter);
+ if (found) {
+ if (result && (pyrna_prop_collection_subscript_is_valid_or_error(result) == -1)) {
+ Py_DECREF(result);
+ result = NULL; /* The exception has been set. */
+ }
+ return result;
+ }
}
PyErr_Format(PyExc_KeyError, "bpy_prop_collection[key]: key \"%.200s\" not found", keyname);
@@ -3989,7 +4074,7 @@ static void pyrna_dir_members_rna(PyObject *list, PointerRNA *ptr)
{
const char *idname;
- /* For looping over attrs and funcs. */
+ /* For looping over attributes and functions. */
PointerRNA tptr;
PropertyRNA *iterprop;
@@ -5852,7 +5937,7 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat
len = RNA_property_array_length(ptr, prop);
}
- /* Resolve the array from a new pytype. */
+ /* Resolve the array from a new Python type. */
/* TODO(Kazanbas): make multi-dimensional sequences here. */
@@ -6402,7 +6487,7 @@ static PyObject *pyrna_func_doc_get(BPy_FunctionRNA *self, void *UNUSED(closure)
PyTypeObject pyrna_struct_meta_idprop_Type = {
PyVarObject_HEAD_INIT(NULL, 0) "bpy_struct_meta_idprop", /* tp_name */
- /* NOTE: would be PyTypeObject, but subtypes of Type must be PyHeapTypeObject's. */
+ /* NOTE: would be PyTypeObject, but sub-types of Type must be PyHeapTypeObject's. */
sizeof(PyHeapTypeObject), /* tp_basicsize */
0, /* tp_itemsize */
@@ -7223,7 +7308,7 @@ static PyObject *pyrna_srna_PyBase(StructRNA *srna) //, PyObject *bpy_types_dic
if (base && base != srna) {
// printf("debug subtype %s %p\n", RNA_struct_identifier(srna), srna);
py_base = pyrna_srna_Subtype(base); //, bpy_types_dict);
- Py_DECREF(py_base); /* Srna owns, this is only to pass as an arg. */
+ Py_DECREF(py_base); /* `srna` owns, this is only to pass as an argument. */
}
if (py_base == NULL) {
@@ -7783,7 +7868,7 @@ static struct PyModuleDef bpy_types_module_def = {
bpy_types_module_doc, /* m_doc */
sizeof(struct BPy_TypesModule_State), /* m_size */
bpy_types_module_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
@@ -8253,7 +8338,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr,
continue;
}
- /* TODO(campbell): this is used for classmethod's too,
+ /* TODO(@campbellbarton): this is used for classmethod's too,
* even though class methods should have 'FUNC_USE_SELF_TYPE' set, see Operator.poll for eg.
* Keep this as-is since it's working, but we should be using
* 'FUNC_USE_SELF_TYPE' for many functions. */
@@ -8344,7 +8429,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr,
continue;
}
- /* TODO(campbell): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */
+ /* TODO(@campbellbarton): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */
identifier = RNA_property_identifier(prop);
item = PyObject_GetAttrString(py_class, identifier);
@@ -8650,10 +8735,10 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
else if (ret_len == 1) {
err = pyrna_py_to_prop(&funcptr, pret_single, retdata_single, ret, "");
- /* when calling operator funcs only gives Function.result with
- * no line number since the func has finished calling on error,
- * re-raise the exception with more info since it would be slow to
- * create prefix on every call (when there are no errors) */
+ /* When calling operator functions only gives `Function.result` with no line number
+ * since the function has finished calling on error, re-raise the exception with more
+ * information since it would be slow to create prefix on every call
+ * (when there are no errors). */
if (err == -1) {
PyC_Err_Format_Prefix(PyExc_RuntimeError,
"class %.200s, function %.200s: incompatible return value ",
@@ -8761,7 +8846,7 @@ static void bpy_class_free(void *pyob_ptr)
}
/**
- * \note This isn't essential to run on startup, since subtypes will lazy initialize.
+ * \note This isn't essential to run on startup, since sub-types will lazy initialize.
* But keep running in debug mode so we get immediate notification of bad class hierarchy
* or any errors in "bpy_types.py" at load time, so errors don't go unnoticed.
*/
diff --git a/source/blender/python/intern/bpy_rna.h b/source/blender/python/intern/bpy_rna.h
index 91fa0ea2c8d..d7778da6213 100644
--- a/source/blender/python/intern/bpy_rna.h
+++ b/source/blender/python/intern/bpy_rna.h
@@ -218,11 +218,13 @@ void pyrna_struct_type_extend_capi(struct StructRNA *srna,
struct PyMethodDef *py_method,
struct PyGetSetDef *py_getset);
-/* called before stopping python */
+/* Called before stopping Python. */
+
void pyrna_alloc_types(void);
void pyrna_free_types(void);
-/* primitive type conversion */
+/* Primitive type conversion. */
+
int pyrna_py_to_array(
PointerRNA *ptr, PropertyRNA *prop, char *param_data, PyObject *py, const char *error_prefix);
int pyrna_py_to_array_index(PointerRNA *ptr,
diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c
index fb744432d4b..d4a164d9482 100644
--- a/source/blender/python/intern/bpy_rna_anim.c
+++ b/source/blender/python/intern/bpy_rna_anim.c
@@ -32,6 +32,7 @@
#include "RNA_access.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "WM_api.h"
@@ -487,7 +488,8 @@ PyObject *pyrna_struct_keyframe_delete(BPy_StructRNA *self, PyObject *args, PyOb
i = BKE_fcurve_bezt_binarysearch_index(fcu->bezt, cfra, fcu->totvert, &found);
if (found) {
/* delete the key at the index (will sanity check + do recalc afterwards) */
- delete_fcurve_key(fcu, i, 1);
+ BKE_fcurve_delete_key(fcu, i);
+ BKE_fcurve_handles_recalc(fcu);
result = true;
}
}
diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c
index 8506ec97bc3..48ba028edf0 100644
--- a/source/blender/python/intern/bpy_rna_array.c
+++ b/source/blender/python/intern/bpy_rna_array.c
@@ -927,7 +927,7 @@ PyObject *pyrna_py_from_array_index(BPy_PropertyArrayRNA *self,
/* just in case check */
len = RNA_property_multi_array_length(ptr, prop, arraydim);
if (index >= len || index < 0) {
- /* this shouldn't happen because higher level funcs must check for invalid index */
+ /* This shouldn't happen because higher level functions must check for invalid index. */
CLOG_WARN(BPY_LOG_RNA, "invalid index %d for array with length=%d", index, len);
PyErr_SetString(PyExc_IndexError, "out of range");
diff --git a/source/blender/python/intern/bpy_rna_data.c b/source/blender/python/intern/bpy_rna_data.c
index cc0b4aa57d5..a3e865f4846 100644
--- a/source/blender/python/intern/bpy_rna_data.c
+++ b/source/blender/python/intern/bpy_rna_data.c
@@ -86,7 +86,7 @@ static PyTypeObject bpy_rna_data_context_Type = {
NULL, /* ternaryfunc tp_call; */
NULL, /* reprfunc tp_str; */
- /* will only use these if this is a subtype of a py class */
+ /* Will only use these if this is a sub-type of a Python class. */
NULL, /* getattrofunc tp_getattro; */
NULL, /* setattrofunc tp_setattro; */
diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c
index 61f439e5152..32ef7865e84 100644
--- a/source/blender/python/intern/bpy_rna_gizmo.c
+++ b/source/blender/python/intern/bpy_rna_gizmo.c
@@ -314,6 +314,8 @@ PyDoc_STRVAR(
"\n"
" Assigns callbacks to a gizmos property.\n"
"\n"
+ " :arg target: Target property name.\n"
+ " :type target: string\n"
" :arg get: Function that returns the value for this property (single value or sequence).\n"
" :type get: callable\n"
" :arg set: Function that takes a single value argument and applies it.\n"
diff --git a/source/blender/python/intern/bpy_rna_operator.c b/source/blender/python/intern/bpy_rna_operator.c
index fd6cc93ed32..e0a4356dc18 100644
--- a/source/blender/python/intern/bpy_rna_operator.c
+++ b/source/blender/python/intern/bpy_rna_operator.c
@@ -84,7 +84,7 @@ PyDoc_STRVAR(BPY_rna_operator_poll_message_set_doc,
" 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"
+ " :arg 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)
diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c
index c3a07847aff..2b830eb9ffe 100644
--- a/source/blender/python/intern/bpy_rna_types_capi.c
+++ b/source/blender/python/intern/bpy_rna_types_capi.c
@@ -185,16 +185,16 @@ PyDoc_STRVAR(
" It will be called every time the specified region in the space type will be drawn.\n"
" Note: All arguments are positional only for now.\n"
"\n"
- " :param callback:\n"
+ " :arg callback:\n"
" A function that will be called when the region is drawn.\n"
" It gets the specified arguments as input.\n"
" :type callback: function\n"
- " :param args: Arguments that will be passed to the callback.\n"
+ " :arg args: Arguments that will be passed to the callback.\n"
" :type args: tuple\n"
- " :param region_type: The region type the callback draws in; usually ``WINDOW``. "
+ " :arg region_type: The region type the callback draws in; usually ``WINDOW``. "
"(:class:`bpy.types.Region.type`)\n"
" :type region_type: str\n"
- " :param draw_type: Usually ``POST_PIXEL`` for 2D drawing and ``POST_VIEW`` for 3D drawing. "
+ " :arg draw_type: Usually ``POST_PIXEL`` for 2D drawing and ``POST_VIEW`` for 3D drawing. "
"In some cases ``PRE_VIEW`` can be used. ``BACKDROP`` can be used for backdrops in the node "
"editor.\n"
" :type draw_type: str\n"
@@ -206,9 +206,9 @@ PyDoc_STRVAR(pyrna_draw_handler_remove_doc,
"\n"
" Remove a draw handler that was added previously.\n"
"\n"
- " :param handler: The draw handler that should be removed.\n"
+ " :arg handler: The draw handler that should be removed.\n"
" :type handler: object\n"
- " :param region_type: Region type the callback was added to.\n"
+ " :arg region_type: Region type the callback was added to.\n"
" :type region_type: str\n");
static struct PyMethodDef pyrna_space_methods[] = {
diff --git a/source/blender/python/intern/bpy_traceback.c b/source/blender/python/intern/bpy_traceback.c
index 1f2458c752f..428263fd4c4 100644
--- a/source/blender/python/intern/bpy_traceback.c
+++ b/source/blender/python/intern/bpy_traceback.c
@@ -25,7 +25,7 @@ static const char *traceback_filepath(PyTracebackObject *tb, PyObject **coerce)
return PyBytes_AS_STRING(*coerce);
}
-/* copied from pythonrun.c, 3.10.0 */
+/* Copied from `pythonrun.c`, 3.10.0 */
_Py_static_string(PyId_string, "<string>");
static int parse_syntax_error(PyObject *err,
diff --git a/source/blender/python/intern/bpy_utils_units.c b/source/blender/python/intern/bpy_utils_units.c
index 1e5856a3285..075a68f31f9 100644
--- a/source/blender/python/intern/bpy_utils_units.c
+++ b/source/blender/python/intern/bpy_utils_units.c
@@ -90,7 +90,7 @@ static PyObject *py_structseq_from_strings(PyTypeObject *py_type,
const char **str_iter;
PyStructSequence_Field *desc;
- /* initialize array */
+ /* Initialize array. */
/* We really populate the contexts' fields here! */
for (str_iter = str_items, desc = py_sseq_desc->fields; *str_iter; str_iter++, desc++) {
desc->name = (char *)*str_iter;
@@ -101,7 +101,7 @@ static PyObject *py_structseq_from_strings(PyTypeObject *py_type,
PyStructSequence_InitType(py_type, py_sseq_desc);
- /* initialize pytype */
+ /* Initialize the Python type. */
py_struct_seq = PyStructSequence_New(py_type);
BLI_assert(py_struct_seq != NULL);
diff --git a/source/blender/python/intern/stubs.c b/source/blender/python/intern/stubs.c
index c29f9188eea..f860bdc36ee 100644
--- a/source/blender/python/intern/stubs.c
+++ b/source/blender/python/intern/stubs.c
@@ -25,7 +25,7 @@ void BPY_pyconstraint_exec(struct bPythonConstraint *con,
void BPY_pyconstraint_target(struct bPythonConstraint *con, struct bConstraintTarget *ct)
{
}
-int BPY_is_pyconstraint(struct Text *text)
+bool BPY_is_pyconstraint(struct Text *text)
{
return 0;
}
diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c
index 1aa2cec861c..7cba95a1f85 100644
--- a/source/blender/python/mathutils/mathutils.c
+++ b/source/blender/python/mathutils/mathutils.c
@@ -710,7 +710,7 @@ void BaseMathObject_dealloc(BaseMathObject *self)
BaseMathObject_clear(self);
}
- Py_TYPE(self)->tp_free(self); // PyObject_DEL(self); /* breaks subtypes. */
+ Py_TYPE(self)->tp_free(self); // PyObject_DEL(self); /* breaks sub-types. */
}
/*----------------------------MODULE INIT-------------------------*/
@@ -724,7 +724,7 @@ static struct PyModuleDef M_Mathutils_module_def = {
M_Mathutils_doc, /* m_doc */
0, /* m_size */
M_Mathutils_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/mathutils/mathutils.h b/source/blender/python/mathutils/mathutils.h
index 84386e99d18..27d84a80601 100644
--- a/source/blender/python/mathutils/mathutils.h
+++ b/source/blender/python/mathutils/mathutils.h
@@ -6,7 +6,7 @@
* \ingroup pymathutils
*/
-/* Can cast different mathutils types to this, use for generic funcs */
+/* Can cast different mathutils types to this, use for generic functions. */
#include "BLI_compiler_attrs.h"
@@ -21,7 +21,7 @@ extern char BaseMathObject_owner_doc[];
((struct_name *)((base_type ? (base_type)->tp_alloc(base_type, 0) : \
_PyObject_GC_New(&(root_type)))))
-/** BaseMathObject.flag */
+/** #BaseMathObject.flag */
enum {
/**
* Do not own the memory used in this vector,
@@ -43,9 +43,9 @@ enum {
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 */ \
+ /** Which user functions do we adhere to, RNA, etc */ \
unsigned char cb_type; \
- /** Subtype: location, rotation... \
+ /** Sub-type: location, rotation... \
* to avoid defining many new functions for every attribute of the same type */ \
unsigned char cb_subtype; \
/** Wrapped data type. */ \
diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c
index 88e8d880360..a66178baefc 100644
--- a/source/blender/python/mathutils/mathutils_Color.c
+++ b/source/blender/python/mathutils/mathutils_Color.c
@@ -14,7 +14,9 @@
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
-#include "IMB_colormanagement.h"
+#ifndef MATH_STANDALONE
+# include "IMB_colormanagement.h"
+#endif
#ifndef MATH_STANDALONE
# include "BLI_dynstr.h"
@@ -92,6 +94,8 @@ static PyObject *Color_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
/** \name Color Methods: Color Space Conversion
* \{ */
+#ifndef MATH_STANDALONE
+
PyDoc_STRVAR(Color_from_scene_linear_to_srgb_doc,
".. function:: from_scene_linear_to_srgb()\n"
"\n"
@@ -204,6 +208,8 @@ static PyObject *Color_from_rec709_linear_to_scene_linear(ColorObject *self)
return Color_CreatePyObject(col, Py_TYPE(self));
}
+#endif /* MATH_STANDALONE */
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1050,7 +1056,8 @@ static struct PyMethodDef Color_methods[] = {
/* base-math methods */
{"freeze", (PyCFunction)BaseMathObject_freeze, METH_NOARGS, BaseMathObject_freeze_doc},
- /* Color-space methods. */
+/* Color-space methods. */
+#ifndef MATH_STANDALONE
{"from_scene_linear_to_srgb",
(PyCFunction)Color_from_scene_linear_to_srgb,
METH_NOARGS,
@@ -1083,6 +1090,8 @@ static struct PyMethodDef Color_methods[] = {
(PyCFunction)Color_from_rec709_linear_to_scene_linear,
METH_NOARGS,
Color_from_rec709_linear_to_scene_linear_doc},
+#endif /* MATH_STANDALONE */
+
{NULL, NULL, 0, NULL},
};
@@ -1102,7 +1111,7 @@ PyDoc_STRVAR(
" the OpenColorIO configuration. The notable exception is user interface theming colors, "
" which are in sRGB color space.\n"
"\n"
- " :param rgb: (r, g, b) color values\n"
+ " :arg rgb: (r, g, b) color values\n"
" :type rgb: 3d vector\n");
PyTypeObject color_Type = {
PyVarObject_HEAD_INIT(NULL, 0) "Color", /* tp_name */
diff --git a/source/blender/python/mathutils/mathutils_Euler.c b/source/blender/python/mathutils/mathutils_Euler.c
index f49868dfba7..8f950bce344 100644
--- a/source/blender/python/mathutils/mathutils_Euler.c
+++ b/source/blender/python/mathutils/mathutils_Euler.c
@@ -774,9 +774,9 @@ PyDoc_STRVAR(
"\n"
" .. seealso:: `Euler angles <https://en.wikipedia.org/wiki/Euler_angles>`__ on Wikipedia.\n"
"\n"
- " :param angles: Three angles, in radians.\n"
+ " :arg angles: Three angles, in radians.\n"
" :type angles: 3d vector\n"
- " :param order: Optional order of the angles, a permutation of ``XYZ``.\n"
+ " :arg order: Optional order of the angles, a permutation of ``XYZ``.\n"
" :type order: str\n");
PyTypeObject euler_Type = {
PyVarObject_HEAD_INIT(NULL, 0) "Euler", /* tp_name */
diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c
index 8cd7a5c7d87..af01c571fe9 100644
--- a/source/blender/python/mathutils/mathutils_Matrix.c
+++ b/source/blender/python/mathutils/mathutils_Matrix.c
@@ -290,6 +290,18 @@ static PyObject *matrix__apply_to_copy(PyObject *(*matrix_func)(MatrixObject *),
return NULL;
}
+static bool matrix_is_identity(MatrixObject *self)
+{
+ for (int row = 0; row < self->row_num; row++) {
+ for (int col = 0; col < self->col_num; col++) {
+ if (MATRIX_ITEM(self, row, col) != ((row != col) ? 0.0f : 1.0f)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -1232,12 +1244,11 @@ static PyObject *Matrix_to_quaternion(MatrixObject *self)
return NULL;
}
if (self->row_num == 3) {
- mat3_to_quat(quat, (float(*)[3])self->matrix);
+ mat3_to_quat(quat, (const float(*)[3])self->matrix);
}
else {
mat4_to_quat(quat, (const float(*)[4])self->matrix);
}
-
return Quaternion_CreatePyObject(quat, NULL);
}
@@ -1876,7 +1887,7 @@ static PyObject *Matrix_decompose(MatrixObject *self)
}
mat4_to_loc_rot_size(loc, rot, size, (const float(*)[4])self->matrix);
- mat3_to_quat(quat, rot);
+ mat3_normalized_to_quat_fast(quat, rot);
ret = PyTuple_New(3);
PyTuple_SET_ITEMS(ret,
@@ -3104,6 +3115,16 @@ static PyObject *Matrix_median_scale_get(MatrixObject *self, void *UNUSED(closur
return PyFloat_FromDouble(mat3_to_scale(mat));
}
+PyDoc_STRVAR(Matrix_is_identity_doc,
+ "True if this is an identity matrix (read-only).\n\n:type: bool");
+static PyObject *Matrix_is_identity_get(MatrixObject *self, void *UNUSED(closure))
+{
+ if (BaseMath_ReadCallback(self) == -1) {
+ return NULL;
+ }
+ return PyBool_FromLong(matrix_is_identity(self));
+}
+
PyDoc_STRVAR(Matrix_is_negative_doc,
"True if this matrix results in a negative scale, 3x3 and 4x4 only, "
"(read-only).\n\n:type: bool");
@@ -3187,6 +3208,7 @@ static PyGetSetDef Matrix_getseters[] = {
NULL},
{"row", (getter)Matrix_row_get, (setter)NULL, Matrix_row_doc, NULL},
{"col", (getter)Matrix_col_get, (setter)NULL, Matrix_col_doc, NULL},
+ {"is_identity", (getter)Matrix_is_identity_get, (setter)NULL, Matrix_is_identity_doc, NULL},
{"is_negative", (getter)Matrix_is_negative_get, (setter)NULL, Matrix_is_negative_doc, NULL},
{"is_orthogonal",
(getter)Matrix_is_orthogonal_get,
@@ -3299,8 +3321,7 @@ PyDoc_STRVAR(
" This object gives access to Matrices in Blender, supporting square and rectangular\n"
" matrices from 2x2 up to 4x4.\n"
"\n"
- " :param rows: Sequence of rows.\n"
- " When omitted, a 4x4 identity matrix is constructed.\n"
+ " :arg rows: Sequence of rows. When omitted, a 4x4 identity matrix is constructed.\n"
" :type rows: 2d number sequence\n");
PyTypeObject matrix_Type = {
PyVarObject_HEAD_INIT(NULL, 0) "Matrix", /*tp_name*/
diff --git a/source/blender/python/mathutils/mathutils_Quaternion.c b/source/blender/python/mathutils/mathutils_Quaternion.c
index 6994a313237..d405d5e63ce 100644
--- a/source/blender/python/mathutils/mathutils_Quaternion.c
+++ b/source/blender/python/mathutils/mathutils_Quaternion.c
@@ -543,7 +543,6 @@ static PyObject *Quaternion_rotate(QuaternionObject *self, PyObject *value)
length = normalize_qt_qt(tquat, self->quat);
quat_to_mat3(self_rmat, tquat);
mul_m3_m3m3(rmat, other_rmat, self_rmat);
-
mat3_to_quat(self->quat, rmat);
mul_qt_fl(self->quat, length); /* maintain length after rotating */
@@ -1663,9 +1662,9 @@ PyDoc_STRVAR(quaternion_doc,
"\n"
" This object gives access to Quaternions in Blender.\n"
"\n"
- " :param seq: size 3 or 4\n"
+ " :arg seq: size 3 or 4\n"
" :type seq: :class:`Vector`\n"
- " :param angle: rotation angle, in radians\n"
+ " :arg angle: rotation angle, in radians\n"
" :type angle: float\n"
"\n"
" The constructor takes arguments in various forms:\n"
diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c
index 0c9cbd6ccfa..cf684341d4f 100644
--- a/source/blender/python/mathutils/mathutils_Vector.c
+++ b/source/blender/python/mathutils/mathutils_Vector.c
@@ -3188,7 +3188,7 @@ PyDoc_STRVAR(vector_doc,
"\n"
" This object gives access to Vectors in Blender.\n"
"\n"
- " :param seq: Components of the vector, must be a sequence of at least two\n"
+ " :arg seq: Components of the vector, must be a sequence of at least two\n"
" :type seq: sequence of numbers\n");
PyTypeObject vector_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c
index ead255a6716..4bdb1adcdde 100644
--- a/source/blender/python/mathutils/mathutils_bvhtree.c
+++ b/source/blender/python/mathutils/mathutils_bvhtree.c
@@ -1148,12 +1148,12 @@ static PyObject *C_BVHTree_FromObject(PyObject *UNUSED(cls), PyObject *args, PyO
coords = MEM_mallocN(sizeof(*coords) * (size_t)coords_len, __func__);
tris = MEM_mallocN(sizeof(*tris) * (size_t)tris_len, __func__);
- MVert *mv = mesh->mvert;
- for (int i = 0; i < mesh->totvert; i++, mv++) {
- copy_v3_v3(coords[i], mv->co);
+ const MVert *verts = BKE_mesh_verts(mesh);
+ for (int i = 0; i < mesh->totvert; i++) {
+ copy_v3_v3(coords[i], verts[i].co);
}
- mloop = mesh->mloop;
+ mloop = BKE_mesh_loops(mesh);
}
{
@@ -1294,7 +1294,7 @@ static struct PyModuleDef bvhtree_moduledef = {
py_bvhtree_doc, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c
index 1e492574903..28deebcf5ac 100644
--- a/source/blender/python/mathutils/mathutils_geometry.c
+++ b/source/blender/python/mathutils/mathutils_geometry.c
@@ -1790,7 +1790,7 @@ static struct PyModuleDef M_Geometry_module_def = {
M_Geometry_doc, /* m_doc */
0, /* m_size */
M_Geometry_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/mathutils/mathutils_interpolate.c b/source/blender/python/mathutils/mathutils_interpolate.c
index 6523b3f7259..10f42d9b070 100644
--- a/source/blender/python/mathutils/mathutils_interpolate.c
+++ b/source/blender/python/mathutils/mathutils_interpolate.c
@@ -93,7 +93,7 @@ static struct PyModuleDef M_Interpolate_module_def = {
M_Interpolate_doc, /* m_doc */
0, /* m_size */
M_Interpolate_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c
index 66c48697b6b..f5a27c6f90f 100644
--- a/source/blender/python/mathutils/mathutils_kdtree.c
+++ b/source/blender/python/mathutils/mathutils_kdtree.c
@@ -428,7 +428,7 @@ static struct PyModuleDef kdtree_moduledef = {
py_kdtree_doc, /* m_doc */
0, /* m_size */
NULL, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c
index e1282e90c48..3a3297f27f7 100644
--- a/source/blender/python/mathutils/mathutils_noise.c
+++ b/source/blender/python/mathutils/mathutils_noise.c
@@ -1089,7 +1089,7 @@ static struct PyModuleDef M_Noise_module_def = {
M_Noise_doc, /* m_doc */
0, /* m_size */
M_Noise_methods, /* m_methods */
- NULL, /* m_reload */
+ NULL, /* m_slots */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt
index db406ae96f2..b17dfe7a6c1 100644
--- a/source/blender/render/CMakeLists.txt
+++ b/source/blender/render/CMakeLists.txt
@@ -17,6 +17,7 @@ set(INC
../nodes
../sequencer
../simulation
+ ../windowmanager
../../../intern/atomic
../../../intern/guardedalloc
../../../intern/mikktspace
@@ -26,11 +27,11 @@ set(INC
set(SRC
intern/bake.c
- intern/engine.c
- intern/initrender.c
+ intern/engine.cc
+ intern/initrender.cc
intern/multires_bake.c
- intern/pipeline.c
- intern/render_result.c
+ intern/pipeline.cc
+ intern/render_result.cc
intern/texture_image.c
intern/texture_margin.cc
intern/texture_pointdensity.c
diff --git a/source/blender/render/RE_bake.h b/source/blender/render/RE_bake.h
index 56c66df5925..ebfc7509504 100644
--- a/source/blender/render/RE_bake.h
+++ b/source/blender/render/RE_bake.h
@@ -7,6 +7,8 @@
#pragma once
+#include "RE_pipeline.h"
+
struct Depsgraph;
struct ImBuf;
struct MLoopUV;
@@ -24,6 +26,9 @@ typedef struct BakeImage {
int width;
int height;
size_t offset;
+
+ /* For associating render result layer with image. */
+ char render_layer_name[RE_MAXNAME];
} BakeImage;
typedef struct BakeTargets {
diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h
index d7815ef3f3c..d5ad70e5a03 100644
--- a/source/blender/render/RE_engine.h
+++ b/source/blender/render/RE_engine.h
@@ -15,6 +15,7 @@
#include "BLI_threads.h"
+struct BakeTargets;
struct BakePixel;
struct Depsgraph;
struct Main;
@@ -140,9 +141,10 @@ typedef struct RenderEngine {
struct ReportList *reports;
struct {
+ const struct BakeTargets *targets;
const struct BakePixel *pixels;
float *result;
- int width, height, depth;
+ int image_id;
int object_id;
} bake;
@@ -155,10 +157,10 @@ typedef struct RenderEngine {
update_render_passes_cb_t update_render_passes_cb;
void *update_render_passes_data;
- rctf last_viewplane;
- rcti last_disprect;
- float last_viewmat[4][4];
- int last_winx, last_winy;
+ /* GPU context. */
+ void *gpu_context;
+ ThreadMutex gpu_context_mutex;
+ bool use_drw_render_context;
} RenderEngine;
RenderEngine *RE_engine_create(RenderEngineType *type);
@@ -244,11 +246,17 @@ struct RenderEngine *RE_engine_get(const struct Render *re);
bool RE_engine_draw_acquire(struct Render *re);
void RE_engine_draw_release(struct Render *re);
-/* NOTE: Only used for Cycles's BLenderGPUDisplay integration with the draw manager. A subject
- * for re-consideration. Do not use this functionality. */
-bool RE_engine_has_render_context(struct RenderEngine *engine);
-void RE_engine_render_context_enable(struct RenderEngine *engine);
-void RE_engine_render_context_disable(struct RenderEngine *engine);
+/* GPU context for engine to create and update GPU resources in its own thread,
+ * without blocking the main thread. Used by Cycles' display driver to create
+ * display textures. */
+bool RE_engine_gpu_context_create(struct RenderEngine *engine);
+void RE_engine_gpu_context_destroy(struct RenderEngine *engine);
+
+bool RE_engine_gpu_context_enable(struct RenderEngine *engine);
+void RE_engine_gpu_context_disable(struct RenderEngine *engine);
+
+void RE_engine_gpu_context_lock(struct RenderEngine *engine);
+void RE_engine_gpu_context_unlock(struct RenderEngine *engine);
/* Engine Types */
diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h
index 548e38d3ef3..6007a64a054 100644
--- a/source/blender/render/RE_pipeline.h
+++ b/source/blender/render/RE_pipeline.h
@@ -7,7 +7,7 @@
#pragma once
-#include "DEG_depsgraph.h"
+#include "DNA_ID.h"
#include "DNA_listBase.h"
#include "DNA_vec_types.h"
@@ -461,9 +461,9 @@ bool RE_allow_render_generic_object(struct Object *ob);
/******* defined in render_result.c *********/
-bool RE_HasCombinedLayer(const RenderResult *res);
-bool RE_HasFloatPixels(const RenderResult *res);
-bool RE_RenderResult_is_stereo(const RenderResult *res);
+bool RE_HasCombinedLayer(const RenderResult *result);
+bool RE_HasFloatPixels(const RenderResult *result);
+bool RE_RenderResult_is_stereo(const RenderResult *result);
struct RenderView *RE_RenderViewGetById(struct RenderResult *rr, int view_id);
struct RenderView *RE_RenderViewGetByName(struct RenderResult *rr, const char *viewname);
diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c
index 9ffe2879779..c383d13e4e1 100644
--- a/source/blender/render/intern/bake.c
+++ b/source/blender/render/intern/bake.c
@@ -72,7 +72,6 @@
#include "RE_texture_margin.h"
/* local include */
-#include "render_types.h"
#include "zbuf.h"
typedef struct BakeDataZSpan {
@@ -460,7 +459,10 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval
unsigned int mpoly_prev = UINT_MAX;
float no[3];
- const MVert *mvert = CustomData_get_layer(&me->vdata, CD_MVERT);
+ const MVert *verts = BKE_mesh_verts(me);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+
looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__);
@@ -471,10 +473,10 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval
if (precomputed_normals != NULL) {
BKE_mesh_recalc_looptri_with_normals(
- me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri, precomputed_normals);
+ loops, polys, verts, me->totloop, me->totpoly, looptri, precomputed_normals);
}
else {
- BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
+ BKE_mesh_recalc_looptri(loops, polys, verts, me->totloop, me->totpoly, looptri);
}
const TSpace *tspace = NULL;
@@ -493,14 +495,14 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me);
for (i = 0; i < tottri; i++) {
const MLoopTri *lt = &looptri[i];
- const MPoly *mp = &me->mpoly[lt->poly];
-
- triangles[i].mverts[0] = &mvert[me->mloop[lt->tri[0]].v];
- triangles[i].mverts[1] = &mvert[me->mloop[lt->tri[1]].v];
- triangles[i].mverts[2] = &mvert[me->mloop[lt->tri[2]].v];
- triangles[i].vert_normals[0] = vert_normals[me->mloop[lt->tri[0]].v];
- triangles[i].vert_normals[1] = vert_normals[me->mloop[lt->tri[1]].v];
- triangles[i].vert_normals[2] = vert_normals[me->mloop[lt->tri[2]].v];
+ const MPoly *mp = &polys[lt->poly];
+
+ triangles[i].mverts[0] = &verts[loops[lt->tri[0]].v];
+ triangles[i].mverts[1] = &verts[loops[lt->tri[1]].v];
+ triangles[i].mverts[2] = &verts[loops[lt->tri[2]].v];
+ triangles[i].vert_normals[0] = vert_normals[loops[lt->tri[0]].v];
+ triangles[i].vert_normals[1] = vert_normals[loops[lt->tri[1]].v];
+ triangles[i].vert_normals[2] = vert_normals[loops[lt->tri[2]].v];
triangles[i].is_smooth = (mp->flag & ME_SMOOTH) != 0;
if (tangent) {
@@ -517,7 +519,7 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval
if (calculate_normal) {
if (lt->poly != mpoly_prev) {
- BKE_mesh_calc_poly_normal(mp, &me->mloop[mp->loopstart], me->mvert, no);
+ BKE_mesh_calc_poly_normal(mp, &loops[mp->loopstart], verts, no);
mpoly_prev = lt->poly;
}
copy_v3_v3(triangles[i].normal, no);
@@ -739,16 +741,20 @@ void RE_bake_pixels_populate(Mesh *me,
const int tottri = poly_to_tri_count(me->totpoly, me->totloop);
MLoopTri *looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
- BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);
+ const MVert *verts = BKE_mesh_verts(me);
+ const MPoly *polys = BKE_mesh_polys(me);
+ const MLoop *loops = BKE_mesh_loops(me);
+ BKE_mesh_recalc_looptri(loops, polys, verts, me->totloop, me->totpoly, looptri);
+
+ const int *material_indices = BKE_mesh_material_indices(me);
for (int i = 0; i < tottri; i++) {
const MLoopTri *lt = &looptri[i];
- const MPoly *mp = &me->mpoly[lt->poly];
bd.primitive_id = i;
/* Find images matching this material. */
- Image *image = targets->material_to_image[mp->mat_nr];
+ Image *image = targets->material_to_image[material_indices ? material_indices[lt->poly] : 0];
for (int image_id = 0; image_id < targets->images_num; image_id++) {
BakeImage *bk_image = &targets->images[image_id];
if (bk_image->image != image) {
@@ -760,8 +766,8 @@ void RE_bake_pixels_populate(Mesh *me,
for (int a = 0; a < 3; a++) {
const float *uv = mloopuv[lt->tri[a]].uv;
- /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our
- * intersection tests where a pixel gets in between 2 faces or the middle of a quad,
+ /* NOTE(@campbellbarton): workaround for pixel aligned UVs which are common and can screw
+ * up our intersection tests where a pixel gets in between 2 faces or the middle of a quad,
* camera aligned quads also have this problem but they are less common.
* Add a small offset to the UVs, fixes bug T18685. */
vec[a][0] = (uv[0] - bk_image->uv_offset[0]) * (float)bk_image->width - (0.5f + 0.001f);
diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.cc
index 8a4b4c2a70d..0024ebe38f7 100644
--- a/source/blender/render/intern/engine.c
+++ b/source/blender/render/intern/engine.cc
@@ -5,9 +5,9 @@
* \ingroup render
*/
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -46,13 +46,16 @@
#include "DRW_engine.h"
+#include "GPU_context.h"
+#include "WM_api.h"
+
#include "pipeline.h"
#include "render_result.h"
#include "render_types.h"
/* Render Engine Types */
-ListBase R_engines = {NULL, NULL};
+ListBase R_engines = {nullptr, nullptr};
void RE_engines_init(void)
{
@@ -70,7 +73,7 @@ void RE_engines_exit(void)
DRW_engines_free();
- for (type = R_engines.first; type; type = next) {
+ for (type = static_cast<RenderEngineType *>(R_engines.first); type; type = next) {
next = type->next;
BLI_remlink(&R_engines, type);
@@ -95,11 +98,11 @@ void RE_engines_register(RenderEngineType *render_type)
RenderEngineType *RE_engines_find(const char *idname)
{
- RenderEngineType *type;
-
- type = BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname));
+ RenderEngineType *type = static_cast<RenderEngineType *>(
+ BLI_findstring(&R_engines, idname, offsetof(RenderEngineType, idname)));
if (!type) {
- type = BLI_findstring(&R_engines, "BLENDER_EEVEE", offsetof(RenderEngineType, idname));
+ type = static_cast<RenderEngineType *>(
+ BLI_findstring(&R_engines, "BLENDER_EEVEE", offsetof(RenderEngineType, idname)));
}
return type;
@@ -113,7 +116,8 @@ bool RE_engine_is_external(const Render *re)
bool RE_engine_is_opengl(RenderEngineType *render_type)
{
/* TODO: refine? Can we have ogl render engine without ogl render pipeline? */
- return (render_type->draw_engine != NULL) && DRW_engine_render_support(render_type->draw_engine);
+ return (render_type->draw_engine != nullptr) &&
+ DRW_engine_render_support(render_type->draw_engine);
}
bool RE_engine_supports_alembic_procedural(const RenderEngineType *render_type, Scene *scene)
@@ -133,10 +137,11 @@ bool RE_engine_supports_alembic_procedural(const RenderEngineType *render_type,
RenderEngine *RE_engine_create(RenderEngineType *type)
{
- RenderEngine *engine = MEM_callocN(sizeof(RenderEngine), "RenderEngine");
+ RenderEngine *engine = MEM_cnew<RenderEngine>("RenderEngine");
engine->type = type;
BLI_mutex_init(&engine->update_render_passes_mutex);
+ BLI_mutex_init(&engine->gpu_context_mutex);
return engine;
}
@@ -151,7 +156,7 @@ static void engine_depsgraph_free(RenderEngine *engine)
}
DEG_graph_free(engine->depsgraph);
- engine->depsgraph = NULL;
+ engine->depsgraph = nullptr;
if (use_gpu_context) {
DRW_render_context_disable(engine->re);
@@ -169,6 +174,7 @@ void RE_engine_free(RenderEngine *engine)
engine_depsgraph_free(engine);
+ BLI_mutex_end(&engine->gpu_context_mutex);
BLI_mutex_end(&engine->update_render_passes_mutex);
MEM_freeN(engine);
@@ -176,10 +182,20 @@ void RE_engine_free(RenderEngine *engine)
/* Bake Render Results */
-static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y, int w, int h)
+static RenderResult *render_result_from_bake(
+ RenderEngine *engine, int x, int y, int w, int h, const char *layername)
{
+ BakeImage *image = &engine->bake.targets->images[engine->bake.image_id];
+ const BakePixel *pixels = engine->bake.pixels + image->offset;
+ const size_t channels_num = engine->bake.targets->channels_num;
+
+ /* Remember layer name for to match images in render_frame_finish. */
+ if (image->render_layer_name[0] == '\0') {
+ STRNCPY(image->render_layer_name, layername);
+ }
+
/* Create render result with specified size. */
- RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__);
+ RenderResult *rr = MEM_cnew<RenderResult>(__func__);
rr->rectx = w;
rr->recty = h;
@@ -189,13 +205,14 @@ static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y,
rr->tilerect.ymax = y + h;
/* Add single baking render layer. */
- RenderLayer *rl = MEM_callocN(sizeof(RenderLayer), "bake render layer");
+ RenderLayer *rl = MEM_cnew<RenderLayer>("bake render layer");
+ STRNCPY(rl->name, layername);
rl->rectx = w;
rl->recty = h;
BLI_addtail(&rr->layers, rl);
/* Add render passes. */
- render_layer_add_pass(rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA", true);
+ render_layer_add_pass(rr, rl, channels_num, RE_PASSNAME_COMBINED, "", "RGBA", true);
RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA", true);
RenderPass *differential_pass = render_layer_add_pass(
@@ -207,8 +224,8 @@ static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y,
float *primitive = primitive_pass->rect + offset;
float *differential = differential_pass->rect + offset;
- size_t bake_offset = (y + ty) * engine->bake.width + x;
- const BakePixel *bake_pixel = engine->bake.pixels + bake_offset;
+ size_t bake_offset = (y + ty) * image->width + x;
+ const BakePixel *bake_pixel = pixels + bake_offset;
for (int tx = 0; tx < w; tx++) {
if (bake_pixel->object_id != engine->bake.object_id) {
@@ -238,35 +255,50 @@ static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y,
static void render_result_to_bake(RenderEngine *engine, RenderResult *rr)
{
- RenderPass *rpass = RE_pass_find_by_name(rr->layers.first, RE_PASSNAME_COMBINED, "");
-
+ RenderLayer *rl = static_cast<RenderLayer *>(rr->layers.first);
+ RenderPass *rpass = RE_pass_find_by_name(rl, RE_PASSNAME_COMBINED, "");
if (!rpass) {
return;
}
+ /* Find bake image corresponding to layer. */
+ int image_id = 0;
+ for (; image_id < engine->bake.targets->images_num; image_id++) {
+ if (STREQ(engine->bake.targets->images[image_id].render_layer_name, rl->name)) {
+ break;
+ }
+ }
+ if (image_id == engine->bake.targets->images_num) {
+ return;
+ }
+
+ const BakeImage *image = &engine->bake.targets->images[image_id];
+ const BakePixel *pixels = engine->bake.pixels + image->offset;
+ const size_t channels_num = engine->bake.targets->channels_num;
+ const size_t channels_size = channels_num * sizeof(float);
+ float *result = engine->bake.result + image->offset * channels_num;
+
/* Copy from tile render result to full image bake result. Just the pixels for the
* object currently being baked, to preserve other objects when baking multiple. */
const int x = rr->tilerect.xmin;
const int y = rr->tilerect.ymin;
const int w = rr->tilerect.xmax - rr->tilerect.xmin;
const int h = rr->tilerect.ymax - rr->tilerect.ymin;
- const size_t pixel_depth = engine->bake.depth;
- const size_t pixel_size = pixel_depth * sizeof(float);
for (int ty = 0; ty < h; ty++) {
const size_t offset = ty * w;
- const size_t bake_offset = (y + ty) * engine->bake.width + x;
+ const size_t bake_offset = (y + ty) * image->width + x;
- const float *pass_rect = rpass->rect + offset * pixel_depth;
- const BakePixel *bake_pixel = engine->bake.pixels + bake_offset;
- float *bake_result = engine->bake.result + bake_offset * pixel_depth;
+ const float *pass_rect = rpass->rect + offset * channels_num;
+ const BakePixel *bake_pixel = pixels + bake_offset;
+ float *bake_result = result + bake_offset * channels_num;
for (int tx = 0; tx < w; tx++) {
if (bake_pixel->object_id == engine->bake.object_id) {
- memcpy(bake_result, pass_rect, pixel_size);
+ memcpy(bake_result, pass_rect, channels_size);
}
- pass_rect += pixel_depth;
- bake_result += pixel_depth;
+ pass_rect += channels_num;
+ bake_result += channels_num;
bake_pixel++;
}
}
@@ -294,7 +326,7 @@ static void engine_tile_highlight_set(RenderEngine *engine,
BLI_mutex_lock(&re->highlighted_tiles_mutex);
- if (re->highlighted_tiles == NULL) {
+ if (re->highlighted_tiles == nullptr) {
re->highlighted_tiles = BLI_gset_new(
BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, "highlighted tiles");
}
@@ -302,7 +334,7 @@ static void engine_tile_highlight_set(RenderEngine *engine,
if (highlight) {
HighlightedTile **tile_in_set;
if (!BLI_gset_ensure_p_ex(re->highlighted_tiles, tile, (void ***)&tile_in_set)) {
- *tile_in_set = MEM_mallocN(sizeof(HighlightedTile), __func__);
+ *tile_in_set = MEM_cnew<HighlightedTile>(__func__);
**tile_in_set = *tile;
}
}
@@ -316,8 +348,8 @@ static void engine_tile_highlight_set(RenderEngine *engine,
RenderResult *RE_engine_begin_result(
RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname)
{
- if (engine->bake.pixels) {
- RenderResult *result = render_result_from_bake(engine, x, y, w, h);
+ if (engine->bake.targets) {
+ RenderResult *result = render_result_from_bake(engine, x, y, w, h, layername);
BLI_addtail(&engine->fullresult, result);
return result;
}
@@ -349,7 +381,7 @@ RenderResult *RE_engine_begin_result(
/* TODO: make this thread safe. */
- /* can be NULL if we CLAMP the width or height to 0 */
+ /* can be nullptr if we CLAMP the width or height to 0 */
if (result) {
render_result_clone_passes(re, result, viewname);
render_result_passes_allocated_ensure(result);
@@ -378,7 +410,7 @@ static void re_ensure_passes_allocated_thread_safe(Render *re)
void RE_engine_update_result(RenderEngine *engine, RenderResult *result)
{
- if (engine->bake.pixels) {
+ if (engine->bake.targets) {
/* No interactive baking updates for now. */
return;
}
@@ -388,8 +420,9 @@ void RE_engine_update_result(RenderEngine *engine, RenderResult *result)
if (result) {
re_ensure_passes_allocated_thread_safe(re);
render_result_merge(re->result, result);
- result->renlay = result->layers.first; /* weak, draws first layer always */
- re->display_update(re->duh, result, NULL);
+ result->renlay = static_cast<RenderLayer *>(
+ result->layers.first); /* weak, draws first layer always */
+ re->display_update(re->duh, result, nullptr);
}
}
@@ -405,7 +438,7 @@ void RE_engine_add_pass(RenderEngine *engine,
return;
}
- RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL, false);
+ RE_create_render_pass(re->result, name, channels, chan_id, layername, nullptr, false);
}
void RE_engine_end_result(
@@ -417,8 +450,10 @@ void RE_engine_end_result(
return;
}
- if (engine->bake.pixels) {
- render_result_to_bake(engine, result);
+ if (engine->bake.targets) {
+ if (!cancel || merge_results) {
+ render_result_to_bake(engine, result);
+ }
BLI_remlink(&engine->fullresult, result);
render_result_free(result);
return;
@@ -438,8 +473,9 @@ void RE_engine_end_result(
/* draw */
if (!re->test_break(re->tbh)) {
- result->renlay = result->layers.first; /* weak, draws first layer always */
- re->display_update(re->duh, result, NULL);
+ result->renlay = static_cast<RenderLayer *>(
+ result->layers.first); /* weak, draws first layer always */
+ re->display_update(re->duh, result, nullptr);
}
}
@@ -463,7 +499,7 @@ bool RE_engine_test_break(RenderEngine *engine)
return re->test_break(re->tbh);
}
- return 0;
+ return false;
}
/* Statistics */
@@ -477,8 +513,8 @@ void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char
re->i.statstr = stats;
re->i.infostr = info;
re->stats_draw(re->sdh, &re->i);
- re->i.infostr = NULL;
- re->i.statstr = NULL;
+ re->i.infostr = nullptr;
+ re->i.statstr = nullptr;
}
/* set engine text */
@@ -520,20 +556,20 @@ void RE_engine_report(RenderEngine *engine, int type, const char *msg)
Render *re = engine->re;
if (re) {
- BKE_report(engine->re->reports, type, msg);
+ BKE_report(engine->re->reports, (eReportType)type, msg);
}
else if (engine->reports) {
- BKE_report(engine->reports, type, msg);
+ BKE_report(engine->reports, (eReportType)type, msg);
}
}
void RE_engine_set_error_message(RenderEngine *engine, const char *msg)
{
Render *re = engine->re;
- if (re != NULL) {
+ if (re != nullptr) {
RenderResult *rr = RE_AcquireResultRead(re);
if (rr) {
- if (rr->error != NULL) {
+ if (rr->error != nullptr) {
MEM_freeN(rr->error);
}
rr->error = BLI_strdup(msg);
@@ -545,17 +581,17 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg)
RenderPass *RE_engine_pass_by_index_get(RenderEngine *engine, const char *layer_name, int index)
{
Render *re = engine->re;
- if (re == NULL) {
- return NULL;
+ if (re == nullptr) {
+ return nullptr;
}
- RenderPass *pass = NULL;
+ RenderPass *pass = nullptr;
RenderResult *rr = RE_AcquireResultRead(re);
- if (rr != NULL) {
+ if (rr != nullptr) {
const RenderLayer *layer = RE_GetRenderLayer(rr, layer_name);
- if (layer != NULL) {
- pass = BLI_findlink(&layer->passes, index);
+ if (layer != nullptr) {
+ pass = static_cast<RenderPass *>(BLI_findlink(&layer->passes, index));
}
}
RE_ReleaseResult(re);
@@ -580,8 +616,8 @@ float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera, bool us
/* When using spherical stereo, get camera shift without multiview,
* leaving stereo to be handled by the engine. */
Render *re = engine->re;
- if (use_spherical_stereo || re == NULL) {
- return BKE_camera_multiview_shift_x(NULL, camera, NULL);
+ if (use_spherical_stereo || re == nullptr) {
+ return BKE_camera_multiview_shift_x(nullptr, camera, nullptr);
}
return BKE_camera_multiview_shift_x(&re->r, camera, re->viewname);
@@ -595,8 +631,8 @@ void RE_engine_get_camera_model_matrix(RenderEngine *engine,
/* When using spherical stereo, get model matrix without multiview,
* leaving stereo to be handled by the engine. */
Render *re = engine->re;
- if (use_spherical_stereo || re == NULL) {
- BKE_camera_multiview_model_matrix(NULL, camera, NULL, (float(*)[4])r_modelmat);
+ if (use_spherical_stereo || re == nullptr) {
+ BKE_camera_multiview_model_matrix(nullptr, camera, nullptr, (float(*)[4])r_modelmat);
}
else {
BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, (float(*)[4])r_modelmat);
@@ -606,7 +642,7 @@ void RE_engine_get_camera_model_matrix(RenderEngine *engine,
bool RE_engine_get_spherical_stereo(RenderEngine *engine, Object *camera)
{
Render *re = engine->re;
- return BKE_camera_multiview_spherical_stereo(re ? &re->r : NULL, camera) ? 1 : 0;
+ return BKE_camera_multiview_spherical_stereo(re ? &re->r : nullptr, camera) ? true : false;
}
rcti *RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free)
@@ -621,10 +657,10 @@ rcti *RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_
*r_needs_free = false;
- if (re->highlighted_tiles == NULL) {
+ if (re->highlighted_tiles == nullptr) {
*r_total_tiles = 0;
BLI_mutex_unlock(&re->highlighted_tiles_mutex);
- return NULL;
+ return nullptr;
}
GSET_FOREACH_BEGIN (HighlightedTile *, tile, re->highlighted_tiles) {
@@ -637,10 +673,10 @@ rcti *RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_
/* Can not realloc yet, tiles are pointing to a
* stack memory.
*/
- tiles = MEM_mallocN(allocation_size * sizeof(rcti), "current engine tiles");
+ tiles = MEM_cnew_array<rcti>(allocation_size, "current engine tiles");
}
else {
- tiles = MEM_reallocN(tiles, allocation_size * sizeof(rcti));
+ tiles = static_cast<rcti *>(MEM_reallocN(tiles, allocation_size * sizeof(rcti)));
}
*r_needs_free = true;
}
@@ -705,7 +741,7 @@ static void engine_depsgraph_init(RenderEngine *engine, ViewLayer *view_layer)
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);
+ RE_FreePersistentData(nullptr);
/* Create new depsgraph if not cached with persistent data. */
engine->depsgraph = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_RENDER);
@@ -783,7 +819,7 @@ void RE_bake_engine_set_engine_parameters(Render *re, Main *bmain, Scene *scene)
bool RE_bake_has_engine(const Render *re)
{
const RenderEngineType *type = RE_engines_find(re->r.engine);
- return (type->bake != NULL);
+ return (type->bake != nullptr);
}
bool RE_bake_engine(Render *re,
@@ -827,23 +863,29 @@ bool RE_bake_engine(Render *re,
type->update(engine, re->main, engine->depsgraph);
}
- for (int i = 0; i < targets->images_num; i++) {
- const BakeImage *image = targets->images + i;
+ /* Bake all images. */
+ engine->bake.targets = targets;
+ engine->bake.pixels = pixel_array;
+ engine->bake.result = result;
+ engine->bake.object_id = object_id;
- engine->bake.pixels = pixel_array + image->offset;
- engine->bake.result = result + image->offset * targets->channels_num;
- engine->bake.width = image->width;
- engine->bake.height = image->height;
- engine->bake.depth = targets->channels_num;
- engine->bake.object_id = object_id;
+ for (int i = 0; i < targets->images_num; i++) {
+ const BakeImage *image = &targets->images[i];
+ engine->bake.image_id = i;
type->bake(
engine, engine->depsgraph, object, pass_type, pass_filter, image->width, image->height);
+ }
- memset(&engine->bake, 0, sizeof(engine->bake));
+ /* Optionally let render images read bake images from disk delayed. */
+ if (type->render_frame_finish) {
+ engine->bake.image_id = 0;
+ type->render_frame_finish(engine);
}
- engine->depsgraph = NULL;
+ memset(&engine->bake, 0, sizeof(engine->bake));
+
+ engine->depsgraph = nullptr;
}
engine->flag &= ~RE_ENGINE_RENDERING;
@@ -851,7 +893,7 @@ bool RE_bake_engine(Render *re,
engine_depsgraph_free(engine);
RE_engine_free(engine);
- re->engine = NULL;
+ re->engine = nullptr;
if (BKE_reports_contain(re->reports, RPT_ERROR)) {
G.is_break = true;
@@ -874,8 +916,8 @@ static void engine_render_view_layer(Render *re,
}
/* Create depsgraph with scene evaluated at render resolution. */
- ViewLayer *view_layer = BLI_findstring(
- &re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name));
+ ViewLayer *view_layer = static_cast<ViewLayer *>(
+ BLI_findstring(&re->scene->view_layers, view_layer_iter->name, offsetof(ViewLayer, name)));
engine_depsgraph_init(engine, view_layer);
/* Sync data to engine, within draw lock so scene data can be accessed safely. */
@@ -918,7 +960,7 @@ static void engine_render_view_layer(Render *re,
/* NOTE: External engine might have been requested to free its
* dependency graph, which is only allowed if there is no grease
* pencil (pipeline is taking care of that). */
- if (!RE_engine_test_break(engine) && engine->depsgraph != NULL) {
+ if (!RE_engine_test_break(engine) && engine->depsgraph != nullptr) {
DRW_render_gpencil(engine, engine->depsgraph);
}
}
@@ -950,6 +992,16 @@ bool RE_engine_render(Render *re, bool do_all)
re->draw_lock(re->dlh, true);
}
+ if ((type->flag & RE_USE_GPU_CONTEXT) && !GPU_backend_supported()) {
+ /* Clear UI drawing locks. */
+ if (re->draw_lock) {
+ re->draw_lock(re->dlh, false);
+ }
+ BKE_report(re->reports, RPT_ERROR, "Can not initialize the GPU");
+ G.is_break = true;
+ return true;
+ }
+
/* update animation here so any render layer animation is applied before
* creating the render result */
if ((re->r.scemode & (R_NO_FRAME_UPDATE | R_BUTS_PREVIEW)) == 0) {
@@ -958,7 +1010,7 @@ bool RE_engine_render(Render *re, bool do_all)
/* create render result */
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
- if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) {
+ if (re->result == nullptr || !(re->r.scemode & R_BUTS_PREVIEW)) {
if (re->result) {
render_result_free(re->result);
}
@@ -967,7 +1019,7 @@ bool RE_engine_render(Render *re, bool do_all)
}
BLI_rw_mutex_unlock(&re->resultmutex);
- if (re->result == NULL) {
+ if (re->result == nullptr) {
/* Clear UI drawing locks. */
if (re->draw_lock) {
re->draw_lock(re->dlh, false);
@@ -1050,14 +1102,15 @@ bool RE_engine_render(Render *re, bool do_all)
/* Clear tile data */
engine->flag &= ~RE_ENGINE_RENDERING;
- render_result_free_list(&engine->fullresult, engine->fullresult.first);
+ render_result_free_list(&engine->fullresult,
+ static_cast<RenderResult *>(engine->fullresult.first));
/* re->engine becomes zero if user changed active render engine during render */
if (!engine_keep_depsgraph(engine) || !re->engine) {
engine_depsgraph_free(engine);
RE_engine_free(engine);
- re->engine = NULL;
+ re->engine = nullptr;
}
if (re->r.scemode & R_EXR_CACHE_FILE) {
@@ -1094,8 +1147,8 @@ void RE_engine_update_render_passes(struct RenderEngine *engine,
engine->update_render_passes_cb = callback;
engine->update_render_passes_data = callback_data;
engine->type->update_render_passes(engine, scene, view_layer);
- engine->update_render_passes_cb = NULL;
- engine->update_render_passes_data = NULL;
+ engine->update_render_passes_cb = nullptr;
+ engine->update_render_passes_data = nullptr;
BLI_mutex_unlock(&engine->update_render_passes_mutex);
}
@@ -1139,7 +1192,8 @@ bool RE_engine_draw_acquire(Render *re)
RenderEngine *engine = re->engine;
- if (engine == NULL || engine->type->draw == NULL || (engine->flag & RE_ENGINE_CAN_DRAW) == 0) {
+ if (engine == nullptr || engine->type->draw == nullptr ||
+ (engine->flag & RE_ENGINE_CAN_DRAW) == 0) {
BLI_mutex_unlock(&re->engine_draw_mutex);
return false;
}
@@ -1171,7 +1225,7 @@ void RE_engine_tile_highlight_clear_all(RenderEngine *engine)
BLI_mutex_lock(&re->highlighted_tiles_mutex);
- if (re->highlighted_tiles != NULL) {
+ if (re->highlighted_tiles != nullptr) {
BLI_gset_clear(re->highlighted_tiles, MEM_freeN);
}
@@ -1181,27 +1235,104 @@ void RE_engine_tile_highlight_clear_all(RenderEngine *engine)
/* -------------------------------------------------------------------- */
/** \name OpenGL context manipulation.
*
- * NOTE: Only used for Cycles's BLenderGPUDisplay integration with the draw manager. A subject
- * for re-consideration. Do not use this functionality.
+ * GPU context for engine to create and update GPU resources in its own thread,
+ * without blocking the main thread. Used by Cycles' display driver to create
+ * display textures.
+ *
* \{ */
-bool RE_engine_has_render_context(RenderEngine *engine)
+bool RE_engine_gpu_context_create(RenderEngine *engine)
{
- if (engine->re == NULL) {
- return false;
+ /* If the there already is a draw manager render context available, reuse it. */
+ engine->use_drw_render_context = (engine->re && RE_gl_context_get(engine->re));
+ if (engine->use_drw_render_context) {
+ return true;
+ }
+
+ /* Viewport render case where no render context is available. We are expected to be on
+ * the main thread here to safely create a context. */
+ BLI_assert(BLI_thread_is_main());
+
+ const bool drw_state = DRW_opengl_context_release();
+ engine->gpu_context = WM_opengl_context_create();
+
+ /* On Windows an old context is restored after creation, and subsequent release of context
+ * generates a Win32 error. Harmless for users, but annoying to have possible misleading
+ * error prints in the console. */
+#ifndef _WIN32
+ if (engine->gpu_context) {
+ WM_opengl_context_release(engine->gpu_context);
}
+#endif
+
+ DRW_opengl_context_activate(drw_state);
+
+ return engine->gpu_context != nullptr;
+}
+
+void RE_engine_gpu_context_destroy(RenderEngine *engine)
+{
+ if (!engine->gpu_context) {
+ return;
+ }
+
+ const bool drw_state = DRW_opengl_context_release();
+
+ WM_opengl_context_activate(engine->gpu_context);
+ WM_opengl_context_dispose(engine->gpu_context);
+
+ DRW_opengl_context_activate(drw_state);
+}
+
+bool RE_engine_gpu_context_enable(RenderEngine *engine)
+{
+ if (engine->use_drw_render_context) {
+ DRW_render_context_enable(engine->re);
+ return true;
+ }
+ if (engine->gpu_context) {
+ BLI_mutex_lock(&engine->gpu_context_mutex);
+ WM_opengl_context_activate(engine->gpu_context);
+ return true;
+ }
+ return false;
+}
- return RE_gl_context_get(engine->re) != NULL;
+void RE_engine_gpu_context_disable(RenderEngine *engine)
+{
+ if (engine->use_drw_render_context) {
+ DRW_render_context_disable(engine->re);
+ }
+ else {
+ if (engine->gpu_context) {
+ WM_opengl_context_release(engine->gpu_context);
+ BLI_mutex_unlock(&engine->gpu_context_mutex);
+ }
+ }
}
-void RE_engine_render_context_enable(RenderEngine *engine)
+void RE_engine_gpu_context_lock(RenderEngine *engine)
{
- DRW_render_context_enable(engine->re);
+ if (engine->use_drw_render_context) {
+ /* Locking already handled by the draw manager. */
+ }
+ else {
+ if (engine->gpu_context) {
+ BLI_mutex_lock(&engine->gpu_context_mutex);
+ }
+ }
}
-void RE_engine_render_context_disable(RenderEngine *engine)
+void RE_engine_gpu_context_unlock(RenderEngine *engine)
{
- DRW_render_context_disable(engine->re);
+ if (engine->use_drw_render_context) {
+ /* Locking already handled by the draw manager. */
+ }
+ else {
+ if (engine->gpu_context) {
+ BLI_mutex_unlock(&engine->gpu_context_mutex);
+ }
+ }
}
/** \} */
diff --git a/source/blender/render/intern/initrender.c b/source/blender/render/intern/initrender.cc
index a2a6a5815a0..1ea93cbf6c8 100644
--- a/source/blender/render/intern/initrender.c
+++ b/source/blender/render/intern/initrender.cc
@@ -7,10 +7,10 @@
/* Global includes */
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -124,7 +124,8 @@ float RE_filter_value(int type, float x)
}
return 1.0f - x;
- case R_FILTER_GAUSS: {
+ case R_FILTER_GAUSS:
+ case R_FILTER_FAST_GAUSS: {
const float two_gaussfac2 = 2.0f * gaussfac * gaussfac;
x *= 3.0f * gaussfac;
return 1.0f / sqrtf((float)M_PI * two_gaussfac2) * expf(-x * x / two_gaussfac2);
diff --git a/source/blender/render/intern/multires_bake.c b/source/blender/render/intern/multires_bake.c
index e3229e20595..79eddd20bdf 100644
--- a/source/blender/render/intern/multires_bake.c
+++ b/source/blender/render/intern/multires_bake.c
@@ -63,6 +63,7 @@ typedef struct {
MVert *mvert;
const float (*vert_normals)[3];
MPoly *mpoly;
+ const int *material_indices;
MLoop *mloop;
MLoopUV *mloopuv;
float uv_offset[2];
@@ -382,8 +383,7 @@ static void *do_multires_bake_thread(void *data_v)
while ((tri_index = multires_bake_queue_next_tri(handle->queue)) >= 0) {
const MLoopTri *lt = &data->mlooptri[tri_index];
- const MPoly *mp = &data->mpoly[lt->poly];
- const short mat_nr = mp->mat_nr;
+ const short mat_nr = data->material_indices == NULL ? 0 : data->material_indices[lt->poly];
const MLoopUV *mloopuv = data->mloopuv;
if (multiresbake_test_break(bkr)) {
@@ -485,10 +485,18 @@ static void do_multires_bake(MultiresBakeRender *bkr,
Mesh *temp_mesh = BKE_mesh_new_nomain(
dm->getNumVerts(dm), dm->getNumEdges(dm), 0, dm->getNumLoops(dm), dm->getNumPolys(dm));
- memcpy(temp_mesh->mvert, dm->getVertArray(dm), temp_mesh->totvert * sizeof(*temp_mesh->mvert));
- memcpy(temp_mesh->medge, dm->getEdgeArray(dm), temp_mesh->totedge * sizeof(*temp_mesh->medge));
- memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly));
- memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop));
+ memcpy(BKE_mesh_verts_for_write(temp_mesh),
+ dm->getVertArray(dm),
+ temp_mesh->totvert * sizeof(MVert));
+ memcpy(BKE_mesh_edges_for_write(temp_mesh),
+ dm->getEdgeArray(dm),
+ temp_mesh->totedge * sizeof(MEdge));
+ memcpy(BKE_mesh_polys_for_write(temp_mesh),
+ dm->getPolyArray(dm),
+ temp_mesh->totpoly * sizeof(MPoly));
+ memcpy(BKE_mesh_loops_for_write(temp_mesh),
+ dm->getLoopArray(dm),
+ temp_mesh->totloop * sizeof(MLoop));
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh);
const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh);
@@ -545,6 +553,8 @@ static void do_multires_bake(MultiresBakeRender *bkr,
handle->queue = &queue;
handle->data.mpoly = mpoly;
+ handle->data.material_indices = CustomData_get_layer_named(
+ &dm->polyData, CD_PROP_INT32, "material_index");
handle->data.mvert = mvert;
handle->data.vert_normals = vert_normals;
handle->data.mloopuv = mloopuv;
diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.cc
index 154b689b4a5..4b52fb62bee 100644
--- a/source/blender/render/intern/pipeline.c
+++ b/source/blender/render/intern/pipeline.cc
@@ -5,12 +5,12 @@
* \ingroup render
*/
-#include <errno.h>
-#include <limits.h>
-#include <math.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cerrno>
+#include <climits>
+#include <cmath>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
#include "DNA_anim_types.h"
#include "DNA_collection_types.h"
@@ -33,6 +33,7 @@
#include "BLI_string.h"
#include "BLI_threads.h"
#include "BLI_timecode.h"
+#include "BLI_vector.hh"
#include "BLT_translation.h"
@@ -41,7 +42,6 @@
#include "BKE_callbacks.h"
#include "BKE_camera.h"
#include "BKE_colortools.h"
-#include "BKE_context.h" /* XXX needed by wm_window.h */
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_image_format.h"
@@ -49,6 +49,7 @@
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_remap.h"
+#include "BKE_main.h"
#include "BKE_mask.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
@@ -79,9 +80,9 @@
#include "SEQ_relations.h"
#include "SEQ_render.h"
-#include "../../windowmanager/WM_api.h" /* XXX */
-#include "../../windowmanager/wm_window.h" /* XXX */
#include "GPU_context.h"
+#include "WM_api.h"
+#include "wm_window.h"
#ifdef WITH_FREESTYLE
# include "FRS_freestyle.h"
@@ -123,7 +124,7 @@
/* here we store all renders */
static struct {
ListBase renderlist;
-} RenderGlobal = {{NULL, NULL}};
+} RenderGlobal = {{nullptr, nullptr}};
/** \} */
@@ -185,7 +186,7 @@ static int default_break(void *UNUSED(arg))
static void stats_background(void *UNUSED(arg), RenderStats *rs)
{
- if (rs->infostr == NULL) {
+ if (rs->infostr == nullptr) {
return;
}
@@ -199,14 +200,20 @@ static void stats_background(void *UNUSED(arg), RenderStats *rs)
megs_used_memory = (mem_in_use) / (1024.0 * 1024.0);
megs_peak_memory = (peak_memory) / (1024.0 * 1024.0);
+ BLI_timecode_string_from_time_simple(
+ info_time_str, sizeof(info_time_str), PIL_check_seconds_timer() - rs->starttime);
+
+ /* Compositor calls this from multiple threads, mutex lock to ensure we don't
+ * get garbled output. */
+ static ThreadMutex mutex = BLI_MUTEX_INITIALIZER;
+ BLI_mutex_lock(&mutex);
+
fprintf(stdout,
TIP_("Fra:%d Mem:%.2fM (Peak %.2fM) "),
rs->cfra,
megs_used_memory,
megs_peak_memory);
- BLI_timecode_string_from_time_simple(
- info_time_str, sizeof(info_time_str), PIL_check_seconds_timer() - rs->starttime);
fprintf(stdout, TIP_("| Time:%s | "), info_time_str);
fprintf(stdout, "%s", rs->infostr);
@@ -215,11 +222,13 @@ static void stats_background(void *UNUSED(arg), RenderStats *rs)
fflush(stdout);
/* NOTE: using G_MAIN seems valid here???
- * Not sure it's actually even used anyway, we could as well pass NULL? */
+ * Not sure it's actually even used anyway, we could as well pass nullptr? */
BKE_callback_exec_null(G_MAIN, BKE_CB_EVT_RENDER_STATS);
fputc('\n', stdout);
fflush(stdout);
+
+ BLI_mutex_unlock(&mutex);
}
void RE_FreeRenderResult(RenderResult *rr)
@@ -230,16 +239,17 @@ void RE_FreeRenderResult(RenderResult *rr)
float *RE_RenderLayerGetPass(RenderLayer *rl, const char *name, const char *viewname)
{
RenderPass *rpass = RE_pass_find_by_name(rl, name, viewname);
- return rpass ? rpass->rect : NULL;
+ return rpass ? rpass->rect : nullptr;
}
RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name)
{
- if (rr == NULL) {
- return NULL;
+ if (rr == nullptr) {
+ return nullptr;
}
- return BLI_findstring(&rr->layers, name, offsetof(RenderLayer, name));
+ return static_cast<RenderLayer *>(
+ BLI_findstring(&rr->layers, name, offsetof(RenderLayer, name)));
}
bool RE_HasSingleLayer(Render *re)
@@ -255,17 +265,18 @@ RenderResult *RE_MultilayerConvert(
RenderLayer *render_get_active_layer(Render *re, RenderResult *rr)
{
- ViewLayer *view_layer = BLI_findlink(&re->view_layers, re->active_view_layer);
+ ViewLayer *view_layer = static_cast<ViewLayer *>(
+ BLI_findlink(&re->view_layers, re->active_view_layer));
if (view_layer) {
- RenderLayer *rl = BLI_findstring(&rr->layers, view_layer->name, offsetof(RenderLayer, name));
+ RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name);
if (rl) {
return rl;
}
}
- return rr->layers.first;
+ return static_cast<RenderLayer *>(rr->layers.first);
}
static bool render_scene_has_layers_to_render(Scene *scene, ViewLayer *single_layer)
@@ -274,8 +285,7 @@ static bool render_scene_has_layers_to_render(Scene *scene, ViewLayer *single_la
return true;
}
- ViewLayer *view_layer;
- for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) {
if (view_layer->flag & VIEW_LAYER_RENDER) {
return true;
}
@@ -291,16 +301,14 @@ static bool render_scene_has_layers_to_render(Scene *scene, ViewLayer *single_la
Render *RE_GetRender(const char *name)
{
- Render *re;
-
/* search for existing renders */
- for (re = RenderGlobal.renderlist.first; re; re = re->next) {
+ LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
if (STREQLEN(re->name, name, RE_MAXNAME)) {
- break;
+ return re;
}
}
- return re;
+ return nullptr;
}
RenderResult *RE_AcquireResultRead(Render *re)
@@ -310,7 +318,7 @@ RenderResult *RE_AcquireResultRead(Render *re)
return re->result;
}
- return NULL;
+ return nullptr;
}
RenderResult *RE_AcquireResultWrite(Render *re)
@@ -321,14 +329,14 @@ RenderResult *RE_AcquireResultWrite(Render *re)
return re->result;
}
- return NULL;
+ return nullptr;
}
void RE_ClearResult(Render *re)
{
if (re) {
render_result_free(re->result);
- re->result = NULL;
+ re->result = nullptr;
}
}
@@ -352,7 +360,7 @@ Scene *RE_GetScene(Render *re)
if (re) {
return re->scene;
}
- return NULL;
+ return nullptr;
}
void RE_SetScene(Render *re, Scene *sce)
@@ -370,30 +378,27 @@ void RE_AcquireResultImageViews(Render *re, RenderResult *rr)
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ);
if (re->result) {
- RenderLayer *rl;
- RenderView *rv, *rview;
-
rr->rectx = re->result->rectx;
rr->recty = re->result->recty;
/* creates a temporary duplication of views */
render_result_views_shallowcopy(rr, re->result);
- rv = rr->views.first;
- rr->have_combined = (rv->rectf != NULL);
+ RenderView *rv = static_cast<RenderView *>(rr->views.first);
+ rr->have_combined = (rv->rectf != nullptr);
/* active layer */
- rl = render_get_active_layer(re, re->result);
+ RenderLayer *rl = render_get_active_layer(re, re->result);
if (rl) {
- if (rv->rectf == NULL) {
- for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) {
+ if (rv->rectf == nullptr) {
+ LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
rview->rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, rview->name);
}
}
- if (rv->rectz == NULL) {
- for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) {
+ if (rv->rectz == nullptr) {
+ LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
rview->rectz = RE_RenderLayerGetPass(rl, RE_PASSNAME_Z, rview->name);
}
}
@@ -431,9 +436,9 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id)
rr->rectx = re->result->rectx;
rr->recty = re->result->recty;
- /* actview view */
+ /* `scene.rd.actview` view. */
rv = RE_RenderViewGetById(re->result, view_id);
- rr->have_combined = (rv->rectf != NULL);
+ rr->have_combined = (rv->rectf != nullptr);
rr->rectf = rv->rectf;
rr->rectz = rv->rectz;
@@ -443,11 +448,11 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id)
rl = render_get_active_layer(re, re->result);
if (rl) {
- if (rv->rectf == NULL) {
+ if (rv->rectf == nullptr) {
rr->rectf = RE_RenderLayerGetPass(rl, RE_PASSNAME_COMBINED, rv->name);
}
- if (rv->rectz == NULL) {
+ if (rv->rectz == nullptr) {
rr->rectz = RE_RenderLayerGetPass(rl, RE_PASSNAME_Z, rv->name);
}
}
@@ -511,10 +516,10 @@ Render *RE_NewRender(const char *name)
/* only one render per name exists */
re = RE_GetRender(name);
- if (re == NULL) {
+ if (re == nullptr) {
/* new render data struct */
- re = MEM_callocN(sizeof(Render), "new render");
+ re = MEM_cnew<Render>("new render");
BLI_addtail(&RenderGlobal.renderlist, re);
BLI_strncpy(re->name, name, RE_MAXNAME);
BLI_rw_mutex_init(&re->resultmutex);
@@ -570,7 +575,7 @@ void RE_InitRenderCB(Render *re)
re->stats_draw = stats_nothing;
}
/* clear callback handles */
- re->dih = re->dch = re->duh = re->sdh = re->prh = re->tbh = NULL;
+ re->dih = re->dch = re->duh = re->sdh = re->prh = re->tbh = nullptr;
}
void RE_FreeRender(Render *re)
@@ -588,13 +593,13 @@ void RE_FreeRender(Render *re)
BKE_curvemapping_free_data(&re->r.mblur_shutter_curve);
- if (re->highlighted_tiles != NULL) {
+ if (re->highlighted_tiles != nullptr) {
BLI_gset_free(re->highlighted_tiles, MEM_freeN);
}
/* main dbase can already be invalid now, some database-free code checks it */
- re->main = NULL;
- re->scene = NULL;
+ re->main = nullptr;
+ re->scene = nullptr;
render_result_free(re->result);
render_result_free(re->pushedresult);
@@ -606,7 +611,7 @@ void RE_FreeRender(Render *re)
void RE_FreeAllRender(void)
{
while (RenderGlobal.renderlist.first) {
- RE_FreeRender(RenderGlobal.renderlist.first);
+ RE_FreeRender(static_cast<Render *>(RenderGlobal.renderlist.first));
}
#ifdef WITH_FREESTYLE
@@ -617,25 +622,22 @@ void RE_FreeAllRender(void)
void RE_FreeAllRenderResults(void)
{
- Render *re;
-
- for (re = RenderGlobal.renderlist.first; re; re = re->next) {
+ LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
render_result_free(re->result);
render_result_free(re->pushedresult);
- re->result = NULL;
- re->pushedresult = NULL;
+ re->result = nullptr;
+ re->pushedresult = nullptr;
}
}
void RE_FreeAllPersistentData(void)
{
- Render *re;
- for (re = RenderGlobal.renderlist.first; re != NULL; re = re->next) {
- if (re->engine != NULL) {
+ LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
+ if (re->engine != nullptr) {
BLI_assert(!(re->engine->flag & RE_ENGINE_RENDERING));
RE_engine_free(re->engine);
- re->engine = NULL;
+ re->engine = nullptr;
}
}
}
@@ -645,7 +647,7 @@ 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 = nullptr;
}
}
@@ -659,7 +661,7 @@ void RE_FreePersistentData(const Scene *scene)
}
}
else {
- for (Render *re = RenderGlobal.renderlist.first; re; re = re->next) {
+ LISTBASE_FOREACH (Render *, re, &RenderGlobal.renderlist) {
re_free_persistent_data(re);
}
}
@@ -711,7 +713,7 @@ void render_copy_renderdata(RenderData *to, RenderData *from)
BLI_freelistN(&to->views);
BKE_curvemapping_free_data(&to->mblur_shutter_curve);
- *to = *from;
+ memcpy(to, from, sizeof(*to));
BLI_duplicatelist(&to->views, &from->views);
BKE_curvemapping_copy_data(&to->mblur_shutter_curve, &from->mblur_shutter_curve);
@@ -779,14 +781,14 @@ void RE_InitState(Render *re,
if (had_freestyle || (re->r.mode & R_EDGE_FRS)) {
/* freestyle manipulates render layers so always have to free */
render_result_free(re->result);
- re->result = NULL;
+ re->result = nullptr;
}
else if (re->result) {
- ViewLayer *active_render_layer = BLI_findlink(&re->view_layers, re->active_view_layer);
- RenderLayer *rl;
+ ViewLayer *active_render_layer = static_cast<ViewLayer *>(
+ BLI_findlink(&re->view_layers, re->active_view_layer));
bool have_layer = false;
- for (rl = re->result->layers.first; rl; rl = rl->next) {
+ LISTBASE_FOREACH (RenderLayer *, rl, &re->result->layers) {
if (STREQ(rl->name, active_render_layer->name)) {
have_layer = true;
}
@@ -799,7 +801,7 @@ void RE_InitState(Render *re,
else {
/* free because resolution changed */
render_result_free(re->result);
- re->result = NULL;
+ re->result = nullptr;
}
}
}
@@ -807,7 +809,7 @@ void RE_InitState(Render *re,
/* make empty render result, so display callbacks can initialize */
render_result_free(re->result);
- re->result = MEM_callocN(sizeof(RenderResult), "new render result");
+ re->result = MEM_cnew<RenderResult>("new render result");
re->result->rectx = re->rectx;
re->result->recty = re->recty;
render_result_view_new(re->result, "");
@@ -906,13 +908,13 @@ void RE_gl_context_destroy(Render *re)
if (re->gl_context) {
if (re->gpu_context) {
WM_opengl_context_activate(re->gl_context);
- GPU_context_active_set(re->gpu_context);
- GPU_context_discard(re->gpu_context);
- re->gpu_context = NULL;
+ GPU_context_active_set(static_cast<GPUContext *>(re->gpu_context));
+ GPU_context_discard(static_cast<GPUContext *>(re->gpu_context));
+ re->gpu_context = nullptr;
}
WM_opengl_context_dispose(re->gl_context);
- re->gl_context = NULL;
+ re->gl_context = nullptr;
}
}
@@ -923,8 +925,8 @@ void *RE_gl_context_get(Render *re)
void *RE_gpu_context_get(Render *re)
{
- if (re->gpu_context == NULL) {
- re->gpu_context = GPU_context_create(NULL);
+ if (re->gpu_context == nullptr) {
+ re->gpu_context = GPU_context_create(nullptr);
}
return re->gpu_context;
}
@@ -974,7 +976,7 @@ static void render_result_uncrop(Render *re)
rres = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS);
rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data);
- render_result_clone_passes(re, rres, NULL);
+ render_result_clone_passes(re, rres, nullptr);
render_result_passes_allocated_ensure(rres);
render_result_merge(rres, re->result);
@@ -987,7 +989,7 @@ static void render_result_uncrop(Render *re)
BLI_rw_mutex_unlock(&re->resultmutex);
re->display_init(re->dih, re->result);
- re->display_update(re->duh, re->result, NULL);
+ re->display_update(re->duh, re->result, nullptr);
/* restore the disprect from border */
re->disprect = orig_disprect;
@@ -1007,7 +1009,7 @@ static void do_render_engine(Render *re)
{
Object *camera = RE_GetCamera(re);
/* also check for camera here */
- if (camera == NULL) {
+ if (camera == nullptr) {
BKE_report(re->reports, RPT_ERROR, "Cannot render, no camera");
G.is_break = true;
return;
@@ -1035,13 +1037,12 @@ static void do_render_compositor_scene(Render *re, Scene *sce, int cfra)
BKE_scene_camera_switch_update(sce);
/* exception: scene uses own size (unfinished code) */
- if (0) {
- winx = (sce->r.size * sce->r.xsch) / 100;
- winy = (sce->r.size * sce->r.ysch) / 100;
+ if (false) {
+ BKE_render_resolution(&sce->r, false, &winx, &winy);
}
/* initial setup */
- RE_InitState(resc, re, &sce->r, &sce->view_layers, NULL, winx, winy, &re->disprect);
+ RE_InitState(resc, re, &sce->r, &sce->view_layers, nullptr, winx, winy, &re->disprect);
/* We still want to use 'rendercache' setting from org (main) scene... */
resc->r.scemode = (resc->r.scemode & ~R_EXR_CACHE_FILE) | (re->r.scemode & R_EXR_CACHE_FILE);
@@ -1068,9 +1069,8 @@ static void do_render_compositor_scene(Render *re, Scene *sce, int cfra)
static int compositor_needs_render(Scene *sce, int this_scene)
{
bNodeTree *ntree = sce->nodetree;
- bNode *node;
- if (ntree == NULL) {
+ if (ntree == nullptr) {
return 1;
}
if (sce->use_nodes == false) {
@@ -1080,9 +1080,9 @@ static int compositor_needs_render(Scene *sce, int this_scene)
return 1;
}
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) {
- if (this_scene == 0 || node->id == NULL || node->id == &sce->id) {
+ if (this_scene == 0 || node->id == nullptr || node->id == &sce->id) {
return 1;
}
}
@@ -1093,11 +1093,10 @@ static int compositor_needs_render(Scene *sce, int this_scene)
/* Render all scenes within a compositor node tree. */
static void do_render_compositor_scenes(Render *re)
{
- bNode *node;
int cfra = re->scene->r.cfra;
Scene *restore_scene = re->scene;
- if (re->scene->nodetree == NULL) {
+ if (re->scene->nodetree == nullptr) {
return;
}
@@ -1106,12 +1105,12 @@ static void do_render_compositor_scenes(Render *re)
/* now foreach render-result node we do a full render */
/* results are stored in a way compositor will find it */
GSet *scenes_rendered = BLI_gset_ptr_new(__func__);
- for (node = re->scene->nodetree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &re->scene->nodetree->nodes) {
if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) {
if (node->id && node->id != (ID *)re->scene) {
Scene *scene = (Scene *)node->id;
if (!BLI_gset_haskey(scenes_rendered, scene) &&
- render_scene_has_layers_to_render(scene, false)) {
+ render_scene_has_layers_to_render(scene, nullptr)) {
do_render_compositor_scene(re, scene, cfra);
BLI_gset_add(scenes_rendered, scene);
node->typeinfo->updatefunc(restore_scene->nodetree, node);
@@ -1123,7 +1122,7 @@ static void do_render_compositor_scenes(Render *re)
}
}
}
- BLI_gset_free(scenes_rendered, NULL);
+ BLI_gset_free(scenes_rendered, nullptr);
if (changed_scene) {
/* If rendered another scene, switch back to the current scene with compositing nodes. */
@@ -1208,64 +1207,59 @@ static void do_render_compositor(Render *re)
/* If we have consistent depsgraph now would be a time to update them. */
}
- RenderView *rv;
- for (rv = re->result->views.first; rv; rv = rv->next) {
+ LISTBASE_FOREACH (RenderView *, rv, &re->result->views) {
ntreeCompositExecTree(
re->pipeline_scene_eval, ntree, &re->r, true, G.background == 0, rv->name);
}
- ntree->stats_draw = NULL;
- ntree->test_break = NULL;
- ntree->progress = NULL;
- ntree->tbh = ntree->sdh = ntree->prh = NULL;
+ ntree->stats_draw = nullptr;
+ ntree->test_break = nullptr;
+ ntree->progress = nullptr;
+ ntree->tbh = ntree->sdh = ntree->prh = nullptr;
}
}
}
/* Weak: the display callback wants an active render-layer pointer. */
- if (re->result != NULL) {
+ if (re->result != nullptr) {
re->result->renlay = render_get_active_layer(re, re->result);
- re->display_update(re->duh, re->result, NULL);
+ re->display_update(re->duh, re->result, nullptr);
}
}
static void renderresult_stampinfo(Render *re)
{
RenderResult rres;
- RenderView *rv;
- int nr;
+ int nr = 0;
/* this is the basic trick to get the displayed float or char rect from render result */
- nr = 0;
- for (rv = re->result->views.first; rv; rv = rv->next, nr++) {
+ LISTBASE_FOREACH (RenderView *, rv, &re->result->views) {
RE_SetActiveRenderView(re, rv->name);
RE_AcquireResultImage(re, &rres, nr);
Object *ob_camera_eval = DEG_get_evaluated_object(re->pipeline_depsgraph, RE_GetCamera(re));
BKE_image_stamp_buf(re->scene,
ob_camera_eval,
- (re->r.stamp & R_STAMP_STRIPMETA) ? rres.stamp_data : NULL,
+ (re->r.stamp & R_STAMP_STRIPMETA) ? rres.stamp_data : nullptr,
(unsigned char *)rres.rect32,
rres.rectf,
rres.rectx,
rres.recty,
4);
RE_ReleaseResultImage(re);
+ nr++;
}
}
int RE_seq_render_active(Scene *scene, RenderData *rd)
{
- Editing *ed;
- Sequence *seq;
-
- ed = scene->ed;
+ Editing *ed = scene->ed;
if (!(rd->scemode & R_DOSEQ) || !ed || !ed->seqbase.first) {
return 0;
}
- for (seq = ed->seqbase.first; seq; seq = seq->next) {
+ LISTBASE_FOREACH (Sequence *, seq, &ed->seqbase) {
if (seq->type != SEQ_TYPE_SOUND_RAM) {
return 1;
}
@@ -1283,7 +1277,6 @@ static void do_render_sequencer(Render *re)
int cfra = re->r.cfra;
SeqRenderData context;
int view_id, tot_views;
- struct ImBuf **ibuf_arr;
int re_x, re_y;
re->i.cfra = cfra;
@@ -1302,7 +1295,7 @@ static void do_render_sequencer(Render *re)
}
tot_views = BKE_scene_multiview_num_views_get(&re->r);
- ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * tot_views, "Sequencer Views ImBufs");
+ blender::Vector<ImBuf *> ibuf_arr(tot_views);
SEQ_render_new_render_data(re->main,
re->pipeline_depsgraph,
@@ -1327,7 +1320,7 @@ static void do_render_sequencer(Render *re)
SEQ_render_imbuf_from_sequencer_space(re->pipeline_scene_eval, ibuf_arr[view_id]);
}
else {
- ibuf_arr[view_id] = NULL;
+ ibuf_arr[view_id] = nullptr;
}
}
@@ -1347,7 +1340,7 @@ static void do_render_sequencer(Render *re)
if (ibuf_arr[view_id]->metadata && (re->r.stamp & R_STAMP_STRIPMETA)) {
/* ensure render stamp info first */
- BKE_render_result_stamp_info(NULL, NULL, rr, true);
+ BKE_render_result_stamp_info(nullptr, nullptr, rr, true);
BKE_stamp_info_from_imbuf(rr, ibuf_arr[view_id]);
}
@@ -1368,11 +1361,9 @@ static void do_render_sequencer(Render *re)
/* would mark display buffers as invalid */
RE_SetActiveRenderView(re, rv->name);
- re->display_update(re->duh, re->result, NULL);
+ re->display_update(re->duh, re->result, nullptr);
}
- MEM_freeN(ibuf_arr);
-
recurs_depth--;
/* just in case this flag went missing at some point */
@@ -1415,7 +1406,7 @@ static void do_render_full_pipeline(Render *re)
}
re->stats_draw(re->sdh, &re->i);
- re->display_update(re->duh, re->result, NULL);
+ re->display_update(re->duh, re->result, nullptr);
}
else {
do_render_compositor(re);
@@ -1426,7 +1417,7 @@ static void do_render_full_pipeline(Render *re)
re->stats_draw(re->sdh, &re->i);
/* save render result stamp if needed */
- if (re->result != NULL) {
+ if (re->result != nullptr) {
/* sequence rendering should have taken care of that already */
if (!(render_seq && (re->r.stamp & R_STAMP_STRIPMETA))) {
Object *ob_camera_eval = DEG_get_evaluated_object(re->pipeline_depsgraph, RE_GetCamera(re));
@@ -1436,7 +1427,7 @@ static void do_render_full_pipeline(Render *re)
/* stamp image info here */
if ((re->r.stamp & R_STAMP_ALL) && (re->r.stamp & R_STAMP_DRAW)) {
renderresult_stampinfo(re);
- re->display_update(re->duh, re->result, NULL);
+ re->display_update(re->duh, re->result, nullptr);
}
}
}
@@ -1444,38 +1435,34 @@ static void do_render_full_pipeline(Render *re)
static bool check_valid_compositing_camera(Scene *scene, Object *camera_override)
{
if (scene->r.scemode & R_DOCOMP && scene->use_nodes) {
- bNode *node = scene->nodetree->nodes.first;
-
- while (node) {
+ LISTBASE_FOREACH (bNode *, node, &scene->nodetree->nodes) {
if (node->type == CMP_NODE_R_LAYERS && (node->flag & NODE_MUTED) == 0) {
Scene *sce = node->id ? (Scene *)node->id : scene;
- if (sce->camera == NULL) {
- sce->camera = BKE_view_layer_camera_find(BKE_view_layer_default_render(sce));
+ if (sce->camera == nullptr) {
+ sce->camera = BKE_view_layer_camera_find(sce, BKE_view_layer_default_render(sce));
}
- if (sce->camera == NULL) {
+ if (sce->camera == nullptr) {
/* all render layers nodes need camera */
return false;
}
}
- node = node->next;
}
return true;
}
- return (camera_override != NULL || scene->camera != NULL);
+ return (camera_override != nullptr || scene->camera != nullptr);
}
static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportList *reports)
{
- SceneRenderView *srv;
bool active_view = false;
- if (camera == NULL || (scene->r.scemode & R_MULTIVIEW) == 0) {
+ if (camera == nullptr || (scene->r.scemode & R_MULTIVIEW) == 0) {
return true;
}
- for (srv = scene->r.views.first; srv; srv = srv->next) {
+ LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) {
active_view = true;
@@ -1509,8 +1496,8 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
{
const char *err_msg = "No camera found in scene \"%s\"";
- if (camera_override == NULL && scene->camera == NULL) {
- scene->camera = BKE_view_layer_camera_find(BKE_view_layer_default_render(scene));
+ if (camera_override == nullptr && scene->camera == nullptr) {
+ scene->camera = BKE_view_layer_camera_find(scene, BKE_view_layer_default_render(scene));
}
if (!check_valid_camera_multiview(scene, scene->camera, reports)) {
@@ -1519,16 +1506,15 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
if (RE_seq_render_active(scene, &scene->r)) {
if (scene->ed) {
- Sequence *seq = scene->ed->seqbase.first;
-
- while (seq) {
+ LISTBASE_FOREACH (Sequence *, seq, &scene->ed->seqbase) {
if ((seq->type == SEQ_TYPE_SCENE) && ((seq->flag & SEQ_SCENE_STRIPS) == 0) &&
- (seq->scene != NULL)) {
+ (seq->scene != nullptr)) {
if (!seq->scene_camera) {
if (!seq->scene->camera &&
- !BKE_view_layer_camera_find(BKE_view_layer_default_render(seq->scene))) {
+ !BKE_view_layer_camera_find(seq->scene,
+ BKE_view_layer_default_render(seq->scene))) {
/* camera could be unneeded due to composite nodes */
- Object *override = (seq->scene == scene) ? camera_override : NULL;
+ Object *override = (seq->scene == scene) ? camera_override : nullptr;
if (!check_valid_compositing_camera(seq->scene, override)) {
BKE_reportf(reports, RPT_ERROR, err_msg, seq->scene->id.name + 2);
@@ -1540,8 +1526,6 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
return false;
}
}
-
- seq = seq->next;
}
}
}
@@ -1555,9 +1539,7 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
static bool node_tree_has_compositor_output(bNodeTree *ntree)
{
- bNode *node;
-
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (ELEM(node->type, CMP_NODE_COMPOSITE, CMP_NODE_OUTPUT_FILE)) {
return true;
}
@@ -1589,7 +1571,7 @@ bool RE_is_rendering_allowed(Scene *scene,
if (scene->r.border.xmax <= scene->r.border.xmin ||
scene->r.border.ymax <= scene->r.border.ymin) {
BKE_report(reports, RPT_ERROR, "No border area selected");
- return 0;
+ return false;
}
}
@@ -1604,28 +1586,28 @@ bool RE_is_rendering_allowed(Scene *scene,
/* Compositor */
if (!scene->nodetree) {
BKE_report(reports, RPT_ERROR, "No node tree in scene");
- return 0;
+ return false;
}
if (!check_compositor_output(scene)) {
BKE_report(reports, RPT_ERROR, "No render output node in scene");
- return 0;
+ return false;
}
}
else {
/* Regular Render */
if (!render_scene_has_layers_to_render(scene, single_layer)) {
BKE_report(reports, RPT_ERROR, "All render layers are disabled");
- return 0;
+ return false;
}
}
/* check valid camera, without camera render is OK (compo, seq) */
if (!check_valid_camera(scene, camera_override, reports)) {
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static void update_physics_cache(Render *re,
@@ -1675,8 +1657,7 @@ static int render_init_from_main(Render *re,
* r.border is the clipping rect */
/* calculate actual render result and display size */
- winx = (rd->size * rd->xsch) / 100;
- winy = (rd->size * rd->ysch) / 100;
+ BKE_render_resolution(rd, false, &winx, &winy);
/* We always render smaller part, inserting it in larger image is compositor business,
* it uses 'disprect' for it. */
@@ -1710,7 +1691,7 @@ static int render_init_from_main(Render *re,
* can be later set as render profile option
* and default for background render.
*/
- if (0) {
+ if (false) {
/* make sure dynamics are up to date */
ViewLayer *view_layer = BKE_view_layer_context_active_PLACEHOLDER(scene);
update_physics_cache(re, scene, view_layer, anim_init);
@@ -1722,7 +1703,7 @@ static int render_init_from_main(Render *re,
BLI_rw_mutex_unlock(&re->resultmutex);
}
- RE_InitState(re, NULL, &scene->r, &scene->view_layers, single_layer, winx, winy, &disprect);
+ RE_InitState(re, nullptr, &scene->r, &scene->view_layers, single_layer, winx, winy, &disprect);
if (!re->ok) { /* if an error was printed, abort */
return 0;
}
@@ -1770,19 +1751,19 @@ 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;
+ re->engine = nullptr;
}
- if (re->pipeline_depsgraph != NULL) {
+ if (re->pipeline_depsgraph != nullptr) {
DEG_graph_free(re->pipeline_depsgraph);
- re->pipeline_depsgraph = NULL;
- re->pipeline_scene_eval = NULL;
+ re->pipeline_depsgraph = nullptr;
+ re->pipeline_scene_eval = nullptr;
}
/* Destroy the opengl context in the correct thread. */
RE_gl_context_destroy(re);
/* In the case the engine did not mark tiles as finished (un-highlight, which could happen in the
* case of cancelled render) ensure the storage is empty. */
- if (re->highlighted_tiles != NULL) {
+ if (re->highlighted_tiles != nullptr) {
BLI_mutex_lock(&re->highlighted_tiles_mutex);
/* Rendering is supposed to be finished here, so no new tiles are expected to be written.
@@ -1790,7 +1771,7 @@ static void render_pipeline_free(Render *re)
BLI_assert(re->highlighted_tiles);
BLI_gset_free(re->highlighted_tiles, MEM_freeN);
- re->highlighted_tiles = NULL;
+ re->highlighted_tiles = nullptr;
BLI_mutex_unlock(&re->highlighted_tiles_mutex);
}
@@ -1815,7 +1796,8 @@ void RE_RenderFrame(Render *re,
scene->r.subframe = subframe;
if (render_init_from_main(re, &scene->r, bmain, scene, single_layer, camera_override, 0, 0)) {
- const RenderData rd = scene->r;
+ RenderData rd;
+ memcpy(&rd, &scene->r, sizeof(rd));
MEM_reset_peak_memory();
render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_PRE);
@@ -1838,10 +1820,10 @@ void RE_RenderFrame(Render *re,
&rd.im_format,
(rd.scemode & R_EXTENSION) != 0,
false,
- NULL);
+ nullptr);
/* reports only used for Movie */
- do_write_image_or_movie(re, bmain, scene, NULL, 0, name);
+ do_write_image_or_movie(re, bmain, scene, nullptr, 0, name);
}
}
@@ -1871,7 +1853,7 @@ static void change_renderdata_engine(Render *re, const char *new_engine)
if (!STREQ(re->r.engine, new_engine)) {
if (re->engine) {
RE_engine_free(re->engine);
- re->engine = NULL;
+ re->engine = nullptr;
}
BLI_strncpy(re->r.engine, new_engine, sizeof(re->r.engine));
}
@@ -1886,7 +1868,7 @@ static bool use_eevee_for_freestyle_render(Render *re)
void RE_RenderFreestyleStrokes(Render *re, Main *bmain, Scene *scene, int render)
{
re->result_ok = 0;
- if (render_init_from_main(re, &scene->r, bmain, scene, NULL, NULL, 0, 0)) {
+ if (render_init_from_main(re, &scene->r, bmain, scene, nullptr, nullptr, 0, 0)) {
if (render) {
char scene_engine[32];
BLI_strncpy(scene_engine, re->r.engine, sizeof(scene_engine));
@@ -1913,7 +1895,8 @@ void RE_RenderFreestyleExternal(Render *re)
LISTBASE_FOREACH (RenderView *, rv, &re->result->views) {
RE_SetActiveRenderView(re, rv->name);
- ViewLayer *active_view_layer = BLI_findlink(&re->view_layers, re->active_view_layer);
+ ViewLayer *active_view_layer = static_cast<ViewLayer *>(
+ BLI_findlink(&re->view_layers, re->active_view_layer));
FRS_begin_stroke_rendering(re);
LISTBASE_FOREACH (ViewLayer *, view_layer, &re->view_layers) {
@@ -1953,7 +1936,7 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
}
ImageFormatData image_format;
- BKE_image_format_init_for_write(&image_format, scene, NULL);
+ BKE_image_format_init_for_write(&image_format, scene, nullptr);
const bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2;
const float dither = scene->r.dither_intensity;
@@ -1966,15 +1949,17 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
IMB_colormanagement_imbuf_for_write(ibuf, true, false, &image_format);
- ok &= mh->append_movie(movie_ctx_arr[view_id],
- rd,
- preview ? scene->r.psfra : scene->r.sfra,
- scene->r.cfra,
- (int *)ibuf->rect,
- ibuf->x,
- ibuf->y,
- suffix,
- reports);
+ if (!mh->append_movie(movie_ctx_arr[view_id],
+ rd,
+ preview ? scene->r.psfra : scene->r.sfra,
+ scene->r.cfra,
+ (int *)ibuf->rect,
+ ibuf->x,
+ ibuf->y,
+ suffix,
+ reports)) {
+ ok = false;
+ }
/* imbuf knows which rects are not part of ibuf */
IMB_freeImBuf(ibuf);
@@ -1983,7 +1968,7 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
}
else { /* R_IMF_VIEWS_STEREO_3D */
const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
- ImBuf *ibuf_arr[3] = {NULL};
+ ImBuf *ibuf_arr[3] = {nullptr};
int i;
BLI_assert((totvideos == 1) && (image_format.views_format == R_IMF_VIEWS_STEREO_3D));
@@ -1997,7 +1982,7 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]);
- ok = mh->append_movie(movie_ctx_arr[0],
+ if (!mh->append_movie(movie_ctx_arr[0],
rd,
preview ? scene->r.psfra : scene->r.sfra,
scene->r.cfra,
@@ -2005,7 +1990,9 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
ibuf_arr[2]->x,
ibuf_arr[2]->y,
"",
- reports);
+ reports)) {
+ ok = false;
+ }
for (i = 0; i < 3; i++) {
/* imbuf knows which rects are not part of ibuf */
@@ -2055,7 +2042,7 @@ static bool do_write_image_or_movie(Render *re,
&scene->r.im_format,
(scene->r.scemode & R_EXTENSION) != 0,
true,
- NULL);
+ nullptr);
}
/* write images as individual images or stereo */
@@ -2075,7 +2062,7 @@ static bool do_write_image_or_movie(Render *re,
fflush(stdout);
/* NOTE: using G_MAIN seems valid here???
- * Not sure it's actually even used anyway, we could as well pass NULL? */
+ * Not sure it's actually even used anyway, we could as well pass nullptr? */
render_callback_exec_null(re, G_MAIN, BKE_CB_EVT_RENDER_STATS);
if (do_write_file) {
@@ -2138,8 +2125,9 @@ void RE_RenderAnim(Render *re,
* copying (e.g. alter the output path). */
render_callback_exec_id(re, re->main, &scene->id, BKE_CB_EVT_RENDER_INIT);
- const RenderData rd = scene->r;
- bMovieHandle *mh = NULL;
+ RenderData rd;
+ memcpy(&rd, &scene->r, sizeof(rd));
+ bMovieHandle *mh = nullptr;
const int cfra_old = rd.cfra;
const float subframe_old = rd.subframe;
int nfra, totrendered = 0, totskipped = 0;
@@ -2169,12 +2157,12 @@ void RE_RenderAnim(Render *re,
get_videos_dimensions(re, &rd, &width, &height);
mh = BKE_movie_handle_get(rd.im_format.imtype);
- if (mh == NULL) {
+ if (mh == nullptr) {
BKE_report(re->reports, RPT_ERROR, "Movie format unsupported");
return;
}
- re->movie_ctx_arr = MEM_mallocN(sizeof(void *) * totvideos, "Movies' Context");
+ re->movie_ctx_arr = MEM_cnew_array<void *>(totvideos, "Movies' Context");
for (i = 0; i < totvideos; i++) {
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&re->r, i);
@@ -2254,7 +2242,7 @@ void RE_RenderAnim(Render *re,
&rd.im_format,
(rd.scemode & R_EXTENSION) != 0,
true,
- NULL);
+ nullptr);
}
if (rd.mode & R_NO_OVERWRITE) {
@@ -2266,11 +2254,10 @@ void RE_RenderAnim(Render *re,
}
}
else {
- SceneRenderView *srv;
bool is_skip = false;
char filepath[FILE_MAX];
- for (srv = scene->r.views.first; srv; srv = srv->next) {
+ LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) {
continue;
}
@@ -2298,10 +2285,9 @@ void RE_RenderAnim(Render *re,
}
}
else {
- SceneRenderView *srv;
char filepath[FILE_MAX];
- for (srv = scene->r.views.first; srv; srv = srv->next) {
+ LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) {
continue;
}
@@ -2328,7 +2314,7 @@ void RE_RenderAnim(Render *re,
if (re->test_break(re->tbh) == 0) {
if (!G.is_break) {
- if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) {
+ if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, nullptr)) {
G.is_break = true;
}
}
@@ -2348,10 +2334,9 @@ void RE_RenderAnim(Render *re,
}
}
else {
- SceneRenderView *srv;
char filepath[FILE_MAX];
- for (srv = scene->r.views.first; srv; srv = srv->next) {
+ LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) {
continue;
}
@@ -2409,10 +2394,9 @@ void RE_PreviewRender(Render *re, Main *bmain, Scene *sce)
Object *camera;
int winx, winy;
- winx = (sce->r.size * sce->r.xsch) / 100;
- winy = (sce->r.size * sce->r.ysch) / 100;
+ BKE_render_resolution(&sce->r, false, &winx, &winy);
- RE_InitState(re, NULL, &sce->r, &sce->view_layers, NULL, winx, winy, NULL);
+ RE_InitState(re, nullptr, &sce->r, &sce->view_layers, nullptr, winx, winy, nullptr);
re->main = bmain;
re->scene = sce;
@@ -2425,7 +2409,7 @@ void RE_PreviewRender(Render *re, Main *bmain, Scene *sce)
/* No persistent data for preview render. */
if (re->engine) {
RE_engine_free(re->engine);
- re->engine = NULL;
+ re->engine = nullptr;
}
}
@@ -2439,8 +2423,7 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode)
rcti disprect;
/* calculate actual render result and display size */
- winx = (scene->r.size * scene->r.xsch) / 100;
- winy = (scene->r.size * scene->r.ysch) / 100;
+ BKE_render_resolution(&scene->r, false, &winx, &winy);
/* only in movie case we render smaller part */
if (scene->r.mode & R_BORDER) {
@@ -2462,10 +2445,10 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode)
/* get render: it can be called from UI with draw callbacks */
re = RE_GetSceneRender(scene);
- if (re == NULL) {
+ if (re == nullptr) {
re = RE_NewSceneRender(scene);
}
- RE_InitState(re, NULL, &scene->r, &scene->view_layers, NULL, winx, winy, &disprect);
+ RE_InitState(re, nullptr, &scene->r, &scene->view_layers, nullptr, winx, winy, &disprect);
re->scene = scene;
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
@@ -2481,17 +2464,17 @@ void RE_layer_load_from_file(
RenderLayer *layer, ReportList *reports, const char *filepath, int x, int y)
{
/* OCIO_TODO: assume layer was saved in default color space */
- ImBuf *ibuf = IMB_loadiffname(filepath, IB_rect, NULL);
- RenderPass *rpass = NULL;
+ ImBuf *ibuf = IMB_loadiffname(filepath, IB_rect, nullptr);
+ RenderPass *rpass = nullptr;
/* multiview: since the API takes no 'view', we use the first combined pass found */
- for (rpass = layer->passes.first; rpass; rpass = rpass->next) {
+ for (rpass = static_cast<RenderPass *>(layer->passes.first); rpass; rpass = rpass->next) {
if (STREQ(rpass->name, RE_PASSNAME_COMBINED)) {
break;
}
}
- if (rpass == NULL) {
+ if (rpass == nullptr) {
BKE_reportf(reports,
RPT_ERROR,
"%s: no Combined pass found in the render layer '%s'",
@@ -2501,7 +2484,7 @@ void RE_layer_load_from_file(
if (ibuf && (ibuf->rect || ibuf->rect_float)) {
if (ibuf->x == layer->rectx && ibuf->y == layer->recty) {
- if (ibuf->rect_float == NULL) {
+ if (ibuf->rect_float == nullptr) {
IMB_float_from_rect(ibuf);
}
@@ -2511,7 +2494,7 @@ void RE_layer_load_from_file(
if ((ibuf->x - x >= layer->rectx) && (ibuf->y - y >= layer->recty)) {
ImBuf *ibuf_clip;
- if (ibuf->rect_float == NULL) {
+ if (ibuf->rect_float == nullptr) {
IMB_float_from_rect(ibuf);
}
@@ -2546,19 +2529,19 @@ void RE_layer_load_from_file(
void RE_result_load_from_file(RenderResult *result, ReportList *reports, const char *filepath)
{
- if (!render_result_exr_file_read_path(result, NULL, filepath)) {
+ if (!render_result_exr_file_read_path(result, nullptr, filepath)) {
BKE_reportf(reports, RPT_ERROR, "%s: failed to load '%s'", __func__, filepath);
return;
}
}
-bool RE_layers_have_name(struct RenderResult *rr)
+bool RE_layers_have_name(struct RenderResult *result)
{
- switch (BLI_listbase_count_at_most(&rr->layers, 2)) {
+ switch (BLI_listbase_count_at_most(&result->layers, 2)) {
case 0:
return false;
case 1:
- return (((RenderLayer *)rr->layers.first)->name[0] != '\0');
+ return (((RenderLayer *)result->layers.first)->name[0] != '\0');
default:
return true;
}
@@ -2578,19 +2561,17 @@ bool RE_passes_have_name(struct RenderLayer *rl)
RenderPass *RE_pass_find_by_name(RenderLayer *rl, const char *name, const char *viewname)
{
- RenderPass *rp = NULL;
-
- for (rp = rl->passes.last; rp; rp = rp->prev) {
+ LISTBASE_FOREACH_BACKWARD (RenderPass *, rp, &rl->passes) {
if (STREQ(rp->name, name)) {
- if (viewname == NULL || viewname[0] == '\0') {
- break;
+ if (viewname == nullptr || viewname[0] == '\0') {
+ return rp;
}
if (STREQ(rp->view, viewname)) {
- break;
+ return rp;
}
}
}
- return rp;
+ return nullptr;
}
RenderPass *RE_pass_find_by_type(RenderLayer *rl, int passtype, const char *viewname)
@@ -2628,15 +2609,15 @@ RenderPass *RE_pass_find_by_type(RenderLayer *rl, int passtype, const char *view
#undef CHECK_PASS
- return NULL;
+ return nullptr;
}
RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const char *viewname)
{
- RenderLayer *rl = BLI_findstring(&rr->layers, layername, offsetof(RenderLayer, name));
+ RenderLayer *rl = RE_GetRenderLayer(rr, layername);
/* only create render layer if not exist */
if (!rl) {
- rl = MEM_callocN(sizeof(RenderLayer), layername);
+ rl = MEM_cnew<RenderLayer>(layername);
BLI_addtail(&rr->layers, rl);
BLI_strncpy(rl->name, layername, sizeof(rl->name));
rl->layflag = SCE_LAY_SOLID;
diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.cc
index 3386a74daba..8b7a07e2b3f 100644
--- a/source/blender/render/intern/render_result.c
+++ b/source/blender/render/intern/render_result.cc
@@ -5,10 +5,10 @@
* \ingroup render
*/
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
#include "MEM_guardedalloc.h"
@@ -28,6 +28,7 @@
#include "BKE_image.h"
#include "BKE_image_format.h"
#include "BKE_image_save.h"
+#include "BKE_main.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -46,7 +47,7 @@
static void render_result_views_free(RenderResult *rr)
{
while (rr->views.first) {
- RenderView *rv = rr->views.first;
+ RenderView *rv = static_cast<RenderView *>(rr->views.first);
BLI_remlink(&rr->views, rv);
if (rv->rect32) {
@@ -69,15 +70,15 @@ static void render_result_views_free(RenderResult *rr)
void render_result_free(RenderResult *rr)
{
- if (rr == NULL) {
+ if (rr == nullptr) {
return;
}
while (rr->layers.first) {
- RenderLayer *rl = rr->layers.first;
+ RenderLayer *rl = static_cast<RenderLayer *>(rr->layers.first);
while (rl->passes.first) {
- RenderPass *rpass = rl->passes.first;
+ RenderPass *rpass = static_cast<RenderPass *>(rl->passes.first);
if (rpass->rect) {
MEM_freeN(rpass->rect);
}
@@ -130,16 +131,14 @@ void render_result_free_list(ListBase *lb, RenderResult *rr)
void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src)
{
- RenderView *rview;
-
- if (dst == NULL || src == NULL) {
+ if (dst == nullptr || src == nullptr) {
return;
}
- for (rview = src->views.first; rview; rview = rview->next) {
+ LISTBASE_FOREACH (RenderView *, rview, &src->views) {
RenderView *rv;
- rv = MEM_mallocN(sizeof(RenderView), "new render view");
+ rv = MEM_cnew<RenderView>("new render view");
BLI_addtail(&dst->views, rv);
BLI_strncpy(rv->name, rview->name, sizeof(rv->name));
@@ -151,12 +150,12 @@ void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src)
void render_result_views_shallowdelete(RenderResult *rr)
{
- if (rr == NULL) {
+ if (rr == nullptr) {
return;
}
while (rr->views.first) {
- RenderView *rv = rr->views.first;
+ RenderView *rv = static_cast<RenderView *>(rr->views.first);
BLI_remlink(&rr->views, rv);
MEM_freeN(rv);
}
@@ -166,12 +165,12 @@ void render_result_views_shallowdelete(RenderResult *rr)
static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp)
{
- if (rp->rect != NULL) {
+ if (rp->rect != nullptr) {
return;
}
const size_t rectsize = ((size_t)rr->rectx) * rr->recty * rp->channels;
- rp->rect = MEM_callocN(sizeof(float) * rectsize, rp->name);
+ rp->rect = MEM_cnew_array<float>(rectsize, rp->name);
if (STREQ(rp->name, RE_PASSNAME_VECTOR)) {
/* initialize to max speed */
@@ -197,7 +196,7 @@ RenderPass *render_layer_add_pass(RenderResult *rr,
const bool allocate)
{
const int view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name));
- RenderPass *rpass = MEM_callocN(sizeof(RenderPass), name);
+ RenderPass *rpass = MEM_cnew<RenderPass>(name);
rpass->channels = channels;
rpass->rectx = rl->rectx;
@@ -208,14 +207,15 @@ RenderPass *render_layer_add_pass(RenderResult *rr,
BLI_strncpy(rpass->chan_id, chan_id, sizeof(rpass->chan_id));
BLI_strncpy(rpass->view, viewname, sizeof(rpass->view));
RE_render_result_full_channel_name(
- rpass->fullname, NULL, rpass->name, rpass->view, rpass->chan_id, -1);
+ rpass->fullname, nullptr, rpass->name, rpass->view, rpass->chan_id, -1);
if (rl->exrhandle) {
int a;
for (a = 0; a < channels; a++) {
char passname[EXR_PASS_MAXNAME];
- RE_render_result_full_channel_name(passname, NULL, rpass->name, NULL, rpass->chan_id, a);
- IMB_exr_add_channel(rl->exrhandle, rl->name, passname, viewname, 0, 0, NULL, false);
+ RE_render_result_full_channel_name(
+ passname, nullptr, rpass->name, nullptr, rpass->chan_id, a);
+ IMB_exr_add_channel(rl->exrhandle, rl->name, passname, viewname, 0, 0, nullptr, false);
}
}
@@ -239,17 +239,16 @@ RenderResult *render_result_new(Render *re,
{
RenderResult *rr;
RenderLayer *rl;
- RenderView *rv;
int rectx, recty;
rectx = BLI_rcti_size_x(partrct);
recty = BLI_rcti_size_y(partrct);
if (rectx <= 0 || recty <= 0) {
- return NULL;
+ return nullptr;
}
- rr = MEM_callocN(sizeof(RenderResult), "new render result");
+ rr = MEM_cnew<RenderResult>("new render result");
rr->rectx = rectx;
rr->recty = recty;
rr->renrect.xmin = 0;
@@ -273,7 +272,7 @@ RenderResult *render_result_new(Render *re,
}
}
- rl = MEM_callocN(sizeof(RenderLayer), "new render layer");
+ rl = MEM_cnew<RenderLayer>("new render layer");
BLI_addtail(&rr->layers, rl);
BLI_strncpy(rl->name, view_layer->name, sizeof(rl->name));
@@ -284,7 +283,7 @@ RenderResult *render_result_new(Render *re,
rl->rectx = rectx;
rl->recty = recty;
- for (rv = rr->views.first; rv; rv = rv->next) {
+ LISTBASE_FOREACH (RenderView *, rv, &rr->views) {
const char *view = rv->name;
if (viewname && viewname[0]) {
@@ -295,9 +294,9 @@ RenderResult *render_result_new(Render *re,
#define RENDER_LAYER_ADD_PASS_SAFE(rr, rl, channels, name, viewname, chan_id) \
do { \
- if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id, false) == NULL) { \
+ if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id, false) == nullptr) { \
render_result_free(rr); \
- return NULL; \
+ return nullptr; \
} \
} while (false)
@@ -383,13 +382,13 @@ RenderResult *render_result_new(Render *re,
/* Preview-render doesn't do layers, so we make a default one. */
if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) {
- rl = MEM_callocN(sizeof(RenderLayer), "new render layer");
+ rl = MEM_cnew<RenderLayer>("new render layer");
BLI_addtail(&rr->layers, rl);
rl->rectx = rectx;
rl->recty = recty;
- for (rv = rr->views.first; rv; rv = rv->next) {
+ LISTBASE_FOREACH (RenderView *, rv, &rr->views) {
const char *view = rv->name;
if (viewname && viewname[0]) {
@@ -424,7 +423,7 @@ RenderResult *render_result_new(Render *re,
void render_result_passes_allocated_ensure(RenderResult *rr)
{
- if (rr == NULL) {
+ if (rr == nullptr) {
/* Happens when the result was not yet allocated for the current scene or slot configuration.
*/
return;
@@ -432,7 +431,7 @@ void render_result_passes_allocated_ensure(RenderResult *rr)
LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
LISTBASE_FOREACH (RenderPass *, rp, &rl->passes) {
- if (rl->exrhandle != NULL && !STREQ(rp->name, RE_PASSNAME_COMBINED)) {
+ if (rl->exrhandle != nullptr && !STREQ(rp->name, RE_PASSNAME_COMBINED)) {
continue;
}
@@ -445,24 +444,20 @@ void render_result_passes_allocated_ensure(RenderResult *rr)
void render_result_clone_passes(Render *re, RenderResult *rr, const char *viewname)
{
- RenderLayer *rl;
- RenderPass *main_rp;
-
- for (rl = rr->layers.first; rl; rl = rl->next) {
- RenderLayer *main_rl = BLI_findstring(
- &re->result->layers, rl->name, offsetof(RenderLayer, name));
+ LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
+ RenderLayer *main_rl = RE_GetRenderLayer(re->result, rl->name);
if (!main_rl) {
continue;
}
- for (main_rp = main_rl->passes.first; main_rp; main_rp = main_rp->next) {
+ LISTBASE_FOREACH (RenderPass *, main_rp, &main_rl->passes) {
if (viewname && viewname[0] && !STREQ(main_rp->view, viewname)) {
continue;
}
/* Compare fullname to make sure that the view also is equal. */
- RenderPass *rp = BLI_findstring(
- &rl->passes, main_rp->fullname, offsetof(RenderPass, fullname));
+ RenderPass *rp = static_cast<RenderPass *>(
+ BLI_findstring(&rl->passes, main_rp->fullname, offsetof(RenderPass, fullname)));
if (!rp) {
render_layer_add_pass(
rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id, false);
@@ -479,16 +474,12 @@ void RE_create_render_pass(RenderResult *rr,
const char *viewname,
const bool allocate)
{
- RenderLayer *rl;
- RenderPass *rp;
- RenderView *rv;
-
- for (rl = rr->layers.first; rl; rl = rl->next) {
+ LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
if (layername && layername[0] && !STREQ(rl->name, layername)) {
continue;
}
- for (rv = rr->views.first; rv; rv = rv->next) {
+ LISTBASE_FOREACH (RenderView *, rv, &rr->views) {
const char *view = rv->name;
if (viewname && viewname[0] && !STREQ(view, viewname)) {
@@ -496,17 +487,15 @@ void RE_create_render_pass(RenderResult *rr,
}
/* Ensure that the pass doesn't exist yet. */
- for (rp = rl->passes.first; rp; rp = rp->next) {
- if (!STREQ(rp->name, name)) {
- continue;
- }
- if (!STREQ(rp->view, view)) {
- continue;
+ bool pass_exists = false;
+ LISTBASE_FOREACH (RenderPass *, rp, &rl->passes) {
+ if (STREQ(rp->name, name) && STREQ(rp->view, view)) {
+ pass_exists = true;
+ break;
}
- break;
}
- if (!rp) {
+ if (!pass_exists) {
render_layer_add_pass(rr, rl, channels, name, view, chan_id, allocate);
}
}
@@ -587,10 +576,9 @@ static int passtype_from_name(const char *name)
/* callbacks for render_result_new_from_exr */
static void *ml_addlayer_cb(void *base, const char *str)
{
- RenderResult *rr = base;
- RenderLayer *rl;
+ RenderResult *rr = static_cast<RenderResult *>(base);
- rl = MEM_callocN(sizeof(RenderLayer), "new render layer");
+ RenderLayer *rl = MEM_cnew<RenderLayer>("new render layer");
BLI_addtail(&rr->layers, rl);
BLI_strncpy(rl->name, str, EXR_LAY_MAXNAME);
@@ -605,9 +593,9 @@ static void ml_addpass_cb(void *base,
const char *chan_id,
const char *view)
{
- RenderResult *rr = base;
- RenderLayer *rl = lay;
- RenderPass *rpass = MEM_callocN(sizeof(RenderPass), "loaded pass");
+ RenderResult *rr = static_cast<RenderResult *>(base);
+ RenderLayer *rl = static_cast<RenderLayer *>(lay);
+ RenderPass *rpass = MEM_cnew<RenderPass>("loaded pass");
BLI_addtail(&rl->passes, rpass);
rpass->channels = totchan;
@@ -619,7 +607,7 @@ static void ml_addpass_cb(void *base,
rpass->rect = rect;
BLI_strncpy(rpass->name, name, EXR_PASS_MAXNAME);
BLI_strncpy(rpass->view, view, sizeof(rpass->view));
- RE_render_result_full_channel_name(rpass->fullname, NULL, name, view, rpass->chan_id, -1);
+ RE_render_result_full_channel_name(rpass->fullname, nullptr, name, view, rpass->chan_id, -1);
if (view[0] != '\0') {
rpass->view_id = BLI_findstringindex(&rr->views, view, offsetof(RenderView, name));
@@ -631,10 +619,9 @@ static void ml_addpass_cb(void *base,
static void *ml_addview_cb(void *base, const char *str)
{
- RenderResult *rr = base;
- RenderView *rv;
+ RenderResult *rr = static_cast<RenderResult *>(base);
- rv = MEM_callocN(sizeof(RenderView), "new render view");
+ RenderView *rv = MEM_cnew<RenderView>("new render view");
BLI_strncpy(rv->name, str, EXR_VIEW_MAXNAME);
/* For stereo drawing we need to ensure:
@@ -645,9 +632,10 @@ static void *ml_addview_cb(void *base, const char *str)
BLI_addhead(&rr->views, rv);
}
else if (STREQ(str, STEREO_RIGHT_NAME)) {
- RenderView *left_rv = BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name));
+ RenderView *left_rv = static_cast<RenderView *>(
+ BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)));
- if (left_rv == NULL) {
+ if (left_rv == nullptr) {
BLI_addhead(&rr->views, rv);
}
else {
@@ -719,9 +707,7 @@ static int order_render_passes(const void *a, const void *b)
RenderResult *render_result_new_from_exr(
void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty)
{
- RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__);
- RenderLayer *rl;
- RenderPass *rpass;
+ RenderResult *rr = MEM_cnew<RenderResult>(__func__);
const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(
COLOR_ROLE_SCENE_LINEAR);
@@ -730,13 +716,13 @@ RenderResult *render_result_new_from_exr(
IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb);
- for (rl = rr->layers.first; rl; rl = rl->next) {
+ LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
rl->rectx = rectx;
rl->recty = recty;
BLI_listbase_sort(&rl->passes, order_render_passes);
- for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
+ LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
rpass->rectx = rectx;
rpass->recty = recty;
@@ -757,21 +743,19 @@ RenderResult *render_result_new_from_exr(
void render_result_view_new(RenderResult *rr, const char *viewname)
{
- RenderView *rv = MEM_callocN(sizeof(RenderView), "new render view");
+ RenderView *rv = MEM_cnew<RenderView>("new render view");
BLI_addtail(&rr->views, rv);
BLI_strncpy(rv->name, viewname, sizeof(rv->name));
}
void render_result_views_new(RenderResult *rr, const RenderData *rd)
{
- SceneRenderView *srv;
-
/* clear previously existing views - for sequencer */
render_result_views_free(rr);
/* check renderdata for amount of views */
if (rd->scemode & R_MULTIVIEW) {
- for (srv = rd->views.first; srv; srv = srv->next) {
+ LISTBASE_FOREACH (SceneRenderView *, srv, &rd->views) {
if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) {
continue;
}
@@ -812,17 +796,17 @@ static void do_merge_tile(
void render_result_merge(RenderResult *rr, RenderResult *rrpart)
{
- RenderLayer *rl, *rlp;
- RenderPass *rpass, *rpassp;
+ LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
+ RenderLayer *rlp = RE_GetRenderLayer(rrpart, rl->name);
- for (rl = rr->layers.first; rl; rl = rl->next) {
- rlp = RE_GetRenderLayer(rrpart, rl->name);
if (rlp) {
/* Passes are allocated in sync. */
- for (rpass = rl->passes.first, rpassp = rlp->passes.first; rpass && rpassp;
+ for (RenderPass *rpass = static_cast<RenderPass *>(rl->passes.first),
+ *rpassp = static_cast<RenderPass *>(rlp->passes.first);
+ rpass && rpassp;
rpass = rpass->next) {
/* For save buffers, skip any passes that are only saved to disk. */
- if (rpass->rect == NULL || rpassp->rect == NULL) {
+ if (rpass->rect == nullptr || rpassp->rect == nullptr) {
continue;
}
/* Render-result have all passes, render-part only the active view's passes. */
@@ -845,21 +829,16 @@ void render_result_single_layer_begin(Render *re)
{
/* all layers except the active one get temporally pushed away */
- /* officially pushed result should be NULL... error can happen with do_seq */
+ /* officially pushed result should be nullptr... error can happen with do_seq */
RE_FreeRenderResult(re->pushedresult);
re->pushedresult = re->result;
- re->result = NULL;
+ re->result = nullptr;
}
void render_result_single_layer_end(Render *re)
{
- ViewLayer *view_layer;
- RenderLayer *rlpush;
- RenderLayer *rl;
- int nr;
-
- if (re->result == NULL) {
+ if (re->result == nullptr) {
printf("pop render result error; no current result!\n");
return;
}
@@ -871,37 +850,36 @@ void render_result_single_layer_end(Render *re)
if (re->pushedresult->rectx == re->result->rectx &&
re->pushedresult->recty == re->result->recty) {
/* find which layer in re->pushedresult should be replaced */
- rl = re->result->layers.first;
+ RenderLayer *rl = static_cast<RenderLayer *>(re->result->layers.first);
/* render result should be empty after this */
BLI_remlink(&re->result->layers, rl);
/* reconstruct render result layers */
- for (nr = 0, view_layer = re->view_layers.first; view_layer;
- view_layer = view_layer->next, nr++) {
+ int nr = 0;
+ LISTBASE_FOREACH (ViewLayer *, view_layer, &re->view_layers) {
if (nr == re->active_view_layer) {
BLI_addtail(&re->result->layers, rl);
}
else {
- rlpush = RE_GetRenderLayer(re->pushedresult, view_layer->name);
+ RenderLayer *rlpush = RE_GetRenderLayer(re->pushedresult, view_layer->name);
if (rlpush) {
BLI_remlink(&re->pushedresult->layers, rlpush);
BLI_addtail(&re->result->layers, rlpush);
}
}
+ nr++;
}
}
RE_FreeRenderResult(re->pushedresult);
- re->pushedresult = NULL;
+ re->pushedresult = nullptr;
}
int render_result_exr_file_read_path(RenderResult *rr,
RenderLayer *rl_single,
const char *filepath)
{
- RenderLayer *rl;
- RenderPass *rpass;
void *exrhandle = IMB_exr_get_handle();
int rectx, recty;
@@ -911,37 +889,37 @@ int render_result_exr_file_read_path(RenderResult *rr,
return 0;
}
- if (rr == NULL || rectx != rr->rectx || recty != rr->recty) {
+ if (rr == nullptr || rectx != rr->rectx || recty != rr->recty) {
if (rr) {
printf("error in reading render result: dimensions don't match\n");
}
else {
- printf("error in reading render result: NULL result pointer\n");
+ printf("error in reading render result: nullptr result pointer\n");
}
IMB_exr_close(exrhandle);
return 0;
}
- for (rl = rr->layers.first; rl; rl = rl->next) {
+ LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
if (rl_single && rl_single != rl) {
continue;
}
/* passes are allocated in sync */
- for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
+ LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
const int xstride = rpass->channels;
int a;
char fullname[EXR_PASS_MAXNAME];
for (a = 0; a < xstride; a++) {
RE_render_result_full_channel_name(
- fullname, NULL, rpass->name, rpass->view, rpass->chan_id, a);
+ fullname, nullptr, rpass->name, rpass->view, rpass->chan_id, a);
IMB_exr_set_channel(
exrhandle, rl->name, fullname, xstride, xstride * rectx, rpass->rect + a);
}
RE_render_result_full_channel_name(
- rpass->fullname, NULL, rpass->name, rpass->view, rpass->chan_id, -1);
+ rpass->fullname, nullptr, rpass->name, rpass->view, rpass->chan_id, -1);
}
}
@@ -951,7 +929,11 @@ int render_result_exr_file_read_path(RenderResult *rr,
return 1;
}
-static void render_result_exr_file_cache_path(Scene *sce, const char *root, char *r_path)
+#define FILE_CACHE_MAX (FILE_MAXFILE + FILE_MAXFILE + MAX_ID_NAME + 100)
+
+static void render_result_exr_file_cache_path(Scene *sce,
+ const char *root,
+ char r_path[FILE_CACHE_MAX])
{
char filename_full[FILE_MAX + MAX_ID_NAME + 100], filename[FILE_MAXFILE], dirname[FILE_MAXDIR];
char path_digest[16] = {0};
@@ -970,7 +952,7 @@ static void render_result_exr_file_cache_path(Scene *sce, const char *root, char
}
BLI_hash_md5_to_hexdigest(path_digest, path_hexdigest);
- /* Default to *non-volatile* tmp dir. */
+ /* Default to *non-volatile* temp dir. */
if (*root == '\0') {
root = BKE_tempdir_base();
}
@@ -981,25 +963,31 @@ static void render_result_exr_file_cache_path(Scene *sce, const char *root, char
filename,
sce->id.name + 2,
path_hexdigest);
- BLI_make_file_string(dirname, r_path, root, filename_full);
+
+ BLI_join_dirfile(r_path, FILE_CACHE_MAX, root, filename_full);
+ if (BLI_path_is_rel(r_path)) {
+ BLI_path_abs(r_path, dirname);
+ }
}
void render_result_exr_file_cache_write(Render *re)
{
RenderResult *rr = re->result;
- char str[FILE_MAXFILE + FILE_MAXFILE + MAX_ID_NAME + 100];
+ char str[FILE_CACHE_MAX];
char *root = U.render_cachedir;
+ render_result_passes_allocated_ensure(rr);
+
render_result_exr_file_cache_path(re->scene, root, str);
printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str);
- BKE_image_render_write_exr(NULL, rr, str, NULL, true, NULL, -1);
+ BKE_image_render_write_exr(nullptr, rr, str, nullptr, true, nullptr, -1);
}
bool render_result_exr_file_cache_read(Render *re)
{
/* File path to cache. */
- char filepath[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = "";
+ char filepath[FILE_CACHE_MAX] = "";
char *root = U.render_cachedir;
render_result_exr_file_cache_path(re->scene, root, filepath);
@@ -1053,7 +1041,7 @@ ImBuf *RE_render_result_rect_to_ibuf(RenderResult *rr,
(R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_24 | R_IMF_CHAN_DEPTH_32)) {
if (imf->depth == R_IMF_CHAN_DEPTH_8) {
/* Higher depth bits are supported but not needed for current file output. */
- ibuf->rect_float = NULL;
+ ibuf->rect_float = nullptr;
}
else {
IMB_float_from_rect(ibuf);
@@ -1061,7 +1049,7 @@ ImBuf *RE_render_result_rect_to_ibuf(RenderResult *rr,
}
else {
/* ensure no float buffer remained from previous frame */
- ibuf->rect_float = NULL;
+ ibuf->rect_float = nullptr;
}
}
@@ -1085,7 +1073,7 @@ void RE_render_result_rect_from_ibuf(RenderResult *rr, const ImBuf *ibuf, const
rr->have_combined = true;
if (!rv->rectf) {
- rv->rectf = MEM_mallocN(sizeof(float[4]) * rr->rectx * rr->recty, "render_seq rectf");
+ rv->rectf = MEM_cnew_array<float>(4 * rr->rectx * rr->recty, "render_seq rectf");
}
memcpy(rv->rectf, ibuf->rect_float, sizeof(float[4]) * rr->rectx * rr->recty);
@@ -1098,10 +1086,10 @@ void RE_render_result_rect_from_ibuf(RenderResult *rr, const ImBuf *ibuf, const
rr->have_combined = true;
if (!rv->rect32) {
- rv->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect");
+ rv->rect32 = MEM_cnew_array<int>(rr->rectx * rr->recty, "render_seq rect");
}
- memcpy(rv->rect32, ibuf->rect, 4 * rr->rectx * rr->recty);
+ memcpy(rv->rect32, ibuf->rect, sizeof(int) * rr->rectx * rr->recty);
/* Same things as above, old rectf can hang around from previous render. */
MEM_SAFE_FREE(rv->rectf);
@@ -1119,7 +1107,7 @@ void render_result_rect_fill_zero(RenderResult *rr, const int view_id)
memset(rv->rect32, 0, 4 * rr->rectx * rr->recty);
}
else {
- rv->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect");
+ rv->rect32 = MEM_cnew_array<int>(rr->rectx * rr->recty, "render_seq rect");
}
}
@@ -1154,23 +1142,23 @@ void render_result_rect_get_pixels(RenderResult *rr,
/*************************** multiview functions *****************************/
-bool RE_HasCombinedLayer(const RenderResult *rr)
+bool RE_HasCombinedLayer(const RenderResult *result)
{
- if (rr == NULL) {
+ if (result == nullptr) {
return false;
}
- const RenderView *rv = rr->views.first;
- if (rv == NULL) {
+ const RenderView *rv = static_cast<RenderView *>(result->views.first);
+ if (rv == nullptr) {
return false;
}
return (rv->rect32 || rv->rectf);
}
-bool RE_HasFloatPixels(const RenderResult *rr)
+bool RE_HasFloatPixels(const RenderResult *result)
{
- for (const RenderView *rview = rr->views.first; rview; rview = rview->next) {
+ LISTBASE_FOREACH (const RenderView *, rview, &result->views) {
if (rview->rect32 && !rview->rectf) {
return false;
}
@@ -1179,13 +1167,13 @@ bool RE_HasFloatPixels(const RenderResult *rr)
return true;
}
-bool RE_RenderResult_is_stereo(const RenderResult *rr)
+bool RE_RenderResult_is_stereo(const RenderResult *result)
{
- if (!BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) {
+ if (!BLI_findstring(&result->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) {
return false;
}
- if (!BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) {
+ if (!BLI_findstring(&result->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) {
return false;
}
@@ -1194,37 +1182,36 @@ bool RE_RenderResult_is_stereo(const RenderResult *rr)
RenderView *RE_RenderViewGetById(RenderResult *rr, const int view_id)
{
- RenderView *rv = BLI_findlink(&rr->views, view_id);
+ RenderView *rv = static_cast<RenderView *>(BLI_findlink(&rr->views, view_id));
BLI_assert(rr->views.first);
- return rv ? rv : rr->views.first;
+ return rv ? rv : static_cast<RenderView *>(rr->views.first);
}
RenderView *RE_RenderViewGetByName(RenderResult *rr, const char *viewname)
{
- RenderView *rv = BLI_findstring(&rr->views, viewname, offsetof(RenderView, name));
+ RenderView *rv = static_cast<RenderView *>(
+ BLI_findstring(&rr->views, viewname, offsetof(RenderView, name)));
BLI_assert(rr->views.first);
- return rv ? rv : rr->views.first;
+ return rv ? rv : static_cast<RenderView *>(rr->views.first);
}
static RenderPass *duplicate_render_pass(RenderPass *rpass)
{
- RenderPass *new_rpass = MEM_mallocN(sizeof(RenderPass), "new render pass");
- *new_rpass = *rpass;
- new_rpass->next = new_rpass->prev = NULL;
- if (new_rpass->rect != NULL) {
- new_rpass->rect = MEM_dupallocN(new_rpass->rect);
+ RenderPass *new_rpass = MEM_cnew("new render pass", *rpass);
+ new_rpass->next = new_rpass->prev = nullptr;
+ if (new_rpass->rect != nullptr) {
+ new_rpass->rect = static_cast<float *>(MEM_dupallocN(new_rpass->rect));
}
return new_rpass;
}
static RenderLayer *duplicate_render_layer(RenderLayer *rl)
{
- RenderLayer *new_rl = MEM_mallocN(sizeof(RenderLayer), "new render layer");
- *new_rl = *rl;
- new_rl->next = new_rl->prev = NULL;
- new_rl->passes.first = new_rl->passes.last = NULL;
- new_rl->exrhandle = NULL;
- for (RenderPass *rpass = rl->passes.first; rpass != NULL; rpass = rpass->next) {
+ RenderLayer *new_rl = MEM_cnew("new render layer", *rl);
+ new_rl->next = new_rl->prev = nullptr;
+ new_rl->passes.first = new_rl->passes.last = nullptr;
+ new_rl->exrhandle = nullptr;
+ LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) {
RenderPass *new_rpass = duplicate_render_pass(rpass);
BLI_addtail(&new_rl->passes, new_rpass);
}
@@ -1233,43 +1220,41 @@ static RenderLayer *duplicate_render_layer(RenderLayer *rl)
static RenderView *duplicate_render_view(RenderView *rview)
{
- RenderView *new_rview = MEM_mallocN(sizeof(RenderView), "new render view");
- *new_rview = *rview;
- if (new_rview->rectf != NULL) {
- new_rview->rectf = MEM_dupallocN(new_rview->rectf);
+ RenderView *new_rview = MEM_cnew("new render view", *rview);
+ if (new_rview->rectf != nullptr) {
+ new_rview->rectf = static_cast<float *>(MEM_dupallocN(new_rview->rectf));
}
- if (new_rview->rectz != NULL) {
- new_rview->rectz = MEM_dupallocN(new_rview->rectz);
+ if (new_rview->rectz != nullptr) {
+ new_rview->rectz = static_cast<float *>(MEM_dupallocN(new_rview->rectz));
}
- if (new_rview->rect32 != NULL) {
- new_rview->rect32 = MEM_dupallocN(new_rview->rect32);
+ if (new_rview->rect32 != nullptr) {
+ new_rview->rect32 = static_cast<int *>(MEM_dupallocN(new_rview->rect32));
}
return new_rview;
}
RenderResult *RE_DuplicateRenderResult(RenderResult *rr)
{
- RenderResult *new_rr = MEM_mallocN(sizeof(RenderResult), "new duplicated render result");
- *new_rr = *rr;
- new_rr->next = new_rr->prev = NULL;
- new_rr->layers.first = new_rr->layers.last = NULL;
- new_rr->views.first = new_rr->views.last = NULL;
- for (RenderLayer *rl = rr->layers.first; rl != NULL; rl = rl->next) {
+ RenderResult *new_rr = MEM_cnew("new duplicated render result", *rr);
+ new_rr->next = new_rr->prev = nullptr;
+ new_rr->layers.first = new_rr->layers.last = nullptr;
+ new_rr->views.first = new_rr->views.last = nullptr;
+ LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) {
RenderLayer *new_rl = duplicate_render_layer(rl);
BLI_addtail(&new_rr->layers, new_rl);
}
- for (RenderView *rview = rr->views.first; rview != NULL; rview = rview->next) {
+ LISTBASE_FOREACH (RenderView *, rview, &rr->views) {
RenderView *new_rview = duplicate_render_view(rview);
BLI_addtail(&new_rr->views, new_rview);
}
- if (new_rr->rect32 != NULL) {
- new_rr->rect32 = MEM_dupallocN(new_rr->rect32);
+ if (new_rr->rectf != nullptr) {
+ new_rr->rectf = static_cast<float *>(MEM_dupallocN(new_rr->rectf));
}
- if (new_rr->rectf != NULL) {
- new_rr->rectf = MEM_dupallocN(new_rr->rectf);
+ if (new_rr->rectz != nullptr) {
+ new_rr->rectz = static_cast<float *>(MEM_dupallocN(new_rr->rectz));
}
- if (new_rr->rectz != NULL) {
- new_rr->rectz = MEM_dupallocN(new_rr->rectz);
+ if (new_rr->rect32 != nullptr) {
+ new_rr->rect32 = static_cast<int *>(MEM_dupallocN(new_rr->rect32));
}
new_rr->stamp_data = BKE_stamp_data_copy(new_rr->stamp_data);
return new_rr;
diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h
index c84e0a04018..2e76efba8a3 100644
--- a/source/blender/render/intern/render_result.h
+++ b/source/blender/render/intern/render_result.h
@@ -136,7 +136,8 @@ void render_result_views_shallowdelete(struct RenderResult *rr);
{ \
int nr_; \
ViewLayer *iter_; \
- for (nr_ = 0, iter_ = (re_)->view_layers.first; iter_ != NULL; iter_ = iter_->next, nr_++) { \
+ for (nr_ = 0, iter_ = static_cast<ViewLayer *>((re_)->view_layers.first); iter_ != NULL; \
+ iter_ = iter_->next, nr_++) { \
if (!G.background && (re_)->r.scemode & R_SINGLE_LAYER) { \
if (nr_ != re->active_view_layer) { \
continue; \
diff --git a/source/blender/render/intern/render_types.h b/source/blender/render/intern/render_types.h
index 58a15e3f013..29bac6e2766 100644
--- a/source/blender/render/intern/render_types.h
+++ b/source/blender/render/intern/render_types.h
@@ -11,16 +11,13 @@
/* exposed internal in render module only! */
/* ------------------------------------------------------------------------- */
-#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_threads.h"
-#include "BKE_main.h"
-
#include "RE_pipeline.h"
-struct GHash;
+struct Depsgraph;
struct GSet;
struct Main;
struct Object;
@@ -91,7 +88,7 @@ struct Render {
/* NOTE: This is a minimal dependency graph and evaluated scene which is enough to access view
* layer visibility and use for postprocessing (compositor and sequencer). */
- Depsgraph *pipeline_depsgraph;
+ struct Depsgraph *pipeline_depsgraph;
Scene *pipeline_scene_eval;
/* callbacks */
diff --git a/source/blender/render/intern/texture_common.h b/source/blender/render/intern/texture_common.h
index 0057779bda6..028b3d22f01 100644
--- a/source/blender/render/intern/texture_common.h
+++ b/source/blender/render/intern/texture_common.h
@@ -73,8 +73,8 @@ int imagewraposa(struct Tex *tex,
struct Image *ima,
struct ImBuf *ibuf,
const float texvec[3],
- const float dxt[2],
- const float dyt[2],
+ const float DXT[2],
+ const float DYT[2],
struct TexResult *texres,
struct ImagePool *pool,
bool skip_load_image);
diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c
index 7da9e7c3d58..38a569877c0 100644
--- a/source/blender/render/intern/texture_image.c
+++ b/source/blender/render/intern/texture_image.c
@@ -32,7 +32,6 @@
#include "RE_texture.h"
-#include "render_types.h"
#include "texture_common.h"
static void boxsample(ImBuf *ibuf,
@@ -1620,7 +1619,6 @@ int imagewraposa(Tex *tex,
/* Choice: */
if (tex->imaflag & TEX_MIPMAP) {
ImBuf *previbuf, *curibuf;
- float bumpscale;
dx = minx;
dy = miny;
@@ -1631,14 +1629,6 @@ int imagewraposa(Tex *tex,
pixsize = 1.0f / (float)MIN2(ibuf->x, ibuf->y);
- bumpscale = pixsize / maxd;
- if (bumpscale > 1.0f) {
- bumpscale = 1.0f;
- }
- else {
- bumpscale *= bumpscale;
- }
-
curmap = 0;
previbuf = curibuf = ibuf;
while (curmap < IMB_MIPMAP_LEVELS && ibuf->mipmap[curmap]) {
diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc
index 37ef9213615..66ab7ba6e2e 100644
--- a/source/blender/render/intern/texture_margin.cc
+++ b/source/blender/render/intern/texture_margin.cc
@@ -491,8 +491,8 @@ static void generate_margin(ImBuf *ibuf,
const float uv_offset[2])
{
- MPoly *mpoly;
- MLoop *mloop;
+ const MPoly *mpoly;
+ const MLoop *mloop;
const MLoopUV *mloopuv;
int totpoly, totloop, totedge;
@@ -505,8 +505,8 @@ static void generate_margin(ImBuf *ibuf,
totpoly = me->totpoly;
totloop = me->totloop;
totedge = me->totedge;
- mpoly = me->mpoly;
- mloop = me->mloop;
+ mpoly = me->polys().data();
+ mloop = me->loops().data();
if ((uv_layer == nullptr) || (uv_layer[0] == '\0')) {
mloopuv = static_cast<const MLoopUV *>(CustomData_get_layer(&me->ldata, CD_MLOOPUV));
@@ -520,7 +520,7 @@ static void generate_margin(ImBuf *ibuf,
tottri = poly_to_tri_count(me->totpoly, me->totloop);
looptri_mem = static_cast<MLoopTri *>(MEM_mallocN(sizeof(*looptri) * tottri, __func__));
BKE_mesh_recalc_looptri(
- me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri_mem);
+ mloop, mpoly, me->verts().data(), me->totloop, me->totpoly, looptri_mem);
looptri = looptri_mem;
}
else {
@@ -558,8 +558,8 @@ static void generate_margin(ImBuf *ibuf,
for (int a = 0; a < 3; a++) {
const float *uv = mloopuv[lt->tri[a]].uv;
- /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our
- * intersection tests where a pixel gets in between 2 faces or the middle of a quad,
+ /* NOTE(@campbellbarton): workaround for pixel aligned UVs which are common and can screw up
+ * our intersection tests where a pixel gets in between 2 faces or the middle of a quad,
* camera aligned quads also have this problem but they are less common.
* Add a small offset to the UVs, fixes bug T18685. */
vec[a][0] = (uv[0] - uv_offset[0]) * (float)ibuf->x - (0.5f + 0.001f);
diff --git a/source/blender/render/intern/texture_pointdensity.c b/source/blender/render/intern/texture_pointdensity.c
index 6e0bae700dc..2a2b62be1f0 100644
--- a/source/blender/render/intern/texture_pointdensity.c
+++ b/source/blender/render/intern/texture_pointdensity.c
@@ -24,6 +24,7 @@
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_particle_types.h"
+#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
#include "BKE_colorband.h"
@@ -39,7 +40,6 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
-#include "render_types.h"
#include "texture_common.h"
#include "RE_texture.h"
@@ -269,7 +269,7 @@ static void pointdensity_cache_vertex_color(PointDensity *pd,
Mesh *mesh,
float *data_color)
{
- const MLoop *mloop = mesh->mloop;
+ const MLoop *mloop = BKE_mesh_loops(mesh);
const int totloop = mesh->totloop;
char layername[MAX_CUSTOMDATA_LAYER_NAME];
int i;
@@ -364,7 +364,7 @@ static void pointdensity_cache_object(PointDensity *pd, Object *ob)
{
float *data_color;
int i;
- MVert *mvert = NULL, *mv;
+ const MVert *mvert = NULL, *mv;
Mesh *mesh = ob->data;
#if 0 /* UNUSED */
@@ -380,7 +380,7 @@ static void pointdensity_cache_object(PointDensity *pd, Object *ob)
}
#endif
- mvert = mesh->mvert; /* local object space */
+ mvert = BKE_mesh_verts(mesh); /* local object space */
pd->totpoints = mesh->totvert;
if (pd->totpoints == 0) {
return;
diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c
index 37605236738..13207543fd7 100644
--- a/source/blender/render/intern/texture_procedural.c
+++ b/source/blender/render/intern/texture_procedural.c
@@ -38,7 +38,6 @@
#include "MEM_guardedalloc.h"
-#include "render_types.h"
#include "texture_common.h"
#include "RE_texture.h"
diff --git a/source/blender/sequencer/SEQ_edit.h b/source/blender/sequencer/SEQ_edit.h
index ff9c387e527..afe35d20c2b 100644
--- a/source/blender/sequencer/SEQ_edit.h
+++ b/source/blender/sequencer/SEQ_edit.h
@@ -16,10 +16,10 @@ struct Main;
struct Scene;
struct Sequence;
-int SEQ_edit_sequence_swap(struct Scene *scene,
- struct Sequence *seq_a,
- struct Sequence *seq_b,
- const char **error_str);
+bool SEQ_edit_sequence_swap(struct Scene *scene,
+ struct Sequence *seq_a,
+ struct Sequence *seq_b,
+ const char **error_str);
/**
* Move sequence to seqbase.
*
diff --git a/source/blender/sequencer/SEQ_relations.h b/source/blender/sequencer/SEQ_relations.h
index 9678ac1cc1c..1b8d9db347d 100644
--- a/source/blender/sequencer/SEQ_relations.h
+++ b/source/blender/sequencer/SEQ_relations.h
@@ -31,7 +31,7 @@ bool SEQ_relations_check_scene_recursion(struct Scene *scene, struct ReportList
* Check if "seq_main" (indirectly) uses strip "seq".
*/
bool SEQ_relations_render_loop_check(struct Sequence *seq_main, struct Sequence *seq);
-void SEQ_relations_free_imbuf(struct Scene *scene, struct ListBase *seqbasep, bool for_render);
+void SEQ_relations_free_imbuf(struct Scene *scene, struct ListBase *seqbase, bool for_render);
void SEQ_relations_invalidate_cache_raw(struct Scene *scene, struct Sequence *seq);
void SEQ_relations_invalidate_cache_preprocessed(struct Scene *scene, struct Sequence *seq);
void SEQ_relations_invalidate_cache_composite(struct Scene *scene, struct Sequence *seq);
diff --git a/source/blender/sequencer/SEQ_select.h b/source/blender/sequencer/SEQ_select.h
index 92fb508372e..52d0bcc120e 100644
--- a/source/blender/sequencer/SEQ_select.h
+++ b/source/blender/sequencer/SEQ_select.h
@@ -15,9 +15,9 @@ struct Scene;
struct Sequence;
struct Sequence *SEQ_select_active_get(struct Scene *scene);
-int SEQ_select_active_get_pair(struct Scene *scene,
- struct Sequence **r_seq_act,
- struct Sequence **r_seq_other);
+bool SEQ_select_active_get_pair(struct Scene *scene,
+ struct Sequence **r_seq_act,
+ struct Sequence **r_seq_other);
void SEQ_select_active_set(struct Scene *scene, struct Sequence *seq);
#ifdef __cplusplus
diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h
index 8bc7733861c..c27a9dc4409 100644
--- a/source/blender/sequencer/SEQ_transform.h
+++ b/source/blender/sequencer/SEQ_transform.h
@@ -30,7 +30,7 @@ bool SEQ_transform_test_overlap(const struct Scene *scene,
bool SEQ_transform_test_overlap_seq_seq(const struct Scene *scene,
struct Sequence *seq1,
struct Sequence *seq2);
-void SEQ_transform_translate_sequence(struct Scene *scene, struct Sequence *seq, int delta);
+void SEQ_transform_translate_sequence(struct Scene *evil_scene, struct Sequence *seq, int delta);
/**
* \return 0 if there weren't enough space.
*/
diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c
index 2ab8b170ce9..25a6acb8975 100644
--- a/source/blender/sequencer/intern/effects.c
+++ b/source/blender/sequencer/intern/effects.c
@@ -2578,21 +2578,6 @@ static int early_out_speed(Sequence *UNUSED(seq), float UNUSED(fac))
return EARLY_DO_EFFECT;
}
-/**
- * Generator strips with zero inputs have their length set to 1 permanently. In some cases it is
- * useful to use speed effect on these strips because they can be animated. This can be done by
- * using their length as is on timeline as content length. See T82698.
- */
-static int seq_effect_speed_get_strip_content_length(Scene *scene, const Sequence *seq)
-{
- if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) == 0) {
- return SEQ_time_right_handle_frame_get(scene, seq) -
- SEQ_time_left_handle_frame_get(scene, seq);
- }
-
- return SEQ_time_strip_length_get(scene, seq);
-}
-
static FCurve *seq_effect_speed_speed_factor_curve_get(Scene *scene, Sequence *seq)
{
return id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, NULL);
@@ -2657,8 +2642,7 @@ float seq_speed_effect_target_frame_get(Scene *scene,
switch (s->speed_control_type) {
case SEQ_SPEED_STRETCH: {
/* Only right handle controls effect speed! */
- const float target_content_length = seq_effect_speed_get_strip_content_length(scene,
- source) -
+ const float target_content_length = SEQ_time_strip_length_get(scene, source) -
source->startofs;
const float speed_effetct_length = SEQ_time_right_handle_frame_get(scene, seq_speed) -
SEQ_time_left_handle_frame_get(scene, seq_speed);
@@ -2678,15 +2662,14 @@ float seq_speed_effect_target_frame_get(Scene *scene,
break;
}
case SEQ_SPEED_LENGTH:
- target_frame = seq_effect_speed_get_strip_content_length(scene, source) *
- (s->speed_fader_length / 100.0f);
+ target_frame = SEQ_time_strip_length_get(scene, source) * (s->speed_fader_length / 100.0f);
break;
case SEQ_SPEED_FRAME_NUMBER:
target_frame = s->speed_fader_frame_number;
break;
}
- CLAMP(target_frame, 0, seq_effect_speed_get_strip_content_length(scene, source));
+ CLAMP(target_frame, 0, SEQ_time_strip_length_get(scene, source));
target_frame += seq_speed->start;
/* No interpolation. */
diff --git a/source/blender/sequencer/intern/modifier.c b/source/blender/sequencer/intern/modifier.c
index b0f2f53396b..93bdcb59e88 100644
--- a/source/blender/sequencer/intern/modifier.c
+++ b/source/blender/sequencer/intern/modifier.c
@@ -598,7 +598,7 @@ static void modifier_color_balance_apply(
ColorBalanceInitData init_data;
if (!ibuf->rect_float && make_float) {
- imb_addrectfloatImBuf(ibuf);
+ imb_addrectfloatImBuf(ibuf, 4);
}
init_data.cb = cb;
@@ -1008,7 +1008,7 @@ static void brightcontrast_apply_threaded(int width,
/*
* The algorithm is by Werner D. Streidt
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
- * Extracted of OpenCV demhist.c
+ * Extracted of OpenCV `demhist.c`.
*/
if (contrast > 0) {
a = 1.0f - delta * 2.0f;
@@ -1177,7 +1177,7 @@ typedef struct AvgLogLum {
static void tonemapmodifier_init_data(SequenceModifierData *smd)
{
SequencerTonemapModifierData *tmmd = (SequencerTonemapModifierData *)smd;
- /* Same as tonemap compositor node. */
+ /* Same as tone-map compositor node. */
tmmd->type = SEQ_TONEMAP_RD_PHOTORECEPTOR;
tmmd->key = 0.18f;
tmmd->offset = 1.0f;
diff --git a/source/blender/sequencer/intern/proxy.c b/source/blender/sequencer/intern/proxy.c
index 522c64f7791..4220efab8bf 100644
--- a/source/blender/sequencer/intern/proxy.c
+++ b/source/blender/sequencer/intern/proxy.c
@@ -177,14 +177,12 @@ static bool seq_proxy_get_fname(Scene *scene,
BLI_snprintf(name,
PROXY_MAXFILE,
- "%s/images/%d/%s_proxy%s",
+ "%s/images/%d/%s_proxy%s.jpg",
dir,
proxy_size_number,
SEQ_render_give_stripelem(scene, seq, timeline_frame)->name,
suffix);
BLI_path_abs(name, BKE_main_blendfile_path_from_global());
- strcat(name, ".jpg");
-
return true;
}
@@ -511,15 +509,11 @@ void SEQ_proxy_rebuild(SeqIndexBuildContext *context,
}
/* fail safe code */
+ int width, height;
+ BKE_render_resolution(&scene->r, false, &width, &height);
- SEQ_render_new_render_data(bmain,
- context->depsgraph,
- context->scene,
- roundf((scene->r.size * (float)scene->r.xsch) / 100.0f),
- roundf((scene->r.size * (float)scene->r.ysch) / 100.0f),
- 100,
- false,
- &render_context);
+ SEQ_render_new_render_data(
+ bmain, context->depsgraph, context->scene, width, height, 100, false, &render_context);
render_context.skip_cache = true;
render_context.is_proxy_render = true;
diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c
index 15487a597d1..eff0f0b17e9 100644
--- a/source/blender/sequencer/intern/render.c
+++ b/source/blender/sequencer/intern/render.c
@@ -134,7 +134,7 @@ void seq_imbuf_to_sequencer_space(Scene *scene, ImBuf *ibuf, bool make_float)
/* We perform conversion to a float buffer so we don't worry about
* precision loss.
*/
- imb_addrectfloatImBuf(ibuf);
+ imb_addrectfloatImBuf(ibuf, 4);
IMB_colormanagement_transform_from_byte_threaded(ibuf->rect_float,
(unsigned char *)ibuf->rect,
ibuf->x,
@@ -1352,7 +1352,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
* find render).
* However, when called from within the UI (image preview in sequencer)
* we do want to use scene Render, that way the render result is defined
- * for display in render/imagewindow
+ * for display in render/image-window
*
* Hmm, don't see, why we can't do that all the time,
* and since G.is_rendering is uhm, gone... (Peter)
@@ -1447,8 +1447,8 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
if ((sequencer_view3d_fn && do_seq_gl && camera) && is_thread_main) {
char err_out[256] = "unknown";
- const int width = (scene->r.xsch * scene->r.size) / 100;
- const int height = (scene->r.ysch * scene->r.size) / 100;
+ int width, height;
+ BKE_render_resolution(&scene->r, false, &width, &height);
const char *viewname = BKE_scene_multiview_render_view_name_get(&scene->r, context->view_id);
unsigned int draw_flags = V3D_OFSDRAW_NONE;
@@ -1506,8 +1506,16 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context,
re = RE_NewSceneRender(scene);
}
- RE_RenderFrame(
- re, context->bmain, scene, have_comp ? NULL : view_layer, camera, frame, 0.0f, false);
+ const float subframe = frame - floorf(frame);
+
+ RE_RenderFrame(re,
+ context->bmain,
+ scene,
+ have_comp ? NULL : view_layer,
+ camera,
+ floorf(frame),
+ subframe,
+ false);
/* restore previous state after it was toggled on & off by RE_RenderFrame */
G.is_rendering = is_rendering;
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index a184f727b9e..4548975574d 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -426,22 +426,22 @@ MetaStack *SEQ_meta_stack_active_get(const Editing *ed)
return ed->metastack.last;
}
-void SEQ_meta_stack_set(const Scene *scene, Sequence *seqm)
+void SEQ_meta_stack_set(const Scene *scene, Sequence *dst_seq)
{
Editing *ed = SEQ_editing_get(scene);
/* Clear metastack */
BLI_freelistN(&ed->metastack);
- if (seqm != NULL) {
+ if (dst_seq != NULL) {
/* Allocate meta stack in a way, that represents meta hierarchy in timeline. */
- seq_meta_stack_alloc(scene, seqm);
- Sequence *meta_parent = seqm;
+ seq_meta_stack_alloc(scene, dst_seq);
+ Sequence *meta_parent = dst_seq;
while ((meta_parent = seq_sequence_lookup_meta_by_seq(scene, meta_parent))) {
seq_meta_stack_alloc(scene, meta_parent);
}
- SEQ_seqbase_active_set(ed, &seqm->seqbase);
- SEQ_channels_displayed_set(ed, &seqm->channels);
+ SEQ_seqbase_active_set(ed, &dst_seq->seqbase);
+ SEQ_channels_displayed_set(ed, &dst_seq->channels);
}
else {
/* Go to top level, exiting meta strip. */
@@ -836,7 +836,7 @@ static bool seq_read_lib_cb(Sequence *seq, void *user_data)
BlendLibReader *reader = data->reader;
Scene *sce = data->scene;
- IDP_BlendReadLib(reader, seq->prop);
+ IDP_BlendReadLib(reader, sce->id.lib, seq->prop);
if (seq->ipo) {
/* XXX: deprecated - old animation system. */
diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c
index c4992848cb5..69ccbdcb54e 100644
--- a/source/blender/sequencer/intern/sound.c
+++ b/source/blender/sequencer/intern/sound.c
@@ -26,7 +26,8 @@
#include "sequencer.h"
#include "strip_time.h"
-/* Unlike _update_sound_ funcs, these ones take info from audaspace to update sequence length! */
+/* Unlike _update_sound_ functions,
+ * these ones take info from audaspace to update sequence length! */
#ifdef WITH_AUDASPACE
static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, ListBase *seqbase)
{
diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c
index 15c472dd5a7..7358f5e74a0 100644
--- a/source/blender/sequencer/intern/strip_edit.c
+++ b/source/blender/sequencer/intern/strip_edit.c
@@ -37,32 +37,32 @@
#include "SEQ_transform.h"
#include "SEQ_utils.h"
-int SEQ_edit_sequence_swap(Scene *scene, Sequence *seq_a, Sequence *seq_b, const char **error_str)
+bool SEQ_edit_sequence_swap(Scene *scene, Sequence *seq_a, Sequence *seq_b, const char **error_str)
{
char name[sizeof(seq_a->name)];
if (SEQ_time_strip_length_get(scene, seq_a) != SEQ_time_strip_length_get(scene, seq_b)) {
*error_str = N_("Strips must be the same length");
- return 0;
+ return false;
}
/* type checking, could be more advanced but disallow sound vs non-sound copy */
if (seq_a->type != seq_b->type) {
if (seq_a->type == SEQ_TYPE_SOUND_RAM || seq_b->type == SEQ_TYPE_SOUND_RAM) {
*error_str = N_("Strips were not compatible");
- return 0;
+ return false;
}
/* disallow effects to swap with non-effects strips */
if ((seq_a->type & SEQ_TYPE_EFFECT) != (seq_b->type & SEQ_TYPE_EFFECT)) {
*error_str = N_("Strips were not compatible");
- return 0;
+ return false;
}
if ((seq_a->type & SEQ_TYPE_EFFECT) && (seq_b->type & SEQ_TYPE_EFFECT)) {
if (SEQ_effect_get_num_inputs(seq_a->type) != SEQ_effect_get_num_inputs(seq_b->type)) {
*error_str = N_("Strips must have the same number of inputs");
- return 0;
+ return false;
}
}
}
@@ -87,27 +87,26 @@ int SEQ_edit_sequence_swap(Scene *scene, Sequence *seq_a, Sequence *seq_b, const
seq_time_effect_range_set(scene, seq_a);
seq_time_effect_range_set(scene, seq_b);
- return 1;
+ return true;
}
static void seq_update_muting_recursive(ListBase *channels,
ListBase *seqbasep,
Sequence *metaseq,
- int mute)
+ const bool mute)
{
Sequence *seq;
- int seqmute;
/* For sound we go over full meta tree to update muted state,
* since sound is played outside of evaluating the imbufs. */
for (seq = seqbasep->first; seq; seq = seq->next) {
- seqmute = (mute || SEQ_render_is_muted(channels, seq));
+ bool seqmute = (mute || SEQ_render_is_muted(channels, seq));
if (seq->type == SEQ_TYPE_META) {
/* if this is the current meta sequence, unmute because
* all sequences above this were set to mute */
if (seq == metaseq) {
- seqmute = 0;
+ seqmute = false;
}
seq_update_muting_recursive(&seq->channels, &seq->seqbase, metaseq, seqmute);
@@ -127,10 +126,10 @@ void SEQ_edit_update_muting(Editing *ed)
MetaStack *ms = ed->metastack.last;
if (ms) {
- seq_update_muting_recursive(&ed->channels, &ed->seqbase, ms->parseq, 1);
+ seq_update_muting_recursive(&ed->channels, &ed->seqbase, ms->parseq, true);
}
else {
- seq_update_muting_recursive(&ed->channels, &ed->seqbase, NULL, 0);
+ seq_update_muting_recursive(&ed->channels, &ed->seqbase, NULL, false);
}
}
}
@@ -261,7 +260,7 @@ bool SEQ_edit_move_strip_to_meta(Scene *scene,
static void seq_split_set_left_hold_offset(Scene *scene, Sequence *seq, int timeline_frame)
{
- /* Adjust within range of extended stillframes before strip. */
+ /* Adjust within range of extended still-frames before strip. */
if (timeline_frame < seq->start) {
seq->start = timeline_frame - 1;
seq->anim_endofs += SEQ_time_strip_length_get(scene, seq) - 1;
@@ -275,7 +274,7 @@ static void seq_split_set_left_hold_offset(Scene *scene, Sequence *seq, int time
seq->endstill = 0;
seq->anim_endofs += (seq->start + SEQ_time_strip_length_get(scene, seq)) - timeline_frame;
}
- /* Adjust within range of extended stillframes after strip. */
+ /* Adjust within range of extended still-frames after strip. */
else if ((seq->start + SEQ_time_strip_length_get(scene, seq)) < timeline_frame) {
seq->endstill = timeline_frame - seq->start - SEQ_time_strip_length_get(scene, seq);
}
@@ -283,7 +282,7 @@ static void seq_split_set_left_hold_offset(Scene *scene, Sequence *seq, int time
static void seq_split_set_right_hold_offset(Scene *scene, Sequence *seq, int timeline_frame)
{
- /* Adjust within range of extended stillframes before strip. */
+ /* Adjust within range of extended still-frames before strip. */
if (timeline_frame < seq->start) {
seq->startstill = seq->start - timeline_frame;
}
@@ -295,7 +294,7 @@ static void seq_split_set_right_hold_offset(Scene *scene, Sequence *seq, int tim
seq->startstill = 0;
seq->startofs = 0;
}
- /* Adjust within range of extended stillframes after strip. */
+ /* Adjust within range of extended still-frames after strip. */
else if ((seq->start + SEQ_time_strip_length_get(scene, seq)) < timeline_frame) {
seq->start = timeline_frame;
seq->startofs = 0;
diff --git a/source/blender/sequencer/intern/strip_select.c b/source/blender/sequencer/intern/strip_select.c
index 69b4ce1f7c7..1a4ad4d997e 100644
--- a/source/blender/sequencer/intern/strip_select.c
+++ b/source/blender/sequencer/intern/strip_select.c
@@ -37,14 +37,14 @@ void SEQ_select_active_set(Scene *scene, Sequence *seq)
ed->act_seq = seq;
}
-int SEQ_select_active_get_pair(Scene *scene, Sequence **r_seq_act, Sequence **r_seq_other)
+bool SEQ_select_active_get_pair(Scene *scene, Sequence **r_seq_act, Sequence **r_seq_other)
{
Editing *ed = SEQ_editing_get(scene);
*r_seq_act = SEQ_select_active_get(scene);
if (*r_seq_act == NULL) {
- return 0;
+ return false;
}
Sequence *seq;
@@ -54,7 +54,7 @@ int SEQ_select_active_get_pair(Scene *scene, Sequence **r_seq_act, Sequence **r_
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && (seq != (*r_seq_act))) {
if (*r_seq_other) {
- return 0;
+ return false;
}
*r_seq_other = seq;
diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c
index aa51813c9b8..6725e0a8394 100644
--- a/source/blender/sequencer/intern/strip_time.c
+++ b/source/blender/sequencer/intern/strip_time.c
@@ -169,11 +169,16 @@ void SEQ_time_update_meta_strip_range(const Scene *scene, Sequence *seq_meta)
seq_meta->len -= seq_meta->anim_startofs;
seq_meta->len -= seq_meta->anim_endofs;
- seq_update_sound_bounds_recursive(scene, seq_meta);
+ /* Functions `SEQ_time_*_handle_frame_set()` can not be used here, because they are clamped, so
+ * change must be done at once. */
+ seq_meta->startofs = strip_start - seq_meta->start;
+ seq_meta->startdisp = strip_start; /* Only to make files usable in older versions. */
+ seq_meta->endofs = seq_meta->start + SEQ_time_strip_length_get(scene, seq_meta) - strip_end;
+ seq_meta->enddisp = strip_end; /* Only to make files usable in older versions. */
- /* Prevent meta-strip to move in timeline. */
- SEQ_time_left_handle_frame_set(scene, seq_meta, strip_start);
- SEQ_time_right_handle_frame_set(scene, seq_meta, strip_end);
+ seq_update_sound_bounds_recursive(scene, seq_meta);
+ SEQ_time_update_meta_strip_range(scene, seq_sequence_lookup_meta_by_seq(scene, seq_meta));
+ seq_time_update_effects_strip_range(scene, seq_sequence_lookup_effects_by_seq(scene, seq_meta));
}
void seq_time_effect_range_set(const Scene *scene, Sequence *seq)
@@ -203,6 +208,8 @@ void seq_time_effect_range_set(const Scene *scene, Sequence *seq)
/* Values unusable for effects, these should be always 0. */
seq->startofs = seq->endofs = seq->anim_startofs = seq->anim_endofs = 0;
+ seq->start = seq->startdisp;
+ seq->len = seq->enddisp - seq->startdisp;
}
/* Update strip startdisp and enddisp (n-input effects have no len to calculate these). */
@@ -544,3 +551,14 @@ void SEQ_time_right_handle_frame_set(const Scene *scene, Sequence *seq, int val)
SEQ_time_update_meta_strip_range(scene, seq_sequence_lookup_meta_by_seq(scene, seq));
seq_time_update_effects_strip_range(scene, seq_sequence_lookup_effects_by_seq(scene, seq));
}
+
+void seq_time_translate_handles(const Scene *scene, Sequence *seq, const int offset)
+{
+ seq->startofs += offset;
+ seq->endofs -= offset;
+ seq->startdisp += offset; /* Only to make files usable in older versions. */
+ seq->enddisp -= offset; /* Only to make files usable in older versions. */
+
+ SEQ_time_update_meta_strip_range(scene, seq_sequence_lookup_meta_by_seq(scene, seq));
+ seq_time_update_effects_strip_range(scene, seq_sequence_lookup_effects_by_seq(scene, seq));
+}
diff --git a/source/blender/sequencer/intern/strip_time.h b/source/blender/sequencer/intern/strip_time.h
index db581649f8a..19f549924df 100644
--- a/source/blender/sequencer/intern/strip_time.h
+++ b/source/blender/sequencer/intern/strip_time.h
@@ -40,6 +40,7 @@ void seq_time_gap_info_get(const struct Scene *scene,
struct GapInfo *r_gap_info);
void seq_time_effect_range_set(const struct Scene *scene, Sequence *seq);
void seq_time_update_effects_strip_range(const struct Scene *scene, struct SeqCollection *effects);
+void seq_time_translate_handles(const struct Scene *scene, struct Sequence *seq, const int offset);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index 68b30c9ce19..2c7bb69da66 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -84,7 +84,7 @@ bool SEQ_transform_seqbase_isolated_sel_check(ListBase *seqbase)
void SEQ_transform_fix_single_image_seq_offsets(const Scene *scene, Sequence *seq)
{
- int left, start, offset;
+ int left, start;
if (!SEQ_transform_single_image_check(seq)) {
return;
}
@@ -94,11 +94,8 @@ void SEQ_transform_fix_single_image_seq_offsets(const Scene *scene, Sequence *se
left = SEQ_time_left_handle_frame_get(scene, seq);
start = seq->start;
if (start != left) {
- offset = left - start;
- SEQ_time_left_handle_frame_set(
- scene, seq, SEQ_time_left_handle_frame_get(scene, seq) - offset);
- SEQ_time_right_handle_frame_set(
- scene, seq, SEQ_time_right_handle_frame_get(scene, seq) - offset);
+ const int offset = left - start;
+ seq_time_translate_handles(scene, seq, -offset);
seq->start += offset;
}
}
@@ -146,10 +143,7 @@ void SEQ_transform_translate_sequence(Scene *evil_scene, Sequence *seq, int delt
SEQ_transform_translate_sequence(evil_scene, seq_child, delta);
}
/* Move meta start/end points. */
- SEQ_time_left_handle_frame_set(
- evil_scene, seq, SEQ_time_left_handle_frame_get(evil_scene, seq) + delta);
- SEQ_time_right_handle_frame_set(
- evil_scene, seq, SEQ_time_right_handle_frame_get(evil_scene, seq) + delta);
+ seq_time_translate_handles(evil_scene, seq, delta);
}
else { /* All other strip types. */
seq->start += delta;
diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c
index 2026e322763..e6c641a5532 100644
--- a/source/blender/sequencer/intern/utils.c
+++ b/source/blender/sequencer/intern/utils.c
@@ -338,14 +338,15 @@ void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile)
const Sequence *SEQ_get_topmost_sequence(const Scene *scene, int frame)
{
Editing *ed = scene->ed;
- ListBase *channels = SEQ_channels_displayed_get(ed);
- const Sequence *seq, *best_seq = NULL;
- int best_machine = -1;
if (!ed) {
return NULL;
}
+ ListBase *channels = SEQ_channels_displayed_get(ed);
+ const Sequence *seq, *best_seq = NULL;
+ int best_machine = -1;
+
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (SEQ_render_is_muted(channels, seq) ||
!SEQ_time_strip_intersects_frame(scene, seq, frame)) {
diff --git a/source/blender/shader_fx/intern/FX_shader_blur.c b/source/blender/shader_fx/intern/FX_shader_blur.c
index 0d57b4c97e0..14e9a647105 100644
--- a/source/blender/shader_fx/intern/FX_shader_blur.c
+++ b/source/blender/shader_fx/intern/FX_shader_blur.c
@@ -64,7 +64,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Blur = {
- /* name */ "Blur",
+ /* name */ N_("Blur"),
/* structName */ "BlurShaderFxData",
/* structSize */ sizeof(BlurShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/shader_fx/intern/FX_shader_colorize.c b/source/blender/shader_fx/intern/FX_shader_colorize.c
index 97ced68b0cf..5551b16f445 100644
--- a/source/blender/shader_fx/intern/FX_shader_colorize.c
+++ b/source/blender/shader_fx/intern/FX_shader_colorize.c
@@ -71,7 +71,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Colorize = {
- /* name */ "Colorize",
+ /* name */ N_("Colorize"),
/* structName */ "ColorizeShaderFxData",
/* structSize */ sizeof(ColorizeShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/shader_fx/intern/FX_shader_flip.c b/source/blender/shader_fx/intern/FX_shader_flip.c
index 050550bb24d..5b5c12205ca 100644
--- a/source/blender/shader_fx/intern/FX_shader_flip.c
+++ b/source/blender/shader_fx/intern/FX_shader_flip.c
@@ -61,7 +61,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Flip = {
- /* name */ "Flip",
+ /* name */ N_("Flip"),
/* structName */ "FlipShaderFxData",
/* structSize */ sizeof(FlipShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/shader_fx/intern/FX_shader_glow.c b/source/blender/shader_fx/intern/FX_shader_glow.c
index 0aaacd1036e..b620754225a 100644
--- a/source/blender/shader_fx/intern/FX_shader_glow.c
+++ b/source/blender/shader_fx/intern/FX_shader_glow.c
@@ -15,6 +15,8 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_modifier.h"
#include "BKE_screen.h"
@@ -80,7 +82,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Glow = {
- /* name */ "Glow",
+ /* name */ N_("Glow"),
/* structName */ "GlowShaderFxData",
/* structSize */ sizeof(GlowShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/shader_fx/intern/FX_shader_pixel.c b/source/blender/shader_fx/intern/FX_shader_pixel.c
index 0eca0643c69..6f43bfb32b7 100644
--- a/source/blender/shader_fx/intern/FX_shader_pixel.c
+++ b/source/blender/shader_fx/intern/FX_shader_pixel.c
@@ -62,7 +62,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Pixel = {
- /* name */ "Pixelate",
+ /* name */ N_("Pixelate"),
/* structName */ "PixelShaderFxData",
/* structSize */ sizeof(PixelShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/shader_fx/intern/FX_shader_rim.c b/source/blender/shader_fx/intern/FX_shader_rim.c
index ec48e86706b..d314724eaca 100644
--- a/source/blender/shader_fx/intern/FX_shader_rim.c
+++ b/source/blender/shader_fx/intern/FX_shader_rim.c
@@ -89,7 +89,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Rim = {
- /* name */ "Rim",
+ /* name */ N_("Rim"),
/* structName */ "RimShaderFxData",
/* structSize */ sizeof(RimShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/shader_fx/intern/FX_shader_shadow.c b/source/blender/shader_fx/intern/FX_shader_shadow.c
index fdce7bf6e20..4b8b44cc5b0 100644
--- a/source/blender/shader_fx/intern/FX_shader_shadow.c
+++ b/source/blender/shader_fx/intern/FX_shader_shadow.c
@@ -159,7 +159,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Shadow = {
- /* name */ "Shadow",
+ /* name */ N_("Shadow"),
/* structName */ "ShadowShaderFxData",
/* structSize */ sizeof(ShadowShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/shader_fx/intern/FX_shader_swirl.c b/source/blender/shader_fx/intern/FX_shader_swirl.c
index 6d835382b29..059fe0ca8b6 100644
--- a/source/blender/shader_fx/intern/FX_shader_swirl.c
+++ b/source/blender/shader_fx/intern/FX_shader_swirl.c
@@ -15,6 +15,8 @@
#include "BLI_math_base.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "BKE_context.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
@@ -88,7 +90,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Swirl = {
- /* name */ "Swirl",
+ /* name */ N_("Swirl"),
/* structName */ "SwirlShaderFxData",
/* structSize */ sizeof(SwirlShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/shader_fx/intern/FX_shader_wave.c b/source/blender/shader_fx/intern/FX_shader_wave.c
index 28469ca42b9..947bf58ec7c 100644
--- a/source/blender/shader_fx/intern/FX_shader_wave.c
+++ b/source/blender/shader_fx/intern/FX_shader_wave.c
@@ -17,6 +17,8 @@
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+
#include "UI_interface.h"
#include "UI_resources.h"
@@ -61,7 +63,7 @@ static void panelRegister(ARegionType *region_type)
}
ShaderFxTypeInfo shaderfx_Type_Wave = {
- /* name */ "WaveDistortion",
+ /* name */ N_("WaveDistortion"),
/* structName */ "WaveShaderFxData",
/* structSize */ sizeof(WaveShaderFxData),
/* type */ eShaderFxType_GpencilType,
diff --git a/source/blender/simulation/intern/hair_volume.cpp b/source/blender/simulation/intern/hair_volume.cpp
index 36ac0c3906e..43f1ee36cd3 100644
--- a/source/blender/simulation/intern/hair_volume.cpp
+++ b/source/blender/simulation/intern/hair_volume.cpp
@@ -787,7 +787,7 @@ bool SIM_hair_volume_solve_divergence(HairGrid *grid,
vert->density, target_density, target_strength);
/* B vector contains the finite difference approximation of the velocity divergence.
- * NOTE: according to the discretized Navier-Stokes equation the rhs vector
+ * NOTE: according to the discretized Navier-Stokes equation the RHS vector
* and resulting pressure gradient should be multiplied by the (inverse) density;
* however, this is already included in the weighting of hair velocities on the grid!
*/
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 4c553d0edfe..9da150e0b7a 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -23,9 +23,9 @@ set(INC
../sequencer
../../../intern/clog
../../../intern/ghost
- ../../../intern/glew-mx
../../../intern/guardedalloc
../../../intern/memutil
+ ../bmesh
# for writefile.c: dna_type_offsets.h
${CMAKE_BINARY_DIR}/source/blender/makesdna/intern
@@ -36,7 +36,7 @@ set(INC
set(SRC
intern/wm.c
intern/wm_cursors.c
- intern/wm_dragdrop.c
+ intern/wm_dragdrop.cc
intern/wm_draw.c
intern/wm_event_query.c
intern/wm_event_system.cc
@@ -164,11 +164,11 @@ if(WIN32 OR APPLE)
endif()
endif()
-if(WITH_COMPOSITOR)
+if(WITH_COMPOSITOR_CPU)
list(APPEND LIB
bf_compositor
)
- add_definitions(-DWITH_COMPOSITOR)
+ add_definitions(-DWITH_COMPOSITOR_CPU)
endif()
if(WITH_XR_OPENXR)
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index d852b85a3d0..775b62e7d39 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -134,7 +134,24 @@ void WM_window_pixel_sample_read(const wmWindowManager *wm,
const int pos[2],
float r_col[3]);
+/**
+ * Read pixels from the front-buffer (fast).
+ *
+ * \note Internally this depends on the front-buffer state,
+ * for a slower but more reliable method of reading pixels, use #WM_window_pixels_read_offscreen.
+ * Fast pixel access may be preferred for file-save thumbnails.
+ *
+ * \warning Drawing (swap-buffers) immediately before calling this function causes
+ * the front-buffer state to be invalid under some EGL configurations.
+ */
uint *WM_window_pixels_read(struct wmWindowManager *wm, struct wmWindow *win, int r_size[2]);
+/**
+ * Draw the window & read pixels from an off-screen buffer (slower than #WM_window_pixels_read).
+ *
+ * \note This is needed because the state of the front-buffer may be damaged
+ * (see in-line code comments for details).
+ */
+uint *WM_window_pixels_read_offscreen(struct bContext *C, struct wmWindow *win, int r_size[2]);
/**
* Support for native pixel size
@@ -844,7 +861,7 @@ void WM_operator_properties_select_action(struct wmOperatorType *ot,
int default_action,
bool hide_gui);
/**
- * Only #SELECT / #DESELECT.
+ * Only for select/de-select.
*/
void WM_operator_properties_select_action_simple(struct wmOperatorType *ot,
int default_action,
@@ -1214,10 +1231,24 @@ int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent
/* Drag and drop. */
/**
- * Note that the pointer should be valid allocated and not on stack.
+ * Start dragging immediately with the given data.
+ * Note that \a poin should be valid allocated and not on stack.
*/
-struct wmDrag *WM_event_start_drag(
+void WM_event_start_drag(
struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags);
+/**
+ * Create and fill the dragging data, but don't start dragging just yet (unlike
+ * #WM_event_start_drag()). Must be followed up by #WM_event_start_prepared_drag(), otherwise the
+ * returned pointer will leak memory.
+ *
+ * Note that \a poin should be valid allocated and not on stack.
+ */
+wmDrag *WM_drag_data_create(
+ struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags);
+/**
+ * Invoke dragging using the given \a drag data.
+ */
+void WM_event_start_prepared_drag(struct bContext *C, wmDrag *drag);
void WM_event_drag_image(struct wmDrag *, struct ImBuf *, float scale);
void WM_drag_free(struct wmDrag *drag);
void WM_drag_data_free(int dragtype, void *poin);
@@ -1530,6 +1561,7 @@ bool WM_event_is_modal_drag_exit(const struct wmEvent *event,
bool WM_event_is_mouse_drag(const struct wmEvent *event);
bool WM_event_is_mouse_drag_or_press(const wmEvent *event);
int WM_event_drag_direction(const wmEvent *event);
+char WM_event_utf8_to_ascii(const struct wmEvent *event) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
/**
* Detect motion between selection (callers should only use this for selection picking),
diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h
index 96094e9e7ef..e9ad216073e 100644
--- a/source/blender/windowmanager/WM_toolsystem.h
+++ b/source/blender/windowmanager/WM_toolsystem.h
@@ -85,16 +85,19 @@ void WM_toolsystem_ref_sync_from_context(struct Main *bmain,
void WM_toolsystem_init(struct bContext *C);
-int WM_toolsystem_mode_from_spacetype(struct ViewLayer *view_layer,
+int WM_toolsystem_mode_from_spacetype(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct ScrArea *area,
int space_type);
-bool WM_toolsystem_key_from_context(struct ViewLayer *view_layer,
+bool WM_toolsystem_key_from_context(const struct Scene *scene,
+ struct ViewLayer *view_layer,
struct ScrArea *area,
bToolKey *tkey);
void WM_toolsystem_update_from_context_view3d(struct bContext *C);
void WM_toolsystem_update_from_context(struct bContext *C,
struct WorkSpace *workspace,
+ const struct Scene *scene,
struct ViewLayer *view_layer,
struct ScrArea *area);
@@ -145,6 +148,7 @@ void WM_toolsystem_ref_properties_init_for_keymap(struct bToolRef *tref,
void WM_toolsystem_refresh_active(struct bContext *C);
void WM_toolsystem_refresh_screen_area(struct WorkSpace *workspace,
+ const struct Scene *scene,
struct ViewLayer *view_layer,
struct ScrArea *area);
void WM_toolsystem_refresh_screen_window(struct wmWindow *win);
diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h
index 32bc2f96365..ba1d8d3ccb7 100644
--- a/source/blender/windowmanager/WM_types.h
+++ b/source/blender/windowmanager/WM_types.h
@@ -321,6 +321,7 @@ typedef struct wmNotifier {
/* category */
#define NOTE_CATEGORY 0xFF000000
+#define NOTE_CATEGORY_TAG_CLEARED NOTE_CATEGORY
#define NC_WM (1 << 24)
#define NC_WINDOW (2 << 24)
#define NC_WORKSPACE (3 << 24)
@@ -415,8 +416,8 @@ typedef struct wmNotifier {
#define ND_POINTCACHE (28 << 16)
#define ND_PARENT (29 << 16)
#define ND_LOD (30 << 16)
-#define ND_DRAW_RENDER_VIEWPORT \
- (31 << 16) /* for camera & sequencer viewport update, also /w NC_SCENE */
+/** For camera & sequencer viewport update, also with #NC_SCENE. */
+#define ND_DRAW_RENDER_VIEWPORT (31 << 16)
#define ND_SHADERFX (32 << 16)
/* For updating motion paths in 3dview. */
#define ND_DRAW_ANIMVIZ (33 << 16)
@@ -669,7 +670,6 @@ typedef struct wmTabletData {
*
* - Mouse-wheel events are excluded even though they generate #KM_PRESS
* as clicking and dragging don't make sense for mouse wheel events.
- *
*/
typedef struct wmEvent {
struct wmEvent *next, *prev;
@@ -683,13 +683,11 @@ typedef struct wmEvent {
/** Region relative mouse position (name convention before Blender 2.5). */
int mval[2];
/**
- * From, ghost if utf8 is enabled for the platform,
- * #BLI_str_utf8_size() must _always_ be valid, check
- * when assigning s we don't need to check on every access after.
+ * A single UTF8 encoded character.
+ * #BLI_str_utf8_size() must _always_ return a valid value,
+ * check when assigning so we don't need to check on every access after.
*/
char utf8_buf[6];
- /** From ghost, fallback if utf8 isn't set. */
- char ascii;
/** Modifier states: #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY. */
uint8_t modifier;
diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
index e30ea618fa4..cbdcb76d9aa 100644
--- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h
+++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h
@@ -180,7 +180,7 @@ typedef enum eWM_GizmoFlagMapTypeUpdateFlag {
/**
* \brief Gizmo tweak flag.
- * Bitflag passed to gizmo while tweaking.
+ * Bit-flag passed to gizmo while tweaking.
*
* \note Gizmos are responsible for handling this #wmGizmo.modal callback.
*/
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
index 4ecadbc5685..1c0f1e03a47 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c
@@ -632,7 +632,7 @@ wmKeyMap *wm_gizmogroup_tweak_modal_keymap(wmKeyConfig *keyconf)
STRNCPY(name, "Generic Gizmo Tweak Modal Map");
keymap = WM_modalkeymap_find(keyconf, name);
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return NULL;
}
@@ -1053,7 +1053,7 @@ void WM_gizmomaptype_group_unlink(bContext *C,
WM_gizmomaptype_group_free(gzgt_ref);
}
- /* TODO(campbell): Gizmos may share key-maps, for now don't
+ /* TODO(@campbellbarton): Gizmos may share key-maps, for now don't
* remove however we could flag them as temporary/owned by the gizmo. */
#if 0
/* NOTE: we may want to keep this key-map for editing. */
diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
index f5974a2176b..9903b0e50fd 100644
--- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
+++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
@@ -259,7 +259,7 @@ bool WM_gizmomap_minmax(const wmGizmoMap *gzmap,
* \param poll: Polling function for excluding gizmos.
* \param data: Custom data passed to \a poll
*
- * TODO(campbell): this uses unreliable order,
+ * TODO(@campbellbarton): this uses unreliable order,
* best we use an iterator function instead of a hash.
*/
static GHash *WM_gizmomap_gizmo_hash_new(const bContext *C,
@@ -430,9 +430,9 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas
return;
}
- /* TODO(campbell): This will need it own shader probably?
+ /* TODO(@campbellbarton): This will need it own shader probably?
* Don't think it can be handled from that point though. */
- /* const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; */
+ // const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0;
bool is_depth_prev = false;
@@ -501,7 +501,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C,
bool *r_use_select_bias)
{
- /* TODO(campbell): this depends on depth buffer being written to,
+ /* TODO(@campbellbarton): this depends on depth buffer being written to,
* currently broken for the 3D view. */
bool is_depth_prev = false;
bool is_depth_skip_prev = false;
@@ -674,7 +674,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C,
* This way we always use the first hit. */
if (has_3d) {
- /* The depth buffer is needed for for gizmos to obscure each other. */
+ /* The depth buffer is needed for gizmos to obscure each other. */
GPUViewport *viewport = WM_draw_region_get_viewport(CTX_wm_region(C));
/* When switching between modes and the mouse pointer is over a gizmo, the highlight test is
diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c
index 32cec8f779c..bd480526f9f 100644
--- a/source/blender/windowmanager/intern/wm.c
+++ b/source/blender/windowmanager/intern/wm.c
@@ -15,6 +15,7 @@
#include <stddef.h>
#include <string.h>
+#include "BLI_ghash.h"
#include "BLI_sys_types.h"
#include "DNA_windowmanager_types.h"
@@ -193,6 +194,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id)
BLI_listbase_clear(&wm->operators);
BLI_listbase_clear(&wm->paintcursors);
BLI_listbase_clear(&wm->notifier_queue);
+ wm->notifier_queue_set = NULL;
BKE_reports_init(&wm->reports, RPT_STORE);
BLI_listbase_clear(&wm->keyconfigs);
@@ -257,7 +259,7 @@ static void window_manager_blend_read_lib(BlendLibReader *reader, ID *id)
IDTypeInfo IDType_ID_WM = {
.id_code = ID_WM,
- .id_filter = 0,
+ .id_filter = FILTER_ID_WM,
.main_listbase_index = INDEX_ID_WM,
.struct_size = sizeof(wmWindowManager),
.name = "WindowManager",
@@ -273,7 +275,7 @@ IDTypeInfo IDType_ID_WM = {
.foreach_id = window_manager_foreach_id,
.foreach_cache = NULL,
.foreach_path = NULL,
- .owner_get = NULL,
+ .owner_pointer_get = NULL,
.blend_write = window_manager_blend_write,
.blend_read_data = window_manager_blend_read_data,
@@ -580,6 +582,10 @@ void wm_close_and_free(bContext *C, wmWindowManager *wm)
}
BLI_freelistN(&wm->notifier_queue);
+ if (wm->notifier_queue_set) {
+ BLI_gset_free(wm->notifier_queue_set, NULL);
+ wm->notifier_queue_set = NULL;
+ }
if (wm->message_bus != NULL) {
WM_msgbus_destroy(wm->message_bus);
@@ -610,7 +616,10 @@ void wm_close_and_free_all(bContext *C, ListBase *wmlist)
while ((wm = wmlist->first)) {
wm_close_and_free(C, wm);
BLI_remlink(wmlist, wm);
- BKE_libblock_free_data(&wm->id, true);
+ /* Don't handle user counts as this is only ever called once #G_MAIN has already been freed via
+ * #BKE_main_free so any ID's referenced by the window-manager (from ID properties) will crash.
+ * See: T100703. */
+ BKE_libblock_free_data(&wm->id, false);
BKE_libblock_free_data_py(&wm->id);
MEM_freeN(wm);
}
diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.cc
index 546ba795892..94bd33a9765 100644
--- a/source/blender/windowmanager/intern/wm_dragdrop.c
+++ b/source/blender/windowmanager/intern/wm_dragdrop.cc
@@ -7,7 +7,7 @@
* Our own drag-and-drop, drag state and drop boxes.
*/
-#include <string.h>
+#include <cstring>
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
@@ -57,7 +57,7 @@
/* ****************************************************** */
-static ListBase dropboxes = {NULL, NULL};
+static ListBase dropboxes = {nullptr, nullptr};
static void wm_drag_free_asset_data(wmDragAsset **asset_data);
@@ -65,14 +65,13 @@ static void wm_drag_free_asset_data(wmDragAsset **asset_data);
/* these are part of blender's UI/space specs, and not like keymaps */
/* when editors become configurable, they can add own dropbox definitions */
-typedef struct wmDropBoxMap {
+struct wmDropBoxMap {
struct wmDropBoxMap *next, *prev;
ListBase dropboxes;
short spaceid, regionid;
char idname[KMAP_MAX_NAME];
-
-} wmDropBoxMap;
+};
ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
{
@@ -84,7 +83,7 @@ ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid)
}
}
- wmDropBoxMap *dm = MEM_callocN(sizeof(struct wmDropBoxMap), "dropmap list");
+ wmDropBoxMap *dm = MEM_cnew<wmDropBoxMap>(__func__);
BLI_strncpy(dm->idname, idname, KMAP_MAX_NAME);
dm->spaceid = spaceid;
dm->regionid = regionid;
@@ -97,20 +96,20 @@ wmDropBox *WM_dropbox_add(ListBase *lb,
const char *idname,
bool (*poll)(bContext *, wmDrag *, const wmEvent *),
void (*copy)(bContext *, wmDrag *, wmDropBox *),
- void (*cancel)(struct Main *, wmDrag *, wmDropBox *),
+ void (*cancel)(Main *, wmDrag *, wmDropBox *),
WMDropboxTooltipFunc tooltip)
{
- wmDropBox *drop = MEM_callocN(sizeof(wmDropBox), "wmDropBox");
+ wmDropBox *drop = MEM_cnew<wmDropBox>(__func__);
drop->poll = poll;
drop->copy = copy;
drop->cancel = cancel;
drop->tooltip = tooltip;
- drop->ot = WM_operatortype_find(idname, 0);
+ drop->ot = WM_operatortype_find(idname, false);
- if (drop->ot == NULL) {
+ if (drop->ot == nullptr) {
MEM_freeN(drop);
printf("Error: dropbox with unknown operator: %s\n", idname);
- return NULL;
+ return nullptr;
}
WM_operator_properties_alloc(&(drop->ptr), &(drop->properties), idname);
@@ -170,27 +169,25 @@ static void wm_dropbox_invoke(bContext *C, wmDrag *drag)
if (drop->on_drag_start) {
drop->on_drag_start(C, drag);
}
- CTX_store_set(C, NULL);
+ CTX_store_set(C, nullptr);
}
}
}
-wmDrag *WM_event_start_drag(
- struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags)
+wmDrag *WM_drag_data_create(
+ bContext *C, int icon, int type, void *poin, double value, unsigned int flags)
{
- wmWindowManager *wm = CTX_wm_manager(C);
- wmDrag *drag = MEM_callocN(sizeof(struct wmDrag), "new drag");
+ wmDrag *drag = MEM_cnew<wmDrag>(__func__);
/* Keep track of future multi-touch drag too, add a mouse-pointer id or so. */
/* if multiple drags are added, they're drawn as list */
- BLI_addtail(&wm->drags, drag);
- drag->flags = flags;
+ drag->flags = static_cast<eWM_DragFlags>(flags);
drag->icon = icon;
drag->type = type;
switch (type) {
case WM_DRAG_PATH:
- BLI_strncpy(drag->path, poin, FILE_MAX);
+ BLI_strncpy(drag->path, static_cast<const char *>(poin), FILE_MAX);
/* As the path is being copied, free it immediately as `drag` won't "own" the data. */
if (flags & WM_DRAG_FREE_DATA) {
MEM_freeN(poin);
@@ -198,7 +195,7 @@ wmDrag *WM_event_start_drag(
break;
case WM_DRAG_ID:
if (poin) {
- WM_drag_add_local_ID(drag, poin, NULL);
+ WM_drag_add_local_ID(drag, static_cast<ID *>(poin), nullptr);
}
break;
case WM_DRAG_ASSET:
@@ -213,7 +210,7 @@ wmDrag *WM_event_start_drag(
const AssetLibraryReference *asset_library = CTX_wm_asset_library_ref(C);
ListBase asset_file_links = CTX_data_collection_get(C, "selected_asset_files");
LISTBASE_FOREACH (const CollectionPointerLink *, link, &asset_file_links) {
- const FileDirEntry *asset_file = link->ptr.data;
+ const FileDirEntry *asset_file = static_cast<const FileDirEntry *>(link->ptr.data);
const AssetHandle asset_handle = {asset_file};
WM_drag_add_asset_list_item(drag, C, asset_library, &asset_handle);
}
@@ -226,9 +223,22 @@ wmDrag *WM_event_start_drag(
}
drag->value = value;
+ return drag;
+}
+
+void WM_event_start_prepared_drag(bContext *C, wmDrag *drag)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+
+ BLI_addtail(&wm->drags, drag);
wm_dropbox_invoke(C, drag);
+}
- return drag;
+void WM_event_start_drag(
+ bContext *C, int icon, int type, void *poin, double value, unsigned int flags)
+{
+ wmDrag *drag = WM_drag_data_create(C, icon, type, poin, value, flags);
+ WM_event_start_prepared_drag(C, drag);
}
void wm_drags_exit(wmWindowManager *wm, wmWindow *win)
@@ -255,12 +265,12 @@ static bContextStore *wm_drop_ui_context_create(const bContext *C)
{
uiBut *active_but = UI_region_active_but_get(CTX_wm_region(C));
if (!active_but) {
- return NULL;
+ return nullptr;
}
bContextStore *but_context = UI_but_context_get(active_but);
if (!but_context) {
- return NULL;
+ return nullptr;
}
return CTX_store_copy(but_context);
@@ -272,7 +282,7 @@ static void wm_drop_ui_context_free(bContextStore **context_store)
return;
}
CTX_store_free(*context_store);
- *context_store = NULL;
+ *context_store = nullptr;
}
void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale)
@@ -283,14 +293,14 @@ void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale)
void WM_drag_data_free(int dragtype, void *poin)
{
- /* Don't require all the callers to have a NULL-check, just allow passing NULL. */
+ /* Don't require all the callers to have a nullptr-check, just allow passing nullptr. */
if (!poin) {
return;
}
/* Not too nice, could become a callback. */
if (dragtype == WM_DRAG_ASSET) {
- wmDragAsset *asset_data = poin;
+ wmDragAsset *asset_data = static_cast<wmDragAsset *>(poin);
wm_drag_free_asset_data(&asset_data);
}
else {
@@ -320,17 +330,17 @@ void WM_drag_free(wmDrag *drag)
MEM_freeN(drag);
}
-void WM_drag_free_list(struct ListBase *lb)
+void WM_drag_free_list(ListBase *lb)
{
wmDrag *drag;
- while ((drag = BLI_pophead(lb))) {
+ while ((drag = static_cast<wmDrag *>(BLI_pophead(lb)))) {
WM_drag_free(drag);
}
}
static char *dropbox_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox *drop)
{
- char *tooltip = NULL;
+ char *tooltip = nullptr;
if (drop->tooltip) {
tooltip = drop->tooltip(C, drag, xy, drop);
}
@@ -350,7 +360,7 @@ static wmDropBox *dropbox_active(bContext *C,
if (drag->drop_state.free_disabled_info) {
MEM_SAFE_FREE(drag->drop_state.disabled_info);
}
- drag->drop_state.disabled_info = NULL;
+ drag->drop_state.disabled_info = nullptr;
LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) {
if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) {
@@ -370,7 +380,7 @@ static wmDropBox *dropbox_active(bContext *C,
const wmOperatorCallContext opcontext = wm_drop_operator_context_get(drop);
if (WM_operator_poll_context(C, drop->ot, opcontext)) {
- CTX_store_set(C, NULL);
+ CTX_store_set(C, nullptr);
return drop;
}
@@ -386,8 +396,8 @@ static wmDropBox *dropbox_active(bContext *C,
}
}
}
- CTX_store_set(C, NULL);
- return NULL;
+ CTX_store_set(C, nullptr);
+ return nullptr;
}
/* return active operator tooltip/name when mouse is in box */
@@ -430,14 +440,14 @@ static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *even
if (drop != drop_prev) {
if (drop_prev && drop_prev->draw_deactivate) {
drop_prev->draw_deactivate(drop_prev, drag);
- BLI_assert(drop_prev->draw_data == NULL);
+ BLI_assert(drop_prev->draw_data == nullptr);
}
if (drop && drop->draw_activate) {
drop->draw_activate(drop, drag);
}
drag->drop_state.active_dropbox = drop;
- drag->drop_state.area_from = drop ? CTX_wm_area(C) : NULL;
- drag->drop_state.region_from = drop ? CTX_wm_region(C) : NULL;
+ drag->drop_state.area_from = drop ? CTX_wm_area(C) : nullptr;
+ drag->drop_state.region_from = drop ? CTX_wm_region(C) : nullptr;
}
if (!drag->drop_state.active_dropbox) {
@@ -465,7 +475,7 @@ void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop)
void wm_drop_end(bContext *C, wmDrag *UNUSED(drag), wmDropBox *UNUSED(drop))
{
- CTX_store_set(C, NULL);
+ CTX_store_set(C, nullptr);
}
void wm_drags_check_ops(bContext *C, const wmEvent *event)
@@ -500,7 +510,7 @@ void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent)
/* Don't drag the same ID twice. */
LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) {
if (drag_id->id == id) {
- if (drag_id->from_parent == NULL) {
+ if (drag_id->from_parent == nullptr) {
drag_id->from_parent = from_parent;
}
return;
@@ -512,7 +522,7 @@ void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent)
}
/* Add to list. */
- wmDragID *drag_id = MEM_callocN(sizeof(wmDragID), __func__);
+ wmDragID *drag_id = MEM_cnew<wmDragID>(__func__);
drag_id->id = id;
drag_id->from_parent = from_parent;
BLI_addtail(&drag->ids, drag_id);
@@ -521,26 +531,26 @@ void WM_drag_add_local_ID(wmDrag *drag, ID *id, ID *from_parent)
ID *WM_drag_get_local_ID(const wmDrag *drag, short idcode)
{
if (drag->type != WM_DRAG_ID) {
- return NULL;
+ return nullptr;
}
- wmDragID *drag_id = drag->ids.first;
+ wmDragID *drag_id = static_cast<wmDragID *>(drag->ids.first);
if (!drag_id) {
- return NULL;
+ return nullptr;
}
ID *id = drag_id->id;
- return (idcode == 0 || GS(id->name) == idcode) ? id : NULL;
+ return (idcode == 0 || GS(id->name) == idcode) ? id : nullptr;
}
ID *WM_drag_get_local_ID_from_event(const wmEvent *event, short idcode)
{
if (event->custom != EVT_DATA_DRAGDROP) {
- return NULL;
+ return nullptr;
}
- ListBase *lb = event->customdata;
- return WM_drag_get_local_ID(lb->first, idcode);
+ ListBase *lb = static_cast<ListBase *>(event->customdata);
+ return WM_drag_get_local_ID(static_cast<const wmDrag *>(lb->first), idcode);
}
bool WM_drag_is_ID_type(const wmDrag *drag, int idcode)
@@ -553,7 +563,7 @@ wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset,
const char *path,
int import_type)
{
- wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset");
+ wmDragAsset *asset_drag = MEM_new<wmDragAsset>(__func__);
BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name));
asset_drag->metadata = metadata;
@@ -573,14 +583,14 @@ static void wm_drag_free_asset_data(wmDragAsset **asset_data)
wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode)
{
if (drag->type != WM_DRAG_ASSET) {
- return NULL;
+ return nullptr;
}
- wmDragAsset *asset_drag = drag->poin;
- return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL;
+ wmDragAsset *asset_drag = static_cast<wmDragAsset *>(drag->poin);
+ return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : nullptr;
}
-struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode)
+AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode)
{
wmDragAsset *drag_asset = WM_drag_get_asset_data(drag, idcode);
if (drag_asset) {
@@ -592,17 +602,18 @@ struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode
return local_id->asset_data;
}
- return NULL;
+ return nullptr;
}
ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra)
{
/* Only support passing in limited flags. */
BLI_assert(flag_extra == (flag_extra & FILE_AUTOSELECT));
- eFileSel_Params_Flag flag = flag_extra | FILE_ACTIVE_COLLECTION;
+ eFileSel_Params_Flag flag = static_cast<eFileSel_Params_Flag>(flag_extra) |
+ FILE_ACTIVE_COLLECTION;
const char *name = asset_drag->name;
- ID_Type idtype = asset_drag->id_type;
+ ID_Type idtype = static_cast<ID_Type>(asset_drag->id_type);
/* FIXME: Link/Append should happens in the operator called at the end of drop process, not from
* here. */
@@ -640,7 +651,7 @@ ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra)
}
BLI_assert_unreachable();
- return NULL;
+ return nullptr;
}
bool WM_drag_asset_will_import_linked(const wmDrag *drag)
@@ -656,7 +667,7 @@ bool WM_drag_asset_will_import_linked(const wmDrag *drag)
ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode)
{
if (!ELEM(drag->type, WM_DRAG_ASSET, WM_DRAG_ID)) {
- return NULL;
+ return nullptr;
}
if (drag->type == WM_DRAG_ID) {
@@ -665,14 +676,14 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode)
wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, idcode);
if (!asset_drag) {
- return NULL;
+ return nullptr;
}
/* Link/append the asset. */
return WM_drag_asset_id_import(asset_drag, 0);
}
-void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox *drop)
+void WM_drag_free_imported_drag_ID(Main *bmain, wmDrag *drag, wmDropBox *drop)
{
if (drag->type != WM_DRAG_ASSET) {
return;
@@ -686,8 +697,8 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox *
/* Try to find the imported ID. For this to work either a "session_uuid" or "name" property must
* have been defined (see #WM_operator_properties_id_lookup()). */
ID *id = WM_operator_properties_id_lookup_from_name_or_session_uuid(
- bmain, drop->ptr, asset_drag->id_type);
- if (id != NULL) {
+ bmain, drop->ptr, static_cast<ID_Type>(asset_drag->id_type));
+ if (id != nullptr) {
/* Do not delete the dragged ID if it has any user, otherwise if it is a 're-used' ID it will
* cause T95636. Note that we need first to add the user that we want to remove in
* #BKE_id_free_us. */
@@ -699,10 +710,10 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox *
wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const wmDrag *drag)
{
if (drag->type != WM_DRAG_ASSET_CATALOG) {
- return NULL;
+ return nullptr;
}
- return drag->poin;
+ return static_cast<wmDragAssetCatalog *>(drag->poin);
}
void WM_drag_add_asset_list_item(
@@ -717,7 +728,7 @@ void WM_drag_add_asset_list_item(
/* No guarantee that the same asset isn't added twice. */
/* Add to list. */
- wmDragAssetListItem *drag_asset = MEM_callocN(sizeof(*drag_asset), __func__);
+ wmDragAssetListItem *drag_asset = MEM_cnew<wmDragAssetListItem>(__func__);
ID *local_id = ED_asset_handle_get_local_id(asset);
if (local_id) {
drag_asset->is_external = false;
@@ -737,7 +748,7 @@ void WM_drag_add_asset_list_item(
const ListBase *WM_drag_asset_list_get(const wmDrag *drag)
{
if (drag->type != WM_DRAG_ASSET_LIST) {
- return NULL;
+ return nullptr;
}
return &drag->asset_items;
@@ -826,7 +837,7 @@ static void wm_drag_draw_icon(bContext *UNUSED(C),
y = xy[1] - (wm_drag_imbuf_icon_height_get(drag) / 2);
const float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */
- IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
+ IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_3D_IMAGE_COLOR);
immDrawPixelsTexTiled_scaling(&state,
x,
y,
@@ -878,7 +889,7 @@ static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const
int iconsize = UI_DPI_ICON_SIZE;
int padding = 4 * UI_DPI_FAC;
- char *tooltip = NULL;
+ char *tooltip = nullptr;
if (drag->drop_state.active_dropbox) {
tooltip = dropbox_tooltip(C, drag, xy, drag->drop_state.active_dropbox);
}
@@ -1002,7 +1013,7 @@ void wm_drags_draw(bContext *C, wmWindow *win)
wm_drag_draw_default(C, win, drag, xy);
}
GPU_blend(GPU_BLEND_NONE);
- CTX_wm_area_set(C, NULL);
- CTX_wm_region_set(C, NULL);
- CTX_store_set(C, NULL);
+ CTX_wm_area_set(C, nullptr);
+ CTX_wm_region_set(C, nullptr);
+ CTX_store_set(C, nullptr);
}
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 66ed36b373d..663a41212ba 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -225,10 +225,9 @@ static void wm_software_cursor_draw_bitmap(const int event_xy[2],
imm_format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
/* Use 3D image for correct display of planar tracked images. */
- immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_MODULATE_ALPHA);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE);
immBindTexture("image", texture);
- immUniform1f("alpha", 1.0f);
immBegin(GPU_PRIM_TRI_FAN, 4);
@@ -263,7 +262,7 @@ static void wm_software_cursor_draw_crosshair(const int event_xy[2])
const float unit = max_ff(U.dpi_fac, 1.0f);
uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(1, 1, 1, 1);
{
@@ -895,7 +894,8 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
if (area->flag & AREA_FLAG_ACTIVE_TOOL_UPDATE) {
if ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) {
- WM_toolsystem_update_from_context(C, CTX_wm_workspace(C), CTX_data_view_layer(C), area);
+ WM_toolsystem_update_from_context(
+ C, CTX_wm_workspace(C), CTX_data_scene(C), CTX_data_view_layer(C), area);
}
area->flag &= ~AREA_FLAG_ACTIVE_TOOL_UPDATE;
}
@@ -957,7 +957,7 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo)
GPU_debug_group_end();
}
- /* Draw menus into their own framebuffer. */
+ /* Draw menus into their own frame-buffer. */
LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) {
if (!region->visible) {
continue;
@@ -994,7 +994,7 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
GPU_debug_group_begin("Window Redraw");
- /* Draw into the window framebuffer, in full window coordinates. */
+ /* Draw into the window frame-buffer, in full window coordinates. */
wmWindowViewport(win);
/* We draw on all pixels of the windows so we don't need to clear them before.
@@ -1106,17 +1106,17 @@ static void wm_draw_window(bContext *C, wmWindow *win)
/* Avoid any BGL call issued before this to alter the window drawin. */
GPU_bgl_end();
- /* Draw area regions into their own framebuffer. This way we can redraw
- * the areas that need it, and blit the rest from existing framebuffers. */
+ /* Draw area regions into their own frame-buffer. This way we can redraw
+ * the areas that need it, and blit the rest from existing frame-buffers. */
wm_draw_window_offscreen(C, win, stereo);
- /* Now we draw into the window framebuffer, in full window coordinates. */
+ /* Now we draw into the window frame-buffer, in full window coordinates. */
if (!stereo) {
/* Regular mono drawing. */
wm_draw_window_onscreen(C, win, -1);
}
else if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
- /* For pageflip we simply draw to both back buffers. */
+ /* For page-flip we simply draw to both back buffers. */
GPU_backbuffer_bind(GPU_BACKBUFFER_RIGHT);
wm_draw_window_onscreen(C, win, 1);
@@ -1125,12 +1125,12 @@ static void wm_draw_window(bContext *C, wmWindow *win)
}
else if (ELEM(win->stereo3d_format->display_mode, S3D_DISPLAY_ANAGLYPH, S3D_DISPLAY_INTERLACE)) {
/* For anaglyph and interlace, we draw individual regions with
- * stereo framebuffers using different shaders. */
+ * stereo frame-buffers using different shaders. */
wm_draw_window_onscreen(C, win, -1);
}
else {
/* For side-by-side and top-bottom, we need to render each view to an
- * an offscreen texture and then draw it. This used to happen for all
+ * an off-screen texture and then draw it. This used to happen for all
* stereo methods, but it's less efficient than drawing directly. */
const int width = WM_window_pixels_x(win);
const int height = WM_window_pixels_y(win);
@@ -1191,6 +1191,39 @@ static void wm_draw_surface(bContext *C, wmSurface *surface)
wm_surface_clear_drawable();
}
+uint *WM_window_pixels_read_offscreen(bContext *C, wmWindow *win, int r_size[2])
+{
+ /* NOTE(@campbellbarton): There is a problem reading the windows front-buffer after redrawing
+ * the window in some cases (typically to clear UI elements such as menus or search popup).
+ * With EGL `eglSurfaceAttrib(..)` may support setting the `EGL_SWAP_BEHAVIOR` attribute to
+ * `EGL_BUFFER_PRESERVED` however not all implementations support this.
+ * Requesting the ability with `EGL_SWAP_BEHAVIOR_PRESERVED_BIT` can even cause the EGL context
+ * not to initialize at all.
+ * Confusingly there are some cases where this *does* work, depending on the state of the window
+ * and prior calls to swap-buffers, however ensuring the state exactly as needed to satisfy a
+ * particular GPU back-end is fragile, see T98462.
+ *
+ * So provide an alternative to #WM_window_pixels_read that avoids using the front-buffer. */
+
+ /* Draw into an off-screen buffer and read it's contents. */
+ r_size[0] = WM_window_pixels_x(win);
+ r_size[1] = WM_window_pixels_y(win);
+
+ GPUOffScreen *offscreen = GPU_offscreen_create(r_size[0], r_size[1], false, GPU_RGBA8, NULL);
+ if (UNLIKELY(!offscreen)) {
+ return NULL;
+ }
+
+ const uint rect_len = r_size[0] * r_size[1];
+ uint *rect = MEM_mallocN(sizeof(*rect) * rect_len, __func__);
+ GPU_offscreen_bind(offscreen, false);
+ wm_draw_window_onscreen(C, win, -1);
+ GPU_offscreen_unbind(offscreen, false);
+ GPU_offscreen_read_pixels(offscreen, GPU_DATA_UBYTE, rect);
+ GPU_offscreen_free(offscreen);
+ return rect;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c
index 221073d288a..a1d94c33f27 100644
--- a/source/blender/windowmanager/intern/wm_event_query.c
+++ b/source/blender/windowmanager/intern/wm_event_query.c
@@ -112,7 +112,7 @@ void WM_event_print(const wmEvent *event)
"wmEvent type:%d/%s, val:%d/%s, "
"prev_type:%d/%s, prev_val:%d/%s, "
"modifier=%s, keymodifier:%d, flag:%s, "
- "mouse:(%d,%d), ascii:'%c', utf8:'%.*s', pointer:%p",
+ "mouse:(%d,%d), utf8:'%.*s', pointer:%p",
event->type,
type_id,
event->val,
@@ -126,7 +126,6 @@ void WM_event_print(const wmEvent *event)
flag_id,
event->xy[0],
event->xy[1],
- event->ascii,
BLI_str_utf8_size(event->utf8_buf),
event->utf8_buf,
(const void *)event);
@@ -347,8 +346,8 @@ bool WM_cursor_test_motion_and_update(const int mval[2])
int WM_event_drag_threshold(const struct wmEvent *event)
{
int drag_threshold;
- if (ISMOUSE(event->prev_press_type)) {
- BLI_assert(event->prev_press_type != MOUSEMOVE);
+ BLI_assert(event->prev_press_type != MOUSEMOVE);
+ if (ISMOUSE_BUTTON(event->prev_press_type)) {
/* 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. */
@@ -401,6 +400,20 @@ void WM_event_drag_start_xy(const wmEvent *event, int r_xy[2])
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Event Text Queries
+ * \{ */
+
+char WM_event_utf8_to_ascii(const struct wmEvent *event)
+{
+ if (BLI_str_utf8_size(event->utf8_buf) == 1) {
+ return event->utf8_buf[0];
+ }
+ return '\0';
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Event Preference Mapping
* \{ */
diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc
index 0395e8bda7a..c26696704dd 100644
--- a/source/blender/windowmanager/intern/wm_event_system.cc
+++ b/source/blender/windowmanager/intern/wm_event_system.cc
@@ -27,6 +27,7 @@
#include "BLI_blenlib.h"
#include "BLI_dynstr.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_timer.h"
#include "BLI_utildefines.h"
@@ -90,6 +91,7 @@
#define USE_GIZMO_MOUSE_PRIORITY_HACK
static void wm_notifier_clear(wmNotifier *note);
+static bool wm_notifier_is_clear(const wmNotifier *note);
static int wm_operator_call_internal(bContext *C,
wmOperatorType *ot,
@@ -190,7 +192,7 @@ void wm_event_free(wmEvent *event)
printf("%s: 'is_repeat=true' for non-keyboard event, this should not happen.\n", __func__);
WM_event_print(event);
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) && (event->val != KM_NOTHING)) {
+ if (ISMOUSE_MOTION(event->type) && (event->val != KM_NOTHING)) {
printf("%s: 'val != NOTHING' for a cursor motion event, this should not happen.\n", __func__);
WM_event_print(event);
}
@@ -252,36 +254,67 @@ void wm_event_init_from_window(wmWindow *win, wmEvent *event)
/** \name Notifiers & Listeners
* \{ */
-static bool wm_test_duplicate_notifier(const wmWindowManager *wm, uint type, void *reference)
+/**
+ * Hash for #wmWindowManager.notifier_queue_set, ignores `window`.
+ */
+static uint note_hash_for_queue_fn(const void *ptr)
{
- LISTBASE_FOREACH (wmNotifier *, note, &wm->notifier_queue) {
- if ((note->category | note->data | note->subtype | note->action) == type &&
- note->reference == reference) {
- return true;
- }
- }
+ const wmNotifier *note = static_cast<const wmNotifier *>(ptr);
+ return (BLI_ghashutil_ptrhash(note->reference) ^
+ (note->category | note->data | note->subtype | note->action));
+}
- return false;
+/**
+ * Comparison for #wmWindowManager.notifier_queue_set
+ *
+ * \note This is not an exact equality function as the `window` is ignored.
+ */
+static bool note_cmp_for_queue_fn(const void *a, const void *b)
+{
+ const wmNotifier *note_a = static_cast<const wmNotifier *>(a);
+ const wmNotifier *note_b = static_cast<const wmNotifier *>(b);
+ return !(((note_a->category | note_a->data | note_a->subtype | note_a->action) ==
+ (note_b->category | note_b->data | note_b->subtype | note_b->action)) &&
+ (note_a->reference == note_b->reference));
}
void WM_event_add_notifier_ex(wmWindowManager *wm, const wmWindow *win, uint type, void *reference)
{
- if (wm_test_duplicate_notifier(wm, type, reference)) {
+ if (wm == nullptr) {
+ /* There may be some cases where e.g. `G_MAIN` is not actually the real current main, but some
+ * other temporary one (e.g. during liboverride processing over linked data), leading to null
+ * window manager.
+ *
+ * This is fairly bad and weak, but unfortunately RNA does not have any way to operate over
+ * another main than G_MAIN currently. */
return;
}
- wmNotifier *note = MEM_cnew<wmNotifier>(__func__);
+ wmNotifier note_test = {nullptr};
- BLI_addtail(&wm->notifier_queue, note);
+ note_test.window = win;
- note->window = win;
+ note_test.category = type & NOTE_CATEGORY;
+ note_test.data = type & NOTE_DATA;
+ note_test.subtype = type & NOTE_SUBTYPE;
+ note_test.action = type & NOTE_ACTION;
+ note_test.reference = reference;
- note->category = type & NOTE_CATEGORY;
- note->data = type & NOTE_DATA;
- note->subtype = type & NOTE_SUBTYPE;
- note->action = type & NOTE_ACTION;
+ BLI_assert(!wm_notifier_is_clear(&note_test));
- note->reference = reference;
+ if (wm->notifier_queue_set == nullptr) {
+ wm->notifier_queue_set = BLI_gset_new_ex(
+ note_hash_for_queue_fn, note_cmp_for_queue_fn, __func__, 1024);
+ }
+
+ void **note_p;
+ if (BLI_gset_ensure_p_ex(wm->notifier_queue_set, &note_test, &note_p)) {
+ return;
+ }
+ wmNotifier *note = MEM_new<wmNotifier>(__func__);
+ *note = note_test;
+ *note_p = note;
+ BLI_addtail(&wm->notifier_queue, note);
}
/* XXX: in future, which notifiers to send to other windows? */
@@ -295,20 +328,7 @@ void WM_main_add_notifier(unsigned int type, void *reference)
Main *bmain = G_MAIN;
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
- if (!wm || wm_test_duplicate_notifier(wm, type, reference)) {
- return;
- }
-
- wmNotifier *note = MEM_cnew<wmNotifier>(__func__);
-
- BLI_addtail(&wm->notifier_queue, note);
-
- note->category = type & NOTE_CATEGORY;
- note->data = type & NOTE_DATA;
- note->subtype = type & NOTE_SUBTYPE;
- note->action = type & NOTE_ACTION;
-
- note->reference = reference;
+ WM_event_add_notifier_ex(wm, nullptr, type, reference);
}
void WM_main_remove_notifier_reference(const void *reference)
@@ -319,6 +339,9 @@ void WM_main_remove_notifier_reference(const void *reference)
if (wm) {
LISTBASE_FOREACH_MUTABLE (wmNotifier *, note, &wm->notifier_queue) {
if (note->reference == reference) {
+ const bool removed = BLI_gset_remove(wm->notifier_queue_set, note, nullptr);
+ BLI_assert(removed);
+ UNUSED_VARS_NDEBUG(removed);
/* Don't remove because this causes problems for #wm_event_do_notifiers
* which may be looping on the data (deleting screens). */
wm_notifier_clear(note);
@@ -374,6 +397,12 @@ static void wm_notifier_clear(wmNotifier *note)
{
/* nullptr the entire notifier, only leaving (`next`, `prev`) members intact. */
memset(((char *)note) + sizeof(Link), 0, sizeof(*note) - sizeof(Link));
+ note->category = NOTE_CATEGORY_TAG_CLEARED;
+}
+
+static bool wm_notifier_is_clear(const wmNotifier *note)
+{
+ return note->category == NOTE_CATEGORY_TAG_CLEARED;
}
void wm_event_do_depsgraph(bContext *C, bool is_after_open_file)
@@ -473,7 +502,7 @@ void wm_event_do_notifiers(bContext *C)
CTX_wm_window_set(C, win);
- LISTBASE_FOREACH_MUTABLE (wmNotifier *, note, &wm->notifier_queue) {
+ LISTBASE_FOREACH_MUTABLE (const wmNotifier *, note, &wm->notifier_queue) {
if (note->category == NC_WM) {
if (ELEM(note->data, ND_FILEREAD, ND_FILESAVE)) {
wm->file_saved = 1;
@@ -565,8 +594,15 @@ void wm_event_do_notifiers(bContext *C)
}
/* The notifiers are sent without context, to keep it clean. */
- wmNotifier *note;
- while ((note = static_cast<wmNotifier *>(BLI_pophead(&wm->notifier_queue)))) {
+ const wmNotifier *note;
+ while ((note = static_cast<const wmNotifier *>(BLI_pophead(&wm->notifier_queue)))) {
+ if (wm_notifier_is_clear(note)) {
+ MEM_freeN((void *)note);
+ continue;
+ }
+ const bool removed = BLI_gset_remove(wm->notifier_queue_set, note, nullptr);
+ BLI_assert(removed);
+ UNUSED_VARS_NDEBUG(removed);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
Scene *scene = WM_window_get_active_scene(win);
bScreen *screen = WM_window_get_active_screen(win);
@@ -630,7 +666,7 @@ void wm_event_do_notifiers(bContext *C)
}
}
- MEM_freeN(note);
+ MEM_freeN((void *)note);
}
#endif /* If 1 (postpone disabling for in favor of message-bus), eventually. */
@@ -657,7 +693,7 @@ void wm_event_do_notifiers(bContext *C)
wm_test_autorun_warning(C);
}
-static int wm_event_always_pass(const wmEvent *event)
+static bool wm_event_always_pass(const wmEvent *event)
{
/* Some events we always pass on, to ensure proper communication. */
return ISTIMER(event->type) || (event->type == WINDEACTIVATE);
@@ -667,14 +703,23 @@ static int wm_event_always_pass(const wmEvent *event)
* Debug only sanity check for the return value of event handlers. Checks that "always pass" events
* don't cause non-passing handler return values, and thus actually pass.
*
- * Can't be executed if the handler just loaded a file (typically identified by `CTX_wm_window(C)`
- * returning `nullptr`), because the event will have been freed then.
+ * \param C: Pass in the context to check if it's "window" was cleared.
+ * The event check can't be executed if the handler just loaded a file or closed the window.
+ * (typically identified by `CTX_wm_window(C)` returning null),
+ * because the event will have been freed then.
+ * When null, always check the event (assume the caller knows the event was not freed).
*/
-BLI_INLINE void wm_event_handler_return_value_check(const wmEvent *event, const int action)
+BLI_INLINE void wm_event_handler_return_value_check(const bContext *C,
+ const wmEvent *event,
+ const int action)
{
- BLI_assert_msg(!wm_event_always_pass(event) || (action != WM_HANDLER_BREAK),
- "Return value for events that should always pass should never be BREAK.");
- UNUSED_VARS_NDEBUG(event, action);
+#ifndef NDEBUG
+ if (C == nullptr || CTX_wm_window(C)) {
+ BLI_assert_msg(!wm_event_always_pass(event) || (action != WM_HANDLER_BREAK),
+ "Return value for events that should always pass should never be BREAK.");
+ }
+#endif
+ UNUSED_VARS_NDEBUG(C, event, action);
}
/** \} */
@@ -1546,6 +1591,7 @@ static int wm_operator_call_internal(bContext *C,
case WM_OP_EXEC_AREA:
case WM_OP_EXEC_SCREEN:
event = nullptr;
+ break;
default:
break;
}
@@ -2086,9 +2132,7 @@ static bool wm_eventmatch(const wmEvent *winevent, const wmKeyMapItem *kmi)
/* The matching rules. */
if (kmitype == KM_TEXTINPUT) {
if (winevent->val == KM_PRESS) { /* Prevent double clicks. */
- /* Not using #ISTEXTINPUT anymore because (at least on Windows) some key codes above 255
- * could have printable ascii keys, See T30479. */
- if (ISKEYBOARD(winevent->type) && (winevent->ascii || winevent->utf8_buf[0])) {
+ if (ISKEYBOARD(winevent->type) && winevent->utf8_buf[0]) {
return true;
}
}
@@ -2733,7 +2777,7 @@ static int wm_handler_fileselect_call(bContext *C,
return wm_handler_fileselect_do(C, handlers, handler, event->val);
}
-static int wm_action_not_handled(int action)
+static bool wm_action_not_handled(int action)
{
return action == WM_HANDLER_CONTINUE || action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL);
}
@@ -3097,13 +3141,13 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
const bool do_debug_handler =
(G.debug & G_DEBUG_HANDLERS) &&
/* Comment this out to flood the console! (if you really want to test). */
- !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE);
+ !ISMOUSE_MOTION(event->type);
wmWindowManager *wm = CTX_wm_manager(C);
int action = WM_HANDLER_CONTINUE;
if (handlers == nullptr) {
- wm_event_handler_return_value_check(event, action);
+ wm_event_handler_return_value_check(C, event, action);
return action;
}
@@ -3269,9 +3313,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis
}
/* Do some extra sanity checking before returning the action. */
- if (CTX_wm_window(C) != nullptr) {
- wm_event_handler_return_value_check(event, action);
- }
+ wm_event_handler_return_value_check(C, event, action);
return action;
}
@@ -3288,7 +3330,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
return action;
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
/* Test for #KM_CLICK_DRAG events. */
/* NOTE(@campbellbarton): Needed so drag can be used for editors that support both click
@@ -3442,7 +3484,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers)
}
}
- wm_event_handler_return_value_check(event, action);
+ wm_event_handler_return_value_check(C, event, action);
return action;
}
@@ -3570,7 +3612,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv
/* Clear drop icon. */
screen->do_draw_drag = true;
- /* Restore cursor (disabled, see `wm_dragdrop.c`) */
+ /* Restore cursor (disabled, see `wm_dragdrop.cc`) */
// WM_cursor_modal_restore(win);
}
}
@@ -3719,7 +3761,7 @@ static int wm_event_do_handlers_area_regions(bContext *C, wmEvent *event, ScrAre
action |= wm_event_do_region_handlers(C, event, region);
}
- wm_event_handler_return_value_check(event, action);
+ wm_event_handler_return_value_check(C, event, action);
return action;
}
@@ -3823,15 +3865,14 @@ void wm_event_do_handlers(bContext *C)
/* Active screen might change during handlers, update pointer. */
screen = WM_window_get_active_screen(win);
- if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) &&
- !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (G.debug & (G_DEBUG_HANDLERS | G_DEBUG_EVENTS) && !ISMOUSE_MOTION(event->type)) {
printf("\n%s: Handling event\n", __func__);
WM_event_print(event);
}
/* Take care of pie event filter. */
if (wm_event_pie_filter(win, event)) {
- if (!ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (!ISMOUSE_MOTION(event->type)) {
CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed");
}
BLI_remlink(&win->event_queue, event);
@@ -3853,7 +3894,7 @@ void wm_event_do_handlers(bContext *C)
/* Clear tool-tip on mouse move. */
if (screen->tool_tip && screen->tool_tip->exit_on_event) {
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) >
WM_EVENT_CURSOR_MOTION_THRESHOLD) {
WM_tooltip_clear(C, win);
@@ -4041,9 +4082,9 @@ void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval
* An appropriate window is either of the following:
* * A parent window that does not yet contain a modal File Browser. This is determined using
* #ED_fileselect_handler_area_find_any_with_op().
- * * A parent window containing a modal File Browser, but in a maximized/fullscreen state. Users
+ * * A parent window containing a modal File Browser, but in a maximized/full-screen state. Users
* shouldn't be able to put a temporary screen like the modal File Browser into
- * maximized/fullscreen state themselves. So this setup indicates that the File Browser was
+ * maximized/full-screen state themselves. So this setup indicates that the File Browser was
* opened using #USER_TEMP_SPACE_DISPLAY_FULLSCREEN.
*
* If no appropriate parent window can be found from the context window, return the first
@@ -4692,7 +4733,8 @@ static int convert_key(GHOST_TKey key)
return EVT_LEFTCTRLKEY;
case GHOST_kKeyRightControl:
return EVT_RIGHTCTRLKEY;
- case GHOST_kKeyOS:
+ case GHOST_kKeyLeftOS:
+ case GHOST_kKeyRightOS:
return EVT_OSKEY;
case GHOST_kKeyLeftAlt:
return EVT_LEFTALTKEY;
@@ -4977,7 +5019,7 @@ static bool wm_event_is_double_click(const wmEvent *event)
{
if ((event->type == event->prev_type) && (event->prev_val == KM_RELEASE) &&
(event->val == KM_PRESS)) {
- if (ISMOUSE(event->type) && WM_event_drag_test(event, event->prev_press_xy)) {
+ if (ISMOUSE_BUTTON(event->type) && WM_event_drag_test(event, event->prev_press_xy)) {
/* Pass. */
}
else {
@@ -5042,7 +5084,6 @@ static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win)
tevent = *event_last;
tevent.flag = (eWM_EventFlag)0;
- tevent.ascii = '\0';
tevent.utf8_buf[0] = '\0';
wm_event_custom_clear(&tevent);
@@ -5136,6 +5177,53 @@ static void wm_event_state_update_and_click_set(wmEvent *event,
wm_event_state_update_and_click_set_ex(event, event_state, is_keyboard, check_double_click);
}
+/* Returns true when the two events corresponds to a press of the same key with the same modifiers.
+ */
+static bool wm_event_is_same_key_press(const wmEvent &event_a, const wmEvent &event_b)
+{
+ if (event_a.val != KM_PRESS || event_b.val != KM_PRESS) {
+ return false;
+ }
+
+ if (event_a.modifier != event_b.modifier || event_a.type != event_b.type) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Returns true if the event is a key press event which is to be ignored and not added to the event
+ * queue.
+ *
+ * A key press event will be ignored if there is already matched key press in the queue.
+ * This avoids the event queue "clogging" in the situations when there is an operator bound to a
+ * key press event and the execution time of the operator is longer than the key repeat.
+ */
+static bool wm_event_is_ignorable_key_press(const wmWindow *win, const wmEvent &event)
+{
+ if (BLI_listbase_is_empty(&win->event_queue)) {
+ /* If the queue is empty never ignore the event.
+ * Empty queue at this point means that the events are handled fast enough, and there is no
+ * reason to ignore anything. */
+ return false;
+ }
+
+ if ((event.flag & WM_EVENT_IS_REPEAT) == 0) {
+ /* Only ignore repeat events from the keyboard, and allow accumulation of non-repeat events.
+ *
+ * The goal of this check is to allow events coming from a keyboard macro software, which can
+ * generate events quicker than the main loop handles them. In this case we want all events to
+ * be handled (unless the keyboard macro software tags them as repeat) because otherwise it
+ * will become impossible to get reliable results of automated events testing. */
+ return false;
+ }
+
+ const wmEvent &last_event = *static_cast<const wmEvent *>(win->event_queue.last);
+
+ return wm_event_is_same_key_press(last_event, event);
+}
+
void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata)
{
if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) {
@@ -5171,6 +5259,13 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event.prev_type = event.type;
event.prev_val = event.val;
+ /* Always use modifiers from the active window since
+ * changes to modifiers aren't sent to inactive windows, see: T66088. */
+ if ((wm->winactive != win) && (wm->winactive && wm->winactive->eventstate)) {
+ event.modifier = wm->winactive->eventstate->modifier;
+ event.keymodifier = wm->winactive->eventstate->keymodifier;
+ }
+
/* Ensure the event state is correct, any deviation from this may cause bugs.
*
* NOTE: #EVENT_NONE is set when unknown keys are pressed,
@@ -5213,6 +5308,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
if (win_other) {
wmEvent event_other = *win_other->eventstate;
+ /* Use the modifier state of this window. */
+ event_other.modifier = event.modifier;
+ event_other.keymodifier = event.keymodifier;
+
/* See comment for this operation on `event` for details. */
event_other.prev_type = event_other.type;
event_other.prev_val = event_other.val;
@@ -5302,6 +5401,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
if (win_other) {
wmEvent event_other = *win_other->eventstate;
+ /* Use the modifier state of this window. */
+ event_other.modifier = event.modifier;
+ event_other.keymodifier = event.keymodifier;
+
/* See comment for this operation on `event` for details. */
event_other.prev_type = event_other.type;
event_other.prev_val = event_other.val;
@@ -5329,8 +5432,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
break;
}
- event.ascii = kd->ascii;
- /* Might be not nullptr terminated. */
+ /* Might be not null terminated. */
memcpy(event.utf8_buf, kd->utf8_buf, sizeof(event.utf8_buf));
if (kd->is_repeat) {
event.flag |= WM_EVENT_IS_REPEAT;
@@ -5341,8 +5443,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
/* Exclude arrow keys, escape, etc from text input. */
if (type == GHOST_kEventKeyUp) {
- event.ascii = '\0';
-
/* Ghost should do this already for key up. */
if (event.utf8_buf[0]) {
CLOG_ERROR(WM_LOG_EVENTS,
@@ -5351,15 +5451,28 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
event.utf8_buf[0] = '\0';
}
else {
- if (event.ascii < 32 && event.ascii > 0) {
- event.ascii = '\0';
- }
if (event.utf8_buf[0] < 32 && event.utf8_buf[0] > 0) {
event.utf8_buf[0] = '\0';
}
}
if (event.utf8_buf[0]) {
+ /* NOTE(@campbellbarton): Detect non-ASCII characters stored in `utf8_buf`,
+ * ideally this would never happen but it can't be ruled out for X11 which has
+ * special handling of Latin1 when building without UTF8 support.
+ * Avoid regressions by adding this conversions, it should eventually be removed. */
+ if ((event.utf8_buf[0] >= 0x80) && (event.utf8_buf[1] == '\0')) {
+ const uint c = (uint)event.utf8_buf[0];
+ int utf8_buf_len = BLI_str_utf8_from_unicode(c, event.utf8_buf, sizeof(event.utf8_buf));
+ CLOG_ERROR(WM_LOG_EVENTS,
+ "ghost detected non-ASCII single byte character '%u', converting to utf8 "
+ "('%.*s', length=%d)",
+ c,
+ utf8_buf_len,
+ event.utf8_buf,
+ utf8_buf_len);
+ }
+
if (BLI_str_utf8_size(event.utf8_buf) == -1) {
CLOG_ERROR(WM_LOG_EVENTS,
"ghost detected an invalid unicode character '%d'",
@@ -5368,6 +5481,25 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
}
}
+ /* NOTE(@campbellbarton): Setting the modifier state based on press/release
+ * is technically incorrect.
+ *
+ * - The user might hold both left/right modifier keys, then only release one.
+ *
+ * This could be solved by storing a separate flag for the left/right modifiers,
+ * and combine them into `event.modifiers`.
+ *
+ * - The user might have multiple keyboards (or keyboard + NDOF device)
+ * where it's possible to press the same modifier key multiple times.
+ *
+ * This could be solved by tracking the number of held modifier keys,
+ * (this is in fact what LIBXKB does), however doing this relies on all GHOST
+ * back-ends properly reporting every press/release as any mismatch could result
+ * in modifier keys being stuck (which is very bad!).
+ *
+ * To my knowledge users never reported a bug relating to these limitations so
+ * it seems reasonable to keep the current logic. */
+
switch (event.type) {
case EVT_LEFTSHIFTKEY:
case EVT_RIGHTSHIFTKEY: {
@@ -5432,7 +5564,9 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void
G.is_break = true;
}
- wm_event_add(win, &event);
+ if (!wm_event_is_ignorable_key_press(win, event)) {
+ wm_event_add(win, &event);
+ }
break;
}
@@ -5815,11 +5949,12 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win)
bToolRef *tref = nullptr;
if ((region->regiontype == RGN_TYPE_WINDOW) &&
((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK)) {
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
WorkSpace *workspace = WM_window_get_active_workspace(win);
bToolKey tkey{};
tkey.space_type = area->spacetype;
- tkey.mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype);
+ tkey.mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype);
tref = WM_toolsystem_ref_find(workspace, &tkey);
}
wm_event_cursor_store(&cd->state, win->eventstate, area->spacetype, region->regiontype, tref);
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index a4d5bed21da..6216cd87e70 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -71,6 +71,7 @@
#include "BKE_lib_override.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_main_namemap.h"
#include "BKE_packedFile.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -295,6 +296,12 @@ static void wm_window_match_keep_current_wm(const bContext *C,
}
}
+ /* we'll be using the current wm list directly; make sure
+ * the names are validated and in the name map. */
+ LISTBASE_FOREACH (wmWindowManager *, wm_item, current_wm_list) {
+ BKE_main_namemap_get_name(bmain, &wm_item->id, wm_item->id.name + 2);
+ }
+
*r_new_wm_list = *current_wm_list;
}
@@ -698,6 +705,14 @@ static void wm_file_read_post(bContext *C, const struct wmFileReadPost_Params *p
}
}
+ if (is_factory_startup && BLT_translate_new_dataname()) {
+ /* Translate workspace names */
+ LISTBASE_FOREACH_MUTABLE (WorkSpace *, workspace, &bmain->workspaces) {
+ BKE_libblock_rename(
+ bmain, &workspace->id, CTX_DATA_(BLT_I18NCONTEXT_ID_WORKSPACE, workspace->id.name + 2));
+ }
+ }
+
if (use_data) {
/* important to do before NULL'ing the context */
BKE_callback_exec_null(bmain, BKE_CB_EVT_VERSION_UPDATE);
@@ -1004,6 +1019,8 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
WM_cursor_wait(false);
+ BLI_assert(BKE_main_namemap_validate(CTX_data_main(C)));
+
return success;
}
@@ -1380,29 +1397,27 @@ void wm_homefile_read_post(struct bContext *C,
void wm_history_file_read(void)
{
- char name[FILE_MAX];
- LinkNode *l, *lines;
- struct RecentFile *recent;
- const char *line;
- int num;
const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
-
if (!cfgdir) {
return;
}
+ char name[FILE_MAX];
+ LinkNode *l;
+ int num;
+
BLI_join_dirfile(name, sizeof(name), cfgdir, BLENDER_HISTORY_FILE);
- lines = BLI_file_read_as_lines(name);
+ LinkNode *lines = BLI_file_read_as_lines(name);
wm_history_files_free();
/* read list of recent opened files from recent-files.txt to memory */
for (l = lines, num = 0; l && (num < U.recent_files); l = l->next) {
- line = l->link;
+ const char *line = l->link;
/* don't check if files exist, causes slow startup for remote/external drives */
if (line[0]) {
- recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile");
+ struct RecentFile *recent = (RecentFile *)MEM_mallocN(sizeof(RecentFile), "RecentFile");
BLI_addtail(&(G.recent_files), recent);
recent->filepath = BLI_strdup(line);
num++;
@@ -1765,9 +1780,11 @@ static bool wm_file_write(bContext *C,
/* Enforce full override check/generation on file save. */
BKE_lib_override_library_main_operations_create(bmain, true);
- /* NOTE: Ideally we would call `WM_redraw_windows` here to remove any open menus. But we
- * can crash if saving from a script, see T92704 & T97627. Just checking `!G.background
- * && BLI_thread_is_main()` is not sufficient to fix this. */
+ /* NOTE: Ideally we would call `WM_redraw_windows` here to remove any open menus.
+ * But we can crash if saving from a script, see T92704 & T97627.
+ * Just checking `!G.background && BLI_thread_is_main()` is not sufficient to fix this.
+ * Additionally some EGL configurations don't support reading the front-buffer
+ * immediately after drawing, see: T98462. In that case off-screen drawing is necessary. */
/* don't forget not to return without! */
WM_cursor_wait(true);
@@ -1821,7 +1838,7 @@ static bool wm_file_write(bContext *C,
ED_editors_flush_edits(bmain);
/* XXX(ton): temp solution to solve bug, real fix coming. */
- bmain->recovered = 0;
+ bmain->recovered = false;
if (BLO_write_file(bmain,
filepath,
@@ -1880,13 +1897,10 @@ static bool wm_file_write(bContext *C,
/** \name Auto-Save API
* \{ */
-static void wm_autosave_location(char *filepath)
+static void wm_autosave_location(char filepath[FILE_MAX])
{
const int pid = abs(getpid());
char path[1024];
-#ifdef WIN32
- const char *savedir;
-#endif
/* Normally there is no need to check for this to be NULL,
* however this runs on exit when it may be cleared. */
@@ -1902,23 +1916,21 @@ static void wm_autosave_location(char *filepath)
BLI_snprintf(path, sizeof(path), "%d_autosave.blend", pid);
}
+ const char *tempdir_base = BKE_tempdir_base();
+ /* NOTE(@campbellbarton): It's strange that this is only used on WIN32.
+ * From reading commits it seems accessing the temporary directory used to be less reliable.
+ * If this is still the case on WIN32 - other features such as copy-paste will also fail.
+ * We could support #BLENDER_USER_AUTOSAVE on all platforms or remove it entirely. */
#ifdef WIN32
- /* XXX Need to investigate how to handle default location of '/tmp/'
- * This is a relative directory on Windows, and it may be
- * found. Example:
- * Blender installed on D:\ drive, D:\ drive has D:\tmp\
- * Now, BLI_exists() will find '/tmp/' exists, but
- * BLI_make_file_string will create string that has it most likely on C:\
- * through BLI_windows_get_default_root_dir().
- * If there is no C:\tmp autosave fails. */
- if (!BLI_exists(BKE_tempdir_base())) {
- savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL);
- BLI_make_file_string("/", filepath, savedir, path);
- return;
+ if (!BLI_exists(tempdir_base)) {
+ const char *savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL);
+ if (savedir) {
+ tempdir_base = savedir;
+ }
}
#endif
- BLI_join_dirfile(filepath, FILE_MAX, BKE_tempdir_base(), path);
+ BLI_join_dirfile(filepath, FILE_MAX, tempdir_base, path);
}
static void wm_autosave_write(Main *bmain, wmWindowManager *wm)
@@ -3003,7 +3015,9 @@ void WM_OT_recover_auto_save(wmOperatorType *ot)
static void wm_filepath_default(const Main *bmain, char *filepath)
{
if (bmain->filepath[0] == '\0') {
- BLI_path_filename_ensure(filepath, FILE_MAX, "untitled.blend");
+ char filename_untitled[FILE_MAXFILE];
+ SNPRINTF(filename_untitled, "%s.blend", DATA_("untitled"));
+ BLI_path_filename_ensure(filepath, FILE_MAX, filename_untitled);
}
}
@@ -3636,7 +3650,7 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C,
BLI_split_file_part(blendfile_path, filename, sizeof(filename));
}
else {
- STRNCPY(filename, "untitled.blend");
+ SNPRINTF(filename, "%s.blend", DATA_("untitled"));
}
uiItemL(layout, filename, ICON_NONE);
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index f2c41dada48..0ea783af1af 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -258,7 +258,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
/* from here down, no error returns */
if (view_layer && RNA_boolean_get(op->ptr, "autoselect")) {
- BKE_view_layer_base_deselect_all(view_layer);
+ BKE_view_layer_base_deselect_all(scene, view_layer);
}
/* tag everything, all untagged data can be made local
diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c
index 9b34468ae82..2bc1fb1519a 100644
--- a/source/blender/windowmanager/intern/wm_gesture.c
+++ b/source/blender/windowmanager/intern/wm_gesture.c
@@ -122,7 +122,7 @@ static void wm_gesture_draw_line_active_side(rcti *rect, const bool flip)
uint shdr_col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
GPU_blend(GPU_BLEND_ALPHA);
- immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
const float gradient_length = 150.0f * U.pixelsize;
float line_dir[2];
@@ -175,7 +175,7 @@ static void wm_gesture_draw_line(wmGesture *gt)
uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -207,7 +207,7 @@ static void wm_gesture_draw_rect(wmGesture *gt)
GPU_blend(GPU_BLEND_ALPHA);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
immRecti(shdr_pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
@@ -218,7 +218,7 @@ static void wm_gesture_draw_rect(wmGesture *gt)
shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -248,7 +248,7 @@ static void wm_gesture_draw_circle(wmGesture *gt)
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
imm_draw_circle_fill_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
@@ -257,7 +257,7 @@ static void wm_gesture_draw_circle(wmGesture *gt)
GPU_blend(GPU_BLEND_NONE);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -361,7 +361,7 @@ static void wm_gesture_draw_lasso(wmGesture *gt, bool filled)
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
@@ -395,7 +395,7 @@ static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt)
const uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 252cfc6e143..7ab2e67e4b6 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -166,7 +166,11 @@ void WM_init_opengl(void)
if (G.background) {
/* Ghost is still not initialized elsewhere in background mode. */
- wm_ghost_init(NULL);
+ wm_ghost_init_background();
+ }
+
+ if (!GPU_backend_supported()) {
+ return;
}
/* Needs to be first to have an OpenGL context bound. */
@@ -536,7 +540,7 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_vfont_clipboard_free();
BKE_node_clipboard_free();
-#ifdef WITH_COMPOSITOR
+#ifdef WITH_COMPOSITOR_CPU
COM_deinitialize();
#endif
@@ -572,14 +576,6 @@ void WM_exit_ex(bContext *C, const bool do_python)
BLF_exit();
- if (opengl_is_init) {
- DRW_opengl_context_enable_ex(false);
- GPU_pass_cache_free();
- GPU_exit();
- DRW_opengl_context_disable_ex(false);
- DRW_opengl_context_destroy();
- }
-
BLT_lang_free();
ANIM_keyingset_infos_exit();
@@ -604,13 +600,24 @@ void WM_exit_ex(bContext *C, const bool do_python)
ED_file_exit(); /* for fsmenu */
- UI_exit();
+ /* Delete GPU resources and context. The UI also uses GPU resources and so
+ * is also deleted with the context active. */
+ if (opengl_is_init) {
+ DRW_opengl_context_enable_ex(false);
+ UI_exit();
+ GPU_pass_cache_free();
+ GPU_exit();
+ DRW_opengl_context_disable_ex(false);
+ DRW_opengl_context_destroy();
+ }
+ else {
+ UI_exit();
+ }
+
BKE_blender_userdef_data_free(&U, false);
RNA_exit(); /* should be after BPY_python_end so struct python slots are cleared */
- GPU_backend_exit();
-
wm_ghost_exit();
CTX_free(C);
@@ -627,13 +634,16 @@ void WM_exit_ex(bContext *C, const bool do_python)
BKE_sound_exit();
BKE_appdir_exit();
- CLG_exit();
BKE_blender_atexit();
wm_autosave_delete();
BKE_tempdir_session_purge();
+
+ /* Logging cannot be called after exiting (#CLOG_INFO, #CLOG_WARN etc will crash).
+ * So postpone exiting until other sub-systems that may use logging have shut down. */
+ CLG_exit();
}
void WM_exit(bContext *C)
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index 71c948dfbb9..bd3322a8023 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -120,16 +120,14 @@ void WM_operator_properties_filesel(wmOperatorType *ot,
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
- if (action == FILE_SAVE) {
- /* NOTE: this is only used to check if we should highlight the filename area red when the
- * filepath is an existing file. */
- prop = RNA_def_boolean(ot->srna,
- "check_existing",
- true,
- "Check Existing",
- "Check and warn on overwriting existing files");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
- }
+ /* NOTE: this is only used to check if we should highlight the filename area red when the
+ * filepath is an existing file. */
+ prop = RNA_def_boolean(ot->srna,
+ "check_existing",
+ action == FILE_SAVE,
+ "Check Existing",
+ "Check and warn on overwriting existing files");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_boolean(
ot->srna, "filter_blender", (filter & FILE_TYPE_BLENDER) != 0, "Filter .blend files", "");
diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c
index bde072bf000..6fc9300926c 100644
--- a/source/blender/windowmanager/intern/wm_operator_utils.c
+++ b/source/blender/windowmanager/intern/wm_operator_utils.c
@@ -209,10 +209,11 @@ static int op_generic_value_invoke(bContext *C, wmOperator *op, const wmEvent *e
return WM_operator_call_notest(C, op);
}
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
uint objects_len;
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
- view_layer, CTX_wm_view3d(C), &objects_len);
+ scene, view_layer, CTX_wm_view3d(C), &objects_len);
if (objects_len == 0) {
MEM_freeN(objects);
return OPERATOR_CANCELLED;
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 33c69a23558..259730513be 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -81,6 +81,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
+#include "RNA_path.h"
#include "RNA_prototypes.h"
#include "UI_interface.h"
@@ -638,7 +639,7 @@ char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, i
if (lhs == NULL) {
/* Fallback to `bpy.data.foo[id]` if we don't find in the context. */
- lhs = RNA_path_full_property_py(CTX_data_main(C), ptr, prop, index);
+ lhs = RNA_path_full_property_py(ptr, prop, index);
}
if (!lhs) {
@@ -939,7 +940,7 @@ int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
return ret_value | OPERATOR_PASS_THROUGH;
}
- if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ if (ISMOUSE_MOTION(event->type)) {
const int drag_delta[2] = {
mval[0] - event->mval[0],
mval[1] - event->mval[1],
@@ -2327,7 +2328,7 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
GPU_matrix_rotate_2d(RAD2DEGF(rot));
}
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR);
immUniformColor3fvAlpha(col, alpha);
immBindTexture("image", rc->texture);
@@ -2358,7 +2359,7 @@ static void radial_control_paint_tex(RadialControl *rc, float radius, float alph
}
else {
/* flat color if no texture available */
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3fvAlpha(col, alpha);
imm_draw_circle_fill_2d(pos, 0.0f, 0.0f, radius, 40);
}
@@ -2470,7 +2471,7 @@ static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
if (rc->subtype == PROP_ANGLE) {
GPU_matrix_push();
@@ -3672,7 +3673,7 @@ static void WM_OT_doc_view_manual_ui_context(wmOperatorType *ot)
/* -------------------------------------------------------------------- */
/** \name Toggle Stereo 3D Operator
*
- * Turning it fullscreen if needed.
+ * Turning it full-screen if needed.
* \{ */
static void WM_OT_stereo3d_set(wmOperatorType *ot)
@@ -3804,7 +3805,7 @@ static void gesture_circle_modal_keymap(wmKeyConfig *keyconf)
/* WARNING: Name is incorrect, use for non-3d views. */
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "View3D Gesture Circle");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -3837,7 +3838,7 @@ static void gesture_straightline_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Gesture Straight Line");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -3866,7 +3867,7 @@ static void gesture_box_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Gesture Box");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -3919,7 +3920,7 @@ static void gesture_lasso_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Gesture Lasso");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
@@ -3954,7 +3955,7 @@ static void gesture_zoom_border_modal_keymap(wmKeyConfig *keyconf)
wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Gesture Zoom Border");
- /* this function is called for each spacetype, only needs to add map once */
+ /* This function is called for each space-type, only needs to add map once. */
if (keymap && keymap->modal_items) {
return;
}
diff --git a/source/blender/windowmanager/intern/wm_platform_support.c b/source/blender/windowmanager/intern/wm_platform_support.c
index becc2d896d0..a0519506d29 100644
--- a/source/blender/windowmanager/intern/wm_platform_support.c
+++ b/source/blender/windowmanager/intern/wm_platform_support.c
@@ -32,35 +32,35 @@
*/
static bool wm_platform_support_check_approval(const char *platform_support_key, bool update)
{
- const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
- bool result = false;
-
if (G.factory_startup) {
- return result;
+ return false;
+ }
+ const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
+ if (!cfgdir) {
+ return false;
}
- if (cfgdir) {
- char filepath[FILE_MAX];
- BLI_join_dirfile(filepath, sizeof(filepath), cfgdir, BLENDER_PLATFORM_SUPPORT_FILE);
- LinkNode *lines = BLI_file_read_as_lines(filepath);
- for (LinkNode *line_node = lines; line_node; line_node = line_node->next) {
- char *line = line_node->link;
- if (STREQ(line, platform_support_key)) {
- result = true;
- break;
- }
+ bool result = false;
+ char filepath[FILE_MAX];
+ BLI_join_dirfile(filepath, sizeof(filepath), cfgdir, BLENDER_PLATFORM_SUPPORT_FILE);
+ LinkNode *lines = BLI_file_read_as_lines(filepath);
+ for (LinkNode *line_node = lines; line_node; line_node = line_node->next) {
+ char *line = line_node->link;
+ if (STREQ(line, platform_support_key)) {
+ result = true;
+ break;
}
+ }
- if (!result && update) {
- FILE *fp = BLI_fopen(filepath, "a");
- if (fp) {
- fprintf(fp, "%s\n", platform_support_key);
- fclose(fp);
- }
+ if (!result && update) {
+ FILE *fp = BLI_fopen(filepath, "a");
+ if (fp) {
+ fprintf(fp, "%s\n", platform_support_key);
+ fclose(fp);
}
-
- BLI_file_free_lines(lines);
}
+
+ BLI_file_free_lines(lines);
return result;
}
diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c
index 99f117f267a..7c6650922a4 100644
--- a/source/blender/windowmanager/intern/wm_playanim.c
+++ b/source/blender/windowmanager/intern/wm_playanim.c
@@ -482,7 +482,7 @@ static void draw_display_buffer(PlayState *ps, ImBuf *ibuf)
GPU_texture_bind(texture, 0);
if (!glsl_used) {
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE_COLOR);
immUniformColor3f(1.0f, 1.0f, 1.0f);
}
@@ -601,7 +601,7 @@ static void playanim_toscreen(
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
immUniformColor3ub(0, 255, 0);
immBegin(GPU_PRIM_LINES, 2);
@@ -1353,7 +1353,7 @@ static void playanim_window_open(const char *title, int posx, int posy, int size
posy,
sizex,
sizey,
- /* could optionally start fullscreen */
+ /* Could optionally start full-screen. */
GHOST_kWindowStateNormal,
false,
GHOST_kDrawingContextTypeOpenGL,
@@ -1807,21 +1807,22 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
AUD_Sound_free(source);
source = NULL;
#endif
+
/* we still miss freeing a lot!,
* but many areas could skip initialization too for anim play */
- GPU_shader_free_builtin_shaders();
+ IMB_exit();
+ DEG_free_node_types();
+
+ BLF_exit();
if (g_WS.gpu_context) {
GPU_context_active_set(g_WS.gpu_context);
+ GPU_exit();
GPU_context_discard(g_WS.gpu_context);
g_WS.gpu_context = NULL;
}
- BLF_exit();
-
- GPU_exit();
-
GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window);
/* early exit, IMB and BKE should be exited only in end */
@@ -1830,9 +1831,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv)
return filepath;
}
- IMB_exit();
- DEG_free_node_types();
-
totblock = MEM_get_memory_blocks_in_use();
if (totblock != 0) {
/* prints many bAKey, bArgument's which are tricky to fix */
diff --git a/source/blender/windowmanager/intern/wm_splash_screen.c b/source/blender/windowmanager/intern/wm_splash_screen.c
index 2e04a629308..8fca3deef92 100644
--- a/source/blender/windowmanager/intern/wm_splash_screen.c
+++ b/source/blender/windowmanager/intern/wm_splash_screen.c
@@ -78,50 +78,51 @@ static void wm_block_splash_add_label(uiBlock *block, const char *label, int x,
static void wm_block_splash_image_roundcorners_add(ImBuf *ibuf)
{
uchar *rct = (uchar *)ibuf->rect;
+ if (!rct) {
+ return;
+ }
- if (rct) {
- bTheme *btheme = UI_GetTheme();
- const float roundness = btheme->tui.wcol_menu_back.roundness * U.dpi_fac;
- const int size = roundness * 20;
-
- if (size < ibuf->x && size < ibuf->y) {
- /* Y-axis initial offset. */
- rct += 4 * (ibuf->y - size) * ibuf->x;
-
- for (int y = 0; y < size; y++) {
- for (int x = 0; x < size; x++, rct += 4) {
- const float pixel = 1.0 / size;
- const float u = pixel * x;
- const float v = pixel * y;
- const float distance = sqrt(u * u + v * v);
-
- /* Pointer offset to the alpha value of pixel. */
- /* NOTE: the left corner is flipped in the X-axis. */
- const int offset_l = 4 * (size - x - x - 1) + 3;
- const int offset_r = 4 * (ibuf->x - size) + 3;
-
- if (distance > 1.0) {
- rct[offset_l] = 0;
- rct[offset_r] = 0;
- }
- else {
- /* Create a single pixel wide transition for anti-aliasing.
- * Invert the distance and map its range [0, 1] to [0, pixel]. */
- const float fac = (1.0 - distance) * size;
-
- if (fac > 1.0) {
- continue;
- }
-
- const uchar alpha = unit_float_to_uchar_clamp(fac);
- rct[offset_l] = alpha;
- rct[offset_r] = alpha;
- }
+ bTheme *btheme = UI_GetTheme();
+ const float roundness = btheme->tui.wcol_menu_back.roundness * U.dpi_fac;
+ const int size = roundness * 20;
+
+ if (size < ibuf->x && size < ibuf->y) {
+ /* Y-axis initial offset. */
+ rct += 4 * (ibuf->y - size) * ibuf->x;
+
+ for (int y = 0; y < size; y++) {
+ for (int x = 0; x < size; x++, rct += 4) {
+ const float pixel = 1.0 / size;
+ const float u = pixel * x;
+ const float v = pixel * y;
+ const float distance = sqrt(u * u + v * v);
+
+ /* Pointer offset to the alpha value of pixel. */
+ /* NOTE: the left corner is flipped in the X-axis. */
+ const int offset_l = 4 * (size - x - x - 1) + 3;
+ const int offset_r = 4 * (ibuf->x - size) + 3;
+
+ if (distance > 1.0) {
+ rct[offset_l] = 0;
+ rct[offset_r] = 0;
}
+ else {
+ /* Create a single pixel wide transition for anti-aliasing.
+ * Invert the distance and map its range [0, 1] to [0, pixel]. */
+ const float fac = (1.0 - distance) * size;
- /* X-axis offset to the next row. */
- rct += 4 * (ibuf->x - size);
+ if (fac > 1.0) {
+ continue;
+ }
+
+ const uchar alpha = unit_float_to_uchar_clamp(fac);
+ rct[offset_l] = alpha;
+ rct[offset_r] = alpha;
+ }
}
+
+ /* X-axis offset to the next row. */
+ rct += 4 * (ibuf->x - size);
}
}
}
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
index f85c818cdf3..48a0d47f26a 100644
--- a/source/blender/windowmanager/intern/wm_stereo.c
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -47,7 +47,7 @@ void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE);
int soffx = WM_window_pixels_x(win) * 0.5f;
if (view == STEREO_LEFT_ID) {
@@ -95,7 +95,7 @@ void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
- immBindBuiltinProgram(GPU_SHADER_2D_IMAGE);
+ immBindBuiltinProgram(GPU_SHADER_3D_IMAGE);
int soffy;
if (view == STEREO_LEFT_ID) {
diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c
index 984a8ef41d0..6e08f607bae 100644
--- a/source/blender/windowmanager/intern/wm_toolsystem.c
+++ b/source/blender/windowmanager/intern/wm_toolsystem.c
@@ -26,6 +26,7 @@
#include "BKE_brush.h"
#include "BKE_context.h"
#include "BKE_idprop.h"
+#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_paint.h"
@@ -57,6 +58,7 @@ static void toolsystem_refresh_screen_from_active_tool(Main *bmain,
struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C)
{
WorkSpace *workspace = CTX_wm_workspace(C);
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
ScrArea *area = CTX_wm_area(C);
if ((area == NULL) || ((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
@@ -64,7 +66,7 @@ struct bToolRef *WM_toolsystem_ref_from_context(struct bContext *C)
}
const bToolKey tkey = {
.space_type = area->spacetype,
- .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
+ .mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype),
};
bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
/* We could return 'area->runtime.tool' in this case. */
@@ -268,6 +270,7 @@ void WM_toolsystem_refresh_all(struct bContext *C, struct WorkSpace *workspace)
void WM_toolsystem_reinit_all(struct bContext *C, wmWindow *win)
{
bScreen *screen = WM_window_get_active_screen(win);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (((1 << area->spacetype) & WM_TOOLSYSTEM_SPACE_MASK) == 0) {
@@ -277,7 +280,7 @@ void WM_toolsystem_reinit_all(struct bContext *C, wmWindow *win)
WorkSpace *workspace = WM_window_get_active_workspace(win);
const bToolKey tkey = {
.space_type = area->spacetype,
- .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
+ .mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype),
};
bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
if (tref) {
@@ -366,8 +369,9 @@ void WM_toolsystem_ref_sync_from_context(Main *bmain, WorkSpace *workspace, bToo
Scene *scene = WM_window_get_active_scene(win);
ToolSettings *ts = scene->toolsettings;
- const ViewLayer *view_layer = WM_window_get_active_view_layer(win);
- const Object *ob = OBACT(view_layer);
+ ViewLayer *view_layer = WM_window_get_active_view_layer(win);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ const Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob == NULL) {
/* pass */
}
@@ -437,13 +441,17 @@ static bool toolsystem_key_ensure_check(const bToolKey *tkey)
return false;
}
-int WM_toolsystem_mode_from_spacetype(ViewLayer *view_layer, ScrArea *area, int space_type)
+int WM_toolsystem_mode_from_spacetype(const Scene *scene,
+ ViewLayer *view_layer,
+ ScrArea *area,
+ int space_type)
{
int mode = -1;
switch (space_type) {
case SPACE_VIEW3D: {
/* 'area' may be NULL in this case. */
- Object *obact = OBACT(view_layer);
+ BKE_view_layer_synced_ensure(scene, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
if (obact != NULL) {
Object *obedit = OBEDIT_FROM_OBACT(obact);
mode = CTX_data_mode_enum_ex(obedit, obact, obact->mode);
@@ -471,14 +479,17 @@ int WM_toolsystem_mode_from_spacetype(ViewLayer *view_layer, ScrArea *area, int
return mode;
}
-bool WM_toolsystem_key_from_context(ViewLayer *view_layer, ScrArea *area, bToolKey *tkey)
+bool WM_toolsystem_key_from_context(const Scene *scene,
+ ViewLayer *view_layer,
+ ScrArea *area,
+ bToolKey *tkey)
{
int space_type = SPACE_EMPTY;
int mode = -1;
if (area != NULL) {
space_type = area->spacetype;
- mode = WM_toolsystem_mode_from_spacetype(view_layer, area, space_type);
+ mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, space_type);
}
if (mode != -1) {
@@ -504,6 +515,7 @@ void WM_toolsystem_refresh_active(bContext *C)
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
WorkSpace *workspace = WM_window_get_active_workspace(win);
bScreen *screen = WM_window_get_active_screen(win);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
/* Could skip loop for modes that don't depend on space type. */
int space_type_mask_handled = 0;
@@ -515,7 +527,7 @@ void WM_toolsystem_refresh_active(bContext *C)
space_type_mask_handled |= space_type_mask;
const bToolKey tkey = {
.space_type = area->spacetype,
- .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
+ .mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype),
};
bToolRef *tref = WM_toolsystem_ref_find(workspace, &tkey);
if (tref != area->runtime.tool) {
@@ -557,11 +569,14 @@ void WM_toolsystem_refresh_active(bContext *C)
}
}
-void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_layer, ScrArea *area)
+void WM_toolsystem_refresh_screen_area(WorkSpace *workspace,
+ const Scene *scene,
+ ViewLayer *view_layer,
+ ScrArea *area)
{
area->runtime.tool = NULL;
area->runtime.is_tool_set = true;
- const int mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype);
+ const int mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype);
LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
if (tref->space_type == area->spacetype) {
if (tref->mode == mode) {
@@ -580,12 +595,13 @@ void WM_toolsystem_refresh_screen_window(wmWindow *win)
space_type_has_tools[tref->space_type] = true;
}
bScreen *screen = WM_window_get_active_screen(win);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
area->runtime.tool = NULL;
area->runtime.is_tool_set = true;
if (space_type_has_tools[area->spacetype]) {
- WM_toolsystem_refresh_screen_area(workspace, view_layer, area);
+ WM_toolsystem_refresh_screen_area(workspace, scene, view_layer, area);
}
}
}
@@ -609,10 +625,11 @@ static void toolsystem_refresh_screen_from_active_tool(Main *bmain,
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
if (workspace == WM_window_get_active_workspace(win)) {
bScreen *screen = WM_window_get_active_screen(win);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == tref->space_type) {
- int mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype);
+ int mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype);
if (mode == tref->mode) {
area->runtime.tool = tref;
area->runtime.is_tool_set = true;
@@ -667,10 +684,11 @@ bToolRef *WM_toolsystem_ref_set_by_id_ex(
bToolRef *WM_toolsystem_ref_set_by_id(bContext *C, const char *name)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
ScrArea *area = CTX_wm_area(C);
bToolKey tkey;
- if (WM_toolsystem_key_from_context(view_layer, area, &tkey)) {
+ if (WM_toolsystem_key_from_context(scene, view_layer, area, &tkey)) {
WorkSpace *workspace = CTX_wm_workspace(C);
return WM_toolsystem_ref_set_by_id_ex(C, workspace, &tkey, name, false);
}
@@ -705,7 +723,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
case CTX_MODE_VERTEX_GPENCIL:
return "builtin_brush.Draw";
case CTX_MODE_SCULPT_CURVES:
- return "builtin_brush.Comb";
+ return "builtin_brush.density";
/* end temporary hack. */
case CTX_MODE_PARTICLE:
@@ -760,11 +778,12 @@ static bToolRef *toolsystem_reinit_ensure_toolref(bContext *C,
static void wm_toolsystem_update_from_context_view3d_impl(bContext *C, WorkSpace *workspace)
{
+ const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
int space_type = SPACE_VIEW3D;
const bToolKey tkey = {
.space_type = space_type,
- .mode = WM_toolsystem_mode_from_spacetype(view_layer, NULL, space_type),
+ .mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, NULL, space_type),
};
toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
}
@@ -800,14 +819,12 @@ void WM_toolsystem_update_from_context_view3d(bContext *C)
}
}
-void WM_toolsystem_update_from_context(bContext *C,
- WorkSpace *workspace,
- ViewLayer *view_layer,
- ScrArea *area)
+void WM_toolsystem_update_from_context(
+ bContext *C, WorkSpace *workspace, const Scene *scene, ViewLayer *view_layer, ScrArea *area)
{
const bToolKey tkey = {
.space_type = area->spacetype,
- .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
+ .mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype),
};
if (toolsystem_key_ensure_check(&tkey)) {
toolsystem_reinit_ensure_toolref(C, workspace, &tkey, NULL);
@@ -837,14 +854,15 @@ void WM_toolsystem_do_msg_notify_tag_refresh(bContext *C,
}
WorkSpace *workspace = WM_window_get_active_workspace(win);
+ const Scene *scene = WM_window_get_active_scene(win);
ViewLayer *view_layer = WM_window_get_active_view_layer(win);
const bToolKey tkey = {
.space_type = area->spacetype,
- .mode = WM_toolsystem_mode_from_spacetype(view_layer, area, area->spacetype),
+ .mode = WM_toolsystem_mode_from_spacetype(scene, view_layer, area, area->spacetype),
};
WM_toolsystem_refresh(C, workspace, &tkey);
- WM_toolsystem_refresh_screen_area(workspace, view_layer, area);
+ WM_toolsystem_refresh_screen_area(workspace, scene, view_layer, area);
}
static IDProperty *idprops_ensure_named_group(IDProperty *group, const char *idname)
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 36f91f8414a..b61ebdd11be 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -83,6 +83,12 @@
# include "BLI_threads.h"
#endif
+/**
+ * When windows are activated, simulate modifier press/release to match the current state of
+ * held modifier keys, see T40317.
+ */
+#define USE_WIN_ACTIVATE
+
/* the global to talk to ghost */
static GHOST_SystemHandle g_system = NULL;
@@ -114,6 +120,36 @@ static struct WMInitStruct {
};
/* -------------------------------------------------------------------- */
+/** \name Modifier Constants
+ * \{ */
+
+static const struct {
+ uint8_t flag;
+ GHOST_TKey ghost_key_pair[2];
+ GHOST_TModifierKey ghost_mask_pair[2];
+} g_modifier_table[] = {
+ {KM_SHIFT,
+ {GHOST_kKeyLeftShift, GHOST_kKeyRightShift},
+ {GHOST_kModifierKeyLeftShift, GHOST_kModifierKeyRightShift}},
+ {KM_CTRL,
+ {GHOST_kKeyLeftControl, GHOST_kKeyRightControl},
+ {GHOST_kModifierKeyLeftControl, GHOST_kModifierKeyRightControl}},
+ {KM_ALT,
+ {GHOST_kKeyLeftAlt, GHOST_kKeyRightAlt},
+ {GHOST_kModifierKeyLeftAlt, GHOST_kModifierKeyRightAlt}},
+ {KM_OSKEY,
+ {GHOST_kKeyLeftOS, GHOST_kKeyRightOS},
+ {GHOST_kModifierKeyLeftOS, GHOST_kModifierKeyRightOS}},
+};
+
+enum ModSide {
+ MOD_SIDE_LEFT = 0,
+ MOD_SIDE_RIGHT = 1,
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Window Open & Close
* \{ */
@@ -155,28 +191,30 @@ static void wm_window_check_size(rcti *rect)
static void wm_ghostwindow_destroy(wmWindowManager *wm, wmWindow *win)
{
- if (win->ghostwin) {
- /* Prevents non-drawable state of main windows (bugs T22967,
- * T25071 and possibly T22477 too). Always clear it even if
- * this window was not the drawable one, because we mess with
- * drawing context to discard the GW context. */
- wm_window_clear_drawable(wm);
+ if (UNLIKELY(!win->ghostwin)) {
+ return;
+ }
- if (win == wm->winactive) {
- wm->winactive = NULL;
- }
+ /* Prevents non-drawable state of main windows (bugs T22967,
+ * T25071 and possibly T22477 too). Always clear it even if
+ * this window was not the drawable one, because we mess with
+ * drawing context to discard the GW context. */
+ wm_window_clear_drawable(wm);
- /* We need this window's opengl context active to discard it. */
- GHOST_ActivateWindowDrawingContext(win->ghostwin);
- GPU_context_active_set(win->gpuctx);
+ if (win == wm->winactive) {
+ wm->winactive = NULL;
+ }
- /* Delete local GPU context. */
- GPU_context_discard(win->gpuctx);
+ /* We need this window's opengl context active to discard it. */
+ GHOST_ActivateWindowDrawingContext(win->ghostwin);
+ GPU_context_active_set(win->gpuctx);
- GHOST_DisposeWindow(g_system, win->ghostwin);
- win->ghostwin = NULL;
- win->gpuctx = NULL;
- }
+ /* Delete local GPU context. */
+ GPU_context_discard(win->gpuctx);
+
+ GHOST_DisposeWindow(g_system, win->ghostwin);
+ win->ghostwin = NULL;
+ win->gpuctx = NULL;
}
void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
@@ -510,7 +548,7 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm,
wmWindow *win,
bool is_dialog)
{
- /* a new window is created when pageflip mode is required for a window */
+ /* A new window is created when page-flip mode is required for a window. */
GHOST_GLSettings glSettings = {0};
if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
glSettings.flags |= GHOST_glStereoVisual;
@@ -961,43 +999,18 @@ void wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y)
wm_cursor_position_from_ghost_client_coords(win, r_x, r_y);
}
-typedef enum {
- SHIFT = 's',
- CONTROL = 'c',
- ALT = 'a',
- OS = 'C',
-} modifierKeyType;
-
-/* check if specified modifier key type is pressed */
-static int query_qual(modifierKeyType qual)
+/** Check if specified modifier key type is pressed. */
+static uint8_t wm_ghost_modifier_query(const enum ModSide side)
{
- GHOST_TModifierKey left, right;
- switch (qual) {
- case SHIFT:
- left = GHOST_kModifierKeyLeftShift;
- right = GHOST_kModifierKeyRightShift;
- break;
- case CONTROL:
- left = GHOST_kModifierKeyLeftControl;
- right = GHOST_kModifierKeyRightControl;
- break;
- case OS:
- left = right = GHOST_kModifierKeyOS;
- break;
- case ALT:
- default:
- left = GHOST_kModifierKeyLeftAlt;
- right = GHOST_kModifierKeyRightAlt;
- break;
- }
-
- bool val = false;
- GHOST_GetModifierKeyState(g_system, left, &val);
- if (!val) {
- GHOST_GetModifierKeyState(g_system, right, &val);
+ uint8_t result = 0;
+ for (int i = 0; i < ARRAY_SIZE(g_modifier_table); i++) {
+ bool val = false;
+ GHOST_GetModifierKeyState(g_system, g_modifier_table[i].ghost_mask_pair[side], &val);
+ if (val) {
+ result |= g_modifier_table[i].flag;
+ }
}
-
- return val;
+ return result;
}
static void wm_window_set_drawable(wmWindowManager *wm, wmWindow *win, bool activate)
@@ -1117,95 +1130,58 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
case GHOST_kEventWindowDeactivate:
wm_event_add_ghostevent(wm, win, type, data);
win->active = 0; /* XXX */
-
- /* clear modifiers for inactive windows */
- win->eventstate->modifier = 0;
- win->eventstate->keymodifier = 0;
-
break;
case GHOST_kEventWindowActivate: {
- const int keymodifier = ((query_qual(SHIFT) ? KM_SHIFT : 0) |
- (query_qual(CONTROL) ? KM_CTRL : 0) |
- (query_qual(ALT) ? KM_ALT : 0) | (query_qual(OS) ? KM_OSKEY : 0));
-
- /* Win23/GHOST modifier bug, see T40317 */
-#ifndef WIN32
-//# define USE_WIN_ACTIVATE
-#endif
/* No context change! C->wm->windrawable is drawable, or for area queues. */
wm->winactive = win;
win->active = 1;
- // window_handle(win, INPUTCHANGE, win->active);
/* bad ghost support for modifier keys... so on activate we set the modifiers again */
- /* TODO: This is not correct since a modifier may be held when a window is activated...
- * better solve this at ghost level. attempted fix r54450 but it caused bug T34255.
- *
- * For now don't send GHOST_kEventKeyDown events, just set the 'eventstate'.
- */
- GHOST_TEventKeyData kdata = {
- .key = GHOST_kKeyUnknown,
- .ascii = '\0',
- .utf8_buf = {'\0'},
- .is_repeat = false,
+ const uint8_t keymodifier_sided[2] = {
+ wm_ghost_modifier_query(MOD_SIDE_LEFT),
+ wm_ghost_modifier_query(MOD_SIDE_RIGHT),
};
- if (win->eventstate->modifier & KM_SHIFT) {
- if ((keymodifier & KM_SHIFT) == 0) {
- kdata.key = GHOST_kKeyLeftShift;
- wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
- }
- }
-#ifdef USE_WIN_ACTIVATE
- else {
- if (keymodifier & KM_SHIFT) {
- win->eventstate->modifier |= KM_SHIFT;
- }
- }
-#endif
- if (win->eventstate->modifier & KM_CTRL) {
- if ((keymodifier & KM_CTRL) == 0) {
- kdata.key = GHOST_kKeyLeftControl;
- wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
- }
- }
-#ifdef USE_WIN_ACTIVATE
- else {
- if (keymodifier & KM_CTRL) {
- win->eventstate->modifier |= KM_CTRL;
- }
- }
-#endif
- if (win->eventstate->modifier & KM_ALT) {
- if ((keymodifier & KM_ALT) == 0) {
- kdata.key = GHOST_kKeyLeftAlt;
- wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
- }
- }
-#ifdef USE_WIN_ACTIVATE
- else {
- if (keymodifier & KM_ALT) {
- win->eventstate->modifier |= KM_ALT;
- }
- }
-#endif
- if (win->eventstate->modifier & KM_OSKEY) {
- if ((keymodifier & KM_OSKEY) == 0) {
- kdata.key = GHOST_kKeyOS;
- wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
- }
- }
+ const uint8_t keymodifier = keymodifier_sided[0] | keymodifier_sided[1];
+ const uint8_t keymodifier_eventstate = win->eventstate->modifier;
+ if (keymodifier != keymodifier_eventstate) {
+ GHOST_TEventKeyData kdata = {
+ .key = GHOST_kKeyUnknown,
+ .utf8_buf = {'\0'},
+ .is_repeat = false,
+ };
+ for (int i = 0; i < ARRAY_SIZE(g_modifier_table); i++) {
+ if (keymodifier_eventstate & g_modifier_table[i].flag) {
+ if ((keymodifier & g_modifier_table[i].flag) == 0) {
+ for (int side = 0; side < 2; side++) {
+ if ((keymodifier_sided[side] & g_modifier_table[i].flag) == 0) {
+ kdata.key = g_modifier_table[i].ghost_key_pair[side];
+ wm_event_add_ghostevent(wm, win, GHOST_kEventKeyUp, &kdata);
+ /* Only ever send one release event
+ * (currently releasing multiple isn't needed and only confuses logic). */
+ break;
+ }
+ }
+ }
+ }
#ifdef USE_WIN_ACTIVATE
- else {
- if (keymodifier & KM_OSKEY) {
- win->eventstate->modifier |= KM_OSKEY;
- }
- }
+ else {
+ if (keymodifier & g_modifier_table[i].flag) {
+ for (int side = 0; side < 2; side++) {
+ if (keymodifier_sided[side] & g_modifier_table[i].flag) {
+ kdata.key = g_modifier_table[i].ghost_key_pair[side];
+ wm_event_add_ghostevent(wm, win, GHOST_kEventKeyDown, &kdata);
+ }
+ }
+ }
+ }
#endif
#undef USE_WIN_ACTIVATE
+ }
+ }
/* keymodifier zero, it hangs on hotkeys that open windows otherwise */
win->eventstate->keymodifier = 0;
@@ -1286,7 +1262,7 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
state_str = "maximized";
}
else if (state == GHOST_kWindowStateFullScreen) {
- state_str = "fullscreen";
+ state_str = "full-screen";
}
else {
state_str = "<unknown>";
@@ -1363,6 +1339,15 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt
event.type = MOUSEMOVE;
event.val = KM_NOTHING;
copy_v2_v2_int(event.prev_xy, event.xy);
+
+ wm_cursor_position_from_ghost_screen_coords(win, &ddd->x, &ddd->y);
+ event.xy[0] = ddd->x;
+ event.xy[1] = ddd->y;
+
+ /* The values from #wm_window_update_eventstate may not match (under WAYLAND they don't)
+ * Write this into the event state. */
+ copy_v2_v2_int(win->eventstate->xy, event.xy);
+
event.flag = 0;
/* No context change! C->wm->windrawable is drawable, or for area queues. */
@@ -1547,36 +1532,55 @@ void wm_window_process_events(const bContext *C)
void wm_ghost_init(bContext *C)
{
- if (!g_system) {
- GHOST_EventConsumerHandle consumer;
+ if (g_system) {
+ return;
+ }
- if (C != NULL) {
- consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
- }
+ BLI_assert(C != NULL);
+ BLI_assert_msg(!G.background, "Use wm_ghost_init_background instead");
- GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace);
+ GHOST_EventConsumerHandle consumer;
- g_system = GHOST_CreateSystem();
+ consumer = GHOST_CreateEventConsumer(ghost_event_proc, C);
- GHOST_Debug debug = {0};
- if (G.debug & G_DEBUG_GHOST) {
- debug.flags |= GHOST_kDebugDefault;
- }
- if (G.debug & G_DEBUG_WINTAB) {
- debug.flags |= GHOST_kDebugWintab;
- }
- GHOST_SystemInitDebug(g_system, debug);
+ GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace);
- if (C != NULL) {
- GHOST_AddEventConsumer(g_system, consumer);
- }
+ g_system = GHOST_CreateSystem();
- if (wm_init_state.native_pixels) {
- GHOST_UseNativePixels();
- }
+ GHOST_Debug debug = {0};
+ if (G.debug & G_DEBUG_GHOST) {
+ debug.flags |= GHOST_kDebugDefault;
+ }
+ if (G.debug & G_DEBUG_WINTAB) {
+ debug.flags |= GHOST_kDebugWintab;
+ }
+ GHOST_SystemInitDebug(g_system, debug);
- GHOST_UseWindowFocus(wm_init_state.window_focus);
+ GHOST_AddEventConsumer(g_system, consumer);
+
+ if (wm_init_state.native_pixels) {
+ GHOST_UseNativePixels();
}
+
+ GHOST_UseWindowFocus(wm_init_state.window_focus);
+}
+
+/* TODO move this to wm_init_exit.c. */
+void wm_ghost_init_background(void)
+{
+ if (g_system) {
+ return;
+ }
+
+ GHOST_SetBacktraceHandler((GHOST_TBacktraceFn)BLI_system_backtrace);
+
+ g_system = GHOST_CreateSystemBackground();
+
+ GHOST_Debug debug = {0};
+ if (G.debug & G_DEBUG_GHOST) {
+ debug.flags |= GHOST_kDebugDefault;
+ }
+ GHOST_SystemInitDebug(g_system, debug);
}
void wm_ghost_exit(void)
@@ -1921,6 +1925,9 @@ void WM_window_pixel_sample_read(const wmWindowManager *wm,
uint *WM_window_pixels_read(wmWindowManager *wm, wmWindow *win, int r_size[2])
{
+ /* WARNING: Reading from the front-buffer immediately after drawing may fail,
+ * for a slower but more reliable version of this function #WM_window_pixels_read_offscreen
+ * should be preferred. See it's comments for details on why it's needed, see also T98462. */
bool setup_context = wm->windrawable != win;
if (setup_context) {
@@ -2002,36 +2009,40 @@ void WM_init_native_pixels(bool do_it)
void WM_init_tablet_api(void)
{
- if (g_system) {
- switch (U.tablet_api) {
- case USER_TABLET_NATIVE:
- GHOST_SetTabletAPI(g_system, GHOST_kTabletWinPointer);
- break;
- case USER_TABLET_WINTAB:
- GHOST_SetTabletAPI(g_system, GHOST_kTabletWintab);
- break;
- case USER_TABLET_AUTOMATIC:
- default:
- GHOST_SetTabletAPI(g_system, GHOST_kTabletAutomatic);
- break;
- }
+ if (UNLIKELY(!g_system)) {
+ return;
+ }
+
+ switch (U.tablet_api) {
+ case USER_TABLET_NATIVE:
+ GHOST_SetTabletAPI(g_system, GHOST_kTabletWinPointer);
+ break;
+ case USER_TABLET_WINTAB:
+ GHOST_SetTabletAPI(g_system, GHOST_kTabletWintab);
+ break;
+ case USER_TABLET_AUTOMATIC:
+ default:
+ GHOST_SetTabletAPI(g_system, GHOST_kTabletAutomatic);
+ break;
}
}
void WM_cursor_warp(wmWindow *win, int x, int y)
{
- if (win && win->ghostwin) {
- int oldx = x, oldy = y;
+ if (!(win && win->ghostwin)) {
+ return;
+ }
- wm_cursor_position_to_ghost_client_coords(win, &x, &y);
- GHOST_SetCursorPosition(g_system, win->ghostwin, x, y);
+ int oldx = x, oldy = y;
- win->eventstate->prev_xy[0] = oldx;
- win->eventstate->prev_xy[1] = oldy;
+ wm_cursor_position_to_ghost_client_coords(win, &x, &y);
+ GHOST_SetCursorPosition(g_system, win->ghostwin, x, y);
- win->eventstate->xy[0] = oldx;
- win->eventstate->xy[1] = oldy;
- }
+ win->eventstate->prev_xy[0] = oldx;
+ win->eventstate->prev_xy[1] = oldy;
+
+ win->eventstate->xy[0] = oldx;
+ win->eventstate->xy[1] = oldy;
}
/** \} */
diff --git a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c
index be153195cee..5c745c32d2c 100644
--- a/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c
+++ b/source/blender/windowmanager/message_bus/intern/wm_message_bus_rna.c
@@ -20,6 +20,7 @@
#include "message_bus/intern/wm_message_bus_intern.h"
#include "RNA_access.h"
+#include "RNA_path.h"
/* -------------------------------------------------------------------- */
/** \name Internal Utilities
diff --git a/source/blender/windowmanager/message_bus/wm_message_bus.h b/source/blender/windowmanager/message_bus/wm_message_bus.h
index 1bc983f20ad..1558fe4004e 100644
--- a/source/blender/windowmanager/message_bus/wm_message_bus.h
+++ b/source/blender/windowmanager/message_bus/wm_message_bus.h
@@ -66,7 +66,7 @@ typedef struct wmMsg {
} wmMsg;
typedef struct wmMsgSubscribeKey {
- /** Linked list for predicable ordering, otherwise we would depend on #GHash bucketing. */
+ /** Linked list for predictable ordering, otherwise we would depend on #GHash bucketing. */
struct wmMsgSubscribeKey *next, *prev;
ListBase values;
/* over-alloc, eg: wmMsgSubscribeKey_RNA */
diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h
index 2d3624704d0..405b7225bd5 100644
--- a/source/blender/windowmanager/wm_event_types.h
+++ b/source/blender/windowmanager/wm_event_types.h
@@ -44,7 +44,10 @@ enum {
/* non-event, for example disabled timer */
EVENT_NONE = 0x0000,
- /* ********** Start of Input devices. ********** */
+/* ********** Start of Input devices. ********** */
+
+/* Minimum mouse value (inclusive). */
+#define _EVT_MOUSE_MIN 0x0001
/* MOUSE: 0x000x, 0x001x */
LEFTMOUSE = 0x0001,
@@ -74,6 +77,9 @@ enum {
* paint and drawing tools however will want to handle these. */
INBETWEEN_MOUSEMOVE = 0x0011,
+/* Maximum keyboard value (inclusive). */
+#define _EVT_MOUSE_MAX 0x0011 /* 17 */
+
/* IME event, GHOST_kEventImeCompositionStart in ghost */
WM_IME_COMPOSITE_START = 0x0014,
/* IME event, GHOST_kEventImeComposition in ghost */
@@ -311,7 +317,7 @@ enum {
/* XXX Those are mixed inside keyboard 'area'! */
/* System: 0x010x */
- INPUTCHANGE = 0x0103, /* Input connected or disconnected, (259). */
+ // INPUTCHANGE = 0x0103, /* Input connected or disconnected, (259). */ /* UNUSED. */
WINDEACTIVATE = 0x0104, /* Window is deactivated, focus lost, (260). */
/* Timer: 0x011x */
TIMER = 0x0110, /* Timer event, passed on to all queues (272). */
@@ -358,12 +364,6 @@ enum {
/** Test whether the event is timer event. */
#define ISTIMER(event_type) ((event_type) >= TIMER && (event_type) <= TIMERF)
-/* for event checks */
-/* only used for KM_TEXTINPUT, so assume that we want all user-inputtable ascii codes included */
-/* Unused, see #wm_eventmatch, see: T30479. */
-// #define ISTEXTINPUT(event_type) ((event_type) >= ' ' && (event_type) <= 255)
-/* NOTE: an alternative could be to check `event->utf8_buf`. */
-
/** Test whether the event is a key on the keyboard (including modifier keys). */
#define ISKEYBOARD(event_type) \
(((event_type) >= _EVT_KEYBOARD_MIN && (event_type) <= _EVT_KEYBOARD_MAX) || \
@@ -385,13 +385,16 @@ enum {
(((event_type) >= EVT_LEFTCTRLKEY && (event_type) <= EVT_LEFTSHIFTKEY) || \
(event_type) == EVT_OSKEY)
-/** Test whether the event is a mouse button. */
-#define ISMOUSE(event_type) \
- (((event_type) >= LEFTMOUSE && (event_type) <= BUTTON7MOUSE) || (event_type) == MOUSESMARTZOOM)
-/** Test whether the event is a mouse wheel. */
-#define ISMOUSE_WHEEL(event_type) ((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE)
-/** Test whether the event is a mouse (track-pad) gesture. */
-#define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSEROTATE)
+/**
+ * Test whether the event is any kind:
+ * #ISMOUSE_MOTION, #ISMOUSE_BUTTON, #ISMOUSE_WHEEL & #ISMOUSE_GESTURE.
+ *
+ * \note It's best to use more specific check if possible as mixing motion/buttons/gestures
+ * is very broad and not necessarily obvious which kinds of events are important.
+ */
+#define ISMOUSE(event_type) ((event_type) >= _EVT_MOUSE_MIN && (event_type) <= _EVT_MOUSE_MAX)
+/** Test whether the event is a mouse button (excluding mouse-wheel). */
+#define ISMOUSE_MOTION(event_type) ELEM(event_type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)
/** Test whether the event is a mouse button (excluding mouse-wheel). */
#define ISMOUSE_BUTTON(event_type) \
(ELEM(event_type, \
@@ -402,6 +405,10 @@ enum {
BUTTON5MOUSE, \
BUTTON6MOUSE, \
BUTTON7MOUSE))
+/** Test whether the event is a mouse wheel. */
+#define ISMOUSE_WHEEL(event_type) ((event_type) >= WHEELUPMOUSE && (event_type) <= WHEELOUTMOUSE)
+/** Test whether the event is a mouse (track-pad) gesture. */
+#define ISMOUSE_GESTURE(event_type) ((event_type) >= MOUSEPAN && (event_type) <= MOUSESMARTZOOM)
/** Test whether the event is a NDOF event. */
#define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX)
diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h
index 3644aa085f7..036a34a5140 100644
--- a/source/blender/windowmanager/wm_window.h
+++ b/source/blender/windowmanager/wm_window.h
@@ -20,6 +20,7 @@ extern "C" {
* need to event handling.
*/
void wm_ghost_init(bContext *C);
+void wm_ghost_init_background(void);
void wm_ghost_exit(void);
/**
diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt
index 278cd4362c3..05577406b48 100644
--- a/source/creator/CMakeLists.txt
+++ b/source/creator/CMakeLists.txt
@@ -1,9 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright 2006 Blender Foundation. All rights reserved.
-blender_include_dirs(
+set(INC
../../intern/clog
- ../../intern/glew-mx
../../intern/guardedalloc
../blender/blenkernel
../blender/blenlib
@@ -22,10 +21,13 @@ set(LIB
bf_windowmanager
)
+if(HAVE_FEENABLEEXCEPT)
+ add_definitions(-DHAVE_FEENABLEEXCEPT)
+endif()
+
if(WITH_TBB)
- # Force TBB libraries to be in front of MKL (part of OpenImageDenoise), so
- # that it is initialized before MKL and static library initialization order
- # issues are avoided.
+ # Force TBB libraries to be in front of MKL (part of `OpenImageDenoise`), so
+ # that it is initialized before MKL and static library initialization order issues are avoided.
#
# This isn't fully robust but seems to work.
list(INSERT LIB 0 ${TBB_LIBRARIES})
@@ -33,17 +35,17 @@ if(WITH_TBB)
endif()
if(WIN32)
- blender_include_dirs(../../intern/utfconv)
+ list(APPEND INC ../../intern/utfconv)
endif()
if(WITH_LIBMV)
- blender_include_dirs(../../intern/libmv)
+ list(APPEND INC ../../intern/libmv)
add_definitions(-DWITH_LIBMV)
endif()
if(WITH_CYCLES)
if(WITH_CYCLES_LOGGING)
- blender_include_dirs(../../intern/cycles/blender)
+ list(APPEND INC ../../intern/cycles/blender)
add_definitions(-DWITH_CYCLES_LOGGING)
endif()
endif()
@@ -53,16 +55,16 @@ if(WITH_CODEC_FFMPEG)
endif()
if(WITH_TBB)
- blender_include_dirs(${TBB_INCLUDE_DIRS})
+ list(APPEND INC ${TBB_INCLUDE_DIRS})
if(WIN32)
- # For pragma that links tbbmalloc_proxy.lib
+ # For `pragma` that links `tbbmalloc_proxy.lib`.
link_directories(${LIBDIR}/tbb/lib)
endif()
endif()
if(WITH_PYTHON)
- blender_include_dirs(../blender/python)
+ list(APPEND INC ../blender/python)
add_definitions(-DWITH_PYTHON)
if(WITH_PYTHON_SECURITY)
@@ -76,19 +78,19 @@ endif()
if(WITH_SDL)
if(WITH_SDL_DYNLOAD)
- blender_include_dirs(../../extern/sdlew/include)
+ list(APPEND INC ../../extern/sdlew/include)
add_definitions(-DWITH_SDL_DYNLOAD)
endif()
add_definitions(-DWITH_SDL)
endif()
if(WITH_BINRELOC)
- blender_include_dirs(${BINRELOC_INCLUDE_DIRS})
+ list(APPEND INC ${BINRELOC_INCLUDE_DIRS})
add_definitions(-DWITH_BINRELOC)
endif()
if(WITH_FREESTYLE)
- blender_include_dirs(../blender/freestyle)
+ list(APPEND INC ../blender/freestyle)
add_definitions(-DWITH_FREESTYLE)
endif()
@@ -97,7 +99,7 @@ if(WITH_XR_OPENXR)
endif()
if(WITH_GMP)
- blender_include_dirs(${GMP_INCLUDE_DIRS})
+ list(APPEND INC ${GMP_INCLUDE_DIRS})
add_definitions(-DWITH_GMP)
endif()
@@ -105,7 +107,7 @@ if(WITH_OPENCOLORIO)
add_definitions(-DWITH_OCIO)
endif()
-# Setup the exe sources and buildinfo
+# Setup the EXE sources and `buildinfo`.
set(SRC
creator.c
creator_args.c
@@ -114,7 +116,7 @@ set(SRC
creator_intern.h
)
-# MSVC 2010 gives linking errors with the manifest
+# MSVC 2010 gives linking errors with the manifest.
if(WIN32 AND NOT UNIX)
add_definitions(
-DBLEN_VER_RC_STR="${BLENDER_VERSION}"
@@ -170,19 +172,20 @@ if(WITH_BUILDINFO)
unset(BUILD_SYSTEM)
# --------------------------------------------------------------------------
- # write header for values that change each build
- # note, generated file is in build dir's source/creator
- # except when used as an include path.
+ # Write header for values that change each build
+ #
+ # NOTE: generated file is in build directory `source/creator`
+ # except when used as an include path.
add_definitions(-DWITH_BUILDINFO_HEADER)
- # include the output directory, where the buildinfo.h file is generated
+ # Include the output directory, where the `buildinfo.h` file is generated.
include_directories(${CMAKE_CURRENT_BINARY_DIR})
- # XXX, ${buildinfo_h_fake} is used here,
+ # XXX: `${buildinfo_h_fake}` is used here,
# because we rely on that file being detected as missing
- # every build so that the real header "buildinfo.h" is updated.
+ # every build so that the real header `buildinfo.h` is updated.
#
# Keep this until we find a better way to resolve!
@@ -190,32 +193,40 @@ if(WITH_BUILDINFO)
set(buildinfo_h_fake "${CMAKE_CURRENT_BINARY_DIR}/buildinfo.h_fake")
if(EXISTS ${buildinfo_h_fake})
- message(FATAL_ERROR "File \"${buildinfo_h_fake}\" found, this should never be created, remove!")
+ message(
+ FATAL_ERROR
+ "File \"${buildinfo_h_fake}\" found, this should never be created, remove!"
+ )
endif()
- # From the cmake documentation "If the output of the custom command is not actually created as a
+ # From the CMAKE documentation "If the output of the custom command is not actually created as a
# file on disk it should be marked with the SYMBOLIC source file property."
#
- # Not doing this leads to build warnings for the not generated file on windows when using msbuild
- SET_SOURCE_FILES_PROPERTIES(${buildinfo_h_fake} PROPERTIES SYMBOLIC TRUE)
+ # Not doing this leads to build warnings for the not generated file on
+ # MS-Windows when using `msbuild`.
+ set_source_files_properties(${buildinfo_h_fake} PROPERTIES SYMBOLIC TRUE)
# a custom target that is always built
- add_custom_target(buildinfo ALL
- DEPENDS ${buildinfo_h_fake})
+ add_custom_target(
+ buildinfo ALL
+ DEPENDS ${buildinfo_h_fake}
+ )
- # creates buildinfo.h using cmake script
+ # Creates `buildinfo.h` using CMAKE script.
add_custom_command(
OUTPUT
${buildinfo_h_fake} # ensure we always run
${buildinfo_h_real}
- COMMAND ${CMAKE_COMMAND}
- -DSOURCE_DIR=${CMAKE_SOURCE_DIR}
- # overrides only used when non-empty strings
- -DBUILD_DATE=${BUILDINFO_OVERRIDE_DATE}
- -DBUILD_TIME=${BUILDINFO_OVERRIDE_TIME}
- -P ${CMAKE_SOURCE_DIR}/build_files/cmake/buildinfo.cmake)
-
- # buildinfo.h is a generated file
+ COMMAND
+ ${CMAKE_COMMAND}
+ -DSOURCE_DIR=${CMAKE_SOURCE_DIR}
+ # Overrides only used when non-empty strings.
+ -DBUILD_DATE=${BUILDINFO_OVERRIDE_DATE}
+ -DBUILD_TIME=${BUILDINFO_OVERRIDE_TIME}
+ -P ${CMAKE_SOURCE_DIR}/build_files/cmake/buildinfo.cmake
+ )
+
+ # `buildinfo.h` is a generated file.
set_source_files_properties(
${buildinfo_h_real}
PROPERTIES GENERATED TRUE
@@ -224,7 +235,7 @@ if(WITH_BUILDINFO)
unset(buildinfo_h_real)
unset(buildinfo_h_fake)
- # add deps below, after adding blender
+ # Add dependencies below, after adding Blender
# -------------- done with header values.
list(APPEND SRC
@@ -242,26 +253,35 @@ add_cc_flags_custom_test(blender)
if(WITH_PYTHON_MODULE)
add_definitions(-DWITH_PYTHON_MODULE)
- # creates ./bin/bpy.so which can be imported as a python module.
+ # Creates `./bpy/__init__.so` which can be imported as a Python module.
#
- # note that 'SHARED' works on Linux and Windows,
- # but not OSX which _must_ be 'MODULE'
+ # Note that 'SHARED' works on Linux and Windows, but not MACOS which _must_ be 'MODULE'.
add_library(blender MODULE ${SRC})
+
+
+ get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+ if(GENERATOR_IS_MULTI_CONFIG)
+ set(BPY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>/bpy)
+ else()
+ set(BPY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/bpy)
+ endif()
+
set_target_properties(
blender
PROPERTIES
PREFIX ""
- OUTPUT_NAME bpy
- LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
- RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin # only needed on windows
+ OUTPUT_NAME __init__
+ LIBRARY_OUTPUT_DIRECTORY ${BPY_OUTPUT_DIRECTORY}
+ RUNTIME_OUTPUT_DIRECTORY ${BPY_OUTPUT_DIRECTORY}
)
+ unset(BPY_OUTPUT_DIRECTORY)
if(APPLE)
set_target_properties(blender PROPERTIES MACOSX_BUNDLE TRUE)
endif()
if(WIN32)
- # python modules use this
+ # Python modules use this.
set_target_properties(
blender
PROPERTIES
@@ -272,72 +292,87 @@ 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)
+ 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)
- # explicitly say that the executable depends on the buildinfo
+ # Explicitly say that the executable depends on the `buildinfo`.
add_dependencies(blender buildinfo)
endif()
set(BLENDER_TEXT_FILES
${CMAKE_SOURCE_DIR}/release/text/copyright.txt
- # generate this file
- # ${CMAKE_SOURCE_DIR}/release/text/readme.html
+ # Generate this file:
+ # `${CMAKE_SOURCE_DIR}/release/text/readme.html`
)
# -----------------------------------------------------------------------------
-# Platform Specific Var: TARGETDIR_VER
+# Platform specific target destinations
+#
+# Setup version directory, libraries, `bpy` & text files.
if(UNIX AND NOT APPLE)
if(WITH_PYTHON_MODULE)
if(WITH_INSTALL_PORTABLE)
- set(TARGETDIR_VER ${BLENDER_VERSION})
+ set(TARGETDIR_BPY bpy)
+ set(TARGETDIR_VER bpy/${BLENDER_VERSION})
+ set(TARGETDIR_LIB bpy/lib)
else()
- set(TARGETDIR_VER ${PYTHON_SITE_PACKAGES}/${BLENDER_VERSION})
+ set(TARGETDIR_BPY ${PYTHON_SITE_PACKAGES}/bpy)
+ set(TARGETDIR_VER ${PYTHON_SITE_PACKAGES}/bpy/${BLENDER_VERSION})
+ set(TARGETDIR_LIB ${PYTHON_SITE_PACKAGES}/bpy/lib)
endif()
else()
if(WITH_INSTALL_PORTABLE)
set(TARGETDIR_VER ${BLENDER_VERSION})
+ set(TARGETDIR_TEXT .)
+ set(TARGETDIR_LIB lib)
else()
set(TARGETDIR_VER share/blender/${BLENDER_VERSION})
+ set(TARGETDIR_TEXT share/doc/blender)
endif()
endif()
elseif(WIN32)
- set(TARGETDIR_VER ${BLENDER_VERSION})
-
+ if(WITH_PYTHON_MODULE)
+ set(TARGETDIR_BPY ${CMAKE_INSTALL_PREFIX_WITH_CONFIG}/bpy)
+ set(TARGETDIR_VER ${CMAKE_INSTALL_PREFIX_WITH_CONFIG}/bpy/${BLENDER_VERSION})
+ # Important the DLL's are next to `__init__.pyd` otherwise it won't load.
+ set(TARGETDIR_LIB ${CMAKE_INSTALL_PREFIX_WITH_CONFIG}/bpy)
+ else()
+ set(TARGETDIR_VER ${BLENDER_VERSION})
+ set(TARGETDIR_TEXT .)
+ set(TARGETDIR_LIB .)
+ endif()
elseif(APPLE)
if(WITH_PYTHON_MODULE)
if(WITH_INSTALL_PORTABLE)
- set(BPY_INSTALL_DIR)
- 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)
+ set(TARGETDIR_BPY bpy)
+ set(TARGETDIR_VER bpy/${BLENDER_VERSION})
+ set(TARGETDIR_LIB bpy/lib)
else()
- # Parent directory of bpy.so for installation.
- set(BPY_INSTALL_DIR ${PYTHON_LIBPATH}/site-packages)
- # Defined in terms of site-packages since the site-packages
+ # Paths defined in terms of site-packages since the site-packages
# directory can be a symlink (brew for example).
- set(TARGETDIR_VER "${BPY_INSTALL_DIR}/../Resources/${BLENDER_VERSION}")
- set(INSTALL_BPY_TO_SITE_PACKAGES ON)
+ set(TARGETDIR_BPY ${PYTHON_SITE_PACKAGES}/bpy)
+ set(TARGETDIR_VER ${PYTHON_SITE_PACKAGES}/bpy/${BLENDER_VERSION})
+ set(TARGETDIR_LIB ${PYTHON_SITE_PACKAGES}/bpy/lib)
endif()
else()
set(TARGETDIR_VER Blender.app/Contents/Resources/${BLENDER_VERSION})
+ set(TARGETDIR_LIB Blender.app/Contents/Resources/lib)
+ set(TARGETDIR_TEXT Blender.app/Contents/Resources/text)
endif()
- # License, copyright, readme files.
- set(BLENDER_TEXT_FILES_DESTINATION "${TARGETDIR_VER}/../text")
- set(MAC_BLENDER_TARGET_DYLIBS_DIR "${TARGETDIR_VER}/lib")
- # Skip relinking on cpack / install
+
+ # Skip re-linking on CPACK / install.
set_target_properties(blender PROPERTIES BUILD_WITH_INSTALL_RPATH true)
endif()
@@ -360,14 +395,14 @@ if(WITH_PYTHON)
"${BLENDER_VERSION_CYCLE}" STREQUAL "rc")
set(ADDON_EXCLUDE_CONDITIONAL "addons_contrib/*")
else()
- set(ADDON_EXCLUDE_CONDITIONAL "_addons_contrib/*") # dummy, won't do anything
+ set(ADDON_EXCLUDE_CONDITIONAL "_addons_contrib/*") # Dummy, won't do anything.
endif()
# do not install freestyle dir if disabled
if(NOT WITH_FREESTYLE)
set(FREESTYLE_EXCLUDE_CONDITIONAL "freestyle/*")
else()
- set(FREESTYLE_EXCLUDE_CONDITIONAL "_freestyle/*") # dummy, won't do anything
+ set(FREESTYLE_EXCLUDE_CONDITIONAL "_freestyle/*") # Dummy, won't do anything.
endif()
install(
@@ -375,6 +410,7 @@ if(WITH_PYTHON)
DESTINATION ${TARGETDIR_VER}
PATTERN ".git" EXCLUDE
PATTERN ".gitignore" EXCLUDE
+ PATTERN ".github" EXCLUDE
PATTERN ".arcconfig" EXCLUDE
PATTERN "__pycache__" EXCLUDE
PATTERN "${ADDON_EXCLUDE_CONDITIONAL}" EXCLUDE
@@ -387,8 +423,7 @@ endif()
# fonts
install(
- DIRECTORY
- ${CMAKE_SOURCE_DIR}/release/datafiles/fonts
+ DIRECTORY ${CMAKE_SOURCE_DIR}/release/datafiles/fonts
DESTINATION ${TARGETDIR_VER}/datafiles
)
@@ -402,14 +437,14 @@ if(WITH_INTERNATIONAL)
msgfmt_simple(${_po_file} _all_mo_files)
endforeach()
- # Create a custom target which will compile all po to mo
+ # Create a custom target which will compile all `*.po` to `*.mo`.
add_custom_target(
locales
- DEPENDS ${_all_mo_files})
-
+ DEPENDS ${_all_mo_files}
+ )
add_dependencies(blender locales)
- # Generate INSTALL rules
+ # Generate INSTALL rules.
install(
FILES ${_locale_dir}/languages
DESTINATION ${_locale_target_dir}
@@ -434,7 +469,7 @@ if(WITH_INTERNATIONAL)
unset(_locale_dir)
endif()
-# color management
+# Color management.
if(WITH_OPENCOLORIO)
install(
DIRECTORY ${CMAKE_SOURCE_DIR}/release/datafiles/colormanagement
@@ -442,13 +477,14 @@ if(WITH_OPENCOLORIO)
)
endif()
-# helpful tip when using make
+# Helpful tip when using make.
if("${CMAKE_GENERATOR}" MATCHES ".*Makefiles.*")
- # message after building.
+ # Message to display after building.
add_custom_command(
TARGET blender POST_BUILD MAIN_DEPENDENCY blender
- COMMAND ${CMAKE_COMMAND} -E
- echo 'now run: \"make install\" to copy runtime files and scripts to ${TARGETDIR_VER}'
+ COMMAND
+ ${CMAKE_COMMAND} -E
+ echo 'now run: \"make install\" to copy runtime files and scripts to ${TARGETDIR_VER}'
)
endif()
@@ -462,24 +498,33 @@ if(UNIX AND NOT APPLE)
if(WITH_DOC_MANPAGE)
add_custom_target(
blender_man_page ALL
- COMMAND ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py
- --blender ${EXECUTABLE_OUTPUT_PATH}/blender
- --output ${CMAKE_CURRENT_BINARY_DIR}/blender.1)
+ COMMAND
+ ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py
+ --blender ${EXECUTABLE_OUTPUT_PATH}/blender
+ --output ${CMAKE_CURRENT_BINARY_DIR}/blender.1
+ )
add_dependencies(blender_man_page blender)
endif()
endif()
- # there are a few differences between portable and system install
+ if(PLATFORM_BUNDLED_LIBRARIES AND TARGETDIR_LIB)
+ install(
+ FILES ${PLATFORM_BUNDLED_LIBRARIES}
+ DESTINATION ${TARGETDIR_LIB}
+ )
+ endif()
+
+ # There are a few differences between portable and system install.
if(WITH_PYTHON_MODULE)
if(WITH_INSTALL_PORTABLE)
install(
TARGETS blender
- DESTINATION "."
+ DESTINATION ${TARGETDIR_BPY}
)
else()
install(
TARGETS blender
- LIBRARY DESTINATION ${PYTHON_SITE_PACKAGES}
+ LIBRARY DESTINATION ${TARGETDIR_BPY}
)
endif()
# none of the other files are needed currently
@@ -518,9 +563,23 @@ if(UNIX AND NOT APPLE)
${CMAKE_SOURCE_DIR}/release/bin/blender-softwaregl
DESTINATION "."
)
- endif()
- set(BLENDER_TEXT_FILES_DESTINATION ".")
+ # Remove from old location, so existing builds don't start with software
+ # OpenGL now that the lib/ folder is used for other libraries.
+ install(
+ CODE
+ "file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libGL.so)\n
+ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libGL.so.1)\n
+ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libGL.so.1.5.0)\n
+ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libGLU.so)\n
+ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libGLU.so.1)\n
+ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libGLU.so.1.3.1)\n
+ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libglapi.so)\n
+ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libglapi.so.0)\n
+ file(REMOVE ${CMAKE_BINARY_DIR}/bin/lib/libglapi.so.0.0.0)\n
+ "
+ )
+ endif()
else()
# main blender binary
install(
@@ -528,14 +587,14 @@ if(UNIX AND NOT APPLE)
DESTINATION bin
)
if(WITH_DOC_MANPAGE)
- # manpage only with 'blender' binary
+ # Manual page (only with `blender` binary).
install(
FILES ${CMAKE_CURRENT_BINARY_DIR}/blender.1
DESTINATION share/man/man1
)
endif()
- # misc files
+ # Misc files.
install(
FILES ${CMAKE_SOURCE_DIR}/release/freedesktop/blender.desktop
DESTINATION share/applications
@@ -554,7 +613,6 @@ if(UNIX AND NOT APPLE)
DESTINATION bin
)
endif()
- set(BLENDER_TEXT_FILES_DESTINATION share/doc/blender)
endif()
if(WITH_PYTHON)
@@ -565,11 +623,9 @@ if(UNIX AND NOT APPLE)
DESTINATION ${TARGETDIR_VER}/python/bin
)
- # on some platforms (like openSUSE) Python is linked
- # to be used from lib64 folder.
- # determine this from Python's libraries path
- #
- # ugh, its possible 'lib64' is just a symlink to 'lib' which causes incorrect use of 'lib64'
+ # On some platforms (like openSUSE) Python is linked to be used from `lib64` directory.
+ # determine this from Python's libraries path.
+ # Ugh, its possible `lib64` is just a symlink to 'lib' which causes incorrect use of `lib64`.
get_filename_component(_pypath_real ${PYTHON_LIBPATH} REALPATH)
if(${_pypath_real} MATCHES "lib64$")
set(_target_LIB "lib64")
@@ -578,7 +634,7 @@ if(UNIX AND NOT APPLE)
endif()
unset(_pypath_real)
- # Copy the systems python into the install directory
+ # Copy the systems python into the install directory:
# install(CODE "message(\"copying a subset of the systems python...\")")
install(
DIRECTORY ${PYTHON_LIBPATH}/python${PYTHON_VERSION}
@@ -596,8 +652,8 @@ if(UNIX AND NOT APPLE)
PATTERN "wininst*.exe" EXCLUDE # from distutils, avoid malware false positive
)
- # Needed for distutils/pip
- # get the last part of the include dir, will be 'python{version}{abiflag}',
+ # Needed for `distutils/pip`.
+ # Get the last part of the include dir, will be `python{version}{abiflag}`.
get_filename_component(_py_inc_suffix ${PYTHON_INCLUDE_DIR} NAME)
install(
FILES ${PYTHON_INCLUDE_DIR}/pyconfig.h
@@ -607,7 +663,7 @@ if(UNIX AND NOT APPLE)
if(WITH_PYTHON_INSTALL_NUMPY)
# Install to the same directory as the source, so debian-like
- # distros are happy with their policy.
+ # distributions are happy with their policy.
set(_suffix "site-packages")
if(${PYTHON_NUMPY_PATH} MATCHES "dist-packages")
set(_suffix "dist-packages")
@@ -644,7 +700,7 @@ if(UNIX AND NOT APPLE)
if(WITH_PYTHON_INSTALL_ZSTANDARD)
# Install to the same directory as the source, so debian-like
- # distros are happy with their policy.
+ # distributions are happy with their policy.
set(_suffix "site-packages")
if(${PYTHON_ZSTANDARD_PATH} MATCHES "dist-packages")
set(_suffix "dist-packages")
@@ -660,7 +716,7 @@ if(UNIX AND NOT APPLE)
unset(_suffix)
endif()
- # Copy requests, we need to generalize site-packages
+ # Copy requests, we need to generalize site-packages.
if(WITH_PYTHON_INSTALL_REQUESTS)
set(_suffix "site-packages")
if(${PYTHON_REQUESTS_PATH} MATCHES "dist-packages")
@@ -676,9 +732,8 @@ if(UNIX AND NOT APPLE)
)
# On some platforms requests does have extra dependencies.
#
- # Either 'chardet' or 'charset_normalizer" is used, depending on the
- # version of Python. The code below silently skips the one that's not
- # available, so we can just list both here.
+ # Either `chardet` or `charset_normalizer` is used, depending on the version of Python.
+ # The code below silently skips the one that's not available, so we can list both here.
set(_requests_deps "certifi" "chardet" "charset_normalizer" "idna" "urllib3")
foreach(_requests_dep ${_requests_deps})
if(EXISTS ${PYTHON_REQUESTS_PATH}/${_requests_dep})
@@ -714,38 +769,45 @@ if(UNIX AND NOT APPLE)
)
endif()
elseif(WIN32)
-
- set(BLENDER_TEXT_FILES_DESTINATION ".")
+ install(
+ FILES ${LIBDIR}/epoxy/bin/epoxy-0.dll
+ DESTINATION ${TARGETDIR_LIB}
+ )
if(WITH_OPENMP AND MSVC_CLANG)
install(
- FILES ${CLANG_OPENMP_DLL}
- DESTINATION "."
+ FILES ${CLANG_OPENMP_DLL}
+ DESTINATION ${TARGETDIR_LIB}
)
endif()
if(WITH_FFTW3)
install(
- FILES ${LIBDIR}/fftw3/lib/libfftw3-3.dll
- DESTINATION "."
+ FILES ${LIBDIR}/fftw3/lib/libfftw3-3.dll
+ DESTINATION ${TARGETDIR_LIB}
)
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.
+ # 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")
+ 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
+ FILES ${ASAN_DLL}
+ DESTINATION ${TARGETDIR_LIB}
+ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
- FILES ${ASAN_DEBUG_DLL}
- DESTINATION "."
- CONFIGURATIONS Debug
+ FILES ${ASAN_DEBUG_DLL}
+ DESTINATION ${TARGETDIR_LIB}
+ CONFIGURATIONS Debug
)
unset(ASAN_DLL)
unset(ASAN_DEBUG_DLL)
@@ -753,66 +815,75 @@ elseif(WIN32)
if(WITH_GMP)
install(
- FILES ${LIBDIR}/gmp/lib/libgmp-10.dll
- DESTINATION "."
+ FILES ${LIBDIR}/gmp/lib/libgmp-10.dll
+ DESTINATION ${TARGETDIR_LIB}
)
install(
- FILES ${LIBDIR}/gmp/lib/libgmpxx.dll
- DESTINATION "."
- CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
+ FILES ${LIBDIR}/gmp/lib/libgmpxx.dll
+ DESTINATION ${TARGETDIR_LIB}
+ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
- FILES ${LIBDIR}/gmp/lib/libgmpxx_d.dll
- DESTINATION "."
- CONFIGURATIONS Debug
+ FILES ${LIBDIR}/gmp/lib/libgmpxx_d.dll
+ DESTINATION ${TARGETDIR_LIB}
+ CONFIGURATIONS Debug
)
endif()
if(WITH_WINDOWS_PDB)
if(WITH_WINDOWS_STRIPPED_PDB)
- # Icky hack for older cmake from https://stackoverflow.com/a/21198501
- # $<CONFIG> will work in newer cmake but the version currently (3.12)
- # on the buildbot does not support this endavour.
- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}/blender_public.pdb DESTINATION . RENAME blender.pdb)
+ # Icky hack for older CMAKE from https://stackoverflow.com/a/21198501
+ # `$<CONFIG>` will work in newer CMAKE but the version currently (3.12)
+ # on the build-bot does not support this endeavor.
+ install(
+ FILES ${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}/blender_public.pdb
+ DESTINATION .
+ RENAME blender.pdb
+ )
else()
install(FILES $<TARGET_PDB_FILE:blender> DESTINATION . RENAME blender.pdb)
endif()
endif()
if(WITH_OPENVDB)
- install(
- FILES ${LIBDIR}/openvdb/bin/openvdb.dll
- DESTINATION "."
- CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
- )
- install(
- FILES ${LIBDIR}/openvdb/bin/openvdb_d.dll
- DESTINATION "."
- CONFIGURATIONS Debug
- )
+ install(
+ FILES ${LIBDIR}/openvdb/bin/openvdb.dll
+ DESTINATION ${TARGETDIR_LIB}
+ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
+ )
+ install(
+ FILES ${LIBDIR}/openvdb/bin/openvdb_d.dll
+ DESTINATION ${TARGETDIR_LIB}
+ CONFIGURATIONS Debug
+ )
endif()
if(WITH_PYTHON)
string(REPLACE "." "" _PYTHON_VERSION_NO_DOTS ${PYTHON_VERSION})
- if(NOT CMAKE_COMPILER_IS_GNUCC)
- install(
- FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll
- ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python3.dll
- DESTINATION "."
- CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
- )
+ if(NOT WITH_PYTHON_MODULE)
+ if(NOT CMAKE_COMPILER_IS_GNUCC)
+ install(
+ FILES
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python3.dll
+ DESTINATION ${TARGETDIR_LIB}
+ CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
+ )
- install(
- FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll
- ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python3_d.dll
- DESTINATION "."
- CONFIGURATIONS Debug
- )
+ install(
+ FILES
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python3_d.dll
+ DESTINATION ${TARGETDIR_LIB}
+ CONFIGURATIONS Debug
+ )
+ endif()
endif()
if(WITH_PYTHON_INSTALL)
- # note, as far as python is concerned 'RelWithDebInfo' is not debug since its without debug flags.
+ # NOTE: as far as python is concerned `RelWithDebInfo`
+ # is not debug since its without debug flags.
install(DIRECTORY DESTINATION ${TARGETDIR_VER}/python)
install(DIRECTORY DESTINATION ${TARGETDIR_VER}/python/lib)
@@ -822,7 +893,7 @@ elseif(WIN32)
DESTINATION ${BLENDER_VERSION}/python/
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
PATTERN ".svn" EXCLUDE
- PATTERN "*_d.*" EXCLUDE # * debug libraries *
+ PATTERN "*_d.*" EXCLUDE # * debug libraries *
PATTERN "__pycache__" EXCLUDE # * any cache *
PATTERN "*.pyc" EXCLUDE # * any cache *
PATTERN "*.pyo" EXCLUDE # * any cache *
@@ -853,28 +924,32 @@ elseif(WIN32)
)
install(
- FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll
- ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python.exe
+ FILES
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}.dll
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python.exe
DESTINATION ${BLENDER_VERSION}/python/bin
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
- FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll
- ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python_d.exe
+ FILES
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python${_PYTHON_VERSION_NO_DOTS}_d.dll
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/bin/python_d.exe
DESTINATION ${BLENDER_VERSION}/python/bin
CONFIGURATIONS Debug
)
if(WINDOWS_PYTHON_DEBUG)
install(
- FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.pdb
- DESTINATION "."
+ FILES
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}.pdb
+ DESTINATION ${TARGETDIR_LIB}
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
- FILES ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}_d.pdb
- DESTINATION "."
+ FILES
+ ${LIBDIR}/python/${_PYTHON_VERSION_NO_DOTS}/libs/python${_PYTHON_VERSION_NO_DOTS}_d.pdb
+ DESTINATION ${TARGETDIR_LIB}
CONFIGURATIONS Debug
)
endif()
@@ -883,22 +958,9 @@ elseif(WIN32)
unset(_PYTHON_VERSION_NO_DOTS)
endif()
- # EGL Runtime Components
- if(WITH_GL_EGL)
- if(WIN32)
- install(FILES "${OPENGLES_DLL}" DESTINATION ".")
- install(FILES "${OPENGLES_EGL_DLL}" DESTINATION ".")
-
- if(WITH_GL_ANGLE)
- install(FILES "${D3DCOMPILER_DLL}" DESTINATION ".")
- endif()
- endif()
- endif()
-
if(WITH_CODEC_FFMPEG)
- # Filenames change slightly between ffmpeg versions
- # check both 5.0 and fallback to 4.4 to ease the transition
- # between versions.
+ # Filenames change slightly between FFMPEG versions check both 5.0 and fallback to 4.4
+ # to ease the transition between versions.
if(EXISTS "${LIBDIR}/ffmpeg/lib/avcodec-59.dll")
install(
FILES
@@ -908,7 +970,7 @@ elseif(WIN32)
${LIBDIR}/ffmpeg/lib/avutil-57.dll
${LIBDIR}/ffmpeg/lib/swscale-6.dll
${LIBDIR}/ffmpeg/lib/swresample-4.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
)
else()
install(
@@ -919,7 +981,7 @@ elseif(WIN32)
${LIBDIR}/ffmpeg/lib/avutil-56.dll
${LIBDIR}/ffmpeg/lib/swscale-5.dll
${LIBDIR}/ffmpeg/lib/swresample-3.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
)
endif()
endif()
@@ -927,13 +989,13 @@ elseif(WIN32)
install(
FILES
${LIBDIR}/tbb/bin/tbb.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
FILES
${LIBDIR}/tbb/bin/tbb_debug.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
CONFIGURATIONS Debug
)
endif()
@@ -942,14 +1004,14 @@ elseif(WIN32)
FILES
${LIBDIR}/tbb/bin/tbbmalloc.dll
${LIBDIR}/tbb/bin/tbbmalloc_proxy.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
FILES
${LIBDIR}/tbb/bin/tbbmalloc_debug.dll
${LIBDIR}/tbb/bin/tbbmalloc_proxy_debug.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
CONFIGURATIONS Debug
)
list(APPEND LIB ${TBB_MALLOC_LIBRARIES})
@@ -958,7 +1020,7 @@ elseif(WIN32)
if(WITH_CODEC_SNDFILE)
install(
FILES ${LIBDIR}/sndfile/lib/libsndfile-1.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
)
endif()
@@ -966,14 +1028,14 @@ elseif(WIN32)
install(
FILES
${LIBDIR}/openal/lib/OpenAL32.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
)
endif()
if(WITH_SDL)
install(
FILES ${LIBDIR}/sdl/lib/SDL2.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
)
endif()
@@ -983,20 +1045,23 @@ elseif(WIN32)
${LIBDIR}/audaspace/lib/audaspace.dll
${LIBDIR}/audaspace/lib/audaspace-c.dll
${LIBDIR}/audaspace/lib/audaspace-py.dll
- DESTINATION "."
+ DESTINATION ${TARGETDIR_LIB}
)
endif()
- install(
- FILES
- ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu.cmd
- ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu_glitchworkaround.cmd
- ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_log.cmd
- ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_factory_startup.cmd
- ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_oculus.cmd
- ${CMAKE_SOURCE_DIR}/release/windows/batch/oculus.json
- DESTINATION "."
- )
+
+ if(NOT WITH_PYTHON_MODULE)
+ install(
+ FILES
+ ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu.cmd
+ ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_gpu_glitchworkaround.cmd
+ ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_debug_log.cmd
+ ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_factory_startup.cmd
+ ${CMAKE_SOURCE_DIR}/release/windows/batch/blender_oculus.cmd
+ ${CMAKE_SOURCE_DIR}/release/windows/batch/oculus.json
+ DESTINATION ${TARGETDIR_LIB}
+ )
+ endif()
if(WITH_BLENDER_THUMBNAILER)
install(
@@ -1013,12 +1078,12 @@ elseif(WIN32)
endif()
elseif(APPLE)
if(NOT WITH_PYTHON_MODULE)
- # Uppercase name for app bundle
+ # Uppercase name for app bundle.
set_target_properties(blender PROPERTIES OUTPUT_NAME Blender)
endif()
- # handy install macro to exclude files, we use \$ escape for the "to"
- # argument when calling so ${BUILD_TYPE} does not get expanded
+ # Handy install macro to exclude files, we use \$ escape for the "to"
+ # argument when calling so `${BUILD_TYPE}` does not get expanded.
macro(install_dir from to)
install(
DIRECTORY ${from}
@@ -1046,29 +1111,35 @@ elseif(APPLE)
set(OSX_APP_SOURCEDIR ${CMAKE_SOURCE_DIR}/release/darwin/Blender.app)
- # setup Info.plist
- execute_process(COMMAND date "+%Y-%m-%d"
- OUTPUT_VARIABLE BLENDER_DATE
- OUTPUT_STRIP_TRAILING_WHITESPACE)
+ # Setup `Info.plist`.
+ execute_process(
+ COMMAND date "+%Y-%m-%d"
+ OUTPUT_VARIABLE BLENDER_DATE
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
set_target_properties(blender PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${OSX_APP_SOURCEDIR}/Contents/Info.plist
MACOSX_BUNDLE_SHORT_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH}"
- MACOSX_BUNDLE_LONG_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH} ${BLENDER_DATE}")
+ MACOSX_BUNDLE_LONG_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH} ${BLENDER_DATE}"
+ )
- # Gather the date in finder-style
- execute_process(COMMAND date "+%m/%d/%Y/%H:%M"
- OUTPUT_VARIABLE SETFILE_DATE
- OUTPUT_STRIP_TRAILING_WHITESPACE)
+ # Gather the date in finder-style.
+ execute_process(
+ COMMAND date "+%m/%d/%Y/%H:%M"
+ OUTPUT_VARIABLE SETFILE_DATE
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
- # Give the bundle actual creation/modification date
+ # Give the bundle actual creation/modification date.
#
- # Note that the directory might not yet exist, which happens when CMake is first run.
+ # Note that the directory might not yet exist, which happens when CMAKE is first run.
if(NOT EXISTS ${EXECUTABLE_OUTPUT_PATH}/Blender.app)
file(MAKE_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}/Blender.app)
endif()
- execute_process(COMMAND SetFile -d ${SETFILE_DATE} -m ${SETFILE_DATE}
- ${EXECUTABLE_OUTPUT_PATH}/Blender.app)
+ execute_process(
+ COMMAND SetFile -d ${SETFILE_DATE} -m ${SETFILE_DATE} ${EXECUTABLE_OUTPUT_PATH}/Blender.app
+ )
install(
TARGETS blender
@@ -1092,35 +1163,29 @@ elseif(APPLE)
)
endif()
- if(WITH_OPENMP AND OPENMP_CUSTOM)
- install(
- FILES "${OpenMP_LIBRARY}"
- DESTINATION "${MAC_BLENDER_TARGET_DYLIBS_DIR}"
- )
- endif()
-
- if(WITH_COMPILER_ASAN)
+ if(PLATFORM_BUNDLED_LIBRARIES AND TARGETDIR_LIB)
install(
- FILES "${COMPILER_ASAN_LIBRARY}"
- DESTINATION "${MAC_BLENDER_TARGET_DYLIBS_DIR}"
+ FILES ${PLATFORM_BUNDLED_LIBRARIES}
+ DESTINATION ${TARGETDIR_LIB}
)
endif()
- # python
+ # Python.
if(WITH_PYTHON AND NOT WITH_PYTHON_MODULE AND NOT WITH_PYTHON_FRAMEWORK)
- # Copy the python libs into the install directory
+ # Copy the python libraries into the install directory.
install_dir(
- ${PYTHON_LIBPATH}
+ ${PYTHON_LIBPATH}/python${PYTHON_VERSION}
${TARGETDIR_VER}/python/lib
)
+ # Install Python executable.
install(
PROGRAMS ${PYTHON_EXECUTABLE}
DESTINATION ${TARGETDIR_VER}/python/bin
)
- # Needed for distutils/pip
- # get the last part of the include dir, will be 'python{version}{abiflag}',
+ # Needed for `distutils/pip`.
+ # Get the last part of the include dir, will be `python{version}{abiflag}`.
get_filename_component(_py_inc_suffix ${PYTHON_INCLUDE_DIR} NAME)
install(
FILES ${PYTHON_INCLUDE_DIR}/pyconfig.h
@@ -1129,13 +1194,11 @@ elseif(APPLE)
unset(_py_inc_suffix)
endif()
- if(WITH_PYTHON_MODULE)
- if(INSTALL_BPY_TO_SITE_PACKAGES)
- install(
- TARGETS blender
- LIBRARY DESTINATION ${BPY_INSTALL_DIR}
- )
- endif()
+ if(WITH_PYTHON_MODULE AND TARGETDIR_BPY)
+ install(
+ TARGETS blender
+ LIBRARY DESTINATION ${TARGETDIR_BPY}
+ )
endif()
if(WITH_DRACO)
@@ -1149,11 +1212,12 @@ endif()
# -----------------------------------------------------------------------------
# Generic Install, for all targets
-if(DEFINED BLENDER_TEXT_FILES_DESTINATION)
+if(DEFINED TARGETDIR_TEXT)
- configure_file(${CMAKE_SOURCE_DIR}/release/text/readme.html
- ${CMAKE_BINARY_DIR}/release/text/readme.html
- @ONLY
+ configure_file(
+ ${CMAKE_SOURCE_DIR}/release/text/readme.html
+ ${CMAKE_BINARY_DIR}/release/text/readme.html
+ @ONLY
)
list(APPEND BLENDER_TEXT_FILES
${CMAKE_BINARY_DIR}/release/text/readme.html
@@ -1161,21 +1225,20 @@ if(DEFINED BLENDER_TEXT_FILES_DESTINATION)
install(
FILES ${BLENDER_TEXT_FILES}
- DESTINATION "${BLENDER_TEXT_FILES_DESTINATION}"
+ DESTINATION "${TARGETDIR_TEXT}"
)
install(
- DIRECTORY
- ${CMAKE_SOURCE_DIR}/release/license
- DESTINATION "${BLENDER_TEXT_FILES_DESTINATION}"
+ DIRECTORY ${CMAKE_SOURCE_DIR}/release/license
+ DESTINATION "${TARGETDIR_TEXT}"
)
endif()
-# install more files specified elsewhere
+# Install more files specified elsewhere.
delayed_do_install(${TARGETDIR_VER})
unset(BLENDER_TEXT_FILES)
-unset(BLENDER_TEXT_FILES_DESTINATION)
+unset(TARGETDIR_TEXT)
# -----------------------------------------------------------------------------
@@ -1198,16 +1261,18 @@ unset(_icon_names)
unset(_icon_files)
unset(_f)
+
# -----------------------------------------------------------------------------
# Studio Lights
+
install(
- DIRECTORY
- ${CMAKE_SOURCE_DIR}/release/datafiles/studiolights
+ DIRECTORY ${CMAKE_SOURCE_DIR}/release/datafiles/studiolights
DESTINATION ${TARGETDIR_VER}/datafiles
)
+
# -----------------------------------------------------------------------------
-# Setup link libs
+# Setup link libraries
add_dependencies(blender makesdna)
target_link_libraries(blender ${LIB})
@@ -1216,43 +1281,50 @@ unset(LIB)
setup_platform_linker_flags(blender)
setup_platform_linker_libs(blender)
-if(APPLE)
- set_target_properties(blender PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/osx_locals.map)
-elseif(UNIX)
- set_target_properties(blender PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/blender.map)
+if(DEFINED PLATFORM_SYMBOLS_MAP)
+ set_target_properties(blender PROPERTIES LINK_DEPENDS ${PLATFORM_SYMBOLS_MAP})
endif()
+blender_target_include_dirs(blender ${INC})
+
# -----------------------------------------------------------------------------
# USD registry.
-# USD requires a set of JSON files that define the standard schemas. These
-# files are required at runtime.
+
+# USD requires a set of JSON files that define the standard schemas.
+# These files are required at runtime.
if(WITH_USD)
add_definitions(-DWITH_USD)
- install(DIRECTORY
- ${USD_LIBRARY_DIR}/usd
+ install(
+ DIRECTORY ${USD_LIBRARY_DIR}/usd
DESTINATION "${TARGETDIR_VER}/datafiles"
)
endif()
-# vcpkg substitutes our libs with theirs, which will cause issues when you
-# you run these builds on other systems due to missing dlls. So we opt out
-# the use of vcpkg
+# `vcpkg` substitutes our libraries with theirs, which will cause issues when you you run
+# these builds on other systems due to missing DLL's. So we opt out the use of `vcpkg`.
if(WIN32)
set_target_properties(blender PROPERTIES VS_GLOBAL_VcpkgEnabled "false")
set_target_properties(blender PROPERTIES
PDB_NAME "blender_private"
- PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>")
- if(WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB)
- # This is slightly messy, but single target generators like ninja will not have the
- # CMAKE_CFG_INTDIR variable and multitarget generators like msbuild will not have
- # CMAKE_BUILD_TYPE. This can be simplified by target_link_options and the $<CONFIG>
- # generator expression in newer cmake (2.13+) but until that time this fill have suffice.
- if(CMAKE_BUILD_TYPE)
- set_property(TARGET blender APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/blender_public.pdb")
- else()
- set_property(TARGET blender APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/blender_public.pdb")
- endif()
+ PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>"
+ )
+ if(WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB)
+ # This is slightly messy, but single target generators like ninja will not have the
+ # `CMAKE_CFG_INTDIR` variable and multi-target generators like `msbuild` will not have
+ # `CMAKE_BUILD_TYPE`. This can be simplified by `target_link_options` and the `$<CONFIG>`
+ # generator expression in newer CMAKE (2.13+) but until that time this fill have suffice.
+ if(CMAKE_BUILD_TYPE)
+ set_property(
+ TARGET blender APPEND_STRING PROPERTY LINK_FLAGS
+ " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/blender_public.pdb"
+ )
+ else()
+ set_property(
+ TARGET blender APPEND_STRING PROPERTY LINK_FLAGS
+ " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/blender_public.pdb"
+ )
+ endif()
endif()
endif()
diff --git a/source/creator/blender.map b/source/creator/blender.map
deleted file mode 100644
index 500892a20f3..00000000000
--- a/source/creator/blender.map
+++ /dev/null
@@ -1,173 +0,0 @@
-
-/* on Linux we exclude LLVM symbols, they conflict with Mesa llvmpipe
- * we also keep boost's symbols local, since some python modules could
- * be using boost as well (mainly that's for lux render)
- */
-
-{
-global:
- *;
- *_boost*;
-local:
- __once_proxy;
- _ZSt11__once_call;
- _ZSt15__once_callable;
- al*;
- *Alembic*;
- av*;
- blosc*;
- *boost*;
- *ceres*;
- *cineon*;
- *clang*;
- *COLLADA*;
- cu*;
- *default_error_condition*;
- *dpx*;
- *embree*;
- ff_*;
- fftw*;
- FLAC*;
- FT_*;
- *GeneratedSaxParser*;
- *google*;
- gsm*;
- Gsm*;
- html*;
- id3tag*;
- *Iex*;
- *Ilm*;
- *Imath*;
- *Imf*;
- jack_*;
- jpeg_*;
- jsimd**;
- lame_*;
- *llvm*;
- *LLVM*;
- *MathML*;
- *mkldnn*;
- nvrtc*;
- oc_*;
- ogg*;
- *oidn*;
- *OpenColorIO*;
- *OpenImageIO*;
- *OpenSubdiv*;
- *openvdb*;
- opj_*;
- opus_*;
- *OSL*;
- *pathYy*;
- png_*;
- *SDL*;
- *squish*;
- *tbb*;
- *textFileFormatYy*;
- *TIFF*;
- *tinyformat*;
- *usdBlender*;
- vorbis*;
- vp8*;
- vp9*;
- vpx*;
- x264_*;
- xml*;
- xvid*;
- *YAML*;
-
- /* LLVM symbols not in the LLVM namespace that can conflict with LLVM usage
- * in OpenGL and OpenCL drivers.
- *
- * These are found by doing a Blender build with and without OSL, and
- * comparing the output of nm -gD ./bin/blender to find symbols. */
- AlwaysSpillBase;
- AsmMacroMaxNestingDepth;
- AttributorRun;
- CheckBFIUnknownBlockQueries;
- *cloneBitwiseIVUser*;
- *computeHostNumHardwareThread*;
- *computeHostNumPhysicalCores*;
- decodeInstruction;
- DisableBasicAA;
- DisablePreInliner;
- DisableWholeProgramVisibility;
- EnableCHR;
- EnableConstraintElimination;
- EnableGVNHoist;
- EnableGVNSink;
- EnableHotColdSplit;
- EnableIPRA;
- EnableIROutliner;
- EnableKnowledgeRetention;
- EnableLoopFlatten;
- EnableMatrix;
- EnableOrderFileInstrumentation;
- EnablePGSO;
- EnableUnrollAndJam;
- EnableVPlanNativePath;
- EnableVPlanPredication;
- ExtraVectorizerPasses;
- FlattenedProfileUsed;
- ForcePGSO;
- ForceStackAlign;
- ForceSummaryEdgesCold;
- FSEC;
- *getExtendedOperandRecurrence*;
- *getWideRecurrence*;
- InlinerFunctionImportStats;
- *IROutlinerLegacyPass*;
- __jit_debug_descriptor;
- __jit_debug_register_code;
- _Jv_RegisterClasses;
- *LiveDebugValues*;
- *LoopInterchangeLegacyPass*;
- MachineRegionInfoPassID;
- MaxDevirtIterations;
- MaxRegistersForGCPointers;
- MemOPOptMemcmpBcmp;
- MemOPSizeLarge;
- MemOPSizeRange;
- MISchedPostRA;
- ModuleSummaryDotFile;
- __morestack;
- Name;
- NumNamedVarArgParams;
- PGOViewCounts;
- PGSOColdCodeOnly;
- PGSOColdCodeOnlyForInstrPGO;
- PGSOColdCodeOnlyForPartialSamplePGO;
- PGSOColdCodeOnlyForSamplePGO;
- PgsoCutoffInstrProf;
- PgsoCutoffSampleProf;
- PGSOLargeWorkingSetSizeOnly;
- PreInlineThreshold;
- PrintBlockFreqFuncName;
- PrintBranchProbFuncName;
- ProfileLikelyProb;
- RunNewGVN;
- RunPartialInlining;
- RunSLPVectorization;
- ScalePartialSampleProfileWorkingSetSize;
- *ScopedAliasMetadataDeepCloner*;
- ShouldPreserveAllAttributes;
- SkipFunctionNames;
- StartAfterOptName;
- StartBeforeOptName;
- StaticLikelyProb;
- StopAfterOptName;
- StopBeforeOptName;
- UseContextLessSummary;
- UseDbgAddr;
- UseLEB128Directives;
- UseRegistersForDeoptValues;
- UseRegistersForGCPointersInLandingPad;
- ViewBlockFreqFuncName;
- ViewBlockLayoutWithBFI;
- ViewHotFreqPercent;
- WholeProgramVisibility;
- *widenLoopCompare*;
- *widenWithVariant*;
- WriteRelBFToSummary;
- X86CompilationCallback*;
-};
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 6c95ee3e490..2cd54deeab5 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -24,7 +24,6 @@
#include "DNA_genfile.h"
-#include "BLI_args.h"
#include "BLI_string.h"
#include "BLI_system.h"
#include "BLI_task.h"
@@ -41,7 +40,6 @@
#include "BKE_global.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_idtype.h"
-#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_modifier.h"
@@ -52,6 +50,10 @@
#include "BKE_vfont.h"
#include "BKE_volume.h"
+#ifndef WITH_PYTHON_MODULE
+# include "BLI_args.h"
+#endif
+
#include "DEG_depsgraph.h"
#include "IMB_imbuf.h" /* For #IMB_init. */
@@ -95,6 +97,18 @@
#include "creator_intern.h" /* Own include. */
/* -------------------------------------------------------------------- */
+/** \name Local Defines
+ * \{ */
+
+/* When building as a Python module, don't use special argument handling
+ * so the module loading logic can control the `argv` & `argc`. */
+#if defined(WIN32) && !defined(WITH_PYTHON_MODULE)
+# define USE_WIN32_UNICODE_ARGS
+#endif
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Local Application State
* \{ */
@@ -133,23 +147,34 @@ static void main_callback_setup(void)
/* free data on early exit (if Python calls 'sys.exit()' while parsing args for eg). */
struct CreatorAtExitData {
+#ifndef WITH_PYTHON_MODULE
bArgs *ba;
-#ifdef WIN32
+#endif
+
+#ifdef USE_WIN32_UNICODE_ARGS
const char **argv;
int argv_num;
#endif
+
+#if defined(WITH_PYTHON_MODULE) && !defined(USE_WIN32_UNICODE_ARGS)
+ void *_empty; /* Prevent empty struct error with MSVC. */
+#endif
};
static void callback_main_atexit(void *user_data)
{
struct CreatorAtExitData *app_init_data = user_data;
+#ifndef WITH_PYTHON_MODULE
if (app_init_data->ba) {
BLI_args_destroy(app_init_data->ba);
app_init_data->ba = NULL;
}
+#else
+ UNUSED_VARS(app_init_data); /* May be unused. */
+#endif
-#ifdef WIN32
+#ifdef USE_WIN32_UNICODE_ARGS
if (app_init_data->argv) {
while (app_init_data->argv_num) {
free((void *)app_init_data->argv[--app_init_data->argv_num]);
@@ -157,6 +182,8 @@ static void callback_main_atexit(void *user_data)
free((void *)app_init_data->argv);
app_init_data->argv = NULL;
}
+#else
+ UNUSED_VARS(app_init_data); /* May be unused. */
#endif
}
@@ -242,7 +269,7 @@ void gmp_blender_init_allocator()
* or exit immediately when running in background-mode.
*/
int main(int argc,
-#ifdef WIN32
+#ifdef USE_WIN32_UNICODE_ARGS
const char **UNUSED(argv_c)
#else
const char **argv
@@ -255,7 +282,7 @@ int main(int argc,
bArgs *ba;
#endif
-#ifdef WIN32
+#ifdef USE_WIN32_UNICODE_ARGS
char **argv;
int argv_num;
#endif
@@ -281,11 +308,11 @@ int main(int argc,
_putenv_s("OMP_WAIT_POLICY", "PASSIVE");
# endif
+# ifdef USE_WIN32_UNICODE_ARGS
/* Win32 Unicode Arguments. */
- /* NOTE: cannot use `guardedalloc` allocation here, as it's not yet initialized
- * (it depends on the arguments passed in, which is what we're getting here!)
- */
{
+ /* NOTE: Can't use `guardedalloc` allocation here, as it's not yet initialized
+ * (it depends on the arguments passed in, which is what we're getting here!) */
wchar_t **argv_16 = CommandLineToArgvW(GetCommandLineW(), &argc);
argv = malloc(argc * sizeof(char *));
for (argv_num = 0; argv_num < argc; argv_num++) {
@@ -297,7 +324,8 @@ int main(int argc,
app_init_data.argv = argv;
app_init_data.argv_num = argv_num;
}
-#endif /* WIN32 */
+# endif /* USE_WIN32_UNICODE_ARGS */
+#endif /* WIN32 */
/* NOTE: Special exception for guarded allocator type switch:
* we need to perform switch from lock-free to fully
@@ -528,7 +556,7 @@ int main(int argc,
(void)ba;
#endif
-#ifdef WIN32
+#ifdef USE_WIN32_UNICODE_ARGS
argv = NULL;
(void)argv;
#endif
diff --git a/source/creator/creator_intern.h b/source/creator/creator_intern.h
index c9519f78af9..29ef6d96f15 100644
--- a/source/creator/creator_intern.h
+++ b/source/creator/creator_intern.h
@@ -11,6 +11,10 @@
struct bArgs;
struct bContext;
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#ifndef WITH_PYTHON_MODULE
/* creator_args.c */
@@ -68,14 +72,14 @@ enum {
# define BUILD_DATE
#endif
-/* from buildinfo.c */
+/* From `buildinfo.c`. */
#ifdef BUILD_DATE
extern char build_date[];
extern char build_time[];
extern char build_hash[];
extern unsigned long build_commit_timestamp;
-/* TODO(sergey): ideally size need to be in sync with buildinfo.c */
+/* TODO(@sergey): ideally size need to be in sync with `buildinfo.c`. */
extern char build_commit_date[16];
extern char build_commit_time[16];
@@ -87,3 +91,7 @@ extern char build_cxxflags[];
extern char build_linkflags[];
extern char build_system[];
#endif /* BUILD_DATE */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c
index 76423d7ba43..c016372e6b0 100644
--- a/source/creator/creator_signals.c
+++ b/source/creator/creator_signals.c
@@ -69,11 +69,10 @@ static void sig_handle_fpe(int UNUSED(sig))
# if !defined(WITH_HEADLESS)
static void sig_handle_blender_esc(int sig)
{
- static int count = 0;
-
G.is_break = true; /* forces render loop to read queue, not sure if its needed */
if (sig == 2) {
+ static int count = 0;
if (count) {
printf("\nBlender killed\n");
exit(2);
@@ -244,11 +243,9 @@ void main_signal_setup_background(void)
/* for all platforms, even windows has it! */
BLI_assert(G.background);
-# if !defined(WITH_HEADLESS)
/* Support pressing `Ctrl-C` to close Blender in background-mode.
* Useful to be able to cancel a render operation. */
signal(SIGINT, sig_handle_blender_esc);
-# endif
}
void main_signal_setup_fpe(void)
@@ -258,7 +255,7 @@ void main_signal_setup_fpe(void)
* set breakpoints on sig_handle_fpe */
signal(SIGFPE, sig_handle_fpe);
-# if defined(__linux__) && defined(__GNUC__)
+# if defined(__linux__) && defined(__GNUC__) && defined(HAVE_FEENABLEEXCEPT)
feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
# endif /* defined(__linux__) && defined(__GNUC__) */
# if defined(OSX_SSE_FPE)
diff --git a/source/creator/osx_locals.map b/source/creator/symbols_apple.map
index 5619f151bd2..5619f151bd2 100644
--- a/source/creator/osx_locals.map
+++ b/source/creator/symbols_apple.map
diff --git a/source/creator/symbols_unix.map b/source/creator/symbols_unix.map
new file mode 100644
index 00000000000..673c402022d
--- /dev/null
+++ b/source/creator/symbols_unix.map
@@ -0,0 +1,44 @@
+/* Hide all symbols except a few required ones.
+ *
+ * Otherwise LLVM symbols conflict with Mesa llvm pipe, boost symbols conflict
+ * with Luxrender, etc. */
+{
+global:
+ /* Essential symbols for the program to start and exit. */
+ _bss_start;
+ __end;
+ _fini;
+ _init;
+ /* Needed for Python modules to work. */
+ Py*;
+ _Py*;
+ /* Needed for sanitizers. Based on:
+ * llvm/compiler-rt/lib/sanitizer_common/scripts/gen_dynamic_list.py. */
+ __asan*;
+ __lsan*;
+ __tsan*;
+ __ubsan*;
+ __sanitizer*;
+ __Znw*;
+ __Zna*;
+ __Zdl*;
+ __Zda*;
+ aligned_alloc;
+ aligned_free;
+ calloc*;
+ free;
+ mallinfo;
+ malloc*;
+ mallopt;
+ memalign;
+ memcpy;
+ posix_memalign;
+ pthread_*;
+ pvalloc;
+ realloc*;
+ realpath;
+ sched_*;
+ valloc;
+local:
+ *;
+};